Skip to content

Commit dd970b9

Browse files
authored
更换tts的语音api至原神语音api (#382)
1 parent b997cb6 commit dd970b9

File tree

1 file changed

+185
-108
lines changed

1 file changed

+185
-108
lines changed

plugin/ai_reply/ai_tts.go

Lines changed: 185 additions & 108 deletions
Original file line numberDiff line numberDiff line change
@@ -1,182 +1,259 @@
11
package aireply
22

33
import (
4+
"fmt"
5+
"net/url"
46
"regexp"
57
"strconv"
68
"sync"
9+
"time"
710

811
"github.com/pkumza/numcn"
912
log "github.com/sirupsen/logrus"
1013
zero "github.com/wdvxdr1123/ZeroBot"
1114
"github.com/wdvxdr1123/ZeroBot/message"
1215

1316
"github.com/FloatTech/AnimeAPI/aireply"
14-
"github.com/FloatTech/AnimeAPI/tts"
15-
"github.com/FloatTech/AnimeAPI/tts/baidutts"
16-
"github.com/FloatTech/AnimeAPI/tts/mockingbird"
17-
fcext "github.com/FloatTech/floatbox/ctxext"
1817
ctrl "github.com/FloatTech/zbpctrl"
1918
"github.com/FloatTech/zbputils/control"
2019
"github.com/FloatTech/zbputils/ctxext"
2120
)
2221

23-
const ttsServiceName = "tts"
22+
const (
23+
ttsServiceName = "tts"
24+
cnapi = "http://233366.proxy.nscc-gz.cn:8888?speaker=%s&text=%s"
25+
// testString = "这是测试语言......"
26+
)
27+
28+
// 每个角色的测试文案
29+
var testRecord = map[string]string{
30+
"派蒙": "哎,又是看不懂的东西。我完全不知道这些奇怪的问题和实验,能得到什么结果…",
31+
"凯亚": "真是个急性子啊你。",
32+
"安柏": "最初的鸟儿是不会飞翔的,飞翔是它们勇敢跃入峡谷的奖励。",
33+
"丽莎": "嗨,小可爱,你是新来的助理吗?",
34+
"琴": "蒲公英骑士,琴,申请入队。",
35+
"香菱": "我是来自璃月的厨师香菱,最擅长的是做各种捞…捞,料理…哎呀,练了那么多次,还是会紧张,嘿。",
36+
"枫原万叶": "飘摇风雨中,带刀归来赤脚行。",
37+
"迪卢克": "在黎明来临之前,总要有人照亮黑暗。",
38+
"温迪": "若你困于无风之地,我将为你奏响高天之歌。",
39+
"可莉": "西风骑士团,火花骑士,可莉,前来报到!…呃—后面该说什么词来着?可莉背不下来啦...",
40+
"早柚": "终末番,早柚,参上。 呼——",
41+
"托马": "初次见面,异乡的旅人,你的名字我可是早就听说了。只要你不嫌弃,我托马,从今天起就是你的朋友了。",
42+
"芭芭拉": "芭芭拉,闪耀登场~治疗就交给我吧,不会让你失望的!",
43+
"优菈": "沉沦是很容易的一件事,但我仍想冻住这股潮流。",
44+
"云堇": "曲高未必人不识,自有知音和清词。",
45+
"钟离": "人间归离复归离,借一浮生逃浮生。",
46+
"魈": "三眼五显仙人,魈,听召,前来守护",
47+
"凝光": "就算古玩价值连城,给人的快乐,也只有刚拥有的一瞬",
48+
"雷电将军": "浮世千百年来风景依旧,人之在世却如白露与泡影。",
49+
"北斗": "不知道如何向前的话,总之先迈出第一步,后面的道路就会自然而然地展开了。",
50+
"甘雨": "这项工作,该划掉了。",
51+
"七七": "椰羊的奶,好喝!比一般的羊奶,好喝!",
52+
"刻晴": "劳逸结合是不错,但也别放松过头。",
53+
"神里绫华": "若知是梦何须醒,不比真如一相会。",
54+
"雷泽": "你是朋友。我和你一起狩猎。",
55+
"神里绫人": "此前听绫华屡次提起阁下,不料公务繁忙,直至今日才有机会相见。",
56+
"罗莎莉亚": "哪怕如今你已经走上截然不同的道路,也不要否认从前的自己,从前的每一个你都是你脚下的基石,不要害怕过去,不要畏惧与它抗衡。",
57+
"阿贝多": "用自己的双脚丈量土地,将未知变为知识。",
58+
"八重神子": "我的神明,就托付给你了。",
59+
"宵宫": "即使只是片刻的火花,也能在仰望黑夜的人心中留下久久不灭的美丽光芒。",
60+
"荒泷一斗": "更好地活下去,绝不该靠牺牲同类换取,应该是,一起更好地活着,才对。",
61+
"九条裟罗": "想要留住雪花。但在手心里,它只会融化的更快。",
62+
"夜兰": "线人来信了,嗯,看来又出现了新的变数。",
63+
"珊瑚宫心海": "成为了现任人神巫女之后,我也慢慢习惯了这样的生活,更重要的是我也因此和你相遇了,不是吗?",
64+
"五郎": "海祇岛反抗军大将,五郎,前来助阵!",
65+
"达达利亚": "许下的诺言就好好遵守,做错了事情就承担责任,这才是家人应有的样子吧。",
66+
"莫娜": "正是因为无法更改,无可违逆,只能接受,命运才会被称之为命运。",
67+
"班尼特": "只要有大家在,伤口就不会痛!",
68+
"申鹤": "不知道你是喜欢人间的灯火,还是山林的月光?",
69+
"行秋": "有时明月无人夜,独向昭潭制恶龙。",
70+
"烟绯": "律法即是约束,也是工具。",
71+
"久岐忍": "有麻烦事要处理的话,直接告诉我就好,我来摆平。",
72+
"辛焱": "马上就要演出了,你也一起来嗨吗?",
73+
"砂糖": "我是砂糖,炼金术的…研究员。",
74+
"胡桃": "阴阳有序,命运无常,死亡难以预测,却也有它的规矩。",
75+
"重云": "我名重云,家族久居璃月,世代以驱邪除魔为业。",
76+
"菲谢尔": "我即断罪之皇女,真名为菲谢尔。应命运的召唤降临在此间——哎?你也是,异世界的旅人吗…?",
77+
"诺艾尔": "我是诺艾尔,西风骑士团的女仆,从今天起会陪你一起去冒险。",
78+
"迪奥娜": "猫尾酒馆的招牌调酒师,迪奥娜,我的出场费可是很贵的。",
79+
"鹿野院平藏": "我叫鹿野院平藏,是天领奉行里破案最多最快的侦探……",
80+
}
2481

25-
var re = regexp.MustCompile(`(\-|\+)?\d+(\.\d+)?`)
82+
var (
83+
re = regexp.MustCompile(`(\-|\+)?\d+(\.\d+)?`)
84+
soundList = [...]string{
85+
"派蒙", "凯亚", "安柏", "丽莎", "琴",
86+
"香菱", "枫原万叶", "迪卢克", "温迪", "可莉",
87+
"早柚", "托马", "芭芭拉", "优菈", "云堇",
88+
"钟离", "魈", "凝光", "雷电将军", "北斗",
89+
"甘雨", "七七", "刻晴", "神里绫华", "雷泽",
90+
"神里绫人", "罗莎莉亚", "阿贝多", "八重神子", "宵宫",
91+
"荒泷一斗", "九条裟罗", "夜兰", "珊瑚宫心海", "五郎",
92+
"达达利亚", "莫娜", "班尼特", "申鹤", "行秋",
93+
"烟绯", "久岐忍", "辛焱", "砂糖", "胡桃",
94+
"重云", "菲谢尔", "诺艾尔", "迪奥娜", "鹿野院平藏",
95+
}
96+
)
2697

2798
type ttsInstances struct {
2899
sync.RWMutex
29-
m map[string]tts.TTS
30-
l []string
100+
defaultSoundMode string
101+
soundMode []string
31102
}
32103

33-
func (t *ttsInstances) List() []string {
34-
t.RLock()
35-
cl := make([]string, len(t.l))
36-
_ = copy(cl, t.l)
37-
t.RUnlock()
38-
return cl
104+
func list(list []string, num int) string {
105+
s := ""
106+
for i, value := range list {
107+
s += value
108+
if (i+1)%num == 0 {
109+
s += "\n"
110+
} else {
111+
s += " | "
112+
}
113+
}
114+
return s
39115
}
40116

41117
func init() {
42-
t := &ttsInstances{
43-
m: map[string]tts.TTS{
44-
"百度女声": baidutts.NewBaiduTTS(0),
45-
"百度男声": baidutts.NewBaiduTTS(1),
46-
"百度度逍遥": baidutts.NewBaiduTTS(3),
47-
"百度度丫丫": baidutts.NewBaiduTTS(4),
48-
"拟声鸟阿梓": nil,
49-
"拟声鸟文静": nil,
50-
"拟声鸟药水哥": nil,
51-
},
52-
l: []string{"拟声鸟阿梓", "拟声鸟文静", "拟声鸟药水哥", "百度女声", "百度男声", "百度度逍遥", "百度度丫丫"},
118+
tts := &ttsInstances{
119+
defaultSoundMode: "派蒙",
120+
soundMode: soundList[:],
53121
}
54122
engine := control.Register(ttsServiceName, &ctrl.Options[*zero.Ctx]{
55123
DisableOnDefault: true,
56-
Help: "语音回复(包括拟声鸟和百度)\n" +
124+
Help: "语音回复(大家一起来炼丹)\n" +
57125
"- @Bot 任意文本(任意一句话回复)\n" +
58-
"- 设置语音模式[拟声鸟阿梓 | 拟声鸟文静 | 拟声鸟药水哥 | 百度女声 | 百度男声| 百度度逍遥 | 百度度丫丫]\n" +
59-
"- 设置默认语音模式[拟声鸟阿梓 | 拟声鸟文静 | 拟声鸟药水哥 | 百度女声 | 百度男声| 百度度逍遥 | 百度度丫丫]\n",
126+
"- 设置语音模式[原神人物]\n" +
127+
"- 设置默认语音模式[原神人物]\n" +
128+
"当前适用的原神人物含有以下:\n" + list(soundList[:], 5),
60129
})
61130
engine.OnMessage(zero.OnlyToMe).SetBlock(true).Limit(ctxext.LimitByUser).
62131
Handle(func(ctx *zero.Ctx) {
63132
msg := ctx.ExtractPlainText()
133+
// 获取回复模式
64134
r := aireply.NewAIReply(getReplyMode(ctx))
65-
tts, err := t.new(t.getSoundMode(ctx))
66-
if err != nil {
67-
ctx.SendChain(message.Text("ERROR: ", err))
135+
// 获取回复的文本
136+
reply := r.TalkPlain(msg, zero.BotConfig.NickName[0])
137+
// 获取角色
138+
name := tts.getSoundMode(ctx)
139+
if _, ok := testRecord[name]; !ok {
140+
ctx.SendChain(message.Text("配置的语言人物数据丢失!请重新设置语言人物。"))
68141
return
69142
}
70-
var reply string
71-
if tts != nil {
72-
rec, err := tts.Speak(ctx.Event.UserID, func() string {
73-
reply = r.TalkPlain(msg, zero.BotConfig.NickName[0])
74-
reply = re.ReplaceAllStringFunc(reply, func(s string) string {
75-
f, err := strconv.ParseFloat(s, 64)
76-
if err != nil {
77-
log.Errorln("[tts]:", err)
78-
return s
79-
}
80-
return numcn.EncodeFromFloat64(f)
81-
})
82-
log.Debugln("[tts]:", reply)
83-
return reply
84-
})
85-
if err == nil {
86-
ctx.SendChain(message.Record(rec))
87-
} else {
88-
ctx.SendChain(message.Reply(ctx.Event.MessageID), message.Text(reply))
89-
}
143+
// 获取语言
144+
record := message.Record(fmt.Sprintf(cnapi, url.QueryEscape(name), url.QueryEscape(
145+
// 将数字转文字
146+
re.ReplaceAllStringFunc(reply, func(s string) string {
147+
f, err := strconv.ParseFloat(s, 64)
148+
if err != nil {
149+
log.Errorln("[tts]:", err)
150+
return s
151+
}
152+
return numcn.EncodeFromFloat64(f)
153+
}),
154+
))).Add("cache", 0)
155+
if record.Data == nil {
156+
ctx.SendChain(message.Reply(ctx.Event.MessageID), message.Text(reply))
90157
}
91-
})
92-
engine.OnRegex(`^设置语音模式(.*)$`, fcext.ValueInList(func(ctx *zero.Ctx) string { return ctx.State["regex_matched"].([]string)[1] }, t)).SetBlock(true).
93-
Handle(func(ctx *zero.Ctx) {
94-
param := ctx.State["regex_matched"].([]string)[1]
95-
err := t.setSoundMode(ctx, param)
96-
if err != nil {
97-
ctx.SendChain(message.Reply(ctx.Event.MessageID), message.Text(err))
98-
return
158+
// 发送语音
159+
if ID := ctx.SendChain(record); ID.ID() == 0 {
160+
ctx.SendChain(message.Reply(ctx.Event.MessageID), message.Text(reply))
99161
}
100-
ctx.SendChain(message.Reply(ctx.Event.MessageID), message.Text("设置成功,当前模式为", param))
101-
})
102-
engine.OnRegex(`^设置默认语音模式(.*)$`, fcext.ValueInList(func(ctx *zero.Ctx) string { return ctx.State["regex_matched"].([]string)[1] }, t)).SetBlock(true).
103-
Handle(func(ctx *zero.Ctx) {
104-
param := ctx.State["regex_matched"].([]string)[1]
105-
t.setDefaultSoundMode(param)
106-
ctx.SendChain(message.Reply(ctx.Event.MessageID), message.Text("设置成功,默认模式为", param))
107162
})
108-
}
109-
110-
// new 语音简单工厂
111-
func (t *ttsInstances) new(name string) (ts tts.TTS, err error) {
112-
t.RLock()
113-
ts = t.m[name]
114-
t.RUnlock()
115-
if ts == nil {
116-
switch name {
117-
case "拟声鸟阿梓":
118-
t.Lock()
119-
ts, err = mockingbird.NewMockingBirdTTS(0)
120-
t.Unlock()
121-
case "拟声鸟文静":
122-
t.Lock()
123-
ts, err = mockingbird.NewMockingBirdTTS(1)
124-
t.Unlock()
125-
case "拟声鸟药水哥":
126-
t.Lock()
127-
ts, err = mockingbird.NewMockingBirdTTS(2)
128-
t.Unlock()
163+
engine.OnRegex(`^设置语音模式(.*)$`, func(ctx *zero.Ctx) bool {
164+
param := ctx.State["regex_matched"].([]string)[1]
165+
if _, ok := testRecord[param]; !ok {
166+
return false
129167
}
130-
}
131-
return
168+
return true
169+
}).SetBlock(true).Handle(func(ctx *zero.Ctx) {
170+
param := ctx.State["regex_matched"].([]string)[1]
171+
// 保存设置
172+
err := tts.setSoundMode(ctx, param)
173+
if err != nil {
174+
ctx.SendChain(message.Reply(ctx.Event.MessageID), message.Text(err))
175+
return
176+
}
177+
// 设置验证
178+
name := tts.getSoundMode(ctx)
179+
if _, ok := testRecord[name]; !ok {
180+
ctx.SendChain(message.Text("配置的语言人物数据丢失!请重新设置语言人物。"))
181+
return
182+
}
183+
record := message.Record(fmt.Sprintf(cnapi, url.QueryEscape(name), url.QueryEscape(testRecord[name]))).Add("cache", 0)
184+
if ID := ctx.SendChain(record); ID.ID() == 0 {
185+
ctx.SendChain(message.Reply(ctx.Event.MessageID), message.Text("设置失败!无法发送测试语言,请重试。"))
186+
return
187+
}
188+
time.Sleep(time.Second * 2)
189+
ctx.SendChain(message.Reply(ctx.Event.MessageID), message.Text("设置成功,默认模式为", name))
190+
})
191+
engine.OnRegex(`^设置默认语音模式(.*)$`, func(ctx *zero.Ctx) bool {
192+
param := ctx.State["regex_matched"].([]string)[1]
193+
if _, ok := testRecord[param]; !ok {
194+
return false
195+
}
196+
return true
197+
}).SetBlock(true).Handle(func(ctx *zero.Ctx) {
198+
param := ctx.State["regex_matched"].([]string)[1]
199+
// 保存设置
200+
tts.setDefaultSoundMode(param)
201+
// 设置验证
202+
name := tts.defaultSoundMode
203+
record := message.Record(fmt.Sprintf(cnapi, url.QueryEscape(name), url.QueryEscape(testRecord[name]))).Add("cache", 0)
204+
if ID := ctx.SendChain(record); ID.ID() == 0 {
205+
ctx.SendChain(message.Reply(ctx.Event.MessageID), message.Text("设置失败!无法发送测试语言,请重试。"))
206+
return
207+
}
208+
time.Sleep(time.Second * 2)
209+
ctx.SendChain(message.Reply(ctx.Event.MessageID), message.Text("设置成功,默认模式为", name))
210+
})
132211
}
133212

134-
func (t *ttsInstances) setSoundMode(ctx *zero.Ctx, name string) error {
213+
func (tts *ttsInstances) setSoundMode(ctx *zero.Ctx, name string) error {
135214
gid := ctx.Event.GroupID
136215
if gid == 0 {
137216
gid = -ctx.Event.UserID
138217
}
139218
var index int64
140-
t.RLock()
141-
for i, s := range t.l {
219+
tts.RLock()
220+
for i, s := range tts.soundMode {
142221
if s == name {
143222
index = int64(i)
144223
break
145224
}
146225
}
147-
t.RUnlock()
226+
tts.RUnlock()
148227
m := ctx.State["manager"].(*ctrl.Control[*zero.Ctx])
149228
return m.SetData(gid, index)
150229
}
151230

152-
func (t *ttsInstances) getSoundMode(ctx *zero.Ctx) (name string) {
231+
func (tts *ttsInstances) getSoundMode(ctx *zero.Ctx) (name string) {
153232
gid := ctx.Event.GroupID
154233
if gid == 0 {
155234
gid = -ctx.Event.UserID
156235
}
157236
m, ok := ctx.State["manager"].(*ctrl.Control[*zero.Ctx])
158237
if ok {
159-
t.RLock()
160-
defer t.RUnlock()
238+
tts.RLock()
239+
defer tts.RUnlock()
161240
index := m.GetData(gid)
162-
if int(index) < len(t.l) {
163-
return t.l[index]
241+
if int(index) < len(tts.soundMode) {
242+
return tts.soundMode[index]
164243
}
165244
}
166-
return "拟声鸟阿梓"
245+
return tts.defaultSoundMode
167246
}
168247

169-
func (t *ttsInstances) setDefaultSoundMode(name string) {
170-
var index int
171-
t.RLock()
172-
for _, s := range t.l {
248+
func (tts *ttsInstances) setDefaultSoundMode(name string) {
249+
var index int64
250+
tts.RLock()
251+
for i, s := range tts.soundMode {
173252
if s == name {
253+
index = int64(i)
174254
break
175255
}
176-
index++
177256
}
178-
t.RUnlock()
179-
t.Lock()
180-
t.l[0], t.l[index] = t.l[index], t.l[0]
181-
t.Unlock()
257+
tts.defaultSoundMode = tts.soundMode[index]
258+
tts.RUnlock()
182259
}

0 commit comments

Comments
 (0)