Compare commits

..

353 Commits

Author SHA1 Message Date
源文雨
56984ef7fb 🔖 v1.6.2-beta4 2023-02-15 17:27:52 +08:00
源文雨
ac4de84188 fix: render text 2023-02-15 17:27:29 +08:00
源文雨
591ad79201 🔖 v1.6.2-beta3 2023-02-10 15:00:18 +08:00
源文雨
2e51c9e9c3 🚀 优化字体加载 2023-02-10 14:59:27 +08:00
源文雨
ca89f01f7d update deps 2023-02-07 16:53:30 +08:00
lianhong2758
5dfc118f64 修复原神 vits api失效 (#582) 2023-02-07 16:50:09 +08:00
源文雨
2410a7b3ed make lint happy 2023-02-07 16:46:26 +08:00
源文雨
7e11924b3f update deps 2023-02-07 16:39:07 +08:00
lianhong2758
4f65900688 readme add kokomi (#579) 2023-02-07 12:20:21 +08:00
源文雨
20821827b3 🔖 v1.6.2-beta2 2023-02-06 21:47:25 +08:00
源文雨
3dd10169e3 update deps 2023-02-06 21:40:41 +08:00
DreamZero
8cd60a84d5 复制黏贴被发现了( (#578) 2023-02-06 16:49:55 +08:00
DreamZero
03a2f4c331 给aifalse加一点细节 (#575) 2023-02-05 12:59:50 +08:00
icarus-ai
cfc9b55fce 更新quic-go版本,以支持使用Go 1.20编译 (#577)
* 支持 Go 1.20

* fix workflows (≖_≖ )

* 回滚 workflows

* 回滚 workflows
2023-02-04 12:05:14 +08:00
苜蓿紫
fc99eb3e22 修正qq号大小比较 (#576) 2023-02-02 11:42:52 +08:00
源文雨
93625f9c4f 优化代码结构 2023-02-01 15:03:05 +08:00
苜蓿紫
b6ce9f7b2e 修正错别字&增加提示语 (#574)
* 修正错别字

* 修正错别字

* 增加整理提示语

代发方柳
2023-02-01 14:53:10 +08:00
github-actions[bot]
20642b3776 🎨 改进代码样式 (#573)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2023-02-01 11:52:42 +08:00
GenesisAN
16ab7c08f6 fix 初始化错误,改善阅读体验 (#572) 2023-02-01 11:47:04 +08:00
源文雨
7bce7f28dc make lint happy 2023-01-31 18:25:32 +08:00
源文雨
8c695c96d7 优化代码结构 2023-01-31 18:23:08 +08:00
源文雨
d4a057e21a 优化代码结构 2023-01-31 18:20:12 +08:00
github-actions[bot]
f18c809355 🎨 改进代码样式 (#571)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2023-01-31 16:30:11 +08:00
github-actions[bot]
0d9c8d9a43 🎨 改进代码样式 (#570)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2023-01-31 16:29:02 +08:00
github-actions[bot]
76e7f81a6c 🎨 改进代码样式 (#569)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2023-01-31 16:27:43 +08:00
方柳煜
faf00dfea7 优化好感度系统,修复重复人名问题 (#568) 2023-01-31 16:23:27 +08:00
GenesisAN
39af90e63d add WarframeAPI (#541) 2023-01-31 16:22:19 +08:00
源文雨
36b09b8e94 tts append 。 2023-01-30 18:36:47 +08:00
源文雨
7ec2b46d75 🔖 v1.6.2-beta1 2023-01-30 18:01:56 +08:00
源文雨
7cc5771a9a update deps 2023-01-30 18:00:14 +08:00
DreamZero
f96b5f8965 优化代码结构 (#565) 2023-01-30 17:42:53 +08:00
himawari
07e6f33b04 添加小说下载 (#553)
*  添加小说下载

* 🎨 修改readme

* 🎨 修lint

* 🎨 修改包

* 🎨 使用新版http封装

* 🎨 new line
2023-01-30 17:40:57 +08:00
源文雨
f1a33a1845 🔖 v1.6.1-fix1 2023-01-16 11:36:31 +08:00
源文雨
0a72566378 fix #561: render text black & shindan 2023-01-16 11:35:11 +08:00
lianhong2758
a029add486 权重查询美化 (#560) 2023-01-16 11:25:39 +08:00
源文雨
778b8749ea 🐛 修复pixiv无法获取original url 2023-01-13 16:28:17 +08:00
源文雨
7d5944fccf 🔖 v1.6.1 2023-01-13 00:51:00 +08:00
github-actions[bot]
3e16a24ab5 🎨 改进代码样式 (#559)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2023-01-12 18:21:06 +08:00
源文雨
ff682da3fe 🔥 remove atri sleep due to global latency 2023-01-12 18:17:12 +08:00
源文雨
a0120485de fix #551: atri 发图 2023-01-12 18:14:47 +08:00
源文雨
0c8e9198ee fix: atri & v1.6.1-beta5 2023-01-11 14:47:05 +08:00
源文雨
40da2481cc fix: setupool file uri 2023-01-10 14:25:24 +08:00
源文雨
a7ae56481b fix: setupool file uri 2023-01-10 14:08:00 +08:00
源文雨
f550d58ad7 fix: heisi 2023-01-10 11:02:49 +08:00
源文雨
f488b75ac6 del: heisi due to api fail 2023-01-09 22:58:04 +08:00
源文雨
4308e386b4 fix: setu 2023-01-09 22:06:14 +08:00
源文雨
df1c207d77 add plugin autowithdraw
触发者撤回时也自动撤回
2023-01-09 21:55:20 +08:00
源文雨
8ca71967b6 移除ChatGPT & 默认注释 thesaurus 2023-01-09 21:17:04 +08:00
源文雨
c99d09ea63 优化代码结构 2023-01-07 16:24:02 +08:00
方柳煜
9ce65de698 增加并发,解决FutureEvent超时问题 (#552) 2023-01-03 18:52:27 +08:00
源文雨
cc7953c486 update 服务 2022-12-30 19:36:15 +08:00
源文雨
d50f330ccf update win32 dl 2022-12-29 14:28:12 +08:00
himawari
36f2a23aa1 🐛 修复moegoe (#546) 2022-12-27 16:01:43 +08:00
Stardust·减
14b98d4006 Update moegoe,add VITS cn interface (#544)
* Update main.go

* Update main.go

* Update main.go
2022-12-26 18:50:44 +08:00
源文雨
d401136966 🔖 1.6.1-beta3 2022-12-26 13:45:24 +08:00
源文雨
d0610b152f update downloadto 2022-12-26 13:27:28 +08:00
源文雨
fa8428ae8b edit README 2022-12-26 13:12:20 +08:00
源文雨
7d1a4b4005 edit README 2022-12-26 13:11:38 +08:00
源文雨
19e3688499 fix tts 2022-12-18 10:12:55 +08:00
github-actions[bot]
b8da6f8cfd 🎨 改进代码样式 (#536)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2022-12-16 18:04:09 +08:00
太好听了吧
58631c5b19 ️update music (#535)
* ️update music

添加咪咕点歌

* ️update music

添加咪咕点歌功能
2022-12-16 18:02:43 +08:00
github-actions[bot]
e994768318 🎨 改进代码样式 (#534)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2022-12-15 22:16:01 +08:00
太好听了吧
c304d72d77 music 音乐自定义分享 (#533)
1. 酷我音乐音乐直链url和参数修改,添加音乐卡片自定义参数。
2. 酷狗音乐添加音乐卡片自定义参数。
2022-12-15 22:12:55 +08:00
DreamZero
a1f1016fb9 独立wallet插件 (#531) 2022-12-15 01:28:33 +08:00
源文雨
913f01d69f 🔖 1.6.1-beta2 2022-12-14 21:39:27 +08:00
源文雨
f56929c852 简化设置原神vits key流程 2022-12-14 21:38:55 +08:00
源文雨
e9e0498b34 fix tts 2022-12-14 20:26:37 +08:00
源文雨
b6026ee76a fix pull ci 2022-12-14 16:56:09 +08:00
源文雨
8fdc358cc5 🔖 1.6.1-beta1 2022-12-14 16:52:59 +08:00
源文雨
fef0ac6049 fix genshin tts 2022-12-14 16:52:17 +08:00
源文雨
e144db9205 fix: aireply nil 2022-12-13 20:49:34 +08:00
源文雨
825a5a0d43 🎨 edit README 2022-12-12 18:59:42 +08:00
源文雨
6dccfc862c 🎨 edit README 2022-12-12 18:58:28 +08:00
源文雨
771f93f9af fix #527:
在设置群回复模式的时候没有生效
2022-12-12 18:50:41 +08:00
源文雨
7893fc9ebe revert 2022-12-12 16:03:42 +08:00
源文雨
9b4363dda8 fix #525: chatgpt add UA/CF config 2022-12-12 16:02:17 +08:00
源文雨
257263bfa2 ✏️ edit README 2022-12-11 15:39:47 +08:00
源文雨
8f009bb4ee config.json supports wss 2022-12-11 15:38:01 +08:00
源文雨
34a3cf82e7 🎨 edit README 2022-12-11 14:34:16 +08:00
源文雨
cd927ec2c4 🎨 优化chatgpt reset 2022-12-11 14:11:38 +08:00
源文雨
1266b2378a 🎨 优化tts 2022-12-11 14:10:35 +08:00
源文雨
575d158d5a Merge branch 'master' of https://github.com/FloatTech/ZeroBot-Plugin 2022-12-10 14:35:32 +08:00
源文雨
a008faf805 🐛 fix push 2022-12-10 14:35:28 +08:00
github-actions[bot]
9a2081de0a 🎨 改进代码样式 (#521)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2022-12-10 14:29:55 +08:00
源文雨
9550b817c5 add Create Pull Request 2022-12-10 14:28:42 +08:00
源文雨
ee895e7dc4 reintroduce Commit back 2022-12-10 14:19:04 +08:00
源文雨
7aa2996540 Merge branch 'master' of https://github.com/FloatTech/ZeroBot-Plugin 2022-12-10 14:16:35 +08:00
源文雨
725aa5b180 💩👌 make lint happy 2022-12-10 14:16:30 +08:00
jinyunboss
f96f14052e feat: 群聊中撤回消息 (#520) 2022-12-10 14:11:26 +08:00
源文雨
adfa9f9e0f 💩👌 make lint happy 2022-12-10 14:11:09 +08:00
源文雨
d8bc336006 🔖 1.6.0 2022-12-10 13:57:30 +08:00
源文雨
b69bd3d640 fix: chatgpt " 2022-12-10 13:32:49 +08:00
莫思潋
5d9ac50106 fix: aireply: ent->enr & update README & optimize bilibili match (#519) 2022-12-09 23:23:56 +08:00
源文雨
df83ffc8b0 add reset 2022-12-07 21:08:54 +08:00
源文雨
e49b568d6d add ChatGPT 2022-12-07 20:36:23 +08:00
源文雨
8399e49072 aireply supports ChatGPT 2022-12-07 19:38:23 +08:00
源文雨
d76a65ec07 fix tts 2022-12-07 11:52:16 +08:00
himawari
ed7cef7566 添加随机女装 (#514) 2022-12-05 11:34:31 +08:00
源文雨
3349ec7694 edit help 2022-12-03 17:23:06 +08:00
源文雨
11f358bcbf fix gs tts 2022-12-03 17:16:45 +08:00
源文雨
82608d01bb genshin vits add api key 2022-12-03 16:35:14 +08:00
源文雨
a93f4f68b4 🔖 1.6.0-beta3 2022-12-03 13:43:00 +08:00
源文雨
6f8c576a44 🐛 fix ttr 2022-12-03 13:40:03 +08:00
源文雨
e75fc83412 🐛 fix ttr 2022-12-03 13:31:59 +08:00
方柳煜
2e538e3905 ygo: 优化指令 (#513) 2022-12-03 12:28:57 +08:00
源文雨
a69399fa58 🔖 1.6.0-beta2 2022-12-03 12:18:27 +08:00
源文雨
3e94d27e6a update deps 2022-12-03 12:11:48 +08:00
源文雨
20293b35f8 优化simai 2022-12-03 12:10:21 +08:00
源文雨
b768e48a74 add succ hint 2022-12-02 20:35:47 +08:00
源文雨
6a64c1ffd6 增加处理{name}和{segment} 2022-12-02 20:28:18 +08:00
源文雨
3b1ad2250f 词库增加设置概率功能 2022-12-02 20:24:44 +08:00
源文雨
83778afd51 fix 2022-12-02 18:59:10 +08:00
源文雨
cbbae11fcd update deps 2022-12-02 17:54:11 +08:00
源文雨
11c01af9da thetharus 增加 傲娇 可爱 词库 2022-12-02 16:50:12 +08:00
源文雨
7547b33f65 Merge branch 'master' of https://github.com/FloatTech/ZeroBot-Plugin 2022-12-02 14:12:43 +08:00
源文雨
4305f589df edit readme 2022-12-02 14:12:36 +08:00
方柳煜
c8684f74a8 修复重置花名册会报错问题 (#511) 2022-12-01 14:04:40 +08:00
方柳煜
7cdc92c142 Update qqwife plugin (#510) 2022-11-30 16:53:32 +08:00
lianhong2758
a7ccf302f2 更新权重查询api地址 (#509) 2022-11-28 22:16:01 +08:00
方柳煜
f4b41efc75 优化qqwife代码,增加代码可读性 (#508) 2022-11-28 11:13:26 +08:00
方柳煜
ac18d3df96 新增一些游戏王相关的插件 (#507) 2022-11-28 11:12:19 +08:00
源文雨
daff47d912 fix nsetu 2022-11-25 15:23:16 +08:00
方柳煜
27d04cbae2 Update README.md (#505) 2022-11-24 10:36:35 +08:00
方柳煜
8eef7718ee qqwife新增好感度列表功能 (#504) 2022-11-23 16:39:32 +08:00
源文雨
fb4259b696 fix 2022-11-22 17:11:11 +08:00
源文雨
eb378ea45a 🍱 更新setu数据库 2022-11-22 17:05:48 +08:00
源文雨
02992271a1 ️ update reg 2022-11-21 23:18:29 +08:00
源文雨
72b11c2717 📄 使用 AGPL v3 2022-11-21 18:45:12 +08:00
方柳煜
a55f28c006 优化代码 (#501) 2022-11-17 12:08:12 +08:00
lianhong2758
9f37962d22 QQ权重查询 (#500) 2022-11-16 23:17:53 +08:00
源文雨
8d001122e2 sync data 2022-11-15 11:50:00 +08:00
源文雨
960aa2231c 🔖 v1.6.0-beta1 2022-11-15 11:14:28 +08:00
源文雨
e55a133f00 🎨 新服务列表 2022-11-15 11:13:25 +08:00
himawari
4075c92e53 添加qq空间表白墙 (#489) 2022-11-13 18:03:15 +08:00
方柳煜
743e5509a2 全新的guessmusic插件 (#499) 2022-11-13 18:01:15 +08:00
lianhong2758
1c93611a16 更正插件名称 (#497) 2022-11-13 17:58:18 +08:00
GenesisAN
df8a320fbe fix baiduaudit的正则错误 (#495)
修复一个正则错误,导致不能正确解析第二个ApiKey的问题
2022-11-12 11:16:31 +08:00
莫思潋
8e8464d727 调整以图绘图 (#493) 2022-11-10 18:30:27 +08:00
源文雨
356d1dfe4d 🐛 ms5 cat panic 2022-11-10 15:15:11 +08:00
源文雨
d185de0f3d fix: 设精 2022-11-10 10:26:15 +08:00
源文雨
644a6cdcc2 fix: 设精 2022-11-09 23:28:08 +08:00
源文雨
85f5fb2d87 🔖 v1.5.2 2022-11-09 15:15:22 +08:00
源文雨
8674c14754 🔖 v1.5.2 2022-11-09 14:54:52 +08:00
DreamZero
6c2fc1b4c2 恢复注册方式 (#491) 2022-11-09 13:29:07 +08:00
源文雨
5d94980e7c 优化设精 2022-11-07 12:26:13 +08:00
源文雨
9bed71a99f feat: job drop limit due to 事件环 2022-11-07 11:04:09 +08:00
方柳煜
6faa2b65e2 添加设精功能 (#488) 2022-11-06 10:42:04 +08:00
lianhong2758
98812b103e 第三次提交兽语加密 (#486) 2022-11-05 22:15:34 +08:00
himawari
deb655de36 Feature huggingface (#478) 2022-11-05 17:32:31 +08:00
方柳煜
8d1c18cb4a Update command.go (#487) 2022-11-05 11:10:36 +08:00
DreamZero
88824126f7 add: 添加Brief (#482)
* add brief

* fix: README中插件位置
2022-11-04 12:12:47 +08:00
喵酱
68a9ecf995 修改了两处错误的文案 (#481) 2022-11-03 22:26:43 +08:00
方柳煜
57404b2dcd 优化签到插件,qqwife插件添加礼物系统 (#480) 2022-11-03 16:38:48 +08:00
源文雨
29705c63c9 doc: 添加项目趋势图 2022-11-02 13:19:57 +08:00
源文雨
5a854d9715 options add brief & banner 2022-11-01 11:30:05 +08:00
源文雨
eb1cbeae3f 🎨 优化事件环 2022-10-30 00:07:17 +08:00
weigui404
b6246cfee6 Update README.md (#479)
2022-10-30 00:03:32 +08:00
源文雨
0619000f9a 🎨 优化事件环 2022-10-29 20:16:02 +08:00
源文雨
01361781c8 🎨 增加最大处理时间 2022-10-29 11:25:50 +08:00
源文雨
3c03a308b3 fix #421: 增加全局响应延迟与处理不及消息的丢弃 2022-10-28 17:41:28 +08:00
源文雨
7895e48420 update zb: 事件环 2022-10-28 16:18:03 +08:00
源文雨
ba15713b99 update zb: 事件环 2022-10-28 15:27:29 +08:00
源文雨
bd98fa3aed update zb: 事件环 2022-10-28 14:44:32 +08:00
源文雨
fce75a9475 🔖 v1.5.2-beta2 2022-10-28 12:50:16 +08:00
源文雨
7616d5759f fix nsetu 循环触发 2022-10-28 11:40:58 +08:00
源文雨
ea2c81a9c7 Merge branch 'master' of https://github.com/FloatTech/ZeroBot-Plugin 2022-10-28 11:35:18 +08:00
源文雨
37cff9ff31 update reg 2022-10-28 11:35:11 +08:00
MoeMagicMango
b8d3d6af9b 修改漂流瓶的帮助 (#477) 2022-10-28 10:50:51 +08:00
MoeMagicMango
fdf90a72cc feat: 全局漂流瓶 (#474)
* feat: remove driftbottle and push bottle global plugin.

* Reverse name

* change by requests
2022-10-28 00:24:57 +08:00
DreamZero
ba0c05a774 change: 新的缓存池key (#476) 2022-10-28 00:20:19 +08:00
DreamZero
3250ec14ac fix: tarot (#475)
* fix tarot get img

* change: use sendtoslef check cache

但是好像过慢

* change: use imgpool

* fix

感觉改不好了

* fix: 修改变量赋值顺序
2022-10-28 00:00:43 +08:00
源文雨
ce2f390361 update md5 2022-10-27 16:57:16 +08:00
方柳煜
71434232fe 修复help显示问题 (#473)
* 修复help显示问题

* Update command.go

* Update wenxinAI.go

* Update README.md
2022-10-27 11:04:10 +08:00
GenesisAN
852629fa2e add:百度内容审核插件 (#468) 2022-10-25 18:43:58 +08:00
源文雨
f10676d16d 🔖 v1.5.2-beta1 2022-10-24 23:46:59 +08:00
方柳煜
d43ea7df1d 扩增文心AI的功能,修复猜歌的权限问题 (#471) 2022-10-24 23:36:28 +08:00
方柳煜
21712c6299 扩展aipaint指令 (#472) 2022-10-24 23:35:43 +08:00
himawari
2fc47a38fa 🎨 优化aipaint撤回,nihongo搜索 (#470) 2022-10-23 16:50:47 +08:00
源文雨
cbb4408668 fix tarot 2022-10-22 12:12:46 +08:00
源文雨
a0df41b859 fix tarot 2022-10-22 12:01:45 +08:00
DreamZero
494c1b33b4 fix: 因为gitcode反爬导致tarot插件图片获取失败 (#465)
* fix tarot get img

* change: use sendtoslef check cache

但是好像过慢

* change: use imgpool

* fix

感觉改不好了
2022-10-22 11:53:46 +08:00
DreamZero
2034d91912 fix: 以图搜图合并转发 (#467) 2022-10-20 00:17:00 +08:00
源文雨
235ced0b78 🔖 v1.5.1 2022-10-16 12:59:24 +08:00
源文雨
3339b6a16f fix runcode 2022-10-15 14:12:36 +08:00
weigui404
c08e3dfe4a Update setu_geter.go (#452) 2022-10-15 10:23:24 +08:00
himawari
894cf41d37 🎨 优化鉴赏图片, 文本重用 (#451) 2022-10-14 20:47:34 +08:00
源文雨
55aa869b1a update deps 2022-10-14 14:30:05 +08:00
himawari
42b18619c3 Feature jptingroom (#448) 2022-10-14 13:58:52 +08:00
himawari
b0c57dfec5 Feature aipaint (#450) 2022-10-14 13:57:42 +08:00
himawari
bdbcf2a5f8 Feature alipayvoice (#449)
*  添加支付宝到账

*  添加readme
2022-10-14 13:53:20 +08:00
源文雨
7807e68519 add plugin Heisi 2022-10-13 11:13:43 +08:00
方柳煜
4cd81cb2e9 更新QQ音乐点歌API,修复猜歌panic (#444) 2022-10-12 23:45:31 +08:00
方柳煜
dc34a33346 修复tts默认语音设置不成功问题 (#445)
* Update main.go

* Update ai_tts.go
2022-10-12 23:43:52 +08:00
方柳煜
2922854f96 百度文心AI画图插件 (#442) 2022-10-12 16:44:05 +08:00
源文雨
29c85442ff update deps 2022-10-11 23:43:21 +08:00
源文雨
048cbf51ca update deps 2022-10-11 13:56:25 +08:00
方柳煜
467dd2ea65 猜歌插件更改为本地猜歌 (#439) 2022-10-11 00:06:50 +08:00
源文雨
aef678be5c Merge branch 'master' of https://github.com/FloatTech/ZeroBot-Plugin 2022-10-10 10:12:35 +08:00
源文雨
77f7567b29 fix: nwife dup slash & genshin vits 2022-10-10 10:12:23 +08:00
MoeMagicMango
c3c0685d3a fix:换成网易云官方的api (#440) 2022-10-09 23:12:09 +08:00
源文雨
c4c3f79c8c 💩👌 make lint happy 2022-10-08 10:57:01 +08:00
源文雨
9cb54a34da 🎨 优化bilibili 2022-10-07 12:21:20 +08:00
源文雨
519ae62760 fix breakrepeat shuffle 2022-10-07 10:49:55 +08:00
方柳煜
6971da1abc fix #433 修复重置之后第一个人娶群友不登记问题 (#435)
* Update command.go

* Update function.go

* Update command.go

* Update function.go
2022-10-07 10:26:23 +08:00
源文雨
5f3054d344 Merge branch 'master' of https://github.com/FloatTech/ZeroBot-Plugin 2022-10-05 17:19:47 +08:00
源文雨
5b586a7402 💩👌 make lint happy 2022-10-05 17:18:51 +08:00
方柳煜
f2ff66ec1d 修复qqwife bug,优化程序 (#432)
* Update command.go

* Update function.go

* Update command.go
2022-10-05 17:18:02 +08:00
源文雨
92b9bab76a edit README 2022-10-05 12:23:29 +08:00
源文雨
4609985b79 优化代码结构 2022-10-05 12:09:09 +08:00
源文雨
6cf14fc7dd 🔖 v1.5.1-beta5 2022-10-04 22:19:11 +08:00
源文雨
053363f781 优化代码结构 2022-10-04 22:18:20 +08:00
源文雨
e319bd61f1 fix baseamasiro 2022-10-04 21:15:54 +08:00
源文雨
baad83172f fix gitcode 图床 2022-10-04 17:54:43 +08:00
源文雨
4b288a59b9 🐛 hyaku 2022-10-04 16:39:18 +08:00
源文雨
a2ada0b67e 🔥🐛 fix lazy data & add base天城文 2022-10-03 23:22:40 +08:00
莫思潋
97fea6a93a fix: []message.MessageSegment->message.Message (#429) 2022-10-03 19:54:39 +08:00
源文雨
145a9fb0b1 fix 64gua 2022-10-02 11:11:05 +08:00
源文雨
202d5e41a1 Merge branch 'master' of https://github.com/FloatTech/ZeroBot-Plugin 2022-10-01 23:46:37 +08:00
源文雨
a64bead5e6 base64卦加解密 2022-10-01 23:46:28 +08:00
DreamZero
f9b84f8cb1 fix lolicon panic and more plugin use forward (#426)
* fix lolicon panic and more plugin use forward

* add back lolicon imgpool

* 带tag不使用缓存
2022-09-26 18:07:56 +08:00
方柳煜
76e0c13f89 好感度需发“重置所有花名册”才能使用 (#423)
* Update function.go

* Update command.go
2022-09-23 11:11:19 +08:00
源文雨
f9319c2a27 优化 breakrepeat 2022-09-22 11:14:24 +08:00
源文雨
295c50a0ae 调整 breakrepeat 优先级为最低 2022-09-22 10:55:44 +08:00
源文雨
942f5abbe4 🔖 v1.5.1-beta4 2022-09-22 10:49:59 +08:00
源文雨
965df40476 ⬆️ update deps 2022-09-22 10:47:11 +08:00
方柳煜
11f7f07009 扩展插件功能 (#422) 2022-09-22 10:40:26 +08:00
源文雨
76cd2afbd3 🔥 优化服务图片,删除服务详情 2022-09-21 13:55:52 +08:00
源文雨
00d07cb7af fix 2022-09-21 12:50:58 +08:00
源文雨
3234c18718 fix tts 2022-09-21 11:49:30 +08:00
源文雨
d48285c450 job 支持分群打印触发指令 2022-09-21 10:07:12 +08:00
源文雨
98ea387254 job cron 增加别名 2022-09-19 23:18:12 +08:00
源文雨
fb80ff3b74 🐛 优化 ban 2022-09-19 20:57:03 +08:00
源文雨
41e0cf8424 🐛 fix ban 2022-09-19 20:12:57 +08:00
源文雨
1bcd321477 🐛 fix ban 2022-09-19 19:49:29 +08:00
源文雨
b0482ee84b fix go.sum 2022-09-17 20:59:55 +08:00
源文雨
404f4041a9 🐛 fix b14 panic on some platform 2022-09-17 20:49:53 +08:00
源文雨
f6d6375667 🐛 fix b14 panic on some platform 2022-09-17 20:44:15 +08:00
源文雨
04acf4af49 Merge branch 'master' of https://github.com/FloatTech/ZeroBot-Plugin 2022-09-17 18:04:09 +08:00
源文雨
df6957cfb6 🔖 v1.5.1-beta3 2022-09-17 18:03:50 +08:00
himawari
d3ab1e1560 Refactor image (#419) 2022-09-17 14:53:25 +08:00
源文雨
598a255e34 make lint happy 2022-09-17 14:50:29 +08:00
源文雨
b4418fda55 🐛 fix 2022-09-17 14:42:26 +08:00
源文雨
3f26dde791 fix: /ban 2022-09-17 14:36:55 +08:00
MoeMagicMango
14d81411b1 Optimize: 更换经常出问题的iw233接口为MoeHu (#412) 2022-09-17 13:43:38 +08:00
莫思潋
4b667835f9 fix: 小鸡词典 (#410)
* - 调整触发器符合说明
- 减少查不到信息的情况
- 增加图片

* typo: imgURL

* - 改用foreach
- 增加被jikipedia封禁的提示

* 对于bilibili,在readme种引入了一键获取cookie工具

* 顺lint心意
2022-09-16 10:10:21 +08:00
莫思潋
002f971882 feat: 小鸡词典查梗 (#408) 2022-09-12 20:04:35 +08:00
源文雨
99a8fcd73a disable commit back 2022-09-12 20:02:39 +08:00
源文雨
3705da38b7 update deps 2022-09-12 19:58:46 +08:00
源文雨
32afee62c5 🐛 ban 人有时失灵 2022-09-12 19:21:46 +08:00
方柳煜
a5ecb4bf7d 修改tts指令使用权限,防止泛用滥用 (#400)
* 修改指令权限,防止泛用

* Update ai_tts.go

* Update ai_tts.go

* Update ai_tts.go
2022-09-12 18:53:53 +08:00
源文雨
1a424ebcd0 优化显示 2022-09-11 22:43:56 +08:00
源文雨
6a44331edd 🔖 v1.5.1-beta2 2022-09-11 10:32:35 +08:00
源文雨
4e78f43649 优化 2022-09-10 13:29:04 +08:00
源文雨
ca7ac08808 优化输出 2022-09-10 13:24:15 +08:00
源文雨
3351450d88 优化显示 2022-09-10 13:21:17 +08:00
源文雨
fa82bdec79 event master check default off 2022-09-10 13:14:43 +08:00
源文雨
0860a105ed 优化代码结构 2022-09-10 11:58:48 +08:00
Jiang-Red
b834307178 新插件event, 好友申请群聊邀请事件处理 (#403) 2022-09-10 11:01:24 +08:00
源文雨
fafe24f1e4 💩👌 make lint happy 2022-09-09 09:54:33 +08:00
源文雨
6f1ec04ac8 Revert "修正moegoe节之Readme (#402)" (#404)
This reverts commit 263278d71d.
2022-09-09 09:49:13 +08:00
Stardust·减
263278d71d 修正moegoe节之Readme (#402)
* Update README.md
2022-09-08 00:04:51 +08:00
源文雨
9ee5b044b4 🐛 fix engine hook 2022-09-07 00:35:08 +08:00
源文雨
21bfa14b55 更改 ban 时间为 10 分 2022-09-06 21:35:31 +08:00
源文雨
0e2d717aa6 添加ban人记忆 2022-09-06 21:24:49 +08:00
源文雨
f5d2bd78b9 添加ban人记忆 2022-09-06 21:24:19 +08:00
源文雨
ef9f764fa0 添加ban人记忆 2022-09-06 21:20:32 +08:00
源文雨
3cfe1a6960 format 2022-09-06 20:33:05 +08:00
源文雨
9387a48b5b format 2022-09-06 20:32:50 +08:00
源文雨
3812150669 edit help 2022-09-06 20:30:50 +08:00
源文雨
4ca100b0bf fix 2022-09-06 20:27:37 +08:00
源文雨
43dee54678 fix 2022-09-06 20:26:21 +08:00
源文雨
14786a94bf 优化 2022-09-06 20:11:40 +08:00
源文雨
98a652f440 优化 2022-09-06 20:02:14 +08:00
源文雨
a004ed8064 优化 2022-09-06 20:00:59 +08:00
源文雨
37b0edde38 add help 2022-09-06 19:51:11 +08:00
源文雨
fd9f19562f 优化 2022-09-06 19:47:26 +08:00
源文雨
acb7b42ded fix 2022-09-06 19:38:16 +08:00
源文雨
f1e4a523ce 优化显示 2022-09-06 19:35:03 +08:00
源文雨
676ea312a3 精简代码 2022-09-06 19:31:01 +08:00
源文雨
6acd930455 优化代码结构 & fix job regex 2022-09-06 19:19:04 +08:00
AkiraXie
340db0c644 improve antiabuse and add testcase (#398)
* improve antiabuse and add testcase

* fix managers link and add managers_test

* make linter happy

* add ttl.cache
2022-09-05 21:44:22 +08:00
方柳煜
f3d66b6abb 重构了qqwife的逻辑 (#395)
* Update and rename qqmapwife.go to command.go

* Add files via upload

* Update command.go

* Update command.go
2022-09-05 18:52:22 +08:00
源文雨
6b8ae2e864 🔖 v1.5.1-beta1 2022-09-01 21:29:49 +08:00
AkiraXie
664cb2f2f9 antiabuse only to me (#393) 2022-08-30 14:21:57 +08:00
AkiraXie
486292434e add anti_abuse plugin (#392)
add anti_abuse plugin
2022-08-30 11:08:00 +08:00
himawari
e5f186ace1 Feature danmakusuki (#388)
*  添加查弹幕

* 🎨 添加注释

* 🎨 优化代码

* 🎨 修lint

Co-authored-by: 源文雨 <41315874+fumiama@users.noreply.github.com>
2022-08-27 12:13:15 +08:00
himawari
0c637d4c8e 添加ahsai和breakrepeat (#384)
*  添加repeatbreak和ahsai

* 📝 添加readme说明
2022-08-27 12:09:28 +08:00
方柳煜
0943023a19 维护guessmusic和qqwife插件 (#385)
* 优化error输出,新增猜歌结束后分享歌曲

* 添加反并发,防止被多娶

* Update main.go
2022-08-26 18:44:47 +08:00
莫思潋
941d6d2884 feat: 抽塔罗牌附加解析 (#383) 2022-08-22 15:33:25 +08:00
fumiama
169af4ccce Merge branch 'master' of https://github.com/FloatTech/ZeroBot-Plugin 2022-08-22 13:35:40 +08:00
fumiama
6f1d0f8c02 fix gif panic 2022-08-22 11:53:52 +08:00
方柳煜
dd970b9a91 更换tts的语音api至原神语音api (#382) 2022-08-21 13:38:35 +08:00
fumiama
b997cb650e 🎨 优化 moegoe regex 2022-08-20 10:53:20 +08:00
fumiama
dd3ccf1d2f 🐛 moegoe Chinese 2022-08-20 09:26:15 +08:00
Stardust·减
52a7977461 新增VITS中文支持 (#378) 2022-08-20 09:20:39 +08:00
fumiama
70713c0719 🎨 优化 ERROR 格式 2022-08-16 15:15:29 +08:00
fumiama
b4d6e1ef04 🎨 update deps 2022-08-16 14:01:32 +08:00
fumiama
5295701541 fix aireply help 2022-08-16 11:51:01 +08:00
fumiama
43b9cea801 🔖 v1.5.0 2022-08-16 11:16:52 +08:00
fumiama
926d48af81 revert 2022-08-16 10:42:41 +08:00
尐酱油
962c48ca36 gif系列,新增,更新旧的素材 (#373) 2022-08-16 10:40:28 +08:00
fumiama
03fc569f87 更新 /用法 2022-08-16 10:38:16 +08:00
fumiama
e5b712ff39 🎨 优化 moegoe KR 2022-08-16 10:35:32 +08:00
fumiama
c7f7d0409e 优化 moegoe regex 2022-08-15 10:31:24 +08:00
fumiama
e4ec7ea5bb 🐛 moegoe regex 2022-08-14 20:45:53 +08:00
fumiama
4bf734778b edit README 2022-08-14 20:11:43 +08:00
fumiama
461325c8ca add plugin moegoe 2022-08-14 19:33:07 +08:00
源文雨
efa2ec6aac Merge branch 'master' of https://github.com/FloatTech/ZeroBot-Plugin 2022-08-11 19:23:12 +08:00
源文雨
eb8be40f77 fix: non-win /用法 (#370) 2022-08-11 19:22:58 +08:00
himawari
ae7e3c9c89 添加bilibilipush和完善gif文档 (#369)
*  添加bilibilipush和完善gif文档

* 🎨 修lint

* 🎨 日语语法标签匹配日文

* 🎨 优化发送逻辑和命令

* 🎨 错误处理

* 🎨 错误处理
2022-08-11 13:08:42 +08:00
莫思潋
4d68cc9fce fix: ImgFactory->Factory (#367)
* 🎨 改进代码样式

* fix: ImgFactory->Factory

* 🎨 改进代码样式

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2022-08-06 21:48:55 +08:00
源文雨
0226e2739a 优化 /服务列表 2022-08-06 15:48:22 +08:00
尐酱油
caa163a879 增加gif制图,菜单没写 (#363)
* Update context.go

* Update gif.go

* 🎨 改进代码样式

* Update png.go

* Update run.go

* 🎨 改进代码样式

* 修正素材地址

* Update run.go

* 呃,,,没改完整

* 继续修!!!

* Update png.go

* 🎨 改进代码样式

* Update gif.go

* 🎨 改进代码样式

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2022-08-06 00:06:16 +08:00
莫思潋
0e29010897 feat:将牌阵结果解析纳入转发列表 (#359) 2022-08-04 13:40:53 +08:00
源文雨
e9a8b99f4f 💩👌 make lint happy 2022-08-03 22:41:00 +08:00
莫思潋
27c637ba66 feat:小阿卡纳塔罗牌 (#346) 2022-07-29 15:26:04 +08:00
源文雨
305db3395c fix: 限速后使用功能直接导致插件崩溃 (#344) 2022-07-28 13:26:25 +08:00
himawari
3da37f0a3e Feature nihongo (#339)
*  添加日语语法学习

* 🎨 添加err
2022-07-24 23:28:30 +08:00
方柳煜
c30c9192f4 修复猜歌已知问题 (#340)
* Update main.go

* Update struct.go

* Update main.go

* Update main.go
2022-07-24 12:22:05 +08:00
源文雨
ceb3df513d 🔖 v1.5.0-beta5 2022-07-22 15:39:51 +08:00
WhitePaper
83372c4209 新增英文字符翻转插件 (#336)
*  新增英文字符翻转插件

* 🎨 代码规范优化

* 🎨 优化代码规范
2022-07-22 15:32:09 +08:00
himawari
20d5e014b7 🐛 支持json卡片 (#335)
Co-authored-by: xiaoguofan <1776620359@qq.com>
2022-07-22 11:50:38 +08:00
himawari
1c9a7eb158 🎨 修改bilibili,bilibiliparse插件结构,添加bilibili动态直播专栏解析 (#319)
* 🎨 修改bilibili,bilibiliparse插件结构,添加bilibili动态直播专栏解析

* 💩 修改大小写

* 🎨 正则全局,错误处理

* 🎨 使用json.NewDecoder()

* 💚 空使用

* 💚 修lint
2022-07-21 12:54:52 +08:00
方柳煜
31555cbf2f 修复群老婆闹离婚成功但实际没离问题 (#330) 2022-07-20 14:34:43 +08:00
方柳煜
4a1d4644ed 取消多个API依赖以提高稳定性,优化用户本地/API歌单可读性和自定义性 (#315) 2022-07-19 11:28:05 +08:00
源文雨
9182d214af ⬆️ update zb 2022-07-15 12:38:02 +08:00
源文雨
45e3383031 🔖 v1.5.0-beta4 2022-07-13 12:24:43 +08:00
himawari
fc91b69b55 添加新表情包 (#306)
*  添加新表情包

* 🐛 处理正则冲突

* 🎨 去掉反射
2022-07-12 12:25:45 +08:00
源文雨
13b6614dc2 🔖 v1.5.0-beta3 2022-07-08 21:40:51 +08:00
源文雨
fec5e4d73f 🐛 all sil 2022-07-08 21:34:23 +08:00
源文雨
585add29fc 🎨 edit README 2022-07-08 21:27:48 +08:00
源文雨
1e48e59cb7 #2: 全局启用、沉默 2022-07-08 21:20:24 +08:00
源文雨
41911af09a 🐛 b14 encode panic 2022-07-07 00:26:46 +08:00
源文雨
37b95e9559 🐛 edit README 2022-07-06 16:02:54 +08:00
源文雨
a7901745e8 🐛 response condition 2022-07-06 15:57:00 +08:00
源文雨
da9f085a50 🐛 create db panic 2022-07-06 14:59:03 +08:00
方柳煜
192c2a8bd6 修复网易云点歌API失效(405)问题 (#303)
* Update main.go

* Update selecter.go

* Update struct.go

* Update main.go
2022-07-06 13:44:24 +08:00
源文雨
e6c00e9b68 Merge branch 'master' of https://github.com/FloatTech/ZeroBot-Plugin 2022-07-05 13:45:46 +08:00
源文雨
1faa5aa5d8 control 增加 /响应 /沉默 2022-07-05 13:45:39 +08:00
源文雨
7e7191536a 🔖 v1.5.0-beta2 2022-07-03 18:28:18 +08:00
时雨◎星空
16ff1975ff 修复猜歌插件创建config.json错误 (#296) 2022-07-03 18:20:04 +08:00
源文雨
b32c86863e edit main 2022-07-03 10:24:33 +08:00
源文雨
d9b361f765 🔖 v1.5.0-beta1 2022-07-03 09:52:01 +08:00
Fox_white
5b2810e6c5 为猜歌插件添加config (#285)
*  猜歌添加config

* 📚 猜歌帮助信息更新

* 📚 猜歌帮助信息更新

*  猜歌错误信息继承

*  错误处理

*  错误处理ep2

* 🐛 猜歌插件bug修正和命名修改

*  猜歌插件不再使用gjson

* Update struct.go

* 修复-动漫2无法下载歌曲问题

* 🐛 Resolve Compilation error

* 🐛 Resolve Lint

* 🐛 Resolve Lint

Co-authored-by: 方柳煜 <101934327+fangliuyu@users.noreply.github.com>
2022-07-03 09:25:14 +08:00
github-actions[bot]
15a5b347e8 🎨 改进代码样式 2022-06-29 12:58:24 +00:00
方柳煜
477ffa825b 优化qqwife插件,新增猜歌插件 (#283)
* 猜歌插件

* 猜歌插件

* 猜歌插件

* 优化重置花名册指令

* Update main.go

* Update main.go

* Update main.go

* Update main.go

* Update main.go

* Update main.go

* Update main.go

* Update main.go

* Update main.go

* Update main.go

* Update main.go

* Update main.go

* Update README.md
2022-06-29 20:57:58 +08:00
Jiang-Red
84363b1513 删除无法使用的zaobao (#278)
* drop zaobao

* edit readme

* drop zaobao
2022-06-24 21:00:55 +08:00
方柳煜
01bf6f049b 修复存在误解的文案 (#272)
* 修复存在误解的文案

* Update qqmapwife.go
2022-06-19 20:51:47 +08:00
莫思潋
a8d93de000 feat:塔罗牌阵 (#268)
* 优化在两个命令中使用空格分隔的体验
- fortune的设置底图功能
- b14的加密功能

* 优化四个插件中使用空格分隔的体验
- 加密
- 哔哩哔哩推送
- 藏头诗
- 运势

* 优化并修正了上一个commit
- 加上了因为复制粘贴疏忽又没有注意测试遗漏的`?`
- 调整藏头诗和加密的正则触发,使其不必多此一举
- 删去了未被发现的测试代码

* - 删去了遗漏的Trim

* 优化了更多插件中使用空格的体验
- 优化了music bilibili image_finder 中使用空格的体验
- 补上了plugin_bilibili中未实现的vup开头触发
- 为plugin_bilibili_parse输出的消息加上一个换行符,优化排版

* - 调整funny
- 补回readme中bilibili_push的注释说明

* - 简化funny说明

* - 用暴力的办法使抽多张塔罗牌不重复
- 逆位的图片现在可以倒过来了

* - 解塔罗牌

* - 修改readme

* 大幅减少全局变量,简化抽塔罗牌输出

* - 更新了塔罗牌阵

* - 顺lint心意
- 添加判断避免重复装填

* 调整塔罗牌阵消息
- 用图片说明牌阵信息避免被封

* 调整DoOnceOnSuccess,减少冗余

* 🎨 改进代码样式

* 调整发送牌阵信息

* 🎨 改进代码样式

* 改回base64

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2022-06-18 16:51:11 +08:00
github-actions[bot]
2da5de3743 🎨 改进代码样式 2022-06-18 04:06:33 +00:00
方柳煜
bf6bac7be6 修复花名册会显示单身贵族的问题 (#269)
* 修复花名册会显示单身贵族的问题

* Update qqmapwife.go

* Update qqmapwife.go
2022-06-18 12:06:07 +08:00
源文雨
394d2e4a08 edit README 2022-06-17 11:59:26 +08:00
himawari
0dd447e270 添加txt转midi,优化midi转txt (#267)
* 🐛 长字符串发文件

*  添加txt转midi

*  音色自定义

*  添加windows脚本说明

* 🐛 修复某些midi读取不了的问题

*  多音轨转换

Co-authored-by: haibaraguo <haibaraguo@yeahka.com>
2022-06-17 11:46:09 +08:00
172 changed files with 17797 additions and 4363 deletions

BIN
.github/hua_nobg_512.gif vendored Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 187 KiB

View File

@@ -28,7 +28,7 @@ jobs:
- name: Setup Go environment
uses: actions/setup-go@master
with:
go-version: 1.18
go-version: 1.19
- name: Cache downloaded module
uses: actions/cache@master
with:

View File

@@ -8,7 +8,7 @@ jobs:
- name: Set up Go
uses: actions/setup-go@master
with:
go-version: 1.18
go-version: 1.19
- name: Check out code into the Go module directory
uses: actions/checkout@master

View File

@@ -8,7 +8,7 @@ jobs:
- name: Set up Go
uses: actions/setup-go@master
with:
go-version: 1.18
go-version: 1.19
- name: Check out code into the Go module directory
uses: actions/checkout@master
@@ -17,12 +17,17 @@ jobs:
uses: golangci/golangci-lint-action@master
with:
version: latest
args: --issues-exit-code=0
- name: Commit back
if: ${{ !github.head_ref }}
continue-on-error: true
run: |
git config --local user.name 'github-actions[bot]'
git config --local user.email '41898282+github-actions[bot]@users.noreply.github.com'
git add --all
git commit -m "🎨 改进代码样式"
git push
- name: Create Pull Request
if: ${{ !github.head_ref }}
continue-on-error: true
uses: peter-evans/create-pull-request@v4

View File

@@ -16,7 +16,7 @@ jobs:
- name: Set up Go
uses: actions/setup-go@master
with:
go-version: '1.18'
go-version: '1.19'
- name: Run GoReleaser
uses: goreleaser/goreleaser-action@master

BIN
.github/黒金.jpg vendored

Binary file not shown.

Before

Width:  |  Height:  |  Size: 191 KiB

3
.gitignore vendored
View File

@@ -5,4 +5,5 @@ plugins/*.dll
.vscode
go-zero*
nohup.out
zerobot
zerobot
ZeroBot-Plugin*

View File

@@ -18,7 +18,6 @@ linters:
fast: false
enable:
- bodyclose
- deadcode
- depguard
- dogsled
- errcheck
@@ -39,13 +38,11 @@ linters:
- nolintlint
- rowserrcheck
- staticcheck
- structcheck
- stylecheck
- typecheck
- unconvert
- unparam
- unused
- varcheck
- whitespace
- prealloc
- predeclared
@@ -62,7 +59,7 @@ run:
tests: false
skip-dirs:
- order
go: '1.18'
go: '1.19'
# output configuration options
output:
@@ -76,4 +73,5 @@ issues:
fix: true
exclude-use-default: false
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\+.*'

145
LICENSE
View File

@@ -1,5 +1,5 @@
GNU GENERAL PUBLIC LICENSE
Version 3, 29 June 2007
GNU AFFERO GENERAL PUBLIC LICENSE
Version 3, 19 November 2007
Copyright (C) 2007 Free Software Foundation, Inc. <https://fsf.org/>
Everyone is permitted to copy and distribute verbatim copies
@@ -7,17 +7,15 @@
Preamble
The GNU General Public License is a free, copyleft license for
software and other kinds of works.
The GNU Affero General Public License is a free, copyleft license for
software and other kinds of works, specifically designed to ensure
cooperation with the community in the case of network server software.
The licenses for most software and other practical works are designed
to take away your freedom to share and change the works. By contrast,
the GNU General Public License is intended to guarantee your freedom to
our General Public Licenses are intended to guarantee your freedom to
share and change all versions of a program--to make sure it remains free
software for all its users. We, the Free Software Foundation, use the
GNU General Public License for most of our software; it applies also to
any other work released this way by its authors. You can apply it to
your programs, too.
software for all its users.
When we speak of free software, we are referring to freedom, not
price. Our General Public Licenses are designed to make sure that you
@@ -26,44 +24,34 @@ them if you wish), that you receive source code or can get it if you
want it, that you can change the software or use pieces of it in new
free programs, and that you know you can do these things.
To protect your rights, we need to prevent others from denying you
these rights or asking you to surrender the rights. Therefore, you have
certain responsibilities if you distribute copies of the software, or if
you modify it: responsibilities to respect the freedom of others.
Developers that use our General Public Licenses protect your rights
with two steps: (1) assert copyright on the software, and (2) offer
you this License which gives you legal permission to copy, distribute
and/or modify the software.
For example, if you distribute copies of such a program, whether
gratis or for a fee, you must pass on to the recipients the same
freedoms that you received. You must make sure that they, too, receive
or can get the source code. And you must show them these terms so they
know their rights.
A secondary benefit of defending all users' freedom is that
improvements made in alternate versions of the program, if they
receive widespread use, become available for other developers to
incorporate. Many developers of free software are heartened and
encouraged by the resulting cooperation. However, in the case of
software used on network servers, this result may fail to come about.
The GNU General Public License permits making a modified version and
letting the public access it on a server without ever releasing its
source code to the public.
Developers that use the GNU GPL protect your rights with two steps:
(1) assert copyright on the software, and (2) offer you this License
giving you legal permission to copy, distribute and/or modify it.
The GNU Affero General Public License is designed specifically to
ensure that, in such cases, the modified source code becomes available
to the community. It requires the operator of a network server to
provide the source code of the modified version running there to the
users of that server. Therefore, public use of a modified version, on
a publicly accessible server, gives the public access to the source
code of the modified version.
For the developers' and authors' protection, the GPL clearly explains
that there is no warranty for this free software. For both users' and
authors' sake, the GPL requires that modified versions be marked as
changed, so that their problems will not be attributed erroneously to
authors of previous versions.
Some devices are designed to deny users access to install or run
modified versions of the software inside them, although the manufacturer
can do so. This is fundamentally incompatible with the aim of
protecting users' freedom to change the software. The systematic
pattern of such abuse occurs in the area of products for individuals to
use, which is precisely where it is most unacceptable. Therefore, we
have designed this version of the GPL to prohibit the practice for those
products. If such problems arise substantially in other domains, we
stand ready to extend this provision to those domains in future versions
of the GPL, as needed to protect the freedom of users.
Finally, every program is threatened constantly by software patents.
States should not allow patents to restrict development and use of
software on general-purpose computers, but in those that do, we wish to
avoid the special danger that patents applied to a free program could
make it effectively proprietary. To prevent this, the GPL assures that
patents cannot be used to render the program non-free.
An older license, called the Affero General Public License and
published by Affero, was designed to accomplish similar goals. This is
a different license, not a version of the Affero GPL, but Affero has
released a new version of the Affero GPL which permits relicensing under
this license.
The precise terms and conditions for copying, distribution and
modification follow.
@@ -72,7 +60,7 @@ modification follow.
0. Definitions.
"This License" refers to version 3 of the GNU General Public License.
"This License" refers to version 3 of the GNU Affero General Public License.
"Copyright" also means copyright-like laws that apply to other kinds of
works, such as semiconductor masks.
@@ -549,35 +537,45 @@ to collect a royalty for further conveying from those to whom you convey
the Program, the only way you could satisfy both those terms and this
License would be to refrain entirely from conveying the Program.
13. Use with the GNU Affero General Public License.
13. Remote Network Interaction; Use with the GNU General Public License.
Notwithstanding any other provision of this License, if you modify the
Program, your modified version must prominently offer all users
interacting with it remotely through a computer network (if your version
supports such interaction) an opportunity to receive the Corresponding
Source of your version by providing access to the Corresponding Source
from a network server at no charge, through some standard or customary
means of facilitating copying of software. This Corresponding Source
shall include the Corresponding Source for any work covered by version 3
of the GNU General Public License that is incorporated pursuant to the
following paragraph.
Notwithstanding any other provision of this License, you have
permission to link or combine any covered work with a work licensed
under version 3 of the GNU Affero General Public License into a single
under version 3 of the GNU General Public License into a single
combined work, and to convey the resulting work. The terms of this
License will continue to apply to the part which is the covered work,
but the special requirements of the GNU Affero General Public License,
section 13, concerning interaction through a network will apply to the
combination as such.
but the work with which it is combined will remain governed by version
3 of the GNU General Public License.
14. Revised Versions of this License.
The Free Software Foundation may publish revised and/or new versions of
the GNU General Public License from time to time. Such new versions will
be similar in spirit to the present version, but may differ in detail to
the GNU Affero General Public License from time to time. Such new versions
will be similar in spirit to the present version, but may differ in detail to
address new problems or concerns.
Each version is given a distinguishing version number. If the
Program specifies that a certain numbered version of the GNU General
Program specifies that a certain numbered version of the GNU Affero General
Public License "or any later version" applies to it, you have the
option of following the terms and conditions either of that numbered
version or of any later version published by the Free Software
Foundation. If the Program does not specify a version number of the
GNU General Public License, you may choose any version ever published
GNU Affero General Public License, you may choose any version ever published
by the Free Software Foundation.
If the Program specifies that a proxy can decide which future
versions of the GNU General Public License can be used, that proxy's
versions of the GNU Affero General Public License can be used, that proxy's
public statement of acceptance of a version permanently authorizes you
to choose that version for the Program.
@@ -635,40 +633,29 @@ the "copyright" line and a pointer to where the full notice is found.
Copyright (C) <year> <name of author>
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
it under the terms of the GNU Affero General Public License as published
by the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
GNU Affero General Public License for more details.
You should have received a copy of the GNU General Public License
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
Also add information on how to contact you by electronic and paper mail.
If the program does terminal interaction, make it output a short
notice like this when it starts in an interactive mode:
<program> Copyright (C) <year> <name of author>
This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
This is free software, and you are welcome to redistribute it
under certain conditions; type `show c' for details.
The hypothetical commands `show w' and `show c' should show the appropriate
parts of the General Public License. Of course, your program's commands
might be different; for a GUI interface, you would use an "about box".
If your software can interact with users remotely through a computer
network, you should also make sure that it provides a way for users to
get its source. For example, if your program is a web application, its
interface could display a "Source" link that leads users to an archive
of the code. There are many ways you could offer source, and different
solutions will be better for different programs; see section 13 for the
specific requirements.
You should also get your employer (if you work as a programmer) or school,
if any, to sign a "copyright disclaimer" for the program, if necessary.
For more information on this, and how to apply and follow the GNU GPL, see
<https://www.gnu.org/licenses/>.
The GNU General Public License does not permit incorporating your program
into proprietary programs. If your program is a subroutine library, you
may consider it more useful to permit linking proprietary applications with
the library. If this is what you want to do, use the GNU Lesser General
Public License instead of this License. But first, please read
<https://www.gnu.org/licenses/why-not-lgpl.html>.
For more information on this, and how to apply and follow the GNU AGPL, see
<https://www.gnu.org/licenses/>.

724
README.md

File diff suppressed because it is too large Load Diff

View File

@@ -1,13 +0,0 @@
package main
import (
zero "github.com/wdvxdr1123/ZeroBot"
"github.com/wdvxdr1123/ZeroBot/driver"
)
type zbpcfg struct {
Z zero.Config `json:"zero"`
W []*driver.WSClient `json:"ws"`
}
var config zbpcfg

2
data

Submodule data updated: 5ce4b48c12...5e0fa81c3b

115
go.mod
View File

@@ -1,77 +1,98 @@
module github.com/FloatTech/ZeroBot-Plugin
go 1.18
go 1.19
require (
github.com/FloatTech/AnimeAPI v1.4.1-0.20220613042537-0adf8c5616ec
github.com/FloatTech/sqlite v0.3.2
github.com/FloatTech/zbpctrl v1.4.1-0.20220610074608-425160596f27
github.com/FloatTech/zbputils v1.4.1-0.20220613042833-33e22060e8d9
github.com/Baidu-AIP/golang-sdk v1.1.1
github.com/FloatTech/AnimeAPI v1.6.1-0.20230207081411-573533b18194
github.com/FloatTech/floatbox v0.0.0-20230207075003-0f70b30c320d
github.com/FloatTech/gg v1.1.2
github.com/FloatTech/imgfactory v0.2.2-0.20230215052637-9f7b05520ca9
github.com/FloatTech/rendercard v0.0.10-0.20230215092509-ff0745852f23
github.com/FloatTech/sqlite v1.5.7
github.com/FloatTech/ttl v0.0.0-20220715042055-15612be72f5b
github.com/FloatTech/zbpctrl v1.5.3-0.20230130095145-714ad318cd52
github.com/FloatTech/zbputils v1.6.2-0.20230215092613-4a7ebf458f16
github.com/RomiChan/syncx v0.0.0-20221202055724-5f842c53020e
github.com/RomiChan/websocket v1.4.3-0.20220227141055-9b2c6168c9c5
github.com/antchfx/htmlquery v1.2.5
github.com/corona10/goimagehash v1.0.3
github.com/fogleman/gg v1.3.0
github.com/corona10/goimagehash v1.1.0
github.com/davidscholberg/go-durationfmt v0.0.0-20170122144659-64843a2083d3
github.com/disintegration/imaging v1.6.2
github.com/fumiama/ahsai v0.1.0
github.com/fumiama/cron v1.3.0
github.com/fumiama/go-base16384 v1.5.3
github.com/fumiama/go-registry v0.1.6
github.com/fumiama/gofastTEA v0.0.10
github.com/fumiama/go-base16384 v1.6.4
github.com/fumiama/go-registry v0.2.5
github.com/fumiama/gotracemoe v0.0.3
github.com/fumiama/sqlite3 v1.14.6
github.com/fumiama/jieba v0.0.0-20221203025406-36c17a10b565
github.com/fumiama/unibase2n v0.0.0-20221020155353-02876e777430
github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0
github.com/jinzhu/gorm v1.9.16
github.com/jozsefsallai/gophersauce v1.0.1
github.com/lucas-clemente/quic-go v0.27.2
github.com/mroth/weightedrand v0.4.1
github.com/pkg/errors v0.8.1
github.com/pkumza/numcn v1.0.0
github.com/shirou/gopsutil/v3 v3.22.3
github.com/sirupsen/logrus v1.8.1
github.com/tidwall/gjson v1.14.1
github.com/lithammer/fuzzysearch v1.1.5
github.com/mroth/weightedrand v1.0.0
github.com/pkg/errors v0.9.1
github.com/quic-go/quic-go v0.32.0
github.com/shirou/gopsutil/v3 v3.23.1
github.com/sirupsen/logrus v1.9.0
github.com/tidwall/gjson v1.14.4
github.com/wcharczuk/go-chart/v2 v2.1.0
github.com/wdvxdr1123/ZeroBot v1.5.2-0.20220610070647-9eeffcb277ee
gitlab.com/gomidi/midi v1.23.7
gitlab.com/gomidi/midi/v2 v2.0.17
golang.org/x/image v0.0.0-20220601225756-64ec528b34cd
github.com/wdvxdr1123/ZeroBot v1.6.9
gitlab.com/gomidi/midi/v2 v2.0.25
golang.org/x/image v0.3.0
golang.org/x/text v0.6.0
gopkg.in/yaml.v3 v3.0.1
)
require (
github.com/RomiChan/syncx v0.0.0-20220404072119-d7ea0ae15a4c // indirect
github.com/RomiChan/websocket v1.4.3-0.20220123145318-307a86b127bc // indirect
github.com/antchfx/xpath v1.2.1 // indirect
github.com/cheekybits/genny v1.0.0 // indirect
github.com/disintegration/imaging v1.6.2 // indirect
github.com/ericpauley/go-quantize v0.0.0-20200331213906-ae555eb2afa4 // indirect
github.com/fsnotify/fsnotify v1.4.9 // indirect
github.com/faiface/beep v1.1.0 // indirect
github.com/fumiama/go-simple-protobuf v0.1.0 // indirect
github.com/fumiama/gofastTEA v0.0.10 // indirect
github.com/fumiama/imgsz v0.0.2 // indirect
github.com/gabriel-vasile/mimetype v1.0.4 // indirect
github.com/go-ole/go-ole v1.2.6 // indirect
github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0 // indirect
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect
github.com/golang/mock v1.6.0 // indirect
github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38 // indirect
github.com/google/uuid v1.3.0 // indirect
github.com/hajimehoshi/oto v0.7.1 // indirect
github.com/jfreymuth/oggvorbis v1.0.1 // indirect
github.com/jfreymuth/vorbis v1.0.0 // indirect
github.com/jinzhu/inflection v1.0.0 // indirect
github.com/kr/pretty v0.3.1 // indirect
github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0 // indirect
github.com/marten-seemann/qpack v0.2.1 // indirect
github.com/marten-seemann/qtls-go1-16 v0.1.5 // indirect
github.com/marten-seemann/qtls-go1-17 v0.1.2 // indirect
github.com/marten-seemann/qtls-go1-18 v0.1.2 // indirect
github.com/mattn/go-isatty v0.0.14 // indirect
github.com/mattn/go-isatty v0.0.16 // indirect
github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646 // indirect
github.com/nxadm/tail v1.4.8 // indirect
github.com/onsi/ginkgo v1.16.4 // indirect
github.com/onsi/ginkgo/v2 v2.2.0 // indirect
github.com/pkumza/numcn v1.0.0 // indirect
github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c // indirect
github.com/quic-go/qpack v0.4.0 // indirect
github.com/quic-go/qtls-go1-18 v0.2.0 // indirect
github.com/quic-go/qtls-go1-19 v0.2.0 // indirect
github.com/quic-go/qtls-go1-20 v0.1.0 // indirect
github.com/remyoudompheng/bigfft v0.0.0-20200410134404-eec4a21b6bb0 // indirect
github.com/tidwall/match v1.1.1 // indirect
github.com/tidwall/pretty v1.2.0 // indirect
github.com/tklauser/go-sysconf v0.3.10 // indirect
github.com/tklauser/numcpus v0.4.0 // indirect
github.com/tklauser/go-sysconf v0.3.11 // indirect
github.com/tklauser/numcpus v0.6.0 // indirect
github.com/yusufpapurcu/wmi v1.2.2 // indirect
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9 // indirect
golang.org/x/mod v0.4.2 // indirect
golang.org/x/net v0.0.0-20220607020251-c690dde0001d // indirect
golang.org/x/sys v0.0.0-20220608164250-635b8c9b7f68 // indirect
golang.org/x/text v0.3.7 // indirect
golang.org/x/tools v0.1.1 // indirect
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 // indirect
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 // indirect
modernc.org/libc v1.16.8 // indirect
modernc.org/mathutil v1.4.1 // indirect
modernc.org/memory v1.1.1 // indirect
golang.org/x/crypto v0.4.0 // indirect
golang.org/x/exp v0.0.0-20221205204356-47842c84f3db // indirect
golang.org/x/exp/shiny v0.0.0-20221126150942-6ab00d035af9 // indirect
golang.org/x/mobile v0.0.0-20201217150744-e6ae53a27f4f // indirect
golang.org/x/mod v0.6.0 // indirect
golang.org/x/net v0.4.0 // indirect
golang.org/x/sys v0.4.0 // indirect
golang.org/x/tools v0.2.0 // indirect
modernc.org/libc v1.21.5 // indirect
modernc.org/mathutil v1.5.0 // indirect
modernc.org/memory v1.4.0 // indirect
modernc.org/sqlite v1.20.0 // indirect
)
replace modernc.org/sqlite => github.com/fumiama/sqlite3 v1.20.0-with-win386
replace github.com/remyoudompheng/bigfft => github.com/fumiama/bigfft v0.0.0-20211011143303-6e0bfa3c836b

615
go.sum
View File

@@ -1,133 +1,123 @@
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
cloud.google.com/go v0.31.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
cloud.google.com/go v0.37.0/go.mod h1:TS1dMSSfndXH133OKGwekG838Om/cQT0BUHV3HcBgoo=
dmitri.shuralyov.com/app/changes v0.0.0-20180602232624-0a106ad413e3/go.mod h1:Yl+fi1br7+Rr3LqpNJf1/uxUdtRUV+Tnj0o93V2B9MU=
dmitri.shuralyov.com/html/belt v0.0.0-20180602232347-f7d459c86be0/go.mod h1:JLBrvjyP0v+ecvNYvCpyZgu5/xkfAUhi6wJj28eUfSU=
dmitri.shuralyov.com/service/change v0.0.0-20181023043359-a85b471d5412/go.mod h1:a1inKt/atXimZ4Mv927x+r7UpyzRUf4emIoiiSC2TN4=
dmitri.shuralyov.com/state v0.0.0-20180228185332-28bcc343414c/go.mod h1:0PRwlb0D6DFvNNtx+9ybjezNCa8XF0xaYcETyp6rHWU=
git.apache.org/thrift.git v0.0.0-20180902110319-2566ecd5d999/go.mod h1:fPE2ZNJGynbRyZ4dJvy6G277gSllfV2HJqblrnkyeyg=
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
github.com/FloatTech/AnimeAPI v1.4.1-0.20220613042537-0adf8c5616ec h1:Hl/BxaQoQFEJUIGmdhR0NyBvZChb7qkfCrF916bNxnI=
github.com/FloatTech/AnimeAPI v1.4.1-0.20220613042537-0adf8c5616ec/go.mod h1:MrCZ7P6WvF0JWHMeedAx6fVm5BP1s6/RUy0r0Jvqrds=
github.com/FloatTech/sqlite v0.3.2 h1:iTg2ZKnzjjZAdlSN3hXmpCBn15odc4Ud484OoM3yXGA=
github.com/FloatTech/sqlite v0.3.2/go.mod h1:VFtLofV5qxw5eBneZRbWwD451SLSm50o9J3J43iB1iw=
github.com/FloatTech/zbpctrl v1.4.1-0.20220610074608-425160596f27 h1:C+D30vpxfgbJetTFXWAHzuU8GydbFb/A8Kv6E3PdRS4=
github.com/FloatTech/zbpctrl v1.4.1-0.20220610074608-425160596f27/go.mod h1:5FDkrlVaQCxUfeqH7XJPTfej0q+y9fzImhvZI4ofu9Y=
github.com/FloatTech/zbputils v1.4.1-0.20220613042833-33e22060e8d9 h1:IeUs08sUqdR/g8DfxPNTSBfsE1g0OtmffLNgKzbrVZ4=
github.com/FloatTech/zbputils v1.4.1-0.20220613042833-33e22060e8d9/go.mod h1:A9AeVHZsv5chyw8p4fDI0cHnEOfMpmsTLoLWqUT7TO4=
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/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=
github.com/DATA-DOG/go-sqlmock v1.3.3/go.mod h1:f/Ixk793poVmq4qj/V1dPUg2JEAKC73Q5eFN3EC/SaM=
github.com/FloatTech/AnimeAPI v1.6.1-0.20230207081411-573533b18194 h1:+p85V0fdZNt3UoKreBeI6S3IOPvNmU5/Fnn39ipSkVk=
github.com/FloatTech/AnimeAPI v1.6.1-0.20230207081411-573533b18194/go.mod h1:LCiKZivPVazRRCnnADQfIBFZiIPHHCFqaCpwZWsFehI=
github.com/FloatTech/floatbox v0.0.0-20230207075003-0f70b30c320d h1:vVqMO5Kejt5pueuZvBxfX+psa7UwE110TpITf5eV7Zw=
github.com/FloatTech/floatbox v0.0.0-20230207075003-0f70b30c320d/go.mod h1:OoZE4Ra7olpFaJSrlD6mcyT4chPLg9QBRE1pzTC8R84=
github.com/FloatTech/gg v1.1.2 h1:YolgOYg3uDHc1+g0bLtt6QuRA/pvLn+b9IBCIhOOX88=
github.com/FloatTech/gg v1.1.2/go.mod h1:uzPzAeT35egARdRuu+1oyjU3CmTwCceoq3Vvje7LpcI=
github.com/FloatTech/imgfactory v0.2.2-0.20230215052637-9f7b05520ca9 h1:Havq0z/N79KeD50L7ms+Hv8F4Sw98Dt8lXM8jECp04o=
github.com/FloatTech/imgfactory v0.2.2-0.20230215052637-9f7b05520ca9/go.mod h1:el5hGpj1C1bDRxcTXYRwEivDCr40zZeJpcrLrB1fajs=
github.com/FloatTech/rendercard v0.0.10-0.20230215092509-ff0745852f23 h1:cffHoKe1wlm1kqmtpJ4ur/7h+E3gR1fzbaPkXAlVj38=
github.com/FloatTech/rendercard v0.0.10-0.20230215092509-ff0745852f23/go.mod h1:dsENe3jJB1LHbshfoGnIAU9V6+N1CKbf2y+RUeAPC3A=
github.com/FloatTech/sqlite v1.5.7 h1:Bvo4LSojcZ6dVtbHrkqvt6z4v8e+sj0G5PSUIvdawsk=
github.com/FloatTech/sqlite v1.5.7/go.mod h1:zFbHzRfB+CJ+VidfjuVbrcin3DAz283F7hF1hIeHzpY=
github.com/FloatTech/ttl v0.0.0-20220715042055-15612be72f5b h1:tvciXWq2nuvTbFeJGLDNIdRX3BI546D3O7k7vrVueZw=
github.com/FloatTech/ttl v0.0.0-20220715042055-15612be72f5b/go.mod h1:fHZFWGquNXuHttu9dUYoKuNbm3dzLETnIOnm1muSfDs=
github.com/FloatTech/zbpctrl v1.5.3-0.20230130095145-714ad318cd52 h1:BrStRXeosWh8L0iA/EjPd8w6xNexDkqki39ITZko/9Q=
github.com/FloatTech/zbpctrl v1.5.3-0.20230130095145-714ad318cd52/go.mod h1:qqMLUwR7tKpqnAqsgI7aZbn0hbs2FEVF4ylMXqIpBdY=
github.com/FloatTech/zbputils v1.6.2-0.20230215092613-4a7ebf458f16 h1:fOnI0V//DOW62vAhnC1a+qDMnlbJIAIiUHVX7CaUk5s=
github.com/FloatTech/zbputils v1.6.2-0.20230215092613-4a7ebf458f16/go.mod h1:Lo7y67u9EGt7l3ZBavc5bbryEIaeRYfbVy9KWIw6qk4=
github.com/PuerkitoBio/goquery v1.5.1/go.mod h1:GsLWisAFVj4WgDibEWF4pvYnkVQBpKBKeU+7zCJoLcc=
github.com/RomiChan/syncx v0.0.0-20220404072119-d7ea0ae15a4c h1:cNPOdTNiVwxLpROLjXCgbIPvdkE+BwvxDvgmdYmWx6Q=
github.com/RomiChan/syncx v0.0.0-20220404072119-d7ea0ae15a4c/go.mod h1:KqZzu7slNKROh3TSYEH/IUMG6f4M+1qubZ5e52QypsE=
github.com/RomiChan/websocket v1.4.3-0.20220123145318-307a86b127bc h1:AAx50/fb/xS4lvsdQg+bFbGvqSDhyV1MF+p2PLCamZ0=
github.com/RomiChan/websocket v1.4.3-0.20220123145318-307a86b127bc/go.mod h1:OMmITAib6POA37xCichWM0aRnoVpSMZO1rB/G01wrr0=
github.com/RomiChan/syncx v0.0.0-20221202055724-5f842c53020e h1:wR3MXQ3VbUlPKOOUwLOYgh/QaJThBTYtsl673O3lqSA=
github.com/RomiChan/syncx v0.0.0-20221202055724-5f842c53020e/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.20220227141055-9b2c6168c9c5/go.mod h1:0UcFaCkhp6vZw6l5Dpq0Dp673CoF9GdvA8lTfst0GiU=
github.com/andybalholm/cascadia v1.1.0/go.mod h1:GsXiBklL0woXo1j/WYWtSYYC4ouU9PqHO0sqidkEA4Y=
github.com/anmitsu/go-shlex v0.0.0-20161002113705-648efa622239/go.mod h1:2FmKhYUyUczH0OGQWaF5ceTx0UBShxjsH6f8oGKYe2c=
github.com/antchfx/htmlquery v1.2.5 h1:1lXnx46/1wtv1E/kzmH8vrfMuUKYgkdDBA9pIdMJnk4=
github.com/antchfx/htmlquery v1.2.5/go.mod h1:2MCVBzYVafPBmKbrmwB9F5xdd+IEgRY61ci2oOsOQVw=
github.com/antchfx/xpath v1.2.1 h1:qhp4EW6aCOVr5XIkT+l6LJ9ck/JsUH/yyauNgTQkBF8=
github.com/antchfx/xpath v1.2.1/go.mod h1:i54GszH55fYfBmoZXapTHN8T8tkcHfRgLyVwwqzXNcs=
github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=
github.com/bradfitz/go-smtpd v0.0.0-20170404230938-deb6d6237625/go.mod h1:HYsPBTaaSFSlLx/70C2HPIMNZpVV8+vt/A+FMnYP11g=
github.com/buger/jsonparser v0.0.0-20181115193947-bf1c66bbce23/go.mod h1:bbYlZJ7hK1yFx9hf58LP0zeX7UjIGs20ufpu3evjr+s=
github.com/cheekybits/genny v1.0.0 h1:uGGa4nei+j20rOSeDeP5Of12XVm7TGUd4dJA9RDitfE=
github.com/cheekybits/genny v1.0.0/go.mod h1:+tQajlRqAUrPI7DOSpB0XAqZYtQakVtB7wXkRAgjxjQ=
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
github.com/coreos/go-systemd v0.0.0-20181012123002-c6f51f82210d/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
github.com/corona10/goimagehash v1.0.3 h1:NZM518aKLmoNluluhfHGxT3LGOnrojrxhGn63DR/CZA=
github.com/corona10/goimagehash v1.0.3/go.mod h1:VkvE0mLn84L4aF8vCb6mafVajEb6QYMHl2ZJLn0mOGI=
github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI=
github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI=
github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU=
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/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.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davidscholberg/go-durationfmt v0.0.0-20170122144659-64843a2083d3 h1:qshMBFxVjYjzI+kwvWvgoByF3uMCvnJiaK8KslWAbr8=
github.com/davidscholberg/go-durationfmt v0.0.0-20170122144659-64843a2083d3/go.mod h1:M9fx6rAdHSYLKxXPgUXGgblb586CA7ceNrpu4DEc2No=
github.com/denisenkom/go-mssqldb v0.0.0-20191124224453-732737034ffd h1:83Wprp6ROGeiHFAP8WJdI2RoxALQYgdllERc3N5N2DM=
github.com/denisenkom/go-mssqldb v0.0.0-20191124224453-732737034ffd/go.mod h1:xbL0rPBG9cCiLr28tMa8zpbdarY27NDyej4t/EjAShU=
github.com/disintegration/imaging v1.6.2 h1:w1LecBlG2Lnp8B3jk5zSuNqd7b4DXhcjwek1ei82L+c=
github.com/disintegration/imaging v1.6.2/go.mod h1:44/5580QXChDfwIclfc/PCwrr44amcmDAg8hxG0Ewe4=
github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk=
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/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/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568/go.mod h1:xEzjJPgXI435gkrCt3MPfRiAkVrwSbHsst4LCFVfpJc=
github.com/fogleman/gg v1.3.0 h1:/7zJX8F6AaYQc57WQCyN9cAIz+4bCJGO9B+dyW29am8=
github.com/fogleman/gg v1.3.0/go.mod h1:R/bRT+9gY/C5z7JzPU0zXsXHKM4/ayA+zqcVNZzPa1k=
github.com/francoispqt/gojay v1.2.13/go.mod h1:ehT5mTG4ua4581f1++1WLG0vPdaA9HaiDsoyrBGkyDY=
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
github.com/fsnotify/fsnotify v1.4.9 h1:hsms1Qyu0jgnwNXIxa+/V/PDsU6CfLf6CNO8H7IWoS4=
github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ=
github.com/faiface/beep v1.1.0 h1:A2gWP6xf5Rh7RG/p9/VAW2jRSDEGQm5sbOb38sf5d4c=
github.com/faiface/beep v1.1.0/go.mod h1:6I8p6kK2q4opL/eWb+kAkk38ehnTunWeToJB+s51sT4=
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/bigfft v0.0.0-20211011143303-6e0bfa3c836b h1:Zt3pFQditAdWTHCOVkiloc9ZauBoWrb37guFV4iIRvE=
github.com/fumiama/bigfft v0.0.0-20211011143303-6e0bfa3c836b/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo=
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/go-base16384 v1.5.3 h1:IUZUlm2ajJB1nEJzauP6yD5IeJoVHyBEkzKJf9O82zs=
github.com/fumiama/go-base16384 v1.5.3/go.mod h1:OEn+947GV5gsbTAnyuUW/SrfxJYUdYupSIQXOuGOcXM=
github.com/fumiama/go-registry v0.1.6 h1:Ee/tXCCIR/xt8celhbbw0W/xDMdhAXLwy2YFBB/LWFk=
github.com/fumiama/go-registry v0.1.6/go.mod h1:dIUVbiOgfk9oZcsgwDvNLC72i+ctibVukSXR/9bLviI=
github.com/fumiama/go-base16384 v1.6.4 h1:rYDRwD/th2cG4U7QLokpzmST1cCxZGXtHmolOUePt5o=
github.com/fumiama/go-base16384 v1.6.4/go.mod h1:OEn+947GV5gsbTAnyuUW/SrfxJYUdYupSIQXOuGOcXM=
github.com/fumiama/go-registry v0.2.5 h1:Y6tnHnTThQPv7E4JPM2vBprU+4EQw/LEDO33HCmxgI4=
github.com/fumiama/go-registry v0.2.5/go.mod h1:GP45kejHuDLFxcWdksrt75r5rHBqYwtfeUl3JzGWxfQ=
github.com/fumiama/go-simple-protobuf v0.1.0 h1:rLzJgNqB6LHNDVMl81yyNt6ZKziWtVfu+ioF0edlEVw=
github.com/fumiama/go-simple-protobuf v0.1.0/go.mod h1:5yYNapXq1tQMOZg9bOIVhQlZk9pQqpuFIO4DZLbsdy4=
github.com/fumiama/gofastTEA v0.0.10 h1:JJJ+brWD4kie+mmK2TkspDXKzqq0IjXm89aGYfoGhhQ=
github.com/fumiama/gofastTEA v0.0.10/go.mod h1:RIdbYZyB4MbH6ZBlPymRaXn3cD6SedlCu5W/HHfMPBk=
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/sqlite3 v1.14.6 h1:+e+iygyiDXQJVi7xeXIviBvR7hAc5y20WA9hRwfKn10=
github.com/fumiama/sqlite3 v1.14.6/go.mod h1:Xx9a2/OtHuy9pBjow0N+bE/RhNeZ7zZz5xh25vqbA5A=
github.com/fumiama/imgsz v0.0.2 h1:fAkC0FnIscdKOXwAxlyw3EUba5NzxZdSxGaq3Uyfxak=
github.com/fumiama/imgsz v0.0.2/go.mod h1:dR71mI3I2O5u6+PCpd47M9TZptzP+39tRBcbdIkoqM4=
github.com/fumiama/jieba v0.0.0-20221203025406-36c17a10b565 h1:sQuR2+N5HurnvsZhiKdEg+Ig354TaqgCQRxd/0KgIOQ=
github.com/fumiama/jieba v0.0.0-20221203025406-36c17a10b565/go.mod h1:UUEvyLTJ7yoOA/viKG4wEis4ERydM7+Ny6gZUWgkS80=
github.com/fumiama/sqlite3 v1.20.0-with-win386 h1:ZR1AXGBEtkfq9GAXehOVcwn+aaCG8itrkgEsz4ggx5k=
github.com/fumiama/sqlite3 v1.20.0-with-win386/go.mod h1:Os58MHwYCcYZCy2PGChBrQtBAw5/LS1ZZOkfc+C/I7s=
github.com/fumiama/unibase2n v0.0.0-20221020155353-02876e777430 h1:XL4SnagpaVHYybnnj6whQxmt8Ps9/kaG6sCNn4X1GGA=
github.com/fumiama/unibase2n v0.0.0-20221020155353-02876e777430/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/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
github.com/gliderlabs/ssh v0.1.1/go.mod h1:U7qILu1NlMHj9FlMhZLlkCdDnU1DBEAqr0aevW3Awn0=
github.com/go-errors/errors v1.0.1/go.mod h1:f4zRHt4oKfwPJE5k8C9vpYG+aDHdBFUsgrm6/TyX73Q=
github.com/gdamore/encoding v1.0.0/go.mod h1:alR0ol34c49FCSBLjhosxzcPHQbf2trDkoo5dl+VrEg=
github.com/gdamore/tcell v1.3.0/go.mod h1:Hjvr+Ofd+gLglo7RYKxxnzCBmev3BzsS67MebKS4zMM=
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-playground/assert/v2 v2.0.1 h1:MsBgLAaY856+nPRTKrp3/OZK38U/wa0CcBYNjji3q3A=
github.com/go-playground/assert/v2 v2.2.0 h1:JvknZsQTYeFEAhQwI4qEt9cyV5ONwRHC+lYKSsYSR8s=
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-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0 h1:p104kn46Q8WdvHunIJ9dAyjPVtrBPhSr3KT2yUst43I=
github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE=
github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
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/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/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
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/lint v0.0.0-20180702182130-06c8688daad7/go.mod h1:tluoj9z5200jBnyusfRPU2LqT6J+DAorxEvtC7LHB+E=
github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
github.com/golang/mock v1.4.4/go.mod h1:l3mdAwkq5BuhzHwde/uurv3sEJeZMXNpwsxVWU71h+4=
github.com/golang/mock v1.6.0 h1:ErTB+efbowRARo13NNdxyJji2egdxLGQhRaY+DUumQc=
github.com/golang/mock v1.6.0/go.mod h1:p6yTPP+5HYm5mzsMV8JkE6ZKdX+/wYM6Hr+LicevLPs=
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8=
github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA=
github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs=
github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w=
github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0=
github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
github.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw=
github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.3/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.7 h1:81/ik6ipDQS2aGcBfIN5dHDB36BwrStyeAQquSYCV4o=
github.com/google/go-cmp v0.5.7/go.mod h1:n+brtR0CgQNWTVd5ZUFpTBC8YFBDLK/h/bpaJ8/DtOE=
github.com/google/go-github v17.0.0+incompatible/go.mod h1:zLgOLi98H3fifZn+44m+umXrS52loVEgC2AApnigrVQ=
github.com/google/go-querystring v1.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck=
github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs=
github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38=
github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38 h1:yAJXTCF9TqKcTiHJAE8dj7HMvPfh66eeA2JYW7eFpSE=
github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I=
github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/googleapis/gax-go v2.0.0+incompatible/go.mod h1:SFVmujtThgffbyetf+mdk2eWhX2bMyUtNHzFKcPA9HY=
github.com/googleapis/gax-go/v2 v2.0.3/go.mod h1:LLvjysVCY1JZeum8Z6l8qUty8fiNwE08qbEPm1M08qg=
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
github.com/gregjones/httpcache v0.0.0-20180305231024-9cad4c3443a7/go.mod h1:FecbI9+v66THATjSRHfNgh1IVFe/9kFxbXtjV0ctIMA=
github.com/grpc-ecosystem/grpc-gateway v1.5.0/go.mod h1:RSKVYQBd5MCa4OVpNdGskqpgL2+G+NZTnrVHpWWfpdw=
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
github.com/jellevandenhooff/dkim v0.0.0-20150330215556-f50fe3d243e1/go.mod h1:E0B/fFc00Y+Rasa88328GlI/XbtyysCtTHZS8h7IrBU=
github.com/hajimehoshi/go-mp3 v0.3.0/go.mod h1:qMJj/CSDxx6CGHiZeCgbiq2DSUkbK0UbtXShQcnfyMM=
github.com/hajimehoshi/oto v0.6.1/go.mod h1:0QXGEkbuJRohbJaxr7ZQSxnju7hEhseiPx2hrh6raOI=
github.com/hajimehoshi/oto v0.7.1 h1:I7maFPz5MBCwiutOrz++DLdbr4rTzBsbBuV2VpgU9kk=
github.com/hajimehoshi/oto v0.7.1/go.mod h1:wovJ8WWMfFKvP587mhHgot/MBr4DnNy9m6EepeVGnos=
github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
github.com/icza/bitio v1.0.0/go.mod h1:0jGnlLAx8MKMr9VGnn/4YrvZiprkvBelsVIbA9Jjr9A=
github.com/icza/mighty v0.0.0-20180919140131-cfd07d671de6/go.mod h1:xQig96I1VNBDIWGCdTt54nHt6EeI639SmHycLYL7FkA=
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/go.mod h1:G3LB3wezTOWM2ITLzPxEXgSkOXAntiLHS7UdBefADcs=
github.com/jinzhu/inflection v1.0.0 h1:K317FqzuhWc8YvSVlFMCCUb36O/S9MCKRDI7QkRKD/E=
@@ -136,414 +126,183 @@ 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/jozsefsallai/gophersauce v1.0.1 h1:BA3ovtQRrAb1qYU9JoRLbDHpxnDunlNcEkEfhCvDDCM=
github.com/jozsefsallai/gophersauce v1.0.1/go.mod h1:YVEI7djliMTmZ1Vh01YPF8bUHi+oKhe3yXgKf1T49vg=
github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU=
github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51/go.mod h1:CzGEWj7cYgsdH8dAjBGEr58BoE7ScuLd+fwFZ44+/x8=
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/kr/pty v1.1.3/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
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/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
github.com/lucas-clemente/quic-go v0.27.2 h1:zsMwwniyybb8B/UDNXRSYee7WpQJVOcjQEGgpw2ikXs=
github.com/lucas-clemente/quic-go v0.27.2/go.mod h1:vXgO/11FBSKM+js1NxoaQ/bPtVFYfB7uxhfHXyMhl1A=
github.com/lithammer/fuzzysearch v1.1.5 h1:Ag7aKU08wp0R9QCfF4GoGST9HbmAIeLP7xwMrOBEp1c=
github.com/lithammer/fuzzysearch v1.1.5/go.mod h1:1R1LRNk7yKid1BaQkmuLQaHruxcC4HmAH30Dh61Ih1Q=
github.com/lucasb-eyer/go-colorful v1.0.2/go.mod h1:0MS4r+7BZKSJ5mw4/S5MPN+qHFF1fYclkSPilDOKW0s=
github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0 h1:6E+4a0GO5zZEnZ81pIr0yLvtUWk2if982qA3F3QD6H4=
github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0/go.mod h1:zJYVVT2jmtg6P3p1VtQj7WsuWi/y4VnjVBn7F8KPB3I=
github.com/lunixbochs/vtclean v1.0.0/go.mod h1:pHhQNgMf3btfWnGBVipUOjRYhoOsdGqdm/+2c2E2WMI=
github.com/mailru/easyjson v0.0.0-20190312143242-1de009706dbe/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
github.com/marten-seemann/qpack v0.2.1 h1:jvTsT/HpCn2UZJdP+UUB53FfUUgeOyG5K1ns0OJOGVs=
github.com/marten-seemann/qpack v0.2.1/go.mod h1:F7Gl5L1jIgN1D11ucXefiuJS9UMVP2opoCp2jDKb7wc=
github.com/marten-seemann/qtls-go1-16 v0.1.5 h1:o9JrYPPco/Nukd/HpOHMHZoBDXQqoNtUCmny98/1uqQ=
github.com/marten-seemann/qtls-go1-16 v0.1.5/go.mod h1:gNpI2Ol+lRS3WwSOtIUUtRwZEQMXjYK+dQSBFbethAk=
github.com/marten-seemann/qtls-go1-17 v0.1.2 h1:JADBlm0LYiVbuSySCHeY863dNkcpMmDR7s0bLKJeYlQ=
github.com/marten-seemann/qtls-go1-17 v0.1.2/go.mod h1:C2ekUKcDdz9SDWxec1N/MvcXBpaX9l3Nx67XaR84L5s=
github.com/marten-seemann/qtls-go1-18 v0.1.2 h1:JH6jmzbduz0ITVQ7ShevK10Av5+jBEKAHMntXmIV7kM=
github.com/marten-seemann/qtls-go1-18 v0.1.2/go.mod h1:mJttiymBAByA49mhlNZZGrH5u1uXYZJ+RW28Py7f4m4=
github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
github.com/mattn/go-isatty v0.0.14 h1:yVuAays6BHfxijgZPzw+3Zlu5yQgKGP2/hcQbHb7S9Y=
github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94=
github.com/mattn/go-isatty v0.0.16 h1:bq3VjFmv/sOjHtdEhmkEV4x1AJtvUvOJ2PFAZ5+peKQ=
github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
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/go.mod h1:JIl7NbARA7phWnGvh0LKTyg7S9BA+6gx71ShQilpsus=
github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
github.com/microcosm-cc/bluemonday v1.0.1/go.mod h1:hsXNsILzKxV+sX77C5b8FSuKF00vh2OMYv+xgHpAMF4=
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
github.com/mroth/weightedrand v0.4.1 h1:rHcbUBopmi/3x4nnrvwGJBhX9d0vk+KgoLUZeDP6YyI=
github.com/mroth/weightedrand v0.4.1/go.mod h1:3p2SIcC8al1YMzGhAIoXD+r9olo/g/cdJgAD905gyNE=
github.com/neelance/astrewrite v0.0.0-20160511093645-99348263ae86/go.mod h1:kHJEU3ofeGjhHklVoIGuVj85JJwZ6kWPaJwCIxgnFmo=
github.com/neelance/sourcemap v0.0.0-20151028013722-8c68805598ab/go.mod h1:Qr6/a/Q4r9LP1IltGz7tA7iOK1WonHEYhu1HRBA7ZiM=
github.com/mewkiz/flac v1.0.7/go.mod h1:yU74UH277dBUpqxPouHSQIar3G1X/QIclVbFahSd1pU=
github.com/mewkiz/pkg v0.0.0-20190919212034-518ade7978e2/go.mod h1:3E2FUC/qYUfM8+r9zAwpeHJzqRVVMIYnpzD/clwWxyA=
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/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/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A=
github.com/nxadm/tail v1.4.8 h1:nPr65rt6Y5JFSKQO7qToXr7pePgD6Gwiw05lkbyAQTE=
github.com/nxadm/tail v1.4.8/go.mod h1:+ncqLTQzXmGhMZNUePPaPqPvBxHAIsmXswZKocGu+AU=
github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk=
github.com/onsi/ginkgo v1.14.0/go.mod h1:iSB4RoI2tjJc9BBv4NKIKWKya62Rps+oPG/Lv9klQyY=
github.com/onsi/ginkgo v1.16.2/go.mod h1:CObGmKUOKaSC0RjmoAK7tKyn4Azo5P2IWuoMnvwxz1E=
github.com/onsi/ginkgo v1.16.4 h1:29JGrr5oVBm5ulCWet69zQkzWipVXIol6ygQUe/EzNc=
github.com/onsi/ginkgo v1.16.4/go.mod h1:dX+/inL/fNMqNlz0e9LfyB9TswhZpCVdJM/Z6Vvnwo0=
github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY=
github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo=
github.com/onsi/gomega v1.13.0 h1:7lLHu94wT9Ij0o6EWWclhu0aOh32VxhkwEJvzuWPeak=
github.com/onsi/gomega v1.13.0/go.mod h1:lRk9szgn8TxENtWd0Tp4c3wjlRfMTMH27I+3Je41yGY=
github.com/openzipkin/zipkin-go v0.1.1/go.mod h1:NtoC/o8u3JlF1lSlyPNswIbeQH9bJTmOf0Erfk+hxe8=
github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I=
github.com/onsi/ginkgo/v2 v2.2.0 h1:3ZNA3L1c5FYDFTTxbFeVGGD8jYvjYauHD30YgLxVsNI=
github.com/onsi/ginkgo/v2 v2.2.0/go.mod h1:MEH45j8TBi6u9BMogfbp0stKC5cdGjumZj5Y7AG4VIk=
github.com/onsi/gomega v1.20.1 h1:PA/3qinGoukvymdIDV8pii6tiZgC8kbmJO6Z5+b002Q=
github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA=
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/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
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/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
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-20210106213030-5aafc221ea8c/go.mod h1:OmDBASR4679mdNQnz2pUhc2G8CO2JrUAVFDRBDP/hJE=
github.com/prometheus/client_golang v0.8.0/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw=
github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
github.com/prometheus/common v0.0.0-20180801064454-c7de2306084e/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro=
github.com/prometheus/procfs v0.0.0-20180725123919-05ee40e3a273/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
github.com/remyoudompheng/bigfft v0.0.0-20200410134404-eec4a21b6bb0 h1:OdAsTTz6OkFY5QxjkYwrChwuRruF69c169dPK26NUlk=
github.com/remyoudompheng/bigfft v0.0.0-20200410134404-eec4a21b6bb0/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo=
github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g=
github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo=
github.com/shirou/gopsutil/v3 v3.22.3 h1:UebRzEomgMpv61e3hgD1tGooqX5trFbdU/ehphbHd00=
github.com/shirou/gopsutil/v3 v3.22.3/go.mod h1:D01hZJ4pVHPpCTZ3m3T2+wDF2YAGfd+H4ifUguaQzHM=
github.com/shurcooL/component v0.0.0-20170202220835-f88ec8f54cc4/go.mod h1:XhFIlyj5a1fBNx5aJTbKoIq0mNaPvOagO+HjB3EtxrY=
github.com/shurcooL/events v0.0.0-20181021180414-410e4ca65f48/go.mod h1:5u70Mqkb5O5cxEA8nxTsgrgLehJeAw6Oc4Ab1c/P1HM=
github.com/shurcooL/github_flavored_markdown v0.0.0-20181002035957-2122de532470/go.mod h1:2dOwnU2uBioM+SGy2aZoq1f/Sd1l9OkAeAUvjSyvgU0=
github.com/shurcooL/go v0.0.0-20180423040247-9e1955d9fb6e/go.mod h1:TDJrrUr11Vxrven61rcy3hJMUqaf/CLWYhHNPmT14Lk=
github.com/shurcooL/go-goon v0.0.0-20170922171312-37c2f522c041/go.mod h1:N5mDOmsrJOB+vfqUK+7DmDyjhSLIIBnXo9lvZJj3MWQ=
github.com/shurcooL/gofontwoff v0.0.0-20180329035133-29b52fc0a18d/go.mod h1:05UtEgK5zq39gLST6uB0cf3NEHjETfB4Fgr3Gx5R9Vw=
github.com/shurcooL/gopherjslib v0.0.0-20160914041154-feb6d3990c2c/go.mod h1:8d3azKNyqcHP1GaQE/c6dDgjkgSx2BZ4IoEi4F1reUI=
github.com/shurcooL/highlight_diff v0.0.0-20170515013008-09bb4053de1b/go.mod h1:ZpfEhSmds4ytuByIcDnOLkTHGUI6KNqRNPDLHDk+mUU=
github.com/shurcooL/highlight_go v0.0.0-20181028180052-98c3abbbae20/go.mod h1:UDKB5a1T23gOMUJrI+uSuH0VRDStOiUVSjBTRDVBVag=
github.com/shurcooL/home v0.0.0-20181020052607-80b7ffcb30f9/go.mod h1:+rgNQw2P9ARFAs37qieuu7ohDNQ3gds9msbT2yn85sg=
github.com/shurcooL/htmlg v0.0.0-20170918183704-d01228ac9e50/go.mod h1:zPn1wHpTIePGnXSHpsVPWEktKXHr6+SS6x/IKRb7cpw=
github.com/shurcooL/httperror v0.0.0-20170206035902-86b7830d14cc/go.mod h1:aYMfkZ6DWSJPJ6c4Wwz3QtW22G7mf/PEgaB9k/ik5+Y=
github.com/shurcooL/httpfs v0.0.0-20171119174359-809beceb2371/go.mod h1:ZY1cvUeJuFPAdZ/B6v7RHavJWZn2YPVFQ1OSXhCGOkg=
github.com/shurcooL/httpgzip v0.0.0-20180522190206-b1c53ac65af9/go.mod h1:919LwcH0M7/W4fcZ0/jy0qGght1GIhqyS/EgWGH2j5Q=
github.com/shurcooL/issues v0.0.0-20181008053335-6292fdc1e191/go.mod h1:e2qWDig5bLteJ4fwvDAc2NHzqFEthkqn7aOZAOpj+PQ=
github.com/shurcooL/issuesapp v0.0.0-20180602232740-048589ce2241/go.mod h1:NPpHK2TI7iSaM0buivtFUc9offApnI0Alt/K8hcHy0I=
github.com/shurcooL/notifications v0.0.0-20181007000457-627ab5aea122/go.mod h1:b5uSkrEVM1jQUspwbixRBhaIjIzL2xazXp6kntxYle0=
github.com/shurcooL/octicon v0.0.0-20181028054416-fa4f57f9efb2/go.mod h1:eWdoE5JD4R5UVWDucdOPg1g2fqQRq78IQa9zlOV1vpQ=
github.com/shurcooL/reactions v0.0.0-20181006231557-f2e0b4ca5b82/go.mod h1:TCR1lToEk4d2s07G3XGfz2QrgHXg4RJBvjrOozvoWfk=
github.com/shurcooL/sanitized_anchor_name v0.0.0-20170918181015-86672fcb3f95/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc=
github.com/shurcooL/users v0.0.0-20180125191416-49c67e49c537/go.mod h1:QJTqeLYEDaXHZDBsXlPCDqdhQuJkuw4NOtaxYe3xii4=
github.com/shurcooL/webdavfs v0.0.0-20170829043945-18c3829fa133/go.mod h1:hKmq5kWdCj2z2KEozexVbfEZIWiTjhE0+UjmZgPqehw=
github.com/sirupsen/logrus v1.8.1 h1:dJKuHgqk1NNQlqoA6BTlM1Wf9DOH3NBjQyu0h9+AZZE=
github.com/sirupsen/logrus v1.8.1/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0=
github.com/sourcegraph/annotate v0.0.0-20160123013949-f4cad6c6324d/go.mod h1:UdhH50NIW0fCiwBSr0co2m7BnFLdv4fQTgdqdJTHFeE=
github.com/sourcegraph/syntaxhighlight v0.0.0-20170531221838-bd320f5d308e/go.mod h1:HuIsMU8RRBOtsCgI77wP899iHVBQpCmg4ErYMZB+2IA=
github.com/quic-go/qpack v0.4.0 h1:Cr9BXA1sQS2SmDUWjSofMPNKmvF6IiIfDRmgU0w1ZCo=
github.com/quic-go/qpack v0.4.0/go.mod h1:UZVnYIfi5GRk+zI9UMaCPsmZ2xKJP7XBUvVyT1Knj9A=
github.com/quic-go/qtls-go1-18 v0.2.0 h1:5ViXqBZ90wpUcZS0ge79rf029yx0dYB0McyPJwqqj7U=
github.com/quic-go/qtls-go1-18 v0.2.0/go.mod h1:moGulGHK7o6O8lSPSZNoOwcLvJKJ85vVNc7oJFD65bc=
github.com/quic-go/qtls-go1-19 v0.2.0 h1:Cvn2WdhyViFUHoOqK52i51k4nDX8EwIh5VJiVM4nttk=
github.com/quic-go/qtls-go1-19 v0.2.0/go.mod h1:ySOI96ew8lnoKPtSqx2BlI5wCpUVPT05RMAlajtnyOI=
github.com/quic-go/qtls-go1-20 v0.1.0 h1:d1PK3ErFy9t7zxKsG3NXBJXZjp/kMLoIb3y/kV54oAI=
github.com/quic-go/qtls-go1-20 v0.1.0/go.mod h1:JKtK6mjbAVcUTN/9jZpvLbGxvdWIKS8uT7EiStoU1SM=
github.com/quic-go/quic-go v0.32.0 h1:lY02md31s1JgPiiyfqJijpu/UX/Iun304FI3yUqX7tA=
github.com/quic-go/quic-go v0.32.0/go.mod h1:/fCsKANhQIeD5l76c2JFU+07gVE3KaA0FP+0zMWwfwo=
github.com/rogpeppe/go-internal v1.9.0 h1:73kH8U+JUqXU8lRuOHeVHaa/SZPifC7BkcraZVejAe8=
github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs=
github.com/shirou/gopsutil/v3 v3.23.1 h1:a9KKO+kGLKEvcPIs4W62v0nu3sciVDOOOPUD0Hz7z/4=
github.com/shirou/gopsutil/v3 v3.23.1/go.mod h1:NN6mnm5/0k8jw4cBfCnJtr5L7ErOTg18tMNpgFkn0hA=
github.com/sirupsen/logrus v1.9.0 h1:trlNQbNUG3OdDrDil03MCb1H2o9nJ1x4/5LYw7byDE0=
github.com/sirupsen/logrus v1.9.0/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
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.2 h1:4jaiDzPyXQvSd7D0EjG45355tLlV3VOECpq10pLC+8s=
github.com/tarm/serial v0.0.0-20180830185346-98f6abe2eb07/go.mod h1:kDXzergiv9cbyO7IOYJZWg1U88JhDg3PB6klq9Hg2pA=
github.com/tidwall/gjson v1.14.1 h1:iymTbGkQBhveq21bEvAQ81I0LEBork8BFe1CUZXdyuo=
github.com/tidwall/gjson v1.14.1/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk=
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk=
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
github.com/tidwall/gjson v1.14.4 h1:uo0p8EbA09J7RQaflQ1aBRffTR7xedD2bcIVSYxLnkM=
github.com/tidwall/gjson v1.14.4/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/pretty v1.2.0 h1:RWIZEg2iJ8/g6fDDYzMpobmaoGh5OLl4AXtGUGPcqCs=
github.com/tidwall/pretty v1.2.0/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU=
github.com/tklauser/go-sysconf v0.3.10 h1:IJ1AZGZRWbY8T5Vfk04D9WOA5WSejdflXxP03OUqALw=
github.com/tklauser/go-sysconf v0.3.10/go.mod h1:C8XykCvCb+Gn0oNCWPIlcb0RuglQTYaQ2hGm7jmxEFk=
github.com/tklauser/numcpus v0.4.0 h1:E53Dm1HjH1/R2/aoCtXtPgzmElmn51aOkhCFSuZq//o=
github.com/tklauser/numcpus v0.4.0/go.mod h1:1+UI3pD8NW14VMwdgJNJ1ESk2UnwhAnz5hMwiKKqXCQ=
github.com/viant/assertly v0.4.8/go.mod h1:aGifi++jvCrUaklKEKT0BU95igDNaqkvz+49uaYMPRU=
github.com/viant/toolbox v0.24.0/go.mod h1:OxMCG57V0PXuIP2HNQrtJf2CjqdmbrOx5EkMILuUhzM=
github.com/tklauser/go-sysconf v0.3.11 h1:89WgdJhk5SNwJfu+GKyYveZ4IaJ7xAkecBo+KdJV0CM=
github.com/tklauser/go-sysconf v0.3.11/go.mod h1:GqXfhXY3kiPa0nAXPDIQIWzJbMCB7AmcWpGR8lSZfqI=
github.com/tklauser/numcpus v0.6.0 h1:kebhY2Qt+3U6RNK7UqpYNA+tJ23IBEGKkB7JQBfDYms=
github.com/tklauser/numcpus v0.6.0/go.mod h1:FEZLMke0lhOUG6w2JadTzp0a+Nl8PF/GFkQ5UVIcaL4=
github.com/wcharczuk/go-chart/v2 v2.1.0 h1:tY2slqVQ6bN+yHSnDYwZebLQFkphK4WNrVwnt7CJZ2I=
github.com/wcharczuk/go-chart/v2 v2.1.0/go.mod h1:yx7MvAVNcP/kN9lKXM/NTce4au4DFN99j6i1OwDclNA=
github.com/wdvxdr1123/ZeroBot v1.5.2-0.20220610070647-9eeffcb277ee h1:b2f+KLhZv+BCMQZuwMJvhKQOrz5YXzOduHC3G1DjQR0=
github.com/wdvxdr1123/ZeroBot v1.5.2-0.20220610070647-9eeffcb277ee/go.mod h1:LJ+VOf523i3IrykuLK53UEeWqnAclRL5d2wGT4sS4Zk=
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/wdvxdr1123/ZeroBot v1.6.9 h1:vaFqtIXpyeU60xEddg4EsN6cX6cpB3MKhLbe/MQ5OVw=
github.com/wdvxdr1123/ZeroBot v1.6.9/go.mod h1:T5kD5vLi/YxL/fyDOCOaawi96LRBdJjcXh2CIjDyFfg=
github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
github.com/yusufpapurcu/wmi v1.2.2 h1:KBNDSne4vP5mbSWnJbO+51IMOXJB67QiYCSBrubbPRg=
github.com/yusufpapurcu/wmi v1.2.2/go.mod h1:SBZ9tNy3G9/m5Oi98Zks0QjeHVDvuK0qfxQmPyzfmi0=
gitlab.com/gomidi/midi v1.23.7 h1:I6qKoIk9s9dcX+pNf0jC+tziCzJFn82bMpuntRkLeik=
gitlab.com/gomidi/midi v1.23.7/go.mod h1:3ohtNOhqoSakkuLG/Li1OI6I3J1c2LErnJF5o/VBq1c=
gitlab.com/gomidi/midi/v2 v2.0.17 h1:kf16wNwFFOskl0trvarOwMuZUQICdIGn37LP9QqIRuo=
gitlab.com/gomidi/midi/v2 v2.0.17/go.mod h1:quTyMKSQ4Klevxu6gY4gy2USbeZra0fV5SalndmPfsY=
go.opencensus.io v0.18.0/go.mod h1:vKdFvxhtzZ9onBp9VKHK8z/sRpBMnKAsufL7wlDrCOA=
go4.org v0.0.0-20180809161055-417644f6feb5/go.mod h1:MkTOUMDaeVYJUOUsaDXIhWPZYa1yOyC1qaOBpL57BhE=
golang.org/x/build v0.0.0-20190111050920-041ab4dc3f9d/go.mod h1:OWs+y06UdEOHN4y+MfF/py+xQ/tYqIWW03b70/CG9Rw=
golang.org/x/crypto v0.0.0-20181030102418-4d3f4d9ffa16/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
gitlab.com/gomidi/midi/v2 v2.0.25 h1:dkzVBqbaFHjyWwP71MrQNX7IeRUIDonddmHbPpO/Ucg=
gitlab.com/gomidi/midi/v2 v2.0.25/go.mod h1:quTyMKSQ4Klevxu6gY4gy2USbeZra0fV5SalndmPfsY=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20190313024323-a1f597ede03a/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-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
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-20200221231518-2aa609cf4a9d/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9 h1:psW17arqaxU48Z5kZ0CQnkZWQJsqcURM6tKiBApRjXI=
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
golang.org/x/crypto v0.4.0 h1:UVQgzMY87xqpKNgb+kDsll2Igd33HszWHFLmpaRMq/8=
golang.org/x/crypto v0.4.0/go.mod h1:3quD/ATkf6oY+rnes5c3ExXTbLc8mueNue5/DoinL80=
golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20190731235908-ec7cb31e5a56/go.mod h1:JhuoJpWY28nO4Vef9tZUw9qufEGTyX1+7lmHxV5q5G4=
golang.org/x/exp v0.0.0-20221205204356-47842c84f3db h1:D/cFflL63o2KSLJIwjlcIt8PR064j/xsmdEJL/YvY/o=
golang.org/x/exp v0.0.0-20221205204356-47842c84f3db/go.mod h1:CxIveKay+FTh1D0yPZemJVgC/95VzuuOLq5Qi4xnoYc=
golang.org/x/exp/shiny v0.0.0-20221126150942-6ab00d035af9 h1:tLxpBz7qD8qFkRDC159unetNbxKp4zeqsqw2rLwvdxc=
golang.org/x/exp/shiny v0.0.0-20221126150942-6ab00d035af9/go.mod h1:VjAR7z0ngyATZTELrBSkxOOHhhlnVUxDye4mcjx5h/8=
golang.org/x/image v0.0.0-20190220214146-31aff87c08e9/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js=
golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js=
golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
golang.org/x/image v0.0.0-20191009234506-e7c1f5e7dbb8/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
golang.org/x/image v0.0.0-20200927104501-e162460cd6b5/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
golang.org/x/image v0.0.0-20220601225756-64ec528b34cd h1:9NbNcTg//wfC5JskFW4Z3sqwVnjmJKHxLAol1bW2qgw=
golang.org/x/image v0.0.0-20220601225756-64ec528b34cd/go.mod h1:doUCurBvlfPMKfmIpRIywoHmhN3VyhnoFDbvIEWF4hY=
golang.org/x/lint v0.0.0-20180702182130-06c8688daad7/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.4.2 h1:Gz96sIWK3OalVv/I/qNygP42zyoKp3xptRVCWRFEBvo=
golang.org/x/image v0.3.0 h1:HTDXbdK9bjfSWkPzDJIw89W8CAtfFGduujWs33NLLsg=
golang.org/x/image v0.3.0/go.mod h1:fXd9211C/0VTlYuAcOhW8dY/RtEJqODXOWBDpmYBf+A=
golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE=
golang.org/x/mobile v0.0.0-20190415191353-3e0bab5405d6/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o=
golang.org/x/mobile v0.0.0-20201217150744-e6ae53a27f4f h1:kgfVkAEEQXXQ0qc6dH7n6y37NAYmTFmz0YRwrRjgxKw=
golang.org/x/mobile v0.0.0-20201217150744-e6ae53a27f4f/go.mod h1:skQtrUTUwhdJvXM/2KKJzY8pDgNr9I/FOMqDVRPBUS4=
golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY=
golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
golang.org/x/mod v0.1.1-0.20191209134235-331c550502dd/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
golang.org/x/mod v0.6.0 h1:b9gGHsz9/HhJ3HF5DHQytPpuwocVTChQJK3AvoLRD5I=
golang.org/x/mod v0.6.0/go.mod h1:4mET923SAdbXp2ki8ey+zGs1SLqsuM2Y0uvdZR/fUNI=
golang.org/x/net v0.0.0-20180218175443-cbe0f9307d01/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20181029044818-c44066c5c816/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20181106065722-10aee1819953/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/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-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190313220215-9f648a60d977/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-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-20200421231249-e086a090c8fd/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
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-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM=
golang.org/x/net v0.0.0-20210428140749-89ef3d95e781/go.mod h1:OJAsFXCWl8Ukc7SiCT/9KSuxbyM7479/AVlXFRxuMCk=
golang.org/x/net v0.0.0-20220607020251-c690dde0001d h1:4SFsTMi4UahlKoloni7L4eYzhFRifURQLw+yv0QDCx8=
golang.org/x/net v0.0.0-20220607020251-c690dde0001d/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
golang.org/x/oauth2 v0.0.0-20181017192945-9dcd33a902f4/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
golang.org/x/oauth2 v0.0.0-20181203162652-d668ce993890/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
golang.org/x/perf v0.0.0-20180704124530-6e6d33e29852/go.mod h1:JLpeXjPJfIyPr5TlbXLkXWLhP8nz10XfvxElABhCtcw=
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
golang.org/x/net v0.4.0 h1:Q5QPcMlvfxFTAPV0+07Xz/MpK9NTXu2VDUuy0FeMfaU=
golang.org/x/net v0.4.0/go.mod h1:MBQ8lrhLObU/6UmLb4fmbmk5OcyYmqtbGd/9yIeKjEE=
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-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20181029174526-d69651ed3497/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190316082340-a2f829d7f35f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
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-20190904154756-749cb33beabd/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-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200519105757-fe76b779f299/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-20201126233918-771906719818/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201204225414-ed752295db88/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210112080510-489259a85091/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210510120138-977fb7262007/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-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210902050250-f475640dd07b/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20211007075335-d3039528d8ac/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220111092808-5a964db01320/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220128215802-99c3d69c2c27/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220209214540-3681064d5158/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220608164250-635b8c9b7f68 h1:z8Hj/bl9cOV2grsOpEaQFUaly0JWN3i97mo3jXKJNp0=
golang.org/x/sys v0.0.0-20220608164250-635b8c9b7f68/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-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.4.0 h1:Zr2JFtRQNX3BCZ8YtxRE9hNJYC8J6I1MVbMg6owUp18=
golang.org/x/sys v0.4.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
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/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.7 h1:olpwvP2KacW1ZWvsR7uQhoyTYvKAupfQrRGBFM352Gk=
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/tools v0.0.0-20180828015842-6cd1fcedba52/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/text v0.6.0 h1:3XmdazWV+ubf7QgHSTWeykHOci5oeekaGJBLkrkaw4k=
golang.org/x/text v0.6.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20181030000716-a0a13e073c7b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20201124115921-2c860bdd6e78/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
golang.org/x/tools v0.0.0-20201224043029-2b0845dc783e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
golang.org/x/tools v0.1.1 h1:wGiQel/hW0NnEkJUk8lbzkX2gFJU6PFxf1v5OlCfuOs=
golang.org/x/tools v0.0.0-20200117012304-6edc0a871e69/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
golang.org/x/tools v0.2.0 h1:G6AHpWxTMGY1KyEYoAQ5WTtIekUUvDNjan3ugu60JvE=
golang.org/x/tools v0.2.0/go.mod h1:y4OqIKeOV/fWJetJ8bXPU1sEVniLMIyDAZWeHdV+NTA=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/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-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE=
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
google.golang.org/api v0.0.0-20180910000450-7ca32eb868bf/go.mod h1:4mhQ8q/RsB7i+udVvVy5NUi08OU8ZlA0gRVgrF7VFY0=
google.golang.org/api v0.0.0-20181030000543-1d582fd0359e/go.mod h1:4mhQ8q/RsB7i+udVvVy5NUi08OU8ZlA0gRVgrF7VFY0=
google.golang.org/api v0.1.0/go.mod h1:UGEZY7KEX120AnNLIHFMKIo4obdJhkp2tPbaPlQx13Y=
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
google.golang.org/appengine v1.2.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
google.golang.org/appengine v1.3.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
google.golang.org/genproto v0.0.0-20180831171423-11092d34479b/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
google.golang.org/genproto v0.0.0-20181029155118-b69ba1387ce2/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
google.golang.org/genproto v0.0.0-20181202183823-bd91e49a0898/go.mod h1:7Ep/1NZk928CDR8SjdVbjWNpdIf6nzjE3BTgJDr2Atg=
google.golang.org/genproto v0.0.0-20190306203927-b5d61aea6440/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
google.golang.org/grpc v1.14.0/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw=
google.golang.org/grpc v1.16.0/go.mod h1:0JHn/cJsOMiMfNA9+DeHDlAU7KAAB5GDlYFpa9MZMio=
google.golang.org/grpc v1.17.0/go.mod h1:6QZJwpn2B+Zp71q/5VxRsJ6NXXVCE5NRUHRo+f3cWCs=
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=
google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE=
google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo=
google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
google.golang.org/protobuf v1.26.0 h1:bxAC2xTBsZGibn2RTntX0oH50xLsqy1OxA9tTL3p/lk=
google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
google.golang.org/protobuf v1.28.0 h1:w43yiav+6bVFTBQFZX0r7ipe9JQ1QsbMgHwbBziscLw=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw=
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ=
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY=
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
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=
grpc.go4.org v0.0.0-20170609214715-11d0a25b4919/go.mod h1:77eQGdRu53HpSqPFJFmuJdjuHRquDANNeA4x7B8WQ9o=
honnef.co/go/tools v0.0.0-20180728063816-88497007e858/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
lukechampine.com/uint128 v1.1.1/go.mod h1:c4eWIwlEGaxC/+H1VguhU4PHXNWDCDMUlWdIWl2j1gk=
modernc.org/cc/v3 v3.33.6/go.mod h1:iPJg1pkwXqAV16SNgFBVYmggfMg6xhs+2oiO0vclK3g=
modernc.org/cc/v3 v3.33.9/go.mod h1:iPJg1pkwXqAV16SNgFBVYmggfMg6xhs+2oiO0vclK3g=
modernc.org/cc/v3 v3.33.11/go.mod h1:iPJg1pkwXqAV16SNgFBVYmggfMg6xhs+2oiO0vclK3g=
modernc.org/cc/v3 v3.34.0/go.mod h1:iPJg1pkwXqAV16SNgFBVYmggfMg6xhs+2oiO0vclK3g=
modernc.org/cc/v3 v3.35.0/go.mod h1:iPJg1pkwXqAV16SNgFBVYmggfMg6xhs+2oiO0vclK3g=
modernc.org/cc/v3 v3.35.4/go.mod h1:iPJg1pkwXqAV16SNgFBVYmggfMg6xhs+2oiO0vclK3g=
modernc.org/cc/v3 v3.35.5/go.mod h1:iPJg1pkwXqAV16SNgFBVYmggfMg6xhs+2oiO0vclK3g=
modernc.org/cc/v3 v3.35.7/go.mod h1:iPJg1pkwXqAV16SNgFBVYmggfMg6xhs+2oiO0vclK3g=
modernc.org/cc/v3 v3.35.8/go.mod h1:iPJg1pkwXqAV16SNgFBVYmggfMg6xhs+2oiO0vclK3g=
modernc.org/cc/v3 v3.35.10/go.mod h1:iPJg1pkwXqAV16SNgFBVYmggfMg6xhs+2oiO0vclK3g=
modernc.org/cc/v3 v3.35.15/go.mod h1:iPJg1pkwXqAV16SNgFBVYmggfMg6xhs+2oiO0vclK3g=
modernc.org/cc/v3 v3.35.16/go.mod h1:iPJg1pkwXqAV16SNgFBVYmggfMg6xhs+2oiO0vclK3g=
modernc.org/cc/v3 v3.35.17/go.mod h1:iPJg1pkwXqAV16SNgFBVYmggfMg6xhs+2oiO0vclK3g=
modernc.org/cc/v3 v3.35.18/go.mod h1:iPJg1pkwXqAV16SNgFBVYmggfMg6xhs+2oiO0vclK3g=
modernc.org/cc/v3 v3.35.20/go.mod h1:iPJg1pkwXqAV16SNgFBVYmggfMg6xhs+2oiO0vclK3g=
modernc.org/cc/v3 v3.35.22/go.mod h1:iPJg1pkwXqAV16SNgFBVYmggfMg6xhs+2oiO0vclK3g=
modernc.org/cc/v3 v3.36.0/go.mod h1:NFUHyPn4ekoC/JHeZFfZurN6ixxawE1BnVonP/oahEI=
modernc.org/ccgo/v3 v3.0.0-20220428102840-41399a37e894/go.mod h1:eI31LL8EwEBKPpNpA4bU1/i+sKOwOrQy8D87zWUcRZc=
modernc.org/ccgo/v3 v3.0.0-20220430103911-bc99d88307be/go.mod h1:bwdAnOoaIt8Ax9YdWGjxWsdkPcZyRPHqrOvJxaKAKGw=
modernc.org/ccgo/v3 v3.9.5/go.mod h1:umuo2EP2oDSBnD3ckjaVUXMrmeAw8C8OSICVa0iFf60=
modernc.org/ccgo/v3 v3.10.0/go.mod h1:c0yBmkRFi7uW4J7fwx/JiijwOjeAeR2NoSaRVFPmjMw=
modernc.org/ccgo/v3 v3.11.0/go.mod h1:dGNposbDp9TOZ/1KBxghxtUp/bzErD0/0QW4hhSaBMI=
modernc.org/ccgo/v3 v3.11.1/go.mod h1:lWHxfsn13L3f7hgGsGlU28D9eUOf6y3ZYHKoPaKU0ag=
modernc.org/ccgo/v3 v3.11.3/go.mod h1:0oHunRBMBiXOKdaglfMlRPBALQqsfrCKXgw9okQ3GEw=
modernc.org/ccgo/v3 v3.12.4/go.mod h1:Bk+m6m2tsooJchP/Yk5ji56cClmN6R1cqc9o/YtbgBQ=
modernc.org/ccgo/v3 v3.12.6/go.mod h1:0Ji3ruvpFPpz+yu+1m0wk68pdr/LENABhTrDkMDWH6c=
modernc.org/ccgo/v3 v3.12.8/go.mod h1:Hq9keM4ZfjCDuDXxaHptpv9N24JhgBZmUG5q60iLgUo=
modernc.org/ccgo/v3 v3.12.11/go.mod h1:0jVcmyDwDKDGWbcrzQ+xwJjbhZruHtouiBEvDfoIsdg=
modernc.org/ccgo/v3 v3.12.14/go.mod h1:GhTu1k0YCpJSuWwtRAEHAol5W7g1/RRfS4/9hc9vF5I=
modernc.org/ccgo/v3 v3.12.18/go.mod h1:jvg/xVdWWmZACSgOiAhpWpwHWylbJaSzayCqNOJKIhs=
modernc.org/ccgo/v3 v3.12.20/go.mod h1:aKEdssiu7gVgSy/jjMastnv/q6wWGRbszbheXgWRHc8=
modernc.org/ccgo/v3 v3.12.21/go.mod h1:ydgg2tEprnyMn159ZO/N4pLBqpL7NOkJ88GT5zNU2dE=
modernc.org/ccgo/v3 v3.12.22/go.mod h1:nyDVFMmMWhMsgQw+5JH6B6o4MnZ+UQNw1pp52XYFPRk=
modernc.org/ccgo/v3 v3.12.25/go.mod h1:UaLyWI26TwyIT4+ZFNjkyTbsPsY3plAEB6E7L/vZV3w=
modernc.org/ccgo/v3 v3.12.29/go.mod h1:FXVjG7YLf9FetsS2OOYcwNhcdOLGt8S9bQ48+OP75cE=
modernc.org/ccgo/v3 v3.12.36/go.mod h1:uP3/Fiezp/Ga8onfvMLpREq+KUjUmYMxXPO8tETHtA8=
modernc.org/ccgo/v3 v3.12.38/go.mod h1:93O0G7baRST1vNj4wnZ49b1kLxt0xCW5Hsa2qRaZPqc=
modernc.org/ccgo/v3 v3.12.43/go.mod h1:k+DqGXd3o7W+inNujK15S5ZYuPoWYLpF5PYougCmthU=
modernc.org/ccgo/v3 v3.12.46/go.mod h1:UZe6EvMSqOxaJ4sznY7b23/k13R8XNlyWsO5bAmSgOE=
modernc.org/ccgo/v3 v3.12.47/go.mod h1:m8d6p0zNps187fhBwzY/ii6gxfjob1VxWb919Nk1HUk=
modernc.org/ccgo/v3 v3.12.50/go.mod h1:bu9YIwtg+HXQxBhsRDE+cJjQRuINuT9PUK4orOco/JI=
modernc.org/ccgo/v3 v3.12.51/go.mod h1:gaIIlx4YpmGO2bLye04/yeblmvWEmE4BBBls4aJXFiE=
modernc.org/ccgo/v3 v3.12.53/go.mod h1:8xWGGTFkdFEWBEsUmi+DBjwu/WLy3SSOrqEmKUjMeEg=
modernc.org/ccgo/v3 v3.12.54/go.mod h1:yANKFTm9llTFVX1FqNKHE0aMcQb1fuPJx6p8AcUx+74=
modernc.org/ccgo/v3 v3.12.55/go.mod h1:rsXiIyJi9psOwiBkplOaHye5L4MOOaCjHg1Fxkj7IeU=
modernc.org/ccgo/v3 v3.12.56/go.mod h1:ljeFks3faDseCkr60JMpeDb2GSO3TKAmrzm7q9YOcMU=
modernc.org/ccgo/v3 v3.12.57/go.mod h1:hNSF4DNVgBl8wYHpMvPqQWDQx8luqxDnNGCMM4NFNMc=
modernc.org/ccgo/v3 v3.12.60/go.mod h1:k/Nn0zdO1xHVWjPYVshDeWKqbRWIfif5dtsIOCUVMqM=
modernc.org/ccgo/v3 v3.12.66/go.mod h1:jUuxlCFZTUZLMV08s7B1ekHX5+LIAurKTTaugUr/EhQ=
modernc.org/ccgo/v3 v3.12.67/go.mod h1:Bll3KwKvGROizP2Xj17GEGOTrlvB1XcVaBrC90ORO84=
modernc.org/ccgo/v3 v3.12.73/go.mod h1:hngkB+nUUqzOf3iqsM48Gf1FZhY599qzVg1iX+BT3cQ=
modernc.org/ccgo/v3 v3.12.81/go.mod h1:p2A1duHoBBg1mFtYvnhAnQyI6vL0uw5PGYLSIgF6rYY=
modernc.org/ccgo/v3 v3.12.84/go.mod h1:ApbflUfa5BKadjHynCficldU1ghjen84tuM5jRynB7w=
modernc.org/ccgo/v3 v3.12.86/go.mod h1:dN7S26DLTgVSni1PVA3KxxHTcykyDurf3OgUzNqTSrU=
modernc.org/ccgo/v3 v3.12.90/go.mod h1:obhSc3CdivCRpYZmrvO88TXlW0NvoSVvdh/ccRjJYko=
modernc.org/ccgo/v3 v3.12.92/go.mod h1:5yDdN7ti9KWPi5bRVWPl8UNhpEAtCjuEE7ayQnzzqHA=
modernc.org/ccgo/v3 v3.13.1/go.mod h1:aBYVOUfIlcSnrsRVU8VRS35y2DIfpgkmVkYZ0tpIXi4=
modernc.org/ccgo/v3 v3.15.9/go.mod h1:md59wBwDT2LznX/OTCPoVS6KIsdRgY8xqQwBV+hkTH0=
modernc.org/ccgo/v3 v3.15.10/go.mod h1:wQKxoFn0ynxMuCLfFD09c8XPUCc8obfchoVR9Cn0fI8=
modernc.org/ccgo/v3 v3.15.12/go.mod h1:VFePOWoCd8uDGRJpq/zfJ29D0EVzMSyID8LCMWYbX6I=
modernc.org/ccgo/v3 v3.16.6/go.mod h1:tGtX0gE9Jn7hdZFeU88slbTh1UtCYKusWOoCJuvkWsQ=
modernc.org/ccorpus v1.11.1/go.mod h1:2gEUTrWqdpH2pXsmTM1ZkjeSrUWDpjMu2T6m29L/ErQ=
modernc.org/ccorpus v1.11.6/go.mod h1:2gEUTrWqdpH2pXsmTM1ZkjeSrUWDpjMu2T6m29L/ErQ=
modernc.org/httpfs v1.0.6/go.mod h1:7dosgurJGp0sPaRanU53W4xZYKh14wfzX420oZADeHM=
modernc.org/libc v0.0.0-20220428101251-2d5f3daf273b/go.mod h1:p7Mg4+koNjc8jkqwcoFBJx7tXkpj00G77X7A72jXPXA=
modernc.org/libc v1.9.8/go.mod h1:U1eq8YWr/Kc1RWCMFUWEdkTg8OTcfLw2kY8EDwl039w=
modernc.org/libc v1.9.11/go.mod h1:NyF3tsA5ArIjJ83XB0JlqhjTabTCHm9aX4XMPHyQn0Q=
modernc.org/libc v1.11.0/go.mod h1:2lOfPmj7cz+g1MrPNmX65QCzVxgNq2C5o0jdLY2gAYg=
modernc.org/libc v1.11.2/go.mod h1:ioIyrl3ETkugDO3SGZ+6EOKvlP3zSOycUETe4XM4n8M=
modernc.org/libc v1.11.5/go.mod h1:k3HDCP95A6U111Q5TmG3nAyUcp3kR5YFZTeDS9v8vSU=
modernc.org/libc v1.11.6/go.mod h1:ddqmzR6p5i4jIGK1d/EiSw97LBcE3dK24QEwCFvgNgE=
modernc.org/libc v1.11.11/go.mod h1:lXEp9QOOk4qAYOtL3BmMve99S5Owz7Qyowzvg6LiZso=
modernc.org/libc v1.11.13/go.mod h1:ZYawJWlXIzXy2Pzghaf7YfM8OKacP3eZQI81PDLFdY8=
modernc.org/libc v1.11.16/go.mod h1:+DJquzYi+DMRUtWI1YNxrlQO6TcA5+dRRiq8HWBWRC8=
modernc.org/libc v1.11.19/go.mod h1:e0dgEame6mkydy19KKaVPBeEnyJB4LGNb0bBH1EtQ3I=
modernc.org/libc v1.11.24/go.mod h1:FOSzE0UwookyT1TtCJrRkvsOrX2k38HoInhw+cSCUGk=
modernc.org/libc v1.11.26/go.mod h1:SFjnYi9OSd2W7f4ct622o/PAYqk7KHv6GS8NZULIjKY=
modernc.org/libc v1.11.27/go.mod h1:zmWm6kcFXt/jpzeCgfvUNswM0qke8qVwxqZrnddlDiE=
modernc.org/libc v1.11.28/go.mod h1:Ii4V0fTFcbq3qrv3CNn+OGHAvzqMBvC7dBNyC4vHZlg=
modernc.org/libc v1.11.31/go.mod h1:FpBncUkEAtopRNJj8aRo29qUiyx5AvAlAxzlx9GNaVM=
modernc.org/libc v1.11.34/go.mod h1:+Tzc4hnb1iaX/SKAutJmfzES6awxfU1BPvrrJO0pYLg=
modernc.org/libc v1.11.37/go.mod h1:dCQebOwoO1046yTrfUE5nX1f3YpGZQKNcITUYWlrAWo=
modernc.org/libc v1.11.39/go.mod h1:mV8lJMo2S5A31uD0k1cMu7vrJbSA3J3waQJxpV4iqx8=
modernc.org/libc v1.11.42/go.mod h1:yzrLDU+sSjLE+D4bIhS7q1L5UwXDOw99PLSX0BlZvSQ=
modernc.org/libc v1.11.44/go.mod h1:KFq33jsma7F5WXiYelU8quMJasCCTnHK0mkri4yPHgA=
modernc.org/libc v1.11.45/go.mod h1:Y192orvfVQQYFzCNsn+Xt0Hxt4DiO4USpLNXBlXg/tM=
modernc.org/libc v1.11.47/go.mod h1:tPkE4PzCTW27E6AIKIR5IwHAQKCAtudEIeAV1/SiyBg=
modernc.org/libc v1.11.49/go.mod h1:9JrJuK5WTtoTWIFQ7QjX2Mb/bagYdZdscI3xrvHbXjE=
modernc.org/libc v1.11.51/go.mod h1:R9I8u9TS+meaWLdbfQhq2kFknTW0O3aw3kEMqDDxMaM=
modernc.org/libc v1.11.53/go.mod h1:5ip5vWYPAoMulkQ5XlSJTy12Sz5U6blOQiYasilVPsU=
modernc.org/libc v1.11.54/go.mod h1:S/FVnskbzVUrjfBqlGFIPA5m7UwB3n9fojHhCNfSsnw=
modernc.org/libc v1.11.55/go.mod h1:j2A5YBRm6HjNkoSs/fzZrSxCuwWqcMYTDPLNx0URn3M=
modernc.org/libc v1.11.56/go.mod h1:pakHkg5JdMLt2OgRadpPOTnyRXm/uzu+Yyg/LSLdi18=
modernc.org/libc v1.11.58/go.mod h1:ns94Rxv0OWyoQrDqMFfWwka2BcaF6/61CqJRK9LP7S8=
modernc.org/libc v1.11.71/go.mod h1:DUOmMYe+IvKi9n6Mycyx3DbjfzSKrdr/0Vgt3j7P5gw=
modernc.org/libc v1.11.75/go.mod h1:dGRVugT6edz361wmD9gk6ax1AbDSe0x5vji0dGJiPT0=
modernc.org/libc v1.11.82/go.mod h1:NF+Ek1BOl2jeC7lw3a7Jj5PWyHPwWD4aq3wVKxqV1fI=
modernc.org/libc v1.11.86/go.mod h1:ePuYgoQLmvxdNT06RpGnaDKJmDNEkV7ZPKI2jnsvZoE=
modernc.org/libc v1.11.87/go.mod h1:Qvd5iXTeLhI5PS0XSyqMY99282y+3euapQFxM7jYnpY=
modernc.org/libc v1.11.88/go.mod h1:h3oIVe8dxmTcchcFuCcJ4nAWaoiwzKCdv82MM0oiIdQ=
modernc.org/libc v1.11.98/go.mod h1:ynK5sbjsU77AP+nn61+k+wxUGRx9rOFcIqWYYMaDZ4c=
modernc.org/libc v1.11.101/go.mod h1:wLLYgEiY2D17NbBOEp+mIJJJBGSiy7fLL4ZrGGZ+8jI=
modernc.org/libc v1.12.0/go.mod h1:2MH3DaF/gCU8i/UBiVE1VFRos4o523M7zipmwH8SIgQ=
modernc.org/libc v1.14.1/go.mod h1:npFeGWjmZTjFeWALQLrvklVmAxv4m80jnG3+xI8FdJk=
modernc.org/libc v1.14.2/go.mod h1:MX1GBLnRLNdvmK9azU9LCxZ5lMyhrbEMK8rG3X/Fe34=
modernc.org/libc v1.14.3/go.mod h1:GPIvQVOVPizzlqyRX3l756/3ppsAgg1QgPxjr5Q4agQ=
modernc.org/libc v1.14.5/go.mod h1:2PJHINagVxO4QW/5OQdRrvMYo+bm5ClpUFfyXCYl9ak=
modernc.org/libc v1.16.0/go.mod h1:N4LD6DBE9cf+Dzf9buBlzVJndKr/iJHG97vGLHYnb5A=
modernc.org/libc v1.16.1/go.mod h1:JjJE0eu4yeK7tab2n4S1w8tlWd9MxXLRzheaRnAKymU=
modernc.org/libc v1.16.8 h1:Ux98PaOMvolgoFX/YwusFOHBnanXdGRmWgI8ciI2z4o=
modernc.org/libc v1.16.8/go.mod h1:hYIV5VZczAmGZAnG15Vdngn5HSF5cSkbvfz2B7GRuVU=
modernc.org/mathutil v1.1.1/go.mod h1:mZW8CKdRPY1v87qxC/wUdX5O1qDzXMP5TH3wjfpga6E=
modernc.org/mathutil v1.2.2/go.mod h1:mZW8CKdRPY1v87qxC/wUdX5O1qDzXMP5TH3wjfpga6E=
modernc.org/mathutil v1.4.0/go.mod h1:mZW8CKdRPY1v87qxC/wUdX5O1qDzXMP5TH3wjfpga6E=
modernc.org/mathutil v1.4.1 h1:ij3fYGe8zBF4Vu+g0oT7mB06r8sqGWKuJu1yXeR4by8=
modernc.org/mathutil v1.4.1/go.mod h1:mZW8CKdRPY1v87qxC/wUdX5O1qDzXMP5TH3wjfpga6E=
modernc.org/memory v1.0.4/go.mod h1:nV2OApxradM3/OVbs2/0OsP6nPfakXpi50C7dcoHXlc=
modernc.org/memory v1.0.5/go.mod h1:B7OYswTRnfGg+4tDH1t1OeUNnsy2viGTdME4tzd+IjM=
modernc.org/memory v1.1.1 h1:bDOL0DIDLQv7bWhP3gMvIrnoFw+Eo6F7a2QK9HPDiFU=
modernc.org/memory v1.1.1/go.mod h1:/0wo5ibyrQiaoUoH7f9D8dnglAmILJ5/cxZlRECf+Nw=
modernc.org/opt v0.1.1/go.mod h1:WdSiB5evDcignE70guQKxYUl14mgWtbClRi5wmkkTX0=
modernc.org/strutil v1.1.1/go.mod h1:DE+MQQ/hjKBZS2zNInV5hhcipt5rLPWkmpbGeW5mmdw=
modernc.org/token v1.0.0/go.mod h1:UGzOrNV1mAFSEB63lOFHIpNRUVMvYTc6yu1SMY/XTDM=
sourcegraph.com/sourcegraph/go-diff v0.5.0/go.mod h1:kuch7UrkMzY0X+p9CRK03kfuPQ2zzQcaEFbx8wA8rck=
sourcegraph.com/sqs/pbtypes v0.0.0-20180604144634-d3ebe8f20ae4/go.mod h1:ketZ/q3QxT9HOBeFhu6RdvsftgpsbFHBF5Cas6cDKZ0=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
modernc.org/libc v1.21.5 h1:xBkU9fnHV+hvZuPSRszN0AXDG4M7nwPLwTWwkYcvLCI=
modernc.org/libc v1.21.5/go.mod h1:przBsL5RDOZajTVslkugzLBj1evTue36jEomFQOoYuI=
modernc.org/mathutil v1.5.0 h1:rV0Ko/6SfM+8G+yKiyI830l3Wuz1zRutdslNoQ0kfiQ=
modernc.org/mathutil v1.5.0/go.mod h1:mZW8CKdRPY1v87qxC/wUdX5O1qDzXMP5TH3wjfpga6E=
modernc.org/memory v1.4.0 h1:crykUfNSnMAXaOJnnxcSzbUGMqkLWjklJKkBK2nwZwk=
modernc.org/memory v1.4.0/go.mod h1:PkUhL0Mugw21sHPeskwZW4D6VscE/GQJOnIpCnW6pSU=

View File

@@ -1,45 +1,10 @@
package kanban
import (
"fmt"
"strings"
// Version ...
var Version = "v1.6.2-beta4"
"github.com/fumiama/go-registry"
)
var (
info = [...]string{
"* OneBot + ZeroBot + Golang",
"* Version 1.4.1 - 2022-06-16 13:31:06 +0800 CST",
"* Copyright © 2020 - 2022 FloatTech. All Rights Reserved.",
"* Project: https://github.com/FloatTech/ZeroBot-Plugin",
}
// Banner ...
Banner = strings.Join(info[:], "\n")
reg = registry.NewRegReader("reilia.fumiama.top:32664", "fumiama")
)
// PrintBanner ...
func PrintBanner() {
fmt.Print(
"\n======================[ZeroBot-Plugin]======================",
"\n", Banner, "\n",
"----------------------[ZeroBot-公告栏]----------------------",
"\n", Kanban(), "\n",
"============================================================\n\n",
)
}
// Kanban ...
func Kanban() string {
err := reg.Connect()
if err != nil {
return err.Error()
}
defer reg.Close()
text, err := reg.Get("ZeroBot-Plugin/kanban")
if err != nil {
return err.Error()
}
return text
}
// Banner ...
var Banner = "* OneBot + ZeroBot + Golang\n" +
"* Version " + Version + " - 2023-02-15 17:27:40 +0800 CST\n" +
"* Copyright © 2020 - 2023 FloatTech. All Rights Reserved.\n" +
"* Project: https://github.com/FloatTech/ZeroBot-Plugin"

46
kanban/gen/banner.go Normal file
View File

@@ -0,0 +1,46 @@
// Package main generates banner.go
package main
import (
"bytes"
"fmt"
"os"
"os/exec"
"strings"
"time"
)
const banner = `package kanban
// Version ...
var Version = "%s"
// Banner ...
var Banner = "* OneBot + ZeroBot + Golang\n" +
"* Version " + Version + " - %s\n" +
"* Copyright © 2020 - %d FloatTech. All Rights Reserved.\n" +
"* Project: https://github.com/FloatTech/ZeroBot-Plugin"
`
const timeformat = `2006-01-02 15:04:05 +0800 CST`
func main() {
f, err := os.Create("banner.go")
if err != nil {
panic(err)
}
defer f.Close()
vartag := bytes.NewBuffer(nil)
vartagcmd := exec.Command("git", "tag", "--sort=committerdate")
vartagcmd.Stdout = vartag
err = vartagcmd.Run()
if err != nil {
panic(err)
}
s := strings.Split(vartag.String(), "\n")
now := time.Now()
_, err = fmt.Fprintf(f, banner, s[len(s)-2], now.Format(timeformat), now.Year())
if err != nil {
panic(err)
}
}

View File

@@ -1,6 +1,45 @@
// Package kanban 打印版本信息
package kanban
import (
"fmt"
"sync"
"github.com/FloatTech/zbputils/control"
"github.com/fumiama/go-registry"
)
//go:generate go run github.com/FloatTech/ZeroBot-Plugin/kanban/gen
var once sync.Once
func init() {
PrintBanner()
once.Do(PrintBanner)
}
var reg = registry.NewRegReader("reilia.fumiama.top:32664", control.Md5File, "fumiama")
// PrintBanner ...
func PrintBanner() {
fmt.Print(
"\n======================[ZeroBot-Plugin]======================",
"\n", Banner, "\n",
"----------------------[ZeroBot-公告栏]----------------------",
"\n", Kanban(), "\n",
"============================================================\n\n",
)
}
// Kanban ...
func Kanban() string {
err := reg.Connect()
if err != nil {
return err.Error()
}
defer reg.Close()
text, err := reg.Get("ZeroBot-Plugin/kanban")
if err != nil {
return err.Error()
}
return text
}

186
main.go
View File

@@ -1,3 +1,4 @@
// Package main ZeroBot-Plugin main file
package main
import (
@@ -25,6 +26,8 @@ import (
// vvvvvvvvvvvvvv //
// vvvv //
_ "github.com/FloatTech/ZeroBot-Plugin/plugin/antiabuse" // 违禁词
_ "github.com/FloatTech/ZeroBot-Plugin/plugin/chat" // 基础词库
_ "github.com/FloatTech/ZeroBot-Plugin/plugin/sleep_manage" // 统计睡眠时间
@@ -33,8 +36,6 @@ import (
_ "github.com/FloatTech/ZeroBot-Plugin/plugin/manager" // 群管
_ "github.com/FloatTech/ZeroBot-Plugin/plugin/thesaurus" // 词典匹配回复
_ "github.com/FloatTech/zbputils/job" // 定时指令触发器
// ^^^^ //
@@ -55,68 +56,92 @@ import (
// vvvvvvvvvvvvvv //
// vvvv //
_ "github.com/FloatTech/ZeroBot-Plugin/plugin/ai_false" // 服务器监控
_ "github.com/FloatTech/ZeroBot-Plugin/plugin/aiwife" // 随机老婆
_ "github.com/FloatTech/ZeroBot-Plugin/plugin/b14" // base16384加解密
_ "github.com/FloatTech/ZeroBot-Plugin/plugin/baidu" // 百度一下
_ "github.com/FloatTech/ZeroBot-Plugin/plugin/bilibili" // 查询b站用户信息
_ "github.com/FloatTech/ZeroBot-Plugin/plugin/bilibili_parse" // b站视频链接解析
_ "github.com/FloatTech/ZeroBot-Plugin/plugin/book_review" // 哀伤雪刃吧推书记录
_ "github.com/FloatTech/ZeroBot-Plugin/plugin/cangtoushi" // 藏头诗
_ "github.com/FloatTech/ZeroBot-Plugin/plugin/choose" // 选择困难症帮手
_ "github.com/FloatTech/ZeroBot-Plugin/plugin/chouxianghua" // 说抽象话
_ "github.com/FloatTech/ZeroBot-Plugin/plugin/coser" // 三次元小姐姐
_ "github.com/FloatTech/ZeroBot-Plugin/plugin/cpstory" // cp短打
_ "github.com/FloatTech/ZeroBot-Plugin/plugin/danbooru" // DeepDanbooru二次元图标签识别
_ "github.com/FloatTech/ZeroBot-Plugin/plugin/diana" // 嘉心糖发病
_ "github.com/FloatTech/ZeroBot-Plugin/plugin/drift_bottle" // 漂流瓶
_ "github.com/FloatTech/ZeroBot-Plugin/plugin/emojimix" // 合成emoji
_ "github.com/FloatTech/ZeroBot-Plugin/plugin/epidemic" // 城市疫情查询
_ "github.com/FloatTech/ZeroBot-Plugin/plugin/font" // 渲染任意文字到图片
_ "github.com/FloatTech/ZeroBot-Plugin/plugin/fortune" // 运势
_ "github.com/FloatTech/ZeroBot-Plugin/plugin/funny" // 笑话
_ "github.com/FloatTech/ZeroBot-Plugin/plugin/genshin" // 原神抽卡
_ "github.com/FloatTech/ZeroBot-Plugin/plugin/gif" // 制图
_ "github.com/FloatTech/ZeroBot-Plugin/plugin/github" // 搜索GitHub仓库
_ "github.com/FloatTech/ZeroBot-Plugin/plugin/hs" // 炉石
_ "github.com/FloatTech/ZeroBot-Plugin/plugin/hyaku" // 百人一首
_ "github.com/FloatTech/ZeroBot-Plugin/plugin/image_finder" // 关键字搜图
_ "github.com/FloatTech/ZeroBot-Plugin/plugin/inject" // 注入指令
_ "github.com/FloatTech/ZeroBot-Plugin/plugin/jandan" // 煎蛋网无聊图
_ "github.com/FloatTech/ZeroBot-Plugin/plugin/juejuezi" // 绝绝子生成器
_ "github.com/FloatTech/ZeroBot-Plugin/plugin/lolicon" // lolicon 随机图片
_ "github.com/FloatTech/ZeroBot-Plugin/plugin/midicreate" // 简易midi音乐制作
_ "github.com/FloatTech/ZeroBot-Plugin/plugin/moyu" // 摸鱼
_ "github.com/FloatTech/ZeroBot-Plugin/plugin/moyu_calendar" // 摸鱼人日历
_ "github.com/FloatTech/ZeroBot-Plugin/plugin/music" // 点歌
_ "github.com/FloatTech/ZeroBot-Plugin/plugin/nativesetu" // 本地涩图
_ "github.com/FloatTech/ZeroBot-Plugin/plugin/nativewife" // 本地老婆
_ "github.com/FloatTech/ZeroBot-Plugin/plugin/nbnhhsh" // 拼音首字母缩写释义工具
_ "github.com/FloatTech/ZeroBot-Plugin/plugin/novel" // 铅笔小说网搜索
_ "github.com/FloatTech/ZeroBot-Plugin/plugin/nsfw" // nsfw图片识别
_ "github.com/FloatTech/ZeroBot-Plugin/plugin/omikuji" // 浅草寺求签
_ "github.com/FloatTech/ZeroBot-Plugin/plugin/qqwife" // 一群一天一夫一妻制群老婆
_ "github.com/FloatTech/ZeroBot-Plugin/plugin/reborn" // 投胎
_ "github.com/FloatTech/ZeroBot-Plugin/plugin/runcode" // 在线运行代码
_ "github.com/FloatTech/ZeroBot-Plugin/plugin/saucenao" // 以图搜图
_ "github.com/FloatTech/ZeroBot-Plugin/plugin/scale" // 叔叔的AI二次元图片放大
_ "github.com/FloatTech/ZeroBot-Plugin/plugin/score" // 分数
_ "github.com/FloatTech/ZeroBot-Plugin/plugin/setutime" // 来份涩图
_ "github.com/FloatTech/ZeroBot-Plugin/plugin/shadiao" // 沙雕app
_ "github.com/FloatTech/ZeroBot-Plugin/plugin/shindan" // 测定
_ "github.com/FloatTech/ZeroBot-Plugin/plugin/tarot" // 抽塔罗牌
_ "github.com/FloatTech/ZeroBot-Plugin/plugin/tiangou" // 舔狗日记
_ "github.com/FloatTech/ZeroBot-Plugin/plugin/tracemoe" // 搜番
_ "github.com/FloatTech/ZeroBot-Plugin/plugin/translation" // 翻译
_ "github.com/FloatTech/ZeroBot-Plugin/plugin/vtb_quotation" // vtb语录
_ "github.com/FloatTech/ZeroBot-Plugin/plugin/wangyiyun" // 网易云音乐热评
_ "github.com/FloatTech/ZeroBot-Plugin/plugin/word_count" // 聊天热词
_ "github.com/FloatTech/ZeroBot-Plugin/plugin/wordle" // 猜单词
_ "github.com/FloatTech/ZeroBot-Plugin/plugin/ymgal" // 月幕galgame
_ "github.com/FloatTech/ZeroBot-Plugin/plugin/zaobao" // 早报
_ "github.com/FloatTech/ZeroBot-Plugin/plugin/ahsai" // ahsai tts
_ "github.com/FloatTech/ZeroBot-Plugin/plugin/ai_false" // 服务器监控
_ "github.com/FloatTech/ZeroBot-Plugin/plugin/aipaint" // ai绘图
_ "github.com/FloatTech/ZeroBot-Plugin/plugin/aiwife" // 随机老婆
_ "github.com/FloatTech/ZeroBot-Plugin/plugin/alipayvoice" // 支付宝到账语音
_ "github.com/FloatTech/ZeroBot-Plugin/plugin/autowithdraw" // 触发者撤回时也自动撤回
_ "github.com/FloatTech/ZeroBot-Plugin/plugin/b14" // base16384加解密
_ "github.com/FloatTech/ZeroBot-Plugin/plugin/baidu" // 百度一下
_ "github.com/FloatTech/ZeroBot-Plugin/plugin/baiduaudit" // 百度内容审核
_ "github.com/FloatTech/ZeroBot-Plugin/plugin/base64gua" // base64卦加解密
_ "github.com/FloatTech/ZeroBot-Plugin/plugin/baseamasiro" // base天城文加解密
_ "github.com/FloatTech/ZeroBot-Plugin/plugin/bilibili" // b站相关
_ "github.com/FloatTech/ZeroBot-Plugin/plugin/book_review" // 哀伤雪刃吧推书记录
_ "github.com/FloatTech/ZeroBot-Plugin/plugin/cangtoushi" // 藏头诗
_ "github.com/FloatTech/ZeroBot-Plugin/plugin/choose" // 选择困难症帮手
_ "github.com/FloatTech/ZeroBot-Plugin/plugin/chouxianghua" // 说抽象话
_ "github.com/FloatTech/ZeroBot-Plugin/plugin/chrev" // 英文字符翻转
_ "github.com/FloatTech/ZeroBot-Plugin/plugin/coser" // 三次元小姐姐
_ "github.com/FloatTech/ZeroBot-Plugin/plugin/cpstory" // cp短打
_ "github.com/FloatTech/ZeroBot-Plugin/plugin/danbooru" // DeepDanbooru二次元图标签识别
_ "github.com/FloatTech/ZeroBot-Plugin/plugin/diana" // 嘉心糖发病
_ "github.com/FloatTech/ZeroBot-Plugin/plugin/dress" // 女装
_ "github.com/FloatTech/ZeroBot-Plugin/plugin/drift_bottle" // 漂流瓶
_ "github.com/FloatTech/ZeroBot-Plugin/plugin/emojimix" // 合成emoji
_ "github.com/FloatTech/ZeroBot-Plugin/plugin/event" // 好友申请群聊邀请事件处理
_ "github.com/FloatTech/ZeroBot-Plugin/plugin/font" // 渲染任意文字到图片
_ "github.com/FloatTech/ZeroBot-Plugin/plugin/fortune" // 运势
_ "github.com/FloatTech/ZeroBot-Plugin/plugin/funny" // 笑话
_ "github.com/FloatTech/ZeroBot-Plugin/plugin/genshin" // 原神抽卡
_ "github.com/FloatTech/ZeroBot-Plugin/plugin/gif" // 制图
_ "github.com/FloatTech/ZeroBot-Plugin/plugin/github" // 搜索GitHub仓库
_ "github.com/FloatTech/ZeroBot-Plugin/plugin/guessmusic" // 猜歌
_ "github.com/FloatTech/ZeroBot-Plugin/plugin/heisi" // 黑丝
_ "github.com/FloatTech/ZeroBot-Plugin/plugin/hs" // 炉石
_ "github.com/FloatTech/ZeroBot-Plugin/plugin/hyaku" // 百人一首
_ "github.com/FloatTech/ZeroBot-Plugin/plugin/image_finder" // 关键字搜图
_ "github.com/FloatTech/ZeroBot-Plugin/plugin/inject" // 注入指令
_ "github.com/FloatTech/ZeroBot-Plugin/plugin/jandan" // 煎蛋网无聊图
_ "github.com/FloatTech/ZeroBot-Plugin/plugin/jiami" // 兽语加密
_ "github.com/FloatTech/ZeroBot-Plugin/plugin/jikipedia" // 小鸡词典
_ "github.com/FloatTech/ZeroBot-Plugin/plugin/jptingroom" // 日语听力学习材料
_ "github.com/FloatTech/ZeroBot-Plugin/plugin/juejuezi" // 绝绝子生成器
_ "github.com/FloatTech/ZeroBot-Plugin/plugin/lolicon" // lolicon 随机图片
_ "github.com/FloatTech/ZeroBot-Plugin/plugin/magicprompt" // magicprompt吟唱提示
_ "github.com/FloatTech/ZeroBot-Plugin/plugin/midicreate" // 简易midi音乐制作
_ "github.com/FloatTech/ZeroBot-Plugin/plugin/moegoe" // 日韩 VITS 模型拟声
_ "github.com/FloatTech/ZeroBot-Plugin/plugin/moyu" // 摸鱼
_ "github.com/FloatTech/ZeroBot-Plugin/plugin/moyu_calendar" // 摸鱼人日历
_ "github.com/FloatTech/ZeroBot-Plugin/plugin/music" // 点歌
_ "github.com/FloatTech/ZeroBot-Plugin/plugin/nativesetu" // 本地涩图
_ "github.com/FloatTech/ZeroBot-Plugin/plugin/nativewife" // 本地老婆
_ "github.com/FloatTech/ZeroBot-Plugin/plugin/nbnhhsh" // 拼音首字母缩写释义工具
_ "github.com/FloatTech/ZeroBot-Plugin/plugin/nihongo" // 日语语法学习
_ "github.com/FloatTech/ZeroBot-Plugin/plugin/novel" // 铅笔小说网搜索
_ "github.com/FloatTech/ZeroBot-Plugin/plugin/nsfw" // nsfw图片识别
_ "github.com/FloatTech/ZeroBot-Plugin/plugin/omikuji" // 浅草寺求签
_ "github.com/FloatTech/ZeroBot-Plugin/plugin/qqwife" // 一群一天一夫一妻制群老婆
_ "github.com/FloatTech/ZeroBot-Plugin/plugin/quan" // QQ权重查询
_ "github.com/FloatTech/ZeroBot-Plugin/plugin/qzone" // qq空间表白墙
_ "github.com/FloatTech/ZeroBot-Plugin/plugin/realcugan" // realcugan清晰术
_ "github.com/FloatTech/ZeroBot-Plugin/plugin/reborn" // 投胎
_ "github.com/FloatTech/ZeroBot-Plugin/plugin/runcode" // 在线运行代码
_ "github.com/FloatTech/ZeroBot-Plugin/plugin/saucenao" // 以图搜图
_ "github.com/FloatTech/ZeroBot-Plugin/plugin/scale" // 叔叔的AI二次元图片放大
_ "github.com/FloatTech/ZeroBot-Plugin/plugin/score" // 分数
_ "github.com/FloatTech/ZeroBot-Plugin/plugin/setutime" // 来份涩图
_ "github.com/FloatTech/ZeroBot-Plugin/plugin/shadiao" // 沙雕app
_ "github.com/FloatTech/ZeroBot-Plugin/plugin/shindan" // 测定
_ "github.com/FloatTech/ZeroBot-Plugin/plugin/tarot" // 抽塔罗牌
_ "github.com/FloatTech/ZeroBot-Plugin/plugin/tiangou" // 舔狗日记
_ "github.com/FloatTech/ZeroBot-Plugin/plugin/tracemoe" // 搜番
_ "github.com/FloatTech/ZeroBot-Plugin/plugin/translation" // 翻译
_ "github.com/FloatTech/ZeroBot-Plugin/plugin/vitsnyaru" // vits猫雷
_ "github.com/FloatTech/ZeroBot-Plugin/plugin/vtb_quotation" // vtb语录
_ "github.com/FloatTech/ZeroBot-Plugin/plugin/wallet" // 钱包
_ "github.com/FloatTech/ZeroBot-Plugin/plugin/wangyiyun" // 网易云音乐热评
_ "github.com/FloatTech/ZeroBot-Plugin/plugin/warframeapi" // warframeAPI插件
_ "github.com/FloatTech/ZeroBot-Plugin/plugin/wenben" // 文本指令大全
_ "github.com/FloatTech/ZeroBot-Plugin/plugin/wenxinAI" // 百度文心AI画图
_ "github.com/FloatTech/ZeroBot-Plugin/plugin/word_count" // 聊天热词
_ "github.com/FloatTech/ZeroBot-Plugin/plugin/wordle" // 猜单词
_ "github.com/FloatTech/ZeroBot-Plugin/plugin/ygo" // 游戏王相关插件
_ "github.com/FloatTech/ZeroBot-Plugin/plugin/ymgal" // 月幕galgame
// _ "github.com/FloatTech/ZeroBot-Plugin/plugin/wtf" // 鬼东西
// _ "github.com/FloatTech/ZeroBot-Plugin/plugin/bilibili_push" // b站推送
// _ "github.com/FloatTech/ZeroBot-Plugin/plugin/thesaurus" // 词典匹配回复
// _ "github.com/FloatTech/ZeroBot-Plugin/plugin/wtf" // 鬼东西
// ^^^^ //
// ^^^^^^^^^^^^^^ //
@@ -140,6 +165,8 @@ import (
_ "github.com/FloatTech/ZeroBot-Plugin/plugin/ai_reply" // 人工智能回复
_ "github.com/FloatTech/ZeroBot-Plugin/plugin/breakrepeat" // 打断复读
// ^^^^ //
// ^^^^^^^^^^^^^^ //
// ^^^^^^^低优先级区^^^^^^^ //
@@ -152,7 +179,7 @@ import (
// //
// //
// -----------------------以下为内置依赖,勿动------------------------ //
"github.com/FloatTech/zbputils/process"
"github.com/FloatTech/floatbox/process"
"github.com/sirupsen/logrus"
zero "github.com/wdvxdr1123/ZeroBot"
"github.com/wdvxdr1123/ZeroBot/driver"
@@ -160,6 +187,14 @@ import (
// -----------------------以上为内置依赖,勿动------------------------ //
)
type zbpcfg struct {
Z zero.Config `json:"zero"`
W []*driver.WSClient `json:"ws"`
S []*driver.WSServer `json:"wss"`
}
var config zbpcfg
func init() {
sus := make([]int64, 0, 16)
// 解析命令行参数
@@ -175,11 +210,13 @@ func init() {
prefix := flag.String("p", "/", "Set command prefix.")
runcfg := flag.String("c", "", "Run from config file.")
save := flag.String("s", "", "Save default config to file and exit.")
late := flag.Uint("l", 233, "Response latency (ms).")
rsz := flag.Uint("r", 4096, "Receiving buffer ring size.")
maxpt := flag.Uint("x", 4, "Max process time (min).")
flag.Parse()
if *h {
kanban.PrintBanner()
fmt.Println("Usage:")
flag.PrintDefaults()
os.Exit(0)
@@ -215,20 +252,25 @@ func init() {
if err != nil {
panic(err)
}
config.Z.Driver = make([]zero.Driver, len(config.W))
config.Z.Driver = make([]zero.Driver, len(config.W)+len(config.S))
for i, w := range config.W {
config.Z.Driver[i] = w
}
for i, s := range config.S {
config.Z.Driver[i+len(config.W)] = s
}
logrus.Infoln("[main] 从", *runcfg, "读取配置文件")
return
}
config.W = []*driver.WSClient{driver.NewWebSocketClient(*url, *token)}
config.Z = zero.Config{
NickName: append([]string{*adana}, "ATRI", "atri", "亚托莉", "アトリ"),
CommandPrefix: *prefix,
SuperUsers: sus,
Driver: []zero.Driver{config.W[0]},
NickName: append([]string{*adana}, "ATRI", "atri", "亚托莉", "アトリ"),
CommandPrefix: *prefix,
SuperUsers: sus,
RingLen: *rsz,
Latency: time.Duration(*late) * time.Millisecond,
MaxProcessTime: time.Duration(*maxpt) * time.Minute,
Driver: []zero.Driver{config.W[0]},
}
if *save != "" {
@@ -257,5 +299,5 @@ func main() {
Handle(func(ctx *zero.Ctx) {
ctx.SendChain(message.Text(kanban.Kanban()))
})
zero.RunAndBlock(config.Z, process.GlobalInitMutex.Unlock)
zero.RunAndBlock(&config.Z, process.GlobalInitMutex.Unlock)
}

97
plugin/ahsai/ahsai.go Normal file
View File

@@ -0,0 +1,97 @@
// Package ahsai AH Soft フリーテキスト音声合成 demo API
package ahsai
import (
"fmt"
"math/rand"
"os"
"sort"
"strconv"
"time"
"github.com/FloatTech/floatbox/file"
ctrl "github.com/FloatTech/zbpctrl"
"github.com/FloatTech/zbputils/control"
ahsaitts "github.com/fumiama/ahsai"
zero "github.com/wdvxdr1123/ZeroBot"
"github.com/wdvxdr1123/ZeroBot/message"
)
var (
namelist = [...]string{"伊織弓鶴", "紲星あかり", "結月ゆかり", "京町セイカ", "東北きりたん", "東北イタコ", "ついなちゃん標準語", "ついなちゃん関西弁", "音街ウナ", "琴葉茜", "吉田くん", "民安ともえ", "桜乃そら", "月読アイ", "琴葉葵", "東北ずん子", "月読ショウタ", "水奈瀬コウ"}
namesort = func() []string {
nl := namelist[:]
sort.Strings(nl)
return nl
}()
)
func init() {
engine := control.Register("ahsai", &ctrl.Options[*zero.Ctx]{
DisableOnDefault: false,
Brief: "フリーテキスト音声合成",
Help: "- 使[伊織弓鶴|紲星あかり|結月ゆかり|京町セイカ|東北きりたん|東北イタコ|ついなちゃん標準語|ついなちゃん関西弁|音街ウナ|琴葉茜|吉田くん|民安ともえ|桜乃そら|月読アイ|琴葉葵|東北ずん子|月読ショウタ|水奈瀬コウ]说(日语)",
PrivateDataFolder: "ahsai",
})
cachePath := engine.DataFolder() + "cache/"
_ = os.RemoveAll(cachePath)
_ = os.MkdirAll(cachePath, 0755)
engine.OnRegex("^使(.{0,10})说([A-Za-z\\s\\d\u3005\u3040-\u30ff\u4e00-\u9fff\uff11-\uff19\uff21-\uff3a\uff41-\uff5a\uff66-\uff9d\\pP]+)$", selectName).SetBlock(true).Handle(func(ctx *zero.Ctx) {
ctx.SendChain(message.Text("少女祈祷中..."))
uid := ctx.Event.UserID
today := time.Now().Format("20060102150405")
ahsaiFile := cachePath + strconv.FormatInt(uid, 10) + today + "ahsai.wav"
s := ahsaitts.NewSpeaker()
err := s.SetName(ctx.State["ahsainame"].(string))
if err != nil {
ctx.SendChain(message.Text("ERROR: ", err))
return
}
u, err := s.Speak(ctx.State["ahsaitext"].(string))
if err != nil {
ctx.SendChain(message.Text("ERROR: ", err))
return
}
err = ahsaitts.SaveOggToFile(u, ahsaiFile)
if err != nil {
ctx.SendChain(message.Text("ERROR: ", err))
return
}
ctx.SendChain(message.Record("file:///" + file.BOTPATH + "/" + ahsaiFile))
})
}
func selectName(ctx *zero.Ctx) bool {
regexMatched := ctx.State["regex_matched"].([]string)
ctx.State["ahsaitext"] = regexMatched[2]
name := regexMatched[1]
index := sort.SearchStrings(namesort, name)
if index < len(namelist) && namesort[index] == name {
ctx.State["ahsainame"] = name
return true
}
speaktext := ""
for i, v := range namelist {
speaktext += fmt.Sprintf("%d. %s\n", i, v)
}
ctx.SendChain(message.Text("输入的音源为空, 请输入音源序号\n", speaktext))
next, cancel := zero.NewFutureEvent("message", 999, false, ctx.CheckSession(), zero.RegexRule(`\d{0,2}`)).Repeat()
defer cancel()
for {
select {
case <-time.After(time.Second * 10):
ctx.State["ahsainame"] = namelist[rand.Intn(len(namelist))]
ctx.SendChain(message.Text("时间太久啦!", zero.BotConfig.NickName[0], "帮你选择", ctx.State["ahsainame"]))
return true
case c := <-next:
msg := c.Event.Message.ExtractPlainText()
num, _ := strconv.Atoi(msg)
if num < 0 || num >= len(namelist) {
ctx.SendChain(message.Text("序号非法!"))
continue
}
ctx.State["ahsainame"] = namelist[num]
return true
}
}
}

View File

@@ -2,28 +2,53 @@
package aifalse
import (
"fmt"
"bytes"
"errors"
"image"
"math"
"runtime"
"strconv"
"strings"
"sync"
"time"
"github.com/FloatTech/AnimeAPI/bilibili"
"github.com/FloatTech/floatbox/file"
"github.com/FloatTech/floatbox/web"
"github.com/FloatTech/gg"
"github.com/FloatTech/imgfactory"
"github.com/FloatTech/rendercard"
ctrl "github.com/FloatTech/zbpctrl"
"github.com/FloatTech/zbputils/control"
"github.com/FloatTech/zbputils/ctxext"
"github.com/FloatTech/zbputils/img/text"
"github.com/disintegration/imaging"
"github.com/shirou/gopsutil/v3/cpu"
"github.com/shirou/gopsutil/v3/disk"
"github.com/shirou/gopsutil/v3/host"
"github.com/shirou/gopsutil/v3/mem"
"github.com/sirupsen/logrus"
"golang.org/x/text/cases"
"golang.org/x/text/language"
"github.com/FloatTech/ZeroBot-Plugin/kanban"
zero "github.com/wdvxdr1123/ZeroBot"
"github.com/wdvxdr1123/ZeroBot/message"
)
const (
backgroundURL = "https://iw233.cn/api.php?sort=mp"
referer = "https://weibo.com/"
)
var boottime = time.Now()
func init() { // 插件主体
engine := control.Register("aifalse", &ctrl.Options[*zero.Ctx]{
DisableOnDefault: false,
Help: "AIfalse\n" +
"- 查询计算机当前活跃度: [检查身体 | 自检 | 启动自检 | 系统状态]\n" +
Brief: "自检, 全局限速",
Help: "- 查询计算机当前活跃度: [检查身体 | 自检 | 启动自检 | 系统状态]\n" +
"- 设置默认限速为每 m [分钟 | 秒] n 次触发",
})
c, ok := control.Lookup("aifalse")
@@ -39,83 +64,531 @@ func init() { // 插件主体
}
engine.OnFullMatchGroup([]string{"检查身体", "自检", "启动自检", "系统状态"}, zero.AdminPermission).SetBlock(true).
Handle(func(ctx *zero.Ctx) {
ctx.SendChain(message.Text(
"* CPU占用: ", cpuPercent(), "%\n",
"* RAM占用: ", memPercent(), "%\n",
"* 硬盘使用: ", diskPercent(),
),
)
img, err := drawstatus(ctx.State["manager"].(*ctrl.Control[*zero.Ctx]), ctx.Event.SelfID, zero.BotConfig.NickName[0])
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: 可能被风控了"))
}
})
engine.OnRegex(`^设置默认限速为每\s*(\d+)\s*(分钟|秒)\s*(\d+)\s*次触发$`, 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"))
ctx.SendChain(message.Text("ERROR: no such plugin"))
return
}
m, err := strconv.ParseInt(ctx.State["regex_matched"].([]string)[1], 10, 64)
if err != nil {
ctx.SendChain(message.Text("ERROR:", err))
ctx.SendChain(message.Text("ERROR: ", err))
return
}
if ctx.State["regex_matched"].([]string)[2] == "分钟" {
m *= 60
}
if m >= 65536 || m <= 0 {
ctx.SendChain(message.Text("ERROR:interval too big"))
ctx.SendChain(message.Text("ERROR: interval too big"))
return
}
n, err := strconv.ParseInt(ctx.State["regex_matched"].([]string)[3], 10, 64)
if err != nil {
ctx.SendChain(message.Text("ERROR:", err))
ctx.SendChain(message.Text("ERROR: ", err))
return
}
if n >= 65536 || n <= 0 {
ctx.SendChain(message.Text("ERROR:burst too big"))
ctx.SendChain(message.Text("ERROR: burst too big"))
return
}
ctxext.SetDefaultLimiterManagerParam(time.Duration(m)*time.Second, int(n))
err = c.SetData(0, (m&0xffff)|((n<<16)&0xffff0000))
if err != nil {
ctx.SendChain(message.Text("ERROR:", err))
ctx.SendChain(message.Text("ERROR: ", err))
return
}
ctx.SendChain(message.Text("设置默认限速为每", m, "秒触发", n, "次"))
})
}
func cpuPercent() float64 {
func drawstatus(m *ctrl.Control[*zero.Ctx], uid int64, botname string) (sendimg image.Image, err error) {
diskstate, err := diskstate()
if err != nil {
return
}
diskcardh := 40 + (20+50)*len(diskstate) + 40 - 20
moreinfo, err := moreinfo(m)
if err != nil {
return
}
moreinfocardh := 30 + (20+32*72/96)*len(moreinfo) + 30 - 20
basicstate, err := basicstate()
if err != nil {
return
}
url, err := bilibili.GetRealURL(backgroundURL)
if err != nil {
return
}
data, err := web.RequestDataWith(web.NewDefaultClient(), url, "", referer, "", nil)
if err != nil {
return
}
back, _, err := image.Decode(bytes.NewReader(data))
if err != nil {
return
}
data, err = web.GetData("http://q4.qlogo.cn/g?b=qq&nk=" + strconv.FormatInt(uid, 10) + "&s=640")
if err != nil {
return
}
avatar, _, err := image.Decode(bytes.NewReader(data))
if err != nil {
return
}
avatarf := imgfactory.Size(avatar, 200, 200)
fontbyte, err := file.GetLazyData(text.GlowSansFontFile, control.Md5File, true)
if err != nil {
return
}
canvas := gg.NewContext(1280, 70+250+40+380+diskcardh+40+moreinfocardh+40+70)
bh, bw, ch, cw := float64(back.Bounds().Dy()), float64(back.Bounds().Dx()), float64(canvas.H()), float64(canvas.W())
if bh/bw < ch/cw {
back = imgfactory.Size(back, int(bw*ch/bh), int(bh*ch/bh)).Image()
canvas.DrawImageAnchored(back, canvas.W()/2, canvas.H()/2, 0.5, 0.5)
} else {
back = imgfactory.Size(back, int(bw*cw/bw), int(bh*cw/bw)).Image()
canvas.DrawImage(back, 0, 0)
}
wg := &sync.WaitGroup{}
wg.Add(5)
cardw := canvas.W() - 70 - 70
titlecardh := 250
basiccardh := 380
var titleimg, basicimg, diskimg, moreinfoimg, shadowimg image.Image
go func() {
defer wg.Done()
titlecard := gg.NewContext(cardw, titlecardh)
titlecard.DrawImage(imaging.Blur(canvas.Image(), 8), -70, -70)
titlecard.DrawRoundedRectangle(1, 1, float64(titlecard.W()-1*2), float64(titlecardh-1*2), 16)
titlecard.SetLineWidth(3)
titlecard.SetRGBA255(255, 255, 255, 100)
titlecard.StrokePreserve()
titlecard.SetRGBA255(255, 255, 255, 140)
titlecard.Fill()
titlecard.DrawImage(avatarf.Circle(0).Image(), (titlecardh-avatarf.H())/2, (titlecardh-avatarf.H())/2)
err = titlecard.ParseFontFace(fontbyte, 72)
if err != nil {
return
}
fw, _ := titlecard.MeasureString(botname)
titlecard.SetRGBA255(30, 30, 30, 255)
titlecard.DrawStringAnchored(botname, float64(titlecardh)+fw/2, float64(titlecardh)*0.5/2, 0.5, 0.5)
err = titlecard.ParseFontFace(fontbyte, 24)
if err != nil {
return
}
titlecard.SetRGBA255(30, 30, 30, 180)
titlecard.NewSubPath()
titlecard.MoveTo(float64(titlecardh), float64(titlecardh)/2)
titlecard.LineTo(float64(titlecard.W()-titlecardh), float64(titlecardh)/2)
titlecard.Stroke()
brt, err := botruntime()
if err != nil {
return
}
fw, _ = titlecard.MeasureString(brt)
titlecard.DrawStringAnchored(brt, float64(titlecardh)+fw/2, float64(titlecardh)*(0.5+0.25/2), 0.5, 0.5)
bs, err := botstatus()
if err != nil {
return
}
fw, _ = titlecard.MeasureString(bs)
titlecard.DrawStringAnchored(bs, float64(titlecardh)+fw/2, float64(titlecardh)*(0.5+0.5/2), 0.5, 0.5)
titleimg = rendercard.Fillet(titlecard.Image(), 16)
}()
go func() {
defer wg.Done()
basiccard := gg.NewContext(cardw, basiccardh)
basiccard.DrawImage(imaging.Blur(canvas.Image(), 8), -70, -70-titlecardh-40)
basiccard.DrawRoundedRectangle(1, 1, float64(basiccard.W()-1*2), float64(basiccardh-1*2), 16)
basiccard.SetLineWidth(3)
basiccard.SetRGBA255(255, 255, 255, 100)
basiccard.StrokePreserve()
basiccard.SetRGBA255(255, 255, 255, 140)
basiccard.Fill()
bslen := len(basicstate)
for i, v := range basicstate {
offset := float64(i) * ((float64(basiccard.W())-200*float64(bslen))/float64(bslen+1) + 200)
basiccard.SetRGBA255(235, 235, 235, 255)
basiccard.DrawCircle((float64(basiccard.W())-200*float64(bslen))/float64(bslen+1)+200/2+offset, 20+200/2, 100)
basiccard.Fill()
switch {
case v.precent > 90:
basiccard.SetRGBA255(255, 70, 0, 255)
case v.precent > 70:
basiccard.SetRGBA255(255, 165, 0, 255)
default:
basiccard.SetRGBA255(145, 240, 145, 255)
}
basiccard.NewSubPath()
basiccard.MoveTo((float64(basiccard.W())-200*float64(bslen))/float64(bslen+1)+200/2+offset, 20+200/2)
basiccard.DrawEllipticalArc((float64(basiccard.W())-200*float64(bslen))/float64(bslen+1)+200/2+offset, 20+200/2, 100, 100, -0.5*math.Pi, -0.5*math.Pi+2*v.precent*0.01*math.Pi)
basiccard.Fill()
basiccard.SetRGBA255(255, 255, 255, 255)
basiccard.DrawCircle((float64(basiccard.W())-200*float64(bslen))/float64(bslen+1)+200/2+offset, 20+200/2, 80)
basiccard.Fill()
err = basiccard.ParseFontFace(fontbyte, 42)
if err != nil {
return
}
basiccard.SetRGBA255(213, 213, 213, 255)
basiccard.DrawStringAnchored(strconv.FormatFloat(v.precent, 'f', 0, 64)+"%", (float64(basiccard.W())-200*float64(bslen))/float64(bslen+1)+200/2+offset, 20+200/2, 0.5, 0.5)
basiccard.SetRGBA255(30, 30, 30, 255)
_, fw := basiccard.MeasureString(v.name)
basiccard.DrawStringAnchored(v.name, (float64(basiccard.W())-200*float64(bslen))/float64(bslen+1)+200/2+offset, 20+200+15+basiccard.FontHeight()/2, 0.5, 0.5)
err = basiccard.ParseFontFace(fontbyte, 20)
if err != nil {
return
}
basiccard.SetRGBA255(30, 30, 30, 180)
textoffsety := basiccard.FontHeight() + 10
for k, s := range v.text {
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)
}()
go func() {
defer wg.Done()
diskcard := gg.NewContext(cardw, diskcardh)
diskcard.DrawImage(imaging.Blur(canvas.Image(), 8), -70, -70-titlecardh-40-basiccardh-40)
diskcard.DrawRoundedRectangle(1, 1, float64(diskcard.W()-1*2), float64(diskcardh-1*2), 16)
diskcard.SetLineWidth(3)
diskcard.SetRGBA255(255, 255, 255, 100)
diskcard.StrokePreserve()
diskcard.SetRGBA255(255, 255, 255, 140)
diskcard.Fill()
err = diskcard.ParseFontFace(fontbyte, 32)
if err != nil {
return
}
dslen := len(diskstate)
if dslen == 1 {
diskcard.SetRGBA255(192, 192, 192, 255)
diskcard.DrawRoundedRectangle(40, 40, float64(diskcard.W())-40-100, 50, 12)
diskcard.Fill()
switch {
case diskstate[0].precent > 90:
diskcard.SetRGBA255(255, 70, 0, 255)
case diskstate[0].precent > 70:
diskcard.SetRGBA255(255, 165, 0, 255)
default:
diskcard.SetRGBA255(145, 240, 145, 255)
}
diskcard.DrawRoundedRectangle(40, 40, (float64(diskcard.W())-40-100)*diskstate[0].precent*0.01, 50, 12)
diskcard.Fill()
diskcard.SetRGBA255(30, 30, 30, 255)
fw, _ := diskcard.MeasureString(diskstate[0].name)
fw1, _ := diskcard.MeasureString(diskstate[0].text[0])
diskcard.DrawStringAnchored(diskstate[0].name, 40+10+fw/2, 40+50/2, 0.5, 0.5)
diskcard.DrawStringAnchored(diskstate[0].text[0], (float64(diskcard.W())-100-10)-fw1/2, 40+50/2, 0.5, 0.5)
diskcard.DrawStringAnchored(strconv.FormatFloat(diskstate[0].precent, 'f', 0, 64)+"%", float64(diskcard.W())-100/2, 40+50/2, 0.5, 0.5)
} else {
for i, v := range diskstate {
offset := float64(i)*(50+20) - 20
diskcard.SetRGBA255(192, 192, 192, 255)
diskcard.DrawRoundedRectangle(40, 40+(float64(diskcardh-40*2)-50*float64(dslen))/float64(dslen-1)+offset, float64(diskcard.W())-40-100, 50, 12)
diskcard.Fill()
switch {
case v.precent > 90:
diskcard.SetRGBA255(255, 70, 0, 255)
case v.precent > 70:
diskcard.SetRGBA255(255, 165, 0, 255)
default:
diskcard.SetRGBA255(145, 240, 145, 255)
}
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.SetRGBA255(30, 30, 30, 255)
fw, _ := diskcard.MeasureString(v.name)
fw1, _ := diskcard.MeasureString(v.text[0])
diskcard.DrawStringAnchored(v.name, 40+10+fw/2, 40+(float64(diskcardh-40*2)-50*float64(dslen))/float64(dslen-1)+50/2+offset, 0.5, 0.5)
diskcard.DrawStringAnchored(v.text[0], (float64(diskcard.W())-100-10)-fw1/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)
}()
go func() {
defer wg.Done()
moreinfocard := gg.NewContext(cardw, moreinfocardh)
moreinfocard.DrawImage(imaging.Blur(canvas.Image(), 8), -70, -70-titlecardh-40-basiccardh-40-diskcardh-40)
moreinfocard.DrawRoundedRectangle(1, 1, float64(moreinfocard.W()-1*2), float64(moreinfocard.H()-1*2), 16)
moreinfocard.SetLineWidth(3)
moreinfocard.SetRGBA255(255, 255, 255, 100)
moreinfocard.StrokePreserve()
moreinfocard.SetRGBA255(255, 255, 255, 140)
moreinfocard.Fill()
err = moreinfocard.ParseFontFace(fontbyte, 32)
if err != nil {
return
}
milen := len(moreinfo)
for i, v := range moreinfo {
offset := float64(i)*(20+moreinfocard.FontHeight()) - 20
moreinfocard.SetRGBA255(30, 30, 30, 255)
fw, _ := moreinfocard.MeasureString(v.name)
fw1, _ := moreinfocard.MeasureString(v.text[0])
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)
}
moreinfoimg = rendercard.Fillet(moreinfocard.Image(), 16)
}()
go func() {
defer wg.Done()
shadow := gg.NewContext(canvas.W(), canvas.H())
shadow.SetRGBA255(0, 0, 0, 100)
shadow.SetLineWidth(12)
shadow.DrawRoundedRectangle(70, 70, float64(cardw), float64(titlecardh), 16)
shadow.Stroke()
shadow.DrawRoundedRectangle(70, float64(70+titlecardh+40), float64(cardw), float64(basiccardh), 16)
shadow.Stroke()
shadow.DrawRoundedRectangle(70, float64(70+titlecardh+40+basiccardh+40), float64(cardw), float64(basiccardh), 16)
shadow.Stroke()
shadow.DrawRoundedRectangle(70, float64(70+titlecardh+40+basiccardh+40+diskcardh+40), float64(cardw), float64(moreinfocardh), 16)
shadow.Stroke()
shadowimg = imaging.Blur(shadow.Image(), 24)
}()
wg.Wait()
if shadowimg == nil || titleimg == nil || basicimg == nil || diskimg == nil || moreinfoimg == nil {
err = errors.New("图片渲染失败")
return
}
canvas.DrawImage(shadowimg, 0, 0)
canvas.DrawImage(titleimg, 70, 70)
canvas.DrawImage(basicimg, 70, 70+titlecardh+40)
canvas.DrawImage(diskimg, 70, 70+titlecardh+40+basiccardh+40)
canvas.DrawImage(moreinfoimg, 70, 70+titlecardh+40+basiccardh+40+diskcardh+40)
err = canvas.ParseFontFace(fontbyte, 28)
if err != nil {
return
}
canvas.SetRGBA255(0, 0, 0, 255)
canvas.DrawStringAnchored("Created By ZeroBot-Plugin "+kanban.Version, float64(canvas.W())/2+3, float64(canvas.H())-70/2+3, 0.5, 0.5)
canvas.SetRGBA255(255, 255, 255, 255)
canvas.DrawStringAnchored("Created By ZeroBot-Plugin "+kanban.Version, float64(canvas.W())/2, float64(canvas.H())-70/2, 0.5, 0.5)
sendimg = canvas.Image()
return
}
func botruntime() (string, error) {
hostinfo, err := host.Info()
if err != nil {
return "", err
}
t := &strings.Builder{}
t.WriteString("ZeroBot-Plugin 已运行 ")
t.WriteString(strconv.FormatInt((time.Now().Unix()-boottime.Unix())/86400, 10))
t.WriteString(" 天 ")
t.WriteString(time.Unix(time.Now().Unix()-boottime.Unix(), 0).UTC().Format("15:04:05"))
t.WriteString(" | 系统运行 ")
t.WriteString(strconv.FormatInt(int64(hostinfo.Uptime)/86400, 10))
t.WriteString(" 天 ")
t.WriteString(time.Unix(int64(hostinfo.Uptime), 0).UTC().Format("15:04:05"))
return t.String(), nil
}
func botstatus() (string, error) {
hostinfo, err := host.Info()
if err != nil {
return "", err
}
t := &strings.Builder{}
t.WriteString(time.Now().Format("2006-01-02 15:04:05"))
t.WriteString(" | Compiled by ")
t.WriteString(runtime.Version())
t.WriteString(" | ")
t.WriteString(cases.Title(language.English).String(hostinfo.OS))
return t.String(), nil
}
type status struct {
precent float64
name string
text []string
}
func basicstate() (stateinfo [3]*status, err error) {
percent, err := cpu.Percent(time.Second, false)
if err != nil {
return -1
return
}
return math.Round(percent[0])
cpuinfo, err := cpu.Info()
if err != nil {
return
}
cores := strconv.Itoa(int(cpuinfo[0].Cores)) + " Core"
times := "最大 " + strconv.FormatFloat(cpuinfo[0].Mhz/1000, 'f', 1, 64) + "Ghz"
stateinfo[0] = &status{
precent: math.Round(percent[0]),
name: "CPU",
text: []string{cores, times},
}
raminfo, err := mem.VirtualMemory()
if err != nil {
return
}
total := "总共 " + storagefmt(float64(raminfo.Total))
used := "已用 " + storagefmt(float64(raminfo.Used))
free := "剩余 " + storagefmt(float64(raminfo.Free))
stateinfo[1] = &status{
precent: math.Round(raminfo.UsedPercent),
name: "RAM",
text: []string{total, used, free},
}
swapinfo, err := mem.SwapMemory()
if err != nil {
return
}
total = "总共 " + storagefmt(float64(swapinfo.Total))
used = "已用 " + storagefmt(float64(swapinfo.Used))
free = "剩余 " + storagefmt(float64(swapinfo.Free))
stateinfo[2] = &status{
precent: math.Round(swapinfo.UsedPercent),
name: "SWAP",
text: []string{total, used, free},
}
return
}
func memPercent() float64 {
memInfo, err := mem.VirtualMemory()
if err != nil {
return -1
func storagefmt(num float64) string {
if num /= 1024; num < 1 {
return strconv.FormatFloat(num*1024, 'f', 2, 64) + "B"
}
return math.Round(memInfo.UsedPercent)
if num /= 1024; num < 1 {
return strconv.FormatFloat(num*1024, 'f', 2, 64) + "KB"
}
if num /= 1024; num < 1 {
return strconv.FormatFloat(num*1024, 'f', 2, 64) + "MB"
}
if num /= 1024; num < 1 {
return strconv.FormatFloat(num*1024, 'f', 2, 64) + "GB"
}
return strconv.FormatFloat(num, 'f', 2, 64) + "TB"
}
func diskPercent() string {
parts, err := disk.Partitions(true)
func diskstate() (stateinfo []*status, err error) {
parts, err := disk.Partitions(false)
if err != nil {
return err.Error()
return
}
msg := ""
for _, p := range parts {
diskInfo, err := disk.Usage(p.Mountpoint)
stateinfo = make([]*status, len(parts))
for i, v := range parts {
mp := v.Mountpoint
diskusage, err := disk.Usage(mp)
usage := ""
precent := 0.0
if err != nil {
msg += "\n - " + err.Error()
continue
usage = err.Error()
} else {
usage = storagefmt(float64(diskusage.Used)) + " / " + storagefmt(float64(diskusage.Total))
precent = math.Round(diskusage.UsedPercent)
}
pc := uint(math.Round(diskInfo.UsedPercent))
if pc > 0 {
msg += fmt.Sprintf("\n - %s(%dM) %d%%", p.Mountpoint, diskInfo.Total/1024/1024, pc)
stateinfo[i] = &status{
precent: precent,
name: mp,
text: []string{usage},
}
}
return msg
return stateinfo, nil
}
func moreinfo(m *ctrl.Control[*zero.Ctx]) (stateinfo []*status, err error) {
hostinfo, err := host.Info()
if err != nil {
return
}
cpuinfo, err := cpu.Info()
if err != nil {
return
}
count := len(m.Manager.M)
stateinfo = []*status{
{name: "OS", text: []string{hostinfo.Platform}},
{name: "CPU", text: []string{cpuinfo[0].ModelName}},
{name: "Version", text: []string{hostinfo.PlatformVersion}},
{name: "Plugin", text: []string{"共 " + strconv.Itoa(count) + " 个"}},
}
return
}

View File

@@ -1,181 +1,251 @@
package aireply
import (
"regexp"
"strconv"
"sync"
"errors"
"net/url"
"github.com/pkumza/numcn"
log "github.com/sirupsen/logrus"
"github.com/RomiChan/syncx"
"github.com/sirupsen/logrus"
zero "github.com/wdvxdr1123/ZeroBot"
"github.com/wdvxdr1123/ZeroBot/message"
"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/mockingbird"
ctrl "github.com/FloatTech/zbpctrl"
"github.com/FloatTech/zbputils/control"
"github.com/FloatTech/zbputils/ctxext"
)
const ttsServiceName = "tts"
// 数据结构: [4 bits] [4 bits] [8 bits] [8 bits]
// [拟声鸟模式] [百度模式] [tts模式] [回复模式]
var re = regexp.MustCompile(`(\-|\+)?\d+(\.\d+)?`)
// defaultttsindexkey
// 数据结构: [4 bits] [4 bits] [8 bits]
// [拟声鸟模式] [百度模式] [tts模式]
type ttsInstances struct {
sync.RWMutex
m map[string]tts.TTS
l []string
}
// [tts模式]: 0~63 genshin 64 baidu 65 mockingbird
func (t *ttsInstances) List() []string {
t.RLock()
cl := make([]string, len(t.l))
_ = copy(cl, t.l)
t.RUnlock()
return cl
}
const (
lastgsttsindex = 63 + iota
baiduttsindex
mockingbirdttsindex
)
func init() {
t := &ttsInstances{
m: map[string]tts.TTS{
"百度女声": baidutts.NewBaiduTTS(0),
"百度男声": baidutts.NewBaiduTTS(1),
"百度度逍遥": baidutts.NewBaiduTTS(3),
"百度度丫丫": baidutts.NewBaiduTTS(4),
"拟声鸟阿梓": nil,
"拟声鸟文静": nil,
"拟声鸟药水哥": nil,
},
l: []string{"拟声鸟阿梓", "拟声鸟文静", "拟声鸟药水哥", "百度女声", "百度男声", "百度度逍遥", "百度度丫丫"},
}
engine := control.Register(ttsServiceName, &ctrl.Options[*zero.Ctx]{
DisableOnDefault: true,
Help: "语音回复(包括拟声鸟和百度)\n" +
"- @Bot 任意文本(任意一句话回复)\n" +
"- 设置语音模式[拟声鸟阿梓 | 拟声鸟文静 | 拟声鸟药水哥 | 百度女声 | 百度男声| 百度度逍遥 | 百度度丫丫]\n" +
"- 设置默认语音模式[拟声鸟阿梓 | 拟声鸟文静 | 拟声鸟药水哥 | 百度女声 | 百度男声| 百度度逍遥 | 百度度丫丫]\n",
})
engine.OnMessage(zero.OnlyToMe).SetBlock(true).Limit(ctxext.LimitByUser).
Handle(func(ctx *zero.Ctx) {
msg := ctx.ExtractPlainText()
r := aireply.NewAIReply(getReplyMode(ctx))
tts, err := t.new(t.getSoundMode(ctx))
if err != nil {
ctx.SendChain(message.Text("ERROR:", err))
return
}
var reply string
if tts != nil {
rec, err := tts.Speak(ctx.Event.UserID, func() string {
reply = r.TalkPlain(msg, zero.BotConfig.NickName[0])
reply = re.ReplaceAllStringFunc(reply, func(s string) string {
f, err := strconv.ParseFloat(s, 64)
if err != nil {
log.Errorln("[tts]:", err)
return s
}
return numcn.EncodeFromFloat64(f)
})
log.Debugln("[tts]:", reply)
return reply
})
if err == nil {
ctx.SendChain(message.Record(rec))
} else {
ctx.SendChain(message.Reply(ctx.Event.MessageID), message.Text(reply))
}
}
})
engine.OnRegex(`^设置语音模式(.*)$`, ctxext.ValueInList(func(ctx *zero.Ctx) string { return ctx.State["regex_matched"].([]string)[1] }, t)).SetBlock(true).
Handle(func(ctx *zero.Ctx) {
param := ctx.State["regex_matched"].([]string)[1]
err := t.setSoundMode(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("设置成功,当前模式为", param))
})
engine.OnRegex(`^设置默认语音模式(.*)$`, ctxext.ValueInList(func(ctx *zero.Ctx) string { return ctx.State["regex_matched"].([]string)[1] }, t)).SetBlock(true).
Handle(func(ctx *zero.Ctx) {
param := ctx.State["regex_matched"].([]string)[1]
t.setDefaultSoundMode(param)
ctx.SendChain(message.Reply(ctx.Event.MessageID), message.Text("设置成功,默认模式为", param))
})
}
const (
defaultttsindexkey = -2905
)
// new 语音简单工厂
func (t *ttsInstances) new(name string) (ts tts.TTS, err error) {
t.RLock()
ts = t.m[name]
t.RUnlock()
if ts == nil {
switch name {
case "拟声鸟阿梓":
t.Lock()
ts, err = mockingbird.NewMockingBirdTTS(0)
t.Unlock()
case "拟声鸟文静":
t.Lock()
ts, err = mockingbird.NewMockingBirdTTS(1)
t.Unlock()
case "拟声鸟药水哥":
t.Lock()
ts, err = mockingbird.NewMockingBirdTTS(2)
t.Unlock()
}
}
return
}
var replyModes = [...]string{"青云客", "小爱"}
func (t *ttsInstances) setSoundMode(ctx *zero.Ctx, name string) error {
func setReplyMode(ctx *zero.Ctx, name string) error {
gid := ctx.Event.GroupID
if gid == 0 {
gid = -ctx.Event.UserID
}
var ok bool
var index int64
t.RLock()
for i, s := range t.l {
for i, s := range replyModes {
if s == name {
ok = true
index = int64(i)
break
}
}
t.RUnlock()
m := ctx.State["manager"].(*ctrl.Control[*zero.Ctx])
return m.SetData(gid, index)
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(index)&^0xff)|(index&0xff))
}
func (t *ttsInstances) getSoundMode(ctx *zero.Ctx) (name string) {
func getReplyMode(ctx *zero.Ctx) aireply.AIReply {
gid := ctx.Event.GroupID
if gid == 0 {
gid = -ctx.Event.UserID
}
m, ok := ctx.State["manager"].(*ctrl.Control[*zero.Ctx])
if ok {
t.RLock()
defer t.RUnlock()
index := m.GetData(gid)
if int(index) < len(t.l) {
return t.l[index]
if m.GetData(gid)&0xff == 1 {
return aireply.NewXiaoAi(aireply.XiaoAiURL, aireply.XiaoAiBotName)
}
return aireply.NewQYK(aireply.QYKURL, aireply.QYKBotName)
}
return "拟声鸟阿梓"
return aireply.NewQYK(aireply.QYKURL, aireply.QYKBotName)
}
func (t *ttsInstances) setDefaultSoundMode(name string) {
var index int
t.RLock()
for _, s := range t.l {
var ttsins = func() map[string]tts.TTS {
m := make(map[string]tts.TTS, 128)
for _, mode := range append(genshin.SoundList[:], "百度", "拟声鸟") {
m[mode] = nil
}
return m
}()
var ttsModes = func() []string {
s := append(genshin.SoundList[:], make([]string, 64-len(genshin.SoundList))...) // 0-63
s = append(s, "百度", "拟声鸟") // 64 65
return s
}()
type ttsmode struct {
APIKey string // APIKey is for genshin vits
mode syncx.Map[int64, int64] `json:"-"` // mode grp index
}
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")
t.mode = syncx.Map[int64, int64]{}
t.mode.Store(defaultttsindexkey, 0)
if ok {
index := m.GetData(defaultttsindexkey)
msk := index & 0xff
if msk >= 0 && (msk < int64(len(genshin.SoundList)) || msk == baiduttsindex || msk == mockingbirdttsindex) {
t.mode.Store(defaultttsindexkey, index)
}
}
return t
}
func (t *ttsmode) getAPIKey(ctx *zero.Ctx) string {
if t.APIKey == "" {
m := ctx.State["manager"].(*ctrl.Control[*zero.Ctx])
_ = m.Manager.GetExtra(-1, &t)
logrus.Debugln("[tts] get api key:", t.APIKey)
}
return url.QueryEscape(t.APIKey)
}
func (t *ttsmode) setAPIKey(m *ctrl.Control[*zero.Ctx], key string) error {
t.APIKey = key
_ = m.Manager.Response(-1)
return m.Manager.SetExtra(-1, t)
}
func (t *ttsmode) setSoundMode(ctx *zero.Ctx, name string, baiduper, mockingsynt 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)
break
}
index++
}
t.RUnlock()
t.Lock()
t.l[0], t.l[index] = t.l[index], t.l[0]
t.Unlock()
if index == -1 {
switch name {
case "百度":
index = baiduttsindex
case "拟声鸟":
index = mockingbirdttsindex
default:
return errors.New("语音人物" + name + "未注册index")
}
}
m := ctx.State["manager"].(*ctrl.Control[*zero.Ctx])
t.mode.Store(gid, index)
return m.SetData(gid, (m.GetData(gid)&^0xffff00)|((index<<8)&0xff00)|((int64(baiduper)<<16)&0x0f0000)|((int64(mockingsynt)<<20)&0xf00000))
}
func (t *ttsmode) getSoundMode(ctx *zero.Ctx) (tts.TTS, error) {
gid := ctx.Event.GroupID
if gid == 0 {
gid = -ctx.Event.UserID
}
i, ok := t.mode.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(genshin.SoundList)) && m != baiduttsindex && m != mockingbirdttsindex) {
i, _ = t.mode.Load(defaultttsindexkey)
m = i & 0xff
}
mode := ttsModes[m]
ins, ok := ttsins[mode]
if !ok || ins == nil {
switch mode {
case "百度":
ins = baidutts.NewBaiduTTS(int(i&0x0f00) >> 8)
case "拟声鸟":
var err error
ins, err = mockingbird.NewMockingBirdTTS(int(i&0xf000) >> 12)
if err != nil {
return nil, err
}
default: // 原神
k := t.getAPIKey(ctx)
if k != "" {
ins = genshin.NewGenshin(int(m), t.getAPIKey(ctx))
ttsins[mode] = ins
} else {
return nil, errors.New("no valid speaker")
}
}
}
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])
index := m.GetData(defaultttsindexkey)
return m.SetData(gid, (m.GetData(gid)&0xff)|((index&^0xff)<<8)) // 重置数据
}
func (t *ttsmode) setDefaultSoundMode(name string, baiduper, mockingsynt 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)
break
}
}
if index == -1 {
switch name {
case "百度":
index = baiduttsindex
case "拟声鸟":
index = mockingbirdttsindex
default:
return errors.New("语音人物" + name + "未注册index")
}
}
m, ok := control.Lookup("tts")
if !ok {
return errors.New("[tts] service not found")
}
t.mode.Store(defaultttsindexkey, index)
return m.SetData(defaultttsindexkey, (index&0xff)|((int64(baiduper)<<8)&0x0f00)|((int64(mockingsynt)<<12)&0xf000))
}

View File

@@ -2,34 +2,44 @@
package aireply
import (
"errors"
"regexp"
"strconv"
"time"
"github.com/FloatTech/AnimeAPI/aireply"
"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"
)
const (
replyServiceName = "aireply"
)
var replyModes = [...]string{"青云客", "小爱"}
var t = newttsmode()
func init() { // 插件主体
engine := control.Register(replyServiceName, &ctrl.Options[*zero.Ctx]{
DisableOnDefault: false,
Help: "人工智能回复\n" +
"- @Bot 任意文本(任意一句话回复)\n- 设置回复模式[青云客 | 小爱]\n- ",
ent := control.Register("tts", &ctrl.Options[*zero.Ctx]{
DisableOnDefault: true,
Brief: "人工智能语音回复",
Help: "- @Bot 任意文本(任意一句话回复)\n" +
"- 设置语音模式[原神人物/百度/拟声鸟] 数字(百度/拟声鸟模式)\n" +
"- 设置默认语音模式[原神人物/百度/拟声鸟] 数字(百度/拟声鸟模式)\n" +
"- 恢复成默认语音模式\n" +
"- 设置原神语音 api key xxxxxx (key请加开发群获得)\n" +
"当前适用的原神人物含有以下:\n" + list(genshin.SoundList[:], 5),
})
// 回复 @和包括名字
engine.OnMessage(zero.OnlyToMe).SetBlock(true).Limit(ctxext.LimitByUser).
enr := control.Register("aireply", &ctrl.Options[*zero.Ctx]{
DisableOnDefault: false,
Brief: "人工智能回复",
Help: "- @Bot 任意文本(任意一句话回复)\n- 设置回复模式[青云客|小爱]",
PrivateDataFolder: "aireply",
})
enr.OnMessage(zero.OnlyToMe).SetBlock(true).Limit(ctxext.LimitByUser).
Handle(func(ctx *zero.Ctx) {
aireply := aireply.NewAIReply(getReplyMode(ctx))
reply := message.ParseMessageFromString(aireply.Talk(ctx.ExtractPlainText(), zero.BotConfig.NickName[0]))
aireply := getReplyMode(ctx)
reply := message.ParseMessageFromString(aireply.Talk(ctx.Event.UserID, ctx.ExtractPlainText(), zero.BotConfig.NickName[0]))
// 回复
time.Sleep(time.Second * 1)
if zero.OnlyPublic(ctx) {
@@ -39,53 +49,132 @@ func init() { // 插件主体
}
ctx.Send(reply)
})
engine.OnPrefix(`设置回复模式`).SetBlock(true).
enr.OnPrefix("设置回复模式", zero.AdminPermission).SetBlock(true).Handle(func(ctx *zero.Ctx) {
param := ctx.State["args"].(string)
err := 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("成功"))
})
endpre := regexp.MustCompile(`\pP$`)
ent.OnMessage(zero.OnlyToMe).SetBlock(true).Limit(ctxext.LimitByUser).
Handle(func(ctx *zero.Ctx) {
param := ctx.State["args"].(string)
err := setReplyMode(ctx, param)
msg := ctx.ExtractPlainText()
// 获取回复模式
r := getReplyMode(ctx)
// 获取回复的文本
reply := r.TalkPlain(ctx.Event.UserID, msg, zero.BotConfig.NickName[0])
// 获取语音
speaker, err := t.getSoundMode(ctx)
if err != nil {
ctx.SendChain(message.Reply(ctx.Event.MessageID), message.Text(err))
ctx.SendChain(message.Text("ERROR: ", err))
return
}
ctx.SendChain(message.Reply(ctx.Event.MessageID), message.Text("成功"))
rec, err := speaker.Speak(ctx.Event.UserID, func() string {
if !endpre.MatchString(reply) {
return reply + "。"
}
return reply
})
if err != nil {
ctx.SendChain(message.Reply(ctx.Event.MessageID), message.Text(reply))
return
}
// 发送语音
if id := ctx.SendChain(message.Record(rec)); id.ID() == 0 {
ctx.SendChain(message.Reply(ctx.Event.MessageID), message.Text(reply))
}
})
}
func 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 replyModes {
if s == name {
ok = true
index = int64(i)
break
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
}
}
}
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, index)
}
// 保存设置
logrus.Debugln("[tts] t.setSoundMode( ctx", param, n, n, ")")
err = t.setSoundMode(ctx, param, n, n)
if err != nil {
ctx.SendChain(message.Reply(ctx.Event.MessageID), message.Text(err))
return
}
if banner, ok := genshin.TestRecord[param]; ok {
logrus.Debugln("[tts] banner:", banner, "get sound mode...")
// 设置验证
speaker, err := t.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("设置成功"))
})
func getReplyMode(ctx *zero.Ctx) (name string) {
gid := ctx.Event.GroupID
if gid == 0 {
gid = -ctx.Event.UserID
}
m, ok := ctx.State["manager"].(*ctrl.Control[*zero.Ctx])
if ok {
index := m.GetData(gid)
if int(index) < len(replyModes) {
return replyModes[index]
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
}
}
}
return "青云客"
// 保存设置
err = t.setDefaultSoundMode(param, n, 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 := t.resetSoundMode(ctx)
if err != nil {
ctx.SendChain(message.Reply(ctx.Event.MessageID), message.Text(err))
return
}
// 设置验证
speaker, err := t.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 := t.setAPIKey(ctx.State["manager"].(*ctrl.Control[*zero.Ctx]), ctx.State["regex_matched"].([]string)[1])
if err != nil {
ctx.SendChain(message.Text("ERROR: ", err))
return
}
ctx.SendChain(message.Text("设置成功"))
})
}

224
plugin/aipaint/aipaint.go Normal file
View File

@@ -0,0 +1,224 @@
// Package aipaint ai绘图
package aipaint
import (
"encoding/base64"
"encoding/json"
"fmt"
"net/url"
"os"
"regexp"
"strconv"
"strings"
"time"
"github.com/FloatTech/floatbox/binary"
"github.com/FloatTech/floatbox/file"
"github.com/FloatTech/floatbox/web"
ctrl "github.com/FloatTech/zbpctrl"
"github.com/FloatTech/zbputils/control"
"github.com/FloatTech/zbputils/ctxext"
zero "github.com/wdvxdr1123/ZeroBot"
"github.com/wdvxdr1123/ZeroBot/message"
)
var (
datapath string
predictRe = regexp.MustCompile(`{"steps".+?}`)
// 参考host http://91.217.139.190:5010 http://91.216.169.75:5010
aipaintTxt2ImgURL = "/got_image?token=%v&tags=%v"
aipaintImg2ImgURL = "/got_image2image?token=%v&tags=%v"
cfg = newServerConfig("data/aipaint/config.json")
)
type result struct {
Steps int `json:"steps"`
Sampler string `json:"sampler"`
Seed int `json:"seed"`
Strength float64 `json:"strength"`
Noise float64 `json:"noise"`
Scale float64 `json:"scale"`
Uc string `json:"uc"`
}
func (r *result) String() string {
return fmt.Sprintf("steps: %v\nsampler: %v\nseed: %v\nstrength: %v\nnoise: %v\nscale: %v\nuc: %v\n", r.Steps, r.Sampler, r.Seed, r.Strength, r.Noise, r.Scale, r.Uc)
}
func init() { // 插件主体
engine := control.Register("aipaint", &ctrl.Options[*zero.Ctx]{
DisableOnDefault: false,
Brief: "ai绘图",
Help: "- [ ai绘图 | 生成色图 | 生成涩图 | ai画图 ] xxx\n" +
"- [ ai高级绘图 | 高级生成色图 | 高级生成涩图 | ai高级画图 ] [prompt]\n" +
"- 设置ai绘图配置 [server] [token]\n" +
"- 设置ai绘图撤回时间90s\n" +
"- 查看ai绘图配置\n" +
"Tips: 使用前请先前往 http://91.217.139.190:5010/token 按提示获取token" +
"设置token示例(请确保是主人并且响应): 设置ai绘图配置 http://91.217.139.190:5010 [token] (中括号无需输入)\n" +
"参考服务器 http://91.217.139.190:5010, http://91.216.169.75:5010, http://185.80.202.180:5010\n" +
"[prompt]参数如下\n" +
"tags:tag词条\nntags:ntag词条\nshape:[Portrait|Landscape|Square]\nscale:[6:20]\nseed:种子\nstrength:[0-1] 建议0-0.7\nnoise:[0-1] 建议0-0.15" +
"参数与参数内容用:连接,每个参数之间用回车分割",
PrivateDataFolder: "aipaint",
})
datapath = file.BOTPATH + "/" + engine.DataFolder()
if file.IsNotExist(cfg.file) {
s := serverConfig{}
data, err := json.Marshal(s)
if err != nil {
panic(err)
}
err = os.WriteFile(cfg.file, data, 0666)
if err != nil {
panic(err)
}
}
engine.OnPrefixGroup([]string{`ai绘图`, `生成色图`, `生成涩图`, `ai画图`}).SetBlock(true).
Handle(func(ctx *zero.Ctx) {
err := cfg.load()
if err != nil {
ctx.SendChain(message.Text("ERROR: ", err))
return
}
ctx.SendChain(message.Text("少女祈祷中..."))
args := ctx.State["args"].(string)
data, err := web.GetData(cfg.BaseURL + fmt.Sprintf(aipaintTxt2ImgURL, cfg.Token, url.QueryEscape(strings.TrimSpace(strings.ReplaceAll(args, " ", "%20")))))
if err != nil {
ctx.SendChain(message.Text("ERROR: ", err))
return
}
sendAiImg(ctx, data, cfg.Interval)
})
engine.OnPrefixGroup([]string{`ai高级绘图`, `高级生成色图`, `高级生成涩图`, `ai高级画图`}).SetBlock(true).
Handle(func(ctx *zero.Ctx) {
err := cfg.load()
if err != nil {
ctx.SendChain(message.Text("ERROR: ", err))
return
}
tags := make(map[string]string)
args := strings.Split(ctx.State["args"].(string), "\n")
if len(args) < 1 {
ctx.SendChain(message.Text("ERROR: 请输入正确的参数"))
return
}
for _, info := range args {
value := strings.Split(info, ":")
if len(value) > 1 {
if value[0] == "R18" && value[1] == "1" {
value[1] = "0"
ctx.SendChain(message.Text("不准涩涩! 已将R18设置为0. "))
}
tags[value[0]] = strings.Join(value[1:], ":")
}
}
ctx.SendChain(message.Text("少女祈祷中..."))
apiurl := "/got_image?token=" + cfg.Token
if _, ok := tags["tags"]; ok {
apiurl += "&tags=" + url.QueryEscape(strings.ReplaceAll(strings.TrimSpace(tags["tags"]), " ", "%20"))
}
if _, ok := tags["ntags"]; ok {
apiurl += "&ntags=" + url.QueryEscape(strings.ReplaceAll(strings.TrimSpace(tags["ntags"]), " ", "%20"))
}
if _, ok := tags["R18"]; ok {
apiurl += "&R18=" + url.QueryEscape(strings.TrimSpace(tags["R18"]))
}
if _, ok := tags["shape"]; ok {
apiurl += "&shape=" + url.QueryEscape(strings.TrimSpace(tags["shape"]))
}
if _, ok := tags["scale"]; ok {
apiurl += "&scale=" + url.QueryEscape(strings.TrimSpace(tags["scale"]))
}
if _, ok := tags["seed"]; ok {
apiurl += "&seed=" + url.QueryEscape(strings.TrimSpace(tags["seed"]))
}
if _, ok := tags["strength"]; ok {
apiurl += "&strength=" + url.QueryEscape(strings.TrimSpace(tags["strength"]))
}
if _, ok := tags["noise"]; ok {
apiurl += "&noise=" + url.QueryEscape(strings.TrimSpace(tags["noise"]))
}
data, err := web.GetData(cfg.BaseURL + apiurl)
if err != nil {
ctx.SendChain(message.Text("ERROR: ", err))
return
}
sendAiImg(ctx, data, cfg.Interval)
})
engine.OnRegex(`^设置ai绘图配置\s(.*[^\s$])\s(.+)$`, zero.OnlyPrivate, zero.SuperUserPermission).SetBlock(true).
Handle(func(ctx *zero.Ctx) {
regexMatched := ctx.State["regex_matched"].([]string)
err := cfg.load()
if err != nil {
ctx.SendChain(message.Text("ERROR: ", err))
return
}
err = cfg.update(regexMatched[1], regexMatched[2], cfg.Interval)
if err != nil {
ctx.SendChain(message.Text("ERROR: ", err))
return
}
ctx.SendChain(message.Text("成功设置\nbase_url: ", cfg.BaseURL, "\ntoken: ", cfg.Token, "\ninterval: ", cfg.Interval))
})
engine.OnRegex(`^设置ai绘图撤回时间(\d{1,3})s$`, zero.SuperUserPermission).SetBlock(true).
Handle(func(ctx *zero.Ctx) {
regexMatched := ctx.State["regex_matched"].([]string)
interval, err := strconv.Atoi(regexMatched[1])
if err != nil {
ctx.SendChain(message.Text("ERROR: ", err))
return
}
err = cfg.load()
if err != nil {
ctx.SendChain(message.Text("ERROR: ", err))
return
}
err = cfg.update(cfg.BaseURL, cfg.Token, interval)
if err != nil {
ctx.SendChain(message.Text("ERROR: ", err))
return
}
ctx.SendChain(message.Text("成功设置撤回时间为", cfg.Interval, "s"))
})
engine.OnFullMatch(`查看ai绘图配置`, zero.OnlyPrivate, zero.SuperUserPermission).SetBlock(true).
Handle(func(ctx *zero.Ctx) {
err := cfg.load()
if err != nil {
ctx.SendChain(message.Text("ERROR: ", err))
return
}
ctx.SendChain(message.Text("base_url: ", cfg.BaseURL, "\ntoken: ", cfg.Token, "\ninterval: ", cfg.Interval))
})
}
func sendAiImg(ctx *zero.Ctx, data []byte, interval int) {
var loadData string
if predictRe.MatchString(binary.BytesToString(data)) {
loadData = predictRe.FindStringSubmatch(binary.BytesToString(data))[0]
}
var r result
if loadData != "" {
err := json.Unmarshal(binary.StringToBytes(loadData), &r)
if err != nil {
ctx.SendChain(message.Text("ERROR: ", err))
return
}
r.Uc, err = url.QueryUnescape(r.Uc)
if err != nil {
ctx.SendChain(message.Text("ERROR: ", err))
return
}
}
encodeStr := base64.StdEncoding.EncodeToString(data)
m := message.Message{ctxext.FakeSenderForwardNode(ctx, message.Image("base64://"+encodeStr))}
m = append(m, ctxext.FakeSenderForwardNode(ctx, message.Text(r.String())))
if mid := ctx.Send(m); mid.ID() == 0 {
ctx.SendChain(message.Text("ERROR: 可能被风控或下载图片用时过长,请耐心等待"))
} else if interval > 0 {
go func(i message.MessageID) {
time.Sleep(time.Duration(interval) * time.Second)
ctx.DeleteMessage(i)
}(mid)
}
}

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

@@ -0,0 +1,56 @@
package aipaint
import (
"encoding/json"
"errors"
"os"
"github.com/FloatTech/floatbox/file"
)
// 配置结构体
type serverConfig struct {
BaseURL string `json:"base_url"`
Token string `json:"token"`
Interval int `json:"interval"`
file string
}
func newServerConfig(file string) *serverConfig {
return &serverConfig{
file: file,
}
}
func (cfg *serverConfig) update(baseURL, token string, interval int) (err error) {
if baseURL != "" {
cfg.BaseURL = baseURL
}
if token != "" {
cfg.Token = token
}
cfg.Interval = interval
reader, err := os.Create(cfg.file)
if err != nil {
return err
}
defer reader.Close()
return json.NewEncoder(reader).Encode(cfg)
}
func (cfg *serverConfig) load() (err error) {
if cfg.BaseURL != "" && cfg.Token != "" && cfg.Interval != 0 {
return
}
if file.IsNotExist(cfg.file) {
err = errors.New("no server config")
return
}
reader, err := os.Open(cfg.file)
if err != nil {
return
}
defer reader.Close()
err = json.NewDecoder(reader).Decode(cfg)
return
}

41
plugin/aipaint/context.go Normal file
View File

@@ -0,0 +1,41 @@
package aipaint
import (
"os"
"strconv"
"strings"
"github.com/FloatTech/floatbox/file"
"github.com/FloatTech/floatbox/process"
)
type context struct {
usrdir string
headimgsdir []string
}
func newContext(user int64) *context {
c := new(context)
c.usrdir = datapath + "users/" + strconv.FormatInt(user, 10) + `/`
_ = os.MkdirAll(c.usrdir, 0755)
c.headimgsdir = make([]string, 2)
c.headimgsdir[0] = c.usrdir + "0.gif"
c.headimgsdir[1] = c.usrdir + "1.gif"
return c
}
func (cc *context) prepareLogos(s ...string) error {
for i, v := range s {
_, err := strconv.Atoi(v)
if err != nil {
err = file.DownloadTo("https://gchat.qpic.cn/gchatpic_new//--"+strings.ToUpper(v)+"/0", cc.usrdir+strconv.Itoa(i)+".gif")
} else {
err = file.DownloadTo("http://q4.qlogo.cn/g?b=qq&nk="+v+"&s=640", cc.usrdir+strconv.Itoa(i)+".gif")
}
if err != nil {
return err
}
process.SleepAbout1sTo2s()
}
return nil
}

88
plugin/aipaint/img2img.go Normal file
View File

@@ -0,0 +1,88 @@
// Package aipaint ai绘图
package aipaint
import (
"bytes"
"fmt"
"image"
"net/url"
"os"
"strconv"
"strings"
"github.com/FloatTech/floatbox/file"
"github.com/FloatTech/floatbox/web"
"github.com/FloatTech/imgfactory"
ctrl "github.com/FloatTech/zbpctrl"
"github.com/FloatTech/zbputils/control"
zero "github.com/wdvxdr1123/ZeroBot"
"github.com/wdvxdr1123/ZeroBot/message"
)
func init() { // 插件主体
engine := control.Register("img2img", &ctrl.Options[*zero.Ctx]{
DisableOnDefault: true,
Brief: "以图绘图",
Help: "- [ 以图绘图 | 以图生图 | 以图画图 ] xxx [图片]|@xxx|[qq号]\n" +
"- 官方以图绘图api已失效 需要自建api 其他配置参数同ai绘图",
PrivateDataFolder: "img2img",
})
datapath = file.BOTPATH + "/" + engine.DataFolder()
engine.OnRegex(`^(以图绘图|以图生图|以图画图)[\s\S]*?(\[CQ:(image\,file=([0-9a-zA-Z]{32}).*|at.+?(\d{5,11}))\].*|(\d+))$`).SetBlock(true).
Handle(func(ctx *zero.Ctx) {
err := cfg.load()
if err != nil {
ctx.SendChain(message.Text("ERROR: ", err))
return
}
c := newContext(ctx.Event.UserID)
list := ctx.State["regex_matched"].([]string)
err = c.prepareLogos(list[4]+list[5]+list[6], strconv.FormatInt(ctx.Event.UserID, 10))
if err != nil {
ctx.SendChain(message.Text("ERROR: ", err))
return
}
args := strings.TrimSuffix(strings.TrimPrefix(list[0], list[1]), list[2])
if args == "" {
ctx.SendChain(message.Text("ERROR: 以图绘图必须添加tag"))
return
}
ctx.SendChain(message.Text("少女祈祷中..."))
postURL := cfg.BaseURL + fmt.Sprintf(aipaintImg2ImgURL, cfg.Token, url.QueryEscape(strings.TrimSpace(strings.ReplaceAll(args, " ", "%20"))))
f, err := os.Open(c.headimgsdir[0])
if err != nil {
ctx.SendChain(message.Text("ERROR: ", err))
return
}
defer f.Close()
img, _, err := image.Decode(f)
if err != nil {
ctx.SendChain(message.Text("ERROR: ", err))
return
}
imageShape := ""
switch {
case img.Bounds().Dx() > img.Bounds().Dy():
imageShape = "Landscape"
case img.Bounds().Dx() == img.Bounds().Dy():
imageShape = "Square"
default:
imageShape = "Portrait"
}
// 图片转base64
base64Bytes, err := imgfactory.ToBase64(img)
if err != nil {
ctx.SendChain(message.Text("ERROR: ", err))
return
}
data, err := web.PostData(postURL+"&shape="+imageShape, "text/plain", bytes.NewReader(base64Bytes))
if err != nil {
ctx.SendChain(message.Text("ERROR: ", err))
return
}
sendAiImg(ctx, data, cfg.Interval)
})
}

View File

@@ -19,8 +19,8 @@ const (
func init() { // 插件主体
control.Register("aiwife", &ctrl.Options[*zero.Ctx]{
DisableOnDefault: false,
Help: "AIWife\n" +
"- waifu | 随机waifu",
Brief: "ai随机生成老婆",
Help: "- waifu | 随机waifu",
}).ApplySingle(ctxext.DefaultSingle).OnFullMatchGroup([]string{"waifu", "随机waifu"}).SetBlock(true).
Handle(func(ctx *zero.Ctx) {
miku := rand.Intn(100000) + 1

View File

@@ -0,0 +1,32 @@
// Package alipayvoice 支付宝到账语音
package alipayvoice
import (
"fmt"
"strings"
ctrl "github.com/FloatTech/zbpctrl"
"github.com/FloatTech/zbputils/control"
zero "github.com/wdvxdr1123/ZeroBot"
"github.com/wdvxdr1123/ZeroBot/message"
)
const (
alipayvoiceURL = "https://mm.cqu.cc/share/zhifubaodaozhang/mp3/%v.mp3"
)
func init() { // 插件主体
engine := control.Register("alipayvoice", &ctrl.Options[*zero.Ctx]{
DisableOnDefault: false,
Brief: "支付宝到账语音",
Help: "- 支付宝到账 1",
PrivateDataFolder: "alipayvoice",
})
// 开启
engine.OnPrefix(`支付宝到账`).SetBlock(true).
Handle(func(ctx *zero.Ctx) {
args := ctx.State["args"].(string)
ctx.SendChain(message.Record(fmt.Sprintf(alipayvoiceURL, strings.TrimSpace(args))))
})
}

124
plugin/antiabuse/anti.go Normal file
View File

@@ -0,0 +1,124 @@
// Package antiabuse defines antiabuse plugin ,support abuse words check and add/remove abuse words
package antiabuse
import (
"strconv"
"strings"
"time"
"github.com/FloatTech/floatbox/binary"
fcext "github.com/FloatTech/floatbox/ctxext"
"github.com/FloatTech/ttl"
ctrl "github.com/FloatTech/zbpctrl"
"github.com/FloatTech/zbputils/control"
"github.com/FloatTech/zbputils/img/text"
"github.com/sirupsen/logrus"
zero "github.com/wdvxdr1123/ZeroBot"
"github.com/wdvxdr1123/ZeroBot/message"
)
const bandur time.Duration = time.Minute * 10
var (
managers *ctrl.Manager[*zero.Ctx] // managers lazy load
cache = ttl.NewCacheOn(bandur, [4]func(int64, struct{}){nil, nil, onDel, nil})
db *antidb
)
func onDel(uid int64, _ struct{}) {
if managers == nil {
return
}
if err := managers.DoUnblock(uid); err != nil {
logrus.Errorln("[antiabuse.onDel] unblock:", err)
}
if err := db.Del("__bantime__", "WHERE id="+strconv.FormatInt(uid, 10)); err != nil {
logrus.Errorln("[antiabuse.onDel] db:", err)
}
}
func init() {
engine := control.Register("antiabuse", &ctrl.Options[*zero.Ctx]{
DisableOnDefault: false,
Brief: "违禁词检测",
Help: "- /[添加|删除|查看]违禁词",
PrivateDataFolder: "anti_abuse",
})
onceRule := fcext.DoOnceOnSuccess(func(ctx *zero.Ctx) bool {
managers = ctx.State["manager"].(*ctrl.Control[*zero.Ctx]).Manager
var err error
db, err = newantidb(engine.DataFolder() + "anti_abuse.db")
if err != nil {
ctx.SendChain(message.Text("ERROR: ", err))
return false
}
return true
})
engine.OnMessage(onceRule, zero.OnlyGroup, func(ctx *zero.Ctx) bool {
if !ctx.Event.IsToMe {
return true
}
uid := ctx.Event.UserID
gid := ctx.Event.GroupID
msg := strings.ReplaceAll(ctx.MessageString(), "\n", "")
msg = strings.ReplaceAll(msg, "\r", "")
msg = strings.ReplaceAll(msg, "\t", "")
msg = strings.ReplaceAll(msg, ";", "")
if db.isInAntiList(gid, msg) {
if err := ctx.State["manager"].(*ctrl.Control[*zero.Ctx]).Manager.DoBlock(uid); err == nil {
t := time.Now().Unix()
cache.Set(uid, struct{}{})
ctx.SetGroupBan(gid, uid, int64(bandur.Minutes()))
ctx.SendChain(message.Text("检测到违禁词, 已封禁/屏蔽", bandur))
db.Lock()
defer db.Unlock()
err := db.Create("__bantime__", nilbt)
if err != nil {
ctx.SendChain(message.Text("ERROR: ", err))
return false
}
err = db.Insert("__bantime__", &banTime{ID: uid, Time: t})
if err != nil {
ctx.SendChain(message.Text("ERROR: ", err))
return false
}
} else {
ctx.SendChain(message.Text("ERROR: block user: ", err))
}
return false
}
return true
})
engine.OnCommand("添加违禁词", zero.OnlyGroup, zero.AdminPermission, onceRule).Handle(
func(ctx *zero.Ctx) {
args := ctx.State["args"].(string)
if err := db.insertWord(ctx.Event.GroupID, args); err != nil {
ctx.SendChain(message.Text("ERROR: ", err))
} else {
ctx.SendChain(message.Text("成功"))
}
})
engine.OnCommand("删除违禁词", zero.OnlyGroup, zero.AdminPermission, onceRule).Handle(
func(ctx *zero.Ctx) {
args := ctx.State["args"].(string)
if err := db.deleteWord(ctx.Event.GroupID, args); err != nil {
ctx.SendChain(message.Text("ERROR: ", err))
} else {
ctx.SendChain(message.Text("成功"))
}
})
engine.OnCommand("查看违禁词", zero.OnlyGroup, onceRule).Handle(
func(ctx *zero.Ctx) {
b, err := text.RenderToBase64(db.listWords(ctx.Event.GroupID), text.FontFile, 400, 20)
if err != nil {
ctx.SendChain(message.Text("ERROR: ", err))
return
}
ctx.SendChain(message.Text("本群违禁词有\n"), message.Image("base64://"+binary.BytesToString(b)))
})
}

102
plugin/antiabuse/db.go Normal file
View File

@@ -0,0 +1,102 @@
package antiabuse
import (
"errors"
"strconv"
"strings"
"sync"
"time"
sqlite "github.com/FloatTech/sqlite"
)
type antidb struct {
sync.RWMutex
sqlite.Sqlite
}
type banWord struct {
Word string `db:"word"`
}
type banTime struct {
ID int64 `db:"id"`
Time int64 `db:"time"`
}
var (
nilban = &banWord{}
nilbt = &banTime{}
)
func newantidb(path string) (*antidb, error) {
db := &antidb{Sqlite: sqlite.Sqlite{DBPath: path}}
err := db.Open(bandur)
if err != nil {
return nil, err
}
_ = db.FindFor("__bantime__", nilbt, "", func() error {
t := time.Unix(nilbt.Time, 0)
ttl := time.Until(t.Add(bandur))
if ttl < time.Minute {
_ = managers.DoUnblock(nilbt.ID)
return nil
}
cache.Set(nilbt.ID, struct{}{})
cache.Touch(nilbt.ID, -time.Since(t))
return nil
})
_ = db.Del("__bantime__", "WHERE time<="+strconv.FormatInt(time.Now().Add(time.Minute-bandur).Unix(), 10))
return db, nil
}
func (db *antidb) isInAntiList(gid int64, msg string) bool {
grp := strconv.FormatInt(gid, 36)
db.RLock()
defer db.RUnlock()
return db.CanFind(grp, "WHERE instr('"+msg+"', word)>0")
}
func (db *antidb) insertWord(gid int64, word string) error {
grp := strconv.FormatInt(gid, 36)
db.Lock()
defer db.Unlock()
err := db.Create(grp, nilban)
if err != nil {
return err
}
return db.Insert(grp, &banWord{Word: word})
}
func (db *antidb) deleteWord(gid int64, word string) error {
grp := strconv.FormatInt(gid, 36)
db.Lock()
defer db.Unlock()
if n, _ := db.Count(grp); n == 0 {
return errors.New("本群还没有违禁词~")
}
return db.Del(grp, "WHERE word='"+word+"'")
}
func (db *antidb) listWords(gid int64) string {
grp := strconv.FormatInt(gid, 36)
word := &banWord{}
sb := strings.Builder{}
sb.WriteByte('[')
i := 0
db.RLock()
defer db.RUnlock()
_ = db.FindFor(grp, word, "", func() error {
if i > 0 {
sb.WriteString(" | ")
}
sb.WriteString(word.Word)
i++
return nil
})
if sb.Len() <= 4 {
return "[]"
}
sb.WriteByte(']')
return sb.String()
}

View File

@@ -6,6 +6,7 @@ Package atri 本文件基于 https://github.com/Kyomotoi/ATRI
package atri
import (
"encoding/base64"
"math/rand"
"time"
@@ -14,58 +15,78 @@ import (
ctrl "github.com/FloatTech/zbpctrl"
"github.com/FloatTech/zbputils/control"
"github.com/FloatTech/zbputils/process"
)
const (
// 服务名
servicename = "atri"
// ATRI 表情的 codechina 镜像
res = "https://gitcode.net/u011570312/zbpdata/-/raw/main/Atri/"
)
type datagetter func(string, bool) ([]byte, error)
func (dgtr datagetter) randImage(file ...string) message.MessageSegment {
data, err := dgtr(file[rand.Intn(len(file))], true)
if err != nil {
return message.Text("ERROR: ", err)
}
return message.ImageBytes(data)
}
func (dgtr datagetter) randRecord(file ...string) message.MessageSegment {
data, err := dgtr(file[rand.Intn(len(file))], true)
if err != nil {
return message.Text("ERROR: ", err)
}
return message.Record("base64://" + base64.StdEncoding.EncodeToString(data))
}
func randText(text ...string) message.MessageSegment {
return message.Text(text[rand.Intn(len(text))])
}
// isAtriSleeping 凌晨0点到6点ATRI 在睡觉,不回应任何请求
func isAtriSleeping(ctx *zero.Ctx) bool {
if now := time.Now().Hour(); now >= 1 && now < 6 {
return false
}
return true
}
func init() { // 插件主体
engine := control.Register(servicename, &ctrl.Options[*zero.Ctx]{
engine := control.Register("atri", &ctrl.Options[*zero.Ctx]{
DisableOnDefault: false,
Brief: "atri人格文本回复",
Help: "本插件基于 ATRI ,为 Golang 移植版\n" +
"- ATRI醒醒\n- ATRI睡吧\n- 萝卜子\n- 喜欢 | 爱你 | 爱 | suki | daisuki | すき | 好き | 贴贴 | 老婆 | 亲一个 | mua\n" +
"- 草你妈 | 操你妈 | 脑瘫 | 废柴 | fw | 废物 | 战斗 | 爬 | 爪巴 | sb | SB | 傻B\n- 早安 | 早哇 | 早上好 | ohayo | 哦哈哟 | お早う | 早好 | 早 | 早早早\n" +
"- 中午好 | 午安 | 午好\n- 晚安 | oyasuminasai | おやすみなさい | 晚好 | 晚上好\n- 高性能 | 太棒了 | すごい | sugoi | 斯国一 | よかった\n" +
"- 没事 | 没关系 | 大丈夫 | 还好 | 不要紧 | 没出大问题 | 没伤到哪\n- 好吗 | 是吗 | 行不行 | 能不能 | 可不可以\n- 啊这\n- 我好了\n- | ? | ¿\n" +
"- 离谱\n- 答应我",
PublicDataFolder: "Atri",
OnEnable: func(ctx *zero.Ctx) {
process.SleepAbout1sTo2s()
ctx.SendChain(message.Text("嗯呜呜……夏生先生……?"))
},
OnDisable: func(ctx *zero.Ctx) {
process.SleepAbout1sTo2s()
ctx.SendChain(message.Text("Zzz……Zzz……"))
},
})
engine.OnFullMatch("萝卜子", isAtriSleeping).SetBlock(true).
engine.UsePreHandler(isAtriSleeping)
var dgtr datagetter = engine.GetLazyData
engine.OnFullMatch("萝卜子").SetBlock(true).
Handle(func(ctx *zero.Ctx) {
process.SleepAbout1sTo2s()
switch rand.Intn(2) {
case 0:
ctx.SendChain(randText("萝卜子是对机器人的蔑称!", "是亚托莉......萝卜子可是对机器人的蔑称"))
case 1:
ctx.SendChain(randRecord("RocketPunch.amr"))
ctx.SendChain(dgtr.randRecord("RocketPunch.amr"))
}
})
engine.OnFullMatchGroup([]string{"喜欢", "爱你", "爱", "suki", "daisuki", "すき", "好き", "贴贴", "老婆", "亲一个", "mua"}, isAtriSleeping, zero.OnlyToMe).SetBlock(true).
engine.OnFullMatchGroup([]string{"喜欢", "爱你", "爱", "suki", "daisuki", "すき", "好き", "贴贴", "老婆", "亲一个", "mua"}, zero.OnlyToMe).SetBlock(true).
Handle(func(ctx *zero.Ctx) {
process.SleepAbout1sTo2s()
ctx.SendChain(randImage("SUKI.jpg", "SUKI1.jpg", "SUKI2.png"))
ctx.SendChain(dgtr.randImage("SUKI.jpg", "SUKI1.jpg", "SUKI2.png"))
})
engine.OnKeywordGroup([]string{"草你妈", "操你妈", "脑瘫", "废柴", "fw", "five", "废物", "战斗", "爬", "爪巴", "sb", "SB", "傻B"}, isAtriSleeping, zero.OnlyToMe).SetBlock(true).
engine.OnKeywordGroup([]string{"草你妈", "操你妈", "脑瘫", "废柴", "fw", "five", "废物", "战斗", "爬", "爪巴", "sb", "SB", "傻B"}, zero.OnlyToMe).SetBlock(true).
Handle(func(ctx *zero.Ctx) {
process.SleepAbout1sTo2s()
ctx.SendChain(randImage("FN.jpg", "WQ.jpg", "WQ1.jpg"))
ctx.SendChain(dgtr.randImage("FN.jpg", "WQ.jpg", "WQ1.jpg"))
})
engine.OnFullMatchGroup([]string{"早安", "早哇", "早上好", "ohayo", "哦哈哟", "お早う", "早好", "早", "早早早"}).SetBlock(true).
Handle(func(ctx *zero.Ctx) {
now := time.Now().Hour()
process.SleepAbout1sTo2s()
switch {
case now < 6: // 凌晨
ctx.SendChain(message.Reply(ctx.Event.MessageID), randText(
@@ -106,7 +127,6 @@ func init() { // 插件主体
Handle(func(ctx *zero.Ctx) {
now := time.Now().Hour()
if now > 11 && now < 15 { // 中午
process.SleepAbout1sTo2s()
ctx.SendChain(message.Reply(ctx.Event.MessageID), randText(
"午安w",
"午觉要好好睡哦ATRI会陪伴在你身旁的w",
@@ -118,7 +138,6 @@ func init() { // 插件主体
engine.OnFullMatchGroup([]string{"晚安", "oyasuminasai", "おやすみなさい", "晚好", "晚上好"}).SetBlock(true).
Handle(func(ctx *zero.Ctx) {
now := time.Now().Hour()
process.SleepAbout1sTo2s()
switch {
case now < 6: // 凌晨
ctx.SendChain(message.Reply(ctx.Event.MessageID), randText(
@@ -158,9 +177,8 @@ func init() { // 插件主体
))
}
})
engine.OnKeywordGroup([]string{"高性能", "太棒了", "すごい", "sugoi", "斯国一", "よかった"}, isAtriSleeping, zero.OnlyToMe).SetBlock(true).
engine.OnKeywordGroup([]string{"高性能", "太棒了", "すごい", "sugoi", "斯国一", "よかった"}, zero.OnlyToMe).SetBlock(true).
Handle(func(ctx *zero.Ctx) {
process.SleepAbout1sTo2s()
ctx.SendChain(randText(
"当然,我是高性能的嘛~",
"小事一桩,我是高性能的嘛",
@@ -179,9 +197,8 @@ func init() { // 插件主体
"呣......我的高性能,毫无遗憾地施展出来了......",
))
})
engine.OnKeywordGroup([]string{"没事", "没关系", "大丈夫", "还好", "不要紧", "没出大问题", "没伤到哪"}, isAtriSleeping, zero.OnlyToMe).SetBlock(true).
engine.OnKeywordGroup([]string{"没事", "没关系", "大丈夫", "还好", "不要紧", "没出大问题", "没伤到哪"}, zero.OnlyToMe).SetBlock(true).
Handle(func(ctx *zero.Ctx) {
process.SleepAbout1sTo2s()
ctx.SendChain(randText(
"当然,我是高性能的嘛~",
"没事没事,因为我是高性能的嘛!嗯哼!",
@@ -194,67 +211,42 @@ func init() { // 插件主体
))
})
engine.OnKeywordGroup([]string{"好吗", "是吗", "行不行", "能不能", "可不可以"}, isAtriSleeping).SetBlock(true).
engine.OnKeywordGroup([]string{"好吗", "是吗", "行不行", "能不能", "可不可以"}).SetBlock(true).
Handle(func(ctx *zero.Ctx) {
process.SleepAbout1sTo2s()
if rand.Intn(2) == 0 {
ctx.SendChain(randImage("YES.png", "NO.jpg"))
ctx.SendChain(dgtr.randImage("YES.png", "NO.jpg"))
}
})
engine.OnKeywordGroup([]string{"啊这"}, isAtriSleeping).SetBlock(true).
engine.OnKeywordGroup([]string{"啊这"}).SetBlock(true).
Handle(func(ctx *zero.Ctx) {
process.SleepAbout1sTo2s()
if rand.Intn(2) == 0 {
ctx.SendChain(randImage("AZ.jpg", "AZ1.jpg"))
ctx.SendChain(dgtr.randImage("AZ.jpg", "AZ1.jpg"))
}
})
engine.OnKeywordGroup([]string{"我好了"}, isAtriSleeping).SetBlock(true).
engine.OnKeywordGroup([]string{"我好了"}).SetBlock(true).
Handle(func(ctx *zero.Ctx) {
process.SleepAbout1sTo2s()
ctx.SendChain(message.Reply(ctx.Event.MessageID), randText("不许好!", "憋回去!"))
})
engine.OnFullMatchGroup([]string{"", "?", "¿"}, isAtriSleeping).SetBlock(true).
Handle(func(ctx *zero.Ctx) {
process.SleepAbout1sTo2s()
switch rand.Intn(5) {
case 0:
ctx.SendChain(randText("?", "", "嗯?", "(。´・ω・)ん?", "ん?"))
case 1, 2:
ctx.SendChain(randImage("WH.jpg", "WH1.jpg", "WH2.jpg", "WH3.jpg"))
}
})
engine.OnKeyword("离谱", isAtriSleeping).SetBlock(true).
engine.OnFullMatchGroup([]string{"", "?", "¿"}).SetBlock(true).
Handle(func(ctx *zero.Ctx) {
switch rand.Intn(5) {
case 0:
ctx.SendChain(randText("?", "", "嗯?", "(。´・ω・)ん?", "ん?"))
case 1, 2:
ctx.SendChain(randImage("WH.jpg"))
ctx.SendChain(dgtr.randImage("WH.jpg", "WH1.jpg", "WH2.jpg", "WH3.jpg"))
}
})
engine.OnKeyword("答应我", isAtriSleeping, zero.OnlyToMe).SetBlock(true).
engine.OnKeyword("离谱").SetBlock(true).
Handle(func(ctx *zero.Ctx) {
switch rand.Intn(5) {
case 0:
ctx.SendChain(randText("?", "", "嗯?", "(。´・ω・)ん?", "ん?"))
case 1, 2:
ctx.SendChain(dgtr.randImage("WH.jpg"))
}
})
engine.OnKeyword("答应我", zero.OnlyToMe).SetBlock(true).
Handle(func(ctx *zero.Ctx) {
process.SleepAbout1sTo2s()
ctx.SendChain(randText("我无法回应你的请求"))
})
}
func randText(text ...string) message.MessageSegment {
return message.Text(text[rand.Intn(len(text))])
}
func randImage(file ...string) message.MessageSegment {
return message.Image(res + file[rand.Intn(len(file))])
}
func randRecord(file ...string) message.MessageSegment {
return message.Record(res + file[rand.Intn(len(file))])
}
// isAtriSleeping 凌晨0点到6点ATRI 在睡觉,不回应任何请求
func isAtriSleeping(ctx *zero.Ctx) bool {
if now := time.Now().Hour(); now >= 1 && now < 6 {
return false
}
return true
}

View File

@@ -0,0 +1,29 @@
// Package autowithdraw 触发者撤回时也自动撤回
package autowithdraw
import (
"github.com/FloatTech/floatbox/process"
ctrl "github.com/FloatTech/zbpctrl"
"github.com/FloatTech/zbputils/control"
zero "github.com/wdvxdr1123/ZeroBot"
"github.com/wdvxdr1123/ZeroBot/message"
)
func init() {
control.Register("autowithdraw", &ctrl.Options[*zero.Ctx]{
DisableOnDefault: false,
Brief: "触发者撤回时也自动撤回",
Help: "- 撤回一条消息\n",
}).OnNotice(func(ctx *zero.Ctx) bool {
return ctx.Event.NoticeType == "group_recall" || ctx.Event.NoticeType == "friend_recall"
}).SetBlock(false).Handle(func(ctx *zero.Ctx) {
id, ok := ctx.Event.MessageID.(int64)
if !ok {
return
}
for _, msg := range zero.GetTriggeredMessages(message.NewMessageIDFromInteger(id)) {
process.SleepAbout1sTo2s()
ctx.DeleteMessage(msg)
}
})
}

View File

@@ -2,12 +2,10 @@
package b14coder
import (
"unsafe"
"github.com/FloatTech/floatbox/crypto"
ctrl "github.com/FloatTech/zbpctrl"
"github.com/FloatTech/zbputils/control"
base14 "github.com/fumiama/go-base16384"
tea "github.com/fumiama/gofastTEA"
zero "github.com/wdvxdr1123/ZeroBot"
"github.com/wdvxdr1123/ZeroBot/message"
"github.com/wdvxdr1123/ZeroBot/utils/helper"
@@ -16,10 +14,10 @@ import (
func init() {
en := control.Register("base16384", &ctrl.Options[*zero.Ctx]{
DisableOnDefault: false,
Help: "base16384加解密\n" +
"- 加密xxx\n- 解密xxx\n- 用yyy加密xxx\n- 用yyy解密xxx",
Brief: "base16384加解密",
Help: "- 加密xxx\n- 解密xxx\n- 用yyy加密xxx\n- 用yyy解密xxx",
})
en.OnRegex(`^加密\s*(.*)`).SetBlock(true).
en.OnRegex(`^加密\s*(.+)$`).SetBlock(true).
Handle(func(ctx *zero.Ctx) {
str := ctx.State["regex_matched"].([]string)[1]
es := base14.EncodeString(str)
@@ -29,7 +27,7 @@ func init() {
ctx.SendChain(message.Text("加密失败!"))
}
})
en.OnRegex(`^解密\s*([一-踀]*[㴁-㴆]?)$`).SetBlock(true).
en.OnRegex(`^解密\s*([一-踀]+[㴁-㴆]?)$`).SetBlock(true).
Handle(func(ctx *zero.Ctx) {
str := ctx.State["regex_matched"].([]string)[1]
es := base14.DecodeString(str)
@@ -39,10 +37,10 @@ func init() {
ctx.SendChain(message.Text("解密失败!"))
}
})
en.OnRegex(`^用(.*)加密\s*(.*)`).SetBlock(true).
en.OnRegex(`^用(.+)加密\s*(.+)$`).SetBlock(true).
Handle(func(ctx *zero.Ctx) {
key, str := ctx.State["regex_matched"].([]string)[1], ctx.State["regex_matched"].([]string)[2]
t := getea(key)
t := crypto.GetTEA(key)
es, err := base14.UTF16BE2UTF8(base14.Encode(t.Encrypt(helper.StringToBytes(str))))
if err == nil {
ctx.SendChain(message.Text(helper.BytesToString(es)))
@@ -50,10 +48,10 @@ func init() {
ctx.SendChain(message.Text("加密失败!"))
}
})
en.OnRegex(`^用(.*)解密\s*([一-踀]*[㴁-㴆]?)$`).SetBlock(true).
en.OnRegex(`^用(.+)解密\s*([一-踀]+[㴁-㴆]?)$`).SetBlock(true).
Handle(func(ctx *zero.Ctx) {
key, str := ctx.State["regex_matched"].([]string)[1], ctx.State["regex_matched"].([]string)[2]
t := getea(key)
t := crypto.GetTEA(key)
es, err := base14.UTF82UTF16BE(helper.StringToBytes(str))
if err == nil {
ctx.SendChain(message.Text(helper.BytesToString(t.Decrypt(base14.Decode(es)))))
@@ -62,15 +60,3 @@ func init() {
}
})
}
func getea(key string) tea.TEA {
kr := []rune(key)
if len(kr) > 4 {
kr = kr[:4]
} else {
for len(kr) < 4 {
kr = append(kr, rune(4-len(kr)))
}
}
return *(*tea.TEA)(*(*unsafe.Pointer)(unsafe.Pointer(&kr)))
}

View File

@@ -15,8 +15,8 @@ import (
func init() {
control.Register("baidu", &ctrl.Options[*zero.Ctx]{
DisableOnDefault: false,
Help: "baidu\n" +
"- 百度下[xxx]",
Brief: "不会百度吗",
Help: "- 百度下[xxx]",
}).OnPrefix("百度下").SetBlock(true).Limit(ctxext.LimitByGroup).
Handle(func(ctx *zero.Ctx) {
txt := ctx.State["args"].(string)

500
plugin/baiduaudit/audit.go Normal file
View File

@@ -0,0 +1,500 @@
// Package baiduaudit 百度内容审核
package baiduaudit
import (
"encoding/json"
"fmt"
"os"
"strconv"
"github.com/Baidu-AIP/golang-sdk/aip/censor"
"github.com/FloatTech/floatbox/binary"
"github.com/FloatTech/floatbox/file"
ctrl "github.com/FloatTech/zbpctrl"
"github.com/FloatTech/zbputils/control"
"github.com/FloatTech/zbputils/img/text"
zero "github.com/wdvxdr1123/ZeroBot"
"github.com/wdvxdr1123/ZeroBot/message"
)
// 服务网址:https://console.bce.baidu.com/ai/?_=1665977657185#/ai/antiporn/overview/index
// 返回参数说明https://cloud.baidu.com/doc/ANTIPORN/s/Nk3h6xbb2
type baiduRes struct {
LogID int `json:"log_id"` // 请求唯一id
Conclusion string `json:"conclusion"` // 审核结果,可取值:合规、不合规、疑似、审核失败
ConclusionType int `json:"conclusionType"` // 审核结果类型可取值1.合规2.不合规3.疑似4.审核失败
Data []auditData `json:"data"`
ErrorCode int `json:"error_code"` // 错误提示码,失败才返回,成功不返回
ErrorMsg string `json:"error_msg"` // 错误提示信息,失败才返回,成功不返回
}
type auditData struct {
Type int `json:"type"` // 审核主类型11百度官方违禁词库、12文本反作弊、13:自定义文本黑名单、14:自定义文本白名单
SubType int `json:"subType"` // 审核子类型0:含多种类型具体看官方链接1:违禁违规、2:文本色情、3:敏感信息、4:恶意推广、5:低俗辱骂 6:恶意推广-联系方式、7:恶意推广-软文推广
Conclusion string `json:"conclusion"` // 审核结果,可取值:合规、不合规、疑似、审核失败
ConclusionType int `json:"conclusionType"` // 审核结果类型可取值1.合规2.不合规3.疑似4.审核失败
Msg string `json:"msg"` // 不合规项描述信息
Hits []hit `json:"hits"`
} // 不合规/疑似/命中白名单项详细信息。响应成功并且conclusion为疑似或不合规或命中白名单时才返回响应失败或conclusion为合规且未命中白名单时不返回。
type hit struct {
DatasetName string `json:"datasetName"` // 违规项目所属数据集名称
Words []string `json:"words"` // 送检文本命中词库的关键词备注建议参考新字段“wordHitPositions”包含信息更丰富关键词以及对应的位置及标签信息
Probability float64 `json:"probability,omitempty"` // 不合规项置信度
} // 送检文本违规原因的详细信息
type keyConfig struct {
Key1 string `json:"key1"` // 百度云服务内容审核key存储
Key2 string `json:"key2"` // 百度云服务内容审核key存储
Groups map[int64]group `json:"groups"` // 群配置存储
}
type group struct {
Enable EnableMark // 是否启用内容审核
TextAudit EnableMark // 文本检测
ImageAudit EnableMark // 图像检测
DMRemind EnableMark // 撤回提示
MoreRemind EnableMark // 详细违规提示
DMBAN EnableMark // 撤回后禁言
BANTimeAddEnable EnableMark // 禁言累加
BANTime int64 // 标准禁言时间,禁用累加,但开启禁言的的情况下采用该值
MaxBANTimeAddRange int64 // 最大禁言时间累加范围,最高禁言时间
BANTimeAddTime int64 // 禁言累加时间该值是开启禁累加功能后再次触发时根据被禁次数X该值计算出的禁言时间
WhiteListType [8]bool // 类型白名单,处于白名单类型的违规,不会被触发 0:含多种类型具体看官方链接1:违禁违规、2:文本色情、3:敏感信息、4:恶意推广、5:低俗辱骂 6:恶意推广-联系方式、7:恶意推广-软文推广
AuditHistory map[int64]auditHistory // 被封禁用户列表
}
// EnableMark 启用:●,禁用:○
type EnableMark bool
// String 打印启用状态
func (em EnableMark) String() string {
if em {
return "开启"
}
return "关闭"
}
type auditHistory struct {
Count int64 `json:"key2"` // 被禁次数
ResList []baiduRes `json:"reslist"` // 禁言原因
}
var bdcli *censor.ContentCensorClient // 百度云审核服务Client
var typetext = [8]string{
0: "默认违禁词库",
1: "违禁违规",
2: "文本色情",
3: "敏感信息",
4: "恶意推广",
5: "低俗辱骂",
6: "恶意推广-联系方式",
7: "恶意推广-软文推广",
} // 文本类型
var (
config keyConfig // 插件配置
configinit bool // 配置初始化
configpath string // 配置路径
)
func init() {
engine := control.Register("baiduaudit", &ctrl.Options[*zero.Ctx]{
DisableOnDefault: false,
Brief: "百度内容审核",
Help: "##该功能来自百度内容审核需购买相关服务并创建app##\n" +
"- 获取BDAKey\n" +
"- 配置BDAKey [API key] [Secret Key]\n" +
"- 开启/关闭内容审核\n" +
"- 开启/关闭撤回提示\n" +
"- 开启/关闭详细提示\n" +
"- 开启/关闭撤回禁言\n" +
"##禁言时间设置## 禁言时间计算方式为:禁言次数*每次禁言累加时间,当达到最大禁言时间时,再次触发按最大禁言时间计算\n" +
"- 开启/关闭禁言累加\n" +
"- 设置撤回禁言时间[分钟,默认:1]\n" +
"- 设置最大禁言时间[分钟,默认:60,最大43200]\n" +
"- 设置每次累加时间[分钟,默认:1]\n" +
"##检测类型设置## 类型编号列表:[1:违禁违规、2:文本色情、3:敏感信息、4:恶意推广、5:低俗辱骂 6:恶意推广-联系方式、7:恶意推广-软文推广]\n" +
"- 查看检测类型\n" +
"- 查看检测配置\n" +
"- 设置检测类型[类型编号]\n" +
"- 设置不检测类型[类型编号]\n" +
"- 开启/关闭文本检测\n" +
"- 开启/关闭图像检测\n" +
"##测试功能##\n" +
"- 测试文本检测[文本内容]\n" +
"- 测试图像检测[图片]\n",
PrivateDataFolder: "baiduaudit",
})
configpath = engine.DataFolder() + "config.json"
loadConfig()
if configinit {
bdcli = censor.NewClient(config.Key1, config.Key2)
}
engine.OnFullMatch("获取BDAKey", zero.SuperUserPermission).SetBlock(true).
Handle(func(ctx *zero.Ctx) {
ctx.SendChain(message.Text("接口key创建网址:\n" +
"https://console.bce.baidu.com/ai/?_=1665977657185#/ai/antiporn/overview/index\n" +
"免费8w次数领取地址:\n" +
"https://console.bce.baidu.com/ai/?_=1665977657185#/ai/antiporn/overview/resource/getFree"))
})
engine.OnRegex("^查看检测(类型|配置)$", zero.AdminPermission, clientCheck).SetBlock(true).
Handle(func(ctx *zero.Ctx) {
// 获取群配置
group := getGroup(ctx.Event.GroupID)
var msgs string
k1 := ctx.State["regex_matched"].([]string)[1]
if k1 == "类型" {
msgs += "本群检测类型:"
find := false
// 遍历群检测类型名单
for i, v := range group.WhiteListType {
if !v {
find = true
msgs += fmt.Sprint("\n", i, ".", typetext[i])
}
}
if !find {
msgs += "无"
}
} else {
// 生成配置文本
msgs = fmt.Sprintf("本群配置:\n"+
"内容审核:%s\n"+
"-文本:%s\n"+
"-图像:%s\n"+
"撤回提示:%s\n"+
"-详细提示:%s\n"+
"撤回禁言:%s\n"+
"-禁言累加:%s\n"+
"-撤回禁言时间:%v分钟\n"+
"-每次累加时间:%v分钟\n"+
"-最大禁言时间:%v分钟", group.Enable, group.TextAudit, group.ImageAudit, group.DMRemind, group.MoreRemind, group.DMBAN, group.BANTimeAddEnable, group.BANTime, group.BANTimeAddTime, group.MaxBANTimeAddRange)
}
b, err := text.RenderToBase64(msgs, text.FontFile, 300, 20)
if err != nil {
ctx.SendChain(message.Text("ERROR: ", err))
return
}
ctx.SendChain(message.Image("base64://" + binary.BytesToString(b)))
})
engine.OnRegex("^设置(不)?检测类型([01234567])$", zero.AdminPermission, clientCheck).SetBlock(true).
Handle(func(ctx *zero.Ctx) {
defer jsonSave(config, configpath)
k1 := ctx.State["regex_matched"].([]string)[1]
k2 := ctx.State["regex_matched"].([]string)[2]
group := getGroup(ctx.Event.GroupID)
inputType, _ := strconv.Atoi(k2)
if k1 == "不" {
group.WhiteListType[inputType] = true // 不检测:则进入类型白名单
} else {
group.WhiteListType[inputType] = false // 检测:则退出白名单
}
config.Groups[ctx.Event.GroupID] = group
ctx.SendChain(message.At(ctx.Event.UserID), message.Text(fmt.Sprintf("本群将%s检测%s类型内容", k1, typetext[inputType])))
})
engine.OnRegex("^设置(最大|每次|撤回)(累加|禁言)时间(\\d{1,5})$", zero.AdminPermission, clientCheck).SetBlock(true).
Handle(func(ctx *zero.Ctx) {
defer jsonSave(config, configpath)
k1 := ctx.State["regex_matched"].([]string)[1]
k3 := ctx.State["regex_matched"].([]string)[3]
group := getGroup(ctx.Event.GroupID)
time, _ := strconv.ParseInt(k1, 10, 64)
switch k1 {
case "最大":
group.MaxBANTimeAddRange = time
case "每次":
group.BANTimeAddTime = time
case "撤回":
group.BANTime = time
}
config.Groups[ctx.Event.GroupID] = group
ctx.SendChain(message.At(ctx.Event.UserID), message.Text(fmt.Sprintf("本群%s禁言累加时间已设置为%s", k3, k1)))
})
engine.OnRegex("^(开启|关闭)(内容审核|撤回提示|撤回禁言|禁言累加|详细提示|文本检测|图像检测)$", zero.AdminPermission, clientCheck).SetBlock(true).
Handle(func(ctx *zero.Ctx) {
defer jsonSave(config, configpath)
k1 := ctx.State["regex_matched"].([]string)[1]
k2 := ctx.State["regex_matched"].([]string)[2]
isEnable := EnableMark(false)
group := getGroup(ctx.Event.GroupID)
if k1 == "开启" {
isEnable = true
}
switch k2 {
case "内容审核":
group.Enable = isEnable
case "撤回提示":
group.DMRemind = isEnable
case "撤回禁言":
group.DMBAN = isEnable
case "禁言累加":
group.BANTimeAddEnable = isEnable
case "详细提示":
group.MoreRemind = isEnable
case "文本检测":
group.TextAudit = isEnable
case "图像检测":
group.ImageAudit = isEnable
}
config.Groups[ctx.Event.GroupID] = group
ctx.SendChain(message.At(ctx.Event.UserID), message.Text(fmt.Sprintf("本群%s已%s", k2, k1)))
})
engine.OnRegex(`^配置BDAKey\s(.*)\s(.*)$`, zero.SuperUserPermission).SetBlock(true).
Handle(func(ctx *zero.Ctx) {
k1 := ctx.State["regex_matched"].([]string)[1]
k2 := ctx.State["regex_matched"].([]string)[2]
bdcli = censor.NewClient(k1, k2)
config.Key1 = k1
config.Key2 = k2
if bdcli != nil {
jsonSave(config, configpath)
ctx.SendChain(message.Text("配置成功"))
}
})
engine.OnMessage().SetBlock(false).Handle(func(ctx *zero.Ctx) {
group, ok := config.Groups[ctx.Event.GroupID]
// 如果没该配置,或者审核功能未开启直接跳过
if !ok || !bool(group.Enable) {
return
}
for _, elem := range ctx.Event.Message {
switch elem.Type {
case "image":
if !group.ImageAudit {
return
}
res := bdcli.ImgCensorUrl(elem.Data["url"], nil)
bdres, err := jsonToBaiduRes(res)
if err != nil {
ctx.SendChain(message.Text("Error:", bdres.ErrorMsg, "(", bdres.ErrorCode, ")"))
return
}
banCheck(ctx, bdres)
case "text":
if !group.TextAudit {
return
}
res := bdcli.TextCensor(elem.Data["text"])
bdres, err := jsonToBaiduRes(res)
if err != nil {
ctx.SendChain(message.Text("Error:", bdres.ErrorMsg, "(", bdres.ErrorCode, ")"))
return
}
banCheck(ctx, bdres)
}
}
})
engine.OnPrefix("文本检测", clientCheck).SetBlock(false).
Handle(func(ctx *zero.Ctx) {
if bdcli == nil {
ctx.SendChain(message.Text("Key未配置"))
return
}
args := ctx.ExtractPlainText()
res := bdcli.TextCensor(args)
bdres, err := jsonToBaiduRes(res)
if err != nil {
ctx.SendChain(message.Text("Error:", bdres.ErrorMsg, "(", bdres.ErrorCode, ")"))
return
}
group := getGroup(ctx.Event.GroupID)
ctx.SendChain(buildResp(bdres, group)...)
})
engine.OnPrefix("^图像检测", clientCheck).SetBlock(false).
Handle(func(ctx *zero.Ctx) {
var urls []string
for _, elem := range ctx.Event.Message {
if elem.Type == "image" {
if elem.Data["url"] != "" {
urls = append(urls, elem.Data["url"])
}
}
}
if len(urls) == 0 {
return
}
res := bdcli.ImgCensorUrl(urls[0], nil)
bdres, err := jsonToBaiduRes(res)
if err != nil {
ctx.SendChain(message.Text("Error:", bdres.ErrorMsg, "(", bdres.ErrorCode, ")"))
return
}
group := getGroup(ctx.Event.GroupID)
ctx.SendChain(buildResp(bdres, group)...)
})
}
// 禁言检测
func banCheck(ctx *zero.Ctx, bdres baiduRes) {
// 如果返回类型为2不合规0为合规3为疑似
if bdres.ConclusionType == 2 {
// 创建消息ID
mid := message.NewMessageIDFromInteger(ctx.Event.MessageID.(int64))
// 获取群配置
group := getGroup(ctx.Event.GroupID)
// 检测群配置里的不检测类型白名单,忽略掉不检测的违规类型
for i, b := range group.WhiteListType {
if i == bdres.Data[0].SubType && b {
return
}
}
// 生成回复文本
res := buildResp(bdres, group)
// 撤回消息
ctx.DeleteMessage(mid)
// 查看是否启用撤回后禁言
if group.DMBAN {
// 从历史违规记录中获取指定用户
user := group.getUser(ctx.Event.UserID)
// 用户违规次数自增
user.Count++
// 用户违规原因记录
user.ResList = append(user.ResList, bdres)
// 覆写该用户到群违规记录中
group.AuditHistory[ctx.Event.UserID] = user
// 覆写该群信息
config.Groups[ctx.Event.GroupID] = group
// 保存到json
jsonSave(config, configpath)
var bantime int64
// 查看是否开启禁言累加功能,并计算禁言时间
if group.BANTimeAddEnable {
bantime = user.Count * group.BANTimeAddTime * 60
} else {
bantime = group.BANTime * 60
}
// 执行禁言
ctx.SetGroupBan(ctx.Event.GroupID, ctx.Event.UserID, bantime)
}
// 查看是否开启撤回提示
if group.DMRemind {
res = append(res, message.At(ctx.Event.Sender.ID))
ctx.SendChain(res...)
}
}
}
// 获取群配置
func getGroup(groupID int64) group {
g, ok := config.Groups[groupID]
if ok {
return g
}
if config.Groups == nil {
config.Groups = make(map[int64]group)
}
g = group{
TextAudit: true,
ImageAudit: true,
BANTime: 1,
MaxBANTimeAddRange: 60,
BANTimeAddTime: 1,
WhiteListType: [8]bool{},
AuditHistory: map[int64]auditHistory{},
}
config.Groups[groupID] = g
return g
}
// 从群历史违规记录中获取用户
func (group *group) getUser(userID int64) auditHistory {
audit, ok := group.AuditHistory[userID]
if ok {
return audit
}
// 如果没有用户,则创建一个并返回
if group.AuditHistory == nil {
group.AuditHistory = make(map[int64]auditHistory)
}
audit = auditHistory{0, []baiduRes{}}
group.AuditHistory[userID] = audit
return audit
}
// 客户端是否初始化检测
func clientCheck(ctx *zero.Ctx) bool {
if bdcli == nil {
ctx.SendChain(message.Text("Key未配置"))
return false
}
return true
}
// 加载JSON配置文件
func loadConfig() {
if file.IsExist(configpath) {
data, err := os.OpenFile(configpath, os.O_RDONLY, 0755)
if err != nil {
panic(err)
}
err = json.NewDecoder(data).Decode(&config)
if err != nil {
panic(err)
}
configinit = true
} else {
config = keyConfig{}
configinit = false
}
}
// 保存配置文件
func jsonSave(v keyConfig, path string) {
jsf, _ := os.OpenFile(path, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0666)
defer func(file *os.File) {
err := file.Close()
if err != nil {
fmt.Println(err)
}
}(jsf) // 结束时关闭句柄,释放资源
err := json.NewEncoder(jsf).Encode(v)
if err != nil {
fmt.Println(err)
}
}
// JSON反序列化
func jsonToBaiduRes(resjson string) (baiduRes, error) {
var bdres baiduRes
err := json.Unmarshal(binary.StringToBytes(resjson), &bdres)
return bdres, err
}
// 生成回复文本
func buildResp(bdres baiduRes, group group) []message.MessageSegment {
// 建立消息段
msgs := make([]message.MessageSegment, 0, 8)
// 生成简略审核结果回复
msgs = append(msgs, message.Text(bdres.Conclusion, "\n"))
// 查看是否开启详细审核内容提示,并确定审核内容值为疑似,或者不合规
if !group.MoreRemind {
return msgs
}
// 遍历返回的不合规数据,生成详细违规内容
for i, datum := range bdres.Data {
msgs = append(msgs, message.Text("[", i, "]:", datum.Msg, "\n"))
// 检查命中词条是否大于0
if len(datum.Hits) == 0 {
return msgs
}
// 遍历打印命中的违规词条
for _, hit := range datum.Hits {
if len(datum.Hits) == 0 {
return msgs
}
msgs = append(msgs, message.Text("("))
for i4, i3 := range hit.Words {
// 检查是否是最后一个要打印的词条,如果是则不加上逗号
if i4 != len(hit.Words)-1 {
msgs = append(msgs, message.Text(i3, ","))
} else {
msgs = append(msgs, message.Text(i3))
}
}
msgs = append(msgs, message.Text(")"))
}
}
return msgs
}

62
plugin/base64gua/main.go Normal file
View File

@@ -0,0 +1,62 @@
// Package base64gua base64卦 与 tea 加解密
package base64gua
import (
"github.com/FloatTech/floatbox/crypto"
ctrl "github.com/FloatTech/zbpctrl"
"github.com/FloatTech/zbputils/control"
"github.com/fumiama/unibase2n"
zero "github.com/wdvxdr1123/ZeroBot"
"github.com/wdvxdr1123/ZeroBot/message"
"github.com/wdvxdr1123/ZeroBot/utils/helper"
)
func init() {
en := control.Register("base64gua", &ctrl.Options[*zero.Ctx]{
DisableOnDefault: false,
Brief: "六十四卦加解密",
Help: "- 六十四卦加密xxx\n- 六十四卦解密xxx\n- 六十四卦用yyy加密xxx\n- 六十四卦用yyy解密xxx",
})
en.OnRegex(`^六十四卦加密\s*(.+)$`).SetBlock(true).
Handle(func(ctx *zero.Ctx) {
str := ctx.State["regex_matched"].([]string)[1]
es := unibase2n.Base64Gua.EncodeString(str)
if es != "" {
ctx.SendChain(message.Text(es))
} else {
ctx.SendChain(message.Text("加密失败!"))
}
})
en.OnRegex(`^六十四卦解密\s*([䷀-䷿]+[☰☱]?)$`).SetBlock(true).
Handle(func(ctx *zero.Ctx) {
str := ctx.State["regex_matched"].([]string)[1]
es := unibase2n.Base64Gua.DecodeString(str)
if es != "" {
ctx.SendChain(message.Text(es))
} else {
ctx.SendChain(message.Text("解密失败!"))
}
})
en.OnRegex(`^六十四卦用(.+)加密\s*(.+)$`).SetBlock(true).
Handle(func(ctx *zero.Ctx) {
key, str := ctx.State["regex_matched"].([]string)[1], ctx.State["regex_matched"].([]string)[2]
t := crypto.GetTEA(key)
es, err := unibase2n.UTF16BE2UTF8(unibase2n.Base64Gua.Encode(t.Encrypt(helper.StringToBytes(str))))
if err == nil {
ctx.SendChain(message.Text(helper.BytesToString(es)))
} else {
ctx.SendChain(message.Text("加密失败!"))
}
})
en.OnRegex(`^六十四卦用(.+)解密\s*([䷀-䷿]+[☰☱]?)$`).SetBlock(true).
Handle(func(ctx *zero.Ctx) {
key, str := ctx.State["regex_matched"].([]string)[1], ctx.State["regex_matched"].([]string)[2]
t := crypto.GetTEA(key)
es, err := unibase2n.UTF82UTF16BE(helper.StringToBytes(str))
if err == nil {
ctx.SendChain(message.Text(helper.BytesToString(t.Decrypt(unibase2n.Base64Gua.Decode(es)))))
} else {
ctx.SendChain(message.Text("解密失败!"))
}
})
}

View File

@@ -0,0 +1,62 @@
// Package baseamasiro base天城文 与 tea 加解密
package baseamasiro
import (
"github.com/FloatTech/floatbox/crypto"
ctrl "github.com/FloatTech/zbpctrl"
"github.com/FloatTech/zbputils/control"
"github.com/fumiama/unibase2n"
zero "github.com/wdvxdr1123/ZeroBot"
"github.com/wdvxdr1123/ZeroBot/message"
"github.com/wdvxdr1123/ZeroBot/utils/helper"
)
func init() {
en := control.Register("baseamasiro", &ctrl.Options[*zero.Ctx]{
DisableOnDefault: false,
Brief: "天城文加解密",
Help: "- 天城文加密xxx\n- 天城文解密xxx\n- 天城文用yyy加密xxx\n- 天城文用yyy解密xxx",
})
en.OnRegex(`^天城文加密\s*(.+)$`).SetBlock(true).
Handle(func(ctx *zero.Ctx) {
str := ctx.State["regex_matched"].([]string)[1]
es := unibase2n.BaseDevanagari.EncodeString(str)
if es != "" {
ctx.SendChain(message.Text(es))
} else {
ctx.SendChain(message.Text("加密失败!"))
}
})
en.OnRegex(`^天城文解密\s*([ऀ-ॿ]+[-৫]?)$`).SetBlock(true).
Handle(func(ctx *zero.Ctx) {
str := ctx.State["regex_matched"].([]string)[1]
es := unibase2n.BaseDevanagari.DecodeString(str)
if es != "" {
ctx.SendChain(message.Text(es))
} else {
ctx.SendChain(message.Text("解密失败!"))
}
})
en.OnRegex(`^天城文用(.+)加密\s*(.+)$`).SetBlock(true).
Handle(func(ctx *zero.Ctx) {
key, str := ctx.State["regex_matched"].([]string)[1], ctx.State["regex_matched"].([]string)[2]
t := crypto.GetTEA(key)
es, err := unibase2n.UTF16BE2UTF8(unibase2n.BaseDevanagari.Encode(t.Encrypt(helper.StringToBytes(str))))
if err == nil {
ctx.SendChain(message.Text(helper.BytesToString(es)))
} else {
ctx.SendChain(message.Text("加密失败!"))
}
})
en.OnRegex(`^天城文用(.+)解密\s*([ऀ-ॿ]+[-৫]?)$`).SetBlock(true).
Handle(func(ctx *zero.Ctx) {
key, str := ctx.State["regex_matched"].([]string)[1], ctx.State["regex_matched"].([]string)[2]
t := crypto.GetTEA(key)
es, err := unibase2n.UTF82UTF16BE(helper.StringToBytes(str))
if err == nil {
ctx.SendChain(message.Text(helper.BytesToString(t.Decrypt(unibase2n.BaseDevanagari.Decode(es)))))
} else {
ctx.SendChain(message.Text("解密失败!"))
}
})
}

View File

@@ -1,179 +0,0 @@
package bilibili
import (
"encoding/json"
"errors"
"io"
"net/http"
"github.com/FloatTech/zbputils/binary"
"github.com/FloatTech/zbputils/web"
"github.com/tidwall/gjson"
)
var (
errNeedCookie = errors.New("该api需要设置b站cookie请发送命令设置cookie例如\"设置b站cookie SESSDATA=82da790d,1663822823,06ecf*31\"")
)
type searchResult struct {
Mid int64 `json:"mid"`
Uname string `json:"uname"`
Gender int64 `json:"gender"`
Usign string `json:"usign"`
Level int64 `json:"level"`
}
// 搜索api通过把触发指令传入的昵称找出uid返回
func search(keyword string) (r []searchResult, err error) {
searchURL := "http://api.bilibili.com/x/web-interface/search/type?search_type=bili_user&keyword=" + keyword
data, err := web.GetData(searchURL)
if err != nil {
return
}
j := gjson.ParseBytes(data)
if j.Get("data.numResults").Int() == 0 {
err = errors.New("查无此人")
return
}
err = json.Unmarshal(binary.StringToBytes(j.Get("data.result").Raw), &r)
if err != nil {
return
}
return
}
type follower struct {
Mid int `json:"mid"`
Uname string `json:"uname"`
Video int `json:"video"`
Roomid int `json:"roomid"`
Rise int `json:"rise"`
Follower int `json:"follower"`
GuardNum int `json:"guardNum"`
AreaRank int `json:"areaRank"`
}
// 请求api
func fansapi(uid string) (result follower, err error) {
fanURL := "https://api.vtbs.moe/v1/detail/" + uid
data, err := web.GetData(fanURL)
if err != nil {
return
}
if err = json.Unmarshal(data, &result); err != nil {
return
}
return
}
func followings(uid string) (s string, err error) {
followingURL := "https://api.bilibili.com/x/relation/same/followings?vmid=" + uid
method := "GET"
client := &http.Client{}
req, err := http.NewRequest(method, followingURL, nil)
if err != nil {
return
}
c := vdb.getBilibiliCookie()
req.Header.Add("cookie", c.Value)
res, err := client.Do(req)
if err != nil {
return
}
defer res.Body.Close()
body, err := io.ReadAll(res.Body)
if err != nil {
return
}
j := gjson.ParseBytes(body)
s = j.Get("data.list.#.uname").Raw
if j.Get("code").Int() == -101 {
err = errNeedCookie
return
}
if j.Get("code").Int() != 0 {
err = errors.New(j.Get("message").String())
return
}
return
}
type userinfo struct {
Name string `json:"name"`
Mid string `json:"mid"`
Face string `json:"face"`
Fans int64 `json:"fans"`
Regtime int64 `json:"regtime"`
Attentions []int64 `json:"attentions"`
}
type medalInfo struct {
Mid int64 `json:"target_id"`
MedalName string `json:"medal_name"`
Level int64 `json:"level"`
MedalColorStart int64 `json:"medal_color_start"`
MedalColorEnd int64 `json:"medal_color_end"`
MedalColorBorder int64 `json:"medal_color_border"`
}
type medal struct {
Uname string `json:"target_name"`
medalInfo `json:"medal_info"`
}
type medalSlice []medal
func (m medalSlice) Len() int {
return len(m)
}
func (m medalSlice) Swap(i, j int) {
m[i], m[j] = m[j], m[i]
}
func (m medalSlice) Less(i, j int) bool {
return m[i].Level > m[j].Level
}
// 获取详情
func card(uid string) (result userinfo, err error) {
cardURL := "https://account.bilibili.com/api/member/getCardByMid?mid=" + uid
data, err := web.GetData(cardURL)
if err != nil {
return
}
err = json.Unmarshal(binary.StringToBytes(gjson.ParseBytes(data).Get("card").Raw), &result)
if err != nil {
return
}
return
}
// 获得牌子
func medalwall(uid string) (result []medal, err error) {
medalwallURL := "https://api.live.bilibili.com/xlive/web-ucenter/user/MedalWall?target_id=" + uid
method := "GET"
client := &http.Client{}
req, err := http.NewRequest(method, medalwallURL, nil)
if err != nil {
return
}
c := vdb.getBilibiliCookie()
req.Header.Add("cookie", c.Value)
res, err := client.Do(req)
if err != nil {
return
}
defer res.Body.Close()
data, err := io.ReadAll(res.Body)
if err != nil {
return
}
j := gjson.ParseBytes(data)
if j.Get("code").Int() == -101 {
err = errNeedCookie
return
}
if j.Get("code").Int() != 0 {
err = errors.New(j.Get("message").String())
}
_ = json.Unmarshal(binary.StringToBytes(j.Get("data.list").Raw), &result)
return
}

632
plugin/bilibili/bilibili.go Normal file
View File

@@ -0,0 +1,632 @@
// Package bilibili 查询b站用户信息
package bilibili
import (
"crypto/tls"
"encoding/binary"
"encoding/json"
"fmt"
"image"
"image/color"
"net/http"
"os"
"path"
"regexp"
"sort"
"strconv"
"time"
bz "github.com/FloatTech/AnimeAPI/bilibili"
fcext "github.com/FloatTech/floatbox/ctxext"
"github.com/FloatTech/floatbox/file"
"github.com/FloatTech/floatbox/web"
"github.com/FloatTech/gg"
"github.com/FloatTech/imgfactory"
ctrl "github.com/FloatTech/zbpctrl"
"github.com/FloatTech/zbputils/control"
"github.com/FloatTech/zbputils/img/text"
log "github.com/sirupsen/logrus"
zero "github.com/wdvxdr1123/ZeroBot"
"github.com/wdvxdr1123/ZeroBot/message"
)
var (
re = regexp.MustCompile(`^\d+$`)
danmakuTypeMap = map[int64]string{
0: "普通消息",
1: "礼物",
2: "上舰",
3: "Superchat",
4: "进入直播间",
5: "标题变动",
6: "分区变动",
7: "直播中止",
8: "直播继续",
}
cfg = bz.NewCookieConfig("data/Bilibili/config.json")
)
// 查成分的
func init() {
engine := control.Register("bilibili", &ctrl.Options[*zero.Ctx]{
DisableOnDefault: false,
Brief: "b站查成分查弹幕",
Help: "- >vup info [xxx]\n" +
"- >user info [xxx]\n" +
"- 查成分 [xxx]\n" +
"- 查弹幕 [xxx]\n" +
"- 设置b站cookie b_ut=7;buvid3=0;i-wanna-go-back=-1;innersign=0;\n" +
"- 更新vup" +
"Tips: (412就是拦截的意思,建议私聊把cookie设全)\n",
PublicDataFolder: "Bilibili",
})
cachePath := engine.DataFolder() + "cache/"
_ = os.RemoveAll(cachePath)
_ = os.MkdirAll(cachePath, 0755)
var getdb = fcext.DoOnceOnSuccess(func(ctx *zero.Ctx) bool {
var err error
_, _ = engine.GetLazyData("bilibili.db", false)
vdb, err = initializeVup(engine.DataFolder() + "bilibili.db")
if err != nil {
ctx.SendChain(message.Text("ERROR: ", err))
return false
}
return true
})
engine.OnRegex(`^>user info\s?(.{1,25})$`, getPara).SetBlock(true).
Handle(func(ctx *zero.Ctx) {
id := ctx.State["uid"].(string)
card, err := bz.GetMemberCard(id)
if err != nil {
ctx.SendChain(message.Text("ERROR: ", err))
return
}
ctx.SendChain(message.Text(
"uid: ", card.Mid, "\n",
"name: ", card.Name, "\n",
"sex: ", card.Sex, "\n",
"sign: ", card.Sign, "\n",
"level: ", card.LevelInfo.CurrentLevel, "\n",
"birthday: ", card.Birthday,
))
})
engine.OnRegex(`^>vup info\s?(.{1,25})$`, getPara).SetBlock(true).
Handle(func(ctx *zero.Ctx) {
id := ctx.State["uid"].(string)
// 获取详情
fo, err := bz.GetVtbDetail(id)
if err != nil {
ctx.SendChain(message.Text("ERROR: ", err))
return
}
ctx.SendChain(message.Text(
"b站id: ", fo.Mid, "\n",
"名字: ", fo.Uname, "\n",
"当前粉丝数: ", fo.Follower, "\n",
"24h涨粉数: ", fo.Rise, "\n",
"视频投稿数: ", fo.Video, "\n",
"直播间id: ", fo.Roomid, "\n",
"舰队: ", fo.GuardNum, "\n",
"直播总排名: ", fo.AreaRank, "\n",
"数据来源: ", "https://vtbs.moe/detail/", fo.Mid, "\n",
"数据获取时间: ", time.Now().Format("2006-01-02 15:04:05"),
))
})
engine.OnRegex(`^查成分\s?(.{1,25})$`, getPara, getdb).SetBlock(true).
Handle(func(ctx *zero.Ctx) {
id := ctx.State["uid"].(string)
today := time.Now().Format("20060102")
drawedFile := cachePath + id + today + "vupLike.png"
if file.IsExist(drawedFile) {
ctx.SendChain(message.Image("file:///" + file.BOTPATH + "/" + drawedFile))
return
}
u, err := bz.GetMemberCard(id)
if err != nil {
ctx.SendChain(message.Text("ERROR: ", err))
return
}
vups, err := vdb.filterVup(u.Attentions)
if err != nil {
ctx.SendChain(message.Text("ERROR: ", err))
return
}
vupLen := len(vups)
medals, err := bz.GetMedalWall(cfg, id)
sort.Sort(bz.MedalSorter(medals))
if err != nil {
ctx.SendChain(message.Text("ERROR: ", err))
}
frontVups := make([]vup, 0)
medalMap := make(map[int64]bz.Medal)
for _, v := range medals {
up := vup{
Mid: v.Mid,
Uname: v.Uname,
}
frontVups = append(frontVups, up)
medalMap[v.Mid] = v
}
vups = append(vups, frontVups...)
copy(vups[len(frontVups):], vups)
copy(vups, frontVups)
for i := len(frontVups); i < len(vups); i++ {
if _, ok := medalMap[vups[i].Mid]; ok {
vups = append(vups[:i], vups[i+1:]...)
i--
}
}
facePath := cachePath + id + "vupFace" + path.Ext(u.Face)
backX := 500
backY := 500
var back image.Image
if path.Ext(u.Face) != ".webp" {
err = initFacePic(facePath, u.Face)
if err != nil {
ctx.SendChain(message.Text("ERROR: ", err))
return
}
back, err = gg.LoadImage(facePath)
if err != nil {
ctx.SendChain(message.Text("ERROR: ", err))
return
}
back = imgfactory.Size(back, backX, backY).Image()
}
if len(vups) > 50 {
ctx.SendChain(message.Text(u.Name + "关注的up主太多了, 只展示前50个up"))
vups = vups[:50]
}
canvas := gg.NewContext(1500, int(500*(1.1+float64(len(vups))/3)))
fontSize := 50.0
canvas.SetColor(color.White)
canvas.Clear()
if back != nil {
canvas.DrawImage(back, 0, 0)
}
canvas.SetColor(color.Black)
data, err := file.GetLazyData(text.BoldFontFile, control.Md5File, true)
if err != nil {
ctx.SendChain(message.Text("ERROR: ", err))
}
if err = canvas.ParseFontFace(data, fontSize); err != nil {
ctx.SendChain(message.Text("ERROR: ", err))
return
}
sl, _ := canvas.MeasureString("好")
length, h := canvas.MeasureString(u.Mid)
n, _ := canvas.MeasureString(u.Name)
canvas.DrawString(u.Name, 550, 160-h)
canvas.DrawRoundedRectangle(600+n-length*0.1, 160-h*2.5, length*1.2, h*2, fontSize*0.2)
canvas.SetRGB255(221, 221, 221)
canvas.Fill()
canvas.SetColor(color.Black)
canvas.DrawString(u.Mid, 600+n, 160-h)
canvas.DrawString(fmt.Sprintf("粉丝:%d", u.Fans), 550, 240-h)
canvas.DrawString(fmt.Sprintf("关注:%d", len(u.Attentions)), 1000, 240-h)
canvas.DrawString(fmt.Sprintf("管人痴成分:%.2f%%%d/%d", float64(vupLen)/float64(len(u.Attentions))*100, vupLen, len(u.Attentions)), 550, 320-h)
regtime := time.Unix(u.Regtime, 0).Format("2006-01-02 15:04:05")
canvas.DrawString("注册日期:"+regtime, 550, 400-h)
canvas.DrawString("查询日期:"+time.Now().Format("2006-01-02"), 550, 480-h)
for i, v := range vups {
if i%2 == 1 {
canvas.SetRGB255(245, 245, 245)
canvas.DrawRectangle(0, float64(backY)*1.1+float64(i)*float64(backY)/3, float64(backX*3), float64(backY)/3)
canvas.Fill()
}
canvas.SetColor(color.Black)
nl, _ := canvas.MeasureString(v.Uname)
canvas.DrawString(v.Uname, float64(backX)*0.1, float64(backY)*1.1+float64(i+1)*float64(backY)/3-2*h)
ml, _ := canvas.MeasureString(strconv.FormatInt(v.Mid, 10))
canvas.DrawRoundedRectangle(nl-0.1*ml+float64(backX)*0.2, float64(backY)*1.1+float64(i+1)*float64(backY)/3-h*3.5, ml*1.2, h*2, fontSize*0.2)
canvas.SetRGB255(221, 221, 221)
canvas.Fill()
canvas.SetColor(color.Black)
canvas.DrawString(strconv.FormatInt(v.Mid, 10), nl+float64(backX)*0.2, float64(backY)*1.1+float64(i+1)*float64(backY)/3-2*h)
if m, ok := medalMap[v.Mid]; ok {
mnl, _ := canvas.MeasureString(m.MedalName)
grad := gg.NewLinearGradient(nl+ml-sl/2+float64(backX)*0.4, float64(backY)*1.1+float64(i+1)*float64(backY)/3-3.5*h, nl+ml+mnl+sl/2+float64(backX)*0.4, float64(backY)*1.1+float64(i+1)*float64(backY)/3-1.5*h)
r, g, b := int2rbg(m.MedalColorStart)
grad.AddColorStop(0, color.RGBA{R: uint8(r), G: uint8(g), B: uint8(b), A: 255})
r, g, b = int2rbg(m.MedalColorEnd)
grad.AddColorStop(1, color.RGBA{R: uint8(r), G: uint8(g), B: uint8(b), A: 255})
canvas.SetFillStyle(grad)
canvas.SetLineWidth(4)
canvas.MoveTo(nl+ml-sl/2+float64(backX)*0.4, float64(backY)*1.1+float64(i+1)*float64(backY)/3-3.5*h)
canvas.LineTo(nl+ml+mnl+sl/2+float64(backX)*0.4, float64(backY)*1.1+float64(i+1)*float64(backY)/3-3.5*h)
canvas.LineTo(nl+ml+mnl+sl/2+float64(backX)*0.4, float64(backY)*1.1+float64(i+1)*float64(backY)/3-1.5*h)
canvas.LineTo(nl+ml-sl/2+float64(backX)*0.4, float64(backY)*1.1+float64(i+1)*float64(backY)/3-1.5*h)
canvas.ClosePath()
canvas.Fill()
canvas.SetColor(color.White)
canvas.DrawString(m.MedalName, nl+ml+float64(backX)*0.4, float64(backY)*1.1+float64(i+1)*float64(backY)/3-2*h)
r, g, b = int2rbg(m.MedalColorBorder)
canvas.SetRGB255(int(r), int(g), int(b))
canvas.DrawString(strconv.FormatInt(m.Level, 10), nl+ml+mnl+sl+float64(backX)*0.4, float64(backY)*1.1+float64(i+1)*float64(backY)/3-2*h)
mll, _ := canvas.MeasureString(strconv.FormatInt(m.Level, 10))
canvas.SetLineWidth(4)
canvas.MoveTo(nl+ml-sl/2+float64(backX)*0.4, float64(backY)*1.1+float64(i+1)*float64(backY)/3-3.5*h)
canvas.LineTo(nl+ml+mnl+mll+sl/2+float64(backX)*0.5, float64(backY)*1.1+float64(i+1)*float64(backY)/3-3.5*h)
canvas.LineTo(nl+ml+mnl+mll+sl/2+float64(backX)*0.5, float64(backY)*1.1+float64(i+1)*float64(backY)/3-1.5*h)
canvas.LineTo(nl+ml-sl/2+float64(backX)*0.4, float64(backY)*1.1+float64(i+1)*float64(backY)/3-1.5*h)
canvas.ClosePath()
canvas.Stroke()
}
}
f, err := os.Create(drawedFile)
if err != nil {
log.Errorln("[bilibili]", err)
data, err := imgfactory.ToBytes(canvas.Image())
if err != nil {
log.Errorln("[bilibili]", err)
return
}
ctx.SendChain(message.ImageBytes(data))
return
}
_, err = imgfactory.WriteTo(canvas.Image(), f)
_ = f.Close()
if err != nil {
ctx.SendChain(message.Text("ERROR: ", err))
return
}
ctx.SendChain(message.Image("file:///" + file.BOTPATH + "/" + drawedFile))
})
engine.OnRegex(`^查弹幕\s?(\S{1,25})\s?(\d*)$`, getPara).SetBlock(true).Handle(func(ctx *zero.Ctx) {
id := ctx.State["uid"].(string)
pagenum := ctx.State["regex_matched"].([]string)[2]
if pagenum == "" {
pagenum = "0"
}
u, err := bz.GetMemberCard(id)
if err != nil {
ctx.SendChain(message.Text("ERROR: ", err))
return
}
var danmaku bz.Danmakusuki
tr := &http.Transport{
DisableKeepAlives: true,
TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
}
client := &http.Client{Transport: tr}
data, err := web.RequestDataWith(client, fmt.Sprintf(bz.DanmakuAPI, id, pagenum), "GET", "", web.RandUA(), nil)
if err != nil {
ctx.SendChain(message.Text("ERROR: ", err))
return
}
err = json.Unmarshal(data, &danmaku)
if err != nil {
ctx.SendChain(message.Text("ERROR: ", err))
return
}
today := time.Now().Format("20060102150415")
drawedFile := cachePath + id + today + "vupLike.png"
facePath := cachePath + id + "vupFace" + path.Ext(u.Face)
backX := 500
backY := 500
var back image.Image
if path.Ext(u.Face) != ".webp" {
err = initFacePic(facePath, u.Face)
if err != nil {
ctx.SendChain(message.Text("ERROR: ", err))
return
}
back, err = gg.LoadImage(facePath)
if err != nil {
ctx.SendChain(message.Text("ERROR: ", err))
return
}
back = imgfactory.Size(back, backX, backY).Image()
}
canvas := gg.NewContext(100, 100)
fontSize := 50.0
data, err = file.GetLazyData(text.BoldFontFile, control.Md5File, true)
if err != nil {
ctx.SendChain(message.Text("ERROR: ", err))
}
if err = canvas.ParseFontFace(data, fontSize); err != nil {
ctx.SendChain(message.Text("ERROR: ", err))
return
}
dz, h := canvas.MeasureString("好")
danmuH := h * 2
faceH := float64(510)
totalDanmuku := 0
for i := 0; i < len(danmaku.Data.Data); i++ {
totalDanmuku += len(danmaku.Data.Data[i].Danmakus) + 1
}
cw := 10000
mcw := float64(2000)
ch := 550 + len(danmaku.Data.Data)*int(faceH) + totalDanmuku*int(danmuH)
canvas = gg.NewContext(cw, ch)
canvas.SetColor(color.White)
canvas.Clear()
canvas.SetColor(color.Black)
if err = canvas.ParseFontFace(data, fontSize); err != nil {
ctx.SendChain(message.Text("ERROR: ", err))
return
}
facestart := 100
fontH := h * 1.6
startWidth := float64(700)
startWidth2 := float64(20)
if back != nil {
canvas.DrawImage(back, facestart, 0)
}
length, _ := canvas.MeasureString(u.Mid)
n, _ := canvas.MeasureString(u.Name)
canvas.DrawString(u.Name, startWidth, 122.5)
canvas.DrawRoundedRectangle(900+n-length*0.1, 66, length*1.2, 75, fontSize*0.2)
canvas.SetRGB255(221, 221, 221)
canvas.Fill()
canvas.SetColor(color.Black)
canvas.DrawString(u.Mid, 900+n, 122.5)
canvas.DrawString(fmt.Sprintf("粉丝:%d 关注:%d", u.Fans, u.Attention), startWidth, 222.5)
canvas.DrawString(fmt.Sprintf("页码:[%d/%d]", danmaku.Data.PageNum, (danmaku.Data.Total-1)/5), startWidth, 322.5)
canvas.DrawString("网页链接: "+fmt.Sprintf(bz.DanmakuURL, u.Mid), startWidth, 422.5)
var channelStart float64
channelStart = float64(550)
for i := 0; i < len(danmaku.Data.Data); i++ {
item := danmaku.Data.Data[i]
facePath = cachePath + strconv.Itoa(int(item.Channel.UID)) + "vupFace" + path.Ext(item.Channel.FaceURL)
if path.Ext(item.Channel.FaceURL) != ".webp" {
err = initFacePic(facePath, item.Channel.FaceURL)
if err != nil {
ctx.SendChain(message.Text("ERROR: ", err))
return
}
back, err = gg.LoadImage(facePath)
if err != nil {
ctx.SendChain(message.Text("ERROR: ", err))
return
}
back = imgfactory.Size(back, backX, backY).Image()
}
if back != nil {
canvas.DrawImage(back, facestart, int(channelStart))
}
canvas.SetRGB255(24, 144, 255)
canvas.DrawString("标题: "+item.Live.Title, startWidth, channelStart+fontH)
canvas.DrawString("主播: "+item.Channel.Name, startWidth, channelStart+fontH*2)
canvas.SetColor(color.Black)
canvas.DrawString("开始时间: "+time.UnixMilli(item.Live.StartDate).Format("2006-01-02 15:04:05"), startWidth, channelStart+fontH*3)
if item.Live.IsFinish {
canvas.DrawString("结束时间: "+time.UnixMilli(item.Live.StopDate).Format("2006-01-02 15:04:05"), startWidth, channelStart+fontH*4)
canvas.DrawString("直播时长: "+strconv.FormatFloat(float64(item.Live.StopDate-item.Live.StartDate)/3600000.0, 'f', 1, 64)+"小时", startWidth, channelStart+fontH*5)
} else {
t := "结束时间:"
l, _ := canvas.MeasureString(t)
canvas.DrawString(t, startWidth, channelStart+fontH*4)
canvas.SetRGB255(0, 128, 0)
t = "正在直播"
canvas.DrawString(t, startWidth+l*1.1, channelStart+fontH*4)
canvas.SetColor(color.Black)
canvas.DrawString("直播时长: "+strconv.FormatFloat(float64(time.Now().UnixMilli()-item.Live.StartDate)/3600000.0, 'f', 1, 64)+"小时", startWidth, channelStart+fontH*5)
}
canvas.DrawString("弹幕数量: "+strconv.Itoa(int(item.Live.DanmakusCount)), startWidth, channelStart+fontH*6)
canvas.DrawString("观看次数: "+strconv.Itoa(int(item.Live.WatchCount)), startWidth, channelStart+fontH*7)
t := "收益:"
l, _ := canvas.MeasureString(t)
canvas.DrawString(t, startWidth, channelStart+fontH*8)
t = "¥" + strconv.Itoa(int(item.Live.TotalIncome))
canvas.SetRGB255(255, 0, 0)
canvas.DrawString(t, startWidth+l*1.1, channelStart+fontH*8)
canvas.SetColor(color.Black)
DanmakuStart := channelStart + faceH
for i := 0; i < len(item.Danmakus); i++ {
moveW := startWidth2
danmuNow := DanmakuStart + danmuH*float64(i+1)
danItem := item.Danmakus[i]
t := time.UnixMilli(danItem.SendDate).Format("15:04:05")
l, _ := canvas.MeasureString(t)
canvas.DrawString(t, moveW, danmuNow)
moveW += l + dz
t = danItem.Name
l, _ = canvas.MeasureString(t)
canvas.SetRGB255(24, 144, 255)
canvas.DrawString(t, moveW, danmuNow)
canvas.SetColor(color.Black)
moveW += l + dz
switch danItem.Type {
case 0:
t = danItem.Message
l, _ = canvas.MeasureString(t)
canvas.DrawString(t, moveW, danmuNow)
moveW += l + dz
case 1:
t = danmakuTypeMap[danItem.Type]
l, _ = canvas.MeasureString(t)
canvas.SetRGB255(255, 0, 0)
canvas.DrawString(t, moveW, danmuNow)
moveW += l + dz
t = danItem.Message
l, _ = canvas.MeasureString(t)
canvas.DrawString(t, moveW, danmuNow)
canvas.SetColor(color.Black)
moveW += l + dz
case 2, 3:
t = danmakuTypeMap[danItem.Type]
l, _ = canvas.MeasureString(t)
if danItem.Type == 3 {
canvas.SetRGB255(0, 85, 255)
} else {
canvas.SetRGB255(128, 0, 128)
}
canvas.DrawString(t, moveW, danmuNow)
moveW += l + dz
t = danItem.Message
l, _ = canvas.MeasureString(t)
canvas.DrawString(t, moveW, danmuNow)
moveW += l
t = "["
l, _ = canvas.MeasureString(t)
canvas.DrawString(t, moveW, danmuNow)
moveW += l
t = "¥" + strconv.FormatFloat(danItem.Price, 'f', 1, 64)
l, _ = canvas.MeasureString(t)
canvas.SetRGB255(255, 0, 0)
canvas.DrawString(t, moveW, danmuNow)
if danItem.Type == 3 {
canvas.SetRGB255(0, 85, 255)
} else {
canvas.SetRGB255(128, 0, 128)
}
moveW += l
t = "]"
l, _ = canvas.MeasureString(t)
canvas.DrawString(t, moveW, danmuNow)
canvas.SetColor(color.Black)
moveW += l + dz
case 4, 5, 6, 7, 8:
t = danmakuTypeMap[danItem.Type]
canvas.SetRGB255(0, 128, 0)
l, _ = canvas.MeasureString(t)
canvas.DrawString(t, moveW, danmuNow)
canvas.SetColor(color.Black)
moveW += l + dz
default:
canvas.SetRGB255(0, 128, 0)
l, _ = canvas.MeasureString("未知类型" + strconv.Itoa(int(danItem.Type)))
canvas.DrawString(t, moveW, danmuNow)
canvas.SetColor(color.Black)
moveW += l + dz
}
if moveW > mcw {
mcw = moveW
}
}
channelStart = DanmakuStart + float64(len(item.Danmakus)+1)*danmuH
}
im := canvas.Image().(*image.RGBA)
nim := im.SubImage(image.Rect(0, 0, int(mcw), ch))
f, err := os.Create(drawedFile)
if err != nil {
log.Errorln("[bilibili]", err)
data, err := imgfactory.ToBytes(nim)
if err != nil {
log.Errorln("[bilibili]", err)
return
}
ctx.SendChain(message.ImageBytes(data))
return
}
_, err = imgfactory.WriteTo(nim, f)
_ = f.Close()
if err != nil {
ctx.SendChain(message.Text("ERROR: ", err))
return
}
ctx.SendChain(message.Image("file:///" + file.BOTPATH + "/" + drawedFile))
})
engine.OnRegex(`^设置b站cookie?\s+(.*)$`, zero.SuperUserPermission).SetBlock(true).
Handle(func(ctx *zero.Ctx) {
cookie := ctx.State["regex_matched"].([]string)[1]
err := cfg.Set(cookie)
if err != nil {
ctx.SendChain(message.Text("ERROR: ", err))
return
}
ctx.SendChain(message.Text("成功设置b站cookie为" + cookie))
})
engine.OnFullMatch("更新vup", zero.SuperUserPermission).SetBlock(true).
Handle(func(ctx *zero.Ctx) {
ctx.SendChain(message.Text("少女祈祷中..."))
err := updateVup()
if err != nil {
ctx.SendChain(message.Text("ERROR: ", err))
return
}
ctx.SendChain(message.Text("vup已更新"))
})
}
func initFacePic(filename, faceURL string) error {
if file.IsNotExist(filename) {
data, err := web.GetData(faceURL)
if err != nil {
return err
}
err = os.WriteFile(filename, data, 0666)
if err != nil {
return err
}
}
return nil
}
func int2rbg(t int64) (int64, int64, int64) {
var buf [8]byte
binary.LittleEndian.PutUint64(buf[:], uint64(t))
b, g, r := int64(buf[0]), int64(buf[1]), int64(buf[2])
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

@@ -0,0 +1,107 @@
// Package bilibili bilibili卡片解析
package bilibili
import (
"regexp"
"time"
bz "github.com/FloatTech/AnimeAPI/bilibili"
ctrl "github.com/FloatTech/zbpctrl"
"github.com/FloatTech/zbputils/control"
"github.com/FloatTech/zbputils/ctxext"
zero "github.com/wdvxdr1123/ZeroBot"
"github.com/wdvxdr1123/ZeroBot/message"
)
var (
limit = ctxext.NewLimiterManager(time.Second*10, 1)
searchVideo = `bilibili.com\\?/video\\?/(?:av(\d+)|([bB][vV][0-9a-zA-Z]+))`
searchDynamic = `(t.bilibili.com|m.bilibili.com\\?/dynamic)\\?/(\d+)`
searchArticle = `bilibili.com\\?/read\\?/(?:cv|mobile\\?/)(\d+)`
searchLiveRoom = `live.bilibili.com\\?/(\d+)`
searchVideoRe = regexp.MustCompile(searchVideo)
searchDynamicRe = regexp.MustCompile(searchDynamic)
searchArticleRe = regexp.MustCompile(searchArticle)
searchLiveRoomRe = regexp.MustCompile(searchLiveRoom)
)
// 插件主体
func init() {
en := control.Register("bilibiliparse", &ctrl.Options[*zero.Ctx]{
DisableOnDefault: false,
Brief: "b站链接解析",
Help: "例:- t.bilibili.com/642277677329285174\n- bilibili.com/read/cv17134450\n- bilibili.com/video/BV13B4y1x7pS\n- live.bilibili.com/22603245 ",
})
en.OnRegex(`((b23|acg).tv|bili2233.cn)/[0-9a-zA-Z]+`).SetBlock(true).Limit(limit.LimitByGroup).
Handle(func(ctx *zero.Ctx) {
url := ctx.State["regex_matched"].([]string)[0]
realurl, err := bz.GetRealURL("https://" + url)
if err != nil {
ctx.SendChain(message.Text("ERROR: ", err))
return
}
switch {
case searchVideoRe.MatchString(realurl):
ctx.State["regex_matched"] = searchVideoRe.FindStringSubmatch(realurl)
handleVideo(ctx)
case searchDynamicRe.MatchString(realurl):
ctx.State["regex_matched"] = searchDynamicRe.FindStringSubmatch(realurl)
handleDynamic(ctx)
case searchArticleRe.MatchString(realurl):
ctx.State["regex_matched"] = searchArticleRe.FindStringSubmatch(realurl)
handleArticle(ctx)
case searchLiveRoomRe.MatchString(realurl):
ctx.State["regex_matched"] = searchLiveRoomRe.FindStringSubmatch(realurl)
handleLive(ctx)
}
})
en.OnRegex(searchVideo).SetBlock(true).Limit(limit.LimitByGroup).Handle(handleVideo)
en.OnRegex(searchDynamic).SetBlock(true).Limit(limit.LimitByGroup).Handle(handleDynamic)
en.OnRegex(searchArticle).SetBlock(true).Limit(limit.LimitByGroup).Handle(handleArticle)
en.OnRegex(searchLiveRoom).SetBlock(true).Limit(limit.LimitByGroup).Handle(handleLive)
}
func handleVideo(ctx *zero.Ctx) {
id := ctx.State["regex_matched"].([]string)[1]
if id == "" {
id = ctx.State["regex_matched"].([]string)[2]
}
card, err := bz.GetVideoInfo(id)
if err != nil {
ctx.SendChain(message.Text("ERROR: ", err))
return
}
msg, err := videoCard2msg(card)
if err != nil {
ctx.SendChain(message.Text("ERROR: ", err))
return
}
ctx.SendChain(msg...)
}
func handleDynamic(ctx *zero.Ctx) {
msg, err := dynamicDetail(ctx.State["regex_matched"].([]string)[2])
if err != nil {
ctx.SendChain(message.Text("ERROR: ", err))
return
}
ctx.SendChain(msg...)
}
func handleArticle(ctx *zero.Ctx) {
card, err := bz.GetArticleInfo(ctx.State["regex_matched"].([]string)[1])
if err != nil {
ctx.SendChain(message.Text("ERROR: ", err))
return
}
ctx.SendChain(articleCard2msg(card, ctx.State["regex_matched"].([]string)[1])...)
}
func handleLive(ctx *zero.Ctx) {
card, err := bz.GetLiveRoomInfo(ctx.State["regex_matched"].([]string)[1])
if err != nil {
ctx.SendChain(message.Text("ERROR: ", err))
return
}
ctx.SendChain(liveCard2msg(card)...)
}

View File

@@ -3,17 +3,12 @@ package bilibili
import (
"os"
"github.com/FloatTech/zbputils/binary"
"github.com/FloatTech/zbputils/web"
_ "github.com/fumiama/sqlite3" // use sql
"github.com/FloatTech/floatbox/binary"
"github.com/FloatTech/floatbox/web"
"github.com/jinzhu/gorm"
"github.com/tidwall/gjson"
)
const (
bilibiliCookie = "bilbili_cookie"
)
var (
vtbURLs = [...]string{"https://api.vtbs.moe/v1/short", "https://api.tokyo.vtbs.moe/v1/short", "https://vtbs.musedash.moe/v1/short"}
vdb *vupdb
@@ -32,17 +27,8 @@ func (vup) TableName() string {
return "vup"
}
type config struct {
Key string `gorm:"column:key;primary_key"`
Value string `gorm:"column:value"`
}
func (config) TableName() string {
return "config"
}
// initialize 初始化vtb数据库
func initialize(dbpath string) (*vupdb, error) {
// initializeVup 初始化vup数据库
func initializeVup(dbpath string) (*vupdb, error) {
if _, err := os.Stat(dbpath); err != nil || os.IsNotExist(err) {
// 生成文件
f, err := os.Create(dbpath)
@@ -55,7 +41,7 @@ func initialize(dbpath string) (*vupdb, error) {
if err != nil {
return nil, err
}
gdb.AutoMigrate(&vup{}).AutoMigrate(&config{})
gdb.AutoMigrate(&vup{})
return (*vupdb)(gdb), nil
}
@@ -102,29 +88,3 @@ func updateVup() error {
}
return nil
}
func (vdb *vupdb) setBilibiliCookie(cookie string) (err error) {
db := (*gorm.DB)(vdb)
c := config{
Key: bilibiliCookie,
Value: cookie,
}
if err = db.Model(&config{}).First(&c, "key = ? ", bilibiliCookie).Error; err != nil {
// error handling...
if gorm.IsRecordNotFoundError(err) {
err = db.Model(&config{}).Create(&c).Error
}
} else {
err = db.Model(&config{}).Where("key = ? ", bilibiliCookie).Update(
map[string]interface{}{
"value": cookie,
}).Error
}
return
}
func (vdb *vupdb) getBilibiliCookie() (c config) {
db := (*gorm.DB)(vdb)
db.Model(&config{}).First(&c, "key = ?", bilibiliCookie)
return
}

View File

@@ -0,0 +1,364 @@
// Package bilibili b站推送
package bilibili
import (
"bytes"
"encoding/json"
"fmt"
"strconv"
"time"
"github.com/pkg/errors"
"github.com/tidwall/gjson"
zero "github.com/wdvxdr1123/ZeroBot"
"github.com/wdvxdr1123/ZeroBot/message"
bz "github.com/FloatTech/AnimeAPI/bilibili"
"github.com/FloatTech/floatbox/binary"
"github.com/FloatTech/floatbox/web"
ctrl "github.com/FloatTech/zbpctrl"
"github.com/FloatTech/zbputils/control"
"github.com/FloatTech/zbputils/img/text"
)
const (
ua = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/92.0.4515.107 Safari/537.36"
referer = "https://www.bilibili.com/"
infoURL = "https://api.bilibili.com/x/space/acc/info?mid=%v"
)
// bdb bilibili推送数据库
var bdb *bilibilipushdb
var (
lastTime = map[int64]int64{}
liveStatus = map[int64]int{}
upMap = map[int64]string{}
)
func init() {
en := control.Register("bilibilipush", &ctrl.Options[*zero.Ctx]{
DisableOnDefault: false,
Brief: "b站推送",
Help: "- 添加b站订阅[uid|name]\n" +
"- 取消b站订阅[uid|name]\n" +
"- 取消b站动态订阅[uid|name]\n" +
"- 取消b站直播订阅[uid|name]\n" +
"- b站推送列表\n" +
"Tips: 需要配合job一起使用, 全局只需要设置一个, 无视响应状态推送, 下为例子\n" +
"记录在\"@every 5m\"触发的指令)\n" +
"拉取b站推送",
PrivateDataFolder: "bilibilipush",
})
// 加载bilibili推送数据库
dbpath := en.DataFolder()
dbfile := dbpath + "push.db"
bdb = initializePush(dbfile)
en.OnRegex(`^添加[B|b]站订阅\s?(.{1,25})$`, zero.UserOrGrpAdmin, getPara).SetBlock(true).Handle(func(ctx *zero.Ctx) {
buid, _ := strconv.ParseInt(ctx.State["uid"].(string), 10, 64)
name, err := getName(buid)
if err != nil {
ctx.SendChain(message.Text("ERROR: ", err))
return
}
gid := ctx.Event.GroupID
if gid == 0 {
gid = -ctx.Event.UserID
}
if err := subscribe(buid, gid); err != nil {
ctx.SendChain(message.Text("ERROR: ", err))
return
}
ctx.SendChain(message.Text("已添加" + name + "的订阅"))
})
en.OnRegex(`^取消[B|b]站订阅\s?(.{1,25})$`, zero.UserOrGrpAdmin, getPara).SetBlock(true).Handle(func(ctx *zero.Ctx) {
buid, _ := strconv.ParseInt(ctx.State["uid"].(string), 10, 64)
name, err := getName(buid)
if err != nil {
ctx.SendChain(message.Text("ERROR: ", err))
return
}
gid := ctx.Event.GroupID
if gid == 0 {
gid = -ctx.Event.UserID
}
if err := unsubscribe(buid, gid); err != nil {
ctx.SendChain(message.Text("ERROR: ", err))
return
}
ctx.SendChain(message.Text("已取消" + name + "的订阅"))
})
en.OnRegex(`^取消[B|b]站动态订阅\s?(.{1,25})$`, zero.UserOrGrpAdmin, getPara).SetBlock(true).Handle(func(ctx *zero.Ctx) {
buid, _ := strconv.ParseInt(ctx.State["uid"].(string), 10, 64)
name, err := getName(buid)
if err != nil {
ctx.SendChain(message.Text("ERROR: ", err))
return
}
gid := ctx.Event.GroupID
if gid == 0 {
gid = -ctx.Event.UserID
}
if err := unsubscribeDynamic(buid, gid); err != nil {
ctx.SendChain(message.Text("ERROR: ", err))
return
}
ctx.SendChain(message.Text("已取消" + name + "的动态订阅"))
})
en.OnRegex(`^取消[B|b]站直播订阅\s?(.{1,25})$`, zero.UserOrGrpAdmin, getPara).SetBlock(true).Handle(func(ctx *zero.Ctx) {
buid, _ := strconv.ParseInt(ctx.State["uid"].(string), 10, 64)
gid := ctx.Event.GroupID
if gid == 0 {
gid = -ctx.Event.UserID
}
name, err := getName(buid)
if err != nil {
ctx.SendChain(message.Text("ERROR: ", err))
return
}
if err := unsubscribeLive(buid, gid); err != nil {
ctx.SendChain(message.Text("ERROR: ", err))
return
}
ctx.SendChain(message.Text("已取消" + name + "的直播订阅"))
})
en.OnRegex(`^[B|b]站推送列表$`, zero.UserOrGrpAdmin).SetBlock(true).Handle(func(ctx *zero.Ctx) {
gid := ctx.Event.GroupID
if gid == 0 {
gid = -ctx.Event.UserID
}
bpl := bdb.getAllPushByGroup(gid)
msg := "--------B站推送列表--------"
for _, v := range bpl {
if _, ok := upMap[v.BilibiliUID]; !ok {
bdb.updateAllUp()
}
msg += fmt.Sprintf("\nuid:%-12d 动态:", v.BilibiliUID)
if v.DynamicDisable == 0 {
msg += "●"
} else {
msg += "○"
}
msg += " 直播:"
if v.LiveDisable == 0 {
msg += "●"
} else {
msg += "○"
}
msg += " up主" + upMap[v.BilibiliUID]
}
data, err := text.RenderToBase64(msg, text.FontFile, 600, 20)
if err != nil {
ctx.SendChain(message.Text("ERROR: ", err))
return
}
if id := ctx.SendChain(message.Image("base64://" + binary.BytesToString(data))); id.ID() == 0 {
ctx.SendChain(message.Text("ERROR: 可能被风控了"))
}
})
en.OnRegex(`拉取[B|b]站推送$`).SetBlock(true).Handle(func(ctx *zero.Ctx) {
err := sendDynamic(ctx)
if err != nil {
ctx.SendPrivateMessage(ctx.Event.UserID, message.Text("Error: bilibilipush,", err))
}
err = sendLive(ctx)
if err != nil {
ctx.SendPrivateMessage(ctx.Event.UserID, message.Text("Error: bilibilipush,", err))
}
})
}
// 取得uid的名字
func getName(buid int64) (name string, err error) {
var ok bool
if name, ok = upMap[buid]; !ok {
var data []byte
data, err = web.RequestDataWith(web.NewDefaultClient(), fmt.Sprintf(infoURL, buid), "GET", referer, ua, nil)
if err != nil {
return
}
status := int(gjson.Get(binary.BytesToString(data), "code").Int())
if status != 0 {
err = errors.New(gjson.Get(binary.BytesToString(data), "message").String())
return
}
name = gjson.Get(binary.BytesToString(data), "data.name").String()
bdb.insertBilibiliUp(buid, name)
upMap[buid] = name
}
return
}
// subscribe 订阅
func subscribe(buid, groupid int64) (err error) {
bpMap := map[string]any{
"bilibili_uid": buid,
"group_id": groupid,
"live_disable": 0,
"dynamic_disable": 0,
}
return bdb.insertOrUpdateLiveAndDynamic(bpMap)
}
// unsubscribe 取消订阅
func unsubscribe(buid, groupid int64) (err error) {
bpMap := map[string]any{
"bilibili_uid": buid,
"group_id": groupid,
"live_disable": 1,
"dynamic_disable": 1,
}
return bdb.insertOrUpdateLiveAndDynamic(bpMap)
}
func unsubscribeDynamic(buid, groupid int64) (err error) {
bpMap := map[string]any{
"bilibili_uid": buid,
"group_id": groupid,
"dynamic_disable": 1,
}
return bdb.insertOrUpdateLiveAndDynamic(bpMap)
}
func unsubscribeLive(buid, groupid int64) (err error) {
bpMap := map[string]any{
"bilibili_uid": buid,
"group_id": groupid,
"live_disable": 1,
}
return bdb.insertOrUpdateLiveAndDynamic(bpMap)
}
func getUserDynamicCard(buid int64) (cardList []gjson.Result, err error) {
data, err := web.RequestDataWith(web.NewDefaultClient(), fmt.Sprintf(bz.SpaceHistoryURL, buid, 0), "GET", referer, ua, nil)
if err != nil {
return
}
cardList = gjson.Get(binary.BytesToString(data), "data.cards").Array()
return
}
func getLiveList(uids ...int64) (string, error) {
m := make(map[string]any)
m["uids"] = uids
b, err := json.Marshal(m)
if err != nil {
return "", err
}
data, err := web.PostData(bz.LiveListURL, "application/json", bytes.NewReader(b))
if err != nil {
return "", err
}
return binary.BytesToString(data), nil
}
func sendDynamic(ctx *zero.Ctx) error {
uids := bdb.getAllBuidByDynamic()
for _, buid := range uids {
time.Sleep(2 * time.Second)
cardList, err := getUserDynamicCard(buid)
if err != nil {
return err
}
if len(cardList) == 0 {
return errors.Errorf("%v的历史动态数为0", buid)
}
t, ok := lastTime[buid]
// 第一次先记录时间,啥也不做
if !ok {
lastTime[buid] = cardList[0].Get("desc.timestamp").Int()
return nil
}
for i := len(cardList) - 1; i >= 0; i-- {
ct := cardList[i].Get("desc.timestamp").Int()
if ct > t && ct > time.Now().Unix()-600 {
lastTime[buid] = ct
m, ok := control.Lookup("bilibilipush")
if ok {
groupList := bdb.getAllGroupByBuidAndDynamic(buid)
dc, err := bz.LoadDynamicDetail(cardList[i].Raw)
if err != nil {
err = errors.Errorf("动态%v的解析有问题,%v", cardList[i].Get("desc.dynamic_id_str"), err)
return err
}
msg, err := dynamicCard2msg(&dc)
if err != nil {
err = errors.Errorf("动态%v的解析有问题,%v", cardList[i].Get("desc.dynamic_id_str"), err)
return err
}
for _, gid := range groupList {
if m.IsEnabledIn(gid) {
time.Sleep(time.Millisecond * 100)
switch {
case gid > 0:
ctx.SendGroupMessage(gid, msg)
case gid < 0:
ctx.SendPrivateMessage(-gid, msg)
}
}
}
}
}
}
}
return nil
}
func sendLive(ctx *zero.Ctx) error {
uids := bdb.getAllBuidByLive()
ll, err := getLiveList(uids...)
if err != nil {
return err
}
gjson.Get(ll, "data").ForEach(func(key, value gjson.Result) bool {
newStatus := int(value.Get("live_status").Int())
if newStatus == 2 {
newStatus = 0
}
if _, ok := liveStatus[key.Int()]; !ok {
liveStatus[key.Int()] = newStatus
return true
}
oldStatus := liveStatus[key.Int()]
if newStatus != oldStatus && newStatus == 1 {
liveStatus[key.Int()] = newStatus
m, ok := control.Lookup("bilibilipush")
if ok {
groupList := bdb.getAllGroupByBuidAndLive(key.Int())
roomID := value.Get("short_id").Int()
if roomID == 0 {
roomID = value.Get("room_id").Int()
}
lURL := bz.LiveURL + strconv.FormatInt(roomID, 10)
lName := value.Get("uname").String()
lTitle := value.Get("title").String()
lCover := value.Get("cover_from_user").String()
if lCover == "" {
lCover = value.Get("keyframe").String()
}
var msg []message.MessageSegment
msg = append(msg, message.Text(lName+" 正在直播:\n"))
msg = append(msg, message.Text(lTitle))
msg = append(msg, message.Image(lCover))
msg = append(msg, message.Text("直播链接:", lURL))
for _, gid := range groupList {
if m.IsEnabledIn(gid) {
time.Sleep(time.Millisecond * 100)
switch {
case gid > 0:
ctx.SendGroupMessage(gid, msg)
case gid < 0:
ctx.SendPrivateMessage(-gid, msg)
}
}
}
}
} else if newStatus != oldStatus {
liveStatus[key.Int()] = newStatus
}
return true
})
return nil
}

View File

@@ -0,0 +1,149 @@
package bilibili
import (
"encoding/json"
"os"
"github.com/jinzhu/gorm"
)
// bilibilipushdb bilibili推送数据库
type bilibilipushdb gorm.DB
type bilibilipush struct {
ID int64 `gorm:"column:id;primary_key" json:"id"`
BilibiliUID int64 `gorm:"column:bilibili_uid;index:idx_buid_gid" json:"bilibili_uid"`
GroupID int64 `gorm:"column:group_id;index:idx_buid_gid" json:"group_id"`
LiveDisable int64 `gorm:"column:live_disable;default:0" json:"live_disable"`
DynamicDisable int64 `gorm:"column:dynamic_disable;default:0" json:"dynamic_disable"`
}
// TableName ...
func (bilibilipush) TableName() string {
return "bilibili_push"
}
type bilibiliup struct {
BilibiliUID int64 `gorm:"column:bilibili_uid;primary_key"`
Name string `gorm:"column:name"`
}
// TableName ...
func (bilibiliup) TableName() string {
return "bilibili_up"
}
// initializePush 初始化bilibilipushdb数据库
func initializePush(dbpath string) *bilibilipushdb {
var err error
if _, err = os.Stat(dbpath); err != nil || os.IsNotExist(err) {
// 生成文件
f, err := os.Create(dbpath)
if err != nil {
return nil
}
defer f.Close()
}
gdb, err := gorm.Open("sqlite3", dbpath)
if err != nil {
panic(err)
}
gdb.AutoMigrate(&bilibilipush{}).AutoMigrate(&bilibiliup{})
return (*bilibilipushdb)(gdb)
}
// insertOrUpdateLiveAndDynamic 插入或更新数据库
func (bdb *bilibilipushdb) insertOrUpdateLiveAndDynamic(bpMap map[string]any) (err error) {
db := (*gorm.DB)(bdb)
bp := bilibilipush{}
data, err := json.Marshal(&bpMap)
if err != nil {
return
}
err = json.Unmarshal(data, &bp)
if err != nil {
return
}
if err = db.Model(&bilibilipush{}).First(&bp, "bilibili_uid = ? and group_id = ?", bp.BilibiliUID, bp.GroupID).Error; err != nil {
if gorm.IsRecordNotFoundError(err) {
err = db.Model(&bilibilipush{}).Create(&bp).Error
}
} else {
err = db.Model(&bilibilipush{}).Where("bilibili_uid = ? and group_id = ?", bp.BilibiliUID, bp.GroupID).Update(bpMap).Error
}
return
}
func (bdb *bilibilipushdb) getAllBuidByLive() (buidList []int64) {
db := (*gorm.DB)(bdb)
var bpl []bilibilipush
db.Model(&bilibilipush{}).Find(&bpl, "live_disable = 0")
temp := make(map[int64]bool)
for _, v := range bpl {
_, ok := temp[v.BilibiliUID]
if !ok {
buidList = append(buidList, v.BilibiliUID)
temp[v.BilibiliUID] = true
}
}
return
}
func (bdb *bilibilipushdb) getAllBuidByDynamic() (buidList []int64) {
db := (*gorm.DB)(bdb)
var bpl []bilibilipush
db.Model(&bilibilipush{}).Find(&bpl, "dynamic_disable = 0")
temp := make(map[int64]bool)
for _, v := range bpl {
_, ok := temp[v.BilibiliUID]
if !ok {
buidList = append(buidList, v.BilibiliUID)
temp[v.BilibiliUID] = true
}
}
return
}
func (bdb *bilibilipushdb) getAllGroupByBuidAndLive(buid int64) (groupList []int64) {
db := (*gorm.DB)(bdb)
var bpl []bilibilipush
db.Model(&bilibilipush{}).Find(&bpl, "bilibili_uid = ? and live_disable = 0", buid)
for _, v := range bpl {
groupList = append(groupList, v.GroupID)
}
return
}
func (bdb *bilibilipushdb) getAllGroupByBuidAndDynamic(buid int64) (groupList []int64) {
db := (*gorm.DB)(bdb)
var bpl []bilibilipush
db.Model(&bilibilipush{}).Find(&bpl, "bilibili_uid = ? and dynamic_disable = 0", buid)
for _, v := range bpl {
groupList = append(groupList, v.GroupID)
}
return
}
func (bdb *bilibilipushdb) getAllPushByGroup(groupID int64) (bpl []bilibilipush) {
db := (*gorm.DB)(bdb)
db.Model(&bilibilipush{}).Find(&bpl, "group_id = ? and (live_disable = 0 or dynamic_disable = 0)", groupID)
return
}
func (bdb *bilibilipushdb) insertBilibiliUp(buid int64, name string) {
db := (*gorm.DB)(bdb)
bu := bilibiliup{
BilibiliUID: buid,
Name: name,
}
db.Model(&bilibiliup{}).Create(bu)
}
func (bdb *bilibilipushdb) updateAllUp() {
db := (*gorm.DB)(bdb)
var bul []bilibiliup
db.Model(&bilibiliup{}).Find(&bul)
for _, v := range bul {
upMap[v.BilibiliUID] = v.Name
}
}

326
plugin/bilibili/card2msg.go Normal file
View File

@@ -0,0 +1,326 @@
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(dynamicIDStr string) (msg []message.MessageSegment, err error) {
dyc, err := bz.GetDynamicDetail(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))
return
}

View File

@@ -0,0 +1,84 @@
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 TestDynamicDetail(t *testing.T) {
t.Log("cType = 1")
t.Log(dynamicDetail("642279068898689029"))
t.Log("cType = 2")
t.Log(dynamicDetail("642470680290394121"))
t.Log("cType = 2048")
t.Log(dynamicDetail("642277677329285174"))
t.Log("cType = 4")
t.Log(dynamicDetail("642154347357011968"))
t.Log("cType = 8")
t.Log(dynamicDetail("675892999274627104"))
t.Log("cType = 4308")
t.Log(dynamicDetail("668598718656675844"))
t.Log("cType = 64")
t.Log(dynamicDetail("675966082178088963"))
t.Log("cType = 256")
t.Log(dynamicDetail("599253048535707632"))
t.Log("cType = 4,投票类型")
t.Log(dynamicDetail("677231070435868704"))
}
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,358 +0,0 @@
// Package bilibili 查询b站用户信息
package bilibili
import (
"encoding/binary"
"fmt"
"image"
"image/color"
"os"
"path"
"regexp"
"sort"
"strconv"
"time"
ctrl "github.com/FloatTech/zbpctrl"
"github.com/FloatTech/zbputils/control"
"github.com/FloatTech/zbputils/ctxext"
"github.com/FloatTech/zbputils/file"
"github.com/FloatTech/zbputils/img"
"github.com/FloatTech/zbputils/img/text"
"github.com/FloatTech/zbputils/img/writer"
"github.com/FloatTech/zbputils/web"
"github.com/fogleman/gg"
log "github.com/sirupsen/logrus"
zero "github.com/wdvxdr1123/ZeroBot"
"github.com/wdvxdr1123/ZeroBot/message"
)
var engine = control.Register("bilibili", &ctrl.Options[*zero.Ctx]{
DisableOnDefault: false,
Help: "bilibili\n" +
"- >vup info [xxx]\n" +
"- >user info [xxx]\n" +
"- 查成分 [xxx]\n" +
"- 设置b站cookie SESSDATA=82da790d,1663822823,06ecf*31\n" +
"- 更新vup",
PublicDataFolder: "Bilibili",
})
var re = regexp.MustCompile(`^\d+$`)
// 查成分的
func init() {
cachePath := engine.DataFolder() + "cache/"
_ = os.RemoveAll(cachePath)
_ = os.MkdirAll(cachePath, 0755)
var getdb = ctxext.DoOnceOnSuccess(func(ctx *zero.Ctx) bool {
var err error
_, _ = engine.GetLazyData("bilibili.db", false)
vdb, err = initialize(engine.DataFolder() + "bilibili.db")
if err != nil {
ctx.SendChain(message.Text("ERROR:", err))
return false
}
return true
})
engine.OnRegex(`^>user info\s?(.{1,25})$`, getdb).SetBlock(true).
Handle(func(ctx *zero.Ctx) {
keyword := ctx.State["regex_matched"].([]string)[1]
uidRes, err := search(keyword)
if err != nil {
ctx.SendChain(message.Text("ERROR:", err))
return
}
id := strconv.FormatInt(uidRes[0].Mid, 10)
follwings, err := followings(id)
if err != nil {
ctx.SendChain(message.Text("ERROR:", err))
}
ctx.SendChain(message.Text(
"search: ", uidRes[0].Mid, "\n",
"name: ", uidRes[0].Uname, "\n",
"sex: ", []string{"", "男", "女", "未知"}[uidRes[0].Gender], "\n",
"sign: ", uidRes[0].Usign, "\n",
"level: ", uidRes[0].Level, "\n",
"follow: ", follwings,
))
})
engine.OnRegex(`^>vup info\s?(.{1,25})$`).SetBlock(true).
Handle(func(ctx *zero.Ctx) {
keyword := ctx.State["regex_matched"].([]string)[1]
res, err := search(keyword)
if err != nil {
ctx.SendChain(message.Text("ERROR:", err))
return
}
id := strconv.FormatInt(res[0].Mid, 10)
// 获取详情
fo, err := fansapi(id)
if err != nil {
ctx.SendChain(message.Text("ERROR:", err))
return
}
ctx.SendChain(message.Text(
"search: ", fo.Mid, "\n",
"名字: ", fo.Uname, "\n",
"当前粉丝数: ", fo.Follower, "\n",
"24h涨粉数: ", fo.Rise, "\n",
"视频投稿数: ", fo.Video, "\n",
"直播间id: ", fo.Roomid, "\n",
"舰队: ", fo.GuardNum, "\n",
"直播总排名: ", fo.AreaRank, "\n",
"数据来源: ", "https://vtbs.moe/detail/", fo.Mid, "\n",
"数据获取时间: ", time.Now().Format("2006-01-02 15:04:05"),
))
})
engine.OnRegex(`^查成分\s?(.{1,25})$`, getdb, getPara).SetBlock(true).
Handle(func(ctx *zero.Ctx) {
id := ctx.State["uid"].(string)
today := time.Now().Format("20060102")
drawedFile := cachePath + id + today + "vupLike.png"
if file.IsExist(drawedFile) {
ctx.SendChain(message.Image("file:///" + file.BOTPATH + "/" + drawedFile))
return
}
u, err := card(id)
if err != nil {
ctx.SendChain(message.Text("ERROR:", err))
return
}
vups, err := vdb.filterVup(u.Attentions)
if err != nil {
ctx.SendChain(message.Text("ERROR:", err))
return
}
vupLen := len(vups)
medals, err := medalwall(id)
sort.Sort(medalSlice(medals))
if err != nil {
ctx.SendChain(message.Text("ERROR:", err))
}
frontVups := make([]vup, 0)
medalMap := make(map[int64]medal)
for _, v := range medals {
up := vup{
Mid: v.Mid,
Uname: v.Uname,
}
frontVups = append(frontVups, up)
medalMap[v.Mid] = v
}
vups = append(vups, frontVups...)
copy(vups[len(frontVups):], vups)
copy(vups, frontVups)
for i := len(frontVups); i < len(vups); i++ {
if _, ok := medalMap[vups[i].Mid]; ok {
vups = append(vups[:i], vups[i+1:]...)
i--
}
}
facePath := cachePath + id + "vupFace" + path.Ext(u.Face)
backX := 500
backY := 500
var back image.Image
if path.Ext(u.Face) != ".webp" {
err = initFacePic(facePath, u.Face)
if err != nil {
ctx.SendChain(message.Text("ERROR:", err))
return
}
back, err = gg.LoadImage(facePath)
if err != nil {
ctx.SendChain(message.Text("ERROR:", err))
return
}
back = img.Size(back, backX, backY).Im
}
if len(vups) > 50 {
ctx.SendChain(message.Text(u.Name + "关注的up主太多了只展示前50个up"))
vups = vups[:50]
}
canvas := gg.NewContext(1500, int(500*(1.1+float64(len(vups))/3)))
fontSize := 50.0
canvas.SetColor(color.White)
canvas.Clear()
if back != nil {
canvas.DrawImage(back, 0, 0)
}
canvas.SetColor(color.Black)
_, err = file.GetLazyData(text.BoldFontFile, true)
if err != nil {
ctx.SendChain(message.Text("ERROR:", err))
}
if err = canvas.LoadFontFace(text.BoldFontFile, fontSize); err != nil {
ctx.SendChain(message.Text("ERROR:", err))
return
}
sl, _ := canvas.MeasureString("好")
length, h := canvas.MeasureString(u.Mid)
n, _ := canvas.MeasureString(u.Name)
canvas.DrawString(u.Name, 550, 160-h)
canvas.DrawRoundedRectangle(600+n-length*0.1, 160-h*2.5, length*1.2, h*2, fontSize*0.2)
canvas.SetRGB255(221, 221, 221)
canvas.Fill()
canvas.SetColor(color.Black)
canvas.DrawString(u.Mid, 600+n, 160-h)
canvas.DrawString(fmt.Sprintf("粉丝:%d", u.Fans), 550, 240-h)
canvas.DrawString(fmt.Sprintf("关注:%d", len(u.Attentions)), 1000, 240-h)
canvas.DrawString(fmt.Sprintf("管人痴成分:%.2f%%%d/%d", float64(vupLen)/float64(len(u.Attentions))*100, vupLen, len(u.Attentions)), 550, 320-h)
regtime := time.Unix(u.Regtime, 0).Format("2006-01-02 15:04:05")
canvas.DrawString("注册日期:"+regtime, 550, 400-h)
canvas.DrawString("查询日期:"+time.Now().Format("2006-01-02"), 550, 480-h)
for i, v := range vups {
if i%2 == 1 {
canvas.SetRGB255(245, 245, 245)
canvas.DrawRectangle(0, float64(backY)*1.1+float64(i)*float64(backY)/3, float64(backX*3), float64(backY)/3)
canvas.Fill()
}
canvas.SetColor(color.Black)
nl, _ := canvas.MeasureString(v.Uname)
canvas.DrawString(v.Uname, float64(backX)*0.1, float64(backY)*1.1+float64(i+1)*float64(backY)/3-2*h)
ml, _ := canvas.MeasureString(strconv.FormatInt(v.Mid, 10))
canvas.DrawRoundedRectangle(nl-0.1*ml+float64(backX)*0.2, float64(backY)*1.1+float64(i+1)*float64(backY)/3-h*3.5, ml*1.2, h*2, fontSize*0.2)
canvas.SetRGB255(221, 221, 221)
canvas.Fill()
canvas.SetColor(color.Black)
canvas.DrawString(strconv.FormatInt(v.Mid, 10), nl+float64(backX)*0.2, float64(backY)*1.1+float64(i+1)*float64(backY)/3-2*h)
if m, ok := medalMap[v.Mid]; ok {
mnl, _ := canvas.MeasureString(m.MedalName)
grad := gg.NewLinearGradient(nl+ml-sl/2+float64(backX)*0.4, float64(backY)*1.1+float64(i+1)*float64(backY)/3-3.5*h, nl+ml+mnl+sl/2+float64(backX)*0.4, float64(backY)*1.1+float64(i+1)*float64(backY)/3-1.5*h)
r, g, b := int2rbg(m.MedalColorStart)
grad.AddColorStop(0, color.RGBA{R: uint8(r), G: uint8(g), B: uint8(b), A: 255})
r, g, b = int2rbg(m.MedalColorEnd)
grad.AddColorStop(1, color.RGBA{R: uint8(r), G: uint8(g), B: uint8(b), A: 255})
canvas.SetFillStyle(grad)
canvas.SetLineWidth(4)
canvas.MoveTo(nl+ml-sl/2+float64(backX)*0.4, float64(backY)*1.1+float64(i+1)*float64(backY)/3-3.5*h)
canvas.LineTo(nl+ml+mnl+sl/2+float64(backX)*0.4, float64(backY)*1.1+float64(i+1)*float64(backY)/3-3.5*h)
canvas.LineTo(nl+ml+mnl+sl/2+float64(backX)*0.4, float64(backY)*1.1+float64(i+1)*float64(backY)/3-1.5*h)
canvas.LineTo(nl+ml-sl/2+float64(backX)*0.4, float64(backY)*1.1+float64(i+1)*float64(backY)/3-1.5*h)
canvas.ClosePath()
canvas.Fill()
canvas.SetColor(color.White)
canvas.DrawString(m.MedalName, nl+ml+float64(backX)*0.4, float64(backY)*1.1+float64(i+1)*float64(backY)/3-2*h)
r, g, b = int2rbg(m.MedalColorBorder)
canvas.SetRGB255(int(r), int(g), int(b))
canvas.DrawString(strconv.FormatInt(m.Level, 10), nl+ml+mnl+sl+float64(backX)*0.4, float64(backY)*1.1+float64(i+1)*float64(backY)/3-2*h)
mll, _ := canvas.MeasureString(strconv.FormatInt(m.Level, 10))
canvas.SetLineWidth(4)
canvas.MoveTo(nl+ml-sl/2+float64(backX)*0.4, float64(backY)*1.1+float64(i+1)*float64(backY)/3-3.5*h)
canvas.LineTo(nl+ml+mnl+mll+sl/2+float64(backX)*0.5, float64(backY)*1.1+float64(i+1)*float64(backY)/3-3.5*h)
canvas.LineTo(nl+ml+mnl+mll+sl/2+float64(backX)*0.5, float64(backY)*1.1+float64(i+1)*float64(backY)/3-1.5*h)
canvas.LineTo(nl+ml-sl/2+float64(backX)*0.4, float64(backY)*1.1+float64(i+1)*float64(backY)/3-1.5*h)
canvas.ClosePath()
canvas.Stroke()
}
}
f, err := os.Create(drawedFile)
if err != nil {
log.Errorln("[bilibili]", err)
data, cl := writer.ToBytes(canvas.Image())
ctx.SendChain(message.ImageBytes(data))
cl()
return
}
_, err = writer.WriteTo(canvas.Image(), f)
_ = f.Close()
if err != nil {
ctx.SendChain(message.Text("ERROR:", err))
return
}
ctx.SendChain(message.Image("file:///" + file.BOTPATH + "/" + drawedFile))
})
engine.OnRegex(`^设置b站cookie?\s+(.{1,100})$`, zero.SuperUserPermission, getdb).SetBlock(true).
Handle(func(ctx *zero.Ctx) {
cookie := ctx.State["regex_matched"].([]string)[1]
err := vdb.setBilibiliCookie(cookie)
if err != nil {
ctx.SendChain(message.Text("ERROR:", err))
return
}
ctx.SendChain(message.Text("成功设置b站cookie为" + cookie))
})
engine.OnFullMatch("更新vup", zero.SuperUserPermission, getdb).SetBlock(true).
Handle(func(ctx *zero.Ctx) {
ctx.SendChain(message.Text("少女祈祷中..."))
err := updateVup()
if err != nil {
ctx.SendChain(message.Text("ERROR:", err))
return
}
ctx.SendChain(message.Text("vup已更新"))
})
}
func initFacePic(filename, faceURL string) error {
if file.IsNotExist(filename) {
data, err := web.GetData(faceURL)
if err != nil {
return err
}
err = os.WriteFile(filename, data, 0666)
if err != nil {
return err
}
}
return nil
}
func int2rbg(t int64) (int64, int64, int64) {
var buf [8]byte
binary.LittleEndian.PutUint64(buf[:], uint64(t))
b, g, r := int64(buf[0]), int64(buf[1]), int64(buf[2])
return r, g, b
}
func getPara(ctx *zero.Ctx) bool {
keyword := ctx.State["regex_matched"].([]string)[1]
if !re.MatchString(keyword) {
searchRes, err := search(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 := search(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,190 +0,0 @@
// Package bilibiliparse b站视频链接解析
package bilibiliparse
import (
"encoding/json"
"net/http"
"regexp"
"strconv"
"strings"
"time"
ctrl "github.com/FloatTech/zbpctrl"
"github.com/FloatTech/zbputils/control"
"github.com/FloatTech/zbputils/ctxext"
"github.com/FloatTech/zbputils/web"
zero "github.com/wdvxdr1123/ZeroBot"
"github.com/wdvxdr1123/ZeroBot/message"
)
type result struct {
Data struct {
Bvid string `json:"bvid"`
Aid int `json:"aid"`
Copyright int `json:"copyright"`
Pic string `json:"pic"`
Title string `json:"title"`
Pubdate int `json:"pubdate"`
Ctime int `json:"ctime"`
Rights struct {
IsCooperation int `json:"is_cooperation"`
} `json:"rights"`
Owner struct {
Mid int `json:"mid"`
Name string `json:"name"`
} `json:"owner"`
Stat struct {
Aid int `json:"aid"`
View int `json:"view"`
Danmaku int `json:"danmaku"`
Reply int `json:"reply"`
Favorite int `json:"favorite"`
Coin int `json:"coin"`
Share int `json:"share"`
Like int `json:"like"`
} `json:"stat"`
Staff []struct {
Title string `json:"title"`
Name string `json:"name"`
Follower int `json:"follower"`
} `json:"staff"`
} `json:"data"`
}
type owner struct {
Data struct {
Card struct {
Fans int `json:"fans"`
} `json:"card"`
} `json:"data"`
}
const (
videoapi = "https://api.bilibili.com/x/web-interface/view?"
cardapi = "http://api.bilibili.com/x/web-interface/card?"
origin = "https://www.bilibili.com/video/"
)
var (
reg = regexp.MustCompile(`https://www.bilibili.com/video/([0-9a-zA-Z]+)`)
limit = ctxext.NewLimiterManager(time.Second*10, 1)
)
// 插件主体
func init() {
en := control.Register("bilibiliparse", &ctrl.Options[*zero.Ctx]{
DisableOnDefault: false,
Help: "b站视频链接解析\n" +
"- https://www.bilibili.com/video/BV1xx411c7BF | https://www.bilibili.com/video/av1605 | https://b23.tv/I8uzWCA | https://www.bilibili.com/video/bv1xx411c7BF",
})
en.OnRegex(`(av[0-9]+|BV[0-9a-zA-Z]{10}){1}`).SetBlock(true).Limit(limit.LimitByGroup).
Handle(func(ctx *zero.Ctx) {
if strings.Contains(ctx.MessageString(), "[CQ:forward") {
return
}
id := ctx.State["regex_matched"].([]string)[1]
m, err := parse(id)
if err != nil {
ctx.SendChain(message.Text("ERROR: ", err))
return
}
ctx.Send(m)
})
en.OnRegex(`https://www.bilibili.com/video/([0-9a-zA-Z]+)`).SetBlock(true).Limit(limit.LimitByGroup).
Handle(func(ctx *zero.Ctx) {
id := ctx.State["regex_matched"].([]string)[1]
m, err := parse(id)
if err != nil {
ctx.SendChain(message.Text("ERROR: ", err))
return
}
ctx.Send(m)
})
en.OnRegex(`(https://b23.tv/[0-9a-zA-Z]+)`).SetBlock(true).Limit(limit.LimitByGroup).
Handle(func(ctx *zero.Ctx) {
url := ctx.State["regex_matched"].([]string)[1]
realurl, err := getrealurl(url)
if err != nil {
ctx.SendChain(message.Text("ERROR: ", err))
return
}
m, err := parse(cuturl(realurl))
if err != nil {
ctx.SendChain(message.Text("ERROR: ", err))
return
}
ctx.Send(m)
})
}
// parse 解析视频数据
func parse(id string) (m message.Message, err error) {
var vid string
switch id[:2] {
case "av":
vid = "aid=" + id[2:]
case "BV":
vid = "bvid=" + id
}
data, err := web.GetData(videoapi + vid)
if err != nil {
return
}
var r result
err = json.Unmarshal(data, &r)
if err != nil {
return
}
m = make(message.Message, 0, 16)
m = append(m, message.Text("标题: ", r.Data.Title, "\n"))
if r.Data.Rights.IsCooperation == 1 {
for i := 0; i < len(r.Data.Staff); i++ {
m = append(m, message.Text(r.Data.Staff[i].Title, ": ", r.Data.Staff[i].Name, ", 粉丝: ", row(r.Data.Staff[i].Follower), "\n"))
}
} else {
o, err := getcard(r.Data.Owner.Mid)
if err != nil {
return m, err
}
m = append(m, message.Text("UP主: ", r.Data.Owner.Name, ", 粉丝: ", row(o.Data.Card.Fans), "\n"))
}
m = append(m, message.Text("播放: ", row(r.Data.Stat.View), ", 弹幕: ", row(r.Data.Stat.Danmaku), "\n"),
message.Image(r.Data.Pic),
message.Text("\n点赞: ", row(r.Data.Stat.Like), ", 投币: ", row(r.Data.Stat.Coin), "\n收藏: ", row(r.Data.Stat.Favorite), ", 分享: ", row(r.Data.Stat.Share), "\n", origin, id))
return
}
// getrealurl 获取跳转后的链接
func getrealurl(url string) (realurl string, err error) {
data, err := http.Head(url)
if err != nil {
return
}
realurl = data.Request.URL.String()
return
}
// cuturl 获取aid或者bvid
func cuturl(url string) (id string) {
if !reg.MatchString(url) {
return
}
return reg.FindStringSubmatch(url)[1]
}
// getcard 获取个人信息
func getcard(mid int) (o owner, err error) {
data, err := web.GetData(cardapi + "mid=" + strconv.Itoa(mid))
if err != nil {
return
}
err = json.Unmarshal(data, &o)
return
}
func row(res int) string {
if res/10000 != 0 {
return strconv.FormatFloat(float64(res)/10000, 'f', 2, 64) + "万"
}
return strconv.Itoa(res)
}

View File

@@ -8,37 +8,38 @@ import (
zero "github.com/wdvxdr1123/ZeroBot"
"github.com/wdvxdr1123/ZeroBot/message"
"github.com/FloatTech/floatbox/binary"
fcext "github.com/FloatTech/floatbox/ctxext"
ctrl "github.com/FloatTech/zbpctrl"
"github.com/FloatTech/zbputils/binary"
"github.com/FloatTech/zbputils/control"
"github.com/FloatTech/zbputils/ctxext"
"github.com/FloatTech/zbputils/img/text"
)
func init() {
engine := control.Register("bookreview", &ctrl.Options[*zero.Ctx]{
DisableOnDefault: false,
Help: "哀伤雪刃推书记录\n- 书评[xxx]\n- 随机书评",
Brief: "哀伤雪刃推书书评",
Help: "- 书评[xxx]\n- 随机书评",
PublicDataFolder: "BookReview",
})
getdb := ctxext.DoOnceOnSuccess(func(ctx *zero.Ctx) bool {
getdb := fcext.DoOnceOnSuccess(func(ctx *zero.Ctx) bool {
db.DBPath = engine.DataFolder() + "bookreview.db"
// os.RemoveAll(dbpath)
_, _ = engine.GetLazyData("bookreview.db", true)
err := db.Open(time.Hour * 24)
if err != nil {
ctx.SendChain(message.Text("ERROR:", err))
ctx.SendChain(message.Text("ERROR: ", err))
return false
}
err = db.Create("bookreview", &book{})
if err != nil {
ctx.SendChain(message.Text("ERROR:", err))
ctx.SendChain(message.Text("ERROR: ", err))
return false
}
n, err := db.Count("bookreview")
if err != nil {
ctx.SendChain(message.Text("ERROR:", err))
ctx.SendChain(message.Text("ERROR: ", err))
return false
}
log.Infof("[bookreview]读取%d条书评", n)
@@ -51,11 +52,11 @@ func init() {
b := getBookReviewByKeyword(ctx.State["regex_matched"].([]string)[1])
data, err := text.RenderToBase64(b.BookReview, text.FontFile, 400, 20)
if err != nil {
ctx.SendChain(message.Text("ERROR:", err))
ctx.SendChain(message.Text("ERROR: ", err))
return
}
if id := ctx.SendChain(message.Image("base64://" + binary.BytesToString(data))); id.ID() == 0 {
ctx.SendChain(message.Text("ERROR:可能被风控了"))
ctx.SendChain(message.Text("ERROR: 可能被风控了"))
}
})
@@ -64,11 +65,11 @@ func init() {
br := getRandomBookReview()
data, err := text.RenderToBase64(br.BookReview, text.FontFile, 400, 20)
if err != nil {
ctx.SendChain(message.Text("ERROR:", err))
ctx.SendChain(message.Text("ERROR: ", err))
return
}
if id := ctx.SendChain(message.Image("base64://" + binary.BytesToString(data))); id.ID() == 0 {
ctx.SendChain(message.Text("ERROR:可能被风控了"))
ctx.SendChain(message.Text("ERROR: 可能被风控了"))
}
})
}

View File

@@ -0,0 +1,50 @@
// Package breakrepeat 打断复读
package breakrepeat
import (
"math/rand"
"strconv"
ctrl "github.com/FloatTech/zbpctrl"
"github.com/FloatTech/zbputils/control"
"github.com/RomiChan/syncx"
zero "github.com/wdvxdr1123/ZeroBot"
)
const throttle = 3 // 不可超过 9
var sm syncx.Map[int64, string]
func init() {
engine := control.Register("breakrepeat", &ctrl.Options[*zero.Ctx]{
DisableOnDefault: false,
Brief: "打断复读",
Help: "- 打断" + strconv.Itoa(throttle) + "次以上复读\n",
})
engine.On("message/group", zero.OnlyGroup, func(ctx *zero.Ctx) bool {
return !zero.HasPicture(ctx)
}).SetBlock(false).
Handle(func(ctx *zero.Ctx) {
gid := ctx.Event.GroupID
raw := ctx.Event.RawMessage
r, ok := sm.Load(gid)
if !ok || len(r) <= 3 || r[3:] != raw {
sm.Store(gid, "0: "+raw)
return
}
c := int(r[0] - '0')
if c < throttle {
sm.Store(gid, strconv.Itoa(c+1)+": "+raw)
return
}
sm.Delete(gid)
if len(r) > 5 {
ru := []rune(r[3:])
rand.Shuffle(len(ru), func(i, j int) {
ru[i], ru[j] = ru[j], ru[i]
})
r = string(ru)
}
ctx.Send(r)
})
}

View File

@@ -32,24 +32,24 @@ var (
func init() {
engine := control.Register("cangtoushi", &ctrl.Options[*zero.Ctx]{
DisableOnDefault: false,
Help: "藏头诗\n" +
"- 藏头诗[xxx]\n- 藏尾诗[xxx]",
Brief: "藏头诗, 藏尾诗",
Help: "- 藏头诗[xxx]\n- 藏尾诗[xxx]",
})
engine.OnRegex(`藏头诗\s?([一-龥]{3,10})$`).SetBlock(true).Handle(func(ctx *zero.Ctx) {
kw := ctx.State["regex_matched"].([]string)[1]
err := login()
if err != nil {
ctx.SendChain(message.Text("ERROR:", err))
ctx.SendChain(message.Text("ERROR: ", err))
return
}
data, err := search(kw, "7", "0")
if err != nil {
ctx.SendChain(message.Text("ERROR:", err))
ctx.SendChain(message.Text("ERROR: ", err))
return
}
text, err := dealHTML(helper.BytesToString(data))
if err != nil {
ctx.SendChain(message.Text("ERROR:", err))
ctx.SendChain(message.Text("ERROR: ", err))
return
}
ctx.SendChain(message.Text(text))
@@ -59,17 +59,17 @@ func init() {
kw := ctx.State["regex_matched"].([]string)[1]
err := login()
if err != nil {
ctx.SendChain(message.Text("ERROR:", err))
ctx.SendChain(message.Text("ERROR: ", err))
return
}
data, err := search(kw, "7", "2")
if err != nil {
ctx.SendChain(message.Text("ERROR:", err))
ctx.SendChain(message.Text("ERROR: ", err))
return
}
text, err := dealHTML(helper.BytesToString(data))
if err != nil {
ctx.SendChain(message.Text("ERROR:", err))
ctx.SendChain(message.Text("ERROR: ", err))
return
}
ctx.SendChain(message.Text(text))

View File

@@ -17,6 +17,7 @@ var (
poke = rate.NewManager[int64](time.Minute*5, 8) // 戳一戳
engine = control.Register("chat", &ctrl.Options[*zero.Ctx]{
DisableOnDefault: false,
Brief: "基础反应, 群空调",
Help: "chat\n- [BOT名字]\n- [戳一戳BOT]\n- 空调开\n- 空调关\n- 群温度\n- 设置温度[正整数]",
})
)

View File

@@ -15,9 +15,9 @@ import (
func init() {
engine := control.Register("choose", &ctrl.Options[*zero.Ctx]{
DisableOnDefault: false,
Help: "choose\n" +
"- 选择可口可乐还是百事可乐\n" +
"- 选择肯德基还是麦当劳还是必胜客",
Brief: "选择困难症帮手",
Help: "例: 选择可口可乐还是百事可乐\n" +
"选择肯德基还是麦当劳还是必胜客",
})
engine.OnPrefix("选择").SetBlock(true).Handle(handle)
}

View File

@@ -8,36 +8,37 @@ import (
zero "github.com/wdvxdr1123/ZeroBot"
"github.com/wdvxdr1123/ZeroBot/message"
fcext "github.com/FloatTech/floatbox/ctxext"
ctrl "github.com/FloatTech/zbpctrl"
"github.com/FloatTech/zbputils/control"
"github.com/FloatTech/zbputils/ctxext"
)
func init() {
en := control.Register("chouxianghua", &ctrl.Options[*zero.Ctx]{
DisableOnDefault: false,
Help: "抽象话\n- 抽象翻译xxx",
Brief: "翻译为抽象话",
Help: "- 抽象翻译xxx",
PublicDataFolder: "ChouXiangHua",
})
en.OnRegex("^抽象翻译((\\s|[\\r\\n]|[\\p{Han}\\p{P}A-Za-z0-9])+)$",
ctxext.DoOnceOnSuccess(func(ctx *zero.Ctx) bool {
fcext.DoOnceOnSuccess(func(ctx *zero.Ctx) bool {
db.DBPath = en.DataFolder() + "cxh.db"
// os.RemoveAll(dbpath)
_, _ = en.GetLazyData("cxh.db", true)
err := db.Open(time.Hour * 24)
if err != nil {
ctx.SendChain(message.Text("ERROR:", err))
ctx.SendChain(message.Text("ERROR: ", err))
return false
}
err = db.Create("pinyin", &pinyin{})
if err != nil {
ctx.SendChain(message.Text("ERROR:", err))
ctx.SendChain(message.Text("ERROR: ", err))
return false
}
n, err := db.Count("pinyin")
if err != nil {
ctx.SendChain(message.Text("ERROR:", err))
ctx.SendChain(message.Text("ERROR: ", err))
return false
}
logrus.Printf("[chouxianghua]读取%d条拼音", n)

33
plugin/chrev/init.go Normal file
View File

@@ -0,0 +1,33 @@
// Package chrev 英文字符反转
package chrev
import (
"strings"
ctrl "github.com/FloatTech/zbpctrl"
"github.com/FloatTech/zbputils/control"
zero "github.com/wdvxdr1123/ZeroBot"
"github.com/wdvxdr1123/ZeroBot/message"
)
func init() {
// 初始化engine
engine := control.Register("chrev", &ctrl.Options[*zero.Ctx]{
DisableOnDefault: false,
Brief: "英文字符翻转",
Help: "例: 翻转 I love you",
})
// 处理字符翻转指令
engine.OnRegex(`^翻转\s*([A-Za-z\s]*)$`).SetBlock(true).
Handle(func(ctx *zero.Ctx) {
// 获取需要翻转的字符串
str := ctx.State["regex_matched"].([]string)[1]
// 将字符顺序翻转
tmp := strings.Builder{}
for i := len(str) - 1; i >= 0; i-- {
tmp.WriteRune(charMap[str[i]])
}
// 发送翻转后的字符串
ctx.SendChain(message.Text(tmp.String()))
})
}

59
plugin/chrev/map.go Normal file
View File

@@ -0,0 +1,59 @@
package chrev
var charMap [256]rune
func init() {
charMap[' '] = ' '
charMap['a'] = 'ɐ'
charMap['b'] = 'q'
charMap['c'] = 'ɔ'
charMap['d'] = 'p'
charMap['e'] = 'ǝ'
charMap['f'] = 'ɟ'
charMap['g'] = 'ƃ'
charMap['h'] = 'ɥ'
charMap['i'] = 'ᴉ'
charMap['j'] = 'ɾ'
charMap['k'] = 'ʞ'
charMap['l'] = 'l'
charMap['m'] = 'ɯ'
charMap['n'] = 'u'
charMap['o'] = 'o'
charMap['p'] = 'd'
charMap['q'] = 'b'
charMap['r'] = 'ɹ'
charMap['s'] = 's'
charMap['t'] = 'ʇ'
charMap['u'] = 'n'
charMap['v'] = 'ʌ'
charMap['w'] = 'ʍ'
charMap['x'] = 'x'
charMap['y'] = 'ʎ'
charMap['z'] = 'z'
charMap['A'] = '∀'
charMap['B'] = 'ᗺ'
charMap['C'] = 'Ɔ'
charMap['D'] = 'ᗡ'
charMap['E'] = 'Ǝ'
charMap['F'] = 'Ⅎ'
charMap['G'] = '⅁'
charMap['H'] = 'H'
charMap['I'] = 'I'
charMap['J'] = 'ſ'
charMap['K'] = 'ʞ'
charMap['L'] = '˥'
charMap['M'] = 'W'
charMap['N'] = 'N'
charMap['O'] = 'O'
charMap['P'] = 'Ԁ'
charMap['Q'] = 'Ò'
charMap['R'] = 'ᴚ'
charMap['S'] = 'S'
charMap['T'] = '⏊'
charMap['U'] = '∩'
charMap['V'] = 'Λ'
charMap['W'] = 'M'
charMap['X'] = 'X'
charMap['Y'] = '⅄'
charMap['Z'] = 'Z'
}

View File

@@ -2,7 +2,10 @@
package coser
import (
"regexp"
"errors"
"math/rand"
"os"
"time"
"github.com/tidwall/gjson"
@@ -10,47 +13,56 @@ import (
"github.com/wdvxdr1123/ZeroBot/message"
"github.com/wdvxdr1123/ZeroBot/utils/helper"
"github.com/FloatTech/AnimeAPI/setu"
"github.com/FloatTech/floatbox/file"
"github.com/FloatTech/floatbox/web"
ctrl "github.com/FloatTech/zbpctrl"
"github.com/FloatTech/zbputils/control"
"github.com/FloatTech/zbputils/ctxext"
"github.com/FloatTech/zbputils/web"
)
var (
ua = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/96.0.4664.93 Safari/537.36"
coserURL = "http://ovooa.com/API/cosplay/api.php"
datestr = regexp.MustCompile(`/\d{4}-\d{2}-\d{2}/`)
)
func init() {
p, err := setu.NewPool(setu.DefaultPoolDir,
func(s string) (string, error) {
if s != "coser" {
return "", errors.New("invalid call")
}
typ := setu.DefaultPoolDir + "/" + "coser"
if file.IsNotExist(typ) {
err := os.MkdirAll(typ, 0755)
if err != nil {
return "", err
}
}
data, err := web.RequestDataWith(web.NewDefaultClient(), coserURL, "GET", "", ua, nil)
if err != nil {
return "", err
}
arr := gjson.Get(helper.BytesToString(data), "data.data").Array()
pic := arr[rand.Intn(len(arr))]
return pic.String(), nil
}, web.GetData, time.Minute)
if err != nil {
panic(err)
}
control.Register("coser", &ctrl.Options[*zero.Ctx]{
DisableOnDefault: false,
Help: "三次元小姐姐\n- coser",
}).ApplySingle(ctxext.DefaultSingle).OnFullMatch("coser", zero.OnlyGroup).SetBlock(true).Limit(ctxext.LimitByGroup).
Brief: "三次元coser",
Help: "- coser",
}).ApplySingle(ctxext.DefaultSingle).OnFullMatch("coser").SetBlock(true).Limit(ctxext.LimitByGroup).
Handle(func(ctx *zero.Ctx) {
ctx.SendChain(message.Text("少女祈祷中......"))
data, err := web.RequestDataWith(web.NewDefaultClient(), coserURL, "GET", "", ua)
pic, err := p.Roll("coser")
if err != nil {
ctx.SendChain(message.Text("ERROR:", err))
ctx.SendChain(message.Text("ERROR: ", err))
return
}
text := gjson.Get(helper.BytesToString(data), "data.Title").String()
m := message.Message{ctxext.FakeSenderForwardNode(ctx, message.Text(text))}
ds := ""
gjson.Get(helper.BytesToString(data), "data.data").ForEach(func(_, value gjson.Result) bool {
if ds == "" {
ds = datestr.FindString(value.String())
} else if ds != datestr.FindString(value.String()) {
return false
}
m = append(m, ctxext.FakeSenderForwardNode(ctx, message.Image(value.String())))
return true
})
if id := ctx.SendGroupForwardMessage(
ctx.Event.GroupID,
m).Get("message_id").Int(); id == 0 {
ctx.SendChain(message.Text("ERROR:可能被风控或下载图片用时过长,请耐心等待"))
if id := ctx.Send(message.Message{ctxext.FakeSenderForwardNode(ctx, message.Image("file:///"+file.BOTPATH+"/"+pic))}).ID(); id == 0 {
ctx.SendChain(message.Text("ERROR: 可能被风控或下载图片用时过长,请耐心等待"))
}
})
}

View File

@@ -9,36 +9,37 @@ import (
zero "github.com/wdvxdr1123/ZeroBot"
"github.com/wdvxdr1123/ZeroBot/message"
fcext "github.com/FloatTech/floatbox/ctxext"
"github.com/FloatTech/floatbox/math"
ctrl "github.com/FloatTech/zbpctrl"
"github.com/FloatTech/zbputils/control"
"github.com/FloatTech/zbputils/ctxext"
"github.com/FloatTech/zbputils/math"
)
func init() {
engine := control.Register("cpstory", &ctrl.Options[*zero.Ctx]{
DisableOnDefault: false,
Help: "cp短打\n- 组cp[@xxx][@xxx]\n- 磕cp大老师 雪乃",
Brief: "cp短打", // 这里也许有更好的名字
Help: "- 组cp[@xxx][@xxx]\n- 磕cp大老师 雪乃",
PublicDataFolder: "CpStory",
})
getdb := ctxext.DoOnceOnSuccess(func(ctx *zero.Ctx) bool {
getdb := fcext.DoOnceOnSuccess(func(ctx *zero.Ctx) bool {
db.DBPath = engine.DataFolder() + "cp.db"
// os.RemoveAll(dbpath)
_, _ = engine.GetLazyData("cp.db", true)
err := db.Open(time.Hour * 24)
if err != nil {
ctx.SendChain(message.Text("ERROR:", err))
ctx.SendChain(message.Text("ERROR: ", err))
return false
}
err = db.Create("cp_story", &cpstory{})
if err != nil {
ctx.SendChain(message.Text("ERROR:", err))
ctx.SendChain(message.Text("ERROR: ", err))
return false
}
n, err := db.Count("cp_story")
if err != nil {
ctx.SendChain(message.Text("ERROR:", err))
ctx.SendChain(message.Text("ERROR: ", err))
return false
}
logrus.Printf("[cpstory]读取%d条故事", n)
@@ -52,7 +53,7 @@ func init() {
text := strings.ReplaceAll(cs.Story, "<攻>", gong)
text = strings.ReplaceAll(text, "<受>", shou)
text = strings.ReplaceAll(text, cs.Gong, gong)
text = strings.ReplaceAll(text, cs.Shou, gong)
text = strings.ReplaceAll(text, cs.Shou, shou)
ctx.SendChain(message.Text(text))
})
engine.OnPrefix("磕cp", getdb).SetBlock(true).Handle(func(ctx *zero.Ctx) {

View File

@@ -8,10 +8,11 @@ import (
zero "github.com/wdvxdr1123/ZeroBot"
"github.com/wdvxdr1123/ZeroBot/message"
fcext "github.com/FloatTech/floatbox/ctxext"
"github.com/FloatTech/floatbox/process"
ctrl "github.com/FloatTech/zbpctrl"
"github.com/FloatTech/zbputils/control"
"github.com/FloatTech/zbputils/ctxext"
"github.com/FloatTech/zbputils/process"
)
const (
@@ -22,30 +23,31 @@ const (
func init() {
engine := control.Register("curse", &ctrl.Options[*zero.Ctx]{
DisableOnDefault: true,
Help: "骂人(求骂,自卫)\n- 骂我\n- 大力骂我",
Brief: "骂人反击",
Help: "- 骂我\n- 大力骂我",
PublicDataFolder: "Curse",
})
getdb := ctxext.DoOnceOnSuccess(func(ctx *zero.Ctx) bool {
getdb := fcext.DoOnceOnSuccess(func(ctx *zero.Ctx) bool {
db.DBPath = engine.DataFolder() + "curse.db"
_, err := engine.GetLazyData("curse.db", true)
if err != nil {
ctx.SendChain(message.Text("ERROR:", err))
ctx.SendChain(message.Text("ERROR: ", err))
return false
}
err = db.Open(time.Hour * 24)
if err != nil {
ctx.SendChain(message.Text("ERROR:", err))
ctx.SendChain(message.Text("ERROR: ", err))
return false
}
err = db.Create("curse", &curse{})
if err != nil {
ctx.SendChain(message.Text("ERROR:", err))
ctx.SendChain(message.Text("ERROR: ", err))
return false
}
c, err := db.Count("curse")
if err != nil {
ctx.SendChain(message.Text("ERROR:", err))
ctx.SendChain(message.Text("ERROR: ", err))
return false
}
logrus.Infoln("[curse]加载", c, "条骂人语录")

View File

@@ -4,12 +4,13 @@ package deepdanbooru
import (
"crypto/md5"
"encoding/hex"
"strings"
"github.com/FloatTech/AnimeAPI/danbooru"
"github.com/FloatTech/floatbox/file"
"github.com/FloatTech/imgfactory"
ctrl "github.com/FloatTech/zbpctrl"
"github.com/FloatTech/zbputils/control"
"github.com/FloatTech/zbputils/file"
"github.com/FloatTech/zbputils/img/writer"
"github.com/FloatTech/zbputils/ctxext"
zero "github.com/wdvxdr1123/ZeroBot"
"github.com/wdvxdr1123/ZeroBot/message"
"github.com/wdvxdr1123/ZeroBot/utils/helper"
@@ -17,9 +18,9 @@ import (
func init() { // 插件主体
engine := control.Register("danbooru", &ctrl.Options[*zero.Ctx]{
DisableOnDefault: false,
Help: "二次元图片标签识别\n" +
"- 鉴赏图片[图片]",
DisableOnDefault: false,
Brief: "二次元图片标签识别",
Help: "- 鉴赏图片[图片]",
PrivateDataFolder: "danbooru",
})
@@ -30,17 +31,21 @@ func init() { // 插件主体
Handle(func(ctx *zero.Ctx) {
ctx.SendChain(message.Text("少女祈祷中..."))
for _, url := range ctx.State["image_url"].([]string) {
t, err := danbooru.TagURL("", url)
t, st, err := tagurl("", url)
if err != nil {
ctx.SendChain(message.Text("ERROR:", err))
ctx.SendChain(message.Text("ERROR: ", err))
return
}
digest := md5.Sum(helper.StringToBytes(url))
f := cachefolder + hex.EncodeToString(digest[:])
if file.IsNotExist(f) {
_ = writer.SavePNG2Path(f, t)
_ = imgfactory.SavePNG2Path(f, t)
}
m := message.Message{ctxext.FakeSenderForwardNode(ctx, message.Image("file:///"+file.BOTPATH+"/"+f))}
m = append(m, ctxext.FakeSenderForwardNode(ctx, message.Text("tags: ", strings.Join(st.tseq, ","))))
if id := ctx.Send(m).ID(); id == 0 {
ctx.SendChain(message.Text("ERROR: 可能被风控或下载图片用时过长,请耐心等待"))
}
ctx.SendChain(message.Image("file:///" + file.BOTPATH + "/" + f))
}
})
}

119
plugin/danbooru/tag.go Normal file
View File

@@ -0,0 +1,119 @@
package deepdanbooru
import (
"bytes"
"encoding/json"
"fmt"
"image"
"net/url"
"sort"
"github.com/FloatTech/floatbox/file"
"github.com/FloatTech/floatbox/web"
"github.com/FloatTech/gg"
"github.com/FloatTech/imgfactory"
"github.com/FloatTech/zbputils/control"
"github.com/FloatTech/zbputils/img/text" // jpg png gif
_ "golang.org/x/image/webp" // webp
)
const api = "https://nsfwtag.azurewebsites.net/api/tag?limit=0.5&url="
type sorttags struct {
tags map[string]float64
tseq []string
}
func newsorttags(tags map[string]float64) (s *sorttags) {
t := make([]string, 0, len(tags))
for k := range tags {
t = append(t, k)
}
return &sorttags{tags: tags, tseq: t}
}
func (s *sorttags) Len() int {
return len(s.tags)
}
func (s *sorttags) Less(i, j int) bool {
v1 := s.tseq[i]
v2 := s.tseq[j]
return s.tags[v1] >= s.tags[v2]
}
// Swap swaps the elements with indexes i and j.
func (s *sorttags) Swap(i, j int) {
s.tseq[j], s.tseq[i] = s.tseq[i], s.tseq[j]
}
func tagurl(name, u string) (im image.Image, st *sorttags, err error) {
ch := make(chan []byte, 1)
go func() {
var data []byte
data, err = web.GetData(u)
ch <- data
}()
data, err := web.GetData(api + url.QueryEscape(u))
if err != nil {
return
}
tags := make(map[string]float64)
err = json.Unmarshal(data, &tags)
if err != nil {
return
}
longestlen := 0
for k := range tags {
if len(k) > longestlen {
longestlen = len(k)
}
}
longestlen++
st = newsorttags(tags)
sort.Sort(st)
boldfd, err := file.GetLazyData(text.BoldFontFile, control.Md5File, true)
if err != nil {
return
}
consfd, err := file.GetLazyData(text.ConsolasFontFile, control.Md5File, true)
if err != nil {
return
}
data = <-ch
if err != nil {
return
}
img, _, err := image.Decode(bytes.NewReader(data))
if err != nil {
return
}
img = imgfactory.Limit(img, 1280, 720)
canvas := gg.NewContext(img.Bounds().Size().X, img.Bounds().Size().Y+int(float64(img.Bounds().Size().X)*0.2)+len(tags)*img.Bounds().Size().X/25)
canvas.SetRGB(1, 1, 1)
canvas.Clear()
canvas.DrawImage(img, 0, 0)
if err = canvas.ParseFontFace(boldfd, float64(img.Bounds().Size().X)*0.1); err != nil {
return
}
canvas.SetRGB(0, 0, 0)
canvas.DrawString(name, float64(img.Bounds().Size().X)*0.02, float64(img.Bounds().Size().Y)+float64(img.Bounds().Size().X)*0.1)
i := float64(img.Bounds().Size().Y) + float64(img.Bounds().Size().X)*0.2
if err = canvas.ParseFontFace(consfd, float64(img.Bounds().Size().X)*0.04); err != nil {
return
}
rate := float64(img.Bounds().Size().X) * 0.04
for _, k := range st.tseq {
canvas.DrawString(fmt.Sprintf("* %-*s -%.3f-", longestlen, k, tags[k]), float64(img.Bounds().Size().X)*0.04, i)
i += rate
}
im = canvas.Image()
return
}

View File

@@ -5,28 +5,27 @@ import (
zero "github.com/wdvxdr1123/ZeroBot"
"github.com/wdvxdr1123/ZeroBot/message"
fcext "github.com/FloatTech/floatbox/ctxext"
ctrl "github.com/FloatTech/zbpctrl"
"github.com/FloatTech/zbputils/control"
"github.com/FloatTech/zbputils/ctxext"
"github.com/FloatTech/ZeroBot-Plugin/plugin/diana/data"
)
var engine = control.Register("diana", &ctrl.Options[*zero.Ctx]{
DisableOnDefault: false,
Help: "嘉然\n" +
"- 小作文\n" +
Brief: "嘉然相关", // 也许使用常用功能当Brief更好
Help: "- 小作文\n" +
"- 发大病\n" +
"- 教你一篇小作文[作文]\n" +
"- [回复]查重",
"- 教你一篇小作文[作文]",
PublicDataFolder: "Diana",
})
func init() {
getdb := ctxext.DoOnceOnSuccess(func(ctx *zero.Ctx) bool {
getdb := fcext.DoOnceOnSuccess(func(ctx *zero.Ctx) bool {
err := data.LoadText(engine.DataFolder() + "text.db")
if err != nil {
ctx.SendChain(message.Text("ERROR:", err))
ctx.SendChain(message.Text("ERROR: ", err))
return false
}
return true
@@ -49,7 +48,7 @@ func init() {
Handle(func(ctx *zero.Ctx) {
err := data.AddText(ctx.State["regex_matched"].([]string)[1])
if err != nil {
ctx.SendChain(message.Text("ERROR:", err))
ctx.SendChain(message.Text("ERROR: ", err))
} else {
ctx.SendChain(message.Text("记住啦!"))
}

View File

@@ -6,9 +6,10 @@ import (
"encoding/binary"
"time"
binutils "github.com/FloatTech/floatbox/binary"
"github.com/FloatTech/floatbox/file"
sql "github.com/FloatTech/sqlite"
binutils "github.com/FloatTech/zbputils/binary"
"github.com/FloatTech/zbputils/file"
"github.com/FloatTech/zbputils/control"
"github.com/sirupsen/logrus"
)
@@ -21,7 +22,7 @@ type text struct {
// LoadText 加载小作文
func LoadText(dbfile string) error {
_, err := file.GetLazyData(dbfile, false)
_, err := file.GetLazyData(dbfile, control.Md5File, false)
db.DBPath = dbfile
if err != nil {
return err

View File

@@ -1,89 +0,0 @@
// Package diana 嘉然相关
package diana
import (
"bytes"
"math"
"strings"
"time"
"github.com/FloatTech/zbputils/binary"
"github.com/FloatTech/zbputils/ctxext"
"github.com/FloatTech/zbputils/web"
"github.com/tidwall/gjson"
"github.com/wdvxdr1123/ZeroBot/message"
zero "github.com/wdvxdr1123/ZeroBot"
)
// 小作文查重: 回复要查的消息 查重
func init() {
engine.OnMessage(func(ctx *zero.Ctx) bool {
msg := ctx.Event.Message
if msg[0].Type != "reply" {
return false
}
for _, elem := range msg {
if elem.Type == "text" {
text := elem.Data["text"]
text = strings.ReplaceAll(text, " ", "")
text = strings.ReplaceAll(text, "\r", "")
text = strings.ReplaceAll(text, "\n", "")
if text == "查重" {
return true
}
}
}
return false
}).SetBlock(true).Limit(ctxext.LimitByUser).Handle(func(ctx *zero.Ctx) {
msg := ctx.GetMessage(message.NewMessageIDFromString(ctx.Event.Message[0].Data["id"])).Elements[0].Data["text"]
result, err := zhiwangapi(msg)
if err != nil {
ctx.SendChain(message.Text("ERROR:", err))
return
}
if result.Get("code").Int() != 0 {
ctx.SendChain(message.Text("api返回错误:", result.Get("code").Int()))
return
}
if result.Get("data.related.#").Int() == 0 {
ctx.Send(message.ReplyWithMessage(ctx.Event.MessageID, message.Text("枝网没搜到查重率为0%,鉴定为原创")))
return
}
related := result.Get("data.related.0.reply").Map()
rate := result.Get("data.related.0.rate").Float()
relatedcontent := related["content"].String()
if len(relatedcontent) > 102 {
relatedcontent = relatedcontent[:102] + "....."
}
ctx.Send(message.ReplyWithMessage(ctx.Event.MessageID, message.Text(
"枝网文本复制检测报告(简洁)", "\n",
"查重时间: ", time.Now().Format("2006-01-02 15:04:05"), "\n",
"总文字复制比: ", math.Floor(rate*100), "%", "\n",
"相似小作文:", "\n", relatedcontent, "\n",
"获赞数:", related["like_num"].String(), "\n",
result.Get("data.related.0.reply_url").String(), "\n",
"作者: ", related["m_name"].String(), "\n",
"发表时间: ", time.Unix(int64(related["ctime"].Float()), 0).Format("2006-01-02 15:04:05"), "\n",
"查重结果仅作参考,请注意辨别是否为原创", "\n",
"数据来源: https://asoulcnki.asia/",
)))
})
}
func zhiwangapi(text string) (*gjson.Result, error) {
b, cl := binary.OpenWriterF(func(w *binary.Writer) {
w.WriteString("{\n\"text\":\"")
w.WriteString(text)
w.WriteString("\"\n}")
})
data, err := web.PostData("https://asoulcnki.asia/v1/api/check", "application/json", bytes.NewReader(b))
cl()
if err != nil {
return nil, err
}
result := gjson.ParseBytes(data)
return &result, nil
}

39
plugin/dress/api.go Normal file
View File

@@ -0,0 +1,39 @@
package dress
import (
"fmt"
"github.com/FloatTech/floatbox/web"
"github.com/tidwall/gjson"
)
const (
dressURL = "http://www.yoooooooooo.com/gitdress"
male = "dress"
female = "girldress"
dressListURL = dressURL + "/%v/album/list.json"
dressDetailURL = dressURL + "/%v/album/%v/info.json"
dressImageURL = dressURL + "/%v/album/%v/%v-m.webp"
)
func dressList(sex string) (dressList []string, err error) {
data, err := web.GetData(fmt.Sprintf(dressListURL, sex))
if err != nil {
return
}
arr := gjson.ParseBytes(data).Get("@this").Array()
dressList = make([]string, len(arr))
for i, v := range arr {
dressList[i] = v.String()
}
return
}
func detail(sex, name string) (count int, err error) {
data, err := web.GetData(fmt.Sprintf(dressDetailURL, sex, name))
if err != nil {
return
}
count = int(gjson.ParseBytes(data).Get("@this.#").Int())
return
}

113
plugin/dress/dress.go Normal file
View File

@@ -0,0 +1,113 @@
// Package dress 女装
package dress
import (
"fmt"
"math/rand"
"strconv"
"strings"
"time"
"github.com/FloatTech/floatbox/binary"
ctrl "github.com/FloatTech/zbpctrl"
"github.com/FloatTech/zbputils/control"
"github.com/FloatTech/zbputils/ctxext"
"github.com/FloatTech/zbputils/img/text"
zero "github.com/wdvxdr1123/ZeroBot"
"github.com/wdvxdr1123/ZeroBot/message"
)
func init() { // 插件主体
engine := control.Register("dress", &ctrl.Options[*zero.Ctx]{
DisableOnDefault: false,
Brief: "女装",
Help: "女装\n" +
"- 女装\n" +
"- 男装\n" +
"- 随机女装\n" +
"- 随机男装",
PrivateDataFolder: "dress",
})
engine.OnFullMatchGroup([]string{"女装", "男装"}).SetBlock(true).
Handle(func(ctx *zero.Ctx) {
matched := ctx.State["matched"].(string)
sex := male
if matched == "男装" {
sex = female
}
next := zero.NewFutureEvent("message", 999, false, ctx.CheckSession())
recv, cancel := next.Repeat()
defer cancel()
nameList, err := dressList(sex)
if err != nil {
ctx.SendChain(message.Text("ERROR: ", err))
return
}
tex := "请输入" + matched + "序号\n"
for i, v := range nameList {
tex += fmt.Sprintf("%d. %s\n", i, v)
}
base64Str, err := text.RenderToBase64(tex, text.FontFile, 400, 20)
if err != nil {
ctx.SendChain(message.Text("ERROR: ", err))
return
}
ctx.SendChain(message.Image("base64://" + binary.BytesToString(base64Str)))
for {
select {
case <-time.After(time.Second * 120):
ctx.SendChain(message.Text(matched, "指令过期"))
return
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 >= len(nameList) {
ctx.SendChain(message.Text("序号非法!"))
continue
}
name := nameList[num]
sendImage(ctx, sex, matched, name)
return
}
}
})
engine.OnFullMatchGroup([]string{"随机女装", "随机男装"}).SetBlock(true).
Handle(func(ctx *zero.Ctx) {
matched := strings.TrimPrefix(ctx.State["matched"].(string), "随机")
sex := male
if matched == "男装" {
sex = female
}
nameList, err := dressList(sex)
if err != nil {
ctx.SendChain(message.Text("ERROR: ", err))
return
}
name := nameList[rand.Intn(len(nameList))]
sendImage(ctx, sex, matched, name)
})
}
func sendImage(ctx *zero.Ctx, sex, matched, name string) {
ctx.SendChain(message.Text("请欣赏", matched, ": ", name))
count, err := detail(sex, name)
if err != nil {
ctx.SendChain(message.Text("ERROR: ", err))
return
}
imageList := make([]string, count)
for i := range imageList {
imageList[i] = fmt.Sprintf(dressImageURL, sex, name, i+1)
}
m := message.Message{}
for _, v := range imageList {
m = append(m, ctxext.FakeSenderForwardNode(ctx, message.Image(v)))
}
if id := ctx.Send(m).ID(); id == 0 {
ctx.SendChain(message.Text("ERROR: 可能被风控或下载图片用时过长,请耐心等待"))
}
}

View File

@@ -1,53 +0,0 @@
package driftbottle
import (
"fmt"
"hash/crc64"
"strconv"
"sync"
sql "github.com/FloatTech/sqlite"
"github.com/FloatTech/zbputils/binary"
)
type bottle struct {
ID int64 `db:"id"` // ID qq_grp_name_msg 的 crc64
QQ int64 `db:"qq"` // QQ 发送者 qq
Grp int64 `db:"grp"` // Grp 限制抽出的群 / 人(负数)
Name string `db:"name"` // Name 发送者 昵称
Msg string `db:"msg"` // Msg 消息,纯文本
}
var sea = &sql.Sqlite{}
var seamu sync.RWMutex
func newBottle(qq, grp int64, name, msg string) *bottle {
id := int64(crc64.Checksum(binary.StringToBytes(fmt.Sprintf("%d_%d_%s_%s", qq, grp, name, msg)), crc64.MakeTable(crc64.ISO)))
return &bottle{ID: id, QQ: qq, Grp: grp, Name: name, Msg: msg}
}
func (b *bottle) throw(db *sql.Sqlite, channel string) error {
seamu.Lock()
defer seamu.Unlock()
return db.Insert(channel, b)
}
func (b *bottle) destroy(db *sql.Sqlite, channel string) error {
seamu.Lock()
defer seamu.Unlock()
return db.Del(channel, "WHERE id="+strconv.FormatInt(b.ID, 10))
}
// fetchBottle grp != 0
func fetchBottle(db *sql.Sqlite, channel string, grp int64) (*bottle, error) {
seamu.RLock()
defer seamu.RUnlock()
b := new(bottle)
return b, db.Find(channel, b, "WHERE grp=0 or grp="+strconv.FormatInt(grp, 10)+" ORDER BY RANDOM() limit 1")
}
func createChannel(db *sql.Sqlite, channel string) error {
seamu.Lock()
defer seamu.Unlock()
return db.Create(channel, &bottle{})
}

View File

@@ -2,132 +2,108 @@
package driftbottle
import (
"fmt"
"hash/crc64"
"strconv"
"strings"
"sync"
"time"
"unicode/utf8"
"github.com/FloatTech/floatbox/binary"
sql "github.com/FloatTech/sqlite"
ctrl "github.com/FloatTech/zbpctrl"
"github.com/FloatTech/zbputils/control"
"github.com/sirupsen/logrus"
"github.com/FloatTech/zbputils/ctxext"
zero "github.com/wdvxdr1123/ZeroBot"
"github.com/wdvxdr1123/ZeroBot/message"
)
type sea struct {
ID int64 `db:"id"` // ID qq_grp_name_msg 的 crc64 hashCheck.
QQ int64 `db:"qq"` // Get current user(Who sends this)
Name string `db:"Name"` // his or her name at that time:P
Msg string `db:"msg"` // What he or she sent to bot?
Grp int64 `db:"grp"` // which group sends this msg?
Time string `db:"time"` // we need to know the current time,master>
}
var seaSide = &sql.Sqlite{}
var seaLocker sync.RWMutex
// We need a container to inject what we need :(
func init() {
en := control.Register("driftbottle", &ctrl.Options[*zero.Ctx]{
DisableOnDefault: false,
Help: "漂流瓶\n- (在群xxx)丢漂流瓶(到频道xxx) [消息]\n- (从频道xxx)捡漂流瓶\n- @BOT 创建频道 xxx\n- 跳入(频道)海中\n- 注:不显式限制时,私聊发送可在所有群抽到,群聊发送仅可在本群抽到,默认频道为 global",
Brief: "漂流瓶",
Help: "- @bot pick" + "- @bot throw xxx (xxx为投递内容)",
PrivateDataFolder: "driftbottle",
})
sea.DBPath = en.DataFolder() + "sea.db"
err := sea.Open(time.Hour * 24)
seaSide.DBPath = en.DataFolder() + "sea.db"
err := seaSide.Open(time.Hour * 24)
if err != nil {
panic(err)
}
_ = createChannel(sea, "global")
en.OnRegex(`^(在群\d+)?丢漂流瓶(到频道\w+)?\s+(.*)$`).SetBlock(true).
Handle(func(ctx *zero.Ctx) {
msgs := ctx.State["regex_matched"].([]string)
grp := ctx.Event.GroupID
channel := "global"
msg := msgs[3]
var err error
if msgs[1] != "" {
grp, err = strconv.ParseInt(msgs[1][6:], 10, 64)
if err != nil {
ctx.SendChain(message.Text("群号非法!"))
return
}
}
if msgs[2] != "" {
channel = msgs[2][9:]
}
if msg == "" {
ctx.SendChain(message.Text("消息为空!"))
return
}
logrus.Debugln("[driftbottle]", grp, channel, msg)
err = newBottle(
ctx.Event.UserID,
grp,
ctx.CardOrNickName(ctx.Event.UserID),
msg,
).throw(sea, channel)
if err != nil {
ctx.SendChain(message.Text("ERROR:", err))
return
}
ctx.Send(message.ReplyWithMessage(ctx.Event.MessageID, message.Text("你将它扔进大海,希望有人捞到吧~")))
})
en.OnRegex(`^(从频道\w+)?捡漂流瓶$`).SetBlock(true).
Handle(func(ctx *zero.Ctx) {
msgs := ctx.State["regex_matched"].([]string)
grp := ctx.Event.GroupID
if grp == 0 {
grp = -ctx.Event.UserID
}
if grp == 0 {
ctx.SendChain(message.Text("找不到对象!"))
return
}
channel := "global"
if msgs[1] != "" {
channel = msgs[1][9:]
}
logrus.Debugln("[driftbottle]", grp, channel)
b, err := fetchBottle(sea, channel, grp)
if err != nil {
ctx.SendChain(message.Text("ERROR:", err))
return
}
var wg sync.WaitGroup
wg.Add(1)
go func() {
err = b.destroy(sea, channel)
wg.Done()
}()
ctx.Send(
message.ReplyWithMessage(
ctx.Event.MessageID,
message.Text("你在海边捡到了一个来自 ", b.Name, " 的漂流瓶,打开瓶子,里面有一张纸条,写着:"),
message.Text(b.Msg),
),
)
wg.Wait()
if err != nil {
ctx.SendChain(message.Text("ERROR:", err))
return
}
})
en.OnPrefix("创建频道", zero.SuperUserPermission, zero.OnlyToMe).SetBlock(true).
Handle(func(ctx *zero.Ctx) {
channel := strings.TrimRight(ctx.State["args"].(string), " ")
if channel == "" {
ctx.SendChain(message.Text("频道名为空!"))
return
}
err := createChannel(sea, channel)
if err != nil {
ctx.SendChain(message.Text("ERROR:", err))
return
}
ctx.Send(message.ReplyWithMessage(ctx.Event.MessageID, message.Text("成功~")))
})
en.OnRegex(`^跳入(\w+)?海中$`).SetBlock(true).
Handle(func(ctx *zero.Ctx) {
msgs := ctx.State["regex_matched"].([]string)
channel := "global"
if msgs[1] != "" {
channel = msgs[1]
}
seamu.RLock()
c, err := sea.Count(channel)
seamu.RUnlock()
if err != nil {
ctx.SendChain(message.Text("ERROR:", err))
return
}
ctx.Send(message.ReplyWithMessage(ctx.Event.MessageID, message.Text("你缓缓走入大海,感受着海浪轻柔地拍打着你的小腿,膝盖……\n波浪卷着你的腰腹你感觉有些把握不住平衡了……\n……\n你沉入海中", c, " 个物体与你一同沉浮。\n不知何处涌来一股暗流你失去了意识。")))
})
_ = createChannel(seaSide)
en.OnFullMatch("pick", zero.OnlyToMe, zero.OnlyGroup).Limit(ctxext.LimitByGroup).SetBlock(true).Handle(func(ctx *zero.Ctx) {
be, err := fetchBottle(seaSide)
if err != nil {
ctx.SendChain(message.Text("ERR:", err))
}
idstr := strconv.Itoa(int(be.ID))
qqstr := strconv.Itoa(int(be.QQ))
grpstr := strconv.Itoa(int(be.Grp))
botname := zero.BotConfig.NickName[0]
msg := message.Message{message.CustomNode(botname, ctx.Event.SelfID, botname+"试着帮你捞出来了这个~\nID:"+idstr+"\n投递人: "+be.Name+"("+qqstr+")"+"\n群号: "+grpstr+"\n时间: "+be.Time+"\n内容: \n"+be.Msg)}
ctx.Send(msg)
})
en.OnRegex(`throw.*?(.*)`, zero.OnlyToMe, zero.OnlyGroup).SetBlock(true).Handle(func(ctx *zero.Ctx) {
senderFormatTime := time.Unix(ctx.Event.Time, 0).Format("2006-01-02 15:04:05")
rawSenderMessage := ctx.State["regex_matched"].([]string)[1]
rawMessageCallBack := message.UnescapeCQCodeText(rawSenderMessage)
keyWordsNum := utf8.RuneCountInString(rawMessageCallBack)
if keyWordsNum < 10 {
ctx.SendChain(message.Reply(ctx.Event.MessageID), message.Text("需要投递的内容过少( "))
return
}
// check current needs and prepare to throw drift_bottle.
err = globalbottle(
ctx.Event.UserID,
ctx.Event.GroupID,
senderFormatTime,
ctx.CardOrNickName(ctx.Event.UserID),
rawMessageCallBack,
).throw(seaSide)
if err != nil {
ctx.SendChain(message.Text("ERROR: ", err))
return
}
ctx.Send(message.ReplyWithMessage(ctx.Event.MessageID, message.Text("已经帮你丢出去了哦~")))
})
}
func globalbottle(qq, grp int64, time, name, msg string) *sea { // Check as if the User is available and collect information to store.
id := int64(crc64.Checksum(binary.StringToBytes(fmt.Sprintf("%d_%d_%s_%s_%s", grp, qq, time, name, msg)), crc64.MakeTable(crc64.ISO)))
return &sea{ID: id, Grp: grp, Time: time, QQ: qq, Name: name, Msg: msg}
}
func (be *sea) throw(db *sql.Sqlite) error {
seaLocker.Lock()
defer seaLocker.Unlock()
return db.Insert("global", be)
}
func fetchBottle(db *sql.Sqlite) (*sea, error) {
seaLocker.Lock()
defer seaLocker.Unlock()
be := new(sea)
return be, db.Pick("global", be)
}
func createChannel(db *sql.Sqlite) error {
seaLocker.Lock()
defer seaLocker.Unlock()
return db.Create("global", &sea{})
}

View File

@@ -19,8 +19,8 @@ const bed = "https://www.gstatic.com/android/keyboard/emojikitchen/%d/u%x/u%x_u%
func init() {
control.Register("emojimix", &ctrl.Options[*zero.Ctx]{
DisableOnDefault: false,
Help: "合成emoji\n" +
"- [emoji][emoji]",
Brief: "合成emoji",
Help: "- [emoji][emoji]",
}).OnMessage(match).SetBlock(true).Limit(ctxext.LimitByUser).
Handle(func(ctx *zero.Ctx) {
r := ctx.State["emojimix"].([]rune)

View File

@@ -1,123 +0,0 @@
// Package epidemic 城市疫情查询
package epidemic
import (
"encoding/json"
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"
"github.com/FloatTech/zbputils/web"
)
const (
servicename = "epidemic"
txurl = "https://api.inews.qq.com/newsqa/v1/query/inner/publish/modules/list?modules=statisGradeCityDetail,diseaseh5Shelf"
)
// result 疫情查询结果
type result struct {
Data struct {
Epidemic epidemic `json:"diseaseh5Shelf"`
} `json:"data"`
}
// epidemic 疫情数据
type epidemic struct {
LastUpdateTime string `json:"lastUpdateTime"`
AreaTree []*area `json:"areaTree"`
}
// area 城市疫情数据
type area struct {
Name string `json:"name"`
Today struct {
Confirm int `json:"confirm"`
Wzzadd interface{} `json:"wzz_add"`
} `json:"today"`
Total struct {
NowConfirm int `json:"nowConfirm"`
Confirm int `json:"confirm"`
Dead int `json:"dead"`
Heal int `json:"heal"`
Grade string `json:"grade"`
Wzz int `json:"wzz"`
} `json:"total"`
Children []*area `json:"children"`
}
func init() {
engine := control.Register(servicename, &ctrl.Options[*zero.Ctx]{
DisableOnDefault: false,
Help: "城市疫情查询\n" +
"- xxx疫情\n",
})
engine.OnSuffix("疫情").SetBlock(true).Limit(ctxext.LimitByUser).
Handle(func(ctx *zero.Ctx) {
city := ctx.State["args"].(string)
if city == "" {
ctx.SendChain(message.Text("你还没有输入城市名字呢!"))
return
}
data, time, err := queryEpidemic(city)
if err != nil {
ctx.SendChain(message.Text("ERROR:", err))
return
}
if data == nil {
ctx.SendChain(message.Text("没有找到【", city, "】城市的疫情数据."))
return
}
ctx.SendChain(
message.Text(
"【", data.Name, "】疫情数据\n",
"新增人数:", data.Today.Confirm, "\n",
"现有确诊:", data.Total.NowConfirm, "\n",
"累计确诊:", data.Total.Confirm, "\n",
"治愈人数:", data.Total.Heal, "\n",
"死亡人数:", data.Total.Dead, "\n",
"无症状人数:", data.Total.Wzz, "\n",
"新增无症状:", data.Today.Wzzadd, "\n",
"更新时间:\n『", time, "』",
),
)
})
}
// rcity 查找城市
func rcity(a *area, cityName string) *area {
if a == nil {
return nil
}
if a.Name == cityName {
return a
}
for _, v := range a.Children {
if v.Name == cityName {
return v
}
c := rcity(v, cityName)
if c != nil {
return c
}
}
return nil
}
// queryEpidemic 查询城市疫情
func queryEpidemic(findCityName string) (citydata *area, times string, err error) {
data, err := web.GetData(txurl)
if err != nil {
return
}
var r result
err = json.Unmarshal(data, &r)
if err != nil {
return
}
citydata = rcity(r.Data.Epidemic.AreaTree[0], findCityName)
return citydata, r.Data.Epidemic.LastUpdateTime, nil
}

45
plugin/event/data.go Normal file
View File

@@ -0,0 +1,45 @@
package event
type storage int64
// 申请
func (s *storage) setapply(on bool) {
if on {
*s |= 0b001
} else {
*s &= 0b110
}
}
// 邀请
func (s *storage) setinvite(on bool) {
if on {
*s |= 0b010
} else {
*s &= 0b101
}
}
// 主人
func (s *storage) setmaster(on bool) {
if on {
*s |= 0b100
} else {
*s &= 0b011
}
}
// 申请
func (s *storage) isapplyon() bool {
return *s&0b001 > 0
}
// 邀请
func (s *storage) isinviteon() bool {
return *s&0b010 > 0
}
// 主人
func (s *storage) ismasteroff() bool {
return *s&0b100 > 0
}

144
plugin/event/event.go Normal file
View File

@@ -0,0 +1,144 @@
// Package event 好友申请以及群聊邀请事件处理
package event
import (
"encoding/binary"
"strconv"
"time"
ctrl "github.com/FloatTech/zbpctrl"
"github.com/FloatTech/zbputils/control"
base14 "github.com/fumiama/go-base16384"
"github.com/sirupsen/logrus"
zero "github.com/wdvxdr1123/ZeroBot"
"github.com/wdvxdr1123/ZeroBot/message"
)
func init() {
engine := control.Register("event", &ctrl.Options[*zero.Ctx]{
DisableOnDefault: false,
Brief: "好友申请和群聊邀请事件处理",
Help: "- [开启|关闭]自动同意[申请|邀请|主人]\n" +
"- [同意|拒绝][申请|邀请][flag]\n" +
"Tips: 信息默认发送给主人列表第一位, 默认同意所有主人的事件, flag跟随事件一起发送",
})
engine.On("request/group/invite").SetBlock(false).
Handle(func(ctx *zero.Ctx) {
c, ok := ctx.State["manager"].(*ctrl.Control[*zero.Ctx])
if ok {
su := zero.BotConfig.SuperUsers[0]
now := time.Unix(ctx.Event.Time, 0).Format("2006-01-02 15:04:05")
flag, err := strconv.ParseInt(ctx.Event.Flag, 10, 64)
if err != nil {
ctx.SendChain(message.Text("ERROR: ", err))
return
}
var buf [8]byte
binary.BigEndian.PutUint64(buf[:], uint64(flag))
es := base14.EncodeToString(buf[1:])
userid := ctx.Event.UserID
username := ctx.CardOrNickName(userid)
data := (storage)(c.GetData(-su))
groupid := ctx.Event.GroupID
groupname := ctx.GetGroupInfo(groupid, true).Name
logrus.Info("[event]收到来自[", username, "](", userid, ")的群聊邀请,群:[", groupname, "](", groupid, ")")
if data.isinviteon() || (!data.ismasteroff() && zero.SuperUserPermission(ctx)) {
ctx.SetGroupAddRequest(ctx.Event.Flag, "invite", true, "")
ctx.SendPrivateForwardMessage(su, message.Message{message.CustomNode(username, userid,
"已自动同意在"+now+"收到来自"+
"\n用户:["+username+"]("+strconv.FormatInt(userid, 10)+")的群聊邀请"+
"\n群聊:["+groupname+"]("+strconv.FormatInt(groupid, 10)+")"+
"\nflag:"+es)})
return
}
ctx.SendPrivateForwardMessage(su,
message.Message{message.CustomNode(username, userid,
"在"+now+"收到来自"+
"\n用户:["+username+"]("+strconv.FormatInt(userid, 10)+")的群聊邀请"+
"\n群聊:["+groupname+"]("+strconv.FormatInt(groupid, 10)+")"+
"\n请在下方复制flag并在前面加上:"+
"\n同意/拒绝邀请,来决定同意还是拒绝"),
message.CustomNode(username, userid, es)})
}
})
engine.On("request/friend").SetBlock(false).
Handle(func(ctx *zero.Ctx) {
c, ok := ctx.State["manager"].(*ctrl.Control[*zero.Ctx])
if ok {
su := zero.BotConfig.SuperUsers[0]
now := time.Unix(ctx.Event.Time, 0).Format("2006-01-02 15:04:05")
flag, err := strconv.ParseInt(ctx.Event.Flag, 10, 64)
if err != nil {
ctx.SendChain(message.Text("ERROR: ", err))
return
}
var buf [8]byte
binary.BigEndian.PutUint64(buf[:], uint64(flag))
es := base14.EncodeToString(buf[1:])
comment := ctx.Event.Comment
userid := ctx.Event.UserID
username := ctx.CardOrNickName(userid)
data := (storage)(c.GetData(-su))
logrus.Info("[event]收到来自[", username, "](", userid, ")的好友申请")
if data.isapplyon() || (!data.ismasteroff() && zero.SuperUserPermission(ctx)) {
ctx.SetFriendAddRequest(ctx.Event.Flag, true, "")
ctx.SendPrivateForwardMessage(su, message.Message{message.CustomNode(username, userid,
"已自动同意在"+now+"收到来自"+
"\n用户:["+username+"]("+strconv.FormatInt(userid, 10)+")"+
"\n的好友请求:"+comment+
"\nflag:"+es)})
return
}
ctx.SendPrivateForwardMessage(su,
message.Message{message.CustomNode(username, userid,
"在"+now+"收到来自"+
"\n用户:["+username+"]("+strconv.FormatInt(userid, 10)+")"+
"\n的好友请求:"+comment+
"\n请在下方复制flag并在前面加上:"+
"\n同意/拒绝申请,来决定同意还是拒绝"),
message.CustomNode(username, userid, es)})
}
})
engine.OnRegex(`^(同意|拒绝)(申请|邀请)\s*([一-踀]{4})\s*(.*)$`, zero.SuperUserPermission, zero.OnlyPrivate).SetBlock(true).
Handle(func(ctx *zero.Ctx) {
su := zero.BotConfig.SuperUsers[0]
cmd := ctx.State["regex_matched"].([]string)[1]
org := ctx.State["regex_matched"].([]string)[2]
es := ctx.State["regex_matched"].([]string)[3]
other := ctx.State["regex_matched"].([]string)[4]
var buf [8]byte
copy(buf[1:], base14.DecodeFromString(es))
flag := strconv.FormatInt(int64(binary.BigEndian.Uint64(buf[:])), 10)
ok := cmd == "同意"
switch org {
case "申请":
ctx.SetFriendAddRequest(flag, ok, other)
ctx.SendPrivateMessage(su, message.Text("已", cmd, org))
case "邀请":
ctx.SetGroupAddRequest(flag, "invite", ok, other)
ctx.SendPrivateMessage(su, message.Text("已", cmd, org))
}
})
engine.OnRegex(`^(开启|关闭)自动同意(申请|邀请|主人)$`, zero.SuperUserPermission, zero.OnlyPrivate).SetBlock(true).
Handle(func(ctx *zero.Ctx) {
c := ctx.State["manager"].(*ctrl.Control[*zero.Ctx])
su := zero.BotConfig.SuperUsers[0]
option := ctx.State["regex_matched"].([]string)[1]
from := ctx.State["regex_matched"].([]string)[2]
data := (storage)(c.GetData(-su))
switch from {
case "申请":
data.setapply(option == "开启")
case "邀请":
data.setinvite(option == "开启")
case "主人":
data.setmaster(option == "关闭")
}
err := c.SetData(-su, int64(data))
if err != nil {
ctx.SendChain(message.Text("ERROR: ", err))
return
}
ctx.SendChain(message.Text("已设置自动同意" + from + "为" + option))
})
}

View File

@@ -2,8 +2,8 @@
package font
import (
"github.com/FloatTech/floatbox/binary"
ctrl "github.com/FloatTech/zbpctrl"
"github.com/FloatTech/zbputils/binary"
"github.com/FloatTech/zbputils/control"
"github.com/FloatTech/zbputils/ctxext"
"github.com/FloatTech/zbputils/img/text"
@@ -14,7 +14,8 @@ import (
func init() {
control.Register("font", &ctrl.Options[*zero.Ctx]{
DisableOnDefault: false,
Help: "渲染任意文字到图片\n- (用[终末体|终末变体|紫罗兰体|樱酥体|Consolas体|苹方体])渲染文字xxx",
Brief: "渲染任意文字到图片",
Help: "- (用[字体])渲染文字xxx\n可选字体: [终末体|终末变体|紫罗兰体|樱酥体|Consolas体|苹方体]",
}).OnRegex(`^(用.+)?渲染文字([\s\S]+)$`).SetBlock(true).Limit(ctxext.LimitByUser).Handle(func(ctx *zero.Ctx) {
fnt := ctx.State["regex_matched"].([]string)[1]
txt := ctx.State["regex_matched"].([]string)[2]
@@ -36,7 +37,7 @@ func init() {
}
b, err := text.RenderToBase64(txt, fnt, 400, 20)
if err != nil {
ctx.SendChain(message.Text("ERROR:", err))
ctx.SendChain(message.Text("ERROR: ", err))
return
}
ctx.SendChain(message.Image("base64://" + binary.BytesToString(b)))

View File

@@ -11,19 +11,20 @@ import (
"os"
"strconv"
"github.com/fogleman/gg" // 注册了 jpg png gif
"github.com/FloatTech/gg" // 注册了 jpg png gif
"github.com/FloatTech/imgfactory"
"github.com/sirupsen/logrus"
zero "github.com/wdvxdr1123/ZeroBot"
"github.com/wdvxdr1123/ZeroBot/message"
"github.com/wdvxdr1123/ZeroBot/utils/helper"
fcext "github.com/FloatTech/floatbox/ctxext"
"github.com/FloatTech/floatbox/file"
"github.com/FloatTech/floatbox/math"
ctrl "github.com/FloatTech/zbpctrl"
"github.com/FloatTech/zbputils/control"
"github.com/FloatTech/zbputils/ctxext"
"github.com/FloatTech/zbputils/file"
"github.com/FloatTech/zbputils/img/pool"
"github.com/FloatTech/zbputils/img/writer"
"github.com/FloatTech/zbputils/math"
)
const (
@@ -31,15 +32,13 @@ const (
images = "data/Fortune/"
// 基础文件位置
omikujson = "data/Fortune/text.json"
// 字体文件位置
font = "data/Font/sakura.ttf"
// 生成图缓存位置
cache = images + "cache/"
)
var (
// 底图类型列表
table = [...]string{"车万", "DC4", "爱因斯坦", "星空列车", "樱云之恋", "富婆妹", "李清歌", "公主连结", "原神", "明日方舟", "碧蓝航线", "碧蓝幻想", "战双", "阴阳师", "赛马娘", "东方归言录", "奇异恩典", "夏日口袋", "ASoul"}
table = [...]string{"车万", "DC4", "爱因斯坦", "星空列车", "樱云之恋", "富婆妹", "李清歌", "公主连结", "原神", "明日方舟", "碧蓝航线", "碧蓝幻想", "战双", "阴阳师", "赛马娘", "东方归言录", "奇异恩典", "夏日口袋", "ASoul", "Hololive"}
// 映射底图与 index
index = make(map[string]uint8)
// 签文
@@ -50,9 +49,9 @@ func init() {
// 插件主体
en := control.Register("fortune", &ctrl.Options[*zero.Ctx]{
DisableOnDefault: false,
Help: "每日运势: \n" +
"- 运势 | 抽签\n" +
"- 设置底图[车万 | DC4 | 爱因斯坦 | 星空列车 | 樱云之恋 | 富婆妹 | 李清歌 | 公主连结 | 原神 | 明日方舟 | 碧蓝航线 | 碧蓝幻想 | 战双 | 阴阳师 | 赛马娘 | 东方归言录 | 奇异恩典 | 夏日口袋 | ASoul]",
Brief: "每日运势",
Help: "- 运势 | 抽签\n" +
"- 设置底图[车万 | DC4 | 爱因斯坦 | 星空列车 | 樱云之恋 | 富婆妹 | 李清歌 | 公主连结 | 原神 | 明日方舟 | 碧蓝航线 | 碧蓝幻想 | 战双 | 阴阳师 | 赛马娘 | 东方归言录 | 奇异恩典 | 夏日口袋 | ASoul | Hololive]",
PublicDataFolder: "Fortune",
})
_ = os.RemoveAll(cache)
@@ -87,23 +86,24 @@ func init() {
}
ctx.SendChain(message.Text("没有这个底图哦~"))
})
en.OnFullMatchGroup([]string{"运势", "抽签"}, ctxext.DoOnceOnSuccess(
en.OnFullMatchGroup([]string{"运势", "抽签"}, fcext.DoOnceOnSuccess(
func(ctx *zero.Ctx) bool {
data, err := file.GetLazyData(omikujson, false)
data, err := file.GetLazyData(omikujson, control.Md5File, false)
if err != nil {
ctx.SendChain(message.Text("ERROR:", err))
ctx.SendChain(message.Text("ERROR: ", err))
return false
}
err = json.Unmarshal(data, &omikujis)
if err != nil {
ctx.SendChain(message.Text("ERROR:", err))
ctx.SendChain(message.Text("ERROR: ", err))
return false
}
_, err = file.GetLazyData(font, true)
fontdata, err := file.GetLazyData("data/Font/sakura.ttf", control.Md5File, true)
if err != nil {
ctx.SendChain(message.Text("ERROR:", err))
ctx.SendChain(message.Text("ERROR: ", err))
return false
}
ctx.State["fontdata"] = fontdata
return true
},
)).Limit(ctxext.LimitByGroup).SetBlock(true).
@@ -125,21 +125,21 @@ func init() {
}
// 检查背景图片是否存在
zipfile := images + kind + ".zip"
_, err := file.GetLazyData(zipfile, false)
_, err := file.GetLazyData(zipfile, control.Md5File, false)
if err != nil {
ctx.SendChain(message.Text("ERROR:", err))
ctx.SendChain(message.Text("ERROR: ", err))
return
}
// 随机获取背景
background, index, err := randimage(zipfile, ctx)
if err != nil {
ctx.SendChain(message.Text("ERROR:", err))
ctx.SendChain(message.Text("ERROR: ", err))
return
}
// 随机获取签文
randtextindex := ctxext.RandSenderPerDayN(ctx.Event.UserID, len(omikujis))
randtextindex := fcext.RandSenderPerDayN(ctx.Event.UserID, len(omikujis))
title, text := omikujis[randtextindex]["title"], omikujis[randtextindex]["content"]
digest := md5.Sum(helper.StringToBytes(zipfile + strconv.Itoa(index) + title + text))
cachefile := cache + hex.EncodeToString(digest[:])
@@ -149,12 +149,12 @@ func init() {
if err != nil {
return err
}
_, err = draw(background, title, text, f)
_, err = draw(background, ctx.State["fontdata"].([]byte), title, text, f)
_ = f.Close()
return err
}, ctxext.Send(ctx), ctxext.GetMessage(ctx))
if err != nil {
ctx.SendChain(message.Text("ERROR:", err))
ctx.SendChain(message.Text("ERROR: ", err))
return
}
})
@@ -171,7 +171,7 @@ func randimage(path string, ctx *zero.Ctx) (im image.Image, index int, err error
}
defer reader.Close()
file := reader.File[ctxext.RandSenderPerDayN(ctx.Event.UserID, len(reader.File))]
file := reader.File[fcext.RandSenderPerDayN(ctx.Event.UserID, len(reader.File))]
f, err := file.Open()
if err != nil {
return
@@ -188,19 +188,19 @@ func randimage(path string, ctx *zero.Ctx) (im image.Image, index int, err error
// @param title 签名
// @param text 签文
// @return 错误信息
func draw(back image.Image, title, txt string, f io.Writer) (int64, error) {
func draw(back image.Image, fontdata []byte, title, txt string, f io.Writer) (int64, error) {
canvas := gg.NewContext(back.Bounds().Size().Y, back.Bounds().Size().X)
canvas.DrawImage(back, 0, 0)
// 写标题
canvas.SetRGB(1, 1, 1)
if err := canvas.LoadFontFace(font, 45); err != nil {
if err := canvas.ParseFontFace(fontdata, 45); err != nil {
return -1, err
}
sw, _ := canvas.MeasureString(title)
canvas.DrawString(title, 140-sw/2, 112)
// 写正文
canvas.SetRGB(0, 0, 0)
if err := canvas.LoadFontFace(font, 23); err != nil {
if err := canvas.ParseFontFace(fontdata, 23); err != nil {
return -1, err
}
tw, th := canvas.MeasureString("测")
@@ -229,7 +229,7 @@ func draw(back image.Image, title, txt string, f io.Writer) (int64, error) {
}
}
}
return writer.WriteTo(canvas.Image(), f)
return imgfactory.WriteTo(canvas.Image(), f)
}
func offest(total, now int, distance float64) float64 {

View File

@@ -9,6 +9,7 @@ import (
zero "github.com/wdvxdr1123/ZeroBot"
"github.com/wdvxdr1123/ZeroBot/message"
fcext "github.com/FloatTech/floatbox/ctxext"
sql "github.com/FloatTech/sqlite"
ctrl "github.com/FloatTech/zbpctrl"
"github.com/FloatTech/zbputils/control"
@@ -25,31 +26,31 @@ var db = &sql.Sqlite{}
func init() {
en := control.Register("funny", &ctrl.Options[*zero.Ctx]{
DisableOnDefault: false,
Help: "讲个笑话\n" +
"- 讲个笑话[@xxx|qq号|人名] | 夸夸[@xxx|qq号|人名] ",
Brief: "讲个笑话",
Help: "- 讲个笑话[@xxx|qq号|人名] | 夸夸[@xxx|qq号|人名] ",
PublicDataFolder: "Funny",
})
en.OnPrefixGroup([]string{"讲个笑话", "夸夸"}, ctxext.DoOnceOnSuccess(func(ctx *zero.Ctx) bool {
en.OnPrefixGroup([]string{"讲个笑话", "夸夸"}, fcext.DoOnceOnSuccess(func(ctx *zero.Ctx) bool {
db.DBPath = en.DataFolder() + "jokes.db"
_, err := en.GetLazyData("jokes.db", true)
if err != nil {
ctx.SendChain(message.Text("ERROR:", err))
ctx.SendChain(message.Text("ERROR: ", err))
return false
}
err = db.Open(time.Hour * 24)
if err != nil {
ctx.SendChain(message.Text("ERROR:", err))
ctx.SendChain(message.Text("ERROR: ", err))
return false
}
err = db.Create("jokes", &joke{})
if err != nil {
ctx.SendChain(message.Text("ERROR:", err))
ctx.SendChain(message.Text("ERROR: ", err))
return false
}
c, err := db.Count("jokes")
if err != nil {
ctx.SendChain(message.Text("ERROR:", err))
ctx.SendChain(message.Text("ERROR: ", err))
return false
}
logrus.Infoln("[funny]加载", c, "个笑话")
@@ -60,7 +61,7 @@ func init() {
var j joke
err := db.Pick("jokes", &j)
if err != nil {
ctx.SendChain(message.Text("ERROR:", err))
ctx.SendChain(message.Text("ERROR: ", err))
return
}
ctx.SendChain(message.Text(strings.ReplaceAll(j.Text, "%name", name)))

View File

@@ -13,11 +13,12 @@ import (
"strings"
"sync/atomic"
fcext "github.com/FloatTech/floatbox/ctxext"
"github.com/FloatTech/floatbox/process"
"github.com/FloatTech/imgfactory"
ctrl "github.com/FloatTech/zbpctrl"
"github.com/FloatTech/zbputils/control"
"github.com/FloatTech/zbputils/ctxext"
"github.com/FloatTech/zbputils/img/writer"
"github.com/FloatTech/zbputils/process"
"github.com/golang/freetype"
"github.com/sirupsen/logrus"
zero "github.com/wdvxdr1123/ZeroBot"
@@ -36,7 +37,8 @@ var (
func init() {
engine := control.Register("genshin", &ctrl.Options[*zero.Ctx]{
DisableOnDefault: false,
Help: "原神抽卡\n- 原神十连\n- 切换原神卡池",
Brief: "原神模拟抽卡",
Help: "- 原神十连\n- 切换原神卡池",
PublicDataFolder: "Genshin",
}).ApplySingle(ctxext.DefaultSingle)
@@ -62,21 +64,21 @@ func init() {
err := c.SetData(gid, int64(store))
if err != nil {
process.SleepAbout1sTo2s()
ctx.SendChain(message.Text("ERROR:", err))
ctx.SendChain(message.Text("ERROR: ", err))
}
})
engine.OnFullMatch("原神十连", ctxext.DoOnceOnSuccess(
engine.OnFullMatch("原神十连", fcext.DoOnceOnSuccess(
func(ctx *zero.Ctx) bool {
zipfile := engine.DataFolder() + "Genshin.zip"
_, err := engine.GetLazyData("Genshin.zip", false)
if err != nil {
ctx.SendChain(message.Text("ERROR:", err))
ctx.SendChain(message.Text("ERROR: ", err))
return false
}
err = parsezip(zipfile)
if err != nil {
ctx.SendChain(message.Text("ERROR:", err))
ctx.SendChain(message.Text("ERROR: ", err))
return false
}
return true
@@ -95,10 +97,14 @@ func init() {
store := (storage)(c.GetData(gid))
img, str, mode, err := randnums(10, store)
if err != nil {
ctx.SendChain(message.Text("ERROR:", err))
ctx.SendChain(message.Text("ERROR: ", err))
return
}
b, err := imgfactory.ToBytes(img)
if err != nil {
ctx.SendChain(message.Text("ERROR: ", err))
return
}
b, cl := writer.ToBytes(img)
if mode {
ctx.Send(message.ReplyWithMessage(ctx.Event.MessageID,
message.Text("恭喜你抽到了: \n", str), message.ImageBytes(b)))
@@ -106,7 +112,6 @@ func init() {
ctx.Send(message.ReplyWithMessage(ctx.Event.MessageID,
message.Text("十连成功~"), message.ImageBytes(b)))
}
cl()
})
}

View File

@@ -1,6 +1,6 @@
# ZeroBot-Plugin-Gif
[ZeroBot QQ机器人](https://github.com/wdvxdr1123/ZeroBot)插件可以制作各种沙雕gif图
> 素材包地址: https://gitcode.net/u011570312/imagematerials
> 素材包地址: https://gitcode.net/anto_july/imagematerials
## 触发方式
1. [指令词]+[qq号] 如爬123456
@@ -29,3 +29,75 @@
- [x] 浮雕
- [x] 打码
- [x] 负片
- [x] 旋转45
- [x] 变形100 100
- [x]
- [x] 娶|结婚申请|结婚登记
- [x] 像只
- [x] 阿尼亚喜欢
- [x] 我永远喜欢|永远喜欢
- [x] 像样的亲亲
- [x] 国旗
- [x] 不要靠近
- [x] 万能表情|空白表情
- [x] 采访
- [x] 需要|你可能需要
- [x] 这像画吗
- [x] 小画家
- [x] 完美
- [x] 玩游戏 (应该使用透视变换)
- [x] 出警
- [x] 警察
- [x] 舔|舔屏|prpr (应该使用透视变换)
- [x] 安全感
- [x] 精神支柱
- [x] 想什么
- [x] 墙纸
- [x] 为什么at我
- [x] 交个朋友
- [x] 打工人|继续干活
- [x] 兑换券
- [ ] 捂脸 (使用了透视变换, 需要研究矩阵变换)
- [x] 注意力涣散
- [x] 垃圾桶|垃圾
- [x]
- [x] 啾啾
- [x] 2敲
- [x] 听音乐
- [ ] 群青 (需要mask)
- [ ] 加载中 (需要mask)
- [x] 永远爱你 (未加闪光)
- [ ] 关注 (处理文字麻烦)
- [x] 2拍
- [x]
- [x]
- [x] 打拳 (未加闪光)
- [ ] 复读 (处理文字麻烦)
- [x]
- [x]
- [x]
- [x]
- [x] 紧贴
- [ ] 膜拜 (使用了透视变换, 需要研究矩阵变换)
- [ ] 小天使 (摆)
- [ ] 一直 (摆)
- [x]
- [ ] 问问 (摆)
- [ ] 典中典 (摆)
- [ ] 震惊 (摆)
- [ ] 哈哈镜 (摆)
- [ ] 对称 (猎奇, 不整)
- [x]
- [x] 2蹭
- [x] 诶嘿
- [x] 膜拜
- [x]
- [x]
- [x] 给我变
- [x] 玩一下
- [x] 不要看
- [x] 小天使
- [x] 你的
- [x] 我老婆
- [x] 远离
- [x] 抬棺

View File

@@ -5,8 +5,9 @@ import (
"strconv"
"sync"
"github.com/FloatTech/zbputils/file"
"github.com/FloatTech/zbputils/img"
"github.com/FloatTech/floatbox/file"
"github.com/FloatTech/floatbox/web"
"github.com/FloatTech/imgfactory"
"github.com/sirupsen/logrus"
)
@@ -18,10 +19,22 @@ type context struct {
func dlchan(name string, s *string, wg *sync.WaitGroup, exit func(error)) {
defer wg.Done()
target := datapath + `materials/` + name
var err error
if file.IsNotExist(target) {
err = file.DownloadTo(`https://gitcode.net/u011570312/imagematerials/-/raw/main/`+name, target, true)
data, err := web.RequestDataWith(web.NewTLS12Client(), `https://gitcode.net/m0_60838134/imagematerials/-/raw/main/`+name, "GET", "gitcode.net", web.RandUA(), nil)
if err != nil {
_ = os.Remove(target)
exit(err)
return
}
f, err := os.Create(target)
if err != nil {
exit(err)
return
}
_, err = f.Write(data)
_ = f.Close()
if err != nil {
_ = os.Remove(target)
exit(err)
return
}
@@ -35,8 +48,19 @@ func dlchan(name string, s *string, wg *sync.WaitGroup, exit func(error)) {
func dlblock(name string) (string, error) {
target := datapath + `materials/` + name
if file.IsNotExist(target) {
err := file.DownloadTo(`https://gitcode.net/u011570312/imagematerials/-/raw/main/`+name, target, true)
data, err := web.RequestDataWith(web.NewTLS12Client(), `https://gitcode.net/m0_60838134/imagematerials/-/raw/main/`+name, "GET", "gitcode.net", web.RandUA(), nil)
if err != nil {
_ = os.Remove(target)
return "", err
}
f, err := os.Create(target)
if err != nil {
return "", err
}
_, err = f.Write(data)
_ = f.Close()
if err != nil {
_ = os.Remove(target)
return "", err
}
logrus.Debugln("[gif] dl", name, "to", target, "succeeded")
@@ -73,10 +97,10 @@ func newContext(user int64) *context {
return c
}
func loadFirstFrames(paths []string, size int) (imgs []*img.ImgFactory, err error) {
imgs = make([]*img.ImgFactory, size)
func loadFirstFrames(paths []string, size int) (imgs []*imgfactory.Factory, err error) {
imgs = make([]*imgfactory.Factory, size)
for i := range imgs {
imgs[i], err = img.LoadFirstFrame(paths[i], 0, 0)
imgs[i], err = imgfactory.LoadFirstFrame(paths[i], 0, 0)
if err != nil {
return nil, err
}

File diff suppressed because it is too large Load Diff

View File

@@ -5,37 +5,39 @@ import (
"strconv"
"strings"
"github.com/FloatTech/zbputils/file"
"github.com/FloatTech/zbputils/img"
"github.com/FloatTech/floatbox/file"
"github.com/FloatTech/floatbox/process"
"github.com/FloatTech/imgfactory"
)
func (cc *context) prepareLogos(s ...string) error {
for i, v := range s {
_, err := strconv.Atoi(v)
if err != nil {
err = file.DownloadTo("https://gchat.qpic.cn/gchatpic_new//--"+strings.ToUpper(v)+"/0", cc.usrdir+strconv.Itoa(i)+".gif", true)
err = file.DownloadTo("https://gchat.qpic.cn/gchatpic_new//--"+strings.ToUpper(v)+"/0", cc.usrdir+strconv.Itoa(i)+".gif")
} else {
err = file.DownloadTo("http://q4.qlogo.cn/g?b=qq&nk="+v+"&s=640", cc.usrdir+strconv.Itoa(i)+".gif", true)
err = file.DownloadTo("http://q4.qlogo.cn/g?b=qq&nk="+v+"&s=640", cc.usrdir+strconv.Itoa(i)+".gif")
}
if err != nil {
return err
}
process.SleepAbout1sTo2s()
}
return nil
}
func (cc *context) getLogo(w int, h int) (*image.NRGBA, error) {
frame, err := img.LoadFirstFrame(cc.headimgsdir[0], w, h)
frame, err := imgfactory.LoadFirstFrame(cc.headimgsdir[0], w, h)
if err != nil {
return nil, err
}
return frame.Circle(0).Im, nil
return frame.Circle(0).Image(), nil
}
func (cc *context) getLogo2(w int, h int) (*image.NRGBA, error) {
frame, err := img.LoadFirstFrame(cc.headimgsdir[1], w, h)
frame, err := imgfactory.LoadFirstFrame(cc.headimgsdir[1], w, h)
if err != nil {
return nil, err
}
return frame.Circle(0).Im, nil
return frame.Circle(0).Image(), nil
}

File diff suppressed because it is too large Load Diff

View File

@@ -2,52 +2,167 @@
package gif
import (
"reflect"
"strconv"
"strings"
"github.com/FloatTech/floatbox/file"
ctrl "github.com/FloatTech/zbpctrl"
"github.com/FloatTech/zbputils/control"
"github.com/FloatTech/zbputils/ctxext"
"github.com/FloatTech/zbputils/file"
zero "github.com/wdvxdr1123/ZeroBot"
"github.com/wdvxdr1123/ZeroBot/message"
)
var (
cmds = []string{"搓", "冲", "摸", "拍", "丢", "吃", "敲", "啃", "蹭", "爬", "撕",
"灰度", "上翻", "下翻", "左翻", "右翻", "反色", "浮雕", "打码", "负片"}
cmd = make([]string, 0)
datapath string
cmdMap = map[string]func(cc *context, args ...string) (string, error){
"搓": cuo,
"冲": xqe,
"摸": mo,
"拍": pai,
"丢": diu,
"吃": chi,
"敲": qiao,
"啃": ken,
"蹭": ceng,
"爬": pa,
"撕": si,
"灰度": grayscale,
"上翻": flipV,
"下翻": flipV,
"左翻": flipH,
"右翻": flipH,
"反色": invert,
"浮雕": convolve3x3,
"打码": blur,
"负片": invertAndGrayscale,
"旋转": rotate,
"变形": deformation,
"亲": kiss,
"结婚申请": marriage,
"结婚登记": marriage,
"阿尼亚喜欢": anyasuki,
"像只": alike,
"我永远喜欢": alwaysLike,
"永远喜欢": alwaysLike,
"像样的亲亲": decentKiss,
"国旗": chinaFlag,
"不要靠近": dontTouch,
"万能表情": universal,
"空白表情": universal,
"采访": interview,
"需要": need,
"你可能需要": need,
"这像画吗": paint,
"小画家": painter,
"完美": perfect,
"玩游戏": playGame,
"出警": police,
"警察": police1,
"舔": prpr,
"舔屏": prpr,
"prpr": prpr,
"安全感": safeSense,
"精神支柱": support,
"想什么": thinkwhat,
"墙纸": wallpaper,
"为什么at我": whyatme,
"交个朋友": makeFriend,
"打工人": backToWork,
"继续干活": backToWork,
"兑换券": coupon,
"注意力涣散": distracted,
"垃圾桶": garbage,
"垃圾": garbage,
"捶": thump,
"啾啾": jiujiu,
"2敲": knock,
"听音乐": listenMusic,
"永远爱你": loveYou,
"2拍": pat,
"顶": jackUp,
"捣": pound,
"打拳": punch,
"滚": roll,
"吸": suck,
"嗦": suck,
"扔": throw,
"锤": hammer,
"紧贴": tightly,
"紧紧贴着": tightly,
"转": turn,
"蒙蔽": mengbi,
"踩": cai,
"好玩": haowan,
"2转": whirl,
"2滚": push,
"踢球": tiqiu,
"2舔": lick,
"可莉吃": klee,
"胡桃啃": hutaoken,
"怀": huai,
"砰": peng,
"你犯法了": fanfa,
"炖": dun,
"2蹭": ceng2,
"诶嘿": eihei,
"膜拜": worship,
"吞": ci,
"揍": zou,
"给我变": bian,
"玩一下": van,
"不要看": neko,
"小天使": xiaotianshi,
"你的": youer,
"我老婆": nowife,
"远离": yuanli,
"抬棺": taiguan,
"一直": alwaysDoGif,
}
)
func init() { // 插件主体
for k := range cmdMap {
cmd = append(cmd, k)
}
en := control.Register("gif", &ctrl.Options[*zero.Ctx]{
DisableOnDefault: false,
Help: "制图\n- " + strings.Join(cmds, "\n- "),
DisableOnDefault: false,
Brief: "制图",
Help: "下为制图命令:\n" +
"- 搓|- 冲|- 摸|-拍|- 丢|- 吃|- 敲|- 啃|- 蹭|- 爬|- 撕\n" +
"- 吸|- 嗦|- 扔|- 锤|- 紧贴|紧紧贴着|- 转|- 抬棺|- 远离\n" +
"- 揍|- 吞|- 膜拜|- 诶嘿|- 2蹭|- 你犯法了|- 砰|- 注意力涣散\n" +
"- 2敲|- 听音乐|- 永远爱你|- 2拍|- 顶|- 捣|- 打拳|- 滚\n" +
"- 灰度|- 上翻|- 下翻|- 左翻|- 右翻|- 反色|- 浮雕|- 打码\n" +
"- 负片|- 旋转|- 变形|- 亲|- 结婚申请|结婚登记|- 阿尼亚喜欢XXX\n" +
"- 像只|- 我永远喜欢XXX|- 像样的亲亲|- 国旗|- 不要靠近\n" +
"- 蒙蔽|- 踩|- 好玩|- 2转|- 踢球|- 2舔|- 可莉吃|- 胡桃啃|- 怀\n" +
"- 小画家|- 完美|- 玩游戏|- 出警|- 警察|- 舔|舔屏|prpr\n" +
"- 安全感|- 精神支柱|- 想什么|- 墙纸|- 为什么at我|- 交个朋友\n" +
"- 打工人|- 继续干活|- 兑换券|- 炖|- 垃圾桶|- 垃圾|- 捶|- 啾啾\n" +
"- 我老婆|- 小天使XXX|- 你的XXX|- 不要看|- 玩一下XXX|- 给我变\n" +
"- 万能表情|- 空白表情|- 采访|- 需要|- 你可能需要|- 这像画吗\n" +
"- 一直(支持动图)\n" +
"例: 制图命令XXX[@用户|QQ号|图片]\n" +
"Tips: XXX可以为限制长度的任何文字\n" +
"对Bot使用为 @Bot制图命令[XXX]@Bot",
PrivateDataFolder: "gif",
}).ApplySingle(ctxext.DefaultSingle)
datapath = file.BOTPATH + "/" + en.DataFolder()
en.OnRegex(`^(` + strings.Join(cmds, "|") + `)\D*?(\[CQ:(image\,file=([0-9a-zA-Z]{32}).*|at.+?(\d{5,11}))\].*|(\d+))$`).
en.OnRegex(`^(` + strings.Join(cmd, "|") + `)[\s\S]*?(\[CQ:(image\,file=([0-9a-zA-Z]{32}).*|at.+?(\d{5,11}))\].*|(\d+))$`).
SetBlock(true).Handle(func(ctx *zero.Ctx) {
c := newContext(ctx.Event.UserID)
list := ctx.State["regex_matched"].([]string)
err := c.prepareLogos(list[4]+list[5]+list[6], strconv.FormatInt(ctx.Event.UserID, 10))
if err != nil {
ctx.SendChain(message.Text("ERROR:", err))
ctx.SendChain(message.Text("ERROR: ", err))
return
}
var picurl string
if len([]rune(list[1])) == 1 {
r := reflect.ValueOf(c).MethodByName("A" + list[1]).Call(nil)
picurl = r[0].String()
if !r[1].IsNil() {
err = r[1].Interface().(error)
}
} else {
picurl, err = c.other(list[1]) // "灰度", "上翻", "下翻", "左翻", "右翻", "反色", "倒放", "浮雕", "打码", "负片"
}
argslist := strings.Split(strings.TrimSuffix(strings.TrimPrefix(list[0], list[1]), list[2]), " ")
picurl, err := cmdMap[list[1]](c, argslist...)
if err != nil {
ctx.SendChain(message.Text("ERROR:", err))
ctx.SendChain(message.Text("ERROR: ", err))
return
}
ctx.SendChain(message.Image(picurl))

View File

@@ -20,8 +20,8 @@ import (
func init() { // 插件主体
control.Register("github", &ctrl.Options[*zero.Ctx]{
DisableOnDefault: false,
Help: "GitHub仓库搜索\n" +
"- >github [xxx]\n" +
Brief: "GitHub仓库搜索",
Help: "- >github [xxx]\n" +
"- >github -p [xxx]",
}).OnRegex(`^>github\s(-.{1,10}? )?(.*)$`).SetBlock(true).
Handle(func(ctx *zero.Ctx) {
@@ -35,12 +35,12 @@ func init() { // 插件主体
}.Encode()
body, err := netGet(api.String(), header)
if err != nil {
ctx.SendChain(message.Text("ERROR:", err))
ctx.SendChain(message.Text("ERROR: ", err))
}
// 解析请求
info := gjson.ParseBytes(body)
if info.Get("total_count").Int() == 0 {
ctx.SendChain(message.Text("ERROR:没有找到这样的仓库"))
ctx.SendChain(message.Text("ERROR: 没有找到这样的仓库"))
return
}
repo := info.Get("items.0")
@@ -61,9 +61,9 @@ func init() { // 插件主体
"Star/Fork/Issue: ",
repo.Get("watchers").Int(), "/", repo.Get("forks").Int(), "/", repo.Get("open_issues").Int(), "\n",
"Language: ",
notnull(repo.Get("language").Str, "None"), "\n",
notnull(repo.Get("language").Str), "\n",
"License: ",
notnull(strings.ToUpper(repo.Get("license.key").Str), "None"), "\n",
notnull(strings.ToUpper(repo.Get("license.key").Str)), "\n",
"Last pushed: ",
repo.Get("pushed_at").Str, "\n",
"Jump: ",
@@ -79,9 +79,9 @@ func init() { // 插件主体
"Star/Fork/Issue: ",
repo.Get("watchers").Int(), "/", repo.Get("forks").Int(), "/", repo.Get("open_issues").Int(), "\n",
"Language: ",
notnull(repo.Get("language").Str, "None"), "\n",
notnull(repo.Get("language").Str), "\n",
"License: ",
notnull(strings.ToUpper(repo.Get("license.key").Str), "None"), "\n",
notnull(strings.ToUpper(repo.Get("license.key").Str)), "\n",
"Last pushed: ",
repo.Get("pushed_at").Str, "\n",
"Jump: ",
@@ -97,9 +97,9 @@ func init() { // 插件主体
// notnull 如果传入文本为空,则返回默认值
func notnull(text, defstr string) string {
func notnull(text string) string {
if text == "" {
return defstr
return "None"
}
return text
}

View File

@@ -0,0 +1,473 @@
package guessmusic
import (
"encoding/json"
"math/rand"
"net/url"
"os"
"strconv"
"strings"
"time"
wyy "github.com/FloatTech/AnimeAPI/neteasemusic"
"github.com/FloatTech/floatbox/file"
"github.com/FloatTech/floatbox/web"
"github.com/FloatTech/zbputils/ctxext"
"github.com/pkg/errors"
zero "github.com/wdvxdr1123/ZeroBot"
"github.com/wdvxdr1123/ZeroBot/message"
)
func init() {
// API配置
engine.OnPrefix("设置猜歌API", zero.SuperUserPermission).SetBlock(true).
Handle(func(ctx *zero.Ctx) {
option := ctx.State["args"].(string)
if option == "帮助" {
ctx.SendChain(message.Text(
"项目地址:binaryify.github.io/NeteaseCloudMusicApi" +
"\n网上有基于该框架的API,可以自行搜索白嫖。\n" +
"添加API指令:\n设置猜歌API [API首页网址]"))
return
}
if !strings.HasSuffix(option, "/") {
option += "/"
}
cfg.APIURL = option
err := saveConfig(cfgFile)
if err == nil {
ctx.SendChain(message.Text("成功!"))
} else {
ctx.SendChain(message.Text(serviceErr, err))
}
})
// API配置
engine.OnRegex(`^猜歌(开启|关闭)(歌单|歌词)自动下载`, zero.SuperUserPermission).SetBlock(true).
Handle(func(ctx *zero.Ctx) {
swtich := ctx.State["regex_matched"].([]string)[1]
option := ctx.State["regex_matched"].([]string)[2]
chose := true
if swtich == "关闭" {
chose = false
}
if option == "歌单" {
cfg.API = chose
} else {
cfg.Local = chose
}
err := saveConfig(cfgFile)
if err == nil {
ctx.SendChain(message.Text("成功!"))
} else {
ctx.SendChain(message.Text(serviceErr, err))
}
})
engine.OnFullMatch("登录网易云", zero.SuperUserPermission, func(ctx *zero.Ctx) bool {
if !zero.OnlyPrivate(ctx) {
ctx.SendChain(message.Text("为了保护登录过程,请bot主人私聊。"))
return false
}
return true
}).SetBlock(true).
Handle(func(ctx *zero.Ctx) {
keyURL := cfg.APIURL + "login/qr/key"
data, err := web.GetData(keyURL)
if err != nil {
ctx.SendChain(message.Text(serviceErr, "获取网易云key失败,", err))
return
}
var keyInfo keyInfo
err = json.Unmarshal(data, &keyInfo)
if err != nil {
ctx.SendChain(message.Text(serviceErr, "解析网易云key失败,", err))
return
}
qrURL := cfg.APIURL + "login/qr/create?key=" + keyInfo.Data.Unikey + "&qrimg=1"
data, err = web.GetData(qrURL)
if err != nil {
ctx.SendChain(message.Text(serviceErr, "获取网易云二维码失败,", err))
return
}
var qrInfo qrInfo
err = json.Unmarshal(data, &qrInfo)
if err != nil {
ctx.SendChain(message.Text(serviceErr, "解析网易云二维码失败,", err))
return
}
ctx.SendChain(message.Text("[请使用手机APP扫描二维码或者进入网页扫码登录]\n", qrInfo.Data.Qrurl),
message.Image("base64://"+strings.ReplaceAll(qrInfo.Data.Qrimg, "data:image/png;base64,", "")),
message.Text("二维码有效时间为6分钟,登陆后请耐心等待结果,获取cookie过程有些漫长。"))
i := 0
for range time.NewTicker(10 * time.Second).C {
APIURL := cfg.APIURL + "login/qr/check?key=" + url.QueryEscape(keyInfo.Data.Unikey)
data, err := web.GetData(APIURL)
if err != nil {
ctx.SendChain(message.Text(serviceErr, "无法获取登录状态,", err))
return
}
var cookiesInfo cookyInfo
err = json.Unmarshal(data, &cookiesInfo)
if err != nil {
ctx.SendChain(message.Text(serviceErr, "解析登录状态失败,", err))
return
}
switch cookiesInfo.Code {
case 803:
cfg.Cookie = cookiesInfo.Cookie
err = saveConfig(cfgFile)
if err == nil {
ctx.SendChain(message.Text("成功!"))
} else {
ctx.SendChain(message.Text(serviceErr, err))
}
return
case 801:
i++
if i%6 == 0 { // 每1分钟才提醒一次,减少提示(380/60=6次)
ctx.SendChain(message.Text("状态:", cookiesInfo.Message))
}
continue
case 800:
ctx.SendChain(message.Text("状态:", cookiesInfo.Message))
return
default:
ctx.SendChain(message.Text("状态:", cookiesInfo.Message))
continue
}
}
})
engine.OnRegex(`^歌单信息\s*((https:\/\/music\.163\.com\/#\/playlist\?id=)?(\d+)|http:\/\/music\.163\.com\/playlist\/(\d+).*)$`).SetBlock(true).Limit(ctxext.LimitByGroup).
Handle(func(ctx *zero.Ctx) {
listID := ctx.State["regex_matched"].([]string)[3] + ctx.State["regex_matched"].([]string)[4]
_, err := strconv.ParseInt(listID, 10, 64)
if err != nil {
ctx.SendChain(message.Text("请输入正确的歌单ID或者歌单连接"))
return
}
APIURL := cfg.APIURL + "playlist/detail?id=" + listID
data, err := web.GetData(APIURL)
if err != nil {
ctx.SendChain(message.Text("无法连接歌单,", err))
return
}
var parsed listInfoOfAPI
err = json.Unmarshal(data, &parsed)
if err != nil {
ctx.SendChain(message.Text("无法解析歌单ID内容,", err))
return
}
ctx.SendChain(
message.Image(parsed.Playlist.CoverImgURL),
message.Text(
"歌单名称:", parsed.Playlist.Name,
"\n歌单ID:", parsed.Playlist.ID,
"\n创建人:", parsed.Playlist.Creator.Nickname,
"\n创建时间:", time.Unix(parsed.Playlist.CreateTime/1000, 0).Format("2006-01-02"),
"\n标签:", strings.Join(parsed.Playlist.Tags, ";"),
"\n歌曲数量:", parsed.Playlist.TrackCount,
"\n歌单简介:\n", parsed.Playlist.Description,
"\n更新时间:", time.Unix(parsed.Playlist.UpdateTime/1000, 0).Format("2006-01-02"),
))
})
// 本地绑定网易云歌单ID
engine.OnRegex(`^(.*)绑定网易云\s*((https:\/\/music\.163\.com\/#\/playlist\?id=)?(\d+)|http:\/\/music\.163\.com\/playlist\/(\d+).*)$`, zero.SuperUserPermission).SetBlock(true).Limit(ctxext.LimitByGroup).
Handle(func(ctx *zero.Ctx) {
listName := ctx.State["regex_matched"].([]string)[1]
listID := ctx.State["regex_matched"].([]string)[4] + ctx.State["regex_matched"].([]string)[5]
ctx.SendChain(message.Text("正在校验歌单信息,请稍等"))
pathOfMusic := cfg.MusicPath + listName + "/"
if file.IsNotExist(pathOfMusic) {
ctx.SendChain(message.Text(serviceErr, "歌单不存在于本地"))
return
}
// 是否存在该歌单
APIURL := cfg.APIURL + "playlist/track/all?id=" + listID
data, err := web.GetData(APIURL)
if err != nil {
ctx.SendChain(message.Text(serviceErr, err))
return
}
var parsed musicListOfApI
err = json.Unmarshal(data, &parsed)
if err != nil {
ctx.SendChain(message.Text(serviceErr, "无法解析歌单ID内容,", err))
return
}
if parsed.Code != 200 {
ctx.SendChain(message.Text(serviceErr, parsed.Code))
return
}
mid, _ := strconv.ParseInt(listID, 10, 64)
cfg.Playlist = append(cfg.Playlist, listRaw{
Name: listName,
ID: mid,
})
err = saveConfig(cfgFile)
if err == nil {
ctx.SendChain(message.Text("成功!"))
} else {
ctx.SendChain(message.Text(serviceErr, err))
}
})
engine.OnPrefix("解除绑定", zero.SuperUserPermission).SetBlock(true).
Handle(func(ctx *zero.Ctx) {
delList := ctx.State["args"].(string)
filelist, err := getlist(cfg.MusicPath)
if err != nil {
ctx.SendChain(message.Text(serviceErr, err))
return
}
var playID int64
for _, listinfo := range filelist {
if delList == listinfo.Name {
playID = listinfo.ID
break
}
}
// 删除ID
if playID == 0 { // 如果ID没有且没删除文件
ctx.SendChain(message.Text("歌单名称错误或者该歌单并没有绑定网易云,可以发送“歌单列表”获取歌单名称"))
return
}
index := -1
for i, list := range cfg.Playlist {
if playID == list.ID {
index = i
break
}
}
if index == -1 {
ctx.SendChain(message.Text("歌单名称错误或者该歌单并没有绑定网易云,可以发送“歌单列表”获取歌单名称"))
return
}
cfg.Playlist = append(cfg.Playlist[:index], cfg.Playlist[index+1:]...)
err = saveConfig(cfgFile)
if err != nil {
ctx.SendChain(message.Text(serviceErr, err))
return
}
if err == nil {
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).
Handle(func(ctx *zero.Ctx) {
keyword := ctx.State["regex_matched"].([]string)[3] + ctx.State["regex_matched"].([]string)[4]
listName := ctx.State["regex_matched"].([]string)[5]
ctx.SendChain(message.Text("正在校验歌单信息,请稍等"))
// 是否存在该歌单
if file.IsNotExist(cfg.MusicPath + listName) {
ctx.SendChain(message.Text("歌单不存在,是否创建?(是/否)"))
next := zero.NewFutureEvent("message", 999, false, zero.OnlyGroup, zero.RegexRule(`(是|否)`), ctx.CheckSession())
recv, cancel := next.Repeat()
defer cancel()
wait := time.NewTimer(120 * time.Second)
answer := ""
for {
select {
case <-wait.C:
wait.Stop()
ctx.SendChain(message.Text("等待超时,取消下载"))
return
case c := <-recv:
wait.Stop()
answer = c.Event.Message.String()
}
if answer == "否" {
ctx.SendChain(message.Text("下载已经取消"))
return
}
if answer != "" {
break
}
}
err := os.MkdirAll(cfg.MusicPath+listName, 0755)
if err != nil {
ctx.SendChain(message.Text(serviceErr, err))
return
}
}
ctx.SendChain(message.Text("开始下载歌曲,需要一定时间下载,请稍等"))
listID, err := strconv.ParseInt(keyword, 10, 64)
if err == nil {
err = downloadlist(listID, cfg.MusicPath+listName+"/")
}
if err == nil {
ctx.SendChain(message.Text("成功!"))
} else {
ctx.SendChain(message.Text(serviceErr, err))
}
})
}
// 随机从歌单下载歌曲(歌单ID, 音乐保存路径)
func drawByAPI(playlistID int64, musicPath string) (musicName string, err error) {
APIURL := cfg.APIURL + "playlist/track/all?id=" + strconv.FormatInt(playlistID, 10)
data, err := web.GetData(APIURL)
if err != nil {
err = errors.Errorf("无法获取歌单列表\n%s", err)
return
}
var parsed musicListOfApI
err = json.Unmarshal(data, &parsed)
if err != nil {
err = errors.Errorf("无法读取歌单列表\n%s", err)
return
}
listlen := len(parsed.Songs)
randidx := rand.Intn(listlen)
// 将"/"符号去除,不然无法生成文件
name := strings.ReplaceAll(parsed.Songs[randidx].Name, "/", "·")
musicID := parsed.Songs[randidx].ID
artistName := ""
for i, ARInfo := range parsed.Songs[randidx].Ar {
if i != 0 {
artistName += "&" + ARInfo.Name
} else {
artistName += ARInfo.Name
}
}
cource := ""
if parsed.Songs[randidx].Alia != nil {
cource = strings.Join(parsed.Songs[randidx].Alia, "&")
// 将"/"符号去除,不然无法下载
cource = strings.ReplaceAll(cource, "/", "&")
}
if name == "" || musicID == 0 {
err = errors.New("无法获API取歌曲信息")
return
}
if cource != "" {
name += " - " + artistName + " - " + cource
} else {
name += " - " + artistName
}
// 下载歌曲
err = wyy.DownloadMusic(musicID, name, musicPath)
if err == nil {
musicName = name + ".mp3"
if cfg.Local {
// 下载歌词
_ = wyy.DownloadLrc(musicID, name, musicPath+"歌词/")
}
}
return
}
// 下载歌单歌曲(歌单ID, 音乐保存路径)
func downloadlist(playlistID int64, musicPath string) error {
APIURL := cfg.APIURL + "playlist/track/all?id=" + strconv.FormatInt(playlistID, 10)
data, err := web.GetData(APIURL)
if err != nil {
return err
}
var parsed musicListOfApI
err = json.Unmarshal(data, &parsed)
if err != nil {
return err
}
if parsed.Code != 200 {
err = errors.Errorf("requset code : %d", parsed.Code)
return err
}
for _, info := range parsed.Songs {
// 将"/"符号去除,不然无法生成文件
musicName := strings.ReplaceAll(info.Name, "/", "·")
musicID := info.ID
artistName := ""
for i, ARInfo := range info.Ar {
if i != 0 {
artistName += "&" + ARInfo.Name
} else {
artistName += ARInfo.Name
}
}
cource := ""
if info.Alia != nil {
cource = strings.Join(info.Alia, "&")
// 将"/"符号去除,不然无法下载
cource = strings.ReplaceAll(cource, "/", "&")
}
if musicName == "" || musicID == 0 {
err = errors.New("无法获API取歌曲信息")
return err
}
if cource != "" {
musicName += " - " + artistName + " - " + cource
} else {
musicName += " - " + artistName
}
// 下载歌曲
err = wyy.DownloadMusic(musicID, musicName, musicPath)
if err == nil {
if cfg.Local {
// 下载歌词
_ = wyy.DownloadLrc(musicID, musicName, musicPath+"歌词/")
}
}
}
return nil
}
/*****************************************************************/
/**************************独角兽API*******************************/
/*****************************************************************/
// 下载从独角兽抽到的歌曲ID(歌单ID, 音乐保存路径, 歌词保存路径)
func downloadByOvooa(playlistID int64, musicPath string) (musicName string, err error) {
// 抽取歌曲
mid, err := drawByOvooa(playlistID)
if err != nil {
err = errors.Errorf("API%s", err)
return
}
// 获取完成的歌名
musiclist, err := wyy.SearchMusic(strconv.Itoa(mid), 1)
if err != nil {
err = errors.Errorf("API歌曲下载ERROR: %s", err)
return
}
// 歌曲ID理论是唯一的
mun := len(musiclist)
if mun == 1 {
// 拉取歌名
musicList := make([]string, mun)
i := 0
for musicName := range musiclist {
musicList[i] = musicName
}
name := musicList[0]
// 下载歌曲
err = wyy.DownloadMusic(mid, name, musicPath)
if err == nil {
musicName = name + ".mp3"
if cfg.Local {
// 下载歌词
_ = wyy.DownloadLrc(mid, name, musicPath+"歌词/")
}
}
} else {
err = errors.Errorf("music IDThis music ID sreached munber is %d", mun)
}
return
}
// 通过独角兽API随机抽取歌单歌曲ID(参数歌单ID)
func drawByOvooa(playlistID int64) (musicID int, err error) {
APIURL := "https://ovooa.com/API/163_Music_Rand/api.php?id=" + strconv.FormatInt(playlistID, 10)
data, err := web.GetData(APIURL)
if err != nil {
return
}
var parsed ovooaData
err = json.Unmarshal(data, &parsed)
if err != nil {
return
}
if parsed.Code != 1 {
return
}
return parsed.Data.ID, nil
}

View File

@@ -0,0 +1,316 @@
package guessmusic
import (
"bytes"
"io/fs"
"math/rand"
"os"
"os/exec"
"strconv"
"strings"
"sync"
"time"
"github.com/FloatTech/floatbox/file"
"github.com/FloatTech/zbputils/ctxext"
"github.com/pkg/errors"
zero "github.com/wdvxdr1123/ZeroBot"
"github.com/wdvxdr1123/ZeroBot/message"
)
var cuttime = [...]string{"00:00:05", "00:00:30", "00:01:00"} // 音乐切割时间点,可自行调节时间(时:分:秒)
func init() {
engine.OnRegex(`^(个人|团队)猜歌(-(.*))?$`, zero.OnlyGroup).SetBlock(true).Limit(ctxext.LimitByGroup).
Handle(func(ctx *zero.Ctx) {
mode := ctx.State["regex_matched"].([]string)[3]
gid := ctx.Event.GroupID
// 获取本地列表
filelist, err := getlist(cfg.MusicPath)
if err != nil {
ctx.SendChain(message.Text(serviceErr, err))
return
}
// 加载默认歌单
if mode == "" {
index := -1
for i, dlist := range cfg.Defaultlist {
if dlist.GroupID == gid {
index = i
break
}
}
if index == -1 {
// 如果没有设置就默认第一个文件夹
mode = filelist[0].Name
} else {
mode = cfg.Defaultlist[index].Name
ok := true
for _, listinfo := range filelist {
if mode == listinfo.Name {
ok = false
break
}
}
// 如果默认的歌单不存在了清空设置
if ok {
cfg.Defaultlist = append(cfg.Defaultlist[:index], cfg.Defaultlist[index+1:]...)
_ = saveConfig(cfgFile)
mode = filelist[0].Name
}
}
}
ctx.SendChain(message.Text("正在准备歌曲,请稍等\n回答“-[歌曲信息(歌名歌手等)|提示|取消]”\n一共3段语音,6次机会"))
// 随机抽歌
pathOfMusic, musicName, err := musicLottery(cfg.MusicPath, mode)
if err != nil {
ctx.SendChain(message.Text(serviceErr, err))
return
}
// 解析歌曲信息
music := strings.Split(musicName, ".")
// 获取音乐后缀
musictype := music[len(music)-1]
if !strings.Contains(musictypelist, musictype) {
ctx.SendChain(message.Text("抽取到了歌曲:\n",
musicName, "\n该歌曲不是音乐后缀,请联系bot主人修改"))
return
}
// 获取音乐信息
musicInfo := strings.Split(strings.ReplaceAll(musicName, "."+musictype, ""), " - ")
infoNum := len(musicInfo)
if infoNum == 1 {
ctx.SendChain(message.Text("抽取到了歌曲:\n",
musicName, "\n该歌曲命名不符合命名规则,请联系bot主人修改"))
return
}
answerString := "歌名:" + musicInfo[0] + "\n歌手:" + musicInfo[1]
if infoNum > 2 {
musicInfo[2] = strings.ReplaceAll(musicInfo[2], "&", "\n")
answerString += "\n其他信息:\n" + musicInfo[2]
}
musicInfo = append(musicInfo, answerString)
// 切割音频,生成3个10秒的音频
outputPath := cachePath + strconv.FormatInt(gid, 10) + "/"
err = cutMusic(musicName, pathOfMusic, outputPath)
if err != nil {
ctx.SendChain(message.Text(err))
return
}
// 进行猜歌环节
ctx.SendChain(message.Record("file:///" + file.BOTPATH + "/" + outputPath + "0.wav"))
var next *zero.FutureEvent
if ctx.State["regex_matched"].([]string)[1] == "个人" {
next = zero.NewFutureEvent("message", 999, false, zero.OnlyGroup, zero.RegexRule(`^-\S{1,}`), ctx.CheckSession())
} else {
next = zero.NewFutureEvent("message", 999, false, zero.OnlyGroup, zero.RegexRule(`^-\S{1,}`), zero.CheckGroup(ctx.Event.GroupID))
}
recv, cancel := next.Repeat()
defer cancel()
wait := time.NewTimer(40 * time.Second)
tick := time.NewTimer(105 * time.Second)
after := time.NewTimer(120 * time.Second)
wg := sync.WaitGroup{}
var (
messageStr message.MessageSegment // 文本信息
tickCount = 0 // 音频数量
answerCount = 0 // 问答次数
win bool // 是否赢得游戏
)
for {
select {
case <-tick.C:
ctx.SendChain(message.Text("猜歌游戏,你还有15s作答时间"))
case <-after.C:
ctx.Send(message.ReplyWithMessage(ctx.Event.MessageID,
message.Text("时间超时,猜歌结束,公布答案:\n", answerString)))
return
case <-wait.C:
wait.Reset(40 * time.Second)
tickCount++
if tickCount > 2 {
wait.Stop()
continue
}
ctx.SendChain(
message.Text("好像有些难度呢,再听这段音频,要仔细听哦"),
)
ctx.SendChain(message.Record("file:///" + file.BOTPATH + "/" + outputPath + strconv.Itoa(tickCount) + ".wav"))
case c := <-recv:
wg.Add(1)
go func() {
messageStr, answerCount, tickCount, win = gameMatch(c, ctx.Event.UserID, musicInfo, answerCount, tickCount)
if win { // 游戏结束的话
wait.Stop()
tick.Stop()
after.Stop()
ctx.SendChain(message.Reply(c.Event.MessageID), messageStr)
ctx.SendChain(message.Record("file:///" + pathOfMusic + musicName))
} else {
wait.Reset(40 * time.Second)
tick.Reset(105 * time.Second)
after.Reset(120 * time.Second)
if tickCount > 2 || messageStr.Data["text"] == "你无权限取消" {
ctx.SendChain(message.Reply(c.Event.MessageID), messageStr)
} else {
ctx.SendChain(message.Reply(c.Event.MessageID), messageStr)
ctx.SendChain(message.Record("file:///" + file.BOTPATH + "/" + outputPath + strconv.Itoa(tickCount) + ".wav"))
}
}
wg.Done()
}()
wg.Wait()
if win {
return
}
}
}
})
}
// 随机抽取音乐
func musicLottery(musicPath, listName string) (pathOfMusic, musicName string, err error) {
// 读取歌单文件
pathOfMusic = musicPath + listName + "/"
if file.IsNotExist(pathOfMusic) {
err = errors.New("指定的歌单不存在,可发送“歌单列表”查看歌单列表")
return
}
files, err := os.ReadDir(pathOfMusic)
if err != nil {
return
}
// 获取绑定的网易云
var playlistID int64
for _, listinfo := range cfg.Playlist {
if listinfo.Name == listName {
playlistID = listinfo.ID
}
}
// 如果本地列表为空
if len(files) == 0 {
if playlistID == 0 || !cfg.API {
err = errors.New("本地歌单数据为0")
return
}
// 如果绑定了歌单ID
if cfg.APIURL == "" {
// 如果没有配置过API地址,尝试连接独角兽
musicName, err = downloadByOvooa(playlistID, pathOfMusic)
if err != nil {
err = errors.Errorf("本地歌单数据为0,API下载歌曲失败\n%s", err)
}
} else {
// 从API中抽取歌曲
musicName, err = drawByAPI(playlistID, pathOfMusic)
if err != nil {
err = errors.Errorf("本地歌单数据为0,API下载歌曲失败\n%s", err)
}
}
return
}
// 进行随机抽取
if playlistID == 0 || !cfg.API {
musicName = getLocalMusic(files, 10)
} else {
switch rand.Intn(3) { // 三分二概率抽取API的
case 1:
musicName = getLocalMusic(files, 10)
default:
if cfg.APIURL == "" {
// 如果没有配置过API地址,尝试连接独角兽
musicName, err = downloadByOvooa(playlistID, pathOfMusic)
} else {
// 从API中抽取歌曲
musicName, err = drawByAPI(playlistID, pathOfMusic)
}
if err != nil {
musicName = getLocalMusic(files, 10)
}
}
}
if musicName == "" {
err = errors.New("抽取歌曲轮空了,请重试")
}
return
}
// 从本地列表中随机抽取一首( indexMax : 最大递归次数 )
func getLocalMusic(files []fs.DirEntry, indexMax int) (musicName string) {
if len(files) > 1 {
music := files[rand.Intn(len(files))]
// 如果是文件夹就递归
if music.IsDir() {
indexMax--
if indexMax <= 0 {
return
}
musicName = getLocalMusic(files, indexMax)
} else {
musicName = music.Name()
}
} else {
music := files[0]
if !music.IsDir() {
musicName = files[0].Name()
}
}
return
}
// 切割音乐成三个10s音频
func cutMusic(musicName, pathOfMusic, outputPath string) (err error) {
err = os.MkdirAll(outputPath, 0755)
if err != nil {
err = errors.Errorf("[生成歌曲目录错误]ERROR: %s", err)
return
}
var stderr bytes.Buffer
cmdArguments := []string{"-y", "-i", pathOfMusic + musicName,
"-ss", cuttime[0], "-t", "10", file.BOTPATH + "/" + outputPath + "0.wav",
"-ss", cuttime[1], "-t", "10", file.BOTPATH + "/" + outputPath + "1.wav",
"-ss", cuttime[2], "-t", "10", file.BOTPATH + "/" + outputPath + "2.wav", "-hide_banner"}
cmd := exec.Command("ffmpeg", cmdArguments...)
cmd.Stderr = &stderr
err = cmd.Run()
if err != nil {
err = errors.Errorf("[生成歌曲错误]ERROR: %s", stderr.String())
return
}
return
}
// 数据匹配(结果信息,答题次数,提示次数,是否结束游戏)
func gameMatch(c *zero.Ctx, beginner int64, musicInfo []string, answerTimes, tickTimes int) (message.MessageSegment, int, int, bool) {
answer := strings.Replace(c.Event.Message.String(), "-", "", 1)
switch {
case answer == "取消":
if c.Event.UserID == beginner {
return message.Text("游戏已取消,猜歌答案是\n", musicInfo[len(musicInfo)-1], "\n\n\n下面欣赏猜歌的歌曲"), answerTimes, tickTimes, true
}
return message.Text("你无权限取消"), answerTimes, tickTimes, false
case answer == "提示":
tickTimes++
if tickTimes > 2 {
return message.Text("已经没有提示了哦"), answerTimes, tickTimes, false
}
return message.Text("再听这段音频,要仔细听哦"), answerTimes, tickTimes, false
case strings.Contains(musicInfo[0], answer) || strings.EqualFold(musicInfo[0], answer):
return message.Text("太棒了,你猜对歌曲名了!答案是\n", musicInfo[len(musicInfo)-1], "\n\n下面欣赏猜歌的歌曲"), answerTimes, tickTimes, true
case strings.Contains(musicInfo[1], answer) || strings.EqualFold(musicInfo[1], answer):
return message.Text("太棒了,你猜对歌手名了!答案是\n", musicInfo[len(musicInfo)-1], "\n\n下面欣赏猜歌的歌曲"), answerTimes, tickTimes, true
case len(musicInfo) == 4 && (strings.Contains(musicInfo[2], answer) || strings.EqualFold(musicInfo[2], answer)):
return message.Text("太棒了,你猜对相关信息了!答案是\n", musicInfo[len(musicInfo)-1], "\n\n下面欣赏猜歌的歌曲"), answerTimes, tickTimes, true
default:
answerTimes++
tickTimes++
switch {
case tickTimes > 2 && answerTimes < 6:
return message.Text("答案不对哦,还有", 6-answerTimes, "次答题,加油啊~"), answerTimes, tickTimes, false
case tickTimes > 2:
return message.Text("次数到了,没能猜出来。答案是\n", musicInfo[len(musicInfo)-1], "\n\n下面欣赏猜歌的歌曲"), answerTimes, tickTimes, true
default:
return message.Text("答案不对,再听这段音频,要仔细听哦"), answerTimes, tickTimes, false
}
}
}

580
plugin/guessmusic/main.go Normal file
View File

@@ -0,0 +1,580 @@
// Package guessmusic 基于zbp的猜歌插件
package guessmusic
import (
"encoding/json"
"os"
"strconv"
"strings"
"time"
wyy "github.com/FloatTech/AnimeAPI/neteasemusic"
"github.com/FloatTech/imgfactory"
ctrl "github.com/FloatTech/zbpctrl"
"github.com/FloatTech/zbputils/control"
"github.com/FloatTech/zbputils/ctxext"
"github.com/pkg/errors"
zero "github.com/wdvxdr1123/ZeroBot"
"github.com/wdvxdr1123/ZeroBot/extension/single"
"github.com/wdvxdr1123/ZeroBot/message"
// 图片输出
"github.com/FloatTech/floatbox/file"
"github.com/FloatTech/floatbox/process"
"github.com/FloatTech/gg"
"github.com/FloatTech/zbputils/img/text"
)
const serviceErr = "[guessmusic]error:"
var (
// 用户数据
cfg config
// 插件主体
engine = control.Register("guessmusic", &ctrl.Options[*zero.Ctx]{
DisableOnDefault: false,
Brief: "猜歌插件",
Help: "------bot主人指令------\n" +
"- 设置猜歌歌库路径 [绝对路径]\n" +
"- [创建/删除]歌单 [歌单名称]\n" +
"- 下载歌曲[歌曲名称/网易云歌曲ID]到[歌单名称]\n" +
"------管 理 员 指 令------\n" +
"- 设置猜歌默认歌单 [歌单名称]\n" +
"- 上传歌曲[群文件的音乐名]到[歌单名称]\n" +
"------公 用 指 令------\n" +
"- 歌单列表\n" +
"- [个人/团队]猜歌\n" +
"\n------重 要 事 项------\n" +
"1.本插件依赖ffmpeg\n" +
"2.\"删除[歌单名称]\"是将本地歌单数据全部删除, 慎用\n" +
"3.不支持下载VIP歌曲,如有需求请用群文件上传\n" +
"4.未设置默认歌单的场合,猜歌歌单为歌单列表第一个。\n" +
"此外可在\"[个人/团队]猜歌\"指令后面添加[-歌单名称]进行指定歌单猜歌\n" +
"5.猜歌内容必须以[-]开头才会识别\n" +
"6.歌曲命名规则为:\n歌名 - 歌手 - 其他(歌曲出处之类)" +
"\n------插 件 扩 展------\n" +
"内置了独角兽API,但API不保证可靠性。\n" +
"可以自行搭建或寻找NeteaseCloudMusicApi框架的API,本插件支持该API以下指令\n" +
"NeteaseCloudMusicApi项目地址:\nhttps://binaryify.github.io/NeteaseCloudMusicApi/#/\n" +
"- 设置猜歌API帮助\n" +
"- 设置猜歌API [API首页网址]\n" +
"- 猜歌[开启/关闭][歌单/歌词]自动下载\n" +
"- 登录网易云(这个指令目前不知道能干嘛,总之先保留了)\n" +
"- 歌单信息 [网易云歌单链接/ID]\n" +
"- [歌单名称]绑定网易云[网易云歌单链接/ID]\n" +
"- 下载歌单[网易云歌单链接/ID]到[歌单名称]\n" +
"- 解除绑定 [歌单名称]",
PrivateDataFolder: "guessmusic",
}).ApplySingle(single.New(
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/"
// 用于存放用户的配置
cfgFile = engine.DataFolder() + "config.json"
// ffmpeg支持的格式
musictypelist = "mp3;MP3;wav;WAV;amr;AMR;3gp;3GP;3gpp;3GPP;acc;ACC"
)
func init() {
// 新建缓存文件夹
err := os.MkdirAll(cachePath, 0755)
if err != nil {
panic(serviceErr + err.Error())
}
// 载入用户配置
if file.IsExist(cfgFile) {
reader, err := os.Open(cfgFile)
if err == nil {
err = json.NewDecoder(reader).Decode(&cfg)
}
if err != nil {
panic(serviceErr + err.Error())
}
err = reader.Close()
if err != nil {
panic(serviceErr + err.Error())
}
} else {
// 配置默认 config
cfg = config{
MusicPath: file.BOTPATH + "/data/guessmusic/music/", // 绝对路径,歌库根目录,通过指令进行更改
Playlist: []listRaw{
{
Name: "这里是歌单名称,id为网易云歌单ID",
ID: 123456,
},
},
Defaultlist: []dlist{
{
GroupID: 123456,
Name: "这里是歌单名称,gid是群号",
},
},
API: true,
Local: true,
}
err = saveConfig(cfgFile)
if err != nil {
panic(serviceErr + err.Error())
}
}
// 用户配置
engine.OnPrefix("设置猜歌歌库路径", zero.SuperUserPermission).SetBlock(true).
Handle(func(ctx *zero.Ctx) {
option := ctx.State["args"].(string)
musicPath := strings.ReplaceAll(option, "\\", "/")
if !strings.HasSuffix(musicPath, "/") {
musicPath += "/"
}
err := os.MkdirAll(musicPath, 0755)
if err != nil {
ctx.SendChain(message.Text(serviceErr, err))
return
}
cfg.MusicPath = musicPath
err = saveConfig(cfgFile)
if err == nil {
ctx.SendChain(message.Text("成功!"))
} else {
ctx.SendChain(message.Text(serviceErr, err))
}
})
engine.OnPrefix("创建歌单", zero.SuperUserPermission).SetBlock(true).
Handle(func(ctx *zero.Ctx) {
newList := cfg.MusicPath + ctx.State["args"].(string)
if file.IsNotExist(newList) {
err := os.MkdirAll(newList, 0755)
if err == nil {
ctx.SendChain(message.Text("成功!"))
} else {
ctx.SendChain(message.Text(serviceErr, err))
}
} else {
ctx.SendChain(message.Text("歌单已存在!"))
}
})
engine.OnPrefix("删除歌单", zero.SuperUserPermission).SetBlock(true).
Handle(func(ctx *zero.Ctx) {
delList := ctx.State["args"].(string)
err = os.RemoveAll(cfg.MusicPath + delList)
if err != nil {
ctx.SendChain(message.Text("删除失败,可能是歌单名称错误。\n可以发送“歌单列表”获取歌单名称"))
return
}
// 删除绑定的网易云ID
index := -1
for i, list := range cfg.Playlist {
if delList == list.Name {
index = i
break
}
}
if index == -1 {
ctx.SendChain(message.Text("成功!"))
return
}
cfg.Playlist = append(cfg.Playlist[:index], cfg.Playlist[index+1:]...)
err = saveConfig(cfgFile)
if err == nil {
ctx.SendChain(message.Text("成功!"))
} else {
ctx.SendChain(message.Text(serviceErr, err))
}
})
// 下载歌曲到对应的歌单里面
engine.OnRegex(`^下载歌曲\s*(.*)\s*到\s*(.*)$`, zero.SuperUserPermission).SetBlock(true).Limit(ctxext.LimitByGroup).
Handle(func(ctx *zero.Ctx) {
keyword := ctx.State["regex_matched"].([]string)[1]
listName := ctx.State["regex_matched"].([]string)[2]
ctx.SendChain(message.Text("正在校验歌单信息,请稍等"))
// 是否存在该歌单
if file.IsNotExist(cfg.MusicPath + listName) {
ctx.SendChain(message.Text("歌单不存在,是否创建?(是/否)"))
next := zero.NewFutureEvent("message", 999, false, zero.RegexRule(`(是|否)`), ctx.CheckSession())
recv, cancel := next.Repeat()
defer cancel()
wait := time.NewTimer(120 * time.Second)
answer := ""
for {
select {
case <-wait.C:
wait.Stop()
ctx.SendChain(message.Text("等待超时,取消下载"))
return
case c := <-recv:
wait.Stop()
answer = c.Event.Message.String()
}
if answer == "否" {
ctx.SendChain(message.Text("下载已经取消"))
return
}
if answer != "" {
break
}
}
err = os.MkdirAll(cfg.MusicPath+listName, 0777)
if err != nil {
ctx.SendChain(message.Text(serviceErr, err))
return
}
}
searchlist, err := wyy.SearchMusic(keyword, 5)
if err != nil {
ctx.SendChain(message.Text("查询歌曲失败!\nerr:", err))
return
}
if len(searchlist) == 0 {
ctx.SendChain(message.Text("歌曲没有查询到,请确认信息正确"))
return
}
var musicchoose []string
for musicName := range searchlist {
musicchoose = append(musicchoose, musicName)
}
savePath := cfg.MusicPath + listName + "/"
index := 0
if len(musicchoose) > 1 {
var msg []string
msg = append(msg, "搜索到相近的歌曲,请回复对应序号进行下载或回复取消")
for i, musicName := range musicchoose {
msg = append(msg, strconv.Itoa(i)+"."+musicName)
}
ctx.SendChain(message.Text(strings.Join(msg, "\n")))
next := zero.NewFutureEvent("message", 999, false, zero.RegexRule(`[0-4]|取消`), ctx.CheckSession())
recv, cancel := next.Repeat()
defer cancel()
wait := time.NewTimer(120 * time.Second)
answer := ""
for {
select {
case <-wait.C:
wait.Stop()
ctx.SendChain(message.Text("等待超时,取消下载"))
return
case c := <-recv:
wait.Stop()
answer = c.Event.Message.String()
if answer == "取消" {
ctx.SendChain(message.Text("已取消下载"))
return
}
index, _ = strconv.Atoi(answer)
}
if answer != "" {
break
}
}
}
musicName := musicchoose[index]
// 下载歌曲
err = wyy.DownloadMusic(searchlist[musicName], musicName, savePath)
if err == nil {
if cfg.Local {
// 下载歌词
_ = wyy.DownloadLrc(searchlist[musicName], musicName, savePath+"歌词/")
}
ctx.SendChain(message.Text("成功!"))
} else {
ctx.SendChain(message.Text(serviceErr, err))
}
})
// 从群文件下载歌曲
engine.OnRegex(`^上传歌曲\s*(.*)\s*到\s*(.*)$`, zero.OnlyGroup, zero.AdminPermission).SetBlock(true).Limit(ctxext.LimitByUser).
Handle(func(ctx *zero.Ctx) {
fileName := ctx.State["regex_matched"].([]string)[1]
listName := ctx.State["regex_matched"].([]string)[2]
// 判断群文件是否存在
fileSearchName, fileURL := getFileURLbyFileName(ctx, fileName)
if fileSearchName == "" {
ctx.SendChain(message.Text(serviceErr, "请确认群文件文件名称是否正确或存在"))
return
}
// 解析歌曲信息
music := strings.Split(fileSearchName, ".")
// 获取音乐后缀
musictype := music[len(music)-1]
if !strings.Contains(musictypelist, musictype) {
ctx.SendChain(message.Text(fileSearchName, "不是插件支持的后缀,请更改后缀"))
return
}
// 获取音乐信息
musicInfo := strings.Split(strings.ReplaceAll(fileSearchName, "."+musictype, ""), " - ")
infoNum := len(musicInfo)
if infoNum == 1 {
ctx.SendChain(message.Text(fileSearchName, "不符合命名规则,请更改名称"))
return
}
fileName = "歌名:" + musicInfo[0] + "\n歌手:" + musicInfo[1]
musicAlia := ""
if infoNum > 2 {
musicAlia = musicInfo[2]
fileName += "\n其他信息:\n" + strings.ReplaceAll(musicAlia, "&", "\n")
}
// 是否存在该歌单
if file.IsNotExist(cfg.MusicPath + listName) {
if !zero.SuperUserPermission(ctx) {
ctx.SendChain(message.Text("歌单名称错误。\n可以发送“歌单列表”获取歌单名称"))
return
}
ctx.SendChain(message.Text("歌单不存在,是否创建?(是/否)"))
next := zero.NewFutureEvent("message", 999, false, zero.RegexRule(`(是|否)`), ctx.CheckSession())
recv, cancel := next.Repeat()
defer cancel()
wait := time.NewTimer(120 * time.Second)
answer := ""
for {
select {
case <-wait.C:
wait.Stop()
ctx.SendChain(message.Text("等待超时,取消下载"))
return
case c := <-recv:
wait.Stop()
answer = c.Event.Message.String()
}
if answer == "否" {
ctx.SendChain(message.Text("下载已经取消"))
return
}
if answer != "" {
break
}
}
err = os.MkdirAll(cfg.MusicPath+listName, 0755)
if err != nil {
ctx.SendChain(message.Text(serviceErr, err))
return
}
}
// 下载歌曲
ctx.SendChain(message.Text("在群文件中找到了歌曲,信息如下:\n", fileName, "\n确认正确后回复“是/否”进行上传"))
next := zero.NewFutureEvent("message", 999, false, zero.RegexRule(`(是|否)`), ctx.CheckSession())
recv, cancel := next.Repeat()
defer cancel()
wait := time.NewTimer(120 * time.Second)
answer := ""
for {
select {
case <-wait.C:
wait.Stop()
ctx.SendChain(message.Text("等待超时,取消上传"))
return
case c := <-recv:
wait.Stop()
answer = c.Event.Message.String()
}
if answer == "否" {
ctx.SendChain(message.Text("上传已经取消"))
return
}
if answer != "" {
break
}
}
err = file.DownloadTo(fileURL, cfg.MusicPath+listName+"/"+fileSearchName)
if err == nil {
process.SleepAbout1sTo2s()
ctx.SendChain(message.Text("成功!"))
} else {
ctx.SendChain(message.Text(serviceErr, err))
}
})
engine.OnFullMatch("歌单列表").SetBlock(true).Limit(ctxext.LimitByGroup).
Handle(func(ctx *zero.Ctx) {
filelist, err := getlist(cfg.MusicPath)
if err != nil {
ctx.SendChain(message.Text(serviceErr, err))
return
}
/***********设置图片的大小和底色***********/
number := len(filelist)
fontSize := 20.0
if number < 10 {
number = 10
}
canvas := gg.NewContext(480, int(80+fontSize*float64(number)))
canvas.SetRGB(1, 1, 1) // 白色
canvas.Clear()
/***********下载字体,可以注销掉***********/
boldfd, err := file.GetLazyData(text.BoldFontFile, control.Md5File, true)
if err != nil {
ctx.SendChain(message.Text(serviceErr, err))
}
fd, err := file.GetLazyData(text.FontFile, control.Md5File, true)
if err != nil {
ctx.SendChain(message.Text(serviceErr, err))
}
/***********设置字体颜色为黑色***********/
canvas.SetRGB(0, 0, 0)
/***********设置字体大小,并获取字体高度用来定位***********/
if err = canvas.ParseFontFace(boldfd, fontSize); err != nil {
ctx.SendChain(message.Text(serviceErr, err))
return
}
_, h := canvas.MeasureString("序号\t\t歌单名\t\t\t歌曲数量\t\t网易云歌单ID")
/***********绘制标题***********/
canvas.DrawString("序号\t\t歌单名\t\t歌曲数量\t\t网易云歌单ID", 20, 50-h) // 放置在中间位置
canvas.DrawString("——————————————————————", 20, 70-h)
/***********设置字体大小,并获取字体高度用来定位***********/
if err = canvas.ParseFontFace(fd, fontSize); err != nil {
ctx.SendChain(message.Text(serviceErr, err))
return
}
_, h = canvas.MeasureString("焯")
j := 0
for i, listinfo := range filelist {
canvas.DrawString(strconv.Itoa(i), 15, float64(85+20*i)-h)
canvas.DrawString(listinfo.Name, 85, float64(85+20*i)-h)
canvas.DrawString(strconv.Itoa(listinfo.Number), 220, float64(85+20*i)-h)
if listinfo.ID != 0 {
canvas.DrawString(strconv.FormatInt(listinfo.ID, 10), 320, float64(85+20*i)-h)
}
j = i + 2
}
for _, dlist := range cfg.Defaultlist {
if dlist.GroupID == ctx.Event.GroupID {
canvas.DrawString("当前设置的默认歌单为: "+dlist.Name, 80, float64(85+20*j)-h)
}
}
data, err := imgfactory.ToBytes(canvas.Image())
if err != nil {
ctx.SendChain(message.Text(serviceErr, err))
return
}
if id := ctx.SendChain(message.ImageBytes(data)); id.ID() == 0 {
ctx.SendChain(message.Text("ERROR: 可能被风控了"))
}
})
engine.OnPrefix("设置猜歌默认歌单", zero.AdminPermission).SetBlock(true).
Handle(func(ctx *zero.Ctx) {
option := ctx.State["args"].(string)
gid := ctx.Event.GroupID
if file.IsNotExist(cfg.MusicPath + option) {
ctx.SendChain(message.Text("歌单名称错误,可以发送“歌单列表”获取歌单名称"))
return
}
cfg.Defaultlist = append(cfg.Defaultlist, dlist{
GroupID: gid,
Name: option,
})
err = saveConfig(cfgFile)
if err == nil {
ctx.SendChain(message.Text("成功!"))
} else {
ctx.SendChain(message.Text(serviceErr, err))
}
})
}
// 保存用户配置
func saveConfig(cfgFile string) error {
if reader, err := os.Create(cfgFile); err == nil {
err = json.NewEncoder(reader).Encode(&cfg)
if err != nil {
return err
}
} else {
return err
}
return nil
}
// 获取本地歌单列表
func getlist(pathOfMusic string) (list []listinfo, err error) {
wyyID := make(map[string]int64, 100)
for _, wyyinfo := range cfg.Playlist {
wyyID[wyyinfo.Name] = wyyinfo.ID
}
err = os.MkdirAll(pathOfMusic, 0755)
if err != nil {
return
}
files, err := os.ReadDir(pathOfMusic)
if err != nil {
return
}
if len(files) == 0 {
err = errors.New("所设置的歌库不存在任何歌单!")
return
}
for _, name := range files {
if !name.IsDir() {
continue
}
listName := name.Name()
listfiles, err := os.ReadDir(pathOfMusic + listName)
if err != nil {
continue
}
list = append(list, listinfo{
Name: listName,
Number: len(listfiles),
ID: wyyID[listName],
})
}
return
}
// 遍历群文件
func getFileURLbyFileName(ctx *zero.Ctx, fileName string) (fileSearchName, fileURL string) {
filesOfGroup := ctx.GetThisGroupRootFiles(ctx.Event.GroupID)
files := filesOfGroup.Get("files").Array()
folders := filesOfGroup.Get("folders").Array()
// 遍历当前目录的文件名
if len(files) != 0 {
for _, fileNameOflist := range files {
if strings.Contains(fileNameOflist.Get("file_name").String(), fileName) {
fileSearchName = fileNameOflist.Get("file_name").String()
fileURL = ctx.GetThisGroupFileUrl(fileNameOflist.Get("busid").Int(), fileNameOflist.Get("file_id").String())
return
}
}
}
// 遍历子文件夹
if len(folders) != 0 {
for _, folderNameOflist := range folders {
folderID := folderNameOflist.Get("folder_id").String()
fileSearchName, fileURL = getFileURLbyfolderID(ctx, fileName, folderID)
if fileSearchName != "" {
return
}
}
}
return
}
func getFileURLbyfolderID(ctx *zero.Ctx, fileName, folderid string) (fileSearchName, fileURL string) {
filesOfGroup := ctx.GetThisGroupFilesByFolder(folderid)
files := filesOfGroup.Get("files").Array()
folders := filesOfGroup.Get("folders").Array()
// 遍历当前目录的文件名
if len(files) != 0 {
for _, fileNameOflist := range files {
if strings.Contains(fileNameOflist.Get("file_name").String(), fileName) {
fileSearchName = fileNameOflist.Get("file_name").String()
fileURL = ctx.GetThisGroupFileUrl(fileNameOflist.Get("busid").Int(), fileNameOflist.Get("file_id").String())
return
}
}
}
// 遍历子文件夹
if len(folders) != 0 {
for _, folderNameOflist := range folders {
folderID := folderNameOflist.Get("folder_id").String()
fileSearchName, fileURL = getFileURLbyfolderID(ctx, fileName, folderID)
if fileSearchName != "" {
return
}
}
}
return
}

446
plugin/guessmusic/struct.go Normal file
View File

@@ -0,0 +1,446 @@
package guessmusic
// config内容
type config struct {
MusicPath string `json:"musicPath"`
APIURL string `json:"apiURL"`
Playlist []listRaw `json:"playlist"`
Defaultlist []dlist `json:"defaultlist"`
Local bool `json:"local"`
API bool `json:"api"`
Cookie string `json:"cookie"`
}
// 记录歌单绑定的网易云歌单ID
type listRaw struct {
Name string `json:"name"` // 歌单名称
ID int64 `json:"id"` // 歌单绑定的网易云ID
}
// 记录群默认猜歌
type dlist struct {
GroupID int64 `json:"gid"` // 群号
Name string `json:"name"` // 歌单名称
}
// 本地歌单列表信息
type listinfo struct {
Name string `json:"name"` // 歌单名称
Number int // 歌曲数量
ID int64 // 歌单绑定的歌曲ID
}
/*****************************************************************/
/***************NeteaseCloudMusicApi框架API************************/
/*****************************************************************/
// 获取登陆信息
type keyInfo struct {
Data struct {
Code int `json:"code"`
Unikey string `json:"unikey"`
} `json:"data"`
Code int `json:"code"`
}
type cookyInfo struct {
Code int `json:"code"`
Message string `json:"message"`
Cookie string `json:"cookie"`
}
type qrInfo struct {
Code int `json:"code"`
Data struct {
Qrurl string `json:"qrurl"`
Qrimg string `json:"qrimg"`
} `json:"data"`
}
// 获取歌单信息
type listInfoOfAPI struct {
Code int `json:"code"`
RelatedVideos interface{} `json:"relatedVideos"`
Playlist struct {
ID int64 `json:"id"`
Name string `json:"name"`
CoverImgID int64 `json:"coverImgId"`
CoverImgURL string `json:"coverImgUrl"`
CoverImgIDStr string `json:"coverImgId_str"`
AdType int `json:"adType"`
UserID int `json:"userId"`
CreateTime int64 `json:"createTime"`
Status int `json:"status"`
OpRecommend bool `json:"opRecommend"`
HighQuality bool `json:"highQuality"`
NewImported bool `json:"newImported"`
UpdateTime int64 `json:"updateTime"`
TrackCount int `json:"trackCount"`
SpecialType int `json:"specialType"`
Privacy int `json:"privacy"`
TrackUpdateTime int64 `json:"trackUpdateTime"`
CommentThreadID string `json:"commentThreadId"`
PlayCount int `json:"playCount"`
TrackNumberUpdateTime int64 `json:"trackNumberUpdateTime"`
SubscribedCount int `json:"subscribedCount"`
CloudTrackCount int `json:"cloudTrackCount"`
Ordered bool `json:"ordered"`
Description string `json:"description"`
Tags []string `json:"tags"`
UpdateFrequency interface{} `json:"updateFrequency"`
BackgroundCoverID int `json:"backgroundCoverId"`
BackgroundCoverURL interface{} `json:"backgroundCoverUrl"`
TitleImage int `json:"titleImage"`
TitleImageURL interface{} `json:"titleImageUrl"`
EnglishTitle interface{} `json:"englishTitle"`
OfficialPlaylistType interface{} `json:"officialPlaylistType"`
Subscribers []struct {
DefaultAvatar bool `json:"defaultAvatar"`
Province int `json:"province"`
AuthStatus int `json:"authStatus"`
Followed bool `json:"followed"`
AvatarURL string `json:"avatarUrl"`
AccountStatus int `json:"accountStatus"`
Gender int `json:"gender"`
City int `json:"city"`
Birthday int `json:"birthday"`
UserID int `json:"userId"`
UserType int `json:"userType"`
Nickname string `json:"nickname"`
Signature string `json:"signature"`
Description string `json:"description"`
DetailDescription string `json:"detailDescription"`
AvatarImgID int64 `json:"avatarImgId"`
BackgroundImgID int64 `json:"backgroundImgId"`
BackgroundURL string `json:"backgroundUrl"`
Authority int `json:"authority"`
Mutual bool `json:"mutual"`
ExpertTags interface{} `json:"expertTags"`
Experts interface{} `json:"experts"`
DjStatus int `json:"djStatus"`
VipType int `json:"vipType"`
RemarkName interface{} `json:"remarkName"`
AuthenticationTypes int `json:"authenticationTypes"`
AvatarDetail interface{} `json:"avatarDetail"`
Anchor bool `json:"anchor"`
BackgroundImgIDStr string `json:"backgroundImgIdStr"`
AvatarImgIDStr string `json:"avatarImgIdStr"`
AvatarImgIDString string `json:"AvatarImgIDString"`
} `json:"subscribers"`
Subscribed interface{} `json:"subscribed"`
Creator struct {
DefaultAvatar bool `json:"defaultAvatar"`
Province int `json:"province"`
AuthStatus int `json:"authStatus"`
Followed bool `json:"followed"`
AvatarURL string `json:"avatarUrl"`
AccountStatus int `json:"accountStatus"`
Gender int `json:"gender"`
City int `json:"city"`
Birthday int `json:"birthday"`
UserID int `json:"userId"`
UserType int `json:"userType"`
Nickname string `json:"nickname"`
Signature string `json:"signature"`
Description string `json:"description"`
DetailDescription string `json:"detailDescription"`
AvatarImgID int64 `json:"avatarImgId"`
BackgroundImgID int64 `json:"backgroundImgId"`
BackgroundURL string `json:"backgroundUrl"`
Authority int `json:"authority"`
Mutual bool `json:"mutual"`
ExpertTags interface{} `json:"expertTags"`
Experts interface{} `json:"experts"`
DjStatus int `json:"djStatus"`
VipType int `json:"vipType"`
RemarkName interface{} `json:"remarkName"`
AuthenticationTypes int `json:"authenticationTypes"`
AvatarDetail struct {
UserType int `json:"userType"`
IdentityLevel int `json:"identityLevel"`
IdentityIconURL string `json:"identityIconUrl"`
} `json:"avatarDetail"`
Anchor bool `json:"anchor"`
BackgroundImgIDStr string `json:"backgroundImgIdStr"`
AvatarImgIDStr string `json:"avatarImgIdStr"`
AvatarImgIDString string `json:"AvatarImgIDString"`
} `json:"creator"`
Tracks []struct {
Name string `json:"name"`
ID int `json:"id"`
Pst int `json:"pst"`
T int `json:"t"`
Ar []struct {
ID int `json:"id"`
Name string `json:"name"`
Tns []interface{} `json:"tns"`
Alias []interface{} `json:"alias"`
} `json:"ar"`
Alia []string `json:"alia"`
Pop int `json:"pop"`
St int `json:"st"`
Rt string `json:"rt"`
Fee int `json:"fee"`
V int `json:"v"`
Crbt interface{} `json:"crbt"`
Cf string `json:"cf"`
Al struct {
ID int `json:"id"`
Name string `json:"name"`
PicURL string `json:"picUrl"`
Tns []interface{} `json:"tns"`
PicStr string `json:"pic_str"`
Pic int64 `json:"pic"`
} `json:"al"`
Dt int `json:"dt"`
H struct {
Br int `json:"br"`
Fid int `json:"fid"`
Size int `json:"size"`
Vd float64 `json:"vd"`
Sr int `json:"sr"`
} `json:"h"`
M struct {
Br int `json:"br"`
Fid int `json:"fid"`
Size int `json:"size"`
Vd float64 `json:"vd"`
Sr int `json:"sr"`
} `json:"m"`
L struct {
Br int `json:"br"`
Fid int `json:"fid"`
Size int `json:"size"`
Vd float64 `json:"vd"`
Sr int `json:"sr"`
} `json:"l"`
Sq interface{} `json:"sq"`
Hr interface{} `json:"hr"`
A interface{} `json:"a"`
Cd string `json:"cd"`
No int `json:"no"`
RtURL interface{} `json:"rtUrl"`
Ftype int `json:"ftype"`
RtUrls []interface{} `json:"rtUrls"`
DjID int `json:"djId"`
Copyright int `json:"copyright"`
SID int `json:"s_id"`
Mark int `json:"mark"`
OriginCoverType int `json:"originCoverType"`
OriginSongSimpleData interface{} `json:"originSongSimpleData"`
TagPicList interface{} `json:"tagPicList"`
ResourceState bool `json:"resourceState"`
Version int `json:"version"`
SongJumpInfo interface{} `json:"songJumpInfo"`
EntertainmentTags interface{} `json:"entertainmentTags"`
Single int `json:"single"`
NoCopyrightRcmd interface{} `json:"noCopyrightRcmd"`
Alg interface{} `json:"alg"`
Rtype int `json:"rtype"`
Rurl interface{} `json:"rurl"`
Mst int `json:"mst"`
Cp int `json:"cp"`
Mv int `json:"mv"`
PublishTime int64 `json:"publishTime"`
Tns []string `json:"tns,omitempty"`
} `json:"tracks"`
VideoIds interface{} `json:"videoIds"`
Videos interface{} `json:"videos"`
TrackIds []struct {
ID int `json:"id"`
V int `json:"v"`
T int `json:"t"`
At int64 `json:"at"`
Alg interface{} `json:"alg"`
UID int `json:"uid"`
RcmdReason string `json:"rcmdReason"`
Sc interface{} `json:"sc"`
Lr int `json:"lr,omitempty"`
} `json:"trackIds"`
ShareCount int `json:"shareCount"`
CommentCount int `json:"commentCount"`
RemixVideo interface{} `json:"remixVideo"`
SharedUsers interface{} `json:"sharedUsers"`
HistorySharedUsers interface{} `json:"historySharedUsers"`
GradeStatus string `json:"gradeStatus"`
Score interface{} `json:"score"`
AlgTags interface{} `json:"algTags"`
} `json:"playlist"`
Urls interface{} `json:"urls"`
Privileges []struct {
ID int `json:"id"`
Fee int `json:"fee"`
Payed int `json:"payed"`
RealPayed int `json:"realPayed"`
St int `json:"st"`
Pl int `json:"pl"`
Dl int `json:"dl"`
Sp int `json:"sp"`
Cp int `json:"cp"`
Subp int `json:"subp"`
Cs bool `json:"cs"`
Maxbr int `json:"maxbr"`
Fl int `json:"fl"`
Pc interface{} `json:"pc"`
Toast bool `json:"toast"`
Flag int `json:"flag"`
PaidBigBang bool `json:"paidBigBang"`
PreSell bool `json:"preSell"`
PlayMaxbr int `json:"playMaxbr"`
DownloadMaxbr int `json:"downloadMaxbr"`
MaxBrLevel string `json:"maxBrLevel"`
PlayMaxBrLevel string `json:"playMaxBrLevel"`
DownloadMaxBrLevel string `json:"downloadMaxBrLevel"`
PlLevel string `json:"plLevel"`
DlLevel string `json:"dlLevel"`
FlLevel string `json:"flLevel"`
Rscl int `json:"rscl"`
FreeTrialPrivilege struct {
ResConsumable bool `json:"resConsumable"`
UserConsumable bool `json:"userConsumable"`
ListenType interface{} `json:"listenType"`
} `json:"freeTrialPrivilege"`
ChargeInfoList []struct {
Rate int `json:"rate"`
ChargeURL interface{} `json:"chargeUrl"`
ChargeMessage interface{} `json:"chargeMessage"`
ChargeType int `json:"chargeType"`
} `json:"chargeInfoList"`
} `json:"privileges"`
SharedPrivilege interface{} `json:"sharedPrivilege"`
ResEntrance interface{} `json:"resEntrance"`
}
// 获取歌单列表
type musicListOfApI struct {
Songs []struct {
Name string `json:"name"`
ID int `json:"id"`
Pst int `json:"pst"`
T int `json:"t"`
Ar []struct {
ID int `json:"id"`
Name string `json:"name"`
Tns []interface{} `json:"tns"`
Alias []interface{} `json:"alias"`
} `json:"ar"`
Alia []string `json:"alia"`
Pop int `json:"pop"`
St int `json:"st"`
Rt string `json:"rt"`
Fee int `json:"fee"`
V int `json:"v"`
Crbt interface{} `json:"crbt"`
Cf string `json:"cf"`
Al struct {
ID int `json:"id"`
Name string `json:"name"`
PicURL string `json:"picUrl"`
Tns []interface{} `json:"tns"`
PicStr string `json:"pic_str"`
Pic int64 `json:"pic"`
} `json:"al"`
Dt int `json:"dt"`
H struct {
Br int `json:"br"`
Fid int `json:"fid"`
Size int `json:"size"`
Vd float32 `json:"vd"`
Sr int `json:"sr"`
} `json:"h"`
M struct {
Br int `json:"br"`
Fid int `json:"fid"`
Size int `json:"size"`
Vd float32 `json:"vd"`
Sr int `json:"sr"`
} `json:"m"`
L struct {
Br int `json:"br"`
Fid int `json:"fid"`
Size int `json:"size"`
Vd float32 `json:"vd"`
Sr int `json:"sr"`
} `json:"l"`
Sq interface{} `json:"sq"`
Hr interface{} `json:"hr"`
A interface{} `json:"a"`
Cd string `json:"cd"`
No int `json:"no"`
RtURL interface{} `json:"rtUrl"`
Ftype int `json:"ftype"`
RtUrls []interface{} `json:"rtUrls"`
DjID int `json:"djId"`
Copyright int `json:"copyright"`
SID int `json:"s_id"`
Mark int `json:"mark"`
OriginCoverType int `json:"originCoverType"`
OriginSongSimpleData interface{} `json:"originSongSimpleData"`
TagPicList interface{} `json:"tagPicList"`
ResourceState bool `json:"resourceState"`
Version int `json:"version"`
SongJumpInfo interface{} `json:"songJumpInfo"`
EntertainmentTags interface{} `json:"entertainmentTags"`
AwardTags interface{} `json:"awardTags"`
Single int `json:"single"`
NoCopyrightRcmd interface{} `json:"noCopyrightRcmd"`
Rtype int `json:"rtype"`
Rurl interface{} `json:"rurl"`
Mst int `json:"mst"`
Cp int `json:"cp"`
Mv int `json:"mv"`
PublishTime int64 `json:"publishTime"`
Tns []string `json:"tns,omitempty"`
} `json:"songs"`
Privileges []struct {
ID int `json:"id"`
Fee int `json:"fee"`
Payed int `json:"payed"`
St int `json:"st"`
Pl int `json:"pl"`
Dl int `json:"dl"`
Sp int `json:"sp"`
Cp int `json:"cp"`
Subp int `json:"subp"`
Cs bool `json:"cs"`
Maxbr int `json:"maxbr"`
Fl int `json:"fl"`
Toast bool `json:"toast"`
Flag int `json:"flag"`
PreSell bool `json:"preSell"`
PlayMaxbr int `json:"playMaxbr"`
DownloadMaxbr int `json:"downloadMaxbr"`
MaxBrLevel string `json:"maxBrLevel"`
PlayMaxBrLevel string `json:"playMaxBrLevel"`
DownloadMaxBrLevel string `json:"downloadMaxBrLevel"`
PlLevel string `json:"plLevel"`
DlLevel string `json:"dlLevel"`
FlLevel string `json:"flLevel"`
Rscl int `json:"rscl"`
FreeTrialPrivilege struct {
ResConsumable bool `json:"resConsumable"`
UserConsumable bool `json:"userConsumable"`
ListenType interface{} `json:"listenType"`
} `json:"freeTrialPrivilege"`
ChargeInfoList []struct {
Rate int `json:"rate"`
ChargeURL interface{} `json:"chargeUrl"`
ChargeMessage interface{} `json:"chargeMessage"`
ChargeType int `json:"chargeType"`
} `json:"chargeInfoList"`
} `json:"privileges"`
Code int `json:"code"`
}
/*****************************************************************/
/*********************独角兽API随机抽歌信息**************************/
/*****************************************************************/
// 独角兽API随机抽歌信息
type ovooaData struct {
Code int `json:"code"`
Text string `json:"text"`
Data struct {
Song string `json:"song"`
Singer string `json:"singer"`
Cover string `json:"cover"`
Music string `json:"Music"`
ID int `json:"id"`
} `json:"data"`
}

119
plugin/heisi/heisi.go Normal file
View File

@@ -0,0 +1,119 @@
// Package heisi 黑丝
package heisi
import (
"errors"
"math/rand"
"os"
"strconv"
"time"
"unsafe"
"github.com/FloatTech/AnimeAPI/setu"
fbctxext "github.com/FloatTech/floatbox/ctxext"
"github.com/FloatTech/floatbox/file"
"github.com/FloatTech/floatbox/web"
ctrl "github.com/FloatTech/zbpctrl"
"github.com/FloatTech/zbputils/control"
"github.com/FloatTech/zbputils/ctxext"
zero "github.com/wdvxdr1123/ZeroBot"
"github.com/wdvxdr1123/ZeroBot/message"
)
var (
heisiPic []item
baisiPic []item
jkPic []item
jurPic []item
zukPic []item
mcnPic []item
fileList = [...]string{"heisi.bin", "baisi.bin", "jk.bin", "jur.bin", "zuk.bin", "mcn.bin"}
)
func init() { // 插件主体
p, err := setu.NewPool(setu.DefaultPoolDir,
func(s string) (string, error) {
if s != "黑丝" && s != "白丝" && s != "jk" && s != "巨乳" && s != "足控" && s != "网红" {
return "", errors.New("invalid call")
}
typ := setu.DefaultPoolDir + "/" + s
if file.IsNotExist(typ) {
err := os.MkdirAll(typ, 0755)
if err != nil {
return "", err
}
}
var pic item
switch s {
case "黑丝":
pic = heisiPic[rand.Intn(len(heisiPic))]
case "白丝":
pic = baisiPic[rand.Intn(len(baisiPic))]
case "jk":
pic = jkPic[rand.Intn(len(jkPic))]
case "巨乳":
pic = jurPic[rand.Intn(len(jurPic))]
case "足控":
pic = zukPic[rand.Intn(len(zukPic))]
case "网红":
pic = mcnPic[rand.Intn(len(mcnPic))]
}
return pic.String(), nil
}, func(s string) ([]byte, error) {
return web.RequestDataWith(web.NewTLS12Client(), s, "GET", "http://hs.heisiwu.com/", web.RandUA(), nil)
}, time.Minute)
if err != nil {
panic(err)
}
engine := control.Register("heisi", &ctrl.Options[*zero.Ctx]{
DisableOnDefault: false,
Brief: "黑丝",
Help: "- 来点黑丝\n- 来点白丝\n- 来点jk\n- 来点巨乳\n- 来点足控\n- 来点网红",
PublicDataFolder: "Heisi",
})
engine.OnFullMatchGroup([]string{"来点黑丝", "来点白丝", "来点jk", "来点巨乳", "来点足控", "来点网红"}, zero.OnlyGroup, fbctxext.DoOnceOnSuccess(func(ctx *zero.Ctx) bool {
for i, filePath := range fileList {
data, err := engine.GetLazyData(filePath, true)
if err != nil {
ctx.SendChain(message.Text("ERROR: ", err))
return false
}
if len(data)%10 != 0 {
ctx.SendChain(message.Text("ERROR: invalid data " + strconv.Itoa(i)))
return false
}
s := (*slice)(unsafe.Pointer(&data))
s.len /= 10
s.cap /= 10
switch i {
case 0:
heisiPic = *(*[]item)(unsafe.Pointer(s))
case 1:
baisiPic = *(*[]item)(unsafe.Pointer(s))
case 2:
jkPic = *(*[]item)(unsafe.Pointer(s))
case 3:
jurPic = *(*[]item)(unsafe.Pointer(s))
case 4:
zukPic = *(*[]item)(unsafe.Pointer(s))
case 5:
mcnPic = *(*[]item)(unsafe.Pointer(s))
}
}
return true
})).SetBlock(true).
Handle(func(ctx *zero.Ctx) {
matched := ctx.State["matched"].(string)
pic, err := p.Roll(matched[3*2:])
if err != nil {
ctx.SendChain(message.Text("ERROR: ", err))
return
}
m := message.Message{ctxext.FakeSenderForwardNode(ctx, message.Image("file:///"+file.BOTPATH+"/"+pic))}
if id := ctx.Send(m).ID(); id == 0 {
ctx.SendChain(message.Text("ERROR: 可能被风控或下载图片用时过长,请耐心等待"))
}
})
}

48
plugin/heisi/packer.go Normal file
View File

@@ -0,0 +1,48 @@
package heisi
import (
"encoding/binary"
"encoding/hex"
"fmt"
"math/bits"
)
const (
template2021 = "http://hs.heisiwu.com/wp-content/uploads/%4d/%02d/%4d%02d16%06d-611a3%8s.jpg"
templategeneral = "http://hs.heisiwu.com/wp-content/uploads/%4d/%02d/%015x"
)
type item [10]byte
// String item to url
func (it item) String() string {
year, month := int((it[0]>>4)&0x0f), int(it[0]&0x0f)
year += 2021
if year == 2021 {
num := binary.BigEndian.Uint32(it[1:5])
dstr := hex.EncodeToString(it[5:9])
return fmt.Sprintf(template2021, year, month, year, month, num, dstr)
}
d := binary.BigEndian.Uint64(it[1:9])
isscaled := it[9]&0x80 > 0
num := int(it[9] & 0x7f)
trestore := fmt.Sprintf(templategeneral, year, month, d&0x0fffffff_ffffffff)
if num > 0 {
trestore += fmt.Sprintf("-%d", num)
}
if isscaled {
trestore += "-scaled"
}
d = bits.RotateLeft64(d, 4) & 0x0f
switch d {
case 0:
trestore += ".jpg"
case 1:
trestore += ".png"
case 2:
trestore += ".webp"
default:
return "invalid ext"
}
return trestore
}

15
plugin/heisi/slice.go Normal file
View File

@@ -0,0 +1,15 @@
package heisi
import "unsafe"
// slice is the runtime representation of a slice.
// It cannot be used safely or portably and its representation may
// change in a later release.
//
// Unlike reflect.SliceHeader, its Data field is sufficient to guarantee the
// data it references will not be garbage collected.
type slice struct {
data unsafe.Pointer
len int
cap int
}

View File

@@ -11,11 +11,11 @@ import (
"github.com/wdvxdr1123/ZeroBot/message"
"github.com/wdvxdr1123/ZeroBot/utils/helper"
"github.com/FloatTech/floatbox/file"
"github.com/FloatTech/floatbox/web"
ctrl "github.com/FloatTech/zbpctrl"
"github.com/FloatTech/zbputils/control"
"github.com/FloatTech/zbputils/ctxext"
"github.com/FloatTech/zbputils/file"
"github.com/FloatTech/zbputils/web"
)
var reqconf = [...]string{"GET", "https://hs.fbigame.com",
@@ -43,8 +43,8 @@ const (
func init() {
engine := control.Register("hs", &ctrl.Options[*zero.Ctx]{
DisableOnDefault: false,
Help: "炉石\n" +
"- 搜卡[xxxx]\n" +
Brief: "炉石搜卡",
Help: "- 搜卡[xxxx]\n" +
"- [卡组代码xxx]\n" +
"- 更多搜卡指令参数https://hs.fbigame.com/misc/searchhelp",
PrivateDataFolder: "hs",
@@ -67,7 +67,7 @@ func init() {
data, err := web.RequestDataWith(web.NewDefaultClient(),
`https://res.fbigame.com/hs/v13/`+cid+`.png?auth_key=`+
gjson.Get(g, `list.`+strconv.Itoa(i)+`.auth_key`).String(),
reqconf[0], reqconf[1], reqconf[2])
reqconf[0], reqconf[1], reqconf[2], nil)
if err == nil {
err = os.WriteFile(cachefile, data, 0644)
}
@@ -81,7 +81,7 @@ func init() {
ctx.Event.GroupID,
sk,
).Get("message_id").Int(); id == 0 {
ctx.SendChain(message.Text("ERROR:可能被风控了"))
ctx.SendChain(message.Text("ERROR: 可能被风控了"))
}
})
// 卡组
@@ -95,10 +95,10 @@ func init() {
}
func sh(s string) string {
data, err := web.RequestDataWith(web.NewDefaultClient(), "https://hs.fbigame.com", reqconf[0], reqconf[1], reqconf[2])
data, err := web.RequestDataWith(web.NewDefaultClient(), "https://hs.fbigame.com", reqconf[0], reqconf[1], reqconf[2], nil)
if err == nil {
url := hs + para + "&hash=" + strings.SplitN(strings.SplitN(helper.BytesToString(data), `var hash = "`, 2)[1], `"`, 2)[0] + "&search=" + s
r, err := web.RequestDataWith(web.NewDefaultClient(), url, reqconf[0], reqconf[1], reqconf[2])
r, err := web.RequestDataWith(web.NewDefaultClient(), url, reqconf[0], reqconf[1], reqconf[2], nil)
if err == nil {
return helper.BytesToString(r)
}
@@ -107,10 +107,10 @@ func sh(s string) string {
}
func kz(s string) string {
data, err := web.RequestDataWith(web.NewDefaultClient(), "https://hs.fbigame.com", reqconf[0], reqconf[1], reqconf[2])
data, err := web.RequestDataWith(web.NewDefaultClient(), "https://hs.fbigame.com", reqconf[0], reqconf[1], reqconf[2], nil)
if err == nil {
url := hs + para + "mod=general_deck_image&deck_code=" + s + "&deck_text=&hash=" + strings.SplitN(strings.SplitN(helper.BytesToString(data), `var hash = "`, 2)[1], `"`, 2)[0] + "&search=" + s
r, err := web.RequestDataWith(web.NewDefaultClient(), url, reqconf[0], reqconf[1], reqconf[2])
r, err := web.RequestDataWith(web.NewDefaultClient(), url, reqconf[0], reqconf[1], reqconf[2], nil)
if err == nil {
return "base64://" + gjson.Get(helper.BytesToString(r), "img").String()
}

View File

@@ -2,6 +2,7 @@
package hyaku
import (
"bytes"
"encoding/csv"
"fmt"
"math/rand"
@@ -10,18 +11,16 @@ import (
"strconv"
"unsafe"
"github.com/FloatTech/floatbox/binary"
ctrl "github.com/FloatTech/zbpctrl"
"github.com/FloatTech/zbputils/binary"
"github.com/FloatTech/zbputils/control"
"github.com/FloatTech/zbputils/ctxext"
"github.com/FloatTech/zbputils/file"
zero "github.com/wdvxdr1123/ZeroBot"
"github.com/wdvxdr1123/ZeroBot/message"
)
const bed = "https://gitcode.net/u011570312/OguraHyakuninIsshu/-/raw/master/"
//nolint: asciicheck
type line struct {
番号, 歌人, 上の句, 下の句, 上の句ひらがな, 下の句ひらがな string
}
@@ -54,70 +53,83 @@ var lines [100]*line
func init() {
engine := control.Register("hyaku", &ctrl.Options[*zero.Ctx]{
DisableOnDefault: false,
Help: "百人一首\n" +
"- 百人一首(随机发一首)\n" +
Brief: "百人一首",
Help: "- 百人一首(随机发一首)\n" +
"- 百人一首之n",
PrivateDataFolder: "hyaku",
})
csvfile := engine.DataFolder() + "hyaku.csv"
go func() {
if file.IsNotExist(csvfile) {
err := file.DownloadTo(bed+"小倉百人一首.csv", csvfile, true)
if err != nil {
_ = os.Remove(csvfile)
panic(err)
}
}
f, err := os.Open(csvfile)
if err != nil {
panic(err)
}
records, err := csv.NewReader(f).ReadAll()
if err != nil {
panic(err)
}
_ = f.Close()
records = records[1:] // skip title
if len(records) != 100 {
err := os.MkdirAll(engine.DataFolder()+"img", 0755)
if err != nil {
panic(err)
}
data, err := engine.GetCustomLazyData(bed, "小倉百人一首.csv")
if err != nil {
panic(err)
}
records, err := csv.NewReader(bytes.NewReader(data)).ReadAll()
if err != nil {
panic(err)
}
records = records[1:] // skip title
if len(records) != 100 {
panic("invalid csvfile")
}
for j, r := range records {
if len(r) != 6 {
panic("invalid csvfile")
}
for j, r := range records {
if len(r) != 6 {
panic("invalid csvfile")
}
i, err := strconv.Atoi(r[0])
if err != nil {
panic(err)
}
i--
if j != i {
panic("invalid csvfile")
}
lines[i] = (*line)(*(*unsafe.Pointer)(unsafe.Pointer(&r)))
i, err := strconv.Atoi(r[0])
if err != nil {
panic(err)
}
}()
i--
if j != i {
panic("invalid csvfile")
}
lines[i] = (*line)(*(*unsafe.Pointer)(unsafe.Pointer(&r)))
}
engine.OnFullMatch("百人一首").SetBlock(true).Limit(ctxext.LimitByUser).Handle(func(ctx *zero.Ctx) {
i := rand.Intn(100)
img0, err := engine.GetCustomLazyData(bed, fmt.Sprintf("img/%03d.jpg", i+1))
if err != nil {
ctx.SendChain(message.Text("ERROR: ", err))
return
}
img1, err := engine.GetCustomLazyData(bed, fmt.Sprintf("img/%03d.png", i+1))
if err != nil {
ctx.SendChain(message.Text("ERROR: ", err))
return
}
ctx.SendChain(
message.Image(fmt.Sprintf(bed+"img/%03d.jpg", i+1)),
message.ImageBytes(img0),
message.Text("\n", lines[i]),
message.Image(fmt.Sprintf(bed+"img/%03d.png", i+1)),
message.ImageBytes(img1),
)
})
engine.OnRegex(`^百人一首之\s?(\d+)$`).SetBlock(true).Limit(ctxext.LimitByUser).Handle(func(ctx *zero.Ctx) {
i, err := strconv.Atoi(ctx.State["regex_matched"].([]string)[1])
if err != nil {
ctx.SendChain(message.Text("ERROR:", err))
ctx.SendChain(message.Text("ERROR: ", err))
return
}
if i > 100 || i < 1 {
ctx.SendChain(message.Text("ERROR:超出范围"))
ctx.SendChain(message.Text("ERROR: 超出范围"))
return
}
img0, err := engine.GetCustomLazyData(bed, fmt.Sprintf("img/%03d.jpg", i))
if err != nil {
ctx.SendChain(message.Text("ERROR: ", err))
return
}
img1, err := engine.GetCustomLazyData(bed, fmt.Sprintf("img/%03d.png", i))
if err != nil {
ctx.SendChain(message.Text("ERROR: ", err))
return
}
ctx.SendChain(
message.Image(fmt.Sprintf(bed+"img/%03d.jpg", i)),
message.ImageBytes(img0),
message.Text("\n", lines[i-1]),
message.Image(fmt.Sprintf(bed+"img/%03d.png", i)),
message.ImageBytes(img1),
)
})
}

View File

@@ -11,19 +11,19 @@ import (
"regexp"
"strings"
"github.com/lucas-clemente/quic-go/http3"
"github.com/quic-go/quic-go/http3"
zero "github.com/wdvxdr1123/ZeroBot"
"github.com/wdvxdr1123/ZeroBot/message"
"github.com/FloatTech/AnimeAPI/pixiv"
"github.com/FloatTech/floatbox/binary"
"github.com/FloatTech/floatbox/web"
ctrl "github.com/FloatTech/zbpctrl"
"github.com/FloatTech/zbputils/binary"
"github.com/FloatTech/zbputils/control"
"github.com/FloatTech/zbputils/ctxext"
"github.com/FloatTech/zbputils/img/pool"
"github.com/FloatTech/zbputils/web"
)
type resultjson struct {
@@ -64,21 +64,25 @@ var hrefre = regexp.MustCompile(`<a href=".*">`)
func init() {
control.Register("imgfinder", &ctrl.Options[*zero.Ctx]{
DisableOnDefault: false,
Help: "关键字搜图\n" +
"- 来张 [xxx]",
Brief: "关键字搜图",
Help: "- 来张 [xxx]",
}).OnRegex(`^来张\s?(.*)$`, zero.AdminPermission).SetBlock(true).
Handle(func(ctx *zero.Ctx) {
keyword := ctx.State["regex_matched"].([]string)[1]
soutujson, err := soutuapi(keyword)
if err != nil {
ctx.SendChain(message.Text("ERROR:", err))
ctx.SendChain(message.Text("ERROR: ", err))
return
}
rannum := rand.Intn(len(soutujson.Data.Illusts))
il := soutujson.Data.Illusts[rannum]
illust, err := pixiv.Works(il.ID)
if err != nil {
ctx.SendChain(message.Text("ERROR:", err))
ctx.SendChain(message.Text("ERROR: ", err))
return
}
if len(illust.ImageUrls) == 0 {
ctx.SendChain(message.Text("ERROR: nil image url"))
return
}
u := illust.ImageUrls[0]
@@ -94,14 +98,14 @@ func init() {
"标题: ", il.Title, "\n",
"副标题: ", il.AltTitle, "\n",
"ID: ", il.ID, "\n",
"画师: ", illust.UserName, " (", illust.UserId, ")", "\n",
"画师: ", illust.UserName, " (", illust.UserID, ")", "\n",
"分级:", il.Sanity, "\n",
hrefre.ReplaceAllString(strings.ReplaceAll(strings.ReplaceAll(il.Description, "<br />", "\n"), "</a>", ""), ""),
printtags(reflect.ValueOf(&il.Tags)),
),
), ctxext.GetFirstMessageInForward(ctx))
if err != nil {
ctx.SendChain(message.Text("ERROR:", err))
ctx.SendChain(message.Text("ERROR: ", err))
return
}
})
@@ -115,6 +119,7 @@ func soutuapi(keyword string) (r resultjson, err error) {
"GET",
"https://pixivel.moe/",
"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/94.0.4606.61 Safari/537.36",
nil,
)
if err != nil {
return

View File

@@ -11,8 +11,8 @@ import (
func init() {
en := control.Register("inject", &ctrl.Options[*zero.Ctx]{
DisableOnDefault: false,
Help: "注入指令\n" +
"- run[CQ码]",
Brief: "注入指令",
Help: "- run[CQ码]",
})
// 运行 CQ 码
en.OnPrefix("run", zero.SuperUserPermission).SetBlock(true).

View File

@@ -8,10 +8,10 @@ import (
"strconv"
"time"
"github.com/FloatTech/floatbox/binary"
fcext "github.com/FloatTech/floatbox/ctxext"
ctrl "github.com/FloatTech/zbpctrl"
"github.com/FloatTech/zbputils/binary"
"github.com/FloatTech/zbputils/control"
"github.com/FloatTech/zbputils/ctxext"
"github.com/antchfx/htmlquery"
"github.com/sirupsen/logrus"
zero "github.com/wdvxdr1123/ZeroBot"
@@ -25,26 +25,27 @@ const (
func init() {
engine := control.Register("jandan", &ctrl.Options[*zero.Ctx]{
DisableOnDefault: false,
Help: "煎蛋网无聊图\n- 来份[屌|弔|吊]图\n- 更新[屌|弔|吊]图\n",
Brief: "煎蛋网无聊图",
Help: "- 来份[屌|弔|吊]图\n- 更新[屌|弔|吊]图\n",
PublicDataFolder: "Jandan",
})
getdb := ctxext.DoOnceOnSuccess(func(ctx *zero.Ctx) bool {
getdb := fcext.DoOnceOnSuccess(func(ctx *zero.Ctx) bool {
db.DBPath = engine.DataFolder() + "pics.db"
_, _ = engine.GetLazyData("pics.db", false)
err := db.Open(time.Hour * 24)
if err != nil {
ctx.SendChain(message.Text("ERROR:", err))
ctx.SendChain(message.Text("ERROR: ", err))
return false
}
err = db.Create("picture", &picture{})
if err != nil {
ctx.SendChain(message.Text("ERROR:", err))
ctx.SendChain(message.Text("ERROR: ", err))
return false
}
n, err := db.Count("picture")
if err != nil {
ctx.SendChain(message.Text("ERROR:", err))
ctx.SendChain(message.Text("ERROR: ", err))
return false
}
logrus.Printf("[jandan]读取%d张图片", n)
@@ -55,7 +56,7 @@ func init() {
Handle(func(ctx *zero.Ctx) {
u, err := getRandomPicture()
if err != nil {
ctx.SendChain(message.Text("ERROR:", err))
ctx.SendChain(message.Text("ERROR: ", err))
return
}
ctx.SendChain(message.Image(u))
@@ -67,13 +68,13 @@ func init() {
webpageURL := api
doc, err := htmlquery.LoadURL(webpageURL)
if err != nil {
ctx.SendChain(message.Text("ERROR:", err))
ctx.SendChain(message.Text("ERROR: ", err))
return
}
re := regexp.MustCompile(`\d+`)
pageTotal, err := strconv.Atoi(re.FindString(htmlquery.FindOne(doc, "//*[@id='comments']/div[2]/div/span[@class='current-comment-page']/text()").Data))
if err != nil {
ctx.SendChain(message.Text("ERROR:", err))
ctx.SendChain(message.Text("ERROR: ", err))
return
}
LOOP:
@@ -81,12 +82,12 @@ func init() {
logrus.Debugln("[jandan]", fmt.Sprintf("处理第%d/%d页...", i, pageTotal))
doc, err = htmlquery.LoadURL(webpageURL)
if err != nil {
ctx.SendChain(message.Text("ERROR:", err))
ctx.SendChain(message.Text("ERROR: ", err))
return
}
picList, err := htmlquery.QueryAll(doc, "//*[@class='view_img_link']")
if err != nil {
ctx.SendChain(message.Text("ERROR:", err))
ctx.SendChain(message.Text("ERROR: ", err))
return
}
if len(picList) != 0 {

65
plugin/jiami/jiami.go Normal file
View File

@@ -0,0 +1,65 @@
// Package jiami 兽语加密与解密
package jiami
import (
"encoding/json"
"fmt"
"github.com/FloatTech/floatbox/web"
ctrl "github.com/FloatTech/zbpctrl"
"github.com/FloatTech/zbputils/control"
zero "github.com/wdvxdr1123/ZeroBot"
"github.com/wdvxdr1123/ZeroBot/message"
)
const (
jiami1 = "http://ovooa.com/API/sho_u/?msg=%v" // 加密api地址
jiami2 = "http://ovooa.com/API/sho_u/?format=1&msg=%v" // 解密api地址
)
type nmd struct { // struct解析格式大概是
Data struct {
Message string
} `json:"data"`
}
func init() { // 主函数
en := control.Register("jiami", &ctrl.Options[*zero.Ctx]{
DisableOnDefault: false,
Brief: "兽语加解密",
Help: "兽语加解密\n" +
"- 兽语加密xxx\n- 兽语解密xxx",
})
en.OnRegex(`^兽语加密\s*(.+)$`).SetBlock(true).Handle(func(ctx *zero.Ctx) {
str := ctx.State["regex_matched"].([]string)[1]
es, err := web.GetData(fmt.Sprintf(jiami1, str)) // 将网站返回结果赋值
if err != nil {
ctx.SendChain(message.Text("出现错误捏:", err))
return
}
var r nmd // r数组
err = json.Unmarshal(es, &r) // 填api返回结果struct地址
if err != nil {
ctx.SendChain(message.Text("出现错误捏:", err))
return
}
ctx.SendChain(message.Text(r.Data.Message)) // 输出提取后的结果
})
en.OnRegex(`^兽语解密\s*(.+)$`).SetBlock(true).Handle(func(ctx *zero.Ctx) {
str := ctx.State["regex_matched"].([]string)[1]
es, err := web.GetData(fmt.Sprintf(jiami2, str)) // 将网站返回结果赋值
if err != nil {
ctx.SendChain(message.Text("出现错误捏:", err))
return
}
var n nmd // r数组
err = json.Unmarshal(es, &n) // 填api返回结果struct地址
if err != nil {
ctx.SendChain(message.Text("出现错误捏:", err))
return
}
ctx.SendChain(message.Text(n.Data.Message)) // 输出提取后的结果
})
}

121
plugin/jikipedia/main.go Normal file
View File

@@ -0,0 +1,121 @@
// Package jikipedia 小鸡词典
// 修改自https://github.com/TeamPGM/PagerMaid_Plugins_Pyro ,非常感谢!!
package jikipedia
import (
"bytes"
"encoding/json"
"errors"
"fmt"
"io"
"net/http"
"strings"
"github.com/FloatTech/floatbox/binary"
ctrl "github.com/FloatTech/zbpctrl"
"github.com/FloatTech/zbputils/control"
"github.com/FloatTech/zbputils/ctxext"
"github.com/tidwall/gjson"
zero "github.com/wdvxdr1123/ZeroBot"
"github.com/wdvxdr1123/ZeroBot/message"
)
const (
url = "https://api.jikipedia.com/go/search_entities"
)
type value struct {
Phrase string `json:"phrase"`
Page int `json:"page"`
Size int `json:"size"`
}
func init() {
// 初始化engine
engine := control.Register("jikipedia", &ctrl.Options[*zero.Ctx]{
DisableOnDefault: false,
Brief: "小鸡词典",
Help: "- [查梗|小鸡词典][梗]",
},
)
engine.OnPrefixGroup([]string{"小鸡词典", "查梗"}).Limit(ctxext.LimitByGroup).SetBlock(true).Handle(
func(ctx *zero.Ctx) {
keyWord := strings.Trim(ctx.State["args"].(string), " ")
definition, err := parseKeyword(keyWord)
if err != nil {
ctx.SendChain(message.Text("ERROR: ", err))
return
}
if definition.String() == "" {
ctx.SendChain(message.Text("好像什么都没查到,换个关键词试一试?"))
return
}
imgURL := definition.Get("images.0.scaled.path").String()
ctx.SendChain(message.Text("【标题】:", definition.Get("term.title"),
"\n【释义】:", definition.Get("plaintext"),
"\n【原文】:https://jikipedia.com/definition/", definition.Get("id")),
message.Image(imgURL))
},
)
}
func parseKeyword(keyWord string) (definition gjson.Result, err error) {
client := &http.Client{}
values := value{Phrase: keyWord, Page: 1, Size: 10}
jsonData, err := json.Marshal(values)
if err != nil {
return
}
var request *http.Request
request, err = http.NewRequest("POST", url, bytes.NewBuffer(jsonData))
if err != nil {
return
}
request.Header = http.Header{
"Accept": {"application/json, text/plain, */*"},
"Accept-Encoding": {"gzip, deflate, br"},
"Accept-Language": {"zh-CN,zh-TW;q=0.9,zh;q=0.8"},
"Client": {"web"},
"Client-Version": {"2.7.2g"},
"Connection": {"keep-alive"},
"Host": {"api.jikipedia.com"},
"Origin": {"https://jikipedia.com"},
"Referer": {"https://jikipedia.com/"},
"Sec-Fetch-Dest": {"empty"},
"Sec-Fetch-Mode": {"cors"},
"Sec-Fetch-Site": {"same-site"},
"Token": {""},
"User-Agent": {"Mozilla/5.0 (Linux; Android 6.0; Nexus 5 Build/MRA58N) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/102.0.0.0 Mobile Safari/537.36"},
"XID": {"uNo5bL1nyNCp/Gm7lJAHQ91220HLbMT8jqk9IJYhtHA4ofP+zgxwM6lSDIKiYoppP2k1IW/1Vxc2vOVGxOOVReebsLmWPHhTs7NCRygfDkE="},
"sec-ch-ua": {`" Not A;Brand";v="99", "Chromium";v="102", "Google Chrome";v="102"`},
"sec-ch-ua-mobile": {"?1"},
"sec-ch-ua-platform": {`"Android"`},
}
request.Header.Set("Content-Type", "application/json;charset=UTF-8")
var response *http.Response
response, err = client.Do(request)
if err != nil {
return
}
defer response.Body.Close()
if response.StatusCode != http.StatusOK {
extraInfo := ""
if response.StatusCode == 423 {
extraInfo = "\n调用过多被网站暂时封禁请等待数个小时后使用该功能~"
}
s := fmt.Sprintf("status code: %d%s", response.StatusCode, extraInfo)
err = errors.New(s)
return
}
data, err := io.ReadAll(response.Body)
if err != nil {
return
}
gjson.Get(binary.BytesToString(data), "data").ForEach(func(key, value gjson.Result) bool {
definition = value.Get("definitions.0")
return definition.String() == ""
})
return
}

View File

@@ -0,0 +1,105 @@
// Package jptingroom 日语听力学习材料
package jptingroom
import (
"time"
"github.com/FloatTech/floatbox/binary"
fcext "github.com/FloatTech/floatbox/ctxext"
ctrl "github.com/FloatTech/zbpctrl"
"github.com/FloatTech/zbputils/control"
"github.com/FloatTech/zbputils/img/text"
log "github.com/sirupsen/logrus"
zero "github.com/wdvxdr1123/ZeroBot"
"github.com/wdvxdr1123/ZeroBot/message"
)
func init() { // 插件主体
engine := control.Register("jptingroom", &ctrl.Options[*zero.Ctx]{
DisableOnDefault: false,
Brief: "日语听力学习材料",
Help: "- 随机日语听力\n" +
"- 随机日语歌曲\n" +
"- 日语听力 xxx\n" +
"- 日语歌曲 xxx\n",
PublicDataFolder: "Jptingroom",
})
getdb := fcext.DoOnceOnSuccess(func(ctx *zero.Ctx) bool {
db.DBPath = engine.DataFolder() + "item.db"
_, err := engine.GetLazyData("item.db", true)
if err != nil {
ctx.SendChain(message.Text("ERROR: ", err))
return false
}
err = db.Open(time.Hour * 24)
if err != nil {
ctx.SendChain(message.Text("ERROR: ", err))
return false
}
err = db.Create("item", &item{})
if err != nil {
ctx.SendChain(message.Text("ERROR: ", err))
return false
}
n, err := db.Count("item")
if err != nil {
ctx.SendChain(message.Text("ERROR: ", err))
return false
}
log.Infof("[jptingroom]读取%d条日语听力材料", n)
return true
})
// 开启
engine.OnFullMatchGroup([]string{"随机日语听力", "随机日语歌曲"}, getdb).SetBlock(true).
Handle(func(ctx *zero.Ctx) {
matched := ctx.State["matched"].(string)
var t item
switch matched {
case "随机日语听力":
t = getRandomAudioByCategory("tingli")
case "随机日语歌曲":
t = getRandomAudioByCategory("gequ")
default:
}
if t.AudioURL == "" {
ctx.SendChain(message.Text("未能找到相关材料"))
return
}
ctx.SendChain(message.Record(t.AudioURL))
content := t.Title + "\n\n" + t.Content
data, err := text.RenderToBase64(content, text.FontFile, 400, 20)
if err != nil {
ctx.SendChain(message.Text("ERROR: ", err))
return
}
if id := ctx.SendChain(message.Image("base64://" + binary.BytesToString(data))); id.ID() == 0 {
ctx.SendChain(message.Text("ERROR: 可能被风控了"))
}
})
engine.OnRegex(`日语(听力|歌曲)\s?([一-龥A-Za-z0-9ぁ-んァ-ヶ]{1,50})$`, getdb).SetBlock(true).
Handle(func(ctx *zero.Ctx) {
regexMatched := ctx.State["regex_matched"].([]string)
var t item
switch regexMatched[1] {
case "听力":
t = getRandomAudioByCategoryAndKeyword("tingli", regexMatched[2])
case "歌曲":
t = getRandomAudioByCategoryAndKeyword("gequ", regexMatched[2])
default:
}
if t.AudioURL == "" {
ctx.SendChain(message.Text("未能找到相关材料"))
return
}
content := t.Title + "\n\n" + t.Content
data, err := text.RenderToBase64(content, text.FontFile, 400, 20)
if err != nil {
ctx.SendChain(message.Text("ERROR: ", err))
return
}
if id := ctx.SendChain(message.Image("base64://" + binary.BytesToString(data))); id.ID() == 0 {
ctx.SendChain(message.Text("ERROR: 可能被风控了"))
}
})
}

View File

@@ -0,0 +1,30 @@
package jptingroom
import (
"time"
sql "github.com/FloatTech/sqlite"
)
type item struct {
ID int64 `db:"id"`
Title string `db:"title"`
PageURL string `db:"page_url"`
Category string `db:"category"`
Intro string `db:"intro"`
AudioURL string `db:"audio_url"`
Content string `db:"content"`
Datetime time.Time `db:"datetime"`
}
var db = &sql.Sqlite{}
func getRandomAudioByCategory(category string) (t item) {
_ = db.Find("item", &t, "where category = '"+category+"' ORDER BY RANDOM() limit 1")
return
}
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")
return
}

View File

@@ -25,8 +25,8 @@ const (
func init() {
control.Register("juejuezi", &ctrl.Options[*zero.Ctx]{
DisableOnDefault: false,
Help: "绝绝子生成器\n" +
"- 喝奶茶绝绝子 | 绝绝子吃饭",
Brief: "绝绝子生成器",
Help: "例: 喝奶茶绝绝子\n绝绝子吃饭",
}).OnRegex("[\u4E00-\u9FA5]{0,10}绝绝子[\u4E00-\u9FA5]{0,10}").SetBlock(true).Limit(ctxext.LimitByUser).Handle(func(ctx *zero.Ctx) {
toDealStr := []rune(strings.ReplaceAll(ctx.ExtractPlainText(), "绝绝子", ""))
switch len(toDealStr) {
@@ -35,7 +35,7 @@ func init() {
case 2:
data, err := juejuezi(string(toDealStr[0]), string(toDealStr[1]))
if err != nil {
ctx.SendChain(message.Text("ERROR:", err))
ctx.SendChain(message.Text("ERROR: ", err))
return
}
ctx.SendChain(message.Text(gjson.Get(helper.BytesToString(data), "text").String()))
@@ -43,7 +43,7 @@ func init() {
params := ctx.GetWordSlices(string(toDealStr)).Get("slices").Array()
data, err := juejuezi(params[0].String(), params[1].String())
if err != nil {
ctx.SendChain(message.Text("ERROR:", err))
ctx.SendChain(message.Text("ERROR: ", err))
return
}
ctx.SendChain(message.Text(gjson.Get(helper.BytesToString(data), "text").String()))

View File

@@ -3,6 +3,8 @@ package lolicon
import (
"encoding/base64"
"errors"
"net/url"
"strings"
"time"
@@ -10,13 +12,13 @@ import (
zero "github.com/wdvxdr1123/ZeroBot"
"github.com/wdvxdr1123/ZeroBot/message"
"github.com/FloatTech/floatbox/math"
"github.com/FloatTech/floatbox/process"
"github.com/FloatTech/floatbox/web"
ctrl "github.com/FloatTech/zbpctrl"
"github.com/FloatTech/zbputils/control"
"github.com/FloatTech/zbputils/ctxext"
"github.com/FloatTech/zbputils/img/pool"
"github.com/FloatTech/zbputils/math"
"github.com/FloatTech/zbputils/process"
"github.com/FloatTech/zbputils/web"
imagepool "github.com/FloatTech/zbputils/img/pool"
)
const (
@@ -25,66 +27,69 @@ const (
)
var (
queue = make(chan string, capacity)
custapi = ""
queue = make(chan string, capacity)
customapi = ""
)
func init() {
en := control.Register("lolicon", &ctrl.Options[*zero.Ctx]{
DisableOnDefault: false,
Help: "lolicon\n" +
"- 来份萝莉\n" +
Brief: "随机图片",
Help: "- 随机图片\n" +
"- 随机图片 萝莉|少女\n" +
"- 设置随机图片地址[http...]",
}).ApplySingle(ctxext.DefaultSingle)
en.OnFullMatch("来份萝莉").Limit(ctxext.LimitByGroup).SetBlock(true).
en.OnPrefix("随机图片").Limit(ctxext.LimitByUser).SetBlock(true).
Handle(func(ctx *zero.Ctx) {
if imgtype := strings.TrimSpace(ctx.State["args"].(string)); imgtype != "" {
imageurl, err := getimgurl(api + "?tag=" + url.QueryEscape(imgtype))
if err != nil {
ctx.SendChain(message.Text("ERROR: ", err))
return
}
if id := ctx.Send(message.Message{ctxext.FakeSenderForwardNode(ctx, message.Image(imageurl))}).ID(); id == 0 {
ctx.SendChain(message.Text("ERROR: 可能被风控了"))
}
return
}
go func() {
for i := 0; i < math.Min(cap(queue)-len(queue), 2); i++ {
if custapi != "" {
data, err := web.GetData(custapi)
if customapi != "" {
data, err := web.GetData(customapi)
if err != nil {
ctx.SendChain(message.Text("ERROR:", err))
ctx.SendChain(message.Text("ERROR: ", err))
continue
}
queue <- "base64://" + base64.StdEncoding.EncodeToString(data)
continue
}
data, err := web.GetData(api)
imageurl, err := getimgurl(api)
if err != nil {
ctx.SendChain(message.Text("ERROR:", err))
ctx.SendChain(message.Text("ERROR: ", err))
continue
}
json := gjson.ParseBytes(data)
if e := json.Get("error").Str; e != "" {
ctx.SendChain(message.Text("ERROR:", e))
continue
}
url := json.Get("data.0.urls.original").Str
url = strings.ReplaceAll(url, "i.pixiv.cat", "i.pixiv.re")
name := url[strings.LastIndex(url, "/")+1 : len(url)-4]
m, err := pool.GetImage(name)
name := imageurl[strings.LastIndex(imageurl, "/")+1 : len(imageurl)-4]
m, err := imagepool.GetImage(name)
if err != nil {
m.SetFile(url)
_, err = m.Push(ctxext.SendToSelf(ctx), ctxext.GetMessage(ctx))
m.SetFile(imageurl)
_, _ = m.Push(ctxext.SendToSelf(ctx), ctxext.GetMessage(ctx))
process.SleepAbout1sTo2s()
}
if err == nil {
queue <- m.String()
} else {
queue <- url
queue <- imageurl
}
}
}()
select {
case <-time.After(time.Minute):
ctx.SendChain(message.Text("ERROR:等待填充,请稍后再试......"))
ctx.SendChain(message.Text("ERROR: 等待填充,请稍后再试......"))
case img := <-queue:
id := ctx.SendChain(message.Image(img))
if id.ID() == 0 {
if id := ctx.Send(message.Message{ctxext.FakeSenderForwardNode(ctx, message.Image(img))}).ID(); id == 0 {
process.SleepAbout1sTo2s()
id = ctx.SendChain(message.Image(img).Add("cache", "0"))
if id.ID() == 0 {
ctx.SendChain(message.Text("ERROR:图片发送失败,可能被风控了~"))
if id := ctx.Send(message.Message{ctxext.FakeSenderForwardNode(ctx, message.Image(img).Add("cache", "0"))}).ID(); id == 0 {
ctx.SendChain(message.Text("ERROR: 可能被风控或下载图片用时过长,请耐心等待"))
}
}
}
@@ -92,10 +97,23 @@ func init() {
en.OnPrefix("设置随机图片地址", zero.SuperUserPermission).SetBlock(true).
Handle(func(ctx *zero.Ctx) {
u := strings.TrimSpace(ctx.State["args"].(string))
if !strings.HasPrefix(u, "http") {
ctx.SendChain(message.Text("ERROR:url非法!"))
return
}
custapi = u
ctx.SendChain(message.Text("成功设置随机图片地址为", u))
customapi = u
})
}
func getimgurl(url string) (string, error) {
data, err := web.GetData(url)
if err != nil {
return "", err
}
json := gjson.ParseBytes(data)
if e := json.Get("error").Str; e != "" {
return "", errors.New(e)
}
var imageurl string
if imageurl = json.Get("data.0.urls.original").Str; imageurl == "" {
return "", errors.New("未找到相关内容, 换个tag试试吧")
}
return strings.ReplaceAll(imageurl, "i.pixiv.cat", "i.pixiv.re"), nil
}

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