Compare commits

...

557 Commits

Author SHA1 Message Date
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
源文雨
92c913e3eb 🔖 1.4.1 2022-06-16 13:31:33 +08:00
github-actions[bot]
5dea461963 🎨 改进代码样式 2022-06-16 05:29:07 +00:00
方柳煜
f768081849 优化花名册名字切片报错问题 (#263) 2022-06-16 13:27:47 +08:00
himawari
668418c48a 添加mid2txt接口 (#264)
Co-authored-by: haibaraguo <haibaraguo@yeahka.com>
2022-06-16 13:27:23 +08:00
github-actions[bot]
2bf387a57c 🎨 改进代码样式 2022-06-15 04:24:49 +00:00
方柳煜
42a4726985 修复checkupdate报错问题 (#262)
* 修复checkupdate报错问题

* Update qqmapwife.go

* Update qqmapwife.go
2022-06-15 12:24:19 +08:00
源文雨
5c4b5359e0 Merge branch 'master' of https://github.com/FloatTech/ZeroBot-Plugin 2022-06-13 13:05:42 +08:00
源文雨
8e3f56a3a2 move info 2022-06-13 13:05:21 +08:00
github-actions[bot]
91a837477a 🎨 改进代码样式 2022-06-13 04:32:42 +00:00
源文雨
a2dfc54068 🐛 fix: b14 decode 2022-06-13 12:32:09 +08:00
github-actions[bot]
7ded2fe13a 🎨 改进代码样式 2022-06-13 04:09:52 +00:00
方柳煜
b6f1b27a81 Update qqmapwife.go (#261) 2022-06-13 12:09:22 +08:00
源文雨
621fd614ea 💩👌 make lint happy 2022-06-11 21:06:08 +08:00
源文雨
be3e7c892c fix b14 2022-06-11 18:27:26 +08:00
源文雨
1f640b7644 drop webgui 2022-06-11 17:00:22 +08:00
源文雨
987b505d32 fix regex 2022-06-11 16:27:09 +08:00
源文雨
4b2f23f093 优化正则 2022-06-11 16:13:26 +08:00
源文雨
1d52d0bafc 优化 parse 2022-06-11 16:04:57 +08:00
源文雨
e73db14976 运势 add ASoul 2022-06-11 14:27:03 +08:00
源文雨
fe8a677541 Merge branch 'master' of https://github.com/FloatTech/ZeroBot-Plugin 2022-06-11 14:16:36 +08:00
源文雨
926cba8197 运势 add ASoul 2022-06-11 14:16:22 +08:00
Jiang-Red
ee6e5db0a6 Update bilibili_parse.go (#259)
修复 粉丝数据获取出错
优化 简化数据显示
优化 不返回无效链接的错误
2022-06-11 11:53:19 +08:00
源文雨
0c62d8dd07 fix: imgfinder format 2022-06-10 23:34:05 +08:00
源文雨
d6e352b3b4 fix: imgfinder format 2022-06-10 23:28:12 +08:00
源文雨
59e46be262 fix: imgfinder format 2022-06-10 23:24:14 +08:00
源文雨
746c048550 优化代码结构 2022-06-10 19:00:27 +08:00
himawari
a92c584997 添加简易midi制作 (#257)
*  添加简易midi制作

* 🐛 修lint

Co-authored-by: haibaraguo <haibaraguo@yeahka.com>
2022-06-10 18:55:28 +08:00
源文雨
7f4c6eb4ac 🔖 1.4.1-beta6 2022-06-10 15:48:33 +08:00
源文雨
fbeb048c44 ⬆️ update sqlite 2022-06-10 15:47:47 +08:00
源文雨
9f748a8119 imgfinder add more info 2022-06-10 13:42:03 +08:00
源文雨
b9fd590095 fix #256: 关键字搜图无法使用 2022-06-10 13:21:31 +08:00
源文雨
430e210b3c update deps 2022-06-10 13:04:16 +08:00
Jiang-Red
706d54db76 更换api版bilibiliparse (#255)
* Update bilibili_parse.go

* make lint happy

* 改进提出的问题
2022-06-10 12:54:53 +08:00
源文雨
9ac276767d change reg host 2022-06-04 17:59:31 +08:00
源文雨
29144348b3 🔖 1.4.1-beta5 2022-06-04 17:27:08 +08:00
源文雨
2f91b11672 fix manager 2022-06-04 15:01:00 +08:00
源文雨
caf0ef88ac update control 2022-06-04 14:55:15 +08:00
源文雨
c8154db108 fix workflow 2022-06-02 12:38:46 +08:00
源文雨
7ce6b5592a 🎨 优化代码格式 2022-06-02 12:30:38 +08:00
源文雨
e1bc4fdf36 优化 score 2022-06-02 12:29:37 +08:00
MoeMagicMango
fd7f5f719f fix:修复API变动造成图片无法返回的问题 (#250) 2022-06-02 12:20:17 +08:00
源文雨
6678ca6c93 💩👌 make lint happy 2022-06-02 12:19:41 +08:00
源文雨
180e03e249 try fixing #249: once SIGSEGV 2022-05-30 14:43:08 +08:00
源文雨
357f15eb01 🔖 1.4.1-beta4 2022-05-29 13:11:24 +08:00
未归404
a08704b60f 更新README.md (#247) 2022-05-29 12:50:36 +08:00
源文雨
03ba81a9c5 🎨 优化 bilibiliparse 2022-05-27 19:16:13 +08:00
源文雨
2933962df8 🎨 优化 bilibiliparse 2022-05-27 19:09:44 +08:00
源文雨
4f248c4dc7 🎨 优化 bilibiliparse 2022-05-27 19:09:07 +08:00
源文雨
14096f24ba add 设置默认限速 #241 2022-05-27 01:00:48 +08:00
源文雨
c0b75accf4 fix #243: add ctrl /改变默认启用状态 2022-05-27 00:41:44 +08:00
源文雨
cb1b7d5da7 add more limiter (#241) 2022-05-26 13:55:14 +08:00
源文雨
c8072171f0 🔖 1.4.1-beta3 2022-05-25 11:58:32 +08:00
源文雨
485f6d4cef 🐛 job panic 2022-05-25 11:58:01 +08:00
源文雨
2b85097e30 Merge branch 'master' of https://github.com/FloatTech/ZeroBot-Plugin 2022-05-24 10:56:15 +08:00
源文雨
0904789291 🔖 1.4.1-beta2 2022-05-24 10:56:00 +08:00
方柳煜
fda69793a5 修复单身技能会报错问题 (#237) 2022-05-24 10:54:11 +08:00
源文雨
3c22aaf99d 🐛 job panic 2022-05-24 10:52:49 +08:00
MoeMagicMango
151a5d480f Fix: 更换稳定的背景api + 修复因api问题签到结果无法出现但数据库已经记录的问题 (#236)
* 通过修改顺序解决签到背景API出问题

* Update sign_in.go

* 🎨 改进代码样式

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2022-05-21 22:53:20 +08:00
源文雨
88d136626d update deps 2022-05-20 23:36:46 +08:00
源文雨
170f5bba79 🔖 1.4.1-beta1 2022-05-20 21:15:45 +08:00
源文雨
c4cbb0cd59 添加我问你答 2022-05-20 21:13:49 +08:00
源文雨
0fa04070f1 Update README.md 2022-05-20 15:23:39 +08:00
方柳煜
f8bea241a2 修复小三技能的判断逻辑的缺陷 (#230) 2022-05-16 22:51:54 +08:00
github-actions[bot]
bb6b2e4b29 🎨 改进代码样式 2022-05-16 14:45:37 +00:00
源文雨
557251a679 saucenao 搜图 使用合并转发 2022-05-16 22:44:47 +08:00
github-actions[bot]
696783b0fe 🎨 改进代码样式 2022-05-16 06:42:24 +00:00
源文雨
6eb29d2cc9 🔖 1.4.0 2022-05-16 14:41:45 +08:00
github-actions[bot]
4b4cafcda8 🎨 改进代码样式 2022-05-16 05:40:05 +00:00
方柳煜
ed2ed8d968 修复CD共用问题,新增功能 (#229) 2022-05-16 13:39:48 +08:00
Jiang-Red
fbb387bd9f fix:saucenao插件搜图相似度判断 (#228) 2022-05-14 23:50:25 +08:00
源文雨
0db2db482f 🐛 job 注入指令结果 递归 2022-05-14 19:10:05 +08:00
himawari
af2fe727fc 添加拟声鸟类型和马丁路德骂人语录 (#227)
*  mockingbird添加类型

*  添加马丁路德骂人语录

Co-authored-by: haibaraguo <haibaraguo@yeahka.com>
2022-05-14 11:32:25 +08:00
源文雨
a7387dcf22 💩👌 make lint happy 2022-05-13 13:12:39 +08:00
github-actions[bot]
5ce44fb161 🎨 改进代码样式 2022-05-13 05:09:55 +00:00
Jiang-Red
9086b9c3ca 修复一个小问题 (#225) 2022-05-13 13:09:13 +08:00
github-actions[bot]
4837c5387a 🎨 改进代码样式 2022-05-13 05:08:51 +00:00
源文雨
5d73216238 🐛 saucenao reflect 2022-05-13 13:08:02 +08:00
源文雨
5053091e44 💩👌 make lint happy 2022-05-13 13:00:54 +08:00
源文雨
6319ed0473 hide saucenao error 2022-05-12 19:07:59 +08:00
源文雨
c67c109af6 优化 qqwife 2022-05-12 14:43:30 +08:00
源文雨
b3cdb1464b 优化 imagefinder 2022-05-12 14:05:18 +08:00
源文雨
3c01c3f0ba 优化 danbooru 2022-05-12 14:01:24 +08:00
源文雨
07d541be20 🔖 1.4.0-beta7 2022-05-12 12:38:12 +08:00
源文雨
4a0dc59585 优化 saucenao 2022-05-12 12:27:02 +08:00
源文雨
b1dd8f52e8 edit README 2022-05-12 11:24:53 +08:00
github-actions[bot]
6907fe230f 🎨 改进代码样式 2022-05-12 03:23:21 +00:00
方柳煜
f2e4071b90 修复单身狗技能逻辑 (#224)
* ⬆️ update deps

* 更改源酱提出的问题

* Update qqmapwife.go

Co-authored-by: 源文雨 <41315874+fumiama@users.noreply.github.com>
2022-05-12 11:22:07 +08:00
源文雨
452f5e5f83 💩👌 make lint happy 2022-05-12 11:21:20 +08:00
源文雨
c94b9f54ce ⬆️ update deps 2022-05-10 23:34:09 +08:00
源文雨
e993f93cf4 🔖 1.4.0-beta6 2022-05-10 23:22:57 +08:00
源文雨
e9fac38c1b 🔖 1.4.0-beta6 2022-05-10 23:00:20 +08:00
源文雨
31a49b8ff9 fix: gif 503 2022-05-10 22:52:46 +08:00
源文雨
7831cdf10c fix: groupwife url 2022-05-09 12:22:36 +08:00
源文雨
c0f176f0af fix #173: 今日早报变成昨日火星晚报 2022-05-09 12:18:57 +08:00
源文雨
dc96a557eb 🔖 1.4.0-beta5 2022-05-09 12:12:18 +08:00
源文雨
7a8c915e62 优化代码结构 2022-05-09 11:23:34 +08:00
github-actions[bot]
dec42cc72b 🎨 改进代码样式 2022-05-09 03:09:45 +00:00
方柳煜
92c9d1d2ce 一天一夫一妻制群老婆 (#219) 2022-05-09 11:08:22 +08:00
Kanri
de017fcd27 Update README.md 2022-05-09 01:57:38 +08:00
MSCxar#0293
1229f62abd README.md: 新增tg群组 (#222)
* README.md: 新增tg群组
2022-05-09 01:38:21 +08:00
github-actions[bot]
512852b817 🎨 改进代码样式 2022-05-08 14:13:47 +00:00
himawari
5a087000a9 🐛 疫情查询、网易点歌、qq点歌问题 (#220)
* 🐛 修复网易点歌,qq点歌问题

* 🚨 错误输出

* 🚨 错误输出
2022-05-08 22:12:27 +08:00
莫思潋
6b56d6649e feat:解塔罗牌 (#217) 2022-05-08 18:31:18 +08:00
林沐馨
5081aab497 fix #215: 摸鱼日历 (#143)
* Update calendar.go

更换API方式

* Update calendar.go

* 更换API

修复BUG

* 🎨 改进代码样式

Co-authored-by: 源文雨 <41315874+fumiama@users.noreply.github.com>
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2022-05-07 22:35:32 +08:00
源文雨
bbb4afde53 fix: kanban scale 2022-05-06 22:30:51 +08:00
源文雨
d28495c3eb 优化显示 2022-05-06 21:11:29 +08:00
源文雨
1785e0c4a3 优化显示 2022-05-06 21:09:47 +08:00
源文雨
1e93e75aac add kanban 2022-05-06 21:06:45 +08:00
github-actions[bot]
a3ed7eeee2 🎨 改进代码样式 2022-05-06 12:04:03 +00:00
himawari
c3582637df 🎨 查成分增加可选择查uid (#214)
*  查成分添加注册日期信息

* 🐛 修复头像是webp图片使用不了的问题

* 🎨 查成分增加可选择查uid
2022-05-06 20:02:36 +08:00
源文雨
3b277b0eb0 搜图 drop yandex 2022-05-06 18:54:39 +08:00
源文雨
5e0991681f 抽n张塔罗牌 2022-05-05 14:19:52 +08:00
源文雨
354fbb7cdd 抽n张塔罗牌 2022-05-05 14:19:33 +08:00
源文雨
a166d06096 抽n张塔罗牌 2022-05-05 14:19:12 +08:00
源文雨
b8a6e07095 优化每日随机 2022-05-05 13:50:54 +08:00
源文雨
a8fba6e7b5 优化GetLazyData 2022-05-05 13:31:22 +08:00
源文雨
17a8c10f8c 🎨 modify badge 2022-05-04 23:40:37 +08:00
源文雨
7ccd29faac 🎨 modify badge 2022-05-04 23:40:01 +08:00
源文雨
d432e48cce 🔖 1.4.0-beta4 2022-05-04 23:21:50 +08:00
莫思潋
19091319cc feat:抽塔罗牌 (#213)
* 优化在两个命令中使用空格分隔的体验
- fortune的设置底图功能
- b14的加密功能

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

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

* - 删去了遗漏的Trim

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

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

* - 简化funny说明

* feat:抽塔罗牌

* 🎨 改进代码样式

* 加上一个换行符

* 🎨 改进代码样式

* - At用户

* 🎨 改进代码样式

* -改进抽塔罗牌

* typo: 小写struct名

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2022-05-04 23:19:21 +08:00
源文雨
d29e12a92c fortune add plugin 夏日口袋 by @XHLin-gamer 2022-05-04 21:23:54 +08:00
himawari
5db03b0441 查成分新增字段注册日期 (#212)
*  查成分添加注册日期信息

* 🐛 修复头像是webp图片使用不了的问题
2022-05-03 14:42:58 +08:00
源文雨
f8c7e2bf6d 🔖 1.4.0-beta3 2022-05-02 10:56:49 +08:00
源文雨
a7fdc6a88f 🐛 服务列表不显示名称 2022-05-02 10:55:17 +08:00
莫思潋
4aaf5b0779 调整讲个笑话 (#210) 2022-05-02 10:46:53 +08:00
源文雨
6150e4c736 add playground instruction 2022-04-30 00:37:12 +08:00
源文雨
9a3b4b3ecb 🔖 1.4.0-beta2 2022-04-29 20:41:57 +08:00
源文雨
cbaeb4d5f2 🐛 无法在job中移除匹配触发器 2022-04-29 20:40:37 +08:00
源文雨
592c306aa0 加速word count 2022-04-28 23:29:48 +08:00
源文雨
136e6bddf4 加速word count 2022-04-28 23:15:27 +08:00
源文雨
0e8bef0541 sync data 2022-04-28 22:26:00 +08:00
fumiama
9d8c52303e update deps 2022-04-28 22:19:53 +08:00
fumiama
e6ae7df546 🔖 1.4.0-beta1 2022-04-28 19:58:22 +08:00
fumiama
5b57a759be update deps 2022-04-28 19:56:37 +08:00
MSCxar#0293
cc96eeae32 🐛README.md查缺补漏 (#208) 2022-04-28 17:38:30 +08:00
himawari
484fef1228 🎨 优化vtb插件代码,更换shadiao的api (#206)
* 🎨 优化vtb插件代码,更换shadiao的api

* 🎨 正则全局变量

Co-authored-by: haibaraguo <haibaraguo@yeahka.com>
2022-04-28 17:37:57 +08:00
Coloured-glaze
ba4ca11c83 🎨增加文字回复 (#205)
* 🎨增加文字回复

* 🎨增加文字回复

* 🎨增加文字回复

* 🎨增加文字回复(fix)

* 🎨small letter=>

* Update ys.go

* Update ys.go

* Update ys.go

Co-authored-by: 源文雨 <41315874+fumiama@users.noreply.github.com>
2022-04-27 14:11:23 +08:00
Mayuri
9372902348 🐛fix : setutime regex (#203) 2022-04-25 09:33:53 +08:00
源文雨
0cc9420570 Merge branch 'master' of https://github.com/FloatTech/ZeroBot-Plugin 2022-04-22 09:24:29 +08:00
源文雨
18fa48d283 🔖 1.3.3-fix1 2022-04-22 09:24:15 +08:00
源文雨
9034fedbd0 lolicon add custom rand api 2022-04-22 09:23:31 +08:00
源文雨
ec3b6ec5fa 🔥 remove plugin acgimage 2022-04-22 09:13:43 +08:00
Mayuri
8cc2a4174e 🐛 更新签到背景图API (#202) 2022-04-21 21:36:23 +08:00
Yiwen-Chan
8a0014928d 🐛 更新疫情API 2022-04-21 12:59:01 +08:00
github-actions[bot]
228ca7f9c0 🎨 改进代码样式 2022-04-20 04:50:42 +00:00
MoeMagicMango
c0fa7efb27 简单修了下网易点歌 (#200) 2022-04-20 12:49:24 +08:00
himawari
3553683bb1 🐛 兼容linux和windows文件换行不同 (#199) 2022-04-19 23:00:01 +08:00
源文雨
ccb58e0a6c 🎨 edit README 2022-04-19 13:02:13 +08:00
源文雨
93253e2188 🎨 edit README 2022-04-19 12:59:47 +08:00
MSCxar#0293
e34bcc9bbd [水pr]整理README.md (#198)
* 整理 README.md
2022-04-19 12:39:45 +08:00
源文雨
72169e3a4d 🔖 1.3.3 2022-04-18 23:07:49 +08:00
源文雨
41c499840f fix #197: 猜单词出现空指针报警 2022-04-18 22:59:33 +08:00
himawari
f5cef170f0 🚑️ 修复查看其他群聊热词的问题 (#195) 2022-04-18 16:31:09 +08:00
源文雨
d7922a7c9b ️ 优化pool 2022-04-18 16:29:09 +08:00
github-actions[bot]
23fc77e8e1 🎨 改进代码样式 2022-04-17 13:25:27 +00:00
himawari
51f3eda09a 👍 添加聊天热词 (#192)
*  添加聊天热词

*  添加聊天热词

*  添加聊天热词

* 🚨 修lint

* 🚨 修lint
2022-04-17 21:24:07 +08:00
源文雨
6ec08a5227 🐛 reg 时序 2022-04-17 21:22:08 +08:00
源文雨
4855f694cb 🎨 edit README 2022-04-17 00:11:54 +08:00
源文雨
eaef37a831 Update README.md 2022-04-16 19:10:06 +08:00
源文雨
b182ff36bb 🔖 1.3.3-beta6 2022-04-16 00:35:05 +08:00
源文雨
c4e543449c 使用真正lazy的资源下载方式 2022-04-16 00:33:50 +08:00
源文雨
c76d9d4461 🔥 remove debug info in gorm 2022-04-16 00:04:07 +08:00
源文雨
dfd184724b 使用真正lazy的资源下载方式 2022-04-16 00:02:41 +08:00
源文雨
fef254031e ️ banmap 使用 ttl 2022-04-15 19:02:06 +08:00
源文雨
89c4e59bf1 🔖 1.3.3-beta5 2022-04-15 15:29:45 +08:00
源文雨
2e9acd3276 Merge branch 'master' of https://github.com/FloatTech/ZeroBot-Plugin 2022-04-15 15:28:02 +08:00
源文雨
5cab59c93f 🐛 戳一戳失效 2022-04-15 15:27:47 +08:00
Mayuri
ebfb5878a1 更新用法和README.md (#189)
* update help manual

* Update README.md
2022-04-15 09:25:25 +08:00
源文雨
de6b11aa22 🔖 1.3.3-beta4 2022-04-14 17:18:01 +08:00
源文雨
9e7e7159e8 🔖 1.3.3-beta3 2022-04-14 17:11:00 +08:00
源文雨
00c909271b 🔖 1.3.3-beta3 2022-04-14 17:10:21 +08:00
源文雨
7401a0cbec 🎨 优化代码结构 2022-04-14 17:09:28 +08:00
源文雨
a9327d1774 🐛 reg close 2022-04-10 16:33:34 +08:00
Mayuri
9e99ef1348 新增 {gid} {groupname} (#186)
* modified:   plugin/manager/manager.go

* use message.UnescapeCQCodeText instead
2022-04-10 11:46:50 +08:00
源文雨
5b4b9fdb01 🔖 1.3.3-beta2 2022-04-06 11:17:02 +08:00
源文雨
d2998c4908 Merge branch 'master' of https://github.com/FloatTech/ZeroBot-Plugin 2022-04-06 11:15:27 +08:00
源文雨
4ab968d563 🎨 优化代码结构 2022-04-06 11:15:24 +08:00
himawari
9b26eea754 增加查看签到排名 (#180)
*  增加查看签到排名

* 🚨 修lint

* 🚨 修lint
2022-04-06 10:59:56 +08:00
源文雨
a6bd717971 🔥 精简 log 输出 2022-04-05 23:18:13 +08:00
Kanri
f2914adb65 Update README.md 2022-04-05 20:52:25 +08:00
Kanri
aa5a426aac 📄 更换 README 图片 2022-04-05 20:50:40 +08:00
Kanri
fef8144187 📄 change title picture 2022-04-05 20:48:32 +08:00
源文雨
d8a429d3b3 🚧 move tag & nsfw to azfun 2022-04-05 15:52:06 +08:00
github-actions[bot]
56e53b75a5 🎨 改进代码样式 2022-04-04 14:55:00 +00:00
himawari
f89b6f5feb ️wordle逻辑优化,bilibilipush、translate添加信息 (#179)
* :sparkles:添加查成分功能

* :rotating_light:修lint

* :rotating_light:减少空格

* :bug:修改网址

* :bug:修改vup数量错误问题

* :bug:图片读取不了,就不读了

* 🐛 固定头像大小,bilibilipush调公有库

* 🎨 修改json转换

*  bilibilipush添加新类型,同时修改命令正则,wordle添加时间提醒,和单词语义,translate充实信息

* ️使用公用的翻译函数

* 🚨 添加return和stop

Co-authored-by: Guohuiyuan <haibaraguo@yeahka.com>
2022-04-04 22:53:37 +08:00
源文雨
ec513e8893 🐛 job 指令触发可能失败 2022-04-04 14:36:25 +08:00
源文雨
35d6792d2e 🐛 job 指令触发可能失败 2022-04-04 13:28:50 +08:00
源文雨
74387aae2d 🔖 1.3.3-beta1 2022-04-03 18:53:06 +08:00
源文雨
959043962d Merge branch 'master' of https://github.com/FloatTech/ZeroBot-Plugin 2022-04-03 18:51:25 +08:00
源文雨
4efb71bafb 🍱 运势 增加底图 奇异恩典 2022-04-03 18:51:06 +08:00
TZMSky
6fd0bc0445 删除Onebot-YAYA (#177) 2022-04-02 22:53:10 +08:00
源文雨
176a2eb392 🔥 精简Register 2022-04-02 22:45:21 +08:00
源文雨
1a85deb36e 🎨 优化代码结构 2022-04-02 22:40:31 +08:00
源文雨
5b409ff3de 🐛 pixiv dns 2022-04-02 13:31:54 +08:00
源文雨
a9e13d3a92 🎨 优化代码结构 2022-03-31 19:50:34 +08:00
莫思潋
0698d8e3b1 fix:修复了枝网查重无法使用的问题 (#175)
* 优化在两个命令中使用空格分隔的体验
- fortune的设置底图功能
- b14的加密功能

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

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

* - 删去了遗漏的Trim

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

* 小调整

- 考虑到屌字既难打又有碍观瞻,改为正则匹配`[屌|弔|吊]图`
- 增加了退群提醒的定制

* - 修复退群提醒本身忘记修改了的问题

* fix:修复了枝网查重无法使用的问题

* readme适配上次的欢迎语修改

* 删去调试语句

* Update setu_geter.go

* Update zhiwang.go

Co-authored-by: 源文雨 <41315874+fumiama@users.noreply.github.com>
2022-03-31 19:36:29 +08:00
源文雨
8e4c496b54 🐛 MustProvicePicture goroutine 泄漏 2022-03-30 13:23:52 +08:00
源文雨
27054b9722 🐛 MustProvicePicture goroutine 泄漏 2022-03-30 13:17:37 +08:00
源文雨
bcad2c4998 🎨 优化代码结构 2022-03-29 16:29:29 +08:00
github-actions[bot]
aae2bd4884 🎨 改进代码样式 2022-03-29 05:05:36 +00:00
himawari
006ef6f672 👍 添加查成分功能,修改bilibili插件的正则 (#171)
* :sparkles:添加查成分功能

* :rotating_light:修lint

* :rotating_light:减少空格

* :bug:修改网址

* :bug:修改vup数量错误问题

* :bug:图片读取不了,就不读了

* 🐛 固定头像大小,bilibilipush调公有库

* 🎨 修改json转换

Co-authored-by: Guohuiyuan <haibaraguo@yeahka.com>
2022-03-29 13:03:46 +08:00
源文雨
7be1a61347 🎨 优化代码结构 2022-03-28 23:13:53 +08:00
源文雨
1ef48f6cd6 🎨 优化代码结构 2022-03-28 23:06:04 +08:00
源文雨
a9a061a4bb 🎨 优化代码结构 2022-03-28 23:01:36 +08:00
源文雨
14ed8b7af7 🐛 reg 2022-03-28 17:23:54 +08:00
源文雨
d17b8fa720 📝 edit readme 2022-03-28 17:14:39 +08:00
源文雨
68980a69b5 🐛 增加百人一首 修复Lazy不关闭 2022-03-28 17:11:44 +08:00
源文雨
636f14c450 🐛 md5 只能连接一次 2022-03-27 14:35:48 +08:00
源文雨
b066bc37b3 #150 缩减coser发图数 2022-03-27 14:26:28 +08:00
源文雨
9ac43fecf8 fix #151 2022-03-27 14:22:34 +08:00
源文雨
d6ff14167d add plugin 渲染任意文字到图片 2022-03-27 14:00:21 +08:00
源文雨
5837a765f1 add plugin 渲染任意文字到图片 2022-03-27 13:54:26 +08:00
源文雨
1bfb76a4b3 📝 edit readme 2022-03-27 12:59:08 +08:00
源文雨
2a78a602e2 shindan add 黄油角色 2022-03-27 12:56:57 +08:00
源文雨
884b0e4226 💩👌 make lint happy 2022-03-27 12:48:12 +08:00
源文雨
4e819eb5d9 fix ci 2022-03-27 12:44:02 +08:00
源文雨
e3a50cc014 Merge branch 'master' of https://github.com/FloatTech/ZeroBot-Plugin 2022-03-27 00:24:56 +08:00
源文雨
25ab8c9234 fix: FutureEvent 2022-03-27 00:24:46 +08:00
github-actions[bot]
901e680fa4 🎨 改进代码样式 2022-03-26 14:03:46 +00:00
源文雨
82768fff59 fix b14 & cd 2022-03-26 22:01:50 +08:00
莫思潋
87e3297904 定制退群提醒+调整煎蛋网无聊图触发 (#166)
* 优化在两个命令中使用空格分隔的体验
- fortune的设置底图功能
- b14的加密功能

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

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

* - 删去了遗漏的Trim

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

* 小调整

- 考虑到屌字既难打又有碍观瞻,改为正则匹配`[屌|弔|吊]图`
- 增加了退群提醒的定制

* - 修复退群提醒本身忘记修改了的问题
2022-03-22 12:31:44 +08:00
Jiang-Red
81a4297eb1 添加更多数据,修改发送排版 (#167) 2022-03-21 18:18:29 +08:00
源文雨
17f5b52931 Merge branch 'master' of https://github.com/FloatTech/ZeroBot-Plugin 2022-03-20 18:49:31 +08:00
源文雨
2250adf854 fix #161 2022-03-20 18:49:20 +08:00
GenesisAN
5cd46b3926 从url正则中去除用字 (#160)
从url正则中去除用字,以防止url中包含用字前缀导致图片显示失败
2022-03-20 14:43:55 +08:00
源文雨
6e2b10b788 fix lint 2022-03-20 14:40:47 +08:00
源文雨
5e301ccaaf fix lint 2022-03-20 14:33:43 +08:00
源文雨
206a38efab fix #154: 优化代码结构 2022-03-20 14:20:37 +08:00
Coloured-glaze
cdf844be52 🎨原神抽卡 (#156)
* Add files via upload

* 原神抽卡

* 🎨原神抽卡
2022-03-20 10:59:11 +08:00
源文雨
70cc28c191 fix ci 2022-03-19 19:58:16 +08:00
源文雨
35282fba6c update data 2022-03-19 19:52:54 +08:00
源文雨
e43f8e594d Merge branch 'master' of https://github.com/FloatTech/ZeroBot-Plugin 2022-03-19 19:51:59 +08:00
源文雨
21988fab76 ⬆️ update to go 1.18 2022-03-19 19:51:45 +08:00
himawari
2f3823abe2 feat:添加舔狗日记和修复睡眠管理的问题 (#152)
* feat:添加舔狗日记和修复睡眠管理的问题

* fix:修lint

* fix:修改铅笔小说网址

* fix:修改铅笔小说网址
2022-03-19 11:29:31 +08:00
源文雨
2c545e7b7f 🎨 优化pool 2022-03-18 16:50:07 +08:00
源文雨
22f2200778 🐛 b14 panic 2022-03-15 22:50:38 +08:00
源文雨
2f63253ec0 🎨 优化代码结构 2022-03-14 12:50:50 +08:00
源文雨
be7e8134f9 🎨 优化代码结构 2022-03-14 12:46:54 +08:00
源文雨
b85408e3e5 pixiv add backend dns result 2022-03-13 22:43:41 +08:00
源文雨
4828451f9e 🔖 v1.3.2 2022-03-13 13:21:52 +08:00
源文雨
b3b50e1353 🐛 搜图 有时无法下载图片 2022-03-13 13:14:01 +08:00
源文雨
95e8bf2f55 📝 edit readme 2022-03-12 20:22:28 +08:00
源文雨
cde7669c0b 优化代码结构 2022-03-12 11:35:20 +08:00
源文雨
ff4a33c51c edit readme 2022-03-12 11:05:19 +08:00
源文雨
06cf6f84aa edit readme 2022-03-12 00:27:27 +08:00
源文雨
556f6f134f edit readme 2022-03-12 00:27:15 +08:00
源文雨
b1ab7ef118 edit readme 2022-03-12 00:25:59 +08:00
源文雨
e5fd108a9c edit readme 2022-03-12 00:25:21 +08:00
源文雨
5a67bf2417 🐛 每天刷新 2022-03-12 00:18:26 +08:00
源文雨
cb020c1bbd 🐛 每天刷新 2022-03-12 00:17:33 +08:00
源文雨
bf75b29e33 🐛 每天刷新 2022-03-12 00:16:04 +08:00
源文雨
d730fd9cab 优化代码结构 2022-03-12 00:05:41 +08:00
Jiang-Red
a862e5be7c 添加 早报插件及设置欢迎语增加更多自定义 (#147)
* 添加 早报插件

* 添加 早报插件

* 添加 早报插件

* 添加 设置欢迎语更多自定义

* 添加 help说明

* 添加 manager设置欢迎语说明

* 怪

* 🎨 改进代码样式

* Update zaobao.go

* Update zaobao.go

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: 源文雨 <41315874+fumiama@users.noreply.github.com>
2022-03-11 23:47:39 +08:00
源文雨
ebca124c86 🐛 修复 run 2022-03-11 23:02:08 +08:00
源文雨
3b7803a81b 优化代码结构 2022-03-11 16:34:26 +08:00
源文雨
b7b9d92148 优化代码结构 2022-03-11 16:18:11 +08:00
Rainy
44575fb19e 增加城市疫情查询 (#146)
* 增加城市疫情查询

* 增加城市疫情查询

* 增加城市疫情查询

* 增加城市疫情查询

* 增加查询城市疫情

* 增加城市查询疫情

* 增加城市疫情查询

* Update epidemic.go

Co-authored-by: 源文雨 <41315874+fumiama@users.noreply.github.com>
2022-03-11 16:10:14 +08:00
源文雨
f181a4b9cd 🎨 可自行编辑json 2022-03-11 11:40:51 +08:00
源文雨
77e5d8b0c2 ⬆️ update deps 2022-03-11 11:36:55 +08:00
源文雨
edd9feb8f8 🔥 drop ioutil 2022-03-11 11:32:13 +08:00
源文雨
81d6a06dbe 🎨 ascii2d color -> bovw 2022-03-11 10:46:01 +08:00
源文雨
8afbc67bf7 💩👌 make lint happy 2022-03-11 10:26:29 +08:00
源文雨
a27132f907 job可获取网页信息,可代表执行 2022-03-09 12:14:20 +08:00
源文雨
183be05d82 job可编程 2022-03-08 23:18:31 +08:00
源文雨
bccf789714 job增加执行指令 2022-03-08 20:39:02 +08:00
源文雨
55944dddb3 job增加参数读取 2022-03-08 18:14:41 +08:00
源文雨
f67932cc56 job增加参数读取 2022-03-08 18:11:24 +08:00
源文雨
6d633fac6a 🐛 edit readme 2022-03-08 18:10:01 +08:00
源文雨
86fc5c51c8 job增加参数读取 2022-03-08 18:07:59 +08:00
源文雨
156e9f07ad 🐛 job禁止递归触发&增加README 2022-03-08 13:04:44 +08:00
源文雨
1ba4722fc7 💩👌 make lint happy 2022-03-07 23:27:11 +08:00
源文雨
54c9857219 🎉 job增加注入指令结果 2022-03-07 22:49:19 +08:00
源文雨
ea56c7d0d2 🐛 panic: sync: WaitGroup is reused 2022-03-07 14:43:25 +08:00
源文雨
0cbe43df97 🐛 panic: sync: WaitGroup is reused 2022-03-07 14:08:23 +08:00
源文雨
1f941d883a 🐛 panic: sync: WaitGroup is reused 2022-03-07 14:01:15 +08:00
源文雨
f5b3e423fb 🐛 panic: sync: WaitGroup is reused 2022-03-07 13:56:05 +08:00
源文雨
6c7f81ca55 🔖 1.3.1 2022-03-07 13:41:23 +08:00
源文雨
3911b5ed82 🎨 优化初始化逻辑 2022-03-07 13:40:32 +08:00
源文雨
f38f3ab69c 🐛 定时指令触发器插件增加查看 2022-03-07 13:15:26 +08:00
源文雨
846db6f063 📝 edit README 2022-03-06 22:53:59 +08:00
源文雨
ff068a05b0 🐛 定时指令触发器插件增加限速器 2022-03-06 22:52:38 +08:00
源文雨
051b7dd182 🐛 定时指令触发器插件允许非管理私聊使用 2022-03-06 22:46:12 +08:00
源文雨
11870aeed6 🐛 定时指令触发器插件在某些bot失效 2022-03-06 22:37:35 +08:00
源文雨
9bcff82d9c Update main.go 2022-03-06 21:27:19 +08:00
源文雨
309efe8cd8 增加定时指令触发器插件 2022-03-06 21:21:28 +08:00
源文雨
bf54789f0f 🎨 优化pixiv下载 2022-03-05 23:01:28 +08:00
Kanri
01e527abdb 增加六阶七阶猜单词 (#140) 2022-03-05 10:36:01 +08:00
fumiama
db6b558c75 ⬆️ update deps 2022-03-04 12:17:58 +08:00
fumiama
9a792eb144 🍱 运势增加底图 东方归言录 by @KafCoppelia 2022-03-02 22:37:41 +08:00
fumiama
44e99664a3 🎨 关键字搜图改用云函数反代 2022-03-01 23:10:10 +08:00
fumiama
9f3dd37bf0 🎨 关键字搜图改用云函数反代 2022-03-01 23:02:22 +08:00
fumiama
f5936d9cb7 🎨 某些发图应用合并转发 2022-03-01 19:47:25 +08:00
fumiama
27e69ef022 yandex与ascii2d并行 2022-03-01 17:30:59 +08:00
fumiama
6262aab6bd 替换CustomNode为ctxext的封装 2022-03-01 17:29:19 +08:00
fumiama
4781bc8eb7 sync 2022-02-27 19:34:47 +08:00
fumiama
b233c4b638 Merge branch 'master' of https://github.com/FloatTech/ZeroBot-Plugin 2022-02-27 19:32:29 +08:00
fumiama
0a0cc807dc fix #139 2022-02-27 19:31:53 +08:00
github-actions[bot]
feeeb63e52 🎨 改进代码样式 2022-02-27 10:59:03 +00:00
himawari
5c620d6268 feat:添加月幕galgame网站图 (#138)
* feat:添加月幕galgame网站图

* fix:修lint

* fix:不重复更新

* fix:增加命令提醒语

* fix:增加日语匹配

* fix:换位置加锁

* fix:隐式声明

* fix:copy数组

* Update ai_tts.go

Co-authored-by: 源文雨 <41315874+fumiama@users.noreply.github.com>
2022-02-27 18:57:52 +08:00
fumiama
a469000d7a 🔖 v1.3.0 2022-02-27 15:04:09 +08:00
fumiama
0e15ac78b2 修复pixiv下载 2022-02-27 14:17:29 +08:00
fumiama
d9e195ccd0 💩👌 make lint happy 2022-02-26 23:22:37 +08:00
fumiama
cf52997279 fix: 图片缓存池 2022-02-26 23:20:08 +08:00
fumiama
88318a7151 增加插件 百度一下 2022-02-26 22:28:37 +08:00
fumiama
91f522b666 Merge branch 'master' of https://github.com/FloatTech/ZeroBot-Plugin 2022-02-26 22:09:55 +08:00
fumiama
1895f8815e fix: gif 爬 撕 2022-02-26 22:09:37 +08:00
Kanri
7be5f52be4 ✏️ 修复抓不到日历的碧油鸡 (#137)
* ✏️ 修复抓不到日历的碧油鸡

* ✏️ 小修改
2022-02-26 21:58:29 +08:00
fumiama
0cfb2e4e06 🎨 优化目录结构 2022-02-25 22:15:14 +08:00
fumiama
5ccf753af3 🎨 优化目录结构 2022-02-25 22:06:30 +08:00
fumiama
bfcf3cac91 删除插件 minecraft 2022-02-25 22:01:37 +08:00
fumiama
bc3d7bc278 删除插件 minecraft 2022-02-25 22:00:03 +08:00
fumiama
7cf767f344 🐛 fix: 摸鱼日历&插件控制 2022-02-25 13:24:21 +08:00
fumiama
7aed9a2c89 fix: reg panic 2022-02-24 21:34:06 +08:00
fumiama
454196a462 sync 2022-02-24 20:02:02 +08:00
fumiama
1597ba7128 💩👌 make lint happy 2022-02-24 19:55:28 +08:00
fumiama
480523ff27 🎨 🔥 ️ feat: pack pooled img sending 2022-02-24 19:54:48 +08:00
Kanri
7ec7ab8682 ✏️ 修复一下碧油鸡 (#136) 2022-02-24 18:06:23 +08:00
Kanri
ba2d011d48 ✏️ 修复获取公众号链接错误的问题 (#135) 2022-02-24 16:56:50 +08:00
fumiama
504e6cd1cc fix(#134): 两个群同时猜单词会互相干扰 2022-02-23 17:10:33 +08:00
github-actions[bot]
b8f3700fe4 🎨 改进代码样式 2022-02-23 08:58:01 +00:00
Jiang-Red
b2cff862bb Update manager.go (#132)
* Update manager.go

修改 自定义欢迎语可带图
添加 修改名片/修改头衔/申请头衔字数限制

* Update manager.go

* Update manager.go

添加 测试欢迎语

* Update manager.go

* Update manager.go

* Update manager.go

Co-authored-by: 源文雨 <41315874+fumiama@users.noreply.github.com>
2022-02-23 16:57:06 +08:00
fumiama
dc348f8500 🔥 🎨 统一 cron 2022-02-23 16:52:40 +08:00
Kanri
8c40293fed 新增插件 摸鱼人日历 (#133)
*  新增插件 摸鱼人日历

*  新增插件 摸鱼人日历

* ✏️ 优化插件 摸鱼人日历
2022-02-23 16:36:23 +08:00
fumiama
48deaf3aca 🔥 🎨 tts 2022-02-23 11:38:00 +08:00
fumiama
6932643788 🔥 🎨 tts 2022-02-23 11:35:43 +08:00
fumiama
7f972e3d68 🐛 fix: wordle 2022-02-23 11:32:26 +08:00
github-actions[bot]
edb0880999 🎨 改进代码样式 2022-02-23 03:17:02 +00:00
himawari
45f0f5cd6c 将mockingbird统一到tts (#131)
* fix:处理回复文本

* feat:将以mockingbird统一到tts里

* fix:修lint

* fix:换一个正则函数

* fix:去掉ok

Co-authored-by: Guohuiyuan <haibaraguo@yeahka.com>
2022-02-23 11:15:42 +08:00
fumiama
e468ac1933 apply new api ImageBytes 2022-02-22 22:16:49 +08:00
fumiama
55b6b40acf fix: use Send instead of SendChain 2022-02-22 21:55:25 +08:00
fumiama
5f1190f72e feat: use single instead of sync.Map 2022-02-22 21:52:06 +08:00
Kanri
b0fbd6507d 发布插件猜单词 2022-02-22 19:48:13 +08:00
fumiama
598a664d30 🐛 修正放大图片 2022-02-22 13:13:04 +08:00
fumiama
211f0a667d 🐛 修正放大图片 2022-02-22 13:10:48 +08:00
fumiama
28fb788778 🎨 shrink imgpool hash 2022-02-21 22:24:29 +08:00
fumiama
628830fbf5 Merge branch 'master' of https://github.com/FloatTech/ZeroBot-Plugin 2022-02-20 16:57:17 +08:00
fumiama
7d235d0077 🎨 scale 更改为 云函数 2022-02-20 16:57:01 +08:00
himawari
861b0b6cef fix:修改vtb语录存为本地路径,去掉bilibilipush缓存 (#130)
* fix:修改vtb语录存为本地路径,去掉bilibilipush缓存

* fix:bilibiliparse添加up和粉丝数,修改bilibilipush的时间

* Update bilibili_parse.go

Co-authored-by: Guohuiyuan <haibaraguo@yeahka.com>
Co-authored-by: 源文雨 <41315874+fumiama@users.noreply.github.com>
2022-02-19 23:54:17 +08:00
fumiama
9986c57727 fix: 缓存池有时无法发出图片 2022-02-19 23:50:39 +08:00
fumiama
f1c9e2331c fix: 缓存池有时无法发出图片 2022-02-19 23:41:11 +08:00
fumiama
7c1ddc6ab1 💩👌 make lint happy 2022-02-19 18:03:35 +08:00
fumiama
44ad78761b 🎨 🔥 update sqlite 2022-02-19 16:16:48 +08:00
fumiama
bd6925f2f1 🎨 🔥 统一 seed 2022-02-18 20:20:25 +08:00
fumiama
3838d74cb8 🎨 🔥 优化代码结构 2022-02-18 20:15:07 +08:00
Kanri
eaad8beb7b 增加插件 猜单词 (#129)
*  增加插件 猜单词

* Update wordle.go

* Update wordle.go

* Update wordle.go

* Update wordle.go

Co-authored-by: 源文雨 <41315874+fumiama@users.noreply.github.com>
2022-02-18 19:08:02 +08:00
fumiama
14acdfdbc3 🎨 ️ control 增加数据库查询缓存 & log 采用 CQ 码打印 2022-02-16 21:57:14 +08:00
fumiama
a5fe7a5b40 scale 增加图片过大保护 2022-02-16 13:55:17 +08:00
fumiama
61df3c55b5 scale 增加图片过大保护 2022-02-16 13:50:12 +08:00
fumiama
eb0059c1a3 fix: writer endian 2022-02-16 12:29:49 +08:00
fumiama
4ca5038ae9 🎨 🔥 优化代码结构 2022-02-15 20:50:49 +08:00
fumiama
121403231a fix: acgimage block 2022-02-15 16:17:40 +08:00
fumiama
b945338a8d 🔥 update go-registry 2022-02-15 13:38:45 +08:00
fumiama
ae5ea542a0 🔥 update go-registry 2022-02-15 00:42:39 +08:00
fumiama
fe95dc8201 🐛 fix: image finder 2022-02-14 17:08:20 +08:00
fumiama
1eefb4e610 🐛 fix: image finder 2022-02-14 17:07:45 +08:00
fumiama
8660d8d990 🐛 fix: image finder 2022-02-14 17:06:54 +08:00
fumiama
37033fe48d 搜图增加 yandex 2022-02-14 16:34:46 +08:00
fumiama
30665ad8c6 💩👌 make lint happy 2022-02-14 14:50:42 +08:00
fumiama
f9e4b8db76 📝 edit README 2022-02-14 14:49:12 +08:00
fumiama
c486a210e4 增加插件 漂流瓶 #2 2022-02-14 14:46:33 +08:00
fumiama
0cd01959a0 ⬆️ update kimoi #126 2022-02-14 13:03:37 +08:00
fumiama
d5ac9f7920 💩👌 make lint happy 2022-02-13 21:27:43 +08:00
fumiama
6b2dd112a6 dd add image size limit 2022-02-13 12:08:50 +08:00
fumiama
01abfab133 🚀 update moyu 春节 to 2023 2022-02-13 11:51:41 +08:00
fumiama
e2a56872dc 🐛 fix: 防止自触发 2022-02-13 11:43:36 +08:00
fumiama
6f22a9c4bc fix: chat call name 2022-02-13 11:35:42 +08:00
fumiama
a2426de70c Squashed commit of the following:
commit 3e4b0216b9f952b800e9be880acd744af3afe593
Merge: 970cb14 aaf832e
Author: fumiama <s862105088@gmail.com>
Date:   Sat Feb 12 23:23:51 2022 +0800

    Merge branch 'master' of https://github.com/MoYoez/ZeroBot-Plugin into MoYoez-master

commit 970cb142256d4575b3e5170b9dfb9370f5802da5
Author: fumiama <s862105088@gmail.com>
Date:   Sat Feb 12 23:18:23 2022 +0800

     🐛 🔥 🎨 fix: gif panic

commit aaf832e3b5
Author: MoeMagicMango <i@himoyo.cn>
Date:   Sat Feb 12 21:06:21 2022 +0800

    Update chat.go

commit fae3458bcf
Author: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Date:   Sat Feb 12 13:05:37 2022 +0000

    🎨 改进代码样式

commit 610cae5229
Author: MoeMagicMango <i@himoyo.cn>
Date:   Sat Feb 12 21:05:08 2022 +0800

    Update chat.go

commit c445c61da5
Author: MoeMagicMango <i@himoyo.cn>
Date:   Sat Feb 12 20:58:57 2022 +0800

    Delete data.go

commit 70172f4d8b
Author: MoeMagicMango <i@himoyo.cn>
Date:   Sat Feb 12 20:53:10 2022 +0800

    换了个网易云音乐热评的API

commit c8b4827e91
Author: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Date:   Sat Feb 12 12:50:57 2022 +0000

    🎨 改进代码样式

commit a233586dfe
Author: MoeMagicMango <i@himoyo.cn>
Date:   Sat Feb 12 20:50:21 2022 +0800

    Update chat.go

commit 87501f6011
Author: MoeMagicMango <i@himoyo.cn>
Date:   Sat Feb 12 20:49:08 2022 +0800

    Update data.go

commit ecf6419065
Author: MoeMagicMango <i@himoyo.cn>
Date:   Sat Feb 12 20:48:14 2022 +0800

    Update chat.go

commit c3550503f0
Author: MoeMagicMango <i@himoyo.cn>
Date:   Sat Feb 12 20:47:47 2022 +0800

    Delete plugin_purechat directory

commit 1da3486d00
Author: MoeMagicMango <i@himoyo.cn>
Date:   Sat Feb 12 20:46:34 2022 +0800

    Add files via upload

commit 34ed8c89fd
Author: MoeMagicMango <i@himoyo.cn>
Date:   Sat Feb 12 20:38:46 2022 +0800

    Update main.go

commit aa08eb0120
Author: MoeMagicMango <i@himoyo.cn>
Date:   Sat Feb 12 20:38:19 2022 +0800

    修改过的回复 删掉了一些ex的词汇

commit faf198289f
Author: MoeMagicMango <i@himoyo.cn>
Date:   Sat Feb 12 20:29:59 2022 +0800

    Update main.go

commit 5ac3b7a790
Author: MoeMagicMango <i@himoyo.cn>
Date:   Sat Feb 12 20:28:48 2022 +0800

    Update chat.go

commit b046d45435
Author: MoeMagicMango <i@himoyo.cn>
Date:   Sat Feb 12 20:25:23 2022 +0800

    Rename purechat.go to chat.go

commit 6bbcdfcd58
Author: MoeMagicMango <i@himoyo.cn>
Date:   Sat Feb 12 20:24:54 2022 +0800

    Add files via upload

commit e7630b5904
Author: MoeMagicMango <i@himoyo.cn>
Date:   Sat Feb 12 20:24:32 2022 +0800

    Update purechat.go

commit 36c903bece
Author: MoeMagicMango <i@himoyo.cn>
Date:   Sat Feb 12 20:23:01 2022 +0800

    Create purechat.go
2022-02-12 23:38:45 +08:00
MoeMagicMango
ed7bbf54a7 Update chat.go 2022-02-12 23:36:58 +08:00
fumiama
69394377d0 fix: emojimix logical error 2022-02-12 14:48:12 +08:00
fumiama
8f20487a15 Merge branch 'master' of https://github.com/FloatTech/ZeroBot-Plugin 2022-02-12 14:20:21 +08:00
fumiama
fae24db2df 💩👌 make lint happy 2022-02-12 14:20:10 +08:00
github-actions[bot]
f8fb791d8f 🎨 改进代码样式 2022-02-12 06:19:04 +00:00
Kanri
f320d9e1e5 增加插件 emojimix (#121)
*  增加插件 emojimix

* 🎨✏️ 优化代码格式

*  增加 qqface 支持

* Update main.go

Co-authored-by: 源文雨 <41315874+fumiama@users.noreply.github.com>
2022-02-12 14:18:05 +08:00
fumiama
18bd34fad2 fix(#122): 避免图片过大导致内存占用过多 2022-02-12 13:37:06 +08:00
fumiama
cf72d0b36a fix(#122): 避免图片过大导致内存占用过多 2022-02-12 13:32:26 +08:00
fumiama
c3722b0a6c ️ perf: 每次匹配仅检查一次 ban 2022-02-11 00:39:50 +08:00
fumiama
3af701e803 🎉 🎨 ️ 优化 base64 图片 log 体验 & 减少 ban 遍历 2022-02-10 23:47:39 +08:00
fumiama
bc4b2e89db 🎉 base14支持所有平台 2022-02-10 14:15:49 +08:00
fumiama
3cbb589d2c 💩👌 make lint happy 2022-02-09 21:00:42 +08:00
github-actions[bot]
63ef93a9f9 🎨 改进代码样式 2022-02-09 07:44:23 +00:00
fumiama
8c943d2c01 🔥 🎨 优化代码结构 2022-02-09 15:43:14 +08:00
fumiama
806b77acce 🔥 🎨 优化代码结构 2022-02-08 19:39:19 +08:00
github-actions[bot]
3df0414621 🎨 改进代码样式 2022-02-08 07:43:30 +00:00
fumiama
04f70b0499 Merge branch 'master' of https://github.com/FloatTech/ZeroBot-Plugin 2022-02-08 15:42:09 +08:00
fumiama
0b20087e61 🎨 🔥 优化 jandan 2022-02-08 15:40:31 +08:00
github-actions[bot]
5c82600972 🎨 改进代码样式 2022-02-08 07:06:57 +00:00
himawari
45edd86cb8 feat:增加煎蛋网无聊图 (#115)
* feat:添加煎蛋网无聊图

* fix:修lint

* fix:修改model和名称

* fix:删除不要的驱动

* fix:修改日志错误的地方

* fix:修改权限

* fix:去掉数据库

Co-authored-by: Guohuiyuan <haibaraguo@yeahka.com>
2022-02-08 15:05:58 +08:00
fumiama
8f6b3fa00d 🎨 🔥 增加反并发 2022-02-08 14:58:47 +08:00
fumiama
a16bcda7da 🎨 🔥 统一限速器 2022-02-08 14:46:35 +08:00
fumiama
259b29f6e3 🐛 🔥 ascii2d返回多结果&修复合并转发 2022-02-07 20:39:49 +08:00
fumiama
477868c601 🐛 fix: imgpool fallback panic 2022-02-07 15:29:29 +08:00
莫思潋
92de963c0c 优化了更多插件中使用空格的体验 (#114)
* 优化在两个命令中使用空格分隔的体验
- fortune的设置底图功能
- b14的加密功能

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

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

* - 删去了遗漏的Trim

* 优化了更多插件中使用空格的体验
- 优化了music bilibili image_finder 中使用空格的体验
- 补上了plugin_bilibili中未实现的vup开头触发
- 为plugin_bilibili_parse输出的消息加上一个换行符,优化排版
2022-02-03 13:41:10 +08:00
fumiama
a7c56d9e77 💩👌 make lint happy 2022-02-03 13:40:00 +08:00
fumiama
4fe64eeb44 🐛 nsfw 防止风控 2022-02-01 18:38:01 +08:00
fumiama
fc0d020a04 新增叔叔的放大图片 2022-02-01 18:30:01 +08:00
fumiama
617e2c9885 🐛 添加封禁命令,修复 sql 2022-02-01 14:28:40 +08:00
github-actions[bot]
bb1130490b 🎨 改进代码样式 2022-01-30 16:40:38 +00:00
fumiama
ca18a323e3 增加初始化等待 2022-01-31 00:39:39 +08:00
fumiama
0770c6e4a1 fix plugin nsfw 2022-01-30 23:13:11 +08:00
fumiama
e1dbfe8f6a 🐛 fix nil ptr 2022-01-30 23:05:13 +08:00
fumiama
a2854069ac add plugin nsfw 2022-01-30 23:00:23 +08:00
fumiama
9f0efc2bda 🔖 v1.2.5 2022-01-30 20:06:19 +08:00
fumiama
ec5798f264 ️ 优化 rule.FirstValueInList 2022-01-30 20:05:39 +08:00
github-actions[bot]
ded20441fa 🎨 改进代码样式 2022-01-30 11:04:49 +00:00
himawari
47064dbcec b站视频链接解析 (#113)
* feat:b站视频链接解析

* fix:修lint

* fix:block改为true

* fix:不加视频链接,以免两个机器人在同一个群疯狂触发

* fix:链接换成bv号

* fix:修改为send方法

Co-authored-by: Guohuiyuan <haibaraguo@yeahka.com>
2022-01-30 19:04:00 +08:00
莫思潋
2cda7ecb04 优化在两个命令中使用空格分隔的体验 (#112)
* 优化在两个命令中使用空格分隔的体验
- fortune的设置底图功能
- b14的加密功能

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

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

* - 删去了遗漏的Trim
2022-01-29 13:48:01 +08:00
fumiama
b372892a20 🐛 adjust prio 2022-01-28 20:02:00 +08:00
fumiama
fabf62059e 💩👌 make lint happy 2022-01-28 19:59:31 +08:00
fumiama
c524c68809 🐛 fix setutime mkdir 2022-01-27 23:06:04 +08:00
fumiama
876fd425e4 ⬆️ update zb 2022-01-27 16:43:11 +08:00
fumiama
72ed1293f6 🔥 优化 setutime 2022-01-27 16:29:19 +08:00
fumiama
fa973a9231 🐛 fix saucenao relative path 2022-01-27 15:50:08 +08:00
fumiama
e245a8f124 🎨 ️ pixiv 图片缓存集中到 data/pixiv 2022-01-27 14:32:13 +08:00
fumiama
aa71fa7cb8 🐛 fix acgimage 2022-01-27 00:14:26 +08:00
fumiama
da5aab3ee6 🔥 use new imgpool 2022-01-26 23:52:37 +08:00
fumiama
e0317d1fe1 ️ 优化代码结构 2022-01-26 11:01:33 +08:00
fumiama
3557a0ebc1 ⬆️ update deps 2022-01-25 13:54:05 +08:00
fumiama
ddd0cc4538 ⬆️ update deps 2022-01-25 13:50:15 +08:00
fumiama
0212ec51cc 🔖 v1.2.4 2022-01-25 13:34:44 +08:00
fumiama
7109e30d93 🎨 🔥 modify wyy 2022-01-24 21:48:21 +08:00
MoeMagicMango
6990819d5c feat:添加来自ovooa的网易云热评API (#111)
* Create readme.md

* 调用网易云热评API

* Update main.go

* Delete readme.md

* Delete main.go

* Create main.go

* Add files via upload

* Update prio.go

* Add files via upload

* Add files via upload

* Update prio.go

* Add files via upload

* Update prio.go

* Add files via upload

* Add files via upload

* Add files via upload

* Add files via upload

* Add files via upload

* revert main

Co-authored-by: 源文雨 <41315874+fumiama@users.noreply.github.com>
2022-01-24 21:40:52 +08:00
fumiama
b4ff754934 🐛 修正 imgpool 2022-01-24 18:23:33 +08:00
fumiama
6ece644a7b 🐛 修正 imgpool 2022-01-24 18:17:53 +08:00
fumiama
f826b4d0bc 🐛 防止风控 2022-01-24 14:28:43 +08:00
fumiama
aa1f82d46f 增加 pixiv 多线程下载与图片缓存 2022-01-24 14:01:30 +08:00
fumiama
f7c6d67428 🐛 fix fortune 2022-01-23 13:48:45 +08:00
fumiama
cd30d6df31 🐛 fix fortune 2022-01-23 13:43:42 +08:00
fumiama
7463792de7 🐛 fix fortune 2022-01-23 13:38:56 +08:00
fumiama
80f49c47c6 🐛 fix fortune 2022-01-23 13:22:31 +08:00
fumiama
4baead3490 Merge branch 'master' of https://github.com/FloatTech/ZeroBot-Plugin 2022-01-23 00:28:16 +08:00
fumiama
2d0c69344a 🐛 fix get&del msg in zb 2022-01-23 00:28:05 +08:00
github-actions[bot]
a94ea2b332 🎨 改进代码样式 2022-01-22 15:44:30 +00:00
fumiama
4ee50a6582 add image pool 2022-01-22 23:43:38 +08:00
fumiama
b21021bd6b Merge branch 'master' of https://github.com/FloatTech/ZeroBot-Plugin 2022-01-22 12:39:51 +08:00
fumiama
7238fe0b6b 🍱 add fortune 赛马娘 2022-01-22 12:38:22 +08:00
Kanri
e1b5c176c3 ✏️ 修复 pixiv.cat 2022-01-22 10:54:38 +08:00
Mayuri
3aabc089d8 Make Help manual legible (#110) 2022-01-19 13:28:37 +08:00
fumiama
db5177027f add rough guild support 2022-01-18 15:07:34 +08:00
fumiama
0e2da1b983 add rough guild support 2022-01-17 23:44:47 +08:00
fumiama
1583006018 🔥 💩 disable bilibilipush by default 2022-01-17 23:20:26 +08:00
fumiama
6b49c2842c 💩👌 make lint happy 2022-01-17 23:06:31 +08:00
fumiama
9c4a5dc818 Merge branch 'master' of https://github.com/FloatTech/ZeroBot-Plugin 2022-01-17 23:05:30 +08:00
fumiama
8256658ac2 add rough guild support 2022-01-17 23:04:32 +08:00
himawari
4b11ff495d fix:去掉chromedp,分类型处理json字符串 (#109)
* fix:新动态处理

* fix:使用正确的变量cOrigType

* fix:单小说的时候不用渲染图片

Co-authored-by: Guohuiyuan <haibaraguo@yeahka.com>
2022-01-17 20:43:02 +08:00
fumiama
461dc925af add rough guild support 2022-01-16 20:48:45 +08:00
fumiama
1233b1d11c 💩👌 make lint happy 2022-01-16 20:37:47 +08:00
fumiama
6dbc0b3891 💩👌 make lint happy 2022-01-15 20:25:07 +08:00
github-actions[bot]
db3cef9bb6 🎨 改进代码样式 2022-01-15 12:18:59 +00:00
fumiama
3de009727e 💩👌 make lint happy 2022-01-15 20:17:56 +08:00
github-actions[bot]
8d35515497 🎨 改进代码样式 2022-01-15 12:15:39 +00:00
fumiama
4519dd5eb8 💫 ♻️ 🎉 ️ 强制使用优先级 2022-01-15 20:14:34 +08:00
fumiama
925e6847f3 🎨 adjust prio 2022-01-15 17:06:50 +08:00
fumiama
9462cbc7e0 🔥 shrink README 2022-01-15 17:02:33 +08:00
fumiama
8ec4271419 🐛 fix curse over-widely matching 2022-01-15 17:00:07 +08:00
fumiama
173fd250cb 🐛 fix: manager timer panic 2022-01-15 15:41:27 +08:00
fumiama
89bdc1e496 🐛 fix gif panic 2022-01-15 15:18:25 +08:00
fumiama
48736d3f4a 💩👌 make lint happy 2022-01-15 11:02:34 +08:00
himawari
f8df60f06b fix:修复直播状态更新的bug (#107)
* feat:添加b站推送,写了一半

* feat:添加b站推送(后续看情况,替换掉chromepd)

* fix:修复一直推送直播的bug

* fix:添加缓存路径

* feat:添加推送列表,修复重复发消息的bug

* feat:发现禁用有用,删除多余指令

* fix:修改格式

* fix:sleep无效

* fix:修改直播card

* fix:增加长度

* fix:修改readme

* fix:修lint,并去掉cron

* fix:修lint

* fix:去掉日志

* fix:更新旧状态

* fix:删掉多余方法

* fix:优化判断语句

* fix:修lint

Co-authored-by: Guohuiyuan <haibaraguo@yeahka.com>
2022-01-15 11:01:33 +08:00
github-actions[bot]
9bd0ed03a5 🎨 改进代码样式 2022-01-14 14:18:27 +00:00
himawari
cb1e4bcdb7 feat:添加b站推送功能 (#106)
* feat:添加b站推送,写了一半

* feat:添加b站推送(后续看情况,替换掉chromepd)

* fix:修复一直推送直播的bug

* fix:添加缓存路径

* feat:添加推送列表,修复重复发消息的bug

* feat:发现禁用有用,删除多余指令

* fix:修改格式

* fix:sleep无效

* fix:修改直播card

* fix:增加长度

* fix:修改readme

* fix:修lint,并去掉cron

* fix:修lint

* fix:去掉日志

Co-authored-by: Guohuiyuan <haibaraguo@yeahka.com>
2022-01-14 22:17:24 +08:00
fumiama
af8e6a63b9 💩👌 make lint happy 2022-01-14 22:17:03 +08:00
fumiama
8c8cd709b5 🔖 v1.2.4-beta1 2022-01-14 20:07:57 +08:00
fumiama
18ae566d34 💩👌 make lint happy 2022-01-14 20:00:02 +08:00
fumiama
9cfa44d434 🐛 fix: aireply 表情 2022-01-14 13:34:46 +08:00
fumiama
23393d9157 ✏️ edit import 2022-01-14 13:17:59 +08:00
fumiama
b676e00f0b 🐛 fix fontfile notfound error 2022-01-14 12:10:05 +08:00
fumiama
790200e519 🔥 运势 移除下载锁 2022-01-13 23:03:42 +08:00
fumiama
ccde99e695 📝 运势 恢复为传递 base64 2022-01-13 23:02:20 +08:00
fumiama
a80f8082cc 📝 update zbputils 2022-01-13 20:57:07 +08:00
fumiama
4b194627bb ️ remove cachefile on start 2022-01-13 20:51:07 +08:00
fumiama
d983c66597 💫 🎉 添加插件 DeepDanbooru二次元图标签识别 2022-01-13 20:47:07 +08:00
fumiama
30f6b57941 🐛 fix txt2img 2022-01-13 20:30:26 +08:00
fumiama
107979c459 🔥 drop writer 2022-01-13 19:17:59 +08:00
fumiama
f05f09d741 🍱 🎨 ️ 运势 不解压 不传 base64 2022-01-13 16:43:50 +08:00
fumiama
bc989f2fb0 🐛 fix score cache error 2022-01-12 12:33:02 +08:00
fumiama
9b4494a24f 🐛 fix new user error 2022-01-12 12:25:16 +08:00
fumiama
d3fafa7b9a 🐛 fix: fortune dlmu 2022-01-12 12:11:14 +08:00
fumiama
ce1e85e0a3 🔥 优化 score 并发 2022-01-12 11:57:16 +08:00
fumiama
560df5cade 🐛 fix(#95): aifalse error unckeck 2022-01-12 11:39:03 +08:00
fumiama
9f6e361e6e 💩👌 make lint happy 2022-01-12 11:33:55 +08:00
fumiama
00be4a856f 🎨 🚚 move zbpctrl into zbputils 2022-01-12 11:33:12 +08:00
fumiama
606aec4ec1 🚚 🎉 📝 合并 gif 2022-01-11 16:09:33 +08:00
github-actions[bot]
7f0f211333 🎨 改进代码样式 2022-01-11 07:32:06 +00:00
fumiama
42f761d44d ♻️ 🔥 优化青云客结构 2022-01-11 15:31:00 +08:00
fumiama
577b5b108f Merge branch 'master' of https://github.com/FloatTech/ZeroBot-Plugin 2022-01-11 12:08:59 +08:00
fumiama
4d39b32cf6 🙈 add ignore 2022-01-11 12:08:48 +08:00
github-actions[bot]
8279ed31d1 🎨 改进代码样式 2022-01-11 03:41:25 +00:00
fumiama
0185d0c730 🚚 ⬆️ 📝 🔥 🎨 迁移 ctrl, utils 2022-01-11 11:40:33 +08:00
fumiama
4ac1674cc0 📝 update data 2022-01-11 10:42:02 +08:00
github-actions[bot]
1c222a36ee 🎨 改进代码样式 2022-01-11 02:08:46 +00:00
himawari
16e05e0320 独立骂人插件 (#102)
* fix:vtb数据不用从zbp更新

* feat:独立骂人插件

* fix:修lint

* feat:添加关键词

Co-authored-by: Guohuiyuan <haibaraguo@yeahka.com>
2022-01-11 10:07:51 +08:00
fumiama
8ea64e7aba 添加 -n -p 参数 2022-01-10 12:55:34 +08:00
fumiama
c22140103f 🐛 小修正 2022-01-10 12:17:39 +08:00
github-actions[bot]
8bd6e40ac8 🎨 改进代码样式 2022-01-09 15:22:17 +00:00
himawari
2ed25c6991 添加签到,引入分数机制 (#101)
* feat:添加签到功能,引入分数机制

* feat:修lint

* fix:加宽

* fix:修lint和加锁

* fix:解决冲突

* fix:二次判断
2022-01-09 23:21:36 +08:00
fumiama
d8991ec016 🐛 启动显示 banner 2022-01-09 23:20:00 +08:00
fumiama
dc9ca1e63f 💩👌 make lint happy 2022-01-09 22:53:22 +08:00
fumiama
7bb3203dfb 🐛 群管全局操作权限错误 2022-01-09 22:20:14 +08:00
fumiama
8ebe9548f7 ️ 优化 abs 2022-01-08 13:56:19 +08:00
fumiama
f90f20d3a8 🙈 同步 data 2022-01-08 11:53:52 +08:00
fumiama
1437e3b323 🎨 小修正 2022-01-07 22:47:32 +08:00
fumiama
d44171de61 ♻️ 🔥 优化 acgimage, 新增 ipv6 分流 2022-01-07 21:57:30 +08:00
fumiama
62a9e413fb 🔖 v1.2.3 2022-01-07 20:10:14 +08:00
207 changed files with 16528 additions and 7205 deletions

View File

@@ -24,13 +24,13 @@ jobs:
goarch: arm64
fail-fast: true
steps:
- uses: actions/checkout@v2
- uses: actions/checkout@master
- name: Setup Go environment
uses: actions/setup-go@v2.1.3
uses: actions/setup-go@master
with:
go-version: 1.17
go-version: 1.18
- name: Cache downloaded module
uses: actions/cache@v2
uses: actions/cache@master
with:
path: |
~/.cache/go-build
@@ -48,7 +48,7 @@ jobs:
export CGO_ENABLED=0
go build -o "output/$BINARY_NAME" -trimpath -ldflags "$LD_FLAGS" .
- name: Upload artifact
uses: actions/upload-artifact@v2
uses: actions/upload-artifact@master
if: ${{ !github.head_ref }}
with:
name: ${{ matrix.goos }}_${{ matrix.goarch }}

View File

@@ -6,12 +6,12 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Set up Go
uses: actions/setup-go@v2
uses: actions/setup-go@master
with:
go-version: 1.17
go-version: 1.18
- name: Check out code into the Go module directory
uses: actions/checkout@v2
uses: actions/checkout@master
- name: golangci-lint
uses: golangci/golangci-lint-action@master
@@ -28,9 +28,6 @@ jobs:
# Optional: show only new issues if it's a pull request. The default value is `false`.
# only-new-issues: true
# Optional: if set to true then the action will use pre-installed Go.
skip-go-installation: true
# Optional: if set to true then the action don't cache or restore ~/go/pkg.
# skip-pkg-cache: true

View File

@@ -6,19 +6,18 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Set up Go
uses: actions/setup-go@v2
uses: actions/setup-go@master
with:
go-version: 1.17
go-version: 1.18
- name: Check out code into the Go module directory
uses: actions/checkout@v2
uses: actions/checkout@master
- name: golangci-lint
uses: golangci/golangci-lint-action@master
with:
version: latest
args: --issues-exit-code=0
skip-go-installation: true
- name: Commit back
continue-on-error: true
run: |

View File

@@ -9,17 +9,17 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v2.3.4
uses: actions/checkout@master
with:
fetch-depth: 0
- name: Set up Go
uses: actions/setup-go@v2
uses: actions/setup-go@master
with:
go-version: '1.17'
go-version: '1.18'
- name: Run GoReleaser
uses: goreleaser/goreleaser-action@v2
uses: goreleaser/goreleaser-action@master
with:
version: latest
args: release --rm-dist

BIN
.github/yaya.jpg vendored

Binary file not shown.

Before

Width:  |  Height:  |  Size: 63 KiB

BIN
.github/黒金.jpg vendored Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 191 KiB

14
.gitignore vendored
View File

@@ -1,14 +1,3 @@
data/SetuTime/cache
data/control
data/SetuTime/search
data/manager
data/acgimage
data/saucenao
data/fortune
data/hs
data/nsetu
data/nwife
data/sleep
plugins/*.so
plugins/*.dll
.idea/
@@ -16,4 +5,5 @@ plugins/*.dll
.vscode
go-zero*
nohup.out
zerobot
zerobot
ZeroBot-Plugin*

View File

@@ -1,15 +1,11 @@
linters-settings:
errcheck:
ignore: fmt:.*,io/ioutil:^Read.*
ignore: fmt:.*
ignoretests: true
goimports:
local-prefixes: github.com/FloatTech/ZeroBot-Plugin
gocritic:
disabled-checks:
- exitAfterDefer
forbidigo:
# Forbid the following identifiers
forbid:
@@ -64,6 +60,9 @@ run:
deadline: 5m
issues-exit-code: 1
tests: false
skip-dirs:
- order
go: '1.18'
# output configuration options
output:

1285
README.md

File diff suppressed because it is too large Load Diff

13
config.go Normal file
View File

@@ -0,0 +1,13 @@
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

View File

@@ -1,94 +0,0 @@
package control
import (
"encoding/binary"
"strings"
"time"
b14 "github.com/fumiama/go-base16384"
zero "github.com/wdvxdr1123/ZeroBot"
"github.com/wdvxdr1123/ZeroBot/message"
"github.com/wdvxdr1123/ZeroBot/utils/helper"
"github.com/FloatTech/ZeroBot-Plugin/utils/math"
"github.com/FloatTech/ZeroBot-Plugin/utils/process"
)
var startTime int64
func init() {
// 插件冲突检测 会在本群发送一条消息并在约 1s 后撤回
zero.OnFullMatch("插件冲突检测", zero.OnlyGroup, zero.AdminPermission, zero.OnlyToMe).SetBlock(true).FirstPriority().
Handle(func(ctx *zero.Ctx) {
tok, err := genToken()
if err != nil {
return
}
t := message.Text("●cd" + tok)
startTime = time.Now().Unix()
id := ctx.SendChain(t)
process.SleepAbout1sTo2s()
ctx.DeleteMessage(id)
})
zero.OnRegex("^●cd([\u4e00-\u8e00]{4})$", zero.OnlyGroup).SetBlock(true).FirstPriority().
Handle(func(ctx *zero.Ctx) {
if isValidToken(ctx.State["regex_matched"].([]string)[1]) {
msg := ""
gid := ctx.Event.GroupID
ForEach(func(key string, manager *Control) bool {
if manager.IsEnabledIn(gid) {
msg += "\xfe\xff" + key
}
return true
})
if len(msg) > 2 {
my, err := b14.UTF16be2utf8(b14.EncodeString(msg[2:]))
mys := "●cd●" + helper.BytesToString(my)
if err == nil {
id := ctx.SendChain(message.Text(mys))
process.SleepAbout1sTo2s()
ctx.DeleteMessage(id)
}
}
}
})
zero.OnRegex("^●cd●(([\u4e00-\u8e00]*[\u3d01-\u3d06]?))", zero.OnlyGroup).SetBlock(true).FirstPriority().
Handle(func(ctx *zero.Ctx) {
if time.Now().Unix()-startTime < 10 {
msg, err := b14.UTF82utf16be(helper.StringToBytes(ctx.State["regex_matched"].([]string)[1]))
if err == nil {
gid := ctx.Event.GroupID
for _, s := range strings.Split(b14.DecodeString(msg), "\xfe\xff") {
mu.RLock()
c, ok := managers[s]
mu.RUnlock()
if ok && c.IsEnabledIn(gid) {
c.Disable(gid)
}
}
}
}
})
}
func genToken() (tok string, err error) {
timebytes := make([]byte, 8)
binary.BigEndian.PutUint64(timebytes, uint64(time.Now().Unix()))
timebytes, err = b14.UTF16be2utf8(b14.Encode(timebytes[1:]))
if err == nil {
tok = helper.BytesToString(timebytes)
}
return
}
func isValidToken(tok string) (yes bool) {
s, err := b14.UTF82utf16be(helper.StringToBytes(tok))
if err == nil {
timebytes := make([]byte, 1, 8)
timebytes = append(timebytes, b14.Decode(s)...)
yes = math.Abs64(time.Now().Unix()-int64(binary.BigEndian.Uint64(timebytes))) < 10
}
return
}

View File

@@ -1,19 +0,0 @@
package control
import "testing"
func TestGenToken(t *testing.T) {
tok, err := genToken()
if err == nil {
t.Log(tok)
t.Log(isValidToken(tok))
t.Fail()
} else {
t.Fatal(err)
}
}
func TestMaru(t *testing.T) {
t.Log(len("\xff"))
t.Fail()
}

View File

@@ -1,31 +0,0 @@
package control
import (
zero "github.com/wdvxdr1123/ZeroBot"
)
var enmap = make(map[string]*zero.Engine)
// Register 注册插件控制器
func Register(service string, o *Options) *zero.Engine {
engine := zero.New()
engine.UsePreHandler(newctrl(service, o).Handler)
enmap[service] = engine
return engine
}
// Delete 删除插件控制器,不会删除数据
func Delete(service string) {
engine, ok := enmap[service]
if ok {
engine.Delete()
mu.RLock()
_, ok = managers[service]
mu.RUnlock()
if ok {
mu.Lock()
delete(managers, service)
mu.Unlock()
}
}
}

View File

@@ -1,455 +0,0 @@
// Package control 控制插件的启用与优先级等
package control
import (
"crypto/md5"
"encoding/binary"
"fmt"
"os"
"strconv"
"strings"
"sync"
log "github.com/sirupsen/logrus"
zero "github.com/wdvxdr1123/ZeroBot"
"github.com/wdvxdr1123/ZeroBot/extension"
"github.com/wdvxdr1123/ZeroBot/message"
"github.com/wdvxdr1123/ZeroBot/utils/helper"
"github.com/FloatTech/ZeroBot-Plugin/utils/sql"
"github.com/FloatTech/ZeroBot-Plugin/utils/txt2img"
)
var (
db = &sql.Sqlite{DBPath: "data/control/plugins.db"}
// managers 每个插件对应的管理
managers = map[string]*Control{}
mu = sync.RWMutex{}
hasinit bool
)
// Control is to control the plugins.
type Control struct {
sync.RWMutex
service string
options Options
}
// newctrl returns Manager with settings.
func newctrl(service string, o *Options) *Control {
m := &Control{service: service,
options: func() Options {
if o == nil {
return Options{}
}
return *o
}(),
}
mu.Lock()
managers[service] = m
mu.Unlock()
err := db.Create(service, &grpcfg{})
if err != nil {
panic(err)
}
err = db.Create(service+"ban", &ban{})
if err != nil {
panic(err)
}
return m
}
// Enable enables a group to pass the Manager.
// groupID == 0 (ALL) will operate on all grps.
func (m *Control) Enable(groupID int64) {
var c grpcfg
m.RLock()
err := db.Find(m.service, &c, "WHERE gid = "+strconv.FormatInt(groupID, 10))
m.RUnlock()
if err != nil {
c.GroupID = groupID
}
c.Disable = int64(uint64(c.Disable) & 0xffffffff_fffffffe)
m.Lock()
err = db.Insert(m.service, &c)
m.Unlock()
if err != nil {
log.Errorf("[control] %v", err)
}
}
// Disable disables a group to pass the Manager.
// groupID == 0 (ALL) will operate on all grps.
func (m *Control) Disable(groupID int64) {
var c grpcfg
m.RLock()
err := db.Find(m.service, &c, "WHERE gid = "+strconv.FormatInt(groupID, 10))
m.RUnlock()
if err != nil {
c.GroupID = groupID
}
c.Disable |= 1
m.Lock()
err = db.Insert(m.service, &c)
m.Unlock()
if err != nil {
log.Errorf("[control] %v", err)
}
}
// Reset resets the default config of a group.
// groupID == 0 (ALL) is not allowed.
func (m *Control) Reset(groupID int64) {
if groupID != 0 {
m.Lock()
err := db.Del(m.service, "WHERE gid = "+strconv.FormatInt(groupID, 10))
m.Unlock()
if err != nil {
log.Errorf("[control] %v", err)
}
}
}
// IsEnabledIn 开启群
func (m *Control) IsEnabledIn(gid int64) bool {
var c grpcfg
var err error
log.Debugln("[control] IsEnabledIn recv gid =", gid)
if gid != 0 {
m.RLock()
err = db.Find(m.service, &c, "WHERE gid = "+strconv.FormatInt(gid, 10))
m.RUnlock()
if err == nil && gid == c.GroupID {
log.Debugf("[control] plugin %s of grp %d : %d", m.service, c.GroupID, c.Disable&1)
return c.Disable&1 == 0
}
}
m.RLock()
err = db.Find(m.service, &c, "WHERE gid = 0")
m.RUnlock()
if err == nil && c.GroupID == 0 {
log.Debugf("[control] plugin %s of all : %d", m.service, c.Disable&1)
return c.Disable&1 == 0
}
return !m.options.DisableOnDefault
}
// Ban 禁止某人在某群使用本插件
func (m *Control) Ban(uid, gid int64) {
var err error
var digest [16]byte
log.Debugln("[control] Ban recv gid =", gid, "uid =", uid)
if gid != 0 { // 特定群
digest = md5.Sum(helper.StringToBytes(fmt.Sprintf("%d_%d", uid, gid)))
m.RLock()
err = db.Insert(m.service+"ban", &ban{ID: int64(binary.LittleEndian.Uint64(digest[:8])), UserID: uid, GroupID: gid})
m.RUnlock()
if err == nil {
log.Debugf("[control] plugin %s is banned in grp %d for usr %d.", m.service, gid, uid)
return
}
}
// 所有群
digest = md5.Sum(helper.StringToBytes(fmt.Sprintf("%d_all", uid)))
m.RLock()
err = db.Insert(m.service+"ban", &ban{ID: int64(binary.LittleEndian.Uint64(digest[:8])), UserID: uid, GroupID: 0})
m.RUnlock()
if err == nil {
log.Debugf("[control] plugin %s is banned in all grp for usr %d.", m.service, uid)
}
}
// Permit 允许某人在某群使用本插件
func (m *Control) Permit(uid, gid int64) {
var digest [16]byte
log.Debugln("[control] Permit recv gid =", gid, "uid =", uid)
if gid != 0 { // 特定群
digest = md5.Sum(helper.StringToBytes(fmt.Sprintf("%d_%d", uid, gid)))
m.RLock()
_ = db.Del(m.service+"ban", "WHERE id = "+strconv.FormatInt(int64(binary.LittleEndian.Uint64(digest[:8])), 10))
m.RUnlock()
log.Debugf("[control] plugin %s is permitted in grp %d for usr %d.", m.service, gid, uid)
return
}
// 所有群
digest = md5.Sum(helper.StringToBytes(fmt.Sprintf("%d_all", uid)))
m.RLock()
_ = db.Del(m.service+"ban", "WHERE id = "+strconv.FormatInt(int64(binary.LittleEndian.Uint64(digest[:8])), 10))
m.RUnlock()
log.Debugf("[control] plugin %s is permitted in all grp for usr %d.", m.service, uid)
}
// IsBannedIn 某人是否在某群被 ban
func (m *Control) IsBannedIn(uid, gid int64) bool {
var b ban
var err error
var digest [16]byte
log.Debugln("[control] IsBannedIn recv gid =", gid, "uid =", uid)
if gid != 0 {
digest = md5.Sum(helper.StringToBytes(fmt.Sprintf("%d_%d", uid, gid)))
m.RLock()
err = db.Find(m.service+"ban", &b, "WHERE id = "+strconv.FormatInt(int64(binary.LittleEndian.Uint64(digest[:8])), 10))
m.RUnlock()
if err == nil && gid == b.GroupID && uid == b.UserID {
log.Debugf("[control] plugin %s is banned in grp %d for usr %d.", m.service, b.GroupID, b.UserID)
return true
}
}
digest = md5.Sum(helper.StringToBytes(fmt.Sprintf("%d_all", uid)))
m.RLock()
err = db.Find(m.service+"ban", &b, "WHERE id = "+strconv.FormatInt(int64(binary.LittleEndian.Uint64(digest[:8])), 10))
m.RUnlock()
if err == nil && b.GroupID == 0 && uid == b.UserID {
log.Debugf("[control] plugin %s is banned in all grp for usr %d.", m.service, b.UserID)
return true
}
return false
}
// GetData 获取某个群的 63 字节配置信息
func (m *Control) GetData(gid int64) int64 {
var c grpcfg
var err error
log.Debugln("[control] IsEnabledIn recv gid =", gid)
m.RLock()
err = db.Find(m.service, &c, "WHERE gid = "+strconv.FormatInt(gid, 10))
m.RUnlock()
if err == nil && gid == c.GroupID {
log.Debugf("[control] plugin %s of grp %d : 0x%x", m.service, c.GroupID, c.Disable>>1)
return c.Disable >> 1
}
return 0
}
// SetData 为某个群设置低 63 位配置数据
func (m *Control) SetData(groupID int64, data int64) error {
var c grpcfg
m.RLock()
err := db.Find(m.service, &c, "WHERE gid = "+strconv.FormatInt(groupID, 10))
m.RUnlock()
if err != nil {
c.GroupID = groupID
if m.options.DisableOnDefault {
c.Disable = 1
}
}
c.Disable &= 1
c.Disable |= data << 1
log.Debugf("[control] set plugin %s of grp %d : 0x%x", m.service, c.GroupID, data)
m.Lock()
err = db.Insert(m.service, &c)
m.Unlock()
if err != nil {
log.Errorf("[control] %v", err)
}
return err
}
// Handler 返回 预处理器
func (m *Control) Handler(ctx *zero.Ctx) bool {
ctx.State["manager"] = m
grp := ctx.Event.GroupID
if grp == 0 {
// 个人用户
return m.IsEnabledIn(-ctx.Event.UserID)
}
log.Debugln("[control] handler get gid =", grp)
return m.IsEnabledIn(grp) && !m.IsBannedIn(ctx.Event.UserID, grp)
}
// Lookup returns a Manager by the service name, if
// not exist, it will return nil.
func Lookup(service string) (*Control, bool) {
mu.RLock()
m, ok := managers[service]
mu.RUnlock()
return m, ok
}
// ForEach iterates through managers.
func ForEach(iterator func(key string, manager *Control) bool) {
mu.RLock()
m := copyMap(managers)
mu.RUnlock()
for k, v := range m {
if !iterator(k, v) {
return
}
}
}
func copyMap(m map[string]*Control) map[string]*Control {
ret := make(map[string]*Control, len(m))
for k, v := range m {
ret[k] = v
}
return ret
}
func userOrGrpAdmin(ctx *zero.Ctx) bool {
if zero.OnlyGroup(ctx) {
return zero.AdminPermission(ctx)
}
return zero.OnlyToMe(ctx)
}
func init() {
if !hasinit {
mu.Lock()
if !hasinit {
err := os.MkdirAll("data/control", 0755)
if err != nil {
panic(err)
} else {
hasinit = true
zero.OnCommandGroup([]string{
"启用", "enable", "禁用", "disable",
"全局启用", "enableall", "全局禁用", "disableall",
}, userOrGrpAdmin).SetBlock(true).FirstPriority().Handle(func(ctx *zero.Ctx) {
model := extension.CommandModel{}
_ = ctx.Parse(&model)
service, ok := Lookup(model.Args)
if !ok {
ctx.SendChain(message.Text("没有找到指定服务!"))
return
}
grp := ctx.Event.GroupID
if grp == 0 {
// 个人用户
grp = -ctx.Event.UserID
}
if strings.Contains(model.Command, "全局") || strings.Contains(model.Command, "all") {
grp = 0
}
if strings.Contains(model.Command, "启用") || strings.Contains(model.Command, "enable") {
service.Enable(grp)
ctx.SendChain(message.Text("已启用服务: " + model.Args))
} else {
service.Disable(grp)
ctx.SendChain(message.Text("已禁用服务: " + model.Args))
}
})
zero.OnCommandGroup([]string{"还原", "reset"}, userOrGrpAdmin).SetBlock(true).FirstPriority().Handle(func(ctx *zero.Ctx) {
model := extension.CommandModel{}
_ = ctx.Parse(&model)
service, ok := Lookup(model.Args)
if !ok {
ctx.SendChain(message.Text("没有找到指定服务!"))
return
}
grp := ctx.Event.GroupID
if grp == 0 {
// 个人用户
grp = -ctx.Event.UserID
}
service.Reset(grp)
ctx.SendChain(message.Text("已还原服务的默认启用状态: " + model.Args))
})
zero.OnCommandGroup([]string{
"禁止", "ban", "允许", "permit",
"全局禁止", "banall", "全局允许", "permitall",
}, zero.OnlyGroup, zero.AdminPermission).SetBlock(true).FirstPriority().Handle(func(ctx *zero.Ctx) {
model := extension.CommandModel{}
_ = ctx.Parse(&model)
args := strings.Split(model.Args, " ")
if len(args) >= 2 {
service, ok := Lookup(args[0])
if !ok {
ctx.SendChain(message.Text("没有找到指定服务!"))
return
}
grp := ctx.Event.GroupID
if strings.Contains(model.Command, "全局") || strings.Contains(model.Command, "all") {
grp = 0
}
msg := "**" + args[0] + "报告**"
if strings.Contains(model.Command, "允许") || strings.Contains(model.Command, "permit") {
for _, usr := range args[1:] {
uid, err := strconv.ParseInt(usr, 10, 64)
if err == nil {
service.Permit(uid, grp)
msg += "\n+ 已允许" + usr
}
}
} else {
for _, usr := range args[1:] {
uid, err := strconv.ParseInt(usr, 10, 64)
if err == nil {
service.Ban(uid, grp)
msg += "\n- 已禁止" + usr
}
}
}
ctx.SendChain(message.Text(msg))
return
}
ctx.SendChain(message.Text("参数错误!"))
})
zero.OnCommandGroup([]string{"用法", "usage"}, userOrGrpAdmin).SetBlock(true).FirstPriority().
Handle(func(ctx *zero.Ctx) {
model := extension.CommandModel{}
_ = ctx.Parse(&model)
service, ok := Lookup(model.Args)
if !ok {
ctx.SendChain(message.Text("没有找到指定服务!"))
return
}
if service.options.Help != "" {
ctx.SendChain(message.Text(service.options.Help))
} else {
ctx.SendChain(message.Text("该服务无帮助!"))
}
})
zero.OnCommandGroup([]string{"服务列表", "service_list"}, userOrGrpAdmin).SetBlock(true).FirstPriority().
Handle(func(ctx *zero.Ctx) {
msg := "--------服务列表--------\n发送\"/用法 name\"查看详情"
i := 0
gid := ctx.Event.GroupID
ForEach(func(key string, manager *Control) bool {
i++
msg += "\n" + strconv.Itoa(i) + `: `
if manager.IsEnabledIn(gid) {
msg += "●" + key
} else {
msg += "○" + key
}
return true
})
ctx.SendChain(message.Text(msg))
})
zero.OnCommandGroup([]string{"服务详情", "service_detail"}, userOrGrpAdmin).SetBlock(true).FirstPriority().
Handle(func(ctx *zero.Ctx) {
text := "---服务详情---\n"
i := 0
ForEach(func(key string, manager *Control) bool {
service, _ := Lookup(key)
help := service.options.Help
i++
msg := strconv.Itoa(i) + `: `
if manager.IsEnabledIn(ctx.Event.GroupID) {
msg += "●" + key
} else {
msg += "○" + key
}
msg += "\n" + help
text += msg + "\n\n"
return true
})
data, err := txt2img.RenderToBase64(text, 40, 20)
if err != nil {
log.Errorf("[control] %v", err)
}
if id := ctx.SendChain(message.Image("base64://" + helper.BytesToString(data))); id == 0 {
ctx.SendChain(message.Text("ERROR: 可能被风控了"))
}
})
}
}
mu.Unlock()
}
}

View File

@@ -1,19 +0,0 @@
package control
// grpcfg holds the group config for the Manager.
type grpcfg struct {
GroupID int64 `db:"gid"` // GroupID 群号
Disable int64 `db:"disable"` // Disable 默认启用该插件
}
type ban struct {
ID int64 `db:"id"`
UserID int64 `db:"uid"`
GroupID int64 `db:"gid"`
}
// Options holds the optional parameters for the Manager.
type Options struct {
DisableOnDefault bool
Help string // 帮助文本信息
}

View File

@@ -1,561 +0,0 @@
// Package webctrl 包含 webui 所需的所有内容
package webctrl
import (
"encoding/json"
"fmt"
"io"
"net/http"
"os"
"strconv"
"sync"
manager "github.com/FloatTech/bot-manager"
// 依赖gin监听server
"github.com/gin-gonic/gin"
"github.com/gorilla/websocket"
// 前端静态文件
log "github.com/sirupsen/logrus"
zero "github.com/wdvxdr1123/ZeroBot"
"github.com/wdvxdr1123/ZeroBot/message"
ctrl "github.com/FloatTech/ZeroBot-Plugin/control"
)
var (
// 向前端推送消息的ws链接
conn *websocket.Conn
// 向前端推送日志的ws链接
logConn *websocket.Conn
l logWriter
// 存储请求事件flag作为键一个request对象作为值
requestData sync.Map
)
// logWriter
// @Description:
//
type logWriter struct {
}
// request
// @Description: 一个请求事件的结构体
//
type request struct {
RequestType string `json:"request_type"`
SubType string `json:"sub_type"`
Type string `json:"type"`
Comment string `json:"comment"`
GroupID int64 `json:"group_id"`
UserID int64 `json:"user_id"`
Flag string `json:"flag"`
SelfID int64 `json:"self_id"`
}
// InitGui 初始化gui
func InitGui(addr string) {
// 将日志重定向到前端hook
writer := io.MultiWriter(l, os.Stdout)
log.SetOutput(writer)
// 监听后端
go controller(addr)
// 注册消息handle
messageHandle()
}
// websocket的协议升级
var upGrader = websocket.Upgrader{
CheckOrigin: func(r *http.Request) bool {
return true
},
}
func controller(addr string) {
defer func() {
err := recover()
if err != nil {
log.Errorln("[gui]" + "bot-manager出现不可恢复的错误")
log.Errorln("[gui]", err)
}
}()
engine := gin.New()
// 支持跨域
engine.Use(cors())
// 注册静态文件
engine.StaticFS("/dist", http.FS(manager.Dist))
engine.POST("/get_bots", getBots)
engine.POST("/get_group_list", getGroupList)
engine.POST("/get_friend_list", getFriendList)
// 注册主路径路由,使其跳转到主页面
engine.GET("/", func(context *gin.Context) {
context.Redirect(http.StatusMovedPermanently, "/dist/dist/default.html")
})
// 更改某个插件状态
engine.POST("/update_plugin_status", updatePluginStatus)
// 更改某一个插件在所有群的状态
engine.POST("/update_plugin_all_group_status", updatePluginAllGroupStatus)
// 更改所有插件状态
engine.POST("/update_all_plugin_status", updateAllPluginStatus)
// 获取所有插件状态
engine.POST("/get_plugins_status", getPluginsStatus)
// 获取一个插件状态
engine.POST("/get_plugin_status", getPluginStatus)
// 获取插件列表
engine.POST("/get_plugins", func(context *gin.Context) {
var datas []map[string]interface{}
ctrl.ForEach(func(key string, manager *ctrl.Control) bool {
datas = append(datas, map[string]interface{}{"id": 1, "handle_type": "", "name": key, "enable": manager.IsEnabledIn(0)})
return true
})
context.JSON(200, datas)
})
// 获取所有请求
engine.POST("/get_requests", getRequests)
// 执行一个请求事件
engine.POST("handle_request", handelRequest)
// 链接日志
engine.GET("/get_log", getLogs)
// 获取前端标签
engine.GET("/get_label", func(context *gin.Context) {
context.JSON(200, "ZeroBot-Plugin")
})
// 发送信息
engine.POST("/send_msg", sendMsg)
engine.GET("/data", upgrade)
log.Infoln("[gui] the webui is running on", addr)
log.Infoln("[gui] ", "you input the `ZeroBot-Plugin.exe -g` can disable the gui")
if err := engine.Run(addr); err != nil {
log.Debugln("[gui] ", err.Error())
}
}
// handelRequest
/**
* @Description: 处理一个请求
* @param context
*/
func handelRequest(context *gin.Context) {
var data map[string]interface{}
err := context.BindJSON(&data)
if err != nil {
context.JSON(404, nil)
return
}
r, ok := requestData.LoadAndDelete(data["flag"].(string))
if !ok {
context.JSON(404, "flag not found")
}
r2 := r.(*request)
r2.handle(data["approve"].(bool), data["reason"].(string))
context.JSON(200, "操作成功")
}
// getRequests
/**
* @Description: 获取所有的请求
* @param context
*/
func getRequests(context *gin.Context) {
var data []interface{}
requestData.Range(func(key, value interface{}) bool {
data = append(data, value)
return true
})
context.JSON(200, data)
}
// updateAllPluginStatus
/**
* @Description: 改变所有插件的状态
* @param context
* example
*/
func updateAllPluginStatus(context *gin.Context) {
enable, err := strconv.ParseBool(context.PostForm("enable"))
if err != nil {
var parse map[string]interface{}
err := context.BindJSON(&parse)
if err != nil {
log.Errorln("[gui] " + err.Error())
return
}
enable = parse["enable"].(bool)
}
ctrl.ForEach(func(key string, manager *ctrl.Control) bool {
if enable {
manager.Enable(0)
} else {
manager.Disable(0)
}
return true
})
context.JSON(200, nil)
}
// updatePluginAllGroupStatus
/**
* @Description: 改变插件在所有群的状态
* @param context
* example
*/
func updatePluginAllGroupStatus(context *gin.Context) {
name := context.PostForm("name")
enable, err := strconv.ParseBool(context.PostForm("enable"))
if err != nil {
var parse map[string]interface{}
err := context.BindJSON(&parse)
if err != nil {
log.Errorln("[gui]" + err.Error())
return
}
name = parse["name"].(string)
enable = parse["enable"].(bool)
}
control, b := ctrl.Lookup(name)
if !b {
context.JSON(404, nil)
return
}
if enable {
control.Enable(0)
} else {
control.Disable(0)
}
context.JSON(200, nil)
}
// updatePluginStatus
/**
* @Description: 更改某一个插件状态
* @param context
* example
*/
func updatePluginStatus(context *gin.Context) {
var parse map[string]interface{}
err := context.BindJSON(&parse)
if err != nil {
log.Errorln("[gui] ", err)
return
}
groupID := int64(parse["group_id"].(float64))
name := parse["name"].(string)
enable := parse["enable"].(bool)
fmt.Println(name)
control, b := ctrl.Lookup(name)
if !b {
context.JSON(404, "服务不存在")
return
}
if enable {
control.Enable(groupID)
} else {
control.Disable(groupID)
}
context.JSON(200, nil)
}
// getPluginStatus
/**
* @Description: 获取一个插件的状态
* @param context
* example
*/
func getPluginStatus(context *gin.Context) {
groupID, err := strconv.ParseInt(context.PostForm("group_id"), 10, 64)
name := context.PostForm("name")
if err != nil {
var parse map[string]interface{}
err := context.BindJSON(&parse)
if err != nil {
log.Errorln("[gui]" + err.Error())
return
}
groupID = int64(parse["group_id"].(float64))
name = parse["name"].(string)
}
control, b := ctrl.Lookup(name)
if !b {
context.JSON(404, "服务不存在")
return
}
context.JSON(200, gin.H{"enable": control.IsEnabledIn(groupID)})
}
// getPluginsStatus
/**
* @Description: 获取所有插件的状态
* @param context
* example
*/
func getPluginsStatus(context *gin.Context) {
groupID, err := strconv.ParseInt(context.PostForm("group_id"), 10, 64)
if err != nil {
var parse map[string]interface{}
err := context.BindJSON(&parse)
if err != nil {
log.Errorln("[gui]" + err.Error())
return
}
groupID = int64(parse["group_id"].(float64))
}
var datas []map[string]interface{}
ctrl.ForEach(func(key string, manager *ctrl.Control) bool {
enable := manager.IsEnabledIn(groupID)
datas = append(datas, map[string]interface{}{"name": key, "enable": enable})
return true
})
context.JSON(200, datas)
}
// getLogs
/**
* @Description: 连接日志
* @param context
* example
*/
func getLogs(context *gin.Context) {
con1, err := upGrader.Upgrade(context.Writer, context.Request, nil)
if err != nil {
return
}
logConn = con1
}
// getFriendList
/**
* @Description: 获取好友列表
* @param context
* example
*/
func getFriendList(context *gin.Context) {
selfID, err := strconv.Atoi(context.PostForm("self_id"))
if err != nil {
log.Errorln("[gui]" + err.Error())
var data map[string]interface{}
err := context.BindJSON(&data)
if err != nil {
log.Errorln("[gui]" + err.Error())
log.Errorln("[gui]" + "绑定错误")
return
}
selfID = int(data["self_id"].(float64))
}
bot := zero.GetBot(int64(selfID))
var resp []interface{}
list := bot.GetFriendList().String()
err = json.Unmarshal([]byte(list), &resp)
if err != nil {
log.Errorln("[gui]" + err.Error())
log.Errorln("[gui]" + "解析json错误")
}
context.JSON(200, resp)
}
// getGroupList
/**
* @Description: 获取群列表
* @param context
* example
*/
func getGroupList(context *gin.Context) {
selfID, err := strconv.Atoi(context.PostForm("self_id"))
if err != nil {
var data map[string]interface{}
err := context.BindJSON(&data)
if err != nil {
log.Errorln("[gui]" + err.Error())
return
}
selfID = int(data["self_id"].(float64))
}
bot := zero.GetBot(int64(selfID))
var resp []interface{}
list := bot.GetGroupList().String()
err = json.Unmarshal([]byte(list), &resp)
if err != nil {
log.Errorln("[gui]" + err.Error())
}
context.JSON(200, resp)
}
// getBots
/**
* @Description: 获取机器人qq号
* @param context
* example
*/
func getBots(context *gin.Context) {
var bots []int64
zero.RangeBot(func(id int64, ctx *zero.Ctx) bool {
bots = append(bots, id)
return true
})
context.JSON(200, bots)
}
// MessageHandle
/**
* @Description: 定义一个向前端发送信息的handle
* example
*/
func messageHandle() {
defer func() {
err := recover()
if err != nil {
log.Errorln("[gui]" + "bot-manager出现不可恢复的错误")
log.Errorln("[gui] ", err)
}
}()
matcher := zero.OnMessage().SetBlock(false).SetPriority(1)
matcher.Handle(func(ctx *zero.Ctx) {
if conn != nil {
err := conn.WriteJSON(ctx.Event)
if err != nil {
log.Debugln("[gui] " + "向发送错误")
return
}
}
})
// 直接注册一个request请求监听器优先级设置为最高设置不阻断事件传播
zero.OnRequest(func(ctx *zero.Ctx) bool {
if ctx.Event.RequestType == "friend" {
ctx.State["type_name"] = "好友添加"
} else {
if ctx.Event.SubType == "add" {
ctx.State["type_name"] = "加群请求"
} else {
ctx.State["type_name"] = "群邀请"
}
}
return true
}).SetBlock(false).FirstPriority().Handle(func(ctx *zero.Ctx) {
r := &request{
RequestType: ctx.Event.RequestType,
SubType: ctx.Event.SubType,
Type: ctx.State["type_name"].(string),
GroupID: ctx.Event.GroupID,
UserID: ctx.Event.UserID,
Flag: ctx.Event.Flag,
Comment: ctx.Event.Comment,
SelfID: ctx.Event.SelfID,
}
requestData.Store(ctx.Event.Flag, r)
})
}
// upgrade
/**
* @Description: 连接ws向前端推送message
* @param context
* example
*/
func upgrade(context *gin.Context) {
con, err := upGrader.Upgrade(context.Writer, context.Request, nil)
if err != nil {
return
}
conn = con
}
// sendMsg
/**
* @Description: 前端调用发送信息
* @param context
* example
*/
func sendMsg(context *gin.Context) {
var data map[string]interface{}
err := context.BindJSON(&data)
if err != nil {
context.JSON(404, nil)
return
}
selfID := int64(data["self_id"].(float64))
id := int64(data["id"].(float64))
message1 := data["message"].(string)
messageType := data["message_type"].(string)
bot := zero.GetBot(selfID)
var msgID int64
if messageType == "group" {
msgID = bot.SendGroupMessage(id, message.ParseMessageFromString(message1))
} else {
msgID = bot.SendPrivateMessage(id, message.ParseMessageFromString(message1))
}
context.JSON(200, msgID)
}
// cors
/**
* @Description: 支持跨域访问
* @return gin.HandlerFunc
* example
*/
func cors() gin.HandlerFunc {
return func(c *gin.Context) {
method := c.Request.Method
origin := c.Request.Header.Get("Origin") // 请求头部
if origin != "" {
// 接收客户端发送的origin (重要!)
c.Writer.Header().Set("Access-Control-Allow-Origin", origin)
// 服务器支持的所有跨域请求的方法
c.Header("Access-Control-Allow-Methods", "POST, GET, OPTIONS, PUT, DELETE,UPDATE")
// 允许跨域设置可以返回其他子段,可以自定义字段
c.Header("Access-Control-Allow-Headers", "Authorization, Content-Length, X-CSRF-Token, Token,session, Content-Type")
// 允许浏览器(客户端)可以解析的头部 (重要)
c.Header("Access-Control-Expose-Headers", "Content-Length, Access-Control-Allow-Origin, Access-Control-Allow-Headers")
// 设置缓存时间
c.Header("Access-Control-Max-Age", "172800")
// 允许客户端传递校验信息比如 cookie (重要)
c.Header("Access-Control-Allow-Credentials", "true")
}
// 允许类型校验
if method == "OPTIONS" {
c.JSON(http.StatusOK, "ok!")
}
defer func() {
if err := recover(); err != nil {
log.Printf("Panic info is: %v", err)
}
}()
c.Next()
}
}
// handle
/**
* @Description: 提交一个请求
* @receiver r
* @param approve 是否通过
* @param reason 拒绝的理由
*/
func (r *request) handle(approve bool, reason string) {
bot := zero.GetBot(r.SelfID)
if r.RequestType == "friend" {
bot.SetFriendAddRequest(r.Flag, approve, "")
} else {
bot.SetGroupAddRequest(r.Flag, r.SubType, approve, reason)
}
log.Debugln("[gui] ", "已处理", r.UserID, "的"+r.Type)
}
func (l logWriter) Write(p []byte) (n int, err error) {
if logConn != nil {
err := logConn.WriteMessage(websocket.TextMessage, p)
if err != nil {
return len(p), nil
}
}
return len(p), nil
}

2
data

Submodule data updated: 219b63ccef...5ce4b48c12

100
go.mod
View File

@@ -1,81 +1,77 @@
module github.com/FloatTech/ZeroBot-Plugin
go 1.17
go 1.18
require (
github.com/FloatTech/AnimeAPI v1.1.11
github.com/FloatTech/ZeroBot-Plugin-Gif v0.2.4
github.com/FloatTech/bot-manager v1.0.1-0.20211112011524-85b9895271ed
github.com/antchfx/htmlquery v1.2.4
github.com/Coloured-glaze/gg v1.3.2
github.com/FloatTech/AnimeAPI v1.5.1-0.20220816023211-5e11b0d21958
github.com/FloatTech/sqlite v0.3.3
github.com/FloatTech/zbpctrl v1.4.1-0.20220715042842-93f081cb0133
github.com/FloatTech/zbputils v1.5.1-0.20220816020252-0f57a5ae28aa
github.com/antchfx/htmlquery v1.2.5
github.com/corona10/goimagehash v1.0.3
github.com/fogleman/gg v1.3.0
github.com/fumiama/cron v1.3.0
github.com/fumiama/go-base16384 v1.2.1
github.com/fumiama/go-registry v0.0.2
github.com/fumiama/gofastTEA v0.0.6
github.com/fumiama/go-base16384 v1.5.4
github.com/fumiama/go-registry v0.1.6
github.com/fumiama/gofastTEA v0.0.10
github.com/fumiama/gotracemoe v0.0.3
github.com/gin-gonic/gin v1.7.7
github.com/gorilla/websocket v1.4.2
github.com/fumiama/sqlite3 v1.14.6
github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0
github.com/jinzhu/gorm v1.9.16
github.com/logoove/sqlite v1.13.0
github.com/mattn/go-runewidth v0.0.13
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/shirou/gopsutil/v3 v3.21.12
github.com/sirupsen/logrus v1.8.1
github.com/tidwall/gjson v1.12.1
github.com/wdvxdr1123/ZeroBot v1.4.1
golang.org/x/image v0.0.0-20211028202545-6944b10bf410
golang.org/x/text v0.3.7
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.9.0
github.com/tidwall/gjson v1.14.2
github.com/wcharczuk/go-chart/v2 v2.1.0
github.com/wdvxdr1123/ZeroBot v1.5.2-0.20220715040337-ef4327320c40
gitlab.com/gomidi/midi/v2 v2.0.17
golang.org/x/image v0.0.0-20220722155232-062f8c9fd539
)
require (
github.com/FloatTech/imgfactory v0.1.1 // indirect
github.com/antchfx/xpath v1.2.0 // indirect
github.com/FloatTech/ttl v0.0.0-20220715042055-15612be72f5b // indirect
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/gin-contrib/sse v0.1.0 // indirect
github.com/fsnotify/fsnotify v1.4.9 // indirect
github.com/gabriel-vasile/mimetype v1.0.4 // indirect
github.com/go-ole/go-ole v1.2.6 // indirect
github.com/go-playground/locales v0.14.0 // indirect
github.com/go-playground/universal-translator v0.18.0 // indirect
github.com/go-playground/validator/v10 v10.10.0 // indirect
github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0 // 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/protobuf v1.5.2 // indirect
github.com/google/uuid v1.3.0 // indirect
github.com/jinzhu/inflection v1.0.0 // indirect
github.com/json-iterator/go v1.1.12 // indirect
github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 // indirect
github.com/leodido/go-urn v1.2.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/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
github.com/modern-go/reflect2 v1.0.2 // 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/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c // indirect
github.com/remyoudompheng/bigfft v0.0.0-20200410134404-eec4a21b6bb0 // indirect
github.com/rivo/uniseg v0.2.0 // indirect
github.com/tidwall/match v1.1.1 // indirect
github.com/tidwall/pretty v1.2.0 // indirect
github.com/tklauser/go-sysconf v0.3.9 // indirect
github.com/tklauser/numcpus v0.3.0 // indirect
github.com/ugorji/go/codec v1.2.6 // indirect
github.com/tklauser/go-sysconf v0.3.10 // indirect
github.com/tklauser/numcpus v0.4.0 // indirect
github.com/yusufpapurcu/wmi v1.2.2 // indirect
golang.org/x/crypto v0.0.0-20211215153901-e495a2d5b3d3 // indirect
golang.org/x/mod v0.5.1 // indirect
golang.org/x/net v0.0.0-20220105145211-5b0dc2dfae98 // indirect
golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e // indirect
golang.org/x/tools v0.1.8 // 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-20220805013720-a33c5aa5df48 // indirect
golang.org/x/sys v0.0.0-20220804214406-8e32c043e418 // 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
google.golang.org/protobuf v1.27.1 // indirect
gopkg.in/yaml.v2 v2.4.0 // indirect
lukechampine.com/uint128 v1.1.1 // indirect
modernc.org/cc/v3 v3.35.22 // indirect
modernc.org/ccgo/v3 v3.14.0 // indirect
modernc.org/libc v1.13.2 // indirect
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 // indirect
modernc.org/libc v1.16.19 // indirect
modernc.org/mathutil v1.4.1 // indirect
modernc.org/memory v1.0.5 // indirect
modernc.org/opt v0.1.1 // indirect
modernc.org/sqlite v1.14.3 // indirect
modernc.org/strutil v1.1.1 // indirect
modernc.org/token v1.0.0 // indirect
modernc.org/memory v1.1.1 // indirect
)

483
go.sum
View File

@@ -1,28 +1,45 @@
github.com/FloatTech/AnimeAPI v1.1.9/go.mod h1:CC+vF30UGBlcIUxwFOcXIEHoJ4r7c5x2iLQsnUCVdDI=
github.com/FloatTech/AnimeAPI v1.1.11 h1:uuV4v5qweh0mI0E2KMiG5XGt0pKboV/EFAlIfSJxIi8=
github.com/FloatTech/AnimeAPI v1.1.11/go.mod h1:CC+vF30UGBlcIUxwFOcXIEHoJ4r7c5x2iLQsnUCVdDI=
github.com/FloatTech/ZeroBot-Plugin v1.1.5/go.mod h1:kWuUARvU7gs4xLggi8Sy37ja2GRL6k0X6kewe5TiZRs=
github.com/FloatTech/ZeroBot-Plugin-Gif v0.2.4 h1:WW0BmmLLqAg+m6qGkrKbsfSIm91fkj3/udt3R7Myodo=
github.com/FloatTech/ZeroBot-Plugin-Gif v0.2.4/go.mod h1:W7ag6hml1pZTNzRXKU74OMr6rS8awQKSU+o2g7Gj4O0=
github.com/FloatTech/ZeroBot-Plugin-Timer v1.4.3/go.mod h1:MVOQQ4e6AVGFm993blXXU4Sd6bAsLY2+Zb+/HMrEeEc=
github.com/FloatTech/bot-manager v1.0.1-0.20211112011524-85b9895271ed h1:GEOgDVbvaxXqZxgWE/y5JOlbMXrmq7n0M+m9g3md2To=
github.com/FloatTech/bot-manager v1.0.1-0.20211112011524-85b9895271ed/go.mod h1:8YYRJ16oroGHQGD2En0oVnmcKJkxR9O/jd5BPSfWfOQ=
github.com/FloatTech/imgfactory v0.1.1 h1:ooL2+fV8yrMhv1ShGGKsN0Rm/flWoKnvqXaUD+dC3DQ=
github.com/FloatTech/imgfactory v0.1.1/go.mod h1:ThDALab8aOuU6KVYESVWFqmjcqtm03e0SvGlTw6s+aw=
github.com/Mrs4s/MiraiGo v0.0.0-20211120033824-43b23f4e6fcb h1:Rkj28fqIwGx/EgBzRYtpmJRfH6wqVn7cNdc7aJ0QE4M=
github.com/Mrs4s/MiraiGo v0.0.0-20211120033824-43b23f4e6fcb/go.mod h1:imVKbfKqqeit+C/eaWGb4MKQ3z3gN6pRpBU5RMtp5so=
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/Coloured-glaze/gg v1.3.2 h1:HRWF0qoIxwhO1O0N5eN2eTPwaO+xHpFu1FfNhtls22w=
github.com/Coloured-glaze/gg v1.3.2/go.mod h1:Ih5NLNNDHOy3RJbB0EPqGTreIzq/H02TGThIagh8HJg=
github.com/FloatTech/AnimeAPI v1.5.1-0.20220816023211-5e11b0d21958 h1:BXjlcvPK/7IwaW1HTEPt/pMvbPrc3id1NfFkJ+lh1e4=
github.com/FloatTech/AnimeAPI v1.5.1-0.20220816023211-5e11b0d21958/go.mod h1:vhrzmpfFp9p4V1T+xY2OaFUAx498LmMxdk/UqJmPMTc=
github.com/FloatTech/sqlite v0.3.3 h1:cSlpv+DcIehqaUG2YyjW0Twh396FoGsybpEpFZnX3Jg=
github.com/FloatTech/sqlite v0.3.3/go.mod h1:i33d92OtR8jcp5fBUvQtospf27+MkfUxnGwnZ95E/dA=
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.4.1-0.20220715042842-93f081cb0133 h1:nP9NI4I+vtwAbiU7wCJwjuzCzMZ/yJYg8h3667HGnv0=
github.com/FloatTech/zbpctrl v1.4.1-0.20220715042842-93f081cb0133/go.mod h1:72BnjyBwQWUC8mqM9dPk5ZrjxXCilQCVp+jfgHATNdw=
github.com/FloatTech/zbputils v1.5.1-0.20220816020252-0f57a5ae28aa h1:nltmv5w1cdB0vU52zkLFI9a5FdBozaAhZZq/GCLi6Xs=
github.com/FloatTech/zbputils v1.5.1-0.20220816020252-0f57a5ae28aa/go.mod h1:l06shz0lp4jqMBZDJaRRSUdOlIwg99cIKV4sx4YR0DI=
github.com/PuerkitoBio/goquery v1.5.1/go.mod h1:GsLWisAFVj4WgDibEWF4pvYnkVQBpKBKeU+7zCJoLcc=
github.com/StackExchange/wmi v1.2.1/go.mod h1:rcmrprowKIVzvc+NUiLncP2uuArMWLCbu9SBzvHz7e8=
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/andybalholm/cascadia v1.1.0/go.mod h1:GsXiBklL0woXo1j/WYWtSYYC4ouU9PqHO0sqidkEA4Y=
github.com/antchfx/htmlquery v1.2.3/go.mod h1:B0ABL+F5irhhMWg54ymEZinzMSi0Kt3I2if0BLYa3V0=
github.com/antchfx/htmlquery v1.2.4 h1:qLteofCMe/KGovBI6SQgmou2QNyedFUW+pE+BpeZ494=
github.com/antchfx/htmlquery v1.2.4/go.mod h1:2xO6iu3EVWs7R2JYqBbp8YzG50gj/ofqs5/0VZoDZLc=
github.com/antchfx/xpath v1.1.6/go.mod h1:Yee4kTMuNiPYJ7nSNorELQMr1J33uOpXDMByNYhvtNk=
github.com/antchfx/xpath v1.2.0 h1:mbwv7co+x0RwgeGAOHdrKy89GvHaGvxxBtPK0uF9Zr8=
github.com/antchfx/xpath v1.2.0/go.mod h1:i54GszH55fYfBmoZXapTHN8T8tkcHfRgLyVwwqzXNcs=
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/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
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=
@@ -30,280 +47,381 @@ github.com/denisenkom/go-mssqldb v0.0.0-20191124224453-732737034ffd h1:83Wprp6RO
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 h1:VSnTsYCnlFHaM2/igO1h6X3HA71jcobQuxemgkq4zYo=
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/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/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568/go.mod h1:xEzjJPgXI435gkrCt3MPfRiAkVrwSbHsst4LCFVfpJc=
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/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.2.1 h1:6OGprW8g/95m2ocmryHi8mipZ7bx9StFMZDKEqLvMiA=
github.com/fumiama/go-base16384 v1.2.1/go.mod h1:1HTC0QFL7BjS0DuO5Qm+fBYKQkHqmAapLbRpCxrhPXQ=
github.com/fumiama/go-registry v0.0.2 h1:2EoZwZpqI7YhkQ1FnuAPvALYPpvUtbsCqk879+r7ehs=
github.com/fumiama/go-registry v0.0.2/go.mod h1:QkcmmHuw1y6y/w7/HiH1c9yjBw5Zt+6EER6YJKl9xh8=
github.com/fumiama/gofastTEA v0.0.6 h1:Yni3MXDbJVa/c4CecgdZDgCJK+fLdvGph+OBqY2mtiI=
github.com/fumiama/gofastTEA v0.0.6/go.mod h1:+sBZ05nCA2skZkursHNvyr8kULlEetrYTM2y5kA4rQc=
github.com/fumiama/go-base16384 v1.5.4 h1:UKx925X7cTsqsVWBlBLBs1v38epFT/q3AGjvyP5E0PM=
github.com/fumiama/go-base16384 v1.5.4/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/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/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE=
github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI=
github.com/gin-gonic/gin v1.7.7 h1:3DoBmSbJbZAWqXJC3SLjAPfutPJJRN1U5pALB7EeTTs=
github.com/gin-gonic/gin v1.7.7/go.mod h1:axIBovoeJpVj8S3BwE0uPMTeReE4+AfFtqpqaZ1qq1U=
github.com/go-ole/go-ole v1.2.5/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0=
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/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/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.0.1/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4=
github.com/go-playground/locales v0.13.0/go.mod h1:taPMhCMXrRLJO55olJkUXHZBHCxTMfnGwq/HNwmWNS8=
github.com/go-playground/locales v0.14.0 h1:u50s323jtVGugKlcYeyzC0etD1HifMjqmJqb8WugfUU=
github.com/go-playground/locales v0.14.0/go.mod h1:sawfccIbzZTqEDETgFXqTho0QybSa7l++s0DH+LDiLs=
github.com/go-playground/universal-translator v0.17.0/go.mod h1:UkSxE5sNxxRwHyU+Scu5vgOQjsIJAF8j9muTVoKLVtA=
github.com/go-playground/universal-translator v0.18.0 h1:82dyy6p4OuJq4/CByFNOn/jYrnRPArHwAcmLoJZxyho=
github.com/go-playground/universal-translator v0.18.0/go.mod h1:UvRDBj+xPUEGrFYl+lu/H90nyDXpg0fqeB/AQUGNTVA=
github.com/go-playground/validator/v10 v10.4.1/go.mod h1:nlOn6nFhuKACm19sB/8EGNn9GlaMV7XkbRSipzJ0Ii4=
github.com/go-playground/validator/v10 v10.10.0 h1:I7mrTYv78z8k8VXa/qJlOlEXn/nBh+BF8dHX5nt/dr0=
github.com/go-playground/validator/v10 v10.10.0/go.mod h1:74x4gJWsvQexRdW8Pn3dXSGrTK4nAUsbPlLADvpJkos=
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.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw=
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/golang/snappy v0.0.0-20180518054509-2e65f85255db/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
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 h1:BKbKCqvP6I+rmFHt06ZmyQtvB8xAkWdhFyr0ZUNZcxQ=
github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
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/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I=
github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/gorilla/websocket v1.4.2 h1:+/TMaTYc4QFitKJxsQ7Yye35DkWvkdLcvGKqM+x0Ufc=
github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
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/imroc/req v0.3.0/go.mod h1:F+NZ+2EFSo6EFXdeIbpfE9hcC233id70kf0byW97Caw=
github.com/jellevandenhooff/dkim v0.0.0-20150330215556-f50fe3d243e1/go.mod h1:E0B/fFc00Y+Rasa88328GlI/XbtyysCtTHZS8h7IrBU=
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=
github.com/jinzhu/inflection v1.0.0/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc=
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/json-iterator/go v1.1.9/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
github.com/json-iterator/go v1.1.11/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM=
github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo=
github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 h1:Z9n2FFNUXsshfwJMBgNA0RU6/i7WVaAegv3PtuIHPMs=
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/klauspost/compress v1.13.6/go.mod h1:/3/Vjq9QcHkK5uEr5lBEmyoZ1iFhe47etQ6QUkpK6sk=
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
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/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
github.com/kr/pretty v0.3.0 h1:WgNl7dwNpEZ6jJ9k1snq4pZsg7DOEN8hP9Xw0Tsjwk0=
github.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk=
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/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
github.com/leodido/go-urn v1.2.0/go.mod h1:+8+nEpDfqqsY+g338gtMEUOtuK+4dEMhiQEgxpxOKII=
github.com/leodido/go-urn v1.2.1 h1:BqpAaACuzVSgi/VLzGZIobT2z4v53pjosyNd9Yv6n/w=
github.com/leodido/go-urn v1.2.1/go.mod h1:zt4jvISO2HfUBqxjfIshjdMTYS56ZS/qv49ictyFfxY=
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/logoove/sqlite v1.13.0 h1:XM7QKK9R3tm8o7bI75R3zmwYBFQ5S3Jqg+XCaqsAMQQ=
github.com/logoove/sqlite v1.13.0/go.mod h1:MRpE/o3qQhT7AgfIdnBue5c63+//xT+KXV0gHeVAUAg=
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/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-runewidth v0.0.13 h1:lTGmDsbAYt5DmK6OnoV7EuIF1wEIFAcxld6ypU4OSgU=
github.com/mattn/go-runewidth v0.0.13/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
github.com/mattn/go-sqlite3 v1.14.0 h1:mLyGNKR8+Vv9CAU7PphKa2hkEqxxhn8i32J6FPj1/QA=
github.com/mattn/go-sqlite3 v1.14.0/go.mod h1:JIl7NbARA7phWnGvh0LKTyg7S9BA+6gx71ShQilpsus=
github.com/mattn/go-sqlite3 v1.14.8/go.mod h1:NyWgC/yNuGj7Q9rpYnZvas74GogHl5/Z4A/KQRfk6bU=
github.com/mattn/go-sqlite3 v1.14.9 h1:10HX2Td0ocZpYEjhilsuo6WWtUqttj2Kb0KtD86/KYA=
github.com/mattn/go-sqlite3 v1.14.9/go.mod h1:NyWgC/yNuGj7Q9rpYnZvas74GogHl5/Z4A/KQRfk6bU=
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg=
github.com/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 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
github.com/modern-go/reflect2 v1.0.2-0.20210109003243-333559e1834b/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M=
github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
github.com/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/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.7.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA=
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
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/pkg/errors v0.8.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/rivo/uniseg v0.2.0 h1:S1pD9weZBuJdFmowNwbpi7BJ8TNftyUImj/0WQi72jY=
github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
github.com/robfig/cron v1.2.0/go.mod h1:JGuDeoQd7Z6yL4zQhZ3OPEVHB7fL6Ka6skscFHfmt2k=
github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc=
github.com/rogpeppe/go-internal v1.8.0 h1:FCbCCtXNOY3UtUuHUYaghJg4y7Fd14rXifAYUAtL9R8=
github.com/rogpeppe/go-internal v1.8.0/go.mod h1:WmiCO8CzOY8rg0OYDC4/i/2WRWAB6poM+XZ2dLUbcbE=
github.com/shirou/gopsutil v3.21.8+incompatible h1:sh0foI8tMRlCidUJR+KzqWYWxrkuuPIGiO6Vp+KXdCU=
github.com/shirou/gopsutil v3.21.8+incompatible/go.mod h1:5b4v6he4MtMOwMlS0TUMTu2PcXUg8+E1lC7eC3UO/RA=
github.com/shirou/gopsutil/v3 v3.21.12 h1:VoGxEW2hpmz0Vt3wUvHIl9fquzYLNpVpgNNB7pGJimA=
github.com/shirou/gopsutil/v3 v3.21.12/go.mod h1:BToYZVTlSVlfazpDDYFnsVZLaoRG+g8ufT6fPQLdJzA=
github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE=
github.com/sirupsen/logrus v1.8.1 h1:dJKuHgqk1NNQlqoA6BTlM1Wf9DOH3NBjQyu0h9+AZZE=
github.com/sirupsen/logrus v1.8.1/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0=
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.9.0 h1:trlNQbNUG3OdDrDil03MCb1H2o9nJ1x4/5LYw7byDE0=
github.com/sirupsen/logrus v1.9.0/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
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/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY=
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/syndtr/goleveldb v1.0.0/go.mod h1:ZVVdQEZoIme9iO1Ch2Jdy24qqXrMMOU6lpPAyBWyWuQ=
github.com/t-tomalak/logrus-easy-formatter v0.0.0-20190827215021-c074f06c5816/go.mod h1:tzym/CEb5jnFI+Q0k4Qq3+LvRF4gO3E2pxS8fHP8jcA=
github.com/tdf1939/ZeroBot-Plugin-Gif v0.0.0-20210828060956-389b1dc33652/go.mod h1:bkxKi7un9gCDvUUZAiIJF6k90pyj8rmxiXLJkiHcsMY=
github.com/tdf1939/img v0.0.0-20210827153520-90cb4e9580a3/go.mod h1:FgTEOcosTWrkOr7++gbtPSj1rX5loRWrf/AL+hm3Cnw=
github.com/tidwall/gjson v1.8.0/go.mod h1:5/xDoumyyDNerp2U36lyolv46b3uF/9Bu6OfyQ9GImk=
github.com/tidwall/gjson v1.9.0/go.mod h1:5/xDoumyyDNerp2U36lyolv46b3uF/9Bu6OfyQ9GImk=
github.com/tidwall/gjson v1.11.0/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk=
github.com/tidwall/gjson v1.12.1 h1:ikuZsLdhr8Ws0IdROXUS1Gi4v9Z4pGqpX/CvJkxvfpo=
github.com/tidwall/gjson v1.12.1/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk=
github.com/tidwall/match v1.0.3/go.mod h1:eRSPERbgtNPcGhD8UCthc6PmLEQXEWd3PRB5JTxsfmM=
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.8.0 h1:pSgiaMZlXftHpm5L7V1+rVB+AZJydKsMxsQBIJw4PKk=
github.com/tarm/serial v0.0.0-20180830185346-98f6abe2eb07/go.mod h1:kDXzergiv9cbyO7IOYJZWg1U88JhDg3PB6klq9Hg2pA=
github.com/tidwall/gjson v1.14.2 h1:6BBkirS0rAHjumnjHF6qgy5d2YAJ1TLIaFE2lzfOLqo=
github.com/tidwall/gjson v1.14.2/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.1.0/go.mod h1:XNkn88O1ChpSDQmQeStsy+sBenx6DDtFZJxhVysOjyk=
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.9 h1:JeUVdAOWhhxVcU6Eqr/ATFHgXk/mmiItdKeJPev3vTo=
github.com/tklauser/go-sysconf v0.3.9/go.mod h1:11DU/5sG7UexIrp/O6g35hrWzu0JxlwQ3LSFUzyeuhs=
github.com/tklauser/numcpus v0.3.0 h1:ILuRUQBtssgnxw0XXIjKUC56fgnOrFoQQ/4+DeU2biQ=
github.com/tklauser/numcpus v0.3.0/go.mod h1:yFGUr7TUHQRAhyqBcEg0Ge34zDBAsIvJJcyE6boqnA8=
github.com/ugorji/go v1.1.7/go.mod h1:kZn38zHttfInRq0xu/PH0az30d+z6vm202qpg1oXVMw=
github.com/ugorji/go v1.2.6 h1:tGiWC9HENWE2tqYycIqFTNorMmFRVhNwCpDOpWqnk8E=
github.com/ugorji/go v1.2.6/go.mod h1:anCg0y61KIhDlPZmnH+so+RQbysYVyDko0IMgJv0Nn0=
github.com/ugorji/go/codec v1.1.7/go.mod h1:Ax+UKWsSmolVDwsd+7N3ZtXu+yMGCf907BLYF3GoBXY=
github.com/ugorji/go/codec v1.2.6 h1:7kbGefxLoDBuYXOms4yD7223OpNMMPNPZxXk5TvFcyQ=
github.com/ugorji/go/codec v1.2.6/go.mod h1:V6TCNZ4PHqoHGFZuSG1W8nrCzzdgA2DozYxWFFpvxTw=
github.com/wdvxdr1123/ZeroBot v1.2.2/go.mod h1:83nHtG8V5TAxPwH/LCDxLpZk4khIgs29dkr5TBWf7fc=
github.com/wdvxdr1123/ZeroBot v1.2.3/go.mod h1:83nHtG8V5TAxPwH/LCDxLpZk4khIgs29dkr5TBWf7fc=
github.com/wdvxdr1123/ZeroBot v1.2.4/go.mod h1:83nHtG8V5TAxPwH/LCDxLpZk4khIgs29dkr5TBWf7fc=
github.com/wdvxdr1123/ZeroBot v1.3.2/go.mod h1:i2DIqQjtjE+3gvVi9r9sc+QpNaUuyTXx/HNXXayIpwI=
github.com/wdvxdr1123/ZeroBot v1.4.1 h1:fk/8RH2D1gB3YeC1eI/SZi/kG31Rh7Z8lAiDc60VZFM=
github.com/wdvxdr1123/ZeroBot v1.4.1/go.mod h1:7t9m4vDZPwWAmzKlhP6IvUoisOIiqNdm/3AJgiY3+ew=
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/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.20220715040337-ef4327320c40 h1:j+cmfkjc8WcBKiOsvMzcIgAuPhiTs7FBeiRn/QLVFhA=
github.com/wdvxdr1123/ZeroBot v1.5.2-0.20220715040337-ef4327320c40/go.mod h1:LJ+VOf523i3IrykuLK53UEeWqnAclRL5d2wGT4sS4Zk=
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.4.1/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
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/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=
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-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/crypto v0.0.0-20210711020723-a769d52b0f97/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
golang.org/x/crypto v0.0.0-20211215153901-e495a2d5b3d3 h1:0es+/5331RGQPcXlMfP+WrnIIS6dNnNRe0WB02W0F4M=
golang.org/x/crypto v0.0.0-20211215153901-e495a2d5b3d3/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/image v0.0.0-20191009234506-e7c1f5e7dbb8/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
golang.org/x/image v0.0.0-20210628002857-a66eb6448b8d/go.mod h1:023OzeP/+EPmXeapQh35lcL3II3LrY8Ic+EFFKVhULM=
golang.org/x/image v0.0.0-20211028202545-6944b10bf410 h1:hTftEOvwiOq2+O8k2D5/Q7COC7k5Qcrgc2TFURJYnvQ=
golang.org/x/image v0.0.0-20211028202545-6944b10bf410/go.mod h1:023OzeP/+EPmXeapQh35lcL3II3LrY8Ic+EFFKVhULM=
golang.org/x/image v0.0.0-20200927104501-e162460cd6b5/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
golang.org/x/image v0.0.0-20220722155232-062f8c9fd539 h1:/eM0PCrQI2xd471rI+snWuu251/+/jpBpZqir2mPdnU=
golang.org/x/image v0.0.0-20220722155232-062f8c9fd539/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.5.1 h1:OJxoQ/rynoF0dcCdI7cLPktw/hR2cueqYfjm43oqK38=
golang.org/x/mod v0.5.1/go.mod h1:5OXOZSfqPIIbmVBIIKWRFfZjPR0E5r58TLhUjH0a2Ro=
golang.org/x/mod v0.4.2 h1:Gz96sIWK3OalVv/I/qNygP42zyoKp3xptRVCWRFEBvo=
golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
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-20211015210444-4f30a5c0130f/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20220105145211-5b0dc2dfae98 h1:+6WJMRLHlD7X7frgp7TUZ36RnQzSf9wVVTNakEp+nqY=
golang.org/x/net v0.0.0-20220105145211-5b0dc2dfae98/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
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-20220805013720-a33c5aa5df48 h1:N9Vc/rorQUDes6B9CNdIxAn5jODGj2wzfrei2x4wNj4=
golang.org/x/net v0.0.0-20220805013720-a33c5aa5df48/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk=
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/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/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-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/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-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-20210806184541-e5e7981a1069/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210816074244-15123e1e1f71/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-20211013075003-97ac67df715c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20211019181941-9d821ace8654/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e h1:fLOSk5Q00efkSvAm+4xcoXD+RRmLmmulPn5I3Y9F2EM=
golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/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-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220804214406-8e32c043e418 h1:9vYwv7OjYaky/tlAeD7C4oC9EsPTlaFl1H2jS++V+ME=
golang.org/x/sys v0.0.0-20220804214406-8e32c043e418/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
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/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-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.1.8 h1:P1HhGGuLW4aAclzjtmJdf0mJOjVUZUzOTqkAkWL+l6w=
golang.org/x/tools v0.1.8/go.mod h1:nABZi5QlRsZVlzPpHl034qft6wpY4eDcsTt5AaioBiU=
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.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
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.27.1 h1:SnqbnDw1V7RiZcXPx5MEeqPv2s79L9i7BJUlG/+RurQ=
google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
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/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
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/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.8/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.0-20210107192922-496545a6307b h1:h8qDotaEPuJATrMmW04NCwg7v22aHH28wwpauUhK9Oo=
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
lukechampine.com/uint128 v1.1.1 h1:pnxCASz787iMf+02ssImqk6OLt+Z5QHMoZyUXR4z6JU=
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=
@@ -320,13 +438,11 @@ 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 h1:BzShpwCAP7TWzFppM4k2t03RhXhgYqaibROWkrWq7lE=
modernc.org/cc/v3 v3.35.22/go.mod h1:iPJg1pkwXqAV16SNgFBVYmggfMg6xhs+2oiO0vclK3g=
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.2/go.mod h1:6kii3AptTDI+nUrM9RFBoIEUEisSWCbdczD9ZwQH2FE=
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=
@@ -358,22 +474,18 @@ modernc.org/ccgo/v3 v3.12.73/go.mod h1:hngkB+nUUqzOf3iqsM48Gf1FZhY599qzVg1iX+BT3
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.88/go.mod h1:0MFzUHIuSIthpVZyMWiFYMwjiFnhrN5MkvBrUwON+ZM=
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.12.95/go.mod h1:ZcLyvtocXYi8uF+9Ebm3G8EF8HNY5hGomBqthDp4eC8=
modernc.org/ccgo/v3 v3.13.1/go.mod h1:aBYVOUfIlcSnrsRVU8VRS35y2DIfpgkmVkYZ0tpIXi4=
modernc.org/ccgo/v3 v3.14.0 h1:Zr1Ny9+7r5yAiXpBdgp8XiXqkNA4ARrRphHGHVXeAp0=
modernc.org/ccgo/v3 v3.14.0/go.mod h1:hBrkiBlUwvr5vV/ZH9YzXIp982jKE8Ek8tR1ytoAL6Q=
modernc.org/ccorpus v1.11.1 h1:K0qPfpVG1MJh5BYazccnmhywH4zHuOgJXgbjzyp6dWA=
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/ccorpus v1.11.1/go.mod h1:2gEUTrWqdpH2pXsmTM1ZkjeSrUWDpjMu2T6m29L/ErQ=
modernc.org/httpfs v1.0.6 h1:AAgIpFZRXuYnkjftxTAZwMIiwEqAfk8aVB2/oA6nAeM=
modernc.org/httpfs v1.0.6/go.mod h1:7dosgurJGp0sPaRanU53W4xZYKh14wfzX420oZADeHM=
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.3/go.mod h1:k3HDCP95A6U111Q5TmG3nAyUcp3kR5YFZTeDS9v8vSU=
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=
@@ -405,33 +517,26 @@ 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.90/go.mod h1:ynK5sbjsU77AP+nn61+k+wxUGRx9rOFcIqWYYMaDZ4c=
modernc.org/libc v1.11.98/go.mod h1:ynK5sbjsU77AP+nn61+k+wxUGRx9rOFcIqWYYMaDZ4c=
modernc.org/libc v1.11.99/go.mod h1:wLLYgEiY2D17NbBOEp+mIJJJBGSiy7fLL4ZrGGZ+8jI=
modernc.org/libc v1.11.101/go.mod h1:wLLYgEiY2D17NbBOEp+mIJJJBGSiy7fLL4ZrGGZ+8jI=
modernc.org/libc v1.11.104/go.mod h1:2MH3DaF/gCU8i/UBiVE1VFRos4o523M7zipmwH8SIgQ=
modernc.org/libc v1.12.0/go.mod h1:2MH3DaF/gCU8i/UBiVE1VFRos4o523M7zipmwH8SIgQ=
modernc.org/libc v1.13.1/go.mod h1:npFeGWjmZTjFeWALQLrvklVmAxv4m80jnG3+xI8FdJk=
modernc.org/libc v1.13.2 h1:GCFjY9bmwDZ/TJC4OZOUWaNgxIxwb104C/QZrqpcVEA=
modernc.org/libc v1.13.2/go.mod h1:npFeGWjmZTjFeWALQLrvklVmAxv4m80jnG3+xI8FdJk=
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.19 h1:S8flPn5ZeXx6iw/8yNa986hwTQDrY8RXU7tObZuAozo=
modernc.org/libc v1.16.19/go.mod h1:p7Mg4+koNjc8jkqwcoFBJx7tXkpj00G77X7A72jXPXA=
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 h1:XRch8trV7GgvTec2i7jc33YlUI0RKVDBvZ5eZ5m8y14=
modernc.org/memory v1.0.5/go.mod h1:B7OYswTRnfGg+4tDH1t1OeUNnsy2viGTdME4tzd+IjM=
modernc.org/opt v0.1.1 h1:/0RX92k9vwVeDXj+Xn23DKp2VJubL7k8qNffND6qn3A=
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/sqlite v1.13.0/go.mod h1:2qO/6jZJrcQaxFUHxOwa6Q6WfiGSsiVj6GXX0Ker+Jg=
modernc.org/sqlite v1.14.3 h1:psrTwgpEujgWEP3FNdsC9yNh5tSeA77U0GeWhHH4XmQ=
modernc.org/sqlite v1.14.3/go.mod h1:xMpicS1i2MJ4C8+Ap0vYBqTwYfpFvdnPE6brbFOtV2Y=
modernc.org/strutil v1.1.1 h1:xv+J1BXY3Opl2ALrBwyfEikFAj8pmqcpnfmuwUwcozs=
modernc.org/strutil v1.1.1/go.mod h1:DE+MQQ/hjKBZS2zNInV5hhcipt5rLPWkmpbGeW5mmdw=
modernc.org/tcl v1.5.9/go.mod h1:bcwjvBJ2u0exY6K35eAmxXBBij5kXb1dHlAWmfhqThE=
modernc.org/tcl v1.9.2/go.mod h1:aw7OnlIoiuJgu1gwbTZtrKnGpDqH9wyH++jZcxdqNsg=
modernc.org/token v1.0.0 h1:a0jaWiNMDhDUtqOj09wvjWWAqd3q7WpBulmL9H2egsk=
modernc.org/token v1.0.0/go.mod h1:UGzOrNV1mAFSEB63lOFHIpNRUVMvYTc6yu1SMY/XTDM=
modernc.org/z v1.1.2/go.mod h1:sj9T1AGBG0dm6SCVzldPOHWrif6XBpooJtbttMn1+Js=
modernc.org/z v1.2.20/go.mod h1:zU9FiF4PbHdOTUxw+IF8j7ArBMRPsHgq10uVPt6xTzo=
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=

45
kanban/banner.go Normal file
View File

@@ -0,0 +1,45 @@
package kanban
import (
"fmt"
"strings"
"github.com/fumiama/go-registry"
)
var (
info = [...]string{
"* OneBot + ZeroBot + Golang",
"* Version 1.5.0-beta5 - 2022-07-22 15:39:17 +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
}

6
kanban/init.go Normal file
View File

@@ -0,0 +1,6 @@
// Package kanban 打印版本信息
package kanban
func init() {
PrintBanner()
}

320
main.go
View File

@@ -1,108 +1,186 @@
package main
import (
"encoding/json"
"flag"
"fmt"
"math/rand"
"os"
"strings"
"strconv"
"time"
// 注:以下插件均可通过前面加 // 注释,注释后停用并不加载插件
// 下列插件可与 wdvxdr1123/ZeroBot v1.1.2 以上配合单独使用
"github.com/FloatTech/ZeroBot-Plugin/kanban" // 在最前打印 banner
// 插件控制
// webctrl "github.com/FloatTech/ZeroBot-Plugin/control/web" // web 后端控制
// ---------以下插件均可通过前面加 // 注释,注释后停用并不加载插件--------- //
// ----------------------插件优先级按顺序从高到低---------------------- //
// //
// //
// //
// //
// //
// ----------------------------高优先级区---------------------------- //
// vvvvvvvvvvvvvvvvvvvvvvvvvvvv高优先级区vvvvvvvvvvvvvvvvvvvvvvvvvvvv //
// vvvvvvvvvvvvvv高优先级区vvvvvvvvvvvvvv //
// vvvvvvv高优先级区vvvvvvv //
// vvvvvvvvvvvvvv //
// vvvv //
// 词库
_ "github.com/FloatTech/ZeroBot-Plugin/plugin_ai_reply" // 人工智能回复
_ "github.com/FloatTech/ZeroBot-Plugin/plugin_atri" // ATRI词库
_ "github.com/FloatTech/ZeroBot-Plugin/plugin_chat" // 基础词库
_ "github.com/FloatTech/ZeroBot-Plugin/plugin/chat" // 基础词库
// 实用类
_ "github.com/FloatTech/ZeroBot-Plugin/plugin_b14" // base16384加解密
_ "github.com/FloatTech/ZeroBot-Plugin/plugin_github" // 搜索GitHub仓库
_ "github.com/FloatTech/ZeroBot-Plugin/plugin_manager" // 群管
_ "github.com/FloatTech/ZeroBot-Plugin/plugin_nbnhhsh" // 拼音首字母缩写释义工具
_ "github.com/FloatTech/ZeroBot-Plugin/plugin_runcode" // 在线运行代码
_ "github.com/FloatTech/ZeroBot-Plugin/plugin_sleep_manage" // 统计睡眠时间
_ "github.com/FloatTech/ZeroBot-Plugin/plugin_translation" // 翻译
_ "github.com/FloatTech/ZeroBot-Plugin/plugin/sleep_manage" // 统计睡眠时间
// 娱乐类
// _ "github.com/FloatTech/ZeroBot-Plugin/plugin_wtf" // 鬼东西
_ "github.com/FloatTech/ZeroBot-Plugin-Gif" // 制图
_ "github.com/FloatTech/ZeroBot-Plugin/plugin_ai_false" // 服务器监控
_ "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_fortune" // 运势
_ "github.com/FloatTech/ZeroBot-Plugin/plugin_funny" // 笑话
_ "github.com/FloatTech/ZeroBot-Plugin/plugin_hs" // 炉石
_ "github.com/FloatTech/ZeroBot-Plugin/plugin_juejuezi" // 绝绝子生成器
_ "github.com/FloatTech/ZeroBot-Plugin/plugin_minecraft" // MCSManager
_ "github.com/FloatTech/ZeroBot-Plugin/plugin_mocking_bird" // 拟声鸟
_ "github.com/FloatTech/ZeroBot-Plugin/plugin_moyu" // 摸鱼
_ "github.com/FloatTech/ZeroBot-Plugin/plugin_music" // 点歌
_ "github.com/FloatTech/ZeroBot-Plugin/plugin_novel" // 铅笔小说网搜索
_ "github.com/FloatTech/ZeroBot-Plugin/plugin_omikuji" // 浅草寺求签
_ "github.com/FloatTech/ZeroBot-Plugin/plugin_reborn" // 投胎
_ "github.com/FloatTech/ZeroBot-Plugin/plugin_shadiao" // 沙雕app
_ "github.com/FloatTech/ZeroBot-Plugin/plugin_shindan" // 测定
_ "github.com/FloatTech/ZeroBot-Plugin/plugin/atri" // ATRI词库
// b站相关
_ "github.com/FloatTech/ZeroBot-Plugin/plugin_bilibili" // 查询b站用户信息
_ "github.com/FloatTech/ZeroBot-Plugin/plugin_diana" // 嘉心糖发病
_ "github.com/FloatTech/ZeroBot-Plugin/plugin/manager" // 群管
// 二次元图片
_ "github.com/FloatTech/ZeroBot-Plugin/plugin_acgimage" // 随机图片与AI点评
_ "github.com/FloatTech/ZeroBot-Plugin/plugin_aiwife" // 随机老婆
_ "github.com/FloatTech/ZeroBot-Plugin/plugin_image_finder" // 关键字搜图
_ "github.com/FloatTech/ZeroBot-Plugin/plugin_lolicon" // lolicon 随机图片
_ "github.com/FloatTech/ZeroBot-Plugin/plugin_nativesetu" // 本地涩图
_ "github.com/FloatTech/ZeroBot-Plugin/plugin_nativewife" // 本地老婆
_ "github.com/FloatTech/ZeroBot-Plugin/plugin_saucenao" // 以图搜图
_ "github.com/FloatTech/ZeroBot-Plugin/plugin_setutime" // 来份涩图
_ "github.com/FloatTech/ZeroBot-Plugin/plugin_tracemoe" // 搜番
_ "github.com/FloatTech/ZeroBot-Plugin/plugin_vtb_quotation" // vtb语录
_ "github.com/FloatTech/ZeroBot-Plugin/plugin/thesaurus" // 词典匹配回复
// 以下为内置依赖,勿动
"github.com/fumiama/go-registry"
_ "github.com/FloatTech/zbputils/job" // 定时指令触发器
// ^^^^ //
// ^^^^^^^^^^^^^^ //
// ^^^^^^^高优先级区^^^^^^^ //
// ^^^^^^^^^^^^^^高优先级区^^^^^^^^^^^^^^ //
// ^^^^^^^^^^^^^^^^^^^^^^^^^^^^高优先级区^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //
// ----------------------------高优先级区---------------------------- //
// //
// //
// //
// //
// //
// ----------------------------中优先级区---------------------------- //
// vvvvvvvvvvvvvvvvvvvvvvvvvvvv中优先级区vvvvvvvvvvvvvvvvvvvvvvvvvvvv //
// vvvvvvvvvvvvvv中优先级区vvvvvvvvvvvvvv //
// vvvvvvv中优先级区vvvvvvv //
// 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/book_review" // 哀伤雪刃吧推书记录
_ "github.com/FloatTech/ZeroBot-Plugin/plugin/cangtoushi" // 藏头诗
_ "github.com/FloatTech/ZeroBot-Plugin/plugin/char_reverser" // 英文字符翻转
_ "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/guessmusic" // 猜歌
_ "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/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/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/wtf" // 鬼东西
// ^^^^ //
// ^^^^^^^^^^^^^^ //
// ^^^^^^^中优先级区^^^^^^^ //
// ^^^^^^^^^^^^^^中优先级区^^^^^^^^^^^^^^ //
// ^^^^^^^^^^^^^^^^^^^^^^^^^^^^中优先级区^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //
// ----------------------------中优先级区---------------------------- //
// //
// //
// //
// //
// //
// ----------------------------低优先级区---------------------------- //
// vvvvvvvvvvvvvvvvvvvvvvvvvvvv低优先级区vvvvvvvvvvvvvvvvvvvvvvvvvvvv //
// vvvvvvvvvvvvvv低优先级区vvvvvvvvvvvvvv //
// vvvvvvv低优先级区vvvvvvv //
// vvvvvvvvvvvvvv //
// vvvv //
_ "github.com/FloatTech/ZeroBot-Plugin/plugin/curse" // 骂人
_ "github.com/FloatTech/ZeroBot-Plugin/plugin/ai_reply" // 人工智能回复
// ^^^^ //
// ^^^^^^^^^^^^^^ //
// ^^^^^^^低优先级区^^^^^^^ //
// ^^^^^^^^^^^^^^低优先级区^^^^^^^^^^^^^^ //
// ^^^^^^^^^^^^^^^^^^^^^^^^^^^^低优先级区^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //
// ----------------------------低优先级区---------------------------- //
// //
// //
// //
// //
// //
// -----------------------以下为内置依赖,勿动------------------------ //
"github.com/FloatTech/zbputils/process"
"github.com/sirupsen/logrus"
zero "github.com/wdvxdr1123/ZeroBot"
"github.com/wdvxdr1123/ZeroBot/driver"
"github.com/wdvxdr1123/ZeroBot/message"
)
var (
contents = []string{
"* OneBot + ZeroBot + Golang",
"* Version 1.2.2 - 2021-12-13 21:22:45 +0800 CST",
"* Copyright © 2020 - 2021 FloatTech. All Rights Reserved.",
"* Project: https://github.com/FloatTech/ZeroBot-Plugin",
}
banner = strings.Join(contents, "\n")
token *string
url *string
reg = registry.NewRegReader("reilia.fumiama.top:32664", "fumiama")
// -----------------------以上为内置依赖,勿动------------------------ //
)
func init() {
sus := make([]int64, 0, 16)
// 解析命令行参数
d := flag.Bool("d", false, "Enable debug level log and higher.")
w := flag.Bool("w", false, "Enable warning level log and higher.")
h := flag.Bool("h", false, "Display this help.")
// 解析命令行参数,输入 `-g 监听地址:端口` 指定 gui 访问地址,默认 127.0.0.1:3000
// g := flag.String("g", "127.0.0.1:3000", "Set web gui listening address.")
// 直接写死 AccessToken 时,请更改下面第二个参数
token = flag.String("t", "", "Set AccessToken of WSClient.")
token := flag.String("t", "", "Set AccessToken of WSClient.")
// 直接写死 URL 时,请更改下面第二个参数
url = flag.String("u", "ws://127.0.0.1:6700", "Set Url of WSClient.")
url := flag.String("u", "ws://127.0.0.1:6700", "Set Url of WSClient.")
// 默认昵称
adana := flag.String("n", "椛椛", "Set default nickname.")
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.")
flag.Parse()
if *h {
printBanner()
kanban.PrintBanner()
fmt.Println("Usage:")
flag.PrintDefaults()
os.Exit(0)
@@ -114,53 +192,71 @@ func init() {
logrus.SetLevel(logrus.WarnLevel)
}
}
// 启用 gui
// webctrl.InitGui(*g)
}
func printBanner() {
fmt.Print(
"\n======================[ZeroBot-Plugin]======================",
"\n", banner, "\n",
"----------------------[ZeroBot-公告栏]----------------------",
"\n", getKanban(), "\n",
"============================================================\n",
)
}
for _, s := range flag.Args() {
i, err := strconv.ParseInt(s, 10, 64)
if err != nil {
continue
}
sus = append(sus, i)
}
func getKanban() string {
err := reg.Connect()
if err != nil {
return err.Error()
// 通过代码写死的方式添加主人账号
// sus = append(sus, 12345678)
// sus = append(sus, 87654321)
if *runcfg != "" {
f, err := os.Open(*runcfg)
if err != nil {
panic(err)
}
config.W = make([]*driver.WSClient, 0, 2)
err = json.NewDecoder(f).Decode(&config)
f.Close()
if err != nil {
panic(err)
}
config.Z.Driver = make([]zero.Driver, len(config.W))
for i, w := range config.W {
config.Z.Driver[i] = w
}
logrus.Infoln("[main] 从", *runcfg, "读取配置文件")
return
}
defer reg.Close()
text, err := reg.Get("ZeroBot-Plugin/kanban")
if err != nil {
return err.Error()
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]},
}
if *save != "" {
f, err := os.Create(*save)
if err != nil {
panic(err)
}
err = json.NewEncoder(f).Encode(&config)
f.Close()
if err != nil {
panic(err)
}
logrus.Infoln("[main] 配置文件已保存到", *save)
os.Exit(0)
}
return text
}
func main() {
// printBanner()
rand.Seed(time.Now().UnixNano()) // 全局 seed其他插件无需再 seed
// 帮助
zero.OnFullMatchGroup([]string{"/help", ".help", "菜单"}, zero.OnlyToMe).SetBlock(true).FirstPriority().
zero.OnFullMatchGroup([]string{"/help", ".help", "菜单"}, zero.OnlyToMe).SetBlock(true).
Handle(func(ctx *zero.Ctx) {
ctx.SendChain(message.Text(banner, "\n可发送\"/服务列表\"查看 bot 功能"))
ctx.SendChain(message.Text(kanban.Banner, "\n可发送\"/服务列表\"查看 bot 功能"))
})
zero.OnFullMatch("查看zbp公告", zero.OnlyToMe, zero.AdminPermission).SetBlock(true).FirstPriority().
zero.OnFullMatch("查看zbp公告", zero.OnlyToMe, zero.AdminPermission).SetBlock(true).
Handle(func(ctx *zero.Ctx) {
ctx.SendChain(message.Text(getKanban()))
ctx.SendChain(message.Text(kanban.Kanban()))
})
zero.RunAndBlock(
zero.Config{
NickName: []string{"椛椛", "ATRI", "atri", "亚托莉", "アトリ"},
CommandPrefix: "/",
// SuperUsers 某些功能需要主人权限,可通过以下两种方式修改
// "12345678", "87654321":通过代码写死的方式添加主人账号
// flag.Args():通过命令行参数的方式添加主人账号,无需修改下方任何代码
SuperUsers: append([]string{"12345678", "87654321"}, flag.Args()...),
Driver: []zero.Driver{driver.NewWebSocketClient(*url, *token)},
},
)
zero.RunAndBlock(config.Z, process.GlobalInitMutex.Unlock)
}

121
plugin/ai_false/ai_false.go Normal file
View File

@@ -0,0 +1,121 @@
// Package aifalse 暂时只有服务器监控
package aifalse
import (
"fmt"
"math"
"strconv"
"time"
ctrl "github.com/FloatTech/zbpctrl"
"github.com/FloatTech/zbputils/control"
"github.com/FloatTech/zbputils/ctxext"
"github.com/shirou/gopsutil/v3/cpu"
"github.com/shirou/gopsutil/v3/disk"
"github.com/shirou/gopsutil/v3/mem"
"github.com/sirupsen/logrus"
zero "github.com/wdvxdr1123/ZeroBot"
"github.com/wdvxdr1123/ZeroBot/message"
)
func init() { // 插件主体
engine := control.Register("aifalse", &ctrl.Options[*zero.Ctx]{
DisableOnDefault: false,
Help: "AIfalse\n" +
"- 查询计算机当前活跃度: [检查身体 | 自检 | 启动自检 | 系统状态]\n" +
"- 设置默认限速为每 m [分钟 | 秒] n 次触发",
})
c, ok := control.Lookup("aifalse")
if !ok {
panic("register aifalse error")
}
m := c.GetData(0)
n := (m >> 16) & 0xffff
m &= 0xffff
if m != 0 || n != 0 {
ctxext.SetDefaultLimiterManagerParam(time.Duration(m)*time.Second, int(n))
logrus.Infoln("设置默认限速为每", m, "秒触发", n, "次")
}
engine.OnFullMatchGroup([]string{"检查身体", "自检", "启动自检", "系统状态"}, zero.AdminPermission).SetBlock(true).
Handle(func(ctx *zero.Ctx) {
ctx.SendChain(message.Text(
"* CPU占用: ", cpuPercent(), "%\n",
"* RAM占用: ", memPercent(), "%\n",
"* 硬盘使用: ", diskPercent(),
),
)
})
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"))
return
}
m, err := strconv.ParseInt(ctx.State["regex_matched"].([]string)[1], 10, 64)
if err != nil {
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"))
return
}
n, err := strconv.ParseInt(ctx.State["regex_matched"].([]string)[3], 10, 64)
if err != nil {
ctx.SendChain(message.Text("ERROR:", err))
return
}
if n >= 65536 || n <= 0 {
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))
return
}
ctx.SendChain(message.Text("设置默认限速为每", m, "秒触发", n, "次"))
})
}
func cpuPercent() float64 {
percent, err := cpu.Percent(time.Second, false)
if err != nil {
return -1
}
return math.Round(percent[0])
}
func memPercent() float64 {
memInfo, err := mem.VirtualMemory()
if err != nil {
return -1
}
return math.Round(memInfo.UsedPercent)
}
func diskPercent() string {
parts, err := disk.Partitions(true)
if err != nil {
return err.Error()
}
msg := ""
for _, p := range parts {
diskInfo, err := disk.Usage(p.Mountpoint)
if err != nil {
msg += "\n - " + err.Error()
continue
}
pc := uint(math.Round(diskInfo.UsedPercent))
if pc > 0 {
msg += fmt.Sprintf("\n - %s(%dM) %d%%", p.Mountpoint, diskInfo.Total/1024/1024, pc)
}
}
return msg
}

181
plugin/ai_reply/ai_tts.go Normal file
View File

@@ -0,0 +1,181 @@
package aireply
import (
"regexp"
"strconv"
"sync"
"github.com/pkumza/numcn"
log "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/mockingbird"
ctrl "github.com/FloatTech/zbpctrl"
"github.com/FloatTech/zbputils/control"
"github.com/FloatTech/zbputils/ctxext"
)
const ttsServiceName = "tts"
var re = regexp.MustCompile(`(\-|\+)?\d+(\.\d+)?`)
type ttsInstances struct {
sync.RWMutex
m map[string]tts.TTS
l []string
}
func (t *ttsInstances) List() []string {
t.RLock()
cl := make([]string, len(t.l))
_ = copy(cl, t.l)
t.RUnlock()
return cl
}
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))
})
}
// 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
}
func (t *ttsInstances) setSoundMode(ctx *zero.Ctx, name string) error {
gid := ctx.Event.GroupID
if gid == 0 {
gid = -ctx.Event.UserID
}
var index int64
t.RLock()
for i, s := range t.l {
if s == name {
index = int64(i)
break
}
}
t.RUnlock()
m := ctx.State["manager"].(*ctrl.Control[*zero.Ctx])
return m.SetData(gid, index)
}
func (t *ttsInstances) getSoundMode(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 {
t.RLock()
defer t.RUnlock()
index := m.GetData(gid)
if int(index) < len(t.l) {
return t.l[index]
}
}
return "拟声鸟阿梓"
}
func (t *ttsInstances) setDefaultSoundMode(name string) {
var index int
t.RLock()
for _, s := range t.l {
if s == name {
break
}
index++
}
t.RUnlock()
t.Lock()
t.l[0], t.l[index] = t.l[index], t.l[0]
t.Unlock()
}

91
plugin/ai_reply/main.go Normal file
View File

@@ -0,0 +1,91 @@
// Package aireply AI 回复
package aireply
import (
"errors"
"time"
"github.com/FloatTech/AnimeAPI/aireply"
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"
)
const (
replyServiceName = "aireply"
)
var replyModes = [...]string{"青云客", "小爱"}
func init() { // 插件主体
engine := control.Register(replyServiceName, &ctrl.Options[*zero.Ctx]{
DisableOnDefault: false,
Help: "人工智能回复\n" +
"- @Bot 任意文本(任意一句话回复)\n- 设置回复模式[青云客 | 小爱]\n- ",
})
// 回复 @和包括名字
engine.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]))
// 回复
time.Sleep(time.Second * 1)
if zero.OnlyPublic(ctx) {
reply = append(reply, message.Reply(ctx.Event.MessageID))
ctx.Send(reply)
return
}
ctx.Send(reply)
})
engine.OnPrefix(`设置回复模式`).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("成功"))
})
}
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
}
}
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)
}
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]
}
}
return "青云客"
}

View File

@@ -4,12 +4,12 @@ package aiwife
import (
"fmt"
"math/rand"
"time"
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"
"github.com/FloatTech/ZeroBot-Plugin/control"
)
const (
@@ -17,14 +17,11 @@ const (
)
func init() { // 插件主体
// TODO: 1.17 特性暂不增加
// rand.Seed(time.Now().UnixMicro())
rand.Seed(time.Now().UnixNano())
control.Register("aiwife", &control.Options{
control.Register("aiwife", &ctrl.Options[*zero.Ctx]{
DisableOnDefault: false,
Help: "AIWife\n" +
"- waifu|随机waifu",
}).OnFullMatchGroup([]string{"waifu", "随机waifu"}).SetPriority(10).SetBlock(true).
"- waifu | 随机waifu",
}).ApplySingle(ctxext.DefaultSingle).OnFullMatchGroup([]string{"waifu", "随机waifu"}).SetBlock(true).
Handle(func(ctx *zero.Ctx) {
miku := rand.Intn(100000) + 1
ctx.SendChain(message.At(ctx.Event.UserID), message.Image(fmt.Sprintf(bed, miku)))

View File

@@ -12,48 +12,37 @@ import (
zero "github.com/wdvxdr1123/ZeroBot"
"github.com/wdvxdr1123/ZeroBot/message"
"github.com/FloatTech/ZeroBot-Plugin/control"
"github.com/FloatTech/ZeroBot-Plugin/utils/process"
ctrl "github.com/FloatTech/zbpctrl"
"github.com/FloatTech/zbputils/control"
"github.com/FloatTech/zbputils/process"
)
const (
// 服务名
servicename = "atri"
// ATRI 所有命令的优先级
prio = 15
// ATRI 表情的 codechina 镜像
res = "https://gitcode.net/u011570312/zbpdata/-/raw/main/Atri/"
)
func init() { // 插件主体
engine := control.Register(servicename, &control.Options{
engine := control.Register(servicename, &ctrl.Options[*zero.Ctx]{
DisableOnDefault: false,
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" +
"- 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- 答应我",
OnEnable: func(ctx *zero.Ctx) {
process.SleepAbout1sTo2s()
ctx.SendChain(message.Text("嗯呜呜……夏生先生……?"))
},
OnDisable: func(ctx *zero.Ctx) {
process.SleepAbout1sTo2s()
ctx.SendChain(message.Text("Zzz……Zzz……"))
},
})
zero.OnFullMatch("ATRI醒醒", zero.AdminPermission).SetBlock(true).SetPriority(prio).
Handle(func(ctx *zero.Ctx) {
c, ok := control.Lookup(servicename)
if ok && !c.IsEnabledIn(ctx.Event.GroupID) {
c.Enable(ctx.Event.GroupID)
process.SleepAbout1sTo2s()
ctx.SendChain(message.Text("嗯呜呜……夏生先生……?"))
}
})
engine.OnFullMatch("ATRI睡吧", zero.AdminPermission).SetBlock(true).SetPriority(prio).
Handle(func(ctx *zero.Ctx) {
c, ok := control.Lookup(servicename)
if ok && c.IsEnabledIn(ctx.Event.GroupID) {
c.Disable(ctx.Event.GroupID)
process.SleepAbout1sTo2s()
ctx.SendChain(message.Text("Zzz……Zzz……"))
}
})
engine.OnFullMatch("萝卜子", atriSleep).SetBlock(true).SetPriority(prio).
engine.OnFullMatch("萝卜子", isAtriSleeping).SetBlock(true).
Handle(func(ctx *zero.Ctx) {
process.SleepAbout1sTo2s()
switch rand.Intn(2) {
@@ -63,17 +52,17 @@ func init() { // 插件主体
ctx.SendChain(randRecord("RocketPunch.amr"))
}
})
engine.OnFullMatchGroup([]string{"喜欢", "爱你", "爱", "suki", "daisuki", "すき", "好き", "贴贴", "老婆", "亲一个", "mua"}, atriSleep, zero.OnlyToMe).SetBlock(true).SetPriority(prio).
engine.OnFullMatchGroup([]string{"喜欢", "爱你", "爱", "suki", "daisuki", "すき", "好き", "贴贴", "老婆", "亲一个", "mua"}, isAtriSleeping, zero.OnlyToMe).SetBlock(true).
Handle(func(ctx *zero.Ctx) {
process.SleepAbout1sTo2s()
ctx.SendChain(randImage("SUKI.jpg", "SUKI1.jpg", "SUKI2.png"))
})
engine.OnKeywordGroup([]string{"草你妈", "操你妈", "脑瘫", "废柴", "fw", "five", "废物", "战斗", "爬", "爪巴", "sb", "SB", "傻B"}, atriSleep, zero.OnlyToMe).SetBlock(true).SetPriority(prio - 1).
engine.OnKeywordGroup([]string{"草你妈", "操你妈", "脑瘫", "废柴", "fw", "five", "废物", "战斗", "爬", "爪巴", "sb", "SB", "傻B"}, isAtriSleeping, zero.OnlyToMe).SetBlock(true).
Handle(func(ctx *zero.Ctx) {
process.SleepAbout1sTo2s()
ctx.SendChain(randImage("FN.jpg", "WQ.jpg", "WQ1.jpg"))
})
engine.OnFullMatchGroup([]string{"早安", "早哇", "早上好", "ohayo", "哦哈哟", "お早う", "早好", "早", "早早早"}).SetBlock(true).SetPriority(prio).
engine.OnFullMatchGroup([]string{"早安", "早哇", "早上好", "ohayo", "哦哈哟", "お早う", "早好", "早", "早早早"}).SetBlock(true).
Handle(func(ctx *zero.Ctx) {
now := time.Now().Hour()
process.SleepAbout1sTo2s()
@@ -113,7 +102,7 @@ func init() { // 插件主体
))
}
})
engine.OnFullMatchGroup([]string{"中午好", "午安", "午好"}).SetBlock(true).SetPriority(prio).
engine.OnFullMatchGroup([]string{"中午好", "午安", "午好"}).SetBlock(true).
Handle(func(ctx *zero.Ctx) {
now := time.Now().Hour()
if now > 11 && now < 15 { // 中午
@@ -126,7 +115,7 @@ func init() { // 插件主体
))
}
})
engine.OnFullMatchGroup([]string{"晚安", "oyasuminasai", "おやすみなさい", "晚好", "晚上好"}).SetBlock(true).SetPriority(prio).
engine.OnFullMatchGroup([]string{"晚安", "oyasuminasai", "おやすみなさい", "晚好", "晚上好"}).SetBlock(true).
Handle(func(ctx *zero.Ctx) {
now := time.Now().Hour()
process.SleepAbout1sTo2s()
@@ -169,7 +158,7 @@ func init() { // 插件主体
))
}
})
engine.OnKeywordGroup([]string{"高性能", "太棒了", "すごい", "sugoi", "斯国一", "よかった"}, atriSleep, zero.OnlyToMe).SetBlock(true).SetPriority(prio).
engine.OnKeywordGroup([]string{"高性能", "太棒了", "すごい", "sugoi", "斯国一", "よかった"}, isAtriSleeping, zero.OnlyToMe).SetBlock(true).
Handle(func(ctx *zero.Ctx) {
process.SleepAbout1sTo2s()
ctx.SendChain(randText(
@@ -190,7 +179,7 @@ func init() { // 插件主体
"呣......我的高性能,毫无遗憾地施展出来了......",
))
})
engine.OnKeywordGroup([]string{"没事", "没关系", "大丈夫", "还好", "不要紧", "没出大问题", "没伤到哪"}, atriSleep, zero.OnlyToMe).SetBlock(true).SetPriority(prio).
engine.OnKeywordGroup([]string{"没事", "没关系", "大丈夫", "还好", "不要紧", "没出大问题", "没伤到哪"}, isAtriSleeping, zero.OnlyToMe).SetBlock(true).
Handle(func(ctx *zero.Ctx) {
process.SleepAbout1sTo2s()
ctx.SendChain(randText(
@@ -205,26 +194,26 @@ func init() { // 插件主体
))
})
engine.OnKeywordGroup([]string{"好吗", "是吗", "行不行", "能不能", "可不可以"}, atriSleep).SetBlock(true).SetPriority(prio).
engine.OnKeywordGroup([]string{"好吗", "是吗", "行不行", "能不能", "可不可以"}, isAtriSleeping).SetBlock(true).
Handle(func(ctx *zero.Ctx) {
process.SleepAbout1sTo2s()
if rand.Intn(2) == 0 {
ctx.SendChain(randImage("YES.png", "NO.jpg"))
}
})
engine.OnKeywordGroup([]string{"啊这"}, atriSleep).SetBlock(true).SetPriority(prio).
engine.OnKeywordGroup([]string{"啊这"}, isAtriSleeping).SetBlock(true).
Handle(func(ctx *zero.Ctx) {
process.SleepAbout1sTo2s()
if rand.Intn(2) == 0 {
ctx.SendChain(randImage("AZ.jpg", "AZ1.jpg"))
}
})
engine.OnKeywordGroup([]string{"我好了"}, atriSleep).SetBlock(true).SetPriority(prio).
engine.OnKeywordGroup([]string{"我好了"}, isAtriSleeping).SetBlock(true).
Handle(func(ctx *zero.Ctx) {
process.SleepAbout1sTo2s()
ctx.SendChain(message.Reply(ctx.Event.MessageID), randText("不许好!", "憋回去!"))
})
engine.OnFullMatchGroup([]string{"", "?", "¿"}, atriSleep).SetBlock(true).SetPriority(prio).
engine.OnFullMatchGroup([]string{"", "?", "¿"}, isAtriSleeping).SetBlock(true).
Handle(func(ctx *zero.Ctx) {
process.SleepAbout1sTo2s()
switch rand.Intn(5) {
@@ -234,7 +223,7 @@ func init() { // 插件主体
ctx.SendChain(randImage("WH.jpg", "WH1.jpg", "WH2.jpg", "WH3.jpg"))
}
})
engine.OnKeyword("离谱", atriSleep).SetBlock(true).SetPriority(prio).
engine.OnKeyword("离谱", isAtriSleeping).SetBlock(true).
Handle(func(ctx *zero.Ctx) {
switch rand.Intn(5) {
case 0:
@@ -243,7 +232,7 @@ func init() { // 插件主体
ctx.SendChain(randImage("WH.jpg"))
}
})
engine.OnKeyword("答应我", atriSleep, zero.OnlyToMe).SetBlock(true).SetPriority(prio).
engine.OnKeyword("答应我", isAtriSleeping, zero.OnlyToMe).SetBlock(true).
Handle(func(ctx *zero.Ctx) {
process.SleepAbout1sTo2s()
ctx.SendChain(randText("我无法回应你的请求"))
@@ -262,8 +251,8 @@ func randRecord(file ...string) message.MessageSegment {
return message.Record(res + file[rand.Intn(len(file))])
}
// atriSleep 凌晨0点到6点ATRI 在睡觉,不回应任何请求
func atriSleep(ctx *zero.Ctx) bool {
// isAtriSleeping 凌晨0点到6点ATRI 在睡觉,不回应任何请求
func isAtriSleeping(ctx *zero.Ctx) bool {
if now := time.Now().Hour(); now >= 1 && now < 6 {
return false
}

View File

@@ -4,57 +4,57 @@ package b14coder
import (
"unsafe"
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"
"github.com/FloatTech/ZeroBot-Plugin/control"
)
func init() {
en := control.Register("base16384", &control.Options{
en := control.Register("base16384", &ctrl.Options[*zero.Ctx]{
DisableOnDefault: false,
Help: "base16384加解密\n" +
"- 加密xxx\n- 解密xxx\n- 用yyy加密xxx\n- 用yyy解密xxx",
})
en.OnRegex(`^加密(.*)`).SetBlock(true).ThirdPriority().
en.OnRegex(`^加密\s*(.+)$`).SetBlock(true).
Handle(func(ctx *zero.Ctx) {
str := ctx.State["regex_matched"].([]string)[1]
es, err := base14.UTF16be2utf8(base14.EncodeString(str))
if err == nil {
ctx.SendChain(message.Text(helper.BytesToString(es)))
es := base14.EncodeString(str)
if es != "" {
ctx.SendChain(message.Text(es))
} else {
ctx.SendChain(message.Text("加密失败!"))
}
})
en.OnRegex("^解密([\u4e00-\u8e00]*[\u3d01-\u3d06]?)$").SetBlock(true).ThirdPriority().
en.OnRegex(`^解密\s*([一-踀]+[㴁-㴆]?)$`).SetBlock(true).
Handle(func(ctx *zero.Ctx) {
str := ctx.State["regex_matched"].([]string)[1]
es, err := base14.UTF82utf16be(helper.StringToBytes(str))
if err == nil {
ctx.SendChain(message.Text(base14.DecodeString(es)))
es := base14.DecodeString(str)
if es != "" {
ctx.SendChain(message.Text(es))
} else {
ctx.SendChain(message.Text("解密失败!"))
}
})
en.OnRegex(`^用(.*)加密(.*)`).SetBlock(true).ThirdPriority().
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)
es, err := base14.UTF16be2utf8(base14.Encode(t.Encrypt(helper.StringToBytes(str))))
es, err := base14.UTF16BE2UTF8(base14.Encode(t.Encrypt(helper.StringToBytes(str))))
if err == nil {
ctx.SendChain(message.Text(helper.BytesToString(es)))
} else {
ctx.SendChain(message.Text("加密失败!"))
}
})
en.OnRegex("^用(.*)解密([\u4e00-\u8e00]*[\u3d01-\u3d06]?)$").SetBlock(true).ThirdPriority().
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)
es, err := base14.UTF82utf16be(helper.StringToBytes(str))
es, err := base14.UTF82UTF16BE(helper.StringToBytes(str))
if err == nil {
ctx.SendChain(message.Text(helper.BytesToString(t.Decrypt(base14.Decode(es)))))
} else {

27
plugin/baidu/search.go Normal file
View File

@@ -0,0 +1,27 @@
// Package baidu 百度一下
package baidu
import (
"net/url"
zero "github.com/wdvxdr1123/ZeroBot"
"github.com/wdvxdr1123/ZeroBot/message"
ctrl "github.com/FloatTech/zbpctrl"
"github.com/FloatTech/zbputils/control"
"github.com/FloatTech/zbputils/ctxext"
)
func init() {
control.Register("baidu", &ctrl.Options[*zero.Ctx]{
DisableOnDefault: false,
Help: "baidu\n" +
"- 百度下[xxx]",
}).OnPrefix("百度下").SetBlock(true).Limit(ctxext.LimitByGroup).
Handle(func(ctx *zero.Ctx) {
txt := ctx.State["args"].(string)
if txt != "" {
ctx.SendChain(message.Text("https://buhuibaidu.me/?s=" + url.QueryEscape(txt)))
}
})
}

128
plugin/bilibili/api.go Normal file
View File

@@ -0,0 +1,128 @@
package bilibili
import (
"encoding/json"
"errors"
"fmt"
"net/http"
"strconv"
"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\"")
)
// searchUser 查找b站用户
func searchUser(keyword string) (r []searchResult, err error) {
data, err := web.GetData(fmt.Sprintf(searchUserURL, keyword))
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
}
// getVtbDetail 查找vtb信息
func getVtbDetail(uid string) (result vtbDetail, err error) {
data, err := web.GetData(fmt.Sprintf(vtbDetailURL, uid))
if err != nil {
return
}
if err = json.Unmarshal(data, &result); err != nil {
return
}
return
}
// getMemberCard 获取b站个人详情
func getMemberCard(uid interface{}) (result memberCard, err error) {
data, err := web.GetData(fmt.Sprintf(memberCardURL, uid))
if err != nil {
return
}
err = json.Unmarshal(binary.StringToBytes(gjson.ParseBytes(data).Get("card").Raw), &result)
if err != nil {
return
}
return
}
// getMedalwall 用b站uid获得牌子
func getMedalwall(uid string) (result []medal, err error) {
client := &http.Client{}
req, err := http.NewRequest("GET", fmt.Sprintf(medalwallURL, uid), 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()
var md medalData
err = json.NewDecoder(res.Body).Decode(&md)
if err != nil {
return
}
if md.Code == -101 {
err = errNeedCookie
return
}
if md.Code != 0 {
err = errors.New(md.Message)
}
result = md.Data.List
return
}
// getArticleInfo 用id查专栏信息
func getArticleInfo(id string) (card Card, err error) {
var data []byte
data, err = web.GetData(fmt.Sprintf(articleInfoURL, id))
if err != nil {
return
}
err = json.Unmarshal(binary.StringToBytes(gjson.ParseBytes(data).Get("data").Raw), &card)
return
}
// getLiveRoomInfo 用直播间id查直播间信息
func getLiveRoomInfo(roomID string) (card roomCard, err error) {
var data []byte
data, err = web.GetData(fmt.Sprintf(liveRoomInfoURL, roomID))
if err != nil {
return
}
err = json.Unmarshal(binary.StringToBytes(gjson.ParseBytes(data).Get("data").Raw), &card)
return
}
// getVideoInfo 用av或bv查视频信息
func getVideoInfo(id string) (card Card, err error) {
var data []byte
_, err = strconv.Atoi(id)
if err == nil {
data, err = web.GetData(fmt.Sprintf(videoInfoURL, id, ""))
} else {
data, err = web.GetData(fmt.Sprintf(videoInfoURL, "", id))
}
if err != nil {
return
}
err = json.Unmarshal(binary.StringToBytes(gjson.ParseBytes(data).Get("data").Raw), &card)
return
}

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

@@ -0,0 +1,347 @@
// Package bilibili 查询b站用户信息
package bilibili
import (
"encoding/binary"
"fmt"
"image"
"image/color"
"os"
"path"
"regexp"
"sort"
"strconv"
"time"
"github.com/Coloured-glaze/gg"
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"
log "github.com/sirupsen/logrus"
zero "github.com/wdvxdr1123/ZeroBot"
"github.com/wdvxdr1123/ZeroBot/message"
)
var re = regexp.MustCompile(`^\d+$`)
// 查成分的
func init() {
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",
})
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 = 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 := 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 := 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})$`, 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 := 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 := getMedalwall(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 := searchUser(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 := searchUser(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,106 @@
// Package bilibili bilibili卡片解析
package bilibili
import (
"regexp"
"time"
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,
Help: "b站动态、专栏、视频、直播解析\n" +
"- t.bilibili.com/642277677329285174 | bilibili.com/read/cv17134450 | bilibili.com/video/BV13B4y1x7pS | 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 := 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 := 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 := 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 := getLiveRoomInfo(ctx.State["regex_matched"].([]string)[1])
if err != nil {
ctx.SendChain(message.Text("ERROR:", err))
return
}
ctx.SendChain(liveCard2msg(card)...)
}

View File

@@ -0,0 +1,130 @@
package bilibili
import (
"os"
"github.com/FloatTech/zbputils/binary"
"github.com/FloatTech/zbputils/web"
_ "github.com/fumiama/sqlite3" // use sql
"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
)
// vupdb 分数数据库
type vupdb gorm.DB
type vup struct {
Mid int64 `gorm:"column:mid;primary_key"`
Uname string `gorm:"column:uname"`
Roomid int64 `gorm:"column:roomid"`
}
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"
}
// initializeVup 初始化vup数据库
func initializeVup(dbpath string) (*vupdb, error) {
if _, err := os.Stat(dbpath); err != nil || os.IsNotExist(err) {
// 生成文件
f, err := os.Create(dbpath)
if err != nil {
return nil, err
}
defer f.Close()
}
gdb, err := gorm.Open("sqlite3", dbpath)
if err != nil {
return nil, err
}
gdb.AutoMigrate(&vup{}).AutoMigrate(&config{})
return (*vupdb)(gdb), nil
}
func (vdb *vupdb) insertVupByMid(mid int64, uname string, roomid int64) (err error) {
db := (*gorm.DB)(vdb)
v := vup{
Mid: mid,
Uname: uname,
Roomid: roomid,
}
if err = db.Model(&vup{}).First(&v, "mid = ? ", mid).Error; err != nil {
if gorm.IsRecordNotFoundError(err) {
err = db.Model(&vup{}).Create(&v).Error
}
}
return
}
// filterVup 筛选vup
func (vdb *vupdb) filterVup(ids []int64) (vups []vup, err error) {
db := (*gorm.DB)(vdb)
if err = db.Model(&vup{}).Find(&vups, "mid in (?)", ids).Error; err != nil {
return vups, err
}
return
}
func updateVup() error {
for _, v := range vtbURLs {
data, err := web.GetData(v)
if err != nil {
return err
}
gjson.Get(binary.BytesToString(data), "@this").ForEach(func(key, value gjson.Result) bool {
mid := value.Get("mid").Int()
uname := value.Get("uname").String()
roomid := value.Get("roomid").Int()
err = vdb.insertVupByMid(mid, uname, roomid)
return err == nil
})
if err != nil {
return err
}
}
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,357 @@
// 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"
ctrl "github.com/FloatTech/zbpctrl"
"github.com/FloatTech/zbputils/binary"
"github.com/FloatTech/zbputils/control"
"github.com/FloatTech/zbputils/img/text"
"github.com/FloatTech/zbputils/web"
)
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"
serviceName = "bilibilipush"
)
// bdb bilibili推送数据库
var bdb *bilibilipushdb
var (
lastTime = map[int64]int64{}
liveStatus = map[int64]int{}
upMap = map[int64]string{}
)
func init() {
en := control.Register(serviceName, &ctrl.Options[*zero.Ctx]{
DisableOnDefault: false,
Help: "bilibilipush,需要配合job一起使用\n" +
"- 添加b站订阅[uid|name]\n" +
"- 取消b站订阅[uid|name]\n" +
"- 取消b站动态订阅[uid|name]\n" +
"- 取消b站直播订阅[uid|name]\n" +
"- b站推送列表\n" +
"- 拉取b站推送 (使用job执行定时任务------记录在\"@every 10s\"触发的指令)",
PrivateDataFolder: serviceName,
})
// 加载bilibili推送数据库
dbpath := en.DataFolder()
dbfile := dbpath + "push.db"
bdb = initializePush(dbfile)
en.OnRegex(`^添加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站订阅\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站动态订阅\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站直播订阅\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.OnFullMatch("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.OnFullMatch("拉取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)
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]interface{}{
"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]interface{}{
"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]interface{}{
"bilibili_uid": buid,
"group_id": groupid,
"dynamic_disable": 1,
}
return bdb.insertOrUpdateLiveAndDynamic(bpMap)
}
func unsubscribeLive(buid, groupid int64) (err error) {
bpMap := map[string]interface{}{
"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(spaceHistoryURL, buid, 0), "GET", referer, ua)
if err != nil {
return
}
cardList = gjson.Get(binary.BytesToString(data), "data.cards").Array()
return
}
func getLiveList(uids ...int64) (string, error) {
m := make(map[string]interface{})
m["uids"] = uids
b, err := json.Marshal(m)
if err != nil {
return "", err
}
data, err := web.PostData(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(serviceName)
if ok {
groupList := bdb.getAllGroupByBuidAndDynamic(buid)
msg, err := dynamicCard2msg(cardList[i].Raw, 0)
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(serviceName)
if ok {
groupList := bdb.getAllGroupByBuidAndLive(key.Int())
roomID := value.Get("short_id").Int()
if roomID == 0 {
roomID = value.Get("room_id").Int()
}
lURL := 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,150 @@
package bilibili
import (
"encoding/json"
"os"
_ "github.com/fumiama/sqlite3" // import sql
"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]interface{}) (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
}
}

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

@@ -0,0 +1,233 @@
package bilibili
import (
"encoding/json"
"errors"
"fmt"
"time"
"github.com/FloatTech/zbputils/binary"
"github.com/FloatTech/zbputils/web"
"github.com/tidwall/gjson"
"github.com/wdvxdr1123/ZeroBot/message"
)
var (
typeMsg = map[int]string{
1: "转发了动态",
2: "有图营业",
4: "无图营业",
8: "投稿了视频",
16: "投稿了短视频",
64: "投稿了文章",
256: "投稿了音频",
2048: "发布了简报",
4200: "发布了直播",
4308: "发布了直播",
}
)
// dynamicCard2msg cType=0时,处理DynCard字符串,cType=1, 2, 4, 8, 16, 64, 256, 2048, 4200, 4308时,处理Card字符串,cType为card类型
func dynamicCard2msg(str string, cType int) (msg []message.MessageSegment, err error) {
var (
dynamicCard dynamicCard
card Card
vote Vote
)
msg = make([]message.MessageSegment, 0, 16)
// 初始化结构体
switch cType {
case 0:
err = json.Unmarshal(binary.StringToBytes(str), &dynamicCard)
if err != nil {
return
}
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
case 1, 2, 4, 8, 16, 64, 256, 2048, 4200, 4308:
err = json.Unmarshal(binary.StringToBytes(str), &card)
if err != nil {
return
}
default:
err = errors.New("只有0, 1, 2, 4, 8, 16, 64, 256, 2048, 4200, 4308模式")
return
}
// 生成消息
switch cType {
case 1:
msg = append(msg, message.Text(card.User.Uname, typeMsg[cType], "\n",
card.Item.Content, "\n",
"转发的内容: \n"))
var originMsg []message.MessageSegment
originMsg, err = dynamicCard2msg(card.Origin, 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"), typeMsg[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"), typeMsg[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",
"参与人数: ", 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"), typeMsg[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"), typeMsg[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]interface{})["name"], "在", time.Unix(int64(card.PublishTime), 0).Format("2006-01-02 15:04:05"), typeMsg[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"), typeMsg[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, typeMsg[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, typeMsg[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("动态链接: ", tURL, dynamicCard.Desc.DynamicIDStr))
}
return
}
// dynamicDetail 用动态id查动态信息
func dynamicDetail(dynamicIDStr string) (msg []message.MessageSegment, err error) {
var data []byte
data, err = web.GetData(fmt.Sprintf(dynamicDetailURL, dynamicIDStr))
if err != nil {
return
}
return dynamicCard2msg(gjson.ParseBytes(data).Get("data.card").Raw, 0)
}
// articleCard2msg 专栏转消息
func articleCard2msg(card 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",
"阅读: ", humanNum(card.Stats.View), " 评论: ", humanNum(card.Stats.Reply), "\n",
cvURL, defaultID))
return
}
// liveCard2msg 直播卡片转消息
func liveCard2msg(card 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("直播中 ", humanNum(card.RoomInfo.Online), "人气\n"))
}
if card.RoomInfo.ShortID != 0 {
msg = append(msg, message.Text("直播间链接: ", lURL, card.RoomInfo.ShortID))
} else {
msg = append(msg, message.Text("直播间链接: ", lURL, card.RoomInfo.RoomID))
}
return
}
// videoCard2msg 视频卡片转消息
func videoCard2msg(card Card) (msg []message.MessageSegment, err error) {
var mCard memberCard
msg = make([]message.MessageSegment, 0, 16)
mCard, err = 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, " 粉丝: ", humanNum(card.Staff[i].Follower), "\n"))
}
} else {
msg = append(msg, message.Text("UP主: ", card.Owner.Name, " 粉丝: ", humanNum(mCard.Fans), "\n"))
}
msg = append(msg, message.Text("播放: ", humanNum(card.Stat.View), " 弹幕: ", humanNum(card.Stat.Danmaku)))
msg = append(msg, message.Image(card.Pic))
msg = append(msg, message.Text("\n点赞: ", humanNum(card.Stat.Like), " 投币: ", humanNum(card.Stat.Coin), "\n",
"收藏: ", humanNum(card.Stat.Favorite), " 分享: ", humanNum(card.Stat.Share), "\n",
vURL, card.BvID))
return
}

View File

@@ -0,0 +1,82 @@
package bilibili
import (
"testing"
)
func TestArticleInfo(t *testing.T) {
card, err := 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 := getMemberCard(2)
if err != nil {
t.Fatal(err)
}
t.Logf("%+v\n", card)
}
func TestVideoInfo(t *testing.T) {
card, err := getVideoInfo("10007")
if err != nil {
t.Fatal(err)
}
t.Log(videoCard2msg(card))
card, err = getVideoInfo("BV1xx411c7mD")
if err != nil {
t.Fatal(err)
}
t.Log(videoCard2msg(card))
card, err = getVideoInfo("bv1xx411c7mD")
if err != nil {
t.Fatal(err)
}
t.Log(videoCard2msg(card))
card, err = getVideoInfo("BV1mF411j7iU")
if err != nil {
t.Fatal(err)
}
t.Log(videoCard2msg(card))
}
func TestLiveRoomInfo(t *testing.T) {
card, err := getLiveRoomInfo("83171")
if err != nil {
t.Fatal(err)
}
t.Log(liveCard2msg(card))
}

273
plugin/bilibili/types.go Normal file
View File

@@ -0,0 +1,273 @@
package bilibili
const (
// tURL bilibili动态前缀
tURL = "https://t.bilibili.com/"
// liveURL bilibili直播前缀
liveURL = "https://live.bilibili.com/"
// dynamicDetailURL 当前动态信息,一个card
dynamicDetailURL = "https://api.vc.bilibili.com/dynamic_svr/v1/dynamic_svr/get_dynamic_detail?dynamic_id=%v"
// memberCardURL 个人信息
memberCardURL = "https://account.bilibili.com/api/member/getCardByMid?mid=%v"
// articleInfoURL 查看专栏信息
articleInfoURL = "https://api.bilibili.com/x/article/viewinfo?id=%v"
// cvURL b站专栏前缀
cvURL = "https://www.bilibili.com/read/cv"
// liveRoomInfoURL 查看直播间信息
liveRoomInfoURL = "https://api.live.bilibili.com/xlive/web-room/v1/index/getInfoByRoom?room_id=%v"
// lURL b站直播间前缀
lURL = "https://live.bilibili.com/"
// videoInfoURL 查看视频信息
videoInfoURL = "https://api.bilibili.com/x/web-interface/view?aid=%v&bvid=%v"
// vURL 视频网址前缀
vURL = "https://www.bilibili.com/video/"
// searchUserURL 查找b站用户
searchUserURL = "http://api.bilibili.com/x/web-interface/search/type?search_type=bili_user&keyword=%v"
// vtbDetailURL 查找vtb信息
vtbDetailURL = "https://api.vtbs.moe/v1/detail/%v"
// medalwallURL 查找牌子
medalwallURL = "https://api.live.bilibili.com/xlive/web-ucenter/user/MedalWall?target_id=%v"
// spaceHistoryURL 历史动态信息,一共12个card
spaceHistoryURL = "https://api.vc.bilibili.com/dynamic_svr/v1/dynamic_svr/space_history?host_uid=%v&offset_dynamic_id=%v&need_top=0"
// liveListURL 获得直播状态
liveListURL = "https://api.live.bilibili.com/room/v1/Room/get_status_info_by_uids"
)
// dynamicCard 总动态结构体,包括desc,card
type dynamicCard struct {
Desc Desc `json:"desc"`
Card string `json:"card"`
Extension struct {
VoteCfg struct {
VoteID int `json:"vote_id"`
Desc string `json:"desc"`
JoinNum int `json:"join_num"`
} `json:"vote_cfg"`
Vote string `json:"vote"`
} `json:"extension"`
}
// Card 卡片结构体
type Card struct {
Item struct {
Content string `json:"content"`
UploadTime int `json:"upload_time"`
Description string `json:"description"`
Pictures []struct {
ImgSrc string `json:"img_src"`
} `json:"pictures"`
Timestamp int `json:"timestamp"`
Cover struct {
Default string `json:"default"`
} `json:"cover"`
OrigType int `json:"orig_type"`
} `json:"item"`
AID interface{} `json:"aid"`
BvID interface{} `json:"bvid"`
Dynamic interface{} `json:"dynamic"`
Pic string `json:"pic"`
Title string `json:"title"`
ID int `json:"id"`
Summary string `json:"summary"`
ImageUrls []string `json:"image_urls"`
OriginImageUrls []string `json:"origin_image_urls"`
Sketch struct {
Title string `json:"title"`
DescText string `json:"desc_text"`
CoverURL string `json:"cover_url"`
TargetURL string `json:"target_url"`
} `json:"sketch"`
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"`
Stats 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:"stats"`
Owner struct {
Name string `json:"name"`
Pubdate int `json:"pubdate"`
Mid int `json:"mid"`
} `json:"owner"`
Cover string `json:"cover"`
ShortID interface{} `json:"short_id"`
LivePlayInfo struct {
ParentAreaName string `json:"parent_area_name"`
AreaName string `json:"area_name"`
Cover string `json:"cover"`
Link string `json:"link"`
Online int `json:"online"`
RoomID int `json:"room_id"`
LiveStatus int `json:"live_status"`
WatchedShow string `json:"watched_show"`
Title string `json:"title"`
} `json:"live_play_info"`
Intro string `json:"intro"`
Schema string `json:"schema"`
Author interface{} `json:"author"`
AuthorName string `json:"author_name"`
PlayCnt int `json:"play_cnt"`
ReplyCnt int `json:"reply_cnt"`
TypeInfo string `json:"type_info"`
User struct {
Name string `json:"name"`
Uname string `json:"uname"`
} `json:"user"`
Desc string `json:"desc"`
ShareSubtitle string `json:"share_subtitle"`
ShortLink string `json:"short_link"`
PublishTime int `json:"publish_time"`
BannerURL string `json:"banner_url"`
Ctime int `json:"ctime"`
Vest struct {
Content string `json:"content"`
} `json:"vest"`
Upper string `json:"upper"`
Origin string `json:"origin"`
Pubdate int `json:"pubdate"`
Rights struct {
IsCooperation int `json:"is_cooperation"`
} `json:"rights"`
Staff []struct {
Title string `json:"title"`
Name string `json:"name"`
Follower int `json:"follower"`
} `json:"staff"`
}
// Desc 描述结构体
type Desc struct {
Type int `json:"type"`
DynamicIDStr string `json:"dynamic_id_str"`
OrigType int `json:"orig_type"`
Timestamp int `json:"timestamp"`
Origin struct {
DynamicIDStr string `json:"dynamic_id_str"`
} `json:"origin"`
UserProfile struct {
Info struct {
Uname string `json:"uname"`
} `json:"info"`
} `json:"user_profile"`
}
// Vote 投票结构体
type Vote struct {
ChoiceCnt int `json:"choice_cnt"`
Desc string `json:"desc"`
Endtime int `json:"endtime"`
JoinNum int `json:"join_num"`
Options []struct {
Idx int `json:"idx"`
Desc string `json:"desc"`
ImgURL string `json:"img_url"`
} `json:"options"`
}
// memberCard 个人信息卡片
type memberCard struct {
Mid string `json:"mid"`
Name string `json:"name"`
Sex string `json:"sex"`
Face string `json:"face"`
Coins float64 `json:"coins"`
Regtime int64 `json:"regtime"`
Birthday string `json:"birthday"`
Sign string `json:"sign"`
Attentions []int64 `json:"attentions"`
Fans int `json:"fans"`
Friend int `json:"friend"`
Attention int `json:"attention"`
LevelInfo struct {
CurrentLevel int `json:"current_level"`
} `json:"level_info"`
}
// roomCard 直播间卡片
type roomCard struct {
RoomInfo struct {
RoomID int `json:"room_id"`
ShortID int `json:"short_id"`
Title string `json:"title"`
LiveStatus int `json:"live_status"`
AreaName string `json:"area_name"`
ParentAreaName string `json:"parent_area_name"`
Keyframe string `json:"keyframe"`
Online int `json:"online"`
} `json:"room_info"`
AnchorInfo struct {
BaseInfo struct {
Uname string `json:"uname"`
} `json:"base_info"`
} `json:"anchor_info"`
}
// searchResult 查找b站用户结果
type searchResult struct {
Mid int64 `json:"mid"`
Uname string `json:"uname"`
Gender int64 `json:"gender"`
Usign string `json:"usign"`
Level int64 `json:"level"`
}
// medalData 牌子接口返回结构体
type medalData struct {
Code int `json:"code"`
Message string `json:"message"`
Data struct {
List []medal `json:"list"`
} `json:"data"`
}
// medalInfo b站牌子信息
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
}
// vtb信息
type vtbDetail 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"`
}

25
plugin/bilibili/util.go Normal file
View File

@@ -0,0 +1,25 @@
package bilibili
import (
"net/http"
"strconv"
)
// humanNum 格式化人数
func humanNum(res int) string {
if res/10000 != 0 {
return strconv.FormatFloat(float64(res)/10000, 'f', 2, 64) + "万"
}
return strconv.Itoa(res)
}
// getrealurl 获取跳转后的链接
func getrealurl(url string) (realurl string, err error) {
data, err := http.Head(url)
if err != nil {
return
}
_ = data.Body.Close()
realurl = data.Request.URL.String()
return
}

View File

@@ -0,0 +1,74 @@
// Package bookreview 书评
package bookreview
import (
"time"
log "github.com/sirupsen/logrus"
zero "github.com/wdvxdr1123/ZeroBot"
"github.com/wdvxdr1123/ZeroBot/message"
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- 随机书评",
PublicDataFolder: "BookReview",
})
getdb := ctxext.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))
return false
}
err = db.Create("bookreview", &book{})
if err != nil {
ctx.SendChain(message.Text("ERROR:", err))
return false
}
n, err := db.Count("bookreview")
if err != nil {
ctx.SendChain(message.Text("ERROR:", err))
return false
}
log.Infof("[bookreview]读取%d条书评", n)
return true
})
// 中文、英文、数字但不包括下划线等符号
engine.OnRegex("^书评([\u4E00-\u9FA5A-Za-z0-9]{1,25})$", getdb).SetBlock(true).
Handle(func(ctx *zero.Ctx) {
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))
return
}
if id := ctx.SendChain(message.Image("base64://" + binary.BytesToString(data))); id.ID() == 0 {
ctx.SendChain(message.Text("ERROR:可能被风控了"))
}
})
engine.OnFullMatch("随机书评", getdb).SetBlock(true).
Handle(func(ctx *zero.Ctx) {
br := getRandomBookReview()
data, err := text.RenderToBase64(br.BookReview, 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

@@ -1,10 +1,14 @@
package bookreview
import sql "github.com/FloatTech/sqlite"
type book struct {
ID uint64 `db:"id"`
BookReview string `db:"bookreview"`
}
var db = &sql.Sqlite{}
// 暂时随机选择一个书评
func getBookReviewByKeyword(keyword string) (b book) {
_ = db.Find("bookreview", &b, "where bookreview LIKE '%"+keyword+"%'")

View File

@@ -9,13 +9,12 @@ import (
"net/url"
"strings"
ctrl "github.com/FloatTech/zbpctrl"
"github.com/FloatTech/zbputils/control"
"github.com/antchfx/htmlquery"
log "github.com/sirupsen/logrus"
zero "github.com/wdvxdr1123/ZeroBot"
"github.com/wdvxdr1123/ZeroBot/message"
"github.com/wdvxdr1123/ZeroBot/utils/helper"
"github.com/FloatTech/ZeroBot-Plugin/control"
)
const (
@@ -23,7 +22,6 @@ const (
searchURL = "https://www.shicimingju.com/cangtoushi/index.html"
ua = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/96.0.4664.110 Safari/537.36"
referer = "https://www.shicimingju.com/cangtoushi/index.html"
prio = 20
)
var (
@@ -32,92 +30,110 @@ var (
)
func init() {
engine := control.Register("cangtoushi", &control.Options{
engine := control.Register("cangtoushi", &ctrl.Options[*zero.Ctx]{
DisableOnDefault: false,
Help: "藏头诗\n" +
"- 藏头诗[xxx]\n- 藏尾诗[xxx]",
})
engine.OnRegex("藏头诗([\u4E00-\u9FA5]{3,10})").SetBlock(true).SetPriority(prio).Handle(func(ctx *zero.Ctx) {
engine.OnRegex(`藏头诗\s?([一-龥]{3,10})$`).SetBlock(true).Handle(func(ctx *zero.Ctx) {
kw := ctx.State["regex_matched"].([]string)[1]
login()
err := login()
if err != nil {
ctx.SendChain(message.Text("ERROR:", err))
return
}
data, err := search(kw, "7", "0")
if err != nil {
log.Errorln("[cangtoushi]:", err)
ctx.SendChain(message.Text("ERROR:", err))
return
}
text, err := dealHTML(helper.BytesToString(data))
if err != nil {
ctx.SendChain(message.Text("ERROR:", err))
return
}
text := dealHTML(helper.BytesToString(data))
ctx.SendChain(message.Text(text))
})
engine.OnRegex("藏尾诗([\u4E00-\u9FA5]{3,10})").SetBlock(true).SetPriority(prio).Handle(func(ctx *zero.Ctx) {
engine.OnRegex(`藏尾诗\s?([一-龥]{3,10})$`).SetBlock(true).Handle(func(ctx *zero.Ctx) {
kw := ctx.State["regex_matched"].([]string)[1]
login()
err := login()
if err != nil {
ctx.SendChain(message.Text("ERROR:", err))
return
}
data, err := search(kw, "7", "2")
if err != nil {
log.Errorln("[cangtoushi]:", err)
ctx.SendChain(message.Text("ERROR:", err))
return
}
text, err := dealHTML(helper.BytesToString(data))
if err != nil {
ctx.SendChain(message.Text("ERROR:", err))
return
}
text := dealHTML(helper.BytesToString(data))
ctx.SendChain(message.Text(text))
})
}
func login() {
func login() error {
gCurCookieJar, _ = cookiejar.New(nil)
client := &http.Client{
Jar: gCurCookieJar,
}
request, err := http.NewRequest("GET", loginURL, nil)
if err != nil {
log.Errorln("[cangtoushi]:", err)
return err
}
request.Header.Add("User-Agent", ua)
response, err := client.Do(request)
if err != nil {
log.Errorln("[cangtoushi]:", err)
return err
}
data, err := io.ReadAll(response.Body)
if err != nil {
log.Errorln("[cangtoushi]:", err)
return err
}
response.Body.Close()
doc, err := htmlquery.Parse(strings.NewReader(helper.BytesToString(data)))
if err != nil {
log.Errorln("[cangtoushi]:", err)
return err
}
csrf = htmlquery.SelectAttr(htmlquery.FindOne(doc, "//input[@name='_csrf']"), "value")
return nil
}
func search(kw, zishu, position string) (data []byte, err error) {
postStr := fmt.Sprintf("_csrf=%s&kw=%s&zishu=%s&position=%s", url.QueryEscape(csrf), url.QueryEscape(kw), zishu, position)
log.Println("postStr:", postStr)
client := &http.Client{
Jar: gCurCookieJar,
}
request, err := http.NewRequest("POST", searchURL, strings.NewReader(postStr))
if err != nil {
log.Errorln("[cangtoushi]:", err)
return nil, err
}
request.Header.Add("Referer", referer)
request.Header.Add("User-Agent", ua)
request.Header.Add("Content-Type", "application/x-www-form-urlencoded")
response, err := client.Do(request)
if err != nil {
log.Errorln("[cangtoushi]:", err)
return nil, err
}
data, err = io.ReadAll(response.Body)
if err != nil {
log.Errorln("[cangtoushi]:", err)
return nil, err
}
response.Body.Close()
return
}
func dealHTML(data string) (text string) {
func dealHTML(data string) (text string, err error) {
doc, err := htmlquery.Parse(strings.NewReader(data))
if err != nil {
log.Errorln("[cangtoushi]:", err)
return "", err
}
text = htmlquery.InnerText(htmlquery.FindOne(doc, "//div[@class='card']/div[@class='card']"))
text = strings.ReplaceAll(text, " ", "")
text = strings.Replace(text, "\n", "", 1)
return text
return text, nil
}

View File

@@ -0,0 +1,111 @@
// Package charreverser 英文字符反转
package charreverser
import (
"regexp"
"strings"
ctrl "github.com/FloatTech/zbpctrl"
"github.com/FloatTech/zbputils/control"
zero "github.com/wdvxdr1123/ZeroBot"
"github.com/wdvxdr1123/ZeroBot/message"
)
const commandRegex = `[A-z]{1}([A-z]|\s)+[A-z]{1}` // 命令正则表达式
var (
charMap = map[rune]rune{
'a': 'ɐ',
'b': 'q',
'c': 'ɔ',
'd': 'p',
'e': 'ǝ',
'f': 'ɟ',
'g': 'ƃ',
'h': 'ɥ',
'i': 'ᴉ',
'j': 'ɾ',
'k': 'ʞ',
'l': 'l',
'm': 'ɯ',
'n': 'u',
'o': 'o',
'p': 'd',
'q': 'b',
'r': 'ɹ',
's': 's',
't': 'ʇ',
'u': 'n',
'v': 'ʌ',
'w': 'ʍ',
'x': 'x',
'y': 'ʎ',
'z': 'z',
'A': '∀',
'B': 'ᗺ',
'C': 'Ɔ',
'D': 'ᗡ',
'E': 'Ǝ',
'F': 'Ⅎ',
'G': '⅁',
'H': 'H',
'I': 'I',
'J': 'ſ',
'K': 'ʞ',
'L': '˥',
'M': 'W',
'N': 'N',
'O': 'O',
'P': 'Ԁ',
'Q': 'Ò',
'R': 'ᴚ',
'S': 'S',
'T': '⏊',
'U': '∩',
'V': 'Λ',
'W': 'M',
'X': 'X',
'Y': '⅄',
'Z': 'Z',
}
compiledRegex = regexp.MustCompile(commandRegex)
)
func init() {
// 初始化engine
engine := control.Register(
"charreverser",
&ctrl.Options[*zero.Ctx]{
DisableOnDefault: false,
Help: "字符翻转\n -翻转 <英文字符串>",
},
)
// 处理字符翻转指令
engine.OnRegex(`翻转( )+[A-z]{1}([A-z]|\s)+[A-z]{1}`).SetBlock(true).Handle(
func(ctx *zero.Ctx) {
// 获取需要翻转的字符串
results := compiledRegex.FindAllString(ctx.MessageString(), -1)
str := results[0]
// 将字符顺序翻转
var tempBuilder strings.Builder
for i := len(str) - 1; i >= 0; i-- {
tempBuilder.WriteByte(str[i])
}
// 翻转字符字形
var reversedStrBuilder strings.Builder
for _, char := range tempBuilder.String() {
if char != ' ' {
reversedStrBuilder.WriteRune(charMap[char])
} else {
reversedStrBuilder.WriteRune(' ')
}
}
// 发送翻转后的字符串
ctx.SendChain(message.Text(reversedStrBuilder.String()))
},
)
}

View File

@@ -6,32 +6,24 @@ import (
"strconv"
"time"
ctrl "github.com/FloatTech/zbpctrl"
"github.com/FloatTech/zbputils/control"
zero "github.com/wdvxdr1123/ZeroBot"
"github.com/wdvxdr1123/ZeroBot/extension/rate"
"github.com/wdvxdr1123/ZeroBot/message"
"github.com/FloatTech/ZeroBot-Plugin/control"
)
const (
dbpath = "data/Chat/"
dbfile = dbpath + "kimoi.json"
prio = 10
)
var (
poke = rate.NewManager(time.Minute*5, 8) // 戳一戳
engine = control.Register("chat", &control.Options{
poke = rate.NewManager[int64](time.Minute*5, 8) // 戳一戳
engine = control.Register("chat", &ctrl.Options[*zero.Ctx]{
DisableOnDefault: false,
Help: "chat\n- [BOT名字]\n- [戳一戳BOT]\n- 空调开\n- 空调关\n- 群温度\n- 设置温度[正整数]\n- mua|啾咪|摸|上你|傻|裸|贴|老婆|抱|亲|一下|咬|操|123|进去|调教|搓|让|捏|挤|略|呐|原味|胖次|内裤|内衣|衣服|ghs|批|憨批|kkp|咕|骚|喜欢|suki|好き|看|不能|砸了|透|口我|草我|自慰|onani|オナニー|炸了|色图|涩图|告白|对不起|回来|吻|软|壁咚|掰开|女友|是|喵|嗷呜|叫|拜|佬|awsl|臭|香|腿|张开|脚|脸|头发|手|pr|舔|小穴|腰|诶嘿嘿|可爱|扭蛋|鼻|眼|色气|推|床|举|手冲|饿|变|敲|爬|怕|冲|射|不穿|迫害|猫粮|揪尾巴|薄荷|早|晚安|揉|榨|掐|胸|奶子|欧派|嫩|蹭|牵手|握手|拍照|w|睡不着|欧尼酱|哥|爱你|过来|自闭|打不过|么么哒|很懂|膝枕|累了|安慰|洗澡|一起睡觉|一起|多大|姐姐|糖|嗦|牛子|🐂子|🐮子|嫌弃|紧|baka|笨蛋|插|插进来|屁股|翘|翘起来|抬|抬起|爸|傲娇|rua|咕噜咕噜|咕噜|上床|做爱|吃掉|吃|揪|种草莓|种草|掀|妹|病娇|嘻",
Help: "chat\n- [BOT名字]\n- [戳一戳BOT]\n- 空调开\n- 空调关\n- 群温度\n- 设置温度[正整数]",
})
kimomap = make(kimo, 256)
chatList = make([]string, 0, 256)
)
func init() { // 插件主体
// 被喊名字
engine.OnFullMatch("", zero.OnlyToMe).SetBlock(true).SetPriority(prio).
engine.OnFullMatch("", zero.OnlyToMe).SetBlock(true).
Handle(func(ctx *zero.Ctx) {
var nickname = zero.BotConfig.NickName[0]
time.Sleep(time.Second * 1)
@@ -45,7 +37,7 @@ func init() { // 插件主体
))
})
// 戳一戳
engine.On("notice/notify/poke", zero.OnlyToMe).SetBlock(false).SetPriority(prio).
engine.On("notice/notify/poke", zero.OnlyToMe).SetBlock(false).
Handle(func(ctx *zero.Ctx) {
var nickname = zero.BotConfig.NickName[0]
switch {
@@ -64,18 +56,18 @@ func init() { // 插件主体
// 群空调
var AirConditTemp = map[int64]int{}
var AirConditSwitch = map[int64]bool{}
engine.OnFullMatch("空调开").SetBlock(true).SetPriority(prio).
engine.OnFullMatch("空调开").SetBlock(true).
Handle(func(ctx *zero.Ctx) {
AirConditSwitch[ctx.Event.GroupID] = true
ctx.SendChain(message.Text("❄️哔~"))
})
engine.OnFullMatch("空调关").SetBlock(true).SetPriority(prio).
engine.OnFullMatch("空调关").SetBlock(true).
Handle(func(ctx *zero.Ctx) {
AirConditSwitch[ctx.Event.GroupID] = false
delete(AirConditTemp, ctx.Event.GroupID)
ctx.SendChain(message.Text("💤哔~"))
})
engine.OnRegex(`设置温度(\d+)`).SetBlock(true).SetPriority(prio).
engine.OnRegex(`设置温度(\d+)`).SetBlock(true).
Handle(func(ctx *zero.Ctx) {
if _, exist := AirConditTemp[ctx.Event.GroupID]; !exist {
AirConditTemp[ctx.Event.GroupID] = 26
@@ -94,7 +86,7 @@ func init() { // 插件主体
))
}
})
engine.OnFullMatch(`群温度`).SetBlock(true).SetPriority(prio).
engine.OnFullMatch(`群温度`).SetBlock(true).
Handle(func(ctx *zero.Ctx) {
if _, exist := AirConditTemp[ctx.Event.GroupID]; !exist {
AirConditTemp[ctx.Event.GroupID] = 26
@@ -111,13 +103,4 @@ func init() { // 插件主体
))
}
})
initChatList(func() {
engine.OnFullMatchGroup(chatList, zero.OnlyToMe).SetBlock(true).SetPriority(prio).Handle(
func(ctx *zero.Ctx) {
key := ctx.MessageString()
val := *kimomap[key]
text := val[rand.Intn(len(val))]
ctx.SendChain(message.Reply(ctx.Event.MessageID), message.Text(text))
})
})
}

View File

@@ -6,20 +6,20 @@ import (
"strconv"
"strings"
ctrl "github.com/FloatTech/zbpctrl"
"github.com/FloatTech/zbputils/control"
zero "github.com/wdvxdr1123/ZeroBot"
"github.com/wdvxdr1123/ZeroBot/message"
"github.com/FloatTech/ZeroBot-Plugin/control"
)
func init() {
engine := control.Register("choose", &control.Options{
engine := control.Register("choose", &ctrl.Options[*zero.Ctx]{
DisableOnDefault: false,
Help: "choose\n" +
"- 选择可口可乐还是百事可乐\n" +
"- 选择肯德基还是麦当劳还是必胜客",
})
engine.OnPrefix("选择").SetBlock(true).FirstPriority().Handle(handle)
engine.OnPrefix("选择").SetBlock(true).Handle(handle)
}
func handle(ctx *zero.Ctx) {
rawOptions := strings.Split(ctx.State["args"].(string), "还是")

View File

@@ -0,0 +1,72 @@
// Package chouxianghua 抽象话转化
package chouxianghua
import (
"time"
"github.com/sirupsen/logrus"
zero "github.com/wdvxdr1123/ZeroBot"
"github.com/wdvxdr1123/ZeroBot/message"
ctrl "github.com/FloatTech/zbpctrl"
"github.com/FloatTech/zbputils/control"
"github.com/FloatTech/zbputils/ctxext"
)
func init() {
en := control.Register("chouxianghua", &ctrl.Options[*zero.Ctx]{
DisableOnDefault: false,
Help: "抽象话\n- 抽象翻译xxx",
PublicDataFolder: "ChouXiangHua",
})
en.OnRegex("^抽象翻译((\\s|[\\r\\n]|[\\p{Han}\\p{P}A-Za-z0-9])+)$",
ctxext.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))
return false
}
err = db.Create("pinyin", &pinyin{})
if err != nil {
ctx.SendChain(message.Text("ERROR:", err))
return false
}
n, err := db.Count("pinyin")
if err != nil {
ctx.SendChain(message.Text("ERROR:", err))
return false
}
logrus.Printf("[chouxianghua]读取%d条拼音", n)
return true
}),
).SetBlock(true).
Handle(func(ctx *zero.Ctx) {
r := cx(ctx.State["regex_matched"].([]string)[1])
ctx.SendChain(message.Text(r))
})
}
func cx(s string) (r string) {
h := []rune(s)
for i := 0; i < len(h); i++ {
if i < len(h)-1 {
e := getEmojiByPronun(getPronunByDWord(h[i], h[i+1]))
if e != "" {
r += e
i++
continue
}
}
e := getEmojiByPronun(getPinyinByWord(string(h[i])))
if e != "" {
r += e
continue
}
r += string(h[i])
}
return
}

View File

@@ -1,5 +1,7 @@
package chouxianghua
import sql "github.com/FloatTech/sqlite"
type pinyin struct {
Word string `db:"word"`
Pronun string `db:"pronunciation"`
@@ -9,6 +11,8 @@ type emoji struct {
Emoji string `db:"emoji"`
}
var db = &sql.Sqlite{}
func getPinyinByWord(word string) string {
var p pinyin
_ = db.Find("pinyin", &p, "where word = '"+word+"'")

56
plugin/coser/coser.go Normal file
View File

@@ -0,0 +1,56 @@
// Package coser images
package coser
import (
"regexp"
"github.com/tidwall/gjson"
zero "github.com/wdvxdr1123/ZeroBot"
"github.com/wdvxdr1123/ZeroBot/message"
"github.com/wdvxdr1123/ZeroBot/utils/helper"
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() {
control.Register("coser", &ctrl.Options[*zero.Ctx]{
DisableOnDefault: false,
Help: "三次元小姐姐\n- coser",
}).ApplySingle(ctxext.DefaultSingle).OnFullMatch("coser", zero.OnlyGroup).SetBlock(true).Limit(ctxext.LimitByGroup).
Handle(func(ctx *zero.Ctx) {
ctx.SendChain(message.Text("少女祈祷中......"))
data, err := web.RequestDataWith(web.NewDefaultClient(), coserURL, "GET", "", ua)
if err != nil {
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:可能被风控或下载图片用时过长,请耐心等待"))
}
})
}

73
plugin/cpstory/cpstory.go Normal file
View File

@@ -0,0 +1,73 @@
// Package cpstory cp短打
package cpstory
import (
"strings"
"time"
"github.com/sirupsen/logrus"
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/math"
)
func init() {
engine := control.Register("cpstory", &ctrl.Options[*zero.Ctx]{
DisableOnDefault: false,
Help: "cp短打\n- 组cp[@xxx][@xxx]\n- 磕cp大老师 雪乃",
PublicDataFolder: "CpStory",
})
getdb := ctxext.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))
return false
}
err = db.Create("cp_story", &cpstory{})
if err != nil {
ctx.SendChain(message.Text("ERROR:", err))
return false
}
n, err := db.Count("cp_story")
if err != nil {
ctx.SendChain(message.Text("ERROR:", err))
return false
}
logrus.Printf("[cpstory]读取%d条故事", n)
return true
})
engine.OnRegex("^组cp.*?(\\d+).*?(\\d+)", zero.OnlyGroup, getdb).SetBlock(true).Handle(func(ctx *zero.Ctx) {
cs := getRandomCpStory()
gong := ctx.CardOrNickName(math.Str2Int64(ctx.State["regex_matched"].([]string)[1]))
shou := ctx.CardOrNickName(math.Str2Int64(ctx.State["regex_matched"].([]string)[2]))
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)
ctx.SendChain(message.Text(text))
})
engine.OnPrefix("磕cp", getdb).SetBlock(true).Handle(func(ctx *zero.Ctx) {
cs := getRandomCpStory()
params := strings.Split(ctx.State["args"].(string), " ")
if len(params) < 2 {
ctx.SendChain(message.Text(ctx.Event.MessageID), message.Text("请用空格分开两个人名"))
} else {
gong := params[0]
shou := params[1]
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)
ctx.SendChain(message.Text(text))
}
})
}

View File

@@ -1,5 +1,7 @@
package cpstory
import sql "github.com/FloatTech/sqlite"
type cpstory struct {
ID int64 `db:"id"`
Gong string `db:"gong"`
@@ -7,6 +9,8 @@ type cpstory struct {
Story string `db:"story"`
}
var db = &sql.Sqlite{}
func getRandomCpStory() (cs cpstory) {
_ = db.Pick("cp_story", &cs)
return

72
plugin/curse/curse.go Normal file
View File

@@ -0,0 +1,72 @@
// Package curse 骂人插件(求骂,自卫)
package curse
import (
"time"
"github.com/sirupsen/logrus"
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/process"
)
const (
minLevel = "min"
maxLevel = "max"
)
func init() {
engine := control.Register("curse", &ctrl.Options[*zero.Ctx]{
DisableOnDefault: true,
Help: "骂人(求骂,自卫)\n- 骂我\n- 大力骂我",
PublicDataFolder: "Curse",
})
getdb := ctxext.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))
return false
}
err = db.Open(time.Hour * 24)
if err != nil {
ctx.SendChain(message.Text("ERROR:", err))
return false
}
err = db.Create("curse", &curse{})
if err != nil {
ctx.SendChain(message.Text("ERROR:", err))
return false
}
c, err := db.Count("curse")
if err != nil {
ctx.SendChain(message.Text("ERROR:", err))
return false
}
logrus.Infoln("[curse]加载", c, "条骂人语录")
return true
})
engine.OnFullMatch("骂我", getdb).SetBlock(true).Limit(ctxext.LimitByUser).Handle(func(ctx *zero.Ctx) {
process.SleepAbout1sTo2s()
text := getRandomCurseByLevel(minLevel).Text
ctx.SendChain(message.Reply(ctx.Event.MessageID), message.Text(text))
})
engine.OnFullMatch("大力骂我", getdb).SetBlock(true).Limit(ctxext.LimitByUser).Handle(func(ctx *zero.Ctx) {
process.SleepAbout1sTo2s()
text := getRandomCurseByLevel(maxLevel).Text
ctx.SendChain(message.Reply(ctx.Event.MessageID), message.Text(text))
})
engine.OnKeywordGroup([]string{"他妈", "公交车", "你妈", "操", "屎", "去死", "快死", "我日", "逼", "尼玛", "艾滋", "癌症", "有病", "烦你", "你爹", "屮", "cnm"}, zero.OnlyToMe, getdb).SetBlock(true).
Handle(func(ctx *zero.Ctx) {
text := getRandomCurseByLevel(maxLevel).Text
ctx.SendChain(message.Reply(ctx.Event.MessageID), message.Text(text))
})
}

16
plugin/curse/model.go Normal file
View File

@@ -0,0 +1,16 @@
package curse
import sql "github.com/FloatTech/sqlite"
type curse struct {
ID uint32 `db:"id"`
Text string `db:"text"`
Level string `db:"level"`
}
var db = &sql.Sqlite{}
func getRandomCurseByLevel(level string) (c curse) {
_ = db.Find("curse", &c, "where level = '"+level+"' ORDER BY RANDOM() limit 1")
return
}

46
plugin/danbooru/main.go Normal file
View File

@@ -0,0 +1,46 @@
// Package deepdanbooru 二次元图片标签识别
package deepdanbooru
import (
"crypto/md5"
"encoding/hex"
"github.com/FloatTech/AnimeAPI/danbooru"
ctrl "github.com/FloatTech/zbpctrl"
"github.com/FloatTech/zbputils/control"
"github.com/FloatTech/zbputils/file"
"github.com/FloatTech/zbputils/img/writer"
zero "github.com/wdvxdr1123/ZeroBot"
"github.com/wdvxdr1123/ZeroBot/message"
"github.com/wdvxdr1123/ZeroBot/utils/helper"
)
func init() { // 插件主体
engine := control.Register("danbooru", &ctrl.Options[*zero.Ctx]{
DisableOnDefault: false,
Help: "二次元图片标签识别\n" +
"- 鉴赏图片[图片]",
PrivateDataFolder: "danbooru",
})
cachefolder := engine.DataFolder()
// 上传一张图进行评价
engine.OnKeywordGroup([]string{"鉴赏图片"}, zero.OnlyGroup, zero.MustProvidePicture).SetBlock(true).
Handle(func(ctx *zero.Ctx) {
ctx.SendChain(message.Text("少女祈祷中..."))
for _, url := range ctx.State["image_url"].([]string) {
t, err := danbooru.TagURL("", url)
if err != nil {
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)
}
ctx.SendChain(message.Image("file:///" + file.BOTPATH + "/" + f))
}
})
}

57
plugin/diana/bing.go Normal file
View File

@@ -0,0 +1,57 @@
// Package diana 虚拟偶像女团 A-SOUL 成员嘉然相关
package diana
import (
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/ZeroBot-Plugin/plugin/diana/data"
)
var engine = control.Register("diana", &ctrl.Options[*zero.Ctx]{
DisableOnDefault: false,
Help: "嘉然\n" +
"- 小作文\n" +
"- 发大病\n" +
"- 教你一篇小作文[作文]\n" +
"- [回复]查重",
PublicDataFolder: "Diana",
})
func init() {
getdb := ctxext.DoOnceOnSuccess(func(ctx *zero.Ctx) bool {
err := data.LoadText(engine.DataFolder() + "text.db")
if err != nil {
ctx.SendChain(message.Text("ERROR:", err))
return false
}
return true
})
// 随机发送一篇上面的小作文
engine.OnFullMatch("小作文", getdb).SetBlock(true).
Handle(func(ctx *zero.Ctx) {
// 绕过第一行发病
ctx.SendChain(message.Text(data.RandText()))
})
// 逆天
engine.OnFullMatch("发大病", getdb).SetBlock(true).
Handle(func(ctx *zero.Ctx) {
// 第一行是发病
ctx.SendChain(message.Text(data.HentaiText()))
})
// 增加小作文
engine.OnRegex(`^教你一篇小作文(.*)$`, zero.AdminPermission, getdb).SetBlock(true).
Handle(func(ctx *zero.Ctx) {
err := data.AddText(ctx.State["regex_matched"].([]string)[1])
if err != nil {
ctx.SendChain(message.Text("ERROR:", err))
} else {
ctx.SendChain(message.Text("记住啦!"))
}
})
}

70
plugin/diana/data/text.go Normal file
View File

@@ -0,0 +1,70 @@
// Package data 加载位于 datapath 的小作文
package data
import (
"crypto/md5"
"encoding/binary"
"time"
sql "github.com/FloatTech/sqlite"
binutils "github.com/FloatTech/zbputils/binary"
"github.com/FloatTech/zbputils/file"
"github.com/sirupsen/logrus"
)
var db = sql.Sqlite{}
type text struct {
ID int64 `db:"id"`
Data string `db:"data"`
}
// LoadText 加载小作文
func LoadText(dbfile string) error {
_, err := file.GetLazyData(dbfile, false)
db.DBPath = dbfile
if err != nil {
return err
}
err = db.Open(time.Hour * 24)
if err != nil {
return err
}
err = db.Create("text", &text{})
if err != nil {
return err
}
c, err := db.Count("text")
if err != nil {
return err
}
logrus.Printf("[Diana]读取%d条小作文", c)
return nil
}
// AddText 添加小作文
func AddText(txt string) error {
s := md5.Sum(binutils.StringToBytes(txt))
i := binary.LittleEndian.Uint64(s[:8])
return db.Insert("text", &text{ID: int64(i), Data: txt})
}
// RandText 随机小作文
func RandText() string {
var t text
err := db.Pick("text", &t)
if err != nil {
return err.Error()
}
return t.Data
}
// HentaiText 发大病
func HentaiText() string {
var t text
err := db.Find("text", &t, "where id = -3802576048116006195")
if err != nil {
return err.Error()
}
return t.Data
}

89
plugin/diana/zhiwang.go Normal file
View File

@@ -0,0 +1,89 @@
// 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
}

View File

@@ -0,0 +1,53 @@
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{})
}

133
plugin/drift_bottle/main.go Normal file
View File

@@ -0,0 +1,133 @@
// Package driftbottle 漂流瓶
package driftbottle
import (
"strconv"
"strings"
"sync"
"time"
ctrl "github.com/FloatTech/zbpctrl"
"github.com/FloatTech/zbputils/control"
"github.com/sirupsen/logrus"
zero "github.com/wdvxdr1123/ZeroBot"
"github.com/wdvxdr1123/ZeroBot/message"
)
func init() {
en := control.Register("driftbottle", &ctrl.Options[*zero.Ctx]{
DisableOnDefault: false,
Help: "漂流瓶\n- (在群xxx)丢漂流瓶(到频道xxx) [消息]\n- (从频道xxx)捡漂流瓶\n- @BOT 创建频道 xxx\n- 跳入(频道)海中\n- 注:不显式限制时,私聊发送可在所有群抽到,群聊发送仅可在本群抽到,默认频道为 global",
PrivateDataFolder: "driftbottle",
})
sea.DBPath = en.DataFolder() + "sea.db"
err := sea.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不知何处涌来一股暗流你失去了意识。")))
})
}

249
plugin/emojimix/emoji.go Normal file
View File

@@ -0,0 +1,249 @@
// Package emojimix 合成emoji
package emojimix
var emojis = map[rune]int64{
128516: 20201001, // 😄 grinning face with smiling eyes
128512: 20201001, // 😀 grinning face
128578: 20201001, // 🙂 slightly smiling face
128579: 20201001, // 🙃 upside-down face
128521: 20201001, // 😉 winking face
128522: 20201001, // 😊 smiling face with smiling eyes
128518: 20201001, // 😆 grinning squinting face
128515: 20201001, // 😃 grinning face with big eyes
128513: 20201001, // 😁 beaming face with smiling eyes
129315: 20201001, // 🤣 rolling on the floor laughing
128517: 20201001, // 😅 grinning face with sweat
128514: 20201001, // 😂 face with tears of joy
128519: 20201001, // 😇 smiling face with halo
129392: 20201001, // 🥰 smiling face with hearts
128525: 20201001, // 😍 smiling face with heart-eyes
128536: 20201001, // 😘 face blowing a kiss
129321: 20201001, // 🤩 star-struck
128535: 20201001, // 😗 kissing face
128538: 20201001, // 😚 kissing face with closed eyes
128537: 20201001, // 😙 kissing face with smiling eyes
128539: 20201001, // 😛 face with tongue
128541: 20201001, // 😝 squinting face with tongue
128523: 20201001, // 😋 face savoring food
129394: 20201001, // 🥲 smiling face with tear
129297: 20201001, // 🤑 money-mouth face
128540: 20201001, // 😜 winking face with tongue
129303: 20201001, // 🤗 smiling face with open hands hugs
129323: 20201001, // 🤫 shushing face quiet whisper
129300: 20201001, // 🤔 thinking face question hmmm
129325: 20201001, // 🤭 face with hand over mouth embarrassed
129320: 20201001, // 🤨 face with raised eyebrow question
129296: 20201001, // 🤐 zipper-mouth face
128528: 20201001, // 😐 neutral face
128529: 20201001, // 😑 expressionless face
128566: 20201001, // 😶 face without mouth
129322: 20201001, // 🤪 zany face
128527: 20201001, // 😏 smirking face suspicious
128530: 20201001, // 😒 unamused face
128580: 20201001, // 🙄 face with rolling eyes
128556: 20201001, // 😬 grimacing face
128558: 20210218, // 😮 face exhaling
129317: 20201001, // 🤥 lying face
128524: 20201001, // 😌 relieved face
128532: 20201001, // 😔 pensive face
128554: 20201001, // 😪 sleepy face
129316: 20201001, // 🤤 drooling face
128564: 20201001, // 😴 sleeping face
128567: 20201001, // 😷 face with medical mask
129298: 20201001, // 🤒 face with thermometer
129301: 20201001, // 🤕 face with head-bandage
129314: 20201001, // 🤢 nauseated face
129326: 20201001, // 🤮 face vomiting throw
129319: 20201001, // 🤧 sneezing face
129397: 20201001, // 🥵 hot face warm
129398: 20201001, // 🥶 cold face freezing ice
128565: 20201001, // 😵 face with crossed-out eyes
129396: 20201001, // 🥴 woozy face drunk tipsy drug high
129327: 20201001, // 🤯 exploding head mindblow
129312: 20201001, // 🤠 cowboy hat face
129395: 20201001, // 🥳 partying face
129400: 20201001, // 🥸 disguised face
129488: 20201001, // 🧐 face with monocle glasses
128526: 20201001, // 😎 smiling face with sunglasses
128533: 20201001, // 😕 confused face
128543: 20201001, // 😟 worried face
128577: 20201001, // 🙁 slightly frowning face
128559: 20201001, // 😯 hushed face
128562: 20201001, // 😲 astonished face
129299: 20201001, // 🤓 nerd face glasses
128563: 20201001, // 😳 flushed face
129402: 20201001, // 🥺 pleading face
128551: 20201001, // 😧 anguished face
128552: 20201001, // 😨 fearful face
128550: 20201001, // 😦 frowning face with open mouth
128560: 20201001, // 😰 anxious face with sweat
128549: 20201001, // 😥 sad but relieved face
128557: 20201001, // 😭 loudly crying face
128553: 20201001, // 😩 weary face
128546: 20201001, // 😢 crying face
128547: 20201001, // 😣 persevering face
128544: 20201001, // 😠 angry face
128531: 20201001, // 😓 downcast face with sweat
128534: 20201001, // 😖 confounded face
129324: 20201001, // 🤬 face with symbols on mouth
128542: 20201001, // 😞 disappointed face
128555: 20201001, // 😫 tired face
128548: 20201001, // 😤 face with steam from nose
129393: 20201001, // 🥱 yawning face
128169: 20201001, // 💩 pile of poo
128545: 20201001, // 😡 pouting face
128561: 20201001, // 😱 face screaming in fear
128127: 20201001, // 👿 angry face with horns
128128: 20201001, // 💀 skull
128125: 20201001, // 👽 alien
128520: 20201001, // 😈 smiling face with horns devil
129313: 20201001, // 🤡 clown face
128123: 20201001, // 👻 ghost
129302: 20201001, // 🤖 robot
128175: 20201001, // 💯 hundred points percent
128064: 20201001, // 👀 eyes
127801: 20201001, // 🌹 rose flower
127804: 20201001, // 🌼 blossom flower
127799: 20201001, // 🌷 tulip flower
127797: 20201001, // 🌵 cactus
127821: 20201001, // 🍍 pineapple
127874: 20201001, // 🎂 birthday cake
127751: 20210831, // 🌇 sunset
129473: 20201001, // 🧁 cupcake muffin
127911: 20210521, // 🎧 headphone earphone
127800: 20210218, // 🌸 cherry blossom flower
129440: 20201001, // 🦠 microbe germ bacteria virus covid corona
128144: 20201001, // 💐 bouquet flowers
127789: 20201001, // 🌭 hot dog food
128139: 20201001, // 💋 kiss mark lips
127875: 20201001, // 🎃 jack-o-lantern pumpkin
129472: 20201001, // 🧀 cheese wedge
9749: 20201001, // ☕ hot beverage coffee cup tea
127882: 20201001, // 🎊 confetti ball
127880: 20201001, // 🎈 balloon
9924: 20201001, // ⛄ snowman without snow
128142: 20201001, // 💎 gem stone crystal diamond
127794: 20201001, // 🌲 evergreen tree
129410: 20210218, // 🦂 scorpion
128584: 20201001, // 🙈 see-no-evil monkey
128148: 20201001, // 💔 broken heart
128140: 20201001, // 💌 love letter heart
128152: 20201001, // 💘 heart with arrow
128159: 20201001, // 💟 heart decoration
128158: 20201001, // 💞 revolving hearts
128147: 20201001, // 💓 beating heart
128149: 20201001, // 💕 two hearts
128151: 20201001, // 💗 growing heart
129505: 20201001, // 🧡 orange heart
128155: 20201001, // 💛 yellow heart
10084: 20210218, // ❤ mending heart
128156: 20201001, // 💜 purple heart
128154: 20201001, // 💚 green heart
128153: 20201001, // 💙 blue heart
129294: 20201001, // 🤎 brown heart
129293: 20201001, // 🤍 white heart
128420: 20201001, // 🖤 black heart
128150: 20201001, // 💖 sparkling heart
128157: 20201001, // 💝 heart with ribbon
127873: 20211115, // 🎁 wrapped-gift
129717: 20211115, // 🪵 wood
127942: 20211115, // 🏆 trophy
127838: 20210831, // 🍞 bread
128240: 20201001, // 📰 newspaper
128302: 20201001, // 🔮 crystal ball
128081: 20201001, // 👑 crown
128055: 20201001, // 🐷 pig face
129412: 20210831, // 🦄 unicorn
127771: 20201001, // 🌛 first quarter moon face
129420: 20201001, // 🦌 deer
129668: 20210521, // 🪄 magic wand
128171: 20201001, // 💫 dizzy
128049: 20201001, // 🐱 meow cat face
129409: 20201001, // 🦁 lion
128293: 20201001, // 🔥 fire
128038: 20210831, // 🐦 bird
129415: 20201001, // 🦇 bat
129417: 20210831, // 🦉 owl
127752: 20201001, // 🌈 rainbow
128053: 20201001, // 🐵 monkey face
128029: 20201001, // 🐝 honeybee bumblebee wasp
128034: 20201001, // 🐢 turtle
128025: 20201001, // 🐙 octopus
129433: 20201001, // 🦙 llama alpaca
128016: 20210831, // 🐐 goat
128060: 20201001, // 🐼 panda
128040: 20201001, // 🐨 koala
129445: 20201001, // 🦥 sloth
128059: 20210831, // 🐻 bear
128048: 20201001, // 🐰 rabbit face
129428: 20201001, // 🦔 hedgehog
128054: 20211115, // 🐶 dog puppy
128041: 20211115, // 🐩 poodle dog
129437: 20211115, // 🦝 raccoon
128039: 20211115, // 🐧 penguin
128012: 20210218, // 🐌 snail
128045: 20201001, // 🐭 mouse face rat
128031: 20210831, // 🐟 fish
127757: 20201001, // 🌍 globe showing Europe-Africa
127774: 20201001, // 🌞 sun with face
127775: 20201001, // 🌟 glowing star
11088: 20201001, // ⭐ star
127772: 20201001, // 🌜 last quarter moon face
129361: 20201001, // 🥑 avocado
127820: 20211115, // 🍌 banana
127827: 20210831, // 🍓 strawberry
127819: 20210521, // 🍋 lemon
127818: 20211115, // 🍊 tangerine orange
}
var qqface = map[int]rune{
0: 128558, // 😮 face exhaling
1: 128556, // 😬 grimacing face
2: 128525, // 😍 smiling face with heart-eyes
4: 128526, // 😎 smiling face with sunglasses
5: 128557, // 😭 loudly crying face
6: 129402, // 🥺 pleading face
7: 129296, // 🤐 zipper-mouth face
8: 128554, // 😪 sleepy face
11: 128545, // 😡 pouting face
12: 128539, // 😛 face with tongue
13: 128513, // 😁 beaming face with smiling eyes
14: 128578, // 🙂 slightly smiling face
15: 128577, // 🙁 slightly frowning face
16: 128526, // 😎 smiling face with sunglasses
19: 129326, // 🤮 face vomiting throw
20: 129325, // 🤭 face with hand over mouth embarrassed
21: 128522, // 😊 smiling face with smiling eyes
23: 128533, // 😕 confused face
24: 128523, // 😋 face savoring food
27: 128531, // 😓 downcast face with sweat
28: 128516, // 😄 grinning face with smiling eyes
31: 129324, // 🤬 face with symbols on mouth
32: 129300, // 🤔 thinking face question hmmm
33: 129323, // 🤫 shushing face quiet whisper
34: 128565, // 😵 face with crossed-out eyes
35: 128547, // 😣 persevering face
37: 128128, // 💀 skull
46: 128055, // 🐷 pig face
53: 127874, // 🎂 birthday cake
59: 128169, // 💩 pile of poo
60: 9749, // ☕ hot beverage coffee cup tea
63: 127801, // 🌹 rose flower
66: 10084, // ❤ mending heart
67: 128148, // 💔 broken heart
69: 127873, // 🎁 wrapped-gift
74: 127774, // 🌞 sun with face
75: 127772, // 🌜 last quarter moon face
96: 128517, // 😅 grinning face with sweat
104: 129393, // 🥱 yawning face
109: 128535, // 😗 kissing face
110: 128562, // 😲 astonished face
111: 129402, // 🥺 pleading face
172: 128539, // 😛 face with tongue
182: 128514, // 😂 face with tears of joy
187: 128123, // 👻 ghost
247: 128567, // 😷 face with medical mask
272: 128579, // 🙃 upside-down face
320: 129395, // 🥳 partying face
325: 128561, // 😱 face screaming in fear
}

101
plugin/emojimix/mix.go Normal file
View File

@@ -0,0 +1,101 @@
// Package emojimix 合成emoji
package emojimix
import (
"fmt"
"net/http"
"strconv"
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 bed = "https://www.gstatic.com/android/keyboard/emojikitchen/%d/u%x/u%x_u%x.png"
func init() {
control.Register("emojimix", &ctrl.Options[*zero.Ctx]{
DisableOnDefault: false,
Help: "合成emoji\n" +
"- [emoji][emoji]",
}).OnMessage(match).SetBlock(true).Limit(ctxext.LimitByUser).
Handle(func(ctx *zero.Ctx) {
r := ctx.State["emojimix"].([]rune)
logrus.Debugln("[emojimix] match:", r)
r1, r2 := r[0], r[1]
u1 := fmt.Sprintf(bed, emojis[r1], r1, r1, r2)
u2 := fmt.Sprintf(bed, emojis[r2], r2, r2, r1)
logrus.Debugln("[emojimix] u1:", u1)
logrus.Debugln("[emojimix] u2:", u2)
resp1, err := http.Head(u1)
if err == nil {
resp1.Body.Close()
if resp1.StatusCode == http.StatusOK {
ctx.SendChain(message.Image(u1))
return
}
}
resp2, err := http.Head(u2)
if err == nil {
resp2.Body.Close()
if resp2.StatusCode == http.StatusOK {
ctx.SendChain(message.Image(u2))
return
}
}
})
}
func match(ctx *zero.Ctx) bool {
logrus.Debugln("[emojimix] msg:", ctx.Event.Message)
if len(ctx.Event.Message) == 2 {
r1 := face2emoji(ctx.Event.Message[0])
if _, ok := emojis[r1]; !ok {
return false
}
r2 := face2emoji(ctx.Event.Message[1])
if _, ok := emojis[r2]; !ok {
return false
}
ctx.State["emojimix"] = []rune{r1, r2}
return true
}
r := []rune(ctx.Event.RawMessage)
logrus.Debugln("[emojimix] raw msg:", ctx.Event.RawMessage)
if len(r) == 2 {
if _, ok := emojis[r[0]]; !ok {
return false
}
if _, ok := emojis[r[1]]; !ok {
return false
}
ctx.State["emojimix"] = r
return true
}
return false
}
func face2emoji(face message.MessageSegment) rune {
if face.Type == "text" {
r := []rune(face.Data["text"])
if len(r) != 1 {
return 0
}
return r[0]
}
if face.Type != "face" {
return 0
}
id, err := strconv.Atoi(face.Data["id"])
if err != nil {
return 0
}
if r, ok := qqface[id]; ok {
return r
}
return 0
}

123
plugin/epidemic/epidemic.go Normal file
View File

@@ -0,0 +1,123 @@
// 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
}

44
plugin/font/main.go Normal file
View File

@@ -0,0 +1,44 @@
// Package font 渲染任意文字到图片
package font
import (
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"
zero "github.com/wdvxdr1123/ZeroBot"
"github.com/wdvxdr1123/ZeroBot/message"
)
func init() {
control.Register("font", &ctrl.Options[*zero.Ctx]{
DisableOnDefault: false,
Help: "渲染任意文字到图片\n- (用[终末体|终末变体|紫罗兰体|樱酥体|Consolas体|苹方体])渲染文字xxx",
}).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]
switch fnt {
case "用终末体":
fnt = text.SyumatuFontFile
case "用终末变体":
fnt = text.NisiFontFile
case "用紫罗兰体":
fnt = text.VioletEvergardenFontFile
case "用樱酥体":
fnt = text.SakuraFontFile
case "用Consolas体":
fnt = text.ConsolasFontFile
case "用苹方体":
fallthrough
default:
fnt = text.FontFile
}
b, err := text.RenderToBase64(txt, fnt, 400, 20)
if err != nil {
ctx.SendChain(message.Text("ERROR:", err))
return
}
ctx.SendChain(message.Image("base64://" + binary.BytesToString(b)))
})
}

248
plugin/fortune/fortune.go Normal file
View File

@@ -0,0 +1,248 @@
// Package fortune 每日运势
package fortune
import (
"archive/zip"
"crypto/md5"
"encoding/hex"
"encoding/json"
"image"
"io"
"os"
"strconv"
"github.com/Coloured-glaze/gg" // 注册了 jpg png gif
"github.com/sirupsen/logrus"
zero "github.com/wdvxdr1123/ZeroBot"
"github.com/wdvxdr1123/ZeroBot/message"
"github.com/wdvxdr1123/ZeroBot/utils/helper"
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 (
// 底图缓存位置
images = "data/Fortune/"
// 基础文件位置
omikujson = "data/Fortune/text.json"
// 字体文件位置
font = "data/Font/sakura.ttf"
// 生成图缓存位置
cache = images + "cache/"
)
var (
// 底图类型列表
table = [...]string{"车万", "DC4", "爱因斯坦", "星空列车", "樱云之恋", "富婆妹", "李清歌", "公主连结", "原神", "明日方舟", "碧蓝航线", "碧蓝幻想", "战双", "阴阳师", "赛马娘", "东方归言录", "奇异恩典", "夏日口袋", "ASoul"}
// 映射底图与 index
index = make(map[string]uint8)
// 签文
omikujis []map[string]string
)
func init() {
// 插件主体
en := control.Register("fortune", &ctrl.Options[*zero.Ctx]{
DisableOnDefault: false,
Help: "每日运势: \n" +
"- 运势 | 抽签\n" +
"- 设置底图[车万 | DC4 | 爱因斯坦 | 星空列车 | 樱云之恋 | 富婆妹 | 李清歌 | 公主连结 | 原神 | 明日方舟 | 碧蓝航线 | 碧蓝幻想 | 战双 | 阴阳师 | 赛马娘 | 东方归言录 | 奇异恩典 | 夏日口袋 | ASoul]",
PublicDataFolder: "Fortune",
})
_ = os.RemoveAll(cache)
err := os.MkdirAll(cache, 0755)
if err != nil {
panic(err)
}
for i, s := range table {
index[s] = uint8(i)
}
en.OnRegex(`^设置底图\s?(.*)`).SetBlock(true).
Handle(func(ctx *zero.Ctx) {
gid := ctx.Event.GroupID
if gid <= 0 {
// 个人用户设为负数
gid = -ctx.Event.UserID
}
i, ok := index[ctx.State["regex_matched"].([]string)[1]]
if ok {
c, ok := ctx.State["manager"].(*ctrl.Control[*zero.Ctx])
if ok {
err := c.SetData(gid, int64(i)&0xff)
if err != nil {
ctx.SendChain(message.Text("设置失败:", err))
return
}
ctx.SendChain(message.Text("设置成功~"))
return
}
ctx.SendChain(message.Text("设置失败: 找不到插件"))
return
}
ctx.SendChain(message.Text("没有这个底图哦~"))
})
en.OnFullMatchGroup([]string{"运势", "抽签"}, ctxext.DoOnceOnSuccess(
func(ctx *zero.Ctx) bool {
data, err := file.GetLazyData(omikujson, false)
if err != nil {
ctx.SendChain(message.Text("ERROR:", err))
return false
}
err = json.Unmarshal(data, &omikujis)
if err != nil {
ctx.SendChain(message.Text("ERROR:", err))
return false
}
_, err = file.GetLazyData(font, true)
if err != nil {
ctx.SendChain(message.Text("ERROR:", err))
return false
}
return true
},
)).Limit(ctxext.LimitByGroup).SetBlock(true).
Handle(func(ctx *zero.Ctx) {
// 获取该群背景类型,默认车万
kind := "车万"
gid := ctx.Event.GroupID
if gid <= 0 {
// 个人用户设为负数
gid = -ctx.Event.UserID
}
logrus.Debugln("[fortune]gid:", ctx.Event.GroupID, "uid:", ctx.Event.UserID)
c, ok := ctx.State["manager"].(*ctrl.Control[*zero.Ctx])
if ok {
v := uint8(c.GetData(gid) & 0xff)
if int(v) < len(table) {
kind = table[v]
}
}
// 检查背景图片是否存在
zipfile := images + kind + ".zip"
_, err := file.GetLazyData(zipfile, false)
if err != nil {
ctx.SendChain(message.Text("ERROR:", err))
return
}
// 随机获取背景
background, index, err := randimage(zipfile, ctx)
if err != nil {
ctx.SendChain(message.Text("ERROR:", err))
return
}
// 随机获取签文
randtextindex := ctxext.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[:])
err = pool.SendImageFromPool(cachefile, cachefile, func() error {
f, err := os.Create(cachefile)
if err != nil {
return err
}
_, err = draw(background, title, text, f)
_ = f.Close()
return err
}, ctxext.Send(ctx), ctxext.GetMessage(ctx))
if err != nil {
ctx.SendChain(message.Text("ERROR:", err))
return
}
})
}
// @function randimage 随机选取zip内的文件
// @param path zip路径
// @param ctx *zero.Ctx
// @return 文件路径 & 错误信息
func randimage(path string, ctx *zero.Ctx) (im image.Image, index int, err error) {
reader, err := zip.OpenReader(path)
if err != nil {
return
}
defer reader.Close()
file := reader.File[ctxext.RandSenderPerDayN(ctx.Event.UserID, len(reader.File))]
f, err := file.Open()
if err != nil {
return
}
defer f.Close()
im, _, err = image.Decode(f)
return
}
// @function draw 绘制运势图
// @param background 背景图片路径
// @param seed 随机数种子
// @param title 签名
// @param text 签文
// @return 错误信息
func draw(back image.Image, 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 {
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 {
return -1, err
}
tw, th := canvas.MeasureString("测")
tw, th = tw+10, th+10
r := []rune(txt)
xsum := rowsnum(len(r), 9)
switch xsum {
default:
for i, o := range r {
xnow := rowsnum(i+1, 9)
ysum := math.Min(len(r)-(xnow-1)*9, 9)
ynow := i%9 + 1
canvas.DrawString(string(o), -offest(xsum, xnow, tw)+115, offest(ysum, ynow, th)+320.0)
}
case 2:
div := rowsnum(len(r), 2)
for i, o := range r {
xnow := rowsnum(i+1, div)
ysum := math.Min(len(r)-(xnow-1)*div, div)
ynow := i%div + 1
switch xnow {
case 1:
canvas.DrawString(string(o), -offest(xsum, xnow, tw)+115, offest(9, ynow, th)+320.0)
case 2:
canvas.DrawString(string(o), -offest(xsum, xnow, tw)+115, offest(9, ynow+(9-ysum), th)+320.0)
}
}
}
return writer.WriteTo(canvas.Image(), f)
}
func offest(total, now int, distance float64) float64 {
if total%2 == 0 {
return (float64(now-total/2) - 1) * distance
}
return (float64(now-total/2) - 1.5) * distance
}
func rowsnum(total, div int) int {
temp := total / div
if total%div != 0 {
temp++
}
return temp
}

68
plugin/funny/laugh.go Normal file
View File

@@ -0,0 +1,68 @@
// Package funny 冷笑话
package funny
import (
"strings"
"time"
"github.com/sirupsen/logrus"
zero "github.com/wdvxdr1123/ZeroBot"
"github.com/wdvxdr1123/ZeroBot/message"
sql "github.com/FloatTech/sqlite"
ctrl "github.com/FloatTech/zbpctrl"
"github.com/FloatTech/zbputils/control"
"github.com/FloatTech/zbputils/ctxext"
)
type joke struct {
ID uint32 `db:"id"`
Text string `db:"text"`
}
var db = &sql.Sqlite{}
func init() {
en := control.Register("funny", &ctrl.Options[*zero.Ctx]{
DisableOnDefault: false,
Help: "讲个笑话\n" +
"- 讲个笑话[@xxx|qq号|人名] | 夸夸[@xxx|qq号|人名] ",
PublicDataFolder: "Funny",
})
en.OnPrefixGroup([]string{"讲个笑话", "夸夸"}, ctxext.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))
return false
}
err = db.Open(time.Hour * 24)
if err != nil {
ctx.SendChain(message.Text("ERROR:", err))
return false
}
err = db.Create("jokes", &joke{})
if err != nil {
ctx.SendChain(message.Text("ERROR:", err))
return false
}
c, err := db.Count("jokes")
if err != nil {
ctx.SendChain(message.Text("ERROR:", err))
return false
}
logrus.Infoln("[funny]加载", c, "个笑话")
return true
})).SetBlock(true).Limit(ctxext.LimitByUser).Handle(func(ctx *zero.Ctx) {
// 获取名字
name := ctx.NickName()
var j joke
err := db.Pick("jokes", &j)
if err != nil {
ctx.SendChain(message.Text("ERROR:", err))
return
}
ctx.SendChain(message.Text(strings.ReplaceAll(j.Text, "%name", name)))
})
}

16
plugin/genshin/data.go Normal file
View File

@@ -0,0 +1,16 @@
package genshin
type storage uint64
func (s *storage) is5starsmode() bool {
return *s&1 == 1
}
func (s *storage) setmode(is5stars bool) bool {
if is5stars {
*s |= 1
} else {
*s &= 0xffffffff_fffffffe
}
return is5stars
}

375
plugin/genshin/ys.go Normal file
View File

@@ -0,0 +1,375 @@
// Package genshin 原神抽卡
package genshin
import (
"archive/zip"
"image"
"image/color"
"image/draw"
"image/jpeg"
"image/png"
"math/rand"
"regexp"
"strings"
"sync/atomic"
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"
"github.com/wdvxdr1123/ZeroBot/message"
)
type zipfilestructure map[string][]*zip.File
var (
totl uint64 // 累计抽奖次数
filetree = make(zipfilestructure, 32)
starN3, starN4, starN5 *zip.File
namereg = regexp.MustCompile(`_(.*)\.png`)
)
func init() {
engine := control.Register("genshin", &ctrl.Options[*zero.Ctx]{
DisableOnDefault: false,
Help: "原神抽卡\n- 原神十连\n- 切换原神卡池",
PublicDataFolder: "Genshin",
}).ApplySingle(ctxext.DefaultSingle)
engine.OnFullMatch("切换原神卡池").SetBlock(true).Limit(ctxext.LimitByUser).
Handle(func(ctx *zero.Ctx) {
c, ok := ctx.State["manager"].(*ctrl.Control[*zero.Ctx])
if !ok {
ctx.SendChain(message.Text("找不到服务!"))
return
}
gid := ctx.Event.GroupID
if gid == 0 {
gid = -ctx.Event.UserID
}
store := (storage)(c.GetData(gid))
if store.setmode(!store.is5starsmode()) {
process.SleepAbout1sTo2s()
ctx.SendChain(message.Text("切换到五星卡池~"))
} else {
process.SleepAbout1sTo2s()
ctx.SendChain(message.Text("切换到普通卡池~"))
}
err := c.SetData(gid, int64(store))
if err != nil {
process.SleepAbout1sTo2s()
ctx.SendChain(message.Text("ERROR:", err))
}
})
engine.OnFullMatch("原神十连", ctxext.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))
return false
}
err = parsezip(zipfile)
if err != nil {
ctx.SendChain(message.Text("ERROR:", err))
return false
}
return true
},
)).SetBlock(true).Limit(ctxext.LimitByUser).
Handle(func(ctx *zero.Ctx) {
c, ok := ctx.State["manager"].(*ctrl.Control[*zero.Ctx])
if !ok {
ctx.SendChain(message.Text("找不到服务!"))
return
}
gid := ctx.Event.GroupID
if gid == 0 {
gid = -ctx.Event.UserID
}
store := (storage)(c.GetData(gid))
img, str, mode, err := randnums(10, store)
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)))
} else {
ctx.Send(message.ReplyWithMessage(ctx.Event.MessageID,
message.Text("十连成功~"), message.ImageBytes(b)))
}
cl()
})
}
func randnums(nums int, store storage) (rgba *image.RGBA, str string, replyMode bool, err error) {
var (
fours, fives = make([]*zip.File, 0, 10), make([]*zip.File, 0, 10) // 抽到 四, 五星角色
threeArms, fourArms, fiveArms = make([]*zip.File, 0, 10), make([]*zip.File, 0, 10), make([]*zip.File, 0, 10) // 抽到 三 , 四, 五星武器
fourN, fiveN = 0, 0 // 抽到 四, 五星角色的数量
bgs = make([]*zip.File, 0, 10) // 背景图片名
threeN2, fourN2, fiveN2 = 0, 0, 0 // 抽到 三 , 四, 五星武器的数量
hero, stars = make([]*zip.File, 0, 10), make([]*zip.File, 0, 10) // 角色武器名, 储存星级图标
cicon = make([]*zip.File, 0, 10) // 元素图标
fivebg, fourbg, threebg = filetree["five_bg.jpg"][0], filetree["four_bg.jpg"][0], filetree["three_bg.jpg"][0] // 背景图片名
fivelen = len(filetree["five"])
five2len = len(filetree["five2"])
threelen = len(filetree["Three"])
fourlen = len(filetree["four"])
four2len = len(filetree["four2"])
)
if totl%9 == 0 { // 累计9次加入一个五星
switch rand.Intn(2) {
case 0:
fiveN++
fives = append(fives, filetree["five"][rand.Intn(fivelen)])
case 1:
fiveN2++
fiveArms = append(fiveArms, filetree["five2"][rand.Intn(five2len)])
}
nums--
}
if store.is5starsmode() { // 5星模式
for i := 0; i < nums; i++ {
switch rand.Intn(2) {
case 0:
fiveN++
fives = append(fives, filetree["five"][rand.Intn(fivelen)])
case 1:
fiveN2++
fiveArms = append(fiveArms, filetree["five2"][rand.Intn(five2len)])
}
}
} else { // 默认模式
for i := 0; i < nums; i++ {
a := rand.Intn(1000) // 抽卡几率 三星80% 四星17% 五星3%
switch {
case a >= 0 && a <= 800:
threeN2++
threeArms = append(threeArms, filetree["Three"][rand.Intn(threelen)])
case a > 800 && a <= 885:
fourN++
fours = append(fours, filetree["four"][rand.Intn(fourlen)]) // 随机角色
case a > 885 && a <= 970:
fourN2++
fourArms = append(fourArms, filetree["four2"][rand.Intn(four2len)]) // 随机武器
case a > 970 && a <= 985:
fiveN++
fives = append(fives, filetree["five"][rand.Intn(fivelen)])
default:
fiveN2++
fiveArms = append(fiveArms, filetree["five2"][rand.Intn(five2len)])
}
}
if fourN+fourN2 == 0 && threeN2 > 0 { // 没有四星时自动加入
threeN2--
threeArms = threeArms[:len(threeArms)-1]
switch rand.Intn(2) {
case 0:
fourN++
fours = append(fours, filetree["four"][rand.Intn(fourlen)]) // 随机角色
case 1:
fourN2++
fourArms = append(fourArms, filetree["four2"][rand.Intn(four2len)]) // 随机武器
}
}
_ = atomic.AddUint64(&totl, 1)
}
icon := func(f *zip.File) *zip.File {
name := f.Name
name = name[strings.LastIndex(name, "/")+1:strings.Index(name, "_")] + ".png"
logrus.Debugln("[genshin]get named file", name)
return filetree[name][0]
}
he := func(cnt int, id int, f *zip.File, bg *zip.File) {
var hen *[]*zip.File
for i := 0; i < cnt; i++ {
switch id {
case 1:
hen = &threeArms
case 2:
hen = &fourArms
case 3:
hen = &fours
case 4:
hen = &fiveArms
case 5:
hen = &fives
}
bgs = append(bgs, bg) // 加入颜色背景
hero = append(hero, (*hen)[i])
stars = append(stars, f) // 加入星级图标
cicon = append(cicon, icon((*hen)[i])) // 加入元素图标
}
}
if fiveN > 0 { // 按顺序加入
he(fiveN, 5, starN5, fivebg) // 五星角色
str += reply(fives, 1, str)
replyMode = true
}
if fourN > 0 {
he(fourN, 3, starN4, fourbg) // 四星角色
}
if fiveN2 > 0 {
he(fiveN2, 4, starN5, fivebg) // 五星武器
str += reply(fiveArms, 2, str)
replyMode = true
}
if fourN2 > 0 {
he(fourN2, 2, starN4, fourbg) // 四星武器
}
if threeN2 > 0 {
he(threeN2, 1, starN3, threebg) // 三星武器
}
var c1, c2, c3 uint8 = 50, 50, 50 // 背景颜色
img00, err := filetree["bg0.jpg"][0].Open() // 打开背景图片
if err != nil {
return
}
rectangle := image.Rect(0, 0, 1920, 1080) // 图片宽度, 图片高度
rgba = image.NewRGBA(rectangle)
draw.Draw(rgba, rgba.Bounds(), image.NewUniform(color.RGBA{c1, c2, c3, 255}), image.Point{}, draw.Over)
context := freetype.NewContext() // 创建一个新的上下文
context.SetDPI(72) // 每英寸 dpi
context.SetClip(rgba.Bounds())
context.SetDst(rgba)
defer img00.Close()
img0, err := jpeg.Decode(img00) // 读取一个本地图像
if err != nil {
return
}
offset := image.Pt(0, 0) // 图片在背景上的位置
draw.Draw(rgba, img0.Bounds().Add(offset), img0, image.Point{}, draw.Over)
w1, h1 := 230, 0
for i := 0; i < len(hero); i++ {
if i > 0 {
w1 += 146 // 图片宽度
}
imgs, err := bgs[i].Open() // 取出背景图片
if err != nil {
return nil, "", false, err
}
defer imgs.Close()
img, _ := jpeg.Decode(imgs)
offset := image.Pt(w1, h1)
draw.Draw(rgba, img.Bounds().Add(offset), img, image.Point{}, draw.Over)
imgs1, err := hero[i].Open() // 取出图片名
if err != nil {
return nil, "", false, err
}
defer imgs1.Close()
img1, _ := png.Decode(imgs1)
offset1 := image.Pt(w1, h1)
draw.Draw(rgba, img1.Bounds().Add(offset1), img1, image.Point{}, draw.Over)
imgs2, err := stars[i].Open() // 取出星级图标
if err != nil {
return nil, "", false, err
}
defer imgs2.Close()
img2, _ := png.Decode(imgs2)
offset2 := image.Pt(w1, h1)
draw.Draw(rgba, img2.Bounds().Add(offset2), img2, image.Point{}, draw.Over)
imgs3, err := cicon[i].Open() // 取出类型图标
if err != nil {
return nil, "", false, err
}
defer imgs3.Close()
img3, _ := png.Decode(imgs3)
offset3 := image.Pt(w1, h1)
draw.Draw(rgba, img3.Bounds().Add(offset3), img3, image.Point{}, draw.Over)
}
imgs4, err := filetree["Reply.png"][0].Open() // "分享" 图标
if err != nil {
return nil, "", false, err
}
defer imgs4.Close()
img4, err := png.Decode(imgs4)
if err != nil {
return nil, "", false, err
}
offset4 := image.Pt(1270, 945) // 宽, 高
draw.Draw(rgba, img4.Bounds().Add(offset4), img4, image.Point{}, draw.Over)
return
}
func parsezip(zipFile string) error {
zipReader, err := zip.OpenReader(zipFile) // will not close
if err != nil {
return err
}
for _, f := range zipReader.File {
if f.FileInfo().IsDir() {
filetree[f.Name] = make([]*zip.File, 0, 32)
continue
}
f.Name = f.Name[8:]
i := strings.LastIndex(f.Name, "/")
if i < 0 {
filetree[f.Name] = []*zip.File{f}
logrus.Debugln("[genshin]insert file", f.Name)
continue
}
folder := f.Name[:i]
if folder != "" {
filetree[folder] = append(filetree[folder], f)
logrus.Debugln("[genshin]insert file into", folder)
if folder == "gacha" {
switch f.Name[i+1:] {
case "ThreeStar.png":
starN3 = f
case "FourStar.png":
starN4 = f
case "FiveStar.png":
starN5 = f
}
}
}
}
return nil
}
// 取出角色武器名
func reply(z []*zip.File, num int, nameStr string) string {
var tmp strings.Builder
tmp.Grow(128)
switch {
case num == 1:
tmp.WriteString("★五星角色★\n")
case num == 2 && len(nameStr) > 0:
tmp.WriteString("\n★五星武器★\n")
default:
tmp.WriteString("★五星武器★\n")
}
for i := range z {
tmp.WriteString(namereg.FindStringSubmatch(z[i].Name)[1] + " * ")
}
return tmp.String()
}

103
plugin/gif/README.md Normal file
View File

@@ -0,0 +1,103 @@
# ZeroBot-Plugin-Gif
[ZeroBot QQ机器人](https://github.com/wdvxdr1123/ZeroBot)插件可以制作各种沙雕gif图
> 素材包地址: https://gitcode.net/anto_july/imagematerials
## 触发方式
1. [指令词]+[qq号] 如爬123456
2. [指令词]+[图片] 如:爬[图片]
3. [指令词]+[艾特] 如:爬@小H
## 指令列表
- [x]
- [x]
- [x]
- [x]
- [x]
- [x]
- [x]
- [x]
- [x]
- [x]
- [x]
- [x] 灰度
- [x] 上翻
- [x] 下翻
- [x] 左翻
- [x] 右翻
- [x] 反色
- [x] 倒放
- [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] 抬棺

85
plugin/gif/context.go Normal file
View File

@@ -0,0 +1,85 @@
package gif
import (
"os"
"strconv"
"sync"
"github.com/FloatTech/zbputils/file"
"github.com/FloatTech/zbputils/img"
"github.com/sirupsen/logrus"
)
type context struct {
usrdir string
headimgsdir []string
}
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/m0_60838134/imagematerials/-/raw/main/`+name, target, true)
if err != nil {
exit(err)
return
}
logrus.Debugln("[gif] dl", name, "to", target, "succeeded")
} else {
logrus.Debugln("[gif] dl", name, "exists at", target)
}
*s = target
}
func dlblock(name string) (string, error) {
target := datapath + `materials/` + name
if file.IsNotExist(target) {
err := file.DownloadTo(`https://gitcode.net/m0_60838134/imagematerials/-/raw/main/`+name, target, true)
if err != nil {
return "", err
}
logrus.Debugln("[gif] dl", name, "to", target, "succeeded")
} else {
logrus.Debugln("[gif] dl", name, "exists at", target)
}
return target, nil
}
func dlrange(prefix string, end int, wg *sync.WaitGroup, exit func(error)) []string {
if file.IsNotExist(datapath + `materials/` + prefix) {
err := os.MkdirAll(datapath+`materials/`+prefix, 0755)
if err != nil {
exit(err)
return nil
}
}
c := make([]string, end)
for i := range c {
wg.Add(1)
go dlchan(prefix+"/"+strconv.Itoa(i)+".png", &c[i], wg, exit)
}
return c
}
// 新的上下文
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 loadFirstFrames(paths []string, size int) (imgs []*img.Factory, err error) {
imgs = make([]*img.Factory, size)
for i := range imgs {
imgs[i], err = img.LoadFirstFrame(paths[i], 0, 0)
if err != nil {
return nil, err
}
}
return imgs, nil
}

1450
plugin/gif/gif.go Normal file

File diff suppressed because it is too large Load Diff

41
plugin/gif/logo.go Normal file
View File

@@ -0,0 +1,41 @@
package gif
import (
"image"
"strconv"
"strings"
"github.com/FloatTech/zbputils/file"
"github.com/FloatTech/zbputils/img"
)
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)
} else {
err = file.DownloadTo("http://q4.qlogo.cn/g?b=qq&nk="+v+"&s=640", cc.usrdir+strconv.Itoa(i)+".gif", true)
}
if err != nil {
return err
}
}
return nil
}
func (cc *context) getLogo(w int, h int) (*image.NRGBA, error) {
frame, err := img.LoadFirstFrame(cc.headimgsdir[0], w, h)
if err != nil {
return nil, err
}
return frame.Circle(0).Im, nil
}
func (cc *context) getLogo2(w int, h int) (*image.NRGBA, error) {
frame, err := img.LoadFirstFrame(cc.headimgsdir[1], w, h)
if err != nil {
return nil, err
}
return frame.Circle(0).Im, nil
}

1523
plugin/gif/png.go Normal file

File diff suppressed because it is too large Load Diff

164
plugin/gif/run.go Normal file
View File

@@ -0,0 +1,164 @@
// Package gif 制图
package gif
import (
"strconv"
"strings"
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 (
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,
}
)
func init() { // 插件主体
for k := range cmdMap {
cmd = append(cmd, k)
}
en := control.Register("gif", &ctrl.Options[*zero.Ctx]{
DisableOnDefault: false,
Help: "GIF制图命令后艾特群友/QQ号/一张图方可触发命令\n其中XXX可以为任何文字可以不写\n对机器人操作请先艾特机器人再执行命令\n" +
"- 搓|-冲|-摸|-拍|-丢|-吃|-敲|-啃|-蹭|-爬|-撕|-灰度|-上翻|-下翻\n" +
"- 左翻|-右翻|-反色|-浮雕|- 打码|- 负片|- 旋转|- 变形|- 亲\n" +
"- 结婚申请|结婚登记|- 阿尼亚喜欢XXX|- 像只|- 我永远喜欢XXX\n" +
"- 像样的亲亲|- 国旗|- 不要靠近|- 万能表情|-空白表情|- 采访\n" +
"- 需要|-你可能需要|- 这像画吗|- 小画家|- 完美|- 玩游戏|- 出警\n" +
"- 警察|- 舔|舔屏|prpr|- 安全感|- 精神支柱|- 想什么|- 墙纸\n" +
"- 为什么at我|- 交个朋友|- 打工人|-继续干活|- 兑换券|- 炖\n" +
"- 垃圾桶|- 垃圾|- 捶|- 啾啾|- 2敲|- 听音乐|- 永远爱你|- 2拍\n" +
"- 顶|- 捣|- 打拳|- 滚|- 吸|- 嗦|- 扔|- 锤|- 紧贴|紧紧贴着|- 转\n" +
"- 抬棺|- 远离|- 我老婆|- 小天使XXX|- 你的XXX|- 不要看\n" +
"- 玩一下XXX|- 给我变|- 揍|- 吞|- 膜拜|- 诶嘿|- 2蹭|- 你犯法了\n" +
"- 砰|- 注意力涣散|- 蒙蔽|- 踩|- 好玩|- 2转|- 踢球|- 2舔|\n" +
"- 可莉吃|- 胡桃啃|- 怀",
PrivateDataFolder: "gif",
}).ApplySingle(ctxext.DefaultSingle)
datapath = file.BOTPATH + "/" + en.DataFolder()
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))
return
}
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))
return
}
ctx.SendChain(message.Image(picurl))
})
}

View File

@@ -4,26 +4,26 @@ package github
import (
"errors"
"fmt"
"io/ioutil"
"io"
"net/http"
"net/url"
"strings"
ctrl "github.com/FloatTech/zbpctrl"
"github.com/FloatTech/zbputils/control"
zero "github.com/wdvxdr1123/ZeroBot"
"github.com/wdvxdr1123/ZeroBot/message"
"github.com/FloatTech/ZeroBot-Plugin/control"
"github.com/tidwall/gjson"
)
func init() { // 插件主体
control.Register("github", &control.Options{
control.Register("github", &ctrl.Options[*zero.Ctx]{
DisableOnDefault: false,
Help: "GitHub仓库搜索\n" +
"- >github [xxx]\n" +
"- >github -p [xxx]",
}).OnRegex(`^>github\s(-.{1,10}? )?(.*)$`).SetBlock(true).FirstPriority().
}).OnRegex(`^>github\s(-.{1,10}? )?(.*)$`).SetBlock(true).
Handle(func(ctx *zero.Ctx) {
// 发送请求
header := http.Header{
@@ -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: ",
@@ -96,10 +96,10 @@ func init() { // 插件主体
}
// notnull 如果传入文本为空,则返回默认值
//nolint: unparam
func notnull(text, defstr string) string {
func notnull(text string) string {
if text == "" {
return defstr
return "None"
}
return text
}
@@ -118,7 +118,7 @@ func netGet(dest string, header http.Header) ([]byte, error) {
return nil, err
}
defer resp.Body.Close()
body, err := ioutil.ReadAll(resp.Body)
body, err := io.ReadAll(resp.Body)
if err != nil {
return nil, err
}

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

@@ -0,0 +1,802 @@
// Package guessmusic 基于zbp的猜歌插件
package guessmusic
import (
"bytes"
"encoding/json"
"io/fs"
"io/ioutil"
"math/rand"
"net/http"
"net/url"
"os"
"os/exec"
"strconv"
"strings"
"time"
"github.com/sirupsen/logrus"
"github.com/wdvxdr1123/ZeroBot/utils/helper"
"github.com/pkg/errors"
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/file"
"github.com/FloatTech/zbputils/web"
"github.com/wdvxdr1123/ZeroBot/extension/single"
// 图片输出
"github.com/FloatTech/zbputils/img/text"
)
const (
ua = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/87.0.4280.88 Safari/537.36 Edg/87.0.664.66"
)
var (
catlist = make(map[string]int64, 100)
filelist []string
musictypelist = "mp3;MP3;wav;WAV;amr;AMR;3gp;3GP;3gpp;3GPP;acc;ACC"
cuttime = [...]string{"00:00:05", "00:00:30", "00:01:00"} // 音乐切割时间点,可自行调节时间(时:分:秒)
cfg config
)
func init() { // 插件主体
engine := control.Register("guessmusic", &ctrl.Options[*zero.Ctx]{
DisableOnDefault: false,
Help: "猜歌插件该插件依赖ffmpeg\n" +
"------bot主人指令------\n" +
"- 设置猜歌缓存歌库路径 [绝对路径]\n" +
"- 设置猜歌[本地/Api] [true/false]\n" +
"- 登录网易云\n" +
"- 添加歌单 [网易云歌单ID] [歌单名称]\n" +
"- 删除歌单 [网易云歌单ID/API歌单名称]\n" +
"注:\n1.不登陆也能用API有几率返回400\n" +
"2.[歌单名称]可为空,默认原标题\n" +
"------公 用 指 令------\n" +
"- 获取歌单列表\n" +
"- [网易云歌单ID/API歌单名称]歌单信息\n" +
"- [个人/团队]猜歌\n" +
"注默认歌库为网易云ACG动画榜\n" +
"可在后面添加[-歌单名称]进行指定歌单猜歌\n" +
"歌单的歌曲命名规则为:\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.Send(
message.ReplyWithMessage(ctx.Event.MessageID,
message.Text("已经有正在进行的游戏..."),
),
)
}),
))
cachePath := engine.DataFolder() + "cache/"
err := os.MkdirAll(cachePath, 0755)
if err != nil {
panic(err)
}
cfgFile := engine.DataFolder() + "config.json"
if file.IsExist(cfgFile) {
reader, err := os.Open(cfgFile)
if err == nil {
err = json.NewDecoder(reader).Decode(&cfg)
if err != nil {
panic(err)
}
} else {
panic(err)
}
err = reader.Close()
if err != nil {
panic(err)
}
} else {
var plist = []listRaw{
{
Name: "动画榜",
ID: 3001835560,
},
}
cfg = config{ // 默认 config
MusicPath: file.BOTPATH + "/data/guessmusic/music/", // 绝对路径,歌库根目录,通过指令进行更改
Local: true, // 是否使用本地音乐库
API: true, // 是否使用 Api
Cookie: "",
Playlist: plist,
}
err = saveConfig(cfgFile)
if err != nil {
panic(err)
}
}
err = getcatlist(cfg.MusicPath)
if err != nil {
logrus.Infof("[guessmusic2]无法获取歌单列表,[error]%s", err)
}
engine.OnRegex(`^设置猜歌(缓存歌库路径|本地|Api)\s*(.*)$`, func(ctx *zero.Ctx) bool {
if !zero.SuperUserPermission(ctx) {
ctx.SendChain(message.Text("只有bot主人可以设置"))
return false
}
return true
}).SetBlock(true).
Handle(func(ctx *zero.Ctx) {
option := ctx.State["regex_matched"].([]string)[1]
value := ctx.State["regex_matched"].([]string)[2]
switch option {
case "缓存歌库路径":
if value == "" {
ctx.SendChain(message.Text("请输入正确的路径!"))
return
}
musicPath := strings.ReplaceAll(value, "\\", "/")
if !strings.HasSuffix(musicPath, "/") {
musicPath += "/"
}
err = os.MkdirAll(cfg.MusicPath, 0755)
if err != nil {
ctx.SendChain(message.Text("[生成文件夹错误]ERROR:", err))
return
}
cfg.MusicPath = musicPath
case "本地":
choice, err := strconv.ParseBool(value)
if err != nil {
ctx.SendChain(message.Text("ERROR:", err))
return
}
cfg.Local = choice
case "Api":
choice, err := strconv.ParseBool(value)
if err != nil {
ctx.SendChain(message.Text("ERROR:", err))
return
}
cfg.API = choice
}
err = saveConfig(cfgFile)
if err == nil {
ctx.SendChain(message.Text("成功!"))
} else {
ctx.SendChain(message.Text("ERROR:", 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 := "https://music.cyrilstudio.top/login/qr/key"
data, err := web.GetData(keyURL)
if err != nil {
ctx.SendChain(message.Text("获取网易云key失败, ERROR:", err))
return
}
var keyInfo keyInfo
err = json.Unmarshal(data, &keyInfo)
if err != nil {
ctx.SendChain(message.Text("解析网易云key失败, ERROR:", err))
return
}
qrURL := "https://music.cyrilstudio.top/login/qr/create?key=" + keyInfo.Data.Unikey + "&qrimg=1"
data, err = web.GetData(qrURL)
if err != nil {
ctx.SendChain(message.Text("获取网易云二维码失败, ERROR:", err))
return
}
var qrInfo qrInfo
err = json.Unmarshal(data, &qrInfo)
if err != nil {
ctx.SendChain(message.Text("解析网易云二维码失败, ERROR:", 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 := "https://music.cyrilstudio.top/login/qr/check?key=" + url.QueryEscape(keyInfo.Data.Unikey)
referer := "https://music.cyrilstudio.top"
data, err := web.RequestDataWith(web.NewDefaultClient(), apiURL, "GET", referer, ua)
if err != nil {
ctx.SendChain(message.Text("无法获取登录状态, ERROR:", err))
return
}
var cookiesInfo cookyInfo
err = json.Unmarshal(data, &cookiesInfo)
if err != nil {
ctx.SendChain(message.Text("解析登录状态失败, ERROR:", 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("ERROR:", 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?(\d+)(\s(.*))?$`, zero.SuperUserPermission).SetBlock(true).Limit(ctxext.LimitByGroup).
Handle(func(ctx *zero.Ctx) {
listID := ctx.State["regex_matched"].([]string)[1]
listName := ctx.State["regex_matched"].([]string)[3]
ctx.SendChain(message.Text("正在校验歌单信息,请稍等"))
// 是否存在该歌单
apiURL := "https://music.cyrilstudio.top/playlist/detail?id=" + listID + "&cookie=" + cfg.Cookie
referer := "https://music.cyrilstudio.top"
data, err := web.RequestDataWith(web.NewDefaultClient(), apiURL, "GET", referer, ua)
if err != nil {
ctx.SendChain(message.Text("无法连接歌单,[error]", err))
return
}
var parsed topList
err = json.Unmarshal(data, &parsed)
if err != nil {
ctx.SendChain(message.Text("无法解析歌单ID内容,[error]", err))
return
}
// 是否有权限访问歌单列表内容
apiURL = "https://music.cyrilstudio.top/playlist/track/all?id=" + listID + "&cookie=" + cfg.Cookie
referer = "https://music.163.com/"
data, err = web.RequestDataWith(web.NewDefaultClient(), apiURL, "GET", referer, ua)
if err != nil {
ctx.SendChain(message.Text("无法获取歌单列表\n ERROR:", err))
return
}
var musiclist topMusicInfo
err = json.Unmarshal(data, &musiclist)
if err != nil {
ctx.SendChain(message.Text("你的cookie在API中无权访问该歌单\n该歌单有可能是用户私人歌单"))
return
}
// 获取列表名字
if listName == "" {
listName = parsed.Playlist.Name
}
playID, _ := strconv.ParseInt(listID, 10, 64)
catlist[listName] = playID
cfg.Playlist = append(cfg.Playlist, listRaw{
Name: listName,
ID: playID,
})
err = saveConfig(cfgFile)
if err == nil {
ctx.SendChain(message.Text("成功!"))
} else {
ctx.SendChain(message.Text("ERROR:", err))
}
})
engine.OnRegex(`^删除歌单\s?(.*)$`, zero.SuperUserPermission).SetBlock(true).Limit(ctxext.LimitByGroup).
Handle(func(ctx *zero.Ctx) {
delList := ctx.State["regex_matched"].([]string)[1]
var playlist []listRaw
var newCatList = make(map[string]int64)
var ok = false
for name, musicID := range catlist {
if delList == name || delList == strconv.FormatInt(musicID, 10) {
ok = true
continue
}
newCatList[name] = musicID
playlist = append(playlist, listRaw{
Name: name,
ID: musicID,
})
}
if !ok {
ctx.SendChain(message.Text("目标歌单未找到,请确认是否正确"))
return
}
catlist = newCatList
cfg.Playlist = playlist
err = saveConfig(cfgFile)
if err == nil {
ctx.SendChain(message.Text("成功!"))
} else {
ctx.SendChain(message.Text("ERROR:", err))
}
})
engine.OnFullMatch("获取歌单列表").SetBlock(true).Limit(ctxext.LimitByGroup).
Handle(func(ctx *zero.Ctx) {
var msg []string
// 获取网易云歌单列表
if cfg.API {
catlist = make(map[string]int64, 100)
msg = append(msg, "当前添加的API歌单含有以下\n")
for i, listInfo := range cfg.Playlist {
catlist[listInfo.Name] = listInfo.ID
msg = append(msg, strconv.Itoa(i)+":"+listInfo.Name)
if i%3 == 2 {
msg = append(msg, "\n")
}
}
}
// 获取本地歌单列表*/
if cfg.Local {
err = os.MkdirAll(cfg.MusicPath, 0755)
if err == nil {
files, err := ioutil.ReadDir(cfg.MusicPath)
if err == nil {
if len(files) == 0 {
ctx.SendChain(message.Text("缓存目录没有读取到任何歌单"))
filelist = nil
} else {
msg = append(msg, "\n当前本地歌单含有以下\n")
i := 0
for _, name := range files {
if !name.IsDir() {
continue
}
filelist[i] = strconv.Itoa(i) + ":" + name.Name()
msg = append(msg, filelist[i])
if i%3 == 2 {
msg = append(msg, "\n")
}
i++
}
}
} else {
ctx.SendChain(message.Text("[读取本地列表错误]ERROR:", err))
}
} else {
ctx.SendChain(message.Text("[生成文件夹错误]ERROR:", err))
}
}
if msg == nil {
ctx.SendChain(message.Text("本地和API均未开启"))
return
}
msgs, err := text.RenderToBase64(strings.Join(msg, " "), text.FontFile, 400, 20)
if err != nil {
ctx.SendChain(message.Text("生成列表图片失败,请重试"))
return
}
if id := ctx.SendChain(message.Image("base64://" + helper.BytesToString(msgs))); id.ID() == 0 {
ctx.SendChain(message.Text("ERROR: 可能被风控了"))
}
})
engine.OnSuffix("歌单信息").SetBlock(true).Limit(ctxext.LimitByGroup).
Handle(func(ctx *zero.Ctx) {
list := ctx.State["args"].(string)
if list == "" {
ctx.SendChain(message.Text("请输入歌单ID或者API歌单名称\n歌单ID为(网页/分享)链接的“playlist”后面的第一串数字"))
return
}
var listIDStr string
for listName, listID := range catlist {
if list == listName || list == strconv.FormatInt(listID, 10) {
listIDStr = strconv.FormatInt(listID, 10)
break
}
}
if listIDStr == "" {
_, err := strconv.ParseInt(list, 10, 64)
if err != nil {
ctx.SendChain(message.Text("仅支持歌单ID查询"))
return
}
listIDStr = list
}
apiURL := "https://music.cyrilstudio.top/playlist/detail?id=" + listIDStr + "&cookie=" + cfg.Cookie
referer := "https://music.cyrilstudio.top"
data, err := web.RequestDataWith(web.NewDefaultClient(), apiURL, "GET", referer, ua)
if err != nil {
ctx.SendChain(message.Text("无法连接歌单,[error]", err))
return
}
var parsed topList
err = json.Unmarshal(data, &parsed)
if err != nil {
ctx.SendChain(message.Text("无法解析歌单ID内容,[error]", 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"),
))
})
engine.OnRegex(`^(个人|团队)猜歌(-(.*))?$`, zero.OnlyGroup).SetBlock(true).Limit(ctxext.LimitByGroup).
Handle(func(ctx *zero.Ctx) {
mode := ctx.State["regex_matched"].([]string)[3]
if mode == "" {
mode = "动画榜"
catlist[mode] = 3001835560
}
_, ok := catlist[mode]
// 如果本地和API不存在该歌单
if !strings.Contains(strings.Join(filelist, " "), mode) && !ok {
ctx.SendChain(message.Text("歌单名称错误,可以发送“获取歌单列表”获取歌单名称"))
return
}
gid := strconv.FormatInt(ctx.Event.GroupID, 10)
ctx.SendChain(message.Text("正在准备歌曲,请稍等\n回答“-[歌曲信息(歌名歌手等)|提示|取消]”\n一共3段语音6次机会"))
// 随机抽歌
musicName, pathOfMusic, err := musicLottery(mode, cfg.MusicPath)
if err != nil {
ctx.SendChain(message.Text(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]
musicAlia := ""
if infoNum > 2 {
musicAlia = musicInfo[2]
answerString += "\n其他信息:\n" + strings.ReplaceAll(musicAlia, "&", "\n")
}
// 切割音频生成3个10秒的音频
outputPath := cachePath + gid + "/"
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))
}
var musicCount = 0 // 音频数量
var answerCount = 0 // 问答次数
recv, cancel := next.Repeat()
defer cancel()
wait := time.NewTimer(40 * time.Second)
tick := time.NewTimer(105 * time.Second)
after := time.NewTimer(120 * time.Second)
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)
musicCount++
if musicCount > 2 {
wait.Stop()
continue
}
ctx.SendChain(
message.Text("好像有些难度呢,再听这段音频,要仔细听哦"),
)
ctx.SendChain(message.Record("file:///" + file.BOTPATH + "/" + outputPath + strconv.Itoa(musicCount) + ".wav"))
case c := <-recv:
wait.Reset(40 * time.Second)
tick.Reset(105 * time.Second)
after.Reset(120 * time.Second)
answer := strings.Replace(c.Event.Message.String(), "-", "", 1)
switch {
case answer == "取消":
if c.Event.UserID == ctx.Event.UserID {
wait.Stop()
tick.Stop()
after.Stop()
ctx.Send(message.ReplyWithMessage(ctx.Event.MessageID,
message.Text("游戏已取消,猜歌答案是\n", answerString)))
return
}
ctx.Send(
message.ReplyWithMessage(c.Event.MessageID,
message.Text("你无权限取消"),
),
)
case answer == "提示":
musicCount++
if musicCount > 2 {
wait.Stop()
ctx.Send(
message.ReplyWithMessage(c.Event.MessageID,
message.Text("已经没有提示了哦"),
),
)
continue
}
wait.Reset(40 * time.Second)
ctx.Send(
message.ReplyWithMessage(c.Event.MessageID,
message.Text("再听这段音频,要仔细听哦"),
),
)
ctx.SendChain(message.Record("file:///" + file.BOTPATH + "/" + outputPath + strconv.Itoa(musicCount) + ".wav"))
case strings.Contains(musicInfo[0], answer) || strings.EqualFold(musicInfo[0], answer):
wait.Stop()
tick.Stop()
after.Stop()
ctx.Send(message.ReplyWithMessage(c.Event.MessageID,
message.Text("太棒了,你猜对歌曲名了!答案是\n", answerString)))
return
case strings.Contains(musicInfo[1], answer) || strings.EqualFold(musicInfo[1], answer):
wait.Stop()
tick.Stop()
after.Stop()
ctx.Send(message.ReplyWithMessage(c.Event.MessageID,
message.Text("太棒了,你猜对歌手名了!答案是\n", answerString)))
return
case strings.Contains(musicAlia, answer) || strings.EqualFold(musicAlia, answer):
wait.Stop()
tick.Stop()
after.Stop()
ctx.Send(message.ReplyWithMessage(c.Event.MessageID,
message.Text("太棒了,你猜对出处了!答案是\n", answerString)))
return
default:
musicCount++
switch {
case musicCount > 2 && answerCount < 6:
wait.Stop()
answerCount++
ctx.Send(
message.ReplyWithMessage(c.Event.MessageID,
message.Text("答案不对哦,加油啊~"),
),
)
case musicCount > 2:
wait.Stop()
tick.Stop()
after.Stop()
ctx.Send(message.ReplyWithMessage(c.Event.MessageID,
message.Text("次数到了,没能猜出来。答案是\n", answerString)))
return
default:
wait.Reset(40 * time.Second)
answerCount++
ctx.Send(
message.ReplyWithMessage(c.Event.MessageID,
message.Text("答案不对,再听这段音频,要仔细听哦"),
),
)
ctx.SendChain(message.Record("file:///" + file.BOTPATH + "/" + outputPath + strconv.Itoa(musicCount) + ".wav"))
}
}
}
}
})
}
func saveConfig(cfgFile string) (err 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 getcatlist(pathOfMusic string) error {
catlist = make(map[string]int64, 100)
for _, listInfo := range cfg.Playlist {
catlist[listInfo.Name] = listInfo.ID
}
err := os.MkdirAll(pathOfMusic, 0755)
if err != nil {
err = errors.Errorf("[生成文件夹错误]ERROR:%s", err)
return err
}
files, err := ioutil.ReadDir(pathOfMusic)
if err != nil {
err = errors.Errorf("[读取本地列表错误]ERROR:%s", err)
return err
}
for i, name := range files {
filelist = append(filelist, strconv.Itoa(i)+":"+name.Name())
}
return nil
}
// 随机抽取音乐
func musicLottery(mode, musicPath string) (musicName, pathOfMusic string, err error) {
pathOfMusic = musicPath + mode + "/"
err = os.MkdirAll(pathOfMusic, 0755)
if err != nil {
err = errors.Errorf("[生成文件夹错误]ERROR:%s", err)
return
}
files, err := ioutil.ReadDir(pathOfMusic)
if err != nil {
err = errors.Errorf("[读取本地列表错误]ERROR:%s", err)
return
}
listID, ok := catlist[mode]
listIDstr := strconv.FormatInt(listID, 10)
if cfg.Local && cfg.API {
switch {
case len(files) == 0:
if !ok {
// 如果歌单是本地歌单
err = errors.New("本地歌单数据为0")
return
}
// 如果没有任何本地就下载歌曲
musicName, err = getListMusic(listIDstr, pathOfMusic)
if err != nil {
err = errors.Errorf("[本地数据为0歌曲下载错误]ERROR:%s", err)
return
}
case rand.Intn(2) == 0 || !ok:
// 1/2概率抽本地或者歌单只有本地有时
musicName = getLocalMusic(files)
default:
musicName, err = getListMusic(listIDstr, pathOfMusic)
if err != nil {
// 如果下载失败就从本地抽一个歌曲
musicName = getLocalMusic(files)
err = nil
}
}
return
}
if cfg.Local {
if len(files) == 0 {
err = errors.New("[本地数据为0未开启API数据]")
return
}
musicName = getLocalMusic(files)
return
}
if cfg.API && ok {
musicName, err = getListMusic(listIDstr, pathOfMusic)
if err != nil {
err = errors.Errorf("[获取API失败未开启本地数据] ERROR:%s", err)
return
}
return
}
err = errors.New("[请确认本地和API设置已开启或歌单存在]")
return
}
func getLocalMusic(files []fs.FileInfo) (musicName string) {
if len(files) > 1 {
musicName = files[rand.Intn(len(files))].Name()
} else {
musicName = files[0].Name()
}
return
}
// 下载网易云歌单音乐
func getListMusic(listID, pathOfMusic string) (musicName string, err error) {
apiURL := "https://music.cyrilstudio.top/playlist/track/all?id=" + listID + "&cookie=" + cfg.Cookie
referer := "https://music.163.com/"
data, err := web.RequestDataWith(web.NewDefaultClient(), apiURL, "GET", referer, ua)
if err != nil {
err = errors.Errorf("无法获取歌单列表\n ERROR: %s", err)
return
}
var parsed topMusicInfo
err = json.Unmarshal(data, &parsed)
if err != nil {
err = errors.Errorf("无法读取歌单列表\n ERROR: %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
}
musicURL := "http://music.163.com/song/media/outer/url?id=" + strconv.Itoa(musicID)
response, err := http.Head(musicURL)
if err != nil {
err = errors.Errorf("下载音乐失败, ERROR: %s", err)
return
}
_ = response.Body.Close()
if response.StatusCode != 200 {
err = errors.Errorf("下载音乐失败, Status Code: %d", response.StatusCode)
return
}
if cource != "" {
musicName = name + " - " + artistName + " - " + cource + ".mp3"
} else {
musicName = name + " - " + artistName + ".mp3"
}
downMusic := pathOfMusic + musicName
if file.IsNotExist(downMusic) {
data, err = web.GetData(musicURL)
if err != nil {
return
}
err = os.WriteFile(downMusic, data, 0666)
if err != nil {
return
}
}
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
}

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

@@ -0,0 +1,406 @@
package guessmusic
type listRaw struct {
Name string `json:"name"`
ID int64 `json:"id"`
}
type config struct {
MusicPath string `json:"musicPath"`
Local bool `json:"local"`
API bool `json:"api"`
Cookie string `json:"cookie"`
Playlist []listRaw `json:"playlist"`
}
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 topList 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 topMusicInfo 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"`
}

View File

@@ -2,7 +2,6 @@
package hs
import (
"fmt"
"os"
"strconv"
"strings"
@@ -12,16 +11,15 @@ import (
"github.com/wdvxdr1123/ZeroBot/message"
"github.com/wdvxdr1123/ZeroBot/utils/helper"
"github.com/FloatTech/ZeroBot-Plugin/control"
"github.com/FloatTech/ZeroBot-Plugin/utils/file"
"github.com/FloatTech/ZeroBot-Plugin/utils/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 (
cachedir = file.BOTPATH + "/data/hs/"
reqconf = [...]string{"GET", "https://hs.fbigame.com",
"Mozilla/5.0 (Linux; Android 6.0; Nexus 5 Build/MRA58N) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/86.0.4240.198 Mobile Safari/537.36"}
)
var reqconf = [...]string{"GET", "https://hs.fbigame.com",
"Mozilla/5.0 (Linux; Android 6.0; Nexus 5 Build/MRA58N) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/86.0.4240.198 Mobile Safari/537.36"}
const (
hs = `https://hs.fbigame.com/ajax.php?`
@@ -43,21 +41,17 @@ const (
)
func init() {
os.RemoveAll(cachedir)
err := os.MkdirAll(cachedir, 0755)
if err != nil {
panic(err)
}
engine := control.Register("hs", &control.Options{
engine := control.Register("hs", &ctrl.Options[*zero.Ctx]{
DisableOnDefault: false,
Help: "炉石\n" +
"- 搜卡[xxxx]\n" +
"- [卡组代码xxx]\n" +
"- 更多搜卡指令参数https://hs.fbigame.com/misc/searchhelp",
})
PrivateDataFolder: "hs",
}).ApplySingle(ctxext.DefaultSingle)
cachedir := file.BOTPATH + "/" + engine.DataFolder()
engine.OnRegex(`^搜卡(.+)$`).
SetBlock(true).SetPriority(20).Handle(func(ctx *zero.Ctx) {
SetBlock(true).Handle(func(ctx *zero.Ctx) {
List := ctx.State["regex_matched"].([]string)[1]
g := sh(List)
t := int(gjson.Get(g, `list.#`).Int())
@@ -69,9 +63,8 @@ func init() {
for i := 0; i < t && i < 5; i++ {
cid := gjson.Get(g, `list.`+strconv.Itoa(i)+`.CardID`).String()
cachefile := cachedir + cid
imgcq := `[CQ:image,file=` + "file:///" + cachefile + `]`
if file.IsNotExist(cachefile) {
data, err := web.ReqWith(
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])
@@ -79,29 +72,21 @@ func init() {
err = os.WriteFile(cachefile, data, 0644)
}
if err != nil {
imgcq = err.Error()
continue
}
}
sk = append(
sk,
message.CustomNode(
zero.BotConfig.NickName[0],
ctx.Event.SelfID,
imgcq, // 图片
),
)
sk = append(sk, ctxext.FakeSenderForwardNode(ctx, message.Image("file:///"+cachefile)))
}
if id := ctx.SendGroupForwardMessage(
ctx.Event.GroupID,
sk,
).Get("message_id").Int(); id == 0 {
ctx.SendChain(message.Text("ERROR: 可能被风控了"))
ctx.SendChain(message.Text("ERROR:可能被风控了"))
}
})
// 卡组
engine.OnRegex(`^[\s\S]*?(AAE[a-zA-Z0-9/\+=]{70,})[\s\S]*$`).
SetBlock(true).SetPriority(20).Handle(func(ctx *zero.Ctx) {
fmt.Print("成功")
SetBlock(true).Handle(func(ctx *zero.Ctx) {
List := ctx.State["regex_matched"].([]string)[1]
ctx.SendChain(
message.Image(kz(List)),
@@ -110,10 +95,10 @@ func init() {
}
func sh(s string) string {
data, err := web.ReqWith("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])
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.ReqWith(url, reqconf[0], reqconf[1], reqconf[2])
r, err := web.RequestDataWith(web.NewDefaultClient(), url, reqconf[0], reqconf[1], reqconf[2])
if err == nil {
return helper.BytesToString(r)
}
@@ -122,10 +107,10 @@ func sh(s string) string {
}
func kz(s string) string {
data, err := web.ReqWith("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])
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.ReqWith(url, reqconf[0], reqconf[1], reqconf[2])
r, err := web.RequestDataWith(web.NewDefaultClient(), url, reqconf[0], reqconf[1], reqconf[2])
if err == nil {
return "base64://" + gjson.Get(helper.BytesToString(r), "img").String()
}

123
plugin/hyaku/main.go Normal file
View File

@@ -0,0 +1,123 @@
// Package hyaku 百人一首
package hyaku
import (
"encoding/csv"
"fmt"
"math/rand"
"os"
"reflect"
"strconv"
"unsafe"
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
}
func (l *line) String() string {
b := binary.NewWriterF(func(w *binary.Writer) {
r := reflect.ValueOf(l).Elem().Type()
for i := 0; i < r.NumField(); i++ {
switch i {
case 0:
w.WriteString("●")
case 1:
w.WriteString("◉")
case 2, 3:
w.WriteString("○")
case 4, 5:
w.WriteString("◎")
}
w.WriteString(r.Field(i).Name)
w.WriteString("")
w.WriteString((*[6]string)(unsafe.Pointer(l))[i])
w.WriteString("\n")
}
})
return binary.BytesToString(b)
}
var lines [100]*line
func init() {
engine := control.Register("hyaku", &ctrl.Options[*zero.Ctx]{
DisableOnDefault: false,
Help: "百人一首\n" +
"- 百人一首(随机发一首)\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 {
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)))
}
}()
engine.OnFullMatch("百人一首").SetBlock(true).Limit(ctxext.LimitByUser).Handle(func(ctx *zero.Ctx) {
i := rand.Intn(100)
ctx.SendChain(
message.Image(fmt.Sprintf(bed+"img/%03d.jpg", i+1)),
message.Text("\n", lines[i]),
message.Image(fmt.Sprintf(bed+"img/%03d.png", i+1)),
)
})
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))
return
}
if i > 100 || i < 1 {
ctx.SendChain(message.Text("ERROR:超出范围"))
return
}
ctx.SendChain(
message.Image(fmt.Sprintf(bed+"img/%03d.jpg", i)),
message.Text("\n", lines[i-1]),
message.Image(fmt.Sprintf(bed+"img/%03d.png", i)),
)
})
}

View File

@@ -0,0 +1,145 @@
// Package imagefinder 关键字搜图
package imagefinder
import (
"encoding/json"
"errors"
"math/rand"
"net/http"
"net/url"
"reflect"
"regexp"
"strings"
"github.com/lucas-clemente/quic-go/http3"
zero "github.com/wdvxdr1123/ZeroBot"
"github.com/wdvxdr1123/ZeroBot/message"
"github.com/FloatTech/AnimeAPI/pixiv"
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 {
Error bool `json:"error"`
Message string `json:"message"`
Data struct {
Illusts []struct {
ID int64 `json:"id"`
Title string `json:"title"`
AltTitle string `json:"altTitle"`
Description string `json:"description"`
Type int64 `json:"type"`
CreateDate string `json:"createDate"`
UploadDate string `json:"uploadDate"`
Sanity int64 `json:"sanity"`
Width int64 `json:"width"`
Height int64 `json:"height"`
PageCount int64 `json:"pageCount"`
Tags []struct {
Name string `json:"name"`
Translation string `json:"translation"`
} `json:"tags"`
Statistic struct {
Bookmarks int64 `json:"bookmarks"`
Likes int64 `json:"likes"`
Comments int64 `json:"comments"`
Views int64 `json:"views"`
} `json:"statistic"`
Image string `json:"image"`
} `json:"illusts"`
Scores []float64 `json:"scores"`
HasNext bool `json:"has_next"`
} `json:"data"`
}
var hrefre = regexp.MustCompile(`<a href=".*">`)
func init() {
control.Register("imgfinder", &ctrl.Options[*zero.Ctx]{
DisableOnDefault: false,
Help: "关键字搜图\n" +
"- 来张 [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))
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))
return
}
u := illust.ImageUrls[0]
n := u[strings.LastIndex(u, "/")+1 : len(u)-4]
f := illust.Path(0)
err = pool.SendImageFromPool(n, f, func() error {
// 下载图片
return illust.DownloadToCache(0)
}, ctxext.SendFakeForwardToGroup(ctx,
message.Text(
il.Width, "x", il.Height, "\n",
"标题: ", il.Title, "\n",
"副标题: ", il.AltTitle, "\n",
"ID: ", il.ID, "\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))
return
}
})
}
// soutuapi 请求api
func soutuapi(keyword string) (r resultjson, err error) {
var data []byte
data, err = web.RequestDataWith(&http.Client{Transport: &http3.RoundTripper{}},
"https://api.pixivel.moe/v2/pixiv/illust/search/"+url.QueryEscape(keyword)+"?page=0",
"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",
)
if err != nil {
return
}
err = json.Unmarshal(data, &r)
if err == nil && r.Error {
err = errors.New(r.Message)
}
return
}
func printtags(r reflect.Value) string {
tags := r.Elem()
s := binary.BytesToString(binary.NewWriterF(func(w *binary.Writer) {
for i := 0; i < tags.Len(); i++ {
_ = w.WriteByte('\n')
tag := tags.Index(i)
_ = w.WriteByte('#')
w.WriteString(tag.Field(0).String())
if !tag.Field(1).IsZero() {
w.WriteString(" (")
w.WriteString(tag.Field(1).String())
w.WriteString(")")
}
}
}))
return s
}

23
plugin/inject/main.go Normal file
View File

@@ -0,0 +1,23 @@
// Package inject 注入指令
package inject
import (
ctrl "github.com/FloatTech/zbpctrl"
"github.com/FloatTech/zbputils/control"
zero "github.com/wdvxdr1123/ZeroBot"
"github.com/wdvxdr1123/ZeroBot/message"
)
func init() {
en := control.Register("inject", &ctrl.Options[*zero.Ctx]{
DisableOnDefault: false,
Help: "注入指令\n" +
"- run[CQ码]",
})
// 运行 CQ 码
en.OnPrefix("run", zero.SuperUserPermission).SetBlock(true).
Handle(func(ctx *zero.Ctx) {
// 可注入,权限为主人
ctx.Send(message.UnescapeCQCodeText(ctx.State["args"].(string)))
})
}

24
plugin/jandan/data.go Normal file
View File

@@ -0,0 +1,24 @@
package jandan
import (
"sync"
sql "github.com/FloatTech/sqlite"
)
var db = &sql.Sqlite{}
var mu sync.RWMutex
type picture struct {
ID uint64 `db:"id"`
URL string `db:"url"`
}
func getRandomPicture() (u string, err error) {
var p picture
mu.RLock()
err = db.Pick("picture", &p)
mu.RUnlock()
u = p.URL
return
}

115
plugin/jandan/jandan.go Normal file
View File

@@ -0,0 +1,115 @@
// Package jandan 煎蛋网无聊图
package jandan
import (
"fmt"
"hash/crc64"
"regexp"
"strconv"
"time"
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"
"github.com/wdvxdr1123/ZeroBot/message"
)
const (
api = "http://jandan.net/pic"
)
func init() {
engine := control.Register("jandan", &ctrl.Options[*zero.Ctx]{
DisableOnDefault: false,
Help: "煎蛋网无聊图\n- 来份[屌|弔|吊]图\n- 更新[屌|弔|吊]图\n",
PublicDataFolder: "Jandan",
})
getdb := ctxext.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))
return false
}
err = db.Create("picture", &picture{})
if err != nil {
ctx.SendChain(message.Text("ERROR:", err))
return false
}
n, err := db.Count("picture")
if err != nil {
ctx.SendChain(message.Text("ERROR:", err))
return false
}
logrus.Printf("[jandan]读取%d张图片", n)
return true
})
engine.OnRegex(`来份[屌|弔|吊]图`, getdb).SetBlock(true).
Handle(func(ctx *zero.Ctx) {
u, err := getRandomPicture()
if err != nil {
ctx.SendChain(message.Text("ERROR:", err))
return
}
ctx.SendChain(message.Image(u))
})
engine.OnRegex(`更新[屌|弔|吊]图`, zero.SuperUserPermission, getdb).SetBlock(true).
Handle(func(ctx *zero.Ctx) {
ctx.Send("少女更新中...")
webpageURL := api
doc, err := htmlquery.LoadURL(webpageURL)
if err != nil {
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))
return
}
LOOP:
for i := 0; i < pageTotal; i++ {
logrus.Debugln("[jandan]", fmt.Sprintf("处理第%d/%d页...", i, pageTotal))
doc, err = htmlquery.LoadURL(webpageURL)
if err != nil {
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))
return
}
if len(picList) != 0 {
for _, v := range picList {
u := "https:" + v.Attr[0].Val
i := crc64.Checksum(binary.StringToBytes(u), crc64.MakeTable(crc64.ISO))
mu.RLock()
ok := db.CanFind("picture", "where id="+strconv.FormatUint(i, 10))
mu.RUnlock()
if !ok {
mu.Lock()
_ = db.Insert("picture", &picture{ID: i, URL: u})
mu.Unlock()
} else {
// 开始重复,说明之后都是重复
break LOOP
}
}
}
if i != pageTotal-1 {
webpageURL = "https:" + htmlquery.FindOne(doc, "//*[@id='comments']/div[@class='comments']/div[@class='cp-pagenavi']/a[@class='previous-comment-page']").Attr[1].Val
}
}
ctx.Send("更新完成!")
})
}

View File

@@ -6,38 +6,28 @@ import (
"io"
"net/http"
"strings"
"time"
log "github.com/sirupsen/logrus"
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/extension/rate"
"github.com/wdvxdr1123/ZeroBot/message"
"github.com/wdvxdr1123/ZeroBot/utils/helper"
"github.com/FloatTech/ZeroBot-Plugin/control"
)
const (
juejueziURL = "https://www.offjuan.com/api/juejuezi/text"
prio = 15
referer = "https://juejuezi.offjuan.com/"
ua = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/96.0.4664.110 Safari/537.36"
)
var (
limit = rate.NewManager(time.Minute, 20)
)
func init() {
control.Register("juejuezi", &control.Options{
control.Register("juejuezi", &ctrl.Options[*zero.Ctx]{
DisableOnDefault: false,
Help: "绝绝子生成器\n" +
"- 喝奶茶绝绝子|绝绝子吃饭",
}).OnRegex("[\u4E00-\u9FA5]{0,10}绝绝子[\u4E00-\u9FA5]{0,10}").SetBlock(true).SetPriority(prio).Handle(func(ctx *zero.Ctx) {
if !limit.Load(ctx.Event.GroupID).Acquire() {
return
}
"- 喝奶茶绝绝子 | 绝绝子吃饭",
}).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) {
case 0, 1:
@@ -45,14 +35,16 @@ func init() {
case 2:
data, err := juejuezi(string(toDealStr[0]), string(toDealStr[1]))
if err != nil {
ctx.SendChain(message.Text(err))
ctx.SendChain(message.Text("ERROR:", err))
return
}
ctx.SendChain(message.Text(gjson.Get(helper.BytesToString(data), "text").String()))
default:
params := ctx.GetWordSlices(string(toDealStr)).Get("slices").Array()
data, err := juejuezi(params[0].String(), params[1].String())
if err != nil {
ctx.SendChain(message.Text(err))
ctx.SendChain(message.Text("ERROR:", err))
return
}
ctx.SendChain(message.Text(gjson.Get(helper.BytesToString(data), "text").String()))
}
@@ -65,13 +57,13 @@ func juejuezi(verb, noun string) (data []byte, err error) {
// 提交请求
request, err := http.NewRequest("POST", juejueziURL, strings.NewReader(juejueziStr))
if err != nil {
log.Errorln("[juejuezi]:", err)
return nil, err
}
request.Header.Add("Referer", referer)
request.Header.Add("User-Agent", ua)
response, err := client.Do(request)
if err != nil {
log.Errorln("[juejuezi]:", err)
return nil, err
}
data, err = io.ReadAll(response.Body)
response.Body.Close()

101
plugin/lolicon/lolicon.go Normal file
View File

@@ -0,0 +1,101 @@
// Package lolicon 基于 https://api.lolicon.app 随机图片
package lolicon
import (
"encoding/base64"
"strings"
"time"
"github.com/tidwall/gjson"
zero "github.com/wdvxdr1123/ZeroBot"
"github.com/wdvxdr1123/ZeroBot/message"
ctrl "github.com/FloatTech/zbpctrl"
"github.com/FloatTech/zbputils/control"
"github.com/FloatTech/zbputils/ctxext"
"github.com/FloatTech/zbputils/img/pool"
"github.com/FloatTech/zbputils/math"
"github.com/FloatTech/zbputils/process"
"github.com/FloatTech/zbputils/web"
)
const (
api = "https://api.lolicon.app/setu/v2"
capacity = 10
)
var (
queue = make(chan string, capacity)
custapi = ""
)
func init() {
en := control.Register("lolicon", &ctrl.Options[*zero.Ctx]{
DisableOnDefault: false,
Help: "lolicon\n" +
"- 来份萝莉\n" +
"- 设置随机图片地址[http...]",
}).ApplySingle(ctxext.DefaultSingle)
en.OnFullMatch("来份萝莉").Limit(ctxext.LimitByGroup).SetBlock(true).
Handle(func(ctx *zero.Ctx) {
go func() {
for i := 0; i < math.Min(cap(queue)-len(queue), 2); i++ {
if custapi != "" {
data, err := web.GetData(custapi)
if err != nil {
ctx.SendChain(message.Text("ERROR:", err))
continue
}
queue <- "base64://" + base64.StdEncoding.EncodeToString(data)
continue
}
data, err := web.GetData(api)
if err != nil {
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)
if err != nil {
m.SetFile(url)
_, err = m.Push(ctxext.SendToSelf(ctx), ctxext.GetMessage(ctx))
process.SleepAbout1sTo2s()
}
if err == nil {
queue <- m.String()
} else {
queue <- url
}
}
}()
select {
case <-time.After(time.Minute):
ctx.SendChain(message.Text("ERROR:等待填充,请稍后再试......"))
case img := <-queue:
id := ctx.SendChain(message.Image(img))
if id.ID() == 0 {
process.SleepAbout1sTo2s()
id = ctx.SendChain(message.Image(img).Add("cache", "0"))
if id.ID() == 0 {
ctx.SendChain(message.Text("ERROR:图片发送失败,可能被风控了~"))
}
}
}
})
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
})
}

View File

@@ -10,8 +10,8 @@ import (
"github.com/sirupsen/logrus"
"github.com/wdvxdr1123/ZeroBot/utils/helper"
"github.com/FloatTech/ZeroBot-Plugin/utils/math"
"github.com/FloatTech/ZeroBot-Plugin/utils/web"
"github.com/FloatTech/zbputils/math"
"github.com/FloatTech/zbputils/web"
)
// user hash file

View File

@@ -4,7 +4,6 @@ package manager
import (
"fmt"
"math/rand"
"os"
"sort"
"strconv"
"strings"
@@ -12,21 +11,20 @@ import (
"github.com/sirupsen/logrus"
zero "github.com/wdvxdr1123/ZeroBot"
"github.com/wdvxdr1123/ZeroBot/extension/rate"
"github.com/wdvxdr1123/ZeroBot/message"
"github.com/FloatTech/ZeroBot-Plugin/control"
"github.com/FloatTech/ZeroBot-Plugin/plugin_manager/timer"
"github.com/FloatTech/ZeroBot-Plugin/utils/ctxext"
"github.com/FloatTech/ZeroBot-Plugin/utils/math"
"github.com/FloatTech/ZeroBot-Plugin/utils/process"
"github.com/FloatTech/ZeroBot-Plugin/utils/sql"
sql "github.com/FloatTech/sqlite"
ctrl "github.com/FloatTech/zbpctrl"
"github.com/FloatTech/zbputils/control"
"github.com/FloatTech/zbputils/ctxext"
"github.com/FloatTech/zbputils/math"
"github.com/FloatTech/zbputils/process"
"github.com/FloatTech/ZeroBot-Plugin/plugin/manager/timer"
)
const (
datapath = "data/manager/"
confile = datapath + "config.db"
hint = "====群管====\n" +
hint = "====群管====\n" +
"- 禁言@QQ 1分钟\n" +
"- 解除禁言 @QQ\n" +
"- 我要自闭 1分钟\n" +
@@ -42,34 +40,40 @@ const (
"- 群聊转发 1234 XXX\n" +
"- 私聊转发 0000 XXX\n" +
"- 在MM月dd日的hh点mm分时(用http://url)提醒大家XXX\n" +
"- 在MM月[每周|周几]的hh点mm分时(用http://url)提醒大家XXX\n" +
"- 在MM月[每周 | 周几]的hh点mm分时(用http://url)提醒大家XXX\n" +
"- 取消在MM月dd日的hh点mm分的提醒\n" +
"- 取消在MM月[每周|周几]的hh点mm分的提醒\n" +
"- 取消在MM月[每周 | 周几]的hh点mm分的提醒\n" +
"- 在\"cron\"时(用[url])提醒大家[xxx]\n" +
"- 取消在\"cron\"的提醒\n" +
"- 列出所有提醒\n" +
"- 翻牌\n" +
"- 设置欢迎语XXX\n" +
"- [开启|关闭]入群验证"
"- 设置欢迎语XXX 可选添加 [{at}] [{nickname}] [{avatar}] [{uid}] [{gid}] [{groupname}] {at}可在发送时艾特被欢迎者 {nickname}是被欢迎者名字 {avatar}是被欢迎者头像 {uid}是被欢迎者QQ号 {gid}是当前群群号 {groupname} 是当前群群名\n" +
"- 测试欢迎语\n" +
"- 设置告别辞 参数同设置欢迎语\n" +
"- 测试告别辞\n" +
"- [开启 | 关闭]入群验证"
)
var (
db = &sql.Sqlite{DBPath: confile}
limit = rate.NewManager(time.Minute*5, 2)
db = &sql.Sqlite{}
clock timer.Clock
)
var engine = control.Register("manager", &control.Options{
DisableOnDefault: false,
Help: hint,
})
func init() { // 插件主体
engine := control.Register("manager", &ctrl.Options[*zero.Ctx]{
DisableOnDefault: false,
Help: hint,
PrivateDataFolder: "manager",
})
go func() {
process.SleepAbout1sTo2s()
_ = os.MkdirAll(datapath, 0755)
db.DBPath = engine.DataFolder() + "config.db"
err := db.Open(time.Hour * 24)
if err != nil {
panic(err)
}
clock = timer.NewClock(db)
err := db.Create("welcome", &welcome{})
err = db.Create("welcome", &welcome{})
if err != nil {
panic(err)
}
@@ -77,9 +81,14 @@ func init() { // 插件主体
if err != nil {
panic(err)
}
err = db.Create("farewell", &welcome{})
if err != nil {
panic(err)
}
}()
// 升为管理
engine.OnRegex(`^升为管理.*?(\d+)`, zero.OnlyGroup, zero.SuperUserPermission).SetBlock(true).SetPriority(40).
engine.OnRegex(`^升为管理.*?(\d+)`, zero.OnlyGroup, zero.SuperUserPermission).SetBlock(true).
Handle(func(ctx *zero.Ctx) {
ctx.SetGroupAdmin(
ctx.Event.GroupID,
@@ -94,7 +103,7 @@ func init() { // 插件主体
ctx.SendChain(message.Text(nickname + " 升为了管理~"))
})
// 取消管理
engine.OnRegex(`^取消管理.*?(\d+)`, zero.OnlyGroup, zero.SuperUserPermission).SetBlock(true).SetPriority(40).
engine.OnRegex(`^取消管理.*?(\d+)`, zero.OnlyGroup, zero.SuperUserPermission).SetBlock(true).
Handle(func(ctx *zero.Ctx) {
ctx.SetGroupAdmin(
ctx.Event.GroupID,
@@ -109,7 +118,7 @@ func init() { // 插件主体
ctx.SendChain(message.Text("残念~ " + nickname + " 暂时失去了管理员的资格"))
})
// 踢出群聊
engine.OnRegex(`^踢出群聊.*?(\d+)`, zero.OnlyGroup, zero.AdminPermission).SetBlock(true).SetPriority(40).
engine.OnRegex(`^踢出群聊.*?(\d+)`, zero.OnlyGroup, zero.AdminPermission).SetBlock(true).
Handle(func(ctx *zero.Ctx) {
ctx.SetGroupKick(
ctx.Event.GroupID,
@@ -124,7 +133,7 @@ func init() { // 插件主体
ctx.SendChain(message.Text("残念~ " + nickname + " 被放逐"))
})
// 退出群聊
engine.OnRegex(`^退出群聊.*?(\d+)`, zero.OnlyToMe, zero.SuperUserPermission).SetBlock(true).SetPriority(40).
engine.OnRegex(`^退出群聊.*?(\d+)`, zero.OnlyToMe, zero.SuperUserPermission).SetBlock(true).
Handle(func(ctx *zero.Ctx) {
ctx.SetGroupLeave(
math.Str2Int64(ctx.State["regex_matched"].([]string)[1]), // 要退出的群的群号
@@ -132,7 +141,7 @@ func init() { // 插件主体
)
})
// 开启全体禁言
engine.OnRegex(`^开启全员禁言$`, zero.OnlyGroup, zero.AdminPermission).SetBlock(true).SetPriority(40).
engine.OnRegex(`^开启全员禁言$`, zero.OnlyGroup, zero.AdminPermission).SetBlock(true).
Handle(func(ctx *zero.Ctx) {
ctx.SetGroupWholeBan(
ctx.Event.GroupID,
@@ -141,7 +150,7 @@ func init() { // 插件主体
ctx.SendChain(message.Text("全员自闭开始~"))
})
// 解除全员禁言
engine.OnRegex(`^解除全员禁言$`, zero.OnlyGroup, zero.AdminPermission).SetBlock(true).SetPriority(40).
engine.OnRegex(`^解除全员禁言$`, zero.OnlyGroup, zero.AdminPermission).SetBlock(true).
Handle(func(ctx *zero.Ctx) {
ctx.SetGroupWholeBan(
ctx.Event.GroupID,
@@ -150,7 +159,7 @@ func init() { // 插件主体
ctx.SendChain(message.Text("全员自闭结束~"))
})
// 禁言
engine.OnRegex(`^禁言.*?(\d+).*?\s(\d+)(.*)`, zero.OnlyGroup, zero.AdminPermission).SetBlock(true).SetPriority(40).
engine.OnRegex(`^禁言.*?(\d+).*?\s(\d+)(.*)`, zero.OnlyGroup, zero.AdminPermission).SetBlock(true).
Handle(func(ctx *zero.Ctx) {
duration := math.Str2Int64(ctx.State["regex_matched"].([]string)[2])
switch ctx.State["regex_matched"].([]string)[3] {
@@ -174,7 +183,7 @@ func init() { // 插件主体
ctx.SendChain(message.Text("小黑屋收留成功~"))
})
// 解除禁言
engine.OnRegex(`^解除禁言.*?(\d+)`, zero.OnlyGroup, zero.AdminPermission).SetBlock(true).SetPriority(40).
engine.OnRegex(`^解除禁言.*?(\d+)`, zero.OnlyGroup, zero.AdminPermission).SetBlock(true).
Handle(func(ctx *zero.Ctx) {
ctx.SetGroupBan(
ctx.Event.GroupID,
@@ -184,7 +193,7 @@ func init() { // 插件主体
ctx.SendChain(message.Text("小黑屋释放成功~"))
})
// 自闭禁言
engine.OnRegex(`^(我要自闭|禅定).*?(\d+)(.*)`, zero.OnlyGroup).SetBlock(true).SetPriority(40).
engine.OnRegex(`^(我要自闭|禅定).*?(\d+)(.*)`, zero.OnlyGroup).SetBlock(true).
Handle(func(ctx *zero.Ctx) {
duration := math.Str2Int64(ctx.State["regex_matched"].([]string)[2])
switch ctx.State["regex_matched"].([]string)[3] {
@@ -208,8 +217,12 @@ func init() { // 插件主体
ctx.SendChain(message.Text("那我就不手下留情了~"))
})
// 修改名片
engine.OnRegex(`^修改名片.*?(\d+).*?\s(.*)`, zero.OnlyGroup, zero.AdminPermission).SetBlock(true).SetPriority(40).
engine.OnRegex(`^修改名片.*?(\d+).*?\s(.*)`, zero.OnlyGroup, zero.AdminPermission).SetBlock(true).
Handle(func(ctx *zero.Ctx) {
if len(ctx.State["regex_matched"].([]string)[2]) > 60 {
ctx.SendChain(message.Text("名字太长啦!"))
return
}
ctx.SetGroupCard(
ctx.Event.GroupID,
math.Str2Int64(ctx.State["regex_matched"].([]string)[1]), // 被修改群名片的人
@@ -218,8 +231,12 @@ func init() { // 插件主体
ctx.SendChain(message.Text("嗯!已经修改了"))
})
// 修改头衔
engine.OnRegex(`^修改头衔.*?(\d+).*?\s(.*)`, zero.OnlyGroup, zero.AdminPermission).SetBlock(true).SetPriority(40).
engine.OnRegex(`^修改头衔.*?(\d+).*?\s(.*)`, zero.OnlyGroup, zero.AdminPermission).SetBlock(true).
Handle(func(ctx *zero.Ctx) {
if len(ctx.State["regex_matched"].([]string)[1]) > 18 {
ctx.SendChain(message.Text("头衔太长啦!"))
return
}
ctx.SetGroupSpecialTitle(
ctx.Event.GroupID,
math.Str2Int64(ctx.State["regex_matched"].([]string)[1]), // 被修改群头衔的人
@@ -228,8 +245,12 @@ func init() { // 插件主体
ctx.SendChain(message.Text("嗯!已经修改了"))
})
// 申请头衔
engine.OnRegex(`^申请头衔(.*)`, zero.OnlyGroup).SetBlock(true).SetPriority(40).
engine.OnRegex(`^申请头衔(.*)`, zero.OnlyGroup).SetBlock(true).
Handle(func(ctx *zero.Ctx) {
if len(ctx.State["regex_matched"].([]string)[1]) > 18 {
ctx.SendChain(message.Text("头衔太长啦!"))
return
}
ctx.SetGroupSpecialTitle(
ctx.Event.GroupID,
ctx.Event.UserID, // 被修改群头衔的人
@@ -238,7 +259,7 @@ func init() { // 插件主体
ctx.SendChain(message.Text("嗯!不错的头衔呢~"))
})
// 群聊转发
engine.OnRegex(`^群聊转发.*?(\d+)\s(.*)`, zero.SuperUserPermission).SetBlock(true).SetPriority(40).
engine.OnRegex(`^群聊转发.*?(\d+)\s(.*)`, zero.SuperUserPermission).SetBlock(true).
Handle(func(ctx *zero.Ctx) {
// 对CQ码进行反转义
content := ctx.State["regex_matched"].([]string)[2]
@@ -251,7 +272,7 @@ func init() { // 插件主体
ctx.SendChain(message.Text("📧 --> " + ctx.State["regex_matched"].([]string)[1]))
})
// 私聊转发
engine.OnRegex(`^私聊转发.*?(\d+)\s(.*)`, zero.SuperUserPermission).SetBlock(true).SetPriority(40).
engine.OnRegex(`^私聊转发.*?(\d+)\s(.*)`, zero.SuperUserPermission).SetBlock(true).
Handle(func(ctx *zero.Ctx) {
// 对CQ码进行反转义
content := ctx.State["regex_matched"].([]string)[2]
@@ -264,25 +285,25 @@ func init() { // 插件主体
ctx.SendChain(message.Text("📧 --> " + ctx.State["regex_matched"].([]string)[1]))
})
// 定时提醒
engine.OnRegex(`^在(.{1,2})月(.{1,3}日|每?周.?)的(.{1,3})点(.{1,3})分时(用.+)?提醒大家(.*)`, zero.AdminPermission, zero.OnlyGroup).SetBlock(true).SetPriority(40).
engine.OnRegex(`^在(.{1,2})月(.{1,3}日|每?周.?)的(.{1,3})点(.{1,3})分时(用.+)?提醒大家(.*)`, zero.AdminPermission, zero.OnlyGroup).SetBlock(true).
Handle(func(ctx *zero.Ctx) {
dateStrs := ctx.State["regex_matched"].([]string)
ts := timer.GetFilledTimer(dateStrs, ctx.Event.SelfID, ctx.Event.GroupID, false)
if ts.En() {
go clock.RegisterTimer(ts, true)
go clock.RegisterTimer(ts, true, false)
ctx.SendChain(message.Text("记住了~"))
} else {
ctx.SendChain(message.Text("参数非法:" + ts.Alert))
}
})
// 定时 cron 提醒
engine.OnRegex(`^在"(.*)"时(用.+)?提醒大家(.*)`, zero.AdminPermission, zero.OnlyGroup).SetBlock(true).SetPriority(40).
engine.OnRegex(`^在"(.*)"时(用.+)?提醒大家(.*)`, zero.AdminPermission, zero.OnlyGroup).SetBlock(true).
Handle(func(ctx *zero.Ctx) {
dateStrs := ctx.State["regex_matched"].([]string)
var url, alert string
switch len(dateStrs) {
case 4:
url = dateStrs[2]
url = strings.TrimPrefix(dateStrs[2], "用")
alert = dateStrs[3]
case 3:
alert = dateStrs[2]
@@ -292,14 +313,14 @@ func init() { // 插件主体
}
logrus.Debugln("[manager] cron:", dateStrs[1])
ts := timer.GetFilledCronTimer(dateStrs[1], alert, url, ctx.Event.SelfID, ctx.Event.GroupID)
if clock.RegisterTimer(ts, true) {
if clock.RegisterTimer(ts, true, false) {
ctx.SendChain(message.Text("记住了~"))
} else {
ctx.SendChain(message.Text("参数非法:" + ts.Alert))
}
})
// 取消定时
engine.OnRegex(`^取消在(.{1,2})月(.{1,3}日|每?周.?)的(.{1,3})点(.{1,3})分的提醒`, zero.AdminPermission, zero.OnlyGroup).SetBlock(true).SetPriority(40).
engine.OnRegex(`^取消在(.{1,2})月(.{1,3}日|每?周.?)的(.{1,3})点(.{1,3})分的提醒`, zero.AdminPermission, zero.OnlyGroup).SetBlock(true).
Handle(func(ctx *zero.Ctx) {
dateStrs := ctx.State["regex_matched"].([]string)
ts := timer.GetFilledTimer(dateStrs, ctx.Event.SelfID, ctx.Event.GroupID, true)
@@ -312,7 +333,7 @@ func init() { // 插件主体
}
})
// 取消 cron 定时
engine.OnRegex(`^取消在"(.*)"的提醒`, zero.AdminPermission, zero.OnlyGroup).SetBlock(true).SetPriority(40).
engine.OnRegex(`^取消在"(.*)"的提醒`, zero.AdminPermission, zero.OnlyGroup).SetBlock(true).
Handle(func(ctx *zero.Ctx) {
dateStrs := ctx.State["regex_matched"].([]string)
ts := timer.Timer{Cron: dateStrs[1], GrpID: ctx.Event.GroupID}
@@ -325,28 +346,19 @@ func init() { // 插件主体
}
})
// 列出本群所有定时
engine.OnFullMatch("列出所有提醒", zero.AdminPermission, zero.OnlyGroup).SetBlock(true).SetPriority(40).
engine.OnFullMatch("列出所有提醒", zero.AdminPermission, zero.OnlyGroup).SetBlock(true).
Handle(func(ctx *zero.Ctx) {
ctx.SendChain(message.Text(clock.ListTimers(ctx.Event.GroupID)))
})
// 随机点名
engine.OnFullMatchGroup([]string{"翻牌"}, zero.OnlyGroup).SetBlock(true).SetPriority(40).
engine.OnFullMatchGroup([]string{"翻牌"}, zero.OnlyGroup).SetBlock(true).Limit(ctxext.LimitByUser).
Handle(func(ctx *zero.Ctx) {
if !limit.Load(ctx.Event.UserID).Acquire() {
ctx.SendChain(message.Text("少女祈祷中......"))
return
}
// 无缓存获取群员列表
list := ctx.CallAction("get_group_member_list", zero.Params{
"group_id": ctx.Event.GroupID,
"no_cache": true,
}).Data
temp := list.Array()
temp := ctx.GetThisGroupMemberListNoCache().Array()
sort.SliceStable(temp, func(i, j int) bool {
return temp[i].Get("last_sent_time").Int() < temp[j].Get("last_sent_time").Int()
})
temp = temp[math.Max(0, len(temp)-10):]
rand.Seed(time.Now().UnixNano())
who := temp[rand.Intn(len(temp))]
if who.Get("user_id").Int() == ctx.Event.SelfID {
ctx.SendChain(message.Text("幸运儿居然是我自己"))
@@ -368,17 +380,17 @@ func init() { // 插件主体
)
})
// 入群欢迎
engine.OnNotice().SetBlock(false).FirstPriority().
engine.OnNotice().SetBlock(false).
Handle(func(ctx *zero.Ctx) {
if ctx.Event.NoticeType == "group_increase" && ctx.Event.SelfID != ctx.Event.UserID {
var w welcome
err := db.Find("welcome", &w, "where gid = "+strconv.FormatInt(ctx.Event.GroupID, 10))
if err == nil {
ctx.SendChain(message.Text(w.Msg))
ctx.SendGroupMessage(ctx.Event.GroupID, message.ParseMessageFromString(welcometocq(ctx, w.Msg)))
} else {
ctx.SendChain(message.Text("欢迎~"))
}
c, ok := control.Lookup("manager")
c, ok := ctx.State["manager"].(*ctrl.Control[*zero.Ctx])
if ok {
enable := c.GetData(ctx.Event.GroupID)&1 == 1
if enable {
@@ -404,13 +416,13 @@ func init() { // 插件主体
}
return false
}
next := zero.NewFutureEvent("message", 999, false, zero.CheckUser(ctx.Event.UserID), rule)
next := zero.NewFutureEvent("message", 999, false, ctx.CheckSession(), rule)
recv, cancel := next.Repeat()
select {
case <-time.After(time.Minute):
cancel()
ctx.SendChain(message.Text("拜拜啦~"))
ctx.SetGroupKick(ctx.Event.GroupID, uid, false)
cancel()
case <-recv:
cancel()
ctx.SendChain(message.Text("答对啦~"))
@@ -420,19 +432,27 @@ func init() { // 插件主体
}
})
// 退群提醒
engine.OnNotice().SetBlock(false).SetPriority(40).
engine.OnNotice().SetBlock(false).
Handle(func(ctx *zero.Ctx) {
if ctx.Event.NoticeType == "group_decrease" {
userid := ctx.Event.UserID
ctx.SendChain(message.Text(ctxext.CardOrNickName(ctx, userid), "(", userid, ")", "离开了我们..."))
var w welcome
err := db.Find("farewell", &w, "where gid = "+strconv.FormatInt(ctx.Event.GroupID, 10))
if err == nil {
ctx.SendGroupMessage(ctx.Event.GroupID, message.ParseMessageFromString(welcometocq(ctx, w.Msg)))
} else {
userid := ctx.Event.UserID
ctx.SendChain(message.Text(ctx.CardOrNickName(userid), "(", userid, ")", "离开了我们..."))
}
}
})
// 设置欢迎语
engine.OnRegex(`^设置欢迎语([\s\S]*)$`, zero.OnlyGroup, zero.AdminPermission).SetBlock(true).SetPriority(40).
engine.OnRegex(`^设置欢迎语([\s\S]*)$`, zero.OnlyGroup, zero.AdminPermission).SetBlock(true).
Handle(func(ctx *zero.Ctx) {
welcomestring := ctx.State["regex_matched"].([]string)[1]
welcomestring = message.UnescapeCQCodeText(welcomestring)
w := &welcome{
GrpID: ctx.Event.GroupID,
Msg: ctx.State["regex_matched"].([]string)[1],
Msg: welcomestring,
}
err := db.Insert("welcome", w)
if err == nil {
@@ -441,11 +461,50 @@ func init() { // 插件主体
ctx.SendChain(message.Text("出错啦: ", err))
}
})
// 测试欢迎语
engine.OnFullMatch("测试欢迎语", zero.OnlyGroup, zero.AdminPermission).SetBlock(true).
Handle(func(ctx *zero.Ctx) {
var w welcome
err := db.Find("welcome", &w, "where gid = "+strconv.FormatInt(ctx.Event.GroupID, 10))
if err == nil {
ctx.SendGroupMessage(ctx.Event.GroupID, message.ParseMessageFromString(welcometocq(ctx, w.Msg)))
} else {
ctx.SendChain(message.Text("欢迎~"))
}
})
// 设置告别辞
engine.OnRegex(`^设置告别辞([\s\S]*)$`, zero.OnlyGroup, zero.AdminPermission).SetBlock(true).
Handle(func(ctx *zero.Ctx) {
farewellstring := ctx.State["regex_matched"].([]string)[1]
farewellstring = message.UnescapeCQCodeText(farewellstring)
w := &welcome{
GrpID: ctx.Event.GroupID,
Msg: farewellstring,
}
err := db.Insert("farewell", w)
if err == nil {
ctx.SendChain(message.Text("记住啦!"))
} else {
ctx.SendChain(message.Text("出错啦: ", err))
}
})
// 测试告别辞
engine.OnFullMatch("测试告别辞", zero.OnlyGroup, zero.AdminPermission).SetBlock(true).
Handle(func(ctx *zero.Ctx) {
var w welcome
err := db.Find("farewell", &w, "where gid = "+strconv.FormatInt(ctx.Event.GroupID, 10))
if err == nil {
ctx.SendGroupMessage(ctx.Event.GroupID, message.ParseMessageFromString(welcometocq(ctx, w.Msg)))
} else {
userid := ctx.Event.UserID
ctx.SendChain(message.Text(ctx.CardOrNickName(userid), "(", userid, ")", "离开了我们..."))
}
})
// 入群后验证开关
engine.OnRegex(`^(.*)入群验证$`, zero.OnlyGroup, zero.AdminPermission).SetBlock(true).SetPriority(40).
engine.OnRegex(`^(.*)入群验证$`, zero.OnlyGroup, zero.AdminPermission).SetBlock(true).
Handle(func(ctx *zero.Ctx) {
option := ctx.State["regex_matched"].([]string)[1]
c, ok := control.Lookup("manager")
c, ok := ctx.State["manager"].(*ctrl.Control[*zero.Ctx])
if ok {
data := c.GetData(ctx.Event.GroupID)
switch option {
@@ -467,10 +526,10 @@ func init() { // 插件主体
ctx.SendChain(message.Text("找不到服务!"))
})
// 加群 gist 验证开关
engine.OnRegex(`^(.*)gist加群自动审批$`, zero.OnlyGroup, zero.AdminPermission).SetBlock(true).SetPriority(40).
engine.OnRegex(`^(.*)gist加群自动审批$`, zero.OnlyGroup, zero.AdminPermission).SetBlock(true).
Handle(func(ctx *zero.Ctx) {
option := ctx.State["regex_matched"].([]string)[1]
c, ok := control.Lookup("manager")
c, ok := ctx.State["manager"].(*ctrl.Control[*zero.Ctx])
if ok {
data := c.GetData(ctx.Event.GroupID)
switch option {
@@ -491,23 +550,14 @@ func init() { // 插件主体
}
ctx.SendChain(message.Text("找不到服务!"))
})
// 运行 CQ 码
engine.OnRegex(`^run(.*)$`, zero.SuperUserPermission).SetBlock(true).SetPriority(0).
Handle(func(ctx *zero.Ctx) {
var cmd = ctx.State["regex_matched"].([]string)[1]
cmd = strings.ReplaceAll(cmd, "&#91;", "[")
cmd = strings.ReplaceAll(cmd, "&#93;", "]")
// 可注入,权限为主人
ctx.Send(cmd)
})
// 根据 gist 自动同意加群
// 加群请在github新建一个gist其文件名为本群群号的字符串的md5(小写)内容为一行是当前unix时间戳(10分钟内有效)。
// 然后请将您的用户名和gist哈希(小写)按照username/gisthash的格式填写到回答即可。
engine.OnRequest().SetBlock(false).FirstPriority().Handle(func(ctx *zero.Ctx) {
engine.OnRequest().SetBlock(false).Handle(func(ctx *zero.Ctx) {
/*if ctx.Event.RequestType == "friend" {
ctx.SetFriendAddRequest(ctx.Event.Flag, true, "")
}*/
c, ok := control.Lookup("manager")
c, ok := ctx.State["manager"].(*ctrl.Control[*zero.Ctx])
if ok && c.GetData(ctx.Event.GroupID)&0x10 == 0x10 && ctx.Event.RequestType == "group" && ctx.Event.SubType == "add" {
// gist 文件名是群号的 ascii 编码的 md5
// gist 内容是当前 uinx 时间戳,在 10 分钟内视为有效
@@ -519,7 +569,7 @@ func init() { // 插件主体
}
ghun := ans[:divi]
hash := ans[divi+1:]
logrus.Infoln("[manager]收到加群申请, 用户:", ghun, ", hash:", hash)
logrus.Debugln("[manager]收到加群申请, 用户:", ghun, ", hash:", hash)
ok, reason := checkNewUser(ctx.Event.UserID, ctx.Event.GroupID, ghun, hash)
if ok {
ctx.SetGroupAddRequest(ctx.Event.Flag, "add", true, "")
@@ -531,3 +581,20 @@ func init() { // 插件主体
}
})
}
// 传入 ctx 和 welcome格式string 返回cq格式string 使用方法:welcometocq(ctx,w.Msg)
func welcometocq(ctx *zero.Ctx, welcome string) string {
uid := strconv.FormatInt(ctx.Event.UserID, 10) // 用户id
nickname := ctx.CardOrNickName(ctx.Event.UserID) // 用户昵称
at := "[CQ:at,qq=" + uid + "]" // at用户
avatar := "[CQ:image,file=" + "http://q4.qlogo.cn/g?b=qq&nk=" + uid + "&s=640]" // 用户头像
gid := strconv.FormatInt(ctx.Event.GroupID, 10) // 群id
groupname := ctx.GetGroupInfo(ctx.Event.GroupID, true).Name // 群名
cqstring := strings.ReplaceAll(welcome, "{at}", at)
cqstring = strings.ReplaceAll(cqstring, "{nickname}", nickname)
cqstring = strings.ReplaceAll(cqstring, "{avatar}", avatar)
cqstring = strings.ReplaceAll(cqstring, "{uid}", uid)
cqstring = strings.ReplaceAll(cqstring, "{gid}", gid)
cqstring = strings.ReplaceAll(cqstring, "{groupname}", groupname)
return cqstring
}

View File

@@ -106,10 +106,10 @@ func GetFilledTimer(dateStrs []string, botqq, grp int64, matchDateOnly bool) *Ti
urlStr := dateStrs[5]
if urlStr != "" { // 是图片url
t.URL = urlStr[3:] // utf-8下用为3字节
logrus.Println("[群管]" + t.URL)
logrus.Debugln("[群管]" + t.URL)
if !strings.HasPrefix(t.URL, "http") {
t.URL = "illegal"
logrus.Println("[群管]url非法")
logrus.Debugln("[群管]url非法")
return &t
}
}

View File

@@ -1,7 +1,7 @@
package timer
import (
"github.com/FloatTech/ZeroBot-Plugin/utils/sql"
sql "github.com/FloatTech/sqlite"
)
// Timer 计时器

View File

@@ -7,12 +7,12 @@ import (
"sync"
"time"
sql "github.com/FloatTech/sqlite"
"github.com/FloatTech/zbputils/process"
"github.com/fumiama/cron"
"github.com/sirupsen/logrus"
zero "github.com/wdvxdr1123/ZeroBot"
"github.com/wdvxdr1123/ZeroBot/message"
"github.com/FloatTech/ZeroBot-Plugin/utils/sql"
)
// Clock 时钟
@@ -39,16 +39,16 @@ var (
// NewClock 添加一个新时钟
func NewClock(db *sql.Sqlite) (c Clock) {
c.loadTimers(db)
c.cron = cron.New()
c.entries = make(map[uint32]cron.EntryID)
c.timers = &map[uint32]*Timer{}
c.loadTimers(db)
c.cron.Start()
return
}
// RegisterTimer 注册计时器
func (c *Clock) RegisterTimer(ts *Timer, save bool) bool {
func (c *Clock) RegisterTimer(ts *Timer, save, isinit bool) bool {
var key uint32
if save {
key = ts.GetTimerID()
@@ -60,9 +60,12 @@ func (c *Clock) RegisterTimer(ts *Timer, save bool) bool {
if t != ts && ok { // 避免重复注册定时器
t.SetEn(false)
}
logrus.Println("[群管]注册计时器", key)
logrus.Infoln("[群管]注册计时器", key)
if ts.Cron != "" {
var ctx *zero.Ctx
if isinit {
process.GlobalInitMutex.Lock()
}
if ts.SelfID != 0 {
ctx = zero.GetBot(ts.SelfID)
} else {
@@ -72,6 +75,9 @@ func (c *Clock) RegisterTimer(ts *Timer, save bool) bool {
return false
})
}
if isinit {
process.GlobalInitMutex.Unlock()
}
eid, err := c.cron.AddFunc(ts.Cron, func() { ts.sendmsg(ts.GrpID, ctx) })
if err == nil {
c.entmu.Lock()
@@ -188,7 +194,7 @@ func (c *Clock) loadTimers(db *sql.Sqlite) {
var t Timer
_ = c.db.FindFor("timer", &t, "", func() error {
tescape := t
go c.RegisterTimer(&tescape, false)
go c.RegisterTimer(&tescape, false, true)
return nil
})
}

View File

@@ -4,7 +4,7 @@ import (
"testing"
"time"
"github.com/FloatTech/ZeroBot-Plugin/utils/sql"
sql "github.com/FloatTech/sqlite"
"github.com/sirupsen/logrus"
)

View File

@@ -0,0 +1,539 @@
// Package midicreate 简易midi音乐制作
package midicreate
import (
"bytes"
"fmt"
"math"
"math/rand"
"os"
"os/exec"
"path"
"path/filepath"
"strconv"
"strings"
"time"
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"
"github.com/FloatTech/zbputils/web"
"github.com/pkg/errors"
zero "github.com/wdvxdr1123/ZeroBot"
"github.com/wdvxdr1123/ZeroBot/message"
"gitlab.com/gomidi/midi/v2"
"gitlab.com/gomidi/midi/v2/smf"
)
func init() {
engine := control.Register("midicreate", &ctrl.Options[*zero.Ctx]{
DisableOnDefault: false,
Help: "midi音乐制作, 该插件需要安装timidity, linux安装脚本可参考https://gitcode.net/anto_july/midi/-/raw/master/timidity.sh, windows安装脚本可参考https://gitcode.net/anto_july/midi/-/raw/master/timidity.bat?inline=false, windows需要管理员模式运行\n" +
"- midi制作 CCGGAAGR FFEEDDCR GGFFEEDR GGFFEEDR CCGGAAGR FFEEDDCR\n" +
"- 个人听音练习\n" +
"- 团队听音练习\n" +
"- *.mid (midi 转 txt)\n" +
"- midi制作*.txt (txt 转 midi)\n" +
"- 设置音色40 (0~127)",
PrivateDataFolder: "midicreate",
})
cachePath := engine.DataFolder() + "cache/"
_ = os.RemoveAll(cachePath)
err := os.MkdirAll(cachePath, 0755)
if err != nil {
panic(err)
}
engine.OnPrefix("midi制作").SetBlock(true).Limit(ctxext.LimitByUser).
Handle(func(ctx *zero.Ctx) {
uid := ctx.Event.UserID
input := ctx.State["args"].(string)
midiFile := cachePath + strconv.FormatInt(uid, 10) + time.Now().Format("20060102150405") + "_midicreate.mid"
cmidiFile, err := str2music(ctx, input, midiFile)
if err != nil {
if file.IsExist(midiFile) {
ctx.UploadThisGroupFile(file.BOTPATH+"/"+midiFile, filepath.Base(midiFile), "")
return
}
ctx.SendChain(message.Text("ERROR:无法转换midi文件,", err))
return
}
ctx.SendChain(message.Record("file:///" + file.BOTPATH + "/" + cmidiFile))
})
engine.OnRegex("^(个人|团队)听音练习$", zero.OnlyGroup).SetBlock(true).Limit(ctxext.LimitByUser).
Handle(func(ctx *zero.Ctx) {
uid := ctx.Event.UserID
ctx.SendChain(message.Text("欢迎来到听音练习, 一共有5个问题, 每个问题1分"))
var mode int
var next *zero.FutureEvent
var maxErrorCount int
if ctx.State["regex_matched"].([]string)[1] == "个人" {
mode = 0
next = zero.NewFutureEvent("message", 999, false, zero.RegexRule(`^[A-G][b|#]?\d{0,2}$`),
zero.OnlyGroup, ctx.CheckSession())
maxErrorCount = 3
} else {
mode = 1
next = zero.NewFutureEvent("message", 999, false, zero.RegexRule(`^[A-G][b|#]?\d{0,2}$`),
zero.OnlyGroup, zero.CheckGroup(ctx.Event.GroupID))
maxErrorCount = 10
}
recv, cancel := next.Repeat()
defer cancel()
score := make(map[int64]float64)
round := 1
maxRound := 6
errorCount := 0
target := uint8(55 + rand.Intn(34))
answer := name(target) + strconv.Itoa(int(target/12))
midiFile := cachePath + strconv.FormatInt(uid, 10) + time.Now().Format("20060102150405") + "_midicreate.mid"
cmidiFile, err := str2music(ctx, answer, midiFile)
if err != nil {
ctx.SendChain(message.Text("ERROR:听音练习结束, 无法转换midi文件, ", err))
return
}
time.Sleep(time.Millisecond * 500)
ctx.SendChain(message.Record("file:///" + file.BOTPATH + "/" + cmidiFile))
ctx.Send(
message.ReplyWithMessage(ctx.Event.MessageID,
message.Text("判断上面的音频, 输入音符, 例如C#6"),
),
)
tick := time.NewTimer(45 * time.Second)
after := time.NewTimer(60 * time.Second)
for {
select {
case <-tick.C:
ctx.SendChain(message.Text("听音练习, 你还有15s作答时间"))
case <-after.C:
var text string
for k, v := range score {
text += fmt.Sprintf("%s: %.1f\n", ctx.CardOrNickName(k), v)
}
ctx.Send(
message.ReplyWithMessage(ctx.Event.MessageID,
message.Text("听音练习超时, 练习结束...答案是: ", answer, "\n所得分数如下:\n", text),
),
)
return
case c := <-recv:
tick.Reset(45 * time.Second)
after.Reset(60 * time.Second)
n := processOne(c.Event.Message.String())
if n != target {
errorCount++
}
if errorCount == maxErrorCount || n == target {
if n == target {
ctx.Send(
message.ReplyWithMessage(c.Event.MessageID,
message.Text("恭喜你回答正确, 答案是: ", answer),
),
)
} else if errorCount == maxErrorCount {
ctx.Send(
message.ReplyWithMessage(c.Event.MessageID,
message.Text("你的回答是: "),
),
)
midiFile = cachePath + strconv.FormatInt(uid, 10) + time.Now().Format("20060102150405") + "_midicreate.mid"
cmidiFile, err = str2music(ctx, c.Event.Message.String(), midiFile)
if err != nil {
ctx.SendChain(message.Text("ERROR: can't convert midi file,", err))
return
}
time.Sleep(time.Millisecond * 500)
ctx.SendChain(message.Record("file:///" + file.BOTPATH + "/" + cmidiFile))
ctx.Send(
message.ReplyWithMessage(c.Event.MessageID,
message.Text("回答错误, 答案是: ", answer, ", 错误次数已达3次, 进入下一关"),
),
)
}
// 统计分数
if mode == 0 {
switch errorCount {
case 0:
score[c.Event.UserID] += 1.0
case 1:
score[c.Event.UserID] += 0.5
case 2:
score[c.Event.UserID] += 0.2
}
} else if mode == 1 {
if errorCount != maxErrorCount {
score[c.Event.UserID] += 1.0
}
}
// 下一关
round++
if round != maxRound {
errorCount = 0
target = uint8(55 + rand.Intn(34))
answer = name(target) + strconv.Itoa(int(target/12))
midiFile = cachePath + strconv.FormatInt(uid, 10) + time.Now().Format("20060102150405") + "_midicreate.mid"
cmidiFile, err = str2music(ctx, answer, midiFile)
if err != nil {
ctx.SendChain(message.Text("ERROR:听音练习结束, 无法转换midi文件, ", err))
return
}
time.Sleep(time.Millisecond * 500)
ctx.SendChain(message.Record("file:///" + file.BOTPATH + "/" + cmidiFile))
ctx.Send(
message.ReplyWithMessage(c.Event.MessageID,
message.Text("判断上面的音频, 输入音符, 例如C#6"),
),
)
}
} else if n != target {
ctx.Send(
message.ReplyWithMessage(c.Event.MessageID,
message.Text("你的回答是: "),
),
)
time.Sleep(time.Millisecond * 500)
midiFile = cachePath + strconv.FormatInt(uid, 10) + time.Now().Format("20060102150405") + "_midicreate.mid"
cmidiFile, err = str2music(ctx, c.Event.Message.String(), midiFile)
if err != nil {
ctx.SendChain(message.Text("ERROR: can't convert midi file,", err))
return
}
time.Sleep(time.Millisecond * 500)
ctx.SendChain(message.Record("file:///" + file.BOTPATH + "/" + cmidiFile))
ctx.Send(
message.ReplyWithMessage(c.Event.MessageID,
message.Text("回答错误, 错误次数为", errorCount, ", 请继续回答"),
),
)
}
if round == maxRound {
var text string
for k, v := range score {
text += fmt.Sprintf("%s: %.1f\n", ctx.CardOrNickName(k), v)
}
ctx.Send(
message.ReplyWithMessage(c.Event.MessageID,
message.Text("回答完毕, 所得分数如下:\n", text),
),
)
return
}
}
}
})
engine.On("notice/group_upload", func(ctx *zero.Ctx) bool {
return path.Ext(ctx.Event.File.Name) == ".mid"
}).SetBlock(false).Limit(ctxext.LimitByGroup).
Handle(func(ctx *zero.Ctx) {
fileURL := ctx.GetThisGroupFileUrl(ctx.Event.File.BusID, ctx.Event.File.ID)
data, err := web.GetData(fileURL)
if err != nil {
ctx.SendChain(message.Text("ERROR:", err))
return
}
s, err := smf.ReadFrom(bytes.NewReader(data))
if err != nil {
ctx.SendChain(message.Text("ERROR:", err))
return
}
for i := 0; i < int(s.NumTracks()); i++ {
midStr := mid2txt(data, i)
if err != nil {
ctx.SendChain(message.Text("ERROR:", err))
return
}
fileName := strings.ReplaceAll(cachePath+"/"+ctx.Event.File.Name, ".mid", fmt.Sprintf("-%d.txt", i))
_ = os.WriteFile(fileName, binary.StringToBytes(midStr), 0666)
ctx.UploadThisGroupFile(file.BOTPATH+"/"+fileName, filepath.Base(fileName), "")
}
})
engine.On("notice/group_upload", func(ctx *zero.Ctx) bool {
return path.Ext(ctx.Event.File.Name) == ".txt" && strings.Contains(ctx.Event.File.Name, "midi制作")
}).SetBlock(false).Limit(ctxext.LimitByGroup).
Handle(func(ctx *zero.Ctx) {
fileURL := ctx.GetThisGroupFileUrl(ctx.Event.File.BusID, ctx.Event.File.ID)
data, err := web.GetData(fileURL)
if err != nil {
ctx.SendChain(message.Text("ERROR:", err))
return
}
uid := ctx.Event.UserID
midiFile := cachePath + strconv.FormatInt(uid, 10) + time.Now().Format("20060102150405") + "_midicreate.mid"
cmidiFile, err := str2music(ctx, binary.BytesToString(data), midiFile)
if err != nil {
ctx.SendChain(message.Text("ERROR:无法转换midi文件,", err))
return
}
ctx.SendChain(message.Record("file:///" + file.BOTPATH + "/" + cmidiFile))
})
engine.OnPrefix("设置音色").SetBlock(true).
Handle(func(ctx *zero.Ctx) {
param := ctx.State["args"].(string)
timbre, err := strconv.Atoi(param)
if err != nil {
ctx.SendChain(message.Text("ERROR:", err))
}
err = setTimbreMode(ctx, int64(timbre))
if err != nil {
ctx.SendChain(message.Reply(ctx.Event.MessageID), message.Text(err))
return
}
ctx.SendChain(message.Reply(ctx.Event.MessageID), message.Text("成功"))
})
}
var (
noteMap = map[string]uint8{
"C": 60,
"Db": 61,
"D": 62,
"Eb": 63,
"E": 64,
"F": 65,
"Gb": 66,
"G": 67,
"Ab": 68,
"A": 69,
"Bb": 70,
"B": 71,
}
)
func str2music(ctx *zero.Ctx, input, midiFile string) (cmidiFile string, err error) {
err = mkMidi(ctx, midiFile, input)
if err != nil {
return
}
cmidiFile = strings.ReplaceAll(midiFile, ".mid", ".wav")
cmd := exec.Command("timidity", file.BOTPATH+"/"+midiFile, "-Ow", "-o", file.BOTPATH+"/"+cmidiFile)
err = cmd.Run()
return
}
func mkMidi(ctx *zero.Ctx, filePath, input string) error {
if file.IsExist(filePath) {
return nil
}
var (
clock smf.MetricTicks
tr smf.Track
)
tr.Add(0, smf.MetaMeter(4, 4))
tr.Add(0, smf.MetaTempo(72))
tr.Add(0, smf.MetaInstrument("Violin"))
timbre := getTimbreMode(ctx)
tr.Add(0, midi.ProgramChange(0, uint8(timbre)))
k := strings.ReplaceAll(input, " ", "")
var (
base uint8
level uint8
delay uint32
sleepFlag bool
lengthBytes = make([]byte, 0)
)
for i := 0; i < len(k); {
base = 0
level = 0
sleepFlag = false
lengthBytes = lengthBytes[:0]
for {
switch {
case k[i] == 'R':
sleepFlag = true
i++
case k[i] >= 'A' && k[i] <= 'G':
base = noteMap[k[i:i+1]] % 12
i++
case k[i] == 'b':
base--
i++
case k[i] == '#':
base++
i++
case k[i] >= '0' && k[i] <= '9':
level = level*10 + k[i] - '0'
i++
case k[i] == '<':
i++
for i < len(k) && (k[i] == '-' || (k[i] >= '0' && k[i] <= '9')) {
lengthBytes = append(lengthBytes, k[i])
i++
}
default:
return errors.Errorf("无法解析第%d个位置的%c字符", i, k[i])
}
if i >= len(k) || (k[i] >= 'A' && k[i] <= 'G') || k[i] == 'R' {
break
}
}
length, _ := strconv.Atoi(string(lengthBytes))
if sleepFlag {
if length >= 0 {
delay = clock.Ticks4th() * (1 << length)
} else {
delay = clock.Ticks4th() / (1 << -length)
}
continue
}
if level == 0 {
level = 5
}
tr.Add(delay, midi.NoteOn(0, o(base, level), 120))
if length >= 0 {
tr.Add(clock.Ticks4th()*(1<<length), midi.NoteOff(0, o(base, level)))
} else {
tr.Add(clock.Ticks4th()/(1<<-length), midi.NoteOff(0, o(base, level)))
}
delay = 0
}
tr.Close(0)
s := smf.New()
s.TimeFormat = clock
err := s.Add(tr)
if err != nil {
return err
}
f, err := os.Create(filePath)
if err != nil {
return err
}
_, err = s.WriteTo(f)
f.Close()
return err
}
func o(base uint8, oct uint8) uint8 {
if oct > 10 {
oct = 10
}
if oct == 0 {
return base
}
res := base + 12*oct
if res > 127 {
res -= 12
}
return res
}
func name(n uint8) string {
for k, v := range noteMap {
if v%12 == n%12 {
return k
}
}
return ""
}
func processOne(note string) uint8 {
k := strings.ReplaceAll(note, " ", "")
var (
base uint8
level uint8
)
for i := 0; i < len(k); i++ {
switch {
case k[i] >= 'A' && k[i] <= 'G':
base = noteMap[k[i:i+1]] % 12
case k[i] == 'b':
base--
case k[i] == '#':
base++
case k[i] >= '0' && k[i] <= '9':
level = level*10 + k[i] - '0'
}
}
if level == 0 {
level = 5
}
return o(base, level)
}
func mid2txt(midBytes []byte, trackNo int) (midStr string) {
var (
absTicksStart float64
absTicksEnd float64
startNote byte
endNote byte
defaultMetric = 960.0
)
_ = smf.ReadTracksFrom(bytes.NewReader(midBytes), trackNo).
Do(
func(te smf.TrackEvent) {
if !te.Message.IsMeta() {
b := te.Message.Bytes()
if te.Message.Is(midi.NoteOnMsg) && b[2] > 0 {
absTicksStart = float64(te.AbsTicks)
startNote = b[1]
}
if te.Message.Is(midi.NoteOffMsg) || (te.Message.Is(midi.NoteOnMsg) && b[2] == 0x00) {
absTicksEnd = float64(te.AbsTicks)
endNote = b[1]
if startNote == endNote {
sign := name(b[1])
level := b[1] / 12
length := (absTicksEnd - absTicksStart) / defaultMetric
midStr += sign
if level != 5 {
midStr += strconv.Itoa(int(level))
}
pow := int(math.Round(math.Log2(length)))
if pow >= -4 && pow != 0 {
midStr += "<" + strconv.Itoa(pow)
}
startNote = 0
endNote = 0
}
}
if (te.Message.Is(midi.NoteOnMsg) && b[2] > 0) && absTicksStart > absTicksEnd {
length := (absTicksStart - absTicksEnd) / defaultMetric
pow := int(math.Round(math.Log2(length)))
if pow == 0 {
midStr += "R"
} else if pow >= -4 {
midStr += "R<" + strconv.Itoa(pow)
}
}
}
},
)
return
}
func setTimbreMode(ctx *zero.Ctx, timbre int64) error {
gid := ctx.Event.GroupID
if gid == 0 {
gid = -ctx.Event.UserID
}
if timbre < 0 || timbre > 127 {
return errors.New("音色应该在0~127之间")
}
m, ok := ctx.State["manager"].(*ctrl.Control[*zero.Ctx])
if !ok {
return errors.New("no such plugin")
}
return m.SetData(gid, timbre)
}
func getTimbreMode(ctx *zero.Ctx) (index int64) {
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)
return index
}
return 40
}

45
plugin/moegoe/main.go Normal file
View File

@@ -0,0 +1,45 @@
// Package moegoe 日韩 VITS 模型拟声
package moegoe
import (
"fmt"
"net/url"
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"
)
const (
jpapi = "https://moegoe.azurewebsites.net/api/speak?text=%s&id=%d"
krapi = "https://moegoe.azurewebsites.net/api/speakkr?text=%s&id=%d"
)
var speakers = map[string]uint{
"宁宁": 0, "爱瑠": 1, "芳乃": 2, "茉子": 3, "丛雨": 4, "小春": 5, "七海": 6,
"Sua": 0, "Mimiru": 1, "Arin": 2, "Yeonhwa": 3, "Yuhwa": 4, "Seonbae": 5,
}
func init() {
en := control.Register("moegoe", &ctrl.Options[*zero.Ctx]{
DisableOnDefault: false,
Help: "moegoe\n" +
"- 让[宁宁|爱瑠|芳乃|茉子|丛雨|小春|七海]说(日语)\n" +
"- 让[Sua|Mimiru|Arin|Yeonhwa|Yuhwa|Seonbae]说(韩语)",
}).ApplySingle(ctxext.DefaultSingle)
en.OnRegex("^让(宁宁|爱瑠|芳乃|茉子|丛雨|小春|七海)说([A-Za-z\\s\\d\u3005\u3040-\u30ff\u4e00-\u9fff\uff11-\uff19\uff21-\uff3a\uff41-\uff5a\uff66-\uff9d.。,,、:;!?]+)$").Limit(ctxext.LimitByGroup).SetBlock(true).
Handle(func(ctx *zero.Ctx) {
text := ctx.State["regex_matched"].([]string)[2]
id := speakers[ctx.State["regex_matched"].([]string)[1]]
ctx.SendChain(message.Record(fmt.Sprintf(jpapi, url.QueryEscape(text), id)))
})
en.OnRegex("^让(Sua|Mimiru|Arin|Yeonhwa|Yuhwa|Seonbae)说([A-Za-z\\s\\d\u3131-\u3163\uac00-\ud7ff.。,,、:;!?]+)$").Limit(ctxext.LimitByGroup).SetBlock(true).
Handle(func(ctx *zero.Ctx) {
text := ctx.State["regex_matched"].([]string)[2]
id := speakers[ctx.State["regex_matched"].([]string)[1]]
ctx.SendChain(message.Record(fmt.Sprintf(krapi, url.QueryEscape(text), id)))
})
}

View File

@@ -12,7 +12,6 @@ var sr = reg.NewRegedit("reilia.fumiama.top:32664", "fumiama", "--")
func TestGetHoliday(t *testing.T) {
registry.Connect()
holidaymap = make(map[string]*Holiday)
h := GetHoliday("元旦")
registry.Close()
t.Fatal(h)
@@ -28,19 +27,19 @@ func TestSetHoliday(t *testing.T) {
if err != nil {
t.Fatal(err)
}
err = SetHoliday("春节", 7, 2022, 1, 31)
err = SetHoliday("春节", 7, 2023, 1, 21)
if err != nil {
t.Fatal(err)
}
err = SetHoliday("清明节", 1, 2022, 4, 3)
err = SetHoliday("清明节", 1, 2023, 4, 5)
if err != nil {
t.Fatal(err)
}
err = SetHoliday("劳动节", 1, 2022, 4, 30)
err = SetHoliday("劳动节", 1, 2023, 5, 1)
if err != nil {
t.Fatal(err)
}
err = SetHoliday("端午节", 1, 2022, 6, 3)
err = SetHoliday("端午节", 1, 2023, 6, 22)
if err != nil {
t.Fatal(err)
}

View File

@@ -21,29 +21,20 @@ func NewHoliday(name string, dur, year int, month time.Month, day int) *Holiday
return &Holiday{name: name, date: time.Date(year, month, day, 0, 0, 0, 0, time.Local), dur: time.Duration(dur) * time.Hour * 24}
}
var (
registry = reg.NewRegReader("reilia.fumiama.top:32664", "fumiama")
holidaymap map[string]*Holiday
)
var registry = reg.NewRegReader("reilia.fumiama.top:32664", "fumiama")
// GetHoliday 从 reg 服务器获取节日
func GetHoliday(name string) *Holiday {
var dur, year int
var month time.Month
var day int
h, ok := holidaymap[name]
if ok {
return h
}
ret, err := registry.Get("holiday/" + name)
if err != nil {
return NewHoliday(name+err.Error(), 0, 0, 0, 0)
}
fmt.Sscanf(ret, "%d_%d_%d_%d", &dur, &year, &month, &day)
logrus.Debugln("[moyu]获取节日:", name, dur, year, month, day)
h = NewHoliday(name, dur, year, month, day)
holidaymap[name] = h
return h
return NewHoliday(name, dur, year, month, day)
}
// String 获取两个时间相差

62
plugin/moyu/run.go Normal file
View File

@@ -0,0 +1,62 @@
// Package moyu 摸鱼
package moyu
import (
"sync"
"time"
ctrl "github.com/FloatTech/zbpctrl"
"github.com/FloatTech/zbputils/control"
zero "github.com/wdvxdr1123/ZeroBot"
"github.com/wdvxdr1123/ZeroBot/message"
)
var (
msg message.Message
mu sync.Mutex
lastupdate time.Time
)
func init() { // 插件主体
control.Register("moyu", &ctrl.Options[*zero.Ctx]{
DisableOnDefault: true,
Help: "moyu\n" +
"- /启用 moyu\n" +
"- /禁用 moyu\n" +
"- 记录在\"0 10 * * *\"触发的指令\n" +
" - 摸鱼提醒",
}).OnFullMatch("摸鱼提醒").SetBlock(true).
Handle(func(ctx *zero.Ctx) {
mu.Lock()
defer mu.Unlock()
if msg == nil || time.Since(lastupdate) > time.Hour*20 {
if registry.Connect() != nil {
return
}
msg = message.Message{
message.Text(time.Now().Format("2006-01-02")),
message.Text("上午好,摸鱼人!\n工作再累一定不要忘记摸鱼哦有事没事起身去茶水间去厕所去廊道走走别老在工位上坐着钱是老板的,但命是自己的。\n"),
message.Text(weekend()),
message.Text("\n"),
message.Text(GetHoliday("元旦")),
message.Text("\n"),
message.Text(GetHoliday("春节")),
message.Text("\n"),
message.Text(GetHoliday("清明节")),
message.Text("\n"),
message.Text(GetHoliday("劳动节")),
message.Text("\n"),
message.Text(GetHoliday("端午节")),
message.Text("\n"),
message.Text(GetHoliday("中秋节")),
message.Text("\n"),
message.Text(GetHoliday("国庆节")),
message.Text("\n"),
message.Text("上班是帮老板赚钱,摸鱼是赚老板的钱!最后,祝愿天下所有摸鱼人,都能愉快的渡过每一天…"),
}
_ = registry.Close()
lastupdate = time.Now()
}
ctx.Send(msg)
})
}

View File

@@ -0,0 +1,29 @@
// Package moyucalendar 摸鱼人日历
package moyucalendar
import (
ctrl "github.com/FloatTech/zbpctrl"
"github.com/FloatTech/zbputils/control"
"github.com/FloatTech/zbputils/web"
zero "github.com/wdvxdr1123/ZeroBot"
"github.com/wdvxdr1123/ZeroBot/message"
)
func init() {
control.Register("moyucalendar", &ctrl.Options[*zero.Ctx]{
DisableOnDefault: true,
Help: "摸鱼人日历\n" +
"- /启用 moyucalendar\n" +
"- /禁用 moyucalendar\n" +
"- 记录在\"30 8 * * *\"触发的指令\n" +
" - 摸鱼人日历",
}).OnFullMatch("摸鱼人日历").SetBlock(true).
Handle(func(ctx *zero.Ctx) {
data, err := web.GetData("https://api.vvhan.com/api/moyu")
if err != nil {
ctx.SendChain(message.Text("ERROR:", err))
return
}
ctx.SendChain(message.ImageBytes(data))
})
}

View File

@@ -5,36 +5,32 @@ import (
"crypto/md5"
"encoding/hex"
"fmt"
"io/ioutil"
"io"
"net/http"
"net/url"
"strings"
"time"
"github.com/FloatTech/zbputils/web"
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/extension/rate"
"github.com/wdvxdr1123/ZeroBot/message"
"github.com/FloatTech/ZeroBot-Plugin/control"
)
var limit = rate.NewManager(time.Minute*3, 5)
func init() {
control.Register("music", &control.Options{
control.Register("music", &ctrl.Options[*zero.Ctx]{
DisableOnDefault: false,
Help: "点歌\n" +
"- 点歌[xxx]\n" +
"- 网易点歌[xxx]\n" +
"- 酷我点歌[xxx]\n" +
"- 酷狗点歌[xxx]",
}).OnRegex("^(.{0,2})点歌(.{1,25})$").SetBlock(true).FirstPriority().
}).OnRegex(`^(.{0,2})点歌\s?(.{1,25})$`).SetBlock(true).Limit(ctxext.LimitByUser).
Handle(func(ctx *zero.Ctx) {
if !limit.Load(ctx.Event.UserID).Acquire() {
ctx.SendChain(message.Text("请稍后重试0x0..."))
return
}
// switch 平台
switch ctx.State["regex_matched"].([]string)[1] {
case "酷我":
@@ -141,81 +137,28 @@ func kugou(keyword string) message.MessageSegment {
}
// cloud163 返回网易云音乐卡片
func cloud163(keyword string) message.MessageSegment {
headers := http.Header{
"Content-Type": []string{"application/x-www-form-urlencoded"},
"User-Agent": []string{"Mozilla/5.0 (Windows NT 6.1; Win64; x64; rv:84.0) Gecko/20100101 Firefox/84.0"},
func cloud163(keyword string) (msg message.MessageSegment) {
requestURL := "https://music.cyrilstudio.top/search?keywords=" + url.QueryEscape(keyword)
data, err := web.GetData(requestURL)
if err != nil {
msg = message.Text("ERROR:", err)
return
}
data := url.Values{
"offset": []string{"0"},
"total": []string{"true"},
"limit": []string{"9"},
"type": []string{"1"},
"s": []string{keyword},
}
// 搜索音乐信息 第一首歌
info := gjson.ParseBytes(netPost("http://music.163.com/api/search/pc", data, headers)).Get("result.songs.0")
// 返回音乐卡片
return message.CustomMusic(
fmt.Sprintf("http://y.music.163.com/m/song?id=%d", info.Get("id").Int()),
fmt.Sprintf("http://music.163.com/song/media/outer/url?id=%d.mp3", info.Get("id").Int()),
info.Get("name").Str,
).Add("content", info.Get("artists.0.name").Str).Add("image", info.Get("album.blurPicUrl").Str)
msg = message.Music("163", gjson.ParseBytes(data).Get("result.songs.0.id").Int())
return
}
// qqmusic 返回QQ音乐卡片
func qqmusic(keyword string) message.MessageSegment {
// 搜索音乐信息 第一首歌
h1 := http.Header{
"User-Agent": []string{"Mozilla/5.0 (Windows NT 6.1; Win64; x64; rv:84.0) Gecko/20100101 Firefox/84.0"},
func qqmusic(keyword string) (msg message.MessageSegment) {
requestURL := "https://c.y.qq.com/soso/fcgi-bin/client_search_cp?w=" + url.QueryEscape(keyword)
data, err := web.RequestDataWith(web.NewDefaultClient(), requestURL, "GET", "", web.RandUA())
if err != nil {
msg = message.Text("ERROR:", err)
return
}
search, _ := url.Parse("https://c.y.qq.com/soso/fcgi-bin/client_search_cp")
search.RawQuery = url.Values{
"w": []string{keyword},
}.Encode()
res := netGet(search.String(), h1)
info := gjson.ParseBytes(res[9 : len(res)-1]).Get("data.song.list.0")
// 获得音乐直链
h2 := http.Header{
"User-Agent": []string{"Mozilla/5.0 (iPhone; CPU iPhone OS 9_1 like Mac OS X) AppleWebKit/601.1.46 (KHTML, like Gecko) Version/9.0 Mobile/13B143 Safari/601.1"},
"referer": []string{"http://y.qq.com"},
}
music, _ := url.Parse("https://u.y.qq.com/cgi-bin/musicu.fcg")
music.RawQuery = url.Values{
"data": []string{`{"req": {"module": "CDN.SrfCdnDispatchServer", "method": "GetCdnDispatch", "param": {"guid": "3982823384", "calltype": 0, "userip": ""}}, "req_0": {"module": "vkey.GetVkeyServer", "method": "CgiGetVkey", "param": {"guid": "3982823384", "songmid": ["` + info.Get("songmid").Str + `"], "songtype": [0], "uin": "0", "loginflag": 1, "platform": "20"}}, "comm": {"uin": 0, "format": "json", "ct": 24, "cv": 0}}`},
}.Encode()
audio := gjson.ParseBytes(netGet(music.String(), h2))
// 获得音乐封面
image := "https://y.gtimg.cn/music/photo_new/" +
find(
`photo_new\u002F`,
"?max_age",
string(
netGet("https://y.qq.com/n/yqq/song/"+info.Get("songmid").Str+".html", nil),
),
)
// 返回音乐卡片
return message.CustomMusic(
"https://y.qq.com/n/yqq/song/"+info.Get("songmid").Str+".html",
"https://isure.stream.qqmusic.qq.com/"+audio.Get("req_0.data.midurlinfo.0.purl").Str,
info.Get("songname").Str,
).Add("content", info.Get("singer.0.name").Str).Add("image", image)
}
// find 返回 pre 到 suf 之间的文本
func find(pre string, suf string, str string) string {
n := strings.Index(str, pre)
if n == -1 {
n = 0
} else {
n += len(pre)
}
str = str[n:]
m := strings.Index(str, suf)
if m == -1 {
m = len(str)
}
return str[:m]
info := gjson.ParseBytes(data[9 : len(data)-1]).Get("data.song.list.0")
msg = message.Music("qq", info.Get("songid").Int())
return
}
// md5str 返回字符串 MD5
@@ -236,20 +179,6 @@ func netGet(url string, header http.Header) []byte {
return nil
}
defer res.Body.Close()
result, _ := ioutil.ReadAll(res.Body)
return result
}
// netPost 返回请求数据
func netPost(url string, data url.Values, header http.Header) []byte {
client := &http.Client{}
request, _ := http.NewRequest("POST", url, strings.NewReader(data.Encode()))
request.Header = header
res, err := client.Do(request)
if err != nil {
return nil
}
defer res.Body.Close()
result, _ := ioutil.ReadAll(res.Body)
result, _ := io.ReadAll(res.Body)
return result
}

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