From 26dfe5c62b34f6818e6f734cfccc0cd7091f5f72 Mon Sep 17 00:00:00 2001 From: cosmo Date: Sun, 24 Nov 2024 11:12:41 +0800 Subject: [PATCH 1/3] beta509 --- config/config.go | 23 +++++++++++++++++++++++ idmap/service.go | 34 +++++++++++++++++----------------- images/upload_api.go | 18 +++++++++--------- oss/baidu.go | 6 +++--- server/uploadpic.go | 4 ++-- url/shorturl.go | 4 ++-- 6 files changed, 56 insertions(+), 33 deletions(-) diff --git a/config/config.go b/config/config.go index 09b5ffbc..56965925 100644 --- a/config/config.go +++ b/config/config.go @@ -2471,3 +2471,26 @@ func GetNoRetMsg() bool { } return instance.Settings.NoRetMsg } + +func GetForceSsl() bool { + mu.RLock() + defer mu.RUnlock() + + if instance == nil { + fmt.Println("Warning: instance is nil when trying to ForceSSL value.") + return false + } + return instance.Settings.ForceSSL +} + +func GetHttpPortAfterSsl() string { + mu.RLock() + defer mu.RUnlock() + + if instance == nil { + fmt.Println("Warning: instance is nil when trying to get HttpPortAfterSSL.") + return "444" // 或者返回一个默认的 ImageLimit 值 + } + + return instance.Settings.HttpPortAfterSSL +} diff --git a/idmap/service.go b/idmap/service.go index 0c0ae205..1dbe49a8 100644 --- a/idmap/service.go +++ b/idmap/service.go @@ -456,7 +456,7 @@ func SimplifiedStoreIDv2(id string) (int64, error) { // 根据portValue确定协议 protocol := "http" - if portValue == "443" { + if portValue == "443" || config.GetForceSsl(){ protocol = "https" } @@ -553,7 +553,7 @@ func StoreIDv2(id string) (int64, error) { // 根据portValue确定协议 protocol := "http" - if portValue == "443" { + if portValue == "443" ||config.GetForceSsl(){ protocol = "https" } @@ -603,7 +603,7 @@ func StoreCachev2(id string) (int64, error) { // 根据portValue确定协议 protocol := "http" - if portValue == "443" { + if portValue == "443" ||config.GetForceSsl(){ protocol = "https" } @@ -653,7 +653,7 @@ func StoreIDv2Pro(id string, subid string) (int64, int64, error) { // 根据portValue确定协议 protocol := "http" - if portValue == "443" { + if portValue == "443" ||config.GetForceSsl(){ protocol = "https" } @@ -746,7 +746,7 @@ func RetrieveRowByIDv2Pro(newRowID string, newSubRowID string) (string, string, // 根据portValue确定协议 protocol := "http" - if portValue == "443" { + if portValue == "443" ||config.GetForceSsl(){ protocol = "https" } @@ -817,7 +817,7 @@ func RetrieveRowByIDv2(rowid string) (string, error) { // 根据portValue确定协议 protocol := "http" portValue := config.GetPortValue() - if portValue == "443" { + if portValue == "443" ||config.GetForceSsl(){ protocol = "https" } if config.GetLotusGrpc() && config.GetLotusValue() { @@ -866,7 +866,7 @@ func RetrieveRowByCachev2(rowid string) (string, error) { // 根据portValue确定协议 protocol := "http" portValue := config.GetPortValue() - if portValue == "443" { + if portValue == "443" ||config.GetForceSsl(){ protocol = "https" } if config.GetLotusGrpc() && config.GetLotusValue() { @@ -947,7 +947,7 @@ func WriteConfigv2(sectionName, keyName, value string) error { // 根据portValue确定协议 protocol := "http" - if portValue == "443" { + if portValue == "443" ||config.GetForceSsl(){ protocol = "https" } @@ -1023,7 +1023,7 @@ func DeleteConfigv2(sectionName, keyName string) error { // 根据portValue确定协议 protocol := "http" portValue := config.GetPortValue() - if portValue == "443" { + if portValue == "443" ||config.GetForceSsl(){ protocol = "https" } @@ -1072,7 +1072,7 @@ func ReadConfigv2(sectionName, keyName string) (string, error) { // 根据portValue确定协议 protocol := "http" portValue := config.GetPortValue() - if portValue == "443" { + if portValue == "443" ||config.GetForceSsl(){ protocol = "https" } if config.GetLotusGrpc() && config.GetLotusValue() { @@ -1236,7 +1236,7 @@ func UpdateVirtualValuev2(oldRowValue, newRowValue int64) error { serverDir := config.GetServer_dir() portValue := config.GetPortValue() protocol := "http" - if portValue == "443" { + if portValue == "443" ||config.GetForceSsl(){ protocol = "https" } url := fmt.Sprintf("%s://%s:%s/getid?type=5&oldRowValue=%d&newRowValue=%d", protocol, serverDir, portValue, oldRowValue, newRowValue) @@ -1272,7 +1272,7 @@ func RetrieveRealValuev2(virtualValue int64) (string, string, error) { serverDir := config.GetServer_dir() portValue := config.GetPortValue() protocol := "http" - if portValue == "443" { + if portValue == "443" ||config.GetForceSsl(){ protocol = "https" } url := fmt.Sprintf("%s://%s:%s/getid?type=6&virtualValue=%d", protocol, serverDir, portValue, virtualValue) @@ -1320,7 +1320,7 @@ func RetrieveVirtualValuev2(realValue string) (string, string, error) { // 根据portValue确定协议 protocol := "http" - if portValue == "443" { + if portValue == "443" ||config.GetForceSsl(){ protocol = "https" } @@ -1374,7 +1374,7 @@ func RetrieveVirtualValuev2Pro(realValue string, realValueSub string) (string, s // 根据portValue确定协议 protocol := "http" - if portValue == "443" { + if portValue == "443" ||config.GetForceSsl(){ protocol = "https" } @@ -1499,7 +1499,7 @@ func RetrieveRealValuesv2Pro(virtualValue int64, virtualValueSub int64) (string, // 根据portValue确定协议 protocol := "http" - if portValue == "443" { + if portValue == "443" ||config.GetForceSsl(){ protocol = "https" } @@ -1593,7 +1593,7 @@ func UpdateVirtualValuev2Pro(oldVirtualValue1, newVirtualValue1, oldVirtualValue serverDir := config.GetServer_dir() portValue := config.GetPortValue() protocol := "http" - if portValue == "443" { + if portValue == "443" ||config.GetForceSsl(){ protocol = "https" } @@ -1695,7 +1695,7 @@ func FindSubKeysByIdPro(id string) ([]string, error) { // 根据portValue确定协议 protocol := "http" - if portValue == "443" { + if portValue == "443" ||config.GetForceSsl(){ protocol = "https" } diff --git a/images/upload_api.go b/images/upload_api.go index 67705aa6..913f00bc 100644 --- a/images/upload_api.go +++ b/images/upload_api.go @@ -177,7 +177,7 @@ func originalUploadBehavior(base64Image string) (string, error) { // 原有的UploadBase64ImageToServer函数的实现 protocol := "http" serverPort := config.GetPortValue() - if serverPort == "443" { + if serverPort == "443" ||config.GetForceSsl(){ protocol = "https" } @@ -194,9 +194,9 @@ func originalUploadBehavior(base64Image string) (string, error) { } serverDir := config.GetServer_dir() - if serverPort == "443" { + if serverPort == "443" ||config.GetForceSsl(){ protocol = "http" - serverPort = "444" + serverPort = config.GetHttpPortAfterSsl() } if isPublicAddress(serverDir) { @@ -227,7 +227,7 @@ func UploadBehaviorV3(base64Image string) (string, int, int, error) { } else { protocol := "http" serverPort := config.GetPortValue() - if serverPort == "443" { + if serverPort == "443" ||config.GetForceSsl(){ protocol = "https" } @@ -241,9 +241,9 @@ func UploadBehaviorV3(base64Image string) (string, int, int, error) { } return resp, width, height, nil } else { - if serverPort == "443" { + if serverPort == "443" ||config.GetForceSsl(){ protocol = "http" - serverPort = "444" + serverPort = config.GetHttpPortAfterSsl() } url = fmt.Sprintf("%s://127.0.0.1:%s/uploadpicv3", protocol, serverPort) @@ -261,7 +261,7 @@ func originalUploadBehaviorRecord(base64Image string) (string, error) { // 根据serverPort确定协议 protocol := "http" serverPort := config.GetPortValue() - if serverPort == "443" { + if serverPort == "443" ||config.GetForceSsl(){ protocol = "https" } @@ -278,9 +278,9 @@ func originalUploadBehaviorRecord(base64Image string) (string, error) { serverDir := config.GetServer_dir() // 当端口是443时,使用HTTP和444端口 - if serverPort == "443" { + if serverPort == "443" ||config.GetForceSsl(){ protocol = "http" - serverPort = "444" + serverPort = config.GetHttpPortAfterSsl() } if isPublicAddress(serverDir) { diff --git a/oss/baidu.go b/oss/baidu.go index 36165d3e..05a5beec 100644 --- a/oss/baidu.go +++ b/oss/baidu.go @@ -133,7 +133,7 @@ func originalUploadBehavior(base64Image string) (string, error) { // 原有的UploadBase64ImageToServer函数的实现 protocol := "http" serverPort := config.GetPortValue() - if serverPort == "443" { + if serverPort == "443" ||config.GetForceSsl(){ protocol = "https" } @@ -149,9 +149,9 @@ func originalUploadBehavior(base64Image string) (string, error) { } serverDir := config.GetServer_dir() - if serverPort == "443" { + if serverPort == "443" ||config.GetForceSsl(){ protocol = "http" - serverPort = "444" + serverPort = config.GetHttpPortAfterSsl() } if isPublicAddress(serverDir) { diff --git a/server/uploadpic.go b/server/uploadpic.go index cd515af0..3c539d42 100644 --- a/server/uploadpic.go +++ b/server/uploadpic.go @@ -112,7 +112,7 @@ func UploadBase64ImageHandler(rateLimiter *RateLimiter) gin.HandlerFunc { } // 根据serverPort确定协议 protocol := "http" - if serverPort == "443" { + if serverPort == "443"||config.GetForceSsl() { protocol = "https" } stun, err := idmap.ReadConfigv2("stun", "addr") @@ -277,7 +277,7 @@ func UploadBase64RecordHandler(rateLimiter *RateLimiter) gin.HandlerFunc { // 根据serverPort确定协议 protocol := "http" - if serverPort == "443" { + if serverPort == "443" ||config.GetForceSsl(){ protocol = "https" } diff --git a/url/shorturl.go b/url/shorturl.go index 0d8381ad..a9726707 100644 --- a/url/shorturl.go +++ b/url/shorturl.go @@ -126,7 +126,7 @@ func GenerateShortURL(longURL string) string { // 根据portValue确定协议 protocol := "http" portValue := config.GetPortValue() - if portValue == "443" { + if portValue == "443" ||config.GetForceSsl(){ protocol = "https" } @@ -242,7 +242,7 @@ func getLongURLFromDB(shortURL string) (string, error) { // 根据portValue确定协议 protocol := "http" portValue := config.GetPortValue() - if portValue == "443" { + if portValue == "443" ||config.GetForceSsl(){ protocol = "https" } From 5b57e0d04b04a2b657b6f529b02e4d8aeba9065b Mon Sep 17 00:00:00 2001 From: cosmo Date: Mon, 25 Nov 2024 16:42:03 +0800 Subject: [PATCH 2/3] beta510 --- main.go | 6 +-- server/webhook.go | 85 ++----------------------------------- template/config_template.go | 1 - 3 files changed, 5 insertions(+), 87 deletions(-) diff --git a/main.go b/main.go index d72a8c9e..ae8070ed 100644 --- a/main.go +++ b/main.go @@ -459,11 +459,7 @@ func main() { r.POST("/uploadrecord", server.UploadBase64RecordHandler(rateLimiter)) // 使用 CreateHandleValidation,传入 WebhookHandler 实例 server.InitPrivateKey(conf.Settings.ClientSecret) - if len(conf.Settings.WebhookPrefixIp) == 0 { - r.POST("/"+conf.Settings.WebhookPath, server.CreateHandleValidationSafe(webhookHandler)) - } else { - r.POST("/"+conf.Settings.WebhookPath, server.CreateHandleValidation(webhookHandler, conf.Settings.WebhookPrefixIp)) - } + r.POST("/"+conf.Settings.WebhookPath, server.CreateHandleValidationSafe(webhookHandler)) r.Static("/channel_temp", "./channel_temp") if config.GetFrpPort() == "0" && !config.GetDisableWebui() { diff --git a/server/webhook.go b/server/webhook.go index 2ddcb917..d1967154 100644 --- a/server/webhook.go +++ b/server/webhook.go @@ -13,6 +13,7 @@ import ( "sync/atomic" "github.com/gin-gonic/gin" + "github.com/hoshinonyaruko/gensokyo/mylog" "github.com/tencent-connect/botgo/dto" "github.com/tencent-connect/botgo/event" "github.com/tencent-connect/botgo/websocket/client" @@ -71,84 +72,6 @@ func InitPrivateKey(botSecret string) { publicKey = pkey } -// CreateHandleValidation 创建用于签名验证和消息入队的处理函数 -func CreateHandleValidation(wh *WebhookHandler, allowedPrefixes []string) gin.HandlerFunc { - return func(c *gin.Context) { - // 提取客户端 IP - clientIP := c.ClientIP() - - // 检查是否匹配任意一个允许的前缀 - allowed := false - for _, prefix := range allowedPrefixes { - if strings.HasPrefix(clientIP, prefix) { - allowed = true - break - } - } - - if !allowed { - log.Printf("Request from unauthorized IP: %s", clientIP) - c.JSON(http.StatusForbidden, gin.H{"error": "Access denied"}) - return - } - - // 读取 HTTP Body - httpBody, err := io.ReadAll(c.Request.Body) - if err != nil { - log.Println("Failed to read HTTP body:", err) - c.JSON(http.StatusBadRequest, gin.H{"error": "Failed to read request body"}) - return - } - - // 解析请求数据 - var payload Payload - if err := json.Unmarshal(httpBody, &payload); err != nil { - log.Println("Failed to parse HTTP payload:", err) - c.JSON(http.StatusBadRequest, gin.H{"error": "Failed to parse payload"}) - return - } - - // 判断 Op 类型 - switch payload.Op { - case 13: - // 签名验证逻辑 - var msg bytes.Buffer - msg.WriteString(payload.D.EventTs) - msg.WriteString(payload.D.PlainToken) - signature := hex.EncodeToString(ed25519.Sign(privateKey, msg.Bytes())) - - // 返回签名验证响应 - c.JSON(http.StatusOK, gin.H{ - "plain_token": payload.D.PlainToken, - "signature": signature, - }) - - default: - // 异步推送消息到队列 - go func(httpBody []byte, payload Payload) { - webhookPayload := &WebhookPayload{ - PlainToken: payload.D.PlainToken, - EventTs: payload.D.EventTs, - RawMessage: httpBody, - } - - // 尝试写入队列 - select { - case wh.messageQueue <- webhookPayload: - log.Println("Message enqueued successfully") - default: - log.Println("Message queue is full, dropping message") - } - }(httpBody, payload) - - // 返回 HTTP Callback ACK 响应 - c.JSON(http.StatusOK, gin.H{ - "op": 12, - }) - } - } -} - func CreateHandleValidationSafe(wh *WebhookHandler) gin.HandlerFunc { return func(c *gin.Context) { // 读取 HTTP Body @@ -204,7 +127,7 @@ func CreateHandleValidationSafe(wh *WebhookHandler) gin.HandlerFunc { // 尝试写入队列 select { case wh.messageQueue <- webhookPayload: - log.Println("Message enqueued successfully") + mylog.Println("Message enqueued successfully") default: log.Println("Message queue is full, dropping message") } @@ -265,7 +188,7 @@ func validateSignature(req *http.Request, publicKey ed25519.PublicKey) error { func (wh *WebhookHandler) ListenAndProcessMessages() { for payload := range wh.messageQueue { go func(p *WebhookPayload) { - log.Printf("Processing Webhook event with token: %s", p.PlainToken) + mylog.Printf("Processing Webhook event with token: %s", p.PlainToken) // 业务逻辑处理的地方 payload := &dto.WSPayload{} if err := json.Unmarshal(p.RawMessage, payload); err != nil { @@ -276,7 +199,7 @@ func (wh *WebhookHandler) ListenAndProcessMessages() { atomic.StoreInt64(&client.Global_s, payload.S) payload.RawMessage = p.RawMessage - log.Printf("%s receive %s message, %s", p.EventTs, dto.OPMeans(payload.OPCode), string(p.RawMessage)) + mylog.Printf("%s receive %s message, %s", p.EventTs, dto.OPMeans(payload.OPCode), string(p.RawMessage)) // 性能不够 报错也没用 就扬了 go event.ParseAndHandle(payload) diff --git a/template/config_template.go b/template/config_template.go index 14da86b4..0a9eb8b7 100644 --- a/template/config_template.go +++ b/template/config_template.go @@ -86,7 +86,6 @@ settings: crt : "" #证书路径 从你的域名服务商或云服务商申请签发SSL证书(qq要求SSL) key : "" #密钥路径 Apache(crt文件、key文件)示例: "C:\\123.key" \需要双写成\\ webhook_path : "webhook" #webhook监听的地址,默认\webhook - webhook_prefix_ip : [] #默认为空,通过webhook进行签名验证来源,设置时,只允许ip前缀的请求,不验证签名. 2024年11月22日最近的webhookip都是 183.47.105. 开始的. force_ssl : false #默认当port设置为443时启用ssl,true可以在其他port设置下强制启用ssl. http_port_after_ssl : "444" # 指定启动SSL之后的备用HTTP服务器的端口号,默认为444 From 3e8964beb233757d0af6664b1e08640c32bcaf8a Mon Sep 17 00:00:00 2001 From: cosmo Date: Mon, 23 Dec 2024 19:29:58 +0800 Subject: [PATCH 3/3] beta511 --- Processor/ProcessInlineSearch.go | 667 +++++++++++++++++++++---------- echo/echo.go | 10 + echo/messageidmap.go | 226 +++++++---- handlers/send_group_msg.go | 6 +- handlers/send_msg.go | 25 ++ handlers/send_private_msg.go | 6 +- 6 files changed, 646 insertions(+), 294 deletions(-) diff --git a/Processor/ProcessInlineSearch.go b/Processor/ProcessInlineSearch.go index bc74f22d..8504f6e2 100644 --- a/Processor/ProcessInlineSearch.go +++ b/Processor/ProcessInlineSearch.go @@ -141,261 +141,484 @@ func (p *Processors) ProcessInlineSearch(data *dto.WSInteractionData) error { echo.AddEvnetID(AppIDString, LongGroupID64, data.EventID) } else { if data.GroupOpenID != "" { - //群回调 - newdata := ConvertInteractionToMessage(data) - //mylog.Printf("回调测试111-newdata:%v\n", newdata) + // 是否使用string形式上报 + if !config.GetStringOb11() { + //群回调 + newdata := ConvertInteractionToMessage(data) + //mylog.Printf("回调测试111-newdata:%v\n", newdata) + + // 如果在Array模式下, 则处理Message为Segment格式 + var segmentedMessages interface{} = data.Data.Resolved.ButtonData + if config.GetArrayValue() { + segmentedMessages = handlers.ConvertToSegmentedMessage(newdata) + } - // 如果在Array模式下, 则处理Message为Segment格式 - var segmentedMessages interface{} = data.Data.Resolved.ButtonData - if config.GetArrayValue() { - segmentedMessages = handlers.ConvertToSegmentedMessage(newdata) - } + var IsBindedUserId, IsBindedGroupId bool + if config.GetHashIDValue() { + IsBindedUserId = idmap.CheckValue(data.GroupMemberOpenID, userid64) + IsBindedGroupId = idmap.CheckValue(data.GroupOpenID, GroupID64) + } else { + IsBindedUserId = idmap.CheckValuev2(userid64) + IsBindedGroupId = idmap.CheckValuev2(GroupID64) + } - var IsBindedUserId, IsBindedGroupId bool - if config.GetHashIDValue() { - IsBindedUserId = idmap.CheckValue(data.GroupMemberOpenID, userid64) - IsBindedGroupId = idmap.CheckValue(data.GroupOpenID, GroupID64) - } else { - IsBindedUserId = idmap.CheckValuev2(userid64) - IsBindedGroupId = idmap.CheckValuev2(GroupID64) - } + //映射str的messageID到int + var messageID64 int64 + if config.GetMemoryMsgid() { + messageID64, err = echo.StoreCacheInMemory(data.ID) + if err != nil { + log.Fatalf("Error storing ID: %v", err) + } + } else { + messageID64, err = idmap.StoreCachev2(data.ID) + if err != nil { + log.Fatalf("Error storing ID: %v", err) + } + } - //映射str的messageID到int - var messageID64 int64 - if config.GetMemoryMsgid() { - messageID64, err = echo.StoreCacheInMemory(data.ID) - if err != nil { - log.Fatalf("Error storing ID: %v", err) + messageID := int(messageID64) + + var selfid64 int64 + if config.GetUseUin() { + selfid64 = config.GetUinint64() + } else { + selfid64 = int64(p.Settings.AppID) } - } else { - messageID64, err = idmap.StoreCachev2(data.ID) - if err != nil { - log.Fatalf("Error storing ID: %v", err) + //mylog.Printf("回调测试-interaction:%v\n", segmentedMessages) + groupMsg := OnebotGroupMessage{ + RawMessage: data.Data.Resolved.ButtonData, + Message: segmentedMessages, + MessageID: messageID, + GroupID: GroupID64, + MessageType: "group", + PostType: "message", + SelfID: selfid64, + UserID: userid64, + Sender: Sender{ + UserID: userid64, + Sex: "0", + Age: 0, + Area: "0", + Level: "0", + }, + SubType: "normal", + Time: time.Now().Unix(), + } + //增强配置 + if !config.GetNativeOb11() { + groupMsg.RealMessageType = "interaction" + groupMsg.IsBindedUserId = IsBindedUserId + groupMsg.IsBindedGroupId = IsBindedGroupId + groupMsg.RealGroupID = data.GroupOpenID + groupMsg.RealUserID = data.GroupMemberOpenID + groupMsg.Avatar, _ = GenerateAvatarURLV2(data.GroupMemberOpenID) + } + //根据条件判断是否增加nick和card + var CaN = config.GetCardAndNick() + if CaN != "" { + groupMsg.Sender.Nickname = CaN + groupMsg.Sender.Card = CaN + } + // 根据条件判断是否添加Echo字段 + if config.GetTwoWayEcho() { + groupMsg.Echo = echostr + //用向应用端(如果支持)发送echo,来确定客户端的send_msg对应的触发词原文 + echo.AddMsgIDv3(AppIDString, echostr, data.Data.Resolved.ButtonData) + } + // 获取MasterID数组 + masterIDs := config.GetMasterID() + + // 判断userid64是否在masterIDs数组里 + isMaster := false + for _, id := range masterIDs { + if strconv.FormatInt(userid64, 10) == id { + isMaster = true + break + } } - } - messageID := int(messageID64) + // 根据isMaster的值为groupMsg的Sender赋值role字段 + if isMaster { + groupMsg.Sender.Role = "owner" + } else { + groupMsg.Sender.Role = "member" + } - var selfid64 int64 - if config.GetUseUin() { - selfid64 = config.GetUinint64() - } else { - selfid64 = int64(p.Settings.AppID) - } - //mylog.Printf("回调测试-interaction:%v\n", segmentedMessages) - groupMsg := OnebotGroupMessage{ - RawMessage: data.Data.Resolved.ButtonData, - Message: segmentedMessages, - MessageID: messageID, - GroupID: GroupID64, - MessageType: "group", - PostType: "message", - SelfID: selfid64, - UserID: userid64, - Sender: Sender{ - UserID: userid64, - Sex: "0", - Age: 0, - Area: "0", - Level: "0", - }, - SubType: "normal", - Time: time.Now().Unix(), - } - //增强配置 - if !config.GetNativeOb11() { - groupMsg.RealMessageType = "interaction" - groupMsg.IsBindedUserId = IsBindedUserId - groupMsg.IsBindedGroupId = IsBindedGroupId - groupMsg.RealGroupID = data.GroupOpenID - groupMsg.RealUserID = data.GroupMemberOpenID - groupMsg.Avatar, _ = GenerateAvatarURLV2(data.GroupMemberOpenID) - } - //根据条件判断是否增加nick和card - var CaN = config.GetCardAndNick() - if CaN != "" { - groupMsg.Sender.Nickname = CaN - groupMsg.Sender.Card = CaN - } - // 根据条件判断是否添加Echo字段 - if config.GetTwoWayEcho() { - groupMsg.Echo = echostr - //用向应用端(如果支持)发送echo,来确定客户端的send_msg对应的触发词原文 - echo.AddMsgIDv3(AppIDString, echostr, data.Data.Resolved.ButtonData) - } - // 获取MasterID数组 - masterIDs := config.GetMasterID() + // 映射消息类型 + echo.AddMsgType(AppIDString, s, "group") - // 判断userid64是否在masterIDs数组里 - isMaster := false - for _, id := range masterIDs { - if strconv.FormatInt(userid64, 10) == id { - isMaster = true - break + //储存当前群或频道号的类型 + idmap.WriteConfigv2(fmt.Sprint(GroupID64), "type", "group") + + //映射类型 + echo.AddMsgType(AppIDString, GroupID64, "group") + + // 调试 + PrintStructWithFieldNames(groupMsg) + + // Convert OnebotGroupMessage to map and send + groupMsgMap := structToMap(groupMsg) + //上报信息到onebotv11应用端(正反ws) + go p.BroadcastMessageToAll(groupMsgMap, p.Apiv2, data) + + // 转换appid + AppIDString := strconv.FormatUint(p.Settings.AppID, 10) + + // 储存和群号相关的eventid + fmt.Printf("测试:储存eventid:[%v]LongGroupID64[%v]\n", data.EventID, LongGroupID64) + echo.AddEvnetID(AppIDString, LongGroupID64, data.EventID) + + // 上报事件 + notice := &OnebotInteractionNotice{ + GroupID: GroupID64, + NoticeType: "interaction", + PostType: "notice", + SelfID: selfid64, + SubType: "create", + Time: time.Now().Unix(), + UserID: userid64, + Data: data, } - } + //增强配置 + if !config.GetNativeOb11() { + notice.RealUserID = fromuid + notice.RealGroupID = fromgid + } + //调试 + PrintStructWithFieldNames(notice) - // 根据isMaster的值为groupMsg的Sender赋值role字段 - if isMaster { - groupMsg.Sender.Role = "owner" + // Convert OnebotGroupMessage to map and send + noticeMap := structToMap(notice) + + //上报信息到onebotv11应用端(正反ws) + go p.BroadcastMessageToAll(noticeMap, p.Apiv2, data) } else { - groupMsg.Sender.Role = "member" - } + //群回调 + newdata := ConvertInteractionToMessage(data) + //mylog.Printf("回调测试111-newdata:%v\n", newdata) + + // 如果在Array模式下, 则处理Message为Segment格式 + var segmentedMessages interface{} = data.Data.Resolved.ButtonData + if config.GetArrayValue() { + segmentedMessages = handlers.ConvertToSegmentedMessage(newdata) + } - // 映射消息类型 - echo.AddMsgType(AppIDString, s, "group") + var selfid64 int64 + if config.GetUseUin() { + selfid64 = config.GetUinint64() + } else { + selfid64 = int64(p.Settings.AppID) + } + //mylog.Printf("回调测试-interaction:%v\n", segmentedMessages) + groupMsg := OnebotGroupMessageS{ + RawMessage: data.Data.Resolved.ButtonData, + Message: segmentedMessages, + MessageID: data.EventID, + GroupID: data.GroupOpenID, + MessageType: "group", + PostType: "message", + SelfID: selfid64, + UserID: data.GroupMemberOpenID, + Sender: Sender{ + UserID: userid64, + Sex: "0", + Age: 0, + Area: "0", + Level: "0", + }, + SubType: "normal", + Time: time.Now().Unix(), + } + //增强配置 + if !config.GetNativeOb11() { + groupMsg.RealMessageType = "group" + groupMsg.RealGroupID = data.GroupOpenID + groupMsg.RealUserID = data.GroupMemberOpenID + groupMsg.Avatar, _ = GenerateAvatarURLV2(data.GroupMemberOpenID) + } + //根据条件判断是否增加nick和card + var CaN = config.GetCardAndNick() + if CaN != "" { + groupMsg.Sender.Nickname = CaN + groupMsg.Sender.Card = CaN + } + // 根据条件判断是否添加Echo字段 + if config.GetTwoWayEcho() { + groupMsg.Echo = echostr + //用向应用端(如果支持)发送echo,来确定客户端的send_msg对应的触发词原文 + echo.AddMsgIDv3(AppIDString, echostr, data.Data.Resolved.ButtonData) + } + // 获取MasterID数组 + masterIDs := config.GetMasterID() + + // 判断userid64是否在masterIDs数组里 + isMaster := false + for _, id := range masterIDs { + if strconv.FormatInt(userid64, 10) == id { + isMaster = true + break + } + } - //储存当前群或频道号的类型 - idmap.WriteConfigv2(fmt.Sprint(GroupID64), "type", "group") + // 根据isMaster的值为groupMsg的Sender赋值role字段 + if isMaster { + groupMsg.Sender.Role = "owner" + } else { + groupMsg.Sender.Role = "member" + } - //映射类型 - echo.AddMsgType(AppIDString, GroupID64, "group") + // 映射消息类型 + echo.AddMsgType(AppIDString, s, "group") - // 调试 - PrintStructWithFieldNames(groupMsg) + //储存当前群或频道号的类型 + //idmap.WriteConfigv2(fmt.Sprint(GroupID64), "type", "group") - // Convert OnebotGroupMessage to map and send - groupMsgMap := structToMap(groupMsg) - //上报信息到onebotv11应用端(正反ws) - go p.BroadcastMessageToAll(groupMsgMap, p.Apiv2, data) - - // 转换appid - AppIDString := strconv.FormatUint(p.Settings.AppID, 10) - - // 储存和群号相关的eventid - fmt.Printf("测试:储存eventid:[%v]LongGroupID64[%v]\n", data.EventID, LongGroupID64) - echo.AddEvnetID(AppIDString, LongGroupID64, data.EventID) - - // 上报事件 - notice := &OnebotInteractionNotice{ - GroupID: GroupID64, - NoticeType: "interaction", - PostType: "notice", - SelfID: selfid64, - SubType: "create", - Time: time.Now().Unix(), - UserID: userid64, - Data: data, - } - //增强配置 - if !config.GetNativeOb11() { - notice.RealUserID = fromuid - notice.RealGroupID = fromgid - } - //调试 - PrintStructWithFieldNames(notice) + //映射类型 + echo.AddMsgType(AppIDString, GroupID64, "group") - // Convert OnebotGroupMessage to map and send - noticeMap := structToMap(notice) + // 调试 + PrintStructWithFieldNames(groupMsg) - //上报信息到onebotv11应用端(正反ws) - go p.BroadcastMessageToAll(noticeMap, p.Apiv2, data) - } else if data.UserOpenID != "" { - //私聊回调 - newdata := ConvertInteractionToMessage(data) + // Convert OnebotGroupMessage to map and send + groupMsgMap := structToMap(groupMsg) + //上报信息到onebotv11应用端(正反ws) + go p.BroadcastMessageToAll(groupMsgMap, p.Apiv2, data) + + // 转换appid + AppIDString := strconv.FormatUint(p.Settings.AppID, 10) + + // 储存和群号相关的eventid + fmt.Printf("测试:储存eventid:[%v]LongGroupID64[%v]\n", data.EventID, LongGroupID64) + echo.AddEvnetIDv2(AppIDString, data.GroupOpenID, data.EventID) + + // 上报事件 + notice := &OnebotInteractionNotice{ + GroupID: GroupID64, + NoticeType: "interaction", + PostType: "notice", + SelfID: selfid64, + SubType: "create", + Time: time.Now().Unix(), + UserID: userid64, + Data: data, + } + //增强配置 + if !config.GetNativeOb11() { + notice.RealUserID = fromuid + notice.RealGroupID = fromgid + } + //调试 + PrintStructWithFieldNames(notice) - // 如果在Array模式下, 则处理Message为Segment格式 - var segmentedMessages interface{} = data.Data.Resolved.ButtonData - if config.GetArrayValue() { - segmentedMessages = handlers.ConvertToSegmentedMessage(newdata) - } + // Convert OnebotGroupMessage to map and send + noticeMap := structToMap(notice) - var IsBindedUserId bool - if config.GetHashIDValue() { - IsBindedUserId = idmap.CheckValue(data.UserOpenID, userid64) - } else { - IsBindedUserId = idmap.CheckValuev2(userid64) + //上报信息到onebotv11应用端(正反ws) + go p.BroadcastMessageToAll(noticeMap, p.Apiv2, data) } + } else if data.UserOpenID != "" { - //平台事件,不是真实信息,无需messageID - messageID64 := 123 + // 是否使用string形式上报 + if !config.GetStringOb11() { + //私聊回调 + newdata := ConvertInteractionToMessage(data) - messageID := int(messageID64) - var selfid64 int64 - if config.GetUseUin() { - selfid64 = config.GetUinint64() - } else { - selfid64 = int64(p.Settings.AppID) - } - privateMsg := OnebotPrivateMessage{ - RawMessage: data.Data.Resolved.ButtonData, - Message: segmentedMessages, - MessageID: messageID, - MessageType: "private", - PostType: "message", - SelfID: selfid64, - UserID: userid64, - Sender: PrivateSender{ - Nickname: "", //这个不支持,但加机器人好友,会收到一个事件,可以对应储存获取,用idmaps可以做到. - UserID: userid64, - }, - SubType: "friend", - Time: time.Now().Unix(), - } - //增强配置 - if !config.GetNativeOb11() { - privateMsg.RealMessageType = "interaction" - privateMsg.IsBindedUserId = IsBindedUserId - if IsBindedUserId { - privateMsg.Avatar, _ = GenerateAvatarURL(userid64) + // 如果在Array模式下, 则处理Message为Segment格式 + var segmentedMessages interface{} = data.Data.Resolved.ButtonData + if config.GetArrayValue() { + segmentedMessages = handlers.ConvertToSegmentedMessage(newdata) } - } - // 根据条件判断是否添加Echo字段 - if config.GetTwoWayEcho() { - privateMsg.Echo = echostr - //用向应用端(如果支持)发送echo,来确定客户端的send_msg对应的触发词原文 - echo.AddMsgIDv3(AppIDString, echostr, data.Data.Resolved.ButtonData) - } - // 映射类型 对S映射 - echo.AddMsgType(AppIDString, s, "group_private") - // 映射类型 对userid64映射 - echo.AddMsgType(AppIDString, userid64, "group_private") + var IsBindedUserId bool + if config.GetHashIDValue() { + IsBindedUserId = idmap.CheckValue(data.UserOpenID, userid64) + } else { + IsBindedUserId = idmap.CheckValuev2(userid64) + } + + //平台事件,不是真实信息,无需messageID + messageID64 := 123 + + messageID := int(messageID64) + var selfid64 int64 + if config.GetUseUin() { + selfid64 = config.GetUinint64() + } else { + selfid64 = int64(p.Settings.AppID) + } + privateMsg := OnebotPrivateMessage{ + RawMessage: data.Data.Resolved.ButtonData, + Message: segmentedMessages, + MessageID: messageID, + MessageType: "private", + PostType: "message", + SelfID: selfid64, + UserID: userid64, + Sender: PrivateSender{ + Nickname: "", //这个不支持,但加机器人好友,会收到一个事件,可以对应储存获取,用idmaps可以做到. + UserID: userid64, + }, + SubType: "friend", + Time: time.Now().Unix(), + } + //增强配置 + if !config.GetNativeOb11() { + privateMsg.RealMessageType = "interaction" + privateMsg.IsBindedUserId = IsBindedUserId + if IsBindedUserId { + privateMsg.Avatar, _ = GenerateAvatarURL(userid64) + } + } + // 根据条件判断是否添加Echo字段 + if config.GetTwoWayEcho() { + privateMsg.Echo = echostr + //用向应用端(如果支持)发送echo,来确定客户端的send_msg对应的触发词原文 + echo.AddMsgIDv3(AppIDString, echostr, data.Data.Resolved.ButtonData) + } + // 映射类型 对S映射 + echo.AddMsgType(AppIDString, s, "group_private") + + // 映射类型 对userid64映射 + echo.AddMsgType(AppIDString, userid64, "group_private") + + // 持久化储存当前用户的类型 + idmap.WriteConfigv2(fmt.Sprint(userid64), "type", "group_private") + + // 调试 + PrintStructWithFieldNames(privateMsg) - // 持久化储存当前用户的类型 - idmap.WriteConfigv2(fmt.Sprint(userid64), "type", "group_private") + // Convert OnebotGroupMessage to map and send + privateMsgMap := structToMap(privateMsg) - // 调试 - PrintStructWithFieldNames(privateMsg) + if privateMsg.RawMessage != "" { + //上报信息到onebotv11应用端(正反ws) + go p.BroadcastMessageToAll(privateMsgMap, p.Apiv2, data) + } + + // 转换appid + AppIDString := strconv.FormatUint(p.Settings.AppID, 10) + + // 储存和用户ID相关的eventid + echo.AddEvnetID(AppIDString, LongUserID64, data.EventID) + + // 上报事件 + notice := &OnebotInteractionNotice{ + GroupID: GroupID64, + NoticeType: "interaction", + PostType: "notice", + SelfID: selfid64, + SubType: "create", + Time: time.Now().Unix(), + UserID: userid64, + Data: data, + } + //增强配置 + if !config.GetNativeOb11() { + notice.RealUserID = fromuid + notice.RealGroupID = fromgid + } + //调试 + PrintStructWithFieldNames(notice) - // Convert OnebotGroupMessage to map and send - privateMsgMap := structToMap(privateMsg) + // Convert OnebotGroupMessage to map and send + noticeMap := structToMap(notice) - if privateMsg.RawMessage != "" { //上报信息到onebotv11应用端(正反ws) - go p.BroadcastMessageToAll(privateMsgMap, p.Apiv2, data) - } + go p.BroadcastMessageToAll(noticeMap, p.Apiv2, data) + } else { + // 这里应该还区分 是否虚拟私信为群聊 这里默认是虚拟成群聊 + //私聊回调 + newdata := ConvertInteractionToMessage(data) + + // 如果在Array模式下, 则处理Message为Segment格式 + var segmentedMessages interface{} = data.Data.Resolved.ButtonData + if config.GetArrayValue() { + segmentedMessages = handlers.ConvertToSegmentedMessage(newdata) + } - // 转换appid - AppIDString := strconv.FormatUint(p.Settings.AppID, 10) - - // 储存和用户ID相关的eventid - echo.AddEvnetID(AppIDString, LongUserID64, data.EventID) - - // 上报事件 - notice := &OnebotInteractionNotice{ - GroupID: GroupID64, - NoticeType: "interaction", - PostType: "notice", - SelfID: selfid64, - SubType: "create", - Time: time.Now().Unix(), - UserID: userid64, - Data: data, - } - //增强配置 - if !config.GetNativeOb11() { - notice.RealUserID = fromuid - notice.RealGroupID = fromgid - } - //调试 - PrintStructWithFieldNames(notice) + var selfid64 int64 + if config.GetUseUin() { + selfid64 = config.GetUinint64() + } else { + selfid64 = int64(p.Settings.AppID) + } + privateMsg := OnebotGroupMessageS{ + RawMessage: data.Data.Resolved.ButtonData, + Message: segmentedMessages, + MessageID: data.EventID, + MessageType: "group", + PostType: "message", + SelfID: selfid64, + GroupID: data.UserOpenID, + UserID: data.UserOpenID, + Sender: Sender{ + UserID: userid64, + }, + SubType: "normal", + Time: time.Now().Unix(), + } + //增强配置 + if !config.GetNativeOb11() { + privateMsg.RealMessageType = "group_private" + privateMsg.RealUserID = data.UserOpenID + } + // 根据条件判断是否添加Echo字段 + if config.GetTwoWayEcho() { + privateMsg.Echo = echostr + //用向应用端(如果支持)发送echo,来确定客户端的send_msg对应的触发词原文 + echo.AddMsgIDv3(AppIDString, echostr, data.Data.Resolved.ButtonData) + } + // 映射类型 对S映射 + echo.AddMsgType(AppIDString, s, "group_private") - // Convert OnebotGroupMessage to map and send - noticeMap := structToMap(notice) + // 映射类型 对userid64映射 + echo.AddMsgType(AppIDString, userid64, "group_private") - //上报信息到onebotv11应用端(正反ws) - go p.BroadcastMessageToAll(noticeMap, p.Apiv2, data) + // 持久化储存当前用户的类型 + //idmap.WriteConfigv2(fmt.Sprint(userid64), "type", "group_private") + + // 调试 + PrintStructWithFieldNames(privateMsg) + + // Convert OnebotGroupMessage to map and send + privateMsgMap := structToMap(privateMsg) + + if privateMsg.RawMessage != "" { + //上报信息到onebotv11应用端(正反ws) + go p.BroadcastMessageToAll(privateMsgMap, p.Apiv2, data) + } + + // 转换appid + AppIDString := strconv.FormatUint(p.Settings.AppID, 10) + + // 储存和用户ID相关的eventid + echo.AddEvnetIDv2(AppIDString, data.UserOpenID, data.EventID) + + // 上报事件 + notice := &OnebotInteractionNotice{ + GroupID: GroupID64, + NoticeType: "interaction", + PostType: "notice", + SelfID: selfid64, + SubType: "create", + Time: time.Now().Unix(), + UserID: userid64, + Data: data, + } + //增强配置 + if !config.GetNativeOb11() { + notice.RealUserID = fromuid + notice.RealGroupID = fromgid + } + //调试 + PrintStructWithFieldNames(notice) + + // Convert OnebotGroupMessage to map and send + noticeMap := structToMap(notice) + + //上报信息到onebotv11应用端(正反ws) + go p.BroadcastMessageToAll(noticeMap, p.Apiv2, data) + } } else { // TODO: 区分频道和频道私信 如果有人提需求 // 频道回调 diff --git a/echo/echo.go b/echo/echo.go index 461662e2..e2094029 100644 --- a/echo/echo.go +++ b/echo/echo.go @@ -143,6 +143,10 @@ func (e *EchoMapping) GenerateKeyEventID(appid string, groupid int64) string { return appid + "_" + strconv.FormatInt(groupid, 10) } +func (e *EchoMapping) GenerateKeyEventIDV2(appid string, groupid string) string { + return appid + "_" + groupid +} + func (e *EchoMapping) GenerateKeyv3(appid string, s string) string { return appid + "_" + s } @@ -181,6 +185,12 @@ func AddEvnetID(appid string, groupid int64, eventID string) { globalEchoMapping.eventIDMapping.Store(key, eventID) } +// 添加group对应的eventid +func AddEvnetIDv2(appid string, groupid string, eventID string) { + key := globalEchoMapping.GenerateKeyEventIDV2(appid, groupid) + globalEchoMapping.eventIDMapping.Store(key, eventID) +} + // 添加echo对应的messageid func AddMsgID(appid string, s int64, msgID string) { key := globalEchoMapping.GenerateKey(appid, s) diff --git a/echo/messageidmap.go b/echo/messageidmap.go index 51a8e9e8..39e6ccae 100644 --- a/echo/messageidmap.go +++ b/echo/messageidmap.go @@ -1,7 +1,6 @@ package echo import ( - "math/rand" "strconv" "strings" "sync" @@ -13,13 +12,13 @@ import ( ) type messageRecord struct { - messageID string - timestamp time.Time + messageID string + timestamp time.Time + usageCount int // 新增字段,记录使用次数,默认为0 } type messageStore struct { - mu sync.RWMutex - records map[string][]messageRecord + records sync.Map // 使用 sync.Map 代替普通 map } var instance *messageStore @@ -28,9 +27,7 @@ var once sync.Once // 惰性初始化 func initInstance() *messageStore { once.Do(func() { - instance = &messageStore{ - records: make(map[string][]messageRecord), - } + instance = &messageStore{} }) return instance } @@ -38,89 +35,178 @@ func initInstance() *messageStore { // AddLazyMessageId 添加 message_id 和它的时间戳到指定群号 func AddLazyMessageId(groupID, messageID string, timestamp time.Time) { store := initInstance() - store.mu.Lock() - defer store.mu.Unlock() - store.records[groupID] = append(store.records[groupID], messageRecord{messageID: messageID, timestamp: timestamp}) + + // 初始化 messageRecord + record := messageRecord{ + messageID: messageID, + timestamp: timestamp, + usageCount: 0, // 默认使用次数为 0 + } + + // 从 sync.Map 获取现有记录 + value, ok := store.records.Load(groupID) + if ok { + // 如果已有记录,追加新记录 + records := value.([]messageRecord) + store.records.Store(groupID, append(records, record)) + } else { + // 如果没有记录,初始化新记录 + store.records.Store(groupID, []messageRecord{record}) + } } -// AddLazyMessageId 添加 message_id 和它的时间戳到指定群号 +// AddLazyMessageIdv2 添加 message_id 和它的时间戳到指定群号 func AddLazyMessageIdv2(groupID, userID, messageID string, timestamp time.Time) { store := initInstance() - store.mu.Lock() - defer store.mu.Unlock() + + // 组合键 key := groupID + "." + userID - store.records[key] = append(store.records[key], messageRecord{messageID: messageID, timestamp: timestamp}) + + // 初始化 messageRecord,并设置 usageCount 为 0 + record := messageRecord{ + messageID: messageID, + timestamp: timestamp, + usageCount: 0, // 默认使用次数为0 + } + + // 通过 sync.Map 读取现有数据 + value, ok := store.records.Load(key) + if ok { + // 如果已存在,追加新记录 + existingRecords := value.([]messageRecord) + store.records.Store(key, append(existingRecords, record)) + } else { + // 如果不存在,初始化记录 + store.records.Store(key, []messageRecord{record}) + } } -// GetRecentMessages 获取指定群号中最近5分钟内的 message_id~ +// GetLazyMessagesId 获取指定群号中最近 4 分钟内的 message_id func GetLazyMessagesId(groupID string) string { store := initInstance() - store.mu.RLock() - defer store.mu.RUnlock() - - fiveMinutesAgo := time.Now().Add(-4 * time.Minute) - var recentMessages []string - for _, record := range store.records[groupID] { - if record.timestamp.After(fiveMinutesAgo) { - recentMessages = append(recentMessages, record.messageID) - } + + // 获取当前时间和时间窗口 + now := time.Now() + fourMinutesAgo := now.Add(-4 * time.Minute) + + // 从 sync.Map 获取记录 + value, ok := store.records.Load(groupID) + if !ok { + return generateDefaultMessageID(groupID) } - var randomMessageID string - if len(recentMessages) > 0 { - randomIndex := rand.Intn(len(recentMessages)) - randomMessageID = recentMessages[randomIndex] - } else { - groupIDint64, err := idmap.StoreIDv2(groupID) - if err != nil { - mylog.Printf("Error storing ID 75: %v", err) - return "2000" //主动信息(不知道消息类型,按2000,纯主动信息处理) - } - msgType := GetMessageTypeByGroupidv2(config.GetAppIDStr(), groupIDint64) - if strings.HasPrefix(msgType, "guild") { - randomMessageID = "1000" // 频道主动信息 - } else { - randomMessageID = "2000" //群主动信息 + + // 类型断言并筛选最近 4 分钟的消息 + records, ok := value.([]messageRecord) + if !ok || len(records) == 0 { + return generateDefaultMessageID(groupID) + } + + var selectedRecord *messageRecord + var validRecords []messageRecord + + // 筛选最近 4 分钟的消息,同时找出使用次数为0且时间最近的记录 + for i := range records { + record := records[i] + if record.timestamp.After(fourMinutesAgo) { + // 添加到有效记录中 + validRecords = append(validRecords, record) + + // 优先选择 usageCount == 0 且时间最近的记录 + if record.usageCount == 0 { + if selectedRecord == nil || record.timestamp.After(selectedRecord.timestamp) { + selectedRecord = &validRecords[len(validRecords)-1] // 指向新增的有效记录 + } + } else if selectedRecord == nil { + // 如果没有 usageCount == 0 的,选择时间最近的 + selectedRecord = &validRecords[len(validRecords)-1] + } } } - return randomMessageID + + // 如果找到合适的记录,更新其 usageCount + if selectedRecord != nil { + selectedRecord.usageCount++ + } + + // 更新有效记录到 sync.Map(仅更新一次) + store.records.Store(groupID, validRecords) + + // 返回选中的 messageID 或生成默认消息ID + if selectedRecord != nil { + return selectedRecord.messageID + } + + return generateDefaultMessageID(groupID) } -// GetLazyMessagesIdv2 获取指定群号和用户ID中最近5分钟内的 message_id -func GetLazyMessagesIdv2(groupID, userID string) string { //1 +func GetLazyMessagesIdv2(groupID, userID string) string { store := initInstance() - store.mu.RLock() - defer store.mu.RUnlock() - - // 构建复合键 + now := time.Now() // 统一时间基准 + fourMinutesAgo := now.Add(-4 * time.Minute) key := groupID + "." + userID - fiveMinutesAgo := time.Now().Add(-4 * time.Minute) - var recentMessages []string - for _, record := range store.records[key] { - if record.timestamp.After(fiveMinutesAgo) { - recentMessages = append(recentMessages, record.messageID) - } + // 获取记录 + value, ok := store.records.Load(key) + if !ok { + // 如果没有找到记录,生成默认消息ID + return generateDefaultMessageID(groupID) } - var randomMessageID string - if len(recentMessages) > 0 { - randomIndex := rand.Intn(len(recentMessages)) - randomMessageID = recentMessages[randomIndex] - } else { - // 如果没有找到最近消息,处理默认行为 - groupIDint64, err := idmap.StoreIDv2(groupID) - if err != nil { - mylog.Printf("Error storing ID 75: %v", err) - return "2000" //主动信息(不知道消息类型,按2000,纯主动信息处理) - } - msgType := GetMessageTypeByGroupidv2(config.GetAppIDStr(), groupIDint64) - if strings.HasPrefix(msgType, "guild") { - randomMessageID = "1000" // 频道主动信息 - } else { - randomMessageID = "2000" //群主动信息 + // 类型断言并检查记录是否为空 + records, ok := value.([]messageRecord) + if !ok || len(records) == 0 { + return generateDefaultMessageID(groupID) + } + + // 筛选最近 4 分钟的记录并找最优记录,同时清理过期记录 + var selectedRecord *messageRecord + var validRecords []messageRecord + + for i := range records { + record := records[i] + if record.timestamp.After(fourMinutesAgo) { + // 保留有效记录 + validRecords = append(validRecords, record) + + // 优先选择 usageCount == 0 且时间最近的 + if record.usageCount == 0 { + if selectedRecord == nil || record.timestamp.After(selectedRecord.timestamp) { + selectedRecord = &validRecords[len(validRecords)-1] // 指向新增的有效记录 + } + } else if selectedRecord == nil { + // 如果没有 usageCount == 0 的,选择时间最近的 + selectedRecord = &validRecords[len(validRecords)-1] // 指向新增的有效记录 + } } } - return randomMessageID + + // 如果找到合适的记录,更新其 usageCount + if selectedRecord != nil { + selectedRecord.usageCount++ // 更新选中记录的 usageCount + } + + // 更新有效记录到 sync.Map(仅更新一次) + store.records.Store(key, validRecords) + + // 返回选中的 messageID 或生成默认消息ID + if selectedRecord != nil { + return selectedRecord.messageID + } + return generateDefaultMessageID(groupID) +} + +// 生成默认消息ID的逻辑拆分为独立函数 +func generateDefaultMessageID(groupID string) string { + groupIDint64, err := idmap.StoreIDv2(groupID) + if err != nil { + mylog.Printf("Error storing ID: %v", err) + return "2000" + } + msgType := GetMessageTypeByGroupidv2(config.GetAppIDStr(), groupIDint64) + if strings.HasPrefix(msgType, "guild") { + return "1000" + } + return "2000" } // 通过group_id获取类型 diff --git a/handlers/send_group_msg.go b/handlers/send_group_msg.go index 10ca8e60..2f4cac00 100644 --- a/handlers/send_group_msg.go +++ b/handlers/send_group_msg.go @@ -243,7 +243,11 @@ func HandleSendGroupMsg(client callapi.Client, api openapi.OpenAPI, apiv2 openap if messageID == "2000" { messageID = "" mylog.Println("通过lazymessage_id模式发送群聊/频道主动信息,群聊每月仅4次机会,如果本信息非主动推送信息,请提交issue") - eventID = GetEventIDByUseridOrGroupid(config.GetAppIDStr(), message.Params.GroupID) + if len(message.Params.GroupID.(string)) != 32 { + eventID = GetEventIDByUseridOrGroupid(config.GetAppIDStr(), message.Params.GroupID) + }else{ + eventID = GetEventIDByUseridOrGroupidv2(config.GetAppIDStr(), message.Params.GroupID) + } mylog.Printf("尝试获取当前是否有eventID可用,如果有则不消耗主动次数:%v", eventID) } mylog.Printf("群组发信息使用messageID:[%v]", messageID) diff --git a/handlers/send_msg.go b/handlers/send_msg.go index 0f8d5eb7..045e7205 100644 --- a/handlers/send_msg.go +++ b/handlers/send_msg.go @@ -275,6 +275,31 @@ func GetEventIDByUseridOrGroupid(appID string, userID interface{}) string { return eventid } +// 通过user_id获取EventID 私聊,群,频道,通用 userID可以是三者之一 这是不需要区分群+用户的 只需要精准到群 私聊只需要精准到用户 idmap不开启的用户使用 +func GetEventIDByUseridOrGroupidv2(appID string, userID interface{}) string { + // 从appID和userID生成key + var userIDStr string + switch u := userID.(type) { + case int: + userIDStr = strconv.Itoa(u) + case int64: + userIDStr = strconv.FormatInt(u, 10) + case float64: + userIDStr = strconv.FormatFloat(u, 'f', 0, 64) + case string: + userIDStr = u + default: + // 可能需要处理其他类型或报错 + return "" + } + + key := appID + "_" + userIDStr + mylog.Printf("GetEventIDByUseridOrGroupid_key:%v", key) + eventid := echo.GetEventIDByKey(key) + + return eventid +} + // 通过user_id获取messageID func GetMessageIDByUseridAndGroupid(appID string, userID interface{}, groupID interface{}) string { // 从appID和userID生成key diff --git a/handlers/send_private_msg.go b/handlers/send_private_msg.go index fb929d93..d65c917d 100644 --- a/handlers/send_private_msg.go +++ b/handlers/send_private_msg.go @@ -165,7 +165,11 @@ func HandleSendPrivateMsg(client callapi.Client, api openapi.OpenAPI, apiv2 open if messageID == "2000" { messageID = "" mylog.Println("通过lazymsgid发送群私聊主动信息,每月可发送1次") - eventID = GetEventIDByUseridOrGroupid(config.GetAppIDStr(), message.Params.UserID) + if len(message.Params.UserID.(string)) != 32 { + eventID = GetEventIDByUseridOrGroupid(config.GetAppIDStr(), message.Params.UserID) + } else { + eventID = GetEventIDByUseridOrGroupidv2(config.GetAppIDStr(), message.Params.UserID) + } mylog.Printf("尝试获取当前是否有eventID可用,如果有则不消耗主动次数:%v", eventID) } //开发环境用 私聊不可用1000