From deb655de3635307e376806de368c13d0bd18510f Mon Sep 17 00:00:00 2001 From: himawari <54976075+guohuiyuan@users.noreply.github.com> Date: Sat, 5 Nov 2022 17:32:31 +0800 Subject: [PATCH] Feature huggingface (#478) --- README.md | 72 ++++++++++----- go.mod | 4 +- go.sum | 4 +- main.go | 3 + plugin/aipaint/aipaint.go | 72 +++++++++++---- plugin/aipaint/config.go | 26 +++--- plugin/magicprompt/magicprompt.go | 91 +++++++++++++++++++ plugin/realcugan/realcugan.go | 105 ++++++++++++++++++++++ plugin/vitsnyaru/vitsnyaru.go | 141 ++++++++++++++++++++++++++++++ 9 files changed, 463 insertions(+), 55 deletions(-) create mode 100644 plugin/magicprompt/magicprompt.go create mode 100644 plugin/realcugan/realcugan.go create mode 100644 plugin/vitsnyaru/vitsnyaru.go diff --git a/README.md b/README.md index 08042ebd..d950b81d 100644 --- a/README.md +++ b/README.md @@ -368,6 +368,10 @@ print("run[CQ:image,file="+j["img"]+"]") - [x] 设置ai绘图配置 [server] [token] + - [x] 设置ai绘图撤回时间90s + + - [x] 查看ai绘图配置 + 例: 设置ai绘图配置 http://91.216.169.75:5010 abc 参考服务器 http://91.217.139.190:5010, http://91.216.169.75:5010, http://185.80.202.180:5010 @@ -860,6 +864,14 @@ print("run[CQ:image,file="+j["img"]+"]") 来份萝莉 ``` + +
+ MagicPrompt-Stable-Diffusion吟唱提示 + + `import _ "github.com/FloatTech/ZeroBot-Plugin/plugin/magicprompt"` + + - [x] 吟唱提示[xxxx] +
简易midi音乐制作 @@ -1045,6 +1057,14 @@ print("run[CQ:image,file="+j["img"]+"]") - [x] 重置花名册 +
+
+ Real-CUGAN清晰术 + + `import _ "github.com/FloatTech/ZeroBot-Plugin/plugin/realcugan"` + + - [x] 清晰术(双重吟唱|三重吟唱|四重吟唱)(强力术式|中等术式|弱术式|不变式|原式)[图片] +
投胎 @@ -1142,6 +1162,14 @@ print("run[CQ:image,file="+j["img"]+"]") - [x] 解塔罗牌[牌名] - [x] [塔罗|大阿卡纳|小阿卡纳|混合]牌阵[圣三角|时间之流|四要素|五牌阵|吉普赛十字|马蹄|六芒星] +
+
+ 舔狗日记 + + `import _ "github.com/FloatTech/ZeroBot-Plugin/plugin/tiangou"` + + - [x] 舔狗日记 +
搜番 @@ -1150,20 +1178,6 @@ print("run[CQ:image,file="+j["img"]+"]") - [x] 搜番 | 搜索番剧[图片] -
-
- 猜单词 - - `import _ "github.com/FloatTech/ZeroBot-Plugin/plugin/wordle"` - - - [x] 个人猜单词 - - - [x] 团队猜单词 - - - [x] 团队六阶猜单词 - - - [x] 团队七阶猜单词 -
翻译 @@ -1172,6 +1186,14 @@ print("run[CQ:image,file="+j["img"]+"]") - [x] >TL 你好 +
+
+ vits猫雷 + + `import _ "github.com/FloatTech/ZeroBot-Plugin/plugin/vitsnyaru"` + + - [x] 让猫雷说[xxxx] +
vtb语录 @@ -1252,14 +1274,6 @@ print("run[CQ:image,file="+j["img"]+"]") - [x] 更新gal -
-
- 舔狗日记 - - `import _ "github.com/FloatTech/ZeroBot-Plugin/plugin/tiangou"` - - - [x] 舔狗日记 -
聊天热词 @@ -1268,6 +1282,20 @@ print("run[CQ:image,file="+j["img"]+"]") - [x] 热词 [群号] [消息数目]|热词 123456 1000 +
+
+ 猜单词 + + `import _ "github.com/FloatTech/ZeroBot-Plugin/plugin/wordle"` + + - [x] 个人猜单词 + + - [x] 团队猜单词 + + - [x] 团队六阶猜单词 + + - [x] 团队七阶猜单词 +
鬼东西 diff --git a/go.mod b/go.mod index 852de31e..2fb5c1df 100644 --- a/go.mod +++ b/go.mod @@ -5,13 +5,14 @@ go 1.19 require ( github.com/Baidu-AIP/golang-sdk v1.1.1 github.com/Coloured-glaze/gg v1.3.4 - github.com/FloatTech/AnimeAPI v1.5.2-0.20221101144606-ec2f4e59d0ef + github.com/FloatTech/AnimeAPI v1.5.2-0.20221105044443-0c9004b2f051 github.com/FloatTech/floatbox v0.0.0-20221029160423-446812ec82d9 github.com/FloatTech/sqlite v0.5.0 github.com/FloatTech/ttl v0.0.0-20220715042055-15612be72f5b github.com/FloatTech/zbpctrl v1.5.2 github.com/FloatTech/zbputils v1.5.1-0.20221101032142-05b2a4825567 github.com/RomiChan/syncx v0.0.0-20220404072119-d7ea0ae15a4c + github.com/RomiChan/websocket v1.4.3-0.20220123145318-307a86b127bc github.com/antchfx/htmlquery v1.2.5 github.com/corona10/goimagehash v1.1.0 github.com/fumiama/ahsai v0.1.0 @@ -38,7 +39,6 @@ require ( ) require ( - github.com/RomiChan/websocket v1.4.3-0.20220123145318-307a86b127bc // indirect github.com/antchfx/xpath v1.2.1 // indirect github.com/disintegration/imaging v1.6.2 // indirect github.com/ericpauley/go-quantize v0.0.0-20200331213906-ae555eb2afa4 // indirect diff --git a/go.sum b/go.sum index c91b427b..77c4b507 100644 --- a/go.sum +++ b/go.sum @@ -4,8 +4,8 @@ github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym github.com/Coloured-glaze/gg v1.3.4 h1:l31zIF/HaVwkzjrj+A56RGQoSKyKuR1IWtIrqXGFStI= github.com/Coloured-glaze/gg v1.3.4/go.mod h1:Ih5NLNNDHOy3RJbB0EPqGTreIzq/H02TGThIagh8HJg= github.com/DATA-DOG/go-sqlmock v1.3.3/go.mod h1:f/Ixk793poVmq4qj/V1dPUg2JEAKC73Q5eFN3EC/SaM= -github.com/FloatTech/AnimeAPI v1.5.2-0.20221101144606-ec2f4e59d0ef h1:IO4ZSaTrRTidAXqUH86dHgzk8vRlw2+YX0Oz+4n/oeg= -github.com/FloatTech/AnimeAPI v1.5.2-0.20221101144606-ec2f4e59d0ef/go.mod h1:7ZXHFEgAOFvfbgIwgyFl8G/ISnUl1KvWBzngoCpQVLg= +github.com/FloatTech/AnimeAPI v1.5.2-0.20221105044443-0c9004b2f051 h1:NEW8HzBNybMXAV0NrDpMF31n/e9BFGvNiGkDPGSkouc= +github.com/FloatTech/AnimeAPI v1.5.2-0.20221105044443-0c9004b2f051/go.mod h1:Z+Q4kIPNo/OX4RWw6WGQOQcaNsbkv/wPmPDw8p4aQIY= github.com/FloatTech/floatbox v0.0.0-20221029160423-446812ec82d9 h1:HYJ7lwaqaOKmbYooPUMWxMhXRTp+JItoyeqa320ARD4= github.com/FloatTech/floatbox v0.0.0-20221029160423-446812ec82d9/go.mod h1:w+ND28mRaJvxUJ6pRXS6i4cLzutpXsWyroutCzBdL78= github.com/FloatTech/sqlite v0.5.0 h1:U7J5Omc534PqmH6csfu+ypCo3DS8L91l5lTsxUu3b/U= diff --git a/main.go b/main.go index 0dae3b39..11bba083 100644 --- a/main.go +++ b/main.go @@ -99,6 +99,7 @@ import ( _ "github.com/FloatTech/ZeroBot-Plugin/plugin/jptingroom" // 日语听力学习材料 _ "github.com/FloatTech/ZeroBot-Plugin/plugin/juejuezi" // 绝绝子生成器 _ "github.com/FloatTech/ZeroBot-Plugin/plugin/lolicon" // lolicon 随机图片 + _ "github.com/FloatTech/ZeroBot-Plugin/plugin/magicprompt" // magicprompt吟唱提示 _ "github.com/FloatTech/ZeroBot-Plugin/plugin/midicreate" // 简易midi音乐制作 _ "github.com/FloatTech/ZeroBot-Plugin/plugin/moegoe" // 日韩 VITS 模型拟声 _ "github.com/FloatTech/ZeroBot-Plugin/plugin/moyu" // 摸鱼 @@ -112,6 +113,7 @@ import ( _ "github.com/FloatTech/ZeroBot-Plugin/plugin/nsfw" // nsfw图片识别 _ "github.com/FloatTech/ZeroBot-Plugin/plugin/omikuji" // 浅草寺求签 _ "github.com/FloatTech/ZeroBot-Plugin/plugin/qqwife" // 一群一天一夫一妻制群老婆 + _ "github.com/FloatTech/ZeroBot-Plugin/plugin/realcugan" // realcugan清晰术 _ "github.com/FloatTech/ZeroBot-Plugin/plugin/reborn" // 投胎 _ "github.com/FloatTech/ZeroBot-Plugin/plugin/runcode" // 在线运行代码 _ "github.com/FloatTech/ZeroBot-Plugin/plugin/saucenao" // 以图搜图 @@ -124,6 +126,7 @@ import ( _ "github.com/FloatTech/ZeroBot-Plugin/plugin/tiangou" // 舔狗日记 _ "github.com/FloatTech/ZeroBot-Plugin/plugin/tracemoe" // 搜番 _ "github.com/FloatTech/ZeroBot-Plugin/plugin/translation" // 翻译 + _ "github.com/FloatTech/ZeroBot-Plugin/plugin/vitsnyaru" // vits猫雷 _ "github.com/FloatTech/ZeroBot-Plugin/plugin/vtb_quotation" // vtb语录 _ "github.com/FloatTech/ZeroBot-Plugin/plugin/wangyiyun" // 网易云音乐热评 _ "github.com/FloatTech/ZeroBot-Plugin/plugin/wenxinAI" // 百度文心AI画图 diff --git a/plugin/aipaint/aipaint.go b/plugin/aipaint/aipaint.go index e1daab2f..dffa615c 100644 --- a/plugin/aipaint/aipaint.go +++ b/plugin/aipaint/aipaint.go @@ -56,6 +56,8 @@ func init() { // 插件主体 "- [ ai高级绘图 | 高级生成色图 | 高级生成涩图 | ai高级画图 ] [prompt]\n" + "- [ 以图绘图 | 以图生图 | 以图画图 ] xxx [图片]|@xxx|[qq号]\n" + "- 设置ai绘图配置 [server] [token]\n" + + "- 设置ai绘图撤回时间90s\n" + + "- 查看ai绘图配置\n" + "例: 设置ai绘图配置 http://91.217.139.190:5010 abc\n" + "参考服务器 http://91.217.139.190:5010, http://91.216.169.75:5010, http://185.80.202.180:5010\n" + "通过 http://91.217.139.190:5010/token 获取token\n" + @@ -67,23 +69,23 @@ func init() { // 插件主体 datapath = file.BOTPATH + "/" + engine.DataFolder() engine.OnPrefixGroup([]string{`ai绘图`, `生成色图`, `生成涩图`, `ai画图`}).SetBlock(true). Handle(func(ctx *zero.Ctx) { - server, token, err := cfg.load() + err := cfg.load() if err != nil { ctx.SendChain(message.Text("ERROR: ", err)) return } ctx.SendChain(message.Text("少女祈祷中...")) args := ctx.State["args"].(string) - data, err := web.GetData(server + fmt.Sprintf(aipaintTxt2ImgURL, token, url.QueryEscape(strings.TrimSpace(strings.ReplaceAll(args, " ", "%20"))))) + data, err := web.GetData(cfg.BaseURL + fmt.Sprintf(aipaintTxt2ImgURL, cfg.Token, url.QueryEscape(strings.TrimSpace(strings.ReplaceAll(args, " ", "%20"))))) if err != nil { ctx.SendChain(message.Text("ERROR: ", err)) return } - sendAiImg(ctx, data) + sendAiImg(ctx, data, cfg.Interval) }) engine.OnRegex(`^(以图绘图|以图生图|以图画图)[\s\S]*?(\[CQ:(image\,file=([0-9a-zA-Z]{32}).*|at.+?(\d{5,11}))\].*|(\d+))$`).SetBlock(true). Handle(func(ctx *zero.Ctx) { - server, token, err := cfg.load() + err := cfg.load() if err != nil { ctx.SendChain(message.Text("ERROR: ", err)) return @@ -101,7 +103,7 @@ func init() { // 插件主体 return } ctx.SendChain(message.Text("少女祈祷中...")) - postURL := server + fmt.Sprintf(aipaintImg2ImgURL, token, url.QueryEscape(strings.TrimSpace(strings.ReplaceAll(args, " ", "%20")))) + postURL := cfg.BaseURL + fmt.Sprintf(aipaintImg2ImgURL, cfg.Token, url.QueryEscape(strings.TrimSpace(strings.ReplaceAll(args, " ", "%20")))) f, err := os.Open(c.headimgsdir[0]) if err != nil { @@ -136,11 +138,11 @@ func init() { // 插件主体 ctx.SendChain(message.Text("ERROR: ", err)) return } - sendAiImg(ctx, data) + sendAiImg(ctx, data, cfg.Interval) }) engine.OnPrefixGroup([]string{`ai高级绘图`, `高级生成色图`, `高级生成涩图`, `ai高级画图`}).SetBlock(true). Handle(func(ctx *zero.Ctx) { - server, token, err := cfg.load() + err := cfg.load() if err != nil { ctx.SendChain(message.Text("ERROR: ", err)) return @@ -156,13 +158,13 @@ func init() { // 插件主体 if len(value) > 1 { if value[0] == "R18" && value[1] == "1" { value[1] = "0" - ctx.SendChain(message.Text("不准涩涩!已将R18设置为0。")) + ctx.SendChain(message.Text("不准涩涩! 已将R18设置为0. ")) } tags[value[0]] = strings.Join(value[1:], ":") } } ctx.SendChain(message.Text("少女祈祷中...")) - apiurl := "/got_image?token=" + token + apiurl := "/got_image?token=" + cfg.Token if _, ok := tags["tags"]; ok { apiurl += "&tags=" + url.QueryEscape(strings.ReplaceAll(strings.TrimSpace(tags["tags"]), " ", "%20")) } @@ -181,26 +183,63 @@ func init() { // 插件主体 if _, ok := tags["seed"]; ok { apiurl += "&seed=" + url.QueryEscape(strings.TrimSpace(tags["seed"])) } - data, err := web.GetData(server + apiurl) + data, err := web.GetData(cfg.BaseURL + apiurl) if err != nil { ctx.SendChain(message.Text("ERROR: ", err)) return } - sendAiImg(ctx, data) + sendAiImg(ctx, data, cfg.Interval) }) engine.OnRegex(`^设置ai绘图配置\s(.*[^\s$])\s(.+)$`, zero.SuperUserPermission).SetBlock(true). Handle(func(ctx *zero.Ctx) { regexMatched := ctx.State["regex_matched"].([]string) - err := cfg.save(regexMatched[1], regexMatched[2]) + err := cfg.load() if err != nil { ctx.SendChain(message.Text("ERROR: ", err)) return } - ctx.SendChain(message.Text("成功设置server为", regexMatched[1], ", token为", regexMatched[2])) + err = cfg.update(regexMatched[1], regexMatched[2], cfg.Interval) + if err != nil { + ctx.SendChain(message.Text("ERROR: ", err)) + return + } + text := fmt.Sprintf("成功设置\nbase_url: %v\ntoken: %v\ninterval: %v\n", cfg.BaseURL, cfg.Token, cfg.Interval) + ctx.SendChain(message.Text(text)) + }) + engine.OnRegex(`^设置ai绘图撤回时间(\d{1,3})s$`, zero.SuperUserPermission).SetBlock(true). + Handle(func(ctx *zero.Ctx) { + regexMatched := ctx.State["regex_matched"].([]string) + interval, err := strconv.Atoi(regexMatched[1]) + if err != nil { + ctx.SendChain(message.Text("ERROR: ", err)) + return + } + err = cfg.load() + if err != nil { + ctx.SendChain(message.Text("ERROR: ", err)) + return + } + err = cfg.update(cfg.BaseURL, cfg.Token, interval) + if err != nil { + ctx.SendChain(message.Text("ERROR: ", err)) + return + } + text := fmt.Sprintf("成功设置\nbase_url: %v\ntoken: %v\ninterval: %v\n", cfg.BaseURL, cfg.Token, cfg.Interval) + ctx.SendChain(message.Text(text)) + }) + engine.OnFullMatch(`查看ai绘图配置`, zero.SuperUserPermission).SetBlock(true). + Handle(func(ctx *zero.Ctx) { + err := cfg.load() + if err != nil { + ctx.SendChain(message.Text("ERROR: ", err)) + return + } + text := fmt.Sprintf("base_url: %v\ntoken: %v\ninterval: %v\n", cfg.BaseURL, cfg.Token, cfg.Interval) + ctx.SendChain(message.Text(text)) }) } -func sendAiImg(ctx *zero.Ctx, data []byte) { +func sendAiImg(ctx *zero.Ctx, data []byte, interval int) { var loadData string if predictRe.MatchString(binary.BytesToString(data)) { loadData = predictRe.FindStringSubmatch(binary.BytesToString(data))[0] @@ -223,11 +262,10 @@ func sendAiImg(ctx *zero.Ctx, data []byte) { m = append(m, ctxext.FakeSenderForwardNode(ctx, message.Text(r.String()))) if mid := ctx.Send(m); mid.ID() == 0 { ctx.SendChain(message.Text("ERROR: 可能被风控或下载图片用时过长,请耐心等待")) - } else { + } else if interval > 0 { go func(i message.MessageID) { - time.Sleep(90 * time.Second) + time.Sleep(time.Duration(interval) * time.Second) ctx.DeleteMessage(i) }(mid) } - } diff --git a/plugin/aipaint/config.go b/plugin/aipaint/config.go index 445a0cec..12f80967 100644 --- a/plugin/aipaint/config.go +++ b/plugin/aipaint/config.go @@ -10,9 +10,10 @@ import ( // 配置结构体 type serverConfig struct { - BaseURL string `json:"base_url"` - Token string `json:"token"` - file string + BaseURL string `json:"base_url"` + Token string `json:"token"` + Interval int `json:"interval"` + file string } func newServerConfig(file string) *serverConfig { @@ -21,9 +22,14 @@ func newServerConfig(file string) *serverConfig { } } -func (cfg *serverConfig) save(baseURL, token string) (err error) { - cfg.BaseURL = baseURL - cfg.Token = token +func (cfg *serverConfig) update(baseURL, token string, interval int) (err error) { + if baseURL != "" { + cfg.BaseURL = baseURL + } + if token != "" { + cfg.Token = token + } + cfg.Interval = interval reader, err := os.Create(cfg.file) if err != nil { return err @@ -32,10 +38,8 @@ func (cfg *serverConfig) save(baseURL, token string) (err error) { return json.NewEncoder(reader).Encode(cfg) } -func (cfg *serverConfig) load() (aipaintServer, token string, err error) { - if cfg.BaseURL != "" && cfg.Token != "" { - aipaintServer = cfg.BaseURL - token = cfg.Token +func (cfg *serverConfig) load() (err error) { + if cfg.BaseURL != "" && cfg.Token != "" && cfg.Interval != 0 { return } if file.IsNotExist(cfg.file) { @@ -48,7 +52,5 @@ func (cfg *serverConfig) load() (aipaintServer, token string, err error) { } defer reader.Close() err = json.NewDecoder(reader).Decode(cfg) - aipaintServer = cfg.BaseURL - token = cfg.Token return } diff --git a/plugin/magicprompt/magicprompt.go b/plugin/magicprompt/magicprompt.go new file mode 100644 index 00000000..1f0d4d32 --- /dev/null +++ b/plugin/magicprompt/magicprompt.go @@ -0,0 +1,91 @@ +// Package magicprompt MagicPrompt-Stable-Diffusion吟唱提示 +package magicprompt + +import ( + "context" + "encoding/json" + "fmt" + "strings" + "time" + + hf "github.com/FloatTech/AnimeAPI/huggingface" + ctrl "github.com/FloatTech/zbpctrl" + "github.com/FloatTech/zbputils/control" + "github.com/FloatTech/zbputils/ctxext" + "github.com/RomiChan/websocket" + "github.com/tidwall/gjson" + zero "github.com/wdvxdr1123/ZeroBot" + "github.com/wdvxdr1123/ZeroBot/message" +) + +const ( + magicpromptRepo = "Gustavosta/MagicPrompt-Stable-Diffusion" +) + +func init() { // 插件主体 + engine := control.Register("magicprompt", &ctrl.Options[*zero.Ctx]{ + DisableOnDefault: false, + Brief: "MagicPrompt-Stable-Diffusion吟唱提示", + Help: "- 吟唱提示 xxx", + PrivateDataFolder: "magicprompt", + }) + + // 开启 + engine.OnPrefixGroup([]string{`吟唱提示`, "吟唱补全"}).SetBlock(true). + Handle(func(ctx *zero.Ctx) { + _ctx, _cancel := context.WithTimeout(context.Background(), hf.TimeoutMax*time.Second) + defer _cancel() + ctx.SendChain(message.Text("少女祈祷中...")) + + magicpromptURL := fmt.Sprintf(hf.WssJoinPath, magicpromptRepo) + args := ctx.State["args"].(string) + c, _, err := websocket.DefaultDialer.Dial(magicpromptURL, nil) + if err != nil { + ctx.SendChain(message.Text("ERROR: ", err)) + return + } + defer c.Close() + + r := hf.PushRequest{ + FnIndex: 0, + Data: []interface{}{args}, + } + b, err := json.Marshal(r) + if err != nil { + ctx.SendChain(message.Text("ERROR: ", err)) + return + } + + err = c.WriteMessage(websocket.TextMessage, b) + if err != nil { + ctx.SendChain(message.Text("ERROR: ", err)) + return + } + t := time.NewTicker(time.Second * 1) + defer t.Stop() + for { + select { + case <-t.C: + _, data, err := c.ReadMessage() + if err != nil { + ctx.SendChain(message.Text("ERROR: ", err)) + return + } + j := gjson.ParseBytes(data) + if j.Get("msg").String() == hf.WssCompleteStatus { + m := message.Message{} + for _, v := range strings.Split(j.Get("output.data.0").String(), "\n\n") { + m = append(m, ctxext.FakeSenderForwardNode(ctx, message.Text(v))) + } + if id := ctx.Send(m).ID(); id == 0 { + ctx.SendChain(message.Text("ERROR: 可能被风控或下载图片用时过长,请耐心等待")) + } + return + } + case <-_ctx.Done(): + ctx.SendChain(message.Text("ERROR: 吟唱提示指令超时")) + return + } + } + }) +} diff --git a/plugin/realcugan/realcugan.go b/plugin/realcugan/realcugan.go new file mode 100644 index 00000000..0c3f65d5 --- /dev/null +++ b/plugin/realcugan/realcugan.go @@ -0,0 +1,105 @@ +// Package realcugan Real-CUGAN清晰术 +package realcugan + +import ( + "bytes" + "encoding/base64" + "encoding/json" + "fmt" + "image" + "strings" + + hf "github.com/FloatTech/AnimeAPI/huggingface" + "github.com/FloatTech/floatbox/web" + ctrl "github.com/FloatTech/zbpctrl" + "github.com/FloatTech/zbputils/control" + "github.com/FloatTech/zbputils/ctxext" + "github.com/tidwall/gjson" + zero "github.com/wdvxdr1123/ZeroBot" + "github.com/wdvxdr1123/ZeroBot/message" +) + +const ( + realcuganRepo = "shichen1231/Real-CUGAN" +) + +func init() { // 插件主体 + engine := control.Register("realcugan", &ctrl.Options[*zero.Ctx]{ + DisableOnDefault: false, + Brief: "Real-CUGAN清晰术", + Help: "- 清晰术(双重吟唱|三重吟唱|四重吟唱)(强力术式|中等术式|弱术式|不变式|原式)[图片]", + PrivateDataFolder: "realcugan", + }) + engine.OnPrefix("清晰术", zero.MustProvidePicture).SetBlock(true). + Handle(func(ctx *zero.Ctx) { + ctx.SendChain(message.Text("少女祈祷中...")) + realcuganURL := fmt.Sprintf(hf.HTTPSPredictPath, realcuganRepo) + for _, url := range ctx.State["image_url"].([]string) { + imgdata, err := web.GetData(url) + if err != nil { + ctx.SendChain(message.Text("ERROR: ", err)) + return + } + img, _, err := image.Decode(bytes.NewReader(imgdata)) + if err != nil { + ctx.SendChain(message.Text("ERROR: ", err)) + return + } + // 初始化参数 + var ( + fashu = ctx.Event.Message.ExtractPlainText() + scale = 2 + con = "conservative" + ) + switch { + case strings.Contains(fashu, "双重吟唱"): + scale = 2 + case strings.Contains(fashu, "三重吟唱") && img.Bounds().Dx()*img.Bounds().Dy() < 400000: + scale = 3 + case strings.Contains(fashu, "四重吟唱") && img.Bounds().Dx()*img.Bounds().Dy() < 400000: + scale = 4 + } + switch { + case strings.Contains(fashu, "强力术式"): + con = "denoise3x" + case strings.Contains(fashu, "中等术式"): + con = "no-denoise" + if scale == 2 { + con = "denoise2x" + } + case strings.Contains(fashu, "弱术式"): + con = "no-denoise" + if scale == 2 { + con = "denoise1x" + } + case strings.Contains(fashu, "不变式"): + con = "no-denoise" + case strings.Contains(fashu, "原式"): + con = "conservative" + } + modelname := fmt.Sprintf("up%vx-latest-%v.pth", scale, con) + encodeStr := base64.StdEncoding.EncodeToString(imgdata) + encodeStr = "data:image/jpeg;base64," + encodeStr + pr := hf.PushRequest{ + Data: []interface{}{encodeStr, modelname, 2}, + } + buf := bytes.NewBuffer([]byte{}) + err = json.NewEncoder(buf).Encode(pr) + if err != nil { + ctx.SendChain(message.Text("ERROR: ", err)) + return + } + data, err := web.PostData(realcuganURL, "application/json", buf) + if err != nil { + ctx.SendChain(message.Text("ERROR: ", err)) + return + } + imgStr := gjson.ParseBytes(data).Get("data.0").String() + m := message.Message{ctxext.FakeSenderForwardNode(ctx, message.Text(scale, "重唱", con, "分支大清晰术!")), + ctxext.FakeSenderForwardNode(ctx, message.Image("base64://"+strings.TrimPrefix(imgStr, "data:image/png;base64,")))} + if id := ctx.Send(m).ID(); id == 0 { + ctx.SendChain(message.Text("ERROR: 可能被风控或下载图片用时过长,请耐心等待")) + } + } + }) +} diff --git a/plugin/vitsnyaru/vitsnyaru.go b/plugin/vitsnyaru/vitsnyaru.go new file mode 100644 index 00000000..a729e82d --- /dev/null +++ b/plugin/vitsnyaru/vitsnyaru.go @@ -0,0 +1,141 @@ +// Package vitsnyaru vits猫雷 +package vitsnyaru + +import ( + "context" + "encoding/json" + "fmt" + "strings" + "time" + + hf "github.com/FloatTech/AnimeAPI/huggingface" + ctrl "github.com/FloatTech/zbpctrl" + "github.com/FloatTech/zbputils/control" + "github.com/tidwall/gjson" + zero "github.com/wdvxdr1123/ZeroBot" + "github.com/wdvxdr1123/ZeroBot/message" +) + +const ( + vitsnyaruRepo = "innnky/vits-nyaru" +) + +func init() { // 插件主体 + engine := control.Register("vitsnyaru", &ctrl.Options[*zero.Ctx]{ + DisableOnDefault: false, + Brief: "vits猫雷", + Help: "- 让猫雷说 xxx", + PrivateDataFolder: "vitsnyaru", + }) + + // 开启 + engine.OnPrefix(`让猫雷说`).SetBlock(true). + Handle(func(ctx *zero.Ctx) { + _ctx, _cancel := context.WithTimeout(context.Background(), hf.TimeoutMax*time.Second) + defer _cancel() + ch := make(chan []byte, 1) + + args := ctx.State["args"].(string) + pushURL := fmt.Sprintf(hf.HTTPSPushPath, vitsnyaruRepo) + statusURL := fmt.Sprintf(hf.HTTPSStatusPath, vitsnyaruRepo) + ctx.SendChain(message.Text("少女祈祷中...")) + var ( + pushReq hf.PushRequest + pushRes hf.PushResponse + statusReq hf.StatusRequest + statusRes hf.StatusResponse + data []byte + ) + + // 获取clean后的文本 + pushReq = hf.PushRequest{ + Action: hf.DefaultAction, + Data: []interface{}{args}, + FnIndex: 1, + } + pushRes, err := hf.Push(pushURL, pushReq) + if err != nil { + ctx.SendChain(message.Text("ERROR: ", err)) + return + } + statusReq = hf.StatusRequest{ + Hash: pushRes.Hash, + } + + t := time.NewTicker(time.Second * 1) + defer t.Stop() + LOOP: + for { + select { + case <-t.C: + data, err = hf.Status(statusURL, statusReq) + if err != nil { + ch <- data + break LOOP + } + if gjson.ParseBytes(data).Get("status").String() == hf.CompleteStatus { + ch <- data + break LOOP + } + case <-_ctx.Done(): + ch <- data + break LOOP + } + } + + data = <-ch + err = json.Unmarshal(data, &statusRes) + if err != nil { + ctx.SendChain(message.Text("ERROR: ", err)) + return + } + + // 用clean的文本预测语音 + pushReq = hf.PushRequest{ + Action: hf.DefaultAction, + Data: statusRes.Data.Data, + FnIndex: 2, + } + pushRes, err = hf.Push(pushURL, pushReq) + if err != nil { + ctx.SendChain(message.Text("ERROR: ", err)) + return + } + statusReq = hf.StatusRequest{ + Hash: pushRes.Hash, + } + + LOOP2: + for { + select { + case <-t.C: + data, err = hf.Status(statusURL, statusReq) + if err != nil { + ch <- data + break LOOP2 + } + if gjson.ParseBytes(data).Get("status").String() == hf.CompleteStatus { + ch <- data + break LOOP2 + } + case <-_ctx.Done(): + ch <- data + break LOOP2 + } + } + + data = <-ch + err = json.Unmarshal(data, &statusRes) + if err != nil { + ctx.SendChain(message.Text("ERROR: ", err)) + return + } + + // 发送语音 + if len(statusRes.Data.Data) < 2 { + ctx.SendChain(message.Text("ERROR: 未能获取语音")) + return + } + ctx.SendChain(message.Record("base64://" + strings.TrimPrefix(statusRes.Data.Data[1].(string), "data:audio/wav;base64,"))) + }) +}