From 3234c18718e1c7fcd35efa30f680835eacce06b0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=BA=90=E6=96=87=E9=9B=A8?= <41315874+fumiama@users.noreply.github.com> Date: Wed, 21 Sep 2022 11:49:30 +0800 Subject: [PATCH] fix tts --- go.mod | 2 +- go.sum | 4 +- plugin/ai_reply/ai_tts.go | 239 ++++++++------------------------------ plugin/ai_reply/main.go | 100 +++++++++++++++- 4 files changed, 148 insertions(+), 197 deletions(-) diff --git a/go.mod b/go.mod index 536708c0..03cd39cc 100644 --- a/go.mod +++ b/go.mod @@ -9,7 +9,7 @@ require ( github.com/FloatTech/sqlite v0.4.0 github.com/FloatTech/ttl v0.0.0-20220715042055-15612be72f5b github.com/FloatTech/zbpctrl v1.5.2-0.20220921013146-40d64bc7799c - github.com/FloatTech/zbputils v1.5.1-0.20220921020212-92dd4623ff83 + github.com/FloatTech/zbputils v1.5.1-0.20220921030811-5454880e2456 github.com/RomiChan/syncx v0.0.0-20220404072119-d7ea0ae15a4c github.com/antchfx/htmlquery v1.2.5 github.com/corona10/goimagehash v1.1.0 diff --git a/go.sum b/go.sum index 0fcaa712..a31b30af 100644 --- a/go.sum +++ b/go.sum @@ -12,8 +12,8 @@ github.com/FloatTech/ttl v0.0.0-20220715042055-15612be72f5b h1:tvciXWq2nuvTbFeJG github.com/FloatTech/ttl v0.0.0-20220715042055-15612be72f5b/go.mod h1:fHZFWGquNXuHttu9dUYoKuNbm3dzLETnIOnm1muSfDs= github.com/FloatTech/zbpctrl v1.5.2-0.20220921013146-40d64bc7799c h1:zYcKvMbwQeifSQg0LnxhmCdyP3mb7ZqS6rjt68jbukw= github.com/FloatTech/zbpctrl v1.5.2-0.20220921013146-40d64bc7799c/go.mod h1:+2mGs9vUWJsvfcbodcmFegqOKEqHnLwopF1jTLVs/gU= -github.com/FloatTech/zbputils v1.5.1-0.20220921020212-92dd4623ff83 h1:r5lc9Gm9oJv5P90ifIGeiWG9PFH7uYh/O3gSmqqJIBU= -github.com/FloatTech/zbputils v1.5.1-0.20220921020212-92dd4623ff83/go.mod h1:C+PfyuEOxooTDb1S/SoptP3K+pTCXbUvC/AxXb3Q2yk= +github.com/FloatTech/zbputils v1.5.1-0.20220921030811-5454880e2456 h1:P18vYFcT/5+E/QmCs/e+TjYX/L5uPG/+FdRuo5eXlKQ= +github.com/FloatTech/zbputils v1.5.1-0.20220921030811-5454880e2456/go.mod h1:C+PfyuEOxooTDb1S/SoptP3K+pTCXbUvC/AxXb3Q2yk= github.com/PuerkitoBio/goquery v1.5.1/go.mod h1:GsLWisAFVj4WgDibEWF4pvYnkVQBpKBKeU+7zCJoLcc= github.com/RomiChan/syncx v0.0.0-20220404072119-d7ea0ae15a4c h1:cNPOdTNiVwxLpROLjXCgbIPvdkE+BwvxDvgmdYmWx6Q= github.com/RomiChan/syncx v0.0.0-20220404072119-d7ea0ae15a4c/go.mod h1:KqZzu7slNKROh3TSYEH/IUMG6f4M+1qubZ5e52QypsE= diff --git a/plugin/ai_reply/ai_tts.go b/plugin/ai_reply/ai_tts.go index 44f39bc3..02fa49c5 100644 --- a/plugin/ai_reply/ai_tts.go +++ b/plugin/ai_reply/ai_tts.go @@ -2,28 +2,18 @@ package aireply import ( "errors" - "fmt" - "net/url" "regexp" - "strconv" "sync" - "time" - "github.com/pkumza/numcn" - log "github.com/sirupsen/logrus" zero "github.com/wdvxdr1123/ZeroBot" - "github.com/wdvxdr1123/ZeroBot/message" - "github.com/FloatTech/AnimeAPI/aireply" ctrl "github.com/FloatTech/zbpctrl" "github.com/FloatTech/zbputils/control" - "github.com/FloatTech/zbputils/ctxext" ) const ( - ttsServiceName = "tts" - cnapi = "http://233366.proxy.nscc-gz.cn:8888?speaker=%s&text=%s" - // testString = "这是测试语言......" + cnapi = "http://233366.proxy.nscc-gz.cn:8888?speaker=%s&text=%s" + // testString = "这是测试语音......" ) // 每个角色的测试文案 @@ -96,10 +86,9 @@ var ( } ) -type ttsInstances struct { +type ttsmode struct { sync.RWMutex - defaultSoundMode string - soundMode []string + mode map[int64]int64 } func list(list []string, num int) string { @@ -115,200 +104,66 @@ func list(list []string, num int) string { return s } -func init() { - tts := &ttsInstances{ - defaultSoundMode: "派蒙", - soundMode: soundList[:], +func newttsmode() *ttsmode { + tts := &ttsmode{} + m, ok := control.Lookup(ttsServiceName) + if ok { + tts.mode[-2905] = m.GetData(-2905) + } + return tts +} + +func (tts *ttsmode) setSoundMode(ctx *zero.Ctx, name string) error { + gid := ctx.Event.GroupID + if gid == 0 { + gid = -ctx.Event.UserID + } + var i int + var s string + for i, s = range soundList { + if s == name { + break + } + } + m := ctx.State["manager"].(*ctrl.Control[*zero.Ctx]) + tts.Lock() + defer tts.Unlock() + tts.mode[gid] = int64(i) + return m.SetData(gid, int64(i)) +} + +func (tts *ttsmode) getSoundMode(ctx *zero.Ctx) (name string) { + gid := ctx.Event.GroupID + if gid == 0 { + gid = -ctx.Event.UserID } - engine := control.Register(ttsServiceName, &ctrl.Options[*zero.Ctx]{ - DisableOnDefault: true, - Help: "语音回复(大家一起来炼丹)\n" + - "- @Bot 任意文本(任意一句话回复)\n" + - "- 设置语音模式[原神人物]\n" + - "- 设置默认语音模式[原神人物]\n" + - "- 恢复成默认语音模式\n" + - "当前适用的原神人物含有以下:\n" + list(soundList[:], 5), - }) tts.RLock() defer tts.RUnlock() - m, ok := control.Lookup(ttsServiceName) - if ok { - tts.defaultSoundMode = soundList[m.GetData(-2905)] - } - engine.OnMessage(zero.OnlyToMe).SetBlock(true).Limit(ctxext.LimitByUser). - Handle(func(ctx *zero.Ctx) { - msg := ctx.ExtractPlainText() - // 获取回复模式 - r := aireply.NewAIReply(getReplyMode(ctx)) - // 获取回复的文本 - reply := r.TalkPlain(msg, zero.BotConfig.NickName[0]) - // 获取角色 - name := tts.getSoundMode(ctx) - if _, ok := testRecord[name]; !ok { - ctx.SendChain(message.Text("配置的语言人物数据丢失!请重新设置语言人物。")) - return - } - // 获取语言 - record := message.Record(fmt.Sprintf(cnapi, url.QueryEscape(name), url.QueryEscape( - // 将数字转文字 - re.ReplaceAllStringFunc(reply, func(s string) string { - f, err := strconv.ParseFloat(s, 64) - if err != nil { - log.Errorln("[tts]:", err) - return s - } - return numcn.EncodeFromFloat64(f) - }), - ))).Add("cache", 0) - if record.Data == nil { - ctx.SendChain(message.Reply(ctx.Event.MessageID), message.Text(reply)) - return - } - // 发送语音 - if ID := ctx.SendChain(record); ID.ID() == 0 { - ctx.SendChain(message.Reply(ctx.Event.MessageID), message.Text(reply)) - } - }) - engine.OnRegex(`^设置语音模式(.*)$`, zero.AdminPermission, func(ctx *zero.Ctx) bool { - param := ctx.State["regex_matched"].([]string)[1] - if _, ok := testRecord[param]; !ok { - return false - } - return true - }).SetBlock(true).Handle(func(ctx *zero.Ctx) { - param := ctx.State["regex_matched"].([]string)[1] - // 保存设置 - err := tts.setSoundMode(ctx, param) - if err != nil { - ctx.SendChain(message.Reply(ctx.Event.MessageID), message.Text(err)) - return - } - // 设置验证 - name := tts.getSoundMode(ctx) - if _, ok := testRecord[name]; !ok { - ctx.SendChain(message.Text("配置的语言人物数据丢失!请重新设置语言人物。")) - return - } - record := message.Record(fmt.Sprintf(cnapi, url.QueryEscape(name), url.QueryEscape(testRecord[name]))).Add("cache", 0) - if ID := ctx.SendChain(record); ID.ID() == 0 { - ctx.SendChain(message.Reply(ctx.Event.MessageID), message.Text("设置失败!无法发送测试语言,请重试。")) - return - } - time.Sleep(time.Second * 2) - ctx.SendChain(message.Reply(ctx.Event.MessageID), message.Text("设置成功,默认模式为", name)) - }) - engine.OnRegex(`^设置默认语音模式(.*)$`, zero.SuperUserPermission, func(ctx *zero.Ctx) bool { - param := ctx.State["regex_matched"].([]string)[1] - if _, ok := testRecord[param]; !ok { - return false - } - return true - }).SetBlock(true).Handle(func(ctx *zero.Ctx) { - param := ctx.State["regex_matched"].([]string)[1] - // 保存设置 - err := tts.setDefaultSoundMode(param) - if err != nil { - ctx.SendChain(message.Reply(ctx.Event.MessageID), message.Text(err)) - return - } - // 设置验证 - name := tts.defaultSoundMode - record := message.Record(fmt.Sprintf(cnapi, url.QueryEscape(name), url.QueryEscape(testRecord[name]))).Add("cache", 0) - if ID := ctx.SendChain(record); ID.ID() == 0 { - ctx.SendChain(message.Reply(ctx.Event.MessageID), message.Text("设置失败!无法发送测试语言,请重试。")) - return - } - time.Sleep(time.Second * 2) - ctx.SendChain(message.Reply(ctx.Event.MessageID), message.Text("设置成功,默认模式为", name)) - }) - engine.OnFullMatch("恢复成默认语音模式", zero.AdminPermission, func(ctx *zero.Ctx) bool { - gid := ctx.Event.GroupID - if gid == 0 { - gid = -ctx.Event.UserID - } - m, ok := ctx.State["manager"].(*ctrl.Control[*zero.Ctx]) - if ok { - tts.RLock() - defer tts.RUnlock() - index := m.GetData(gid) - if int(index) < len(tts.soundMode) { - return true - } - } - return false - }).SetBlock(true).Handle(func(ctx *zero.Ctx) { - gid := ctx.Event.GroupID - if gid == 0 { - gid = -ctx.Event.UserID - } - m := ctx.State["manager"].(*ctrl.Control[*zero.Ctx]) - err := m.SetData(gid, 255) - if err != nil { - ctx.SendChain(message.Reply(ctx.Event.MessageID), message.Text(err)) - return - } - // 设置验证 - name := tts.getSoundMode(ctx) - ctx.SendChain(message.Reply(ctx.Event.MessageID), message.Text("设置成功,当前默认语音模式为", name)) - }) + return soundList[tts.mode[gid]] } -func (tts *ttsInstances) setSoundMode(ctx *zero.Ctx, name string) error { +func (tts *ttsmode) resetSoundMode(ctx *zero.Ctx) { gid := ctx.Event.GroupID if gid == 0 { gid = -ctx.Event.UserID } - var index int64 - tts.RLock() - for i, s := range tts.soundMode { - if s == name { - index = int64(i) - break - } - } - tts.RUnlock() + tts.Lock() + defer tts.Unlock() m := ctx.State["manager"].(*ctrl.Control[*zero.Ctx]) - return m.SetData(gid, index) + tts.mode[gid] = m.GetData(-2905) } -func (tts *ttsInstances) getSoundMode(ctx *zero.Ctx) (name string) { - gid := ctx.Event.GroupID - if gid == 0 { - gid = -ctx.Event.UserID - } - m, ok := ctx.State["manager"].(*ctrl.Control[*zero.Ctx]) - if ok { - tts.RLock() - defer tts.RUnlock() - index := m.GetData(gid) - if int(index) < len(tts.soundMode) { - return tts.soundMode[index] - } - } - return tts.defaultSoundMode -} - -func (tts *ttsInstances) setDefaultSoundMode(name string) error { - var index int64 - tts.RLock() - for i, s := range tts.soundMode { +func setDefaultSoundMode(name string) error { + var i int + var s string + for i, s = range soundList { if s == name { - index = int64(i) break } } - tts.RUnlock() m, ok := control.Lookup(ttsServiceName) if !ok { - return errors.New("[tts]service no fund ") + return errors.New("[tts] service not found") } - err := m.SetData(-2905, index) - if err == nil { - soundMode := tts.soundMode[m.GetData(-2905)] - if soundMode != tts.soundMode[index] { - return errors.New("[tts]检验数据失败,当前写入的数据为" + soundMode) - } - tts.defaultSoundMode = soundMode - } - return err + return m.SetData(-2905, int64(i)) } diff --git a/plugin/ai_reply/main.go b/plugin/ai_reply/main.go index 59aabdef..01172f7c 100644 --- a/plugin/ai_reply/main.go +++ b/plugin/ai_reply/main.go @@ -3,24 +3,120 @@ package aireply import ( "errors" + "fmt" + "net/url" + "strconv" "time" "github.com/FloatTech/AnimeAPI/aireply" ctrl "github.com/FloatTech/zbpctrl" "github.com/FloatTech/zbputils/control" "github.com/FloatTech/zbputils/ctxext" + "github.com/pkumza/numcn" + log "github.com/sirupsen/logrus" zero "github.com/wdvxdr1123/ZeroBot" "github.com/wdvxdr1123/ZeroBot/message" ) const ( replyServiceName = "aireply" + ttsServiceName = "tts" ) var replyModes = [...]string{"青云客", "小爱"} func init() { // 插件主体 - engine := control.Register(replyServiceName, &ctrl.Options[*zero.Ctx]{ + engine := control.Register(ttsServiceName, &ctrl.Options[*zero.Ctx]{ + DisableOnDefault: true, + Help: "语音回复(大家一起来炼丹)\n" + + "- @Bot 任意文本(任意一句话回复)\n" + + "- 设置语音模式[原神人物]\n" + + "- 设置默认语音模式[原神人物]\n" + + "- 恢复成默认语音模式\n" + + "当前适用的原神人物含有以下:\n" + list(soundList[:], 5), + }) + tts := newttsmode() + engine.OnMessage(zero.OnlyToMe).SetBlock(true).Limit(ctxext.LimitByUser). + Handle(func(ctx *zero.Ctx) { + msg := ctx.ExtractPlainText() + // 获取回复模式 + r := aireply.NewAIReply(getReplyMode(ctx)) + // 获取回复的文本 + reply := r.TalkPlain(msg, zero.BotConfig.NickName[0]) + // 获取角色 + name := tts.getSoundMode(ctx) + // 获取语音 + record := message.Record(fmt.Sprintf(cnapi, url.QueryEscape(name), url.QueryEscape( + // 将数字转文字 + re.ReplaceAllStringFunc(reply, func(s string) string { + f, err := strconv.ParseFloat(s, 64) + if err != nil { + log.Errorln("[tts]:", err) + return s + } + return numcn.EncodeFromFloat64(f) + }), + ))).Add("cache", 0) + if record.Data == nil { + ctx.SendChain(message.Reply(ctx.Event.MessageID), message.Text(reply)) + return + } + // 发送语音 + if ID := ctx.SendChain(record); ID.ID() == 0 { + ctx.SendChain(message.Reply(ctx.Event.MessageID), message.Text(reply)) + } + }) + engine.OnRegex(`^设置语音模式(.*)$`, zero.AdminPermission, func(ctx *zero.Ctx) bool { + param := ctx.State["regex_matched"].([]string)[1] + if _, ok := testRecord[param]; !ok { + return false + } + return true + }).SetBlock(true).Handle(func(ctx *zero.Ctx) { + param := ctx.State["regex_matched"].([]string)[1] + // 保存设置 + err := tts.setSoundMode(ctx, param) + if err != nil { + ctx.SendChain(message.Reply(ctx.Event.MessageID), message.Text(err)) + return + } + // 设置验证 + name := tts.getSoundMode(ctx) + if _, ok := testRecord[name]; !ok { + ctx.SendChain(message.Text("配置的语音人物数据丢失!请重新设置语音人物。")) + return + } + record := message.Record(fmt.Sprintf(cnapi, url.QueryEscape(name), url.QueryEscape(testRecord[name]))).Add("cache", 0) + if ID := ctx.SendChain(record); ID.ID() == 0 { + ctx.SendChain(message.Reply(ctx.Event.MessageID), message.Text("设置失败!无法发送测试语音,请重试。")) + return + } + time.Sleep(time.Second * 2) + ctx.SendChain(message.Reply(ctx.Event.MessageID), message.Text("设置成功")) + }) + engine.OnRegex(`^设置默认语音模式(.*)$`, zero.SuperUserPermission, func(ctx *zero.Ctx) bool { + param := ctx.State["regex_matched"].([]string)[1] + if _, ok := testRecord[param]; !ok { + return false + } + return true + }).SetBlock(true).Handle(func(ctx *zero.Ctx) { + param := ctx.State["regex_matched"].([]string)[1] + // 保存设置 + err := setDefaultSoundMode(param) + if err != nil { + ctx.SendChain(message.Reply(ctx.Event.MessageID), message.Text(err)) + return + } + ctx.SendChain(message.Reply(ctx.Event.MessageID), message.Text("设置成功")) + }) + engine.OnFullMatch("恢复成默认语音模式", zero.AdminPermission).SetBlock(true).Handle(func(ctx *zero.Ctx) { + tts.resetSoundMode(ctx) + // 设置验证 + name := tts.getSoundMode(ctx) + ctx.SendChain(message.Reply(ctx.Event.MessageID), message.Text("设置成功,当前为", name)) + }) + engine = control.Register(replyServiceName, &ctrl.Options[*zero.Ctx]{ DisableOnDefault: false, Help: "人工智能回复\n" + "- @Bot 任意文本(任意一句话回复)\n- 设置回复模式[青云客|小爱]", @@ -39,7 +135,7 @@ func init() { // 插件主体 } ctx.Send(reply) }) - engine.OnPrefix(`设置回复模式`).SetBlock(true). + engine.OnPrefix("设置回复模式").SetBlock(true). Handle(func(ctx *zero.Ctx) { param := ctx.State["args"].(string) err := setReplyMode(ctx, param)