Skip to content

Commit 0d708cd

Browse files
committed
插件:AnimeTrace 动画/Galgame识别
1 parent 6a747d2 commit 0d708cd

File tree

3 files changed

+159
-0
lines changed

3 files changed

+159
-0
lines changed

README.md

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -392,6 +392,18 @@ print("run[CQ:image,file="+j["img"]+"]")
392392

393393
- [x] waifu | 随机waifu(从[100000个AI生成的waifu](https://www.thiswaifudoesnotexist.net/)中随机一位)
394394

395+
</details>
396+
<details>
397+
<summary>AnimeTrace 动画/Galgame识别</summary>
398+
399+
`import _ "github.com/FloatTech/ZeroBot-Plugin/plugin/animetrace"`
400+
401+
基于[AnimeTrace](https://ai.animedb.cn/)API 的识图搜索插件
402+
403+
- [x] Gal识图 | Gal识图 [模型名]
404+
405+
- [x] 动漫识图 | 动漫识图 2 | 动漫识图 [模型名]
406+
395407
</details>
396408
<details>
397409
<summary>支付宝到账语音</summary>

main.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,7 @@ import (
6767
_ "github.com/FloatTech/ZeroBot-Plugin/plugin/aifalse" // 服务器监控
6868
_ "github.com/FloatTech/ZeroBot-Plugin/plugin/aiwife" // 随机老婆
6969
_ "github.com/FloatTech/ZeroBot-Plugin/plugin/alipayvoice" // 支付宝到账语音
70+
_ "github.com/FloatTech/ZeroBot-Plugin/plugin/animetrace" // AnimeTrace 动画/Galgame识别
7071
_ "github.com/FloatTech/ZeroBot-Plugin/plugin/autowithdraw" // 触发者撤回时也自动撤回
7172
_ "github.com/FloatTech/ZeroBot-Plugin/plugin/baiduaudit" // 百度内容审核
7273
_ "github.com/FloatTech/ZeroBot-Plugin/plugin/base16384" // base16384加解密

plugin/animetrace/main.go

Lines changed: 146 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,146 @@
1+
// Package animetrace AnimeTrace 动画/Galgame识别
2+
package animetrace
3+
4+
import (
5+
"bytes"
6+
"encoding/base64"
7+
"errors"
8+
"fmt"
9+
"image"
10+
"image/jpeg"
11+
"mime/multipart"
12+
"strings"
13+
14+
"github.com/FloatTech/floatbox/web"
15+
"github.com/FloatTech/imgfactory"
16+
ctrl "github.com/FloatTech/zbpctrl"
17+
"github.com/FloatTech/zbputils/control"
18+
"github.com/FloatTech/zbputils/ctxext"
19+
"github.com/disintegration/imaging"
20+
"github.com/tidwall/gjson"
21+
zero "github.com/wdvxdr1123/ZeroBot"
22+
"github.com/wdvxdr1123/ZeroBot/message"
23+
)
24+
25+
func init() {
26+
engine := control.Register("animetrace", &ctrl.Options[*zero.Ctx]{
27+
DisableOnDefault: false,
28+
Brief: "AnimeTrace 动画/Galgame识别插件",
29+
Help: "- Gal识图\n- 动漫识图\n- 动漫识图 2\n- 动漫识图 [模型名]\n- Gal识图 [模型名]",
30+
})
31+
32+
engine.OnPrefix("gal识图", zero.OnlyGroup, zero.MustProvidePicture).SetBlock(true).Handle(func(ctx *zero.Ctx) {
33+
args := ctx.State["args"].(string)
34+
var model string
35+
switch strings.TrimSpace(args) {
36+
case "":
37+
model = "full_game_model_kira" // 默认使用的模型
38+
default:
39+
model = args // 自定义设置模型
40+
}
41+
processImageRecognition(ctx, model)
42+
})
43+
44+
engine.OnPrefix("动漫识图", zero.OnlyGroup, zero.MustProvidePicture).SetBlock(true).Handle(func(ctx *zero.Ctx) {
45+
args := ctx.State["args"].(string)
46+
var model string
47+
switch strings.TrimSpace(args) {
48+
case "":
49+
model = "anime_model_lovelive"
50+
case "2":
51+
model = "pre_stable"
52+
default:
53+
model = args
54+
}
55+
processImageRecognition(ctx, model)
56+
})
57+
}
58+
59+
// 处理图片识别
60+
func processImageRecognition(ctx *zero.Ctx, model string) {
61+
urls := ctx.State["image_url"].([]string)
62+
if len(urls) == 0 {
63+
return
64+
}
65+
imageData, err := imgfactory.Load(urls[0])
66+
if err != nil {
67+
ctx.Send(message.Text("下载图片失败: ", err))
68+
return
69+
}
70+
//ctx.Send(message.Text(model))
71+
respBody, err := createAndSendMultipartRequest("https://api.animetrace.com/v1/search", imageData, map[string]string{
72+
"is_multi": "0",
73+
"model": model,
74+
"ai_detect": "0",
75+
})
76+
if err != nil {
77+
ctx.Send(message.Text("识别请求失败: ", err))
78+
return
79+
}
80+
code := gjson.Get(string(respBody), "code").Int()
81+
if code != 0 {
82+
ctx.Send(message.Text("错误: ", gjson.Get(string(respBody), "zh_message").String()))
83+
return
84+
}
85+
dataArray := gjson.Get(string(respBody), "data").Array()
86+
countStr := fmt.Sprintf("%d", len(dataArray))
87+
if countStr == "0" {
88+
ctx.Send(message.Text("未识别到任何角色"))
89+
return
90+
}
91+
var sk message.Message
92+
sk = append(sk, ctxext.FakeSenderForwardNode(ctx, message.Text("共识别到 "+countStr+" 个角色,可能是以下来源")))
93+
for _, value := range dataArray {
94+
boxArray := value.Get("box").Array()
95+
imgWidth, imgHeight := imageData.Bounds().Dx(), imageData.Bounds().Dy() // 你可以从 `imageData.Bounds()` 获取
96+
box := []int{
97+
int(boxArray[0].Float() * float64(imgWidth)),
98+
int(boxArray[1].Float() * float64(imgHeight)),
99+
int(boxArray[2].Float() * float64(imgWidth)),
100+
int(boxArray[3].Float() * float64(imgHeight)),
101+
}
102+
croppedImg := imaging.Crop(imageData, image.Rect(box[0], box[1], box[2], box[3]))
103+
var buf bytes.Buffer
104+
if err := imaging.Encode(&buf, croppedImg, imaging.JPEG, imaging.JPEGQuality(80)); err != nil {
105+
ctx.Send(message.Text("图片编码失败: ", err))
106+
continue
107+
}
108+
109+
base64Str := base64.StdEncoding.EncodeToString(buf.Bytes())
110+
var sb strings.Builder
111+
value.Get("character").ForEach(func(_, character gjson.Result) bool {
112+
sb.WriteString(fmt.Sprintf("《%s》的角色 %s\n", character.Get("work").String(), character.Get("character").String()))
113+
return true
114+
})
115+
sk = append(sk, ctxext.FakeSenderForwardNode(ctx, message.Image("base64://"+base64Str), message.Text(sb.String())))
116+
}
117+
ctx.SendGroupForwardMessage(ctx.Event.GroupID, sk)
118+
}
119+
120+
// 发送图片识别请求
121+
func createAndSendMultipartRequest(url string, img image.Image, formFields map[string]string) ([]byte, error) {
122+
body := &bytes.Buffer{}
123+
writer := multipart.NewWriter(body)
124+
125+
// 直接编码图片
126+
part, err := writer.CreateFormFile("file", "image.jpg")
127+
if err != nil {
128+
return nil, errors.New("创建文件字段失败: " + err.Error())
129+
}
130+
if err := jpeg.Encode(part, img, &jpeg.Options{Quality: 80}); err != nil {
131+
return nil, errors.New("图片编码失败: " + err.Error())
132+
}
133+
134+
// 写入其他字段
135+
for key, value := range formFields {
136+
if err := writer.WriteField(key, value); err != nil {
137+
return nil, errors.New("写入表单字段失败 (" + key + "): " + err.Error())
138+
}
139+
}
140+
141+
if err := writer.Close(); err != nil {
142+
return nil, errors.New("关闭 multipart writer 失败: " + err.Error())
143+
}
144+
145+
return web.PostData(url, writer.FormDataContentType(), body)
146+
}

0 commit comments

Comments
 (0)