Skip to content

Commit a960b7c

Browse files
committed
⚡️ 更好的识图触发模式
1 parent 4a00a5e commit a960b7c

File tree

2 files changed

+113
-130
lines changed

2 files changed

+113
-130
lines changed

setutime/pic_searcher.go

Lines changed: 80 additions & 90 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,13 @@ package setutime
22

33
import (
44
"fmt"
5+
"strconv"
6+
"strings"
7+
"time"
58

69
utils "github.com/Yiwen-Chan/ZeroBot-Plugin/setutime/utils"
710
zero "github.com/wdvxdr1123/ZeroBot"
11+
"github.com/wdvxdr1123/ZeroBot/message"
812
)
913

1014
func init() { // 插件主体
@@ -28,111 +32,97 @@ func init() { // 插件主体
2832
ctx.Send(illust.DetailPic)
2933
return
3034
})
31-
// 通过回复以图搜图
32-
zero.OnRegex(`\[CQ:reply,id=(.*?)\](.*)搜索图片`).SetBlock(true).SetPriority(32).
35+
// 以图搜图
36+
zero.OnMessage(FullMatchText("以图搜图", "搜索图片", "以图识图"), MustHasPicture()).SetBlock(true).SetPriority(999).
3337
Handle(func(ctx *zero.Ctx) {
34-
var pics []string // 图片搜索池子
35-
// 获取回复的上文图片链接
36-
id := utils.Str2Int(ctx.State["regex_matched"].([]string)[1])
37-
for _, elem := range ctx.GetMessage(id).Elements {
38-
if elem.Type == "image" {
39-
pics = append(pics, elem.Data["url"])
40-
}
41-
}
42-
// 没有收到图片则向用户索取
43-
if len(pics) == 0 {
44-
ctx.Send("请发送多张图片!")
45-
next := ctx.FutureEvent("message", ctx.CheckSession())
46-
recv, cancel := next.Repeat()
47-
for e := range recv { // 循环获取channel发来的信息
48-
if len(e.Message) == 1 && e.Message[0].Type == "text" {
49-
cancel() // 如果是纯文本则退出索取
50-
break
51-
}
52-
for _, elem := range e.Message {
53-
if elem.Type == "image" { // 将信息中的图片添加到搜索池子
54-
pics = append(pics, elem.Data["url"])
55-
}
56-
}
57-
if len(pics) >= 5 {
58-
cancel() // 如果是图片数量大于等于5则退出索取
59-
break
60-
}
61-
}
62-
}
63-
if len(pics) == 0 {
64-
ctx.Send("没有收到图片,搜图结束......")
65-
return
66-
}
6738
// 开始搜索图片
6839
ctx.Send("少女祈祷中......")
69-
for _, pic := range pics {
70-
if text, err := utils.SauceNaoSearch(pic); err == nil {
71-
ctx.Send(text) // 返回SauceNAO的结果
40+
for _, pic := range ctx.State["image_url"].([]string) {
41+
fmt.Println(pic)
42+
if m, err := utils.SauceNaoSearch(pic); err == nil {
43+
ctx.SendChain(m...) // 返回SauceNAO的结果
7244
continue
7345
} else {
74-
ctx.Send(fmt.Sprintf("ERROR: %v", err))
46+
ctx.SendChain(message.Text("ERROR: ", err))
7547
}
76-
if text, err := utils.Ascii2dSearch(pic); err == nil {
77-
ctx.Send(text) // 返回Ascii2d的结果
48+
if m, err := utils.Ascii2dSearch(pic); err == nil {
49+
ctx.SendChain(m...) // 返回Ascii2d的结果
7850
continue
7951
} else {
80-
ctx.Send(fmt.Sprintf("ERROR: %v", err))
52+
ctx.SendChain(message.Text("ERROR: ", err))
8153
}
8254
}
8355
return
8456
})
85-
// 通过命令以图搜图
86-
zero.OnKeywordGroup([]string{"以图识图", "以图搜图", "搜索图片"}).SetBlock(true).SetPriority(33).
87-
Handle(func(ctx *zero.Ctx) {
88-
var pics []string // 图片搜索池子
89-
// 获取信息中图片链接
90-
for _, elem := range ctx.Event.Message {
91-
if elem.Type == "image" {
92-
pics = append(pics, elem.Data["url"])
93-
}
94-
}
95-
// 没有收到图片则向用户索取
96-
if len(pics) == 0 {
97-
ctx.Send("请发送多张图片!")
98-
next := ctx.FutureEvent("message", zero.CheckUser(ctx.Event.UserID))
99-
recv, cancel := next.Repeat()
100-
for e := range recv { // 循环获取channel发来的信息
101-
if len(e.Message) == 1 && e.Message[0].Type == "text" {
102-
cancel() // 如果是纯文本则退出索取
103-
break
104-
}
105-
for _, elem := range e.Message {
106-
if elem.Type == "image" { // 将信息中的图片添加到搜索池子
107-
pics = append(pics, elem.Data["url"])
108-
}
109-
}
110-
if len(pics) >= 5 {
111-
cancel() // 如果是图片数量大于等于5则退出索取
112-
break
57+
}
58+
59+
// FullMatchText 如果信息中文本完全匹配则返回 true
60+
func FullMatchText(src ...string) zero.Rule {
61+
return func(ctx *zero.Ctx) bool {
62+
msg := ctx.Event.Message
63+
for _, elem := range msg {
64+
if elem.Type == "text" {
65+
text := elem.Data["text"]
66+
text = strings.ReplaceAll(text, " ", "")
67+
text = strings.ReplaceAll(text, "\r", "")
68+
text = strings.ReplaceAll(text, "\n", "")
69+
for _, s := range src {
70+
if text == s {
71+
return true
11372
}
11473
}
11574
}
116-
if len(pics) == 0 {
117-
ctx.Send("没有收到图片,搜图结束......")
118-
return
75+
}
76+
return false
77+
}
78+
}
79+
80+
// HasPicture 消息含有图片返回 true
81+
func HasPicture() zero.Rule {
82+
return func(ctx *zero.Ctx) bool {
83+
msg := ctx.Event.Message
84+
url := []string{}
85+
// 如果是回复信息则将信息替换成被回复的那条
86+
if msg[0].Type == "reply" {
87+
id, _ := strconv.Atoi(msg[0].Data["id"])
88+
msg = ctx.GetMessage(int64(id)).Elements
89+
}
90+
// 遍历信息中所有图片
91+
for _, elem := range msg {
92+
if elem.Type == "image" {
93+
url = append(url, elem.Data["url"])
11994
}
120-
// 开始搜索图片
121-
ctx.Send("少女祈祷中......")
122-
for _, pic := range pics {
123-
if text, err := utils.SauceNaoSearch(pic); err == nil {
124-
ctx.Send(text) // 返回SauceNAO的结果
125-
continue
126-
} else {
127-
ctx.Send(fmt.Sprintf("ERROR: %v", err))
128-
}
129-
if text, err := utils.Ascii2dSearch(pic); err == nil {
130-
ctx.Send(text) // 返回Ascii2d的结果
131-
continue
132-
} else {
133-
ctx.Send(fmt.Sprintf("ERROR: %v", err))
134-
}
95+
}
96+
// 如果有图片就返回true
97+
if len(url) > 0 {
98+
ctx.State["image_url"] = url
99+
return true
100+
}
101+
return false
102+
}
103+
}
104+
105+
// MustHasPicture 消息不存在图片阻塞60秒至有图片,超时返回 false
106+
func MustHasPicture() zero.Rule {
107+
return func(ctx *zero.Ctx) bool {
108+
if HasPicture()(ctx) {
109+
return true
110+
}
111+
// 没有图片就索取
112+
ctx.Send("请发送一张图片")
113+
next := zero.NewFutureEvent("message", 999, false, zero.CheckUser(ctx.Event.UserID), HasPicture())
114+
recv, cancel := next.Repeat()
115+
select {
116+
case e := <-recv:
117+
cancel()
118+
newCtx := &zero.Ctx{Event: e, State: zero.State{}}
119+
if HasPicture()(newCtx) {
120+
ctx.State["image_url"] = newCtx.State["image_url"]
121+
return true
135122
}
136-
return
137-
})
123+
return false
124+
case <-time.After(time.Second * 60):
125+
return false
126+
}
127+
}
138128
}

setutime/utils/saucenao.go

Lines changed: 33 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -1,84 +1,77 @@
11
package utils
22

33
import (
4-
"errors"
54
"fmt"
65
"io/ioutil"
76
"net/http"
87
"net/url"
9-
"strings"
108

119
"github.com/tidwall/gjson"
10+
"github.com/wdvxdr1123/ZeroBot/message"
1211
)
1312

1413
// SauceNaoSearch SauceNao 以图搜图 需要链接 返回错误和信息
15-
func SauceNaoSearch(pic string) (text string, err error) {
14+
func SauceNaoSearch(pic string) (message.Message, error) {
1615
var (
1716
api = "https://saucenao.com/search.php"
1817
apiKey = "2cc2772ca550dbacb4c35731a79d341d1a143cb5"
1918

2019
minSimilarity = 70.0 // 返回图片结果的最小相似度
2120
)
2221

23-
transport := http.Transport{
24-
DisableKeepAlives: true,
25-
}
26-
client := &http.Client{
27-
Transport: &transport,
28-
}
29-
3022
// 包装请求参数
31-
data := url.Values{}
32-
data.Set("url", pic) // 图片链接
33-
data.Set("api_key", apiKey) // api_key
34-
data.Set("db", "5") // 只搜索Pixiv
35-
data.Set("numres", "1") // 返回一个结果
36-
data.Set("output_type", "2") // 返回JSON格式数据
37-
fromData := strings.NewReader(data.Encode())
23+
link, _ := url.Parse(api)
24+
link.RawQuery = url.Values{
25+
"url": []string{pic},
26+
"api_key": []string{apiKey},
27+
"db": []string{"5"},
28+
"numres": []string{"1"},
29+
"output_type": []string{"2"},
30+
}.Encode()
3831

3932
// 网络请求
40-
req, err := http.NewRequest("POST", api, fromData)
33+
client := &http.Client{}
34+
req, err := http.NewRequest("GET", link.String(), nil)
4135
if err != nil {
42-
return "", err
36+
return nil, err
4337
}
4438
req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
4539
req.Header.Set("Accept", "Mozilla/5.0 (Windows NT 6.1; WOW64; rv:6.0) Gecko/20100101 Firefox/6.0")
4640
resp, err := client.Do(req)
4741
if err != nil {
48-
return "", err
42+
return nil, err
4943
}
5044
defer resp.Body.Close()
5145
body, err := ioutil.ReadAll(resp.Body)
5246
if err != nil {
53-
return "", err
47+
return nil, err
5448
}
5549

56-
if code := resp.StatusCode; code != 200 {
50+
if resp.StatusCode != http.StatusOK {
5751
// 如果返回不是200则立刻抛出错误
58-
return "", errors.New(fmt.Sprintf("SauceNAO not found, code %d", code))
52+
return nil, fmt.Errorf("SauceNAO not found, code %d", resp.StatusCode)
5953
}
6054
content := gjson.ParseBytes(body)
6155
if status := content.Get("header.status").Int(); status != 0 {
6256
// 如果json信息返回status不为0则立刻抛出错误
63-
return "", errors.New(fmt.Sprintf("SauceNAO not found, status %d", status))
57+
return nil, fmt.Errorf("SauceNAO not found, status %d", status)
6458
}
6559
if content.Get("results.0.header.similarity").Float() < minSimilarity {
66-
return "", errors.New("SauceNAO not found")
60+
return nil, fmt.Errorf("SauceNAO not found")
6761
}
62+
result := content.Get("results.0")
6863
// 正常发送
69-
return fmt.Sprintf(
70-
`[SetuTime] 我有把握是这个![CQ:image,file=%s]相似度:%s%%
71-
标题:%s
72-
插画ID:%d
73-
画师:%s
74-
画师ID:%d
75-
直链:https://pixivel.moe/detail?id=%d`,
76-
content.Get("results.0.header.thumbnail").Str,
77-
content.Get("results.0.header.similarity").Str,
78-
content.Get("results.0.data.title").Str,
79-
content.Get("results.0.data.pixiv_id").Int(),
80-
content.Get("results.0.data.member_name").Str,
81-
content.Get("results.0.data.member_id").Int(),
82-
content.Get("results.0.data.pixiv_id").Int(),
83-
), nil
64+
return message.Message{
65+
message.Text("[SetuTime] 我有把握是这个!"),
66+
message.Image(result.Get("header.thumbnail").Str),
67+
message.Text(
68+
"\n",
69+
"相似度:", result.Get("header.similarity").Str, "\n",
70+
"标题:", result.Get("data.title").Str, "\n",
71+
"插画ID:", result.Get("data.pixiv_id").Int(), "\n",
72+
"画师:", result.Get("data.member_name").Str, "\n",
73+
"画师ID:", result.Get("data.member_id").Int(), "\n",
74+
"直链:", "https://pixivel.moe/detail?id=", result.Get("data.pixiv_id").Int(),
75+
),
76+
}, nil
8477
}

0 commit comments

Comments
 (0)