11// Package cybercat 云养猫
22package cybercat
3-
3+ import "errors"
44import (
55 "fmt"
66 "os"
77 "path"
88 "path/filepath"
99 "sync"
1010 "time"
11+ "strconv"
1112
1213 fcext "github.com/FloatTech/floatbox/ctxext"
1314 "github.com/FloatTech/floatbox/file"
@@ -50,6 +51,14 @@ type catdb struct {
5051 sql.Sqlite
5152}
5253
54+ type breedInfo struct {
55+ TypeName string
56+ Temperament string
57+ Description string
58+ ImageURL string
59+ Err error
60+ }
61+
5362type catInfo struct {
5463 User int64 // 主人
5564 Name string // 喵喵名称
@@ -65,44 +74,85 @@ type catInfo struct {
6574}
6675
6776func (inf * catInfo ) avatar (gid int64 ) string {
68- cache := path .Join (engine .DataFolder (), "cache" )
69- imgname := fmt .Sprintf ("%d_%d" , inf .User , gid )
70- imgfile := filepath .Join (cache , inf .Type + imgname + ".png" )
71- aimgfile := filepath .Join (file .BOTPATH , imgfile )
77+ // 构建缓存文件路径
78+ cache := path .Join (engine .DataFolder (), "cache" )
79+ imgname := fmt .Sprintf ("%d_%d" , inf .User , gid )
80+ imgfile := filepath .Join (cache , inf .Type + imgname + ".png" )
81+ aimgfile := filepath .Join (file .BOTPATH , imgfile )
7282
73- if _ , err := os .Stat (cache ); os .IsNotExist (err ) {
74- err := os .MkdirAll (cache , 0755 )
75- if err != nil {
76- fmt .Println ("Error creating cache directory:" , err )
77- return err .Error ()
78- }
79- }
80- if file .IsNotExist (aimgfile ) {
81- breed := inf .Type
82- data , err := web .GetData (apiURL + "search?has_breeds=" + breed )
83- if err != nil {
84- fmt .Println ("Error fetching avatar URL:" , err )
85- return err .Error () // 返回错误信息
86- }
87- imgurl := gjson .ParseBytes (data ).Get ("0.url" ).String ()
88- imgdata , err := web .GetData (imgurl )
89- if err != nil {
90- return "错误:未能解析图片URL"
91- }
92- var f * os.File
93- f , err = os .Create (aimgfile ) // 使用 aimgfile 作为文件路径
94- if err != nil {
95- fmt .Println ("Error creating file:" , err )
96- return err .Error () // 返回错误信息
97- }
98- defer f .Close ()
99- _ , err = f .Write (imgdata ) // 写入图片数据
100- if err != nil {
101- fmt .Println ("Error writing file:" , err )
102- return err .Error () // 返回错误信息
83+ // 确保缓存目录存在
84+ _ = os .MkdirAll (cache , 0755 )
85+
86+ // 1. 如果本地已有缓存文件,直接返回
87+ if file .IsExist (aimgfile ) {
88+ return "file:///" + aimgfile
89+ }
90+
91+ // 2. 优先使用已有的 Picurl(例如用户上传的猫娘图片)
92+ if inf .Picurl != "" {
93+ if err := downloadAndSave (inf .Picurl , aimgfile ); err == nil {
94+ return "file:///" + aimgfile
95+ }
96+ // 下载失败,继续尝试通过品种获取
97+ }
98+
99+ // 3. 通过品种ID获取图片
100+ breedID , ok := typeZH2Breeds [inf .Type ]
101+ if ok {
102+ // 构建请求URL(使用 breed_ids 参数)
103+ url := apiURL + "search?breed_ids=" + breedID + "&limit=1"
104+ // 添加 API 密钥
105+ if apiKey := getCatAPIKey (); apiKey != "" {
106+ url += "&api_key=" + apiKey
107+ }
108+
109+ data , err := web .GetData (url )
110+ if err == nil {
111+ imgURL := gjson .ParseBytes (data ).Get ("0.url" ).String ()
112+ if imgURL != "" {
113+ if err := downloadAndSave (imgURL , aimgfile ); err == nil {
114+ // 保存 URL 到数据库供下次使用
115+ inf .Picurl = imgURL
116+ go func () {
117+ gidStr := "group" + strconv .FormatInt (gid , 10 )
118+ _ = catdata .insert (gidStr , inf ) // 异步更新,忽略错误
119+ }()
120+ return "file:///" + aimgfile
121+ }
122+ }
123+ }
124+ }
125+
126+ // 4. 后备方案:随机猫图
127+ result := suineko ()
128+ if result .Err == nil && result .ImageURL != "" {
129+ if err := downloadAndSave (result .ImageURL , aimgfile ); err == nil {
130+ return "file:///" + aimgfile
103131 }
104132 }
105- return "file:///" + aimgfile // 返回文件协议的完整路径
133+
134+ // 5. 最终后备:透明PNG(避免报错)
135+ return "base64://iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mP8z8BQDwAEhQGAhKmMIQAAAABJRU5ErkJggg=="
136+ }
137+
138+ // downloadAndSave 下载图片并保存到本地
139+ func downloadAndSave (url , filepath string ) error {
140+ data , err := web .GetData (url )
141+ if err != nil {
142+ return err
143+ }
144+ f , err := os .Create (filepath )
145+ if err != nil {
146+ return err
147+ }
148+ defer f .Close ()
149+ _ , err = f .Write (data )
150+ return err
151+ }
152+
153+ // getCatAPIKey API密钥
154+ func getCatAPIKey () string {
155+ return "live_jjmlzq8FSiuFko40Utf43EfniNWYY1MtBaCej1Fy2YRDW9nMEmtgJwOMiZzs6fXc" // 替换为真实免费密钥
106156}
107157
108158var (
@@ -135,52 +185,67 @@ var (
135185)
136186
137187func init () {
138- engine .OnRegex (`^吸(.*猫)$` ).SetBlock (true ).Handle (func (ctx * zero.Ctx ) {
139- typeOfcat := ctx .State ["regex_matched" ].([]string )[1 ]
140- if typeOfcat == "猫" {
141- typeName , temperament , description , url , err := suineko ()
142- if err != nil {
143- ctx .SendChain (message .Text ("[ERROR]: " , err ))
144- return
145- }
146- ctx .SendChain (message .Image (url ), message .Text ("品种: " , typeName ,
147- "\n 气质:\n " , temperament , "\n 描述:\n " , description ))
148- return
149- }
150- breeds , ok := typeZH2Breeds [typeOfcat ]
151- if ! ok {
152- ctx .SendChain (message .Reply (ctx .Event .MessageID ), message .Text ("没有相关该品种的猫图" ))
153- return
154- }
155- picurl , err := getPicByBreed (breeds )
156- if err != nil {
157- ctx .SendChain (message .Text ("[ERROR]: " , err ))
158- return
159- }
160- ctx .SendChain (message .Text ("品种: " , typeOfcat ), message .Image (picurl ))
161- })
188+ engine .OnRegex (`^吸(.*猫)$` ).SetBlock (true ).Handle (func (ctx * zero.Ctx ) {
189+ typeOfcat := ctx .State ["regex_matched" ].([]string )[1 ]
190+ if typeOfcat == "猫" {
191+ result := suineko ()
192+ if result .Err != nil {
193+ ctx .SendChain (message .Text ("[ERROR]: " , result .Err ))
194+ return
195+ }
196+ ctx .SendChain (
197+ message .Image (result .ImageURL ),
198+ message .Text ("品种: " , result .TypeName ,
199+ "\n 气质:\n " , result .Temperament ,
200+ "\n 描述:\n " , result .Description ),
201+ )
202+ return
203+ }
204+ breeds , ok := typeZH2Breeds [typeOfcat ]
205+ if ! ok {
206+ ctx .SendChain (message .Reply (ctx .Event .MessageID ), message .Text ("没有相关该品种的猫图" ))
207+ return
208+ }
209+ picurl , err := getPicByBreed (breeds )
210+ if err != nil {
211+ ctx .SendChain (message .Text ("[ERROR]: " , err ))
212+ return
213+ }
214+ ctx .SendChain (message .Text ("品种: " , typeOfcat ), message .Image (picurl ))
215+ })
162216}
163217
164- func suineko () (typeName , temperament , description , url string , err error ) {
165- data , err := web .GetData (apiURL + "search?has_breeds=1" )
166- if err != nil {
167- return
168- }
169- picID := gjson .ParseBytes (data ).Get ("0.id" ).String ()
170- picdata , err := web .GetData (apiURL + picID )
171- if err != nil {
172- return
173- }
174- name := gjson .ParseBytes (picdata ).Get ("breeds.0.name" ).String ()
175- return typeEN2ZH [name ], gjson .ParseBytes (picdata ).Get ("breeds.0.temperament" ).String (), gjson .ParseBytes (picdata ).Get ("breeds.0.description" ).String (), gjson .ParseBytes (picdata ).Get ("url" ).String (), nil
218+
219+ func suineko () breedInfo {
220+ data , err := web .GetData (apiURL + "search?has_breeds=1" )
221+ if err != nil {
222+ return breedInfo {Err : err }
223+ }
224+ picID := gjson .ParseBytes (data ).Get ("0.id" ).String ()
225+ picdata , err := web .GetData (apiURL + picID )
226+ if err != nil {
227+ return breedInfo {Err : err }
228+ }
229+ name := gjson .ParseBytes (picdata ).Get ("breeds.0.name" ).String ()
230+ return breedInfo {
231+ TypeName : typeEN2ZH [name ],
232+ Temperament : gjson .ParseBytes (picdata ).Get ("breeds.0.temperament" ).String (),
233+ Description : gjson .ParseBytes (picdata ).Get ("breeds.0.description" ).String (),
234+ ImageURL : gjson .ParseBytes (picdata ).Get ("url" ).String (),
235+ Err : nil ,
236+ }
176237}
177238
178239func getPicByBreed (catBreed string ) (url string , err error ) {
179- data , err := web .GetData (apiURL + "search?breed_ids=" + catBreed )
180- if err != nil {
181- return
182- }
183- return gjson .ParseBytes (data ).Get ("0.url" ).String (), nil
240+ data , err := web .GetData (apiURL + "search?breed_ids=" + catBreed )
241+ if err != nil {
242+ return
243+ }
244+ url = gjson .ParseBytes (data ).Get ("0.url" ).String ()
245+ if url == "" {
246+ err = errors .New ("no image found" )
247+ }
248+ return
184249}
185250
186251func (sql * catdb ) insert (gid string , dbInfo * catInfo ) error {
0 commit comments