Skip to content

Commit 9079f50

Browse files
authored
improve logic based on requested changes
1 parent 926d87d commit 9079f50

File tree

1 file changed

+130
-153
lines changed

1 file changed

+130
-153
lines changed

plugin/pig/pig.go

Lines changed: 130 additions & 153 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,8 @@
22
package pigpig
33

44
import (
5-
"encoding/base64"
65
"encoding/json"
7-
"errors" // 【新增】引入 errors 库
6+
"errors"
87
"fmt"
98
"math/rand"
109
"path/filepath"
@@ -18,217 +17,195 @@ import (
1817
"github.com/wdvxdr1123/ZeroBot/message"
1918
)
2019

21-
// PigResponse 对应精简后的 JSON 结构
22-
type PigResponse struct {
20+
// pigResponse 内部结构体
21+
type pigResponse struct {
2322
Total int `json:"total"`
24-
Images []PigImage `json:"images"`
23+
Images []pigImage `json:"images"`
2524
}
2625

27-
// PigImage 图片信息结构
28-
type PigImage struct {
26+
// pigImage 内部结构体
27+
type pigImage struct {
2928
ID string `json:"id"`
3029
Title string `json:"title"`
3130
Filename string `json:"filename"`
3231
}
3332

3433
var (
35-
// 数据缓存
36-
pigCache []PigImage
37-
// 读写锁
38-
pigMutex sync.RWMutex
39-
// 上次更新时间
34+
pigCache []pigImage
35+
pigMap = make(map[string]*pigImage) // 使用 map 方便 ID 查找
36+
pigMutex sync.RWMutex
4037
lastUpdateTime time.Time
41-
// 引擎实例
42-
engine *control.Engine
43-
)
4438

45-
func init() {
39+
// 初始化 engine
4640
engine = control.AutoRegister(&ctrl.Options[*zero.Ctx]{
4741
DisableOnDefault: false,
4842
Brief: "来份猪猪",
4943
Help: "- 随机猪猪:随机发送一张猪猪表情\n- 搜索猪猪 [关键词]:搜索相关猪猪\n- 猪猪id [id]:精确查找",
5044
PrivateDataFolder: "Pig",
5145
})
46+
)
5247

53-
engine.OnRegex(`^随机猪猪$`).SetBlock(true).Handle(handleRandomPig)
54-
engine.OnRegex(`^搜索猪猪\s+(.+)$`).SetBlock(true).Handle(handleSearchPig)
55-
engine.OnRegex(`^猪猪id\s+(\d+)$`).SetBlock(true).Handle(handlePigByID)
56-
}
57-
58-
// checkAndUpdateData 检查并更新数据
59-
func checkAndUpdateData(ctx *zero.Ctx) error {
60-
pigMutex.Lock()
61-
defer pigMutex.Unlock()
48+
func init() {
49+
// 1. 随机猪猪
50+
engine.OnRegex(`^(随机猪猪|来份猪猪|抽个猪猪)$`).SetBlock(true).Handle(func(ctx *zero.Ctx) {
51+
if err := checkAndUpdateData(); err != nil {
52+
ctx.SendChain(message.Text("[Pig] ERROR: ", err, "\nEXP: 随机猪猪失败,获取数据错误"))
53+
return
54+
}
6255

63-
now := time.Now()
64-
// 如果缓存为空,或者今天是新的一天,则尝试更新
65-
shouldUpdate := len(pigCache) == 0 || now.Format("2006-01-02") != lastUpdateTime.Format("2006-01-02")
56+
pigMutex.RLock()
57+
defer pigMutex.RUnlock()
6658

67-
if shouldUpdate {
68-
if ctx != nil {
69-
ctx.SendChain(message.Text("🐷 正在同步今日猪猪数据,请稍候..."))
59+
if len(pigCache) == 0 {
60+
ctx.SendChain(message.Text("[Pig] ERROR: 暂无猪猪数据,请联系管理员"))
61+
return
7062
}
7163

72-
// 读取根目录下的 pig_data.json
73-
dataBytes, err := engine.GetLazyData("pig_data.json", true)
64+
target := pigCache[rand.Intn(len(pigCache))]
65+
imgData, err := target.fetch()
7466
if err != nil {
75-
return errors.New("读取数据文件失败: " + err.Error())
67+
ctx.SendChain(message.Text("[Pig] ERROR: ", err, "\nEXP: 图片加载失败"))
68+
return
7669
}
7770

78-
var data PigResponse
79-
if err := json.Unmarshal(dataBytes, &data); err != nil {
80-
return errors.New("解析JSON失败: " + err.Error())
81-
}
71+
ctx.SendChain(
72+
message.Text(fmt.Sprintf("🐷 ID: %s | %s", target.ID, target.Title)),
73+
message.ImageBytes(imgData), // 直接使用 ImageBytes,无需 base64
74+
)
75+
})
8276

83-
if len(data.Images) == 0 {
84-
return errors.New("数据文件为空")
77+
// 2. 搜索猪猪
78+
engine.OnRegex(`^搜索猪猪\s+(.+)$`).SetBlock(true).Handle(func(ctx *zero.Ctx) {
79+
keyword := strings.TrimSpace(ctx.State["regex_matched"].([]string)[1])
80+
81+
if err := checkAndUpdateData(); err != nil {
82+
ctx.SendChain(message.Text("[Pig] ERROR: ", err, "\nEXP: 搜索猪猪失败,获取数据错误"))
83+
return
8584
}
8685

87-
pigCache = data.Images
88-
lastUpdateTime = now
86+
pigMutex.RLock()
87+
defer pigMutex.RUnlock()
8988

90-
if ctx != nil {
91-
ctx.SendChain(message.Text(fmt.Sprintf("✅ 同步完成!当前共有 %d 只猪猪。", len(pigCache))))
89+
var results []pigImage
90+
for _, p := range pigCache {
91+
if strings.Contains(p.Title, keyword) {
92+
results = append(results, p)
93+
}
9294
}
93-
}
94-
return nil
95-
}
9695

97-
// fetchImageLazy 按需从 assets 文件夹获取图片并转为 Base64
98-
func fetchImageLazy(img PigImage) (string, error) {
99-
if img.Filename == "" {
100-
return "", errors.New("图片数据异常,缺少文件名")
101-
}
96+
if len(results) == 0 {
97+
ctx.SendChain(message.Text("[Pig] ERROR: 未找到包含“", keyword, "”的猪猪"))
98+
return
99+
}
102100

103-
// 拼接 assets 子目录
104-
targetPath := filepath.Join("assets", img.Filename)
101+
var sb strings.Builder
102+
sb.WriteString(fmt.Sprintf("🔎 根据关键词“%s”找到 %d 只猪猪:\n", keyword, len(results)))
105103

106-
// false 表示优先使用本地文件,不强制从网络拉取
107-
imgData, err := engine.GetLazyData(targetPath, false)
108-
if err != nil {
109-
return "", errors.New("图片资源缺失 (" + targetPath + "): " + err.Error())
110-
}
104+
maxShow := 10
105+
for i, p := range results {
106+
if i >= maxShow {
107+
sb.WriteString(fmt.Sprintf("\n...等共 %d 条结果", len(results)))
108+
break
109+
}
110+
sb.WriteString(fmt.Sprintf("%d: %s (ID: %s)\n", i+1, p.Title, p.ID))
111+
}
111112

112-
return "base64://" + base64.StdEncoding.EncodeToString(imgData), nil
113-
}
113+
sb.WriteString("\n为您返回第一个猪猪:\n💡 提示:输入“猪猪id [id]”可精确获取")
114114

115-
// handleRandomPig 处理随机猪猪
116-
func handleRandomPig(ctx *zero.Ctx) {
117-
if err := checkAndUpdateData(ctx); err != nil {
118-
ctx.SendChain(message.Text("[Pig] ERROR: ", err, "\nEXP: 随机猪猪失败,获取数据错误"))
119-
return
120-
}
115+
imgData, err := results[0].fetch()
116+
if err != nil {
117+
ctx.SendChain(message.Text(sb.String(), "\n\n[Pig] ERROR: ", err, "\nEXP: 图片加载失败"))
118+
return
119+
}
120+
121+
ctx.SendChain(
122+
message.Text(sb.String()),
123+
message.ImageBytes(imgData), // 直接使用 ImageBytes
124+
)
125+
})
121126

122-
pigMutex.RLock()
123-
defer pigMutex.RUnlock()
127+
// 3. 猪猪id精确查找
128+
engine.OnRegex(`^猪猪id\s+(\d+)$`).SetBlock(true).Handle(func(ctx *zero.Ctx) {
129+
targetID := ctx.State["regex_matched"].([]string)[1]
124130

125-
if len(pigCache) == 0 {
126-
ctx.SendChain(message.Text("[Pig] ERROR: 暂无猪猪数据,请联系管理员"))
127-
return
128-
}
131+
if err := checkAndUpdateData(); err != nil {
132+
ctx.SendChain(message.Text("[Pig] ERROR: ", err, "\nEXP: 精确查找失败,获取数据错误"))
133+
return
134+
}
129135

130-
idx := rand.Intn(len(pigCache))
131-
target := pigCache[idx]
136+
pigMutex.RLock()
137+
defer pigMutex.RUnlock()
132138

133-
b64Image, err := fetchImageLazy(target)
134-
if err != nil {
135-
ctx.SendChain(message.Text("[Pig] ERROR: ", err, "\nEXP: 图片加载失败"))
136-
return
137-
}
139+
// 直接使用 map 进行 O(1) 查找,抛弃 for 循环
140+
target, exists := pigMap[targetID]
141+
if !exists {
142+
ctx.SendChain(message.Text("[Pig] ERROR: 未找到 ID 为 ", targetID, " 的猪猪"))
143+
return
144+
}
145+
146+
imgData, err := target.fetch()
147+
if err != nil {
148+
ctx.SendChain(message.Text("[Pig] ERROR: ", err, "\nEXP: 图片加载失败"))
149+
return
150+
}
138151

139-
ctx.SendChain(
140-
message.Text(fmt.Sprintf("🐷 ID: %s | %s", target.ID, target.Title)),
141-
message.Image(b64Image),
142-
)
152+
ctx.SendChain(
153+
message.Text(fmt.Sprintf("🐷 ID: %s | %s", target.ID, target.Title)),
154+
message.ImageBytes(imgData), // 直接使用 ImageBytes
155+
)
156+
})
143157
}
144158

145-
// handleSearchPig 处理搜索猪猪
146-
func handleSearchPig(ctx *zero.Ctx) {
147-
keyword := ctx.State["regex_matched"].([]string)[1]
148-
keyword = strings.TrimSpace(keyword)
159+
// checkAndUpdateData
160+
func checkAndUpdateData() error {
161+
pigMutex.Lock()
162+
defer pigMutex.Unlock()
149163

150-
if err := checkAndUpdateData(ctx); err != nil {
151-
ctx.SendChain(message.Text("[Pig] ERROR: ", err, "\nEXP: 搜索猪猪失败,获取数据错误"))
152-
return
164+
// 如果有缓存且距上次更新不足 24 小时,直接返回
165+
if len(pigCache) > 0 && time.Since(lastUpdateTime) < 24*time.Hour {
166+
return nil
153167
}
154168

155-
pigMutex.RLock()
156-
defer pigMutex.RUnlock()
157-
158-
var results []PigImage
159-
for _, p := range pigCache {
160-
// 模糊匹配标题
161-
if strings.Contains(p.Title, keyword) {
162-
results = append(results, p)
163-
}
169+
dataBytes, err := engine.GetLazyData("pig_data.json", true)
170+
if err != nil {
171+
return errors.New("读取数据文件失败: " + err.Error())
164172
}
165173

166-
if len(results) == 0 {
167-
ctx.SendChain(message.Text("[Pig] ERROR: 未找到包含“", keyword, "”的猪猪"))
168-
return
174+
var data pigResponse
175+
if err := json.Unmarshal(dataBytes, &data); err != nil {
176+
return errors.New("解析JSON失败: " + err.Error())
169177
}
170178

171-
var sb strings.Builder
172-
sb.WriteString(fmt.Sprintf("🔎 根据关键词“%s”找到 %d 只猪猪:\n", keyword, len(results)))
173-
174-
maxShow := 10
175-
for i, p := range results {
176-
if i >= maxShow {
177-
sb.WriteString(fmt.Sprintf("\n...等共 %d 条结果", len(results)))
178-
break
179-
}
180-
sb.WriteString(fmt.Sprintf("%d: %s (ID: %s)\n", i+1, p.Title, p.ID))
179+
if len(data.Images) == 0 {
180+
return errors.New("数据文件为空")
181181
}
182182

183-
sb.WriteString("\n为您返回第一个猪猪:\n")
184-
sb.WriteString("💡 提示:输入“猪猪id [id]”可精确获取")
183+
pigCache = data.Images
185184

186-
b64Image, err := fetchImageLazy(results[0])
187-
if err != nil {
188-
ctx.SendChain(message.Text(sb.String(), "\n\n[Pig] ERROR: ", err, "\nEXP: 图片加载失败"))
189-
return
185+
// 更新缓存时,顺便重构一份查询 Map
186+
newMap := make(map[string]*pigImage, len(pigCache))
187+
for i := range pigCache {
188+
newMap[pigCache[i].ID] = &pigCache[i]
190189
}
190+
pigMap = newMap
191191

192-
ctx.SendChain(
193-
message.Text(sb.String()),
194-
message.Image(b64Image),
195-
)
192+
lastUpdateTime = time.Now()
193+
return nil
196194
}
197195

198-
// handlePigByID 处理ID精确查找
199-
func handlePigByID(ctx *zero.Ctx) {
200-
targetID := ctx.State["regex_matched"].([]string)[1]
201-
202-
if err := checkAndUpdateData(ctx); err != nil {
203-
ctx.SendChain(message.Text("[Pig] ERROR: ", err, "\nEXP: 精确查找失败,获取数据错误"))
204-
return
205-
}
206-
207-
pigMutex.RLock()
208-
defer pigMutex.RUnlock()
209-
210-
var target *PigImage
211-
for _, p := range pigCache {
212-
if p.ID == targetID {
213-
val := p
214-
target = &val
215-
break
216-
}
196+
// fetch 作为 pigImage 的专属方法,直接返回 []byte
197+
func (img *pigImage) fetch() ([]byte, error) {
198+
if img.Filename == "" {
199+
return nil, errors.New("图片数据异常,缺少文件名")
217200
}
218201

219-
if target == nil {
220-
ctx.SendChain(message.Text("[Pig] ERROR: 未找到 ID 为 ", targetID, " 的猪猪"))
221-
return
222-
}
202+
targetPath := filepath.Join("assets", img.Filename)
223203

224-
b64Image, err := fetchImageLazy(*target)
204+
// 使用 true,直接返回字节数组
205+
imgData, err := engine.GetLazyData(targetPath, true)
225206
if err != nil {
226-
ctx.SendChain(message.Text("[Pig] ERROR: ", err, "\nEXP: 图片加载失败"))
227-
return
207+
return nil, errors.New("图片资源缺失 (" + targetPath + "): " + err.Error())
228208
}
229209

230-
ctx.SendChain(
231-
message.Text(fmt.Sprintf("🐷 ID: %s | %s", target.ID, target.Title)),
232-
message.Image(b64Image),
233-
)
210+
return imgData, nil
234211
}

0 commit comments

Comments
 (0)