From 2f00ffdb9c21a66b86a1a444e4b6347ca9dae4c0 Mon Sep 17 00:00:00 2001
From: cosmo
Date: Mon, 8 Jan 2024 13:47:06 +0800
Subject: [PATCH 01/13] beta122
---
readme.md | 3 +++
1 file changed, 3 insertions(+)
diff --git a/readme.md b/readme.md
index d8f7a8ee..90341e90 100644
--- a/readme.md
+++ b/readme.md
@@ -40,6 +40,9 @@ _✨ 基于 [OneBot](https://github.com/howmanybots/onebot/blob/master/README.md
·
参与贡献
+
+ 项目主页:gensokyo.bot
+
## 引用
- [`tencent-connect/botgo`](https://github.com/tencent-connect/botgo): 本项目引用了此项目,并做了一点改动.
From 2158740465a2f59dd5ae7727c71b8b63158088a1 Mon Sep 17 00:00:00 2001
From: cosmo
Date: Thu, 11 Jan 2024 17:13:42 +0800
Subject: [PATCH 02/13] beta123
---
handlers/send_msg.go | 8 +++++---
1 file changed, 5 insertions(+), 3 deletions(-)
diff --git a/handlers/send_msg.go b/handlers/send_msg.go
index 1982ae06..b06dc2b5 100644
--- a/handlers/send_msg.go
+++ b/handlers/send_msg.go
@@ -33,13 +33,15 @@ func HandleSendMsg(client callapi.Client, api openapi.OpenAPI, apiv2 openapi.Ope
if msgType == "" {
msgType = GetMessageTypeByUserid(config.GetAppIDStr(), message.Params.UserID)
}
- //新增 内存获取不到从数据库获取
+ //顺序有讲究
if msgType == "" {
- msgType = GetMessageTypeByUseridV2(message.Params.UserID)
+ msgType = GetMessageTypeByGroupidV2(message.Params.GroupID)
}
+ //新增 内存获取不到从数据库获取
if msgType == "" {
- msgType = GetMessageTypeByGroupidV2(message.Params.GroupID)
+ msgType = GetMessageTypeByUseridV2(message.Params.UserID)
}
+
var idInt64 int64
var err error
From ea41561628f1d01fd60b334592e65bb3cef4c156 Mon Sep 17 00:00:00 2001
From: cosmo
Date: Thu, 11 Jan 2024 19:38:41 +0800
Subject: [PATCH 03/13] beta124
---
handlers/send_msg.go | 5 ++++-
handlers/send_private_msg.go | 12 ++++++------
2 files changed, 10 insertions(+), 7 deletions(-)
diff --git a/handlers/send_msg.go b/handlers/send_msg.go
index b06dc2b5..fa6562f8 100644
--- a/handlers/send_msg.go
+++ b/handlers/send_msg.go
@@ -42,13 +42,15 @@ func HandleSendMsg(client callapi.Client, api openapi.OpenAPI, apiv2 openapi.Ope
msgType = GetMessageTypeByUseridV2(message.Params.UserID)
}
- var idInt64 int64
+ var idInt64, idInt642 int64
var err error
if message.Params.GroupID != "" {
idInt64, err = ConvertToInt64(message.Params.GroupID)
+ idInt642, err = ConvertToInt64(message.Params.UserID)
} else if message.Params.UserID != "" {
idInt64, err = ConvertToInt64(message.Params.UserID)
+ idInt642, err = ConvertToInt64(message.Params.GroupID)
}
//设置递归 对直接向gsk发送action时有效果
@@ -101,6 +103,7 @@ func HandleSendMsg(client callapi.Client, api openapi.OpenAPI, apiv2 openapi.Ope
//重置递归类型
if echo.GetMapping(idInt64) <= 0 {
echo.AddMsgType(config.GetAppIDStr(), idInt64, "")
+ echo.AddMsgType(config.GetAppIDStr(), idInt642, "")
}
echo.AddMapping(idInt64, echo.GetMapping(idInt64)-1)
diff --git a/handlers/send_private_msg.go b/handlers/send_private_msg.go
index 43bcba67..6dcefe7d 100644
--- a/handlers/send_private_msg.go
+++ b/handlers/send_private_msg.go
@@ -29,14 +29,14 @@ func HandleSendPrivateMsg(client callapi.Client, api openapi.OpenAPI, apiv2 open
msgType = echo.GetMsgTypeByKey(echoStr)
}
- //如果获取不到 就用group_id获取信息类型
- if msgType == "" {
- msgType = GetMessageTypeByGroupid(config.GetAppIDStr(), message.Params.GroupID)
- }
//如果获取不到 就用user_id获取信息类型
if msgType == "" {
msgType = GetMessageTypeByUserid(config.GetAppIDStr(), message.Params.UserID)
}
+ //顺序,私聊优先从UserID推断类型会更准确
+ if msgType == "" {
+ msgType = GetMessageTypeByGroupid(config.GetAppIDStr(), message.Params.GroupID)
+ }
//新增 内存获取不到从数据库获取
if msgType == "" {
msgType = GetMessageTypeByUseridV2(message.Params.UserID)
@@ -68,7 +68,7 @@ func HandleSendPrivateMsg(client callapi.Client, api openapi.OpenAPI, apiv2 open
}
switch msgType {
- case "group_private","group":
+ case "group_private", "group":
//私聊信息
var UserID string
if config.GetIdmapPro() {
@@ -266,7 +266,7 @@ func HandleSendPrivateMsg(client callapi.Client, api openapi.OpenAPI, apiv2 open
retmsg, _ = SendResponse(client, err, &message)
}
}
- case "guild_private","guild":
+ case "guild_private", "guild":
//当收到发私信调用 并且来源是频道
retmsg, _ = HandleSendGuildChannelPrivateMsg(client, api, apiv2, message, nil, nil)
default:
From c8561ec14daf76e5daa679773968a9f78465a7d1 Mon Sep 17 00:00:00 2001
From: cosmo
Date: Thu, 11 Jan 2024 19:41:19 +0800
Subject: [PATCH 04/13] beta124
---
handlers/send_msg.go | 8 ++++----
1 file changed, 4 insertions(+), 4 deletions(-)
diff --git a/handlers/send_msg.go b/handlers/send_msg.go
index fa6562f8..843e744e 100644
--- a/handlers/send_msg.go
+++ b/handlers/send_msg.go
@@ -46,11 +46,11 @@ func HandleSendMsg(client callapi.Client, api openapi.OpenAPI, apiv2 openapi.Ope
var err error
if message.Params.GroupID != "" {
- idInt64, err = ConvertToInt64(message.Params.GroupID)
- idInt642, err = ConvertToInt64(message.Params.UserID)
+ idInt64, _ = ConvertToInt64(message.Params.GroupID)
+ idInt642, _ = ConvertToInt64(message.Params.UserID)
} else if message.Params.UserID != "" {
- idInt64, err = ConvertToInt64(message.Params.UserID)
- idInt642, err = ConvertToInt64(message.Params.GroupID)
+ idInt64, _ = ConvertToInt64(message.Params.UserID)
+ idInt642, _ = ConvertToInt64(message.Params.GroupID)
}
//设置递归 对直接向gsk发送action时有效果
From 22865d4b69358b20317304ca685d340b81f03896 Mon Sep 17 00:00:00 2001
From: cosmo
Date: Thu, 11 Jan 2024 20:07:19 +0800
Subject: [PATCH 05/13] beta124
---
handlers/send_msg.go | 4 ++++
1 file changed, 4 insertions(+)
diff --git a/handlers/send_msg.go b/handlers/send_msg.go
index 843e744e..00960811 100644
--- a/handlers/send_msg.go
+++ b/handlers/send_msg.go
@@ -29,18 +29,22 @@ func HandleSendMsg(client callapi.Client, api openapi.OpenAPI, apiv2 openapi.Ope
if msgType == "" {
msgType = GetMessageTypeByGroupid(config.GetAppIDStr(), message.Params.GroupID)
}
+ mylog.Printf("测试1:%v", msgType)
//如果获取不到 就用user_id获取信息类型
if msgType == "" {
msgType = GetMessageTypeByUserid(config.GetAppIDStr(), message.Params.UserID)
}
+ mylog.Printf("测试2:%v", msgType)
//顺序有讲究
if msgType == "" {
msgType = GetMessageTypeByGroupidV2(message.Params.GroupID)
}
+ mylog.Printf("测试3:%v", msgType)
//新增 内存获取不到从数据库获取
if msgType == "" {
msgType = GetMessageTypeByUseridV2(message.Params.UserID)
}
+ mylog.Printf("测试4:%v", msgType)
var idInt64, idInt642 int64
var err error
From d51f4190ed57f9052fb2c069e46c35877ab820b9 Mon Sep 17 00:00:00 2001
From: cosmo
Date: Thu, 11 Jan 2024 20:25:13 +0800
Subject: [PATCH 06/13] beta127
---
handlers/send_guild_channel_msg.go | 7 ++-----
handlers/send_msg.go | 8 --------
2 files changed, 2 insertions(+), 13 deletions(-)
diff --git a/handlers/send_guild_channel_msg.go b/handlers/send_guild_channel_msg.go
index c84ed3e5..c625902b 100644
--- a/handlers/send_guild_channel_msg.go
+++ b/handlers/send_guild_channel_msg.go
@@ -34,20 +34,17 @@ func HandleSendGuildChannelMsg(client callapi.Client, api openapi.OpenAPI, apiv2
// 当 message.Echo 是字符串类型时执行此块
msgType = echo.GetMsgTypeByKey(echoStr)
}
- //如果获取不到 就用group_id获取信息类型
if msgType == "" {
msgType = GetMessageTypeByGroupid(config.GetAppIDStr(), message.Params.GroupID)
}
- //如果获取不到 就用user_id获取信息类型
if msgType == "" {
msgType = GetMessageTypeByUserid(config.GetAppIDStr(), message.Params.UserID)
}
- //新增 内存获取不到从数据库获取
if msgType == "" {
- msgType = GetMessageTypeByUseridV2(message.Params.UserID)
+ msgType = GetMessageTypeByGroupidV2(message.Params.GroupID)
}
if msgType == "" {
- msgType = GetMessageTypeByGroupidV2(message.Params.GroupID)
+ msgType = GetMessageTypeByUseridV2(message.Params.UserID)
}
//当不转换频道信息时(不支持频道私聊)
if msgType == "" {
diff --git a/handlers/send_msg.go b/handlers/send_msg.go
index 00960811..f5ba50e9 100644
--- a/handlers/send_msg.go
+++ b/handlers/send_msg.go
@@ -25,26 +25,18 @@ func HandleSendMsg(client callapi.Client, api openapi.OpenAPI, apiv2 openapi.Ope
// 当 message.Echo 是字符串类型时执行此块
msgType = echo.GetMsgTypeByKey(echoStr)
}
- //如果获取不到 就用group_id获取信息类型
if msgType == "" {
msgType = GetMessageTypeByGroupid(config.GetAppIDStr(), message.Params.GroupID)
}
- mylog.Printf("测试1:%v", msgType)
- //如果获取不到 就用user_id获取信息类型
if msgType == "" {
msgType = GetMessageTypeByUserid(config.GetAppIDStr(), message.Params.UserID)
}
- mylog.Printf("测试2:%v", msgType)
- //顺序有讲究
if msgType == "" {
msgType = GetMessageTypeByGroupidV2(message.Params.GroupID)
}
- mylog.Printf("测试3:%v", msgType)
- //新增 内存获取不到从数据库获取
if msgType == "" {
msgType = GetMessageTypeByUseridV2(message.Params.UserID)
}
- mylog.Printf("测试4:%v", msgType)
var idInt64, idInt642 int64
var err error
From d6efb107bdf6660cb93751fd4b611655a172b105 Mon Sep 17 00:00:00 2001
From: cosmo
Date: Fri, 12 Jan 2024 22:27:57 +0800
Subject: [PATCH 07/13] beta128
---
Processor/ProcessC2CMessage.go | 4 +
Processor/ProcessChannelDirectMessage.go | 6 +
Processor/ProcessGroupMessage.go | 2 +
Processor/ProcessGuildATMessage.go | 9 +-
Processor/ProcessGuildNormalMessage.go | 4 +
botgo/dto/keyboard/keyboard.go | 4 +
config/config.go | 26 ++++
echo/echo.go | 9 ++
handlers/send_group_msg.go | 185 +++++++++++++++++++----
images/format.go | 180 ++++++++++++++++++++++
template/config_template.go | 2 +
11 files changed, 404 insertions(+), 27 deletions(-)
create mode 100644 images/format.go
diff --git a/Processor/ProcessC2CMessage.go b/Processor/ProcessC2CMessage.go
index 2b734017..465ae443 100644
--- a/Processor/ProcessC2CMessage.go
+++ b/Processor/ProcessC2CMessage.go
@@ -118,6 +118,8 @@ func (p *Processors) ProcessC2CMessage(data *dto.WSC2CMessageData) error {
// 根据条件判断是否添加Echo字段
if config.GetTwoWayEcho() {
privateMsg.Echo = echostr
+ //用向应用端(如果支持)发送echo,来确定客户端的send_msg对应的触发词原文
+ echo.AddMsgIDv3(AppIDString, echostr, messageText)
}
// 将当前s和appid和message进行映射
echo.AddMsgID(AppIDString, s, data.ID)
@@ -220,6 +222,8 @@ func (p *Processors) ProcessC2CMessage(data *dto.WSC2CMessageData) error {
// 根据条件判断是否添加Echo字段
if config.GetTwoWayEcho() {
groupMsg.Echo = echostr
+ //用向应用端(如果支持)发送echo,来确定客户端的send_msg对应的触发词原文
+ echo.AddMsgIDv3(AppIDString, echostr, messageText)
}
// 获取MasterID数组
masterIDs := config.GetMasterID()
diff --git a/Processor/ProcessChannelDirectMessage.go b/Processor/ProcessChannelDirectMessage.go
index 53eb6ee7..e27faa94 100644
--- a/Processor/ProcessChannelDirectMessage.go
+++ b/Processor/ProcessChannelDirectMessage.go
@@ -141,6 +141,8 @@ func (p *Processors) ProcessChannelDirectMessage(data *dto.WSDirectMessageData)
// 根据条件判断是否添加Echo字段
if config.GetTwoWayEcho() {
privateMsg.Echo = echostr
+ //用向应用端(如果支持)发送echo,来确定客户端的send_msg对应的触发词原文
+ echo.AddMsgIDv3(AppIDString, echostr, messageText)
}
// 将当前s和appid和message进行映射
echo.AddMsgID(AppIDString, s, data.ID)
@@ -218,6 +220,8 @@ func (p *Processors) ProcessChannelDirectMessage(data *dto.WSDirectMessageData)
// 根据条件判断是否添加Echo字段
if config.GetTwoWayEcho() {
onebotMsg.Echo = echostr
+ //用向应用端(如果支持)发送echo,来确定客户端的send_msg对应的触发词原文
+ echo.AddMsgIDv3(AppIDString, echostr, messageText)
}
// 获取MasterID数组
masterIDs := config.GetMasterID()
@@ -368,6 +372,8 @@ func (p *Processors) ProcessChannelDirectMessage(data *dto.WSDirectMessageData)
// 根据条件判断是否添加Echo字段
if config.GetTwoWayEcho() {
groupMsg.Echo = echostr
+ //用向应用端(如果支持)发送echo,来确定客户端的send_msg对应的触发词原文
+ echo.AddMsgIDv3(AppIDString, echostr, messageText)
}
// 获取MasterID数组
masterIDs := config.GetMasterID()
diff --git a/Processor/ProcessGroupMessage.go b/Processor/ProcessGroupMessage.go
index 28387413..87d2e8a1 100644
--- a/Processor/ProcessGroupMessage.go
+++ b/Processor/ProcessGroupMessage.go
@@ -135,6 +135,8 @@ func (p *Processors) ProcessGroupMessage(data *dto.WSGroupATMessageData) error {
// 根据条件判断是否添加Echo字段
if config.GetTwoWayEcho() {
groupMsg.Echo = echostr
+ //用向应用端(如果支持)发送echo,来确定客户端的send_msg对应的触发词原文
+ echo.AddMsgIDv3(AppIDString, echostr, messageText)
}
// 获取MasterID数组
masterIDs := config.GetMasterID()
diff --git a/Processor/ProcessGuildATMessage.go b/Processor/ProcessGuildATMessage.go
index e043dfba..c9426919 100644
--- a/Processor/ProcessGuildATMessage.go
+++ b/Processor/ProcessGuildATMessage.go
@@ -75,7 +75,12 @@ func (p *Processors) ProcessGuildATMessage(data *dto.WSATMessageData) error {
SubType: "channel",
Time: t.Unix(),
Avatar: data.Author.Avatar,
- Echo: echostr,
+ }
+ // 根据条件判断是否添加Echo字段
+ if config.GetTwoWayEcho() {
+ onebotMsg.Echo = echostr
+ //用向应用端(如果支持)发送echo,来确定客户端的send_msg对应的触发词原文
+ echo.AddMsgIDv3(AppIDString, echostr, messageText)
}
// 获取MasterID数组
masterIDs := config.GetMasterID()
@@ -226,6 +231,8 @@ func (p *Processors) ProcessGuildATMessage(data *dto.WSATMessageData) error {
// 根据条件判断是否添加Echo字段
if config.GetTwoWayEcho() {
groupMsg.Echo = echostr
+ //用向应用端(如果支持)发送echo,来确定客户端的send_msg对应的触发词原文
+ echo.AddMsgIDv3(AppIDString, echostr, messageText)
}
// 获取MasterID数组
masterIDs := config.GetMasterID()
diff --git a/Processor/ProcessGuildNormalMessage.go b/Processor/ProcessGuildNormalMessage.go
index 71b66abc..da7c32a6 100644
--- a/Processor/ProcessGuildNormalMessage.go
+++ b/Processor/ProcessGuildNormalMessage.go
@@ -78,6 +78,8 @@ func (p *Processors) ProcessGuildNormalMessage(data *dto.WSMessageData) error {
// 根据条件判断是否添加Echo字段
if config.GetTwoWayEcho() {
onebotMsg.Echo = echostr
+ //用向应用端(如果支持)发送echo,来确定客户端的send_msg对应的触发词原文
+ echo.AddMsgIDv3(AppIDString, echostr, messageText)
}
// 获取MasterID数组
masterIDs := config.GetMasterID()
@@ -225,6 +227,8 @@ func (p *Processors) ProcessGuildNormalMessage(data *dto.WSMessageData) error {
// 根据条件判断是否添加Echo字段
if config.GetTwoWayEcho() {
groupMsg.Echo = echostr
+ //用向应用端(如果支持)发送echo,来确定客户端的send_msg对应的触发词原文
+ echo.AddMsgIDv3(AppIDString, echostr, messageText)
}
// 获取MasterID数组
masterIDs := config.GetMasterID()
diff --git a/botgo/dto/keyboard/keyboard.go b/botgo/dto/keyboard/keyboard.go
index f53b4a74..b6c26149 100644
--- a/botgo/dto/keyboard/keyboard.go
+++ b/botgo/dto/keyboard/keyboard.go
@@ -60,6 +60,10 @@ type Action struct {
ClickLimit uint32 `json:"click_limit,omitempty"` // 可点击的次数, 默认不限
Data string `json:"data,omitempty"` // 操作相关数据
AtBotShowChannelList bool `json:"at_bot_show_channel_list,omitempty"` // false:当前 true:弹出展示子频道选择器
+ UnsupportTips string `json:"unsupport_tips"` //2024-1-12 新增字段
+ AnChor int `json:"anchor"` //本字段仅在指令按钮下有效,设置后后会忽略 action.enter 配置。设置为 1 时 ,点击按钮自动唤起启手Q选图器,其他值暂无效果。(仅支持手机端版本 8983+ 的单聊场景,桌面端不支持)
+ Enter bool `json:"enter"` //指令按钮可用,点击按钮后直接自动发送 data,默认 false。支持版本 8983
+ Reply bool `json:"reply"` //指令按钮可用,指令是否带引用回复本消息,默认 false。支持版本 8983
}
// Permission 按纽操作权限
diff --git a/config/config.go b/config/config.go
index 021ed06f..9f8a7940 100644
--- a/config/config.go
+++ b/config/config.go
@@ -131,6 +131,8 @@ type Settings struct {
WhiteEnable []bool `yaml:"white_enable"`
IdentifyAppids []int64 `yaml:"identify_appids"`
TransFormApiIds bool `yaml:"transform_api_ids"`
+ CustomTemplateID string `yaml:"custom_template_id"`
+ KeyBoardID string `yaml:"keyboard_id"`
}
// LoadConfig 从文件中加载配置并初始化单例配置
@@ -1650,3 +1652,27 @@ func GetTransFormApiIds() bool {
}
return instance.Settings.TransFormApiIds
}
+
+// 获取 CustomTemplateID 的值
+func GetCustomTemplateID() string {
+ mu.Lock()
+ defer mu.Unlock()
+
+ if instance == nil {
+ mylog.Println("Warning: instance is nil when trying to get CustomTemplateID.")
+ return ""
+ }
+ return instance.Settings.CustomTemplateID
+}
+
+// 获取 KeyBoardIDD 的值
+func GetKeyBoardID() string {
+ mu.Lock()
+ defer mu.Unlock()
+
+ if instance == nil {
+ mylog.Println("Warning: instance is nil when trying to get KeyBoardID.")
+ return ""
+ }
+ return instance.Settings.KeyBoardID
+}
diff --git a/echo/echo.go b/echo/echo.go
index c898215a..fc840e4f 100644
--- a/echo/echo.go
+++ b/echo/echo.go
@@ -106,6 +106,15 @@ func AddMsgIDv3(appid string, s string, msgID string) {
globalEchoMapping.msgIDMapping[key] = msgID
}
+// GetMsgIDv3 返回给定appid和s的msgID
+func GetMsgIDv3(appid string, s string) string {
+ key := globalEchoMapping.GenerateKeyv3(appid, s)
+ globalEchoMapping.mu.Lock()
+ defer globalEchoMapping.mu.Unlock()
+
+ return globalEchoMapping.msgIDMapping[key]
+}
+
// 添加group和userid对应的messageid
func AddMsgIDv2(appid string, groupid int64, userid int64, msgID string) {
key := globalEchoMapping.GenerateKeyv2(appid, groupid, userid)
diff --git a/handlers/send_group_msg.go b/handlers/send_group_msg.go
index 9905aada..93b869e7 100644
--- a/handlers/send_group_msg.go
+++ b/handlers/send_group_msg.go
@@ -4,10 +4,12 @@ import (
"bytes"
"context"
"encoding/base64"
+ "fmt"
"io"
"net/http"
"os"
"strconv"
+ "strings"
"time"
"github.com/hoshinonyaruko/gensokyo/callapi"
@@ -18,6 +20,7 @@ import (
"github.com/hoshinonyaruko/gensokyo/mylog"
"github.com/hoshinonyaruko/gensokyo/silk"
"github.com/tencent-connect/botgo/dto"
+ "github.com/tencent-connect/botgo/dto/keyboard"
"github.com/tencent-connect/botgo/openapi"
)
@@ -33,22 +36,18 @@ func HandleSendGroupMsg(client callapi.Client, api openapi.OpenAPI, apiv2 openap
// 当 message.Echo 是字符串类型时执行此块
msgType = echo.GetMsgTypeByKey(echoStr)
}
- //如果获取不到 就用user_id获取信息类型
- if msgType == "" {
- msgType = GetMessageTypeByUserid(config.GetAppIDStr(), message.Params.UserID)
- }
-
- //如果获取不到 就用group_id获取信息类型
if msgType == "" {
msgType = GetMessageTypeByGroupid(config.GetAppIDStr(), message.Params.GroupID)
}
- //新增 内存获取不到从数据库获取
if msgType == "" {
- msgType = GetMessageTypeByUseridV2(message.Params.UserID)
+ msgType = GetMessageTypeByUserid(config.GetAppIDStr(), message.Params.UserID)
}
if msgType == "" {
msgType = GetMessageTypeByGroupidV2(message.Params.GroupID)
}
+ if msgType == "" {
+ msgType = GetMessageTypeByUseridV2(message.Params.UserID)
+ }
mylog.Printf("send_group_msg获取到信息类型:%v", msgType)
var idInt64 int64
var err error
@@ -202,26 +201,52 @@ func HandleSendGroupMsg(client callapi.Client, api openapi.OpenAPI, apiv2 openap
mylog.Printf("Error: Expected RichMediaMessage type for key ")
return "", nil
}
- // 上传图片并获取FileInfo
- fileInfo, err := uploadMedia(context.TODO(), message.Params.GroupID.(string), richMediaMessage, apiv2)
- if err != nil {
- mylog.Printf("上传图片失败: %v", err)
- return "", nil // 或其他错误处理
- }
- // 创建包含文本和图像信息的消息
- msgseq = echo.GetMappingSeq(messageID)
- echo.AddMappingSeq(messageID, msgseq+1)
- groupMessage := &dto.MessageToCreate{
- Content: messageText, // 添加文本内容
- Media: dto.Media{
- FileInfo: fileInfo, // 添加图像信息
- },
- MsgID: messageID,
- MsgSeq: msgseq,
- MsgType: 7, // 假设7是组合消息类型
+ var groupMessage *dto.MessageToCreate
+ var transmd bool
+ var md *dto.Markdown
+ var kb *keyboard.MessageKeyboard
+ //判断是否需要自动转换md
+ if config.GetTwoWayEcho() {
+ md, kb, transmd = auto_md(message, messageText, richMediaMessage)
}
- groupMessage.Timestamp = time.Now().Unix() // 设置时间戳
+ //如果没有转换成md发送
+ if !transmd {
+ // 上传图片并获取FileInfo
+ fileInfo, err := uploadMedia(context.TODO(), message.Params.GroupID.(string), richMediaMessage, apiv2)
+ if err != nil {
+ mylog.Printf("上传图片失败: %v", err)
+ return "", nil // 或其他错误处理
+ }
+ // 创建包含文本和图像信息的消息
+ msgseq = echo.GetMappingSeq(messageID)
+ echo.AddMappingSeq(messageID, msgseq+1)
+ groupMessage = &dto.MessageToCreate{
+ Content: messageText, // 添加文本内容
+ Media: dto.Media{
+ FileInfo: fileInfo, // 添加图像信息
+ },
+ MsgID: messageID,
+ MsgSeq: msgseq,
+ MsgType: 7, // 假设7是组合消息类型
+ }
+ groupMessage.Timestamp = time.Now().Unix() // 设置时间戳
+ } else {
+ //将kb和md组合成groupMessage并用MsgType=2发送
+
+ msgseq = echo.GetMappingSeq(messageID)
+ echo.AddMappingSeq(messageID, msgseq+1)
+ groupMessage = &dto.MessageToCreate{
+ Content: "markdown", // 添加文本内容
+ MsgID: messageID,
+ MsgSeq: msgseq,
+ Markdown: md,
+ Keyboard: kb,
+ MsgType: 2, // 假设7是组合消息类型
+ }
+ groupMessage.Timestamp = time.Now().Unix() // 设置时间戳
+
+ }
// 发送组合消息
ret, err = apiv2.PostGroupMessage(context.TODO(), message.Params.GroupID.(string), groupMessage)
if err != nil {
@@ -1020,3 +1045,111 @@ func SendStackMessages(apiv2 openapi.OpenAPI, messageid string, originalGroupID
}
}
+
+func auto_md(message callapi.ActionMessage, messageText string, richMediaMessage *dto.RichMediaMessage) (md *dto.Markdown, kb *keyboard.MessageKeyboard, transmd bool) {
+ if echoStr, ok := message.Echo.(string); ok {
+ // 当 message.Echo 是字符串类型时才执行此块
+ msg_on_touch := echo.GetMsgIDv3(config.GetAppIDStr(), echoStr)
+ mylog.Printf("msg_on_touch:%v", msg_on_touch)
+ // 判断是否是 GetVisualkPrefixs 数组开头的文本
+ visualkPrefixs := config.GetVisualkPrefixs()
+ var matchedPrefix *config.VisualPrefixConfig
+ // 去掉前缀开头的*
+ for i, vp := range visualkPrefixs {
+ if strings.HasPrefix(vp.Prefix, "*") {
+ visualkPrefixs[i].Prefix = strings.TrimPrefix(vp.Prefix, "*")
+ }
+ }
+
+ for _, vp := range visualkPrefixs {
+ if strings.HasPrefix(msg_on_touch, vp.Prefix) {
+ if len(msg_on_touch) >= len(vp.Prefix) {
+ if msg_on_touch != " " {
+ transmd = true
+ matchedPrefix = &vp
+ break // 匹配到了
+ }
+ }
+ }
+ }
+ if transmd {
+ //将messageText和groupReply组合成一个md
+ // 处理 Markdown
+ CustomTemplateID := config.GetCustomTemplateID()
+ imgURL := richMediaMessage.URL
+ height, width, err := images.GetImageDimensions(imgURL)
+ if err != nil {
+ mylog.Printf("获取图片宽高出错")
+ }
+ imgDesc := fmt.Sprintf("图片 #%dpx #%dpx", width, height)
+ // 检查messageText是否以\r开头
+ if !strings.HasPrefix(messageText, "\r") {
+ messageText = "\r" + messageText
+ }
+ // 创建 MarkdownParams 的实例
+ mdParams := []*dto.MarkdownParams{
+ {Key: "text_start", Values: []string{" "}}, //空着
+ {Key: "img_dec", Values: []string{imgDesc}},
+ {Key: "img_url", Values: []string{imgURL}},
+ {Key: "text_end", Values: []string{messageText}},
+ }
+ // 组合模板 Markdown
+ md = &dto.Markdown{
+ CustomTemplateID: CustomTemplateID,
+ Params: mdParams,
+ }
+ whiteList := matchedPrefix.WhiteList
+ // 创建 CustomKeyboard
+ customKeyboard := &keyboard.CustomKeyboard{
+ Rows: []*keyboard.Row{},
+ }
+
+ var currentRow *keyboard.Row
+ buttonCount := 0 // 当前行的按钮计数
+
+ for _, whiteLabel := range whiteList {
+ // 使用 strconv.Atoi 检查 whiteLabel 是否为纯数字
+ if _, err := strconv.Atoi(whiteLabel); err == nil {
+ // 如果没有错误,表示 whiteLabel 是一个数字,因此忽略这个元素并继续下一个迭代
+ continue
+ }
+
+ // 创建按钮
+ button := &keyboard.Button{
+ RenderData: &keyboard.RenderData{
+ Label: whiteLabel,
+ VisitedLabel: whiteLabel,
+ Style: 1,
+ },
+ Action: &keyboard.Action{
+ Type: 2, // 帮用户输入二级指令
+ Permission: &keyboard.Permission{
+ Type: 2, //所有人可操作
+ },
+ Data: msg_on_touch + " " + whiteLabel,
+ UnsupportTips: "请升级新版手机QQ",
+ },
+ }
+
+ // 如果当前行为空或已满(4个按钮),则创建一个新行
+ if currentRow == nil || buttonCount == 4 {
+ currentRow = &keyboard.Row{}
+ customKeyboard.Rows = append(customKeyboard.Rows, currentRow)
+ buttonCount = 0 // 重置按钮计数
+ }
+
+ // 将按钮添加到当前行
+ currentRow.Buttons = append(currentRow.Buttons, button)
+ buttonCount++
+ }
+
+ // 在循环结束后,最后一行可能不满4个按钮,但已经被正确处理
+
+ // 创建 MessageKeyboard 并设置其 Content
+ kb = &keyboard.MessageKeyboard{
+ Content: customKeyboard,
+ }
+ }
+ }
+ return md, kb, transmd
+}
diff --git a/images/format.go b/images/format.go
new file mode 100644
index 00000000..a5cb61c2
--- /dev/null
+++ b/images/format.go
@@ -0,0 +1,180 @@
+package images
+
+import (
+ "bytes"
+ "encoding/binary"
+ "fmt"
+ "io"
+ "net/http"
+ "strings"
+ "time"
+)
+
+// 宽度 高度
+func GetImageDimensions(url string) (int, int, error) {
+ if strings.HasSuffix(url, ".png") {
+ return getPNGDimensions(url)
+ } else if strings.HasSuffix(url, ".jpg") || strings.HasSuffix(url, ".jpeg") {
+ return getJpegDimensions(url)
+ } else if strings.HasSuffix(url, ".gif") {
+ return getGIFDimensions(url)
+ }
+ return 0, 0, fmt.Errorf("unsupported image format")
+}
+
+func getPNGDimensions(url string) (int, int, error) {
+ resp, err := http.Get(url)
+ if err != nil {
+ return 0, 0, fmt.Errorf("error occurred while making request: %w", err)
+ }
+ defer resp.Body.Close()
+
+ if resp.StatusCode != http.StatusOK {
+ return 0, 0, fmt.Errorf("failed to download image with status code: %d", resp.StatusCode)
+ }
+
+ // 只读取前33字节
+ buffer := make([]byte, 33)
+ _, err = io.ReadFull(resp.Body, buffer)
+ if err != nil {
+ return 0, 0, fmt.Errorf("error occurred while reading the header: %w", err)
+ }
+
+ // 检查PNG文件头
+ if bytes.HasPrefix(buffer, []byte("\x89PNG\r\n\x1a\n")) {
+ var width, height int32
+ reader := bytes.NewReader(buffer[16:24])
+ err := binary.Read(reader, binary.BigEndian, &width)
+ if err != nil {
+ return 0, 0, fmt.Errorf("error occurred while reading width: %w", err)
+ }
+ err = binary.Read(reader, binary.BigEndian, &height)
+ if err != nil {
+ return 0, 0, fmt.Errorf("error occurred while reading height: %w", err)
+ }
+ return int(width), int(height), nil
+ }
+
+ width, height, err := getJpegDimensions(url)
+ return width, height, err
+}
+
+func getJpegDimensions(url string) (int, int, error) {
+ response, err := http.Get(url)
+ if err != nil {
+ return 0, 0, fmt.Errorf("error occurred while making request: %w", err)
+ }
+ defer response.Body.Close()
+
+ if response.StatusCode != 200 {
+ return 0, 0, fmt.Errorf("failed to download image with status code: %d", response.StatusCode)
+ }
+
+ bytesRead := 0 // 用于跟踪读取的字节数
+ for {
+ b := make([]byte, 1)
+ _, err := response.Body.Read(b)
+ bytesRead++ // 更新读取的字节数
+ if err == io.EOF {
+ return 0, 0, fmt.Errorf("reached end of file before finding dimensions")
+ } else if err != nil {
+ return 0, 0, fmt.Errorf("error occurred while reading byte: %w", err)
+ }
+
+ if b[0] == 0xFF {
+ nextByte := make([]byte, 1)
+ _, err := response.Body.Read(nextByte)
+ if err == io.EOF {
+ return 0, 0, fmt.Errorf("reached end of file before finding SOI marker")
+ } else if err != nil {
+ return 0, 0, fmt.Errorf("error occurred while reading next byte: %w", err)
+ }
+
+ if nextByte[0] == 0xD8 { // SOI
+ continue
+ } else if (nextByte[0] >= 0xC0 && nextByte[0] <= 0xCF) && nextByte[0] != 0xC4 && nextByte[0] != 0xC8 && nextByte[0] != 0xCC {
+ c := make([]byte, 3)
+ _, err := response.Body.Read(c)
+ if err != nil {
+ return 0, 0, fmt.Errorf("error occurred while skipping bytes: %w", err)
+ }
+ heightBytes := make([]byte, 2)
+ widthBytes := make([]byte, 2)
+ _, err = response.Body.Read(heightBytes)
+ if err != nil {
+ return 0, 0, fmt.Errorf("error occurred while reading height: %w", err)
+ }
+ _, err = response.Body.Read(widthBytes)
+ if err != nil {
+ return 0, 0, fmt.Errorf("error occurred while reading width: %w", err)
+ }
+ // 使用binary.BigEndian.Uint16将字节转换为uint16
+ height := binary.BigEndian.Uint16(heightBytes)
+ width := binary.BigEndian.Uint16(widthBytes)
+ return int(height), int(width), nil
+ } else {
+ time.Sleep(5 * time.Millisecond)
+ lengthBytes := make([]byte, 2)
+ _, err := response.Body.Read(lengthBytes)
+ if err != nil {
+ return 0, 0, fmt.Errorf("error occurred while reading segment length: %w", err)
+ }
+
+ length := binary.BigEndian.Uint16(lengthBytes)
+ bytesToSkip := int(length) - 2
+
+ // 循环读取并跳过指定的字节数
+ for bytesToSkip > 0 {
+ bufferSize := bytesToSkip
+ if bufferSize > 512 { // 可以调整这个值以优化性能和内存使用
+ bufferSize = 512 //如果成功率低 就继续减少它
+ }
+ buffer := make([]byte, bufferSize)
+ n, err := response.Body.Read(buffer)
+ if err != nil {
+ if err == io.EOF {
+ break // 数据流结束
+ }
+ return 0, 0, fmt.Errorf("error occurred while skipping segment: %w", err)
+ }
+ bytesToSkip -= n
+ }
+ }
+ }
+ }
+}
+
+func getGIFDimensions(url string) (int, int, error) {
+ resp, err := http.Get(url)
+ if err != nil {
+ return 0, 0, fmt.Errorf("error occurred while making request: %w", err)
+ }
+ defer resp.Body.Close()
+
+ if resp.StatusCode != http.StatusOK {
+ return 0, 0, fmt.Errorf("failed to download image with status code: %d", resp.StatusCode)
+ }
+
+ // 仅读取前10字节
+ buffer := make([]byte, 10)
+ _, err = io.ReadFull(resp.Body, buffer)
+ if err != nil {
+ return 0, 0, fmt.Errorf("error occurred while reading the header: %w", err)
+ }
+
+ if bytes.HasPrefix(buffer, []byte("GIF")) {
+ var width, height int16
+ reader := bytes.NewReader(buffer[6:10])
+ err := binary.Read(reader, binary.LittleEndian, &width)
+ if err != nil {
+ return 0, 0, fmt.Errorf("error occurred while reading width: %w", err)
+ }
+ err = binary.Read(reader, binary.LittleEndian, &height)
+ if err != nil {
+ return 0, 0, fmt.Errorf("error occurred while reading height: %w", err)
+ }
+ return int(width), int(height), nil
+ }
+
+ return 0, 0, fmt.Errorf("not a valid GIF file")
+}
diff --git a/template/config_template.go b/template/config_template.go
index f01a81d8..bec2f464 100644
--- a/template/config_template.go
+++ b/template/config_template.go
@@ -128,6 +128,8 @@ settings:
custom_bot_name : "Gensokyo全域机器人" #自定义机器人名字,会在api调用中返回,默认Gensokyo全域机器人
twoway_echo : false #是否采用双向echo,根据机器人选择,獭獭\早苗 true 红色问答\椛椛 或者其他 请使用 false
+ custom_template_id : "" #自动转换图文信息到md所需要的id *需要应用端支持双方向echo
+ keyboard_id : "" #自动转换图文信息到md所需要的按钮id *需要应用端支持双方向echo
lazy_message_id : false #false=message_id 条条准确对应 true=message_id 按时间范围随机对应(适合主动推送bot)前提,有足够多的活跃信息刷新id池
visible_ip : false #转换url时,如果server_dir是ip true将以ip形式发出url 默认隐藏url 将server_dir配置为自己域名可以转换url
From 5aaf4208df4ed0dca3bff80b24bac52e56351208 Mon Sep 17 00:00:00 2001
From: cosmo
Date: Mon, 15 Jan 2024 11:54:49 +0800
Subject: [PATCH 08/13] beta129
---
Processor/ProcessGroupMessage.go | 2 +
Processor/ProcessGuildATMessage.go | 4 +
Processor/ProcessGuildNormalMessage.go | 4 +
Processor/Processor.go | 76 ++++++++++--
botgo/dto/interaction.go | 34 ++++--
botgo/dto/keyboard/keyboard.go | 8 +-
config/config.go | 35 ++++++
handlers/message_parser.go | 4 +-
handlers/send_group_msg.go | 83 +++++++++++--
main.go | 3 +-
template/config_template.go | 9 +-
wsclient/ws.go | 158 +++++++++++++++++--------
12 files changed, 329 insertions(+), 91 deletions(-)
diff --git a/Processor/ProcessGroupMessage.go b/Processor/ProcessGroupMessage.go
index 87d2e8a1..91ece57a 100644
--- a/Processor/ProcessGroupMessage.go
+++ b/Processor/ProcessGroupMessage.go
@@ -169,6 +169,8 @@ func (p *Processors) ProcessGroupMessage(data *dto.WSGroupATMessageData) error {
echo.AddMsgType(AppIDString, GroupID64, "group")
//懒message_id池
echo.AddLazyMessageId(strconv.FormatInt(GroupID64, 10), data.ID, time.Now())
+ //懒message_id池
+ echo.AddLazyMessageId(strconv.FormatInt(userid64, 10), data.ID, time.Now())
// 调试
PrintStructWithFieldNames(groupMsg)
diff --git a/Processor/ProcessGuildATMessage.go b/Processor/ProcessGuildATMessage.go
index c9426919..6bd646d8 100644
--- a/Processor/ProcessGuildATMessage.go
+++ b/Processor/ProcessGuildATMessage.go
@@ -111,6 +111,8 @@ func (p *Processors) ProcessGuildATMessage(data *dto.WSATMessageData) error {
//todo 完善频道转换
//懒message_id池
echo.AddLazyMessageId(data.ChannelID, data.ID, time.Now())
+ //懒message_id池
+ echo.AddLazyMessageId(strconv.FormatInt(userid64, 10), data.ID, time.Now())
//调试
PrintStructWithFieldNames(onebotMsg)
@@ -264,6 +266,8 @@ func (p *Processors) ProcessGuildATMessage(data *dto.WSATMessageData) error {
echo.AddMsgType(AppIDString, ChannelID64, "guild")
//懒message_id池
echo.AddLazyMessageId(strconv.FormatInt(ChannelID64, 10), data.ID, time.Now())
+ //懒message_id池
+ echo.AddLazyMessageId(strconv.FormatInt(userid64, 10), data.ID, time.Now())
//调试
PrintStructWithFieldNames(groupMsg)
diff --git a/Processor/ProcessGuildNormalMessage.go b/Processor/ProcessGuildNormalMessage.go
index da7c32a6..1c280e7e 100644
--- a/Processor/ProcessGuildNormalMessage.go
+++ b/Processor/ProcessGuildNormalMessage.go
@@ -111,6 +111,8 @@ func (p *Processors) ProcessGuildNormalMessage(data *dto.WSMessageData) error {
//todo 完善频道ob信息
//懒message_id池
echo.AddLazyMessageId(data.ChannelID, data.ID, time.Now())
+ //懒message_id池
+ echo.AddLazyMessageId(strconv.FormatInt(userid64, 10), data.ID, time.Now())
//调试
PrintStructWithFieldNames(onebotMsg)
@@ -272,6 +274,8 @@ func (p *Processors) ProcessGuildNormalMessage(data *dto.WSMessageData) error {
echo.AddMsgType(AppIDString, ChannelID64, "guild")
//懒message_id池
echo.AddLazyMessageId(strconv.FormatInt(ChannelID64, 10), data.ID, time.Now())
+ //懒message_id池
+ echo.AddLazyMessageId(strconv.FormatInt(userid64, 10), data.ID, time.Now())
//调试
PrintStructWithFieldNames(groupMsg)
diff --git a/Processor/Processor.go b/Processor/Processor.go
index b95d58ba..cc104dca 100644
--- a/Processor/Processor.go
+++ b/Processor/Processor.go
@@ -112,6 +112,18 @@ type OnebotPrivateMessage struct {
IsBindedUserId bool `json:"is_binded_user_id,omitempty"` //当前用户号号是否是binded后的
}
+// onebotv11标准扩展
+type OnebotInteractionNotice struct {
+ GroupID int64 `json:"group_id,omitempty"`
+ NoticeType string `json:"notice_type,omitempty"`
+ PostType string `json:"post_type,omitempty"`
+ SelfID int64 `json:"self_id,omitempty"`
+ SubType string `json:"sub_type,omitempty"`
+ Time int64 `json:"time,omitempty"`
+ UserID int64 `json:"user_id,omitempty"`
+ Data *dto.WSInteractionData `json:"data,omitempty"`
+}
+
type PrivateSender struct {
Nickname string `json:"nickname"`
UserID int64 `json:"user_id"` // Can be either string or int depending on logic
@@ -123,19 +135,63 @@ func FoxTimestamp() int64 {
// ProcessInlineSearch 处理内联查询
func (p *Processors) ProcessInlineSearch(data *dto.WSInteractionData) error {
- //ctx := context.Background() // 或从更高级别传递一个上下文
+ // 转换appid
+ var userid64 int64
+ var GroupID64 int64
+ var err error
+ var fromgid, fromuid string
+ if data.GroupOpenID != "" {
+ fromgid = data.GroupOpenID
+ fromuid = data.GroupMemberOpenID
+ } else {
+ fromgid = data.ChannelID
+ fromuid = "0"
+ }
+ if config.GetIdmapPro() {
+ //将真实id转为int userid64
+ GroupID64, userid64, err = idmap.StoreIDv2Pro(fromgid, fromuid)
+ if err != nil {
+ mylog.Fatalf("Error storing ID: %v", err)
+ }
+ //当参数不全
+ _, _ = idmap.StoreIDv2(fromgid)
+ _, _ = idmap.StoreIDv2(fromuid)
+ if !config.GetHashIDValue() {
+ mylog.Fatalf("避坑日志:你开启了高级id转换,请设置hash_id为true,并且删除idmaps并重启")
+ }
+ } else {
+ // 映射str的GroupID到int
+ GroupID64, err = idmap.StoreIDv2(fromgid)
+ if err != nil {
+ mylog.Errorf("failed to convert ChannelID to int: %v", err)
+ return nil
+ }
+ // 映射str的userid到int
+ userid64, err = idmap.StoreIDv2(fromuid)
+ if err != nil {
+ mylog.Printf("Error storing ID: %v", err)
+ return nil
+ }
+ }
+ notice := &OnebotInteractionNotice{
+ GroupID: GroupID64,
+ NoticeType: "interaction",
+ PostType: "notice",
+ SelfID: int64(p.Settings.AppID),
+ SubType: "create",
+ Time: time.Now().Unix(),
+ UserID: userid64,
+ Data: data,
+ }
- // 在这里处理内联查询
- // 这可能涉及解析查询、调用某些API、获取结果并格式化为响应
- // ...
+ //调试
+ PrintStructWithFieldNames(notice)
- // 示例:发送响应
- // response := "Received your interaction!" // 创建响应消息
- // err := p.api.PostInteractionResponse(ctx, response) // 替换为您的OpenAPI方法
- // if err != nil {
- // return err
- // }
+ // Convert OnebotGroupMessage to map and send
+ noticeMap := structToMap(notice)
+ //上报信息到onebotv11应用端(正反ws)
+ p.BroadcastMessageToAll(noticeMap)
return nil
}
diff --git a/botgo/dto/interaction.go b/botgo/dto/interaction.go
index 9d633c5b..31544046 100644
--- a/botgo/dto/interaction.go
+++ b/botgo/dto/interaction.go
@@ -1,16 +1,20 @@
package dto
-import "encoding/json"
-
// Interaction 互动行为对象
type Interaction struct {
- ID string `json:"id,omitempty"` // 互动行为唯一标识
- ApplicationID string `json:"application_id,omitempty"` // 应用ID
- Type InteractionType `json:"type,omitempty"` // 互动类型
- Data *InteractionData `json:"data,omitempty"` // 互动数据
- GuildID string `json:"guild_id,omitempty"` // 频道 ID
- ChannelID string `json:"channel_id,omitempty"` // 子频道 ID
- Version uint32 `json:"version,omitempty"` // 版本,默认为 1
+ ID string `json:"id,omitempty"` // 平台方事件 ID
+ Type InteractionType `json:"type,omitempty"` // 消息按钮: 11, 单聊快捷菜单: 12
+ Scene string `json:"scene,omitempty"` // 事件发生的场景
+ ChatType int `json:"chat_type,omitempty"` // 频道场景: 0, 群聊场景: 1, 单聊场景: 2
+ Timestamp string `json:"timestamp,omitempty"` // 触发时间 RFC 3339 格式
+ GuildID string `json:"guild_id,omitempty"` // 频道的 openid
+ ChannelID string `json:"channel_id,omitempty"` // 文字子频道的 openid
+ UserOpenID string `json:"user_openid,omitempty"` // 单聊按钮触发的用户 openid
+ GroupOpenID string `json:"group_openid,omitempty"` // 群的 openid
+ GroupMemberOpenID string `json:"group_member_openid,omitempty"` // 群成员 openid
+ Data *InteractionData `json:"data,omitempty"` // 互动数据
+ Version uint32 `json:"version,omitempty"` // 版本,默认为 1
+ ApplicationID string `json:"application_id,omitempty"` // 机器人的 appid
}
// InteractionType 互动类型
@@ -25,9 +29,15 @@ const (
// InteractionData 互动数据
type InteractionData struct {
- Name string `json:"name,omitempty"` // 标题
- Type InteractionDataType `json:"type,omitempty"` // 数据类型,不同数据类型对应不同的 resolved 数据
- Resolved json.RawMessage `json:"resolved,omitempty"` // 跟不同的互动类型和数据类型有关系的数据
+ Name string `json:"name,omitempty"` // 标题
+ Type InteractionDataType `json:"type,omitempty"` // 数据类型
+ Resolved struct {
+ ButtonData string `json:"button_data,omitempty"` // 操作按钮的 data 字段值
+ ButtonID string `json:"button_id,omitempty"` // 操作按钮的 id 字段值
+ UserID string `json:"user_id,omitempty"` // 操作的用户 userid
+ FeatureID string `json:"feature_id,omitempty"` // 操作按钮的 feature id
+ MessageID string `json:"message_id,omitempty"` // 操作的消息 id
+ } `json:"resolved,omitempty"`
}
// InteractionDataType 互动数据类型
diff --git a/botgo/dto/keyboard/keyboard.go b/botgo/dto/keyboard/keyboard.go
index b6c26149..c1087fb5 100644
--- a/botgo/dto/keyboard/keyboard.go
+++ b/botgo/dto/keyboard/keyboard.go
@@ -55,7 +55,7 @@ type RenderData struct {
// Action 按纽点击操作
type Action struct {
- Type ActionType `json:"type,omitempty"` // 操作类型
+ Type ActionType `json:"type,omitempty"` // 操作类型 设置 0 跳转按钮:http 或 小程序 客户端识别 scheme,设置 1 回调按钮:回调后台接口, data 传给后台,设置 2 指令按钮:自动在输入框插入 @bot data
Permission *Permission `json:"permission,omitempty"` // 可操作
ClickLimit uint32 `json:"click_limit,omitempty"` // 可点击的次数, 默认不限
Data string `json:"data,omitempty"` // 操作相关数据
@@ -68,10 +68,10 @@ type Action struct {
// Permission 按纽操作权限
type Permission struct {
- // Type 操作权限类型
+ // Type 操作权限类型 0 指定用户可操作,1 仅管理者可操作,2 所有人可操作,3 指定身份组可操作(仅频道可用)
Type PermissionType `json:"type,omitempty"`
- // SpecifyRoleIDs 身份组
+ // SpecifyRoleIDs 身份组(仅频道可用)
SpecifyRoleIDs []string `json:"specify_role_ids,omitempty"`
- // SpecifyUserIDs 指定 UserID
+ // SpecifyUserIDs 指定 UserID 有权限的用户 id 的列表
SpecifyUserIDs []string `json:"specify_user_ids,omitempty"`
}
diff --git a/config/config.go b/config/config.go
index 9f8a7940..78ddf764 100644
--- a/config/config.go
+++ b/config/config.go
@@ -133,6 +133,9 @@ type Settings struct {
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"`
}
// LoadConfig 从文件中加载配置并初始化单例配置
@@ -1676,3 +1679,35 @@ func GetKeyBoardID() string {
}
return instance.Settings.KeyBoardID
}
+
+// 获取Uin String
+func GetUinStr() string {
+ mu.Lock()
+ defer mu.Unlock()
+ if instance != nil {
+ return fmt.Sprintf("%d", instance.Settings.Uin)
+ }
+ return "0"
+}
+
+// 获取 VV GetVwhitePrefixMode 的值
+func GetVwhitePrefixMode() bool {
+ mu.Lock()
+ defer mu.Unlock()
+
+ if instance == nil {
+ mylog.Println("Warning: instance is nil when trying to VwhitePrefixMode value.")
+ return false
+ }
+ return instance.Settings.VwhitePrefixMode
+}
+
+// 获取Enters的值
+func GetEnters() []string {
+ mu.Lock()
+ defer mu.Unlock()
+ if instance != nil {
+ return instance.Settings.Enters
+ }
+ return nil // 返回nil,如果instance为nil
+}
diff --git a/handlers/message_parser.go b/handlers/message_parser.go
index 4298ce71..48e3519a 100644
--- a/handlers/message_parser.go
+++ b/handlers/message_parser.go
@@ -503,8 +503,8 @@ func RevertTransformedText(data interface{}, msgtype string, api openapi.OpenAPI
aliases := config.GetAlias()
messageText = processMessageText(messageText, aliases)
//mylog.Printf("4[%v]", messageText)
- // 检查是否启用白名单模式
- if config.GetWhitePrefixMode() && matchedPrefix != nil {
+ // 检查是否启用二级白名单模式
+ if config.GetVwhitePrefixMode() && matchedPrefix != nil {
// 获取白名单反转标志
whiteBypassRevers := config.GetWhiteBypassRevers()
diff --git a/handlers/send_group_msg.go b/handlers/send_group_msg.go
index 93b869e7..651827d6 100644
--- a/handlers/send_group_msg.go
+++ b/handlers/send_group_msg.go
@@ -85,6 +85,11 @@ func HandleSendGroupMsg(client callapi.Client, api openapi.OpenAPI, apiv2 openap
//由于实现了Params的自定义unmarshell 所以可以类型安全的断言为string
messageID = echo.GetLazyMessagesId(message.Params.GroupID.(string))
mylog.Printf("GetLazyMessagesId: %v", messageID)
+ //如果应用端传递了user_id 就让at不要顺序乱套
+ if message.Params.UserID != nil {
+ messageID = echo.GetLazyMessagesId(message.Params.UserID.(string))
+ mylog.Printf("GetLazyMessagesIdv2: %v", messageID)
+ }
if messageID != "" {
//尝试发送栈内信息
SSM = true
@@ -1082,6 +1087,10 @@ func auto_md(message callapi.ActionMessage, messageText string, richMediaMessage
mylog.Printf("获取图片宽高出错")
}
imgDesc := fmt.Sprintf("图片 #%dpx #%dpx", width, height)
+ // 将所有的\r\n替换为\r
+ messageText = strings.ReplaceAll(messageText, "\r\n", "\r")
+ // 将所有的\n替换为\r
+ messageText = strings.ReplaceAll(messageText, "\n", "\r")
// 检查messageText是否以\r开头
if !strings.HasPrefix(messageText, "\r") {
messageText = "\r" + messageText
@@ -1108,26 +1117,64 @@ func auto_md(message callapi.ActionMessage, messageText string, richMediaMessage
buttonCount := 0 // 当前行的按钮计数
for _, whiteLabel := range whiteList {
- // 使用 strconv.Atoi 检查 whiteLabel 是否为纯数字
- if _, err := strconv.Atoi(whiteLabel); err == nil {
- // 如果没有错误,表示 whiteLabel 是一个数字,因此忽略这个元素并继续下一个迭代
- continue
+ // 如果whiteList的成员数大于或等于15,才检查whiteLabel是否为纯数字
+ if len(whiteList) >= 15 {
+ if _, err := strconv.Atoi(whiteLabel); err == nil {
+ // 如果没有错误,表示 whiteLabel 是一个数字,因此忽略这个元素并继续下一个迭代
+ // 避免 因为纯数字按钮太多导致混乱,但是少量的纯数字按钮可以允许
+ continue
+ }
+ }
+ // 检查msg_on_touch是否已经以whiteLabel结尾
+ //场景 按钮递归时 比如 随机meme 再来一次,同时随机meme是*类型一级指令
+ var dataLabel string
+ if !strings.HasSuffix(msg_on_touch, whiteLabel) {
+ dataLabel = msg_on_touch + whiteLabel
+ } else {
+ dataLabel = msg_on_touch
}
+ var actiontype keyboard.ActionType
+ var permission *keyboard.Permission
+ var actiondata string
+ //检查是否设置了enter数组
+ enter := checkDataLabelPrefix(dataLabel)
+ switch whiteLabel {
+ case "邀请机器人":
+ botuin := config.GetUinStr()
+ botappid := config.GetAppIDStr()
+ boturl := BuildQQBotShareLink(botuin, botappid)
+ actiontype = 0
+ actiondata = boturl
+ permission = &keyboard.Permission{
+ Type: 2, // 所有人可操作
+ }
+ case "测试按钮回调":
+ actiontype = 1
+ actiondata = "收到就代表是管理员哦"
+ permission = &keyboard.Permission{
+ Type: 1, // 仅管理可操作
+ }
+ default:
+ actiontype = 2 //帮用户输入指令
+ actiondata = dataLabel //从虚拟前缀的二级指令组合md按钮
+ permission = &keyboard.Permission{
+ Type: 2, // 所有人可操作
+ }
+ }
// 创建按钮
button := &keyboard.Button{
RenderData: &keyboard.RenderData{
Label: whiteLabel,
VisitedLabel: whiteLabel,
- Style: 1,
+ Style: 1, //蓝色边缘
},
Action: &keyboard.Action{
- Type: 2, // 帮用户输入二级指令
- Permission: &keyboard.Permission{
- Type: 2, //所有人可操作
- },
- Data: msg_on_touch + " " + whiteLabel,
+ Type: actiontype,
+ Permission: permission,
+ Data: actiondata,
UnsupportTips: "请升级新版手机QQ",
+ Enter: enter,
},
}
@@ -1153,3 +1200,19 @@ func auto_md(message callapi.ActionMessage, messageText string, richMediaMessage
}
return md, kb, transmd
}
+
+// 构建QQ群机器人分享链接的函数
+func BuildQQBotShareLink(uin string, appid string) string {
+ return fmt.Sprintf("https://qun.qq.com/qunpro/robot/qunshare?robot_uin=%s&robot_appid=%s", uin, appid)
+}
+
+// 检查dataLabel是否以config中getenters返回的任一字符串开头
+func checkDataLabelPrefix(dataLabel string) bool {
+ enters := config.GetEnters()
+ for _, enter := range enters {
+ if enter != "" && strings.HasPrefix(dataLabel, enter) {
+ return true
+ }
+ }
+ return false
+}
diff --git a/main.go b/main.go
index e45c8c6a..b2ca9fdd 100644
--- a/main.go
+++ b/main.go
@@ -523,7 +523,6 @@ func DirectMessageHandler() event.DirectMessageEventHandler {
// CreateMessageHandler 处理消息事件 私域的事件 不at信息
func CreateMessageHandler() event.MessageEventHandler {
return func(event *dto.WSPayload, data *dto.WSMessageData) error {
- log.Println("收到私域信息", data)
return p.ProcessGuildNormalMessage(data)
}
}
@@ -531,7 +530,7 @@ func CreateMessageHandler() event.MessageEventHandler {
// InteractionHandler 处理内联交互事件
func InteractionHandler() event.InteractionEventHandler {
return func(event *dto.WSPayload, data *dto.WSInteractionData) error {
- log.Println(data)
+ mylog.Printf("收到按钮回调:%v", data)
return p.ProcessInlineSearch(data)
}
}
diff --git a/template/config_template.go b/template/config_template.go
index bec2f464..cda74bc4 100644
--- a/template/config_template.go
+++ b/template/config_template.go
@@ -6,7 +6,8 @@ settings:
#反向ws设置
ws_address: ["ws://:"] # WebSocket服务的地址 支持多个["","",""]
ws_token: ["","",""] #连接wss地址时服务器所需的token,按顺序一一对应,如果是ws地址,没有密钥,请留空.
- app_id: 12345 # 你的应用ID
+ app_id: 12345 # 你的应用ID
+ uin : 0 # 你的机器人QQ号,点击机器人资料卡查看
token: "" # 你的应用令牌
client_secret: "" # 你的客户端密钥
@@ -19,7 +20,7 @@ settings:
# - "MemberEventHandler" # 频道成员新增
# - "ChannelEventHandler" # 频道事件
# - "CreateMessageHandler" # 频道不at信息 私域机器人需要开启 公域机器人开启会连接失败
- # - "InteractionHandler" # 添加频道互动回应
+ # - "InteractionHandler" # 添加频道互动回应 卡片按钮data回调事件
# - "GroupATMessageEventHandler" # 群at信息 仅频道机器人时候需要注释
# - "C2CMessageEventHandler" # 群私聊 仅频道机器人时候需要注释
# - "ThreadEventHandler" # 发帖事件 (当前版本已禁用)
@@ -92,6 +93,7 @@ settings:
add_at_group : false #自动在群聊指令前加上at,某些机器人写法特别,必须有at才反应时,请打开,默认请关闭(如果需要at,不需要at指令混杂,请优化代码适配群场景,群场景目前没有at概念)
white_prefix_mode : false #公域 过审用 指令白名单模式开关 如果审核严格 请开启并设置白名单指令 以白名单开头的指令会被通过,反之被拦截
+ v_white_prefix_mode : true #虚拟二级指令(虚拟前缀)的白名单,默认开启,有二级指令帮助的效果
white_prefixs : [""] #可设置多个 比如设置 机器人 测试 则只有信息以机器人 测试开头会相应 remove_prefix remove_at 需为true时生效
white_bypass : [] #格式[1,2,3],白名单不生效的群或用户(私聊时),用于设置自己的灰度沙箱群/灰度沙箱私聊,避免开发测试时反复开关白名单的不便,请勿用于生产环境.
white_enable : [true,true,true,true,true] #指令白名单生效范围,5个分别对应,频道公(ATMessageEventHandler),频道私(CreateMessageHandler),频道私聊,群,群私聊,改成false,这个范围就不生效指令白名单(使用场景:群全量,频道私域的机器人,或有私信资质的机器人)
@@ -101,11 +103,12 @@ settings:
black_prefix_mode : false #公私域 过审用 指令黑名单模式开关 过滤被审核打回的指令不响应 无需改机器人后端
black_prefixs : [""] #可设置多个 比如设置 查询 则查询开头的信息均被拦截 防止审核失败
alias : ["",""] #两两成对,指令替换,"a","b","c","d"代表将a开头替换为b开头,c开头替换为d开头.
+ enters : ["",""] #自动md卡片点击直接触发,小众功能,满足以下条件:应用端支持双向echo+设置了visual_prefixs和whiteList
visual_prefixs : #虚拟前缀 与white_prefixs配合使用 处理流程自动忽略该前缀 remove_prefix remove_at 需为true时生效
- prefix: "" #虚拟前缀开头 例 你有3个指令 帮助 测试 查询 将 prefix 设置为 工具类 后 则可通过 工具类 帮助 触发机器人
whiteList: [""] #开关状态取决于 white_prefix_mode 为每一个二级指令头设计独立的白名单
- No_White_Response : ""
+ No_White_Response : "" #带有*代表不忽略掉,但是应用二级白名单的普通指令 如果 whiteList=邀请机器人 就是邀请机器人按钮
- prefix: ""
whiteList: [""]
No_White_Response : ""
diff --git a/wsclient/ws.go b/wsclient/ws.go
index b5eff231..5227ffa2 100644
--- a/wsclient/ws.go
+++ b/wsclient/ws.go
@@ -25,13 +25,14 @@ type WebSocketClient struct {
cancel context.CancelFunc
mutex sync.Mutex // 用于同步写入和重连操作的互斥锁
isReconnecting bool
- sendFailures chan map[string]interface{}
+ sendFailures []map[string]interface{} // 存储失败的消息
+
}
// 发送json信息给onebot应用端
-func (c *WebSocketClient) SendMessage(message map[string]interface{}) error {
- c.mutex.Lock() // 在写操作之前锁定
- defer c.mutex.Unlock() // 确保在函数返回时解锁
+func (client *WebSocketClient) SendMessage(message map[string]interface{}) error {
+ client.mutex.Lock() // 在写操作之前锁定
+ defer client.mutex.Unlock() // 确保在函数返回时解锁
msgBytes, err := json.Marshal(message)
if err != nil {
@@ -39,13 +40,11 @@ func (c *WebSocketClient) SendMessage(message map[string]interface{}) error {
return err
}
- err = c.conn.WriteMessage(websocket.TextMessage, msgBytes)
+ err = client.conn.WriteMessage(websocket.TextMessage, msgBytes)
if err != nil {
mylog.Println("Error sending message:", err)
- // 发送失败,将消息放入channel
- go func() {
- c.sendFailures <- message
- }()
+ // 发送失败,将消息添加到切片
+ client.sendFailures = append(client.sendFailures, message)
return err
}
@@ -53,72 +52,133 @@ func (c *WebSocketClient) SendMessage(message map[string]interface{}) error {
}
// 处理onebotv11应用端发来的信息
-func (c *WebSocketClient) handleIncomingMessages(ctx context.Context, cancel context.CancelFunc) {
+func (client *WebSocketClient) handleIncomingMessages(ctx context.Context, cancel context.CancelFunc) {
for {
- _, msg, err := c.conn.ReadMessage()
+ _, msg, err := client.conn.ReadMessage()
if err != nil {
mylog.Println("WebSocket connection closed:", err)
cancel() // 取消心跳 goroutine
- if !c.isReconnecting {
- go c.Reconnect()
+ if !client.isReconnecting {
+ go client.Reconnect()
}
return // 退出循环,不再尝试读取消息
}
- go c.recvMessage(msg)
+ go client.recvMessage(msg)
}
}
// 断线重连
func (client *WebSocketClient) Reconnect() {
- client.mutex.Lock()
- if client.isReconnecting {
- client.mutex.Unlock()
- return // 如果已经有其他携程在重连了,就直接返回
+ client.isReconnecting = true
+
+ addresses := config.GetWsAddress()
+ tokens := config.GetWsToken()
+
+ var token string
+ for index, address := range addresses {
+ if address == client.urlStr && index < len(tokens) {
+ token = tokens[index]
+ break
+ }
}
- // 暂存旧的 sendFailures channel,不要关闭它
- oldSendFailures := client.sendFailures
+ // 检查URL中是否有access_token参数
+ mp := getParamsFromURI(client.urlStr)
+ if val, ok := mp["access_token"]; ok {
+ token = val
+ }
- client.isReconnecting = true
- client.mutex.Unlock()
+ headers := http.Header{
+ "User-Agent": []string{"CQHttp/4.15.0"},
+ "X-Client-Role": []string{"Universal"},
+ "X-Self-ID": []string{fmt.Sprintf("%d", client.botID)},
+ }
+
+ if token != "" {
+ headers["Authorization"] = []string{"Token " + token}
+ }
+ mylog.Printf("准备使用token[%s]重新连接到[%s]\n", token, client.urlStr)
+ dialer := websocket.Dialer{
+ Proxy: http.ProxyFromEnvironment,
+ HandshakeTimeout: 45 * time.Second,
+ }
+
+ var conn *websocket.Conn
+ var err error
+
+ maxRetryAttempts := config.GetReconnecTimes()
+ retryCount := 0
+ for {
+ mylog.Println("Dialing URL:", client.urlStr)
+ conn, _, err = dialer.Dial(client.urlStr, headers)
+ if err != nil {
+ retryCount++
+ if retryCount > maxRetryAttempts {
+ mylog.Printf("Exceeded maximum retry attempts for WebSocket[%v]: %v\n", client.urlStr, err)
+ return
+ }
+ mylog.Printf("Failed to connect to WebSocket[%v]: %v, retrying in 5 seconds...\n", client.urlStr, err)
+ time.Sleep(5 * time.Second) // sleep for 5 seconds before retrying
+ } else {
+ mylog.Printf("Successfully connected to %s.\n", client.urlStr) // 输出连接成功提示
+ break // successfully connected, break the loop
+ }
+ }
+ // 复用现有的client完成重连
+ client.conn = conn
+
+ // 再次发送元事件
+ message := map[string]interface{}{
+ "meta_event_type": "lifecycle",
+ "post_type": "meta_event",
+ "self_id": client.botID,
+ "sub_type": "connect",
+ "time": int(time.Now().Unix()),
+ }
+
+ mylog.Printf("Message: %+v\n", message)
+
+ err = client.SendMessage(message)
+ if err != nil {
+ // handle error
+ mylog.Printf("Error sending message: %v\n", err)
+ }
+
+ //退出老的sendHeartbeat和handleIncomingMessages
+ client.cancel()
+
+ // Starting goroutine for heartbeats and another for listening to messages
+ ctx, cancel := context.WithCancel(context.Background())
+
+ client.cancel = cancel
+ heartbeatinterval := config.GetHeartBeatInterval()
+ go client.sendHeartbeat(ctx, client.botID, heartbeatinterval)
+ go client.handleIncomingMessages(ctx, cancel)
defer func() {
- client.mutex.Lock()
client.isReconnecting = false
- client.mutex.Unlock()
}()
- reconnecttimes := config.GetReconnecTimes()
- newClient, err := NewWebSocketClient(client.urlStr, client.botID, client.api, client.apiv2, reconnecttimes)
- if err == nil && newClient != nil {
- client.mutex.Lock() // 在替换连接之前锁定
- client.cancel() // 退出旧的连接
- client.conn = newClient.conn
- client.api = newClient.api
- client.apiv2 = newClient.apiv2
- client.cancel = newClient.cancel // 更新取消函数
- client.mutex.Unlock()
- // 重发失败的消息
- newClient.processFailedMessages(oldSendFailures)
- mylog.Println("Successfully reconnected to WebSocket.")
- return
- }
+
+ mylog.Printf("Successfully reconnected to WebSocket.")
}
// 处理发送失败的消息
-func (client *WebSocketClient) processFailedMessages(failuresChan chan map[string]interface{}) {
- for failedMessage := range failuresChan {
+func (client *WebSocketClient) processFailedMessages() {
+ for _, failedMessage := range client.sendFailures {
+ // 尝试重新发送消息
err := client.SendMessage(failedMessage)
if err != nil {
- // 处理重发失败的情况,比如重新放入 channel 或记录日志
- mylog.Println("Error re-sending message:", err)
+ mylog.Printf("Error resending message: %v\n", err)
}
}
+ // 清空失败消息列表
+ client.sendFailures = []map[string]interface{}{}
}
// 处理信息,调用腾讯api
-func (c *WebSocketClient) recvMessage(msg []byte) {
+func (client *WebSocketClient) recvMessage(msg []byte) {
var message callapi.ActionMessage
//mylog.Println("Received from onebotv11 server raw:", string(msg))
err := json.Unmarshal(msg, &message)
@@ -128,7 +188,7 @@ func (c *WebSocketClient) recvMessage(msg []byte) {
}
mylog.Println("Received from onebotv11 server:", TruncateMessage(message, 800))
// 调用callapi
- callapi.CallAPIFromDict(c, c.api, c.apiv2, message)
+ callapi.CallAPIFromDict(client, client.api, client.apiv2, message)
}
// 截断信息
@@ -148,7 +208,7 @@ func TruncateMessage(message callapi.ActionMessage, maxLength int) string {
}
// 发送心跳包
-func (c *WebSocketClient) sendHeartbeat(ctx context.Context, botID uint64, heartbeatinterval int) {
+func (client *WebSocketClient) sendHeartbeat(ctx context.Context, botID uint64, heartbeatinterval int) {
for {
select {
case <-ctx.Done():
@@ -179,7 +239,9 @@ func (c *WebSocketClient) sendHeartbeat(ctx context.Context, botID uint64, heart
},
"interval": 10000, // 以毫秒为单位
}
- c.SendMessage(message)
+ client.SendMessage(message)
+ // 重发失败的消息
+ client.processFailedMessages()
}
}
}
@@ -244,7 +306,7 @@ func NewWebSocketClient(urlStr string, botID uint64, api openapi.OpenAPI, apiv2
apiv2: apiv2,
botID: botID,
urlStr: urlStr,
- sendFailures: make(chan map[string]interface{}, 100),
+ sendFailures: []map[string]interface{}{},
}
// Sending initial message similar to your setupB function
From 351c835cdd5843e469a9b418518bc40c333c0f84 Mon Sep 17 00:00:00 2001
From: cosmo
Date: Mon, 15 Jan 2024 18:49:01 +0800
Subject: [PATCH 09/13] beta130
---
handlers/message_parser.go | 6 ++++--
1 file changed, 4 insertions(+), 2 deletions(-)
diff --git a/handlers/message_parser.go b/handlers/message_parser.go
index 48e3519a..e53351d2 100644
--- a/handlers/message_parser.go
+++ b/handlers/message_parser.go
@@ -119,7 +119,8 @@ func parseMessageContent(paramsMessage callapi.ParamsContent, message callapi.Ac
segmentContent = "[CQ:at,qq=" + qqNumber + "]"
case "markdown":
mdContent, _ := segmentMap["data"].(map[string]interface{})["data"].(string)
- segmentContent = "[CQ:markdown,data=" + mdContent + "]"
+ encoded := "base64://" + base64.StdEncoding.EncodeToString([]byte(mdContent))
+ segmentContent = "[CQ:markdown,data=" + encoded + "]"
}
messageText += segmentContent
@@ -145,7 +146,8 @@ func parseMessageContent(paramsMessage callapi.ParamsContent, message callapi.Ac
messageText = "[CQ:at,qq=" + qqNumber + "]"
case "markdown":
mdContent, _ := message["data"].(map[string]interface{})["data"].(string)
- messageText = "[CQ:markdown,data=" + mdContent + "]"
+ encoded := "base64://" + base64.StdEncoding.EncodeToString([]byte(mdContent))
+ messageText = "[CQ:markdown,data=" + encoded + "]"
}
default:
mylog.Println("Unsupported message format: params.message field is not a string, map or slice")
From 639ef6b8b6514365a7fe62cfb639b7a540f69d51 Mon Sep 17 00:00:00 2001
From: cosmo
Date: Mon, 15 Jan 2024 20:28:55 +0800
Subject: [PATCH 10/13] beta131
---
handlers/message_parser.go | 17 +++++++++++++----
1 file changed, 13 insertions(+), 4 deletions(-)
diff --git a/handlers/message_parser.go b/handlers/message_parser.go
index e53351d2..8f767b1a 100644
--- a/handlers/message_parser.go
+++ b/handlers/message_parser.go
@@ -118,8 +118,12 @@ func parseMessageContent(paramsMessage callapi.ParamsContent, message callapi.Ac
qqNumber, _ := segmentMap["data"].(map[string]interface{})["qq"].(string)
segmentContent = "[CQ:at,qq=" + qqNumber + "]"
case "markdown":
- mdContent, _ := segmentMap["data"].(map[string]interface{})["data"].(string)
- encoded := "base64://" + base64.StdEncoding.EncodeToString([]byte(mdContent))
+ mdContentMap, _ := segmentMap["data"].(map[string]interface{})["data"].(map[string]interface{})
+ mdContentBytes, err := json.Marshal(mdContentMap)
+ if err != nil {
+ fmt.Println("Error marshaling mdContentMap to JSON:", err)
+ }
+ encoded := base64.StdEncoding.EncodeToString(mdContentBytes)
segmentContent = "[CQ:markdown,data=" + encoded + "]"
}
@@ -145,8 +149,13 @@ func parseMessageContent(paramsMessage callapi.ParamsContent, message callapi.Ac
qqNumber, _ := message["data"].(map[string]interface{})["qq"].(string)
messageText = "[CQ:at,qq=" + qqNumber + "]"
case "markdown":
- mdContent, _ := message["data"].(map[string]interface{})["data"].(string)
- encoded := "base64://" + base64.StdEncoding.EncodeToString([]byte(mdContent))
+ mdContentMap, _ := message["data"].(map[string]interface{})["data"].(map[string]interface{})
+ mdContentBytes, err := json.Marshal(mdContentMap)
+ if err != nil {
+ fmt.Println("Error marshaling mdContentMap to JSON:", err)
+
+ }
+ encoded := "base64://" + base64.StdEncoding.EncodeToString(mdContentBytes)
messageText = "[CQ:markdown,data=" + encoded + "]"
}
default:
From 5c4e887bb18a9bc1bdc2276bff15b86e8b3f7652 Mon Sep 17 00:00:00 2001
From: cosmo
Date: Tue, 16 Jan 2024 10:10:10 +0800
Subject: [PATCH 11/13] beta132
---
handlers/message_parser.go | 87 ++++++++++++++++++++++++++++++++------
handlers/send_group_msg.go | 15 ++++++-
2 files changed, 88 insertions(+), 14 deletions(-)
diff --git a/handlers/message_parser.go b/handlers/message_parser.go
index 8f767b1a..deafacf4 100644
--- a/handlers/message_parser.go
+++ b/handlers/message_parser.go
@@ -118,13 +118,44 @@ func parseMessageContent(paramsMessage callapi.ParamsContent, message callapi.Ac
qqNumber, _ := segmentMap["data"].(map[string]interface{})["qq"].(string)
segmentContent = "[CQ:at,qq=" + qqNumber + "]"
case "markdown":
- mdContentMap, _ := segmentMap["data"].(map[string]interface{})["data"].(map[string]interface{})
- mdContentBytes, err := json.Marshal(mdContentMap)
- if err != nil {
- fmt.Println("Error marshaling mdContentMap to JSON:", err)
+ mdContent, ok := segmentMap["data"].(map[string]interface{})["data"]
+ if ok {
+ if mdContentMap, isMap := mdContent.(map[string]interface{}); isMap {
+ // mdContent是map[string]interface{},按map处理
+ mdContentBytes, err := json.Marshal(mdContentMap)
+ if err != nil {
+ mylog.Printf("Error marshaling mdContentMap to JSON:%v", err)
+ }
+ encoded := base64.StdEncoding.EncodeToString(mdContentBytes)
+ segmentContent = "[CQ:markdown,data=" + encoded + "]"
+ } else if mdContentStr, isString := mdContent.(string); isString {
+ // mdContent是string
+ if strings.HasPrefix(mdContentStr, "base64://") {
+ // 如果以base64://开头,直接使用
+ segmentContent = "[CQ:markdown,data=" + mdContentStr + "]"
+ } else {
+ // 处理实体化后的JSON文本
+ mdContentStr = strings.ReplaceAll(mdContentStr, "&", "&")
+ mdContentStr = strings.ReplaceAll(mdContentStr, "[", "[")
+ mdContentStr = strings.ReplaceAll(mdContentStr, "]", "]")
+ mdContentStr = strings.ReplaceAll(mdContentStr, ",", ",")
+
+ // 将处理过的字符串视为JSON对象,进行序列化和编码
+ var jsonMap map[string]interface{}
+ if err := json.Unmarshal([]byte(mdContentStr), &jsonMap); err != nil {
+ mylog.Printf("Error unmarshaling string to JSON:%v", err)
+ }
+ mdContentBytes, err := json.Marshal(jsonMap)
+ if err != nil {
+ mylog.Printf("Error marshaling jsonMap to JSON:%v", err)
+ }
+ encoded := base64.StdEncoding.EncodeToString(mdContentBytes)
+ segmentContent = "[CQ:markdown,data=" + encoded + "]"
+ }
+ }
+ } else {
+ mylog.Printf("Error marshaling markdown segment to interface,contain type but data is nil.")
}
- encoded := base64.StdEncoding.EncodeToString(mdContentBytes)
- segmentContent = "[CQ:markdown,data=" + encoded + "]"
}
messageText += segmentContent
@@ -149,14 +180,44 @@ func parseMessageContent(paramsMessage callapi.ParamsContent, message callapi.Ac
qqNumber, _ := message["data"].(map[string]interface{})["qq"].(string)
messageText = "[CQ:at,qq=" + qqNumber + "]"
case "markdown":
- mdContentMap, _ := message["data"].(map[string]interface{})["data"].(map[string]interface{})
- mdContentBytes, err := json.Marshal(mdContentMap)
- if err != nil {
- fmt.Println("Error marshaling mdContentMap to JSON:", err)
-
+ mdContent, ok := message["data"].(map[string]interface{})["data"]
+ if ok {
+ if mdContentMap, isMap := mdContent.(map[string]interface{}); isMap {
+ // mdContent是map[string]interface{},按map处理
+ mdContentBytes, err := json.Marshal(mdContentMap)
+ if err != nil {
+ mylog.Printf("Error marshaling mdContentMap to JSON:%v", err)
+ }
+ encoded := base64.StdEncoding.EncodeToString(mdContentBytes)
+ messageText = "[CQ:markdown,data=" + encoded + "]"
+ } else if mdContentStr, isString := mdContent.(string); isString {
+ // mdContent是string
+ if strings.HasPrefix(mdContentStr, "base64://") {
+ // 如果以base64://开头,直接使用
+ messageText = "[CQ:markdown,data=" + mdContentStr + "]"
+ } else {
+ // 处理实体化后的JSON文本
+ mdContentStr = strings.ReplaceAll(mdContentStr, "&", "&")
+ mdContentStr = strings.ReplaceAll(mdContentStr, "[", "[")
+ mdContentStr = strings.ReplaceAll(mdContentStr, "]", "]")
+ mdContentStr = strings.ReplaceAll(mdContentStr, ",", ",")
+
+ // 将处理过的字符串视为JSON对象,进行序列化和编码
+ var jsonMap map[string]interface{}
+ if err := json.Unmarshal([]byte(mdContentStr), &jsonMap); err != nil {
+ mylog.Printf("Error unmarshaling string to JSON:%v", err)
+ }
+ mdContentBytes, err := json.Marshal(jsonMap)
+ if err != nil {
+ mylog.Printf("Error marshaling jsonMap to JSON:%v", err)
+ }
+ encoded := base64.StdEncoding.EncodeToString(mdContentBytes)
+ messageText = "[CQ:markdown,data=" + encoded + "]"
+ }
+ }
+ } else {
+ mylog.Printf("Error marshaling markdown segment to interface,contain type but data is nil.")
}
- encoded := "base64://" + base64.StdEncoding.EncodeToString(mdContentBytes)
- messageText = "[CQ:markdown,data=" + encoded + "]"
}
default:
mylog.Println("Unsupported message format: params.message field is not a string, map or slice")
diff --git a/handlers/send_group_msg.go b/handlers/send_group_msg.go
index 651827d6..e9f66eab 100644
--- a/handlers/send_group_msg.go
+++ b/handlers/send_group_msg.go
@@ -1059,15 +1059,22 @@ func auto_md(message callapi.ActionMessage, messageText string, richMediaMessage
// 判断是否是 GetVisualkPrefixs 数组开头的文本
visualkPrefixs := config.GetVisualkPrefixs()
var matchedPrefix *config.VisualPrefixConfig
+ var isSpecialType bool // 用于标记是否为特殊类型
// 去掉前缀开头的*
+ // 处理特殊类型前缀
+ specialPrefixes := make(map[int]string)
for i, vp := range visualkPrefixs {
if strings.HasPrefix(vp.Prefix, "*") {
+ specialPrefixes[i] = vp.Prefix
visualkPrefixs[i].Prefix = strings.TrimPrefix(vp.Prefix, "*")
}
}
- for _, vp := range visualkPrefixs {
+ for i, vp := range visualkPrefixs {
if strings.HasPrefix(msg_on_touch, vp.Prefix) {
+ if _, ok := specialPrefixes[i]; ok {
+ isSpecialType = true
+ }
if len(msg_on_touch) >= len(vp.Prefix) {
if msg_on_touch != " " {
transmd = true
@@ -1133,6 +1140,12 @@ func auto_md(message callapi.ActionMessage, messageText string, richMediaMessage
} else {
dataLabel = msg_on_touch
}
+ //当虚拟二级指令是*开头,真实的二级指令
+ //以xxxx指令为例 第一次 xxxx 然后 xx攻略 然后 xxxx攻略商店(会失效)
+ //作用 第一次 xxxx 第二次 xxxx攻略 第三次 xxxx商店
+ if isSpecialType && len(msg_on_touch) > len(matchedPrefix.Prefix) {
+ dataLabel = matchedPrefix.Prefix + whiteLabel
+ }
var actiontype keyboard.ActionType
var permission *keyboard.Permission
From 894b540a802e0614f625f1778f765e55f47fa7ae Mon Sep 17 00:00:00 2001
From: cosmo
Date: Tue, 16 Jan 2024 14:29:55 +0800
Subject: [PATCH 12/13] beta133
---
Processor/Processor.go | 223 ++++++++++++++++++++++++++++++++++++
config/config.go | 52 +++++++++
handlers/send_group_msg.go | 24 +++-
template/config_template.go | 4 +
4 files changed, 302 insertions(+), 1 deletion(-)
diff --git a/Processor/Processor.go b/Processor/Processor.go
index cc104dca..82b36f5b 100644
--- a/Processor/Processor.go
+++ b/Processor/Processor.go
@@ -23,9 +23,11 @@ import (
"github.com/hoshinonyaruko/gensokyo/echo"
"github.com/hoshinonyaruko/gensokyo/handlers"
"github.com/hoshinonyaruko/gensokyo/idmap"
+ "github.com/hoshinonyaruko/gensokyo/images"
"github.com/hoshinonyaruko/gensokyo/mylog"
"github.com/hoshinonyaruko/gensokyo/wsclient"
"github.com/tencent-connect/botgo/dto"
+ "github.com/tencent-connect/botgo/dto/keyboard"
"github.com/tencent-connect/botgo/openapi"
)
@@ -552,6 +554,13 @@ func (p *Processors) HandleFrameworkCommand(messageText string, data interface{}
mylog.Printf("您没有权限,使用临时指令:%s 忽略权限检查,或将masterid设置为空数组", tempCmd)
SendMessage("您没有权限,请配置config.yml或查看日志,使用临时指令", data, Type, p.Api, p.Apiv2)
}
+
+ //link指令
+ if Type == "group" && strings.HasPrefix(cleanedMessage, config.GetLinkPrefix()) {
+ md, kb := generateMdByConfig()
+ SendMessageMd(md, kb, data, Type, p.Api, p.Apiv2)
+ }
+
return nil
}
@@ -789,6 +798,113 @@ func SendMessage(messageText string, data interface{}, messageType string, api o
return nil
}
+// SendMessageMd 发送Md消息根据不同的类型
+func SendMessageMd(md *dto.Markdown, kb *keyboard.MessageKeyboard, data interface{}, messageType string, api openapi.OpenAPI, apiv2 openapi.OpenAPI) error {
+ // 强制类型转换,获取Message结构
+ var msg *dto.Message
+ switch v := data.(type) {
+ case *dto.WSGroupATMessageData:
+ msg = (*dto.Message)(v)
+ case *dto.WSATMessageData:
+ msg = (*dto.Message)(v)
+ case *dto.WSMessageData:
+ msg = (*dto.Message)(v)
+ case *dto.WSDirectMessageData:
+ msg = (*dto.Message)(v)
+ case *dto.WSC2CMessageData:
+ msg = (*dto.Message)(v)
+ default:
+ return nil
+ }
+ switch messageType {
+ case "guild":
+ // 处理公会消息
+ msgseq := echo.GetMappingSeq(msg.ID)
+ echo.AddMappingSeq(msg.ID, msgseq+1)
+ Message := &dto.MessageToCreate{
+ Content: "markdown",
+ MsgID: msg.ID,
+ MsgSeq: msgseq,
+ Markdown: md,
+ Keyboard: kb,
+ MsgType: 2, //md信息
+ }
+ Message.Timestamp = time.Now().Unix() // 设置时间戳
+ if _, err := api.PostMessage(context.TODO(), msg.ChannelID, Message); err != nil {
+ mylog.Printf("发送文本信息失败: %v", err)
+ return err
+ }
+
+ case "group":
+ // 处理群组消息
+ msgseq := echo.GetMappingSeq(msg.ID)
+ echo.AddMappingSeq(msg.ID, msgseq+1)
+ Message := &dto.MessageToCreate{
+ Content: "markdown",
+ MsgID: msg.ID,
+ MsgSeq: msgseq,
+ Markdown: md,
+ Keyboard: kb,
+ MsgType: 2, //md信息
+ }
+ Message.Timestamp = time.Now().Unix() // 设置时间戳
+ _, err := apiv2.PostGroupMessage(context.TODO(), msg.GroupID, Message)
+ if err != nil {
+ mylog.Printf("发送文本群组信息失败: %v", err)
+ return err
+ }
+
+ case "guild_private":
+ // 处理私信
+ timestamp := time.Now().Unix()
+ timestampStr := fmt.Sprintf("%d", timestamp)
+ dm := &dto.DirectMessage{
+ GuildID: msg.GuildID,
+ ChannelID: msg.ChannelID,
+ CreateTime: timestampStr,
+ }
+ msgseq := echo.GetMappingSeq(msg.ID)
+ echo.AddMappingSeq(msg.ID, msgseq+1)
+ Message := &dto.MessageToCreate{
+ Content: "markdown",
+ MsgID: msg.ID,
+ MsgSeq: msgseq,
+ Markdown: md,
+ Keyboard: kb,
+ MsgType: 2, //md信息
+ }
+ Message.Timestamp = time.Now().Unix() // 设置时间戳
+ if _, err := apiv2.PostDirectMessage(context.TODO(), dm, Message); err != nil {
+ mylog.Printf("发送文本信息失败: %v", err)
+ return err
+ }
+
+ case "group_private":
+ // 处理群组私聊消息
+ msgseq := echo.GetMappingSeq(msg.ID)
+ echo.AddMappingSeq(msg.ID, msgseq+1)
+ Message := &dto.MessageToCreate{
+ Content: "markdown",
+ MsgID: msg.ID,
+ MsgSeq: msgseq,
+ Markdown: md,
+ Keyboard: kb,
+ MsgType: 2, //md信息
+ }
+ Message.Timestamp = time.Now().Unix() // 设置时间戳
+ _, err := apiv2.PostC2CMessage(context.TODO(), msg.Author.ID, Message)
+ if err != nil {
+ mylog.Printf("发送文本私聊信息失败: %v", err)
+ return err
+ }
+
+ default:
+ return errors.New("未知的消息类型")
+ }
+
+ return nil
+}
+
// autobind 函数接受 interface{} 类型的数据
// commit by 紫夜 2023-11-19
func (p *Processors) Autobind(data interface{}) error {
@@ -922,3 +1038,110 @@ func GenerateAvatarURL(userID int64) (string, error) {
// 构建并返回 URL
return fmt.Sprintf("http://q%d.qlogo.cn/g?b=qq&nk=%d&s=640", qNumber, userID), nil
}
+
+// 生成link卡片
+func generateMdByConfig() (md *dto.Markdown, kb *keyboard.MessageKeyboard) {
+ //相关配置获取
+ mdtext := config.GetLinkText()
+ mdtext = "\r" + mdtext
+ CustomTemplateID := config.GetCustomTemplateID()
+ linkBots := config.GetLinkBots()
+ imgURL := config.GetLinkPic()
+
+ //超过16个时候随机显示
+ if len(linkBots) > 16 {
+ linkBots = getRandomSelection(linkBots, 16)
+ }
+
+ //组合 mdParams
+ var mdParams []*dto.MarkdownParams
+ if imgURL != "" {
+ height, width, err := images.GetImageDimensions(imgURL)
+ if err != nil {
+ mylog.Printf("获取图片宽高出错")
+ }
+ imgDesc := fmt.Sprintf("图片 #%dpx #%dpx", width, height)
+ // 创建 MarkdownParams 的实例
+ mdParams = []*dto.MarkdownParams{
+ {Key: "img_dec", Values: []string{imgDesc}},
+ {Key: "img_url", Values: []string{imgURL}},
+ {Key: "text_end", Values: []string{mdtext}},
+ }
+ } else {
+ mdParams = []*dto.MarkdownParams{
+ {Key: "text_end", Values: []string{mdtext}},
+ }
+ }
+
+ // 组合模板 Markdown
+ md = &dto.Markdown{
+ CustomTemplateID: CustomTemplateID,
+ Params: mdParams,
+ }
+
+ // 创建自定义键盘
+ customKeyboard := &keyboard.CustomKeyboard{}
+ var currentRow *keyboard.Row
+ var buttonCount int
+
+ for _, bot := range linkBots {
+ parts := strings.SplitN(bot, "-", 3)
+ if len(parts) < 3 {
+ continue // 跳过无效的格式
+ }
+ name := parts[2]
+ botuin := parts[1]
+ botappid := parts[0]
+ boturl := handlers.BuildQQBotShareLink(botuin, botappid)
+
+ button := &keyboard.Button{
+ RenderData: &keyboard.RenderData{
+ Label: name,
+ VisitedLabel: name,
+ Style: 1, // 蓝色边缘
+ },
+ Action: &keyboard.Action{
+ Type: 0, // 链接类型
+ Permission: &keyboard.Permission{Type: 2}, // 所有人可操作
+ Data: boturl,
+ UnsupportTips: "请升级新版手机QQ",
+ },
+ }
+
+ // 如果当前行为空或已满(4个按钮),则创建一个新行
+ if currentRow == nil || buttonCount == 4 {
+ currentRow = &keyboard.Row{}
+ customKeyboard.Rows = append(customKeyboard.Rows, currentRow)
+ buttonCount = 0
+ }
+
+ // 将按钮添加到当前行
+ currentRow.Buttons = append(currentRow.Buttons, button)
+ buttonCount++
+ }
+
+ // 创建 MessageKeyboard 并设置其 Content
+ kb = &keyboard.MessageKeyboard{
+ Content: customKeyboard,
+ }
+
+ return md, kb
+}
+
+func getRandomSelection(slice []string, max int) []string {
+ if len(slice) <= max {
+ return slice
+ }
+
+ selected := make(map[int]bool)
+ var result []string
+ for len(result) < max {
+ index, _ := rand.Int(rand.Reader, big.NewInt(int64(len(slice))))
+ idx := int(index.Int64())
+ if !selected[idx] {
+ selected[idx] = true
+ result = append(result, slice[idx])
+ }
+ }
+ return result
+}
diff --git a/config/config.go b/config/config.go
index 78ddf764..dfeb04b6 100644
--- a/config/config.go
+++ b/config/config.go
@@ -136,6 +136,10 @@ type Settings struct {
Uin int64 `yaml:"uin"`
VwhitePrefixMode bool `yaml:"v_white_prefix_mode"`
Enters []string `yaml:"enters"`
+ LinkPrefix string `yaml:"link_prefix"`
+ LinkBots []string `yaml:"link_bots"`
+ LinkText string `yaml:"link_text"`
+ LinkPic string `yaml:"link_pic"`
}
// LoadConfig 从文件中加载配置并初始化单例配置
@@ -1711,3 +1715,51 @@ func GetEnters() []string {
}
return nil // 返回nil,如果instance为nil
}
+
+// 获取 LinkPrefix
+func GetLinkPrefix() string {
+ mu.Lock()
+ defer mu.Unlock()
+
+ if instance == nil {
+ mylog.Println("Warning: instance is nil when trying to get LinkPrefix.")
+ return ""
+ }
+ return instance.Settings.LinkPrefix
+}
+
+// 获取 LinkBots 数组
+func GetLinkBots() []string {
+ mu.Lock()
+ defer mu.Unlock()
+
+ if instance == nil {
+ mylog.Println("Warning: instance is nil when trying to get LinkBots.")
+ return nil
+ }
+ return instance.Settings.LinkBots
+}
+
+// 获取 LinkText
+func GetLinkText() string {
+ mu.Lock()
+ defer mu.Unlock()
+
+ if instance == nil {
+ mylog.Println("Warning: instance is nil when trying to get LinkText.")
+ return ""
+ }
+ return instance.Settings.LinkText
+}
+
+// 获取 LinkPic
+func GetLinkPic() string {
+ mu.Lock()
+ defer mu.Unlock()
+
+ if instance == nil {
+ mylog.Println("Warning: instance is nil when trying to get LinkPic.")
+ return ""
+ }
+ return instance.Settings.LinkPic
+}
diff --git a/handlers/send_group_msg.go b/handlers/send_group_msg.go
index e9f66eab..a2cb87c5 100644
--- a/handlers/send_group_msg.go
+++ b/handlers/send_group_msg.go
@@ -1147,13 +1147,35 @@ func auto_md(message callapi.ActionMessage, messageText string, richMediaMessage
dataLabel = matchedPrefix.Prefix + whiteLabel
}
+ //在虚拟二级指令白名单,设置*前缀,代表不真实添加,仅再来一次
+ if strings.HasPrefix(whiteLabel, "*") {
+ //移除whiteLabel前端的*,*仅作为判断,不作为显示
+ whiteLabel = strings.TrimPrefix(whiteLabel, "*")
+ dataLabel = matchedPrefix.Prefix
+ }
+
+ //在虚拟二级指令白名单,设置&前缀,代表仅触发其本身
+ //如果&前缀指令包含了空格 则只显示空格右侧的文本
+ if strings.HasPrefix(whiteLabel, "&") {
+ //移除whiteLabel前端的*,*仅作为判断,不作为显示
+ whiteLabel = strings.TrimPrefix(whiteLabel, "&")
+ //这里是实际填充到data的
+ dataLabel = whiteLabel
+ // 找到最后一个空格的位置 显示空格右边的文本 没有找到空格则不变
+ lastSpaceIndex := strings.LastIndex(whiteLabel, " ")
+ if lastSpaceIndex != -1 && lastSpaceIndex < len(whiteLabel)-1 {
+ // 获取空格右侧的子字符串
+ whiteLabel = whiteLabel[lastSpaceIndex+1:]
+ }
+ }
+
var actiontype keyboard.ActionType
var permission *keyboard.Permission
var actiondata string
//检查是否设置了enter数组
enter := checkDataLabelPrefix(dataLabel)
switch whiteLabel {
- case "邀请机器人":
+ case "邀请机器人", "邀请我", "添加我":
botuin := config.GetUinStr()
botappid := config.GetAppIDStr()
boturl := BuildQQBotShareLink(botuin, botappid)
diff --git a/template/config_template.go b/template/config_template.go
index cda74bc4..d41bbde2 100644
--- a/template/config_template.go
+++ b/template/config_template.go
@@ -144,6 +144,10 @@ settings:
bind_prefix : "/bind" #需设置 #增强配置项 master_id 可触发
me_prefix : "/me" #需设置 #增强配置项 master_id 可触发
unlock_prefix : "/unlock" #频道私信卡住了? gsk可以帮到你 在任意子频道发送unlock 你会收到来自机器人的频道私信
+ link_prefix : "/link" #友情链接配置 配置custom_template_id后可用(https://www.yuque.com/km57bt/hlhnxg/tzbr84y59dbz6pib)
+ link_bots : ["",""] #发送友情链接时 下方按钮携带的机器人 格式 "appid-qq-name","appid-qq-name"
+ link_text : "" #友情链接文本 不可为空!
+ link_pic : "" #友情链接图片 可为空 需url图片 可带端口 不填可能会有显示错误
#穿透\cos\oss类配置(可选!)
frp_port : "0" #不使用请保持为0,frp的端口,frp有内外端口,请在frp软件设置gensokyo的port,并将frp显示的对外端口填入这里
From 766fa060ca302ff47f98937fa2e9a256ab1516a2 Mon Sep 17 00:00:00 2001
From: cosmo
Date: Wed, 17 Jan 2024 10:44:02 +0800
Subject: [PATCH 13/13] beta134
---
handlers/message_parser.go | 20 +++++++++++++++++---
handlers/send_group_msg.go | 2 +-
2 files changed, 18 insertions(+), 4 deletions(-)
diff --git a/handlers/message_parser.go b/handlers/message_parser.go
index deafacf4..f927e85e 100644
--- a/handlers/message_parser.go
+++ b/handlers/message_parser.go
@@ -552,6 +552,7 @@ func RevertTransformedText(data interface{}, msgtype string, api openapi.OpenAPI
}
}
+ //从当前信息去掉虚拟前缀(因为是虚拟的),不会实际发给应用端
for i, vp := range visualkPrefixs {
if strings.HasPrefix(messageText, vp.Prefix) {
if _, ok := specialPrefixes[i]; ok {
@@ -560,8 +561,8 @@ func RevertTransformedText(data interface{}, msgtype string, api openapi.OpenAPI
}
// 检查 messageText 的长度是否大于 prefix 的长度
if len(messageText) > len(vp.Prefix) {
- // 移除找到的前缀 且messageText不为空格
- if messageText != " " {
+ // 移除找到的前缀 且messageText不为空
+ if messageText != "" {
messageText = strings.TrimPrefix(messageText, vp.Prefix)
messageText = strings.TrimSpace(messageText)
matchedPrefix = &vp
@@ -618,7 +619,20 @@ func RevertTransformedText(data interface{}, msgtype string, api openapi.OpenAPI
// 遍历白名单数组,检查是否有匹配项
for _, prefix := range allPrefixes {
- if strings.HasPrefix(messageText, prefix) {
+ trimmedPrefix := prefix
+ if strings.HasPrefix(prefix, "*") {
+ // 如果前缀以 * 开头,则移除 *
+ trimmedPrefix = strings.TrimPrefix(prefix, "*")
+ } else if strings.HasPrefix(prefix, "&") {
+ // 如果前缀以 & 开头,则移除 & 并从 trimmedPrefix 前端去除 matchedPrefix.Prefix
+ trimmedPrefix = strings.TrimPrefix(prefix, "&")
+ trimmedPrefix = strings.TrimPrefix(trimmedPrefix, matchedPrefix.Prefix)
+ }
+
+ // 从trimmedPrefix中去除前后空格(可能会有bug)
+ trimmedPrefix = strings.TrimSpace(trimmedPrefix)
+
+ if strings.HasPrefix(messageText, trimmedPrefix) {
matched = true
break
}
diff --git a/handlers/send_group_msg.go b/handlers/send_group_msg.go
index a2cb87c5..3a39d88d 100644
--- a/handlers/send_group_msg.go
+++ b/handlers/send_group_msg.go
@@ -1076,7 +1076,7 @@ func auto_md(message callapi.ActionMessage, messageText string, richMediaMessage
isSpecialType = true
}
if len(msg_on_touch) >= len(vp.Prefix) {
- if msg_on_touch != " " {
+ if msg_on_touch != "" {
transmd = true
matchedPrefix = &vp
break // 匹配到了