Compare commits

...
205 Commits
Author SHA1 Message Date
HibiKier 5de5667f80 更新manage models 2022-11-21 22:20:19 +08:00
HibiKier 69b3ba47b3 update README.md 2022-11-21 20:52:07 +08:00
HibiKier 11c1d0b827 Update __version__ 2022-11-21 20:51:17 +08:00
HibiKier 8f97880754 update README.md 2022-11-21 20:50:21 +08:00
HibiKier ff435bea95 Merge pull request #1180 from PercyDan54/patch-1
修复pid搜图
2022-11-21 20:49:49 +08:00
HibiKier cd66b7f5b8 优化manager代码 2022-11-21 20:43:41 +08:00
PercyDan a60286c328 Fix pid search 2022-11-20 16:52:51 +08:00
HibiKier 49e38862af 修改优化帮助图片生成逻辑 2022-11-19 01:05:08 +08:00
HibiKier 75b8cc0766 Merge pull request #1176 from yajiwa/main
poetry添加适配器依赖,更新支持py3.10
2022-11-17 11:31:51 +08:00
yajiwa a8e2f00e16 poetry flie add adapter 2022-11-15 22:04:13 +08:00
HibiKier d099a85c59 限制帮助图片最小宽度 2022-11-13 04:46:08 +08:00
HibiKier 55b2a86397 使用道具可以附带额外信息供函数使用 2022-11-13 04:12:56 +08:00
HibiKier d39dfbe62f 更新天气api 2022-11-13 02:05:34 +08:00
HibiKier a41e62cefe update README.md 2022-11-13 00:37:22 +08:00
HibiKier c981562f90 Merge pull request #1168 from wse320/main
更新yiqing插件数据显示
2022-11-11 23:11:50 +08:00
HibiKier e6cd57cc26 Merge pull request #1162 from DDS-DS/main
fix: B站直播订阅的相关问题(issues 1158)
2022-11-10 19:52:46 +08:00
wse320 074152ae14 Update data_source.py 2022-11-07 20:24:49 +08:00
DDS-DS 91ca7b40b8 补充代码 2022-11-05 02:17:04 +08:00
dingshan 5321c8d9c0 fix:
1、兼容B站下播的两种状态
2、更改订阅入库逻辑,加入订阅类型
2022-10-31 15:47:40 +08:00
HibiKier b7f753ebb6 商店简介动态行数,根据文字长度自动换行 2022-10-30 18:28:10 +08:00
HibiKier c5e8ba3cdf Merge pull request #1154 from guohu7777/main
把某人漏掉的给复制上去
2022-10-28 18:29:38 +08:00
HibiKier 658ef5309b Merge pull request #1155 from Lycoiref/main
为exec指令进行了SELECT语句适配,添加了查看所有表指令
2022-10-28 18:29:27 +08:00
HDU_Nbsp 823f642225 为exec指令进行了SELECT语句适配,添加了查看所有表指令 2022-10-25 03:27:26 +08:00
guohu7777 57ea6bad07 把某人漏掉的给复制上去
1111
2022-10-24 22:13:25 +08:00
HibiKier 109c645e68 复读修改回图片下载 2022-10-23 11:49:15 +08:00
HibiKier 5ef0ce5aa8 Merge remote-tracking branch 'origin/main' 2022-10-23 00:34:03 +08:00
HibiKier f6d9e083b8 Merge pull request #1151 from DDS-DS/main
update bt url
2022-10-22 18:26:59 +08:00
HibiKier 499517e882 更新依赖注入 2022-10-22 18:12:55 +08:00
dingshan 648db532bb update bt url 2022-10-21 10:07:55 +08:00
HibiKier 1ce4537605 upadte README.md 2022-10-16 22:54:34 +08:00
HibiKier 701812ed9d upadte README.md 2022-10-16 22:39:57 +08:00
HibiKier 8d9e81827b 修改版本 2022-10-16 22:38:12 +08:00
HibiKier f51a2816b4 修改依赖注入方法 2022-10-16 22:37:58 +08:00
HibiKier 3d4a7950c5 修改依赖注入方法 2022-10-16 22:36:13 +08:00
HibiKier ba6668f144 修改商店道具icon可以为空 2022-10-16 11:46:56 +08:00
HibiKier e1f7a77d15 修正 2022-10-16 00:21:42 +08:00
HibiKier 64e2c512d3 Merge remote-tracking branch 'origin/main' 2022-10-16 00:21:30 +08:00
HibiKier 6082e68a1f Delete Generate.yml 2022-10-16 00:21:00 +08:00
HibiKier f135d48e55 Merge remote-tracking branch 'origin/main' 2022-10-16 00:20:24 +08:00
HibiKier 751fc8fdb4 修复我的道具仅有被动或主动道具时图片显示错误 2022-10-16 00:19:55 +08:00
github-actions[bot] df73e79ee4 Auto generate poetry.lock 2022-10-15 15:10:25 +00:00
HibiKier fa6cfdb609 修正 2022-10-15 23:07:26 +08:00
HibiKier b057a05520 更多的依赖注入 2022-10-15 22:13:41 +08:00
HibiKier cb0938721c 新增用户使用道具,花费金币及用途记录表以及更细致的金币使用依赖注入 2022-10-15 21:21:23 +08:00
HibiKier 4f3f8de734 错误捕获 2022-10-15 20:50:02 +08:00
HibiKier 5ba303aada Merge pull request #1139 from yuzb1beiz/main
色图插件p站反向代理失效
2022-10-15 20:42:49 +08:00
yusean bd42fefe4e Merge branch 'HibiKier:main' into main 2022-10-15 20:15:29 +08:00
sean d24c081973 修复色图插件p站反向代理失效问题 2022-10-15 20:00:06 +08:00
HibiKier 1b9eddd905 Add files via upload 2022-10-15 19:53:11 +08:00
HibiKier 0bfe398dc4 更新至nonebot-rc1 2022-10-15 19:49:53 +08:00
HibiKier e9216a472f Add files via upload 2022-10-13 19:36:54 +08:00
HibiKier 8b93ec0df6 Delete poetry.lock 2022-10-13 19:36:40 +08:00
HibiKier 7497706a0d Merge pull request #1124 from DDS-DS/main
修复碧蓝档案角色获取问题,换源
2022-10-09 19:14:35 +08:00
dingshan 6e41cb0630 修复碧蓝档案角色获取问题,换源 2022-10-09 16:08:55 +08:00
HibiKier 62d99e7e90 Merge remote-tracking branch 'origin/main' 2022-10-07 22:07:51 +08:00
HibiKier 43cddb3580 关闭功能与被动时不再区分大小写 2022-10-07 22:07:36 +08:00
github-actions[bot] c9c1beda75 Auto generate poetry.lock 2022-10-07 13:00:26 +00:00
HibiKier 3ee9ae5fb8 Merge pull request #1119 from SK-415/main
修复 B 站请求返回 -401 错误
2022-10-07 20:57:33 +08:00
github-actions[bot] ea6344fae1 Auto generate poetry.lock 2022-10-02 22:53:37 +00:00
SK 6f54e91f5c ⬆️ Bump bilireq to v0.2.3 2022-10-02 22:50:00 +00:00
HibiKier 27fb3aaa19 Merge pull request #1118 from guohu7777/main
修改重置开箱的使用权限
2022-09-29 18:01:39 +08:00
guohu7777 86d736745d 修改重置开箱的使用权限
禁止普通群员使用重置每日开箱次数指令
2022-09-28 01:23:42 +08:00
HibiKier 821fe21c2b Merge pull request #1117 from yajiwa/main
更新b站转发解析
2022-09-27 19:31:11 +08:00
yajiwa 7f4aa518dc update parse_bilibili 2022-09-27 03:11:57 +08:00
HibiKier 8cf589e433 UPDATE README.md 2022-09-25 21:43:23 +08:00
HibiKier 105a29cd70 Merge pull request #1114 from DDS-DS/main
fix: 重载赛马娘卡池失败
2022-09-24 17:50:52 +08:00
HibiKier cb6a5e81e7 Merge pull request #1112 from yajiwa/main
修复b站订阅
2022-09-24 17:49:57 +08:00
dingshan 9c073383ad fix: 重载赛马娘卡池失败 2022-09-23 18:27:04 +08:00
github-actions[bot] 5e0756825d Auto generate poetry.lock 2022-09-21 19:39:34 +00:00
yajiwa 933523ae6d fix bilibili_sub 2022-09-22 03:35:34 +08:00
HibiKier ed6b05605c Update map.py 2022-09-21 21:59:19 +08:00
HibiKier 71a9c34620 Merge pull request #1108 from yajiwa/main
修复原神资源查询没有图片
2022-09-21 21:57:55 +08:00
HibiKier e29f2cb920 Update query_resource.py 2022-09-21 21:57:25 +08:00
yajiwa d47dd88d6c fix genshin path 2022-09-21 01:13:37 +08:00
github-actions[bot] fb34bbce7a Auto generate poetry.lock 2022-09-20 15:36:00 +00:00
HibiKier 100236da83 修复csgo每日开箱可以多开一次 2022-09-19 22:50:56 +08:00
HibiKier a45a3002d5 Merge pull request #1097 from LambdaYH/main
更换bilibili_sub获取用户昵称用的API&尝试修了一下get_video()
2022-09-19 22:34:01 +08:00
HibiKier 9214a4c978 Merge pull request #1098 from yajiwa/main
update check_zhenxun
2022-09-18 15:07:52 +08:00
HibiKier 70761dc222 Merge pull request #1096 from SK-415/main
修复 bilireq 版本过低导致 B 站视频解析错误
2022-09-18 15:05:29 +08:00
Cinte 32e8136f71 fix get_video()
目前测试了下,至少-401的情况少了很多
2022-09-17 18:26:49 +08:00
yajiwa 8b20d99b2c update check_zhenxun 2022-09-17 13:01:50 +08:00
Cinte 1174b5cc7f x 2022-09-17 11:23:55 +08:00
Cinte ec448cfdfb replace get_user_info with get_user_card 2022-09-17 10:58:58 +08:00
SK f3e44b1baf chore(deps): update bilireq 2022-09-16 23:24:40 +00:00
HibiKier 0ea740aa7c Update __version__ 2022-09-16 19:35:33 +08:00
HibiKier 5f118471cb 修复一些问题 2022-09-16 19:29:05 +08:00
HibiKier 504f78dd49 Merge pull request #1090 from NumberSir/main
fix: bilibili_sub, azur_draw_card
2022-09-16 19:14:19 +08:00
Number_Sir 72723c7d1f 修复碧蓝航线抽卡错误 2022-09-16 12:55:22 +08:00
Number_Sir 6a74053853 bilibili_sub更新检查失败时改为logger通知,不在群里发消息 2022-09-16 12:43:22 +08:00
HibiKier af0b4987a4 Merge pull request #1067 from DDS-DS/main
fix:bt url打开失败
2022-09-10 00:20:44 +08:00
HibiKier 775140cf79 自定义群欢迎消息参数不完全时提示报错 2022-09-09 22:41:39 +08:00
HibiKier 6a8a796add 添加插件数据初始化判断 2022-09-08 20:13:26 +08:00
DDS-DS 8e4bda3760 fix bt url打开失败 2022-09-08 01:35:41 +08:00
HibiKier a36e8145a8 旧词条提供图片迁移 2022-09-04 22:05:56 +08:00
HibiKier 3cbd899daa 修复原神资源查询地图api数据变更导致更新的地图不完全 2022-09-03 18:17:42 +08:00
HibiKier 30f356a2c7 Merge remote-tracking branch 'origin/main' 2022-09-03 17:30:21 +08:00
HibiKier 1138a115ce B站订阅在群里中任意群管理员可以统一管理 2022-09-03 17:30:03 +08:00
HibiKier c397da75e7 Merge pull request #1033 from q56535/main
av号覆盖全面,且修复av号链接
2022-09-03 17:14:22 +08:00
HibiKier 1d141c9313 Merge pull request #1059 from unknownsno/main
fix bug
2022-09-03 16:56:49 +08:00
q56535 a4cc365af8 Merge branch 'HibiKier:main' into main 2022-09-03 16:44:57 +08:00
HibiKier 4e551bec0d 修复词条含有CQ回答的模糊匹配无法被解析 2022-09-03 16:16:33 +08:00
unknwonsno e03086062d fix bug 2022-09-03 14:43:13 +08:00
HibiKier 270a5a5e20 Merge pull request #1053 from unknownsno/main
原神玩家查询增加须弥地区
2022-09-03 14:24:18 +08:00
unknwonsno bda9133ed6 原神玩家查询增加须弥地区 2022-08-31 22:22:00 +08:00
HibiKier 749f475f2c UPDATE README.md 2022-08-27 18:25:30 +08:00
HibiKier e38394948c Merge pull request #1044 from zyj3421/main
修复签到积分双倍后,日志记录获得积分变4倍问题
2022-08-27 18:22:35 +08:00
zyj2134 51fc4cbb6a 修复签到积分双倍后,日志记录获得积分变4倍问题 2022-08-26 22:49:51 +08:00
zyj2134 9a976cda37 Merge remote-tracking branch 'origin/main' 2022-08-26 22:39:48 +08:00
HibiKier e42521f0a3 修复词条关键词"问"前空格问题 2022-08-26 19:39:23 +08:00
HibiKier 0756d39ac5 修复群管理员无法添加词条 2022-08-26 19:26:09 +08:00
zyj2134 4595bdcee4 Merge remote-tracking branch 'origin/main'
# Conflicts:
#	README.md
#	plugins/bilibili_sub/__init__.py
2022-08-25 23:00:49 +08:00
HibiKier 38160065cc UPDATE README.md 2022-08-23 23:02:06 +08:00
HibiKier 3d04f013cd Merge pull request #1032 from LambdaYH/p1
修了下模糊匹配 issue#1026
2022-08-23 20:21:25 +08:00
q56535 8e1a416be3 av号覆盖全面 2022-08-23 13:43:43 +08:00
LambdaYH a98bc42f1a fix https://github.com/HibiKier/zhenxun_bot/issues/1026 2022-08-23 13:16:14 +08:00
HibiKier 6c93769838 UPDATE README.md 2022-08-22 22:51:36 +08:00
HibiKier d2dccfebc5 取消配置替换定时任务,统一存储 2022-08-22 22:49:35 +08:00
HibiKier 82fe59e469 修复首次安装时词条旧表出错 2022-08-22 20:59:08 +08:00
HibiKier afc05e1731 Merge remote-tracking branch 'origin/main' 2022-08-22 20:48:57 +08:00
HibiKier 0206b0a522 UPDATE README.md 2022-08-22 20:47:32 +08:00
HibiKier 0e1e5a5ebe Merge pull request #1012 from LambdaYH/patch-4
修复词库问答几个问题
2022-08-22 20:45:31 +08:00
HibiKier 9ad088ed6a Merge pull request #1010 from CRAZYShimakaze/main
修正尘歌壶和质变仪图片获取地址
2022-08-22 20:43:22 +08:00
HibiKier 34da04bba5 Merge pull request #1014 from xiayingLIULI/main
对米游社cookie进行判断,整合米游社签到信息
2022-08-22 20:41:32 +08:00
HibiKier 09ce812faf Update __init__.py 2022-08-22 20:39:03 +08:00
xiayingLIULI 1dc8cb2a98 修复米游社签到在无用户cookie的情况下执行的错误 2022-08-22 13:45:04 +08:00
xiayingLIULI e73c509823 修复米游社签到在无用户cookie的情况下执行的错误 2022-08-22 13:33:49 +08:00
Cinte e70a85400f 查看词条的id范围判断少了个等号 2022-08-22 12:56:35 +08:00
Cinte f7aecf8eed Update word_handle.py 2022-08-22 12:37:06 +08:00
Cinte cc2645aa9d Update word_handle.py 2022-08-22 12:34:21 +08:00
xiayingLIULI 52d23c4a8d 修复能在群聊中查看我的cookie 2022-08-22 11:44:21 +08:00
Cinte 1e57ddb9ee 处理at后自动接空格的情况,problem中允许单空白字符号 & 修复二次元触发 2022-08-22 11:14:04 +08:00
Cinte 7ae883d921 使用check构造的problem而不在message_handle中重新构造 2022-08-22 10:14:59 +08:00
Cinte 27e97214bd 仅仅是检查图片hash就将图片下载到磁盘是否有些浪费io,于是改成了内存中直接计算 2022-08-22 09:49:33 +08:00
LambdaYH 6321a9e7e0 使用gino提供的方法,避免构建sql字符串时出现非法查询语句 2022-08-22 00:49:30 +08:00
xiayingLIULI a9eadca46c 对米游社cookie进行判断,整合米游社签到信息 2022-08-22 00:42:00 +08:00
xiayingLIULI 76bef8a725 对米游社cookie进行判断,整合米游社签到信息 2022-08-22 00:40:51 +08:00
LambdaYH 308b76dc7d _rule中使用messagesegment中未转义的字符串,而添加词条时使用了转义后的字符串,导致二者不一致 2022-08-22 00:38:25 +08:00
LambdaYH 31d4d29f3e 修复匹配时候忽视了word_type的约束 2022-08-21 23:47:21 +08:00
LambdaYH 016a7e87ea Merge branch 'HibiKier-main' into patch-4 2022-08-21 23:46:46 +08:00
LambdaYH 4ce88c7bfc merge 2022-08-21 23:46:38 +08:00
LambdaYH f570bffcf7 修复匹配时候忽视了word_type的约束 2022-08-21 23:42:46 +08:00
LambdaYH 3bb9aaf637 添加正则词条前先检查正则有效性 & 正则词条先进行转义,不然符号都乱了 2022-08-21 23:21:34 +08:00
LambdaYH cf7a64c934 fix: 问答为[at + 文本]的情况时无法正常添加 2022-08-21 22:24:06 +08:00
LambdaYH c466603193 Fix at messages with suffixed text 2022-08-21 21:58:31 +08:00
CRAZY bc53f329a9 修正尘歌壶和质变仪图片获取地址 2022-08-21 20:33:06 +08:00
CRAZY 4a5cc5153f 修正质变仪图片获取地址 2022-08-21 20:11:44 +08:00
HibiKier 8697670fe1 UPDATE README.md 2022-08-21 18:43:58 +08:00
HibiKier 64d8543af9 UPDATE __version__ 2022-08-21 18:42:16 +08:00
HibiKier 482cc242cb UPDATE README.md 2022-08-21 18:41:13 +08:00
HibiKier 2051e8183c 词条模糊匹配使用contains方法 2022-08-21 18:32:24 +08:00
HibiKier d183903917 update README.md 2022-08-21 18:15:18 +08:00
HibiKier 5c695899a1 将配置替换加入单次定时任务,提高启动速度 2022-08-21 18:09:31 +08:00
HibiKier 708e912069 Merge pull request #1008 from LambdaYH/patch-4
fix: WordBank.check()
2022-08-21 16:57:22 +08:00
Cinte db1f4b2b52 fix: WordBank.check() 2022-08-21 16:54:28 +08:00
HibiKier 1508782f49 提供全局字典GDict,适配omega 13w张图的数据结构表 2022-08-21 16:47:50 +08:00
HibiKier 445b830683 开箱提供重置开箱命令 2022-08-21 14:10:49 +08:00
HibiKier 1a17e88142 b站订阅直播订阅支持短号 2022-08-21 13:57:48 +08:00
HibiKier c0ec2e7856 b站订阅直播订阅支持短号 2022-08-21 13:57:17 +08:00
HibiKier 9e962bd0d1 修正词条加载路径 2022-08-21 13:44:06 +08:00
HibiKier 7efcff13bb backup 2022-08-21 13:37:03 +08:00
zyj2134 e70d0df949 Merge remote-tracking branch 'origin/main' 2022-08-19 23:49:16 +08:00
HibiKier fcaa469edd Merge pull request #996 from shinianj/patch-1
Update README.md
2022-08-16 08:46:10 +08:00
shinianj 51508182ef Update README.md 2022-08-16 00:38:14 +08:00
HibiKier 27e78c1626 update README.md 2022-08-14 22:11:36 +08:00
HibiKier 0b4d37a0b6 修复订阅主播时动态获取的id是直播间id 2022-08-14 21:31:57 +08:00
HibiKier ddbebba6f1 修复epic未获取时间时出错 2022-08-14 21:18:29 +08:00
zyj2134 7f296348e4 Merge remote-tracking branch 'origin/main' 2022-08-09 22:20:32 +08:00
HibiKier ca78e32d97 Update README.md 2022-08-08 04:23:05 +08:00
HibiKier 4efc561f4d Merge pull request #969 from DDS-DS/main
修复赛马娘重载卡池失败的问题
2022-08-08 04:22:24 +08:00
HibiKier 40fed798a1 Update data_source.py 2022-08-07 01:10:19 +08:00
HibiKier 0876afc08c Update README.md 2022-08-06 20:40:10 +08:00
HibiKier ba4c4d06ff Merge pull request #971 from HDU-Nbsp/main
修复了原神自动签到返回invalid request的问题,新增查看我的cookie命令
2022-08-06 20:39:30 +08:00
HibiKier f64c30af48 Update __init__.py 2022-08-06 20:39:16 +08:00
HDU_Nbsp bb0cea2dd3 修复了原神自动签到返回invalid request的问题,新增查看我的cookie命令 2022-08-05 22:32:15 +08:00
dingshan 3c120ce602 增加备注说明 2022-08-05 15:49:21 +08:00
dingshan 6151dfb161 修复赛马娘重载卡池失败的问题 2022-08-05 15:42:53 +08:00
dingshan f05d4ec931 修复赛马娘重载卡池失败的问题 2022-08-05 15:35:45 +08:00
HibiKier e16afc3fc2 update README.md 2022-08-03 20:26:41 +08:00
HibiKier 1b8753fead Merge pull request #961 from Gaomengkai/main
修复 bili动态链接在投稿视频时URL和分割线连在一起
2022-08-03 20:20:29 +08:00
HibiKier ba960d1ced Merge pull request #957 from monsterxcn/patch-1
更新 Epic 免费游戏商城链接拼接规则
2022-08-03 20:14:13 +08:00
Merky Gao 41b60b6152 修复 bili动态链接在投稿视频时URL和分割线连在一起
比如up发布一条动态后,qq显示的链接事[https://t.bilibili.com/123456789012345678-------------],会把后面的分隔符算在链接里边,导致直接点开后404(*^_^*). だから……お願い!
2022-08-01 11:11:34 +08:00
github-actions[bot] 70ddce713c Auto generate poetry.lock 2022-08-01 00:23:45 +00:00
HibiKier e15622748b Update pyproject.toml 2022-08-01 08:21:30 +08:00
HibiKier 96687021ff Merge pull request #958 from yajiwa/main
Update poetry.lock
2022-08-01 08:21:12 +08:00
yajiwa 3c0a7ef510 Update poetry.lock 2022-07-31 22:22:54 +08:00
HibiKier 4d84c9fb3f 修复metadata中version判断 2022-07-31 18:00:44 +08:00
HibiKier aa35a16610 修复pix无图时不会提醒而是出错 2022-07-31 17:48:54 +08:00
HibiKier a380fcb680 修复webui元祖类型无法正确转换类型 2022-07-31 17:45:14 +08:00
Monst.x e544afe475 更新 Epic 免费游戏商城链接拼接规则 2022-07-31 17:41:10 +08:00
HibiKier c089388323 适配metadata 2022-07-31 17:30:29 +08:00
HibiKier e97a043031 Merge pull request #953 from tanzhi77/main
发言记录统计添加日消息统计
2022-07-30 10:55:50 +08:00
HibiKier d1b6dfe577 Merge pull request #951 from yajiwa/main
替换了cos和bt的url
2022-07-30 10:53:49 +08:00
HibiKier 9189fd411e Update __init__.py 2022-07-29 19:50:21 +08:00
弹指 432eba80a9 发言记录统计添加日消息统计 2022-07-29 19:08:16 +08:00
yajiwa db39612c54 update bt 2022-07-28 16:49:28 +08:00
yajiwa 5794c82a2d update coser 2022-07-28 16:48:45 +08:00
yajiwa acf875a886 Merge branch 'HibiKier:main' into main 2022-07-28 14:42:47 +08:00
zyj2134 a1308fbdc9 Merge remote-tracking branch 'origin/main' 2022-07-24 22:16:51 +08:00
yajiwa e4f2b761d6 Merge branch 'HibiKier:main' into main 2022-07-24 18:51:09 +08:00
yajiwa ee0ff762e5 Merge branch 'HibiKier:main' into main 2022-07-23 18:02:24 +08:00
zyj2134 946fbbec3c Merge remote-tracking branch 'origin/main' 2022-07-17 15:34:18 +08:00
zyj2134 bd531c85b1 Merge remote-tracking branch 'origin/main'
# Conflicts:
#	README.md
2022-07-14 20:44:12 +08:00
HibiKier cda9aae8a3 Update README.md 2022-07-07 22:02:33 +08:00
HibiKier a33e785043 Merge pull request #891 from yajiwa/main
更新微博热搜
2022-07-07 22:01:53 +08:00
yajiwa 833e266a1f update wbtop 2022-07-06 04:59:54 +08:00
139 changed files with 7577 additions and 4930 deletions
-38
View File
@@ -1,38 +0,0 @@
name: Generate poetry.lock
on:
push:
branches:
- "main"
paths:
- 'pyproject.toml'
jobs:
poetry_lock:
name: Generate
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v2
- name: Set up Python
uses: he0119/setup-python@main
with:
python-version: "3.9"
- name: poetry lock
run: |
rm -f poetry.lock
sed -i.bak "8,11d" pyproject.toml
poetry lock
mv pyproject.toml.bak pyproject.toml
# commit
- name: commit file
run: |
git config --local user.email "github-actions[bot]@users.noreply.github.com"
git config --local user.name "github-actions[bot]"
git add poetry.lock
git commit -m "Auto generate poetry.lock" -a
# push
- name: push changes
uses: ad-m/github-push-action@master
with:
github_token: ${{ secrets.GITHUB_TOKEN }}
+17 -1
View File
@@ -138,10 +138,26 @@ dmypy.json
# Cython debug symbols
cython_debug/
demo.py
test.py
server_ip.py
game_utils.py
member_activity_handle.py
Yu-Gi-Oh/
csgo/
fantasy_card/
data/
log/
backup/
extensive_plugin/
test/
bot.py
data/
.env
.env.dev
resources/
!/configs/config.py
!/configs/config.yaml
!/.env
!/.env.dev
!/plugins/csgo_server/
!/plugins/activity/
+158 -404
View File
@@ -13,7 +13,9 @@
如果该项目的图片等等侵犯猫豆腐老师权益请联系我删除!
如果希望有个地方讨论绪山真寻Bot,或者有问题或建议,可以发送issues或加入[ <strong>[是真寻酱哒](https://jq.qq.com/?_wv=1027&k=u8PgBkMZ) </strong>]
是新手!希望有个地方讨论绪山真寻Bot,或者有问题或建议,可以发送issues或加入[ <strong>[是真寻酱哒(萌新版)](https://jq.qq.com/?_wv=1027&k=u8PgBkMZ) </strong>]
[//]: # (是老手!讨论插件开发,nonebot2开发,可以加入[ <strong>[真寻酱的技术群]&#40;https://jq.qq.com/?_wv=1027&k=u8PgBkMZ&#41; </strong>])
## 声明
此项目仅用于学习交流,请勿用于非法用途
@@ -128,7 +130,7 @@
- [x] 移动图片 (同上)
- [x] 删除图片 (同上)
- [x] 群内B站订阅
- [x] 群词条
- [x] 词条设置
- [x] 休息吧/醒来
### 已实现的超级用户功能
@@ -245,481 +247,233 @@ __Docker 全量版(包含 真寻Bot PostgreSQL数据库 go-cqhttp webui等)_
**点击上方的 GitHub 徽标查看教程**
PS: **ARM平台** 请使用全量版 同时 **如果你的机器 RAM < 1G 可能无法正常启动全量版容器**
## [爱发电](https://afdian.net/@HibiKier)
<details>
<summary>爱发电 以及 感谢投喂 </summary>
<img width="365px" height="450px" src="https://user-images.githubusercontent.com/45528451/175059389-cfeb8174-fa07-4939-80ab-a039087a50f6.png">
### 感谢名单
(可以告诉我你的 __github__ 地址,我偷偷换掉0v|)
[阿奎](https://afdian.net/u/da41f72845d511ed930d52540025c377)
[醉梦尘逸](https://afdian.net/u/bc11d2683cd011ed99b552540025c377)
[Abc](https://afdian.net/u/870dc10a3cd311ed828852540025c377)
[本喵无敌哒](https://afdian.net/u/dffaa9005bc911ebb69b52540025c377)
[椎名冬羽](https://afdian.net/u/ca1ebd64395e11ed81b452540025c377)
[kaito](https://afdian.net/u/a055e20a498811eab1f052540025c377)
[笑柒XIAO_Q7](https://afdian.net/u/4696db5c529111ec84ea52540025c377)
[请问一份爱多少钱](https://afdian.net/u/f57ef6602dbd11ed977f52540025c377)
[咸鱼鱼鱼鱼](https://afdian.net/u/8e39b9a400e011ed9f4a52540025c377)
[Kafka](https://afdian.net/u/41d66798ef6911ecbc5952540025c377)
[墨然](https://afdian.net/u/8aa5874a644d11eb8a6752540025c377)
[爱发电用户_T9e4](https://afdian.net/u/2ad1bb82f3a711eca22852540025c377)
[笑柒XIAO_Q7](https://afdian.net/u/4696db5c529111ec84ea52540025c377)
[noahzark](https://afdian.net/a/noahzark)
[腊条](https://afdian.net/u/f739c4d69eca11eba94b52540025c377)
[ze roller](https://afdian.net/u/0e599e96257211ed805152540025c377)
[爱发电用户_4jrf](https://afdian.net/u/6b2cdcc817c611ed949152540025c377)
[爱发电用户_TBsd](https://afdian.net/u/db638b60217911ed9efd52540025c377)
[烟寒若雨](https://afdian.net/u/067bd2161eec11eda62b52540025c377)
[ln](https://afdian.net/u/b51914ba1c6611ed8a4e52540025c377)
[爱发电用户_b9S4](https://afdian.net/u/3d8f30581a2911edba6d52540025c377)
[爱发电用户_c58s](https://afdian.net/u/a6ad8dda195e11ed9a4152540025c377)
[爱发电用户_eNr9](https://afdian.net/u/05fdb41c0c9a11ed814952540025c377)
[MangataAkihi](https://github.com/Sakuracio)
[炀](https://afdian.net/u/69b76e9ec77b11ec874f52540025c377)
[爱发电用户_Bc6j](https://afdian.net/u/8546be24f44111eca64052540025c377)
[大魔王](https://github.com/xipesoy)
[CopilotLaLaLa](https://github.com/CopilotLaLaLa)
[嘿小欧](https://afdian.net/u/daa4bec4f24911ec82e552540025c377)
[回忆的秋千](https://afdian.net/u/e315d9c6f14f11ecbeef52540025c377)
[十年くん](https://github.com/shinianj)
[哇](https://afdian.net/u/9b266244f23911eca19052540025c377)
[yajiwa](https://github.com/yajiwa)
[爆金币](https://afdian.net/u/0d78879ef23711ecb22452540025c377)
</details>
## 更新
### 2022/7/24 \[v0.1.6.2]
### 2022/11/21 \[v0.1.6.5]
* 订阅up动态提供直链
* 优化manager, hook代码
* 修复pid搜图 [@pull/1180](https://github.com/HibiKier/zhenxun_bot/pull/1180)
### 2022/7/23
### 2022/11/19
* 优化开箱次数判断
* 修改优化帮助图片生成逻辑
### 2022/7/16
### 2022/11/18
* cos提供连发命令
* 修复B站视频解析中b23解析错误
* poetry添加适配器依赖,更新支持py3.10 [@pull/1176](https://github.com/HibiKier/zhenxun_bot/pull/1176)
### 2022/7/13
### 2022/11/13
* B站订阅提供配置项`LIVE_MSG_AT_ALL`,`UP_MSG_AT_ALL`:控制UP动态投稿和直播是否AT全体
* 修改了Omega图库检测的数据库文件名
* game_utils提供了Game类
* 更新天气api
* 使用道具可以附带额外信息供函数使用
* 限制帮助图片最小宽度
### 2022/7/7
### 2022/11/12
* 微博热搜选择单条热搜时也会检测时效性 [@pull/891](https://github.com/HibiKier/zhenxun_bot/pull/891)
* 更新yiqing插件数据显示 [@pull/1168](https://github.com/HibiKier/zhenxun_bot/pull/1168)
### 2022/7/4
### 2022/11/11
* 修复商品未设置限购时无法购买
* 修复B站订阅UP动态 [@pull/886](https://github.com/HibiKier/zhenxun_bot/pull/886)
* 修复色图命令带tag时配置项`MAX_ONCE_NUM2FORWARD`无法生效
* fix: B站直播订阅的相关问题 [@pull/1158](https://github.com/HibiKier/zhenxun_bot/pull/1158)
### 2022/7/3 \[v0.1.6.1]
### 2022/10/30
* BagUser提供方法`check_props`方法,用于判断用户是否含有该道具并是否使用
* 优化商品显示图片,提供限制,限购,折扣提示
* 修复图库内图片无法被连续删除的问题 [@pull/879](https://github.com/HibiKier/zhenxun_bot/pull/879)
* 色图提供配置项`MAX_ONCE_NUM2FORWARD`:群聊中单次发送图片数量达到指定时使用合并转发
* 优化修复了商品修改命令
* B站订阅UP动态改为详情页截图
* 商店简介动态行数,根据文字长度自动换行
### 2022/6/28
### 2022/10/28
* 修复色图插件api修改无法搜索图片 [@pull/866](https://github.com/HibiKier/zhenxun_bot/pull/866)
* 为exec指令进行了SELECT语句适配,添加了查看所有表指令 [@pull/1155](https://github.com/HibiKier/zhenxun_bot/pull/1155)
* 修复复读 [@pull/1154](https://github.com/HibiKier/zhenxun_bot/pull/1154)
### 2022/6/26
### 2022/10/23
* 替换了bt URL
* PIX当使用pid查询图片时,会发送该pid下所有图片
* 商店提供了每日购买次数限制
* 复读修改回图片下载
### 2022/6/24
### 2022/10/22
* 对WSL虚拟机 代理 添加写法 [@pull/847](https://github.com/HibiKier/zhenxun_bot/pull/847)
* 更新依赖注入
### 2022/6/21
### 2022/10/16 \[v0.1.6.4]
* update parse_bilibili [@pull/840](https://github.com/HibiKier/zhenxun_bot/pull/840)
* 修改商店道具icon可以为空
### 2022/6/19 \[v0.1.6.0]
### 2022/10/15
* 暂时使用hook修复webui中plugins2setting修改时会改变plugins2setting.cmd为字符串
* 修复原神树脂重复提醒的bug [@pull/828](https://github.com/HibiKier/zhenxun_bot/pull/828)
* 词条优化问题展示,修改含机器人昵称和@逻辑 [@pull/835](https://github.com/HibiKier/zhenxun_bot/pull/835)
* nonebot2版本更新为rc1
* 我的道具改为图片形式
* 商品添加图标与是否为被动道具(被动道具无法被主动使用)
* 商品添加使用前方法和使用后方法(类似hook),使用方法具体查看文档或签到商品文件中注册的例子
* 新增用户使用道具,花费金币(包括插件)及用途记录
* 更细致的金币使用依赖注入
* 更多的依赖注入(包含图片获取等等..
* 修复我的道具仅有被动或主动道具时图片显示错误
* 色图插件p站反向代理失效 [@pull/1139](https://github.com/HibiKier/zhenxun_bot/pull/1139)
### 2022/6/18
### 2022/10/9
* 修复昵称系统`BLACK_WORD`为空时造成报错
* 修复特殊头像时背景透明化出错
* 修复text2image纯文本时换行时颜色不统一
* 优化webUI,当有插件出错时不会影响其他插件显示
* 优化Config类型注释 [@pull/830](https://github.com/HibiKier/zhenxun_bot/pull/830)
* 优化browser
* 修复碧蓝档案角色获取问题,换源 [@pull/1124](https://github.com/HibiKier/zhenxun_bot/pull/1124)
### 2022/6/17
### 2022/10/7
* 修复wordbank的一些小问题 @pull/820
* 修复 B 站请求返回 -401 错误 [@pull/1119](https://github.com/HibiKier/zhenxun_bot/pull/1119)
* 关闭功能与被动时不再区分大小写,同名时仅被动关闭操作生效
### 2022/6/14
### 2022/9/30
* AsyncHttpx提供可关闭 ssl 验证参数 [@pull/812](https://github.com/HibiKier/zhenxun_bot/pull/812)
* fix: 自动更新群组 [@pull/810](https://github.com/HibiKier/zhenxun_bot/pull/810)
* fix: 修复添加词条时对answer中图片的处理 [@pull/806](https://github.com/HibiKier/zhenxun_bot/pull/806)
* 修复pix中指定num无法生效
* 替换了bt URL
* 修改重置开箱的使用权限 [@pull/1118](https://github.com/HibiKier/zhenxun_bot/pull/1118)
### 2022/6/13
### 2022/9/27
* Windows 重启适配 [@pull/804](https://github.com/HibiKier/zhenxun_bot/pull/804)
* 色图等提供修改md5方法 [@pull/800](https://github.com/HibiKier/zhenxun_bot/pull/800)
* webui中token.json文件写入补充 [@pull/798](https://github.com/HibiKier/zhenxun_bot/pull/798)
* 修复识图某些图片可能会出错
* 更新b站转发解析 [@pull/1117](https://github.com/HibiKier/zhenxun_bot/pull/1117)
### 2022/6/11
### 2022/9/24
* pix当只有一个tag时且为数字时可以通过添加'#'防止判定为数量
* 修复b站订阅 [@pull/1112](https://github.com/HibiKier/zhenxun_bot/pull/1112)
* fix: 重载赛马娘卡池失败 [@pull/1114](https://github.com/HibiKier/zhenxun_bot/pull/1114)
### 2022/6/9
### 2022/9/19
* 修复b站订阅同群二人以上相同订阅时发送多次信息
* 修复超级用户帮助中缺少了 ‘插件商店’ 相关帮助
* 昵称系统提供了详细帮助
* 更换bilibili_sub获取用户昵称用的API&尝试修了一下get_video() [@pull/1097](https://github.com/HibiKier/zhenxun_bot/pull/1097)
* 修复csgo每日开箱可以多开一次
### 2022/6/5 \[v0.1.5.9]
### 2022/9/18
* webui修复plugin2setting中cmd从list转变为str
* 当命令`我的金币`被风控时将以图片形式发送
* fix gold_redbag [@pull/763](https://github.com/HibiKier/zhenxun_bot/pull/763)
* 金币红包功能增加更多封面 [@pull/764](https://github.com/HibiKier/zhenxun_bot/pull/764)
* 修复 bilireq 版本过低导致 B 站视频解析错误 [@pull/1090](https://github.com/HibiKier/zhenxun_bot/pull/1096)
### 2022/6/3
### 2022/9/16
* 插件仓库在已安装插件边上会提示\[已安装]
* 修复ShopRegister kwargs某些字段无效
* 调整了一下查看所有请求中的年龄绘制 [@pull/745](https://github.com/HibiKier/zhenxun_bot/pull/745)
* 修复原神树脂提醒bug [@pull/756](https://github.com/HibiKier/zhenxun_bot/pull/756)
* fix: bilibili_sub, azur_draw_card [@pull/1090](https://github.com/HibiKier/zhenxun_bot/pull/1090)
* 修复原神资源查询查询完毕后图片存储错误
* b站订阅发送 与 b站订阅 使用相同开关,即:关闭b站订阅
### 2022/5/31
### 2022/9/10
* 修复开启/关闭全部功能时帮助图片未重绘 [@pull/721](https://github.com/HibiKier/zhenxun_bot/pull/721)
* bot_friend_group将group_handle.修改为friend_handle. [@pull/711](https://github.com/HibiKier/zhenxun_bot/pull/711)
* 修复发红包图片背景未透明化;修复原神树脂提醒参数错误 [@pull/712](https://github.com/HibiKier/zhenxun_bot/pull/712)
* 修复抽卡插件:方舟更新UP池信息时,若公告的第一个池子过期会导致无法更新UP池信息 [@pull/707](https://github.com/HibiKier/zhenxun_bot/pull/707)
* 商店插件判断是否有requirements.txt文件 [@pull/705](https://github.com/HibiKier/zhenxun_bot/pull/705)
* 删除原神玩家查询api返回变更的多余键值
* 优化了text2image方法
* 自定义群欢迎消息参数不完全时提示报错
* 修改bt插件的url地址 [@pull/1067](https://github.com/HibiKier/zhenxun_bot/pull/1067)
### 2022/5/29 \[v0.1.5.8]
### 2022/9/8
* 提供了真寻适配仓库的插件 安装/卸载 操作
* 暂时关闭了插件资源清空
* 通过指令安装插件时会在插件目录下生成plugin_info.json记录当前插件信息
* 添加插件数据初始化判断
### 2022/5/28
### 2022/9/4
* 修复私聊无法添加昵称
* 修复原神玩家查询层岩巨渊地下矿区没开时报错
* 修复 ```休息吧``` 无法阻断戳一戳
* 当图库无图片时,戳一戳将略过发送图片
* 新增搜图提供配置项```ALLOW_GROUP_R18```:允许在群聊中使用r18参数
* 新增自动更新插件```UPDATE_REMIND```:新版本提醒,原配置项```AUTO_UPDATE_ZHENXUN```改为自动更新升级
* black_word新增当群权限为-1时不再检测该群
* 修复非超级用户绑定原神cookie会被black_word阻拦
* 修复微博热搜报错,发红包小bug [@pull/688](https://github.com/HibiKier/zhenxun_bot/pull/688)
* 更多的中文提示
* 旧词条提供图片迁移(需要重新获取old_model文件,并将数据库中user_qq为0的数据删除)
### 2022/5/26
### 2022/9/3
* 修复\[滴滴滴]会被转义成&#91;滴滴滴&#93;导致无法触发的问题
* 将一些错误以中文提示输出
* 更新BT搜索源地址 [@pull/668](https://github.com/HibiKier/zhenxun_bot/pull/668)
* 更新抽卡插件 [@pull/667](https://github.com/HibiKier/zhenxun_bot/pull/667)
* 原神玩家查询增加须弥地区 [@pull/1053](https://github.com/HibiKier/zhenxun_bot/pull/1053)
* av号覆盖全面,且修复av号链接 [@pull/1033](https://github.com/HibiKier/zhenxun_bot/pull/1033)
* 修复词条含有CQ回答的模糊匹配无法被解析
* 禁言检测图片在内存中获取图片hash
* B站订阅在群里中任意群管理员可以统一管理(原来为管理员1无法删除管理员2的订阅)
* 修复原神资源查询地图api数据变更导致更新的地图不完全
### 2022/5/25
### 2022/8/27
* 修复webui中CountLimit字段limit_type类型错误
* 修改nickname插件:一处文案错误,添加敏感词 [@pull/624](https://github.com/HibiKier/zhenxun_bot/pull/624)
* gamedraw的ba卡池搬运了过来并且进行了真寻的适配 [@pull/617](https://github.com/HibiKier/zhenxun_bot/pull/617)
* feat: stream downloading and progress [@pull/607](https://github.com/HibiKier/zhenxun_bot/pull/607)
* 修改翻译插件,添加缺失的冒号 [@pull/602](https://github.com/HibiKier/zhenxun_bot/pull/602)
* 修复碧蓝航线/明日方舟up池解析出错的问题 [@pull/610](https://github.com/HibiKier/zhenxun_bot/pull/602)
* 修复签到积分双倍后,日志记录获得积分变4倍问题 [@pull/1044](https://github.com/HibiKier/zhenxun_bot/pull/1044)
### 2022/5/24
### 2022/8/26
* fix: 修正了签到时日期时间的显示不补齐零的bug (符合日期时间表示法 ISO 8601)[@pull/600](https://github.com/HibiKier/zhenxun_bot/pull/600)
* 更新"微博热搜"接口 [@pull/579](https://github.com/HibiKier/zhenxun_bot/pull/579)
* refactor&fix(manager): modify argument [@pull/576](https://github.com/HibiKier/zhenxun_bot/pull/576)
* 修复复读不能复读图片的问题 [@pull/573](https://github.com/HibiKier/zhenxun_bot/pull/573)
* 修复抽卡插件:方舟抽卡的抽取和显示问题 [@pull/581](https://github.com/HibiKier/zhenxun_bot/pull/581)
* 修复群管理员无法添加词条
* 修复词条关键词"问"前空格问题
### 2022/5/23 \[v0.1.5.6]
### 2022/8/23
* 修复"清除已删除插件数据" [@pull/545](https://github.com/HibiKier/zhenxun_bot/pull/545)
* 修复有置顶的up主B站动态获取失败 [@pull/552](https://github.com/HibiKier/zhenxun_bot/pull/552)
* 添加pixiv搜图多关键词支持;修复p站搜图数量参数问题 [@pull/441](https://github.com/HibiKier/zhenxun_bot/pull/441)
* 修复开箱更新价格错误传参
* 修复pix无法正确查询uid
* 新增色图插件添加配置项```ALLOW_GROUP_R18```:允许群聊中使用色图r
* 新增PIX插件添加配置项```ALLOW_GROUP_SETU```:允许非超级用户使用-s参数
* 新增PIX插件添加配置项```ALLOW_GROUP_R18```:允许非超级用户使用-r参数
* 修了下模糊匹配 issue#1026 [@pull/1026](https://github.com/HibiKier/zhenxun_bot/pull/1026)
### 2022/5/22 \[v0.1.5.4]
### 2022/8/22
* 使用action自动更新poetry.lock [@pull/515](https://github.com/HibiKier/zhenxun_bot/pull/515)
* fix(bilibili_sub): card is None and timeout [@pull/516](https://github.com/HibiKier/zhenxun_bot/pull/516)
* 修复了epic有时获取新免费游戏消息时获取不到图片
* 修复好感度满时签到出错(虽然是不可能满的
* 修复原神资源图标下载路径错误
* 修复自动更新群组可能失败
* 修复首次安装时词条旧表出错(因为根本就没有这张表!)
* 取消配置替换定时任务,统一存储
* 对米游社cookie进行判断,整合米游社签到信息 [@pull/1014](https://github.com/HibiKier/zhenxun_bot/pull/1014)
* 修正尘歌壶和质变仪图片获取地址 [@pull/1010](https://github.com/HibiKier/zhenxun_bot/pull/1010)
* 修复词库问答 **很多** 问题[@pull/1012](https://github.com/HibiKier/zhenxun_bot/pull/1012)
### 2022/5/21
### 2022/8/21 \[v0.1.6.3]
* 修复搜番无结果时报错无正确反馈
* 解锁了windows上无法使用playwright的限制
* 修复p搜对应pid有多张图时出错,改为连续发送图片
* 修复p搜对数字的错误判断
* 修复添加商品折扣无法正确添加
* 修复了bilibili订阅直播间订阅up名称不一致的问题
* 修复原神玩家查询没开地图时报错
* 最低priority修改为 999
* 修复刷屏检测失效
* 修复刷屏检测设置命令无法生效
* 优化刷屏显示设置禁言时长显示,并改为分钟
* 修复了多连开箱无法指定武器箱
* 修复识番链接无法正确获取
* 新增真寻入群时即刻刷新权限
* 提高了微博热搜截图的等待时间
* 重构群词条,改为词库Plus,增加 精准|模糊|正则 问题匹配,问题与回答均支持at,image,face,超级用户额外提供 全局|私聊 词库设置,数据迁移目前只提供了问题和回答都是纯文本的词条
* 修复b站转发解析av号无法解析
* B站订阅直播订阅支持短号
* 开箱提供重置开箱命令,重置今日所有开箱数据(重置次数,并不会删除今日已开箱记录)
* 提供全局字典GDict,通过from utils.utils import GDict导入
* 适配omega 13w张图的数据结构表(建议删表重导)
* 除首次启动外将配置替换加入单次定时任务,加快启动速度
* fix: WordBank.check() [@pull/1008](https://github.com/HibiKier/zhenxun_bot/pull/1008)
* 改进插件 `我有一个朋友`,避免触发过于频繁 [@pull/1001](https://github.com/HibiKier/zhenxun_bot/pull/1001)
* 原神便笺新增洞天宝钱和参量质变仪提示 [@pull/1005](https://github.com/HibiKier/zhenxun_bot/pull/1005)
* 新增米游社签到功能,自动领取(白嫖)米游币 [@pull/991](https://github.com/HibiKier/zhenxun_bot/pull/991)
### 2022/5/19
### 2022/8/14
* fix: mihoyo bbs api changed [@pull/357](https://github.com/HibiKier/zhenxun_bot/pull/357)
* Add word_clouds [@pull/265](https://github.com/HibiKier/zhenxun_bot/pull/265)
* Fix wrong live streamer name [@pull/284](https://github.com/HibiKier/zhenxun_bot/pull/284)
* 修复epic未获取到时间时出错
* 修复订阅主播时动态获取的id是直播间id
### 2022/5/16
### 2022/8/8
* 词条支持图片和@问题 [@pull/160](https://github.com/HibiKier/zhenxun_bot/pull/160)
* 修复赛马娘重载卡池失败的问题 [@pull/969](https://github.com/HibiKier/zhenxun_bot/pull/969)
### 2022/5/15
### 2022/8/3
* 修复了商店商品无法正确添加
* 修复了多张色图无法正确发送
* 修复 bili动态链接在投稿视频时URL和分割线连在一起 [@pull/951](https://github.com/HibiKier/zhenxun_bot/pull/961)
* 更新 Epic 免费游戏商城链接拼接规则 [@pull/957](https://github.com/HibiKier/zhenxun_bot/pull/957)
### 2022/5/14
### 2022/8/6
* 修复B站动态生成失败的问题 [@pull/159](https://github.com/HibiKier/zhenxun_bot/pull/159)
### 2022/5/11
* fix: 更改p搜api,解决p搜无法使用的问题 [@pull/155](https://github.com/HibiKier/zhenxun_bot/pull/155)
### 2022/5/9 \[v0.1.5.3]
* 替换了疫情API
* 修复了私聊.ban/.unban出错
### 2022/5/5
* 修改bilibili_sub插件在windows平台下报错 [@pull/153](https://github.com/HibiKier/zhenxun_bot/pull/153)
### 2022/5/3 \[v0.1.5.2]
* 商品使用函数可以添加特定参数,例如:user_id, group_id, ShopParam等以及自己提供的参数
* 添加商品注册装饰器shop_register
* 修复商品函数kwargs无法获取参数值
### 2022/5/1
* 删除了`group_last_chat`插件(该功能可由`chat_history`替代
* 新增敏感词检测(全新反击系统,是时候重拳出击了
### 2022/4/26
* 修复了群白名单无法正确添加
* 优化了管理员帮助图片,背景图层将位于最下层
* 修复了树脂140时不断提醒(未测试
* 新增了消息记录的消息排行
* WebUI新增CPU,内存,磁盘监控
* WebUI新增资源文件夹统计可视化
### 2022/4/12
* 修复b了命令私聊出错
### 2022/4/10 \[v0.1.4.7]
* 新增消息记录模块
* 丰富处理请求操作提示
* web ui新增配置项修改
### 2022/4/9
* fix: 更新问题,戳一戳图片路径问题 [@pull/144](https://github.com/HibiKier/zhenxun_bot/pull/144)
### 2022/4/8
* 修复原神玩家查询
### 2022/4/6
* update search_type [@pull/143](https://github.com/HibiKier/zhenxun_bot/pull/143)
### 2022/4/5 \[v0.1.4.6]
* 修复web修改插件后帮助图片生成错误
### 2022/4/4 \[v0.1.4.5]
* 替换了bt搜索URL
* 优化使用playwright的相关代码
* 原神玩家查询新增层岩巨渊探索
* 修复原神便笺角色头像黑框
* 修复同意群聊请求错误
* 提供webui方面的api
* 新增web-ui(前端简易管理页面插件)插件
### 2022/3/21
* 修复statistics_handle.py乱码
### 2022/3/18 \[v0.1.4.4]
* 修复戳一戳无法功能关闭与ban禁用
* 新增图片搜索 search_image
### 2022/3/7
* 优化增删权限插件
### 2022/3/6
* 修复树脂提醒无法开启
* 修复p搜图片路径错误
### 2022/3/3 \[v0.1.4.3]
* 修复手动同意群聊请求依旧退出
### 2022/3/1 \[v0.1.4.2]
* 0.1.4内容
### 2022/2/27 \[v0.1.4.1]
* 优化抽卡
### 2022/2/25 \[v0.1.4]
* PIX提供配置MAX_ONCE_NUM2FORWARD:当单次发送图片超过指定张数且在群聊时,将转为合并消息
* 修复点歌无法正确发送
* 修复我有一个朋友有时文本会包含CQ码
* 修复群欢消息被动控制文本未删除 [@pull/124](https://github.com/HibiKier/zhenxun_bot/pull/124)
* message_builder.image不再提供参数:abspath
### 2022/2/23
* 插件状态将从已加载插件模块中读取
* 修复b站订阅插件订阅失败问题
* 修复重启命令无法使用
### 2022/2/21 \[v0.1.3.2]
* 群权限为-1时,超级用户发送的命令依旧生效
* 当群权限为-1时,被动技能也将不会发送
* 修复功能开关,b站转发解析,复读 ignore无法使用
* 修复色图下载文件名与路径错误
* 修复被动技能提醒有时无法删除控制文本
### 2022/2/20 \[v0.1.3.1]
* 修复pix下载临时文件目录错误
* 修复AI,天气,发送图片ignore导致无法使用
* 修复纯文本被动技能提醒有时无法删除控制文本
### 2022/2/19 \[v0.1.3] (nonebot beta2!)
* 由于nonebot升级版本,提供更新建议(__该次升级将会导致nonebot.beta1以下的插件无法使用__
* 保证services,utils,configs,plugins,basic_plugins,文件夹均为最新
* 根目录有pyproject.toml与poetry.lock
* 执行命令:
* pip3 install poetry
* poetry install
* poetry shell
* playwright install chromium
* python3 bot.py
* 适配nonebot.beta2
* 删除图片搜索 nonebot_plugin_picsearcher
* 替换cos api
* 原神签到树脂提醒新增绑定群里,在某群绑定uid就会在某群发送提醒信息(有好友则私聊,需要重新绑定uid
* 修改update_info.json
* 修复原神资源查询下载数据失败时导致报错
* 优化BuildImage.circle()锯齿问题 [@pull/109](https://github.com/HibiKier/zhenxun_bot/pull/109)
* epic restful 替换 [@pull/119](https://github.com/HibiKier/zhenxun_bot/pull/119)
* fix: 修复远古时期残留的epic推送问题 [@pull/122](https://github.com/HibiKier/zhenxun_bot/pull/122)
### 2022/2/11
* 修复pix不使用反代无法下载图片
### 2022/2/10 \[v0.1.1]
* 修复购买道具出错
### 2022/2/9 \[v0.1]
* 新增原神自动签到和手动签到
* 新增原神树脂提醒
* 新增手动重载Config.yaml命令以及重载配置定时任务(极少部分帮助或配置可能需要重启
* 修改了发送本地图库的matcher,改为on_message
* register_use可以通过返回值发送消息
* 修复修改商品时限制时间出错
* 修复超时商品依旧可以被购买
### 2022/1/16 \[v0.0.9.0]
* Ai提供文本敏感词过滤器
* 疫情插件适配新版腾讯API
* 修复/t回复带空格切分
* 修复原神玩家查询缺少渊下宫和稻妻家园以及角色不完全
* 修复方法 text2image 中 padding 和 font 无法对纯文本生效
* 修复签到图片中信息并未使用配置文件中的色图概率
* 修改原神大地图合成方式,改为先压缩再合成
* bag_user弃用字段props(该字段会在下次更新删除),使用新字段property
* 数据库中所有belonging_group统一修改为group_id
* 商店将registered_use和register_goods更名为register_use何register_goods
* 商品注册提供了kwargs参数提供:
* bot
* event
* 特殊字段
* “send_success_msg”(发送成功的交互信息->即:使用道具 {name} {num} 次成功)
* “_max_num_limit”(该道具单次使用的最多个数,默认1)
### 2022/1/5 \[v0.0.8.2]
* 提供金币消费hook,可在plugins2settings.yaml中配置该功能需要消费的金币
* 商店插件将作为内置插件移动至basic_plugins
* 商店插件通过export提供了方法,不需要修改商店插件代码添加商品数据和生效方法
* 修改了hook插件顺序,主要以auth_hook为主
* 修改商店图片样式
* 取消每次启动更新城市列表(首次除外),采用定时更新,加快bot启动速度
* 取消每次启动时截取今日素材,采用调用时截取保存,加快bot启动速度
* 更新色图时当图片404时会删除并替换
* 疫情消息回复改为图片
* 修复商店折扣和限时时间无法生效
* 修复原神玩家查询尘歌壶缺少图片
### 2021/12/26
* 修复群词条问题 空格 会被录入导致不断回复
* 修复米游社app替换api导致无法正常查询
### 2021/12/24
* 支持国际疫情数据查询 [@pull/99](https://github.com/HibiKier/zhenxun_bot/pull/99)
### 2021/12/20
* 只有发布小于存储时间的新动态/视频的时候才获取并推送 [@pull/96](https://github.com/HibiKier/zhenxun_bot/pull/96)
### 2021/12/16 \[v0.0.7.0]
* 提供了真寻群聊功能总开关和对应默认配置项,命令:休息吧 醒来
* 新增原神玩家查询,原神便笺查询
* 群功能管理提供全部开启/关闭命令:开启/关闭全部功能
* 提供主要数据自动备份,且提供自定义配置项
* 提供命令:关于,用于介绍Bot之类的
* 新增命令exec,用于执行sql语句
* 签到提供参数 "all",用于签到所有群聊
* Ban提醒提供cd
* 本地图库提供配置项SHOW_ID,用于设置发送图片时是否显示id
* 色图和PIX提供配置项SHOW_INFO,用于设置发送图片时是否显示图片信息
* 所有被动技能提供了进群默认状态配置项
* 修复添加权限第二种添加形式无法正确添加正确的权限
* 修复签到获取好感度卡时金币不会增加
* 修复当红包数量不合法时依旧扣除金币
* 修复金币红包再次使用塞红包时无法正确退回上次未开完的金币
* 修复 滴滴滴- 只包含图片时不会发送至管理员
* 修复添加权限等级错误
* 修复群词条以bot名称为开头时无法正确触发
* 修改了权限插件加载顺序防止小概率优先加载权限插件引起报错
* 本地图库新图库会统一建立在resource/img/image_management文件夹下,如果该文件夹内未找到图库,会从上级目录查找(即:resource/img/)
* 修复了原神自动签到返回invalid request的问题,新增查看我的cookie命令 [@pull/971](https://github.com/HibiKier/zhenxun_bot/pull/971)
<br>
__..... 更多更新信息请查看文档__
## Todo
- [ ] web管理
## 爱发电
<img width="365px" height="450px" src="https://user-images.githubusercontent.com/45528451/175059389-cfeb8174-fa07-4939-80ab-a039087a50f6.png">
- [x] web管理
## 感谢
[botuniverse / onebot](https://github.com/botuniverse/onebot) :超棒的机器人协议
+1 -1
View File
@@ -1 +1 @@
__version__: v0.1.6.2
__version__: v0.1.6.5
+33 -35
View File
@@ -23,7 +23,7 @@ except ModuleNotFoundError:
import json
async def group_current_status(group_id: int) -> str:
def group_current_status(group_id: int) -> str:
"""
获取当前所有通知的开关
:param group_id: 群号
@@ -31,17 +31,17 @@ async def group_current_status(group_id: int) -> str:
rst = "[被动技能 状态]\n"
_data = group_manager.get_task_data()
for task in _data.keys():
rst += f'{_data[task]}: {"√" if await group_manager.check_group_task_status(group_id, task) else "×"}\n'
rst += f'{_data[task]}: {"√" if group_manager.check_group_task_status(group_id, task) else "×"}\n'
return rst.strip()
custom_welcome_msg_json = (
Path() / "data" / "custom_welcome_msg" / "custom_welcome_msg.json"
Path() / "data" / "custom_welcome_msg" / "custom_welcome_msg.json"
)
async def custom_group_welcome(
msg: str, imgs: List[str], user_id: int, group_id: int
msg: str, imgs: List[str], user_id: int, group_id: int
) -> str:
"""
替换群欢迎消息
@@ -87,13 +87,13 @@ task_data = None
async def change_group_switch(cmd: str, group_id: int, is_super: bool = False):
global task_data
"""
修改群功能状态
:param cmd: 功能名称
:param group_id: 群号
:param is_super: 是否位超级用户,超级用户用于私聊开关功能状态
"""
global task_data
if not task_data:
task_data = group_manager.get_task_data()
group_help_file = DATA_PATH / "group_help" / f"{group_id}.png"
@@ -104,43 +104,44 @@ async def change_group_switch(cmd: str, group_id: int, is_super: bool = False):
if cmd == "全部被动":
for task in task_data:
if status == "开启":
if not await group_manager.check_group_task_status(group_id, task):
await group_manager.open_group_task(group_id, task)
if not group_manager.check_group_task_status(group_id, task):
group_manager.open_group_task(group_id, task)
else:
if await group_manager.check_group_task_status(group_id, task):
await group_manager.close_group_task(group_id, task)
if group_manager.check_group_task_status(group_id, task):
group_manager.close_group_task(group_id, task)
if group_help_file.exists():
group_help_file.unlink()
return f"已 {status} 全部被动技能!"
if cmd == "全部功能":
for f in plugins2settings_manager.get_data():
if status == "开启":
group_manager.unblock_plugin(f, group_id)
group_manager.unblock_plugin(f, group_id, False)
else:
group_manager.block_plugin(f, group_id)
group_manager.block_plugin(f, group_id, False)
group_manager.save()
if group_help_file.exists():
group_help_file.unlink()
return f"已 {status} 全部功能!"
if cmd in [task_data[x] for x in task_data.keys()]:
if cmd.lower() in [task_data[x].lower() for x in task_data.keys()]:
type_ = "task"
modules = [x for x in task_data.keys() if task_data[x] == cmd]
modules = [x for x in task_data.keys() if task_data[x].lower() == cmd.lower()]
for module in modules:
if is_super:
module = f"{module}:super"
if status == "开启":
if type_ == "task":
if await group_manager.check_group_task_status(group_id, module):
if group_manager.check_group_task_status(group_id, module):
return f"被动 {task_data[module]} 正处于开启状态!不要重复开启."
await group_manager.open_group_task(group_id, module)
group_manager.open_group_task(group_id, module)
else:
if group_manager.get_plugin_status(module, group_id):
return f"功能 {cmd} 正处于开启状态!不要重复开启."
group_manager.unblock_plugin(module, group_id)
else:
if type_ == "task":
if not await group_manager.check_group_task_status(group_id, module):
if not group_manager.check_group_task_status(group_id, module):
return f"被动 {task_data[module]} 正处于关闭状态!不要重复关闭."
await group_manager.close_group_task(group_id, module)
group_manager.close_group_task(group_id, module)
else:
if not group_manager.get_plugin_status(module, group_id):
return f"功能 {cmd} 正处于关闭状态!不要重复关闭."
@@ -197,17 +198,17 @@ def _get_plugin_status() -> MessageSegment:
flag = plugins_manager.get_plugin_block_type(module)
flag = flag.upper() + " CLOSE" if flag else "OPEN"
try:
plugin_name = plugins_manager.get(module)["plugin_name"]
plugin_name = plugins_manager.get(module).plugin_name
if (
"[Hidden]" in plugin_name
or "[Admin]" in plugin_name
or "[Superuser]" in plugin_name
"[Hidden]" in plugin_name
or "[Admin]" in plugin_name
or "[Superuser]" in plugin_name
):
continue
rst += f"{plugin_name}"
except KeyError:
rst += f"{module}"
if plugins_manager.get(module)["error"]:
if plugins_manager.get(module).error:
rst += "[ERROR]"
rst += "\n"
flag_str += f"{flag}\n"
@@ -238,12 +239,12 @@ async def update_member_info(group_id: int, remind_superuser: bool = False) -> b
async with db.transaction():
# 更新权限
if (
user_info["role"]
in [
"owner",
"admin",
]
and not await LevelUser.is_group_flag(user_info["user_id"], group_id)
user_info["role"]
in [
"owner",
"admin",
]
and not await LevelUser.is_group_flag(user_info["user_id"], group_id)
):
await LevelUser.set_level(
user_info["user_id"],
@@ -272,10 +273,10 @@ async def update_member_info(group_id: int, remind_superuser: bool = False) -> b
"%Y-%m-%d %H:%M:%S",
)
if await GroupInfoUser.add_member_info(
user_info["user_id"],
user_info["group_id"],
nickname,
join_time,
user_info["user_id"],
user_info["group_id"],
nickname,
join_time,
):
_exist_member_list.append(int(user_info["user_id"]))
logger.info(f"用户{user_info['user_id']} 所属{user_info['group_id']} 更新成功")
@@ -317,7 +318,4 @@ def set_group_bot_status(group_id: int, status: bool) -> str:
return "呜..醒来了..."
else:
group_manager.shutdown_group_bot_status(group_id)
# for x in group_manager.get_task_data():
# group_manager.close_group_task(group_id, x)
return "那我先睡觉了..."
@@ -36,11 +36,11 @@ custom_welcome = on_command(
@custom_welcome.handle()
async def _(event: GroupMessageEvent, arg: Message = CommandArg()):
msg = arg.extract_plain_text().strip()
img = get_message_img(event.json())
if not msg and not img:
await custom_welcome.finish(__plugin_usage__)
try:
msg = arg.extract_plain_text().strip()
img = get_message_img(event.json())
if not msg and not img:
await custom_welcome.finish(__plugin_usage__)
await custom_welcome.send(
await custom_group_welcome(msg, img, event.user_id, event.group_id),
at_sender=True,
+11 -6
View File
@@ -2,18 +2,23 @@ from nonebot.adapters.onebot.v11 import Event
from utils.manager import group_manager, plugins2settings_manager
from utils.utils import get_message_text
from services.log import logger
import time
cmd = []
v = time.time()
def switch_rule(event: Event) -> bool:
"""
检测文本是否是关闭功能命令
:param event: pass
说明:
检测文本是否是关闭功能命令
参数:
:param event: pass
"""
global cmd
try:
if not cmd:
if not cmd or time.time() - v > 60 * 60:
cmd = ["关闭全部被动", "开启全部被动", "开启全部功能", "关闭全部功能"]
_data = group_manager.get_task_data()
for key in _data:
@@ -22,10 +27,10 @@ def switch_rule(event: Event) -> bool:
cmd.append(f"开启 {_data[key]}")
cmd.append(f"关闭 {_data[key]}")
_data = plugins2settings_manager.get_data()
for key in _data:
for key in _data.keys():
try:
if isinstance(_data[key]["cmd"], list):
for x in _data[key]["cmd"]:
if isinstance(_data[key].cmd, list):
for x in _data[key].cmd:
cmd.append(f"开启{x}")
cmd.append(f"关闭{x}")
cmd.append(f"开启 {x}")
@@ -110,7 +110,7 @@ async def _():
@group_task_status.handle()
async def _(event: GroupMessageEvent):
await group_task_status.send(await group_current_status(event.group_id))
await group_task_status.send(group_current_status(event.group_id))
@group_status.handle()
+2 -2
View File
@@ -16,9 +16,9 @@ admin_help_image = IMAGE_PATH / 'admin_help_img.png'
@driver.on_bot_connect
async def init_task(bot: Bot = None):
async def init_task():
if not group_manager.get_task_data():
await group_manager.init_group_task()
group_manager.load_task()
logger.info(f'已成功加载 {len(group_manager.get_task_data())} 个被动技能.')
+2 -2
View File
@@ -60,7 +60,7 @@ async def _():
try:
await bot.send_group_msg(group_id=g, message="[[_task|zwa]]早上好" + result)
except ActionFailed:
logger.warning(f"{g} 群被禁言中,无法发送早安")
logger.warning(f"{g} 发送早安失败")
except Exception as e:
logger.error(f"早晚安错误 e:{e}")
@@ -83,7 +83,7 @@ async def _():
group_id=g, message=f"[[_task|zwa]]{NICKNAME}要睡觉了,你们也要早点睡呀" + result
)
except ActionFailed:
logger.warning(f"{g} 群被禁言中,无法发送晚安")
logger.warning(f"{g} 发送晚安失败")
except Exception as e:
logger.error(f"早晚安错误 e:{e}")
+1 -1
View File
@@ -44,7 +44,7 @@ async def _(bot: Bot, event: MessageEvent, arg: Message = CommandArg()):
gl = [
g["group_id"]
for g in gl
if await group_manager.check_group_task_status(g["group_id"], "broadcast")
if group_manager.check_group_task_status(g["group_id"], "broadcast")
]
g_cnt = len(gl)
cnt = 0
@@ -16,11 +16,12 @@ __zx_plugin_name__ = "消息统计"
__plugin_usage__ = """
usage:
发言记录统计
regex:(周|月)?消息排行(des|DES)?(n=[0-9]{1,2})?
regex:(周|月|日)?消息排行(des|DES)?(n=[0-9]{1,2})?
指令:
消息统计?(des)?(n=?)
周消息统计?(des)?(n=?)
月消息统计?(des)?(n=?)
日消息统计?(des)?(n=?)
示例:
消息统计
消息统计des
@@ -31,7 +32,8 @@ __plugin_des__ = "发言消息排行"
__plugin_cmd__ = [
"消息统计",
"周消息统计",
"月消息统计"
"月消息统计",
"日消息统计"
]
__plugin_type__ = ("数据统计", 1)
__plugin_version__ = 0.1
@@ -42,7 +44,7 @@ __plugin_settings__ = {
}
msg_handler = on_regex(r"^(周|月)?消息统计(des|DES)?(n=[0-9]{1,2})?$", priority=5, block=True)
msg_handler = on_regex(r"^(周|月|日)?消息统计(des|DES)?(n=[0-9]{1,2})?$", priority=5, block=True)
@msg_handler.handle()
@@ -53,10 +55,14 @@ async def _(event: GroupMessageEvent, reg_group: Tuple[Any, ...] = RegexGroup())
num = num.split("=")[-1] if num else 10
if num and is_number(num) and 10 < int(num) < 50:
num = int(num)
if date in ["周"]:
date_scope = (datetime.now() - timedelta(days=7), datetime.now())
time_now = datetime.now()
zero_today = time_now - timedelta(hours=time_now.hour, minutes=time_now.minute, seconds=time_now.second)
if date in ["日"]:
date_scope = (zero_today, time_now)
elif date in ["周"]:
date_scope = (time_now - timedelta(days=7), time_now)
elif date in ["月"]:
date_scope = (datetime.now() - timedelta(days=30), datetime.now())
date_scope = (time_now - timedelta(days=30), time_now)
if rank_data := await ChatHistory.get_group_msg_rank(
gid, num, order or "DESC", date_scope
):
@@ -79,7 +85,7 @@ async def _(event: GroupMessageEvent, reg_group: Tuple[Any, ...] = RegexGroup())
pytz.timezone("Asia/Shanghai")
).replace(microsecond=0)
else:
date_scope = datetime.now().replace(microsecond=0)
date_scope = time_now.replace(microsecond=0)
date_str = f"日期:{date_scope} - 至今"
else:
date_str = f"日期:{date_scope[0].replace(microsecond=0)} - {date_scope[1].replace(microsecond=0)}"
+1 -1
View File
@@ -44,7 +44,7 @@ async def _(bot: Bot, event: MessageEvent, state: T_State):
@simple_help.handle()
async def _(bot: Bot, event: MessageEvent, state: T_State, arg: Message = CommandArg()):
async def _(bot: Bot, event: MessageEvent, arg: Message = CommandArg()):
msg = arg.extract_plain_text().strip()
is_super = False
if msg:
+17 -73
View File
@@ -1,3 +1,4 @@
from .utils import group_image, build_help_image
from utils.image_utils import BuildImage
from configs.path_config import IMAGE_PATH
from utils.manager import (
@@ -6,14 +7,11 @@ from utils.manager import (
plugins_manager,
group_manager,
)
from typing import Optional
from typing import Optional, List, Tuple
from services.log import logger
from pathlib import Path
from utils.utils import get_matchers
import random
import asyncio
import nonebot
import os
random_bk_path = IMAGE_PATH / "background" / "help" / "simple_help"
@@ -30,12 +28,10 @@ async def create_help_img(
:param help_image: 图片路径
:param simple_help_image: 简易帮助图片路径
"""
return await asyncio.get_event_loop().run_in_executor(
None, _create_help_img, group_id, help_image, simple_help_image
)
return await _create_help_img(group_id, help_image, simple_help_image)
def _create_help_img(
async def _create_help_img(
group_id: Optional[int], help_image: Path, simple_help_image: Path
):
"""
@@ -44,33 +40,31 @@ def _create_help_img(
:param help_image: 图片路径
:param simple_help_image: 简易帮助图片路径
"""
_matchers = get_matchers()
width = 0
matchers_data = {}
_des_tmp = {}
_plugin_name_tmp = []
_tmp = []
tmp_img = BuildImage(0, 0, plain_text="1", font_size=24)
font_height = tmp_img.h
# 插件分类
for matcher in _matchers:
for matcher in get_matchers(True):
plugin_name = None
_plugin = nonebot.plugin.get_plugin(matcher.plugin_name)
_plugin = matcher.plugin
if not _plugin:
logger.warning(f"获取 功能:{matcher.plugin_name} 失败...")
continue
metadata = _plugin.metadata
_module = _plugin.module
try:
plugin_name = _module.__getattribute__("__zx_plugin_name__")
plugin_name = metadata.name if metadata else _module.__getattribute__("__zx_plugin_name__")
try:
plugin_des = _module.__getattribute__("__plugin_des__")
plugin_des = metadata.description if metadata else _module.__getattribute__("__plugin_des__")
except AttributeError:
plugin_des = "_"
if (
"[hidden]" in plugin_name.lower()
or "[admin]" in plugin_name.lower()
or "[superuser]" in plugin_name.lower()
or plugin_name in _plugin_name_tmp
or plugin_name == "帮助"
):
continue
@@ -78,11 +72,9 @@ def _create_help_img(
text_type = 0
if plugins2settings_manager.get(
matcher.plugin_name
) and plugins2settings_manager[matcher.plugin_name].get("plugin_type"):
) and plugins2settings_manager.get(matcher.plugin_name).plugin_type:
plugin_type = tuple(
plugins2settings_manager.get_plugin_data(matcher.plugin_name)[
"plugin_type"
]
plugins2settings_manager.get_plugin_data(matcher.plugin_name).plugin_type
)
else:
try:
@@ -131,10 +123,7 @@ def _create_help_img(
if plugin_des not in _des_tmp:
_des_tmp[plugin_des] = plugin_name
except AttributeError as e:
if plugin_name not in _plugin_name_tmp:
logger.warning(f"获取功能 {matcher.plugin_name}: {plugin_name} 设置失败...e:{e}")
if plugin_name not in _plugin_name_tmp:
_plugin_name_tmp.append(plugin_name)
logger.warning(f"获取功能 {matcher.plugin_name}: {plugin_name} 设置失败...e:{e}")
help_img_list = []
simple_help_img_list = []
types = list(matchers_data.keys())
@@ -256,22 +245,8 @@ def _create_help_img(
if img.h > height:
height = img.h
width += img.w + 10
B = BuildImage(width + 100, height + 250, font_size=24)
width, _ = get_max_width_or_paste(simple_help_img_list, B)
bk = None
random_bk = os.listdir(random_bk_path)
if random_bk:
bk = random.choice(random_bk)
x = max(width + 50, height + 250)
B = BuildImage(
x,
x,
font_size=24,
color="#FFEFD5",
background=random_bk_path / bk,
)
B.filter("GaussianBlur", 10)
_, B = get_max_width_or_paste(simple_help_img_list, B, True)
image_group, h = group_image(simple_help_img_list)
B = await build_help_image(image_group, h)
w = 10
h = 10
for msg in ["目前支持的功能列表:", "可以通过 ‘帮助[功能名称]’ 来获取对应功能的使用方法", "或者使用 ‘详细帮助’ 来获取所有功能方法"]:
@@ -301,55 +276,24 @@ def _create_help_img(
B.save(simple_help_image)
def get_max_width_or_paste(
simple_help_img_list: list, B: BuildImage = None, is_paste: bool = False
) -> "int, BuildImage":
"""
获取最大宽度,或直接贴图
:param simple_help_img_list: 简单帮助图片列表
:param B: 背景图
:param is_paste: 是否直接贴图
"""
current_width = 50
current_height = 180
max_width = simple_help_img_list[0].w
for i in range(len(simple_help_img_list)):
try:
if is_paste and B:
B.paste(simple_help_img_list[i], (current_width, current_height), True)
current_height += simple_help_img_list[i].h + 40
if current_height + simple_help_img_list[i + 1].h > B.h - 10:
current_height = 180
current_width += max_width + 30
max_width = 0
elif simple_help_img_list[i].w > max_width:
max_width = simple_help_img_list[i].w
except IndexError:
pass
if current_width > simple_help_img_list[0].w + 50:
current_width += simple_help_img_list[-1].w
return current_width, B
def get_plugin_help(msg: str, is_super: bool = False) -> Optional[str]:
"""
获取功能的帮助信息
:param msg: 功能cmd
:param is_super: 是否为超级用户
"""
module = plugins2settings_manager.get_plugin_module(msg)
if not module:
module = admin_manager.get_plugin_module(msg)
module = plugins2settings_manager.get_plugin_module(msg) or admin_manager.get_plugin_module(msg)
if module:
try:
plugin = nonebot.plugin.get_plugin(module)
metadata = plugin.metadata
if plugin:
if is_super:
result = plugin.module.__getattribute__(
"__plugin_superuser_usage__"
)
else:
result = plugin.module.__getattribute__("__plugin_usage__")
result = metadata.usage if metadata else plugin.module.__getattribute__("__plugin_usage__")
if result:
width = 0
for x in result.split("\n"):
+105
View File
@@ -0,0 +1,105 @@
from typing import List, Tuple
from configs.path_config import IMAGE_PATH
from utils.image_utils import BuildImage
import os
import random
background_path = IMAGE_PATH / "background" / "help" / "simple_help"
async def build_help_image(image_group: List[List[BuildImage]], h: int):
bk = None
random_bk = os.listdir(background_path)
if random_bk:
bk = random.choice(random_bk)
A = BuildImage(
h,
h,
font_size=24,
color="#FFEFD5",
background=(background_path / bk) if bk else None,
)
curr_w = 50
for ig in image_group:
curr_h = 180
for img in ig:
await A.apaste(img, (curr_w, curr_h), True)
curr_h += img.h + 10
curr_w += max([x.w for x in ig]) + 30
return A
def group_image(image_list: List[BuildImage]) -> Tuple[List[List[BuildImage]], int]:
"""
说明:
根据图片大小进行分组
参数:
:param image_list: 排序图片列表
"""
image_list.sort(key=lambda x: x.h, reverse=True)
max_image = max(image_list, key=lambda x: x.h)
image_list.remove(max_image)
max_h = max_image.h
total_w = 0
# 图片分组
image_group = [[max_image]]
is_use = []
surplus_list = image_list[:]
for image in image_list:
if image.uid not in is_use:
group = [image]
is_use.append(image.uid)
curr_h = image.h
while True:
surplus_list = [x for x in surplus_list if x.uid not in is_use]
for tmp in surplus_list:
temp_h = curr_h + tmp.h + 10
if temp_h < max_h or abs(max_h - temp_h) < 100:
curr_h += tmp.h + 15
is_use.append(tmp.uid)
group.append(tmp)
break
else:
break
total_w += max([x.w for x in group]) + 15
image_group.append(group)
while surplus_list:
surplus_list = [x for x in surplus_list if x.uid not in is_use]
if not surplus_list:
break
surplus_list.sort(key=lambda x: x.h, reverse=True)
for img in surplus_list:
if img.uid not in is_use:
_w = 0
index = -1
for i, ig in enumerate(image_group):
if s := sum([x.h for x in ig]) > _w:
_w = s
index = i
if index != -1:
image_group[index].append(img)
is_use.append(img.uid)
max_h = 0
max_w = 0
for i, ig in enumerate(image_group):
if (_h := sum([x.h + 15 for x in ig])) > max_h:
max_h = _h
max_w += max([x.w for x in ig]) + 30
is_use.clear()
while abs(max_h - max_w) > 200 and len(image_group) - 1 >= len(image_group[-1]):
for img in image_group[-1]:
_min_h = 0
_min_index = -1
for i, ig in enumerate(image_group):
if i not in is_use and (_h := sum([x.h for x in ig]) + img.h) > _min_h:
_min_h = _h
_min_index = i
is_use.append(_min_index)
image_group[_min_index].append(img)
max_w -= max([x.w for x in image_group[-1]])
image_group.pop(-1)
return image_group, max(max_h + 250, max_w + 70)
+469 -5
View File
@@ -1,5 +1,37 @@
from nonebot.adapters.onebot.v11 import GroupMessageEvent, PrivateMessageEvent
from utils.manager import plugins2block_manager, StaticData
from typing import Optional
import nonebot
from nonebot.adapters.onebot.v11 import (
GroupMessageEvent,
PrivateMessageEvent,
Bot,
Event,
MessageEvent,
Message,
PokeNotifyEvent,
)
from nonebot.exception import ActionFailed, IgnoredException
from nonebot.internal.matcher import Matcher
from models.bag_user import BagUser
from models.ban_user import BanUser
from models.friend_user import FriendUser
from models.group_member_info import GroupInfoUser
from models.level_user import LevelUser
from models.user_shop_gold_log import UserShopGoldLog
from utils.manager import (
plugins2block_manager,
StaticData,
plugins2settings_manager,
group_manager,
admin_manager,
plugins_manager,
plugins2cd_manager,
plugins2count_manager,
)
from utils.message_builder import at
from utils.utils import FreqLimiter
from configs.config import Config
import time
ignore_rst_module = ["ai", "poke", "dialogue"]
@@ -8,7 +40,6 @@ other_limit_plugins = ["poke"]
class StatusMessageManager(StaticData):
def __init__(self):
super().__init__(None)
@@ -39,8 +70,8 @@ def set_block_limit_false(event, module):
"""
if plugins2block_manager.check_plugin_block_status(module):
plugin_block_data = plugins2block_manager.get_plugin_block_data(module)
check_type = plugin_block_data["check_type"]
limit_type = plugin_block_data["limit_type"]
check_type = plugin_block_data.check_type
limit_type = plugin_block_data.limit_type
if not (
(isinstance(event, GroupMessageEvent) and check_type == "private")
or (isinstance(event, PrivateMessageEvent) and check_type == "group")
@@ -50,3 +81,436 @@ def set_block_limit_false(event, module):
block_type_ = event.group_id
plugins2block_manager.set_false(block_type_, module)
async def send_msg(msg: str, bot: Bot, event: MessageEvent):
"""
说明:
发送信息
参数:
:param msg: pass
:param bot: pass
:param event: pass
"""
if "[uname]" in msg:
uname = event.sender.card or event.sender.nickname
msg = msg.replace("[uname]", uname)
if "[nickname]" in msg:
if isinstance(event, GroupMessageEvent):
nickname = await GroupInfoUser.get_group_member_nickname(
event.user_id, event.group_id
)
else:
nickname = await FriendUser.get_friend_nickname(event.user_id)
msg = msg.replace("[nickname]", nickname)
if "[at]" in msg and isinstance(event, GroupMessageEvent):
msg = msg.replace("[at]", str(at(event.user_id)))
try:
if isinstance(event, GroupMessageEvent):
status_message_manager.add(event.group_id)
await bot.send_group_msg(group_id=event.group_id, message=Message(msg))
else:
status_message_manager.add(event.user_id)
await bot.send_private_msg(user_id=event.user_id, message=Message(msg))
except ActionFailed:
pass
class ReturnException(Exception):
pass
class AuthChecker:
"""
权限检查
"""
checker: Optional["AuthChecker"] = None
def __init__(self):
self._flmt = FreqLimiter(Config.get_config("hook", "CHECK_NOTICE_INFO_CD"))
self._flmt_g = FreqLimiter(Config.get_config("hook", "CHECK_NOTICE_INFO_CD"))
self._flmt_s = FreqLimiter(Config.get_config("hook", "CHECK_NOTICE_INFO_CD"))
self._flmt_c = FreqLimiter(Config.get_config("hook", "CHECK_NOTICE_INFO_CD"))
def __new__(cls, *args, **kwargs):
if not cls.checker:
cls.checker = super().__new__(cls)
return cls.checker
async def auth(self, matcher: Matcher, bot: Bot, event: Event):
"""
说明:
权限检查
参数:
:param matcher: matcher
:param bot: bot
:param event: event
"""
try:
plugin_name = matcher.plugin_name
cost_gold = await self.auth_cost(plugin_name, bot, event)
await self.auth_basic(plugin_name, bot, event)
self.auth_group(plugin_name, bot, event)
await self.auth_admin(plugin_name, matcher, bot, event)
await self.auth_plugin(plugin_name, matcher, bot, event)
await self.auth_limit(plugin_name, bot, event)
if cost_gold and str(event.user_id) not in bot.config.superusers:
await BagUser.spend_gold(event.user_id, event.group_id, cost_gold)
except ReturnException:
return
async def auth_limit(self, plugin_name: str, bot: Bot, event: Event):
"""
说明:
插件限制
参数:
:param plugin_name: 模块名
:param bot: bot
:param event: event
"""
if plugins2cd_manager.check_plugin_cd_status(plugin_name):
plugin_cd_data = plugins2cd_manager.get_plugin_cd_data(plugin_name)
check_type = plugin_cd_data.check_type
limit_type = plugin_cd_data.limit_type
msg = plugin_cd_data.rst
if (
(isinstance(event, PrivateMessageEvent) and check_type == "private")
or (isinstance(event, GroupMessageEvent) and check_type == "group")
or plugins2cd_manager.get_plugin_data(plugin_name).check_type == "all"
):
cd_type_ = event.user_id
if limit_type == "group" and isinstance(event, GroupMessageEvent):
cd_type_ = event.group_id
if not plugins2cd_manager.check(plugin_name, cd_type_):
if msg:
await send_msg(msg, bot, event)
raise IgnoredException(f"{plugin_name} 正在cd中...")
else:
plugins2cd_manager.start_cd(plugin_name, cd_type_)
# Block
if plugins2block_manager.check_plugin_block_status(plugin_name):
plugin_block_data = plugins2block_manager.get_plugin_block_data(plugin_name)
check_type = plugin_block_data.check_type
limit_type = plugin_block_data.limit_type
msg = plugin_block_data.rst
if (
(isinstance(event, PrivateMessageEvent) and check_type == "private")
or (isinstance(event, GroupMessageEvent) and check_type == "group")
or check_type == "all"
):
block_type_ = event.user_id
if limit_type == "group" and isinstance(event, GroupMessageEvent):
block_type_ = event.group_id
if plugins2block_manager.check(block_type_, plugin_name):
if msg:
await send_msg(msg, bot, event)
raise IgnoredException(f"{event.user_id}正在调用{plugin_name}....")
else:
plugins2block_manager.set_true(block_type_, plugin_name)
# Count
if (
plugins2count_manager.check_plugin_count_status(plugin_name)
and event.user_id not in bot.config.superusers
):
plugin_count_data = plugins2count_manager.get_plugin_count_data(plugin_name)
limit_type = plugin_count_data.limit_type
msg = plugin_count_data.rst
count_type_ = event.user_id
if limit_type == "group" and isinstance(event, GroupMessageEvent):
count_type_ = event.group_id
if not plugins2count_manager.check(plugin_name, count_type_):
if msg:
await send_msg(msg, bot, event)
raise IgnoredException(f"{plugin_name} count次数限制...")
else:
plugins2count_manager.increase(plugin_name, count_type_)
async def auth_plugin(self, plugin_name: str, matcher: Matcher, bot: Bot, event: Event):
"""
说明:
插件状态
参数:
:param plugin_name: 模块名
:param matcher: matcher
:param bot: bot
:param event: event
"""
if plugin_name in plugins2settings_manager.keys() and matcher.priority not in [
1,
999,
]:
# 戳一戳单独判断
if (
isinstance(event, GroupMessageEvent)
or isinstance(event, PokeNotifyEvent)
or matcher.plugin_name in other_limit_plugins
):
if status_message_manager.get(event.group_id) is None:
status_message_manager.delete(event.group_id)
if plugins2settings_manager[
plugin_name
].level > group_manager.get_group_level(event.group_id):
try:
if (
self._flmt_g.check(event.user_id)
and plugin_name not in ignore_rst_module
):
self._flmt_g.start_cd(event.user_id)
await bot.send_group_msg(
group_id=event.group_id, message="群权限不足..."
)
except ActionFailed:
pass
if event.is_tome():
status_message_manager.add(event.group_id)
set_block_limit_false(event, plugin_name)
raise IgnoredException("群权限不足")
# 插件状态
if not group_manager.get_plugin_status(plugin_name, event.group_id):
try:
if plugin_name not in ignore_rst_module and self._flmt_s.check(
event.group_id
):
self._flmt_s.start_cd(event.group_id)
await bot.send_group_msg(
group_id=event.group_id, message="该群未开启此功能.."
)
except ActionFailed:
pass
if event.is_tome():
status_message_manager.add(event.group_id)
set_block_limit_false(event, plugin_name)
raise IgnoredException("未开启此功能...")
# 管理员禁用
if not group_manager.get_plugin_status(
f"{plugin_name}:super", event.group_id
):
try:
if (
self._flmt_s.check(event.group_id)
and plugin_name not in ignore_rst_module
):
self._flmt_s.start_cd(event.group_id)
await bot.send_group_msg(
group_id=event.group_id, message="管理员禁用了此群该功能..."
)
except ActionFailed:
pass
if event.is_tome():
status_message_manager.add(event.group_id)
set_block_limit_false(event, plugin_name)
raise IgnoredException("管理员禁用了此群该功能...")
# 群聊禁用
if not plugins_manager.get_plugin_status(
plugin_name, block_type="group"
):
try:
if (
self._flmt_c.check(event.group_id)
and plugin_name not in ignore_rst_module
):
self._flmt_c.start_cd(event.group_id)
await bot.send_group_msg(
group_id=event.group_id, message="该功能在群聊中已被禁用..."
)
except ActionFailed:
pass
if event.is_tome():
status_message_manager.add(event.group_id)
set_block_limit_false(event, plugin_name)
raise IgnoredException("该插件在群聊中已被禁用...")
else:
# 私聊禁用
if not plugins_manager.get_plugin_status(
plugin_name, block_type="private"
):
try:
if self._flmt_c.check(event.user_id):
self._flmt_c.start_cd(event.user_id)
await bot.send_private_msg(
user_id=event.user_id, message="该功能在私聊中已被禁用..."
)
except ActionFailed:
pass
if event.is_tome():
status_message_manager.add(event.user_id)
set_block_limit_false(event, plugin_name)
raise IgnoredException("该插件在私聊中已被禁用...")
# 维护
if not plugins_manager.get_plugin_status(plugin_name, block_type="all"):
if isinstance(
event, GroupMessageEvent
) and group_manager.check_group_is_white(event.group_id):
raise ReturnException()
try:
if isinstance(event, GroupMessageEvent):
if (
self._flmt_c.check(event.group_id)
and plugin_name not in ignore_rst_module
):
self._flmt_c.start_cd(event.group_id)
await bot.send_group_msg(
group_id=event.group_id, message="此功能正在维护..."
)
else:
await bot.send_private_msg(
user_id=event.user_id, message="此功能正在维护..."
)
except ActionFailed:
pass
if event.is_tome():
id_ = (
event.group_id
if isinstance(event, GroupMessageEvent)
else event.user_id
)
status_message_manager.add(id_)
set_block_limit_false(event, plugin_name)
raise IgnoredException("此功能正在维护...")
async def auth_admin(self, plugin_name: str, matcher: Matcher, bot: Bot, event: Event):
"""
说明:
管理员命令 个人权限
参数:
:param plugin_name: 模块名
:param matcher: matcher
:param bot: bot
:param event: event
"""
if plugin_name in admin_manager.keys() and matcher.priority not in [1, 999]:
if isinstance(event, GroupMessageEvent):
# 个人权限
if (
not await LevelUser.check_level(
event.user_id,
event.group_id,
admin_manager.get_plugin_level(plugin_name),
)
and admin_manager.get_plugin_level(plugin_name) > 0
):
try:
if self._flmt.check(event.user_id):
self._flmt.start_cd(event.user_id)
await bot.send_group_msg(
group_id=event.group_id,
message=f"{at(event.user_id)}你的权限不足喔,该功能需要的权限等级:"
f"{admin_manager.get_plugin_level(plugin_name)}",
)
except ActionFailed:
pass
set_block_limit_false(event, plugin_name)
if event.is_tome():
status_message_manager.add(event.group_id)
raise IgnoredException("权限不足")
else:
if not await LevelUser.check_level(
event.user_id, 0, admin_manager.get_plugin_level(plugin_name)
):
try:
await bot.send_private_msg(
user_id=event.user_id,
message=f"你的权限不足喔,该功能需要的权限等级:{admin_manager.get_plugin_level(plugin_name)}",
)
except ActionFailed:
pass
set_block_limit_false(event, plugin_name)
if event.is_tome():
status_message_manager.add(event.user_id)
raise IgnoredException("权限不足")
def auth_group(self, plugin_name: str, bot: Bot, event: Event):
"""
说明:
群黑名单检测 群总开关检测
参数:
:param plugin_name: 模块名
:param bot: bot
:param event: event
"""
if isinstance(event, GroupMessageEvent) or plugin_name in other_limit_plugins:
try:
if (
group_manager.get_group_level(event.group_id) < 0
and str(event.user_id) not in bot.config.superusers
):
raise IgnoredException("群黑名单")
if not group_manager.check_group_bot_status(event.group_id):
try:
if str(event.get_message()) != "醒来":
raise IgnoredException("功能总开关关闭状态")
except ValueError:
raise IgnoredException("功能总开关关闭状态")
except AttributeError:
pass
async def auth_basic(self, plugin_name: str, bot: Bot, event: Event):
"""
说明:
检测是否满足超级用户权限,是否被ban等
参数:
:param plugin_name: 模块名
:param bot: bot
:param event: event
"""
ban = await BanUser.is_ban(event.user_id)
try:
if (
(
not isinstance(event, MessageEvent)
and plugin_name not in other_limit_plugins
)
or await BanUser.is_ban(event.user_id)
and str(event.user_id) not in bot.config.superusers
) or (
str(event.user_id) in bot.config.superusers
and plugins2settings_manager.get_plugin_data(plugin_name)
and not plugins2settings_manager.get_plugin_data(
plugin_name
).limit_superuser
):
raise ReturnException()
_plugin = nonebot.plugin.get_plugin(plugin_name)
_module = _plugin.module
_plugin_name = _module.__getattribute__("__zx_plugin_name__")
if (
"[superuser]" in _plugin_name.lower()
and str(event.user_id) in bot.config.superusers
):
raise ReturnException()
except AttributeError:
pass
async def auth_cost(self, plugin_name: str, bot: Bot, event: Event) -> int:
"""
说明:
检测是否满足金币条件
参数:
:param plugin_name: 模块名
:param bot: bot
:param event: event
"""
cost_gold = 0
if isinstance(event, GroupMessageEvent) and (
psm := plugins2settings_manager.get_plugin_data(plugin_name)
):
if psm.cost_gold > 0:
if (
await BagUser.get_gold(event.user_id, event.group_id)
< psm.cost_gold
):
await send_msg(f"金币不足..该功能需要{psm.cost_gold}金币..", bot, event)
raise IgnoredException(f"{plugin_name} 金币限制...")
# 当插件不阻塞超级用户时,超级用户提前扣除金币
if (
str(event.user_id) in bot.config.superusers
and not psm.limit_superuser
):
await BagUser.spend_gold(
event.user_id, event.group_id, psm.cost_gold
)
await UserShopGoldLog.add_shop_log(
event.user_id, event.group_id, 2, plugin_name, psm.cost_gold, 1
)
cost_gold = psm.cost_gold
return cost_gold
+16 -373
View File
@@ -1,393 +1,36 @@
from nonebot.matcher import Matcher
from nonebot.message import run_preprocessor, run_postprocessor, IgnoredException
from models.friend_user import FriendUser
from models.group_member_info import GroupInfoUser
from models.bag_user import BagUser
from utils.manager import (
plugins2settings_manager,
admin_manager,
group_manager,
plugins_manager,
plugins2cd_manager,
plugins2block_manager,
plugins2count_manager,
)
from ._utils import (
set_block_limit_false,
status_message_manager,
ignore_rst_module,
other_limit_plugins,
)
from nonebot.typing import T_State
from typing import Optional
from nonebot.adapters.onebot.v11 import (
Bot,
ActionFailed,
MessageEvent,
GroupMessageEvent,
PokeNotifyEvent,
PrivateMessageEvent,
Message,
Event,
)
from configs.config import Config
from models.ban_user import BanUser
from utils.utils import FreqLimiter
from utils.message_builder import at
from models.level_user import LevelUser
import nonebot
from nonebot.matcher import Matcher
from nonebot.message import run_preprocessor, run_postprocessor
from nonebot.typing import T_State
_flmt = FreqLimiter(Config.get_config("hook", "CHECK_NOTICE_INFO_CD"))
_flmt_g = FreqLimiter(Config.get_config("hook", "CHECK_NOTICE_INFO_CD"))
_flmt_s = FreqLimiter(Config.get_config("hook", "CHECK_NOTICE_INFO_CD"))
_flmt_c = FreqLimiter(Config.get_config("hook", "CHECK_NOTICE_INFO_CD"))
from ._utils import (
set_block_limit_false,
AuthChecker,
)
# 权限检测
# # 权限检测
@run_preprocessor
async def _(matcher: Matcher, bot: Bot, event: Event, state: T_State):
module = matcher.plugin_name
plugins2info_dict = plugins2settings_manager.get_data()
# 功能的金币检测 #######################################
# 功能的金币检测 #######################################
# 功能的金币检测 #######################################
cost_gold = 0
if isinstance(
event, GroupMessageEvent
) and plugins2settings_manager.get_plugin_data(module).get("cost_gold"):
cost_gold = plugins2settings_manager.get_plugin_data(module).get("cost_gold")
if await BagUser.get_gold(event.user_id, event.group_id) < cost_gold:
await send_msg(f"金币不足..该功能需要{cost_gold}金币..", bot, event)
raise IgnoredException(f"{module} 金币限制...")
# 当插件不阻塞超级用户时,超级用户提前扣除金币
if (
str(event.user_id) in bot.config.superusers
and not plugins2info_dict[module]["limit_superuser"]
):
await BagUser.spend_gold(event.user_id, event.group_id, cost_gold)
try:
if (
(not isinstance(event, MessageEvent) and module not in other_limit_plugins)
or await BanUser.is_ban(event.user_id)
and str(event.user_id) not in bot.config.superusers
) or (
str(event.user_id) in bot.config.superusers
and plugins2info_dict.get(module)
and not plugins2info_dict[module]["limit_superuser"]
):
return
except AttributeError:
pass
# 超级用户命令
try:
_plugin = nonebot.plugin.get_plugin(module)
_module = _plugin.module
plugin_name = _module.__getattribute__("__zx_plugin_name__")
if (
"[superuser]" in plugin_name.lower()
and str(event.user_id) in bot.config.superusers
):
return
except AttributeError:
pass
# 群黑名单检测 群总开关检测
if (
isinstance(event, GroupMessageEvent)
or matcher.plugin_name in other_limit_plugins
):
try:
if (
group_manager.get_group_level(event.group_id) < 0
and str(event.user_id) not in bot.config.superusers
):
raise IgnoredException("群黑名单")
if not group_manager.check_group_bot_status(event.group_id):
try:
if str(event.get_message()) != "醒来":
raise IgnoredException("功能总开关关闭状态")
except ValueError:
raise IgnoredException("功能总开关关闭状态")
except AttributeError:
pass
if module in admin_manager.keys() and matcher.priority not in [1, 999]:
if isinstance(event, GroupMessageEvent):
# 个人权限
if (
not await LevelUser.check_level(
event.user_id,
event.group_id,
admin_manager.get_plugin_level(module),
)
and admin_manager.get_plugin_level(module) > 0
):
try:
if _flmt.check(event.user_id):
_flmt.start_cd(event.user_id)
await bot.send_group_msg(
group_id=event.group_id,
message=f"{at(event.user_id)}你的权限不足喔,该功能需要的权限等级:"
f"{admin_manager.get_plugin_level(module)}",
)
except ActionFailed:
pass
set_block_limit_false(event, module)
if event.is_tome():
status_message_manager.add(event.group_id)
raise IgnoredException("权限不足")
else:
if not await LevelUser.check_level(
event.user_id, 0, admin_manager.get_plugin_level(module)
):
try:
await bot.send_private_msg(
user_id=event.user_id,
message=f"你的权限不足喔,该功能需要的权限等级:{admin_manager.get_plugin_level(module)}",
)
except ActionFailed:
pass
set_block_limit_false(event, module)
if event.is_tome():
status_message_manager.add(event.user_id)
raise IgnoredException("权限不足")
if module in plugins2info_dict.keys() and matcher.priority not in [1, 999]:
# 戳一戳单独判断
if (
isinstance(event, GroupMessageEvent)
or isinstance(event, PokeNotifyEvent)
or matcher.plugin_name in other_limit_plugins
):
if status_message_manager.get(event.group_id) is None:
status_message_manager.delete(event.group_id)
if plugins2info_dict[module]["level"] > group_manager.get_group_level(
event.group_id
):
try:
if _flmt_g.check(event.user_id) and module not in ignore_rst_module:
_flmt_g.start_cd(event.user_id)
await bot.send_group_msg(
group_id=event.group_id, message="群权限不足..."
)
except ActionFailed:
pass
if event.is_tome():
status_message_manager.add(event.group_id)
set_block_limit_false(event, module)
raise IgnoredException("群权限不足")
# 插件状态
if not group_manager.get_plugin_status(module, event.group_id):
try:
if module not in ignore_rst_module and _flmt_s.check(
event.group_id
):
_flmt_s.start_cd(event.group_id)
await bot.send_group_msg(
group_id=event.group_id, message="该群未开启此功能.."
)
except ActionFailed:
pass
if event.is_tome():
status_message_manager.add(event.group_id)
set_block_limit_false(event, module)
raise IgnoredException("未开启此功能...")
# 管理员禁用
if not group_manager.get_plugin_status(f"{module}:super", event.group_id):
try:
if (
_flmt_s.check(event.group_id)
and module not in ignore_rst_module
):
_flmt_s.start_cd(event.group_id)
await bot.send_group_msg(
group_id=event.group_id, message="管理员禁用了此群该功能..."
)
except ActionFailed:
pass
if event.is_tome():
status_message_manager.add(event.group_id)
set_block_limit_false(event, module)
raise IgnoredException("管理员禁用了此群该功能...")
# 群聊禁用
if not plugins_manager.get_plugin_status(module, block_type="group"):
try:
if (
_flmt_c.check(event.group_id)
and module not in ignore_rst_module
):
_flmt_c.start_cd(event.group_id)
await bot.send_group_msg(
group_id=event.group_id, message="该功能在群聊中已被禁用..."
)
except ActionFailed:
pass
if event.is_tome():
status_message_manager.add(event.group_id)
set_block_limit_false(event, module)
raise IgnoredException("该插件在群聊中已被禁用...")
else:
# 私聊禁用
if not plugins_manager.get_plugin_status(module, block_type="private"):
try:
if _flmt_c.check(event.user_id):
_flmt_c.start_cd(event.user_id)
await bot.send_private_msg(
user_id=event.user_id, message="该功能在私聊中已被禁用..."
)
except ActionFailed:
pass
if event.is_tome():
status_message_manager.add(event.user_id)
set_block_limit_false(event, module)
raise IgnoredException("该插件在私聊中已被禁用...")
# 维护
if not plugins_manager.get_plugin_status(module, block_type="all"):
if isinstance(
event, GroupMessageEvent
) and group_manager.check_group_is_white(event.group_id):
return
try:
if isinstance(event, GroupMessageEvent):
if (
_flmt_c.check(event.group_id)
and module not in ignore_rst_module
):
_flmt_c.start_cd(event.group_id)
await bot.send_group_msg(
group_id=event.group_id, message="此功能正在维护..."
)
else:
await bot.send_private_msg(
user_id=event.user_id, message="此功能正在维护..."
)
except ActionFailed:
pass
if event.is_tome():
id_ = (
event.group_id
if isinstance(event, GroupMessageEvent)
else event.user_id
)
status_message_manager.add(id_)
set_block_limit_false(event, module)
raise IgnoredException("此功能正在维护...")
# 以下为限制检测 #######################################################
# 以下为限制检测 #######################################################
# 以下为限制检测 #######################################################
# 以下为限制检测 #######################################################
# 以下为限制检测 #######################################################
# 以下为限制检测 #######################################################
# Cd
if plugins2cd_manager.check_plugin_cd_status(module):
plugin_cd_data = plugins2cd_manager.get_plugin_cd_data(module)
check_type = plugin_cd_data["check_type"]
limit_type = plugin_cd_data["limit_type"]
rst = plugin_cd_data["rst"]
if (
(isinstance(event, PrivateMessageEvent) and check_type == "private")
or (isinstance(event, GroupMessageEvent) and check_type == "group")
or plugins2cd_manager.get_plugin_data(module).get("check_type") == "all"
):
cd_type_ = event.user_id
if limit_type == "group" and isinstance(event, GroupMessageEvent):
cd_type_ = event.group_id
if not plugins2cd_manager.check(module, cd_type_):
if rst:
rst = await init_rst(rst, event)
await send_msg(rst, bot, event)
raise IgnoredException(f"{module} 正在cd中...")
else:
plugins2cd_manager.start_cd(module, cd_type_)
# Block
if plugins2block_manager.check_plugin_block_status(module):
plugin_block_data = plugins2block_manager.get_plugin_block_data(module)
check_type = plugin_block_data["check_type"]
limit_type = plugin_block_data["limit_type"]
rst = plugin_block_data["rst"]
if (
(isinstance(event, PrivateMessageEvent) and check_type == "private")
or (isinstance(event, GroupMessageEvent) and check_type == "group")
or check_type == "all"
):
block_type_ = event.user_id
if limit_type == "group" and isinstance(event, GroupMessageEvent):
block_type_ = event.group_id
if plugins2block_manager.check(block_type_, module):
if rst:
rst = await init_rst(rst, event)
await send_msg(rst, bot, event)
raise IgnoredException(f"{event.user_id}正在调用{module}....")
else:
plugins2block_manager.set_true(block_type_, module)
# Count
if (
plugins2count_manager.check_plugin_count_status(module)
and event.user_id not in bot.config.superusers
):
plugin_count_data = plugins2count_manager.get_plugin_count_data(module)
limit_type = plugin_count_data["limit_type"]
rst = plugin_count_data["rst"]
count_type_ = event.user_id
if limit_type == "group" and isinstance(event, GroupMessageEvent):
count_type_ = event.group_id
if not plugins2count_manager.check(module, count_type_):
if rst:
rst = await init_rst(rst, event)
await send_msg(rst, bot, event)
raise IgnoredException(f"{module} count次数限制...")
else:
plugins2count_manager.increase(module, count_type_)
# 功能花费的金币 #######################################
# 功能花费的金币 #######################################
if cost_gold:
await BagUser.spend_gold(event.user_id, event.group_id, cost_gold)
async def send_msg(rst: str, bot: Bot, event: MessageEvent):
"""
发送信息
:param rst: pass
:param bot: pass
:param event: pass
"""
rst = await init_rst(rst, event)
try:
if isinstance(event, GroupMessageEvent):
status_message_manager.add(event.group_id)
await bot.send_group_msg(group_id=event.group_id, message=Message(rst))
else:
status_message_manager.add(event.user_id)
await bot.send_private_msg(user_id=event.user_id, message=Message(rst))
except ActionFailed:
pass
async def _(matcher: Matcher, bot: Bot, event: Event):
await AuthChecker().auth(matcher, bot, event)
# 解除命令block阻塞
@run_postprocessor
async def _(
matcher: Matcher,
exception: Optional[Exception],
bot: Bot,
event: Event,
state: T_State,
matcher: Matcher,
exception: Optional[Exception],
bot: Bot,
event: Event,
state: T_State,
):
if not isinstance(event, MessageEvent) and matcher.plugin_name != "poke":
return
module = matcher.plugin_name
set_block_limit_false(event, module)
async def init_rst(rst: str, event: MessageEvent):
if "[uname]" in rst:
uname = event.sender.card or event.sender.nickname
rst = rst.replace("[uname]", uname)
if "[nickname]" in rst:
if isinstance(event, GroupMessageEvent):
nickname = await GroupInfoUser.get_group_member_nickname(
event.user_id, event.group_id
)
else:
nickname = await FriendUser.get_friend_nickname(event.user_id)
rst = rst.replace("[nickname]", nickname)
if "[at]" in rst and isinstance(event, GroupMessageEvent):
rst = rst.replace("[at]", str(at(event.user_id)))
return rst
-8
View File
@@ -2,15 +2,12 @@ from nonebot.matcher import Matcher
from nonebot.message import run_preprocessor, IgnoredException
from nonebot.typing import T_State
from ._utils import status_message_manager
from utils.image_utils import text2image
from typing import Dict, Any
from nonebot.adapters.onebot.v11 import (
Bot,
MessageEvent,
PrivateMessageEvent,
GroupMessageEvent,
)
import re
# 为什么AI会自己和自己聊天
@@ -41,11 +38,6 @@ async def _(matcher: Matcher, bot: Bot, event: MessageEvent, state: T_State):
status_message_manager.delete(event.user_id)
raise IgnoredException("有命令就别说话了")
# @Bot.on_calling_api
# async def handle_api_call(bot: Bot, api: str, data: Dict[str, Any]):
# if api in ["send_msg", "send_group_msg", "send_private_msg"]:
# msg = str(data["message"])
# if (r := re.search("\[\[To_Img\|?(.*?)]]", msg)) or (r := re.search("&#91;&#91;To_Img\|?(.*?)&#91;&#91;")):
+4 -2
View File
@@ -6,7 +6,7 @@ import re
@Bot.on_calling_api
async def handle_api_call(bot: Bot, api: str, data: Dict[str, Any]):
async def _(bot: Bot, api: str, data: Dict[str, Any]):
r = None
if (
(
@@ -33,11 +33,13 @@ async def handle_api_call(bot: Bot, api: str, data: Dict[str, Any]):
)
and r.group(1) in group_manager.get_task_data().keys()
):
# if bot.self_id in bot.config.superusers:
# raise MockApiException(f"禁止社死...")
task = r.group(1)
group_id = data["group_id"]
if group_manager.get_group_level(
group_id
) < 0 or not await group_manager.check_group_task_status(group_id, task):
) < 0 or not group_manager.check_group_task_status(group_id, task):
raise MockApiException(f"被动技能 {task} 处于关闭状态...")
else:
msg = str(data["message"]).strip()
+14 -17
View File
@@ -1,3 +1,4 @@
from configs.path_config import DATA_PATH
from .init_group_manager import init_group_manager, group_manager
from .init_plugins_config import init_plugins_config
from .init_plugins_data import init_plugins_data, plugins_manager
@@ -12,9 +13,7 @@ from .init_plugins_limit import (
from .init import init
from .check_plugin_status import check_plugin_status
from nonebot.adapters.onebot.v11 import Bot
from configs.path_config import DATA_PATH
from services.log import logger
from pathlib import Path
from nonebot import Driver
import nonebot
@@ -28,27 +27,25 @@ driver: Driver = nonebot.get_driver()
@driver.on_startup
def _():
async def _():
"""
初始化数据
"""
_flag = False
config_file = DATA_PATH / "configs" / "plugins2config.yaml"
if not config_file.exists():
_flag = True
_flag = not config_file.exists()
init()
init_plugins_settings(DATA_PATH)
init_plugins_cd_limit(DATA_PATH)
init_plugins_block_limit(DATA_PATH)
init_plugins_count_limit(DATA_PATH)
init_plugins_data(DATA_PATH)
init_plugins_config(DATA_PATH)
init_plugins_settings()
init_plugins_cd_limit()
init_plugins_block_limit()
init_plugins_count_limit()
init_plugins_data()
init_plugins_config()
init_plugins_resources()
init_none_plugin_count_manager()
x = group_manager.get_super_old_data()
if x:
for key in x.keys():
plugins_manager.block_plugin(key, block_type=x[key])
# x = group_manager.get_super_old_data()
# if x:
# for key in x.keys():
# plugins_manager.block_plugin(key, block_type=x[key])
if _flag:
raise Exception("首次运行,已在configs目录下生成配置文件config.yaml,修改后重启即可...")
logger.info("初始化数据完成...")
@@ -56,5 +53,5 @@ def _():
@driver.on_bot_connect
async def _(bot: Bot):
await init_group_manager()
# await init_group_manager()
await check_plugin_status(bot)
@@ -6,13 +6,13 @@ async def check_plugin_status(bot: Bot):
"""
遍历查看插件加载情况
"""
rst = ""
msg = ""
for plugin in plugins_manager.keys():
data = plugins_manager.get(plugin)
if data.get("error") or data.get("error") is None:
rst += f'{plugin}:{data["plugin_name"]}\n'
if rst:
rst = "以下插件加载失败..\n" + rst
if not data.error:
msg += f'{plugin}:{data.plugin_name}\n'
if msg and bot.config.superusers:
msg = "以下插件加载失败..\n" + msg
await bot.send_private_msg(
user_id=int(list(bot.config.superusers)[0]), message=rst[:-1]
user_id=int(list(bot.config.superusers)[0]), message=msg.strip()
)
@@ -5,7 +5,6 @@ from utils.manager import (
plugins2settings_manager,
plugins2block_manager,
plugins_manager,
resources_manager
)
from services.log import logger
from utils.utils import get_matchers
@@ -20,36 +19,44 @@ def init_none_plugin_count_manager():
"""
清除已删除插件数据
"""
modules = [x.plugin_name for x in get_matchers()]
modules = [x.plugin_name for x in get_matchers(True)]
plugins_manager_list = list(plugins_manager.keys())
for module in plugins_manager_list:
if module not in modules or none_plugin_count_manager.check(module):
try:
plugin_name = plugins_manager.get(module)["plugin_name"]
except (AttributeError, KeyError):
plugin_name = ""
if none_plugin_count_manager.check(module):
try:
if module not in modules or none_plugin_count_manager.check(module):
try:
plugins2settings_manager.delete(module)
plugins2settings_manager.save()
plugins2count_manager.delete(module)
plugins2count_manager.save()
plugins2cd_manager.delete(module)
plugins2cd_manager.save()
plugins2block_manager.delete(module)
plugins2block_manager.save()
plugins_manager.delete(module)
plugins_manager.save()
# resources_manager.remove_resource(module)
none_plugin_count_manager.delete(module)
logger.info(f"{module}:{plugin_name} 插件疑似已删除,清除对应插件数据...")
except Exception as e:
logger.exception(
f"{module}:{plugin_name} 插件疑似已删除,清除对应插件数据失败...{type(e)}:{e}")
plugin_name = plugins_manager.get(module).plugin_name
except (AttributeError, KeyError):
plugin_name = ""
if none_plugin_count_manager.check(module):
try:
plugins2settings_manager.delete(module)
plugins2count_manager.delete(module)
plugins2cd_manager.delete(module)
plugins2block_manager.delete(module)
plugins_manager.delete(module)
plugins_manager.save()
# resources_manager.remove_resource(module)
none_plugin_count_manager.delete(module)
logger.info(f"{module}:{plugin_name} 插件疑似已删除,清除对应插件数据...")
except Exception as e:
logger.exception(
f"{module}:{plugin_name} 插件疑似已删除,清除对应插件数据失败...{type(e)}:{e}"
)
else:
none_plugin_count_manager.add_count(module)
logger.info(
f"{module}:{plugin_name} 插件疑似已删除,"
f"加载{none_plugin_count_manager._max_count}次失败后将清除对应插件数据,"
f"当前次数:{none_plugin_count_manager._data[module]}"
)
else:
none_plugin_count_manager.add_count(module)
logger.info(
f"{module}:{plugin_name} 插件疑似已删除,加载{none_plugin_count_manager._max_count}次失败后将清除对应插件数据,当前次数:{none_plugin_count_manager._data[module]}")
else:
none_plugin_count_manager.reset(module)
none_plugin_count_manager.reset(module)
except Exception as e:
logger.error(f"清除插件数据错误 {type(e)}:{e}")
plugins2settings_manager.save()
plugins2count_manager.save()
plugins2cd_manager.save()
plugins2block_manager.save()
plugins_manager.save()
none_plugin_count_manager.save()
@@ -1,3 +1,5 @@
import asyncio
from datetime import datetime, timedelta
from pathlib import Path
from ruamel.yaml import round_trip_load, round_trip_dump, YAML
from utils.manager import admin_manager, plugins_manager
@@ -5,45 +7,53 @@ from configs.config import Config
from services.log import logger
from utils.text_utils import prompt2cn
from utils.utils import get_matchers
from utils.utils import scheduler
from configs.path_config import DATA_PATH
from ruamel import yaml
import nonebot
_yaml = YAML(typ="safe")
def init_plugins_config(data_path):
def init_plugins_config():
"""
初始化插件数据配置
"""
plugins2config_file = data_path / "configs" / "plugins2config.yaml"
plugins2config_file = DATA_PATH / "configs" / "plugins2config.yaml"
plugins2config_file.parent.mkdir(parents=True, exist_ok=True)
_data = {}
if plugins2config_file.exists():
_data = _yaml.load(open(plugins2config_file, "r", encoding="utf8"))
_matchers = get_matchers()
for matcher in _matchers:
_plugin = nonebot.plugin.get_plugin(matcher.plugin_name)
try:
_module = _plugin.module
except AttributeError:
continue
try:
# 优先使用 metadata 数据
for matcher in [matcher for matcher in get_matchers(True) if matcher.plugin and matcher.plugin.module]:
_plugin = matcher.plugin
metadata = _plugin.metadata
_module = _plugin.module
plugin_version = None
if metadata:
plugin_version = metadata.extra.get("version")
if not plugin_version and hasattr(_module, "__plugin_version__"):
plugin_version = _module.__getattribute__("__plugin_version__")
except AttributeError:
plugin_version = None
try:
plugin_configs = _module.__getattribute__("__plugin_configs__")
except AttributeError:
continue
# 插件配置版本更新或为Version为None或不在存储配置内
if (
if metadata and metadata.config:
plugin_configs = {}
for key, value in metadata.config.__fields__.items():
plugin_configs[key.upper()] = {
"value": value.default,
"default_value": value.default
}
else:
try:
plugin_configs = _module.__getattribute__("__plugin_configs__")
except AttributeError:
continue
# 插件配置版本更新或为Version为None或不在存储配置内,当使用metadata时,必定更新
if isinstance(plugin_version, str) or (
plugin_version is None
or (
_data.get(matcher.plugin_name)
and _data[matcher.plugin_name].keys() != plugin_configs.keys()
)
or plugin_version > plugins_manager.get(matcher.plugin_name)["version"]
or plugin_version > plugins_manager.get(matcher.plugin_name).version
or matcher.plugin_name not in _data.keys()
):
for key in plugin_configs:
@@ -76,93 +86,111 @@ def init_plugins_config(data_path):
_data = round_trip_load(open(plugins2config_file, encoding="utf8"))
for plugin in _data.keys():
try:
plugin_name = plugins_manager.get(plugin)["plugin_name"]
plugin_name = plugins_manager.get(plugin).plugin_name
except (AttributeError, TypeError):
plugin_name = plugin
_data[plugin].yaml_set_start_comment(plugin_name, indent=2)
# 初始化未设置的管理员权限等级
for k, v in Config.get_admin_level_data():
try:
admin_manager.set_admin_level(k, v)
except KeyError as e:
raise KeyError(f"{e} ****** 请检查是否有插件加载失败 ******")
# try:
admin_manager.set_admin_level(k, v)
# except KeyError as e:
# raise KeyError(f"{e} ****** 请检查是否有插件加载失败 ******")
# 存完插件基本设置
with open(plugins2config_file, "w", encoding="utf8") as wf:
round_trip_dump(
_data, wf, indent=2, Dumper=yaml.RoundTripDumper, allow_unicode=True
)
# 再开始读取用户配置
user_config_file = Path() / "configs" / "config.yaml"
_data = {}
_tmp_data = {}
if user_config_file.exists():
with open(user_config_file, "r", encoding="utf8") as f:
_data = _yaml.load(f)
# 数据替换
for plugin in Config.keys():
_tmp_data[plugin] = {}
for k in Config[plugin].keys():
try:
if _data.get(plugin) and k in _data[plugin].keys():
Config.set_config(plugin, k, _data[plugin][k])
if level2module := Config.get_level2module(plugin, k):
try:
admin_manager.set_admin_level(
level2module, _data[plugin][k]
)
except KeyError:
logger.warning(
f"{level2module} 设置权限等级失败:{_data[plugin][k]}"
)
_tmp_data[plugin][k] = Config.get_config(plugin, k)
except AttributeError as e:
raise AttributeError(
f"{e}\n" + prompt2cn("可能为config.yaml配置文件填写不规范", 46)
)
Config.save()
temp_file = Path() / "configs" / "temp_config.yaml"
try:
with open(temp_file, "w", encoding="utf8") as wf:
yaml.dump(
_tmp_data, wf, Dumper=yaml.RoundTripDumper, allow_unicode=True
)
with open(temp_file, "r", encoding="utf8") as rf:
_data = round_trip_load(rf)
# 添加注释
for plugin in _data.keys():
rst = ""
plugin_name = None
try:
plugin_data = Config.get(plugin)
for x in list(Config.get(plugin).keys()):
user_config_file = Path() / "configs" / "config.yaml"
# if not user_config_file.exists():
_replace_config()
# else:
# logger.info('五分钟后将进行配置数据替换,请注意...')
# scheduler.add_job(
# _replace_config,
# "date",
# run_date=datetime.now() + timedelta(minutes=5),
# id=f"_replace_config"
# )
def _replace_config():
"""
说明:
定时任务加载的配置读取替换
"""
# 再开始读取用户配置
user_config_file = Path() / "configs" / "config.yaml"
_data = {}
_tmp_data = {}
if user_config_file.exists():
with open(user_config_file, "r", encoding="utf8") as f:
_data = _yaml.load(f)
# 数据替换
for plugin in Config.keys():
_tmp_data[plugin] = {}
for k in Config[plugin].keys():
try:
if _data.get(plugin) and k in _data[plugin].keys():
Config.set_config(plugin, k, _data[plugin][k])
if level2module := Config.get_level2module(plugin, k):
try:
_x = plugin_data[x].get("name")
if _x:
plugin_name = _x
except AttributeError:
pass
except (KeyError, AttributeError):
plugin_name = None
if not plugin_name:
admin_manager.set_admin_level(
level2module, _data[plugin][k]
)
except KeyError:
logger.warning(
f"{level2module} 设置权限等级失败:{_data[plugin][k]}"
)
_tmp_data[plugin][k] = Config.get_config(plugin, k)
except AttributeError as e:
raise AttributeError(
f"{e}\n" + prompt2cn("可能为config.yaml配置文件填写不规范", 46)
)
Config.save()
temp_file = Path() / "configs" / "temp_config.yaml"
try:
with open(temp_file, "w", encoding="utf8") as wf:
yaml.dump(
_tmp_data, wf, Dumper=yaml.RoundTripDumper, allow_unicode=True
)
with open(temp_file, "r", encoding="utf8") as rf:
_data = round_trip_load(rf)
# 添加注释
for plugin in _data.keys():
rst = ""
plugin_name = None
try:
plugin_data = Config.get(plugin)
for x in list(Config.get(plugin).keys()):
try:
plugin_name = plugins_manager.get(plugin)["plugin_name"]
except (AttributeError, TypeError):
plugin_name = plugin
plugin_name = (
plugin_name.replace("[Hidden]", "")
.replace("[Superuser]", "")
.replace("[Admin]", "")
.strip()
)
rst += plugin_name + "\n"
for k in _data[plugin].keys():
rst += f'{k}: {Config[plugin][k]["help"]}' + "\n"
_data[plugin].yaml_set_start_comment(rst[:-1], indent=2)
with open(Path() / "configs" / "config.yaml", "w", encoding="utf8") as wf:
round_trip_dump(
_data, wf, Dumper=yaml.RoundTripDumper, allow_unicode=True
)
except Exception as e:
logger.error(f"生成简易配置注释错误 {type(e)}:{e}")
if temp_file.exists():
temp_file.unlink()
_x = plugin_data[x].get("name")
if _x:
plugin_name = _x
except AttributeError:
pass
except (KeyError, AttributeError):
plugin_name = None
if not plugin_name:
try:
plugin_name = plugins_manager.get(plugin).plugin_name
except (AttributeError, TypeError):
plugin_name = plugin
plugin_name = (
plugin_name.replace("[Hidden]", "")
.replace("[Superuser]", "")
.replace("[Admin]", "")
.strip()
)
rst += plugin_name + "\n"
for k in _data[plugin].keys():
rst += f'{k}: {Config[plugin][k]["help"]}' + "\n"
_data[plugin].yaml_set_start_comment(rst[:-1], indent=2)
with open(Path() / "configs" / "config.yaml", "w", encoding="utf8") as wf:
round_trip_dump(
_data, wf, Dumper=yaml.RoundTripDumper, allow_unicode=True
)
except Exception as e:
logger.error(f"生成简易配置注释错误 {type(e)}:{e}")
if temp_file.exists():
temp_file.unlink()
@@ -1,8 +1,9 @@
from pathlib import Path
from ruamel.yaml import YAML
from utils.manager import plugins_manager
from utils.utils import get_matchers
import nonebot
from services.log import logger
from configs.path_config import DATA_PATH
try:
import ujson as json
@@ -13,70 +14,70 @@ except ModuleNotFoundError:
_yaml = YAML(typ="safe")
def init_plugins_data(data_path):
def init_plugins_data():
"""
初始化插件数据信息
"""
plugin2data_file = data_path / "manager" / "plugin_manager.json"
plugin2data_file = DATA_PATH / "manager" / "plugin_manager.json"
plugin2data_file.parent.mkdir(parents=True, exist_ok=True)
_data = {}
if plugin2data_file.exists():
_data = json.load(open(plugin2data_file, "r", encoding="utf8"))
_matchers = get_matchers()
for matcher in _matchers:
_plugin = nonebot.plugin.get_plugin(matcher.plugin_name)
for matcher in get_matchers(True):
_plugin = matcher.plugin
if not _plugin:
continue
metadata = _plugin.metadata
try:
_module = _plugin.module
except AttributeError:
if matcher.plugin_name not in _data.keys():
if matcher.plugin_name not in plugins_manager.keys():
plugins_manager.add_plugin_data(
matcher.plugin_name, matcher.plugin_name, error=True
)
else:
plugins_manager.set_module_data(matcher.plugin_name, "error", True)
plugin_data = plugins_manager.get(matcher.plugin_name)
if plugin_data:
plugins_manager.set_module_data(
matcher.plugin_name, "version", plugin_data.get("version")
)
plugins_manager[matcher.plugin_name].error = True
else:
try:
plugin_version = _module.__getattribute__("__plugin_version__")
except AttributeError:
plugin_version = None
try:
plugin_name = _module.__getattribute__("__zx_plugin_name__")
except AttributeError:
plugin_name = matcher.plugin_name
try:
plugin_author = _module.__getattribute__("__plugin_author__")
except AttributeError:
if metadata:
plugin_version = metadata.extra.get("version")
if not plugin_version and hasattr(_module, "__plugin_version__"):
plugin_version = _module.__getattribute__("__plugin_version__")
if metadata:
plugin_name = metadata.name
else:
try:
plugin_name = _module.__getattribute__("__zx_plugin_name__")
except AttributeError:
plugin_name = matcher.plugin_name
plugin_author = None
if matcher.plugin_name in plugins_manager.keys():
plugins_manager.set_module_data(matcher.plugin_name, "error", False)
if matcher.plugin_name not in plugins_manager.keys():
plugins_manager.add_plugin_data(
matcher.plugin_name,
plugin_name=plugin_name,
author=plugin_author,
version=plugin_version,
)
elif plugins_manager[matcher.plugin_name]["version"] is None or (
plugin_version is not None
and plugin_version > plugins_manager[matcher.plugin_name]["version"]
):
plugins_manager.set_module_data(
matcher.plugin_name, "plugin_name", plugin_name
)
plugins_manager.set_module_data(matcher.plugin_name, "author", plugin_author)
plugins_manager.set_module_data(
matcher.plugin_name, "version", plugin_version
)
if matcher.plugin_name in _data.keys():
plugins_manager.set_module_data(
matcher.plugin_name, "error", _data[matcher.plugin_name]["error"]
)
plugins_manager.set_module_data(
matcher.plugin_name, "plugin_name", _data[matcher.plugin_name]["plugin_name"]
)
if metadata:
plugin_author = metadata.extra.get('author')
if not plugin_author and hasattr(_module, "__plugin_author__"):
plugin_author = _module.__getattribute__("__plugin_author__")
if matcher.plugin_name in plugins_manager.keys():
plugins_manager[matcher.plugin_name].error = False
if matcher.plugin_name not in plugins_manager.keys():
plugins_manager.add_plugin_data(
matcher.plugin_name,
plugin_name=plugin_name,
author=plugin_author,
version=plugin_version,
)
# metadata不检测version
elif isinstance(plugin_version, str) or plugins_manager[matcher.plugin_name].version is None or (
plugin_version is not None
and plugin_version > float(plugins_manager[matcher.plugin_name].version)
):
plugins_manager[matcher.plugin_name].plugin_name = plugin_name
plugins_manager[matcher.plugin_name].author = plugin_author
plugins_manager[matcher.plugin_name].version = plugin_version
# if matcher.plugin_name in _data.keys():
# plugins_manager[matcher.plugin_name].error = _data[matcher.plugin_name]["error"]
# plugins_manager.set_module_data(
# matcher.plugin_name, "error", _data[matcher.plugin_name]["error"]
# )
# plugins_manager.set_module_data(
# matcher.plugin_name, "plugin_name", _data[matcher.plugin_name]["plugin_name"]
# )
except Exception as e:
logger.error(f"插件数据 {matcher.plugin_name} 加载发生错误 {type(e)}:{e}")
plugins_manager.save()
@@ -1,27 +1,21 @@
from pathlib import Path
from ruamel.yaml import round_trip_load, round_trip_dump, YAML
from utils.manager import (
plugins2cd_manager,
plugins2block_manager,
plugins2count_manager,
)
from utils.utils import get_matchers
from ruamel import yaml
from configs.path_config import DATA_PATH
import nonebot
_yaml = YAML(typ="safe")
def init_plugins_cd_limit(data_path):
def init_plugins_cd_limit():
"""
加载 cd 限制
"""
plugins2cd_file = data_path / "configs" / "plugins2cd.yaml"
plugins2cd_file = DATA_PATH / "configs" / "plugins2cd.yaml"
plugins2cd_file.parent.mkdir(exist_ok=True, parents=True)
_data = {}
_matchers = get_matchers()
for matcher in _matchers:
for matcher in get_matchers(True):
if not plugins2cd_manager.get_plugin_cd_data(matcher.plugin_name):
_plugin = nonebot.plugin.get_plugin(matcher.plugin_name)
try:
@@ -36,42 +30,19 @@ def init_plugins_cd_limit(data_path):
plugins2cd_manager.add_cd_limit(
"这是一个示例"
)
_tmp_data = {"PluginCdLimit": plugins2cd_manager.get_data()}
with open(plugins2cd_file, "w", encoding="utf8") as wf:
yaml.dump(_tmp_data, wf, Dumper=yaml.RoundTripDumper, allow_unicode=True)
_data = round_trip_load(open(plugins2cd_file, encoding="utf8"))
_data["PluginCdLimit"].yaml_set_start_comment(
"""# 需要cd的功能
# 自定义的功能需要cd也可以在此配置
# key:模块名称
# cd:cd 时长(秒)
# status:此限制的开关状态
# check_type:'private'/'group'/'all',限制私聊/群聊/全部
# limit_type:监听对象,以user_id或group_id作为键来限制,'user':用户id,'group':群id
# 示例:'user':用户N秒内触发1次,'group':群N秒内触发1次
# rst:回复的话,可以添加[at],[uname],[nickname]来对应艾特,用户群名称,昵称系统昵称
# rst 为 "" 或 None 时则不回复
# rst示例:"[uname]你冲的太快了,[nickname]先生,请稍后再冲[at]"
# rst回复:"老色批你冲的太快了,欧尼酱先生,请稍后再冲@老色批"
# 用户昵称↑ 昵称系统的昵称↑ 艾特用户↑""",
indent=2,
)
with open(plugins2cd_file, "w", encoding="utf8") as wf:
round_trip_dump(_data, wf, Dumper=yaml.RoundTripDumper, allow_unicode=True)
plugins2cd_manager.save()
plugins2cd_manager.reload_cd_limit()
def init_plugins_block_limit(data_path):
def init_plugins_block_limit():
"""
加载阻塞限制
"""
plugins2block_file = data_path / "configs" / "plugins2block.yaml"
plugins2block_file = DATA_PATH / "configs" / "plugins2block.yaml"
plugins2block_file.parent.mkdir(exist_ok=True, parents=True)
_data = {}
_matchers = get_matchers()
for matcher in _matchers:
for matcher in get_matchers(True):
if not plugins2block_manager.get_plugin_block_data(matcher.plugin_name):
_plugin = nonebot.plugin.get_plugin(matcher.plugin_name)
_plugin = matcher.plugin
try:
_module = _plugin.module
plugin_block_limit = _module.__getattribute__("__plugin_block_limit__")
@@ -84,36 +55,15 @@ def init_plugins_block_limit(data_path):
plugins2block_manager.add_block_limit(
"这是一个示例"
)
_tmp_data = {"PluginBlockLimit": plugins2block_manager.get_data()}
with open(plugins2block_file, "w", encoding="utf8") as wf:
yaml.dump(_tmp_data, wf, Dumper=yaml.RoundTripDumper, allow_unicode=True)
_data = round_trip_load(open(plugins2block_file, encoding="utf8"))
_data["PluginBlockLimit"].yaml_set_start_comment(
"""# 用户调用阻塞
# 即 当用户调用此功能还未结束时
# 用发送消息阻止用户重复调用此命令直到该命令结束
# key:模块名称
# status:此限制的开关状态
# check_type:'private'/'group'/'all',限制私聊/群聊/全部
# limit_type:监听对象,以user_id或group_id作为键来限制,'user':用户id,'group':群id
# 示例:'user':阻塞用户,'group':阻塞群聊
# rst:回复的话,可以添加[at],[uname],[nickname]来对应艾特,用户群名称,昵称系统昵称
# rst 为 "" 或 None 时则不回复
# rst示例:"[uname]你冲的太快了,[nickname]先生,请稍后再冲[at]"
# rst回复:"老色批你冲的太快了,欧尼酱先生,请稍后再冲@老色批"
# 用户昵称↑ 昵称系统的昵称↑ 艾特用户↑""",
indent=2,
)
with open(plugins2block_file, "w", encoding="utf8") as wf:
round_trip_dump(_data, wf, Dumper=yaml.RoundTripDumper, allow_unicode=True)
plugins2block_manager.save()
plugins2block_manager.reload_block_limit()
def init_plugins_count_limit(data_path):
def init_plugins_count_limit():
"""
加载次数限制
"""
plugins2count_file = data_path / "configs" / "plugins2count.yaml"
plugins2count_file = DATA_PATH / "configs" / "plugins2count.yaml"
plugins2count_file.parent.mkdir(exist_ok=True, parents=True)
_data = {}
_matchers = get_matchers()
@@ -132,26 +82,5 @@ def init_plugins_count_limit(data_path):
plugins2count_manager.add_count_limit(
"这是一个示例"
)
_tmp_data = {"PluginCountLimit": plugins2count_manager.get_data()}
with open(plugins2count_file, "w", encoding="utf8") as wf:
yaml.dump(_tmp_data, wf, Dumper=yaml.RoundTripDumper, allow_unicode=True)
_data = round_trip_load(open(plugins2count_file, encoding="utf8"))
_data["PluginCountLimit"].yaml_set_start_comment(
"""# 命令每日次数限制
# 即 用户/群聊 每日可调用命令的次数 [数据内存存储,重启将会重置]
# 每日调用直到 00:00 刷新
# key:模块名称
# max_count: 每日调用上限
# status:此限制的开关状态
# limit_type:监听对象,以user_id或group_id作为键来限制,'user':用户id,'group':群id
# 示例:'user':用户上限,'group':群聊上限
# rst:回复的话,可以添加[at],[uname],[nickname]来对应艾特,用户群名称,昵称系统昵称
# rst 为 "" 或 None 时则不回复
# rst示例:"[uname]你冲的太快了,[nickname]先生,请稍后再冲[at]"
# rst回复:"老色批你冲的太快了,欧尼酱先生,请稍后再冲@老色批"
# 用户昵称↑ 昵称系统的昵称↑ 艾特用户↑""",
indent=2,
)
with open(plugins2count_file, "w", encoding="utf8") as wf:
round_trip_dump(_data, wf, Dumper=yaml.RoundTripDumper, allow_unicode=True)
plugins2count_manager.save()
plugins2count_manager.reload_count_limit()
@@ -26,18 +26,8 @@ def init_plugins_resources():
else:
path = Path(_module.__getattribute__("__file__")).parent
for resource in resources.keys():
resources_manager.add_resource(matcher.plugin_name, path / resource, resources[resource])
resources_manager.add_resource(
matcher.plugin_name, path / resource, resources[resource]
)
resources_manager.save()
resources_manager.start_move()
@@ -1,78 +1,74 @@
from pathlib import Path
from ruamel.yaml import round_trip_load, round_trip_dump, YAML
from utils.manager import plugins2settings_manager, admin_manager
from services.log import logger
from utils.utils import get_matchers
from ruamel import yaml
import nonebot
_yaml = YAML(typ="safe")
def init_plugins_settings(data_path: str):
def init_plugins_settings():
"""
初始化插件设置,从插件中获取 __zx_plugin_name__,__plugin_cmd__,__plugin_settings__
"""
plugins2settings_file = data_path / "configs" / "plugins2settings.yaml"
plugins2settings_file.parent.mkdir(exist_ok=True, parents=True)
_matchers = get_matchers()
_tmp_module = {}
_tmp = []
for x in plugins2settings_manager.keys():
try:
_plugin = nonebot.plugin.get_plugin(x)
_module = _plugin.module
plugin_name = _module.__getattribute__("__zx_plugin_name__")
metadata = _plugin.metadata
plugin_name = (
metadata.name
if metadata
else _module.__getattribute__("__zx_plugin_name__")
)
_tmp_module[x] = plugin_name
except (KeyError, AttributeError) as e:
logger.warning(f"配置文件 模块:{x} 获取 plugin_name 失败...{e}")
_tmp_module[x] = ""
for matcher in _matchers:
if matcher.plugin_name not in plugins2settings_manager.keys():
_plugin = nonebot.plugin.get_plugin(matcher.plugin_name)
try:
_module = _plugin.module
except AttributeError:
logger.warning(f"插件 {matcher.plugin_name} 加载失败...,插件控制未加载.")
else:
for matcher in [x for x in get_matchers(True) if x.plugin]:
try:
if matcher.plugin_name not in plugins2settings_manager.keys():
_plugin = matcher.plugin
metadata = _plugin.metadata
try:
plugin_name = _module.__getattribute__("__zx_plugin_name__")
if "[admin]" in plugin_name.lower():
try:
admin_settings = _module.__getattribute__(
"__plugin_settings__"
)
level = admin_settings["admin_level"]
cmd = admin_settings.get("cmd")
except (AttributeError, KeyError):
_module = _plugin.module
except AttributeError:
logger.warning(f"插件 {matcher.plugin_name} 加载失败...,插件控制未加载.")
else:
try:
plugin_name = metadata.name if metadata else _module.__getattribute__("__zx_plugin_name__")
# 管理员命令
if "[admin]" in plugin_name.lower():
level = 5
cmd = None
if level is None:
level = 5
admin_manager.add_admin_plugin_settings(
matcher.plugin_name, cmd, level
)
if (
"[hidden]" in plugin_name.lower()
or "[admin]" in plugin_name.lower()
or "[superuser]" in plugin_name.lower()
or matcher.plugin_name in plugins2settings_manager.keys()
):
continue
except AttributeError:
if matcher.plugin_name not in _tmp:
if hasattr(_module, "__plugin_settings__"):
admin_settings = _module.__getattribute__(
"__plugin_settings__"
)
level = admin_settings.get("admin_level", 5)
cmd = admin_settings.get("cmd")
admin_manager.add_admin_plugin_settings(
matcher.plugin_name, cmd, level
)
if (
"[hidden]" in plugin_name.lower()
or "[admin]" in plugin_name.lower()
or "[superuser]" in plugin_name.lower()
or matcher.plugin_name in plugins2settings_manager.keys()
):
continue
except AttributeError:
logger.warning(
f"获取插件 {matcher.plugin_name} __zx_plugin_name__ 失败...,插件控制未加载."
)
else:
try:
else:
_tmp_module[matcher.plugin_name] = plugin_name
plugin_settings = _module.__getattribute__(
"__plugin_settings__"
)
if plugin_settings.get('cost_gold') is None:
plugin_settings['cost_gold'] = 0
if hasattr(_module, "__plugin_settings__"):
plugin_settings = _module.__getattribute__(
"__plugin_settings__"
)
else:
plugin_settings = {"cmd": [matcher.plugin_name, plugin_name]}
if plugin_settings.get("cost_gold") is None:
plugin_settings["cost_gold"] = 0
if (
plugin_settings.get("cmd") is not None
and plugin_name not in plugin_settings["cmd"]
@@ -86,14 +82,12 @@ def init_plugins_settings(data_path: str):
plugin_type = tuple(
plugins2settings_manager.get_plugin_data(
matcher.plugin_name
)["plugin_type"]
).plugin_type
)
else:
try:
plugin_type = _module.__getattribute__(
"__plugin_type__"
)
except AttributeError:
if hasattr(_module, "__plugin_type__"):
plugin_type = _module.__getattribute__("__plugin_type__")
else:
plugin_type = ("normal",)
if plugin_settings and matcher.plugin_name:
plugins2settings_manager.add_plugin_settings(
@@ -101,28 +95,7 @@ def init_plugins_settings(data_path: str):
plugin_type=plugin_type,
**plugin_settings,
)
except AttributeError:
pass
_tmp.append(matcher.plugin_name)
_tmp_data = {"PluginSettings": plugins2settings_manager.get_data()}
with open(plugins2settings_file, "w", encoding="utf8") as wf:
yaml.dump(_tmp_data, wf, Dumper=yaml.RoundTripDumper, allow_unicode=True)
_data = round_trip_load(open(plugins2settings_file, encoding="utf8"))
_data["PluginSettings"].yaml_set_start_comment(
"""# 模块与对应命令和对应群权限
# 用于生成帮助图片 和 开关功能
# key:模块名称
# level:需要的群等级
# default_status:加入群时功能的默认开关状态
# limit_superuser: 功能状态是否限制超级用户
# cmd: 关闭[cmd] 都会触发命令 关闭对应功能,cmd列表第一个词为统计的功能名称
# plugin_type: 帮助类别 示例:('原神相关',) 或 ('原神相关', 1),1代表帮助命令列向排列,否则为横向排列""",
indent=2,
)
for plugin in _data["PluginSettings"].keys():
_data["PluginSettings"][plugin].yaml_set_start_comment(
f"{plugin}:{_tmp_module[plugin]}", indent=2
)
with open(plugins2settings_file, "w", encoding="utf8") as wf:
round_trip_dump(_data, wf, Dumper=yaml.RoundTripDumper, allow_unicode=True)
except Exception as e:
logger.error(f'{matcher.plugin_name} 初始化 plugin_settings 发生错误 {type(e)}:{e}')
plugins2settings_manager.save()
logger.info(f"已成功加载 {len(plugins2settings_manager.get_data())} 个非限制插件.")
+21 -26
View File
@@ -1,3 +1,5 @@
import random
from asyncpg.exceptions import (
DuplicateColumnError,
UndefinedColumnError,
@@ -13,6 +15,7 @@ from configs.path_config import TEXT_PATH
from asyncio.exceptions import TimeoutError
from typing import List
from utils.http_utils import AsyncHttpx
from utils.utils import GDict
from utils.utils import scheduler
import nonebot
@@ -108,6 +111,18 @@ async def _():
"ALTER TABLE genshin ADD bind_group Integer;",
"genshin"
), # 新增原神群号绑定字段
(
"ALTER TABLE genshin ADD login_ticket VARCHAR(255) DEFAULT '';",
"genshin"
), # 新增米游社login_ticket绑定字段
(
"ALTER TABLE genshin ADD stuid VARCHAR(255) DEFAULT '';",
"genshin"
), # 新增米游社stuid绑定字段
(
"ALTER TABLE genshin ADD stoken VARCHAR(255) DEFAULT '';",
"genshin"
), # 新增米游社stoken绑定字段
(
"ALTER TABLE chat_history ADD plain_text Text;",
"chat_history"
@@ -121,10 +136,13 @@ async def _():
"goods_info"
), # 新增纯文本
]
for sql in sql_str:
for sql in sql_str + GDict.get('run_sql', []):
try:
flag = sql[1]
sql = sql[0]
if isinstance(sql, str):
flag = f'{random.randint(1, 10000)}'
else:
flag = sql[1]
sql = sql[0]
query = db.text(sql)
await db.first(query)
logger.info(f"完成sql操作:{sql}")
@@ -136,29 +154,6 @@ async def _():
# bag_user 将文本转为字典格式
await __database_script(_flag)
# 完成后
end_sql_str = [
# "ALTER TABLE bag_users DROP COLUMN props;" # 删除 bag_users 的 props 字段(还不到时候)
]
for sql in end_sql_str:
try:
query = db.text(sql)
await db.first(query)
logger.info(f"完成执行sql操作:{sql}")
except (DuplicateColumnError, UndefinedColumnError):
pass
except PostgresSyntaxError:
logger.error(f"语法错误:执行sql失败:{sql}")
# str2json_sql = ["alter table bag_users alter COLUMN props type json USING props::json;"] # 字段类型替换
# rename_sql = 'alter table {} rename {} to {};' # 字段更名
# for sql in str2json_sql:
# try:
# query = db.text(sql)
# await db.first(query)
# except DuplicateColumnError:
# pass
@driver.on_bot_connect
async def _(bot: Bot):
+10 -1
View File
@@ -1,5 +1,9 @@
import nonebot
from configs.config import Config
from nonebot import Driver
from utils.decorator.shop import shop_register
import nonebot
driver: Driver = nonebot.get_driver()
Config.add_plugin_config(
@@ -12,3 +16,8 @@ Config.add_plugin_config(
nonebot.load_plugins("basic_plugins/shop")
@driver.on_bot_connect
async def _():
await shop_register.load_register()
+17 -6
View File
@@ -1,4 +1,6 @@
from nonebot import on_command
from models.user_shop_gold_log import UserShopGoldLog
from services.log import logger
from nonebot.adapters.onebot.v11 import GroupMessageEvent, Message
from nonebot.params import CommandArg
@@ -46,10 +48,7 @@ async def _(event: GroupMessageEvent, arg: Message = CommandArg()):
for x in await GoodsInfo.get_all_goods()
if x.goods_limit_time > time.time() or x.goods_limit_time == 0
]
goods_name_list = [
x.goods_name
for x in goods_list
]
goods_name_list = [x.goods_name for x in goods_list]
msg = arg.extract_plain_text().strip().split()
num = 1
if len(msg) > 1:
@@ -77,11 +76,15 @@ async def _(event: GroupMessageEvent, arg: Message = CommandArg()):
await BagUser.get_gold(event.user_id, event.group_id)
) < goods.goods_price * num * goods.goods_discount:
await buy.finish("您的金币好像不太够哦", at_sender=True)
flag, n = await GoodsInfo.check_user_daily_purchase(goods, event.user_id, event.group_id, num)
flag, n = await GoodsInfo.check_user_daily_purchase(
goods, event.user_id, event.group_id, num
)
if flag:
await buy.finish(f"该次购买将超过每日次数限制,目前该道具还可以购买{n}次哦", at_sender=True)
if await BagUser.buy_property(event.user_id, event.group_id, goods, num):
await GoodsInfo.add_user_daily_purchase(goods, event.user_id, event.group_id, num)
await GoodsInfo.add_user_daily_purchase(
goods, event.user_id, event.group_id, num
)
await buy.send(
f"花费 {goods.goods_price * num * goods.goods_discount} 金币购买 {goods.goods_name} ×{num} 成功!",
at_sender=True,
@@ -90,6 +93,14 @@ async def _(event: GroupMessageEvent, arg: Message = CommandArg()):
f"USER {event.user_id} GROUP {event.group_id} "
f"花费 {goods.goods_price*num} 金币购买 {goods.goods_name} ×{num} 成功!"
)
await UserShopGoldLog.add_shop_log(
event.user_id,
event.group_id,
0,
goods.goods_name,
num,
goods.goods_price * num * goods.goods_discount,
)
else:
await buy.send(f"{goods.goods_name} 购买失败!", at_sender=True)
logger.info(
@@ -1,4 +1,7 @@
from nonebot import on_command
from utils.message_builder import image
from ._data_source import create_bag_image
from services.log import logger
from nonebot.adapters.onebot.v11 import GroupMessageEvent
from models.bag_user import BagUser
@@ -32,10 +35,11 @@ my_props = on_command("我的道具", priority=5, block=True, permission=GROUP)
async def _(event: GroupMessageEvent):
props = await BagUser.get_property(event.user_id, event.group_id)
if props:
rst = ""
for i, p in enumerate(props.keys()):
rst += f"{i+1}.{p}\t×{props[p]}\n"
await my_props.send("\n" + rst[:-1], at_sender=True)
await my_props.send(image(b64=await create_bag_image(props)))
# rst = ""
# for i, p in enumerate(props.keys()):
# rst += f"{i+1}.{p}\t×{props[p]}\n"
# await my_props.send("\n" + rst[:-1], at_sender=True)
logger.info(f"USER {event.user_id} GROUP {event.group_id} 查看我的道具")
else:
await my_props.finish("您的背包里没有任何的道具噢~", at_sender=True)
+121
View File
@@ -0,0 +1,121 @@
from typing import Dict, List, Optional
from models.bag_user import BagUser
from models.goods_info import GoodsInfo
from utils.image_utils import BuildImage
from configs.path_config import IMAGE_PATH
icon_path = IMAGE_PATH / "shop_icon"
async def create_bag_image(props: Dict[str, int]):
"""
说明:
创建背包道具图片
参数:
:param props: 道具仓库字典
"""
goods_list = await GoodsInfo.get_all_goods()
active_props = await _init_prop(props, [x for x in goods_list if not x.is_passive])
passive_props = await _init_prop(props, [x for x in goods_list if x.is_passive])
active_w = 0
active_h = 0
passive_w = 0
passive_h = 0
if active_props:
img = BuildImage(
active_props.w,
active_props.h + 70,
font="CJGaoDeGuo.otf",
font_size=30,
color="#f9f6f2",
)
await img.apaste(active_props, (0, 70))
await img.atext((0, 30), "主动道具")
active_props = img
active_w = img.w
active_h = img.h
if passive_props:
img = BuildImage(
passive_props.w,
passive_props.h + 70,
font="CJGaoDeGuo.otf",
font_size=30,
color="#f9f6f2",
)
await img.apaste(passive_props, (0, 70))
await img.atext((0, 30), "被动道具")
passive_props = img
passive_w = img.w
passive_h = img.h
A = BuildImage(
active_w + passive_w + 100,
max(active_h, passive_h) + 60,
font="CJGaoDeGuo.otf",
font_size=30,
color="#f9f6f2",
)
curr_w = 50
if active_props:
await A.apaste(active_props, (curr_w, 0))
curr_w += active_props.w + 10
if passive_props:
await A.apaste(passive_props, (curr_w, 0))
if active_props and passive_props:
await A.aline(
(active_props.w + 45, 70, active_props.w + 45, A.h - 20), fill=(0, 0, 0)
)
return A.pic2bs4()
async def _init_prop(
props: Dict[str, int], _props: List[GoodsInfo]
) -> Optional[BuildImage]:
"""
说明:
构造道具列表图片
参数:
:param props: 道具仓库字典
:param _props: 道具列表
"""
active_name = [x.goods_name for x in _props]
name_list = [x for x in props.keys() if x in active_name]
if not name_list:
return None
temp_img = BuildImage(0, 0, font_size=20)
image_list = []
num_list = []
for i, name in enumerate(name_list):
img = BuildImage(
temp_img.getsize(name)[0] + 50,
30,
font="msyh.ttf",
font_size=20,
color="#f9f6f2",
)
await img.atext((30, 5), f"{i + 1}.{name}")
goods = [x for x in _props if x.goods_name == name][0]
if goods.icon and (icon_path / goods.icon).exists():
icon = BuildImage(30, 30, background=icon_path / goods.icon)
await img.apaste(icon, alpha=True)
image_list.append(img)
num_list.append(
BuildImage(
30, 30, font_size=20, font="msyh.ttf", plain_text=f"×{props[name]}"
)
)
max_w = 0
num_max_w = 0
h = 0
for img, num in zip(image_list, num_list):
h += img.h
max_w = max_w if max_w > img.w else img.w
num_max_w = num_max_w if num_max_w > num.w else num.w
A = BuildImage(max_w + num_max_w + 30, h, color="#f9f6f2")
curr_h = 0
for img, num in zip(image_list, num_list):
await A.apaste(img, (0, curr_h))
await A.apaste(num, (max_w + 20, curr_h + 5), True)
curr_h += img.h
return A
@@ -6,7 +6,6 @@ from utils.message_builder import image
from nonebot.permission import SUPERUSER
from utils.utils import is_number, scheduler
from nonebot.params import CommandArg
from nonebot.plugin import export
from services.log import logger
import os
@@ -51,11 +50,6 @@ __plugin_block_limit__ = {
"limit_type": "group"
}
# 导出方法供其他插件使用
export = export()
export.register_goods = register_goods
export.delete_goods = delete_goods
export.update_goods = update_goods
shop_help = on_command("商店", priority=5, block=True)
+72 -58
View File
@@ -1,50 +1,18 @@
from PIL import Image
from models.goods_info import GoodsInfo
from utils.image_utils import BuildImage
from models.sign_group_user import SignGroupUser
from utils.image_utils import BuildImage, text2image
from utils.utils import is_number
from configs.path_config import IMAGE_PATH
from typing import Optional, Union, Tuple
from configs.config import Config
from nonebot import Driver
from nonebot.plugin import require
from utils.decorator.shop import shop_register
import nonebot
from utils.utils import GDict
import time
driver: Driver = nonebot.get_driver()
icon_path = IMAGE_PATH / 'shop_icon'
use = require("use")
@driver.on_startup
async def init_default_shop_goods():
"""
导入内置的三个商品
"""
@shop_register(
name=("好感度双倍加持卡Ⅰ", "好感度双倍加持卡Ⅱ", "好感度双倍加持卡Ⅲ"),
price=(30, 150, 250),
des=(
"下次签到双倍好感度概率 + 10%(谁才是真命天子?)(同类商品将覆盖)",
"下次签到双倍好感度概率 + 20%(平平庸庸)(同类商品将覆盖)",
"下次签到双倍好感度概率 + 30%(金币才是真命天子!)(同类商品将覆盖)",
),
load_status=Config.get_config("shop", "IMPORT_DEFAULT_SHOP_GOODS"),
daily_limit=(10, 20, 30),
** {"好感度双倍加持卡Ⅰ_prob": 0.1, "好感度双倍加持卡Ⅱ_prob": 0.2, "好感度双倍加持卡Ⅲ_prob": 0.3},
)
async def sign_card(user_id: int, group_id: int, prob: float):
user = await SignGroupUser.ensure(user_id, group_id)
await user.update(add_probability=prob).apply()
@driver.on_bot_connect
async def _():
await shop_register.load_register()
GDict['run_sql'].append("ALTER TABLE goods_info ADD is_passive boolean DEFAULT False;")
GDict['run_sql'].append("ALTER TABLE goods_info ADD icon VARCHAR(255);")
# 创建商店界面
@@ -54,28 +22,22 @@ async def create_shop_help() -> str:
:return: 图片base64
"""
goods_lst = await GoodsInfo.get_all_goods()
idx = 1
_dc = {}
font_h = BuildImage(0, 0).getsize("正")[1]
h = 10
_list = []
for goods in goods_lst:
if goods.goods_limit_time == 0 or time.time() < goods.goods_limit_time:
h += len(goods.goods_description.strip().split("\n")) * font_h + 80
_list.append(goods)
A = BuildImage(1000, h, color="#f9f6f2")
current_h = 0
# A = BuildImage(1100, h, color="#f9f6f2")
total_n = 0
for goods in _list:
bk = BuildImage(1180, 80, font_size=15, color="#f9f6f2", font="CJGaoDeGuo.otf")
goods_image = BuildImage(
600, 80, font_size=20, color="#a29ad6", font="CJGaoDeGuo.otf"
)
image_list = []
for idx, goods in enumerate(_list):
name_image = BuildImage(
580, 40, font_size=25, color="#e67b6b", font="CJGaoDeGuo.otf"
)
await name_image.atext(
(15, 0), f"{idx}.{goods.goods_name}", center_type="by_height"
(15, 0), f"{idx + 1}.{goods.goods_name}", center_type="by_height"
)
await name_image.aline((380, -5, 280, 45), "#a29ad6", 5)
await name_image.atext((390, 0), "售价:", center_type="by_height")
@@ -102,13 +64,48 @@ async def create_shop_help() -> str:
f" 金币",
center_type="by_height",
)
des_image = None
font_img = BuildImage(
600, 80, font_size=20, color="#a29ad6", font="CJGaoDeGuo.otf"
)
p = font_img.getsize('简介:')[0] + 20
if goods.goods_description:
des_list = goods.goods_description.split('\n')
desc = ''
for des in des_list:
if font_img.getsize(des)[0] > font_img.w - p - 20:
msg = ''
tmp = ''
for i in range(len(des)):
if font_img.getsize(tmp)[0] < font_img.w - p - 20:
tmp += des[i]
else:
msg += tmp + '\n'
tmp = des[i]
desc += msg
if tmp:
desc += tmp
else:
desc += des + '\n'
if desc[-1] == '\n':
desc = desc[:-1]
des_image = await text2image(desc, color="#a29ad6")
goods_image = BuildImage(
600, (50 + des_image.h) if des_image else 50, font_size=20, color="#a29ad6", font="CJGaoDeGuo.otf"
)
if des_image:
await goods_image.atext((15, 50), '简介:')
await goods_image.apaste(des_image, (p, 50))
await name_image.acircle_corner(5)
await goods_image.apaste(name_image, (0, 5), True, center_type="by_width")
await goods_image.atext((15, 50), f"简介:{goods.goods_description}")
await goods_image.acircle_corner(20)
await bk.apaste(goods_image, alpha=True)
bk = BuildImage(1180, (50 + des_image.h) if des_image else 50, font_size=15, color="#f9f6f2", font="CJGaoDeGuo.otf")
if goods.icon and (icon_path / goods.icon).exists():
icon = BuildImage(70, 70, background=icon_path / goods.icon)
await bk.apaste(icon)
await bk.apaste(goods_image, (70, 0), alpha=True)
n = 0
_w = 550
_w = 650
# 添加限时图标和时间
if goods.goods_limit_time > 0:
n += 140
@@ -162,14 +159,22 @@ async def create_shop_help() -> str:
if total_n < n:
total_n = n
if n:
await bk.aline((550, -1, 550 + n, -1), "#a29ad6", 5)
await bk.aline((550, 80, 550 + n, 80), "#a29ad6", 5)
await bk.aline((650, -1, 650 + n, -1), "#a29ad6", 5)
# await bk.aline((650, 80, 650 + n, 80), "#a29ad6", 5)
# 添加限时图标和时间
idx += 1
await A.apaste(bk, (0, current_h), True)
current_h += 90
w = 850
image_list.append(bk)
# await A.apaste(bk, (0, current_h), True)
# current_h += 90
h = 0
current_h = 0
for img in image_list:
h += img.h + 10
A = BuildImage(1100, h, color="#f9f6f2")
for img in image_list:
await A.apaste(img, (0, current_h), True)
current_h += img.h + 10
w = 950
if total_n:
w += total_n
h = A.h + 230 + 100
@@ -197,6 +202,8 @@ async def register_goods(
discount: Optional[float] = 1,
limit_time: Optional[int] = 0,
daily_limit: Optional[int] = 0,
is_passive: Optional[bool] = False,
icon: Optional[str] = None,
) -> bool:
"""
添加商品
@@ -209,6 +216,8 @@ async def register_goods(
:param discount: 商品折扣
:param limit_time: 商品限时销售时间,单位为小时
:param daily_limit: 每日购买次数限制
:param is_passive: 是否为被动
:param icon: 图标
:return: 是否添加成功
"""
if not await GoodsInfo.get_goods_info(name):
@@ -220,7 +229,7 @@ async def register_goods(
else 0
)
return await GoodsInfo.add_goods(
name, int(price), des, float(discount), limit_time, daily_limit
name, int(price), des, float(discount), limit_time, daily_limit, is_passive, icon
)
return False
@@ -272,6 +281,7 @@ async def update_goods(**kwargs) -> Tuple[bool, str, str]:
discount = goods.goods_discount
limit_time = goods.goods_limit_time
daily_limit = goods.daily_limit
is_passive = goods.is_passive
new_time = 0
tmp = ""
if kwargs.get("price"):
@@ -294,6 +304,9 @@ async def update_goods(**kwargs) -> Tuple[bool, str, str]:
if kwargs.get("daily_limit"):
tmp += f'每日购买限制:{daily_limit} --> {kwargs["daily_limit"]}\n' if daily_limit else "取消了购买限制\n"
daily_limit = int(kwargs["daily_limit"])
if kwargs.get("is_passive"):
tmp += f'被动道具:{is_passive} --> {kwargs["is_passive"]}\n'
des = kwargs["is_passive"]
await GoodsInfo.update_goods(
name,
int(price),
@@ -304,7 +317,8 @@ async def update_goods(**kwargs) -> Tuple[bool, str, str]:
if limit_time != 0 and new_time
else 0
),
daily_limit
daily_limit,
is_passive
)
return True, name, tmp[:-1],
+60 -34
View File
@@ -1,13 +1,18 @@
from nonebot import on_command
from typing import Any, Tuple
from nonebot import on_command, on_regex
from models.user_shop_gold_log import UserShopGoldLog
from services.log import logger
from nonebot.adapters.onebot.v11 import Bot, GroupMessageEvent, Message
from nonebot.params import CommandArg
from nonebot.params import CommandArg, RegexGroup
from utils.decorator.shop import NotMeetUseConditionsException
from utils.utils import is_number
from models.bag_user import BagUser
from nonebot.adapters.onebot.v11.permission import GROUP
from services.db_context import db
from nonebot.plugin import export
from .data_source import effect, register_use, func_manager
from .data_source import effect, register_use, func_manager, build_params
__zx_plugin_name__ = "商店 - 使用道具"
@@ -15,12 +20,16 @@ __plugin_usage__ = """
usage:
普通的使用道具
指令:
使用道具 [序号或道具名称] ?[数量]=1
使用道具 [序号或道具名称] ?[数量]=1 ?[其他信息]
示例:使用道具好感度双倍加持卡 使用道具好感度双倍加持卡
示例:使用道具1 使用第一个道具
示例:使用道具1 10 使用10个第一个道具
示例:使用道具1 1 来点色图 使用第一个道具并附带信息
* 序号以 ”我的道具“ 为准 *
""".strip()
__plugin_des__ = "商店 - 使用道具"
__plugin_cmd__ = ["使用道具 [序号或道具名称]"]
__plugin_type__ = ('商店',)
__plugin_type__ = ("商店",)
__plugin_version__ = 0.1
__plugin_author__ = "HibiKier"
__plugin_settings__ = {
@@ -30,52 +39,69 @@ __plugin_settings__ = {
"cmd": ["商店", "使用道具"],
}
# 导出方法供其他插件使用
export = export()
export.register_use = register_use
use_props = on_command("使用道具", priority=5, block=True, permission=GROUP)
use_props = on_command(r"使用道具", priority=5, block=True, permission=GROUP)
@use_props.handle()
async def _(bot: Bot, event: GroupMessageEvent, arg: Message = CommandArg()):
msg = arg.extract_plain_text().strip()
msg = arg.extract_plain_text()
num = 1
msg_sp = msg.split()
if len(msg_sp) > 1 and is_number(msg_sp[-1]) and int(msg_sp[-1]) > 0:
num = int(msg.split()[-1])
msg = " ".join(msg.split()[:-1])
property_ = await BagUser.get_property(event.user_id, event.group_id)
text = ""
prop_n = None
index = None
split = msg.split()
if size := len(split):
if size == 1:
prop_n = split[0].strip()
index = 1
if size > 1 and is_number(split[1].strip()):
prop_n = split[0].strip()
num = int(split[1].strip())
index = 2
else:
await use_props.finish("缺少参数,请查看帮助", at_sender=True)
if index:
text = " ".join(split[index:])
property_ = await BagUser.get_property(event.user_id, event.group_id, True)
if property_:
async with db.transaction():
if is_number(msg):
if 0 < int(msg) <= len(property_):
name = list(property_.keys())[int(msg) - 1]
else:
await use_props.finish("仔细看看自己的道具仓库有没有这个道具?", at_sender=True)
name = None
if prop_n and is_number(prop_n):
if 0 < int(prop_n) <= len(property_):
name = list(property_.keys())[int(prop_n) - 1]
else:
if msg not in property_.keys():
await use_props.finish("道具名称错误!", at_sender=True)
name = msg
_user_prop_count = property_[name]
if num > _user_prop_count:
await use_props.finish(f"道具数量不足,无法使用{num}次!")
if num > (n := func_manager.get_max_num_limit(name)):
await use_props.finish(f"该道具单次只能使用 {n} 个!")
if await BagUser.delete_property(
event.user_id, event.group_id, name, num
):
await use_props.finish("仔细看看自己的道具仓库有没有这个道具?", at_sender=True)
else:
if prop_n not in property_.keys():
await use_props.finish("道具名称错误!", at_sender=True)
name = prop_n
_user_prop_count = property_[name]
if num > _user_prop_count:
await use_props.finish(f"道具数量不足,无法使用{num}次!")
if num > (n := func_manager.get_max_num_limit(name)):
await use_props.finish(f"该道具单次只能使用 {n} 个!")
model, kwargs = build_params(bot, event, name, num, text)
try:
await func_manager.run_handle(type_="before_handle", param=model, **kwargs)
except NotMeetUseConditionsException as e:
await use_props.finish(e.get_info(), at_sender=True)
async with db.transaction():
if await BagUser.delete_property(event.user_id, event.group_id, name, num):
if func_manager.check_send_success_message(name):
await use_props.send(f"使用道具 {name} {num} 次成功!", at_sender=True)
if msg := await effect(bot, event, name, num):
if msg := await effect(bot, event, name, num, text, event.message):
await use_props.send(msg, at_sender=True)
logger.info(
f"USER {event.user_id} GROUP {event.group_id} 使用道具 {name} {num} 次成功"
)
await UserShopGoldLog.add_shop_log(
event.user_id, event.group_id, 1, name, num
)
else:
await use_props.send(f"使用道具 {name} {num} 次失败!", at_sender=True)
logger.info(
f"USER {event.user_id} GROUP {event.group_id} 使用道具 {name} {num} 次失败"
)
await func_manager.run_handle(type_="after_handle", param=model, **kwargs)
else:
await use_props.send("您的背包里没有任何的道具噢", at_sender=True)
+100 -34
View File
@@ -1,9 +1,9 @@
from nonebot.adapters.onebot.v11 import GroupMessageEvent, MessageSegment
from nonebot.adapters.onebot.v11 import GroupMessageEvent, MessageSegment, Message
from services.log import logger
from nonebot.adapters.onebot.v11 import Bot
from pydantic import create_model
from utils.models import ShopParam
from typing import Optional, Union
from typing import Optional, Union, Callable, List, Tuple, Dict, Any
from types import MappingProxyType
import inspect
import asyncio
@@ -13,6 +13,30 @@ class GoodsUseFuncManager:
def __init__(self):
self._data = {}
def register_use_before_handle(self, goods_name: str, fun_list: List[Callable]):
"""
说明:
注册商品使用前函数
参数:
:param goods_name: 商品名称
:param fun_list: 函数列表
"""
if fun_list:
self._data[goods_name]["before_handle"] = fun_list
logger.info(f"register_use_before_handle 成功注册商品:{goods_name} 的{len(fun_list)}个使用前函数")
def register_use_after_handle(self, goods_name: str, fun_list: List[Callable]):
"""
说明:
注册商品使用后函数
参数:
:param goods_name: 商品名称
:param fun_list: 函数列表
"""
if fun_list:
self._data[goods_name]["after_handle"] = fun_list
logger.info(f"register_use_after_handle 成功注册商品:{goods_name} 的{len(fun_list)}个使用后函数")
def register_use(self, goods_name: str, **kwargs):
"""
注册商品使用方法
@@ -26,7 +50,7 @@ class GoodsUseFuncManager:
判断商品使用方法是否被注册
:param goods_name: 商品名称
"""
return bool(self ._data.get(goods_name))
return bool(self._data.get(goods_name))
def get_max_num_limit(self, goods_name: str) -> int:
"""
@@ -37,6 +61,21 @@ class GoodsUseFuncManager:
return self._data[goods_name]["kwargs"]["max_num_limit"]
return 1
def _parse_args(self, args_: MappingProxyType, param: ShopParam, **kwargs):
param_list_ = []
_bot = param.bot
param.bot = None
param_json = param.dict()
param_json["bot"] = _bot
for par in args_.keys():
if par in ["shop_param"]:
param_list_.append(param)
elif par not in ["args", "kwargs"]:
param_list_.append(param_json.get(par))
if kwargs.get(par) is not None:
del kwargs[par]
return param_list_
async def use(
self, param: ShopParam, **kwargs
) -> Optional[Union[str, MessageSegment]]:
@@ -45,31 +84,18 @@ class GoodsUseFuncManager:
:param param: BaseModel
:param kwargs: kwargs
"""
def parse_args(args_: MappingProxyType):
param_list_ = []
_bot = param.bot
param.bot = None
param_json = param.dict()
param_json["bot"] = _bot
for par in args_.keys():
if par in ["shop_param"]:
param_list_.append(param)
elif par not in ["args", "kwargs"]:
param_list_.append(param_json.get(par))
if kwargs.get(par) is not None:
del kwargs[par]
return param_list_
goods_name = param.goods_name
if self.exists(goods_name):
# 使用方法
args = inspect.signature(self._data[goods_name]["func"]).parameters
if args and list(args.keys())[0] != "kwargs":
if asyncio.iscoroutinefunction(self._data[goods_name]["func"]):
return await self._data[goods_name]["func"](
*parse_args(args)
*self._parse_args(args, param, **kwargs)
)
else:
return self._data[goods_name]["func"](
*parse_args(args)
*self._parse_args(args, param, **kwargs)
)
else:
if asyncio.iscoroutinefunction(self._data[goods_name]["func"]):
@@ -81,6 +107,21 @@ class GoodsUseFuncManager:
**kwargs,
)
async def run_handle(self, goods_name: str, type_: str, param: ShopParam, **kwargs):
if self._data[goods_name].get(type_):
for func in self._data[goods_name].get(type_):
args = inspect.signature(func).parameters
if args and list(args.keys())[0] != "kwargs":
if asyncio.iscoroutinefunction(func):
await func(*self._parse_args(args, param, **kwargs))
else:
func(*self._parse_args(args, param, **kwargs))
else:
if asyncio.iscoroutinefunction(func):
await func(**kwargs)
else:
func(**kwargs)
def check_send_success_message(self, goods_name: str) -> bool:
"""
检查是否发送使用成功信息
@@ -99,7 +140,7 @@ class GoodsUseFuncManager:
return self._data[goods_name]["kwargs"]
return {}
def init_model(self, goods_name: str, bot: Bot, event: GroupMessageEvent, num: int):
def init_model(self, goods_name: str, bot: Bot, event: GroupMessageEvent, num: int, text: str):
return self._data[goods_name]["model"](
**{
"goods_name": goods_name,
@@ -108,6 +149,8 @@ class GoodsUseFuncManager:
"user_id": event.user_id,
"group_id": event.group_id,
"num": num,
"message": event.message,
"text": text
}
)
@@ -115,8 +158,39 @@ class GoodsUseFuncManager:
func_manager = GoodsUseFuncManager()
def build_params(
bot: Bot, event: GroupMessageEvent, goods_name: str, num: int, text: str
) -> Tuple[ShopParam, Dict[str, Any]]:
"""
说明:
构造参数
参数:
:param bot: bot
:param event: event
:param goods_name: 商品名称
:param num: 数量
:param text: 其他信息
:return:
"""
_kwargs = func_manager.get_kwargs(goods_name)
return (
func_manager.init_model(goods_name, bot, event, num, text),
{
**_kwargs,
"_bot": bot,
"event": event,
"group_id": event.group_id,
"user_id": event.user_id,
"num": num,
"text": text,
"message": event.message,
"goods_name": goods_name,
},
)
async def effect(
bot: Bot, event: GroupMessageEvent, goods_name: str, num: int
bot: Bot, event: GroupMessageEvent, goods_name: str, num: int, text: str, message: Message
) -> Optional[Union[str, MessageSegment]]:
"""
商品生效
@@ -124,30 +198,22 @@ async def effect(
:param event: GroupMessageEvent
:param goods_name: 商品名称
:param num: 使用数量
:param text: 其他信息
:param message: Message
:return: 使用是否成功
"""
# 优先使用注册的商品插件
# try:
if func_manager.exists(goods_name):
_kwargs = func_manager.get_kwargs(goods_name)
return await func_manager.use(
func_manager.init_model(goods_name, bot, event, num),
**{
**_kwargs,
"_bot": bot,
"event": event,
"group_id": event.group_id,
"user_id": event.user_id,
"num": num,
"goods_name": goods_name,
},
)
model, kwargs = build_params(bot, event, goods_name, num, text)
return await func_manager.use(model, **kwargs)
# except Exception as e:
# logger.error(f"use 商品生效函数effect 发生错误 {type(e)}:{e}")
return None
def register_use(goods_name: str, func, **kwargs):
def register_use(goods_name: str, func: Callable, **kwargs):
"""
注册商品使用方法
:param goods_name: 商品名称
+53 -4
View File
@@ -1,37 +1,86 @@
from nonebot.adapters.onebot.v11 import Message
from nonebot.adapters.onebot.v11 import Message, Bot, MessageEvent
from nonebot import on_command
from nonebot.permission import SUPERUSER
from nonebot.rule import to_me
from utils.message_builder import custom_forward_msg
from services.db_context import db
from nonebot.params import CommandArg
from services.log import logger
import asyncio
__zx_plugin_name__ = "执行sql [Superuser]"
__plugin_usage__ = """
usage:
执行一段sql语句
指令:
exec [sql语句]
exec [sql语句] ([查询页数,19条/页])
查看所有表
""".strip()
__plugin_des__ = "执行一段sql语句"
__plugin_cmd__ = [
"exec [sql语句]",
]
__plugin_version__ = 0.1
__plugin_version__ = 0.2
__plugin_author__ = "HibiKier"
exec_ = on_command("exec", rule=to_me(), permission=SUPERUSER, priority=1, block=True)
tables = on_command("查看所有表", rule=to_me(), permission=SUPERUSER, priority=1, block=True)
@exec_.handle()
async def _(arg: Message = CommandArg()):
async def _(bot: Bot, event: MessageEvent, arg: Message = CommandArg()):
sql = arg.extract_plain_text().strip()
async with db.transaction():
try:
# 判断是否为SELECT语句
if sql.lower().startswith("select"):
# 分割语句
try:
page = int(sql.split(" ")[-1]) - 1
sql_list = sql.split(" ")[:-1]
except ValueError:
page = 0
sql_list = sql.split(" ")
# 拼接语句
sql = " ".join(sql_list)
query = db.text(sql)
res = await db.all(query)
msg_list = [f"第{page+1}页查询结果:"]
# logger.info(res)
# 获取所有字段
keys = res[0].keys()
# 每页10条
for i in res[page * 10 : (page + 1) * 10]:
msg = ""
for key in keys:
msg += f"{key}: {i[key]}\n"
msg += f"第{page+1}页第{res.index(i)+1}条"
msg_list.append(msg)
# 检查是私聊还是群聊
try:
forward_msg_list = custom_forward_msg(msg_list, bot.self_id)
await bot.send_group_forward_msg(group_id=event.group_id, messages=forward_msg_list)
except:
for msg in msg_list:
await exec_.send(msg)
await asyncio.sleep(0.2)
return
query = db.text(sql)
await db.first(query)
await exec_.send("执行 sql 语句成功.")
except Exception as e:
await exec_.send(f"执行 sql 语句失败 {type(e)}:{e}")
logger.error(f"执行 sql 语句失败 {type(e)}:{e}")
@tables.handle()
async def _(bot: Bot, event: MessageEvent):
# 获取所有表
query = db.text("select tablename from pg_tables where schemaname = 'public'")
res = await db.all(query)
msg = "数据库中的所有表名:\n"
for tablename in res:
msg += str(tablename["tablename"]) + "\n"
await tables.finish(msg)
+2 -2
View File
@@ -122,8 +122,8 @@ async def _(event: GroupMessageEvent):
tmp = ""
data = plugins2settings_manager.get_data()
for module in data:
if data[module]["level"] > level:
plugin_name = data[module]["cmd"][0]
if data[module].level > level:
plugin_name = data[module].cmd[0]
if plugin_name == "pixiv":
plugin_name = "搜图 p站排行"
tmp += f"{plugin_name}\n"
+11 -10
View File
@@ -94,13 +94,14 @@ class ConfigsManager:
del self._data[module]
self.save()
def set_config(self, module: str, key: str, value: Any , save_simple_data: bool = False):
def set_config(self, module: str, key: str, value: Any, auto_save: bool = False, save_simple_data: bool = True):
"""
设置配置值
:param module: 模块名
:param key: 配置名称
:param value: 值
:param save_simple_data: 同时保存至config.yaml
:param auto_save: 自动保存
:param save_simple_data: 保存至config.yaml
"""
if module in self._data.keys():
if (
@@ -109,7 +110,8 @@ class ConfigsManager:
):
self._data[module][key]["value"] = value
self._simple_data[module][key] = value
self.save(save_simple_data = save_simple_data)
if auto_save:
self.save(save_simple_data=save_simple_data)
def set_help(self, module: str, key: str, help_: str):
"""
@@ -200,13 +202,12 @@ class ConfigsManager:
重新加载配置文件
"""
_yaml = YAML()
temp_file = Path() / "configs" / "config.yaml"
if temp_file.exists():
with open(temp_file, "r", encoding="utf8") as f:
temp = _yaml.load(f)
for key in temp.keys():
for k in temp[key].keys():
self._data[key][k]["value"] = temp[key][k]
if self._simple_file.exists():
with open(self._simple_file, "r", encoding="utf8") as f:
self._simple_data = _yaml.load(f)
for key in self._simple_data.keys():
for k in self._simple_data[key].keys():
self._data[key][k]["value"] = self._simple_data[key][k]
self.save()
def get_admin_level_data(self):
Binary file not shown.

After

Width:  |  Height:  |  Size: 694 KiB

BIN
View File
Binary file not shown.

Before

Width:  |  Height:  |  Size: 694 KiB

After

Width:  |  Height:  |  Size: 326 KiB

+9 -1
View File
@@ -2,6 +2,7 @@ from services.db_context import db
from typing import Dict
from typing import Optional, List
from services.log import logger
from .goods_info import GoodsInfo
class BagUser(db.Model):
@@ -62,17 +63,24 @@ class BagUser(db.Model):
return 100
@classmethod
async def get_property(cls, user_qq: int, group_id: int) -> Dict[str, int]:
async def get_property(cls, user_qq: int, group_id: int, only_active: bool = False) -> Dict[str, int]:
"""
说明:
获取当前道具
参数:
:param user_qq: qq号
:param group_id: 所在群号
:param only_active: 仅仅获取主动使用的道具
"""
query = cls.query.where((cls.user_qq == user_qq) & (cls.group_id == group_id))
user = await query.gino.first()
if user:
if only_active and user.property:
data = {}
name_list = [x.goods_name for x in await GoodsInfo.get_all_goods() if not x.is_passive]
for key in [x for x in user.property.keys() if x in name_list]:
data[key] = user.property[key]
return data
return user.property
else:
await cls.create(
+16 -2
View File
@@ -16,6 +16,8 @@ class GoodsInfo(db.Model):
daily_purchase_limit = db.Column(
db.JSON(), nullable=False, default={}
) # 每日购买限制数据存储
is_passive = db.Column(db.Boolean(), nullable=False, default=0) # 是否为被动
icon = db.Column(db.String(), default=0) # 图标
_idx1 = db.Index("goods_group_users_idx1", "goods_name", unique=True)
@@ -28,6 +30,8 @@ class GoodsInfo(db.Model):
goods_discount: float = 1,
goods_limit_time: int = 0,
daily_limit: int = 0,
is_passive: bool = False,
icon: Optional[str] = None,
) -> bool:
"""
说明:
@@ -39,6 +43,8 @@ class GoodsInfo(db.Model):
:param goods_discount: 商品折扣
:param goods_limit_time: 商品限时
:param daily_limit: 每日购买限制
:param is_passive: 是否为被动道具
:param icon: 图标
"""
try:
if not await cls.get_goods_info(goods_name):
@@ -49,10 +55,12 @@ class GoodsInfo(db.Model):
goods_discount=goods_discount,
goods_limit_time=goods_limit_time,
daily_limit=daily_limit,
is_passive=is_passive,
icon=icon
)
return True
except Exception as e:
logger.error(f"GoodsInfo add_goods 发生错误 {type(e)}:{e}")
logger.error(f"GoodsInfo add_goods {goods_name} 发生错误 {type(e)}:{e}")
return False
@classmethod
@@ -81,7 +89,9 @@ class GoodsInfo(db.Model):
goods_description: Optional[str] = None,
goods_discount: Optional[float] = None,
goods_limit_time: Optional[int] = None,
daily_limit: Optional[int] = None
daily_limit: Optional[int] = None,
is_passive: Optional[bool] = None,
icon: Optional[str] = None,
) -> bool:
"""
说明:
@@ -93,6 +103,8 @@ class GoodsInfo(db.Model):
:param goods_discount: 商品折扣
:param goods_limit_time: 商品限时时间
:param daily_limit: 每日次数限制
:param is_passive: 是否为被动
:param icon: 图标
"""
try:
query = (
@@ -108,6 +120,8 @@ class GoodsInfo(db.Model):
goods_discount=goods_discount or query.goods_discount,
goods_limit_time=goods_limit_time if goods_limit_time is not None else query.goods_limit_time,
daily_limit=daily_limit if daily_limit is not None else query.daily_limit,
is_passive=is_passive if is_passive is not None else query.is_passive,
icon=icon or query.icon
).apply()
return True
except Exception as e:
+59
View File
@@ -0,0 +1,59 @@
from datetime import datetime
from services.db_context import db
class UserShopGoldLog(db.Model):
__tablename__ = "user_shop_gold_log"
id = db.Column(db.Integer(), primary_key=True)
user_qq = db.Column(db.BigInteger(), nullable=False)
group_id = db.Column(db.BigInteger(), nullable=False)
type = db.Column(db.Integer(), nullable=False) # 0: 购买,1: 使用,2: 插件
name = db.Column(db.String())
spend_gold = db.Column(db.Integer(), nullable=False)
num = db.Column(db.Integer(), nullable=False)
create_time = db.Column(db.DateTime(timezone=True), nullable=False)
@classmethod
async def add_shop_log(
cls,
user_qq: int,
group_id: int,
type_: int,
name: str,
num: int,
spend_gold: int = 0,
):
"""
说明:
添加商店购买或使用日志
参数:
:param user_qq: qq号
:param group_id: 所在群号
:param type_: 类型
:param name: 商品名称
:param num: 数量
:param spend_gold: 花费金币
"""
await cls.create(
user_qq=user_qq,
group_id=group_id,
type=type_,
name=name,
num=num,
spend_gold=spend_gold,
create_time=datetime.now(),
)
@classmethod
async def get_user_log(cls, user_qq: int, group_id: int) -> "UserShopGoldLog":
"""
说明:
获取用户日志
参数:
:param user_qq: qq号
:param group_id: 所在群号
"""
return await cls.query.where(
(cls.user_qq == user_qq) & (cls.group_qq == group_id)
).first()
+55 -38
View File
@@ -1,8 +1,9 @@
from nonebot import on_command
from nonebot import on_command, on_regex
from nonebot.typing import T_State
from nonebot.adapters.onebot.v11 import Bot, MessageEvent, GroupMessageEvent, Message
from utils.message_builder import at
from utils.image_utils import text2image
from utils.message_builder import image
from .data_source import (
add_live_sub,
delete_sub,
@@ -14,12 +15,13 @@ from .data_source import (
BilibiliSub,
)
from models.level_user import LevelUser
from utils.manager import group_manager
from configs.config import Config
from utils.utils import is_number, scheduler, get_bot
from typing import Optional
from typing import Optional, Tuple, Any
from services.log import logger
from nonebot import Driver
from nonebot.params import CommandArg, ArgStr
from nonebot.params import CommandArg, ArgStr, RegexGroup
import nonebot
__zx_plugin_name__ = "B站订阅"
@@ -29,16 +31,16 @@ usage:
主播订阅相当于 直播间订阅 + UP订阅
指令:[示例Id乱打的,仅做示例]
添加订阅 ['主播'/'UP'/'番剧'] [id/链接/番名]
删除订阅 [id]
删除订阅 ['主播'/'UP'/'id'] [id]
查看订阅
示例:添加订阅主播 2345344 <-(直播房间id)
示例:添加订阅UP 2355543 <-(个人主页id)
示例:添加订阅番剧 史莱姆 <-(支持模糊搜索)
示例:添加订阅番剧 125344 <-(番剧id)
示例:删除订阅 2324344 <-(任意id,通过查看订阅获取)
示例:删除订阅id 2324344 <-(任意id,通过查看订阅获取)
""".strip()
__plugin_des__ = "非常便利的B站订阅通知"
__plugin_cmd__ = ["添加订阅 [主播/UP/番剧] [id/链接/番名]", "删除订阅 [id]", "查看订阅"]
__plugin_cmd__ = ["添加订阅 [主播/UP/番剧] [id/链接/番名]", "删除订阅 ['主播'/'UP'/'id'] [id]", "查看订阅"]
__plugin_version__ = 0.1
__plugin_author__ = "HibiKier & NumberSir"
__plugin_settings__ = {
@@ -67,7 +69,7 @@ __plugin_configs__ = {
add_sub = on_command("添加订阅", priority=5, block=True)
del_sub = on_command("删除订阅", priority=5, block=True)
show_sub_info = on_command("查看订阅", priority=5, block=True)
show_sub_info = on_regex("^查看订阅$", priority=5, block=True)
driver: Driver = nonebot.get_driver()
@@ -82,6 +84,7 @@ async def _():
@add_sub.handle()
@del_sub.handle()
async def _(event: MessageEvent, state: T_State, arg: Message = CommandArg()):
msg = arg.extract_plain_text().strip().split()
if len(msg) < 2:
@@ -155,25 +158,30 @@ async def _(
)
@del_sub.handle()
async def _(event: MessageEvent, arg: Message = CommandArg()):
msg = arg.extract_plain_text().strip()
if not is_number(msg):
await del_sub.finish("Id必须为数字!", at_sender=True)
id_ = (
f"{event.user_id}:{event.group_id}"
if isinstance(event, GroupMessageEvent)
else f"{event.user_id}"
)
if await BilibiliSub.delete_bilibili_sub(int(msg), id_):
await del_sub.send(f"删除订阅id:{msg} 成功...")
@del_sub.got("sub_type")
@del_sub.got("sub_user")
@del_sub.got("id")
async def _(
event: MessageEvent,
id_: str = ArgStr("id"),
sub_type: str = ArgStr("sub_type"),
sub_user: str = ArgStr("sub_user"),
):
if sub_type in ["主播", "直播"]:
result = await BilibiliSub.delete_bilibili_sub(int(id_),sub_user,"live")
elif sub_type.lower() in ["up", "用户"]:
result = await BilibiliSub.delete_bilibili_sub(int(id_),sub_user,"up")
else: result = await BilibiliSub.delete_bilibili_sub(int(id_),sub_user)
if result:
await del_sub.send(f"删除订阅id:{id_} 成功...")
logger.info(
f"(USER {event.user_id}, GROUP "
f"{event.group_id if isinstance(event, GroupMessageEvent) else 'private'})"
f" 删除订阅 {id_}"
)
f"(USER {event.user_id}, GROUP "
f"{event.group_id if isinstance(event, GroupMessageEvent) else 'private'})"
f" 删除订阅 {id_}"
)
else:
await del_sub.send(f"删除订阅id:{msg} 失败...")
await del_sub.send(f"删除订阅id:{id_} 失败...")
@show_sub_info.handle()
@@ -207,7 +215,15 @@ async def _(event: MessageEvent):
live_rst = (
"该群目前没有任何订阅..." if isinstance(event, GroupMessageEvent) else "您目前没有任何订阅..."
)
await show_sub_info.send(live_rst + up_rst + season_rst)
await show_sub_info.send(
image(
b64=(
await text2image(
live_rst + up_rst + season_rst, padding=10, color="#f9f6f2"
)
).pic2bs4()
)
)
# 推送
@@ -219,18 +235,18 @@ async def _():
bot = get_bot()
sub = None
if bot:
try:
await sub_manager.reload_sub_data()
sub = await sub_manager.random_sub_data()
if sub:
logger.debug(f"Bilibili订阅开始检测:{sub.sub_id}")
rst = await get_sub_status(sub.sub_id, sub.sub_type)
# try:
await sub_manager.reload_sub_data()
sub = await sub_manager.random_sub_data()
if sub:
logger.debug(f"Bilibili订阅开始检测:{sub.sub_id}")
rst = await get_sub_status(sub.sub_id, sub.sub_type)
await send_sub_msg(rst, sub, bot)
if sub.sub_type == "live":
rst = await get_sub_status(sub.sub_id, "up")
await send_sub_msg(rst, sub, bot)
if sub.sub_type == "live":
rst = await get_sub_status(sub.sub_id, "up")
await send_sub_msg(rst, sub, bot)
except Exception as e:
logger.error(f"B站订阅推送发生错误 sub_id:{sub.sub_id if sub else 0} {type(e)}:{e}")
# except Exception as e:
# logger.error(f"B站订阅推送发生错误 sub_id:{sub.sub_id if sub else 0} {type(e)}:{e}")
async def send_sub_msg(rst: str, sub: BilibiliSub, bot: Bot):
@@ -260,7 +276,8 @@ async def send_sub_msg(rst: str, sub: BilibiliSub, bot: Bot):
and Config.get_config("bilibili_sub", "UP_MSG_AT_ALL")
):
rst = "[CQ:at,qq=all]\n" + rst
await bot.send_group_msg(group_id=group_id, message=Message(rst))
if group_manager.get_plugin_status("bilibili_sub", group_id):
await bot.send_group_msg(group_id=group_id, message=Message(rst))
else:
await bot.send_private_msg(user_id=int(x), message=Message(rst))
except Exception as e:
+46 -52
View File
@@ -7,11 +7,11 @@ from asyncio.exceptions import TimeoutError
from utils.utils import get_bot
from .model import BilibiliSub
from bilireq.live import get_room_info_by_id
from .utils import get_meta
from .utils import get_meta, get_user_card
from utils.message_builder import image
from bilireq.user import get_user_info
from bilireq.user import get_videos
# from .utils import get_videos
from bilireq import dynamic
from .utils import get_videos
from typing import Optional, Tuple
from configs.path_config import IMAGE_PATH
from datetime import datetime
@@ -39,36 +39,35 @@ async def add_live_sub(live_id: int, sub_user: str) -> str:
:return:
"""
try:
async with db.transaction():
try:
"""bilibili_api.live库的LiveRoom类中get_room_info改为bilireq.live库的get_room_info_by_id方法"""
live_info = await get_room_info_by_id(live_id)
except ResponseCodeError:
return f"未找到房间号Id:{live_id} 的信息,请检查Id是否正确"
uid = live_info["uid"]
room_id = live_info["room_id"]
short_id = live_info["short_id"]
title = live_info["title"]
live_status = live_info["live_status"]
if await BilibiliSub.add_bilibili_sub(
room_id,
"live",
sub_user,
uid=uid,
live_short_id=short_id,
live_status=live_status,
):
await _get_up_status(live_id)
uname = (await BilibiliSub.get_sub(live_id)).uname
return (
"已成功订阅主播:\n"
f"\ttitle:{title}\n"
f"\tname: {uname}\n"
f"\tlive_id:{live_id}\n"
f"\tuid:{uid}"
try:
"""bilibili_api.live库的LiveRoom类中get_room_info改为bilireq.live库的get_room_info_by_id方法"""
live_info = await get_room_info_by_id(live_id)
except ResponseCodeError:
return f"未找到房间号Id:{live_id} 的信息,请检查Id是否正确"
uid = live_info["uid"]
room_id = live_info["room_id"]
short_id = live_info["short_id"]
title = live_info["title"]
live_status = live_info["live_status"]
if await BilibiliSub.add_bilibili_sub(
room_id,
"live",
sub_user,
uid=uid,
live_short_id=short_id,
live_status=live_status,
):
await _get_up_status(room_id)
uname = (await BilibiliSub.get_sub(room_id)).uname
return (
"已成功订阅主播:\n"
f"\ttitle:{title}\n"
f"\tname: {uname}\n"
f"\tlive_id:{room_id}\n"
f"\tuid:{uid}"
)
else:
return "添加订阅失败..."
else:
return "添加订阅失败..."
except Exception as e:
logger.error(f"订阅主播live_id:{live_id} 发生了错误 {type(e)}:{e}")
return "添加订阅失败..."
@@ -84,7 +83,7 @@ async def add_up_sub(uid: int, sub_user: str) -> str:
async with db.transaction():
try:
"""bilibili_api.user库中User类的get_user_info改为bilireq.user库的get_user_info方法"""
user_info = await get_user_info(uid)
user_info = await get_user_card(uid)
except ResponseCodeError:
return f"未找到UpId:{uid} 的信息,请检查Id是否正确"
uname = user_info["name"]
@@ -208,8 +207,10 @@ async def get_sub_status(id_: int, sub_type: str) -> Optional[str]:
return await _get_up_status(id_)
elif sub_type == "season":
return await _get_season_status(id_)
except ResponseCodeError:
return f"Id:{id_} 获取信息失败...请检查订阅Id是否存在或稍后再试..."
except ResponseCodeError as msg:
logger.info(f"Id:{id_} 获取信息失败...{msg}")
return None
# return f"Id:{id_} 获取信息失败...请检查订阅Id是否存在或稍后再试..."
# except Exception as e:
# logger.error(f"获取订阅状态发生预料之外的错误 id_:{id_} {type(e)}:{e}")
# return "发生了预料之外的错误..请稍后再试或联系管理员....."
@@ -229,7 +230,7 @@ async def _get_live_status(id_: int) -> Optional[str]:
sub = await BilibiliSub.get_sub(id_)
if sub.live_status != live_status:
await BilibiliSub.update_sub_info(id_, live_status=live_status)
if sub.live_status == 0 and live_status == 1:
if sub.live_status in [0, 2] and live_status == 1:
return (
f""
f"{image(cover)}\n"
@@ -243,20 +244,21 @@ async def _get_live_status(id_: int) -> Optional[str]:
async def _get_up_status(id_: int) -> Optional[str]:
"""
获取用户投稿状态
:param id_: 用户 id
:param id_: 订阅 id
:return:
"""
_user = await BilibiliSub.get_sub(id_)
"""bilibili_api.user库中User类的get_user_info改为bilireq.user库的get_user_info方法"""
user_info = await get_user_info(_user.uid)
user_info = await get_user_card(_user.uid)
uname = user_info["name"]
"""bilibili_api.user库中User类的get_videos改为bilireq.user库的get_videos方法"""
video_info = await get_videos(id_)
video_info = await get_videos(_user.uid)
latest_video_created = 0
video = None
dividing_line = "\n-------------\n"
if _user.uname != uname:
await BilibiliSub.update_sub_info(id_, uname=uname)
dynamic_img, dynamic_upload_time, link = await get_user_dynamic(id_, _user)
dynamic_img, dynamic_upload_time, link = await get_user_dynamic(_user.uid, _user)
if video_info["list"].get("vlist"):
video = video_info["list"]["vlist"][0]
latest_video_created = video["created"]
@@ -270,7 +272,7 @@ async def _get_up_status(id_: int) -> Optional[str]:
and video
and _user.latest_video_created < latest_video_created
):
rst = rst + "-------------\n" if rst else rst
rst = rst + dividing_line if rst else rst
await BilibiliSub.update_sub_info(
id_, latest_video_created=latest_video_created
)
@@ -281,7 +283,7 @@ async def _get_up_status(id_: int) -> Optional[str]:
f'Bvid:{video["bvid"]}\n'
f'直链:https://www.bilibili.com/video/{video["bvid"]}'
)
rst = None if rst == "-------------\n" else rst
rst = None if rst == dividing_line else rst
return rst
@@ -347,16 +349,8 @@ async def get_user_dynamic(
await page.set_viewport_size(
{"width": 2560, "height": 1080, "timeout": 10000 * 20}
)
await page.wait_for_selector(".panel-area")
await page.evaluate(
"""
xs = document.getElementById('internationalHeader');
xs.remove();
xs = document.getElementsByClassName('panel-area')
xs[0].remove();
"""
)
card = page.locator(".detail-card")
await page.wait_for_selector(".bili-dyn-item__main")
card = page.locator(".bili-dyn-item__main")
await card.wait_for()
await card.screenshot(
path=dynamic_path / f"{local_user.sub_id}_{dynamic_upload_time}.jpg",
+52 -44
View File
@@ -65,50 +65,49 @@ class BilibiliSub(db.Model):
:param season_update_time: 番剧更新时间
"""
try:
async with db.transaction():
query = (
await cls.query.where(cls.sub_id == sub_id)
.with_for_update()
.gino.first()
query = (
await cls.query.where( (cls.sub_id == sub_id) & (cls.sub_type == sub_type) )
.with_for_update()
.gino.first()
)
sub_user = sub_user if sub_user[-1] == "," else f"{sub_user},"
if query:
if sub_user not in query.sub_users:
sub_users = query.sub_users + sub_user
await query.update(sub_users=sub_users).apply()
else:
sub = await cls.create(
sub_id=sub_id, sub_type=sub_type, sub_users=sub_user
)
sub_user = sub_user if sub_user[-1] == "," else f"{sub_user},"
if query:
if sub_user not in query.sub_users:
sub_users = query.sub_users + sub_user
await query.update(sub_users=sub_users).apply()
else:
sub = await cls.create(
sub_id=sub_id, sub_type=sub_type, sub_users=sub_user
)
await sub.update(
live_short_id=live_short_id
if live_short_id
else sub.live_short_id,
live_status=live_status if live_status else sub.live_status,
dynamic_upload_time=dynamic_upload_time
if dynamic_upload_time
else sub.dynamic_upload_time,
uid=uid if uid else sub.uid,
uname=uname if uname else sub.uname,
latest_video_created=latest_video_created
if latest_video_created
else sub.latest_video_created,
season_update_time=season_update_time
if season_update_time
else sub.season_update_time,
season_current_episode=season_current_episode
if season_current_episode
else sub.season_current_episode,
season_id=season_id if season_id else sub.season_id,
season_name=season_name if season_name else sub.season_name,
).apply()
return True
await sub.update(
live_short_id=live_short_id
if live_short_id
else sub.live_short_id,
live_status=live_status if live_status else sub.live_status,
dynamic_upload_time=dynamic_upload_time
if dynamic_upload_time
else sub.dynamic_upload_time,
uid=uid if uid else sub.uid,
uname=uname if uname else sub.uname,
latest_video_created=latest_video_created
if latest_video_created
else sub.latest_video_created,
season_update_time=season_update_time
if season_update_time
else sub.season_update_time,
season_current_episode=season_current_episode
if season_current_episode
else sub.season_current_episode,
season_id=season_id if season_id else sub.season_id,
season_name=season_name if season_name else sub.season_name,
).apply()
return True
except Exception as e:
logger.info(f"bilibili_sub 添加订阅错误 {type(e)}: {e}")
return False
@classmethod
async def delete_bilibili_sub(cls, sub_id: int, sub_user: str) -> bool:
async def delete_bilibili_sub(cls, sub_id: int, sub_user: str,sub_type: Optional[str] = None) -> bool:
"""
说明:
删除订阅
@@ -118,13 +117,22 @@ class BilibiliSub(db.Model):
"""
try:
async with db.transaction():
query = (
await cls.query.where(
(cls.sub_id == sub_id) & (cls.sub_users.contains(sub_user))
if sub_type:
query = (
await cls.query.where(
(cls.sub_id == sub_id) & (cls.sub_users.contains(sub_user) & (cls.sub_type == sub_type))
)
.with_for_update()
.gino.first()
)
else:
query = (
await cls.query.where(
(cls.sub_id == sub_id) & (cls.sub_users.contains(sub_user))
)
.with_for_update()
.gino.first()
)
.with_for_update()
.gino.first()
)
if not query:
return False
await query.update(
+45 -16
View File
@@ -1,14 +1,15 @@
from utils.image_utils import BuildImage
from configs.path_config import IMAGE_PATH
from utils.http_utils import AsyncHttpx
from utils.http_utils import AsyncHttpx, get_user_agent
# from bilibili_api import user
from bilireq.user import get_user_info
from httpx import AsyncClient
from io import BytesIO
BORDER_PATH = IMAGE_PATH / "border"
BORDER_PATH.mkdir(parents=True, exist_ok=True)
BASE_URL = "https://api.bilibili.com"
async def get_pic(url: str) -> bytes:
"""
@@ -78,7 +79,6 @@ async def get_meta(media_id: int, auth=None, reqtype="both", **kwargs):
"""
from bilireq.utils import get
BASE_URL = "https://api.bilibili.com"
url = f"{BASE_URL}/pgc/review/user"
params = {"media_id": media_id}
raw_json = await get(url, raw=True, params=params, auth=auth, reqtype=reqtype, **kwargs)
@@ -86,8 +86,7 @@ async def get_meta(media_id: int, auth=None, reqtype="both", **kwargs):
async def get_videos(
uid: int, tid: int = 0, pn: int = 1, keyword: str = "", order: str = "pubdate",
*, auth=None, reqtype="both", **kwargs
uid: int, tid: int = 0, pn: int = 1, keyword: str = "", order: str = "pubdate"
):
"""
获取用户投该视频信息
@@ -100,15 +99,45 @@ async def get_videos(
:param keyword: 搜索关键词
:param order: 排序方式,可以为 “pubdate(上传日期从新到旧), stow(收藏从多到少), click(播放量从多到少)”
"""
from bilireq.utils import get
BASE_URL = "https://api.bilibili.com"
from bilireq.utils import ResponseCodeError
url = f"{BASE_URL}/x/space/arc/search"
params = {
"mid": uid,
"ps": 30,
"tid": tid,
"pn": pn,
"keyword": keyword,
"order": order
}
return await get(url, params=params, auth=auth, reqtype=reqtype, **kwargs)
headers = get_user_agent()
headers["Referer"] = f"https://space.bilibili.com/{uid}/video"
async with AsyncClient() as client:
r = await client.head(
"https://space.bilibili.com",
headers=headers,
)
params = {
"mid": uid,
"ps": 30,
"tid": tid,
"pn": pn,
"keyword": keyword,
"order": order,
}
raw_json = (
await client.get(url, params=params, headers=headers, cookies=r.cookies)
).json()
if raw_json["code"] != 0:
raise ResponseCodeError(
code=raw_json["code"],
msg=raw_json["message"],
data=raw_json.get("data", None),
)
return raw_json["data"]
async def get_user_card(mid, photo: bool = False, auth=None, reqtype="both", **kwargs):
from bilireq.utils import get
url = f"{BASE_URL}/x/web-interface/card"
return (
await get(
url,
params={"mid": mid, "photo": photo},
auth=auth,
reqtype=reqtype,
**kwargs,
)
)["card"]
+1 -1
View File
@@ -9,7 +9,7 @@ import platform
# asyncio.set_event_loop_policy(asyncio.WindowsSelectorEventLoopPolicy())
url = "http://www.eclzz.zone/"
url = "http://www.eclzz.love"
async def get_bt_info(keyword: str, page: int):
+5 -5
View File
@@ -1,4 +1,4 @@
from nonebot.adapters.onebot.v11 import Bot
from nonebot.adapters.onebot.v11 import Bot, MessageEvent
from nonebot.permission import SUPERUSER
from nonebot import on_command
from .data_source import check_update, get_latest_version_data
@@ -50,24 +50,24 @@ restart = on_command(
@update_zhenxun.handle()
async def _(bot: Bot):
async def _(bot: Bot, event: MessageEvent):
try:
code, error = await check_update(bot)
if error:
logger.error(f"更新真寻未知错误 {error}")
await bot.send_private_msg(
user_id=int(list(bot.config.superusers)[0]), message=f"更新真寻未知错误 {error}"
user_id=event.user_id, message=f"更新真寻未知错误 {error}"
)
except Exception as e:
logger.error(f"更新真寻未知错误 {type(e)}:{e}")
await bot.send_private_msg(
user_id=int(list(bot.config.superusers)[0]),
user_id=event.user_id,
message=f"更新真寻未知错误 {type(e)}:{e}",
)
else:
if code == 200:
await bot.send_private_msg(
user_id=int(list(bot.config.superusers)[0]), message=f"更新完毕,请重启真寻...."
user_id=event.user_id, message=f"更新完毕,请重启真寻...."
)
+5 -4
View File
@@ -36,14 +36,15 @@ __plugin_configs__ = {
},
}
coser = on_regex(r"^(\d)连?(cos|COS|coser|括丝)$", priority=5, block=True)
coser = on_regex(r"^(\d)?连?(cos|COS|coser|括丝)$", priority=5, block=True)
url = "https://api.iyk0.com/cos"
# 纯cos,较慢:https://picture.yinux.workers.dev
# 比较杂,有福利姬,较快:https://api.jrsgslb.cn/cos/url.php?return=img
url = "https://picture.yinux.workers.dev/"
@coser.handle()
async def _(bot: Bot, event: MessageEvent, reg_group: Tuple[Any, ...] = RegexGroup()):
async def _(event: MessageEvent, reg_group: Tuple[Any, ...] = RegexGroup()):
num = reg_group[0] or 1
for _ in range(int(num)):
try:
+26 -56
View File
@@ -1,7 +1,8 @@
import contextlib
import random
import dateparser
from lxml import etree
from typing import List, Optional
from typing import List, Optional, Tuple
from urllib.parse import unquote
from pydantic import ValidationError
from nonebot.log import logger
@@ -12,7 +13,6 @@ from .base_handle import BaseHandle, BaseData, UpEvent as _UpEvent, UpChar as _U
from ..config import draw_config
from ..util import remove_prohibited_str, cn2py
from utils.image_utils import BuildImage
import asyncio
try:
import ujson as json
@@ -29,7 +29,7 @@ class AzurChar(BaseData):
class UpChar(_UpChar):
type_: str # 舰娘类型
type_: str # 舰娘类型
class UpEvent(_UpEvent):
@@ -53,7 +53,9 @@ class AzurHandle(BaseHandle[AzurChar]):
type_ = ["维修", "潜艇", "重巡", "轻航", "航母"]
up_pool_flag = pool_name == "活动"
# Up
up_ship = [x for x in self.UP_EVENT.up_char if x.zoom > 0]
up_ship = (
[x for x in self.UP_EVENT.up_char if x.zoom > 0] if self.UP_EVENT else []
)
# print(up_ship)
acquire_char = None
if up_ship and up_pool_flag:
@@ -61,19 +63,15 @@ class AzurHandle(BaseHandle[AzurChar]):
# 初始化概率
cur_ = up_ship[0].zoom / 100
for i in range(len(up_ship)):
try:
up_zoom.append((cur_, cur_ + up_ship[i+1].zoom / 100))
cur_ += up_ship[i+1].zoom / 100
except IndexError:
pass
with contextlib.suppress(IndexError):
up_zoom.append((cur_, cur_ + up_ship[i + 1].zoom / 100))
cur_ += up_ship[i + 1].zoom / 100
rand = random.random()
# 抽取up
for i, zoom in enumerate(up_zoom):
if zoom[0] <= rand <= zoom[1]:
try:
with contextlib.suppress(IndexError):
acquire_char = [x for x in self.ALL_CHAR if x.name == up_ship[i].name][0]
except IndexError:
pass
# 没有up或者未抽取到up
if not acquire_char:
star = self.get_star(
@@ -92,9 +90,6 @@ class AzurHandle(BaseHandle[AzurChar]):
])
return acquire_char
# async def draw(self, count: int, **kwargs) -> Message:
# return await asyncio.get_event_loop().run_in_executor(None, self._draw, count)
async def draw(self, count: int, **kwargs) -> Message:
index2card = self.get_cards(count, **kwargs)
cards = [card[0] for card in index2card]
@@ -162,22 +157,22 @@ class AzurHandle(BaseHandle[AzurChar]):
return
dom = etree.HTML(result, etree.HTMLParser())
contents = dom.xpath(
"//div[@class='resp-tabs-container']/div[@class='resp-tab-content']"
"//div[@class='mw-body-content mw-content-ltr']/div[@class='mw-parser-output']"
)
for index, content in enumerate(contents):
char_list = content.xpath("./table/tbody/tr[2]/td/div/div/div/div")
char_list = content.xpath("./div[@id='CardSelectTr']/div")
for char in char_list:
try:
name = char.xpath("./a/@title")[0]
frame = char.xpath("./div/a/img/@alt")[0]
avatar = char.xpath("./a/img/@srcset")[0]
name = char.xpath("./div/a/@title")[0]
frame = char.xpath("./div/div/a/img/@alt")[0]
avatar = char.xpath("./div/a/img/@srcset")[0]
except IndexError:
continue
member_dict = {
"名称": remove_prohibited_str(name),
"头像": unquote(str(avatar).split(" ")[-2]),
"星级": self.parse_star(frame),
"类型": self.parse_type(index),
"类型": char.xpath("./@data-param1")[0].split(",")[1],
}
info[member_dict["名称"]] = member_dict
# 更新额外信息
@@ -225,48 +220,21 @@ class AzurHandle(BaseHandle[AzurChar]):
@staticmethod
def parse_star(star: str) -> int:
if star in ["舰娘头像外框普通.png", "舰娘头像外框白色.png"]:
if star in {"舰娘头像外框普通.png", "舰娘头像外框白色.png"}:
return 1
elif star in ["舰娘头像外框稀有.png", "舰娘头像外框蓝色.png"]:
elif star in {"舰娘头像外框稀有.png", "舰娘头像外框蓝色.png"}:
return 2
elif star in ["舰娘头像外框精锐.png", "舰娘头像外框紫色.png"]:
elif star in {"舰娘头像外框精锐.png", "舰娘头像外框紫色.png"}:
return 3
elif star in ["舰娘头像外框超稀有.png", "舰娘头像外框金色.png"]:
elif star in {"舰娘头像外框超稀有.png", "舰娘头像外框金色.png"}:
return 4
elif star in ["舰娘头像外框海上传奇.png", "舰娘头像外框彩色.png"]:
elif star in {"舰娘头像外框海上传奇.png", "舰娘头像外框彩色.png"}:
return 5
elif star in [
"舰娘头像外框最高方案.png",
"舰娘头像外框决战方案.png",
"舰娘头像外框超稀有META.png",
"舰娘头像外框精锐META.png",
]:
elif star in {"舰娘头像外框最高方案.png", "舰娘头像外框决战方案.png", "舰娘头像外框超稀有META.png", "舰娘头像外框精锐META.png"}:
return 6
else:
return 6
@staticmethod
def parse_type(index: int) -> str:
azur_types = [
"驱逐",
"轻巡",
"重巡",
"超巡",
"战巡",
"战列",
"航母",
"航站",
"轻航",
"重炮",
"维修",
"潜艇",
"运输",
]
try:
return azur_types[index]
except IndexError:
return azur_types[0]
async def update_up_char(self):
url = "https://wiki.biligame.com/blhx/游戏活动表"
result = await self.get_url(url)
@@ -290,7 +258,7 @@ class AzurHandle(BaseHandle[AzurChar]):
up_chars = []
for ship in ships:
name = ship.xpath("./tbody/tr/td[2]/p/a/@title")[0]
type_ = ship.xpath("./tbody/tr/td[2]/p/small/text()")[0] # 舰船类型
type_ = ship.xpath("./tbody/tr/td[2]/p/small/text()")[0] # 舰船类型
try:
p = float(str(ship.xpath(".//sup/text()")[0]).strip("%"))
except (IndexError, ValueError):
@@ -298,7 +266,9 @@ class AzurHandle(BaseHandle[AzurChar]):
star = self.parse_star(
ship.xpath("./tbody/tr/td[1]/div/div/div/a/img/@alt")[0]
)
up_chars.append(UpChar(name=name, star=star, limited=False, zoom=p, type_=type_))
up_chars.append(
UpChar(name=name, star=star, limited=False, zoom=p, type_=type_)
)
self.UP_EVENT = UpEvent(
title=title,
pool_img="",
+56 -10
View File
@@ -1,4 +1,6 @@
import random
import json
from utils.http_utils import AsyncHttpx
from lxml import etree
from typing import List, Tuple
from PIL import ImageDraw
@@ -107,27 +109,70 @@ class BaHandle(BaseHandle[BaChar]):
else:
return 1
# Bwiki 待恢复
# async def _update_info(self):
# info = {}
# url = "https://wiki.biligame.com/bluearchive/学生筛选"
# result = await self.get_url(url)
# if not result:
# logger.warning(f"更新 {self.game_name_cn} 出错")
# return
# else:
# dom = etree.HTML(result, etree.HTMLParser())
# char_list = dom.xpath("//div[@class='filters']/table[2]/tbody/tr")
# for char in char_list:
# try:
# name = char.xpath("./td[2]/a/div/text()")[0]
# avatar = char.xpath("./td[1]/div/div/a/img/@data-src")[0]
# star_pic = char.xpath("./td[4]/img/@alt")[0]
# except IndexError:
# continue
# member_dict = {
# "头像": unquote(str(avatar)),
# "名称": remove_prohibited_str(name),
# "星级": self.title2star(star_pic),
# }
# info[member_dict["名称"]] = member_dict
# self.dump_data(info)
# logger.info(f"{self.game_name_cn} 更新成功")
# # 下载头像
# for value in info.values():
# await self.download_img(value["头像"], value["名称"])
# # 下载星星
# await self.download_img(
# "https://patchwiki.biligame.com/images/bluearchive/thumb/e/e0/82nj2x9sxko473g7782r14fztd4zyky.png/15px-Star-1.png",
# "star-1",
# )
# await self.download_img(
# "https://patchwiki.biligame.com/images/bluearchive/thumb/0/0b/msaff2g0zk6nlyl1rrn7n1ri4yobcqc.png/30px-Star-2.png",
# "star-2",
# )
# await self.download_img(
# "https://patchwiki.biligame.com/images/bluearchive/thumb/8/8a/577yv79x1rwxk8efdccpblo0lozl158.png/46px-Star-3.png",
# "star-3"
# )
# 数据来源:SCHALE DB
async def _update_info(self):
info = {}
url = "https://wiki.biligame.com/bluearchive/学生筛选"
result = await self.get_url(url)
if not result:
url = "https://lonqie.github.io/SchaleDB/data/cn/students.min.json?v=49"
result = await AsyncHttpx.get(url)
if result.status_code != 200:
logger.warning(f"更新 {self.game_name_cn} 出错")
return
else:
dom = etree.HTML(result, etree.HTMLParser())
char_list = dom.xpath("//div[@class='filters']/table[2]/tbody/tr")
char_list = json.loads(result.text)
for char in char_list:
try:
name = char.xpath("./td[2]/a/div/text()")[0]
avatar = char.xpath("./td[1]/div/div/a/img/@data-src")[0]
star_pic = char.xpath("./td[4]/img/@alt")[0]
name = char.get("Name")
avatar = "https://lonqie.github.io/SchaleDB/images/student/icon/"+char.get("CollectionTexture")+".png"
star = char.get("StarGrade")
except IndexError:
continue
member_dict = {
"头像": unquote(str(avatar)),
"名称": remove_prohibited_str(name),
"星级": self.title2star(star_pic),
"星级": star,
}
info[member_dict["名称"]] = member_dict
self.dump_data(info)
@@ -147,4 +192,5 @@ class BaHandle(BaseHandle[BaChar]):
await self.download_img(
"https://patchwiki.biligame.com/images/bluearchive/thumb/8/8a/577yv79x1rwxk8efdccpblo0lozl158.png/46px-Star-3.png",
"star-3"
)
)
+1
View File
@@ -50,6 +50,7 @@ class UpEvent(BaseModel):
start_time: Optional[datetime] # 开始时间
end_time: Optional[datetime] # 结束时间
up_char: List[UpChar] # up对象
up_name: str = "" # up名称
TC = TypeVar("TC", bound="BaseData")
+27 -27
View File
@@ -331,9 +331,7 @@ class PrettyHandle(BaseHandle[PrettyData]):
try:
title = announcement.xpath("./@title")[0]
url = "https://wiki.biligame.com/" + announcement.xpath("./@href")[0]
if re.match(r".*?\d{8}$", title) or re.match(
r"^\d{1,2}月\d{1,2}日.*?", title
):
if "卡池" in title:
break
except IndexError:
continue
@@ -350,48 +348,49 @@ class PrettyHandle(BaseHandle[PrettyData]):
char_img = ""
card_img = ""
up_chars = []
up_chars_name = []
up_cards = []
soup = BeautifulSoup(result, "lxml")
heads = soup.find_all("span", {"class": "mw-headline"})
up_cards_name = []
dom = etree.HTML(result, etree.HTMLParser())
content = dom.xpath('//*[@id="mw-content-text"]/div/div/div[1]/div')[0]
heads = content.xpath("./div")
for head in heads:
if "时间" in head.text:
time = head.find_next("p").text.split("\n")[0]
if "时间" in head.find("./img").tail or "期间" in head.find("./img").tail:
time = content.xpath("./p")[1].text.split("\n")[0]
if "~" in time:
start, end = time.split("~")
start_time = dateparser.parse(start)
end_time = dateparser.parse(end)
elif "赛马娘" in head.text:
char_img = head.find_next("a", {"class": "image"}).find("img")[
"src"
]
lines = str(head.find_next("p").text).split("\n")
elif "赛马娘" in head.find("./img").tail:
char_img = content.xpath("./center")[1].find("./a/img").get("src")
lines = content.xpath("./p")[2].xpath("string(.)").split("\n")
chars = [
line
for line in lines
if "★" in line and "(" in line and ")" in line
if "★" in line and "[" in line and "]" in line
]
for char in chars:
for char in set(chars): # list去重
star = char.count("★")
name = re.split(r"[()]", char)[-2].strip()
name = re.split("[ ]",char)[-1]
up_chars.append(
UpChar(name=name, star=star, limited=False, zoom=70)
)
elif "支援卡" in head.text:
card_img = head.find_next("a", {"class": "image"}).find("img")[
"src"
]
lines = str(head.find_next("p").text).split("\n")
up_chars_name.append(name)
elif "支援卡" in head.find("./img").tail:
card_img = content.xpath("./center")[2].find("./a/img").get("src")
lines = content.xpath("./p")[3].xpath("string(.)").split("\n")
cards = [
line
for line in lines
if "R" in line and "(" in line and ")" in line
if "R" in line and "[" in line and "]" in line
]
for card in cards:
for card in set(cards):
star = 3 if "SSR" in card else 2 if "SR" in card else 1
name = re.split(r"[()]", card)[-2].strip()
name = re.split("[ ]",card)[-1]
up_cards.append(
UpChar(name=name, star=star, limited=False, zoom=70)
)
up_cards_name.append(name)
if start_time and end_time:
if start_time <= datetime.now() <= end_time:
self.UP_CHAR = UpEvent(
@@ -400,6 +399,7 @@ class PrettyHandle(BaseHandle[PrettyData]):
start_time=start_time,
end_time=end_time,
up_char=up_chars,
up_name=",".join(up_chars_name),
)
self.UP_CARD = UpEvent(
title=title,
@@ -407,6 +407,7 @@ class PrettyHandle(BaseHandle[PrettyData]):
start_time=start_time,
end_time=end_time,
up_char=up_cards,
up_name=",".join(up_cards_name),
)
self.dump_up_char()
logger.info(f"成功获取{self.game_name_cn}当前up信息...当前up池: {title}")
@@ -418,9 +419,8 @@ class PrettyHandle(BaseHandle[PrettyData]):
self.load_up_char()
if self.UP_CHAR and self.UP_CARD:
return Message(
Message.template("重载成功!\n当前UP池子:{}{:image}{:image}").format(
self.UP_CHAR.title,
self.UP_CHAR.pool_img,
self.UP_CARD.pool_img,
Message.template("重载成功!\n当前UP池子:{}\n当前支援卡池子:{}").format(
self.UP_CHAR.up_name,
self.UP_CARD.up_name,
)
)
+1 -1
View File
@@ -67,7 +67,7 @@ async def _():
gl = [g["group_id"] for g in gl]
msg_list, code = await get_epic_free(bot, "Group")
for g in gl:
if await group_manager.check_group_task_status(g, "epic_free_game"):
if group_manager.check_group_task_status(g, "epic_free_game"):
try:
if msg_list and code == 200:
await bot.send_group_forward_msg(group_id=g, messages=msg_list)
+38 -19
View File
@@ -7,7 +7,7 @@ from utils.http_utils import AsyncHttpx
# 获取所有 Epic Game Store 促销游戏
# 方法参考:RSSHub /epicgames 路由
# https://github.com/DIYgod/RSSHub/blob/master/lib/routes/epicgames/index.js
# https://github.com/DIYgod/RSSHub/blob/master/lib/v2/epicgames/index.js
async def get_epic_game():
epic_url = "https://store-site-backend-static-ipv4.ak.epicgames.com/freeGamesPromotions?locale=zh-CN&country=CN&allowCountries=CN"
headers = {
@@ -39,7 +39,7 @@ async def get_epic_free(bot: Bot, type_event: str):
game_corp = game["seller"]["name"]
game_price = game["price"]["totalPrice"]["fmtPrice"]["originalPrice"]
# 赋初值以避免 local variable referenced before assignment
game_dev, game_pub, game_thumbnail = (None, None, None)
game_thumbnail, game_dev, game_pub = None, game_corp, game_corp
try:
game_promotions = game["promotions"]["promotionalOffers"]
upcoming_promotions = game["promotions"]["upcomingPromotionalOffers"]
@@ -95,25 +95,44 @@ async def get_epic_free(bot: Bot, type_event: str):
game_dev = pair["value"]
if pair["key"] == "publisherName":
game_pub = pair["value"]
# 如 game['customAttributes'] 未找到则均使用 game_corp 值
game_dev = game_dev if game_dev is not None else game_corp
game_pub = game_pub if game_pub is not None else game_corp
game_desp = game["description"]
end_date_iso = game["promotions"]["promotionalOffers"][0][
"promotionalOffers"
][0]["endDate"][:-1]
end_date = datetime.fromisoformat(end_date_iso).strftime(
"%b.%d %H:%M"
)
try:
end_date_iso = game["promotions"]["promotionalOffers"][0][
"promotionalOffers"
][0]["endDate"][:-1]
end_date = datetime.fromisoformat(end_date_iso).strftime(
"%b.%d %H:%M"
)
except IndexError:
end_date = '未知'
# API 返回不包含游戏商店 URL,此处自行拼接,可能出现少数游戏 404 请反馈
game_url_part = (
(game["productSlug"].replace("/home", ""))
if ("/home" in game["productSlug"])
else game["productSlug"]
)
game_url = "https://www.epicgames.com/store/zh-CN/p/{}".format(
game_url_part
)
if game.get("productSlug"):
game_url = "https://store.epicgames.com/zh-CN/p/{}".format(
game["productSlug"].replace("/home", "")
)
elif game.get("url"):
game_url = game["url"]
else:
slugs = (
[
x["pageSlug"]
for x in game.get("offerMappings", [])
if x.get("pageType") == "productHome"
]
+ [
x["pageSlug"]
for x in game.get("catalogNs", {}).get("mappings", [])
if x.get("pageType") == "productHome"
]
+ [
x["value"]
for x in game.get("customAttributes", [])
if "productSlug" in x.get("key")
]
)
game_url = "https://store.epicgames.com/zh-CN{}".format(
f"/p/{slugs[0]}" if len(slugs) else ""
)
if type_event == "Group":
_message = "[CQ:image,file={}]\n\nFREE now :: {} ({})\n{}\n此游戏由 {} 开发、{} 发行,将在 UTC 时间 {} 结束免费游玩,戳链接速度加入你的游戏库吧~\n{}\n".format(
game_thumbnail,
+2 -2
View File
@@ -1,6 +1,6 @@
from utils.image_utils import get_img_hash
from nonebot.adapters.onebot.v11.permission import GROUP
from configs.path_config import TEMP_PATH
from utils.image_utils import get_img_hash
import random
from utils.message_builder import image
from nonebot import on_message
@@ -90,7 +90,7 @@ async def _(event: GroupMessageEvent):
if not img and not msg:
return
if img:
img_hash = await get_fudu_img_hash(img[0], event.group_id)
img_hash = await get_fudu_img_hash(img[0],event.group_id)
else:
img_hash = ""
add_msg = msg + "|-|" + img_hash
+1 -1
View File
@@ -71,5 +71,5 @@ async def _():
if alc_img:
mes = "[[_task|genshin_alc]]" + alc_img + "\n ※ 黄历数据来源于 genshin.pub"
for gid in gl:
if await group_manager.check_group_task_status(gid, "genshin_alc"):
if group_manager.check_group_task_status(gid, "genshin_alc"):
await bot.send_group_msg(group_id=int(gid), message="" + mes)
@@ -47,7 +47,7 @@ __plugin_block_limit__ = {
qr = on_command("原神资源查询", aliases={"原神资源查找"}, priority=5, block=True)
qr_lst = on_command("原神资源列表", priority=5, block=True)
rex_qr = on_regex(".*?(在哪|在哪里|哪有|哪里有).*?", priority=5, block=True)
update_info = on_command("更新原神资源信息", permission=SUPERUSER, priority=1, block=True)
update_info = on_regex("^更新原神资源信息$", permission=SUPERUSER, priority=1, block=True)
@qr.handle()
+2 -2
View File
@@ -1,4 +1,4 @@
from configs.path_config import IMAGE_PATH, TEXT_PATH
from configs.path_config import IMAGE_PATH, TEXT_PATH, TEMP_PATH
from utils.image_utils import BuildImage
from typing import Tuple, List
from math import sqrt, pow
@@ -118,7 +118,7 @@ class Map:
self._generate_best_route()
self.map.crop((min_width, min_height, max_width, max_height))
rand = random.randint(1, 10000)
self.map.save(f"{IMAGE_PATH}/temp/genshin_map_{rand}.png")
self.map.save(f"{TEMP_PATH}/genshin_map_{rand}.png")
return rand
# 资源数量
@@ -1,6 +1,6 @@
from pathlib import Path
from typing import Tuple, Optional, List
from configs.path_config import IMAGE_PATH, TEXT_PATH
from configs.path_config import IMAGE_PATH, TEXT_PATH, TEMP_PATH
from utils.message_builder import image
from services.log import logger
from utils.image_utils import BuildImage
@@ -57,7 +57,7 @@ async def query_resource(resource_name: str) -> str:
None, map_.generate_resource_icon_in_map
)
return (
f"{image(f'genshin_map_{rand}.png', 'temp')}"
f"{image(TEMP_PATH / f'genshin_map_{rand}.png')}"
f"\n\n※ {resource_name} 一共找到 {count} 个位置点\n※ 数据来源于米游社wiki"
)
@@ -172,28 +172,29 @@ async def download_map_init(semaphore: Semaphore, flag: bool = False):
if data["message"] == "OK":
data = json.loads(data["data"]["info"]["detail"])
CENTER_POINT = (data["origin"][0], data["origin"][1])
if not _map.exists():
if not _map.exists() or flag:
data = data["slices"]
idx = 0
for _map_data in data[0]:
map_url = _map_data["url"]
await download_image(
map_url,
map_path / f"{idx}.png",
semaphore,
force_flag=flag,
)
BuildImage(
0, 0, background=f"{map_path}/{idx}.png", ratio=MAP_RATIO
).save()
idx += 1
_w, h = BuildImage(0, 0, background=f"{map_path}/0.png").size
w = _w * len(os.listdir(map_path))
map_file = BuildImage(w, h, _w, h, ratio=MAP_RATIO)
w_len = len(data[0])
h_len = len(data)
for _map_data in data:
for _map in _map_data:
map_url = _map["url"]
await download_image(
map_url,
map_path / f"{idx}.png",
semaphore,
force_flag=flag,
)
BuildImage(
0, 0, background=f"{map_path}/{idx}.png", ratio=MAP_RATIO
).save()
idx += 1
w, h = BuildImage(0, 0, background=f"{map_path}/0.png").size
map_file = BuildImage(w * w_len, h * h_len, w, h, ratio=MAP_RATIO)
for i in range(idx):
map_file.paste(
BuildImage(0, 0, background=f"{map_path}/{i}.png")
)
img = BuildImage(0, 0, background=f"{map_path}/{i}.png")
await map_file.apaste(img)
map_file.save(f"{map_path}/map.png")
else:
logger.warning(f'获取原神地图失败 msg: {data["message"]}')
@@ -202,7 +203,7 @@ async def download_map_init(semaphore: Semaphore, flag: bool = False):
except (TimeoutError, ConnectTimeout):
logger.warning("下载原神地图数据超时....")
except Exception as e:
logger.error(f"下载原神地图数据超时 {type(e)}:{e}")
logger.error(f"下载原神地图数据失败 {type(e)}:{e}")
# 下载资源类型数据
@@ -19,6 +19,9 @@ class Genshin(db.Model):
resin_remind = db.Column(db.Boolean(), default=False) # 树脂提醒
resin_recovery_time = db.Column(db.DateTime(timezone=True)) # 满树脂提醒日期
bind_group = db.Column(db.BigInteger())
login_ticket = db.Column(db.String(), default="")
stuid = db.Column(db.String(), default="")
stoken = db.Column(db.String(), default="")
_idx1 = db.Index("genshin_uid_idx1", "user_qq", "uid", unique=True)
@@ -386,3 +389,96 @@ class Genshin(db.Model):
for u in await cls.query.with_for_update().gino.all():
if u.today_query_uid:
await u.update(today_query_uid="").apply()
@classmethod
async def set_stuid(cls, uid: int, stuid: str) -> bool:
"""
说明:
设置stuid
参数:
:param uid: 原神uid
:param stuid: stuid
"""
query = cls.query.where(cls.uid == uid).with_for_update()
user = await query.gino.first()
if user:
await user.update(stuid=stuid).apply()
return True
return False
@classmethod
async def set_stoken(cls, uid: int, stoken: str) -> bool:
"""
说明:
设置stoken
参数:
:param uid: 原神uid
:param stoken: stoken
"""
query = cls.query.where(cls.uid == uid).with_for_update()
user = await query.gino.first()
if user:
await user.update(stoken=stoken).apply()
return True
return False
@classmethod
async def set_login_ticket(cls, uid: int, login_ticket: str) -> bool:
"""
说明:
设置login_ticket
参数:
:param uid: 原神uid
:param login_ticket: login_ticket
"""
query = cls.query.where(cls.uid == uid).with_for_update()
user = await query.gino.first()
if user:
await user.update(login_ticket=login_ticket).apply()
return True
return False
# 获取login_ticket
@classmethod
async def get_login_ticket(cls, uid: int) -> Optional[str]:
"""
说明:
获取login_ticket
参数:
:param uid: 原神uid
"""
query = cls.query.where(cls.uid == uid)
user = await query.gino.first()
if user:
return user.login_ticket
return None
# 获取stuid
@classmethod
async def get_stuid(cls, uid: int) -> Optional[str]:
"""
说明:
获取stuid
参数:
:param uid: 原神uid
"""
query = cls.query.where(cls.uid == uid)
user = await query.gino.first()
if user:
return user.stuid
return None
# 获取stoken
@classmethod
async def get_stoken(cls, uid: int) -> Optional[str]:
"""
说明:
获取stoken
参数:
:param uid: 原神uid
"""
query = cls.query.where(cls.uid == uid)
user = await query.gino.first()
if user:
return user.stoken
return None
+42 -3
View File
@@ -5,6 +5,8 @@ from .._models import Genshin
from services.log import logger
from nonebot.params import CommandArg, Command
from typing import Tuple
from utils.http_utils import AsyncHttpx
import json
__zx_plugin_name__ = "原神绑定"
@@ -39,6 +41,10 @@ bind = on_command(
unbind = on_command("原神解绑", priority=5, block=True)
web_Api = "https://api-takumi.mihoyo.com"
bbs_Cookie_url = "https://webapi.account.mihoyo.com/Api/cookie_accountinfo_by_loginticket?login_ticket={}"
bbs_Cookie_url2 = web_Api + "/auth/api/getMultiTokenByLoginTicket?login_ticket={}&token_types=3&uid={}"
@bind.handle()
async def _(event: MessageEvent, cmd: Tuple[str, ...] = Command(), arg: Message = CommandArg()):
@@ -64,9 +70,13 @@ async def _(event: MessageEvent, cmd: Tuple[str, ...] = Command(), arg: Message
_x = f"已成功为uid:{uid} 设置米游社id:{msg}"
else:
if not msg:
await bind.finish(
"私聊发送!!\n打开 https://bbs.mihoyo.com/ys/\n登录后按F12点击控制台输入document.cookie复制输出的内容即可"
)
await bind.finish("""私聊发送!!
1.以无痕模式打开浏览器(Edge请新建InPrivate窗口)
2.打开http://bbs.mihoyo.com/ys/并登陆
3.登陆后打开http://user.mihoyo.com/进行登陆
4.按下F12,打开控制台,输入以下命令:
var cookie=document.cookie;var ask=confirm('Cookie:'+cookie+'\\n\\nDo you want to copy the cookie to the clipboard?');if(ask==true){copy(cookie);msg=cookie}else{msg='Cancel'}
5.私聊发送:原神绑定cookie 刚刚复制的cookie""")
if isinstance(event, GroupMessageEvent):
await bind.finish("请立即撤回你的消息并私聊发送!")
uid = await Genshin.get_user_uid(event.user_id)
@@ -77,6 +87,35 @@ async def _(event: MessageEvent, cmd: Tuple[str, ...] = Command(), arg: Message
if msg.endswith('"') or msg.endswith("'"):
msg = msg[:-1]
await Genshin.set_cookie(uid, msg)
cookie = msg
# 用: 代替=, ,代替;
cookie = '{"' + cookie.replace('=', '": "').replace("; ", '","') + '"}'
print(cookie)
cookie_json = json.loads(cookie)
print(cookie_json)
if 'login_ticket' not in cookie_json:
await bind.finish("请发送正确完整的cookie!")
login_ticket = cookie_json['login_ticket']
# try:
res = await AsyncHttpx.get(url=bbs_Cookie_url.format(login_ticket))
res.encoding = "utf-8"
data = json.loads(res.text)
print(data)
if "成功" in data["data"]["msg"]:
stuid = str(data["data"]["cookie_info"]["account_id"])
res = await AsyncHttpx.get(url=bbs_Cookie_url2.format(
login_ticket, stuid))
res.encoding = "utf-8"
data = json.loads(res.text)
stoken = data["data"]["list"][0]["token"]
# await Genshin.set_cookie(uid, cookie)
await Genshin.set_stoken(uid, stoken)
await Genshin.set_stuid(uid, stuid)
await Genshin.set_login_ticket(uid, login_ticket)
# except Exception as e:
# await bind.finish("获取登陆信息失败,请检查cookie是否正确或更新cookie")
elif data["data"]["msg"] == "登录信息已失效,请重新登录":
await bind.finish("登录信息失效,请重新获取最新cookie进行绑定")
_x = f"已成功为uid:{uid} 设置cookie"
if isinstance(event, GroupMessageEvent):
await Genshin.set_bind_group(uid, event.group_id)
@@ -1,4 +1,5 @@
from .data_source import get_sign_reward_list, genshin_sign
from ..mihoyobbs_sign import mihoyobbs_sign
from nonebot.adapters.onebot.v11 import MessageEvent, GroupMessageEvent
from nonebot import on_command
from services.log import logger
@@ -14,15 +15,15 @@ __plugin_usage__ = """
usage:
米游社原神签到,需要uid以及cookie
且在第二天自动排序签到时间
# 不听,就要手动签到!(使用命令 “原神我硬签 or 米游社我硬签”
# 不听,就要手动签到!(使用命令 “原神我硬签
指令:
开/关原神自动签到
原神我硬签
""".strip()
__plugin_des__ = "原神懒人签到"
__plugin_cmd__ = ["开启/关闭原神自动签到", "原神我硬签"]
__plugin_cmd__ = ["开启/关闭原神自动签到", "原神我硬签", "查看我的cookie"]
__plugin_type__ = ("原神相关",)
__plugin_version__ = 0.1
__plugin_version__ = 0.2
__plugin_author__ = "HibiKier"
__plugin_settings__ = {
"level": 5,
@@ -33,7 +34,7 @@ __plugin_settings__ = {
genshin_matcher = on_command(
"开原神自动签到", aliases={"关原神自动签到", "原神我硬签"}, priority=5, block=True
"开原神自动签到", aliases={"关原神自动签到", "原神我硬签", "查看我的cookie"}, priority=5, block=True
)
@@ -41,17 +42,25 @@ genshin_matcher = on_command(
async def _(event: MessageEvent, cmd: Tuple[str, ...] = Command()):
cmd = cmd[0]
uid = await Genshin.get_user_uid(event.user_id)
if cmd == "查看我的cookie":
my_cookie = await Genshin.get_user_cookie(uid, True)
if isinstance(event, GroupMessageEvent):
await genshin_matcher.finish("请私聊查看您的cookie!")
await genshin_matcher.finish("您的cookie为" + my_cookie)
if not uid or not await Genshin.get_user_cookie(uid, True):
await genshin_matcher.finish("请先绑定uid和cookie!")
if "account_id" not in await Genshin.get_user_cookie(uid, True):
await genshin_matcher.finish("请更新cookie!")
# if "account_id" not in await Genshin.get_user_cookie(uid, True):
# await genshin_matcher.finish("请更新cookie!")
if cmd == "原神我硬签":
try:
await genshin_matcher.send("正在进行签到...", at_sender=True)
msg = await genshin_sign(uid)
return_data = await mihoyobbs_sign(event.user_id)
logger.info(
f"(USER {event.user_id}, "
f"GROUP {event.group_id if isinstance(event, GroupMessageEvent) else 'private'}) UID:{uid} 原神签到"
)
logger.info(msg)
# 硬签,移除定时任务
try:
for i in range(3):
@@ -63,7 +72,7 @@ async def _(event: MessageEvent, cmd: Tuple[str, ...] = Command()):
await u.clear_sign_time(uid)
next_date = await Genshin.random_sign_time(uid)
add_job(event.user_id, uid, next_date)
msg += f"因开启自动签到\n下一次签到时间为:{next_date.replace(microsecond=0)}"
msg += f"\n{return_data}\n因开启自动签到\n下一次签到时间为:{next_date.replace(microsecond=0)}"
except Exception as e:
msg = "原神签到失败..请尝试检查cookie或报告至管理员!"
logger.info(
@@ -1,9 +1,14 @@
from utils.http_utils import AsyncHttpx
from configs.config import Config
from services.log import logger
from .._utils import random_hex, get_old_ds
from ..mihoyobbs_sign.setting import *
from .._models import Genshin
from typing import Optional, Dict
import hashlib
import random
import string
import uuid
import time
async def genshin_sign(uid: int) -> Optional[str]:
@@ -16,22 +21,62 @@ async def genshin_sign(uid: int) -> Optional[str]:
return "签到失败..."
status = data["message"]
if status == "OK":
sign_info = await _get_sign_info(uid)
if sign_info:
sign_info = sign_info["data"]
sign_list = await get_sign_reward_list()
get_reward = sign_list["data"]["awards"][
int(sign_info["total_sign_day"]) - 1
]["name"]
reward_num = sign_list["data"]["awards"][
int(sign_info["total_sign_day"]) - 1
]["cnt"]
get_im = f"本次签到获得:{get_reward}x{reward_num}"
if status == "OK" and sign_info["is_sign"]:
return f"\n原神签到成功!\n{get_im}\n本月漏签次数:{sign_info['sign_cnt_missed']}"
try:
sign_info = await _get_sign_info(uid)
if sign_info:
sign_info = sign_info["data"]
sign_list = await get_sign_reward_list()
get_reward = sign_list["data"]["awards"][
int(sign_info["total_sign_day"]) - 1
]["name"]
reward_num = sign_list["data"]["awards"][
int(sign_info["total_sign_day"]) - 1
]["cnt"]
get_im = f"本次签到获得:{get_reward}x{reward_num}"
logger.info("get_im:" + get_im + "\nsign_info:" + str(sign_info))
if status == "OK" and sign_info["is_sign"]:
return f"原神签到成功!\n{get_im}\n本月漏签次数:{sign_info['sign_cnt_missed']}"
except Exception as e:
logger.error(f"原神签到发生错误 UID:{str(data)}")
return f"原神签到发生错误: {str(data)}"
else:
return status
return None
if data["data"]["risk_code"] == 375:
return "原神签到失败\n账号可能被风控,请前往米游社手动签到!"
return str(data)
# 获取请求Header里的DS 当web为true则生成网页端的DS
def get_ds(web: bool) -> str:
if web:
n = mihoyobbs_Salt_web
else:
n = mihoyobbs_Salt
i = str(timestamp())
r = random_text(6)
c = md5("salt=" + n + "&t=" + i + "&r=" + r)
return f"{i},{r},{c}"
# 时间戳
def timestamp() -> int:
return int(time.time())
def random_text(num: int) -> str:
return ''.join(random.sample(string.ascii_lowercase + string.digits, num))
def md5(text: str) -> str:
md5 = hashlib.md5()
md5.update(text.encode())
return md5.hexdigest()
# 生成一个device id
def get_device_id(cookie) -> str:
return str(uuid.uuid3(uuid.NAMESPACE_URL, cookie)).replace(
'-', '').upper()
async def _sign(uid: int, server_id: str = "cn_gf01") -> Optional[Dict[str, str]]:
@@ -43,20 +88,16 @@ async def _sign(uid: int, server_id: str = "cn_gf01") -> Optional[Dict[str, str]
if str(uid)[0] == "5":
server_id = "cn_qd01"
try:
cookie = await Genshin.get_user_cookie(uid, True)
headers['DS'] = get_ds(web=True)
headers['Referer'] = 'https://webstatic.mihoyo.com/bbs/event/signin-ys/index.html?bbs_auth_required=true' \
f'&act_id={genshin_Act_id}&utm_source=bbs&utm_medium=mys&utm_campaign=icon'
headers['Cookie'] = cookie
headers['x-rpc-device_id'] = get_device_id(cookie)
req = await AsyncHttpx.post(
url="https://api-takumi.mihoyo.com/event/bbs_sign_reward/sign",
headers={
"User_Agent": "Mozilla/5.0 (Linux; Android 10; MIX 2 Build/QKQ1.190825.002; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/83.0.4103.101 Mobile Safari/537.36 miHoYoBBS/2.3.0",
"Cookie": await Genshin.get_user_cookie(int(uid), True),
"x-rpc-device_id": random_hex(32),
"Origin": "https://webstatic.mihoyo.com",
"X_Requested_With": "com.mihoyo.hyperion",
"DS": get_old_ds(),
"x-rpc-client_type": "5",
"Referer": "https://webstatic.mihoyo.com/bbs/event/signin-ys/index.html?bbs_auth_required=true&act_id=e202009291139501&utm_source=bbs&utm_medium=mys&utm_campaign=icon",
"x-rpc-app_version": "2.3.0",
},
json={"act_id": "e202009291139501", "uid": uid, "region": server_id},
url=genshin_Signurl,
headers=headers,
json={"act_id": genshin_Act_id, "uid": uid, "region": server_id},
)
return req.json()
except Exception as e:
@@ -1,4 +1,5 @@
from .data_source import genshin_sign
from ..mihoyobbs_sign import mihoyobbs_sign
from models.group_member_info import GroupInfoUser
from utils.message_builder import at
from services.log import logger
@@ -57,6 +58,11 @@ async def _sign(user_id: int, uid: int, count: int):
:param uid: uid
:param count: 执行次数
"""
try:
return_data = await mihoyobbs_sign(user_id)
except Exception as e:
logger.error(f"mihoyobbs_sign error:{e}")
return_data = "米游社签到失败,请尝试发送'米游社签到'进行手动签到"
if count < 3:
try:
msg = await genshin_sign(uid)
@@ -101,6 +107,7 @@ async def _sign(user_id: int, uid: int, count: int):
bot = get_bot()
if bot:
if user_id in [x["user_id"] for x in await bot.get_friend_list()]:
await bot.send_private_msg(user_id=user_id, message=return_data)
await bot.send_private_msg(user_id=user_id, message=msg)
else:
if not (group_id := await Genshin.get_bind_group(uid)):
@@ -0,0 +1,81 @@
from nonebot.adapters.onebot.v11 import MessageEvent
from nonebot import on_command
from services.log import logger
# from .init_task import add_job, scheduler, _sign
# from apscheduler.jobstores.base import JobLookupError
from .._models import Genshin
from nonebot.params import Command
from typing import Tuple
from .mihoyobbs import *
__zx_plugin_name__ = "米游社自动签到"
__plugin_usage__ = """
usage:
发送'米游社签到'或绑定原神自动签到
即可手动/自动进行米游社签到
(若启用了原神自动签到会在签到原神同时完成米游币领取)
--> 每天白嫖90-110米游币不香吗
注:需要重新绑定原神cookie!!!
遇到问题请提issue或@作者
""".strip()
__plugin_des__ = "米游社自动签到任务"
__plugin_cmd__ = ["米游社签到", "米游社我硬签"]
__plugin_type__ = ("原神相关",)
__plugin_version__ = 0.1
__plugin_author__ = "HDU_Nbsp"
__plugin_settings__ = {
"level": 5,
"default_status": True,
"limit_superuser": False,
"cmd": ["原神签到"],
}
mihoyobbs_matcher = on_command(
"米游社签到", aliases={"米游社我硬签"}, priority=5, block=True
)
@mihoyobbs_matcher.handle()
async def _(event: MessageEvent, cmd: Tuple[str, ...] = Command()):
await mihoyobbs_matcher.send("提交米游社签到申请", at_sender=True)
return_data = await mihoyobbs_sign(event.user_id)
if return_data:
await mihoyobbs_matcher.finish(return_data, at_sender=True)
else:
await mihoyobbs_matcher.finish("米游社签到失败,请查看控制台输出", at_sender=True)
async def mihoyobbs_sign(user_id):
uid = await Genshin.get_user_uid(user_id)
if not uid or not await Genshin.get_user_cookie(uid, True):
await mihoyobbs_matcher.finish("请先绑定uid和cookie!", at_sender=True)
stuid = await Genshin.get_stuid(uid)
stoken = await Genshin.get_stoken(uid)
cookie = await Genshin.get_user_cookie(uid)
bbs = mihoyobbs.Mihoyobbs(stuid=stuid, stoken=stoken, cookie=cookie)
await bbs.init()
return_data = ""
if bbs.Task_do["bbs_Sign"] and bbs.Task_do["bbs_Read_posts"] and bbs.Task_do["bbs_Like_posts"] and \
bbs.Task_do["bbs_Share"]:
return_data += f"今天的米游社签到任务已经全部完成了!\n" \
f"一共获得{mihoyobbs.today_have_get_coins}个米游币\n目前有{mihoyobbs.Have_coins}个米游币"
logger.info(f"今天已经全部完成了!一共获得{mihoyobbs.today_have_get_coins}个米游币,目前有{mihoyobbs.Have_coins}个米游币")
else:
i = 0
print("开始签到")
print(mihoyobbs.today_have_get_coins)
while mihoyobbs.today_get_coins != 0 and i < 3:
# if i > 0:
await bbs.refresh_list()
await bbs.signing()
await bbs.read_posts()
await bbs.like_posts()
await bbs.share_post()
await bbs.get_tasks_list()
i += 1
return_data += "\n" + f"今天已经获得{mihoyobbs.today_have_get_coins}个米游币\n" \
f"还能获得{mihoyobbs.today_get_coins}个米游币\n目前有{mihoyobbs.Have_coins}个米游币"
logger.info(f"今天已经获得{mihoyobbs.today_have_get_coins}个米游币,"
f"还能获得{mihoyobbs.today_get_coins}个米游币,目前有{mihoyobbs.Have_coins}个米游币")
return return_data
@@ -0,0 +1,6 @@
class CookieError(Exception):
def __init__(self, info):
self.info = info
def __str__(self):
return repr(self.info)
@@ -0,0 +1,193 @@
from services.log import logger
from .error import CookieError
from utils.http_utils import AsyncHttpx
from .setting import *
from .tools import *
import json
today_get_coins = 0
today_have_get_coins = 0 # 这个变量以后可能会用上,先留着了
Have_coins = 0
class Mihoyobbs:
def __init__(self, stuid: str, stoken: str, cookie: str) -> None:
self.postsList = None
self.headers = {
"DS": get_ds(web=False),
"cookie": f'stuid={stuid};stoken={stoken}',
"x-rpc-client_type": mihoyobbs_Client_type,
"x-rpc-app_version": mihoyobbs_Version,
"x-rpc-sys_version": "6.0.1",
"x-rpc-channel": "miyousheluodi",
"x-rpc-device_id": get_device_id(cookie=cookie),
"x-rpc-device_name": random_text(random.randint(1, 10)),
"x-rpc-device_model": "Mi 10",
"Referer": "https://app.mihoyo.com",
"Host": "bbs-api.mihoyo.com",
"User-Agent": "okhttp/4.8.0"
}
self.Task_do = {
"bbs_Sign": False,
"bbs_Read_posts": False,
"bbs_Read_posts_num": 3,
"bbs_Like_posts": False,
"bbs_Like_posts_num": 5,
"bbs_Share": False
}
async def init(self):
await self.get_tasks_list()
# 如果这三个任务都做了就没必要获取帖子了
if self.Task_do["bbs_Read_posts"] and self.Task_do["bbs_Like_posts"] and self.Task_do["bbs_Share"]:
pass
else:
self.postsList = await self.get_list()
async def refresh_list(self) -> None:
self.postsList = await self.get_list()
# 获取任务列表,用来判断做了哪些任务
async def get_tasks_list(self):
global today_get_coins
global today_have_get_coins
global Have_coins
logger.info("正在获取任务列表")
req = await AsyncHttpx.get(url=bbs_Tasks_list, headers=self.headers)
data = req.json()
if "err" in data["message"] or data["retcode"] == -100:
logger.error("获取任务列表失败,你的cookie可能已过期,请重新设置cookie。")
raise CookieError('Cookie expires')
else:
today_get_coins = data["data"]["can_get_points"]
today_have_get_coins = data["data"]["already_received_points"]
Have_coins = data["data"]["total_points"]
# 如果当日可获取米游币数量为0直接判断全部任务都完成了
if today_get_coins == 0:
self.Task_do["bbs_Sign"] = True
self.Task_do["bbs_Read_posts"] = True
self.Task_do["bbs_Like_posts"] = True
self.Task_do["bbs_Share"] = True
else:
# 如果第0个大于或等于62则直接判定任务没做
if data["data"]["states"][0]["mission_id"] >= 62:
logger.info(f"今天可以获得{today_get_coins}个米游币")
pass
else:
logger.info(f"还有任务未完成,今天还能获得{today_get_coins}米游币")
for i in data["data"]["states"]:
# 58是讨论区签到
if i["mission_id"] == 58:
if i["is_get_award"]:
self.Task_do["bbs_Sign"] = True
# 59是看帖子
elif i["mission_id"] == 59:
if i["is_get_award"]:
self.Task_do["bbs_Read_posts"] = True
else:
self.Task_do["bbs_Read_posts_num"] -= i["happened_times"]
# 60是给帖子点赞
elif i["mission_id"] == 60:
if i["is_get_award"]:
self.Task_do["bbs_Like_posts"] = True
else:
self.Task_do["bbs_Like_posts_num"] -= i["happened_times"]
# 61是分享帖子
elif i["mission_id"] == 61:
if i["is_get_award"]:
self.Task_do["bbs_Share"] = True
# 分享帖子,是最后一个任务,到这里了下面都是一次性任务,直接跳出循环
break
# 获取要帖子列表
async def get_list(self) -> list:
temp_list = []
logger.info("正在获取帖子列表......")
req = await AsyncHttpx.get(url=bbs_List_url.format(mihoyobbs_List_Use[0]["forumId"]),
headers=self.headers)
data = req.json()["data"]["list"]
for n in range(5):
r_l = random.choice(data)
while r_l["post"]["subject"] in str(temp_list):
r_l = random.choice(data)
temp_list.append([r_l["post"]["post_id"], r_l["post"]["subject"]])
# temp_list.append([data["data"]["list"][n]["post"]["post_id"], data["data"]["list"][n]["post"]["subject"]])
logger.info("已获取{}个帖子".format(len(temp_list)))
return temp_list
# 进行签到操作
async def signing(self):
if self.Task_do["bbs_Sign"]:
logger.info("讨论区任务已经完成过了~")
else:
logger.info("正在签到......")
header = {}
header.update(self.headers)
for i in mihoyobbs_List_Use:
header["DS"] = get_ds2("", json.dumps({"gids": i["id"]}))
req = await AsyncHttpx.post(url=bbs_Sign_url, json={"gids": i["id"]}, headers=header)
data = req.json()
if "err" not in data["message"]:
logger.info(str(i["name"] + data["message"]))
time.sleep(random.randint(2, 8))
else:
logger.error("签到失败,你的cookie可能已过期,请重新设置cookie。")
raise CookieError('Cookie expires')
# 看帖子
async def read_posts(self):
if self.Task_do["bbs_Read_posts"]:
logger.info("看帖任务已经完成过了~")
else:
logger.info("正在看帖......")
for i in range(self.Task_do["bbs_Read_posts_num"]):
req = await AsyncHttpx.get(url=bbs_Detail_url.format(self.postsList[i][0]), headers=self.headers)
data = req.json()
if data["message"] == "OK":
logger.debug("看帖:{} 成功".format(self.postsList[i][1]))
time.sleep(random.randint(2, 8))
# 点赞
async def like_posts(self):
if self.Task_do["bbs_Like_posts"]:
logger.info("点赞任务已经完成过了~")
else:
logger.info("正在点赞......")
for i in range(self.Task_do["bbs_Like_posts_num"]):
req = await AsyncHttpx.post(url=bbs_Like_url, headers=self.headers,
json={"post_id": self.postsList[i][0], "is_cancel": False})
data = req.json()
if data["message"] == "OK":
logger.debug("点赞:{} 成功".format(self.postsList[i][1]))
# 判断取消点赞是否打开
# if config.config["mihoyobbs"]["un_like"] :
# time.sleep(random.randint(2, 8))
# req = httpx.post(url=bbs_Like_url, headers=self.headers,
# json={"post_id": self.postsList[i][0], "is_cancel": True})
# data = req.json()
# if data["message"] == "OK":
# logger.debug("取消点赞:{} 成功".format(self.postsList[i][1]))
time.sleep(random.randint(2, 8))
# 分享操作
async def share_post(self):
if self.Task_do["bbs_Share"]:
logger.info("分享任务已经完成过了~")
else:
logger.info("正在执行分享任务......")
for i in range(3):
req = await AsyncHttpx.get(url=bbs_Share_url.format(self.postsList[0][0]), headers=self.headers)
data = req.json()
if data["message"] == "OK":
logger.debug("分享:{} 成功".format(self.postsList[0][1]))
logger.info("分享任务执行成功......")
break
else:
logger.debug(f"分享任务执行失败,正在执行第{i + 2}次,共3次")
time.sleep(random.randint(2, 8))
time.sleep(random.randint(2, 8))
@@ -0,0 +1,124 @@
# 米游社的Salt
mihoyobbs_Salt = "z8DRIUjNDT7IT5IZXvrUAxyupA1peND9"
mihoyobbs_Salt2 = "t0qEgfub6cvueAPgR5m9aQWWVciEer7v"
mihoyobbs_Salt_web = "9nQiU3AV0rJSIBWgdynfoGMGKaklfbM7"
# 米游社的版本
mihoyobbs_Version = "2.34.1" # Slat和Version相互对应
# 米游社的客户端类型
mihoyobbs_Client_type = "2" # 1为ios 2为安卓
mihoyobbs_Client_type_web = "5" # 4为pc web 5为mobile web
# 米游社的分区列表
mihoyobbs_List = [{
"id": "1",
"forumId": "1",
"name": "崩坏3",
"url": "https://bbs.mihoyo.com/bh3/"
}, {
"id": "2",
"forumId": "26",
"name": "原神",
"url": "https://bbs.mihoyo.com/ys/"
}, {
"id": "3",
"forumId": "30",
"name": "崩坏2",
"url": "https://bbs.mihoyo.com/bh2/"
}, {
"id": "4",
"forumId": "37",
"name": "未定事件簿",
"url": "https://bbs.mihoyo.com/wd/"
}, {
"id": "5",
"forumId": "34",
"name": "大别野",
"url": "https://bbs.mihoyo.com/dby/"
}, {
"id": "6",
"forumId": "52",
"name": "崩坏:星穹铁道",
"url": "https://bbs.mihoyo.com/sr/"
}, {
"id": "8",
"forumId": "57",
"name": "绝区零",
"url": "https://bbs.mihoyo.com/zzz/"
}]
game_id2name = {
"bh2_cn": "崩坏2",
"bh3_cn": "崩坏3",
"nxx_cn": "未定事件簿",
"hk4e_cn": "原神",
}
# Config Load之后run里面进行列表的选择
mihoyobbs_List_Use = [{
"id": "2",
"forumId": "26",
"name": "原神",
"url": "https://bbs.mihoyo.com/ys/"
},
# 不玩原神可以把签到讨论区换为大别墅
# {
# "id": "5",
# "forumId": "34",
# "name": "大别野",
# "url": "https://bbs.mihoyo.com/dby/"
# }
]
# 游戏签到的请求头
headers = {
'Accept': 'application/json, text/plain, */*',
'DS': "",
'Origin': 'https://webstatic.mihoyo.com',
'x-rpc-app_version': mihoyobbs_Version,
'User-Agent': 'Mozilla/5.0 (Linux; Android 12; Unspecified Device) AppleWebKit/537.36 (KHTML, like Gecko) '
f'Version/4.0 Chrome/103.0.5060.129 Mobile Safari/537.36 miHoYoBBS/{mihoyobbs_Version}',
'x-rpc-client_type': mihoyobbs_Client_type_web,
'Referer': '',
'Accept-Encoding': 'gzip, deflate',
'Accept-Language': 'zh-CN,en-US;q=0.8',
'X-Requested-With': 'com.mihoyo.hyperion',
"Cookie": "",
'x-rpc-device_id': ""
}
# 通用设置
bbs_Api = "https://bbs-api.mihoyo.com"
web_Api = "https://api-takumi.mihoyo.com"
account_Info_url = web_Api + "/binding/api/getUserGameRolesByCookie?game_biz="
# 米游社的API列表
bbs_Cookie_url = "https://webapi.account.mihoyo.com/Api/cookie_accountinfo_by_loginticket?login_ticket={}"
bbs_Cookie_url2 = web_Api + "/auth/api/getMultiTokenByLoginTicket?login_ticket={}&token_types=3&uid={}"
bbs_Tasks_list = bbs_Api + "/apihub/sapi/getUserMissionsState" # 获取任务列表
bbs_Sign_url = bbs_Api + "/apihub/app/api/signIn" # post
bbs_List_url = bbs_Api + "/post/api/getForumPostList?forum_id={}&is_good=false&is_hot=false&page_size=20&sort_type=1"
bbs_Detail_url = bbs_Api + "/post/api/getPostFull?post_id={}"
bbs_Share_url = bbs_Api + "/apihub/api/getShareConf?entity_id={}&entity_type=1"
bbs_Like_url = bbs_Api + "/apihub/sapi/upvotePost" # post json
# 崩坏2自动签到相关的相关设置
honkai2_Act_id = "e202203291431091"
honkai2_checkin_rewards = f'{web_Api}/event/luna/home?lang=zh-cn&act_id={honkai2_Act_id}'
honkai2_Is_signurl = web_Api + "/event/luna/info?lang=zh-cn&act_id={}&region={}&uid={}"
honkai2_Sign_url = web_Api + "/event/luna/sign"
# 崩坏3自动签到相关的设置
honkai3rd_Act_id = "e202207181446311"
honkai3rd_checkin_rewards = f'{web_Api}/event/luna/home?lang=zh-cn&act_id={honkai3rd_Act_id}'
honkai3rd_Is_signurl = web_Api + "/event/luna/info?lang=zh-cn&act_id={}&region={}&uid={}"
honkai3rd_Sign_url = web_Api + "/event/luna/sign"
# 未定事件簿自动签到相关设置
tearsofthemis_Act_id = "e202202251749321"
tearsofthemis_checkin_rewards = f'{web_Api}/event/luna/home?lang=zh-cn&act_id={tearsofthemis_Act_id}'
tearsofthemis_Is_signurl = honkai2_Is_signurl
tearsofthemis_Sign_url = honkai2_Sign_url # 和二崩完全一致
# 原神自动签到相关的设置
genshin_Act_id = "e202009291139501"
genshin_checkin_rewards = f'{web_Api}/event/bbs_sign_reward/home?act_id={genshin_Act_id}'
genshin_Is_signurl = web_Api + "/event/bbs_sign_reward/info?act_id={}&region={}&uid={}"
genshin_Signurl = web_Api + "/event/bbs_sign_reward/sign"
@@ -0,0 +1,65 @@
import uuid
import time
import random
import string
import hashlib
from .setting import *
# md5计算
def md5(text: str) -> str:
md5 = hashlib.md5()
md5.update(text.encode())
return md5.hexdigest()
# 随机文本
def random_text(num: int) -> str:
return ''.join(random.sample(string.ascii_lowercase + string.digits, num))
# 时间戳
def timestamp() -> int:
return int(time.time())
# 获取请求Header里的DS 当web为true则生成网页端的DS
def get_ds(web: bool) -> str:
if web:
n = mihoyobbs_Salt_web
else:
n = mihoyobbs_Salt
i = str(timestamp())
r = random_text(6)
c = md5("salt=" + n + "&t=" + i + "&r=" + r)
return f"{i},{r},{c}"
# 获取请求Header里的DS(版本2) 这个版本ds之前见到都是查询接口里的
def get_ds2(q: str, b: str) -> str:
n = mihoyobbs_Salt2
i = str(timestamp())
r = str(random.randint(100001, 200000))
add = f'&b={b}&q={q}'
c = md5("salt=" + n + "&t=" + i + "&r=" + r + add)
return f"{i},{r},{c}"
# 生成一个device id
def get_device_id(cookie) -> str:
return str(uuid.uuid3(uuid.NAMESPACE_URL, cookie))
# 获取签到的奖励名称
def get_item(raw_data: dict) -> str:
temp_name = raw_data["name"]
temp_cnt = raw_data["cnt"]
return f"{temp_name}x{temp_cnt}"
# 获取明天早晨0点的时间戳
def next_day() -> int:
now_time = int(time.time())
next_day_time = now_time - now_time % 86400 + time.timezone + 86400
return next_day_time
@@ -3,7 +3,6 @@ from nonebot.adapters.onebot.v11 import MessageEvent, GroupMessageEvent
from services.log import logger
from .data_source import get_user_memo, get_memo
from .._models import Genshin
from nonebot.plugin import export
__zx_plugin_name__ = "原神便笺查询"
@@ -28,10 +27,6 @@ __plugin_settings__ = {
__plugin_block_limit__ = {}
export = export()
export.get_memo = get_memo
query_memo_matcher = on_command("原神便签查询", aliases={"原神便笺查询", "yss"}, priority=5, block=True)
@@ -26,12 +26,17 @@ memo_path.mkdir(exist_ok=True, parents=True)
@driver.on_startup
async def _():
for name, url in zip(
["resin.png", "task.png", "resin_discount.png"],
[
"resin.png", "task.png", "resin_discount.png", "chengehu.png",
"zhibian.png"
],
[
"https://upload-bbs.mihoyo.com/upload/2021/09/29/8819732/54266243c7d15ba31690c8f5d63cc3c6_71491376413333325"
"20.png?x-oss-process=image//resize,s_600/quality,q_80/auto-orient,0/interlace,1/format,png",
"https://patchwiki.biligame.com/images/ys/thumb/c/cc/6k6kuj1kte6m1n7hexqfrn92z6h4yhh.png/60px-委托任务logo.png",
"https://patchwiki.biligame.com/images/ys/d/d9/t1hv6wpucbwucgkhjntmzroh90nmcdv.png",
"https://s3.bmp.ovh/imgs/2022/08/21/3a3b2e6c22e305ff.png",
"https://s3.bmp.ovh/imgs/2022/08/21/c2d7ace21e1d46cf.png",
],
):
file = memo_path / name
@@ -40,7 +45,8 @@ async def _():
logger.info(f"已下载原神便签资源 -> {file}...")
async def get_user_memo(user_id: int, uid: int, uname: str) -> Optional[Union[str, MessageSegment]]:
async def get_user_memo(user_id: int, uid: int,
uname: str) -> Optional[Union[str, MessageSegment]]:
uid = str(uid)
if uid[0] in ["1", "2"]:
server_id = "cn_gf01"
@@ -54,12 +60,16 @@ async def get_user_memo(user_id: int, uid: int, uname: str) -> Optional[Union[st
async def get_memo(uid: str, server_id: str) -> "Union[str, dict], int":
try:
req = await AsyncHttpx.get(
url=f"https://api-takumi-record.mihoyo.com/game_record/app/genshin/api/dailyNote?server={server_id}&role_id={uid}",
url=
f"https://api-takumi-record.mihoyo.com/game_record/app/genshin/api/dailyNote?server={server_id}&role_id={uid}",
headers={
"DS": get_ds(f"role_id={uid}&server={server_id}"),
"x-rpc-app_version": Config.get_config("genshin", "mhyVersion"),
"User-Agent": "Mozilla/5.0 (iPhone; CPU iPhone OS 13_2_3 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) miHoYoBBS/2.11.1",
"x-rpc-client_type": Config.get_config("genshin", "client_type"),
"x-rpc-app_version": Config.get_config("genshin",
"mhyVersion"),
"User-Agent":
"Mozilla/5.0 (iPhone; CPU iPhone OS 13_2_3 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) miHoYoBBS/2.11.1",
"x-rpc-client_type": Config.get_config("genshin",
"client_type"),
"Referer": "https://webstatic.mihoyo.com/",
"Cookie": await Genshin.get_user_cookie(int(uid))
},
@@ -75,11 +85,18 @@ async def get_memo(uid: str, server_id: str) -> "Union[str, dict], int":
return "发生了一些错误,请稍后再试", 998
def create_border(
image_name: str, content: str, notice_text: str, value: str
) -> BuildImage:
border = BuildImage(500, 100, color="#E0D9D1", font="HYWenHei-85W.ttf", font_size=20)
text_bk = BuildImage(350, 96, color="#F5F1EB", font_size=23, font="HYWenHei-85W.ttf")
def create_border(image_name: str, content: str, notice_text: str,
value: str) -> BuildImage:
border = BuildImage(500,
75,
color="#E0D9D1",
font="HYWenHei-85W.ttf",
font_size=20)
text_bk = BuildImage(350,
75,
color="#F5F1EB",
font_size=23,
font="HYWenHei-85W.ttf")
_x = 70 if image_name == "resin.png" else 50
_px = 10 if image_name == "resin.png" else 20
text_bk.paste(
@@ -88,7 +105,7 @@ def create_border(
True,
center_type="by_height",
)
text_bk.text((87, 20), content)
text_bk.text((87, 15), content)
text_bk.paste(
BuildImage(
0,
@@ -98,18 +115,19 @@ def create_border(
font="HYWenHei-85W.ttf",
font_size=17,
),
(87, 50),
(87, 45),
True,
)
font_width, _ = border.getsize(value)
border.text((350 + 76 - int(font_width / 2), 0), value, center_type="by_height")
border.text((350 + 76 - int(font_width / 2), 0),
value,
center_type="by_height")
border.paste(text_bk, (2, 0), center_type="by_height")
return border
async def parse_data_and_draw(
user_id: int, uid: str, server_id: str, uname: str
) -> Union[str, MessageSegment]:
async def parse_data_and_draw(user_id: int, uid: str, server_id: str,
uname: str) -> Union[str, MessageSegment]:
data, code = await get_memo(uid, server_id)
if code != 200:
return data
@@ -120,13 +138,11 @@ async def parse_data_and_draw(
if not role_avatar.exists():
await AsyncHttpx.download_file(x["avatar_side_icon"], role_avatar)
return await asyncio.get_event_loop().run_in_executor(
None, _parse_data_and_draw, data, user_avatar, uid, uname
)
None, _parse_data_and_draw, data, user_avatar, uid, uname)
def _parse_data_and_draw(
data: dict, user_avatar: BytesIO, uid: int, uname: str
) -> Union[str, MessageSegment]:
def _parse_data_and_draw(data: dict, user_avatar: BytesIO, uid: int,
uname: str) -> Union[str, MessageSegment]:
current_resin = data["current_resin"] # 当前树脂
max_resin = data["max_resin"] # 最大树脂
resin_recovery_time = data["resin_recovery_time"] # 树脂全部回复时间
@@ -137,17 +153,37 @@ def _parse_data_and_draw(
current_expedition_num = data["current_expedition_num"] # 当前挖矿人数
max_expedition_num = data["max_expedition_num"] # 每日挖矿最大人数
expeditions = data["expeditions"] # 挖矿详情
current_coin = data["current_home_coin"] # 当前宝钱
max_coin = data["max_home_coin"] # 最大宝钱
coin_recovery_time = data["home_coin_recovery_time"] # 宝钱全部回复时间
transformer_available = data["transformer"]["obtained"] # 参量质变仪可获取
transformer_state = data["transformer"]["recovery_time"][
"reached"] # 参量质变仪状态
transformer_recovery_time = data["transformer"]["recovery_time"][
"Day"] # 参量质变仪回复时间
transformer_recovery_hour = data["transformer"]["recovery_time"][
"Hour"] # 参量质变仪回复时间
coin_minute, coin_second = divmod(int(coin_recovery_time), 60)
coin_hour, coin_minute = divmod(coin_minute, 60)
#print(data)
minute, second = divmod(int(resin_recovery_time), 60)
hour, minute = divmod(minute, 60)
A = BuildImage(1030, 520, color="#f1e9e1", font_size=15, font="HYWenHei-85W.ttf")
A = BuildImage(1030,
570,
color="#f1e9e1",
font_size=15,
font="HYWenHei-85W.ttf")
A.text((10, 15), "原神便笺 | Create By ZhenXun", (198, 186, 177))
ava = BuildImage(100, 100, background=user_avatar)
ava.circle()
A.paste(ava, (40, 40), True)
A.paste(
BuildImage(0, 0, plain_text=uname, font_size=20, font="HYWenHei-85W.ttf"),
BuildImage(0,
0,
plain_text=uname,
font_size=20,
font="HYWenHei-85W.ttf"),
(160, 62),
True,
)
@@ -177,26 +213,52 @@ def _parse_data_and_draw(
"今日委托已全部完成" if finished_task_num == total_task_num else "今日委托完成数量不足",
f"{finished_task_num}/{total_task_num}",
)
A.paste(border, (10, 265))
A.paste(border, (10, 235))
border = create_border(
"resin_discount.png",
"值得铭记的强敌",
"本周剩余消耗减半次数",
f"{remain_resin_discount_num}/{resin_discount_num_limit}",
)
A.paste(border, (10, 375))
expeditions_border = BuildImage(
470, 430, color="#E0D9D1", font="HYWenHei-85W.ttf", font_size=20
A.paste(border, (10, 315))
border = create_border(
"chengehu.png",
"洞天财翁-洞天宝钱",
"洞天财翁已达到存储上限"
if current_coin == max_coin else f"{coin_hour}小时{coin_minute}分钟后存满",
f"{current_coin}/{max_coin}",
)
expeditions_text = BuildImage(
466, 426, color="#F5F1EB", font_size=23, font="HYWenHei-85W.ttf"
A.paste(border, (10, 395))
border = create_border(
"zhibian.png",
"参量质变仪",
"不存在" if not transformer_available else
"已准备完成 " if transformer_state else f"{transformer_recovery_hour}小时后可使用" if not transformer_recovery_time else f"{transformer_recovery_time}天后可使用",
"不存在" if not transformer_available else
"可使用" if transformer_state else "冷却中",
)
A.paste(border, (10, 475))
expeditions_border = BuildImage(470,
510,
color="#E0D9D1",
font="HYWenHei-85W.ttf",
font_size=20)
expeditions_text = BuildImage(466,
506,
color="#F5F1EB",
font_size=23,
font="HYWenHei-85W.ttf")
expeditions_text.text(
(5, 5), f"探索派遣限制{current_expedition_num}/{max_expedition_num}", (100, 100, 98)
)
(5, 5), f"探索派遣限制{current_expedition_num}/{max_expedition_num}",
(100, 100, 98))
h = 45
for x in expeditions:
_bk = BuildImage(400, 66, color="#ECE3D8", font="HYWenHei-85W.ttf", font_size=21)
_bk = BuildImage(400,
82,
color="#ECE3D8",
font="HYWenHei-85W.ttf",
font_size=21)
file_name = x["avatar_side_icon"].split("_")[-1]
role_avatar = memo_path / "role_avatar" / file_name
_ava_img = BuildImage(75, 75, background=role_avatar)
@@ -227,7 +289,7 @@ def _parse_data_and_draw(
_bk.circle_corner(20)
expeditions_text.paste(_bk, (25, h), True)
h += 75
h += 75 + 16
expeditions_border.paste(expeditions_text, center_type="center")
@@ -195,6 +195,7 @@ def parsed_data(
"magic_chest_number": data["stats"]["magic_chest_number"], # 奇馈宝箱
"common_chest_number": data["stats"]["common_chest_number"], # 普通宝箱
"electroculus_number": data["stats"]["electroculus_number"], # 雷神瞳已收集
"dendroculus_number": data["stats"]["dendroculus_number"], # 草神瞳已收集
}
world_data_dict = {}
for world in data["world_explorations"]:
@@ -129,7 +129,7 @@ def get_user_data_image(
"""
if mys_data:
nickname = [x["nickname"] for x in mys_data if x["game_id"] == 2][0]
region = BuildImage(1440, 450, color="#E3DBD1", font="HYWenHei-85W.ttf")
region = BuildImage(1440, 560, color="#E3DBD1", font="HYWenHei-85W.ttf")
region.circle_corner(30)
uname_img = BuildImage(
0,
@@ -163,36 +163,42 @@ def get_user_data_image(
/ random.choice(os.listdir(image_path / "chars_ava")),
)
ava_bk.paste(ava_img, (12, 16), alpha=True)
region.paste(uname_img, (int(170 + uid_img.w / 2 - uname_img.w / 2), 305), True)
region.paste(uid_img, (170, 355), True)
region.paste(ava_bk, (int(550 / 2 - ava_bk.w / 2), 40), True)
region.paste(uname_img, (int(170 + uid_img.w / 2 - uname_img.w / 2), 365), True)
region.paste(uid_img, (170, 415), True)
region.paste(ava_bk, (int(550 / 2 - ava_bk.w / 2), 100), True)
data_img = BuildImage(
800, 400, color="#E3DBD1", font="HYWenHei-85W.ttf", font_size=40
800, 510, color="#E3DBD1", font="HYWenHei-85W.ttf", font_size=40
)
_height = 0
keys = [
["活跃天数", "成就达成", "获得角色", "深境螺旋"],
["华丽宝箱", "珍贵宝箱", "精致宝箱", "普通宝箱"],
["奇馈宝箱", "风神瞳", "岩神瞳", "雷神瞳"],
["活跃天数", "成就达成", "获得角色", "解锁传送"],
["风神瞳", "岩神瞳", "雷神瞳", "草神瞳"],
["解锁秘境", "深境螺旋", "华丽宝箱", "珍贵宝箱"],
["精致宝箱", "普通宝箱", "奇馈宝箱",],
]
values = [
[
role_data["active_day_number"],
role_data["achievement_number"],
role_data["avatar_number"],
role_data["spiral_abyss"],
role_data["way_point_number"],
],
[
role_data["luxurious_chest_number"],
role_data["precious_chest_number"],
role_data["exquisite_chest_number"],
role_data["common_chest_number"],
],
[
role_data["magic_chest_number"],
role_data["anemoculus_number"],
role_data["geoculus_number"],
role_data["electroculus_number"],
role_data["dendroculus_number"],
],
[
role_data["domain_number"],
role_data["spiral_abyss"],
role_data["luxurious_chest_number"],
role_data["precious_chest_number"],
],
[
role_data["exquisite_chest_number"],
role_data["common_chest_number"],
role_data["magic_chest_number"],
],
]
for key, value in zip(keys, values):
@@ -215,7 +221,7 @@ def get_user_data_image(
)
tmp_.text((0, 0), str(v), center_type="by_width")
tmp_.paste(t_, (0, 50), True, "by_width")
_tmp_data_img.paste(tmp_, (_width if len(key) > 3 else _width + 15, 0))
_tmp_data_img.paste(tmp_, ((_width + 15) if keys.index(key) == 1 else _width, 0))
_width += 200
data_img.paste(_tmp_data_img, (0, _height))
_height += _tmp_data_img.h - 70
@@ -228,7 +234,7 @@ def get_home_data_image(home_data_list: List[Dict]) -> BuildImage:
画出家园数据
:param home_data_list: 家园列表
"""
h = 130 + 300 * 4
h = 130 + 340 * 4
region = BuildImage(
550, h, color="#E3DBD1", font="HYWenHei-85W.ttf", font_size=40
)
@@ -287,7 +293,7 @@ def get_home_data_image(home_data_list: List[Dict]) -> BuildImage:
x.paste(black_img, alpha=True, center_type="center")
x.circle_corner(50)
region.paste(x, (0, height), True, "by_width")
height += 300
height += 340
return region
@@ -297,9 +303,9 @@ def get_country_data_image(world_data_dict: Dict) -> BuildImage:
:param world_data_dict: 国家数据字典
"""
# 层岩巨渊 和 地下矿区 算一个
region = BuildImage(790, 267 * (len(world_data_dict) - 1), color="#F9F6F2")
region = BuildImage(790, 267 * ((len(world_data_dict) - 1) if world_data_dict.get("层岩巨渊·地下矿区") else len(world_data_dict)), color="#F9F6F2")
height = 0
for country in ["蒙德", "龙脊雪山", "璃月", "层岩巨渊", "稻妻", "渊下宫"]:
for country in ["蒙德", "龙脊雪山", "璃月", "层岩巨渊", "稻妻", "渊下宫", "须弥"]:
if not world_data_dict.get(country):
continue
x = BuildImage(790, 250, color="#3A4467")
@@ -311,15 +317,15 @@ def get_country_data_image(world_data_dict: Dict) -> BuildImage:
)
content_bk.paste(logo, (50, 0), True, "by_height")
if country in ["蒙德", "璃月"]:
content_bk.text((300, 40), "探索", fill=(239, 211, 114))
content_bk.text((300, 40), "蒙德探索" if country == "蒙德" else "璃月探索", fill=(239, 211, 114))
content_bk.text(
(450, 40),
(500, 40),
f"{world_data_dict[country]['exploration_percentage'] / 10}%",
fill=(255, 255, 255),
)
content_bk.text((300, 120), "声望", fill=(239, 211, 114))
content_bk.text((300, 120), "蒙德声望" if country == "蒙德" else "璃月声望", fill=(239, 211, 114))
content_bk.text(
(450, 120),
(500, 120),
f"Lv.{world_data_dict[country]['level']}",
fill=(255, 255, 255),
)
@@ -344,45 +350,65 @@ def get_country_data_image(world_data_dict: Dict) -> BuildImage:
fill=(255, 255, 255),
)
elif country in ["龙脊雪山"]:
content_bk.text((300, 40), "探索", fill=(239, 211, 114))
content_bk.text((300, 40), "雪山探索", fill=(239, 211, 114))
content_bk.text(
(450, 40),
(500, 40),
f"{world_data_dict[country]['exploration_percentage'] / 10}%",
fill=(255, 255, 255),
)
content_bk.text((300, 120), "供奉", fill=(239, 211, 114))
content_bk.text((300, 120), "忍冬之树", fill=(239, 211, 114))
content_bk.text(
(450, 120),
(500, 120),
f"Lv.{world_data_dict[country]['offerings'][0]['level']}",
fill=(255, 255, 255),
)
elif country in ["稻妻"]:
content_bk.text((300, 20), "探索", fill=(239, 211, 114))
content_bk.text((300, 20), "稻妻探索", fill=(239, 211, 114))
content_bk.text(
(450, 20),
(500, 20),
f"{world_data_dict[country]['exploration_percentage'] / 10}%",
fill=(255, 255, 255),
)
content_bk.text((300, 85), "声望", fill=(239, 211, 114))
content_bk.text((300, 85), "稻妻声望", fill=(239, 211, 114))
content_bk.text(
(450, 85),
(500, 85),
f"Lv.{world_data_dict[country]['level']}",
fill=(255, 255, 255),
)
content_bk.text((300, 150), "神樱", fill=(239, 211, 114))
content_bk.text((300, 150), "神樱眷顾", fill=(239, 211, 114))
content_bk.text(
(450, 150),
(500, 150),
f"Lv.{world_data_dict[country]['offerings'][0]['level']}",
fill=(255, 255, 255),
)
elif country in ["渊下宫"]:
content_bk.text((300, 0), "探索", fill=(239, 211, 114), center_type="by_height")
content_bk.text((300, 0), "渊下宫探索", fill=(239, 211, 114), center_type="by_height")
content_bk.text(
(450, 20),
(530, 20),
f"{world_data_dict[country]['exploration_percentage'] / 10}%",
fill=(255, 255, 255),
center_type="by_height",
)
elif country in ["须弥"]:
content_bk.text((300, 20), "须弥探索", fill=(239, 211, 114))
content_bk.text(
(500, 20),
f"{world_data_dict[country]['exploration_percentage'] / 10}%",
fill=(255, 255, 255),
)
content_bk.text((300, 85), "须弥声望", fill=(239, 211, 114))
content_bk.text(
(500, 85),
f"Lv.{world_data_dict[country]['level']}",
fill=(255, 255, 255),
)
content_bk.text((300, 150), "梦之树", fill=(239, 211, 114))
content_bk.text(
(500, 150),
f"Lv.{world_data_dict[country]['offerings'][0]['level']}",
fill=(255, 255, 255),
)
x.paste(tmp_bk, alpha=True, center_type="center")
x.paste(content_bk, alpha=True, center_type="center")
x.circle_corner(20)
@@ -19,7 +19,9 @@ import pytz
driver: Driver = nonebot.get_driver()
get_memo = require("query_memo").get_memo
require("query_memo")
from ..query_memo import get_memo
global_map = {}
+7 -5
View File
@@ -1,3 +1,7 @@
from io import BytesIO
import imagehash
from PIL import Image
from nonebot import on_message, on_command
from nonebot.adapters.onebot.v11 import Bot, GroupMessageEvent, Message, ActionFailed
from nonebot.adapters.onebot.v11.permission import GROUP
@@ -70,10 +74,8 @@ def save_data():
json.dump(mute_data, f, indent=4)
async def download_img_and_hash(url, group_id) -> str:
if await AsyncHttpx.download_file(url, TEMP_PATH / f"mute_{group_id}_img.jpg"):
return str(get_img_hash(TEMP_PATH / f"mute_{group_id}_img.jpg"))
return ""
async def download_img_and_hash(url) -> str:
return str(imagehash.average_hash(Image.open(BytesIO((await AsyncHttpx.get(url)).content))))
mute_dict = {}
@@ -87,7 +89,7 @@ async def _(bot: Bot, event: GroupMessageEvent):
img_list = get_message_img(event.json())
img_hash = ""
for img in img_list:
img_hash += await download_img_and_hash(img, event.group_id)
img_hash += await download_img_and_hash(img)
msg += img_hash
if not mute_data.get(group_id):
mute_data[group_id] = {
+1 -1
View File
@@ -27,7 +27,7 @@ __plugin_settings__ = {
}
one_friend = on_regex(
"^我.*?朋友.*?[想问问|说|让我问问|想问|让我问|想知道|让我帮他问问|让我帮他问|让我帮忙问|让我帮忙问问|问](.*)",
"^我.{0,4}朋友.{0,2}(?:想问问|说|让我问问|想问|让我问|想知道|让我帮他问问|让我帮他问|让我帮忙问|让我帮忙问问|问)(.{0,30})$",
priority=4,
block=True,
)
+8
View File
@@ -43,6 +43,7 @@ usage:
__plugin_superuser_usage__ = """
usage:
更新皮肤指令
重置开箱: 重置今日开箱所有次数
指令:
更新开箱图片 ?[武器箱]
更新开箱价格 ?[武器箱]
@@ -100,6 +101,13 @@ cases_matcher_group = MatcherGroup(priority=5, permission=GROUP, block=True)
k_open_case = cases_matcher_group.on_command("开箱")
reload_count = cases_matcher_group.on_command("重置开箱",permission=SUPERUSER)
@reload_count.handle()
async def _(event: GroupMessageEvent):
await update_count_daily()
@k_open_case.handle()
async def _(event: GroupMessageEvent, arg: Message = CommandArg()):
+2 -2
View File
@@ -31,7 +31,7 @@ async def open_case(user_qq: int, group: int, case_name: str = "狂牙大行动"
async with db.transaction():
user = await OpenCasesUser.ensure(user_qq, group, for_update=True)
# 一天次数上限
if user.today_open_total > int(
if user.today_open_total >= int(
Config.get_config("open_cases", "INITIAL_OPEN_CASE_COUNT")
+ int(impression)
/ Config.get_config("open_cases", "EACH_IMPRESSION_ADD_COUNT")
@@ -155,7 +155,7 @@ async def open_shilian_case(user_qq: int, group: int, case_name: str, num: int =
Config.get_config("open_cases", "INITIAL_OPEN_CASE_COUNT")
+ int(impression) / Config.get_config("open_cases", "EACH_IMPRESSION_ADD_COUNT")
)
if user.today_open_total == max_count:
if user.today_open_total >= max_count:
return _handle_is_MAX_COUNT()
if max_count - user.today_open_total < num:
return (
+10 -5
View File
@@ -1,6 +1,8 @@
from nonebot import on_message
from services.log import logger
from nonebot.adapters.onebot.v11 import GroupMessageEvent, ActionFailed
from utils.manager import group_manager
from utils.utils import get_message_json, get_local_proxy, is_number, get_message_text
from nonebot.adapters.onebot.v11.permission import GROUP
from utils.message_builder import image
@@ -34,7 +36,10 @@ Config.add_plugin_config(
)
parse_bilibili_json = on_message(priority=1, permission=GROUP, block=False)
async def plugin_on_checker(event: GroupMessageEvent) -> bool:
return group_manager.get_plugin_status("parse_bilibili_json", event.group_id)
parse_bilibili_json = on_message(priority=1, permission=GROUP, block=False, rule=plugin_on_checker)
_tmp = {}
@@ -110,11 +115,11 @@ async def _(event: GroupMessageEvent):
vd_info = await video.get_video_base_info(msg)
elif "av" in msg:
index = msg.find("av")
if len(msg[index + 2 :]) >= 9:
if len(msg[index + 2 :]) >= 1:
msg = msg[index + 2 : index + 11]
if is_number(msg):
url = f"https://www.bilibili.com/video/{msg}"
vd_info = await video.get_video_base_info(msg)
url = f"https://www.bilibili.com/video/av{msg}"
vd_info = await video.get_video_base_info('av' + msg)
elif "https://b23.tv" in msg:
url = "https://" + msg[msg.find("b23.tv"): msg.find("b23.tv") + 14]
async with aiohttp.ClientSession(
@@ -124,7 +129,7 @@ async def _(event: GroupMessageEvent):
url,
timeout=7,
) as response:
url = str(response.url).split("?")[0]
url = (str(response.url).split("?")[0]).strip("/")
bvid = url.split("/")[-1]
vd_info = await video.get_video_base_info(bvid)
if vd_info:
+1 -1
View File
@@ -50,7 +50,7 @@ headers = {
@pid_search.got("pid", prompt="需要查询的图片PID是?")
async def _g(event: MessageEvent, state: T_State, pid: str = Arg("pid")):
url = Config.get_config("hibiapi", "HIBIAPI") + "/api/pixiv/"
url = Config.get_config("hibiapi", "HIBIAPI") + "/api/pixiv/illust"
if pid in ["取消", "算了"]:
await pid_search.finish("已取消操作...")
if not is_number(pid):
+3
View File
@@ -1,4 +1,5 @@
from configs.config import Config
from utils.utils import GDict
import nonebot
@@ -60,6 +61,8 @@ Config.add_plugin_config(
default_value=True
)
GDict['run_sql'].append("ALTER TABLE omega_pixiv_illusts ADD classified Integer;")
nonebot.load_plugins("plugins/pix_gallery")
@@ -1,5 +1,4 @@
from typing import Optional, List
from datetime import datetime
from typing import Optional, List, Tuple
from services.db_context import db
@@ -12,13 +11,12 @@ class OmegaPixivIllusts(db.Model):
uid = db.Column(db.BigInteger(), nullable=False)
title = db.Column(db.String(), nullable=False)
uname = db.Column(db.String(), nullable=False)
classified = db.Column(db.Integer(), nullable=False)
nsfw_tag = db.Column(db.Integer(), nullable=False)
width = db.Column(db.Integer(), nullable=False)
height = db.Column(db.Integer(), nullable=False)
tags = db.Column(db.String(), nullable=False)
url = db.Column(db.String(), nullable=False)
created_at = db.Column(db.DateTime(timezone=True))
updated_at = db.Column(db.DateTime(timezone=True))
_idx1 = db.Index("omega_pixiv_illusts_idx1", "pid", "url", unique=True)
@@ -32,10 +30,9 @@ class OmegaPixivIllusts(db.Model):
url: str,
uid: int,
uname: str,
classified: int,
nsfw_tag: int,
tags: str,
created_at: datetime,
updated_at: datetime,
):
"""
说明:
@@ -48,10 +45,9 @@ class OmegaPixivIllusts(db.Model):
:param url: url链接
:param uid: 作者uid
:param uname: 作者名称
:param nsfw_tag: nsfw标签, 0=safe, 1=setu. 2=r18
:param classified: 标记标签, 0=未标记, 1=已人工标记或从可信已标记来源获取
:param nsfw_tag: nsfw标签,-1=未标记, 0=safe, 1=setu. 2=r18
:param tags: 相关tag
:param created_at: 创建日期
:param updated_at: 更新日期
"""
if not await cls.check_exists(pid):
await cls.create(
@@ -62,6 +58,7 @@ class OmegaPixivIllusts(db.Model):
url=url,
uid=uid,
uname=uname,
classified=classified,
nsfw_tag=nsfw_tag,
tags=tags,
)
@@ -113,7 +110,7 @@ class OmegaPixivIllusts(db.Model):
return bool(query)
@classmethod
async def get_keyword_num(cls, tags: List[str] = None) -> "int, int, int":
async def get_keyword_num(cls, tags: List[str] = None) -> Tuple[int, int, int]:
"""
说明:
获取相关关键词(keyword, tag)在图库中的数量
+2 -2
View File
@@ -149,8 +149,8 @@ async def _(bot: Bot, event: MessageEvent, arg: Message = CommandArg()):
for _ in range(num):
img_url = None
author = None
# if not all_image:
# await pix.finish("坏了...发完了,没图了...")
if not all_image:
await pix.finish("坏了...发完了,没图了...")
img = random.choice(all_image)
all_image.remove(img)
if isinstance(img, OmegaPixivIllusts):
+29 -29
View File
@@ -1,3 +1,5 @@
import re
from nonebot import on_command
from utils.utils import is_number
from nonebot.permission import SUPERUSER
@@ -11,7 +13,6 @@ import time
from services.log import logger
from pathlib import Path
from typing import List
from datetime import datetime
import asyncio
import os
@@ -144,38 +145,36 @@ async def _(arg: Message = CommandArg()):
@check_omega.handle()
async def _():
async def _tasks(line: str, all_pid: List[int], length: int, index: int):
data = line.split("VALUES", maxsplit=1)[-1].strip()
if data.startswith("("):
data = data[1:]
if data.endswith(");"):
data = data[:-2]
x = data.split(maxsplit=3)
pid = int(x[1][:-1].strip())
data = line.split("VALUES", maxsplit=1)[-1].strip()[1:-2]
num_list = re.findall(r'(\d+)', data)
pid = int(num_list[1])
uid = int(num_list[2])
id_ = 3
while num_list[id_] not in ['0', '1']:
id_ += 1
classified = int(num_list[id_])
nsfw_tag = int(num_list[id_ + 1])
width = int(num_list[id_ + 2])
height = int(num_list[id_ + 3])
str_list = re.findall(r"'(.*?)',", data)
title = str_list[0]
uname = str_list[1]
tags = str_list[2]
url = str_list[3]
if pid in all_pid:
logger.info(f"添加OmegaPixivIllusts图库数据已存在 ---> pid:{pid}")
return
uid = int(x[2][:-1].strip())
x = x[3].split(", '")
title = x[0].strip()[1:-1]
tmp = x[1].split(", ")
author = tmp[0].strip()[:-1]
nsfw_tag = int(tmp[1])
width = int(tmp[2])
height = int(tmp[3])
tags = x[2][:-1]
url = x[3][:-1]
if await OmegaPixivIllusts.add_image_data(
pid,
title,
width,
height,
url,
uid,
author,
nsfw_tag,
tags,
datetime.min,
datetime.min,
pid=pid,
title=title,
width=width,
height=height,
url=url,
uid=uid,
nsfw_tag=nsfw_tag,
tags=tags,
uname=uname,
classified=classified
):
logger.info(
f"成功添加OmegaPixivIllusts图库数据 pid:{pid} 本次预计存储 {length} 张,已更新第 {index} 张"
@@ -197,6 +196,7 @@ async def _():
for line in lines:
if "INSERT INTO" in line.upper():
index += 1
logger.info(f'line: {line} 加入更新计划')
tasks.append(
asyncio.ensure_future(_tasks(line, all_pid, length, index))
)
+2 -2
View File
@@ -1,4 +1,4 @@
from nonebot import on_command
from nonebot import on_regex
from services.log import logger
from nonebot.adapters.onebot.v11 import Bot, MessageEvent, GroupMessageEvent
from nonebot.typing import T_State
@@ -24,7 +24,7 @@ __plugin_settings__ = {
}
quotations = on_command("语录", aliases={"二次元", "二次元语录"}, priority=5, block=True)
quotations = on_regex("^(语录|二次元)$", priority=5, block=True)
url = "https://international.v1.hitokoto.cn/?c=a"
+5 -1
View File
@@ -12,6 +12,7 @@ from .._model import Setu
import asyncio
import os
import random
import re
try:
import ujson as json
@@ -22,6 +23,7 @@ except ModuleNotFoundError:
url = "https://api.lolicon.app/setu/v2"
path = "_setu"
r18_path = "_r18"
host_pattern = re.compile(r"https?://([^/]+)")
# 获取url
@@ -89,7 +91,9 @@ async def search_online_setu(
"""
ws_url = Config.get_config("pixiv", "PIXIV_NGINX_URL")
if ws_url:
url_ = url_.replace("i.pximg.net", ws_url).replace("i.pixiv.cat", ws_url)
host_match = re.match(host_pattern, url_)
host = host_match.group(1)
url_ = url_.replace(host, ws_url)
index = random.randint(1, 100000) if id_ is None else id_
path_ = IMAGE_PATH / path_ if path_ else TEMP_PATH
file_name = f"{index}_temp_setu.jpg" if path_ == TEMP_PATH else f"{index}.jpg"
+1
View File
@@ -18,6 +18,7 @@ from configs.path_config import DATA_PATH
from services.log import logger
from .utils import clear_sign_data_pic
from utils.utils import is_number
from .goods_register import driver
try:
import ujson as json
+57
View File
@@ -0,0 +1,57 @@
from models.sign_group_user import SignGroupUser
from configs.config import Config
from nonebot import Driver
from utils.decorator.shop import shop_register, NotMeetUseConditionsException
import nonebot
driver: Driver = nonebot.get_driver()
@driver.on_startup
async def _():
"""
导入内置的三个商品
"""
@shop_register(
name=("好感度双倍加持卡Ⅰ", "好感度双倍加持卡Ⅱ", "好感度双倍加持卡Ⅲ"),
price=(30, 150, 250),
des=(
"下次签到双倍好感度概率 + 10%(谁才是真命天子?)(同类商品将覆盖)",
"下次签到双倍好感度概率 + 20%(平平庸庸)(同类商品将覆盖)",
"下次签到双倍好感度概率 + 30%(金币才是真命天子!)(同类商品将覆盖)",
),
load_status=Config.get_config("shop", "IMPORT_DEFAULT_SHOP_GOODS"),
icon=("favorability_card_1.png", "favorability_card_2.png", "favorability_card_3.png"),
** {"好感度双倍加持卡Ⅰ_prob": 0.1, "好感度双倍加持卡Ⅱ_prob": 0.2, "好感度双倍加持卡Ⅲ_prob": 0.3},
)
async def sign_card(user_id: int, group_id: int, prob: float):
user = await SignGroupUser.ensure(user_id, group_id)
await user.update(add_probability=prob).apply()
@shop_register(
name="测试道具A",
price=99,
des="随便侧而出",
load_status=False,
icon="sword.png",
)
async def _(user_id: int, group_id: int):
print(user_id, group_id, '使用测试道具')
@shop_register.before_handle(name="测试道具A", load_status=False)
async def _(user_id: int, group_id: int):
print(user_id, group_id, '第一个使用前函数(before handle)')
@shop_register.before_handle(name="测试道具A", load_status=False)
async def _(user_id: int, group_id: int):
print(user_id, group_id, '第二个使用前函数(before handle)222')
raise NotMeetUseConditionsException("太笨了!") # 抛出异常,阻断使用,并返回信息
@shop_register.after_handle(name="测试道具A", load_status=False)
async def _(user_id: int, group_id: int):
print(user_id, group_id, '第一个使用后函数(after handle)')
+6 -10
View File
@@ -81,19 +81,15 @@ async def _handle_check_in(
await BagUser.add_gold(user_qq, group, gold)
await BagUser.add_property(user_qq, group, gift)
gift += ' + 1'
logger.info(
f"(USER {user.user_qq}, GROUP {user.group_id})"
f" CHECKED IN successfully. score: {user.impression:.2f} "
f"(+{impression_added:.2f}).获取金币:{gold + gift if gift == 'gold' else gold}"
)
if critx2 + add_probability > 0.97 or critx2 < specify_probability:
logger.info(
f"(USER {user.user_qq}, GROUP {user.group_id})"
f" CHECKED IN successfully. score: {user.impression:.2f} "
f"(+{impression_added * 2:.2f}).获取金币:{gold + gift if gift == 'gold' else gold}"
)
return await get_card(user, nickname, impression_added, gold, gift, True)
else:
logger.info(
f"(USER {user.user_qq}, GROUP {user.group_id})"
f" CHECKED IN successfully. score: {user.impression:.2f} "
f"(+{impression_added:.2f}).获取金币:{gold + gift if gift == 'gold' else gold}"
)
return await get_card(user, nickname, impression_added, gold, gift)
-2
View File
@@ -24,8 +24,6 @@ from io import BytesIO
import asyncio
import random
import nonebot
import time
import locale
import os
+1 -1
View File
@@ -172,7 +172,7 @@ async def generate_statistics_img(
data: dict, arg: str, name: str, plugin: str, day_index: int
):
try:
plugin = plugins2settings_manager.get_plugin_data(plugin)['cmd'][0]
plugin = plugins2settings_manager.get_plugin_data(plugin).cmd[0]
except (KeyError, IndexError):
pass
bar_graph = None
+2 -2
View File
@@ -107,8 +107,8 @@ async def _(
group_id = "total"
user_id = str(event.user_id)
plugin_name = plugins2settings_manager.get_plugin_data(module)
if plugin_name and plugin_name.get('cmd'):
plugin_name = plugin_name.get('cmd')[0]
if plugin_name and plugin_name.cmd:
plugin_name = plugin_name.cmd[0]
check_exists_key(group_id, user_id, plugin_name)
for data in [_prefix_count_dict, _prefix_user_count_dict]:
data["total_statistics"]["total"][plugin_name] += 1
+22 -20
View File
@@ -40,28 +40,30 @@ wbtop_data = []
async def _(event: MessageEvent, arg: Message = CommandArg()):
global wbtop_data
msg = arg.extract_plain_text().strip()
if not wbtop_data or not msg:
if wbtop_data:
now_time = datetime.datetime.now()
if now_time > wbtop_data["time"] + datetime.timedelta(minutes=5):
data, code = await get_wbtop(wbtop_url)
if code != 200:
await wbtop.finish(data, at_sender=True)
wbtop_data = data
else:
if wbtop_data:
now_time = datetime.datetime.now()
if now_time > wbtop_data["time"] + datetime.timedelta(minutes=5):
data, code = await get_wbtop(wbtop_url)
if code != 200:
await wbtop.finish(data, at_sender=True)
else:
wbtop_data = data
else:
data, code = await get_wbtop(wbtop_url)
if code != 200:
await wbtop.finish(data, at_sender=True)
else:
wbtop_data = data
if not msg:
img = await asyncio.get_event_loop().run_in_executor(
None, gen_wbtop_pic, wbtop_data["data"]
)
await wbtop.send(img)
logger.info(
f"(USER {event.user_id}, GROUP {event.group_id if isinstance(event, GroupMessageEvent) else 'private'})"
f" 查询微博热搜"
)
if not msg:
img = await asyncio.get_event_loop().run_in_executor(
None, gen_wbtop_pic, wbtop_data["data"]
)
await wbtop.send(img)
logger.info(
f"(USER {event.user_id}, GROUP {event.group_id if isinstance(event, GroupMessageEvent) else 'private'})"
f" 查询微博热搜"
)
if is_number(msg) and 0 < int(msg) <= 50:
url = wbtop_data["data"][int(msg) - 1]["url"]
await wbtop.send("开始截取数据...")
@@ -72,7 +74,7 @@ async def _(event: MessageEvent, arg: Message = CommandArg()):
wait_time=12
)
if img:
await wbtop.send(img)
await wbtop.finish(img)
else:
await wbtop.send("发生了一些错误.....")
await wbtop.finish("发生了一些错误.....")
+15 -20
View File
@@ -25,27 +25,22 @@ async def get_weather_of_city(city: str) -> str:
elif code == 998:
return f"{NICKNAME}没查到!!试试查火星的天气?"
else:
data_json = json.loads(
(
await AsyncHttpx.get(
f"http://wthrcdn.etouch.cn/weather_mini?city={city}"
)
).text
)
if "desc" in data_json:
if data_json["desc"] == "invilad-citykey":
return f"{NICKNAME}没查到!!试试查火星的天气?" + image("shengqi", "zhenxun")
elif data_json["desc"] == "OK":
w_type = data_json["data"]["forecast"][0]["type"]
w_max = data_json["data"]["forecast"][0]["high"][3:]
w_min = data_json["data"]["forecast"][0]["low"][3:]
fengli = data_json["data"]["forecast"][0]["fengli"][9:-3]
ganmao = data_json["data"]["ganmao"]
fengxiang = data_json["data"]["forecast"][0]["fengxiang"]
repass = f"{city}的天气是 {w_type} 天\n最高温度: {w_max}\n最低温度: {w_min}\n风力: {fengli} {fengxiang}\n{ganmao}"
return repass
data_json = (
await AsyncHttpx.get(
f"https://v0.yiketianqi.com/api?unescape=1&version=v91&appid=43656176&appsecret=I42og6Lm&ext=&cityid=&city={city[:-1]}"
)
).json()
if wh := data_json.get('data'):
w_type = wh[0]["wea_day"]
w_max = wh[0]["tem1"]
w_min = wh[0]["tem2"]
fengli = wh[0]["win_speed"]
ganmao = wh[0]["narrative"]
fengxiang = ','.join(wh[0].get('win', []))
repass = f"{city}的天气是 {w_type} 天\n最高温度: {w_max}\n最低温度: {w_min}\n风力: {fengli} {fengxiang}\n{ganmao}"
return repass
else:
return "好像出错了?再试试?"
return data_json.get("errmsg") or "好像出错了?再试试?"
def _check_exists_city(city: str) -> int:
+2 -2
View File
@@ -23,11 +23,11 @@ gConfig.add_plugin_config("web-ui", "password", None, name="web-ui", help_="前
async def _(matcher: Matcher, bot: Bot, event: MessageEvent, state: T_State):
flag = False
for module in plugins2settings_manager.keys():
if isinstance(plugins2settings_manager.get_plugin_data(module).get("cmd"), str):
if isinstance(plugins2settings_manager.get_plugin_data(module).cmd, str):
plugins2settings_manager.set_module_data(
module,
"cmd",
plugins2settings_manager.get_plugin_data(module).get("cmd").split(","),
plugins2settings_manager.get_plugin_data(module).cmd.split(","),
False
)
flag = True
+7 -4
View File
@@ -61,8 +61,8 @@ def _(type_: Optional[str], user: User = Depends(token_to_user)) -> Result:
)
data["plugin_manager"] = PluginManager(**x)
if x := plugins2settings_manager.get(model):
if x.get("cmd") and isinstance(x.get("cmd"), list):
x["cmd"] = ",".join(x["cmd"])
if x.cmd and isinstance(x.cmd, list):
x.cmd = ",".join(x.cmd)
data["plugin_settings"] = PluginSettings(**x)
if x := plugins2cd_manager.get(model):
data["cd_limit"] = CdLimit(**x)
@@ -111,6 +111,9 @@ def _(plugin: Plugin, user: User = Depends(token_to_user)) -> Result:
try:
if plugin.plugin_config:
for c in plugin.plugin_config:
if not c.value:
Config.set_config(plugin.model, c.key, None)
continue
if str(c.value).lower() in ["true", "false"] and (
c.default_value is None or isinstance(c.default_value, bool)
):
@@ -124,8 +127,8 @@ def _(plugin: Plugin, user: User = Depends(token_to_user)) -> Result:
) or isinstance(c.default_value, float):
c.value = float(c.value)
elif isinstance(c.value, str) and (
isinstance(Config.get_config(plugin.model, c.key, c.value), list)
or isinstance(c.default_value, list)
isinstance(Config.get_config(plugin.model, c.key, c.value), (list, tuple))
or isinstance(c.default_value, (list, tuple))
):
default_value = Config.get_config(plugin.model, c.key, c.value)
c.value = c.value.split(",")
-21
View File
@@ -10,25 +10,4 @@ Config.add_plugin_config(
default_value=5
)
Config.add_plugin_config(
"word_bank",
"WORD_BANK_FUZZY",
False,
help_="模糊匹配",
default_value=False
)
Config.add_plugin_config(
"word_bank",
"WORD_BANK_KEY",
True,
help_="关键字匹配",
default_value=True
)
Config.add_plugin_config(
"word_bank",
"WORD_BANK_MIX",
25,
help_="查看词条时图片内最多显示条数",
default_value=25
)
nonebot.load_plugins("plugins/word_bank")

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