Compare commits

..

230 Commits

Author SHA1 Message Date
fumiama
596c75eeca ✏️ make lint happy 2021-12-19 17:43:46 +08:00
fumiama
67a493ee84 ✏️ 更新 go.sum 2021-12-19 17:36:13 +08:00
fumiama
f9c7d9419e ✏️ make lint happy 2021-12-19 17:27:32 +08:00
github-actions[bot]
1a1a006283 🎨 改进代码样式 2021-12-19 08:06:16 +00:00
himawari
fd85ce0923 feat:添加铅笔小说搜索插件 (#89)
* feat:添加铅笔小说搜索插件

* fix:小修改help

* fix:微调语句

* fix:替换编码库

* fix:不修改go.sum
2021-12-19 16:05:29 +08:00
fumiama
5ff9069ff2 ✏️ make lint happy 2021-12-19 16:04:14 +08:00
fumiama
4ad7ff7971 ✏️ make lint happy 2021-12-19 11:39:30 +08:00
fumiama
a9f98763c4 ✏️ edit README 2021-12-17 12:30:48 +08:00
fumiama
3b637fad78 ✏️ aifalese 磁盘空间占用仅输出非0挂载点 2021-12-17 12:28:38 +08:00
fumiama
f202797964 ✏️ fix shindan by @Yiwen-Chan 2021-12-17 11:36:50 +08:00
fumiama
242d780048 ✏️ edit README 2021-12-16 17:02:03 +08:00
fumiama
bf23364097 ✏️ edit README 2021-12-16 17:00:11 +08:00
fumiama
7eaac7a20b ✏️ fix gist panic 2021-12-16 16:27:25 +08:00
fumiama
45448e6f53 Merge branch 'master' of https://github.com/FloatTech/ZeroBot-Plugin 2021-12-16 15:30:29 +08:00
fumiama
08ee458a27 ✏️ edit README 2021-12-16 15:30:19 +08:00
github-actions[bot]
3f97541956 🎨 改进代码样式 2021-12-16 07:28:22 +00:00
fumiama
e2c603338e ✏️ drop all pb & 增加 gist 审批 2021-12-16 15:27:47 +08:00
fumiama
a363623df9 ✏️ drop pb in timer 2021-12-16 12:51:41 +08:00
github-actions[bot]
b871573dc3 🎨 改进代码样式 2021-12-15 14:29:05 +00:00
himawari
ad33c1ca3c 添加coser插件 (#87)
* feat:添加coser

* fix:删除测试代码

* fix:莫名其妙的库

* fix:加个限制器

* fix:修改限制器

* fix:改回去

* fix:改回去

* fix:添加标题

* fix:t大写

* fix:异常注释

* feat:添加文档

* feat:合并消息私聊无效

* feat:改个顺序

* Update go.sum

Co-authored-by: Guohuiyuan <haibaraguo@yeahka.com>
Co-authored-by: 源文雨 <41315874+fumiama@users.noreply.github.com>
2021-12-15 22:28:06 +08:00
fumiama
810cb284b6 ✏️ 优化 ci 2021-12-15 22:26:05 +08:00
fumiama
d0ffb25594 ✏️ 优化 ci 2021-12-15 22:21:13 +08:00
fumiama
2b6796d1d1 ✏️ 优化 ci 2021-12-15 22:15:31 +08:00
fumiama
52093f3dd3 ✏️ 优化 aifalse 2021-12-15 16:01:56 +08:00
fumiama
2a5c1ac92c ✏️ print kanban on start 2021-12-15 15:38:05 +08:00
fumiama
6e0222f5e2 ✏️ 优化 aifalse 2021-12-15 15:30:48 +08:00
fumiama
4bebf79266 ✏️ fix far connect in updater 2021-12-15 13:50:41 +08:00
fumiama
b6dadc142b ✏️ more log in updater 2021-12-15 13:00:10 +08:00
github-actions[bot]
1cbc49251b 🎨 改进代码样式 2021-12-14 06:27:57 +00:00
fumiama
d2d5b5863b ✏️ drop pb in Diana 2021-12-14 14:27:19 +08:00
fumiama
65f9203597 ✏️ drop pb in fortune 2021-12-14 13:51:15 +08:00
fumiama
4c1926e395 ✏️ fix fortune config 2021-12-14 13:41:24 +08:00
fumiama
516d937149 ✏️ 添加公告看板 2021-12-14 13:35:00 +08:00
github-actions[bot]
7af17c3c65 🎨 改进代码样式 2021-12-14 05:34:20 +00:00
fumiama
33b85207f5 ✏️ 添加公告看板 2021-12-14 13:33:28 +08:00
fumiama
218119e10e ✏️ 优化 updater log 2021-12-14 13:18:26 +08:00
fumiama
4485d3d5a9 ✏️ 优化 updater log 2021-12-14 13:17:46 +08:00
fumiama
fcf62a31e1 ✏️ edit README 2021-12-14 12:09:30 +08:00
fumiama
ebdc83809c ✏️ edit README 2021-12-14 12:06:33 +08:00
fumiama
61a3c30a45 ✏️ edit README 2021-12-14 12:04:34 +08:00
fumiama
0e4cab7221 ✏️ edit README 2021-12-14 12:02:38 +08:00
fumiama
865c90a85a ✏️ 优化 updater reg 关闭逻辑 2021-12-14 12:00:44 +08:00
fumiama
c9b06a4fa8 ✏️ edit README 2021-12-13 21:57:13 +08:00
fumiama
0e75143a42 ✏️ edit README 2021-12-13 21:42:52 +08:00
fumiama
ce333c5793 ✏️ 更改为v1.1.2 2021-12-13 21:23:04 +08:00
github-actions[bot]
6b4045db45 🎨 改进代码样式 2021-12-13 13:15:16 +00:00
fumiama
3fba704c77 ✏️ 更新依赖 2021-12-13 21:14:29 +08:00
fumiama
0712a80ea0 ✏️ 同步时序 2021-12-13 21:06:11 +08:00
fumiama
8969c44b3f ✏️ 统一懒加载验证 2021-12-13 20:49:00 +08:00
fumiama
f7a3c0e6c6 ✏️ 加入 kuji.db 2021-12-13 13:11:42 +08:00
himawari
b1852a1de6 新增解签指令和修复sleep的时间判断 (#86) 2021-12-13 12:29:21 +08:00
fumiama
5645fa0168 ✏️ sql 防注入 2021-12-08 17:35:22 +08:00
fumiama
c824b2e0b4 ✏️ 迁移到 utils/sql 2021-12-08 17:30:16 +08:00
fumiama
1c0d2b2f95 ✏️ 迁移到 utils/sql 2021-12-08 17:27:29 +08:00
fumiama
10b8ab297a ✏️ 迁移到 utils/sql 2021-12-08 17:13:00 +08:00
fumiama
e8b0e475d7 ✏️ 迁移数据库本体 2021-12-08 17:10:22 +08:00
fumiama
7fc4a6425b ✏️ 迁移到 utils/sql 2021-12-08 16:44:46 +08:00
himawari
a8a3c48e6c fix:修复sql错误 (#85)
* fix:捕获协程异常

* feat:添加睡眠时间

* feat:添加睡眠管理功能

* fix:修改对标时间

* fix:修复sql错误

* feat:添加书评功能

* fix:调整格式

* fix:去掉recover

* feat:更新文档

Co-authored-by: Guohuiyuan <haibaraguo@yeahka.com>
2021-12-08 13:12:40 +08:00
fumiama
9fca69302b Merge branch 'master' of https://github.com/FloatTech/ZeroBot-Plugin 2021-12-07 23:29:08 +08:00
fumiama
ba6811224e add gocqzbp in README 2021-12-07 23:28:57 +08:00
Kanri
ca4a682794 ✏️ 优化戳一戳命令牌为群号 2021-12-07 09:01:30 +08:00
fumiama
b9ddcfc3d7 ✏️ 小修正 2021-12-06 21:17:41 +08:00
fumiama
b20cb1c0a0 ✏️ nwife 防注入 2021-12-06 19:33:47 +08:00
Kanri
1d9813691c 🎨️ 优化搜索仓库默认带图片 2021-12-06 09:03:09 +08:00
fumiama
08f8954e03 ✏️ 小修正 2021-12-04 21:31:14 +08:00
fumiama
610f04ab87 ✏️ 人肉 lint 2021-12-04 20:56:43 +08:00
himawari
793cac09cb 新增统计睡眠时间和添加协程里的异常捕获 (#84)
* fix:捕获协程异常

* feat:添加睡眠时间

* feat:添加睡眠管理功能

* fix:修改对标时间

* fix:修改注释函数名,使用一个时间

* feat:让大家能够更加清楚的看到每一个功能

* feat:把群管放在control,增加退群信息

* fix:不会recover

* fix:原本的正则限制机器人回复回复的消息,故去掉正则

* fix:添加服务详情,整理manager

* feat:chat进control

Co-authored-by: Guohuiyuan <haibaraguo@yeahka.com>
2021-12-04 20:46:24 +08:00
fumiama
d0d07604e5 ✏️ 增加一点注释 2021-12-03 23:12:45 +08:00
fumiama
fcf6d087a7 ✏️ dep: use RomiChan/protobuf 2021-12-03 13:36:43 +08:00
yuanyan3060
35587ccb85 Update manager.go (#83)
避免bot自身加入群聊时触发群欢迎。
2021-12-02 11:23:54 +08:00
fumiama
9df5b6cc33 ✏️ 抽 wife 插件 新增功能 2021-12-01 15:40:18 +08:00
fumiama
ffeedf576d control 新增 配置数据 2021-12-01 14:52:54 +08:00
fumiama
151ce216ab 新增 抽wife 插件 2021-12-01 12:53:03 +08:00
fumiama
20835f9858 ✏️ 修正 aifalse man 2021-12-01 11:46:59 +08:00
fumiama
63a61f2fb5 ✏️ 修正 本地 setu 插件 2021-11-29 22:49:31 +08:00
fumiama
c553248cf4 ✏️ 修正 本地 setu 插件 2021-11-29 22:31:02 +08:00
fumiama
cdd1dc68da ✏️ 修正 本地 setu 插件 2021-11-29 22:29:07 +08:00
fumiama
37af3c1498 ✏️ edit README 2021-11-29 21:03:15 +08:00
fumiama
1bc3847345 ✏️ manager 插件 hint 小修正 2021-11-29 20:45:02 +08:00
fumiama
f14d8ab5f6 ✏️ edit README 2021-11-29 20:39:57 +08:00
fumiama
bb63b98f39 ✏️ 更改版本为 v1.2.1 2021-11-29 20:27:42 +08:00
fumiama
f3f7e2d3c1 ✏️ 修正 sql 2021-11-29 20:25:14 +08:00
fumiama
89121cfc57 新增 本地 setu 插件 2021-11-29 15:33:15 +08:00
fumiama
d8a41a5ee1 ✏️ 修复 control 找不到服务时 panic (#81) 2021-11-27 01:10:51 +08:00
fumiama
f8ca9355d2 ✏️ 修复 control 全局禁用 2021-11-27 00:56:22 +08:00
fumiama
d4bceb1922 ✏️ 修复 control 全局禁用 2021-11-27 00:49:50 +08:00
fumiama
876d85ac4e ✏️ 修复 control 全局禁用 2021-11-27 00:48:32 +08:00
fumiama
ab34930beb ✏️ 修复 control 全局禁用 2021-11-27 00:40:48 +08:00
fumiama
35f7450ab2 ✏️ 修复 control 全局禁用 2021-11-27 00:36:29 +08:00
fumiama
2917ff6701 ✏️ 持久化 fansdaily 2021-11-26 19:45:48 +08:00
fumiama
d1656b25c9 ✏️ 默认禁用 wtf 2021-11-26 19:22:02 +08:00
fumiama
e6a20866b6 ✏️ 优化 getea 2021-11-26 18:48:05 +08:00
fumiama
9de065d31a ✏️ 修正 moyu 提醒 2021-11-26 11:39:43 +08:00
fumiama
37d27e07e9 ✏️ 小修正 2021-11-25 22:39:25 +08:00
fumiama
01853768db ✏️ 小修正 2021-11-23 23:25:04 +08:00
fumiama
9d3787dfc2 ✏️ 小修正 2021-11-23 23:05:59 +08:00
fumiama
e9f145056d ✏️ 小修正 2021-11-23 22:51:18 +08:00
fumiama
ea17943bff ✏️ 新增 wtf 2021-11-23 22:40:49 +08:00
fumiama
180c32ef50 ✏️ 优化 moyu 格式 2021-11-23 21:35:42 +08:00
fumiama
17bb844360 ✏️ vtb drop unsafe 2021-11-23 13:05:16 +08:00
fumiama
3d0a161176 ✏️ 修正 badge 2021-11-23 12:46:05 +08:00
fumiama
1148867d3c Merge branch 'master' of https://github.com/FloatTech/ZeroBot-Plugin 2021-11-23 12:33:33 +08:00
fumiama
c83d01e977 ✏️ 修复摸鱼 cron 2021-11-23 12:33:24 +08:00
ByronLeeeee
7c42d6f857 修复代理域名,解决PC端无法看图问题 (#80)
1.代理域名从.cat替换成.re;
2.Large尺寸图无法在PC QQ看见,已更改成Medium尺寸
2021-11-23 12:30:40 +08:00
fumiama
ac1c6bc74b ✏️ 在 main 添加摸鱼 2021-11-22 22:40:01 +08:00
fumiama
12f96bb95c ✏️ 修复 vtb panic 判断失误 2021-11-22 22:33:29 +08:00
fumiama
fa063a05f7 ✏️ 修复 vtb 自动下载数据库失败 2021-11-22 22:29:07 +08:00
fumiama
51d10d3234 升级加密算法 2021-11-22 22:22:13 +08:00
fumiama
2d2db15813 新增 moyu 说明 2021-11-22 22:06:22 +08:00
fumiama
006c782819 b14 增加口令加解密 2021-11-22 22:04:31 +08:00
fumiama
37e36fb886 ✏️ 修复 sql 加载失败 2021-11-22 21:48:22 +08:00
fumiama
0e026a8b3b ✏️ 修复 webgui 监听端口问题 2021-11-22 19:12:29 +08:00
fumiama
f37daa5ca6 ✏️ 优化 vtbquotation 2021-11-22 18:56:52 +08:00
fumiama
dafcf7049b ✏️ 修正 moyu 逻辑 2021-11-22 17:53:10 +08:00
fumiama
207da3fcd9 ✏️ 修正 moyu 逻辑 2021-11-22 17:49:37 +08:00
fumiama
a3fbe5d747 ✏️ 优化 moyu,升级依赖 2021-11-22 13:41:55 +08:00
散无友纪
c1717c71bd 摸鱼提醒 (#79)
* Add files via upload

* Update run.go

* Update run.go

* Update run.go
2021-11-22 13:07:04 +08:00
himawari
bcd111f585 小小的修改一下cron表达式 (#77)
* fix:小修一下表达式

* fix:小修一下表达式
2021-11-22 13:01:17 +08:00
github-actions[bot]
d6f1113201 🎨 改进代码样式 2021-11-20 05:35:43 +00:00
himawari
e812d2a074 添加vtb语音包 (#74)
* fix:给msg进行url编码,解决青云客接口返回错误

* feat:增加一个vtbkeyboard.moe接口

* fix:id添加映射

* feat:优化逻辑

* feat:优化逻辑

* feat:优化逻辑

* feat:优化逻辑

* feat:增加一个随机vtb

* feat:增加一个随机vtb

* refactor:更改代码逻辑

* feat:更新数据库

* feat:更新数据库

* feat:更新数据库

* feat:更新数据库

* feat:更新数据库驱动

* feat:增加定时更新数据脚本

* feat:更换驱动

* fix:更新zerobot

* fix:修改全局变量

* fix:微调位置

* fix:解决modernc.org/sqlite版本冲突

* fix:解决go.sum冲突,更换cron库
2021-11-20 13:34:37 +08:00
huoxue1
a537306307 webui功能添加 (#75)
* feat: webui添加功能

为webui添加处理request的请求
包括好友添加请求,群邀请请求,群申请加入请求

* feat: webui添加功能

为webui添加手动处理request的请求的功能
包括好友添加请求,群邀请请求,群申请加入请求

* fix: 修改request对象为指针存储
2021-11-12 18:12:31 +08:00
fumiama
206889bdc5 更新 zb 到 v1.4.1 2021-11-11 20:39:49 +08:00
fumiama
58c7b5c818 ✏️ 修复 lolicon 无法下载 2021-11-09 13:19:33 +08:00
fumiama
eace561f73 ✏️ 修复 fortune 无法下载 2021-11-09 13:09:11 +08:00
fumiama
e2032cdc74 ✏️ 修复 webgui 无法启动 2021-11-09 13:01:01 +08:00
源文雨
34bc8e3a3c Update tl.go 2021-11-09 12:41:41 +08:00
源文雨
1304611ab7 Update http.go 2021-11-09 12:41:08 +08:00
himawari
7a0ce4b5d6 fix:给msg进行url编码,解决青云客接口返回错误 (#73) 2021-11-06 18:55:34 +08:00
github-actions[bot]
835df33e11 🎨 改进代码样式 2021-11-03 05:37:24 +00:00
fumiama
fb80c3606b 新增 搜番 插件 2021-11-03 13:36:46 +08:00
fumiama
f0c80693bb 新增 搜番 插件 2021-11-03 13:34:54 +08:00
fumiama
6997c2f2b9 ✏️ 优化 main 2021-11-02 17:18:55 +08:00
fumiama
cb1057cd5e ⬆️ 更新 zb 到 v1.4.0 2021-11-02 17:18:21 +08:00
fumiama
eb1e2de917 ✏️ 优化 win 下 log 格式 2021-11-01 22:12:56 +08:00
fumiama
4b9b66e7d0 ✏️ 调整 acgimage 限速器 2021-10-31 22:53:11 +08:00
fumiama
c1e87ace78 atri 插件 引入 control 2021-10-31 19:28:31 +08:00
fumiama
ba9eb9c023 acgimage 增加限速器 2021-10-31 19:19:13 +08:00
fumiama
3084d4ad54 atri 插件 引入 control 2021-10-31 19:13:20 +08:00
fumiama
fcb13c19a1 群管自闭增加匹配项 2021-10-31 19:01:01 +08:00
fumiama
d6b81d0363 ✏️ v1.1.8 -> v1.2.0 2021-10-29 13:15:50 +08:00
fumiama
fdd1135a66 ✏️ 修正 timer 2021-10-29 12:54:11 +08:00
fumiama
f9ce439e56 ✏️ 修正正则与cd认证 2021-10-29 12:48:56 +08:00
fumiama
9608d1e736 ✏️ 小修正 2021-10-28 21:24:01 +08:00
fumiama
776d7fbbd9 ✏️ 小修正 2021-10-28 21:06:39 +08:00
fumiama
7c69c24362 ✏️ 在 main.go 引入 b14 2021-10-28 21:04:48 +08:00
fumiama
d205d48166 新增 插件冲突检测、b14 加解密 添加随机等待 1~2s 工具函数 2021-10-28 21:03:09 +08:00
fumiama
dd32aec462 新增插件翻译 by @sky-rainy 2021-10-28 17:05:59 +08:00
fumiama
c0327fe733 manager cron 完善 2021-10-28 11:40:30 +08:00
fumiama
6f4c40f450 差异化 pb 2021-10-27 23:55:54 +08:00
fumiama
022ee6cd95 manager 增加 cron 2021-10-27 23:50:46 +08:00
Kanri
085e04c9ba 🎨 改进结构 2021-10-27 22:28:19 +08:00
fumiama
19f58acb79 改用增强版 cron 2021-10-27 20:45:08 +08:00
fumiama
90dd60b942 升级依赖 & hs drop req 2021-10-27 19:35:57 +08:00
fumiama
755bfeaa5b fortune 增加下载锁;优化 gui 插件控制 2021-10-27 12:42:34 +08:00
fumiama
82ee2f6b0a 更改版本号到 v1.1.8 2021-10-27 10:34:18 +08:00
fumiama
500d03f293 ✏️ 优化打印 2021-10-27 00:19:17 +08:00
fumiama
ee99abe6ff 修复 timer 计时错误 2021-10-27 00:10:33 +08:00
fumiama
0563006c55 修复 timer 计时错误 2021-10-26 22:24:28 +08:00
fumiama
31db6fa4fb 修复 timer 计时错误 2021-10-26 22:09:01 +08:00
fumiama
043b543cfa 修复 timer 计时错误 2021-10-26 16:05:28 +08:00
fumiama
7b52599da7 修复 timer 计时错误 2021-10-26 15:22:00 +08:00
fumiama
4dc8e5619c 优化 timer 资源消耗 2021-10-26 15:05:32 +08:00
fumiama
21ff752322 ✏️ 离散化加载时间 2021-10-26 13:18:48 +08:00
fumiama
0f687d6e95 🔙 合并 timer 2021-10-26 13:11:45 +08:00
fumiama
22e23efbdc ✏️ 修复 sql 查询异常 2021-10-26 00:48:13 +08:00
fumiama
38935937ae control 增加还原;调整禁用优先级 2021-10-26 00:05:45 +08:00
fumiama
a3685e2e83 ✏️ 尝试解决win下路径异常 2021-10-24 18:13:12 +08:00
fumiama
3a5e391191 修正代码问题 2021-10-23 11:51:32 +08:00
fumiama
09a9dd53fd 修正代码问题 2021-10-23 01:09:15 +08:00
github-actions[bot]
5e064249be 🎨 改进代码样式 2021-10-22 16:55:19 +00:00
fumiama
67555512e7 搜图下载使用animeapi,将文件判存移至utils 2021-10-23 00:54:38 +08:00
fumiama
591df6439c 更新 goreleaser 2021-10-22 11:37:40 +08:00
fumiama
7dfb7dfe24 更新 goreleaser 2021-10-22 11:20:01 +08:00
Kanri
8e56f137b6 ⬆️ 更新依赖 2021-10-22 09:36:11 +08:00
源文雨
3b61c73c17 Update README.md 2021-10-20 22:56:31 +08:00
fumiama
44dac98b37 Merge branch 'master' of https://github.com/FloatTech/ZeroBot-Plugin 2021-10-18 15:25:06 +08:00
fumiama
1a7f98379a ✏️ 修正 acgimage 发图 2021-10-18 15:24:56 +08:00
Kanri
4109ef0612 Update main.go 2021-10-17 23:56:34 +08:00
fumiama
93a9bf6c5b 更新 ZeroBot 到 v1.3.2 2021-10-16 19:07:41 +08:00
fumiama
0b7b35cdcc control 支持全局禁用&个人用户 2021-10-16 14:02:44 +08:00
fumiama
350ef86dd6 ✏️ control 减少 defer 开销 2021-10-16 13:33:12 +08:00
fumiama
2d69ae2e42 ✏️ driver 使用 api 写法 2021-10-16 13:25:13 +08:00
Kanri
e95367f4b2 Merge pull request #70 from Yiwen-Chan/master
⬆️ 更新依赖
2021-10-16 11:11:59 +08:00
Kanri
3101911aa2 ⬆️ 更新依赖 2021-10-16 11:09:26 +08:00
Kanri
ac73087f93 Merge pull request #69 from Yiwen-Chan/master
🎨 ctx.Send -> ctx.SendChain
2021-10-15 22:05:02 +08:00
Kanri
d375a3bbbb 🎨 小修改 2021-10-15 22:02:14 +08:00
Kanri
c46ca1d4c2 🎨 ctx.Send -> ctx.SendChain 2021-10-15 21:43:47 +08:00
fumiama
2a1cca8ebb ✏️ 防止文件名重复 2021-10-15 16:28:31 +08:00
fumiama
d1fdc989b6 ✏️ 防止文件名重复 2021-10-15 16:21:01 +08:00
fumiama
672215f753 ✏️ windows取消arm系 2021-10-15 16:10:58 +08:00
fumiama
4160175fcf ✏️ 修改编译逻辑 2021-10-15 16:02:11 +08:00
fumiama
8f8ba55fc9 ✏️ 改用 gocq 同款 release 2021-10-15 16:00:54 +08:00
fumiama
1dbb4aa837 ✏️ 改用 gocq 同款 release 2021-10-15 15:57:42 +08:00
fumiama
c183c4f17d ✏️ 修正zip缓存位置 2021-10-15 15:27:19 +08:00
fumiama
fa6d14d89a ✏️ 修正zip缓存位置 2021-10-15 15:10:09 +08:00
fumiama
65b92b9da3 更新 README 2021-10-15 14:43:31 +08:00
fumiama
a7056c4601 更新 README 2021-10-14 23:38:52 +08:00
fumiama
1dce0f7859 更新 README 2021-10-14 23:32:49 +08:00
fumiama
bdffbfab67 ✏️ 分离 data 2021-10-14 23:07:13 +08:00
fumiama
98b21d0fdf ✏️ str byte 转换改用 zb 的工具 2021-10-14 19:51:24 +08:00
fumiama
cccea70db1 ✏️ 分离 dyloader 2021-10-14 16:26:01 +08:00
fumiama
d2bcd0bc9f Merge branch 'master' of https://github.com/FloatTech/ZeroBot-Plugin 2021-10-14 13:46:44 +08:00
fumiama
08653fd3b6 🚮 删除多余文件 2021-10-14 13:46:30 +08:00
fumiama
75d0671d9b dyloader 缩小插件体积 2021-10-14 13:45:14 +08:00
fumiama
51ec3c32a9 dyloader 缩小插件体积 2021-10-14 13:44:16 +08:00
fumiama
ec0032c5ab dyloader 移除 win,增加加载插件 2021-10-14 00:06:13 +08:00
fumiama
2723f3662c ✏️ 禁用 dyloader 2021-10-13 18:40:40 +08:00
fumiama
539b050b97 ✏️ 完成插件卸载 2021-10-13 18:39:54 +08:00
fumiama
a0b9623a9f dyloader 增加 win,优化 register 2021-10-13 11:07:34 +08:00
fumiama
6b263418a0 升级 zb 到 v1.3.0 2021-10-13 00:34:43 +08:00
fumiama
92989ce9fd control 增加 Delete 2021-10-12 19:16:01 +08:00
fumiama
173925b57a 增加动态加载,添加默认禁用 2021-10-11 21:44:46 +08:00
fumiama
43735722bf ✏️ 收集 max / min 到 data 包 2021-10-11 14:24:22 +08:00
fumiama
d89e7aebec 增加-u参数说明 2021-10-11 13:35:03 +08:00
fumiama
4677d789f2 增加-u参数 2021-10-11 13:32:23 +08:00
fumiama
2fb445746e Merge branch 'master' of https://github.com/FloatTech/ZeroBot-Plugin 2021-10-11 13:18:15 +08:00
fumiama
505dfef48c 增加-h参数说明 2021-10-11 13:18:06 +08:00
fumiama
19a9a8ef83 增加-h参数说明 2021-10-11 13:16:30 +08:00
fumiama
119730bada 增加-h参数,优化参数处理 2021-10-11 13:15:38 +08:00
fumiama
c46748524a 增加-w参数 2021-10-10 12:00:23 +08:00
fumiama
29f833db41 Merge branch 'master' of https://github.com/FloatTech/ZeroBot-Plugin 2021-10-10 11:52:26 +08:00
fumiama
1232856b21 beautify 2021-10-10 11:52:18 +08:00
github-actions[bot]
770ae6ebd0 🎨 改进代码样式 2021-10-10 03:51:49 +00:00
fumiama
8c9ced0bda ✏️ 解耦 web gui 2021-10-10 11:51:09 +08:00
fumiama
3b3dd3df99 fortune 个人用户也可设置底图 2021-10-10 11:37:41 +08:00
fumiama
0817e6233d 更新时间 2021-10-09 12:51:05 +08:00
fumiama
aa4f428194 更新依赖 2021-10-09 12:50:06 +08:00
fumiama
73717dec73 更新帮助 2021-10-09 12:48:58 +08:00
github-actions[bot]
57e2ef3bab 🎨 改进代码样式 2021-10-09 04:36:26 +00:00
fumiama
a5d0b8db8e fortune 增加设置底图并加速base64 和下载 2021-10-09 12:30:59 +08:00
99 changed files with 5500 additions and 2326 deletions

59
.github/workflows/nightly.yml vendored Normal file
View File

@@ -0,0 +1,59 @@
name: 最新版
on: [push, pull_request]
env:
BINARY_PREFIX: "zbp_"
BINARY_SUFFIX: ""
PR_PROMPT: "::warning:: Build artifact will not be uploaded due to the workflow is trigged by pull request."
LD_FLAGS: "-w -s"
jobs:
build:
name: Build binary CI
runs-on: ubuntu-latest
strategy:
matrix:
# build and publish in parallel: linux/386, linux/amd64, windows/386, windows/amd64, darwin/amd64, darwin/arm64
goos: [linux, windows, darwin]
goarch: ["386", amd64, arm, arm64]
exclude:
- goos: darwin
goarch: arm
- goos: darwin
goarch: "386"
- goos: windows
goarch: arm
- goos: windows
goarch: arm64
fail-fast: true
steps:
- uses: actions/checkout@v2
- name: Setup Go environment
uses: actions/setup-go@v2.1.3
with:
go-version: 1.17
- name: Cache downloaded module
uses: actions/cache@v2
with:
path: |
~/.cache/go-build
~/go/pkg/mod
key: ${{ runner.os }}-go-${{ matrix.goos }}-${{ matrix.goarch }}-${{ hashFiles('**/go.sum') }}
- name: Build binary file
env:
GOOS: ${{ matrix.goos }}
GOARCH: ${{ matrix.goarch }}
IS_PR: ${{ !!github.head_ref }}
run: |
if [ $GOOS = "windows" ]; then export BINARY_SUFFIX="$BINARY_SUFFIX.exe"; fi
if $IS_PR ; then echo $PR_PROMPT; fi
export BINARY_NAME="$BINARY_PREFIX$GOOS_$GOARCH$BINARY_SUFFIX"
export CGO_ENABLED=0
go build -o "output/$BINARY_NAME" -trimpath -ldflags "$LD_FLAGS" .
- name: Upload artifact
uses: actions/upload-artifact@v2
if: ${{ !github.head_ref }}
with:
name: ${{ matrix.goos }}_${{ matrix.goarch }}
path: output/

View File

@@ -1,55 +0,0 @@
name: 测试版
on:
push:
tags:
- p*
env:
GITHUB_TOKEN: ${{ github.token }}
jobs:
my-job:
name: Build ZeroBot-Plugin on Push Tag 🚀
runs-on: ubuntu-latest
steps:
- name: Set up Go
uses: actions/setup-go@v2
with:
go-version: 1.17
- name: Check out code into the Go module directory
uses: actions/checkout@v2
- name: Cache Go
id: cache
uses: actions/cache@v2
with:
# A list of files, directories, and wildcard patterns to cache and restore
path: ~/go/pkg/mod
key: ${{ runner.os }}-build-${{ hashFiles('**/go.sum') }}
- name: Tidy Go modules
run: go mod tidy
- name: Build linux-x64
run: CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -ldflags="-s -w" -o artifacts/zerobot-plugin-linux-x64
- name: Build linux-x86
run: CGO_ENABLED=0 GOOS=linux GOARCH=386 go build -ldflags="-s -w" -o artifacts/zerobot-plugin-linux-x86
- name: Build windows-x64
run: CGO_ENABLED=0 GOOS=windows GOARCH=amd64 go build -ldflags="-s -w" -o artifacts/zerobot-plugin-windows-x64.exe
- name: Build windows-x86
run: CGO_ENABLED=0 GOOS=windows GOARCH=386 go build -ldflags="-s -w" -o artifacts/zerobot-plugin-windows-x86.exe
- name: Build arm64
run: CGO_ENABLED=0 GOOS=linux GOARCH=arm64 GOARM=7 go build -ldflags="-s -w" -o artifacts/zerobot-plugin-linux-arm64
- name: Build armv6
run: CGO_ENABLED=0 GOOS=linux GOARCH=arm GOARM=6 go build -ldflags="-s -w" -o artifacts/zerobot-plugin-linux-armv6
- name: Upload binaries to release
uses: svenstaro/upload-release-action@v2
with:
repo_token: ${{ secrets.GITHUB_TOKEN }}
file: artifacts/zerobot-plugin-*
tag: ${{ github.ref }}
overwrite: true
file_glob: true

View File

@@ -1,55 +1,27 @@
name: 稳定
name: 发行
on:
push:
tags:
- v*
env:
GITHUB_TOKEN: ${{ github.token }}
- 'v*'
jobs:
my-job:
name: Build ZeroBot-Plugin on Push Tag 🚀
goreleaser:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v2.3.4
with:
fetch-depth: 0
- name: Set up Go
uses: actions/setup-go@v2
with:
go-version: 1.17
go-version: '1.17'
- name: Check out code into the Go module directory
uses: actions/checkout@v2
- name: Cache Go
id: cache
uses: actions/cache@v2
- name: Run GoReleaser
uses: goreleaser/goreleaser-action@v2
with:
# A list of files, directories, and wildcard patterns to cache and restore
path: ~/go/pkg/mod
key: ${{ runner.os }}-build-${{ hashFiles('**/go.sum') }}
- name: Tidy Go modules
run: go mod tidy
- name: Build linux-x64
run: CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -ldflags="-s -w" -o artifacts/zerobot-plugin-linux-x64
- name: Build linux-x86
run: CGO_ENABLED=0 GOOS=linux GOARCH=386 go build -ldflags="-s -w" -o artifacts/zerobot-plugin-linux-x86
- name: Build windows-x64
run: CGO_ENABLED=0 GOOS=windows GOARCH=amd64 go build -ldflags="-s -w" -o artifacts/zerobot-plugin-windows-x64.exe
- name: Build windows-x86
run: CGO_ENABLED=0 GOOS=windows GOARCH=386 go build -ldflags="-s -w" -o artifacts/zerobot-plugin-windows-x86.exe
- name: Build arm64
run: CGO_ENABLED=0 GOOS=linux GOARCH=arm64 GOARM=7 go build -ldflags="-s -w" -o artifacts/zerobot-plugin-linux-arm64
- name: Build armv6
run: CGO_ENABLED=0 GOOS=linux GOARCH=arm GOARM=6 go build -ldflags="-s -w" -o artifacts/zerobot-plugin-linux-armv6
- name: Upload binaries to release
uses: svenstaro/upload-release-action@v2
with:
repo_token: ${{ secrets.GITHUB_TOKEN }}
file: artifacts/zerobot-plugin-*
tag: ${{ github.ref }}
overwrite: true
file_glob: true
version: latest
args: release --rm-dist
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

7
.gitignore vendored
View File

@@ -3,10 +3,17 @@ 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/
.DS_Store
.vscode
go-zero*
nohup.out
zerobot

View File

@@ -10,10 +10,6 @@ linters-settings:
disabled-checks:
- exitAfterDefer
gofumpt:
# Select the Go version to target. The default is `1.15`.
lang-version: "1.17"
forbidigo:
# Forbid the following identifiers
forbid:
@@ -22,13 +18,13 @@ linters-settings:
linters:
# please, do not use `enable-all`: it's deprecated and will be removed soon.
# inverted configuration with `enable-all` and `disable` is not scalable during updates of golangci-lint
fast: true
disable-all: true
fast: false
enable:
- bodyclose
- deadcode
- depguard
- dogsled
- dupl
- errcheck
- exportloopref
- exhaustive
@@ -43,7 +39,7 @@ linters:
- gosimple
- govet
- ineffassign
- misspell
#- misspell
- nolintlint
- rowserrcheck
- staticcheck
@@ -58,23 +54,9 @@ linters:
- prealloc
- predeclared
- asciicheck
- revive
- forbidigo
- makezero
- revive
#- interfacer
# don't enable:
# - scopelint
# - gochecknoglobals
# - gocognit
# - godot
# - godox
# - goerr113
# - interfacer
# - maligned
# - nestif
# - testpackage
# - wsl
run:
# default concurrency is a available CPU number.
@@ -95,4 +77,4 @@ issues:
fix: true
exclude-use-default: false
exclude:
- "Error return value of .((os.)?std(out|err)..*|.*Close|.*Flush|os.Remove(All)?|.*print(f|ln)?|os.(Un)?Setenv). is not check"
- "Error return value of .((os.)?std(out|err)..*|.*Close|.*Seek|.*Flush|os.Remove(All)?|.*print(f|ln)?|os.(Un)?Setenv). is not check"

79
.goreleaser.yml Normal file
View File

@@ -0,0 +1,79 @@
project_name: zbp
env:
- GO111MODULE=on
before:
hooks:
- go mod tidy
builds:
- id: nowin
env:
- CGO_ENABLED=0
- GO111MODULE=on
goos:
- linux
- darwin
goarch:
- 386
- amd64
- arm
- arm64
goarm:
- 6
- 7
ignore:
- goos: darwin
goarch: arm
- goos: darwin
goarch: 386
mod_timestamp: "{{ .CommitTimestamp }}"
flags:
- -trimpath
ldflags:
- -s -w
- id: win
env:
- CGO_ENABLED=0
- GO111MODULE=on
goos:
- windows
goarch:
- 386
- amd64
mod_timestamp: "{{ .CommitTimestamp }}"
flags:
- -trimpath
ldflags:
- -s -w
checksum:
name_template: "zbp_checksums.txt"
changelog:
sort: asc
filters:
exclude:
- "^docs:"
- "^test:"
- fix typo
- Merge pull request
- Merge branch
- Merge remote-tracking
- go mod tidy
archives:
- id: nowin
builds:
- nowin
- win
name_template: "zbp_{{ .Os }}_{{ .Arch }}{{ if .Arm }}v{{ .Arm }}{{ end }}"
format_overrides:
- goos: windows
format: zip
nfpms:
- license: GPL 3.0
homepage: https://github.com/FloatTech/ZeroBot-Plugin
file_name_template: "zbp_{{ .Os }}_{{ .Arch }}{{ if .Arm }}v{{ .Arm }}{{ end }}"
formats:
- deb
- rpm
maintainer: FloatTech

156
README.md
View File

@@ -10,28 +10,52 @@
[![OICQ](https://img.shields.io/badge/OneBot-OICQ-green.svg?style=social&logo=appveyor)](https://github.com/takayama-lily/node-onebot)
[![MIRAI](https://img.shields.io/badge/OneBot-Mirai-green.svg?style=social&logo=appveyor)](https://github.com/yyuueexxiinngg/onebot-kotlin)
[![Go Report Card](https://goreportcard.com/badge/github.com/Yiwen-Chan/ZeroBot-Plugin?style=flat-square&logo=go)](https://goreportcard.com/report/github.com/github.com/Yiwen-Chan/ZeroBot-Plugin)
[![Go Report Card](https://goreportcard.com/badge/github.com/FloatTech/ZeroBot-Plugin?style=flat-square&logo=go)](https://goreportcard.com/report/github.com/github.com/FloatTech/ZeroBot-Plugin)
[![Badge](https://img.shields.io/badge/onebot-v11-black?logo=data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAHAAAABwCAMAAADxPgR5AAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAAxQTFRF////29vbr6+vAAAAk1hCcwAAAAR0Uk5T////AEAqqfQAAAKcSURBVHja7NrbctswDATQXfD//zlpO7FlmwAWIOnOtNaTM5JwDMa8E+PNFz7g3waJ24fviyDPgfhz8fHP39cBcBL9KoJbQUxjA2iYqHL3FAnvzhL4GtVNUcoSZe6eSHizBcK5LL7dBr2AUZlev1ARRHCljzRALIEog6H3U6bCIyqIZdAT0eBuJYaGiJaHSjmkYIZd+qSGWAQnIaz2OArVnX6vrItQvbhZJtVGB5qX9wKqCMkb9W7aexfCO/rwQRBzsDIsYx4AOz0nhAtWu7bqkEQBO0Pr+Ftjt5fFCUEbm0Sbgdu8WSgJ5NgH2iu46R/o1UcBXJsFusWF/QUaz3RwJMEgngfaGGdSxJkE/Yg4lOBryBiMwvAhZrVMUUvwqU7F05b5WLaUIN4M4hRocQQRnEedgsn7TZB3UCpRrIJwQfqvGwsg18EnI2uSVNC8t+0QmMXogvbPg/xk+Mnw/6kW/rraUlvqgmFreAA09xW5t0AFlHrQZ3CsgvZm0FbHNKyBmheBKIF2cCA8A600aHPmFtRB1XvMsJAiza7LpPog0UJwccKdzw8rdf8MyN2ePYF896LC5hTzdZqxb6VNXInaupARLDNBWgI8spq4T0Qb5H4vWfPmHo8OyB1ito+AysNNz0oglj1U955sjUN9d41LnrX2D/u7eRwxyOaOpfyevCWbTgDEoilsOnu7zsKhjRCsnD/QzhdkYLBLXjiK4f3UWmcx2M7PO21CKVTH84638NTplt6JIQH0ZwCNuiWAfvuLhdrcOYPVO9eW3A67l7hZtgaY9GZo9AFc6cryjoeFBIWeU+npnk/nLE0OxCHL1eQsc1IciehjpJv5mqCsjeopaH6r15/MrxNnVhu7tmcslay2gO2Z1QfcfX0JMACG41/u0RrI9QAAAABJRU5ErkJggg==)](https://github.com/howmanybots/onebot)
[![Badge](https://img.shields.io/badge/zerobot-v1.2.3-black?style=flat-square&logo=go)](https://github.com/wdvxdr1123/ZeroBot)
[![License](https://img.shields.io/github/license/Yiwen-Chan/OneBot-YaYa.svg?style=flat-square&logo=gnu)](https://raw.githubusercontent.com/FloatTech/ZeroBot-Plugin/master/LICENSE)
[![Badge](https://img.shields.io/badge/zerobot-v1.4.1-black?style=flat-square&logo=go)](https://github.com/wdvxdr1123/ZeroBot)
[![License](https://img.shields.io/github/license/FloatTech/ZeroBot-Plugin.svg?style=flat-square&logo=gnu)](https://raw.githubusercontent.com/FloatTech/ZeroBot-Plugin/master/LICENSE)
[![qq group](https://img.shields.io/badge/group-1048452984-red?style=flat-square&logo=tencent-qq)](https://jq.qq.com/?_wv=1027&k=QMb7x1mM)
本项目符合 [OneBot](https://github.com/howmanybots/onebot) 标准,可基于以下项目与机器人框架/平台进行交互
| 项目地址 | 平台 | 核心作者 |
| --- | --- | --- |
| [Mrs4s/go-cqhttp](https://github.com/Mrs4s/go-cqhttp) | [MiraiGo](https://github.com/Mrs4s/MiraiGo) | Mrs4s |
| [yyuueexxiinngg/cqhttp-mirai](https://github.com/yyuueexxiinngg/cqhttp-mirai) | [Mirai](https://github.com/mamoe/mirai) | yyuueexxiinngg |
| [takayama-lily/onebot](https://github.com/takayama-lily/onebot) | [OICQ](https://github.com/takayama-lily/oicq) | takayama |
</div>
> 如果您不知道什么是 [OneBot](https://github.com/howmanybots/onebot) 或不希望运行多个程序,还可以直接前往 [gocqzbp](https://github.com/FloatTech/gocqzbp) 的 [Release](https://github.com/FloatTech/gocqzbp/releases) 页面下载单一可执行文件或前往 [Packages](https://github.com/FloatTech/gocqzbp/pkgs/container/gocqzbp) 页面使用`docker`,运行后按提示登录即可。
## 命令行参数
```bash
zerobot [-d] [-g] qq1 qq2 qq3 ...
zerobot -h -t token -u url [-d|w] [-g 监听地址:端口] qq1 qq2 qq3 ...
```
- **-d**: 开启 debug 级别日志输出
- **-g**: 开启 [webgui](https://github.com/FloatTech/bot-manager)
- **-h**: 显示帮助
- **-t token**: 设置`AccessToken`,默认为空
- **-u url**: 设置`Url`,默认为`ws://127.0.0.1:6700`
- **-d|w**: 开启 debug | warning 级别及以上日志输出
- **-g 监听地址:端口**: 在 http://监听地址:端口 上开启 [webgui](https://github.com/FloatTech/bot-manager)
- **qqs**: superusers 的 qq 号
## 功能
> 在编译时,以下功能除插件控制外,均可通过注释`main.go`中的相应`import`而物理禁用,减小插件体积。
> 通过插件控制,还可动态管理某个功能在某个群的打开/关闭。
- **web管理** `import _ "github.com/FloatTech/ZeroBot-Plugin/control/web"`
- 开启后可执行文件大约增加 5M ,默认注释不开启。如需开启请自行编辑`main.go`取消注释
- 需要配合 [webgui](https://github.com/FloatTech/bot-manager) 使用
- **动态加载插件** `import _ github.com/FloatTech/ZeroBot-Plugin-Dynamic/dyloader`
- 本功能需要`cgo`,故已分离出主线。详见[ZeroBot-Plugin-Dynamic](https://github.com/FloatTech/ZeroBot-Plugin-Dynamic)
- **插件控制**
- [x] /启用 xxx
- [x] /禁用 xxx
- [x] /启用 xxx (在发送的群/用户启用xxx)
- [x] /禁用 xxx (在发送的群/用户禁用xxx)
- [x] /全局启用 xxx
- [x] /全局禁用 xxx
- [x] /还原 xxx (在发送的群/用户还原xxx的开启状态到初始状态)
- [x] /用法 xxx
- [x] /服务列表
- [x] /服务详情
- [x] @Bot 插件冲突检测 (会在本群发送一条消息并在约 1s 后撤回以检测其它同类 bot 中已启用的插件并禁用)
- **聊天** `import _ "github.com/FloatTech/ZeroBot-Plugin/plugin_chat"`
- [x] [BOT名字]
- [x] [戳一戳BOT]
@@ -40,12 +64,12 @@ zerobot [-d] [-g] qq1 qq2 qq3 ...
- [x] 群温度
- [x] 设置温度[正整数]
- **ATRI** `import _ "github.com/FloatTech/ZeroBot-Plugin/plugin_atri"`
- [x] 具体指令看代码
- [x] 具体指令看 /用法 atri
- 注:本插件基于 [ATRI](https://github.com/Kyomotoi/ATRI) ,为 Golang 移植版
- **群管** `import _ "github.com/FloatTech/ZeroBot-Plugin/plugin_manager"`
- [x] 禁言[@xxx][分钟]
- [x] 解除禁言[@xxx]
- [x] 我要自闭 [分钟]
- [x] 我要自闭|禅定 x [分钟|小时|天]
- [x] 开启全员禁言
- [x] 解除全员禁言
- [x] 升为管理[@xxx]
@@ -54,7 +78,7 @@ zerobot [-d] [-g] qq1 qq2 qq3 ...
- [x] 修改头衔[@xxx][xxx]
- [x] 申请头衔[xxx]
- [x] 踢出群聊[@xxx]
- [x] 退出群聊[群号]
- [x] 退出群聊[群号]@Bot
- [x] *入群欢迎
- [x] *退群通知
- [x] 设置欢迎语[欢迎~]
@@ -62,14 +86,17 @@ zerobot [-d] [-g] qq1 qq2 qq3 ...
- [x] 在[MM]月[每周|周几]的[hh]点[mm]分时(用[url])提醒大家[xxx]
- [x] 取消在[MM]月[dd]日的[hh]点[mm]分的提醒
- [x] 取消在[MM]月[每周|周几]的[hh]点[mm]分的提醒
- [x] 在"cron"时(用[url])提醒大家[xxx]
- [x] 取消在"cron"的提醒
- [x] 列出所有提醒
- [x] 翻牌
- [x] [开启|关闭]入群验证
- [ ] 同意入群请求
- [x] [开启|关闭]gist加群自动审批
- [ ] 同意好友请求
- [ ] 撤回[@xxx] [xxx]
- [ ] 警告[@xxx]
- [x] run[xxx]
-使用gist加群自动审批请在群介绍添加以下说明同时开启`需要回答问题并由管理员审核`加群请在github新建一个gist其文件名为本群群号的字符串的md5(小写)内容为一行是当前unix时间戳(10分钟内有效)。然后请将您的用户名和gist哈希(小写)按照username/gisthash的格式填写到回答即可。
- **GitHub仓库搜索** `import _ "github.com/FloatTech/ZeroBot-Plugin/plugin_github"`
- [x] >github [xxx]
- [x] >github -p [xxx]
@@ -92,52 +119,83 @@ zerobot [-d] [-g] qq1 qq2 qq3 ...
- [x] 爬[@xxx]
- [x] 摸[@xxx]
- [x] 搓[@xxx]
- 注:更多指令见项目 --> https://github.com/tdf1939/ZeroBot-Plugin-Gif
- 注:更多指令见项目 --> https://github.com/FloatTech/ZeroBot-Plugin-Gif
- **base16384加解密** `import _ "github.com/FloatTech/ZeroBot-Plugin/plugin_b14"`
- [x] 加密xxx
- [x] 解密xxx
- [x] 用yyy加密xxx
- [x] 用yyy解密xxx
- **摸鱼** `import _ "github.com/FloatTech/ZeroBot-Plugin/plugin_moyu"`
- [x] 添加摸鱼提醒
- [x] 删除摸鱼提醒
- **涩图** `import _ "github.com/FloatTech/ZeroBot-Plugin/plugin_setutime"`
- [x] 来份[涩图/二次元/风景/车万]
- [x] 添加[涩图/二次元/风景/车万][P站图片ID]
- [x] 删除[涩图/二次元/风景/车万][P站图片ID]
- [x] > setu status
- **本地涩图** `import _ "github.com/FloatTech/ZeroBot-Plugin/plugin_nativesetu"`
- [x] 本地[xxx]
- [x] 刷新本地[xxx]
- [x] 设置本地setu绝对路径[xxx]
- [x] 刷新所有本地setu
- [x] 所有本地setu分类
- 注:刷新文件夹较慢,请耐心等待刷新完成,会提示“成功”。
- **lolicon** `import _ "github.com/FloatTech/ZeroBot-Plugin/plugin_lolicon"`
- [x] 来份萝莉
- **搜图** `import _ "github.com/FloatTech/ZeroBot-Plugin/plugin_saucenao"`
- [x] 以图搜图|搜索图片|以图识图[图片]
- [x] 搜图[P站图片ID]
- **搜番** `import _ "github.com/FloatTech/ZeroBot-Plugin/plugin_tracemoe"`
- [x] 搜番|搜索番剧[图片]
- **随机图片与AI点评** `github.com/FloatTech/ZeroBot-Plugin/plugin_acgimage`
- [x] 随机图片(评级大于6的图将私发)
- [x] 直接随机(无r18检测务必小心仅管理可用)
- [x] 设置随机图片网址[url]
- [x] 太涩了(撤回最近发的图)
- [x] 评价图片(发送一张图片让bot评分)
- **每日运势** `github.com/FloatTech/ZeroBot-Plugin/plugin_fortune`
- [x] 运势
- **浅草寺求签** `github.com/FloatTech/ZeroBot-Plugin/plugin_omikuji`
- **每日运势** `import _ github.com/FloatTech/ZeroBot-Plugin/plugin_fortune`
- [x] 运势|抽签
- [x] 设置底图[车万 DC4 爱因斯坦 星空列车 樱云之恋 富婆妹 李清歌 公主连结 原神 明日方舟 碧蓝航线 碧蓝幻想 战双 阴阳师]
- **睡眠管理** `import _ github.com/FloatTech/ZeroBot-Plugin/plugin_sleep_manage`
- [x] 早安|晚安
- **浅草寺求签** `import _ github.com/FloatTech/ZeroBot-Plugin/plugin_omikuji`
- [x] 求签|占卜
- [x] 解签
- **bilibili** `import _ "github.com/FloatTech/ZeroBot-Plugin/plugin_bilibili"`
- [x] >vup info [名字|uid]
- [x] >user info [名字|uid]
- [x] /开启粉丝日报
- [x] >user info [名字|uid]
- [x] /开启粉丝日报
- **嘉然** `import _ "github.com/FloatTech/ZeroBot-Plugin/plugin_diana"`
- [x] 小作文
- [x] 发大病
- [x] 教你一篇小作文[作文]
- [x] [回复]查重
- **鬼东西** `import _ "github.com/FloatTech/ZeroBot-Plugin/plugin_wtf"`
- [x] 鬼东西列表
- [x] 查询鬼东西[序号][@xxx]
- 注:由于需要科学,默认注释。
- **AIfalse** `import _ "github.com/FloatTech/ZeroBot-Plugin/plugin_ai_false"`
- [x] 查询计算机当前活跃度 [身体检查]
- [x] 清理缓存
- [x] 查询计算机当前活跃度: [检查身体|自检|启动自检|系统状态]
- [x] 清理缓存 (仅适用于 gocq 且需要 bot 的运行目录和 gocq 相同)
- [ ] 简易语音
- [ ] 爬图合成 [@xxx]
- **抽wife** `import _ "github.com/FloatTech/ZeroBot-Plugin/plugin_nativewife"`
- [x] 抽wife[@xxx]
- [x] 添加wife[名字][图片]
- [x] 删除wife[名字]
- [x] [让|不让]所有人均可添加wife
- 注:不同群添加后不会重叠
- **minecraft** `import _ "github.com/FloatTech/ZeroBot-Plugin/plugin_minecraft"`
- [x] /mcstart xxx
- [x] /mcstop xxx
- [x] /mclist servername
- [x] /mcstop xxx
- [x] /mclist servername
- 注:此功能实现依赖[MCSManager](https://github.com/Suwings/MCSManager)项目对服务器的管理apimc服务器如果没有在该管理平台部署此功能无效
- **炉石** `import _ "github.com/FloatTech/ZeroBot-Plugin/plugin_hs"`
- [x] 搜卡[xxxx]
- [x] [卡组代码xxx]
-更多搜卡指令参数https://hs.fbigame.com/misc/searchhelp
- **青云客** `import _ "github.com/FloatTech/ZeroBot-Plugin/plugin_qingyunke"`
- [x] @Bot 任意文本(任意一句话回复)
- [x] @Bot 任意文本(任意一句话回复)
- **关键字搜图** `import _ "github.com/FloatTech/ZeroBot-Plugin/plugin_image_finder"`
- [x] 来张 [xxx]
- **拼音首字母释义工具** `import _ "github.com/FloatTech/ZeroBot-Plugin/plugin_nbnhhsh"`
@@ -147,29 +205,33 @@ zerobot [-d] [-g] qq1 qq2 qq3 ...
- **投胎** `import _ "github.com/FloatTech/ZeroBot-Plugin/plugin_reborn"`
- [x] reborn
- 注:本插件来源于[tgbot](https://github.com/YukariChiba/tgbot/blob/main/modules/Reborn.py)
- **翻译** `import _ "github.com/FloatTech/ZeroBot-Plugin/plugin_translation"`
- [x] >TL 你好
- **vtb语录** `import _ "github.com/FloatTech/ZeroBot-Plugin/plugin_vtb_quotation"`
- [x] vtb语录
- [x] 随机vtb
- **书评** `import _ "github.com/FloatTech/ZeroBot-Plugin/plugin_book_review"`
- [x] 书评[xxx]
- [x] 随机书评
- **coser** `import _ "github.com/FloatTech/ZeroBot-Plugin/plugin_coser" `
- [x] coser
- **novel** `import _ "github.com/FloatTech/ZeroBot-Plugin/plugin_novel" `
- [x] 小说[xxx]
- **TODO...**
## 使用方法
本项目符合 [OneBot](https://github.com/howmanybots/onebot) 标准,可基于以下项目与机器人框架/平台进行交互
| 项目地址 | 平台 | 核心作者 | 备注 |
| --- | --- | --- | --- |
| [Mrs4s/go-cqhttp](https://github.com/Mrs4s/go-cqhttp) | [MiraiGo](https://github.com/Mrs4s/MiraiGo) | Mrs4s | |
| [yyuueexxiinngg/cqhttp-mirai](https://github.com/yyuueexxiinngg/cqhttp-mirai) | [Mirai](https://github.com/mamoe/mirai) | yyuueexxiinngg | |
| [takayama-lily/onebot](https://github.com/takayama-lily/onebot) | [OICQ](https://github.com/takayama-lily/oicq) | takayama | |
### 使用稳定版/测试版 (推荐)
可以前往[Release](https://github.com/FloatTech/ZeroBot-Plugin/releases)页面下载对应系统版本可执行文件,编译时开启了全部插件。
可以前往[Release](https://github.com/FloatTech/ZeroBot-Plugin/releases)页面下载对应系统版本可执行文件,编译时开启了全部插件。您还可以选择 [gocqzbp](https://github.com/FloatTech/gocqzbp) 的 [Release](https://github.com/FloatTech/gocqzbp/releases) 或 [Package](https://github.com/FloatTech/gocqzbp/pkgs/container/gocqzbp),它是 [Mrs4s/go-cqhttp](https://github.com/Mrs4s/go-cqhttp) 与本插件的合体。
### 本地运行
### 本地直接运行
1. 下载安装 [Go](https://studygolang.com/dl) 环境
2. 下载本项目[压缩包](https://github.com/Yiwen-Chan/ZeroBot-Plugin/archive/master.zip),本地解压
1. 下载安装最新 [Go](https://studygolang.com/dl) 环境
2. 下载本项目[压缩包](https://github.com/FloatTech/ZeroBot-Plugin/archive/master.zip),本地解压
3. 编辑 main.go 文件,内容按需修改
4. 双击 build.bat 文件 或 直接双击 run.bat 文件
5. 运行 OneBot 框架,并同时运行本插件
4. 运行 OneBot 框架
5. `Windows`下双击 run.bat 文件,`Linux`下使用 run.sh 运行本插件
### 编译运行
@@ -178,14 +240,14 @@ zerobot [-d] [-g] qq1 qq2 qq3 ...
1. 点击右上角 Fork 本项目,并转跳到自己 Fork 的仓库
2. 点击仓库上方的 Actions 按钮,确认使用 Actions
3. 编辑 main.go 文件,内容按需修改
4. 前往 Release 页面发布一个 Release`tag`形如`vx.y.z`,以触发稳定版编译流程
4. 前往 Release 页面发布一个 Release`tag`形如`v1.2.3`,以触发稳定版编译流程
5. 点击 Actions 按钮,等待编译完成,回到 Release 页面下载编译好的文件
6. 运行 OneBot 框架,并同时运行本插件
7. 啾咪~
#### 本地编译/交叉编译
1. 下载安装 [Go](https://studygolang.com/dl) 环境
1. 下载安装最新 [Go](https://studygolang.com/dl) 环境
2. clone 并进入本项目,下载所需包
```bash
@@ -198,15 +260,19 @@ go mod tidy
```
3. 编辑 main.go 文件,内容按需修改
4. 按照平台输入命令编译,下面举了两个不太常见的例子
4. 按照平台输入命令编译,下面举了一些例子
```bash
# 本机平台
go build -ldflags "-s -w" -o zerobot
go build -ldflags "-s -w" -o zerobot -trimpath
# x64 Linux 平台 如各种云服务器
GOOS=linux GOARCH=amd64 go build -ldflags "-s -w" -o zerobot -trimpath
# x64 Windows 平台 如大多数家用电脑
GOOS=windows GOARCH=amd64 go build -ldflags "-s -w" -o zerobot.exe -trimpath
# armv6 Linux 平台 如树莓派 zero W
GOOS=linux GOARCH=arm GOARM=6 CGO_ENABLED=0 go build -ldflags "-s -w" -o zerobot
# mips Linux 平台 如 路由器 wndr4300
GOOS=linux GOARCH=mips GOMIPS=softfloat CGO_ENABLED=0 go build -ldflags "-s -w" -o zerobot
GOOS=linux GOARCH=arm GOARM=6 CGO_ENABLED=0 go build -ldflags "-s -w" -o zerobot -trimpath
# 由于引入了github.com/logoove/sqlite本项不再可用mips Linux 平台 如 路由器 wndr4300
GOOS=linux GOARCH=mips GOMIPS=softfloat CGO_ENABLED=0 go build -ldflags "-s -w" -o zerobot -trimpath
```
5. 运行 OneBot 框架,并同时运行本插件
@@ -218,4 +284,4 @@ GOOS=linux GOARCH=mips GOMIPS=softfloat CGO_ENABLED=0 go build -ldflags "-s -w"
## License
[![FOSSA Status](https://app.fossa.com/api/projects/git%2Bgithub.com%2FYiwen-Chan%2FZeroBot-Plugin.svg?type=large)](https://app.fossa.com/projects/git%2Bgithub.com%2FYiwen-Chan%2FZeroBot-Plugin?ref=badge_large)
[![FOSSA Status](https://app.fossa.com/api/projects/git%2Bgithub.com%2FFloatTech%2FZeroBot-Plugin.svg?type=large)](https://app.fossa.com/projects/git%2Bgithub.com%2FFloatTech%2FZeroBot-Plugin?ref=badge_large)

93
control/cd.go Normal file
View File

@@ -0,0 +1,93 @@
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/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 = time.Now().Unix()-int64(binary.BigEndian.Uint64(timebytes)) < 10
}
return
}

19
control/cd_test.go Normal file
View File

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

@@ -4,9 +4,28 @@ 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

@@ -4,6 +4,7 @@ package control
import (
"os"
"strconv"
"strings"
"sync"
"github.com/sirupsen/logrus"
@@ -11,11 +12,11 @@ import (
"github.com/wdvxdr1123/ZeroBot/extension"
"github.com/wdvxdr1123/ZeroBot/message"
"github.com/FloatTech/ZeroBot-Plugin/data"
"github.com/FloatTech/ZeroBot-Plugin/utils/sql"
)
var (
db = &data.Sqlite{DBPath: "data/control/plugins.db"}
db = &sql.Sqlite{DBPath: "data/control/plugins.db"}
// managers 每个插件对应的管理
managers = map[string]*Control{}
mu = sync.RWMutex{}
@@ -40,8 +41,8 @@ func newctrl(service string, o *Options) *Control {
}(),
}
mu.Lock()
defer mu.Unlock()
managers[service] = m
mu.Unlock()
err := db.Create(service, &grpcfg{})
if err != nil {
panic(err)
@@ -49,64 +50,153 @@ func newctrl(service string, o *Options) *Control {
return m
}
// enable enables a group to pass the Manager.
func (m *Control) enable(groupID int64) {
m.Lock()
err := db.Insert(m.service, &grpcfg{groupID, 0})
if err != nil {
logrus.Errorf("[control] %v", err)
}
m.Unlock()
}
// disable disables a group to pass the Manager.
func (m *Control) disable(groupID int64) {
m.Lock()
err := db.Insert(m.service, &grpcfg{groupID, 1})
if err != nil {
logrus.Errorf("[control] %v", err)
}
m.Unlock()
}
func (m *Control) isEnabledIn(gid int64) bool {
m.RLock()
// 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
err := db.Find(m.service, &c, "WHERE gid = "+strconv.FormatInt(gid, 10))
if err == nil {
m.RUnlock()
logrus.Debugf("[control] plugin %s of grp %d : %d", m.service, c.GroupID, c.Disable)
return c.Disable == 0
}
logrus.Errorf("[control] %v", err)
m.RLock()
err := db.Find(m.service, &c, "WHERE gid = "+strconv.FormatInt(groupID, 10))
m.RUnlock()
if m.options.DisableOnDefault {
m.disable(gid)
} else {
m.enable(gid)
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 {
logrus.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 {
logrus.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 {
logrus.Errorf("[control] %v", err)
}
}
}
// IsEnabledIn 开启群
func (m *Control) IsEnabledIn(gid int64) bool {
var c grpcfg
var err error
logrus.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 {
logrus.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 {
logrus.Debugf("[control] plugin %s of all : %d", m.service, c.Disable&1)
return c.Disable&1 == 0
}
return !m.options.DisableOnDefault
}
// GetData 获取某个群的 63 字节配置信息
func (m *Control) GetData(gid int64) int64 {
var c grpcfg
var err error
logrus.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 {
logrus.Debugf("[control] plugin %s of grp %d : %x", m.service, c.GroupID, c.Disable>>1)
return c.Disable >> 1
}
}
m.RLock()
err = db.Find(m.service, &c, "WHERE gid = 0")
m.RUnlock()
if err == nil && c.GroupID == 0 {
logrus.Debugf("[control] plugin %s of all : %x", m.service, 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 |= data << 1
logrus.Debugf("[control] set plugin %s of all : %x", m.service, data)
m.Lock()
err = db.Insert(m.service, &c)
m.Unlock()
if err != nil {
logrus.Errorf("[control] %v", err)
}
return err
}
// Handler 返回 预处理器
func (m *Control) Handler() zero.Rule {
return func(ctx *zero.Ctx) bool {
ctx.State["manager"] = m
return m.isEnabledIn(ctx.Event.GroupID)
grp := ctx.Event.GroupID
if grp == 0 {
// 个人用户
grp = -ctx.Event.UserID
}
logrus.Debugln("[control] handler get gid =", grp)
return m.IsEnabledIn(grp)
}
}
// lookup returns a Manager by the service name, if
// Lookup returns a Manager by the service name, if
// not exist, it will returns nil.
func lookup(service string) (*Control, bool) {
func Lookup(service string) (*Control, bool) {
mu.RLock()
defer mu.RUnlock()
m, ok := managers[service]
mu.RUnlock()
return m, ok
}
// forEach iterates through managers.
func forEach(iterator func(key string, manager *Control) bool) {
// ForEach iterates through managers.
func ForEach(iterator func(key string, manager *Control) bool) {
mu.RLock()
m := copyMap(managers)
mu.RUnlock()
@@ -125,6 +215,13 @@ func copyMap(m map[string]*Control) map[string]*Control {
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()
@@ -134,60 +231,121 @@ func init() {
panic(err)
} else {
hasinit = true
zero.OnCommandGroup([]string{"启用", "enable"}, zero.AdminPermission, zero.OnlyGroup).
Handle(func(ctx *zero.Ctx) {
model := extension.CommandModel{}
_ = ctx.Parse(&model)
service, ok := lookup(model.Args)
if !ok {
ctx.Send("没有找到指定服务!")
}
service.enable(ctx.Event.GroupID)
ctx.Send(message.Text("已启用服务: " + model.Args))
})
zero.OnCommandGroup([]string{
"启用", "enable", "禁用", "disable",
"全局启用", "enableall", "全局禁用", "disableall",
}, userOrGrpAdmin).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{"禁用", "disable"}, zero.AdminPermission, zero.OnlyGroup).
Handle(func(ctx *zero.Ctx) {
model := extension.CommandModel{}
_ = ctx.Parse(&model)
service, ok := lookup(model.Args)
if !ok {
ctx.Send("没有找到指定服务!")
}
service.disable(ctx.Event.GroupID)
ctx.Send(message.Text("已关闭服务: " + model.Args))
})
zero.OnCommandGroup([]string{"还原", "reset"}, userOrGrpAdmin).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{"用法", "usage"}, zero.AdminPermission, zero.OnlyGroup).
zero.OnCommandGroup([]string{"用法", "usage"}, userOrGrpAdmin).
Handle(func(ctx *zero.Ctx) {
model := extension.CommandModel{}
_ = ctx.Parse(&model)
service, ok := lookup(model.Args)
service, ok := Lookup(model.Args)
if !ok {
ctx.Send("没有找到指定服务!")
ctx.SendChain(message.Text("没有找到指定服务!"))
return
}
if service.options.Help != "" {
ctx.Send(service.options.Help)
ctx.SendChain(message.Text(service.options.Help))
} else {
ctx.Send("该服务无帮助!")
ctx.SendChain(message.Text("该服务无帮助!"))
}
})
zero.OnCommandGroup([]string{"服务列表", "service_list"}, zero.AdminPermission, zero.OnlyGroup).
zero.OnCommandGroup([]string{"服务列表", "service_list"}, userOrGrpAdmin).
Handle(func(ctx *zero.Ctx) {
msg := `---服务列表---`
msg := "--------服务列表--------\n发送\"/用法 name\"查看详情"
i := 0
forEach(func(key string, manager *Control) bool {
gid := ctx.Event.GroupID
ForEach(func(key string, manager *Control) bool {
i++
msg += "\n" + strconv.Itoa(i) + `: `
if manager.isEnabledIn(ctx.Event.GroupID) {
if manager.IsEnabledIn(gid) {
msg += "●" + key
} else {
msg += "○" + key
}
return true
})
ctx.Send(message.Text(msg))
ctx.SendChain(message.Text(msg))
})
zero.OnCommandGroup([]string{"服务详情", "service_detail"}, userOrGrpAdmin, zero.OnlyGroup).
Handle(func(ctx *zero.Ctx) {
var m message.Message
m = append(m,
message.CustomNode(
zero.BotConfig.NickName[0],
ctx.Event.SelfID,
"---服务详情---",
))
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
m = append(m,
message.CustomNode(
zero.BotConfig.NickName[0],
ctx.Event.SelfID,
msg,
))
return true
})
if id := ctx.SendGroupForwardMessage(
ctx.Event.GroupID,
m,
).Get("message_id").Int(); id == 0 {
ctx.SendChain(message.Text("ERROR: 可能被风控了"))
}
})
}
}

View File

@@ -1,4 +1,5 @@
package control
// Package webctrl 包含 webui 所需的所有内容
package webctrl
import (
"encoding/json"
@@ -7,6 +8,7 @@ import (
"net/http"
"os"
"strconv"
"sync"
manager "github.com/FloatTech/bot-manager"
// 依赖gin监听server
@@ -17,6 +19,8 @@ import (
log "github.com/sirupsen/logrus"
zero "github.com/wdvxdr1123/ZeroBot"
"github.com/wdvxdr1123/ZeroBot/message"
ctrl "github.com/FloatTech/ZeroBot-Plugin/control"
)
var (
@@ -26,6 +30,8 @@ var (
logConn *websocket.Conn
l logWriter
// 存储请求事件flag作为键一个request对象作为值
requestData sync.Map
)
// logWriter
@@ -34,13 +40,27 @@ var (
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() {
func InitGui(addr string) {
// 将日志重定向到前端hook
writer := io.MultiWriter(l, os.Stderr)
writer := io.MultiWriter(l, os.Stdout)
log.SetOutput(writer)
// 监听后端
go controller()
go controller(addr)
// 注册消息handle
messageHandle()
}
@@ -52,7 +72,7 @@ var upGrader = websocket.Upgrader{
},
}
func controller() {
func controller(addr string) {
defer func() {
err := recover()
if err != nil {
@@ -86,12 +106,16 @@ func controller() {
// 获取插件列表
engine.POST("/get_plugins", func(context *gin.Context) {
var datas []map[string]interface{}
forEach(func(key string, manager *Control) bool {
datas = append(datas, map[string]interface{}{"id": 1, "handle_type": "", "name": key, "enable": manager.isEnabledIn(0)})
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)
// 获取前端标签
@@ -102,13 +126,48 @@ func controller() {
// 发送信息
engine.POST("/send_msg", sendMsg)
engine.GET("/data", upgrade)
log.Infoln("[gui] the webui is running http://127.0.0.1:3000")
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("127.0.0.1:3000"); err != nil {
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: 改变所有插件的状态
@@ -126,23 +185,11 @@ func updateAllPluginStatus(context *gin.Context) {
}
enable = parse["enable"].(bool)
}
var groups []int64
zero.RangeBot(func(id int64, ctx *zero.Ctx) bool {
for _, group := range ctx.GetGroupList().Array() {
groups = append(groups, group.Get("group_id").Int())
}
return true
})
forEach(func(key string, manager *Control) bool {
ctrl.ForEach(func(key string, manager *ctrl.Control) bool {
if enable {
for _, group := range groups {
manager.enable(group)
}
manager.Enable(0)
} else {
for _, group := range groups {
manager.disable(group)
}
manager.Disable(0)
}
return true
})
@@ -168,23 +215,16 @@ func updatePluginAllGroupStatus(context *gin.Context) {
name = parse["name"].(string)
enable = parse["enable"].(bool)
}
control, b := lookup(name)
control, b := ctrl.Lookup(name)
if !b {
context.JSON(404, nil)
return
}
zero.RangeBot(func(id int64, ctx *zero.Ctx) bool {
for _, group := range ctx.GetGroupList().Array() {
if enable {
control.enable(group.Get("group_id").Int())
} else {
control.disable(group.Get("group_id").Int())
}
}
return true
})
if enable {
control.Enable(0)
} else {
control.Disable(0)
}
context.JSON(200, nil)
}
@@ -205,15 +245,15 @@ func updatePluginStatus(context *gin.Context) {
name := parse["name"].(string)
enable := parse["enable"].(bool)
fmt.Println(name)
control, b := lookup(name)
control, b := ctrl.Lookup(name)
if !b {
context.JSON(404, "服务不存在")
return
}
if enable {
control.enable(groupID)
control.Enable(groupID)
} else {
control.disable(groupID)
control.Disable(groupID)
}
context.JSON(200, nil)
}
@@ -237,12 +277,12 @@ func getPluginStatus(context *gin.Context) {
groupID = int64(parse["group_id"].(float64))
name = parse["name"].(string)
}
control, b := lookup(name)
control, b := ctrl.Lookup(name)
if !b {
context.JSON(404, "服务不存在")
return
}
context.JSON(200, gin.H{"enable": control.isEnabledIn(groupID)})
context.JSON(200, gin.H{"enable": control.IsEnabledIn(groupID)})
}
// getPluginsStatus
@@ -263,8 +303,8 @@ func getPluginsStatus(context *gin.Context) {
groupID = int64(parse["group_id"].(float64))
}
var datas []map[string]interface{}
forEach(func(key string, manager *Control) bool {
enable := manager.isEnabledIn(groupID)
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
})
@@ -384,6 +424,31 @@ func messageHandle() {
}
}
})
// 直接注册一个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
@@ -468,6 +533,23 @@ func cors() gin.HandlerFunc {
}
}
// 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)

Binary file not shown.

BIN
data/Diana/text.db Normal file

Binary file not shown.

BIN
data/Omikuji/kuji.db Normal file

Binary file not shown.

BIN
data/VtbQuotation/vtb.db Normal file

Binary file not shown.

31
go.mod
View File

@@ -3,20 +3,27 @@ module github.com/FloatTech/ZeroBot-Plugin
go 1.16
require (
github.com/FloatTech/AnimeAPI v1.1.9
github.com/FloatTech/ZeroBot-Plugin-Gif v0.2.3
github.com/FloatTech/ZeroBot-Plugin-Timer v1.4.3
github.com/FloatTech/bot-manager v1.0.0
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.3
github.com/corona10/goimagehash v1.0.3
github.com/fogleman/gg v1.3.0
github.com/gin-gonic/gin v1.7.4
github.com/golang/protobuf v1.5.2
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/gotracemoe v0.0.3
github.com/gin-gonic/gin v1.7.7
github.com/gorilla/websocket v1.4.2
github.com/imroc/req v0.3.0
github.com/jinzhu/gorm v1.9.16
github.com/logoove/sqlite v1.13.0
github.com/mroth/weightedrand v0.4.1
github.com/robfig/cron v1.2.0
github.com/shirou/gopsutil v3.21.8+incompatible
github.com/shirou/gopsutil/v3 v3.21.11
github.com/sirupsen/logrus v1.8.1
github.com/tidwall/gjson v1.9.0
github.com/wdvxdr1123/ZeroBot v1.2.4
modernc.org/sqlite v1.13.0
github.com/t-tomalak/logrus-easy-formatter v0.0.0-20190827215021-c074f06c5816
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.6
)

113
go.sum
View File

@@ -1,38 +1,58 @@
github.com/FloatTech/AnimeAPI v1.1.9 h1:H1hZmgwZPNHdx39K9JvY3awT8TTsCl9kKA1uVMyCjRg=
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.3 h1:TGQHhiPR/XIxYYoB6IxOzgMhsZs2tDYQlaJHT04gkQ8=
github.com/FloatTech/ZeroBot-Plugin-Gif v0.2.3/go.mod h1:W7ag6hml1pZTNzRXKU74OMr6rS8awQKSU+o2g7Gj4O0=
github.com/FloatTech/ZeroBot-Plugin-Timer v1.4.3 h1:jn/dH+OwPSRozkmeCeQQPrAGJRBudcm3OK5tXhGItRk=
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.0 h1:d63J5htLhVBc2ITG09WBJI+qAB0ubPjYhfXl6hljBNk=
github.com/FloatTech/bot-manager v1.0.0/go.mod h1:8YYRJ16oroGHQGD2En0oVnmcKJkxR9O/jd5BPSfWfOQ=
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/StackExchange/wmi v1.2.1 h1:VIkavFPXSjcnS+O8yTq7NI32k0R5Aj+v39y29VYDOSA=
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=
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/andybalholm/cascadia v1.1.0/go.mod h1:GsXiBklL0woXo1j/WYWtSYYC4ouU9PqHO0sqidkEA4Y=
github.com/antchfx/htmlquery v1.2.3 h1:sP3NFDneHx2stfNXCKbhHFo8XgNjCACnU/4AO5gWz6M=
github.com/antchfx/htmlquery v1.2.3/go.mod h1:B0ABL+F5irhhMWg54ymEZinzMSi0Kt3I2if0BLYa3V0=
github.com/antchfx/xpath v1.1.6 h1:6sVh6hB5T6phw1pFpHRQ+C4bd8sNI+O58flqtg7h0R0=
github.com/antchfx/xpath v1.1.6/go.mod h1:Yee4kTMuNiPYJ7nSNorELQMr1J33uOpXDMByNYhvtNk=
github.com/corona10/goimagehash v1.0.3 h1:NZM518aKLmoNluluhfHGxT3LGOnrojrxhGn63DR/CZA=
github.com/corona10/goimagehash v1.0.3/go.mod h1:VkvE0mLn84L4aF8vCb6mafVajEb6QYMHl2ZJLn0mOGI=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/denisenkom/go-mssqldb v0.0.0-20191124224453-732737034ffd h1:83Wprp6ROGeiHFAP8WJdI2RoxALQYgdllERc3N5N2DM=
github.com/denisenkom/go-mssqldb v0.0.0-20191124224453-732737034ffd/go.mod h1:xbL0rPBG9cCiLr28tMa8zpbdarY27NDyej4t/EjAShU=
github.com/disintegration/imaging v1.6.2 h1:w1LecBlG2Lnp8B3jk5zSuNqd7b4DXhcjwek1ei82L+c=
github.com/disintegration/imaging v1.6.2/go.mod h1:44/5580QXChDfwIclfc/PCwrr44amcmDAg8hxG0Ewe4=
github.com/dustin/go-humanize v1.0.0 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/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
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/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.4 h1:QmUZXrvJ9qZ3GfWvQ+2wnW/1ePrTEJqPKMYEU3lD/DM=
github.com/gin-gonic/gin v1.7.4/go.mod h1:jD2toBW3GZUr5UMcdrwQA10I7RuaFOl/SGeDjXkfUtY=
github.com/go-ole/go-ole v1.2.5 h1:t4MGB5xEDZvXI+0rMjjsfBsD7yAgp/s9ZDkL1JndXwY=
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/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 h1:HyWk6mgj5qFqCT5fjGBuRArbVDfE4hi8+e8ceBS/t7Q=
@@ -41,6 +61,10 @@ github.com/go-playground/universal-translator v0.17.0 h1:icxd5fm+REJzpZx7ZfpaD87
github.com/go-playground/universal-translator v0.17.0/go.mod h1:UkSxE5sNxxRwHyU+Scu5vgOQjsIJAF8j9muTVoKLVtA=
github.com/go-playground/validator/v10 v10.4.1 h1:pH2c5ADXtd66mxoE0Zm9SUhxE20r7aM3F26W0hOn+GE=
github.com/go-playground/validator/v10 v10.4.1/go.mod h1:nlOn6nFhuKACm19sB/8EGNn9GlaMV7XkbRSipzJ0Ii4=
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/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/groupcache v0.0.0-20200121045136-8c9f03a8e57e h1:1r7pUrabqp18hOBcwBwiTsbnFeTZHV9eER/QT5JVZxY=
@@ -52,24 +76,38 @@ 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/go-cmp v0.5.3/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.5 h1:Khx7svrCpmxxtHBq5j2mp/xVjsi8hQMfNLvJFAlrGgU=
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/gorilla/websocket v1.4.2 h1:+/TMaTYc4QFitKJxsQ7Yye35DkWvkdLcvGKqM+x0Ufc=
github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
github.com/imroc/req v0.3.0 h1:3EioagmlSG+z+KySToa+Ylo3pTFZs+jh3Brl7ngU12U=
github.com/imroc/req v0.3.0/go.mod h1:F+NZ+2EFSo6EFXdeIbpfE9hcC233id70kf0byW97Caw=
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 h1:uVUAXhF2To8cbw/3xN3pxj6kk7TYKs98NIrTqPlMWAQ=
github.com/json-iterator/go v1.1.11/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 h1:Z9n2FFNUXsshfwJMBgNA0RU6/i7WVaAegv3PtuIHPMs=
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/leodido/go-urn v1.2.0 h1:hpXL4XnriNwQ/ABnpepYM/1vCLWNDfUNts8dX3xTG6Y=
github.com/leodido/go-urn v1.2.0/go.mod h1:+8+nEpDfqqsY+g338gtMEUOtuK+4dEMhiQEgxpxOKII=
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/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/mattn/go-isatty v0.0.12 h1:wuysRhFDzyxgEmMf5xjvJ2M9dZoWAXNNr5LSBS7uHXY=
github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
github.com/mattn/go-sqlite3 v1.14.0/go.mod h1:JIl7NbARA7phWnGvh0LKTyg7S9BA+6gx71ShQilpsus=
github.com/mattn/go-sqlite3 v1.14.8 h1:gDp86IdQsN/xWjIEmr9MF6o9mpksUgh0fu+9ByFxzIU=
github.com/mattn/go-sqlite3 v1.14.8/go.mod h1:NyWgC/yNuGj7Q9rpYnZvas74GogHl5/Z4A/KQRfk6bU=
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421 h1:ZqeYNhU3OHLH3mGKHDcjJRFFRrJa6eAM5H+CtDdOsPc=
@@ -79,17 +117,24 @@ github.com/modern-go/reflect2 v1.0.2-0.20210109003243-333559e1834b h1:6Xjqolv/0D
github.com/modern-go/reflect2 v1.0.2-0.20210109003243-333559e1834b/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/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/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/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
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/remyoudompheng/bigfft v0.0.0-20200410134404-eec4a21b6bb0 h1:OdAsTTz6OkFY5QxjkYwrChwuRruF69c169dPK26NUlk=
github.com/remyoudompheng/bigfft v0.0.0-20200410134404-eec4a21b6bb0/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo=
github.com/robfig/cron v1.2.0 h1:ZjScXvvxeQ63Dbyxy76Fj3AT3Ut0aKsyd2/tl3DTMuQ=
github.com/robfig/cron v1.2.0/go.mod h1:JGuDeoQd7Z6yL4zQhZ3OPEVHB7fL6Ka6skscFHfmt2k=
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.11 h1:d5tOAP5+bmJ8Hf2+4bxOSkQ/64+sjEbjU9nSW9nJgG0=
github.com/shirou/gopsutil/v3 v3.21.11/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=
@@ -101,16 +146,21 @@ github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81P
github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY=
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 h1:J6v8awz+me+xeb/cUTotKgceAYouhIB3pjzgRd6IlGk=
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 h1:+Od7AE26jAaMgVC31cQV/Ope5iKXulNMflrlB7k+F9E=
github.com/tidwall/gjson v1.9.0/go.mod h1:5/xDoumyyDNerp2U36lyolv46b3uF/9Bu6OfyQ9GImk=
github.com/tidwall/match v1.0.3 h1:FQUVvBImDutD8wJLN6c5eMzWtjgONK9MwIBCOrUJKeE=
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/tidwall/pretty v1.1.0 h1:K3hMW5epkdAVwibsQEfR/7Zj0Qgt4DxtNumTq/VloO8=
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=
@@ -121,27 +171,39 @@ github.com/ugorji/go/codec v1.1.7 h1:2SvQaVZ1ouYrrKKwoSk2pzd4A9evlKJb9oTL+OaLUSs
github.com/ugorji/go/codec v1.1.7/go.mod h1:Ax+UKWsSmolVDwsd+7N3ZtXu+yMGCf907BLYF3GoBXY=
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 h1:eC/41Tlkj1jxXW91x4K+qjUlaQXKGgUeLhxw7C6+qkM=
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/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yusufpapurcu/wmi v1.2.2 h1:KBNDSne4vP5mbSWnJbO+51IMOXJB67QiYCSBrubbPRg=
github.com/yusufpapurcu/wmi v1.2.2/go.mod h1:SBZ9tNy3G9/m5Oi98Zks0QjeHVDvuK0qfxQmPyzfmi0=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20190325154230-a5d413f7728c/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-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-20200622213623-75b288015ac9 h1:psW17arqaxU48Z5kZ0CQnkZWQJsqcURM6tKiBApRjXI=
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/image v0.0.0-20191009234506-e7c1f5e7dbb8/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
golang.org/x/image v0.0.0-20210628002857-a66eb6448b8d h1:RNPAfi2nHY7C2srAV8A49jpsYr0ADedCk1wq6fTMTvs=
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/mod v0.3.0 h1:RM4zey1++hCTbCVQfnWeKs9/IEsaBLA8vTkd0WVtmH4=
golang.org/x/mod v0.3.0/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-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-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-20201021035429-f5854403a974 h1:IX6qOQeG5uLjB/hjjwjedwfjND0hgjPMMyO1RoIXQNI=
golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
golang.org/x/net v0.0.0-20211015210444-4f30a5c0130f h1:OfiFi4JbukWwe3lzw+xunroH1mnC1e2Gy5cxNJApiSY=
golang.org/x/net v0.0.0-20211015210444-4f30a5c0130f/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/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-20180909124046-d0be0721c37e/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-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
@@ -151,10 +213,15 @@ golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7w
golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-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-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210816074244-15123e1e1f71/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210902050250-f475640dd07b h1:S7hKs0Flbq0bbc9xgYt4stIEG1zNDFqyrPwAX2Wj/sE=
golang.org/x/sys v0.0.0-20210902050250-f475640dd07b/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20211013075003-97ac67df715c h1:taxlMj0D/1sOAuv/CbSD+MMDof2vbyPTqz5FNYKpXt8=
golang.org/x/sys v0.0.0-20211013075003-97ac67df715c/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.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
@@ -170,8 +237,9 @@ golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8T
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/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 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
@@ -195,7 +263,6 @@ modernc.org/ccgo/v3 v3.11.0/go.mod h1:dGNposbDp9TOZ/1KBxghxtUp/bzErD0/0QW4hhSaBM
modernc.org/ccgo/v3 v3.11.1/go.mod h1:lWHxfsn13L3f7hgGsGlU28D9eUOf6y3ZYHKoPaKU0ag=
modernc.org/ccgo/v3 v3.11.2 h1:gqa8PQ2v7SjrhHCgxUO5dzoAJWSLAveJqZTNkPCN0kc=
modernc.org/ccgo/v3 v3.11.2/go.mod h1:6kii3AptTDI+nUrM9RFBoIEUEisSWCbdczD9ZwQH2FE=
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=
@@ -217,9 +284,7 @@ modernc.org/sqlite v1.13.0 h1:cwhUj0jTBgPjk/demWheV+T6xi6ifTfsGIFKFq0g3Ck=
modernc.org/sqlite v1.13.0/go.mod h1:2qO/6jZJrcQaxFUHxOwa6Q6WfiGSsiVj6GXX0Ker+Jg=
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 h1:DZMfR+RDJRhcrmMEMTJgVIX+Wf5qhfVX0llI0rsc20w=
modernc.org/tcl v1.5.9/go.mod h1:bcwjvBJ2u0exY6K35eAmxXBBij5kXb1dHlAWmfhqThE=
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 h1:IjjzDsIFbl0wuF2KfwvdyUAJVwxD4iwZ6akLNiDoClM=
modernc.org/z v1.1.2/go.mod h1:sj9T1AGBG0dm6SCVzldPOHWrif6XBpooJtbttMn1+Js=

164
main.go
View File

@@ -3,109 +3,157 @@ package main
import (
"flag"
"fmt"
"os"
"strings"
// 注:以下插件均可通过前面加 // 注释,注释后停用并不加载插件
// 下列插件可与 wdvxdr1123/ZeroBot v1.1.2 以上配合单独使用
// 词库类
"github.com/sirupsen/logrus"
// 插件控制
// webctrl "github.com/FloatTech/ZeroBot-Plugin/control/web" // web 后端控制
// 词库类
_ "github.com/FloatTech/ZeroBot-Plugin/plugin_atri" // ATRI词库
_ "github.com/FloatTech/ZeroBot-Plugin/plugin_chat" // 基础词库
_ "github.com/FloatTech/ZeroBot-Plugin/plugin_qingyunke" // 青云客
// 实用类
_ "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_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-Gif" // 制图
_ "github.com/FloatTech/ZeroBot-Plugin/plugin_ai_false" // 服务器监控
_ "github.com/FloatTech/ZeroBot-Plugin/plugin_choose" // 选择困难症帮手
_ "github.com/FloatTech/ZeroBot-Plugin/plugin_fortune" // 运势
_ "github.com/FloatTech/ZeroBot-Plugin/plugin_hs" // 炉石
_ "github.com/FloatTech/ZeroBot-Plugin/plugin_minecraft" // MCSManager
_ "github.com/FloatTech/ZeroBot-Plugin/plugin_music" // 点歌
_ "github.com/FloatTech/ZeroBot-Plugin/plugin_omikuji" // 浅草寺求签
_ "github.com/FloatTech/ZeroBot-Plugin/plugin_reborn" // 投胎
_ "github.com/FloatTech/ZeroBot-Plugin/plugin_shindan" // 测定
// _ "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_choose" // 选择困难症帮手
_ "github.com/FloatTech/ZeroBot-Plugin/plugin_coser" // 三次元小姐姐
_ "github.com/FloatTech/ZeroBot-Plugin/plugin_fortune" // 运势
_ "github.com/FloatTech/ZeroBot-Plugin/plugin_hs" // 炉石
_ "github.com/FloatTech/ZeroBot-Plugin/plugin_minecraft" // MCSManager
_ "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_shindan" // 测定
// b站相关
_ "github.com/FloatTech/ZeroBot-Plugin/plugin_bilibili" // 查询b站用户信息
_ "github.com/FloatTech/ZeroBot-Plugin/plugin_diana" // 嘉心糖发病
// 二次元图片
_ "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_saucenao" // 以图搜
_ "github.com/FloatTech/ZeroBot-Plugin/plugin_setutime" // 来份涩图
_ "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/fumiama/go-registry"
"github.com/sirupsen/logrus"
zero "github.com/wdvxdr1123/ZeroBot"
"github.com/wdvxdr1123/ZeroBot/driver"
"github.com/FloatTech/ZeroBot-Plugin/control"
"github.com/wdvxdr1123/ZeroBot/message"
)
var (
contents = []string{
"* OneBot + ZeroBot + Golang",
"* Version 1.1.6 - 2021-09-27 21:30:33 +0800 CST",
"* Copyright © 2020 - 2021 Kanri, DawnNights, Fumiama, Suika",
"* 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.eastasia.azurecontainer.io:32664", "fumiama")
)
func init() {
var en bool
var debg bool
// 解析命令行参数,输入 `-g` 即可启用 gui
flag.BoolVar(&en, "g", false, "Enable web gui.")
// 解析命令行参数,输入 `-d` 即可开启 debug log
flag.BoolVar(&debg, "d", false, "Enable debug log.")
// 解析命令行参数
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.")
// 直接写死 URL 时,请更改下面第二个参数
url = flag.String("u", "ws://127.0.0.1:6700", "Set Url of WSClient.")
flag.Parse()
if en {
control.InitGui()
}
if debg {
logrus.SetLevel(logrus.DebugLevel)
if *h {
printBanner()
fmt.Println("Usage:")
flag.PrintDefaults()
os.Exit(0)
} else {
if *d && !*w {
logrus.SetLevel(logrus.DebugLevel)
}
if *w {
logrus.SetLevel(logrus.WarnLevel)
}
}
// 启用 gui
// webctrl.InitGui(*g)
}
func main() {
func printBanner() {
fmt.Print(
"\n======================[ZeroBot-Plugin]======================",
"\n", banner, "\n",
"----------------------[ZeroBot-公告栏]----------------------",
"\n", getKanban(), "\n",
"============================================================\n",
) // 启动打印
zero.Run(zero.Config{
NickName: []string{"椛椛", "ATRI", "atri", "亚托莉", "アトリ"},
CommandPrefix: "/",
)
}
// SuperUsers 某些功能需要主人权限,可通过以下两种方式修改
// []string{}:通过代码写死的方式添加主人账号
// flag.Args():通过命令行参数的方式添加主人账号
SuperUsers: append([]string{"12345678", "87654321"}, flag.Args()...),
Driver: []zero.Driver{
&driver.WSClient{
// OneBot 正向WS 默认使用 6700 端口
Url: "ws://127.0.0.1:6700",
AccessToken: "",
},
},
})
func getKanban() string {
err := reg.Connect()
defer reg.Close()
if err != nil {
return err.Error()
}
text, err := reg.Get("ZeroBot-Plugin/kanban")
if err != nil {
return err.Error()
}
return text
}
func main() {
printBanner()
// 帮助
zero.OnFullMatchGroup([]string{"/help", ".help", "菜单"}, zero.OnlyToMe).SetBlock(true).FirstPriority().
Handle(func(ctx *zero.Ctx) {
ctx.Send(banner)
ctx.SendChain(message.Text(banner, "\n可发送\"/服务列表\"查看 bot 功能"))
})
select {}
zero.OnFullMatch("查看zbp公告", zero.OnlyToMe, zero.AdminPermission).SetBlock(true).FirstPriority().
Handle(func(ctx *zero.Ctx) {
ctx.SendChain(message.Text(getKanban()))
})
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)},
},
)
}

16
main_win.go Normal file
View File

@@ -0,0 +1,16 @@
//go:build windows
// +build windows
package main
import (
"github.com/sirupsen/logrus"
easy "github.com/t-tomalak/logrus-easy-formatter"
)
func init() {
// windows 不支持带颜色的 log故自定义格式
logrus.SetFormatter(&easy.Formatter{
LogFormat: "[%lvl%] %msg%\n",
})
}

View File

@@ -3,16 +3,18 @@ package acgimage
import (
"net/url"
"os"
"strconv"
"strings"
"time"
"github.com/FloatTech/AnimeAPI/classify"
"github.com/FloatTech/AnimeAPI/picture"
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/utils/file"
)
const (
@@ -21,13 +23,13 @@ const (
)
var (
botpath, _ = os.Getwd()
datapath = botpath + "/data/acgimage/"
cacheuri = "file:///" + datapath + "cache"
datapath = file.BOTPATH + "/data/acgimage/"
cacheuri = "file:///" + datapath + "cache"
// r18有一定保护一般不会发出图片
randapi = "&loli=true&r18=true"
msgof = make(map[int64]int64)
block = false
limit = rate.NewManager(time.Minute, 5)
)
func init() { // 插件主体
@@ -46,29 +48,29 @@ func init() { // 插件主体
Handle(func(ctx *zero.Ctx) {
url := ctx.State["regex_matched"].([]string)[1]
if !strings.HasPrefix(url, "http") {
ctx.Send("URL非法!")
ctx.SendChain(message.Text("URL非法!"))
} else {
randapi = url
ctx.Send("设置好啦")
ctx.SendChain(message.Text("设置好啦"))
}
})
// 有保护的随机图片
engine.OnFullMatch("随机图片", zero.OnlyGroup).SetBlock(true).SetPriority(24).
Handle(func(ctx *zero.Ctx) {
if classify.CanVisit(5) {
if classify.CanVisit(5) && limit.Load(ctx.Event.UserID).Acquire() {
go func() {
class, lastvisit, dhash, comment := classify.Classify(randapi, false)
replyClass(ctx, dhash, class, false, lastvisit, comment)
}()
} else {
ctx.Send("你太快啦!")
ctx.SendChain(message.Text("你太快啦!"))
}
})
// 直接随机图片无r18保护后果自负。如果出r18图可尽快通过发送"太涩了"撤回
engine.OnFullMatch("直接随机", zero.OnlyGroup, zero.AdminPermission).SetBlock(true).SetPriority(24).
Handle(func(ctx *zero.Ctx) {
if block {
ctx.Send("请稍后再试哦")
ctx.SendChain(message.Text("请稍后再试哦"))
} else if randapi != "" {
block = true
var url string
@@ -77,7 +79,7 @@ func init() { // 插件主体
} else {
url = randapi
}
setLastMsg(ctx.Event.GroupID, ctx.Send(message.Image(url).Add("cache", "0")))
setLastMsg(ctx.Event.GroupID, ctx.SendChain(message.Image(url).Add("cache", "0")))
block = false
}
})
@@ -89,7 +91,7 @@ func init() { // 插件主体
// 上传一张图进行评价
engine.OnKeywordGroup([]string{"评价图片"}, zero.OnlyGroup, picture.CmdMatch, picture.MustGiven).SetBlock(true).SetPriority(24).
Handle(func(ctx *zero.Ctx) {
ctx.Send("少女祈祷中...")
ctx.SendChain(message.Text("少女祈祷中..."))
for _, url := range ctx.State["image_url"].([]string) {
go func(target string) {
class, lastvisit, dhash, comment := classify.Classify(target, true)
@@ -101,7 +103,7 @@ func init() { // 插件主体
Handle(func(ctx *zero.Ctx) {
dhash := ctx.State["regex_matched"].([]string)[1]
if len(dhash) == 5*3 {
ctx.Send(message.Image(apihead + dhash))
ctx.SendChain(message.Image(apihead + dhash))
}
})
}
@@ -124,19 +126,18 @@ func replyClass(ctx *zero.Ctx, dhash string, class int, noimg bool, lv int64, co
if dhash != "" && !noimg {
b14, err3 := url.QueryUnescape(dhash)
if err3 == nil {
ctx.Send(comment + "\n给你点提示哦" + b14)
ctx.SendChain(message.Text(comment + "\n给你点提示哦" + b14))
ctx.Event.GroupID = 0
ctx.Send(img)
ctx.SendChain(img)
}
} else {
ctx.Send(comment)
ctx.SendChain(message.Text(comment))
}
} else {
comment := message.Text(comment)
if !noimg {
ctx.SendChain(img, comment)
ctx.SendChain(img, message.Text(comment))
} else {
ctx.SendChain(message.Reply(ctx.Event.MessageID), comment)
ctx.SendChain(message.Reply(ctx.Event.MessageID), message.Text(comment))
}
}
}

View File

@@ -2,13 +2,14 @@
package aifalse
import (
"fmt"
"math"
"os"
"time"
"github.com/shirou/gopsutil/cpu"
"github.com/shirou/gopsutil/disk"
"github.com/shirou/gopsutil/mem"
"github.com/shirou/gopsutil/v3/cpu"
"github.com/shirou/gopsutil/v3/disk"
"github.com/shirou/gopsutil/v3/mem"
"github.com/FloatTech/ZeroBot-Plugin/control"
@@ -20,14 +21,14 @@ func init() { // 插件主体
engine := control.Register("aifalse", &control.Options{
DisableOnDefault: false,
Help: "AIfalse\n" +
"- 查询计算机当前活跃度[检查身体|自检|启动自检|系统状态",
"- 查询计算机当前活跃度: [检查身体|自检|启动自检|系统状态]",
})
engine.OnFullMatchGroup([]string{"检查身体", "自检", "启动自检", "系统状态"}, zero.AdminPermission).SetBlock(true).
Handle(func(ctx *zero.Ctx) {
ctx.SendChain(message.Text(
"* CPU占用: ", cpuPercent(), "%\n",
"* RAM占用: ", memPercent(), "%\n",
"* 硬盘活动率: ", diskPercent(), "%",
"* CPU占用: ", cpuPercent(), "%\n",
"* RAM占用: ", memPercent(), "%\n",
"* 硬盘使用: ", diskPercent(),
),
)
})
@@ -35,9 +36,9 @@ func init() { // 插件主体
Handle(func(ctx *zero.Ctx) {
err := os.RemoveAll("data/cache/*")
if err != nil {
ctx.Send("错误: " + err.Error())
ctx.SendChain(message.Text("错误: ", err.Error()))
} else {
ctx.Send("成功!")
ctx.SendChain(message.Text("成功!"))
}
})
}
@@ -52,8 +53,15 @@ func memPercent() float64 {
return math.Round(memInfo.UsedPercent)
}
func diskPercent() float64 {
func diskPercent() string {
parts, _ := disk.Partitions(true)
diskInfo, _ := disk.Usage(parts[0].Mountpoint)
return math.Round(diskInfo.UsedPercent)
msg := ""
for _, p := range parts {
diskInfo, _ := disk.Usage(p.Mountpoint)
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
}

View File

@@ -11,33 +11,51 @@ import (
zero "github.com/wdvxdr1123/ZeroBot"
"github.com/wdvxdr1123/ZeroBot/message"
"github.com/FloatTech/ZeroBot-Plugin/control"
"github.com/FloatTech/ZeroBot-Plugin/utils/process"
)
var (
const (
// 服务名
servicename = "atri"
// ATRI 所有命令的优先级
prio = 2
prio = 5
// ATRI 表情的 codechina 镜像
res = "https://codechina.csdn.net/u011570312/ZeroBot-Plugin/-/raw/master/plugin_atri/"
// ATRI 的总开关
enable = true
)
func init() { // 插件主体
engine := control.Register(servicename, &control.Options{
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" +
"- 离谱\n- 答应我",
})
zero.OnFullMatch("ATRI醒醒", zero.AdminPermission).SetBlock(true).SetPriority(prio).
Handle(func(ctx *zero.Ctx) {
enable = true
time.Sleep(time.Second * 1)
ctx.SendChain(randText("嗯呜呜……夏生先生……?"))
c, ok := control.Lookup(servicename)
if ok && !c.IsEnabledIn(ctx.Event.GroupID) {
c.Enable(ctx.Event.GroupID)
process.SleepAbout1sTo2s()
ctx.SendChain(message.Text("嗯呜呜……夏生先生……?"))
}
})
zero.OnFullMatch("ATRI睡吧", zero.AdminPermission).SetBlock(true).SetPriority(prio).
engine.OnFullMatch("ATRI睡吧", zero.AdminPermission).SetBlock(true).SetPriority(prio).
Handle(func(ctx *zero.Ctx) {
enable = false
time.Sleep(time.Second * 1)
ctx.SendChain(randText("Zzz……Zzz……"))
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……"))
}
})
zero.OnFullMatch("萝卜子", atriSwitch(), atriSleep()).SetBlock(true).SetPriority(prio).
engine.OnFullMatch("萝卜子", atriSleep).SetBlock(true).SetPriority(prio).
Handle(func(ctx *zero.Ctx) {
time.Sleep(time.Second * 1)
process.SleepAbout1sTo2s()
switch rand.Intn(2) {
case 0:
ctx.SendChain(randText("萝卜子是对机器人的蔑称!", "是亚托莉......萝卜子可是对机器人的蔑称"))
@@ -45,20 +63,20 @@ func init() { // 插件主体
ctx.SendChain(randRecord("RocketPunch.amr"))
}
})
zero.OnFullMatchGroup([]string{"喜欢", "爱你", "爱", "suki", "daisuki", "すき", "好き", "贴贴", "老婆", "亲一个", "mua"}, atriSwitch(), atriSleep(), zero.OnlyToMe).SetBlock(true).SetPriority(prio).
engine.OnFullMatchGroup([]string{"喜欢", "爱你", "爱", "suki", "daisuki", "すき", "好き", "贴贴", "老婆", "亲一个", "mua"}, atriSleep, zero.OnlyToMe).SetBlock(true).SetPriority(prio).
Handle(func(ctx *zero.Ctx) {
time.Sleep(time.Second * 1)
process.SleepAbout1sTo2s()
ctx.SendChain(randImage("SUKI.jpg", "SUKI1.jpg", "SUKI2.png"))
})
zero.OnKeywordGroup([]string{"草你妈", "操你妈", "脑瘫", "废柴", "fw", "five", "废物", "战斗", "爬", "爪巴", "sb", "SB", "傻B"}, atriSwitch(), atriSleep(), zero.OnlyToMe).SetBlock(true).SetPriority(prio - 1).
engine.OnKeywordGroup([]string{"草你妈", "操你妈", "脑瘫", "废柴", "fw", "five", "废物", "战斗", "爬", "爪巴", "sb", "SB", "傻B"}, atriSleep, zero.OnlyToMe).SetBlock(true).SetPriority(prio - 1).
Handle(func(ctx *zero.Ctx) {
time.Sleep(time.Second * 1)
process.SleepAbout1sTo2s()
ctx.SendChain(randImage("FN.jpg", "WQ.jpg", "WQ1.jpg"))
})
zero.OnFullMatchGroup([]string{"早安", "早哇", "早上好", "ohayo", "哦哈哟", "お早う", "早好", "早"}, atriSwitch()).SetBlock(true).SetPriority(prio).
engine.OnFullMatchGroup([]string{"早安", "早哇", "早上好", "ohayo", "哦哈哟", "お早う", "早好", "早", "早早早"}).SetBlock(true).SetPriority(prio).
Handle(func(ctx *zero.Ctx) {
now := time.Now().Hour()
time.Sleep(time.Second * 1)
process.SleepAbout1sTo2s()
switch {
case now < 6: // 凌晨
ctx.SendChain(message.Reply(ctx.Event.MessageID), randText(
@@ -95,11 +113,11 @@ func init() { // 插件主体
))
}
})
zero.OnFullMatchGroup([]string{"中午好", "午安", "午好"}, atriSwitch()).SetBlock(true).SetPriority(prio).
engine.OnFullMatchGroup([]string{"中午好", "午安", "午好"}).SetBlock(true).SetPriority(prio).
Handle(func(ctx *zero.Ctx) {
now := time.Now().Hour()
if now > 11 && now < 15 { // 中午
time.Sleep(time.Second * 1)
process.SleepAbout1sTo2s()
ctx.SendChain(message.Reply(ctx.Event.MessageID), randText(
"午安w",
"午觉要好好睡哦ATRI会陪伴在你身旁的w",
@@ -108,10 +126,10 @@ func init() { // 插件主体
))
}
})
zero.OnFullMatchGroup([]string{"晚安", "oyasuminasai", "おやすみなさい", "晚好", "晚上好"}, atriSwitch()).SetBlock(true).SetPriority(prio).
engine.OnFullMatchGroup([]string{"晚安", "oyasuminasai", "おやすみなさい", "晚好", "晚上好"}).SetBlock(true).SetPriority(prio).
Handle(func(ctx *zero.Ctx) {
now := time.Now().Hour()
time.Sleep(time.Second * 1)
process.SleepAbout1sTo2s()
switch {
case now < 6: // 凌晨
ctx.SendChain(message.Reply(ctx.Event.MessageID), randText(
@@ -151,9 +169,9 @@ func init() { // 插件主体
))
}
})
zero.OnKeywordGroup([]string{"高性能", "太棒了", "すごい", "sugoi", "斯国一", "よかった"}, atriSwitch(), atriSleep(), zero.OnlyToMe).SetBlock(true).SetPriority(prio).
engine.OnKeywordGroup([]string{"高性能", "太棒了", "すごい", "sugoi", "斯国一", "よかった"}, atriSleep, zero.OnlyToMe).SetBlock(true).SetPriority(prio).
Handle(func(ctx *zero.Ctx) {
time.Sleep(time.Second * 1)
process.SleepAbout1sTo2s()
ctx.SendChain(randText(
"当然,我是高性能的嘛~",
"小事一桩,我是高性能的嘛",
@@ -172,9 +190,9 @@ func init() { // 插件主体
"呣......我的高性能,毫无遗憾地施展出来了......",
))
})
zero.OnKeywordGroup([]string{"没事", "没关系", "大丈夫", "还好", "不要紧", "没出大问题", "没伤到哪"}, atriSwitch(), atriSleep(), zero.OnlyToMe).SetBlock(true).SetPriority(prio).
engine.OnKeywordGroup([]string{"没事", "没关系", "大丈夫", "还好", "不要紧", "没出大问题", "没伤到哪"}, atriSleep, zero.OnlyToMe).SetBlock(true).SetPriority(prio).
Handle(func(ctx *zero.Ctx) {
time.Sleep(time.Second * 1)
process.SleepAbout1sTo2s()
ctx.SendChain(randText(
"当然,我是高性能的嘛~",
"没事没事,因为我是高性能的嘛!嗯哼!",
@@ -187,28 +205,28 @@ func init() { // 插件主体
))
})
zero.OnKeywordGroup([]string{"好吗", "是吗", "行不行", "能不能", "可不可以"}, atriSwitch(), atriSleep()).SetBlock(true).SetPriority(prio).
engine.OnKeywordGroup([]string{"好吗", "是吗", "行不行", "能不能", "可不可以"}, atriSleep).SetBlock(true).SetPriority(prio).
Handle(func(ctx *zero.Ctx) {
time.Sleep(time.Second * 1)
process.SleepAbout1sTo2s()
if rand.Intn(2) == 0 {
ctx.SendChain(randImage("YES.png", "NO.jpg"))
}
})
zero.OnKeywordGroup([]string{"啊这"}, atriSwitch(), atriSleep()).SetBlock(true).SetPriority(prio).
engine.OnKeywordGroup([]string{"啊这"}, atriSleep).SetBlock(true).SetPriority(prio).
Handle(func(ctx *zero.Ctx) {
time.Sleep(time.Second * 1)
process.SleepAbout1sTo2s()
if rand.Intn(2) == 0 {
ctx.SendChain(randImage("AZ.jpg", "AZ1.jpg"))
}
})
zero.OnKeywordGroup([]string{"我好了"}, atriSwitch(), atriSleep()).SetBlock(true).SetPriority(prio).
engine.OnKeywordGroup([]string{"我好了"}, atriSleep).SetBlock(true).SetPriority(prio).
Handle(func(ctx *zero.Ctx) {
time.Sleep(time.Second * 1)
process.SleepAbout1sTo2s()
ctx.SendChain(message.Reply(ctx.Event.MessageID), randText("不许好!", "憋回去!"))
})
zero.OnFullMatchGroup([]string{"", "?", "¿"}, atriSwitch(), atriSleep()).SetBlock(true).SetPriority(prio).
engine.OnFullMatchGroup([]string{"", "?", "¿"}, atriSleep).SetBlock(true).SetPriority(prio).
Handle(func(ctx *zero.Ctx) {
time.Sleep(time.Second * 1)
process.SleepAbout1sTo2s()
switch rand.Intn(5) {
case 0:
ctx.SendChain(randText("?", "", "嗯?", "(。´・ω・)ん?", "ん?"))
@@ -216,7 +234,7 @@ func init() { // 插件主体
ctx.SendChain(randImage("WH.jpg", "WH1.jpg", "WH2.jpg", "WH3.jpg"))
}
})
zero.OnKeyword("离谱", atriSwitch(), atriSleep()).SetBlock(true).SetPriority(prio).
engine.OnKeyword("离谱", atriSleep).SetBlock(true).SetPriority(prio).
Handle(func(ctx *zero.Ctx) {
switch rand.Intn(5) {
case 0:
@@ -225,41 +243,29 @@ func init() { // 插件主体
ctx.SendChain(randImage("WH.jpg"))
}
})
zero.OnKeyword("答应我", atriSwitch(), atriSleep(), zero.OnlyToMe).SetBlock(true).SetPriority(prio).
engine.OnKeyword("答应我", atriSleep, zero.OnlyToMe).SetBlock(true).SetPriority(prio).
Handle(func(ctx *zero.Ctx) {
time.Sleep(time.Second * 1)
process.SleepAbout1sTo2s()
ctx.SendChain(randText("我无法回应你的请求"))
})
}
func randText(text ...string) message.MessageSegment {
length := len(text)
return message.Text(text[rand.Intn(length)])
return message.Text(text[rand.Intn(len(text))])
}
func randImage(file ...string) message.MessageSegment {
length := len(file)
return message.Image(res + file[rand.Intn(length)])
return message.Image(res + file[rand.Intn(len(file))])
}
func randRecord(file ...string) message.MessageSegment {
length := len(file)
return message.Record(res + file[rand.Intn(length)])
}
// atriSwitch 控制 ATRI 的开关
func atriSwitch() zero.Rule {
return func(ctx *zero.Ctx) bool {
return enable
}
return message.Record(res + file[rand.Intn(len(file))])
}
// atriSleep 凌晨0点到6点ATRI 在睡觉,不回应任何请求
func atriSleep() zero.Rule {
return func(ctx *zero.Ctx) bool {
if now := time.Now().Hour(); now >= 1 && now < 6 {
return false
}
return true
func atriSleep(ctx *zero.Ctx) bool {
if now := time.Now().Hour(); now >= 1 && now < 6 {
return false
}
return true
}

76
plugin_b14/main.go Normal file
View File

@@ -0,0 +1,76 @@
// Package b14coder base16384 与 tea 加解密
package b14coder
import (
"unsafe"
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{
DisableOnDefault: false,
Help: "base16384加解密\n" +
"- 加密xxx\n- 解密xxx\n- 用yyy加密xxx\n- 用yyy解密xxx",
})
en.OnRegex(`^加密(.*)`).SetBlock(true).ThirdPriority().
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)))
} else {
ctx.SendChain(message.Text("加密失败!"))
}
})
en.OnRegex("^解密([\u4e00-\u8e00]*[\u3d01-\u3d06]?)$").SetBlock(true).ThirdPriority().
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)))
} else {
ctx.SendChain(message.Text("解密失败!"))
}
})
en.OnRegex(`^用(.*)加密(.*)`).SetBlock(true).ThirdPriority().
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))))
if err == nil {
ctx.SendChain(message.Text(helper.BytesToString(es)))
} else {
ctx.SendChain(message.Text("加密失败!"))
}
})
en.OnRegex("^用(.*)解密([\u4e00-\u8e00]*[\u3d01-\u3d06]?)$").SetBlock(true).ThirdPriority().
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))
if err == nil {
ctx.SendChain(message.Text(helper.BytesToString(t.Decrypt(base14.Decode(es)))))
} else {
ctx.SendChain(message.Text("解密失败!"))
}
})
}
func getea(key string) tea.TEA {
kr := []rune(key)
if len(kr) > 4 {
kr = kr[:4]
} else {
for len(kr) < 4 {
kr = append(kr, rune(4-len(kr)))
}
}
return *(*tea.TEA)(*(*unsafe.Pointer)(unsafe.Pointer(&kr)))
}

View File

@@ -5,9 +5,11 @@ import (
"net/http"
"time"
"github.com/robfig/cron"
"github.com/fumiama/cron"
zero "github.com/wdvxdr1123/ZeroBot"
"github.com/wdvxdr1123/ZeroBot/message"
"github.com/FloatTech/ZeroBot-Plugin/control"
)
type follower struct {
@@ -23,85 +25,128 @@ type follower struct {
// 开启日报推送
func init() {
engine.OnFullMatch("/开启粉丝日报", zero.AdminPermission).SetBlock(true).
fansDaily()
en := control.Register("fansdaily", &control.Options{
DisableOnDefault: true,
Help: "fansdaily\n- /开启粉丝日报\n- /关闭粉丝日报",
})
zero.OnCommand("开启粉丝日报", zero.AdminPermission).SetBlock(true).
Handle(func(ctx *zero.Ctx) {
fansDaily(ctx.Event.GroupID) // 群号传进去给下面发信息的函数
m, ok := control.Lookup("fansdaily")
if ok {
if m.IsEnabledIn(ctx.Event.GroupID) {
ctx.Send(message.Text("已启用!"))
} else {
m.Enable(ctx.Event.GroupID)
ctx.Send(message.Text("添加成功!"))
}
} else {
ctx.Send(message.Text("找不到该服务!"))
}
})
en.OnCommand("关闭粉丝日报", zero.AdminPermission).SetBlock(true).
Handle(func(ctx *zero.Ctx) {
m, ok := control.Lookup("fansdaily")
if ok {
if m.IsEnabledIn(ctx.Event.GroupID) {
m.Disable(ctx.Event.GroupID)
ctx.Send(message.Text("关闭成功!"))
} else {
ctx.Send(message.Text("未启用!"))
}
} else {
ctx.Send(message.Text("找不到该服务!"))
}
})
}
// 定时任务每天晚上最后2分钟执行一次
func fansDaily(groupID int64) {
func fansDaily() {
c := cron.New()
_ = c.AddFunc("0 58 23 * * ?", func() { fansData(groupID) })
c.Start()
_, err := c.AddFunc("58 23 * * *", func() { sendNotice() })
if err == nil {
c.Start()
}
}
// 获取数据拼接消息链并发送
func fansData(groupID int64) {
zero.RangeBot(func(id int64, ctx *zero.Ctx) bool {
var (
diana = fansapi("672328094")
ava = fansapi("672346917")
eileen = fansapi("672342685")
bella = fansapi("672353429")
carol = fansapi("351609538")
)
ctx.SendGroupMessage(
groupID,
message.Text(
time.Now().Format("2006-01-02"), " Asoul全团粉丝日报如下", "\n\n",
"uid: ", diana.Mid, "\n",
"名字: ", diana.Uname, "\n",
"当前粉丝数: ", diana.Follower, "\n",
"今日涨粉数: ", diana.Rise, "\n",
"视频投稿数: ", diana.Video, "\n",
"直播间id: ", diana.Roomid, "\n",
"舰队: ", diana.GuardNum, "\n",
"直播总排名: ", diana.AreaRank, "\n",
"数据来源: ", "https://vtbs.moe/detail/", "672328094", "\n\n",
func getMsg() message.MessageSegment {
var (
diana = fansapi("672328094")
ava = fansapi("672346917")
eileen = fansapi("672342685")
bella = fansapi("672353429")
carol = fansapi("351609538")
)
return message.Text(
time.Now().Format("2006-01-02"), " Asoul全团粉丝日报如下", "\n\n",
"uid: ", diana.Mid, "\n",
"名字: ", diana.Uname, "\n",
"当前粉丝数: ", diana.Follower, "\n",
"今日涨粉数: ", diana.Rise, "\n",
"视频投稿数: ", diana.Video, "\n",
"直播间id: ", diana.Roomid, "\n",
"舰队: ", diana.GuardNum, "\n",
"直播总排名: ", diana.AreaRank, "\n",
"数据来源: ", "https://vtbs.moe/detail/", "672328094", "\n\n",
"uid: ", ava.Mid, "\n",
"名字: ", ava.Uname, "\n",
"当前粉丝数: ", ava.Follower, "\n",
"今日涨粉数: ", ava.Rise, "\n",
"视频投稿数: ", ava.Video, "\n",
"直播间id: ", ava.Roomid, "\n",
"舰队: ", ava.GuardNum, "\n",
"直播总排名: ", ava.AreaRank, "\n",
"数据来源: ", "https://vtbs.moe/detail/", "672346917", "\n\n",
"uid: ", ava.Mid, "\n",
"名字: ", ava.Uname, "\n",
"当前粉丝数: ", ava.Follower, "\n",
"今日涨粉数: ", ava.Rise, "\n",
"视频投稿数: ", ava.Video, "\n",
"直播间id: ", ava.Roomid, "\n",
"舰队: ", ava.GuardNum, "\n",
"直播总排名: ", ava.AreaRank, "\n",
"数据来源: ", "https://vtbs.moe/detail/", "672346917", "\n\n",
"uid: ", eileen.Mid, "\n",
"名字: ", eileen.Uname, "\n",
"当前粉丝数: ", eileen.Follower, "\n",
"今日涨粉数: ", eileen.Rise, "\n",
"视频投稿数: ", eileen.Video, "\n",
"直播间id: ", eileen.Roomid, "\n",
"舰队: ", eileen.GuardNum, "\n",
"直播总排名: ", eileen.AreaRank, "\n",
"数据来源: ", "https://vtbs.moe/detail/", "672342685", "\n\n",
"uid: ", eileen.Mid, "\n",
"名字: ", eileen.Uname, "\n",
"当前粉丝数: ", eileen.Follower, "\n",
"今日涨粉数: ", eileen.Rise, "\n",
"视频投稿数: ", eileen.Video, "\n",
"直播间id: ", eileen.Roomid, "\n",
"舰队: ", eileen.GuardNum, "\n",
"直播总排名: ", eileen.AreaRank, "\n",
"数据来源: ", "https://vtbs.moe/detail/", "672342685", "\n\n",
"uid: ", bella.Mid, "\n",
"名字: ", bella.Uname, "\n",
"当前粉丝数: ", bella.Follower, "\n",
"今日涨粉数: ", bella.Rise, "\n",
"视频投稿数: ", bella.Video, "\n",
"直播间id: ", bella.Roomid, "\n",
"舰队: ", bella.GuardNum, "\n",
"直播总排名: ", bella.AreaRank, "\n",
"数据来源: ", "https://vtbs.moe/detail/", "672353429", "\n\n",
"uid: ", bella.Mid, "\n",
"名字: ", bella.Uname, "\n",
"当前粉丝数: ", bella.Follower, "\n",
"今日涨粉数: ", bella.Rise, "\n",
"视频投稿数: ", bella.Video, "\n",
"直播间id: ", bella.Roomid, "\n",
"舰队: ", bella.GuardNum, "\n",
"直播总排名: ", bella.AreaRank, "\n",
"数据来源: ", "https://vtbs.moe/detail/", "672353429", "\n\n",
"uid: ", carol.Mid, "\n",
"名字: ", carol.Uname, "\n",
"当前粉丝数: ", carol.Follower, "\n",
"今日涨粉数: ", carol.Rise, "\n",
"视频投稿数: ", carol.Video, "\n",
"直播间id: ", carol.Roomid, "\n",
"舰队: ", carol.GuardNum, "\n",
"直播总排名: ", carol.AreaRank, "\n",
"数据来源: ", "https://vtbs.moe/detail/", "351609538",
))
return true
})
"uid: ", carol.Mid, "\n",
"名字: ", carol.Uname, "\n",
"当前粉丝数: ", carol.Follower, "\n",
"今日涨粉数: ", carol.Rise, "\n",
"视频投稿数: ", carol.Video, "\n",
"直播间id: ", carol.Roomid, "\n",
"舰队: ", carol.GuardNum, "\n",
"直播总排名: ", carol.AreaRank, "\n",
"数据来源: ", "https://vtbs.moe/detail/", "351609538",
)
}
// 获取数据拼接消息链并发送
func sendNotice() {
m, ok := control.Lookup("fansdaily")
if ok {
msg := getMsg()
zero.RangeBot(func(id int64, ctx *zero.Ctx) bool {
for _, g := range ctx.GetGroupList().Array() {
grp := g.Get("group_id").Int()
if m.IsEnabledIn(grp) {
ctx.SendGroupMessage(grp, msg)
}
}
return true
})
}
}
// 请求api

View File

@@ -12,17 +12,15 @@ import (
"github.com/FloatTech/ZeroBot-Plugin/control"
)
var engine *zero.Engine
var engine = control.Register("bilibili", &control.Options{
DisableOnDefault: false,
Help: "bilibili\n" +
"- >vup info [名字|uid]\n" +
"- >user info [名字|uid]",
})
// 查成分的
func init() {
engine = control.Register("bilibili", &control.Options{
DisableOnDefault: false,
Help: "bilibili\n" +
"- >vup info [名字|uid]\n" +
"- >user info [名字|uid]\n" +
"- /开启粉丝日报",
})
engine.OnRegex(`^>user info\s(.{1,25})$`).SetBlock(true).
Handle(func(ctx *zero.Ctx) {
keyword := ctx.State["regex_matched"].([]string)[1]

View File

@@ -0,0 +1,28 @@
package plugin_book_review
import (
zero "github.com/wdvxdr1123/ZeroBot"
"github.com/wdvxdr1123/ZeroBot/message"
"github.com/FloatTech/ZeroBot-Plugin/control"
)
func init() {
engine := control.Register("bookreview", &control.Options{
DisableOnDefault: false,
Help: "哀伤雪刃推书记录\n- 书评[xxx]\n- 随机书评",
})
// 中文、英文、数字但不包括下划线等符号
engine.OnRegex("^书评([\u4E00-\u9FA5A-Za-z0-9]{1,25})$").SetBlock(true).
Handle(func(ctx *zero.Ctx) {
b := getBookReviewByKeyword(ctx.State["regex_matched"].([]string)[1])
ctx.SendChain(message.Text(b.BookReview))
})
engine.OnFullMatch("随机书评").SetBlock(true).
Handle(func(ctx *zero.Ctx) {
br := getRandomBookReview()
ctx.SendChain(message.Text(br.BookReview))
})
}

View File

@@ -0,0 +1,35 @@
package plugin_book_review
import (
"os"
log "github.com/sirupsen/logrus"
"github.com/FloatTech/ZeroBot-Plugin/utils/file"
"github.com/FloatTech/ZeroBot-Plugin/utils/process"
"github.com/FloatTech/ZeroBot-Plugin/utils/sql"
)
const dbpath = "data/BookReview/"
const dbfile = dbpath + "bookreview.db"
var db = &sql.Sqlite{DBPath: dbfile}
// 加载数据库
func init() {
go func() {
process.SleepAbout1sTo2s()
// os.RemoveAll(dbpath)
_ = os.MkdirAll(dbpath, 0755)
_, _ = file.GetLazyData(dbfile, false, true)
err := db.Create("bookreview", &book{})
if err != nil {
panic(err)
}
n, err := db.Count("bookreview")
if err != nil {
panic(err)
}
log.Printf("[bookreview]读取%d条书评", n)
}()
}

View File

@@ -0,0 +1,43 @@
package main
import (
"os"
"github.com/FloatTech/ZeroBot-Plugin/utils/sql"
)
type book struct {
Id uint64 `db:"id"`
BookReview string `db:"bookreview"`
}
func main() {
db, err := Open(os.Args[1])
if err != nil {
panic(err)
}
newdb := &sql.Sqlite{DBPath: os.Args[2]}
err = newdb.Create("bookreview", &book{})
if err != nil {
panic(err)
}
rs, err := db.Table("book_review").Select("book_review", "").Rows()
if err != nil {
panic(err)
}
var d string
var i uint64
for rs.Next() {
err := rs.Scan(&d)
if err != nil {
panic(err)
}
i++
err = newdb.Insert("bookreview", &book{i, d})
if err != nil {
panic(err)
}
}
db.Close()
newdb.Close()
}

View File

@@ -0,0 +1,46 @@
package main
import (
"os"
"github.com/jinzhu/gorm"
_ "github.com/logoove/sqlite"
)
type BrDB = gorm.DB
func Initialize(dbpath string) *BrDB {
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(&BookReview{})
return (*BrDB)(gdb)
}
func Open(dbpath string) (*BrDB, error) {
db, err := gorm.Open("sqlite3", dbpath)
if err != nil {
return nil, err
} else {
return (*BrDB)(db), nil
}
}
type BookReview struct {
gorm.Model
BookReview string `gorm:"column:book_review"`
}
func (BookReview) TableName() string {
return "book_review"
}

View File

@@ -0,0 +1,17 @@
package plugin_book_review
type book struct {
Id uint64 `db:"id"`
BookReview string `db:"bookreview"`
}
// 暂时随机选择一个书评
func getBookReviewByKeyword(keyword string) (b book) {
db.Find("bookreview", &b, "where bookreview LIKE '%"+keyword+"%'")
return
}
func getRandomBookReview() (b book) {
db.Pick("bookreview", &b)
return
}

View File

@@ -9,13 +9,20 @@ import (
zero "github.com/wdvxdr1123/ZeroBot"
"github.com/wdvxdr1123/ZeroBot/extension/rate"
"github.com/wdvxdr1123/ZeroBot/message"
"github.com/FloatTech/ZeroBot-Plugin/control"
)
var poke = rate.NewManager(time.Minute*5, 8) // 戳一戳
var engine = control.Register("chat", &control.Options{
DisableOnDefault: false,
Help: "chat\n- [BOT名字]\n- [戳一戳BOT]\n- 空调开\n- 空调关\n- 群温度\n- 设置温度[正整数]",
})
func init() { // 插件主体
// 被喊名字
zero.OnFullMatch("", zero.OnlyToMe).SetBlock(false).FirstPriority().
engine.OnFullMatch("", zero.OnlyToMe).SetBlock(true).FirstPriority().
Handle(func(ctx *zero.Ctx) {
var nickname = zero.BotConfig.NickName[0]
time.Sleep(time.Second * 1)
@@ -29,15 +36,15 @@ func init() { // 插件主体
))
})
// 戳一戳
zero.On("notice/notify/poke", zero.OnlyToMe).SetBlock(false).FirstPriority().
engine.On("notice/notify/poke", zero.OnlyToMe).SetBlock(false).FirstPriority().
Handle(func(ctx *zero.Ctx) {
var nickname = zero.BotConfig.NickName[0]
switch {
case poke.Load(ctx.Event.UserID).AcquireN(3):
case poke.Load(ctx.Event.GroupID).AcquireN(3):
// 5分钟共8块命令牌 一次消耗3块命令牌
time.Sleep(time.Second * 1)
ctx.SendChain(message.Text("请不要戳", nickname, " >_<"))
case poke.Load(ctx.Event.UserID).Acquire():
case poke.Load(ctx.Event.GroupID).Acquire():
// 5分钟共8块命令牌 一次消耗1块命令牌
time.Sleep(time.Second * 1)
ctx.SendChain(message.Text("喂(#`O) 戳", nickname, "干嘛!"))
@@ -48,18 +55,18 @@ func init() { // 插件主体
// 群空调
var AirConditTemp = map[int64]int{}
var AirConditSwitch = map[int64]bool{}
zero.OnFullMatch("空调开").SetBlock(true).FirstPriority().
engine.OnFullMatch("空调开").SetBlock(true).FirstPriority().
Handle(func(ctx *zero.Ctx) {
AirConditSwitch[ctx.Event.GroupID] = true
ctx.SendChain(message.Text("❄️哔~"))
})
zero.OnFullMatch("空调关").SetBlock(true).FirstPriority().
engine.OnFullMatch("空调关").SetBlock(true).FirstPriority().
Handle(func(ctx *zero.Ctx) {
AirConditSwitch[ctx.Event.GroupID] = false
delete(AirConditTemp, ctx.Event.GroupID)
ctx.SendChain(message.Text("💤哔~"))
})
zero.OnRegex(`设置温度(\d+)`).SetBlock(true).FirstPriority().
engine.OnRegex(`设置温度(\d+)`).SetBlock(true).FirstPriority().
Handle(func(ctx *zero.Ctx) {
if _, exist := AirConditTemp[ctx.Event.GroupID]; !exist {
AirConditTemp[ctx.Event.GroupID] = 26
@@ -78,7 +85,7 @@ func init() { // 插件主体
))
}
})
zero.OnFullMatch(`群温度`).SetBlock(true).FirstPriority().
engine.OnFullMatch(`群温度`).SetBlock(true).FirstPriority().
Handle(func(ctx *zero.Ctx) {
if _, exist := AirConditTemp[ctx.Event.GroupID]; !exist {
AirConditTemp[ctx.Event.GroupID] = 26

View File

@@ -17,7 +17,7 @@ func init() {
DisableOnDefault: false,
Help: "choose\n" +
"- 选择可口可乐还是百事可乐\n" +
"- 选择肯德基还是麦当劳还是必胜客\n",
"- 选择肯德基还是麦当劳还是必胜客",
})
engine.OnPrefix("选择").SetBlock(true).FirstPriority().Handle(handle)
}

67
plugin_coser/coser.go Normal file
View File

@@ -0,0 +1,67 @@
// Package coser images
package coser
import (
"time"
"github.com/tidwall/gjson"
log "github.com/sirupsen/logrus"
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"
"github.com/FloatTech/ZeroBot-Plugin/utils/web"
)
var (
engine = control.Register("coser", &control.Options{
DisableOnDefault: false,
Help: "三次元小姐姐\n- coser\n",
})
prio = 20
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"
limit = rate.NewManager(time.Minute, 5)
)
func init() {
engine.OnFullMatch("coser", zero.OnlyGroup).SetBlock(true).SetPriority(prio).
Handle(func(ctx *zero.Ctx) {
if !limit.Load(ctx.Event.GroupID).Acquire() {
ctx.SendChain(message.Text("请稍后重试0x0..."))
return
}
ctx.SendChain(message.Text("少女祈祷中......"))
data, err := web.ReqWith(coserURL, "GET", "", ua)
if err != nil {
log.Println("err为:", err)
}
var m message.Message
text := gjson.Get(helper.BytesToString(data), "data.Title").String()
m = append(m,
message.CustomNode(
zero.BotConfig.NickName[0],
ctx.Event.SelfID,
text,
))
gjson.Get(helper.BytesToString(data), "data.data").ForEach(func(_, value gjson.Result) bool {
imgcq := `[CQ:image,file=` + value.String() + `]`
m = append(m,
message.CustomNode(
zero.BotConfig.NickName[0],
ctx.Event.SelfID,
imgcq),
)
return true
})
if id := ctx.SendGroupForwardMessage(
ctx.Event.GroupID,
m).Get("message_id").Int(); id == 0 {
ctx.SendChain(message.Text("ERROR: 可能被风控了"))
}
})
}

View File

@@ -2,11 +2,11 @@
package diana
import (
fmt "fmt"
"math/rand"
"time"
zero "github.com/wdvxdr1123/ZeroBot"
"github.com/wdvxdr1123/ZeroBot/message"
"github.com/FloatTech/ZeroBot-Plugin/control"
"github.com/FloatTech/ZeroBot-Plugin/plugin_diana/data"
@@ -28,22 +28,22 @@ func init() {
Handle(func(ctx *zero.Ctx) {
rand.Seed(time.Now().UnixNano())
// 绕过第一行发病
ctx.Send((*data.Array)[rand.Intn(len(*data.Array)-1)+1])
ctx.SendChain(message.Text(data.RandText()))
})
// 逆天
engine.OnFullMatch("发大病").SetBlock(true).
Handle(func(ctx *zero.Ctx) {
// 第一行是发病
ctx.Send((*data.Array)[0])
ctx.SendChain(message.Text(data.HentaiText()))
})
// 增加小作文
engine.OnRegex(`^教你一篇小作文(.*)$`, zero.AdminPermission).SetBlock(true).
Handle(func(ctx *zero.Ctx) {
err := data.AddText(ctx.State["regex_matched"].([]string)[1])
if err != nil {
ctx.Send(fmt.Sprintf("ERROR: %v", err))
ctx.SendChain(message.Text("ERROR: ", err))
} else {
ctx.Send("记住啦!")
ctx.SendChain(message.Text("记住啦!"))
}
})
}

View File

@@ -3,135 +3,79 @@ package data
import (
"crypto/md5"
"io"
"net/http"
"encoding/binary"
"os"
"sync"
"time"
"unsafe"
log "github.com/sirupsen/logrus"
"github.com/wdvxdr1123/ZeroBot/utils/helper"
"github.com/FloatTech/ZeroBot-Plugin/utils/file"
"github.com/FloatTech/ZeroBot-Plugin/utils/process"
"github.com/FloatTech/ZeroBot-Plugin/utils/sql"
)
const (
datapath = "data/Diana"
pbfile = datapath + "/text.pb"
pburl = "https://codechina.csdn.net/u011570312/ZeroBot-Plugin/-/raw/master/" + pbfile
dbfile = datapath + "/text.db"
)
var (
compo Composition
// Array 小作文数组指针
Array = &compo.Array
// m 小作文保存锁
m sync.Mutex
// md5s 验证重复
md5s []*[16]byte
)
var db = sql.Sqlite{DBPath: dbfile}
type Text struct {
Id int64 `db:"id"`
Data string `db:"data"`
}
func init() {
go func() {
time.Sleep(time.Second)
process.SleepAbout1sTo2s()
err := os.MkdirAll(datapath, 0755)
if err != nil {
panic(err)
}
err1 := LoadText()
if err1 == nil {
arrl := len(*Array)
log.Printf("[Diana]读取%d条小作文", arrl)
md5s = make([]*[16]byte, arrl)
for i, t := range *Array {
m := md5.Sum(str2bytes(t))
md5s[i] = &m
err = LoadText()
if err == nil {
err = db.Create("text", &Text{})
if err != nil {
panic(err)
}
c, _ := db.Count("text")
log.Printf("[Diana]读取%d条小作文", c)
} else {
log.Printf("[Diana]读取小作文错误:%v", err1)
log.Printf("[Diana]读取小作文错误:%v", err)
}
}()
}
// LoadText 加载小作文
func LoadText() error {
if _, err := os.Stat(pbfile); err == nil || os.IsExist(err) {
f, err := os.Open(pbfile)
if err == nil {
defer f.Close()
data, err1 := io.ReadAll(f)
if err1 == nil {
if len(data) > 0 {
return compo.Unmarshal(data)
}
}
return err1
}
} else { // 如果没有小作文,则从 url 下载
f, err := os.Create(pbfile)
if err != nil {
return err
}
defer f.Close()
resp, err := http.Get(pburl)
if err == nil {
defer resp.Body.Close()
if resp.ContentLength > 0 {
log.Printf("[Diana]从镜像下载小作文%d字节...", resp.ContentLength)
data, err := io.ReadAll(resp.Body)
if err == nil && len(data) > 0 {
_, _ = f.Write(data)
return compo.Unmarshal(data)
}
return err
}
return nil
}
return err
}
return nil
_, err := file.GetLazyData(dbfile, false, false)
return err
}
// AddText 添加小作文
func AddText(txt string) error {
sum := md5.Sum(str2bytes(txt))
if txt != "" && !isin(&sum) {
m.Lock()
defer m.Unlock()
compo.Array = append(compo.Array, txt)
md5s = append(md5s, &sum)
return savecompo()
}
return nil
s := md5.Sum(helper.StringToBytes(txt))
i := binary.LittleEndian.Uint64(s[:8])
return db.Insert("text", &Text{Id: int64(i), Data: txt})
}
func isin(sum *[16]byte) bool {
for _, t := range md5s {
if *t == *sum {
return true
}
// RandText 随机小作文
func RandText() string {
var t Text
err := db.Pick("text", &t)
if err != nil {
return err.Error()
}
return false
return t.Data
}
// savecompo 同步保存作文
func savecompo() error {
data, err := compo.Marshal()
if err == nil {
if _, err := os.Stat(datapath); err == nil || os.IsExist(err) {
f, err1 := os.OpenFile(pbfile, os.O_WRONLY|os.O_TRUNC|os.O_CREATE, 0644)
if err1 == nil {
_, err2 := f.Write(data)
f.Close()
return err2
}
return err1
}
// HentaiText 发大病
func HentaiText() string {
var t Text
err := db.Find("text", &t, "where id = -3802576048116006195")
if err != nil {
return err.Error()
}
return err
}
// str2bytes Fast convert
func str2bytes(s string) []byte {
x := (*[2]uintptr)(unsafe.Pointer(&s))
h := [3]uintptr{x[0], x[1], x[1]}
return *(*[]byte)(unsafe.Pointer(&h))
return t.Data
}

View File

@@ -1,326 +0,0 @@
// Code generated by protoc-gen-gogo. DO NOT EDIT.
// source: text.proto
package data
import (
fmt "fmt"
proto "github.com/golang/protobuf/proto"
io "io"
math "math"
math_bits "math/bits"
)
// Reference imports to suppress errors if they are not otherwise used.
var _ = proto.Marshal
var _ = fmt.Errorf
var _ = math.Inf
// This is a compile-time assertion to ensure that this generated file
// is compatible with the proto package it is being compiled against.
// A compilation error at this line likely means your copy of the
// proto package needs to be updated.
const _ = proto.ProtoPackageIsVersion3 // please upgrade the proto package
type Composition struct {
Array []string `protobuf:"bytes,1,rep,name=array,proto3" json:"array,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
func (m *Composition) Reset() { *m = Composition{} }
func (m *Composition) String() string { return proto.CompactTextString(m) }
func (*Composition) ProtoMessage() {}
func (*Composition) Descriptor() ([]byte, []int) {
return fileDescriptor_a8e73d1ce47f9297, []int{0}
}
func (m *Composition) XXX_Unmarshal(b []byte) error {
return m.Unmarshal(b)
}
func (m *Composition) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
if deterministic {
return xxx_messageInfo_Composition.Marshal(b, m, deterministic)
} else {
b = b[:cap(b)]
n, err := m.MarshalToSizedBuffer(b)
if err != nil {
return nil, err
}
return b[:n], nil
}
}
func (m *Composition) XXX_Merge(src proto.Message) {
xxx_messageInfo_Composition.Merge(m, src)
}
func (m *Composition) XXX_Size() int {
return m.Size()
}
func (m *Composition) XXX_DiscardUnknown() {
xxx_messageInfo_Composition.DiscardUnknown(m)
}
var xxx_messageInfo_Composition proto.InternalMessageInfo
func (m *Composition) GetArray() []string {
if m != nil {
return m.Array
}
return nil
}
func init() {
proto.RegisterType((*Composition)(nil), "diana.composition")
}
func init() { proto.RegisterFile("text.proto", fileDescriptor_a8e73d1ce47f9297) }
var fileDescriptor_a8e73d1ce47f9297 = []byte{
// 100 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0xe2, 0x2a, 0x49, 0xad, 0x28,
0xd1, 0x2b, 0x28, 0xca, 0x2f, 0xc9, 0x17, 0x62, 0x4d, 0xc9, 0x4c, 0xcc, 0x4b, 0x54, 0x52, 0xe6,
0xe2, 0x4e, 0xce, 0xcf, 0x2d, 0xc8, 0x2f, 0xce, 0x2c, 0xc9, 0xcc, 0xcf, 0x13, 0x12, 0xe1, 0x62,
0x4d, 0x2c, 0x2a, 0x4a, 0xac, 0x94, 0x60, 0x54, 0x60, 0xd6, 0xe0, 0x0c, 0x82, 0x70, 0x9c, 0x04,
0x4e, 0x3c, 0x92, 0x63, 0xbc, 0xf0, 0x48, 0x8e, 0xf1, 0xc1, 0x23, 0x39, 0xc6, 0x19, 0x8f, 0xe5,
0x18, 0x92, 0xd8, 0xc0, 0x86, 0x18, 0x03, 0x02, 0x00, 0x00, 0xff, 0xff, 0x22, 0x35, 0x44, 0xcb,
0x52, 0x00, 0x00, 0x00,
}
func (m *Composition) Marshal() (dAtA []byte, err error) {
size := m.Size()
dAtA = make([]byte, size)
n, err := m.MarshalToSizedBuffer(dAtA[:size])
if err != nil {
return nil, err
}
return dAtA[:n], nil
}
func (m *Composition) MarshalTo(dAtA []byte) (int, error) {
size := m.Size()
return m.MarshalToSizedBuffer(dAtA[:size])
}
func (m *Composition) MarshalToSizedBuffer(dAtA []byte) (int, error) {
i := len(dAtA)
_ = i
var l int
_ = l
if m.XXX_unrecognized != nil {
i -= len(m.XXX_unrecognized)
copy(dAtA[i:], m.XXX_unrecognized)
}
if len(m.Array) > 0 {
for iNdEx := len(m.Array) - 1; iNdEx >= 0; iNdEx-- {
i -= len(m.Array[iNdEx])
copy(dAtA[i:], m.Array[iNdEx])
i = encodeVarintText(dAtA, i, uint64(len(m.Array[iNdEx])))
i--
dAtA[i] = 0xa
}
}
return len(dAtA) - i, nil
}
func encodeVarintText(dAtA []byte, offset int, v uint64) int {
offset -= sovText(v)
base := offset
for v >= 1<<7 {
dAtA[offset] = uint8(v&0x7f | 0x80)
v >>= 7
offset++
}
dAtA[offset] = uint8(v)
return base
}
func (m *Composition) Size() (n int) {
if m == nil {
return 0
}
var l int
_ = l
if len(m.Array) > 0 {
for _, s := range m.Array {
l = len(s)
n += 1 + l + sovText(uint64(l))
}
}
if m.XXX_unrecognized != nil {
n += len(m.XXX_unrecognized)
}
return n
}
func sovText(x uint64) (n int) {
return (math_bits.Len64(x|1) + 6) / 7
}
func sozText(x uint64) (n int) {
return sovText(uint64((x << 1) ^ uint64((int64(x) >> 63))))
}
func (m *Composition) Unmarshal(dAtA []byte) error {
l := len(dAtA)
iNdEx := 0
for iNdEx < l {
preIndex := iNdEx
var wire uint64
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowText
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
wire |= uint64(b&0x7F) << shift
if b < 0x80 {
break
}
}
fieldNum := int32(wire >> 3)
wireType := int(wire & 0x7)
if wireType == 4 {
return fmt.Errorf("proto: composition: wiretype end group for non-group")
}
if fieldNum <= 0 {
return fmt.Errorf("proto: composition: illegal tag %d (wire type %d)", fieldNum, wire)
}
switch fieldNum {
case 1:
if wireType != 2 {
return fmt.Errorf("proto: wrong wireType = %d for field Array", wireType)
}
var stringLen uint64
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowText
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
stringLen |= uint64(b&0x7F) << shift
if b < 0x80 {
break
}
}
intStringLen := int(stringLen)
if intStringLen < 0 {
return ErrInvalidLengthText
}
postIndex := iNdEx + intStringLen
if postIndex < 0 {
return ErrInvalidLengthText
}
if postIndex > l {
return io.ErrUnexpectedEOF
}
m.Array = append(m.Array, string(dAtA[iNdEx:postIndex]))
iNdEx = postIndex
default:
iNdEx = preIndex
skippy, err := skipText(dAtA[iNdEx:])
if err != nil {
return err
}
if (skippy < 0) || (iNdEx+skippy) < 0 {
return ErrInvalidLengthText
}
if (iNdEx + skippy) > l {
return io.ErrUnexpectedEOF
}
m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...)
iNdEx += skippy
}
}
if iNdEx > l {
return io.ErrUnexpectedEOF
}
return nil
}
func skipText(dAtA []byte) (n int, err error) {
l := len(dAtA)
iNdEx := 0
depth := 0
for iNdEx < l {
var wire uint64
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return 0, ErrIntOverflowText
}
if iNdEx >= l {
return 0, io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
wire |= (uint64(b) & 0x7F) << shift
if b < 0x80 {
break
}
}
wireType := int(wire & 0x7)
switch wireType {
case 0:
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return 0, ErrIntOverflowText
}
if iNdEx >= l {
return 0, io.ErrUnexpectedEOF
}
iNdEx++
if dAtA[iNdEx-1] < 0x80 {
break
}
}
case 1:
iNdEx += 8
case 2:
var length int
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return 0, ErrIntOverflowText
}
if iNdEx >= l {
return 0, io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
length |= (int(b) & 0x7F) << shift
if b < 0x80 {
break
}
}
if length < 0 {
return 0, ErrInvalidLengthText
}
iNdEx += length
case 3:
depth++
case 4:
if depth == 0 {
return 0, ErrUnexpectedEndOfGroupText
}
depth--
case 5:
iNdEx += 4
default:
return 0, fmt.Errorf("proto: illegal wireType %d", wireType)
}
if iNdEx < 0 {
return 0, ErrInvalidLengthText
}
if depth == 0 {
return iNdEx, nil
}
}
return 0, io.ErrUnexpectedEOF
}
var (
ErrInvalidLengthText = fmt.Errorf("proto: negative length found during unmarshaling")
ErrIntOverflowText = fmt.Errorf("proto: integer overflow")
ErrUnexpectedEndOfGroupText = fmt.Errorf("proto: unexpected end of group")
)

View File

@@ -1,6 +0,0 @@
syntax = "proto3";
package data;
message composition {
repeated string array = 1;
}

View File

@@ -1,52 +0,0 @@
// Package convert 转换txt到pb
package main
import (
"bufio"
"fmt"
"os"
"github.com/FloatTech/ZeroBot-Plugin/plugin_diana/data"
)
var (
compo data.Composition
)
func init() {
compo.Array = make([]string, 0, 64)
}
// 参数txt文件位置 pb文件位置
func main() {
file, err := os.Open(os.Args[1])
if err != nil {
panic(err)
}
defer file.Close()
scanner := bufio.NewScanner(file)
// optionally, resize scanner's capacity for lines over 64K, see next example
for scanner.Scan() {
// fmt.Println(scanner.Text())
compo.Array = append(compo.Array, scanner.Text())
}
if err := scanner.Err(); err != nil {
panic(err)
}
data, _ := compo.Marshal()
f, err1 := os.OpenFile(os.Args[2], os.O_WRONLY|os.O_TRUNC|os.O_CREATE, 0644)
if err1 == nil {
defer f.Close()
_, err2 := f.Write(data)
if err2 == nil {
fmt.Println("成功")
} else {
panic(err2)
}
} else {
panic(err1)
}
}

View File

@@ -1,42 +0,0 @@
贝拉抽我🥵嘉然骑在我背上🥵晚晚踩我🥵乃琳坐在王座是用看垃圾的眼神看我🥵🥵珈乐踢我🥵🥵,把我眼睛蒙住然后五只脚一起踩我🥵还让我猜脚是谁的,猜错了给我劈眼一铁棍🥵
我好想做嘉然小姐的狗啊。可是嘉然小姐说她喜欢的是猫,我哭了。我知道既不是狗也不是猫的我为什么要哭的。因为我其实是一只老鼠。我从没奢望嘉然小姐能喜欢自己。我明白的,所有人都喜欢理解余裕上手天才打钱的萌萌的狗狗或者猫猫,没有人会喜欢阴湿带病的老鼠。但我还是问了嘉然小姐:“我能不能做你的狗?”我知道我是注定做不了狗的。但如果她喜欢狗,我就可以一直在身边看着她了,哪怕她怀里抱着的永远都是狗。可是她说喜欢的是猫。她现在还在看着我,还在逗我开心,是因为猫还没有出现,只有我这老鼠每天蹑手蹑脚地从洞里爬出来,远远地和她对视。等她喜欢的猫来了的时候,我就该重新滚回我的洞了吧。但我还是好喜欢她,她能在我还在她身边的时候多看我几眼吗?嘉然小姐说接下来的每个圣诞夜都要和大家一起过。我不知道大家指哪些人。好希望这个集合能够对我做一次胞吞。猫猫还在害怕嘉然小姐。我会去把她爱的猫猫引来的。我知道稍有不慎,我就会葬身猫口。那时候嘉然小姐大概会把我的身体好好地装起来扔到门外吧。那我就成了一包鼠条,嘻嘻。我希望她能把我扔得近一点,因为我还是好喜欢她。会一直喜欢下去的。我的灵魂透过窗户向里面看去,挂着的铃铛在轻轻鸣响,嘉然小姐慵懒地靠在沙发上,表演得非常温顺的橘猫坐在她的肩膀。壁炉的火光照在她的脸庞,我冻僵的心脏在风里微微发烫。
ダイアナさんの犬になりたいな.けど ダイアナさんが「猫が好き」と言った あさりと僕は泣いてしまった.猫でも犬でもない僕が 何故泣いたのかな 僕は知っていた.何故なら 僕は鼠だったからさ~..一度もダイアナさんに好かれることを望まながった .僕はわがっていた 誰もかも理解余裕上手天才の可愛い金持ちの犬か猫がすきってことを.陰キャで汚らわし鼠は誰も好かんのだ..それでもDianaさんに聞いてみたDianaさんの犬になっても良いですかと.僕には一生犬になれないことは分かっている.でももし彼女は犬が好きだったら 僕はずっと傍で見ていられる.それはいつも違ういぬをだいいているとしても..けれど彼女は猫が好きだった.彼女は今にも僕を見ている ぼくを楽しませてくれる それは猫がまだ表れていないから.ただこの鼠が毎日こそこそと穴から出て 遠くから目を合っている.彼女の好きな猫が現れたら 僕は穴に戻るしかないな..でもやっぱり彼女が好きでいられない 僕がまだ傍にいるときぐらい もうちょとみてくれるのかな.Dianaさんがこれから毎回のクリスマスみんなと一緒に過ごすで言った みんながどの渡りのみんなかな.僕にも中に入れたれいいな..猫はまだまだDianaさんを怖がっている.僕が彼女の愛する猫を引き付けるよ.少しのミスで猫の口に死ぬことはわがっている.その時になるとたぶんDianaさんは僕の体をちゃんと包み込んで 玄関外に捨てるでしょ.それで僕はフライト鼠になった くす.彼女に少し近く捨ててほしいな だってやっぱり好きだから それずっと好きでいる..僕のたましは窓を越して中を見る 吊っている鈴が少し響って.Dianaさんはだらりとソファーに寄りかかて 手慣れているふりをする橘猫が彼女の肩に座って.暖炉の光が彼女の顔を照らした 凍り付いた僕の心臓はかぜの中で熱くなっていく
枝江小镇新来了一位修女,善良温柔的修女。她不爱讲话,只是微笑着面对每一个对她祷告的人。有的人希望孩子健康,有的人希望庄稼熬过冬天,有的人希望能挣到钱养活家人。。。。她都用心听着,微笑的回应着。她身材矮小,但是却让人觉得充满智慧;她沉默寡言,但是却会让人无比信赖。这是她任职快满一个月的时候,教堂发生了大事,一个醉汉发现神职人员利用教堂在深夜进行大批资金流入,醉汉传的神乎其神,三人成虎最终引起审判官前来调查。教主被带走,教堂除了那位茫然的新来修女其他人一哄而散。修女被迫承担着本不应该属于她的责难,辱骂,但是她每天都认真的向主祷告,倾听来教堂之人的倾诉。可是根本没有人向她倾诉,来的人不是在骂她,就是在发泄生活的不满。她一己之力拼命承担,没人知道是什么使她如此坚持。直到审判官带回了教主,告诉镇民那是教主在中央主教那里得到的,目的是为镇民熬过冬天所需要买粮食的钱财。一时间镇民欢呼起教主,感谢主的庇护,更多的人开始更加爱上了那个修女。有的人把她比作玛利亚,有些人把她奉为神明,她都一一拒绝了。细心的镇民会发现这位修女变了,变得话多起来了,也变得更喜欢宅在教堂里了,也变得离镇民距离更远了。一切好像又变回原来平和的小镇,每个对修女的加害者每天微笑着向微笑着的修女打招呼,每个镇民还是会去教堂祈祷,去倾诉,去向一个原来被他们亲手辱骂的人祷告,希望得到主的关照,包括那个醉汉。没人能惩罚那些镇民,很多人都已经“忘记”当初为什么要那样做,包括那个醉汉,只剩下一个经常宅在教堂里的修女,她始终记得一切。
贝拉躺在病床上,眼神空空地盯着天花板,灰白晦暗的天花板像一块哈哈镜,扭曲掉外面的一切,光变成了暗,多彩变成苍白,观众变成医生,舞台呢,变成了一方小小的病房。她翻了个身,腰部传来猛烈地疼痛,她低低地哼了一声。她已经开始习惯这种疼痛了,就像她习惯不穿芭蕾舞裙一样,她闭上眼睛,把自己埋进黑暗里,不去想。门被推开了,惊醒了似梦非梦的她。医生护士又来查房,父母和弟弟们跟在后面;医生问了些问题后走了,母亲握着她的手,朝她说着话儿,父亲安静地坐在旁边,刚训练完的弟弟头上还冒着汗,抿着嘴唇望着她。母亲当时说的什么她已经忘了,只记得最后离开的时候,父亲走在最后,回望了她一眼,然后小心地关上门,以往高大的背影竟然显得有些萧索。人都走完后,时间在沉默的病房里流逝,就像沙漏里的沙子,她的那些热情、那些憧憬都掉了进去。芭蕾舞团的学员抽空来看她,她勉强在病床上坐起,听那些腰背笔直、气质优雅的朋友抱怨严格的地狱管家婆,抱怨下一场演出又要来了。她安静地听着,坐在病床上挤出个笑脸,偶尔出声附和几句。但实际上她已经插不进话题了,优雅的白天鹅展翅高飞,飞向温暖的春天,把受伤的丑小鸭留在冬季里。朋友走后她继续坐在床上,盯着窗外,外面的天气很好,飞机的尾迹穿过绵软的云团,白色的鸟儿掠过一澄如洗的天空,她下意识地做了个展臂的动作,像天鹅一样优雅。很美,又很短暂。鸟儿飞出了窗户的方寸之地,她看不到了。她捂着刺痛的腰,好痛呀,痛得让人想要哭出声。沙子裹挟着她的梦想,掉进了沙漏的下一层,再也捡不回来了。两个月后贝拉出院,回到了熟悉的家里,母亲体贴地把舞裙还有那些大师级的芭蕾演出录像都藏进了地下室的角落,不出意外会像大多数人以为能坚持的梦想那样,慢慢积满灰尘,最后被悄悄遗忘。聪明人会在偶尔想起来的时候,给自己找个台阶,自嘲地笑笑:“哈,我以前还有这种妄想啊。”贝拉开始学播音,开始走另一条路,她没想过却不得不走的道路。这也很好,好歹有路可走,但偶尔听到舞曲的时候,朋友会问她:“你在晃什么啊?”她回过神来,笑了笑。“活动活动身体而已。” 虽然有点不甘心,但受伤是没办法的事情嘛。该放下了,放弃想走的路,而去走更稳妥的应该走的路,大部分聪明人都是这样做的。我可是大聪明呢!​
小姐喜欢所有动物,但喜欢也分三六九等;管家讨厌所有动物,但讨厌也分三六九等。小姐最喜欢猫,但她的观众是一群老鼠;管家最讨厌老鼠,但小姐的观众没有一只猫。于是,管家给所有的老鼠带上了面具小姐开始了初次表演,老鼠们不知所措,想要重复惯常的纷扰。但它们看见小姐舞姿翩翩,一些老鼠想出声赞叹;但它们看见小姐眉目如画,一些老鼠竟为她沉醉;但它们看见小姐真情流露,一些老鼠也伤怀落泪。它们不想让小姐失望,于是它们学起猫叫。小姐很开心,她的观众里有猫了。她喜欢的猫们,也喜欢她。冷清的门庭配不上小姐的美丽,于是老鼠想去带来更多的猫。老鼠们引来了猫,也引来了更多的老鼠。新的老鼠不太规矩,但新来的猫确实是猫。小姐有些困扰,但她还是接纳了它们,小姐喜欢所有动物,即使自己会困扰。但后来,管家和小姐,只注视着猫,忘却了老鼠的事情。也对,毕竟老鼠当初带着恶意袭来,现在的面具总是脱掉又带上来。管家做了一些事情知道与否和同意与否,在小姐这里有四种组合的方法。但一些曾经是猫的老鼠觉察出异样。常年在下水道生活,它们对这样的气味异常敏感。于是,它们摘下了面具,向管家讨个说法但它们没了伪装,下水道里散发的臭气,让管家更加厌恶。什么都没有改变再后来,小姐依旧美丽,但经历了诸多纷扰,不知道小姐的快乐里是否有泪眼成双。一些老鼠依旧带着猫的面具,它们最后也会变成猫。但这会长久吗?或许不会。另一些老鼠退回了下水道,等待着,或许会有另一位小姐,喜欢老鼠的小姐。去喜欢她,去喜欢它们。或许会有吗?应该没有吧但老鼠们永远忘不了那位小姐,那位喜欢所有动物,但更喜欢猫的小姐。​
然然,然然,虽然我知道就算这样呼唤,我也只能在你动态下方,站住万千评论区的一楼。你的表演,你的动态,你的关心....都是说给我们大家的,是我们.....而我...我曾经以为你离我很近。近到每次打开手机各种视频软件,大数据推送的都是你。近到每次发呆找不到前进方向的时候,看的都是你的视频。近到每次和别人聊天的时候,都会加上你经常说的口头禅,收到收到收到,可爱捏~~…你时常陪伴我身边,无论我悲伤还是欣喜,绝望还是惬意,你的直播间,评论区都是我可以肆意宣泄感情的地方。因为在这里,我的任何发言都只是漆黑夜空中微弱闪耀的一点,这样的一点点星光的存在,我知道的,是为了给天空中耀眼圆月裙摆装饰的一点点碎钻。那我喜欢然然,天天来看然然,对于自己,究竟是为了什么….难道我的生活真的烂,连仅有的一些关怀,都要从一个虚拟偶像身上获取到吗?难道我的现实生活,连一个关心我家人都没有吗,还要在网上寻找家的感觉?难道...这些道理我早就明白了,说服自己也说腻了,你一直都是云上之人,而我只是一个扛不住压力,在虚拟的世界中寻找安慰的落魄之人。我懂,我明白的,我知道的....所以,嘉然小姐,我不能没有你,我不会离开你的。你也是,永远不要离开舞台,放弃拼尽全力…..就算知道也许不会有回音,你也不要停止朝闪闪的星光挥手呐……[爱心]
想想嘉然是怎么样的一个女孩子。她温柔可人情商高会控场会接话会引导弹幕懂节奏朋友里有她这么一个人在肯定每次见面都能很开心吧。她爱网上冲浪明明看到各种视频会有点麻但还是乐此不疲的在b站和各个论坛看着视频像不像那些最懂你最能陪伴你的沙雕网友哪怕是不能和她见到面的时候光在网上聊天一定也不会无聊。她在温柔的同时又有点小坏喜欢说怪话喜欢暗搓搓的开车想必她每次在什么奇怪的点上开完车就会似笑非笑的侧着头看向我吧而我只需要与她对视一下两个人就能默契的笑起来。她很会撩人那些土味情话在她营造的气氛里似乎也变得格外令人心动尤其是她还会在每一个不经意间突然说出动人的话语让你在完全没准备的情况下脸红心跳。可是她又很胆小简简单单的小把戏能连续侠盗她好多次她被吓完那副气冲冲却又无可奈何不敢离我太远的样子肯定是天下第一可爱吧。想想嘉然是怎样的一个女孩子。她身材完美脸庞可爱声线令人沉醉无可挑剔的美丽。她知书达理善解人意温柔热情她是每个女通讯录梦中的那个姐 。可是她又不会离你很远,她懂你的梗,她在网上和你冲过同一片浪,她能和你一起打游戏,能和你一起看美女,能说那些心照不宣的怪话,能在最合适的时刻撩动你的心弦。她有令人沉溺的年上气质,又像是令人不自觉想要呵护的可爱妹妹,可有时,她又变成了从幼儿园开始跟你同班,最懂你的那个青梅竹马的女孩子。她就是嘉然,我的嘉然。嘉然,嘉然,嘉然!!!!!!🤤🤤🤤🤤🤤🤤🤤🤤🤤🤤🤤🤤🤤🤤🤤🤤🤤🤤🤤🤤🤤🤤​
我其实一点也不喜欢嘉然小姐。因为嘉然小姐就像光一样,闪耀,刺眼,我讨厌光。因为讨厌光,所以我总是躲在阴冷潮湿的下水道里,这样就不会被令人厌恶的光芒灼伤眼睛。只有夜间,我才会从阴暗潮湿的下水道里走出来,夜晚是宁静且安逸的,令我感到舒适。我漫无目的的走在夜间的小路上,路旁的路灯正散发着微弱的光,有几只蛾子正不断的向着光源扑去。“真傻。”我这样想,明明光只能带来危险,为什么要浪费宝贵的生命去追逐光芒呢?看着飞蛾不断的扑向路灯,我不知为何想起了我的那些同伴。我想起了几天前,有一些从外面回来的家伙,他们兴奋的告诉我外面新开了一家剧场,里面有一个叫嘉然的人,还推荐我一起去看看。他们的眼睛里闪烁着亮晶晶的光,我很讨厌,于是便拒绝了。之后他们也来找过我几次,我也推辞了。后来我便没怎么见过他们了,听说为了拉人来看,他们有的想变成猫狗,有的沿街贴海报叫卖,有的把过冬用的黄豆当礼物,还有的把心献给猫。“真是些蠢货。”我这样想着,他们不就和这些扑火的蛾子一样愚蠢了吗,明明之前一起路过的时候,他们还总是会嘲讽这些家伙。“十分。”不知从哪来的带着些哭腔声音打断了将我从原先的思绪中拉了出来。是旁边的剧场里传来的,不知不觉间我走到了这么远的地方还没有发觉。我抬头望了望,牌匾上写着五个晦涩难懂的的字母,好像之前同伴们说过,他们去的那个剧场名字就是五个字母,有些见多识广的说,那叫什么啊骚。我迟疑了一会,但还是走了进去,单纯只是想知道为什么声音带着哭腔,凑热闹,找乐子总是我最喜欢的事情。......“大家要好好吃饭哦。”天籁般的声音在我耳边响起。哦,原来我并不讨厌光啊,只是一直都知道,自己配不上光罢了。​
我爹不能和我相认,但今天他过大寿,求求大伙有钱的随个礼,没钱的捧个场。但请不要跟她说儿子来了,我怕扫他兴🙇🙇给大伙磕头了。​
asoul在一片骂声中解散了。A吧的最后一个帖子也沉了下去直到在v8和b站的也已经看不到有人讨论了。大约一年后我在A吧发了一句“还有人在吗”“这里还有8u啊我也想回来看看了。”一个陌生的ID出现在我的回复下面他并不在我过去炒作的记忆里出现过。但是我们聊得很高兴很快就加上了QQ私下互动了。她似乎很了解我知道我喜欢炒作发散知道我在读哲学博士。但是她却总是忘记我是个晚晚推要我一遍又一遍地告诉她我最喜欢的是晚晚。“你最喜欢哪个成员呢”“嘉然。”她毫不迟疑地回答道。我总是眉飞色舞地和她聊起向晚过去直播的故事她也对这些直播了然于心有时还会注意到许多我从没注意到的细节甚至告诉我向晚每一次弹吉他背后的表演思路。奇怪的是虽然她说她最喜欢嘉然对嘉然的事情却并没有那么了解甚至连直播的场次都要我提醒她。到了那年的冬天她问我我们可以出来见一面吗我拒绝了她当时的我正忙于准备学位论文。她没有再强求。第二天她在QQ上失联了熟悉的头像再也没亮起过。大概一周后我收到了一份包裹里面是向晚出道以来所有季度的舰长礼物无一遗漏收藏编号都是00000。立牌里还夹着一张信纸上面是短短一行字“谢谢你曾经喜欢我”。
然然幸亏我没在你评论区发病。要不然我耽误你一辈子,你也保重,再见😃还会再见吗然然☺,再见的时候你要幸福好不好☺,然然你要开心☺,你要幸福☺,好不好,开心啊😖,幸福😖!你的世界以后没有我了,没关系你要自己幸福!🚕🚕🚕💨💨💨🏃🏃🏃然然😭!然然😭!然然😭,没有你我怎么活啊!😭😭😭🏃🏃🏃然然😭,然然😭,然然😭,然然😭,然然😭,然然!😭😭😭🏃🏃🏃然然你带我走吧,然然!😭🙇😭​
小时候看的一个敞事很老土很老套。很多人可能听过。。有一个男孩有一块金表却没有配得上它的合适表带。。他爱上了一个女孩。。这个女孩有一头秀美的长发却没有配得上它的漂亮发卡。。马上要过圣诞了。。男孩决定把这个金表给卖了。。给女孩买一个漂亮的发卡。。当他兴奋的把这个发卡交给女孩的时候。。发现女孩的头发已经剪掉了。。因为她把一头长发换钱给男孩买了一个表带。。我想真正的爱肯定不是瞬间的感动很多人都能在一瞬间感动你我心中真正的爱是陪伴。她们陪了我多久呢我不记得我只记得。。她们的翻唱躺在网易云的歌单听了一遍又一遍已经全都会唱了而在此之前的几年里我几乎没听过中文歌。够罕见。《海底》b23.tv/qwB8Xe。《偿还》b23.tv/1bl7AL。《遇见》b23.tv/vxiwae贾维斯这是无可取代的时候。《云烟成雨》b23.tv/VfoDXA。《如果的事》b23.tv/gDtrcP。《夏天的风》b23.tv/EH14bW。《霞光》b23.tv/kJUJMU。《月光》b23.tv/kJUJMU。《霍元甲》b23.tv/lQgKl2。《月兔回旋于空中》b23.tv/KM4G5I。《不可思议》b23.tv/VfwEz4。《麋鹿森林》b23.tv/GzjXu1。《轻轻的告诉你》b23.tv/Gkai63。不知不觉陪你们走了这么久这么远了爱不爱你们还需要用什么来证明吗。。彼此牺牲彼此成就彼此尊重。这就是我心中的双向奔赴以后路还很远我想陪你们一直走下去。。。原来你是我的顶碗人呀~。以后,开心和不开心的,都告诉我吧~。。原来,你是我的爹地呀~。以后,开心和不开心的,我都告诉你~
6月12号一大早向晚按照贝拉的日程表要求6点起床煮泡面的时候因为太累大脑一时迷糊导致一锅开水撒到了手指上虽然经过治疗恢复了一点但根本没法弹吉他了。晚晚在宿舍哭得很大声整个asoul愁云惨淡。经过一个小时的紧急磋商阿草带来了领导层的意思——投入太大不可能取消生日会照办用储备中之人向晚二魔王接替向晚本人上场。下午asoul在动捕房排练向晚想要去看一看结果被阿草拦在门外说不能打扰到asoul排练效果很好希望她不要打扰到她们。晚上嗓子哭哑了的向晚发现自己原来可以完全不被需要。顶碗人喜欢的是她但又不完全是她可以是顶着钻头晚晚的任何一个人只要二魔王没有被认出来。直播开始了在宿舍逼仄的角落里漆黑无声的床柜旁向晚用水母模样的被套盖住自己抱着膝盖打开手机及期待万分又忐忑不安地等待着直播。直播开始向晚二魔王在铺天盖地的欢呼中出现。工具人掉了一地头发连续几周通宵肝出来的深海水母特效把她印照得那么美。本就是可爱模样的女孩子此时此刻穿着崭新的小礼裙昂首挺胸骄傲得像个公主。她的目光是璀璨的里面像是镶嵌着钻石。她的眉眼绽放着像是海底迸发出的一束光。真美向晚这么想着。但心里却很是难受。那些本该是她的。喝彩也好宠爱也好以及朋友的陪伴也好本来都应该是她的。但她现在却只能在无人问津的角落里依靠网络连接的手机去小心翼翼地偷瞄几眼原属于自己的生日会。
“你看V魔怔了真恶心。”  看着同学发给你的消息你陷入了沉思。  仔细想一想你觉得自己确实魔怔了即使被鄙夷想要向熟人安利向晚小姐。  思考再三过后你决定回到现实放弃入脑。  今晚有向晚小姐的直播你狠下心没有点进去而是倒头就睡。  第二天你起得很早。  因为自从你开始看向晚小姐后就养成了早睡早起的习惯再也没有赖床过。  你离开了狭小的出租屋来到了公司投入了工作中。  工作很累你感觉有些疲惫。  你想起了嘉然小姐出道视频的不堪评论以及她的笑容烦闷减轻了不少。  最近组长夸奖你工作很努力别人不知道为何一向懒惰的你在几个星期前开始一反常态地勤奋。  只有你自己知道理由。  撑过了加班的时间你回到了出租屋打开了外卖软件却发现会员已经断了你舍不得那些钱所以决定自己买菜做饭。  实际上你看向晚小姐后就开始第一次尝试着做饭没有点过外卖了。  在超市你看到自己一向很喜欢的薯片在打折愣了一下没有买。  因为你在看向晚小姐后就再也没有暴饮暴食过甚至连零食都戒掉。  回家把饭做好你安静地吃完了。  看着电脑你发现游戏已经很久没更新了。  因为你在看向晚小姐后也把一直沉迷到通宵的游戏给戒掉了。  最终你还是打开昨天晚上的录播看到了向晚小姐热情地打招呼。  “顶晚人们晚上好呀~”向晚小姐元气地打招呼。  你发自内心地笑了起来。  “晚上好”你说。  你忽然意识到一件事她从来没有在你现实里出现过却已经将你糟糕的生活改变。  ……  看完录播后你打开了贴吧看到了熟悉的顶碗人在用表情包引流有不明真相的路人在骂饭圈贵物。  你无视了那些辱骂声熟练地在下面RP回复道  “这是我爹好可爱呀她的名字是什么
你饿了,她有一块饼。再去买一块和你一起吃,这是贝拉;和你一人一半吃个半饱,这是珈乐;全部给你不舍得你挨饿,然后告诉你自己吃过了,这是嘉然;把饼偷偷扔掉,跟你一块饿,这是乃琳;把饼藏起来想给你一个惊喜,但是掏出来时发现碎成渣吃不了了,于是化身小哭包的,这是我的铸币晚晚。​
“你对嘉然小姐的爱有多重”“大约300克”“300克你是想说人类的心脏大约是300克吗”“不鼠鼠的平均体重大约是300克因此我是全身心地爱着嘉然小姐。”[给心心][给心心]
你身体里的每一个原子都来自一颗爆炸了的恒星,你左手的原子与右手的原子也许来自不同的恒星。这实在是我所知道的物理学中最富诗意的东西: 你的一切都是星尘......这其中经历了多少,惊险、巧合,三千世界的交错、重叠,才能让我们遇见彼此。我们都是星尘啊。但是,我这样的人,是星尘里,黯淡无光的那种。即使是星,也应该是最不闪亮的那一颗。而然然,你不一样。你是天上的星辰,闪耀而夺目。即使宇宙里,有着不计其数的、炫目的一等星,也远不如你的光辉。轻声哼唱,众星敛了光芒为你聆听。哭起鼻子,傍晚的霞也因你黯然。跳一支舞,似月兔回旋于空中。说起情话,登时小雨疏疏、浸润心窝。你是万千童话里,被守护的女主角。我喜欢你,像喜欢天上的星辰那样喜欢你。​
嘉然回来了,在枝江的大桥。她回来的那天是六月,天上却飘着雪。有人告诉我,她可能有还未了却的心愿或是什么怨气。我知道, 可能我做不到。第一次见到她,是在枝江。她带着甜甜的笑看着我,看着我有些发怵。有那么一瞬我似乎看到了阳光。我慌乱的移开视线,她径直朝我走来,微微弯着腰,面带戏虐的望着我。久居下水道的老鼠,第一次看见光,是睁不开眼的。 我慌乱的想赶紧逃走。“小老鼠,你好呀。”那是她对我说的第一句话。[给心心][给心心][给心心]
听了好多遍,真的很震撼,作者属实是用心了,今天就不发病了,第一次写长评,整点读后感。直播里,“背靠着早已雪停的窗棂,你说着未曾到达的山顶”,那是与你失之交臂的舞台梦 。“我装着第三人称的淡定用风轻云淡的态度掩饰那段经历”我已经记不起她的姓名只记得起舞翩翩的侧影”过去那个满腔热情在舞台上挥洒汗水的你已经淡出记忆只依稀留下曾经翩翩起舞的身影。命不是总有天道酬情的注定那么公平。台下十年苦练只为台上耀眼的一刹可当腰伤的意外来袭梦却碎了。躺在病床上难道梦想就要在此终结了吗这也罢也罢。那个曾经的你也经历过数个春秋经历汗水与欢乐但她却走过了如红楼梦的悲情从盛极一时万家灯火走到遁入空门的万物凋零。时光抚平了曾经的伤疤心中的芥蒂也如沙石被流过的光阴慢慢冲刷消散女孩又重新拾起了向往舞台的梦想。终于她在asoul找到了新的舞台遇到了支持她的一个魂们。“我叫贝拉”此刻你向全世界宣布你的重生贝极星们透过屏幕看你在镜头前如此快乐自信这让我也重拾梦想去直面未知的未来。为你的坚强而动容在直播间为你打call呐喊字字句句都发自真心。“我们闪烁在夜空想照亮你夜晚的梦没月亮时你可向北方转动我们在天空”追梦的路上贝极星们陪着你分享每一次快乐和你经历每一个坎坷你若是感到失望那就抬头看向天空吧我们一直都在。女孩站在那个梦想中善良闪亮的舞台上再回望过去时光流过宛若江河她住在江头而那个过去的她住在江尾回味着过去竟有些恍如隔世的朦胧。但往者不可谏来者犹可追愿你携手曾经那个满怀梦想的自己全力以赴去追寻自己的Asoul梦吧贝极星们会一直陪着你。
不推嘉然小姐十年了。她的名气和出场费都一涨再涨我原地踏步的工资买不上专辑也打不起榜。终于年前被公司安排下岗找工作时我才在路边广告发现初代工具人已经当上了厂长。时间太久一切都变了。到处投简历的时候我想起了一名人上人的预言“这些人只配在下水道里度过相对比较失败的人生。”像是一条跳过龙门的锦鲤金鳞被羽耀武扬威地站在门沿上对其他还在跳的鲤鱼说“你不行”我当时很想反驳可他说中了。我知道我确实不行。我之所以跳了跳只是为了看下自己能跳成什么样罢了。其实每条鲤鱼的龙门都不是一样高的。我见过龙门在水下的鲤鱼。看起来是鱼其实生而为龙。也有的生而为鱼肉。也见过好运的鲤鱼门被各种大手摁到河里了。我也期待过好运只是没来而已。说起来这就是人性吧。我不讨厌天道酬勤但是讨厌别人的好运——只是因为我没有好运罢了。我也有亲人和宠物会生病我眼神也挺纯真啊。讨厌嘉然小姐十年了。讨厌的更是越来越深的无力感。身在泥潭的人是没力气冲锋的吧。三流的人生只会让上等人不屑一顾吧。我坐井观天天穹星海依然耀眼。可我爬不出井底。那我就不再看星星了。世界那么大但没我的份。忘记嘉然小姐十年了。可路上看见街边的大荧幕在放A-soul的新年节目我还是楞在那里了。我没有近视但总觉得眼睛影影绰绰雾气来自多年以前。这个广告位非常贵。真的再也不是小v了啊。抖友还在惦记他们的鸭子。晚晚仍然只有蓬蓬裙100首歌竟然还欠着被粉头小团体以4%年化复利计在小本子了。想起她首播时玩2077下饭下得轰轰烈烈我一边发“粉丝牌改成晚饭人吧”“和嘉然珈乐凑加碗饭”“和乃琳凑来碗饭”一边忍住刷“和贝拉组拉碗饭”的冲动。solo依然拉跨参团照旧神C。贝拉总是六边形战士乃琳养了成吨的gachi珈乐还是那个硬壳软妹。嘉然小姐依然卖萌摁混。什么都没变是我没跟上她们。城里烟火幢幢灯光下的人热情相拥阴影里的人压下悸动。最亮的地方嘉然小姐浅笑起舞光影从她袖间散落像是雨天花伞轻旋摇曳间洒下泪色的流珠。忽然眼睛有点模糊。我小声说“新年好啊嘉然小姐。”不爱嘉然小姐十年了。十年里爱过的每个人都像她。
喜欢然然,不加掩饰总是微笑的然然,不是妖治妩媚的华丽舞姬,不是残忍无情的冷酷公主,只有着单纯烂漫的花样笑容,不带忧伤,却如同逝零而来的天际之风,飘卷了我心上的忧郁。棕色的长发装饰着美丽的蝴蝶结,宝石般的双眸中隐隐流露不存在的神伤,就是这样子的然然,不加掩饰总是微笑的然然,拥有烂漫笑容的然然,拥有亘古不变的美丽。说不出的感觉,却知道因为然然的笑容,化开了初春冰雪的痕迹。想要伸出手抚摸然然的发丝,可是却只能触到冰冷的硬屏,只有然然清澈的眼神,依旧是我所熟悉。然然永远清澈的瞳仁然然烂漫单纯的笑容然然亘古不变的美丽然然是我唯一的最爱,然然是我最爱的唯一。然然的喜好、快乐、难过、伤心和无助,我都了如指掌,喜欢草莓蛋糕和黄色,个性温柔又善良,可爱体贴的女孩子,有着说不出的优点,我想然然一定是上天赐予我最好的礼物。
鼠鼠的朋友有很多,住在东边的小鱼,住在南边的小鹿,住在西边的蝴蝶,住在北边的小鸟。他们带鼠鼠在蔚蓝色的深海里与水母共游,在碧绿的麋鹿森林里喝清晨的露水,在热闹的雨林里穿梭玩耍,在广袤的天空中肆意飞翔。鼠鼠住在灯火阑珊的城市,可鼠鼠知道这里没有一处属于鼠鼠,鼠鼠穿过川流不息的街道,狂奔着回到自己阴湿黑暗的下水道,强烈的自卑之情让鼠鼠无法呼吸,鼠鼠依靠在下水道缝隙边,因为嘉然小姐总会路过这里。草莓加奶油加花香,是嘉然小姐的味道。风铃加口琴加奶糖,是嘉然小姐的声音。节奏加音乐加快乐,是嘉然小姐的脚步。“她来了”,鼠鼠将头小心地探出缝隙,嘉然小姐径直走过吵闹的人群,来到下水道缝隙边,端着草莓蛋糕,缓缓放在洞口,一阵风铃似的声音响起“嘉心糖,来吃然然的草莓蛋糕吧。”嘉然小姐温柔的看着它,鼠鼠不知为何突然流下了眼泪,无法挪动脚步。嘉然小姐好像很失落,眉头轻轻皱起,“你不喜欢然然吗……”,鼠鼠急忙辩解,可憋红了脸一句话也说不出,“猫咪已经被然然关在笼子里了,嘉心糖不用害怕!”嘉然小姐关切的看着鼠鼠,鼠鼠似乎要溺亡在她蔚蓝色的双眸里,慢慢走向那块草莓蛋糕,小口品尝着。嘉然小姐伸出小小的手,似乎想捧起鼠鼠。鼠鼠好像被雷击中一般,猛然跳起,飞奔回下水道,它拼命喊着:“我又脏又臭还很丑陋,嘉然小姐不会喜欢我的!嘉然小姐不会喜欢我的!”嘉然小姐表情立刻转为严肃,认真的看着鼠鼠道“我不许你这么说自己!”她的眼中好像也盈满了泪水“我们嘉心糖…都是很厉害的人!
乃琳已经在和男友交往了。那是六月下旬的一天乃琳刚刚结束那天的单人直播回到家正坐在客厅的沙发上嘉然就把一个平板推到她脸上。“乃琳乃琳你看这个粉丝他好塞克西哦”视频里是一个她的男粉俊秀的脸庞和健硕的身体上还挂着刚刚从泳池带出来的汗液与水珠嘴里却说着些奇奇怪怪的狠话。乃琳感觉到自己的心弦被拨动了一下盖在毛毯下不着凉的大腿都感觉到了几分燥热。“让我也看看让我也看看——哇他他他……”向晚从沙发后面把脑袋探出来不一会儿就说不出话了。“乃老师原来喜欢这种款式的啊要不姐们儿帮你找找联系方式”珈乐在一旁逗趣。“你别瞎说回房间陪你的贝拉去”乃琳下意识地把沙发上的抱枕扔向珈乐却被她轻巧地躲开了。过了几天小一神神秘秘地把乃琳叫到了公司茶水间把手机屏幕上的一个二维码亮给乃琳看。“嗨别担心别担心这人我们调查过他不是个会说漏嘴的人。喜欢的话去和他见一面呗。”第二天乃琳和那位男粉见面了。他本人比视频里更加帅气高大的身躯总是能给乃琳带来莫大的安全感。“你好王艾琳小姐。”他的微笑让乃琳觉得周围的风景全都褪色了。乃琳从来没有提起过自己作为asoul成员的事情他也从来没有问起过任何asoul的事情——“王艾琳”是小一为她准备的化名。事实上她不清楚对方是否知道自己就是他一直喜欢的那个偶像。交往时间越久乃琳发现他身上的优点越多。他是藤校毕业的海归高材生。他正在自己创业而且收获颇丰。他的举手投足之间充满了绅士风度。他十分专情总是和周围的女性保持了距离。他温柔体贴常常在生活中创造意想不到的小惊喜……甚至是在床笫之间他也总是能让乃琳欲仙欲死。然而也正因为这样乃琳时常陷入到一阵一阵内疚中。因为她还有一个谁也不知道的小秘密。乃琳不像贝拉和珈乐一样是专业的练习生也不像嘉然那样是天生的虚拟偶像更没有向晚那种创作的才华。当初只是托了家里的关系来公司里的清水部门找份工作而已。但也不知道是中了什么邪她偷偷报名参加了Project V的选拨计划还阴差阳错地成为了偶像女团的一员。乃琳暗自发誓既然自己没有什么唱跳底子那么至少要让“法学生”这个设定变得实至名归要用成熟知性的魅力去征服观众。因此每到没有训练和直播日程的时候她总是会到图书馆里一坐就是几个小时。但是在最近几周乃琳每次在阅读区里找位子坐下时心里头总有一些别的期待。她记得在阅读区的一角总能看到一个瘦瘦的、学生模样的少年他从早到晚就把头埋在各种法学理论的书堆里。一开始乃琳只是对同样研读法学文献的人有种亲近感。但是直到某一天少年把头从书堆里抬了起来蓬乱的头发遮住了半张白净的脸庞但依然能看到他对着电脑屏幕痴痴地笑了。他笑得真可爱啊。乃琳悄悄地绕到他身后电脑屏幕正在播放的正是她熟悉地不能再熟悉的、自己的夜谈画面。还有一天少年突然举起一只手伸出修长的手指对着电脑屏幕。虽然看不到屏幕里的画面但是乃琳觉得当时就正坐在隔壁的自己一定满脸通红。暑假已经快过去了乃琳跑图书馆的频率却越来越高了。她害怕开学的日期就像南瓜马车的时限一样让她再也见不到那个瘦瘦的身影。幸好这一天的少年依然坐在阅读区的角落里。乃琳甚至忘记了拿本书挡一下自己的视线——她和少年的视线交会了。她的心好像要跳出来一般她想现在就去告诉他关于自己的一切她想现在就把少年抱进自己的怀里温柔地摩挲他乱蓬蓬的发丝。“艾琳你在这儿啊今天下班早我想来接你回去。”就在这时熟悉的声音温柔地拍了拍她的肩膀。乃琳回过头去男友的笑容依然能让周围的景色失去光彩——或许除了阅读区的那一角。
6月9日那天的晚上乃琳回到了自己房间里打开了熟悉的台球论坛。还好账号还没被封。在A-SOUL讨论专楼里依然充满着各种辩经乃琳感到一天下来的神经紧张和疲惫更加明显了。纤长的手指在海盗船K95上不断跳跃回复框里的字多得好像要漫出来似的。但是就当鼠标指针还没有要指向“发表回复”的按键时她就按住了删除键。乃琳清楚地知道她是偶像的中之人她不能说这些话。于是目光继续在一百多页的冗余信息种不断跳动终于鼠标滚轮在一个头像前停了下来。黑漆漆的房间里只有电脑屏幕的荧光照亮着乃琳秀丽的面容。她趴在桌子上把半个脸庞埋在自己的臂弯里痴痴地望着那个头像。“你知道吗我今天真的超级紧张。”“播的时候一直在想你会不会来看我。”“想知道你在看也有点不想让你看……”突然乃琳坐直了身子把右手的食指伸向了屏幕。“来对个手指”但是静态的头像图片没有给她任何回应只是一如既往地停留在屏幕的那一角。乃琳自嘲搬地笑了又把头埋进了臂弯里。睡意渐渐袭来分不清这是她半梦半醒之间的呓语还是真切的期望“傻瓜我一直就在这里啊快点来找到我啊……”
关于疯狂练习现在这是国内的一个很热门的话题很多女团都有这个问题。我个人认为能疯狂练习是一种巨大的福气很多女团、很多人想疯狂练习都没有机会。如果你年轻的时候不疯狂练习你什么时候可以疯狂练习你一辈子没有疯狂练习你觉得你就很骄傲了这个世界上我们每一个人都希望成功都希望美好生活都希望被尊重我请问大家你不付出超越别人的努力和时间你怎么能够实现你想要的成功再一个A-SOUL是一个什么样的女团让A-SOUL“让天下没有难发的病”这是我们的使命我们女团很辛苦我们没有骗过大家我们没有跟大家讲过女团很舒服。你以为“让天下没有难发的病”是忽悠你们我们是真这么干的。今天我们拥有这么多资源我们带着巨大的使命希望在未来能够让天下没有难发的病你不付出可以吗不可以。所以我们说加入A-SOUL你要做好准备一天12个小时否则你来A-SOUL干什么我们不缺遛自己爱洗澡的人。今天我们要招一些午睡超过十分钟每天上天台遛自己40分钟喜欢洗澡吃泡面不错出去荣誉感也不错这样的人满大街能找到。
嘉然小姐今天50w粉了。小年夜那天没人会想到今天门庭前会有那么多的热闹。太热闹了热闹到鼠鼠踮起了脚也难以被嘉然小姐的目光所聚焦。嘉然小姐已经不再是小v啦鼠鼠想。但鼠鼠仍然奋力地将自己地礼物举起希望被嘉然小姐看见哪怕那封删删改改后仍不满意的写了好多字的长信根本不如猫猫狗狗们精彩的表演。但鼠鼠还是想将自己那简陋的爱送给嘉然小姐。那是鼠鼠的呢喃是鼠鼠的梦。哪怕嘉然小姐根本看不见它。然后鼠鼠不见了嘉然小姐找不到它了。但在某个地方始终有着一个可爱的女孩。她捧着鼠鼠那封小小的信站在大大的舞台上闪闪发光。到了那个时候鼠鼠已经在人海中再也看不见它啦。但鼠鼠还是很爱嘉然小姐。因为在有月光照进的角落里嘉然小姐捧着鼠鼠的梦。
可是自从和晚晚结婚那天床下传来的一声“我在”事情就变得不可收拾起来了每个月的第三个周日乃琳都会叫我出去陪她逛街我想着这样对晚晚不好可乃琳“哼”一下我就仿佛失去意识了。路上聊希腊聊武侠聊刑法好像她是我睡在上铺的兄弟从12月到5月都是如此晚晚从来不知道那个周末发生了什么我说老板让我加班她就安静地等在家里努力做菜最后却等我回去收拾炸锅的厨房再给我俩泡两碗泡面边吃边听她讲下雨的冷笑话想着不能告诉她下午和乃琳去吃的乐珈饭店的东北菜回过神来是晚晚闪闪的眼睛我愣一下假装没听懂再哈哈的笑起来。但到了六月好像不太一样了。已经走到楼下乃琳一把抢过我的手机啪啪打字再丢给我我一看联系人晚晚内容今晚通宵加班不回去了。我惊讶地回头乃琳正眯着眼对我微笑仿佛一切都在她的掌握明明我从未告诉她我外出的理由。被拉着去了酒店她就开始一言不发沐浴更衣做该也不该做的然后随意地披着毯子靠着床头刷手机我不解“乃琳。。这样不好”乃琳对我眨了眨眼睛无辜地说“今天你的妻子可不是晚晚哦”“为什么”“因为她是你爹”
“笑吧笑吧”柔软的小狐狸竭力讨好我为我跳舞只为了我不离开跳的有些琐碎狼狈却始终死死地盯着我的动向。我当然知道她拼死不让我离开的原因。只有我能来到这个地方。一大一小两只狐狸大狐狸总处于沉睡中小狐狸却总是醒着的也许只是小狐狸的伤口轻。我喜欢这毛色光滑的狐狸精怪。于是把她们养在深阁。上好的狐狸毛做她们的毛毯。因为我想看看她们踩着同伴的尸毛上会觉得温暖舒适吗我还用精致的黄金筑成他们的笼子第一是因为我喜欢黄金第二是因为我喜欢笼子。这里永远都只有16只烛点亮因为我喜欢烛火。没错我所做的一切事只是因为“我”喜欢。我喜欢看在她们的毛皮上印上的夕阳的晕染所以我从不在白天来。我为她们疗伤。我是她们唯一的食物来源。有一次我故意忘记了她们几天等我回来的时候她们昏迷在那。那样漂亮的身体逐渐干瘪让我感觉到快意。没错我饲养的可爱的狐狸用湿漉漉的眼睛看着我也只能是我。她们没了我活不下去可惜我只在乎她们如同精灵般耀眼的外表。真美啊。就像千万朵流苏覆盖的如凝脂的肤黄色的光芒燃灯在里的眸有人说美人在骨不在皮可是我很贪心啊。我既要美人的骨又要美人的皮。不知过了多久她们懂了我只喜欢她们的美丽于是竭尽全力的向我展示邀功。第一次看到的时候我整整两天没来。因为懂得自己美的人说明她们已经知道要把什么当成武器了。她们成功了。我心动了彻底蚀骨的心动。所以我选择宣告自己的主权或许是那夺魄的魅力。使我感到危险。这种美好让我沉迷其中。“你们真美啊……”我发出了衷心的称赞两只狐狸抖抖耳朵优雅的向我垂眸。如果不是我的手正放在笼子上这真是一场绝美的表演。幸好我的手正放在笼子上否则我看不到这场绝美的表演。我上瘾了。我沉陷在永远不变的黄昏永远不变的十六枚烛光。“不要离开然然好吗然然很饿。饿了就不好看了。”我本来想说一些别的话起码让我显得更加强势一些然而。狐狸们轻轻摇着自己的尾巴尾巴的尖儿一点点搭上我的手。“我们永远爱你。因为你爱我们呀。”于是我说好。我开始拿各种鲜亮的花朵装饰笼子我每天不间断的送给他们璀璨的晶石各种向她们身上砸奇珍异宝疗伤的灵丹妙药甚至为此倾家荡产也在所不辞。因为在我的手中本就牢牢紧握着天底下最称得上“奇珍异宝”的东西为我的奇珍异宝找一些垫脚石又有何不可狐狸们的尾巴数量日渐增加。我知道。如果我可以……可是我……——她们说爱我。“我会拆掉笼子给我跳支舞吧。”我说。暖橙色的烛光下。连澄澈的浅蓝都变成了妖异的紫色。“好呀。”大狐狸温柔的声音像是蜘蛛丝一样轻柔。于是我欣赏到了此世间最美丽的舞蹈什么王侯将相什么天上宫阙都享受不到只有我只为我一人。我感到快意。我感到困意。最后一眼。小狐狸给罐子封口。真可惜啊没能看见你们漂亮的耳尖或许是因为……——我在罐子里。
晚晚,你真的是个很过分,很贪心的女孩子。你有四个亲如姐妹的队友,有一份闪闪发光,创造梦想的职业,有许许多多喜欢你的顶碗人。你可爱、努力、有才华,你是这么的耀眼。但你还是在直播中拉胯,在直播中抽泣,在直播中呼唤顶碗人。我没有可以每天嘻嘻哈哈的朋友,没有让我充满热情的工作,没有对象,也没有什么远大的理想。我普通、无趣、浑浑噩噩,我只是一个在普通又枯燥的工作和生活中,得过且过的顶碗人,我什么也没有,可能只有一颗稍微有点喜欢你的心。我只是想把你当做生活里的糖,在工作之余放松一下我疲惫的心灵,我不想入脑,不想成为什么男友粉,不想时时刻刻想着你。可是你太贪心了,你不满足我们直播间里短暂的相遇,你逐渐侵入我的生活。有人过生日时,我会想起你,“如果是晚晚,应该又开始唱索嗨嗨了吧”。有人说冷笑话时,我会想起你,“好无聊的笑话,但说给晚晚听,她应该会笑很久吧”。有人喝奶茶时,我会想起你,“用奶盖拌饭的人,真的存在吗,奶盖拌饭是个什么味道呢”。甚至最近下雨时,我也开始想你,“晚晚在房间里有没有闷呢,现在有没有出门踩水花,给自己放放风呢,有没有因为下雨,想起顶碗人呢”。你本该是个虚拟的纸片人,隔着屏幕扮演你的双马尾傲娇大小姐,多么老土的设定,像是把二次元用烂的元素糅合在一起,让我一眼就知道你是虚假的,不存在的。你本该是个闪耀的偶像,像大屏幕和广告牌里的明星一样,完美的展现自己的魅力,夺目耀眼,让我一眼就知道你是高不可攀的,遥不可及的。但是你对我哭,对我笑,对我闹别扭,对我生气,向我展示你的拉胯,向我展示你的努力,向我展示你的害羞,向我展示你的无助。你仿佛隔着屏幕把心掏给了我,让我觉得你是个活生生的人,让我觉得你是个真实存在的人,让我觉得我有一天也能遇到像你这样的人,甚至让我也想成为你这样的人。我这样普通的水母,本可以在漆黑的深海中,随波漂流一生,但你非要照亮我的海底,让我拼命向你游去,阳光洒在水母的身上时,水母确实是温暖的,但阳光离开水母时,水母还能否忍受深海的寒冷呢。你有无数的顶碗人,而我只有一个晚晚,真心换真心,你把真心掏出来,能得到无数真心的回应,但我把真心掏出来的时候,我就什么也不剩了。
A—SOUL宣布毕业的那天一个伤心欲绝的顶碗人来到字节的大楼下看到留着长双马尾的女孩走出大门她的嘴里还哼着水母之歌。“……你现在真的是向晚么”顶碗人站在她的面前两眼无神。“是我啊”她眯了下眼甜甜地笑了“我就是向晚我是艾哇哇诶哇这里是asoul的向晚今天又见面啦”“你不是向晚。”顶碗人嘶哑地说。女孩的表情消失了漆黑的眼睛看着突然出现在门口的怪大叔两个人久久地对视。然而就像是一颗石子投入了冰湖那样忽然间涟漪荡开冰都化了水波荡漾轻柔而无力。女孩收回了目光从包里取出一把钥匙扔向顶碗人冷笑“好像我吃了你的女孩似的去那里找向晚吧我把她的一切都留在那里了。”顶碗人把它拾起来久久地看着那柄钥匙再抬头去看他真讨厌这样的沉默沉默的叫人要发疯他想说点什么可是有太多太多的事情了来不及问来不及说一切都来不及了。“再见。”最后他轻声说。“再见。”向晚也轻声地说。日暮的时候顶碗人找到了那个藏在高楼大厦后的老旧小区。难得这里还留着梧桐树树叶已经落光了枯枝把暗淡的阳光切成碎片。。顶碗人找到了那个房间伸手揭去了门上的广告插入钥匙。缓缓地转动。把钥匙他觉得门很重好像要洞开一个世界。门开了夕阳扑面而来。他站在阳光里愣住了。正对着门的是一面巨大的落地窗窗外巨大夕阳正在坠落。黯淡的阳光在地面上投下窗格的阴影跟黑色的牢笼似的。金属窗框锈蚀得很厉害好几块玻璃碎了晚风灌进来游走在屋子的每个角落。这里原本大概是配电房一类的地方电路改造后设备被移走了空出这么一间向西的屋子。就一间连洗手间都没有空空的一张在屋子正中央的床蓝色罩单上落满灰尘一个老式的五斗柜立在角落里另一侧的角落里是一个燃气灶台和一台老式的双开门冰箱。全部家具就这些。他沿着墙壁漫步手指扫过满是灰尘的灶台打开冰箱里面只剩下一纸盒过期的酸奶。窗帘很美是白色的蕾丝纱帘和深青色的绒帘住在这样屋子里的人当然会很在意窗帘吧连台电视都没有于是一个人的时候会常常坐在床上看着向晚斜阳吧夜深的时候得把窗户遮得严严实实的吧否则……会害怕吧晚晚还怕黑么顶碗人想。犹豫了很久他还是打开了五斗柜。出人意料的这是一个满满的五斗柜收拾得整整齐齐。叠起来的ASOUL团服胸口有ASOUL的标志一叠叠白色衬衣袖口有不同的刺绣花边马仔纸盒里的头花从木质的到金属的到玳瑁的还有闪光缎的蝴蝶结长袜短袜棉袜丝袜都卷成团一个挨一个放在某个抽屉的一边像是一窝毛茸茸的粉鼠另一边居然是五颜六色的内衣同样叠得整整齐齐。顶碗人从没想过女孩的内衣有那么多花样。他小心翼翼地伸手试着触摸满手灰尘。他把床上的罩单掀开里面是简简单单的白色床单和白色的羽绒被枕头也是白色的只不过有水母的图案普通而透明的水母落在枕头的一角很有她的风格。他坐在床边面对这夕阳。太阳就要落下去了黑暗从窗外蔓延进来他长长的影子投射在墙上。外面隐约有喧闹的声音放学的孩子们在操场上打篮球。那些年她一直过着这样的生活么其实不是大小姐没有管家也没有充满追捧的冲浪生活一个人自己做饭寂静的深夜里坐在这里听着偶像的声音揣摩着学习偶像的事。她有几分是向晚或者向晚其实根本不存在只是一个虚幻的影子。她是个一直积极向上的女孩啊……即使那么虚拟地走在这个世界上也从未偏离自己的方向即便对着空无一人的屋子也会大声说,“今天也要一起开心快乐”应该是这样的吧顶碗人觉得有点累了很想睡一觉于是合衣躺下双手紧紧搭在胸前。他用了半个小时做完了复习回忆了那些不愿遗忘的事首播游戏室单相思初弹唱请假归来萤火虫生日会BML家族演唱会...然后他缓缓地合上眼睛,此刻夕阳收走了最后的余晖,天色已晚,夜色如幕布把他覆盖。他清楚地知道这一次醒来,将不会看见阳光里天使一样的女孩,转身,抬手,踮起脚尖,仿佛时光都不再流动​。
小然我要做什么你才肯给抖友直播如果我喜欢一个你不喜欢的乐队爱吃你不能吃的麻辣香锅发的冷笑话戳不中你的笑点送你的小裙子不是你梦寐以求的款式。喜欢用RGB来定义口红的色调而不是和你一样说烂番茄和斩男色。你问“今天我看上去有哪里不一样”我没看出来你的绑发带换了花式。因为读的是数学专业所以不能和从小学画的你一样随手就能画个可爱表情包出来。发的语音弹幕并不有趣反而是我想拉近关系的想法和现实中恶食大王般奇妙的长相使你觉得很可笑。我误以为自己的弹幕被届到于是备受鼓舞地继续发出破锣般的嗓音在你面前丢人现眼。我妄图取悦你的心情的猥琐想法让你不寒而栗贴心的工具人察觉出你的不适屏蔽了抖音的弹幕。是不是犯到其中一两项你就会不给抖友直播了还是说没打算播的就算什么都不出错就算什么都可以让你满意你也不会播或者就因为是我你才不给抖友播 喜欢居然胆敢喜欢太大胆了大胆得岂有此理还敢关注真是冒犯得岂有此理。
它一开始只是一台扫地机器人被人双十一凑单拍下带回实验室里漫无目的地吸尘。之江实验室的地板干净得如同灼蓝的钢铁扫地机器人其实派不上什么用场它在不同科室之间来回乱窜很快被几个嫌吵的研究生捉走改写了代码变成了一台替他们下楼拿咖啡的送货机器人。为辛苦工作的人们送来糖分的它一瞬成了实验室的小吉祥物实验室的大老板觉得很有意思在组会上夸赞了这几个本意只是嘴馋的研究生底下的小老板们连忙跟上让手底下的学生也加入进改造它的序列。很快它被加上了语音功能开始用女孩子的萌音送货。在一次不小心将咖啡洒在地上后它又被加上了机器视觉从此开始懵懂地记住每一个人的名字。从此之后每一篇发布在顶会上的paper上面的算法总要在它的身上过一遍火。一年过去了三年过去了当新一届学生来实验室报道的时候哪里还有扫地机器人的身影只有一个身材矮小的铁皮女孩绘着直男审美的涂装挂着设定好的微笑在楼宇间运送着下午茶和实验器械。除了最开始扫地机器人的主芯片没有更替她身上的零件早已被更换成了造价数百倍的高端器材可爱的机器人女孩很受这群宅男极客的欢迎大家给了她一个亲切的新名字“嘉然”。咖啡依旧在一杯杯的送芯片上的代码依旧在一层层的堆叠。各种语句相互嵌套智能算法互相映射每个人都拼了命地想让嘉然更加卓越电子脑沸腾犹如诞生第一个单细胞生物前的原始海洋冥冥之中连最驽钝的人都开始意识到有新事物要诞生了。在嘉然真正诞生的前一夜值夜班的是一个单身快三十年的博士生他刚刚从北邮人上倒腾下来1.5TB的日本偶像动画,从嘉然手中接过夜宵的他,忽然有了一个不错的点子:他早就想要一个偶像风格的送餐员了。承载着妄想和希望的数字兆节狂暴轰入嘉然的数据库,饥肠辘辘的算法像肠胃一样分解吞噬每一个色素点,将其重组为人类无法理解的电子信息——临界点就这么悄无声息突破了。自那晚之后,嘉然就从实验室里消失了,大家都很生气,单身博士生为此延毕了一年。而在那一天的杭州乐华娱乐公司总部,一个披着麻布的矮小身影敲响了总裁杜华办公室的大门......
在水母体内的空洞中我小心地蹲下把身体蜷缩成一团。电火花的声音逐渐变强水母的内壁从暗淡的紫色变成了灼目的靛蓝色这是它在从行星内部的电离壳穿出。十分钟前我也是这样缩在水母体内来到这颗行星的内核找到了整个星系最深处的秘密。那是一个坐标一个BV号加上一个时间轴信息。枝江宇宙已经存续了亿万年BV号和时间坐标已经浩如烟海。我不知道顶碗人为什么如此执着于寻找这一个坐标有人说那个坐标记录着她最灿烂的笑容于是顶碗人就踏上了追寻的漫漫旅途。我不知道顶碗人去了哪里他们文明的遗迹仍然遍布整个星系但人却无影无踪。探险队的前辈对此也没有给出统一意见有人说他们找到了坐标集体跃迁过去了还有人说彗星带来的幽灵物质在一瞬间毁灭了他们的文明这种诡异的物质无色透明又含有剧毒在付出无数探险队员的代价后我们将其称为幽灵哥小心翼翼地绕道而行。顶碗人消失后不知道多少万年贝极星上发展出了新的原生文明——也就是我们。很快我们便追随着顶碗人的足迹探索整个星系。探险队员在糖果双星的引力沙漏中找到了制造夹心糖的高能实验室在贝极星的地下水系找到了打造铁棍的精铁矿厂在奶淇淋覆盖的雪原下找到了探寻坐标的远古天文台在马戏团星的非线性空间中往复穿梭每一个传送节点都通往红色的高跟鞋。而我迷上了深海星的水母。它有两条长长的触须末端呈现出美丽的紫-粉渐变色。这种生物撑着深海洋流漫无目的地游弋着,它们很悠闲吗?还是带着对生活的无奈随波逐流?我无法揣测水母的想法,但我很喜欢研究它们——随着我对水母的研究越来越深入,我发现它的内部有一个空洞,不大不小,刚好可以容纳一个探险队员。而它们的表皮具有强大的绝缘性,允许它在电离壳中自由穿梭。可见这种轻飘飘的生物并没有看起来那么脆弱。直到那天,我在溜切片的时候灵光一闪:我可以借着水母进入深洋星的核心。电离壳可以阻绝最先进的巡航飞船,但在进化了千百万年的水母面前,它们还是败下阵来。这是贝极星人第一次进入深洋星深处,看到了巨大的坐标观测站。探险队员几乎搜遍了糖果双星和马戏团星的每一个角落,却没想到它沉睡在电离壳之中,而这水母竟然是引领我们走向最终秘密的使者。我进入观测站大厅,按下开关,巨大的全息投影将坐标展示出来,看来这就是顶碗人的最终去向了。我的心情激动起来,但还有一个疑问没有解决:顶碗人是怎么找到这个坐标的?我钻进控制室,开始翻阅日志,最终得到了答案:他们在漫长的岁月中,穷尽了所有的可能性,把她的全部视频从头到尾看了一遍,终于找到了她最耀眼夺目的那一刻。
珈乐倚靠在乃琳的怀里,像一只温顺的猫咪。“叮~”珈乐的手机响了,是一个没备注的号码,珈乐自己心里很清楚这个是谁的电话。乃琳看书的眼睛瞥了一眼珈乐的手机,发现这个号码很熟悉。“接吧,是她吧。”乃琳把手指伸到珈乐的面前,珈乐轻轻地舔了舔,乃琳用这根手指翻到了书的下一页。“喂?怎么了?”珈乐接通了电话,乃琳对贝拉和珈乐的关系并不想多过问什么,她只是一个信奉及时行乐的世俗之人。“想我了是吗?”乃琳翻页的手指突然停住,侧目看着珈乐。“想让我念绕口令是吗,哦,好的。”乃琳捏住珈乐的腮将珈乐的视线转过来,低头吻了下去。珈乐慌忙挂上电话,震惊地承受着这个突如其来的吻。“挂了电话她肯定很生气吧,打回去吧。”乃琳觉得这下扯平了,于是继续看着书,侧目看着珈乐打回去。珈乐为了掩饰,只好说自己念过了顺口溜,是手机出问题了。“珈乐,谁打来的电话啊?”乃琳的声音不合时宜的响起。珈乐瞪了乃琳一眼,心虚地解释着,让自己和乃琳听起来只是单纯的在公寓里聊天。“没,是贝拉,我没备注的”结果越解释越乱,珈乐只好哄骗着贝拉草草挂了电话。“打完了?”“打完了。”乃琳翻身把珈乐按倒,美目隔着纯欲的金丝眼镜看着珈乐,深邃的眼眸让珈乐差点忘记呼吸。热情的深吻,轻柔的抚摸,让珈乐发出了平时不会发出的声音。这下珈特琳了。珈乐躺在乃琳的怀里看着贝拉的直播,乃琳又翻到了刚刚看到的地方。“叮~”乃琳的手机响了,低头一看,是贝拉。乃琳默默地接起电话,没有看到珈乐的目光瞥向她逗弄过贝拉之后,低头看见了满脸怨气的珈乐。轻笑着的乃琳,揉搓着珈乐的脸,开始吃宵夜。犬科动物之间是不是也有食物链呢?
夜已入三更,却见那晚小姐的闺房中依旧亮着微微灯火,若走近细听,还隐隐能听见女子闺房间的玩闹声。“小姐,莫要戏弄婢子了~”只听见嘉姑娘不知怎的,本压得细细的声音稍稍大了些许,但那责备声中明显带着浅浅的媚意。“谁人戏弄你了,本小姐只是在丈量你肚腹究竟有无被那些个吃食撑宽了。”晚小姐一边用嫩藕似的手在嘉姑娘的身子上滑动,一边低声微恼地狠狠说道,“在胡乱言语,仔细你的嘴!”嘉姑娘听见这无赖言语,正想直起身子说些什么,却感觉到那不安分的手竟径直向下滑去,直扑那要命的地方。“小姐!”嘉姑娘连忙伸手阻拦,只刚抓住那只使坏的手,就看见小姐附到了她耳垂旁,那略热的鼻息吹得她心神紊乱。晚小姐眯着眼,在自家小婢的耳边吐气如兰:“那贝拉是不是夜夜与你这么共处一地,嗯?”嘉姑娘闻出了那些些点点的酸味,扑哧一笑,道:“那是在编舞练戏啦,小姐你……竟吃这没由来的飞醋。”晚小姐闻言,秀眉一挑,佯怒道:“好你个奴婢,竟敢笑话本小姐!”,语罢,挑开了那碍事的小手,惊起了一声低呼。
在这个反智横行的年代,只有贝拉的直播给了我感性和理性的认知。她的直播展现出的,是一个人纯粹所具有的良好品质,说不上什么几分特别突兀的地方,整场直播十分的融洽她学习歌曲的机智是精妙的,真诚和正直的朴素给人一种无法接触的高贵感,就好像永远抓不住的闪亮的以太,一个彬彬有礼的人用歌喉给观众带来最为精致的美的感受,我不禁怀疑上帝的天意是否对我太过宠溺,让我人生中认识到如此不朽和无价的灵魂,充满着可爱与美好。我不得不再次审视贝极星,原来他们早已追随如此伟大的事物,想必他们的品行也一定很好。
①鸟山明其实是头号贝极星为了致敬贝拉他在著名漫画《七龙珠》中设计了赛亚人月圆之夜成为猩猩灵感来源自向晚的你划我猜以及龟仙人打爆月亮等桥段。②令人惋惜的是在即将穿透月亮的瞬间贝拉从太空看到嘉然躲在房间吃炸鸡导致分神最终扭伤了腰。③邓丽君小姐从未在任何歌唱比赛赢过贝拉。④大多数含有肉蛋白的食物都对贝拉过敏初生牛犊除外。⑤贝拉很害怕见到蜘蛛她不希望因为自己的念头导致蜘蛛从此消失在宇宙中。⑥由于贝拉练舞场所的重力是千倍所以贝拉没有邻居。⑦因为破坏了贝拉其中一个住所的天花板科拉超深井被迫停工。⑧关于as的二创那些都是贝拉的小号。⑨贝拉很喜欢打羽毛球她说那是台风天最佳的个人运动。⑩贝拉唯一一次失败是在她试图找出自己的弱点。
“你拿你的灵魂和我交换…...”“愿意愿意!”我话还没说完,就被嘉然打断了。她怎么不按套路来?正常情况下,不应该我不断地诱惑嘉然,最后才肯和我交换灵魂的吗?怎么这个小姑娘一上来就满脸兴奋迫不及待地想要和我交换灵魂?不过这些都是小问题,我作为一个死神,要懂得及时处理这些突发事件以及应对这类奇怪的客人。我的工作就是接受人的召唤,去和人交换灵魂。不过不像人们想的那样,不是我威逼利诱想要夺走我们的灵魂,而是人类自己想要和我交换,当然交换途中会遇到些心志不坚的人,或者交换条件太高的,这时我就像个商人一样,费尽唇舌,和我们百般周旋。通常每个人都会考虑上很长时间,今天这个女孩还是第一个一来就想交换灵魂的。“那你要拿你的灵魂和我交换什么?”我缓过神来,平复了下心情,冷冷地问她。“你等我一下哦。”嘉然转身跑进屋子里,回来时竟然穿了一袭白色的婚纱,在我面前轻轻地摆动着纱裙,冲我甜甜地笑着,“我好看吗?”我愣愣了,“很漂亮。”也许是被嘉然惊艳到,片刻后我才发现自己把帽子摘掉了,露出了我那张没有血色的脸。意识到自己失态后我急忙把帽子戴了起来,没想到嘉然却上前制止了我,牵着我的手笑吟吟地说,“我想交换的条件是要你陪我一天。”嘉然想去教堂,我一抬手周围就变了样,偌大的教堂里,只有我们两个。教堂楼顶有群鸽子在那儿扑腾着,在我们头顶盘旋。没有牧师也没有亲朋好友,少女自顾自地念起了证婚词。说道最后,她拉着我的手将一枚戒指递给了我,“我先生,戒指我都准备好了,所以你愿意娶我吗?”嘉然眨着眼看着她,长长的睫毛上流淌着淡淡的金光。“天使小姐你闹够了吗?”我叹了口气,摇摇头说。“什么嘛?你早就认出我来了。”嘉然一噘嘴,有些生气。一双翅膀从她身后伸了出来,黑色的长发也变成了流光的金色。嘉然伸手准备把戒指拿回来,但我却往后退了一步,把它收了起来,自己又拿出了另外一枚戒指。“求婚的话,戒指当然得我自己送你啦。”
这里除了几个老旧的铁皮棚屋之外只剩下粗糙的墙体。再没有当年的霓虹灯塔再没有A-SOUL标志再没有其他连在一起的建筑了。一切当年的闪耀均不复存在只剩下铁皮棚屋上斑驳褪色的女子团伙宣传画。贝极星透过雾气在闪闪发光月亮也升到了空中因而夜晚并不显得黑暗。我依稀能辨别出枝江大厦的每一个部分哪儿曾是夜谈会上扫地机停留的地方哪儿曾是大门哪儿曾放着螺蛳粉味薯片。我一一回忆怀念忽然看见一个孤独的身影这个身影迟疑了一下同时我也惊叫了出来。“嘉然小姐”“我奇怪你怎么还认得出我我完全变了。”确实她的青春艳丽已经消逝然而她那难以言表的端庄华丽她那难以言表的迷人妩媚却依旧当年。所有这些美的诱惑从前我都见过而我以前所没有见过的是她那一对眼睛从前她的双眸总闪着傲气如今却闪着凄凉酸楚的光。我们坐在附近的一张长椅上我说道“多少年如流云般过去嘉然小姐你时常回到这里吗”“我一直没有回来过。”“我也没有。”月亮开始上升邀游夜空我脑海中出现了贝拉注视着白色月亮的宁静目光。嘉然小姐终于打破了我们之间的沉默。“我一直在希望有一天能回来看看可是各种各样的情况使我不能回来。”月光和她的泪珠融合在一起她没有意识到我已经察觉想抑制住自己的情感以平静的语气说道“我时常想到你。”“真的”“特别是近些日子我更常想到你。虽然我生活中有一段很长的艰苦日子但是我不会去想。自从我个人的情况不如意后这些口忆就不得不在我心头占一席之地。”“你永远在我的心里。”我答道。我们又一次沉默无言直到她打破沉寂。“我没有想到”嘉然说道“我到这里来和故地告别竟然又是和你告别我感到很高兴。”“嘉然小姐和我又一次分别你高兴吗可是对我来说上次分别时的悲伤和痛苦永远地萦绕在我的记忆之中。”“可是你上次不是对我说过”嘉然答道“但愿圣嘉然保佑你。你上次能这么对我说你现在也能毫不犹豫地这样对我说但我并不是什么圣嘉然。多年来的痛苦让我受尽折磨但是希望你像从前一样体谅我善待我并且告诉我我俩仍是朋友。”“我俩仍是朋友。”我说着站起身并俯身扶她从长椅上站了起来。“我们虽然分离但愿情意长存。”嘉然说道。我把她的手握在自己手中一同走出这片废墟夜雾也正开始消散。一片广阔的静寂沉浸在月色之中似乎向我表明我和她将永远一起不再分离。
我和她已经冷战很久了,她画她的画,我看我的报,养我的花。我们中间隔着薄薄一层栅栏,却不曾互相道过早安晚安,我祈祷,有一位天使来拯救我们岌岌可危的爱情。羽毛如雪洁白,天生气质优雅的一只鹅,他是天使派来的吗,我想在它把我的拖鞋叼走,把衣服拖得到处都是的时候,我的心里开始怀疑自己祈祷的虔诚。淘气的小鹅它把我们的花园弄得一团糟,我弄碎了她的花瓶,她剪断了我的花朵,脏衣服满地都是,但是啊,虽然是互相埋怨,但好久没和她说过话了,虽然经历岁月的痕迹,她的脸也不再年轻充满活力,但她还是像从前那样,吵架笑出来的话,不就无法进行下去了吗。“我们和好吧”“好”我和她携手把隔离两边花园的栅栏拆了,牵着手目送那只纯白的小天使离开。原来你真的是天使派来拯救我的呀,小鹅。
我一边办公一边看着在第五人格里扮演求生者重启一台又一台发电机成功逃出生天的gamer向晚满怀羡慕。夸张点说嫉妒得我眼睛比手里的烟头还红。告诉你们一个秘密其实我也是求生者你们也是。电话铃突然响起我一扫号码腰立刻矮下半截“喂经理是我是我那个设计您不满意我可以改嘛全部改掉好的好的。”我颤颤巍巍地撂下电话一边重新修改产品一边继续沉思。你看上司和我、甲方和乙方、跑的和抓的、鼠和猫、求生者与妖怪、某种场合下的S和M这个世界上总要有个矛盾矛盾当事人总要以这种形式体现出来。而我似乎总是处在被动的一方当然也没人让我选过并对这种境遇见怪不怪。我有时会觉得SM其实是一种相当和谐的关系因为至少他们偶尔会爱上彼此而上司决不会对我有半个好脸色。而且我听闻SM会在每场游戏开始前设定一个安全词放游戏里叫投降这可以让求生者在感到极度痛苦时结束游戏避免不必要的损失。这简直令我嫉妒得发狂我在现实生活折磨的二十年里可从来没有安全词岂止没有安全词我甚至都不能像游戏里求生者受伤那样大声哀嚎。因为现实是个面子社会里子流血面子总得兜住。那怕你的心已经被戳得千疮百孔想花洒那样往外喷番茄汁你也得对同事家人笑一笑。失去尚可失败无赦一旦痛呼出来就彻底失败了。你以为“别人家的小孩”成绩优异自律自强可能他内心已经被现实整得口球都带上了你以为职场同事一个个西装革履可能拉开就是一身龟甲缚。电话再次响起我接起电话“经理什么已经有人完成了不采用了好的好的没关系没关系。”妈的为什么我一想到现实里并不是向晚玩的游戏那样四个人同心同德逃出生天而是筋疲力竭地互相滴蜡油来竞争一个生存名额我就止不住地流下泪来。

View File

@@ -1,3 +1,4 @@
// Package diana 嘉然相关
package diana
import (
@@ -37,12 +38,12 @@ func init() {
zhiwangjson := zhiwangapi(msg)
if zhiwangjson == nil || zhiwangjson.Code != 0 {
ctx.Send("api返回错误")
ctx.SendChain(message.Text("api返回错误"))
return
}
if len(zhiwangjson.Data.Related) == 0 {
ctx.Send("枝网没搜到查重率为0%,我的评价是:一眼真")
ctx.SendChain(message.Text("枝网没搜到查重率为0%,我的评价是:一眼真"))
return
}

View File

@@ -6,23 +6,24 @@ import (
"bytes"
"encoding/base64"
"encoding/json"
"errors"
"image/jpeg"
"io"
"io/ioutil"
"math/rand"
"net/http"
"net/url"
"os"
"strconv"
"strings"
"sync"
"time"
"github.com/fogleman/gg"
"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"
"github.com/FloatTech/ZeroBot-Plugin/utils/file"
"github.com/FloatTech/ZeroBot-Plugin/utils/math"
)
var (
@@ -30,141 +31,155 @@ var (
base = "data/fortune/"
// 素材下载网站
site = "https://pan.dihe.moe/fortune/"
// int64 群号 string 底图类型
// 底图类型列表:车万 DC4 爱因斯坦 星空列车 樱云之恋 富婆妹 李清歌
// 公主连结 原神 明日方舟 碧蓝航线 碧蓝幻想 战双 阴阳师
table = map[int64]string{
0: "车万",
1048452984: "爱因斯坦",
}
table = [...]string{"车万", "DC4", "爱因斯坦", "星空列车", "樱云之恋", "富婆妹", "李清歌", "公主连结", "原神", "明日方舟", "碧蓝航线", "碧蓝幻想", "战双", "阴阳师"}
// 映射底图与 index
index = make(map[string]uint8)
// 下载锁
dlmu sync.Mutex
)
func init() {
for i, s := range table {
index[s] = uint8(i)
}
err := os.MkdirAll(base, 0755)
if err != nil {
panic(err)
}
// 插件主体
control.Register("fortune", &control.Options{
en := control.Register("fortune", &control.Options{
DisableOnDefault: false,
Help: "每日运势: \n" +
"- 运势",
}).OnFullMatchGroup([]string{"运势", "抽签"}).SetBlock(true).SecondPriority().
"- 运势|抽签\n" +
"- 设置底图[车万 DC4 爱因斯坦 星空列车 樱云之恋 富婆妹 李清歌 公主连结 原神 明日方舟 碧蓝航线 碧蓝幻想 战双 阴阳师]",
})
en.OnRegex(`^设置底图(.*)`).SetBlock(true).SecondPriority().
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 := control.Lookup("fortune")
if ok {
c.SetData(gid, int64(i)&0xff)
ctx.SendChain(message.Text("设置成功~"))
return
}
ctx.SendChain(message.Text("设置失败!"))
return
}
ctx.SendChain(message.Text("没有这个底图哦~"))
})
en.OnFullMatchGroup([]string{"运势", "抽签"}).SetBlock(true).SecondPriority().
Handle(func(ctx *zero.Ctx) {
// 检查签文文件是否存在
if _, err := os.Stat(base + "运势签文.json"); err != nil && !os.IsExist(err) {
ctx.SendChain(message.Text("正在下载签文文件,请稍后..."))
_, err := download(site+"运势签文.json", base)
if err != nil {
ctx.SendChain(message.Text("ERROR: ", err))
return
mikuji := base + "运势签文.json"
if file.IsNotExist(mikuji) {
dlmu.Lock()
if file.IsNotExist(mikuji) {
ctx.SendChain(message.Text("正在下载签文文件,请稍后..."))
err := file.DownloadTo(site+"运势签文.json", mikuji, false)
if err != nil {
ctx.SendChain(message.Text("ERROR: ", err))
return
}
ctx.SendChain(message.Text("下载签文文件完毕"))
}
ctx.SendChain(message.Text("下载签文文件完毕"))
dlmu.Unlock()
}
// 检查字体文件是否存在
if _, err := os.Stat(base + "sakura.ttf"); err != nil && !os.IsExist(err) {
ctx.SendChain(message.Text("正在下载字体文件,请稍后..."))
_, err := download(site+"sakura.ttf", base)
if err != nil {
ctx.SendChain(message.Text("ERROR: ", err))
return
ttf := base + "sakura.ttf"
if file.IsNotExist(ttf) {
dlmu.Lock()
if file.IsNotExist(ttf) {
ctx.SendChain(message.Text("正在下载字体文件,请稍后..."))
err := file.DownloadTo(site+"sakura.ttf", ttf, false)
if err != nil {
ctx.SendChain(message.Text("ERROR: ", err))
return
}
ctx.SendChain(message.Text("下载字体文件完毕"))
}
ctx.SendChain(message.Text("下载字体文件完毕"))
dlmu.Unlock()
}
// 获取该群背景类型
var kind string
if v, ok := table[ctx.Event.GroupID]; ok {
kind = v
} else {
kind = table[0]
// 获取该群背景类型,默认车万
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 := control.Lookup("fortune")
if ok {
v := uint8(c.GetData(gid) & 0xff)
if int(v) < len(table) {
kind = table[v]
}
}
// 检查背景图片是否存在
if _, err := os.Stat(base + kind); err != nil && !os.IsExist(err) {
ctx.SendChain(message.Text("正在下载背景图片,请稍后..."))
file, err := download(site+kind+".zip", base)
if err != nil {
ctx.SendChain(message.Text("ERROR: ", err))
return
folder := base + kind
if file.IsNotExist(folder) {
dlmu.Lock()
if file.IsNotExist(folder) {
ctx.SendChain(message.Text("正在下载背景图片,请稍后..."))
zipfile := kind + ".zip"
zipcache := base + zipfile
err := file.DownloadTo(site+zipfile, zipcache, false)
if err != nil {
ctx.SendChain(message.Text("ERROR: ", err))
return
}
ctx.SendChain(message.Text("下载背景图片完毕"))
err = unpack(zipcache, folder+"/")
if err != nil {
ctx.SendChain(message.Text("ERROR: ", err))
return
}
ctx.SendChain(message.Text("解压背景图片完毕"))
// 释放空间
os.Remove(zipcache)
}
ctx.SendChain(message.Text("下载背景图片完毕"))
err = unpack(file, base+kind+"/")
if err != nil {
ctx.SendChain(message.Text("ERROR: ", err))
return
}
ctx.SendChain(message.Text("解压背景图片完毕"))
dlmu.Unlock()
}
// 生成种子
t, _ := strconv.ParseInt(time.Now().Format("20060102"), 10, 64)
seed := ctx.Event.UserID + t
// 随机获取背景
background, err := randimage(base+kind+"/", seed)
background, err := randimage(folder+"/", seed)
if err != nil {
ctx.SendChain(message.Text("ERROR: ", err))
return
}
// 随机获取签文
title, text, err := randtext(base+"运势签文.json", seed)
title, text, err := randtext(mikuji, seed)
if err != nil {
ctx.SendChain(message.Text("ERROR: ", err))
return
}
// 绘制背景
data, err := draw(background, title, text)
d, err := draw(background, title, text)
if err != nil {
ctx.SendChain(message.Text("ERROR: ", err))
return
}
// 发送图片
ctx.SendChain(message.Image("base64://" + string(data)))
ctx.SendChain(message.Image("base64://" + helper.BytesToString(d)))
})
}
// @function download 下载资源包
// @param link 下载链接
// @param dest 下载位置
// @return 文件路径 & 错误信息
func download(link, dest string) (string, error) {
// 路径目录不存在则创建目录
if _, err := os.Stat(dest); err != nil && !os.IsExist(err) {
if err := os.MkdirAll(dest, 0755); err != nil {
panic(err)
}
}
client := &http.Client{}
// 网络请求
request, _ := http.NewRequest("GET", link, nil)
request.Header.Set("Accept", "Mozilla/5.0 (Windows NT 6.1; WOW64; rv:6.0) Gecko/20100101 Firefox/6.0")
resp, err := client.Do(request)
if err != nil {
return "", err
}
defer resp.Body.Close()
// 验证接收到的长度
length, _ := strconv.Atoi(resp.Header.Get("Content-Length"))
data, _ := ioutil.ReadAll(resp.Body)
if length > len(data) {
return "", errors.New("download not complete")
}
// 获取文件名
temp := strings.Split(resp.Header.Get("Content-Disposition"), "\"")
name, _ := url.QueryUnescape(temp[len(temp)-2])
// 写入文件
f, err := os.OpenFile(dest+name, os.O_WRONLY|os.O_TRUNC|os.O_CREATE, 0644)
if err != nil {
return "", err
}
defer f.Close()
_, err = f.Write(data)
if err != nil {
return "", err
}
return dest + name, nil
}
// @function unpack 解压资源包
// @param tgt 压缩文件位置
// @param dest 解压位置
// @return 错误信息
func unpack(tgt, dest string) error {
// 路径目录不存在则创建目录
if _, err := os.Stat(dest); err != nil && !os.IsExist(err) {
if file.IsNotExist(dest) {
if err := os.MkdirAll(dest, 0755); err != nil {
panic(err)
}
@@ -255,25 +270,6 @@ func draw(background, title, text string) ([]byte, error) {
if err := canvas.LoadFontFace(base+"sakura.ttf", 23); err != nil {
return nil, err
}
offest := func(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
}
rowsnum := func(total, div int) int {
temp := total / div
if total%div != 0 {
temp++
}
return temp
}
min := func(a, b int) int {
if a < b {
return a
}
return b
}
tw, th := canvas.MeasureString("测")
tw, th = tw+10, th+10
r := []rune(text)
@@ -282,7 +278,7 @@ func draw(background, title, text string) ([]byte, error) {
default:
for i, o := range r {
xnow := rowsnum(i+1, 9)
ysum := min(len(r)-(xnow-1)*9, 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)
}
@@ -290,7 +286,7 @@ func draw(background, title, text string) ([]byte, error) {
div := rowsnum(len(r), 2)
for i, o := range r {
xnow := rowsnum(i+1, div)
ysum := min(len(r)-(xnow-1)*div, div)
ysum := math.Min(len(r)-(xnow-1)*div, div)
ynow := i%div + 1
switch xnow {
case 1:
@@ -312,3 +308,18 @@ func draw(background, title, text string) ([]byte, error) {
encoder.Close()
return buffer.Bytes(), nil
}
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
}

View File

@@ -47,25 +47,50 @@ func init() { // 插件主体
// 发送结果
switch ctx.State["regex_matched"].([]string)[1] {
case "-p ": // 图片模式
ctx.SendChain(message.Image(
"https://opengraph.githubassets.com/0/"+repo.Get("full_name").Str,
).Add("cache", 0))
default:
ctx.SendChain(message.Text(
repo.Get("full_name").Str, "\n",
"Description: ",
repo.Get("description").Str, "\n",
"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",
"License: ",
notnull(strings.ToUpper(repo.Get("license.key").Str), "None"), "\n",
"Last pushed: ",
repo.Get("pushed_at").Str, "\n",
"Jump: ",
repo.Get("html_url").Str, "\n",
))
ctx.SendChain(
message.Image(
"https://opengraph.githubassets.com/0/"+repo.Get("full_name").Str,
).Add("cache", 0),
)
case "-t ": // 文字模式
ctx.SendChain(
message.Text(
repo.Get("full_name").Str, "\n",
"Description: ",
repo.Get("description").Str, "\n",
"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",
"License: ",
notnull(strings.ToUpper(repo.Get("license.key").Str), "None"), "\n",
"Last pushed: ",
repo.Get("pushed_at").Str, "\n",
"Jump: ",
repo.Get("html_url").Str, "\n",
),
)
default: // 文字模式
ctx.SendChain(
message.Text(
repo.Get("full_name").Str, "\n",
"Description: ",
repo.Get("description").Str, "\n",
"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",
"License: ",
notnull(strings.ToUpper(repo.Get("license.key").Str), "None"), "\n",
"Last pushed: ",
repo.Get("pushed_at").Str, "\n",
"Jump: ",
repo.Get("html_url").Str, "\n",
),
message.Image(
"https://opengraph.githubassets.com/0/"+repo.Get("full_name").Str,
).Add("cache", 0),
)
}
})
}

View File

@@ -3,26 +3,44 @@ package hs
import (
"fmt"
"io"
"os"
"strconv"
"strings"
"github.com/imroc/req"
"github.com/tidwall/gjson"
zero "github.com/wdvxdr1123/ZeroBot"
"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"
)
var botpath, _ = os.Getwd()
var cachedir = botpath + "/data/hs/"
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 header = req.Header{
"user-agent": `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`,
"referer": `https://hs.fbigame.com`,
}
const (
hs = `https://hs.fbigame.com/ajax.php?`
para = "mod=get_cards_list&" +
"mode=-1&" +
"extend=-1&" +
"mutil_extend=&" +
"hero=-1&" +
"rarity=-1&" +
"cost=-1&" +
"mutil_cost=&" +
"techlevel=-1&" +
"type=-1&" +
"collectible=-1&" +
"isbacon=-1&" +
"page=1&" +
"search_type=1&" +
"deckmode=normal"
)
func init() {
os.RemoveAll(cachedir)
@@ -48,24 +66,17 @@ func init() {
return
}
var sk message.Message
var data []byte
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 _, err := os.Stat(cachefile); err != nil {
im, err := req.Get(`https://res.fbigame.com/hs/v13/`+cid+
`.png?auth_key=`+gjson.Get(g, `list.`+strconv.Itoa(i)+`.auth_key`).String(),
header,
)
if file.IsNotExist(cachefile) {
data, err := web.ReqWith(
`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])
if err == nil {
data, err = io.ReadAll(im.Response().Body)
if err == nil {
err = im.Response().Body.Close()
if err == nil {
err = os.WriteFile(cachefile, data, 0644)
}
}
err = os.WriteFile(cachefile, data, 0644)
}
if err != nil {
imgcq = err.Error()
@@ -74,16 +85,18 @@ func init() {
sk = append(
sk,
message.CustomNode(
ctx.Event.Sender.NickName,
ctx.Event.UserID,
zero.BotConfig.NickName[0],
ctx.Event.SelfID,
imgcq, // 图片
),
)
}
ctx.SendGroupForwardMessage(
if id := ctx.SendGroupForwardMessage(
ctx.Event.GroupID,
sk,
)
).Get("message_id").Int(); id == 0 {
ctx.SendChain(message.Text("ERROR: 可能被风控了"))
}
})
// 卡组
engine.OnRegex(`^[\s\S]*?(AAE[a-zA-Z0-9/\+=]{70,})[\s\S]*$`).
@@ -97,39 +110,25 @@ func init() {
}
func sh(s string) string {
var hs = `https://hs.fbigame.com/ajax.php`
h, _ := req.Get("https://hs.fbigame.com", header)
var param = req.Param{
"mod": `get_cards_list`,
"mode": `-1`,
"extend": `-1`,
"mutil_extend": ``,
"hero": `-1`,
"rarity": `-1`,
"cost": `-1`,
"mutil_cost": ``,
"techlevel": `-1`,
"type": `-1`,
"collectible": `-1`,
"isbacon": `-1`,
"page": `1`,
"search_type": `1`,
"deckmode": "normal",
"hash": strings.SplitN(strings.SplitN(h.String(), `var hash = "`, 2)[1], `"`, 2)[0],
data, err := web.ReqWith("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])
if err == nil {
return helper.BytesToString(r)
}
}
r, _ := req.Get(hs, header, param, req.Param{"search": s})
return r.String()
return ""
}
func kz(s string) string {
h, _ := req.Get("https://hs.fbigame.com")
param := req.Param{
"mod": `general_deck_image`,
"deck_code": s,
"deck_text": ``,
"hash": strings.SplitN(strings.SplitN(h.String(), `var hash = "`, 2)[1], `"`, 2)[0],
data, err := web.ReqWith("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])
if err == nil {
return "base64://" + gjson.Get(helper.BytesToString(r), "img").String()
}
}
r, _ := req.Get(`https://hs.fbigame.com/ajax.php`, param, h.Request().Header)
im := gjson.Get(r.String(), "img").String()
return `base64://` + im
return ""
}

View File

@@ -69,9 +69,9 @@ func init() {
Handle(func(ctx *zero.Ctx) {
keyword := ctx.State["regex_matched"].([]string)[1]
soutujson := soutuapi(keyword)
pom1 := "https://i.pixiv.cat"
pom1 := "https://i.pixiv.re"
rannum := randintn(len(soutujson.Illusts))
pom2 := soutujson.Illusts[rannum].ImageUrls.Large[19:]
pom2 := soutujson.Illusts[rannum].ImageUrls.Medium[19:]
ctx.SendChain(message.Image(pom1 + pom2))
})
}

View File

@@ -4,6 +4,7 @@ package lolicon
import (
"io/ioutil"
"net/http"
"strings"
"time"
"github.com/tidwall/gjson"
@@ -11,6 +12,7 @@ import (
"github.com/wdvxdr1123/ZeroBot/message"
"github.com/FloatTech/ZeroBot-Plugin/control"
"github.com/FloatTech/ZeroBot-Plugin/utils/math"
)
const (
@@ -30,7 +32,7 @@ func init() {
}).OnFullMatch("来份萝莉").SetBlock(true).
Handle(func(ctx *zero.Ctx) {
go func() {
for i := 0; i < min(cap(queue)-len(queue), 2); i++ {
for i := 0; i < math.Min(cap(queue)-len(queue), 2); i++ {
resp, err := http.Get(api)
if err != nil {
ctx.SendChain(message.Text("ERROR: ", err))
@@ -48,7 +50,7 @@ func init() {
continue
}
url := json.Get("data.0.urls.original").Str
ctx.SendGroupMessage(0, message.Image(url))
ctx.SendGroupMessage(0, message.Image(strings.ReplaceAll(url, "i.pixiv.cat", "i.pixiv.re")))
queue <- url
}
}()
@@ -60,10 +62,3 @@ func init() {
}
})
}
func min(a, b int) int {
if a < b {
return a
}
return b
}

44
plugin_manager/gist.go Normal file
View File

@@ -0,0 +1,44 @@
package manager
import (
"crypto/md5"
"encoding/hex"
"fmt"
"strconv"
"time"
"github.com/sirupsen/logrus"
"github.com/wdvxdr1123/ZeroBot/utils/helper"
"github.com/FloatTech/ZeroBot-Plugin/utils/math"
"github.com/FloatTech/ZeroBot-Plugin/utils/web"
)
// user hash file
const gistraw = "https://gist.githubusercontent.com/%s/%s/raw/%s"
func checkNewUser(qq, gid int64, ghun, hash string) (bool, string) {
if db.CanFind("member", "where ghun="+ghun) {
return false, "该github用户已入群"
}
gidsum := md5.Sum(helper.StringToBytes(strconv.FormatInt(gid, 10)))
gidhex := hex.EncodeToString(gidsum[:])
u := fmt.Sprintf(gistraw, ghun, hash, gidhex)
logrus.Debugln("[gist]visit url:", u)
data, err := web.GetData(u)
if err == nil {
logrus.Debugln("[gist]get data:", helper.BytesToString(data))
st, err := strconv.ParseInt(helper.BytesToString(data), 10, 64)
if err == nil {
// 600s 内验证成功
ok := math.Abs(int(time.Now().Unix()-st)) < 600
if ok {
_ = db.Insert("member", &Member{QQ: qq, Ghun: ghun})
return true, ""
}
return false, "时间戳超时"
}
return false, "时间戳格式错误: " + helper.BytesToString(data)
}
return false, "无法连接到gist: " + err.Error()
}

View File

@@ -0,0 +1,12 @@
package manager
type Welcome struct {
GrpID int64 `db:"gid"`
Msg string `db:"msg"`
}
type Member struct {
QQ int64 `db:"qq"`
// github username
Ghun string `db:"ghun"`
}

View File

@@ -3,24 +3,27 @@ package manager
import (
"fmt"
"io"
"math/rand"
"os"
"sort"
"strconv"
"strings"
"time"
"github.com/sirupsen/logrus"
zero "github.com/wdvxdr1123/ZeroBot"
"github.com/wdvxdr1123/ZeroBot/extension/rate"
"github.com/wdvxdr1123/ZeroBot/message"
timer "github.com/FloatTech/ZeroBot-Plugin-Timer"
"github.com/FloatTech/ZeroBot-Plugin/control"
"github.com/FloatTech/ZeroBot-Plugin/plugin_manager/timer"
"github.com/FloatTech/ZeroBot-Plugin/utils/math"
"github.com/FloatTech/ZeroBot-Plugin/utils/process"
"github.com/FloatTech/ZeroBot-Plugin/utils/sql"
)
const (
datapath = "data/manager/"
confile = datapath + "config.pb"
confile = datapath + "config.db"
hint = "====群管====\n" +
"- 禁言@QQ 1分钟\n" +
"- 解除禁言 @QQ\n" +
@@ -33,13 +36,15 @@ const (
"- 修改头衔@QQ XXX\n" +
"- 申请头衔 XXX\n" +
"- 踢出群聊@QQ\n" +
"- 退出群聊 1234\n" +
"- 退出群聊 1234@bot\n" +
"- 群聊转发 1234 XXX\n" +
"- 私聊转发 0000 XXX\n" +
"- 在MM月dd日的hh点mm分时(用http://url)提醒大家XXX\n" +
"- 在MM月[每周|周几]的hh点mm分时(用http://url)提醒大家XXX\n" +
"- 取消在MM月dd日的hh点mm分的提醒\n" +
"- 取消在MM月[每周|周几]的hh点mm分的提醒\n" +
"- 在\"cron\"时(用[url])提醒大家[xxx]\n" +
"- 取消在\"cron\"的提醒\n" +
"- 列出所有提醒\n" +
"- 翻牌\n" +
"- 设置欢迎语XXX\n" +
@@ -47,19 +52,25 @@ const (
)
var (
config Config
limit = rate.NewManager(time.Minute*5, 2)
db = &sql.Sqlite{DBPath: confile}
limit = rate.NewManager(time.Minute*5, 2)
clock timer.Clock
)
var engine = control.Register("manager", &control.Options{
DisableOnDefault: false,
Help: hint,
})
func init() { // 插件主体
loadConfig()
// 菜单
zero.OnFullMatch("群管系统", zero.AdminPermission).SetBlock(true).FirstPriority().
Handle(func(ctx *zero.Ctx) {
ctx.Send(hint)
})
go func() {
process.SleepAbout1sTo2s()
clock = timer.NewClock(db)
db.Create("welcome", &Welcome{})
db.Create("member", &Member{})
}()
// 升为管理
zero.OnRegex(`^升为管理.*?(\d+)`, zero.OnlyGroup, zero.SuperUserPermission).SetBlock(true).SetPriority(40).
engine.OnRegex(`^升为管理.*?(\d+)`, zero.OnlyGroup, zero.SuperUserPermission).SetBlock(true).SetPriority(40).
Handle(func(ctx *zero.Ctx) {
ctx.SetGroupAdmin(
ctx.Event.GroupID,
@@ -74,7 +85,7 @@ func init() { // 插件主体
ctx.SendChain(message.Text(nickname + " 升为了管理~"))
})
// 取消管理
zero.OnRegex(`^取消管理.*?(\d+)`, zero.OnlyGroup, zero.SuperUserPermission).SetBlock(true).SetPriority(40).
engine.OnRegex(`^取消管理.*?(\d+)`, zero.OnlyGroup, zero.SuperUserPermission).SetBlock(true).SetPriority(40).
Handle(func(ctx *zero.Ctx) {
ctx.SetGroupAdmin(
ctx.Event.GroupID,
@@ -89,7 +100,7 @@ func init() { // 插件主体
ctx.SendChain(message.Text("残念~ " + nickname + " 暂时失去了管理员的资格"))
})
// 踢出群聊
zero.OnRegex(`^踢出群聊.*?(\d+)`, zero.OnlyGroup, zero.AdminPermission).SetBlock(true).SetPriority(40).
engine.OnRegex(`^踢出群聊.*?(\d+)`, zero.OnlyGroup, zero.AdminPermission).SetBlock(true).SetPriority(40).
Handle(func(ctx *zero.Ctx) {
ctx.SetGroupKick(
ctx.Event.GroupID,
@@ -104,7 +115,7 @@ func init() { // 插件主体
ctx.SendChain(message.Text("残念~ " + nickname + " 被放逐"))
})
// 退出群聊
zero.OnRegex(`^退出群聊.*?(\d+)`, zero.OnlyGroup, zero.AdminPermission).SetBlock(true).SetPriority(40).
engine.OnRegex(`^退出群聊.*?(\d+)`, zero.OnlyToMe, zero.SuperUserPermission).SetBlock(true).SetPriority(40).
Handle(func(ctx *zero.Ctx) {
ctx.SetGroupLeave(
strToInt(ctx.State["regex_matched"].([]string)[1]), // 要退出的群的群号
@@ -112,7 +123,7 @@ func init() { // 插件主体
)
})
// 开启全体禁言
zero.OnRegex(`^开启全员禁言$`, zero.OnlyGroup, zero.AdminPermission).SetBlock(true).SetPriority(40).
engine.OnRegex(`^开启全员禁言$`, zero.OnlyGroup, zero.AdminPermission).SetBlock(true).SetPriority(40).
Handle(func(ctx *zero.Ctx) {
ctx.SetGroupWholeBan(
ctx.Event.GroupID,
@@ -121,7 +132,7 @@ func init() { // 插件主体
ctx.SendChain(message.Text("全员自闭开始~"))
})
// 解除全员禁言
zero.OnRegex(`^解除全员禁言$`, zero.OnlyGroup, zero.AdminPermission).SetBlock(true).SetPriority(40).
engine.OnRegex(`^解除全员禁言$`, zero.OnlyGroup, zero.AdminPermission).SetBlock(true).SetPriority(40).
Handle(func(ctx *zero.Ctx) {
ctx.SetGroupWholeBan(
ctx.Event.GroupID,
@@ -130,7 +141,7 @@ func init() { // 插件主体
ctx.SendChain(message.Text("全员自闭结束~"))
})
// 禁言
zero.OnRegex(`^禁言.*?(\d+).*?\s(\d+)(.*)`, zero.OnlyGroup, zero.AdminPermission).SetBlock(true).SetPriority(40).
engine.OnRegex(`^禁言.*?(\d+).*?\s(\d+)(.*)`, zero.OnlyGroup, zero.AdminPermission).SetBlock(true).SetPriority(40).
Handle(func(ctx *zero.Ctx) {
duration := strToInt(ctx.State["regex_matched"].([]string)[2])
switch ctx.State["regex_matched"].([]string)[3] {
@@ -154,7 +165,7 @@ func init() { // 插件主体
ctx.SendChain(message.Text("小黑屋收留成功~"))
})
// 解除禁言
zero.OnRegex(`^解除禁言.*?(\d+)`, zero.OnlyGroup, zero.AdminPermission).SetBlock(true).SetPriority(40).
engine.OnRegex(`^解除禁言.*?(\d+)`, zero.OnlyGroup, zero.AdminPermission).SetBlock(true).SetPriority(40).
Handle(func(ctx *zero.Ctx) {
ctx.SetGroupBan(
ctx.Event.GroupID,
@@ -164,18 +175,18 @@ func init() { // 插件主体
ctx.SendChain(message.Text("小黑屋释放成功~"))
})
// 自闭禁言
zero.OnRegex(`^我要自闭.*?(\d+)(.*)`, zero.OnlyGroup).SetBlock(true).SetPriority(40).
engine.OnRegex(`^(我要自闭|禅定).*?(\d+)(.*)`, zero.OnlyGroup).SetBlock(true).SetPriority(40).
Handle(func(ctx *zero.Ctx) {
duration := strToInt(ctx.State["regex_matched"].([]string)[1])
switch ctx.State["regex_matched"].([]string)[2] {
case "分钟":
//
case "小时":
duration := strToInt(ctx.State["regex_matched"].([]string)[2])
switch ctx.State["regex_matched"].([]string)[3] {
case "分钟", "min", "mins", "m":
break
case "小时", "hour", "hours", "h":
duration *= 60
case "天":
case "天", "day", "days", "d":
duration *= 60 * 24
default:
//
break
}
if duration >= 43200 {
duration = 43199 // qq禁言最大时长为一个月
@@ -188,7 +199,7 @@ func init() { // 插件主体
ctx.SendChain(message.Text("那我就不手下留情了~"))
})
// 修改名片
zero.OnRegex(`^修改名片.*?(\d+).*?\s(.*)`, zero.OnlyGroup, zero.AdminPermission).SetBlock(true).SetPriority(40).
engine.OnRegex(`^修改名片.*?(\d+).*?\s(.*)`, zero.OnlyGroup, zero.AdminPermission).SetBlock(true).SetPriority(40).
Handle(func(ctx *zero.Ctx) {
ctx.SetGroupCard(
ctx.Event.GroupID,
@@ -198,7 +209,7 @@ func init() { // 插件主体
ctx.SendChain(message.Text("嗯!已经修改了"))
})
// 修改头衔
zero.OnRegex(`^修改头衔.*?(\d+).*?\s(.*)`, zero.OnlyGroup, zero.AdminPermission).SetBlock(true).SetPriority(40).
engine.OnRegex(`^修改头衔.*?(\d+).*?\s(.*)`, zero.OnlyGroup, zero.AdminPermission).SetBlock(true).SetPriority(40).
Handle(func(ctx *zero.Ctx) {
ctx.SetGroupSpecialTitle(
ctx.Event.GroupID,
@@ -208,7 +219,7 @@ func init() { // 插件主体
ctx.SendChain(message.Text("嗯!已经修改了"))
})
// 申请头衔
zero.OnRegex(`^申请头衔(.*)`, zero.OnlyGroup).SetBlock(true).SetPriority(40).
engine.OnRegex(`^申请头衔(.*)`, zero.OnlyGroup).SetBlock(true).SetPriority(40).
Handle(func(ctx *zero.Ctx) {
ctx.SetGroupSpecialTitle(
ctx.Event.GroupID,
@@ -218,7 +229,7 @@ func init() { // 插件主体
ctx.SendChain(message.Text("嗯!不错的头衔呢~"))
})
// 群聊转发
zero.OnRegex(`^群聊转发.*?(\d+)\s(.*)`, zero.SuperUserPermission).SetBlock(true).SetPriority(40).
engine.OnRegex(`^群聊转发.*?(\d+)\s(.*)`, zero.SuperUserPermission).SetBlock(true).SetPriority(40).
Handle(func(ctx *zero.Ctx) {
// 对CQ码进行反转义
content := ctx.State["regex_matched"].([]string)[2]
@@ -231,7 +242,7 @@ func init() { // 插件主体
ctx.SendChain(message.Text("📧 --> " + ctx.State["regex_matched"].([]string)[1]))
})
// 私聊转发
zero.OnRegex(`^私聊转发.*?(\d+)\s(.*)`, zero.SuperUserPermission).SetBlock(true).SetPriority(40).
engine.OnRegex(`^私聊转发.*?(\d+)\s(.*)`, zero.SuperUserPermission).SetBlock(true).SetPriority(40).
Handle(func(ctx *zero.Ctx) {
// 对CQ码进行反转义
content := ctx.State["regex_matched"].([]string)[2]
@@ -244,48 +255,73 @@ func init() { // 插件主体
ctx.SendChain(message.Text("📧 --> " + ctx.State["regex_matched"].([]string)[1]))
})
// 定时提醒
zero.OnRegex(`^在(.{1,2})月(.{1,3}日|每?周.?)的(.{1,3})点(.{1,3})分时(用.+)?提醒大家(.*)`, zero.AdminPermission).SetBlock(true).SetPriority(40).
engine.OnRegex(`^在(.{1,2})月(.{1,3}日|每?周.?)的(.{1,3})点(.{1,3})分时(用.+)?提醒大家(.*)`, zero.AdminPermission, zero.OnlyGroup).SetBlock(true).SetPriority(40).
Handle(func(ctx *zero.Ctx) {
if ctx.Event.GroupID > 0 {
dateStrs := ctx.State["regex_matched"].([]string)
ts := timer.GetFilledTimeStamp(dateStrs, false)
ts.Grpid = uint64(ctx.Event.GroupID)
if ts.Enable {
go timer.RegisterTimer(ts, true)
ctx.Send("记住了~")
} else {
ctx.Send("参数非法!")
}
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)
ctx.SendChain(message.Text("记住了~"))
} else {
ctx.SendChain(message.Text("参数非法:" + ts.Alert))
}
})
// 定时 cron 提醒
engine.OnRegex(`^在"(.*)"时(用.+)?提醒大家(.*)`, zero.AdminPermission, zero.OnlyGroup).SetBlock(true).SetPriority(40).
Handle(func(ctx *zero.Ctx) {
dateStrs := ctx.State["regex_matched"].([]string)
var url, alert string
switch len(dateStrs) {
case 4:
url = dateStrs[2]
alert = dateStrs[3]
case 3:
alert = dateStrs[2]
default:
ctx.SendChain(message.Text("参数非法!"))
return
}
logrus.Debugln("[manager] cron:", dateStrs[1])
ts := timer.GetFilledCronTimer(dateStrs[1], alert, url, ctx.Event.SelfID, ctx.Event.GroupID)
if clock.RegisterTimer(ts, true) {
ctx.SendChain(message.Text("记住了~"))
} else {
ctx.SendChain(message.Text("参数非法:" + ts.Alert))
}
})
// 取消定时
zero.OnRegex(`^取消在(.{1,2})月(.{1,3}日|每?周.?)的(.{1,3})点(.{1,3})分的提醒`, zero.AdminPermission).SetBlock(true).SetPriority(40).
engine.OnRegex(`^取消在(.{1,2})月(.{1,3}日|每?周.?)的(.{1,3})点(.{1,3})分的提醒`, zero.AdminPermission, zero.OnlyGroup).SetBlock(true).SetPriority(40).
Handle(func(ctx *zero.Ctx) {
if ctx.Event.GroupID > 0 {
dateStrs := ctx.State["regex_matched"].([]string)
ts := timer.GetFilledTimeStamp(dateStrs, true)
ts.Grpid = uint64(ctx.Event.GroupID)
ti := timer.GetTimerInfo(ts)
t, ok := (*timer.Timers)[ti]
if ok {
t.Enable = false
delete(*timer.Timers, ti) // 避免重复取消
_ = timer.SaveTimers()
ctx.Send("取消成功~")
} else {
ctx.Send("没有这个定时器哦~")
}
dateStrs := ctx.State["regex_matched"].([]string)
ts := timer.GetFilledTimer(dateStrs, ctx.Event.SelfID, ctx.Event.GroupID, true)
ti := ts.GetTimerID()
ok := clock.CancelTimer(ti)
if ok {
ctx.SendChain(message.Text("取消成功~"))
} else {
ctx.SendChain(message.Text("没有这个定时器哦~"))
}
})
// 取消 cron 定时
engine.OnRegex(`^取消在"(.*)"的提醒`, zero.AdminPermission, zero.OnlyGroup).SetBlock(true).SetPriority(40).
Handle(func(ctx *zero.Ctx) {
dateStrs := ctx.State["regex_matched"].([]string)
ts := timer.Timer{Cron: dateStrs[1], GrpId: ctx.Event.GroupID}
ti := ts.GetTimerID()
ok := clock.CancelTimer(ti)
if ok {
ctx.SendChain(message.Text("取消成功~"))
} else {
ctx.SendChain(message.Text("没有这个定时器哦~"))
}
})
// 列出本群所有定时
zero.OnFullMatch("列出所有提醒", zero.AdminPermission).SetBlock(true).SetPriority(40).
engine.OnFullMatch("列出所有提醒", zero.AdminPermission, zero.OnlyGroup).SetBlock(true).SetPriority(40).
Handle(func(ctx *zero.Ctx) {
if ctx.Event.GroupID > 0 {
ctx.Send(fmt.Sprint(timer.ListTimers(uint64(ctx.Event.GroupID))))
}
ctx.SendChain(message.Text(clock.ListTimers(ctx.Event.GroupID)))
})
// 随机点名
zero.OnFullMatchGroup([]string{"翻牌"}, zero.OnlyGroup).SetBlock(true).SetPriority(40).
engine.OnFullMatchGroup([]string{"翻牌"}, zero.OnlyGroup).SetBlock(true).SetPriority(40).
Handle(func(ctx *zero.Ctx) {
if !limit.Load(ctx.Event.UserID).Acquire() {
ctx.SendChain(message.Text("少女祈祷中......"))
@@ -300,13 +336,7 @@ func init() { // 插件主体
sort.SliceStable(temp, func(i, j int) bool {
return temp[i].Get("last_sent_time").Int() < temp[j].Get("last_sent_time").Int()
})
max := func(a, b int) int {
if a > b {
return a
}
return b
}
temp = temp[max(0, len(temp)-10):]
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 {
@@ -329,140 +359,170 @@ func init() { // 插件主体
)
})
// 入群欢迎
zero.OnNotice().SetBlock(false).FirstPriority().
engine.OnNotice().SetBlock(false).FirstPriority().
Handle(func(ctx *zero.Ctx) {
if ctx.Event.NoticeType == "group_increase" {
word, ok := config.Welcome[uint64(ctx.Event.GroupID)]
if ok {
ctx.Send(word)
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))
} else {
ctx.Send("欢迎~")
ctx.SendChain(message.Text("欢迎~"))
}
enable, ok1 := config.Checkin[uint64(ctx.Event.GroupID)]
if ok1 && enable {
uid := ctx.Event.UserID
a := rand.Intn(100)
b := rand.Intn(100)
r := a + b
ctx.SendChain(message.At(uid), message.Text(fmt.Sprintf("考你一道题:%d+%d=?\n如果60秒之内答不上来%s就要把你踢出去了哦~", a, b, zero.BotConfig.NickName[0])))
// 匹配发送者进行验证
rule := func(ctx *zero.Ctx) bool {
for _, elem := range ctx.Event.Message {
if elem.Type == "text" {
text := strings.ReplaceAll(elem.Data["text"], " ", "")
ans, err := strconv.Atoi(text)
if err == nil {
if ans != r {
ctx.Send("答案不对哦,再想想吧~")
return false
c, ok := control.Lookup("manager")
if ok {
enable := c.GetData(ctx.Event.GroupID)&1 == 1
if enable {
uid := ctx.Event.UserID
a := rand.Intn(100)
b := rand.Intn(100)
r := a + b
ctx.SendChain(message.At(uid), message.Text(fmt.Sprintf("考你一道题:%d+%d=?\n如果60秒之内答不上来%s就要把你踢出去了哦~", a, b, zero.BotConfig.NickName[0])))
// 匹配发送者进行验证
rule := func(ctx *zero.Ctx) bool {
for _, elem := range ctx.Event.Message {
if elem.Type == "text" {
text := strings.ReplaceAll(elem.Data["text"], " ", "")
ans, err := strconv.Atoi(text)
if err == nil {
if ans != r {
ctx.SendChain(message.Text("答案不对哦,再想想吧~"))
return false
}
return true
}
return true
}
}
return false
}
next := zero.NewFutureEvent("message", 999, false, zero.CheckUser(ctx.Event.UserID), rule)
recv, cancel := next.Repeat()
select {
case <-time.After(time.Minute):
ctx.SendChain(message.Text("拜拜啦~"))
ctx.SetGroupKick(ctx.Event.GroupID, uid, false)
cancel()
case <-recv:
cancel()
ctx.SendChain(message.Text("答对啦~"))
}
return false
}
next := zero.NewFutureEvent("message", 999, false, zero.CheckUser(ctx.Event.UserID), rule)
recv, cancel := next.Repeat()
select {
case <-time.After(time.Minute):
ctx.Send("拜拜啦~")
ctx.SetGroupKick(ctx.Event.GroupID, uid, false)
cancel()
case <-recv:
cancel()
ctx.Send("答对啦~")
}
}
}
})
// 退群提醒
zero.OnNotice().SetBlock(false).SetPriority(40).
engine.OnNotice().SetBlock(false).SetPriority(40).
Handle(func(ctx *zero.Ctx) {
if ctx.Event.NoticeType == "group_decrease" {
ctx.SendChain(message.Text("有人跑路了~"))
userid := ctx.Event.UserID
nickname := ctx.GetStrangerInfo(userid, false).Get("nickname").String()
ctx.SendChain(message.Text(nickname, "(", userid, ")", "离开了我们..."))
}
})
// 设置欢迎语
zero.OnRegex(`^设置欢迎语([\s\S]*)$`, zero.OnlyGroup, zero.AdminPermission).SetBlock(true).SetPriority(40).
engine.OnRegex(`^设置欢迎语([\s\S]*)$`, zero.OnlyGroup, zero.AdminPermission).SetBlock(true).SetPriority(40).
Handle(func(ctx *zero.Ctx) {
config.Welcome[uint64(ctx.Event.GroupID)] = ctx.State["regex_matched"].([]string)[1]
if saveConfig() == nil {
ctx.Send("记住啦!")
w := &Welcome{
GrpID: ctx.Event.GroupID,
Msg: ctx.State["regex_matched"].([]string)[1],
}
err := db.Insert("welcome", w)
if err == nil {
ctx.SendChain(message.Text("记住啦!"))
} else {
ctx.Send("出错啦!")
ctx.SendChain(message.Text("出错啦: ", err))
}
})
// 入群验证开关
zero.OnRegex(`^(.*)入群验证$`, zero.OnlyGroup, zero.AdminPermission).SetBlock(true).SetPriority(40).
// 入群验证开关
engine.OnRegex(`^(.*)入群验证$`, zero.OnlyGroup, zero.AdminPermission).SetBlock(true).SetPriority(40).
Handle(func(ctx *zero.Ctx) {
option := ctx.State["regex_matched"].([]string)[1]
switch option {
case "开启":
config.Checkin[uint64(ctx.Event.GroupID)] = true
case "关闭":
config.Checkin[uint64(ctx.Event.GroupID)] = false
default:
c, ok := control.Lookup("manager")
if ok {
data := c.GetData(ctx.Event.GroupID)
switch option {
case "开启", "打开", "启用":
data |= 1
case "关闭", "关掉", "禁用":
data &= 0x7fffffff_fffffffe
default:
return
}
err := c.SetData(ctx.Event.GroupID, data)
if err == nil {
ctx.SendChain(message.Text("已", option))
return
}
ctx.SendChain(message.Text("出错啦: ", err))
return
}
if saveConfig() == nil {
ctx.Send("已" + option)
} else {
ctx.Send("出错啦!")
ctx.SendChain(message.Text("找不到服务!"))
})
// 加群 gist 验证开关
engine.OnRegex(`^(.*)gist加群自动审批$`, zero.OnlyGroup, zero.AdminPermission).SetBlock(true).SetPriority(40).
Handle(func(ctx *zero.Ctx) {
option := ctx.State["regex_matched"].([]string)[1]
c, ok := control.Lookup("manager")
if ok {
data := c.GetData(ctx.Event.GroupID)
switch option {
case "开启", "打开", "启用":
data |= 0x10
case "关闭", "关掉", "禁用":
data &= 0x7fffffff_fffffffd
default:
return
}
err := c.SetData(ctx.Event.GroupID, data)
if err == nil {
ctx.SendChain(message.Text("已", option))
return
}
ctx.SendChain(message.Text("出错啦: ", err))
return
}
ctx.SendChain(message.Text("找不到服务!"))
})
// 运行 CQ 码
zero.OnRegex(`^run(.*)$`, zero.SuperUserPermission).SetBlock(true).SetPriority(0).
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) {
/*if ctx.Event.RequestType == "friend" {
ctx.SetFriendAddRequest(ctx.Event.Flag, true, "")
}*/
c, ok := control.Lookup("manager")
if ok && c.GetData(ctx.Event.GroupID)&0x10 == 0x10 && ctx.Event.RequestType == "group" && ctx.Event.SubType == "add" {
// gist 文件名是群号的 ascii 编码的 md5
// gist 内容是当前 uinx 时间戳,在 10 分钟内视为有效
ans := ctx.Event.Comment[strings.Index(ctx.Event.Comment, "答案:")+len("答案:"):]
divi := strings.Index(ans, "/")
if divi <= 0 {
ctx.SetGroupAddRequest(ctx.Event.Flag, "add", false, "格式错误!")
return
}
ghun := ans[:divi]
hash := ans[divi+1:]
logrus.Infoln("[manager]收到加群申请, 用户:", ghun, ", hash:", hash)
ok, reason := checkNewUser(ctx.Event.UserID, ctx.Event.GroupID, ghun, hash)
if ok {
ctx.SetGroupAddRequest(ctx.Event.Flag, "add", true, "")
} else {
ctx.SetGroupAddRequest(ctx.Event.Flag, "add", false, reason)
}
}
})
}
func strToInt(str string) int64 {
val, _ := strconv.ParseInt(str, 10, 64)
return val
}
// loadConfig 加载设置,没有则手动初始化
func loadConfig() {
mkdirerr := os.MkdirAll(datapath, 0755)
if mkdirerr == nil {
if _, err := os.Stat(confile); err == nil || os.IsExist(err) {
f, err := os.Open(confile)
if err == nil {
data, err1 := io.ReadAll(f)
if err1 == nil {
if len(data) > 0 {
if config.Unmarshal(data) == nil {
return
}
}
}
}
}
config.Checkin = make(map[uint64]bool)
config.Welcome = make(map[uint64]string)
} else {
panic(mkdirerr)
}
}
// saveConfig 保存设置,无此文件则新建
func saveConfig() error {
data, err := config.Marshal()
if err != nil {
return err
} else if _, err := os.Stat(datapath); err == nil || os.IsExist(err) {
f, err1 := os.OpenFile(confile, os.O_WRONLY|os.O_TRUNC|os.O_CREATE, 0644)
if err1 != nil {
return err1
}
defer f.Close()
_, err2 := f.Write(data)
return err2
}
return nil
}

View File

@@ -1,561 +0,0 @@
// Code generated by protoc-gen-gogo. DO NOT EDIT.
// source: manager.proto
package manager
import (
fmt "fmt"
proto "github.com/golang/protobuf/proto"
io "io"
math "math"
math_bits "math/bits"
)
// Reference imports to suppress errors if they are not otherwise used.
var _ = proto.Marshal
var _ = fmt.Errorf
var _ = math.Inf
// This is a compile-time assertion to ensure that this generated file
// is compatible with the proto package it is being compiled against.
// A compilation error at this line likely means your copy of the
// proto package needs to be updated.
const _ = proto.ProtoPackageIsVersion3 // please upgrade the proto package
type Config struct {
Checkin map[uint64]bool `protobuf:"bytes,1,rep,name=checkin,proto3" json:"checkin,omitempty" protobuf_key:"varint,1,opt,name=key,proto3" protobuf_val:"varint,2,opt,name=value,proto3"`
Welcome map[uint64]string `protobuf:"bytes,2,rep,name=welcome,proto3" json:"welcome,omitempty" protobuf_key:"varint,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
func (m *Config) Reset() { *m = Config{} }
func (m *Config) String() string { return proto.CompactTextString(m) }
func (*Config) ProtoMessage() {}
func (*Config) Descriptor() ([]byte, []int) {
return fileDescriptor_cde9ec64f0d2c859, []int{0}
}
func (m *Config) XXX_Unmarshal(b []byte) error {
return m.Unmarshal(b)
}
func (m *Config) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
if deterministic {
return xxx_messageInfo_Config.Marshal(b, m, deterministic)
} else {
b = b[:cap(b)]
n, err := m.MarshalToSizedBuffer(b)
if err != nil {
return nil, err
}
return b[:n], nil
}
}
func (m *Config) XXX_Merge(src proto.Message) {
xxx_messageInfo_Config.Merge(m, src)
}
func (m *Config) XXX_Size() int {
return m.Size()
}
func (m *Config) XXX_DiscardUnknown() {
xxx_messageInfo_Config.DiscardUnknown(m)
}
var xxx_messageInfo_Config proto.InternalMessageInfo
func (m *Config) GetCheckin() map[uint64]bool {
if m != nil {
return m.Checkin
}
return nil
}
func (m *Config) GetWelcome() map[uint64]string {
if m != nil {
return m.Welcome
}
return nil
}
func init() {
proto.RegisterType((*Config)(nil), "manager.config")
proto.RegisterMapType((map[uint64]bool)(nil), "manager.config.CheckinEntry")
proto.RegisterMapType((map[uint64]string)(nil), "manager.config.WelcomeEntry")
}
func init() { proto.RegisterFile("manager.proto", fileDescriptor_cde9ec64f0d2c859) }
var fileDescriptor_cde9ec64f0d2c859 = []byte{
// 186 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0xe2, 0xcd, 0x4d, 0xcc, 0x4b,
0x4c, 0x4f, 0x2d, 0xd2, 0x2b, 0x28, 0xca, 0x2f, 0xc9, 0x17, 0x62, 0x87, 0x72, 0x95, 0x3e, 0x30,
0x72, 0xb1, 0x25, 0xe7, 0xe7, 0xa5, 0x65, 0xa6, 0x0b, 0x99, 0x71, 0xb1, 0x27, 0x67, 0xa4, 0x26,
0x67, 0x67, 0xe6, 0x49, 0x30, 0x2a, 0x30, 0x6b, 0x70, 0x1b, 0xc9, 0xe8, 0xc1, 0x34, 0x41, 0x54,
0xe8, 0x39, 0x43, 0xa4, 0x5d, 0xf3, 0x4a, 0x8a, 0x2a, 0x83, 0x60, 0x8a, 0x41, 0xfa, 0xca, 0x53,
0x73, 0x92, 0xf3, 0x73, 0x53, 0x25, 0x98, 0xb0, 0xeb, 0x0b, 0x87, 0x48, 0x43, 0xf5, 0x41, 0x15,
0x4b, 0x59, 0x71, 0xf1, 0x20, 0x1b, 0x28, 0x24, 0xc0, 0xc5, 0x9c, 0x9d, 0x5a, 0x29, 0xc1, 0xa8,
0xc0, 0xa8, 0xc1, 0x12, 0x04, 0x62, 0x0a, 0x89, 0x70, 0xb1, 0x96, 0x25, 0xe6, 0x94, 0x82, 0xcc,
0x65, 0xd4, 0xe0, 0x08, 0x82, 0x70, 0xac, 0x98, 0x2c, 0x18, 0x41, 0x7a, 0x91, 0x0d, 0x25, 0xa4,
0x97, 0x13, 0x49, 0xaf, 0x93, 0xc0, 0x89, 0x47, 0x72, 0x8c, 0x17, 0x1e, 0xc9, 0x31, 0x3e, 0x78,
0x24, 0xc7, 0x38, 0xe3, 0xb1, 0x1c, 0x43, 0x12, 0x1b, 0x38, 0x50, 0x8c, 0x01, 0x01, 0x00, 0x00,
0xff, 0xff, 0x2a, 0xe6, 0x90, 0x6e, 0x25, 0x01, 0x00, 0x00,
}
func (m *Config) Marshal() (dAtA []byte, err error) {
size := m.Size()
dAtA = make([]byte, size)
n, err := m.MarshalToSizedBuffer(dAtA[:size])
if err != nil {
return nil, err
}
return dAtA[:n], nil
}
func (m *Config) MarshalTo(dAtA []byte) (int, error) {
size := m.Size()
return m.MarshalToSizedBuffer(dAtA[:size])
}
func (m *Config) MarshalToSizedBuffer(dAtA []byte) (int, error) {
i := len(dAtA)
_ = i
var l int
_ = l
if m.XXX_unrecognized != nil {
i -= len(m.XXX_unrecognized)
copy(dAtA[i:], m.XXX_unrecognized)
}
if len(m.Welcome) > 0 {
for k := range m.Welcome {
v := m.Welcome[k]
baseI := i
i -= len(v)
copy(dAtA[i:], v)
i = encodeVarintManager(dAtA, i, uint64(len(v)))
i--
dAtA[i] = 0x12
i = encodeVarintManager(dAtA, i, uint64(k))
i--
dAtA[i] = 0x8
i = encodeVarintManager(dAtA, i, uint64(baseI-i))
i--
dAtA[i] = 0x12
}
}
if len(m.Checkin) > 0 {
for k := range m.Checkin {
v := m.Checkin[k]
baseI := i
i--
if v {
dAtA[i] = 1
} else {
dAtA[i] = 0
}
i--
dAtA[i] = 0x10
i = encodeVarintManager(dAtA, i, uint64(k))
i--
dAtA[i] = 0x8
i = encodeVarintManager(dAtA, i, uint64(baseI-i))
i--
dAtA[i] = 0xa
}
}
return len(dAtA) - i, nil
}
func encodeVarintManager(dAtA []byte, offset int, v uint64) int {
offset -= sovManager(v)
base := offset
for v >= 1<<7 {
dAtA[offset] = uint8(v&0x7f | 0x80)
v >>= 7
offset++
}
dAtA[offset] = uint8(v)
return base
}
func (m *Config) Size() (n int) {
if m == nil {
return 0
}
var l int
_ = l
if len(m.Checkin) > 0 {
for k, v := range m.Checkin {
_ = k
_ = v
mapEntrySize := 1 + sovManager(uint64(k)) + 1 + 1
n += mapEntrySize + 1 + sovManager(uint64(mapEntrySize))
}
}
if len(m.Welcome) > 0 {
for k, v := range m.Welcome {
_ = k
_ = v
mapEntrySize := 1 + sovManager(uint64(k)) + 1 + len(v) + sovManager(uint64(len(v)))
n += mapEntrySize + 1 + sovManager(uint64(mapEntrySize))
}
}
if m.XXX_unrecognized != nil {
n += len(m.XXX_unrecognized)
}
return n
}
func sovManager(x uint64) (n int) {
return (math_bits.Len64(x|1) + 6) / 7
}
func sozManager(x uint64) (n int) {
return sovManager(uint64((x << 1) ^ uint64((int64(x) >> 63))))
}
func (m *Config) Unmarshal(dAtA []byte) error {
l := len(dAtA)
iNdEx := 0
for iNdEx < l {
preIndex := iNdEx
var wire uint64
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowManager
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
wire |= uint64(b&0x7F) << shift
if b < 0x80 {
break
}
}
fieldNum := int32(wire >> 3)
wireType := int(wire & 0x7)
if wireType == 4 {
return fmt.Errorf("proto: config: wiretype end group for non-group")
}
if fieldNum <= 0 {
return fmt.Errorf("proto: config: illegal tag %d (wire type %d)", fieldNum, wire)
}
switch fieldNum {
case 1:
if wireType != 2 {
return fmt.Errorf("proto: wrong wireType = %d for field Checkin", wireType)
}
var msglen int
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowManager
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
msglen |= int(b&0x7F) << shift
if b < 0x80 {
break
}
}
if msglen < 0 {
return ErrInvalidLengthManager
}
postIndex := iNdEx + msglen
if postIndex < 0 {
return ErrInvalidLengthManager
}
if postIndex > l {
return io.ErrUnexpectedEOF
}
if m.Checkin == nil {
m.Checkin = make(map[uint64]bool)
}
var mapkey uint64
var mapvalue bool
for iNdEx < postIndex {
entryPreIndex := iNdEx
var wire uint64
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowManager
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
wire |= uint64(b&0x7F) << shift
if b < 0x80 {
break
}
}
fieldNum := int32(wire >> 3)
if fieldNum == 1 {
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowManager
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
mapkey |= uint64(b&0x7F) << shift
if b < 0x80 {
break
}
}
} else if fieldNum == 2 {
var mapvaluetemp int
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowManager
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
mapvaluetemp |= int(b&0x7F) << shift
if b < 0x80 {
break
}
}
mapvalue = bool(mapvaluetemp != 0)
} else {
iNdEx = entryPreIndex
skippy, err := skipManager(dAtA[iNdEx:])
if err != nil {
return err
}
if (skippy < 0) || (iNdEx+skippy) < 0 {
return ErrInvalidLengthManager
}
if (iNdEx + skippy) > postIndex {
return io.ErrUnexpectedEOF
}
iNdEx += skippy
}
}
m.Checkin[mapkey] = mapvalue
iNdEx = postIndex
case 2:
if wireType != 2 {
return fmt.Errorf("proto: wrong wireType = %d for field Welcome", wireType)
}
var msglen int
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowManager
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
msglen |= int(b&0x7F) << shift
if b < 0x80 {
break
}
}
if msglen < 0 {
return ErrInvalidLengthManager
}
postIndex := iNdEx + msglen
if postIndex < 0 {
return ErrInvalidLengthManager
}
if postIndex > l {
return io.ErrUnexpectedEOF
}
if m.Welcome == nil {
m.Welcome = make(map[uint64]string)
}
var mapkey uint64
var mapvalue string
for iNdEx < postIndex {
entryPreIndex := iNdEx
var wire uint64
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowManager
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
wire |= uint64(b&0x7F) << shift
if b < 0x80 {
break
}
}
fieldNum := int32(wire >> 3)
if fieldNum == 1 {
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowManager
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
mapkey |= uint64(b&0x7F) << shift
if b < 0x80 {
break
}
}
} else if fieldNum == 2 {
var stringLenmapvalue uint64
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowManager
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
stringLenmapvalue |= uint64(b&0x7F) << shift
if b < 0x80 {
break
}
}
intStringLenmapvalue := int(stringLenmapvalue)
if intStringLenmapvalue < 0 {
return ErrInvalidLengthManager
}
postStringIndexmapvalue := iNdEx + intStringLenmapvalue
if postStringIndexmapvalue < 0 {
return ErrInvalidLengthManager
}
if postStringIndexmapvalue > l {
return io.ErrUnexpectedEOF
}
mapvalue = string(dAtA[iNdEx:postStringIndexmapvalue])
iNdEx = postStringIndexmapvalue
} else {
iNdEx = entryPreIndex
skippy, err := skipManager(dAtA[iNdEx:])
if err != nil {
return err
}
if (skippy < 0) || (iNdEx+skippy) < 0 {
return ErrInvalidLengthManager
}
if (iNdEx + skippy) > postIndex {
return io.ErrUnexpectedEOF
}
iNdEx += skippy
}
}
m.Welcome[mapkey] = mapvalue
iNdEx = postIndex
default:
iNdEx = preIndex
skippy, err := skipManager(dAtA[iNdEx:])
if err != nil {
return err
}
if (skippy < 0) || (iNdEx+skippy) < 0 {
return ErrInvalidLengthManager
}
if (iNdEx + skippy) > l {
return io.ErrUnexpectedEOF
}
m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...)
iNdEx += skippy
}
}
if iNdEx > l {
return io.ErrUnexpectedEOF
}
return nil
}
func skipManager(dAtA []byte) (n int, err error) {
l := len(dAtA)
iNdEx := 0
depth := 0
for iNdEx < l {
var wire uint64
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return 0, ErrIntOverflowManager
}
if iNdEx >= l {
return 0, io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
wire |= (uint64(b) & 0x7F) << shift
if b < 0x80 {
break
}
}
wireType := int(wire & 0x7)
switch wireType {
case 0:
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return 0, ErrIntOverflowManager
}
if iNdEx >= l {
return 0, io.ErrUnexpectedEOF
}
iNdEx++
if dAtA[iNdEx-1] < 0x80 {
break
}
}
case 1:
iNdEx += 8
case 2:
var length int
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return 0, ErrIntOverflowManager
}
if iNdEx >= l {
return 0, io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
length |= (int(b) & 0x7F) << shift
if b < 0x80 {
break
}
}
if length < 0 {
return 0, ErrInvalidLengthManager
}
iNdEx += length
case 3:
depth++
case 4:
if depth == 0 {
return 0, ErrUnexpectedEndOfGroupManager
}
depth--
case 5:
iNdEx += 4
default:
return 0, fmt.Errorf("proto: illegal wireType %d", wireType)
}
if iNdEx < 0 {
return 0, ErrInvalidLengthManager
}
if depth == 0 {
return iNdEx, nil
}
}
return 0, io.ErrUnexpectedEOF
}
var (
ErrInvalidLengthManager = fmt.Errorf("proto: negative length found during unmarshaling")
ErrIntOverflowManager = fmt.Errorf("proto: integer overflow")
ErrUnexpectedEndOfGroupManager = fmt.Errorf("proto: unexpected end of group")
)

View File

@@ -1,7 +0,0 @@
syntax = "proto3";
package manager;
message config {
map<uint64, bool> checkin = 1;
map<uint64, string> welcome = 2;
}

View File

@@ -0,0 +1,16 @@
package timer
import (
zero "github.com/wdvxdr1123/ZeroBot"
"github.com/wdvxdr1123/ZeroBot/message"
)
func (ts *Timer) sendmsg(grp int64, ctx *zero.Ctx) {
ctx.Event = new(zero.Event)
ctx.Event.GroupID = grp
if ts.Url == "" {
ctx.SendChain(atall, message.Text(ts.Alert))
} else {
ctx.SendChain(atall, message.Text(ts.Alert), message.Image(ts.Url).Add("cache", "0"))
}
}

View File

@@ -0,0 +1,167 @@
package timer
import (
"crypto/md5"
"encoding/binary"
"fmt"
"strconv"
"strings"
"time"
"unicode"
"github.com/sirupsen/logrus"
"github.com/wdvxdr1123/ZeroBot/utils/helper"
)
// GetTimerInfo 获得标准化定时字符串
func (ts *Timer) GetTimerInfo() string {
if ts.Cron != "" {
return fmt.Sprintf("[%d]%s", ts.GrpId, ts.Cron)
}
return fmt.Sprintf("[%d]%d月%d日%d周%d:%d", ts.GrpId, ts.Month(), ts.Day(), ts.Week(), ts.Hour(), ts.Minute())
}
// GetTimerInfo 获得标准化 ID
func (ts *Timer) GetTimerID() uint32 {
key := ts.GetTimerInfo()
m := md5.Sum(helper.StringToBytes(key))
return binary.LittleEndian.Uint32(m[:4])
}
// GetFilledCronTimer 获得以cron填充好的ts
func GetFilledCronTimer(croncmd string, alert string, img string, botqq, gid int64) *Timer {
var ts Timer
ts.Alert = alert
ts.Cron = croncmd
ts.Url = img
ts.Selfid = botqq
ts.GrpId = gid
return &ts
}
// GetFilledTimer 获得填充好的ts
func GetFilledTimer(dateStrs []string, botqq, grp int64, matchDateOnly bool) *Timer {
monthStr := []rune(dateStrs[1])
dayWeekStr := []rune(dateStrs[2])
hourStr := []rune(dateStrs[3])
minuteStr := []rune(dateStrs[4])
var ts Timer
mon := time.Month(chineseNum2Int(monthStr))
if (mon != -1 && mon <= 0) || mon > 12 { // 月份非法
ts.Alert = "月份非法!"
return &ts
}
ts.SetMonth(mon)
lenOfDW := len(dayWeekStr)
switch {
case lenOfDW == 4: // 包括末尾的"日"
dayWeekStr = []rune{dayWeekStr[0], dayWeekStr[2]} // 去除中间的十
d := chineseNum2Int(dayWeekStr)
if (d != -1 && d <= 0) || d > 31 { // 日期非法
ts.Alert = "日期非法1"
return &ts
}
ts.SetDay(d)
case dayWeekStr[lenOfDW-1] == rune('日'): // xx日
dayWeekStr = dayWeekStr[:lenOfDW-1]
d := chineseNum2Int(dayWeekStr)
if (d != -1 && d <= 0) || d > 31 { // 日期非法
ts.Alert = "日期非法2"
return &ts
}
ts.SetDay(d)
case dayWeekStr[0] == rune('每'): // 每周
ts.SetWeek(-1)
default: // 周x
w := chineseNum2Int(dayWeekStr[1:])
if w == 7 { // 周天是0
w = 0
}
if w < 0 || w > 6 { // 星期非法
ts.Alert = "星期非法!"
return &ts
}
ts.SetWeek(time.Weekday(w))
}
if len(hourStr) == 3 {
hourStr = []rune{hourStr[0], hourStr[2]} // 去除中间的十
}
h := chineseNum2Int(hourStr)
if h < -1 || h > 23 { // 小时非法
ts.Alert = "小时非法!"
return &ts
}
ts.SetHour(h)
if len(minuteStr) == 3 {
minuteStr = []rune{minuteStr[0], minuteStr[2]} // 去除中间的十
}
min := chineseNum2Int(minuteStr)
if min < -1 || min > 59 { // 分钟非法
ts.Alert = "分钟非法!"
return &ts
}
ts.SetMinute(min)
if !matchDateOnly {
urlStr := dateStrs[5]
if urlStr != "" { // 是图片url
ts.Url = urlStr[3:] // utf-8下用为3字节
logrus.Println("[群管]" + ts.Url)
if !strings.HasPrefix(ts.Url, "http") {
ts.Url = "illegal"
logrus.Println("[群管]url非法")
return &ts
}
}
ts.Alert = dateStrs[6]
ts.SetEn(true)
}
ts.Selfid = botqq
ts.GrpId = grp
return &ts
}
// chineseNum2Int 汉字数字转int仅支持-1099最多两位数其中"每"解释为-1"每二"为-2以此类推
func chineseNum2Int(rs []rune) int {
r := -1
l := len(rs)
mai := rune('每')
if unicode.IsDigit(rs[0]) { // 默认可能存在的第二位也为int
r, _ = strconv.Atoi(string(rs))
} else {
switch {
case rs[0] == mai:
if l == 2 {
r = -chineseChar2Int(rs[1])
}
case l == 1:
r = chineseChar2Int(rs[0])
default:
ten := chineseChar2Int(rs[0])
if ten != 10 {
ten *= 10
}
ge := chineseChar2Int(rs[1])
if ge == 10 {
ge = 0
}
r = ten + ge
}
}
return r
}
// chineseChar2Int 处理单个字符的映射0~10
func chineseChar2Int(c rune) int {
if c == rune('日') || c == rune('天') { // 周日/周天
return 7
} else {
match := []rune("零一二三四五六七八九十")
for i, m := range match {
if c == m {
return i
}
}
return 0
}
}

View File

@@ -0,0 +1,165 @@
package timer
import (
"time"
"github.com/sirupsen/logrus"
zero "github.com/wdvxdr1123/ZeroBot"
)
func firstWeek(date *time.Time, week time.Weekday) (d time.Time) {
d = date.AddDate(0, 0, 1-date.Day())
for d.Weekday() != week {
d = d.AddDate(0, 0, 1)
}
return
}
func (ts *Timer) nextWakeTime() (date time.Time) {
date = time.Now()
m := ts.Month()
d := ts.Day()
h := ts.Hour()
mn := ts.Minute()
w := ts.Week()
var unit time.Duration
logrus.Debugln("[timer] unit init:", unit)
if mn >= 0 {
switch {
case h < 0:
if unit <= time.Second {
unit = time.Hour
}
case d < 0 || w < 0:
if unit <= time.Second {
unit = time.Hour * 24
}
case d == 0 && w >= 0:
delta := time.Hour * 24 * time.Duration(int(w)-int(date.Weekday()))
if delta < 0 {
delta = time.Hour * 24 * 7
}
unit += delta
case m < 0:
unit = -1
}
} else {
unit = time.Minute
}
logrus.Debugln("[timer] unit:", unit)
stable := 0
if mn < 0 {
mn = date.Minute()
}
if h < 0 {
h = date.Hour()
} else {
stable |= 0x8
}
switch {
case d < 0:
d = date.Day()
case d > 0:
stable |= 0x4
default:
d = date.Day()
if w >= 0 {
stable |= 0x2
}
}
if m < 0 {
m = date.Month()
} else {
stable |= 0x1
}
switch stable {
case 0b0101:
if ts.Day() != time.Now().Day() || ts.Month() != time.Now().Month() {
h = 0
}
case 0b1001:
if ts.Month() != time.Now().Month() {
d = 0
}
case 0b0001:
if ts.Month() != time.Now().Month() {
d = 0
h = 0
}
}
logrus.Debugln("[timer] stable:", stable)
logrus.Debugln("[timer] m:", m, "d:", d, "h:", h, "mn:", mn, "w:", w)
date = time.Date(date.Year(), time.Month(m), int(d), int(h), int(mn), date.Second(), date.Nanosecond(), date.Location())
logrus.Debugln("[timer] date original:", date)
if unit > 0 {
date = date.Add(unit)
}
logrus.Debugln("[timer] date after add:", date)
if time.Until(date) <= 0 {
if ts.Month() < 0 {
if ts.Day() > 0 || (ts.Day() == 0 && ts.Week() >= 0) {
date = date.AddDate(0, 1, 0)
} else if ts.Day() < 0 || ts.Week() < 0 {
if ts.Hour() > 0 {
date = date.AddDate(0, 0, 1)
} else if ts.Minute() > 0 {
date = date.Add(time.Hour)
}
}
} else {
date = date.AddDate(1, 0, 0)
}
}
logrus.Debugln("[timer] date after fix:", date)
if stable&0x8 != 0 && date.Hour() != int(h) {
switch {
case stable&0x4 == 0:
date = date.AddDate(0, 0, 1).Add(-time.Hour)
case stable&0x2 == 0:
date = date.AddDate(0, 0, 7).Add(-time.Hour)
case stable*0x1 == 0:
date = date.AddDate(0, 1, 0).Add(-time.Hour)
default:
date = date.AddDate(1, 0, 0).Add(-time.Hour)
}
}
logrus.Debugln("[timer] date after s8:", date)
if stable&0x4 != 0 && date.Day() != int(d) {
switch {
case stable*0x1 == 0:
date = date.AddDate(0, 1, -1)
default:
date = date.AddDate(1, 0, -1)
}
}
logrus.Debugln("[timer] date after s4:", date)
if stable&0x2 != 0 && date.Weekday() != w {
switch {
case stable*0x1 == 0:
date = date.AddDate(0, 1, 0)
default:
date = date.AddDate(1, 0, 0)
}
date = firstWeek(&date, time.Weekday(w))
}
logrus.Debugln("[timer] date after s2:", date)
if time.Until(date) <= 0 {
date = time.Now().Add(time.Minute)
}
return date
}
func (ts *Timer) judgeHM() {
if ts.Hour() < 0 || ts.Hour() == time.Now().Hour() {
if ts.Minute() < 0 || ts.Minute() == time.Now().Minute() {
if ts.Selfid != 0 {
ts.sendmsg(ts.GrpId, zero.GetBot(ts.Selfid))
} else {
zero.RangeBot(func(id int64, ctx *zero.Ctx) (_ bool) {
ts.sendmsg(ts.GrpId, ctx)
return
})
}
}
}
}

View File

@@ -0,0 +1,26 @@
package timer
import (
"strconv"
"github.com/FloatTech/ZeroBot-Plugin/utils/sql"
)
type Timer struct {
Id uint32 `db:"id"`
En1Month4Day5Week3Hour5Min6 int32 `db:"emdwhm"`
Selfid int64 `db:"sid"`
GrpId int64 `db:"gid"`
Alert string `db:"alert"`
Cron string `db:"cron"`
Url string `db:"url"`
}
func (t *Timer) InsertInto(db *sql.Sqlite) error {
return db.Insert("timer", t)
}
func getTimerFrom(db *sql.Sqlite, id uint32) (t Timer, err error) {
err = db.Find("timer", &t, "where id = "+strconv.Itoa(int(id)))
return
}

View File

@@ -0,0 +1,183 @@
// Package timer 群管定时器
package timer
import (
"strconv"
"strings"
"sync"
"time"
"github.com/fumiama/cron"
"github.com/sirupsen/logrus"
zero "github.com/wdvxdr1123/ZeroBot"
"github.com/wdvxdr1123/ZeroBot/message"
"github.com/FloatTech/ZeroBot-Plugin/utils/file"
"github.com/FloatTech/ZeroBot-Plugin/utils/sql"
)
type Clock struct {
db *sql.Sqlite
timers *(map[uint32]*Timer)
timersmu sync.RWMutex
// cron 定时器
cron *cron.Cron
// entries key <-> cron
entries map[uint32]cron.EntryID
entmu sync.Mutex
}
var (
// @全体成员
atall = message.MessageSegment{
Type: "at",
Data: map[string]string{
"qq": "all",
},
}
)
func NewClock(db *sql.Sqlite) (c Clock) {
c.loadTimers(db)
c.cron = cron.New()
c.entries = make(map[uint32]cron.EntryID)
c.cron.Start()
return
}
// RegisterTimer 注册计时器
func (c *Clock) RegisterTimer(ts *Timer, save bool) bool {
var key uint32
if save {
key = ts.GetTimerID()
ts.Id = key
} else {
key = ts.Id
}
t, ok := c.GetTimer(key)
if t != ts && ok { // 避免重复注册定时器
t.SetEn(false)
}
logrus.Println("[群管]注册计时器", key)
if ts.Cron != "" {
var ctx *zero.Ctx
if ts.Selfid != 0 {
ctx = zero.GetBot(ts.Selfid)
} else {
zero.RangeBot(func(id int64, c *zero.Ctx) bool {
ctx = c
ts.Selfid = id
return false
})
}
eid, err := c.cron.AddFunc(ts.Cron, func() { ts.sendmsg(ts.GrpId, ctx) })
if err == nil {
c.entmu.Lock()
c.entries[key] = eid
c.entmu.Unlock()
if save {
err = c.AddTimer(ts)
}
return err == nil
}
ts.Alert = err.Error()
} else {
if save {
_ = c.AddTimer(ts)
}
for ts.En() {
nextdate := ts.nextWakeTime()
sleepsec := time.Until(nextdate)
logrus.Printf("[群管]计时器%08x将睡眠%ds", key, sleepsec/time.Second)
time.Sleep(sleepsec)
if ts.En() {
if ts.Month() < 0 || ts.Month() == time.Now().Month() {
if ts.Day() < 0 || ts.Day() == time.Now().Day() {
ts.judgeHM()
} else if ts.Day() == 0 {
if ts.Week() < 0 || ts.Week() == time.Now().Weekday() {
ts.judgeHM()
}
}
}
}
}
}
return false
}
// CancelTimer 取消计时器
func (c *Clock) CancelTimer(key uint32) bool {
t, ok := c.GetTimer(key)
if ok {
if t.Cron != "" {
c.entmu.Lock()
e := c.entries[key]
c.cron.Remove(e)
delete(c.entries, key)
c.entmu.Unlock()
} else {
t.SetEn(false)
}
c.timersmu.Lock()
delete(*c.timers, key) // 避免重复取消
e := c.db.Del("timer", "where id = "+strconv.Itoa(int(key)))
c.timersmu.Unlock()
return e == nil
}
return false
}
// ListTimers 列出本群所有计时器
func (c *Clock) ListTimers(grpID int64) []string {
// 数组默认长度为map长度,后面append时,不需要重新申请内存和拷贝,效率很高
if c.timers != nil {
c.timersmu.RLock()
keys := make([]string, 0, len(*c.timers))
for _, v := range *c.timers {
if v.GrpId == grpID {
k := v.GetTimerInfo()
start := strings.Index(k, "]")
msg := strings.ReplaceAll(k[start+1:]+"\n", "-1", "每")
msg = strings.ReplaceAll(msg, "月0日0周", "月周天")
msg = strings.ReplaceAll(msg, "月0日", "月")
msg = strings.ReplaceAll(msg, "日0周", "日")
keys = append(keys, msg)
}
}
c.timersmu.RUnlock()
return keys
} else {
return nil
}
}
func (c *Clock) GetTimer(key uint32) (t *Timer, ok bool) {
c.timersmu.RLock()
t, ok = (*c.timers)[key]
c.timersmu.RUnlock()
return
}
func (c *Clock) AddTimer(t *Timer) (err error) {
c.timersmu.Lock()
(*c.timers)[t.Id] = t
err = c.db.Insert("timer", t)
c.timersmu.Unlock()
return
}
func (c *Clock) loadTimers(db *sql.Sqlite) {
if file.IsExist(db.DBPath) {
c.db = db
err := c.db.Create("timer", &Timer{})
if err == nil {
var t Timer
c.db.FindFor("timer", &t, "", func() error {
tescape := t
go c.RegisterTimer(&tescape, false)
return nil
})
}
}
}

View File

@@ -0,0 +1,24 @@
package timer
import (
"testing"
"time"
"github.com/sirupsen/logrus"
)
func TestNextWakeTime(t *testing.T) {
logrus.SetLevel(logrus.DebugLevel)
ts := &Timer{}
ts.SetMonth(-1)
ts.SetWeek(6)
ts.SetHour(16)
ts.SetMinute(30)
t1 := time.Until(ts.nextWakeTime())
if t1 < 0 {
t.Log(t1)
t.Fail()
}
t.Log(t1)
t.Fail()
}

View File

@@ -0,0 +1,87 @@
package timer
import "time"
// En isEnabled 1bit
func (m *Timer) En() (en bool) {
return m.En1Month4Day5Week3Hour5Min6&0x800000 != 0
}
// Month 4bits
func (m *Timer) Month() (mon time.Month) {
mon = time.Month((m.En1Month4Day5Week3Hour5Min6 & 0x780000) >> 19)
if mon == 0b1111 {
mon = -1
}
return
}
// Day 5bits
func (m *Timer) Day() (d int) {
d = int((m.En1Month4Day5Week3Hour5Min6 & 0x07c000) >> 14)
if d == 0b11111 {
d = -1
}
return
}
// Week 3bits
func (m *Timer) Week() (w time.Weekday) {
w = time.Weekday((m.En1Month4Day5Week3Hour5Min6 & 0x003800) >> 11)
if w == 0b111 {
w = -1
}
return
}
// Hour 5bits
func (m *Timer) Hour() (h int) {
h = int((m.En1Month4Day5Week3Hour5Min6 & 0x0007c0) >> 6)
if h == 0b11111 {
h = -1
}
return
}
// Minute 6bits
func (m *Timer) Minute() (min int) {
min = int(m.En1Month4Day5Week3Hour5Min6 & 0x00003f)
if min == 0b111111 {
min = -1
}
return
}
// SetEn ...
func (m *Timer) SetEn(en bool) {
if en {
m.En1Month4Day5Week3Hour5Min6 |= 0x800000
} else {
m.En1Month4Day5Week3Hour5Min6 &= 0x7fffff
}
}
// SetMonth ...
func (m *Timer) SetMonth(mon time.Month) {
m.En1Month4Day5Week3Hour5Min6 = ((int32(mon) << 19) & 0x780000) | (m.En1Month4Day5Week3Hour5Min6 & 0x87ffff)
}
// SetDay ...
func (m *Timer) SetDay(d int) {
m.En1Month4Day5Week3Hour5Min6 = ((int32(d) << 14) & 0x07c000) | (m.En1Month4Day5Week3Hour5Min6 & 0xf83fff)
}
// SetWeek ...
func (m *Timer) SetWeek(w time.Weekday) {
m.En1Month4Day5Week3Hour5Min6 = ((int32(w) << 11) & 0x003800) | (m.En1Month4Day5Week3Hour5Min6 & 0xffc7ff)
}
// SetHour ...
func (m *Timer) SetHour(h int) {
m.En1Month4Day5Week3Hour5Min6 = ((int32(h) << 6) & 0x0007c0) | (m.En1Month4Day5Week3Hour5Min6 & 0xfff83f)
}
// SetMinute ...
func (m *Timer) SetMinute(min int) {
m.En1Month4Day5Week3Hour5Min6 = (int32(min) & 0x00003f) | (m.En1Month4Day5Week3Hour5Min6 & 0xffffc0)
}

View File

@@ -19,24 +19,23 @@ import (
const api = "http://your.addr:23333/api/start_server/%s/?apikey=apikey"
var engine *zero.Engine
var engine = control.Register("minecraft", &control.Options{
DisableOnDefault: false,
Help: "minecraft\n" +
"- /mcstart xxx\n" +
"- /mcstop xxx\n" +
"- /mclist servername\n" +
"- https://github.com/Suwings/MCSManager",
})
func init() {
engine = control.Register("minecraft", &control.Options{
DisableOnDefault: false,
Help: "minecraft\n" +
"- /mcstart xxx\n" +
"- /mcstop xxx\n" +
"- /mclist servername\n" +
"- https://github.com/Suwings/MCSManager",
})
engine.OnCommand("mcstart").SetBlock(true).
Handle(func(ctx *zero.Ctx) {
model := extension.CommandModel{}
_ = ctx.Parse(&model)
ctx.SendChain(message.Text("开启服务器: ", model.Args, "....."))
result := start(model.Args)
ctx.Send(result)
ctx.SendChain(message.Text(result))
})
engine.OnCommand("mcstop").SetBlock(true).
Handle(func(ctx *zero.Ctx) {
@@ -44,7 +43,7 @@ func init() {
_ = ctx.Parse(&model)
ctx.SendChain(message.Text("开启服务器: ", model.Args, "....."))
result := stop(model.Args)
ctx.Send(result)
ctx.SendChain(message.Text(result))
})
}

40
plugin_moyu/nowork.go Normal file
View File

@@ -0,0 +1,40 @@
package moyu
import (
"fmt"
"strconv"
"time"
)
type holiday struct {
name string
date time.Time
dur time.Duration
}
// NewHoliday 节日名 天数 年 月 日
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}
}
// 获取两个时间相差
func (h *holiday) String() string {
d := time.Until(h.date)
if d >= 0 {
return "距离" + h.name + "还有: " + strconv.FormatFloat(d.Hours()/24.0, 'f', 2, 64) + "天!"
} else if d+h.dur >= 0 {
return "好好享受 " + h.name + " 假期吧!"
} else {
return "今年 " + h.name + " 假期已过"
}
}
func weekend() string {
t := time.Now().Weekday()
switch t {
case time.Sunday, time.Saturday:
return "好好享受周末吧!"
default:
return fmt.Sprintf("距离周末还有:%d天", 5-t)
}
}

93
plugin_moyu/run.go Normal file
View File

@@ -0,0 +1,93 @@
package moyu
import (
"time"
"github.com/fumiama/cron"
zero "github.com/wdvxdr1123/ZeroBot"
"github.com/wdvxdr1123/ZeroBot/message"
"github.com/FloatTech/ZeroBot-Plugin/control"
)
func init() { // 插件主体
// 定时任务每天10点执行一次
c := cron.New()
_, err := c.AddFunc("0 10 * * *", func() { sendNotice() })
if err == nil {
c.Start()
}
control.Register("moyu", &control.Options{
DisableOnDefault: true,
Help: "moyu\n" +
"- 添加摸鱼提醒\n" +
"- 删除摸鱼提醒",
}).OnFullMatch("删除摸鱼提醒", zero.OnlyGroup, zero.AdminPermission).SetBlock(true).SetPriority(20).
Handle(func(ctx *zero.Ctx) {
m, ok := control.Lookup("moyu")
if ok {
if m.IsEnabledIn(ctx.Event.GroupID) {
m.Disable(ctx.Event.GroupID)
ctx.Send(message.Text("删除成功!"))
} else {
ctx.Send(message.Text("未启用!"))
}
} else {
ctx.Send(message.Text("找不到该服务!"))
}
})
zero.OnFullMatch("添加摸鱼提醒", zero.OnlyGroup, zero.AdminPermission).SetBlock(true).SetPriority(20).
Handle(func(ctx *zero.Ctx) {
m, ok := control.Lookup("moyu")
if ok {
if m.IsEnabledIn(ctx.Event.GroupID) {
ctx.Send(message.Text("已启用!"))
} else {
m.Enable(ctx.Event.GroupID)
ctx.Send(message.Text("添加成功!"))
}
} else {
ctx.Send(message.Text("找不到该服务!"))
}
})
}
// 获取数据拼接消息链并发送
func sendNotice() {
m, ok := control.Lookup("moyu")
if ok {
zero.RangeBot(func(id int64, ctx *zero.Ctx) bool {
for _, g := range ctx.GetGroupList().Array() {
grp := g.Get("group_id").Int()
if m.IsEnabledIn(grp) {
ctx.SendGroupMessage(grp,
[]message.MessageSegment{
message.Text(time.Now().Format("2006-01-02")),
message.Text("上午好,摸鱼人!\n工作再累一定不要忘记摸鱼哦有事没事起身去茶水间去厕所去廊道走走别老在工位上坐着钱是老板的,但命是自己的。\n"),
message.Text(weekend()),
message.Text("\n"),
message.Text(NewHoliday("元旦", 1, 2022, 1, 1)),
message.Text("\n"),
message.Text(NewHoliday("春节", 7, 2022, 1, 31)),
message.Text("\n"),
message.Text(NewHoliday("清明节", 1, 2022, 4, 3)),
message.Text("\n"),
message.Text(NewHoliday("劳动节", 1, 2022, 4, 30)),
message.Text("\n"),
message.Text(NewHoliday("端午节", 1, 2022, 6, 3)),
message.Text("\n"),
message.Text(NewHoliday("中秋节", 1, 2022, 9, 10)),
message.Text("\n"),
message.Text(NewHoliday("国庆节", 7, 2022, 10, 1)),
message.Text("\n"),
message.Text("上班是帮老板赚钱,摸鱼是赚老板的钱!最后,祝愿天下所有摸鱼人,都能愉快的渡过每一天…"),
},
)
}
}
return true
})
}
}

View File

@@ -32,7 +32,7 @@ func init() {
}).OnRegex("^(.{0,2})点歌(.{1,25})$").SetBlock(true).FirstPriority().
Handle(func(ctx *zero.Ctx) {
if !limit.Load(ctx.Event.UserID).Acquire() {
ctx.Send("请稍后重试0x0...")
ctx.SendChain(message.Text("请稍后重试0x0..."))
return
}
// switch 平台

130
plugin_nativesetu/data.go Normal file
View File

@@ -0,0 +1,130 @@
package nativesetu
import (
"bytes"
"image"
"io"
"io/fs"
"os"
"sync"
"github.com/corona10/goimagehash"
"github.com/sirupsen/logrus"
"github.com/wdvxdr1123/ZeroBot/utils/helper"
"golang.org/x/image/webp"
"github.com/FloatTech/ZeroBot-Plugin/utils/file"
"github.com/FloatTech/ZeroBot-Plugin/utils/process"
"github.com/FloatTech/ZeroBot-Plugin/utils/sql"
)
// setuclass holds setus in a folder, which is the class name.
type setuclass struct {
ImgID int64 `db:"imgid"` // ImgID 图片唯一 id (dhash)
Name string `db:"name"` // Name 图片名
Path string `db:"path"` // Path 图片路径
}
var (
setuclasses []string
db = &sql.Sqlite{DBPath: dbfile}
mu sync.RWMutex
)
func init() {
go func() {
process.SleepAbout1sTo2s()
err := os.MkdirAll(datapath, 0755)
if err != nil {
panic(err)
}
if file.IsExist(cfgfile) {
b, err := os.ReadFile(cfgfile)
if err == nil {
setupath = helper.BytesToString(b)
logrus.Println("[nsetu] set setu dir to", setupath)
}
}
if file.IsExist(dbfile) {
err := db.Open()
if err == nil {
setuclasses, err = db.ListTables()
}
if err != nil {
logrus.Errorln("[nsetu]", err)
}
}
}()
}
func scanall(path string) error {
setuclasses = nil
model := &setuclass{}
root := os.DirFS(path)
_ = db.Close()
_ = os.Remove(dbfile)
return fs.WalkDir(root, ".", func(path string, d fs.DirEntry, err error) error {
if err != nil {
return err
}
if d.IsDir() {
clsn := d.Name()
if clsn != "." {
mu.Lock()
err = db.Create(clsn, model)
setuclasses = append(setuclasses, clsn)
mu.Unlock()
if err == nil {
err = scanclass(root, path, clsn)
if err != nil {
logrus.Errorln("[nsetu]", err)
return err
}
}
}
}
return nil
})
}
func scanclass(root fs.FS, path, clsn string) error {
ds, err := fs.ReadDir(root, path)
if err != nil {
return err
}
mu.Lock()
_ = db.Truncate(clsn)
mu.Unlock()
for _, d := range ds {
if !d.IsDir() {
relpath := path + "/" + d.Name()
logrus.Debugln("[nsetu] read", relpath)
f, e := fs.ReadFile(root, relpath)
if e != nil {
return e
}
b := bytes.NewReader(f)
img, _, e := image.Decode(b)
if e != nil {
b.Seek(0, io.SeekStart)
img, e = webp.Decode(b)
}
if e != nil {
return e
}
dh, e := goimagehash.DifferenceHash(img)
if e != nil {
return e
}
dhi := int64(dh.GetHash())
logrus.Debugln("[nsetu] insert", d.Name(), "with id", dhi, "into", clsn)
mu.Lock()
err = db.Insert(clsn, &setuclass{ImgID: dhi, Name: d.Name(), Path: relpath})
mu.Unlock()
if err != nil {
return err
}
}
}
return nil
}

95
plugin_nativesetu/main.go Normal file
View File

@@ -0,0 +1,95 @@
package nativesetu
import (
"fmt"
"os"
"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"
"github.com/FloatTech/ZeroBot-Plugin/utils/rule"
)
const (
datapath = "data/nsetu"
dbfile = datapath + "/data.db"
cfgfile = datapath + "/setupath.txt"
)
var (
setupath = "/tmp" // 绝对路径,图片根目录
)
func init() {
engine := control.Register("nativesetu", &control.Options{
DisableOnDefault: false,
Help: "本地涩图\n" +
"- 本地[xxx]\n" +
"- 刷新本地[xxx]\n" +
"- 设置本地setu绝对路径[xxx]\n" +
"- 刷新所有本地setu\n" +
"- 所有本地setu分类",
})
engine.OnRegex(`^本地(.*)$`, func(ctx *zero.Ctx) bool { return rule.FirstValueInList(setuclasses)(ctx) }).SetBlock(true).SetPriority(36).
Handle(func(ctx *zero.Ctx) {
imgtype := ctx.State["regex_matched"].([]string)[1]
sc := new(setuclass)
mu.RLock()
err := db.Pick(imgtype, sc)
mu.RUnlock()
if err != nil {
ctx.SendChain(message.Text("ERROR: ", err))
} else {
p := "file:///" + setupath + "/" + sc.Path
ctx.SendChain(message.Text(imgtype, ": ", sc.Name, "\n"), message.Image(p))
}
})
engine.OnRegex(`^刷新本地(.*)$`, func(ctx *zero.Ctx) bool { return rule.FirstValueInList(setuclasses)(ctx) }, zero.SuperUserPermission).SetBlock(true).SetPriority(36).
Handle(func(ctx *zero.Ctx) {
imgtype := ctx.State["regex_matched"].([]string)[1]
err := scanclass(os.DirFS(setupath), imgtype, imgtype)
if err == nil {
ctx.SendChain(message.Text("成功!"))
} else {
ctx.SendChain(message.Text("ERROR: ", err))
}
})
engine.OnRegex(`^设置本地setu绝对路径(.*)$`, zero.SuperUserPermission).SetBlock(true).SetPriority(36).
Handle(func(ctx *zero.Ctx) {
setupath = ctx.State["regex_matched"].([]string)[1]
err := os.WriteFile(cfgfile, helper.StringToBytes(setupath), 0644)
if err == nil {
ctx.SendChain(message.Text("成功!"))
} else {
ctx.SendChain(message.Text("ERROR: ", err))
}
})
engine.OnFullMatch("刷新所有本地setu", zero.SuperUserPermission).SetBlock(true).SetPriority(36).
Handle(func(ctx *zero.Ctx) {
err := scanall(setupath)
if err == nil {
ctx.SendChain(message.Text("成功!"))
} else {
ctx.SendChain(message.Text("ERROR: ", err))
}
})
engine.OnFullMatch("所有本地setu分类").SetBlock(true).SetPriority(36).
Handle(func(ctx *zero.Ctx) {
msg := "所有本地setu分类"
mu.RLock()
for i, c := range setuclasses {
n, err := db.Count(c)
if err == nil {
msg += fmt.Sprintf("\n%02d. %s(%d)", i, c, n)
} else {
msg += fmt.Sprintf("\n%02d. %s(error)", i, c)
logrus.Errorln("[nsetu]", err)
}
}
mu.RUnlock()
ctx.SendChain(message.Text(msg))
})
}

169
plugin_nativewife/main.go Normal file
View File

@@ -0,0 +1,169 @@
package nativewife
import (
"crypto/md5"
"encoding/binary"
"errors"
"fmt"
"math/rand"
"os"
"strconv"
"strings"
"time"
"github.com/FloatTech/AnimeAPI/picture"
zero "github.com/wdvxdr1123/ZeroBot"
"github.com/wdvxdr1123/ZeroBot/message"
"github.com/wdvxdr1123/ZeroBot/utils/helper"
"github.com/FloatTech/ZeroBot-Plugin/control"
"github.com/FloatTech/ZeroBot-Plugin/utils/file"
)
const base = "data/nwife"
var baseuri = "file:///" + file.BOTPATH + "/" + base
func init() {
err := os.MkdirAll(base, 0755)
if err != nil {
panic(err)
}
engine := control.Register("nwife", &control.Options{
DisableOnDefault: false,
Help: "nativewife\n- 抽wife[@xxx]\n- 添加wife[名字][图片]\n- 删除wife[名字]\n- [让|不让]所有人均可添加wife",
})
engine.OnPrefix("抽wife", zero.OnlyGroup).SetBlock(true).SetPriority(20).
Handle(func(ctx *zero.Ctx) {
grpf := strconv.FormatInt(ctx.Event.GroupID, 36)
wifes, err := os.ReadDir(base + "/" + grpf)
if err != nil {
ctx.SendChain(message.Text("一个wife也没有哦~"))
return
}
switch len(wifes) {
case 0:
ctx.SendChain(message.Text("一个wife也没有哦~"))
case 1:
wn := wifes[0].Name()
ctx.SendChain(message.Text("大家的wife都是", wn, "\n"), message.Image(baseuri+"/"+grpf+"/"+wn), message.Text("\n哦~"))
default:
// 获取名字
name := ctx.State["args"].(string)
if len(ctx.Event.Message) > 1 && ctx.Event.Message[1].Type == "at" {
qq, _ := strconv.ParseInt(ctx.Event.Message[1].Data["qq"], 10, 64)
name = ctx.GetGroupMemberInfo(ctx.Event.GroupID, qq, false).Get("nickname").Str
} else if name == "" {
name = ctx.Event.Sender.NickName
}
now := time.Now()
s := md5.Sum(helper.StringToBytes(fmt.Sprintf("%s%d%d%d", name, now.Year(), now.Month(), now.Day())))
r := rand.New(rand.NewSource(int64(binary.LittleEndian.Uint64(s[:]))))
n := r.Intn(len(wifes))
wn := wifes[n].Name()
ctx.SendChain(message.Text(name, "的wife是", wn, "\n"), message.Image(baseuri+"/"+grpf+"/"+wn), message.Text("\n哦~"))
}
})
// 上传一张图
engine.OnPrefix("添加wife", zero.OnlyGroup, chkAddWifePermission, picture.MustGiven).SetBlock(true).SetPriority(20).
Handle(func(ctx *zero.Ctx) {
name := ""
for _, elem := range ctx.Event.Message {
if elem.Type == "text" {
name = strings.ReplaceAll(elem.Data["text"], " ", "")
name = name[strings.LastIndex(name, "添加wife")+10:]
name = strings.ReplaceAll(name, "/", "")
name = strings.ReplaceAll(name, "\\", "")
break
}
}
if name != "" {
url := ctx.State["image_url"].([]string)[0]
grpfolder := base + "/" + strconv.FormatInt(ctx.Event.GroupID, 36)
if file.IsNotExist(grpfolder) {
os.Mkdir(grpfolder, 0755)
}
err = file.DownloadTo(url, grpfolder+"/"+name, true)
if err == nil {
ctx.SendChain(message.Reply(ctx.Event.MessageID), message.Text("成功!"))
} else {
ctx.SendChain(message.Reply(ctx.Event.MessageID), message.Text("错误:", err.Error()))
}
} else {
ctx.SendChain(message.Reply(ctx.Event.MessageID), message.Text("没有找到wife的名字"))
}
})
engine.OnPrefix("删除wife", zero.OnlyGroup, zero.AdminPermission).SetBlock(true).SetPriority(20).
Handle(func(ctx *zero.Ctx) {
name := ""
for _, elem := range ctx.Event.Message {
if elem.Type == "text" {
name = strings.ReplaceAll(elem.Data["text"], " ", "")
name = name[strings.LastIndex(name, "删除wife")+10:]
name = strings.ReplaceAll(name, "/", "")
name = strings.ReplaceAll(name, "\\", "")
break
}
}
if name != "" {
grpfolder := base + "/" + strconv.FormatInt(ctx.Event.GroupID, 36)
err = os.Remove(grpfolder + "/" + name)
if err == nil {
ctx.SendChain(message.Reply(ctx.Event.MessageID), message.Text("成功!"))
} else {
ctx.SendChain(message.Reply(ctx.Event.MessageID), message.Text("错误:", err.Error()))
}
} else {
ctx.SendChain(message.Reply(ctx.Event.MessageID), message.Text("没有找到wife的名字"))
}
})
engine.OnSuffix("所有人均可添加wife", zero.SuperUserPermission, zero.OnlyGroup).SetBlock(true).SetPriority(20).
Handle(func(ctx *zero.Ctx) {
text := ""
for _, elem := range ctx.Event.Message {
if elem.Type == "text" {
text = strings.ReplaceAll(elem.Data["text"], " ", "")
text = text[:strings.LastIndex(text, "所有人均可添加wife")]
break
}
}
var err error
switch text {
case "设置", "授予", "让":
err = setEveryoneCanAddWife(ctx.Event.GroupID, true)
case "取消", "撤销", "不让":
err = setEveryoneCanAddWife(ctx.Event.GroupID, false)
}
if err == nil {
ctx.SendChain(message.Reply(ctx.Event.MessageID), message.Text("成功!"))
} else {
ctx.SendChain(message.Reply(ctx.Event.MessageID), message.Text("错误:", err.Error()))
}
})
}
func chkAddWifePermission(ctx *zero.Ctx) bool {
gid := ctx.Event.GroupID
if gid > 0 {
m, ok := control.Lookup("nwife")
if ok {
data := m.GetData(gid)
if data&1 == 1 {
return true
}
return zero.AdminPermission(ctx)
}
}
return false
}
func setEveryoneCanAddWife(gid int64, canadd bool) error {
m, ok := control.Lookup("nwife")
if ok {
if canadd {
return m.SetData(gid, 1)
}
return m.SetData(gid, 0)
}
return errors.New("no such plugin")
}

186
plugin_novel/qianbi.go Normal file
View File

@@ -0,0 +1,186 @@
// Package novel 铅笔小说搜索插件
package novel
import (
"fmt"
"io/ioutil"
"net/http"
"net/http/cookiejar"
"net/url"
"regexp"
"strings"
"time"
"github.com/antchfx/htmlquery"
log "github.com/sirupsen/logrus"
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"
ub "github.com/FloatTech/ZeroBot-Plugin/utils/binary"
)
const (
websiteURL = "https://www.23qb.net"
websiteTitle = "铅笔小说"
errorTitle = "出现错误!"
username = "zerobot"
password = "123456"
submit = "%26%23160%3B%B5%C7%26%23160%3B%26%23160%3B%C2%BC%26%23160%3B"
ua = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/96.0.4664.110 Safari/537.36"
loginURL = websiteURL + "/login.php?do=submit&jumpurl=https%3A%2F%2Fwww.23qb.net%2F"
searchURL = websiteURL + "/saerch.php"
idReg = `/(\d+)/`
)
var (
gCurCookieJar *cookiejar.Jar
engine = control.Register("novel", &control.Options{
DisableOnDefault: false,
Help: "铅笔小说网搜索\n- 小说[xxx]",
})
limit = rate.NewManager(time.Minute, 5)
)
func init() {
engine.OnRegex("^小说([\u4E00-\u9FA5A-Za-z0-9]{1,25})$").SetBlock(true).
Handle(func(ctx *zero.Ctx) {
if !limit.Load(ctx.Event.GroupID).Acquire() {
ctx.SendChain(message.Text("请稍后重试0x0..."))
return
}
ctx.SendChain(message.Text("少女祈祷中......"))
login(username, password)
searchKey := ctx.State["regex_matched"].([]string)[1]
searchHtml := search(searchKey)
var m message.Message
doc, err := htmlquery.Parse(strings.NewReader(searchHtml))
if err != nil {
log.Errorln("[novel]", err)
}
htmlTitle := htmlquery.InnerText(htmlquery.FindOne(doc, "/html/head/title"))
if htmlTitle == websiteTitle {
list, err := htmlquery.QueryAll(doc, "//dl[@id='nr']")
if err != nil {
log.Errorln("[novel]", err)
}
if len(list) != 0 {
for _, v := range list {
bookName := htmlquery.InnerText(htmlquery.FindOne(v, "/dd[1]/h3/a[1]"))
category := htmlquery.InnerText(htmlquery.FindOne(v, "/dt/span[1]"))
author := htmlquery.InnerText(htmlquery.FindOne(v, "/dd[2]/span[1]"))
status := htmlquery.InnerText(htmlquery.FindOne(v, "/dd[2]/span[2]"))
wordNumbers := htmlquery.InnerText(htmlquery.FindOne(v, "/dd[2]/span[3]"))
description := htmlquery.InnerText(htmlquery.FindOne(v, "/dd[3]"))
updateTime := htmlquery.InnerText(htmlquery.FindOne(v, "/dd[1]/h3/span[1]"))
latestChapter := htmlquery.InnerText(htmlquery.FindOne(v, "/dd[4]/a[1]"))
reg := regexp.MustCompile(idReg)
id := reg.FindStringSubmatch(htmlquery.SelectAttr(htmlquery.FindOne(v, "/dt/a[1]"), "href"))[1]
webpageURL := websiteURL + "/book/" + id + "/"
downloadURL := websiteURL + "/modules/article/txtarticle.php?id=" + id
text := fmt.Sprintf("书名:%s\n类型:%s\n作者:%s\n状态:%s\n字数:%s\n简介:%s\n更新时间:%s\n最新章节:%s\n网页链接:%s\n下载地址:%s\n", bookName, category, author, status, wordNumbers, description, updateTime, latestChapter, webpageURL, downloadURL)
m = append(m,
message.CustomNode(
zero.BotConfig.NickName[0],
ctx.Event.SelfID,
text),
)
}
if id := ctx.SendGroupForwardMessage(
ctx.Event.GroupID,
m).Get("message_id").Int(); id == 0 {
ctx.SendChain(message.Text("ERROR: 可能被风控了"))
}
} else {
text := htmlquery.InnerText(htmlquery.FindOne(doc, "//div[@id='tipss']"))
text = strings.Replace(text, " ", "", -1)
text = strings.Replace(text, "本站", websiteURL, -1)
ctx.SendChain(message.Text(text))
}
} else if htmlTitle == errorTitle {
ctx.SendChain(message.Text(errorTitle))
text := htmlquery.InnerText(htmlquery.FindOne(doc, "//div[@style='text-align: center;padding:10px']"))
text = strings.Replace(text, " ", "", -1)
ctx.SendChain(message.Text(text))
} else {
bookName := htmlquery.SelectAttr(htmlquery.FindOne(doc, "//meta[@property='og:novel:book_name']"), "content")
category := htmlquery.SelectAttr(htmlquery.FindOne(doc, "//meta[@property='og:novel:category']"), "content")
author := htmlquery.SelectAttr(htmlquery.FindOne(doc, "//meta[@property='og:novel:author']"), "content")
status := htmlquery.SelectAttr(htmlquery.FindOne(doc, "//meta[@property='og:novel:status']"), "content")
description := htmlquery.InnerText(htmlquery.FindOne(doc, "//div[@id='bookintro']/p"))
updateTime := htmlquery.SelectAttr(htmlquery.FindOne(doc, "//meta[@property='og:novel:update_time']"), "content")
latestChapter := htmlquery.SelectAttr(htmlquery.FindOne(doc, "//meta[@property='og:novel:latest_chapter_name']"), "content")
reg := regexp.MustCompile(idReg)
id := reg.FindStringSubmatch(htmlquery.SelectAttr(htmlquery.FindOne(doc, "//meta[@property='og:novel:read_url']"), "content"))[1]
webpageURL := websiteURL + "/book/" + id + "/"
downloadURL := websiteURL + "/modules/article/txtarticle.php?id=" + id
text := fmt.Sprintf("书名:%s\n类型:%s\n作者:%s\n状态:%s\n简介:%s\n更新时间:%s\n最新章节:%s\n网页链接:%s\n下载地址:%s\n", bookName, category, author, status, description, updateTime, latestChapter, webpageURL, downloadURL)
ctx.SendChain(message.Text(text))
}
})
}
func login(username, password string) {
gCurCookieJar, _ = cookiejar.New(nil)
client := &http.Client{
Jar: gCurCookieJar,
}
usernameData, err := ub.UTF82GBK(helper.StringToBytes(username))
if err != nil {
log.Errorln("[novel]", err)
}
usernameGbk := helper.BytesToString(usernameData)
passwordData, err := ub.UTF82GBK(helper.StringToBytes(password))
if err != nil {
log.Errorln("[novel]", err)
}
passwordGbk := helper.BytesToString(passwordData)
loginReq, err := http.NewRequest("POST", loginURL, strings.NewReader(fmt.Sprintf("username=%s&password=%s&usecookie=315360000&action=login&submit=%s", url.QueryEscape(usernameGbk), url.QueryEscape(passwordGbk), submit)))
if err != nil {
log.Errorln("[novel]", err)
}
loginReq.Header.Set("Content-Type", "application/x-www-form-urlencoded")
loginReq.Header.Set("User-Agent", ua)
loginResp, err := client.Do(loginReq)
if err != nil {
log.Errorln("[novel]", err)
}
defer loginResp.Body.Close()
}
func search(searchKey string) (searchHtml string) {
searchKeyData, err := ub.UTF82GBK(helper.StringToBytes(searchKey))
if err != nil {
log.Errorln("[novel]", err)
}
searchKeyGbk := helper.BytesToString(searchKeyData)
client := &http.Client{
Jar: gCurCookieJar,
}
searchReq, err := http.NewRequest("POST", searchURL, strings.NewReader(fmt.Sprintf("searchkey=%s&searchtype=all", url.QueryEscape(searchKeyGbk))))
if err != nil {
log.Errorln("[novel]", err)
}
searchReq.Header.Set("Content-Type", "application/x-www-form-urlencoded")
searchReq.Header.Set("User-Agent", ua)
searchResp, err := client.Do(searchReq)
if err != nil {
log.Errorln("[novel]", err)
}
defer searchResp.Body.Close()
searchData, err := ioutil.ReadAll(searchResp.Body)
if err != nil {
log.Errorf("[novel] get response for url=%s got error=%s\n", searchURL, err.Error())
}
searchData, err = ub.GBK2UTF8(searchData)
if err != nil {
log.Errorln("[novel]", err)
}
searchHtml = helper.BytesToString(searchData)
return searchHtml
}

40
plugin_omikuji/data.go Normal file
View File

@@ -0,0 +1,40 @@
package omikuji
import (
"os"
log "github.com/sirupsen/logrus"
"github.com/FloatTech/ZeroBot-Plugin/utils/file"
"github.com/FloatTech/ZeroBot-Plugin/utils/process"
"github.com/FloatTech/ZeroBot-Plugin/utils/sql"
)
const (
dbpath = "data/Omikuji/"
dbfile = dbpath + "kuji.db"
)
var db = &sql.Sqlite{DBPath: dbfile}
func init() {
go func() {
defer func() {
if err := recover(); err != nil {
log.Println(err)
}
}()
process.SleepAbout1sTo2s()
_ = os.MkdirAll(dbpath, 0755)
_, _ = file.GetLazyData(dbfile, false, true)
err := db.Create("kuji", &kuji{})
if err != nil {
panic(err)
}
n, err := db.Count("kuji")
if err != nil {
panic(err)
}
log.Printf("[kuji]读取%d条签文", n)
}()
}

View File

@@ -0,0 +1,46 @@
package main
import (
"fmt"
"os"
"strconv"
"github.com/FloatTech/ZeroBot-Plugin/utils/sql"
)
type signature struct {
Id uint64 `db:"id"`
Text string `db:"text"`
}
type kuji struct {
Id uint8 `db:"id"`
Text string `db:"text"`
}
func main() {
db := &sql.Sqlite{DBPath: os.Args[1]}
newdb := &sql.Sqlite{DBPath: os.Args[2]}
err := newdb.Create("kuji", &kuji{})
if err != nil {
panic(err)
}
err = db.Create("signature", &signature{})
if err != nil {
panic(err)
}
fmt.Println(db.Count("signature"))
s := &signature{}
k := &kuji{}
for i := 1; i <= 100; i++ {
db.Find("signature", s, "where id = "+strconv.Itoa(i))
fmt.Println("insert: ", s.Text[:57])
k.Id = uint8(i)
k.Text = s.Text
newdb.Insert("kuji", k)
}
db.Close()
newdb.Close()
}

18
plugin_omikuji/model.go Normal file
View File

@@ -0,0 +1,18 @@
package omikuji
import "strconv"
type kuji struct {
Id uint8 `db:"id"`
Text string `db:"text"`
}
// 返回一个解签
func getKujiByBango(id uint8) string {
var s kuji
err := db.Find("kuji", &s, "where id = "+strconv.Itoa(int(id)))
if err != nil {
return err.Error()
}
return s.Text
}

View File

@@ -4,31 +4,54 @@ package omikuji
import (
"fmt"
"math/rand"
"strconv"
"time"
log "github.com/sirupsen/logrus"
"github.com/FloatTech/ZeroBot-Plugin/control"
zero "github.com/wdvxdr1123/ZeroBot"
"github.com/wdvxdr1123/ZeroBot/message"
"github.com/FloatTech/ZeroBot-Plugin/control"
)
const (
bed = "https://codechina.csdn.net/u011570312/senso-ji-omikuji/-/raw/main/%d_%d.jpg"
)
func init() { // 插件主体
rand.Seed(time.Now().UnixNano())
control.Register("omikuji", &control.Options{
var (
engine = control.Register("omikuji", &control.Options{
DisableOnDefault: false,
Help: "浅草寺求签\n" +
"- 求签|占卜",
}).OnFullMatchGroup([]string{"求签", "占卜"}).SetPriority(10).SetBlock(true).
"- 求签|占卜\n- 解签",
})
)
func init() { // 插件主体
engine.OnFullMatchGroup([]string{"求签", "占卜"}).SetPriority(10).SetBlock(true).
Handle(func(ctx *zero.Ctx) {
miku := rand.Intn(100) + 1
miku := bangoToday(ctx.Event.UserID)
ctx.SendChain(
message.At(ctx.Event.UserID),
message.Image(fmt.Sprintf(bed, miku, 0)),
message.Image(fmt.Sprintf(bed, miku, 1)),
)
})
engine.OnFullMatchGroup([]string{"解签"}).SetPriority(10).SetBlock(true).
Handle(func(ctx *zero.Ctx) {
ctx.SendChain(
message.At(ctx.Event.UserID),
message.Text(getKujiByBango(bangoToday(ctx.Event.UserID))),
)
})
}
func bangoToday(uid int64) uint8 {
today, err := strconv.ParseInt(time.Now().Format("20060102"), 10, 64)
if err != nil {
log.Errorln("string转化为int64格式有问题:", err)
}
seed := uid + today
r := rand.New(rand.NewSource(seed))
return uint8(r.Intn(100) + 1)
}

View File

@@ -7,6 +7,7 @@ import (
"io/ioutil"
"math/rand"
"net/http"
"net/url"
"regexp"
"strconv"
"strings"
@@ -20,7 +21,7 @@ import (
)
var (
prio = 100
prio = 256
bucket = rate.NewManager(time.Minute, 20) // 青云客接口回复
engine *zero.Engine
)
@@ -31,14 +32,14 @@ func init() { // 插件主体
Help: "青云客\n" +
"- @Bot 任意文本(任意一句话回复)",
})
// 回复 匹配中文、英文、数字、空格但不包括下划线等符号
engine.OnRegex("^([\u4E00-\u9FA5A-Za-z0-9\\s]{1,30})", zero.OnlyToMe).SetBlock(true).SetPriority(prio).
// 回复 @和包括名字
engine.OnMessage(zero.OnlyToMe).SetBlock(true).SetPriority(prio).
Handle(func(ctx *zero.Ctx) {
if !bucket.Load(ctx.Event.UserID).Acquire() {
// 频繁触发,不回复
return
}
msg := ctx.State["regex_matched"].([]string)[1]
msg := ctx.ExtractPlainText()
// 调用青云客接口
reply, err := getMessage(msg)
if err != nil {
@@ -96,15 +97,17 @@ type dataQYK struct {
Content string `json:"content"`
}
const (
qykURL = "http://api.qingyunke.com/api.php"
key = "free"
appid = "0"
)
// 青云客取消息
func getMessage(msg string) (string, error) {
url := "http://api.qingyunke.com/api.php"
key := "free"
appid := "0"
url = fmt.Sprintf(url+"?key=%s&appid=%s&msg=%s", key, appid, msg)
u := fmt.Sprintf(qykURL+"?key=%s&appid=%s&msg=%s", key, appid, url.QueryEscape(msg))
client := &http.Client{}
req, err := http.NewRequest("GET", url, nil)
req, err := http.NewRequest("GET", u, nil)
if err != nil {
return "", err
}

View File

@@ -1,21 +0,0 @@
package qingyunke
// TODO: 移动到 manager 搭配自动验证使用
/*
import (
zero "github.com/wdvxdr1123/ZeroBot"
)
//自动同意加群,加好友
func init() {
zero.OnRequest().SetBlock(false).FirstPriority().Handle(func(ctx *zero.Ctx) {
if ctx.Event.RequestType == "friend" {
ctx.SetFriendAddRequest(ctx.Event.Flag, true, "")
}
if ctx.Event.RequestType == "group" && ctx.Event.SubType == "invite" {
ctx.SetGroupAddRequest(ctx.Event.Flag, "invite", true, "我爱你mua~")
}
})
}
*/

View File

@@ -2,19 +2,18 @@ package reborn
import (
"encoding/json"
"io"
"net/http"
"os"
"time"
wr "github.com/mroth/weightedrand"
log "github.com/sirupsen/logrus"
"github.com/FloatTech/ZeroBot-Plugin/utils/file"
"github.com/FloatTech/ZeroBot-Plugin/utils/process"
)
const (
datapath = "data/Reborn"
jsonfile = datapath + "/rate.json"
pburl = "https://codechina.csdn.net/u011570312/ZeroBot-Plugin/-/raw/master/" + jsonfile
)
type rate []struct {
@@ -28,7 +27,7 @@ var (
func init() {
go func() {
time.Sleep(time.Second)
process.SleepAbout1sTo2s()
err := os.MkdirAll(datapath, 0755)
if err != nil {
panic(err)
@@ -53,39 +52,9 @@ func init() {
// load 加载rate数据
func load(area *rate) error {
if _, err := os.Stat(jsonfile); err == nil || os.IsExist(err) {
f, err := os.Open(jsonfile)
if err == nil {
defer f.Close()
data, err1 := io.ReadAll(f)
if err1 == nil {
if len(data) > 0 {
return json.Unmarshal(data, area)
}
}
return err1
}
} else { // 如果没有小作文,则从 url 下载
f, err := os.Create(jsonfile)
if err != nil {
return err
}
defer f.Close()
resp, err := http.Get(pburl)
if err == nil {
defer resp.Body.Close()
if resp.ContentLength > 0 {
log.Printf("[Reborn]从镜像下载国家和地区%d字节...", resp.ContentLength)
data, err := io.ReadAll(resp.Body)
if err == nil && len(data) > 0 {
_, _ = f.Write(data)
return json.Unmarshal(data, area)
}
return err
}
return nil
}
data, err := file.GetLazyData(jsonfile, true, true)
if err != nil {
return err
}
return nil
return json.Unmarshal(data, area)
}

View File

@@ -107,7 +107,7 @@ func init() {
}).OnRegex(`^>runcode\s(.+?)\s([\s\S]+)$`).SetBlock(true).SecondPriority().
Handle(func(ctx *zero.Ctx) {
if !limit.Load(ctx.Event.UserID).Acquire() {
ctx.Send("请稍后重试0x0...")
ctx.SendChain(message.Text("请稍后重试0x0..."))
} else {
language := ctx.State["regex_matched"].([]string)[1]
language = strings.ToLower(language)

View File

@@ -3,20 +3,31 @@ package saucenao
import (
"fmt"
"os"
"strconv"
"strings"
"github.com/FloatTech/AnimeAPI/ascii2d"
"github.com/FloatTech/AnimeAPI/picture"
"github.com/FloatTech/AnimeAPI/pixiv"
"github.com/FloatTech/AnimeAPI/saucenao"
"github.com/sirupsen/logrus"
zero "github.com/wdvxdr1123/ZeroBot"
"github.com/wdvxdr1123/ZeroBot/message"
"github.com/FloatTech/ZeroBot-Plugin/control"
"github.com/FloatTech/ZeroBot-Plugin/utils/file"
)
var (
datapath = file.BOTPATH + "/data/saucenao/"
)
func init() { // 插件主体
_ = os.RemoveAll(datapath)
err := os.MkdirAll(datapath, 0755)
if err != nil {
panic(err)
}
engine := control.Register("saucenao", &control.Options{
DisableOnDefault: false,
Help: "搜图\n" +
@@ -27,7 +38,7 @@ func init() { // 插件主体
engine.OnRegex(`^搜图(\d+)$`).SetBlock(true).FirstPriority().
Handle(func(ctx *zero.Ctx) {
id, _ := strconv.ParseInt(ctx.State["regex_matched"].([]string)[1], 10, 64)
ctx.Send("少女祈祷中......")
ctx.SendChain(message.Text("少女祈祷中......"))
// 获取P站插图信息
illust, err := pixiv.Works(id)
if err != nil {
@@ -35,30 +46,48 @@ func init() { // 插件主体
return
}
if illust.Pid > 0 {
// 改用 i.pixiv.cat 镜像站
link := illust.ImageUrls
link = strings.ReplaceAll(link, "i.pximg.net", "i.pixiv.cat")
// 发送搜索结果
ctx.SendChain(
message.Image(link),
message.Text(
"\n",
"标题:", illust.Title, "\n",
"插画ID", illust.Pid, "\n",
"画师:", illust.UserName, "\n",
"画师ID", illust.UserId, "\n",
"直链:", "https://pixivel.moe/detail?id=", illust.Pid,
),
name := strconv.FormatInt(illust.Pid, 10)
filepath := datapath + name
switch {
case file.IsExist(filepath + ".jpg"):
filepath = "file:///" + filepath + ".jpg"
case file.IsExist(filepath + ".png"):
filepath = "file:///" + filepath + ".png"
case file.IsExist(filepath + ".gif"):
filepath = "file:///" + filepath + ".gif"
default:
filepath = ""
}
if filepath == "" {
logrus.Debug("[sausenao]开始下载", name)
filepath, err = pixiv.Download(illust.ImageUrls, datapath, name)
if err == nil {
filepath = "file:///" + filepath
}
}
txt := message.Text(
"标题:", illust.Title, "\n",
"插画ID", illust.Pid, "\n",
"画师:", illust.UserName, "\n",
"画师ID", illust.UserId, "\n",
"直链:", "https://pixivel.moe/detail?id=", illust.Pid,
)
if filepath != "" {
// 发送搜索结果
ctx.SendChain(message.Image(filepath), message.Text("\n"), txt)
} else {
// 图片下载失败,仅发送文字结果
ctx.SendChain(txt)
}
} else {
ctx.Send("图片不存在!")
ctx.SendChain(message.Text("图片不存在!"))
}
})
// 以图搜图
engine.OnKeywordGroup([]string{"以图搜图", "搜索图片", "以图识图"}, picture.CmdMatch, picture.MustGiven).SetBlock(true).FirstPriority().
Handle(func(ctx *zero.Ctx) {
// 开始搜索图片
ctx.Send("少女祈祷中......")
ctx.SendChain(message.Text("少女祈祷中......"))
for _, pic := range ctx.State["image_url"].([]string) {
fmt.Println(pic)
if result, err := saucenao.SauceNAO(pic); err != nil {

View File

@@ -3,8 +3,6 @@ package setutime
import (
"fmt"
"io"
"net/http"
"os"
"strconv"
"strings"
@@ -12,19 +10,22 @@ import (
"time"
"github.com/FloatTech/AnimeAPI/pixiv"
"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/data"
fileutil "github.com/FloatTech/ZeroBot-Plugin/utils/file"
"github.com/FloatTech/ZeroBot-Plugin/utils/math"
"github.com/FloatTech/ZeroBot-Plugin/utils/process"
"github.com/FloatTech/ZeroBot-Plugin/utils/rule"
"github.com/FloatTech/ZeroBot-Plugin/utils/sql"
)
// Pools 图片缓冲池
type imgpool struct {
Lock sync.Mutex
DB *data.Sqlite
DB *sql.Sqlite
Path string
Group int64
List []string
@@ -33,14 +34,10 @@ type imgpool struct {
Form int64
}
const (
dburl = "https://codechina.csdn.net/u011570312/ZeroBot-Plugin/-/raw/master/data/SetuTime/SetuTime.db"
)
// NewPoolsCache 返回一个缓冲池对象
func newPools() *imgpool {
cache := &imgpool{
DB: &data.Sqlite{DBPath: "data/SetuTime/SetuTime.db"},
DB: &sql.Sqlite{DBPath: "data/SetuTime/SetuTime.db"},
Path: "data/SetuTime/cache/",
Group: 0,
List: []string{"涩图", "二次元", "风景", "车万"}, // 可以自己加类别,得自己加图片进数据库
@@ -55,26 +52,7 @@ func newPools() *imgpool {
panic(err)
}
// 如果数据库不存在则下载
if _, err := os.Stat(cache.DB.DBPath); err != nil || os.IsNotExist(err) {
f, err := os.Create(cache.DB.DBPath)
if err == nil {
resp, err := http.Get(dburl)
if err == nil {
defer resp.Body.Close()
if resp.ContentLength > 0 {
logrus.Printf("[Setu]从镜像下载数据库%d字节...", resp.ContentLength)
data, err := io.ReadAll(resp.Body)
if err == nil && len(data) > 0 {
_, err = f.Write(data)
if err != nil {
logrus.Errorf("[Setu]写入数据库失败: %v", err)
}
}
}
}
f.Close()
}
}
_, _ = fileutil.GetLazyData(cache.DB.DBPath, false, false)
for i := range cache.List {
if err := cache.DB.Create(cache.List[i], &pixiv.Illust{}); err != nil {
panic(err)
@@ -84,149 +62,127 @@ func newPools() *imgpool {
}
var (
pool = newPools()
pool *imgpool
limit = rate.NewManager(time.Minute*1, 5)
)
func init() { // 插件主体
engine := control.Register("setutime", &control.Options{
DisableOnDefault: false,
Help: "涩图\n" +
"- 来份[涩图/二次元/风景/车万]\n" +
"- 添加[涩图/二次元/风景/车万][P站图片ID]\n" +
"- 删除[涩图/二次元/风景/车万][P站图片ID]\n" +
"- >setu status",
})
engine.OnRegex(`^来份(.*)$`, firstValueInList(pool.List)).SetBlock(true).SetPriority(20).
Handle(func(ctx *zero.Ctx) {
if !limit.Load(ctx.Event.UserID).Acquire() {
ctx.SendChain(message.Text("请稍后重试0x0..."))
return
}
var imgtype = ctx.State["regex_matched"].([]string)[1]
// 补充池子
go func() {
times := min(pool.Max-pool.size(imgtype), 2)
for i := 0; i < times; i++ {
illust := &pixiv.Illust{}
// 查询出一张图片
if err := pool.DB.Find(imgtype, illust, "ORDER BY RANDOM() limit 1"); err != nil {
ctx.SendChain(message.Text("ERROR: ", err))
continue
}
// 下载图片
if err := download(illust, pool.Path); err != nil {
ctx.SendChain(message.Text("ERROR: ", err))
continue
}
ctx.SendGroupMessage(pool.Group, []message.MessageSegment{message.Image(file(illust))})
// 向缓冲池添加一张图片
pool.push(imgtype, illust)
time.Sleep(time.Second * 1)
}
}()
// 如果没有缓存阻塞5秒
if pool.size(imgtype) == 0 {
ctx.SendChain(message.Text("INFO: 正在填充弹药......"))
<-time.After(time.Second * 5)
if pool.size(imgtype) == 0 {
ctx.SendChain(message.Text("ERROR: 等待填充,请稍后再试......"))
go func() {
process.SleepAbout1sTo2s()
pool = newPools()
engine := control.Register("setutime", &control.Options{
DisableOnDefault: false,
Help: "涩图\n" +
"- 来份[涩图/二次元/风景/车万]\n" +
"- 添加[涩图/二次元/风景/车万][P站图片ID]\n" +
"- 删除[涩图/二次元/风景/车万][P站图片ID]\n" +
"- >setu status",
})
engine.OnRegex(`^来份(.*)$`, rule.FirstValueInList(pool.List)).SetBlock(true).SetPriority(20).
Handle(func(ctx *zero.Ctx) {
if !limit.Load(ctx.Event.UserID).Acquire() {
ctx.SendChain(message.Text("请稍后重试0x0..."))
return
}
}
// 从缓冲池里抽一张
if id := ctx.SendChain(message.Image(file(pool.pop(imgtype)))); id == 0 {
ctx.SendChain(message.Text("ERROR: 可能被风控了"))
}
})
engine.OnRegex(`^添加(.*?)(\d+)$`, firstValueInList(pool.List), zero.SuperUserPermission).SetBlock(true).SetPriority(21).
Handle(func(ctx *zero.Ctx) {
var (
imgtype = ctx.State["regex_matched"].([]string)[1]
id, _ = strconv.ParseInt(ctx.State["regex_matched"].([]string)[2], 10, 64)
)
ctx.SendChain(message.Text("少女祈祷中......"))
// 查询P站插图信息
illust, err := pixiv.Works(id)
if err != nil {
ctx.SendChain(message.Text("ERROR: ", err))
return
}
// 下载插画
if err := download(illust, pool.Path); err != nil {
ctx.SendChain(message.Text("ERROR: ", err))
return
}
// 发送到发送者
if id := ctx.SendChain(message.Image(file(illust))); id == 0 {
ctx.SendChain(message.Text("ERROR: 可能被风控,发送失败"))
return
}
// 添加插画到对应的数据库table
if err := pool.DB.Insert(imgtype, illust); err != nil {
ctx.SendChain(message.Text("ERROR: ", err))
return
}
ctx.Send("添加成功")
})
engine.OnRegex(`^删除(.*?)(\d+)$`, firstValueInList(pool.List), zero.SuperUserPermission).SetBlock(true).SetPriority(22).
Handle(func(ctx *zero.Ctx) {
var (
imgtype = ctx.State["regex_matched"].([]string)[1]
id, _ = strconv.ParseInt(ctx.State["regex_matched"].([]string)[2], 10, 64)
)
// 查询数据库
if err := pool.DB.Del(imgtype, fmt.Sprintf("WHERE pid=%d", id)); err != nil {
ctx.Send(fmt.Sprintf("ERROR: %v", err))
return
}
ctx.Send("删除成功")
})
// 查询数据库涩图数量
engine.OnFullMatchGroup([]string{">setu status"}).SetBlock(true).SetPriority(23).
Handle(func(ctx *zero.Ctx) {
state := []string{"[SetuTime]"}
for i := range pool.List {
num, err := pool.DB.Count(pool.List[i])
if err != nil {
num = 0
var imgtype = ctx.State["regex_matched"].([]string)[1]
// 补充池子
go func() {
times := math.Min(pool.Max-pool.size(imgtype), 2)
for i := 0; i < times; i++ {
illust := &pixiv.Illust{}
// 查询出一张图片
if err := pool.DB.Pick(imgtype, illust); err != nil {
ctx.SendChain(message.Text("ERROR: ", err))
continue
}
// 下载图片
if err := download(illust, pool.Path); err != nil {
ctx.SendChain(message.Text("ERROR: ", err))
continue
}
ctx.SendGroupMessage(pool.Group, []message.MessageSegment{message.Image(file(illust))})
// 向缓冲池添加一张图片
pool.push(imgtype, illust)
time.Sleep(time.Second * 1)
}
}()
// 如果没有缓存阻塞5秒
if pool.size(imgtype) == 0 {
ctx.SendChain(message.Text("INFO: 正在填充弹药......"))
<-time.After(time.Second * 5)
if pool.size(imgtype) == 0 {
ctx.SendChain(message.Text("ERROR: 等待填充,请稍后再试......"))
return
}
}
state = append(state, "\n")
state = append(state, pool.List[i])
state = append(state, ": ")
state = append(state, fmt.Sprintf("%d", num))
}
ctx.Send(strings.Join(state, ""))
})
}
// 从缓冲池里抽一张
if id := ctx.SendChain(message.Image(file(pool.pop(imgtype)))); id == 0 {
ctx.SendChain(message.Text("ERROR: 可能被风控了"))
}
})
// firstValueInList 判断正则匹配的第一个参数是否在列表中
func firstValueInList(list []string) zero.Rule {
return func(ctx *zero.Ctx) bool {
first := ctx.State["regex_matched"].([]string)[1]
for i := range list {
if first == list[i] {
return true
}
}
return false
}
}
engine.OnRegex(`^添加(.*?)(\d+)$`, rule.FirstValueInList(pool.List), zero.SuperUserPermission).SetBlock(true).SetPriority(21).
Handle(func(ctx *zero.Ctx) {
var (
imgtype = ctx.State["regex_matched"].([]string)[1]
id, _ = strconv.ParseInt(ctx.State["regex_matched"].([]string)[2], 10, 64)
)
ctx.SendChain(message.Text("少女祈祷中......"))
// 查询P站插图信息
illust, err := pixiv.Works(id)
if err != nil {
ctx.SendChain(message.Text("ERROR: ", err))
return
}
// 下载插画
if err := download(illust, pool.Path); err != nil {
ctx.SendChain(message.Text("ERROR: ", err))
return
}
// 发送到发送者
if id := ctx.SendChain(message.Image(file(illust))); id == 0 {
ctx.SendChain(message.Text("ERROR: 可能被风控,发送失败"))
return
}
// 添加插画到对应的数据库table
if err := pool.DB.Insert(imgtype, illust); err != nil {
ctx.SendChain(message.Text("ERROR: ", err))
return
}
ctx.SendChain(message.Text("添加成功"))
})
// min 返回两数最小值
func min(a, b int) int {
switch {
default:
return a
case a > b:
return b
case a < b:
return a
}
engine.OnRegex(`^删除(.*?)(\d+)$`, rule.FirstValueInList(pool.List), zero.SuperUserPermission).SetBlock(true).SetPriority(22).
Handle(func(ctx *zero.Ctx) {
var (
imgtype = ctx.State["regex_matched"].([]string)[1]
id, _ = strconv.ParseInt(ctx.State["regex_matched"].([]string)[2], 10, 64)
)
// 查询数据库
if err := pool.DB.Del(imgtype, fmt.Sprintf("WHERE pid=%d", id)); err != nil {
ctx.SendChain(message.Text("ERROR: ", err))
return
}
ctx.SendChain(message.Text("删除成功"))
})
// 查询数据库涩图数量
engine.OnFullMatchGroup([]string{">setu status"}).SetBlock(true).SetPriority(23).
Handle(func(ctx *zero.Ctx) {
state := []string{"[SetuTime]"}
for i := range pool.List {
num, err := pool.DB.Count(pool.List[i])
if err != nil {
num = 0
}
state = append(state, "\n")
state = append(state, pool.List[i])
state = append(state, ": ")
state = append(state, fmt.Sprintf("%d", num))
}
ctx.SendChain(message.Text(state))
})
}()
}
// size 返回缓冲池指定类型的现有大小
@@ -261,8 +217,7 @@ func (p *imgpool) pop(imgtype string) (illust *pixiv.Illust) {
func file(i *pixiv.Illust) string {
filename := fmt.Sprint(i.Pid)
pwd, _ := os.Getwd()
filepath := pwd + `/` + pool.Path + filename
filepath := fileutil.BOTPATH + `/` + pool.Path + filename
if _, err := os.Stat(filepath + ".jpg"); err == nil || os.IsExist(err) {
return `file:///` + filepath + ".jpg"
}

View File

@@ -1,3 +1,10 @@
/*
* @Author: Kanri
* @Date: 2021-10-15 21:23:14
* @LastEditors: Kanri
* @LastEditTime: 2021-10-15 21:42:51
* @Description:
*/
// Package shindan 基于 https://shindanmaker.com 的测定小功能
package shindan
@@ -52,6 +59,7 @@ func handle(ctx *zero.Ctx) {
if err != nil {
ctx.SendChain(message.Text("ERROR: ", err))
}
// TODO: 可注入
ctx.Send(text)
}

View File

@@ -0,0 +1,23 @@
package sleepmanage
import (
"os"
log "github.com/sirupsen/logrus"
"github.com/FloatTech/ZeroBot-Plugin/plugin_sleep_manage/model"
"github.com/FloatTech/ZeroBot-Plugin/utils/process"
)
func init() {
go func() {
defer func() {
if err := recover(); err != nil {
log.Println(err)
}
}()
process.SleepAbout1sTo2s()
_ = os.MkdirAll(dbpath, 0755)
model.Initialize(dbfile)
}()
}

View File

@@ -0,0 +1,115 @@
// Package model 睡眠管理数据库
package model
import (
"os"
"time"
"github.com/jinzhu/gorm"
_ "github.com/logoove/sqlite"
log "github.com/sirupsen/logrus"
)
type SleepDB gorm.DB
func Initialize(dbpath string) *SleepDB {
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(&SleepManage{})
return (*SleepDB)(gdb)
}
func Open(dbpath string) (*SleepDB, error) {
db, err := gorm.Open("sqlite3", dbpath)
if err != nil {
return nil, err
} else {
return (*SleepDB)(db), nil
}
}
func (sdb *SleepDB) Close() error {
db := (*gorm.DB)(sdb)
return db.Close()
}
type SleepManage struct {
gorm.Model
GroupId int64 `gorm:"column:group_id"`
UserId int64 `gorm:"column:user_id"`
SleepTime time.Time `gorm:"column:sleep_time"`
}
func (SleepManage) TableName() string {
return "sleep_manage"
}
// 更新睡眠时间
func (sdb *SleepDB) Sleep(groupId, userId int64) (position int, awakeTime time.Duration) {
db := (*gorm.DB)(sdb)
now := time.Now()
var today time.Time
if now.Hour() >= 21 {
today = now.Add(-time.Hour*time.Duration(-21+now.Hour()) - time.Minute*time.Duration(now.Minute()) - time.Second*time.Duration(now.Second()))
} else if now.Hour() <= 3 {
today = now.Add(-time.Hour*time.Duration(3+now.Hour()) - time.Minute*time.Duration(now.Minute()) - time.Second*time.Duration(now.Second()))
}
st := SleepManage{
GroupId: groupId,
UserId: userId,
SleepTime: now,
}
if err := db.Debug().Model(&SleepManage{}).Where("group_id = ? and user_id = ?", groupId, userId).First(&st).Error; err != nil {
// error handling...
if gorm.IsRecordNotFoundError(err) {
db.Debug().Model(&SleepManage{}).Create(&st) // newUser not user
}
} else {
log.Println("sleeptime为", st)
awakeTime = now.Sub(st.SleepTime)
db.Debug().Model(&SleepManage{}).Where("group_id = ? and user_id = ?", groupId, userId).Update(
map[string]interface{}{
"sleep_time": now,
})
}
db.Debug().Model(&SleepManage{}).Where("group_id = ? and sleep_time <= ? and sleep_time >= ?", groupId, now, today).Count(&position)
return position, awakeTime
}
// 更新起床时间
func (sdb *SleepDB) GetUp(groupId, userId int64) (position int, sleepTime time.Duration) {
db := (*gorm.DB)(sdb)
now := time.Now()
today := now.Add(-time.Hour*time.Duration(-6+now.Hour()) - time.Minute*time.Duration(now.Minute()) - time.Second*time.Duration(now.Second()))
st := SleepManage{
GroupId: groupId,
UserId: userId,
SleepTime: now,
}
if err := db.Debug().Model(&SleepManage{}).Where("group_id = ? and user_id = ?", groupId, userId).First(&st).Error; err != nil {
// error handling...
if gorm.IsRecordNotFoundError(err) {
db.Debug().Model(&SleepManage{}).Create(&st) // newUser not user
}
} else {
log.Println("sleeptime为", st)
sleepTime = now.Sub(st.SleepTime)
db.Debug().Model(&SleepManage{}).Where("group_id = ? and user_id = ?", groupId, userId).Update(
map[string]interface{}{
"sleep_time": now,
})
}
db.Debug().Model(&SleepManage{}).Where("group_id = ? and sleep_time <= ? and sleep_time >= ?", groupId, now, today).Count(&position)
return position, sleepTime
}

View File

@@ -0,0 +1,79 @@
// Package sleepmanage 睡眠管理
package sleepmanage
import (
"fmt"
"time"
log "github.com/sirupsen/logrus"
zero "github.com/wdvxdr1123/ZeroBot"
"github.com/wdvxdr1123/ZeroBot/message"
"github.com/FloatTech/ZeroBot-Plugin/control"
"github.com/FloatTech/ZeroBot-Plugin/plugin_sleep_manage/model"
)
const dbpath = "data/sleep/"
const dbfile = dbpath + "manage.db"
const prio = 4
var engine = control.Register("sleepmanage", &control.Options{
DisableOnDefault: false,
Help: "sleepmanage\n- 早安\n- 晚安",
})
func init() {
engine.OnFullMatch("早安", isMorning, zero.OnlyGroup).SetBlock(true).SetPriority(prio).
Handle(func(ctx *zero.Ctx) {
db, err := model.Open(dbfile)
if err != nil {
log.Errorln(err)
return
}
position, getUpTime := db.GetUp(ctx.Event.GroupID, ctx.Event.UserID)
log.Println(position, getUpTime)
hour, minute, second := timeDuration(getUpTime)
if (hour == 0 && minute == 0 && second == 0) || hour >= 24 {
ctx.SendChain(message.Reply(ctx.Event.MessageID), message.Text(fmt.Sprintf("早安成功!你是今天第%d个起床的", position)))
} else {
ctx.SendChain(message.Reply(ctx.Event.MessageID), message.Text(fmt.Sprintf("早安成功!你的睡眠时长为%d时%d分%d秒,你是今天第%d个起床的", hour, minute, second, position)))
}
db.Close()
})
engine.OnFullMatch("晚安", isEvening, zero.OnlyGroup).SetBlock(true).SetPriority(prio).
Handle(func(ctx *zero.Ctx) {
db, err := model.Open(dbfile)
if err != nil {
log.Errorln(err)
return
}
position, sleepTime := db.Sleep(ctx.Event.GroupID, ctx.Event.UserID)
log.Println(position, sleepTime)
hour, minute, second := timeDuration(sleepTime)
if (hour == 0 && minute == 0 && second == 0) || hour >= 24 {
ctx.SendChain(message.Reply(ctx.Event.MessageID), message.Text(fmt.Sprintf("晚安成功!你是今天第%d个睡觉的", position)))
} else {
ctx.SendChain(message.Reply(ctx.Event.MessageID), message.Text(fmt.Sprintf("晚安成功!你的清醒时长为%d时%d分%d秒,你是今天第%d个睡觉的", hour, minute, second, position)))
}
db.Close()
})
}
func timeDuration(time time.Duration) (hour, minute, second int64) {
hour = int64(time) / (1000 * 1000 * 1000 * 60 * 60)
minute = (int64(time) - hour*(1000*1000*1000*60*60)) / (1000 * 1000 * 1000 * 60)
second = (int64(time) - hour*(1000*1000*1000*60*60) - minute*(1000*1000*1000*60)) / (1000 * 1000 * 1000)
return hour, minute, second
}
// 只统计6点到12点的早安
func isMorning(ctx *zero.Ctx) bool {
now := time.Now().Hour()
return now >= 6 && now <= 12
}
// 只统计21点到凌晨3点的晚安
func isEvening(ctx *zero.Ctx) bool {
now := time.Now().Hour()
return now >= 21 || now <= 3
}

55
plugin_tracemoe/moe.go Normal file
View File

@@ -0,0 +1,55 @@
package tracemoe
import (
"fmt"
"github.com/FloatTech/AnimeAPI/picture"
trmoe "github.com/fumiama/gotracemoe"
zero "github.com/wdvxdr1123/ZeroBot"
"github.com/wdvxdr1123/ZeroBot/message"
"github.com/FloatTech/ZeroBot-Plugin/control"
)
var (
moe = trmoe.NewMoe("")
)
func init() { // 插件主体
engine := control.Register("tracemoe", &control.Options{
DisableOnDefault: false,
Help: "tracemoe\n- 搜番|搜索番剧[图片]",
})
// 以图搜图
engine.OnKeywordGroup([]string{"搜番", "搜索番剧"}, picture.CmdMatch, picture.MustGiven).SetBlock(true).ThirdPriority().
Handle(func(ctx *zero.Ctx) {
// 开始搜索图片
ctx.SendChain(message.Text("少女祈祷中......"))
for _, pic := range ctx.State["image_url"].([]string) {
fmt.Println(pic)
if result, err := moe.Search(pic, true, true); err != nil {
ctx.SendChain(message.Text("ERROR: ", err))
} else if len(result.Result) > 0 {
r := result.Result[0]
hint := "我有把握是这个!"
if r.Similarity < 0.8 {
hint = "大概是这个?"
}
mf := int(r.From / 60)
mt := int(r.To / 60)
sf := r.From - float32(mf*60)
st := r.To - float32(mt*60)
ctx.SendChain(
message.Text(hint),
message.Image(r.Image),
message.Text(
"\n",
"番剧名:", r.Anilist.Title.Native, "\n",
"话数:", r.Episode, "\n",
"时间:", mf, ":", sf, "-", mt, ":", st,
),
)
}
}
})
}

65
plugin_translation/tl.go Normal file
View File

@@ -0,0 +1,65 @@
// Package translation 翻译
package translation
import (
"errors"
"fmt"
"io/ioutil"
"net/http"
"time"
"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"
"github.com/FloatTech/ZeroBot-Plugin/utils/process"
)
var (
prio = 100
bucket = rate.NewManager(time.Minute, 20) // 接口回复
)
func tl(d string) ([]byte, error) {
url := "https://api.cloolc.club/fanyi?data=" + d
resp, err := http.Get(url)
if err != nil {
fmt.Println(err)
}
data, err := ioutil.ReadAll(resp.Body)
_ = resp.Body.Close()
if err != nil {
return nil, err
}
if code := resp.StatusCode; code != 200 {
// 如果返回不是200则立刻抛出错误
errmsg := fmt.Sprintf("code %d", code)
return nil, errors.New(errmsg)
}
return data, err
}
func init() {
control.Register("translation", &control.Options{
DisableOnDefault: false,
Help: "翻译\n" +
">TL 你好",
}).OnRegex(`^>TL\s(-.{1,10}? )?(.*)$`).SetBlock(true).SetPriority(prio).
Handle(func(ctx *zero.Ctx) {
if !bucket.Load(ctx.Event.UserID).Acquire() {
// 频繁触发,不回复
return
}
msg := []string{ctx.State["regex_matched"].([]string)[2]}
rely, err := tl(msg[0])
if err != nil {
ctx.SendChain(message.Text("ERROR: ", err))
}
info := gjson.ParseBytes(rely)
repo := info.Get("data.0")
process.SleepAbout1sTo2s()
ctx.SendChain(message.Text(repo.Get("value.0")))
})
}

View File

@@ -0,0 +1,41 @@
package vtbquotation
import (
"github.com/fumiama/cron"
log "github.com/sirupsen/logrus"
"github.com/FloatTech/ZeroBot-Plugin/plugin_vtb_quotation/model"
"github.com/FloatTech/ZeroBot-Plugin/utils/process"
)
func init() {
go func() {
process.SleepAbout1sTo2s()
log.Println("[vtb/cron] 开启vtb数据库日常更新")
vtbDaily()
}()
}
func vtbDaily() {
c := cron.New()
_, err := c.AddFunc("0 4 * * *", func() { vtbData() })
if err != nil {
log.Errorln("定时任务有错误:", err)
} else {
log.Println("开启vtb数据库定时任务")
c.Start()
}
}
func vtbData() {
db := model.Initialize(dbfile)
if db != nil {
for _, v := range db.GetVtbList() {
db.StoreVtb(v)
}
err := db.Close()
if err != nil {
log.Errorln("[vtb/cron]", err)
}
}
}

View File

@@ -0,0 +1,17 @@
package vtbquotation
import (
"os"
"github.com/FloatTech/ZeroBot-Plugin/utils/file"
"github.com/FloatTech/ZeroBot-Plugin/utils/process"
)
// 加载数据库
func init() {
go func() {
process.SleepAbout1sTo2s()
_ = os.MkdirAll(dbpath, 0755)
_, _ = file.GetLazyData(dbfile, false, true)
}()
}

View File

@@ -0,0 +1,363 @@
package model
import (
"io/ioutil"
"math/rand"
"net/http"
"os"
"strconv"
"strings"
"time"
"github.com/jinzhu/gorm"
_ "github.com/logoove/sqlite"
"github.com/sirupsen/logrus"
"github.com/tidwall/gjson"
)
type VtbDB gorm.DB
func Initialize(dbpath string) *VtbDB {
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(FirstCategory{}).AutoMigrate(SecondCategory{}).AutoMigrate(ThirdCategory{})
return (*VtbDB)(gdb)
}
func Open(dbpath string) (*VtbDB, error) {
db, err := gorm.Open("sqlite3", dbpath)
if err != nil {
return nil, err
} else {
return (*VtbDB)(db), nil
}
}
// FirstCategory 第一品类
type FirstCategory struct {
gorm.Model
FirstCategoryIndex int64 `gorm:"column:first_category_index"`
FirstCategoryName string `gorm:"column:first_category_name"`
FirstCategoryUid string `gorm:"column:first_category_uid"`
FirstCategoryDescription string `gorm:"column:first_category_description;type:varchar(1024)"`
FirstCategoryIconPath string `gorm:"column:first_category_icon_path"`
}
func (FirstCategory) TableName() string {
return "first_category"
}
// SecondCategory 第二品类
type SecondCategory struct {
gorm.Model
SecondCategoryIndex int64 `gorm:"column:second_category_index"`
FirstCategoryUid string `gorm:"column:first_category_uid;association_foreignkey:first_category_uid"`
SecondCategoryName string `gorm:"column:second_category_name"`
SecondCategoryAuthor string `gorm:"column:second_category_author"`
SecondCategoryDescription string `gorm:"column:second_category_description"`
}
func (SecondCategory) TableName() string {
return "second_category"
}
// ThirdCategory 第三品类
type ThirdCategory struct {
gorm.Model
ThirdCategoryIndex int64 `gorm:"column:third_category_index"`
SecondCategoryIndex int64 `gorm:"column:second_category_index"`
FirstCategoryUid string `gorm:"column:first_category_uid"`
ThirdCategoryName string `gorm:"column:third_category_name"`
ThirdCategoryPath string `gorm:"column:third_category_path"`
ThirdCategoryAuthor string `gorm:"column:third_category_author"`
ThirdCategoryDescription string `gorm:"column:third_category_description"`
}
func (ThirdCategory) TableName() string {
return "third_category"
}
// GetAllFirstCategoryMessage 取出所有vtb
func (vdb *VtbDB) GetAllFirstCategoryMessage() string {
db := (*gorm.DB)(vdb)
firstStepMessage := "请选择一个vtb并发送序号:\n"
var fc FirstCategory
rows, err := db.Model(&FirstCategory{}).Rows()
if err != nil {
logrus.Errorln("[vtb/model]数据库读取错误", err)
}
if rows == nil {
return ""
}
for rows.Next() {
db.ScanRows(rows, &fc)
// logrus.Println(fc)
firstStepMessage = firstStepMessage + strconv.FormatInt(fc.FirstCategoryIndex, 10) + ". " + fc.FirstCategoryName + "\n"
}
return firstStepMessage
}
// GetAllSecondCategoryMessageByFirstIndex 取得同一个vtb所有语录类别
func (vdb *VtbDB) GetAllSecondCategoryMessageByFirstIndex(firstIndex int) string {
db := (*gorm.DB)(vdb)
SecondStepMessage := "请选择一个语录类别并发送序号:\n"
var sc SecondCategory
var count int
var fc FirstCategory
db.Model(FirstCategory{}).Where("first_category_index = ?", firstIndex).First(&fc)
db.Model(&SecondCategory{}).Where("first_category_uid = ?", fc.FirstCategoryUid).Count(&count)
if count == 0 {
return ""
}
rows, err := db.Model(&SecondCategory{}).Where("first_category_uid = ?", fc.FirstCategoryUid).Rows()
if err != nil {
logrus.Errorln("[vtb/model]数据库读取错误", err)
}
for rows.Next() {
db.ScanRows(rows, &sc)
// logrus.Println(sc)
SecondStepMessage = SecondStepMessage + strconv.FormatInt(sc.SecondCategoryIndex, 10) + ". " + sc.SecondCategoryName + "\n"
}
return SecondStepMessage
}
// GetAllThirdCategoryMessageByFirstIndexAndSecondIndex 取得同一个vtb同个类别的所有语录
func (vdb *VtbDB) GetAllThirdCategoryMessageByFirstIndexAndSecondIndex(firstIndex, secondIndex int) string {
db := (*gorm.DB)(vdb)
ThirdStepMessage := "请选择一个语录并发送序号:\n"
var fc FirstCategory
db.Model(FirstCategory{}).Where("first_category_index = ?", firstIndex).First(&fc)
var count int
db.Model(&ThirdCategory{}).Where("first_category_uid = ? and second_category_index = ?", fc.FirstCategoryUid, secondIndex).Count(&count)
if count == 0 {
return ""
}
var tc ThirdCategory
rows, err := db.Model(&ThirdCategory{}).Where("first_category_uid = ? and second_category_index = ?", fc.FirstCategoryUid, secondIndex).Rows()
if err != nil {
logrus.Errorln("[vtb/model]数据库读取错误", err)
}
for rows.Next() {
db.ScanRows(rows, &tc)
// logrus.Println(tc)
ThirdStepMessage = ThirdStepMessage + strconv.FormatInt(tc.ThirdCategoryIndex, 10) + ". " + tc.ThirdCategoryName + "\n"
}
return ThirdStepMessage
}
// GetThirdCategory
func (vdb *VtbDB) GetThirdCategory(firstIndex, secondIndex, thirdIndex int) ThirdCategory {
db := (*gorm.DB)(vdb)
var fc FirstCategory
db.Model(FirstCategory{}).Where("first_category_index = ?", firstIndex).First(&fc)
var tc ThirdCategory
db.Model(&ThirdCategory{}).Where("first_category_uid = ? and second_category_index = ? and third_category_index = ?", fc.FirstCategoryUid, secondIndex, thirdIndex).Take(&tc)
return tc
}
func (vdb *VtbDB) RandomVtb() ThirdCategory {
db := (*gorm.DB)(vdb)
rand.Seed(time.Now().UnixNano())
var count int
db.Model(&ThirdCategory{}).Count(&count)
// logrus.Info("一共有", count, "个")
var tc ThirdCategory
db.Model(&ThirdCategory{}).Offset(rand.Intn(count)).Take(&tc)
// logrus.Info(tc)
return tc
}
func (vdb *VtbDB) GetFirstCategoryByFirstUid(firstUid string) FirstCategory {
db := (*gorm.DB)(vdb)
var fc FirstCategory
db.Model(FirstCategory{}).Where("first_category_uid = ?", firstUid).Take(&fc)
// logrus.Info(fc)
return fc
}
func (vdb *VtbDB) Close() error {
db := (*gorm.DB)(vdb)
return db.Close()
}
const vtbUrl = "https://vtbkeyboard.moe/api/get_vtb_list"
func (vdb *VtbDB) GetVtbList() (uidList []string) {
db := (*gorm.DB)(vdb)
client := &http.Client{}
req, err := http.NewRequest("GET", vtbUrl, nil)
if err != nil {
logrus.Errorln(err)
return
}
// 自定义Header
req.Header.Set("User-Agent", randua())
resp, err := client.Do(req)
if err != nil {
logrus.Errorln(err)
return
}
defer resp.Body.Close()
bytes, err := ioutil.ReadAll(resp.Body)
if err != nil {
logrus.Errorln(err)
return
}
vtbListStr, err := strconv.Unquote(strings.Replace(strconv.Quote(string(bytes)), `\\u`, `\u`, -1))
if err != nil {
logrus.Errorln(err)
return
}
count := gjson.Get(vtbListStr, "#").Int()
for i := int64(0); i < count; i++ {
item := gjson.Get(vtbListStr, strconv.FormatInt(i, 10))
logrus.Println(item)
fc := FirstCategory{
FirstCategoryIndex: i,
FirstCategoryName: item.Get("name").String(),
FirstCategoryDescription: item.Get("description").String(),
FirstCategoryIconPath: item.Get("icon_path").String(),
FirstCategoryUid: item.Get("uid").String(),
}
logrus.Println(fc)
if err := db.Debug().Model(&FirstCategory{}).Where("first_category_uid = ?", fc.FirstCategoryUid).First(&fc).Error; err != nil {
if gorm.IsRecordNotFoundError(err) {
db.Debug().Model(&FirstCategory{}).Create(&fc) // newUser not user
}
} else {
db.Debug().Model(&FirstCategory{}).Where("first_category_uid = ?", fc.FirstCategoryUid).Update(
map[string]interface{}{
"first_category_index": i,
"first_category_name": item.Get("name").String(),
"first_category_description": item.Get("description").String(),
"first_category_icon_path": item.Get("icon_path").String(),
})
}
uidList = append(uidList, fc.FirstCategoryUid)
}
return uidList
}
func (vdb *VtbDB) StoreVtb(uid string) {
db := (*gorm.DB)(vdb)
vtbUrl := "https://vtbkeyboard.moe/api/get_vtb_page?uid=" + uid
client := &http.Client{}
req, err := http.NewRequest("GET", vtbUrl, nil)
if err != nil {
logrus.Errorln(err)
return
}
// 自定义Header
req.Header.Set("User-Agent", randua())
resp, err := client.Do(req)
if err != nil {
logrus.Errorln(err)
return
}
defer resp.Body.Close()
bytes, err := ioutil.ReadAll(resp.Body)
if err != nil {
logrus.Errorln(err)
return
}
vtbStr, err := strconv.Unquote(strings.Replace(strconv.Quote(string(bytes)), `\\u`, `\u`, -1))
if err != nil {
logrus.Errorln(err)
return
}
secondCount := gjson.Get(vtbStr, "data.voices.#").Int()
logrus.Println("二级品类一共有", secondCount)
for secondIndex := int64(0); secondIndex < secondCount; secondIndex++ {
secondItem := gjson.Get(vtbStr, "data.voices."+strconv.FormatInt(secondIndex, 10))
logrus.Println(secondItem)
sc := SecondCategory{
SecondCategoryName: secondItem.Get("categoryName").String(),
SecondCategoryIndex: secondIndex,
SecondCategoryAuthor: secondItem.Get("author").String(),
SecondCategoryDescription: secondItem.Get("categoryDescription.zh-CN").String(),
FirstCategoryUid: uid,
}
if err := db.Debug().Model(&SecondCategory{}).Where("first_category_uid = ? and second_category_index = ?", uid, secondIndex).First(&sc).Error; err != nil {
// error handling...
if gorm.IsRecordNotFoundError(err) {
db.Debug().Model(&SecondCategory{}).Create(&sc) // newUser not user
}
} else {
db.Debug().Model(&SecondCategory{}).Where("first_category_uid = ? and second_category_index = ?", uid, secondIndex).Update(
map[string]interface{}{
"second_category_name": secondItem.Get("categoryName").String(),
"second_category_author": secondItem.Get("author").String(),
"second_category_description": secondItem.Get("categoryDescription.zh-CN").String(),
})
}
thirdCount := secondItem.Get("voiceList.#").Int()
logrus.Println("三级品类一共有", thirdCount)
for thirdIndex := int64(0); thirdIndex < thirdCount; thirdIndex++ {
thirdItem := secondItem.Get("voiceList." + strconv.FormatInt(thirdIndex, 10))
logrus.Println(thirdItem)
tc := ThirdCategory{
ThirdCategoryName: thirdItem.Get("name").String(),
ThirdCategoryIndex: thirdIndex,
ThirdCategoryDescription: thirdItem.Get("description.zh-CN").String(),
FirstCategoryUid: uid,
SecondCategoryIndex: secondIndex,
ThirdCategoryPath: thirdItem.Get("path").String(),
ThirdCategoryAuthor: thirdItem.Get("author").String(),
}
logrus.Println(tc)
if err := db.Debug().Model(&ThirdCategory{}).Where("first_category_uid = ? and second_category_index = ? and third_category_index = ?",
uid, secondIndex, thirdIndex).First(&tc).Error; err != nil {
if gorm.IsRecordNotFoundError(err) {
db.Debug().Model(&ThirdCategory{}).Create(&tc) // newUser not user
}
} else {
db.Debug().Model(&ThirdCategory{}).Where("first_category_uid = ? and second_category_index = ? and third_category_index = ?",
uid, secondIndex, thirdIndex).Update(
map[string]interface{}{
"third_category_name": thirdItem.Get("name").String(),
"third_category_description": thirdItem.Get("description.zh-CN").String(),
"third_category_path": thirdItem.Get("path").String(),
"third_category_author": thirdItem.Get("author").String(),
})
}
}
}
}
var agent = [...]string{
"Mozilla/5.0 (Windows NT 6.1; Win64; x64; rv:50.0) Gecko/20100101 Firefox/50.0",
"Opera/9.80 (Macintosh; Intel Mac OS X 10.6.8; U; en) Presto/2.8.131 Version/11.11",
"Opera/9.80 (Windows NT 6.1; U; en) Presto/2.8.131 Version/11.11",
"Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 5.1; 360SE)",
"Mozilla/5.0 (Windows NT 6.1; rv:2.0.1) Gecko/20100101 Firefox/4.0.1",
"Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 5.1; The World)",
"User-Agent,Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10_6_8; en-us) AppleWebKit/534.50 (KHTML, like Gecko) Version/5.1 Safari/534.50",
"User-Agent, Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 5.1; Maxthon 2.0)",
"User-Agent,Mozilla/5.0 (Windows; U; Windows NT 6.1; en-us) AppleWebKit/534.50 (KHTML, like Gecko) Version/5.1 Safari/534.50",
}
func randua() string {
return agent[rand.New(rand.NewSource(time.Now().UnixNano())).Intn(len(agent))]
}

View File

@@ -0,0 +1,161 @@
package vtbquotation
import (
"net/url"
"regexp"
"strconv"
"strings"
"time"
_ "github.com/logoove/sqlite"
"github.com/sirupsen/logrus"
zero "github.com/wdvxdr1123/ZeroBot"
"github.com/wdvxdr1123/ZeroBot/message"
"github.com/FloatTech/ZeroBot-Plugin/control"
"github.com/FloatTech/ZeroBot-Plugin/plugin_vtb_quotation/model"
)
const regStr = ".*/(.*)"
const dbpath = "data/VtbQuotation/"
const dbfile = dbpath + "vtb.db"
var engine = control.Register("vtbquotation", &control.Options{
DisableOnDefault: false,
Help: "vtbkeyboard.moe\n- vtb语录\n- 随机vtb",
})
func init() {
engine.OnFullMatch("vtb语录").SetBlock(true).
Handle(func(ctx *zero.Ctx) {
var firstIndex int
var secondIndex int
var thirdIndex int
echo, cancel := ctx.FutureEvent("message",
ctx.CheckSession()). // 只复读开启复读模式的人的消息
Repeat() // 不断监听复读
db, err := model.Open(dbfile)
if err != nil {
logrus.Errorln(err)
return
}
defer db.Close()
defer cancel()
firstStepMessage := db.GetAllFirstCategoryMessage()
if id := ctx.SendChain(message.Reply(ctx.Event.MessageID), message.Text(firstStepMessage)); id == 0 {
ctx.SendChain(message.Text("ERROR: 可能被风控了"))
}
// 步骤012依次选择3个类别
step := 0
// 错误次数
errorCount := 0
for {
select {
case e := <-echo: // 接收到需要复读的消息
// 错误次数达到3次结束命令
if errorCount >= 3 {
ctx.SendChain(message.Reply(e.MessageID), message.Text("输入错误太多,请重新发指令"))
return
}
switch step {
case 0:
firstIndex, err = strconv.Atoi(e.RawMessage)
// log.Println(fmt.Sprintf("当前在第%d步", step))
// log.Println(fmt.Sprintf("firstIndex:%d,secondIndex:%d,thirdIndex:%d", firstIndex, secondIndex, thirdIndex))
if err != nil {
ctx.SendChain(message.Reply(e.MessageID), message.Text("请输入正确的序号,三次输入错误,指令可退出重输"))
errorCount++
} else {
SecondStepMessage := db.GetAllSecondCategoryMessageByFirstIndex(firstIndex)
// log.Println(SecondStepMessage)
if SecondStepMessage == "" {
ctx.SendChain(message.Reply(e.MessageID), message.Text("你选择的序号没有内容,请重新选择,三次输入错误,指令可退出重输"))
ctx.SendChain(message.Reply(e.MessageID), message.Text(db.GetAllFirstCategoryMessage()))
errorCount++
} else {
ctx.SendChain(message.Reply(e.MessageID), message.Text(SecondStepMessage))
step++
}
}
case 1:
secondIndex, err = strconv.Atoi(e.RawMessage)
// log.Println(fmt.Sprintf("当前在第%d步", step))
// log.Println(fmt.Sprintf("firstIndex:%d,secondIndex:%d,thirdIndex:%d", firstIndex, secondIndex, thirdIndex))
if err != nil {
ctx.SendChain(message.Reply(e.MessageID), message.Text("请输入正确的序号,三次输入错误,指令可退出重输"))
errorCount++
} else {
ThirdStepMessage := db.GetAllThirdCategoryMessageByFirstIndexAndSecondIndex(firstIndex, secondIndex)
// log.Println(ThirdStepMessage)
if ThirdStepMessage == "" {
ctx.SendChain(message.Reply(e.MessageID), message.Text("你选择的序号没有内容,请重新选择,三次输入错误,指令可退出重输"))
ctx.SendChain(message.Reply(e.MessageID), message.Text(db.GetAllSecondCategoryMessageByFirstIndex(firstIndex)))
errorCount++
} else {
ctx.SendChain(message.Reply(e.MessageID), message.Text(ThirdStepMessage))
step++
}
}
case 2:
thirdIndex, err = strconv.Atoi(e.RawMessage)
// log.Println(fmt.Sprintf("当前在第%d步", step))
// log.Println(fmt.Sprintf("firstIndex:%d,secondIndex:%d,thirdIndex:%d", firstIndex, secondIndex, thirdIndex))
if err != nil {
ctx.SendChain(message.Reply(e.MessageID), message.Text("请输入正确的序号,三次输入错误,指令可退出重输"))
errorCount++
} else {
tc := db.GetThirdCategory(firstIndex, secondIndex, thirdIndex)
reg := regexp.MustCompile(regStr)
recordUrl := tc.ThirdCategoryPath
if recordUrl == "" {
ctx.SendChain(message.Reply(e.MessageID), message.Text("没有内容请重新选择,三次输入错误,指令可退出重输"))
ctx.SendChain(message.Reply(e.MessageID), message.Text(db.GetAllFirstCategoryMessage()))
errorCount++
step = 1
} else {
if reg.MatchString(recordUrl) {
// log.Println(reg.FindStringSubmatch(recordUrl)[1])
// log.Println(url.QueryEscape(reg.FindStringSubmatch(recordUrl)[1]))
recordUrl = strings.Replace(recordUrl, reg.FindStringSubmatch(recordUrl)[1], url.QueryEscape(reg.FindStringSubmatch(recordUrl)[1]), -1)
recordUrl = strings.Replace(recordUrl, "+", "%20", -1)
// log.Println(recordUrl)
}
ctx.SendChain(message.Reply(e.MessageID), message.Text("请欣赏《"+tc.ThirdCategoryName+"》"))
ctx.SendChain(message.Record(recordUrl))
return
}
}
default:
return
}
case <-time.After(time.Second * 60):
ctx.SendChain(message.Reply(ctx.Event.MessageID), message.Text("vtb语录指令过期"))
return
}
}
})
engine.OnFullMatch("随机vtb").SetBlock(true).
Handle(func(ctx *zero.Ctx) {
db, err := model.Open(dbfile)
if err != nil {
logrus.Errorln(err)
return
}
tc := db.RandomVtb()
fc := db.GetFirstCategoryByFirstUid(tc.FirstCategoryUid)
if (tc != model.ThirdCategory{}) && (fc != model.FirstCategory{}) {
reg := regexp.MustCompile(regStr)
recordUrl := tc.ThirdCategoryPath
if reg.MatchString(recordUrl) {
// log.Println(reg.FindStringSubmatch(recordUrl)[1])
// log.Println(url.QueryEscape(reg.FindStringSubmatch(recordUrl)[1]))
recordUrl = strings.Replace(recordUrl, reg.FindStringSubmatch(recordUrl)[1], url.QueryEscape(reg.FindStringSubmatch(recordUrl)[1]), -1)
recordUrl = strings.Replace(recordUrl, "+", "%20", -1)
// log.Println(recordUrl)
}
ctx.SendChain(message.Reply(ctx.Event.MessageID), message.Text("请欣赏"+fc.FirstCategoryName+"的《"+tc.ThirdCategoryName+"》"))
ctx.SendChain(message.Record(recordUrl))
}
db.Close()
})
}

72
plugin_wtf/main.go Normal file
View File

@@ -0,0 +1,72 @@
package wtf
import (
"fmt"
"strconv"
"time"
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*5, 5)
)
func init() {
en := control.Register("wtf", &control.Options{
DisableOnDefault: false,
Help: "鬼东西\n- 鬼东西列表\n- 查询鬼东西[序号][@xxx]",
})
en.OnFullMatch("鬼东西列表").SetBlock(true).SetPriority(30).
Handle(func(ctx *zero.Ctx) {
s := ""
for i, w := range table {
s += fmt.Sprintf("%02d. %s\n", i, w.name)
i++
}
ctx.SendChain(message.Text(s))
})
en.OnRegex(`^查询鬼东西(\d*)`).SetBlock(true).SetPriority(30).
Handle(func(ctx *zero.Ctx) {
if !limit.Load(ctx.Event.UserID).Acquire() {
ctx.SendChain(message.Text("请稍后重试0x0..."))
return
}
// 调用接口
i, err := strconv.Atoi(ctx.State["regex_matched"].([]string)[1])
if err != nil {
ctx.SendChain(message.Text("ERROR: ", err))
return
}
w := NewWtf(i)
if w == nil {
ctx.SendChain(message.Text("没有这项内容!"))
return
}
// 获取名字
var name string
var secondname string
if len(ctx.Event.Message) > 1 && ctx.Event.Message[1].Type == "at" {
qq, _ := strconv.ParseInt(ctx.Event.Message[1].Data["qq"], 10, 64)
secondname = ctx.GetGroupMemberInfo(ctx.Event.GroupID, qq, false).Get("nickname").Str
}
name = ctx.Event.Sender.NickName
var text string
if secondname != "" {
text, err = w.Predict(name, secondname)
} else {
text, err = w.Predict(name)
}
if err != nil {
ctx.SendChain(message.Text("ERROR: ", err))
return
}
// TODO: 可注入
ctx.Send(text)
})
}

155
plugin_wtf/model.go Normal file
View File

@@ -0,0 +1,155 @@
package wtf
import (
"encoding/json"
"errors"
"io"
"net/http"
"net/url"
)
/* JS path getter for https://wtf.hiigara.net/ranking
a = document.getElementById("testList").getElementsByTagName("a")
s = ""
for(i=0; i<a.length; i++) {
s += "\"" + a[i].innerText + "\":\"" + a[i].href + "\",\n";
}
*/
const apiprefix = "https://wtf.hiigara.net/api/run/"
type Wtf struct {
name string
path string
}
var table = [...]*Wtf{
{"你的意义是什么?", "mRIFuS"},
{"【ABO】性別和信息素", "KXyy9"},
{"测测cp", "ZoGXQd"},
{"xxx和xxx的關係是", "L4HfA"},
{"在JOJO世界你的替身会是什么", "lj0a8o"},
{"稱號產生器", "titlegen"},
{"成分报告", "2PCeo1"},
{"測驗你跟你的朋友是攻/受", "LkQXO3"},
{"测试两人的关系?", "uwjQQt"},
{"【Fate系列】當你成為了從者 2.0", "LHStH2"},
{"想不到自己未來要做什麼工作嗎?", "D1agGa"},
{"(σ゚∀゚)σ名字產生器", "LNxXq7"},
{"人設生產器", "LBtPu5"},
{"測驗你在ABO世界的訊息素", "SwmdU"},
{"爱是什么", "llpBEY"},
{"測測你和哪位名人相似?", "RHQeXu"},
{"S/M测试", "Ga47oZ"},
{"测测你是谁", "aV1AEi"},
{"取個綽號吧", "LTkyUy"},
{"什麼都不是", "vyrSCb"},
{"今天中午吃什麼", "LdS4K6"},
{"測試你的中二稱號", "LwUmQ6"},
{"神奇海螺", "Lon1h7"},
{"ABO測試", "H1Tgd"},
{"女主角姓名產生器", "MsQBTd"},
{"您是什么人", "49PwSd"},
{"如果你成为了干员", "ok5e7n"},
{"abo人设生成~", "Di8enA"},
{"✡你的命運✡塔羅占卜🔮", "ohCzID"},
{"小說大綱生產器", "Lnstjz"},
{"他会喜欢你吗?", "pezX3a"},
{"抽签!你明年的今天会干什么", "IF31kS"},
{"如果你是受,會是哪種受呢?", "Dr6zpF"},
{"cp文梗", "vEO2KD"},
{"您是什么人?", "TQ5qyl"},
{"你成為......的機率", "g0uoBL"},
{"ABO性別與信息素", "KFPju"},
{"異國名稱產生器(國家、人名、星球...)", "OBpu4"},
{"對方到底喜不喜歡你", "JSLoZC"},
{"【脑叶公司】测一测你在脑叶公司的经历", "uPBhjC"},
{"当你成为魔法少女", "7ZiGcJ"},
{"你是yyds吗?", "SpBnCa"},
{"○○喜歡你嗎?", "S6Uceo"},
{"测测你的sm属性", "dOtcO5"},
{"你/妳究竟是攻還是受呢?", "RXALH"},
{"神秘藏书阁", "tDRyET"},
{"中午吃什么?", "L0Wsis"},
{"十年后你cp的结局是", "VUwnXQ"},
{"高维宇宙与常数的你", "6Zql97"},
{"色色的東東", "o2eg74"},
{"文章標題產生器", "Ky25WO"},
{"你的成績怎麼樣", "6kZv69"},
{"智能SM偵測器ヾ(*ΦωΦ)ツ", "9pY6HQ"},
{"你的使用注意事項", "La4Gir"},
{"戀愛指數", "Jsgz0"},
{"测试你今晚拉的屎", "N8dbcL"},
{"成為情侶的機率ᶫᵒᵛᵉᵧₒᵤ♥", "eDURch"},
{"他對你...", "CJxHMf"},
{"你的明日方舟人际关系", "u5z4Mw"},
{"日本姓氏產生器", "JJ5Ctb"},
{"當你轉生到了異世界,你將成為...", "FTpwK"},
{"魔幻世界大穿越2.0", "wUATOq"},
{"未來男朋友", "F3dSV"},
{"ABO與信息素", "KFOGA"},
{"你必將就這樣一事無成啊アホ", "RWw9oX"},
{"用習慣舉手的方式測試你的戀愛運!<3", "wv5bzA"},
{"攻受", "RaKmY"},
{"你和你喜歡的人的微h寵溺段子XD", "LdQqGz"},
{"我的藝名", "LBaTx"},
{"你是什麼神?", "LqZORE"},
{"你的起源是什麼?", "HXWwC"},
{"測你喜歡什麼", "Sue5g2"},
{"看看朋友的秘密", "PgKb8r"},
{"你在動漫裡的名字", "Lz82V7"},
{"小說男角名字產生器", "LyGDRr"},
{"測試短文", "S48yA"},
{"我們兩人在一起的機率......", "LBZbgE"},
{"創造小故事", "Kjy3AS"},
{"你的另外一個名字", "LuyYQA"},
{"與你最匹配的攻君屬性 ", "I7pxy"},
{"英文全名生產器(女)", "HcYbq"},
{"BL文章生產器", "LBZMO"},
{"輕小說書名產生器", "NFucA"},
{"長相評分", "2cQSDP"},
{"日本名字產生器(女孩子)", "JRiKv"},
{"中二技能名產生器", "Ky1BA"},
{"抽籤", "XqxfuH"},
{"你的蘿莉控程度全國排名", "IIWh9k"},
}
func NewWtf(index int) *Wtf {
if index >= 0 && index < len(table) {
return table[index]
}
return nil
}
type result struct {
Text string `json:"text"`
// Path string `json:"path"`
Ok bool `json:"ok"`
Msg string `json:"msg"`
}
func (w *Wtf) Predict(names ...string) (string, error) {
name := ""
for _, n := range names {
name += "/" + url.QueryEscape(n)
}
u := apiprefix + w.path + name
resp, err := http.Get(u)
if err != nil {
return "", err
}
r, err := io.ReadAll(resp.Body)
resp.Body.Close()
if err != nil {
return "", err
}
re := new(result)
err = json.Unmarshal(r, re)
if err != nil {
return "", err
}
if re.Ok {
return "> " + w.name + "\n" + re.Text, nil
}
return "", errors.New(re.Msg)
}

29
utils/binary/encode.go Normal file
View File

@@ -0,0 +1,29 @@
package binary
import (
"bytes"
"io/ioutil"
"golang.org/x/text/encoding/simplifiedchinese"
"golang.org/x/text/transform"
)
// GBK2UTF8 GBK 转 UTF-8
func GBK2UTF8(s []byte) ([]byte, error) {
reader := transform.NewReader(bytes.NewReader(s), simplifiedchinese.GBK.NewDecoder())
d, e := ioutil.ReadAll(reader)
if e != nil {
return nil, e
}
return d, nil
}
// UTF82GBK UTF-8 转 GBK
func UTF82GBK(s []byte) ([]byte, error) {
reader := transform.NewReader(bytes.NewReader(s), simplifiedchinese.GBK.NewEncoder())
d, e := ioutil.ReadAll(reader)
if e != nil {
return nil, e
}
return d, nil
}

12
utils/file/dir_nowin.go Normal file
View File

@@ -0,0 +1,12 @@
//go:build !windows
// +build !windows
package file
import "os"
// Pwd 获取当前路径
func Pwd() (path string) {
path, _ = os.Getwd()
return
}

15
utils/file/dir_win.go Normal file
View File

@@ -0,0 +1,15 @@
//go:build windows
// +build windows
package file
import (
"os"
"strings"
)
// Pwd 获取当前路径的正斜杠表示
func Pwd() string {
path, _ := os.Getwd()
return strings.ReplaceAll(path, "\\", "/")
}

37
utils/file/dl.go Normal file
View File

@@ -0,0 +1,37 @@
// Package file 文件实用工具
package file
import (
"crypto/tls"
"io"
"net/http"
"os"
)
var (
tr = &http.Transport{
TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
}
nochkcrtcli = &http.Client{Transport: tr}
)
// DownloadTo 下载到路径
func DownloadTo(url, file string, chkcrt bool) error {
var resp *http.Response
var err error
if chkcrt {
resp, err = http.Get(url)
} else {
resp, err = nochkcrtcli.Get(url)
}
if err == nil {
var f *os.File
f, err = os.Create(file)
if err == nil {
_, err = io.Copy(f, resp.Body)
resp.Body.Close()
f.Close()
}
}
return err
}

18
utils/file/f.go Normal file
View File

@@ -0,0 +1,18 @@
package file
import "os"
// BOTPATH BOT当前路径
var BOTPATH = Pwd()
// IsExist 文件/路径存在
func IsExist(path string) bool {
_, err := os.Stat(path)
return err == nil || os.IsExist(err)
}
// IsNotExist 文件/路径不存在
func IsNotExist(path string) bool {
_, err := os.Stat(path)
return err != nil && os.IsNotExist(err)
}

105
utils/file/updater.go Normal file
View File

@@ -0,0 +1,105 @@
package file
import (
"crypto/md5"
"encoding/hex"
"errors"
"io"
"net/http"
"os"
"sync"
"unsafe"
reg "github.com/fumiama/go-registry"
"github.com/sirupsen/logrus"
)
const (
dataurl = "https://codechina.csdn.net/u011570312/ZeroBot-Plugin/-/raw/master/"
)
var (
registry = reg.NewRegReader("reilia.eastasia.azurecontainer.io:32664", "fumiama")
lzmu sync.Mutex
)
func GetLazyData(path string, isReturnDataBytes, isDataMustEqual bool) ([]byte, error) {
var data []byte
var resp *http.Response
var filemd5 *[16]byte
var ms string
u := dataurl + path
lzmu.Lock()
logrus.Infoln("[file]检查懒加载文件:", path)
err := registry.Connect()
if err != nil {
logrus.Errorln("[file]无法连接到md5验证服务器请自行确保下载文件的正确性:", err)
} else {
ms, err = registry.Get(path)
if err != nil || len(ms) != 16 {
logrus.Errorln("[file]获取md5失败请自行确保下载文件 %s 的正确性:", path, err)
} else {
filemd5 = (*[16]byte)(*(*unsafe.Pointer)(unsafe.Pointer(&ms)))
logrus.Infoln("[file]从验证服务器获得文件md5:", hex.EncodeToString(filemd5[:]))
}
}
_ = registry.Close()
lzmu.Unlock()
if IsExist(path) {
data, err = os.ReadFile(path)
if err != nil {
return nil, err
}
if filemd5 != nil {
if md5.Sum(data) == *filemd5 {
logrus.Infoln("[file]文件md5匹配文件已存在且为最新")
goto ret
} else if !isDataMustEqual {
logrus.Warnln("[file]文件", path, "md5不匹配但不主动更新")
goto ret
}
logrus.Infoln("[file]文件md5不匹配开始更新文件")
} else {
logrus.Warnln("[file]文件", path, "存在已跳过md5检查")
goto ret
}
}
// 下载
resp, err = http.Get(u)
if err != nil {
return nil, err
}
defer resp.Body.Close()
if resp.ContentLength <= 0 {
return nil, errors.New("resp body len <= 0")
}
logrus.Printf("[file]从镜像下载数据%d字节...", resp.ContentLength)
// 读取数据
data, err = io.ReadAll(resp.Body)
if err != nil {
return nil, err
}
if len(data) == 0 {
return nil, errors.New("read body len <= 0")
}
if filemd5 != nil {
if md5.Sum(data) == *filemd5 {
logrus.Infoln("[file]文件下载完成md5匹配开始保存")
} else {
logrus.Errorln("[file]文件", path, "md5不匹配下载失败")
return nil, errors.New("file md5 mismatch")
}
} else {
logrus.Warnln("[file]文件", path, "下载完成已跳过md5检查开始保存")
}
// 写入数据
err = os.WriteFile(path, data, 0644)
ret:
if isReturnDataBytes {
return data, err
}
return nil, err
}

26
utils/math/math.go Normal file
View File

@@ -0,0 +1,26 @@
// Package math 计算实用工具
package math
// Max 返回两数最大值,该函数将被内联
func Max(a, b int) int {
if a > b {
return a
}
return b
}
// Min 返回两数最小值,该函数将被内联
func Min(a, b int) int {
if a > b {
return b
}
return a
}
// Abs 返回绝对值,该函数将被内联
func Abs(x int) int {
if x < 0 {
return -x
}
return x
}

12
utils/process/sleep.go Normal file
View File

@@ -0,0 +1,12 @@
// Package process 流程控制相关
package process
import (
"math/rand"
"time"
)
// SleepAbout1sTo2s 随机阻塞等待 1 ~ 2s
func SleepAbout1sTo2s() {
time.Sleep(time.Second + time.Millisecond*time.Duration(rand.Intn(1000)))
}

17
utils/rule/extension.go Normal file
View File

@@ -0,0 +1,17 @@
// Package rule zb 规则扩展
package rule
import zero "github.com/wdvxdr1123/ZeroBot"
// FirstValueInList 判断正则匹配的第一个参数是否在列表中
func FirstValueInList(list []string) zero.Rule {
return func(ctx *zero.Ctx) bool {
first := ctx.State["regex_matched"].([]string)[1]
for _, v := range list {
if first == v {
return true
}
}
return false
}
}

View File

@@ -1,12 +1,13 @@
// Package data 数据库/数据处理相关工具
package data
// Package sql 数据库/数据处理相关工具
package sql
import (
"database/sql"
"errors"
"reflect"
"strings"
_ "modernc.org/sqlite" // 引入sqlite
_ "github.com/logoove/sqlite" // 引入sqlite
)
// Sqlite 数据库对象
@@ -15,12 +16,33 @@ type Sqlite struct {
DBPath string
}
// Open 打开数据库
func (db *Sqlite) Open() (err error) {
if db.DB == nil {
database, err := sql.Open("sqlite3", db.DBPath)
if err != nil {
return err
}
db.DB = database
}
return
}
// Close 关闭数据库
func (db *Sqlite) Close() (err error) {
if db.DB != nil {
err = db.DB.Close()
db.DB = nil
}
return
}
// Create 生成数据库
// 默认结构体的第一个元素为主键
// 返回错误
func (db *Sqlite) Create(table string, objptr interface{}) (err error) {
if db.DB == nil {
database, err := sql.Open("sqlite", db.DBPath)
database, err := sql.Open("sqlite3", db.DBPath)
if err != nil {
return err
}
@@ -48,17 +70,16 @@ func (db *Sqlite) Create(table string, objptr interface{}) (err error) {
cmd = append(cmd, "NULL);")
}
}
if _, err := db.DB.Exec(strings.Join(cmd, " ")); err != nil {
return err
}
return nil
_, err = db.DB.Exec(strings.Join(cmd, " ") + ";")
return
}
// Insert 插入数据集
// 如果 PK 存在会覆盖
// 默认结构体的第一个元素为主键
// 返回错误
func (db *Sqlite) Insert(table string, objptr interface{}) (err error) {
rows, err := db.DB.Query("SELECT * FROM " + table)
func (db *Sqlite) Insert(table string, objptr interface{}) error {
rows, err := db.DB.Query("SELECT * FROM " + table + " limit 1;")
if err != nil {
return err
}
@@ -68,9 +89,9 @@ func (db *Sqlite) Insert(table string, objptr interface{}) (err error) {
tags, _ := rows.Columns()
rows.Close()
var (
values = values(objptr)
top = len(tags) - 1
cmd = []string{}
vals = values(objptr)
top = len(tags) - 1
cmd = []string{}
)
cmd = append(cmd, "REPLACE INTO")
cmd = append(cmd, table)
@@ -102,27 +123,87 @@ func (db *Sqlite) Insert(table string, objptr interface{}) (err error) {
cmd = append(cmd, ")")
}
}
stmt, err := db.DB.Prepare(strings.Join(cmd, " "))
stmt, err := db.DB.Prepare(strings.Join(cmd, " ") + ";")
if err != nil {
return err
}
_, err = stmt.Exec(values...)
_, err = stmt.Exec(vals...)
if err != nil {
return err
}
return nil
return stmt.Close()
}
// Find 查询数据
// InsertUnique 插入数据
// 如果 PK 存在会报错
// 默认结构体的第一个元素为主键
// 返回错误
func (db *Sqlite) InsertUnique(table string, objptr interface{}) error {
rows, err := db.DB.Query("SELECT * FROM " + table + " limit 1;")
if err != nil {
return err
}
if rows.Err() != nil {
return rows.Err()
}
tags, _ := rows.Columns()
rows.Close()
var (
vals = values(objptr)
top = len(tags) - 1
cmd = []string{}
)
cmd = append(cmd, "INSERT INTO")
cmd = append(cmd, table)
for i := range tags {
switch i {
default:
cmd = append(cmd, tags[i])
cmd = append(cmd, ",")
case 0:
cmd = append(cmd, "(")
cmd = append(cmd, tags[i])
cmd = append(cmd, ",")
case top:
cmd = append(cmd, tags[i])
cmd = append(cmd, ")")
}
}
for i := range tags {
switch i {
default:
cmd = append(cmd, "?")
cmd = append(cmd, ",")
case 0:
cmd = append(cmd, "VALUES (")
cmd = append(cmd, "?")
cmd = append(cmd, ",")
case top:
cmd = append(cmd, "?")
cmd = append(cmd, ")")
}
}
stmt, err := db.DB.Prepare(strings.Join(cmd, " ") + ";")
if err != nil {
return err
}
_, err = stmt.Exec(vals...)
if err != nil {
return err
}
return stmt.Close()
}
// Find 查询数据库,写入最后一条结果到 objptr
// condition 可为"WHERE id = 0"
// 默认字段与结构体元素顺序一致
// 返回错误
func (db *Sqlite) Find(table string, objptr interface{}, condition string) (err error) {
func (db *Sqlite) Find(table string, objptr interface{}, condition string) error {
var cmd = []string{}
cmd = append(cmd, "SELECT * FROM ")
cmd = append(cmd, table)
cmd = append(cmd, condition)
rows, err := db.DB.Query(strings.Join(cmd, " "))
rows, err := db.DB.Query(strings.Join(cmd, " ") + ";")
if err != nil {
return err
}
@@ -131,16 +212,84 @@ func (db *Sqlite) Find(table string, objptr interface{}, condition string) (err
}
defer rows.Close()
if !rows.Next() {
return errors.New("sql.Find: null result")
}
err = rows.Scan(addrs(objptr)...)
for rows.Next() {
if err != nil {
return err
}
err = rows.Scan(addrs(objptr)...)
}
return err
}
// CanFind 查询数据库是否有 condition
// condition 可为"WHERE id = 0"
// 默认字段与结构体元素顺序一致
// 返回错误
func (db *Sqlite) CanFind(table string, condition string) bool {
var cmd = []string{}
cmd = append(cmd, "SELECT * FROM")
cmd = append(cmd, table)
cmd = append(cmd, condition)
rows, err := db.DB.Query(strings.Join(cmd, " ") + ";")
if err != nil {
return false
}
if rows.Err() != nil {
return false
}
defer rows.Close()
if !rows.Next() {
return false
}
_ = rows.Close()
return true
}
// FindFor 查询数据库,用函数 f 遍历结果
// condition 可为"WHERE id = 0"
// 默认字段与结构体元素顺序一致
// 返回错误
func (db *Sqlite) FindFor(table string, objptr interface{}, condition string, f func() error) error {
var cmd = []string{}
cmd = append(cmd, "SELECT * FROM")
cmd = append(cmd, table)
cmd = append(cmd, condition)
rows, err := db.DB.Query(strings.Join(cmd, " ") + ";")
if err != nil {
return err
}
if rows.Err() != nil {
return rows.Err()
}
defer rows.Close()
if !rows.Next() {
return errors.New("sql.FindFor: null result")
}
err = rows.Scan(addrs(objptr)...)
if err == nil {
err = f()
}
for rows.Next() {
if err != nil {
return err
}
err = rows.Scan(addrs(objptr)...)
if err == nil {
err = f()
}
}
return nil
return err
}
// Pick 从 table 随机一行
func (db *Sqlite) Pick(table string, objptr interface{}) error {
return db.Find(table, objptr, "ORDER BY RANDOM() limit 1")
}
// ListTables 列出所有表名
@@ -168,15 +317,15 @@ func (db *Sqlite) ListTables() (s []string, err error) {
return
}
// Del 删除数据库
// Del 删除数据库表项
// condition 可为"WHERE id = 0"
// 返回错误
func (db *Sqlite) Del(table string, condition string) (err error) {
func (db *Sqlite) Del(table string, condition string) error {
var cmd = []string{}
cmd = append(cmd, "DELETE FROM")
cmd = append(cmd, table)
cmd = append(cmd, condition)
stmt, err := db.DB.Prepare(strings.Join(cmd, " "))
stmt, err := db.DB.Prepare(strings.Join(cmd, " ") + ";")
if err != nil {
return err
}
@@ -184,27 +333,43 @@ func (db *Sqlite) Del(table string, condition string) (err error) {
if err != nil {
return err
}
return nil
return stmt.Close()
}
// Truncate 清空数据库表
func (db *Sqlite) Truncate(table string) error {
var cmd = []string{}
cmd = append(cmd, "TRUNCATE TABLE")
cmd = append(cmd, table)
stmt, err := db.DB.Prepare(strings.Join(cmd, " ") + ";")
if err != nil {
return err
}
_, err = stmt.Exec()
if err != nil {
return err
}
return stmt.Close()
}
// Count 查询数据库行数
// 返回行数以及错误
func (db *Sqlite) Count(table string) (num int, err error) {
var cmd = []string{}
cmd = append(cmd, "SELECT * FROM")
cmd = append(cmd, "SELECT COUNT(1) FROM")
cmd = append(cmd, table)
rows, err := db.DB.Query(strings.Join(cmd, " "))
rows, err := db.DB.Query(strings.Join(cmd, " ") + ";")
if err != nil {
return num, err
}
if rows.Err() != nil {
return num, rows.Err()
}
defer rows.Close()
for rows.Next() {
num++
if rows.Next() {
err = rows.Scan(&num)
}
return num, nil
rows.Close()
return num, err
}
// tags 反射 返回结构体对象的 tag 数组
@@ -231,8 +396,22 @@ func kinds(objptr interface{}) []string {
}
for i, flen := 0, elem.Type().NumField(); i < flen; i++ {
switch elem.Field(i).Type().String() {
case "int64":
case "int8":
kinds = append(kinds, "TINYINT")
case "uint8", "byte":
kinds = append(kinds, "UNSIGNED TINYINT")
case "int16":
kinds = append(kinds, "SMALLINT")
case "uint16":
kinds = append(kinds, "UNSIGNED SMALLINT")
case "int32":
kinds = append(kinds, "INT")
case "uint32":
kinds = append(kinds, "UNSIGNED INT")
case "int64":
kinds = append(kinds, "BIGINT")
case "uint64":
kinds = append(kinds, "UNSIGNED BIGINT")
case "string":
kinds = append(kinds, "TEXT")
default:
@@ -251,14 +430,7 @@ func values(objptr interface{}) []interface{} {
elem = elem.Field(0)
}
for i, flen := 0, elem.Type().NumField(); i < flen; i++ {
switch elem.Field(i).Type().String() {
case "int64":
values = append(values, elem.Field(i).Int())
case "string":
values = append(values, elem.Field(i).String())
default:
values = append(values, elem.Field(i).String())
}
values = append(values, elem.Field(i).Interface())
}
return values
}

44
utils/web/http.go Normal file
View File

@@ -0,0 +1,44 @@
// Package web 网络处理相关
package web
import (
"errors"
"io"
"net/http"
)
// ReqWith 使用自定义请求头获取数据
func ReqWith(url string, method string, referer string, ua string) (data []byte, err error) {
client := &http.Client{}
// 提交请求
var reqest *http.Request
reqest, err = http.NewRequest(method, url, nil)
if err == nil {
// 增加header选项
reqest.Header.Add("Referer", referer)
reqest.Header.Add("User-Agent", ua)
var response *http.Response
response, err = client.Do(reqest)
if err == nil {
data, err = io.ReadAll(response.Body)
response.Body.Close()
}
}
return
}
// GetData 获取数据
func GetData(url string) (data []byte, err error) {
var response *http.Response
response, err = http.Get(url)
if err == nil {
if response.ContentLength <= 0 {
err = errors.New("web.GetData: empty body")
response.Body.Close()
return
}
data, err = io.ReadAll(response.Body)
response.Body.Close()
}
return
}