From a291d98c24f797dff44b41565bda4797cc5a97ba Mon Sep 17 00:00:00 2001 From: SanaeFox <36219542+Hoshinonyaruko@users.noreply.github.com> Date: Mon, 8 Jan 2024 13:47:44 +0800 Subject: [PATCH 1/6] beta122 (#291) --- readme.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/readme.md b/readme.md index d8f7a8ee..90341e90 100644 --- a/readme.md +++ b/readme.md @@ -40,6 +40,9 @@ _✨ 基于 [OneBot](https://github.com/howmanybots/onebot/blob/master/README.md · 参与贡献
+ ## 引用 - [`tencent-connect/botgo`](https://github.com/tencent-connect/botgo): 本项目引用了此项目,并做了一点改动. From b7a2dd49701359e653be1418d80bfe6af0f08c4b Mon Sep 17 00:00:00 2001 From: SanaeFox <36219542+Hoshinonyaruko@users.noreply.github.com> Date: Thu, 11 Jan 2024 17:14:52 +0800 Subject: [PATCH 2/6] Beta123 (#294) * beta122 * beta123 --- handlers/send_msg.go | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/handlers/send_msg.go b/handlers/send_msg.go index 1982ae06..b06dc2b5 100644 --- a/handlers/send_msg.go +++ b/handlers/send_msg.go @@ -33,13 +33,15 @@ func HandleSendMsg(client callapi.Client, api openapi.OpenAPI, apiv2 openapi.Ope if msgType == "" { msgType = GetMessageTypeByUserid(config.GetAppIDStr(), message.Params.UserID) } - //新增 内存获取不到从数据库获取 + //顺序有讲究 if msgType == "" { - msgType = GetMessageTypeByUseridV2(message.Params.UserID) + msgType = GetMessageTypeByGroupidV2(message.Params.GroupID) } + //新增 内存获取不到从数据库获取 if msgType == "" { - msgType = GetMessageTypeByGroupidV2(message.Params.GroupID) + msgType = GetMessageTypeByUseridV2(message.Params.UserID) } + var idInt64 int64 var err error From 72e841f4f64d24eb802087b9251274418b7a53ff Mon Sep 17 00:00:00 2001 From: SanaeFox <36219542+Hoshinonyaruko@users.noreply.github.com> Date: Thu, 11 Jan 2024 19:44:27 +0800 Subject: [PATCH 3/6] Beta124 (#295) * beta122 * beta123 * beta124 * beta124 --- handlers/send_msg.go | 11 +++++++---- handlers/send_private_msg.go | 12 ++++++------ 2 files changed, 13 insertions(+), 10 deletions(-) diff --git a/handlers/send_msg.go b/handlers/send_msg.go index b06dc2b5..04870d4c 100644 --- a/handlers/send_msg.go +++ b/handlers/send_msg.go @@ -41,14 +41,16 @@ func HandleSendMsg(client callapi.Client, api openapi.OpenAPI, apiv2 openapi.Ope if msgType == "" { msgType = GetMessageTypeByUseridV2(message.Params.UserID) } - - var idInt64 int64 + + var idInt64, idInt642 int64 var err error if message.Params.GroupID != "" { - idInt64, err = ConvertToInt64(message.Params.GroupID) + idInt64, _ = ConvertToInt64(message.Params.GroupID) + idInt642, _ = ConvertToInt64(message.Params.UserID) } else if message.Params.UserID != "" { - idInt64, err = ConvertToInt64(message.Params.UserID) + idInt64, _ = ConvertToInt64(message.Params.UserID) + idInt642, _ = ConvertToInt64(message.Params.GroupID) } //设置递归 对直接向gsk发送action时有效果 @@ -101,6 +103,7 @@ func HandleSendMsg(client callapi.Client, api openapi.OpenAPI, apiv2 openapi.Ope //重置递归类型 if echo.GetMapping(idInt64) <= 0 { echo.AddMsgType(config.GetAppIDStr(), idInt64, "") + echo.AddMsgType(config.GetAppIDStr(), idInt642, "") } echo.AddMapping(idInt64, echo.GetMapping(idInt64)-1) diff --git a/handlers/send_private_msg.go b/handlers/send_private_msg.go index 43bcba67..6dcefe7d 100644 --- a/handlers/send_private_msg.go +++ b/handlers/send_private_msg.go @@ -29,14 +29,14 @@ func HandleSendPrivateMsg(client callapi.Client, api openapi.OpenAPI, apiv2 open msgType = echo.GetMsgTypeByKey(echoStr) } - //如果获取不到 就用group_id获取信息类型 - if msgType == "" { - msgType = GetMessageTypeByGroupid(config.GetAppIDStr(), message.Params.GroupID) - } //如果获取不到 就用user_id获取信息类型 if msgType == "" { msgType = GetMessageTypeByUserid(config.GetAppIDStr(), message.Params.UserID) } + //顺序,私聊优先从UserID推断类型会更准确 + if msgType == "" { + msgType = GetMessageTypeByGroupid(config.GetAppIDStr(), message.Params.GroupID) + } //新增 内存获取不到从数据库获取 if msgType == "" { msgType = GetMessageTypeByUseridV2(message.Params.UserID) @@ -68,7 +68,7 @@ func HandleSendPrivateMsg(client callapi.Client, api openapi.OpenAPI, apiv2 open } switch msgType { - case "group_private","group": + case "group_private", "group": //私聊信息 var UserID string if config.GetIdmapPro() { @@ -266,7 +266,7 @@ func HandleSendPrivateMsg(client callapi.Client, api openapi.OpenAPI, apiv2 open retmsg, _ = SendResponse(client, err, &message) } } - case "guild_private","guild": + case "guild_private", "guild": //当收到发私信调用 并且来源是频道 retmsg, _ = HandleSendGuildChannelPrivateMsg(client, api, apiv2, message, nil, nil) default: From 88e408a4b7037be598d884186ca10c5b0ff844ee Mon Sep 17 00:00:00 2001 From: SanaeFox <36219542+Hoshinonyaruko@users.noreply.github.com> Date: Thu, 11 Jan 2024 20:09:03 +0800 Subject: [PATCH 4/6] Beta125 (#296) * beta122 * beta123 * beta124 * beta124 * beta124 --- handlers/send_msg.go | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/handlers/send_msg.go b/handlers/send_msg.go index 04870d4c..00960811 100644 --- a/handlers/send_msg.go +++ b/handlers/send_msg.go @@ -29,19 +29,23 @@ func HandleSendMsg(client callapi.Client, api openapi.OpenAPI, apiv2 openapi.Ope if msgType == "" { msgType = GetMessageTypeByGroupid(config.GetAppIDStr(), message.Params.GroupID) } + mylog.Printf("测试1:%v", msgType) //如果获取不到 就用user_id获取信息类型 if msgType == "" { msgType = GetMessageTypeByUserid(config.GetAppIDStr(), message.Params.UserID) } + mylog.Printf("测试2:%v", msgType) //顺序有讲究 if msgType == "" { msgType = GetMessageTypeByGroupidV2(message.Params.GroupID) } + mylog.Printf("测试3:%v", msgType) //新增 内存获取不到从数据库获取 if msgType == "" { msgType = GetMessageTypeByUseridV2(message.Params.UserID) } - + mylog.Printf("测试4:%v", msgType) + var idInt64, idInt642 int64 var err error From 2a28acfdd9e7b47f1f587b9a246b579b48787dcf Mon Sep 17 00:00:00 2001 From: SanaeFox <36219542+Hoshinonyaruko@users.noreply.github.com> Date: Thu, 11 Jan 2024 20:28:13 +0800 Subject: [PATCH 5/6] Beta127 (#297) * beta122 * beta123 * beta124 * beta124 * beta124 * beta127 --- handlers/send_guild_channel_msg.go | 7 ++----- handlers/send_msg.go | 8 -------- 2 files changed, 2 insertions(+), 13 deletions(-) diff --git a/handlers/send_guild_channel_msg.go b/handlers/send_guild_channel_msg.go index c84ed3e5..c625902b 100644 --- a/handlers/send_guild_channel_msg.go +++ b/handlers/send_guild_channel_msg.go @@ -34,20 +34,17 @@ func HandleSendGuildChannelMsg(client callapi.Client, api openapi.OpenAPI, apiv2 // 当 message.Echo 是字符串类型时执行此块 msgType = echo.GetMsgTypeByKey(echoStr) } - //如果获取不到 就用group_id获取信息类型 if msgType == "" { msgType = GetMessageTypeByGroupid(config.GetAppIDStr(), message.Params.GroupID) } - //如果获取不到 就用user_id获取信息类型 if msgType == "" { msgType = GetMessageTypeByUserid(config.GetAppIDStr(), message.Params.UserID) } - //新增 内存获取不到从数据库获取 if msgType == "" { - msgType = GetMessageTypeByUseridV2(message.Params.UserID) + msgType = GetMessageTypeByGroupidV2(message.Params.GroupID) } if msgType == "" { - msgType = GetMessageTypeByGroupidV2(message.Params.GroupID) + msgType = GetMessageTypeByUseridV2(message.Params.UserID) } //当不转换频道信息时(不支持频道私聊) if msgType == "" { diff --git a/handlers/send_msg.go b/handlers/send_msg.go index 00960811..f5ba50e9 100644 --- a/handlers/send_msg.go +++ b/handlers/send_msg.go @@ -25,26 +25,18 @@ func HandleSendMsg(client callapi.Client, api openapi.OpenAPI, apiv2 openapi.Ope // 当 message.Echo 是字符串类型时执行此块 msgType = echo.GetMsgTypeByKey(echoStr) } - //如果获取不到 就用group_id获取信息类型 if msgType == "" { msgType = GetMessageTypeByGroupid(config.GetAppIDStr(), message.Params.GroupID) } - mylog.Printf("测试1:%v", msgType) - //如果获取不到 就用user_id获取信息类型 if msgType == "" { msgType = GetMessageTypeByUserid(config.GetAppIDStr(), message.Params.UserID) } - mylog.Printf("测试2:%v", msgType) - //顺序有讲究 if msgType == "" { msgType = GetMessageTypeByGroupidV2(message.Params.GroupID) } - mylog.Printf("测试3:%v", msgType) - //新增 内存获取不到从数据库获取 if msgType == "" { msgType = GetMessageTypeByUseridV2(message.Params.UserID) } - mylog.Printf("测试4:%v", msgType) var idInt64, idInt642 int64 var err error From 5f2261e6976f6aebc967522c8cbde2cb3742245b Mon Sep 17 00:00:00 2001 From: SanaeFox <36219542+Hoshinonyaruko@users.noreply.github.com> Date: Fri, 12 Jan 2024 22:30:13 +0800 Subject: [PATCH 6/6] Beta128 (#298) * beta122 * beta123 * beta124 * beta124 * beta124 * beta127 * beta128 --- Processor/ProcessC2CMessage.go | 4 + Processor/ProcessChannelDirectMessage.go | 6 + Processor/ProcessGroupMessage.go | 2 + Processor/ProcessGuildATMessage.go | 9 +- Processor/ProcessGuildNormalMessage.go | 4 + botgo/dto/keyboard/keyboard.go | 4 + config/config.go | 26 ++++ echo/echo.go | 9 ++ handlers/send_group_msg.go | 185 +++++++++++++++++++---- images/format.go | 180 ++++++++++++++++++++++ template/config_template.go | 2 + 11 files changed, 404 insertions(+), 27 deletions(-) create mode 100644 images/format.go diff --git a/Processor/ProcessC2CMessage.go b/Processor/ProcessC2CMessage.go index 2b734017..465ae443 100644 --- a/Processor/ProcessC2CMessage.go +++ b/Processor/ProcessC2CMessage.go @@ -118,6 +118,8 @@ func (p *Processors) ProcessC2CMessage(data *dto.WSC2CMessageData) error { // 根据条件判断是否添加Echo字段 if config.GetTwoWayEcho() { privateMsg.Echo = echostr + //用向应用端(如果支持)发送echo,来确定客户端的send_msg对应的触发词原文 + echo.AddMsgIDv3(AppIDString, echostr, messageText) } // 将当前s和appid和message进行映射 echo.AddMsgID(AppIDString, s, data.ID) @@ -220,6 +222,8 @@ func (p *Processors) ProcessC2CMessage(data *dto.WSC2CMessageData) error { // 根据条件判断是否添加Echo字段 if config.GetTwoWayEcho() { groupMsg.Echo = echostr + //用向应用端(如果支持)发送echo,来确定客户端的send_msg对应的触发词原文 + echo.AddMsgIDv3(AppIDString, echostr, messageText) } // 获取MasterID数组 masterIDs := config.GetMasterID() diff --git a/Processor/ProcessChannelDirectMessage.go b/Processor/ProcessChannelDirectMessage.go index 53eb6ee7..e27faa94 100644 --- a/Processor/ProcessChannelDirectMessage.go +++ b/Processor/ProcessChannelDirectMessage.go @@ -141,6 +141,8 @@ func (p *Processors) ProcessChannelDirectMessage(data *dto.WSDirectMessageData) // 根据条件判断是否添加Echo字段 if config.GetTwoWayEcho() { privateMsg.Echo = echostr + //用向应用端(如果支持)发送echo,来确定客户端的send_msg对应的触发词原文 + echo.AddMsgIDv3(AppIDString, echostr, messageText) } // 将当前s和appid和message进行映射 echo.AddMsgID(AppIDString, s, data.ID) @@ -218,6 +220,8 @@ func (p *Processors) ProcessChannelDirectMessage(data *dto.WSDirectMessageData) // 根据条件判断是否添加Echo字段 if config.GetTwoWayEcho() { onebotMsg.Echo = echostr + //用向应用端(如果支持)发送echo,来确定客户端的send_msg对应的触发词原文 + echo.AddMsgIDv3(AppIDString, echostr, messageText) } // 获取MasterID数组 masterIDs := config.GetMasterID() @@ -368,6 +372,8 @@ func (p *Processors) ProcessChannelDirectMessage(data *dto.WSDirectMessageData) // 根据条件判断是否添加Echo字段 if config.GetTwoWayEcho() { groupMsg.Echo = echostr + //用向应用端(如果支持)发送echo,来确定客户端的send_msg对应的触发词原文 + echo.AddMsgIDv3(AppIDString, echostr, messageText) } // 获取MasterID数组 masterIDs := config.GetMasterID() diff --git a/Processor/ProcessGroupMessage.go b/Processor/ProcessGroupMessage.go index 28387413..87d2e8a1 100644 --- a/Processor/ProcessGroupMessage.go +++ b/Processor/ProcessGroupMessage.go @@ -135,6 +135,8 @@ func (p *Processors) ProcessGroupMessage(data *dto.WSGroupATMessageData) error { // 根据条件判断是否添加Echo字段 if config.GetTwoWayEcho() { groupMsg.Echo = echostr + //用向应用端(如果支持)发送echo,来确定客户端的send_msg对应的触发词原文 + echo.AddMsgIDv3(AppIDString, echostr, messageText) } // 获取MasterID数组 masterIDs := config.GetMasterID() diff --git a/Processor/ProcessGuildATMessage.go b/Processor/ProcessGuildATMessage.go index e043dfba..c9426919 100644 --- a/Processor/ProcessGuildATMessage.go +++ b/Processor/ProcessGuildATMessage.go @@ -75,7 +75,12 @@ func (p *Processors) ProcessGuildATMessage(data *dto.WSATMessageData) error { SubType: "channel", Time: t.Unix(), Avatar: data.Author.Avatar, - Echo: echostr, + } + // 根据条件判断是否添加Echo字段 + if config.GetTwoWayEcho() { + onebotMsg.Echo = echostr + //用向应用端(如果支持)发送echo,来确定客户端的send_msg对应的触发词原文 + echo.AddMsgIDv3(AppIDString, echostr, messageText) } // 获取MasterID数组 masterIDs := config.GetMasterID() @@ -226,6 +231,8 @@ func (p *Processors) ProcessGuildATMessage(data *dto.WSATMessageData) error { // 根据条件判断是否添加Echo字段 if config.GetTwoWayEcho() { groupMsg.Echo = echostr + //用向应用端(如果支持)发送echo,来确定客户端的send_msg对应的触发词原文 + echo.AddMsgIDv3(AppIDString, echostr, messageText) } // 获取MasterID数组 masterIDs := config.GetMasterID() diff --git a/Processor/ProcessGuildNormalMessage.go b/Processor/ProcessGuildNormalMessage.go index 71b66abc..da7c32a6 100644 --- a/Processor/ProcessGuildNormalMessage.go +++ b/Processor/ProcessGuildNormalMessage.go @@ -78,6 +78,8 @@ func (p *Processors) ProcessGuildNormalMessage(data *dto.WSMessageData) error { // 根据条件判断是否添加Echo字段 if config.GetTwoWayEcho() { onebotMsg.Echo = echostr + //用向应用端(如果支持)发送echo,来确定客户端的send_msg对应的触发词原文 + echo.AddMsgIDv3(AppIDString, echostr, messageText) } // 获取MasterID数组 masterIDs := config.GetMasterID() @@ -225,6 +227,8 @@ func (p *Processors) ProcessGuildNormalMessage(data *dto.WSMessageData) error { // 根据条件判断是否添加Echo字段 if config.GetTwoWayEcho() { groupMsg.Echo = echostr + //用向应用端(如果支持)发送echo,来确定客户端的send_msg对应的触发词原文 + echo.AddMsgIDv3(AppIDString, echostr, messageText) } // 获取MasterID数组 masterIDs := config.GetMasterID() diff --git a/botgo/dto/keyboard/keyboard.go b/botgo/dto/keyboard/keyboard.go index f53b4a74..b6c26149 100644 --- a/botgo/dto/keyboard/keyboard.go +++ b/botgo/dto/keyboard/keyboard.go @@ -60,6 +60,10 @@ type Action struct { ClickLimit uint32 `json:"click_limit,omitempty"` // 可点击的次数, 默认不限 Data string `json:"data,omitempty"` // 操作相关数据 AtBotShowChannelList bool `json:"at_bot_show_channel_list,omitempty"` // false:当前 true:弹出展示子频道选择器 + UnsupportTips string `json:"unsupport_tips"` //2024-1-12 新增字段 + AnChor int `json:"anchor"` //本字段仅在指令按钮下有效,设置后后会忽略 action.enter 配置。设置为 1 时 ,点击按钮自动唤起启手Q选图器,其他值暂无效果。(仅支持手机端版本 8983+ 的单聊场景,桌面端不支持) + Enter bool `json:"enter"` //指令按钮可用,点击按钮后直接自动发送 data,默认 false。支持版本 8983 + Reply bool `json:"reply"` //指令按钮可用,指令是否带引用回复本消息,默认 false。支持版本 8983 } // Permission 按纽操作权限 diff --git a/config/config.go b/config/config.go index 021ed06f..9f8a7940 100644 --- a/config/config.go +++ b/config/config.go @@ -131,6 +131,8 @@ type Settings struct { WhiteEnable []bool `yaml:"white_enable"` IdentifyAppids []int64 `yaml:"identify_appids"` TransFormApiIds bool `yaml:"transform_api_ids"` + CustomTemplateID string `yaml:"custom_template_id"` + KeyBoardID string `yaml:"keyboard_id"` } // LoadConfig 从文件中加载配置并初始化单例配置 @@ -1650,3 +1652,27 @@ func GetTransFormApiIds() bool { } return instance.Settings.TransFormApiIds } + +// 获取 CustomTemplateID 的值 +func GetCustomTemplateID() string { + mu.Lock() + defer mu.Unlock() + + if instance == nil { + mylog.Println("Warning: instance is nil when trying to get CustomTemplateID.") + return "" + } + return instance.Settings.CustomTemplateID +} + +// 获取 KeyBoardIDD 的值 +func GetKeyBoardID() string { + mu.Lock() + defer mu.Unlock() + + if instance == nil { + mylog.Println("Warning: instance is nil when trying to get KeyBoardID.") + return "" + } + return instance.Settings.KeyBoardID +} diff --git a/echo/echo.go b/echo/echo.go index c898215a..fc840e4f 100644 --- a/echo/echo.go +++ b/echo/echo.go @@ -106,6 +106,15 @@ func AddMsgIDv3(appid string, s string, msgID string) { globalEchoMapping.msgIDMapping[key] = msgID } +// GetMsgIDv3 返回给定appid和s的msgID +func GetMsgIDv3(appid string, s string) string { + key := globalEchoMapping.GenerateKeyv3(appid, s) + globalEchoMapping.mu.Lock() + defer globalEchoMapping.mu.Unlock() + + return globalEchoMapping.msgIDMapping[key] +} + // 添加group和userid对应的messageid func AddMsgIDv2(appid string, groupid int64, userid int64, msgID string) { key := globalEchoMapping.GenerateKeyv2(appid, groupid, userid) diff --git a/handlers/send_group_msg.go b/handlers/send_group_msg.go index 9905aada..93b869e7 100644 --- a/handlers/send_group_msg.go +++ b/handlers/send_group_msg.go @@ -4,10 +4,12 @@ import ( "bytes" "context" "encoding/base64" + "fmt" "io" "net/http" "os" "strconv" + "strings" "time" "github.com/hoshinonyaruko/gensokyo/callapi" @@ -18,6 +20,7 @@ import ( "github.com/hoshinonyaruko/gensokyo/mylog" "github.com/hoshinonyaruko/gensokyo/silk" "github.com/tencent-connect/botgo/dto" + "github.com/tencent-connect/botgo/dto/keyboard" "github.com/tencent-connect/botgo/openapi" ) @@ -33,22 +36,18 @@ func HandleSendGroupMsg(client callapi.Client, api openapi.OpenAPI, apiv2 openap // 当 message.Echo 是字符串类型时执行此块 msgType = echo.GetMsgTypeByKey(echoStr) } - //如果获取不到 就用user_id获取信息类型 - if msgType == "" { - msgType = GetMessageTypeByUserid(config.GetAppIDStr(), message.Params.UserID) - } - - //如果获取不到 就用group_id获取信息类型 if msgType == "" { msgType = GetMessageTypeByGroupid(config.GetAppIDStr(), message.Params.GroupID) } - //新增 内存获取不到从数据库获取 if msgType == "" { - msgType = GetMessageTypeByUseridV2(message.Params.UserID) + msgType = GetMessageTypeByUserid(config.GetAppIDStr(), message.Params.UserID) } if msgType == "" { msgType = GetMessageTypeByGroupidV2(message.Params.GroupID) } + if msgType == "" { + msgType = GetMessageTypeByUseridV2(message.Params.UserID) + } mylog.Printf("send_group_msg获取到信息类型:%v", msgType) var idInt64 int64 var err error @@ -202,26 +201,52 @@ func HandleSendGroupMsg(client callapi.Client, api openapi.OpenAPI, apiv2 openap mylog.Printf("Error: Expected RichMediaMessage type for key ") return "", nil } - // 上传图片并获取FileInfo - fileInfo, err := uploadMedia(context.TODO(), message.Params.GroupID.(string), richMediaMessage, apiv2) - if err != nil { - mylog.Printf("上传图片失败: %v", err) - return "", nil // 或其他错误处理 - } - // 创建包含文本和图像信息的消息 - msgseq = echo.GetMappingSeq(messageID) - echo.AddMappingSeq(messageID, msgseq+1) - groupMessage := &dto.MessageToCreate{ - Content: messageText, // 添加文本内容 - Media: dto.Media{ - FileInfo: fileInfo, // 添加图像信息 - }, - MsgID: messageID, - MsgSeq: msgseq, - MsgType: 7, // 假设7是组合消息类型 + var groupMessage *dto.MessageToCreate + var transmd bool + var md *dto.Markdown + var kb *keyboard.MessageKeyboard + //判断是否需要自动转换md + if config.GetTwoWayEcho() { + md, kb, transmd = auto_md(message, messageText, richMediaMessage) } - groupMessage.Timestamp = time.Now().Unix() // 设置时间戳 + //如果没有转换成md发送 + if !transmd { + // 上传图片并获取FileInfo + fileInfo, err := uploadMedia(context.TODO(), message.Params.GroupID.(string), richMediaMessage, apiv2) + if err != nil { + mylog.Printf("上传图片失败: %v", err) + return "", nil // 或其他错误处理 + } + // 创建包含文本和图像信息的消息 + msgseq = echo.GetMappingSeq(messageID) + echo.AddMappingSeq(messageID, msgseq+1) + groupMessage = &dto.MessageToCreate{ + Content: messageText, // 添加文本内容 + Media: dto.Media{ + FileInfo: fileInfo, // 添加图像信息 + }, + MsgID: messageID, + MsgSeq: msgseq, + MsgType: 7, // 假设7是组合消息类型 + } + groupMessage.Timestamp = time.Now().Unix() // 设置时间戳 + } else { + //将kb和md组合成groupMessage并用MsgType=2发送 + + msgseq = echo.GetMappingSeq(messageID) + echo.AddMappingSeq(messageID, msgseq+1) + groupMessage = &dto.MessageToCreate{ + Content: "markdown", // 添加文本内容 + MsgID: messageID, + MsgSeq: msgseq, + Markdown: md, + Keyboard: kb, + MsgType: 2, // 假设7是组合消息类型 + } + groupMessage.Timestamp = time.Now().Unix() // 设置时间戳 + + } // 发送组合消息 ret, err = apiv2.PostGroupMessage(context.TODO(), message.Params.GroupID.(string), groupMessage) if err != nil { @@ -1020,3 +1045,111 @@ func SendStackMessages(apiv2 openapi.OpenAPI, messageid string, originalGroupID } } + +func auto_md(message callapi.ActionMessage, messageText string, richMediaMessage *dto.RichMediaMessage) (md *dto.Markdown, kb *keyboard.MessageKeyboard, transmd bool) { + if echoStr, ok := message.Echo.(string); ok { + // 当 message.Echo 是字符串类型时才执行此块 + msg_on_touch := echo.GetMsgIDv3(config.GetAppIDStr(), echoStr) + mylog.Printf("msg_on_touch:%v", msg_on_touch) + // 判断是否是 GetVisualkPrefixs 数组开头的文本 + visualkPrefixs := config.GetVisualkPrefixs() + var matchedPrefix *config.VisualPrefixConfig + // 去掉前缀开头的* + for i, vp := range visualkPrefixs { + if strings.HasPrefix(vp.Prefix, "*") { + visualkPrefixs[i].Prefix = strings.TrimPrefix(vp.Prefix, "*") + } + } + + for _, vp := range visualkPrefixs { + if strings.HasPrefix(msg_on_touch, vp.Prefix) { + if len(msg_on_touch) >= len(vp.Prefix) { + if msg_on_touch != " " { + transmd = true + matchedPrefix = &vp + break // 匹配到了 + } + } + } + } + if transmd { + //将messageText和groupReply组合成一个md + // 处理 Markdown + CustomTemplateID := config.GetCustomTemplateID() + imgURL := richMediaMessage.URL + height, width, err := images.GetImageDimensions(imgURL) + if err != nil { + mylog.Printf("获取图片宽高出错") + } + imgDesc := fmt.Sprintf("图片 #%dpx #%dpx", width, height) + // 检查messageText是否以\r开头 + if !strings.HasPrefix(messageText, "\r") { + messageText = "\r" + messageText + } + // 创建 MarkdownParams 的实例 + mdParams := []*dto.MarkdownParams{ + {Key: "text_start", Values: []string{" "}}, //空着 + {Key: "img_dec", Values: []string{imgDesc}}, + {Key: "img_url", Values: []string{imgURL}}, + {Key: "text_end", Values: []string{messageText}}, + } + // 组合模板 Markdown + md = &dto.Markdown{ + CustomTemplateID: CustomTemplateID, + Params: mdParams, + } + whiteList := matchedPrefix.WhiteList + // 创建 CustomKeyboard + customKeyboard := &keyboard.CustomKeyboard{ + Rows: []*keyboard.Row{}, + } + + var currentRow *keyboard.Row + buttonCount := 0 // 当前行的按钮计数 + + for _, whiteLabel := range whiteList { + // 使用 strconv.Atoi 检查 whiteLabel 是否为纯数字 + if _, err := strconv.Atoi(whiteLabel); err == nil { + // 如果没有错误,表示 whiteLabel 是一个数字,因此忽略这个元素并继续下一个迭代 + continue + } + + // 创建按钮 + button := &keyboard.Button{ + RenderData: &keyboard.RenderData{ + Label: whiteLabel, + VisitedLabel: whiteLabel, + Style: 1, + }, + Action: &keyboard.Action{ + Type: 2, // 帮用户输入二级指令 + Permission: &keyboard.Permission{ + Type: 2, //所有人可操作 + }, + Data: msg_on_touch + " " + whiteLabel, + 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++ + } + + // 在循环结束后,最后一行可能不满4个按钮,但已经被正确处理 + + // 创建 MessageKeyboard 并设置其 Content + kb = &keyboard.MessageKeyboard{ + Content: customKeyboard, + } + } + } + return md, kb, transmd +} diff --git a/images/format.go b/images/format.go new file mode 100644 index 00000000..a5cb61c2 --- /dev/null +++ b/images/format.go @@ -0,0 +1,180 @@ +package images + +import ( + "bytes" + "encoding/binary" + "fmt" + "io" + "net/http" + "strings" + "time" +) + +// 宽度 高度 +func GetImageDimensions(url string) (int, int, error) { + if strings.HasSuffix(url, ".png") { + return getPNGDimensions(url) + } else if strings.HasSuffix(url, ".jpg") || strings.HasSuffix(url, ".jpeg") { + return getJpegDimensions(url) + } else if strings.HasSuffix(url, ".gif") { + return getGIFDimensions(url) + } + return 0, 0, fmt.Errorf("unsupported image format") +} + +func getPNGDimensions(url string) (int, int, error) { + resp, err := http.Get(url) + if err != nil { + return 0, 0, fmt.Errorf("error occurred while making request: %w", err) + } + defer resp.Body.Close() + + if resp.StatusCode != http.StatusOK { + return 0, 0, fmt.Errorf("failed to download image with status code: %d", resp.StatusCode) + } + + // 只读取前33字节 + buffer := make([]byte, 33) + _, err = io.ReadFull(resp.Body, buffer) + if err != nil { + return 0, 0, fmt.Errorf("error occurred while reading the header: %w", err) + } + + // 检查PNG文件头 + if bytes.HasPrefix(buffer, []byte("\x89PNG\r\n\x1a\n")) { + var width, height int32 + reader := bytes.NewReader(buffer[16:24]) + err := binary.Read(reader, binary.BigEndian, &width) + if err != nil { + return 0, 0, fmt.Errorf("error occurred while reading width: %w", err) + } + err = binary.Read(reader, binary.BigEndian, &height) + if err != nil { + return 0, 0, fmt.Errorf("error occurred while reading height: %w", err) + } + return int(width), int(height), nil + } + + width, height, err := getJpegDimensions(url) + return width, height, err +} + +func getJpegDimensions(url string) (int, int, error) { + response, err := http.Get(url) + if err != nil { + return 0, 0, fmt.Errorf("error occurred while making request: %w", err) + } + defer response.Body.Close() + + if response.StatusCode != 200 { + return 0, 0, fmt.Errorf("failed to download image with status code: %d", response.StatusCode) + } + + bytesRead := 0 // 用于跟踪读取的字节数 + for { + b := make([]byte, 1) + _, err := response.Body.Read(b) + bytesRead++ // 更新读取的字节数 + if err == io.EOF { + return 0, 0, fmt.Errorf("reached end of file before finding dimensions") + } else if err != nil { + return 0, 0, fmt.Errorf("error occurred while reading byte: %w", err) + } + + if b[0] == 0xFF { + nextByte := make([]byte, 1) + _, err := response.Body.Read(nextByte) + if err == io.EOF { + return 0, 0, fmt.Errorf("reached end of file before finding SOI marker") + } else if err != nil { + return 0, 0, fmt.Errorf("error occurred while reading next byte: %w", err) + } + + if nextByte[0] == 0xD8 { // SOI + continue + } else if (nextByte[0] >= 0xC0 && nextByte[0] <= 0xCF) && nextByte[0] != 0xC4 && nextByte[0] != 0xC8 && nextByte[0] != 0xCC { + c := make([]byte, 3) + _, err := response.Body.Read(c) + if err != nil { + return 0, 0, fmt.Errorf("error occurred while skipping bytes: %w", err) + } + heightBytes := make([]byte, 2) + widthBytes := make([]byte, 2) + _, err = response.Body.Read(heightBytes) + if err != nil { + return 0, 0, fmt.Errorf("error occurred while reading height: %w", err) + } + _, err = response.Body.Read(widthBytes) + if err != nil { + return 0, 0, fmt.Errorf("error occurred while reading width: %w", err) + } + // 使用binary.BigEndian.Uint16将字节转换为uint16 + height := binary.BigEndian.Uint16(heightBytes) + width := binary.BigEndian.Uint16(widthBytes) + return int(height), int(width), nil + } else { + time.Sleep(5 * time.Millisecond) + lengthBytes := make([]byte, 2) + _, err := response.Body.Read(lengthBytes) + if err != nil { + return 0, 0, fmt.Errorf("error occurred while reading segment length: %w", err) + } + + length := binary.BigEndian.Uint16(lengthBytes) + bytesToSkip := int(length) - 2 + + // 循环读取并跳过指定的字节数 + for bytesToSkip > 0 { + bufferSize := bytesToSkip + if bufferSize > 512 { // 可以调整这个值以优化性能和内存使用 + bufferSize = 512 //如果成功率低 就继续减少它 + } + buffer := make([]byte, bufferSize) + n, err := response.Body.Read(buffer) + if err != nil { + if err == io.EOF { + break // 数据流结束 + } + return 0, 0, fmt.Errorf("error occurred while skipping segment: %w", err) + } + bytesToSkip -= n + } + } + } + } +} + +func getGIFDimensions(url string) (int, int, error) { + resp, err := http.Get(url) + if err != nil { + return 0, 0, fmt.Errorf("error occurred while making request: %w", err) + } + defer resp.Body.Close() + + if resp.StatusCode != http.StatusOK { + return 0, 0, fmt.Errorf("failed to download image with status code: %d", resp.StatusCode) + } + + // 仅读取前10字节 + buffer := make([]byte, 10) + _, err = io.ReadFull(resp.Body, buffer) + if err != nil { + return 0, 0, fmt.Errorf("error occurred while reading the header: %w", err) + } + + if bytes.HasPrefix(buffer, []byte("GIF")) { + var width, height int16 + reader := bytes.NewReader(buffer[6:10]) + err := binary.Read(reader, binary.LittleEndian, &width) + if err != nil { + return 0, 0, fmt.Errorf("error occurred while reading width: %w", err) + } + err = binary.Read(reader, binary.LittleEndian, &height) + if err != nil { + return 0, 0, fmt.Errorf("error occurred while reading height: %w", err) + } + return int(width), int(height), nil + } + + return 0, 0, fmt.Errorf("not a valid GIF file") +} diff --git a/template/config_template.go b/template/config_template.go index f01a81d8..bec2f464 100644 --- a/template/config_template.go +++ b/template/config_template.go @@ -128,6 +128,8 @@ settings: custom_bot_name : "Gensokyo全域机器人" #自定义机器人名字,会在api调用中返回,默认Gensokyo全域机器人 twoway_echo : false #是否采用双向echo,根据机器人选择,獭獭\早苗 true 红色问答\椛椛 或者其他 请使用 false + custom_template_id : "" #自动转换图文信息到md所需要的id *需要应用端支持双方向echo + keyboard_id : "" #自动转换图文信息到md所需要的按钮id *需要应用端支持双方向echo lazy_message_id : false #false=message_id 条条准确对应 true=message_id 按时间范围随机对应(适合主动推送bot)前提,有足够多的活跃信息刷新id池 visible_ip : false #转换url时,如果server_dir是ip true将以ip形式发出url 默认隐藏url 将server_dir配置为自己域名可以转换url