diff --git a/Processor/ProcessGroupAddBot.go b/Processor/ProcessGroupAddBot.go index 4bf65c5a..912b0ed8 100644 --- a/Processor/ProcessGroupAddBot.go +++ b/Processor/ProcessGroupAddBot.go @@ -159,7 +159,7 @@ func (p *Processors) ProcessGroupAddBot(data *dto.GroupAddBotEvent) error { // 创建 ActionMessage 实例 message := callapi.ActionMessage{ - Action: "send_group_msg", + Action: "send_group_msg_group", Params: callapi.ParamsContent{ GroupID: strconv.FormatInt(GroupID64, 10), // 转换 GroupID 类型 UserID: strconv.FormatInt(userid64, 10), @@ -172,7 +172,13 @@ func (p *Processors) ProcessGroupAddBot(data *dto.GroupAddBotEvent) error { _, err = handlers.HandleSendGroupMsg(client, p.Api, p.Apiv2, message) if err != nil { mylog.Printf("自我介绍发送失败%v", err) - return nil } + + //link指令 + if config.GetAutoLink() { + md, kb := generateMdByConfig() + SendMessageMdAddBot(md, kb, data, p.Api, p.Apiv2) + } + return nil } diff --git a/Processor/Processor.go b/Processor/Processor.go index c10db3fb..12af8330 100644 --- a/Processor/Processor.go +++ b/Processor/Processor.go @@ -928,6 +928,31 @@ func SendMessageMd(md *dto.Markdown, kb *keyboard.MessageKeyboard, data interfac return nil } +// SendMessageMdAddBot 发送Md消息在AddBot事件 +func SendMessageMdAddBot(md *dto.Markdown, kb *keyboard.MessageKeyboard, data *dto.GroupAddBotEvent, api openapi.OpenAPI, apiv2 openapi.OpenAPI) error { + + // 处理群组消息 + msgseq := echo.GetMappingSeq(data.EventID) + echo.AddMappingSeq(data.ID, msgseq+1) + Message := &dto.MessageToCreate{ + Content: "markdown", + EventID: data.EventID, + MsgSeq: msgseq, + Markdown: md, + Keyboard: kb, + MsgType: 2, //md信息 + } + + Message.Timestamp = time.Now().Unix() // 设置时间戳 + _, err := apiv2.PostGroupMessage(context.TODO(), data.GroupOpenID, Message) + if err != nil { + mylog.Printf("发送文本群组信息失败: %v", err) + return err + } + + return nil +} + // autobind 函数接受 interface{} 类型的数据 // commit by 紫夜 2023-11-19 func (p *Processors) Autobind(data interface{}) error { @@ -1084,30 +1109,50 @@ func generateMdByConfig() (md *dto.Markdown, kb *keyboard.MessageKeyboard) { linkBots = getRandomSelection(linkBots, 16) } - //组合 mdParams var mdParams []*dto.MarkdownParams - if imgURL != "" { - height, width, err := images.GetImageDimensions(imgURL) - if err != nil { - mylog.Printf("获取图片宽高出错") + if !config.GetNativeMD() { + //组合 mdParams + 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}}, + } } - 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}}, + + // 组合模板 Markdown + md = &dto.Markdown{ + CustomTemplateID: CustomTemplateID, + Params: mdParams, } } else { - mdParams = []*dto.MarkdownParams{ - {Key: "text_end", Values: []string{mdtext}}, + // 使用原生Markdown格式 + var content string + if imgURL != "" { + height, width, err := images.GetImageDimensions(imgURL) + if err != nil { + mylog.Printf("获取图片宽高出错") + } + imgDesc := fmt.Sprintf("图片 #%dpx #%dpx", width, height) + content = fmt.Sprintf("![%s](%s)\n%s", imgDesc, imgURL, mdtext) + } else { + content = mdtext } - } - // 组合模板 Markdown - md = &dto.Markdown{ - CustomTemplateID: CustomTemplateID, - Params: mdParams, + // 原生 Markdown + md = &dto.Markdown{ + Content: content, + } } // 创建自定义键盘 @@ -1117,30 +1162,52 @@ func generateMdByConfig() (md *dto.Markdown, kb *keyboard.MessageKeyboard) { for _, bot := range linkBots { parts := strings.SplitN(bot, "-", 3) - if len(parts) < 3 { + if len(parts) != 3 && len(parts) != 2 { 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", - }, + var button *keyboard.Button + if len(parts) == 3 { + 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", + }, + } + } else if len(parts) == 2 { + boturl := parts[0] + name := parts[1] + + 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 { + lines := config.GetLinkLines() + + // 如果当前行为空或已满(lines个按钮),则创建一个新行 + if currentRow == nil || buttonCount == lines { currentRow = &keyboard.Row{} customKeyboard.Rows = append(customKeyboard.Rows, currentRow) buttonCount = 0 diff --git a/config/config.go b/config/config.go index 79f57b77..32930042 100644 --- a/config/config.go +++ b/config/config.go @@ -2302,3 +2302,28 @@ func GetDowntimeMessage() string { } return "" } + +// 获取GetAutoLink的值 +func GetAutoLink() bool { + mu.Lock() + defer mu.Unlock() + + if instance == nil { + mylog.Println("Warning: instance is nil when trying to AutoLink value.") + return false + } + return instance.Settings.AutoLink +} + +// 获取GetLinkLines的值 +func GetLinkLines() int { + mu.Lock() + defer mu.Unlock() + + if instance == nil { + mylog.Println("Warning: instance is nil when trying to LinkLines value.") + return 2 //默认2个一行 + } + + return instance.Settings.LinkLines +} diff --git a/handlers/send_group_msg.go b/handlers/send_group_msg.go index 8d5608c8..1ad8256b 100644 --- a/handlers/send_group_msg.go +++ b/handlers/send_group_msg.go @@ -84,6 +84,12 @@ func HandleSendGroupMsg(client callapi.Client, api openapi.OpenAPI, apiv2 openap mylog.Printf("send_group_msgs接收到错误action: %v", message) return "", nil } + + // 内部逻辑 ProcessGroupAddBot.go 中定义的 通过http和ws无法触发 锁定类型 + if message.Action == "send_group_msg_group" { + msgType = "group" + } + mylog.Printf("send_group_msg获取到信息类型:%v", msgType) var idInt64 int64 var err error diff --git a/idmap/service.go b/idmap/service.go index bca6c94a..d6029185 100644 --- a/idmap/service.go +++ b/idmap/service.go @@ -15,6 +15,7 @@ import ( "strconv" "strings" "sync" + "time" "github.com/hoshinonyaruko/gensokyo/config" "github.com/hoshinonyaruko/gensokyo/mylog" @@ -77,6 +78,7 @@ func ClearBucket(bucketName string) { // 获取指定的bucket bucket := tx.Bucket([]byte(bucketName)) if bucket == nil { + mylog.Printf("ids表不存在.") return nil // 如果bucket不存在,直接返回nil } @@ -94,9 +96,57 @@ func ClearBucket(bucketName string) { log.Fatalf("Error clearing bucket %s: %v", bucketName, err) } else { mylog.Printf("ids清理成功.") + err := Compaction("idmap.db", "idmap_compacted.db") + if err != nil { + log.Fatalf("Failed to compact database: %v", err) + } else { + log.Println("Database compaction successful.") + // 可选:替换旧数据库文件 + // os.Remove("idmap.db") + // os.Rename("idmap_compacted.db", "idmap.db") + log.Println("请手动备份原始idmap.db(可选)并将idmap_compacted.db改名为idmap.db") + } } } +// Compaction 创建一个新的数据库文件并复制现有的数据到这个新文件中 +func Compaction(sourceDBPath, targetDBPath string) error { + // 创建目标数据库文件 + targetDB, err := bbolt.Open(targetDBPath, 0600, &bbolt.Options{Timeout: 1 * time.Second}) + if err != nil { + return err + } + defer targetDB.Close() + + // 从源数据库复制数据到目标数据库 + err = db.View(func(tx *bbolt.Tx) error { + return tx.ForEach(func(name []byte, b *bbolt.Bucket) error { + // 在目标数据库中创建相同的bucket + return targetDB.Update(func(tx2 *bbolt.Tx) error { + bucket, err := tx2.CreateBucketIfNotExists(name) + if err != nil { + return err + } + // 复制所有键值对 + return b.ForEach(func(k, v []byte) error { + return bucket.Put(k, v) + }) + }) + }) + }) + + if err != nil { + return err + } + + // 确保所有操作都已完成 + if err := targetDB.Sync(); err != nil { + return err + } + + return nil +} + func CloseDB() { db.Close() } diff --git a/main.go b/main.go index 2387ef5a..ac6d724b 100644 --- a/main.go +++ b/main.go @@ -209,6 +209,7 @@ func main() { mylog.Printf("开始清理ids\n") idmap.ClearBucket("ids") mylog.Printf("ids清理完成\n") + return } if configURL == "" && !fix11300 { //初始化handlers diff --git a/structs/structs.go b/structs/structs.go index 29c7c725..5c1bfa09 100644 --- a/structs/structs.go +++ b/structs/structs.go @@ -148,10 +148,12 @@ type Settings struct { MePrefix string `yaml:"me_prefix"` UnlockPrefix string `yaml:"unlock_prefix"` LinkPrefix string `yaml:"link_prefix"` + AutoLink bool `yaml:"auto_link"` MusicPrefix string `yaml:"music_prefix"` LinkBots []string `yaml:"link_bots"` LinkText string `yaml:"link_text"` LinkPic string `yaml:"link_pic"` + LinkLines int `yaml:"link_lines"` //HTTP API配置 HttpAddress string `yaml:"http_address"` AccessToken string `yaml:"http_access_token"` diff --git a/template/config_template.go b/template/config_template.go index 48863c48..cc137df2 100644 --- a/template/config_template.go +++ b/template/config_template.go @@ -190,10 +190,12 @@ settings: me_prefix : "/me" #需设置 #增强配置项 master_id 可触发 unlock_prefix : "/unlock" #频道私信卡住了? gsk可以帮到你 在任意子频道发送unlock 你会收到来自机器人的频道私信 link_prefix : "/link" #友情链接配置 配置custom_template_id后可用(https://www.yuque.com/km57bt/hlhnxg/tzbr84y59dbz6pib) + auto_link : false #友情链接最高礼仪,机器人被添加到群内时发送友情链接. music_prefix : "点歌" #[CQ:music,type=qq,id=123] 在消息文本组合qq音乐歌曲id,可以发送点歌,这是歌曲按钮第二个按钮的填充内容,应为你的机器人点歌插件的指令. - link_bots : ["",""] #发送友情链接时 下方按钮携带的机器人 格式 "appid-qq-name","appid-qq-name" + link_bots : ["",""] #发送友情链接时 下方按钮携带的机器人 格式 "appid-qq-name","appid-qq-name"或"http://xxx.com-文字" 链接中的-号自行用%2D替换 如 cgi-bin替换为cgi%2Dbin link_text : "" #友情链接文本 不可为空! link_pic : "" #友情链接图片 可为空 需url图片 可带端口 不填可能会有显示错误 + link_lines : 2 #内置的/link指令按钮列数(默认一行2个按钮) #HTTP API配置-正向http http_address: "" #http监听地址 与websocket独立 示例:0.0.0.0:5700 为空代表不开启