Compare commits

..

239 Commits

Author SHA1 Message Date
昔音幻离
62a57fa61d
chore: update gopsutil to v4 (#1244)
Some checks failed
自动更新 nix 依赖 / gomod2nix update (push) Has been cancelled
最新版 / Build binary CI (386, linux) (push) Has been cancelled
最新版 / Build binary CI (386, windows) (push) Has been cancelled
最新版 / Build binary CI (amd64, linux) (push) Has been cancelled
最新版 / Build binary CI (amd64, windows) (push) Has been cancelled
最新版 / Build binary CI (arm, linux) (push) Has been cancelled
最新版 / Build binary CI (arm64, linux) (push) Has been cancelled
PushLint / lint (push) Has been cancelled
2025-12-16 10:58:21 +08:00
himawari
0142050d6a
fix(aichat): gpt 修改提示词 (#1242)
Some checks failed
最新版 / Build binary CI (386, linux) (push) Has been cancelled
最新版 / Build binary CI (386, windows) (push) Has been cancelled
最新版 / Build binary CI (amd64, linux) (push) Has been cancelled
最新版 / Build binary CI (amd64, windows) (push) Has been cancelled
最新版 / Build binary CI (arm, linux) (push) Has been cancelled
最新版 / Build binary CI (arm64, linux) (push) Has been cancelled
PushLint / lint (push) Has been cancelled
2025-11-26 13:06:33 +08:00
源文雨
9cfad308b0 fix: boot panic on rsshub
Some checks failed
自动更新 nix 依赖 / gomod2nix update (push) Has been cancelled
最新版 / Build binary CI (386, linux) (push) Has been cancelled
最新版 / Build binary CI (386, windows) (push) Has been cancelled
最新版 / Build binary CI (amd64, linux) (push) Has been cancelled
最新版 / Build binary CI (amd64, windows) (push) Has been cancelled
最新版 / Build binary CI (arm, linux) (push) Has been cancelled
最新版 / Build binary CI (arm64, linux) (push) Has been cancelled
PushLint / lint (push) Has been cancelled
2025-11-24 17:51:56 +08:00
源文雨
28a8ee945c chore: update go.sum 2025-11-24 17:46:33 +08:00
github-actions[bot]
2154faec2c
chore: bump deps (#1240)
Some checks failed
最新版 / Build binary CI (386, linux) (push) Has been cancelled
最新版 / Build binary CI (386, windows) (push) Has been cancelled
最新版 / Build binary CI (amd64, linux) (push) Has been cancelled
最新版 / Build binary CI (amd64, windows) (push) Has been cancelled
最新版 / Build binary CI (arm, linux) (push) Has been cancelled
最新版 / Build binary CI (arm64, linux) (push) Has been cancelled
PushLint / lint (push) Has been cancelled
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2025-11-13 00:13:59 +08:00
莫思潋
577b3f3f30
chore: update deps (#1241)
Some checks failed
自动更新 nix 依赖 / gomod2nix update (push) Has been cancelled
最新版 / Build binary CI (386, linux) (push) Has been cancelled
最新版 / Build binary CI (386, windows) (push) Has been cancelled
最新版 / Build binary CI (amd64, linux) (push) Has been cancelled
最新版 / Build binary CI (amd64, windows) (push) Has been cancelled
最新版 / Build binary CI (arm, linux) (push) Has been cancelled
最新版 / Build binary CI (arm64, linux) (push) Has been cancelled
PushLint / lint (push) Has been cancelled
2025-11-09 17:57:01 +08:00
源文雨
99c9cc3de6 feat(aichat): add tests for storage
Some checks failed
自动更新 nix 依赖 / gomod2nix update (push) Has been cancelled
最新版 / Build binary CI (386, linux) (push) Has been cancelled
最新版 / Build binary CI (386, windows) (push) Has been cancelled
最新版 / Build binary CI (amd64, linux) (push) Has been cancelled
最新版 / Build binary CI (amd64, windows) (push) Has been cancelled
最新版 / Build binary CI (arm, linux) (push) Has been cancelled
最新版 / Build binary CI (arm64, linux) (push) Has been cancelled
PushLint / lint (push) Has been cancelled
2025-10-24 00:54:36 +08:00
源文雨
216d99c05d feat: drop unsafe -checklinkname 2025-10-24 00:28:47 +08:00
宇~
8101c02ca8
fix: 启动脚本 (#1236)
Some checks failed
最新版 / Build binary CI (386, linux) (push) Has been cancelled
最新版 / Build binary CI (386, windows) (push) Has been cancelled
最新版 / Build binary CI (amd64, linux) (push) Has been cancelled
最新版 / Build binary CI (amd64, windows) (push) Has been cancelled
最新版 / Build binary CI (arm, linux) (push) Has been cancelled
最新版 / Build binary CI (arm64, linux) (push) Has been cancelled
PushLint / lint (push) Has been cancelled
2025-10-08 19:41:16 +08:00
github-actions[bot]
da81875f8b
chore: bump deps (#1235)
Some checks failed
自动更新 nix 依赖 / gomod2nix update (push) Has been cancelled
最新版 / Build binary CI (386, linux) (push) Has been cancelled
最新版 / Build binary CI (386, windows) (push) Has been cancelled
最新版 / Build binary CI (amd64, linux) (push) Has been cancelled
最新版 / Build binary CI (amd64, windows) (push) Has been cancelled
最新版 / Build binary CI (arm, linux) (push) Has been cancelled
最新版 / Build binary CI (arm64, linux) (push) Has been cancelled
PushLint / lint (push) Has been cancelled
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2025-10-07 13:22:19 +08:00
github-actions[bot]
dd1a0ae478
chore: bump deps (#1234)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2025-10-07 12:39:39 +08:00
himawari
aafac8d6fa
feat: 添加 rsshub (#1232) 2025-10-07 12:39:16 +08:00
源文雨
a74bcb869a chore: update deps 2025-10-07 12:36:40 +08:00
github-actions[bot]
584882282f
chore: bump deps (#1233)
Some checks failed
最新版 / Build binary CI (386, linux) (push) Has been cancelled
最新版 / Build binary CI (386, windows) (push) Has been cancelled
最新版 / Build binary CI (amd64, linux) (push) Has been cancelled
最新版 / Build binary CI (amd64, windows) (push) Has been cancelled
最新版 / Build binary CI (arm, linux) (push) Has been cancelled
最新版 / Build binary CI (arm64, linux) (push) Has been cancelled
PushLint / lint (push) Has been cancelled
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2025-10-03 23:41:32 +08:00
源文雨
05345e4c13 fix(wordcount): jieba panic
Some checks failed
自动更新 nix 依赖 / gomod2nix update (push) Has been cancelled
最新版 / Build binary CI (386, linux) (push) Has been cancelled
最新版 / Build binary CI (386, windows) (push) Has been cancelled
最新版 / Build binary CI (amd64, linux) (push) Has been cancelled
最新版 / Build binary CI (amd64, windows) (push) Has been cancelled
最新版 / Build binary CI (arm, linux) (push) Has been cancelled
最新版 / Build binary CI (arm64, linux) (push) Has been cancelled
PushLint / lint (push) Has been cancelled
2025-10-02 16:42:59 +08:00
源文雨
b8a57b80f2 doc: add go generate main.go in README 2025-10-02 16:34:02 +08:00
源文雨
177fdbae68 optimize: drop gse in order to reduce mem cons. 2025-10-02 16:32:48 +08:00
Kajiekazz
fef48e405a
fix(dailynews): update to new API (#1230)
Some checks failed
自动更新 nix 依赖 / gomod2nix update (push) Has been cancelled
最新版 / Build binary CI (386, linux) (push) Has been cancelled
最新版 / Build binary CI (386, windows) (push) Has been cancelled
最新版 / Build binary CI (amd64, linux) (push) Has been cancelled
最新版 / Build binary CI (amd64, windows) (push) Has been cancelled
最新版 / Build binary CI (arm, linux) (push) Has been cancelled
最新版 / Build binary CI (arm64, linux) (push) Has been cancelled
PushLint / lint (push) Has been cancelled
2025-10-01 01:05:31 +08:00
源文雨
2d1603d65a fix: goreleaser 2025-10-01 01:04:28 +08:00
源文雨
73c7bbda1a doc: update compile instructions 2025-10-01 00:53:29 +08:00
源文雨
558c3ee482 fix: make lint happy 2025-10-01 00:40:15 +08:00
源文雨
bc20d95bd1 fix: make lint&ci happy 2025-10-01 00:35:55 +08:00
源文雨
883adc621c fix: make lint&ci happy 2025-10-01 00:33:19 +08:00
源文雨
7be1f2342b fix: make lint&ci happy 2025-10-01 00:30:24 +08:00
源文雨
4481ea4861 fix: make lint&ci happy 2025-10-01 00:25:44 +08:00
源文雨
4aace203cf fix: make lint&ci happy 2025-10-01 00:21:11 +08:00
源文雨
324a0022a7 fix: make lint&ci happy 2025-10-01 00:17:25 +08:00
源文雨
8825514fb2 fix: make lint&ci happy 2025-10-01 00:16:07 +08:00
github-actions[bot]
d2e6c9780f
chore: bump deps (#1231)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2025-10-01 00:03:48 +08:00
源文雨
06b8b518f8 fix: make lint&ci happy 2025-10-01 00:03:23 +08:00
源文雨
aa67d09ee1 feat(all): update to go1.24+ 2025-09-30 23:55:08 +08:00
github-actions[bot]
724e48f3e8
chore: bump deps (#1228)
Some checks failed
最新版 / Build binary CI (386, linux) (push) Waiting to run
最新版 / Build binary CI (386, windows) (push) Waiting to run
最新版 / Build binary CI (amd64, linux) (push) Waiting to run
最新版 / Build binary CI (amd64, windows) (push) Waiting to run
最新版 / Build binary CI (arm, linux) (push) Waiting to run
最新版 / Build binary CI (arm64, linux) (push) Waiting to run
PushLint / lint (push) Waiting to run
打包最新版为 Docker Image / build docker (push) Has been cancelled
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2025-09-30 17:07:07 +08:00
源文雨
57c95178d8 chore: make lint happy
Some checks failed
自动更新 nix 依赖 / gomod2nix update (push) Has been cancelled
打包最新版为 Docker Image / build docker (push) Has been cancelled
最新版 / Build binary CI (386, linux) (push) Has been cancelled
最新版 / Build binary CI (386, windows) (push) Has been cancelled
最新版 / Build binary CI (amd64, linux) (push) Has been cancelled
最新版 / Build binary CI (amd64, windows) (push) Has been cancelled
最新版 / Build binary CI (arm, linux) (push) Has been cancelled
最新版 / Build binary CI (arm64, linux) (push) Has been cancelled
PushLint / lint (push) Has been cancelled
2025-09-27 01:23:31 +08:00
源文雨
1e2d425cf4 optimize(bilibili): separate packages 2025-09-27 01:02:53 +08:00
源文雨
c22e4b543c chore: make lint happy
Some checks failed
自动更新 nix 依赖 / gomod2nix update (push) Has been cancelled
打包最新版为 Docker Image / build docker (push) Has been cancelled
最新版 / Build binary CI (386, linux) (push) Has been cancelled
最新版 / Build binary CI (386, windows) (push) Has been cancelled
最新版 / Build binary CI (amd64, linux) (push) Has been cancelled
最新版 / Build binary CI (amd64, windows) (push) Has been cancelled
最新版 / Build binary CI (arm, linux) (push) Has been cancelled
最新版 / Build binary CI (arm64, linux) (push) Has been cancelled
PushLint / lint (push) Has been cancelled
2025-09-27 00:21:02 +08:00
源文雨
4434a617ed fix(aichat): mis setting 2025-09-27 00:05:04 +08:00
源文雨
d6eb3ba28c feat(aichat): agent add more trigger conds 2025-09-26 23:57:59 +08:00
源文雨
d0f4d296d9 feat(aichat): fill missing print 2025-09-26 23:55:01 +08:00
源文雨
13aaaba8f3 fix(aichat): rate print error 2025-09-26 23:52:16 +08:00
源文雨
3302d8d295 feat(aichat): agent support fallback to normal chat 2025-09-26 23:50:44 +08:00
方柳煜
dd328afae8
fix: wifegame (#1225) 2025-09-26 23:11:10 +08:00
源文雨
d304cda7f0 fix(aichat): terminus
Some checks are pending
自动更新 nix 依赖 / gomod2nix update (push) Waiting to run
打包最新版为 Docker Image / build docker (push) Waiting to run
最新版 / Build binary CI (386, linux) (push) Waiting to run
最新版 / Build binary CI (386, windows) (push) Waiting to run
最新版 / Build binary CI (amd64, linux) (push) Waiting to run
最新版 / Build binary CI (amd64, windows) (push) Waiting to run
最新版 / Build binary CI (arm, linux) (push) Waiting to run
最新版 / Build binary CI (arm64, linux) (push) Waiting to run
PushLint / lint (push) Waiting to run
2025-09-26 01:37:23 +08:00
源文雨
109395a6af fix(aichat): terminus
Some checks are pending
自动更新 nix 依赖 / gomod2nix update (push) Waiting to run
打包最新版为 Docker Image / build docker (push) Waiting to run
最新版 / Build binary CI (386, linux) (push) Waiting to run
最新版 / Build binary CI (386, windows) (push) Waiting to run
最新版 / Build binary CI (amd64, linux) (push) Waiting to run
最新版 / Build binary CI (amd64, windows) (push) Waiting to run
最新版 / Build binary CI (arm, linux) (push) Waiting to run
最新版 / Build binary CI (arm64, linux) (push) Waiting to run
PushLint / lint (push) Waiting to run
2025-09-26 00:48:28 +08:00
源文雨
f7c0f4df98 feat(aichat): add terminus 2025-09-26 00:37:44 +08:00
源文雨
43cc90b724 feat(aichat): add handling resp 2025-09-26 00:13:04 +08:00
莫思潋
7bd1653cb5
feat(wife): 增加作品名&猜老婆 (#1208)
Some checks are pending
自动更新 nix 依赖 / gomod2nix update (push) Waiting to run
打包最新版为 Docker Image / build docker (push) Waiting to run
最新版 / Build binary CI (386, linux) (push) Waiting to run
最新版 / Build binary CI (386, windows) (push) Waiting to run
最新版 / Build binary CI (amd64, linux) (push) Waiting to run
最新版 / Build binary CI (amd64, windows) (push) Waiting to run
最新版 / Build binary CI (arm, linux) (push) Waiting to run
最新版 / Build binary CI (arm64, linux) (push) Waiting to run
PushLint / lint (push) Waiting to run
2025-09-24 16:38:33 +00:00
源文雨
6c4c6a3b8b chore: make lint happy 2025-09-25 00:36:01 +08:00
源文雨
72e0e796c4 chore: update deps 2025-09-25 00:23:29 +08:00
源文雨
3c41c18d27 feat(aichat): agent 支持识图 2025-09-25 00:20:07 +08:00
源文雨
7640f0cfac chore: update deps
Some checks are pending
自动更新 nix 依赖 / gomod2nix update (push) Waiting to run
打包最新版为 Docker Image / build docker (push) Waiting to run
最新版 / Build binary CI (386, linux) (push) Waiting to run
最新版 / Build binary CI (386, windows) (push) Waiting to run
最新版 / Build binary CI (amd64, linux) (push) Waiting to run
最新版 / Build binary CI (amd64, windows) (push) Waiting to run
最新版 / Build binary CI (arm, linux) (push) Waiting to run
最新版 / Build binary CI (arm64, linux) (push) Waiting to run
PushLint / lint (push) Waiting to run
2025-09-24 00:24:04 +08:00
源文雨
73eef961b5 fix(aichat): agent group send
Some checks are pending
自动更新 nix 依赖 / gomod2nix update (push) Waiting to run
打包最新版为 Docker Image / build docker (push) Waiting to run
最新版 / Build binary CI (386, linux) (push) Waiting to run
最新版 / Build binary CI (386, windows) (push) Waiting to run
最新版 / Build binary CI (amd64, linux) (push) Waiting to run
最新版 / Build binary CI (amd64, windows) (push) Waiting to run
最新版 / Build binary CI (arm, linux) (push) Waiting to run
最新版 / Build binary CI (arm64, linux) (push) Waiting to run
PushLint / lint (push) Waiting to run
2025-09-22 23:55:40 +08:00
源文雨
8e87be262c fix(aichat): agent group send 2025-09-22 23:06:46 +08:00
源文雨
8811df5968 feat(aichat): add perm check for cross-group
Some checks are pending
自动更新 nix 依赖 / gomod2nix update (push) Waiting to run
打包最新版为 Docker Image / build docker (push) Waiting to run
最新版 / Build binary CI (386, linux) (push) Waiting to run
最新版 / Build binary CI (386, windows) (push) Waiting to run
最新版 / Build binary CI (amd64, linux) (push) Waiting to run
最新版 / Build binary CI (amd64, windows) (push) Waiting to run
最新版 / Build binary CI (arm, linux) (push) Waiting to run
最新版 / Build binary CI (arm64, linux) (push) Waiting to run
PushLint / lint (push) Waiting to run
2025-09-22 22:51:04 +08:00
源文雨
f09f15937c chore: update deps 2025-09-22 22:46:18 +08:00
源文雨
49983ab451 feat(aichat): add agent mode
Some checks are pending
自动更新 nix 依赖 / gomod2nix update (push) Waiting to run
打包最新版为 Docker Image / build docker (push) Waiting to run
最新版 / Build binary CI (386, linux) (push) Waiting to run
最新版 / Build binary CI (386, windows) (push) Waiting to run
最新版 / Build binary CI (amd64, linux) (push) Waiting to run
最新版 / Build binary CI (amd64, windows) (push) Waiting to run
最新版 / Build binary CI (arm, linux) (push) Waiting to run
最新版 / Build binary CI (arm64, linux) (push) Waiting to run
PushLint / lint (push) Waiting to run
2025-09-21 23:18:39 +08:00
源文雨
4dd5189a3c chore: update deps
Some checks are pending
自动更新 nix 依赖 / gomod2nix update (push) Waiting to run
打包最新版为 Docker Image / build docker (push) Waiting to run
最新版 / Build binary CI (386, linux) (push) Waiting to run
最新版 / Build binary CI (386, windows) (push) Waiting to run
最新版 / Build binary CI (amd64, linux) (push) Waiting to run
最新版 / Build binary CI (amd64, windows) (push) Waiting to run
最新版 / Build binary CI (arm, linux) (push) Waiting to run
最新版 / Build binary CI (arm64, linux) (push) Waiting to run
PushLint / lint (push) Waiting to run
2025-09-21 15:25:54 +08:00
Kajiekazz
b9250ab82d
doc: update zb version tag (#1206)
Some checks failed
打包最新版为 Docker Image / build docker (push) Has been cancelled
最新版 / Build binary CI (386, linux) (push) Has been cancelled
最新版 / Build binary CI (386, windows) (push) Has been cancelled
最新版 / Build binary CI (amd64, linux) (push) Has been cancelled
最新版 / Build binary CI (amd64, windows) (push) Has been cancelled
最新版 / Build binary CI (arm, linux) (push) Has been cancelled
最新版 / Build binary CI (arm64, linux) (push) Has been cancelled
PushLint / lint (push) Has been cancelled
2025-09-16 21:49:34 +08:00
莫思潋
641495b579
chore: update wife data (#1207) 2025-09-16 21:44:15 +08:00
源文雨
e6a4dfcdf2 fix(aichat): adapt to 百炼
Some checks failed
自动更新 nix 依赖 / gomod2nix update (push) Has been cancelled
打包最新版为 Docker Image / build docker (push) Has been cancelled
最新版 / Build binary CI (386, linux) (push) Has been cancelled
最新版 / Build binary CI (386, windows) (push) Has been cancelled
最新版 / Build binary CI (amd64, linux) (push) Has been cancelled
最新版 / Build binary CI (amd64, windows) (push) Has been cancelled
最新版 / Build binary CI (arm, linux) (push) Has been cancelled
最新版 / Build binary CI (arm64, linux) (push) Has been cancelled
PushLint / lint (push) Has been cancelled
2025-09-10 22:51:44 +08:00
源文雨
6b505d050a 🔖 v1.9.9
Some checks are pending
自动更新 nix 依赖 / gomod2nix update (push) Waiting to run
打包最新版为 Docker Image / build docker (push) Waiting to run
最新版 / Build binary CI (386, linux) (push) Waiting to run
最新版 / Build binary CI (386, windows) (push) Waiting to run
最新版 / Build binary CI (amd64, linux) (push) Waiting to run
最新版 / Build binary CI (amd64, windows) (push) Waiting to run
最新版 / Build binary CI (arm, linux) (push) Waiting to run
最新版 / Build binary CI (arm64, linux) (push) Waiting to run
PushLint / lint (push) Waiting to run
2025-09-10 10:41:07 +08:00
github-actions[bot]
fc9a21d2d1
Changes by create-pull-request action (#1199)
* chore: bump deps

* Change Go version in go.mod

Updated Go version from 1.23.0 to 1.20 and removed toolchain specification.

---------

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: 源文雨 <41315874+fumiama@users.noreply.github.com>
2025-09-10 10:39:51 +08:00
github-actions[bot]
e84e44476a
Changes by create-pull-request action (#1198)
* chore: bump deps

* Change Go version in go.mod

---------

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: 源文雨 <41315874+fumiama@users.noreply.github.com>
2025-09-10 10:39:15 +08:00
宇~
b012df4c23
feat(niuniu): 添加可自定义购买商品数量,调整商品单价 (#1189) 2025-09-10 10:33:21 +08:00
源文雨
08e02ab730 fix(aichat): adapt to 百炼 2025-09-10 10:32:30 +08:00
himawari
fb090839d6
feat(crypter): 添加语音 (#1197)
Some checks failed
打包最新版为 Docker Image / build docker (push) Has been cancelled
最新版 / Build binary CI (386, linux) (push) Has been cancelled
最新版 / Build binary CI (386, windows) (push) Has been cancelled
最新版 / Build binary CI (amd64, linux) (push) Has been cancelled
最新版 / Build binary CI (amd64, windows) (push) Has been cancelled
最新版 / Build binary CI (arm, linux) (push) Has been cancelled
最新版 / Build binary CI (arm64, linux) (push) Has been cancelled
PushLint / lint (push) Has been cancelled
2025-09-03 17:07:09 +08:00
莫思潋
ac2d53352c
feat(bilibiliparse): B站视频解析 视频上传开关 (#1196)
Some checks are pending
打包最新版为 Docker Image / build docker (push) Waiting to run
最新版 / Build binary CI (386, linux) (push) Waiting to run
最新版 / Build binary CI (386, windows) (push) Waiting to run
最新版 / Build binary CI (amd64, linux) (push) Waiting to run
最新版 / Build binary CI (amd64, windows) (push) Waiting to run
最新版 / Build binary CI (arm, linux) (push) Waiting to run
最新版 / Build binary CI (arm64, linux) (push) Waiting to run
PushLint / lint (push) Waiting to run
* add control for getVideoDownload

* fix: typo
2025-09-03 13:02:12 +08:00
github-actions[bot]
35292a69fc
chore(lint): 改进代码样式 (#1192)
Some checks are pending
打包最新版为 Docker Image / build docker (push) Waiting to run
最新版 / Build binary CI (386, linux) (push) Waiting to run
最新版 / Build binary CI (386, windows) (push) Waiting to run
最新版 / Build binary CI (amd64, linux) (push) Waiting to run
最新版 / Build binary CI (amd64, windows) (push) Waiting to run
最新版 / Build binary CI (arm, linux) (push) Waiting to run
最新版 / Build binary CI (arm64, linux) (push) Waiting to run
PushLint / lint (push) Waiting to run
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2025-09-02 13:13:22 +08:00
Kajiekazz
cd16a755d7
doc: update README.md (#1193) 2025-09-02 13:12:13 +08:00
Kajiekazz
1e7b2d3335
feat: 添加插件 crypter (#1191)
Some checks are pending
打包最新版为 Docker Image / build docker (push) Waiting to run
最新版 / Build binary CI (386, linux) (push) Waiting to run
最新版 / Build binary CI (386, windows) (push) Waiting to run
最新版 / Build binary CI (amd64, linux) (push) Waiting to run
最新版 / Build binary CI (amd64, windows) (push) Waiting to run
最新版 / Build binary CI (arm, linux) (push) Waiting to run
最新版 / Build binary CI (arm64, linux) (push) Waiting to run
PushLint / lint (push) Waiting to run
2025-09-01 22:42:42 +08:00
himawari
20d49ccf15
feat(aichat): 添加/gpt命令,直接聊天 (#1190)
*  添加大模型聊天,便于使用版本的

* 🐛 减少重复关键词

* 🎨 优化换行符

*  添加转换函数

*  添加lint优化

* 🎨 按字符切片

* 🎨 修改lint
2025-09-01 22:38:15 +08:00
github-actions[bot]
1f66f47ce6
chore: bump deps (#1188)
Some checks failed
打包最新版为 Docker Image / build docker (push) Has been cancelled
最新版 / Build binary CI (386, linux) (push) Has been cancelled
最新版 / Build binary CI (386, windows) (push) Has been cancelled
最新版 / Build binary CI (amd64, linux) (push) Has been cancelled
最新版 / Build binary CI (amd64, windows) (push) Has been cancelled
最新版 / Build binary CI (arm, linux) (push) Has been cancelled
最新版 / Build binary CI (arm64, linux) (push) Has been cancelled
PushLint / lint (push) Has been cancelled
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2025-08-25 11:08:05 +08:00
himawari
34f3b9ba2a
feat(aiimage&aichat): add new plugin & summary of group chat (#1187)
Some checks failed
自动更新 nix 依赖 / gomod2nix update (push) Has been cancelled
打包最新版为 Docker Image / build docker (push) Has been cancelled
最新版 / Build binary CI (386, linux) (push) Has been cancelled
最新版 / Build binary CI (386, windows) (push) Has been cancelled
最新版 / Build binary CI (amd64, linux) (push) Has been cancelled
最新版 / Build binary CI (amd64, windows) (push) Has been cancelled
最新版 / Build binary CI (arm, linux) (push) Has been cancelled
最新版 / Build binary CI (arm64, linux) (push) Has been cancelled
PushLint / lint (push) Has been cancelled
2025-08-22 22:37:35 +08:00
Dodoj
2fa7868838
fix(gif): branch名称导致的404问题 (#1186)
Some checks failed
打包最新版为 Docker Image / build docker (push) Has been cancelled
最新版 / Build binary CI (386, linux) (push) Has been cancelled
最新版 / Build binary CI (386, windows) (push) Has been cancelled
最新版 / Build binary CI (amd64, linux) (push) Has been cancelled
最新版 / Build binary CI (amd64, windows) (push) Has been cancelled
最新版 / Build binary CI (arm, linux) (push) Has been cancelled
最新版 / Build binary CI (arm64, linux) (push) Has been cancelled
PushLint / lint (push) Has been cancelled
2025-08-12 00:18:32 +08:00
Dodoj
b6ddda1d51
fix(kfccrazythursday): API解析 (#1184)
Some checks failed
打包最新版为 Docker Image / build docker (push) Has been cancelled
最新版 / Build binary CI (386, linux) (push) Has been cancelled
最新版 / Build binary CI (386, windows) (push) Has been cancelled
最新版 / Build binary CI (amd64, linux) (push) Has been cancelled
最新版 / Build binary CI (amd64, windows) (push) Has been cancelled
最新版 / Build binary CI (arm, linux) (push) Has been cancelled
最新版 / Build binary CI (arm64, linux) (push) Has been cancelled
PushLint / lint (push) Has been cancelled
2025-07-25 10:31:33 +09:00
himawari
a1621f34a0
optimize(antiabuse): 添加违禁词解释 (#1183)
Some checks failed
打包最新版为 Docker Image / build docker (push) Has been cancelled
最新版 / Build binary CI (386, linux) (push) Has been cancelled
最新版 / Build binary CI (386, windows) (push) Has been cancelled
最新版 / Build binary CI (amd64, linux) (push) Has been cancelled
最新版 / Build binary CI (amd64, windows) (push) Has been cancelled
最新版 / Build binary CI (arm, linux) (push) Has been cancelled
最新版 / Build binary CI (arm64, linux) (push) Has been cancelled
PushLint / lint (push) Has been cancelled
2025-07-23 21:24:00 +09:00
github-actions[bot]
21aa3bc49f
chore: bump deps (#1182)
Some checks failed
自动更新 nix 依赖 / gomod2nix update (push) Has been cancelled
打包最新版为 Docker Image / build docker (push) Has been cancelled
最新版 / Build binary CI (386, linux) (push) Has been cancelled
最新版 / Build binary CI (386, windows) (push) Has been cancelled
最新版 / Build binary CI (amd64, linux) (push) Has been cancelled
最新版 / Build binary CI (amd64, windows) (push) Has been cancelled
最新版 / Build binary CI (arm, linux) (push) Has been cancelled
最新版 / Build binary CI (arm64, linux) (push) Has been cancelled
PushLint / lint (push) Has been cancelled
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2025-07-20 14:33:29 +09:00
himawari
617d4f50a4
feat: airecord (#1180)
Co-authored-by: 源文雨 <41315874+fumiama@users.noreply.github.com>
2025-07-20 14:31:06 +09:00
himawari
cb0ffa0c17
feat(music): 龙珠聚合搜索 (#1179)
Some checks failed
打包最新版为 Docker Image / build docker (push) Has been cancelled
最新版 / Build binary CI (386, linux) (push) Has been cancelled
最新版 / Build binary CI (386, windows) (push) Has been cancelled
最新版 / Build binary CI (amd64, linux) (push) Has been cancelled
最新版 / Build binary CI (amd64, windows) (push) Has been cancelled
最新版 / Build binary CI (arm, linux) (push) Has been cancelled
最新版 / Build binary CI (arm64, linux) (push) Has been cancelled
PushLint / lint (push) Has been cancelled
* 🐛 修改听歌问题

*  添加龙珠聚合搜索

* 🎨 优化聚合搜索

* 🐛 一定能点出歌

* 🎨 删除调试
2025-07-05 18:05:28 +09:00
宇~
0615993297
fix: 修复注销牛牛无法进行累加收费的问题&&优化代码 (#1178)
Some checks failed
打包最新版为 Docker Image / build docker (push) Has been cancelled
最新版 / Build binary CI (386, linux) (push) Has been cancelled
最新版 / Build binary CI (386, windows) (push) Has been cancelled
最新版 / Build binary CI (amd64, linux) (push) Has been cancelled
最新版 / Build binary CI (amd64, windows) (push) Has been cancelled
最新版 / Build binary CI (arm, linux) (push) Has been cancelled
最新版 / Build binary CI (arm64, linux) (push) Has been cancelled
PushLint / lint (push) Has been cancelled
* fix:修复注销牛牛无法进行累加收费的问题

* 修改牛牛商店的循环条件为商品变量的长度
2025-07-02 14:49:59 +09:00
Rinai
1c0d91424a
fix: 修复 niuniu 插件 bug,修改标点,添加部分注释 (#1177)
Some checks failed
打包最新版为 Docker Image / build docker (push) Has been cancelled
最新版 / Build binary CI (386, linux) (push) Has been cancelled
最新版 / Build binary CI (386, windows) (push) Has been cancelled
最新版 / Build binary CI (amd64, linux) (push) Has been cancelled
最新版 / Build binary CI (amd64, windows) (push) Has been cancelled
最新版 / Build binary CI (arm, linux) (push) Has been cancelled
最新版 / Build binary CI (arm64, linux) (push) Has been cancelled
PushLint / lint (push) Has been cancelled
2025-06-27 17:49:50 +09:00
github-actions[bot]
c94ee365ce
chore: bump deps (#1175)
Some checks failed
打包最新版为 Docker Image / build docker (push) Has been cancelled
最新版 / Build binary CI (386, linux) (push) Has been cancelled
最新版 / Build binary CI (386, windows) (push) Has been cancelled
最新版 / Build binary CI (amd64, linux) (push) Has been cancelled
最新版 / Build binary CI (amd64, windows) (push) Has been cancelled
最新版 / Build binary CI (arm, linux) (push) Has been cancelled
最新版 / Build binary CI (arm64, linux) (push) Has been cancelled
PushLint / lint (push) Has been cancelled
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2025-06-15 17:04:45 +09:00
github-actions[bot]
19e5e6636f
chore: bump deps (#1173)
Some checks failed
打包最新版为 Docker Image / build docker (push) Waiting to run
最新版 / Build binary CI (386, linux) (push) Waiting to run
最新版 / Build binary CI (386, windows) (push) Waiting to run
最新版 / Build binary CI (amd64, linux) (push) Waiting to run
最新版 / Build binary CI (amd64, windows) (push) Waiting to run
最新版 / Build binary CI (arm, linux) (push) Waiting to run
最新版 / Build binary CI (arm64, linux) (push) Waiting to run
PushLint / lint (push) Waiting to run
自动更新 nix 依赖 / gomod2nix update (push) Has been cancelled
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2025-06-15 02:03:33 +09:00
github-actions[bot]
cac3a4be81
chore(lint): 改进代码样式 (#1174)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2025-06-15 02:03:23 +09:00
源文雨
beada7f4da chore: update deps 2025-06-15 02:01:23 +09:00
github-actions[bot]
43b45ce6c5
chore: bump deps (#1172)
Some checks failed
自动更新 nix 依赖 / gomod2nix update (push) Failing after 1s
最新版 / Build binary CI (386, linux) (push) Failing after 1s
最新版 / Build binary CI (386, windows) (push) Failing after 1s
最新版 / Build binary CI (amd64, linux) (push) Failing after 1s
最新版 / Build binary CI (amd64, windows) (push) Failing after 1s
最新版 / Build binary CI (arm, linux) (push) Failing after 1s
最新版 / Build binary CI (arm64, linux) (push) Failing after 1s
PushLint / lint (push) Failing after 1s
打包最新版为 Docker Image / build docker (push) Has been cancelled
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2025-06-01 20:32:24 +09:00
源文雨
95dd5e6b94 chore: update deps 2025-06-01 20:30:46 +09:00
源文雨
566f6ecfd5 🔖 v1.9.8 2025-06-01 18:53:06 +09:00
github-actions[bot]
5b28ad75b7
chore: bump deps (#1171)
Some checks failed
打包最新版为 Docker Image / build docker (push) Waiting to run
自动更新 nix 依赖 / gomod2nix update (push) Failing after 1s
最新版 / Build binary CI (386, linux) (push) Failing after 1s
最新版 / Build binary CI (386, windows) (push) Failing after 1s
最新版 / Build binary CI (amd64, linux) (push) Failing after 1s
最新版 / Build binary CI (amd64, windows) (push) Failing after 1s
最新版 / Build binary CI (arm, linux) (push) Failing after 1s
最新版 / Build binary CI (arm64, linux) (push) Failing after 1s
PushLint / lint (push) Failing after 1s
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2025-06-01 17:58:46 +09:00
源文雨
997857a558 chore: update deps 2025-06-01 17:56:37 +09:00
github-actions[bot]
4269057283
chore: bump deps (#1170)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2025-06-01 16:27:17 +09:00
源文雨
f70cab80c2 feat(aichat): add more configs 2025-06-01 15:49:03 +09:00
源文雨
609d819610 chore: sync data 2025-06-01 15:06:20 +09:00
源文雨
961fbb098e 🔖 v1.9.7
Some checks failed
最新版 / Build binary CI (386, linux) (push) Failing after 1s
最新版 / Build binary CI (386, windows) (push) Failing after 1s
最新版 / Build binary CI (amd64, linux) (push) Failing after 1s
最新版 / Build binary CI (amd64, windows) (push) Failing after 1s
最新版 / Build binary CI (arm, linux) (push) Failing after 1s
最新版 / Build binary CI (arm64, linux) (push) Failing after 1s
PushLint / lint (push) Failing after 1s
打包最新版为 Docker Image / build docker (push) Has been cancelled
2025-05-14 21:54:07 +09:00
github-actions[bot]
42fe124b09
chore(lint): 改进代码样式 (#1167)
Some checks failed
打包最新版为 Docker Image / build docker (push) Waiting to run
最新版 / Build binary CI (386, linux) (push) Failing after 1s
最新版 / Build binary CI (386, windows) (push) Failing after 1s
最新版 / Build binary CI (amd64, linux) (push) Failing after 1s
最新版 / Build binary CI (amd64, windows) (push) Failing after 1s
最新版 / Build binary CI (arm, linux) (push) Failing after 1s
最新版 / Build binary CI (arm64, linux) (push) Failing after 1s
PushLint / lint (push) Failing after 1s
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2025-05-14 15:42:18 +09:00
Dodoj
076b113455
fix(wordcount): 修改分词模块至外部gse仓库 (#1165)
Some checks failed
打包最新版为 Docker Image / build docker (push) Waiting to run
最新版 / Build binary CI (386, linux) (push) Failing after 1s
最新版 / Build binary CI (386, windows) (push) Failing after 1s
最新版 / Build binary CI (amd64, linux) (push) Failing after 1s
最新版 / Build binary CI (amd64, windows) (push) Failing after 1s
最新版 / Build binary CI (arm, linux) (push) Failing after 1s
最新版 / Build binary CI (arm64, linux) (push) Failing after 1s
PushLint / lint (push) Failing after 1s
Co-authored-by: 源文雨 <41315874+fumiama@users.noreply.github.com>
2025-05-13 12:05:24 +00:00
github-actions[bot]
c888936489
chore: bump deps (#1166)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2025-05-13 20:58:41 +09:00
源文雨
e1d2dee881 feat: replace jieba with gse
Some checks failed
打包最新版为 Docker Image / build docker (push) Waiting to run
自动更新 nix 依赖 / gomod2nix update (push) Failing after 1s
最新版 / Build binary CI (386, linux) (push) Failing after 1s
最新版 / Build binary CI (386, windows) (push) Failing after 1s
最新版 / Build binary CI (amd64, linux) (push) Failing after 1s
最新版 / Build binary CI (amd64, windows) (push) Failing after 1s
最新版 / Build binary CI (arm, linux) (push) Failing after 1s
最新版 / Build binary CI (arm64, linux) (push) Failing after 1s
PushLint / lint (push) Failing after 1s
2025-05-13 20:19:34 +09:00
Doordoorjay
39e1f56955
fix: 疯狂星期四 API (#1161)
Some checks failed
最新版 / Build binary CI (386, linux) (push) Failing after 1s
最新版 / Build binary CI (386, windows) (push) Failing after 1s
最新版 / Build binary CI (amd64, linux) (push) Failing after 1s
最新版 / Build binary CI (amd64, windows) (push) Failing after 1s
最新版 / Build binary CI (arm, linux) (push) Failing after 1s
最新版 / Build binary CI (arm64, linux) (push) Failing after 1s
PushLint / lint (push) Failing after 1s
打包最新版为 Docker Image / build docker (push) Has been cancelled
2025-05-06 18:12:41 +09:00
Nobody6825
4151464bdc
chore: use new nixpkgs with overlay which bring back go_1_20 instead of using old nixpkgs (#1162) 2025-05-06 18:11:46 +09:00
github-actions[bot]
0b89312d9d
chore(lint): 改进代码样式 (#1159)
Some checks failed
自动更新 nix 依赖 / gomod2nix update (push) Failing after 1s
最新版 / Build binary CI (386, linux) (push) Failing after 1s
最新版 / Build binary CI (386, windows) (push) Failing after 1s
最新版 / Build binary CI (amd64, linux) (push) Failing after 1s
最新版 / Build binary CI (amd64, windows) (push) Failing after 1s
最新版 / Build binary CI (arm, linux) (push) Failing after 1s
最新版 / Build binary CI (arm64, linux) (push) Failing after 1s
PushLint / lint (push) Failing after 1s
打包最新版为 Docker Image / build docker (push) Has been cancelled
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2025-04-23 18:36:41 +09:00
github-actions[bot]
2c607dedee
chore: bump deps (#1158)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2025-04-23 18:36:20 +09:00
方柳煜
30e9d04f74
feat: 电影查询 (#1155) 2025-04-23 18:35:34 +09:00
himawari
4b90a0659b
feat(bilibili): 添加视频下载 (#1157) 2025-04-23 18:33:15 +09:00
github-actions[bot]
109b7661b7
chore(lint): 改进代码样式 (#1151)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2025-03-30 23:50:57 +09:00
github-actions[bot]
8da52a2772
chore: bump deps (#1150)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: 源文雨 <41315874+fumiama@users.noreply.github.com>
2025-03-30 23:50:36 +09:00
yexiaoyu
7515983b55
插件:AnimeTrace 动画/Galgame识别 (#1141)
* 插件:AnimeTrace 动画/Galgame识别

* update: 插件:AnimeTrace 动画/Galgame识别
2025-03-30 23:48:49 +09:00
github-actions[bot]
7519ea548d
chore: bump deps (#1149)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2025-03-30 23:47:42 +09:00
源文雨
6a55d9b279 🔖 v1.9.6 2025-03-30 23:47:12 +09:00
源文雨
d5227f1159 optimize(aichat): use config struct 2025-03-30 23:28:55 +09:00
源文雨
62e9fe69ed feat(aichat): add more funcs 2025-03-30 22:12:48 +09:00
源文雨
2df52161e5 feat(aichat): add OLLaMA & GenAI support 2025-03-30 21:36:31 +09:00
源文雨
a29f4cb1f9 feat(aichat): add sanitize 2025-03-29 17:07:14 +09:00
github-actions[bot]
6a747d2f9d
chore(lint): 改进代码样式 (#1144)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2025-03-25 12:41:42 +09:00
源文雨
164c71a3a3
chore: remove dependabot 2025-03-25 12:35:46 +09:00
github-actions[bot]
3f1b0ad67b
chore: bump deps (#1142)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2025-03-25 12:33:20 +09:00
github-actions[bot]
6c6699a5d6
chore(lint): 改进代码样式 (#1143)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2025-03-25 12:33:02 +09:00
RefactoringHero
e292b69ee5
feat: add plugin Minecraft 服务器状态查询 (#1135) 2025-03-25 12:30:40 +09:00
Nobody6825
e6e6dd4565
chore: pin nixpkgs to preserve dropped go_1_20 (#1139) 2025-03-08 19:33:09 +09:00
github-actions[bot]
28bfc3e71d
chore: bump deps (#1133)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2025-02-22 22:05:07 +09:00
源文雨
d3975cf461 chore: update deps 2025-02-22 21:52:43 +09:00
源文雨
7430c41c1e fix(ci): deprecations 2025-02-22 15:42:03 +09:00
源文雨
dd1064db26 🔖 v1.9.5 2025-02-22 15:34:55 +09:00
github-actions[bot]
1167866b16
chore: bump deps (#1131)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2025-02-22 15:23:56 +09:00
github-actions[bot]
caed8eab0c
chore(lint): 改进代码样式 (#1128)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2025-02-22 15:23:44 +09:00
dependabot[bot]
8d85334049
chore(deps): bump golang.org/x/image from 0.21.0 to 0.24.0 (#1109)
Bumps [golang.org/x/image](https://github.com/golang/image) from 0.21.0 to 0.24.0.
- [Commits](https://github.com/golang/image/compare/v0.21.0...v0.24.0)

---
updated-dependencies:
- dependency-name: golang.org/x/image
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-02-22 06:21:13 +00:00
dependabot[bot]
da89fb29ca
chore(deps): bump golang.org/x/sys from 0.29.0 to 0.30.0 (#1111)
Bumps [golang.org/x/sys](https://github.com/golang/sys) from 0.29.0 to 0.30.0.
- [Commits](https://github.com/golang/sys/compare/v0.29.0...v0.30.0)

---
updated-dependencies:
- dependency-name: golang.org/x/sys
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-02-22 15:18:17 +09:00
源文雨
e005621fbb doc: edit README 2025-02-22 15:11:59 +09:00
源文雨
511f04c6f9 feat(aichat): new template 2025-02-22 15:10:40 +09:00
github-actions[bot]
fb29619b9e
chore: bump deps (#1125)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2025-02-22 13:13:36 +09:00
源文雨
325adf8911 feat(aichat): more compatibility 2025-02-22 01:50:19 +09:00
github-actions[bot]
b494390373
chore(lint): 改进代码样式 (#1124)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2025-02-20 02:01:45 +09:00
方柳煜
085e95cd48
optimize(mcfish): 更改插件规则 (#1122)
每日商店会固定刷新1初始木竿。该木竿价格也会随木竿的价格浮动。
2025-02-20 01:46:35 +09:00
github-actions[bot]
659f4e07c2
chore: bump deps (#1123)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2025-02-18 23:30:13 +09:00
源文雨
3abf6317de feat(aichat): add user name 2025-02-18 23:26:36 +09:00
源文雨
e8305c5886 optimize(thesaurus): cnfd. calc. 2025-02-17 23:45:16 +09:00
源文雨
71d029f7a0 fix(aichat): block chat at at 2025-02-17 23:39:41 +09:00
源文雨
dc899f55fe fix(aichat): load noreplyat 2025-02-17 23:38:21 +09:00
源文雨
8c11f48502 fix(thesaurus): cnfd. calc. 2025-02-17 23:31:11 +09:00
源文雨
e12ec697e6 chore: make lint happy 2025-02-17 23:20:16 +09:00
github-actions[bot]
b01d3a4f14
chore: bump deps (#1121)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2025-02-17 23:17:56 +09:00
源文雨
552c1a9a35 optimize(chat): avoid conflicts 2025-02-17 23:12:23 +09:00
源文雨
aacf720b88 🔖 v1.9.4 2025-02-15 18:39:55 +09:00
github-actions[bot]
832addc55e
chore: bump deps (#1119)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2025-02-15 18:38:54 +09:00
源文雨
f7c5584016 fix(aichat): context 2025-02-15 18:36:25 +09:00
github-actions[bot]
df5ceb930f
chore: bump deps (#1118)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2025-02-15 18:21:32 +09:00
源文雨
d4071f54f9 optimize(aichat): context 2025-02-15 18:19:38 +09:00
源文雨
ae859a1ece 🔖 v1.9.3 2025-02-15 12:51:58 +09:00
源文雨
112328a7f2 fix(aichat): some errors 2025-02-15 12:38:43 +09:00
源文雨
512561d8fd feat: add set sep 2025-02-15 02:06:25 +09:00
源文雨
f73bf5a270 feat(aichat): add temp setting 2025-02-15 02:02:53 +09:00
源文雨
3c7034e46c 🔖 v1.9.2 2025-02-14 23:10:52 +09:00
源文雨
2a848366f3 optimize(aichat): more replys 2025-02-14 22:48:47 +09:00
github-actions[bot]
549a8ce30b
chore: bump deps (#1117)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2025-02-14 22:34:11 +09:00
源文雨
d00c4aec47 fix(sqlite): slice query 2025-02-14 22:31:25 +09:00
github-actions[bot]
4ef5b854bc
chore: bump deps (#1114)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2025-02-14 21:43:30 +09:00
github-actions[bot]
41f02f34c2
chore(lint): 改进代码样式 (#1116)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2025-02-14 21:43:06 +09:00
源文雨
b9cf52404d fix(lint): make lint happy 2025-02-14 21:30:48 +09:00
源文雨
350e2e3907 chore: make lint happy 2025-02-14 17:30:05 +09:00
源文雨
a8b2587cee update deps 2025-02-14 17:27:10 +09:00
源文雨
735c3cdde1 feat: add plugin aichat 2025-02-14 17:23:00 +09:00
源文雨
24955dc4a7 fix(thesaurus): ingore empty seg 2025-02-01 16:13:43 +08:00
github-actions[bot]
b4eb61e36a
chore(lint): 改进代码样式 (#1108)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2025-01-20 21:17:23 +09:00
宇~
70ebb03434
feat:使用niu包,添加新玩法牛牛拍卖行 (#1098) 2025-01-20 15:45:02 +09:00
源文雨
2c5596cd96 🔖 v1.9.1 (fix #1076) 2025-01-16 17:32:05 +09:00
github-actions[bot]
d6c13337d1
chore(lint): 改进代码样式 (#1106)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2025-01-16 16:29:35 +09:00
github-actions[bot]
eb2daf1827
chore: bump deps (#1105)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2025-01-16 16:29:04 +09:00
dependabot[bot]
c4d4bd9f40
chore(deps): bump github.com/antchfx/htmlquery from 1.3.3 to 1.3.4 (#1104)
Bumps [github.com/antchfx/htmlquery](https://github.com/antchfx/htmlquery) from 1.3.3 to 1.3.4.
- [Release notes](https://github.com/antchfx/htmlquery/releases)
- [Commits](https://github.com/antchfx/htmlquery/compare/v1.3.3...v1.3.4)

---
updated-dependencies:
- dependency-name: github.com/antchfx/htmlquery
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-01-16 16:26:56 +09:00
github-actions[bot]
fd1c8bf9b9
chore(lint): 改进代码样式 (#1101)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2025-01-14 17:40:12 +09:00
github-actions[bot]
7fef4b08c0
chore: bump deps (#1102)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2025-01-14 17:39:57 +09:00
dependabot[bot]
b04c244cab
chore(deps): bump golang.org/x/sys from 0.26.0 to 0.29.0 (#1095)
Bumps [golang.org/x/sys](https://github.com/golang/sys) from 0.26.0 to 0.29.0.
- [Commits](https://github.com/golang/sys/compare/v0.26.0...v0.29.0)

---
updated-dependencies:
- dependency-name: golang.org/x/sys
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-01-14 17:37:17 +09:00
vatebur
cfb98ca241
更新bilibili API和 钓鱼插件 (#1100) 2025-01-14 17:34:39 +09:00
莫思潋
885544077b
fix(tarot): 使用Getlazydata获取数据 (#1097)
* fix(tarot): use GetLazyData

* fix(tarot): add cache folder & improve error handle

* refactor(tarot): no need for clean cache
2025-01-07 22:02:49 +09:00
starim00
fcb01c2c18
fix: 处理b站解析获取卡片失败的问题 (#1089)
Co-authored-by: hutiance <hutiance@newings.net.cn>
2025-01-06 14:10:20 +09:00
catboss
41d28b71bf
fix: 塔罗牌Gitcode数据源仓库切换到经由镜像的GitHub源 (#1090) 2025-01-06 14:08:34 +09:00
vatebur
8fa91a13c3
fix:[mcfish]User purchases fish pole initialization (#1088)
修复用户第一次购买鱼竿的时候初始化错误
2025-01-03 22:30:50 +09:00
github-actions[bot]
696a0ac99c
chore(lint): 改进代码样式 (#1083)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2024-12-18 00:32:12 +09:00
宇~
37818acee1
feat(manager): onregex -> pattern (#1082)
* feat(manager): onregex -> pattern

* delete nil
2024-12-15 00:51:45 +09:00
vatebur
38eae9375d
chore:替换图片API地址 (#1077)
感谢大自然的馈赠
2024-12-05 14:48:49 +09:00
vatebur
c35fc543d3
[mcfish]数值调整,[score]调整逻辑 (#1074)
* chore:[mcfish]数值调整,[score]调整逻辑

* chore:[score]修正错误的文本
2024-12-04 01:25:52 +09:00
vatebur
2c8726dda3
[mcfish]修改钓鱼规则 (#1072)
* fix: 修复[mcfish]交易检测逻辑的bug

- 修复出售限制不更新的bug
- 修改商品价格浮动区间

* update:[mcfish]美西螈物品翻5倍

- 使用美西螈物品翻倍率3->5
- 移除不使用的函数checkIsFish

* fix: 修复[mcfish]交易检测对垃圾的处理
2024-11-30 16:06:32 +09:00
vatebur
83037f621c
update: 重写[mcfish]交易检测逻辑 (#1070) 2024-11-29 00:41:19 +09:00
vatebur
6a2c7e8740
feat: 签到失败时使用本地图片#1067 (#1068)
* update:签到失败时使用本地图片#1067

1. 修改score插件提示词,对用户更友好。
2. 图片下载失败时,会使用本地图片。

> 如果用户网络一直不通,可能会一直用某张图片作为背景

* update:修改score抽取本地图片逻辑
2024-11-27 13:57:54 +09:00
Jiang-Red
9e8ae43b15
feat(qqwife): some onregex -> pattern (#1058) 2024-11-09 14:25:42 +09:00
源文雨
4ba0b36be1 chore: remove aireply due to 没人用 2024-11-07 14:48:43 +09:00
github-actions[bot]
9470977092
chore: bump deps (#1057)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2024-11-07 14:45:48 +09:00
源文雨
08d33fd74f chore: remove moegoe due to 没人用 2024-11-07 00:37:38 +09:00
源文雨
66a8ec91bc chore: update deps 2024-11-07 00:16:49 +09:00
github-actions[bot]
b4edabb91d
chore: bump deps (#1056)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2024-11-07 00:14:35 +09:00
源文雨
83ca8c344b chore: update deps 2024-11-07 00:12:51 +09:00
源文雨
f5e1c197dd feat: add custom folder 2024-11-01 14:55:01 +09:00
vatebur
7c5a17761e
feat(qqwife): 添加好感度提升途径 (#1049)
支持使用"娶|嫁at"提升好感度
2024-10-30 21:56:25 +09:00
Cu4water
3c7289997a
fix: 进行1e5次钓鱼不出下界合金竿的问题 (#1051) 2024-10-30 21:55:12 +09:00
宇~
eb065e1984
fix(niuniu): 一些小问题 (#1043) 2024-10-22 01:15:55 +09:00
github-actions[bot]
814fa0ce33
chore(lint): 改进代码样式 (#1040)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2024-10-21 00:12:03 +09:00
宇~
4b0a2a12a8
feat(niuniu): 寫真으로 順位 表示 (#1024) 2024-10-20 23:45:11 +09:00
Image
321c941ce5
fix:修复猜单词插件最后一轮无法正常发送的错误 (#1039) 2024-10-16 19:44:58 +09:00
vatebur
e653475e08
fix:修复出售限制未生效的问题 (#1038)
-  修改更新购买限制逻辑的位置
- `checkCanSalesFor` & `selectCanSalesFishFor`俩个函数一个是检测出售鱼竿上限,一个是检测出售鱼上限。
  暂时解决俩个函数对于buff状态更新冲突的问题;下个版本打算重构一下这部分,把俩个函数合并一下。用一个函数就够了
2024-10-15 23:39:01 +09:00
源文雨
23c0949388 fix(emozi): login 2024-10-15 01:48:07 +09:00
github-actions[bot]
6349f38d57
chore: bump deps (#1037)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2024-10-15 01:44:09 +09:00
源文雨
105553c386 fix(emozi): login 2024-10-15 01:41:51 +09:00
源文雨
48a2703b99 chore: update deps 2024-10-15 01:32:20 +09:00
github-actions[bot]
ba3df3d9a0
chore: bump deps (#1036)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2024-10-15 01:09:11 +09:00
源文雨
b448e972e1 chore: update deps 2024-10-15 01:05:53 +09:00
github-actions[bot]
0537fcbd6e
chore(lint): 改进代码样式 (#1034)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2024-10-15 00:05:51 +09:00
github-actions[bot]
6c79b3385f
chore: bump deps (#1033)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2024-10-15 00:05:33 +09:00
dependabot[bot]
42b63d836d
chore(deps): bump github.com/wdvxdr1123/ZeroBot (#1030)
Bumps [github.com/wdvxdr1123/ZeroBot](https://github.com/wdvxdr1123/ZeroBot) from 1.7.5-0.20240829093431-bea5257d1a2b to 1.7.5.
- [Commits](https://github.com/wdvxdr1123/ZeroBot/commits/v1.7.5)

---
updated-dependencies:
- dependency-name: github.com/wdvxdr1123/ZeroBot
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-10-15 00:04:08 +09:00
vatebur
b9bea7dff7
fix: two bugs in mcifsh (#1029)
1. 修复商品数量大于250时,商品价格浮动区间数值异常
2. 修复出售限制未生效的问题。(时间未更新)
2024-10-14 14:09:43 +09:00
github-actions[bot]
78d156395b
chore: bump deps (#1028)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2024-10-14 14:09:27 +09:00
源文雨
8bcab9bf43 optimize(img): drop remote image pool 2024-10-14 02:57:08 +09:00
源文雨
ef3fa92de3 fix(emozi): login 2024-10-13 21:03:53 +09:00
源文雨
50be1bbe68 fix(emozi): print username only 2024-10-13 20:53:00 +09:00
Jiang-Red
4cf296c839
feat(chatcount): rank use image (#1027)
* feat(chatcount): rank use image

* chore(lint): 改进代码样式 (#31)

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>

---------

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2024-10-13 20:23:45 +09:00
Jiang-Red
26d2074db6
fix(score): use mem too high & (aifalse): style adjust (#1026) 2024-10-13 18:15:50 +09:00
github-actions[bot]
419f67f1c7
chore: bump deps (#1025)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2024-10-13 18:15:12 +09:00
源文雨
066ee59741 chore: make lint happy 2024-10-13 18:14:43 +09:00
源文雨
001fdcceae fix(emozi): usr login 2024-10-13 18:13:44 +09:00
源文雨
baf1a80dc2 feat: add plugin emozi & remove vitsnyaru 2024-10-13 18:09:07 +09:00
源文雨
6a038643d6 feat(manager): no forward on single slow 2024-10-13 16:35:51 +09:00
github-actions[bot]
339b9db271
chore(lint): 改进代码样式 (#1021)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2024-10-13 16:11:01 +09:00
github-actions[bot]
6a0d8fbaa4
chore: bump deps (#1020)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2024-10-13 16:10:46 +09:00
dependabot[bot]
24b438741b
chore(deps): bump github.com/wcharczuk/go-chart/v2 from 2.1.1 to 2.1.2 (#1009)
Bumps [github.com/wcharczuk/go-chart/v2](https://github.com/wcharczuk/go-chart) from 2.1.1 to 2.1.2.
- [Release notes](https://github.com/wcharczuk/go-chart/releases)
- [Commits](https://github.com/wcharczuk/go-chart/compare/v2.1.1...v2.1.2)

---
updated-dependencies:
- dependency-name: github.com/wcharczuk/go-chart/v2
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: 源文雨 <41315874+fumiama@users.noreply.github.com>
2024-10-13 07:09:19 +00:00
github-actions[bot]
25ce35fd63
chore: bump deps (#1018)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2024-10-13 16:07:21 +09:00
github-actions[bot]
8b302d715f
chore: bump deps (#1017)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2024-10-13 16:04:31 +09:00
dependabot[bot]
077da3746c
chore(deps): bump github.com/shirou/gopsutil/v3 from 3.24.4 to 3.24.5 (#1008)
Bumps [github.com/shirou/gopsutil/v3](https://github.com/shirou/gopsutil) from 3.24.4 to 3.24.5.
- [Release notes](https://github.com/shirou/gopsutil/releases)
- [Commits](https://github.com/shirou/gopsutil/compare/v3.24.4...v3.24.5)

---
updated-dependencies:
- dependency-name: github.com/shirou/gopsutil/v3
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: 源文雨 <41315874+fumiama@users.noreply.github.com>
2024-10-13 16:03:59 +09:00
github-actions[bot]
831fe5482a
chore: bump deps (#1015)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: 源文雨 <41315874+fumiama@users.noreply.github.com>
2024-10-13 16:03:14 +09:00
github-actions[bot]
edf186f5b8
chore: bump deps (#1012)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2024-10-13 16:01:59 +09:00
github-actions[bot]
db50375141
chore: bump deps (#1013)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2024-10-13 16:01:38 +09:00
github-actions[bot]
8e6eb618dd
chore(lint): 改进代码样式 (#1011)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2024-10-13 16:01:23 +09:00
github-actions[bot]
3102577a16
chore: bump deps (#1010)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2024-10-13 16:01:05 +09:00
dependabot[bot]
24ce891c4d
chore(deps): bump golang.org/x/sys from 0.20.0 to 0.26.0 (#1006)
Bumps [golang.org/x/sys](https://github.com/golang/sys) from 0.20.0 to 0.26.0.
- [Commits](https://github.com/golang/sys/compare/v0.20.0...v0.26.0)

---
updated-dependencies:
- dependency-name: golang.org/x/sys
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-10-13 15:59:50 +09:00
dependabot[bot]
112da0f37d
chore(deps): bump github.com/antchfx/htmlquery from 1.3.1 to 1.3.3 (#1005)
Bumps [github.com/antchfx/htmlquery](https://github.com/antchfx/htmlquery) from 1.3.1 to 1.3.3.
- [Release notes](https://github.com/antchfx/htmlquery/releases)
- [Commits](https://github.com/antchfx/htmlquery/compare/v1.3.1...v1.3.3)

---
updated-dependencies:
- dependency-name: github.com/antchfx/htmlquery
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-10-13 15:59:28 +09:00
dependabot[bot]
a8cb6f2061
chore(deps): bump github.com/tidwall/gjson from 1.17.3 to 1.18.0 (#1004)
Bumps [github.com/tidwall/gjson](https://github.com/tidwall/gjson) from 1.17.3 to 1.18.0.
- [Commits](https://github.com/tidwall/gjson/compare/v1.17.3...v1.18.0)

---
updated-dependencies:
- dependency-name: github.com/tidwall/gjson
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-10-13 15:59:16 +09:00
dependabot[bot]
74edd1c375
chore(deps): bump github.com/FloatTech/rendercard from 0.1.1 to 0.1.2 (#1007)
Bumps [github.com/FloatTech/rendercard](https://github.com/FloatTech/rendercard) from 0.1.1 to 0.1.2.
- [Release notes](https://github.com/FloatTech/rendercard/releases)
- [Commits](https://github.com/FloatTech/rendercard/compare/v0.1.1...v0.1.2)

---
updated-dependencies:
- dependency-name: github.com/FloatTech/rendercard
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-10-13 15:56:49 +09:00
源文雨
defcdf34bb feat(ci): add dependabot 2024-10-13 15:53:56 +09:00
vatebur
f13342350d
fix: 重写交易鱼类上限逻辑 (#1002) (#1003) 2024-10-11 16:27:21 +09:00
昔音幻离
b777b34126
fix(dish): 修复客官名显示为菜名的问题 (#1000)
* fix(dish): 修复客官名显示为菜名的问题
去除了多余的换行

* optimize(dish): 去除不必要的 fmt.Sprintf
2024-10-10 02:45:10 +09:00
宇~
410dd05600
fix: 牛牛逻辑问题 (#996) 2024-10-09 21:59:00 +09:00
源文雨
147ebb001c fix(gif): remove gitcode 👎 2024-10-05 21:52:51 +09:00
github-actions[bot]
096bd4c99a
chore: bump deps (#995)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2024-10-05 21:12:38 +09:00
155 changed files with 7181 additions and 4356 deletions

View File

@ -18,10 +18,12 @@ jobs:
- name: Set up Go - name: Set up Go
uses: actions/setup-go@master uses: actions/setup-go@master
with: with:
go-version: "1.20" go-version: "1.25"
- name: Check out code into the Go module directory - name: Check out code into the Go module directory
uses: actions/checkout@master uses: actions/checkout@master
with:
fetch-depth: 0
- name: gomod2nix update - name: gomod2nix update
run: | run: |

View File

@ -1,42 +0,0 @@
name: 打包最新版为 Docker Image
on: [push]
jobs:
docker-builder:
name: build docker
runs-on: ubuntu-23.04
steps:
- name: Check out code into the Go module directory
uses: actions/checkout@master
- run: sudo apt-get install -y qemu-user-static
- name: Set up nix
uses: cachix/install-nix-action@v27
with:
nix_path: nixpkgs=channel:nixos-unstable
extra_nix_config: |
sandbox = true
- name: Speed Up nix
uses: DeterminateSystems/magic-nix-cache-action@main
- name: build docker
run: |
mkdir output/
# https://discourse.nixos.org/t/nix-github-actions-aarch64/11034
nix build .#packages.aarch64-linux.docker_builder -o aarch64-linux.docker --print-out-paths --option system aarch64-linux --extra-platforms aarch64-linux
cp $(readlink aarch64-linux.docker) ./output/aarch64-linux.docker.tar.gz
nix build .#packages.x86_64-linux.docker_builder -o x86_64-linux.docker --print-out-paths --option system x86_64-linux --extra-platforms x86_64-linux
cp $(readlink x86_64-linux.docker) ./output/x86_64-linux.docker.tar.gz
# gomod2nix did not provide this
# nix build .#packages.i686-linux.docker_builder -o i686-linux.docker --print-out-paths --option system i686-linux --extra-platforms i686-linux
# cp $(readlink i686-linux.docker) ./output/i686-linux.docker.tar.gz
- name: Upload artifact
uses: actions/upload-artifact@master
if: ${{ !github.head_ref }}
with:
path: output/

View File

@ -27,10 +27,12 @@ jobs:
fail-fast: true fail-fast: true
steps: steps:
- uses: actions/checkout@master - uses: actions/checkout@master
with:
fetch-depth: 0
- name: Setup Go environment - name: Setup Go environment
uses: actions/setup-go@master uses: actions/setup-go@master
with: with:
go-version: '1.20' go-version: '1.25'
- name: Cache downloaded module - name: Cache downloaded module
uses: actions/cache@master uses: actions/cache@master
continue-on-error: true continue-on-error: true
@ -45,6 +47,7 @@ jobs:
GOARCH: ${{ matrix.goarch }} GOARCH: ${{ matrix.goarch }}
IS_PR: ${{ !!github.head_ref }} IS_PR: ${{ !!github.head_ref }}
run: | run: |
GOOS= GOARCH= go generate ./...
if [ $GOOS = "windows" ]; then export BINARY_SUFFIX="$BINARY_SUFFIX.exe"; fi if [ $GOOS = "windows" ]; then export BINARY_SUFFIX="$BINARY_SUFFIX.exe"; fi
if $IS_PR ; then echo $PR_PROMPT; fi if $IS_PR ; then echo $PR_PROMPT; fi
export BINARY_NAME="$BINARY_PREFIX$GOOS_$GOARCH$BINARY_SUFFIX" export BINARY_NAME="$BINARY_PREFIX$GOOS_$GOARCH$BINARY_SUFFIX"

View File

@ -26,15 +26,18 @@ jobs:
- name: Set up Go - name: Set up Go
uses: actions/setup-go@master uses: actions/setup-go@master
with: with:
go-version: '1.20' go-version: '1.24'
- name: Check out code into the Go module directory - name: Check out code into the Go module directory
uses: actions/checkout@v4 uses: actions/checkout@v4
with: with:
ref: ${{ github.event.pull_request.head.sha }} ref: ${{ github.event.pull_request.head.sha }}
fetch-depth: 0
- name: Tidy Modules - name: Tidy Modules
run: go mod tidy run: |
go mod tidy
go generate main.go
- name: golangci-lint - name: golangci-lint
uses: golangci/golangci-lint-action@master uses: golangci/golangci-lint-action@master

View File

@ -8,13 +8,17 @@ jobs:
- name: Set up Go - name: Set up Go
uses: actions/setup-go@master uses: actions/setup-go@master
with: with:
go-version: '1.20' go-version: '1.24'
- name: Check out code into the Go module directory - name: Check out code into the Go module directory
uses: actions/checkout@master uses: actions/checkout@master
with:
fetch-depth: 0
- name: Tidy Modules - name: Tidy Modules
run: go mod tidy run: |
go mod tidy
go generate main.go
- name: Run Lint - name: Run Lint
uses: golangci/golangci-lint-action@master uses: golangci/golangci-lint-action@master

View File

@ -16,12 +16,12 @@ jobs:
- name: Set up Go - name: Set up Go
uses: actions/setup-go@master uses: actions/setup-go@master
with: with:
go-version: '1.20' go-version: '1.25'
- name: Run GoReleaser - name: Run GoReleaser
uses: goreleaser/goreleaser-action@master uses: goreleaser/goreleaser-action@master
with: with:
version: latest version: "~> v2"
args: release --clean args: release --clean
env: env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

View File

@ -20,7 +20,7 @@ linters:
#- depguard #- depguard
- dogsled - dogsled
- errcheck - errcheck
- exportloopref #- exportloopref
- exhaustive - exhaustive
#- funlen #- funlen
#- goconst #- goconst
@ -56,7 +56,7 @@ run:
deadline: 5m deadline: 5m
issues-exit-code: 1 issues-exit-code: 1
tests: false tests: false
go: '1.20' go: '1.24'
# output configuration options # output configuration options
output: output:
@ -64,12 +64,12 @@ output:
- format: "colored-line-number" - format: "colored-line-number"
print-issued-lines: true print-issued-lines: true
print-linter-name: true print-linter-name: true
uniq-by-line: true
issues: issues:
# Fix found issues (if it's supported by the linter) # Fix found issues (if it's supported by the linter)
fix: true fix: true
exclude-use-default: false exclude-use-default: false
uniq-by-line: true
exclude: exclude:
- "Error return value of .((os.)?std(out|err)..*|.*Close|.*Seek|.*Flush|os.Remove(All)?|.*print(f|ln)?|os.(Un)?Setenv). is not check" - "Error return value of .((os.)?std(out|err)..*|.*Close|.*Seek|.*Flush|os.Remove(All)?|.*print(f|ln)?|os.(Un)?Setenv). is not check"
- 'identifier ".*" contain non-ASCII character: U\+.*' - 'identifier ".*" contain non-ASCII character: U\+.*'

View File

@ -4,6 +4,7 @@ env:
before: before:
hooks: hooks:
- go mod tidy - go mod tidy
- go generate ./...
- go install github.com/tc-hib/go-winres@latest - go install github.com/tc-hib/go-winres@latest
- go-winres make - go-winres make
builds: builds:
@ -62,7 +63,7 @@ archives:
name_template: "zbp_{{ .Os }}_{{ .Arch }}{{ if .Arm }}v{{ .Arm }}{{ end }}" name_template: "zbp_{{ .Os }}_{{ .Arch }}{{ if .Arm }}v{{ .Arm }}{{ end }}"
format_overrides: format_overrides:
- goos: windows - goos: windows
format: zip formats: zip
nfpms: nfpms:
- license: AGPL 3.0 - license: AGPL 3.0

228
README.md
View File

@ -20,7 +20,7 @@
[![go](https://goreportcard.com/badge/github.com/FloatTech/ZeroBot-Plugin?style=flat-square&logo=go)](https://goreportcard.com/badge/github.com/FloatTech/ZeroBot-Plugin) [![go](https://goreportcard.com/badge/github.com/FloatTech/ZeroBot-Plugin?style=flat-square&logo=go)](https://goreportcard.com/badge/github.com/FloatTech/ZeroBot-Plugin)
[![onebot](https://img.shields.io/badge/onebot-v11-black?style=flat-square&logo=data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAHAAAABwCAMAAADxPgR5AAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAAxQTFRF////29vbr6+vAAAAk1hCcwAAAAR0Uk5T////AEAqqfQAAAKcSURBVHja7NrbctswDATQXfD//zlpO7FlmwAWIOnOtNaTM5JwDMa8E+PNFz7g3waJ24fviyDPgfhz8fHP39cBcBL9KoJbQUxjA2iYqHL3FAnvzhL4GtVNUcoSZe6eSHizBcK5LL7dBr2AUZlev1ARRHCljzRALIEog6H3U6bCIyqIZdAT0eBuJYaGiJaHSjmkYIZd+qSGWAQnIaz2OArVnX6vrItQvbhZJtVGB5qX9wKqCMkb9W7aexfCO/rwQRBzsDIsYx4AOz0nhAtWu7bqkEQBO0Pr+Ftjt5fFCUEbm0Sbgdu8WSgJ5NgH2iu46R/o1UcBXJsFusWF/QUaz3RwJMEgngfaGGdSxJkE/Yg4lOBryBiMwvAhZrVMUUvwqU7F05b5WLaUIN4M4hRocQQRnEedgsn7TZB3UCpRrIJwQfqvGwsg18EnI2uSVNC8t+0QmMXogvbPg/xk+Mnw/6kW/rraUlvqgmFreAA09xW5t0AFlHrQZ3CsgvZm0FbHNKyBmheBKIF2cCA8A600aHPmFtRB1XvMsJAiza7LpPog0UJwccKdzw8rdf8MyN2ePYF896LC5hTzdZqxb6VNXInaupARLDNBWgI8spq4T0Qb5H4vWfPmHo8OyB1ito+AysNNz0oglj1U955sjUN9d41LnrX2D/u7eRwxyOaOpfyevCWbTgDEoilsOnu7zsKhjRCsnD/QzhdkYLBLXjiK4f3UWmcx2M7PO21CKVTH84638NTplt6JIQH0ZwCNuiWAfvuLhdrcOYPVO9eW3A67l7hZtgaY9GZo9AFc6cryjoeFBIWeU+npnk/nLE0OxCHL1eQsc1IciehjpJv5mqCsjeopaH6r15/MrxNnVhu7tmcslay2gO2Z1QfcfX0JMACG41/u0RrI9QAAAABJRU5ErkJggg==)](https://t.me/zerobotplugin) [![onebot](https://img.shields.io/badge/onebot-v11-black?style=flat-square&logo=data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAHAAAABwCAMAAADxPgR5AAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAAxQTFRF////29vbr6+vAAAAk1hCcwAAAAR0Uk5T////AEAqqfQAAAKcSURBVHja7NrbctswDATQXfD//zlpO7FlmwAWIOnOtNaTM5JwDMa8E+PNFz7g3waJ24fviyDPgfhz8fHP39cBcBL9KoJbQUxjA2iYqHL3FAnvzhL4GtVNUcoSZe6eSHizBcK5LL7dBr2AUZlev1ARRHCljzRALIEog6H3U6bCIyqIZdAT0eBuJYaGiJaHSjmkYIZd+qSGWAQnIaz2OArVnX6vrItQvbhZJtVGB5qX9wKqCMkb9W7aexfCO/rwQRBzsDIsYx4AOz0nhAtWu7bqkEQBO0Pr+Ftjt5fFCUEbm0Sbgdu8WSgJ5NgH2iu46R/o1UcBXJsFusWF/QUaz3RwJMEgngfaGGdSxJkE/Yg4lOBryBiMwvAhZrVMUUvwqU7F05b5WLaUIN4M4hRocQQRnEedgsn7TZB3UCpRrIJwQfqvGwsg18EnI2uSVNC8t+0QmMXogvbPg/xk+Mnw/6kW/rraUlvqgmFreAA09xW5t0AFlHrQZ3CsgvZm0FbHNKyBmheBKIF2cCA8A600aHPmFtRB1XvMsJAiza7LpPog0UJwccKdzw8rdf8MyN2ePYF896LC5hTzdZqxb6VNXInaupARLDNBWgI8spq4T0Qb5H4vWfPmHo8OyB1ito+AysNNz0oglj1U955sjUN9d41LnrX2D/u7eRwxyOaOpfyevCWbTgDEoilsOnu7zsKhjRCsnD/QzhdkYLBLXjiK4f3UWmcx2M7PO21CKVTH84638NTplt6JIQH0ZwCNuiWAfvuLhdrcOYPVO9eW3A67l7hZtgaY9GZo9AFc6cryjoeFBIWeU+npnk/nLE0OxCHL1eQsc1IciehjpJv5mqCsjeopaH6r15/MrxNnVhu7tmcslay2gO2Z1QfcfX0JMACG41/u0RrI9QAAAABJRU5ErkJggg==)](https://t.me/zerobotplugin)
[![zerobot](https://img.shields.io/badge/zerobot-v1.7.4-black?style=flat-square&logo=go)](https://github.com/wdvxdr1123/ZeroBot) [![zerobot](https://img.shields.io/badge/zerobot-v1.8.1-black?style=flat-square&logo=go)](https://github.com/wdvxdr1123/ZeroBot)
@ -192,6 +192,18 @@ zerobot [-h] [-m] [-n nickname] [-t token] [-u url] [-g url] [-p prefix] [-d|w]
- [x] 早安 | 晚安 - [x] 早安 | 晚安
</details>
<details>
<summary>违禁词检测</summary>
`import _ "github.com/FloatTech/ZeroBot-Plugin/plugin/antiabuse"
`
- [x] 添加违禁词
- [x] 删除违禁词
- [x] 查看违禁词
</details> </details>
<details> <details>
<summary>ATRI</summary> <summary>ATRI</summary>
@ -256,6 +268,8 @@ zerobot [-h] [-m] [-n nickname] [-t token] [-u url] [-g url] [-p prefix] [-d|w]
- [x] 赞我 - [x] 赞我
- [x] 群签到
- [x] [开启 | 关闭]入群验证 - [x] [开启 | 关闭]入群验证
- [x] [开启 | 关闭]gist加群自动审批 - [x] [开启 | 关闭]gist加群自动审批
@ -276,6 +290,20 @@ zerobot [-h] [-m] [-n nickname] [-t token] [-u url] [-g url] [-p prefix] [-d|w]
- 设置欢迎语可选添加参数说明:{at}可在发送时艾特被欢迎者 {nickname}是被欢迎者名字 {avatar}是被欢迎者头像 {uid}是被欢迎者QQ号 {gid}是当前群群号 {groupname} 是当前群群名 - 设置欢迎语可选添加参数说明:{at}可在发送时艾特被欢迎者 {nickname}是被欢迎者名字 {avatar}是被欢迎者头像 {uid}是被欢迎者QQ号 {gid}是当前群群号 {groupname} 是当前群群名
</details>
<details>
<summary>群应用AI声聊</summary>
`import _ "github.com/FloatTech/ZeroBot-Plugin/plugin/airecord"`
- [x] 设置AI语音群号1048452984(tips机器人任意所在群聊即可)
- [x] 设置AI语音模型
- [x] 查看AI语音配置
- [x] 发送AI语音xxx
</details> </details>
<details> <details>
<summary>定时指令触发器</summary> <summary>定时指令触发器</summary>
@ -384,6 +412,18 @@ print("run[CQ:image,file="+j["img"]+"]")
- [x] 设置默认限速为每 m [分钟 | 秒] n 次触发 - [x] 设置默认限速为每 m [分钟 | 秒] n 次触发
</details>
<details>
<summary>aiimage</summary>
`import _ "github.com/FloatTech/ZeroBot-Plugin/plugin/aiimage"`
- [x] 设置AI画图密钥xxx
- [x] 设置AI画图接口地址https://api.siliconflow.cn/v1/images/generations
- [x] 设置AI画图模型名Kwai-Kolors/Kolors
- [x] 查看AI画图配置
- [x] AI画图 [描述]
</details> </details>
<details> <details>
<summary>AIWife</summary> <summary>AIWife</summary>
@ -400,6 +440,18 @@ print("run[CQ:image,file="+j["img"]+"]")
- [x] 支付宝到账 1 - [x] 支付宝到账 1
</details>
<details>
<summary>AnimeTrace 动画/Galgame识别</summary>
`import _ "github.com/FloatTech/ZeroBot-Plugin/plugin/animetrace"`
基于[AnimeTrace](https://ai.animedb.cn/)API 的识图搜索插件
- [x] Gal识图 | Gal识图 [模型名]
- [x] 动漫识图 | 动漫识图 2 | 动漫识图 [模型名]
</details> </details>
<details> <details>
<summary>触发者撤回时也自动撤回</summary> <summary>触发者撤回时也自动撤回</summary>
@ -519,7 +571,7 @@ print("run[CQ:image,file="+j["img"]+"]")
<details> <details>
<summary>b站动态、专栏、视频、直播解析</summary> <summary>b站动态、专栏、视频、直播解析</summary>
`import _ "github.com/FloatTech/ZeroBot-Plugin/plugin/bilibili"` `import _ "github.com/FloatTech/ZeroBot-Plugin/plugin/bilibiliparse"`
- [x] t.bilibili.com/642277677329285174 | bilibili.com/read/cv17134450 | bilibili.com/video/BV13B4y1x7pS | live.bilibili.com/22603245 - [x] t.bilibili.com/642277677329285174 | bilibili.com/read/cv17134450 | bilibili.com/video/BV13B4y1x7pS | live.bilibili.com/22603245
@ -527,7 +579,7 @@ print("run[CQ:image,file="+j["img"]+"]")
<details> <details>
<summary>b站动态、直播推送,需要配合job一起使用</summary> <summary>b站动态、直播推送,需要配合job一起使用</summary>
`import _ "github.com/FloatTech/ZeroBot-Plugin/plugin/bilibili"` `import _ "github.com/FloatTech/ZeroBot-Plugin/plugin/bilibilipush"`
- [x] 添加b站订阅[uid|name] - [x] 添加b站订阅[uid|name]
@ -593,6 +645,17 @@ print("run[CQ:image,file="+j["img"]+"]")
- [x] 磕cp大老师 雪乃 - [x] 磕cp大老师 雪乃
</details>
<details>
<summary>奇怪语言加解密</summary>
`import _ "github.com/FloatTech/ZeroBot-Plugin/plugin/crypter"`
- [x] 齁语加密 [文本] 或 h加密 [文本]
- [x] 齁语解密 [密文] 或 h解密 [密文]
- [x] fumo加密 [文本]
- [x] fumo解密 [文本]
</details> </details>
<details> <details>
<summary>今日早报</summary> <summary>今日早报</summary>
@ -667,6 +730,16 @@ print("run[CQ:image,file="+j["img"]+"]")
- [x] [emoji][emoji] - [x] [emoji][emoji]
</details>
<details>
<summary>颜文字抽象转写</summary>
`import _ "github.com/FloatTech/ZeroBot-Plugin/plugin/emozi"`
- [x] 抽象转写[文段]
- [x] 抽象还原[文段]
- [x] 抽象登录[用户名]
</details> </details>
<details> <details>
<summary>好友申请及群聊邀请事件处理</summary> <summary>好友申请及群聊邀请事件处理</summary>
@ -941,12 +1014,26 @@ print("run[CQ:image,file="+j["img"]+"]")
</details> </details>
<details> <details>
<summary>日韩 VITS 模型拟声</summary> <summary>Minecraft服务器监控&订阅</summary>
`import _ "github.com/FloatTech/ZeroBot-Plugin/plugin/moegoe"` `import _ "github.com/FloatTech/ZeroBot-Plugin/plugin/minecraftobserver"`
- [x] 让[派蒙|空|荧|阿贝多|枫原万叶|温迪|八重神子|纳西妲|钟离|诺艾尔|凝光|托马|北斗|莫娜|荒泷一斗|提纳里|芭芭拉|艾尔海森|雷电将军|赛诺|琴|班尼特|五郎|神里绫华|迪希雅|夜兰|辛焱|安柏|宵宫|云堇|妮露|烟绯|鹿野院平藏|凯亚|达达利亚|迪卢克|可莉|早柚|香菱|重云|刻晴|久岐忍|珊瑚宫心海|迪奥娜|戴因斯雷布|魈|神里绫人|丽莎|优菈|凯瑟琳|雷泽|菲谢尔|九条裟罗|甘雨|行秋|胡桃|迪娜泽黛|柯莱|申鹤|砂糖|萍姥姥|奥兹|罗莎莉亚|式大将|哲平|坎蒂丝|托克|留云借风真君|昆钧|塞琉斯|多莉|大肉丸|莱依拉|散兵|拉赫曼|杜拉夫|阿守|玛乔丽|纳比尔|海芭夏|九条镰治|阿娜耶|阿晃|阿扎尔|七七|博士|白术|埃洛伊|大慈树王|女士|丽塔|失落迷迭|缭乱星棘|伊甸|伏特加女孩|狂热蓝调|莉莉娅|萝莎莉娅|八重樱|八重霞|卡莲|第六夜想曲|卡萝尔|姬子|极地战刃|布洛妮娅|次生银翼|理之律者|迷城骇兔|希儿|魇夜星渊|黑希儿|帕朵菲莉丝|天元骑英|幽兰黛尔|德丽莎|月下初拥|朔夜观星|暮光骑士|明日香|李素裳|格蕾修|梅比乌斯|渡鸦|人之律者|爱莉希雅|爱衣|天穹游侠|琪亚娜|空之律者|薪炎之律者|云墨丹心|符华|识之律者|维尔薇|芽衣|雷之律者|阿波尼亚]说(中文) - [x] mc服务器状态 [服务器IP/URI]
- [x] mc服务器添加订阅 [服务器IP/URI]
- [x] mc服务器取消订阅 [服务器IP/URI]
- [x] mc服务器订阅拉取 (需要插件定时任务配合使用,全局只需要设置一个)
- 使用job插件设置定时, 对话例子如下:
- 记录在"@every 1m"触发的指令
- (机器人回答:您的下一条指令将被记录,在@@every 1m时触发
- mc服务器订阅拉取
</details>
<details>
<summary>Movies猫眼电影查询</summary>
`import _ "github.com/FloatTech/ZeroBot-Plugin/plugin/movies"`
- [x] 今日电影
- [x] 预售电影
</details> </details>
<details> <details>
<summary>摸鱼</summary> <summary>摸鱼</summary>
@ -991,6 +1078,10 @@ print("run[CQ:image,file="+j["img"]+"]")
- [x] 酷狗点歌[xxx] - [x] 酷狗点歌[xxx]
- [x] qq点歌[xxx]
- [x] 咪咕点歌[xxx]
</details> </details>
<details> <details>
<summary>本地涩图</summary> <summary>本地涩图</summary>
@ -1043,6 +1134,10 @@ print("run[CQ:image,file="+j["img"]+"]")
- [x] 赎牛牛 - [x] 赎牛牛
- [x] 牛牛拍卖行
- [x] 出售牛牛
- [x] 牛牛商店 - [x] 牛牛商店
- [x] 牛牛背包 - [x] 牛牛背包
@ -1189,6 +1284,17 @@ print("run[CQ:image,file="+j["img"]+"]")
- [x] 打劫[对方Q号|@对方QQ] - [x] 打劫[对方Q号|@对方QQ]
</details>
<details>
<summary>RSSHub</summary>
`import _ "github.com/FloatTech/ZeroBot-Plugin/plugin/rsshub"`
- [x] 添加rsshub订阅-/bookfere/weekly
- [x] 删除rsshub订阅-/bookfere/weekly
- [x] 查看rsshub订阅列表
- [x] rsshub同步 (使用job执行定时任务------记录在"@every 10m"触发的指令)
</details> </details>
<details> <details>
<summary>在线代码运行</summary> <summary>在线代码运行</summary>
@ -1309,14 +1415,6 @@ print("run[CQ:image,file="+j["img"]+"]")
- [x] >TL 你好 - [x] >TL 你好
</details>
<details>
<summary>vits猫雷</summary>
`import _ "github.com/FloatTech/ZeroBot-Plugin/plugin/vitsnyaru"`
- [x] 让猫雷说[xxxx]
</details> </details>
<details> <details>
<summary>vtb语录</summary> <summary>vtb语录</summary>
@ -1375,50 +1473,6 @@ print("run[CQ:image,file="+j["img"]+"]")
- [x] 每日特惠 - [x] 每日特惠
</details> </details>
<details>
<summary>百度文心AI</summary>
`import _ "github.com/FloatTech/ZeroBot-Plugin/plugin/wenxinAI"`
基于百度文心API的一些功能
key申请链接https://wenxin.baidu.com/moduleApi/key
- [x] 为[自己/本群/QQ号/群+群号]设置文心key [API Key] [Secret Key]
- [x] 为[自己/本群/QQ号/群+群号]设置画图key [API Key] [Secret Key]
“为10086设置画图key 123 456”“为群10010设置画图key 789 101”
文心key和画图key的API key 可以是相同的只是文心key日限为200画图日限为50以此作区别。
- [x] 文心作文 (x字的)[作文题目]
- [x] 文心提案 (x字的)[文案标题]
- [x] 文心摘要 (x字的)[文章内容]
- [x] 文心小说 (x字的)[小说上文]
- [x] 文心对联 [上联]
- [x] 文心问答 [问题]
- [x] 文心补全 [带“_”的填空题]
- [x] 文心自定义 [prompt]
- [x] [bot名称]画几张[图片描述]的[图片类型][图片尺寸]
指令示例:
- 文心作文 我的椛椛机器人
- 文心作文 300字的我的椛椛机器人
- 椛椛帮我画几张金凤凰背景绚烂高饱和古风仙境高清4K古风的油画方图
</details>
<details> <details>
<summary>抽老婆</summary> <summary>抽老婆</summary>
@ -1432,7 +1486,7 @@ print("run[CQ:image,file="+j["img"]+"]")
`import _ "github.com/FloatTech/ZeroBot-Plugin/plugin/word_count"` `import _ "github.com/FloatTech/ZeroBot-Plugin/plugin/word_count"`
- [x] 热词 [群号] [消息数目]|热词 123456 1000 - [x] 热词 [消息数目]|热词 1000
</details> </details>
<details> <details>
@ -1462,21 +1516,21 @@ print("run[CQ:image,file="+j["img"]+"]")
</details> </details>
<details> <details>
<summary>一些游戏王插件</summary> <summary>游戏王白鸽API卡查</summary>
`import _ "github.com/FloatTech/ZeroBot-Plugin/plugin/ygo"` `import _ "github.com/FloatTech/ZeroBot-Plugin/plugin/ygocdb"`
##### 白鸽API卡查
###### `"github.com/FloatTech/ZeroBot-Plugin/plugin/ygo/ygocdb.go"`
- [x] /ydp [xxx] - [x] /ydp [xxx]
- [x] /yds [xxx] - [x] /yds [xxx]
- [x] /ydb [xxx] - [x] /ydb [xxx]
- 注:[xxx]为搜索内容;p:返回一张图片;s:返回一张效果描述;b:高级搜索 - 注:[xxx]为搜索内容;p:返回一张图片;s:返回一张效果描述;b:高级搜索
##### 集换社卡价查询 </details>
<details>
<summary>游戏王集换社卡价查询</summary>
`import _ "github.com/FloatTech/ZeroBot-Plugin/plugin/ygotrade"`
###### `"github.com/FloatTech/ZeroBot-Plugin/plugin/ygo/ygotrade.go"`
- [x] 查卡价 [卡名] - [x] 查卡价 [卡名]
- [x] 查卡价 [卡名] -r [稀有度 稀有度 ...] - [x] 查卡价 [卡名] -r [稀有度 稀有度 ...]
- [x] 查卡店 [卡名] - [x] 查卡店 [卡名]
@ -1539,6 +1593,33 @@ print("run[CQ:image,file="+j["img"]+"]")
### *低优先级* ### *低优先级*
<details>
<summary>OpenAI聊天</summary>
`import _ "github.com/FloatTech/ZeroBot-Plugin/plugin/aichat"`
- [x] 设置AI聊天触发概率10
- [x] 设置AI聊天温度80
- [x] 设置AI聊天(识图|Agent)接口类型[OpenAI|OLLaMA|GenAI]
- [x] 设置AI聊天(不)使用Agent模式
- [x] 设置AI聊天(不)支持系统提示词
- [x] 设置AI聊天(识图|Agent)接口地址https://api.siliconflow.cn/v1/chat/completions
- [x] 设置AI聊天(识图|Agent)密钥xxx
- [x] 设置AI聊天(识图|Agent)模型名Qwen/Qwen3-8B
- [x] 查看AI聊天系统提示词
- [x] 重置AI聊天系统提示词
- [x] 设置AI聊天系统提示词xxx
- [x] 设置AI聊天分隔符`</think>`(留空则清除)
- [x] 设置AI聊天(不)响应AT
- [x] 设置AI聊天最大长度4096
- [x] 设置AI聊天TopP 0.9
- [x] 设置AI聊天(不)以AI语音输出
- [x] 查看AI聊天配置
- [x] 重置AI聊天
- [x] 群聊总结 [消息数目]|群聊总结 1000
- [x] /gpt [内容](使用大模型聊天)
</details>
<details> <details>
<summary>骂人</summary> <summary>骂人</summary>
@ -1550,19 +1631,7 @@ print("run[CQ:image,file="+j["img"]+"]")
</details> </details>
<details> <details>
<summary>人工智能回复</summary> <summary>词典匹配回复, 仅@触发</summary>
`import _ "github.com/FloatTech/ZeroBot-Plugin/plugin/aireply"`
- [x] @Bot 任意文本(任意一句话回复)
- [x] 设置文字回复模式[婧枫|沫沫|青云客|小爱|ChatGPT]
- [x] 设置 ChatGPT api key xxx
</details>
<details>
<summary>词典匹配回复</summary>
`import _ "github.com/FloatTech/ZeroBot-Plugin/plugin/thesaurus"` `import _ "github.com/FloatTech/ZeroBot-Plugin/plugin/thesaurus"`
@ -1616,6 +1685,7 @@ go version
go env -w GOPROXY=https://goproxy.cn,direct go env -w GOPROXY=https://goproxy.cn,direct
go env -w GO111MODULE=auto go env -w GO111MODULE=auto
go mod tidy go mod tidy
go generate main.go
``` ```
3. 编辑 main.go 文件,内容按需修改 3. 编辑 main.go 文件,内容按需修改

164
abineundo/main.go Normal file
View File

@ -0,0 +1,164 @@
// Package abineundo provides an explicit "from the beginning" (Latin: "ab ineundō")
// initialization anchor.
//
// Name origin:
//
// Latin phrase "ab ineundō" meaning "from which is to be begun".
//
// Purpose:
//
// Place this package at the very top of top-level main.go so its init (present
// or future) executes before other plugin packages, filling in a predictable
// plugin priority.
//
// Typical usage:
//
// import (
// _ "github.com/your/module/abineundo" // priority anchor
// // ... other imports ...
// )
//
// A blank identifier import preserves ordering side-effects without expanding the
// exported API surface.
//
// (No further code is required here; the package's presence alone defines ordering semantics.)
package abineundo
import (
"bufio"
_ "embed"
"regexp"
"strings"
"github.com/FloatTech/zbputils/control"
"github.com/sirupsen/logrus"
)
//go:embed ref/main/main.go
var maincode string
//go:embed ref/custom/register.go
var customcode string
const (
statusnone = iota
statushigh
statushighend
statusmid
statusmidend
statuslow
statuslowend
)
var (
priore = regexp.MustCompile(`^\t// -{28}(高|中|低)优先级区-{28} //$`)
mainpluginre = regexp.MustCompile(`^\t_ "github\.com/FloatTech/ZeroBot-Plugin/plugin/(\w+)"\s+// `)
custpluginre = regexp.MustCompile(`^\t_ "github\.com/FloatTech/ZeroBot-Plugin/custom/plugin/(\w+)"\s+// `)
)
func init() {
highprios := make([]string, 0, 64)
midprios := make([]string, 0, 64)
lowprios := make([]string, 0, 64)
status := statusnone
scanner := bufio.NewScanner(strings.NewReader(maincode))
for scanner.Scan() {
line := scanner.Text()
prioword := ""
match := priore.FindStringSubmatch(line)
if len(match) > 1 {
prioword = match[1]
}
switch prioword {
case "高":
switch status {
case statusnone:
status = statushigh
case statushigh:
status = statushighend
default:
panic("unexpected")
}
case "中":
switch status {
case statushighend:
status = statusmid
case statusmid:
status = statusmidend
default:
panic("unexpected")
}
case "低":
switch status {
case statusmidend:
status = statuslow
case statuslow:
status = statuslowend
default:
panic("unexpected")
}
default:
switch status {
case statusnone: // 还未开始匹配
continue
case statuslowend: // 匹配已结束
break
default: // 继续匹配插件
}
}
// 在对应优先级区域内匹配插件
if matches := mainpluginre.FindStringSubmatch(line); len(matches) > 1 {
name := matches[1]
switch status {
case statushigh:
highprios = append(highprios, name)
case statusmid:
midprios = append(midprios, name)
case statuslow:
lowprios = append(lowprios, name)
default: // 在不该匹配到插件的区域匹配到
panic("unexpected")
}
}
}
custprios := make([]string, 0, 64)
scanner = bufio.NewScanner(strings.NewReader(customcode))
for scanner.Scan() {
line := scanner.Text()
if matches := custpluginre.FindStringSubmatch(line); len(matches) > 1 {
custprios = append(custprios, matches[1])
}
}
// 生成最终插件优先级表
m := make(map[string]uint64, 4*(len(highprios)+len(midprios)+len(lowprios)+len(custprios)))
i := 0
for _, name := range highprios {
m[name] = (uint64(i) + 1) * 10
logrus.Debugln("[ab] set high plugin", name, "prio to", m[name])
i++
}
for _, name := range custprios {
m[name] = (uint64(i) + 1) * 10
logrus.Debugln("[ab] set cust plugin", name, "prio to", m[name])
i++
}
for _, name := range midprios {
m[name] = (uint64(i) + 1) * 10
logrus.Debugln("[ab] set mid plugin", name, "prio to", m[name])
i++
}
for _, name := range lowprios {
m[name] = (uint64(i) + 1) * 10
logrus.Debugln("[ab] set low plugin", name, "prio to", m[name])
i++
}
control.LoadCustomPriority(m)
}

2
abineundo/ref/custom/.gitignore vendored Normal file
View File

@ -0,0 +1,2 @@
!.gitignore
*

54
abineundo/ref/main.go Normal file
View File

@ -0,0 +1,54 @@
// Package main generate necessary files that needed for compiling
package main
import (
"flag"
"io"
"os"
"path"
)
func main() {
root := flag.String("r", "", "project root dir")
flag.Parse()
fi, err := os.Open(path.Join(*root, "main.go"))
if err != nil {
panic(err)
}
fo, err := os.Create(path.Join(*root, "abineundo/ref/main/main.go"))
if err != nil {
panic(err)
}
_, err = io.Copy(fo, fi)
if err != nil {
panic(err)
}
fi.Close()
fo.Close()
regf := path.Join(*root, "custom/register.go")
tgtf := path.Join(*root, "abineundo/ref/custom/register.go")
if _, err := os.Stat(regf); err != nil {
if os.IsNotExist(err) {
_ = os.WriteFile(tgtf, []byte("// Package custom ...\npackage custom\n"), 0644)
return
}
panic(err)
}
fi, err = os.Open(regf)
if err != nil {
panic(err)
}
fo, err = os.Create(tgtf)
if err != nil {
panic(err)
}
_, err = io.Copy(fo, fi)
if err != nil {
panic(err)
}
fi.Close()
fo.Close()
}

2
abineundo/ref/main/.gitignore vendored Normal file
View File

@ -0,0 +1,2 @@
!.gitignore
*

View File

@ -38,12 +38,18 @@ func setConsoleTitle(title string) (err error) {
} }
func init() { func init() {
debugMode := os.Getenv("DEBUG_MODE") == "1"
stdin := windows.Handle(os.Stdin.Fd()) stdin := windows.Handle(os.Stdin.Fd())
var mode uint32 var mode uint32
err := windows.GetConsoleMode(stdin, &mode) err := windows.GetConsoleMode(stdin, &mode)
if err != nil { if err != nil {
panic(err) if debugMode {
logrus.Warnf("调试模式下忽略控制台模式获取失败: %v", err)
return // 调试模式下直接返回,跳过后续配置
} else {
panic(err) // 非调试模式下 panic
}
} }
mode &^= windows.ENABLE_QUICK_EDIT_MODE // 禁用快速编辑模式 mode &^= windows.ENABLE_QUICK_EDIT_MODE // 禁用快速编辑模式

4
custom/.gitignore vendored Normal file
View File

@ -0,0 +1,4 @@
!.gitignore
!doc.go
!plugin
*

2
custom/doc.go Normal file
View File

@ -0,0 +1,2 @@
// Package custom 注册用户自定义插件于此
package custom

2
custom/plugin/.gitignore vendored Normal file
View File

@ -0,0 +1,2 @@
!.gitignore
*

2
data

@ -1 +1 @@
Subproject commit f1f8cd107cacaf8d8e358d7468f7c9b25adf637a Subproject commit 1b0abcd3fe4943fa3298885cf0311e8d94a02c0b

View File

@ -11,6 +11,7 @@
} }
), ),
buildGoApplication ? pkgs.buildGoApplication, buildGoApplication ? pkgs.buildGoApplication,
...
}: }:
buildGoApplication { buildGoApplication {
pname = "ZeroBot-Plugin"; pname = "ZeroBot-Plugin";

View File

@ -5,11 +5,11 @@
"systems": "systems" "systems": "systems"
}, },
"locked": { "locked": {
"lastModified": 1705309234, "lastModified": 1731533236,
"narHash": "sha256-uNRRNRKmJyCRC/8y1RqBkqWBLM034y4qN7EprSdmgyA=", "narHash": "sha256-l0KFg5HjrsfsO/JpG+r7fRrqm12kzFHyUHqHCVpMMbI=",
"owner": "numtide", "owner": "numtide",
"repo": "flake-utils", "repo": "flake-utils",
"rev": "1ef2e671c3b0c19053962c07dbda38332dcebf26", "rev": "11707dc2f618dd54ca8739b309ec4fc024de578b",
"type": "github" "type": "github"
}, },
"original": { "original": {
@ -28,11 +28,11 @@
] ]
}, },
"locked": { "locked": {
"lastModified": 1705314449, "lastModified": 1742209644,
"narHash": "sha256-yfQQ67dLejP0FLK76LKHbkzcQqNIrux6MFe32MMFGNQ=", "narHash": "sha256-jMy1XqXqD0/tJprEbUmKilTkvbDY/C0ZGSsJJH4TNCE=",
"owner": "nix-community", "owner": "nix-community",
"repo": "gomod2nix", "repo": "gomod2nix",
"rev": "30e3c3a9ec4ac8453282ca7f67fca9e1da12c3e6", "rev": "8f3534eb8f6c5c3fce799376dc3b91bae6b11884",
"type": "github" "type": "github"
}, },
"original": { "original": {
@ -43,11 +43,11 @@
}, },
"nixpkgs": { "nixpkgs": {
"locked": { "locked": {
"lastModified": 1705856552, "lastModified": 1745391562,
"narHash": "sha256-JXfnuEf5Yd6bhMs/uvM67/joxYKoysyE3M2k6T3eWbg=", "narHash": "sha256-sPwcCYuiEopaafePqlG826tBhctuJsLx/mhKKM5Fmjo=",
"owner": "NixOS", "owner": "NixOS",
"repo": "nixpkgs", "repo": "nixpkgs",
"rev": "612f97239e2cc474c13c9dafa0df378058c5ad8d", "rev": "8a2f738d9d1f1d986b5a4cd2fd2061a7127237d7",
"type": "github" "type": "github"
}, },
"original": { "original": {
@ -57,11 +57,28 @@
"type": "github" "type": "github"
} }
}, },
"nixpkgs-with-go_1_20": {
"locked": {
"lastModified": 1710843028,
"narHash": "sha256-CMbK45c4nSkGvayiEHFkGFH+doGPbgo3AWfecd2t1Fk=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "33c51330782cb486764eb598d5907b43dc87b4c2",
"type": "github"
},
"original": {
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "33c51330782cb486764eb598d5907b43dc87b4c2",
"type": "github"
}
},
"root": { "root": {
"inputs": { "inputs": {
"flake-utils": "flake-utils", "flake-utils": "flake-utils",
"gomod2nix": "gomod2nix", "gomod2nix": "gomod2nix",
"nixpkgs": "nixpkgs" "nixpkgs": "nixpkgs",
"nixpkgs-with-go_1_20": "nixpkgs-with-go_1_20"
} }
}, },
"systems": { "systems": {

View File

@ -1,6 +1,7 @@
{ {
description = " ZeroBot OneBot "; description = " ZeroBot OneBot ";
inputs.nixpkgs-with-go_1_20.url = "github:NixOS/nixpkgs/33c51330782cb486764eb598d5907b43dc87b4c2";
inputs.nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable"; inputs.nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable";
inputs.flake-utils.url = "github:numtide/flake-utils"; inputs.flake-utils.url = "github:numtide/flake-utils";
inputs.gomod2nix.url = "github:nix-community/gomod2nix"; inputs.gomod2nix.url = "github:nix-community/gomod2nix";
@ -10,14 +11,25 @@
outputs = { outputs = {
self, self,
nixpkgs, nixpkgs,
nixpkgs-with-go_1_20,
flake-utils, flake-utils,
gomod2nix, gomod2nix,
}: let ...
} @ inputs: let
allSystems = flake-utils.lib.allSystems; allSystems = flake-utils.lib.allSystems;
in ( in (
flake-utils.lib.eachSystem allSystems flake-utils.lib.eachSystem allSystems
(system: let (system: let
pkgs = nixpkgs.legacyPackages.${system}; old-nixpkgs = nixpkgs-with-go_1_20.legacyPackages.${system};
pkgs = import nixpkgs {
inherit system;
overlays = [
(_: _: {
go_1_20 = old-nixpkgs.go_1_20;
})
];
};
# The current default sdk for macOS fails to compile go projects, so we use a newer one for now. # The current default sdk for macOS fails to compile go projects, so we use a newer one for now.
# This has no effect on other platforms. # This has no effect on other platforms.
@ -25,11 +37,10 @@
in { in {
# doCheck will fail at write files # doCheck will fail at write files
packages = rec { packages = rec {
ZeroBot-Plugin = (callPackage ./. (inputs
ZeroBot-Plugin = // {
(callPackage ./. {
inherit (gomod2nix.legacyPackages.${system}) buildGoApplication; inherit (gomod2nix.legacyPackages.${system}) buildGoApplication;
}) }))
.overrideAttrs (_: {doCheck = false;}); .overrideAttrs (_: {doCheck = false;});
default = ZeroBot-Plugin; default = ZeroBot-Plugin;
@ -42,7 +53,6 @@
pkgs.cacert pkgs.cacert
]; ];
}; };
}; };
devShells.default = callPackage ./shell.nix { devShells.default = callPackage ./shell.nix {
inherit (gomod2nix.legacyPackages.${system}) mkGoEnv gomod2nix; inherit (gomod2nix.legacyPackages.${system}) mkGoEnv gomod2nix;

110
go.mod
View File

@ -1,99 +1,105 @@
module github.com/FloatTech/ZeroBot-Plugin module github.com/FloatTech/ZeroBot-Plugin
go 1.20 go 1.24.2
require ( require (
github.com/Baidu-AIP/golang-sdk v1.1.1 github.com/Baidu-AIP/golang-sdk v1.1.1
github.com/FloatTech/AnimeAPI v1.7.1-0.20240826120833-9bf54389aadb github.com/FloatTech/AnimeAPI v1.7.1-0.20251028071248-0c948e3db65c
github.com/FloatTech/floatbox v0.0.0-20240505082030-226ec6713e14 github.com/FloatTech/floatbox v0.0.0-20251002074805-f95cbc7edb31
github.com/FloatTech/gg v1.1.3 github.com/FloatTech/gg v1.1.3
github.com/FloatTech/imgfactory v0.2.2-0.20230413152719-e101cc3606ef github.com/FloatTech/imgfactory v0.2.2-0.20230413152719-e101cc3606ef
github.com/FloatTech/rendercard v0.1.1 github.com/FloatTech/rendercard v0.2.0
github.com/FloatTech/sqlite v1.6.3 github.com/FloatTech/sqlite v1.7.2
github.com/FloatTech/ttl v0.0.0-20240716161252-965925764562 github.com/FloatTech/ttl v0.0.0-20250224045156-012b1463287d
github.com/FloatTech/zbpctrl v1.6.2-0.20240904160347-1317e11a15bb github.com/FloatTech/zbpctrl v1.7.0
github.com/FloatTech/zbputils v1.7.2-0.20240822065525-5ea6811ed91c github.com/FloatTech/zbputils v1.7.2-0.20251002080916-b554b7039913
github.com/RomiChan/syncx v0.0.0-20240418144900-b7402ffdebc7 github.com/RomiChan/syncx v0.0.0-20240418144900-b7402ffdebc7
github.com/RomiChan/websocket v1.4.3-0.20220227141055-9b2c6168c9c5 github.com/RomiChan/websocket v1.4.3-0.20251002072000-d3eb41798438
github.com/antchfx/htmlquery v1.3.1 github.com/Tnze/go-mc v1.20.2
github.com/antchfx/htmlquery v1.3.4
github.com/corona10/goimagehash v1.1.0 github.com/corona10/goimagehash v1.1.0
github.com/davidscholberg/go-durationfmt v0.0.0-20170122144659-64843a2083d3 github.com/davidscholberg/go-durationfmt v0.0.0-20170122144659-64843a2083d3
github.com/disintegration/imaging v1.6.2 github.com/disintegration/imaging v1.6.2
github.com/fumiama/ahsai v0.1.0 github.com/fumiama/ahsai v0.1.1
github.com/fumiama/cron v1.3.0 github.com/fumiama/cron v1.3.0
github.com/fumiama/deepinfra v0.0.0-20250924162107-cf156d49a0fa
github.com/fumiama/go-base16384 v1.7.0 github.com/fumiama/go-base16384 v1.7.0
github.com/fumiama/go-onebot-agent v0.0.0-20250926145606-37ebfa6131c8
github.com/fumiama/go-registry v0.2.7 github.com/fumiama/go-registry v0.2.7
github.com/fumiama/gotracemoe v0.0.3 github.com/fumiama/gotracemoe v0.0.3
github.com/fumiama/jieba v0.0.0-20221203025406-36c17a10b565 github.com/fumiama/jieba v0.0.0-20221203025406-36c17a10b565
github.com/fumiama/slowdo v0.0.0-20241001074058-27c4fe5259a4 github.com/fumiama/slowdo v0.0.0-20241001074058-27c4fe5259a4
github.com/fumiama/terasu v0.0.0-20240507144117-547a591149c0 github.com/fumiama/terasu v1.0.1
github.com/fumiama/unibase2n v0.0.0-20240530074540-ec743fd5a6d6 github.com/fumiama/unibase2n v0.0.0-20240530074540-ec743fd5a6d6
github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0 github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0
github.com/google/uuid v1.6.0
github.com/jinzhu/gorm v1.9.16 github.com/jinzhu/gorm v1.9.16
github.com/jozsefsallai/gophersauce v1.0.1 github.com/jozsefsallai/gophersauce v1.0.1
github.com/kanrichan/resvg-go v0.0.2-0.20231001163256-63db194ca9f5 github.com/kanrichan/resvg-go v0.0.2-0.20231001163256-63db194ca9f5
github.com/lithammer/fuzzysearch v1.1.8 github.com/lithammer/fuzzysearch v1.1.8
github.com/liuzl/gocc v0.0.0-20231231122217-0372e1059ca5 github.com/liuzl/gocc v0.0.0-20231231122217-0372e1059ca5
github.com/mmcdole/gofeed v1.3.0
github.com/mroth/weightedrand v1.0.0 github.com/mroth/weightedrand v1.0.0
github.com/notnil/chess v1.9.0 github.com/notnil/chess v1.10.0
github.com/pkg/errors v0.9.1 github.com/pkg/errors v0.9.1
github.com/shirou/gopsutil/v3 v3.24.4 github.com/shirou/gopsutil/v4 v4.25.11
github.com/sirupsen/logrus v1.9.3 github.com/sirupsen/logrus v1.9.3
github.com/tidwall/gjson v1.17.3 github.com/tidwall/gjson v1.18.0
github.com/wcharczuk/go-chart/v2 v2.1.1 github.com/wcharczuk/go-chart/v2 v2.1.2
github.com/wdvxdr1123/ZeroBot v1.7.5-0.20240829093431-bea5257d1a2b github.com/wdvxdr1123/ZeroBot v1.8.2-0.20251002074418-56567b7fc282
gitlab.com/gomidi/midi/v2 v2.1.7 gitlab.com/gomidi/midi/v2 v2.3.16
golang.org/x/image v0.16.0 golang.org/x/image v0.32.0
golang.org/x/sys v0.20.0 golang.org/x/sys v0.38.0
golang.org/x/text v0.15.0 golang.org/x/text v0.30.0
gopkg.in/yaml.v3 v3.0.1
) )
require ( require (
github.com/PuerkitoBio/goquery v1.10.3 // indirect
github.com/adamzy/cedar-go v0.0.0-20170805034717-80a9c64b256d // indirect github.com/adamzy/cedar-go v0.0.0-20170805034717-80a9c64b256d // indirect
github.com/ajstarks/svgo v0.0.0-20200320125537-f189e35d30ca // indirect github.com/ajstarks/svgo v0.0.0-20211024235047-1546f124cd8b // indirect
github.com/antchfx/xpath v1.3.0 // indirect github.com/andybalholm/cascadia v1.3.3 // indirect
github.com/blend/go-sdk v1.20220411.3 // indirect github.com/antchfx/xpath v1.3.5 // indirect
github.com/dustin/go-humanize v1.0.1 // indirect github.com/dustin/go-humanize v1.0.1 // indirect
github.com/ebitengine/oto/v3 v3.4.0 // indirect
github.com/ebitengine/purego v0.9.1 // indirect
github.com/ericpauley/go-quantize v0.0.0-20200331213906-ae555eb2afa4 // indirect github.com/ericpauley/go-quantize v0.0.0-20200331213906-ae555eb2afa4 // indirect
github.com/faiface/beep v1.1.0 // indirect
github.com/fumiama/go-simple-protobuf v0.2.0 // indirect github.com/fumiama/go-simple-protobuf v0.2.0 // indirect
github.com/fumiama/gofastTEA v0.0.10 // indirect github.com/fumiama/gofastTEA v0.1.3 // indirect
github.com/fumiama/imgsz v0.0.4 // indirect github.com/fumiama/imgsz v0.0.4 // indirect
github.com/gabriel-vasile/mimetype v1.0.4 // indirect github.com/fumiama/orbyte v0.0.0-20251002065953-3bb358367eb5 // indirect
github.com/go-ole/go-ole v1.2.6 // indirect github.com/gabriel-vasile/mimetype v1.4.10 // indirect
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect github.com/go-ole/go-ole v1.3.0 // indirect
github.com/google/uuid v1.6.0 // indirect github.com/golang/groupcache v0.0.0-20241129210726-2c02b8208cf8 // indirect
github.com/hajimehoshi/oto v0.7.1 // indirect github.com/gopxl/beep/v2 v2.1.1 // indirect
github.com/jfreymuth/oggvorbis v1.0.1 // indirect github.com/jfreymuth/oggvorbis v1.0.5 // indirect
github.com/jfreymuth/vorbis v1.0.0 // indirect github.com/jfreymuth/vorbis v1.0.2 // indirect
github.com/jinzhu/inflection v1.0.0 // indirect github.com/jinzhu/inflection v1.0.0 // indirect
github.com/kr/text v0.2.0 // indirect github.com/json-iterator/go v1.1.12 // indirect
github.com/liuzl/cedar-go v0.0.0-20170805034717-80a9c64b256d // indirect github.com/liuzl/cedar-go v0.0.0-20170805034717-80a9c64b256d // indirect
github.com/liuzl/da v0.0.0-20180704015230-14771aad5b1d // indirect github.com/liuzl/da v0.0.0-20180704015230-14771aad5b1d // indirect
github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0 // indirect github.com/lufia/plan9stats v0.0.0-20251013123823-9fd1530e3ec3 // indirect
github.com/mattn/go-isatty v0.0.20 // indirect github.com/mattn/go-isatty v0.0.20 // indirect
github.com/ncruces/go-strftime v0.1.9 // indirect github.com/mmcdole/goxpp v1.1.1 // indirect
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
github.com/modern-go/reflect2 v1.0.2 // indirect
github.com/ncruces/go-strftime v1.0.0 // indirect
github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646 // indirect github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646 // indirect
github.com/pbnjay/memory v0.0.0-20210728143218-7b4eea64cf58 // indirect github.com/pbnjay/memory v0.0.0-20210728143218-7b4eea64cf58 // indirect
github.com/pkumza/numcn v1.0.0 // indirect github.com/pkumza/numcn v1.0.0 // indirect
github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c // indirect github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55 // indirect
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec // indirect github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec // indirect
github.com/rogpeppe/go-internal v1.12.0 // indirect github.com/tetratelabs/wazero v1.9.0 // indirect
github.com/shoenig/go-m1cpu v0.1.6 // indirect github.com/tidwall/match v1.2.0 // indirect
github.com/tetratelabs/wazero v1.5.0 // indirect github.com/tidwall/pretty v1.2.1 // indirect
github.com/tidwall/match v1.1.1 // indirect github.com/tklauser/go-sysconf v0.3.16 // indirect
github.com/tidwall/pretty v1.2.0 // indirect github.com/tklauser/numcpus v0.11.0 // indirect
github.com/tklauser/go-sysconf v0.3.12 // indirect
github.com/tklauser/numcpus v0.6.1 // indirect
github.com/yusufpapurcu/wmi v1.2.4 // indirect github.com/yusufpapurcu/wmi v1.2.4 // indirect
golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8 // indirect golang.org/x/net v0.46.0 // indirect
golang.org/x/mobile v0.0.0-20190415191353-3e0bab5405d6 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect
golang.org/x/net v0.24.0 // indirect modernc.org/libc v1.66.10 // indirect
modernc.org/libc v1.49.3 // indirect modernc.org/mathutil v1.7.1 // indirect
modernc.org/mathutil v1.6.0 // indirect modernc.org/memory v1.11.0 // indirect
modernc.org/memory v1.8.0 // indirect modernc.org/sqlite v1.39.1 // indirect
modernc.org/sqlite v1.20.0 // indirect
) )
replace modernc.org/sqlite => github.com/fumiama/sqlite3 v1.29.10-simp replace modernc.org/sqlite => github.com/fumiama/sqlite3 v1.29.10-simp

315
go.sum
View File

@ -1,44 +1,50 @@
github.com/Baidu-AIP/golang-sdk v1.1.1 h1:RQsAmgDSAkiq22I6n7XJ2t3afgzFeqjY46FGhvrx4cw= github.com/Baidu-AIP/golang-sdk v1.1.1 h1:RQsAmgDSAkiq22I6n7XJ2t3afgzFeqjY46FGhvrx4cw=
github.com/Baidu-AIP/golang-sdk v1.1.1/go.mod h1:bXnGw7xPeKt8aF7UCELKrV6UZ/46spItONK1RQBQj1Y= github.com/Baidu-AIP/golang-sdk v1.1.1/go.mod h1:bXnGw7xPeKt8aF7UCELKrV6UZ/46spItONK1RQBQj1Y=
github.com/DATA-DOG/go-sqlmock v1.3.3/go.mod h1:f/Ixk793poVmq4qj/V1dPUg2JEAKC73Q5eFN3EC/SaM= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
github.com/FloatTech/AnimeAPI v1.7.1-0.20240826120833-9bf54389aadb h1:j7m84zwcDWLoMLjgG4MDnvanGQoDNnG8A7/aNCnYMIk= github.com/FloatTech/AnimeAPI v1.7.1-0.20251028071248-0c948e3db65c h1:fmvlRUzwoK6KdoRSW+XeTQ9myKHimd0pV6GbmRJLNRo=
github.com/FloatTech/AnimeAPI v1.7.1-0.20240826120833-9bf54389aadb/go.mod h1:Ru6q5pZUnfMg1iu0M1Hp73q9N3LNIbDr16kjkzyG6Xk= github.com/FloatTech/AnimeAPI v1.7.1-0.20251028071248-0c948e3db65c/go.mod h1:cuDd67B23xmICSmFBhWzXN51blod2BlM1liN9Ux0pSc=
github.com/FloatTech/floatbox v0.0.0-20240505082030-226ec6713e14 h1:8O0Iq9MnKsKowltY9txhOqcJdmGTjxHPQ4gEYzbJc9A= github.com/FloatTech/floatbox v0.0.0-20251002074805-f95cbc7edb31 h1:2K+/M64ixD1Pg5hr00Nbxr7GoWQOgahvpmp1pAMnrYc=
github.com/FloatTech/floatbox v0.0.0-20240505082030-226ec6713e14/go.mod h1:OzGLhvmtz1TKIdGaJDd8pQumvD36UqK+dWsiCISmzQQ= github.com/FloatTech/floatbox v0.0.0-20251002074805-f95cbc7edb31/go.mod h1:kf+Ywc2lk8PLdg3RX0vrUmFLPO6k+23MFmt4GviV8C0=
github.com/FloatTech/gg v1.1.3 h1:+GlL02lTKsxJQr4WCuNwVxC1/eBZrCvypCIBtxuOFb4= github.com/FloatTech/gg v1.1.3 h1:+GlL02lTKsxJQr4WCuNwVxC1/eBZrCvypCIBtxuOFb4=
github.com/FloatTech/gg v1.1.3/go.mod h1:/9oLP54CMfq4r+71XL26uaFTJ1uL1boAyX67680/1HE= github.com/FloatTech/gg v1.1.3/go.mod h1:/9oLP54CMfq4r+71XL26uaFTJ1uL1boAyX67680/1HE=
github.com/FloatTech/imgfactory v0.2.2-0.20230413152719-e101cc3606ef h1:CJbK/2FRwPuZpeb6M4sWK2d7oXDnBEGhpkQuQrgc91A= github.com/FloatTech/imgfactory v0.2.2-0.20230413152719-e101cc3606ef h1:CJbK/2FRwPuZpeb6M4sWK2d7oXDnBEGhpkQuQrgc91A=
github.com/FloatTech/imgfactory v0.2.2-0.20230413152719-e101cc3606ef/go.mod h1:el5hGpj1C1bDRxcTXYRwEivDCr40zZeJpcrLrB1fajs= github.com/FloatTech/imgfactory v0.2.2-0.20230413152719-e101cc3606ef/go.mod h1:el5hGpj1C1bDRxcTXYRwEivDCr40zZeJpcrLrB1fajs=
github.com/FloatTech/rendercard v0.1.1 h1:vXz3x92bLavmNexTywdUvhft2/ipUSuo8aPRkqVdGQ8= github.com/FloatTech/rendercard v0.2.0 h1:PBTZ2gCEy/dAEGSfWecrGTrWDYpiBJD1dVzNDDaOxh4=
github.com/FloatTech/rendercard v0.1.1/go.mod h1:Sbojcy1t3NfFz7/WicZRmR/uKFxNMYkKF8qHx69dxY0= github.com/FloatTech/rendercard v0.2.0/go.mod h1:Sbojcy1t3NfFz7/WicZRmR/uKFxNMYkKF8qHx69dxY0=
github.com/FloatTech/sqlite v1.6.3 h1:MQkqBNlkPuCoKQQgoNLuTL/2Ci3tBTFAnVYBdD0Wy4M= github.com/FloatTech/sqlite v1.7.2 h1:b8COegNLSzofzOyARsVwSbz9OOzWEa8IElsTlx1TBLw=
github.com/FloatTech/sqlite v1.6.3/go.mod h1:zFbHzRfB+CJ+VidfjuVbrcin3DAz283F7hF1hIeHzpY= github.com/FloatTech/sqlite v1.7.2/go.mod h1:/4tzfCGhrZnnjC1U8vcfwGQeF6eR649fhOsS3+Le0+s=
github.com/FloatTech/ttl v0.0.0-20240716161252-965925764562 h1:snfw7FNFym1eNnLrQ/VCf80LiQo9C7jHgrunZDwiRcY= github.com/FloatTech/ttl v0.0.0-20250224045156-012b1463287d h1:mUQ/c3wXKsUGa4Sg9DBy01APXKB68PmobhxOyaJI7lY=
github.com/FloatTech/ttl v0.0.0-20240716161252-965925764562/go.mod h1:fHZFWGquNXuHttu9dUYoKuNbm3dzLETnIOnm1muSfDs= github.com/FloatTech/ttl v0.0.0-20250224045156-012b1463287d/go.mod h1:fHZFWGquNXuHttu9dUYoKuNbm3dzLETnIOnm1muSfDs=
github.com/FloatTech/zbpctrl v1.6.2-0.20240904160347-1317e11a15bb h1:sGqwCiMDyUD/znWEVVRVxbd6Kg1KLgGnnIuq5bCUWaQ= github.com/FloatTech/zbpctrl v1.7.0 h1:Hxo6EIhJo+pHjcQP9QgIJgluaT1pHH99zkk3njqTNMo=
github.com/FloatTech/zbpctrl v1.6.2-0.20240904160347-1317e11a15bb/go.mod h1:I+MetM++1sJhNPg3zww1aw04BicYsNohvHC4Jh52XSo= github.com/FloatTech/zbpctrl v1.7.0/go.mod h1:xmM4dSwHA02Gei3ogCRiG+RTrw/7Z69PfrN5NYf8BPE=
github.com/FloatTech/zbputils v1.7.2-0.20240822065525-5ea6811ed91c h1:hFiqx4uk6+lc2zHAaQ3JkkI2KH59c6O4yHKWKXFzxLs= github.com/FloatTech/zbputils v1.7.2-0.20251002080916-b554b7039913 h1:uGexKAPL26sAWGemyHbfkjYyzFItMsbI8EREBLSZ/sU=
github.com/FloatTech/zbputils v1.7.2-0.20240822065525-5ea6811ed91c/go.mod h1:MwTFLPhlP0qMMLcq4x90oiu1IVE1T5dN0ZsxyTGSf6k= github.com/FloatTech/zbputils v1.7.2-0.20251002080916-b554b7039913/go.mod h1:mNvv0+wCou042n/3QkK23WmbayNctT5wgkKC3A6nbmM=
github.com/PuerkitoBio/goquery v1.5.1/go.mod h1:GsLWisAFVj4WgDibEWF4pvYnkVQBpKBKeU+7zCJoLcc= github.com/PuerkitoBio/goquery v1.5.1/go.mod h1:GsLWisAFVj4WgDibEWF4pvYnkVQBpKBKeU+7zCJoLcc=
github.com/PuerkitoBio/goquery v1.10.3 h1:pFYcNSqHxBD06Fpj/KsbStFRsgRATgnf3LeXiUkhzPo=
github.com/PuerkitoBio/goquery v1.10.3/go.mod h1:tMUX0zDMHXYlAQk6p35XxQMqMweEKB7iK7iLNd4RH4Y=
github.com/RomiChan/syncx v0.0.0-20240418144900-b7402ffdebc7 h1:S/ferNiehVjNaBMNNBxUjLtVmP/YWD6Yh79RfPv4ehU= github.com/RomiChan/syncx v0.0.0-20240418144900-b7402ffdebc7 h1:S/ferNiehVjNaBMNNBxUjLtVmP/YWD6Yh79RfPv4ehU=
github.com/RomiChan/syncx v0.0.0-20240418144900-b7402ffdebc7/go.mod h1:vD7Ra3Q9onRtojoY5sMCLQ7JBgjUsrXDnDKyFxqpf9w= github.com/RomiChan/syncx v0.0.0-20240418144900-b7402ffdebc7/go.mod h1:vD7Ra3Q9onRtojoY5sMCLQ7JBgjUsrXDnDKyFxqpf9w=
github.com/RomiChan/websocket v1.4.3-0.20220227141055-9b2c6168c9c5 h1:bBmmB7he0iVN4m5mcehfheeRUEer/Avo4ujnxI3uCqs= github.com/RomiChan/websocket v1.4.3-0.20251002072000-d3eb41798438 h1:I0bdwHZ+2DY45b39xPoTD2u+Z8zhvBuu9aZfjMZeiZM=
github.com/RomiChan/websocket v1.4.3-0.20220227141055-9b2c6168c9c5/go.mod h1:0UcFaCkhp6vZw6l5Dpq0Dp673CoF9GdvA8lTfst0GiU= github.com/RomiChan/websocket v1.4.3-0.20251002072000-d3eb41798438/go.mod h1:GO+9i5UYB4BuZEel6BfGx7O1u3ggwgZWUnGxPATUoTE=
github.com/Tnze/go-mc v1.20.2 h1:arHCE/WxLCxY73C/4ZNLdOymRYtdwoXE05ohB7HVN6Q=
github.com/Tnze/go-mc v1.20.2/go.mod h1:geoRj2HsXSkB3FJBuhr7wCzXegRlzWsVXd7h7jiJ6aQ=
github.com/adamzy/cedar-go v0.0.0-20170805034717-80a9c64b256d h1:ir/IFJU5xbja5UaBEQLjcvn7aAU01nqU/NUyOBEU+ew= github.com/adamzy/cedar-go v0.0.0-20170805034717-80a9c64b256d h1:ir/IFJU5xbja5UaBEQLjcvn7aAU01nqU/NUyOBEU+ew=
github.com/adamzy/cedar-go v0.0.0-20170805034717-80a9c64b256d/go.mod h1:PRWNwWq0yifz6XDPZu48aSld8BWwBfr2JKB2bGWiEd4= github.com/adamzy/cedar-go v0.0.0-20170805034717-80a9c64b256d/go.mod h1:PRWNwWq0yifz6XDPZu48aSld8BWwBfr2JKB2bGWiEd4=
github.com/ajstarks/svgo v0.0.0-20200320125537-f189e35d30ca h1:kWzLcty5V2rzOqJM7Tp/MfSX0RMSI1x4IOLApEefYxA= github.com/ajstarks/deck v0.0.0-20200831202436-30c9fc6549a9/go.mod h1:JynElWSGnm/4RlzPXRlREEwqTHAN3T56Bv2ITsFT3gY=
github.com/ajstarks/deck/generate v0.0.0-20210309230005-c3f852c02e19/go.mod h1:T13YZdzov6OU0A1+RfKZiZN9ca6VeKdBdyDV+BY97Tk=
github.com/ajstarks/svgo v0.0.0-20200320125537-f189e35d30ca/go.mod h1:K08gAheRH3/J6wwsYMMT4xOr94bZjxIelGM0+d/wbFw= github.com/ajstarks/svgo v0.0.0-20200320125537-f189e35d30ca/go.mod h1:K08gAheRH3/J6wwsYMMT4xOr94bZjxIelGM0+d/wbFw=
github.com/ajstarks/svgo v0.0.0-20211024235047-1546f124cd8b h1:slYM766cy2nI3BwyRiyQj/Ud48djTMtMebDqepE95rw=
github.com/ajstarks/svgo v0.0.0-20211024235047-1546f124cd8b/go.mod h1:1KcenG0jGWcpt8ov532z81sp/kMMUG485J2InIOyADM=
github.com/andybalholm/cascadia v1.1.0/go.mod h1:GsXiBklL0woXo1j/WYWtSYYC4ouU9PqHO0sqidkEA4Y= github.com/andybalholm/cascadia v1.1.0/go.mod h1:GsXiBklL0woXo1j/WYWtSYYC4ouU9PqHO0sqidkEA4Y=
github.com/antchfx/htmlquery v1.3.1 h1:wm0LxjLMsZhRHfQKKZscDf2COyH4vDYA3wyH+qZ+Ylc= github.com/andybalholm/cascadia v1.3.3 h1:AG2YHrzJIm4BZ19iwJ/DAua6Btl3IwJX+VI4kktS1LM=
github.com/antchfx/htmlquery v1.3.1/go.mod h1:PTj+f1V2zksPlwNt7uVvZPsxpKNa7mlVliCRxLX6Nx8= github.com/andybalholm/cascadia v1.3.3/go.mod h1:xNd9bqTn98Ln4DwST8/nG+H0yuB8Hmgu1YHNnWw0GeA=
github.com/antchfx/xpath v1.3.0 h1:nTMlzGAK3IJ0bPpME2urTuFL76o4A96iYvoKFHRXJgc= github.com/antchfx/htmlquery v1.3.4 h1:Isd0srPkni2iNTWCwVj/72t7uCphFeor5Q8nCzj1jdQ=
github.com/antchfx/xpath v1.3.0/go.mod h1:i54GszH55fYfBmoZXapTHN8T8tkcHfRgLyVwwqzXNcs= github.com/antchfx/htmlquery v1.3.4/go.mod h1:K9os0BwIEmLAvTqaNSua8tXLWRWZpocZIH73OzWQbwM=
github.com/blend/go-sdk v1.20220411.3 h1:GFV4/FQX5UzXLPwWV03gP811pj7B8J2sbuq+GJQofXc= github.com/antchfx/xpath v1.3.3/go.mod h1:i54GszH55fYfBmoZXapTHN8T8tkcHfRgLyVwwqzXNcs=
github.com/blend/go-sdk v1.20220411.3/go.mod h1:7lnH8fTi6U4i1fArEXRyOIY2E1X4MALg09qsQqY1+ak= github.com/antchfx/xpath v1.3.5 h1:PqbXLC3TkfeZyakF5eeh3NTWEbYl4VHNVeufANzDbKQ=
github.com/antchfx/xpath v1.3.5/go.mod h1:i54GszH55fYfBmoZXapTHN8T8tkcHfRgLyVwwqzXNcs=
github.com/corona10/goimagehash v1.1.0 h1:teNMX/1e+Wn/AYSbLHX8mj+mF9r60R1kBeqE9MkoYwI= github.com/corona10/goimagehash v1.1.0 h1:teNMX/1e+Wn/AYSbLHX8mj+mF9r60R1kBeqE9MkoYwI=
github.com/corona10/goimagehash v1.1.0/go.mod h1:VkvE0mLn84L4aF8vCb6mafVajEb6QYMHl2ZJLn0mOGI= github.com/corona10/goimagehash v1.1.0/go.mod h1:VkvE0mLn84L4aF8vCb6mafVajEb6QYMHl2ZJLn0mOGI=
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
github.com/d4l3k/messagediff v1.2.2-0.20190829033028-7e0a312ae40b/go.mod h1:Oozbb1TVXFac9FtSIxHBMnBCq2qeH/2KkEQxENCrlLo=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
@ -50,24 +56,30 @@ github.com/disintegration/imaging v1.6.2 h1:w1LecBlG2Lnp8B3jk5zSuNqd7b4DXhcjwek1
github.com/disintegration/imaging v1.6.2/go.mod h1:44/5580QXChDfwIclfc/PCwrr44amcmDAg8hxG0Ewe4= github.com/disintegration/imaging v1.6.2/go.mod h1:44/5580QXChDfwIclfc/PCwrr44amcmDAg8hxG0Ewe4=
github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY= github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY=
github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto= github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto=
github.com/ebitengine/oto/v3 v3.4.0 h1:br0PgASsEWaoWn38b2Goe7m1GKFYfNgnsjSd5Gg+/bQ=
github.com/ebitengine/oto/v3 v3.4.0/go.mod h1:IOleLVD0m+CMak3mRVwsYY8vTctQgOM0iiL6S7Ar7eI=
github.com/ebitengine/purego v0.9.1 h1:a/k2f2HQU3Pi399RPW1MOaZyhKJL9w/xFpKAg4q1s0A=
github.com/ebitengine/purego v0.9.1/go.mod h1:iIjxzd6CiRiOG0UyXP+V1+jWqUXVjPKLAI0mRfJZTmQ=
github.com/ericpauley/go-quantize v0.0.0-20200331213906-ae555eb2afa4 h1:BBade+JlV/f7JstZ4pitd4tHhpN+w+6I+LyOS7B4fyU= github.com/ericpauley/go-quantize v0.0.0-20200331213906-ae555eb2afa4 h1:BBade+JlV/f7JstZ4pitd4tHhpN+w+6I+LyOS7B4fyU=
github.com/ericpauley/go-quantize v0.0.0-20200331213906-ae555eb2afa4/go.mod h1:H7chHJglrhPPzetLdzBleF8d22WYOv7UM/lEKYiwlKM= github.com/ericpauley/go-quantize v0.0.0-20200331213906-ae555eb2afa4/go.mod h1:H7chHJglrhPPzetLdzBleF8d22WYOv7UM/lEKYiwlKM=
github.com/erikstmartin/go-testdb v0.0.0-20160219214506-8d10e4a1bae5 h1:Yzb9+7DPaBjB8zlTR87/ElzFsnQfuHnVUVqpZZIcV5Y= github.com/erikstmartin/go-testdb v0.0.0-20160219214506-8d10e4a1bae5 h1:Yzb9+7DPaBjB8zlTR87/ElzFsnQfuHnVUVqpZZIcV5Y=
github.com/erikstmartin/go-testdb v0.0.0-20160219214506-8d10e4a1bae5/go.mod h1:a2zkGnVExMxdzMo3M0Hi/3sEU+cWnZpSni0O6/Yb/P0= github.com/erikstmartin/go-testdb v0.0.0-20160219214506-8d10e4a1bae5/go.mod h1:a2zkGnVExMxdzMo3M0Hi/3sEU+cWnZpSni0O6/Yb/P0=
github.com/faiface/beep v1.1.0 h1:A2gWP6xf5Rh7RG/p9/VAW2jRSDEGQm5sbOb38sf5d4c= github.com/fumiama/ahsai v0.1.1 h1:/t5tdKRim0TK6YwgNFQfqtDOW7Y2tFBsmdUWt3JK+C0=
github.com/faiface/beep v1.1.0/go.mod h1:6I8p6kK2q4opL/eWb+kAkk38ehnTunWeToJB+s51sT4= github.com/fumiama/ahsai v0.1.1/go.mod h1:rBhHLgN2bygcqLpBi+XQa8B8Afn4UkPHQ5vvQibdbDQ=
github.com/fumiama/ahsai v0.1.0 h1:LXD61Kaj6kJHa3AEGsLIfKNzcgaVxg7JB72OR4yNNZ4=
github.com/fumiama/ahsai v0.1.0/go.mod h1:fFeNnqgo44i8FIaguK659aQryuZeFy+4klYLQu/rfdk=
github.com/fumiama/cron v1.3.0 h1:ZWlwuexF+HQHl3cYytEE5HNwD99q+3vNZF1GrEiXCFo= github.com/fumiama/cron v1.3.0 h1:ZWlwuexF+HQHl3cYytEE5HNwD99q+3vNZF1GrEiXCFo=
github.com/fumiama/cron v1.3.0/go.mod h1:bz5Izvgi/xEUI8tlBN8BI2jr9Moo8N4or0KV8xXuPDY= github.com/fumiama/cron v1.3.0/go.mod h1:bz5Izvgi/xEUI8tlBN8BI2jr9Moo8N4or0KV8xXuPDY=
github.com/fumiama/deepinfra v0.0.0-20250924162107-cf156d49a0fa h1:UMMNejpPp8dn92GPaVSZ2XKNSgp7+CVneOkZfExUilk=
github.com/fumiama/deepinfra v0.0.0-20250924162107-cf156d49a0fa/go.mod h1:uqsWK/GM9OvKV0pXZOQB63rWugBbiXInY8E1JoRKhkg=
github.com/fumiama/go-base16384 v1.7.0 h1:6fep7XPQWxRlh4Hu+KsdH+6+YdUp+w6CwRXtMWSsXCA= github.com/fumiama/go-base16384 v1.7.0 h1:6fep7XPQWxRlh4Hu+KsdH+6+YdUp+w6CwRXtMWSsXCA=
github.com/fumiama/go-base16384 v1.7.0/go.mod h1:OEn+947GV5gsbTAnyuUW/SrfxJYUdYupSIQXOuGOcXM= github.com/fumiama/go-base16384 v1.7.0/go.mod h1:OEn+947GV5gsbTAnyuUW/SrfxJYUdYupSIQXOuGOcXM=
github.com/fumiama/go-onebot-agent v0.0.0-20250926145606-37ebfa6131c8 h1:aXk5IVXvPy2IfajL6gH+V/6ZOVV1BBVKjnFISLvyw60=
github.com/fumiama/go-onebot-agent v0.0.0-20250926145606-37ebfa6131c8/go.mod h1:oH8DGDpRPjUAu8Fd/K+RxsB+z0Yis+BHeJAh+ZkO5EM=
github.com/fumiama/go-registry v0.2.7 h1:tLEqgEpsiybQMqBv0dLHm5leia/z1DhajMupwnOHeNs= github.com/fumiama/go-registry v0.2.7 h1:tLEqgEpsiybQMqBv0dLHm5leia/z1DhajMupwnOHeNs=
github.com/fumiama/go-registry v0.2.7/go.mod h1:m+wp5fF8dYgVoFkBPZl+vlK90loymaJE0JCtocVQLEs= github.com/fumiama/go-registry v0.2.7/go.mod h1:m+wp5fF8dYgVoFkBPZl+vlK90loymaJE0JCtocVQLEs=
github.com/fumiama/go-simple-protobuf v0.2.0 h1:ACyN1MAlu7pDR3EszWgzUeNP+IRsSHwH6V9JCJA5R5o= github.com/fumiama/go-simple-protobuf v0.2.0 h1:ACyN1MAlu7pDR3EszWgzUeNP+IRsSHwH6V9JCJA5R5o=
github.com/fumiama/go-simple-protobuf v0.2.0/go.mod h1:5yYNapXq1tQMOZg9bOIVhQlZk9pQqpuFIO4DZLbsdy4= github.com/fumiama/go-simple-protobuf v0.2.0/go.mod h1:5yYNapXq1tQMOZg9bOIVhQlZk9pQqpuFIO4DZLbsdy4=
github.com/fumiama/gofastTEA v0.0.10 h1:JJJ+brWD4kie+mmK2TkspDXKzqq0IjXm89aGYfoGhhQ= github.com/fumiama/gofastTEA v0.1.3 h1:fxOi2D66knV6QN170hb59YiqxPhjlgizvBw+o0OjxUA=
github.com/fumiama/gofastTEA v0.0.10/go.mod h1:RIdbYZyB4MbH6ZBlPymRaXn3cD6SedlCu5W/HHfMPBk= github.com/fumiama/gofastTEA v0.1.3/go.mod h1:RIdbYZyB4MbH6ZBlPymRaXn3cD6SedlCu5W/HHfMPBk=
github.com/fumiama/gotracemoe v0.0.3 h1:iI5EbE9A3UUbfukG6+/soYPjp1S31eCNYf4tw7s6/Jc= github.com/fumiama/gotracemoe v0.0.3 h1:iI5EbE9A3UUbfukG6+/soYPjp1S31eCNYf4tw7s6/Jc=
github.com/fumiama/gotracemoe v0.0.3/go.mod h1:tyqahdUzHf0bQIAVY/GYmDWvYYe5ik1ZbhnGYh+zl40= github.com/fumiama/gotracemoe v0.0.3/go.mod h1:tyqahdUzHf0bQIAVY/GYmDWvYYe5ik1ZbhnGYh+zl40=
github.com/fumiama/imgsz v0.0.4 h1:Lsasu2hdSSFS+vnD+nvR1UkiRMK7hcpyYCC0FzgSMFI= github.com/fumiama/imgsz v0.0.4 h1:Lsasu2hdSSFS+vnD+nvR1UkiRMK7hcpyYCC0FzgSMFI=
@ -76,48 +88,45 @@ github.com/fumiama/jieba v0.0.0-20221203025406-36c17a10b565 h1:sQuR2+N5HurnvsZhi
github.com/fumiama/jieba v0.0.0-20221203025406-36c17a10b565/go.mod h1:UUEvyLTJ7yoOA/viKG4wEis4ERydM7+Ny6gZUWgkS80= github.com/fumiama/jieba v0.0.0-20221203025406-36c17a10b565/go.mod h1:UUEvyLTJ7yoOA/viKG4wEis4ERydM7+Ny6gZUWgkS80=
github.com/fumiama/libc v0.0.0-20240530081950-6f6d8586b5c5 h1:jDxsIupsT84A6WHcs6kWbst+KqrRQ8/o0VyoFMnbBOA= github.com/fumiama/libc v0.0.0-20240530081950-6f6d8586b5c5 h1:jDxsIupsT84A6WHcs6kWbst+KqrRQ8/o0VyoFMnbBOA=
github.com/fumiama/libc v0.0.0-20240530081950-6f6d8586b5c5/go.mod h1:15P6ublJ9FJR8YQCGy8DeQ2Uwur7iW9Hserr/T3OFZE= github.com/fumiama/libc v0.0.0-20240530081950-6f6d8586b5c5/go.mod h1:15P6ublJ9FJR8YQCGy8DeQ2Uwur7iW9Hserr/T3OFZE=
github.com/fumiama/orbyte v0.0.0-20251002065953-3bb358367eb5 h1:j9o0XVvdAeLwrBYMnh0SerrMc9CgNU6AGszbsvFzoc0=
github.com/fumiama/orbyte v0.0.0-20251002065953-3bb358367eb5/go.mod h1:FOjdw7KdCbK2eH3gRPhwFNCoXKpu9sN5vPH4El/8e0c=
github.com/fumiama/slowdo v0.0.0-20241001074058-27c4fe5259a4 h1:zN9e09TYKXI1mNkuS6YbH+Sn+4k5tBir+ovhZZcRYAs= github.com/fumiama/slowdo v0.0.0-20241001074058-27c4fe5259a4 h1:zN9e09TYKXI1mNkuS6YbH+Sn+4k5tBir+ovhZZcRYAs=
github.com/fumiama/slowdo v0.0.0-20241001074058-27c4fe5259a4/go.mod h1:iZf1H/Jcw5gjOOFb4C5nlweJtViWc7uwUxRCe14pbYk= github.com/fumiama/slowdo v0.0.0-20241001074058-27c4fe5259a4/go.mod h1:iZf1H/Jcw5gjOOFb4C5nlweJtViWc7uwUxRCe14pbYk=
github.com/fumiama/sqlite3 v1.29.10-simp h1:c5y3uKyU0q9t0/SyfynzYyuslQ5zP+5CD8e0yYY554A= github.com/fumiama/sqlite3 v1.29.10-simp h1:c5y3uKyU0q9t0/SyfynzYyuslQ5zP+5CD8e0yYY554A=
github.com/fumiama/sqlite3 v1.29.10-simp/go.mod h1:ItX2a1OVGgNsFh6Dv60JQvGfJfTPHPVpV6DF59akYOA= github.com/fumiama/sqlite3 v1.29.10-simp/go.mod h1:ItX2a1OVGgNsFh6Dv60JQvGfJfTPHPVpV6DF59akYOA=
github.com/fumiama/terasu v0.0.0-20240507144117-547a591149c0 h1:So/3Bg/m2ZcUvqCzzEjjkjHBjcvnV3AN5tCxwsdMwYU= github.com/fumiama/terasu v1.0.1 h1:WdP12r8xhRTyeCGWBtZx0QP0Eg5S3obJyS5MQVbBilE=
github.com/fumiama/terasu v0.0.0-20240507144117-547a591149c0/go.mod h1:UVx8YP1jKKL1Cj+uy+OnQRM2Ih6U36Mqy9GSf7jabsI= github.com/fumiama/terasu v1.0.1/go.mod h1:ksDiLG3X7RM+B9XW2qvP+v8PU8hv+xhN5vdJdLoBWX4=
github.com/fumiama/unibase2n v0.0.0-20240530074540-ec743fd5a6d6 h1:LtDgr628eji8jRpjPCxsk7ibjcfi97QieZVCTjxLCBw= github.com/fumiama/unibase2n v0.0.0-20240530074540-ec743fd5a6d6 h1:LtDgr628eji8jRpjPCxsk7ibjcfi97QieZVCTjxLCBw=
github.com/fumiama/unibase2n v0.0.0-20240530074540-ec743fd5a6d6/go.mod h1:lEaZsT4FRSqcjnQ5q8y+mkenkzR/r1D3BJmfdp0vqDg= github.com/fumiama/unibase2n v0.0.0-20240530074540-ec743fd5a6d6/go.mod h1:lEaZsT4FRSqcjnQ5q8y+mkenkzR/r1D3BJmfdp0vqDg=
github.com/gabriel-vasile/mimetype v1.0.4 h1:uBejfH8l3/2f+5vjl1e4xIaSyNEhRBZ5N/ij7ohpNd8=
github.com/gabriel-vasile/mimetype v1.0.4/go.mod h1:6CDPel/o/3/s4+bp6kIbsWATq8pmgOisOPG40CJa6To= github.com/gabriel-vasile/mimetype v1.0.4/go.mod h1:6CDPel/o/3/s4+bp6kIbsWATq8pmgOisOPG40CJa6To=
github.com/gdamore/encoding v1.0.0/go.mod h1:alR0ol34c49FCSBLjhosxzcPHQbf2trDkoo5dl+VrEg= github.com/gabriel-vasile/mimetype v1.4.10 h1:zyueNbySn/z8mJZHLt6IPw0KoZsiQNszIpU+bX4+ZK0=
github.com/gdamore/tcell v1.3.0/go.mod h1:Hjvr+Ofd+gLglo7RYKxxnzCBmev3BzsS67MebKS4zMM= github.com/gabriel-vasile/mimetype v1.4.10/go.mod h1:d+9Oxyo1wTzWdyVUPMmXFvp4F9tea18J8ufA774AB3s=
github.com/go-audio/audio v1.0.0/go.mod h1:6uAu0+H2lHkwdGsAY+j2wHPNPpPoeg5AaEFh9FlA+Zs=
github.com/go-audio/riff v1.0.0/go.mod h1:l3cQwc85y79NQFCRB7TiPoNiaijp6q8Z0Uv38rVG498=
github.com/go-audio/wav v1.0.0/go.mod h1:3yoReyQOsiARkvPl3ERCi8JFjihzG6WhjYpZCf5zAWE=
github.com/go-ole/go-ole v1.2.6 h1:/Fpf6oFPoeFik9ty7siob0G6Ke8QvQEuVcuChpwXzpY=
github.com/go-ole/go-ole v1.2.6/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0= github.com/go-ole/go-ole v1.2.6/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0=
github.com/go-ole/go-ole v1.3.0 h1:Dt6ye7+vXGIKZ7Xtk4s6/xVdGDQynvom7xCFEdWr6uE=
github.com/go-ole/go-ole v1.3.0/go.mod h1:5LS6F96DhAwUc7C+1HLexzMXY1xGRSryjyPPKW6zv78=
github.com/go-playground/assert/v2 v2.2.0 h1:JvknZsQTYeFEAhQwI4qEt9cyV5ONwRHC+lYKSsYSR8s= github.com/go-playground/assert/v2 v2.2.0 h1:JvknZsQTYeFEAhQwI4qEt9cyV5ONwRHC+lYKSsYSR8s=
github.com/go-playground/assert/v2 v2.2.0/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4=
github.com/go-sql-driver/mysql v1.5.0 h1:ozyZYNQW3x3HtqT1jira07DN2PArx2v7/mN66gGcHOs= github.com/go-sql-driver/mysql v1.5.0 h1:ozyZYNQW3x3HtqT1jira07DN2PArx2v7/mN66gGcHOs=
github.com/go-sql-driver/mysql v1.5.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg= github.com/go-sql-driver/mysql v1.5.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg=
github.com/golang-sql/civil v0.0.0-20190719163853-cb61b32ac6fe h1:lXe2qZdvpiX5WZkZR4hgp4KJVfY3nMkvmwbVkpv1rVY= github.com/golang-sql/civil v0.0.0-20190719163853-cb61b32ac6fe h1:lXe2qZdvpiX5WZkZR4hgp4KJVfY3nMkvmwbVkpv1rVY=
github.com/golang-sql/civil v0.0.0-20190719163853-cb61b32ac6fe/go.mod h1:8vg3r2VgvsThLBIFL93Qb5yWzgyZWhEmBwUJWevAkK0= github.com/golang-sql/civil v0.0.0-20190719163853-cb61b32ac6fe/go.mod h1:8vg3r2VgvsThLBIFL93Qb5yWzgyZWhEmBwUJWevAkK0=
github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0 h1:DACJavvAHhabrF08vX0COfcOBJRhZ8lUbR+ZWIs0Y5g= github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0 h1:DACJavvAHhabrF08vX0COfcOBJRhZ8lUbR+ZWIs0Y5g=
github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0/go.mod h1:E/TSTwGwJL78qG/PmXZO1EjYhfJinVAhrmmHX6Z8B9k= github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0/go.mod h1:E/TSTwGwJL78qG/PmXZO1EjYhfJinVAhrmmHX6Z8B9k=
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da h1:oI5xCqsCo564l8iNU+DwB5epxmsaqB+rhGL0m5jtYqE=
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/golang/groupcache v0.0.0-20241129210726-2c02b8208cf8 h1:f+oWsMOmNPc8JmEHVZIycC7hBoQxHH9pNKQORJNozsQ=
github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/golang/groupcache v0.0.0-20241129210726-2c02b8208cf8/go.mod h1:wcDNUvekVysuuOpQKo3191zZyTpiI6se1N1ULghS0sw=
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=
github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU=
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/hajimehoshi/go-mp3 v0.3.0/go.mod h1:qMJj/CSDxx6CGHiZeCgbiq2DSUkbK0UbtXShQcnfyMM= github.com/gopxl/beep/v2 v2.1.1 h1:6FYIYMm2qPAdWkjX+7xwKrViS1x0Po5kDMdRkq8NVbU=
github.com/hajimehoshi/oto v0.6.1/go.mod h1:0QXGEkbuJRohbJaxr7ZQSxnju7hEhseiPx2hrh6raOI= github.com/gopxl/beep/v2 v2.1.1/go.mod h1:ZAm9TGQ9lvpoiFLd4zf5B1IuyxZhgRACMId1XJbaW0E=
github.com/hajimehoshi/oto v0.7.1 h1:I7maFPz5MBCwiutOrz++DLdbr4rTzBsbBuV2VpgU9kk= github.com/jfreymuth/oggvorbis v1.0.5 h1:u+Ck+R0eLSRhgq8WTmffYnrVtSztJcYrl588DM4e3kQ=
github.com/hajimehoshi/oto v0.7.1/go.mod h1:wovJ8WWMfFKvP587mhHgot/MBr4DnNy9m6EepeVGnos= github.com/jfreymuth/oggvorbis v1.0.5/go.mod h1:1U4pqWmghcoVsCJJ4fRBKv9peUJMBHixthRlBeD6uII=
github.com/icza/bitio v1.0.0/go.mod h1:0jGnlLAx8MKMr9VGnn/4YrvZiprkvBelsVIbA9Jjr9A= github.com/jfreymuth/vorbis v1.0.2 h1:m1xH6+ZI4thH927pgKD8JOH4eaGRm18rEE9/0WKjvNE=
github.com/icza/mighty v0.0.0-20180919140131-cfd07d671de6/go.mod h1:xQig96I1VNBDIWGCdTt54nHt6EeI639SmHycLYL7FkA= github.com/jfreymuth/vorbis v1.0.2/go.mod h1:DoftRo4AznKnShRl1GxiTFCseHr4zR9BN3TWXyuzrqQ=
github.com/jfreymuth/oggvorbis v1.0.1 h1:NT0eXBgE2WHzu6RT/6zcb2H10Kxj6Fm3PccT0LE6bqw=
github.com/jfreymuth/oggvorbis v1.0.1/go.mod h1:NqS+K+UXKje0FUYUPosyQ+XTVvjmVjps1aEZH1sumIk=
github.com/jfreymuth/vorbis v1.0.0 h1:SmDf783s82lIjGZi8EGUUaS7YxPHgRj4ZXW/h7rUi7U=
github.com/jfreymuth/vorbis v1.0.0/go.mod h1:8zy3lUAm9K/rJJk223RKy6vjCZTWC61NA2QD06bfOE0=
github.com/jinzhu/gorm v1.9.16 h1:+IyIjPEABKRpsu/F8OvDPy9fyQlgsg2luMV2ZIH5i5o= github.com/jinzhu/gorm v1.9.16 h1:+IyIjPEABKRpsu/F8OvDPy9fyQlgsg2luMV2ZIH5i5o=
github.com/jinzhu/gorm v1.9.16/go.mod h1:G3LB3wezTOWM2ITLzPxEXgSkOXAntiLHS7UdBefADcs= github.com/jinzhu/gorm v1.9.16/go.mod h1:G3LB3wezTOWM2ITLzPxEXgSkOXAntiLHS7UdBefADcs=
github.com/jinzhu/inflection v1.0.0 h1:K317FqzuhWc8YvSVlFMCCUb36O/S9MCKRDI7QkRKD/E= github.com/jinzhu/inflection v1.0.0 h1:K317FqzuhWc8YvSVlFMCCUb36O/S9MCKRDI7QkRKD/E=
@ -126,11 +135,11 @@ github.com/jinzhu/now v1.0.1 h1:HjfetcXq097iXP0uoPCdnM4Efp5/9MsM0/M+XOTeR3M=
github.com/jinzhu/now v1.0.1/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8= github.com/jinzhu/now v1.0.1/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8=
github.com/jozsefsallai/gophersauce v1.0.1 h1:BA3ovtQRrAb1qYU9JoRLbDHpxnDunlNcEkEfhCvDDCM= github.com/jozsefsallai/gophersauce v1.0.1 h1:BA3ovtQRrAb1qYU9JoRLbDHpxnDunlNcEkEfhCvDDCM=
github.com/jozsefsallai/gophersauce v1.0.1/go.mod h1:YVEI7djliMTmZ1Vh01YPF8bUHi+oKhe3yXgKf1T49vg= github.com/jozsefsallai/gophersauce v1.0.1/go.mod h1:YVEI7djliMTmZ1Vh01YPF8bUHi+oKhe3yXgKf1T49vg=
github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM=
github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo=
github.com/kanrichan/resvg-go v0.0.2-0.20231001163256-63db194ca9f5 h1:BXnB1Gz4y/zwQh+ZFNy7rgd+ZfMOrwRr4uZSHEI+ieY= github.com/kanrichan/resvg-go v0.0.2-0.20231001163256-63db194ca9f5 h1:BXnB1Gz4y/zwQh+ZFNy7rgd+ZfMOrwRr4uZSHEI+ieY=
github.com/kanrichan/resvg-go v0.0.2-0.20231001163256-63db194ca9f5/go.mod h1:c9+VS9GaommgIOzNWb5ze4lYwfT8BZ2UDyGiuQTT7yc= github.com/kanrichan/resvg-go v0.0.2-0.20231001163256-63db194ca9f5/go.mod h1:c9+VS9GaommgIOzNWb5ze4lYwfT8BZ2UDyGiuQTT7yc=
github.com/kr/pretty v0.3.0 h1:WgNl7dwNpEZ6jJ9k1snq4pZsg7DOEN8hP9Xw0Tsjwk0= github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
github.com/lib/pq v1.1.1 h1:sJZmqHoEaY7f+NPP8pgLB/WxulyR3fewgCM2qaSlBb4= github.com/lib/pq v1.1.1 h1:sJZmqHoEaY7f+NPP8pgLB/WxulyR3fewgCM2qaSlBb4=
github.com/lib/pq v1.1.1/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= github.com/lib/pq v1.1.1/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
github.com/lithammer/fuzzysearch v1.1.8 h1:/HIuJnjHuXS8bKaiTMeeDlW2/AyIWk2brx1V8LFgLN4= github.com/lithammer/fuzzysearch v1.1.8 h1:/HIuJnjHuXS8bKaiTMeeDlW2/AyIWk2brx1V8LFgLN4=
@ -141,163 +150,205 @@ github.com/liuzl/da v0.0.0-20180704015230-14771aad5b1d h1:hTRDIpJ1FjS9ULJuEzu69n
github.com/liuzl/da v0.0.0-20180704015230-14771aad5b1d/go.mod h1:7xD3p0XnHvJFQ3t/stEJd877CSIMkH/fACVWen5pYnc= github.com/liuzl/da v0.0.0-20180704015230-14771aad5b1d/go.mod h1:7xD3p0XnHvJFQ3t/stEJd877CSIMkH/fACVWen5pYnc=
github.com/liuzl/gocc v0.0.0-20231231122217-0372e1059ca5 h1:wnbHIeP1UX8ClYEWKGnw66PfYvReCHu9G5lXSte3Sqc= github.com/liuzl/gocc v0.0.0-20231231122217-0372e1059ca5 h1:wnbHIeP1UX8ClYEWKGnw66PfYvReCHu9G5lXSte3Sqc=
github.com/liuzl/gocc v0.0.0-20231231122217-0372e1059ca5/go.mod h1:7KaV9YIR92M1FpbczAcfYQ3UZ5ayT27pNtunDmXvLBo= github.com/liuzl/gocc v0.0.0-20231231122217-0372e1059ca5/go.mod h1:7KaV9YIR92M1FpbczAcfYQ3UZ5ayT27pNtunDmXvLBo=
github.com/lucasb-eyer/go-colorful v1.0.2/go.mod h1:0MS4r+7BZKSJ5mw4/S5MPN+qHFF1fYclkSPilDOKW0s= github.com/lufia/plan9stats v0.0.0-20251013123823-9fd1530e3ec3 h1:PwQumkgq4/acIiZhtifTV5OUqqiP82UAl0h87xj/l9k=
github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0 h1:6E+4a0GO5zZEnZ81pIr0yLvtUWk2if982qA3F3QD6H4= github.com/lufia/plan9stats v0.0.0-20251013123823-9fd1530e3ec3/go.mod h1:autxFIvghDt3jPTLoqZ9OZ7s9qTGNAWmYCjVFWPX/zg=
github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0/go.mod h1:zJYVVT2jmtg6P3p1VtQj7WsuWi/y4VnjVBn7F8KPB3I=
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
github.com/mattn/go-runewidth v0.0.4/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU=
github.com/mattn/go-sqlite3 v1.14.0 h1:mLyGNKR8+Vv9CAU7PphKa2hkEqxxhn8i32J6FPj1/QA= github.com/mattn/go-sqlite3 v1.14.0 h1:mLyGNKR8+Vv9CAU7PphKa2hkEqxxhn8i32J6FPj1/QA=
github.com/mattn/go-sqlite3 v1.14.0/go.mod h1:JIl7NbARA7phWnGvh0LKTyg7S9BA+6gx71ShQilpsus= github.com/mattn/go-sqlite3 v1.14.0/go.mod h1:JIl7NbARA7phWnGvh0LKTyg7S9BA+6gx71ShQilpsus=
github.com/mewkiz/flac v1.0.7/go.mod h1:yU74UH277dBUpqxPouHSQIar3G1X/QIclVbFahSd1pU= github.com/mmcdole/gofeed v1.3.0 h1:5yn+HeqlcvjMeAI4gu6T+crm7d0anY85+M+v6fIFNG4=
github.com/mewkiz/pkg v0.0.0-20190919212034-518ade7978e2/go.mod h1:3E2FUC/qYUfM8+r9zAwpeHJzqRVVMIYnpzD/clwWxyA= github.com/mmcdole/gofeed v1.3.0/go.mod h1:9TGv2LcJhdXePDzxiuMnukhV2/zb6VtnZt1mS+SjkLE=
github.com/mmcdole/goxpp v1.1.1 h1:RGIX+D6iQRIunGHrKqnA2+700XMCnNv0bAOOv5MUhx8=
github.com/mmcdole/goxpp v1.1.1/go.mod h1:v+25+lT2ViuQ7mVxcncQ8ch1URund48oH+jhjiwEgS8=
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg=
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M=
github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
github.com/mroth/weightedrand v1.0.0 h1:V8JeHChvl2MP1sAoXq4brElOcza+jxLkRuwvtQu8L3E= github.com/mroth/weightedrand v1.0.0 h1:V8JeHChvl2MP1sAoXq4brElOcza+jxLkRuwvtQu8L3E=
github.com/mroth/weightedrand v1.0.0/go.mod h1:3p2SIcC8al1YMzGhAIoXD+r9olo/g/cdJgAD905gyNE= github.com/mroth/weightedrand v1.0.0/go.mod h1:3p2SIcC8al1YMzGhAIoXD+r9olo/g/cdJgAD905gyNE=
github.com/ncruces/go-strftime v0.1.9 h1:bY0MQC28UADQmHmaF5dgpLmImcShSi2kHU9XLdhx/f4= github.com/ncruces/go-strftime v1.0.0 h1:HMFp8mLCTPp341M/ZnA4qaf7ZlsbTc+miZjCLOFAw7w=
github.com/ncruces/go-strftime v0.1.9/go.mod h1:Fwc5htZGVVkseilnfgOVb9mKy6w1naJmn9CehxcKcls= github.com/ncruces/go-strftime v1.0.0/go.mod h1:Fwc5htZGVVkseilnfgOVb9mKy6w1naJmn9CehxcKcls=
github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646 h1:zYyBkD/k9seD2A7fsi6Oo2LfFZAehjjQMERAvZLEDnQ= github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646 h1:zYyBkD/k9seD2A7fsi6Oo2LfFZAehjjQMERAvZLEDnQ=
github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646/go.mod h1:jpp1/29i3P1S/RLdc7JQKbRpFeM1dOBd8T9ki5s+AY8= github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646/go.mod h1:jpp1/29i3P1S/RLdc7JQKbRpFeM1dOBd8T9ki5s+AY8=
github.com/notnil/chess v1.9.0 h1:YMxR5kUVjtwcuFptGU0/3q7eG3MSHQNbg0VUekvRKV0= github.com/notnil/chess v1.10.0 h1:RR3MgS9G6zZmJ+VPTJolyxdaIgxoUPyUUY+2iaw35G0=
github.com/notnil/chess v1.9.0/go.mod h1:cRuJUIBFq9Xki05TWHJxHYkC+fFpq45IWwk94DdlCrA= github.com/notnil/chess v1.10.0/go.mod h1:cRuJUIBFq9Xki05TWHJxHYkC+fFpq45IWwk94DdlCrA=
github.com/orcaman/writerseeker v0.0.0-20200621085525-1d3f536ff85e h1:s2RNOM/IGdY0Y6qfTeUKhDawdHDpK9RGBdx80qN4Ttw=
github.com/orcaman/writerseeker v0.0.0-20200621085525-1d3f536ff85e/go.mod h1:nBdnFKj15wFbf94Rwfq4m30eAcyY9V/IyKAGQFtqkW0=
github.com/pbnjay/memory v0.0.0-20210728143218-7b4eea64cf58 h1:onHthvaw9LFnH4t2DcNVpwGmV9E1BkGknEliJkfwQj0= github.com/pbnjay/memory v0.0.0-20210728143218-7b4eea64cf58 h1:onHthvaw9LFnH4t2DcNVpwGmV9E1BkGknEliJkfwQj0=
github.com/pbnjay/memory v0.0.0-20210728143218-7b4eea64cf58/go.mod h1:DXv8WO4yhMYhSNPKjeNKa5WY9YCIEBRbNzFFPJbWO6Y= github.com/pbnjay/memory v0.0.0-20210728143218-7b4eea64cf58/go.mod h1:DXv8WO4yhMYhSNPKjeNKa5WY9YCIEBRbNzFFPJbWO6Y=
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pkumza/numcn v1.0.0 h1:ZT5cf9IJkUZgRgEtCiNNykk0RwsrKXSTsvDHOwUTzgE= github.com/pkumza/numcn v1.0.0 h1:ZT5cf9IJkUZgRgEtCiNNykk0RwsrKXSTsvDHOwUTzgE=
github.com/pkumza/numcn v1.0.0/go.mod h1:QSeH+al9dWCd8di5HZM/ZqHqhZmUKfph572e9Ev/ETc= github.com/pkumza/numcn v1.0.0/go.mod h1:QSeH+al9dWCd8di5HZM/ZqHqhZmUKfph572e9Ev/ETc=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c h1:ncq/mPwQF4JjgDlrVEn3C11VoGHZN7m8qihwgMEtzYw= github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55 h1:o4JXh1EVt9k/+g42oCprj/FisM4qX9L3sZB3upGN2ZU=
github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c/go.mod h1:OmDBASR4679mdNQnz2pUhc2G8CO2JrUAVFDRBDP/hJE= github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55/go.mod h1:OmDBASR4679mdNQnz2pUhc2G8CO2JrUAVFDRBDP/hJE=
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec h1:W09IVJc94icq4NjY3clb7Lk8O1qJ8BdBEF8z0ibU0rE= github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec h1:W09IVJc94icq4NjY3clb7Lk8O1qJ8BdBEF8z0ibU0rE=
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo= github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo=
github.com/rogpeppe/go-internal v1.12.0 h1:exVL4IDcn6na9z1rAb56Vxr+CgyK3nn3O+epU5NdKM8= github.com/shirou/gopsutil/v4 v4.25.11 h1:X53gB7muL9Gnwwo2evPSE+SfOrltMoR6V3xJAXZILTY=
github.com/rogpeppe/go-internal v1.12.0/go.mod h1:E+RYuTGaKKdloAfM02xzb0FW3Paa99yedzYV+kq4uf4= github.com/shirou/gopsutil/v4 v4.25.11/go.mod h1:EivAfP5x2EhLp2ovdpKSozecVXn1TmuG7SMzs/Wh4PU=
github.com/shirou/gopsutil/v3 v3.24.4 h1:dEHgzZXt4LMNm+oYELpzl9YCqV65Yr/6SfrvgRBtXeU=
github.com/shirou/gopsutil/v3 v3.24.4/go.mod h1:lTd2mdiOspcqLgAnr9/nGi71NkeMpWKdmhuxm9GusH8=
github.com/shoenig/go-m1cpu v0.1.6 h1:nxdKQNcEB6vzgA2E2bvzKIYRuNj7XNJ4S/aRSwKzFtM=
github.com/shoenig/go-m1cpu v0.1.6/go.mod h1:1JJMcUBvfNwpq05QDQVAnx3gUHr9IYF7GNg9SUEw2VQ=
github.com/shoenig/test v0.6.4 h1:kVTaSd7WLz5WZ2IaoM0RSzRsUD+m8wRR+5qvntpn4LU=
github.com/shoenig/test v0.6.4/go.mod h1:byHiCGXqrVaflBLAMq/srcZIHynQPQgeyvkvXnjqq0k=
github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ= github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ=
github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA=
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U=
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U=
github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= github.com/tetratelabs/wazero v1.9.0 h1:IcZ56OuxrtaEz8UYNRHBrUa9bYeX9oVY93KspZZBf/I=
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= github.com/tetratelabs/wazero v1.9.0/go.mod h1:TSbcXCfFP0L2FGkRPxHphadXPjo1T6W+CseNNY7EkjM=
github.com/tetratelabs/wazero v1.5.0 h1:Yz3fZHivfDiZFUXnWMPUoiW7s8tC1sjdBtlJn08qYa0= github.com/tidwall/gjson v1.18.0 h1:FIDeeyB800efLX89e5a8Y0BNH+LOngJyGrIWxG2FKQY=
github.com/tetratelabs/wazero v1.5.0/go.mod h1:0U0G41+ochRKoPKCJlh0jMg1CHkyfK8kDqiirMmKY8A= github.com/tidwall/gjson v1.18.0/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk=
github.com/tidwall/gjson v1.17.3 h1:bwWLZU7icoKRG+C+0PNwIKC6FCJO/Q3p2pZvuP0jN94=
github.com/tidwall/gjson v1.17.3/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk=
github.com/tidwall/match v1.1.1 h1:+Ho715JplO36QYgwN9PGYNhgZvoUSc9X2c80KVTi+GA=
github.com/tidwall/match v1.1.1/go.mod h1:eRSPERbgtNPcGhD8UCthc6PmLEQXEWd3PRB5JTxsfmM= github.com/tidwall/match v1.1.1/go.mod h1:eRSPERbgtNPcGhD8UCthc6PmLEQXEWd3PRB5JTxsfmM=
github.com/tidwall/pretty v1.2.0 h1:RWIZEg2iJ8/g6fDDYzMpobmaoGh5OLl4AXtGUGPcqCs= github.com/tidwall/match v1.2.0 h1:0pt8FlkOwjN2fPt4bIl4BoNxb98gGHN2ObFEDkrfZnM=
github.com/tidwall/match v1.2.0/go.mod h1:eRSPERbgtNPcGhD8UCthc6PmLEQXEWd3PRB5JTxsfmM=
github.com/tidwall/pretty v1.2.0/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU= github.com/tidwall/pretty v1.2.0/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU=
github.com/tklauser/go-sysconf v0.3.12 h1:0QaGUFOdQaIVdPgfITYzaTegZvdCjmYO52cSFAEVmqU= github.com/tidwall/pretty v1.2.1 h1:qjsOFOWWQl+N3RsoF5/ssm1pHmJJwhjlSbZ51I6wMl4=
github.com/tklauser/go-sysconf v0.3.12/go.mod h1:Ho14jnntGE1fpdOqQEEaiKRpvIavV0hSfmBq8nJbHYI= github.com/tidwall/pretty v1.2.1/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU=
github.com/tklauser/numcpus v0.6.1 h1:ng9scYS7az0Bk4OZLvrNXNSAO2Pxr1XXRAPyjhIx+Fk= github.com/tklauser/go-sysconf v0.3.16 h1:frioLaCQSsF5Cy1jgRBrzr6t502KIIwQ0MArYICU0nA=
github.com/tklauser/numcpus v0.6.1/go.mod h1:1XfjsgE2zo8GVw7POkMbHENHzVg3GzmoZ9fESEdAacY= github.com/tklauser/go-sysconf v0.3.16/go.mod h1:/qNL9xxDhc7tx3HSRsLWNnuzbVfh3e7gh/BmM179nYI=
github.com/wcharczuk/go-chart/v2 v2.1.1 h1:2u7na789qiD5WzccZsFz4MJWOJP72G+2kUuJoSNqWnE= github.com/tklauser/numcpus v0.11.0 h1:nSTwhKH5e1dMNsCdVBukSZrURJRoHbSEQjdEbY+9RXw=
github.com/wcharczuk/go-chart/v2 v2.1.1/go.mod h1:CyCAUt2oqvfhCl6Q5ZvAZwItgpQKZOkCJGb+VGv6l14= github.com/tklauser/numcpus v0.11.0/go.mod h1:z+LwcLq54uWZTX0u/bGobaV34u6V7KNlTZejzM6/3MQ=
github.com/wdvxdr1123/ZeroBot v1.7.5-0.20240829093431-bea5257d1a2b h1:DGVFcw0yQxLXmqWmVCqt5AfJd3V1Sea6af7hB0ynCfg= github.com/wcharczuk/go-chart/v2 v2.1.2 h1:Y17/oYNuXwZg6TFag06qe8sBajwwsuvPiJJXcUcLL6E=
github.com/wdvxdr1123/ZeroBot v1.7.5-0.20240829093431-bea5257d1a2b/go.mod h1:C86nQ0gIdAri4K2vg8IIQIslt08zzrKMcqYt8zhkx1M= github.com/wcharczuk/go-chart/v2 v2.1.2/go.mod h1:Zi4hbaqlWpYajnXB2K22IUYVXRXaLfSGNNR7P4ukyyQ=
github.com/wdvxdr1123/ZeroBot v1.8.2-0.20251002074418-56567b7fc282 h1:YctW/t88sQ0H8cJ69PWULU6xWfh8kNsX/XgCpW2OPHw=
github.com/wdvxdr1123/ZeroBot v1.8.2-0.20251002074418-56567b7fc282/go.mod h1:trueIIVRywKJa3ov4QphzVvzYzgCNrlXdf9JvPJOFW8=
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
github.com/yusufpapurcu/wmi v1.2.4 h1:zFUKzehAFReQwLys1b/iSMl+JQGSCSjtVqQn9bBrPo0= github.com/yusufpapurcu/wmi v1.2.4 h1:zFUKzehAFReQwLys1b/iSMl+JQGSCSjtVqQn9bBrPo0=
github.com/yusufpapurcu/wmi v1.2.4/go.mod h1:SBZ9tNy3G9/m5Oi98Zks0QjeHVDvuK0qfxQmPyzfmi0= github.com/yusufpapurcu/wmi v1.2.4/go.mod h1:SBZ9tNy3G9/m5Oi98Zks0QjeHVDvuK0qfxQmPyzfmi0=
gitlab.com/gomidi/midi/v2 v2.1.7 h1:lIjVXH+bnGG04j/kUVOFILt0BQvBeGz8Kyz0l6aM830= gitlab.com/gomidi/midi/v2 v2.3.16 h1:yufWSENyjnJ4LFQa9BerzUm4E4aLfTyzw5nmnCteO0c=
gitlab.com/gomidi/midi/v2 v2.1.7/go.mod h1:Cj6K9VH5GhYvPgL2JddxHBmZiP3nxKxB5XyTxiXvL9U= gitlab.com/gomidi/midi/v2 v2.3.16/go.mod h1:jDpP4O4skYi+7iVwt6Zyp18bd2M4hkjtMuw2cmgKgfw=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20190325154230-a5d413f7728c/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20190325154230-a5d413f7728c/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20191205180655-e7c4368fe9dd/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20191205180655-e7c4368fe9dd/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
golang.org/x/crypto v0.22.0 h1:g1v0xeRhjcugydODzvb3mEM9SQ0HGp9s/nh3COQ/C30= golang.org/x/crypto v0.13.0/go.mod h1:y6Z2r+Rw4iayiXXAIxJIDAJ1zMW4yaTpebo8fPOliYc=
golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8 h1:idBdZTd9UioThJp8KpM/rTSinK/ChZFBE43/WtIy8zg= golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU=
golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/crypto v0.23.0/go.mod h1:CKFgDieR+mRhux2Lsu27y0fO304Db0wZe70UKqHu0v8=
golang.org/x/image v0.0.0-20190220214146-31aff87c08e9/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= golang.org/x/crypto v0.31.0/go.mod h1:kDsLvtWBEx7MV9tJOj9bnXsPbxwJQ6csT/x4KIN4Ssk=
golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= golang.org/x/crypto v0.43.0 h1:dduJYIi3A3KOfdGOHX8AVZ/jGiyPa3IbBozJ5kNuE04=
golang.org/x/crypto v0.43.0/go.mod h1:BFbav4mRNlXJL4wNeejLpWxB7wMbc79PdRGhWKncxR0=
golang.org/x/image v0.0.0-20191009234506-e7c1f5e7dbb8/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= golang.org/x/image v0.0.0-20191009234506-e7c1f5e7dbb8/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
golang.org/x/image v0.11.0/go.mod h1:bglhjqbqVuEb9e9+eNR45Jfu7D+T4Qan+NhQk8Ck2P8= golang.org/x/image v0.18.0/go.mod h1:4yyo5vMFQjVjUcVk4jEQcU9MGy/rulF5WvUILseCM2E=
golang.org/x/image v0.16.0 h1:9kloLAKhUufZhA12l5fwnx2NZW39/we1UhBesW433jw= golang.org/x/image v0.32.0 h1:6lZQWq75h7L5IWNk0r+SCpUJ6tUVd3v4ZHnbRKLkUDQ=
golang.org/x/image v0.16.0/go.mod h1:ugSZItdV4nOxyqp56HmXwH0Ry0nBCpjnZdpDaIHdoPs= golang.org/x/image v0.32.0/go.mod h1:/R37rrQmKXtO6tYXAjtDLwQgFLHmhW+V6ayXlxzP2Pc=
golang.org/x/mobile v0.0.0-20190415191353-3e0bab5405d6 h1:vyLBGJPIl9ZYbcQFM2USFmJBK6KI+t+z6jL0lbwjrnc= golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mobile v0.0.0-20190415191353-3e0bab5405d6/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o=
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
golang.org/x/mod v0.16.0 h1:QX4fJ0Rr5cPQCF7O9lh9Se4pmwfwskqZfq5moyldzic= golang.org/x/mod v0.12.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
golang.org/x/mod v0.15.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
golang.org/x/mod v0.17.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
golang.org/x/mod v0.28.0 h1:gQBtGhjxykdjY9YhZpSlZIsbnaE2+PgjfLWUQTnoZ1U=
golang.org/x/mod v0.28.0/go.mod h1:yfB/L0NOf/kmEbXjzCPOx1iK1fRutOydrCMsqRhEBxI=
golang.org/x/net v0.0.0-20180218175443-cbe0f9307d01/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180218175443-cbe0f9307d01/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg=
golang.org/x/net v0.24.0 h1:1PcaxkF854Fu3+lvBIx5SYn9wRlBzzcnHZSiaFFAb0w= golang.org/x/net v0.15.0/go.mod h1:idbUs1IY1+zTqbi8yxTbhexhEEk5ur9LInksu6HrEpk=
golang.org/x/net v0.24.0/go.mod h1:2Q7sJY5mzlzWjKtYUEXSlBWCdyaioyXzRB2RtU8KVE8= golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44=
golang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM=
golang.org/x/net v0.33.0/go.mod h1:HXLR5J+9DxmrqMwG9qjGCxZ+zKXxBru04zlTvWlWuN4=
golang.org/x/net v0.46.0 h1:giFlY12I07fugqwPuWJi68oOnpfqFnJIJzaIIm2JVV4=
golang.org/x/net v0.46.0/go.mod h1:Q9BGdFy1y4nkUwiLvT5qtyhAnEHgnQ/zd8PfU6nc210=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.3.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y=
golang.org/x/sync v0.6.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
golang.org/x/sync v0.10.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
golang.org/x/sync v0.17.0 h1:l60nONMj9l5drqw6jlhIELNv9I0A4OFgRsG9k2oT9Ug=
golang.org/x/sync v0.17.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190429190828-d89cdac9e872/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190626150813-e07cf5db2756/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201204225414-ed752295db88/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201204225414-ed752295db88/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.19.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.20.0 h1:Od9JTbYCk261bKm4M/mw7AklTlFYIa0bIp9BgSm1S8Y=
golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.28.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.38.0 h1:3yZWxaJjBmCWXqhN1qh02AkOnCQ1poK6oF+a7xWL6Gc=
golang.org/x/sys v0.38.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
golang.org/x/telemetry v0.0.0-20240228155512-f48c80bd79b2/go.mod h1:TeRTkGYfJXctD9OcfyVLyj2J3IxLnKwHJR8f4D8a3YE=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo=
golang.org/x/term v0.12.0/go.mod h1:owVbMEjm3cBLCHdkQu9b1opXd4ETQWc3BhuQGKgXgvU=
golang.org/x/term v0.17.0/go.mod h1:lLRBjIVuehSbZlaOtGMbcMncT+aqLLLmKrsjNrUguwk=
golang.org/x/term v0.20.0/go.mod h1:8UkIAJTvZgivsXaD6/pH6U9ecQzZ45awqEOzuCvwpFY=
golang.org/x/term v0.27.0/go.mod h1:iMsnZpn0cago0GOrHO2+Y7u7JPn5AylBrcoWkElMTSM=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
golang.org/x/text v0.12.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
golang.org/x/text v0.15.0 h1:h1V/4gjBv8v9cjcR6+AR5+/cIYK5N/WAgiv4xlsEtAk= golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
golang.org/x/text v0.15.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= golang.org/x/text v0.15.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
golang.org/x/text v0.16.0/go.mod h1:GhwF1Be+LQoKShO3cGOHzqOgRrGaYc9AvblQOmPVHnI=
golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ=
golang.org/x/text v0.30.0 h1:yznKA/E9zq54KzlzBEAWn1NXSQ8DIp/NYMy88xJjl4k=
golang.org/x/text v0.30.0/go.mod h1:yDdHFIX9t+tORqspjENWgzaCVXgk0yYnYuSZ8UzzBVM=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0=
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU=
golang.org/x/tools v0.19.0 h1:tfGCXNR1OsFG+sVdLAitlpjAvD/I6dHDKnYrpEZUHkw= golang.org/x/tools v0.13.0/go.mod h1:HvlwmtVNQAhOuCjW7xxvovg8wbNq7LwfXh/k7wXUl58=
golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d/go.mod h1:aiJjzUbINMkxbQROHiO6hDPo2LHcIPhhQsa9DLh0yGk=
golang.org/x/tools v0.37.0 h1:DVSRzp7FwePZW356yEAChSdNcQo6Nsp+fex1SUW09lE=
golang.org/x/tools v0.37.0/go.mod h1:MBN5QPQtLMHVdvsbtarmTNukZDdgwdwlO5qGacAzF0w=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
honnef.co/go/tools v0.1.3/go.mod h1:NgwopIslSNH47DimFoV78dnkksY2EFtX0ajyb3K/las=
modernc.org/cc/v4 v4.21.2 h1:dycHFB/jDc3IyacKipCNSDrjIC0Lm1hyoWOZTRR20Lk= modernc.org/cc/v4 v4.21.2 h1:dycHFB/jDc3IyacKipCNSDrjIC0Lm1hyoWOZTRR20Lk=
modernc.org/cc/v4 v4.21.2/go.mod h1:HM7VJTZbUCR3rV8EYBi9wxnJ0ZBRiGE5OeGXNA0IsLQ=
modernc.org/ccgo/v4 v4.17.8 h1:yyWBf2ipA0Y9GGz/MmCmi3EFpKgeS7ICrAFes+suEbs= modernc.org/ccgo/v4 v4.17.8 h1:yyWBf2ipA0Y9GGz/MmCmi3EFpKgeS7ICrAFes+suEbs=
modernc.org/ccgo/v4 v4.17.8/go.mod h1:buJnJ6Fn0tyAdP/dqePbrrvLyr6qslFfTbFrCuaYvtA=
modernc.org/fileutil v1.3.0 h1:gQ5SIzK3H9kdfai/5x41oQiKValumqNTDXMvKo62HvE= modernc.org/fileutil v1.3.0 h1:gQ5SIzK3H9kdfai/5x41oQiKValumqNTDXMvKo62HvE=
modernc.org/fileutil v1.3.0/go.mod h1:XatxS8fZi3pS8/hKG2GH/ArUogfxjpEKs3Ku3aK4JyQ=
modernc.org/gc/v2 v2.4.1 h1:9cNzOqPyMJBvrUipmynX0ZohMhcxPtMccYgGOJdOiBw= modernc.org/gc/v2 v2.4.1 h1:9cNzOqPyMJBvrUipmynX0ZohMhcxPtMccYgGOJdOiBw=
modernc.org/mathutil v1.6.0 h1:fRe9+AmYlaej+64JsEEhoWuAYBkOtQiMEU7n/XgfYi4= modernc.org/gc/v2 v2.4.1/go.mod h1:wzN5dK1AzVGoH6XOzc3YZ+ey/jPgYHLuVckd62P0GYU=
modernc.org/mathutil v1.6.0/go.mod h1:Ui5Q9q1TR2gFm0AQRqQUaBWFLAhQpCwNcuhBOSedWPo= modernc.org/mathutil v1.7.1 h1:GCZVGXdaN8gTqB1Mf/usp1Y/hSqgI2vAGGP4jZMCxOU=
modernc.org/memory v1.8.0 h1:IqGTL6eFMaDZZhEWwcREgeMXYwmW83LYW8cROZYkg+E= modernc.org/mathutil v1.7.1/go.mod h1:4p5IwJITfppl0G4sUEDtCr4DthTaT47/N3aT6MhfgJg=
modernc.org/memory v1.8.0/go.mod h1:XPZ936zp5OMKGWPqbD3JShgd/ZoQ7899TUuQqxY+peU= modernc.org/memory v1.11.0 h1:o4QC8aMQzmcwCK3t3Ux/ZHmwFPzE6hf2Y5LbkRs+hbI=
modernc.org/memory v1.11.0/go.mod h1:/JP4VbVC+K5sU2wZi9bHoq2MAkCnrt2r98UGeSK7Mjw=
modernc.org/opt v0.1.3 h1:3XOZf2yznlhC+ibLltsDGzABUGVx8J6pnFMS3E4dcq4= modernc.org/opt v0.1.3 h1:3XOZf2yznlhC+ibLltsDGzABUGVx8J6pnFMS3E4dcq4=
modernc.org/opt v0.1.3/go.mod h1:WdSiB5evDcignE70guQKxYUl14mgWtbClRi5wmkkTX0=
modernc.org/sortutil v1.2.0 h1:jQiD3PfS2REGJNzNCMMaLSp/wdMNieTbKX920Cqdgqc= modernc.org/sortutil v1.2.0 h1:jQiD3PfS2REGJNzNCMMaLSp/wdMNieTbKX920Cqdgqc=
modernc.org/sortutil v1.2.0/go.mod h1:TKU2s7kJMf1AE84OoiGppNHJwvB753OYfNl2WRb++Ss=
modernc.org/strutil v1.2.0 h1:agBi9dp1I+eOnxXeiZawM8F4LawKv4NzGWSaLfyeNZA= modernc.org/strutil v1.2.0 h1:agBi9dp1I+eOnxXeiZawM8F4LawKv4NzGWSaLfyeNZA=
modernc.org/strutil v1.2.0/go.mod h1:/mdcBmfOibveCTBxUl5B5l6W+TTH1FXPLHZE6bTosX0=
modernc.org/token v1.1.0 h1:Xl7Ap9dKaEs5kLoOQeQmPWevfnk/DM5qcLcYlA8ys6Y= modernc.org/token v1.1.0 h1:Xl7Ap9dKaEs5kLoOQeQmPWevfnk/DM5qcLcYlA8ys6Y=
modernc.org/token v1.1.0/go.mod h1:UGzOrNV1mAFSEB63lOFHIpNRUVMvYTc6yu1SMY/XTDM=

View File

@ -5,11 +5,11 @@ schema = 3
version = "v1.1.1" version = "v1.1.1"
hash = "sha256-hKshA0K92bKuK92mmtM0osVmqLJcSbeobeWSDpQoRCo=" hash = "sha256-hKshA0K92bKuK92mmtM0osVmqLJcSbeobeWSDpQoRCo="
[mod."github.com/FloatTech/AnimeAPI"] [mod."github.com/FloatTech/AnimeAPI"]
version = "v1.7.1-0.20240530072450-71c23d2f01f8" version = "v1.7.1-0.20250926171956-ba37dfebfc4a"
hash = "sha256-NUYNGhjVW5bdpWIeKjBhnVTsjf6OXNqCcqzrRd3c+gE=" hash = "sha256-wtr9hgHSPsD5NqSmI3Lo8RVWE+cNMIUGo2bcsKTN8Gg="
[mod."github.com/FloatTech/floatbox"] [mod."github.com/FloatTech/floatbox"]
version = "v0.0.0-20240505082030-226ec6713e14" version = "v0.0.0-20251002074805-f95cbc7edb31"
hash = "sha256-v296D9T1QzFmcHQJNxJvx7sMtK+Jd1TUHXWqZtIvvf4=" hash = "sha256-c50unGhF0JVPHN8geZM/YYQKgGqJgCtVksh4Ij1Pg+4="
[mod."github.com/FloatTech/gg"] [mod."github.com/FloatTech/gg"]
version = "v1.1.3" version = "v1.1.3"
hash = "sha256-7K/R2mKjUHVnoJ3b1wDObJ5Un2Htj59Y97G1Ja1tuPo=" hash = "sha256-7K/R2mKjUHVnoJ3b1wDObJ5Un2Htj59Y97G1Ja1tuPo="
@ -17,41 +17,47 @@ schema = 3
version = "v0.2.2-0.20230413152719-e101cc3606ef" version = "v0.2.2-0.20230413152719-e101cc3606ef"
hash = "sha256-2okFyPQSYIxrc8hxICsbjEM9xq25a3I2A4wmDIYFCg8=" hash = "sha256-2okFyPQSYIxrc8hxICsbjEM9xq25a3I2A4wmDIYFCg8="
[mod."github.com/FloatTech/rendercard"] [mod."github.com/FloatTech/rendercard"]
version = "v0.1.1" version = "v0.2.0"
hash = "sha256-w5GcscWQzgdcfC0Vw4u+7/NipP3PTB2UrVcxki88IPo=" hash = "sha256-fgntEYGh2mEl618hM13kb0GGeQEXdP+lochYX8F2OXs="
[mod."github.com/FloatTech/sqlite"] [mod."github.com/FloatTech/sqlite"]
version = "v1.6.3" version = "v1.7.2"
hash = "sha256-zWPByEMi89ms67ubPg0fAPIRxfpBC2IRKc0iNVLqkPU=" hash = "sha256-R9QaP5FQwtWpHdbCoNX/rYOS/CgkIeRdFB9cwJ4n/JM="
[mod."github.com/FloatTech/ttl"] [mod."github.com/FloatTech/ttl"]
version = "v0.0.0-20240716161252-965925764562" version = "v0.0.0-20250224045156-012b1463287d"
hash = "sha256-/XjfdVXEzYgeM+OYuyy76tf13lO91vCcwpjWgkRGteU=" hash = "sha256-C5xBt0roPgahradCOTgkhL+j5bvoSXmGwdqcu0aSczc="
[mod."github.com/FloatTech/zbpctrl"] [mod."github.com/FloatTech/zbpctrl"]
version = "v1.6.2-0.20240904160347-1317e11a15bb" version = "v1.7.0"
hash = "sha256-x0ZR2bnkboEIjjRFMtMAN0T34BP9BPs7r3AwT3BCyzo=" hash = "sha256-HDDnE0oktWJH1tkxuQwUUbeJhmVwY5fyc/vR72D2mkU="
[mod."github.com/FloatTech/zbputils"] [mod."github.com/FloatTech/zbputils"]
version = "v1.7.2-0.20240822065525-5ea6811ed91c" version = "v1.7.2-0.20251002080916-b554b7039913"
hash = "sha256-ouAExps1iPCcD1AmOxyhRXMBGHBDXvUGkplcnQCf3Bg=" hash = "sha256-9z7c79uuFl2LKaCgW1gQN5lmMjgKIcKrakcBlb3zJ90="
[mod."github.com/PuerkitoBio/goquery"]
version = "v1.10.3"
hash = "sha256-Mth7nYm/MtcOhPMbHj7gXF+Mot7eDUBVN570RitGR/c="
[mod."github.com/RomiChan/syncx"] [mod."github.com/RomiChan/syncx"]
version = "v0.0.0-20240418144900-b7402ffdebc7" version = "v0.0.0-20240418144900-b7402ffdebc7"
hash = "sha256-L1j1vgiwqXpF9pjMoRRlrQUHzoULisw/01plaEAwxs4=" hash = "sha256-L1j1vgiwqXpF9pjMoRRlrQUHzoULisw/01plaEAwxs4="
[mod."github.com/RomiChan/websocket"] [mod."github.com/RomiChan/websocket"]
version = "v1.4.3-0.20220227141055-9b2c6168c9c5" version = "v1.4.3-0.20251002072000-d3eb41798438"
hash = "sha256-Adx+gvqB+CCoUXx7ebIaBDjVkav+wS5qZPmaqcApBWA=" hash = "sha256-vLu9Va+9AbOIdh1LEetz5JlJK0P2IXKsYRvCdAO8tYw="
[mod."github.com/Tnze/go-mc"]
version = "v1.20.2"
hash = "sha256-Nu4PXNxeARH0itm6yIIplFaywL2yQnPJFksmmuyIptI="
[mod."github.com/adamzy/cedar-go"] [mod."github.com/adamzy/cedar-go"]
version = "v0.0.0-20170805034717-80a9c64b256d" version = "v0.0.0-20170805034717-80a9c64b256d"
hash = "sha256-N19KTxh70IUBqnchFuWkrJD8uuFOIVqv1iSuN3YFIT0=" hash = "sha256-N19KTxh70IUBqnchFuWkrJD8uuFOIVqv1iSuN3YFIT0="
[mod."github.com/ajstarks/svgo"] [mod."github.com/ajstarks/svgo"]
version = "v0.0.0-20200320125537-f189e35d30ca" version = "v0.0.0-20211024235047-1546f124cd8b"
hash = "sha256-ALeRuEJN9jHjGb4wNKJcxC59vVx8Tj7hHikEGkaZZ0s=" hash = "sha256-sPwt5sImKFk949TzUeYEF2UiJDqHxXFJKRL2Y7JWJ6Y="
[mod."github.com/andybalholm/cascadia"]
version = "v1.3.3"
hash = "sha256-jv7ZshpSd7FZzKKN6hqlUgiR8C3y85zNIS/hq7g76Ho="
[mod."github.com/antchfx/htmlquery"] [mod."github.com/antchfx/htmlquery"]
version = "v1.3.1" version = "v1.3.4"
hash = "sha256-4ZzKk7Z+vH8ytisdtcZz/Y0MbnVVhruiO/7gtUy3ouQ=" hash = "sha256-nrtIgRgdOvo0iIQyrhHOFKOmoT8e2gduUsct3f5zDNA="
[mod."github.com/antchfx/xpath"] [mod."github.com/antchfx/xpath"]
version = "v1.3.0" version = "v1.3.5"
hash = "sha256-SU+Tnf5c9vsDCrY1BVKjqYLhB91xt9oHBS5bicbs2cA=" hash = "sha256-AVM0rR81hgVAI0QVzlz4WijFUjByf6Zew3ZwuikKw2Q="
[mod."github.com/blend/go-sdk"]
version = "v1.20220411.3"
hash = "sha256-yxrf24hru8NeTPUmoaJG1PcmHE5pn/U36Sj9Qg+JVqg="
[mod."github.com/corona10/goimagehash"] [mod."github.com/corona10/goimagehash"]
version = "v1.1.0" version = "v1.1.0"
hash = "sha256-HyS8nc7kUNnDaVBDzJ9Ym4pRs83YB4M2vHSRwfm6mr4=" hash = "sha256-HyS8nc7kUNnDaVBDzJ9Ym4pRs83YB4M2vHSRwfm6mr4="
@ -64,21 +70,30 @@ schema = 3
[mod."github.com/dustin/go-humanize"] [mod."github.com/dustin/go-humanize"]
version = "v1.0.1" version = "v1.0.1"
hash = "sha256-yuvxYYngpfVkUg9yAmG99IUVmADTQA0tMbBXe0Fq0Mc=" hash = "sha256-yuvxYYngpfVkUg9yAmG99IUVmADTQA0tMbBXe0Fq0Mc="
[mod."github.com/ebitengine/oto/v3"]
version = "v3.4.0"
hash = "sha256-8JU4iu+2pUKWVWMpEe8EAZ8FVo3MZdILu82vVVmDSVY="
[mod."github.com/ebitengine/purego"]
version = "v0.9.0"
hash = "sha256-jSlhGjhuYf15zHy7shSKR1rQN1sRyH5zXAzWqy+huHY="
[mod."github.com/ericpauley/go-quantize"] [mod."github.com/ericpauley/go-quantize"]
version = "v0.0.0-20200331213906-ae555eb2afa4" version = "v0.0.0-20200331213906-ae555eb2afa4"
hash = "sha256-sMN6D7IlDpDqUWM8ppoE5Sdb7DvLAJaN6qAucBWJ3rs=" hash = "sha256-sMN6D7IlDpDqUWM8ppoE5Sdb7DvLAJaN6qAucBWJ3rs="
[mod."github.com/faiface/beep"]
version = "v1.1.0"
hash = "sha256-66qAbnJjUjhXofxlGCa6G1+vjQcSTyN/POCZvYzHaQo="
[mod."github.com/fumiama/ahsai"] [mod."github.com/fumiama/ahsai"]
version = "v0.1.0" version = "v0.1.1"
hash = "sha256-lSoos+SFjALcL0ZYPsbOb8wntwn2fcubvSsz0YKgL9c=" hash = "sha256-knYw0R5fhjE/asc/TwlGJDzVr+Oaj8sH7kr7x6Mqs3E="
[mod."github.com/fumiama/cron"] [mod."github.com/fumiama/cron"]
version = "v1.3.0" version = "v1.3.0"
hash = "sha256-/sN7X8dKXQgv8J+EDzVUB+o+AY9gBC8e1C6sYhaTy1k=" hash = "sha256-/sN7X8dKXQgv8J+EDzVUB+o+AY9gBC8e1C6sYhaTy1k="
[mod."github.com/fumiama/deepinfra"]
version = "v0.0.0-20250924162107-cf156d49a0fa"
hash = "sha256-D0lgA7jBDLE8v9ePDiWwH439eB5+cDlj2fKRAc0wUms="
[mod."github.com/fumiama/go-base16384"] [mod."github.com/fumiama/go-base16384"]
version = "v1.7.0" version = "v1.7.0"
hash = "sha256-vTAsBBYe2ISzb2Nba5E96unodZSkhMcqo6hbwR01nz8=" hash = "sha256-vTAsBBYe2ISzb2Nba5E96unodZSkhMcqo6hbwR01nz8="
[mod."github.com/fumiama/go-onebot-agent"]
version = "v0.0.0-20250926145606-37ebfa6131c8"
hash = "sha256-oq1SSyddsXhsFvSHFhqSEAF9SH03b2jI0KvGbsFPXYQ="
[mod."github.com/fumiama/go-registry"] [mod."github.com/fumiama/go-registry"]
version = "v0.2.7" version = "v0.2.7"
hash = "sha256-Rjl+z0Hlp2LMi8+pnFe5HrxctyHMi7UPiK33g/OgLdA=" hash = "sha256-Rjl+z0Hlp2LMi8+pnFe5HrxctyHMi7UPiK33g/OgLdA="
@ -86,8 +101,8 @@ schema = 3
version = "v0.2.0" version = "v0.2.0"
hash = "sha256-2kULBi1sXsFDX2g/KRFmCGkwF60o/UXacNUbIYa/cvw=" hash = "sha256-2kULBi1sXsFDX2g/KRFmCGkwF60o/UXacNUbIYa/cvw="
[mod."github.com/fumiama/gofastTEA"] [mod."github.com/fumiama/gofastTEA"]
version = "v0.0.10" version = "v0.1.3"
hash = "sha256-FOCbkXoS8s/K54yZbhX5pmaN/ouELnCHZoNS8a90VAg=" hash = "sha256-/Qu57mkkFt7aFufhlkMYPgrWj5XCGbuM28EHDD8w4XY="
[mod."github.com/fumiama/gotracemoe"] [mod."github.com/fumiama/gotracemoe"]
version = "v0.0.3" version = "v0.0.3"
hash = "sha256-O3cDkVXu5NG1ZtzubxhH+S91zfgu4uH1L+OiSGYSNXQ=" hash = "sha256-O3cDkVXu5NG1ZtzubxhH+S91zfgu4uH1L+OiSGYSNXQ="
@ -97,39 +112,42 @@ schema = 3
[mod."github.com/fumiama/jieba"] [mod."github.com/fumiama/jieba"]
version = "v0.0.0-20221203025406-36c17a10b565" version = "v0.0.0-20221203025406-36c17a10b565"
hash = "sha256-DvDx1pdldkdaSszrbadM/VwqT9TTSmWl6G6a+ysXYEM=" hash = "sha256-DvDx1pdldkdaSszrbadM/VwqT9TTSmWl6G6a+ysXYEM="
[mod."github.com/fumiama/orbyte"]
version = "v0.0.0-20251002065953-3bb358367eb5"
hash = "sha256-mRQwhR0v922UXlJ7lXo/osv21K8kZDaHx3DsBCjmzoo="
[mod."github.com/fumiama/slowdo"] [mod."github.com/fumiama/slowdo"]
version = "v0.0.0-20241001074058-27c4fe5259a4" version = "v0.0.0-20241001074058-27c4fe5259a4"
hash = "sha256-rsV3MKRCSOBMIgJXFCGbCHRY2aBAb32ftU49hT3GjqY=" hash = "sha256-rsV3MKRCSOBMIgJXFCGbCHRY2aBAb32ftU49hT3GjqY="
[mod."github.com/fumiama/terasu"] [mod."github.com/fumiama/terasu"]
version = "v0.0.0-20240507144117-547a591149c0" version = "v1.0.1"
hash = "sha256-ZZG5/Ckq4R0eojmiuli5ZRToDNQt4VeRwdy0jjVCvbg=" hash = "sha256-7pe0ByxkmqWeYsxWNwa8urfv6lK1cEPrW1b5l86eM1Y="
[mod."github.com/fumiama/unibase2n"] [mod."github.com/fumiama/unibase2n"]
version = "v0.0.0-20240530074540-ec743fd5a6d6" version = "v0.0.0-20240530074540-ec743fd5a6d6"
hash = "sha256-I3xNzjrj5y0fy0dfa75V57GanfmHIHmubEn9/y0BBHw=" hash = "sha256-I3xNzjrj5y0fy0dfa75V57GanfmHIHmubEn9/y0BBHw="
[mod."github.com/gabriel-vasile/mimetype"] [mod."github.com/gabriel-vasile/mimetype"]
version = "v1.0.4" version = "v1.4.10"
hash = "sha256-5hl9zBo3nkPt8dZfcLoOix8lAKLm3qIkWhopoS4V34E=" hash = "sha256-ha8NJOAWcmEfQaybfNxyUupt0+zlWxoAKLjCacew43s="
[mod."github.com/go-ole/go-ole"] [mod."github.com/go-ole/go-ole"]
version = "v1.2.6" version = "v1.3.0"
hash = "sha256-+oxitLeJxYF19Z6g+6CgmCHJ1Y5D8raMi2Cb3M6nXCs=" hash = "sha256-tF8t3VcV71jQ4jbPL91BwR59AKDpUAFV1waIKzkXJu8="
[mod."github.com/golang/freetype"] [mod."github.com/golang/freetype"]
version = "v0.0.0-20170609003504-e2365dfdc4a0" version = "v0.0.0-20170609003504-e2365dfdc4a0"
hash = "sha256-AHAFBd20/tqxohkWyQkui2bUef9i1HWYgk9LOIFErvA=" hash = "sha256-AHAFBd20/tqxohkWyQkui2bUef9i1HWYgk9LOIFErvA="
[mod."github.com/golang/groupcache"] [mod."github.com/golang/groupcache"]
version = "v0.0.0-20210331224755-41bb18bfe9da" version = "v0.0.0-20241129210726-2c02b8208cf8"
hash = "sha256-7Gs7CS9gEYZkbu5P4hqPGBpeGZWC64VDwraSKFF+VR0=" hash = "sha256-AdLZ3dJLe/yduoNvZiXugZxNfmwJjNQyQGsIdzYzH74="
[mod."github.com/google/uuid"] [mod."github.com/google/uuid"]
version = "v1.6.0" version = "v1.6.0"
hash = "sha256-VWl9sqUzdOuhW0KzQlv0gwwUQClYkmZwSydHG2sALYw=" hash = "sha256-VWl9sqUzdOuhW0KzQlv0gwwUQClYkmZwSydHG2sALYw="
[mod."github.com/hajimehoshi/oto"] [mod."github.com/gopxl/beep/v2"]
version = "v0.7.1" version = "v2.1.1"
hash = "sha256-eRgbEbsziY5F0oI7wAe29FepZG7uGmq2M4deouDHcXI=" hash = "sha256-JLCUJCG+VvNlVF296JWIOUvvUFHlqEAJvZfw853qwwU="
[mod."github.com/jfreymuth/oggvorbis"] [mod."github.com/jfreymuth/oggvorbis"]
version = "v1.0.1" version = "v1.0.5"
hash = "sha256-DpkiTLxAA/iCoiylpNRvMzvaDWtK+U4UMJYNnnCmJMU=" hash = "sha256-jphTCaPr34ZT9Id4ZZ6zU9Vnxzy6cTjCwjpQ819eGV0="
[mod."github.com/jfreymuth/vorbis"] [mod."github.com/jfreymuth/vorbis"]
version = "v1.0.0" version = "v1.0.2"
hash = "sha256-6kTol+g3NnZ3MazD786fvraw7ydUf0RWNBzHpzgN9Jk=" hash = "sha256-gVS+/PZ5pDnswpTQNZILcrx5ZNq9ShXd6vXn7Jabes4="
[mod."github.com/jinzhu/gorm"] [mod."github.com/jinzhu/gorm"]
version = "v1.9.16" version = "v1.9.16"
hash = "sha256-qKEwgNE8NxcX1uzT20LwC1TKVmve/nIy+oxdAKlxAuc=" hash = "sha256-qKEwgNE8NxcX1uzT20LwC1TKVmve/nIy+oxdAKlxAuc="
@ -139,12 +157,12 @@ schema = 3
[mod."github.com/jozsefsallai/gophersauce"] [mod."github.com/jozsefsallai/gophersauce"]
version = "v1.0.1" version = "v1.0.1"
hash = "sha256-29DsfnGmK51DPunR/leRBKCcokN/yLoB7S2HxCsqtgY=" hash = "sha256-29DsfnGmK51DPunR/leRBKCcokN/yLoB7S2HxCsqtgY="
[mod."github.com/json-iterator/go"]
version = "v1.1.12"
hash = "sha256-To8A0h+lbfZ/6zM+2PpRpY3+L6725OPC66lffq6fUoM="
[mod."github.com/kanrichan/resvg-go"] [mod."github.com/kanrichan/resvg-go"]
version = "v0.0.2-0.20231001163256-63db194ca9f5" version = "v0.0.2-0.20231001163256-63db194ca9f5"
hash = "sha256-plRZ3yhyCafCXmAD4vnFUoCTRsHmLp7Jn9gFKcEKbds=" hash = "sha256-plRZ3yhyCafCXmAD4vnFUoCTRsHmLp7Jn9gFKcEKbds="
[mod."github.com/kr/text"]
version = "v0.2.0"
hash = "sha256-fadcWxZOORv44oak3jTxm6YcITcFxdGt4bpn869HxUE="
[mod."github.com/lithammer/fuzzysearch"] [mod."github.com/lithammer/fuzzysearch"]
version = "v1.1.8" version = "v1.1.8"
hash = "sha256-aMMRcrlUc9CBiiNkcnWWn4hfNMNyVhrAt67kvP4D4Do=" hash = "sha256-aMMRcrlUc9CBiiNkcnWWn4hfNMNyVhrAt67kvP4D4Do="
@ -158,23 +176,35 @@ schema = 3
version = "v0.0.0-20231231122217-0372e1059ca5" version = "v0.0.0-20231231122217-0372e1059ca5"
hash = "sha256-Dr1xDbO+eR4Y/EpPgQ/S6g6C5etRFKWr8de77skcJR8=" hash = "sha256-Dr1xDbO+eR4Y/EpPgQ/S6g6C5etRFKWr8de77skcJR8="
[mod."github.com/lufia/plan9stats"] [mod."github.com/lufia/plan9stats"]
version = "v0.0.0-20211012122336-39d0f177ccd0" version = "v0.0.0-20251013123823-9fd1530e3ec3"
hash = "sha256-thb+rkDx5IeWMgw5/5jgu5gZ+6RjJAUXeMgSkJHhRlA=" hash = "sha256-N760qPHHaMcxICyA3Ap/b/3exi40AStu7458VPvC9GI="
[mod."github.com/mattn/go-isatty"] [mod."github.com/mattn/go-isatty"]
version = "v0.0.20" version = "v0.0.20"
hash = "sha256-qhw9hWtU5wnyFyuMbKx+7RB8ckQaFQ8D+8GKPkN3HHQ=" hash = "sha256-qhw9hWtU5wnyFyuMbKx+7RB8ckQaFQ8D+8GKPkN3HHQ="
[mod."github.com/mmcdole/gofeed"]
version = "v1.3.0"
hash = "sha256-GHpqGZvNg+3RSIkVKXrWg6/e8dJD8Y5v2Sx6MzmRlQ0="
[mod."github.com/mmcdole/goxpp"]
version = "v1.1.1"
hash = "sha256-LtZDUtflL06HnDuQiCS6cpoF3VPk+gmABkYXBvdQOz0="
[mod."github.com/modern-go/concurrent"]
version = "v0.0.0-20180306012644-bacd9c7ef1dd"
hash = "sha256-OTySieAgPWR4oJnlohaFTeK1tRaVp/b0d1rYY8xKMzo="
[mod."github.com/modern-go/reflect2"]
version = "v1.0.2"
hash = "sha256-+W9EIW7okXIXjWEgOaMh58eLvBZ7OshW2EhaIpNLSBU="
[mod."github.com/mroth/weightedrand"] [mod."github.com/mroth/weightedrand"]
version = "v1.0.0" version = "v1.0.0"
hash = "sha256-bP+yIaBUY5+oI455mNM8zh14z/SNPaQg44L3RJ0/v/c=" hash = "sha256-bP+yIaBUY5+oI455mNM8zh14z/SNPaQg44L3RJ0/v/c="
[mod."github.com/ncruces/go-strftime"] [mod."github.com/ncruces/go-strftime"]
version = "v0.1.9" version = "v1.0.0"
hash = "sha256-T0iw+UEckzueWHT88PkTnZZixyKCEa+DTLzIiiohuWY=" hash = "sha256-GYIwYDONuv/yTE0AEugCHQbtV3oiBaco93xUNYFcVBQ="
[mod."github.com/nfnt/resize"] [mod."github.com/nfnt/resize"]
version = "v0.0.0-20180221191011-83c6a9932646" version = "v0.0.0-20180221191011-83c6a9932646"
hash = "sha256-yvPV+HlDOyJsiwAcVHQkmtw8DHSXyw+cXHkigXm8rAA=" hash = "sha256-yvPV+HlDOyJsiwAcVHQkmtw8DHSXyw+cXHkigXm8rAA="
[mod."github.com/notnil/chess"] [mod."github.com/notnil/chess"]
version = "v1.9.0" version = "v1.10.0"
hash = "sha256-2bHp/H5hBE/hPMT1HLOBqMaCZ/DYWJMDri26O9Yzoms=" hash = "sha256-hsUOS4rVuMW+UCPJzhsZh3PHCi1Lol12BwKujcICayo="
[mod."github.com/pbnjay/memory"] [mod."github.com/pbnjay/memory"]
version = "v0.0.0-20210728143218-7b4eea64cf58" version = "v0.0.0-20210728143218-7b4eea64cf58"
hash = "sha256-QI+F1oPLOOtwNp8+m45OOoSfYFs3QVjGzE0rFdpF/IA=" hash = "sha256-QI+F1oPLOOtwNp8+m45OOoSfYFs3QVjGzE0rFdpF/IA="
@ -185,71 +215,62 @@ schema = 3
version = "v1.0.0" version = "v1.0.0"
hash = "sha256-cPxqj5tb10+MurN1Lehkk/v8KjaxXpL08+pVgL4x4Hg=" hash = "sha256-cPxqj5tb10+MurN1Lehkk/v8KjaxXpL08+pVgL4x4Hg="
[mod."github.com/power-devops/perfstat"] [mod."github.com/power-devops/perfstat"]
version = "v0.0.0-20210106213030-5aafc221ea8c" version = "v0.0.0-20240221224432-82ca36839d55"
hash = "sha256-ywykDYuqcMt0TvZOz1l9Z6Z2JMTYQw8cP2fT8AtpmX4=" hash = "sha256-ujzuJ1ttQgjHQJEij4O/2+I8DZaUVZQCQgA4ysfqulI="
[mod."github.com/remyoudompheng/bigfft"] [mod."github.com/remyoudompheng/bigfft"]
version = "v0.0.0-20230129092748-24d4a6f8daec" version = "v0.0.0-20230129092748-24d4a6f8daec"
hash = "sha256-vYmpyCE37eBYP/navhaLV4oX4/nu0Z/StAocLIFqrmM=" hash = "sha256-vYmpyCE37eBYP/navhaLV4oX4/nu0Z/StAocLIFqrmM="
[mod."github.com/rogpeppe/go-internal"]
version = "v1.12.0"
hash = "sha256-qvDNCe3l84/LgrA8X4O15e1FeDcazyX91m9LmXGXX6M="
[mod."github.com/shirou/gopsutil/v3"] [mod."github.com/shirou/gopsutil/v3"]
version = "v3.24.4" version = "v3.24.5"
hash = "sha256-ubkBxu9X4LRhI1HqkjsIShR4e8rQsuKQs4VNOIIhZCU=" hash = "sha256-tc+t1u7gf5A+Bd956dYeM8pGbxs9ezQHqKAKfLQLpuQ="
[mod."github.com/shoenig/go-m1cpu"] [mod."github.com/shoenig/go-m1cpu"]
version = "v0.1.6" version = "v0.1.7"
hash = "sha256-hT+JP30BBllsXosK/lo89HV/uxxPLsUyO3dRaDiLnCg=" hash = "sha256-kIKIE9uxHOniFHGgMgtypYB/OPI8VSQxFUHXehlpuYY="
[mod."github.com/sirupsen/logrus"] [mod."github.com/sirupsen/logrus"]
version = "v1.9.3" version = "v1.9.3"
hash = "sha256-EnxsWdEUPYid+aZ9H4/iMTs1XMvCLbXZRDyvj89Ebms=" hash = "sha256-EnxsWdEUPYid+aZ9H4/iMTs1XMvCLbXZRDyvj89Ebms="
[mod."github.com/tetratelabs/wazero"] [mod."github.com/tetratelabs/wazero"]
version = "v1.5.0" version = "v1.9.0"
hash = "sha256-fGdJM4LJrZA9jxHuYVo4EUQ3I1k0IVG3QQCBCgZkeZI=" hash = "sha256-b8D0cDMuDgyjvJ6LFY8REdcL95BIjM27SeOEQWfB0+0="
[mod."github.com/tidwall/gjson"] [mod."github.com/tidwall/gjson"]
version = "v1.17.3" version = "v1.18.0"
hash = "sha256-zui8S4qlfFXNLartKynJbYqeM/MW3f3eDbojIvh/KS8=" hash = "sha256-CO6hqDu8Y58Po6A01e5iTpwiUBQ5khUZsw7czaJHw0I="
[mod."github.com/tidwall/match"] [mod."github.com/tidwall/match"]
version = "v1.1.1"
hash = "sha256-M2klhPId3Q3T3VGkSbOkYl/2nLHnsG+yMbXkPkyrRdg="
[mod."github.com/tidwall/pretty"]
version = "v1.2.0" version = "v1.2.0"
hash = "sha256-esRQGsn2Ee/CiySlwyuOICSLdqUkH4P7u8qXszos8Yc=" hash = "sha256-O2wTU0SmNIEEOxfncl2BW2czgWeIW5vqR6+A7dtNtXI="
[mod."github.com/tidwall/pretty"]
version = "v1.2.1"
hash = "sha256-S0uTDDGD8qr415Ut7QinyXljCp0TkL4zOIrlJ+9OMl8="
[mod."github.com/tklauser/go-sysconf"] [mod."github.com/tklauser/go-sysconf"]
version = "v0.3.12" version = "v0.3.15"
hash = "sha256-91VBZNb3L2TZkEETF1AE4wnraLoGxKeofUbC5ZiWVHk=" hash = "sha256-Twh9dD/mfNb7Qr5p/KmaK5Xi25VJwj5jESZeJr0qeUc="
[mod."github.com/tklauser/numcpus"] [mod."github.com/tklauser/numcpus"]
version = "v0.6.1" version = "v0.10.0"
hash = "sha256-8eFcw4YI0w6+GPhU5xMMQjiio94q/O5PpNO3QsvXve0=" hash = "sha256-NWg68H2Y6MijtlscPaZlLQwgPthEwLV+WpRT01m6kkI="
[mod."github.com/wcharczuk/go-chart/v2"] [mod."github.com/wcharczuk/go-chart/v2"]
version = "v2.1.1" version = "v2.1.2"
hash = "sha256-emvjt/ze8skM+MBflwV0EgS/svpaEGU/mn27Ie4VTXs=" hash = "sha256-GXWWea/u6BezTsPPrWhTYiTetPP/YW6P+Sj4YdocPaM="
[mod."github.com/wdvxdr1123/ZeroBot"] [mod."github.com/wdvxdr1123/ZeroBot"]
version = "v1.7.5-0.20240829093431-bea5257d1a2b" version = "v1.8.2-0.20251002074418-56567b7fc282"
hash = "sha256-P8kexm2KOaXIk4Xnex5e02vv1ObTeWKhnWnxnDXrUDE=" hash = "sha256-KaoqopWcXqiRhGYNaA3UqYtXf27yMuBEj/bvqOWxaC4="
[mod."github.com/yusufpapurcu/wmi"] [mod."github.com/yusufpapurcu/wmi"]
version = "v1.2.4" version = "v1.2.4"
hash = "sha256-N+YDBjOW59YOsZ2lRBVtFsEEi48KhNQRb63/0ZSU3bA=" hash = "sha256-N+YDBjOW59YOsZ2lRBVtFsEEi48KhNQRb63/0ZSU3bA="
[mod."gitlab.com/gomidi/midi/v2"] [mod."gitlab.com/gomidi/midi/v2"]
version = "v2.1.7" version = "v2.3.16"
hash = "sha256-fbgxSMCk7PVII3sNEKuGWbN56fy3eM564Xb+lnYTxRQ=" hash = "sha256-o+6UtQH+TRSQlcX8J53esAA/b2c9e7BY7gcO5iSeOy0="
[mod."golang.org/x/exp"]
version = "v0.0.0-20190306152737-a1d7652674e8"
hash = "sha256-VJ0sxFsqnx2O/NmXamL2F5bQeUw5sizVQ7NLusceK5Q="
[mod."golang.org/x/image"] [mod."golang.org/x/image"]
version = "v0.16.0" version = "v0.32.0"
hash = "sha256-+BOLefaFM/c+AV3kmnNvztbhZ+a9GCNwkEya8hZSKYg=" hash = "sha256-a5C5nG4IKFkqM/EXjt29wzjrkYEFLKlg5C0aBnXnlZw="
[mod."golang.org/x/mobile"]
version = "v0.0.0-20190415191353-3e0bab5405d6"
hash = "sha256-Ds7JS9muxzDc7WgCncAd0rMSFeBI88/I0dQsk13/56k="
[mod."golang.org/x/net"] [mod."golang.org/x/net"]
version = "v0.24.0" version = "v0.46.0"
hash = "sha256-w1c21ljta5wNIyel9CSIn/crPzwOCRofNKhqmfs4aEQ=" hash = "sha256-GkAUXwqEJZF2t5dmxJVEE+t58EDzO24KtihqHqsrZH0="
[mod."golang.org/x/sys"] [mod."golang.org/x/sys"]
version = "v0.20.0" version = "v0.37.0"
hash = "sha256-mowlaoG2k4n1c1rApWef5EMiXd3I77CsUi8jPh6pTYA=" hash = "sha256-5aT0xP02sW1o9sfJHtWoGGNVYDdwb9FyiX/n6RAlzPo="
[mod."golang.org/x/text"] [mod."golang.org/x/text"]
version = "v0.15.0" version = "v0.30.0"
hash = "sha256-pBnj0AEkfkvZf+3bN7h6epCD2kurw59clDP7yWvxKlk=" hash = "sha256-VPT1Y2zzgdk+Q3Gx8hdwKdxbVh0/Zn/HwDpILCW3ZNA="
[mod."gopkg.in/yaml.v3"] [mod."gopkg.in/yaml.v3"]
version = "v3.0.1" version = "v3.0.1"
hash = "sha256-FqL9TKYJ0XkNwJFnq9j0VvJ5ZUU1RvH/52h/f5bkYAU=" hash = "sha256-FqL9TKYJ0XkNwJFnq9j0VvJ5ZUU1RvH/52h/f5bkYAU="
@ -258,11 +279,11 @@ schema = 3
hash = "sha256-SJYYRaiDUmIbqy9l/IgiT/4VkFsPYsaslqGEowut34w=" hash = "sha256-SJYYRaiDUmIbqy9l/IgiT/4VkFsPYsaslqGEowut34w="
replaced = "github.com/fumiama/libc" replaced = "github.com/fumiama/libc"
[mod."modernc.org/mathutil"] [mod."modernc.org/mathutil"]
version = "v1.6.0" version = "v1.7.1"
hash = "sha256-lfuEiS1odd2TWrTylnaGihSJ9myqKs3FLdpvd7PqTnE=" hash = "sha256-COZ5rF2GhQVR1r6a0DanJ8qwQ94JSKdQxTMWrDzE0Cc="
[mod."modernc.org/memory"] [mod."modernc.org/memory"]
version = "v1.8.0" version = "v1.11.0"
hash = "sha256-ucvPr73zg8LjvU+bcoIPKTgwgcon3U9VhKrLEMH81xg=" hash = "sha256-MkybF8vvrxXS5j7O8w3skwTo0aMo1yjWS0K440rYcHM="
[mod."modernc.org/sqlite"] [mod."modernc.org/sqlite"]
version = "v1.29.10-simp" version = "v1.29.10-simp"
hash = "sha256-HCUVN6gZDG0g2WIsQ4ksqE1+XR1IjxvnqEBEU2MO1eE=" hash = "sha256-HCUVN6gZDG0g2WIsQ4ksqE1+XR1IjxvnqEBEU2MO1eE="

View File

@ -3,13 +3,13 @@
package banner package banner
// Version ... // Version ...
var Version = "v1.8.4" var Version = "v1.10.0"
// Copyright ... // Copyright ...
var Copyright = "© 2020 - 2024 FloatTech" var Copyright = "© 2020 - 2025 FloatTech"
// Banner ... // Banner ...
var Banner = "* OneBot + ZeroBot + Golang\n" + var Banner = "* OneBot + ZeroBot + Golang\n" +
"* Version " + Version + " - 2024-10-05 21:11:11 +0900 JST\n" + "* Version " + Version + " - 2025-09-30 23:45:28 +0800 CST\n" +
"* Copyright " + Copyright + ". All Rights Reserved.\n" + "* Copyright " + Copyright + ". All Rights Reserved.\n" +
"* Project: https://github.com/FloatTech/ZeroBot-Plugin" "* Project: https://github.com/FloatTech/ZeroBot-Plugin"

View File

@ -27,7 +27,7 @@ var Banner = "* OneBot + ZeroBot + Golang\n" +
"* Project: https://github.com/FloatTech/ZeroBot-Plugin" "* Project: https://github.com/FloatTech/ZeroBot-Plugin"
` `
const timeformat = `2006-01-02 15:04:05 +0900 JST` const timeformat = `2006-01-02 15:04:05 +0800 CST`
func main() { func main() {
f, err := os.Create("banner/banner.go") f, err := os.Create("banner/banner.go")

View File

@ -1,5 +0,0 @@
//go:build go1.21
package kanban
const Error int = "请使用小于1.21版本的Go"

26
main.go
View File

@ -1,6 +1,8 @@
// Package main ZeroBot-Plugin main file // Package main ZeroBot-Plugin main file
package main package main
//go:generate go run github.com/FloatTech/ZeroBot-Plugin/abineundo/ref -r .
import ( import (
"encoding/json" "encoding/json"
"flag" "flag"
@ -12,8 +14,8 @@ import (
"strings" "strings"
"time" "time"
_ "github.com/FloatTech/ZeroBot-Plugin/abineundo" // 设置插件优先级
_ "github.com/FloatTech/ZeroBot-Plugin/console" // 更改控制台属性 _ "github.com/FloatTech/ZeroBot-Plugin/console" // 更改控制台属性
"github.com/FloatTech/ZeroBot-Plugin/kanban" // 打印 banner "github.com/FloatTech/ZeroBot-Plugin/kanban" // 打印 banner
// ---------以下插件均可通过前面加 // 注释,注释后停用并不加载插件--------- // // ---------以下插件均可通过前面加 // 注释,注释后停用并不加载插件--------- //
@ -38,6 +40,8 @@ import (
_ "github.com/FloatTech/ZeroBot-Plugin/plugin/sleepmanage" // 统计睡眠时间 _ "github.com/FloatTech/ZeroBot-Plugin/plugin/sleepmanage" // 统计睡眠时间
_ "github.com/FloatTech/ZeroBot-Plugin/plugin/airecord" // 群应用AI声聊
_ "github.com/FloatTech/ZeroBot-Plugin/plugin/atri" // ATRI词库 _ "github.com/FloatTech/ZeroBot-Plugin/plugin/atri" // ATRI词库
_ "github.com/FloatTech/ZeroBot-Plugin/plugin/manager" // 群管 _ "github.com/FloatTech/ZeroBot-Plugin/plugin/manager" // 群管
@ -62,16 +66,21 @@ import (
// vvvvvvvvvvvvvv // // vvvvvvvvvvvvvv //
// vvvv // // vvvv //
_ "github.com/FloatTech/ZeroBot-Plugin/custom" // 自定义插件合集
_ "github.com/FloatTech/ZeroBot-Plugin/plugin/ahsai" // ahsai tts _ "github.com/FloatTech/ZeroBot-Plugin/plugin/ahsai" // ahsai tts
_ "github.com/FloatTech/ZeroBot-Plugin/plugin/aifalse" // 服务器监控 _ "github.com/FloatTech/ZeroBot-Plugin/plugin/aifalse" // 服务器监控
_ "github.com/FloatTech/ZeroBot-Plugin/plugin/aiimage" // AI画图
_ "github.com/FloatTech/ZeroBot-Plugin/plugin/aiwife" // 随机老婆 _ "github.com/FloatTech/ZeroBot-Plugin/plugin/aiwife" // 随机老婆
_ "github.com/FloatTech/ZeroBot-Plugin/plugin/alipayvoice" // 支付宝到账语音 _ "github.com/FloatTech/ZeroBot-Plugin/plugin/alipayvoice" // 支付宝到账语音
_ "github.com/FloatTech/ZeroBot-Plugin/plugin/animetrace" // AnimeTrace 动画/Galgame识别
_ "github.com/FloatTech/ZeroBot-Plugin/plugin/autowithdraw" // 触发者撤回时也自动撤回 _ "github.com/FloatTech/ZeroBot-Plugin/plugin/autowithdraw" // 触发者撤回时也自动撤回
_ "github.com/FloatTech/ZeroBot-Plugin/plugin/baiduaudit" // 百度内容审核 _ "github.com/FloatTech/ZeroBot-Plugin/plugin/baiduaudit" // 百度内容审核
_ "github.com/FloatTech/ZeroBot-Plugin/plugin/base16384" // base16384加解密 _ "github.com/FloatTech/ZeroBot-Plugin/plugin/base16384" // base16384加解密
_ "github.com/FloatTech/ZeroBot-Plugin/plugin/base64gua" // base64卦加解密 _ "github.com/FloatTech/ZeroBot-Plugin/plugin/base64gua" // base64卦加解密
_ "github.com/FloatTech/ZeroBot-Plugin/plugin/baseamasiro" // base天城文加解密 _ "github.com/FloatTech/ZeroBot-Plugin/plugin/baseamasiro" // base天城文加解密
_ "github.com/FloatTech/ZeroBot-Plugin/plugin/bilibili" // b站相关 _ "github.com/FloatTech/ZeroBot-Plugin/plugin/bilibili" // b站相关
_ "github.com/FloatTech/ZeroBot-Plugin/plugin/bilibiliparse" // b站相关
_ "github.com/FloatTech/ZeroBot-Plugin/plugin/bilibilipush" // b站相关
_ "github.com/FloatTech/ZeroBot-Plugin/plugin/bookreview" // 哀伤雪刃吧推书记录 _ "github.com/FloatTech/ZeroBot-Plugin/plugin/bookreview" // 哀伤雪刃吧推书记录
_ "github.com/FloatTech/ZeroBot-Plugin/plugin/chess" // 国际象棋 _ "github.com/FloatTech/ZeroBot-Plugin/plugin/chess" // 国际象棋
_ "github.com/FloatTech/ZeroBot-Plugin/plugin/choose" // 选择困难症帮手 _ "github.com/FloatTech/ZeroBot-Plugin/plugin/choose" // 选择困难症帮手
@ -79,6 +88,7 @@ import (
_ "github.com/FloatTech/ZeroBot-Plugin/plugin/chrev" // 英文字符翻转 _ "github.com/FloatTech/ZeroBot-Plugin/plugin/chrev" // 英文字符翻转
_ "github.com/FloatTech/ZeroBot-Plugin/plugin/coser" // 三次元小姐姐 _ "github.com/FloatTech/ZeroBot-Plugin/plugin/coser" // 三次元小姐姐
_ "github.com/FloatTech/ZeroBot-Plugin/plugin/cpstory" // cp短打 _ "github.com/FloatTech/ZeroBot-Plugin/plugin/cpstory" // cp短打
_ "github.com/FloatTech/ZeroBot-Plugin/plugin/crypter" // 奇怪语言加解密
_ "github.com/FloatTech/ZeroBot-Plugin/plugin/dailynews" // 今日早报 _ "github.com/FloatTech/ZeroBot-Plugin/plugin/dailynews" // 今日早报
_ "github.com/FloatTech/ZeroBot-Plugin/plugin/danbooru" // DeepDanbooru二次元图标签识别 _ "github.com/FloatTech/ZeroBot-Plugin/plugin/danbooru" // DeepDanbooru二次元图标签识别
_ "github.com/FloatTech/ZeroBot-Plugin/plugin/diana" // 嘉心糖发病 _ "github.com/FloatTech/ZeroBot-Plugin/plugin/diana" // 嘉心糖发病
@ -86,6 +96,7 @@ import (
_ "github.com/FloatTech/ZeroBot-Plugin/plugin/drawlots" // 多功能抽签 _ "github.com/FloatTech/ZeroBot-Plugin/plugin/drawlots" // 多功能抽签
_ "github.com/FloatTech/ZeroBot-Plugin/plugin/driftbottle" // 漂流瓶 _ "github.com/FloatTech/ZeroBot-Plugin/plugin/driftbottle" // 漂流瓶
_ "github.com/FloatTech/ZeroBot-Plugin/plugin/emojimix" // 合成emoji _ "github.com/FloatTech/ZeroBot-Plugin/plugin/emojimix" // 合成emoji
_ "github.com/FloatTech/ZeroBot-Plugin/plugin/emozi" // 颜文字抽象转写
_ "github.com/FloatTech/ZeroBot-Plugin/plugin/event" // 好友申请群聊邀请事件处理 _ "github.com/FloatTech/ZeroBot-Plugin/plugin/event" // 好友申请群聊邀请事件处理
_ "github.com/FloatTech/ZeroBot-Plugin/plugin/font" // 渲染任意文字到图片 _ "github.com/FloatTech/ZeroBot-Plugin/plugin/font" // 渲染任意文字到图片
_ "github.com/FloatTech/ZeroBot-Plugin/plugin/fortune" // 运势 _ "github.com/FloatTech/ZeroBot-Plugin/plugin/fortune" // 运势
@ -106,7 +117,8 @@ import (
_ "github.com/FloatTech/ZeroBot-Plugin/plugin/magicprompt" // magicprompt吟唱提示 _ "github.com/FloatTech/ZeroBot-Plugin/plugin/magicprompt" // magicprompt吟唱提示
_ "github.com/FloatTech/ZeroBot-Plugin/plugin/mcfish" // 钓鱼模拟器 _ "github.com/FloatTech/ZeroBot-Plugin/plugin/mcfish" // 钓鱼模拟器
_ "github.com/FloatTech/ZeroBot-Plugin/plugin/midicreate" // 简易midi音乐制作 _ "github.com/FloatTech/ZeroBot-Plugin/plugin/midicreate" // 简易midi音乐制作
_ "github.com/FloatTech/ZeroBot-Plugin/plugin/moegoe" // 日韩 VITS 模型拟声 _ "github.com/FloatTech/ZeroBot-Plugin/plugin/minecraftobserver" // Minecraft服务器监控&订阅
_ "github.com/FloatTech/ZeroBot-Plugin/plugin/movies" // 电影插件
_ "github.com/FloatTech/ZeroBot-Plugin/plugin/moyu" // 摸鱼 _ "github.com/FloatTech/ZeroBot-Plugin/plugin/moyu" // 摸鱼
_ "github.com/FloatTech/ZeroBot-Plugin/plugin/moyucalendar" // 摸鱼人日历 _ "github.com/FloatTech/ZeroBot-Plugin/plugin/moyucalendar" // 摸鱼人日历
_ "github.com/FloatTech/ZeroBot-Plugin/plugin/music" // 点歌 _ "github.com/FloatTech/ZeroBot-Plugin/plugin/music" // 点歌
@ -124,6 +136,7 @@ import (
_ "github.com/FloatTech/ZeroBot-Plugin/plugin/realcugan" // realcugan清晰术 _ "github.com/FloatTech/ZeroBot-Plugin/plugin/realcugan" // realcugan清晰术
_ "github.com/FloatTech/ZeroBot-Plugin/plugin/reborn" // 投胎 _ "github.com/FloatTech/ZeroBot-Plugin/plugin/reborn" // 投胎
_ "github.com/FloatTech/ZeroBot-Plugin/plugin/robbery" // 打劫群友的ATRI币 _ "github.com/FloatTech/ZeroBot-Plugin/plugin/robbery" // 打劫群友的ATRI币
_ "github.com/FloatTech/ZeroBot-Plugin/plugin/rsshub" // RSSHub订阅姬
_ "github.com/FloatTech/ZeroBot-Plugin/plugin/runcode" // 在线运行代码 _ "github.com/FloatTech/ZeroBot-Plugin/plugin/runcode" // 在线运行代码
_ "github.com/FloatTech/ZeroBot-Plugin/plugin/saucenao" // 以图搜图 _ "github.com/FloatTech/ZeroBot-Plugin/plugin/saucenao" // 以图搜图
_ "github.com/FloatTech/ZeroBot-Plugin/plugin/score" // 分数 _ "github.com/FloatTech/ZeroBot-Plugin/plugin/score" // 分数
@ -135,15 +148,14 @@ import (
_ "github.com/FloatTech/ZeroBot-Plugin/plugin/tiangou" // 舔狗日记 _ "github.com/FloatTech/ZeroBot-Plugin/plugin/tiangou" // 舔狗日记
_ "github.com/FloatTech/ZeroBot-Plugin/plugin/tracemoe" // 搜番 _ "github.com/FloatTech/ZeroBot-Plugin/plugin/tracemoe" // 搜番
_ "github.com/FloatTech/ZeroBot-Plugin/plugin/translation" // 翻译 _ "github.com/FloatTech/ZeroBot-Plugin/plugin/translation" // 翻译
_ "github.com/FloatTech/ZeroBot-Plugin/plugin/vitsnyaru" // vits猫雷
_ "github.com/FloatTech/ZeroBot-Plugin/plugin/wallet" // 钱包 _ "github.com/FloatTech/ZeroBot-Plugin/plugin/wallet" // 钱包
_ "github.com/FloatTech/ZeroBot-Plugin/plugin/wantquotes" // 据意查句 _ "github.com/FloatTech/ZeroBot-Plugin/plugin/wantquotes" // 据意查句
_ "github.com/FloatTech/ZeroBot-Plugin/plugin/warframeapi" // warframeAPI插件 _ "github.com/FloatTech/ZeroBot-Plugin/plugin/warframeapi" // warframeAPI插件
_ "github.com/FloatTech/ZeroBot-Plugin/plugin/wenxinvilg" // 百度文心AI画图
_ "github.com/FloatTech/ZeroBot-Plugin/plugin/wife" // 抽老婆 _ "github.com/FloatTech/ZeroBot-Plugin/plugin/wife" // 抽老婆
_ "github.com/FloatTech/ZeroBot-Plugin/plugin/wordcount" // 聊天热词 _ "github.com/FloatTech/ZeroBot-Plugin/plugin/wordcount" // 聊天热词
_ "github.com/FloatTech/ZeroBot-Plugin/plugin/wordle" // 猜单词 _ "github.com/FloatTech/ZeroBot-Plugin/plugin/wordle" // 猜单词
_ "github.com/FloatTech/ZeroBot-Plugin/plugin/ygo" // 游戏王相关插件 _ "github.com/FloatTech/ZeroBot-Plugin/plugin/ygocdb" // 游戏王白鸽API卡查
_ "github.com/FloatTech/ZeroBot-Plugin/plugin/ygotrade" // 游戏王集换社卡价查询
_ "github.com/FloatTech/ZeroBot-Plugin/plugin/ymgal" // 月幕galgame _ "github.com/FloatTech/ZeroBot-Plugin/plugin/ymgal" // 月幕galgame
_ "github.com/FloatTech/ZeroBot-Plugin/plugin/yujn" // 遇见API _ "github.com/FloatTech/ZeroBot-Plugin/plugin/yujn" // 遇见API
@ -167,9 +179,9 @@ import (
// vvvvvvvvvvvvvv // // vvvvvvvvvvvvvv //
// vvvv // // vvvv //
_ "github.com/FloatTech/ZeroBot-Plugin/plugin/curse" // 骂人 _ "github.com/FloatTech/ZeroBot-Plugin/plugin/aichat" // AI聊天
_ "github.com/FloatTech/ZeroBot-Plugin/plugin/aireply" // 人工智能回复 _ "github.com/FloatTech/ZeroBot-Plugin/plugin/curse" // 骂人
_ "github.com/FloatTech/ZeroBot-Plugin/plugin/thesaurus" // 词典匹配回复 _ "github.com/FloatTech/ZeroBot-Plugin/plugin/thesaurus" // 词典匹配回复

302
plugin/aichat/cfg.go Normal file
View File

@ -0,0 +1,302 @@
package aichat
import (
"errors"
"fmt"
"strconv"
"strings"
ctrl "github.com/FloatTech/zbpctrl"
"github.com/FloatTech/zbputils/chat"
"github.com/fumiama/deepinfra"
"github.com/fumiama/deepinfra/model"
"github.com/sirupsen/logrus"
zero "github.com/wdvxdr1123/ZeroBot"
"github.com/wdvxdr1123/ZeroBot/message"
)
var (
cfg = newconfig()
)
var (
apitypes = map[string]uint8{
"OpenAI": 0,
"OLLaMA": 1,
"GenAI": 2,
}
apilist = [3]string{"OpenAI", "OLLaMA", "GenAI"}
)
// ModelType 支持打印 string 并生产 protocal
type ModelType int
func newModelType(typ string) (ModelType, error) {
t, ok := apitypes[typ]
if !ok {
return 0, errors.New("未知类型 " + typ)
}
return ModelType(t), nil
}
func (mt ModelType) String() string {
return apilist[mt]
}
func (mt ModelType) protocol(modn string, temp float32, topp float32, maxn uint) (mod model.Protocol, err error) {
switch cfg.Type {
case 0:
mod = model.NewOpenAI(
modn, cfg.Separator,
temp, topp, maxn,
)
case 1:
mod = model.NewOLLaMA(
modn, cfg.Separator,
temp, topp, maxn,
)
case 2:
mod = model.NewGenAI(
modn,
temp, topp, maxn,
)
default:
err = errors.New("unsupported model type " + strconv.Itoa(int(cfg.Type)))
}
return
}
// ModelBool 支持打印成 "是/否"
type ModelBool bool
func (mb ModelBool) String() string {
if mb {
return "是"
}
return "否"
}
// ModelKey 支持隐藏密钥
type ModelKey string
func (mk ModelKey) String() string {
if len(mk) == 0 {
return "未设置"
}
if len(mk) <= 4 {
return "****"
}
key := string(mk)
return key[:2] + strings.Repeat("*", len(key)-4) + key[len(key)-2:]
}
type config struct {
ModelName string
ImageModelName string
AgentModelName string
Type ModelType
ImageType ModelType
AgentType ModelType
MaxN uint
TopP float32
SystemP string
API string
ImageAPI string
AgentAPI string
Key ModelKey
ImageKey ModelKey
AgentKey ModelKey
Separator string
NoSystemP ModelBool
}
func newconfig() config {
return config{
ModelName: model.ModelDeepDeek,
SystemP: chat.SystemPrompt,
API: deepinfra.OpenAIDeepInfra,
}
}
func (c *config) String() string {
topp, maxn := c.mparams()
sb := strings.Builder{}
sb.WriteString(fmt.Sprintf("• 模型名:%s\n", c.ModelName))
sb.WriteString(fmt.Sprintf("• 图像模型名:%s\n", c.ImageModelName))
sb.WriteString(fmt.Sprintf("• Agent模型名%s\n", c.AgentModelName))
sb.WriteString(fmt.Sprintf("• 接口类型:%v\n", c.Type))
sb.WriteString(fmt.Sprintf("• 图像接口类型:%v\n", c.ImageType))
sb.WriteString(fmt.Sprintf("• Agent接口类型%v\n", c.AgentType))
sb.WriteString(fmt.Sprintf("• 最大长度:%d\n", maxn))
sb.WriteString(fmt.Sprintf("• TopP%.1f\n", topp))
sb.WriteString(fmt.Sprintf("• 系统提示词:%s\n", c.SystemP))
sb.WriteString(fmt.Sprintf("• 接口地址:%s\n", c.API))
sb.WriteString(fmt.Sprintf("• 图像接口地址:%s\n", c.ImageAPI))
sb.WriteString(fmt.Sprintf("• Agent接口地址%s\n", c.AgentAPI))
sb.WriteString(fmt.Sprintf("• 密钥:%v\n", c.Key))
sb.WriteString(fmt.Sprintf("• 图像密钥:%v\n", c.ImageKey))
sb.WriteString(fmt.Sprintf("• Agent密钥%v\n", c.AgentKey))
sb.WriteString(fmt.Sprintf("• 分隔符:%s\n", c.Separator))
sb.WriteString(fmt.Sprintf("• 支持系统提示词:%v\n", !c.NoSystemP))
return sb.String()
}
func (c *config) isvalid() bool {
return c.ModelName != "" && c.API != "" && c.Key != ""
}
// 获取全局模型参数TopP和最大长度
func (c *config) mparams() (topp float32, maxn uint) {
// 处理TopP参数
topp = c.TopP
if topp == 0 {
topp = 0.9
}
// 处理最大长度参数
maxn = c.MaxN
if maxn == 0 {
maxn = 4096
}
return topp, maxn
}
func ensureconfig(ctx *zero.Ctx) bool {
c, ok := ctx.State["manager"].(*ctrl.Control[*zero.Ctx])
if !ok {
return false
}
if !cfg.isvalid() {
err := c.GetExtra(&cfg)
if err != nil {
logrus.Warnln("ERROR: get extra err:", err)
}
if !cfg.isvalid() {
cfg = newconfig()
}
}
return true
}
func newextrasetstr[T ~string](ptr *T) func(ctx *zero.Ctx) {
return func(ctx *zero.Ctx) {
args := strings.TrimSpace(ctx.State["args"].(string))
if args == "" {
ctx.SendChain(message.Text("ERROR: empty args"))
return
}
c, ok := ctx.State["manager"].(*ctrl.Control[*zero.Ctx])
if !ok {
ctx.SendChain(message.Text("ERROR: no such plugin"))
return
}
*ptr = T(args)
err := c.SetExtra(&cfg)
if err != nil {
ctx.SendChain(message.Text("ERROR: set extra err: ", err))
return
}
ctx.SendChain(message.Text("成功"))
}
}
func newextrasetbool[T ~bool](ptr *T) func(ctx *zero.Ctx) {
return func(ctx *zero.Ctx) {
args := ctx.State["regex_matched"].([]string)
isno := args[1] == "不"
c, ok := ctx.State["manager"].(*ctrl.Control[*zero.Ctx])
if !ok {
ctx.SendChain(message.Text("ERROR: no such plugin"))
return
}
*ptr = T(isno)
err := c.SetExtra(&cfg)
if err != nil {
ctx.SendChain(message.Text("ERROR: set extra err: ", err))
return
}
ctx.SendChain(message.Text("成功"))
}
}
func newextrasetuint(ptr *uint) func(ctx *zero.Ctx) {
return func(ctx *zero.Ctx) {
args := strings.TrimSpace(ctx.State["args"].(string))
if args == "" {
ctx.SendChain(message.Text("ERROR: empty args"))
return
}
c, ok := ctx.State["manager"].(*ctrl.Control[*zero.Ctx])
if !ok {
ctx.SendChain(message.Text("ERROR: no such plugin"))
return
}
n, err := strconv.ParseUint(args, 10, 64)
if err != nil {
ctx.SendChain(message.Text("ERROR: parse args err: ", err))
return
}
*ptr = uint(n)
err = c.SetExtra(&cfg)
if err != nil {
ctx.SendChain(message.Text("ERROR: set extra err: ", err))
return
}
ctx.SendChain(message.Text("成功"))
}
}
func newextrasetfloat32(ptr *float32) func(ctx *zero.Ctx) {
return func(ctx *zero.Ctx) {
args := strings.TrimSpace(ctx.State["args"].(string))
if args == "" {
ctx.SendChain(message.Text("ERROR: empty args"))
return
}
c, ok := ctx.State["manager"].(*ctrl.Control[*zero.Ctx])
if !ok {
ctx.SendChain(message.Text("ERROR: no such plugin"))
return
}
n, err := strconv.ParseFloat(args, 32)
if err != nil {
ctx.SendChain(message.Text("ERROR: parse args err: ", err))
return
}
*ptr = float32(n)
err = c.SetExtra(&cfg)
if err != nil {
ctx.SendChain(message.Text("ERROR: set extra err: ", err))
return
}
ctx.SendChain(message.Text("成功"))
}
}
func newextrasetmodeltype(ptr *ModelType) func(ctx *zero.Ctx) {
return func(ctx *zero.Ctx) {
args := strings.TrimSpace(ctx.State["args"].(string))
if args == "" {
ctx.SendChain(message.Text("ERROR: empty args"))
return
}
c, ok := ctx.State["manager"].(*ctrl.Control[*zero.Ctx])
if !ok {
ctx.SendChain(message.Text("ERROR: no such plugin"))
return
}
typ, err := newModelType(args)
if err != nil {
ctx.SendChain(message.Text("ERROR: ", err))
return
}
*ptr = typ
err = c.SetExtra(&cfg)
if err != nil {
ctx.SendChain(message.Text("ERROR: set extra err: ", err))
return
}
ctx.SendChain(message.Text("成功"))
}
}

487
plugin/aichat/main.go Normal file
View File

@ -0,0 +1,487 @@
// Package aichat OpenAI聊天和群聊总结
package aichat
import (
"encoding/json"
"math/rand"
"strconv"
"strings"
"time"
"github.com/fumiama/deepinfra"
"github.com/fumiama/deepinfra/model"
goba "github.com/fumiama/go-onebot-agent"
"github.com/sirupsen/logrus"
"github.com/tidwall/gjson"
zero "github.com/wdvxdr1123/ZeroBot"
"github.com/wdvxdr1123/ZeroBot/extension/single"
"github.com/wdvxdr1123/ZeroBot/message"
"github.com/FloatTech/AnimeAPI/airecord"
"github.com/FloatTech/floatbox/process"
ctrl "github.com/FloatTech/zbpctrl"
"github.com/FloatTech/zbputils/chat"
"github.com/FloatTech/zbputils/control"
"github.com/FloatTech/zbputils/ctxext"
)
var (
// en data [8 temp] [8 rate] LSB
en = control.AutoRegister(&ctrl.Options[*zero.Ctx]{
DisableOnDefault: false,
Extra: control.ExtraFromString("aichat"),
Brief: "OpenAI聊天",
Help: "- 设置AI聊天触发概率10\n" +
"- 设置AI聊天温度80\n" +
"- 设置AI聊天(识图|Agent)接口类型[OpenAI|OLLaMA|GenAI]\n" +
"- 设置AI聊天(不)使用Agent模式\n" +
"- 设置AI聊天(不)支持系统提示词\n" +
"- 设置AI聊天(识图|Agent)接口地址https://api.siliconflow.cn/v1/chat/completions\n" +
"- 设置AI聊天(识图|Agent)密钥xxx\n" +
"- 设置AI聊天(识图|Agent)模型名Qwen/Qwen3-8B\n" +
"- 查看AI聊天系统提示词\n" +
"- 重置AI聊天系统提示词\n" +
"- 设置AI聊天系统提示词xxx\n" +
"- 设置AI聊天分隔符</think>(留空则清除)\n" +
"- 设置AI聊天(不)响应AT\n" +
"- 设置AI聊天最大长度4096\n" +
"- 设置AI聊天TopP 0.9\n" +
"- 设置AI聊天(不)以AI语音输出\n" +
"- 查看AI聊天配置\n" +
"- 重置AI聊天\n" +
"- 群聊总结 [消息数目]|群聊总结 1000\n" +
"- /gpt [内容] (使用大模型聊天)\n",
PrivateDataFolder: "aichat",
}).ApplySingle(single.New(
single.WithKeyFn(func(ctx *zero.Ctx) int64 {
if ctx.Event.GroupID == 0 {
return -ctx.Event.UserID
}
return ctx.Event.GroupID
}),
// no post option, silently quit
))
)
var (
limit = ctxext.NewLimiterManager(time.Second*30, 1)
)
func init() {
en.OnMessage(ensureconfig, func(ctx *zero.Ctx) bool {
gid := ctx.Event.GroupID
if gid == 0 {
gid = -ctx.Event.UserID
}
stor, err := newstorage(ctx, gid)
if err != nil {
logrus.Warnln("ERROR: ", err)
return false
}
ctx.State["__aichat_stor__"] = stor
return ctx.ExtractPlainText() != "" &&
(!stor.noreplyat() || (stor.noreplyat() && !ctx.Event.IsToMe))
}).SetBlock(false).Handle(func(ctx *zero.Ctx) {
gid := ctx.Event.GroupID
if gid == 0 {
gid = -ctx.Event.UserID
}
stor := ctx.State["__aichat_stor__"].(storage)
rate := stor.rate()
if !ctx.Event.IsToMe && rand.Intn(100) >= int(rate) {
return
}
if ctx.Event.IsToMe {
ctx.Block()
}
if cfg.Key == "" {
logrus.Warnln("ERROR: get extra err: empty key")
return
}
temperature := stor.temp()
topp, maxn := cfg.mparams()
if !stor.noagent() && cfg.AgentAPI != "" && cfg.AgentModelName != "" {
x := deepinfra.NewAPI(cfg.AgentAPI, string(cfg.AgentKey))
mod, err := cfg.Type.protocol(cfg.AgentModelName, temperature, topp, maxn)
if err != nil {
logrus.Warnln("ERROR: ", err)
return
}
role := goba.PermRoleUser
if zero.AdminPermission(ctx) {
role = goba.PermRoleAdmin
if zero.SuperUserPermission(ctx) {
role = goba.PermRoleOwner
}
}
ag := chat.AgentOf(ctx.Event.SelfID)
if cfg.ImageAPI != "" && !ag.CanViewImage() {
mod, err := cfg.ImageType.protocol(cfg.ImageModelName, temperature, topp, maxn)
if err != nil {
logrus.Warnln("ERROR: ", err)
return
}
ag.SetViewImageAPI(deepinfra.NewAPI(cfg.ImageAPI, string(cfg.ImageKey)), mod)
}
ctx.NoTimeout()
hasresp := false
for i := 0; i < 8; i++ { // 最大运行 8 轮因为问答上下文只有 16
reqs := chat.CallAgent(ag, zero.SuperUserPermission(ctx), x, mod, gid, role)
if len(reqs) == 0 {
break
}
hasresp = true
for _, req := range reqs {
resp := ctx.CallAction(req.Action, req.Params)
logrus.Infoln("[aichat] agent get resp:", reqs)
ag.AddResponse(gid, &goba.APIResponse{
Status: resp.Status,
Data: json.RawMessage(resp.Data.Raw),
Message: resp.Message,
Wording: resp.Wording,
RetCode: resp.RetCode,
})
}
}
if hasresp {
ag.AddTerminus(gid)
return
}
// no response, fall back to normal chat
}
x := deepinfra.NewAPI(cfg.API, string(cfg.Key))
mod, err := cfg.Type.protocol(cfg.ModelName, temperature, topp, maxn)
if err != nil {
logrus.Warnln("ERROR: ", err)
return
}
data, err := x.Request(chat.GetChatContext(mod, gid, cfg.SystemP, bool(cfg.NoSystemP)))
if err != nil {
logrus.Warnln("[aichat] post err:", err)
return
}
txt := chat.Sanitize(strings.Trim(data, "\n  "))
if len(txt) > 0 {
chat.AddChatReply(gid, txt)
nick := zero.BotConfig.NickName[rand.Intn(len(zero.BotConfig.NickName))]
txt = strings.ReplaceAll(txt, "{name}", ctx.CardOrNickName(ctx.Event.UserID))
txt = strings.ReplaceAll(txt, "{me}", nick)
id := any(nil)
if ctx.Event.IsToMe {
id = ctx.Event.MessageID
}
for _, t := range strings.Split(txt, "{segment}") {
if t == "" {
continue
}
logrus.Infoln("[aichat] 回复内容:", t)
recCfg := airecord.GetConfig()
record := ""
if !stor.norecord() {
record = ctx.GetAIRecord(recCfg.ModelID, recCfg.Customgid, t)
}
if record != "" {
ctx.SendChain(message.Record(record))
} else {
if id != nil {
id = ctx.SendChain(message.Reply(id), message.Text(t))
} else {
id = ctx.SendChain(message.Text(t))
}
}
process.SleepAbout1sTo2s()
}
}
})
en.OnPrefix("设置AI聊天触发概率", zero.AdminPermission).SetBlock(true).
Handle(ctxext.NewStorageSaveBitmapHandler(bitmaprate, 0, 100))
en.OnPrefix("设置AI聊天温度", zero.AdminPermission).SetBlock(true).
Handle(ctxext.NewStorageSaveBitmapHandler(bitmaptemp, 0, 100))
en.OnPrefix("设置AI聊天接口类型", ensureconfig, zero.OnlyPrivate, zero.SuperUserPermission).SetBlock(true).
Handle(newextrasetmodeltype(&cfg.Type))
en.OnPrefix("设置AI聊天识图接口类型", ensureconfig, zero.OnlyPrivate, zero.SuperUserPermission).SetBlock(true).
Handle(newextrasetmodeltype(&cfg.ImageType))
en.OnPrefix("设置AI聊天Agent接口类型", ensureconfig, zero.OnlyPrivate, zero.SuperUserPermission).SetBlock(true).
Handle(newextrasetmodeltype(&cfg.AgentType))
en.OnPrefix("设置AI聊天接口地址", ensureconfig, zero.OnlyPrivate, zero.SuperUserPermission).SetBlock(true).
Handle(newextrasetstr(&cfg.API))
en.OnPrefix("设置AI聊天识图接口地址", ensureconfig, zero.OnlyPrivate, zero.SuperUserPermission).SetBlock(true).
Handle(newextrasetstr(&cfg.ImageAPI))
en.OnPrefix("设置AI聊天Agent接口地址", ensureconfig, zero.OnlyPrivate, zero.SuperUserPermission).SetBlock(true).
Handle(newextrasetstr(&cfg.AgentAPI))
en.OnPrefix("设置AI聊天密钥", ensureconfig, zero.OnlyPrivate, zero.SuperUserPermission).SetBlock(true).
Handle(newextrasetstr(&cfg.Key))
en.OnPrefix("设置AI聊天识图密钥", ensureconfig, zero.OnlyPrivate, zero.SuperUserPermission).SetBlock(true).
Handle(newextrasetstr(&cfg.ImageKey))
en.OnPrefix("设置AI聊天Agent密钥", ensureconfig, zero.OnlyPrivate, zero.SuperUserPermission).SetBlock(true).
Handle(newextrasetstr(&cfg.AgentKey))
en.OnPrefix("设置AI聊天模型名", ensureconfig, zero.OnlyPrivate, zero.SuperUserPermission).SetBlock(true).
Handle(newextrasetstr(&cfg.ModelName))
en.OnPrefix("设置AI聊天识图模型名", ensureconfig, zero.OnlyPrivate, zero.SuperUserPermission).SetBlock(true).
Handle(newextrasetstr(&cfg.ImageModelName))
en.OnPrefix("设置AI聊天Agent模型名", ensureconfig, zero.OnlyPrivate, zero.SuperUserPermission).SetBlock(true).
Handle(newextrasetstr(&cfg.AgentModelName))
en.OnPrefix("设置AI聊天系统提示词", ensureconfig, zero.OnlyPrivate, zero.SuperUserPermission).SetBlock(true).
Handle(newextrasetstr(&cfg.SystemP))
en.OnFullMatch("查看AI聊天系统提示词", ensureconfig, zero.OnlyPrivate, zero.SuperUserPermission).SetBlock(true).Handle(func(ctx *zero.Ctx) {
ctx.SendChain(message.Text(cfg.SystemP))
})
en.OnFullMatch("重置AI聊天系统提示词", ensureconfig, zero.OnlyPrivate, zero.SuperUserPermission).SetBlock(true).Handle(func(ctx *zero.Ctx) {
c, ok := ctx.State["manager"].(*ctrl.Control[*zero.Ctx])
if !ok {
ctx.SendChain(message.Text("ERROR: no such plugin"))
return
}
cfg.SystemP = chat.SystemPrompt
err := c.SetExtra(&cfg)
if err != nil {
ctx.SendChain(message.Text("ERROR: set extra err: ", err))
return
}
ctx.SendChain(message.Text("成功"))
})
en.OnPrefix("设置AI聊天分隔符", ensureconfig, zero.OnlyPrivate, zero.SuperUserPermission).SetBlock(true).
Handle(newextrasetstr(&cfg.Separator))
en.OnRegex("^设置AI聊天(不)?响应AT$", ensureconfig, zero.OnlyPrivate, zero.SuperUserPermission).SetBlock(true).
Handle(ctxext.NewStorageSaveBoolHandler(bitmapnrat))
en.OnRegex("^设置AI聊天(不)?支持系统提示词$", ensureconfig, zero.OnlyPrivate, zero.SuperUserPermission).SetBlock(true).
Handle(newextrasetbool(&cfg.NoSystemP))
en.OnRegex("^设置AI聊天(不)?使用Agent模式$", ensureconfig, zero.SuperUserPermission).SetBlock(true).
Handle(ctxext.NewStorageSaveBoolHandler(bitmapnagt))
en.OnPrefix("设置AI聊天最大长度", ensureconfig, zero.OnlyPrivate, zero.SuperUserPermission).SetBlock(true).
Handle(newextrasetuint(&cfg.MaxN))
en.OnPrefix("设置AI聊天TopP", ensureconfig, zero.OnlyPrivate, zero.SuperUserPermission).SetBlock(true).
Handle(newextrasetfloat32(&cfg.TopP))
en.OnRegex("^设置AI聊天(不)?以AI语音输出$", ensureconfig, zero.AdminPermission).SetBlock(true).
Handle(ctxext.NewStorageSaveBoolHandler(bitmapnrec))
en.OnFullMatch("查看AI聊天配置", ensureconfig, zero.OnlyPrivate, zero.SuperUserPermission).SetBlock(true).
Handle(func(ctx *zero.Ctx) {
gid := ctx.Event.GroupID
stor, err := newstorage(ctx, gid)
if err != nil {
ctx.SendChain(message.Text("ERROR: ", err))
return
}
ctx.SendChain(
message.Text(
"【当前AI聊天本群配置】\n",
"• 触发概率:", int(stor.rate()), "\n",
"• 温度:", stor.temp(), "\n",
"• 以AI语音输出", ModelBool(!stor.norecord()), "\n",
"• 使用Agent", ModelBool(!stor.noagent()), "\n",
"• 响应@", ModelBool(!stor.noreplyat()), "\n",
),
message.Text("【当前AI聊天全局配置】\n", &cfg),
)
})
en.OnFullMatch("重置AI聊天", ensureconfig, zero.OnlyPrivate, zero.SuperUserPermission).SetBlock(true).Handle(func(ctx *zero.Ctx) {
chat.ResetChat()
ctx.SendChain(message.Text("成功"))
})
// 添加群聊总结功能
en.OnRegex(`^群聊总结\s?(\d*)$`, ensureconfig, zero.OnlyGroup, zero.AdminPermission).SetBlock(true).Limit(limit.LimitByGroup).Handle(func(ctx *zero.Ctx) {
ctx.SendChain(message.Text("少女思考中..."))
gid := ctx.Event.GroupID
if gid == 0 {
gid = -ctx.Event.UserID
}
p, _ := strconv.ParseInt(ctx.State["regex_matched"].([]string)[1], 10, 64)
if p > 1000 {
p = 1000
}
if p == 0 {
p = 200
}
group := ctx.GetGroupInfo(gid, false)
if group.MemberCount == 0 {
ctx.SendChain(message.Text(zero.BotConfig.NickName[0], "未加入", group.Name, "(", gid, "),无法获取总结"))
return
}
var messages []string
h := ctx.GetGroupMessageHistory(gid, 0, p, false)
h.Get("messages").ForEach(func(_, msgObj gjson.Result) bool {
nickname := msgObj.Get("sender.nickname").Str
text := strings.TrimSpace(message.ParseMessageFromString(msgObj.Get("raw_message").Str).ExtractPlainText())
if text != "" {
messages = append(messages, nickname+": "+text)
}
return true
})
if len(messages) == 0 {
ctx.SendChain(message.Text("ERROR: 历史消息为空或者无法获得历史消息"))
return
}
// 构造总结请求提示 (使用通用版省流提示词)
// 使用反引号定义多行字符串,更清晰
promptTemplate := `请对以下群聊对话进行极简总结
要求
1. 剔除客套与废话直击主题
2. 使用 Markdown 列表格式
3. 按以下结构输出
- 🎯 核心议题(一句话概括)
- 💡 关键观点/结论(提取3-5个重点)
- 下一步/待办(如果有明确谁做什么)
群聊对话内容如下
`
summaryPrompt := promptTemplate + strings.Join(messages, "\n")
stor, err := newstorage(ctx, gid)
if err != nil {
ctx.SendChain(message.Text("ERROR: ", err))
return
}
// 调用大模型API进行总结
summary, err := llmchat(summaryPrompt, stor.temp())
if err != nil {
ctx.SendChain(message.Text("ERROR: ", err))
return
}
var b strings.Builder
b.WriteString("群 ")
b.WriteString(group.Name)
b.WriteByte('(')
b.WriteString(strconv.FormatInt(gid, 10))
b.WriteString(") 的 ")
b.WriteString(strconv.FormatInt(p, 10))
b.WriteString(" 条消息总结:\n\n")
b.WriteString(summary)
// 分割总结内容为多段按1000字符长度切割
summaryText := b.String()
msg := make(message.Message, 0)
for len(summaryText) > 0 {
if len(summaryText) <= 1000 {
msg = append(msg, ctxext.FakeSenderForwardNode(ctx, message.Text(summaryText)))
break
}
// 查找1000字符内的最后一个换行符尽量在换行处分割
chunk := summaryText[:1000]
lastNewline := strings.LastIndex(chunk, "\n")
if lastNewline > 0 {
chunk = summaryText[:lastNewline+1]
}
msg = append(msg, ctxext.FakeSenderForwardNode(ctx, message.Text(chunk)))
summaryText = summaryText[len(chunk):]
}
if len(msg) > 0 {
ctx.Send(msg)
}
})
// 添加 /gpt 命令处理(同时支持回复消息和直接使用)
en.OnKeyword("/gpt", ensureconfig).SetBlock(true).Handle(func(ctx *zero.Ctx) {
gid := ctx.Event.GroupID
if gid == 0 {
gid = -ctx.Event.UserID
}
text := ctx.MessageString()
var query string
var replyContent string
// 检查是否是回复消息 (使用MessageElement检查而不是CQ码)
for _, elem := range ctx.Event.Message {
if elem.Type == "reply" {
// 提取被回复的消息ID
replyIDStr := elem.Data["id"]
replyID, err := strconv.ParseInt(replyIDStr, 10, 64)
if err == nil {
// 获取被回复的消息内容
replyMsg := ctx.GetMessage(replyID)
if replyMsg.Elements != nil {
replyContent = replyMsg.Elements.ExtractPlainText()
}
}
break // 找到回复元素后退出循环
}
}
// 提取 /gpt 后面的内容
parts := strings.SplitN(text, "/gpt", 2)
var gContent string
if len(parts) > 1 {
gContent = strings.TrimSpace(parts[1])
}
// 组合内容:优先使用回复内容,如果同时有/gpt内容则拼接
switch {
case replyContent != "" && gContent != "":
query = replyContent + "\n" + gContent
case replyContent != "":
query = replyContent
case gContent != "":
query = gContent
default:
return
}
stor, err := newstorage(ctx, gid)
if err != nil {
ctx.SendChain(message.Text("ERROR: ", err))
return
}
// 调用大模型API进行聊天
reply, err := llmchat(query, stor.temp())
if err != nil {
ctx.SendChain(message.Text("ERROR: ", err))
return
}
// 分割总结内容为多段按1000字符长度切割
msg := make(message.Message, 0)
for len(reply) > 0 {
if len(reply) <= 1000 {
msg = append(msg, ctxext.FakeSenderForwardNode(ctx, message.Text(reply)))
break
}
// 查找1000字符内的最后一个换行符尽量在换行处分割
chunk := reply[:1000]
lastNewline := strings.LastIndex(chunk, "\n")
if lastNewline > 0 {
chunk = reply[:lastNewline+1]
}
msg = append(msg, ctxext.FakeSenderForwardNode(ctx, message.Text(chunk)))
reply = reply[len(chunk):]
}
if len(msg) > 0 {
ctx.Send(msg)
}
})
}
// llmchat 调用大模型API包装
func llmchat(prompt string, temp float32) (string, error) {
topp, maxn := cfg.mparams()
x := deepinfra.NewAPI(cfg.API, string(cfg.Key))
mod, err := cfg.Type.protocol(cfg.ModelName, temp, topp, maxn)
if err != nil {
return "", nil
}
data, err := x.Request(mod.User(model.NewContentText(prompt)))
if err != nil {
return "", err
}
return strings.TrimSpace(data), nil
}

49
plugin/aichat/storage.go Normal file
View File

@ -0,0 +1,49 @@
package aichat
import (
"github.com/FloatTech/zbputils/ctxext"
zero "github.com/wdvxdr1123/ZeroBot"
)
const (
bitmaprate = 0x0000ff
bitmaptemp = 0x00ff00
bitmapnagt = 0x010000
bitmapnrec = 0x020000
bitmapnrat = 0x040000
)
type storage ctxext.Storage
func newstorage(ctx *zero.Ctx, gid int64) (storage, error) {
s, err := ctxext.NewStorage(ctx, gid)
return storage(s), err
}
func (s storage) rate() uint8 {
return uint8((ctxext.Storage)(s).Get(bitmaprate))
}
func (s storage) temp() float32 {
temp := int8((ctxext.Storage)(s).Get(bitmaptemp))
// 处理温度参数
if temp <= 0 {
temp = 70 // default setting
}
if temp > 100 {
temp = 100
}
return float32(temp) / 100
}
func (s storage) noagent() bool {
return (ctxext.Storage)(s).GetBool(bitmapnagt)
}
func (s storage) norecord() bool {
return (ctxext.Storage)(s).GetBool(bitmapnrec)
}
func (s storage) noreplyat() bool {
return (ctxext.Storage)(s).GetBool(bitmapnrat)
}

View File

@ -0,0 +1,149 @@
package aichat
import (
"testing"
"github.com/FloatTech/zbputils/ctxext"
)
func TestStorage_rate(t *testing.T) {
s := storage(ctxext.Storage(0))
// 测试默认值
if rate := s.rate(); rate != 0 {
t.Errorf("default rate() = %v, want 0", rate)
}
// 设置值并测试
s = storage((ctxext.Storage)(s).Set(int64(100), bitmaprate))
if rate := s.rate(); rate != 100 {
t.Errorf("rate() after set = %v, want 100", rate)
}
}
func TestStorage_temp(t *testing.T) {
s := storage(ctxext.Storage(0))
tests := []struct {
name string
setValue int64
expected float32
}{
{"default temp (0)", 0, 0.70}, // 默认值 70/100
{"valid temp 50", 50, 0.50}, // 50/100 = 0.50
{"valid temp 80", 80, 0.80}, // 80/100 = 0.80
{"max temp 100", 100, 1.00}, // 100/100 = 1.00
{"over max temp", 127, 1.00}, // 限制为 100/100 = 1.00
{"negative temp", -10, 0.70}, // 默认值 70/100
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
s = storage((ctxext.Storage)(s).Set(tt.setValue, bitmaptemp))
result := s.temp()
if result != tt.expected {
t.Errorf("temp() = %v, want %v", result, tt.expected)
}
})
}
}
func TestStorage_noagent(t *testing.T) {
s := storage(ctxext.Storage(0))
// 测试默认值
if noagent := s.noagent(); noagent != false {
t.Errorf("default noagent() = %v, want false", noagent)
}
// 设置为 true 并测试
s = storage((ctxext.Storage)(s).Set(1, bitmapnagt))
if noagent := s.noagent(); noagent != true {
t.Errorf("noagent() after set true = %v, want true", noagent)
}
}
func TestStorage_norecord(t *testing.T) {
s := storage(ctxext.Storage(0))
// 测试默认值
if norecord := s.norecord(); norecord != false {
t.Errorf("default norecord() = %v, want false", norecord)
}
// 设置为 true 并测试
s = storage((ctxext.Storage)(s).Set(1, bitmapnrec))
if norecord := s.norecord(); norecord != true {
t.Errorf("norecord() after set true = %v, want true", norecord)
}
}
func TestStorage_noreplyat(t *testing.T) {
s := storage(ctxext.Storage(0))
// 测试默认值
if noreplyat := s.noreplyat(); noreplyat != false {
t.Errorf("default noreplyat() = %v, want false", noreplyat)
}
// 设置为 true 并测试
s = storage((ctxext.Storage)(s).Set(1, bitmapnrat))
if noreplyat := s.noreplyat(); noreplyat != true {
t.Errorf("noreplyat() after set true = %v, want true", noreplyat)
}
}
func TestStorage_Integration(t *testing.T) {
s := storage(ctxext.Storage(0))
// 设置各种值
s = storage((ctxext.Storage)(s).Set(int64(75), bitmaprate))
s = storage((ctxext.Storage)(s).Set(int64(85), bitmaptemp))
s = storage((ctxext.Storage)(s).Set(1, bitmapnagt))
s = storage((ctxext.Storage)(s).Set(0, bitmapnrec))
s = storage((ctxext.Storage)(s).Set(1, bitmapnrat))
// 验证所有方法
if rate := s.rate(); rate != 75 {
t.Errorf("rate() = %v, want 75", rate)
}
if temp := s.temp(); temp != 0.85 {
t.Errorf("temp() = %v, want 0.85", temp)
}
if noagent := s.noagent(); !noagent {
t.Errorf("noagent() = %v, want true", noagent)
}
if norecord := s.norecord(); norecord {
t.Errorf("norecord() = %v, want false", norecord)
}
if noreplyat := s.noreplyat(); !noreplyat {
t.Errorf("noreplyat() = %v, want true", noreplyat)
}
}
func BenchmarkStorage_rate(b *testing.B) {
s := storage(ctxext.Storage(0))
s = storage((ctxext.Storage)(s).Set(int64(100), bitmaprate))
b.ResetTimer()
for i := 0; i < b.N; i++ {
s.rate()
}
}
func BenchmarkStorage_temp(b *testing.B) {
s := storage(ctxext.Storage(0))
s = storage((ctxext.Storage)(s).Set(int64(80), bitmaptemp))
b.ResetTimer()
for i := 0; i < b.N; i++ {
s.temp()
}
}

View File

@ -20,16 +20,15 @@ import (
"github.com/FloatTech/floatbox/web" "github.com/FloatTech/floatbox/web"
"github.com/FloatTech/gg" "github.com/FloatTech/gg"
"github.com/FloatTech/imgfactory" "github.com/FloatTech/imgfactory"
"github.com/FloatTech/rendercard"
ctrl "github.com/FloatTech/zbpctrl" ctrl "github.com/FloatTech/zbpctrl"
"github.com/FloatTech/zbputils/control" "github.com/FloatTech/zbputils/control"
"github.com/FloatTech/zbputils/ctxext" "github.com/FloatTech/zbputils/ctxext"
"github.com/FloatTech/zbputils/img/text" "github.com/FloatTech/zbputils/img/text"
"github.com/disintegration/imaging" "github.com/disintegration/imaging"
"github.com/shirou/gopsutil/v3/cpu" "github.com/shirou/gopsutil/v4/cpu"
"github.com/shirou/gopsutil/v3/disk" "github.com/shirou/gopsutil/v4/disk"
"github.com/shirou/gopsutil/v3/host" "github.com/shirou/gopsutil/v4/host"
"github.com/shirou/gopsutil/v3/mem" "github.com/shirou/gopsutil/v4/mem"
"github.com/sirupsen/logrus" "github.com/sirupsen/logrus"
"golang.org/x/text/cases" "golang.org/x/text/cases"
"golang.org/x/text/language" "golang.org/x/text/language"
@ -41,7 +40,7 @@ import (
) )
const ( const (
backgroundURL = "https://iw233.cn/api.php?sort=mp" backgroundURL = "https://pic.re/image"
referer = "https://weibo.com/" referer = "https://weibo.com/"
) )
@ -237,14 +236,17 @@ func drawstatus(m *ctrl.Control[*zero.Ctx], uid int64, botname string, botrunsta
defer wg.Done() defer wg.Done()
titlecard := gg.NewContext(cardw, titlecardh) titlecard := gg.NewContext(cardw, titlecardh)
bwg.Wait() bwg.Wait()
titlecard.DrawImage(blurback, -70, -70)
titlecard.DrawRoundedRectangle(1, 1, float64(titlecard.W()-1*2), float64(titlecardh-1*2), 16) titlecard.DrawRoundedRectangle(1, 1, float64(titlecard.W()-1*2), float64(titlecardh-1*2), 16)
titlecard.ClipPreserve()
titlecard.DrawImage(blurback, -70, -70)
titlecard.SetColor(colorswitch(140))
titlecard.FillPreserve()
titlecard.SetLineWidth(3) titlecard.SetLineWidth(3)
titlecard.SetColor(colorswitch(100)) titlecard.SetColor(colorswitch(100))
titlecard.StrokePreserve() titlecard.ResetClip()
titlecard.SetColor(colorswitch(140)) titlecard.Stroke()
titlecard.Fill()
titlecard.DrawImage(avatarf.Circle(0).Image(), (titlecardh-avatarf.H())/2, (titlecardh-avatarf.H())/2) titlecard.DrawImage(avatarf.Circle(0).Image(), (titlecardh-avatarf.H())/2, (titlecardh-avatarf.H())/2)
@ -288,20 +290,23 @@ func drawstatus(m *ctrl.Control[*zero.Ctx], uid int64, botname string, botrunsta
fw, _ = titlecard.MeasureString(bs) fw, _ = titlecard.MeasureString(bs)
titlecard.DrawStringAnchored(bs, float64(titlecardh)+fw/2, float64(titlecardh)*(0.5+0.75/2), 0.5, 0.5) titlecard.DrawStringAnchored(bs, float64(titlecardh)+fw/2, float64(titlecardh)*(0.5+0.75/2), 0.5, 0.5)
titleimg = rendercard.Fillet(titlecard.Image(), 16) titleimg = titlecard.Image()
}() }()
go func() { go func() {
defer wg.Done() defer wg.Done()
basiccard := gg.NewContext(cardw, basiccardh) basiccard := gg.NewContext(cardw, basiccardh)
bwg.Wait() bwg.Wait()
basiccard.DrawImage(blurback, -70, -70-titlecardh-40)
basiccard.DrawRoundedRectangle(1, 1, float64(basiccard.W()-1*2), float64(basiccardh-1*2), 16) basiccard.DrawRoundedRectangle(1, 1, float64(basiccard.W()-1*2), float64(basiccardh-1*2), 16)
basiccard.ClipPreserve()
basiccard.DrawImage(blurback, -70, -70-titlecardh-40)
basiccard.SetColor(colorswitch(140))
basiccard.FillPreserve()
basiccard.SetLineWidth(3) basiccard.SetLineWidth(3)
basiccard.SetColor(colorswitch(100)) basiccard.SetColor(colorswitch(100))
basiccard.StrokePreserve() basiccard.ResetClip()
basiccard.SetColor(colorswitch(140)) basiccard.Stroke()
basiccard.Fill()
bslen := len(basicstate) bslen := len(basicstate)
for i, v := range basicstate { for i, v := range basicstate {
@ -361,20 +366,23 @@ func drawstatus(m *ctrl.Control[*zero.Ctx], uid int64, botname string, botrunsta
basiccard.DrawStringAnchored(s, (float64(basiccard.W())-200*float64(bslen))/float64(bslen+1)+200/2+offset, 20+200+15+fw+15+basiccard.FontHeight()/2+float64(k)*textoffsety, 0.5, 0.5) basiccard.DrawStringAnchored(s, (float64(basiccard.W())-200*float64(bslen))/float64(bslen+1)+200/2+offset, 20+200+15+fw+15+basiccard.FontHeight()/2+float64(k)*textoffsety, 0.5, 0.5)
} }
} }
basicimg = rendercard.Fillet(basiccard.Image(), 16) basicimg = basiccard.Image()
}() }()
go func() { go func() {
defer wg.Done() defer wg.Done()
diskcard := gg.NewContext(cardw, diskcardh) diskcard := gg.NewContext(cardw, diskcardh)
bwg.Wait() bwg.Wait()
diskcard.DrawImage(blurback, -70, -70-titlecardh-40-basiccardh-40)
diskcard.DrawRoundedRectangle(1, 1, float64(diskcard.W()-1*2), float64(diskcardh-1*2), 16) diskcard.DrawRoundedRectangle(1, 1, float64(diskcard.W()-1*2), float64(diskcardh-1*2), 16)
diskcard.ClipPreserve()
diskcard.DrawImage(blurback, -70, -70-titlecardh-40-basiccardh-40)
diskcard.SetColor(colorswitch(140))
diskcard.FillPreserve()
diskcard.SetLineWidth(3) diskcard.SetLineWidth(3)
diskcard.SetColor(colorswitch(100)) diskcard.SetColor(colorswitch(100))
diskcard.StrokePreserve() diskcard.ResetClip()
diskcard.SetColor(colorswitch(140)) diskcard.Stroke()
diskcard.Fill()
err = diskcard.ParseFontFace(fontbyte, 32) err = diskcard.ParseFontFace(fontbyte, 32)
if err != nil { if err != nil {
@ -427,6 +435,7 @@ func drawstatus(m *ctrl.Control[*zero.Ctx], uid int64, botname string, botrunsta
} }
diskcard.DrawRoundedRectangle(40, 40+(float64(diskcardh-40*2)-50*float64(dslen))/float64(dslen-1)+offset, float64(diskcard.W())-40-100, 50, 12) diskcard.DrawRoundedRectangle(40, 40+(float64(diskcardh-40*2)-50*float64(dslen))/float64(dslen-1)+offset, float64(diskcard.W())-40-100, 50, 12)
diskcard.ClipPreserve()
diskcard.Fill() diskcard.Fill()
colors := darkcolor colors := darkcolor
@ -445,6 +454,7 @@ func drawstatus(m *ctrl.Control[*zero.Ctx], uid int64, botname string, botrunsta
diskcard.DrawRoundedRectangle(40, 40+(float64(diskcardh-40*2)-50*float64(dslen))/float64(dslen-1)+offset, (float64(diskcard.W())-40-100)*v.precent*0.01, 50, 12) diskcard.DrawRoundedRectangle(40, 40+(float64(diskcardh-40*2)-50*float64(dslen))/float64(dslen-1)+offset, (float64(diskcard.W())-40-100)*v.precent*0.01, 50, 12)
diskcard.Fill() diskcard.Fill()
diskcard.ResetClip()
diskcard.SetColor(fontcolorswitch()) diskcard.SetColor(fontcolorswitch())
@ -456,20 +466,23 @@ func drawstatus(m *ctrl.Control[*zero.Ctx], uid int64, botname string, botrunsta
diskcard.DrawStringAnchored(strconv.FormatFloat(v.precent, 'f', 0, 64)+"%", float64(diskcard.W())-100/2, 40+(float64(diskcardh-40*2)-50*float64(dslen))/float64(dslen-1)+50/2+offset, 0.5, 0.5) diskcard.DrawStringAnchored(strconv.FormatFloat(v.precent, 'f', 0, 64)+"%", float64(diskcard.W())-100/2, 40+(float64(diskcardh-40*2)-50*float64(dslen))/float64(dslen-1)+50/2+offset, 0.5, 0.5)
} }
} }
diskimg = rendercard.Fillet(diskcard.Image(), 16) diskimg = diskcard.Image()
}() }()
go func() { go func() {
defer wg.Done() defer wg.Done()
moreinfocard := gg.NewContext(cardw, moreinfocardh) moreinfocard := gg.NewContext(cardw, moreinfocardh)
bwg.Wait() bwg.Wait()
moreinfocard.DrawImage(blurback, -70, -70-titlecardh-40-basiccardh-40-diskcardh-40)
moreinfocard.DrawRoundedRectangle(1, 1, float64(moreinfocard.W()-1*2), float64(moreinfocard.H()-1*2), 16) moreinfocard.DrawRoundedRectangle(1, 1, float64(moreinfocard.W()-1*2), float64(moreinfocard.H()-1*2), 16)
moreinfocard.ClipPreserve()
moreinfocard.DrawImage(blurback, -70, -70-titlecardh-40-basiccardh-40-diskcardh-40)
moreinfocard.SetColor(colorswitch(140))
moreinfocard.FillPreserve()
moreinfocard.SetLineWidth(3) moreinfocard.SetLineWidth(3)
moreinfocard.SetColor(colorswitch(100)) moreinfocard.SetColor(colorswitch(100))
moreinfocard.StrokePreserve() moreinfocard.ResetClip()
moreinfocard.SetColor(colorswitch(140)) moreinfocard.Stroke()
moreinfocard.Fill()
err = moreinfocard.ParseFontFace(fontbyte, 32) err = moreinfocard.ParseFontFace(fontbyte, 32)
if err != nil { if err != nil {
@ -488,7 +501,7 @@ func drawstatus(m *ctrl.Control[*zero.Ctx], uid int64, botname string, botrunsta
moreinfocard.DrawStringAnchored(v.name, 20+fw/2, 30+(float64(moreinfocardh-30*2)-moreinfocard.FontHeight()*float64(milen))/float64(milen-1)+moreinfocard.FontHeight()/2+offset, 0.5, 0.5) moreinfocard.DrawStringAnchored(v.name, 20+fw/2, 30+(float64(moreinfocardh-30*2)-moreinfocard.FontHeight()*float64(milen))/float64(milen-1)+moreinfocard.FontHeight()/2+offset, 0.5, 0.5)
moreinfocard.DrawStringAnchored(v.text[0], float64(moreinfocard.W())-20-fw1/2, 30+(float64(moreinfocardh-30*2)-moreinfocard.FontHeight()*float64(milen))/float64(milen-1)+moreinfocard.FontHeight()/2+offset, 0.5, 0.5) moreinfocard.DrawStringAnchored(v.text[0], float64(moreinfocard.W())-20-fw1/2, 30+(float64(moreinfocardh-30*2)-moreinfocard.FontHeight()*float64(milen))/float64(milen-1)+moreinfocard.FontHeight()/2+offset, 0.5, 0.5)
} }
moreinfoimg = rendercard.Fillet(moreinfocard.Image(), 16) moreinfoimg = moreinfocard.Image()
}() }()
go func() { go func() {
defer wg.Done() defer wg.Done()
@ -668,7 +681,7 @@ func diskstate() (stateinfo []*status, err error) {
func moreinfo(m *ctrl.Control[*zero.Ctx]) (stateinfo []*status, err error) { func moreinfo(m *ctrl.Control[*zero.Ctx]) (stateinfo []*status, err error) {
var mems runtime.MemStats var mems runtime.MemStats
runtime.ReadMemStats(&mems) runtime.ReadMemStats(&mems)
fmtmem := storagefmt(float64(mems.Sys)) fmtmem := storagefmt(float64(mems.Alloc))
hostinfo, err := host.Info() hostinfo, err := host.Info()
if err != nil { if err != nil {

56
plugin/aiimage/config.go Normal file
View File

@ -0,0 +1,56 @@
// Package aiimage 提供AI画图功能配置
package aiimage
import (
"fmt"
"strings"
"sync"
sql "github.com/FloatTech/sqlite"
)
// storage 管理画图配置存储
type storage struct {
sync.RWMutex
db sql.Sqlite
}
// imageConfig 存储AI画图配置信息
type imageConfig struct {
ID int64 `db:"id"` // 主键ID
APIKey string `db:"apiKey"` // API密钥
APIURL string `db:"apiUrl"` // API地址
ModelName string `db:"modelName"` // 画图模型名称
}
// getConfig 获取当前配置
func (sdb *storage) getConfig() imageConfig {
sdb.RLock()
defer sdb.RUnlock()
cfg := imageConfig{}
_ = sdb.db.Find("config", &cfg, "WHERE id = 1")
return cfg
}
// setConfig 设置AI画图配置
func (sdb *storage) setConfig(apiKey, apiURL, modelName string) error {
sdb.Lock()
defer sdb.Unlock()
return sdb.db.Insert("config", &imageConfig{
ID: 1,
APIKey: apiKey,
APIURL: apiURL,
ModelName: modelName,
})
}
// PrintConfig 返回格式化后的配置信息
func (sdb *storage) PrintConfig() string {
cfg := sdb.getConfig()
var builder strings.Builder
builder.WriteString("当前AI画图配置:\n")
builder.WriteString(fmt.Sprintf("• 密钥: %s\n", cfg.APIKey))
builder.WriteString(fmt.Sprintf("• 接口地址: %s\n", cfg.APIURL))
builder.WriteString(fmt.Sprintf("• 模型名: %s\n", cfg.ModelName))
return builder.String()
}

171
plugin/aiimage/main.go Normal file
View File

@ -0,0 +1,171 @@
// Package aiimage AI画图
package aiimage
import (
"bytes"
"encoding/json"
"net/http"
"strings"
"time"
fcext "github.com/FloatTech/floatbox/ctxext"
"github.com/FloatTech/floatbox/web"
sql "github.com/FloatTech/sqlite"
"github.com/tidwall/gjson"
zero "github.com/wdvxdr1123/ZeroBot"
"github.com/wdvxdr1123/ZeroBot/message"
ctrl "github.com/FloatTech/zbpctrl"
"github.com/FloatTech/zbputils/control"
"github.com/FloatTech/zbputils/ctxext"
)
func init() {
var sdb = &storage{}
en := control.AutoRegister(&ctrl.Options[*zero.Ctx]{
DisableOnDefault: false,
Extra: control.ExtraFromString("aiimage"),
Brief: "AI画图",
Help: "- 设置AI画图密钥xxx\n" +
"- 设置AI画图接口地址https://api.siliconflow.cn/v1/images/generations\n" +
"- 设置AI画图模型名Kwai-Kolors/Kolors\n" +
"- 查看AI画图配置\n" +
"- AI画图 [描述]",
PrivateDataFolder: "aiimage",
})
getdb := fcext.DoOnceOnSuccess(func(ctx *zero.Ctx) bool {
sdb.db = sql.New(en.DataFolder() + "aiimage.db")
err := sdb.db.Open(time.Hour)
if err == nil {
// 创建配置表
err = sdb.db.Create("config", &imageConfig{})
if err != nil {
ctx.SendChain(message.Text("[ERROR]:", err))
return false
}
return true
}
ctx.SendChain(message.Text("[ERROR]:", err))
return false
})
en.OnPrefix("设置AI画图密钥", getdb, zero.OnlyPrivate, zero.SuperUserPermission).SetBlock(true).
Handle(func(ctx *zero.Ctx) {
apiKey := strings.TrimSpace(ctx.State["args"].(string))
cfg := sdb.getConfig()
err := sdb.setConfig(apiKey, cfg.APIURL, cfg.ModelName)
if err != nil {
ctx.SendChain(message.Text("ERROR: 设置API密钥失败: ", err))
return
}
ctx.SendChain(message.Text("成功设置API密钥"))
})
en.OnPrefix("设置AI画图接口地址", getdb, zero.OnlyPrivate, zero.SuperUserPermission).SetBlock(true).
Handle(func(ctx *zero.Ctx) {
apiURL := strings.TrimSpace(ctx.State["args"].(string))
cfg := sdb.getConfig()
err := sdb.setConfig(cfg.APIKey, apiURL, cfg.ModelName)
if err != nil {
ctx.SendChain(message.Text("ERROR: 设置API地址失败: ", err))
return
}
ctx.SendChain(message.Text("成功设置API地址"))
})
en.OnPrefix("设置AI画图模型名", getdb, zero.OnlyPrivate, zero.SuperUserPermission).SetBlock(true).
Handle(func(ctx *zero.Ctx) {
modelName := strings.TrimSpace(ctx.State["args"].(string))
cfg := sdb.getConfig()
err := sdb.setConfig(cfg.APIKey, cfg.APIURL, modelName)
if err != nil {
ctx.SendChain(message.Text("ERROR: 设置模型失败: ", err))
return
}
ctx.SendChain(message.Text("成功设置模型: ", modelName))
})
en.OnFullMatch("查看AI画图配置", getdb, zero.OnlyPrivate, zero.SuperUserPermission).SetBlock(true).
Handle(func(ctx *zero.Ctx) {
ctx.SendChain(message.Text(sdb.PrintConfig()))
})
en.OnPrefix("AI画图", getdb).SetBlock(true).
Handle(func(ctx *zero.Ctx) {
ctx.SendChain(message.Text("少女思考中..."))
prompt := strings.TrimSpace(ctx.State["args"].(string))
if prompt == "" {
ctx.SendChain(message.Text("请输入图片描述"))
return
}
cfg := sdb.getConfig()
if cfg.APIKey == "" || cfg.APIURL == "" || cfg.ModelName == "" {
ctx.SendChain(message.Text("请先配置API密钥、地址和模型"))
return
}
// 准备请求数据
reqBytes, _ := json.Marshal(map[string]interface{}{
"model": cfg.ModelName,
"prompt": prompt,
"image_size": "1024x1024",
"batch_size": 4,
"num_inference_steps": 20,
"guidance_scale": 7.5,
})
// 发送API请求
data, err := web.RequestDataWithHeaders(
web.NewDefaultClient(),
cfg.APIURL,
"POST",
func(req *http.Request) error {
req.Header.Set("Authorization", "Bearer "+cfg.APIKey)
req.Header.Set("Content-Type", "application/json")
return nil
},
bytes.NewReader(reqBytes),
)
if err != nil {
ctx.SendChain(message.Text("API请求失败: ", err))
return
}
// 解析API响应
jsonData := gjson.ParseBytes(data)
images := jsonData.Get("images")
if !images.Exists() {
images = jsonData.Get("data")
if !images.Exists() {
ctx.SendChain(message.Text("未获取到图片URL"))
return
}
}
// 发送生成的图片和相关信息
inferenceTime := jsonData.Get("timings.inference").Float()
seed := jsonData.Get("seed").Int()
msg := make(message.Message, 0, 1)
msg = append(msg, ctxext.FakeSenderForwardNode(ctx, message.Text("图片生成成功!\n",
"提示词: ", prompt, "\n",
"模型: ", cfg.ModelName, "\n",
"推理时间: ", inferenceTime, "秒\n",
"种子: ", seed)))
// 添加所有图片
images.ForEach(func(_, value gjson.Result) bool {
url := value.Get("url").String()
if url != "" {
msg = append(msg, ctxext.FakeSenderForwardNode(ctx, message.Image(url)))
}
return true
})
if len(msg) > 0 {
ctx.Send(msg)
}
})
}

134
plugin/airecord/record.go Normal file
View File

@ -0,0 +1,134 @@
// Package airecord 群应用AI声聊
package airecord
import (
"strconv"
"strings"
"time"
"github.com/tidwall/gjson"
zero "github.com/wdvxdr1123/ZeroBot"
"github.com/wdvxdr1123/ZeroBot/message"
"github.com/FloatTech/AnimeAPI/airecord"
ctrl "github.com/FloatTech/zbpctrl"
"github.com/FloatTech/zbputils/control"
)
func init() {
en := control.AutoRegister(&ctrl.Options[*zero.Ctx]{
DisableOnDefault: false,
Extra: control.ExtraFromString("airecord"),
Brief: "群应用AI声聊",
Help: "- 设置AI语音群号1048452984(tips机器人任意所在群聊即可)\n" +
"- 设置AI语音模型\n" +
"- 查看AI语音配置\n" +
"- 发送AI语音xxx",
PrivateDataFolder: "airecord",
})
en.OnPrefix("设置AI语音群号", zero.OnlyPrivate, zero.SuperUserPermission).SetBlock(true).
Handle(func(ctx *zero.Ctx) {
u := strings.TrimSpace(ctx.State["args"].(string))
num, err := strconv.ParseInt(u, 10, 64)
if err != nil {
ctx.SendChain(message.Text("ERROR: parse gid err: ", err))
return
}
err = airecord.SetCustomGID(num)
if err != nil {
ctx.SendChain(message.Text("ERROR: set gid err: ", err))
return
}
ctx.SendChain(message.Text("设置AI语音群号为", num))
})
en.OnFullMatch("设置AI语音模型", zero.OnlyPrivate, zero.SuperUserPermission).SetBlock(true).
Handle(func(ctx *zero.Ctx) {
next := zero.NewFutureEvent("message", 999, false, ctx.CheckSession())
recv, cancel := next.Repeat()
defer cancel()
jsonData := ctx.GetAICharacters(0, 1)
// 转换为字符串数组
var names []string
// 初始化两个映射表
nameToID := make(map[string]string)
nameToURL := make(map[string]string)
characters := jsonData.Get("#.characters")
// 遍历每个角色对象
characters.ForEach(func(_, group gjson.Result) bool {
group.ForEach(func(_, character gjson.Result) bool {
// 提取当前角色的三个字段
name := character.Get("character_name").String()
names = append(names, name)
// 存入映射表(重复名称会覆盖,保留最后出现的条目)
nameToID[name] = character.Get("character_id").String()
nameToURL[name] = character.Get("preview_url").String()
return true // 继续遍历
})
return true // 继续遍历
})
var builder strings.Builder
// 写入开头文本
builder.WriteString("请选择语音模型序号:\n")
// 遍历names数组拼接序号和名称
for i, v := range names {
// 将数字转换为字符串不依赖fmt
numStr := strconv.Itoa(i)
// 拼接格式:"序号. 名称\n"
builder.WriteString(numStr)
builder.WriteString(". ")
builder.WriteString(v)
builder.WriteString("\n")
}
// 获取最终字符串
ctx.SendChain(message.Text(builder.String()))
for {
select {
case <-time.After(time.Second * 120):
ctx.SendChain(message.Text("设置AI语音模型指令过期"))
return
case ct := <-recv:
msg := ct.Event.Message.ExtractPlainText()
num, err := strconv.Atoi(msg)
if err != nil {
ctx.SendChain(message.Text("请输入数字!"))
continue
}
if num < 0 || num >= len(names) {
ctx.SendChain(message.Text("序号非法!"))
continue
}
err = airecord.SetRecordModel(names[num], nameToID[names[num]])
if err != nil {
ctx.SendChain(message.Text("ERROR: set model err: ", err))
continue
}
ctx.SendChain(message.Text("已选择语音模型: ", names[num]))
ctx.SendChain(message.Record(nameToURL[names[num]]))
return
}
}
})
en.OnFullMatch("查看AI语音配置", zero.OnlyPrivate, zero.SuperUserPermission).SetBlock(true).
Handle(func(ctx *zero.Ctx) {
ctx.SendChain(message.Text(airecord.PrintRecordConfig()))
})
en.OnPrefix("发送AI语音", zero.UserOrGrpAdmin).SetBlock(true).
Handle(func(ctx *zero.Ctx) {
u := strings.TrimSpace(ctx.State["args"].(string))
recCfg := airecord.GetConfig()
record := ctx.GetAIRecord(recCfg.ModelID, recCfg.Customgid, u)
if record == "" {
id := ctx.SendGroupAIRecord(recCfg.ModelID, ctx.Event.GroupID, u)
if id == "" {
ctx.SendChain(message.Text("ERROR: get record err: empty record"))
return
}
}
ctx.SendChain(message.Record(record))
})
}

View File

@ -1,286 +0,0 @@
package aireply
import (
"errors"
"strings"
"github.com/RomiChan/syncx"
zero "github.com/wdvxdr1123/ZeroBot"
"github.com/FloatTech/AnimeAPI/aireply"
"github.com/FloatTech/AnimeAPI/tts"
"github.com/FloatTech/AnimeAPI/tts/baidutts"
"github.com/FloatTech/AnimeAPI/tts/genshin"
"github.com/FloatTech/AnimeAPI/tts/lolimi"
"github.com/FloatTech/AnimeAPI/tts/ttscn"
ctrl "github.com/FloatTech/zbpctrl"
"github.com/FloatTech/zbputils/control"
)
// 数据结构: [8 bits] [8 bits] [8 bits]
// [具体人物] [tts模式] [回复模式]
// defaultttsindexkey
// 数据结构: [8 bits] [8 bits]
// [具体人物] [tts模式]
// [tts模式]: 0~200 genshin 201 baidu 202 ttscn 203 lolimi
const (
baiduttsindex = 201 + iota
ttscnttsindex
lolimittsindex
)
// extrattsname is the tts other than genshin vits
var extrattsname = []string{"百度", "TTSCN", "桑帛云"}
var ttscnspeakers = [...]string{
"晓晓(女 - 年轻人)",
"云扬(男 - 年轻人)",
"晓辰(女 - 年轻人 - 抖音热门)",
"晓涵(女 - 年轻人)",
"晓墨(女 - 年轻人)",
"晓秋(女 - 中年人)",
"晓睿(女 - 老年)",
"晓双(女 - 儿童)",
"晓萱(女 - 年轻人)",
"晓颜(女 - 年轻人)",
"晓悠(女 - 儿童)",
"云希(男 - 年轻人 - 抖音热门)",
"云野(男 - 中年人)",
"晓梦(女 - 年轻人)",
"晓伊(女 - 儿童)",
"晓甄(女 - 年轻人)",
}
const defaultttsindexkey = -2905
var (
= newapikeystore("./data/tts/o.txt")
ཆཏ = newapikeystore("./data/tts/c.txt")
= newapikeystore("./data/tts/b.txt")
= newapikeystore("./data/tts/s.txt")
)
type replymode []string
func (r replymode) setReplyMode(ctx *zero.Ctx, name string) error {
gid := ctx.Event.GroupID
if gid == 0 {
gid = -ctx.Event.UserID
}
var ok bool
var index int64
for i, s := range r {
if s == name {
ok = true
index = int64(i)
break
}
}
if !ok {
return errors.New("no such mode")
}
m, ok := ctx.State["manager"].(*ctrl.Control[*zero.Ctx])
if !ok {
return errors.New("no such plugin")
}
return m.SetData(gid, (m.GetData(gid)&^0xff)|(index&0xff))
}
func (r replymode) getReplyMode(ctx *zero.Ctx) aireply.AIReply {
k := .k
gid := ctx.Event.GroupID
if gid == 0 {
gid = -ctx.Event.UserID
}
m, ok := ctx.State["manager"].(*ctrl.Control[*zero.Ctx])
if ok {
switch m.GetData(gid) & 0xff {
case 0:
return aireply.NewLolimiAi(aireply.JingfengURL, aireply.JingfengBotName, k, false, 0)
case 1:
return aireply.NewLolimiAi(aireply.MomoURL, aireply.MomoBotName, k, false, 0)
case 2:
return aireply.NewQYK(aireply.QYKURL, aireply.QYKBotName)
case 3:
return aireply.NewXiaoAi(aireply.XiaoAiURL, aireply.XiaoAiBotName)
case 4:
if ཆཏ.k != "" {
return aireply.NewChatGPT(aireply.ChatGPTURL, ཆཏ.k)
}
return aireply.NewLolimiAi(aireply.JingfengURL, aireply.JingfengBotName, k, false, 0)
}
}
return aireply.NewLolimiAi(aireply.JingfengURL, aireply.JingfengBotName, k, false, 0)
}
var ttsins = func() map[string]tts.TTS {
m := make(map[string]tts.TTS, 512)
for _, mode := range append(genshin.SoundList[:], extrattsname...) {
m[mode] = nil
}
return m
}()
var ttsModes = func() []string {
s := append(genshin.SoundList[:], make([]string, baiduttsindex-len(genshin.SoundList))...) // 0-200
s = append(s, extrattsname...) // 201 202 ...
return s
}()
type ttsmode syncx.Map[int64, int64]
func list(list []string, num int) string {
s := ""
for i, value := range list {
s += value
if (i+1)%num == 0 {
s += "\n"
} else {
s += " | "
}
}
return s
}
func newttsmode() *ttsmode {
t := &ttsmode{}
m, ok := control.Lookup("tts")
(*syncx.Map[int64, int64])(t).Store(defaultttsindexkey, 0)
if ok {
index := m.GetData(defaultttsindexkey)
msk := index & 0xff
if msk >= 0 && (msk < int64(len(ttsModes))) {
(*syncx.Map[int64, int64])(t).Store(defaultttsindexkey, index)
}
}
return t
}
func (t *ttsmode) setSoundMode(ctx *zero.Ctx, name string, character int) error {
gid := ctx.Event.GroupID
if gid == 0 {
gid = -ctx.Event.UserID
}
_, ok := ttsins[name]
if !ok {
return errors.New("不支持设置语音人物" + name)
}
var index = int64(-1)
for i, s := range genshin.SoundList {
if s == name {
index = int64(i + 1)
break
}
}
if index == -1 {
switch name {
case extrattsname[0]:
index = baiduttsindex
case extrattsname[1]:
index = ttscnttsindex
case extrattsname[2]:
index = lolimittsindex
default:
return errors.New("语音人物" + name + "未注册index")
}
}
m := ctx.State["manager"].(*ctrl.Control[*zero.Ctx])
// 按原来的逻辑map存的是前16位
storeIndex := (m.GetData(gid) &^ 0xffff00) | ((index << 8) & 0xff00) | ((int64(character) << 16) & 0xff0000)
(*syncx.Map[int64, int64])(t).Store(gid, (storeIndex>>8)&0xffff)
return m.SetData(gid, storeIndex)
}
func (t *ttsmode) getSoundMode(ctx *zero.Ctx) (tts.TTS, error) {
gid := ctx.Event.GroupID
if gid == 0 {
gid = -ctx.Event.UserID
}
i, ok := (*syncx.Map[int64, int64])(t).Load(gid)
if !ok {
m := ctx.State["manager"].(*ctrl.Control[*zero.Ctx])
i = m.GetData(gid) >> 8
}
m := i & 0xff
if m <= 0 || (m >= int64(len(ttsModes))) {
i, _ = (*syncx.Map[int64, int64])(t).Load(defaultttsindexkey)
if i == 0 {
i = ctx.State["manager"].(*ctrl.Control[*zero.Ctx]).GetData(defaultttsindexkey)
(*syncx.Map[int64, int64])(t).Store(defaultttsindexkey, i)
}
m = i & 0xff
}
mode := ttsModes[m]
ins, ok := ttsins[mode]
if !ok || ins == nil {
switch mode {
case extrattsname[0]:
id, sec, _ := strings.Cut(.k, ",")
ins = baidutts.NewBaiduTTS(int(i&0xff00)>>8, id, sec)
case extrattsname[1]:
var err error
ins, err = ttscn.NewTTSCN("中文(普通话,简体)", ttscnspeakers[int(i&0xff00)>>8], ttscn.KBRates[0])
if err != nil {
return nil, err
}
case extrattsname[2]:
ins = lolimi.NewLolimi(int(i&0xff00) >> 8)
default: // 原神
k := .k
if k != "" {
ins = genshin.NewGenshin(int(m-1), .k)
ttsins[mode] = ins
} else {
ins = lolimi.NewLolimi(int(i&0xff00) >> 8)
}
}
}
return ins, nil
}
func (t *ttsmode) resetSoundMode(ctx *zero.Ctx) error {
gid := ctx.Event.GroupID
if gid == 0 {
gid = -ctx.Event.UserID
}
m := ctx.State["manager"].(*ctrl.Control[*zero.Ctx])
// 只保留后面8位
(*syncx.Map[int64, int64])(t).Delete(gid)
return m.SetData(gid, (m.GetData(gid) & 0xff)) // 重置数据
}
func (t *ttsmode) setDefaultSoundMode(name string, character int) error {
_, ok := ttsins[name]
if !ok {
return errors.New("不支持设置语音人物" + name)
}
index := int64(-1)
for i, s := range genshin.SoundList {
if s == name {
index = int64(i + 1)
break
}
}
if index == -1 {
switch name {
case extrattsname[0]:
index = baiduttsindex
case extrattsname[1]:
index = ttscnttsindex
case extrattsname[2]:
index = lolimittsindex
default:
return errors.New("语音人物" + name + "未注册index")
}
}
m, ok := control.Lookup("tts")
if !ok {
return errors.New("[tts] service not found")
}
storeIndex := (index & 0xff) | ((int64(character) << 8) & 0xff00)
(*syncx.Map[int64, int64])(t).Store(defaultttsindexkey, storeIndex)
return m.SetData(defaultttsindexkey, storeIndex)
}

View File

@ -1,235 +0,0 @@
// Package aireply AI 回复
package aireply
import (
"os"
"regexp"
"strconv"
"strings"
"time"
"github.com/FloatTech/AnimeAPI/tts/genshin"
ctrl "github.com/FloatTech/zbpctrl"
"github.com/FloatTech/zbputils/control"
"github.com/FloatTech/zbputils/ctxext"
"github.com/sirupsen/logrus"
zero "github.com/wdvxdr1123/ZeroBot"
"github.com/wdvxdr1123/ZeroBot/message"
)
var replmd = replymode([]string{"婧枫", "沫沫", "青云客", "小爱", "ChatGPT"})
var ttsmd = newttsmode()
func init() { // 插件主体
ent := control.Register("tts", &ctrl.Options[*zero.Ctx]{
DisableOnDefault: true,
Brief: "人工智能语音回复",
Help: "- @Bot 任意文本(任意一句话回复)\n" +
"- 设置语音模式[原神人物/百度/TTSCN/桑帛云] 数字(百度/TTSCN说话人/桑帛云)\n" +
"- 设置默认语音模式[原神人物/百度/TTSCN/桑帛云] 数字(百度/TTSCN说话人/桑帛云)\n" +
"- 恢复成默认语音模式\n" +
"- 设置语音回复模式[沫沫|婧枫|青云客|小爱|ChatGPT]\n" +
"- 设置原神语音 api key xxxxxx (key请加开发群获得)\n" +
"- 设置百度语音 api id xxxxxx secret xxxxxx (请自行获得)\n" +
"当前适用的原神人物含有以下: \n" + list(genshin.SoundList[:], 5) +
"\n当前适用的TTSCN人物含有以下(以数字顺序代表): \n" + list(ttscnspeakers[:], 5),
PrivateDataFolder: "tts",
})
enr := control.AutoRegister(&ctrl.Options[*zero.Ctx]{
DisableOnDefault: false,
Brief: "人工智能回复",
Help: "- @Bot 任意文本(任意一句话回复)\n- 设置文字回复模式[婧枫|沫沫|青云客|小爱|ChatGPT]\n- 设置 ChatGPT api key xxx",
PrivateDataFolder: "aireply",
})
enr.OnMessage(zero.OnlyToMe).SetBlock(true).Limit(ctxext.LimitByUser).
Handle(func(ctx *zero.Ctx) {
aireply := replmd.getReplyMode(ctx)
reply := message.ParseMessageFromString(aireply.Talk(ctx.Event.UserID, ctx.ExtractPlainText(), zero.BotConfig.NickName[0]))
// 回复
time.Sleep(time.Second * 1)
reply = append(reply, message.Reply(ctx.Event.MessageID))
ctx.Send(reply)
})
setReplyMode := func(ctx *zero.Ctx) {
param := ctx.State["args"].(string)
err := replmd.setReplyMode(ctx, param)
if err != nil {
ctx.SendChain(message.Reply(ctx.Event.MessageID), message.Text(err))
return
}
ctx.SendChain(message.Reply(ctx.Event.MessageID), message.Text("成功"))
}
enr.OnPrefix("设置文字回复模式", zero.AdminPermission).SetBlock(true).Handle(setReplyMode)
enr.OnRegex(`^设置\s*桑帛云\s*api\s*key\s*(.*)$`, zero.OnlyPrivate, zero.SuperUserPermission).SetBlock(true).Handle(func(ctx *zero.Ctx) {
err := .set(ctx.State["regex_matched"].([]string)[1])
if err != nil {
ctx.SendChain(message.Text("ERROR: ", err))
return
}
ctx.SendChain(message.Text("设置成功"))
})
enr.OnRegex(`^设置\s*ChatGPT\s*api\s*key\s*(.*)$`, zero.OnlyPrivate, zero.SuperUserPermission).SetBlock(true).Handle(func(ctx *zero.Ctx) {
err := ཆཏ.set(ctx.State["regex_matched"].([]string)[1])
if err != nil {
ctx.SendChain(message.Text("ERROR: ", err))
return
}
ctx.SendChain(message.Text("设置成功"))
})
endpre := regexp.MustCompile(`\pP$`)
ttscachedir := ent.DataFolder() + "cache/"
_ = os.RemoveAll(ttscachedir)
err := os.MkdirAll(ttscachedir, 0755)
if err != nil {
panic(err)
}
ent.OnMessage(zero.OnlyToMe).SetBlock(true).Limit(ctxext.LimitByUser).
Handle(func(ctx *zero.Ctx) {
msg := ctx.ExtractPlainText()
// 获取回复模式
r := replmd.getReplyMode(ctx)
// 获取回复的文本
reply := message.ParseMessageFromString(r.TalkPlain(ctx.Event.UserID, msg, zero.BotConfig.NickName[0]))
// 过滤掉文字消息
filterMsg := make([]message.MessageSegment, 0, len(reply))
sb := strings.Builder{}
for _, v := range reply {
if v.Type != "text" {
filterMsg = append(filterMsg, v)
} else {
sb.WriteString(v.Data["text"])
}
}
// 纯文本
plainReply := sb.String()
plainReply = strings.ReplaceAll(plainReply, "\n", "")
// 获取语音
speaker, err := ttsmd.getSoundMode(ctx)
if err != nil {
ctx.SendChain(message.Text("ERROR: ", err))
return
}
rec, err := speaker.Speak(ctx.Event.UserID, func() string {
if !endpre.MatchString(plainReply) {
return plainReply + "。"
}
return plainReply
})
// 发送前面的图片
if len(filterMsg) != 0 {
filterMsg = append(filterMsg, message.Reply(ctx.Event.MessageID))
ctx.Send(filterMsg)
}
if err != nil {
ctx.SendChain(message.Reply(ctx.Event.MessageID), message.Text(plainReply))
return
}
// 发送语音
if id := ctx.SendChain(message.Record(rec)); id.ID() == 0 {
ctx.SendChain(message.Reply(ctx.Event.MessageID), message.Text(plainReply))
}
})
ent.OnPrefix("设置语音回复模式", zero.AdminPermission).SetBlock(true).Handle(setReplyMode)
ent.OnRegex(`^设置语音模式\s*([\S\D]*)\s*(\d*)$`, zero.AdminPermission).SetBlock(true).Handle(func(ctx *zero.Ctx) {
param := ctx.State["regex_matched"].([]string)[1]
num := ctx.State["regex_matched"].([]string)[2]
n := 0
var err error
if num != "" {
n, err = strconv.Atoi(num)
if err != nil {
ctx.SendChain(message.Text("ERROR: ", err))
return
}
}
// 保存设置
logrus.Debugln("[tts] t.setSoundMode( ctx", param, n, ")")
err = ttsmd.setSoundMode(ctx, param, n)
if err != nil {
ctx.SendChain(message.Reply(ctx.Event.MessageID), message.Text(err))
return
}
banner := genshin.TestRecord[param]
if banner == "" {
banner = genshin.TestRecord["默认"]
}
logrus.Debugln("[tts] banner:", banner, "get sound mode...")
// 设置验证
speaker, err := ttsmd.getSoundMode(ctx)
if err != nil {
ctx.SendChain(message.Text("ERROR: ", err))
return
}
logrus.Debugln("[tts] got sound mode, speaking...")
rec, err := speaker.Speak(ctx.Event.UserID, func() string { return banner })
if err != nil {
ctx.SendChain(message.Reply(ctx.Event.MessageID), message.Text("无法发送测试语音,请重试。"))
return
}
logrus.Debugln("[tts] sending...")
if id := ctx.SendChain(message.Record(rec).Add("cache", 0)); id.ID() == 0 {
ctx.SendChain(message.Reply(ctx.Event.MessageID), message.Text("无法发送测试语音,请重试。"))
return
}
time.Sleep(time.Second * 2)
ctx.SendChain(message.Reply(ctx.Event.MessageID), message.Text("设置成功,当前为", speaker))
})
ent.OnRegex(`^设置默认语音模式\s*([\S\D]*)\s+(\d*)$`, zero.SuperUserPermission).SetBlock(true).Handle(func(ctx *zero.Ctx) {
param := ctx.State["regex_matched"].([]string)[1]
num := ctx.State["regex_matched"].([]string)[2]
n := 0
var err error
if num != "" {
n, err = strconv.Atoi(num)
if err != nil {
ctx.SendChain(message.Text("ERROR: ", err))
return
}
}
// 保存设置
err = ttsmd.setDefaultSoundMode(param, n)
if err != nil {
ctx.SendChain(message.Reply(ctx.Event.MessageID), message.Text(err))
return
}
ctx.SendChain(message.Reply(ctx.Event.MessageID), message.Text("设置成功"))
})
ent.OnFullMatch("恢复成默认语音模式", zero.AdminPermission).SetBlock(true).Handle(func(ctx *zero.Ctx) {
err := ttsmd.resetSoundMode(ctx)
if err != nil {
ctx.SendChain(message.Reply(ctx.Event.MessageID), message.Text(err))
return
}
// 设置验证
speaker, err := ttsmd.getSoundMode(ctx)
if err != nil {
ctx.SendChain(message.Text("ERROR: ", err))
return
}
ctx.SendChain(message.Reply(ctx.Event.MessageID), message.Text("设置成功,当前为", speaker))
})
ent.OnRegex(`^设置原神语音\s*api\s*key\s*([0-9a-zA-Z-_]{54}==)$`, zero.OnlyPrivate, zero.SuperUserPermission).SetBlock(true).Handle(func(ctx *zero.Ctx) {
err := .set(ctx.State["regex_matched"].([]string)[1])
if err != nil {
ctx.SendChain(message.Text("ERROR: ", err))
return
}
ctx.SendChain(message.Text("设置成功"))
})
ent.OnRegex(`^设置百度语音\s*api\s*id\s*(.*)\s*secret\s*(.*)\s*$`, zero.OnlyPrivate, zero.SuperUserPermission).SetBlock(true).Handle(func(ctx *zero.Ctx) {
err := .set(ctx.State["regex_matched"].([]string)[1] + "," + ctx.State["regex_matched"].([]string)[2])
if err != nil {
ctx.SendChain(message.Text("ERROR: ", err))
return
}
ctx.SendChain(message.Text("设置成功"))
})
}

View File

@ -1,29 +0,0 @@
package aireply
import (
"os"
"github.com/FloatTech/floatbox/binary"
"github.com/FloatTech/floatbox/file"
)
type apikeystore struct {
k string
p string
}
func newapikeystore(p string) (s apikeystore) {
s.p = p
if file.IsExist(p) {
data, err := os.ReadFile(p)
if err == nil {
s.k = binary.BytesToString(data)
}
}
return
}
func (s *apikeystore) set(k string) error {
s.k = k
return os.WriteFile(s.p, binary.StringToBytes(k), 0644)
}

145
plugin/animetrace/main.go Normal file
View File

@ -0,0 +1,145 @@
// Package animetrace AnimeTrace 动画/Galgame识别
package animetrace
import (
"bytes"
"encoding/base64"
"errors"
"fmt"
"image"
"image/jpeg"
"mime/multipart"
"strings"
"github.com/FloatTech/floatbox/web"
"github.com/FloatTech/imgfactory"
ctrl "github.com/FloatTech/zbpctrl"
"github.com/FloatTech/zbputils/control"
"github.com/FloatTech/zbputils/ctxext"
"github.com/disintegration/imaging"
"github.com/tidwall/gjson"
zero "github.com/wdvxdr1123/ZeroBot"
"github.com/wdvxdr1123/ZeroBot/message"
)
func init() {
engine := control.AutoRegister(&ctrl.Options[*zero.Ctx]{
DisableOnDefault: false,
Brief: "AnimeTrace 动画/Galgame识别插件",
Help: "- Gal识图\n- 动漫识图\n- 动漫识图 2\n- 动漫识图 [模型名]\n- Gal识图 [模型名]",
})
engine.OnPrefix("gal识图", zero.OnlyGroup, zero.MustProvidePicture).SetBlock(true).Handle(func(ctx *zero.Ctx) {
args := ctx.State["args"].(string)
var model string
switch strings.TrimSpace(args) {
case "":
model = "full_game_model_kira" // 默认使用的模型
default:
model = args // 自定义设置模型
}
processImageRecognition(ctx, model)
})
engine.OnPrefix("动漫识图", zero.OnlyGroup, zero.MustProvidePicture).SetBlock(true).Handle(func(ctx *zero.Ctx) {
args := ctx.State["args"].(string)
var model string
switch strings.TrimSpace(args) {
case "":
model = "anime_model_lovelive"
case "2":
model = "pre_stable"
default:
model = args
}
processImageRecognition(ctx, model)
})
}
// 处理图片识别
func processImageRecognition(ctx *zero.Ctx, model string) {
urls := ctx.State["image_url"].([]string)
if len(urls) == 0 {
return
}
imageData, err := imgfactory.Load(urls[0])
if err != nil {
ctx.Send(message.Text("下载图片失败: ", err))
return
}
// ctx.Send(message.Text(model))
respBody, err := createAndSendMultipartRequest("https://api.animetrace.com/v1/search", imageData, map[string]string{
"is_multi": "0",
"model": model,
"ai_detect": "0",
})
if err != nil {
ctx.Send(message.Text("识别请求失败: ", err))
return
}
code := gjson.Get(string(respBody), "code").Int()
if code != 0 {
ctx.Send(message.Text("错误: ", gjson.Get(string(respBody), "zh_message").String()))
return
}
dataArray := gjson.Get(string(respBody), "data").Array()
if len(dataArray) == 0 {
ctx.Send(message.Text("未识别到任何角色"))
return
}
var sk message.Message
sk = append(sk, ctxext.FakeSenderForwardNode(ctx, message.Text("共识别到 ", len(dataArray), " 个角色,可能是以下来源")))
for _, value := range dataArray {
boxArray := value.Get("box").Array()
imgWidth, imgHeight := imageData.Bounds().Dx(), imageData.Bounds().Dy() // 你可以从 `imageData.Bounds()` 获取
box := []int{
int(boxArray[0].Float() * float64(imgWidth)),
int(boxArray[1].Float() * float64(imgHeight)),
int(boxArray[2].Float() * float64(imgWidth)),
int(boxArray[3].Float() * float64(imgHeight)),
}
croppedImg := imaging.Crop(imageData, image.Rect(box[0], box[1], box[2], box[3]))
var buf bytes.Buffer
if err := imaging.Encode(&buf, croppedImg, imaging.JPEG, imaging.JPEGQuality(80)); err != nil {
ctx.Send(message.Text("图片编码失败: ", err))
continue
}
base64Str := base64.StdEncoding.EncodeToString(buf.Bytes())
var sb strings.Builder
value.Get("character").ForEach(func(_, character gjson.Result) bool {
sb.WriteString(fmt.Sprintf("《%s》的角色 %s\n", character.Get("work").String(), character.Get("character").String()))
return true
})
sk = append(sk, ctxext.FakeSenderForwardNode(ctx, message.Image("base64://"+base64Str), message.Text(sb.String())))
}
ctx.SendGroupForwardMessage(ctx.Event.GroupID, sk)
}
// 发送图片识别请求
func createAndSendMultipartRequest(url string, img image.Image, formFields map[string]string) ([]byte, error) {
body := &bytes.Buffer{}
writer := multipart.NewWriter(body)
// 直接编码图片
part, err := writer.CreateFormFile("file", "image.jpg")
if err != nil {
return nil, errors.New("创建文件字段失败: " + err.Error())
}
if err := jpeg.Encode(part, img, &jpeg.Options{Quality: 80}); err != nil {
return nil, errors.New("图片编码失败: " + err.Error())
}
// 写入其他字段
for key, value := range formFields {
if err := writer.WriteField(key, value); err != nil {
return nil, errors.New("写入表单字段失败 (" + key + "): " + err.Error())
}
}
if err := writer.Close(); err != nil {
return nil, errors.New("关闭 multipart writer 失败: " + err.Error())
}
return web.PostData(url, writer.FormDataContentType(), body)
}

View File

@ -17,7 +17,12 @@ import (
"github.com/wdvxdr1123/ZeroBot/message" "github.com/wdvxdr1123/ZeroBot/message"
) )
const bandur time.Duration = time.Minute * 10 const (
bandur time.Duration = time.Minute * 2
add = "添加违禁词"
del = "删除违禁词"
list = "查看违禁词"
)
var ( var (
managers *ctrl.Manager[*zero.Ctx] // managers lazy load managers *ctrl.Manager[*zero.Ctx] // managers lazy load
@ -41,7 +46,7 @@ func init() {
engine := control.AutoRegister(&ctrl.Options[*zero.Ctx]{ engine := control.AutoRegister(&ctrl.Options[*zero.Ctx]{
DisableOnDefault: false, DisableOnDefault: false,
Brief: "违禁词检测", Brief: "违禁词检测",
Help: "- /[添加|删除|查看]违禁词", Help: "- [添加|删除|查看]违禁词",
PrivateDataFolder: "anti_abuse", PrivateDataFolder: "anti_abuse",
}) })
@ -56,10 +61,14 @@ func init() {
return true return true
}) })
engine.OnMessage(onceRule, zero.OnlyGroup, func(ctx *zero.Ctx) bool { notAntiabuse := func(ctx *zero.Ctx) bool {
if !ctx.Event.IsToMe { if zero.PrefixRule(add)(ctx) || zero.PrefixRule(del)(ctx) || zero.PrefixRule(list)(ctx) {
return false
}
return true return true
} }
engine.OnMessage(onceRule, notAntiabuse, zero.OnlyGroup, func(ctx *zero.Ctx) bool {
uid := ctx.Event.UserID uid := ctx.Event.UserID
gid := ctx.Event.GroupID gid := ctx.Event.GroupID
msg := strings.ReplaceAll(ctx.MessageString(), "\n", "") msg := strings.ReplaceAll(ctx.MessageString(), "\n", "")
@ -70,7 +79,8 @@ func init() {
if err := ctx.State["manager"].(*ctrl.Control[*zero.Ctx]).Manager.DoBlock(uid); err == nil { if err := ctx.State["manager"].(*ctrl.Control[*zero.Ctx]).Manager.DoBlock(uid); err == nil {
t := time.Now().Unix() t := time.Now().Unix()
cache.Set(uid, struct{}{}) cache.Set(uid, struct{}{})
ctx.SetThisGroupBan(uid, int64(bandur.Minutes())) ctx.SetThisGroupBan(uid, int64(bandur.Seconds()))
ctx.DeleteMessage(ctx.Event.MessageID)
ctx.SendChain(message.Text("检测到违禁词, 已封禁/屏蔽", bandur)) ctx.SendChain(message.Text("检测到违禁词, 已封禁/屏蔽", bandur))
db.Lock() db.Lock()
defer db.Unlock() defer db.Unlock()
@ -92,9 +102,9 @@ func init() {
return true return true
}) })
engine.OnCommand("添加违禁词", zero.OnlyGroup, zero.AdminPermission, onceRule).Handle( engine.OnPrefix(add, zero.OnlyGroup, zero.AdminPermission, onceRule).SetBlock(true).Handle(
func(ctx *zero.Ctx) { func(ctx *zero.Ctx) {
args := ctx.State["args"].(string) args := strings.TrimSpace(ctx.State["args"].(string))
if err := db.insertWord(ctx.Event.GroupID, args); err != nil { if err := db.insertWord(ctx.Event.GroupID, args); err != nil {
ctx.SendChain(message.Text("ERROR: ", err)) ctx.SendChain(message.Text("ERROR: ", err))
} else { } else {
@ -102,9 +112,9 @@ func init() {
} }
}) })
engine.OnCommand("删除违禁词", zero.OnlyGroup, zero.AdminPermission, onceRule).Handle( engine.OnPrefix(del, zero.OnlyGroup, zero.AdminPermission, onceRule).SetBlock(true).Handle(
func(ctx *zero.Ctx) { func(ctx *zero.Ctx) {
args := ctx.State["args"].(string) args := strings.TrimSpace(ctx.State["args"].(string))
if err := db.deleteWord(ctx.Event.GroupID, args); err != nil { if err := db.deleteWord(ctx.Event.GroupID, args); err != nil {
ctx.SendChain(message.Text("ERROR: ", err)) ctx.SendChain(message.Text("ERROR: ", err))
} else { } else {
@ -112,7 +122,7 @@ func init() {
} }
}) })
engine.OnCommand("查看违禁词", zero.OnlyGroup, onceRule).Handle( engine.OnPrefix(list, zero.OnlyGroup, onceRule).SetBlock(true).Handle(
func(ctx *zero.Ctx) { func(ctx *zero.Ctx) {
b, err := text.RenderToBase64(db.listWords(ctx.Event.GroupID), text.FontFile, 400, 20) b, err := text.RenderToBase64(db.listWords(ctx.Event.GroupID), text.FontFile, 400, 20)
if err != nil { if err != nil {

View File

@ -30,7 +30,7 @@ var (
) )
func newantidb(path string) (*antidb, error) { func newantidb(path string) (*antidb, error) {
db := &antidb{Sqlite: sqlite.Sqlite{DBPath: path}} db := &antidb{Sqlite: sqlite.New(path)}
err := db.Open(bandur) err := db.Open(bandur)
if err != nil { if err != nil {
return nil, err return nil, err
@ -46,7 +46,7 @@ func newantidb(path string) (*antidb, error) {
cache.Touch(nilbt.ID, -time.Since(t)) cache.Touch(nilbt.ID, -time.Since(t))
return nil return nil
}) })
_ = db.Del("__bantime__", "WHERE time<="+strconv.FormatInt(time.Now().Add(time.Minute-bandur).Unix(), 10)) _ = db.Del("__bantime__", "WHERE time <= ?", strconv.FormatInt(time.Now().Add(time.Minute-bandur).Unix(), 10))
return db, nil return db, nil
} }

View File

@ -19,7 +19,7 @@ import (
type datagetter func(string, bool) ([]byte, error) type datagetter func(string, bool) ([]byte, error)
func (dgtr datagetter) randImage(file ...string) message.MessageSegment { func (dgtr datagetter) randImage(file ...string) message.Segment {
data, err := dgtr(file[rand.Intn(len(file))], true) data, err := dgtr(file[rand.Intn(len(file))], true)
if err != nil { if err != nil {
return message.Text("ERROR: ", err) return message.Text("ERROR: ", err)
@ -27,7 +27,7 @@ func (dgtr datagetter) randImage(file ...string) message.MessageSegment {
return message.ImageBytes(data) return message.ImageBytes(data)
} }
func (dgtr datagetter) randRecord(file ...string) message.MessageSegment { func (dgtr datagetter) randRecord(file ...string) message.Segment {
data, err := dgtr(file[rand.Intn(len(file))], true) data, err := dgtr(file[rand.Intn(len(file))], true)
if err != nil { if err != nil {
return message.Text("ERROR: ", err) return message.Text("ERROR: ", err)
@ -35,7 +35,7 @@ func (dgtr datagetter) randRecord(file ...string) message.MessageSegment {
return message.Record("base64://" + base64.StdEncoding.EncodeToString(data)) return message.Record("base64://" + base64.StdEncoding.EncodeToString(data))
} }
func randText(text ...string) message.MessageSegment { func randText(text ...string) message.Segment {
return message.Text(text[rand.Intn(len(text))]) return message.Text(text[rand.Intn(len(text))])
} }

View File

@ -224,7 +224,7 @@ func (g *group) reply(bdres *baiduRes) message.Message {
g.mu.Lock() g.mu.Lock()
defer g.mu.Unlock() defer g.mu.Unlock()
// 建立消息段 // 建立消息段
msgs := make([]message.MessageSegment, 0, 8) msgs := make([]message.Segment, 0, 8)
// 生成简略审核结果回复 // 生成简略审核结果回复
msgs = append(msgs, message.Text(bdres.Conclusion, "\n")) msgs = append(msgs, message.Text(bdres.Conclusion, "\n"))
// 查看是否开启详细审核内容提示, 并确定审核内容值为疑似, 或者不合规 // 查看是否开启详细审核内容提示, 并确定审核内容值为疑似, 或者不合规

View File

@ -11,7 +11,6 @@ import (
"net/http" "net/http"
"os" "os"
"path" "path"
"regexp"
"sort" "sort"
"strconv" "strconv"
"time" "time"
@ -31,7 +30,6 @@ import (
) )
var ( var (
re = regexp.MustCompile(`^\d+$`)
danmakuTypeMap = map[int64]string{ danmakuTypeMap = map[int64]string{
0: "普通消息", 0: "普通消息",
1: "礼物", 1: "礼物",
@ -73,7 +71,7 @@ func init() {
} }
return true return true
}) })
engine.OnRegex(`^>user info\s?(.{1,25})$`, getPara).SetBlock(true). engine.OnRegex(`^>user info\s?(.{1,25})$`, bz.RequireUser(cfg)).SetBlock(true).
Handle(func(ctx *zero.Ctx) { Handle(func(ctx *zero.Ctx) {
id := ctx.State["uid"].(string) id := ctx.State["uid"].(string)
card, err := bz.GetMemberCard(id) card, err := bz.GetMemberCard(id)
@ -91,7 +89,7 @@ func init() {
)) ))
}) })
engine.OnRegex(`^>vup info\s?(.{1,25})$`, getPara).SetBlock(true). engine.OnRegex(`^>vup info\s?(.{1,25})$`, bz.RequireUser(cfg)).SetBlock(true).
Handle(func(ctx *zero.Ctx) { Handle(func(ctx *zero.Ctx) {
id := ctx.State["uid"].(string) id := ctx.State["uid"].(string)
// 获取详情 // 获取详情
@ -114,7 +112,7 @@ func init() {
)) ))
}) })
engine.OnRegex(`^查成分\s?(.{1,25})$`, getPara, getdb).SetBlock(true). engine.OnRegex(`^查成分\s?(.{1,25})$`, bz.RequireUser(cfg), getdb).SetBlock(true).
Handle(func(ctx *zero.Ctx) { Handle(func(ctx *zero.Ctx) {
id := ctx.State["uid"].(string) id := ctx.State["uid"].(string)
today := time.Now().Format("20060102") today := time.Now().Format("20060102")
@ -134,7 +132,7 @@ func init() {
return return
} }
vupLen := len(vups) vupLen := len(vups)
medals, err := bz.GetMedalWall(cfg, id) medals, err := cfg.GetMedalWall(id)
sort.Sort(bz.MedalSorter(medals)) sort.Sort(bz.MedalSorter(medals))
if err != nil { if err != nil {
ctx.SendChain(message.Text("ERROR: ", err)) ctx.SendChain(message.Text("ERROR: ", err))
@ -275,7 +273,7 @@ func init() {
ctx.SendChain(message.Image("file:///" + file.BOTPATH + "/" + drawedFile)) ctx.SendChain(message.Image("file:///" + file.BOTPATH + "/" + drawedFile))
}) })
engine.OnRegex(`^查弹幕\s?(\S{1,25})\s?(\d*)$`, getPara).SetBlock(true).Handle(func(ctx *zero.Ctx) { engine.OnRegex(`^查弹幕\s?(\S{1,25})\s?(\d*)$`, bz.RequireUser(cfg)).SetBlock(true).Handle(func(ctx *zero.Ctx) {
id := ctx.State["uid"].(string) id := ctx.State["uid"].(string)
pagenum := ctx.State["regex_matched"].([]string)[2] pagenum := ctx.State["regex_matched"].([]string)[2]
if pagenum == "" { if pagenum == "" {
@ -582,51 +580,3 @@ func int2rbg(t int64) (int64, int64, int64) {
b, g, r := int64(buf[0]), int64(buf[1]), int64(buf[2]) b, g, r := int64(buf[0]), int64(buf[1]), int64(buf[2])
return r, g, b return r, g, b
} }
func getPara(ctx *zero.Ctx) bool {
keyword := ctx.State["regex_matched"].([]string)[1]
if !re.MatchString(keyword) {
searchRes, err := bz.SearchUser(cfg, keyword)
if err != nil {
ctx.SendChain(message.Text("ERROR: ", err))
return false
}
ctx.State["uid"] = strconv.FormatInt(searchRes[0].Mid, 10)
return true
}
next := zero.NewFutureEvent("message", 999, false, ctx.CheckSession())
recv, cancel := next.Repeat()
defer cancel()
ctx.SendChain(message.Text("输入为纯数字, 请选择查询uid还是用户名, 输入对应序号:\n0. 查询uid\n1. 查询用户名"))
for {
select {
case <-time.After(time.Second * 10):
ctx.SendChain(message.Text("时间太久啦!", zero.BotConfig.NickName[0], "帮你选择查询uid"))
ctx.State["uid"] = keyword
return true
case c := <-recv:
msg := c.Event.Message.ExtractPlainText()
num, err := strconv.Atoi(msg)
if err != nil {
ctx.SendChain(message.Text("请输入数字!"))
continue
}
if num < 0 || num > 1 {
ctx.SendChain(message.Text("序号非法!"))
continue
}
if num == 0 {
ctx.State["uid"] = keyword
return true
} else if num == 1 {
searchRes, err := bz.SearchUser(cfg, keyword)
if err != nil {
ctx.SendChain(message.Text("ERROR: ", err))
return false
}
ctx.State["uid"] = strconv.FormatInt(searchRes[0].Mid, 10)
return true
}
}
}
}

View File

@ -1,326 +0,0 @@
package bilibili
import (
"encoding/json"
"time"
bz "github.com/FloatTech/AnimeAPI/bilibili"
"github.com/FloatTech/floatbox/binary"
"github.com/wdvxdr1123/ZeroBot/message"
)
var (
msgType = map[int]string{
1: "转发了动态",
2: "有图营业",
4: "无图营业",
8: "投稿了视频",
16: "投稿了短视频",
64: "投稿了文章",
256: "投稿了音频",
2048: "发布了简报",
4200: "发布了直播",
4308: "发布了直播",
}
)
// dynamicCard2msg 处理DynCard
func dynamicCard2msg(dynamicCard *bz.DynamicCard) (msg []message.MessageSegment, err error) {
var (
card bz.Card
vote bz.Vote
cType int
)
msg = make([]message.MessageSegment, 0, 16)
// 初始化结构体
err = json.Unmarshal(binary.StringToBytes(dynamicCard.Card), &card)
if err != nil {
return
}
if dynamicCard.Extension.Vote != "" {
err = json.Unmarshal(binary.StringToBytes(dynamicCard.Extension.Vote), &vote)
if err != nil {
return
}
}
cType = dynamicCard.Desc.Type
// 生成消息
switch cType {
case 1:
msg = append(msg, message.Text(card.User.Uname, msgType[cType], "\n",
card.Item.Content, "\n",
"转发的内容: \n"))
var originMsg []message.MessageSegment
var co bz.Card
co, err = bz.LoadCardDetail(card.Origin)
if err != nil {
return
}
originMsg, err = card2msg(dynamicCard, &co, card.Item.OrigType)
if err != nil {
return
}
msg = append(msg, originMsg...)
case 2:
msg = append(msg, message.Text(card.User.Name, "在", time.Unix(int64(card.Item.UploadTime), 0).Format("2006-01-02 15:04:05"), msgType[cType], "\n",
card.Item.Description))
for i := 0; i < len(card.Item.Pictures); i++ {
msg = append(msg, message.Image(card.Item.Pictures[i].ImgSrc))
}
case 4:
msg = append(msg, message.Text(card.User.Uname, "在", time.Unix(int64(card.Item.Timestamp), 0).Format("2006-01-02 15:04:05"), msgType[cType], "\n",
card.Item.Content, "\n"))
if dynamicCard.Extension.Vote != "" {
msg = append(msg, message.Text("【投票】", vote.Desc, "\n",
"截止日期: ", time.Unix(int64(vote.Endtime), 0).Format("2006-01-02 15:04:05"), "\n",
"参与人数: ", bz.HumanNum(vote.JoinNum), "\n",
"投票选项( 最多选择", vote.ChoiceCnt, "项 )\n"))
for i := 0; i < len(vote.Options); i++ {
msg = append(msg, message.Text("- ", vote.Options[i].Idx, ". ", vote.Options[i].Desc, "\n"))
if vote.Options[i].ImgURL != "" {
msg = append(msg, message.Image(vote.Options[i].ImgURL))
}
}
}
case 8:
msg = append(msg, message.Text(card.Owner.Name, "在", time.Unix(int64(card.Pubdate), 0).Format("2006-01-02 15:04:05"), msgType[cType], "\n",
card.Title))
msg = append(msg, message.Image(card.Pic))
msg = append(msg, message.Text(card.Desc, "\n",
card.ShareSubtitle, "\n",
"视频链接: ", card.ShortLink, "\n"))
case 16:
msg = append(msg, message.Text(card.User.Name, "在", time.Unix(int64(card.Item.UploadTime), 0).Format("2006-01-02 15:04:05"), msgType[cType], "\n",
card.Item.Description))
msg = append(msg, message.Image(card.Item.Cover.Default))
case 64:
msg = append(msg, message.Text(card.Author.(map[string]any)["name"], "在", time.Unix(int64(card.PublishTime), 0).Format("2006-01-02 15:04:05"), msgType[cType], "\n",
card.Title, "\n",
card.Summary))
for i := 0; i < len(card.ImageUrls); i++ {
msg = append(msg, message.Image(card.ImageUrls[i]))
}
if card.ID != 0 {
msg = append(msg, message.Text("文章链接: https://www.bilibili.com/read/cv", card.ID, "\n"))
}
case 256:
msg = append(msg, message.Text(card.Upper, "在", time.Unix(int64(card.Ctime), 0).Format("2006-01-02 15:04:05"), msgType[cType], "\n",
card.Title))
msg = append(msg, message.Image(card.Cover))
msg = append(msg, message.Text(card.Intro, "\n"))
if card.ID != 0 {
msg = append(msg, message.Text("音频链接: https://www.bilibili.com/audio/au", card.ID, "\n"))
}
case 2048:
msg = append(msg, message.Text(card.User.Uname, msgType[cType], "\n",
card.Vest.Content, "\n",
card.Sketch.Title, "\n",
card.Sketch.DescText, "\n"))
msg = append(msg, message.Image(card.Sketch.CoverURL))
msg = append(msg, message.Text("分享链接: ", card.Sketch.TargetURL, "\n"))
case 4308:
if dynamicCard.Desc.UserProfile.Info.Uname != "" {
msg = append(msg, message.Text(dynamicCard.Desc.UserProfile.Info.Uname, msgType[cType], "\n"))
}
msg = append(msg, message.Image(card.LivePlayInfo.Cover))
msg = append(msg, message.Text("\n", card.LivePlayInfo.Title, "\n",
"房间号: ", card.LivePlayInfo.RoomID, "\n",
"分区: ", card.LivePlayInfo.ParentAreaName))
if card.LivePlayInfo.ParentAreaName != card.LivePlayInfo.AreaName {
msg = append(msg, message.Text("-", card.LivePlayInfo.AreaName))
}
if card.LivePlayInfo.LiveStatus == 0 {
msg = append(msg, message.Text("未开播 \n"))
} else {
msg = append(msg, message.Text("直播中 ", card.LivePlayInfo.WatchedShow, "\n"))
}
msg = append(msg, message.Text("直播链接: ", card.LivePlayInfo.Link))
default:
msg = append(msg, message.Text("动态id: ", dynamicCard.Desc.DynamicIDStr, "未知动态类型: ", cType, "\n"))
}
if dynamicCard.Desc.DynamicIDStr != "" {
msg = append(msg, message.Text("动态链接: ", bz.TURL, dynamicCard.Desc.DynamicIDStr))
}
return
}
// card2msg cType=1, 2, 4, 8, 16, 64, 256, 2048, 4200, 4308时,处理Card字符串,cType为card类型
func card2msg(dynamicCard *bz.DynamicCard, card *bz.Card, cType int) (msg []message.MessageSegment, err error) {
var (
vote bz.Vote
)
msg = make([]message.MessageSegment, 0, 16)
// 生成消息
switch cType {
case 1:
msg = append(msg, message.Text(card.User.Uname, msgType[cType], "\n",
card.Item.Content, "\n",
"转发的内容: \n"))
var originMsg []message.MessageSegment
var co bz.Card
co, err = bz.LoadCardDetail(card.Origin)
if err != nil {
return
}
originMsg, err = card2msg(dynamicCard, &co, card.Item.OrigType)
if err != nil {
return
}
msg = append(msg, originMsg...)
case 2:
msg = append(msg, message.Text(card.User.Name, "在", time.Unix(int64(card.Item.UploadTime), 0).Format("2006-01-02 15:04:05"), msgType[cType], "\n",
card.Item.Description))
for i := 0; i < len(card.Item.Pictures); i++ {
msg = append(msg, message.Image(card.Item.Pictures[i].ImgSrc))
}
case 4:
msg = append(msg, message.Text(card.User.Uname, "在", time.Unix(int64(card.Item.Timestamp), 0).Format("2006-01-02 15:04:05"), msgType[cType], "\n",
card.Item.Content, "\n"))
if dynamicCard.Extension.Vote != "" {
msg = append(msg, message.Text("【投票】", vote.Desc, "\n",
"截止日期: ", time.Unix(int64(vote.Endtime), 0).Format("2006-01-02 15:04:05"), "\n",
"参与人数: ", bz.HumanNum(vote.JoinNum), "\n",
"投票选项( 最多选择", vote.ChoiceCnt, "项 )\n"))
for i := 0; i < len(vote.Options); i++ {
msg = append(msg, message.Text("- ", vote.Options[i].Idx, ". ", vote.Options[i].Desc, "\n"))
if vote.Options[i].ImgURL != "" {
msg = append(msg, message.Image(vote.Options[i].ImgURL))
}
}
}
case 8:
msg = append(msg, message.Text(card.Owner.Name, "在", time.Unix(int64(card.Pubdate), 0).Format("2006-01-02 15:04:05"), msgType[cType], "\n",
card.Title))
msg = append(msg, message.Image(card.Pic))
msg = append(msg, message.Text(card.Desc, "\n",
card.ShareSubtitle, "\n",
"视频链接: ", card.ShortLink, "\n"))
case 16:
msg = append(msg, message.Text(card.User.Name, "在", time.Unix(int64(card.Item.UploadTime), 0).Format("2006-01-02 15:04:05"), msgType[cType], "\n",
card.Item.Description))
msg = append(msg, message.Image(card.Item.Cover.Default))
case 64:
msg = append(msg, message.Text(card.Author.(map[string]any)["name"], "在", time.Unix(int64(card.PublishTime), 0).Format("2006-01-02 15:04:05"), msgType[cType], "\n",
card.Title, "\n",
card.Summary))
for i := 0; i < len(card.ImageUrls); i++ {
msg = append(msg, message.Image(card.ImageUrls[i]))
}
if card.ID != 0 {
msg = append(msg, message.Text("文章链接: https://www.bilibili.com/read/cv", card.ID, "\n"))
}
case 256:
msg = append(msg, message.Text(card.Upper, "在", time.Unix(int64(card.Ctime), 0).Format("2006-01-02 15:04:05"), msgType[cType], "\n",
card.Title))
msg = append(msg, message.Image(card.Cover))
msg = append(msg, message.Text(card.Intro, "\n"))
if card.ID != 0 {
msg = append(msg, message.Text("音频链接: https://www.bilibili.com/audio/au", card.ID, "\n"))
}
case 2048:
msg = append(msg, message.Text(card.User.Uname, msgType[cType], "\n",
card.Vest.Content, "\n",
card.Sketch.Title, "\n",
card.Sketch.DescText, "\n"))
msg = append(msg, message.Image(card.Sketch.CoverURL))
msg = append(msg, message.Text("分享链接: ", card.Sketch.TargetURL, "\n"))
case 4308:
if dynamicCard.Desc.UserProfile.Info.Uname != "" {
msg = append(msg, message.Text(dynamicCard.Desc.UserProfile.Info.Uname, msgType[cType], "\n"))
}
msg = append(msg, message.Image(card.LivePlayInfo.Cover))
msg = append(msg, message.Text("\n", card.LivePlayInfo.Title, "\n",
"房间号: ", card.LivePlayInfo.RoomID, "\n",
"分区: ", card.LivePlayInfo.ParentAreaName))
if card.LivePlayInfo.ParentAreaName != card.LivePlayInfo.AreaName {
msg = append(msg, message.Text("-", card.LivePlayInfo.AreaName))
}
if card.LivePlayInfo.LiveStatus == 0 {
msg = append(msg, message.Text("未开播 \n"))
} else {
msg = append(msg, message.Text("直播中 ", card.LivePlayInfo.WatchedShow, "\n"))
}
msg = append(msg, message.Text("直播链接: ", card.LivePlayInfo.Link))
default:
msg = append(msg, message.Text("动态id: ", dynamicCard.Desc.DynamicIDStr, "未知动态类型: ", cType, "\n"))
}
if dynamicCard.Desc.DynamicIDStr != "" {
msg = append(msg, message.Text("动态链接: ", bz.TURL, dynamicCard.Desc.DynamicIDStr))
}
return
}
// dynamicDetail 用动态id查动态信息
func dynamicDetail(cookiecfg *bz.CookieConfig, dynamicIDStr string) (msg []message.MessageSegment, err error) {
dyc, err := bz.GetDynamicDetail(cookiecfg, dynamicIDStr)
if err != nil {
return
}
return dynamicCard2msg(&dyc)
}
// articleCard2msg 专栏转消息
func articleCard2msg(card bz.Card, defaultID string) (msg []message.MessageSegment) {
msg = make([]message.MessageSegment, 0, 16)
for i := 0; i < len(card.OriginImageUrls); i++ {
msg = append(msg, message.Image(card.OriginImageUrls[i]))
}
msg = append(msg, message.Text("\n", card.Title, "\n", "UP主: ", card.AuthorName, "\n",
"阅读: ", bz.HumanNum(card.Stats.View), " 评论: ", bz.HumanNum(card.Stats.Reply), "\n",
bz.CVURL, defaultID))
return
}
// liveCard2msg 直播卡片转消息
func liveCard2msg(card bz.RoomCard) (msg []message.MessageSegment) {
msg = make([]message.MessageSegment, 0, 16)
msg = append(msg, message.Image(card.RoomInfo.Keyframe))
msg = append(msg, message.Text("\n", card.RoomInfo.Title, "\n",
"主播: ", card.AnchorInfo.BaseInfo.Uname, "\n",
"房间号: ", card.RoomInfo.RoomID, "\n"))
if card.RoomInfo.ShortID != 0 {
msg = append(msg, message.Text("短号: ", card.RoomInfo.ShortID, "\n"))
}
msg = append(msg, message.Text("分区: ", card.RoomInfo.ParentAreaName))
if card.RoomInfo.ParentAreaName != card.RoomInfo.AreaName {
msg = append(msg, message.Text("-", card.RoomInfo.AreaName))
}
if card.RoomInfo.LiveStatus == 0 {
msg = append(msg, message.Text("未开播 \n"))
} else {
msg = append(msg, message.Text("直播中 ", bz.HumanNum(card.RoomInfo.Online), "人气\n"))
}
if card.RoomInfo.ShortID != 0 {
msg = append(msg, message.Text("直播间链接: ", bz.LURL, card.RoomInfo.ShortID))
} else {
msg = append(msg, message.Text("直播间链接: ", bz.LURL, card.RoomInfo.RoomID))
}
return
}
// videoCard2msg 视频卡片转消息
func videoCard2msg(card bz.Card) (msg []message.MessageSegment, err error) {
var mCard bz.MemberCard
msg = make([]message.MessageSegment, 0, 16)
mCard, err = bz.GetMemberCard(card.Owner.Mid)
if err != nil {
return
}
msg = append(msg, message.Text("标题: ", card.Title, "\n"))
if card.Rights.IsCooperation == 1 {
for i := 0; i < len(card.Staff); i++ {
msg = append(msg, message.Text(card.Staff[i].Title, ": ", card.Staff[i].Name, " 粉丝: ", bz.HumanNum(card.Staff[i].Follower), "\n"))
}
} else {
msg = append(msg, message.Text("UP主: ", card.Owner.Name, " 粉丝: ", bz.HumanNum(mCard.Fans), "\n"))
}
msg = append(msg, message.Text("播放: ", bz.HumanNum(card.Stat.View), " 弹幕: ", bz.HumanNum(card.Stat.Danmaku)))
msg = append(msg, message.Image(card.Pic))
msg = append(msg, message.Text("\n点赞: ", bz.HumanNum(card.Stat.Like), " 投币: ", bz.HumanNum(card.Stat.Coin), "\n",
"收藏: ", bz.HumanNum(card.Stat.Favorite), " 分享: ", bz.HumanNum(card.Stat.Share), "\n",
bz.VURL, card.BvID, "\n\n"))
return
}

View File

@ -1,55 +0,0 @@
package bilibili
import (
"testing"
bz "github.com/FloatTech/AnimeAPI/bilibili"
)
func TestArticleInfo(t *testing.T) {
card, err := bz.GetArticleInfo("17279244")
if err != nil {
t.Fatal(err)
}
t.Log(articleCard2msg(card, "17279244"))
}
func TestMemberCard(t *testing.T) {
card, err := bz.GetMemberCard(2)
if err != nil {
t.Fatal(err)
}
t.Logf("%+v\n", card)
}
func TestVideoInfo(t *testing.T) {
card, err := bz.GetVideoInfo("10007")
if err != nil {
t.Fatal(err)
}
t.Log(videoCard2msg(card))
card, err = bz.GetVideoInfo("BV1xx411c7mD")
if err != nil {
t.Fatal(err)
}
t.Log(videoCard2msg(card))
card, err = bz.GetVideoInfo("bv1xx411c7mD")
if err != nil {
t.Fatal(err)
}
t.Log(videoCard2msg(card))
card, err = bz.GetVideoInfo("BV1mF411j7iU")
if err != nil {
t.Fatal(err)
}
t.Log(videoCard2msg(card))
}
func TestLiveRoomInfo(t *testing.T) {
card, err := bz.GetLiveRoomInfo("83171")
if err != nil {
t.Fatal(err)
}
t.Log(liveCard2msg(card))
}

View File

@ -1,26 +1,35 @@
// Package bilibili bilibili卡片解析 // Package bilibiliparse bilibili卡片解析
package bilibili package bilibiliparse
import ( import (
"bytes"
"encoding/json" "encoding/json"
"fmt" "fmt"
"net/http" "net/http"
"os"
"os/exec"
"regexp" "regexp"
"strings" "strings"
"time" "time"
bz "github.com/FloatTech/AnimeAPI/bilibili" bz "github.com/FloatTech/AnimeAPI/bilibili"
"github.com/FloatTech/floatbox/file"
"github.com/FloatTech/floatbox/web" "github.com/FloatTech/floatbox/web"
ctrl "github.com/FloatTech/zbpctrl" ctrl "github.com/FloatTech/zbpctrl"
"github.com/FloatTech/zbputils/control" "github.com/FloatTech/zbputils/control"
"github.com/FloatTech/zbputils/ctxext" "github.com/FloatTech/zbputils/ctxext"
"github.com/pkg/errors"
zero "github.com/wdvxdr1123/ZeroBot" zero "github.com/wdvxdr1123/ZeroBot"
"github.com/wdvxdr1123/ZeroBot/message" "github.com/wdvxdr1123/ZeroBot/message"
) )
const ( const (
enableHex = 0x10 enableVideoSummary = int64(0x10)
unableHex = 0x7fffffff_fffffffd disableVideoSummary = ^enableVideoSummary
enableVideoDownload = int64(0x20)
disableVideoDownload = ^enableVideoDownload
bilibiliparseReferer = "https://www.bilibili.com"
ua = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/92.0.4515.107 Safari/537.36"
) )
var ( var (
@ -33,15 +42,20 @@ var (
searchDynamicRe = regexp.MustCompile(searchDynamic) searchDynamicRe = regexp.MustCompile(searchDynamic)
searchArticleRe = regexp.MustCompile(searchArticle) searchArticleRe = regexp.MustCompile(searchArticle)
searchLiveRoomRe = regexp.MustCompile(searchLiveRoom) searchLiveRoomRe = regexp.MustCompile(searchLiveRoom)
cachePath string
cfg = bz.NewCookieConfig("data/Bilibili/config.json")
) )
// 插件主体 // 插件主体
func init() { func init() {
en := control.Register("bilibiliparse", &ctrl.Options[*zero.Ctx]{ en := control.AutoRegister(&ctrl.Options[*zero.Ctx]{
DisableOnDefault: false, DisableOnDefault: false,
Brief: "b站链接解析", Brief: "b站链接解析",
Help: "例:- t.bilibili.com/642277677329285174\n- bilibili.com/read/cv17134450\n- bilibili.com/video/BV13B4y1x7pS\n- live.bilibili.com/22603245 ", Help: "例:- t.bilibili.com/642277677329285174\n- bilibili.com/read/cv17134450\n- bilibili.com/video/BV13B4y1x7pS\n- live.bilibili.com/22603245 ",
}) })
cachePath = en.DataFolder() + "cache/"
_ = os.RemoveAll(cachePath)
_ = os.MkdirAll(cachePath, 0755)
en.OnRegex(`((b23|acg).tv|bili2233.cn)\\?/[0-9a-zA-Z]+`).SetBlock(true).Limit(limit.LimitByGroup). en.OnRegex(`((b23|acg).tv|bili2233.cn)\\?/[0-9a-zA-Z]+`).SetBlock(true).Limit(limit.LimitByGroup).
Handle(func(ctx *zero.Ctx) { Handle(func(ctx *zero.Ctx) {
u := ctx.State["regex_matched"].([]string)[0] u := ctx.State["regex_matched"].([]string)[0]
@ -82,9 +96,9 @@ func init() {
data := c.GetData(ctx.Event.GroupID) data := c.GetData(ctx.Event.GroupID)
switch option { switch option {
case "开启", "打开", "启用": case "开启", "打开", "启用":
data |= enableHex data |= enableVideoSummary
case "关闭", "关掉", "禁用": case "关闭", "关掉", "禁用":
data &= unableHex data &= disableVideoSummary
default: default:
return return
} }
@ -95,6 +109,35 @@ func init() {
} }
ctx.SendChain(message.Text("已", option, "视频总结")) ctx.SendChain(message.Text("已", option, "视频总结"))
}) })
en.OnRegex(`^(开启|打开|启用|关闭|关掉|禁用)视频上传$`, zero.AdminPermission).SetBlock(true).
Handle(func(ctx *zero.Ctx) {
gid := ctx.Event.GroupID
if gid <= 0 {
// 个人用户设为负数
gid = -ctx.Event.UserID
}
option := ctx.State["regex_matched"].([]string)[1]
c, ok := ctx.State["manager"].(*ctrl.Control[*zero.Ctx])
if !ok {
ctx.SendChain(message.Text("找不到服务!"))
return
}
data := c.GetData(ctx.Event.GroupID)
switch option {
case "开启", "打开", "启用":
data |= enableVideoDownload
case "关闭", "关掉", "禁用":
data &= disableVideoDownload
default:
return
}
err := c.SetData(gid, data)
if err != nil {
ctx.SendChain(message.Text("出错啦: ", err))
return
}
ctx.SendChain(message.Text("已", option, "视频上传"))
})
en.OnRegex(searchVideo).SetBlock(true).Limit(limit.LimitByGroup).Handle(handleVideo) en.OnRegex(searchVideo).SetBlock(true).Limit(limit.LimitByGroup).Handle(handleVideo)
en.OnRegex(searchDynamic).SetBlock(true).Limit(limit.LimitByGroup).Handle(handleDynamic) en.OnRegex(searchDynamic).SetBlock(true).Limit(limit.LimitByGroup).Handle(handleDynamic)
en.OnRegex(searchArticle).SetBlock(true).Limit(limit.LimitByGroup).Handle(handleArticle) en.OnRegex(searchArticle).SetBlock(true).Limit(limit.LimitByGroup).Handle(handleArticle)
@ -111,13 +154,13 @@ func handleVideo(ctx *zero.Ctx) {
ctx.SendChain(message.Text("ERROR: ", err)) ctx.SendChain(message.Text("ERROR: ", err))
return return
} }
msg, err := videoCard2msg(card) msg, err := card.ToVideoMessage()
if err != nil { if err != nil {
ctx.SendChain(message.Text("ERROR: ", err)) ctx.SendChain(message.Text("ERROR: ", err))
return return
} }
c, ok := ctx.State["manager"].(*ctrl.Control[*zero.Ctx]) c, ok := ctx.State["manager"].(*ctrl.Control[*zero.Ctx])
if ok && c.GetData(ctx.Event.GroupID)&enableHex == enableHex { if ok && c.GetData(ctx.Event.GroupID)&enableVideoSummary == enableVideoSummary {
summaryMsg, err := getVideoSummary(cfg, card) summaryMsg, err := getVideoSummary(cfg, card)
if err != nil { if err != nil {
msg = append(msg, message.Text("ERROR: ", err)) msg = append(msg, message.Text("ERROR: ", err))
@ -126,10 +169,18 @@ func handleVideo(ctx *zero.Ctx) {
} }
} }
ctx.SendChain(msg...) ctx.SendChain(msg...)
if ok && c.GetData(ctx.Event.GroupID)&enableVideoDownload == enableVideoDownload {
downLoadMsg, err := getVideoDownload(cfg, card, cachePath)
if err != nil {
ctx.SendChain(message.Text("ERROR: ", err))
return
}
ctx.SendChain(downLoadMsg...)
}
} }
func handleDynamic(ctx *zero.Ctx) { func handleDynamic(ctx *zero.Ctx) {
msg, err := dynamicDetail(cfg, ctx.State["regex_matched"].([]string)[2]) msg, err := cfg.GetDetailMessage(ctx.State["regex_matched"].([]string)[2])
if err != nil { if err != nil {
ctx.SendChain(message.Text("ERROR: ", err)) ctx.SendChain(message.Text("ERROR: ", err))
return return
@ -143,20 +194,25 @@ func handleArticle(ctx *zero.Ctx) {
ctx.SendChain(message.Text("ERROR: ", err)) ctx.SendChain(message.Text("ERROR: ", err))
return return
} }
ctx.SendChain(articleCard2msg(card, ctx.State["regex_matched"].([]string)[1])...) ctx.SendChain(card.ToArticleMessage(ctx.State["regex_matched"].([]string)[1])...)
} }
func handleLive(ctx *zero.Ctx) { func handleLive(ctx *zero.Ctx) {
card, err := bz.GetLiveRoomInfo(ctx.State["regex_matched"].([]string)[1]) cookie, err := cfg.Load()
if err != nil { if err != nil {
ctx.SendChain(message.Text("ERROR: ", err)) ctx.SendChain(message.Text("ERROR: ", err))
return return
} }
ctx.SendChain(liveCard2msg(card)...) card, err := bz.GetLiveRoomInfo(ctx.State["regex_matched"].([]string)[1], cookie)
if err != nil {
ctx.SendChain(message.Text("ERROR: ", err))
return
}
ctx.SendChain(card.ToMessage()...)
} }
// getVideoSummary AI视频总结 // getVideoSummary AI视频总结
func getVideoSummary(cookiecfg *bz.CookieConfig, card bz.Card) (msg []message.MessageSegment, err error) { func getVideoSummary(cookiecfg *bz.CookieConfig, card bz.Card) (msg []message.Segment, err error) {
var ( var (
data []byte data []byte
videoSummary bz.VideoSummary videoSummary bz.VideoSummary
@ -177,7 +233,7 @@ func getVideoSummary(cookiecfg *bz.CookieConfig, card bz.Card) (msg []message.Me
return return
} }
err = json.Unmarshal(data, &videoSummary) err = json.Unmarshal(data, &videoSummary)
msg = make([]message.MessageSegment, 0, 16) msg = make([]message.Segment, 0, 16)
msg = append(msg, message.Text("已为你生成视频总结\n\n")) msg = append(msg, message.Text("已为你生成视频总结\n\n"))
msg = append(msg, message.Text(videoSummary.Data.ModelResult.Summary, "\n\n")) msg = append(msg, message.Text(videoSummary.Data.ModelResult.Summary, "\n\n"))
for _, v := range videoSummary.Data.ModelResult.Outline { for _, v := range videoSummary.Data.ModelResult.Outline {
@ -189,3 +245,47 @@ func getVideoSummary(cookiecfg *bz.CookieConfig, card bz.Card) (msg []message.Me
} }
return return
} }
func getVideoDownload(cookiecfg *bz.CookieConfig, card bz.Card, cachePath string) (msg []message.Segment, err error) {
var (
data []byte
videoDownload bz.VideoDownload
stderr bytes.Buffer
)
today := time.Now().Format("20060102")
videoFile := fmt.Sprintf("%s%s%s.mp4", cachePath, card.BvID, today)
if file.IsExist(videoFile) {
msg = append(msg, message.Video("file:///"+file.BOTPATH+"/"+videoFile))
return
}
data, err = web.RequestDataWithHeaders(web.NewDefaultClient(), bz.SignURL(fmt.Sprintf(bz.VideoDownloadURL, card.BvID, card.CID)), "GET", func(req *http.Request) error {
if cookiecfg != nil {
cookie := ""
cookie, err = cookiecfg.Load()
if err != nil {
return err
}
req.Header.Add("cookie", cookie)
}
req.Header.Set("User-Agent", ua)
return nil
}, nil)
if err != nil {
return
}
err = json.Unmarshal(data, &videoDownload)
if err != nil {
return
}
headers := fmt.Sprintf("User-Agent: %s\nReferer: %s", ua, bilibiliparseReferer)
// 限制最多下载8分钟视频
cmd := exec.Command("ffmpeg", "-ss", "0", "-t", "480", "-headers", headers, "-i", videoDownload.Data.Durl[0].URL, "-c", "copy", videoFile)
cmd.Stderr = &stderr
err = cmd.Run()
if err != nil {
err = errors.Errorf("未配置ffmpeg%v", stderr)
return
}
msg = append(msg, message.Video("file:///"+file.BOTPATH+"/"+videoFile))
return
}

View File

@ -1,5 +1,5 @@
// Package bilibili b站推送 // Package bilibilipush b站推送
package bilibili package bilibilipush
import ( import (
"bytes" "bytes"
@ -34,10 +34,11 @@ var (
lastTime = map[int64]int64{} lastTime = map[int64]int64{}
liveStatus = map[int64]int{} liveStatus = map[int64]int{}
upMap = map[int64]string{} upMap = map[int64]string{}
cfg = bz.NewCookieConfig("data/Bilibili/config.json")
) )
func init() { func init() {
en := control.Register("bilibilipush", &ctrl.Options[*zero.Ctx]{ en := control.AutoRegister(&ctrl.Options[*zero.Ctx]{
DisableOnDefault: false, DisableOnDefault: false,
Brief: "b站推送", Brief: "b站推送",
Help: "- 添加b站订阅[uid|name]\n" + Help: "- 添加b站订阅[uid|name]\n" +
@ -75,7 +76,7 @@ func init() {
ctx.SendChain(message.Text("已关闭艾特全体Oo")) ctx.SendChain(message.Text("已关闭艾特全体Oo"))
}) })
en.OnRegex(`^添加[B|b]站订阅\s?(.{1,25})$`, zero.UserOrGrpAdmin, getPara).SetBlock(true).Handle(func(ctx *zero.Ctx) { en.OnRegex(`^添加[B|b]站订阅\s?(.{1,25})$`, zero.UserOrGrpAdmin, bz.RequireUser(cfg)).SetBlock(true).Handle(func(ctx *zero.Ctx) {
buid, _ := strconv.ParseInt(ctx.State["uid"].(string), 10, 64) buid, _ := strconv.ParseInt(ctx.State["uid"].(string), 10, 64)
name, err := getName(buid, cfg) name, err := getName(buid, cfg)
if err != nil || name == "" { if err != nil || name == "" {
@ -93,7 +94,7 @@ func init() {
ctx.SendChain(message.Text("已添加" + name + "的订阅")) ctx.SendChain(message.Text("已添加" + name + "的订阅"))
}) })
en.OnRegex(`^取消[B|b]站订阅\s?(.{1,25})$`, zero.UserOrGrpAdmin, getPara).SetBlock(true).Handle(func(ctx *zero.Ctx) { en.OnRegex(`^取消[B|b]站订阅\s?(.{1,25})$`, zero.UserOrGrpAdmin, bz.RequireUser(cfg)).SetBlock(true).Handle(func(ctx *zero.Ctx) {
buid, _ := strconv.ParseInt(ctx.State["uid"].(string), 10, 64) buid, _ := strconv.ParseInt(ctx.State["uid"].(string), 10, 64)
name, err := getName(buid, cfg) name, err := getName(buid, cfg)
if err != nil { if err != nil {
@ -110,7 +111,7 @@ func init() {
} }
ctx.SendChain(message.Text("已取消" + name + "的订阅")) ctx.SendChain(message.Text("已取消" + name + "的订阅"))
}) })
en.OnRegex(`^取消[B|b]站动态订阅\s?(.{1,25})$`, zero.UserOrGrpAdmin, getPara).SetBlock(true).Handle(func(ctx *zero.Ctx) { en.OnRegex(`^取消[B|b]站动态订阅\s?(.{1,25})$`, zero.UserOrGrpAdmin, bz.RequireUser(cfg)).SetBlock(true).Handle(func(ctx *zero.Ctx) {
buid, _ := strconv.ParseInt(ctx.State["uid"].(string), 10, 64) buid, _ := strconv.ParseInt(ctx.State["uid"].(string), 10, 64)
name, err := getName(buid, cfg) name, err := getName(buid, cfg)
if err != nil { if err != nil {
@ -127,7 +128,7 @@ func init() {
} }
ctx.SendChain(message.Text("已取消" + name + "的动态订阅")) ctx.SendChain(message.Text("已取消" + name + "的动态订阅"))
}) })
en.OnRegex(`^取消[B|b]站直播订阅\s?(.{1,25})$`, zero.UserOrGrpAdmin, getPara).SetBlock(true).Handle(func(ctx *zero.Ctx) { en.OnRegex(`^取消[B|b]站直播订阅\s?(.{1,25})$`, zero.UserOrGrpAdmin, bz.RequireUser(cfg)).SetBlock(true).Handle(func(ctx *zero.Ctx) {
buid, _ := strconv.ParseInt(ctx.State["uid"].(string), 10, 64) buid, _ := strconv.ParseInt(ctx.State["uid"].(string), 10, 64)
gid := ctx.Event.GroupID gid := ctx.Event.GroupID
if gid == 0 { if gid == 0 {
@ -333,7 +334,7 @@ func sendDynamic(ctx *zero.Ctx) error {
err = errors.Errorf("动态%v的解析有问题,%v", cardList[i].Get("desc.dynamic_id_str"), err) err = errors.Errorf("动态%v的解析有问题,%v", cardList[i].Get("desc.dynamic_id_str"), err)
return err return err
} }
msg, err := dynamicCard2msg(&dc) msg, err := dc.ToMessage()
if err != nil { if err != nil {
err = errors.Errorf("动态%v的解析有问题,%v", cardList[i].Get("desc.dynamic_id_str"), err) err = errors.Errorf("动态%v的解析有问题,%v", cardList[i].Get("desc.dynamic_id_str"), err)
return err return err
@ -388,7 +389,7 @@ func sendLive(ctx *zero.Ctx) error {
if lCover == "" { if lCover == "" {
lCover = value.Get("keyframe").String() lCover = value.Get("keyframe").String()
} }
var msg []message.MessageSegment var msg []message.Segment
msg = append(msg, message.Text(lName+" 正在直播:\n")) msg = append(msg, message.Text(lName+" 正在直播:\n"))
msg = append(msg, message.Text(lTitle)) msg = append(msg, message.Text(lTitle))
msg = append(msg, message.Image(lCover)) msg = append(msg, message.Image(lCover))
@ -399,7 +400,7 @@ func sendLive(ctx *zero.Ctx) error {
switch { switch {
case gid > 0: case gid > 0:
if res := bdb.getAtAll(gid); res == 1 { if res := bdb.getAtAll(gid); res == 1 {
msg = append([]message.MessageSegment{message.AtAll()}, msg...) msg = append([]message.Segment{message.AtAll()}, msg...)
} }
ctx.SendGroupMessage(gid, msg) ctx.SendGroupMessage(gid, msg)
case gid < 0: case gid < 0:

View File

@ -1,4 +1,4 @@
package bilibili package bilibilipush
import ( import (
"encoding/json" "encoding/json"

View File

@ -10,6 +10,7 @@ import (
"github.com/FloatTech/floatbox/binary" "github.com/FloatTech/floatbox/binary"
fcext "github.com/FloatTech/floatbox/ctxext" fcext "github.com/FloatTech/floatbox/ctxext"
sql "github.com/FloatTech/sqlite"
ctrl "github.com/FloatTech/zbpctrl" ctrl "github.com/FloatTech/zbpctrl"
"github.com/FloatTech/zbputils/control" "github.com/FloatTech/zbputils/control"
"github.com/FloatTech/zbputils/img/text" "github.com/FloatTech/zbputils/img/text"
@ -24,7 +25,7 @@ func init() {
}) })
getdb := fcext.DoOnceOnSuccess(func(ctx *zero.Ctx) bool { getdb := fcext.DoOnceOnSuccess(func(ctx *zero.Ctx) bool {
db.DBPath = engine.DataFolder() + "bookreview.db" db = sql.New(engine.DataFolder() + "bookreview.db")
// os.RemoveAll(dbpath) // os.RemoveAll(dbpath)
_, _ = engine.GetLazyData("bookreview.db", true) _, _ = engine.GetLazyData("bookreview.db", true)
err := db.Open(time.Hour) err := db.Open(time.Hour)

View File

@ -7,11 +7,11 @@ type book struct {
BookReview string `db:"bookreview"` BookReview string `db:"bookreview"`
} }
var db = &sql.Sqlite{} var db sql.Sqlite
// 暂时随机选择一个书评 // 暂时随机选择一个书评
func getBookReviewByKeyword(keyword string) (b book) { func getBookReviewByKeyword(keyword string) (b book) {
_ = db.Find("bookreview", &b, "where bookreview LIKE '%"+keyword+"%'") _ = db.Find("bookreview", &b, "WHERE bookreview LIKE ?", "%"+keyword+"%")
return return
} }

View File

@ -3,15 +3,21 @@ package chatcount
import ( import (
"fmt" "fmt"
"image"
"net/http"
"strconv" "strconv"
"strings" "sync"
zero "github.com/wdvxdr1123/ZeroBot" zero "github.com/wdvxdr1123/ZeroBot"
"github.com/wdvxdr1123/ZeroBot/message" "github.com/wdvxdr1123/ZeroBot/message"
"github.com/FloatTech/floatbox/file"
"github.com/FloatTech/imgfactory"
"github.com/FloatTech/rendercard"
ctrl "github.com/FloatTech/zbpctrl" ctrl "github.com/FloatTech/zbpctrl"
"github.com/FloatTech/zbputils/control" "github.com/FloatTech/zbputils/control"
"github.com/FloatTech/zbputils/ctxext" "github.com/FloatTech/zbputils/ctxext"
"github.com/FloatTech/zbputils/img/text"
) )
const ( const (
@ -37,28 +43,66 @@ func init() {
}) })
engine.OnPrefix(`查询水群`, zero.OnlyGroup).SetBlock(true).Handle(func(ctx *zero.Ctx) { engine.OnPrefix(`查询水群`, zero.OnlyGroup).SetBlock(true).Handle(func(ctx *zero.Ctx) {
param := ctx.State["args"].(string)
var uid int64
if len(ctx.Event.Message) > 1 && ctx.Event.Message[1].Type == "at" {
uid, _ = strconv.ParseInt(ctx.Event.Message[1].Data["qq"], 10, 64)
} else if param == "" {
uid = ctx.Event.UserID
}
name := ctx.NickName() name := ctx.NickName()
todayTime, todayMessage, totalTime, totalMessage := ctdb.getChatTime(ctx.Event.GroupID, ctx.Event.UserID) todayTime, todayMessage, totalTime, totalMessage := ctdb.getChatTime(ctx.Event.GroupID, uid)
ctx.SendChain(message.Reply(ctx.Event.MessageID), message.Text(fmt.Sprintf("%s今天水了%d分%d秒发了%d条消息总计水了%d分%d秒发了%d条消息。", name, todayTime/60, todayTime%60, todayMessage, totalTime/60, totalTime%60, totalMessage))) ctx.SendChain(message.Reply(ctx.Event.MessageID), message.Text(fmt.Sprintf("%s今天水了%d分%d秒发了%d条消息总计水了%d分%d秒发了%d条消息。", name, todayTime/60, todayTime%60, todayMessage, totalTime/60, totalTime%60, totalMessage)))
}) })
engine.OnFullMatch("查看水群排名", zero.OnlyGroup).Limit(ctxext.LimitByGroup).SetBlock(true). engine.OnFullMatch("查看水群排名", zero.OnlyGroup).Limit(ctxext.LimitByGroup).SetBlock(true).
Handle(func(ctx *zero.Ctx) { Handle(func(ctx *zero.Ctx) {
text := strings.Builder{}
text.WriteString("今日水群排行榜:\n")
chatTimeList := ctdb.getChatRank(ctx.Event.GroupID) chatTimeList := ctdb.getChatRank(ctx.Event.GroupID)
for i := 0; i < len(chatTimeList) && i < rankSize; i++ { if len(chatTimeList) == 0 {
text.WriteString("第") ctx.SendChain(message.Text("ERROR: 没有水群数据"))
text.WriteString(strconv.Itoa(i + 1)) return
text.WriteString("名:") }
text.WriteString(ctx.CardOrNickName(chatTimeList[i].UserID)) rankinfo := make([]*rendercard.RankInfo, len(chatTimeList))
text.WriteString(" - ")
text.WriteString(strconv.FormatInt(chatTimeList[i].TodayMessage, 10)) wg := &sync.WaitGroup{}
text.WriteString("条,共") wg.Add(len(chatTimeList))
text.WriteString(strconv.FormatInt(chatTimeList[i].TodayTime/60, 10)) for i := 0; i < len(chatTimeList) && i < rankSize; i++ {
text.WriteString("分") go func(i int) {
text.WriteString(strconv.FormatInt(chatTimeList[i].TodayTime%60, 10)) defer wg.Done()
text.WriteString("秒\n") resp, err := http.Get("https://q4.qlogo.cn/g?b=qq&nk=" + strconv.FormatInt(chatTimeList[i].UserID, 10) + "&s=100")
if err != nil {
return
}
defer resp.Body.Close()
img, _, err := image.Decode(resp.Body)
if err != nil {
return
}
rankinfo[i] = &rendercard.RankInfo{
TopLeftText: ctx.CardOrNickName(chatTimeList[i].UserID),
BottomLeftText: "消息数: " + strconv.FormatInt(chatTimeList[i].TodayMessage, 10) + " 条",
RightText: strconv.FormatInt(chatTimeList[i].TodayTime/60, 10) + "分" + strconv.FormatInt(chatTimeList[i].TodayTime%60, 10) + "秒",
Avatar: img,
}
}(i)
}
wg.Wait()
fontbyte, err := file.GetLazyData(text.GlowSansFontFile, control.Md5File, true)
if err != nil {
ctx.SendChain(message.Text("ERROR: ", err))
return
}
img, err := rendercard.DrawRankingCard(fontbyte, "今日水群排行榜", rankinfo)
if err != nil {
ctx.SendChain(message.Text("ERROR: ", err))
return
}
sendimg, err := imgfactory.ToBytes(img)
if err != nil {
ctx.SendChain(message.Text("ERROR: ", err))
return
}
if id := ctx.SendChain(message.ImageBytes(sendimg)); id.ID() == 0 {
ctx.SendChain(message.Text("ERROR: 可能被风控了"))
} }
ctx.SendChain(message.Text(text.String()))
}) })
} }

View File

@ -13,7 +13,6 @@ import (
"github.com/FloatTech/zbputils/control" "github.com/FloatTech/zbputils/control"
"github.com/FloatTech/zbputils/ctxext" "github.com/FloatTech/zbputils/ctxext"
zero "github.com/wdvxdr1123/ZeroBot" zero "github.com/wdvxdr1123/ZeroBot"
"github.com/wdvxdr1123/ZeroBot/extension/single"
"github.com/wdvxdr1123/ZeroBot/message" "github.com/wdvxdr1123/ZeroBot/message"
) )
@ -35,16 +34,7 @@ var (
Brief: "国际象棋", Brief: "国际象棋",
Help: helpString, Help: helpString,
PrivateDataFolder: "chess", PrivateDataFolder: "chess",
}).ApplySingle(single.New( }).ApplySingle(ctxext.GroupSingle)
single.WithKeyFn(func(ctx *zero.Ctx) int64 { return ctx.Event.GroupID }),
single.WithPostFn[int64](func(ctx *zero.Ctx) {
ctx.Send(
message.ReplyWithMessage(ctx.Event.MessageID,
message.Text("有操作正在执行, 请稍后再试..."),
),
)
}),
))
) )
func init() { func init() {

View File

@ -235,7 +235,7 @@ func play(groupCode, senderUin int64, moveStr string) (msg message.Message, err
chessRoomMap.Store(groupCode, room) chessRoomMap.Store(groupCode, room)
} }
// 生成棋盘图片 // 生成棋盘图片
var boardImgEle message.MessageSegment var boardImgEle message.Segment
if !room.isBlindfold { if !room.isBlindfold {
boardImgEle, err = getBoardElement(groupCode) boardImgEle, err = getBoardElement(groupCode)
if err != nil { if err != nil {
@ -400,7 +400,7 @@ func createGame(isBlindfold bool, groupCode, senderUin int64, senderName string)
room.blackPlayer = senderUin room.blackPlayer = senderUin
room.blackName = senderName room.blackName = senderName
chessRoomMap.Store(groupCode, room) chessRoomMap.Store(groupCode, room)
var boardImgEle message.MessageSegment var boardImgEle message.Segment
if !room.isBlindfold { if !room.isBlindfold {
boardImgEle, err = getBoardElement(groupCode) boardImgEle, err = getBoardElement(groupCode)
if err != nil { if err != nil {
@ -442,7 +442,7 @@ func abortGame(room chessRoom, groupCode int64, hint string) (message.Message, e
} }
// getBoardElement 获取棋盘图片的消息内容 // getBoardElement 获取棋盘图片的消息内容
func getBoardElement(groupCode int64) (imgMsg message.MessageSegment, err error) { func getBoardElement(groupCode int64) (imgMsg message.Segment, err error) {
fontdata, err := file.GetLazyData(text.GNUUnifontFontFile, control.Md5File, true) fontdata, err := file.GetLazyData(text.GNUUnifontFontFile, control.Md5File, true)
if err != nil { if err != nil {
return return

View File

@ -9,6 +9,7 @@ import (
"github.com/wdvxdr1123/ZeroBot/message" "github.com/wdvxdr1123/ZeroBot/message"
fcext "github.com/FloatTech/floatbox/ctxext" fcext "github.com/FloatTech/floatbox/ctxext"
sql "github.com/FloatTech/sqlite"
ctrl "github.com/FloatTech/zbpctrl" ctrl "github.com/FloatTech/zbpctrl"
"github.com/FloatTech/zbputils/control" "github.com/FloatTech/zbputils/control"
) )
@ -23,7 +24,7 @@ func init() {
en.OnRegex("^抽象翻译((\\s|[\\r\\n]|[\\p{Han}\\p{P}A-Za-z0-9])+)$", en.OnRegex("^抽象翻译((\\s|[\\r\\n]|[\\p{Han}\\p{P}A-Za-z0-9])+)$",
fcext.DoOnceOnSuccess(func(ctx *zero.Ctx) bool { fcext.DoOnceOnSuccess(func(ctx *zero.Ctx) bool {
db.DBPath = en.DataFolder() + "cxh.db" db = sql.New(en.DataFolder() + "cxh.db")
// os.RemoveAll(dbpath) // os.RemoveAll(dbpath)
_, _ = en.GetLazyData("cxh.db", true) _, _ = en.GetLazyData("cxh.db", true)
err := db.Open(time.Hour) err := db.Open(time.Hour)

View File

@ -11,11 +11,11 @@ type emoji struct {
Emoji string `db:"emoji"` Emoji string `db:"emoji"`
} }
var db = &sql.Sqlite{} var db sql.Sqlite
func getPinyinByWord(word string) string { func getPinyinByWord(word string) string {
var p pinyin var p pinyin
_ = db.Find("pinyin", &p, "where word = '"+word+"'") _ = db.Find("pinyin", &p, "WHERE word = ?", word)
return p.Pronun return p.Pronun
} }
@ -25,6 +25,6 @@ func getPronunByDWord(w0, w1 rune) string {
func getEmojiByPronun(pronun string) string { func getEmojiByPronun(pronun string) string {
var e emoji var e emoji
_ = db.Find("emoji", &e, "where pronunciation = '"+pronun+"'") _ = db.Find("emoji", &e, "WHERE pronunciation = ?", pronun)
return e.Emoji return e.Emoji
} }

View File

@ -11,6 +11,7 @@ import (
fcext "github.com/FloatTech/floatbox/ctxext" fcext "github.com/FloatTech/floatbox/ctxext"
"github.com/FloatTech/floatbox/math" "github.com/FloatTech/floatbox/math"
sql "github.com/FloatTech/sqlite"
ctrl "github.com/FloatTech/zbpctrl" ctrl "github.com/FloatTech/zbpctrl"
"github.com/FloatTech/zbputils/control" "github.com/FloatTech/zbputils/control"
) )
@ -24,7 +25,7 @@ func init() {
}) })
getdb := fcext.DoOnceOnSuccess(func(ctx *zero.Ctx) bool { getdb := fcext.DoOnceOnSuccess(func(ctx *zero.Ctx) bool {
db.DBPath = engine.DataFolder() + "cp.db" db = sql.New(engine.DataFolder() + "cp.db")
// os.RemoveAll(dbpath) // os.RemoveAll(dbpath)
_, _ = engine.GetLazyData("cp.db", true) _, _ = engine.GetLazyData("cp.db", true)
err := db.Open(time.Hour) err := db.Open(time.Hour)

View File

@ -9,7 +9,7 @@ type cpstory struct {
Story string `db:"story"` Story string `db:"story"`
} }
var db = &sql.Sqlite{} var db sql.Sqlite
func getRandomCpStory() (cs cpstory) { func getRandomCpStory() (cs cpstory) {
_ = db.Pick("cp_story", &cs) _ = db.Pick("cp_story", &cs)

95
plugin/crypter/fumo.go Normal file
View File

@ -0,0 +1,95 @@
// Package crypter Fumo语
package crypter
import (
"encoding/base64"
"fmt"
"regexp"
"strings"
)
// Base64字符表
const base64Chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"
// Fumo语字符表 - 使用各种fumo变体来表示base64字符
var fumoChars = []string{
"fumo-", "Fumo-", "fUmo-", "fuMo-", "fumO-", "FUmo-", "FuMo-", "FumO-",
"fUMo-", "fUmO-", "fuMO-", "FUMo-", "FUmO-", "fUMO-", "FUMO-", "fumo.",
"Fumo.", "fUmo.", "fuMo.", "fumO.", "FUmo.", "FuMo.", "FumO.", "fUMo.",
"fUmO.", "fuMO.", "FUMo.", "FUmO.", "fUMO.", "FUMO.", "fumo,", "Fumo,",
"fUmo,", "fuMo,", "fumO,", "FUmo,", "FuMo,", "FumO,", "fUMo,", "fUmO,",
"fuMO,", "FUMo,", "FuMO,", "fUMO,", "FUMO,", "fumo+", "Fumo+", "fUmo+",
"fuMo+", "fumO+", "FUmo+", "FuMo+", "FumO+", "fUMo+", "fUmO+", "fuMO+",
"FUMo+", "FUmO+", "fUMO+", "FUMO+", "fumo|", "Fumo|", "fUmo|", "fuMo|",
"fumO|", "FUmo|", "FuMo|", "FumO|", "fUMo|", "fUmO|", "fuMO|", "fumo/",
"Fumo/", "fUmo/",
}
// Base64 2 Fumo
// 创建编码映射表
var encodeMap = make(map[byte]string)
// 创建解码映射表
var decodeMap = make(map[string]byte)
func init() {
for i := 0; i < 64 && i < len(fumoChars); i++ {
base64Char := base64Chars[i]
fumoChar := fumoChars[i]
encodeMap[base64Char] = fumoChar
decodeMap[fumoChar] = base64Char
}
}
// 加密
func encryptFumo(text string) string {
if text == "" {
return "请输入要加密的文本"
}
textBytes := []byte(text)
base64String := base64.StdEncoding.EncodeToString(textBytes)
base64Body := strings.TrimRight(base64String, "=")
paddingCount := len(base64String) - len(base64Body)
var fumoBody strings.Builder
for _, char := range base64Body {
if fumoChar, exists := encodeMap[byte(char)]; exists {
fumoBody.WriteString(fumoChar)
} else {
return fmt.Sprintf("Fumo加密失败: 未知字符 %c", char)
}
}
result := fumoBody.String() + strings.Repeat("=", paddingCount)
return result
}
// 解密
func decryptFumo(fumoText string) string {
if fumoText == "" {
return "请输入要解密的Fumo语密文"
}
fumoBody := strings.TrimRight(fumoText, "=")
paddingCount := len(fumoText) - len(fumoBody)
fumoPattern := regexp.MustCompile(`(\w+[-.,+|/])`)
fumoWords := fumoPattern.FindAllString(fumoBody, -1)
reconstructed := strings.Join(fumoWords, "")
if reconstructed != fumoBody {
return "Fumo解密失败: 包含无效的Fumo字符或格式错误"
}
var base64Body strings.Builder
for _, fumoWord := range fumoWords {
if base64Char, exists := decodeMap[fumoWord]; exists {
base64Body.WriteByte(base64Char)
} else {
return fmt.Sprintf("Fumo解密失败: 包含无效的Fumo字符 %s", fumoWord)
}
}
base64String := base64Body.String() + strings.Repeat("=", paddingCount)
decodedBytes, err := base64.StdEncoding.DecodeString(base64String)
if err != nil {
return fmt.Sprintf("Fumo解密失败: Base64解码错误 %v", err)
}
originalText := string(decodedBytes)
return originalText
}

View File

@ -0,0 +1,42 @@
// Package crypter 处理函数
package crypter
import (
"github.com/FloatTech/AnimeAPI/airecord"
"github.com/sirupsen/logrus"
zero "github.com/wdvxdr1123/ZeroBot"
"github.com/wdvxdr1123/ZeroBot/message"
)
// hou
func houEncryptHandler(ctx *zero.Ctx) {
text := ctx.State["regex_matched"].([]string)[1]
result := encodeHou(text)
logrus.Infoln("[crypter] 回复内容:", result)
recCfg := airecord.GetConfig()
record := ctx.GetAIRecord(recCfg.ModelID, recCfg.Customgid, result)
if record != "" {
ctx.SendChain(message.Record(record))
} else {
ctx.SendChain(message.Text(result))
}
}
func houDecryptHandler(ctx *zero.Ctx) {
text := ctx.State["regex_matched"].([]string)[1]
result := decodeHou(text)
ctx.SendChain(message.Text(result))
}
// fumo
func fumoEncryptHandler(ctx *zero.Ctx) {
text := ctx.State["regex_matched"].([]string)[1]
result := encryptFumo(text)
ctx.SendChain(message.Text(result))
}
func fumoDecryptHandler(ctx *zero.Ctx) {
text := ctx.State["regex_matched"].([]string)[1]
result := decryptFumo(text)
ctx.SendChain(message.Text(result))
}

88
plugin/crypter/hou.go Normal file
View File

@ -0,0 +1,88 @@
// Package crypter 齁语加解密
package crypter
import (
"strings"
)
// 齁语密码表
var houCodebook = []string{
"齁", "哦", "噢", "喔", "咕", "咿", "嗯", "啊",
"", "哈", "", "唔", "哼", "❤", "呃", "呼",
}
// 索引: 0 1 2 3 4 5 6 7
// 8 9 10 11 12 13 14 15
// 创建映射表
var houCodebookMap = make(map[string]int)
// 初始化映射表
func init() {
for idx, ch := range houCodebook {
houCodebookMap[ch] = idx
}
}
func encodeHou(text string) string {
if text == "" {
return "请输入要加密的文本"
}
var encoded strings.Builder
textBytes := []byte(text)
for _, b := range textBytes {
high := (b >> 4) & 0x0F
low := b & 0x0F
encoded.WriteString(houCodebook[high])
encoded.WriteString(houCodebook[low])
}
return encoded.String()
}
func decodeHou(code string) string {
if code == "" {
return "请输入要解密的齁语密文"
}
// 过滤出有效的齁语字符
var validChars []string
for _, r := range code {
charStr := string(r)
if _, exists := houCodebookMap[charStr]; exists {
validChars = append(validChars, charStr)
}
}
if len(validChars)%2 != 0 {
return "齁语密文长度错误,无法解密"
}
// 解密过程
var byteList []byte
for i := 0; i < len(validChars); i += 2 {
highIdx, highExists := houCodebookMap[validChars[i]]
lowIdx, lowExists := houCodebookMap[validChars[i+1]]
if !highExists || !lowExists {
return "齁语密文包含无效字符"
}
originalByte := byte((highIdx << 4) | lowIdx)
byteList = append(byteList, originalByte)
}
result := string(byteList)
if !isValidUTF8(result) {
return "齁语解密失败,结果不是有效的文本"
}
return result
}
// 检查字符串是否为有效的UTF-8编码
func isValidUTF8(s string) bool {
// Go的string类型默认就是UTF-8如果转换没有出错说明是有效的
return len(s) > 0 || s == ""
}

31
plugin/crypter/main.go Normal file
View File

@ -0,0 +1,31 @@
// Package crypter 奇怪语言加解密
package crypter
import (
ctrl "github.com/FloatTech/zbpctrl"
"github.com/FloatTech/zbputils/control"
zero "github.com/wdvxdr1123/ZeroBot"
)
func init() {
engine := control.AutoRegister(&ctrl.Options[*zero.Ctx]{
DisableOnDefault: false,
Brief: "奇怪语言加解密",
Help: "多种语言加解密插件\n" +
"- 齁语加解密:\n" +
"- 齁语加密 [文本] 或 h加密 [文本]\n" +
"- 齁语解密 [密文] 或 h解密 [密文]\n\n" +
"- Fumo语加解密:\n" +
"- fumo加密 [文本]\n" +
"- fumo解密 [密文]\n\n",
PublicDataFolder: "Crypter",
})
// hou
engine.OnRegex(`^(?:齁语加密|h加密)\s*(.+)$`).SetBlock(true).Handle(houEncryptHandler)
engine.OnRegex(`^(?:齁语解密|h解密)\s*(.+)$`).SetBlock(true).Handle(houDecryptHandler)
// Fumo
engine.OnRegex(`^fumo加密\s*(.+)$`).SetBlock(true).Handle(fumoEncryptHandler)
engine.OnRegex(`^fumo解密\s*(.+)$`).SetBlock(true).Handle(fumoDecryptHandler)
}

View File

@ -10,6 +10,7 @@ import (
fcext "github.com/FloatTech/floatbox/ctxext" fcext "github.com/FloatTech/floatbox/ctxext"
"github.com/FloatTech/floatbox/process" "github.com/FloatTech/floatbox/process"
sql "github.com/FloatTech/sqlite"
ctrl "github.com/FloatTech/zbpctrl" ctrl "github.com/FloatTech/zbpctrl"
"github.com/FloatTech/zbputils/control" "github.com/FloatTech/zbputils/control"
"github.com/FloatTech/zbputils/ctxext" "github.com/FloatTech/zbputils/ctxext"
@ -29,7 +30,7 @@ func init() {
}) })
getdb := fcext.DoOnceOnSuccess(func(ctx *zero.Ctx) bool { getdb := fcext.DoOnceOnSuccess(func(ctx *zero.Ctx) bool {
db.DBPath = engine.DataFolder() + "curse.db" db = sql.New(engine.DataFolder() + "curse.db")
_, err := engine.GetLazyData("curse.db", true) _, err := engine.GetLazyData("curse.db", true)
if err != nil { if err != nil {
ctx.SendChain(message.Text("ERROR: ", err)) ctx.SendChain(message.Text("ERROR: ", err))

View File

@ -8,9 +8,9 @@ type curse struct {
Level string `db:"level"` Level string `db:"level"`
} }
var db = &sql.Sqlite{} var db sql.Sqlite
func getRandomCurseByLevel(level string) (c curse) { func getRandomCurseByLevel(level string) (c curse) {
_ = db.Find("curse", &c, "where level = '"+level+"' ORDER BY RANDOM() limit 1") _ = db.Find("curse", &c, "WHERE level = ? ORDER BY RANDOM() limit 1", level)
return return
} }

View File

@ -2,16 +2,14 @@
package dailynews package dailynews
import ( import (
"github.com/FloatTech/floatbox/binary"
"github.com/FloatTech/floatbox/web" "github.com/FloatTech/floatbox/web"
ctrl "github.com/FloatTech/zbpctrl" ctrl "github.com/FloatTech/zbpctrl"
"github.com/FloatTech/zbputils/control" "github.com/FloatTech/zbputils/control"
"github.com/tidwall/gjson"
zero "github.com/wdvxdr1123/ZeroBot" zero "github.com/wdvxdr1123/ZeroBot"
"github.com/wdvxdr1123/ZeroBot/message" "github.com/wdvxdr1123/ZeroBot/message"
) )
const api = "http://dwz.2xb.cn/zaob" const api = "https://uapis.cn/api/v1/daily/news-image"
func init() { func init() {
engine := control.AutoRegister(&ctrl.Options[*zero.Ctx]{ engine := control.AutoRegister(&ctrl.Options[*zero.Ctx]{
@ -28,7 +26,6 @@ func init() {
ctx.SendChain(message.Text("ERROR: ", err)) ctx.SendChain(message.Text("ERROR: ", err))
return return
} }
picURL := gjson.Get(binary.BytesToString(data), "imageUrl").String() ctx.SendChain(message.ImageBytes(data))
ctx.SendChain(message.Image(picURL))
}) })
} }

View File

@ -23,7 +23,7 @@ type text struct {
// LoadText 加载小作文 // LoadText 加载小作文
func LoadText(dbfile string) error { func LoadText(dbfile string) error {
_, err := file.GetLazyData(dbfile, control.Md5File, false) _, err := file.GetLazyData(dbfile, control.Md5File, false)
db.DBPath = dbfile db = sql.New(dbfile)
if err != nil { if err != nil {
return err return err
} }
@ -63,7 +63,7 @@ func RandText() string {
// HentaiText 发大病 // HentaiText 发大病
func HentaiText() string { func HentaiText() string {
var t text var t text
err := db.Find("text", &t, "where id = -3802576048116006195") err := db.Find("text", &t, "WHERE id = -3802576048116006195")
if err != nil { if err != nil {
return err.Error() return err.Error()
} }

View File

@ -2,7 +2,6 @@
package dish package dish
import ( import (
"fmt"
"strings" "strings"
"time" "time"
@ -25,7 +24,7 @@ type dish struct {
} }
var ( var (
db = &sql.Sqlite{} db sql.Sqlite
initialized = false initialized = false
) )
@ -37,7 +36,7 @@ func init() {
PublicDataFolder: "Dish", PublicDataFolder: "Dish",
}) })
db.DBPath = en.DataFolder() + "dishes.db" db = sql.New(en.DataFolder() + "dishes.db")
if _, err := en.GetLazyData("dishes.db", true); err != nil { if _, err := en.GetLazyData("dishes.db", true); err != nil {
logrus.Warnln("[dish]获取菜谱数据库文件失败") logrus.Warnln("[dish]获取菜谱数据库文件失败")
@ -62,7 +61,7 @@ func init() {
return return
} }
name := ctx.NickName() name := ctx.CardOrNickName(ctx.Event.UserID)
dishName := ctx.State["args"].(string) dishName := ctx.State["args"].(string)
if dishName == "" { if dishName == "" {
@ -77,17 +76,15 @@ func init() {
} }
var d dish var d dish
if err := db.Find("dish", &d, fmt.Sprintf("WHERE name like '%%%s%%'", dishName)); err != nil { if err := db.Find("dish", &d, "WHERE name LIKE ?", "%"+dishName+"%"); err != nil {
ctx.SendChain(message.Text("客官,本店没有" + dishName)) ctx.SendChain(message.Text("客官,本店没有" + dishName))
return return
} }
ctx.SendChain(message.Text(fmt.Sprintf( ctx.SendChain(message.Text(
"已为客官%s找到%s的做法辣\n"+ "已为客官", name, "找到", d.Name, "的做法辣!\n",
"原材料:%s\n"+ "原材料:", d.Materials, "\n",
"步骤:\n"+ "步骤:", d.Steps,
"%s",
name, d.Name, d.Materials, d.Steps),
)) ))
}) })
@ -105,12 +102,10 @@ func init() {
return return
} }
ctx.SendChain(message.Text(fmt.Sprintf( ctx.SendChain(message.Text(
"已为客官%s送上%s的做法\n"+ "已为客官", name, "送上", d.Name, "的做法:\n",
"原材料:%s\n"+ "原材料:", d.Materials, "\n",
"步骤:\n"+ "步骤:", d.Steps,
"%s",
name, d.Name, d.Materials, d.Steps),
)) ))
}) })
} }

View File

@ -27,7 +27,7 @@ type sea struct {
Time string `db:"time"` // we need to know the current time,master> Time string `db:"time"` // we need to know the current time,master>
} }
var seaSide = &sql.Sqlite{} var seaSide sql.Sqlite
var seaLocker sync.RWMutex var seaLocker sync.RWMutex
// We need a container to inject what we need :( // We need a container to inject what we need :(
@ -39,15 +39,15 @@ func init() {
Help: "- @bot pick" + "- @bot throw xxx (xxx为投递内容)", Help: "- @bot pick" + "- @bot throw xxx (xxx为投递内容)",
PrivateDataFolder: "driftbottle", PrivateDataFolder: "driftbottle",
}) })
seaSide.DBPath = en.DataFolder() + "sea.db" seaSide = sql.New(en.DataFolder() + "sea.db")
err := seaSide.Open(time.Hour) err := seaSide.Open(time.Hour)
if err != nil { if err != nil {
panic(err) panic(err)
} }
_ = createChannel(seaSide) _ = createChannel(&seaSide)
en.OnFullMatch("pick", zero.OnlyToMe, zero.OnlyGroup).Limit(ctxext.LimitByGroup).SetBlock(true).Handle(func(ctx *zero.Ctx) { en.OnFullMatch("pick", zero.OnlyToMe, zero.OnlyGroup).Limit(ctxext.LimitByGroup).SetBlock(true).Handle(func(ctx *zero.Ctx) {
be, err := fetchBottle(seaSide) be, err := fetchBottle(&seaSide)
if err != nil { if err != nil {
ctx.SendChain(message.Text("ERR:", err)) ctx.SendChain(message.Text("ERR:", err))
} }
@ -75,7 +75,7 @@ func init() {
senderFormatTime, senderFormatTime,
ctx.CardOrNickName(ctx.Event.UserID), ctx.CardOrNickName(ctx.Event.UserID),
rawMessageCallBack, rawMessageCallBack,
).throw(seaSide) ).throw(&seaSide)
if err != nil { if err != nil {
ctx.SendChain(message.Text("ERROR: ", err)) ctx.SendChain(message.Text("ERROR: ", err))
return return

View File

@ -79,7 +79,7 @@ func match(ctx *zero.Ctx) bool {
return false return false
} }
func face2emoji(face message.MessageSegment) rune { func face2emoji(face message.Segment) rune {
if face.Type == "text" { if face.Type == "text" {
r := []rune(face.Data["text"]) r := []rune(face.Data["text"])
if len(r) != 1 { if len(r) != 1 {

118
plugin/emozi/main.go Normal file
View File

@ -0,0 +1,118 @@
// Package emozi 颜文字抽象转写
package emozi
import (
"os"
"strconv"
"strings"
"sync"
"time"
"github.com/FloatTech/AnimeAPI/emozi"
ctrl "github.com/FloatTech/zbpctrl"
"github.com/FloatTech/zbputils/control"
"github.com/FloatTech/zbputils/ctxext"
"github.com/sirupsen/logrus"
zero "github.com/wdvxdr1123/ZeroBot"
"github.com/wdvxdr1123/ZeroBot/message"
)
func init() {
en := control.AutoRegister(&ctrl.Options[*zero.Ctx]{
DisableOnDefault: false,
Brief: "颜文字抽象转写",
Help: "- 抽象转写[文段]\n- 抽象还原[文段]\n- 抽象登录[用户名]",
PrivateDataFolder: "emozi",
})
usr := emozi.Anonymous()
data, err := os.ReadFile(en.DataFolder() + "user.txt")
refresh := func() {
go func() {
t := time.NewTicker(time.Hour)
defer t.Stop()
for range t.C {
if !usr.IsValid() {
time.Sleep(time.Second * 2)
err := usr.Login()
if err != nil {
logrus.Warnln("[emozi] 重新登录账号失败:", err)
}
}
}
}()
}
refresher := sync.Once{}
if err == nil {
arr := strings.Split(string(data), "\n")
if len(arr) >= 2 {
usr = emozi.NewUser(arr[0], arr[1])
err = usr.Login()
if err != nil {
logrus.Infoln("[emozi]", "以", arr[0], "身份登录失败:", err)
usr = emozi.Anonymous()
} else {
logrus.Infoln("[emozi]", "以", arr[0], "身份登录成功")
refresher.Do(refresh)
}
}
}
en.OnPrefix("抽象转写").Limit(ctxext.LimitByUser).SetBlock(true).Handle(func(ctx *zero.Ctx) {
txt := strings.TrimSpace(ctx.State["args"].(string))
out, chs, err := usr.Marshal(false, txt)
if err != nil {
ctx.SendChain(message.Text("ERROR: ", err))
return
}
if len(chs) == 0 {
ctx.Send(message.ReplyWithMessage(ctx.Event.MessageID, message.Text(out)))
return
}
for i, c := range chs {
ch := ctx.Get("请选择第" + strconv.Itoa(i) + "个多音字(1~" + strconv.Itoa(c) + ")")
n, err := strconv.Atoi(ch)
if err != nil {
ctx.SendChain(message.Text("ERROR: ", err))
return
}
if n < 1 || n > c {
ctx.SendChain(message.Text("ERROR: 输入越界"))
return
}
chs[i] = n - 1
}
out, _, err = usr.Marshal(false, txt, chs...)
if err != nil {
ctx.SendChain(message.Text("ERROR: ", err))
return
}
ctx.Send(message.ReplyWithMessage(ctx.Event.MessageID, message.Text(out)))
})
en.OnPrefix("抽象还原").Limit(ctxext.LimitByUser).SetBlock(true).Handle(func(ctx *zero.Ctx) {
txt := strings.TrimSpace(ctx.State["args"].(string))
out, err := usr.Unmarshal(false, txt)
if err != nil {
ctx.SendChain(message.Text("ERROR: ", err))
return
}
ctx.Send(message.ReplyWithMessage(ctx.Event.MessageID, message.Text(out)))
})
en.OnPrefix("抽象登录", zero.OnlyPrivate).Limit(ctxext.LimitByUser).SetBlock(true).Handle(func(ctx *zero.Ctx) {
name := strings.TrimSpace(ctx.State["args"].(string))
pswd := strings.TrimSpace(ctx.Get("请输入密码"))
newusr := emozi.NewUser(name, pswd)
err := newusr.Login()
if err != nil {
ctx.SendChain(message.Text("ERROR: ", err))
return
}
err = os.WriteFile(en.DataFolder()+"user.txt", []byte(name+"\n"+pswd), 0644)
if err != nil {
ctx.SendChain(message.Text("ERROR: ", err))
return
}
usr = newusr
refresher.Do(refresh)
ctx.SendChain(message.Text("成功"))
})
}

View File

@ -146,7 +146,7 @@ func init() {
digest := md5.Sum(helper.StringToBytes(zipfile + strconv.Itoa(index) + title + text)) digest := md5.Sum(helper.StringToBytes(zipfile + strconv.Itoa(index) + title + text))
cachefile := cache + hex.EncodeToString(digest[:]) cachefile := cache + hex.EncodeToString(digest[:])
err = pool.SendImageFromPool(cachefile, cachefile, func() error { err = pool.SendImageFromPool(cachefile, func(cachefile string) error {
f, err := os.Create(cachefile) f, err := os.Create(cachefile)
if err != nil { if err != nil {
return err return err
@ -154,7 +154,7 @@ func init() {
_, err = draw(background, fontdata, title, text, f) _, err = draw(background, fontdata, title, text, f)
_ = f.Close() _ = f.Close()
return err return err
}, ctxext.Send(ctx), ctxext.GetMessage(ctx)) }, ctxext.Send(ctx))
if err != nil { if err != nil {
ctx.SendChain(message.Text("ERROR: ", err)) ctx.SendChain(message.Text("ERROR: ", err))
return return

View File

@ -21,7 +21,7 @@ type joke struct {
Text string `db:"text"` Text string `db:"text"`
} }
var db = &sql.Sqlite{} var db sql.Sqlite
func init() { func init() {
en := control.AutoRegister(&ctrl.Options[*zero.Ctx]{ en := control.AutoRegister(&ctrl.Options[*zero.Ctx]{
@ -32,7 +32,7 @@ func init() {
}) })
en.OnPrefixGroup([]string{"讲个笑话", "夸夸"}, fcext.DoOnceOnSuccess(func(ctx *zero.Ctx) bool { en.OnPrefixGroup([]string{"讲个笑话", "夸夸"}, fcext.DoOnceOnSuccess(func(ctx *zero.Ctx) bool {
db.DBPath = en.DataFolder() + "jokes.db" db = sql.New(en.DataFolder() + "jokes.db")
_, err := en.GetLazyData("jokes.db", true) _, err := en.GetLazyData("jokes.db", true)
if err != nil { if err != nil {
ctx.SendChain(message.Text("ERROR: ", err)) ctx.SendChain(message.Text("ERROR: ", err))

View File

@ -20,7 +20,7 @@ func dlchan(name string, s *string, wg *sync.WaitGroup, exit func(error)) {
defer wg.Done() defer wg.Done()
target := datapath + `materials/` + name target := datapath + `materials/` + name
if file.IsNotExist(target) { if file.IsNotExist(target) {
data, err := web.RequestDataWith(web.NewTLS12Client(), `https://gitcode.net/m0_60838134/imagematerials/-/raw/main/`+name, "GET", "gitcode.net", web.RandUA(), nil) data, err := web.GetData(`https://gitea.seku.su/fumiama/ImageMaterials/raw/branch/master/` + name)
if err != nil { if err != nil {
_ = os.Remove(target) _ = os.Remove(target)
exit(err) exit(err)
@ -48,7 +48,7 @@ func dlchan(name string, s *string, wg *sync.WaitGroup, exit func(error)) {
func dlblock(name string) (string, error) { func dlblock(name string) (string, error) {
target := datapath + `materials/` + name target := datapath + `materials/` + name
if file.IsNotExist(target) { if file.IsNotExist(target) {
data, err := web.RequestDataWith(web.NewTLS12Client(), `https://gitcode.net/m0_60838134/imagematerials/-/raw/main/`+name, "GET", "gitcode.net", web.RandUA(), nil) data, err := web.GetData(`https://gitea.seku.su/fumiama/ImageMaterials/raw/branch/master/` + name)
if err != nil { if err != nil {
_ = os.Remove(target) _ = os.Remove(target)
return "", err return "", err

View File

@ -246,11 +246,7 @@ func init() {
ctx.SendChain(message.Text(serviceErr, err)) ctx.SendChain(message.Text(serviceErr, err))
return return
} }
if err == nil {
ctx.SendChain(message.Text("成功!")) ctx.SendChain(message.Text("成功!"))
} else {
ctx.SendChain(message.Text(serviceErr, err))
}
}) })
// 下载歌曲到对应的歌单里面 // 下载歌曲到对应的歌单里面
engine.OnRegex(`^下载歌单\s*((https:\/\/music\.163\.com\/#\/playlist\?id=)?(\d+)|http:\/\/music\.163\.com\/playlist\/(\d+).*[^\s$])\s*到\s*(.*)$`, zero.SuperUserPermission).SetBlock(true).Limit(ctxext.LimitByGroup). engine.OnRegex(`^下载歌单\s*((https:\/\/music\.163\.com\/#\/playlist\?id=)?(\d+)|http:\/\/music\.163\.com\/playlist\/(\d+).*[^\s$])\s*到\s*(.*)$`, zero.SuperUserPermission).SetBlock(true).Limit(ctxext.LimitByGroup).

View File

@ -130,7 +130,7 @@ func init() {
after := time.NewTimer(120 * time.Second) after := time.NewTimer(120 * time.Second)
wg := sync.WaitGroup{} wg := sync.WaitGroup{}
var ( var (
messageStr message.MessageSegment // 文本信息 messageStr message.Segment // 文本信息
tickCount = 0 // 音频数量 tickCount = 0 // 音频数量
answerCount = 0 // 问答次数 answerCount = 0 // 问答次数
win bool // 是否赢得游戏 win bool // 是否赢得游戏
@ -281,7 +281,7 @@ func cutMusic(musicName, pathOfMusic, outputPath string) (err error) {
} }
// 数据匹配(结果信息,答题次数,提示次数,是否结束游戏) // 数据匹配(结果信息,答题次数,提示次数,是否结束游戏)
func gameMatch(c *zero.Ctx, beginner int64, musicInfo []string, answerTimes, tickTimes int) (message.MessageSegment, int, int, bool) { func gameMatch(c *zero.Ctx, beginner int64, musicInfo []string, answerTimes, tickTimes int) (message.Segment, int, int, bool) {
answer := strings.Replace(c.Event.Message.String(), "-", "", 1) answer := strings.Replace(c.Event.Message.String(), "-", "", 1)
// 回答内容转小写,比对时再把标准答案转小写 // 回答内容转小写,比对时再把标准答案转小写
answer = ConvertText(answer) answer = ConvertText(answer)

View File

@ -15,7 +15,6 @@ import (
"github.com/FloatTech/zbputils/ctxext" "github.com/FloatTech/zbputils/ctxext"
"github.com/pkg/errors" "github.com/pkg/errors"
zero "github.com/wdvxdr1123/ZeroBot" zero "github.com/wdvxdr1123/ZeroBot"
"github.com/wdvxdr1123/ZeroBot/extension/single"
"github.com/wdvxdr1123/ZeroBot/message" "github.com/wdvxdr1123/ZeroBot/message"
// 图片输出 // 图片输出
@ -65,17 +64,7 @@ var (
"- 下载歌单[网易云歌单链接/ID]到[歌单名称]\n" + "- 下载歌单[网易云歌单链接/ID]到[歌单名称]\n" +
"- 解除绑定 [歌单名称]", "- 解除绑定 [歌单名称]",
PrivateDataFolder: "guessmusic", PrivateDataFolder: "guessmusic",
}).ApplySingle(single.New( }).ApplySingle(ctxext.NewGroupSingle("已经有正在进行的游戏..."))
single.WithKeyFn(func(ctx *zero.Ctx) int64 { return ctx.Event.GroupID }),
single.WithPostFn[int64](func(ctx *zero.Ctx) {
ctx.Break()
ctx.Send(
message.ReplyWithMessage(ctx.Event.MessageID,
message.Text("已经有正在进行的游戏..."),
),
)
}),
))
// 用于存放歌曲三个片段的缓存文件夹 // 用于存放歌曲三个片段的缓存文件夹
cachePath = engine.DataFolder() + "cache/" cachePath = engine.DataFolder() + "cache/"
// 用于存放用户的配置 // 用于存放用户的配置
@ -535,7 +524,7 @@ func getFileURLbyFileName(ctx *zero.Ctx, fileName string) (fileSearchName, fileU
for _, fileNameOflist := range files { for _, fileNameOflist := range files {
if strings.Contains(fileNameOflist.Get("file_name").String(), fileName) { if strings.Contains(fileNameOflist.Get("file_name").String(), fileName) {
fileSearchName = fileNameOflist.Get("file_name").String() fileSearchName = fileNameOflist.Get("file_name").String()
fileURL = ctx.GetThisGroupFileUrl(fileNameOflist.Get("busid").Int(), fileNameOflist.Get("file_id").String()) fileURL = ctx.GetThisGroupFileURL(fileNameOflist.Get("busid").Int(), fileNameOflist.Get("file_id").String())
return return
} }
} }
@ -561,7 +550,7 @@ func getFileURLbyfolderID(ctx *zero.Ctx, fileName, folderid string) (fileSearchN
for _, fileNameOflist := range files { for _, fileNameOflist := range files {
if strings.Contains(fileNameOflist.Get("file_name").String(), fileName) { if strings.Contains(fileNameOflist.Get("file_name").String(), fileName) {
fileSearchName = fileNameOflist.Get("file_name").String() fileSearchName = fileNameOflist.Get("file_name").String()
fileURL = ctx.GetThisGroupFileUrl(fileNameOflist.Get("busid").Int(), fileNameOflist.Get("file_id").String()) fileURL = ctx.GetThisGroupFileURL(fileNameOflist.Get("busid").Int(), fileNameOflist.Get("file_id").String())
return return
} }
} }

View File

@ -6,7 +6,7 @@ import (
sql "github.com/FloatTech/sqlite" sql "github.com/FloatTech/sqlite"
) )
var db = &sql.Sqlite{} var db sql.Sqlite
var mu sync.RWMutex var mu sync.RWMutex
type picture struct { type picture struct {

View File

@ -10,6 +10,7 @@ import (
"github.com/FloatTech/floatbox/binary" "github.com/FloatTech/floatbox/binary"
fcext "github.com/FloatTech/floatbox/ctxext" fcext "github.com/FloatTech/floatbox/ctxext"
sql "github.com/FloatTech/sqlite"
ctrl "github.com/FloatTech/zbpctrl" ctrl "github.com/FloatTech/zbpctrl"
"github.com/FloatTech/zbputils/control" "github.com/FloatTech/zbputils/control"
"github.com/antchfx/htmlquery" "github.com/antchfx/htmlquery"
@ -31,7 +32,7 @@ func init() {
}) })
getdb := fcext.DoOnceOnSuccess(func(ctx *zero.Ctx) bool { getdb := fcext.DoOnceOnSuccess(func(ctx *zero.Ctx) bool {
db.DBPath = engine.DataFolder() + "pics.db" db = sql.New(engine.DataFolder() + "pics.db")
_, _ = engine.GetLazyData("pics.db", false) _, _ = engine.GetLazyData("pics.db", false)
err := db.Open(time.Hour) err := db.Open(time.Hour)
if err != nil { if err != nil {
@ -95,7 +96,7 @@ func init() {
u := "https:" + v.Attr[0].Val u := "https:" + v.Attr[0].Val
i := crc64.Checksum(binary.StringToBytes(u), crc64.MakeTable(crc64.ISO)) i := crc64.Checksum(binary.StringToBytes(u), crc64.MakeTable(crc64.ISO))
mu.RLock() mu.RLock()
ok := db.CanFind("picture", "where id="+strconv.FormatUint(i, 10)) ok := db.CanFind("picture", "WHERE id = ?", i)
mu.RUnlock() mu.RUnlock()
if !ok { if !ok {
mu.Lock() mu.Lock()

View File

@ -6,6 +6,7 @@ import (
"github.com/FloatTech/floatbox/binary" "github.com/FloatTech/floatbox/binary"
fcext "github.com/FloatTech/floatbox/ctxext" fcext "github.com/FloatTech/floatbox/ctxext"
sql "github.com/FloatTech/sqlite"
ctrl "github.com/FloatTech/zbpctrl" ctrl "github.com/FloatTech/zbpctrl"
"github.com/FloatTech/zbputils/control" "github.com/FloatTech/zbputils/control"
"github.com/FloatTech/zbputils/img/text" "github.com/FloatTech/zbputils/img/text"
@ -26,7 +27,7 @@ func init() { // 插件主体
}) })
getdb := fcext.DoOnceOnSuccess(func(ctx *zero.Ctx) bool { getdb := fcext.DoOnceOnSuccess(func(ctx *zero.Ctx) bool {
db.DBPath = engine.DataFolder() + "item.db" db = sql.New(engine.DataFolder() + "item.db")
_, err := engine.GetLazyData("item.db", true) _, err := engine.GetLazyData("item.db", true)
if err != nil { if err != nil {
ctx.SendChain(message.Text("ERROR: ", err)) ctx.SendChain(message.Text("ERROR: ", err))

View File

@ -17,14 +17,16 @@ type item struct {
Datetime time.Time `db:"datetime"` Datetime time.Time `db:"datetime"`
} }
var db = &sql.Sqlite{} var db sql.Sqlite
func getRandomAudioByCategory(category string) (t item) { func getRandomAudioByCategory(category string) (t item) {
_ = db.Find("item", &t, "where category = '"+category+"' ORDER BY RANDOM() limit 1") _ = db.Find("item", &t, "WHERE category = ? ORDER BY RANDOM() limit 1", category)
return return
} }
func getRandomAudioByCategoryAndKeyword(category string, keyword string) (t item) { func getRandomAudioByCategoryAndKeyword(category string, keyword string) (t item) {
_ = db.Find("item", &t, "where category = '"+category+"' and (title like '%"+keyword+"%' or content like '%"+keyword+"%') ORDER BY RANDOM() limit 1") _ = db.Find("item", &t,
"WHERE category = ? and (title LIKE ? OR content LIKE ?) ORDER BY RANDOM() limit 1",
category, "%"+keyword+"%", "%"+keyword+"%")
return return
} }

View File

@ -2,7 +2,6 @@
package kfccrazythursday package kfccrazythursday
import ( import (
"github.com/FloatTech/floatbox/binary"
"github.com/FloatTech/floatbox/web" "github.com/FloatTech/floatbox/web"
ctrl "github.com/FloatTech/zbpctrl" ctrl "github.com/FloatTech/zbpctrl"
"github.com/FloatTech/zbputils/control" "github.com/FloatTech/zbputils/control"
@ -11,7 +10,7 @@ import (
) )
const ( const (
crazyURL = "http://api.jixs.cc/api/wenan-fkxqs/index.php" crazyURL = "https://api.pearktrue.cn/api/kfc/"
) )
func init() { func init() {
@ -26,6 +25,8 @@ func init() {
ctx.SendChain(message.Text("ERROR: ", err)) ctx.SendChain(message.Text("ERROR: ", err))
return return
} }
ctx.SendChain(message.Text(binary.BytesToString(data)))
// 根据来源API修改返回方式到直接输出文本
ctx.SendChain(message.Text(string(data)))
}) })
} }

View File

@ -18,7 +18,6 @@ import (
ctrl "github.com/FloatTech/zbpctrl" ctrl "github.com/FloatTech/zbpctrl"
"github.com/FloatTech/zbputils/control" "github.com/FloatTech/zbputils/control"
"github.com/FloatTech/zbputils/ctxext" "github.com/FloatTech/zbputils/ctxext"
imagepool "github.com/FloatTech/zbputils/img/pool"
) )
const ( const (
@ -68,19 +67,8 @@ func init() {
ctx.SendChain(message.Text("ERROR: ", err)) ctx.SendChain(message.Text("ERROR: ", err))
continue continue
} }
name := imageurl[strings.LastIndex(imageurl, "/")+1 : len(imageurl)-4]
m, err := imagepool.GetImage(name)
if err != nil {
m.SetFile(imageurl)
_, _ = m.Push(ctxext.SendToSelf(ctx), ctxext.GetMessage(ctx))
process.SleepAbout1sTo2s()
}
if err == nil {
queue <- m.String()
} else {
queue <- imageurl queue <- imageurl
} }
}
}() }()
select { select {
case <-time.After(time.Minute): case <-time.After(time.Minute):

View File

@ -18,7 +18,7 @@ import (
const gistraw = "https://gist.githubusercontent.com/%s/%s/raw/%s" const gistraw = "https://gist.githubusercontent.com/%s/%s/raw/%s"
func checkNewUser(qq, gid int64, ghun, hash string) (bool, string) { func checkNewUser(qq, gid int64, ghun, hash string) (bool, string) {
if db.CanFind("member", "where ghun="+ghun) { if db.CanFind("member", "WHERE ghun = ?", ghun) {
return false, "该github用户已入群" return false, "该github用户已入群"
} }
gidsum := md5.Sum(helper.StringToBytes(strconv.FormatInt(gid, 10))) gidsum := md5.Sum(helper.StringToBytes(strconv.FormatInt(gid, 10)))

View File

@ -50,6 +50,7 @@ const (
"- 列出所有提醒\n" + "- 列出所有提醒\n" +
"- 翻牌\n" + "- 翻牌\n" +
"- 赞我\n" + "- 赞我\n" +
"- 群签到\n" +
"- 对信息回复: 回应表情 [表情]\n" + "- 对信息回复: 回应表情 [表情]\n" +
"- 设置欢迎语XXX 可选添加 [{at}] [{nickname}] [{avatar}] [{uid}] [{gid}] [{groupname}]\n" + "- 设置欢迎语XXX 可选添加 [{at}] [{nickname}] [{avatar}] [{uid}] [{gid}] [{groupname}]\n" +
"- 测试欢迎语\n" + "- 测试欢迎语\n" +
@ -63,7 +64,7 @@ const (
) )
var ( var (
db = &sql.Sqlite{} db sql.Sqlite
clock timer.Clock clock timer.Clock
) )
@ -76,12 +77,12 @@ func init() { // 插件主体
}) })
go func() { go func() {
db.DBPath = engine.DataFolder() + "config.db" db = sql.New(engine.DataFolder() + "config.db")
err := db.Open(time.Hour) err := db.Open(time.Hour)
if err != nil { if err != nil {
panic(err) panic(err)
} }
clock = timer.NewClock(db) clock = timer.NewClock(&db)
err = db.Create("welcome", &welcome{}) err = db.Create("welcome", &welcome{})
if err != nil { if err != nil {
panic(err) panic(err)
@ -156,10 +157,11 @@ func init() { // 插件主体
ctx.SendChain(message.Text("全员自闭结束~")) ctx.SendChain(message.Text("全员自闭结束~"))
}) })
// 禁言 // 禁言
engine.OnRegex(`^禁言.*?(\d+).*?\s(\d+)(.*)`, zero.OnlyGroup, zero.AdminPermission).SetBlock(true). engine.OnMessage(zero.NewPattern(nil).Text("^禁言").At().Text("(\\d+)\\s*(.*)").AsRule(), zero.OnlyGroup, zero.AdminPermission).SetBlock(true).
Handle(func(ctx *zero.Ctx) { Handle(func(ctx *zero.Ctx) {
duration := math.Str2Int64(ctx.State["regex_matched"].([]string)[2]) parsed := ctx.State[zero.KeyPattern].([]zero.PatternParsed)
switch ctx.State["regex_matched"].([]string)[3] { duration := math.Str2Int64(parsed[2].Text()[1])
switch parsed[2].Text()[2] {
case "分钟": case "分钟":
// //
case "小时": case "小时":
@ -173,7 +175,7 @@ func init() { // 插件主体
duration = 43199 // qq禁言最大时长为一个月 duration = 43199 // qq禁言最大时长为一个月
} }
ctx.SetThisGroupBan( ctx.SetThisGroupBan(
math.Str2Int64(ctx.State["regex_matched"].([]string)[1]), // 要禁言的人的qq math.Str2Int64(parsed[1].At()), // 要禁言的人的qq
duration*60, // 要禁言的时间(分钟) duration*60, // 要禁言的时间(分钟)
) )
ctx.SendChain(message.Text("小黑屋收留成功~")) ctx.SendChain(message.Text("小黑屋收留成功~"))
@ -404,6 +406,12 @@ func init() { // 插件主体
ctx.SendLike(ctx.Event.UserID, 10) ctx.SendLike(ctx.Event.UserID, 10)
ctx.SendChain(message.Reply(ctx.Event.MessageID), message.Text("给你赞了10下哦记得回我~")) ctx.SendChain(message.Reply(ctx.Event.MessageID), message.Text("给你赞了10下哦记得回我~"))
}) })
// 群签到
engine.OnFullMatch("群签到", zero.OnlyGroup).SetBlock(true).Limit(ctxext.LimitByUser).
Handle(func(ctx *zero.Ctx) {
ctx.SetGroupSign(ctx.Event.GroupID)
ctx.SendChain(message.Text("群签到成功,可在手机端输入框中的打卡查看"))
})
facere := regexp.MustCompile(`\[CQ:face,id=(\d+)\]`) facere := regexp.MustCompile(`\[CQ:face,id=(\d+)\]`)
// 给消息回应表情 // 给消息回应表情
engine.OnRegex(`^\[CQ:reply,id=(-?\d+)\].*回应表情\s*(.+)\s*$`, zero.AdminPermission, zero.OnlyGroup).SetBlock(true). engine.OnRegex(`^\[CQ:reply,id=(-?\d+)\].*回应表情\s*(.+)\s*$`, zero.AdminPermission, zero.OnlyGroup).SetBlock(true).
@ -442,7 +450,7 @@ func init() { // 插件主体
Handle(func(ctx *zero.Ctx) { Handle(func(ctx *zero.Ctx) {
if ctx.Event.NoticeType == "group_increase" && ctx.Event.SelfID != ctx.Event.UserID { if ctx.Event.NoticeType == "group_increase" && ctx.Event.SelfID != ctx.Event.UserID {
var w welcome var w welcome
err := db.Find("welcome", &w, "where gid = "+strconv.FormatInt(ctx.Event.GroupID, 10)) err := db.Find("welcome", &w, "WHERE gid = ?", ctx.Event.GroupID)
if err == nil { if err == nil {
ctx.SendGroupMessage(ctx.Event.GroupID, message.ParseMessageFromString(welcometocq(ctx, w.Msg))) ctx.SendGroupMessage(ctx.Event.GroupID, message.ParseMessageFromString(welcometocq(ctx, w.Msg)))
} else { } else {
@ -494,7 +502,7 @@ func init() { // 插件主体
Handle(func(ctx *zero.Ctx) { Handle(func(ctx *zero.Ctx) {
if ctx.Event.NoticeType == "group_decrease" { if ctx.Event.NoticeType == "group_decrease" {
var w welcome var w welcome
err := db.Find("farewell", &w, "where gid = "+strconv.FormatInt(ctx.Event.GroupID, 10)) err := db.Find("farewell", &w, "WHERE gid = ?", ctx.Event.GroupID)
if err == nil { if err == nil {
collectsend(ctx, message.ParseMessageFromString(welcometocq(ctx, w.Msg))...) collectsend(ctx, message.ParseMessageFromString(welcometocq(ctx, w.Msg))...)
} else { } else {
@ -523,7 +531,7 @@ func init() { // 插件主体
engine.OnFullMatch("测试欢迎语", zero.OnlyGroup, zero.AdminPermission).SetBlock(true). engine.OnFullMatch("测试欢迎语", zero.OnlyGroup, zero.AdminPermission).SetBlock(true).
Handle(func(ctx *zero.Ctx) { Handle(func(ctx *zero.Ctx) {
var w welcome var w welcome
err := db.Find("welcome", &w, "where gid = "+strconv.FormatInt(ctx.Event.GroupID, 10)) err := db.Find("welcome", &w, "WHERE gid = ?", ctx.Event.GroupID)
if err == nil { if err == nil {
ctx.SendGroupMessage(ctx.Event.GroupID, message.ParseMessageFromString(welcometocq(ctx, w.Msg))) ctx.SendGroupMessage(ctx.Event.GroupID, message.ParseMessageFromString(welcometocq(ctx, w.Msg)))
} else { } else {
@ -550,7 +558,7 @@ func init() { // 插件主体
engine.OnFullMatch("测试告别辞", zero.OnlyGroup, zero.AdminPermission).SetBlock(true). engine.OnFullMatch("测试告别辞", zero.OnlyGroup, zero.AdminPermission).SetBlock(true).
Handle(func(ctx *zero.Ctx) { Handle(func(ctx *zero.Ctx) {
var w welcome var w welcome
err := db.Find("farewell", &w, "where gid = "+strconv.FormatInt(ctx.Event.GroupID, 10)) err := db.Find("farewell", &w, "WHERE gid = ?", ctx.Event.GroupID)
if err == nil { if err == nil {
ctx.SendGroupMessage(ctx.Event.GroupID, message.ParseMessageFromString(welcometocq(ctx, w.Msg))) ctx.SendGroupMessage(ctx.Event.GroupID, message.ParseMessageFromString(welcometocq(ctx, w.Msg)))
} else { } else {
@ -649,7 +657,7 @@ func init() { // 插件主体
if rsp.RetCode == 0 { if rsp.RetCode == 0 {
ctx.SendChain(message.Text(option, "成功")) ctx.SendChain(message.Text(option, "成功"))
} else { } else {
ctx.SendChain(message.Text(option, "失败, 信息: ", rsp.Msg, "解释: ", rsp.Wording)) ctx.SendChain(message.Text(option, "失败, 信息: ", rsp.Message, "解释: ", rsp.Wording))
} }
}) })
engine.OnCommand("精华列表", zero.OnlyGroup, zero.AdminPermission).SetBlock(true).Limit(ctxext.LimitByUser).Handle(func(ctx *zero.Ctx) { engine.OnCommand("精华列表", zero.OnlyGroup, zero.AdminPermission).SetBlock(true).Limit(ctxext.LimitByUser).Handle(func(ctx *zero.Ctx) {
@ -698,7 +706,7 @@ func init() { // 插件主体
if rsp.RetCode == 0 { if rsp.RetCode == 0 {
ctx.SendChain(message.Text("取消成功")) ctx.SendChain(message.Text("取消成功"))
} else { } else {
ctx.SendChain(message.Text("取消失败, 信息: ", rsp.Msg, "解释: ", rsp.Wording)) ctx.SendChain(message.Text("取消失败, 信息: ", rsp.Message, "解释: ", rsp.Wording))
} }
}) })
} }

View File

@ -9,17 +9,21 @@ import (
"github.com/wdvxdr1123/ZeroBot/message" "github.com/wdvxdr1123/ZeroBot/message"
) )
var slowsenders = syncx.Map[int64, *syncx.Lazy[*slowdo.Job[*zero.Ctx, message.MessageSegment]]]{} var slowsenders = syncx.Map[int64, *syncx.Lazy[*slowdo.Job[*zero.Ctx, message.Segment]]]{}
func collectsend(ctx *zero.Ctx, msgs ...message.MessageSegment) { func collectsend(ctx *zero.Ctx, msgs ...message.Segment) {
id := ctx.Event.GroupID id := ctx.Event.GroupID
if id == 0 { if id == 0 {
// only support group // only support group
return return
} }
lazy, _ := slowsenders.LoadOrStore(id, &syncx.Lazy[*slowdo.Job[*zero.Ctx, message.MessageSegment]]{ lazy, _ := slowsenders.LoadOrStore(id, &syncx.Lazy[*slowdo.Job[*zero.Ctx, message.Segment]]{
Init: func() *slowdo.Job[*zero.Ctx, message.MessageSegment] { Init: func() *slowdo.Job[*zero.Ctx, message.Segment] {
x, err := slowdo.NewJob(time.Second*5, ctx, func(ctx *zero.Ctx, msg []message.MessageSegment) { x, err := slowdo.NewJob(time.Second*5, ctx, func(ctx *zero.Ctx, msg []message.Segment) {
if len(msg) == 1 {
ctx.Send(msg)
return
}
m := make(message.Message, len(msg)) m := make(message.Message, len(msg))
for i, item := range msg { for i, item := range msg {
m[i] = message.CustomNode( m[i] = message.CustomNode(

View File

@ -22,7 +22,7 @@ func (t *Timer) InsertInto(db *sql.Sqlite) error {
/* /*
func getTimerFrom(db *sql.Sqlite, id uint32) (t Timer, err error) { func getTimerFrom(db *sql.Sqlite, id uint32) (t Timer, err error) {
err = db.Find("timer", &t, "where id = "+strconv.Itoa(int(id))) err = db.Find("timer", &t, "WHERE id = "+strconv.Itoa(int(id)))
return return
} }
*/ */

View File

@ -2,7 +2,6 @@
package timer package timer
import ( import (
"strconv"
"strings" "strings"
"sync" "sync"
"time" "time"
@ -29,7 +28,7 @@ type Clock struct {
var ( var (
// @全体成员 // @全体成员
atall = message.MessageSegment{ atall = message.Segment{
Type: "at", Type: "at",
Data: map[string]string{ Data: map[string]string{
"qq": "all", "qq": "all",
@ -133,7 +132,7 @@ func (c *Clock) CancelTimer(key uint32) bool {
} }
c.timersmu.Lock() c.timersmu.Lock()
delete(*c.timers, key) // 避免重复取消 delete(*c.timers, key) // 避免重复取消
e := c.db.Del("timer", "where id = "+strconv.Itoa(int(key))) e := c.db.Del("timer", "WHERE id = ?", key)
c.timersmu.Unlock() c.timersmu.Unlock()
return e == nil return e == nil
} }

View File

@ -25,8 +25,8 @@ func TestNextWakeTime(t *testing.T) {
} }
func TestClock(t *testing.T) { func TestClock(t *testing.T) {
db := &sql.Sqlite{DBPath: "test.db"} db := sql.New("test.db")
c := NewClock(db) c := NewClock(&db)
c.AddTimerIntoDB(GetFilledTimer([]string{"", "12", "-1", "12", "0", "", "test"}, 0, 0, false)) c.AddTimerIntoDB(GetFilledTimer([]string{"", "12", "-1", "12", "0", "", "test"}, 0, 0, false))
t.Log(c.ListTimers(0)) t.Log(c.ListTimers(0))
t.Fail() t.Fail()

View File

@ -129,12 +129,12 @@ func init() {
fishNumber *= 3 fishNumber *= 3
} }
} else { } else {
fishNmaes, err := dbdata.pickFishFor(uid, fishNumber) fishNames, err := dbdata.pickFishFor(uid, fishNumber*3)
if err != nil { if err != nil {
ctx.SendChain(message.Text("[ERROR at fish.go.5.1]:", err)) ctx.SendChain(message.Text("[ERROR at fish.go.5.1]:", err))
return return
} }
if len(fishNmaes) == 0 { if len(fishNames) == 0 {
equipInfo.Durable = 0 equipInfo.Durable = 0
err = dbdata.updateUserEquip(equipInfo) err = dbdata.updateUserEquip(equipInfo)
if err != nil { if err != nil {
@ -143,14 +143,14 @@ func init() {
ctx.SendChain(message.Text("美西螈因为没吃到鱼,钓鱼时一直没回来,你失去了美西螈")) ctx.SendChain(message.Text("美西螈因为没吃到鱼,钓鱼时一直没回来,你失去了美西螈"))
return return
} }
msg = "(美西螈吃掉了" msg = "(美西螈掉落翻5倍吃3倍鱼\n吃掉了"
fishNumber = 0 fishNumber = 0
for name, number := range fishNmaes { for name, number := range fishNames {
fishNumber += number fishNumber += number
msg += strconv.Itoa(number) + name + " " msg += strconv.Itoa(number) + name + " "
} }
msg += ")" msg += ")"
fishNumber /= 2 fishNumber /= 3
} }
waitTime := 120 / (equipInfo.Induce + 1) waitTime := 120 / (equipInfo.Induce + 1)
ctx.SendChain(message.Reply(ctx.Event.MessageID), message.Text("你开始去钓鱼了,请耐心等待鱼上钩(预计要", time.Second*time.Duration(waitTime), ")")) ctx.SendChain(message.Reply(ctx.Event.MessageID), message.Text("你开始去钓鱼了,请耐心等待鱼上钩(预计要", time.Second*time.Duration(waitTime), ")"))
@ -267,8 +267,8 @@ func init() {
thingName = "金竿" thingName = "金竿"
case dice >= probabilities["钻石竿"].Min && dice < probabilities["钻石竿"].Max: case dice >= probabilities["钻石竿"].Min && dice < probabilities["钻石竿"].Max:
thingName = "钻石竿" thingName = "钻石竿"
case dice >= probabilities["下界合金竿竿竿"].Min && dice < probabilities["下界合金竿竿竿"].Max: case dice >= probabilities["下界合金竿"].Min && dice < probabilities["下界合金竿"].Max:
thingName = "下界合金竿竿竿" thingName = "下界合金竿"
default: default:
thingName = "木竿" thingName = "木竿"
} }
@ -323,7 +323,7 @@ func init() {
newThing = thingInfo[0] newThing = thingInfo[0]
} }
if equipInfo.Equip == "美西螈" && thingName != "美西螈" { if equipInfo.Equip == "美西螈" && thingName != "美西螈" {
number += 2 number += 4
} }
newThing.Number += number newThing.Number += number
} }

View File

@ -6,6 +6,7 @@ import (
"math/rand" "math/rand"
"os" "os"
"strconv" "strconv"
"strings"
"sync" "sync"
"time" "time"
@ -20,15 +21,15 @@ import (
) )
type fishdb struct { type fishdb struct {
db *sql.Sqlite
sync.RWMutex sync.RWMutex
db sql.Sqlite
} }
// FishLimit 钓鱼次数上限 // FishLimit 钓鱼次数上限
const FishLimit = 50 const FishLimit = 50
// version 规则版本号 // version 规则版本号
const version = "5.5.8" const version = "5.6.2"
// 各物品信息 // 各物品信息
type jsonInfo struct { type jsonInfo struct {
@ -101,7 +102,7 @@ type buffInfo struct {
Coupon int `db:"Buff1"` // 优惠卷 Coupon int `db:"Buff1"` // 优惠卷
SalesPole int `db:"Buff2"` // 卖鱼竿上限 SalesPole int `db:"Buff2"` // 卖鱼竿上限
BuyTing int `db:"Buff3"` // 购买上限 BuyTing int `db:"Buff3"` // 购买上限
SalesFish int `db:"Buff4"` // 卖鱼次数 Buff4 int `db:"Buff4"` // 暂定
Buff5 int `db:"Buff5"` // 暂定 Buff5 int `db:"Buff5"` // 暂定
Buff6 int `db:"Buff6"` // 暂定 Buff6 int `db:"Buff6"` // 暂定
Buff7 int `db:"Buff7"` // 暂定 Buff7 int `db:"Buff7"` // 暂定
@ -121,39 +122,31 @@ var (
durationList = make(map[string]int, 50) // 装备耐久分布 durationList = make(map[string]int, 50) // 装备耐久分布
discountList = make(map[string]int, 50) // 价格波动信息 discountList = make(map[string]int, 50) // 价格波动信息
enchantLevel = []string{"0", "", "Ⅱ", "Ⅲ"} enchantLevel = []string{"0", "", "Ⅱ", "Ⅲ"}
dbdata = &fishdb{ dbdata fishdb
db: &sql.Sqlite{},
}
) )
var ( var (
engine = control.AutoRegister(&ctrl.Options[*zero.Ctx]{ engine = control.AutoRegister(&ctrl.Options[*zero.Ctx]{
DisableOnDefault: false, DisableOnDefault: false,
Brief: "钓鱼", Brief: "钓鱼",
Help: "一款钓鱼模拟器\n----------指令----------\n" + Help: "一款钓鱼模拟器,规则:V" + version +
"- 钓鱼看板/钓鱼商店\n- 购买xxx\n- 购买xxx [数量]\n- 出售xxx\n- 出售xxx [数量]\n- 出售所有垃圾\n" + "\n----------指令----------\n" +
"- 钓鱼背包\n- 装备[xx竿|三叉戟|美西螈]\n- 附魔[诱钓|海之眷顾]\n- 修复鱼竿\n- 合成[xx竿|三叉戟]\n- 消除[绑定|宝藏]诅咒\n- 消除[绑定|宝藏]诅咒 [数量]\n" + "- 钓鱼背包\n" +
"- 进行钓鱼\n- 进行n次钓鱼\n- 当前装备概率明细\n" + "- 进行钓鱼 / 进行n次钓鱼\n" +
"规则V" + version + ":\n" + "- 修复鱼竿\n" +
"1.每日的商店价格是波动的!!如何最大化收益自己考虑一下喔\n" + "- 钓鱼商店 / 钓鱼看板\n" +
"2.装备信息:\n-> 木竿 : 耐久上限:30 均价:100 上钩概率:0.7%\n-> 铁竿 : 耐久上限:50 均价:300 上钩概率:0.2%\n-> 金竿 : 耐久上限:70 均价700 上钩概率:0.06%\n" + "- 购买xxx / 购买xxx [数量]\n- 出售xxx / 出售xxx [数量]\n" +
"-> 钻石竿 : 耐久上限:100 均价1500 上钩概率:0.03%\n-> 下界合金竿 : 耐久上限:150 均价3100 上钩概率:0.01%\n-> 三叉戟 : 可使1次钓鱼视为3次钓鱼. 耐久上限:300 均价4000 只能合成、修复和交易\n" + "- 消除[绑定|宝藏]诅咒 / 消除[绑定|宝藏]诅咒 [数量]\n" +
"3.附魔书信息:\n-> 诱钓 : 减少上钩时间. 均价:1000, 上钩概率:0.25%\n-> 海之眷顾 : 增加宝藏上钩概率. 均价:2500, 上钩概率:0.10%\n" + "- 装备[xx竿|三叉戟|美西螈]\n" +
"4.稀有物品:\n-> 唱片 : 出售物品时使用该物品使价格翻倍. 均价:3000, 上钩概率:0.01%\n" + "- 附魔[诱钓|海之眷顾]\n" +
"-> 美西螈 : 可装备,获得隐形[钓鱼佬]buff,并让钓到除鱼竿和美西螈外的物品数量变成3,无耐久上限.不可修复/附魔,每次钓鱼消耗两任意鱼类物品. 均价:3000, 上钩概率:0.01%\n" + "- 合成[xx竿|三叉戟]\n" +
"-> 海豚 : 使空竿概率变成垃圾概率. 均价:1000, 上钩概率:0.19%\n" + "- 出售所有垃圾\n" +
"-> 宝藏诅咒 : 无法交易,每一层就会增加购买时10%价格和减少出售时10%价格(超过10层会变为倒贴钱). 上钩概率:0.25%\n-> 净化书 : 用于消除宝藏诅咒. 均价:5000, 上钩概率:0.19%\n" + "- 当前装备概率明细\n" +
"5.鱼类信息:\n-> 鳕鱼 : 均价:10 上钩概率:0.69%\n-> 鲑鱼 : 均价:50 上钩概率:0.2%\n-> 热带鱼 : 均价:100 上钩概率:0.06%\n-> 河豚 : 均价:300 上钩概率:0.03%\n-> 鹦鹉螺 : 均价:500 上钩概率:0.01%\n-> 墨鱼 : 均价:500 上钩概率:0.01%\n" + "- 查看钓鱼规则\n",
"6.垃圾:\n-> 均价:10 上钩概率:30%\n" +
"7.物品BUFF:\n-> 钓鱼佬 : 当背包名字含有'鱼'的物品数量超过100时激活,钓到物品概率提高至90%\n-> 修复大师 : 当背包鱼竿数量超过10时激活,修复物品时耐久百分百继承\n" +
"8.合成:\n-> 铁竿 : 3x木竿\n-> 金竿 : 3x铁竿\n-> 钻石竿 : 3x金竿\n-> 下界合金竿 : 3x钻石竿\n-> 三叉戟 : 3x下界合金竿\n注:合成成功率90%(包括梭哈),合成鱼竿的附魔等级=(附魔等级合/合成鱼竿数量)\n" +
"9.杂项:\n-> 无装备的情况下,每人最多可以购买3次100块钱的鱼竿\n-> 默认状态钓鱼上钩概率为60%(理论值!!!)\n-> 附魔的鱼竿会因附魔变得昂贵,每个附魔最高3级\n-> 三叉戟不算鱼竿,修复时可直接满耐久\n" +
"-> 鱼竿数量大于50的不能买东西;\n 鱼竿数量大于30的不能钓鱼;\n 每购/售10次鱼竿获得1层宝藏诅咒;\n 每购买20次物品将获得3次价格减半福利;\n 每钓鱼75次获得1本净化书;\n" +
" 每天最多只可出售5个鱼竿和购买15次物品;鱼类交易每天最多100条.",
PublicDataFolder: "McFish", PublicDataFolder: "McFish",
}).ApplySingle(ctxext.DefaultSingle) }).ApplySingle(ctxext.DefaultSingle)
getdb = fcext.DoOnceOnSuccess(func(ctx *zero.Ctx) bool { getdb = fcext.DoOnceOnSuccess(func(ctx *zero.Ctx) bool {
dbdata.db.DBPath = engine.DataFolder() + "fishdata.db" dbdata.db = sql.New(engine.DataFolder() + "fishdata.db")
err := dbdata.db.Open(time.Hour * 24) err := dbdata.db.Open(time.Hour * 24)
if err != nil { if err != nil {
ctx.SendChain(message.Text("[ERROR at main.go.1]:", err)) ctx.SendChain(message.Text("[ERROR at main.go.1]:", err))
@ -245,7 +238,7 @@ func (sql *fishdb) updateFishInfo(uid int64, number int) (residue int, err error
if err != nil { if err != nil {
return 0, err return 0, err
} }
_ = sql.db.Find("fishState", &userInfo, "where ID = "+strconv.FormatInt(uid, 10)) _ = sql.db.Find("fishState", &userInfo, "WHERE ID = ?", uid)
if time.Unix(userInfo.Duration, 0).Day() != time.Now().Day() { if time.Unix(userInfo.Duration, 0).Day() != time.Now().Day() {
userInfo.Fish = 0 userInfo.Fish = 0
userInfo.Duration = time.Now().Unix() userInfo.Duration = time.Now().Unix()
@ -278,7 +271,7 @@ func (sql *fishdb) updateCurseFor(uid int64, info string, number int) (err error
changeCheck := false changeCheck := false
add := 0 add := 0
buffName := "宝藏诅咒" buffName := "宝藏诅咒"
_ = sql.db.Find("fishState", &userInfo, "where ID = "+strconv.FormatInt(uid, 10)) _ = sql.db.Find("fishState", &userInfo, "WHERE ID = ?", uid)
if info == "fish" { if info == "fish" {
userInfo.Bless += number userInfo.Bless += number
for userInfo.Bless >= 75 { for userInfo.Bless >= 75 {
@ -306,7 +299,7 @@ func (sql *fishdb) updateCurseFor(uid int64, info string, number int) (err error
Name: buffName, Name: buffName,
Type: "treasure", Type: "treasure",
} }
_ = sql.db.Find(table, &thing, "where Name = '"+buffName+"'") _ = sql.db.Find(table, &thing, "WHERE Name = ?", buffName)
thing.Number += add thing.Number += add
return sql.db.Insert(table, &thing) return sql.db.Insert(table, &thing)
} }
@ -325,10 +318,10 @@ func (sql *fishdb) checkEquipFor(uid int64) (ok bool, err error) {
if err != nil { if err != nil {
return false, err return false, err
} }
if !sql.db.CanFind("fishState", "where ID = "+strconv.FormatInt(uid, 10)) { if !sql.db.CanFind("fishState", "WHERE ID = ?", uid) {
return true, nil return true, nil
} }
err = sql.db.Find("fishState", &userInfo, "where ID = "+strconv.FormatInt(uid, 10)) err = sql.db.Find("fishState", &userInfo, "WHERE ID = ?", uid)
if err != nil { if err != nil {
return false, err return false, err
} }
@ -346,10 +339,7 @@ func (sql *fishdb) setEquipFor(uid int64) (err error) {
if err != nil { if err != nil {
return err return err
} }
_ = sql.db.Find("fishState", &userInfo, "where ID = "+strconv.FormatInt(uid, 10)) _ = sql.db.Find("fishState", &userInfo, "WHERE ID = ?", uid)
if err != nil {
return err
}
userInfo.Equip++ userInfo.Equip++
return sql.db.Insert("fishState", &userInfo) return sql.db.Insert("fishState", &userInfo)
} }
@ -362,10 +352,10 @@ func (sql *fishdb) getUserEquip(uid int64) (userInfo equip, err error) {
if err != nil { if err != nil {
return return
} }
if !sql.db.CanFind("equips", "where ID = "+strconv.FormatInt(uid, 10)) { if !sql.db.CanFind("equips", "WHERE ID = ?", uid) {
return return
} }
err = sql.db.Find("equips", &userInfo, "where ID = "+strconv.FormatInt(uid, 10)) err = sql.db.Find("equips", &userInfo, "WHERE ID = ?", uid)
return return
} }
@ -378,7 +368,7 @@ func (sql *fishdb) updateUserEquip(userInfo equip) (err error) {
return return
} }
if userInfo.Durable == 0 { if userInfo.Durable == 0 {
return sql.db.Del("equips", "where ID = "+strconv.FormatInt(userInfo.ID, 10)) return sql.db.Del("equips", "WHERE ID = ?", userInfo.ID)
} }
return sql.db.Insert("equips", &userInfo) return sql.db.Insert("equips", &userInfo)
} }
@ -400,13 +390,13 @@ func (sql *fishdb) pickFishFor(uid int64, number int) (fishNames map[string]int,
if count == 0 { if count == 0 {
return return
} }
if !sql.db.CanFind(name, "where Type is 'fish'") { if !sql.db.CanFind(name, "WHERE Type = 'fish'") {
return return
} }
fishInfo := article{} fishInfo := article{}
k := 0 k := 0
for i := number * 2; i > 0 && k < len(fishList); { for i := number; i > 0 && k < len(fishList); {
_ = sql.db.Find(name, &fishInfo, "where Name is '"+fishList[k]+"'") _ = sql.db.Find(name, &fishInfo, "WHERE Name = ?", fishList[k])
if fishInfo.Number <= 0 { if fishInfo.Number <= 0 {
k++ k++
continue continue
@ -422,7 +412,7 @@ func (sql *fishdb) pickFishFor(uid int64, number int) (fishNames map[string]int,
i = 0 i = 0
} }
if fishInfo.Number <= 0 { if fishInfo.Number <= 0 {
err = sql.db.Del(name, "where Duration = "+strconv.FormatInt(fishInfo.Duration, 10)) err = sql.db.Del(name, "WHERE Duration = ?", fishInfo.Duration)
} else { } else {
err = sql.db.Insert(name, &fishInfo) err = sql.db.Insert(name, &fishInfo)
} }
@ -477,13 +467,13 @@ func (sql *fishdb) getUserThingInfo(uid int64, thing string) (thingInfos []artic
if count == 0 { if count == 0 {
return return
} }
if !sql.db.CanFind(name, "where Name = '"+thing+"'") { if !sql.db.CanFind(name, "WHERE Name = ?", thing) {
return return
} }
err = sql.db.FindFor(name, &userInfo, "where Name = '"+thing+"'", func() error { err = sql.db.FindFor(name, &userInfo, "WHERE Name = ?", func() error {
thingInfos = append(thingInfos, userInfo) thingInfos = append(thingInfos, userInfo)
return nil return nil
}) }, thing)
return return
} }
@ -497,7 +487,7 @@ func (sql *fishdb) updateUserThingInfo(uid int64, userInfo article) (err error)
return return
} }
if userInfo.Number == 0 { if userInfo.Number == 0 {
return sql.db.Del(name, "where Duration = "+strconv.FormatInt(userInfo.Duration, 10)) return sql.db.Del(name, "WHERE Duration = ?", userInfo.Duration)
} }
return sql.db.Insert(name, &userInfo) return sql.db.Insert(name, &userInfo)
} }
@ -519,14 +509,14 @@ func (sql *fishdb) getNumberFor(uid int64, thing string) (number int, err error)
if count == 0 { if count == 0 {
return return
} }
if !sql.db.CanFind(name, "where Name glob '*"+thing+"*'") { if !sql.db.CanFind(name, "WHERE Name glob ?", "*"+thing+"*") {
return return
} }
info := article{} info := article{}
err = sql.db.FindFor(name, &info, "where Name glob '*"+thing+"*'", func() error { err = sql.db.FindFor(name, &info, "WHERE Name glob ?", func() error {
number += info.Number number += info.Number
return nil return nil
}) }, "*"+thing+"*")
return return
} }
@ -540,13 +530,13 @@ func (sql *fishdb) getUserTypeInfo(uid int64, thingType string) (thingInfos []ar
if err != nil { if err != nil {
return return
} }
if !sql.db.CanFind(name, "where Type = '"+thingType+"'") { if !sql.db.CanFind(name, "WHERE Type = ?", thingType) {
return return
} }
err = sql.db.FindFor(name, &userInfo, "where Type = '"+thingType+"'", func() error { err = sql.db.FindFor(name, &userInfo, "WHERE Type = ?", func() error {
thingInfos = append(thingInfos, userInfo) thingInfos = append(thingInfos, userInfo)
return nil return nil
}) }, thingType)
return return
} }
@ -567,7 +557,7 @@ func (sql *fishdb) refreshStroeInfo() (ok bool, err error) {
return false, err return false, err
} }
lastTime := storeDiscount{} lastTime := storeDiscount{}
_ = sql.db.Find("stroeDiscount", &lastTime, "where Name = 'lastTime'") _ = sql.db.Find("stroeDiscount", &lastTime, "WHERE Name = 'lastTime'")
refresh := false refresh := false
timeNow := time.Now().Day() timeNow := time.Now().Day()
if timeNow != lastTime.Discount { if timeNow != lastTime.Discount {
@ -591,17 +581,17 @@ func (sql *fishdb) refreshStroeInfo() (ok bool, err error) {
Discount: thingDiscount, Discount: thingDiscount,
} }
thingInfo := store{} thingInfo := store{}
_ = sql.db.Find("store", &thingInfo, "where Name = '"+name+"'") _ = sql.db.Find("store", &thingInfo, "WHERE Name = ?", name)
if thingInfo.Number > 150 { if thingInfo.Number > 150 {
// 通货膨胀 // 控制价格浮动区间: -10%到10%
thing.Discount = (1000 - 5*(thingInfo.Number-150)) / 10 thing.Discount = 90 + rand.Intn(20)
} }
err = sql.db.Insert("stroeDiscount", &thing) err = sql.db.Insert("stroeDiscount", &thing)
if err != nil { if err != nil {
return return
} }
default: default:
_ = sql.db.Find("stroeDiscount", &thing, "where Name = '"+name+"'") _ = sql.db.Find("stroeDiscount", &thing, "WHERE Name = ?", name)
} }
if thing.Discount != 0 { if thing.Discount != 0 {
discountList[name] = thing.Discount discountList[name] = thing.Discount
@ -610,15 +600,15 @@ func (sql *fishdb) refreshStroeInfo() (ok bool, err error) {
} }
} }
thing := store{} thing := store{}
oldThing := []store{} var oldThing []store
_ = sql.db.FindFor("stroeDiscount", &thing, "where type = 'pole'", func() error { _ = sql.db.FindFor("stroeDiscount", &thing, "WHERE type = 'pole'", func() error {
if time.Since(time.Unix(thing.Duration, 0)) > 24 { if time.Since(time.Unix(thing.Duration, 0)) > 24 {
oldThing = append(oldThing, thing) oldThing = append(oldThing, thing)
} }
return nil return nil
}) })
for _, info := range oldThing { for _, info := range oldThing {
_ = sql.db.Del("stroeDiscount", "where Duration = "+strconv.FormatInt(info.Duration, 10)) _ = sql.db.Del("stroeDiscount", "WHERE Duration = ?", info.Duration)
} }
if refresh { if refresh {
// 每天调控1种鱼 // 每天调控1种鱼
@ -629,21 +619,25 @@ func (sql *fishdb) refreshStroeInfo() (ok bool, err error) {
Type: "fish", Type: "fish",
Price: priceList[fish] * discountList[fish] / 100, Price: priceList[fish] * discountList[fish] / 100,
} }
_ = sql.db.Find("store", &thingInfo, "where Name = '"+fish+"'") _ = sql.db.Find("store", &thingInfo, "WHERE Name = ?", fish)
thingInfo.Number += (100 - discountList[fish]) thingInfo.Number += 100 - discountList[fish]
if thingInfo.Number < 1 { if thingInfo.Number < 1 {
thingInfo.Number = 100 thingInfo.Number = 100
} }
_ = sql.db.Insert("store", &thingInfo) _ = sql.db.Insert("store", &thingInfo)
// 每天上架20本净化书 // 每天上架1木竿
thingInfo = store{ thingInfo = store{
Duration: time.Now().Unix(), Duration: time.Now().Unix(),
Name: "净化书", Name: "初始木竿",
Type: "article", Type: "pole",
Price: priceList["净化书"] * discountList["净化书"] / 100, Price: priceList["木竿"] + priceList["木竿"]*discountList["木竿"]/100,
Other: "30/0/0/0",
}
_ = sql.db.Find("store", &thingInfo, "WHERE Name = '初始木竿'")
thingInfo.Number++
if thingInfo.Number > 5 {
thingInfo.Number = 1
} }
_ = sql.db.Find("store", &thingInfo, "where Name = '净化书'")
thingInfo.Number = 20
_ = sql.db.Insert("store", &thingInfo) _ = sql.db.Insert("store", &thingInfo)
} }
return true, nil return true, nil
@ -688,13 +682,13 @@ func (sql *fishdb) getStoreThingInfo(thing string) (thingInfos []store, err erro
if count == 0 { if count == 0 {
return return
} }
if !sql.db.CanFind("store", "where Name = '"+thing+"'") { if !sql.db.CanFind("store", "WHERE Name = ?", thing) {
return return
} }
err = sql.db.FindFor("store", &thingInfo, "where Name = '"+thing+"'", func() error { err = sql.db.FindFor("store", &thingInfo, "WHERE Name = ?", func() error {
thingInfos = append(thingInfos, thingInfo) thingInfos = append(thingInfos, thingInfo)
return nil return nil
}) }, thing)
return return
} }
@ -713,10 +707,10 @@ func (sql *fishdb) checkStoreFor(thing store, number int) (ok bool, err error) {
if count == 0 { if count == 0 {
return false, nil return false, nil
} }
if !sql.db.CanFind("store", "where Duration = "+strconv.FormatInt(thing.Duration, 10)) { if !sql.db.CanFind("store", "WHERE Duration = ?", thing.Duration) {
return false, nil return false, nil
} }
err = sql.db.Find("store", &thing, "where Duration = "+strconv.FormatInt(thing.Duration, 10)) err = sql.db.Find("store", &thing, "WHERE Duration = ?", thing.Duration)
if err != nil { if err != nil {
return return
} }
@ -735,7 +729,7 @@ func (sql *fishdb) updateStoreInfo(thingInfo store) (err error) {
return return
} }
if thingInfo.Number == 0 { if thingInfo.Number == 0 {
return sql.db.Del("store", "where Duration = "+strconv.FormatInt(thingInfo.Duration, 10)) return sql.db.Del("store", "WHERE Duration = ?", thingInfo.Duration)
} }
return sql.db.Insert("store", &thingInfo) return sql.db.Insert("store", &thingInfo)
} }
@ -749,7 +743,7 @@ func (sql *fishdb) updateBuyTimeFor(uid int64, add int) (err error) {
if err != nil { if err != nil {
return err return err
} }
_ = sql.db.Find("buff", &userInfo, "where ID = "+strconv.FormatInt(uid, 10)) _ = sql.db.Find("buff", &userInfo, "WHERE ID = ?", uid)
userInfo.BuyTimes += add userInfo.BuyTimes += add
if userInfo.BuyTimes > 20 { if userInfo.BuyTimes > 20 {
userInfo.BuyTimes -= 20 userInfo.BuyTimes -= 20
@ -768,7 +762,7 @@ func (sql *fishdb) useCouponAt(uid int64, times int) (int, error) {
if err != nil { if err != nil {
return useTimes, err return useTimes, err
} }
_ = sql.db.Find("buff", &userInfo, "where ID = "+strconv.FormatInt(uid, 10)) _ = sql.db.Find("buff", &userInfo, "WHERE ID = ?", uid)
if userInfo.Coupon > 0 { if userInfo.Coupon > 0 {
useTimes = math.Min(userInfo.Coupon, times) useTimes = math.Min(userInfo.Coupon, times)
userInfo.Coupon -= useTimes userInfo.Coupon -= useTimes
@ -776,64 +770,66 @@ func (sql *fishdb) useCouponAt(uid int64, times int) (int, error) {
return useTimes, sql.db.Insert("buff", &userInfo) return useTimes, sql.db.Insert("buff", &userInfo)
} }
// 检测卖鱼竿上限 // 买卖上限检测
func (sql *fishdb) checkCanSalesFor(uid int64, sales bool) (int, error) { func (sql *fishdb) checkCanSalesFor(uid int64, saleName string, salesNum int) (int, error) {
residue := 0
sql.Lock() sql.Lock()
defer sql.Unlock() defer sql.Unlock()
userInfo := buffInfo{ID: uid} userInfo := buffInfo{ID: uid}
err := sql.db.Create("buff", &userInfo) err := sql.db.Create("buff", &userInfo)
if err != nil { if err != nil {
return residue, err return salesNum, err
} }
_ = sql.db.Find("buff", &userInfo, "where ID = "+strconv.FormatInt(uid, 10)) _ = sql.db.Find("buff", &userInfo, "WHERE ID = ?", uid)
if time.Now().Day() != time.Unix(userInfo.Duration, 0).Day() { if time.Now().Day() != time.Unix(userInfo.Duration, 0).Day() {
userInfo.Duration = time.Now().Unix() userInfo.Duration = time.Now().Unix()
userInfo.SalesPole = 0 userInfo.SalesPole = 0
userInfo.BuyTing = 0 userInfo.BuyTing = 0
err := sql.db.Insert("buff", &userInfo)
if err != nil {
return salesNum, err
} }
if sales && userInfo.SalesPole < 5 {
residue = 5 - userInfo.SalesPole
userInfo.SalesPole++
} else if userInfo.BuyTing < 15 {
residue = 15 - userInfo.BuyTing
userInfo.BuyTing++
} }
return residue, sql.db.Insert("buff", &userInfo) if strings.Contains(saleName, "竿") {
if userInfo.SalesPole >= 10 {
salesNum = -1
}
} else if !checkIsWaste(saleName) {
maxSales := 30 - userInfo.BuyTing
if maxSales < 0 {
salesNum = 0
}
if salesNum > maxSales {
salesNum = maxSales
}
}
return salesNum, err
} }
// 检测物品是否是鱼 // 更新买卖鱼上限假定sales变量已经在 checkCanSalesFor 进行了防护
func checkIsFish(thing string) bool { func (sql *fishdb) updateCanSalesFor(uid int64, saleName string, sales int) error {
for _, v := range fishList { sql.Lock()
defer sql.Unlock()
userInfo := buffInfo{ID: uid}
err := sql.db.Create("buff", &userInfo)
if err != nil {
return err
}
_ = sql.db.Find("buff", &userInfo, "WHERE ID = ?", uid)
if strings.Contains(saleName, "竿") {
userInfo.SalesPole++
} else if !checkIsWaste(saleName) {
userInfo.BuyTing += sales
}
return sql.db.Insert("buff", &userInfo)
}
// 检测物品是否是垃圾
func checkIsWaste(thing string) bool {
for _, v := range wasteList {
if v == thing { if v == thing {
return true return true
} }
} }
return false return false
} }
// 检测买卖鱼上限
func (sql *fishdb) checkCanSalesFishFor(uid int64, sales int) (int, error) {
residue := 0
sql.Lock()
defer sql.Unlock()
userInfo := buffInfo{ID: uid}
err := sql.db.Create("buff", &userInfo)
if err != nil {
return residue, err
}
_ = sql.db.Find("buff", &userInfo, "where ID = "+strconv.FormatInt(uid, 10))
if time.Now().Day() != time.Unix(userInfo.Duration, 0).Day() {
userInfo.Duration = time.Now().Unix()
userInfo.SalesFish = 0
}
maxSales := 100 - userInfo.SalesFish
if maxSales < 0 {
maxSales = 0
}
if sales > maxSales {
sales = maxSales
}
userInfo.SalesFish += sales
return sales, sql.db.Insert("buff", &userInfo)
}

View File

@ -42,9 +42,9 @@ func init() {
} }
ctx.SendChain(message.ImageBytes(pic)) ctx.SendChain(message.ImageBytes(pic))
}) })
engine.OnRegex(`^消除绑定诅咒(\d*)$`, getdb).SetBlock(true).Limit(ctxext.LimitByUser).Handle(func(ctx *zero.Ctx) { engine.OnRegex(`^消除(绑定|宝藏)诅咒(\d*)$`, getdb).SetBlock(true).Limit(ctxext.LimitByUser).Handle(func(ctx *zero.Ctx) {
uid := ctx.Event.UserID uid := ctx.Event.UserID
number, _ := strconv.Atoi(ctx.State["regex_matched"].([]string)[1]) number, _ := strconv.Atoi(ctx.State["regex_matched"].([]string)[2])
if number == 0 { if number == 0 {
number = 1 number = 1
} }
@ -171,6 +171,31 @@ func init() {
msg = append(msg, message.Text("-----------")) msg = append(msg, message.Text("-----------"))
ctx.Send(msg) ctx.Send(msg)
}) })
engine.OnFullMatch("查看钓鱼规则", getdb).SetBlock(true).Limit(ctxext.LimitByUser).Handle(func(ctx *zero.Ctx) {
msg := "一款钓鱼模拟器\n----------指令----------\n" +
"- 钓鱼看板/钓鱼商店\n- 购买xxx\n- 购买xxx [数量]\n- 出售xxx\n- 出售xxx [数量]\n- 出售所有垃圾\n" +
"- 钓鱼背包\n- 装备[xx竿|三叉戟|美西螈]\n- 附魔[诱钓|海之眷顾]\n- 修复鱼竿\n- 合成[xx竿|三叉戟]\n- 消除[绑定|宝藏]诅咒\n- 消除[绑定|宝藏]诅咒 [数量]\n" +
"- 进行钓鱼\n- 进行n次钓鱼\n- " +
"当前装备概率明细\n" +
"规则V" + version + ":\n" +
"1.每日的商店价格是波动的!!如何最大化收益自己考虑一下喔\n" +
"2.装备信息:\n-> 木竿 : 耐久上限:30 均价:100 上钩概率:0.7%\n-> 铁竿 : 耐久上限:50 均价:300 上钩概率:0.2%\n-> 金竿 : 耐久上限:70 均价700 上钩概率:0.06%\n" +
"-> 钻石竿 : 耐久上限:100 均价1500 上钩概率:0.03%\n-> 下界合金竿 : 耐久上限:150 均价3100 上钩概率:0.01%\n-> 三叉戟 : 可使1次钓鱼视为3次钓鱼. 耐久上限:300 均价4000 只能合成、修复和交易\n" +
"3.附魔书信息:\n-> 诱钓 : 减少上钩时间. 均价:1000, 上钩概率:0.25%\n-> 海之眷顾 : 增加宝藏上钩概率. 均价:2500, 上钩概率:0.10%\n" +
"4.稀有物品:\n-> 唱片 : 出售物品时使用该物品使价格翻倍. 均价:3000, 上钩概率:0.01%\n" +
"-> 美西螈 : 可装备,获得隐形[钓鱼佬]buff,并让钓到除鱼竿和美西螈外的物品数量变成5,无耐久上限.不可修复/附魔,每次钓鱼消耗3条鱼. 均价:3000, 上钩概率:0.01%\n" +
"-> 海豚 : 使空竿概率变成垃圾概率. 均价:1000, 上钩概率:0.19%\n" +
"-> 宝藏诅咒 : 无法交易,每一层就会增加购买时10%价格和减少出售时10%价格(超过10层会变为倒贴钱). 上钩概率:0.25%\n-> 净化书 : 用于消除宝藏诅咒. 均价:5000, 上钩概率:0.19%\n" +
"5.鱼类信息:\n-> 鳕鱼 : 均价:10 上钩概率:0.69%\n-> 鲑鱼 : 均价:50 上钩概率:0.2%\n-> 热带鱼 : 均价:100 上钩概率:0.06%\n-> 河豚 : 均价:300 上钩概率:0.03%\n-> 鹦鹉螺 : 均价:500 上钩概率:0.01%\n-> 墨鱼 : 均价:500 上钩概率:0.01%\n" +
"6.垃圾:\n-> 均价:10 上钩概率:30%\n" +
"7.物品BUFF:\n-> 钓鱼佬 : 当背包名字含有'鱼'的物品数量超过100时激活,钓到物品概率提高至90%\n-> 修复大师 : 当背包鱼竿数量超过10时激活,修复物品时耐久百分百继承\n" +
"8.合成:\n-> 铁竿 : 3x木竿\n-> 金竿 : 3x铁竿\n-> 钻石竿 : 3x金竿\n-> 下界合金竿 : 3x钻石竿\n-> 三叉戟 : 3x下界合金竿\n注:合成成功率90%(包括梭哈),合成鱼竿的附魔等级=(附魔等级合/合成鱼竿数量)\n" +
"9.杂项:\n-> 无装备的情况下,每人最多可以购买3次100块钱的鱼竿,商店每日会上架1木竿\n-> 默认状态钓鱼上钩概率为60%(理论值!!!)\n-> 附魔的鱼竿会因附魔变得昂贵,每个附魔最高3级\n-> 三叉戟不算鱼竿,修复时可直接满耐久\n" +
"-> 鱼竿数量大于50的不能买东西;\n 鱼竿数量大于30的不能钓鱼;\n 每购/售10次鱼竿获得1层宝藏诅咒;\n 每购买20次物品将获得3次价格减半福利;\n 每钓鱼75次获得1本净化书;\n" +
" 每天可交易鱼竿10个购买物品30件垃圾除外."
ctx.Send(msg)
})
} }
func drawPackImage(uid int64, equipInfo equip, articles []article) (imagePicByte []byte, err error) { func drawPackImage(uid int64, equipInfo equip, articles []article) (imagePicByte []byte, err error) {

View File

@ -70,33 +70,24 @@ func init() {
engine.OnRegex(`^出售(`+strings.Join(thingList, "|")+`)\s*(\d*)$`, getdb, refreshFish).SetBlock(true).Limit(limitSet).Handle(func(ctx *zero.Ctx) { engine.OnRegex(`^出售(`+strings.Join(thingList, "|")+`)\s*(\d*)$`, getdb, refreshFish).SetBlock(true).Limit(limitSet).Handle(func(ctx *zero.Ctx) {
uid := ctx.Event.UserID uid := ctx.Event.UserID
thingName := ctx.State["regex_matched"].([]string)[1] thingName := ctx.State["regex_matched"].([]string)[1]
if strings.Contains(thingName, "竿") {
times, err := dbdata.checkCanSalesFor(uid, true)
if err != nil {
ctx.SendChain(message.Text("[ERROR at store.go.75]:", err))
return
}
if times <= 0 {
ctx.SendChain(message.Text("出售次数已达到上限,明天再来售卖吧"))
return
}
}
number, _ := strconv.Atoi(ctx.State["regex_matched"].([]string)[2]) number, _ := strconv.Atoi(ctx.State["regex_matched"].([]string)[2])
if number == 0 || strings.Contains(thingName, "竿") { if number == 0 || strings.Contains(thingName, "竿") {
number = 1 number = 1
} }
if checkIsFish(thingName) {
residue, err := dbdata.checkCanSalesFishFor(uid, number) // 检测物品交易次数
if strings.Contains(thingName, "竿") {
number, err := dbdata.checkCanSalesFor(uid, thingName, number)
if err != nil { if err != nil {
ctx.SendChain(message.Text("[ERROR]:", err)) ctx.SendChain(message.Text("[ERROR,查询购买资质失败]:", err))
return return
} }
if residue <= 0 { if number <= 0 {
ctx.SendChain(message.Text("今天你已经超出了鱼交易数量上限,明天再来买鱼吧")) ctx.SendChain(message.Text("一天只能交易10把鱼竿,明天再来售卖吧"))
return return
} }
number = residue
} }
articles, err := dbdata.getUserThingInfo(uid, thingName) articles, err := dbdata.getUserThingInfo(uid, thingName)
if err != nil { if err != nil {
ctx.SendChain(message.Text("[ERROR at store.go.5]:", err)) ctx.SendChain(message.Text("[ERROR at store.go.5]:", err))
@ -318,7 +309,13 @@ func init() {
logrus.Warnln(err) logrus.Warnln(err)
} }
} }
ctx.Send(message.ReplyWithMessage(ctx.Event.MessageID, message.Text("出售成功,你赚到了", pice*number, msg))) // 更新交易限制
err = dbdata.updateCanSalesFor(uid, thingName, number)
if err != nil {
ctx.SendChain(message.Text("[ERROR,记录鱼类交易数量失败,此次交易不记录]:", err))
}
ctx.Send(message.ReplyWithMessage(ctx.Event.MessageID, message.Text("成功出售", thingName, "", number, "个", ",你赚到了", pice*number, msg)))
}) })
engine.OnRegex(`^出售所有垃圾`, getdb, refreshFish).SetBlock(true).Limit(limitSet).Handle(func(ctx *zero.Ctx) { engine.OnRegex(`^出售所有垃圾`, getdb, refreshFish).SetBlock(true).Limit(limitSet).Handle(func(ctx *zero.Ctx) {
uid := ctx.Event.UserID uid := ctx.Event.UserID
@ -396,8 +393,14 @@ func init() {
} }
ctx.Send(message.ReplyWithMessage(ctx.Event.MessageID, message.Text("出售成功,你赚到了", pice, msg))) ctx.Send(message.ReplyWithMessage(ctx.Event.MessageID, message.Text("出售成功,你赚到了", pice, msg)))
}) })
engine.OnRegex(`^购买(`+strings.Join(thingList, "|")+`)\s*(\d*)$`, getdb, refreshFish).SetBlock(true).Limit(limitSet).Handle(func(ctx *zero.Ctx) { engine.OnRegex(`^购买(`+strings.Join(thingList, "|")+`|初始木竿)\s*(\d*)$`, getdb, refreshFish).SetBlock(true).Limit(limitSet).Handle(func(ctx *zero.Ctx) {
uid := ctx.Event.UserID uid := ctx.Event.UserID
thingName := ctx.State["regex_matched"].([]string)[1]
number, _ := strconv.Atoi(ctx.State["regex_matched"].([]string)[2])
if number == 0 || strings.Contains(thingName, "竿") {
number = 1
}
numberOfPole, err := dbdata.getNumberFor(uid, "竿") numberOfPole, err := dbdata.getNumberFor(uid, "竿")
if err != nil { if err != nil {
ctx.SendChain(message.Text("[ERROR at store.go.9.3]:", err)) ctx.SendChain(message.Text("[ERROR at store.go.9.3]:", err))
@ -407,32 +410,24 @@ func init() {
ctx.SendChain(message.Text("你有", numberOfPole, "支鱼竿,大于50支的玩家不允许购买东西")) ctx.SendChain(message.Text("你有", numberOfPole, "支鱼竿,大于50支的玩家不允许购买东西"))
return return
} }
buytimes, err := dbdata.checkCanSalesFor(uid, false)
// 检测物品交易次数
number, err = dbdata.checkCanSalesFor(uid, thingName, number)
if err != nil { if err != nil {
ctx.SendChain(message.Text("[ERROR at store.go.75]:", err)) ctx.SendChain(message.Text("[ERROR at store.go.75]:", err))
return return
} }
if buytimes <= 0 { if number <= 0 {
ctx.SendChain(message.Text("购买次数已达到上限,明天再来购买吧")) var msg string
if strings.Contains(thingName, "竿") {
msg = "一天只能交易10把鱼竿,明天再来购买吧"
} else {
msg = "一天只能购买30次物品明天再来吧~"
}
ctx.SendChain(message.Text(msg))
return return
} }
thingName := ctx.State["regex_matched"].([]string)[1]
number, _ := strconv.Atoi(ctx.State["regex_matched"].([]string)[2])
if number == 0 {
number = 1
}
if checkIsFish(thingName) {
residue, err := dbdata.checkCanSalesFishFor(uid, number)
if err != nil {
ctx.SendChain(message.Text("[ERROR]:", err))
return
}
if residue <= 0 {
ctx.SendChain(message.Text("今天你已经超出了鱼交易数量上限,明天再来买鱼吧"))
return
}
number = residue
}
thingInfos, err := dbdata.getStoreThingInfo(thingName) thingInfos, err := dbdata.getStoreThingInfo(thingName)
if err != nil { if err != nil {
ctx.SendChain(message.Text("[ERROR at store.go.11]:", err)) ctx.SendChain(message.Text("[ERROR at store.go.11]:", err))
@ -477,6 +472,9 @@ func init() {
thingPice := (priceList[info.Name] - (durationList[info.Name] - durable) - maintenance*2 + thingPice := (priceList[info.Name] - (durationList[info.Name] - durable) - maintenance*2 +
induceLevel*600*discountList["诱钓"]/100 + induceLevel*600*discountList["诱钓"]/100 +
favorLevel*1800*discountList["海之眷顾"]/100) * discountList[info.Name] / 100 favorLevel*1800*discountList["海之眷顾"]/100) * discountList[info.Name] / 100
if strings.Contains(thingName, "初始木竿") {
thingPice = priceList["木竿"] + priceList["木竿"]*discountList["木竿"]/100
}
pice = append(pice, thingPice) pice = append(pice, thingPice)
} else { } else {
thingPice := priceList[info.Name] * discountList[info.Name] / 100 thingPice := priceList[info.Name] * discountList[info.Name] / 100
@ -623,6 +621,9 @@ func init() {
Number: 1, Number: 1,
Other: thing.Other, Other: thing.Other,
} }
if thingName == "初始木竿" {
newCommodity.Name = "木竿"
}
} else { } else {
things, err1 := dbdata.getUserThingInfo(uid, thingName) things, err1 := dbdata.getUserThingInfo(uid, thingName)
if err1 != nil { if err1 != nil {
@ -650,6 +651,11 @@ func init() {
logrus.Warnln(err) logrus.Warnln(err)
} }
} }
// 更新交易限制
err = dbdata.updateCanSalesFor(uid, thingName, number)
if err != nil {
ctx.SendChain(message.Text("[ERROR,记录鱼类交易数量失败,此次交易不记录]:", err))
}
ctx.Send(message.ReplyWithMessage(ctx.Event.MessageID, message.Text("你用", price, "购买了", number, thingName))) ctx.Send(message.ReplyWithMessage(ctx.Event.MessageID, message.Text("你用", price, "购买了", number, thingName)))
}) })
} }
@ -794,6 +800,9 @@ func drawStroeInfoImage(stroeInfo []store) (picImage image.Image, err error) {
induceLevel, _ := strconv.Atoi(poleInfo[2]) induceLevel, _ := strconv.Atoi(poleInfo[2])
favorLevel, _ := strconv.Atoi(poleInfo[3]) favorLevel, _ := strconv.Atoi(poleInfo[3])
pice = (priceList[info.Name] - (durationList[info.Name] - durable) - maintenance*2 + induceLevel*600 + favorLevel*1800) * discountList[info.Name] / 100 pice = (priceList[info.Name] - (durationList[info.Name] - durable) - maintenance*2 + induceLevel*600 + favorLevel*1800) * discountList[info.Name] / 100
if strings.Contains(name, "初始木竿") {
pice = priceList["木竿"] + priceList["木竿"]*discountList["木竿"]/100
}
} else { } else {
pice = priceList[info.Name] * discountList[info.Name] / 100 pice = priceList[info.Name] * discountList[info.Name] / 100
} }

View File

@ -229,7 +229,7 @@ func init() {
return path.Ext(ctx.Event.File.Name) == ".mid" return path.Ext(ctx.Event.File.Name) == ".mid"
}).SetBlock(false).Limit(ctxext.LimitByGroup). }).SetBlock(false).Limit(ctxext.LimitByGroup).
Handle(func(ctx *zero.Ctx) { Handle(func(ctx *zero.Ctx) {
fileURL := ctx.GetThisGroupFileUrl(ctx.Event.File.BusID, ctx.Event.File.ID) fileURL := ctx.GetThisGroupFileURL(ctx.Event.File.BusID, ctx.Event.File.ID)
data, err := web.GetData(fileURL) data, err := web.GetData(fileURL)
if err != nil { if err != nil {
ctx.SendChain(message.Text("ERROR: ", err)) ctx.SendChain(message.Text("ERROR: ", err))
@ -242,12 +242,12 @@ func init() {
} }
for i := 0; i < int(s.NumTracks()); i++ { for i := 0; i < int(s.NumTracks()); i++ {
midStr := mid2txt(data, i) midStr := mid2txt(data, i)
fileName := strings.ReplaceAll(cachePath+"/"+ctx.Event.File.Name, ".mid", fmt.Sprintf("-%d.txt", i))
err := os.WriteFile(fileName, binary.StringToBytes(midStr), 0666)
if err != nil { if err != nil {
ctx.SendChain(message.Text("ERROR: ", err)) ctx.SendChain(message.Text("ERROR: ", err))
return return
} }
fileName := strings.ReplaceAll(cachePath+"/"+ctx.Event.File.Name, ".mid", fmt.Sprintf("-%d.txt", i))
_ = os.WriteFile(fileName, binary.StringToBytes(midStr), 0666)
ctx.UploadThisGroupFile(file.BOTPATH+"/"+fileName, filepath.Base(fileName), "") ctx.UploadThisGroupFile(file.BOTPATH+"/"+fileName, filepath.Base(fileName), "")
} }
}) })
@ -255,7 +255,7 @@ func init() {
return path.Ext(ctx.Event.File.Name) == ".txt" && strings.Contains(ctx.Event.File.Name, "midi制作") return path.Ext(ctx.Event.File.Name) == ".txt" && strings.Contains(ctx.Event.File.Name, "midi制作")
}).SetBlock(false).Limit(ctxext.LimitByGroup). }).SetBlock(false).Limit(ctxext.LimitByGroup).
Handle(func(ctx *zero.Ctx) { Handle(func(ctx *zero.Ctx) {
fileURL := ctx.GetThisGroupFileUrl(ctx.Event.File.BusID, ctx.Event.File.ID) fileURL := ctx.GetThisGroupFileURL(ctx.Event.File.BusID, ctx.Event.File.ID)
data, err := web.GetData(fileURL) data, err := web.GetData(fileURL)
if err != nil { if err != nil {
ctx.SendChain(message.Text("ERROR: ", err)) ctx.SendChain(message.Text("ERROR: ", err))

View File

@ -0,0 +1,301 @@
// Package minecraftobserver 通过mc服务器地址获取服务器状态信息并绘制图片发送到QQ群
package minecraftobserver
import (
"fmt"
"strings"
"time"
ctrl "github.com/FloatTech/zbpctrl"
"github.com/FloatTech/zbputils/control"
zbpCtxExt "github.com/FloatTech/zbputils/ctxext"
zero "github.com/wdvxdr1123/ZeroBot"
"github.com/wdvxdr1123/ZeroBot/message"
)
const (
name = "minecraftobserver"
)
var (
// 注册插件
engine = control.AutoRegister(&ctrl.Options[*zero.Ctx]{
// 默认不启动
DisableOnDefault: false,
Brief: "Minecraft服务器状态查询/订阅",
// 详细帮助
Help: "- mc服务器状态 [服务器IP/URI]\n" +
"- mc服务器添加订阅 [服务器IP/URI]\n" +
"- mc服务器取消订阅 [服务器IP/URI]\n" +
"- mc服务器订阅拉取 (需要插件定时任务配合使用,全局只需要设置一个)" +
"-----------------------\n" +
"使用job插件设置定时, 例:" +
"记录在\"@every 1m\"触发的指令\n" +
"(机器人回答:您的下一条指令将被记录,在@@every 1m时触发" +
"mc服务器订阅拉取",
// 插件数据存储路径
PrivateDataFolder: name,
}).ApplySingle(zbpCtxExt.DefaultSingle)
)
func init() {
// 状态查询
engine.OnRegex("^[mM][cC]服务器状态 (.+)$").SetBlock(true).Handle(func(ctx *zero.Ctx) {
// 关键词查找
addr := ctx.State["regex_matched"].([]string)[1]
resp, err := getMinecraftServerStatus(addr)
if err != nil {
ctx.Send(message.Text("服务器状态获取失败... 错误信息: ", err))
return
}
status := resp.genServerSubscribeSchema(addr, 0)
textMsg, iconBase64 := status.generateServerStatusMsg()
var msg message.Message
if iconBase64 != "" {
msg = append(msg, message.Image(iconBase64))
}
msg = append(msg, message.Text(textMsg))
if id := ctx.Send(msg); id.ID() == 0 {
// logrus.Errorln(logPrefix + "Send failed")
return
}
})
// 添加订阅
engine.OnRegex(`^[mM][cC]服务器添加订阅\s*(.+)$`, getDB).SetBlock(true).Handle(func(ctx *zero.Ctx) {
// 关键词查找
addr := ctx.State["regex_matched"].([]string)[1]
status, err := getMinecraftServerStatus(addr)
if err != nil {
ctx.Send(message.Text("服务器信息初始化失败,请检查服务器是否可用!\n错误信息: ", err))
return
}
targetID, targetType := warpTargetIDAndType(ctx.Event.GroupID, ctx.Event.UserID)
err = dbInstance.newSubscribe(addr, targetID, targetType)
if err != nil {
ctx.Send(message.Text("订阅添加失败... 错误信息: ", err))
return
}
// 插入数据库(首条,需要更新状态)
err = dbInstance.updateServerStatus(status.genServerSubscribeSchema(addr, 0))
if err != nil {
ctx.Send(message.Text("服务器状态更新失败... 错误信息: ", err))
return
}
if sid := ctx.Send(message.Text(fmt.Sprintf("服务器 %s 订阅添加成功", addr))); sid.ID() == 0 {
// logrus.Errorln(logPrefix + "Send failed")
return
}
// 成功后立即发送一次状态
textMsg, iconBase64 := status.genServerSubscribeSchema(addr, 0).generateServerStatusMsg()
var msg message.Message
if iconBase64 != "" {
msg = append(msg, message.Image(iconBase64))
}
msg = append(msg, message.Text(textMsg))
if id := ctx.Send(msg); id.ID() == 0 {
// logrus.Errorln(logPrefix + "Send failed")
return
}
})
// 删除
engine.OnRegex(`^[mM][cC]服务器取消订阅\s*(.+)$`, getDB).SetBlock(true).Handle(func(ctx *zero.Ctx) {
addr := ctx.State["regex_matched"].([]string)[1]
// 通过群组id和服务器地址获取服务器状态
targetID, targetType := warpTargetIDAndType(ctx.Event.GroupID, ctx.Event.UserID)
err := dbInstance.deleteSubscribe(addr, targetID, targetType)
if err != nil {
ctx.Send(message.Text("取消订阅失败...", fmt.Sprintf("错误信息: %v", err)))
return
}
ctx.Send(message.Text("取消订阅成功"))
})
// 查看当前渠道的所有订阅
engine.OnRegex(`^[mM][cC]服务器订阅列表$`, getDB).SetBlock(true).Handle(func(ctx *zero.Ctx) {
subList, err := dbInstance.getSubscribesByTarget(warpTargetIDAndType(ctx.Event.GroupID, ctx.Event.UserID))
if err != nil {
ctx.Send(message.Text("获取订阅列表失败... 错误信息: ", err))
return
}
if len(subList) == 0 {
ctx.Send(message.Text("当前没有订阅哦"))
return
}
stringBuilder := strings.Builder{}
stringBuilder.WriteString("[订阅列表]\n")
for _, v := range subList {
stringBuilder.WriteString(fmt.Sprintf("服务器地址: %s\n", v.ServerAddr))
}
if sid := ctx.Send(message.Text(stringBuilder.String())); sid.ID() == 0 {
// logrus.Errorln(logPrefix + "Send failed")
return
}
})
// 查看全局订阅情况(仅限管理员私聊可用)
engine.OnRegex(`^[mM][cC]服务器全局订阅列表$`, zero.OnlyPrivate, zero.SuperUserPermission, getDB).SetBlock(true).Handle(func(ctx *zero.Ctx) {
subList, err := dbInstance.getAllSubscribes()
if err != nil {
ctx.Send(message.Text("获取全局订阅列表失败... 错误信息: ", err))
return
}
if len(subList) == 0 {
ctx.Send(message.Text("当前一个订阅都没有哦"))
return
}
userID := ctx.Event.UserID
userName := ctx.CardOrNickName(userID)
msg := make(message.Message, 0)
// 按照群组or用户分组来定
groupSubMap := make(map[int64][]serverSubscribe)
userSubMap := make(map[int64][]serverSubscribe)
for _, v := range subList {
switch v.TargetType {
case targetTypeGroup:
groupSubMap[v.TargetID] = append(groupSubMap[v.TargetID], v)
case targetTypeUser:
userSubMap[v.TargetID] = append(userSubMap[v.TargetID], v)
default:
}
}
// 群
for k, v := range groupSubMap {
stringBuilder := strings.Builder{}
stringBuilder.WriteString(fmt.Sprintf("[群 %d]存在以下订阅:\n", k))
for _, sub := range v {
stringBuilder.WriteString(fmt.Sprintf("服务器地址: %s\n", sub.ServerAddr))
}
msg = append(msg, message.CustomNode(userName, userID, stringBuilder.String()))
}
// 个人
for k, v := range userSubMap {
stringBuilder := strings.Builder{}
stringBuilder.WriteString(fmt.Sprintf("[用户 %d]存在以下订阅:\n", k))
for _, sub := range v {
stringBuilder.WriteString(fmt.Sprintf("服务器地址: %s\n", sub.ServerAddr))
}
msg = append(msg, message.CustomNode(userName, userID, stringBuilder.String()))
}
// 合并发送
ctx.SendPrivateForwardMessage(ctx.Event.UserID, msg)
})
// 状态变更通知,全局触发,逐个服务器检查,检查到变更则逐个发送通知
engine.OnRegex(`^[mM][cC]服务器订阅拉取$`, getDB).SetBlock(true).Handle(func(ctx *zero.Ctx) {
serverList, err := dbInstance.getAllSubscribes()
if err != nil {
su := zero.BotConfig.SuperUsers[0]
// 如果订阅列表获取失败,通知管理员
ctx.SendPrivateMessage(su, message.Text(logPrefix, "获取订阅列表失败..."))
return
}
// logrus.Debugln(logPrefix+"global get ", len(serverList), " subscribe(s)")
serverMap := make(map[string][]serverSubscribe)
for _, v := range serverList {
serverMap[v.ServerAddr] = append(serverMap[v.ServerAddr], v)
}
changedCount := 0
for subAddr, oneServerSubList := range serverMap {
// 查询当前存储的状态
storedStatus, sErr := dbInstance.getServerStatus(subAddr)
if sErr != nil {
// logrus.Errorln(logPrefix+fmt.Sprintf("getServerStatus ServerAddr(%s) error: ", subAddr), sErr)
continue
}
isChanged, changedNotifyMsg, sErr := singleServerScan(storedStatus)
if sErr != nil {
// logrus.Errorln(logPrefix+"singleServerScan error: ", sErr)
continue
}
if !isChanged {
continue
}
changedCount++
// 发送变化信息
for _, subInfo := range oneServerSubList {
time.Sleep(100 * time.Millisecond)
if subInfo.TargetType == targetTypeUser {
ctx.SendPrivateMessage(subInfo.TargetID, changedNotifyMsg)
} else if subInfo.TargetType == targetTypeGroup {
m, ok := control.Lookup(name)
if !ok {
continue
}
if !m.IsEnabledIn(subInfo.TargetID) {
continue
}
ctx.SendGroupMessage(subInfo.TargetID, changedNotifyMsg)
}
}
}
})
}
// singleServerScan 单个服务器状态扫描
func singleServerScan(oldSubStatus *serverStatus) (changed bool, notifyMsg message.Message, err error) {
notifyMsg = make(message.Message, 0)
newSubStatus := &serverStatus{}
// 获取服务器状态 & 检查是否需要更新
rawServerStatus, err := getMinecraftServerStatus(oldSubStatus.ServerAddr)
if err != nil {
// logrus.Warnln(logPrefix+"getMinecraftServerStatus error: ", err)
err = nil
// 计数器没有超限,增加计数器并跳过
if cnt, ts := addPingServerUnreachableCounter(oldSubStatus.ServerAddr, time.Now()); cnt < pingServerUnreachableCounterThreshold &&
time.Since(ts) < pingServerUnreachableCounterTimeThreshold {
// logrus.Warnln(logPrefix+"server ", oldSubStatus.ServerAddr, " unreachable, counter: ", cnt, " ts:", ts)
return
}
// 不可达计数器已经超限,则更新服务器状态
// 深拷贝设置PingDelay为不可达
newSubStatus = oldSubStatus.deepCopy()
newSubStatus.PingDelay = pingDelayUnreachable
} else {
newSubStatus = rawServerStatus.genServerSubscribeSchema(oldSubStatus.ServerAddr, oldSubStatus.ID)
}
if newSubStatus == nil {
// logrus.Errorln(logPrefix + "newSubStatus is nil")
return
}
// 检查是否有订阅信息变化
if oldSubStatus.isServerStatusSpecChanged(newSubStatus) {
// logrus.Warnf(logPrefix+"server subscribe spec changed: (%+v) -> (%+v)", oldSubStatus, newSubStatus)
changed = true
// 更新数据库
err = dbInstance.updateServerStatus(newSubStatus)
if err != nil {
// logrus.Errorln(logPrefix+"updateServerSubscribeStatus error: ", err)
return
}
// 纯文本信息
notifyMsg = append(notifyMsg, message.Text(formatSubStatusChangeText(oldSubStatus, newSubStatus)))
// 如果有图标变更
if oldSubStatus.FaviconMD5 != newSubStatus.FaviconMD5 {
// 有图标变更
notifyMsg = append(notifyMsg, message.Text("\n-----[图标变更]-----\n"))
// 旧图标
notifyMsg = append(notifyMsg, message.Text("[旧]\n"))
if oldSubStatus.FaviconRaw != "" {
notifyMsg = append(notifyMsg, message.Image(oldSubStatus.FaviconRaw.toBase64String()))
} else {
notifyMsg = append(notifyMsg, message.Text("(空)\n"))
}
// 新图标
notifyMsg = append(notifyMsg, message.Text("[新]\n"))
if newSubStatus.FaviconRaw != "" {
notifyMsg = append(notifyMsg, message.Image(newSubStatus.FaviconRaw.toBase64String()))
} else {
notifyMsg = append(notifyMsg, message.Text("(空)\n"))
}
}
notifyMsg = append(notifyMsg, message.Text("\n-------最新状态-------\n"))
// 服务状态
textMsg, iconBase64 := newSubStatus.generateServerStatusMsg()
if iconBase64 != "" {
notifyMsg = append(notifyMsg, message.Image(iconBase64))
}
notifyMsg = append(notifyMsg, message.Text(textMsg))
}
// 逻辑到达这里,说明状态已经变更 or 无变更且服务器可达,重置不可达计数器
resetPingServerUnreachableCounter(oldSubStatus.ServerAddr)
return
}

View File

@ -0,0 +1,127 @@
package minecraftobserver
import (
"fmt"
"github.com/wdvxdr1123/ZeroBot/message"
"testing"
)
func Test_singleServerScan(t *testing.T) {
initErr := initializeDB("data/minecraftobserver/" + dbPath)
if initErr != nil {
t.Fatalf("initializeDB() error = %v", initErr)
}
if dbInstance == nil {
t.Fatalf("initializeDB() got = %v, want not nil", dbInstance)
}
t.Run("状态变更", func(t *testing.T) {
cleanTestData(t)
newSS1 := &serverStatus{
ServerAddr: "cn.nekoland.top",
Description: "测试服务器",
Players: "1/20",
Version: "1.16.5",
FaviconMD5: "",
}
err := dbInstance.updateServerStatus(newSS1)
if err != nil {
t.Fatalf("upsertServerStatus() error = %v", err)
}
err = dbInstance.newSubscribe("cn.nekoland.top", 123456, 1)
if err != nil {
t.Fatalf("getServerSubscribeByTargetGroupAndAddr() error = %v", err)
}
changed, msg, err := singleServerScan(newSS1)
if err != nil {
t.Fatalf("singleServerScan() error = %v", err)
}
if !changed {
t.Fatalf("singleServerScan() got = %v, want true", changed)
}
if len(msg) == 0 {
t.Fatalf("singleServerScan() got = %v, want not empty", msg)
}
fmt.Printf("msg: %v\n", msg)
})
t.Run("可达 -> 不可达", func(t *testing.T) {
cleanTestData(t)
newSS1 := &serverStatus{
ServerAddr: "dx.123213213123123.net",
Description: "测试服务器",
Players: "1/20",
Version: "1.16.5",
FaviconMD5: "",
PingDelay: 123,
}
err := dbInstance.updateServerStatus(newSS1)
if err != nil {
t.Fatalf("upsertServerStatus() error = %v", err)
}
err = dbInstance.newSubscribe("dx.123213213123123.net", 123456, 1)
if err != nil {
t.Fatalf("getServerSubscribeByTargetGroupAndAddr() error = %v", err)
}
var msg message.Message
changed, _, err := singleServerScan(newSS1)
if err != nil {
t.Fatalf("singleServerScan() error = %v", err)
}
if changed {
t.Fatalf("singleServerScan() got = %v, want false", changed)
}
// 第二次
changed, _, err = singleServerScan(newSS1)
if err != nil {
t.Fatalf("singleServerScan() error = %v", err)
}
if changed {
t.Fatalf("singleServerScan() got = %v, want false", changed)
}
// 第三次
changed, msg, err = singleServerScan(newSS1)
if err != nil {
t.Fatalf("singleServerScan() error = %v", err)
}
if !changed {
t.Fatalf("singleServerScan() got = %v, want true", changed)
}
if len(msg) == 0 {
t.Fatalf("singleServerScan() got = %v, want not empty", msg)
}
fmt.Printf("msg: %v\n", msg)
})
t.Run("不可达 -> 可达", func(t *testing.T) {
cleanTestData(t)
newSS1 := &serverStatus{
ServerAddr: "cn.nekoland.top",
Description: "测试服务器",
Players: "1/20",
Version: "1.16.5",
FaviconMD5: "",
PingDelay: pingDelayUnreachable,
}
err := dbInstance.updateServerStatus(newSS1)
if err != nil {
t.Fatalf("upsertServerStatus() error = %v", err)
}
err = dbInstance.newSubscribe("cn.nekoland.top", 123456, 1)
if err != nil {
t.Fatalf("newSubscribe() error = %v", err)
}
changed, msg, err := singleServerScan(newSS1)
if err != nil {
t.Fatalf("singleServerScan() error = %v", err)
}
if !changed {
t.Fatalf("singleServerScan() got = %v, want true", changed)
}
if len(msg) == 0 {
t.Fatalf("singleServerScan() got = %v, want not empty", msg)
}
fmt.Printf("msg: %v\n", msg)
})
}

View File

@ -0,0 +1,253 @@
package minecraftobserver
import (
"crypto/md5"
"encoding/hex"
"fmt"
"strings"
"time"
"github.com/Tnze/go-mc/chat"
"github.com/google/uuid"
"github.com/wdvxdr1123/ZeroBot/utils/helper"
)
// ====================
// DB Schema
// serverStatus 服务器状态
type serverStatus struct {
// ID 主键
ID int64 `json:"id" gorm:"column:id;primary_key:pk_id;auto_increment;default:0"`
// 服务器地址
ServerAddr string `json:"server_addr" gorm:"column:server_addr;default:'';unique_index:udx_server_addr"`
// 服务器描述
Description string `json:"description" gorm:"column:description;default:null;type:CLOB"`
// 在线玩家
Players string `json:"players" gorm:"column:players;default:''"`
// 版本
Version string `json:"version" gorm:"column:version;default:''"`
// FaviconMD5 Favicon MD5
FaviconMD5 string `json:"favicon_md5" gorm:"column:favicon_md5;default:''"`
// FaviconRaw 原始数据
FaviconRaw icon `json:"favicon_raw" gorm:"column:favicon_raw;default:null;type:CLOB"`
// 延迟,不可达时为-1
PingDelay int64 `json:"ping_delay" gorm:"column:ping_delay;default:-1"`
// 更新时间
LastUpdate int64 `json:"last_update" gorm:"column:last_update;default:0"`
}
// serverSubscribe 订阅信息
type serverSubscribe struct {
// ID 主键
ID int64 `json:"id" gorm:"column:id;primary_key:pk_id;auto_increment;default:0"`
// 服务器地址
ServerAddr string `json:"server_addr" gorm:"column:server_addr;default:'';unique_index:udx_ait"`
// 推送目标id
TargetID int64 `json:"target_id" gorm:"column:target_id;default:0;unique_index:udx_ait"`
// 类型 1群组 2个人
TargetType int64 `json:"target_type" gorm:"column:target_type;default:0;unique_index:udx_ait"`
// 更新时间
LastUpdate int64 `json:"last_update" gorm:"column:last_update;default:0"`
}
const (
// pingDelayUnreachable 不可达
pingDelayUnreachable = -1
)
// isServerStatusSpecChanged 检查是否有状态变化
func (ss *serverStatus) isServerStatusSpecChanged(newStatus *serverStatus) (res bool) {
res = false
if ss == nil || newStatus == nil {
res = false
return
}
// 描述变化、版本变化、Favicon变化
if ss.Description != newStatus.Description || ss.Version != newStatus.Version || ss.FaviconMD5 != newStatus.FaviconMD5 {
res = true
return
}
// 状态由不可达变为可达 or 反之
if (ss.PingDelay == pingDelayUnreachable && newStatus.PingDelay != pingDelayUnreachable) ||
(ss.PingDelay != pingDelayUnreachable && newStatus.PingDelay == pingDelayUnreachable) {
res = true
return
}
return
}
// deepCopy 深拷贝
func (ss *serverStatus) deepCopy() (dst *serverStatus) {
if ss == nil {
return
}
dst = &serverStatus{}
*dst = *ss
return
}
// generateServerStatusMsg 生成服务器状态消息
func (ss *serverStatus) generateServerStatusMsg() (msg string, iconBase64 string) {
var msgBuilder strings.Builder
if ss == nil {
return
}
msgBuilder.WriteString(ss.Description)
msgBuilder.WriteString("\n")
msgBuilder.WriteString("服务器地址:")
msgBuilder.WriteString(ss.ServerAddr)
msgBuilder.WriteString("\n")
// 版本
msgBuilder.WriteString("版本:")
msgBuilder.WriteString(ss.Version)
msgBuilder.WriteString("\n")
// Ping
if ss.PingDelay < 0 {
msgBuilder.WriteString("Ping延迟超时\n")
} else {
msgBuilder.WriteString("Ping延迟")
msgBuilder.WriteString(fmt.Sprintf("%d 毫秒\n", ss.PingDelay))
msgBuilder.WriteString("在线人数:")
msgBuilder.WriteString(ss.Players)
}
// 图标
if ss.FaviconRaw != "" && ss.FaviconRaw.checkPNG() {
iconBase64 = ss.FaviconRaw.toBase64String()
}
msg = msgBuilder.String()
return
}
// DB Schema End
// ====================
// Ping & List Response DTO
// serverPingAndListResp 服务器状态数据传输对象 From mc server response
type serverPingAndListResp struct {
Description chat.Message
Players struct {
Max int
Online int
Sample []struct {
ID uuid.UUID
Name string
}
}
Version struct {
Name string
Protocol int
}
Favicon icon
Delay time.Duration
}
// icon should be a PNG image that is Base64 encoded
// (without newlines: \n, new lines no longer work since 1.13)
// and prepended with "data:image/png;base64,".
type icon string
// func (i icon) toImage() (icon image.Image, err error) {
// const prefix = "data:image/png;base64,"
// if !strings.HasPrefix(string(i), prefix) {
// return nil, errors.Errorf("server icon should prepended with %s", prefix)
// }
// base64png := strings.TrimPrefix(string(i), prefix)
// r := base64.NewDecoder(base64.StdEncoding, strings.NewReader(base64png))
// icon, err = png.Decode(r)
// return
//}
// checkPNG 检查是否为PNG
func (i icon) checkPNG() bool {
const prefix = "data:image/png;base64,"
return strings.HasPrefix(string(i), prefix)
}
// toBase64String 转换为base64字符串
func (i icon) toBase64String() string {
return "base64://" + strings.TrimPrefix(string(i), "data:image/png;base64,")
}
// genServerSubscribeSchema 将DTO转换为DB Schema
func (dto *serverPingAndListResp) genServerSubscribeSchema(addr string, id int64) *serverStatus {
if dto == nil {
return nil
}
faviconMD5 := md5.Sum(helper.StringToBytes(string(dto.Favicon)))
return &serverStatus{
ID: id,
ServerAddr: addr,
Description: dto.Description.ClearString(),
Version: dto.Version.Name,
Players: fmt.Sprintf("%d/%d", dto.Players.Online, dto.Players.Max),
FaviconMD5: hex.EncodeToString(faviconMD5[:]),
FaviconRaw: dto.Favicon,
PingDelay: dto.Delay.Milliseconds(),
LastUpdate: time.Now().Unix(),
}
}
// Ping & List Response DTO End
// ====================
// ====================
// Biz Model
const (
logPrefix = "[minecraft observer] "
)
// warpTargetIDAndType 转换消息信息到订阅的目标ID和类型
func warpTargetIDAndType(groupID, userID int64) (int64, int64) {
// 订阅
var targetID int64
var targetType int64
if groupID == 0 {
targetType = targetTypeUser
targetID = userID
} else {
targetType = targetTypeGroup
targetID = groupID
}
return targetID, targetType
}
// formatSubStatusChangeText 格式化状态变更文本
func formatSubStatusChangeText(oldStatus, newStatus *serverStatus) string {
var msgBuilder strings.Builder
if oldStatus == nil || newStatus == nil {
return ""
}
// 变更通知
msgBuilder.WriteString("[Minecraft服务器状态变更通知]\n")
// 地址
msgBuilder.WriteString(fmt.Sprintf("服务器地址: %v\n", oldStatus.ServerAddr))
// 描述
if oldStatus.Description != newStatus.Description {
msgBuilder.WriteString("\n-----[描述变更]-----\n")
msgBuilder.WriteString(fmt.Sprintf("[旧]\n%v\n", oldStatus.Description))
msgBuilder.WriteString(fmt.Sprintf("[新]\n%v\n", newStatus.Description))
}
// 版本
if oldStatus.Version != newStatus.Version {
msgBuilder.WriteString("\n-----[版本变更]-----\n")
msgBuilder.WriteString(fmt.Sprintf("[旧]\n%v\n", oldStatus.Version))
msgBuilder.WriteString(fmt.Sprintf("[新]\n%v\n", newStatus.Version))
}
// 状态由不可达变为可达,反之
if oldStatus.PingDelay == pingDelayUnreachable && newStatus.PingDelay != pingDelayUnreachable {
msgBuilder.WriteString("\n-----[Ping延迟]-----\n")
msgBuilder.WriteString("[旧]\n超时\n")
msgBuilder.WriteString(fmt.Sprintf("[新]\n%v毫秒\n", newStatus.PingDelay))
}
if oldStatus.PingDelay != pingDelayUnreachable && newStatus.PingDelay == pingDelayUnreachable {
msgBuilder.WriteString("\n-----[Ping延迟]-----\n")
msgBuilder.WriteString(fmt.Sprintf("[旧]\n%v毫秒\n", oldStatus.PingDelay))
msgBuilder.WriteString("[新]\n超时\n")
}
return msgBuilder.String()
}
// Biz Model End
// ====================

View File

@ -0,0 +1,63 @@
package minecraftobserver
import (
"encoding/json"
"time"
"github.com/RomiChan/syncx"
"github.com/Tnze/go-mc/bot"
)
var (
// pingServerUnreachableCounter Ping服务器不可达计数器防止bot本体网络抖动导致误报
pingServerUnreachableCounter = syncx.Map[string, pingServerUnreachableCounterDef]{}
// 计数器阈值
pingServerUnreachableCounterThreshold = int64(3)
// 时间阈值
pingServerUnreachableCounterTimeThreshold = time.Minute * 30
)
type pingServerUnreachableCounterDef struct {
count int64
firstUnreachableTime time.Time
}
func addPingServerUnreachableCounter(addr string, ts time.Time) (int64, time.Time) {
key := addr
get, ok := pingServerUnreachableCounter.Load(key)
if !ok {
pingServerUnreachableCounter.Store(key, pingServerUnreachableCounterDef{
count: 1,
firstUnreachableTime: ts,
})
return 1, ts
}
// 存在则更新,时间戳不变
pingServerUnreachableCounter.Store(key, pingServerUnreachableCounterDef{
count: get.count + 1,
firstUnreachableTime: get.firstUnreachableTime,
})
return get.count + 1, get.firstUnreachableTime
}
func resetPingServerUnreachableCounter(addr string) {
key := addr
pingServerUnreachableCounter.Delete(key)
}
// getMinecraftServerStatus 获取Minecraft服务器状态
func getMinecraftServerStatus(addr string) (*serverPingAndListResp, error) {
var s serverPingAndListResp
resp, delay, err := bot.PingAndListTimeout(addr, time.Second*5)
if err != nil {
// logrus.Errorln(logPrefix+"PingAndList error: ", err)
return nil, err
}
err = json.Unmarshal(resp, &s)
if err != nil {
// logrus.Errorln(logPrefix+"Parse json response fail: ", err)
return nil, err
}
s.Delay = delay
return &s, nil
}

Some files were not shown because too many files have changed in this diff Show More