From ca914e2f1047c5e06160a8a0b941fdccb2f82e80 Mon Sep 17 00:00:00 2001 From: cosmo Date: Mon, 4 Mar 2024 22:26:33 +0800 Subject: [PATCH 01/33] beta338 --- readme.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/readme.md b/readme.md index 90341e90..56be0d40 100644 --- a/readme.md +++ b/readme.md @@ -45,7 +45,7 @@ _✨ 基于 [OneBot](https://github.com/howmanybots/onebot/blob/master/README.md

## 引用 -- [`tencent-connect/botgo`](https://github.com/tencent-connect/botgo): 本项目引用了此项目,并做了一点改动. +- [`tencent-connect/botgo`](https://github.com/tencent-connect/botgo): 本项目引用了此项目,并做了一些改动. ## 兼容性 gensokyo兼容 [OneBot-v11](https://github.com/botuniverse/onebot-11) ,并在其基础上做了一些扩展,详情请看 OneBot 的文档。 From 62e6985c471557d623d073dd66af0ad48b034510 Mon Sep 17 00:00:00 2001 From: cosmo Date: Tue, 5 Mar 2024 23:58:15 +0800 Subject: [PATCH 02/33] beta319 --- config/config.go | 13 +++++++++++++ main.go | 5 +++-- template/config_template.go | 3 ++- 3 files changed, 18 insertions(+), 3 deletions(-) diff --git a/config/config.go b/config/config.go index 205fc97e..e602bb82 100644 --- a/config/config.go +++ b/config/config.go @@ -151,6 +151,7 @@ type Settings struct { AutoPutInteraction bool `yaml:"auto_put_interaction"` PutInteractionDelay int `yaml:"put_interaction_delay"` ImgUpApiVtv2 bool `yaml:"img_up_api_ntv2"` + Fix11300 bool `yaml:"fix_11300"` } // LoadConfig 从文件中加载配置并初始化单例配置 @@ -1870,3 +1871,15 @@ func GetImgUpApiVtv2() bool { } return instance.Settings.ImgUpApiVtv2 } + +// 获取Fix11300开关 +func GetFix11300() bool { + mu.Lock() + defer mu.Unlock() + + if instance == nil { + mylog.Println("Warning: instance is nil when trying to Fix11300 value.") + return false + } + return instance.Settings.Fix11300 +} diff --git a/main.go b/main.go index 1cbe9545..d3282d49 100644 --- a/main.go +++ b/main.go @@ -155,8 +155,9 @@ func main() { } configURL := config.GetDevelop_Acdir() + fix11300 := config.GetFix11300() var me *dto.User - if configURL == "" { // 执行API请求 显示机器人信息 + if configURL == "" && !fix11300 { // 执行API请求 显示机器人信息 me, err = api.Me(ctx) // Adjusted to pass only the context if err != nil { log.Printf("Error fetching bot details: %v\n", err) @@ -168,7 +169,7 @@ func main() { log.Printf("自定义ac地址模式...请从日志手动获取bot的真实id并设置,不然at会不正常") } if !nologin { - if configURL == "" { //初始化handlers + if configURL == "" && !fix11300 { //初始化handlers handlers.BotID = me.ID } else { //初始化handlers handlers.BotID = config.GetDevBotid() diff --git a/template/config_template.go b/template/config_template.go index 103b418f..a73f3442 100644 --- a/template/config_template.go +++ b/template/config_template.go @@ -134,7 +134,8 @@ settings: shard_id: 0 #当前分片id 默认从0开始,详细请看 https://bot.q.qq.com/wiki/develop/api/gateway/reference.html auto_put_interaction : false #自动回应按钮回调的/interactions/{interaction_id} 注本api需要邮件申请,详细方法参考群公告:196173384 put_interaction_delay : 0 #单位毫秒 表示回应已收到回调类型的按钮的毫秒数 会按用户进行区分 非全局delay - img_up_api_ntv2: false #gsk内建图片上传api 是否将图片转换为ntqq图床url(md发图用,自行调用)文档: + img_up_api_ntv2: false #gsk内建图片上传api 是否将图片转换为ntqq图床url(md发图用,自行调用)文档:https://www.yuque.com/km57bt/hlhnxg/ig2nk88fccykn6dm + fix_11300: false #修复11300报错,需要在develop_bot_id填入自己机器人的appid. 11300原因暂时未知,临时修复方案. title : "Gensokyo © 2023 - Hoshinonyaruko" #程序的标题 如果多个机器人 可根据标题区分 custom_bot_name : "Gensokyo全域机器人" #自定义机器人名字,会在api调用中返回,默认Gensokyo全域机器人 From 8c8792c75b786d03672bf570e22b315d10dc37c1 Mon Sep 17 00:00:00 2001 From: cosmo Date: Thu, 7 Mar 2024 18:54:58 +0800 Subject: [PATCH 03/33] beta340 --- Processor/ProcessGroupDelBot.go | 3 + config/config.go | 13 ++++ handlers/get_group_list.go | 12 +++- handlers/send_group_msg.go | 39 ++++++++++-- handlers/send_guild_channel_forum.go | 37 ++++++++++-- handlers/send_guild_channel_msg.go | 36 +++++++++-- handlers/send_msg.go | 60 ++++++++++++++++--- handlers/send_private_msg.go | 40 ++++++++++--- idmap/service.go | 89 +++++++++++++++++++++++----- server/getIDHandler.go | 17 +++++- template/config_template.go | 1 + 11 files changed, 300 insertions(+), 47 deletions(-) diff --git a/Processor/ProcessGroupDelBot.go b/Processor/ProcessGroupDelBot.go index 34803c29..5dfe2e65 100644 --- a/Processor/ProcessGroupDelBot.go +++ b/Processor/ProcessGroupDelBot.go @@ -2,6 +2,7 @@ package Processor import ( + "fmt" "strconv" "github.com/hoshinonyaruko/gensokyo/config" @@ -50,6 +51,8 @@ func (p *Processors) ProcessGroupDelBot(data *dto.GroupAddBotEvent) error { return nil } mylog.Printf("Bot被[%v]从群[%v]移出", userid64, GroupID64) + //从数据库删除群数据(仅删除类型缓存,再次加入会刷新) + idmap.DeleteConfigv2(fmt.Sprint(GroupID64), "type") Notice = GroupNoticeEvent{ GroupID: GroupID64, NoticeType: "group_decrease", diff --git a/config/config.go b/config/config.go index e602bb82..4f03406e 100644 --- a/config/config.go +++ b/config/config.go @@ -152,6 +152,7 @@ type Settings struct { PutInteractionDelay int `yaml:"put_interaction_delay"` ImgUpApiVtv2 bool `yaml:"img_up_api_ntv2"` Fix11300 bool `yaml:"fix_11300"` + LotusWithoutIdmaps bool `yaml:"lotus_without_idmaps"` } // LoadConfig 从文件中加载配置并初始化单例配置 @@ -1883,3 +1884,15 @@ func GetFix11300() bool { } return instance.Settings.Fix11300 } + +// 获取LotusWithoutIdmaps开关 +func GetLotusWithoutIdmaps() bool { + mu.Lock() + defer mu.Unlock() + + if instance == nil { + mylog.Println("Warning: instance is nil when trying to LotusWithoutIdmaps value.") + return false + } + return instance.Settings.LotusWithoutIdmaps +} diff --git a/handlers/get_group_list.go b/handlers/get_group_list.go index 0acc64c4..61294642 100644 --- a/handlers/get_group_list.go +++ b/handlers/get_group_list.go @@ -110,20 +110,28 @@ func GetGroupList(client callapi.Client, api openapi.OpenAPI, apiv2 openapi.Open } // 将channel信息转换为Group对象并添加到groups for _, channel := range channels { - //转换ChannelID64 + // 转换ChannelID64 ChannelID64, err := idmap.StoreIDv2(channel.ID) if err != nil { mylog.Printf("Error storing ID: %v", err) } + + // 根据channel.Type添加前缀 + groupName := channel.Name + if channel.Type == dto.ChannelTypeText { + groupName = "&" + groupName + } + channelGroup := Group{ GroupCreateTime: 0, // 频道没有直接对应的创建时间字段 GroupID: ChannelID64, GroupLevel: 0, // 频道没有直接对应的级别字段 GroupMemo: "", // 频道没有直接对应的描述字段 - GroupName: channel.Name, + GroupName: groupName, MaxMemberCount: 0, // 频道没有直接对应的最大成员数字段 MemberCount: 0, // 频道没有直接对应的成员数字段 } + groupList.Data = append(groupList.Data, channelGroup) } } diff --git a/handlers/send_group_msg.go b/handlers/send_group_msg.go index 9329bcdc..3e4f0c6f 100644 --- a/handlers/send_group_msg.go +++ b/handlers/send_group_msg.go @@ -36,18 +36,47 @@ func HandleSendGroupMsg(client callapi.Client, api openapi.OpenAPI, apiv2 openap // 当 message.Echo 是字符串类型时执行此块 msgType = echo.GetMsgTypeByKey(echoStr) } - if msgType == "" { + // 检查GroupID是否为0 + checkZeroGroupID := func(id interface{}) bool { + switch v := id.(type) { + case int: + return v != 0 + case int64: + return v != 0 + case string: + return v != "0" // 检查字符串形式的0 + default: + return true // 如果不是int、int64或string,假定它不为0 + } + } + + // 检查UserID是否为0 + checkZeroUserID := func(id interface{}) bool { + switch v := id.(type) { + case int: + return v != 0 + case int64: + return v != 0 + case string: + return v != "0" // 同样检查字符串形式的0 + default: + return true // 如果不是int、int64或string,假定它不为0 + } + } + + if msgType == "" && message.Params.GroupID != nil && checkZeroGroupID(message.Params.GroupID) { msgType = GetMessageTypeByGroupid(config.GetAppIDStr(), message.Params.GroupID) } - if msgType == "" { + if msgType == "" && message.Params.UserID != nil && checkZeroUserID(message.Params.UserID) { msgType = GetMessageTypeByUserid(config.GetAppIDStr(), message.Params.UserID) } - if msgType == "" { + if msgType == "" && message.Params.GroupID != nil && checkZeroGroupID(message.Params.GroupID) { msgType = GetMessageTypeByGroupidV2(message.Params.GroupID) } - if msgType == "" { + if msgType == "" && message.Params.UserID != nil && checkZeroUserID(message.Params.UserID) { msgType = GetMessageTypeByUseridV2(message.Params.UserID) } + mylog.Printf("send_group_msg获取到信息类型:%v", msgType) var idInt64 int64 var err error @@ -493,7 +522,7 @@ func HandleSendGroupMsg(client callapi.Client, api openapi.OpenAPI, apiv2 openap echo.AddMsgType(config.GetAppIDStr(), idInt64, tryMessageTypes[echo.GetMapping(idInt64)-1]) delay := config.GetSendDelay() time.Sleep(time.Duration(delay) * time.Millisecond) - HandleSendGroupMsg(client, api, apiv2, messageCopy) + retmsg, _ = HandleSendGroupMsg(client, api, apiv2, messageCopy) } return retmsg, nil } diff --git a/handlers/send_guild_channel_forum.go b/handlers/send_guild_channel_forum.go index a812d869..7b1d5ee4 100644 --- a/handlers/send_guild_channel_forum.go +++ b/handlers/send_guild_channel_forum.go @@ -30,18 +30,47 @@ func HandleSendGuildChannelForum(client callapi.Client, api openapi.OpenAPI, api // 当 message.Echo 是字符串类型时执行此块 msgType = echo.GetMsgTypeByKey(echoStr) } - if msgType == "" { + // 检查GroupID是否为0 + checkZeroGroupID := func(id interface{}) bool { + switch v := id.(type) { + case int: + return v != 0 + case int64: + return v != 0 + case string: + return v != "0" // 检查字符串形式的0 + default: + return true // 如果不是int、int64或string,假定它不为0 + } + } + + // 检查UserID是否为0 + checkZeroUserID := func(id interface{}) bool { + switch v := id.(type) { + case int: + return v != 0 + case int64: + return v != 0 + case string: + return v != "0" // 同样检查字符串形式的0 + default: + return true // 如果不是int、int64或string,假定它不为0 + } + } + + if msgType == "" && message.Params.GroupID != nil && checkZeroGroupID(message.Params.GroupID) { msgType = GetMessageTypeByGroupid(config.GetAppIDStr(), message.Params.GroupID) } - if msgType == "" { + if msgType == "" && message.Params.UserID != nil && checkZeroUserID(message.Params.UserID) { msgType = GetMessageTypeByUserid(config.GetAppIDStr(), message.Params.UserID) } - if msgType == "" { + if msgType == "" && message.Params.GroupID != nil && checkZeroGroupID(message.Params.GroupID) { msgType = GetMessageTypeByGroupidV2(message.Params.GroupID) } - if msgType == "" { + if msgType == "" && message.Params.UserID != nil && checkZeroUserID(message.Params.UserID) { msgType = GetMessageTypeByUseridV2(message.Params.UserID) } + //当不转换频道信息时(不支持频道私聊) if msgType == "" { msgType = "forum" diff --git a/handlers/send_guild_channel_msg.go b/handlers/send_guild_channel_msg.go index 94bb86fc..a9e8a48b 100644 --- a/handlers/send_guild_channel_msg.go +++ b/handlers/send_guild_channel_msg.go @@ -34,16 +34,44 @@ func HandleSendGuildChannelMsg(client callapi.Client, api openapi.OpenAPI, apiv2 // 当 message.Echo 是字符串类型时执行此块 msgType = echo.GetMsgTypeByKey(echoStr) } - if msgType == "" { + // 检查GroupID是否为0 + checkZeroGroupID := func(id interface{}) bool { + switch v := id.(type) { + case int: + return v != 0 + case int64: + return v != 0 + case string: + return v != "0" // 检查字符串形式的0 + default: + return true // 如果不是int、int64或string,假定它不为0 + } + } + + // 检查UserID是否为0 + checkZeroUserID := func(id interface{}) bool { + switch v := id.(type) { + case int: + return v != 0 + case int64: + return v != 0 + case string: + return v != "0" // 同样检查字符串形式的0 + default: + return true // 如果不是int、int64或string,假定它不为0 + } + } + + if msgType == "" && message.Params.GroupID != nil && checkZeroGroupID(message.Params.GroupID) { msgType = GetMessageTypeByGroupid(config.GetAppIDStr(), message.Params.GroupID) } - if msgType == "" { + if msgType == "" && message.Params.UserID != nil && checkZeroUserID(message.Params.UserID) { msgType = GetMessageTypeByUserid(config.GetAppIDStr(), message.Params.UserID) } - if msgType == "" { + if msgType == "" && message.Params.GroupID != nil && checkZeroGroupID(message.Params.GroupID) { msgType = GetMessageTypeByGroupidV2(message.Params.GroupID) } - if msgType == "" { + if msgType == "" && message.Params.UserID != nil && checkZeroUserID(message.Params.UserID) { msgType = GetMessageTypeByUseridV2(message.Params.UserID) } //当不转换频道信息时(不支持频道私聊) diff --git a/handlers/send_msg.go b/handlers/send_msg.go index f95751f9..f9aca80a 100644 --- a/handlers/send_msg.go +++ b/handlers/send_msg.go @@ -25,28 +25,72 @@ func HandleSendMsg(client callapi.Client, api openapi.OpenAPI, apiv2 openapi.Ope // 当 message.Echo 是字符串类型时执行此块 msgType = echo.GetMsgTypeByKey(echoStr) } - if msgType == "" { + // 检查GroupID是否为0 + checkZeroGroupID := func(id interface{}) bool { + switch v := id.(type) { + case int: + return v != 0 + case int64: + return v != 0 + case string: + return v != "0" // 检查字符串形式的0 + default: + return true // 如果不是int、int64或string,假定它不为0 + } + } + + // 检查UserID是否为0 + checkZeroUserID := func(id interface{}) bool { + switch v := id.(type) { + case int: + return v != 0 + case int64: + return v != 0 + case string: + return v != "0" // 同样检查字符串形式的0 + default: + return true // 如果不是int、int64或string,假定它不为0 + } + } + + if msgType == "" && message.Params.GroupID != nil && checkZeroGroupID(message.Params.GroupID) { msgType = GetMessageTypeByGroupid(config.GetAppIDStr(), message.Params.GroupID) } - if msgType == "" { + if msgType == "" && message.Params.UserID != nil && checkZeroUserID(message.Params.UserID) { msgType = GetMessageTypeByUserid(config.GetAppIDStr(), message.Params.UserID) } - if msgType == "" { + if msgType == "" && message.Params.GroupID != nil && checkZeroGroupID(message.Params.GroupID) { msgType = GetMessageTypeByGroupidV2(message.Params.GroupID) } - if msgType == "" { + if msgType == "" && message.Params.UserID != nil && checkZeroUserID(message.Params.UserID) { msgType = GetMessageTypeByUseridV2(message.Params.UserID) } var idInt64, idInt642 int64 var err error + var tempErr error + if message.Params.GroupID != "" { - idInt64, err = ConvertToInt64(message.Params.GroupID) - idInt642, err = ConvertToInt64(message.Params.UserID) + idInt64, tempErr = ConvertToInt64(message.Params.GroupID) + if tempErr != nil { + err = tempErr + } + idInt642, tempErr = ConvertToInt64(message.Params.UserID) + if tempErr != nil { + err = tempErr + } + } else if message.Params.UserID != "" { - idInt64, err = ConvertToInt64(message.Params.UserID) - idInt642, err = ConvertToInt64(message.Params.GroupID) + idInt64, tempErr = ConvertToInt64(message.Params.UserID) + if tempErr != nil { + err = tempErr + } + idInt642, tempErr = ConvertToInt64(message.Params.GroupID) + if tempErr != nil { + err = tempErr + } + } //设置递归 对直接向gsk发送action时有效果 diff --git a/handlers/send_private_msg.go b/handlers/send_private_msg.go index 573e588b..e71c493b 100644 --- a/handlers/send_private_msg.go +++ b/handlers/send_private_msg.go @@ -27,20 +27,44 @@ func HandleSendPrivateMsg(client callapi.Client, api openapi.OpenAPI, apiv2 open // 当 message.Echo 是字符串类型时执行此块 msgType = echo.GetMsgTypeByKey(echoStr) } + // 检查GroupID是否为0 + checkZeroGroupID := func(id interface{}) bool { + switch v := id.(type) { + case int: + return v != 0 + case int64: + return v != 0 + case string: + return v != "0" // 检查字符串形式的0 + default: + return true // 如果不是int、int64或string,假定它不为0 + } + } - //如果获取不到 就用user_id获取信息类型 - if msgType == "" { + // 检查UserID是否为0 + checkZeroUserID := func(id interface{}) bool { + switch v := id.(type) { + case int: + return v != 0 + case int64: + return v != 0 + case string: + return v != "0" // 同样检查字符串形式的0 + default: + return true // 如果不是int、int64或string,假定它不为0 + } + } + + if msgType == "" && message.Params.UserID != nil && checkZeroUserID(message.Params.UserID) { msgType = GetMessageTypeByUserid(config.GetAppIDStr(), message.Params.UserID) } - //顺序,私聊优先从UserID推断类型会更准确 - if msgType == "" { + if msgType == "" && message.Params.GroupID != nil && checkZeroGroupID(message.Params.GroupID) { msgType = GetMessageTypeByGroupid(config.GetAppIDStr(), message.Params.GroupID) } - //新增 内存获取不到从数据库获取 - if msgType == "" { + if msgType == "" && message.Params.UserID != nil && checkZeroUserID(message.Params.UserID) { msgType = GetMessageTypeByUseridV2(message.Params.UserID) } - if msgType == "" { + if msgType == "" && message.Params.GroupID != nil && checkZeroGroupID(message.Params.GroupID) { msgType = GetMessageTypeByGroupidV2(message.Params.GroupID) } var idInt64 int64 @@ -292,7 +316,7 @@ func HandleSendPrivateMsg(client callapi.Client, api openapi.OpenAPI, apiv2 open echo.AddMsgType(config.GetAppIDStr(), idInt64, tryMessageTypes[echo.GetMapping(idInt64)-1]) delay := config.GetSendDelay() time.Sleep(time.Duration(delay) * time.Millisecond) - HandleSendPrivateMsg(client, api, apiv2, messageCopy) + retmsg, _ = HandleSendPrivateMsg(client, api, apiv2, messageCopy) } return retmsg, nil } diff --git a/idmap/service.go b/idmap/service.go index 0fd44e65..844486d0 100644 --- a/idmap/service.go +++ b/idmap/service.go @@ -217,7 +217,7 @@ func SimplifiedStoreID(id string) (int64, error) { // SimplifiedStoreID 根据a储存b 储存一半 func SimplifiedStoreIDv2(id string) (int64, error) { - if config.GetLotusValue() { + if config.GetLotusValue() && !config.GetLotusWithoutIdmaps() { // 使用网络请求方式 serverDir := config.GetServer_dir() portValue := config.GetPortValue() @@ -253,7 +253,7 @@ func SimplifiedStoreIDv2(id string) (int64, error) { return int64(rowValue), nil } - // 如果lotus为假,就保持原来的store的方法 + // 如果lotus为假,或不走idmaps是真,就保持原来的store的方法 return SimplifiedStoreID(id) } @@ -305,7 +305,7 @@ func StoreIDPro(id string, subid string) (int64, int64, error) { // StoreIDv2 根据a储存b func StoreIDv2(id string) (int64, error) { - if config.GetLotusValue() { + if config.GetLotusValue() && !config.GetLotusWithoutIdmaps() { // 使用网络请求方式 serverDir := config.GetServer_dir() portValue := config.GetPortValue() @@ -347,7 +347,7 @@ func StoreIDv2(id string) (int64, error) { // 群号 然后 用户号 func StoreIDv2Pro(id string, subid string) (int64, int64, error) { - if config.GetLotusValue() { + if config.GetLotusValue() && !config.GetLotusWithoutIdmaps() { // 使用网络请求方式 serverDir := config.GetServer_dir() portValue := config.GetPortValue() @@ -413,7 +413,7 @@ func RetrieveRowByID(rowid string) (string, error) { // 群号 然后 用户号 func RetrieveRowByIDv2Pro(newRowID string, newSubRowID string) (string, string, error) { - if config.GetLotusValue() { + if config.GetLotusValue() && !config.GetLotusWithoutIdmaps() { // 使用网络请求方式 serverDir := config.GetServer_dir() portValue := config.GetPortValue() @@ -495,7 +495,7 @@ func RetrieveRowByIDv2(rowid string) (string, error) { protocol = "https" } - if config.GetLotusValue() { + if config.GetLotusValue() && !config.GetLotusWithoutIdmaps() { // 使用网络请求方式 serverDir := config.GetServer_dir() @@ -550,7 +550,7 @@ func WriteConfig(sectionName, keyName, value string) error { // WriteConfigv2 根据a以b为类别储存c func WriteConfigv2(sectionName, keyName, value string) error { - if config.GetLotusValue() { + if config.GetLotusValue() && !config.GetLotusWithoutIdmaps() { // 使用网络请求方式 serverDir := config.GetServer_dir() portValue := config.GetPortValue() @@ -610,6 +610,65 @@ func ReadConfig(sectionName, keyName string) (string, error) { return result, err } +// DeleteConfig根据sectionName和keyName删除指定的键值对 +func DeleteConfig(sectionName, keyName string) error { + return db.Update(func(tx *bolt.Tx) error { + b := tx.Bucket([]byte(ConfigBucket)) + if b == nil { + return fmt.Errorf("bucket %s does not exist", ConfigBucket) + } + + key := joinSectionAndKey(sectionName, keyName) + err := b.Delete(key) + if err != nil { + return fmt.Errorf("failed to delete data with key %s: %w", key, err) + } + + return nil + }) +} + +// DeleteConfigv2 根据sectionName和keyName远程删除配置 +func DeleteConfigv2(sectionName, keyName string) error { + // 根据portValue确定协议 + protocol := "http" + portValue := config.GetPortValue() + if portValue == "443" { + protocol = "https" + } + + if config.GetLotusValue() && !config.GetLotusWithoutIdmaps() { + // 使用网络请求方式 + serverDir := config.GetServer_dir() + + // 构建请求URL和参数 + baseURL := fmt.Sprintf("%s://%s:%s/getid", protocol, serverDir, portValue) + params := url.Values{} + params.Add("type", "15") // type 15是用于删除操作的 + params.Add("id", sectionName) + params.Add("subtype", keyName) + url := baseURL + "?" + params.Encode() + + resp, err := http.Get(url) + if err != nil { + return fmt.Errorf("failed to send request: %v", err) + } + defer resp.Body.Close() + + // 如果HTTP状态码是200 OK,表示操作成功完成 + if resp.StatusCode == http.StatusOK { + // 成功,可以返回nil或者根据需要返回具体的成功消息 + return nil + } else { + // 如果状态码不是200 OK,返回错误信息 + return fmt.Errorf("error response from server: %s", resp.Status) + } + } + + // 如果lotus为假,则使用原始方法在本地删除配置 + return DeleteConfig(sectionName, keyName) // 假设你已经有了一个本地删除的方法 +} + // ReadConfigv2 根据a和b取出c func ReadConfigv2(sectionName, keyName string) (string, error) { // 根据portValue确定协议 @@ -619,7 +678,7 @@ func ReadConfigv2(sectionName, keyName string) (string, error) { protocol = "https" } - if config.GetLotusValue() { + if config.GetLotusValue() && !config.GetLotusWithoutIdmaps() { // 使用网络请求方式 serverDir := config.GetServer_dir() @@ -756,7 +815,7 @@ func RetrieveVirtualValue(realValue string) (string, string, error) { // 更新真实值对应的虚拟值 func UpdateVirtualValuev2(oldRowValue, newRowValue int64) error { - if config.GetLotusValue() { + if config.GetLotusValue() && !config.GetLotusWithoutIdmaps() { // 构建请求URL serverDir := config.GetServer_dir() portValue := config.GetPortValue() @@ -783,7 +842,7 @@ func UpdateVirtualValuev2(oldRowValue, newRowValue int64) error { // RetrieveRealValuev2 根据虚拟值获取真实值 func RetrieveRealValuev2(virtualValue int64) (string, string, error) { - if config.GetLotusValue() { + if config.GetLotusValue() && !config.GetLotusWithoutIdmaps() { serverDir := config.GetServer_dir() portValue := config.GetPortValue() protocol := "http" @@ -818,7 +877,7 @@ func RetrieveRealValuev2(virtualValue int64) (string, string, error) { // RetrieveVirtualValuev2 根据真实值获取虚拟值 func RetrieveVirtualValuev2(realValue string) (string, string, error) { - if config.GetLotusValue() { + if config.GetLotusValue() && !config.GetLotusWithoutIdmaps() { // 使用网络请求方式 serverDir := config.GetServer_dir() portValue := config.GetPortValue() @@ -860,7 +919,7 @@ func RetrieveVirtualValuev2(realValue string) (string, string, error) { // 根据2个真实值 获取2个虚拟值 群号 然后 用户号 func RetrieveVirtualValuev2Pro(realValue string, realValueSub string) (string, string, error) { - if config.GetLotusValue() { + if config.GetLotusValue() && !config.GetLotusWithoutIdmaps() { // 使用网络请求方式 serverDir := config.GetServer_dir() portValue := config.GetPortValue() @@ -974,7 +1033,7 @@ func RetrieveRealValuePro(virtualValue1, virtualValue2 int64) (string, string, e // RetrieveRealValuesv2Pro 根据两个虚拟值获取两个真实值 群号 然后 用户号 func RetrieveRealValuesv2Pro(virtualValue int64, virtualValueSub int64) (string, string, error) { - if config.GetLotusValue() { + if config.GetLotusValue() && !config.GetLotusWithoutIdmaps() { // 使用网络请求方式 serverDir := config.GetServer_dir() portValue := config.GetPortValue() @@ -1057,7 +1116,7 @@ func UpdateVirtualValuePro(oldVirtualValue1, newVirtualValue1, oldVirtualValue2, // UpdateVirtualValuev2Pro 根据配置更新两对虚拟值 旧群 新群 旧用户 新用户 func UpdateVirtualValuev2Pro(oldVirtualValue1, newVirtualValue1, oldVirtualValue2, newVirtualValue2 int64) error { - if config.GetLotusValue() { + if config.GetLotusValue() && !config.GetLotusWithoutIdmaps() { // 构建请求URL serverDir := config.GetServer_dir() portValue := config.GetPortValue() @@ -1147,7 +1206,7 @@ func FindSubKeysById(id string) ([]string, error) { // FindSubKeysByIdPro 根据1个值获取key中的k:v给出k获取所有v,通过网络调用 func FindSubKeysByIdPro(id string) ([]string, error) { - if config.GetLotusValue() { + if config.GetLotusValue() && !config.GetLotusWithoutIdmaps() { // 使用网络请求方式 serverDir := config.GetServer_dir() portValue := config.GetPortValue() diff --git a/server/getIDHandler.go b/server/getIDHandler.go index 338052df..9c01cbff 100644 --- a/server/getIDHandler.go +++ b/server/getIDHandler.go @@ -221,8 +221,23 @@ func GetIDHandler(c *gin.Context) { c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) return } - c.JSON(http.StatusOK, gin.H{"keys": keys}) + case 15: + // 删除 + // 从请求中获取参数 + section := c.Query("id") + subtype := c.Query("subtype") + + // 调用DeleteConfigv2来删除配置 + err := idmap.DeleteConfigv2(section, subtype) + if err != nil { + // 如果有错误,记录并返回错误信息 + c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) + return + } + + // 如果删除成功,返回成功响应 + c.JSON(http.StatusOK, gin.H{"status": "success"}) } } diff --git a/template/config_template.go b/template/config_template.go index a73f3442..a0a255e2 100644 --- a/template/config_template.go +++ b/template/config_template.go @@ -136,6 +136,7 @@ settings: put_interaction_delay : 0 #单位毫秒 表示回应已收到回调类型的按钮的毫秒数 会按用户进行区分 非全局delay img_up_api_ntv2: false #gsk内建图片上传api 是否将图片转换为ntqq图床url(md发图用,自行调用)文档:https://www.yuque.com/km57bt/hlhnxg/ig2nk88fccykn6dm fix_11300: false #修复11300报错,需要在develop_bot_id填入自己机器人的appid. 11300原因暂时未知,临时修复方案. + lotus_without_idmaps: false #lotus只通过url,图片上传,语音,不通过id转换,在本地当前gsk维护idmaps转换. title : "Gensokyo © 2023 - Hoshinonyaruko" #程序的标题 如果多个机器人 可根据标题区分 custom_bot_name : "Gensokyo全域机器人" #自定义机器人名字,会在api调用中返回,默认Gensokyo全域机器人 From 5fe4fae6acdecc551f3d844c87c92a0e241c40fc Mon Sep 17 00:00:00 2001 From: cosmo Date: Sun, 17 Mar 2024 22:48:38 +0800 Subject: [PATCH 04/33] beta341 --- .github/workflows/cross_compile.yml | 14 ++++++++++++-- main.go | 4 ++-- 2 files changed, 14 insertions(+), 4 deletions(-) diff --git a/.github/workflows/cross_compile.yml b/.github/workflows/cross_compile.yml index 6d600158..a712a2cc 100644 --- a/.github/workflows/cross_compile.yml +++ b/.github/workflows/cross_compile.yml @@ -95,11 +95,21 @@ jobs: CGO_ENABLED: 0 run: | if [ "$GOOS" = "windows" ]; then - go build -o output/gensokyo-${{ matrix.os }}-${{ matrix.goarch }}.exe + go build -ldflags="-s -w" -o output/gensokyo-${{ matrix.os }}-${{ matrix.goarch }}.exe else - go build -o output/gensokyo-${{ matrix.os }}-${{ matrix.goarch }} + go build -ldflags="-s -w" -o output/gensokyo-${{ matrix.os }}-${{ matrix.goarch }} fi + - name: Compress executable files with UPX + run: | + sudo apt-get update + sudo apt-get install -y upx + if [ "$GOOS" = "windows" ]; then + upx --best --lzma output/gensokyo-${{ matrix.os }}-${{ matrix.goarch }}.exe + else + upx --best --lzma output/gensokyo-${{ matrix.os }}-${{ matrix.goarch }} + fi + - name: Upload artifacts uses: actions/upload-artifact@v2 with: diff --git a/main.go b/main.go index d3282d49..66037a94 100644 --- a/main.go +++ b/main.go @@ -276,8 +276,8 @@ func main() { // 所有客户端都成功初始化 p = Processor.NewProcessor(api, apiV2, &conf.Settings, wsClients) } - } else if conf.Settings.EnableWsServer { - log.Println("只启动正向ws") + } else { + log.Println("提示,目前只启动了正向ws或httpapi") p = Processor.NewProcessorV2(api, apiV2, &conf.Settings) } } else { From bdb5874b4db45945ba528d3e0ec1a895c484b63d Mon Sep 17 00:00:00 2001 From: cosmo Date: Sun, 17 Mar 2024 22:50:53 +0800 Subject: [PATCH 05/33] actionfix --- .github/workflows/cross_compile.yml | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/.github/workflows/cross_compile.yml b/.github/workflows/cross_compile.yml index a712a2cc..bd429339 100644 --- a/.github/workflows/cross_compile.yml +++ b/.github/workflows/cross_compile.yml @@ -101,14 +101,14 @@ jobs: fi - name: Compress executable files with UPX - run: | - sudo apt-get update - sudo apt-get install -y upx - if [ "$GOOS" = "windows" ]; then - upx --best --lzma output/gensokyo-${{ matrix.os }}-${{ matrix.goarch }}.exe - else - upx --best --lzma output/gensokyo-${{ matrix.os }}-${{ matrix.goarch }} - fi + run: | + sudo apt-get update + sudo apt-get install -y upx + if [ "$GOOS" = "windows" ]; then + upx --best --lzma output/gensokyo-${{ matrix.os }}-${{ matrix.goarch }}.exe + else + upx --best --lzma output/gensokyo-${{ matrix.os }}-${{ matrix.goarch }} + fi - name: Upload artifacts uses: actions/upload-artifact@v2 From b2f53f5a944b35060a72bf9b106028804ccfc869 Mon Sep 17 00:00:00 2001 From: cosmo Date: Sun, 17 Mar 2024 23:07:59 +0800 Subject: [PATCH 06/33] beta342 --- .github/workflows/cross_compile.yml | 2 +- readme.md | 6 +++++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/.github/workflows/cross_compile.yml b/.github/workflows/cross_compile.yml index bd429339..62aac0ae 100644 --- a/.github/workflows/cross_compile.yml +++ b/.github/workflows/cross_compile.yml @@ -104,7 +104,7 @@ jobs: run: | sudo apt-get update sudo apt-get install -y upx - if [ "$GOOS" = "windows" ]; then + if [[ "${{ matrix.os }}" == *"windows"* ]]; then upx --best --lzma output/gensokyo-${{ matrix.os }}-${{ matrix.goarch }}.exe else upx --best --lzma output/gensokyo-${{ matrix.os }}-${{ matrix.goarch }} diff --git a/readme.md b/readme.md index 56be0d40..7578a14d 100644 --- a/readme.md +++ b/readme.md @@ -47,7 +47,7 @@ _✨ 基于 [OneBot](https://github.com/howmanybots/onebot/blob/master/README.md ## 引用 - [`tencent-connect/botgo`](https://github.com/tencent-connect/botgo): 本项目引用了此项目,并做了一些改动. -## 兼容性 +## 介绍 gensokyo兼容 [OneBot-v11](https://github.com/botuniverse/onebot-11) ,并在其基础上做了一些扩展,详情请看 OneBot 的文档。 可将官方的websocket和api转换至onebotv11标准, @@ -66,6 +66,10 @@ gensokyo兼容 [OneBot-v11](https://github.com/botuniverse/onebot-11) ,并在 欢迎测试,询问任何有关使用的问题,有问必答,有难必帮~ +[Gensokyo临时文档](https://www.yuque.com/km57bt/hlhnxg/mw7gm8dlpccd324e)展开左侧折叠栏,临时文档包含markdown定义、额外api文档等内容 + +后续会将文档独立,因为语雀文档公开查看无需登录需要vip,故暂时放在我的机器人文档中。 + ## 特别鸣谢 - [`mnixry/nonebot-plugin-gocqhttp`](https://github.com/mnixry/nonebot-plugin-gocqhttp/): 本项目采用了mnixry编写的前端,并实现了与它对应的,基于qq官方api的后端api. From 666ea3a81878b16769e80c6b1a191d946249746d Mon Sep 17 00:00:00 2001 From: cosmo Date: Sun, 17 Mar 2024 23:09:05 +0800 Subject: [PATCH 07/33] beta342 --- readme.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/readme.md b/readme.md index 7578a14d..b77013a7 100644 --- a/readme.md +++ b/readme.md @@ -68,7 +68,7 @@ gensokyo兼容 [OneBot-v11](https://github.com/botuniverse/onebot-11) ,并在 [Gensokyo临时文档](https://www.yuque.com/km57bt/hlhnxg/mw7gm8dlpccd324e)展开左侧折叠栏,临时文档包含markdown定义、额外api文档等内容 -后续会将文档独立,因为语雀文档公开查看无需登录需要vip,故暂时放在我的机器人文档中。 +后续会将文档独立,因为语雀文档公开查看无需登录需要vip,故暂时放在我的机器人文档中。临时文档也包含了Gensokyo的完整编译教程。 ## 特别鸣谢 From a5e3867f339acf7a55b4f61fce2fff82b096711c Mon Sep 17 00:00:00 2001 From: cosmo Date: Sun, 17 Mar 2024 23:13:44 +0800 Subject: [PATCH 08/33] beta342 --- .github/workflows/cross_compile.yml | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/.github/workflows/cross_compile.yml b/.github/workflows/cross_compile.yml index 62aac0ae..eeb57fce 100644 --- a/.github/workflows/cross_compile.yml +++ b/.github/workflows/cross_compile.yml @@ -100,14 +100,19 @@ jobs: go build -ldflags="-s -w" -o output/gensokyo-${{ matrix.os }}-${{ matrix.goarch }} fi - - name: Compress executable files with UPX + - name: Compress executable files with UPX (except for gensokyo-android-arm64) run: | sudo apt-get update sudo apt-get install -y upx if [[ "${{ matrix.os }}" == *"windows"* ]]; then - upx --best --lzma output/gensokyo-${{ matrix.os }}-${{ matrix.goarch }}.exe + FILENAME="output/gensokyo-${{ matrix.os }}-${{ matrix.goarch }}.exe" else - upx --best --lzma output/gensokyo-${{ matrix.os }}-${{ matrix.goarch }} + FILENAME="output/gensokyo-${{ matrix.os }}-${{ matrix.goarch }}" + fi + if [[ "${{ matrix.os }}" == "android" && "${{ matrix.goarch }}" == "arm64" ]]; then + echo "Skipping UPX compression for $FILENAME" + else + upx --best --lzma "$FILENAME" fi - name: Upload artifacts From a217d2f84cff319c212036c8bb9fcb7edb6396ea Mon Sep 17 00:00:00 2001 From: cosmo Date: Mon, 18 Mar 2024 13:26:29 +0800 Subject: [PATCH 09/33] beta344 --- config/config.go | 65 ++++++++++++ handlers/get_group_list.go | 190 +++++++++++++++++++++++------------- template/config_template.go | 6 ++ 3 files changed, 193 insertions(+), 68 deletions(-) diff --git a/config/config.go b/config/config.go index 4f03406e..2f68ec44 100644 --- a/config/config.go +++ b/config/config.go @@ -153,6 +153,11 @@ type Settings struct { ImgUpApiVtv2 bool `yaml:"img_up_api_ntv2"` Fix11300 bool `yaml:"fix_11300"` LotusWithoutIdmaps bool `yaml:"lotus_without_idmaps"` + GetGroupListAllGuilds bool `yaml:"get_g_list_all_guilds"` + GetGroupListGuilds string `yaml:"get_g_list_guilds"` + GetGroupListReturnGuilds bool `yaml:"get_g_list_return_guilds"` + GetGroupListGuidsType int `yaml:"get_g_list_guilds_type"` + GetGroupListDelay int `yaml:"get_g_list_delay"` } // LoadConfig 从文件中加载配置并初始化单例配置 @@ -1896,3 +1901,63 @@ func GetLotusWithoutIdmaps() bool { } return instance.Settings.LotusWithoutIdmaps } + +// 获取GetGroupListAllGuilds开关 +func GetGroupListAllGuilds() bool { + mu.Lock() + defer mu.Unlock() + + if instance == nil { + mylog.Println("Warning: instance is nil when trying to GetGroupListAllGuilds value.") + return false + } + return instance.Settings.GetGroupListAllGuilds +} + +// 获取 GetGroupListGuilds 数量 +func GetGetGroupListGuilds() string { + mu.Lock() + defer mu.Unlock() + + if instance == nil { + mylog.Println("Warning: instance is nil when trying to get GetGroupListGuilds.") + return "10" + } + return instance.Settings.GetGroupListGuilds +} + +// 获取GetGroupListReturnGuilds开关 +func GetGroupListReturnGuilds() bool { + mu.Lock() + defer mu.Unlock() + + if instance == nil { + mylog.Println("Warning: instance is nil when trying to GetGroupListReturnGuilds value.") + return false + } + return instance.Settings.GetGroupListReturnGuilds +} + +// 获取 GetGroupListGuidsType 数量 +func GetGroupListGuidsType() int { + mu.Lock() + defer mu.Unlock() + + if instance == nil { + mylog.Println("Warning: instance is nil when trying to get GetGroupListGuidsType.") + return 0 + } + return instance.Settings.GetGroupListGuidsType +} + +// 获取 GetGroupListDelay 数量 +func GetGroupListDelay() int { + mu.Lock() + defer mu.Unlock() + + if instance == nil { + mylog.Println("Warning: instance is nil when trying to get GetGroupListDelay.") + return 0 + } + return instance.Settings.GetGroupListDelay +} diff --git a/handlers/get_group_list.go b/handlers/get_group_list.go index 61294642..712c5b0c 100644 --- a/handlers/get_group_list.go +++ b/handlers/get_group_list.go @@ -7,6 +7,7 @@ import ( "time" "github.com/hoshinonyaruko/gensokyo/callapi" + "github.com/hoshinonyaruko/gensokyo/config" "github.com/hoshinonyaruko/gensokyo/idmap" "github.com/hoshinonyaruko/gensokyo/mylog" "github.com/tencent-connect/botgo/dto" @@ -59,82 +60,67 @@ func GetGroupList(client callapi.Client, api openapi.OpenAPI, apiv2 openapi.Open // 初始化 groupList.Data 为一个空数组 groupList.Data = []Group{} - // 检查时间差异 - if time.Since(lastCallTime) > 5*time.Minute { - // 如果超过5分钟,则重置分页状态 - globalPager = &dto.GuildPager{Limit: "10"} - } - // 全局pager - guilds, err := api.MeGuilds(context.TODO(), globalPager) - if err != nil { - mylog.Println("Error fetching guild list:", err) - return "", nil - } - if len(guilds) > 0 { - // 更新Pager的After为最后一个元素的ID - globalPager.After = guilds[len(guilds)-1].ID - } - lastCallTime = time.Now() // 更新上次调用API的时间 - //如果为空 则不使用分页 - if len(guilds) == 0 { - Pager := &dto.GuildPager{Limit: "10"} - guilds, err = api.MeGuilds(context.TODO(), Pager) - if err != nil { - mylog.Println("Error fetching guild list2:", err) - return "", nil - } - } - for _, guild := range guilds { - joinedAtTime, err := guild.JoinedAt.Time() - if err != nil { - mylog.Println("Error parsing JoinedAt timestamp:", err) - continue - } - groupID, _ := strconv.ParseInt(guild.ID, 10, 64) - joinedAtTimestamp := int32(joinedAtTime.Unix()) // 获取 10 位时间戳并转换为 int32 类型 - group := Group{ - GroupCreateTime: joinedAtTimestamp, - GroupID: groupID, - GroupLevel: 0, - GroupMemo: guild.Desc, - GroupName: "*" + guild.Name, - MaxMemberCount: int32(guild.MaxMembers), // 确保这里也是 int32 类型 - MemberCount: int32(guild.MemberCount), // 将这里也转换为 int32 类型 - } - groupList.Data = append(groupList.Data, group) - // 获取每个guild的channel信息 - channels, err := api.Channels(context.TODO(), guild.ID) // 使用guild.ID作为参数 - if err != nil { - mylog.Println("Error fetching channels list:", err) - continue - } - // 将channel信息转换为Group对象并添加到groups - for _, channel := range channels { - // 转换ChannelID64 - ChannelID64, err := idmap.StoreIDv2(channel.ID) + if config.GetGroupListAllGuilds() { + pager := &dto.GuildPager{Limit: "100"} + totalFetched := 0 + + for { + guilds, err := api.MeGuilds(context.TODO(), pager) if err != nil { - mylog.Printf("Error storing ID: %v", err) + mylog.Println("Error fetching guild list:", err) + break } - // 根据channel.Type添加前缀 - groupName := channel.Name - if channel.Type == dto.ChannelTypeText { - groupName = "&" + groupName + fetched := len(guilds) + if fetched == 0 { + break // 没有更多数据时退出循环 } - channelGroup := Group{ - GroupCreateTime: 0, // 频道没有直接对应的创建时间字段 - GroupID: ChannelID64, - GroupLevel: 0, // 频道没有直接对应的级别字段 - GroupMemo: "", // 频道没有直接对应的描述字段 - GroupName: groupName, - MaxMemberCount: 0, // 频道没有直接对应的最大成员数字段 - MemberCount: 0, // 频道没有直接对应的成员数字段 - } + totalFetched += fetched + requestCount := totalFetched / 100 + mylog.Printf("Fetched %d guilds in request %d, total %d guilds\n", fetched, requestCount, totalFetched) + + // 更新after,准备下一次请求 + pager.After = guilds[fetched-1].ID - groupList.Data = append(groupList.Data, channelGroup) + for _, guild := range guilds { + ProcessGuildAndChannels(guild, api, &groupList.Data) + } + delay := config.GetGroupListDelay() // 获取配置中的延迟时间,单位为毫秒 + time.Sleep(time.Duration(delay) * time.Millisecond) // 使用配置的延迟时间 + } + } else if config.GetGroupListReturnGuilds() { + // 检查时间差异 + if time.Since(lastCallTime) > 5*time.Minute { + // 如果超过5分钟,则重置分页状态 + guildsLimit := config.GetGetGroupListGuilds() + globalPager = &dto.GuildPager{Limit: guildsLimit} + } + // 全局pager + guilds, err := api.MeGuilds(context.TODO(), globalPager) + if err != nil { + mylog.Println("Error fetching guild list:", err) + return "", nil + } + if len(guilds) > 0 { + // 更新Pager的After为最后一个元素的ID + globalPager.After = guilds[len(guilds)-1].ID + } + lastCallTime = time.Now() // 更新上次调用API的时间 + //如果为空 则不使用分页 + if len(guilds) == 0 { + Pager := &dto.GuildPager{Limit: "10"} + guilds, err = api.MeGuilds(context.TODO(), Pager) + if err != nil { + mylog.Println("Error fetching guild list2:", err) + return "", nil + } + } + for _, guild := range guilds { + ProcessGuildAndChannels(guild, api, &groupList.Data) } } + //从idmaps数据库找群,组合成群列表需要的格式 groupIDs, err := idmap.FindKeysBySubAndType("group", "type") if err != nil { @@ -189,3 +175,71 @@ func GetGroupList(client callapi.Client, api openapi.OpenAPI, apiv2 openapi.Open mylog.Printf("get_group_list: %s", result) return string(result), nil } + +// ProcessGuildAndChannels 处理单个guild及其channels,并将结果添加到groupList.Data +func ProcessGuildAndChannels(guild *dto.Guild, api openapi.OpenAPI, groupListData *[]Group) { + joinedAtTime, err := guild.JoinedAt.Time() + if err != nil { + mylog.Println("Error parsing JoinedAt timestamp:", err) + return + } + groupID, _ := strconv.ParseInt(guild.ID, 10, 64) + joinedAtTimestamp := int32(joinedAtTime.Unix()) // 获取 10 位时间戳并转换为 int32 类型 + group := Group{ + GroupCreateTime: joinedAtTimestamp, + GroupID: groupID, + GroupLevel: 0, + GroupMemo: guild.Desc, + GroupName: "*" + guild.Name, + MaxMemberCount: int32(guild.MaxMembers), + MemberCount: int32(guild.MemberCount), + } + *groupListData = append(*groupListData, group) + + channels, err := api.Channels(context.TODO(), guild.ID) + if err != nil { + mylog.Println("Error fetching channels list:", err) + return + } + + guidsType := config.GetGroupListGuidsType() + textChannelCount := 0 // 用于记录dto.ChannelTypeText类型的频道数量 + + for _, channel := range channels { + ChannelID64, err := idmap.StoreIDv2(channel.ID) + if err != nil { + mylog.Printf("Error storing ID: %v", err) + } + + // 当guidsType不为0时,对频道类型进行特殊处理 + if guidsType != 0 { + if channel.Type == dto.ChannelTypeText { + textChannelCount++ + // 当guidsType为1但textChannelCount大于1时,或当guidsType为2但textChannelCount大于2时,跳过当前频道 + if (guidsType == 1 && textChannelCount > 1) || (guidsType == 2 && textChannelCount > 2) { + continue + } + } else { + // 如果频道类型不是dto.ChannelTypeText,则根据guidsType丢弃 + continue + } + } + + groupName := channel.Name + if channel.Type == dto.ChannelTypeText { + groupName = "&" + groupName + } + + channelGroup := Group{ + GroupCreateTime: 0, + GroupID: ChannelID64, + GroupLevel: 0, + GroupMemo: "", + GroupName: groupName, + MaxMemberCount: 0, + MemberCount: 0, + } + + *groupListData = append(*groupListData, channelGroup) + } +} diff --git a/template/config_template.go b/template/config_template.go index a0a255e2..52c6711b 100644 --- a/template/config_template.go +++ b/template/config_template.go @@ -138,6 +138,12 @@ settings: fix_11300: false #修复11300报错,需要在develop_bot_id填入自己机器人的appid. 11300原因暂时未知,临时修复方案. lotus_without_idmaps: false #lotus只通过url,图片上传,语音,不通过id转换,在本地当前gsk维护idmaps转换. + get_g_list_all_guilds : false #在获取群列表api时,轮询获取全部的频道列表(api一次只能获取100个),建议仅在广播公告通知等特别场景时开启. + get_g_list_delay : 500 #轮询时的延迟时间,毫秒数. + get_g_list_guilds_type : 0 #0=全部返回,1=获取第1个子频道.以此类推.可以缩减返回值的大小. + get_g_list_guilds : "10" #在获取群列表api时,一次返回的频道数量.这里是string,不要去掉引号.最大100(5分钟内连续请求=翻页),获取全部请开启get_g_list_return_guilds. + get_g_list_return_guilds : true #获取群列表时是否返回频道列表. + title : "Gensokyo © 2023 - Hoshinonyaruko" #程序的标题 如果多个机器人 可根据标题区分 custom_bot_name : "Gensokyo全域机器人" #自定义机器人名字,会在api调用中返回,默认Gensokyo全域机器人 From 6df84f8fa2b2c93af4fd0f18482365fb56765622 Mon Sep 17 00:00:00 2001 From: cosmo Date: Fri, 22 Mar 2024 21:53:23 +0800 Subject: [PATCH 10/33] beta345 --- go.mod | 3 +-- go.sum | 4 --- main.go | 1 + server/uploadpic.go | 65 +++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 67 insertions(+), 6 deletions(-) diff --git a/go.mod b/go.mod index ab7d1abe..e64ad884 100644 --- a/go.mod +++ b/go.mod @@ -22,7 +22,6 @@ require ( require ( github.com/clbanning/mxj v1.8.4 // indirect github.com/go-ole/go-ole v1.2.6 // indirect - github.com/golang/protobuf v1.5.3 // indirect github.com/google/go-querystring v1.0.0 // indirect github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af // indirect github.com/mattn/go-colorable v0.1.13 // indirect @@ -70,6 +69,6 @@ require ( golang.org/x/net v0.10.0 golang.org/x/sys v0.13.0 golang.org/x/text v0.9.0 // indirect - google.golang.org/protobuf v1.32.0 // indirect + google.golang.org/protobuf v1.32.0 mvdan.cc/xurls v1.1.0 ) diff --git a/go.sum b/go.sum index c368c245..bf141ce6 100644 --- a/go.sum +++ b/go.sum @@ -70,8 +70,6 @@ github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvq github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= -github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg= -github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= @@ -276,8 +274,6 @@ google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzi google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= -google.golang.org/protobuf v1.30.0 h1:kPPoIgf3TsEvrm0PFe15JQ+570QVxYzEvvHqChK+cng= -google.golang.org/protobuf v1.30.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= google.golang.org/protobuf v1.32.0 h1:pPC6BG5ex8PDFnkbrGU3EixyhKcQ2aDuBS36lqK/C7I= google.golang.org/protobuf v1.32.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= diff --git a/main.go b/main.go index 66037a94..cf690003 100644 --- a/main.go +++ b/main.go @@ -320,6 +320,7 @@ func main() { r.GET("/updateport", server.HandleIpupdate) r.POST("/uploadpic", server.UploadBase64ImageHandler(rateLimiter)) r.POST("/uploadpicv2", server.UploadBase64ImageHandlerV2(rateLimiter, apiV2)) + r.POST("/uploadpicv3", server.UploadBase64ImageHandlerV3(rateLimiter, api)) r.POST("/uploadrecord", server.UploadBase64RecordHandler(rateLimiter)) r.Static("/channel_temp", "./channel_temp") if config.GetFrpPort() == "0" && !config.GetDisableWebui() { diff --git a/server/uploadpic.go b/server/uploadpic.go index b1fc1c77..ca642439 100644 --- a/server/uploadpic.go +++ b/server/uploadpic.go @@ -2,6 +2,7 @@ package server import ( "bytes" + "context" "crypto/md5" "encoding/base64" "encoding/hex" @@ -13,6 +14,7 @@ import ( _ "image/png" "net/http" "os" + "strings" "time" "github.com/gin-gonic/gin" @@ -20,6 +22,7 @@ import ( "github.com/hoshinonyaruko/gensokyo/idmap" "github.com/hoshinonyaruko/gensokyo/images" "github.com/hoshinonyaruko/gensokyo/mylog" + "github.com/tencent-connect/botgo/dto" "github.com/tencent-connect/botgo/openapi" ) @@ -160,6 +163,68 @@ func UploadBase64ImageHandlerV2(rateLimiter *RateLimiter, apiv2 openapi.OpenAPI) } } +func UploadBase64ImageHandlerV3(rateLimiter *RateLimiter, apiv1 openapi.OpenAPI) gin.HandlerFunc { + return func(c *gin.Context) { + ipAddress := c.ClientIP() + if !rateLimiter.CheckAndUpdateRateLimit(ipAddress) { + c.JSON(http.StatusTooManyRequests, gin.H{"error": "rate limit exceeded"}) + return + } + + base64Image := c.PostForm("base64Image") + channelID := c.PostForm("channelID") + + if channelID == "" { + c.JSON(http.StatusBadRequest, gin.H{"error": "channelID is required"}) + return + } + + fileImageData, err := base64.StdEncoding.DecodeString(base64Image) + if err != nil { + mylog.Printf("Base64 解码失败: %v", err) + return + } + // 压缩 只有设置了阈值才会压缩 + compressedData, err := images.CompressSingleImage(fileImageData) + if err != nil { + mylog.Printf("Error compressing image: %v", err) + return + } + + newMessage := &dto.MessageToCreate{ + Content: "", + MsgID: "1000", + MsgType: 0, + Timestamp: time.Now().Unix(), + } + + if _, err = apiv1.PostMessageMultipart(context.TODO(), channelID, newMessage, compressedData); err != nil { + mylog.Printf("使用multipart发送图文信息失败: %v message_id %v", err, 1000) + return + } + + // 计算压缩数据的MD5值 + // 计算压缩数据的MD5值 + md5Hash := md5.Sum(compressedData) + md5String := strings.ToUpper(hex.EncodeToString(md5Hash[:])) + imageURL := fmt.Sprintf("https://gchat.qpic.cn/qmeetpic/0/0-0-%s/0", md5String) + + // 获取图片宽高 + height, width, err := images.GetImageDimensions(imageURL) + if err != nil { + mylog.Printf("获取图片宽高出错: %v", err) + return + } + + c.JSON(http.StatusOK, gin.H{ + "url": imageURL, + "channelID": channelID, + "width": width, + "height": height, + }) + } +} + // 闭包,网页后端,语音床逻辑,基于gin和www静态文件的简易语音床 func UploadBase64RecordHandler(rateLimiter *RateLimiter) gin.HandlerFunc { return func(c *gin.Context) { From 2aac729308e239bdaad0567b3bd37a0ee684624e Mon Sep 17 00:00:00 2001 From: cosmo Date: Fri, 22 Mar 2024 22:45:41 +0800 Subject: [PATCH 11/33] beta346 --- images/format.go | 13 ++++++++++++- server/uploadpic.go | 1 - 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/images/format.go b/images/format.go index a5cb61c2..4790afd0 100644 --- a/images/format.go +++ b/images/format.go @@ -12,6 +12,7 @@ import ( // 宽度 高度 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") { @@ -19,7 +20,17 @@ func GetImageDimensions(url string) (int, int, error) { } else if strings.HasSuffix(url, ".gif") { return getGIFDimensions(url) } - return 0, 0, fmt.Errorf("unsupported image format") + + // 如果图片格式不受支持,则尝试其他方法 + methods := []func(string) (int, int, error){getPNGDimensions, getJpegDimensions, getGIFDimensions} + for _, method := range methods { + width, height, err := method(url) + if err == nil && (width != 0 || height != 0) { + return width, height, nil + } + } + + return 0, 0, fmt.Errorf("unsupported image format or failed to get dimensions") } func getPNGDimensions(url string) (int, int, error) { diff --git a/server/uploadpic.go b/server/uploadpic.go index ca642439..c60cc337 100644 --- a/server/uploadpic.go +++ b/server/uploadpic.go @@ -203,7 +203,6 @@ func UploadBase64ImageHandlerV3(rateLimiter *RateLimiter, apiv1 openapi.OpenAPI) return } - // 计算压缩数据的MD5值 // 计算压缩数据的MD5值 md5Hash := md5.Sum(compressedData) md5String := strings.ToUpper(hex.EncodeToString(md5Hash[:])) From 75469893361a4b8f295071fbbabc8664f79c58c8 Mon Sep 17 00:00:00 2001 From: cosmo Date: Sat, 23 Mar 2024 11:10:53 +0800 Subject: [PATCH 12/33] beta347 --- config/config.go | 37 +++++++++++++ handlers/send_group_msg.go | 9 ++- handlers/send_msg.go | 6 ++ handlers/send_private_msg.go | 7 +++ images/upload_api.go | 103 +++++++++++++++++++++++++++++++++++ template/config_template.go | 6 +- 6 files changed, 165 insertions(+), 3 deletions(-) diff --git a/config/config.go b/config/config.go index 2f68ec44..d97e6d4d 100644 --- a/config/config.go +++ b/config/config.go @@ -158,6 +158,9 @@ type Settings struct { GetGroupListReturnGuilds bool `yaml:"get_g_list_return_guilds"` GetGroupListGuidsType int `yaml:"get_g_list_guilds_type"` GetGroupListDelay int `yaml:"get_g_list_delay"` + GlobalServerTempQQguild bool `yaml:"global_server_temp_qqguild"` + ServerTempQQguild string `yaml:"server_temp_qqguild"` + ServerTempQQguildPool []string `yaml:"server_temp_qqguild_pool"` } // LoadConfig 从文件中加载配置并初始化单例配置 @@ -1961,3 +1964,37 @@ func GetGroupListDelay() int { } return instance.Settings.GetGroupListDelay } + +// 获取GetGlobalServerTempQQguild开关 +func GetGlobalServerTempQQguild() bool { + mu.Lock() + defer mu.Unlock() + + if instance == nil { + mylog.Println("Warning: instance is nil when trying to GlobalServerTempQQguild value.") + return false + } + return instance.Settings.GlobalServerTempQQguild +} + +// 获取ServerTempQQguild +func GetServerTempQQguild() string { + mu.Lock() + defer mu.Unlock() + + if instance == nil { + mylog.Println("Warning: instance is nil when trying to ServerTempQQguild value.") + return "0" + } + return instance.Settings.ServerTempQQguild +} + +// 获取ServerTempQQguildPool +func GetServerTempQQguildPool() []string { + mu.Lock() + defer mu.Unlock() + if instance != nil { + return instance.Settings.ServerTempQQguildPool + } + return nil // 返回nil,如果instance为nil +} diff --git a/handlers/send_group_msg.go b/handlers/send_group_msg.go index 3e4f0c6f..1caf0507 100644 --- a/handlers/send_group_msg.go +++ b/handlers/send_group_msg.go @@ -76,7 +76,12 @@ func HandleSendGroupMsg(client callapi.Client, api openapi.OpenAPI, apiv2 openap if msgType == "" && message.Params.UserID != nil && checkZeroUserID(message.Params.UserID) { msgType = GetMessageTypeByUseridV2(message.Params.UserID) } - + // New checks for UserID and GroupID being nil or 0 + if (message.Params.UserID == nil || !checkZeroUserID(message.Params.UserID)) && + (message.Params.GroupID == nil || !checkZeroGroupID(message.Params.GroupID)) { + mylog.Printf("send_group_msgs接收到错误action: %v", message) + return "", nil + } mylog.Printf("send_group_msg获取到信息类型:%v", msgType) var idInt64 int64 var err error @@ -1116,7 +1121,7 @@ func SendStackMessages(apiv2 openapi.OpenAPI, messageid string, GroupID string) mylog.Printf("取出数量: %v", count) pairs := echo.PopGlobalStackMulti(count) for i, pair := range pairs { - mylog.Printf("发送栈中的消息匹配 %v: %v", pair.Group, GroupID) + //mylog.Printf("发送栈中的消息匹配 %v: %v", pair.Group, GroupID) if pair.Group == GroupID { // 发送消息 msgseq := echo.GetMappingSeq(messageid) diff --git a/handlers/send_msg.go b/handlers/send_msg.go index f9aca80a..42578736 100644 --- a/handlers/send_msg.go +++ b/handlers/send_msg.go @@ -65,6 +65,12 @@ func HandleSendMsg(client callapi.Client, api openapi.OpenAPI, apiv2 openapi.Ope if msgType == "" && message.Params.UserID != nil && checkZeroUserID(message.Params.UserID) { msgType = GetMessageTypeByUseridV2(message.Params.UserID) } + // New checks for UserID and GroupID being nil or 0 + if (message.Params.UserID == nil || !checkZeroUserID(message.Params.UserID)) && + (message.Params.GroupID == nil || !checkZeroGroupID(message.Params.GroupID)) { + mylog.Printf("send_group_msgs接收到错误action: %v", message) + return "", nil + } var idInt64, idInt642 int64 var err error diff --git a/handlers/send_private_msg.go b/handlers/send_private_msg.go index e71c493b..f8daf4c5 100644 --- a/handlers/send_private_msg.go +++ b/handlers/send_private_msg.go @@ -67,6 +67,13 @@ func HandleSendPrivateMsg(client callapi.Client, api openapi.OpenAPI, apiv2 open if msgType == "" && message.Params.GroupID != nil && checkZeroGroupID(message.Params.GroupID) { msgType = GetMessageTypeByGroupidV2(message.Params.GroupID) } + // New checks for UserID and GroupID being nil or 0 + if (message.Params.UserID == nil || !checkZeroUserID(message.Params.UserID)) && + (message.Params.GroupID == nil || !checkZeroGroupID(message.Params.GroupID)) { + mylog.Printf("send_group_msgs接收到错误action: %v", message) + return "", nil + } + var idInt64 int64 var err error diff --git a/images/upload_api.go b/images/upload_api.go index 0ee3d461..f70bff01 100644 --- a/images/upload_api.go +++ b/images/upload_api.go @@ -13,6 +13,7 @@ import ( "net/url" "regexp" "strings" + "sync" "github.com/hoshinonyaruko/gensokyo/config" "github.com/hoshinonyaruko/gensokyo/echo" @@ -24,6 +25,12 @@ import ( "google.golang.org/protobuf/proto" ) +// 包级私有变量,用于存储当前URL索引 +var ( + currentURLIndex int + urlsMutex sync.Mutex +) + // uploadMedia 上传媒体并返回FileInfo func uploadMedia(ctx context.Context, groupID string, richMediaMessage *dto.RichMediaMessage, apiv2 openapi.OpenAPI) (string, error) { // 调用API来上传媒体 @@ -55,6 +62,17 @@ func isNumeric(s string) bool { func UploadBase64ImageToServer(msgid string, base64Image string, groupID string, apiv2 openapi.OpenAPI) (string, uint64, uint32, uint32, error) { var picURL string var err error + // 检查是否应该使用全局服务器临时QQ群的特殊上传行为 + if config.GetGlobalServerTempQQguild() { + // 直接调用UploadBehaviorV3 + downloadURL, width, height, err := UploadBehaviorV3(base64Image) + if err != nil { + log.Printf("Error UploadBehaviorV3: %v", err) + return "", 0, 0, 0, nil + } + return downloadURL, 0, width, height, nil + } + extraPicAuditingType := config.GetOssType() switch extraPicAuditingType { @@ -200,6 +218,51 @@ func originalUploadBehavior(base64Image string) (string, error) { return "", errors.New("local server uses a private address; image upload failed") } +func UploadBehaviorV3(base64Image string) (string, uint32, uint32, error) { + urls := config.GetServerTempQQguildPool() + if len(urls) > 0 { + urlsMutex.Lock() + url := urls[currentURLIndex] + currentURLIndex = (currentURLIndex + 1) % len(urls) + urlsMutex.Unlock() + + resp, width, height, err := postImageToServerV3(base64Image, url) + if err != nil { + return "", 0, 0, err + } + return resp, width, height, nil + } else { + protocol := "http" + serverPort := config.GetPortValue() + if serverPort == "443" { + protocol = "https" + } + + serverDir := config.GetServer_dir() + url := fmt.Sprintf("%s://%s:%s/uploadpicv3", protocol, serverDir, serverPort) + + if config.GetLotusValue() { + resp, width, height, err := postImageToServerV3(base64Image, url) + if err != nil { + return "", 0, 0, err + } + return resp, width, height, nil + } else { + if serverPort == "443" { + protocol = "http" + serverPort = "444" + } + url = fmt.Sprintf("%s://127.0.0.1:%s/uploadpicv3", protocol, serverPort) + + resp, width, height, err := postImageToServerV3(base64Image, url) + if err != nil { + return "", 0, 0, err + } + return resp, width, height, nil + } + } +} + // 将base64语音通过lotus转换成url func originalUploadBehaviorRecord(base64Image string) (string, error) { // 根据serverPort确定协议 @@ -271,6 +334,46 @@ func postImageToServer(base64Image, targetURL string) (string, error) { return "", fmt.Errorf("URL not found in response") } +// 请求图床api(图床就是lolus为false的gensokyo) +func postImageToServerV3(base64Image, targetURL string) (string, uint32, uint32, error) { + data := url.Values{} + channelID := config.GetServerTempQQguild() + data.Set("base64Image", base64Image) // 修改字段名以与服务器匹配 + data.Set("channelID", channelID) // 修改字段名以与服务器匹配 + + resp, err := http.PostForm(targetURL, data) + if err != nil { + return "", 0, 0, fmt.Errorf("failed to send request: %v", err) + } + defer resp.Body.Close() + + if resp.StatusCode != http.StatusOK { + return "", 0, 0, fmt.Errorf("error response from server: %s", resp.Status) + } + + body, err := io.ReadAll(resp.Body) + if err != nil { + return "", 0, 0, fmt.Errorf("failed to read response body: %v", err) + } + + var responseMap map[string]interface{} + if err := json.Unmarshal(body, &responseMap); err != nil { + return "", 0, 0, fmt.Errorf("failed to unmarshal response: %v", err) + } + + url, okURL := responseMap["url"].(string) + width, okWidth := responseMap["width"].(float64) // JSON numbers are decoded as float64 + height, okHeight := responseMap["height"].(float64) + if !okURL { + return "", 0, 0, fmt.Errorf("uRL not found in response") + } + if !okWidth || !okHeight { + return "", 0, 0, fmt.Errorf("width or Height not found in response") + } + + return url, uint32(width), uint32(height), nil +} + // 请求语音床api(图床就是lolus为false的gensokyo) func postRecordToServer(base64Image, targetURL string) (string, error) { data := url.Values{} diff --git a/template/config_template.go b/template/config_template.go index 52c6711b..92fa87d6 100644 --- a/template/config_template.go +++ b/template/config_template.go @@ -52,7 +52,7 @@ settings: record_bitRate : 24000 #语音文件的比特率 默认25000 代表 25 kbps 最高无限 请根据带宽 您发送的实际码率调整 card_nick : "" #默认为空,连接mirai-overflow时,请设置为非空,这里是机器人对用户称谓,为空为插件获取,mirai不支持 auto_bind : true #测试功能,后期会移除 - AMsgRetryAsPMsg_Count : 1 #当主动信息发送失败时,自动转为后续的被动信息发送,需要开启Lazy message id,该配置项为每次跟随被动信息发送的信息数量,最大5,建议1-3 + AMsgRetryAsPMsg_Count : 30 #当主动信息发送失败时,自动转为后续的被动信息发送,需要开启Lazy message id,该配置项为所有群、频道的主动转被动消息队列最大长度,建议30-100,无上限 reconnect_times : 100 #反向ws连接失败后的重试次数,希望一直重试,可设置9999 heart_beat_interval : 10 #反向ws心跳间隔 单位秒 推荐5-10 launch_reconnect_times : 1 #启动时尝试反向ws连接次数,建议先打开应用端再开启gensokyo,因为启动时连接会阻塞webui启动,默认只连接一次,可自行增大 @@ -144,6 +144,10 @@ settings: get_g_list_guilds : "10" #在获取群列表api时,一次返回的频道数量.这里是string,不要去掉引号.最大100(5分钟内连续请求=翻页),获取全部请开启get_g_list_return_guilds. get_g_list_return_guilds : true #获取群列表时是否返回频道列表. + global_server_temp_qqguild : false #需设置server_temp_qqguild,公域私域均可用,以频道为底层发图,速度快,该接口为进阶接口,使用有一定难度. + server_temp_qqguild : "0" #在v3图片接口采用固定的子频道号,可以是帖子子频道 https://www.yuque.com/km57bt/hlhnxg/uqmnsno3vx1ytp2q + server_temp_qqguild_pool : [] #填写v3发图接口的endpoint http://127.0.0.1:12345/uploadpicv3 当填写多个时采用循环方式负载均衡,注,不包括自身,如需要自身也要填写 + title : "Gensokyo © 2023 - Hoshinonyaruko" #程序的标题 如果多个机器人 可根据标题区分 custom_bot_name : "Gensokyo全域机器人" #自定义机器人名字,会在api调用中返回,默认Gensokyo全域机器人 From 3ea73d59c7a58d47df9cd30c2da1e1c8691e692c Mon Sep 17 00:00:00 2001 From: cosmo Date: Sun, 24 Mar 2024 01:00:33 +0800 Subject: [PATCH 13/33] beta348 --- botgo/openapi/iface.go | 2 + botgo/openapi/v1/message.go | 30 ++ botgo/openapi/v1/resource.go | 4 +- botgo/openapi/v2/message.go | 30 ++ botgo/openapi/v2/resource.go | 4 +- callapi/callapi.go | 63 +++- handlers/delete_msg.go | 110 ++++++ handlers/get_group_member_list.go | 16 +- handlers/get_guild_channel_list.go | 2 +- handlers/message_parser.go | 196 ++++++++++- handlers/send_group_forward_msg.go | 2 +- handlers/send_group_msg.go | 32 +- handlers/send_group_msg_raw.go | 493 +++++++++++++++++++++++++++ handlers/send_guild_channel_forum.go | 10 +- handlers/send_guild_channel_msg.go | 36 +- handlers/send_guild_private_msg.go | 12 +- handlers/send_msg.go | 8 +- handlers/send_private_msg.go | 14 +- httpapi/httpapi.go | 60 +++- 19 files changed, 1043 insertions(+), 81 deletions(-) create mode 100644 handlers/delete_msg.go create mode 100644 handlers/send_group_msg_raw.go diff --git a/botgo/openapi/iface.go b/botgo/openapi/iface.go index d9670d8e..58bd24ec 100644 --- a/botgo/openapi/iface.go +++ b/botgo/openapi/iface.go @@ -74,6 +74,8 @@ type MessageAPI interface { PatchMessage(ctx context.Context, channelID string, messageID string, msg *dto.MessageToCreate) (*dto.Message, error) RetractMessage(ctx context.Context, channelID, msgID string, options ...RetractMessageOption) error + RetractGroupMessage(ctx context.Context, groupID, msgID string, options ...RetractMessageOption) error + RetractC2CMessage(ctx context.Context, userID, msgID string, options ...RetractMessageOption) error // PostSettingGuide 发送设置引导 PostSettingGuide(ctx context.Context, channelID string, atUserIDs []string) (*dto.Message, error) diff --git a/botgo/openapi/v1/message.go b/botgo/openapi/v1/message.go index 43e11c4c..4285a823 100644 --- a/botgo/openapi/v1/message.go +++ b/botgo/openapi/v1/message.go @@ -175,6 +175,36 @@ func (o *openAPI) RetractMessage(ctx context.Context, return err } +// RetractMessage 撤回群消息 +func (o *openAPI) RetractGroupMessage(ctx context.Context, + groupID, msgID string, options ...openapi.RetractMessageOption) error { + request := o.request(ctx). + SetPathParam("group_id", groupID). + SetPathParam("message_id", string(msgID)) + for _, option := range options { + if option == openapi.RetractMessageOptionHidetip { + request = request.SetQueryParam("hidetip", "true") + } + } + _, err := request.Delete(o.getURL(groupMessagesURL)) + return err +} + +// RetractMessage 撤回私聊消息 +func (o *openAPI) RetractC2CMessage(ctx context.Context, + UserID, msgID string, options ...openapi.RetractMessageOption) error { + request := o.request(ctx). + SetPathParam("user_id", UserID). + SetPathParam("message_id", string(msgID)) + for _, option := range options { + if option == openapi.RetractMessageOptionHidetip { + request = request.SetQueryParam("hidetip", "true") + } + } + _, err := request.Delete(o.getURL(c2cMessageURI)) + return err +} + // PostSettingGuide 发送设置引导消息, atUserID为要at的用户 func (o *openAPI) PostSettingGuide(ctx context.Context, channelID string, atUserIDs []string) (*dto.Message, error) { diff --git a/botgo/openapi/v1/resource.go b/botgo/openapi/v1/resource.go index 85496f83..6f554df0 100644 --- a/botgo/openapi/v1/resource.go +++ b/botgo/openapi/v1/resource.go @@ -34,7 +34,8 @@ const ( c2cMessagesURI uri = "/v2/users/{user_id}/messages" c2cRichMediaURI uri = "/v2/users/{user_id}/files" - messageURI uri = "/channels/{channel_id}/messages/{message_id}" + messageURI uri = "/channels/{channel_id}/messages/{message_id}" + groupMessagesURL uri = "/v2/groups/{group_id}/messages/{message_id}" userMeURI uri = "/users/@me" userMeGuildsURI uri = "/users/@me/guilds" @@ -53,6 +54,7 @@ const ( dmsURI uri = "/dms/{guild_id}/messages" dmsMessageURI uri = "/dms/{guild_id}/messages/{message_id}" + c2cMessageURI uri = "/v2/users/{user_id}/messages/{message_id}" channelAnnouncesURI = "/channels/{channel_id}/announces" channelAnnounceURI = "/channels/{channel_id}/announces/{message_id}" diff --git a/botgo/openapi/v2/message.go b/botgo/openapi/v2/message.go index 4627d3d0..d3e23707 100644 --- a/botgo/openapi/v2/message.go +++ b/botgo/openapi/v2/message.go @@ -175,6 +175,36 @@ func (o *openAPIv2) RetractMessage(ctx context.Context, return err } +// RetractMessage 撤回群消息 +func (o *openAPIv2) RetractGroupMessage(ctx context.Context, + groupID, msgID string, options ...openapi.RetractMessageOption) error { + request := o.request(ctx). + SetPathParam("group_id", groupID). + SetPathParam("message_id", string(msgID)) + for _, option := range options { + if option == openapi.RetractMessageOptionHidetip { + request = request.SetQueryParam("hidetip", "true") + } + } + _, err := request.Delete(o.getURL(groupMessagesURL)) + return err +} + +// RetractMessage 撤回私聊消息 +func (o *openAPIv2) RetractC2CMessage(ctx context.Context, + UserID, msgID string, options ...openapi.RetractMessageOption) error { + request := o.request(ctx). + SetPathParam("user_id", UserID). + SetPathParam("message_id", string(msgID)) + for _, option := range options { + if option == openapi.RetractMessageOptionHidetip { + request = request.SetQueryParam("hidetip", "true") + } + } + _, err := request.Delete(o.getURL(c2cMessageURI)) + return err +} + // PostSettingGuide 发送设置引导消息, atUserID为要at的用户 func (o *openAPIv2) PostSettingGuide(ctx context.Context, channelID string, atUserIDs []string) (*dto.Message, error) { diff --git a/botgo/openapi/v2/resource.go b/botgo/openapi/v2/resource.go index b8835c1a..2649d46b 100644 --- a/botgo/openapi/v2/resource.go +++ b/botgo/openapi/v2/resource.go @@ -34,7 +34,8 @@ const ( c2cMessagesURI uri = "/v2/users/{user_id}/messages" c2cRichMediaURI uri = "/v2/users/{user_id}/files" - messageURI uri = "/channels/{channel_id}/messages/{message_id}" + messageURI uri = "/channels/{channel_id}/messages/{message_id}" + groupMessagesURL uri = "/v2/groups/{group_id}/messages/{message_id}" userMeURI uri = "/users/@me" userMeGuildsURI uri = "/users/@me/guilds" @@ -53,6 +54,7 @@ const ( dmsURI uri = "/dms/{guild_id}/messages" dmsMessageURI uri = "/dms/{guild_id}/messages/{message_id}" + c2cMessageURI uri = "/v2/users/{user_id}/messages/{message_id}" channelAnnouncesURI = "/channels/{channel_id}/announces" channelAnnounceURI = "/channels/{channel_id}/announces/{message_id}" diff --git a/callapi/callapi.go b/callapi/callapi.go index e6e88536..c0e6a5ff 100644 --- a/callapi/callapi.go +++ b/callapi/callapi.go @@ -68,18 +68,19 @@ func (a *ActionMessage) UnmarshalJSON(data []byte) error { // params类型 type ParamsContent struct { - BotQQ string `json:"botqq"` - ChannelID string `json:"channel_id"` - GuildID string `json:"guild_id"` - GroupID interface{} `json:"group_id"` // 每一种onebotv11实现的字段类型都可能不同 - Message interface{} `json:"message"` // 这里使用interface{}因为它可能是多种类型 - Messages interface{} `json:"messages,omitempty"` // 坑爹转发信息 - UserID interface{} `json:"user_id"` // 这里使用interface{}因为它可能是多种类型 - Duration int `json:"duration,omitempty"` // 可选的整数 - Enable bool `json:"enable,omitempty"` // 可选的布尔值 + BotQQ string `json:"botqq,omitempty"` + ChannelID interface{} `json:"channel_id,omitempty"` + GuildID interface{} `json:"guild_id,omitempty"` + GroupID interface{} `json:"group_id,omitempty"` // 每一种onebotv11实现的字段类型都可能不同 + MessageID interface{} `json:"message_id,omitempty"` // 用于撤回信息 + Message interface{} `json:"message,omitempty"` // 这里使用interface{}因为它可能是多种类型 + Messages interface{} `json:"messages,omitempty"` // 坑爹转发信息 + UserID interface{} `json:"user_id,omitempty"` // 这里使用interface{}因为它可能是多种类型 + Duration int `json:"duration,omitempty"` // 可选的整数 + Enable bool `json:"enable,omitempty"` // 可选的布尔值 // handle quick operation - Context Context `json:"context"` // context 字段 - Operation Operation `json:"operation"` // operation 字段 + Context Context `json:"context,omitempty"` // context 字段 + Operation Operation `json:"operation,omitempty"` // operation 字段 } // Context 结构体用于存储 context 字段相关信息 @@ -106,8 +107,11 @@ type Operation struct { func (p *ParamsContent) UnmarshalJSON(data []byte) error { type Alias ParamsContent aux := &struct { - GroupID interface{} `json:"group_id"` - UserID interface{} `json:"user_id"` + GroupID interface{} `json:"group_id"` + UserID interface{} `json:"user_id"` + MessageID interface{} `json:"message_id"` + ChannelID interface{} `json:"channel_id"` + GuildID interface{} `json:"guild_id"` *Alias }{ Alias: (*Alias)(p), @@ -138,6 +142,39 @@ func (p *ParamsContent) UnmarshalJSON(data []byte) error { return fmt.Errorf("UserID has unsupported type") } + switch v := aux.MessageID.(type) { + case nil: // 当UserID不存在时 + p.MessageID = "" + case float64: // JSON的数字默认被解码为float64 + p.MessageID = fmt.Sprintf("%.0f", v) // 将其转换为字符串,忽略小数点后的部分 + case string: + p.MessageID = v + default: + return fmt.Errorf("MessageID has unsupported type") + } + + switch v := aux.ChannelID.(type) { + case nil: // 当ChannelID不存在时 + p.ChannelID = "" + case float64: // JSON的数字默认被解码为float64 + p.ChannelID = fmt.Sprintf("%.0f", v) // 将其转换为字符串,忽略小数点后的部分 + case string: + p.ChannelID = v + default: + return fmt.Errorf("MessageID has unsupported type") + } + + switch v := aux.GuildID.(type) { + case nil: // 当GuildID不存在时 + p.GuildID = "" + case float64: // JSON的数字默认被解码为float64 + p.GuildID = fmt.Sprintf("%.0f", v) // 将其转换为字符串,忽略小数点后的部分 + case string: + p.GuildID = v + default: + return fmt.Errorf("MessageID has unsupported type") + } + return nil } diff --git a/handlers/delete_msg.go b/handlers/delete_msg.go new file mode 100644 index 00000000..f7a87295 --- /dev/null +++ b/handlers/delete_msg.go @@ -0,0 +1,110 @@ +package handlers + +import ( + "context" + "encoding/json" + "fmt" + + "github.com/hoshinonyaruko/gensokyo/callapi" + "github.com/hoshinonyaruko/gensokyo/idmap" + "github.com/hoshinonyaruko/gensokyo/mylog" + "github.com/tencent-connect/botgo/openapi" +) + +func init() { + callapi.RegisterHandler("delete_msg", DeleteMsg) +} + +func DeleteMsg(client callapi.Client, api openapi.OpenAPI, apiv2 openapi.OpenAPI, message callapi.ActionMessage) (string, error) { + + //还原msgid + RealMsgID, err := idmap.RetrieveRowByIDv2(message.Params.MessageID.(string)) + if err != nil { + mylog.Printf("error retrieving real RChannelID: %v", err) + } + //重新赋值 + message.Params.MessageID = RealMsgID + //撤回频道信息 + if message.Params.ChannelID != nil && message.Params.ChannelID != "" { + var RChannelID string + var err error + // 使用RetrieveRowByIDv2还原真实的ChannelID + RChannelID, err = idmap.RetrieveRowByIDv2(message.Params.ChannelID.(string)) + if err != nil { + mylog.Printf("error retrieving real RChannelID: %v", err) + } + message.Params.ChannelID = RChannelID + err = api.RetractMessage(context.TODO(), message.Params.ChannelID.(string), message.Params.MessageID.(string), openapi.RetractMessageOptionHidetip) + if err != nil { + fmt.Println("Error retracting channel message:", err) + } + + } + + //撤回频道私信 + if message.Params.GuildID != nil && message.Params.GuildID != "" { + //这里很复杂 要取的话需要调用internal-api 根据情况还原,虚拟成群就用群(channel-id)还原完整channel-id, + //然后internal-api读配置获取guild-id ,虚拟成私信就用userid还原完整userid,然后读channel-id然后读guild-id + //因为GuildID本身不直接出现在ob11事件里。 + err := api.RetractDMMessage(context.TODO(), message.Params.GuildID.(string), message.Params.MessageID.(string), openapi.RetractMessageOptionHidetip) + if err != nil { + fmt.Println("Error retracting DM message:", err) + } + + } + + //撤回群信息 + if message.Params.GroupID != nil && message.Params.GroupID != "" { + var originalGroupID string + originalGroupID, err := idmap.RetrieveRowByIDv2(message.Params.GroupID.(string)) + if err != nil { + mylog.Printf("Error retrieving original GroupID: %v", err) + } + message.Params.GroupID = originalGroupID + err = api.RetractGroupMessage(context.TODO(), message.Params.GroupID.(string), message.Params.MessageID.(string), openapi.RetractMessageOptionHidetip) + if err != nil { + fmt.Println("Error retracting group message:", err) + } + + } + + //撤回C2C私信消息列表 + if message.Params.UserID != nil && message.Params.UserID != "" { + var UserID string + //还原真实的userid + UserID, err := idmap.RetrieveRowByIDv2(message.Params.UserID.(string)) + if err != nil { + mylog.Printf("Error reading config: %v", err) + return "", nil + } + message.Params.UserID = UserID + err = api.RetractC2CMessage(context.TODO(), message.Params.UserID.(string), message.Params.MessageID.(string), openapi.RetractMessageOptionHidetip) + if err != nil { + fmt.Println("Error retracting C2C message:", err) + } + + } + + var response GetStatusResponse + response.Message = "" + response.RetCode = 0 + response.Status = "ok" + response.Echo = message.Echo + + outputMap := structToMap(response) + + mylog.Printf("delete_msg: %+v\n", outputMap) + + err = client.SendMessage(outputMap) + if err != nil { + mylog.Printf("Error sending message via client: %v", err) + } + //把结果从struct转换为json + result, err := json.Marshal(response) + if err != nil { + mylog.Printf("Error marshaling data: %v", err) + //todo 符合onebotv11 ws返回的错误码 + return "", nil + } + return string(result), nil +} diff --git a/handlers/get_group_member_list.go b/handlers/get_group_member_list.go index 51e4602e..05069f31 100644 --- a/handlers/get_group_member_list.go +++ b/handlers/get_group_member_list.go @@ -123,7 +123,7 @@ func GetGroupMemberList(client callapi.Client, api openapi.OpenAPI, apiv2 openap //用group_id还原出channelid 这是虚拟成群的私聊信息 message.Params.ChannelID = message.Params.GroupID.(string) // 使用RetrieveRowByIDv2还原真实的ChannelID - RChannelID, err := idmap.RetrieveRowByIDv2(message.Params.ChannelID) + RChannelID, err := idmap.RetrieveRowByIDv2(message.Params.ChannelID.(string)) if err != nil { mylog.Printf("error retrieving real ChannelID: %v", err) } @@ -147,7 +147,7 @@ func GetGroupMemberList(client callapi.Client, api openapi.OpenAPI, apiv2 openap var members []MemberList var userIDInt, groupIDInt uint64 // 使用 message.Params.ChannelID 作为 id 来调用 FindSubKeysById - userIDs, err := idmap.FindSubKeysByIdPro(message.Params.ChannelID) + userIDs, err := idmap.FindSubKeysByIdPro(message.Params.ChannelID.(string)) if err != nil { mylog.Printf("Error retrieving user IDs: %v", err) return "", nil // 或者处理错误 @@ -254,7 +254,7 @@ func GetGroupMemberList(client callapi.Client, api openapi.OpenAPI, apiv2 openap //用GroupID给ChannelID赋值,因为是把频道虚拟成了群 message.Params.ChannelID = message.Params.GroupID.(string) //将真实id转为int userid64 - _, userIDInt64, err = idmap.StoreIDv2Pro(message.Params.ChannelID, memberFromAPI.User.ID) + _, userIDInt64, err = idmap.StoreIDv2Pro(message.Params.ChannelID.(string), memberFromAPI.User.ID) if err != nil { mylog.Fatalf("Error storing ID: %v", err) } @@ -277,14 +277,14 @@ func GetGroupMemberList(client callapi.Client, api openapi.OpenAPI, apiv2 openap var RChannelID string //根据api调用中的参数,还原真实的频道号 if memberFromAPI.User.ID != "" && config.GetIdmapPro() { - RChannelID, _, err = idmap.RetrieveRowByIDv2Pro(message.Params.ChannelID, memberFromAPI.User.ID) + RChannelID, _, err = idmap.RetrieveRowByIDv2Pro(message.Params.ChannelID.(string), memberFromAPI.User.ID) if err != nil { mylog.Printf("测试,通过Proid获取的RChannelID出错232:%v", err) } } if RChannelID == "" { // 使用RetrieveRowByIDv2还原真实的ChannelID - RChannelID, err = idmap.RetrieveRowByIDv2(message.Params.ChannelID) + RChannelID, err = idmap.RetrieveRowByIDv2(message.Params.ChannelID.(string)) if err != nil { mylog.Printf("测试,通过idmap.RetrieveRowByIDv2获取的RChannelID出错241:%v", err) } @@ -303,9 +303,9 @@ func GetGroupMemberList(client callapi.Client, api openapi.OpenAPI, apiv2 openap GroupID: uint64(groupIDInt), Nickname: memberFromAPI.Nick, Card: memberFromAPI.Nick, // 使用昵称作为默认值(TODO: 将来可能发生变更) - Sex: "0", // 使用默认值 - Age: 0, // 使用默认值 - Area: "0", // 使用默认值 + Sex: "0", // 使用默认值 + Age: 0, // 使用默认值 + Area: "0", // 使用默认值 JoinTime: joinTimeInt, LastSentTime: 0, // 使用默认值 Level: "0", // 0 diff --git a/handlers/get_guild_channel_list.go b/handlers/get_guild_channel_list.go index 9bcc249c..c568f86e 100644 --- a/handlers/get_guild_channel_list.go +++ b/handlers/get_guild_channel_list.go @@ -28,7 +28,7 @@ func GetGuildChannelList(client callapi.Client, api openapi.OpenAPI, apiv2 opena guildID := message.Params.GuildID // 根据请求参数调用API - channels, err := api.Channels(context.TODO(), guildID) + channels, err := api.Channels(context.TODO(), guildID.(string)) if err != nil { // 如果发生错误,记录日志并返回null mylog.Printf("Error fetching channels: %v", err) diff --git a/handlers/message_parser.go b/handlers/message_parser.go index 7e530e53..5b7d2e64 100644 --- a/handlers/message_parser.go +++ b/handlers/message_parser.go @@ -40,18 +40,202 @@ type ServerResponse struct { Data struct { MessageID int `json:"message_id"` } `json:"data"` - Message string `json:"message"` - RetCode int `json:"retcode"` - Status string `json:"status"` - Echo interface{} `json:"echo"` + Message string `json:"message"` + GroupID int64 `json:"group_id,omitempty"` + UserID int64 `json:"user_id,omitempty"` + ChannelID int64 `json:"channel_id,omitempty"` + GuildID string `json:"guild_id,omitempty"` + RetCode int `json:"retcode"` + Status string `json:"status"` + Echo interface{} `json:"echo"` +} + +// 发送成功回执 todo 返回可互转的messageid 实现群撤回api +func SendResponse(client callapi.Client, err error, message *callapi.ActionMessage, resp *dto.GroupMessageResponse) (string, error) { + var messageID64 int64 + var mapErr error + // 设置响应值 + response := ServerResponse{} + if resp != nil { + messageID64, mapErr = idmap.StoreIDv2(resp.Message.ID) + if mapErr != nil { + mylog.Printf("Error storing ID: %v", mapErr) + return "", nil + } + response.Data.MessageID = int(messageID64) + } else { + // Default ID handling + response.Data.MessageID = 123 + } + // 映射str的GroupID到int + GroupID64, errr := idmap.StoreIDv2(message.Params.GroupID.(string)) + if errr != nil { + mylog.Errorf("failed to convert ChannelID to int: %v", err) + return "", nil + } + response.GroupID = GroupID64 + response.Echo = message.Echo + if err != nil { + response.Message = err.Error() // 可选:在响应中添加错误消息 + //response.RetCode = -1 // 可以是任何非零值,表示出错 + //response.Status = "failed" + response.RetCode = 0 //官方api审核异步的 审核中默认返回失败,但其实信息发送成功了 + response.Status = "ok" + } else { + response.Message = "" + response.RetCode = 0 + response.Status = "ok" + } + + // 转化为map并发送 + outputMap := structToMap(response) + // 将map转换为JSON字符串 + jsonResponse, jsonErr := json.Marshal(outputMap) + if jsonErr != nil { + log.Printf("Error marshaling response to JSON: %v", jsonErr) + return "", jsonErr + } + //发送给ws 客户端 + sendErr := client.SendMessage(outputMap) + if sendErr != nil { + mylog.Printf("Error sending message via client: %v", sendErr) + return "", sendErr + } + + mylog.Printf("发送成功回执: %+v", string(jsonResponse)) + return string(jsonResponse), nil } // 发送成功回执 todo 返回可互转的messageid 实现频道撤回api -func SendResponse(client callapi.Client, err error, message *callapi.ActionMessage) (string, error) { +func SendGuildResponse(client callapi.Client, err error, message *callapi.ActionMessage, resp *dto.Message) (string, error) { + var messageID64 int64 + var mapErr error // 设置响应值 response := ServerResponse{} - response.Data.MessageID = 123 // todo 实现messageid转换 + if resp != nil { + messageID64, mapErr = idmap.StoreIDv2(resp.ID) + if mapErr != nil { + mylog.Printf("Error storing ID: %v", mapErr) + return "", nil + } + response.Data.MessageID = int(messageID64) + } else { + // Default ID handling + response.Data.MessageID = 123 + } + //转换成int + ChannelID64, errr := idmap.StoreIDv2(message.Params.ChannelID.(string)) + if errr != nil { + mylog.Printf("Error storing ID: %v", err) + return "", nil + } + response.ChannelID = ChannelID64 + response.Echo = message.Echo + if err != nil { + response.Message = err.Error() // 可选:在响应中添加错误消息 + //response.RetCode = -1 // 可以是任何非零值,表示出错 + //response.Status = "failed" + response.RetCode = 0 //官方api审核异步的 审核中默认返回失败,但其实信息发送成功了 + response.Status = "ok" + } else { + response.Message = "" + response.RetCode = 0 + response.Status = "ok" + } + + // 转化为map并发送 + outputMap := structToMap(response) + // 将map转换为JSON字符串 + jsonResponse, jsonErr := json.Marshal(outputMap) + if jsonErr != nil { + log.Printf("Error marshaling response to JSON: %v", jsonErr) + return "", jsonErr + } + //发送给ws 客户端 + sendErr := client.SendMessage(outputMap) + if sendErr != nil { + mylog.Printf("Error sending message via client: %v", sendErr) + return "", sendErr + } + + mylog.Printf("发送成功回执: %+v", string(jsonResponse)) + return string(jsonResponse), nil +} + +// 发送成功回执 todo 返回可互转的messageid 实现C2C撤回api +func SendC2CResponse(client callapi.Client, err error, message *callapi.ActionMessage, resp *dto.C2CMessageResponse) (string, error) { + var messageID64 int64 + var mapErr error + // 设置响应值 + response := ServerResponse{} + if resp != nil { + messageID64, mapErr = idmap.StoreIDv2(resp.Message.ID) + if mapErr != nil { + mylog.Printf("Error storing ID: %v", mapErr) + return "", nil + } + response.Data.MessageID = int(messageID64) + } else { + // Default ID handling + response.Data.MessageID = 123 + } + //将真实id转为int userid64 + userid64, errr := idmap.StoreIDv2(message.Params.UserID.(string)) + if errr != nil { + mylog.Fatalf("Error storing ID: %v", err) + } + response.UserID = userid64 + response.Echo = message.Echo + if err != nil { + response.Message = err.Error() // 可选:在响应中添加错误消息 + //response.RetCode = -1 // 可以是任何非零值,表示出错 + //response.Status = "failed" + response.RetCode = 0 //官方api审核异步的 审核中默认返回失败,但其实信息发送成功了 + response.Status = "ok" + } else { + response.Message = "" + response.RetCode = 0 + response.Status = "ok" + } + + // 转化为map并发送 + outputMap := structToMap(response) + // 将map转换为JSON字符串 + jsonResponse, jsonErr := json.Marshal(outputMap) + if jsonErr != nil { + log.Printf("Error marshaling response to JSON: %v", jsonErr) + return "", jsonErr + } + //发送给ws 客户端 + sendErr := client.SendMessage(outputMap) + if sendErr != nil { + mylog.Printf("Error sending message via client: %v", sendErr) + return "", sendErr + } + + mylog.Printf("发送成功回执: %+v", string(jsonResponse)) + return string(jsonResponse), nil +} + +// 会返回guildid的频道私信专用SendGuildPrivateResponse +func SendGuildPrivateResponse(client callapi.Client, err error, message *callapi.ActionMessage, resp *dto.Message, guildID string) (string, error) { + var messageID64 int64 + var mapErr error + // 设置响应值 + response := ServerResponse{} + if resp != nil { + messageID64, mapErr = idmap.StoreIDv2(resp.ID) + if mapErr != nil { + mylog.Printf("Error storing ID: %v", mapErr) + return "", nil + } + response.Data.MessageID = int(messageID64) + } else { + // Default ID handling + response.Data.MessageID = 123 + } response.Echo = message.Echo + response.GuildID = guildID if err != nil { response.Message = err.Error() // 可选:在响应中添加错误消息 //response.RetCode = -1 // 可以是任何非零值,表示出错 diff --git a/handlers/send_group_forward_msg.go b/handlers/send_group_forward_msg.go index ef31d0d8..6795e931 100644 --- a/handlers/send_group_forward_msg.go +++ b/handlers/send_group_forward_msg.go @@ -66,6 +66,6 @@ func HandleSendGroupForwardMsg(client callapi.Client, api openapi.OpenAPI, apiv2 count++ time.Sleep(500 * time.Millisecond) // 每条消息之间的延时 } - retmsg, _ = SendResponse(client, nil, &message) + retmsg, _ = SendResponse(client, nil, &message, nil) return retmsg, nil } diff --git a/handlers/send_group_msg.go b/handlers/send_group_msg.go index 1caf0507..2cfb59e9 100644 --- a/handlers/send_group_msg.go +++ b/handlers/send_group_msg.go @@ -297,7 +297,7 @@ func HandleSendGroupMsg(client callapi.Client, api openapi.OpenAPI, apiv2 openap } // 发送组合消息 - _, err = apiv2.PostGroupMessage(context.TODO(), message.Params.GroupID.(string), groupMessage) + resp, err := apiv2.PostGroupMessage(context.TODO(), message.Params.GroupID.(string), groupMessage) if err != nil { mylog.Printf("发送组合消息失败: %v", err) } @@ -310,7 +310,7 @@ func HandleSendGroupMsg(client callapi.Client, api openapi.OpenAPI, apiv2 openap } // 发送成功回执 - retmsg, _ = SendResponse(client, err, &message) + retmsg, _ = SendResponse(client, err, &message, resp) delete(foundItems, imageType) // 从foundItems中删除已处理的图片项 messageText = "" @@ -331,7 +331,7 @@ func HandleSendGroupMsg(client callapi.Client, api openapi.OpenAPI, apiv2 openap groupMessage.Timestamp = time.Now().Unix() // 设置时间戳 //重新为err赋值 - _, err = apiv2.PostGroupMessage(context.TODO(), message.Params.GroupID.(string), groupMessage) + resp, err := apiv2.PostGroupMessage(context.TODO(), message.Params.GroupID.(string), groupMessage) if err != nil { mylog.Printf("发送文本群组信息失败: %v", err) } @@ -343,9 +343,9 @@ func HandleSendGroupMsg(client callapi.Client, api openapi.OpenAPI, apiv2 openap echo.PushGlobalStack(pair) } //发送成功回执 - retmsg, _ = SendResponse(client, err, &message) + retmsg, _ = SendResponse(client, err, &message, resp) } - + var resp *dto.GroupMessageResponse // 遍历foundItems并发送每种信息 for key, urls := range foundItems { for _, url := range urls { @@ -367,7 +367,7 @@ func HandleSendGroupMsg(client callapi.Client, api openapi.OpenAPI, apiv2 openap return "", nil // 或其他错误处理 } //重新为err赋值 - _, err = apiv2.PostGroupMessage(context.TODO(), message.Params.GroupID.(string), groupMessage) + resp, err := apiv2.PostGroupMessage(context.TODO(), message.Params.GroupID.(string), groupMessage) if err != nil { mylog.Printf("发送md信息失败: %v", err) } @@ -379,7 +379,7 @@ func HandleSendGroupMsg(client callapi.Client, api openapi.OpenAPI, apiv2 openap echo.PushGlobalStack(pair) } //发送成功回执 - retmsg, _ = SendResponse(client, err, &message) + retmsg, _ = SendResponse(client, err, &message, resp) } continue // 跳过这个项,继续下一个 } @@ -398,7 +398,7 @@ func HandleSendGroupMsg(client callapi.Client, api openapi.OpenAPI, apiv2 openap } groupMessage.Timestamp = time.Now().Unix() // 设置时间戳 //重新为err赋值 - _, err = apiv2.PostGroupMessage(context.TODO(), message.Params.GroupID.(string), groupMessage) + resp, err = apiv2.PostGroupMessage(context.TODO(), message.Params.GroupID.(string), groupMessage) if err != nil { mylog.Printf("发送文本报错信息失败: %v", err) } @@ -426,7 +426,7 @@ func HandleSendGroupMsg(client callapi.Client, api openapi.OpenAPI, apiv2 openap } groupMessage.Timestamp = time.Now().Unix() // 设置时间戳 //重新为err赋值 - _, err = apiv2.PostGroupMessage(context.TODO(), message.Params.GroupID.(string), groupMessage) + resp, err = apiv2.PostGroupMessage(context.TODO(), message.Params.GroupID.(string), groupMessage) if err != nil { mylog.Printf("发送图片失败: %v", err) } @@ -439,7 +439,7 @@ func HandleSendGroupMsg(client callapi.Client, api openapi.OpenAPI, apiv2 openap } } //发送成功回执 - retmsg, _ = SendResponse(client, err, &message) + retmsg, _ = SendResponse(client, err, &message, resp) } } case "guild": @@ -447,12 +447,12 @@ func HandleSendGroupMsg(client callapi.Client, api openapi.OpenAPI, apiv2 openap message.Params.ChannelID = message.Params.GroupID.(string) var RChannelID string if message.Params.UserID != nil && config.GetIdmapPro() { - RChannelID, _, err = idmap.RetrieveRowByIDv2Pro(message.Params.ChannelID, message.Params.UserID.(string)) + RChannelID, _, err = idmap.RetrieveRowByIDv2Pro(message.Params.ChannelID.(string), message.Params.UserID.(string)) mylog.Printf("测试,通过Proid获取的RChannelID:%v", RChannelID) } if RChannelID == "" { // 使用RetrieveRowByIDv2还原真实的ChannelID - RChannelID, err = idmap.RetrieveRowByIDv2(message.Params.ChannelID) + RChannelID, err = idmap.RetrieveRowByIDv2(message.Params.ChannelID.(string)) } if err != nil { mylog.Printf("error retrieving real RChannelID: %v", err) @@ -472,11 +472,11 @@ func HandleSendGroupMsg(client callapi.Client, api openapi.OpenAPI, apiv2 openap return "", nil } if Vuserid != "" && config.GetIdmapPro() { - RChannelID, _, err = idmap.RetrieveRowByIDv2Pro(message.Params.ChannelID, Vuserid) + RChannelID, _, err = idmap.RetrieveRowByIDv2Pro(message.Params.ChannelID.(string), Vuserid) mylog.Printf("测试,通过Proid获取的RChannelID:%v", RChannelID) } else { // 使用RetrieveRowByIDv2还原真实的ChannelID - RChannelID, err = idmap.RetrieveRowByIDv2(message.Params.ChannelID) + RChannelID, err = idmap.RetrieveRowByIDv2(message.Params.ChannelID.(string)) } if err != nil { mylog.Printf("error retrieving real ChannelID: %v", err) @@ -497,12 +497,12 @@ func HandleSendGroupMsg(client callapi.Client, api openapi.OpenAPI, apiv2 openap message.Params.ChannelID = message.Params.GroupID.(string) var RChannelID string if message.Params.UserID != nil && config.GetIdmapPro() { - RChannelID, _, err = idmap.RetrieveRowByIDv2Pro(message.Params.ChannelID, message.Params.UserID.(string)) + RChannelID, _, err = idmap.RetrieveRowByIDv2Pro(message.Params.ChannelID.(string), message.Params.UserID.(string)) mylog.Printf("测试,通过Proid获取的RChannelID:%v", RChannelID) } if RChannelID == "" { // 使用RetrieveRowByIDv2还原真实的ChannelID - RChannelID, err = idmap.RetrieveRowByIDv2(message.Params.ChannelID) + RChannelID, err = idmap.RetrieveRowByIDv2(message.Params.ChannelID.(string)) } if err != nil { mylog.Printf("error retrieving real RChannelID: %v", err) diff --git a/handlers/send_group_msg_raw.go b/handlers/send_group_msg_raw.go new file mode 100644 index 00000000..e3ecb263 --- /dev/null +++ b/handlers/send_group_msg_raw.go @@ -0,0 +1,493 @@ +package handlers + +import ( + "context" + "regexp" + "strings" + "time" + + "github.com/hoshinonyaruko/gensokyo/callapi" + "github.com/hoshinonyaruko/gensokyo/config" + "github.com/hoshinonyaruko/gensokyo/echo" + "github.com/hoshinonyaruko/gensokyo/idmap" + "github.com/hoshinonyaruko/gensokyo/mylog" + "github.com/tencent-connect/botgo/dto" + "github.com/tencent-connect/botgo/dto/keyboard" + "github.com/tencent-connect/botgo/openapi" +) + +func init() { + callapi.RegisterHandler("send_group_msg_raw", HandleSendGroupMsgRaw) +} + +func HandleSendGroupMsgRaw(client callapi.Client, api openapi.OpenAPI, apiv2 openapi.OpenAPI, message callapi.ActionMessage) (string, error) { + // 使用 message.Echo 作为key来获取消息类型 + var msgType string + if echoStr, ok := message.Echo.(string); ok { + // 当 message.Echo 是字符串类型时执行此块 + msgType = echo.GetMsgTypeByKey(echoStr) + } + // 检查GroupID是否为0 + checkZeroGroupID := func(id interface{}) bool { + switch v := id.(type) { + case int: + return v != 0 + case int64: + return v != 0 + case string: + return v != "0" // 检查字符串形式的0 + default: + return true // 如果不是int、int64或string,假定它不为0 + } + } + + // 检查UserID是否为0 + checkZeroUserID := func(id interface{}) bool { + switch v := id.(type) { + case int: + return v != 0 + case int64: + return v != 0 + case string: + return v != "0" // 同样检查字符串形式的0 + default: + return true // 如果不是int、int64或string,假定它不为0 + } + } + + if msgType == "" && message.Params.GroupID != nil && checkZeroGroupID(message.Params.GroupID) { + msgType = GetMessageTypeByGroupid(config.GetAppIDStr(), message.Params.GroupID) + } + if msgType == "" && message.Params.UserID != nil && checkZeroUserID(message.Params.UserID) { + msgType = GetMessageTypeByUserid(config.GetAppIDStr(), message.Params.UserID) + } + if msgType == "" && message.Params.GroupID != nil && checkZeroGroupID(message.Params.GroupID) { + msgType = GetMessageTypeByGroupidV2(message.Params.GroupID) + } + if msgType == "" && message.Params.UserID != nil && checkZeroUserID(message.Params.UserID) { + msgType = GetMessageTypeByUseridV2(message.Params.UserID) + } + // New checks for UserID and GroupID being nil or 0 + if (message.Params.UserID == nil || !checkZeroUserID(message.Params.UserID)) && + (message.Params.GroupID == nil || !checkZeroGroupID(message.Params.GroupID)) { + mylog.Printf("send_group_msgs接收到错误action: %v", message) + return "", nil + } + mylog.Printf("send_group_msg获取到信息类型:%v", msgType) + var idInt64 int64 + var err error + var retmsg string + + if message.Params.GroupID != "" { + idInt64, err = ConvertToInt64(message.Params.GroupID) + } else if message.Params.UserID != "" { + idInt64, err = ConvertToInt64(message.Params.UserID) + } + + //设置递归 对直接向gsk发送action时有效果 + if msgType == "" { + messageCopy := message + if err != nil { + mylog.Printf("错误:无法转换 ID %v\n", err) + } else { + // 递归3次 + echo.AddMapping(idInt64, 4) + // 递归调用handleSendGroupMsg,使用设置的消息类型 + echo.AddMsgType(config.GetAppIDStr(), idInt64, "group_private") + retmsg, _ = HandleSendGroupMsg(client, api, apiv2, messageCopy) + } + } + + switch msgType { + case "group": + // 解析消息内容 + messageText, foundItems := parseMessageContent(message.Params, message, client, api, apiv2) + var SSM bool + + var originalGroupID, originalUserID string + // 检查UserID是否为nil + if message.Params.UserID != nil && config.GetIdmapPro() { + // 如果UserID不是nil且配置为使用Pro版本,则调用RetrieveRowByIDv2Pro + originalGroupID, originalUserID, err = idmap.RetrieveRowByIDv2Pro(message.Params.GroupID.(string), message.Params.UserID.(string)) + if err != nil { + mylog.Printf("Error1 retrieving original GroupID: %v", err) + } + mylog.Printf("测试,通过idmaps-pro获取的originalGroupID:%v", originalGroupID) + if originalGroupID == "" { + originalGroupID, err = idmap.RetrieveRowByIDv2(message.Params.GroupID.(string)) + if err != nil { + mylog.Printf("Error2 retrieving original GroupID: %v", err) + return "", nil + } + mylog.Printf("测试,通过idmaps获取的originalGroupID:%v", originalGroupID) + } + } else { + // 如果UserID是nil或配置不使用Pro版本,则调用RetrieveRowByIDv2 + originalGroupID, err = idmap.RetrieveRowByIDv2(message.Params.GroupID.(string)) + if err != nil { + mylog.Printf("Error retrieving original GroupID: %v", err) + } + // 检查 message.Params.UserID 是否为 nil + if message.Params.UserID == nil { + //mylog.Println("UserID is nil") + } else { + // 进行类型断言,确认 UserID 不是 nil + userID, ok := message.Params.UserID.(string) + if !ok { + mylog.Println("UserID is not a string") + // 处理类型断言失败的情况 + } else { + originalUserID, err = idmap.RetrieveRowByIDv2(userID) + if err != nil { + mylog.Printf("Error retrieving original UserID: %v", err) + } + } + } + } + message.Params.GroupID = originalGroupID + message.Params.UserID = originalUserID + + // 检查字符串是否仅包含数字 + isNumeric := func(s string) bool { + return regexp.MustCompile(`^\d+$`).MatchString(s) + } + + messageID := message.Params.MessageID.(string) + + if isNumeric(messageID) && messageID != "0" { + // 当messageID是字符串形式的数字时,执行转换 + RealMsgID, err := idmap.RetrieveRowByIDv2(messageID) + if err != nil { + mylog.Printf("error retrieving real MessageID: %v", err) + } else { + // 重新赋值,RealMsgID的类型与message.Params.MessageID兼容 + messageID = RealMsgID + } + } + + //2000是群主动 此时不能被动转主动 + if SSM { + //mylog.Printf("正在使用Msgid:%v 补发之前失败的主动信息,请注意AtoP不要设置超过3,否则可能会影响正常信息发送", messageID) + //mylog.Printf("originalGroupID:%v ", originalGroupID) + SendStackMessages(apiv2, messageID, message.Params.GroupID.(string)) + } + mylog.Println("群组发信息messageText:", messageText) + + mylog.Printf("群组发信息使用messageID:[%v]", messageID) + var singleItem = make(map[string][]string) + var imageType, imageUrl string + imageCount := 0 + + // 检查不同类型的图片并计算数量 + if imageURLs, ok := foundItems["local_image"]; ok && len(imageURLs) == 1 { + imageType = "local_image" + imageUrl = imageURLs[0] + imageCount++ + } else if imageURLs, ok := foundItems["url_image"]; ok && len(imageURLs) == 1 { + imageType = "url_image" + imageUrl = imageURLs[0] + imageCount++ + } else if imageURLs, ok := foundItems["url_images"]; ok && len(imageURLs) == 1 { + imageType = "url_images" + imageUrl = imageURLs[0] + imageCount++ + } else if base64Images, ok := foundItems["base64_image"]; ok && len(base64Images) == 1 { + imageType = "base64_image" + imageUrl = base64Images[0] + imageCount++ + } + + if imageCount == 1 && messageText != "" { + mylog.Printf("发图文混合信息-群") + // 创建包含单个图片的 singleItem + singleItem[imageType] = []string{imageUrl} + msgseq := echo.GetMappingSeq(messageID) + echo.AddMappingSeq(messageID, msgseq+1) + groupReply := generateGroupMessage(messageID, singleItem, "", msgseq+1, apiv2, message.Params.GroupID.(string)) + // 进行类型断言 + richMediaMessage, ok := groupReply.(*dto.RichMediaMessage) + if !ok { + mylog.Printf("Error: Expected RichMediaMessage type for key ") + return "", nil + } + 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) + } + + //如果没有转换成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() // 设置时间戳 + + } + // 发送组合消息 + resp, err := apiv2.PostGroupMessage(context.TODO(), message.Params.GroupID.(string), groupMessage) + if err != nil { + mylog.Printf("发送组合消息失败: %v", err) + } + if err != nil && strings.Contains(err.Error(), `"code":22009`) { + mylog.Printf("信息发送失败,加入到队列中,下次被动信息进行发送") + var pair echo.MessageGroupPair + pair.Group = message.Params.GroupID.(string) + pair.GroupMessage = groupMessage + echo.PushGlobalStack(pair) + } + + // 发送成功回执 + retmsg, _ = SendResponse(client, err, &message, resp) + + delete(foundItems, imageType) // 从foundItems中删除已处理的图片项 + messageText = "" + } + + // 优先发送文本信息 + if messageText != "" { + msgseq := echo.GetMappingSeq(messageID) + echo.AddMappingSeq(messageID, msgseq+1) + groupReply := generateGroupMessage(messageID, nil, messageText, msgseq+1, apiv2, message.Params.GroupID.(string)) + + // 进行类型断言 + groupMessage, ok := groupReply.(*dto.MessageToCreate) + if !ok { + mylog.Println("Error: Expected MessageToCreate type.") + return "", nil // 或其他错误处理 + } + + groupMessage.Timestamp = time.Now().Unix() // 设置时间戳 + //重新为err赋值 + resp, err := apiv2.PostGroupMessage(context.TODO(), message.Params.GroupID.(string), groupMessage) + if err != nil { + mylog.Printf("发送文本群组信息失败: %v", err) + } + if err != nil && strings.Contains(err.Error(), `"code":22009`) { + mylog.Printf("信息发送失败,加入到队列中,下次被动信息进行发送") + var pair echo.MessageGroupPair + pair.Group = message.Params.GroupID.(string) + pair.GroupMessage = groupMessage + echo.PushGlobalStack(pair) + } + //发送成功回执 + retmsg, _ = SendResponse(client, err, &message, resp) + } + var resp *dto.GroupMessageResponse + // 遍历foundItems并发送每种信息 + for key, urls := range foundItems { + for _, url := range urls { + var singleItem = make(map[string][]string) + singleItem[key] = []string{url} // 创建一个只包含一个 URL 的 singleItem + //mylog.Println("singleItem:", singleItem) + msgseq := echo.GetMappingSeq(messageID) + echo.AddMappingSeq(messageID, msgseq+1) + groupReply := generateGroupMessage(messageID, singleItem, "", msgseq+1, apiv2, message.Params.GroupID.(string)) + // 进行类型断言 + richMediaMessage, ok := groupReply.(*dto.RichMediaMessage) + if !ok { + mylog.Printf("Error: Expected RichMediaMessage type for key %s.", key) + if key == "markdown" || key == "qqmusic" { + // 进行类型断言 + groupMessage, ok := groupReply.(*dto.MessageToCreate) + if !ok { + mylog.Println("Error: Expected MessageToCreate type.") + return "", nil // 或其他错误处理 + } + //重新为err赋值 + resp, err := apiv2.PostGroupMessage(context.TODO(), message.Params.GroupID.(string), groupMessage) + if err != nil { + mylog.Printf("发送md信息失败: %v", err) + } + if err != nil && strings.Contains(err.Error(), `"code":22009`) { + mylog.Printf("信息发送失败,加入到队列中,下次被动信息进行发送") + var pair echo.MessageGroupPair + pair.Group = message.Params.GroupID.(string) + pair.GroupMessage = groupMessage + echo.PushGlobalStack(pair) + } + //发送成功回执 + retmsg, _ = SendResponse(client, err, &message, resp) + } + continue // 跳过这个项,继续下一个 + } + message_return, err := apiv2.PostGroupMessage(context.TODO(), message.Params.GroupID.(string), richMediaMessage) + if err != nil { + mylog.Printf("发送 %s 信息失败_send_group_msg: %v", key, err) + if config.GetSendError() { //把报错当作文本发出去 + msgseq := echo.GetMappingSeq(messageID) + echo.AddMappingSeq(messageID, msgseq+1) + groupReply := generateGroupMessage(messageID, nil, err.Error(), msgseq+1, apiv2, message.Params.GroupID.(string)) + // 进行类型断言 + groupMessage, ok := groupReply.(*dto.MessageToCreate) + if !ok { + mylog.Println("Error: Expected MessageToCreate type.") + return "", nil // 或其他错误处理 + } + groupMessage.Timestamp = time.Now().Unix() // 设置时间戳 + //重新为err赋值 + resp, err = apiv2.PostGroupMessage(context.TODO(), message.Params.GroupID.(string), groupMessage) + if err != nil { + mylog.Printf("发送文本报错信息失败: %v", err) + } + if err != nil && strings.Contains(err.Error(), `"code":22009`) { + mylog.Printf("信息发送失败,加入到队列中,下次被动信息进行发送") + var pair echo.MessageGroupPair + pair.Group = message.Params.GroupID.(string) + pair.GroupMessage = groupMessage + echo.PushGlobalStack(pair) + } + } + } + if message_return != nil && message_return.MediaResponse != nil && message_return.MediaResponse.FileInfo != "" { + msgseq := echo.GetMappingSeq(messageID) + echo.AddMappingSeq(messageID, msgseq+1) + media := dto.Media{ + FileInfo: message_return.MediaResponse.FileInfo, + } + groupMessage := &dto.MessageToCreate{ + Content: " ", + MsgID: messageID, + MsgSeq: msgseq, + MsgType: 7, // 默认文本类型 + Media: media, + } + groupMessage.Timestamp = time.Now().Unix() // 设置时间戳 + //重新为err赋值 + resp, err = apiv2.PostGroupMessage(context.TODO(), message.Params.GroupID.(string), groupMessage) + if err != nil { + mylog.Printf("发送图片失败: %v", err) + } + if err != nil && strings.Contains(err.Error(), `"code":22009`) { + mylog.Printf("信息发送失败,加入到队列中,下次被动信息进行发送") + var pair echo.MessageGroupPair + pair.Group = message.Params.GroupID.(string) + pair.GroupMessage = groupMessage + echo.PushGlobalStack(pair) + } + } + //发送成功回执 + retmsg, _ = SendResponse(client, err, &message, resp) + } + } + case "guild": + //用GroupID给ChannelID赋值,因为我们是把频道虚拟成了群 + message.Params.ChannelID = message.Params.GroupID.(string) + var RChannelID string + if message.Params.UserID != nil && config.GetIdmapPro() { + RChannelID, _, err = idmap.RetrieveRowByIDv2Pro(message.Params.ChannelID.(string), message.Params.UserID.(string)) + mylog.Printf("测试,通过Proid获取的RChannelID:%v", RChannelID) + } + if RChannelID == "" { + // 使用RetrieveRowByIDv2还原真实的ChannelID + RChannelID, err = idmap.RetrieveRowByIDv2(message.Params.ChannelID.(string)) + } + if err != nil { + mylog.Printf("error retrieving real RChannelID: %v", err) + } + message.Params.ChannelID = RChannelID + //这一句是group_private的逻辑,发频道信息用的是channelid + //message.Params.GroupID = value + retmsg, _ = HandleSendGuildChannelMsg(client, api, apiv2, message) + case "guild_private": + //用group_id还原出channelid 这是虚拟成群的私聊信息 + var RChannelID string + var Vuserid string + message.Params.ChannelID = message.Params.GroupID.(string) + Vuserid, ok := message.Params.UserID.(string) + if !ok { + mylog.Printf("Error illegal UserID") + return "", nil + } + if Vuserid != "" && config.GetIdmapPro() { + RChannelID, _, err = idmap.RetrieveRowByIDv2Pro(message.Params.ChannelID.(string), Vuserid) + mylog.Printf("测试,通过Proid获取的RChannelID:%v", RChannelID) + } else { + // 使用RetrieveRowByIDv2还原真实的ChannelID + RChannelID, err = idmap.RetrieveRowByIDv2(message.Params.ChannelID.(string)) + } + if err != nil { + mylog.Printf("error retrieving real ChannelID: %v", err) + } + //读取ini 通过ChannelID取回之前储存的guild_id + value, err := idmap.ReadConfigv2(RChannelID, "guild_id") + if err != nil { + mylog.Printf("Error reading config: %v", err) + return "", nil + } + retmsg, _ = HandleSendGuildChannelPrivateMsg(client, api, apiv2, message, &value, &RChannelID) + case "group_private": + //用userid还原出openid 这是虚拟成群的群聊私聊信息 + message.Params.UserID = message.Params.GroupID.(string) + retmsg, _ = HandleSendPrivateMsg(client, api, apiv2, message) + case "forum": + //用GroupID给ChannelID赋值,因为我们是把频道虚拟成了群 + message.Params.ChannelID = message.Params.GroupID.(string) + var RChannelID string + if message.Params.UserID != nil && config.GetIdmapPro() { + RChannelID, _, err = idmap.RetrieveRowByIDv2Pro(message.Params.ChannelID.(string), message.Params.UserID.(string)) + mylog.Printf("测试,通过Proid获取的RChannelID:%v", RChannelID) + } + if RChannelID == "" { + // 使用RetrieveRowByIDv2还原真实的ChannelID + RChannelID, err = idmap.RetrieveRowByIDv2(message.Params.ChannelID.(string)) + } + if err != nil { + mylog.Printf("error retrieving real RChannelID: %v", err) + } + message.Params.ChannelID = RChannelID + //这一句是group_private的逻辑,发频道信息用的是channelid + //message.Params.GroupID = value + retmsg, _ = HandleSendGuildChannelForum(client, api, apiv2, message) + default: + mylog.Printf("Unknown message type: %s", msgType) + } + //重置递归类型 + if echo.GetMapping(idInt64) <= 0 { + echo.AddMsgType(config.GetAppIDStr(), idInt64, "") + } + echo.AddMapping(idInt64, echo.GetMapping(idInt64)-1) + + //递归3次枚举类型 + if echo.GetMapping(idInt64) > 0 { + tryMessageTypes := []string{"group", "guild", "guild_private"} + messageCopy := message // 创建message的副本 + echo.AddMsgType(config.GetAppIDStr(), idInt64, tryMessageTypes[echo.GetMapping(idInt64)-1]) + delay := config.GetSendDelay() + time.Sleep(time.Duration(delay) * time.Millisecond) + retmsg, _ = HandleSendGroupMsg(client, api, apiv2, messageCopy) + } + return retmsg, nil +} diff --git a/handlers/send_guild_channel_forum.go b/handlers/send_guild_channel_forum.go index 7b1d5ee4..fd686c48 100644 --- a/handlers/send_guild_channel_forum.go +++ b/handlers/send_guild_channel_forum.go @@ -70,6 +70,12 @@ func HandleSendGuildChannelForum(client callapi.Client, api openapi.OpenAPI, api if msgType == "" && message.Params.UserID != nil && checkZeroUserID(message.Params.UserID) { msgType = GetMessageTypeByUseridV2(message.Params.UserID) } + // New checks for UserID and GroupID being nil or 0 + if (message.Params.UserID == nil || !checkZeroUserID(message.Params.UserID)) && + (message.Params.GroupID == nil || !checkZeroGroupID(message.Params.GroupID)) { + mylog.Printf("send_group_msgs接收到错误action: %v", message) + return "", nil + } //当不转换频道信息时(不支持频道私聊) if msgType == "" { @@ -90,12 +96,12 @@ func HandleSendGuildChannelForum(client callapi.Client, api openapi.OpenAPI, api if err != nil { mylog.Printf("组合帖子信息失败: %v", err) } - if _, err = api.PostFourm(context.TODO(), channelID, Forum); err != nil { + if _, err = api.PostFourm(context.TODO(), channelID.(string), Forum); err != nil { mylog.Printf("发送帖子信息失败: %v", err) } //发送成功回执 - retmsg, _ = SendResponse(client, err, &message) + retmsg, _ = SendResponse(client, err, &message, nil) default: mylog.Printf("2Unknown message type: %s", msgType) diff --git a/handlers/send_guild_channel_msg.go b/handlers/send_guild_channel_msg.go index a9e8a48b..aeb263a9 100644 --- a/handlers/send_guild_channel_msg.go +++ b/handlers/send_guild_channel_msg.go @@ -74,6 +74,12 @@ func HandleSendGuildChannelMsg(client callapi.Client, api openapi.OpenAPI, apiv2 if msgType == "" && message.Params.UserID != nil && checkZeroUserID(message.Params.UserID) { msgType = GetMessageTypeByUseridV2(message.Params.UserID) } + // New checks for UserID and GroupID being nil or 0 + if (message.Params.UserID == nil || !checkZeroUserID(message.Params.UserID)) && + (message.Params.GroupID == nil || !checkZeroGroupID(message.Params.GroupID)) { + mylog.Printf("send_group_msgs接收到错误action: %v", message) + return "", nil + } //当不转换频道信息时(不支持频道私聊) if msgType == "" { msgType = "guild" @@ -89,7 +95,7 @@ func HandleSendGuildChannelMsg(client callapi.Client, api openapi.OpenAPI, apiv2 var messageID string if config.GetLazyMessageId() { //由于实现了Params的自定义unmarshell 所以可以类型安全的断言为string - messageID = echo.GetLazyMessagesId(channelID) + messageID = echo.GetLazyMessagesId(channelID.(string)) mylog.Printf("GetLazyMessagesId: %v", messageID) } if messageID == "" { @@ -136,6 +142,7 @@ func HandleSendGuildChannelMsg(client callapi.Client, api openapi.OpenAPI, apiv2 imageCount++ } + var resp *dto.Message if imageCount == 1 && messageText != "" { //我想优化一下这里,让它优雅一点 mylog.Printf("发图文混合信息-频道") @@ -157,7 +164,7 @@ func HandleSendGuildChannelMsg(client callapi.Client, api openapi.OpenAPI, apiv2 } newMessage.Timestamp = time.Now().Unix() // 设置时间戳 - if _, err = api.PostMessage(context.TODO(), channelID, newMessage); err != nil { + if _, err = api.PostMessage(context.TODO(), channelID.(string), newMessage); err != nil { mylog.Printf("发送图文混合信息失败: %v", err) } // 检查是否是 40003 错误 @@ -184,7 +191,7 @@ func HandleSendGuildChannelMsg(client callapi.Client, api openapi.OpenAPI, apiv2 mylog.Printf("Error compressing image: %v", err) } // 使用 Multipart 方法发送 - if _, err = api.PostMessageMultipart(context.TODO(), channelID, newMessage, compressedData); err != nil { + if _, err = api.PostMessageMultipart(context.TODO(), channelID.(string), newMessage, compressedData); err != nil { mylog.Printf("40003重试,使用 multipart 发送图文混合信息失败: %v message_id %v", err, messageID) } } @@ -212,12 +219,12 @@ func HandleSendGuildChannelMsg(client callapi.Client, api openapi.OpenAPI, apiv2 } newMessage.Timestamp = time.Now().Unix() // 设置时间戳 // 使用Multipart方法发送 - if _, err = api.PostMessageMultipart(context.TODO(), channelID, newMessage, compressedData); err != nil { + if resp, err = api.PostMessageMultipart(context.TODO(), channelID.(string), newMessage, compressedData); err != nil { mylog.Printf("使用multipart发送图文信息失败: %v message_id %v", err, messageID) } } // 发送成功回执 - retmsg, _ = SendResponse(client, err, &message) + retmsg, _ = SendGuildResponse(client, err, &message, resp) delete(foundItems, imageType) // 从foundItems中删除已处理的图片项 messageText = "" } @@ -228,11 +235,11 @@ func HandleSendGuildChannelMsg(client callapi.Client, api openapi.OpenAPI, apiv2 msgseq := echo.GetMappingSeq(messageID) echo.AddMappingSeq(messageID, msgseq+1) textMsg, _ := GenerateReplyMessage(messageID, nil, messageText, msgseq+1) - if _, err = api.PostMessage(context.TODO(), channelID, textMsg); err != nil { + if resp, err = api.PostMessage(context.TODO(), channelID.(string), textMsg); err != nil { mylog.Printf("发送文本信息失败: %v", err) } //发送成功回执 - retmsg, _ = SendResponse(client, err, &message) + retmsg, _ = SendGuildResponse(client, err, &message, resp) } // 遍历foundItems并发送每种信息 @@ -259,13 +266,13 @@ func HandleSendGuildChannelMsg(client callapi.Client, api openapi.OpenAPI, apiv2 mylog.Printf("Error compressing image: %v", err) } // 使用Multipart方法发送 - if _, err = api.PostMessageMultipart(context.TODO(), channelID, reply, compressedData); err != nil { + if resp, err = api.PostMessageMultipart(context.TODO(), channelID.(string), reply, compressedData); err != nil { mylog.Printf("使用multipart发送 %s 信息失败: %v message_id %v", key, err, messageID) } //发送成功回执 - retmsg, _ = SendResponse(client, err, &message) + retmsg, _ = SendGuildResponse(client, err, &message, resp) } else { - if _, err = api.PostMessage(context.TODO(), channelID, reply); err != nil { + if _, err = api.PostMessage(context.TODO(), channelID.(string), reply); err != nil { mylog.Printf("发送 %s 信息失败: %v", key, err) } // 检查是否是 40003 错误 @@ -292,14 +299,14 @@ func HandleSendGuildChannelMsg(client callapi.Client, api openapi.OpenAPI, apiv2 mylog.Printf("Error compressing image: %v", err) } // 使用 Multipart 方法发送 - if _, err = api.PostMessageMultipart(context.TODO(), channelID, reply, compressedData); err != nil { + if resp, err = api.PostMessageMultipart(context.TODO(), channelID.(string), reply, compressedData); err != nil { mylog.Printf("40003重试,使用 multipart 发送 %s 信息失败: %v message_id %v", key, err, messageID) } } } } //发送成功回执 - retmsg, _ = SendResponse(client, err, &message) + retmsg, _ = SendGuildResponse(client, err, &message, resp) } } } @@ -311,11 +318,12 @@ func HandleSendGuildChannelMsg(client callapi.Client, api openapi.OpenAPI, apiv2 var RChannelID string var err error // 使用RetrieveRowByIDv2还原真实的ChannelID - RChannelID, err = idmap.RetrieveRowByIDv2(channelID) + RChannelID, err = idmap.RetrieveRowByIDv2(channelID.(string)) if err != nil { mylog.Printf("error retrieving real UserID: %v", err) } - retmsg, _ = HandleSendGuildChannelPrivateMsg(client, api, apiv2, message, &guildID, &RChannelID) + RguildID := guildID.(string) + retmsg, _ = HandleSendGuildChannelPrivateMsg(client, api, apiv2, message, &RguildID, &RChannelID) case "forum": //api一样的 直接丢进去试试 retmsg, _ = HandleSendGuildChannelForum(client, api, apiv2, message) diff --git a/handlers/send_guild_private_msg.go b/handlers/send_guild_private_msg.go index 72e7bf30..2177aa18 100644 --- a/handlers/send_guild_private_msg.go +++ b/handlers/send_guild_private_msg.go @@ -196,17 +196,17 @@ func HandleSendGuildChannelPrivateMsg(client callapi.Client, api openapi.OpenAPI ChannelID: channelID, CreateTime: timestampStr, } - + var resp *dto.Message // 优先发送文本信息 if messageText != "" { msgseq := echo.GetMappingSeq(messageID) echo.AddMappingSeq(messageID, msgseq+1) textMsg, _ := GenerateReplyMessage(messageID, nil, messageText, msgseq+1) - if _, err = apiv2.PostDirectMessage(context.TODO(), dm, textMsg); err != nil { + if resp, err = apiv2.PostDirectMessage(context.TODO(), dm, textMsg); err != nil { mylog.Printf("发送文本信息失败: %v", err) } //发送成功回执 - retmsg, _ = SendResponse(client, err, &message) + retmsg, _ = SendGuildPrivateResponse(client, err, &message, resp, guildID) } // 遍历foundItems并发送每种信息 @@ -232,16 +232,16 @@ func HandleSendGuildChannelPrivateMsg(client callapi.Client, api openapi.OpenAPI if err != nil { mylog.Printf("Error compressing image: %v", err) } - if _, err = api.PostDirectMessageMultipart(context.TODO(), dm, reply, compressedData); err != nil { + if resp, err = api.PostDirectMessageMultipart(context.TODO(), dm, reply, compressedData); err != nil { mylog.Printf("使用multipart发送 %s 信息失败: %v message_id %v", key, err, messageID) } - retmsg, _ = SendResponse(client, err, &message) + retmsg, _ = SendGuildResponse(client, err, &message, resp) } else { // 处理非 Base64 图片的逻辑 if _, err = api.PostDirectMessage(context.TODO(), dm, reply); err != nil { mylog.Printf("发送 %s 信息失败: %v", key, err) } - retmsg, _ = SendResponse(client, err, &message) + retmsg, _ = SendGuildPrivateResponse(client, err, &message, resp, guildID) } } } diff --git a/handlers/send_msg.go b/handlers/send_msg.go index 42578736..5a4137e8 100644 --- a/handlers/send_msg.go +++ b/handlers/send_msg.go @@ -122,12 +122,12 @@ func HandleSendMsg(client callapi.Client, api openapi.OpenAPI, apiv2 openapi.Ope message.Params.ChannelID = message.Params.GroupID.(string) var RChannelID string if message.Params.UserID != nil && config.GetIdmapPro() { - RChannelID, _, err = idmap.RetrieveRowByIDv2Pro(message.Params.ChannelID, message.Params.UserID.(string)) + RChannelID, _, err = idmap.RetrieveRowByIDv2Pro(message.Params.ChannelID.(string), message.Params.UserID.(string)) mylog.Printf("测试,通过Proid获取的RChannelID:%v", RChannelID) } if RChannelID == "" { // 使用RetrieveRowByIDv2还原真实的ChannelID - RChannelID, err = idmap.RetrieveRowByIDv2(message.Params.ChannelID) + RChannelID, err = idmap.RetrieveRowByIDv2(message.Params.ChannelID.(string)) } if err != nil { mylog.Printf("error retrieving real RChannelID: %v", err) @@ -148,12 +148,12 @@ func HandleSendMsg(client callapi.Client, api openapi.OpenAPI, apiv2 openapi.Ope message.Params.ChannelID = message.Params.GroupID.(string) var RChannelID string if message.Params.UserID != nil && config.GetIdmapPro() { - RChannelID, _, err = idmap.RetrieveRowByIDv2Pro(message.Params.ChannelID, message.Params.UserID.(string)) + RChannelID, _, err = idmap.RetrieveRowByIDv2Pro(message.Params.ChannelID.(string), message.Params.UserID.(string)) mylog.Printf("测试,通过Proid获取的RChannelID:%v", RChannelID) } if RChannelID == "" { // 使用RetrieveRowByIDv2还原真实的ChannelID - RChannelID, err = idmap.RetrieveRowByIDv2(message.Params.ChannelID) + RChannelID, err = idmap.RetrieveRowByIDv2(message.Params.ChannelID.(string)) } if err != nil { mylog.Printf("error retrieving real RChannelID: %v", err) diff --git a/handlers/send_private_msg.go b/handlers/send_private_msg.go index f8daf4c5..be98f276 100644 --- a/handlers/send_private_msg.go +++ b/handlers/send_private_msg.go @@ -96,7 +96,7 @@ func HandleSendPrivateMsg(client callapi.Client, api openapi.OpenAPI, apiv2 open HandleSendPrivateMsg(client, api, apiv2, messageCopy) } } - + var resp *dto.C2CMessageResponse switch msgType { //这里是pr上来的,我也不明白为什么私聊会出现group类型 猜测是为了匹配包含了groupid的私聊? case "group_private", "group": @@ -206,14 +206,14 @@ func HandleSendPrivateMsg(client callapi.Client, api openapi.OpenAPI, apiv2 open groupMessage.Timestamp = time.Now().Unix() // 设置时间戳 // 发送组合消息 - _, err = apiv2.PostC2CMessage(context.TODO(), UserID, groupMessage) + resp, err = apiv2.PostC2CMessage(context.TODO(), UserID, groupMessage) if err != nil { mylog.Printf("发送组合消息失败: %v", err) return "", nil // 或其他错误处理 } // 发送成功回执 - retmsg, _ = SendResponse(client, err, &message) + retmsg, _ = SendC2CResponse(client, err, &message, resp) delete(foundItems, imageType) // 从foundItems中删除已处理的图片项 messageText = "" @@ -233,14 +233,14 @@ func HandleSendPrivateMsg(client callapi.Client, api openapi.OpenAPI, apiv2 open } groupMessage.Timestamp = time.Now().Unix() // 设置时间戳 - _, err := apiv2.PostC2CMessage(context.TODO(), UserID, groupMessage) + resp, err := apiv2.PostC2CMessage(context.TODO(), UserID, groupMessage) if err != nil { mylog.Printf("发送文本私聊信息失败: %v", err) //如果失败 防止进入递归 return "", nil } //发送成功回执 - retmsg, _ = SendResponse(client, err, &message) + retmsg, _ = SendC2CResponse(client, err, &message, resp) } // 遍历foundItems并发送每种信息 @@ -294,13 +294,13 @@ func HandleSendPrivateMsg(client callapi.Client, api openapi.OpenAPI, apiv2 open } groupMessage.Timestamp = time.Now().Unix() // 设置时间戳 //重新为err赋值 - _, err = apiv2.PostC2CMessage(context.TODO(), UserID, groupMessage) + resp, err = apiv2.PostC2CMessage(context.TODO(), UserID, groupMessage) if err != nil { mylog.Printf("发送 %s 私聊信息失败: %v", key, err) } } //发送成功回执 - retmsg, _ = SendResponse(client, err, &message) + retmsg, _ = SendC2CResponse(client, err, &message, resp) } } //这里是pr上来的,我也不明白为什么私聊会出现guild类型 diff --git a/httpapi/httpapi.go b/httpapi/httpapi.go index 986bc2e6..5f95d4f2 100644 --- a/httpapi/httpapi.go +++ b/httpapi/httpapi.go @@ -1,11 +1,12 @@ package httpapi import ( - "github.com/hoshinonyaruko/gensokyo/config" "net/http" "strconv" "strings" + "github.com/hoshinonyaruko/gensokyo/config" + "github.com/gin-gonic/gin" "github.com/hoshinonyaruko/gensokyo/callapi" "github.com/hoshinonyaruko/gensokyo/handlers" @@ -29,6 +30,10 @@ func CombinedMiddleware(api openapi.OpenAPI, apiV2 openapi.OpenAPI) gin.HandlerF handleSendGroupMessage(c, api, apiV2) return } + if c.Request.URL.Path == "/send_group_msg_raw" { + handleSendGroupMessageRaw(c, api, apiV2) + return + } if c.Request.URL.Path == "/send_private_msg" { handleSendPrivateMessage(c, api, apiV2) return @@ -102,6 +107,59 @@ func handleSendGroupMessage(c *gin.Context, api openapi.OpenAPI, apiV2 openapi.O c.String(http.StatusOK, retmsg) } +// handleSendGroupMessageRaw 处理发送群聊消息的请求 +func handleSendGroupMessageRaw(c *gin.Context, api openapi.OpenAPI, apiV2 openapi.OpenAPI) { + var retmsg string + var req struct { + GroupID int64 `json:"group_id" form:"group_id"` + MessageID string `json:"message_id" form:"message_id"` + UserID *int64 `json:"user_id,omitempty" form:"user_id"` + Message string `json:"message" form:"message"` + AutoEscape bool `json:"auto_escape" form:"auto_escape"` + } + + // 根据请求方法解析参数 + if c.Request.Method == http.MethodGet { + // 从URL查询参数解析 + if err := c.ShouldBindQuery(&req); err != nil { + c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) + return + } + } else { + // 从JSON或表单数据解析 + if err := c.ShouldBind(&req); err != nil { + c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) + return + } + } + + // 使用解析后的参数处理请求 + client := &HttpAPIClient{} + // 创建 ActionMessage 实例 + message := callapi.ActionMessage{ + Action: "send_group_msg_raw", + Params: callapi.ParamsContent{ + GroupID: strconv.FormatInt(req.GroupID, 10), // 注意这里需要转换类型,因为 GroupID 是 int64 + MessageID: req.MessageID, + Message: req.Message, + }, + } + // 如果 UserID 存在,则加入到参数中 + if req.UserID != nil { + message.Params.UserID = strconv.FormatInt(*req.UserID, 10) + } + // 调用处理函数 + retmsg, err := handlers.HandleSendGroupMsgRaw(client, api, apiV2, message) + if err != nil { + c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) + return + } + + // 返回处理结果 + c.Header("Content-Type", "application/json") + c.String(http.StatusOK, retmsg) +} + // handleSendPrivateMessage 处理发送私聊消息的请求 func handleSendPrivateMessage(c *gin.Context, api openapi.OpenAPI, apiV2 openapi.OpenAPI) { var retmsg string From ab6c474385c4ab445f7916d75ae00d2d6babe6f0 Mon Sep 17 00:00:00 2001 From: cosmo Date: Sun, 24 Mar 2024 17:24:55 +0800 Subject: [PATCH 14/33] beta349 --- botgo/dto/message_create.go | 1 + config/config.go | 470 ++++++++++++++++++++++++++---------- images/upload_api.go | 60 +++-- main.go | 7 + template/config_template.go | 128 +++++----- 5 files changed, 452 insertions(+), 214 deletions(-) diff --git a/botgo/dto/message_create.go b/botgo/dto/message_create.go index 0e1be64b..0107c976 100644 --- a/botgo/dto/message_create.go +++ b/botgo/dto/message_create.go @@ -21,6 +21,7 @@ type RichMediaMessage struct { EventID string `json:"event_id,omitempty"` // 要回复的事件id, 逻辑同MsgID FileType uint64 `json:"file_type,omitempty"` // 业务类型,图片,文件,语音,视频 文件类型,取值:1图片,2视频,3语音(目前语音只支持silk格式) URL string `json:"url,omitempty"` + FileData string `json:"file_data,omitempty"` //没有base64头的base64 SrvSendMsg bool `json:"srv_send_msg,omitempty"` Content string `json:"content,omitempty"` } diff --git a/config/config.go b/config/config.go index d97e6d4d..a5a6bf91 100644 --- a/config/config.go +++ b/config/config.go @@ -9,6 +9,7 @@ import ( "regexp" "strings" "sync" + "time" "github.com/hoshinonyaruko/gensokyo/mylog" "github.com/hoshinonyaruko/gensokyo/sys" @@ -31,136 +32,170 @@ type VisualPrefixConfig struct { NoWhiteResponse string `yaml:"No_White_Response"` } type Settings struct { - WsAddress []string `yaml:"ws_address"` - AppID uint64 `yaml:"app_id"` - Token string `yaml:"token"` - ClientSecret string `yaml:"client_secret"` - TextIntent []string `yaml:"text_intent"` - GlobalChannelToGroup bool `yaml:"global_channel_to_group"` - GlobalPrivateToChannel bool `yaml:"global_private_to_channel"` - GlobalForumToChannel bool `yaml:"global_forum_to_channel"` - Array bool `yaml:"array"` - Server_dir string `yaml:"server_dir"` - Lotus bool `yaml:"lotus"` - Port string `yaml:"port"` - WsToken []string `yaml:"ws_token,omitempty"` // 连接wss时使用,不是wss可留空 一一对应 - MasterID []string `yaml:"master_id,omitempty"` // 如果需要在群权限判断是管理员是,将user_id填入这里,master_id是一个文本数组 - EnableWsServer bool `yaml:"enable_ws_server,omitempty"` //正向ws开关 - WsServerToken string `yaml:"ws_server_token,omitempty"` //正向ws token - IdentifyFile bool `yaml:"identify_file"` // 域名校验文件 - Crt string `yaml:"crt"` - Key string `yaml:"key"` - DeveloperLog bool `yaml:"developer_log"` - Username string `yaml:"server_user_name"` - Password string `yaml:"server_user_password"` - ImageLimit int `yaml:"image_sizelimit"` - RemovePrefix bool `yaml:"remove_prefix"` - BackupPort string `yaml:"backup_port"` - DevlopAcDir string `yaml:"develop_access_token_dir"` - RemoveAt bool `yaml:"remove_at"` - DevBotid string `yaml:"develop_bot_id"` - SandBoxMode bool `yaml:"sandbox_mode"` - Title string `yaml:"title"` - HashID bool `yaml:"hash_id"` - TwoWayEcho bool `yaml:"twoway_echo"` - LazyMessageId bool `yaml:"lazy_message_id"` - WhitePrefixMode bool `yaml:"white_prefix_mode"` - WhitePrefixs []string `yaml:"white_prefixs"` - BlackPrefixMode bool `yaml:"black_prefix_mode"` - BlackPrefixs []string `yaml:"black_prefixs"` - VisualPrefixs []VisualPrefixConfig `yaml:"visual_prefixs"` - VisibleIp bool `yaml:"visible_ip"` - ForwardMsgLimit int `yaml:"forward_msg_limit"` - DevMessgeID bool `yaml:"dev_message_id"` - LogLevel int `yaml:"log_level"` - SaveLogs bool `yaml:"save_logs"` - BindPrefix string `yaml:"bind_prefix"` - MePrefix string `yaml:"me_prefix"` - FrpPort string `yaml:"frp_port"` - RemoveBotAtGroup bool `yaml:"remove_bot_at_group"` - ImageLimitB int `yaml:"image_limit"` - RecordSampleRate int `yaml:"record_sampleRate"` - RecordBitRate int `yaml:"record_bitRate"` - NoWhiteResponse string `yaml:"No_White_Response"` - SendError bool `yaml:"send_error"` - AddAtGroup bool `yaml:"add_at_group"` - UrlPicTransfer bool `yaml:"url_pic_transfer"` - LotusPassword string `yaml:"lotus_password"` - WsServerPath string `yaml:"ws_server_path"` - IdmapPro bool `yaml:"idmap_pro"` - CardAndNick string `yaml:"card_nick"` - AutoBind bool `yaml:"auto_bind"` - CustomBotName string `yaml:"custom_bot_name"` - SendDelay int `yaml:"send_delay"` - AtoPCount int `yaml:"AMsgRetryAsPMsg_Count"` - ReconnecTimes int `yaml:"reconnect_times"` - HeartBeatInterval int `yaml:"heart_beat_interval"` - LaunchReconectTimes int `yaml:"launch_reconnect_times"` - UnlockPrefix string `yaml:"unlock_prefix"` - WhiteBypass []int64 `yaml:"white_bypass"` - TransferUrl bool `yaml:"transfer_url"` - HttpAddress string `yaml:"http_address"` - AccessToken string `yaml:"http_access_token"` - HttpVersion int `yaml:"http_version"` - HttpTimeOut int `yaml:"http_timeout"` - PostUrl []string `yaml:"post_url"` - PostSecret []string `yaml:"post_secret"` - PostMaxRetries []int `yaml:"post_max_retries"` - PostRetriesInterval []int `yaml:"post_retries_interval"` - NativeOb11 bool `yaml:"native_ob11"` - RamDomSeq bool `yaml:"ramdom_seq"` - UrlToQrimage bool `yaml:"url_to_qrimage"` - QrSize int `yaml:"qr_size"` - WhiteBypassRevers bool `yaml:"white_bypass_reverse"` - GuildUrlImageToBase64 bool `yaml:"guild_url_image_to_base64"` - TencentBucketName string `yaml:"t_COS_BUCKETNAME"` - TencentBucketRegion string `yaml:"t_COS_REGION"` - TencentCosSecretid string `yaml:"t_COS_SECRETID"` - TencentSecretKey string `yaml:"t_COS_SECRETKEY"` - TencentAudit bool `yaml:"t_audit"` - OssType int `yaml:"oss_type"` - BaiduBOSBucketName string `yaml:"b_BOS_BUCKETNAME"` - BaiduBCEAK string `yaml:"b_BCE_AK"` - BaiduBCESK string `yaml:"b_BCE_SK"` - BaiduAudit int `yaml:"b_audit"` - AliyunEndpoint string `yaml:"a_OSS_EndPoint"` - AliyunAccessKeyId string `yaml:"a_OSS_AccessKeyId"` - AliyunAccessKeySecret string `yaml:"a_OSS_AccessKeySecret"` - AliyunBucketName string `yaml:"a_OSS_BucketName"` - AliyunAudit bool `yaml:"a_audit"` - Alias []string `yaml:"alias"` - SelfIntroduce []string `yaml:"self_introduce"` - 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"` - 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"` - MusicPrefix string `yaml:"music_prefix"` - DisableWebui bool `yaml:"disable_webui"` - ShardCount int `yaml:"shard_count"` - ShardID int `yaml:"shard_id"` - BotForumTitle string `yaml:"bot_forum_title"` - GlobalInteractionToMessage bool `yaml:"global_interaction_to_message"` - AutoPutInteraction bool `yaml:"auto_put_interaction"` - PutInteractionDelay int `yaml:"put_interaction_delay"` - ImgUpApiVtv2 bool `yaml:"img_up_api_ntv2"` - Fix11300 bool `yaml:"fix_11300"` - LotusWithoutIdmaps bool `yaml:"lotus_without_idmaps"` - GetGroupListAllGuilds bool `yaml:"get_g_list_all_guilds"` - GetGroupListGuilds string `yaml:"get_g_list_guilds"` - GetGroupListReturnGuilds bool `yaml:"get_g_list_return_guilds"` - GetGroupListGuidsType int `yaml:"get_g_list_guilds_type"` - GetGroupListDelay int `yaml:"get_g_list_delay"` - GlobalServerTempQQguild bool `yaml:"global_server_temp_qqguild"` - ServerTempQQguild string `yaml:"server_temp_qqguild"` - ServerTempQQguildPool []string `yaml:"server_temp_qqguild_pool"` + //反向ws设置 + WsAddress []string `yaml:"ws_address"` + WsToken []string `yaml:"ws_token,omitempty"` + ReconnecTimes int `yaml:"reconnect_times"` + HeartBeatInterval int `yaml:"heart_beat_interval"` + LaunchReconectTimes int `yaml:"launch_reconnect_times"` + //基础配置 + AppID uint64 `yaml:"app_id"` + Uin int64 `yaml:"uin"` + Token string `yaml:"token"` + ClientSecret string `yaml:"client_secret"` + ShardCount int `yaml:"shard_count"` + ShardID int `yaml:"shard_id"` + //事件订阅类 + TextIntent []string `yaml:"text_intent"` + //转换类 + GlobalChannelToGroup bool `yaml:"global_channel_to_group"` + GlobalPrivateToChannel bool `yaml:"global_private_to_channel"` + GlobalForumToChannel bool `yaml:"global_forum_to_channel"` + GlobalInteractionToMessage bool `yaml:"global_interaction_to_message"` + HashID bool `yaml:"hash_id"` + IdmapPro bool `yaml:"idmap_pro"` + //gensokyo互联类 + Server_dir string `yaml:"server_dir"` + Port string `yaml:"port"` + BackupPort string `yaml:"backup_port"` + Lotus bool `yaml:"lotus"` + LotusPassword string `yaml:"lotus_password"` + LotusWithoutIdmaps bool `yaml:"lotus_without_idmaps"` + //增强配置 + MasterID []string `yaml:"master_id,omitempty"` + RecordSampleRate int `yaml:"record_sampleRate"` + RecordBitRate int `yaml:"record_bitRate"` + CardAndNick string `yaml:"card_nick"` + AutoBind bool `yaml:"auto_bind"` + //发图相关 + OssType int `yaml:"oss_type"` + ImageLimit int `yaml:"image_sizelimit"` + ImageLimitB int `yaml:"image_limit"` + GuildUrlImageToBase64 bool `yaml:"guild_url_image_to_base64"` + UrlPicTransfer bool `yaml:"url_pic_transfer"` + ImgUpApiVtv2 bool `yaml:"img_up_api_ntv2"` + UploadPicV2Base64 bool `yaml:"uploadpicv2_b64"` + GlobalServerTempQQguild bool `yaml:"global_server_temp_qqguild"` + ServerTempQQguild string `yaml:"server_temp_qqguild"` + ServerTempQQguildPool []string `yaml:"server_temp_qqguild_pool"` + //正向ws设置 + WsServerPath string `yaml:"ws_server_path"` + EnableWsServer bool `yaml:"enable_ws_server,omitempty"` + WsServerToken string `yaml:"ws_server_token,omitempty"` + //ssl和链接转换类 + IdentifyFile bool `yaml:"identify_file"` + IdentifyAppids []int64 `yaml:"identify_appids"` + Crt string `yaml:"crt"` + Key string `yaml:"key"` + //日志类 + DeveloperLog bool `yaml:"developer_log"` + LogLevel int `yaml:"log_level"` + SaveLogs bool `yaml:"save_logs"` + //webui相关 + DisableWebui bool `yaml:"disable_webui"` + Username string `yaml:"server_user_name"` + Password string `yaml:"server_user_password"` + //指令魔法类 + RemovePrefix bool `yaml:"remove_prefix"` + RemoveAt bool `yaml:"remove_at"` + RemoveBotAtGroup bool `yaml:"remove_bot_at_group"` + AddAtGroup bool `yaml:"add_at_group"` + WhitePrefixMode bool `yaml:"white_prefix_mode"` + VwhitePrefixMode bool `yaml:"v_white_prefix_mode"` + WhitePrefixs []string `yaml:"white_prefixs"` + WhiteBypass []int64 `yaml:"white_bypass"` + WhiteEnable []bool `yaml:"white_enable"` + WhiteBypassRevers bool `yaml:"white_bypass_reverse"` + NoWhiteResponse string `yaml:"No_White_Response"` + BlackPrefixMode bool `yaml:"black_prefix_mode"` + BlackPrefixs []string `yaml:"black_prefixs"` + Alias []string `yaml:"alias"` + Enters []string `yaml:"enters"` + VisualPrefixs []VisualPrefixConfig `yaml:"visual_prefixs"` + //开发增强类 + DevlopAcDir string `yaml:"develop_access_token_dir"` + DevBotid string `yaml:"develop_bot_id"` + SandBoxMode bool `yaml:"sandbox_mode"` + DevMessgeID bool `yaml:"dev_message_id"` + SendError bool `yaml:"send_error"` + //增长营销类 + SelfIntroduce []string `yaml:"self_introduce"` + //api修改 + GetGroupListAllGuilds bool `yaml:"get_g_list_all_guilds"` + GetGroupListGuilds string `yaml:"get_g_list_guilds"` + GetGroupListReturnGuilds bool `yaml:"get_g_list_return_guilds"` + GetGroupListGuidsType int `yaml:"get_g_list_guilds_type"` + GetGroupListDelay int `yaml:"get_g_list_delay"` + ForwardMsgLimit int `yaml:"forward_msg_limit"` + CustomBotName string `yaml:"custom_bot_name"` + TransFormApiIds bool `yaml:"transform_api_ids"` + AutoPutInteraction bool `yaml:"auto_put_interaction"` + PutInteractionDelay int `yaml:"put_interaction_delay"` + //onebot修改 + TwoWayEcho bool `yaml:"twoway_echo"` + Array bool `yaml:"array"` + NativeOb11 bool `yaml:"native_ob11"` + //url相关 + VisibleIp bool `yaml:"visible_ip"` + UrlToQrimage bool `yaml:"url_to_qrimage"` + QrSize int `yaml:"qr_size"` + TransferUrl bool `yaml:"transfer_url"` + //框架修改 + Title string `yaml:"title"` + FrpPort string `yaml:"frp_port"` + //MD相关 + CustomTemplateID string `yaml:"custom_template_id"` + KeyBoardID string `yaml:"keyboard_id"` + //发送行为修改 + LazyMessageId bool `yaml:"lazy_message_id"` + RamDomSeq bool `yaml:"ramdom_seq"` + BotForumTitle string `yaml:"bot_forum_title"` + AtoPCount int `yaml:"AMsgRetryAsPMsg_Count"` + SendDelay int `yaml:"send_delay"` + //错误临时修复类 + Fix11300 bool `yaml:"fix_11300"` + //内置指令 + BindPrefix string `yaml:"bind_prefix"` + MePrefix string `yaml:"me_prefix"` + UnlockPrefix string `yaml:"unlock_prefix"` + LinkPrefix string `yaml:"link_prefix"` + MusicPrefix string `yaml:"music_prefix"` + LinkBots []string `yaml:"link_bots"` + LinkText string `yaml:"link_text"` + LinkPic string `yaml:"link_pic"` + //HTTP API配置 + HttpAddress string `yaml:"http_address"` + AccessToken string `yaml:"http_access_token"` + HttpVersion int `yaml:"http_version"` + HttpTimeOut int `yaml:"http_timeout"` + PostUrl []string `yaml:"post_url"` + PostSecret []string `yaml:"post_secret"` + PostMaxRetries []int `yaml:"post_max_retries"` + PostRetriesInterval []int `yaml:"post_retries_interval"` + //腾讯云 + TencentBucketName string `yaml:"t_COS_BUCKETNAME"` + TencentBucketRegion string `yaml:"t_COS_REGION"` + TencentCosSecretid string `yaml:"t_COS_SECRETID"` + TencentSecretKey string `yaml:"t_COS_SECRETKEY"` + TencentAudit bool `yaml:"t_audit"` + //百度云 + BaiduBOSBucketName string `yaml:"b_BOS_BUCKETNAME"` + BaiduBCEAK string `yaml:"b_BCE_AK"` + BaiduBCESK string `yaml:"b_BCE_SK"` + BaiduAudit int `yaml:"b_audit"` + //阿里云 + AliyunEndpoint string `yaml:"a_OSS_EndPoint"` + AliyunAccessKeyId string `yaml:"a_OSS_AccessKeyId"` + AliyunAccessKeySecret string `yaml:"a_OSS_AccessKeySecret"` + AliyunBucketName string `yaml:"a_OSS_BucketName"` + AliyunAudit bool `yaml:"a_audit"` +} + +// CommentInfo 用于存储注释及其定位信息 +type CommentBlock struct { + Comments []string // 一个或多个连续的注释 + TargetKey string // 注释所指向的键(如果有) + Offset int // 注释与目标键之间的行数 } // LoadConfig 从文件中加载配置并初始化单例配置 @@ -204,6 +239,169 @@ func LoadConfig(path string) (*Config, error) { return instance, nil } +func CreateAndWriteConfigTemp() error { + // 读取config.yml + configFile, err := os.ReadFile("config.yml") + if err != nil { + return err + } + + // 获取当前日期 + currentDate := time.Now().Format("2006-1-2") + // 重命名原始config.yml文件 + err = os.Rename("config.yml", "config"+currentDate+".yml") + if err != nil { + return err + } + + var config Config + err = yaml.Unmarshal(configFile, &config) + if err != nil { + return err + } + + // 创建config_temp.yml文件 + tempFile, err := os.Create("config.yml") + if err != nil { + return err + } + defer tempFile.Close() + + // 使用yaml.Encoder写入,以保留注释 + encoder := yaml.NewEncoder(tempFile) + encoder.SetIndent(2) // 设置缩进 + err = encoder.Encode(config) + if err != nil { + return err + } + + // 处理注释并重命名文件 + err = addCommentsToConfigTemp(template.ConfigTemplate, "config.yml") + if err != nil { + return err + } + + return nil +} + +func parseTemplate(template string) ([]CommentBlock, map[string]string) { + var blocks []CommentBlock + lines := strings.Split(template, "\n") + + var currentBlock CommentBlock + var lastKey string + + directComments := make(map[string]string) + + for _, line := range lines { + trimmed := strings.TrimSpace(line) + if strings.HasPrefix(trimmed, "#") { + currentBlock.Comments = append(currentBlock.Comments, trimmed) // 收集注释行 + } else { + if containsKey(trimmed) { + key := strings.SplitN(trimmed, ":", 2)[0] + trimmedKey := strings.TrimSpace(key) + + if len(currentBlock.Comments) > 0 { + currentBlock.TargetKey = lastKey // 关联到上一个找到的键 + blocks = append(blocks, currentBlock) + currentBlock = CommentBlock{} // 重置为新的注释块 + } + + // 如果当前行包含注释,则单独处理 + if parts := strings.SplitN(trimmed, "#", 2); len(parts) > 1 { + directComments[trimmedKey] = "#" + parts[1] + } + lastKey = trimmedKey // 更新最后一个键 + } else if len(currentBlock.Comments) > 0 { + // 如果当前行不是注释行且存在挂起的注释,但并没有新的键出现,将其作为独立的注释块 + blocks = append(blocks, currentBlock) + currentBlock = CommentBlock{} // 重置为新的注释块 + } + } + } + + // 处理文件末尾的挂起注释块 + if len(currentBlock.Comments) > 0 { + blocks = append(blocks, currentBlock) + } + + return blocks, directComments +} + +func addCommentsToConfigTemp(template, tempFilePath string) error { + commentBlocks, directComments := parseTemplate(template) + //fmt.Printf("%v\n", directComments) + + // 读取并分割新生成的配置文件内容 + content, err := os.ReadFile(tempFilePath) + if err != nil { + return err + } + lines := strings.Split(string(content), "\n") + + // 处理并插入注释 + for _, block := range commentBlocks { + // 根据注释块的目标键,找到插入位置并插入注释 + for i, line := range lines { + if containsKey(line) { + key := strings.SplitN(line, ":", 2)[0] + if strings.TrimSpace(key) == block.TargetKey { + // 在目标键之前插入注释 + insertionPoint := i + block.Offset + if insertionPoint >= len(lines) { + lines = append(lines, block.Comments...) + } else { + lines = append(lines[:insertionPoint], append(block.Comments, lines[insertionPoint:]...)...) + } + break + } + } + } + } + + // 处理直接跟在键后面的注释 + // 接着处理直接跟在键后面的注释 + for i, line := range lines { + if containsKey(line) { + key := strings.SplitN(line, ":", 2)[0] + trimmedKey := strings.TrimSpace(key) + //fmt.Printf("%v\n", trimmedKey) + if comment, exists := directComments[trimmedKey]; exists { + // 如果这个键有直接的注释 + lines[i] = line + " " + comment + } + } + } + + // 重新组合lines为一个字符串,准备写回文件 + updatedContent := strings.Join(lines, "\n") + + // 写回更新后的内容到原配置文件 + err = os.WriteFile(tempFilePath, []byte(updatedContent), 0644) + if err != nil { + return err + } + + return nil +} + +// containsKey 检查给定的字符串行是否可能包含YAML键。 +// 它尝试排除注释行和冒号用于其他目的的行(例如,在URLs中)。 +func containsKey(line string) bool { + // 去除行首和行尾的空格 + trimmedLine := strings.TrimSpace(line) + + // 如果行是注释,直接返回false + if strings.HasPrefix(trimmedLine, "#") { + return false + } + + // 检查是否存在冒号,如果不存在,则直接返回false + colonIndex := strings.Index(trimmedLine, ":") + return colonIndex != -1 +} + // 确保配置完整性 func ensureConfigComplete(path string) error { // 读取配置文件到缓冲区 @@ -1998,3 +2196,15 @@ func GetServerTempQQguildPool() []string { } return nil // 返回nil,如果instance为nil } + +// 获取UploadPicV2Base64开关 +func GetUploadPicV2Base64() bool { + mu.Lock() + defer mu.Unlock() + + if instance == nil { + mylog.Println("Warning: instance is nil when trying to UploadPicV2 value.") + return false + } + return instance.Settings.UploadPicV2Base64 +} diff --git a/images/upload_api.go b/images/upload_api.go index f70bff01..8287f3fc 100644 --- a/images/upload_api.go +++ b/images/upload_api.go @@ -73,26 +73,27 @@ func UploadBase64ImageToServer(msgid string, base64Image string, groupID string, return downloadURL, 0, width, height, nil } - extraPicAuditingType := config.GetOssType() - - switch extraPicAuditingType { - case 0: - picURL, err = originalUploadBehavior(base64Image) - case 1: - picURL, err = oss.UploadAndAuditImage(base64Image) // 腾讯 - case 2: - picURL, err = oss.UploadAndAuditImageB(base64Image) // 百度 - case 3: - picURL, err = oss.UploadAndAuditImageA(base64Image) // 阿里 - default: - return "", 0, 0, 0, errors.New("invalid extraPicAuditingType") - } - if err != nil { - return "", 0, 0, 0, err + //v2接口是否使用base64 + if !config.GetUploadPicV2Base64() { + extraPicAuditingType := config.GetOssType() + switch extraPicAuditingType { + case 0: + picURL, err = originalUploadBehavior(base64Image) + case 1: + picURL, err = oss.UploadAndAuditImage(base64Image) // 腾讯 + case 2: + picURL, err = oss.UploadAndAuditImageB(base64Image) // 百度 + case 3: + picURL, err = oss.UploadAndAuditImageA(base64Image) // 阿里 + default: + return "", 0, 0, 0, errors.New("invalid extraPicAuditingType") + } + if err != nil { + return "", 0, 0, 0, err + } } if config.GetImgUpApiVtv2() && groupID != "" { - if msgid == "" { msgid = echo.GetLazyMessagesId(groupID) } @@ -109,13 +110,25 @@ func UploadBase64ImageToServer(msgid string, base64Image string, groupID string, // 用originalGroupID更新groupID groupID = originalGroupID } - richMediaMessage := &dto.RichMediaMessage{ - EventID: msgid, - FileType: 1, // 1代表图片 - URL: picURL, - Content: "", // 这个字段文档没有了 - SrvSendMsg: false, + var richMediaMessage *dto.RichMediaMessage + if !config.GetUploadPicV2Base64() { + richMediaMessage = &dto.RichMediaMessage{ + EventID: msgid, + FileType: 1, // 1代表图片 + URL: picURL, + Content: "", // 这个字段文档没有了 + SrvSendMsg: false, + } + } else { + richMediaMessage = &dto.RichMediaMessage{ + EventID: msgid, + FileType: 1, // 1代表图片 + FileData: base64Image, + Content: "", // 这个字段文档没有了 + SrvSendMsg: false, + } } + var fileInfo string //尝试群聊发图 fileInfo, err = uploadMedia(context.TODO(), groupID, richMediaMessage, apiv2) @@ -146,6 +159,7 @@ func UploadBase64ImageToServer(msgid string, base64Image string, groupID string, // 从Proto消息中读取值 realGroupID := mainMessage.GetA().GetB().GetInfo().GetDetail().GetGroupInfo().GetGroupNumber() downloadURL := mainMessage.GetA().GetImageData().GetImageInfo().GetUrl() + downloadURL = "https://multimedia.nt.qq.com.cn" + downloadURL width := mainMessage.GetA().GetImageData().GetWidth() height := mainMessage.GetA().GetImageData().GetHeight() diff --git a/main.go b/main.go index cf690003..6dd281e0 100644 --- a/main.go +++ b/main.go @@ -44,6 +44,7 @@ var p *Processor.Processors func main() { // 定义faststart命令行标志。默认为false。 fastStart := flag.Bool("faststart", false, "start without initialization if set") + tidy := flag.Bool("tidy", false, "backup and tidy your config.yml") // 解析命令行参数到定义的标志。 flag.Parse() @@ -52,6 +53,12 @@ func main() { if !*fastStart { sys.InitBase() // 如果不是faststart模式,则执行初始化 } + if *tidy { + //备份配置 并刷新 + config.CreateAndWriteConfigTemp() + log.Println("配置文件已更新为新版,当前配置文件已备份.如产生问题请到群196173384反馈开发者。") + return + } if _, err := os.Stat("config.yml"); os.IsNotExist(err) { var ip string var err error diff --git a/template/config_template.go b/template/config_template.go index 92fa87d6..76c9fc7f 100644 --- a/template/config_template.go +++ b/template/config_template.go @@ -6,11 +6,19 @@ settings: #反向ws设置 ws_address: ["ws://:"] # WebSocket服务的地址 支持多个["","",""] ws_token: ["","",""] #连接wss地址时服务器所需的token,按顺序一一对应,如果是ws地址,没有密钥,请留空. + reconnect_times : 100 #反向ws连接失败后的重试次数,希望一直重试,可设置9999 + heart_beat_interval : 10 #反向ws心跳间隔 单位秒 推荐5-10 + launch_reconnect_times : 1 #启动时尝试反向ws连接次数,建议先打开应用端再开启gensokyo,因为启动时连接会阻塞webui启动,默认只连接一次,可自行增大 + + #基础设置 app_id: 12345 # 你的应用ID uin : 0 # 你的机器人QQ号,点击机器人资料卡查看 token: "" # 你的应用令牌 client_secret: "" # 你的客户端密钥 + shard_count: 1 #分片数量 默认1 + shard_id: 0 #当前分片id 默认从0开始,详细请看 https://bot.q.qq.com/wiki/develop/api/gateway/reference.html + #事件订阅 text_intent: # 请根据公域 私域来选择intent,错误的intent将连接失败 - "ATMessageEventHandler" # 频道at信息 - "DirectMessageHandler" # 私域频道私信(dms) @@ -25,71 +33,63 @@ settings: # - "C2CMessageEventHandler" # 群私聊 仅频道机器人时候需要注释 # - "ThreadEventHandler" # 频道发帖事件 仅频道私域机器人可用 - + #转换类 global_channel_to_group: true # 是否将频道转换成群 默认true global_private_to_channel: false # 是否将私聊转换成频道 如果是群场景 会将私聊转为群(方便提审\测试) global_forum_to_channel: false # 是否将频道帖子信息转化为频道 子频道信息 如果开启global_channel_to_group会进一步转换为群信息 global_interaction_to_message : false # 是否将按钮和表态回调转化为消息 仅在设置了按钮回调中的message时有效 - bot_forum_title : "机器人帖子" # 机器人发帖子回复默认标题 - array: false # 连接trss云崽请开启array hash_id : false # 使用hash来进行idmaps转换,可以让user_id不是123开始的递增值 + idmap_pro : false #需开启hash_id配合,高级id转换增强,可以多个真实值bind到同一个虚拟值,对于每个用户,每个群\私聊\判断私聊\频道,都会产生新的虚拟值,但可以多次bind,bind到同一个数字.数据库负担会变大. - server_dir: "" # 提供图片上传服务的服务器(图床)需要带端口号. 如果需要发base64图,需为公网ip,且开放对应端口 - port: "15630" # idmaps和图床对外开放的端口号 + #Gensokyo互联类 + server_dir: "" # Lotus地址.不带http头的域名或ip,提供图片上传服务的服务器(图床)需要带端口号. 如果需要发base64图,需为公网ip,且开放对应端口 + port: "15630" # Lotus端口.idmaps和图床对外开放的端口号,若要连接到另一个gensokyo,也是链接端口 backup_port : "5200" # 当totus为ture时,port值不再是本地webui的端口,使用lotus_Port来访问webui - - lotus: false # lotus特性默认为false,当为true时,将会连接到另一个lotus为false的gensokyo。 - # 使用它提供的图床和idmaps服务(场景:同一个机器人在不同服务器运行,或内网需要发送base64图)。 - # 如果需要发送base64图片,需要设置正确的公网server_dir和开放对应的port - lotus_password : "" # lotus鉴权 设置后,从gsk需要保持相同密码来访问主gsk + lotus: false # lotus特性默认为false,当为true时,将会连接到另一个lotus为false的gensokyo。使用它提供的图床和idmaps服务(场景:同一个机器人在不同服务器运行,或内网需要发送base64图)。如果需要发送base64图片,需要设置正确的公网server_dir和开放对应的port, lotus鉴权 设置后,从gsk需要保持相同密码来访问主gsk + lotus_password : "" + lotus_without_idmaps: false #lotus只通过url,图片上传,语音,不通过id转换,在本地当前gsk维护idmaps转换. #增强配置项 - - image_sizelimit : 0 #代表kb 腾讯api要求图片1500ms完成传输 如果图片发不出 请提升上行或设置此值 默认为0 不压缩 - image_limit : 100 #每分钟上传的最大图片数量,可自行增加 master_id : ["1","2"] #群场景尚未开放获取管理员和列表能力,手动从日志中获取需要设置为管理,的user_id并填入(适用插件有权限判断场景) record_sampleRate : 24000 #语音文件的采样率 最高48000 默认24000 单位Khz record_bitRate : 24000 #语音文件的比特率 默认25000 代表 25 kbps 最高无限 请根据带宽 您发送的实际码率调整 card_nick : "" #默认为空,连接mirai-overflow时,请设置为非空,这里是机器人对用户称谓,为空为插件获取,mirai不支持 auto_bind : true #测试功能,后期会移除 - AMsgRetryAsPMsg_Count : 30 #当主动信息发送失败时,自动转为后续的被动信息发送,需要开启Lazy message id,该配置项为所有群、频道的主动转被动消息队列最大长度,建议30-100,无上限 - reconnect_times : 100 #反向ws连接失败后的重试次数,希望一直重试,可设置9999 - heart_beat_interval : 10 #反向ws心跳间隔 单位秒 推荐5-10 - launch_reconnect_times : 1 #启动时尝试反向ws连接次数,建议先打开应用端再开启gensokyo,因为启动时连接会阻塞webui启动,默认只连接一次,可自行增大 - native_ob11 : false #如果你的机器人收到事件报错,请开启此选项增加兼容性 - ramdom_seq : false #当多开gensokyo时,如果遇到群信息只能发出一条,请开启每个gsk的此项.(建议使用一个gsk连接多个应用) - url_to_qrimage : false #将信息中的url转换为二维码单独作为图片发出,需要同时设置 #SSL配置类 机器人发送URL设置 的 transfer_url 为 true visible_ip也需要为true - qr_size : 200 #二维码尺寸,单位像素 - guild_url_image_to_base64 : false #解决频道发不了某些url图片,报错40003问题 + + #发图相关 oss_type : 0 #请完善后方具体配置 完成#腾讯云配置...,0代表配置server dir port服务器自行上传(省钱),1,腾讯cos存储桶 2,百度oss存储桶 3,阿里oss存储桶 - self_introduce : ["",""] #自我介绍,可设置多个随机发送,当不为空时,机器人被邀入群会发送自定义自我介绍 需手动添加新textintent - "GroupAddRobotEventHandler" - "GroupDelRobotEventHandler" + image_sizelimit : 0 #代表kb 腾讯api要求图片1500ms完成传输 如果图片发不出 请提升上行或设置此值 默认为0 不压缩 + image_limit : 100 #每分钟上传的最大图片数量,可自行增加 + guild_url_image_to_base64 : false #解决频道发不了某些url图片,报错40003问题 + url_pic_transfer : false #把图片url(任意来源图链)变成你备案的白名单url 需要较高上下行+ssl+自备案域名+设置白名单域名(暂时不需要) + img_up_api_ntv2: false #gsk内建图片上传api 开启后全局有效 是否将图片转换为ntqq图床url(md发图用,自行调用)文档:https://www.yuque.com/km57bt/hlhnxg/ig2nk88fccykn6dm + uploadpicv2_b64: true #uploadpicv2接口使用base64直接上传 https://www.yuque.com/km57bt/hlhnxg/ig2nk88fccykn6dm + global_server_temp_qqguild : false #需设置server_temp_qqguild,公域私域均可用,以频道为底层发图,速度快,该接口为进阶接口,使用有一定难度. + server_temp_qqguild : "0" #在v3图片接口采用固定的子频道号,可以是帖子子频道 https://www.yuque.com/km57bt/hlhnxg/uqmnsno3vx1ytp2q + server_temp_qqguild_pool : [] #填写v3发图接口的endpoint http://127.0.0.1:12345/uploadpicv3 当填写多个时采用循环方式负载均衡,注,不包括自身,如需要自身也要填写 #正向ws设置 ws_server_path : "ws" #默认监听0.0.0.0:port/ws_server_path 若有安全需求,可不放通port到公网,或设置ws_server_token 若想监听/ 可改为"",若想监听到不带/地址请写nil enable_ws_server: true #是否启用正向ws服务器 监听server_dir:port/ws_server_path ws_server_token : "12345" #正向ws的token 不启动正向ws可忽略 可为空 - #SSL配置类 机器人发送URL设置 - + #SSL配置类 和 白名单域名自动验证 identify_file : true #自动生成域名校验文件,在q.qq.com配置信息URL,在server_dir填入自己已备案域名,正确解析到机器人所在服务器ip地址,机器人即可发送链接 identify_appids : [] #默认不需要设置,完成SSL配置类+server_dir设置为域名+完成备案+ssl全套设置后,若有多个机器人需要过域名校验(自己名下)可设置,格式为,整数appid,组成的数组 crt : "" #证书路径 从你的域名服务商或云服务商申请签发SSL证书(qq要求SSL) key : "" #密钥路径 Apache(crt文件、key文件)示例: "C:\\123.key" \需要双写成\\ - transfer_url : true #默认开启,关闭后自理url发送,配置server_dir为你的域名,配置crt和key后,将域名/url和/image在q.qq.com后台通过校验,自动使用302跳转处理机器人发出的所有域名. - + #日志类 - developer_log : false #开启开发者日志 默认关闭 log_level : 1 # 0=debug 1=info 2=warning 3=error 默认1 save_logs : false #自动储存日志 #webui设置 - + disable_webui: false #禁用webui server_user_name : "useradmin" #默认网页面板用户名 server_user_password : "admin" #默认网页面板密码 - #指令过滤类 - + #指令魔法类 remove_prefix : false #是否忽略公域机器人指令前第一个/ remove_at : false #是否忽略公域机器人指令前第一个[CQ:aq,qq=机器人] 场景(公域机器人,但插件未适配at开头) remove_bot_at_group : true #因为群聊机器人不支持发at,开启本开关会自动隐藏群机器人发出的at(不影响频道场景) @@ -120,48 +120,59 @@ settings: No_White_Response : "" #开发增强类 - develop_access_token_dir : "" #开发者测试环境access_token自定义获取地址 默认留空 请留空忽略 develop_bot_id : "1234" #开发者环境需自行获取botid 填入 用户请不要设置这两行...开发者调试用 sandbox_mode : false #默认false 如果你只希望沙箱频道使用,请改为true dev_message_id : false #在沙盒和测试环境使用无限制msg_id 仅沙盒有效,正式环境请关闭,内测结束后,tx侧未来会移除 send_error : true #将报错用文本发出,避免机器人被审核报无响应 - url_pic_transfer : false #把图片url(任意来源图链)变成你备案的白名单url 需要较高上下行+ssl+自备案域名+设置白名单域名(暂时不需要) - idmap_pro : false #需开启hash_id配合,高级id转换增强,可以多个真实值bind到同一个虚拟值,对于每个用户,每个群\私聊\判断私聊\频道,都会产生新的虚拟值,但可以多次bind,bind到同一个数字.数据库负担会变大. - send_delay : 300 #单位 毫秒 默认300ms 可以视情况减少到100或者50 - disable_webui: false #禁用webui - shard_count: 1 #分片数量 默认1 - shard_id: 0 #当前分片id 默认从0开始,详细请看 https://bot.q.qq.com/wiki/develop/api/gateway/reference.html - auto_put_interaction : false #自动回应按钮回调的/interactions/{interaction_id} 注本api需要邮件申请,详细方法参考群公告:196173384 - put_interaction_delay : 0 #单位毫秒 表示回应已收到回调类型的按钮的毫秒数 会按用户进行区分 非全局delay - img_up_api_ntv2: false #gsk内建图片上传api 是否将图片转换为ntqq图床url(md发图用,自行调用)文档:https://www.yuque.com/km57bt/hlhnxg/ig2nk88fccykn6dm - fix_11300: false #修复11300报错,需要在develop_bot_id填入自己机器人的appid. 11300原因暂时未知,临时修复方案. - lotus_without_idmaps: false #lotus只通过url,图片上传,语音,不通过id转换,在本地当前gsk维护idmaps转换. + #增长营销类(推荐gensokyo-broadcast项目) + self_introduce : ["",""] #自我介绍,可设置多个随机发送,当不为空时,机器人被邀入群会发送自定义自我介绍 需手动添加新textintent - "GroupAddRobotEventHandler" - "GroupDelRobotEventHandler" + + + #API修改 get_g_list_all_guilds : false #在获取群列表api时,轮询获取全部的频道列表(api一次只能获取100个),建议仅在广播公告通知等特别场景时开启. get_g_list_delay : 500 #轮询时的延迟时间,毫秒数. get_g_list_guilds_type : 0 #0=全部返回,1=获取第1个子频道.以此类推.可以缩减返回值的大小. get_g_list_guilds : "10" #在获取群列表api时,一次返回的频道数量.这里是string,不要去掉引号.最大100(5分钟内连续请求=翻页),获取全部请开启get_g_list_return_guilds. get_g_list_return_guilds : true #获取群列表时是否返回频道列表. + forward_msg_limit : 3 #发送折叠转发信息时的最大限制条数 若要发转发信息 请设置lazy_message_id为true + custom_bot_name : "Gensokyo全域机器人" #自定义api返回的机器人名字,会在api调用中返回,默认Gensokyo全域机器人 + transform_api_ids : true #对get_group_menmber_list\get_group_member_info\get_group_list生效,是否在其中返回转换后的值(默认转换,不转换请自行处理插件逻辑,比如调用gsk的http api转换) + auto_put_interaction : false #自动回应按钮回调的/interactions/{interaction_id} 注本api需要邮件申请,详细方法参考群公告:196173384 + put_interaction_delay : 0 #单位毫秒 表示回应已收到回调类型的按钮的毫秒数 会按用户进行区分 非全局delay - global_server_temp_qqguild : false #需设置server_temp_qqguild,公域私域均可用,以频道为底层发图,速度快,该接口为进阶接口,使用有一定难度. - server_temp_qqguild : "0" #在v3图片接口采用固定的子频道号,可以是帖子子频道 https://www.yuque.com/km57bt/hlhnxg/uqmnsno3vx1ytp2q - server_temp_qqguild_pool : [] #填写v3发图接口的endpoint http://127.0.0.1:12345/uploadpicv3 当填写多个时采用循环方式负载均衡,注,不包括自身,如需要自身也要填写 + #Onebot修改 + twoway_echo : false #是否采用双向echo,根据机器人选择,獭獭\早苗 true 红色问答\椛椛 或者其他 请使用 false + array: false # 连接trss云崽请开启array + native_ob11 : false #如果你的机器人收到事件报错,请开启此选项增加兼容性 + #URL相关 + visible_ip : false #转换url时,如果server_dir是ip true将以ip形式发出url 默认隐藏url 将server_dir配置为自己域名可以转换url + url_to_qrimage : false #将信息中的url转换为二维码单独作为图片发出,需要同时设置 #SSL配置类 机器人发送URL设置 的 transfer_url 为 true visible_ip也需要为true + qr_size : 200 #二维码尺寸,单位像素 + transfer_url : true #默认开启,关闭后自理url发送,配置server_dir为你的域名,配置crt和key后,将域名/url和/image在q.qq.com后台通过校验,自动使用302跳转处理机器人发出的所有域名. + + #框架修改 title : "Gensokyo © 2023 - Hoshinonyaruko" #程序的标题 如果多个机器人 可根据标题区分 - custom_bot_name : "Gensokyo全域机器人" #自定义机器人名字,会在api调用中返回,默认Gensokyo全域机器人 + frp_port : "0" #不使用请保持为0,frp的端口,frp有内外端口,请在frp软件设置gensokyo的port,并将frp显示的对外端口填入这里 - twoway_echo : false #是否采用双向echo,根据机器人选择,獭獭\早苗 true 红色问答\椛椛 或者其他 请使用 false + #MD相关 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 - forward_msg_limit : 3 #发送折叠转发信息时的最大限制条数 若要发转发信息 请设置lazy_message_id为true - transform_api_ids : true #对get_group_menmber_list\get_group_member_info\get_group_list生效,是否在其中返回转换后的值(默认转换,不转换请自行处理插件逻辑,比如调用gsk的http api转换) - #bind指令类 + #发送行为修改 + lazy_message_id : false #false=message_id 条条准确对应 true=message_id 按时间范围随机对应(适合主动推送bot)前提,有足够多的活跃信息刷新id池 + ramdom_seq : false #当多开gensokyo时,如果遇到群信息只能发出一条,请开启每个gsk的此项.(建议使用一个gsk连接多个应用) + bot_forum_title : "机器人帖子" # 机器人发帖子回复默认标题 + AMsgRetryAsPMsg_Count : 30 #当主动信息发送失败时,自动转为后续的被动信息发送,需要开启Lazy message id,该配置项为所有群、频道的主动转被动消息队列最大长度,建议30-100,无上限 + send_delay : 300 #单位 毫秒 默认300ms 可以视情况减少到100或者50 + + #错误临时修复类 + fix_11300: false #修复11300报错,需要在develop_bot_id填入自己机器人的appid. 11300原因暂时未知,临时修复方案. + + #内置指令类 bind_prefix : "/bind" #需设置 #增强配置项 master_id 可触发 me_prefix : "/me" #需设置 #增强配置项 master_id 可触发 unlock_prefix : "/unlock" #频道私信卡住了? gsk可以帮到你 在任意子频道发送unlock 你会收到来自机器人的频道私信 @@ -171,18 +182,13 @@ settings: link_text : "" #友情链接文本 不可为空! link_pic : "" #友情链接图片 可为空 需url图片 可带端口 不填可能会有显示错误 - #穿透\cos\oss类配置(可选!) - frp_port : "0" #不使用请保持为0,frp的端口,frp有内外端口,请在frp软件设置gensokyo的port,并将frp显示的对外端口填入这里 - - #HTTP API配置 - - #正向http + #HTTP API配置-正向http http_address: "" #http监听地址 与websocket独立 示例:0.0.0.0:5700 为空代表不开启 http_access_token: "" #http访问令牌 http_version : 11 #暂时只支持11 http_timeout: 5 #反向 HTTP 超时时间, 单位秒,<5 时将被忽略 - #反向http + #HTTP API配置-反向http post_url: [""] #反向HTTP POST地址列表 为空代表不开启 示例:http://192.168.0.100:5789 post_secret: [""] #密钥 post_max_retries: [3] #最大重试,0 时禁用 From 8a8304532710c10bc77ca25d766da9fa5ce82289 Mon Sep 17 00:00:00 2001 From: cosmo Date: Sun, 24 Mar 2024 18:24:17 +0800 Subject: [PATCH 15/33] beta350 --- config/config.go | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/config/config.go b/config/config.go index a5a6bf91..c33f3a3b 100644 --- a/config/config.go +++ b/config/config.go @@ -34,7 +34,7 @@ type VisualPrefixConfig struct { type Settings struct { //反向ws设置 WsAddress []string `yaml:"ws_address"` - WsToken []string `yaml:"ws_token,omitempty"` + WsToken []string `yaml:"ws_token"` ReconnecTimes int `yaml:"reconnect_times"` HeartBeatInterval int `yaml:"heart_beat_interval"` LaunchReconectTimes int `yaml:"launch_reconnect_times"` @@ -62,7 +62,7 @@ type Settings struct { LotusPassword string `yaml:"lotus_password"` LotusWithoutIdmaps bool `yaml:"lotus_without_idmaps"` //增强配置 - MasterID []string `yaml:"master_id,omitempty"` + MasterID []string `yaml:"master_id"` RecordSampleRate int `yaml:"record_sampleRate"` RecordBitRate int `yaml:"record_bitRate"` CardAndNick string `yaml:"card_nick"` @@ -80,8 +80,8 @@ type Settings struct { ServerTempQQguildPool []string `yaml:"server_temp_qqguild_pool"` //正向ws设置 WsServerPath string `yaml:"ws_server_path"` - EnableWsServer bool `yaml:"enable_ws_server,omitempty"` - WsServerToken string `yaml:"ws_server_token,omitempty"` + EnableWsServer bool `yaml:"enable_ws_server"` + WsServerToken string `yaml:"ws_server_token"` //ssl和链接转换类 IdentifyFile bool `yaml:"identify_file"` IdentifyAppids []int64 `yaml:"identify_appids"` From 0eb64d67cdd26d00b48bdb5d21be8209291e0f93 Mon Sep 17 00:00:00 2001 From: cosmo Date: Sun, 24 Mar 2024 19:46:09 +0800 Subject: [PATCH 16/33] beta351 --- images/upload_api.go | 21 ++++++++++++++++++--- 1 file changed, 18 insertions(+), 3 deletions(-) diff --git a/images/upload_api.go b/images/upload_api.go index 8287f3fc..35608fa3 100644 --- a/images/upload_api.go +++ b/images/upload_api.go @@ -12,6 +12,7 @@ import ( "net/http" "net/url" "regexp" + "strconv" "strings" "sync" @@ -94,10 +95,12 @@ func UploadBase64ImageToServer(msgid string, base64Image string, groupID string, } if config.GetImgUpApiVtv2() && groupID != "" { - if msgid == "" { - msgid = echo.GetLazyMessagesId(groupID) - } + if isNumeric(groupID) { + //用转换前的群号获取msgid + if msgid == "" { + msgid = echo.GetLazyMessagesId(groupID) + } // 检查groupID是否为纯数字构成 RetrieveRowByIDv2是通用逻辑,也可以将userid还原为32位数originaluserid // 但没有私信权限,故没有测试 originalGroupID, err := idmap.RetrieveRowByIDv2(groupID) @@ -109,6 +112,18 @@ func UploadBase64ImageToServer(msgid string, base64Image string, groupID string, // 用originalGroupID更新groupID groupID = originalGroupID + } else { + // 映射str的GroupID到int + GroupID64, err := idmap.StoreIDv2(groupID) + if err != nil { + log.Printf("failed to convert ChannelID to int: %v", err) + return picURL, 0, 0, 0, nil + } + groupIDTemp := strconv.FormatInt(GroupID64, 10) + //用数字的群号获取msgid + if msgid == "" { + msgid = echo.GetLazyMessagesId(groupIDTemp) + } } var richMediaMessage *dto.RichMediaMessage if !config.GetUploadPicV2Base64() { From 123be98de0f2fd1a02730b0f2e4df69c2968c602 Mon Sep 17 00:00:00 2001 From: cosmo Date: Sun, 24 Mar 2024 20:39:57 +0800 Subject: [PATCH 17/33] beta352 --- images/upload_api.go | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/images/upload_api.go b/images/upload_api.go index 35608fa3..6c5a983e 100644 --- a/images/upload_api.go +++ b/images/upload_api.go @@ -174,7 +174,11 @@ func UploadBase64ImageToServer(msgid string, base64Image string, groupID string, // 从Proto消息中读取值 realGroupID := mainMessage.GetA().GetB().GetInfo().GetDetail().GetGroupInfo().GetGroupNumber() downloadURL := mainMessage.GetA().GetImageData().GetImageInfo().GetUrl() - downloadURL = "https://multimedia.nt.qq.com.cn" + downloadURL + //https的地址不能放到md里 + //downloadURL = "https://multimedia.nt.qq.com.cn" + downloadURL + // 将 downloadURL 中的所有下划线 "_" 替换为 "%5f" + downloadURL = strings.Replace(downloadURL, "_", "%5f", -1) + downloadURL = "http://multimedia.nt.qq.com" + downloadURL width := mainMessage.GetA().GetImageData().GetWidth() height := mainMessage.GetA().GetImageData().GetHeight() From 33611e15ac6486ae42255d3969a144fb728a9265 Mon Sep 17 00:00:00 2001 From: cosmo Date: Mon, 25 Mar 2024 13:41:10 +0800 Subject: [PATCH 18/33] beta353 --- config/config.go | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/config/config.go b/config/config.go index c33f3a3b..d10feda5 100644 --- a/config/config.go +++ b/config/config.go @@ -347,11 +347,19 @@ func addCommentsToConfigTemp(template, tempFilePath string) error { if containsKey(line) { key := strings.SplitN(line, ":", 2)[0] if strings.TrimSpace(key) == block.TargetKey { - // 在目标键之前插入注释 - insertionPoint := i + block.Offset + // 计算基本插入点:在目标键之后 + insertionPoint := i + block.Offset + 1 + + // 向下移动插入点直到找到键行或到达文件末尾 + for insertionPoint < len(lines) && !containsKey(lines[insertionPoint]) { + insertionPoint++ + } + + // 在计算出的插入点插入注释 if insertionPoint >= len(lines) { - lines = append(lines, block.Comments...) + lines = append(lines, block.Comments...) // 如果到达文件末尾,直接追加注释 } else { + // 插入注释到计算出的位置 lines = append(lines[:insertionPoint], append(block.Comments, lines[insertionPoint:]...)...) } break From 3d9d2b6dcbc5c9a05c11985e92c7b64fe2e5d781 Mon Sep 17 00:00:00 2001 From: cosmo Date: Mon, 25 Mar 2024 20:13:28 +0800 Subject: [PATCH 19/33] beta354 --- Processor/ProcessGroupMessage.go | 2 +- Processor/Processor.go | 22 ++++++++++++---------- handlers/message_parser.go | 21 ++++++++++++++++----- handlers/send_group_msg.go | 11 +++++++++++ idmap/service.go | 25 ++++++++++++------------- 5 files changed, 52 insertions(+), 29 deletions(-) diff --git a/Processor/ProcessGroupMessage.go b/Processor/ProcessGroupMessage.go index eee3eedd..4cacbfab 100644 --- a/Processor/ProcessGroupMessage.go +++ b/Processor/ProcessGroupMessage.go @@ -51,7 +51,7 @@ func (p *Processors) ProcessGroupMessage(data *dto.WSGroupATMessageData) error { // 映射str的GroupID到int GroupID64, err = idmap.StoreIDv2(data.GroupID) if err != nil { - mylog.Errorf("failed to convert ChannelID to int: %v", err) + mylog.Errorf("failed to convert GroupID64 to int: %v", err) return nil } // 映射str的userid到int diff --git a/Processor/Processor.go b/Processor/Processor.go index 44f24d0d..297cffef 100644 --- a/Processor/Processor.go +++ b/Processor/Processor.go @@ -344,22 +344,24 @@ func (p *Processors) HandleFrameworkCommand(messageText string, data interface{} // 获取MasterID数组 masterIDs := config.GetMasterID() - // 根据realid获取new(用户id) - now, new, err = idmap.RetrieveVirtualValuev2(realid) - if err != nil { - mylog.Printf("根据realid获取new(用户id) 错误:%v", err) - } - // 根据realid获取new(群id) - nowgroup, newgroup, err = idmap.RetrieveVirtualValuev2(realid2) - if err != nil { - mylog.Printf("根据realid获取new(群id)错误:%v", err) - } + // idmaps-pro获取群和用户id if config.GetIdmapPro() { newpro1, newpro2, err = idmap.RetrieveVirtualValuev2Pro(realid2, realid) if err != nil { mylog.Printf("idmaps-pro获取群和用户id 错误:%v", err) } + } else { + // 根据realid获取new(用户id) + now, new, err = idmap.RetrieveVirtualValuev2(realid) + if err != nil { + mylog.Printf("根据realid获取new(用户id) 错误:%v", err) + } + // 根据realid获取new(群id) + nowgroup, newgroup, err = idmap.RetrieveVirtualValuev2(realid2) + if err != nil { + mylog.Printf("根据realid获取new(群id)错误:%v", err) + } } // 检查真实值或虚拟值是否在数组中 var realValueIncluded, virtualValueIncluded bool diff --git a/handlers/message_parser.go b/handlers/message_parser.go index 5b7d2e64..6d106509 100644 --- a/handlers/message_parser.go +++ b/handlers/message_parser.go @@ -67,12 +67,23 @@ func SendResponse(client callapi.Client, err error, message *callapi.ActionMessa // Default ID handling response.Data.MessageID = 123 } - // 映射str的GroupID到int - GroupID64, errr := idmap.StoreIDv2(message.Params.GroupID.(string)) - if errr != nil { - mylog.Errorf("failed to convert ChannelID to int: %v", err) - return "", nil + + var errr error + var GroupID64 int64 + if config.GetIdmapPro() { + //将真实id转为int userid64 + GroupID64, _, errr = idmap.StoreIDv2Pro(message.Params.GroupID.(string), message.Params.UserID.(string)) + if errr != nil { + mylog.Fatalf("Error storing ID: %v", err) + } + } else { + // 映射str的GroupID到int + GroupID64, err = idmap.StoreIDv2(message.Params.GroupID.(string)) + if err != nil { + mylog.Errorf("failed to convert GroupID64 to int: %v", err) + } } + response.GroupID = GroupID64 response.Echo = message.Echo if err != nil { diff --git a/handlers/send_group_msg.go b/handlers/send_group_msg.go index 2cfb59e9..b7207fdc 100644 --- a/handlers/send_group_msg.go +++ b/handlers/send_group_msg.go @@ -1314,6 +1314,17 @@ func auto_md(message callapi.ActionMessage, messageText string, richMediaMessage Type: 2, // 所有人可操作 } } + case strings.HasPrefix(whiteLabel, "^"): + // 分割whiteLabel来获取显示内容和URL + parts := strings.SplitN(whiteLabel[1:], " ", 2) // [1:] 用于去除白名单标签开头的'^' + if len(parts) == 2 { + whiteLabel = parts[0] // 显示内容 + actiondata = parts[1] // 发送给服务端内容 + actiontype = 1 // 回调类型 + permission = &keyboard.Permission{ + Type: 2, // 所有人可操作 + } + } default: actiontype = 2 //帮用户输入指令 用户自己回车发送 actiondata = dataLabel //从虚拟前缀的二级指令组合md按钮 diff --git a/idmap/service.go b/idmap/service.go index 844486d0..d14e5f71 100644 --- a/idmap/service.go +++ b/idmap/service.go @@ -140,22 +140,21 @@ func StoreID(id string) (int64, error) { } else { // 生成新的行号 var err error - newRow, err = GenerateRowID(id, 9) - if err != nil { - return err - } - // 检查新生成的行号是否重复 - rowKey := fmt.Sprintf("row-%d", newRow) - if b.Get([]byte(rowKey)) != nil { - // 如果行号重复,使用10位数字生成行号 - newRow, err = GenerateRowID(id, 10) + maxDigits := 18 // int64的位数上限-1 + for digits := 9; digits <= maxDigits; digits++ { + newRow, err = GenerateRowID(id, digits) if err != nil { return err } - rowKey = fmt.Sprintf("row-%d", newRow) - // 再次检查重复性,如果还是重复,则返回错误 - if b.Get([]byte(rowKey)) != nil { - return fmt.Errorf("unable to find a unique row ID") + // 检查新生成的行号是否重复 + rowKey := fmt.Sprintf("row-%d", newRow) + if b.Get([]byte(rowKey)) == nil { + // 找到了一个唯一的行号,可以跳出循环 + break + } + // 如果到达了最大尝试次数还没有找到唯一的行号,则返回错误 + if digits == maxDigits { + return fmt.Errorf("unable to find a unique row ID after %d attempts", maxDigits-8) } } } From b3a8b7ff1c25cbd8d3c61401660e51ece3a0fc98 Mon Sep 17 00:00:00 2001 From: cosmo Date: Mon, 25 Mar 2024 20:23:20 +0800 Subject: [PATCH 20/33] beta355 --- handlers/message_parser.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/handlers/message_parser.go b/handlers/message_parser.go index 6d106509..20884de2 100644 --- a/handlers/message_parser.go +++ b/handlers/message_parser.go @@ -78,12 +78,12 @@ func SendResponse(client callapi.Client, err error, message *callapi.ActionMessa } } else { // 映射str的GroupID到int - GroupID64, err = idmap.StoreIDv2(message.Params.GroupID.(string)) - if err != nil { + GroupID64, errr = idmap.StoreIDv2(message.Params.GroupID.(string)) + if errr != nil { mylog.Errorf("failed to convert GroupID64 to int: %v", err) } } - + //mylog.Printf("convert GroupID64 to int: %v", GroupID64) 测试 response.GroupID = GroupID64 response.Echo = message.Echo if err != nil { From 0adfd26fc41b5dccf67bc892bd0db0b14c5dd90f Mon Sep 17 00:00:00 2001 From: cosmo Date: Sun, 31 Mar 2024 18:03:56 +0800 Subject: [PATCH 21/33] beta356 --- botgo/dto/message_create.go | 67 +++++++++ botgo/openapi/iface.go | 2 + botgo/openapi/v1/message.go | 21 +++ botgo/openapi/v2/message.go | 21 +++ handlers/send_private_msg_sse.go | 229 +++++++++++++++++++++++++++++++ httpapi/httpapi.go | 53 +++++++ 6 files changed, 393 insertions(+) create mode 100644 handlers/send_private_msg_sse.go diff --git a/botgo/dto/message_create.go b/botgo/dto/message_create.go index 0107c976..554fabea 100644 --- a/botgo/dto/message_create.go +++ b/botgo/dto/message_create.go @@ -117,3 +117,70 @@ type SettingGuide struct { // 频道ID, 当通过私信发送设置引导消息时,需要指定guild_id GuildID string `json:"guild_id"` } + +// 仅供测试 + +type MessageSSE struct { + MsgType int `json:"msg_type,omitempty"` + Markdown *MarkdownSSE `json:"markdown,omitempty"` + MsgID string `json:"msg_id,omitempty"` + MsgSeq int `json:"msg_seq,omitempty"` + Stream *StreamSSE `json:"stream,omitempty"` + PromptKeyboard *KeyboardSSE `json:"prompt_keyboard,omitempty"` + ActionButton *ActionButtonSSE `json:"action_button,omitempty"` +} + +// GetEventID 事件ID +func (msg MessageSSE) GetEventID() string { + return "" +} + +// GetSendType 消息类型 +func (msg MessageSSE) GetSendType() SendType { + return 1 +} + +type MarkdownSSE struct { + Content string `json:"content"` +} + +type StreamSSE struct { + State int `json:"state"` + Index int `json:"index"` + ID string `json:"id,omitempty"` +} + +type KeyboardSSE struct { + KeyboardContentSSE `json:"keyboard"` +} + +type KeyboardContentSSE struct { + Content ContentSSE `json:"content"` +} + +type ContentSSE struct { + Rows []RowSSE `json:"rows"` +} + +type RowSSE struct { + Buttons []ButtonSSE `json:"buttons"` +} + +type ButtonSSE struct { + RenderData RenderDataSSE `json:"render_data"` + Action ActionSSE `json:"action"` +} + +type RenderDataSSE struct { + Label string `json:"label"` + Style int `json:"style"` +} + +type ActionSSE struct { + Type int `json:"type"` +} + +type ActionButtonSSE struct { + TemplateID int `json:"template_id"` + CallbackData string `json:"callback_data"` +} diff --git a/botgo/openapi/iface.go b/botgo/openapi/iface.go index 58bd24ec..7e843809 100644 --- a/botgo/openapi/iface.go +++ b/botgo/openapi/iface.go @@ -83,6 +83,8 @@ type MessageAPI interface { PostGroupMessage(ctx context.Context, groupID string, msg dto.APIMessage) (*dto.GroupMessageResponse, error) // PostC2CMessage 发送C2C消息 PostC2CMessage(ctx context.Context, userID string, msg dto.APIMessage) (*dto.C2CMessageResponse, error) + // PostC2CMessage 发送C2CSSE消息 + PostC2CMessageSSE(ctx context.Context, userID string, msg dto.APIMessage) (*dto.C2CMessageResponse, error) } // GuildAPI guild 相关接口 diff --git a/botgo/openapi/v1/message.go b/botgo/openapi/v1/message.go index 4285a823..6f77ab1a 100644 --- a/botgo/openapi/v1/message.go +++ b/botgo/openapi/v1/message.go @@ -300,3 +300,24 @@ func (o *openAPI) PostC2CMessage(ctx context.Context, userID string, msg dto.API return result, nil } + +// PostC2CMessage 回复C2CSSE消息 +func (o *openAPI) PostC2CMessageSSE(ctx context.Context, userID string, msg dto.APIMessage) (*dto.C2CMessageResponse, error) { + var resp *resty.Response + var err error + + resp, err = o.request(ctx). + SetResult(dto.Message{}). // 设置为消息类型 + SetPathParam("user_id", userID). + SetBody(msg). + Post(o.getURL("/v2/users/{user_id}/messages")) + + if err != nil { + return nil, err + } + + result := &dto.C2CMessageResponse{} + result.Message = resp.Result().(*dto.Message) + + return result, nil +} diff --git a/botgo/openapi/v2/message.go b/botgo/openapi/v2/message.go index d3e23707..7a29802c 100644 --- a/botgo/openapi/v2/message.go +++ b/botgo/openapi/v2/message.go @@ -315,3 +315,24 @@ func (o *openAPIv2) PostC2CMessage(ctx context.Context, userID string, msg dto.A return result, nil } + +// PostC2CMessage 回复C2CSSE消息 +func (o *openAPIv2) PostC2CMessageSSE(ctx context.Context, userID string, msg dto.APIMessage) (*dto.C2CMessageResponse, error) { + var resp *resty.Response + var err error + + resp, err = o.request(ctx). + SetResult(dto.Message{}). // 设置为消息类型 + SetPathParam("user_id", userID). + SetBody(msg). + Post(o.getURL("/v2/users/{user_id}/messages")) + + if err != nil { + return nil, err + } + + result := &dto.C2CMessageResponse{} + result.Message = resp.Result().(*dto.Message) + + return result, nil +} diff --git a/handlers/send_private_msg_sse.go b/handlers/send_private_msg_sse.go new file mode 100644 index 00000000..602ba965 --- /dev/null +++ b/handlers/send_private_msg_sse.go @@ -0,0 +1,229 @@ +package handlers + +import ( + "context" + "encoding/json" + "fmt" + + "github.com/hoshinonyaruko/gensokyo/callapi" + "github.com/hoshinonyaruko/gensokyo/config" + "github.com/hoshinonyaruko/gensokyo/echo" + "github.com/hoshinonyaruko/gensokyo/idmap" + "github.com/hoshinonyaruko/gensokyo/mylog" + "github.com/tencent-connect/botgo/dto" + "github.com/tencent-connect/botgo/openapi" +) + +var msgIDToIndex = make(map[string]int) +var msgIDToRelatedID = make(map[string]string) + +func init() { + callapi.RegisterHandler("send_private_msg_sse", HandleSendPrivateMsgSSE) +} + +type InterfaceBody struct { + Content string `json:"content"` + State int `json:"state"` + PromptKeyboard []string `json:"prompt_keyboard,omitempty"` + ActionButton int `json:"action_button,omitempty"` + CallbackData string `json:"callback_data,omitempty"` +} + +func incrementIndex(msgID string) int { + if _, exists := msgIDToIndex[msgID]; !exists { + msgIDToIndex[msgID] = 0 // 初始化为0 + return 0 + } + msgIDToIndex[msgID]++ // 递增Index + return msgIDToIndex[msgID] +} + +// GetRelatedID 根据MessageID获取相关的ID +func GetRelatedID(MessageID string) string { + if relatedID, exists := msgIDToRelatedID[MessageID]; exists { + return relatedID + } + // 如果没有找到转换关系,返回空字符串 + return "" +} + +// UpdateRelatedID 更新MessageID到respID的映射关系 +func UpdateRelatedID(MessageID, ID string) { + msgIDToRelatedID[MessageID] = ID +} + +func HandleSendPrivateMsgSSE(client callapi.Client, api openapi.OpenAPI, apiv2 openapi.OpenAPI, message callapi.ActionMessage) (string, error) { + // 使用 message.Echo 作为key来获取消息类型 + var retmsg string + + // 检查UserID是否为0 + checkZeroUserID := func(id interface{}) bool { + switch v := id.(type) { + case int: + return v != 0 + case int64: + return v != 0 + case string: + return v != "0" // 同样检查字符串形式的0 + default: + return true // 如果不是int、int64或string,假定它不为0 + } + } + + // New checks for UserID and GroupID being nil or 0 + if message.Params.UserID == nil || !checkZeroUserID(message.Params.UserID) { + mylog.Printf("send_group_msg_sse接收到错误action: %v", message) + return "", nil + } + + var err error + + var resp *dto.C2CMessageResponse + + //私聊信息 + var UserID string + if config.GetIdmapPro() { + //还原真实的userid + //mylog.Printf("group_private:%v", message.Params.UserID.(string)) + _, UserID, err = idmap.RetrieveRowByIDv2Pro("690426430", message.Params.UserID.(string)) + if err != nil { + mylog.Printf("Error reading config: %v", err) + return "", nil + } + mylog.Printf("测试,通过Proid获取的UserID:%v", UserID) + } else { + //还原真实的userid + UserID, err = idmap.RetrieveRowByIDv2(message.Params.UserID.(string)) + if err != nil { + mylog.Printf("Error reading config: %v", err) + return "", nil + } + } + + // 首先,将message.Params.Message序列化成JSON字符串 + messageJSON, err := json.Marshal(message.Params.Message) + if err != nil { + fmt.Printf("Error marshalling message: %v\n", err) + return "", nil + } + + // 然后,将这个JSON字符串反序列化到InterfaceBody类型的对象中 + var messageBody InterfaceBody + err = json.Unmarshal(messageJSON, &messageBody) + if err != nil { + fmt.Printf("Error unmarshalling to InterfaceBody: %v\n", err) + return "", nil + } + + // 输出反序列化后的对象,确认是否成功转换 + fmt.Printf("Recovered InterfaceBody: %+v\n", messageBody) + // 使用 echo 获取消息ID + var messageID string + if config.GetLazyMessageId() { + //由于实现了Params的自定义unmarshell 所以可以类型安全的断言为string + messageID = echo.GetLazyMessagesId(UserID) + mylog.Printf("GetLazyMessagesId: %v", messageID) + } + if messageID == "" { + if echoStr, ok := message.Echo.(string); ok { + messageID = echo.GetMsgIDByKey(echoStr) + mylog.Println("echo取私聊发信息对应的message_id:", messageID) + } + } + // 如果messageID仍然为空,尝试使用config.GetAppID和UserID的组合来获取messageID + // 如果messageID为空,通过函数获取 + if messageID == "" { + messageID = GetMessageIDByUseridOrGroupid(config.GetAppIDStr(), UserID) + mylog.Println("通过GetMessageIDByUserid函数获取的message_id:", messageID) + } + if messageID == "2000" { + messageID = "" + mylog.Println("通过lazymsgid发送群私聊主动信息,每月可发送1次") + } + + // 获取并打印相关ID + relatedID := GetRelatedID(messageID) + fmt.Println("相关ID:", relatedID) + dtoSSE := generateMessageSSE(messageBody, messageID, relatedID) + + mylog.Printf("私聊发信息sse:%v", dtoSSE) + + resp, err = apiv2.PostC2CMessageSSE(context.TODO(), UserID, dtoSSE) + if err != nil { + mylog.Printf("发送文本私聊信息失败: %v", err) + //如果失败 防止进入递归 + return "", nil + } + + // 更新或刷新映射关系 + UpdateRelatedID(messageID, resp.Message.ID) + + //发送成功回执 + retmsg, _ = SendC2CResponse(client, err, &message, resp) + + return retmsg, nil +} + +func generateMessageSSE(body InterfaceBody, msgID, ID string) *dto.MessageSSE { + index := incrementIndex(msgID) // 获取并递增Index + + // 将InterfaceBody的PromptKeyboard转换为MessageSSE的结构 + var rows []dto.RowSSE + for _, label := range body.PromptKeyboard { + row := dto.RowSSE{ + Buttons: []dto.ButtonSSE{ + { + RenderData: dto.RenderDataSSE{Label: label, Style: 2}, + Action: dto.ActionSSE{Type: 2}, + }, + }, + } + rows = append(rows, row) + } + + var msgsse dto.MessageSSE + + if body.Content != "" { + // 确保Markdown已经初始化 + msgsse.Markdown = &dto.MarkdownSSE{} + msgsse.Markdown.Content = body.Content + } + + if len(rows) > 0 { + // 确保PromptKeyboard及其嵌套结构已经初始化 + msgsse.PromptKeyboard = &dto.KeyboardSSE{ + KeyboardContentSSE: dto.KeyboardContentSSE{ + Content: dto.ContentSSE{ + Rows: []dto.RowSSE{}, // 初始化空切片,避免nil切片赋值 + }, + }, + } + msgsse.PromptKeyboard.KeyboardContentSSE.Content.Rows = rows + } + + // 剩余字段赋值 + msgsse.MsgType = 2 + msgsse.MsgSeq = index + 3 + msgsse.Stream = &dto.StreamSSE{ + State: body.State, + Index: index, + } + + if ID != "" { + msgsse.Stream.ID = ID + } + if msgID != "" { + msgsse.MsgID = msgID + } + + // 初始化ActionButtonSSE,如果CallbackData有值 + if body.CallbackData != "" { + msgsse.ActionButton = &dto.ActionButtonSSE{ + TemplateID: body.ActionButton, + CallbackData: body.CallbackData, + } + } + + return &msgsse + +} diff --git a/httpapi/httpapi.go b/httpapi/httpapi.go index 5f95d4f2..e3dad08c 100644 --- a/httpapi/httpapi.go +++ b/httpapi/httpapi.go @@ -38,6 +38,10 @@ func CombinedMiddleware(api openapi.OpenAPI, apiV2 openapi.OpenAPI) gin.HandlerF handleSendPrivateMessage(c, api, apiV2) return } + if c.Request.URL.Path == "/send_private_msg_sse" { + handleSendPrivateMessageSSE(c, api, apiV2) + return + } if c.Request.URL.Path == "/send_guild_channel_msg" { handleSendGuildChannelMessage(c, api, apiV2) return @@ -210,6 +214,55 @@ func handleSendPrivateMessage(c *gin.Context, api openapi.OpenAPI, apiV2 openapi c.String(http.StatusOK, retmsg) } +// handleSendPrivateMessageSSE 处理发送私聊SSE消息的请求 +func handleSendPrivateMessageSSE(c *gin.Context, api openapi.OpenAPI, apiV2 openapi.OpenAPI) { + var retmsg string + var req struct { + GroupID int64 `json:"group_id" form:"group_id"` + UserID int64 `json:"user_id" form:"user_id"` + Message interface{} `json:"message" form:"message"` + AutoEscape bool `json:"auto_escape" form:"auto_escape"` + } + + // 根据请求方法解析参数 + if c.Request.Method == http.MethodGet { + // 从URL查询参数解析 + if err := c.ShouldBindQuery(&req); err != nil { + c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) + return + } + } else { + // 从JSON或表单数据解析 + if err := c.ShouldBind(&req); err != nil { + c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) + return + } + } + + // 使用解析后的参数处理请求 + // 例如:api.SendGroupMessage(req.GroupID, req.Message, req.AutoEscape) + client := &HttpAPIClient{} + // 创建 ActionMessage 实例 + message := callapi.ActionMessage{ + Action: "send_private_msg_sse", + Params: callapi.ParamsContent{ + GroupID: strconv.FormatInt(req.GroupID, 10), // 注意这里需要转换类型,因为 GroupID 是 int64 + UserID: strconv.FormatInt(req.UserID, 10), + Message: req.Message, + }, + } + // 调用处理函数 + retmsg, err := handlers.HandleSendPrivateMsgSSE(client, api, apiV2, message) + if err != nil { + c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) + return + } + + // 返回处理结果 + c.Header("Content-Type", "application/json") + c.String(http.StatusOK, retmsg) +} + // handleSendGuildChannelMessage 处理发送消频道息的请求 func handleSendGuildChannelMessage(c *gin.Context, api openapi.OpenAPI, apiV2 openapi.OpenAPI) { var retmsg string From afdf12e80c7483a9f68c375a08e0af76606e154b Mon Sep 17 00:00:00 2001 From: cosmo Date: Sun, 7 Apr 2024 20:23:07 +0800 Subject: [PATCH 22/33] beta357 --- Processor/ProcessGroupMessage.go | 8 ++- Processor/Processor.go | 2 + go.mod | 2 +- go.sum | 6 ++- idmap/service.go | 42 +++++++-------- images/upload_api.go | 89 ++++++++++++++++++++++++++++++++ server/uploadpic.go | 24 ++++++--- url/shorturl.go | 14 ++--- webui/cookie.go | 12 ++--- 9 files changed, 151 insertions(+), 48 deletions(-) diff --git a/Processor/ProcessGroupMessage.go b/Processor/ProcessGroupMessage.go index 4cacbfab..620c8c5f 100644 --- a/Processor/ProcessGroupMessage.go +++ b/Processor/ProcessGroupMessage.go @@ -122,11 +122,9 @@ func (p *Processors) ProcessGroupMessage(data *dto.WSGroupATMessageData) error { groupMsg.RealMessageType = "group" groupMsg.IsBindedUserId = IsBindedUserId groupMsg.IsBindedGroupId = IsBindedGroupId - if IsBindedUserId { - groupMsg.Avatar, _ = GenerateAvatarURL(userid64) - } else { - groupMsg.Avatar, _ = GenerateAvatarURLV2(data.Author.ID) - } + groupMsg.RealGroupID = data.GroupID + groupMsg.RealUserID = data.Author.ID + groupMsg.Avatar, _ = GenerateAvatarURLV2(data.Author.ID) } //根据条件判断是否增加nick和card var CaN = config.GetCardAndNick() diff --git a/Processor/Processor.go b/Processor/Processor.go index 297cffef..71f23ed0 100644 --- a/Processor/Processor.go +++ b/Processor/Processor.go @@ -91,6 +91,8 @@ type OnebotGroupMessage struct { Font int `json:"font"` UserID int64 `json:"user_id"` RealMessageType string `json:"real_message_type,omitempty"` //当前信息的真实类型 group group_private guild guild_private + RealUserID string `json:"real_user_id,omitempty"` //当前真实uid + RealGroupID string `json:"real_group_id,omitempty"` //当前真实gid IsBindedGroupId bool `json:"is_binded_group_id,omitempty"` //当前群号是否是binded后的 IsBindedUserId bool `json:"is_binded_user_id,omitempty"` //当前用户号号是否是binded后的 } diff --git a/go.mod b/go.mod index e64ad884..c41d3ff0 100644 --- a/go.mod +++ b/go.mod @@ -7,7 +7,6 @@ require ( github.com/aliyun/alibaba-cloud-sdk-go v1.62.645 github.com/aliyun/aliyun-oss-go-sdk v3.0.1+incompatible github.com/baidubce/bce-sdk-go v0.9.161 - github.com/boltdb/bolt v1.3.1 github.com/fatih/color v1.15.0 github.com/gin-gonic/gin v1.9.1 github.com/google/uuid v1.4.0 @@ -16,6 +15,7 @@ require ( github.com/skip2/go-qrcode v0.0.0-20200617195104-da1b6568686e github.com/tencent-connect/botgo v0.1.6 github.com/tencentyun/cos-go-sdk-v5 v0.7.45 + go.etcd.io/bbolt v1.3.9 gopkg.in/yaml.v3 v3.0.1 ) diff --git a/go.sum b/go.sum index bf141ce6..401b1c22 100644 --- a/go.sum +++ b/go.sum @@ -11,8 +11,6 @@ github.com/aliyun/aliyun-oss-go-sdk v3.0.1+incompatible h1:so4m5rRA32Tc5GgKg/5gK github.com/aliyun/aliyun-oss-go-sdk v3.0.1+incompatible/go.mod h1:T/Aws4fEfogEE9v+HPhhw+CntffsBHJ8nXQCwKr0/g8= github.com/baidubce/bce-sdk-go v0.9.161 h1:dNf2K7clz167h7XOffqPsBg0GPntUp7EYGHd6yOFSJw= github.com/baidubce/bce-sdk-go v0.9.161/go.mod h1:zbYJMQwE4IZuyrJiFO8tO8NbtYiKTFTbwh4eIsqjVdg= -github.com/boltdb/bolt v1.3.1 h1:JQmyP4ZBrce+ZQu0dY660FMfatumYDLun9hBCUVIkF4= -github.com/boltdb/bolt v1.3.1/go.mod h1:clJnj/oiGkjum5o1McbSZDSLxVThjynRyGBgiAx27Ps= github.com/bytedance/sonic v1.5.0/go.mod h1:ED5hyg4y6t3/9Ku1R6dU/4KyJ48DZ4jPhfY1O2AihPM= github.com/bytedance/sonic v1.9.1 h1:6iJ6NqdoxCDr6mbY8h18oSO+cShGSMRGCEo7F2h0x8s= github.com/bytedance/sonic v1.9.1/go.mod h1:i736AoUSYt75HyZLoJW9ERYxcy6eaN6h4BZXU064P/U= @@ -186,6 +184,8 @@ github.com/ugorji/go/codec v1.2.11/go.mod h1:UNopzCgEMSXjBc6AOMqYvWC1ktqTAfzJZUZ github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yusufpapurcu/wmi v1.2.3 h1:E1ctvB7uKFMOJw3fdOW32DwGE9I7t++CRUEMKvFoFiw= github.com/yusufpapurcu/wmi v1.2.3/go.mod h1:SBZ9tNy3G9/m5Oi98Zks0QjeHVDvuK0qfxQmPyzfmi0= +go.etcd.io/bbolt v1.3.9 h1:8x7aARPEXiXbHmtUwAIv7eV2fQFHrLLavdiJ3uzJXoI= +go.etcd.io/bbolt v1.3.9/go.mod h1:zaO32+Ti0PK1ivdPtgMESzuzL2VPoIG1PCQNvOdo/dE= go.uber.org/atomic v1.9.0 h1:ECmE8Bn/WFTYwEW/bpKD3M8VtR/zQVbavAoalC1PYyE= go.uber.org/atomic v1.9.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= golang.org/x/arch v0.0.0-20210923205945-b76863e36670/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8= @@ -220,6 +220,8 @@ golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.5.0 h1:60k92dhOjHxJkrqnwsfl8KuaHbn/5dl0lUPUklKo3qE= +golang.org/x/sync v0.5.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= diff --git a/idmap/service.go b/idmap/service.go index d14e5f71..69590765 100644 --- a/idmap/service.go +++ b/idmap/service.go @@ -16,9 +16,9 @@ import ( "strings" "sync" - "github.com/boltdb/bolt" "github.com/hoshinonyaruko/gensokyo/config" "github.com/hoshinonyaruko/gensokyo/mylog" + "go.etcd.io/bbolt" ) var ( @@ -35,18 +35,18 @@ const ( CounterKey = "currentRow" ) -var db *bolt.DB +var db *bbolt.DB var ErrKeyNotFound = errors.New("key not found") func InitializeDB() { var err error - db, err = bolt.Open(DBName, 0600, nil) + db, err = bbolt.Open(DBName, 0600, nil) if err != nil { log.Fatalf("Error opening DB: %v", err) } - db.Update(func(tx *bolt.Tx) error { + db.Update(func(tx *bbolt.Tx) error { _, err := tx.CreateBucketIfNotExists([]byte(BucketName)) return err }) @@ -118,7 +118,7 @@ func CheckValuev2(value int64) bool { func StoreID(id string) (int64, error) { var newRow int64 - err := db.Update(func(tx *bolt.Tx) error { + err := db.Update(func(tx *bbolt.Tx) error { b := tx.Bucket([]byte(BucketName)) // 检查ID是否已经存在 @@ -180,7 +180,7 @@ func StoreID(id string) (int64, error) { func SimplifiedStoreID(id string) (int64, error) { var newRow int64 - err := db.Update(func(tx *bolt.Tx) error { + err := db.Update(func(tx *bbolt.Tx) error { b := tx.Bucket([]byte(BucketName)) // 生成新的行号 @@ -261,7 +261,7 @@ func StoreIDPro(id string, subid string) (int64, int64, error) { var newRowID, newSubRowID int64 var err error - err = db.Update(func(tx *bolt.Tx) error { + err = db.Update(func(tx *bbolt.Tx) error { b := tx.Bucket([]byte(BucketName)) // 生成正向键 @@ -394,7 +394,7 @@ func StoreIDv2Pro(id string, subid string) (int64, int64, error) { // 根据b得到a func RetrieveRowByID(rowid string) (string, error) { var id string - err := db.View(func(tx *bolt.Tx) error { + err := db.View(func(tx *bbolt.Tx) error { b := tx.Bucket([]byte(BucketName)) // 根据行号检索ID @@ -461,7 +461,7 @@ func RetrieveRowByIDv2Pro(newRowID string, newSubRowID string) (string, string, func RetrieveRowByIDPro(newRowID, newSubRowID string) (string, string, error) { var id, subid string - err := db.View(func(tx *bolt.Tx) error { + err := db.View(func(tx *bbolt.Tx) error { b := tx.Bucket([]byte(BucketName)) // 根据新的行号和子行号检索ID和SubID @@ -529,7 +529,7 @@ func RetrieveRowByIDv2(rowid string) (string, error) { // 根据a 以b为类别 储存c func WriteConfig(sectionName, keyName, value string) error { - return db.Update(func(tx *bolt.Tx) error { + return db.Update(func(tx *bbolt.Tx) error { b, err := tx.CreateBucketIfNotExists([]byte(ConfigBucket)) if err != nil { mylog.Printf("Error creating or accessing bucket: %v", err) @@ -590,7 +590,7 @@ func WriteConfigv2(sectionName, keyName, value string) error { // 根据a和b取出c func ReadConfig(sectionName, keyName string) (string, error) { var result string - err := db.View(func(tx *bolt.Tx) error { + err := db.View(func(tx *bbolt.Tx) error { b := tx.Bucket([]byte(ConfigBucket)) if b == nil { return fmt.Errorf("bucket not found") @@ -611,7 +611,7 @@ func ReadConfig(sectionName, keyName string) (string, error) { // DeleteConfig根据sectionName和keyName删除指定的键值对 func DeleteConfig(sectionName, keyName string) error { - return db.Update(func(tx *bolt.Tx) error { + return db.Update(func(tx *bbolt.Tx) error { b := tx.Bucket([]byte(ConfigBucket)) if b == nil { return fmt.Errorf("bucket %s does not exist", ConfigBucket) @@ -727,7 +727,7 @@ func joinSectionAndKey(sectionName, keyName string) []byte { // UpdateVirtualValue 更新旧的虚拟值到新的虚拟值的映射 func UpdateVirtualValue(oldRowValue, newRowValue int64) error { - return db.Update(func(tx *bolt.Tx) error { + return db.Update(func(tx *bbolt.Tx) error { b := tx.Bucket([]byte(BucketName)) // 查找旧虚拟值对应的真实值 @@ -766,7 +766,7 @@ func UpdateVirtualValue(oldRowValue, newRowValue int64) error { // RetrieveRealValue 根据虚拟值获取真实值,并返回虚拟值及其对应的真实值 func RetrieveRealValue(virtualValue int64) (string, string, error) { var realValue string - err := db.View(func(tx *bolt.Tx) error { + err := db.View(func(tx *bbolt.Tx) error { b := tx.Bucket([]byte(BucketName)) // 构造键,根据虚拟值查找 @@ -791,7 +791,7 @@ func RetrieveRealValue(virtualValue int64) (string, string, error) { // RetrieveVirtualValue 根据真实值获取虚拟值,并返回真实值及其对应的虚拟值 func RetrieveVirtualValue(realValue string) (string, string, error) { var virtualValue int64 - err := db.View(func(tx *bolt.Tx) error { + err := db.View(func(tx *bbolt.Tx) error { b := tx.Bucket([]byte(BucketName)) // 根据真实值查找虚拟值 @@ -967,7 +967,7 @@ func RetrieveVirtualValuev2Pro(realValue string, realValueSub string) (string, s func RetrieveVirtualValuePro(realValue string, realValueSub string) (string, string, error) { var newRowID, newSubRowID string - err := db.View(func(tx *bolt.Tx) error { + err := db.View(func(tx *bbolt.Tx) error { b := tx.Bucket([]byte(BucketName)) // 构建正向键 @@ -1001,7 +1001,7 @@ func RetrieveVirtualValuePro(realValue string, realValueSub string) (string, str func RetrieveRealValuePro(virtualValue1, virtualValue2 int64) (string, string, error) { var realValue1, realValue2 string - err := db.View(func(tx *bolt.Tx) error { + err := db.View(func(tx *bbolt.Tx) error { b := tx.Bucket([]byte(BucketName)) // 根据两个虚拟值构造键 @@ -1079,7 +1079,7 @@ func RetrieveRealValuesv2Pro(virtualValue int64, virtualValueSub int64) (string, // UpdateVirtualValuePro 更新一对旧虚拟值到新虚拟值的映射 旧群号 新群号 旧用户 新用户 func UpdateVirtualValuePro(oldVirtualValue1, newVirtualValue1, oldVirtualValue2, newVirtualValue2 int64) error { - return db.Update(func(tx *bolt.Tx) error { + return db.Update(func(tx *bbolt.Tx) error { b := tx.Bucket([]byte(BucketName)) // 构造旧和新的复合键 oldCompositeKey := fmt.Sprintf("%d:%d", oldVirtualValue1, oldVirtualValue2) @@ -1147,7 +1147,7 @@ func UpdateVirtualValuev2Pro(oldVirtualValue1, newVirtualValue1, oldVirtualValue func FindKeysBySubAndType(sub string, typeSuffix string) ([]string, error) { var ids []string - err := db.View(func(tx *bolt.Tx) error { + err := db.View(func(tx *bbolt.Tx) error { b := tx.Bucket([]byte(ConfigBucket)) if b == nil { return fmt.Errorf("bucket %s not found", ConfigBucket) @@ -1178,7 +1178,7 @@ func FindKeysBySubAndType(sub string, typeSuffix string) ([]string, error) { func FindSubKeysById(id string) ([]string, error) { var subKeys []string - err := db.View(func(tx *bolt.Tx) error { + err := db.View(func(tx *bbolt.Tx) error { b := tx.Bucket([]byte("ids")) if b == nil { return fmt.Errorf("bucket %s not found", "ids") @@ -1257,7 +1257,7 @@ func FindSubKeysByIdPro(id string) ([]string, error) { // 场景: xxx:yyy zzz:bbb zzz:bbb xxx:yyy 把xxx(id)替换为newID 比如更换群号(会卡住) func UpdateKeysWithNewID(id, newID string) error { - return db.Update(func(tx *bolt.Tx) error { + return db.Update(func(tx *bbolt.Tx) error { b := tx.Bucket([]byte(BucketName)) if b == nil { return fmt.Errorf("bucket %s not found", BucketName) diff --git a/images/upload_api.go b/images/upload_api.go index 6c5a983e..c55c586c 100644 --- a/images/upload_api.go +++ b/images/upload_api.go @@ -194,6 +194,95 @@ func UploadBase64ImageToServer(msgid string, base64Image string, groupID string, return picURL, 0, 0, 0, nil } +// TransferUrlToServerUrlr 将url转化为ntv2链接 +func TransferUrlToServerUrl(msgid string, url string, groupID string, apiv2 openapi.OpenAPI) (string, uint64, uint32, uint32, error) { + var err error + + if isNumeric(groupID) { + //用转换前的群号获取msgid + if msgid == "" { + msgid = echo.GetLazyMessagesId(groupID) + } + // 检查groupID是否为纯数字构成 RetrieveRowByIDv2是通用逻辑,也可以将userid还原为32位数originaluserid + // 但没有私信权限,故没有测试 + originalGroupID, err := idmap.RetrieveRowByIDv2(groupID) + if err != nil { + log.Printf("Error retrieving original GroupID: %v", err) + return url, 0, 0, 0, nil + } + log.Printf("通过idmap获取的originalGroupID: %v", originalGroupID) + + // 用originalGroupID更新groupID + groupID = originalGroupID + } else { + // 映射str的GroupID到int + GroupID64, err := idmap.StoreIDv2(groupID) + if err != nil { + log.Printf("failed to convert ChannelID to int: %v", err) + return url, 0, 0, 0, nil + } + groupIDTemp := strconv.FormatInt(GroupID64, 10) + //用数字的群号获取msgid + if msgid == "" { + msgid = echo.GetLazyMessagesId(groupIDTemp) + } + } + + richMediaMessage := &dto.RichMediaMessage{ + EventID: msgid, + FileType: 1, // 1代表图片 + URL: url, + Content: "", // 这个字段文档没有了 + SrvSendMsg: false, + } + + var fileInfo string + //尝试群聊发图 + fileInfo, err = uploadMedia(context.TODO(), groupID, richMediaMessage, apiv2) + if err != nil { + //尝试私信发图 + fileInfo, err = uploadMediaPrivate(context.TODO(), groupID, richMediaMessage, apiv2) + if err != nil { + //返回原始图片url + return url, 0, 0, 0, nil + } + } + + // 将Base64字符串解码为二进制 + fileInfoBytes, err := base64.StdEncoding.DecodeString(fileInfo) + if err != nil { + log.Fatalf("Failed to decode Base64 string: %v", err) + } + + // 初始化Proto消息类型 + var mainMessage protobuf.Main + + // 解析二进制数据到Proto消息 + err = proto.Unmarshal(fileInfoBytes, &mainMessage) + if err != nil { + log.Fatalf("Failed to unmarshal Proto message: %v", err) + } + + // 从Proto消息中读取值 + realGroupID := mainMessage.GetA().GetB().GetInfo().GetDetail().GetGroupInfo().GetGroupNumber() + downloadURL := mainMessage.GetA().GetImageData().GetImageInfo().GetUrl() + //https的地址不能放到md里 + //downloadURL = "https://multimedia.nt.qq.com.cn" + downloadURL + // 将 downloadURL 中的所有下划线 "_" 替换为 "%5f" + downloadURL = strings.Replace(downloadURL, "_", "%5f", -1) + downloadURL = "http://multimedia.nt.qq.com" + downloadURL + width := mainMessage.GetA().GetImageData().GetWidth() + height := mainMessage.GetA().GetImageData().GetHeight() + + // 打印读取的值 + log.Printf("RealGroup ID: %d\n", realGroupID) + log.Printf("Download URL: %s, Width: %d, Height: %d\n", downloadURL, width, height) + + // 根据需要返回适当的值 + return downloadURL, realGroupID, width, height, nil + +} + // 将base64语音通过lotus转换成url func UploadBase64RecordToServer(base64Record string) (string, error) { extraPicAuditingType := config.GetOssType() diff --git a/server/uploadpic.go b/server/uploadpic.go index c60cc337..e8dce780 100644 --- a/server/uploadpic.go +++ b/server/uploadpic.go @@ -126,7 +126,6 @@ func UploadBase64ImageHandler(rateLimiter *RateLimiter) gin.HandlerFunc { } } - func UploadBase64ImageHandlerV2(rateLimiter *RateLimiter, apiv2 openapi.OpenAPI) gin.HandlerFunc { return func(c *gin.Context) { ipAddress := c.ClientIP() @@ -137,18 +136,31 @@ func UploadBase64ImageHandlerV2(rateLimiter *RateLimiter, apiv2 openapi.OpenAPI) // 从请求中获取必要的参数 base64Image := c.PostForm("base64Image") - msgid := c.DefaultPostForm("msgid", "") // msgid可以为空 - groupID := c.PostForm("groupID") // groupID是必需的 + imageUrl := c.PostForm("url") // 新增的url参数 + msgid := c.DefaultPostForm("msgid", "") + groupID := c.PostForm("groupID") if groupID == "" { c.JSON(http.StatusBadRequest, gin.H{"error": "groupID is required"}) return } - // 调用函数上传图片 - imageURL, groupid, width, height, err := images.UploadBase64ImageToServer(msgid, base64Image, groupID, apiv2) + var imageURL string + var groupid uint64 + var width, height uint32 + var err error + + // 根据参数调用不同的处理逻辑 + if base64Image != "" { + imageURL, groupid, width, height, err = images.UploadBase64ImageToServer(msgid, base64Image, groupID, apiv2) + } else if imageUrl != "" { + imageURL, groupid, width, height, err = images.TransferUrlToServerUrl(msgid, imageUrl, groupID, apiv2) + } else { + c.JSON(http.StatusBadRequest, gin.H{"error": "either base64Image or url is required"}) + return + } + if err != nil { - // 根据错误类型返回合适的HTTP状态码和错误信息 c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) return } diff --git a/url/shorturl.go b/url/shorturl.go index 36c70048..0d8381ad 100644 --- a/url/shorturl.go +++ b/url/shorturl.go @@ -14,10 +14,10 @@ import ( "strings" "time" - "github.com/boltdb/bolt" "github.com/gin-gonic/gin" "github.com/hoshinonyaruko/gensokyo/config" "github.com/hoshinonyaruko/gensokyo/mylog" + "go.etcd.io/bbolt" ) const ( @@ -25,7 +25,7 @@ const ( ) var ( - db *bolt.DB + db *bbolt.DB ) const charset = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789" @@ -48,13 +48,13 @@ func generateHashedString(url string) string { // 这里的数据库是在init创建的 func init() { var err error - db, err = bolt.Open("gensokyo.db", 0600, nil) + db, err = bbolt.Open("gensokyo.db", 0600, nil) if err != nil { panic(err) } // Ensure bucket exists - err = db.Update(func(tx *bolt.Tx) error { + err = db.Update(func(tx *bbolt.Tx) error { _, err := tx.CreateBucketIfNotExists([]byte(bucketName)) if err != nil { return fmt.Errorf("failed to create or get the bucket: %v", err) @@ -222,7 +222,7 @@ func GenerateShortURL(longURL string) string { func existsInDB(shortURL string) (bool, error) { exists := false - err := db.View(func(tx *bolt.Tx) error { + err := db.View(func(tx *bbolt.Tx) error { b := tx.Bucket([]byte(bucketName)) v := b.Get([]byte(shortURL)) if v != nil { @@ -269,7 +269,7 @@ func getLongURLFromDB(shortURL string) (string, error) { return response["longURL"].(string), nil } else { var longURL string - err := db.View(func(tx *bolt.Tx) error { + err := db.View(func(tx *bbolt.Tx) error { b := tx.Bucket([]byte(bucketName)) v := b.Get([]byte(shortURL)) if v == nil { @@ -284,7 +284,7 @@ func getLongURLFromDB(shortURL string) (string, error) { // storeURL 存储长URL和对应的短URL func storeURL(shortURL, longURL string) error { - return db.Update(func(tx *bolt.Tx) error { + return db.Update(func(tx *bbolt.Tx) error { b := tx.Bucket([]byte(bucketName)) return b.Put([]byte(shortURL), []byte(longURL)) }) diff --git a/webui/cookie.go b/webui/cookie.go index bbafa277..a858639c 100644 --- a/webui/cookie.go +++ b/webui/cookie.go @@ -6,8 +6,8 @@ import ( "log" "time" - "github.com/boltdb/bolt" "github.com/google/uuid" + "go.etcd.io/bbolt" ) const ( @@ -17,18 +17,18 @@ const ( ExpirationHours = 24 // Cookie 有效期为24小时 ) -var db *bolt.DB +var db *bbolt.DB var ErrCookieNotFound = errors.New("cookie not found") var ErrCookieExpired = errors.New("cookie has expired") func InitializeDB() { var err error - db, err = bolt.Open(DBName, 0600, nil) + db, err = bbolt.Open(DBName, 0600, nil) if err != nil { log.Fatalf("Error opening DB: %v", err) } - db.Update(func(tx *bolt.Tx) error { + db.Update(func(tx *bbolt.Tx) error { _, err := tx.CreateBucketIfNotExists([]byte(CookieBucket)) return err }) @@ -42,7 +42,7 @@ func GenerateCookie() (string, error) { cookie := uuid.New().String() expiration := time.Now().Add(ExpirationHours * time.Hour).Unix() - err := db.Update(func(tx *bolt.Tx) error { + err := db.Update(func(tx *bbolt.Tx) error { bucket := tx.Bucket([]byte(CookieBucket)) if err := bucket.Put([]byte(cookie), intToBytes(expiration)); err != nil { return err @@ -59,7 +59,7 @@ func GenerateCookie() (string, error) { func ValidateCookie(cookie string) (bool, error) { isValid := false - err := db.View(func(tx *bolt.Tx) error { + err := db.View(func(tx *bbolt.Tx) error { bucket := tx.Bucket([]byte(CookieBucket)) expBytes := bucket.Get([]byte(cookie)) if expBytes == nil { From 78ab73775e339835b0848803303a3506adcb7303 Mon Sep 17 00:00:00 2001 From: cosmo Date: Mon, 8 Apr 2024 17:18:26 +0800 Subject: [PATCH 23/33] beta358 --- httpapi/httpapi.go | 76 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 76 insertions(+) diff --git a/httpapi/httpapi.go b/httpapi/httpapi.go index e3dad08c..96dd7dc9 100644 --- a/httpapi/httpapi.go +++ b/httpapi/httpapi.go @@ -1,7 +1,9 @@ package httpapi import ( + "fmt" "net/http" + "reflect" "strconv" "strings" @@ -54,6 +56,10 @@ func CombinedMiddleware(api openapi.OpenAPI, apiV2 openapi.OpenAPI) gin.HandlerF handlePutInteraction(c, api, apiV2) return } + if c.Request.URL.Path == "/delete_msg" { + handleDeleteMsg(c, api, apiV2) + return + } // 调用c.Next()以继续处理请求链 c.Next() @@ -392,3 +398,73 @@ func handlePutInteraction(c *gin.Context, api openapi.OpenAPI, apiV2 openapi.Ope c.Header("Content-Type", "application/json") c.String(http.StatusOK, retmsg) } + +// 类型转换函数,将interface{}转换为string +func convertToString(value interface{}) string { + switch v := value.(type) { + case int: + return strconv.Itoa(v) + case int64: + return strconv.FormatInt(v, 10) + case float64: + return strconv.FormatFloat(v, 'f', -1, 64) + case string: + return v + default: + fmt.Println("Unexpected type:", reflect.TypeOf(value)) + return "" + } +} + +func handleDeleteMsg(c *gin.Context, api openapi.OpenAPI, apiV2 openapi.OpenAPI) { + // 使用interface{}以适应不同类型的输入,接受动态参数类型 + var req struct { + UserID interface{} `json:"user_id,omitempty" form:"user_id"` + GroupID interface{} `json:"group_id,omitempty" form:"group_id"` + ChannelID interface{} `json:"channel_id,omitempty" form:"channel_id"` + GuildID interface{} `json:"guild_id,omitempty" form:"guild_id"` + MessageID interface{} `json:"message_id" form:"message_id"` + } + + // 解析请求参数 + if err := c.ShouldBind(&req); err != nil { + c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) + return + } + + // 构造参数内容,只包括实际有值的字段 + params := callapi.ParamsContent{} + + if req.UserID != nil { + params.UserID = convertToString(req.UserID) + } + if req.GroupID != nil { + params.GroupID = convertToString(req.GroupID) + } + if req.ChannelID != nil { + params.ChannelID = convertToString(req.ChannelID) + } + if req.GuildID != nil { + params.GuildID = convertToString(req.GuildID) + } + if req.MessageID != nil { + params.MessageID = convertToString(req.MessageID) + } + + // 创建 ActionMessage 实例 + message := callapi.ActionMessage{ + Action: "delete_msg", + Params: params, + } + + // 调用处理函数,假设 handlers.DeleteMsg 已经实现并且适合处理消息删除的操作 + client := &HttpAPIClient{} + retmsg, err := handlers.DeleteMsg(client, api, apiV2, message) + if err != nil { + c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) + return + } + + // 返回处理结果 + c.JSON(http.StatusOK, gin.H{"message": retmsg}) +} From 1af000b83a5487e07e2323df8bd8b85f1211380b Mon Sep 17 00:00:00 2001 From: cosmo Date: Fri, 12 Apr 2024 11:25:38 +0800 Subject: [PATCH 24/33] beta359 --- handlers/send_group_msg.go | 38 +++++++++++++++++++++------------- handlers/send_group_msg_raw.go | 34 ++++++++++++++++++------------ handlers/send_msg.go | 36 +++++++++++++++++++------------- handlers/send_private_msg.go | 37 ++++++++++++++++++++------------- 4 files changed, 90 insertions(+), 55 deletions(-) diff --git a/handlers/send_group_msg.go b/handlers/send_group_msg.go index b7207fdc..23c5d30d 100644 --- a/handlers/send_group_msg.go +++ b/handlers/send_group_msg.go @@ -105,6 +105,9 @@ func HandleSendGroupMsg(client callapi.Client, api openapi.OpenAPI, apiv2 openap echo.AddMsgType(config.GetAppIDStr(), idInt64, "group_private") retmsg, _ = HandleSendGroupMsg(client, api, apiv2, messageCopy) } + } else { + // 特殊值代表不递归 + echo.AddMapping(idInt64, 10) } switch msgType { @@ -514,21 +517,28 @@ func HandleSendGroupMsg(client callapi.Client, api openapi.OpenAPI, apiv2 openap default: mylog.Printf("Unknown message type: %s", msgType) } - //重置递归类型 - if echo.GetMapping(idInt64) <= 0 { - echo.AddMsgType(config.GetAppIDStr(), idInt64, "") - } - echo.AddMapping(idInt64, echo.GetMapping(idInt64)-1) - - //递归3次枚举类型 - if echo.GetMapping(idInt64) > 0 { - tryMessageTypes := []string{"group", "guild", "guild_private"} - messageCopy := message // 创建message的副本 - echo.AddMsgType(config.GetAppIDStr(), idInt64, tryMessageTypes[echo.GetMapping(idInt64)-1]) - delay := config.GetSendDelay() - time.Sleep(time.Duration(delay) * time.Millisecond) - retmsg, _ = HandleSendGroupMsg(client, api, apiv2, messageCopy) + + // 如果递归id不是10(不递归特殊值) + if echo.GetMapping(idInt64) != 10 { + //重置递归类型 递归结束重置类型,避免下一次同样id,不同类型的请求被使用上一次类型 + if echo.GetMapping(idInt64) <= 0 { + echo.AddMsgType(config.GetAppIDStr(), idInt64, "") + } + + //减少递归计数器 + echo.AddMapping(idInt64, echo.GetMapping(idInt64)-1) + + //递归3次枚举类型 + if echo.GetMapping(idInt64) > 0 { + tryMessageTypes := []string{"group", "guild", "guild_private"} + messageCopy := message // 创建message的副本 + echo.AddMsgType(config.GetAppIDStr(), idInt64, tryMessageTypes[echo.GetMapping(idInt64)-1]) + delay := config.GetSendDelay() + time.Sleep(time.Duration(delay) * time.Millisecond) + retmsg, _ = HandleSendGroupMsg(client, api, apiv2, messageCopy) + } } + return retmsg, nil } diff --git a/handlers/send_group_msg_raw.go b/handlers/send_group_msg_raw.go index e3ecb263..f08ac062 100644 --- a/handlers/send_group_msg_raw.go +++ b/handlers/send_group_msg_raw.go @@ -96,6 +96,9 @@ func HandleSendGroupMsgRaw(client callapi.Client, api openapi.OpenAPI, apiv2 ope echo.AddMsgType(config.GetAppIDStr(), idInt64, "group_private") retmsg, _ = HandleSendGroupMsg(client, api, apiv2, messageCopy) } + } else { + // 特殊值代表不递归 + echo.AddMapping(idInt64, 10) } switch msgType { @@ -474,20 +477,25 @@ func HandleSendGroupMsgRaw(client callapi.Client, api openapi.OpenAPI, apiv2 ope default: mylog.Printf("Unknown message type: %s", msgType) } - //重置递归类型 - if echo.GetMapping(idInt64) <= 0 { - echo.AddMsgType(config.GetAppIDStr(), idInt64, "") - } - echo.AddMapping(idInt64, echo.GetMapping(idInt64)-1) - //递归3次枚举类型 - if echo.GetMapping(idInt64) > 0 { - tryMessageTypes := []string{"group", "guild", "guild_private"} - messageCopy := message // 创建message的副本 - echo.AddMsgType(config.GetAppIDStr(), idInt64, tryMessageTypes[echo.GetMapping(idInt64)-1]) - delay := config.GetSendDelay() - time.Sleep(time.Duration(delay) * time.Millisecond) - retmsg, _ = HandleSendGroupMsg(client, api, apiv2, messageCopy) + // 如果递归id不是10(不递归特殊值) + if echo.GetMapping(idInt64) != 10 { + //重置递归类型 + if echo.GetMapping(idInt64) <= 0 { + echo.AddMsgType(config.GetAppIDStr(), idInt64, "") + } + echo.AddMapping(idInt64, echo.GetMapping(idInt64)-1) + + //递归3次枚举类型 + if echo.GetMapping(idInt64) > 0 { + tryMessageTypes := []string{"group", "guild", "guild_private"} + messageCopy := message // 创建message的副本 + echo.AddMsgType(config.GetAppIDStr(), idInt64, tryMessageTypes[echo.GetMapping(idInt64)-1]) + delay := config.GetSendDelay() + time.Sleep(time.Duration(delay) * time.Millisecond) + retmsg, _ = HandleSendGroupMsg(client, api, apiv2, messageCopy) + } } + return retmsg, nil } diff --git a/handlers/send_msg.go b/handlers/send_msg.go index 5a4137e8..61a01597 100644 --- a/handlers/send_msg.go +++ b/handlers/send_msg.go @@ -111,6 +111,9 @@ func HandleSendMsg(client callapi.Client, api openapi.OpenAPI, apiv2 openapi.Ope echo.AddMsgType(config.GetAppIDStr(), idInt64, "group_private") retmsg, _ = HandleSendMsg(client, api, apiv2, messageCopy) } + } else { + // 特殊值代表不递归 + echo.AddMapping(idInt64, 10) } switch msgType { @@ -163,22 +166,27 @@ func HandleSendMsg(client callapi.Client, api openapi.OpenAPI, apiv2 openapi.Ope default: mylog.Printf("1Unknown message type: %s", msgType) } - //重置递归类型 - if echo.GetMapping(idInt64) <= 0 { - echo.AddMsgType(config.GetAppIDStr(), idInt64, "") - echo.AddMsgType(config.GetAppIDStr(), idInt642, "") - } - echo.AddMapping(idInt64, echo.GetMapping(idInt64)-1) - //递归3次枚举类型 - if echo.GetMapping(idInt64) > 0 { - tryMessageTypes := []string{"group", "guild", "guild_private"} - messageCopy := message // 创建message的副本 - echo.AddMsgType(config.GetAppIDStr(), idInt64, tryMessageTypes[echo.GetMapping(idInt64)-1]) - delay := config.GetSendDelay() - time.Sleep(time.Duration(delay) * time.Millisecond) - retmsg, _ = HandleSendMsg(client, api, apiv2, messageCopy) + // 如果递归id不是10(不递归特殊值) + if echo.GetMapping(idInt64) != 10 { + //重置递归类型 + if echo.GetMapping(idInt64) <= 0 { + echo.AddMsgType(config.GetAppIDStr(), idInt64, "") + echo.AddMsgType(config.GetAppIDStr(), idInt642, "") + } + echo.AddMapping(idInt64, echo.GetMapping(idInt64)-1) + + //递归3次枚举类型 + if echo.GetMapping(idInt64) > 0 { + tryMessageTypes := []string{"group", "guild", "guild_private"} + messageCopy := message // 创建message的副本 + echo.AddMsgType(config.GetAppIDStr(), idInt64, tryMessageTypes[echo.GetMapping(idInt64)-1]) + delay := config.GetSendDelay() + time.Sleep(time.Duration(delay) * time.Millisecond) + retmsg, _ = HandleSendMsg(client, api, apiv2, messageCopy) + } } + return retmsg, nil } diff --git a/handlers/send_private_msg.go b/handlers/send_private_msg.go index be98f276..dc9c801f 100644 --- a/handlers/send_private_msg.go +++ b/handlers/send_private_msg.go @@ -95,7 +95,11 @@ func HandleSendPrivateMsg(client callapi.Client, api openapi.OpenAPI, apiv2 open echo.AddMsgType(config.GetAppIDStr(), idInt64, "group_private") HandleSendPrivateMsg(client, api, apiv2, messageCopy) } + } else { + // 特殊值代表不递归 + echo.AddMapping(idInt64, 10) } + var resp *dto.C2CMessageResponse switch msgType { //这里是pr上来的,我也不明白为什么私聊会出现group类型 猜测是为了匹配包含了groupid的私聊? @@ -310,21 +314,26 @@ func HandleSendPrivateMsg(client callapi.Client, api openapi.OpenAPI, apiv2 open default: mylog.Printf("Unknown message type: %s", msgType) } - //重置递归类型 - if echo.GetMapping(idInt64) <= 0 { - echo.AddMsgType(config.GetAppIDStr(), idInt64, "") - } - echo.AddMapping(idInt64, echo.GetMapping(idInt64)-1) - - //递归3次枚举类型 - if echo.GetMapping(idInt64) > 0 { - tryMessageTypes := []string{"group", "guild", "guild_private"} - messageCopy := message // 创建message的副本 - echo.AddMsgType(config.GetAppIDStr(), idInt64, tryMessageTypes[echo.GetMapping(idInt64)-1]) - delay := config.GetSendDelay() - time.Sleep(time.Duration(delay) * time.Millisecond) - retmsg, _ = HandleSendPrivateMsg(client, api, apiv2, messageCopy) + + // 如果递归id不是10(不递归特殊值) + if echo.GetMapping(idInt64) != 10 { + //重置递归类型 + if echo.GetMapping(idInt64) <= 0 { + echo.AddMsgType(config.GetAppIDStr(), idInt64, "") + } + echo.AddMapping(idInt64, echo.GetMapping(idInt64)-1) + + //递归3次枚举类型 + if echo.GetMapping(idInt64) > 0 { + tryMessageTypes := []string{"group", "guild", "guild_private"} + messageCopy := message // 创建message的副本 + echo.AddMsgType(config.GetAppIDStr(), idInt64, tryMessageTypes[echo.GetMapping(idInt64)-1]) + delay := config.GetSendDelay() + time.Sleep(time.Duration(delay) * time.Millisecond) + retmsg, _ = HandleSendPrivateMsg(client, api, apiv2, messageCopy) + } } + return retmsg, nil } From 40f832c2180fbd8f487c135c12124317ae636b8a Mon Sep 17 00:00:00 2001 From: cosmo Date: Mon, 15 Apr 2024 13:57:56 +0800 Subject: [PATCH 25/33] beta360 --- botstats/botstats.go | 123 ++++++++++++++++++++++ config/config.go | 37 +++++++ handlers/get_status.go | 27 +++-- handlers/message_parser.go | 152 ++++++++++++++++++++++++--- handlers/send_group_forward_msg.go | 2 +- handlers/send_group_msg.go | 23 +++- handlers/send_group_msg_raw.go | 8 +- handlers/send_guild_channel_forum.go | 2 +- httpapi/httpapi.go | 2 +- main.go | 11 ++ template/config_template.go | 5 +- wsclient/ws.go | 13 ++- 12 files changed, 362 insertions(+), 43 deletions(-) create mode 100644 botstats/botstats.go diff --git a/botstats/botstats.go b/botstats/botstats.go new file mode 100644 index 00000000..b4af3c47 --- /dev/null +++ b/botstats/botstats.go @@ -0,0 +1,123 @@ +package botstats + +import ( + "log" + "strconv" + "strings" + "time" + + "go.etcd.io/bbolt" +) + +var db *bbolt.DB + +const ( + bucketName = "stats" +) + +func InitializeDB() { + var err error + db, err = bbolt.Open("botstats.db", 0600, nil) + if err != nil { + log.Fatalf("Failed to open database: %v", err) + } + + db.Update(func(tx *bbolt.Tx) error { + _, err := tx.CreateBucketIfNotExists([]byte(bucketName)) + if err != nil { + return err + } + return nil + }) +} + +const ( + messageReceivedKey = "messageReceived" + messageSentKey = "messageSent" + lastMessageTimeKey = "lastMessageTime" +) + +func RecordMessageReceived() { + recordStats(1, 0) +} + +func RecordMessageSent() { + recordStats(0, 1) +} + +// 收到增量 发出增量 +func recordStats(receivedIncrement int, sentIncrement int) { + db.Update(func(tx *bbolt.Tx) error { + b := tx.Bucket([]byte(bucketName)) + now := time.Now() + today := now.Format("2006-01-02") + + // Reset stats if not the current day + lastTimeBytes := b.Get([]byte(lastMessageTimeKey)) + if lastTimeBytes != nil && !strings.HasPrefix(string(lastTimeBytes), today) { + b.Put([]byte(messageReceivedKey), []byte("0")) + b.Put([]byte(messageSentKey), []byte("0")) + } + + updateCounter(b, messageReceivedKey, receivedIncrement) + updateCounter(b, messageSentKey, sentIncrement) + b.Put([]byte(lastMessageTimeKey), []byte(today+" "+now.Format(time.RFC3339))) + + return nil + }) +} + +func updateCounter(b *bbolt.Bucket, key string, increment int) { + currentValueBytes := b.Get([]byte(key)) + currentValue := 0 + if currentValueBytes != nil { + currentValue, _ = strconv.Atoi(string(currentValueBytes)) + } + newValue := currentValue + increment + b.Put([]byte(key), []byte(strconv.Itoa(newValue))) +} + +// 获取 收到 发出 上次收到Time 错误 +func GetStats() (int, int, int64, error) { + var messageReceived, messageSent int + var lastMessageTime int64 + err := db.View(func(tx *bbolt.Tx) error { + b := tx.Bucket([]byte(bucketName)) + messageReceived = getInt(b, messageReceivedKey) + messageSent = getInt(b, messageSentKey) + lastMessageTime = getLastMessageTime(b) + return nil + }) + return messageReceived, messageSent, lastMessageTime, err +} + +func getInt(b *bbolt.Bucket, key string) int { + valueBytes := b.Get([]byte(key)) + value, _ := strconv.Atoi(string(valueBytes)) + return value +} + +func getLastMessageTime(b *bbolt.Bucket) int64 { + lastTimeBytes := b.Get([]byte("lastMessageTimeKey")) // 确保使用正确的键 + if lastTimeBytes == nil { + return 0 // 如果键不存在或值为空,直接返回0 + } + + // 将字节切片转换为字符串,并尝试按空格分割 + splitResult := strings.Split(string(lastTimeBytes), " ") + if len(splitResult) < 2 { + return 0 // 如果没有足够的分割结果,返回0 + } + + // 安全地解析时间 + lastTime, err := time.Parse(time.RFC3339, splitResult[1]) + if err != nil { + return 0 // 如果解析时间出错,返回0 + } + + return lastTime.Unix() // 返回Unix时间戳 +} + +func CloseDB() { + db.Close() +} diff --git a/config/config.go b/config/config.go index d10feda5..a3420674 100644 --- a/config/config.go +++ b/config/config.go @@ -111,7 +111,10 @@ type Settings struct { BlackPrefixs []string `yaml:"black_prefixs"` Alias []string `yaml:"alias"` Enters []string `yaml:"enters"` + EntersExcept []string `yaml:"enters_except"` VisualPrefixs []VisualPrefixConfig `yaml:"visual_prefixs"` + AutoWithdraw []string `yaml:"auto_withdraw"` + AutoWithdrawTime int `yaml:"auto_withdraw_time"` //开发增强类 DevlopAcDir string `yaml:"develop_access_token_dir"` DevBotid string `yaml:"develop_bot_id"` @@ -1955,6 +1958,16 @@ func GetEnters() []string { return nil // 返回nil,如果instance为nil } +// 获取EntersExcept +func GetEntersExcept() []string { + mu.Lock() + defer mu.Unlock() + if instance != nil { + return instance.Settings.EntersExcept + } + return nil // 返回nil,如果instance为nil +} + // 获取 LinkPrefix func GetLinkPrefix() string { mu.Lock() @@ -2216,3 +2229,27 @@ func GetUploadPicV2Base64() bool { } return instance.Settings.UploadPicV2Base64 } + +// 获取 AutoWithdraw 数组 +func GetAutoWithdraw() []string { + mu.Lock() + defer mu.Unlock() + + if instance == nil { + mylog.Println("Warning: instance is nil when trying to get AutoWithdraw.") + return nil + } + return instance.Settings.AutoWithdraw +} + +// 获取 GetAutoWithdrawTime 数量 +func GetAutoWithdrawTime() int { + mu.Lock() + defer mu.Unlock() + + if instance == nil { + mylog.Println("Warning: instance is nil when trying to get AutoWithdrawTime.") + return 0 + } + return instance.Settings.AutoWithdrawTime +} diff --git a/handlers/get_status.go b/handlers/get_status.go index b668d3e6..86483276 100644 --- a/handlers/get_status.go +++ b/handlers/get_status.go @@ -3,6 +3,7 @@ package handlers import ( "encoding/json" + "github.com/hoshinonyaruko/gensokyo/botstats" "github.com/hoshinonyaruko/gensokyo/callapi" "github.com/hoshinonyaruko/gensokyo/mylog" "github.com/tencent-connect/botgo/openapi" @@ -30,8 +31,8 @@ type Statistics struct { PacketReceived uint64 `json:"packet_received"` PacketSent uint64 `json:"packet_sent"` PacketLost uint32 `json:"packet_lost"` - MessageReceived uint64 `json:"message_received"` - MessageSent uint64 `json:"message_sent"` + MessageReceived int `json:"message_received"` + MessageSent int `json:"message_sent"` DisconnectTimes uint32 `json:"disconnect_times"` LostTimes uint32 `json:"lost_times"` LastMessageTime int64 `json:"last_message_time"` @@ -45,6 +46,10 @@ func GetStatus(client callapi.Client, api openapi.OpenAPI, apiv2 openapi.OpenAPI var response GetStatusResponse + messageReceived, messageSent, lastMessageTime, err := botstats.GetStats() + if err != nil { + mylog.Printf("get_status错误,获取机器人发信状态错误:%v", err) + } response.Data = StatusData{ AppInitialized: true, AppEnabled: true, @@ -53,14 +58,14 @@ func GetStatus(client callapi.Client, api openapi.OpenAPI, apiv2 openapi.OpenAPI Online: true, //测试数据 Good: true, //测试数据 Stat: Statistics{ - PacketReceived: 1000, //测试数据 - PacketSent: 950, //测试数据 - PacketLost: 50, //测试数据 - MessageReceived: 500, //测试数据 - MessageSent: 490, //测试数据 - DisconnectTimes: 5, //测试数据 - LostTimes: 2, //测试数据 - LastMessageTime: 1677721600, //测试数据 + PacketReceived: 1000, //测试数据 + PacketSent: 950, //测试数据 + PacketLost: 50, //测试数据 + MessageReceived: messageReceived, //实际数据 + MessageSent: messageSent, //实际数据 + DisconnectTimes: 5, //测试数据 + LostTimes: 2, //测试数据 + LastMessageTime: lastMessageTime, //实际数据 }, } response.Message = "" @@ -72,7 +77,7 @@ func GetStatus(client callapi.Client, api openapi.OpenAPI, apiv2 openapi.OpenAPI mylog.Printf("get_status: %+v\n", outputMap) - err := client.SendMessage(outputMap) + err = client.SendMessage(outputMap) if err != nil { mylog.Printf("Error sending message via client: %v", err) } diff --git a/handlers/message_parser.go b/handlers/message_parser.go index 20884de2..5fcae8c3 100644 --- a/handlers/message_parser.go +++ b/handlers/message_parser.go @@ -17,6 +17,7 @@ import ( "strings" "time" + "github.com/hoshinonyaruko/gensokyo/botstats" "github.com/hoshinonyaruko/gensokyo/callapi" "github.com/hoshinonyaruko/gensokyo/config" "github.com/hoshinonyaruko/gensokyo/echo" @@ -50,10 +51,96 @@ type ServerResponse struct { Echo interface{} `json:"echo"` } +// 定义了一个符合 Client 接口的 HttpAPIClient 结构体 +type HttpAPIClient struct { + // 可添加所需字段 +} + +// 实现 Client 接口的 SendMessage 方法 +// 假client中不执行任何操作,只是返回 nil 来符合接口要求 +func (c *HttpAPIClient) SendMessage(message map[string]interface{}) error { + // 不实际发送消息 + // log.Printf("SendMessage called with: %v", message) + + // 返回nil占位符 + return nil +} + // 发送成功回执 todo 返回可互转的messageid 实现群撤回api -func SendResponse(client callapi.Client, err error, message *callapi.ActionMessage, resp *dto.GroupMessageResponse) (string, error) { +func SendResponse(client callapi.Client, err error, message *callapi.ActionMessage, resp *dto.GroupMessageResponse, api openapi.OpenAPI, apiv2 openapi.OpenAPI) (string, error) { var messageID64 int64 var mapErr error + + // 转换群号 + var errr error + var GroupID64 int64 + if groupID, ok := message.Params.GroupID.(string); ok && groupID != "" { + if config.GetIdmapPro() { + //将真实id转为int userid64 + GroupID64, _, errr = idmap.StoreIDv2Pro(message.Params.GroupID.(string), message.Params.UserID.(string)) + if errr != nil { + mylog.Fatalf("Error storing ID: %v", err) + } + } else { + // 映射str的GroupID到int + GroupID64, errr = idmap.StoreIDv2(message.Params.GroupID.(string)) + if errr != nil { + mylog.Errorf("failed to convert GroupID64 to int: %v", err) + } + } + } + + var channelID64 int64 + if channelID, ok := message.Params.ChannelID.(string); ok && channelID != "" { + if config.GetIdmapPro() { + //将真实id转为int userid64 + channelID64, _, errr = idmap.StoreIDv2Pro(message.Params.ChannelID.(string), message.Params.UserID.(string)) + if errr != nil { + mylog.Fatalf("Error storing ID: %v", err) + } + } else { + // 映射str的GroupID到int + channelID64, errr = idmap.StoreIDv2(message.Params.ChannelID.(string)) + if errr != nil { + mylog.Errorf("failed to convert GroupID64 to int: %v", err) + } + } + } + + var guildID64 int64 + if guildID, ok := message.Params.GuildID.(string); ok && guildID != "" { + if config.GetIdmapPro() { + //将真实id转为int userid64 + guildID64, _, errr = idmap.StoreIDv2Pro(message.Params.GuildID.(string), message.Params.UserID.(string)) + if errr != nil { + mylog.Fatalf("Error storing ID: %v", err) + } + } else { + // 映射str的GroupID到int + guildID64, errr = idmap.StoreIDv2(message.Params.GuildID.(string)) + if errr != nil { + mylog.Errorf("failed to convert GroupID64 to int: %v", err) + } + } + } + + var userID64 int64 + if userID, ok := message.Params.UserID.(string); ok && userID != "" { + if config.GetIdmapPro() { + //将真实id转为int userid64 + userID64, _, errr = idmap.StoreIDv2Pro("group_private", message.Params.UserID.(string)) + if errr != nil { + mylog.Fatalf("Error storing ID: %v", err) + } + } else { + // 映射str的GroupID到int + userID64, errr = idmap.StoreIDv2(message.Params.UserID.(string)) + if errr != nil { + mylog.Errorf("failed to convert GroupID64 to int: %v", err) + } + } + } + // 设置响应值 response := ServerResponse{} if resp != nil { @@ -63,27 +150,60 @@ func SendResponse(client callapi.Client, err error, message *callapi.ActionMessa return "", nil } response.Data.MessageID = int(messageID64) + // 发送成功 增加今日发信息数 + botstats.RecordMessageSent() + // 是否自动撤回 + if echoStr, ok := message.Echo.(string); ok { + msg_on_touch := echo.GetMsgIDv3(config.GetAppIDStr(), echoStr) + // 检查是否需要自动撤回 + autoWithdrawPrefixes := config.GetAutoWithdraw() + if len(autoWithdrawPrefixes) > 0 { + for _, prefix := range autoWithdrawPrefixes { + if strings.HasPrefix(msg_on_touch, prefix) { + go func() { + delay := config.GetAutoWithdrawTime() // 获取延迟时间 + time.Sleep(time.Duration(delay) * time.Second) + + // 构建参数 + var params callapi.ParamsContent + if groupID, ok := message.Params.GroupID.(string); ok && groupID != "" { + params.GroupID = strconv.FormatInt(GroupID64, 10) + } else if channelID, ok := message.Params.ChannelID.(string); ok && channelID != "" { + params.ChannelID = strconv.FormatInt(channelID64, 10) + } else if guildID, ok := message.Params.GuildID.(string); ok && guildID != "" { + params.GuildID = strconv.FormatInt(guildID64, 10) + } else if userID, ok := message.Params.UserID.(string); ok && userID != "" { + params.UserID = strconv.FormatInt(userID64, 10) + } else { + return // 如果没有有效的参数,则退出 + } + params.MessageID = strconv.FormatInt(messageID64, 10) + + // 创建撤回消息的请求 + deleteMessage := callapi.ActionMessage{ + Action: "delete_msg", + Params: params, + } + + // 调用删除消息函数 + client := &HttpAPIClient{} + _, err := DeleteMsg(client, api, apiv2, deleteMessage) + if err != nil { + mylog.Printf("Error DeleteMsg: %v", err) + } + }() + break + } + } + } + } } else { // Default ID handling response.Data.MessageID = 123 } - var errr error - var GroupID64 int64 - if config.GetIdmapPro() { - //将真实id转为int userid64 - GroupID64, _, errr = idmap.StoreIDv2Pro(message.Params.GroupID.(string), message.Params.UserID.(string)) - if errr != nil { - mylog.Fatalf("Error storing ID: %v", err) - } - } else { - // 映射str的GroupID到int - GroupID64, errr = idmap.StoreIDv2(message.Params.GroupID.(string)) - if errr != nil { - mylog.Errorf("failed to convert GroupID64 to int: %v", err) - } - } //mylog.Printf("convert GroupID64 to int: %v", GroupID64) 测试 + // TODO: 改为动态参数 不是固定GroupID 但应用端不支持.会报错.暂时统一从group取id,自己判断类型发撤回请求. response.GroupID = GroupID64 response.Echo = message.Echo if err != nil { diff --git a/handlers/send_group_forward_msg.go b/handlers/send_group_forward_msg.go index 6795e931..f89fa9f0 100644 --- a/handlers/send_group_forward_msg.go +++ b/handlers/send_group_forward_msg.go @@ -66,6 +66,6 @@ func HandleSendGroupForwardMsg(client callapi.Client, api openapi.OpenAPI, apiv2 count++ time.Sleep(500 * time.Millisecond) // 每条消息之间的延时 } - retmsg, _ = SendResponse(client, nil, &message, nil) + retmsg, _ = SendResponse(client, nil, &message, nil, api, apiv2) return retmsg, nil } diff --git a/handlers/send_group_msg.go b/handlers/send_group_msg.go index 23c5d30d..b99b26a0 100644 --- a/handlers/send_group_msg.go +++ b/handlers/send_group_msg.go @@ -313,7 +313,7 @@ func HandleSendGroupMsg(client callapi.Client, api openapi.OpenAPI, apiv2 openap } // 发送成功回执 - retmsg, _ = SendResponse(client, err, &message, resp) + retmsg, _ = SendResponse(client, err, &message, resp, api, apiv2) delete(foundItems, imageType) // 从foundItems中删除已处理的图片项 messageText = "" @@ -346,7 +346,7 @@ func HandleSendGroupMsg(client callapi.Client, api openapi.OpenAPI, apiv2 openap echo.PushGlobalStack(pair) } //发送成功回执 - retmsg, _ = SendResponse(client, err, &message, resp) + retmsg, _ = SendResponse(client, err, &message, resp, api, apiv2) } var resp *dto.GroupMessageResponse // 遍历foundItems并发送每种信息 @@ -382,7 +382,7 @@ func HandleSendGroupMsg(client callapi.Client, api openapi.OpenAPI, apiv2 openap echo.PushGlobalStack(pair) } //发送成功回执 - retmsg, _ = SendResponse(client, err, &message, resp) + retmsg, _ = SendResponse(client, err, &message, resp, api, apiv2) } continue // 跳过这个项,继续下一个 } @@ -442,7 +442,7 @@ func HandleSendGroupMsg(client callapi.Client, api openapi.OpenAPI, apiv2 openap } } //发送成功回执 - retmsg, _ = SendResponse(client, err, &message, resp) + retmsg, _ = SendResponse(client, err, &message, resp, api, apiv2) } } case "guild": @@ -1278,6 +1278,10 @@ func auto_md(message callapi.ActionMessage, messageText string, richMediaMessage var actiondata string //检查是否设置了enter数组 enter := checkDataLabelPrefix(dataLabel) + //例外规则 + if checkDataLabelPrefixExcept(whiteLabel) { + enter = false + } switch { case strings.HasPrefix(whiteLabel, "邀请机器人"): //默认是群 @@ -1419,3 +1423,14 @@ func checkDataLabelPrefix(dataLabel string) bool { } return false } + +// 检查whiteLabel是否以config中getentersexcept返回的任一字符串开头 +func checkDataLabelPrefixExcept(whiteLabel string) bool { + enters := config.GetEntersExcept() + for _, enter := range enters { + if enter != "" && strings.HasPrefix(whiteLabel, enter) { + return true + } + } + return false +} diff --git a/handlers/send_group_msg_raw.go b/handlers/send_group_msg_raw.go index f08ac062..893839cf 100644 --- a/handlers/send_group_msg_raw.go +++ b/handlers/send_group_msg_raw.go @@ -273,7 +273,7 @@ func HandleSendGroupMsgRaw(client callapi.Client, api openapi.OpenAPI, apiv2 ope } // 发送成功回执 - retmsg, _ = SendResponse(client, err, &message, resp) + retmsg, _ = SendResponse(client, err, &message, resp, api, apiv2) delete(foundItems, imageType) // 从foundItems中删除已处理的图片项 messageText = "" @@ -306,7 +306,7 @@ func HandleSendGroupMsgRaw(client callapi.Client, api openapi.OpenAPI, apiv2 ope echo.PushGlobalStack(pair) } //发送成功回执 - retmsg, _ = SendResponse(client, err, &message, resp) + retmsg, _ = SendResponse(client, err, &message, resp, api, apiv2) } var resp *dto.GroupMessageResponse // 遍历foundItems并发送每种信息 @@ -342,7 +342,7 @@ func HandleSendGroupMsgRaw(client callapi.Client, api openapi.OpenAPI, apiv2 ope echo.PushGlobalStack(pair) } //发送成功回执 - retmsg, _ = SendResponse(client, err, &message, resp) + retmsg, _ = SendResponse(client, err, &message, resp, api, apiv2) } continue // 跳过这个项,继续下一个 } @@ -402,7 +402,7 @@ func HandleSendGroupMsgRaw(client callapi.Client, api openapi.OpenAPI, apiv2 ope } } //发送成功回执 - retmsg, _ = SendResponse(client, err, &message, resp) + retmsg, _ = SendResponse(client, err, &message, resp, api, apiv2) } } case "guild": diff --git a/handlers/send_guild_channel_forum.go b/handlers/send_guild_channel_forum.go index fd686c48..b4ed7720 100644 --- a/handlers/send_guild_channel_forum.go +++ b/handlers/send_guild_channel_forum.go @@ -101,7 +101,7 @@ func HandleSendGuildChannelForum(client callapi.Client, api openapi.OpenAPI, api } //发送成功回执 - retmsg, _ = SendResponse(client, err, &message, nil) + retmsg, _ = SendResponse(client, err, &message, nil, api, apiv2) default: mylog.Printf("2Unknown message type: %s", msgType) diff --git a/httpapi/httpapi.go b/httpapi/httpapi.go index 96dd7dc9..9bbb8642 100644 --- a/httpapi/httpapi.go +++ b/httpapi/httpapi.go @@ -8,10 +8,10 @@ import ( "strings" "github.com/hoshinonyaruko/gensokyo/config" + "github.com/hoshinonyaruko/gensokyo/handlers" "github.com/gin-gonic/gin" "github.com/hoshinonyaruko/gensokyo/callapi" - "github.com/hoshinonyaruko/gensokyo/handlers" "github.com/tencent-connect/botgo/openapi" ) diff --git a/main.go b/main.go index 6dd281e0..11258e74 100644 --- a/main.go +++ b/main.go @@ -17,6 +17,7 @@ import ( "github.com/fatih/color" "github.com/hoshinonyaruko/gensokyo/Processor" + "github.com/hoshinonyaruko/gensokyo/botstats" "github.com/hoshinonyaruko/gensokyo/config" "github.com/hoshinonyaruko/gensokyo/handlers" "github.com/hoshinonyaruko/gensokyo/httpapi" @@ -298,9 +299,14 @@ func main() { //创建idmap服务器 数据库 idmap.InitializeDB() + //创建botstats数据库 + botstats.InitializeDB() //创建webui数据库 webui.InitializeDB() + + //关闭时候释放数据库 defer idmap.CloseDB() + defer botstats.CloseDB() defer webui.CloseDB() //图片上传 调用次数限制 @@ -519,6 +525,7 @@ func ErrorNotifyHandler() event.ErrorNotifyHandler { // ATMessageEventHandler 实现处理 频道at 消息的回调 func ATMessageEventHandler() event.ATMessageEventHandler { return func(event *dto.WSPayload, data *dto.WSATMessageData) error { + botstats.RecordMessageReceived() return p.ProcessGuildATMessage(data) } } @@ -550,6 +557,7 @@ func MemberEventHandler() event.GuildMemberEventHandler { // DirectMessageHandler 处理私信事件 func DirectMessageHandler() event.DirectMessageEventHandler { return func(event *dto.WSPayload, data *dto.WSDirectMessageData) error { + botstats.RecordMessageReceived() return p.ProcessChannelDirectMessage(data) } } @@ -557,6 +565,7 @@ func DirectMessageHandler() event.DirectMessageEventHandler { // CreateMessageHandler 处理消息事件 私域的事件 不at信息 func CreateMessageHandler() event.MessageEventHandler { return func(event *dto.WSPayload, data *dto.WSMessageData) error { + botstats.RecordMessageReceived() return p.ProcessGuildNormalMessage(data) } } @@ -580,6 +589,7 @@ func ThreadEventHandler() event.ThreadEventHandler { // GroupATMessageEventHandler 实现处理 群at 消息的回调 func GroupATMessageEventHandler() event.GroupATMessageEventHandler { return func(event *dto.WSPayload, data *dto.WSGroupATMessageData) error { + botstats.RecordMessageReceived() return p.ProcessGroupMessage(data) } } @@ -587,6 +597,7 @@ func GroupATMessageEventHandler() event.GroupATMessageEventHandler { // C2CMessageEventHandler 实现处理 群私聊 消息的回调 func C2CMessageEventHandler() event.C2CMessageEventHandler { return func(event *dto.WSPayload, data *dto.WSC2CMessageData) error { + botstats.RecordMessageReceived() return p.ProcessC2CMessage(data) } } diff --git a/template/config_template.go b/template/config_template.go index 76c9fc7f..10cbda4e 100644 --- a/template/config_template.go +++ b/template/config_template.go @@ -7,7 +7,7 @@ settings: ws_address: ["ws://:"] # WebSocket服务的地址 支持多个["","",""] ws_token: ["","",""] #连接wss地址时服务器所需的token,按顺序一一对应,如果是ws地址,没有密钥,请留空. reconnect_times : 100 #反向ws连接失败后的重试次数,希望一直重试,可设置9999 - heart_beat_interval : 10 #反向ws心跳间隔 单位秒 推荐5-10 + heart_beat_interval : 5 #反向ws心跳间隔 单位秒 推荐5-10 launch_reconnect_times : 1 #启动时尝试反向ws连接次数,建议先打开应用端再开启gensokyo,因为启动时连接会阻塞webui启动,默认只连接一次,可自行增大 #基础设置 @@ -107,6 +107,9 @@ settings: black_prefixs : [""] #可设置多个 比如设置 查询 则查询开头的信息均被拦截 防止审核失败 alias : ["",""] #两两成对,指令替换,"a","b","c","d"代表将a开头替换为b开头,c开头替换为d开头. enters : ["",""] #自动md卡片点击直接触发,小众功能,满足以下条件:应用端支持双向echo+设置了visual_prefixs和whiteList + enters_except : ["",""] #自动md卡片点击直接触发,例外,对子按钮生效. + auto_withdraw : [] #仅当应用端实现了双向echo可用.实现不难,可以去找对应开发者去提需求. + auto_withdraw_time : 30 #30秒 visual_prefixs : #虚拟前缀 与white_prefixs配合使用 处理流程自动忽略该前缀 remove_prefix remove_at 需为true时生效 - prefix: "" #虚拟前缀开头 例 你有3个指令 帮助 测试 查询 将 prefix 设置为 工具类 后 则可通过 工具类 帮助 触发机器人 diff --git a/wsclient/ws.go b/wsclient/ws.go index 5227ffa2..318c8350 100644 --- a/wsclient/ws.go +++ b/wsclient/ws.go @@ -10,6 +10,7 @@ import ( "time" "github.com/gorilla/websocket" + "github.com/hoshinonyaruko/gensokyo/botstats" "github.com/hoshinonyaruko/gensokyo/callapi" "github.com/hoshinonyaruko/gensokyo/config" "github.com/hoshinonyaruko/gensokyo/mylog" @@ -214,6 +215,10 @@ func (client *WebSocketClient) sendHeartbeat(ctx context.Context, botID uint64, case <-ctx.Done(): return case <-time.After(time.Duration(heartbeatinterval) * time.Second): + messageReceived, messageSent, lastMessageTime, err := botstats.GetStats() + if err != nil { + mylog.Printf("心跳错误,获取机器人发信状态错误:%v", err) + } message := map[string]interface{}{ "post_type": "meta_event", "meta_event_type": "heartbeat", @@ -230,14 +235,14 @@ func (client *WebSocketClient) sendHeartbeat(ctx context.Context, botID uint64, "packet_received": 34933, "packet_sent": 8513, "packet_lost": 0, - "message_received": 24674, - "message_sent": 1663, + "message_received": messageReceived, + "message_sent": messageSent, "disconnect_times": 0, "lost_times": 0, - "last_message_time": int(time.Now().Unix()) - 10, // 假设最后一条消息是10秒前收到的 + "last_message_time": int(lastMessageTime), }, }, - "interval": 10000, // 以毫秒为单位 + "interval": 5000, // 以毫秒为单位 } client.SendMessage(message) // 重发失败的消息 From a9afc91f349b545729a2fa28938e671d8a08f52c Mon Sep 17 00:00:00 2001 From: cosmo Date: Tue, 16 Apr 2024 20:30:57 +0800 Subject: [PATCH 26/33] beta361 --- handlers/message_parser.go | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/handlers/message_parser.go b/handlers/message_parser.go index 5fcae8c3..c9f67183 100644 --- a/handlers/message_parser.go +++ b/handlers/message_parser.go @@ -250,6 +250,8 @@ func SendGuildResponse(client callapi.Client, err error, message *callapi.Action return "", nil } response.Data.MessageID = int(messageID64) + // 发送成功 增加今日发信息数 + botstats.RecordMessageSent() } else { // Default ID handling response.Data.MessageID = 123 @@ -306,6 +308,8 @@ func SendC2CResponse(client callapi.Client, err error, message *callapi.ActionMe return "", nil } response.Data.MessageID = int(messageID64) + // 发送成功 增加今日发信息数 + botstats.RecordMessageSent() } else { // Default ID handling response.Data.MessageID = 123 From 16cef02a503517ae070bc5a2260362c888e3809e Mon Sep 17 00:00:00 2001 From: cosmo Date: Tue, 16 Apr 2024 21:10:30 +0800 Subject: [PATCH 27/33] beta362 --- botstats/botstats.go | 13 ++++--------- 1 file changed, 4 insertions(+), 9 deletions(-) diff --git a/botstats/botstats.go b/botstats/botstats.go index b4af3c47..c62b376d 100644 --- a/botstats/botstats.go +++ b/botstats/botstats.go @@ -61,7 +61,8 @@ func recordStats(receivedIncrement int, sentIncrement int) { updateCounter(b, messageReceivedKey, receivedIncrement) updateCounter(b, messageSentKey, sentIncrement) - b.Put([]byte(lastMessageTimeKey), []byte(today+" "+now.Format(time.RFC3339))) + // Ensure the time format is RFC3339 and only store date and time + b.Put([]byte(lastMessageTimeKey), []byte(now.Format(time.RFC3339))) return nil }) @@ -98,19 +99,13 @@ func getInt(b *bbolt.Bucket, key string) int { } func getLastMessageTime(b *bbolt.Bucket) int64 { - lastTimeBytes := b.Get([]byte("lastMessageTimeKey")) // 确保使用正确的键 + lastTimeBytes := b.Get([]byte("lastMessageTime")) // 确保使用正确的键 if lastTimeBytes == nil { return 0 // 如果键不存在或值为空,直接返回0 } - // 将字节切片转换为字符串,并尝试按空格分割 - splitResult := strings.Split(string(lastTimeBytes), " ") - if len(splitResult) < 2 { - return 0 // 如果没有足够的分割结果,返回0 - } - // 安全地解析时间 - lastTime, err := time.Parse(time.RFC3339, splitResult[1]) + lastTime, err := time.Parse(time.RFC3339, string(lastTimeBytes)) if err != nil { return 0 // 如果解析时间出错,返回0 } From 48aa4c7e353c87d90b7d06434cf5d064607a5532 Mon Sep 17 00:00:00 2001 From: cosmo Date: Wed, 17 Apr 2024 18:44:51 +0800 Subject: [PATCH 28/33] beta363 --- botstats/botstats.go | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/botstats/botstats.go b/botstats/botstats.go index c62b376d..23429e05 100644 --- a/botstats/botstats.go +++ b/botstats/botstats.go @@ -1,11 +1,14 @@ package botstats import ( + "errors" + "fmt" "log" "strconv" "strings" "time" + "github.com/hoshinonyaruko/gensokyo/mylog" "go.etcd.io/bbolt" ) @@ -47,6 +50,10 @@ func RecordMessageSent() { // 收到增量 发出增量 func recordStats(receivedIncrement int, sentIncrement int) { + if db == nil { + mylog.Printf("recordStats db is nil") + return + } db.Update(func(tx *bbolt.Tx) error { b := tx.Bucket([]byte(bucketName)) now := time.Now() @@ -82,14 +89,23 @@ func updateCounter(b *bbolt.Bucket, key string, increment int) { func GetStats() (int, int, int64, error) { var messageReceived, messageSent int var lastMessageTime int64 + if db == nil { + return 0, 0, 0, errors.New("database is not initialized") + } err := db.View(func(tx *bbolt.Tx) error { b := tx.Bucket([]byte(bucketName)) + if b == nil { + return fmt.Errorf("bucket %s not found", bucketName) + } messageReceived = getInt(b, messageReceivedKey) messageSent = getInt(b, messageSentKey) lastMessageTime = getLastMessageTime(b) return nil }) - return messageReceived, messageSent, lastMessageTime, err + if err != nil { + return 0, 0, 0, err + } + return messageReceived, messageSent, lastMessageTime, nil } func getInt(b *bbolt.Bucket, key string) int { From f30fe85605c6d36ed44cf9d533bb0989ac726bc5 Mon Sep 17 00:00:00 2001 From: cosmo Date: Thu, 18 Apr 2024 11:51:02 +0800 Subject: [PATCH 29/33] beta365 --- Processor/ProcessC2CMessage.go | 11 ++++++++--- Processor/ProcessChannelDirectMessage.go | 23 +++++++++++++++-------- Processor/ProcessGroupMessage.go | 8 ++++++-- Processor/ProcessGuildATMessage.go | 17 +++++++++++++---- Processor/ProcessGuildNormalMessage.go | 12 ++++++++---- Processor/ProcessThreadMessage.go | 18 ++++++++++++------ 6 files changed, 62 insertions(+), 27 deletions(-) diff --git a/Processor/ProcessC2CMessage.go b/Processor/ProcessC2CMessage.go index 465ae443..d23c6b29 100644 --- a/Processor/ProcessC2CMessage.go +++ b/Processor/ProcessC2CMessage.go @@ -34,7 +34,10 @@ func (p *Processors) ProcessC2CMessage(data *dto.WSC2CMessageData) error { //转换appidstring AppIDString := strconv.FormatUint(p.Settings.AppID, 10) - echostr := AppIDString + "_" + strconv.FormatInt(s, 10) + // 获取当前时间的13位毫秒级时间戳 + currentTimeMillis := time.Now().UnixNano() / 1e6 + // 构造echostr,包括AppID,原始的s变量和当前时间戳 + echostr := fmt.Sprintf("%s_%d_%d", AppIDString, s, currentTimeMillis) var userid64 int64 var err error if config.GetIdmapPro() { @@ -148,8 +151,10 @@ func (p *Processors) ProcessC2CMessage(data *dto.WSC2CMessageData) error { //转换appid AppIDString := strconv.FormatUint(p.Settings.AppID, 10) - //构造echo - echostr := AppIDString + "_" + strconv.FormatInt(s, 10) + // 获取当前时间的13位毫秒级时间戳 + currentTimeMillis := time.Now().UnixNano() / 1e6 + // 构造echostr,包括AppID,原始的s变量和当前时间戳 + echostr := fmt.Sprintf("%s_%d_%d", AppIDString, s, currentTimeMillis) //把userid作为群号 //映射str的userid到int var userid64 int64 diff --git a/Processor/ProcessChannelDirectMessage.go b/Processor/ProcessChannelDirectMessage.go index e27faa94..6c705e33 100644 --- a/Processor/ProcessChannelDirectMessage.go +++ b/Processor/ProcessChannelDirectMessage.go @@ -35,7 +35,10 @@ func (p *Processors) ProcessChannelDirectMessage(data *dto.WSDirectMessageData) //转换appidstring AppIDString := strconv.FormatUint(p.Settings.AppID, 10) - echostr := AppIDString + "_" + strconv.FormatInt(s, 10) + // 获取当前时间的13位毫秒级时间戳 + currentTimeMillis := time.Now().UnixNano() / 1e6 + // 构造echostr,包括AppID,原始的s变量和当前时间戳 + echostr := fmt.Sprintf("%s_%d_%d", AppIDString, s, currentTimeMillis) var userid64 int64 var ChannelID64 int64 @@ -183,8 +186,10 @@ func (p *Processors) ProcessChannelDirectMessage(data *dto.WSDirectMessageData) p.HandleFrameworkCommand(messageText, data, "guild_private") //转换appid AppIDString := strconv.FormatUint(p.Settings.AppID, 10) - //构造echo - echostr := AppIDString + "_" + strconv.FormatInt(s, 10) + // 获取当前时间的13位毫秒级时间戳 + currentTimeMillis := time.Now().UnixNano() / 1e6 + // 构造echostr,包括AppID,原始的s变量和当前时间戳 + echostr := fmt.Sprintf("%s_%d_%d", AppIDString, s, currentTimeMillis) //映射str的userid到int userid64, err := idmap.StoreIDv2(data.Author.ID) if err != nil { @@ -271,16 +276,16 @@ func (p *Processors) ProcessChannelDirectMessage(data *dto.WSDirectMessageData) var err error if config.GetIdmapPro() { //将真实id转为int userid64 - _, _, err = idmap.StoreIDv2Pro(data.ChannelID, data.Author.ID) + ChannelID64, userid64, err = idmap.StoreIDv2Pro(data.ChannelID, data.Author.ID) if err != nil { mylog.Fatalf("Error storing ID: %v", err) } //将真实id转为int userid64 - userid64, err = idmap.StoreIDv2(data.Author.ID) + _, err = idmap.StoreIDv2(data.Author.ID) if err != nil { mylog.Fatalf("Error storing ID: %v", err) } - ChannelID64, err = idmap.StoreIDv2(data.ChannelID) + _, err = idmap.StoreIDv2(data.ChannelID) if err != nil { mylog.Printf("Error storing ID: %v", err) return nil @@ -319,8 +324,10 @@ func (p *Processors) ProcessChannelDirectMessage(data *dto.WSDirectMessageData) p.HandleFrameworkCommand(messageText, data, "guild_private") //转换appid AppIDString := strconv.FormatUint(p.Settings.AppID, 10) - //构造echo - echostr := AppIDString + "_" + strconv.FormatInt(s, 10) + // 获取当前时间的13位毫秒级时间戳 + currentTimeMillis := time.Now().UnixNano() / 1e6 + // 构造echostr,包括AppID,原始的s变量和当前时间戳 + echostr := fmt.Sprintf("%s_%d_%d", AppIDString, s, currentTimeMillis) //userid := int(userid64) //映射str的messageID到int diff --git a/Processor/ProcessGroupMessage.go b/Processor/ProcessGroupMessage.go index 620c8c5f..4631ab20 100644 --- a/Processor/ProcessGroupMessage.go +++ b/Processor/ProcessGroupMessage.go @@ -24,8 +24,12 @@ func (p *Processors) ProcessGroupMessage(data *dto.WSGroupATMessageData) error { // 转换appid AppIDString := strconv.FormatUint(p.Settings.AppID, 10) - // 构造echo - echostr := AppIDString + "_" + strconv.FormatInt(s, 10) + // 获取当前时间的13位毫秒级时间戳 + currentTimeMillis := time.Now().UnixNano() / 1e6 + + // 构造echostr,包括AppID,原始的s变量和当前时间戳 + echostr := fmt.Sprintf("%s_%d_%d", AppIDString, s, currentTimeMillis) + var userid64 int64 var GroupID64 int64 var err error diff --git a/Processor/ProcessGuildATMessage.go b/Processor/ProcessGuildATMessage.go index 1ccf0bdb..563da6d0 100644 --- a/Processor/ProcessGuildATMessage.go +++ b/Processor/ProcessGuildATMessage.go @@ -37,8 +37,12 @@ func (p *Processors) ProcessGuildATMessage(data *dto.WSATMessageData) error { p.HandleFrameworkCommand(messageText, data, "guild") //转换appid AppIDString = strconv.FormatUint(p.Settings.AppID, 10) - //构造echo - echostr := AppIDString + "_" + strconv.FormatInt(s, 10) + + // 获取当前时间的13位毫秒级时间戳 + currentTimeMillis := time.Now().UnixNano() / 1e6 + // 构造echostr,包括AppID,原始的s变量和当前时间戳 + echostr := fmt.Sprintf("%s_%d_%d", AppIDString, s, currentTimeMillis) + //映射str的userid到int userid64, err := idmap.StoreIDv2(data.Author.ID) if err != nil { @@ -177,8 +181,13 @@ func (p *Processors) ProcessGuildATMessage(data *dto.WSATMessageData) error { p.HandleFrameworkCommand(messageText, data, "guild") //转换appid AppIDString := strconv.FormatUint(p.Settings.AppID, 10) - //构造echo - echostr := AppIDString + "_" + strconv.FormatInt(s, 10) + + // 获取当前时间的13位毫秒级时间戳 + currentTimeMillis := time.Now().UnixNano() / 1e6 + + // 构造echostr,包括AppID,原始的s变量和当前时间戳 + echostr := fmt.Sprintf("%s_%d_%d", AppIDString, s, currentTimeMillis) + //映射str的messageID到int messageID64, err := idmap.StoreIDv2(data.ID) if err != nil { diff --git a/Processor/ProcessGuildNormalMessage.go b/Processor/ProcessGuildNormalMessage.go index 06a787aa..f3f01942 100644 --- a/Processor/ProcessGuildNormalMessage.go +++ b/Processor/ProcessGuildNormalMessage.go @@ -36,8 +36,10 @@ func (p *Processors) ProcessGuildNormalMessage(data *dto.WSMessageData) error { p.HandleFrameworkCommand(messageText, data, "guild") //转换appid AppIDString = strconv.FormatUint(p.Settings.AppID, 10) - //构造echo - echostr := AppIDString + "_" + strconv.FormatInt(s, 10) + // 获取当前时间的13位毫秒级时间戳 + currentTimeMillis := time.Now().UnixNano() / 1e6 + // 构造echostr,包括AppID,原始的s变量和当前时间戳 + echostr := fmt.Sprintf("%s_%d_%d", AppIDString, s, currentTimeMillis) //映射str的userid到int userid64, err := idmap.StoreIDv2(data.Author.ID) if err != nil { @@ -177,8 +179,10 @@ func (p *Processors) ProcessGuildNormalMessage(data *dto.WSMessageData) error { p.HandleFrameworkCommand(messageText, data, "guild") //转换appid AppIDString := strconv.FormatUint(p.Settings.AppID, 10) - //构造echo - echostr := AppIDString + "_" + strconv.FormatInt(s, 10) + // 获取当前时间的13位毫秒级时间戳 + currentTimeMillis := time.Now().UnixNano() / 1e6 + // 构造echostr,包括AppID,原始的s变量和当前时间戳 + echostr := fmt.Sprintf("%s_%d_%d", AppIDString, s, currentTimeMillis) //映射str的messageID到int messageID64, err := idmap.StoreIDv2(data.ID) if err != nil { diff --git a/Processor/ProcessThreadMessage.go b/Processor/ProcessThreadMessage.go index c72934c0..9f61c27c 100644 --- a/Processor/ProcessThreadMessage.go +++ b/Processor/ProcessThreadMessage.go @@ -38,8 +38,10 @@ func (p *Processors) ProcessThreadMessage(data *dto.WSThreadData) error { //帖子不需要 //转换appid AppIDString := strconv.FormatUint(p.Settings.AppID, 10) - //构造echo - echostr := AppIDString + "_" + strconv.FormatInt(s, 10) + // 获取当前时间的13位毫秒级时间戳 + currentTimeMillis := time.Now().UnixNano() / 1e6 + // 构造echostr,包括AppID,原始的s变量和当前时间戳 + echostr := fmt.Sprintf("%s_%d_%d", AppIDString, s, currentTimeMillis) //映射str的userid到int userid64, err := idmap.StoreIDv2(data.AuthorID) if err != nil { @@ -147,8 +149,10 @@ func (p *Processors) ProcessThreadMessage(data *dto.WSThreadData) error { //帖子不需要 //转换appid AppIDString := strconv.FormatUint(p.Settings.AppID, 10) - //构造echo - echostr := AppIDString + "_" + strconv.FormatInt(s, 10) + // 获取当前时间的13位毫秒级时间戳 + currentTimeMillis := time.Now().UnixNano() / 1e6 + // 构造echostr,包括AppID,原始的s变量和当前时间戳 + echostr := fmt.Sprintf("%s_%d_%d", AppIDString, s, currentTimeMillis) //映射str的userid到int userid64, err := idmap.StoreIDv2(data.AuthorID) if err != nil { @@ -287,8 +291,10 @@ func (p *Processors) ProcessThreadMessage(data *dto.WSThreadData) error { if err != nil { mylog.Printf("Error parseContent Forum: %v", err) } - //构造echo - echostr := AppIDString + "_" + strconv.FormatInt(s, 10) + // 获取当前时间的13位毫秒级时间戳 + currentTimeMillis := time.Now().UnixNano() / 1e6 + // 构造echostr,包括AppID,原始的s变量和当前时间戳 + echostr := fmt.Sprintf("%s_%d_%d", AppIDString, s, currentTimeMillis) //映射str的messageID到int messageID64, err := idmap.StoreIDv2(data.ID) if err != nil { From a6c503dfe1c58ec73a4c2426df3a5df70701a14b Mon Sep 17 00:00:00 2001 From: cosmo Date: Fri, 19 Apr 2024 15:32:53 +0800 Subject: [PATCH 30/33] beta366 --- Processor/ProcessC2CMessage.go | 21 ++++++++ handlers/get_friend_list.go | 34 ++++++------ httpapi/httpapi.go | 27 ++++++++++ idmap/service.go | 95 +++++++++++++++++++++++++++++----- structs/structs.go | 7 +++ 5 files changed, 152 insertions(+), 32 deletions(-) create mode 100644 structs/structs.go diff --git a/Processor/ProcessC2CMessage.go b/Processor/ProcessC2CMessage.go index d23c6b29..69f69456 100644 --- a/Processor/ProcessC2CMessage.go +++ b/Processor/ProcessC2CMessage.go @@ -12,6 +12,7 @@ import ( "github.com/hoshinonyaruko/gensokyo/handlers" "github.com/hoshinonyaruko/gensokyo/idmap" "github.com/hoshinonyaruko/gensokyo/mylog" + "github.com/hoshinonyaruko/gensokyo/structs" "github.com/tencent-connect/botgo/dto" "github.com/tencent-connect/botgo/websocket/client" ) @@ -146,6 +147,15 @@ func (p *Processors) ProcessC2CMessage(data *dto.WSC2CMessageData) error { privateMsgMap := structToMap(privateMsg) //上报信息到onebotv11应用端(正反ws) p.BroadcastMessageToAll(privateMsgMap) + //组合FriendData + struserid := strconv.FormatInt(userid64, 10) + userdata := structs.FriendData{ + Nickname: "", + Remark: "", + UserID: struserid, + } + //缓存私信好友列表 + idmap.StoreUserInfo(data.Author.ID, userdata) } else { //将私聊信息转化为群信息(特殊需求情况下) @@ -267,6 +277,17 @@ func (p *Processors) ProcessC2CMessage(data *dto.WSC2CMessageData) error { groupMsgMap := structToMap(groupMsg) //上报信息到onebotv11应用端(正反ws) p.BroadcastMessageToAll(groupMsgMap) + + //组合FriendData + struserid := strconv.FormatInt(userid64, 10) + userdata := structs.FriendData{ + Nickname: "", + Remark: "", + UserID: struserid, + } + //缓存私信好友列表 + idmap.StoreUserInfo(data.Author.ID, userdata) } + return nil } diff --git a/handlers/get_friend_list.go b/handlers/get_friend_list.go index e21f4ac7..d11a6370 100644 --- a/handlers/get_friend_list.go +++ b/handlers/get_friend_list.go @@ -4,7 +4,9 @@ import ( "encoding/json" "github.com/hoshinonyaruko/gensokyo/callapi" + "github.com/hoshinonyaruko/gensokyo/idmap" "github.com/hoshinonyaruko/gensokyo/mylog" + "github.com/hoshinonyaruko/gensokyo/structs" "github.com/tencent-connect/botgo/openapi" ) @@ -13,31 +15,25 @@ func init() { } type APIOutput struct { - Data []FriendData `json:"data"` - Message string `json:"message"` - RetCode int `json:"retcode"` - Status string `json:"status"` - Echo interface{} `json:"echo"` -} - -type FriendData struct { - Nickname string `json:"nickname"` - Remark string `json:"remark"` - UserID string `json:"user_id"` + Data []structs.FriendData `json:"data"` + Message string `json:"message"` + RetCode int `json:"retcode"` + Status string `json:"status"` + Echo interface{} `json:"echo"` } func HandleGetFriendList(client callapi.Client, api openapi.OpenAPI, apiv2 openapi.OpenAPI, message callapi.ActionMessage) (string, error) { var output APIOutput - for i := 0; i < 10; i++ { // Assume we want to loop 10 times to create friend data - data := FriendData{ - Nickname: "小狐狸", - Remark: "", - UserID: "2022717137", - } - output.Data = append(output.Data, data) + // 从数据库获取所有用户信息 + users, err := idmap.ListAllUsers() + if err != nil { + mylog.Fatalf("Failed to list users: %v", err) } + // 添加数据库中读取的用户数据到output.Data + output.Data = append(output.Data, users...) + output.Message = "" output.RetCode = 0 output.Status = "ok" @@ -48,7 +44,7 @@ func HandleGetFriendList(client callapi.Client, api openapi.OpenAPI, apiv2 opena outputMap := structToMap(output) // Send the map - err := client.SendMessage(outputMap) //发回去 + err = client.SendMessage(outputMap) //发回去 if err != nil { mylog.Printf("error sending friend list via wsclient: %v", err) } diff --git a/httpapi/httpapi.go b/httpapi/httpapi.go index 9bbb8642..cd5fe1d7 100644 --- a/httpapi/httpapi.go +++ b/httpapi/httpapi.go @@ -52,6 +52,10 @@ func CombinedMiddleware(api openapi.OpenAPI, apiV2 openapi.OpenAPI) gin.HandlerF handleGetGroupList(c, api, apiV2) return } + if c.Request.URL.Path == "/get_friend_list" { + handleGetFriendList(c, api, apiV2) + return + } if c.Request.URL.Path == "/put_interaction" { handlePutInteraction(c, api, apiV2) return @@ -357,6 +361,29 @@ func handleGetGroupList(c *gin.Context, api openapi.OpenAPI, apiV2 openapi.OpenA c.String(http.StatusOK, retmsg) } +// handleGetFriendList 处理获取好友列表 +func handleGetFriendList(c *gin.Context, api openapi.OpenAPI, apiV2 openapi.OpenAPI) { + var retmsg string + + // 使用解析后的参数处理请求 + client := &HttpAPIClient{} + // 创建 ActionMessage 实例 + message := callapi.ActionMessage{ + Action: "get_friend_list", + } + + // 调用处理函数 + retmsg, err := handlers.HandleGetFriendList(client, api, apiV2, message) + if err != nil { + c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) + return + } + + // 返回处理结果 + c.Header("Content-Type", "application/json") + c.String(http.StatusOK, retmsg) +} + // handlePutInteraction 处理put_interaction的请求 func handlePutInteraction(c *gin.Context, api openapi.OpenAPI, apiV2 openapi.OpenAPI) { var req struct { diff --git a/idmap/service.go b/idmap/service.go index 69590765..c080f83f 100644 --- a/idmap/service.go +++ b/idmap/service.go @@ -18,6 +18,7 @@ import ( "github.com/hoshinonyaruko/gensokyo/config" "github.com/hoshinonyaruko/gensokyo/mylog" + "github.com/hoshinonyaruko/gensokyo/structs" "go.etcd.io/bbolt" ) @@ -29,10 +30,11 @@ var ( ) const ( - DBName = "idmap.db" - BucketName = "ids" - ConfigBucket = "config" - CounterKey = "currentRow" + DBName = "idmap.db" + BucketName = "ids" + ConfigBucket = "config" + UserInfoBucket = "UserInfo" + CounterKey = "currentRow" ) var db *bbolt.DB @@ -41,15 +43,32 @@ var ErrKeyNotFound = errors.New("key not found") func InitializeDB() { var err error + // 打开数据库文件 db, err = bbolt.Open(DBName, 0600, nil) if err != nil { log.Fatalf("Error opening DB: %v", err) } - db.Update(func(tx *bbolt.Tx) error { - _, err := tx.CreateBucketIfNotExists([]byte(BucketName)) - return err + // 在数据库中创建必要的buckets + err = db.Update(func(tx *bbolt.Tx) error { + // 创建默认的Bucket + if _, err := tx.CreateBucketIfNotExists([]byte(BucketName)); err != nil { + return err + } + // 创建存储用户信息的Bucket + if _, err := tx.CreateBucketIfNotExists([]byte(UserInfoBucket)); err != nil { + return err + } + // 创建配置数据的Bucket + if _, err := tx.CreateBucketIfNotExists([]byte(ConfigBucket)); err != nil { + return err + } + return nil }) + + if err != nil { + log.Fatalf("Error setting up buckets: %v", err) + } } func CloseDB() { @@ -530,19 +549,19 @@ func RetrieveRowByIDv2(rowid string) (string, error) { // 根据a 以b为类别 储存c func WriteConfig(sectionName, keyName, value string) error { return db.Update(func(tx *bbolt.Tx) error { - b, err := tx.CreateBucketIfNotExists([]byte(ConfigBucket)) - if err != nil { - mylog.Printf("Error creating or accessing bucket: %v", err) - return fmt.Errorf("failed to access or create bucket %s: %w", ConfigBucket, err) + b := tx.Bucket([]byte(ConfigBucket)) // 直接获取bucket + if b == nil { + mylog.Printf("Bucket %s not found", ConfigBucket) + return fmt.Errorf("bucket %s not found", ConfigBucket) } key := joinSectionAndKey(sectionName, keyName) - err = b.Put(key, []byte(value)) + err := b.Put(key, []byte(value)) if err != nil { mylog.Printf("Error putting data into bucket with key %s: %v", key, err) return fmt.Errorf("failed to put data into bucket with key %s: %w", key, err) } - //mylog.Printf("Data saved successfully with key %s,value %s", key, value) + //log.Printf("Data saved successfully with key %s, value %s", key, value) return nil }) } @@ -1311,3 +1330,53 @@ func UpdateKeysWithNewID(id, newID string) error { return nil }) } + +// StoreUserInfo 存储用户信息 +func StoreUserInfo(rawID string, userInfo structs.FriendData) error { + return db.Update(func(tx *bbolt.Tx) error { + b := tx.Bucket([]byte(UserInfoBucket)) + key := fmt.Sprintf("%s:%s", rawID, userInfo.UserID) // 创建复合键 + if v := b.Get([]byte(key)); v != nil { + return fmt.Errorf("duplicate key: %s", key) + } + + // 序列化用户信息作为值 + value, err := json.Marshal(userInfo) + if err != nil { + return fmt.Errorf("could not encode user info: %s", err) + } + + // 存储键值对 + if err := b.Put([]byte(key), value); err != nil { + return fmt.Errorf("could not store user info: %s", err) + } + return nil + }) +} + +// ListAllUsers 返回数据库中所有用户的信息 +func ListAllUsers() ([]structs.FriendData, error) { + var users []structs.FriendData + err := db.View(func(tx *bbolt.Tx) error { + b := tx.Bucket([]byte(UserInfoBucket)) + if b == nil { + return fmt.Errorf("bucket %s not found", UserInfoBucket) + } + + // 遍历bucket中的所有键值对 + err := b.ForEach(func(key, value []byte) error { + var user structs.FriendData + if err := json.Unmarshal(value, &user); err != nil { + log.Printf("Error unmarshaling user data: %v", err) + return err + } + users = append(users, user) + return nil + }) + return err + }) + if err != nil { + return nil, err + } + return users, nil +} diff --git a/structs/structs.go b/structs/structs.go new file mode 100644 index 00000000..9af3ed9a --- /dev/null +++ b/structs/structs.go @@ -0,0 +1,7 @@ +package structs + +type FriendData struct { + Nickname string `json:"nickname"` + Remark string `json:"remark"` + UserID string `json:"user_id"` +} From 4976b9af788c68002bf9a085041d29cda98f04e7 Mon Sep 17 00:00:00 2001 From: cosmo Date: Mon, 22 Apr 2024 13:44:19 +0800 Subject: [PATCH 31/33] beta367 --- handlers/message_parser.go | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/handlers/message_parser.go b/handlers/message_parser.go index c9f67183..dc2c388f 100644 --- a/handlers/message_parser.go +++ b/handlers/message_parser.go @@ -958,7 +958,11 @@ func RevertTransformedText(data interface{}, msgtype string, api openapi.OpenAPI //如果二级指令白名单全部是*(忽略自身,那么不判断二级白名单是否匹配) if allStarPrefixed { - matched = true + if len(messageText) == len(matchedPrefix.Prefix) { + matched = true + } else { + matched = false + } } else { // 遍历白名单数组,检查是否有匹配项 for _, prefix := range allPrefixes { @@ -975,6 +979,11 @@ func RevertTransformedText(data interface{}, msgtype string, api openapi.OpenAPI // 从trimmedPrefix中去除前后空格(可能会有bug) trimmedPrefix = strings.TrimSpace(trimmedPrefix) + if trimmedPrefix == "" { + matched = false + break + } + if strings.HasPrefix(messageText, trimmedPrefix) { matched = true break From f4d67a0dc2590ca1c4f28598b8c4f017c8684717 Mon Sep 17 00:00:00 2001 From: cosmo Date: Mon, 22 Apr 2024 20:07:05 +0800 Subject: [PATCH 32/33] beta368 --- handlers/message_parser.go | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/handlers/message_parser.go b/handlers/message_parser.go index dc2c388f..70f9c62b 100644 --- a/handlers/message_parser.go +++ b/handlers/message_parser.go @@ -978,13 +978,8 @@ func RevertTransformedText(data interface{}, msgtype string, api openapi.OpenAPI // 从trimmedPrefix中去除前后空格(可能会有bug) trimmedPrefix = strings.TrimSpace(trimmedPrefix) - - if trimmedPrefix == "" { - matched = false - break - } - - if strings.HasPrefix(messageText, trimmedPrefix) { + // trimmedPrefix如果是""就会导致任意内容都是true,所以不能是"" + if strings.HasPrefix(messageText, trimmedPrefix) && trimmedPrefix != "" { matched = true break } From 8dec46c83797ac87499dfe36de90001b6fe6220a Mon Sep 17 00:00:00 2001 From: cosmo Date: Thu, 25 Apr 2024 15:51:39 +0800 Subject: [PATCH 33/33] beta382 --- Processor/ProcessC2CMessage.go | 20 ++++++- Processor/ProcessChannelDirectMessage.go | 27 ++++++++- Processor/ProcessGroupAddBot.go | 14 ++++- Processor/ProcessGroupDelBot.go | 9 ++- Processor/ProcessGroupMessage.go | 9 ++- Processor/ProcessGuildATMessage.go | 16 +++++- Processor/ProcessGuildNormalMessage.go | 19 +++++- Processor/ProcessInlineSearch.go | 26 +++++++-- Processor/ProcessThreadMessage.go | 28 ++++++++- Processor/Processor.go | 8 ++- config/config.go | 73 ++++++++++++++++++------ handlers/message_parser.go | 11 ++++ main.go | 8 ++- server/wsserver.go | 8 ++- template/config_template.go | 2 + wsclient/ws.go | 6 +- 16 files changed, 238 insertions(+), 46 deletions(-) diff --git a/Processor/ProcessC2CMessage.go b/Processor/ProcessC2CMessage.go index 69f69456..c9945915 100644 --- a/Processor/ProcessC2CMessage.go +++ b/Processor/ProcessC2CMessage.go @@ -97,13 +97,21 @@ func (p *Processors) ProcessC2CMessage(data *dto.WSC2CMessageData) error { } else { IsBindedUserId = idmap.CheckValuev2(userid64) } + + var selfid64 int64 + if config.GetUseUin() { + selfid64 = config.GetUinint64() + } else { + selfid64 = int64(p.Settings.AppID) + } + privateMsg := OnebotPrivateMessage{ RawMessage: messageText, Message: segmentedMessages, MessageID: messageID, MessageType: "private", PostType: "message", - SelfID: int64(p.Settings.AppID), + SelfID: selfid64, UserID: userid64, Sender: PrivateSender{ Nickname: "", //这个不支持,但加机器人好友,会收到一个事件,可以对应储存获取,用idmaps可以做到. @@ -205,6 +213,14 @@ func (p *Processors) ProcessC2CMessage(data *dto.WSC2CMessageData) error { messageID := int(messageID64) //todo 判断array模式 然后对Message处理成array格式 IsBindedUserId := idmap.CheckValue(data.Author.ID, userid64) + + var selfid64 int64 + if config.GetUseUin() { + selfid64 = config.GetUinint64() + } else { + selfid64 = int64(p.Settings.AppID) + } + groupMsg := OnebotGroupMessage{ RawMessage: messageText, Message: messageText, @@ -212,7 +228,7 @@ func (p *Processors) ProcessC2CMessage(data *dto.WSC2CMessageData) error { GroupID: userid64, MessageType: "group", PostType: "message", - SelfID: int64(p.Settings.AppID), + SelfID: selfid64, UserID: userid64, Sender: Sender{ UserID: userid64, diff --git a/Processor/ProcessChannelDirectMessage.go b/Processor/ProcessChannelDirectMessage.go index 6c705e33..94d73567 100644 --- a/Processor/ProcessChannelDirectMessage.go +++ b/Processor/ProcessChannelDirectMessage.go @@ -120,13 +120,20 @@ func (p *Processors) ProcessChannelDirectMessage(data *dto.WSDirectMessageData) IsBindedUserId = idmap.CheckValuev2(userid64) } + var selfid64 int64 + if config.GetUseUin() { + selfid64 = config.GetUinint64() + } else { + selfid64 = int64(p.Settings.AppID) + } + privateMsg := OnebotPrivateMessage{ RawMessage: messageText, Message: segmentedMessages, MessageID: messageID, MessageType: "private", PostType: "message", - SelfID: int64(p.Settings.AppID), + SelfID: selfid64, UserID: userid64, Sender: PrivateSender{ Nickname: data.Member.Nick, @@ -196,6 +203,12 @@ func (p *Processors) ProcessChannelDirectMessage(data *dto.WSDirectMessageData) mylog.Printf("Error storing ID: %v", err) return nil } + var selfid64 int64 + if config.GetUseUin() { + selfid64 = config.GetUinint64() + } else { + selfid64 = int64(p.Settings.AppID) + } //OnebotChannelMessage onebotMsg := OnebotChannelMessage{ ChannelID: data.ChannelID, @@ -205,7 +218,7 @@ func (p *Processors) ProcessChannelDirectMessage(data *dto.WSDirectMessageData) MessageID: data.ID, MessageType: "guild", PostType: "message", - SelfID: int64(p.Settings.AppID), + SelfID: selfid64, UserID: userid64, SelfTinyID: "", Sender: Sender{ @@ -348,6 +361,14 @@ func (p *Processors) ProcessChannelDirectMessage(data *dto.WSDirectMessageData) } else { IsBindedUserId = idmap.CheckValuev2(userid64) } + + var selfid64 int64 + if config.GetUseUin() { + selfid64 = config.GetUinint64() + } else { + selfid64 = int64(p.Settings.AppID) + } + groupMsg := OnebotGroupMessage{ RawMessage: messageText, Message: segmentedMessages, @@ -355,7 +376,7 @@ func (p *Processors) ProcessChannelDirectMessage(data *dto.WSDirectMessageData) GroupID: ChannelID64, MessageType: "group", PostType: "message", - SelfID: int64(p.Settings.AppID), + SelfID: selfid64, UserID: userid64, Sender: Sender{ Nickname: data.Member.Nick, diff --git a/Processor/ProcessGroupAddBot.go b/Processor/ProcessGroupAddBot.go index 5fd4863f..eeef7f5e 100644 --- a/Processor/ProcessGroupAddBot.go +++ b/Processor/ProcessGroupAddBot.go @@ -93,15 +93,23 @@ func (p *Processors) ProcessGroupAddBot(data *dto.GroupAddBotEvent) error { mylog.Printf("Invalid type for timestamp: %T", v) return nil } - + mylog.Printf("Bot被[%v]邀请进入群[%v]", userid64, GroupID64) + + var selfid64 int64 + if config.GetUseUin() { + selfid64 = config.GetUinint64() + } else { + selfid64 = int64(p.Settings.AppID) + } + Request = GroupRequestEvent{ Comment: "", Flag: "", GroupID: GroupID64, PostType: "request", RequestType: "group", - SelfID: int64(config.GetAppID()), + SelfID: selfid64, SubType: "invite", Time: timestampInt64, UserID: userid64, @@ -111,7 +119,7 @@ func (p *Processors) ProcessGroupAddBot(data *dto.GroupAddBotEvent) error { NoticeType: "group_increase", OperatorID: 0, PostType: "notice", - SelfID: int64(config.GetAppID()), + SelfID: selfid64, SubType: "invite", Time: timestampInt64, UserID: userid64, diff --git a/Processor/ProcessGroupDelBot.go b/Processor/ProcessGroupDelBot.go index 5dfe2e65..44d1287e 100644 --- a/Processor/ProcessGroupDelBot.go +++ b/Processor/ProcessGroupDelBot.go @@ -53,12 +53,19 @@ func (p *Processors) ProcessGroupDelBot(data *dto.GroupAddBotEvent) error { mylog.Printf("Bot被[%v]从群[%v]移出", userid64, GroupID64) //从数据库删除群数据(仅删除类型缓存,再次加入会刷新) idmap.DeleteConfigv2(fmt.Sprint(GroupID64), "type") + + var selfid64 int64 + if config.GetUseUin() { + selfid64 = config.GetUinint64() + } else { + selfid64 = int64(p.Settings.AppID) + } Notice = GroupNoticeEvent{ GroupID: GroupID64, NoticeType: "group_decrease", OperatorID: 0, PostType: "notice", - SelfID: int64(config.GetAppID()), + SelfID: selfid64, SubType: "kick_me", Time: timestampInt64, UserID: userid64, diff --git a/Processor/ProcessGroupMessage.go b/Processor/ProcessGroupMessage.go index 4631ab20..5cdcf065 100644 --- a/Processor/ProcessGroupMessage.go +++ b/Processor/ProcessGroupMessage.go @@ -102,6 +102,13 @@ func (p *Processors) ProcessGroupMessage(data *dto.WSGroupATMessageData) error { IsBindedUserId = idmap.CheckValuev2(userid64) IsBindedGroupId = idmap.CheckValuev2(GroupID64) } + var selfid64 int64 + if config.GetUseUin() { + selfid64 = config.GetUinint64() + } else { + selfid64 = int64(p.Settings.AppID) + } + groupMsg := OnebotGroupMessage{ RawMessage: messageText, Message: segmentedMessages, @@ -109,7 +116,7 @@ func (p *Processors) ProcessGroupMessage(data *dto.WSGroupATMessageData) error { GroupID: GroupID64, MessageType: "group", PostType: "message", - SelfID: int64(p.Settings.AppID), + SelfID: selfid64, UserID: userid64, Sender: Sender{ UserID: userid64, diff --git a/Processor/ProcessGuildATMessage.go b/Processor/ProcessGuildATMessage.go index 563da6d0..aef11d04 100644 --- a/Processor/ProcessGuildATMessage.go +++ b/Processor/ProcessGuildATMessage.go @@ -54,6 +54,12 @@ func (p *Processors) ProcessGuildATMessage(data *dto.WSATMessageData) error { if config.GetArrayValue() { segmentedMessages = handlers.ConvertToSegmentedMessage(data) } + var selfid64 int64 + if config.GetUseUin() { + selfid64 = config.GetUinint64() + } else { + selfid64 = int64(p.Settings.AppID) + } // 处理onebot_channel_message逻辑 onebotMsg := OnebotChannelMessage{ ChannelID: data.ChannelID, @@ -63,7 +69,7 @@ func (p *Processors) ProcessGuildATMessage(data *dto.WSATMessageData) error { MessageID: data.ID, MessageType: "guild", PostType: "message", - SelfID: int64(p.Settings.AppID), + SelfID: selfid64, UserID: userid64, SelfTinyID: "0", Sender: Sender{ @@ -208,6 +214,12 @@ func (p *Processors) ProcessGuildATMessage(data *dto.WSATMessageData) error { IsBindedUserId = idmap.CheckValuev2(userid64) IsBindedGroupId = idmap.CheckValuev2(ChannelID64) } + var selfid64 int64 + if config.GetUseUin() { + selfid64 = config.GetUinint64() + } else { + selfid64 = int64(p.Settings.AppID) + } groupMsg := OnebotGroupMessage{ RawMessage: messageText, Message: segmentedMessages, @@ -215,7 +227,7 @@ func (p *Processors) ProcessGuildATMessage(data *dto.WSATMessageData) error { GroupID: ChannelID64, MessageType: "group", PostType: "message", - SelfID: int64(p.Settings.AppID), + SelfID: selfid64, UserID: userid64, Sender: Sender{ Nickname: data.Member.Nick, diff --git a/Processor/ProcessGuildNormalMessage.go b/Processor/ProcessGuildNormalMessage.go index f3f01942..36b9a32a 100644 --- a/Processor/ProcessGuildNormalMessage.go +++ b/Processor/ProcessGuildNormalMessage.go @@ -51,6 +51,13 @@ func (p *Processors) ProcessGuildNormalMessage(data *dto.WSMessageData) error { if config.GetArrayValue() { segmentedMessages = handlers.ConvertToSegmentedMessage(data) } + var selfid64 int64 + if config.GetUseUin() { + selfid64 = config.GetUinint64() + } else { + selfid64 = int64(p.Settings.AppID) + } + // 处理onebot_channel_message逻辑 onebotMsg := OnebotChannelMessage{ ChannelID: data.ChannelID, @@ -60,7 +67,7 @@ func (p *Processors) ProcessGuildNormalMessage(data *dto.WSMessageData) error { MessageID: data.ID, MessageType: "guild", PostType: "message", - SelfID: int64(p.Settings.AppID), + SelfID: selfid64, UserID: userid64, SelfTinyID: "0", Sender: Sender{ @@ -203,6 +210,14 @@ func (p *Processors) ProcessGuildNormalMessage(data *dto.WSMessageData) error { IsBindedUserId = idmap.CheckValuev2(userid64) IsBindedGroupId = idmap.CheckValuev2(ChannelID64) } + + var selfid64 int64 + if config.GetUseUin() { + selfid64 = config.GetUinint64() + } else { + selfid64 = int64(p.Settings.AppID) + } + groupMsg := OnebotGroupMessage{ RawMessage: messageText, Message: segmentedMessages, @@ -210,7 +225,7 @@ func (p *Processors) ProcessGuildNormalMessage(data *dto.WSMessageData) error { GroupID: ChannelID64, MessageType: "group", PostType: "message", - SelfID: int64(p.Settings.AppID), + SelfID: selfid64, UserID: userid64, Sender: Sender{ Nickname: data.Member.Nick, diff --git a/Processor/ProcessInlineSearch.go b/Processor/ProcessInlineSearch.go index fa03d10f..4c205b08 100644 --- a/Processor/ProcessInlineSearch.go +++ b/Processor/ProcessInlineSearch.go @@ -64,12 +64,19 @@ func (p *Processors) ProcessInlineSearch(data *dto.WSInteractionData) error { return nil } } + var selfid64 int64 + if config.GetUseUin() { + selfid64 = config.GetUinint64() + } else { + selfid64 = int64(p.Settings.AppID) + } + if !config.GetGlobalInteractionToMessage() { notice := &OnebotInteractionNotice{ GroupID: GroupID64, NoticeType: "interaction", PostType: "notice", - SelfID: int64(p.Settings.AppID), + SelfID: selfid64, SubType: "create", Time: time.Now().Unix(), UserID: userid64, @@ -95,6 +102,12 @@ func (p *Processors) ProcessInlineSearch(data *dto.WSInteractionData) error { return nil } messageID := int(messageID64) + var selfid64 int64 + if config.GetUseUin() { + selfid64 = config.GetUinint64() + } else { + selfid64 = int64(p.Settings.AppID) + } groupMsg := OnebotGroupMessage{ RawMessage: data.Data.Resolved.ButtonData, Message: segmentedMessages, @@ -102,7 +115,7 @@ func (p *Processors) ProcessInlineSearch(data *dto.WSInteractionData) error { GroupID: GroupID64, MessageType: "group", PostType: "message", - SelfID: int64(p.Settings.AppID), + SelfID: selfid64, UserID: userid64, Sender: Sender{ UserID: userid64, @@ -131,7 +144,12 @@ func (p *Processors) ProcessInlineSearch(data *dto.WSInteractionData) error { //群回调 newdata := ConvertInteractionToMessage(data) segmentedMessages := handlers.ConvertToSegmentedMessage(newdata) - + var selfid64 int64 + if config.GetUseUin() { + selfid64 = config.GetUinint64() + } else { + selfid64 = int64(p.Settings.AppID) + } onebotMsg := OnebotChannelMessage{ ChannelID: data.ChannelID, GuildID: data.GuildID, @@ -140,7 +158,7 @@ func (p *Processors) ProcessInlineSearch(data *dto.WSInteractionData) error { MessageID: data.ID, MessageType: "guild", PostType: "message", - SelfID: int64(p.Settings.AppID), + SelfID: selfid64, UserID: userid64, SelfTinyID: "0", Sender: Sender{ diff --git a/Processor/ProcessThreadMessage.go b/Processor/ProcessThreadMessage.go index 9f61c27c..833ec72d 100644 --- a/Processor/ProcessThreadMessage.go +++ b/Processor/ProcessThreadMessage.go @@ -57,6 +57,14 @@ func (p *Processors) ProcessThreadMessage(data *dto.WSThreadData) error { if err != nil { mylog.Printf("Error parseContent Forum: %v", err) } + + var selfid64 int64 + if config.GetUseUin() { + selfid64 = config.GetUinint64() + } else { + selfid64 = int64(p.Settings.AppID) + } + // 处理onebot_channel_message逻辑 onebotMsg := OnebotChannelMessage{ ChannelID: data.ChannelID, @@ -66,7 +74,7 @@ func (p *Processors) ProcessThreadMessage(data *dto.WSThreadData) error { MessageID: data.ID, MessageType: "guild", PostType: "message", - SelfID: int64(p.Settings.AppID), + SelfID: selfid64, UserID: userid64, SelfTinyID: "0", Sender: Sender{ @@ -168,6 +176,14 @@ func (p *Processors) ProcessThreadMessage(data *dto.WSThreadData) error { if err != nil { mylog.Printf("Error parseContent Forum: %v", err) } + + var selfid64 int64 + if config.GetUseUin() { + selfid64 = config.GetUinint64() + } else { + selfid64 = int64(p.Settings.AppID) + } + // 处理onebot_channel_message逻辑 onebotMsg := OnebotChannelMessage{ ChannelID: data.ChannelID, @@ -177,7 +193,7 @@ func (p *Processors) ProcessThreadMessage(data *dto.WSThreadData) error { MessageID: data.ID, MessageType: "guild", PostType: "message", - SelfID: int64(p.Settings.AppID), + SelfID: selfid64, UserID: userid64, SelfTinyID: "0", Sender: Sender{ @@ -315,6 +331,12 @@ func (p *Processors) ProcessThreadMessage(data *dto.WSThreadData) error { IsBindedUserId = idmap.CheckValuev2(userid64) IsBindedGroupId = idmap.CheckValuev2(ChannelID64) } + var selfid64 int64 + if config.GetUseUin() { + selfid64 = config.GetUinint64() + } else { + selfid64 = int64(p.Settings.AppID) + } groupMsg := OnebotGroupMessage{ RawMessage: messageText, Message: segmentedMessages, @@ -322,7 +344,7 @@ func (p *Processors) ProcessThreadMessage(data *dto.WSThreadData) error { GroupID: ChannelID64, MessageType: "group", PostType: "message", - SelfID: int64(p.Settings.AppID), + SelfID: selfid64, UserID: userid64, Sender: Sender{ Nickname: "发帖人昵称", diff --git a/Processor/Processor.go b/Processor/Processor.go index 71f23ed0..3ac94aab 100644 --- a/Processor/Processor.go +++ b/Processor/Processor.go @@ -275,7 +275,13 @@ func PostMessageToUrls(message map[string]interface{}) { // 设置请求头 req.Header.Set("Content-Type", "application/json") // 设置 X-Self-ID - selfid := config.GetAppIDStr() + var selfid string + if config.GetUseUin() { + selfid = config.GetUinStr() + } else { + selfid = config.GetAppIDStr() + } + req.Header.Set("X-Self-ID", selfid) // 发送请求 diff --git a/config/config.go b/config/config.go index a3420674..70e5d415 100644 --- a/config/config.go +++ b/config/config.go @@ -45,6 +45,7 @@ type Settings struct { ClientSecret string `yaml:"client_secret"` ShardCount int `yaml:"shard_count"` ShardID int `yaml:"shard_id"` + UseUin bool `yaml:"use_uin"` //事件订阅类 TextIntent []string `yaml:"text_intent"` //转换类 @@ -96,25 +97,26 @@ type Settings struct { Username string `yaml:"server_user_name"` Password string `yaml:"server_user_password"` //指令魔法类 - RemovePrefix bool `yaml:"remove_prefix"` - RemoveAt bool `yaml:"remove_at"` - RemoveBotAtGroup bool `yaml:"remove_bot_at_group"` - AddAtGroup bool `yaml:"add_at_group"` - WhitePrefixMode bool `yaml:"white_prefix_mode"` - VwhitePrefixMode bool `yaml:"v_white_prefix_mode"` - WhitePrefixs []string `yaml:"white_prefixs"` - WhiteBypass []int64 `yaml:"white_bypass"` - WhiteEnable []bool `yaml:"white_enable"` - WhiteBypassRevers bool `yaml:"white_bypass_reverse"` - NoWhiteResponse string `yaml:"No_White_Response"` - BlackPrefixMode bool `yaml:"black_prefix_mode"` - BlackPrefixs []string `yaml:"black_prefixs"` - Alias []string `yaml:"alias"` - Enters []string `yaml:"enters"` - EntersExcept []string `yaml:"enters_except"` - VisualPrefixs []VisualPrefixConfig `yaml:"visual_prefixs"` - AutoWithdraw []string `yaml:"auto_withdraw"` - AutoWithdrawTime int `yaml:"auto_withdraw_time"` + RemovePrefix bool `yaml:"remove_prefix"` + RemoveAt bool `yaml:"remove_at"` + RemoveBotAtGroup bool `yaml:"remove_bot_at_group"` + AddAtGroup bool `yaml:"add_at_group"` + WhitePrefixMode bool `yaml:"white_prefix_mode"` + VwhitePrefixMode bool `yaml:"v_white_prefix_mode"` + WhitePrefixs []string `yaml:"white_prefixs"` + WhiteBypass []int64 `yaml:"white_bypass"` + WhiteEnable []bool `yaml:"white_enable"` + WhiteBypassRevers bool `yaml:"white_bypass_reverse"` + NoWhiteResponse string `yaml:"No_White_Response"` + BlackPrefixMode bool `yaml:"black_prefix_mode"` + BlackPrefixs []string `yaml:"black_prefixs"` + Alias []string `yaml:"alias"` + Enters []string `yaml:"enters"` + EntersExcept []string `yaml:"enters_except"` + VisualPrefixs []VisualPrefixConfig `yaml:"visual_prefixs"` + AutoWithdraw []string `yaml:"auto_withdraw"` + AutoWithdrawTime int `yaml:"auto_withdraw_time"` + VisualPrefixsBypass []string `yaml:"visual_prefixs_bypass"` //开发增强类 DevlopAcDir string `yaml:"develop_access_token_dir"` DevBotid string `yaml:"develop_bot_id"` @@ -1497,6 +1499,18 @@ func GetPostSecret() []string { return instance.Settings.PostSecret } +// 获取 VisualPrefixsBypass +func GetVisualPrefixsBypass() []string { + mu.Lock() + defer mu.Unlock() + + if instance == nil { + mylog.Println("Warning: instance is nil when trying to getVisualPrefixsBypass.") + return nil + } + return instance.Settings.VisualPrefixsBypass +} + // 获取 POST 最大重试次数数组 func GetPostMaxRetries() []int { mu.Lock() @@ -1557,6 +1571,17 @@ func GetUrlToQrimage() bool { return instance.Settings.UrlToQrimage } +func GetUseUin() bool { + mu.Lock() + defer mu.Unlock() + + if instance == nil { + mylog.Println("Warning: instance is nil when trying to UseUin value.") + return false + } + return instance.Settings.UseUin +} + // 获取GetQrSize的值 func GetQrSize() int { mu.Lock() @@ -1926,6 +1951,16 @@ func GetKeyBoardID() string { return instance.Settings.KeyBoardID } +// 获取Uin int64 +func GetUinint64() int64 { + mu.Lock() + defer mu.Unlock() + if instance != nil { + return instance.Settings.Uin + } + return 0 +} + // 获取Uin String func GetUinStr() string { mu.Lock() diff --git a/handlers/message_parser.go b/handlers/message_parser.go index 70f9c62b..84c1d361 100644 --- a/handlers/message_parser.go +++ b/handlers/message_parser.go @@ -740,6 +740,7 @@ func RevertTransformedText(data interface{}, msgtype string, api openapi.OpenAPI //处理前 先去前后空 messageText = strings.TrimSpace(msg.Content) } + var originmessageText = messageText //mylog.Printf("1[%v]", messageText) // 将messageText里的BotID替换成AppID @@ -962,6 +963,15 @@ func RevertTransformedText(data interface{}, msgtype string, api openapi.OpenAPI matched = true } else { matched = false + // 调用 GetVisualPrefixsBypass 获取前缀数组 + visualPrefixes := config.GetVisualPrefixsBypass() + // 判断 messageText 是否以数组中的任一前缀开头 + for _, prefix := range visualPrefixes { + if strings.HasPrefix(originmessageText, prefix) { + matched = true + break + } + } } } else { // 遍历白名单数组,检查是否有匹配项 @@ -990,6 +1000,7 @@ func RevertTransformedText(data interface{}, msgtype string, api openapi.OpenAPI if !matched { messageText = "" SendMessage(matchedPrefix.NoWhiteResponse, data, msgtype, api, apiv2) + return messageText //2024-4-25新增,可能会有bug.观察 } } } diff --git a/main.go b/main.go index 11258e74..27026ae1 100644 --- a/main.go +++ b/main.go @@ -255,7 +255,13 @@ func main() { attemptedConnections++ // 增加尝试连接的计数 go func(address string) { retry := config.GetLaunchReconectTimes() - wsClient, err := wsclient.NewWebSocketClient(address, conf.Settings.AppID, api, apiV2, retry) + var BotID uint64 + if config.GetUseUin() { + BotID = uint64(config.GetUinint64()) + } else { + BotID = conf.Settings.AppID + } + wsClient, err := wsclient.NewWebSocketClient(address, BotID, api, apiV2, retry) if err != nil { log.Printf("Error creating WebSocketClient for address(连接到反向ws失败) %s: %v\n", address, err) errorChan <- err diff --git a/server/wsserver.go b/server/wsserver.go index fb223d22..5bd2b366 100644 --- a/server/wsserver.go +++ b/server/wsserver.go @@ -93,7 +93,13 @@ func wsHandler(api openapi.OpenAPI, apiV2 openapi.OpenAPI, p *Processor.Processo p.WsServerClients = append(p.WsServerClients, client) // 获取botID - botID := config.GetAppID() + + var botID uint64 + if config.GetUseUin() { + botID = uint64(config.GetUinint64()) + } else { + botID = config.GetAppID() + } // 发送连接成功的消息 message := map[string]interface{}{ diff --git a/template/config_template.go b/template/config_template.go index 10cbda4e..e8ce50d4 100644 --- a/template/config_template.go +++ b/template/config_template.go @@ -13,6 +13,7 @@ settings: #基础设置 app_id: 12345 # 你的应用ID uin : 0 # 你的机器人QQ号,点击机器人资料卡查看 + use_uin : false # false=使用appid作为机器人id,true=使用机器人QQ号,需设置正确的uin token: "" # 你的应用令牌 client_secret: "" # 你的客户端密钥 shard_count: 1 #分片数量 默认1 @@ -110,6 +111,7 @@ settings: enters_except : ["",""] #自动md卡片点击直接触发,例外,对子按钮生效. auto_withdraw : [] #仅当应用端实现了双向echo可用.实现不难,可以去找对应开发者去提需求. auto_withdraw_time : 30 #30秒 + visual_prefixs_bypass : [] #要绕过二级指令白名单的指令,比如需要用户回答自定义内容的指令. visual_prefixs : #虚拟前缀 与white_prefixs配合使用 处理流程自动忽略该前缀 remove_prefix remove_at 需为true时生效 - prefix: "" #虚拟前缀开头 例 你有3个指令 帮助 测试 查询 将 prefix 设置为 工具类 后 则可通过 工具类 帮助 触发机器人 diff --git a/wsclient/ws.go b/wsclient/ws.go index 318c8350..3435e87c 100644 --- a/wsclient/ws.go +++ b/wsclient/ws.go @@ -53,7 +53,7 @@ func (client *WebSocketClient) SendMessage(message map[string]interface{}) error } // 处理onebotv11应用端发来的信息 -func (client *WebSocketClient) handleIncomingMessages(ctx context.Context, cancel context.CancelFunc) { +func (client *WebSocketClient) handleIncomingMessages(cancel context.CancelFunc) { for { _, msg, err := client.conn.ReadMessage() if err != nil { @@ -155,7 +155,7 @@ func (client *WebSocketClient) Reconnect() { client.cancel = cancel heartbeatinterval := config.GetHeartBeatInterval() go client.sendHeartbeat(ctx, client.botID, heartbeatinterval) - go client.handleIncomingMessages(ctx, cancel) + go client.handleIncomingMessages(cancel) defer func() { client.isReconnecting = false @@ -337,7 +337,7 @@ func NewWebSocketClient(urlStr string, botID uint64, api openapi.OpenAPI, apiv2 client.cancel = cancel heartbeatinterval := config.GetHeartBeatInterval() go client.sendHeartbeat(ctx, botID, heartbeatinterval) - go client.handleIncomingMessages(ctx, cancel) + go client.handleIncomingMessages(cancel) return client, nil }