From 4614ad38bbd2d16b187d176b3669d5b5818b6732 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=B0=8F=E9=94=85=E9=A5=AD?= <1156544355@qq.com> Date: Fri, 4 Jul 2025 17:37:04 +0800 Subject: [PATCH 1/4] =?UTF-8?q?=E2=9C=A8=20=E6=B7=BB=E5=8A=A0ai=E5=A3=B0?= =?UTF-8?q?=E8=81=8A?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- airecord/record.go | 130 ++++++++++++++++++++++++++++++++++++++++++ airecord/recordcfg.go | 72 +++++++++++++++++++++++ 2 files changed, 202 insertions(+) create mode 100644 airecord/record.go create mode 100644 airecord/recordcfg.go diff --git a/airecord/record.go b/airecord/record.go new file mode 100644 index 0000000..928c3d9 --- /dev/null +++ b/airecord/record.go @@ -0,0 +1,130 @@ +// Package aichat OpenAI聊天 +package aichat + +import ( + "strconv" + "strings" + "time" + + "github.com/tidwall/gjson" + + zero "github.com/wdvxdr1123/ZeroBot" + "github.com/wdvxdr1123/ZeroBot/message" + + ctrl "github.com/FloatTech/zbpctrl" + "github.com/FloatTech/zbputils/control" +) + +func init() { + en := control.AutoRegister(&ctrl.Options[*zero.Ctx]{ + DisableOnDefault: false, + Extra: control.ExtraFromString("airecord"), + Brief: "群应用:AI声聊", + Help: "- 设置AI语音群号1048452984 (tips:群里必须有AI声聊应用)\n" + + "- 设置AI语音模型\n" + + "- 查看AI语音配置\n" + + "- 发送AI语音xxx", + PrivateDataFolder: "airecord", + }) + en.OnPrefix("设置AI语音群号", ensureRecordConfig, zero.SuperUserPermission).SetBlock(true). + Handle(func(ctx *zero.Ctx) { + u := strings.TrimSpace(ctx.State["args"].(string)) + num, err := strconv.ParseInt(u, 10, 64) + if err != nil { + ctx.SendChain(message.Text("ERROR: parse gid err: ", err)) + return + } + c, ok := ctx.State["manager"].(*ctrl.Control[*zero.Ctx]) + if !ok { + ctx.SendChain(message.Text("ERROR: no such plugin")) + return + } + err = c.SetExtra(&recordcfg) + if err != nil { + ctx.SendChain(message.Text("ERROR: set extra err: ", err)) + return + } + setCustomGID(num) + ctx.SendChain(message.Text("设置AI语音群号为", num)) + }) + en.OnFullMatch("设置AI语音模型", ensureRecordConfig, zero.SuperUserPermission).SetBlock(true). + Handle(func(ctx *zero.Ctx) { + next := zero.NewFutureEvent("message", 999, false, ctx.CheckSession()) + recv, cancel := next.Repeat() + defer cancel() + c, ok := ctx.State["manager"].(*ctrl.Control[*zero.Ctx]) + if !ok { + ctx.SendChain(message.Text("ERROR: no such plugin")) + return + } + jsonData := ctx.GetAICharacters(0, 1) + + // 转换为字符串数组 + var names []string + // 初始化两个映射表 + nameToID := make(map[string]string) + nameToURL := make(map[string]string) + characters := jsonData.Get("#.characters") + + // 遍历每个角色对象 + characters.ForEach(func(_, group gjson.Result) bool { + group.ForEach(func(_, character gjson.Result) bool { + // 提取当前角色的三个字段 + name := character.Get("character_name").String() + names = append(names, name) + // 存入映射表(重复名称会覆盖,保留最后出现的条目) + nameToID[name] = character.Get("character_id").String() + nameToURL[name] = character.Get("preview_url").String() + return true // 继续遍历 + }) + return true // 继续遍历 + }) + var builder strings.Builder + // 写入开头文本 + builder.WriteString("请选择语音模型序号:\n") + + // 遍历names数组,拼接序号和名称 + for i, v := range names { + // 将数字转换为字符串(不依赖fmt) + numStr := strconv.Itoa(i) + // 拼接格式:"序号. 名称\n" + builder.WriteString(numStr) + builder.WriteString(". ") + builder.WriteString(v) + builder.WriteString("\n") + } + // 获取最终字符串 + ctx.SendChain(message.Text(builder.String())) + for { + select { + case <-time.After(time.Second * 120): + ctx.SendChain(message.Text("设置AI语音模型指令过期")) + return + case ct := <-recv: + msg := ct.Event.Message.ExtractPlainText() + num, err := strconv.Atoi(msg) + if err != nil { + ctx.SendChain(message.Text("请输入数字!")) + continue + } + if num < 0 || num >= len(names) { + ctx.SendChain(message.Text("序号非法!")) + continue + } + setRecordModel(names[num], nameToID[names[num]]) + err = c.SetExtra(&recordcfg) + if err != nil { + ctx.SendChain(message.Text("ERROR: set extra err: ", err)) + return + } + ctx.SendChain(message.Text("已选择语音模型: ", names[num])) + ctx.SendChain(message.Record(nameToURL[names[num]])) + return + } + } + }) + en.OnFullMatch("查看AI语音配置", ensureRecordConfig, zero.SuperUserPermission).SetBlock(true). + Handle(func(ctx *zero.Ctx) { + ctx.SendChain(message.Text(printRecordConfig(recordcfg))) + }) +} diff --git a/airecord/recordcfg.go b/airecord/recordcfg.go new file mode 100644 index 0000000..32ed8b1 --- /dev/null +++ b/airecord/recordcfg.go @@ -0,0 +1,72 @@ +package aichat + +import ( + "fmt" + "strings" + + ctrl "github.com/FloatTech/zbpctrl" + "github.com/sirupsen/logrus" + zero "github.com/wdvxdr1123/ZeroBot" +) + +var ( + recordcfg = newrecordconfig() +) + +// recordconfig 存储语音记录相关配置 +type recordconfig struct { + ModelName string // 语音模型名称 + ModelID string // 语音模型ID + Customgid int64 // 自定义群ID +} + +// newrecordconfig 创建并返回默认语音记录配置 +func newrecordconfig() recordconfig { + return recordconfig{} +} + +// getRecordConfig 返回当前语音记录配置信息 +func getRecordConfig() recordconfig { + return recordcfg +} + +// setRecordModel 设置语音记录模型 +func setRecordModel(modelName, modelID string) { + recordcfg.ModelName = modelName + recordcfg.ModelID = modelID +} + +// setCustomGID 设置自定义群ID +func setCustomGID(gid int64) { + recordcfg.Customgid = gid +} + +// isvalid 检查语音记录配置是否有效 +func (c *recordconfig) isvalid() bool { + return c.ModelName != "" && c.ModelID != "" && c.Customgid != 0 +} + +// ensureRecordConfig 确保语音记录配置存在 +func ensureRecordConfig(ctx *zero.Ctx) bool { + c, ok := ctx.State["manager"].(*ctrl.Control[*zero.Ctx]) + if !ok { + return false + } + if !recordcfg.isvalid() { + err := c.GetExtra(&recordcfg) + if err != nil { + logrus.Warnln("ERROR: get extra err:", err) + } + } + return true +} + +// printRecordConfig 生成格式化的语音记录配置信息字符串 +func printRecordConfig(recCfg recordconfig) string { + var builder strings.Builder + builder.WriteString("当前语音记录配置:\n") + builder.WriteString(fmt.Sprintf("• 语音模型名称:%s\n", recCfg.ModelName)) + builder.WriteString(fmt.Sprintf("• 语音模型ID:%s\n", recCfg.ModelID)) + builder.WriteString(fmt.Sprintf("• 自定义群ID:%d\n", recCfg.Customgid)) + return builder.String() +} From 648a715c43a53a59b2d49c775ea8245f61bb8709 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=B0=8F=E9=94=85=E9=A5=AD?= <1156544355@qq.com> Date: Fri, 4 Jul 2025 17:48:22 +0800 Subject: [PATCH 2/4] =?UTF-8?q?=E2=9C=A8=20=E6=8A=8A=E9=85=8D=E7=BD=AE?= =?UTF-8?q?=E6=9A=B4=E9=9C=B2=E5=87=BA=E5=8E=BB?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- airecord/record.go | 7 ++++++- airecord/recordcfg.go | 4 ++-- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/airecord/record.go b/airecord/record.go index 928c3d9..cafbe2e 100644 --- a/airecord/record.go +++ b/airecord/record.go @@ -123,8 +123,13 @@ func init() { } } }) - en.OnFullMatch("查看AI语音配置", ensureRecordConfig, zero.SuperUserPermission).SetBlock(true). + en.OnFullMatch("查看AI语音配置", ensureRecordConfig).SetBlock(true). Handle(func(ctx *zero.Ctx) { ctx.SendChain(message.Text(printRecordConfig(recordcfg))) }) + en.OnPrefix("发送AI语音", ensureRecordConfig).SetBlock(true). + Handle(func(ctx *zero.Ctx) { + u := strings.TrimSpace(ctx.State["args"].(string)) + ctx.SendChain(message.Text(u)) + }) } diff --git a/airecord/recordcfg.go b/airecord/recordcfg.go index 32ed8b1..0fdf055 100644 --- a/airecord/recordcfg.go +++ b/airecord/recordcfg.go @@ -25,8 +25,8 @@ func newrecordconfig() recordconfig { return recordconfig{} } -// getRecordConfig 返回当前语音记录配置信息 -func getRecordConfig() recordconfig { +// GetRecordConfig 返回当前语音记录配置信息 +func GetRecordConfig() recordconfig { return recordcfg } From 4c1506f375af6f9997f0652fadd543c584e195ba Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=B0=8F=E9=94=85=E9=A5=AD?= <1156544355@qq.com> Date: Fri, 4 Jul 2025 17:55:36 +0800 Subject: [PATCH 3/4] =?UTF-8?q?=F0=9F=90=9B=20=E4=BF=AE=E6=94=B9=E5=8C=85?= =?UTF-8?q?=E5=90=8D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- airecord/record.go | 4 ++-- driver/funcall.go | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/airecord/record.go b/airecord/record.go index cafbe2e..fe25658 100644 --- a/airecord/record.go +++ b/airecord/record.go @@ -1,5 +1,5 @@ -// Package aichat OpenAI聊天 -package aichat +// Package airecord 群应用:AI声聊 +package airecord import ( "strconv" diff --git a/driver/funcall.go b/driver/funcall.go index 2043f05..0b9454e 100644 --- a/driver/funcall.go +++ b/driver/funcall.go @@ -162,7 +162,7 @@ func (f *FCClient) handleRequest(req *zero.APIRequest) (r *zero.APIResponse, err r = &zero.APIResponse{ // 发送api调用响应 Status: s, Data: data.Get("data"), - Msg: m, + Message: m, Wording: w, RetCode: c, Echo: req.Echo, From 524db9e01ce7fa21dbcc11c8faa0fdce83a84a7f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=B0=8F=E9=94=85=E9=A5=AD?= <1156544355@qq.com> Date: Fri, 4 Jul 2025 21:42:40 +0800 Subject: [PATCH 4/4] =?UTF-8?q?=E2=9C=A8=20=E6=B7=BB=E5=8A=A0ai=E5=A3=B0?= =?UTF-8?q?=E8=81=8A?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- airecord/record.go | 44 +++++++++++---------------- airecord/recordcfg.go | 71 ++++++++++++++++++++++++------------------- 2 files changed, 57 insertions(+), 58 deletions(-) diff --git a/airecord/record.go b/airecord/record.go index fe25658..ac841d6 100644 --- a/airecord/record.go +++ b/airecord/record.go @@ -6,6 +6,7 @@ import ( "strings" "time" + "github.com/sirupsen/logrus" "github.com/tidwall/gjson" zero "github.com/wdvxdr1123/ZeroBot" @@ -16,17 +17,23 @@ import ( ) func init() { + if err := loadConfig(); err != nil { + logrus.Warnln("WARN: 加载配置文件失败,使用默认配置:", err) + } else { + logrus.Infoln("成功从文件加载语音记录配置") + } en := control.AutoRegister(&ctrl.Options[*zero.Ctx]{ DisableOnDefault: false, Extra: control.ExtraFromString("airecord"), Brief: "群应用:AI声聊", - Help: "- 设置AI语音群号1048452984 (tips:群里必须有AI声聊应用)\n" + + Help: "- 设置AI语音群号1048452984(tips:机器人任意所在群聊即可)\n" + "- 设置AI语音模型\n" + "- 查看AI语音配置\n" + "- 发送AI语音xxx", PrivateDataFolder: "airecord", }) - en.OnPrefix("设置AI语音群号", ensureRecordConfig, zero.SuperUserPermission).SetBlock(true). + + en.OnPrefix("设置AI语音群号", zero.SuperUserPermission).SetBlock(true). Handle(func(ctx *zero.Ctx) { u := strings.TrimSpace(ctx.State["args"].(string)) num, err := strconv.ParseInt(u, 10, 64) @@ -34,29 +41,14 @@ func init() { ctx.SendChain(message.Text("ERROR: parse gid err: ", err)) return } - c, ok := ctx.State["manager"].(*ctrl.Control[*zero.Ctx]) - if !ok { - ctx.SendChain(message.Text("ERROR: no such plugin")) - return - } - err = c.SetExtra(&recordcfg) - if err != nil { - ctx.SendChain(message.Text("ERROR: set extra err: ", err)) - return - } setCustomGID(num) ctx.SendChain(message.Text("设置AI语音群号为", num)) }) - en.OnFullMatch("设置AI语音模型", ensureRecordConfig, zero.SuperUserPermission).SetBlock(true). + en.OnFullMatch("设置AI语音模型", zero.SuperUserPermission).SetBlock(true). Handle(func(ctx *zero.Ctx) { next := zero.NewFutureEvent("message", 999, false, ctx.CheckSession()) recv, cancel := next.Repeat() defer cancel() - c, ok := ctx.State["manager"].(*ctrl.Control[*zero.Ctx]) - if !ok { - ctx.SendChain(message.Text("ERROR: no such plugin")) - return - } jsonData := ctx.GetAICharacters(0, 1) // 转换为字符串数组 @@ -112,24 +104,24 @@ func init() { continue } setRecordModel(names[num], nameToID[names[num]]) - err = c.SetExtra(&recordcfg) - if err != nil { - ctx.SendChain(message.Text("ERROR: set extra err: ", err)) - return - } ctx.SendChain(message.Text("已选择语音模型: ", names[num])) ctx.SendChain(message.Record(nameToURL[names[num]])) return } } }) - en.OnFullMatch("查看AI语音配置", ensureRecordConfig).SetBlock(true). + en.OnFullMatch("查看AI语音配置").SetBlock(true). Handle(func(ctx *zero.Ctx) { ctx.SendChain(message.Text(printRecordConfig(recordcfg))) }) - en.OnPrefix("发送AI语音", ensureRecordConfig).SetBlock(true). + en.OnPrefix("发送AI语音").SetBlock(true). Handle(func(ctx *zero.Ctx) { u := strings.TrimSpace(ctx.State["args"].(string)) - ctx.SendChain(message.Text(u)) + record := ctx.GetAIRecord(recordcfg.ModelID, recordcfg.Customgid, u).String() + if record == "" { + ctx.SendChain(message.Text("ERROR: get record err: empty record")) + return + } + ctx.SendChain(message.Record(record)) }) } diff --git a/airecord/recordcfg.go b/airecord/recordcfg.go index 0fdf055..247d241 100644 --- a/airecord/recordcfg.go +++ b/airecord/recordcfg.go @@ -1,28 +1,24 @@ -package aichat +package airecord import ( + "encoding/json" "fmt" + "os" "strings" - ctrl "github.com/FloatTech/zbpctrl" "github.com/sirupsen/logrus" - zero "github.com/wdvxdr1123/ZeroBot" ) var ( - recordcfg = newrecordconfig() + recordcfg recordconfig + configPath = "data/airecord/recordconfig.json" // 配置文件路径 ) // recordconfig 存储语音记录相关配置 type recordconfig struct { - ModelName string // 语音模型名称 - ModelID string // 语音模型ID - Customgid int64 // 自定义群ID -} - -// newrecordconfig 创建并返回默认语音记录配置 -func newrecordconfig() recordconfig { - return recordconfig{} + ModelName string `json:"modelName"` // 语音模型名称 + ModelID string `json:"modelID"` // 语音模型ID + Customgid int64 `json:"customgid"` // 自定义群ID } // GetRecordConfig 返回当前语音记录配置信息 @@ -34,31 +30,13 @@ func GetRecordConfig() recordconfig { func setRecordModel(modelName, modelID string) { recordcfg.ModelName = modelName recordcfg.ModelID = modelID + saveConfig() // 保存配置 } // setCustomGID 设置自定义群ID func setCustomGID(gid int64) { recordcfg.Customgid = gid -} - -// isvalid 检查语音记录配置是否有效 -func (c *recordconfig) isvalid() bool { - return c.ModelName != "" && c.ModelID != "" && c.Customgid != 0 -} - -// ensureRecordConfig 确保语音记录配置存在 -func ensureRecordConfig(ctx *zero.Ctx) bool { - c, ok := ctx.State["manager"].(*ctrl.Control[*zero.Ctx]) - if !ok { - return false - } - if !recordcfg.isvalid() { - err := c.GetExtra(&recordcfg) - if err != nil { - logrus.Warnln("ERROR: get extra err:", err) - } - } - return true + saveConfig() // 保存配置 } // printRecordConfig 生成格式化的语音记录配置信息字符串 @@ -70,3 +48,32 @@ func printRecordConfig(recCfg recordconfig) string { builder.WriteString(fmt.Sprintf("• 自定义群ID:%d\n", recCfg.Customgid)) return builder.String() } + +// saveConfig 将配置保存到JSON文件 +func saveConfig() error { + data, err := json.MarshalIndent(recordcfg, "", " ") + if err != nil { + logrus.Warnln("ERROR: 序列化配置失败:", err) + return err + } + err = os.WriteFile(configPath, data, 0644) + if err != nil { + logrus.Warnln("ERROR: 写入配置文件失败:", err) + return err + } + return nil +} + +// loadConfig 从JSON文件加载配置 +func loadConfig() error { + data, err := os.ReadFile(configPath) + if err != nil { + return err + } + err = json.Unmarshal(data, &recordcfg) + if err != nil { + logrus.Warnln("ERROR: 解析配置文件失败:", err) + return err + } + return nil +}