From 86a712ae8a9172bc63b6ac83a84db5eca5db923a Mon Sep 17 00:00:00 2001 From: SanaeFox <36219542+Hoshinonyaruko@users.noreply.github.com> Date: Tue, 16 Jan 2024 14:32:21 +0800 Subject: [PATCH] Beta133 (#307) * beta122 * beta123 * beta124 * beta124 * beta124 * beta127 * beta128 * beta129 * beta130 * beta131 * beta132 * beta133 --- Processor/Processor.go | 223 ++++++++++++++++++++++++++++++++++++ config/config.go | 52 +++++++++ handlers/send_group_msg.go | 24 +++- template/config_template.go | 4 + 4 files changed, 302 insertions(+), 1 deletion(-) diff --git a/Processor/Processor.go b/Processor/Processor.go index cc104dca..82b36f5b 100644 --- a/Processor/Processor.go +++ b/Processor/Processor.go @@ -23,9 +23,11 @@ import ( "github.com/hoshinonyaruko/gensokyo/echo" "github.com/hoshinonyaruko/gensokyo/handlers" "github.com/hoshinonyaruko/gensokyo/idmap" + "github.com/hoshinonyaruko/gensokyo/images" "github.com/hoshinonyaruko/gensokyo/mylog" "github.com/hoshinonyaruko/gensokyo/wsclient" "github.com/tencent-connect/botgo/dto" + "github.com/tencent-connect/botgo/dto/keyboard" "github.com/tencent-connect/botgo/openapi" ) @@ -552,6 +554,13 @@ func (p *Processors) HandleFrameworkCommand(messageText string, data interface{} mylog.Printf("您没有权限,使用临时指令:%s 忽略权限检查,或将masterid设置为空数组", tempCmd) SendMessage("您没有权限,请配置config.yml或查看日志,使用临时指令", data, Type, p.Api, p.Apiv2) } + + //link指令 + if Type == "group" && strings.HasPrefix(cleanedMessage, config.GetLinkPrefix()) { + md, kb := generateMdByConfig() + SendMessageMd(md, kb, data, Type, p.Api, p.Apiv2) + } + return nil } @@ -789,6 +798,113 @@ func SendMessage(messageText string, data interface{}, messageType string, api o return nil } +// SendMessageMd 发送Md消息根据不同的类型 +func SendMessageMd(md *dto.Markdown, kb *keyboard.MessageKeyboard, data interface{}, messageType string, api openapi.OpenAPI, apiv2 openapi.OpenAPI) error { + // 强制类型转换,获取Message结构 + var msg *dto.Message + switch v := data.(type) { + case *dto.WSGroupATMessageData: + msg = (*dto.Message)(v) + case *dto.WSATMessageData: + msg = (*dto.Message)(v) + case *dto.WSMessageData: + msg = (*dto.Message)(v) + case *dto.WSDirectMessageData: + msg = (*dto.Message)(v) + case *dto.WSC2CMessageData: + msg = (*dto.Message)(v) + default: + return nil + } + switch messageType { + case "guild": + // 处理公会消息 + msgseq := echo.GetMappingSeq(msg.ID) + echo.AddMappingSeq(msg.ID, msgseq+1) + Message := &dto.MessageToCreate{ + Content: "markdown", + MsgID: msg.ID, + MsgSeq: msgseq, + Markdown: md, + Keyboard: kb, + MsgType: 2, //md信息 + } + Message.Timestamp = time.Now().Unix() // 设置时间戳 + if _, err := api.PostMessage(context.TODO(), msg.ChannelID, Message); err != nil { + mylog.Printf("发送文本信息失败: %v", err) + return err + } + + case "group": + // 处理群组消息 + msgseq := echo.GetMappingSeq(msg.ID) + echo.AddMappingSeq(msg.ID, msgseq+1) + Message := &dto.MessageToCreate{ + Content: "markdown", + MsgID: msg.ID, + MsgSeq: msgseq, + Markdown: md, + Keyboard: kb, + MsgType: 2, //md信息 + } + Message.Timestamp = time.Now().Unix() // 设置时间戳 + _, err := apiv2.PostGroupMessage(context.TODO(), msg.GroupID, Message) + if err != nil { + mylog.Printf("发送文本群组信息失败: %v", err) + return err + } + + case "guild_private": + // 处理私信 + timestamp := time.Now().Unix() + timestampStr := fmt.Sprintf("%d", timestamp) + dm := &dto.DirectMessage{ + GuildID: msg.GuildID, + ChannelID: msg.ChannelID, + CreateTime: timestampStr, + } + msgseq := echo.GetMappingSeq(msg.ID) + echo.AddMappingSeq(msg.ID, msgseq+1) + Message := &dto.MessageToCreate{ + Content: "markdown", + MsgID: msg.ID, + MsgSeq: msgseq, + Markdown: md, + Keyboard: kb, + MsgType: 2, //md信息 + } + Message.Timestamp = time.Now().Unix() // 设置时间戳 + if _, err := apiv2.PostDirectMessage(context.TODO(), dm, Message); err != nil { + mylog.Printf("发送文本信息失败: %v", err) + return err + } + + case "group_private": + // 处理群组私聊消息 + msgseq := echo.GetMappingSeq(msg.ID) + echo.AddMappingSeq(msg.ID, msgseq+1) + Message := &dto.MessageToCreate{ + Content: "markdown", + MsgID: msg.ID, + MsgSeq: msgseq, + Markdown: md, + Keyboard: kb, + MsgType: 2, //md信息 + } + Message.Timestamp = time.Now().Unix() // 设置时间戳 + _, err := apiv2.PostC2CMessage(context.TODO(), msg.Author.ID, Message) + if err != nil { + mylog.Printf("发送文本私聊信息失败: %v", err) + return err + } + + default: + return errors.New("未知的消息类型") + } + + return nil +} + // autobind 函数接受 interface{} 类型的数据 // commit by 紫夜 2023-11-19 func (p *Processors) Autobind(data interface{}) error { @@ -922,3 +1038,110 @@ func GenerateAvatarURL(userID int64) (string, error) { // 构建并返回 URL return fmt.Sprintf("http://q%d.qlogo.cn/g?b=qq&nk=%d&s=640", qNumber, userID), nil } + +// 生成link卡片 +func generateMdByConfig() (md *dto.Markdown, kb *keyboard.MessageKeyboard) { + //相关配置获取 + mdtext := config.GetLinkText() + mdtext = "\r" + mdtext + CustomTemplateID := config.GetCustomTemplateID() + linkBots := config.GetLinkBots() + imgURL := config.GetLinkPic() + + //超过16个时候随机显示 + if len(linkBots) > 16 { + linkBots = getRandomSelection(linkBots, 16) + } + + //组合 mdParams + var mdParams []*dto.MarkdownParams + if imgURL != "" { + height, width, err := images.GetImageDimensions(imgURL) + if err != nil { + mylog.Printf("获取图片宽高出错") + } + imgDesc := fmt.Sprintf("图片 #%dpx #%dpx", width, height) + // 创建 MarkdownParams 的实例 + mdParams = []*dto.MarkdownParams{ + {Key: "img_dec", Values: []string{imgDesc}}, + {Key: "img_url", Values: []string{imgURL}}, + {Key: "text_end", Values: []string{mdtext}}, + } + } else { + mdParams = []*dto.MarkdownParams{ + {Key: "text_end", Values: []string{mdtext}}, + } + } + + // 组合模板 Markdown + md = &dto.Markdown{ + CustomTemplateID: CustomTemplateID, + Params: mdParams, + } + + // 创建自定义键盘 + customKeyboard := &keyboard.CustomKeyboard{} + var currentRow *keyboard.Row + var buttonCount int + + for _, bot := range linkBots { + parts := strings.SplitN(bot, "-", 3) + if len(parts) < 3 { + continue // 跳过无效的格式 + } + name := parts[2] + botuin := parts[1] + botappid := parts[0] + boturl := handlers.BuildQQBotShareLink(botuin, botappid) + + button := &keyboard.Button{ + RenderData: &keyboard.RenderData{ + Label: name, + VisitedLabel: name, + Style: 1, // 蓝色边缘 + }, + Action: &keyboard.Action{ + Type: 0, // 链接类型 + Permission: &keyboard.Permission{Type: 2}, // 所有人可操作 + Data: boturl, + UnsupportTips: "请升级新版手机QQ", + }, + } + + // 如果当前行为空或已满(4个按钮),则创建一个新行 + if currentRow == nil || buttonCount == 4 { + currentRow = &keyboard.Row{} + customKeyboard.Rows = append(customKeyboard.Rows, currentRow) + buttonCount = 0 + } + + // 将按钮添加到当前行 + currentRow.Buttons = append(currentRow.Buttons, button) + buttonCount++ + } + + // 创建 MessageKeyboard 并设置其 Content + kb = &keyboard.MessageKeyboard{ + Content: customKeyboard, + } + + return md, kb +} + +func getRandomSelection(slice []string, max int) []string { + if len(slice) <= max { + return slice + } + + selected := make(map[int]bool) + var result []string + for len(result) < max { + index, _ := rand.Int(rand.Reader, big.NewInt(int64(len(slice)))) + idx := int(index.Int64()) + if !selected[idx] { + selected[idx] = true + result = append(result, slice[idx]) + } + } + return result +} diff --git a/config/config.go b/config/config.go index 78ddf764..dfeb04b6 100644 --- a/config/config.go +++ b/config/config.go @@ -136,6 +136,10 @@ type Settings struct { Uin int64 `yaml:"uin"` VwhitePrefixMode bool `yaml:"v_white_prefix_mode"` Enters []string `yaml:"enters"` + LinkPrefix string `yaml:"link_prefix"` + LinkBots []string `yaml:"link_bots"` + LinkText string `yaml:"link_text"` + LinkPic string `yaml:"link_pic"` } // LoadConfig 从文件中加载配置并初始化单例配置 @@ -1711,3 +1715,51 @@ func GetEnters() []string { } return nil // 返回nil,如果instance为nil } + +// 获取 LinkPrefix +func GetLinkPrefix() string { + mu.Lock() + defer mu.Unlock() + + if instance == nil { + mylog.Println("Warning: instance is nil when trying to get LinkPrefix.") + return "" + } + return instance.Settings.LinkPrefix +} + +// 获取 LinkBots 数组 +func GetLinkBots() []string { + mu.Lock() + defer mu.Unlock() + + if instance == nil { + mylog.Println("Warning: instance is nil when trying to get LinkBots.") + return nil + } + return instance.Settings.LinkBots +} + +// 获取 LinkText +func GetLinkText() string { + mu.Lock() + defer mu.Unlock() + + if instance == nil { + mylog.Println("Warning: instance is nil when trying to get LinkText.") + return "" + } + return instance.Settings.LinkText +} + +// 获取 LinkPic +func GetLinkPic() string { + mu.Lock() + defer mu.Unlock() + + if instance == nil { + mylog.Println("Warning: instance is nil when trying to get LinkPic.") + return "" + } + return instance.Settings.LinkPic +} diff --git a/handlers/send_group_msg.go b/handlers/send_group_msg.go index e9f66eab..a2cb87c5 100644 --- a/handlers/send_group_msg.go +++ b/handlers/send_group_msg.go @@ -1147,13 +1147,35 @@ func auto_md(message callapi.ActionMessage, messageText string, richMediaMessage dataLabel = matchedPrefix.Prefix + whiteLabel } + //在虚拟二级指令白名单,设置*前缀,代表不真实添加,仅再来一次 + if strings.HasPrefix(whiteLabel, "*") { + //移除whiteLabel前端的*,*仅作为判断,不作为显示 + whiteLabel = strings.TrimPrefix(whiteLabel, "*") + dataLabel = matchedPrefix.Prefix + } + + //在虚拟二级指令白名单,设置&前缀,代表仅触发其本身 + //如果&前缀指令包含了空格 则只显示空格右侧的文本 + if strings.HasPrefix(whiteLabel, "&") { + //移除whiteLabel前端的*,*仅作为判断,不作为显示 + whiteLabel = strings.TrimPrefix(whiteLabel, "&") + //这里是实际填充到data的 + dataLabel = whiteLabel + // 找到最后一个空格的位置 显示空格右边的文本 没有找到空格则不变 + lastSpaceIndex := strings.LastIndex(whiteLabel, " ") + if lastSpaceIndex != -1 && lastSpaceIndex < len(whiteLabel)-1 { + // 获取空格右侧的子字符串 + whiteLabel = whiteLabel[lastSpaceIndex+1:] + } + } + var actiontype keyboard.ActionType var permission *keyboard.Permission var actiondata string //检查是否设置了enter数组 enter := checkDataLabelPrefix(dataLabel) switch whiteLabel { - case "邀请机器人": + case "邀请机器人", "邀请我", "添加我": botuin := config.GetUinStr() botappid := config.GetAppIDStr() boturl := BuildQQBotShareLink(botuin, botappid) diff --git a/template/config_template.go b/template/config_template.go index cda74bc4..d41bbde2 100644 --- a/template/config_template.go +++ b/template/config_template.go @@ -144,6 +144,10 @@ settings: bind_prefix : "/bind" #需设置 #增强配置项 master_id 可触发 me_prefix : "/me" #需设置 #增强配置项 master_id 可触发 unlock_prefix : "/unlock" #频道私信卡住了? gsk可以帮到你 在任意子频道发送unlock 你会收到来自机器人的频道私信 + link_prefix : "/link" #友情链接配置 配置custom_template_id后可用(https://www.yuque.com/km57bt/hlhnxg/tzbr84y59dbz6pib) + link_bots : ["",""] #发送友情链接时 下方按钮携带的机器人 格式 "appid-qq-name","appid-qq-name" + link_text : "" #友情链接文本 不可为空! + link_pic : "" #友情链接图片 可为空 需url图片 可带端口 不填可能会有显示错误 #穿透\cos\oss类配置(可选!) frp_port : "0" #不使用请保持为0,frp的端口,frp有内外端口,请在frp软件设置gensokyo的port,并将frp显示的对外端口填入这里