|
1 | 1 | package aireply
|
2 | 2 |
|
3 | 3 | import (
|
| 4 | + "fmt" |
| 5 | + "net/url" |
4 | 6 | "regexp"
|
5 | 7 | "strconv"
|
6 | 8 | "sync"
|
| 9 | + "time" |
7 | 10 |
|
8 | 11 | "github.com/pkumza/numcn"
|
9 | 12 | log "github.com/sirupsen/logrus"
|
10 | 13 | zero "github.com/wdvxdr1123/ZeroBot"
|
11 | 14 | "github.com/wdvxdr1123/ZeroBot/message"
|
12 | 15 |
|
13 | 16 | "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" |
18 | 17 | ctrl "github.com/FloatTech/zbpctrl"
|
19 | 18 | "github.com/FloatTech/zbputils/control"
|
20 | 19 | "github.com/FloatTech/zbputils/ctxext"
|
21 | 20 | )
|
22 | 21 |
|
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 | +} |
24 | 81 |
|
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 | +) |
26 | 97 |
|
27 | 98 | type ttsInstances struct {
|
28 | 99 | sync.RWMutex
|
29 |
| - m map[string]tts.TTS |
30 |
| - l []string |
| 100 | + defaultSoundMode string |
| 101 | + soundMode []string |
31 | 102 | }
|
32 | 103 |
|
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 |
39 | 115 | }
|
40 | 116 |
|
41 | 117 | 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[:], |
53 | 121 | }
|
54 | 122 | engine := control.Register(ttsServiceName, &ctrl.Options[*zero.Ctx]{
|
55 | 123 | DisableOnDefault: true,
|
56 |
| - Help: "语音回复(包括拟声鸟和百度)\n" + |
| 124 | + Help: "语音回复(大家一起来炼丹)\n" + |
57 | 125 | "- @Bot 任意文本(任意一句话回复)\n" +
|
58 |
| - "- 设置语音模式[拟声鸟阿梓 | 拟声鸟文静 | 拟声鸟药水哥 | 百度女声 | 百度男声| 百度度逍遥 | 百度度丫丫]\n" + |
59 |
| - "- 设置默认语音模式[拟声鸟阿梓 | 拟声鸟文静 | 拟声鸟药水哥 | 百度女声 | 百度男声| 百度度逍遥 | 百度度丫丫]\n", |
| 126 | + "- 设置语音模式[原神人物]\n" + |
| 127 | + "- 设置默认语音模式[原神人物]\n" + |
| 128 | + "当前适用的原神人物含有以下:\n" + list(soundList[:], 5), |
60 | 129 | })
|
61 | 130 | engine.OnMessage(zero.OnlyToMe).SetBlock(true).Limit(ctxext.LimitByUser).
|
62 | 131 | Handle(func(ctx *zero.Ctx) {
|
63 | 132 | msg := ctx.ExtractPlainText()
|
| 133 | + // 获取回复模式 |
64 | 134 | 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("配置的语言人物数据丢失!请重新设置语言人物。")) |
68 | 141 | return
|
69 | 142 | }
|
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)) |
90 | 157 | }
|
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)) |
99 | 161 | }
|
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)) |
107 | 162 | })
|
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 |
129 | 167 | }
|
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 | + }) |
132 | 211 | }
|
133 | 212 |
|
134 |
| -func (t *ttsInstances) setSoundMode(ctx *zero.Ctx, name string) error { |
| 213 | +func (tts *ttsInstances) setSoundMode(ctx *zero.Ctx, name string) error { |
135 | 214 | gid := ctx.Event.GroupID
|
136 | 215 | if gid == 0 {
|
137 | 216 | gid = -ctx.Event.UserID
|
138 | 217 | }
|
139 | 218 | var index int64
|
140 |
| - t.RLock() |
141 |
| - for i, s := range t.l { |
| 219 | + tts.RLock() |
| 220 | + for i, s := range tts.soundMode { |
142 | 221 | if s == name {
|
143 | 222 | index = int64(i)
|
144 | 223 | break
|
145 | 224 | }
|
146 | 225 | }
|
147 |
| - t.RUnlock() |
| 226 | + tts.RUnlock() |
148 | 227 | m := ctx.State["manager"].(*ctrl.Control[*zero.Ctx])
|
149 | 228 | return m.SetData(gid, index)
|
150 | 229 | }
|
151 | 230 |
|
152 |
| -func (t *ttsInstances) getSoundMode(ctx *zero.Ctx) (name string) { |
| 231 | +func (tts *ttsInstances) getSoundMode(ctx *zero.Ctx) (name string) { |
153 | 232 | gid := ctx.Event.GroupID
|
154 | 233 | if gid == 0 {
|
155 | 234 | gid = -ctx.Event.UserID
|
156 | 235 | }
|
157 | 236 | m, ok := ctx.State["manager"].(*ctrl.Control[*zero.Ctx])
|
158 | 237 | if ok {
|
159 |
| - t.RLock() |
160 |
| - defer t.RUnlock() |
| 238 | + tts.RLock() |
| 239 | + defer tts.RUnlock() |
161 | 240 | 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] |
164 | 243 | }
|
165 | 244 | }
|
166 |
| - return "拟声鸟阿梓" |
| 245 | + return tts.defaultSoundMode |
167 | 246 | }
|
168 | 247 |
|
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 { |
173 | 252 | if s == name {
|
| 253 | + index = int64(i) |
174 | 254 | break
|
175 | 255 | }
|
176 |
| - index++ |
177 | 256 | }
|
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() |
182 | 259 | }
|
0 commit comments