Compare commits

..
101 Commits
Author SHA1 Message Date
HibiKier 71026542df Update image_utils.py 2021-10-05 15:53:58 +08:00
HibiKier e756a50d97 Update README.md 2021-10-04 22:59:00 +08:00
HibiKier 49c47c01a3 Update data_source.py 2021-10-04 22:58:11 +08:00
HibiKier e7eea2a7d9 Update __init__.py 2021-10-04 22:57:59 +08:00
HibiKier 311ae7d4e8 Update README.md 2021-10-04 20:23:31 +08:00
HibiKier 058f0885f7 fix 0.0.5.1 2021-10-04 20:22:09 +08:00
HibiKier c6eaf435f2 Update update_info.json 2021-10-04 14:31:31 +08:00
HibiKier 22676f3bfa Merge pull request #59 from AkashiCoin/main
epic
2021-10-03 21:40:12 +08:00
HibiKier fedcb37b1a Update data_source.py 2021-10-03 21:40:05 +08:00
HibiKier 54025c9cfc Update __init__.py 2021-10-03 21:39:03 +08:00
AkashiCoin 2cdf3c28b6 Update __init__.py 2021-10-03 20:35:55 +08:00
AkashiCoin ba29232c9f Update __init__.py 2021-10-03 20:17:08 +08:00
HibiKier 5fd0667061 Update README.md 2021-10-03 18:46:06 +08:00
HibiKier 2c9f99ac3c Update README.md 2021-10-03 18:45:39 +08:00
HibiKier 775b1b937b Merge pull request #58 from AkashiCoin/main
epic
2021-10-03 18:44:42 +08:00
AkashiCoin 65723aee15 Update __init__.py 2021-10-03 18:34:56 +08:00
AkashiCoin 46b55e4224 Update data_source.py 2021-10-03 18:34:31 +08:00
HibiKier 918adc4b1c Add files via upload 2021-10-03 16:07:57 +08:00
HibiKier e99341c6aa Add files via upload 2021-10-03 15:35:30 +08:00
HibiKier 4b0d2a104b Add files via upload 2021-10-03 15:34:53 +08:00
HibiKier eb9bb1b173 Update __init__.py 2021-10-03 15:26:19 +08:00
HibiKier f2b4204a23 Update README.md 2021-10-03 15:21:09 +08:00
HibiKier b63aa952c4 Update requirements.txt 2021-10-03 15:18:20 +08:00
HibiKier 7a5767717f Update README.md 2021-10-03 14:46:16 +08:00
HibiKier 22af4f2b27 Update config.py 2021-10-03 14:39:12 +08:00
HibiKier 00658cf165 update v0.0.5.0 2021-10-03 14:24:07 +08:00
HibiKier 87ed445a58 Update group_user_checkin.py 2021-09-23 11:10:15 +08:00
HibiKier 36ea174804 Merge pull request #53 from Adsicmes/main
修改最低好感度 避免0好感度问题
2021-09-21 13:46:11 +08:00
FrZ 6ba2fedba2 最低好感改0.01 2021-09-21 13:45:33 +08:00
FrZ ca89ff7761 使用OS提供的最高质量源生成随机数计算好感度 2021-09-21 13:31:29 +08:00
FrZ f39d53a865 修改最低好感度 避免0好感度问题 2021-09-21 13:18:48 +08:00
HibiKier 6864d54e27 Update config.py 2021-09-10 20:10:31 +08:00
HibiKier bdc084212f Update README.md 2021-09-10 20:09:47 +08:00
HibiKier fc2487df0e fix withdraw 2021-09-10 20:08:01 +08:00
HibiKier 264a827a2c Update README.md 2021-09-10 11:23:31 +08:00
HibiKier 401886805b Update __version__ 2021-09-09 22:51:50 +08:00
HibiKier efcdf79f57 Update setu.py 2021-09-09 22:50:14 +08:00
HibiKier 616f215075 Update data_source.py 2021-09-09 22:49:45 +08:00
HibiKier 00b674cebb Update setu.py 2021-09-09 18:16:15 +08:00
HibiKier 8ac440335e Update README.md 2021-09-09 17:17:13 +08:00
HibiKier 4b1821ce42 Update query_resource.py 2021-09-09 17:15:26 +08:00
HibiKier 3ad955abf9 Update image_utils.py 2021-09-09 17:14:33 +08:00
HibiKier 5fabb974ce Update config.py 2021-09-09 16:39:37 +08:00
HibiKier 5325bc3e32 Update init_config.py 2021-09-09 16:39:03 +08:00
HibiKier 6688c4626b Update update_info.json 2021-09-09 16:26:29 +08:00
HibiKier 2b95ab3d7e Update init_config.py 2021-09-09 16:25:42 +08:00
HibiKier 23e6d7e27e Update config.py 2021-09-09 16:25:09 +08:00
HibiKier 2760e22336 Update update_info.json 2021-09-09 16:05:48 +08:00
HibiKier d026d09f98 Update __version__ 2021-09-09 16:04:34 +08:00
HibiKier e6fd1ce791 Update README.md 2021-09-09 16:02:58 +08:00
HibiKier 342eaa9a97 Update pixiv.py 2021-09-09 16:02:12 +08:00
HibiKier dc7681357e Update omega_pixiv_illusts.py 2021-09-09 16:01:50 +08:00
HibiKier b61cb49c1a Update setu.py 2021-09-09 16:01:35 +08:00
HibiKier c7b341393d Update setu.py 2021-09-09 14:30:42 +08:00
HibiKier 1f9fa2adb9 Update README.md 2021-09-09 12:59:23 +08:00
HibiKier 133b927d9d Update __version__ 2021-09-09 10:53:37 +08:00
HibiKier 46e6b26d75 Update README.md 2021-09-09 10:51:50 +08:00
hibiki 8d0b0e5505 update code 2021-09-09 10:47:26 +08:00
HibiKier 754a0c111c Update README.md 2021-09-08 15:16:58 +08:00
HibiKier 81ef3f04e4 Update group_user_checkin.py 2021-09-08 11:05:23 +08:00
HibiKier e7c00484b3 Update README.md 2021-09-07 11:17:19 +08:00
HibiKier 065432448f Merge pull request #40 from Ailitonia/patch-1
Fix: Check info_list to avoid TypeError
2021-09-07 11:11:38 +08:00
HibiKier 5d45a9c28a Update README.md 2021-09-07 11:08:43 +08:00
Ailitonia 7db5b3b5c0 Fix: Check info_list to avoid TypeError 2021-09-07 02:08:56 +08:00
HibiKier 5eb64060dc Update data_source.py 2021-09-06 21:14:26 +08:00
HibiKier 2c4877b9f8 Update update_info.json 2021-09-06 21:10:44 +08:00
HibiKier 218dad3b86 fix bilibili_sub 2021-09-06 21:03:50 +08:00
HibiKier 974c6b106a Update update_info.json 2021-09-06 20:42:06 +08:00
HibiKier 954b281b69 Update data_source.py 2021-09-06 18:09:16 +08:00
HibiKier b1b15b63a1 Update data_source.py 2021-09-06 18:03:21 +08:00
HibiKier 49567aa958 Update __version__ 2021-09-05 13:57:27 +08:00
HibiKier a7a470066b Update update_info.json 2021-09-05 13:57:06 +08:00
HibiKier 4e3010d75e Update data_source.py 2021-09-05 13:56:29 +08:00
HibiKier cbacd726ef Update __init__.py 2021-09-05 13:54:55 +08:00
HibiKier 58a1fc1ca0 Update __version__ 2021-09-05 13:06:41 +08:00
HibiKier a704f88d75 Update update_info.json 2021-09-05 13:05:06 +08:00
HibiKier 10d0ad7d47 Update update_info.json 2021-09-05 12:15:47 +08:00
HibiKier b47a29eb67 Update __init__.py 2021-09-05 06:58:40 +08:00
HibiKier 887177913d Update hook.py 2021-09-05 03:20:03 +08:00
HibiKier b6b0dc85bf Update update_info.json 2021-09-05 02:49:16 +08:00
HibiKier 29f47878e9 Update data_source.py 2021-09-05 02:46:52 +08:00
HibiKier 440b548a4f Update __init__.py 2021-09-05 02:46:40 +08:00
HibiKier 5191953657 Update update_info.json 2021-09-05 02:32:42 +08:00
HibiKier 877ac72761 Update update_info.json 2021-09-05 02:32:24 +08:00
HibiKier 685befee6b Update update_info.json 2021-09-05 02:29:59 +08:00
HibiKier 7afb099c12 update code 2021-09-05 02:21:38 +08:00
hibiki 21a5feda43 update readme.md 2021-08-22 12:20:23 +08:00
hibiki b7b16cfd21 update send_img 2021-08-19 19:12:33 +08:00
hibiki 615f3a58e9 fix upload_img 2021-08-19 18:43:24 +08:00
hibiki f65fd73d6d update readme.md 2021-08-19 13:57:11 +08:00
hibiki 6a8eb9e67a update admin_manager 2021-08-18 10:06:06 +08:00
hibiki 8b7adf0a6a update pa 2021-08-18 09:56:53 +08:00
hibiki 973182c27c update setu 2021-08-18 02:04:26 +08:00
hibiki 0dced0bc90 update check 2021-08-18 01:47:21 +08:00
hibiki fc34cd541a code 2021-08-18 01:33:43 +08:00
hibiki 8090ebe8ce update hook 2021-08-18 01:29:36 +08:00
hibiki f87c14595f update hook 2021-08-18 00:44:03 +08:00
hibiki 39bb5a9ab9 update check_zhenxun_update 2021-08-18 00:26:11 +08:00
hibiki f754ad1c76 update code 2021-08-18 00:17:57 +08:00
hibiki 37e1a06528 update checkup 2021-08-18 00:14:55 +08:00
hibiki 7096d258db update code 2021-08-17 23:49:07 +08:00
436 changed files with 9887 additions and 5193 deletions
+132 -400
View File
@@ -2,7 +2,7 @@
![maven](https://img.shields.io/badge/python-3.8%2B-blue) ![maven](https://img.shields.io/badge/python-3.8%2B-blue)
![maven](https://img.shields.io/badge/nonebot-2.0.0-yellow) ![maven](https://img.shields.io/badge/nonebot-2.0.0-yellow)
![maven](https://img.shields.io/badge/go--cqhttp-0.9.40--fix4-red) ![maven](https://img.shields.io/badge/go--cqhttp-1.0.0-red)
# 绪山真寻Bot # 绪山真寻Bot
**** ****
@@ -11,7 +11,9 @@
用爱发电,某些功能学习借鉴了大佬们的代码,因为绪山真寻实在太可爱了因此开发了 用爱发电,某些功能学习借鉴了大佬们的代码,因为绪山真寻实在太可爱了因此开发了
绪山真寻bot,实现了一些对群友的娱乐功能和实用功能(大概)。 绪山真寻bot,实现了一些对群友的娱乐功能和实用功能(大概)。
如果该项目的图片等等侵犯猫豆腐老师权益请联系我删除! 如果该项目的图片等等侵犯猫豆腐老师权益请联系我删除!
如果希望有个地方讨论绪山真寻Bot,或者有问题或建议,可以发送issues或加入[ <strong>[是真寻酱哒](https://jq.qq.com/?_wv=1027&k=u8PgBkMZ) </strong>]
## 声明 ## 声明
此项目仅用于学习交流,请勿用于非法用途 此项目仅用于学习交流,请勿用于非法用途
@@ -29,7 +31,6 @@
- [x] 图灵AI(会把'你'等关键字替换为你的昵称),且带有 [AnimeThesaurus](https://github.com/Kyomotoi/AnimeThesaurus),够味 - [x] 图灵AI(会把'你'等关键字替换为你的昵称),且带有 [AnimeThesaurus](https://github.com/Kyomotoi/AnimeThesaurus),够味
- [x] 签到/我的签到/好感度排行/好感度总排行(影响色图概率和开箱次数,支持配置) - [x] 签到/我的签到/好感度排行/好感度总排行(影响色图概率和开箱次数,支持配置)
- [x] 发送某文件夹下的随机图片(支持自定义,默认:美图,萝莉,壁纸) - [x] 发送某文件夹下的随机图片(支持自定义,默认:美图,萝莉,壁纸)
- [x] 尝试搜索不色的图片 ↑↑
- [x] 色图(这不是基础功能嘛喂) - [x] 色图(这不是基础功能嘛喂)
- [x] coser - [x] coser
- [x] 黑白草图生成器 - [x] 黑白草图生成器
@@ -47,6 +48,7 @@
- [x] 原神资源查询 (借鉴[Genshin_Impact_bot](https://github.com/H-K-Y/Genshin_Impact_bot)插件) - [x] 原神资源查询 (借鉴[Genshin_Impact_bot](https://github.com/H-K-Y/Genshin_Impact_bot)插件)
- [x] 金币红包 - [x] 金币红包
- [x] 微博热搜 - [x] 微博热搜
- [x] B站主播/UP/番剧订阅
- [x] pil对图片的一些操作 - [x] pil对图片的一些操作
- [x] BUFF饰品底价查询(需要session) - [x] BUFF饰品底价查询(需要session)
@@ -69,7 +71,7 @@
- [x] 我的信息(只是为了看看什么时候入群) - [x] 我的信息(只是为了看看什么时候入群)
- [x] 更新信息(如果继续更新的话) - [x] 更新信息(如果继续更新的话)
- [x] go-cqhttp最新版下载和上传(不需要请删除) - [x] go-cqhttp最新版下载和上传(不需要请删除)
- [x] 撤回 (使用[nonebot-plugin-withdraw](https://github.com/MeetWq/nonebot-plugin-withdraw)插件) - [x] 撤回
- [x] 滴滴滴-(用户对超级用户发送消息) - [x] 滴滴滴-(用户对超级用户发送消息)
- [x] 金币红包/金币排行 - [x] 金币红包/金币排行
- [x] 俄罗斯轮盘/胜场排行/败场排行/欧洲人排行/慈善家排行 - [x] 俄罗斯轮盘/胜场排行/败场排行/欧洲人排行/慈善家排行
@@ -81,7 +83,7 @@
### 已实现的管理员功能 ### 已实现的管理员功能
- [x] 更新群组成员信息 - [x] 更新群组成员信息
- [x] 95%的群功能开关 (基于[nonebot_plugin_manager](https://github.com/Jigsaw111/nonebot_plugin_manager)插件修改) - [x] 95%的群功能开关
- [x] 查看群内被动技能状态 - [x] 查看群内被动技能状态
- [x] 自定义群欢迎消息(是真寻的不是管家的!) - [x] 自定义群欢迎消息(是真寻的不是管家的!)
- [x] .ban/.unban(支持设置ban时长)= 黑白名单 - [x] .ban/.unban(支持设置ban时长)= 黑白名单
@@ -89,9 +91,10 @@
- [x] 上传图片/连续上传图片 (上传图片至指定图库) - [x] 上传图片/连续上传图片 (上传图片至指定图库)
- [x] 移动图片 (同上) - [x] 移动图片 (同上)
- [x] 删除图片 (同上) - [x] 删除图片 (同上)
- [x] 群内B站订阅
### 已实现的超级用户功能 ### 已实现的超级用户功能
- [x] 添加/删除管理(是真寻的管理员权限,不是群管理员) - [x] 添加/删除权限(是真寻的管理员权限,不是群管理员)
- [x] 开启/关闭指定群的广播通知 - [x] 开启/关闭指定群的广播通知
- [x] 广播 - [x] 广播
- [x] 自检(检查系统状态) - [x] 自检(检查系统状态)
@@ -103,7 +106,6 @@
- [x] 节日红包发送 - [x] 节日红包发送
- [x] 修改群权限 - [x] 修改群权限
- [x] ban - [x] ban
- [x] 添加/删除群白名单
- [x] 更新色图 - [x] 更新色图
- [x] 更新价格/更加图片(csgo开箱) - [x] 更新价格/更加图片(csgo开箱)
- [x] 重载原神/方舟/赛马娘/坎公骑冠剑卡池 - [x] 重载原神/方舟/赛马娘/坎公骑冠剑卡池
@@ -111,8 +113,10 @@
- [x] PIX相关操作 - [x] PIX相关操作
- [x] 检查更新真寻 - [x] 检查更新真寻
- [x] 重启 - [x] 重启
- [x] 添加/删除群白名单 - [x] 添加/删除/查看群白名单
- [x] 功能开关(更多设置) - [x] 功能开关(更多设置)
- [x] 功能状态
- [x] b了
#### 超级用户的被动技能 #### 超级用户的被动技能
- [x] 邀请入群提醒(别人邀请真寻入群) - [x] 邀请入群提醒(别人邀请真寻入群)
@@ -122,7 +126,7 @@
- [x] 进群欢迎消息 - [x] 进群欢迎消息
- [x] 群早晚安 - [x] 群早晚安
- [x] 每日开箱重置提醒 - [x] 每日开箱重置提醒
- [x] b站转发解析(解析b站分享信息,支持bv,bilibili链接,b站手机端转发卡片,cv,b23.tv) - [x] b站转发解析(解析b站分享信息,支持bv,bilibili链接,b站手机端转发卡片,cv,b23.tv),且5分钟内不解析相同url
- [x] 丢人爬(爬表情包) - [x] 丢人爬(爬表情包)
- [x] epic通知(每日发送epic免费游戏链接) - [x] epic通知(每日发送epic免费游戏链接)
- [x] 原神黄历提醒 - [x] 原神黄历提醒
@@ -145,10 +149,12 @@
### 常用功能 ### 常用功能
** [*]:表示该参数可有多个 [?]:表示参数可选 **
| 功能 | 指令 | 说明 | 功能 | 指令 | 说明
| ----------------------| :--------------------------------------:| :------------------------: | ----------------------| :--------------------------------------:| :------------------------:
| 签到 | 签到/我的签到/好感度排行/好感度总榜/好感度总榜\[显示我/屏蔽我] | 普通的签到插件,可以获得好感度和金币<br>好感度影响开箱次数和涩图触发概率,金币用于购买道具,俄罗斯轮盘赌注以及金币红包<br>好感度总榜,显示所有群的群员好感度排行,可通过命令好感度总榜\[显示我/屏蔽我] 来设置是否隐藏 | 签到 | 签到/我的签到/好感度排行/好感度总榜/好感度总榜\[显示我/屏蔽我] | 普通的签到插件,可以获得好感度和金币<br>好感度影响开箱次数和涩图触发概率,金币用于购买道具,俄罗斯轮盘赌注以及金币红包<br>好感度总榜,显示所有群的群员好感度排行,可通过命令好感度总榜\[显示我/屏蔽我] 来设置是否隐藏
| 发送图片 | 美图/壁纸/萝莉 \[id](默认随机)/\[num]张图\[keyword] | 发送指定文件夹下的图片<br>示例:萝莉->发送img文件夹下luoli文件夹下的图片<br>在线搜索一些不色的图,示例:3张图米浴 | 发送图片 | 美图/壁纸/萝莉 \[id]?(默认随机)/\[num]张图\[keyword] | 发送指定文件夹下的图片<br>示例:萝莉->发送img文件夹下luoli文件夹下的图片<br>在线搜索一些不色的图,示例:3张图米浴
| 色图 | 色图/色图xx/n张色图/n张xx的色图/查色图(查询本地色图信息)/色图r【n<10】 | 色图r返回10张r18色图(仅私聊),并限制每日次数(默认5次)<br>其他示例:色图 真寻 <br>5张真寻的色图 | 色图 | 色图/色图xx/n张色图/n张xx的色图/查色图(查询本地色图信息)/色图r【n<10】 | 色图r返回10张r18色图(仅私聊),并限制每日次数(默认5次)<br>其他示例:色图 真寻 <br>5张真寻的色图
| 黑白草图 | 黑白草图/黑白图 \[文字] \[图片] | 整活生成器,示例:黑白图 我喜欢真寻 \[图片] | 黑白草图 | 黑白草图/黑白图 \[文字] \[图片] | 整活生成器,示例:黑白图 我喜欢真寻 \[图片]
| coser | coser/cos/括丝 | coser图片,说实话挺失望的,太色了 | coser | coser/cos/括丝 | coser图片,说实话挺失望的,太色了
@@ -156,7 +162,7 @@
| 戳一戳 | 戳一戳 | 随机发送钉宫语音 or 美图 or 萝莉图 or 文本 | 戳一戳 | 戳一戳 | 随机发送钉宫语音 or 美图 or 萝莉图 or 文本
| 模拟开箱 | 开箱 \[武器箱名称](默认随机)/N连开箱 \[武器箱名称](默认随机)/我的开箱/群开箱统计/我的金色 | 当不指定武器箱时默认随机,此功能需要先在/open_cases/config.py中编写指定武器箱数据,然后提前爬取价格,使用超级用户命令更新cookie后,再使用命令更新xx武器箱【注:未设置爬取频率,可能会被禁用api(请谨慎!!用小号!!)】 | 模拟开箱 | 开箱 \[武器箱名称](默认随机)/N连开箱 \[武器箱名称](默认随机)/我的开箱/群开箱统计/我的金色 | 当不指定武器箱时默认随机,此功能需要先在/open_cases/config.py中编写指定武器箱数据,然后提前爬取价格,使用超级用户命令更新cookie后,再使用命令更新xx武器箱【注:未设置爬取频率,可能会被禁用api(请谨慎!!用小号!!)】
| 鲁迅说过 | 鲁迅说过 \[文本] | 示例:鲁迅说过 真寻世界第一可爱 | 鲁迅说过 | 鲁迅说过 \[文本] | 示例:鲁迅说过 真寻世界第一可爱
| 假消息 | 假消息 \[网址] \[标题] \[内容](可选) \[图片](可选) | 构造虚假的分享消息 | 假消息 | 假消息 \[网址] \[标题] \[内容]? \[图片]? | 构造虚假的分享消息
| 商店系统 | 商店/我的金币/购买道具 \[名称或序号] \[数量](默认1)/使用道具 \[名称或序号] | 示例:<br>购买道具 1 3<br>购买道具 好感度双倍加持卡Ⅰ 3<br>使用道具 1<br>使用道具 好感度双倍加持卡Ⅰ | 商店系统 | 商店/我的金币/购买道具 \[名称或序号] \[数量](默认1)/使用道具 \[名称或序号] | 示例:<br>购买道具 1 3<br>购买道具 好感度双倍加持卡Ⅰ 3<br>使用道具 1<br>使用道具 好感度双倍加持卡Ⅰ
| 抽卡系统 | 原神/明日方舟/赛马娘/坎公骑冠剑/碧蓝航线/阴阳师/公主连结(pcr)/FGO N抽/一井 | 详细帮助请查看: [nonebot_plugin_gamedraw](https://github.com/HibiKier/nonebot_plugin_gamedraw) <br>示例:原神90抽 | 抽卡系统 | 原神/明日方舟/赛马娘/坎公骑冠剑/碧蓝航线/阴阳师/公主连结(pcr)/FGO N抽/一井 | 详细帮助请查看: [nonebot_plugin_gamedraw](https://github.com/HibiKier/nonebot_plugin_gamedraw) <br>示例:原神90抽
| 我有一个朋友 | 我有一个朋友他说/想问问/\[文本] | 会将文本中的(他,她,它)替换成 '我'<br>示例:我有一个朋友想问问他喜不喜欢真寻 | 我有一个朋友 | 我有一个朋友他说/想问问/\[文本] | 会将文本中的(他,她,它)替换成 '我'<br>示例:我有一个朋友想问问他喜不喜欢真寻
@@ -170,20 +176,20 @@
| 金币排行 | 金币排行 | 字面意思 | 金币排行 | 金币排行 | 字面意思
|网易云热评 | 到点了/12点了/网易云热评/网易云评论 | 防下塔 |网易云热评 | 到点了/12点了/网易云热评/网易云评论 | 防下塔
| 古诗 | 念诗/念首诗/来首诗 | 突然文艺起来了 | 古诗 | 念诗/念首诗/来首诗 | 突然文艺起来了
|微博热搜 | 微博热搜/微博热搜[序号] | 快捷热搜查询方式 |微博热搜 | 微博热搜/微博热搜[序号]? | 快捷热搜查询方式
| pil对图片的操作 | 修改尺寸/等比压缩/旋转图片/水平翻转/铅笔滤镜/模糊效果/锐化效果/高斯模糊/边缘检测/底色替换 | 选项较多,请直接发送 真寻图片帮助 | pil对图片的操作 | 修改尺寸/等比压缩/旋转图片/水平翻转/铅笔滤镜/模糊效果/锐化效果/高斯模糊/边缘检测/底色替换 | 选项较多,请直接发送 真寻图片帮助
| BUFF皮肤底价查询 | 查询皮肤 \[武器名称] \[皮肤名称] | 网络不友好的话会经常超时<br>示例:查询皮肤 沙漠之鹰 印花集 | BUFF皮肤底价查询 | 查询皮肤 \[武器名称] \[皮肤名称] | 网络不友好的话会经常超时<br>示例:查询皮肤 沙漠之鹰 印花集
| 天气查询 | \[城市]天气 | 非常常见的插件,第一个入门插件 | 天气查询 | \[城市]天气 | 非常常见的插件,第一个入门插件
| 疫情查询 | 疫情/查询疫情 \[城市名或省份名] | 示例:疫情杭州 | 疫情查询 | 疫情/查询疫情 \[城市名或省份名] | 示例:疫情杭州
| bt磁力搜索 | bt \[关键词] \[页数](默认第1页) | 该功能仅仅提供给私聊,因为可以搜到一些色色的东西,示例:bt钢铁侠 5 | bt磁力搜索 | bt \[关键词] \[页数]?(默认第1页) | 该功能仅仅提供给私聊,因为可以搜到一些色色的东西,示例:bt钢铁侠 5
| 上车 | 略 | 直接查看真寻帮助 上车,每日限制次数(默认5) | 上车 | 略 | 直接查看真寻帮助 上车,每日限制次数(默认5)
| 以图识番 | 识番 \[图片] | 以图搜翻,图片越清晰越完整正确率越高 | 以图识番 | 识番 \[图片] | 以图搜翻,图片越清晰越完整正确率越高
| 以图搜图 | 识图 (asc)? \[图片] | 参数asc更换搜索引擎为ascii2d,默认为saucenao | 以图搜图 | 识图 (asc)? \[图片] | 参数asc更换搜索引擎为ascii2d,默认为saucenao
| 点歌 | 点歌 \[歌名] | 网易云点歌小助手 | 点歌 | 点歌 \[歌名] | 网易云点歌小助手
| 搜番 | 搜番 \[关键字] | 群聊只返回5个结果,私聊返回20个结果 | 搜番 | 搜番 \[关键字] | 群聊只返回5个结果,私聊返回20个结果
| epic白嫖游戏通知 | epic | 通知你又到了白嫖游戏的时候,可以不玩,不能没有 | epic白嫖游戏通知 | epic | 通知你又到了白嫖游戏的时候,可以不玩,不能没有
| P站排行榜 | p站排行 \[排行类型参数](默认日榜) \[数量](可选) \[日期](可选)| 9种不同排行榜,r18类型仅可私聊,通过参数选择,查看真寻帮助p站排行<br>示例:p站排行榜 1 9 2018-4-25 | P站排行榜 | p站排行 \[排行类型参数]? \[数量]? \[日期]?| 9种不同排行榜,r18类型仅可私聊,通过参数选择,查看真寻帮助p站排行<br>示例:p站排行榜 1 9 2018-4-25
| 搜图 | 搜图 \[关键词] \[数量](可选) \[排序方式](可选) \[r18](可选)| r18仅可私聊,查看真寻帮助搜图 | 搜图 | 搜图 \[关键词] \[数量]? \[排序方式]? \[r18]?| r18仅可私聊,查看真寻帮助搜图
| 通过PID搜索图片 | p搜 [pid] | 在群内使用此功能会在30秒内撤回 | 通过PID搜索图片 | p搜 [pid] | 在群内使用此功能会在30秒内撤回
| 翻译 | 英翻/日翻/韩翻/翻韩/翻日/翻英 \[文本] | 三种语言互相翻译 | 翻译 | 英翻/日翻/韩翻/翻韩/翻日/翻英 \[文本] | 三种语言互相翻译
| 获取b站视频封面 | b封面 [链接/av/bv/cv/直播id] | 快捷的封面获取方式 | 获取b站视频封面 | b封面 [链接/av/bv/cv/直播id] | 快捷的封面获取方式
@@ -191,13 +197,15 @@
| 自我介绍 | 自我介绍 | 没错,一份正经的真寻自我介绍 | 自我介绍 | 自我介绍 | 没错,一份正经的真寻自我介绍
| 我的权限 | 我的权限 | 真寻内部定义的一套权限系统 | 我的权限 | 我的权限 | 真寻内部定义的一套权限系统
| 我的信息 | 我的信息 | 唯一的作用就是看看什么时候加入群 | 我的信息 | 我的信息 | 唯一的作用就是看看什么时候加入群
| 撤回 | 撤回 \[消息位置](默认为最新一条消息) | 按顺序撤回发送的消息,示例:撤回 1 | 撤回 | 撤回 \[消息位置]?(默认为最新一条消息) | 按顺序撤回发送的消息,示例:撤回 1
| 滴滴滴- | 滴滴滴- \[文本] | 用于用户联系真寻的超级用户 | 滴滴滴- | 滴滴滴- \[文本] | 用于用户联系真寻的超级用户
|功能调用统计可视化 | 功能调用统计(自记录以来的功能调用统计)<br>周功能调用统计 [plugin_name]<br>月功能调用统计 [plugin_name]| 当plugin_name为空时为7天或30内的所有功能统计 |功能调用统计可视化 | 功能调用统计(自记录以来的功能调用统计)<br>周功能调用统计 [plugin_name]<br>月功能调用统计 [plugin_name]| 当plugin_name为空时为7天或30内的所有功能统计
| pix | pix/PIX [tags/uid/pid:pid] [num] | 无参数时随机查看pix图库的图片(无r18),num数量默认=1,tags:查看相关tags图片,uid:查找相关画师图片,pid:pid:指定查看pid图片<br>示例:pix原神 3<br>pix23493844<br>pixpid:29429933 | pix | pix/PIX [tags/uid/pid:pid] [num] | 无参数时随机查看pix图库的图片(无r18),num数量默认=1,tags:查看相关tags图片,uid:查找相关画师图片,pid:pid:指定查看pid图片<br>示例:pix原神 3<br>pix23493844<br>pixpid:29429933
| 添加pix关键词/uid/pid | 添加pix关键词/uid/pid *[关键词/uid/pid]| 添加关键词或uid或pid用于下次搜索,关键词搜索相关tag,uid会收录作者下收藏符合标准的作品,pid收录单张作品<br>示例:添加pix关键词 原神<br> 添加pixuid 123441<br>添加pixpid 2748937| | 添加pix关键词/uid/pid | 添加pix关键词/uid/pid *[关键词/uid/pid]| 添加关键词或uid或pid用于下次搜索,关键词搜索相关tag,uid会收录作者下收藏符合标准的作品,pid收录单张作品<br>示例:添加pix关键词 原神<br> 添加pixuid 123441<br>添加pixpid 2748937|
| 查看pix图库 | 查看pix图库 [tags] | 查看已收录的tag相关图片数量<br>示例:查看pix图库 原神 莫娜 | 查看pix图库 | 查看pix图库 [tags] | 查看已收录的tag相关图片数量<br>示例:查看pix图库 原神 莫娜
|显示pix关键词 | 显示pix关键词 | 查看已收录的所有关键词/UID/PID |显示pix关键词 | 显示pix关键词 | 查看已收录的所有关键词/UID/PID
| b了 | b了 [at] | 使真寻完全忽略一个用户的所有信息
| B站订阅 | 添加订阅 [主播/UP/番剧] [id/链接/番名] / 删除订阅 [id] / 查看订阅 | 可以通过直接间链接或主播间id添加订阅主播开播提醒,动态和投稿,可以通过番名,番剧的id,或者番剧链接添加番剧订阅(是番剧id,md开头,不是集数id,ep开头的)更新, 可以通过UP个人id或UP主页链接订阅UP动态和投稿
### 管理员功能 ### 管理员功能
@@ -210,18 +218,19 @@
| 查看群被动技能 | 2 | 群通知状态 | 详细请查看被动技能列表 | 查看群被动技能 | 2 | 群通知状态 | 详细请查看被动技能列表
| 被动技能开关 | 2 | 开启/关闭被动技能 | 有时候花里胡哨通知也会很烦人 | 被动技能开关 | 2 | 开启/关闭被动技能 | 有时候花里胡哨通知也会很烦人
| 自定义群欢迎消息 | 2 | 自定义群欢迎消息 \[文本] \[图片] | 文本和图片至少需要一个,在文本内添加"\[at]"字符串可以用来设置艾特进群的新群员 | 自定义群欢迎消息 | 2 | 自定义群欢迎消息 \[文本] \[图片] | 文本和图片至少需要一个,在文本内添加"\[at]"字符串可以用来设置艾特进群的新群员
| 黑名单 | 5 | .ban/.unban \[at] \[小时](可选) \[分钟](可选)| 不提供具体时间的话则ban掉永久,且权限低的用户无法unban高权限用户的ban,同级权限也无法进行ban/unban <br>示例:.ban@笨蛋 1 50 | 黑名单 | 5 | .ban/.unban \[at] \[小时]? \[分钟]?| 不提供具体时间的话则ban掉永久,且权限低的用户无法unban高权限用户的ban,同级权限也无法进行ban/unban <br>示例:.ban@笨蛋 1 50
| 刷屏检测相关 | 5 | 刷屏检测设置/设置检测时间 \[文本]/设置检测次数 \[文本]/设置禁言时长 \[分钟]| 非常讨厌刷屏的人,打算给他们一点教训 | 刷屏检测相关 | 5 | 刷屏检测设置/设置检测时间 \[文本]/设置检测次数 \[文本]/设置禁言时长 \[分钟]| 非常讨厌刷屏的人,打算给他们一点教训
| 上传图片 | 6 | 上传图片 \[图库] \[图片]... | 上传图片至指定图库,虽然并不打算开放给群员,但还是写了,支持批量图片<br>示例:上传图片 美图 \[图片].. | 上传图片 | 6 | 上传图片 \[图库] \[图片]... | 上传图片至指定图库,虽然并不打算开放给群员,但还是写了,支持批量图片<br>示例:上传图片 美图 \[图片]..
| 删除图片 | 6 | 删除图片 [图库] \[图片id] | 通过指定本地图片id来删除指定图库的图片<br>示例:删除图片 美图 1 | 删除图片 | 6 | 删除图片 [图库] \[图片id] | 通过指定本地图片id来删除指定图库的图片<br>示例:删除图片 美图 1
| 移动图片 | 6 | 移动图片 \[移出的图库] \[移入的图库] \[图片id] | 移动指定图库中的图片到指定的新图库中,移入的图片id更改为移入图库的最后一位,移除的图库中原本图片的id又最后一位图片替代<br>示例:移动图片 美图 萝莉 22 | 移动图片 | 6 | 移动图片 \[移出的图库] \[移入的图库] \[图片id] | 移动指定图库中的图片到指定的新图库中,移入的图片id更改为移入图库的最后一位,移除的图库中原本图片的id又最后一位图片替代<br>示例:移动图片 美图 萝莉 22
|B站订阅 | 5 | 功能同上,就是在群中有权限限制 | 略
### 超级用户功能 ### 超级用户功能
| 功能 | 指令 | 说明 | 功能 | 指令 | 说明
| ----------------------| :--------------------------------------:| :------------------------: | ----------------------| :--------------------------------------:| :------------------------:
| 权限增删 | 添加/删除权限 \[at] \[level]<br>添加/删除权限 [qq] [group] [level] | 用于添加或修改权限等级,且该权限不会被自动更新取消 | 权限增删 | 添加/删除权限 \[at] \[level]<br>添加/删除权限 [qq] [group] [level] | 用于添加或修改权限等级,且该权限不会被自动更新取消
| 查看群组/好友 | 查看群组/好友 | 查看真寻添加的群组与好友 | 所有群组/好友 | 所有群组/好友 | 查看真寻添加的群组与好友
| 广播 | 广播- [文本] | 广播所有群组 | 广播 | 广播- [文本] | 广播所有群组
| 更新色图 | 更新色图 | 更新群友搜索色图时保存的url | 更新色图 | 更新色图 | 更新群友搜索色图时保存的url
| 回复 | /t | 命令较多,请查看/t帮助,省略群号则私聊用户(必须要有用户的好友) | 回复 | /t | 命令较多,请查看/t帮助,省略群号则私聊用户(必须要有用户的好友)
@@ -231,240 +240,30 @@
| 检查系统状态 | 自检 | 略 | 检查系统状态 | 自检 | 略
| 更新好友/群组信息| 更新好友/群组信息 | 包含自动更新,被t出群等等有更好的可视信息 | 更新好友/群组信息| 更新好友/群组信息 | 包含自动更新,被t出群等等有更好的可视信息
| 重载卡池 | 略 | 重载抽卡的游戏卡池,请查看 \[nonebot_plugin_gamedraw](https://github.com/HibiKier/nonebot_plugin_gamedraw) | 重载卡池 | 略 | 重载抽卡的游戏卡池,请查看 \[nonebot_plugin_gamedraw](https://github.com/HibiKier/nonebot_plugin_gamedraw)
| 添加商品 | 添加商品 \[名称]-\[价格]-\[描述]-\[折扣](小数)(可选)-\[限时时间](分钟)(可选) | 为真寻的商店添加一点点道具<br>示例:添加商品-昏睡红茶-300-一杯上好的奇怪红茶-0.9-60 | 添加商品 | 添加商品 \[名称]-\[价格]-\[描述]-\[折扣](小数)?-\[限时时间](分钟)? | 为真寻的商店添加一点点道具<br>示例:添加商品-昏睡红茶-300-一杯上好的奇怪红茶-0.9-60
| 删除商品 | 删除商品 \[名称或序号] | 在真寻的商店中删除一点东西 | 删除商品 | 删除商品 \[名称或序号] | 在真寻的商店中删除一点东西
| 修改商品 | 修改商品 -name \[名称或序号] -price \[价格] -des \[描述] -discount \[折扣] -time \[限时]| 注意空格,不需要的参数可以不加<br>示例:修改商品 -name 1 -price 900 【修改序号为1的商品的价格为900】 | 修改商品 | 修改商品 -name \[名称或序号] -price \[价格] -des \[描述] -discount \[折扣] -time \[限时]| 注意空格,不需要的参数可以不加<br>示例:修改商品 -name 1 -price 900 【修改序号为1的商品的价格为900】
| 节日红包 | 节日红包 \[金额] \[数量] \[祝福语](可选) \[群号](可选)... | 群号支持批量,使用空格隔开,不使用群号则对所有群发送节日红包,节日红包有效时间为24小时,祝福语默认为“恭喜发财 大吉大利”<br>示例:节日红包 10000 15 真寻真可爱 123324423 23423423 | 节日红包 | 节日红包 \[金额] \[数量] \[祝福语]? \[群号]?... | 群号支持批量,使用空格隔开,不使用群号则对所有群发送节日红包,节日红包有效时间为24小时,祝福语默认为“恭喜发财 大吉大利”<br>示例:节日红包 10000 15 真寻真可爱 123324423 23423423
| 修改群权限 | 修改群权限 \[group] \[level] | 所以说这功能是对内鬼的无奈,默认群权限为5,默认无法使用 色图/coser/p站排行/搜图(这些功能都要9级权限) | 修改群权限 | 修改群权限 \[group] \[level] | 所以说这功能是对内鬼的无奈,默认群权限为5,默认无法使用 色图/coser/p站排行/搜图(这些功能都要9级权限)
|更新原神今日素材| 更新原神今日素材 | 自动更新原神每日素材失败时可以手动触发 |更新原神今日素材| 更新原神今日素材 | 自动更新原神每日素材失败时可以手动触发
|更新原神资源信息| 更新原神资源信息 | 除了每日自动更新的资源外,额外更新大地图 |更新原神资源信息| 更新原神资源信息 | 除了每日自动更新的资源外,额外更新大地图
| 清理数据 | 清理数据 | 清理 temp,rar,r18_rar 文件夹的文件数据 | 清理数据 | 清理数据 | 清理 temp,rar,r18_rar 文件夹的文件数据
|添加pix关键词/uid/pid | 添加pix关键词/uid/pid [keyword/pid:pid/uid:uid] [-f]? | 与普通功能相同,额外可以通过参数 -f 强制通过检测 |添加pix关键词/uid/pid | 添加pix关键词/uid/pid [keyword/pid:pid/uid:uid] [-f]? | 与普通功能相同,额外可以通过参数 -f 强制通过检测
|删除pix关键词 | 删除pix关键词 *[keyword/uid/pid:pid] | 删除已收录的keyword/uid/pid |删除pix关键词 | 删除pix关键词 *[keyword/uid/pid:pid] | 删除已收录的keyword/uid/pid
|更新pix关键词 | 更新pix关键词 [keyword/pid:pid/uid:uid] [num]| 更新keyword,uid,pid或指定uid,pid,未指定时,则更新全部,当num未指定时为keyword/uid/pid更新全部<br>示例:更新pix关键词<br>更新pix关键词uid 8<br>更新pix关键词pid:83457477 |更新pix关键词 | 更新pix关键词 [keyword/pid:pid/uid:uid] [num]?| 更新keyword,uid,pid或指定uid,pid,未指定时,则更新全部,当num未指定时为keyword/uid/pid更新全部<br>示例:更新pix关键词<br>更新pix关键词uid 8<br>更新pix关键词pid:83457477<br>num:倒叙更新数量
|删除pix图片 | 删除pix图片 [*pid] [-b]? | [-b]参数为删除的同时加入黑名单(不再更新),虽然是pid,但是_p也可以<br>示例:删除pix图片3458344 8235234_p1 -b |删除pix图片 | 删除pix图片 [*pid] [-b]? | [-b]参数为删除的同时加入黑名单(不再更新),虽然是pid,但是_p也可以<br>示例:删除pix图片3458344 8235234_p1 -b
|显示pix关键词 | 显示pix关键词 | 与普通功能相同,额外显示待收录和黑名单 |显示pix关键词 | 显示pix关键词 | 与普通功能相同,额外显示待收录和黑名单
|pix检测更新 | pix检测更新 [update]? | 检测从未更新过的pid或uid,-update参数将在检测后直接更新未更新过的pid或uid<br>示例:pix检测更新 update |pix检测更新 | pix检测更新 [update]? | 检测从未更新过的pid或uid,-update参数将在检测后直接更新未更新过的pid或uid<br>示例:pix检测更新 update
| 检查更新真寻 | 检查更新真寻 | 不再需要麻烦的clone第一步 | 检查更新真寻 | 检查更新真寻 | 不再需要麻烦的clone第一步
| 关闭功能 | 关闭[功能] [group/private]/*[群号] | 关闭色图:维护功能(关闭总开关)<br>关闭色图 g:在群内限制功能<br>关闭色图 p:在私聊限制功能<br>关闭色图 1234678:禁用12345678的色图功能 | 关闭功能 | 关闭[功能] [group/private]/*[群号]? | 关闭色图:维护功能(关闭总开关)<br>关闭色图 g:在群内限制功能<br>关闭色图 p:在私聊限制功能<br>关闭色图 1234678:禁用12345678的色图功能
| 群白名单 | 添加/删除群白名单 *[群号] | 白名单内的群不会受到 维护 限制 | 群白名单 | 添加/删除群白名单 *[群号] / 查看群白名单 | 白名单内的群不会受到 维护 限制
| 功能状态 | 功能状态 | 查看功能开关状态
| pix | pix [-s/-r] [keyword] | 可以通过pix -s 查看图库的涩图,pix -r查看图库的r18图,支持搜索,当然,pix图库只区分了r18和非r18,如果-s查询到不色的图也问题不大
| 重载插件配置 |重载插件配置 |用于生效手动修改配文件
|帮助 -super | 帮助[功能] -super | 显示该插件的超级用户帮助
</details> </details>
## 部分功能展示
<details>
<summary>部分功能展示及说明</summary>
### 帮助以及开关(功能控制)
群帮助将会在功能左侧展示该功能的开关,带有√或×的功能代表可以开关<br>
此插件使用 [nonebot_plugin_manager](https://github.com/Jigsaw111/nonebot_plugin_manager) 并魔改一点实现
![](https://raw.githubusercontent.com/HibiKier/zhenxun_bot/main/docs/help.PNG)
![](https://raw.githubusercontent.com/HibiKier/zhenxun_bot/main/docs/kg1.png)
![](https://raw.githubusercontent.com/HibiKier/zhenxun_bot/main/docs/kg3.png)
![](https://raw.githubusercontent.com/HibiKier/zhenxun_bot/main/docs/kg2.png)
<br>
如果你希望某功能暂时停用<br>
私聊发送 npm block xx (xx=功能名)来锁定<br>
使用npm unblock xx 进行解锁
![](https://raw.githubusercontent.com/HibiKier/zhenxun_bot/main/docs/ocgn.png)
![](https://raw.githubusercontent.com/HibiKier/zhenxun_bot/main/docs/ocgn2.png)
### 签到
普普通通的签到,设置影响开箱次数和涩图触发成功的概率(可配置)<br>
开箱次数 = 初始开箱数量 + 好感度 / 3<br>
金币 = random.randint(100) + random.randint(好感度)【好感度获取的金币不会超过200】
![](https://raw.githubusercontent.com/HibiKier/zhenxun_bot/main/docs/sign.png)
### 黑白草图
整活生成器(从未设想的道路)
![](https://raw.githubusercontent.com/HibiKier/zhenxun_bot/main/docs/w2b.png)
### 发送文件夹下随机图片
提供了 美图589(获取该图库下文件名589.jpg的图片)方法,图库内图片名称需要有序(如:0.jpg,1.jpg....)
![](https://raw.githubusercontent.com/HibiKier/zhenxun_bot/main/docs/send_img.png)
### 色图
略,send_setu/check_setu_hash.py文件用于记录涩图hash和检测文件名是否连贯(例如:0.jpg, 1.jpg....)
### 开箱(csgo模拟开箱)
我的开箱/群开箱统计/我的金色 功能是对开箱数据的统计展示 <br>
目前支持的武器箱(数据已备好):
* 狂牙大行动武器箱
* 突围大行动武器箱
* 命悬一线武器箱
* 裂空武器箱
* 光谱武器箱
<br>
BUFF账号可能会因为短时间内访问api次数过多被禁止访问api!!
如果是第一次启动请先使用命令 “更新价格”, “更新图片” (需要配置cookie!!如果经常超时请设置代理,配置文件中的 buff_proxy!)<br>
如果需要配置新的箱子,请在.config.py中配置好该箱子中的皮肤,且列表名是箱子名称的大写拼音<br>
示例:光谱武器箱 GUANGPU_CASE_KNIFE,GUANGPU_CASE_RED...后面的颜色代表皮肤品质
![](https://raw.githubusercontent.com/HibiKier/zhenxun_bot/main/docs/kaixiang.png)
### BUFF皮肤底价查询
需要配置cookie!!!!!!!!<br>
如果经常超时请设置代理,配置文件中的 buff_proxy!
![](https://raw.githubusercontent.com/HibiKier/zhenxun_bot/main/docs/buff.png)
### coser
![](https://raw.githubusercontent.com/HibiKier/zhenxun_bot/main/docs/coser.png)
### 鸡汤/语录
![](https://raw.githubusercontent.com/HibiKier/zhenxun_bot/main/docs/jitang.png)
### 骂我
![](https://raw.githubusercontent.com/HibiKier/zhenxun_bot/main/docs/mawo.png)
### 鲁迅说
此插件使用 [nonebot2_luxun_says](https://github.com/NothAmor/nonebot2_luxun_says)
![](https://raw.githubusercontent.com/HibiKier/zhenxun_bot/main/docs/luxun.png)
### 假消息
![](https://raw.githubusercontent.com/HibiKier/zhenxun_bot/main/docs/jiaxiaoxi.png)
### 商店系统
商店内的道具支持自定义,但需要写触发后的效果...整不出活,到头来也就增加好感度概率的商品
![](https://raw.githubusercontent.com/HibiKier/zhenxun_bot/main/docs/shop.png)
![](https://raw.githubusercontent.com/HibiKier/zhenxun_bot/main/docs/daoju.png)
### 昵称系统
养成方法第一步,让可爱的小真寻叫自己昵称!(替换ai中的'你'等等)
![](https://raw.githubusercontent.com/HibiKier/zhenxun_bot/main/docs/nicheng1.png)
![](https://raw.githubusercontent.com/HibiKier/zhenxun_bot/main/docs/nicheng2.png)
### 抽卡(8种手游的抽卡)
已单独分离并上传至nb2商店,不再放图片了,项目地址:[nonebot_plugin_gamedraw](https://github.com/HibiKier/nonebot_plugin_gamedraw)
### 我有一个朋友...
使用大佬的插件 [cappuccilo_plugins](https://github.com/pcrbot/cappuccilo_plugins#%E7%94%9F%E6%88%90%E5%99%A8%E6%8F%92%E4%BB%B6)
![](https://raw.githubusercontent.com/HibiKier/zhenxun_bot/main/docs/one_firend.png)
### 原神黄历/今日素材/丘丘语翻译/地图资源查询
使用大佬的插件 [Genshin_Impact_bot](https://github.com/H-K-Y/Genshin_Impact_bot)
### 对图片的操作
只是一些简单对图片操作(娱乐整活)
![](https://raw.githubusercontent.com/HibiKier/zhenxun_bot/main/docs/tupian.png)
### 识番
使用大佬的插件 [XUN_Langskip](https://github.com/Angel-Hair/XUN_Bot)
![](https://raw.githubusercontent.com/HibiKier/zhenxun_bot/main/docs/shifan.png)
### 识图
使用nb2商店插件 [nonebot_plugin_picsearcher](https://github.com/synodriver/nonebot_plugin_picsearcher) (可配置图片返回的最大数量)
![](https://raw.githubusercontent.com/HibiKier/zhenxun_bot/main/docs/shitu.png)
### epic免费游戏
访问rsshub获取数据解析<br>可以不玩,不能没有
![](https://raw.githubusercontent.com/HibiKier/zhenxun_bot/main/docs/epic.png)
### P站排行/搜图
访问rsshub获取数据解析
![](https://raw.githubusercontent.com/HibiKier/zhenxun_bot/main/docs/p_rank.png)
![](https://raw.githubusercontent.com/HibiKier/zhenxun_bot/main/docs/p_sou.png)
### 翻译
![](https://raw.githubusercontent.com/HibiKier/zhenxun_bot/main/docs/fanyi.png)
### 自定义群欢迎消息
关键字 [at] 判断是否艾特入群用户
![](https://raw.githubusercontent.com/HibiKier/zhenxun_bot/main/docs/qhyxx.png)
### 查看当前群欢迎消息
![](https://raw.githubusercontent.com/HibiKier/zhenxun_bot/main/docs/qunhuanying.png)
### 自检
![](https://raw.githubusercontent.com/HibiKier/zhenxun_bot/main/docs/check.png)
### .ban/.unban
![](https://raw.githubusercontent.com/HibiKier/zhenxun_bot/main/docs/ban.png)
### 查看被动技能(被动技能除复读外都提供了开关)
![](https://raw.githubusercontent.com/HibiKier/zhenxun_bot/main/docs/beidong.png)
### 自我介绍
只是一段简单自我介绍,但是,还是想放上来
![](https://raw.githubusercontent.com/HibiKier/zhenxun_bot/main/docs/jieshao.png)
### 我的信息/我的权限
![](https://raw.githubusercontent.com/HibiKier/zhenxun_bot/main/docs/info.png)
### 金币红包
![](https://raw.githubusercontent.com/HibiKier/zhenxun_bot/main/docs/redbag0.png)
![](https://raw.githubusercontent.com/HibiKier/zhenxun_bot/main/docs/redbag1.png)
![](https://raw.githubusercontent.com/HibiKier/zhenxun_bot/main/docs/redbag2.png)
### 俄罗斯轮盘
![](https://raw.githubusercontent.com/HibiKier/zhenxun_bot/main/docs/russian0.png)
![](https://raw.githubusercontent.com/HibiKier/zhenxun_bot/main/docs/russian1.png)
![](https://raw.githubusercontent.com/HibiKier/zhenxun_bot/main/docs/russian2.png)
<br>
### 其他
点歌:使用 [nonebot_plugin_songpicker2](https://github.com/maxesisn/nonebot_plugin_songpicker2) 插件<br>
<br><br>
## 其他功能请自己试一试 )
</details>
## 部署 ## 部署
@@ -491,7 +290,7 @@ pip install -r requirements.txt
python bot.py python bot.py
``` ```
## 配置(暂时不更新 json 配置,请将USE_CONFIG_FILE设置为False) ## 配置
在 configs/config.py 中的 USE_CONFIG_FILE,默认为False 在 configs/config.py 中的 USE_CONFIG_FILE,默认为False
``` ```
@@ -507,167 +306,98 @@ python bot.py
在./configs/config.py中配置基本配置(除API KEY ,数据库和代理外都含有默认值) 在./configs/config.py中配置基本配置(除API KEY ,数据库和代理外都含有默认值)
在./configs/path_config.py配置路径(含有默认配置) 在./configs/path_config.py配置路径(含有默认配置)
########(暂时不更新 json 配置,请将USE_CONFIG_FILE设置为False,直接进入./configs/config.py进行配置)
# 是否使用配置文件(为True时这将会生成三份配置文件
./config.json:主要配置
./configs/plugins2cmd_config.json: 功能模块与对应命令配置
./configs/other_config.json: 一些插件配置)
USE_CONFIG_FILE = True
# 如果不使用配置文件,将USE_CONFIG_FILE设置为False
#可在./configs/config.py文件中修改配置,在./configs/path_config.py修改资源路径
# 已在./configs/config.py和./configs/path_config.py中为各个配置提供注解!
``` ```
## 配置文件注解(如果使用配置文件的话)
<details>
<summary>配置文件注解</summary>
./config.json
```
{
# 必填(影响功能运行)
"apikey": {
"LOLICON_KEY": "", # loliconAPI,缺失会导致色图功能异常
"TL_KEY": [] # 图灵Key(为什么要用列表?因为白嫖用户能拿5个apikey,每个apikey每日限制100条)
},
# (必填!!!)
# 数据库配置(如果填写了bind,后面就不用再填了)只是帮你拼接好)
# 示例:"bind": "postgresql://user:password@127.0.0.1:5432/database"
"sql": {
"bind": "",
"sql_name": "",
"user": "",
"password": "",
"address": "",
"port": "",
"database": ""
},
# 路径设置(不填则使用默认)
"path": {
"IMAGE_PATH": "", # 图片路径
"VOICE_PATH": "", # 音频路径
"TXT_PATH": "", # 文本路径
"LOG_PATH": "", # 日志路径
"DATA_PATH": "", # 数据路径
"DRAW_PATH": "" # 抽卡数据路径
"TEMP_PATH": "" # 临时图片路径
},
# 代理设置
"proxy": {
"system_proxy": "", # 系统代理
"buff_proxy": "" # buff代理
},
# RSSHUB地址
"rsshub": {
"RSSHUBAPP": "https:\/\/docs.rsshub.app\/"
},
# 各个管理员功能 对应的 权限
"level": {
"DELETE_IMG_LEVEL": 7,
"MOVE_IMG_LEVEL": 7,
"UPLOAD_LEVEL": 6,
"BAN_LEVEL": 5,
"OC_LEVEL": 2,
"MUTE_LEVEL": 5
}
}
```
./configs/plugins2cmd_config.json
```
# 单个例子注解
"send_img": [
"发送图片",
"萝莉",
"美图",
"壁纸"
]
# 发送 关闭发送图片/关闭萝莉/关闭美图/关闭壁纸 都将触发命令 关闭send_img
```
./configs/other_config.json
```
{
"base": {
# 图库配置,会影响 上传/删除/移动/发送图片等功能
"IMAGE_DIR_LIST": [
"色图",
"美图",
"萝莉",
"壁纸"
],
"BAN_RESULT": "才不会给你发消息." # 当被ban用户触发命令后发送的消息
},
"bool": {
"AUTO_ADD_FRIEND": true, # 是否自动添加好友
"DOWNLOAD_SETU": true # 是否下载bot发送过的色图(不会重复)
},
"max_count": {
"MAXINFO_REIMU": 7, # 上车(reimu)功能查找目的地的最大数
"COUNT_PER_DAY_REIMU": 5, # 每日上车(reimu)次数限制
"MAXINFO_BT": 10, # bt功能单次查找最大数
"MAXINFO_PRIVATE_ANIME": 20, # 私聊搜索动漫返回的最大数量
"MAXINFO_GROUP_ANIME": 5, # 群搜索动漫返回的最大数量
"MAX_FIND_IMG_COUNT": 3, # 识图最大返回数
"MAX_SIGN_GOLD": 200, # 签到好感度加成额外获得的最大金币数
"MAX_SETU_R_COUNT": 5, # 每日色图r次数限制
},
"probability": {
"INITIAL_SETU_PROBABILITY": 0.7, # 涩图触发的基础概率(触发概率 = 基础概率 + 好感度)
"FUDU_PROBABILITY": 0.7 # 复读概率
},
# 注:即在 MALICIOUS_CHECK_TIME 时间内触发相同命令 MALICIOUS_BAN_COUNT 将被ban MALICIOUS_BAN_TIME 分钟
"malicious_ban": {
"MALICIOUS_BAN_TIME": 30, # 恶意触发命令被ban的时长(分钟)
"MALICIOUS_BAN_COUNT": 4, # 恶意触发命令的规定次数
"MALICIOUS_CHECK_TIME": 5 # 恶意触发命令的规定时间(秒)
},
"open_case": {
"INITIAL_OPEN_CASE_COUNT": 20 # 每日开箱的基本数量(总数量=基本数量 + 好感度/3)
},
# 注:即在 MUTE_DEFAULT_TIME 内发送相似(包含)消息超过 MUTE_DEFAULT_COUNT 将会被 MUTE_DEFAULT_DURATION 分钟
# 这只是默认配置,各个群可以自由设置群内刷屏检测配置
"mute": {
"MUTE_DEFAULT_COUNT": 10, # 刷屏检测默认检测最大次数
"MUTE_DEFAULT_TIME": 7, # 刷屏检测默认规定时间(秒)
"MUTE_DEFAULT_DURATION": 10 # 刷屏检测默认禁言时长(分钟)
},
"other": {
"UPDATE_GOCQ_GROUP": [], # 是否需要为某些群上传最新版的gocq?
"ADMIN_DEFAULT_AUTH": 5 # 默认群管理员权限
},
# 管理员功能 和 对应的 权限
"auth": {
"admin_plugins_auth": {
"admin_bot_manage": 2,
"ban": 5,
"delete_img": 7,
"move_img": 7,
"upload_img": 6,
"admin_help": 1,
"mute": 5
}
}
}
```
</details>
## 更新 ## 更新
### 2021/10/4
* 修复了功能调用统计失效问题
* 当色图库中没有色图时,会在线搜索色图而不是‘没找到符合条件的色图...’
* 快速更新权限再给超级用户发送错误日志
* 修复疫情未加载省份城市无法正常使用
### 2021/10/3
* 对插件进行分离
* 重写了插件与限制管理器以及帮助获取
* 修改一些插件目录和数据存储目录
* 插件通用配置与限制数据将以ymal文件存储 \[路径:data/configs]
* 所有商店相关操作调用统计合并为商店(包括之前已经保存的数据,会先进行备份)
* 简化了点歌的代码相关
* 修复了碧蓝航线抽卡新框导致报错无法正常初始化
* 修复了P站排行/搜图在PC端无法正常显示
* 添加了插件对超级用户是否限制的配置 ‘limit_superuser’
* 添加命令 ‘重载插件配置’,用于生效手动修改配文件
* 超级用户帮助可以添加 -super 来显示该插件的超级用户帮助,示例:帮助.ban -super
* 原神黄历改为网页截图
* 修改了鲁迅说逻辑结构
* 修改了统计图表样式,改为自定义CreateMat
* 节日红包不再被24小时限制,群内多个节日红包将会覆盖
* 当群权限为-1时,不会对群发送修改权限通知,并屏蔽此群一切命令(包括提醒)
* 修复了红包数量可以过大或为负数,红包数量大于群员数量时会修改为群员数量
* 修复了负数开箱
* 签到最低好感度设置为0.01 [@pull/53](https://github.com/HibiKier/zhenxun_bot/pull/53)
* pip安装新依赖 ruamel.yaml
* 修复功能 EPIC [@pull/58](https://github.com/HibiKier/zhenxun_bot/pull/58)
### 2021/9/10
* 修复撤回消息有时无法正确获取消息id
### 2021/9/9
* 替换coser API
* 修复签到uid可能不默认为0
* 修复签到可能重复的问题
* 修复无订阅时递归出错
* 启用了plugins2info_dict, plugins2cd_dict, plugins2exists_dict配置文件,通过USE_CONFIG_FILE=True开启
* 修复涩图local_id会被固定为50
* 优化图库数量查询
* 修复原神大地图过大无法打开报错
* 修复无法显示正确的涩图上限
### 2021/9/7
* 修改 update_info.json
* 修改 更新信息 图片大小
* 修复 查看订阅 命令 UP和番剧无法正常显示
* 修复订阅推送无法正确推送
* 修复搜图返回列表为空时无法正确回复 [@pull/40](https://github.com/HibiKier/zhenxun_bot/pull/40)
### 2021/9/5
* 添加配置PIX_IMAGE_SIZE,调整PIX下载图片大小,当设置的图片404时,改为原图
* 新增配置DEFAULT_GROUP_LEVEL,默认群等级
* 新增超级用户功能 super ban,将屏蔽被ban用户的所有消息,指令:b了
* b站转发解析支持纯BV号解析,且五分钟内不会解析相同url
* 俄罗斯轮盘新增 连胜/最高连胜/连败/最高连败 纪录,新增 最高连胜排行榜/最高连败排行榜
* 增加扩展图库 OmegaPixivIllusts,不想自己找图的人福音([Ailitonia](https://github.com/Ailitonia) 佬的高质量精品手筛图库)([传送门](https://github.com/Ailitonia/omega-miya/blob/master/archive_data/db_pixiv.7z) ),可以手动导入图库,也可以将解压文件放在bot.py同级目录重启bot
* 增加配置PIX_OMEGA_PIXIV_RATIO,PIX功能发送PIX图库和扩展图库OmegaPixivIllusts图片的比例,如果没有使用扩展图库OmegaPixivIllusts,请设置为(10, 0)
* 增加配置WITHDRAW_PIX_TIME,用于配置在开关PIX图片在群私聊的自动撤回
* 上传图库cases, 开箱 也可以连抽(未更新过没有价格)
* 新增命令 查看群白名单
* plugins2info_dict新增键"default_status",设置加入新群时功能的默认开关状态
* 增加配置plugins2exists_dict,可自定义是否阻塞某命令同时触发多次
* 增加配置plugins2cd_dict,可自定义为命令添加cd
* 新增B站订阅(直播/番剧/UP)[测试],提供命令:添加订阅 [主播/UP/番剧] [id/链接/番名],删除订阅 [id],查看订阅
* 优化pix和色图的数据库查询
* 触发已关闭的功能的正则时不再触发ai
* 更换coser API
* PIX搜索pid功能在群聊无法搜索PIX图库的r18和OmegaPixivIllusts的r15以及r18,超级用户除外
* PIX单次搜索的图片张数超级用户限制为至多30张,普通用户10张
* PIX超级用户新增-s,-r,可以通过pix -s 查看图库的涩图,pix -r查看图库的r18图,支持搜索,当然,pix图库只区分了r18和非r18,如果-s查询到不色的图也问题不大
* 优化P站排行和搜图,现在需要艾特,改为使用HIBIAPI,在群内时将使用合并消息(群聊搜图会屏蔽R-18)
* win10下playwright相关功能无法使用,但是不再需要删除文件
* 签到大改,优化签到方式与逻辑,改为图片形式发送,有概率额外获得随机道具(好感度有加成)
* 修改撤回功能,改为回复撤回,回复发送撤回
* 更改logging为loguru
* 删除了 发送图片 中的 [N]张图[keyword] 功能
* 修复私聊 关闭[功能] 默认不为 全部 而要添加参数 ‘a’
* 修复0权限用户可以修改禁言检测相关设置
### 2021/8/17 ### 2021/8/17
* 新增配置CHECK_NOTICE_INFO_CD,修改群权限,个人权限检测各种检测的提示消息cd * 新增配置CHECK_NOTICE_INFO_CD,修改群权限,个人权限检测各种检测的提示消息cd
@@ -687,6 +417,7 @@ python bot.py
* 我有一个朋友功能,当艾特一个对象时,‘朋友’改为艾特对象的群名片或昵称 * 我有一个朋友功能,当艾特一个对象时,‘朋友’改为艾特对象的群名片或昵称
* 修复‘上传/删除/移动图片’目录不正确 * 修复‘上传/删除/移动图片’目录不正确
* 修复天气功能,当城市名在‘天气’后时报错 * 修复天气功能,当城市名在‘天气’后时报错
* 修复配置INITIAL_SETU_PROBABILITY不生效
### 2021/8/10 ### 2021/8/10
@@ -736,13 +467,13 @@ python bot.py
* 功能维护时超级用户依然可以调用(苦了谁都不能苦了自己) * 功能维护时超级用户依然可以调用(苦了谁都不能苦了自己)
* 修复获取赛马娘UP公告 * 修复获取赛马娘UP公告
* 重写 色图/更新色图 * 重写 色图/更新色图
* 色图数据存储改为数据库,启动时会更新之前的色图数据(有的话),更新完毕后会删除原数据文件,如果需要保留请提前备份, * 色图数据存储改为数据库,启动时会更新之前的色图数据(有的话),更新完毕后会删除原数据文件,如果需要保留请提前备份,
* lolicon api改为v2 * lolicon api改为v2
* 取消r18次数限制 * 取消r18次数限制
* 单次搜索至多保存100条链接 * 单次搜索至多保存100条链接
* 添加定时撤回 * 添加定时撤回
* 暂时取消上传/删除色图 * 暂时取消上传/删除色图
* *
* 更新建议(不要替换你的data和resources文件夹!) * 更新建议(不要替换你的data和resources文件夹!)
* 删除configs,plugins,services,utils,models文件夹重新clone * 删除configs,plugins,services,utils,models文件夹重新clone
* 删除多余文件夹,resources/img/genshin/seek_god_eye * 删除多余文件夹,resources/img/genshin/seek_god_eye
@@ -854,3 +585,4 @@ python bot.py
[Genshin_Impact_bot](https://github.com/H-K-Y/Genshin_Impact_bot) [Genshin_Impact_bot](https://github.com/H-K-Y/Genshin_Impact_bot)
[nonebot2_luxun_says](https://github.com/NothAmor/nonebot2_luxun_says) [nonebot2_luxun_says](https://github.com/NothAmor/nonebot2_luxun_says)
[AnimeThesaurus](https://github.com/Kyomotoi/AnimeThesaurus) [AnimeThesaurus](https://github.com/Kyomotoi/AnimeThesaurus)
[omega-miya](https://github.com/Ailitonia/omega-miya)
+1
View File
@@ -0,0 +1 @@
__version__: v0.0.5.0
+51
View File
@@ -0,0 +1,51 @@
from typing import Optional, Dict
from .data_class import StaticData
from utils.utils import FreqLimiter
from services.log import logger
from pathlib import Path
class AdminManager(StaticData):
"""
管理员命令 管理器
"""
def __init__(self):
super().__init__(None)
def add_admin_command(self, plugin: str, level: int):
"""
添加一个管理员命令
:param plugin: 模块名
:param level: 权限等级
"""
self._data[plugin] = level
def remove_admin_command(self, plugin: str):
"""
删除一个管理员命令
:param plugin: 模块名
"""
if plugin in self._data.keys():
del self._data[plugin]
def check(self, plugin: str, level: int) -> bool:
"""
检查是否满足权限
:param plugin: 模块名
:param level: 权限等级
"""
if plugin in self._data.keys():
return level >= self._data[plugin]
return True
def get_plugin_level(self, plugin: str) -> int:
"""
获取插件等级
:param plugin: 模块名
"""
if plugin in self._data.keys():
return self._data[plugin]
return 0
+1 -6
View File
@@ -2,19 +2,14 @@ import nonebot
from nonebot.adapters.cqhttp import Bot as CQHTTPBot from nonebot.adapters.cqhttp import Bot as CQHTTPBot
from services.db_context import init, disconnect from services.db_context import init, disconnect
nonebot.init() nonebot.init()
driver = nonebot.get_driver() driver = nonebot.get_driver()
driver.register_adapter("cqhttp", CQHTTPBot) driver.register_adapter("cqhttp", CQHTTPBot)
config = driver.config config = driver.config
driver.on_startup(init) driver.on_startup(init)
driver.on_shutdown(disconnect) driver.on_shutdown(disconnect)
nonebot.load_builtin_plugins()
nonebot.load_plugins("plugins") nonebot.load_plugins("plugins")
nonebot.load_plugins("plugins/shop")
nonebot.load_plugins("plugins/genshin")
nonebot.load_plugins("plugins/alapi")
nonebot.load_plugins("plugins/pix_gallery")
nonebot.load_plugins("plugins/admin_bot_manage")
if __name__ == "__main__": if __name__ == "__main__":
+18 -108
View File
@@ -1,17 +1,13 @@
from typing import List, Optional, Tuple from typing import List, Optional, Tuple
from services.service_config import TL_M_KEY, SYSTEM_M_PROXY, ALAPI_M_TOKEN from services.service_config import TL_M_KEY, SYSTEM_M_PROXY, ALAPI_M_TOKEN
try: try:
import ujson as json import ujson as json
except ModuleNotFoundError: except ModuleNotFoundError:
import json import json
# 是否使用配置文件
USE_CONFIG_FILE: bool = False
# 回复消息名称 # 回复消息名称
NICKNAME: str = '小真寻' NICKNAME: str = "小真寻"
# API KEY(必要) # API KEY(必要)
RSSHUBAPP: str = "https://rsshub.app" # rsshub RSSHUBAPP: str = "https://rsshub.app" # rsshub
@@ -41,6 +37,9 @@ IMAGE_DIR_LIST: List[str] = ["美图", "萝莉", "壁纸"]
# 对被ban用户发送的消息 # 对被ban用户发送的消息
BAN_RESULT: str = "才不会给你发消息." BAN_RESULT: str = "才不会给你发消息."
# PIX图库下载的画质 可能的值:original:原图,master:缩略图(加快发送速度)
PIX_IMAGE_SIZE: str = "master"
# 插件配置 # 插件配置
MAXINFO_REIMU: int = 7 # 上车(reimu)功能查找目的地的最大数 MAXINFO_REIMU: int = 7 # 上车(reimu)功能查找目的地的最大数
@@ -51,6 +50,11 @@ MAXINFO_GROUP_ANIME: int = 5 # 群搜索动漫返回的最大数量
MAX_FIND_IMG_COUNT: int = 3 # 识图最大返回数 MAX_FIND_IMG_COUNT: int = 3 # 识图最大返回数
# 参1:延迟撤回色图时间(秒),0 为关闭 | 参2:监控聊天类型,0(私聊) 1(群聊) 2(群聊+私聊) # 参1:延迟撤回色图时间(秒),0 为关闭 | 参2:监控聊天类型,0(私聊) 1(群聊) 2(群聊+私聊)
WITHDRAW_SETU_TIME: Tuple[int, int] = (0, 1) WITHDRAW_SETU_TIME: Tuple[int, int] = (0, 1)
# 参1:延迟撤回PIX图片时间(秒),0 为关闭 | 参2:监控聊天类型,0(私聊) 1(群聊) 2(群聊+私聊)
WITHDRAW_PIX_TIME: Tuple[int, int] = (0, 1)
# PIX图库 与 额外图库OmegaPixivIllusts 混合搜索的比例 参1:PIX图库 参2:OmegaPixivIllusts扩展图库(没有此图库请设置为0)
PIX_OMEGA_PIXIV_RATIO: Tuple[int, int] = (10, 0)
# 各种卡池的开关 # 各种卡池的开关
PRTS_FLAG = True # 明日方舟 PRTS_FLAG = True # 明日方舟
@@ -68,7 +72,7 @@ SEMAPHORE = 5 # 限制碧蓝航线和FGO并发数
ADMIN_DEFAULT_AUTH: int = 5 # 默认群管理员权限 ADMIN_DEFAULT_AUTH: int = 5 # 默认群管理员权限
MAX_SIGN_GOLD: int = 200 # 签到好感度加成额外获得的最大金币数 MAX_SIGN_GOLD: int = 200 # 签到好感度加成额外获得的最大金币数
MAX_RUSSIAN_BET_GOLD: int = 1000 # 俄罗斯轮盘最大赌注金额 MAX_RUSSIAN_BET_GOLD: int = 1000 # 俄罗斯轮盘最大赌注金额
INITIAL_SETU_PROBABILITY: float = 0.7 # 色图概率 INITIAL_SETU_PROBABILITY: float = 0.7 # 色图概率
FUDU_PROBABILITY: float = 0.7 # 复读概率 FUDU_PROBABILITY: float = 0.7 # 复读概率
@@ -78,21 +82,24 @@ MUTE_DEFAULT_COUNT: int = 10 # 刷屏禁言默认检测次数
MUTE_DEFAULT_TIME: int = 7 # 刷屏检测默认规定时间 MUTE_DEFAULT_TIME: int = 7 # 刷屏检测默认规定时间
MUTE_DEFAULT_DURATION: int = 10 # 刷屏检测默禁言时长(分钟) MUTE_DEFAULT_DURATION: int = 10 # 刷屏检测默禁言时长(分钟)
CHECK_NOTICE_INFO_CD = 300 # 群检测,个人权限检测等各种检测提示信息cd CHECK_NOTICE_INFO_CD = 300 # 群检测,个人权限检测等各种检测提示信息cd
# 注:即在 MALICIOUS_CHECK_TIME 时间内触发相同命令 MALICIOUS_BAN_COUNT 将被ban MALICIOUS_BAN_TIME 分钟 # 注:即在 MALICIOUS_CHECK_TIME 时间内触发相同命令 MALICIOUS_BAN_COUNT 将被ban MALICIOUS_BAN_TIME 分钟
MALICIOUS_BAN_TIME: int = 30 # 恶意命令触发检测触发后ban的时长(分钟) MALICIOUS_BAN_TIME: int = 30 # 恶意命令触发检测触发后ban的时长(分钟)
MALICIOUS_BAN_COUNT: int = 8 # 恶意命令触发检测最大触发次数 MALICIOUS_BAN_COUNT: int = 6 # 恶意命令触发检测最大触发次数
MALICIOUS_CHECK_TIME: int = 5 # 恶意命令触发检测规定时间内(秒) MALICIOUS_CHECK_TIME: int = 5 # 恶意命令触发检测规定时间内(秒)
# LEVEL # LEVEL
DELETE_IMG_LEVEL: int = 7 # 删除图片权限 DELETE_IMG_LEVEL: int = 7 # 删除图片权限
MOVE_IMG_LEVEL: int = 7 # 移动图片权限 MOVE_IMG_LEVEL: int = 7 # 移动图片权限
UPLOAD_LEVEL: int = 6 # 上传图片权限 UPLOAD_IMG_LEVEL: int = 6 # 上传图片权限
BAN_LEVEL: int = 5 # BAN权限 BAN_LEVEL: int = 5 # BAN权限
OC_LEVEL: int = 2 # 开关群功能权限 OC_LEVEL: int = 2 # 开关群功能权限
MUTE_LEVEL: int = 5 # 更改禁言设置权限 MUTE_LEVEL: int = 5 # 更改禁言设置权限
MEMBER_ACTIVITY_LEVEL = 5 # 群员活跃检测设置权限 MEMBER_ACTIVITY_LEVEL = 5 # 群员活跃检测设置权限
GROUP_BILIBILI_SUB_LEVEL = 5 # 群内bilibili订阅需要的权限
DEFAULT_GROUP_LEVEL = 5 # 默认群等级
# 是否开启HIBIAPI搜图功能(该功能会搜索群友提交的xp) # 是否开启HIBIAPI搜图功能(该功能会搜索群友提交的xp)
HIBIAPI_FLAG: bool = True HIBIAPI_FLAG: bool = True
@@ -116,92 +123,6 @@ IMPORT_DEFAULT_SHOP_GOODS: bool = True
# 真寻是否自动更新 # 真寻是否自动更新
AUTO_UPDATE_ZHENXUN: bool = True AUTO_UPDATE_ZHENXUN: bool = True
# 群管理员功能 与 对应权限
admin_plugins_auth = {
"custom_welcome_message": OC_LEVEL,
"group_notification_state": OC_LEVEL,
"switch_rule": OC_LEVEL,
"update_group_member_info": OC_LEVEL,
"ban": BAN_LEVEL,
"delete_img": DELETE_IMG_LEVEL,
"move_img": MOVE_IMG_LEVEL,
"upload_img": UPLOAD_LEVEL,
"admin_help": 1,
"mute": MUTE_LEVEL,
"member_activity_handle": MEMBER_ACTIVITY_LEVEL,
}
# 模块与对应命令和对应群权限
# 用于生成帮助图片 和 开关功能
plugins2info_dict = {
"sign_in": {"level": 5, "cmd": ["签到"]},
"send_img": {"level": 5, "cmd": ["发送图片", "发图", "萝莉", "美图", "壁纸"]},
"send_setu": {"level": 9, "cmd": ["色图", "涩图", "瑟图", "查色图"]},
"white2black": {"level": 5, "cmd": ["黑白图", "黑白草图"]},
"coser": {"level": 9, "cmd": ["coser", "cos"]},
"quotations": {"level": 5, "cmd": ["语录"]},
"jitang": {"level": 5, "cmd": ["鸡汤"]},
"send_dinggong_voice": {"level": 5, "cmd": ["骂我", "骂老子", "骂劳资"]},
"open_cases": {"level": 5, "cmd": ["开箱", "我的开箱", "群开箱统计", "我的金色"]},
"luxun": {"level": 5, "cmd": ["鲁迅说", "鲁迅说过"]},
"fake_msg": {"level": 5, "cmd": ["假消息"]},
"buy": {"level": 5, "cmd": ["购买", "购买道具"]},
"my_gold": {"level": 5, "cmd": ["我的金币"]},
"my_props": {"level": 5, "cmd": ["我的道具"]},
"shop_handle": {"level": 5, "cmd": ["商店"]},
"update_pic": {"level": 5, "cmd": ["图片", "操作图片", "修改图片"]},
"search_buff_skin_price": {"level": 5, "cmd": ["查询皮肤"]},
"weather": {"level": 5, "cmd": ["天气", "查询天气", "天气查询"]},
"yiqing": {"level": 5, "cmd": ["疫情", "疫情查询", "查询疫情"]},
"what_anime": {"level": 5, "cmd": ["识番"]},
"search_anime": {"level": 5, "cmd": ["搜番"]},
"songpicker2": {"level": 5, "cmd": ["点歌"]},
"epic": {"level": 5, "cmd": ["epic"]},
"pixiv": {"level": 9, "cmd": ["pixiv", "p站排行", "搜图"]},
"poke": {"level": 5, "cmd": ["戳一戳", "拍一拍"]},
"draw_card": {
"level": 5,
"cmd": [
"抽卡",
"游戏抽卡",
"原神抽卡",
"方舟抽卡",
"坎公骑冠剑抽卡",
"pcr抽卡",
"fgo抽卡",
"碧蓝抽卡",
"碧蓝航线抽卡",
"阴阳师抽卡",
],
},
"ai": {"level": 5, "cmd": ["ai", "Ai", "AI", "aI"]},
"one_friend": {"level": 5, "cmd": ["我有一个朋友", "我有一个朋友想问问"]},
"translate": {"level": 5, "cmd": ["翻译", "英翻", "翻英", "日翻", "翻日", "韩翻", "翻韩"]},
"nonebot_plugin_picsearcher": {"level": 5, "cmd": ["识图"]},
"almanac": {"level": 5, "cmd": ["原神黄历", "黄历"]},
"material_remind": {"level": 5, "cmd": ["今日素材", "天赋材料"]},
"qiu_qiu_translation": {"level": 5, "cmd": ["丘丘翻译", "丘丘一下", "丘丘语翻译"]},
"query_resource_points": {"level": 5, "cmd": ["原神资源查询", "原神资源列表"]},
"russian": {"level": 5, "cmd": ["俄罗斯轮盘", "俄罗斯转盘", "装弹"]},
"gold_redbag": {"level": 5, "cmd": ["塞红包", "红包", "抢红包"]},
"poetry": {"level": 5, "cmd": ["念诗", "来首诗", "念首诗"]},
"comments_163": {"level": 5, "cmd": ["到点了", "12点了", "网易云热评", "网易云评论"]},
"cover": {"level": 5, "cmd": ["b封面", "B封面"]},
"pid_search": {"level": 9, "cmd": ["p搜", "P搜"]},
"pix": {
"level": 5,
"cmd": ["pix", "PIX", "pIX", "Pix", "PIx"],
},
"wbtop": {
"level": 5,
"cmd": ['微博热搜', "微博", "wbtop"]
},
"update_info": {
"level": 5,
"cmd": ['更新信息', '更新日志']
}
}
if TL_M_KEY: if TL_M_KEY:
TL_KEY = TL_M_KEY TL_KEY = TL_M_KEY
@@ -214,16 +135,5 @@ if ALAPI_M_TOKEN:
HIBIAPI = HIBIAPI[:-1] if HIBIAPI[-1] == "/" else HIBIAPI HIBIAPI = HIBIAPI[:-1] if HIBIAPI[-1] == "/" else HIBIAPI
RSSHUBAPP = RSSHUBAPP[:-1] if RSSHUBAPP[-1] == "/" else RSSHUBAPP RSSHUBAPP = RSSHUBAPP[:-1] if RSSHUBAPP[-1] == "/" else RSSHUBAPP
# 配置文件应用
# if USE_CONFIG_FILE:
# config = get_config_data()
# if config:
# for key in config.keys():
# if isinstance(config[key], str):
# config[key] = config[key].strip()
# if key.find("proxy") != -1:
# if not config[key]:
# config[key] = None
# # if not configs[key] and key.find("PATH") == -1:
# # configs[key] = None
# globals().update(config)
+8 -29
View File
@@ -1,17 +1,15 @@
from .utils.util import get_config_data
from pathlib import Path from pathlib import Path
# from configs.config import USE_CONFIG_FILE
# 图片路径 # 图片路径
IMAGE_PATH = Path("resources/img/") IMAGE_PATH = Path("resources/img/")
# 音频路径 # 音频路径
VOICE_PATH = Path("resources/voice/") VOICE_PATH = Path("resources/voice/")
# 文本路径 # 文本路径
TXT_PATH = Path("resources/txt/") TEXT_PATH = Path("resources/txt/")
# 日志路径 # 日志路径
LOG_PATH = Path("log/") LOG_PATH = Path("log/")
# 字体路径 # 字体路径
TTF_PATH = Path("resources/ttf/") FONT_PATH = Path("resources/ttf/")
# 数据路径 # 数据路径
DATA_PATH = Path("data/") DATA_PATH = Path("data/")
# 临时图片路径 # 临时图片路径
@@ -19,38 +17,20 @@ TEMP_PATH = Path("resources/img/temp/")
def init_path(): def init_path():
global IMAGE_PATH, VOICE_PATH, TXT_PATH, LOG_PATH, TTF_PATH, DATA_PATH, TEMP_PATH global IMAGE_PATH, VOICE_PATH, TEXT_PATH, LOG_PATH, FONT_PATH, DATA_PATH, TEMP_PATH
# if USE_CONFIG_FILE:
# data = get_config_data()
# if data.get('IMAGE_PATH'):
# IMAGE_PATH = Path(data['IMAGE_PATH'])
# if data.get('VOICE_PATH'):
# VOICE_PATH = Path(data['VOICE_PATH'])
# if data.get('TXT_PATH'):
# TXT_PATH = Path(data['TXT_PATH'])
# if data.get('LOG_PATH'):
# LOG_PATH = Path(data['LOG_PATH'])
# if data.get('TTF_PATH'):
# TTF_PATH = Path(data['TTF_PATH'])
# if data.get('DATA_PATH'):
# DATA_PATH = Path(data['DATA_PATH'])
# if data.get('DRAW_PATH'):
# DRAW_PATH = Path(data['DRAW_PATH'])
# if data.get('TEMP_PATH'):
# TEMP_PATH = Path(data['TEMP_PATH'])
IMAGE_PATH.mkdir(parents=True, exist_ok=True) IMAGE_PATH.mkdir(parents=True, exist_ok=True)
VOICE_PATH.mkdir(parents=True, exist_ok=True) VOICE_PATH.mkdir(parents=True, exist_ok=True)
TXT_PATH.mkdir(parents=True, exist_ok=True) TEXT_PATH.mkdir(parents=True, exist_ok=True)
LOG_PATH.mkdir(parents=True, exist_ok=True) LOG_PATH.mkdir(parents=True, exist_ok=True)
TTF_PATH.mkdir(parents=True, exist_ok=True) FONT_PATH.mkdir(parents=True, exist_ok=True)
DATA_PATH.mkdir(parents=True, exist_ok=True) DATA_PATH.mkdir(parents=True, exist_ok=True)
TEMP_PATH.mkdir(parents=True, exist_ok=True) TEMP_PATH.mkdir(parents=True, exist_ok=True)
IMAGE_PATH = str(IMAGE_PATH.absolute()) + '/' IMAGE_PATH = str(IMAGE_PATH.absolute()) + '/'
VOICE_PATH = str(VOICE_PATH.absolute()) + '/' VOICE_PATH = str(VOICE_PATH.absolute()) + '/'
TXT_PATH = str(TXT_PATH.absolute()) + '/' TEXT_PATH = str(TEXT_PATH.absolute()) + '/'
LOG_PATH = str(LOG_PATH.absolute()) + '/' LOG_PATH = str(LOG_PATH.absolute()) + '/'
TTF_PATH = str(TTF_PATH.absolute()) + '/' FONT_PATH = str(FONT_PATH.absolute()) + '/'
DATA_PATH = str(DATA_PATH.absolute()) + '/' DATA_PATH = str(DATA_PATH.absolute()) + '/'
TEMP_PATH = str(TEMP_PATH.absolute()) + '/' TEMP_PATH = str(TEMP_PATH.absolute()) + '/'
@@ -58,5 +38,4 @@ def init_path():
init_path() init_path()
if __name__ == '__main__':
print(IMAGE_PATH)
-176
View File
@@ -1,176 +0,0 @@
# import nonebot
from pathlib import Path
try:
import ujson as json
except ModuleNotFoundError:
import json
# driver: nonebot.Driver = nonebot.get_driver()
base_config = Path() / 'config.json'
plugins_cmd_config = Path() / 'configs' / 'plugins2cmd_config.json'
plugins_setting = Path() / 'configs' / 'plugins_setting.json'
def init_config():
if not base_config.exists():
base_config.parent.mkdir(parents=True, exist_ok=True)
config_dict = {
'apikey': {
'LOLICON_KEY': '',
'TL_KEY': [],
},
'sql': {
'bind': '',
'sql_name': '',
'user': '',
'password': '',
'address': '',
'port': '',
'database': '',
},
'path': {
'IMAGE_PATH': '',
'VOICE_PATH': '',
'TXT_PATH': '',
'LOG_PATH': '',
'DATA_PATH': '',
'DRAW_PATH': '',
'TEMP_PATH': '',
},
'proxy': {
'system_proxy': '',
'buff_proxy': ''
},
'level': {
'DELETE_IMG_LEVEL': 7,
'MOVE_IMG_LEVEL': 7,
'UPLOAD_LEVEL': 6,
'BAN_LEVEL': 5,
'OC_LEVEL': 2,
'MUTE_LEVEL': 5,
},
'auth': {
'ADMIN_DEFAULT_AUTH': 5,
'admin_plugins_auth': {
"admin_bot_manage": 2,
"ban": 5,
"delete_img": 7,
"move_img": 7,
"upload_img": 6,
"admin_help": 1,
"mute": 5
}
}
}
with open(base_config, 'w', encoding='utf8') as f:
json.dump(config_dict, f, indent=4, ensure_ascii=False)
if not plugins_cmd_config.exists():
plugins_cmd_config.parent.mkdir(parents=True, exist_ok=True)
config_dict = {
'base_config': {
'sign_in': ['签到'],
'send_img': ['发送图片', '萝莉', '美图', '壁纸'],
'send_setu': ['色图', '涩图', '瑟图', '查色图'],
'white2black': ['黑白图', '黑白草图'],
'coser': ['coser', 'cos'],
'quotations': ['语录'],
'jitang': ['鸡汤'],
'send_dinggong_voice': ['骂我', '骂老子', '骂劳资'],
'open_cases': ['开箱', '我的开箱', '群开箱统计', '我的金色'],
'luxun': ['鲁迅说过', '鲁迅说'],
'fake_msg': ['假消息'],
'buy': ['购买', '购买道具'],
'my_gold': ['我的金币'],
'my_props': ['我的道具'],
'shop_handle': ['商店'],
'nonebot_plugin_cocdicer': ['骰子娘'],
'update_pic': ['图片', '操作图片', '修改图片'],
'search_buff_skin_price': ['查询皮肤'],
'weather': ['天气', '查询天气', '天气查询'],
'yiqing': ['疫情', '疫情查询', '查询疫情'],
'what_anime': ['识番'],
'search_anime': ['搜番'],
'songpicker2': ['点歌'],
'epic': ['epic'],
'pixiv': ['pixiv', 'p站排行', '搜图'],
'poke': ['戳一戳'],
'draw_card': ['游戏抽卡', '原神一井', '原神来一井', '方舟一井', '方舟来一井'],
'ai': ['ai', 'Ai', 'AI', 'aI'],
'one_friend': ['我有一个朋友', '我有一个朋友想问问'],
'translate': ['翻译', '英翻', '翻英', '日翻', '翻日', '韩翻', '翻韩'],
'nonebot_plugin_picsearcher': ['识图'],
'almanac': ['原神黄历', '黄历'],
'material_remind': ['今日素材', '天赋材料'],
'qiu_qiu_translation': ['丘丘翻译', '丘丘一下', '丘丘语翻译'],
'query_resource_points': ['原神资源查询', '原神资源列表'],
}
}
with open(plugins_cmd_config, 'w', encoding='utf8') as f:
json.dump(config_dict, f, indent=4, ensure_ascii=False)
if not plugins_setting.exists():
plugins_setting.parent.mkdir(parents=True, exist_ok=True)
config_dict = {
'base': {
'IMAGE_DIR_LIST': ["色图", "美图", "萝莉", "壁纸"],
'BAN_RESULT': "才不会给你发消息.",
},
'draw_card': {
'PRTS_FLAG': True, # 明日方舟
'GENSHIN_FLAG': True, # 原神
'PRETTY_FLAG': True, # 赛马娘
'GUARDIAN_FLAG': True, # 坎公骑冠剑
'PCR_FLAG': True, # 公主连结
'AZUR_FLAG': True, # 碧蓝航线
'FGO_FLAG': True, # 命运-冠位指定(FGO)
'ONMYOJI_FLAG': True, # 阴阳师
'PCR_TAI': False # pcr是否开启台服卡池
},
'fudu': {
'FUDU_PROBABILITY': 0.7,
},
'reimu': {
'MAXINFO_REIMU': 7,
'COUNT_PER_DAY_REIMU': 5,
},
'bt': {
'MAXINFO_BT': 10,
},
'search_anime': {
'MAXINFO_PRIVATE_ANIME': 20,
'MAXINFO_GROUP_ANIME': 5,
},
'picsearcher': {
'MAX_FIND_IMG_COUNT': 3,
},
'sign': {
'MAX_SIGN_GOLD': 200,
},
'send_setu': {
'INITIAL_SETU_PROBABILITY': 0.7,
'MAX_SETU_R_COUNT': 5,
'DOWNLOAD_SETU': True,
},
'malicious_ban': {
'MALICIOUS_BAN_TIME': 30,
'MALICIOUS_BAN_COUNT': 8,
'MALICIOUS_CHECK_TIME': 5,
},
'open_case': {
'INITIAL_OPEN_CASE_COUNT': 20,
},
'mute': {
'MUTE_DEFAULT_COUNT': 10,
'MUTE_DEFAULT_TIME': 7,
'MUTE_DEFAULT_DURATION': 10,
},
'update_gocq': {
'UPDATE_GOCQ_GROUP': [],
},
}
with open(plugins_setting, 'w', encoding='utf8') as f:
json.dump(config_dict, f, indent=4, ensure_ascii=False)
-35
View File
@@ -1,35 +0,0 @@
from pathlib import Path
from configs.utils.init_config import init_config
try:
import ujson as json
except ModuleNotFoundError:
import json
data: dict = {}
def get_config_data():
global data
if not data:
try:
base_config = json.load(open(Path() / "config.json", 'r', encoding='utf8'))
plugins2cmd_config = json.load(open(Path() / 'configs' / 'plugins2cmd_config.json', 'r', encoding='utf8'))
other_config = json.load(open(Path() / 'configs' / 'other_config.json', 'r', encoding='utf8'))
for key in base_config.keys():
data.update(base_config[key])
for key in plugins2cmd_config.keys():
data.update(plugins2cmd_config[key])
for key in other_config.keys():
data.update(other_config[key])
except FileNotFoundError:
# logger.warning('配置文件不存在,生成默认配置....请填写数据库等必要数据后再次启动bot...')
init_config()
raise FileNotFoundError('配置文件不存在,生成默认配置....请填写数据库等必要数据后再次启动bot...')
except ValueError:
# logger.error('配置文件错误....')
raise ValueError('配置文件错误....')
return data
+14
View File
@@ -60,6 +60,20 @@ class BanUser(db.Model):
await cls.unban(user_qq) await cls.unban(user_qq)
return False return False
@classmethod
async def is_super_ban(cls, user_qq: int) -> bool:
"""
说明:
判断用户是否被ban
参数:
:param user_qq: qq号
"""
user = await cls.query.where((cls.user_qq == user_qq)).gino.first()
if not user:
return False
if user.ban_level == 10:
return True
@classmethod @classmethod
async def ban(cls, user_qq: int, ban_level: int, duration: int) -> bool: async def ban(cls, user_qq: int, ban_level: int, duration: int) -> bool:
""" """
+249
View File
@@ -0,0 +1,249 @@
from services.log import logger
from services.db_context import db
from datetime import datetime
from typing import Optional, List
class BilibiliSub(db.Model):
__tablename__ = "bilibili_sub"
id = db.Column(db.Integer(), primary_key=True)
sub_id = db.Column(db.Integer(), nullable=False)
sub_type = db.Column(db.String(), nullable=False)
# 订阅用户
sub_users = db.Column(db.String(), nullable=False)
# 直播
live_short_id = db.Column(db.Integer())
live_status = db.Column(db.Integer)
# 主播/UP
uid = db.Column(db.BigInteger())
uname = db.Column(db.String())
latest_video_created = db.Column(db.BigInteger()) # 视频上传时间
dynamic_upload_time = db.Column(db.BigInteger(), default=0) # 动态发布时间
# 番剧
season_name = db.Column(db.String())
season_id = db.Column(db.Integer())
season_current_episode = db.Column(db.String())
season_update_time = db.Column(db.DateTime())
_idx1 = db.Index("bilibili_sub_idx1", "sub_id", "sub_type", unique=True)
@classmethod
async def add_bilibili_sub(
cls,
sub_id: int,
sub_type: str,
sub_user: str,
*,
live_short_id: Optional[int] = None,
live_status: Optional[int] = None,
dynamic_upload_time: Optional[int] = None,
uid: Optional[int] = None,
uname: Optional[str] = None,
latest_video_created: Optional[int] = None,
season_name: Optional[str] = None,
season_id: Optional[int] = None,
season_current_episode: Optional[str] = None,
season_update_time: Optional[datetime] = None,
) -> bool:
"""
说明:
添加订阅
参数:
:param sub_id: 订阅名称,房间号,番剧号等
:param sub_type: 订阅类型
:param sub_user: 订阅此条目的用户
:param live_short_id: 直接短 id
:param live_status: 主播开播状态
:param dynamic_upload_time: 主播/UP最新动态时间
:param uid: 主播/UP uid
:param uname: 用户名称
:param latest_video_created: 最新视频上传时间
:param season_name: 番剧名称
:param season_id: 番剧 season_id
:param season_current_episode: 番剧最新集数
:param season_update_time: 番剧更新时间
"""
try:
async with db.transaction():
query = (
await cls.query.where(cls.sub_id == sub_id)
.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
)
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:
"""
说明:
删除订阅
参数:
:param sub_id: 订阅名称
:param sub_user: 删除此条目的用户
"""
try:
async with db.transaction():
query = (
await cls.query.where(
(cls.sub_id == sub_id) & (cls.sub_users.contains(sub_user))
)
.with_for_update()
.gino.first()
)
if not query:
return False
await query.update(
sub_users=query.sub_users.replace(f"{sub_user},", "")
).apply()
if not query.sub_users.strip():
await query.delete()
return True
except Exception as e:
logger.info(f"bilibili_sub 删除订阅错误 {type(e)}: {e}")
return False
@classmethod
async def get_sub(cls, sub_id: int) -> Optional["BilibiliSub"]:
"""
说明:
获取订阅对象
参数:
:param sub_id: 订阅 id
"""
return await cls.query.where(cls.sub_id == sub_id).gino.first()
@classmethod
async def get_sub_data(cls, id_: str) -> List["BilibiliSub"]:
"""
获取 id_ 订阅的所有内容
:param id_: id
"""
query = cls.query.where(cls.sub_users.contains(id_))
return await query.gino.all()
@classmethod
async def update_sub_info(
cls,
sub_id: int,
*,
live_short_id: Optional[int] = None,
live_status: Optional[int] = None,
dynamic_upload_time: Optional[int] = None,
uid: Optional[int] = None,
uname: Optional[str] = None,
latest_video_created: Optional[int] = None,
season_name: Optional[str] = None,
season_id: Optional[int] = None,
season_current_episode: Optional[str] = None,
season_update_time: Optional[datetime] = None,
) -> bool:
"""
说明:
更新订阅信息
参数:
:param sub_id: 订阅名称,房间号,番剧号等
:param live_short_id: 直接短 id
:param live_status: 主播开播状态
:param dynamic_upload_time: 主播/UP最新动态时间
:param uid: 主播/UP uid
:param uname: 用户名称
:param latest_video_created: 最新视频上传时间
:param season_name: 番剧名称
:param season_id: 番剧 season_id
:param season_current_episode: 番剧最新集数
:param season_update_time: 番剧更新时间
"""
try:
async with db.transaction():
sub = (
await cls.query.where(cls.sub_id == sub_id)
.with_for_update()
.gino.first()
)
if sub:
await sub.update(
live_short_id=live_short_id
if live_short_id is not None
else sub.live_short_id,
live_status=live_status
if live_status is not None
else sub.live_status,
dynamic_upload_time=dynamic_upload_time
if dynamic_upload_time is not None
else sub.dynamic_upload_time,
uid=uid if uid is not None else sub.uid,
uname=uname if uname is not None else sub.uname,
latest_video_created=latest_video_created
if latest_video_created is not None
else sub.latest_video_created,
season_update_time=season_update_time
if season_update_time is not None
else sub.season_update_time,
season_current_episode=season_current_episode
if season_current_episode is not None
else sub.season_current_episode,
season_id=season_id if season_id is not None else sub.season_id,
season_name=season_name
if season_name is not None
else sub.season_name,
).apply()
return True
except Exception as e:
logger.info(f"bilibili_sub 更新订阅错误 {type(e)}: {e}")
return False
@classmethod
async def get_all_sub_data(
cls,
) -> "List[BilibiliSub], List[BilibiliSub], List[BilibiliSub]":
"""
说明:
分类获取所有数据
"""
live_data = []
up_data = []
season_data = []
query = await cls.query.gino.all()
for x in query:
if x.sub_type == "live":
live_data.append(x)
if x.sub_type == "up":
up_data.append(x)
if x.sub_type == "season":
season_data.append(x)
return live_data, up_data, season_data
+35 -1
View File
@@ -1,7 +1,7 @@
from datetime import datetime from datetime import datetime
from services.db_context import db from services.db_context import db
from typing import List from typing import List, Optional
class GroupInfoUser(db.Model): class GroupInfoUser(db.Model):
@@ -13,6 +13,7 @@ class GroupInfoUser(db.Model):
belonging_group = db.Column(db.BigInteger(), nullable=False) belonging_group = db.Column(db.BigInteger(), nullable=False)
user_join_time = db.Column(db.DateTime(), nullable=False) user_join_time = db.Column(db.DateTime(), nullable=False)
nickname = db.Column(db.Unicode()) nickname = db.Column(db.Unicode())
uid = db.Column(db.BigInteger())
_idx1 = db.Index("info_group_users_idx1", "user_qq", "belonging_group", unique=True) _idx1 = db.Index("info_group_users_idx1", "user_qq", "belonging_group", unique=True)
@@ -23,6 +24,7 @@ class GroupInfoUser(db.Model):
belonging_group: int, belonging_group: int,
user_name: str, user_name: str,
user_join_time: datetime, user_join_time: datetime,
uid: Optional[int] = None,
) -> bool: ) -> bool:
""" """
说明: 说明:
@@ -32,6 +34,7 @@ class GroupInfoUser(db.Model):
:param belonging_group: 群号 :param belonging_group: 群号
:param user_name: 用户名称 :param user_name: 用户名称
:param user_join_time: 入群时间 :param user_join_time: 入群时间
:param uid: 用户唯一 id(自动生成)
""" """
query = cls.query.where( query = cls.query.where(
(cls.user_qq == user_qq) & (cls.belonging_group == belonging_group) (cls.user_qq == user_qq) & (cls.belonging_group == belonging_group)
@@ -43,6 +46,7 @@ class GroupInfoUser(db.Model):
user_name=user_name, user_name=user_name,
belonging_group=belonging_group, belonging_group=belonging_group,
user_join_time=user_join_time, user_join_time=user_join_time,
uid=uid
) )
return True return True
except Exception: except Exception:
@@ -141,3 +145,33 @@ class GroupInfoUser(db.Model):
if user.nickname: if user.nickname:
return user.nickname return user.nickname
return "" return ""
@classmethod
async def get_group_member_uid(cls, user_qq: int, belonging_group: int) -> Optional[str]:
query = cls.query.where(
(cls.user_qq == user_qq) & (cls.belonging_group == belonging_group)
)
user = await query.gino.first()
_max_uid = cls.query.where((cls.user_qq == 114514) & (cls.belonging_group == 114514)).with_for_update()
_max_uid_user = await _max_uid.gino.first()
_max_uid = _max_uid_user.uid
if not user or not user.uid:
all_user = await cls.query.where(cls.user_qq == user_qq).gino.all()
for x in all_user:
if x.uid:
return x.uid
else:
if not user:
await GroupInfoUser.add_member_info(user_qq, belonging_group, '', datetime.min)
user = await cls.query.where(
(cls.user_qq == user_qq) & (cls.belonging_group == belonging_group)
).gino.first()
await user.update(
uid=_max_uid + 1,
).apply()
await _max_uid_user.update(
uid=_max_uid + 1,
).apply()
return user.uid if user and user.uid else None
+1 -1
View File
@@ -102,4 +102,4 @@ class GroupRemind(db.Model):
).apply() ).apply()
return True return True
except Exception as e: except Exception as e:
return False return False
+139
View File
@@ -0,0 +1,139 @@
from typing import Optional, List
from datetime import datetime
from services.db_context import db
class OmegaPixivIllusts(db.Model):
__tablename__ = "omega_pixiv_illusts"
id = db.Column(db.Integer(), primary_key=True)
pid = db.Column(db.BigInteger(), nullable=False)
uid = db.Column(db.BigInteger(), nullable=False)
title = db.Column(db.String(), nullable=False)
uname = db.Column(db.String(), 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)
@classmethod
async def add_image_data(
cls,
pid: int,
title: str,
width: int,
height: int,
url: str,
uid: int,
uname: str,
nsfw_tag: int,
tags: str,
created_at: datetime,
updated_at: datetime,
):
"""
说明:
添加图片信息
参数:
:param pid: pid
:param title: 标题
:param width: 宽度
:param height: 长度
:param url: url链接
:param uid: 作者uid
:param uname: 作者名称
:param nsfw_tag: nsfw标签, 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(
pid=pid,
title=title,
width=width,
height=height,
url=url,
uid=uid,
uname=uname,
nsfw_tag=nsfw_tag,
tags=tags,
)
return True
return False
@classmethod
async def query_images(
cls,
keywords: Optional[List[str]] = None,
uid: Optional[int] = None,
pid: Optional[int] = None,
nsfw_tag: Optional[int] = 0,
num: int = 100
) -> List[Optional["OmegaPixivIllusts"]]:
"""
说明:
查找符合条件的图片
参数:
:param keywords: 关键词
:param uid: 画师uid
:param pid: 图片pid
:param nsfw_tag: nsfw标签, 0=safe, 1=setu. 2=r18
:param num: 获取图片数量
"""
if nsfw_tag is not None:
query = cls.query.where(cls.nsfw_tag == nsfw_tag)
else:
query = cls.query
if keywords:
for keyword in keywords:
query = query.where(cls.tags.contains(keyword))
elif uid:
query = query.where(cls.uid == uid)
elif pid:
query = query.where(cls.uid == pid)
query = query.order_by(db.func.random()).limit(num)
return await query.gino.all()
@classmethod
async def check_exists(cls, pid: int) -> bool:
"""
说明:
检测pid是否已存在
参数:
:param pid: 图片PID
"""
query = await cls.query.where(cls.pid == pid).gino.all()
return bool(query)
@classmethod
async def get_keyword_num(cls, tags: List[str] = None) -> "int, int, int":
"""
说明:
获取相关关键词(keyword, tag)在图库中的数量
参数:
:param tags: 关键词/Tag
"""
setattr(OmegaPixivIllusts, 'count', db.func.count(cls.pid).label('count'))
query = cls.select('count')
if tags:
for tag in tags:
query = query.where(cls.tags.contains(tag))
count = await query.where(cls.nsfw_tag == 0).gino.first()
setu_count = await query.where(cls.nsfw_tag == 1).gino.first()
r18_count = await query.where(cls.nsfw_tag == 2).gino.first()
return count[0], setu_count[0], r18_count[0]
@classmethod
async def get_all_pid(cls) -> List[int]:
"""
说明:
获取所有图片PID
"""
data = await cls.select('pid').gino.all()
return [x[0] for x in data]
+42 -63
View File
@@ -1,6 +1,5 @@
from typing import Optional, List from typing import Optional, List
from services.db_context import db from services.db_context import db
import asyncio
class Pixiv(db.Model): class Pixiv(db.Model):
@@ -24,18 +23,19 @@ class Pixiv(db.Model):
@classmethod @classmethod
async def add_image_data( async def add_image_data(
cls, cls,
pid: int, pid: int,
title: str, title: str,
width: int, width: int,
height: int, height: int,
view: int, view: int,
bookmarks: int, bookmarks: int,
img_url: str, img_url: str,
img_p: str, img_p: str,
uid: int, uid: int,
author: str, author: str,
tags: str, tags: str,
nws
): ):
""" """
说明: 说明:
@@ -65,7 +65,7 @@ class Pixiv(db.Model):
img_p=img_p, img_p=img_p,
uid=uid, uid=uid,
author=author, author=author,
is_r18=True if 'R-18' in tags else False, is_r18=True if "R-18" in tags else False,
tags=tags, tags=tags,
) )
return True return True
@@ -97,44 +97,45 @@ class Pixiv(db.Model):
说明: 说明:
获取所有PID 获取所有PID
""" """
pid = [] query = await cls.query.select("pid").gino.first()
query = await cls.query.gino.all() pid = [x[0] for x in query]
for image in query: return list(set(pid))
if image.pid not in pid:
pid.append(image.pid)
return pid
# 0:非r18 1:r18 2:混合 # 0:非r18 1:r18 2:混合
@classmethod @classmethod
async def query_images( async def query_images(
cls, cls,
keyword: Optional[List[str]] = None, keywords: Optional[List[str]] = None,
uid: Optional[int] = None, uid: Optional[int] = None,
pid: Optional[int] = None, pid: Optional[int] = None,
r18: int = 0, r18: Optional[int] = 0,
num: int = 100
) -> List[Optional["Pixiv"]]: ) -> List[Optional["Pixiv"]]:
""" """
说明: 说明:
查找符合条件的图片 查找符合条件的图片
参数: 参数:
:param keyword: 关键词 :param keywords: 关键词
:param uid: 画师uid :param uid: 画师uid
:param pid: 图片pid :param pid: 图片pid
:param r18: 是否r18,0:非r18 1:r18 2:混合 :param r18: 是否r18,0:非r18 1:r18 2:混合
:param num: 查找图片的数量
""" """
if r18 == 0: if r18 == 0:
query = await cls.query.where(cls.is_r18 == False).gino.all() query = cls.query.where(cls.is_r18 == False)
elif r18 == 1: elif r18 == 1:
query = await cls.query.where(cls.is_r18 == True).gino.all() query = cls.query.where(cls.is_r18 == True)
else: else:
query = await cls.query.gino.all() query = cls.query
if keyword: if keywords:
query = [x for x in query if set(x.tags.split(',')) > set(keyword)] for keyword in keywords:
query = query.where(cls.tags.contains(keyword))
elif uid: elif uid:
query = [x for x in query if x.uid == uid] query = query.where(cls.uid == uid)
elif pid: elif pid:
query = [x for x in query if x.pid == pid] query = query.where(cls.uid == pid)
return query query = query.order_by(db.func.random()).limit(num)
return await query.gino.all()
@classmethod @classmethod
async def check_exists(cls, pid: int, img_p: str) -> bool: async def check_exists(cls, pid: int, img_p: str) -> bool:
@@ -151,41 +152,19 @@ class Pixiv(db.Model):
return bool(query) return bool(query)
@classmethod @classmethod
async def get_keyword_num(cls, keyword: List[str]) -> "int, int": async def get_keyword_num(cls, tags: List[str] = None) -> "int, int":
""" """
说明: 说明:
获取相关关键词(keyword, tag)在图库中的数量 获取相关关键词(keyword, tag)在图库中的数量
参数: 参数:
:param keyword: 关键词/Tag :param tags: 关键词/Tag
""" """
query = await cls.query.gino.all() setattr(Pixiv, 'count', db.func.count(cls.pid).label('count'))
i = int(len(query) / 200) query = cls.select('count')
mod = len(query) % 200 if tags:
tasks = [] for tag in tags:
start = 0 query = query.where(cls.tags.contains(tag))
end = 200 count = await query.where(cls.is_r18 == False).gino.first()
count = 0 r18_count = await query.where(cls.is_r18 == True).gino.first()
r18_count = 0 return count[0], r18_count[0]
for _ in range(i):
tasks.append(asyncio.ensure_future(split_query_list(query[start: end], keyword)))
start += 200
end += 200
if mod:
tasks.append(asyncio.ensure_future(split_query_list(query[end:], keyword)))
result = await asyncio.gather(*tasks)
for x, j in result:
count += x
r18_count += j
# query = [x for x in query if set(x.tags.split(',')) > set(keyword)]
# r18_count = len([x for x in query if x.is_r18])
return count, r18_count
async def split_query_list(query: List['Pixiv'], keyword: List[str]) -> 'int, int':
return await asyncio.get_event_loop().run_in_executor(None, _split_query_list, query, keyword)
def _split_query_list(query: List['Pixiv'], keyword: List[str]) -> 'int, int':
query = [x for x in query if set(x.tags.split(',')) > set(keyword)]
r18_count = len([x for x in query if x.is_r18])
return len(query) - r18_count, r18_count
+20
View File
@@ -12,6 +12,10 @@ class RussianUser(db.Model):
fail_count = db.Column(db.Integer(), default=0) fail_count = db.Column(db.Integer(), default=0)
make_money = db.Column(db.Integer(), default=0) make_money = db.Column(db.Integer(), default=0)
lose_money = db.Column(db.Integer(), default=0) lose_money = db.Column(db.Integer(), default=0)
winning_streak = db.Column(db.Integer(), default=0)
losing_streak = db.Column(db.Integer(), default=0)
max_winning_streak = db.Column(db.Integer(), default=0)
max_losing_streak = db.Column(db.Integer(), default=0)
_idx1 = db.Index("russian_group_users_idx1", "user_qq", "group_id", unique=True) _idx1 = db.Index("russian_group_users_idx1", "user_qq", "group_id", unique=True)
@@ -52,12 +56,28 @@ class RussianUser(db.Model):
if not user: if not user:
user = await cls.create(user_qq=user_qq, group_id=group_id) user = await cls.create(user_qq=user_qq, group_id=group_id)
if itype == "win": if itype == "win":
_max = (
user.max_winning_streak
if user.max_winning_streak > user.winning_streak + 1
else user.winning_streak + 1
)
await user.update( await user.update(
win_count=user.win_count + 1, win_count=user.win_count + 1,
winning_streak=user.winning_streak + 1,
losing_streak=0,
max_winning_streak=_max
).apply() ).apply()
elif itype == "lose": elif itype == "lose":
_max = (
user.max_losing_streak
if user.max_losing_streak > user.losing_streak + 1
else user.losing_streak + 1
)
await user.update( await user.update(
fail_count=user.fail_count + 1, fail_count=user.fail_count + 1,
losing_streak=user.losing_streak + 1,
winning_streak=0,
max_losing_streak=_max,
).apply() ).apply()
return True return True
except Exception: except Exception:
+15 -8
View File
@@ -57,6 +57,7 @@ class Setu(db.Model):
local_id: Optional[int] = None, local_id: Optional[int] = None,
tags: Optional[List[str]] = None, tags: Optional[List[str]] = None,
r18: int = 0, r18: int = 0,
limit: int = 50,
): ):
""" """
说明: 说明:
@@ -65,6 +66,7 @@ class Setu(db.Model):
:param local_id: 本地色图 id :param local_id: 本地色图 id
:param tags: tags :param tags: tags
:param r18: 是否 r18,0:非r18 1:r18 2:混合 :param r18: 是否 r18,0:非r18 1:r18 2:混合
:param limit: 获取数量
""" """
if local_id: if local_id:
flag = True if r18 == 1 else False flag = True if r18 == 1 else False
@@ -72,23 +74,27 @@ class Setu(db.Model):
(cls.local_id == local_id) & (cls.is_r18 == flag) (cls.local_id == local_id) & (cls.is_r18 == flag)
).gino.first() ).gino.first()
if r18 == 0: if r18 == 0:
query = await cls.query.where(cls.is_r18 == False).gino.all() query = cls.query.where(cls.is_r18 == False)
elif r18 == 1: elif r18 == 1:
query = await cls.query.where(cls.is_r18 == True).gino.all() query = cls.query.where(cls.is_r18 == True)
else: else:
query = await cls.query.gino.all() query = cls.query
if tags: if tags:
query = [x for x in query if set(x.tags.split(",")) > set(tags)] for tag in tags:
return query query = query.where(cls.tags.contains(tag) | cls.title.contains(tag) | cls.author.contains(tag))
query = query.order_by(db.func.random()).limit(limit)
return await query.gino.all()
@classmethod @classmethod
async def get_image_count(cls, r18: int = 0): async def get_image_count(cls, r18: int = 0) -> int:
""" """
说明: 说明:
查询图片数量 查询图片数量
""" """
return len(await cls.query_image(r18=r18)) flag = False if r18 == 0 else True
# return await db.func.count(cls.local_id).gino.scalar() setattr(Setu, 'count', db.func.count(cls.local_id).label('count'))
count = await cls.select('count').where(cls.is_r18 == flag).gino.first()
return count[0]
@classmethod @classmethod
async def get_image_in_hash(cls, img_hash: str) -> "Setu": async def get_image_in_hash(cls, img_hash: str) -> "Setu":
@@ -166,3 +172,4 @@ class Setu(db.Model):
获取所有图片对象 获取所有图片对象
""" """
return await cls.query.gino.all() return await cls.query.gino.all()
@@ -41,7 +41,17 @@ class SignGroupUser(db.Model):
) )
@classmethod @classmethod
async def get_all_impression(cls, belonging_group: int) -> "list, list": async def sign(cls, user: "SignGroupUser", impression: float, checkin_time_last: datetime):
await user.update(
checkin_count=user.checkin_count + 1,
checkin_time_last=checkin_time_last,
impression=user.impression + impression,
add_probability=0,
specify_probability=0,
).apply()
@classmethod
async def get_all_impression(cls, belonging_group: int) -> "list, list, list":
""" """
说明: 说明:
获取该群所有用户 id 及对应 好感度 获取该群所有用户 id 及对应 好感度
+6 -4
View File
@@ -12,22 +12,24 @@ from utils.utils import FreqLimiter
from configs.config import NICKNAME from configs.config import NICKNAME
__plugin_name__ = "基本设置 [Hidden]" __zx_plugin_name__ = "基本设置 [Hidden]"
__plugin_usage__ = "用法: 无" __plugin_usage__ = "用法: 无"
__plugin_version__ = 0.1
__plugin_author__ = 'HibiKier'
_flmt = FreqLimiter(300) _flmt = FreqLimiter(300)
config_playgame = on_keyword({"打游戏"}, permission=GROUP, priority=1, block=True) config_play_game = on_keyword({"打游戏"}, permission=GROUP, priority=1, block=True)
@config_playgame.handle() @config_play_game.handle()
async def _(bot: Bot, event: GroupMessageEvent, state: T_State): async def _(bot: Bot, event: GroupMessageEvent, state: T_State):
if not _flmt.check(event.group_id): if not _flmt.check(event.group_id):
return return
_flmt.start_cd(event.group_id) _flmt.start_cd(event.group_id)
await config_playgame.finish( await config_play_game.finish(
image(random.choice(os.listdir(IMAGE_PATH + "dayouxi/")), "dayouxi") image(random.choice(os.listdir(IMAGE_PATH + "dayouxi/")), "dayouxi")
) )
+3
View File
@@ -0,0 +1,3 @@
import nonebot
nonebot.load_plugins("plugins/admin_bot_manage")
@@ -4,16 +4,26 @@ from nonebot.typing import T_State
from nonebot.adapters.cqhttp import Bot, GroupMessageEvent from nonebot.adapters.cqhttp import Bot, GroupMessageEvent
from .data_source import custom_group_welcome from .data_source import custom_group_welcome
from nonebot.adapters.cqhttp.permission import GROUP from nonebot.adapters.cqhttp.permission import GROUP
from configs.config import OC_LEVEL
from services.log import logger from services.log import logger
__plugin_name__ = "自定义进群欢迎消息" __zx_plugin_name__ = "自定义进群欢迎消息 [Admin]"
__plugin_usage__ = """ __plugin_usage__ = """
自定义进群欢迎消息 [消息] [图片](可省略) usage:
示例:自定义进群欢迎消息 欢迎新人![图片] 指令:
""" 自定义进群欢迎消息 ?[文本] ?[图片]
示例:自定义进群欢迎消息 欢迎新人![图片]
Note:可以通过[at]来确认是否艾特新成员
示例:自定义进群欢迎消息 欢迎你[at]
""".strip()
__plugin_des__ = '简易的自定义群欢迎消息'
__plugin_cmd__ = ['自定义群欢迎消息 ?[文本] ?[图片]']
__plugin_version__ = 0.1
__plugin_author__ = 'HibiKier'
__plugin_settings__ = {
"admin_level": OC_LEVEL,
}
custom_welcome = on_command( custom_welcome = on_command(
"自定义进群欢迎消息", "自定义进群欢迎消息",
@@ -26,12 +36,16 @@ custom_welcome = on_command(
@custom_welcome.handle() @custom_welcome.handle()
async def _(bot: Bot, event: GroupMessageEvent, state: T_State): async def _(bot: Bot, event: GroupMessageEvent, state: T_State):
msg = get_message_text(event.json()) try:
imgs = get_message_imgs(event.json()) msg = get_message_text(event.json())
if not msg and not imgs: imgs = get_message_imgs(event.json())
await custom_welcome.finish(__plugin_usage__) if not msg and not imgs:
await custom_welcome.send( await custom_welcome.finish(__plugin_usage__)
await custom_group_welcome(msg, imgs, event.user_id, event.group_id), await custom_welcome.send(
at_sender=True, await custom_group_welcome(msg, imgs, event.user_id, event.group_id),
) at_sender=True,
logger.info(f"USER {event.user_id} GROUP {event.group_id} 自定义群欢迎消息:{msg}") )
logger.info(f"USER {event.user_id} GROUP {event.group_id} 自定义群欢迎消息:{msg}")
except Exception as e:
logger.error(f"自定义进群欢迎消息发生错误 {type(e)}:{e}")
await custom_welcome.send("发生了一些未知错误...")
+110 -106
View File
@@ -1,22 +1,22 @@
from models.group_remind import GroupRemind from typing import List
from nonebot.adapters.cqhttp.message import MessageSegment
from services.log import logger from services.log import logger
from configs.path_config import DATA_PATH from configs.path_config import DATA_PATH
import os
import aiofiles
import aiohttp
from utils.message_builder import image from utils.message_builder import image
from utils.utils import get_local_proxy, get_bot from utils.utils import get_local_proxy, get_bot
from pathlib import Path from pathlib import Path
from configs.config import plugins2info_dict
from models.group_member_info import GroupInfoUser from models.group_member_info import GroupInfoUser
import time
from datetime import datetime from datetime import datetime
from services.db_context import db from services.db_context import db
from models.level_user import LevelUser from models.level_user import LevelUser
from configs.config import ADMIN_DEFAULT_AUTH from configs.config import ADMIN_DEFAULT_AUTH
from utils.static_data import group_manager from utils.manager import group_manager, plugins2settings_manager
from utils.image_utils import CreateImg from utils.image_utils import CreateImg
import aiofiles
import aiohttp
import asyncio import asyncio
import time
import os
try: try:
import ujson as json import ujson as json
@@ -24,70 +24,16 @@ except ModuleNotFoundError:
import json import json
command_dict = { async def group_current_status(group_id: int) -> str:
"早晚安": "zwa", """
"进群欢迎": "hy", 获取当前所有通知的开关
"每日开箱重置提醒": "kxcz", :param group_id: 群号
"b站转发解析": "blpar", """
"epic": "epic", rst = "[被动技能 状态]\n"
"丢人爬": "pa", _data = group_manager.get_task_data()
"原神黄历提醒": "almanac", for task in _data.keys():
} rst += f'{_data[task]}: {"√" if await group_manager.check_group_task_status(group_id, task) else "×"}\n'
command_list = list(command_dict.values()) return rst.strip()
command_info_dt = {
"早晚安": "将会在每晚11:59晚安,在6:01早安哦",
"每日开箱重置提醒": "将会在每日00:01提示开箱重置!",
"epic": "将会在每日中午12:01发送可白嫖的epic游戏",
"原神黄历提醒": "将会在每日8:00发送当日的原神黄历",
}
async def remind_status(group: int, name: str, flag: bool) -> str:
_name = ""
if name in command_dict.values():
_name = list(command_dict.keys())[list(command_dict.values()).index(name)]
if flag:
rst = "开启"
if await GroupRemind.get_status(group, name):
return f"该群已经{rst}过 {_name},请勿重复开启!"
else:
rst = "关闭"
if not await GroupRemind.get_status(group, name):
return f"该群已经{rst}过 {_name},请勿重复开启!"
if await GroupRemind.set_status(group, name, flag):
info = command_info_dt[_name] if command_info_dt.get(_name) else ""
if info:
info = "\n" + info
return f"成功{rst} {_name}!0v0 {info}"
else:
return f"{rst} {_name} 失败了..."
async def set_group_status(name: str, group_id: int):
flag = None
if name[:2] == "开启":
flag = True
elif name[:2] == "关闭":
flag = False
cmd = name[2:]
if cmd in ["全部通知", "所有通知"]:
for command in command_list:
await remind_status(group_id, command, flag)
return f"已{name[:2]}所有通知!"
return await remind_status(group_id, command_dict[cmd], flag)
async def group_current_status(group_id: int):
result = (
f'(被动技能)\n早晚安通知:{"√" if await GroupRemind.get_status(group_id, "zwa") else "×"}\n'
f'进群欢迎:{"√" if await GroupRemind.get_status(group_id, "hy") else "×"}\n'
f'每日开箱重置通知:{"√" if await GroupRemind.get_status(group_id, "kxcz") else "×"}\n'
f'b站转发解析:{"√" if await GroupRemind.get_status(group_id, "blpar") else "×"}\n'
f'丢人爬:{"√" if await GroupRemind.get_status(group_id, "pa") else "×"}\n'
f'epic免费游戏:{"√" if await GroupRemind.get_status(group_id, "epic") else "×"}\n'
f'原神黄历提醒:{"√" if await GroupRemind.get_status(group_id, "almanac") else "×"}'
)
return result
custom_welcome_msg_json = ( custom_welcome_msg_json = (
@@ -95,13 +41,21 @@ custom_welcome_msg_json = (
) )
async def custom_group_welcome(msg, imgs, user_id, group_id): async def custom_group_welcome(
msg: str, imgs: List[str], user_id: int, group_id: int
) -> str:
"""
替换群欢迎消息
:param msg: 欢迎消息文本
:param imgs: 欢迎消息图片,只取第一张
:param user_id: 用户id,用于log记录
:param group_id: 群号
"""
img_result = "" img_result = ""
img = imgs[0] if imgs else "" img = imgs[0] if imgs else ""
result = "" result = ""
if os.path.exists(DATA_PATH + f"custom_welcome_msg/{group_id}.jpg"): if os.path.exists(DATA_PATH + f"custom_welcome_msg/{group_id}.jpg"):
os.remove(DATA_PATH + f"custom_welcome_msg/{group_id}.jpg") os.remove(DATA_PATH + f"custom_welcome_msg/{group_id}.jpg")
# print(custom_welcome_msg_json)
if not custom_welcome_msg_json.exists(): if not custom_welcome_msg_json.exists():
custom_welcome_msg_json.parent.mkdir(parents=True, exist_ok=True) custom_welcome_msg_json.parent.mkdir(parents=True, exist_ok=True)
data = {} data = {}
@@ -133,54 +87,99 @@ async def custom_group_welcome(msg, imgs, user_id, group_id):
return f"替换群欢迎消息成功:\n{result}" + img_result return f"替换群欢迎消息成功:\n{result}" + img_result
def change_group_switch(cmd: str, group_id: int, is_super: bool = False): 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: 是否位超级用户,超级用户用于私聊开关功能状态
"""
if not task_data:
task_data = group_manager.get_task_data()
group_help_file = Path(DATA_PATH) / "group_help" / f"{group_id}.png" group_help_file = Path(DATA_PATH) / "group_help" / f"{group_id}.png"
group_id = str(group_id)
status = cmd[:2] status = cmd[:2]
cmd = cmd[2:] cmd = cmd[2:]
for plugin_cmd in plugins2info_dict.keys(): type_ = 'plugin'
if cmd in plugins2info_dict[plugin_cmd]["cmd"]: modules = plugins2settings_manager.get_plugin_module(cmd, True)
if is_super: if cmd == '全部被动':
plugin_cmd = f'{plugin_cmd}:super' for task in task_data:
if status == "开启": if status == "开启":
if group_manager.get_plugin_status(plugin_cmd, group_id): if not await group_manager.check_group_task_status(group_id, task):
return f"功能 {cmd} 正处于开启状态!不要重复开启." await group_manager.open_group_task(group_id, task)
group_manager.unblock_plugin(plugin_cmd, group_id)
else: else:
if not group_manager.get_plugin_status(plugin_cmd, group_id): if await group_manager.check_group_task_status(group_id, task):
await group_manager.close_group_task(group_id, task)
return f"已 {status} 全部被动技能!"
if cmd in [task_data[x] for x in task_data.keys()]:
type_ = 'task'
modules = [x for x in task_data.keys() if task_data[x] == cmd]
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):
return f"被动 {task_data[module]} 正处于开启状态!不要重复开启."
await 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):
return f"被动 {task_data[module]} 正处于关闭状态!不要重复关闭."
await group_manager.close_group_task(group_id, module)
else:
if not group_manager.get_plugin_status(module, group_id):
return f"功能 {cmd} 正处于关闭状态!不要重复关闭." return f"功能 {cmd} 正处于关闭状态!不要重复关闭."
group_manager.block_plugin(plugin_cmd, group_id) group_manager.block_plugin(module, group_id)
if group_help_file.exists(): if group_help_file.exists():
group_help_file.unlink() group_help_file.unlink()
return f"{status} {cmd} 功能!" return f"{status} {cmd} 功能!"
return f"没有找到 {cmd} 功能..."
def set_plugin_status(cmd: str, block_type: str = "all"): def set_plugin_status(cmd: str, block_type: str = "all"):
"""
设置插件功能状态(超级用户使用)
:param cmd: 功能名称
:param block_type: 限制类型, 'all': 私聊+群里, 'private': 私聊, 'group': 群聊
"""
status = cmd[:2] status = cmd[:2]
cmd = cmd[2:] cmd = cmd[2:]
for plugin_cmd in plugins2info_dict.keys(): module = plugins2settings_manager.get_plugin_module(cmd)
if cmd in plugins2info_dict[plugin_cmd]["cmd"]: if status == "开启":
if status == "开启": group_manager.unblock_plugin(module)
group_manager.unblock_plugin(plugin_cmd) else:
else: group_manager.block_plugin(module, block_type=block_type)
group_manager.block_plugin(plugin_cmd, block_type=block_type)
break
async def get_plugin_status(): async def get_plugin_status():
"""
获取功能状态
"""
return await asyncio.get_event_loop().run_in_executor(None, _get_plugin_status) return await asyncio.get_event_loop().run_in_executor(None, _get_plugin_status)
def _get_plugin_status(): def _get_plugin_status() -> MessageSegment:
rst = '\t功能\n' """
flag_str = '状态'.rjust(4) + '\n' 合成功能状态图片
for plugin_cmd in plugins2info_dict: """
flag = group_manager.get_plugin_block_type(plugin_cmd) rst = "\t功能\n"
flag = flag.upper() + ' CLOSE' if flag else 'OPEN' flag_str = "状态".rjust(4) + "\n"
rst += f'{plugins2info_dict[plugin_cmd]["cmd"][0]}\n' for module in plugins2settings_manager.get_data():
flag_str += f'{flag}\n' flag = group_manager.get_plugin_block_type(module)
height = len(rst.split('\n')) * 24 flag = flag.upper() + " CLOSE" if flag else "OPEN"
try:
rst += f'{plugins2settings_manager.get(module)["cmd"][0]}\n'
except IndexError:
rst += f"{module}\n"
flag_str += f"{flag}\n"
height = len(rst.split("\n")) * 24
a = CreateImg(150, height, font_size=20) a = CreateImg(150, height, font_size=20)
a.text((10, 10), rst) a.text((10, 10), rst)
b = CreateImg(200, height, font_size=20) b = CreateImg(200, height, font_size=20)
@@ -191,7 +190,12 @@ def _get_plugin_status():
return image(b64=A.pic2bs4()) return image(b64=A.pic2bs4())
async def update_member_info(group_id: int) -> bool: async def update_member_info(group_id: int, remind_superuser: bool = False) -> bool:
"""
更新群成员信息
:param group_id: 群号
:param remind_superuser: 失败信息提醒超级用户
"""
bot = get_bot() bot = get_bot()
_group_user_list = await bot.get_group_member_list(group_id=group_id) _group_user_list = await bot.get_group_member_list(group_id=group_id)
_error_member_list = [] _error_member_list = []
@@ -259,7 +263,7 @@ async def update_member_info(group_id: int) -> bool:
logger.info(f"退群用户{del_user} 所属{group_id} 已删除") logger.info(f"退群用户{del_user} 所属{group_id} 已删除")
else: else:
logger.info(f"退群用户{del_user} 所属{group_id} 删除失败") logger.info(f"退群用户{del_user} 所属{group_id} 删除失败")
if _error_member_list: if _error_member_list and remind_superuser:
result = "" result = ""
for error_user in _error_member_list: for error_user in _error_member_list:
result += error_user result += error_user
@@ -1,57 +0,0 @@
from nonebot.typing import T_State
from nonebot.adapters.cqhttp import Bot, GroupMessageEvent
from .data_source import set_group_status, group_current_status
from nonebot.adapters.cqhttp.permission import GROUP
from services.log import logger
from nonebot import on_command
__plugin_name__ = "群通知开关"
__plugin_usage__ = """
示例:
开启早晚安
关闭早晚安
"""
group_status = on_command(
"oc_reminds",
aliases={
"开启早晚安",
"关闭早晚安",
"开启进群欢迎",
"关闭进群欢迎",
"开启每日开箱重置提醒",
"关闭每日开箱重置提醒",
"开启b站转发解析",
"关闭b站转发解析",
"开启epic通知",
"关闭epic通知",
"开启丢人爬",
"关闭丢人爬",
"开启原神黄历提醒",
"关闭原神黄历提醒",
"开启全部通知",
"开启所有通知",
"关闭全部通知",
"关闭所有通知",
"群通知状态",
},
permission=GROUP,
priority=5,
block=True,
)
@group_status.handle()
async def _(bot: Bot, event: GroupMessageEvent, state: T_State):
if state["_prefix"]["raw_command"] in ["群通知状态"]:
await group_status.finish(await group_current_status(event.group_id))
await group_status.send(
await set_group_status(state["_prefix"]["raw_command"], event.group_id),
at_sender=True,
)
logger.info(
f'USER {event.user_id} GROUP {event.group_id} 使用群通知管理命令 {state["_prefix"]["raw_command"]}'
)
+42
View File
@@ -0,0 +1,42 @@
from nonebot.adapters.cqhttp import Bot, Event
from nonebot.typing import T_State
from utils.manager import group_manager, plugins2settings_manager
from utils.utils import get_message_text
from services.log import logger
cmd = []
def switch_rule(bot: Bot, event: Event, state: T_State) -> bool:
"""
检测文本是否是关闭功能命令
:param bot: pass
:param event: pass
:param state: pass
"""
try:
if not cmd:
cmd.append('关闭全部被动')
cmd.append('开启全部被动')
_data = group_manager.get_task_data()
for key in _data:
cmd.append(f"开启{_data[key]}")
cmd.append(f"关闭{_data[key]}")
cmd.append(f"开启 {_data[key]}")
cmd.append(f"关闭 {_data[key]}")
_data = plugins2settings_manager.get_data()
for key in _data:
try:
for x in _data[key]["cmd"]:
cmd.append(f"开启{x}")
cmd.append(f"关闭{x}")
cmd.append(f"开启 {x}")
cmd.append(f"关闭 {x}")
except KeyError:
pass
msg = get_message_text(event.json()).split()
msg = msg[0] if msg else ""
return msg in cmd
except Exception as e:
logger.error(f"检测是否为功能开关命令发生错误 {type(e)}: {e}")
return False
+83 -54
View File
@@ -1,77 +1,106 @@
from nonebot import on_command from nonebot import on_command, on_message
from nonebot.typing import T_State from nonebot.typing import T_State
from nonebot.adapters.cqhttp import Bot, GroupMessageEvent, MessageEvent from nonebot.adapters.cqhttp import Bot, GroupMessageEvent, MessageEvent, GROUP
from .data_source import change_group_switch, set_plugin_status, get_plugin_status from .data_source import (
change_group_switch,
set_plugin_status,
get_plugin_status,
group_current_status,
)
from services.log import logger from services.log import logger
from configs.config import plugins2info_dict, NICKNAME from configs.config import NICKNAME, OC_LEVEL
from utils.utils import get_message_text, is_number from utils.utils import get_message_text, is_number
from nonebot.permission import SUPERUSER from nonebot.permission import SUPERUSER
from .rule import switch_rule
__plugin_name__ = "群功能开关" __zx_plugin_name__ = "群功能开关 [Admin]"
__plugin_usage__ = """ __plugin_usage__ = """
示例: usage:
开启色图 群内功能与被动技能开关
关闭色图 指令:
""" 开启/关闭[功能]
群被动状态
开启全部被动
关闭全部被动
示例:开启/关闭色图
""".strip()
__plugin_superuser_usage__ = """
usage:
功能总开关与指定群禁用
指令:
功能状态
开启/关闭[功能] [group]
开启/关闭[功能] ['private'/'group']
""".strip()
__plugin_des__ = "群内功能开关"
__plugin_cmd__ = [
"开启/关闭[功能]",
"群被动状态",
"开启全部被动",
"关闭全部被动",
"功能状态 [_superuser]",
"开启/关闭[功能] [group] [_superuser]",
"开启/关闭[功能] ['private'/'group'] [_superuser]",
]
__plugin_version__ = 0.1
__plugin_author__ = "HibiKier"
__plugin_settings__ = {
"admin_level": OC_LEVEL,
}
switch_rule_matcher = on_message(rule=switch_rule, priority=4, block=True)
plugins_status = on_command("功能状态", permission=SUPERUSER, priority=5, block=True)
group_task_status = on_command("群被动状态", permission=GROUP, priority=5, block=True)
cmds = [] @switch_rule_matcher.handle()
for cmd_list in plugins2info_dict.values():
for cmd in cmd_list["cmd"]:
cmds.append(f"开启{cmd}")
cmds.append(f"关闭{cmd}")
cmds = set(cmds)
switch_rule = on_command(
"switch_rule", aliases=cmds, priority=5, block=True
)
plugins_status = on_command('功能状态', permission=SUPERUSER, priority=5, block=True)
@switch_rule.handle()
async def _(bot: Bot, event: MessageEvent, state: T_State): async def _(bot: Bot, event: MessageEvent, state: T_State):
_cmd = get_message_text(event.json()).split()[0]
if isinstance(event, GroupMessageEvent): if isinstance(event, GroupMessageEvent):
await switch_rule.send( await switch_rule_matcher.send(await change_group_switch(_cmd, event.group_id))
change_group_switch( logger.info(f"USER {event.user_id} GROUP {event.group_id} 使用群功能管理命令 {_cmd}")
state["_prefix"]["raw_command"].strip(), event.group_id
)
)
logger.info(
f'USER {event.user_id} GROUP {event.group_id} 使用群功能管理命令 {state["_prefix"]["raw_command"]}'
)
else: else:
if str(event.user_id) in bot.config.superusers: if str(event.user_id) in bot.config.superusers:
block_type = get_message_text(event.json()) block_type = " ".join(get_message_text(event.json()).split()[1:])
_cmd = state["_prefix"]["raw_command"].strip() block_type = block_type if block_type else "a"
if is_number(block_type): if is_number(block_type):
if not int(block_type) in [g["group_id"] for g in await bot.get_group_list(self_id=int(bot.self_id))]: if not int(block_type) in [
await switch_rule.finish(f'{NICKNAME}未加入群聊:{block_type}') g["group_id"]
change_group_switch(_cmd, int(block_type), True) for g in await bot.get_group_list(self_id=int(bot.self_id))
group_name = (await bot.get_group_info(group_id=int(block_type)))['group_name'] ]:
await switch_rule.send(f'已禁用群聊 {group_name}({block_type}) 的 {_cmd[2:]} 功能') await switch_rule_matcher.finish(f"{NICKNAME}未加入群聊:{block_type}")
else: await change_group_switch(_cmd, int(block_type), True)
if block_type not in ['all', 'private', 'group', 'a', 'p', 'g']: group_name = (await bot.get_group_info(group_id=int(block_type)))[
block_type = 'all' "group_name"
block_type = 'all' if block_type == 'a' else block_type ]
block_type = 'private' if block_type == 'p' else block_type await switch_rule_matcher.send(
block_type = 'group' if block_type == 'g' else block_type f"已禁用群聊 {group_name}({block_type}) 的 {_cmd[2:]} 功能"
)
elif block_type in ["all", "private", "group", "a", "p", "g"]:
block_type = "all" if block_type == "a" else block_type
block_type = "private" if block_type == "p" else block_type
block_type = "group" if block_type == "g" else block_type
set_plugin_status(_cmd, block_type) set_plugin_status(_cmd, block_type)
if block_type == 'all': if block_type == "all":
await switch_rule.send(f'已{_cmd[:2]}功能:{_cmd[2:]}') await switch_rule_matcher.send(f"已{_cmd[:2]}功能:{_cmd[2:]}")
elif block_type == 'private': elif block_type == "private":
await switch_rule.send(f'已在私聊中{_cmd[:2]}功能:{_cmd[2:]}') await switch_rule_matcher.send(f"已在私聊中{_cmd[:2]}功能:{_cmd[2:]}")
else: else:
await switch_rule.send(f'已在群聊中{_cmd[:2]}功能:{_cmd[2:]}') await switch_rule_matcher.send(f"已在群聊中{_cmd[:2]}功能:{_cmd[2:]}")
logger.info( else:
f'USER {event.user_id} 使用功能管理命令 {state["_prefix"]["raw_command"]} | {block_type}' await switch_rule_matcher.finish("格式错误:关闭[功能] [group]/[p/g]")
) logger.info(f"USER {event.user_id} 使用功能管理命令 {_cmd} | {block_type}")
@plugins_status.handle() @plugins_status.handle()
async def _(bot: Bot, event: MessageEvent, state: T_State): async def _(bot: Bot, event: MessageEvent, state: T_State):
await plugins_status.send(await get_plugin_status()) await plugins_status.send(await get_plugin_status())
@group_task_status.handle()
async def _(bot: Bot, event: GroupMessageEvent, state: T_State):
await group_task_status.send(await group_current_status(event.group_id))
+24 -15
View File
@@ -5,6 +5,13 @@ from models.group_info import GroupInfo
from asyncpg.exceptions import ConnectionDoesNotExistError from asyncpg.exceptions import ConnectionDoesNotExistError
__zx_plugin_name__ = '管理方面定时任务 [Hidden]'
__plugin_usage__ = '无'
__plugin_des__ = '成员信息和管理权限的定时更新'
__plugin_version__ = 0.1
__plugin_author__ = 'HibiKier'
# 自动更新群员信息 # 自动更新群员信息
@scheduler.scheduled_job( @scheduler.scheduled_job(
"cron", "cron",
@@ -13,14 +20,15 @@ from asyncpg.exceptions import ConnectionDoesNotExistError
) )
async def _(): async def _():
bot = get_bot() bot = get_bot()
gl = await bot.get_group_list(self_id=bot.self_id) if bot:
gl = [g["group_id"] for g in gl] gl = await bot.get_group_list()
for g in gl: gl = [g["group_id"] for g in gl]
try: for g in gl:
await update_member_info(g) try:
logger.info(f"更新群组 g:{g} 成功") await update_member_info(g)
except Exception as e: logger.info(f"更新群组 g:{g} 成功")
logger.error(f"更新群组错误 g:{g} e:{e}") except Exception as e:
logger.error(f"更新群组错误 g:{g} e:{e}")
# 快速更新群员信息以及管理员权限 # 快速更新群员信息以及管理员权限
@@ -31,12 +39,13 @@ async def _():
async def _(): async def _():
try: try:
bot = get_bot() bot = get_bot()
gl = await bot.get_group_list(self_id=bot.self_id) if bot:
gl = [g["group_id"] for g in gl] gl = await bot.get_group_list()
all_group = [x.group_id for x in await GroupInfo.get_all_group()] gl = [g["group_id"] for g in gl]
for g in gl: all_group = [x.group_id for x in await GroupInfo.get_all_group()]
if g not in all_group: for g in gl:
await update_member_info(g) if g not in all_group:
logger.info(f"快速更新群信息以及权限:{g}") await update_member_info(g, False)
logger.info(f"快速更新群信息以及权限:{g}")
except (IndexError, ConnectionDoesNotExistError): except (IndexError, ConnectionDoesNotExistError):
pass pass
@@ -1,19 +1,23 @@
from nonebot import on_command, export from nonebot import on_command, on_notice
from nonebot.typing import T_State from nonebot.typing import T_State
from nonebot.adapters.cqhttp import Bot, GroupMessageEvent, GROUP from nonebot.adapters.cqhttp import Bot, GroupMessageEvent, GROUP, GroupIncreaseNoticeEvent
from .data_source import update_member_info from .data_source import update_member_info
__plugin_name__ = "更新群组成员列表" __zx_plugin_name__ = "更新群组成员列表 [Admin]"
__plugin_usage__ = """ __plugin_usage__ = """
说明: usage:
更新群组成员的基本信息 更新群组成员的基本信息
示例: 指令:
更新群组成员列表 更新群组成员列表/更新群组成员信息
""" """.strip()
__plugin_des__ = '更新群组成员列表'
__plugin_cmd__ = ['更新群组成员列表']
__plugin_version__ = 0.1
__plugin_author__ = 'HibiKier'
__plugin_settings__ = {
"admin_level": 1,
}
export = export()
export.update_member_info = update_member_info
refresh_member_group = on_command( refresh_member_group = on_command(
"更新群组成员列表", aliases={"更新群组成员信息"}, permission=GROUP, priority=5, block=True "更新群组成员列表", aliases={"更新群组成员信息"}, permission=GROUP, priority=5, block=True
@@ -26,3 +30,12 @@ async def _(bot: Bot, event: GroupMessageEvent, state: T_State):
await refresh_member_group.finish("更新群员信息成功!", at_sender=True) await refresh_member_group.finish("更新群员信息成功!", at_sender=True)
else: else:
await refresh_member_group.finish("更新群员信息失败!", at_sender=True) await refresh_member_group.finish("更新群员信息失败!", at_sender=True)
group_increase_handle = on_notice(priority=1, block=False)
@group_increase_handle.handle()
async def _(bot: Bot, event: GroupIncreaseNoticeEvent, state: dict):
if event.user_id == int(bot.self_id):
await update_member_info(event.group_id)
+3 -3
View File
@@ -7,9 +7,9 @@ from models.group_member_info import GroupInfoUser
from configs.config import ADMIN_DEFAULT_AUTH from configs.config import ADMIN_DEFAULT_AUTH
__plugin_name__ = "群管理员监测" __zx_plugin_name__ = "群管理员变动监测 [Hidden]"
__plugin_version__ = 0.1
__plugin_usage__ = "无" __plugin_author__ = "HibiKier"
admin_notice = on_notice(priority=5) admin_notice = on_notice(priority=5)
+12 -35
View File
@@ -2,49 +2,26 @@ from nonebot import on_command
from nonebot.typing import T_State from nonebot.typing import T_State
from nonebot.adapters import Bot from nonebot.adapters import Bot
from nonebot.adapters.cqhttp import GroupMessageEvent from nonebot.adapters.cqhttp import GroupMessageEvent
from utils.image_utils import CreateImg
from configs.path_config import IMAGE_PATH
from utils.message_builder import image from utils.message_builder import image
from configs.config import NICKNAME from .data_source import create_help_image, admin_help_image
__plugin_name__ = '管理帮助 [Hidden]' __zx_plugin_name__ = '管理帮助 [Admin]'
__plugin_usage__ = f'''管理帮助(权限等级): __plugin_usage__ = '管理员帮助,在群内回复“管理员帮助”'
1.更新群组成员列表(1) __plugin_version__ = 0.1
2.功能开关 --> 指令:开启/关闭xx功能(2) __plugin_author__ = 'HibiKier'
3.查看群被动技能 --> 指令:群通知状态(2) __plugin_settings__ = {
4.自定义群欢迎 --> 指令:自定义进群欢迎消息(2) "admin_level": 1,
5.将用户拉入{NICKNAME}黑名单 --> .ban/.unban(5) }
6.刷屏禁言相关 --> 指令:刷屏检测设置/设置检测时间
\t\t/设置检测次数/设置禁言时长(5)
8.上传图片/连续上传图片(6)
9.移动图片(7)
10.删除图片(7)
对我说 “{NICKNAME}帮助 指令” 获取对应详细帮助
群主与管理员默认 5 级权限
'''
passive_help = '''【被动技能开关(2):
开启/关闭早晚安
开启/关闭进群欢迎
开启/关闭每日开箱重置提醒
开启/关闭b站转发解析
开启/关闭丢人爬
开启/关闭epic通知
开启/关闭原神黄历提醒
开启/关闭全部通知】
'''
admin_help = on_command("管理员帮助", aliases={"管理帮助"}, priority=5, block=True) admin_help = on_command("管理员帮助", aliases={"管理帮助"}, priority=5, block=True)
admin_help_img = CreateImg(1000, 600, font_size=24) if admin_help_image.exists():
admin_help_img.text((10, 10), __plugin_usage__) admin_help_image.unlink()
text_img = CreateImg(450, 600, font_size=24)
text_img.text((0, 0), passive_help)
admin_help_img.paste(text_img, (650, 50))
admin_help_img.save(IMAGE_PATH + 'admin_help_img.png')
@admin_help.handle() @admin_help.handle()
async def _(bot: Bot, event: GroupMessageEvent, state: T_State): async def _(bot: Bot, event: GroupMessageEvent, state: T_State):
if not admin_help_image.exists():
await create_help_image()
await admin_help.send(image('admin_help_img.png')) await admin_help.send(image('admin_help_img.png'))
+91
View File
@@ -0,0 +1,91 @@
from utils.image_utils import CreateImg
from configs.path_config import IMAGE_PATH
from services.log import logger
from utils.utils import get_matchers
from utils.manager import group_manager
from nonebot.adapters.cqhttp import Bot
from pathlib import Path
from nonebot import Driver
import asyncio
import nonebot
driver: Driver = nonebot.get_driver()
background = Path(IMAGE_PATH) / "background" / "0.png"
admin_help_image = Path(IMAGE_PATH) / 'admin_help_img.png'
@driver.on_bot_connect
async def create_help_image(bot: Bot = None):
"""
创建管理员帮助图片
"""
if not group_manager.get_task_data():
await group_manager.init_group_task()
logger.info(f'已成功加载 {len(group_manager.get_task_data())} 个被动技能.')
await asyncio.get_event_loop().run_in_executor(
None, _create_help_image
)
def _create_help_image():
"""
创建管理员帮助图片
"""
_matchers = get_matchers()
_plugin_name_list = []
width = 0
_plugin_level = {}
for matcher in _matchers:
_plugin = nonebot.plugin.get_plugin(matcher.module)
_module = _plugin.module
try:
plugin_name = _module.__getattribute__("__zx_plugin_name__")
except AttributeError:
continue
try:
if (
"[admin]" in plugin_name.lower()
and plugin_name not in _plugin_name_list
and plugin_name != "管理帮助 [Admin]"
):
_plugin_name_list.append(plugin_name)
plugin_settings = _module.__getattribute__("__plugin_settings__")
plugin_des = _module.__getattribute__("__plugin_des__")
plugin_cmd = _module.__getattribute__("__plugin_cmd__")
plugin_cmd = [x for x in plugin_cmd if "[_superuser]" not in x]
admin_level = int(plugin_settings["admin_level"])
if _plugin_level.get(admin_level):
_plugin_level[admin_level].append(
f"[{admin_level}] {plugin_des} -> " + " / ".join(plugin_cmd)
)
else:
_plugin_level[admin_level] = [
f"[{admin_level}] {plugin_des} -> " + " / ".join(plugin_cmd)
]
x = len(f"[{admin_level}] {plugin_des} -> " + " / ".join(plugin_cmd)) * 23
width = width if width > x else x
except AttributeError:
logger.warning(f"获取管理插件 {matcher.module}: {plugin_name} 设置失败...")
help_str = "* 注: ‘*’ 代表可有多个相同参数 ‘?’ 代表可省略该参数 *\n\n" \
"[权限等级] 管理员帮助:\n\n"
x = list(_plugin_level.keys())
x.sort()
for level in x:
for help_ in _plugin_level[level]:
help_str += f"\t{help_}\n\n"
help_str += '-----[被动技能开关]-----\n\n'
task_data = group_manager.get_task_data()
for i, x in enumerate(task_data.keys()):
help_str += f'{i+1}.开启/关闭{task_data[x]}\n\n'
height = len(help_str.split("\n")) * 33
A = CreateImg(width, height, font_size=24)
_background = CreateImg(width, height, background=background)
A.text((150, 110), help_str)
A.paste(_background, alpha=True)
A.save(admin_help_image)
logger.info(f'已成功加载 {len(_plugin_name_list)} 条管理员命令')
+29 -16
View File
@@ -1,20 +1,33 @@
from .data_source import get_chat_result, hello, no_result
from services.log import logger
from nonebot import on_message from nonebot import on_message
from nonebot.rule import to_me
from nonebot.typing import T_State
from nonebot.adapters.cqhttp import ( from nonebot.adapters.cqhttp import (
Bot, Bot,
PrivateMessageEvent, GroupMessageEvent,
Message, Message,
MessageEvent, MessageEvent,
) )
from utils.utils import get_message_text, get_message_imgs from nonebot.rule import to_me
from nonebot.typing import T_State
from models.friend_user import FriendUser from models.friend_user import FriendUser
from models.group_member_info import GroupInfoUser from models.group_member_info import GroupInfoUser
from services.log import logger
from utils.utils import get_message_text, get_message_imgs
from .data_source import get_chat_result, hello, no_result
from configs.config import NICKNAME
__plugin_name__ = "AI [Hidden]" __zx_plugin_name__ = "AI"
__plugin_usage__ = f"""
usage:
与{NICKNAME}普普通通的对话吧!
"""
__plugin_version__ = 0.1
__plugin_author__ = 'HibiKier'
__plugin_settings__ = {
"level": 5,
"default_status": True,
"limit_superuser": False,
"cmd": ["Ai", "ai", "AI", "aI"],
}
ai = on_message(rule=to_me(), priority=8) ai = on_message(rule=to_me(), priority=8)
@@ -38,21 +51,21 @@ async def _(bot: Bot, event: MessageEvent, state: T_State):
]: ]:
await ai.finish(hello()) await ai.finish(hello())
img = imgs[0] if imgs else "" img = imgs[0] if imgs else ""
if isinstance(event, PrivateMessageEvent): if isinstance(event, GroupMessageEvent):
nickname = await FriendUser.get_friend_nickname(event.user_id)
else:
nickname = await GroupInfoUser.get_group_member_nickname( nickname = await GroupInfoUser.get_group_member_nickname(
event.user_id, event.group_id event.user_id, event.group_id
) )
else:
nickname = await FriendUser.get_friend_nickname(event.user_id)
if not nickname: if not nickname:
if isinstance(event, PrivateMessageEvent): if isinstance(event, GroupMessageEvent):
nickname = event.sender.nickname
else:
nickname = event.sender.card if event.sender.card else event.sender.nickname nickname = event.sender.card if event.sender.card else event.sender.nickname
else:
nickname = event.sender.nickname
result = await get_chat_result(msg, img, event.user_id, nickname) result = await get_chat_result(msg, img, event.user_id, nickname)
logger.info( logger.info(
f"USER {event.user_id} GROUP {event.group_id if not isinstance(event, PrivateMessageEvent) else ''} " f"USER {event.user_id} GROUP {event.group_id if isinstance(event, GroupMessageEvent) else ''} "
f"问题:{msg}\n回答:{result}" f"问题:{msg} ---- 回答:{result}"
) )
if result: if result:
await ai.finish(Message(result)) await ai.finish(Message(result))
+55 -17
View File
@@ -1,19 +1,21 @@
import os
import random
import re
import aiohttp
from aiohttp.client import ClientSession
from configs.config import TL_KEY, ALAPI_TOKEN, ALAPI_AI_CHECK, NICKNAME from configs.config import TL_KEY, ALAPI_TOKEN, ALAPI_AI_CHECK, NICKNAME
from configs.path_config import IMAGE_PATH, DATA_PATH from configs.path_config import IMAGE_PATH, DATA_PATH
from aiohttp.client import ClientSession
from services.log import logger from services.log import logger
from utils.message_builder import image, face from utils.message_builder import image, face
from utils.utils import get_bot
import re
import aiohttp
import random
import os
try: try:
import ujson as json import ujson as json
except ModuleNotFoundError: except ModuleNotFoundError:
import json import json
url = "http://openapi.tuling123.com/openapi/api/v2" url = "http://openapi.tuling123.com/openapi/api/v2"
check_url = "https://v2.alapi.cn/api/censor/text" check_url = "https://v2.alapi.cn/api/censor/text"
@@ -23,8 +25,15 @@ index = 0
anime_data = json.load(open(DATA_PATH + "anime.json", "r", encoding="utf8")) anime_data = json.load(open(DATA_PATH + "anime.json", "r", encoding="utf8"))
# 图灵AI
async def get_chat_result(text: str, img_url: str, user_id: int, nickname: str) -> str: async def get_chat_result(text: str, img_url: str, user_id: int, nickname: str) -> str:
"""
获取 AI 返回值,顺序:图灵 -> 青云客
:param text: 问题
:param img_url: 图片链接
:param user_id: 用户id
:param nickname: 用户昵称
:return: 回答
"""
global index global index
if index == 5: if index == 5:
index = 0 index = 0
@@ -51,7 +60,15 @@ async def get_chat_result(text: str, img_url: str, user_id: int, nickname: str)
# 图灵接口 # 图灵接口
async def tu_ling(text: str, img_url: str, user_id: int, sess: ClientSession): async def tu_ling(text: str, img_url: str, user_id: int, sess: ClientSession) -> str:
"""
获取图灵接口的回复
:param text: 问题
:param img_url: 图片链接
:param user_id: 用户id
:param sess: AIOHTTP SESSION
:return: 图灵回复
"""
global index global index
try: try:
if text: if text:
@@ -95,7 +112,13 @@ async def tu_ling(text: str, img_url: str, user_id: int, sess: ClientSession):
# 屑 AI # 屑 AI
async def xie_ai(text: str, sess: ClientSession): async def xie_ai(text: str, sess: ClientSession) -> str:
"""
获取青云客回复
:param text: 问题
:param sess: AIOHTTP SESSION
:return: 青云可回复
"""
async with sess.get( async with sess.get(
f"http://api.qingyunke.com/api.php?key=free&appid=0&msg={text}" f"http://api.qingyunke.com/api.php?key=free&appid=0&msg={text}"
) as res: ) as res:
@@ -104,7 +127,9 @@ async def xie_ai(text: str, sess: ClientSession):
if data["result"] == 0: if data["result"] == 0:
content = data["content"] content = data["content"]
if "菲菲" in content: if "菲菲" in content:
content = content.replace("菲菲", f"{NICKNAME}") content = content.replace("菲菲", NICKNAME)
if "艳儿" in content:
content = content.replace("艳儿", NICKNAME)
if "公众号" in content: if "公众号" in content:
content = "" content = ""
if "{br}" in content: if "{br}" in content:
@@ -122,16 +147,22 @@ async def xie_ai(text: str, sess: ClientSession):
) )
else: else:
break break
return content if not content and not ALAPI_AI_CHECK else await check_text(content, sess) return (
content
if not content and not ALAPI_AI_CHECK
else await check_text(content, sess)
)
# 打招呼内容
def hello() -> str: def hello() -> str:
"""
一些打招呼的内容
"""
result = random.choice( result = random.choice(
( (
"哦豁?!", "哦豁?!",
"你好!Ov<", "你好!Ov<",
f"库库库,呼唤{list(get_bot().config.nickname)[0]}做什么呢", f"库库库,呼唤{NICKNAME}做什么呢",
"我在呢!", "我在呢!",
"呼呼,叫俺干嘛", "呼呼,叫俺干嘛",
) )
@@ -146,6 +177,9 @@ def hello() -> str:
# 没有回答时回复内容 # 没有回答时回复内容
def no_result() -> str: def no_result() -> str:
"""
没有回答时的回复
"""
return ( return (
random.choice( random.choice(
[ [
@@ -160,8 +194,12 @@ def no_result() -> str:
) )
# 检测屑AI回复的文本是否是 *话
async def check_text(text: str, sess: ClientSession) -> str: async def check_text(text: str, sess: ClientSession) -> str:
"""
ALAPI文本检测,主要针对青云客API,检测为恶俗文本改为无回复的回答
:param text: 回复
:param sess: AIOHTTP SESSION
"""
if not ALAPI_TOKEN: if not ALAPI_TOKEN:
return text return text
params = {"token": ALAPI_TOKEN, "text": text} params = {"token": ALAPI_TOKEN, "text": text}
@@ -169,8 +207,8 @@ async def check_text(text: str, sess: ClientSession) -> str:
async with sess.get(check_url, timeout=2, params=params) as response: async with sess.get(check_url, timeout=2, params=params) as response:
data = await response.json() data = await response.json()
if data["code"] == 200: if data["code"] == 200:
if data["conclusion_type"] == 2: if data["data"]["conclusion_type"] == 2:
return '' return ""
except Exception as e: except Exception as e:
logger.error(f"检测违规文本错误...e:{e}") logger.error(f"检测违规文本错误...{type(e)}:{e}")
return text return text
+3
View File
@@ -0,0 +1,3 @@
import nonebot
nonebot.load_plugins("plugins/alapi")
+30 -18
View File
@@ -5,32 +5,44 @@ from configs.config import ALAPI_TOKEN
from .data_source import get_data from .data_source import get_data
from services.log import logger from services.log import logger
__plugin_name__ = '网易云热评' __zx_plugin_name__ = "网易云热评"
__plugin_usage__ = '用法: 生了个人,我很抱歉' __plugin_usage__ = """
usage:
comments_163 = on_command("网易云热评", aliases={'网易云评论', '到点了', '12点了'}, priority=5, block=True) 到点了,还是防不了下塔
指令:
网易云热评/到点了/12点了
""".strip()
__plugin_des__ = "生了个人,我很抱歉"
__plugin_cmd__ = ["网易云热评", "到点了", "12点了"]
__plugin_version__ = 0.1
__plugin_author__ = "HibiKier"
__plugin_settings__ = {
"level": 5,
"default_status": True,
"limit_superuser": False,
"cmd": ["网易云热评", "网易云评论", "到点了", "12点了"],
}
comments_163_url = 'https://v2.alapi.cn/api/comment' comments_163 = on_command(
"网易云热评", aliases={"网易云评论", "到点了", "12点了"}, priority=5, block=True
)
comments_163_url = "https://v2.alapi.cn/api/comment"
@comments_163.handle() @comments_163.handle()
async def _(bot: Bot, event: MessageEvent, state: T_State): async def _(bot: Bot, event: MessageEvent, state: T_State):
params = { params = {"token": ALAPI_TOKEN}
'token': ALAPI_TOKEN
}
data, code = await get_data(comments_163_url, params) data, code = await get_data(comments_163_url, params)
if code != 200: if code != 200:
await comments_163.finish(data, at_sender=True) await comments_163.finish(data, at_sender=True)
data = data['data'] data = data["data"]
comment = data['comment_content'] comment = data["comment_content"]
song_name = data['title'] song_name = data["title"]
await comments_163.send(f'{comment}\n\t——《{song_name}》') await comments_163.send(f"{comment}\n\t——《{song_name}》")
logger.info( logger.info(
f"(USER {event.user_id}, GROUP {event.group_id if isinstance(event, GroupMessageEvent) else 'private'})" f"(USER {event.user_id}, GROUP {event.group_id if isinstance(event, GroupMessageEvent) else 'private'})"
f" 发送网易云热评: {comment} \n\t\t————{song_name}") f" 发送网易云热评: {comment} \n\t\t————{song_name}"
)
+16 -3
View File
@@ -7,9 +7,22 @@ from utils.utils import get_message_text
from .data_source import get_data from .data_source import get_data
from services.log import logger from services.log import logger
__plugin_name__ = 'b封面' __zx_plugin_name__ = 'b封面'
__plugin_usage__ = '用法: b封面 (链接,av,bv,cv,直播id)\n\t' \ __plugin_usage__ = """usage:
'示例:b封面 av86863038' b封面 [链接/av/bv/cv/直播id]
示例:b封面 av86863038
"""
__plugin_des__ = '快捷的b站视频封面获取方式'
__plugin_cmd__ = ['b封面/B封面']
__plugin_type__ = ('一些工具',)
__plugin_version__ = 0.1
__plugin_author__ = 'HibiKier'
__plugin_settings__ = {
"level": 5,
"default_status": True,
"limit_superuser": False,
"cmd": ["b封面", 'B封面'],
}
cover = on_command('b封面', aliases={'B封面'}, priority=5, block=True) cover = on_command('b封面', aliases={'B封面'}, priority=5, block=True)
+12 -2
View File
@@ -2,10 +2,16 @@ from nonebot.adapters.cqhttp import MessageSegment
from utils.image_utils import CreateImg from utils.image_utils import CreateImg
from utils.message_builder import image from utils.message_builder import image
from configs.path_config import IMAGE_PATH from configs.path_config import IMAGE_PATH
from typing import Union
import aiohttp import aiohttp
async def get_data(url: str, params: dict): async def get_data(url: str, params: dict) -> 'Union[dict, str], int':
"""
获取ALAPI数据
:param url: 请求链接
:param params: 参数
"""
async with aiohttp.ClientSession() as session: async with aiohttp.ClientSession() as session:
try: try:
async with session.get( async with session.get(
@@ -22,7 +28,11 @@ async def get_data(url: str, params: dict):
return "超时了....", 998 return "超时了....", 998
def gen_wbtop_pic(data) -> MessageSegment: def gen_wbtop_pic(data: dict) -> MessageSegment:
"""
生成微博热搜图片
:param data: 微博热搜数据
"""
bk = CreateImg(700, 32 * 50 + 280, 700, 32, color='#797979') bk = CreateImg(700, 32 * 50 + 280, 700, 32, color='#797979')
wbtop_bk = CreateImg(700, 280, background=f'{IMAGE_PATH}/other/webtop.png') wbtop_bk = CreateImg(700, 280, background=f'{IMAGE_PATH}/other/webtop.png')
bk.paste(wbtop_bk) bk.paste(wbtop_bk)
+15 -3
View File
@@ -5,9 +5,21 @@ from configs.config import ALAPI_TOKEN
from services.log import logger from services.log import logger
from .data_source import get_data from .data_source import get_data
__plugin_name__ = '古诗' __zx_plugin_name__ = '古诗'
__plugin_usage__ = '用法: 无' __plugin_usage__ = """usage:
平白无故念首诗
示例:念诗/来首诗/念首诗
"""
__plugin_des__ = '为什么突然文艺起来了!'
__plugin_cmd__ = ['念诗/来首诗/念首诗']
__plugin_version__ = 0.1
__plugin_author__ = 'HibiKier'
__plugin_settings__ = {
"level": 5,
"default_status": True,
"limit_superuser": False,
"cmd": ['念诗', '来首诗', '念首诗'],
}
poetry = on_command("念诗", aliases={'来首诗', '念首诗'}, priority=5, block=True) poetry = on_command("念诗", aliases={'来首诗', '念首诗'}, priority=5, block=True)
+27 -10
View File
@@ -10,9 +10,25 @@ from configs.path_config import IMAGE_PATH
from utils.message_builder import image from utils.message_builder import image
import asyncio import asyncio
__plugin_name__ = '微博热搜' __zx_plugin_name__ = '微博热搜'
__plugin_usage__ = '用法: 无' __plugin_usage__ = """
usage:
在QQ上吃个瓜
指令:
微博热搜:发送实时热搜
微博热搜 [id]:截图该热搜页面
示例:微博热搜 5
""".strip()
__plugin_des__ = '刚买完瓜,在吃瓜现场'
__plugin_cmd__ = ['微博热搜', '微博热搜 [id]']
__plugin_version__ = 0.1
__plugin_author__ = 'HibiKier'
__plugin_settings__ = {
"level": 5,
"default_status": True,
"limit_superuser": False,
"cmd": ['微博热搜'],
}
wbtop = on_command("wbtop", aliases={'微博热搜'}, priority=5, block=True) wbtop = on_command("wbtop", aliases={'微博热搜'}, priority=5, block=True)
@@ -34,11 +50,12 @@ async def _(bot: Bot, event: MessageEvent, state: T_State):
if code != 200: if code != 200:
await wbtop.finish(data, at_sender=True) await wbtop.finish(data, at_sender=True)
wbtop_data = data['data'] wbtop_data = data['data']
img = await asyncio.get_event_loop().run_in_executor(None, gen_wbtop_pic, wbtop_data) if not msg:
await wbtop.send(img) img = await asyncio.get_event_loop().run_in_executor(None, gen_wbtop_pic, wbtop_data)
logger.info( await wbtop.send(img)
f"(USER {event.user_id}, GROUP {event.group_id if isinstance(event, GroupMessageEvent) else 'private'})" logger.info(
f" 查询微博热搜") 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: if is_number(msg) and 0 < int(msg) <= 50:
url = wbtop_data[int(msg) - 1]['url'] url = wbtop_data[int(msg) - 1]['url']
browser = await get_browser() browser = await get_browser()
@@ -51,9 +68,9 @@ async def _(bot: Bot, event: MessageEvent, state: T_State):
await page.goto(url, wait_until='networkidle', timeout=10000) await page.goto(url, wait_until='networkidle', timeout=10000)
await page.set_viewport_size({"width": 2560, "height": 1080}) await page.set_viewport_size({"width": 2560, "height": 1080})
div = await page.query_selector("#pl_feedlist_index") div = await page.query_selector("#pl_feedlist_index")
await div.screenshot(path=f'{IMAGE_PATH}/temp/webtop_{event.user_id}.png', timeout=100000) await div.screenshot(path=f'{IMAGE_PATH}/temp/wbtop_{event.user_id}.png', timeout=100000)
await page.close() await page.close()
await wbtop.send(image(f'webtop_{event.user_id}.png', 'temp')) await wbtop.send(image(f'wbtop_{event.user_id}.png', 'temp'))
except Exception as e: except Exception as e:
logger.error(f'微博热搜截图出错... {type(e)}: {e}') logger.error(f'微博热搜截图出错... {type(e)}: {e}')
if page: if page:
+3 -1
View File
@@ -7,7 +7,9 @@ from nonebot.adapters.cqhttp.exception import ActionFailed
from utils.utils import scheduler from utils.utils import scheduler
import time import time
__plugin_name__ = "好友群聊处理请求 [Hidden]" __zx_plugin_name__ = "好友群聊处理请求 [Hidden]"
__plugin_version__ = 0.1
__plugin_author__ = 'HibiKier'
friend_req = on_request(priority=5) friend_req = on_request(priority=5)
+43 -13
View File
@@ -4,22 +4,38 @@ from models.level_user import LevelUser
from nonebot.typing import T_State from nonebot.typing import T_State
from nonebot.adapters.cqhttp import Bot from nonebot.adapters.cqhttp import Bot
from nonebot.adapters.cqhttp import GroupMessageEvent, PrivateMessageEvent from nonebot.adapters.cqhttp import GroupMessageEvent, PrivateMessageEvent
from nonebot.adapters.cqhttp.permission import GROUP
from utils.utils import get_message_at, get_message_text, is_number from utils.utils import get_message_at, get_message_text, is_number
from configs.config import NICKNAME from configs.config import NICKNAME, BAN_LEVEL
from nonebot.permission import SUPERUSER
from services.log import logger from services.log import logger
__plugin_name__ = "Ban/unBan" __zx_plugin_name__ = "封禁Ban用户 [Admin]"
__plugin_usage__ = ( __plugin_usage__ = """
"用法:\n" usage:
f"(不是禁言!是针对{NICKNAME}是否处理封禁用户消息)\n" 将用户拉入或拉出黑名单
"封禁/解封用户 [小时] [分钟]\n" 指令:
"示例:.ban @djdsk\n" .ban [at] ?[小时] ?[分钟]
"示例:.ban @djdsk 0 30\n" .unban
"示例:.ban @sdasf 4\n" 示例:.ban @user
"示例:.unban @sdasf" 示例:.ban @user 6
) 示例:.ban @user 3 10
示例:.unban @user
""".strip()
__plugin_superuser_usage__ = """
usage:
屏蔽用户消息,相当于最上级.ban
指令:
b了 [at]
示例:b了 @user
""".strip()
__plugin_des__ = '你被逮捕了!丢进小黑屋!'
__plugin_cmd__ = ['.ban [at] ?[小时] ?[分钟]', '.unban [at]', 'b了 [at] [_superuser]']
__plugin_version__ = 0.1
__plugin_author__ = 'HibiKier'
__plugin_settings__ = {
"admin_level": BAN_LEVEL,
}
ban = on_command( ban = on_command(
@@ -29,6 +45,8 @@ ban = on_command(
block=True, block=True,
) )
super_ban = on_command('b了', permission=SUPERUSER, priority=5, block=True)
@ban.handle() @ban.handle()
async def _(bot: Bot, event: GroupMessageEvent, state: T_State): async def _(bot: Bot, event: GroupMessageEvent, state: T_State):
@@ -155,5 +173,17 @@ async def _(bot: Bot, event: PrivateMessageEvent, state: T_State):
await ban.finish('qq号必须是数字!\n格式:.ban [qq] [hour]? [minute]?', at_sender=True) await ban.finish('qq号必须是数字!\n格式:.ban [qq] [hour]? [minute]?', at_sender=True)
@super_ban.handle()
async def _(bot: Bot, event: GroupMessageEvent, state: T_State):
qq = get_message_at(event.json())
if qq:
qq = qq[0]
user = await bot.get_group_member_info(group_id=event.group_id, user_id=qq)
user_name = user['card'] if user['card'] else user['nickname']
if not await BanUser.ban(qq, 10, 99999999):
await BanUser.unban(qq)
await BanUser.ban(qq, 10, 99999999)
await ban.send(f"已将 {user_name} 拉入黑名单!")
else:
await super_ban.send('需要艾特被super ban的对象..')
+217
View File
@@ -0,0 +1,217 @@
from nonebot import on_command
from nonebot.typing import T_State
from nonebot.adapters.cqhttp import Bot, MessageEvent, GroupMessageEvent, Message
from .data_source import (
add_live_sub,
delete_sub,
add_up_sub,
add_season_sub,
get_media_id,
get_sub_status,
SubManager,
)
from models.level_user import LevelUser
from configs.config import GROUP_BILIBILI_SUB_LEVEL
from utils.utils import get_message_text, is_number, scheduler, get_bot
from models.bilibili_sub import BilibiliSub
from typing import Optional
from services.log import logger
from nonebot import Driver
import nonebot
__zx_plugin_name__ = "B站订阅"
__plugin_usage__ = """
usage:
B站直播,番剧,UP动态开播等提醒
主播订阅相当于 直播间订阅 + UP订阅
指令:[示例Id乱打的,仅做示例]
添加订阅 ['主播'/'UP'/'番剧'] [id/链接/番名]
删除订阅 [id]
查看订阅
示例:添加订阅主播 2345344 <-(直播房间id)
示例:添加订阅UP 2355543 <-(个人主页id)
示例:添加订阅番剧 史莱姆 <-(支持模糊搜索)
示例:添加订阅番剧 125344 <-(番剧id)
示例:删除订阅 2324344 <-(任意id,通过查看订阅获取)
""".strip()
__plugin_des__ = '非常便利的B站订阅通知'
__plugin_cmd__ = ['添加订阅 [主播/UP/番剧] [id/链接/番名]', '删除订阅 [id]', '查看订阅']
__plugin_version__ = 0.1
__plugin_author__ = 'HibiKier'
__plugin_settings__ = {
"level": 5,
"default_status": True,
"limit_superuser": False,
"cmd": ["B站订阅", 'b站订阅', '添加订阅', '删除订阅', '查看订阅'],
}
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)
driver: Driver = nonebot.get_driver()
sub_manager: Optional[SubManager] = None
@driver.on_startup
async def _():
global sub_manager
sub_manager = SubManager()
@add_sub.args_parser
async def _(bot: Bot, event: MessageEvent, state: T_State):
season_data = state["season_data"]
msg = get_message_text(event.json())
if not is_number(msg) or int(msg) < 1 or int(msg) > len(season_data):
await add_sub.reject("Id必须为数字且在范围内!请重新输入...")
state["id"] = season_data[int(msg) - 1]["media_id"]
@add_sub.handle()
async def _(bot: Bot, event: MessageEvent, state: T_State):
msg = get_message_text(event.json()).split()
if len(msg) < 2:
await add_sub.finish("参数不完全,请查看订阅帮助...")
sub_type = msg[0]
id_ = ""
if isinstance(event, GroupMessageEvent):
if not await LevelUser.check_level(
event.user_id, event.group_id, GROUP_BILIBILI_SUB_LEVEL
):
await add_sub.finish(
f"您的权限不足,群内订阅的需要 {GROUP_BILIBILI_SUB_LEVEL} 级权限..", at_sender=True
)
sub_user = f"{event.user_id}:{event.group_id}"
else:
sub_user = f"{event.user_id}"
state["sub_type"] = sub_type
state["sub_user"] = sub_user
if len(msg) > 1:
if "http" in msg[1]:
msg[1] = msg[1].split("?")[0]
msg[1] = msg[1][:-1] if msg[1][-1] == "/" else msg[1]
msg[1] = msg[1].split("/")[-1]
id_ = msg[1][2:] if msg[1].startswith("md") else msg[1]
if not is_number(id_):
if sub_type in ["season", "动漫", "番剧"]:
rst = "*以为您找到以下番剧,请输入Id选择:*\n"
state["season_data"] = await get_media_id(id_)
if len(state["season_data"]) == 0:
await add_sub.finish(f"未找到番剧:{msg}")
for i, x in enumerate(state["season_data"]):
rst += f'{i + 1}.{state["season_data"][x]["title"]}\n----------\n'
await add_sub.send("\n".join(rst.split("\n")[:-1]))
else:
await add_sub.finish("Id 必须为全数字!")
else:
state["id"] = int(id_)
@add_sub.got("id")
async def _(bot: Bot, event: MessageEvent, state: T_State):
sub_type = state["sub_type"]
sub_user = state["sub_user"]
id_ = state["id"]
if sub_type in ["主播", "直播"]:
await add_sub.send(await add_live_sub(id_, sub_user))
elif sub_type.lower() in ["up", "用户"]:
await add_sub.send(await add_up_sub(id_, sub_user))
elif sub_type in ["season", "动漫", "番剧"]:
await add_sub.send(await add_season_sub(id_, sub_user))
else:
await add_sub.finish("参数错误,第一参数必须为:主播/up/番剧!")
logger.info(
f"(USER {event.user_id}, GROUP "
f"{event.group_id if isinstance(event, GroupMessageEvent) else 'private'})"
f" 添加订阅:{sub_type} -> {sub_user} -> {id_}"
)
@del_sub.handle()
async def _(bot: Bot, event: MessageEvent, state: T_State):
msg = get_message_text(event.json())
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} 成功...')
logger.info(
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} 失败...')
@show_sub_info.handle()
async def _(bot: Bot, event: MessageEvent, state: T_State):
id_ = f'{event.user_id}:{event.group_id}' if isinstance(event, GroupMessageEvent) else f'{event.user_id}'
data = await BilibiliSub.get_sub_data(id_)
live_rst = ''
up_rst = ''
season_rst = ''
for x in data:
if x.sub_type == 'live':
live_rst += f'\t直播间id:{x.sub_id}\n' \
f'\t名称:{x.uname}\n' \
f'------------------\n'
if x.sub_type == 'up':
up_rst += f'\tUP:{x.uname}\n' \
f'\tuid:{x.uid}\n' \
f'------------------\n'
if x.sub_type == 'season':
season_rst += f'\t番名:{x.season_name}\n' \
f'\t当前集数:{x.season_current_episode}\n' \
f'------------------\n'
live_rst = '当前订阅的直播:\n' + live_rst if live_rst else live_rst
up_rst = '当前订阅的UP:\n' + up_rst if up_rst else up_rst
season_rst = '当前订阅的番剧:\n' + season_rst if season_rst else season_rst
if not live_rst and not up_rst and not season_rst:
live_rst = '您目前没有任何订阅...'
await show_sub_info.send(live_rst + up_rst + season_rst)
# 推送
@scheduler.scheduled_job(
"interval",
seconds=30,
)
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:
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)
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):
"""
推送信息
:param rst: 回复
:param sub: BilibiliSub
:param bot: Bot
"""
if rst:
for x in sub.sub_users.split(",")[:-1]:
try:
if ":" in x:
await bot.send_group_msg(
group_id=int(x.split(":")[1]), message=Message(rst)
)
else:
await bot.send_private_msg(user_id=int(x), message=Message(rst))
except Exception as e:
logger.error(f"B站订阅推送发生错误 sub_id:{sub.sub_id} {type(e)}:{e}")
+390
View File
@@ -0,0 +1,390 @@
from bilibili_api.exceptions.ResponseCodeException import ResponseCodeException
from asyncio.exceptions import TimeoutError
from models.bilibili_sub import BilibiliSub
from bilibili_api.live import LiveRoom
from bilibili_api import bangumi
from utils.message_builder import image
from bilibili_api.user import User
from bilibili_api import user
from typing import Optional
from pathlib import Path
from configs.path_config import IMAGE_PATH
from datetime import datetime
from utils.browser import get_browser
from services.db_context import db
from services.log import logger
import aiohttp
import random
bilibili_search_url = "https://api.bilibili.com/x/web-interface/search/all/v2"
dynamic_path = Path(IMAGE_PATH) / "bilibili_sub" / "dynamic"
dynamic_path.mkdir(exist_ok=True, parents=True)
async def add_live_sub(live_id: int, sub_user: str) -> str:
"""
添加直播订阅
:param live_id: 直播房间号
:param sub_user: 订阅用户 id # 7384933:private or 7384933:2342344(group)
:return:
"""
try:
async with db.transaction():
try:
live = LiveRoom(live_id)
live_info = (await live.get_room_info())["room_info"]
except ResponseCodeException:
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}"
)
else:
return "添加订阅失败..."
except Exception as e:
logger.error(f"订阅主播live_id:{live_id} 发生了错误 {type(e)}:{e}")
return "添加订阅失败..."
async def add_up_sub(uid: int, sub_user: str) -> str:
"""
添加订阅 UP
:param uid: UP uid
:param sub_user: 订阅用户
"""
try:
async with db.transaction():
try:
u = user.User(uid)
user_info = await u.get_user_info()
except ResponseCodeException:
return f"未找到UpId:{uid} 的信息,请检查Id是否正确"
uname = user_info["name"]
dynamic_info = await u.get_dynamics(0)
dynamic_upload_time = 0
if dynamic_info.get("cards"):
dynamic_upload_time = dynamic_info["cards"][0]["desc"]["timestamp"]
video_info = await u.get_videos()
latest_video_created = 0
if video_info["list"].get("vlist"):
latest_video_created = video_info["list"]["vlist"][0]["created"]
if await BilibiliSub.add_bilibili_sub(
uid,
"up",
sub_user,
uid=uid,
uname=uname,
dynamic_upload_time=dynamic_upload_time,
latest_video_created=latest_video_created,
):
return "已成功订阅UP:\n" f"\tname: {uname}\n" f"\tuid:{uid}"
else:
return "添加订阅失败..."
except Exception as e:
logger.error(f"订阅Up uid:{uid} 发生了错误 {type(e)}:{e}")
return "添加订阅失败..."
async def add_season_sub(media_id: int, sub_user: str) -> str:
"""
添加订阅 UP
:param media_id: 番剧 media_id
:param sub_user: 订阅用户
"""
try:
async with db.transaction():
try:
season_info = await bangumi.get_meta(media_id)
except ResponseCodeException:
return f"未找到media_id:{media_id} 的信息,请检查Id是否正确"
season_id = season_info["media"]["season_id"]
season_current_episode = season_info["media"]["new_ep"]["index"]
season_name = season_info["media"]["title"]
if await BilibiliSub.add_bilibili_sub(
media_id,
"season",
sub_user,
season_name=season_name,
season_id=season_id,
season_current_episode=season_current_episode,
):
return (
"已成功订阅番剧:\n"
f"\ttitle: {season_name}\n"
f"\tcurrent_episode: {season_current_episode}"
)
else:
return "添加订阅失败..."
except Exception as e:
logger.error(f"订阅番剧 media_id:{media_id} 发生了错误 {type(e)}:{e}")
return "添加订阅失败..."
async def delete_sub(sub_id: str, sub_user: str) -> str:
"""
删除订阅
:param sub_id: 订阅 id
:param sub_user: 订阅用户 id # 7384933:private or 7384933:2342344(group)
"""
if await BilibiliSub.delete_bilibili_sub(sub_id, sub_user):
return f"已成功取消订阅:{sub_id}"
else:
return f"取消订阅:{sub_id} 失败,请检查是否订阅过该Id...."
async def get_media_id(keyword: str) -> dict:
"""
获取番剧的 media_id
:param keyword: 番剧名称
"""
params = {"keyword": keyword}
async with aiohttp.ClientSession() as session:
for _ in range(3):
try:
_season_data = {}
async with session.get(
bilibili_search_url, timeout=5, params=params
) as response:
if response.status == 200:
data = await response.json()
if data.get("data"):
for item in data["data"]["result"]:
if item["result_type"] == "media_bangumi":
idx = 0
for x in item["data"]:
_season_data[idx] = {
"media_id": x["media_id"],
"title": x["title"]
.replace('<em class="keyword">', "")
.replace("</em>", ""),
}
idx += 1
return _season_data
except TimeoutError:
pass
return {}
async def get_sub_status(id_: int, sub_type: str) -> Optional[str]:
"""
获取订阅状态
:param id_: 订阅 id
:param sub_type: 订阅类型
:return:
"""
try:
if sub_type == "live":
return await _get_live_status(id_)
elif sub_type == "up":
return await _get_up_status(id_)
elif sub_type == "season":
return await _get_season_status(id_)
except ResponseCodeException:
return "获取信息失败...请检查订阅Id是否存在或稍后再试..."
# except Exception as e:
# logger.error(f"获取订阅状态发生预料之外的错误 id_:{id_} {type(e)}:{e}")
# return "发生了预料之外的错误..请稍后再试或联系管理员....."
async def _get_live_status(id_: int) -> Optional[str]:
"""
获取直播订阅状态
:param id_: 直播间 id
"""
live = LiveRoom(id_)
live_info = (await live.get_room_info())["room_info"]
title = live_info["title"]
room_id = live_info["room_id"]
live_status = live_info["live_status"]
cover = live_info["cover"]
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:
return (
f"{image(cover)}\n"
f"{sub.uname} 开播啦!\n"
f"标题:{title}\n"
f"直链:https://live.bilibili.com/{room_id}"
)
return None
async def _get_up_status(id_: int) -> Optional[str]:
"""
获取用户投稿状态
:param id_: 用户 id
:return:
"""
_user = await BilibiliSub.get_sub(id_)
u = user.User(_user.uid)
user_info = await u.get_user_info()
uname = user_info["name"]
video_info = await u.get_videos()
latest_video_created = 0
video = None
if _user.uname != uname:
await BilibiliSub.update_sub_info(id_, uname=uname)
dynamic_img, dynamic_upload_time = await get_user_dynamic(u, _user)
if video_info["list"].get("vlist"):
video = video_info["list"]["vlist"][0]
latest_video_created = video["created"]
rst = ""
if dynamic_img:
await BilibiliSub.update_sub_info(id_, dynamic_upload_time=dynamic_upload_time)
rst += f"{uname} 发布了动态!\n" f"{dynamic_img}\n"
if _user.latest_video_created != latest_video_created and video:
rst = rst + "-------------\n" if rst else rst
await BilibiliSub.update_sub_info(
id_, latest_video_created=latest_video_created
)
rst += (
f'{image(video["pic"])}\n'
f"{uname} 投稿了新视频啦\n"
f'标题:{video["title"]}\n'
f'Bvid:{video["bvid"]}\n'
f'直链:https://www.bilibili.com/video/{video["bvid"]}'
)
rst = None if rst == "-------------\n" else rst
return rst
async def _get_season_status(id_) -> Optional[str]:
"""
获取 番剧 更新状态
:param id_: 番剧 id
"""
season_info = await bangumi.get_meta(id_)
title = season_info["media"]["title"]
_idx = (await BilibiliSub.get_sub(id_)).season_current_episode
new_ep = season_info["media"]["new_ep"]["index"]
if new_ep != _idx:
await BilibiliSub.update_sub_info(
id_, season_current_episode=new_ep, season_update_time=datetime.now()
)
return (
f'{image(season_info["media"]["cover"])}\n'
f"[{title}]更新啦\n"
f"最新集数:{new_ep}"
)
return None
async def get_user_dynamic(
u: User, local_user: BilibiliSub
) -> "Optional[MessageSegment], int":
"""
获取用户动态
:param u: 用户类
:param local_user: 数据库存储的用户数据
:return: 最新动态截图与时间
"""
dynamic_info = await u.get_dynamics(0)
browser = await get_browser()
if dynamic_info.get("cards") and browser:
dynamic_upload_time = dynamic_info["cards"][0]["desc"]["timestamp"]
if local_user.dynamic_upload_time != dynamic_upload_time:
page = await browser.new_page()
await page.goto(
f"https://space.bilibili.com/{local_user.uid}/dynamic",
wait_until="networkidle",
timeout=10000,
)
await page.set_viewport_size({"width": 2560, "height": 1080})
# 删除置顶
await page.evaluate(
"""
xs = document.getElementsByClassName('first-card-with-title');
for (x of xs) {
x.remove();
}
"""
)
card = await page.query_selector(".card")
# 截图并保存
await card.screenshot(
path=dynamic_path / f"{local_user.sub_id}_{dynamic_upload_time}.jpg",
timeout=100000,
)
await page.close()
return (
image(
f"{local_user.sub_id}_{dynamic_upload_time}.jpg",
"bilibili_sub/dynamic",
),
dynamic_upload_time,
)
return None, None
class SubManager:
def __init__(self):
self.live_data = []
self.up_data = []
self.season_data = []
self.current_index = -1
async def reload_sub_data(self):
"""
重载数据
"""
if not self.live_data or not self.up_data or not self.season_data:
(
_live_data,
_up_data,
_season_data,
) = await BilibiliSub.get_all_sub_data()
if not self.live_data:
self.live_data = _live_data
if not self.up_data:
self.up_data = _up_data
if not self.season_data:
self.season_data = _season_data
async def random_sub_data(self) -> Optional[BilibiliSub]:
"""
随机获取一条数据
:return:
"""
sub = None
if not self.live_data and not self.up_data and not self.season_data:
return sub
self.current_index += 1
if self.current_index == 0:
if self.live_data:
sub = random.choice(self.live_data)
self.live_data.remove(sub)
elif self.current_index == 1:
if self.up_data:
sub = random.choice(self.up_data)
self.up_data.remove(sub)
elif self.current_index == 2:
if self.season_data:
sub = random.choice(self.season_data)
self.season_data.remove(sub)
else:
self.current_index = -1
if sub:
return sub
await self.reload_sub_data()
return await self.random_sub_data()
+72
View File
@@ -0,0 +1,72 @@
from utils.image_utils import CreateImg
from configs.path_config import IMAGE_PATH
from pathlib import Path
from bilibili_api import user
from io import BytesIO
import aiohttp
BORDER_PATH = Path(IMAGE_PATH) / 'border'
BORDER_PATH.mkdir(parents=True, exist_ok=True)
async def get_pic(url: str) -> bytes:
"""
获取图像
:param url: 图像链接
:return: 图像二进制
"""
async with aiohttp.ClientSession() as session:
async with session.get(url, timeout=2) as response:
return await response.read()
async def create_live_des_image(uid: int, title: str, cover: str, tags: str, des: str):
"""
生成主播简介图片
:param uid: 主播 uid
:param title: 直播间标题
:param cover: 直播封面
:param tags: 直播标签
:param des: 直播简介
:return:
"""
u = user.User(uid)
user_info = await u.get_user_info()
name = user_info['name']
sex = user_info['sex']
face = user_info['face']
sign = user_info['sign']
ava = CreateImg(100, 100, background=BytesIO(await get_pic(face)))
ava.circle()
cover = CreateImg(470, 265, background=BytesIO(await get_pic(cover)))
print()
def _create_live_des_image(title: str, cover: CreateImg, tags: str, des: str, user_name: str, sex: str, sign: str, ava: CreateImg):
"""
生成主播简介图片
:param title: 直播间标题
:param cover: 直播封面
:param tags: 直播标签
:param des: 直播简介
:param user_name: 主播名称
:param sex: 主播性别
:param sign: 主播签名
:param ava: 主播头像
:return:
"""
border = BORDER_PATH / '0.png'
border_img = None
if border.exists():
border_img = CreateImg(1772, 2657, background=border)
bk = CreateImg(1772, 2657, font_size=30)
bk.paste(cover, (0, 100), center_type='by_width')
+37 -26
View File
@@ -5,12 +5,21 @@ from nonebot.permission import SUPERUSER
import asyncio import asyncio
from utils.utils import get_message_text, get_message_imgs from utils.utils import get_message_text, get_message_imgs
from services.log import logger from services.log import logger
from models.group_remind import GroupRemind
from utils.message_builder import image from utils.message_builder import image
from utils.manager import group_manager
__plugin_name__ = "广播 [Hidden]" __zx_plugin_name__ = "广播 [Superuser]"
__plugin_usage__ = """
__plugin_usage__ = '广播- [消息] or [图片]' usage:
指令:
广播- ?[消息] ?[图片]
示例:广播- 你们好!
""".strip()
__plugin_des__ = "昭告天下!"
__plugin_cmd__ = ["广播-"]
__plugin_version__ = 0.1
__plugin_author__ = "HibiKier"
__plugin_task__ = {"broadcast": "广播"}
broadcast = on_command("广播-", priority=1, permission=SUPERUSER, block=True) broadcast = on_command("广播-", priority=1, permission=SUPERUSER, block=True)
@@ -19,30 +28,32 @@ broadcast = on_command("广播-", priority=1, permission=SUPERUSER, block=True)
async def _(bot: Bot, event: Event, state: T_State): async def _(bot: Bot, event: Event, state: T_State):
msg = get_message_text(event.json()) msg = get_message_text(event.json())
imgs = get_message_imgs(event.json()) imgs = get_message_imgs(event.json())
rst = '' rst = ""
for img in imgs: for img in imgs:
rst += image(img) rst += image(img)
sid = bot.self_id sid = bot.self_id
gl = await bot.get_group_list(self_id=sid) gl = await bot.get_group_list(self_id=sid)
gl = [g['group_id'] for g in gl] gl = [
g["group_id"]
for g in gl
if await group_manager.check_group_task_status(g["group_id"], "broadcast")
]
g_cnt = len(gl)
cnt = 0
error = ""
x = 0.25
for g in gl: for g in gl:
if await GroupRemind.get_status(g, 'gb'): cnt += 1
await asyncio.sleep(0.5) if cnt / g_cnt > x:
try: await broadcast.send(f"已播报至 {int(cnt / g_cnt * 100)}% 的群聊")
await bot.send_group_msg(self_id=sid, group_id=g, message=msg+rst) x += 0.25
logger.info(f'群{g} 投递广播成功') try:
except Exception as e: await bot.send_group_msg(self_id=sid, group_id=g, message=msg + rst)
logger.error(f'群{g} 投递广播失败:{type(e)}') logger.info(f"GROUP {g} 投递广播成功")
try: except Exception as e:
await broadcast.send(f'群{g} 投递广播失败:{type(e)}') logger.error(f"GROUP {g} 投递广播失败:{type(e)}")
except Exception as e: error += f"GROUP {g} 投递广播失败:{type(e)}\n"
logger.critical(f'向广播发起者进行错误回报时发生错误:{type(e)}') await asyncio.sleep(0.5)
await broadcast.send(f'广播完成!') await broadcast.send(f"已播报至 100% 的群聊")
if error:
await broadcast.send(f"播报时错误:{error}")
+22 -17
View File
@@ -6,21 +6,33 @@ from nonebot.adapters import Bot
from nonebot.adapters.cqhttp import PrivateMessageEvent from nonebot.adapters.cqhttp import PrivateMessageEvent
from utils.utils import get_message_text from utils.utils import get_message_text
from nonebot.adapters.cqhttp.permission import PRIVATE from nonebot.adapters.cqhttp.permission import PRIVATE
from utils.utils import UserExistLimiter
from asyncio.exceptions import TimeoutError from asyncio.exceptions import TimeoutError
from aiohttp.client_exceptions import ServerDisconnectedError from aiohttp.client_exceptions import ServerDisconnectedError
__plugin_name__ = "磁力搜索" __zx_plugin_name__ = "磁力搜索"
__plugin_usage__ = r""" __plugin_usage__ = """
* 请各位使用后不要转发 * usage:
* 拒绝反冲斗士! * * 请各位使用后不要转发 *
bt [关键词] [页数](默认为1) * 拒绝反冲斗士! *
示例: 指令:
bt 钢铁侠 bt [关键词] ?[页数]
bt 钢铁侠 3 示例:bt 钢铁侠
示例:bt 钢铁侠 3
""".strip() """.strip()
__plugin_des__ = "bt(磁力搜索)[仅支持私聊,懂的都懂]"
__plugin_cmd__ = ["bt [关键词] ?[页数]"]
__plugin_version__ = 0.1
__plugin_author__ = "HibiKier"
__plugin_settings__ = {
"level": 5,
"default_status": True,
"limit_superuser": False,
"cmd": ["bt", "磁力搜索", "Bt", "BT"],
}
__plugin_block_limit__ = {
"rst": "您有bt任务正在进行,请等待结束."
}
_ulmt = UserExistLimiter()
bt = on_command("bt", permission=PRIVATE, priority=5, block=True) bt = on_command("bt", permission=PRIVATE, priority=5, block=True)
@@ -38,8 +50,6 @@ async def _(bot: Bot, event: PrivateMessageEvent, state: T_State):
@bt.handle() @bt.handle()
async def _(bot: Bot, event: PrivateMessageEvent, state: T_State): async def _(bot: Bot, event: PrivateMessageEvent, state: T_State):
if _ulmt.check(event.user_id):
await bt.finish("您有bt任务正在进行,请等待结束.", at_sender=True)
mp = get_message_text(event.json()) mp = get_message_text(event.json())
if not mp: if not mp:
return return
@@ -54,7 +64,6 @@ async def _(bot: Bot, event: PrivateMessageEvent, state: T_State):
@bt.got("keyword", prompt="虚空磁力?查什么GKD") @bt.got("keyword", prompt="虚空磁力?查什么GKD")
async def _(bot: Bot, event: PrivateMessageEvent, state: T_State): async def _(bot: Bot, event: PrivateMessageEvent, state: T_State):
_ulmt.set_True(event.user_id)
keyword = state["keyword"] keyword = state["keyword"]
page = state["page"] page = state["page"]
await bt.send("开始搜索....", at_sender=True) await bt.send("开始搜索....", at_sender=True)
@@ -72,16 +81,12 @@ async def _(bot: Bot, event: PrivateMessageEvent, state: T_State):
) )
send_flag = True send_flag = True
except TimeoutError: except TimeoutError:
_ulmt.set_False(event.user_id)
await bt.finish(f"搜索 {keyword} 超时...") await bt.finish(f"搜索 {keyword} 超时...")
except ServerDisconnectedError: except ServerDisconnectedError:
_ulmt.set_False(event.user_id)
await bt.finish(f"搜索 {keyword} 连接失败") await bt.finish(f"搜索 {keyword} 连接失败")
except Exception as e: except Exception as e:
_ulmt.set_False(event.user_id)
await bt.finish(f"bt 其他未知错误..") await bt.finish(f"bt 其他未知错误..")
logger.error(f"bt 错误 e:{e}") logger.error(f"bt 错误 e:{e}")
if not send_flag: if not send_flag:
await bt.send(f"{keyword} 未搜索到...") await bt.send(f"{keyword} 未搜索到...")
logger.info(f"USER {event.user_id} BT搜索 {keyword} 第 {page} 页") logger.info(f"USER {event.user_id} BT搜索 {keyword} 第 {page} 页")
_ulmt.set_False(event.user_id)
+57
View File
@@ -0,0 +1,57 @@
from .music_163 import get_song_id, get_song_info
from nonebot.adapters.cqhttp import Bot, Event, GroupMessageEvent
from nonebot.typing import T_State
from services.log import logger
from nonebot import on_command
__zx_plugin_name__ = "点歌"
__plugin_usage__ = """
usage:
在线点歌
指令:
点歌 [歌名]
""".strip()
__plugin_des__ = "为你点播了一首曾经的歌"
__plugin_cmd__ = ["点歌 [歌名]"]
__plugin_type__ = ("一些工具",)
__plugin_version__ = 0.1
__plugin_author__ = "HibiKier"
__plugin_settings__ = {
"level": 5,
"default_status": True,
"limit_superuser": False,
"cmd": ["点歌"],
}
songpicker = on_command("点歌", priority=5, block=True)
@songpicker.handle()
async def handle_first_receive(bot: Bot, event: Event, state: T_State):
args = str(event.get_message()).strip()
if args:
state["song_name"] = args
@songpicker.got("song_name", prompt="歌名是?")
async def _(bot: Bot, event: Event, state: T_State):
song = state["song_name"]
song_id = await get_song_id(song)
if not song_id:
await songpicker.finish("没有找到这首歌!", at_sender=True)
for _ in range(3):
song_content = [{"type": "music", "data": {"type": 163, "id": song_id}}]
logger.info(
f"(USER {event.user_id}, GROUP "
f"{event.group_id if isinstance(event, GroupMessageEvent) else 'private'})"
f" 点歌 :{song}"
)
await songpicker.finish(song_content)
else:
await songpicker.finish("网易云繁忙...")
+47
View File
@@ -0,0 +1,47 @@
import aiohttp
import json
headers = {"referer": "http://music.163.com"}
cookies = {"appver": "2.0.2"}
async def search_song(song_name: str):
async with aiohttp.ClientSession(
headers=headers, cookies=cookies
) as session:
async with session.post(
f"http://music.163.com/api/search/get/",
data={"s": song_name, "limit": 1, "type": 1, "offset": 0},
) as r:
if r.status != 200:
return None
r = await r.text()
return json.loads(r)
async def get_song_id(songName: str) -> int:
"""
根据用户输入的songName 获取候选songId列表 [默认songId数量:5]
"""
r = await search_song(songName)
return r["result"]["songs"][0]["id"]
async def get_song_info(songId: int):
"""
获取歌曲信息
"""
async with aiohttp.ClientSession(
headers=headers, cookies=cookies
) as session:
async with session.post(
f"http://music.163.com/api/song/detail/?id={songId}&ids=%5B{songId}%5D",
) as r:
if r.status != 200:
return None
r = await r.text()
return json.loads(r)
+11 -2
View File
@@ -6,9 +6,18 @@ from nonebot.rule import to_me
from nonebot.permission import SUPERUSER from nonebot.permission import SUPERUSER
__plugin_name__ = "自我检查 [Hidden]" __zx_plugin_name__ = "服务器自我检查 [Superuser]"
__plugin_usage__ = """
usage:
查看服务器当前状态
指令:
自检
"""
__plugin_des__ = "查看服务器当前状态"
__plugin_cmd__ = ["自检/check"]
__plugin_version__ = 0.1
__plugin_author__ = "HibiKier"
__plugin_usage__ = "用法:自检"
check = Check() check = Check()
+50 -26
View File
@@ -12,44 +12,68 @@ import platform
import os import os
update_zhenxun = on_command('检查更新真寻', permission=SUPERUSER, priority=1, block=True) __zx_plugin_name__ = "自动更新 [Superuser]"
__plugin_usage__ = """
usage:
检查更新真寻最新版本,包括了自动更新
指令:
检查更新真寻
重启
""".strip()
__plugin_des__ = "就算是真寻也会成长的"
__plugin_cmd__ = ["检查更新真寻", "重启"]
__plugin_version__ = 0.1
__plugin_author__ = "HibiKier"
restart = on_command('重启', aliases={'restart'}, permission=SUPERUSER, rule=to_me(), priority=1, block=True) update_zhenxun = on_command("检查更新真寻", permission=SUPERUSER, priority=1, block=True)
restart = on_command(
"重启",
aliases={"restart"},
permission=SUPERUSER,
rule=to_me(),
priority=1,
block=True,
)
@update_zhenxun.handle() @update_zhenxun.handle()
async def _(bot: Bot, event: MessageEvent, state: T_State): async def _(bot: Bot, event: MessageEvent, state: T_State):
try: try:
code = await check_update(bot) 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}"
)
except Exception as e: except Exception as e:
logger.error(f'更新真寻未知错误 {type(e)}:{e}') logger.error(f"更新真寻未知错误 {type(e)}:{e}")
await bot.send_private_msg( await bot.send_private_msg(
user_id=int(list(bot.config.superusers)[0]), user_id=int(list(bot.config.superusers)[0]),
message=f'更新真寻未知错误 {type(e)}:{e}' message=f"更新真寻未知错误 {type(e)}:{e}",
) )
else: else:
if code == 200: if code == 200:
await bot.send_private_msg( await bot.send_private_msg(
user_id=int(list(bot.config.superusers)[0]), user_id=int(list(bot.config.superusers)[0]), message=f"更新完毕,请重启真寻...."
message=f'更新完毕,请重启真寻....'
) )
@restart.handle() @restart.handle()
async def _(bot: Bot, event: MessageEvent, state: T_State): async def _(bot: Bot, event: MessageEvent, state: T_State):
if str(platform.system()).lower() == 'windows': if str(platform.system()).lower() == "windows":
await restart.finish('暂无windows重启脚本...') await restart.finish("暂无windows重启脚本...")
@restart.got('flag', prompt='确定是否重启真寻?(重启失败咱们将失去联系,请谨慎!)') @restart.got("flag", prompt="确定是否重启真寻?(重启失败咱们将失去联系,请谨慎!)")
async def _(bot: Bot, event: MessageEvent, state: T_State): async def _(bot: Bot, event: MessageEvent, state: T_State):
flag = state['flag'] flag = state["flag"]
if flag.lower() in ['true', '是', '好']: if flag.lower() in ["true", "是", "好", "确定", "确定是"]:
await restart.send('开始重启真寻..请稍等...') await restart.send("开始重启真寻..请稍等...")
open('is_restart', 'w') open("is_restart", "w")
os.system('./restart.sh') os.system("./restart.sh")
else: else:
await restart.send('已取消操作...') await restart.send("已取消操作...")
@scheduler.scheduled_job( @scheduler.scheduled_job(
@@ -63,7 +87,10 @@ async def _():
_version_file = Path() / "__version__" _version_file = Path() / "__version__"
if _version_file.exists(): if _version_file.exists():
_version = ( _version = (
open(_version_file, "r", encoding="utf8").readline().split(":")[-1].strip() open(_version_file, "r", encoding="utf8")
.readline()
.split(":")[-1]
.strip()
) )
data = await get_latest_version_data() data = await get_latest_version_data()
if data: if data:
@@ -72,24 +99,21 @@ async def _():
bot = get_bot() bot = get_bot()
await bot.send_private_msg( await bot.send_private_msg(
user_id=int(list(bot.config.superusers)[0]), user_id=int(list(bot.config.superusers)[0]),
message=f'检测到真寻版本更新\n' message=f"检测到真寻版本更新\n"
f'当前版本:{_version},最新版本:{latest_version}\n' f"当前版本:{_version},最新版本:{latest_version}\n"
f'尝试自动更新...' f"尝试自动更新...",
) )
try: try:
code = await check_update(bot) code = await check_update(bot)
except Exception as e: except Exception as e:
logger.error(f'更新真寻未知错误 {type(e)}:{e}') logger.error(f"更新真寻未知错误 {type(e)}:{e}")
await bot.send_private_msg( await bot.send_private_msg(
user_id=int(list(bot.config.superusers)[0]), user_id=int(list(bot.config.superusers)[0]),
message=f'更新真寻未知错误 {type(e)}:{e}\n' message=f"更新真寻未知错误 {type(e)}:{e}\n",
) )
else: else:
if code == 200: if code == 200:
await bot.send_private_msg( await bot.send_private_msg(
user_id=int(list(bot.config.superusers)[0]), user_id=int(list(bot.config.superusers)[0]),
message=f'更新完毕,请重启真寻....' message=f"更新完毕,请重启真寻....",
) )
+70 -59
View File
@@ -1,9 +1,10 @@
from aiohttp.client_exceptions import ClientConnectorError from aiohttp.client_exceptions import ClientConnectorError
from nonebot.adapters.cqhttp import Bot from nonebot.adapters.cqhttp import Bot, Message
from utils.user_agent import get_user_agent from utils.user_agent import get_user_agent
from utils.utils import get_local_proxy from utils.utils import get_local_proxy
from utils.image_utils import CreateImg from utils.image_utils import CreateImg
from configs.path_config import IMAGE_PATH from configs.path_config import IMAGE_PATH
from utils.message_builder import image
from typing import List from typing import List
from services.log import logger from services.log import logger
from pathlib import Path from pathlib import Path
@@ -58,7 +59,7 @@ async def remind(bot: Bot):
is_restart_file.unlink() is_restart_file.unlink()
async def check_update(bot: Bot) -> int: async def check_update(bot: Bot) -> 'int, str':
logger.info("开始检查更新真寻酱....") logger.info("开始检查更新真寻酱....")
_version = "v0.0.0" _version = "v0.0.0"
if _version_file.exists(): if _version_file.exists():
@@ -78,28 +79,32 @@ async def check_update(bot: Bot) -> int:
logger.info(f"开始下载真寻最新版文件....") logger.info(f"开始下载真寻最新版文件....")
if await download_latest_file(tar_gz_url): if await download_latest_file(tar_gz_url):
logger.info("下载真寻最新版文件完成....") logger.info("下载真寻最新版文件完成....")
await asyncio.get_event_loop().run_in_executor( error = await asyncio.get_event_loop().run_in_executor(
None, _file_handle, latest_version None, _file_handle, latest_version
) )
if error:
return 998, error
logger.info("真寻更新完毕,清理文件完成....") logger.info("真寻更新完毕,清理文件完成....")
logger.info("开始获取真寻更新日志.....") logger.info("开始获取真寻更新日志.....")
update_info = data["body"] update_info = data["body"]
await bot.send_private_msg(
user_id=int(list(bot.config.superusers)[0]),
message=f"真寻更新完成,版本:{_version} -> {latest_version}\n"
f"更新日期:{data['created_at']}\n"
f"更新日志:\n"
f"{update_info}",
)
width = 0 width = 0
height = len(update_info.split('\n')) * 24 height = len(update_info.split('\n')) * 24
A = CreateImg(width, height, font_size=20)
for m in update_info.split('\n'): for m in update_info.split('\n'):
if len(m) * 20 > width: w, h = A.getsize(m)
width = len(m) * 17 if w > width:
A = CreateImg(800, height, font_size=20) width = w
A = CreateImg(width + 50, height, font_size=20)
A.text((10, 10), update_info) A.text((10, 10), update_info)
A.save(f'{IMAGE_PATH}/update_info.png') A.save(f'{IMAGE_PATH}/update_info.png')
return 200 await bot.send_private_msg(
user_id=int(list(bot.config.superusers)[0]),
message=Message(f"真寻更新完成,版本:{_version} -> {latest_version}\n"
f"更新日期:{data['created_at']}\n"
f"更新日志:\n"
f"{image('update_info.png')}"),
)
return 200, ''
else: else:
logger.warning(f"下载真寻最新版本失败...版本号:{latest_version}") logger.warning(f"下载真寻最新版本失败...版本号:{latest_version}")
await bot.send_private_msg( await bot.send_private_msg(
@@ -117,57 +122,62 @@ async def check_update(bot: Bot) -> int:
await bot.send_private_msg( await bot.send_private_msg(
user_id=int(list(bot.config.superusers)[0]), message=f"自动获取真寻版本失败...." user_id=int(list(bot.config.superusers)[0]), message=f"自动获取真寻版本失败...."
) )
return 999 return 999, ''
def _file_handle(latest_version: str): def _file_handle(latest_version: str) -> str:
if not temp_dir.exists(): if not temp_dir.exists():
temp_dir.mkdir(exist_ok=True, parents=True) temp_dir.mkdir(exist_ok=True, parents=True)
if backup_dir.exists(): if backup_dir.exists():
shutil.rmtree(backup_dir) shutil.rmtree(backup_dir)
backup_dir.mkdir(exist_ok=True, parents=True) tf = None
logger.info("开始解压真寻文件压缩包....") error = ''
tf = tarfile.open(zhenxun_latest_tar_gz) try:
tf.extractall(temp_dir) backup_dir.mkdir(exist_ok=True, parents=True)
logger.info("解压真寻文件压缩包完成....") logger.info("开始解压真寻文件压缩包....")
zhenxun_latest_file = Path(temp_dir) / f"zhenxun_bot-{latest_version[1:]}" tf = tarfile.open(zhenxun_latest_tar_gz)
update_info_file = Path(zhenxun_latest_file) / "update_info.json" tf.extractall(temp_dir)
update_info = json.load(open(update_info_file, "r", encoding="utf8")) logger.info("解压真寻文件压缩包完成....")
update_file = update_info["update_file"] zhenxun_latest_file = Path(temp_dir) / os.listdir(temp_dir)[0]
add_file = update_info["add_file"] update_info_file = Path(zhenxun_latest_file) / "update_info.json"
delete_file = update_info["delete_file"] update_info = json.load(open(update_info_file, "r", encoding="utf8"))
config_file = Path() / "configs" / "config.py" update_file = update_info["update_file"]
config_path_file = Path() / "configs" / "config_path.py" add_file = update_info["add_file"]
for file in delete_file + update_file: delete_file = update_info["delete_file"]
file = Path() / file config_file = Path() / "configs" / "config.py"
backup_file = Path(backup_dir) / file config_path_file = Path() / "configs" / "config_path.py"
if file.exists(): for file in delete_file + update_file:
backup_file.parent.mkdir(parents=True, exist_ok=True) file = Path() / file
if backup_file.exists(): backup_file = Path(backup_dir) / file
backup_file.unlink() if file.exists():
if file not in [config_file, config_path_file]: backup_file.parent.mkdir(parents=True, exist_ok=True)
os.rename(file.absolute(), backup_file.absolute()) if backup_file.exists():
backup_file.unlink()
if file not in [config_file, config_path_file]:
os.rename(file.absolute(), backup_file.absolute())
else:
with open(file, "r", encoding="utf8") as rf:
data = rf.read()
with open(backup_file, "w", encoding="utf8") as wf:
wf.write(data)
logger.info(f"已备份文件:{file}")
for file in add_file + update_file:
new_file = Path(zhenxun_latest_file) / file
old_file = Path() / file
if old_file not in [config_file, config_path_file]:
if not old_file.exists() and new_file.exists():
os.rename(new_file.absolute(), old_file.absolute())
logger.info(f"已更新文件:{file}")
else: else:
with open(file, "r", encoding="utf8") as rf: tmp = ""
data = rf.read() new_lines = open(new_file, "r", encoding="utf8").readlines()
with open(backup_file, "w", encoding="utf8") as wf: old_lines = open(old_file, "r", encoding="utf8").readlines()
wf.write(data) for nl in new_lines:
logger.info(f"已备份文件:{file}") tmp += check_old_lines(old_lines, nl)
for file in add_file + update_file: with open(file, "w", encoding="utf8") as f:
new_file = Path(zhenxun_latest_file) / file f.write(tmp)
old_file = Path() / file except Exception as e:
if old_file not in [config_file, config_path_file]: error = f'{type(e)}:{e}'
if not old_file.exists() and new_file.exists():
os.rename(new_file.absolute(), old_file.absolute())
logger.info(f"已更新文件:{file}")
else:
tmp = ""
new_lines = open(new_file, "r", encoding="utf8").readlines()
old_lines = open(old_file, "r", encoding="utf8").readlines()
for nl in new_lines:
tmp += check_old_lines(old_lines, nl)
with open(file, "w", encoding="utf8") as f:
f.write(tmp)
if tf: if tf:
tf.close() tf.close()
if temp_dir.exists(): if temp_dir.exists():
@@ -179,6 +189,7 @@ def _file_handle(latest_version: str):
local_update_info_file.unlink() local_update_info_file.unlink()
with open(_version_file, "w", encoding="utf8") as f: with open(_version_file, "w", encoding="utf8") as f:
f.write(f"__version__: {latest_version}") f.write(f"__version__: {latest_version}")
return error
# 获取最新版本号 # 获取最新版本号
@@ -215,6 +226,6 @@ def check_old_lines(lines: List[str], line: str) -> str:
return line return line
for l in lines: for l in lines:
if "=" in l and l.split("=")[0].strip() == line.split("=")[0].strip(): if "=" in l and l.split("=")[0].strip() == line.split("=")[0].strip():
if len(l) > len(line): if l.split("=")[1].strip() == 'None':
return l return l
return line return line
+53 -11
View File
@@ -2,26 +2,68 @@ from nonebot import on_command
from nonebot.typing import T_State from nonebot.typing import T_State
from nonebot.adapters.cqhttp import Bot, MessageEvent, GroupMessageEvent from nonebot.adapters.cqhttp import Bot, MessageEvent, GroupMessageEvent
from services.log import logger from services.log import logger
from asyncio.exceptions import TimeoutError
from utils.message_builder import image from utils.message_builder import image
import requests from configs.path_config import IMAGE_PATH
from utils.utils import get_local_proxy
__plugin_name__ = "coser" import aiohttp
import aiofiles
__plugin_usage__ = "用法:发送‘coser’"
__zx_plugin_name__ = "coser"
__plugin_usage__ = """
usage:
三次元也不戳,嘿嘿嘿
指令:
cos/coser
""".strip()
__plugin_des__ = "三次元也不戳,嘿嘿嘿"
__plugin_cmd__ = ["cos/coser"]
__plugin_version__ = 0.1
__plugin_author__ = "HibiKier"
__plugin_settings__ = {
"level": 5,
"default_status": True,
"limit_superuser": False,
"cmd": ["cos", "coser", "括丝", "COS", "Cos", "cOS", "coS"],
}
coser = on_command( coser = on_command(
"cos", aliases={"coser", "括丝", "COS", "Cos", "cOS", "coS"}, priority=5, block=True "cos", aliases={"coser", "括丝", "COS", "Cos", "cOS", "coS"}, priority=5, block=True
) )
url_2 = "http://api.rosysun.cn/cos" url = "http://ovooa.com/API/cosplay/api.php"
@coser.handle() @coser.handle()
async def _(bot: Bot, event: MessageEvent, state: T_State): async def _(bot: Bot, event: MessageEvent, state: T_State):
img_url = requests.get(url_2).text async with aiohttp.ClientSession() as session:
await coser.send(image(img_url)) try:
logger.info( for _ in range(3):
f"(USER {event.user_id}, GROUP {event.group_id if isinstance(event, GroupMessageEvent) else 'private'}) 发送COSER" try:
) async with session.get(url, proxy=get_local_proxy(), timeout=2) as response:
_url = (await response.json())['text']
async with session.get(
_url, timeout=5, proxy=get_local_proxy(), verify_ssl=False
) as res:
if res.status == 200:
async with aiofiles.open(
f"{IMAGE_PATH}/temp/{event.user_id}_coser.jpg", "wb"
) as f:
await f.write(await res.read())
logger.info(
f"(USER {event.user_id}, "
f"GROUP {event.group_id if isinstance(event, GroupMessageEvent) else 'private'})"
f" 发送COSER"
)
await coser.send(
image(f"{event.user_id}_coser.jpg", "temp")
)
break
except (TimeoutError, KeyError):
pass
else:
await coser.send("你cos给我看!")
except Exception as e:
await coser.send("发生了预料之外的错误..请稍后再试或联系管理员修复...")
logger.error(f"coser 发送了未知错误 {type(e)}:{e}")
+43 -13
View File
@@ -8,9 +8,46 @@ from services.log import logger
from utils.message_builder import at from utils.message_builder import at
__plugin_name__ = "联系管理员" __zx_plugin_name__ = "联系管理员"
__plugin_usage__ = "用法:滴滴滴- [消息]" __plugin_usage__ = """
usage:
有什么话想对管理员说嘛?
指令:
[滴滴滴]/滴滴滴- ?[文本] ?[图片]
示例:滴滴滴- 我喜欢你
""".strip()
__plugin_superuser_usage__ = """
superuser usage:
管理员对消息的回复
指令[以下qq与group均为乱打]:
/t: 查看当前存储的消息
/t [qq] [group] [文本]: 在group回复指定用户
/t [qq] [文本]: 私聊用户
/t -1 [group] [文本]: 在group内发送消息
/t [id] [文本]: 回复指定id的对话,id在 /t 中获取
示例:/t 73747222 32848432 你好啊
示例:/t 73747222 你好不好
示例:/t -1 32848432 我不太好
示例:/t 0 我收到你的话了
"""
__plugin_des__ = "跨越空间与时间跟管理员对话"
__plugin_cmd__ = [
"滴滴滴-/[滴滴滴] ?[文本] ?[图片]",
"/t [_superuser]",
"t [qq] [group] [文本] [_superuser]",
"/t [qq] [文本] [_superuser]",
"/t -1 [group] [_superuser]",
"/t [id] [文本] [_superuser]",
]
__plugin_type__ = ("联系管理员",)
__plugin_version__ = 0.1
__plugin_author__ = "HibiKier"
__plugin_settings__ = {
"level": 5,
"default_status": True,
"limit_superuser": False,
"cmd": ["滴滴滴-", "滴滴滴"],
}
dialogue_data = {} dialogue_data = {}
@@ -57,7 +94,7 @@ async def _(bot: Bot, event: MessageEvent, state: T_State):
"user_id": event.user_id, "user_id": event.user_id,
"group_id": group_id, "group_id": group_id,
"group_name": group_name, "group_name": group_name,
"msg": f'{text} {img_msg}', "msg": f"{text} {img_msg}",
} }
# print(dialogue_data) # print(dialogue_data)
logger.info(f"Q{uid}@群{group_id} 联系管理员:{coffee} text:{text}") logger.info(f"Q{uid}@群{group_id} 联系管理员:{coffee} text:{text}")
@@ -66,22 +103,15 @@ async def _(bot: Bot, event: MessageEvent, state: T_State):
@reply.handle() @reply.handle()
async def _(bot: Bot, event: MessageEvent, state: T_State): async def _(bot: Bot, event: MessageEvent, state: T_State):
msg = get_message_text(event.json()) msg = get_message_text(event.json())
if msg in ["帮助"]:
await reply.finish(
f"/t [qq] [group] [text] -> 回复指定用户\n"
f"/t [qq] [text] -> 私聊用户\n"
f"/t -1 [group] -> 在某群发送消息\n"
f"/t [id] [text] -> 回复指定id的对话"
)
if not msg: if not msg:
result = "*****待回复消息总览*****\n" result = "*****待回复消息总览*****\n"
for key in dialogue_data.keys(): for key in dialogue_data.keys():
result += ( result += (
f'id:{key}\n' f"id:{key}\n"
f'\t昵称:{dialogue_data[key]["nickname"]}({dialogue_data[key]["user_id"]})\n' f'\t昵称:{dialogue_data[key]["nickname"]}({dialogue_data[key]["user_id"]})\n'
f'\t群群:{dialogue_data[key]["group_name"]}({dialogue_data[key]["group_id"]})\n' f'\t群群:{dialogue_data[key]["group_name"]}({dialogue_data[key]["group_id"]})\n'
f'\t消息:{dialogue_data[key]["msg"]}' f'\t消息:{dialogue_data[key]["msg"]}'
f'\n--------------------\n' f"\n--------------------\n"
) )
await reply.finish(Message(result[:-1])) await reply.finish(Message(result[:-1]))
msg = msg.split() msg = msg.split()
+209 -84
View File
@@ -1,8 +1,13 @@
from nonebot import on_regex, on_keyword from nonebot import on_regex, on_keyword
from nonebot.adapters.cqhttp import Bot, MessageEvent, Message from nonebot.adapters.cqhttp import Bot, MessageEvent, Message, GroupMessageEvent
from nonebot.permission import SUPERUSER from nonebot.permission import SUPERUSER
from nonebot.typing import T_State from nonebot.typing import T_State
from .genshin_handle import genshin_draw, update_genshin_info, reset_count, reload_genshin_pool from .genshin_handle import (
genshin_draw,
update_genshin_info,
reset_count,
reload_genshin_pool,
)
from .prts_handle import update_prts_info, prts_draw, reload_prts_pool from .prts_handle import update_prts_info, prts_draw, reload_prts_pool
from .pretty_handle import update_pretty_info, pretty_draw, reload_pretty_pool from .pretty_handle import update_pretty_info, pretty_draw, reload_pretty_pool
from .guardian_handle import update_guardian_info, guardian_draw from .guardian_handle import update_guardian_info, guardian_draw
@@ -13,52 +18,164 @@ from .onmyoji_handle import update_onmyoji_info, onmyoji_draw
from .update_game_info import update_info from .update_game_info import update_info
from .util import is_number, check_num from .util import is_number, check_num
from .rule import is_switch from .rule import is_switch
from .config import PRTS_FLAG, PRETTY_FLAG, GUARDIAN_FLAG, GENSHIN_FLAG, PCR_FLAG, AZUR_FLAG, FGO_FLAG, ONMYOJI_FLAG from .config import (
PRTS_FLAG,
PRETTY_FLAG,
GUARDIAN_FLAG,
GENSHIN_FLAG,
PCR_FLAG,
AZUR_FLAG,
FGO_FLAG,
ONMYOJI_FLAG,
)
from .async_update_game_info import async_update_game from .async_update_game_info import async_update_game
import re import re
import asyncio import asyncio
from utils.utils import scheduler from utils.utils import scheduler
from services.log import logger from services.log import logger
__plugin_name__ = '游戏抽卡' __zx_plugin_name__ = "游戏抽卡"
__plugin_usage__ = """
usage:
模拟赛马娘,原神,明日方舟,坎公骑冠剑,公主连结(国/台),碧蓝航线,FGO,阴阳师进行抽卡
指令:
原神[1-300]抽: 原神常驻池
原神角色[1-300]抽: 原神角色UP池子
原神武器[1-300]抽: 原神武器UP池子
重置原神抽卡: 清空当前卡池的抽卡次数[即从0开始计算UP概率]
方舟[1-300]抽: 方舟卡池,当有当期UP时指向UP池
赛马娘[1-200]抽: 赛马娘卡池,当有当期UP时指向UP池
坎公骑冠剑[1-300]抽: 坎公骑冠剑卡池,当有当期UP时指向UP池
pcr/公主连接[1-300]抽: 公主连接卡池
碧蓝航线/碧蓝[重型/轻型/特型][1-300]抽: 碧蓝航线重型/轻型/特型卡池
fgo[1-300]抽: fgo卡池
阴阳师[1-300]抽: 阴阳师卡池
* 以上指令可以通过 XX一井 来指定最大抽取数量 *
* 示例:原神一井 *
""".strip()
__plugin_superuser_usage__ = """
usage:
卡池方面的更新
指令:
更新方舟信息
重载方舟卡池
更新原神信息
重载原神卡池
更新赛马娘信息
重载赛马娘卡池
更新坎公骑冠剑信息
更新碧蓝航线信息
更新fgo信息
更新阴阳师信息
""".strip()
__plugin_des__ = "就算是模拟抽卡也不能改变自己是个非酋"
__plugin_cmd__ = [
"原神[1-300]抽",
"原神角色[1-300]抽",
"原神武器[1-300]抽",
"重置原神抽卡",
"方舟[1-300]抽",
"赛马娘[1-200]抽",
"坎公骑冠剑[1-300]抽",
"pcr/公主连接[1-300]抽",
"fgo[1-300]抽",
"阴阳师[1-300]抽",
"更新方舟信息 [_superuser]",
"重载方舟卡池 [_superuser]",
"更新原神信息 [_superuser]",
"重载原神卡池 [_superuser]",
"更新赛马娘信息 [_superuser]",
"重载赛马娘卡池 [_superuser]",
"更新坎公骑冠剑信息 [_superuser]",
"更新碧蓝航线信息 [_superuser]",
"更新fgo信息 [_superuser]",
"更新阴阳师信息 [_superuser]",
]
__plugin_type__ = ("抽卡相关", 1)
__plugin_version__ = 0.1
__plugin_author__ = "HibiKier"
__plugin_settings__ = {
"level": 5,
"default_status": True,
"limit_superuser": False,
"cmd": ["游戏抽卡", "抽卡"],
}
prts = on_regex(r'.*?方舟[1-9|一][0-9]{0,2}[抽|井]', rule=is_switch('prts'), priority=5, block=True) prts = on_regex(
prts_update = on_keyword({'更新方舟信息', '更新明日方舟信息'}, permission=SUPERUSER, priority=1, block=True) r".*?方舟[1-9|一][0-9]{0,2}[抽|井]", rule=is_switch("prts"), priority=5, block=True
prts_up_reload = on_keyword({'重载方舟卡池'}, priority=1, block=True) )
prts_update = on_keyword(
{"更新方舟信息", "更新明日方舟信息"}, permission=SUPERUSER, priority=1, block=True
)
prts_up_reload = on_keyword({"重载方舟卡池"}, priority=1, block=True)
genshin = on_regex('.*?原神(武器|角色)?池?[1-9|一][0-9]{0,2}[抽|井]', rule=is_switch('genshin'), priority=5, block=True) genshin = on_regex(
genshin_update = on_keyword({'更新原神信息'}, permission=SUPERUSER, priority=1, block=True) ".*?原神(武器|角色)?池?[1-9|一][0-9]{0,2}[抽|井]",
genshin_reset = on_keyword({'重置原神抽卡'}, priority=1, block=True) rule=is_switch("genshin"),
genshin_up_reload = on_keyword({'重载原神卡池'}, priority=1, block=True) priority=5,
block=True,
)
genshin_update = on_keyword({"更新原神信息"}, permission=SUPERUSER, priority=1, block=True)
genshin_reset = on_keyword({"重置原神抽卡"}, priority=1, block=True)
genshin_up_reload = on_keyword({"重载原神卡池"}, priority=1, block=True)
pretty = on_regex('.*?马娘卡?[1-9|一][0-9]{0,2}[抽|井]', rule=is_switch('pretty'), priority=5, block=True) pretty = on_regex(
pretty_update = on_keyword({'更新马娘信息', '更新赛马娘信息'}, permission=SUPERUSER, priority=1, block=True) ".*?马娘卡?[1-9|一][0-9]{0,2}[抽|井]", rule=is_switch("pretty"), priority=5, block=True
pretty_up_reload = on_keyword({'重载赛马娘卡池'}, priority=1, block=True) )
pretty_update = on_keyword(
{"更新马娘信息", "更新赛马娘信息"}, permission=SUPERUSER, priority=1, block=True
)
pretty_up_reload = on_keyword({"重载赛马娘卡池"}, priority=1, block=True)
guardian = on_regex('.*?坎公骑冠剑武?器?[1-9|一][0-9]{0,2}[抽|井]', rule=is_switch('guardian'), priority=5, block=True) guardian = on_regex(
guardian_update = on_keyword({'更新坎公骑冠剑信息'}, permission=SUPERUSER, priority=1, block=True) ".*?坎公骑冠剑武?器?[1-9|一][0-9]{0,2}[抽|井]",
rule=is_switch("guardian"),
priority=5,
block=True,
)
guardian_update = on_keyword(
{"更新坎公骑冠剑信息"}, permission=SUPERUSER, priority=1, block=True
)
pcr = on_regex('.*?(pcr|公主连结|公主连接|公主链接|公主焊接)[1-9|一][0-9]{0,2}[抽|井]', rule=is_switch('pcr'), priority=5, block=True) pcr = on_regex(
pcr_update = on_keyword({'更新pcr信息', '更新公主连结信息'}, permission=SUPERUSER, priority=1, block=True) ".*?(pcr|公主连结|公主连接|公主链接|公主焊接)[1-9|一][0-9]{0,2}[抽|井]",
rule=is_switch("pcr"),
priority=5,
block=True,
)
pcr_update = on_keyword(
{"更新pcr信息", "更新公主连结信息"}, permission=SUPERUSER, priority=1, block=True
)
azur = on_regex('.*?碧蓝航?线?(轻型|重型|特型)池?[1-9|一][0-9]{0,2}[抽]', rule=is_switch('azur'), priority=5, block=True) azur = on_regex(
azur_update = on_keyword({'更新碧蓝信息', '更新碧蓝航线信息'}, permission=SUPERUSER, priority=1, block=True) ".*?碧蓝航?线?(轻型|重型|特型)池?[1-9|一][0-9]{0,2}[抽]",
rule=is_switch("azur"),
priority=5,
block=True,
)
azur_update = on_keyword(
{"更新碧蓝信息", "更新碧蓝航线信息"}, permission=SUPERUSER, priority=1, block=True
)
fgo = on_regex('.*?fgo[1-9|一][0-9]{0,2}[抽]', rule=is_switch('fgo'), priority=5, block=True) fgo = on_regex(
fgo_update = on_keyword({'更新fgo信息'}, permission=SUPERUSER, priority=1, block=True) ".*?fgo[1-9|一][0-9]{0,2}[抽]", rule=is_switch("fgo"), priority=5, block=True
)
fgo_update = on_keyword({"更新fgo信息"}, permission=SUPERUSER, priority=1, block=True)
onmyoji = on_regex('.*?阴阳师[1-9|一][0-9]{0,2}[抽]', rule=is_switch('onmyoji'), priority=5, block=True) onmyoji = on_regex(
onmyoji_update = on_keyword({'更新阴阳师信息'}, permission=SUPERUSER, priority=1, block=True) ".*?阴阳师[1-9|一][0-9]{0,2}[抽]", rule=is_switch("onmyoji"), priority=5, block=True
)
onmyoji_update = on_keyword({"更新阴阳师信息"}, permission=SUPERUSER, priority=1, block=True)
@prts.handle() @prts.handle()
async def _(bot: Bot, event: MessageEvent, state: T_State): async def _(bot: Bot, event: MessageEvent, state: T_State):
msg = str(event.get_message()).strip() msg = str(event.get_message()).strip()
if msg in ['方舟一井', '方舟1井']: if msg in ["方舟一井", "方舟1井"]:
num = 300 num = 300
else: else:
rmsg = re.search(r'.*?方舟(.*)抽', msg) rmsg = re.search(r".*?方舟(.*)抽", msg)
if rmsg: if rmsg:
num, flag = check_num(rmsg.group(1), 300) num, flag = check_num(rmsg.group(1), 300)
if not flag: if not flag:
@@ -67,29 +184,30 @@ async def _(bot: Bot, event: MessageEvent, state: T_State):
return return
await prts.send(await prts_draw(int(num)), at_sender=True) await prts.send(await prts_draw(int(num)), at_sender=True)
logger.info( logger.info(
f"(USER {event.user_id}, GROUP {event.group_id if event.message_type != 'private' else 'private'}) 方舟 {num}抽") f"(USER {event.user_id}, GROUP {event.group_id if isinstance(event, GroupMessageEvent) else 'private'}) 方舟 {num}抽"
)
@prts_up_reload.handle() @prts_up_reload.handle()
async def _(bot: Bot, event: MessageEvent, state: T_State): async def _(bot: Bot, event: MessageEvent, state: T_State):
text = await reload_prts_pool() text = await reload_prts_pool()
await prts_up_reload.finish(Message(f'重载完成!\n{text}')) await prts_up_reload.finish(Message(f"重载完成!\n{text}"))
@genshin.handle() @genshin.handle()
async def _(bot: Bot, event: MessageEvent, state: T_State): async def _(bot: Bot, event: MessageEvent, state: T_State):
msg = str(event.get_message()).strip() msg = str(event.get_message()).strip()
rmsg = re.search(r'.*?原神(武器|角色)?池?(.*)[抽|井]', msg) rmsg = re.search(r".*?原神(武器|角色)?池?(.*)[抽|井]", msg)
if rmsg: if rmsg:
pool_name = rmsg.group(1) pool_name = rmsg.group(1)
if pool_name == '武器': if pool_name == "武器":
pool_name = 'arms' pool_name = "arms"
elif pool_name == '角色': elif pool_name == "角色":
pool_name = 'char' pool_name = "char"
else: else:
pool_name = '' pool_name = ""
num = rmsg.group(2) num = rmsg.group(2)
if msg.find('一井') != -1 or msg.find('1井') != -1: if msg.find("一井") != -1 or msg.find("1井") != -1:
num = 180 num = 180
else: else:
num, flag = check_num(num, 180) num, flag = check_num(num, 180)
@@ -97,41 +215,44 @@ async def _(bot: Bot, event: MessageEvent, state: T_State):
await genshin.finish(num, at_sender=True) await genshin.finish(num, at_sender=True)
else: else:
return return
await genshin.send(await genshin_draw(event.user_id, int(num), pool_name), at_sender=True) await genshin.send(
await genshin_draw(event.user_id, int(num), pool_name), at_sender=True
)
logger.info( logger.info(
f"(USER {event.user_id}, GROUP {event.group_id if event.message_type != 'private' else 'private'}) 原神 {num}抽") f"(USER {event.user_id}, GROUP {event.group_id if isinstance(event, GroupMessageEvent) else 'private'}) 原神 {num}抽"
)
@genshin_up_reload.handle() @genshin_up_reload.handle()
async def _(bot: Bot, event: MessageEvent, state: T_State): async def _(bot: Bot, event: MessageEvent, state: T_State):
text = await reload_genshin_pool() text = await reload_genshin_pool()
await genshin_reset.finish(Message(f'重载成功!\n{text}')) await genshin_reset.finish(Message(f"重载成功!\n{text}"))
@genshin_reset.handle() @genshin_reset.handle()
async def _(bot: Bot, event: MessageEvent, state: T_State): async def _(bot: Bot, event: MessageEvent, state: T_State):
reset_count(event.user_id) reset_count(event.user_id)
await genshin_reset.send('重置了原神抽卡次数', at_sender=True) await genshin_reset.send("重置了原神抽卡次数", at_sender=True)
@pretty.handle() @pretty.handle()
async def _(bot: Bot, event: MessageEvent, state: T_State): async def _(bot: Bot, event: MessageEvent, state: T_State):
msg = str(event.get_message()).strip() msg = str(event.get_message()).strip()
if msg.find('1井') != -1 or msg.find('一井') != -1: if msg.find("1井") != -1 or msg.find("一井") != -1:
num = 200 num = 200
if msg.find("卡") == -1: if msg.find("卡") == -1:
pool_name = 'char' pool_name = "char"
else: else:
pool_name = 'card' pool_name = "card"
else: else:
rmsg = re.search(r'.*?马娘(.*)抽', msg) rmsg = re.search(r".*?马娘(.*)抽", msg)
if rmsg: if rmsg:
num = rmsg.group(1) num = rmsg.group(1)
if num[0] == '卡': if num[0] == "卡":
num = num[1:] num = num[1:]
pool_name = 'card' pool_name = "card"
else: else:
pool_name = 'char' pool_name = "char"
num, flag = check_num(num, 200) num, flag = check_num(num, 200)
if not flag: if not flag:
await pretty.finish(num, at_sender=True) await pretty.finish(num, at_sender=True)
@@ -139,30 +260,31 @@ async def _(bot: Bot, event: MessageEvent, state: T_State):
return return
await pretty.send(await pretty_draw(int(num), pool_name), at_sender=True) await pretty.send(await pretty_draw(int(num), pool_name), at_sender=True)
logger.info( logger.info(
f"(USER {event.user_id}, GROUP {event.group_id if event.message_type != 'private' else 'private'}) 赛马娘 {num}抽") f"(USER {event.user_id}, GROUP {event.group_id if isinstance(event, GroupMessageEvent) else 'private'}) 赛马娘 {num}抽"
)
@pretty_up_reload.handle() @pretty_up_reload.handle()
async def _(bot: Bot, event: MessageEvent, state: T_State): async def _(bot: Bot, event: MessageEvent, state: T_State):
text = await reload_pretty_pool() text = await reload_pretty_pool()
await genshin_reset.finish(Message(f'重载成功!\n{text}')) await genshin_reset.finish(Message(f"重载成功!\n{text}"))
@guardian.handle() @guardian.handle()
async def _(bot: Bot, event: MessageEvent, state: T_State): async def _(bot: Bot, event: MessageEvent, state: T_State):
msg = str(event.get_message()).strip() msg = str(event.get_message()).strip()
pool_name = 'char' pool_name = "char"
if msg.find('1井') != -1 or msg.find('一井') != -1: if msg.find("1井") != -1 or msg.find("一井") != -1:
num = 300 num = 300
if msg.find('武器') != -1: if msg.find("武器") != -1:
pool_name = 'arms' pool_name = "arms"
else: else:
rmsg = re.search(r'.*?坎公骑冠剑(.*)抽', msg) rmsg = re.search(r".*?坎公骑冠剑(.*)抽", msg)
if rmsg: if rmsg:
num = rmsg.group(1) num = rmsg.group(1)
if num.find('武器') != -1: if num.find("武器") != -1:
pool_name = 'arms' pool_name = "arms"
num = num.replace('武器', '') num = num.replace("武器", "")
num, flag = check_num(num, 300) num, flag = check_num(num, 300)
if not flag: if not flag:
await guardian.finish(num, at_sender=True) await guardian.finish(num, at_sender=True)
@@ -170,16 +292,17 @@ async def _(bot: Bot, event: MessageEvent, state: T_State):
return return
await guardian.send(await guardian_draw(int(num), pool_name), at_sender=True) await guardian.send(await guardian_draw(int(num), pool_name), at_sender=True)
logger.info( logger.info(
f"(USER {event.user_id}, GROUP {event.group_id if event.message_type != 'private' else 'private'}) 坎公骑冠剑 {num}抽") f"(USER {event.user_id}, GROUP {event.group_id if isinstance(event, GroupMessageEvent) else 'private'}) 坎公骑冠剑 {num}抽"
)
@pcr.handle() @pcr.handle()
async def _(bot: Bot, event: MessageEvent, state: T_State): async def _(bot: Bot, event: MessageEvent, state: T_State):
msg = str(event.get_message()).strip() msg = str(event.get_message()).strip()
if msg.find('1井') != -1 or msg.find('一井') != -1: if msg.find("1井") != -1 or msg.find("一井") != -1:
num = 300 num = 300
else: else:
rmsg = re.search(r'.*?(pcr|公主连结)(.*)[抽|井]', msg) rmsg = re.search(r".*?(pcr|公主连结)(.*)[抽|井]", msg)
if rmsg: if rmsg:
num, flag = check_num(rmsg.group(2), 300) num, flag = check_num(rmsg.group(2), 300)
if not flag: if not flag:
@@ -188,13 +311,14 @@ async def _(bot: Bot, event: MessageEvent, state: T_State):
return return
await pcr.send(await pcr_draw(int(num)), at_sender=True) await pcr.send(await pcr_draw(int(num)), at_sender=True)
logger.info( logger.info(
f"(USER {event.user_id}, GROUP {event.group_id if event.message_type != 'private' else 'private'}) 公主连结 {num}抽") f"(USER {event.user_id}, GROUP {event.group_id if isinstance(event, GroupMessageEvent) else 'private'}) 公主连结 {num}抽"
)
@azur.handle() @azur.handle()
async def _(bot: Bot, event: MessageEvent, state: T_State): async def _(bot: Bot, event: MessageEvent, state: T_State):
msg = str(event.get_message()).strip() msg = str(event.get_message()).strip()
rmsg = re.search('.*?碧蓝航?线?(轻型|重型|特型)池?(.*)[抽]', msg) rmsg = re.search(".*?碧蓝航?线?(轻型|重型|特型)池?(.*)[抽]", msg)
if rmsg: if rmsg:
pool_name = rmsg.group(1) pool_name = rmsg.group(1)
num, flag = check_num(rmsg.group(2), 300) num, flag = check_num(rmsg.group(2), 300)
@@ -204,13 +328,14 @@ async def _(bot: Bot, event: MessageEvent, state: T_State):
return return
await azur.send(await azur_draw(int(num), pool_name), at_sender=True) await azur.send(await azur_draw(int(num), pool_name), at_sender=True)
logger.info( logger.info(
f"(USER {event.user_id}, GROUP {event.group_id if event.message_type != 'private' else 'private'}) 碧蓝航线 {num}抽") f"(USER {event.user_id}, GROUP {event.group_id if isinstance(event, GroupMessageEvent) else 'private'}) 碧蓝航线 {num}抽"
)
@fgo.handle() @fgo.handle()
async def _(bot: Bot, event: MessageEvent, state: T_State): async def _(bot: Bot, event: MessageEvent, state: T_State):
msg = str(event.get_message()).strip() msg = str(event.get_message()).strip()
rmsg = re.search('.*?fgo(.*)抽', msg) rmsg = re.search(".*?fgo(.*)抽", msg)
if rmsg: if rmsg:
num, flag = check_num(rmsg.group(1), 300) num, flag = check_num(rmsg.group(1), 300)
if not flag: if not flag:
@@ -219,13 +344,14 @@ async def _(bot: Bot, event: MessageEvent, state: T_State):
return return
await fgo.send(await fgo_draw(int(num)), at_sender=True) await fgo.send(await fgo_draw(int(num)), at_sender=True)
logger.info( logger.info(
f"(USER {event.user_id}, GROUP {event.group_id if event.message_type != 'private' else 'private'}) fgo {num}抽") f"(USER {event.user_id}, GROUP {event.group_id if isinstance(event, GroupMessageEvent) else 'private'}) fgo {num}抽"
)
@onmyoji.handle() @onmyoji.handle()
async def _(bot: Bot, event: MessageEvent, state: T_State): async def _(bot: Bot, event: MessageEvent, state: T_State):
msg = str(event.get_message()).strip() msg = str(event.get_message()).strip()
rmsg = re.search('.*?阴阳师(.*)抽', msg) rmsg = re.search(".*?阴阳师(.*)抽", msg)
if rmsg: if rmsg:
num, flag = check_num(rmsg.group(1), 300) num, flag = check_num(rmsg.group(1), 300)
if not flag: if not flag:
@@ -234,60 +360,61 @@ async def _(bot: Bot, event: MessageEvent, state: T_State):
return return
await onmyoji.send(await onmyoji_draw(int(num)), at_sender=True) await onmyoji.send(await onmyoji_draw(int(num)), at_sender=True)
logger.info( logger.info(
f"(USER {event.user_id}, GROUP {event.group_id if event.message_type != 'private' else 'private'}) 阴阳师 {num}抽") f"(USER {event.user_id}, GROUP {event.group_id if isinstance(event, GroupMessageEvent) else 'private'}) 阴阳师 {num}抽"
)
@prts_update.handle() @prts_update.handle()
async def _(bot: Bot, event: MessageEvent, state: T_State): async def _(bot: Bot, event: MessageEvent, state: T_State):
await update_prts_info() await update_prts_info()
await prts_update.finish('更新完成!') await prts_update.finish("更新完成!")
@genshin_update.handle() @genshin_update.handle()
async def _(bot: Bot, event: MessageEvent, state: T_State): async def _(bot: Bot, event: MessageEvent, state: T_State):
await update_genshin_info() await update_genshin_info()
await genshin_update.finish('更新完成!') await genshin_update.finish("更新完成!")
@pretty_update.handle() @pretty_update.handle()
async def _(bot: Bot, event: MessageEvent, state: T_State): async def _(bot: Bot, event: MessageEvent, state: T_State):
await update_pretty_info() await update_pretty_info()
await genshin_update.finish('更新完成!') await genshin_update.finish("更新完成!")
@guardian_update.handle() @guardian_update.handle()
async def _(bot: Bot, event: MessageEvent, state: T_State): async def _(bot: Bot, event: MessageEvent, state: T_State):
await update_guardian_info() await update_guardian_info()
await genshin_update.finish('更新完成!') await genshin_update.finish("更新完成!")
@pcr_update.handle() @pcr_update.handle()
async def _(bot: Bot, event: MessageEvent, state: T_State): async def _(bot: Bot, event: MessageEvent, state: T_State):
await update_pcr_info() await update_pcr_info()
await genshin_update.finish('更新完成!') await genshin_update.finish("更新完成!")
@azur_update.handle() @azur_update.handle()
async def _(bot: Bot, event: MessageEvent, state: T_State): async def _(bot: Bot, event: MessageEvent, state: T_State):
await update_azur_info() await update_azur_info()
await genshin_update.finish('更新完成!') await genshin_update.finish("更新完成!")
@fgo_update.handle() @fgo_update.handle()
async def _(bot: Bot, event: MessageEvent, state: T_State): async def _(bot: Bot, event: MessageEvent, state: T_State):
await update_fgo_info() await update_fgo_info()
await genshin_update.finish('更新完成!') await genshin_update.finish("更新完成!")
@onmyoji_update.handle() @onmyoji_update.handle()
async def _(bot: Bot, event: MessageEvent, state: T_State): async def _(bot: Bot, event: MessageEvent, state: T_State):
await update_onmyoji_info() await update_onmyoji_info()
await genshin_update.finish('更新完成!') await genshin_update.finish("更新完成!")
# 更新资源 # 更新资源
@scheduler.scheduled_job( @scheduler.scheduled_job(
'cron', "cron",
hour=4, hour=4,
minute=1, minute=1,
) )
@@ -310,42 +437,40 @@ async def _():
if ONMYOJI_FLAG: if ONMYOJI_FLAG:
tasks.append(asyncio.ensure_future(update_onmyoji_info())) tasks.append(asyncio.ensure_future(update_onmyoji_info()))
await asyncio.gather(*tasks) await asyncio.gather(*tasks)
logger.info('draw_card 抽卡自动更新完成...') logger.info("draw_card 抽卡自动更新完成...")
# 每天四点重载方舟up卡池 # 每天四点重载方舟up卡池
@scheduler.scheduled_job( @scheduler.scheduled_job(
'cron', "cron",
hour=4, hour=4,
minute=1, minute=1,
) )
async def _(): async def _():
if PRTS_FLAG: if PRTS_FLAG:
await reload_prts_pool() await reload_prts_pool()
logger.info('自动重载方舟卡池UP成功') logger.info("自动重载方舟卡池UP成功")
# 每天四点重载赛马娘up卡池 # 每天四点重载赛马娘up卡池
@scheduler.scheduled_job( @scheduler.scheduled_job(
'cron', "cron",
hour=4, hour=4,
minute=1, minute=1,
) )
async def _(): async def _():
if PRETTY_FLAG: if PRETTY_FLAG:
await reload_pretty_pool() await reload_pretty_pool()
logger.info('自动重载赛马娘UP成功') logger.info("自动重载赛马娘UP成功")
# 每天下午六点点重载原神up卡池 # 每天下午六点点重载原神up卡池
@scheduler.scheduled_job( @scheduler.scheduled_job(
'cron', "cron",
hour=18, hour=18,
minute=1, minute=1,
) )
async def _(): async def _():
if PRTS_FLAG: if PRTS_FLAG:
await reload_genshin_pool() await reload_genshin_pool()
logger.info('自动重载原神卡池UP成功') logger.info("自动重载原神卡池UP成功")
+6 -1
View File
@@ -1,7 +1,7 @@
import aiohttp import aiohttp
from bs4 import BeautifulSoup from bs4 import BeautifulSoup
import re import re
from datetime import datetime from datetime import datetime, timedelta
from .config import DRAW_PATH from .config import DRAW_PATH
from pathlib import Path from pathlib import Path
from asyncio.exceptions import TimeoutError from asyncio.exceptions import TimeoutError
@@ -177,6 +177,11 @@ class GenshinAnnouncement:
data[itype]['time'] = trs[1].find('td').text data[itype]['time'] = trs[1].find('td').text
if data[itype]['time'][-1] == '\n': if data[itype]['time'][-1] == '\n':
data[itype]['time'] = data[itype]['time'][:-1] data[itype]['time'] = data[itype]['time'][:-1]
if '版本更新后' in data[itype]['time']:
sp = data[itype]['time'].split('~')
end_time = datetime.strptime(sp[1].strip(), "%Y/%m/%d %H:%M:%S")
start_time = end_time - timedelta(days=20)
data[itype]['time'] = start_time.strftime('%Y/%m/%d') + ' ~ ' + end_time.strftime('%Y/%m/%d')
tmp = '' tmp = ''
for tm in data[itype]['time'].split('~'): for tm in data[itype]['time'].split('~'):
date_time_sp = tm.split('/') date_time_sp = tm.split('/')
+7 -5
View File
@@ -1,5 +1,6 @@
from typing import Any from typing import Any
from .config import DATA_PATH from .config import DATA_PATH
from utils.utils import is_number
from pathlib import Path from pathlib import Path
try: try:
import ujson as json import ujson as json
@@ -65,11 +66,12 @@ def init_game_pool(game: str, data: dict, Operator: Any):
tmp_lst.append(Operator(name=data[key]['名称'], star=int(data[key]['星级']), limited=limited)) tmp_lst.append(Operator(name=data[key]['名称'], star=int(data[key]['星级']), limited=limited))
if game == 'azur': if game == 'azur':
for key in data.keys(): for key in data.keys():
limited = False if is_number(data[key]['星级']):
if '可以建造' not in data[key]['获取途径']: limited = False
limited = True if '可以建造' not in data[key]['获取途径']:
tmp_lst.append(Operator(name=data[key]['名称'], star=int(data[key]['星级']), limited = True
limited=limited, itype=data[key]['类型'])) tmp_lst.append(Operator(name=data[key]['名称'], star=int(data[key]['星级']),
limited=limited, itype=data[key]['类型']))
if game in ['fgo', 'fgo_card']: if game in ['fgo', 'fgo_card']:
for key in data.keys(): for key in data.keys():
limited = False limited = False
+18 -23
View File
@@ -1,42 +1,37 @@
from nonebot.rule import Rule from nonebot.rule import Rule
from nonebot.adapters.cqhttp import Bot, MessageEvent from nonebot.adapters.cqhttp import Bot, MessageEvent
from nonebot.typing import T_State from nonebot.typing import T_State
from .config import GENSHIN_FLAG, PRTS_FLAG, PRETTY_FLAG, GUARDIAN_FLAG, PCR_FLAG, AZUR_FLAG, FGO_FLAG, ONMYOJI_FLAG from .config import (
GENSHIN_FLAG,
PRTS_FLAG,
PRETTY_FLAG,
GUARDIAN_FLAG,
PCR_FLAG,
AZUR_FLAG,
FGO_FLAG,
ONMYOJI_FLAG,
)
def is_switch(game_name: str) -> Rule: def is_switch(game_name: str) -> Rule:
async def _is_switch(bot: Bot, event: MessageEvent, state: T_State) -> bool: async def _is_switch(bot: Bot, event: MessageEvent, state: T_State) -> bool:
if game_name == 'prts': if game_name == "prts":
return PRTS_FLAG return PRTS_FLAG
if game_name == 'genshin': if game_name == "genshin":
return GENSHIN_FLAG return GENSHIN_FLAG
if game_name == 'pretty': if game_name == "pretty":
return PRETTY_FLAG return PRETTY_FLAG
if game_name == 'guardian': if game_name == "guardian":
return GUARDIAN_FLAG return GUARDIAN_FLAG
if game_name == 'pcr': if game_name == "pcr":
return PCR_FLAG return PCR_FLAG
if game_name == 'azur': if game_name == "azur":
return AZUR_FLAG return AZUR_FLAG
if game_name == 'fgo': if game_name == "fgo":
return FGO_FLAG return FGO_FLAG
if game_name == 'onmyoji': if game_name == "onmyoji":
return ONMYOJI_FLAG return ONMYOJI_FLAG
else: else:
return False return False
return Rule(_is_switch) return Rule(_is_switch)
+6 -2
View File
@@ -83,6 +83,8 @@ async def _last_check(data: dict, game_name: str, session: aiohttp.ClientSession
'-舰娘头像外框精锐.png', '-舰娘头像外框精锐.png',
'https://patchwiki.biligame.com/images/blhx/thumb/a/a2/ptog1j220x5q02hytpwc8al7f229qk9.png/60px-' 'https://patchwiki.biligame.com/images/blhx/thumb/a/a2/ptog1j220x5q02hytpwc8al7f229qk9.png/60px-'
'舰娘头像外框超稀有.png' '舰娘头像外框超稀有.png'
'https://patchwiki.biligame.com/images/blhx/thumb/0/0f/n28p7p3opfn5mhgjyio55ljsllhknwz.png/60px-'
'舰娘头像外框精锐META.png'
]: ]:
await download_img(url, 'azur', f'{idx}_star') await download_img(url, 'azur', f'{idx}_star')
idx += 1 idx += 1
@@ -135,7 +137,7 @@ async def retrieve_char_data(char: bs4.element.Tag, game_name: str, data: dict,
member_dict['头像'] = unquote(str(avatar_img['src']).split(' ')[-2]) member_dict['头像'] = unquote(str(avatar_img['src']).split(' ')[-2])
except TypeError: except TypeError:
member_dict['头像'] = "img link not find..." member_dict['头像'] = "img link not find..."
logger(f'{member_dict["名称"]} 图片缺失....') logger.warning(f'{member_dict["名称"]} 图片缺失....')
star = char.find('div').find('img')['alt'] star = char.find('div').find('img')['alt']
if star == '舰娘头像外框普通.png': if star == '舰娘头像外框普通.png':
star = 1 star = 1
@@ -147,7 +149,9 @@ async def retrieve_char_data(char: bs4.element.Tag, game_name: str, data: dict,
star = 4 star = 4
elif star == '舰娘头像外框海上传奇.png': elif star == '舰娘头像外框海上传奇.png':
star = 5 star = 5
elif star in ['舰娘头像外框最高方案.png', '舰娘头像外框决战方案.png', '舰娘头像外框超稀有META.png']: elif star in ['舰娘头像外框最高方案.png', '舰娘头像外框决战方案.png', '舰娘头像外框超稀有META.png', '舰娘头像外框精锐META.png']:
star = 6
else:
star = 6 star = 6
member_dict['星级'] = star member_dict['星级'] = star
member_dict['类型'] = azur_type[str(index)] member_dict['类型'] = azur_type[str(index)]
+39 -18
View File
@@ -3,22 +3,41 @@ from services.log import logger
from nonebot.adapters.cqhttp import Bot, MessageEvent, GroupMessageEvent from nonebot.adapters.cqhttp import Bot, MessageEvent, GroupMessageEvent
from nonebot.typing import T_State from nonebot.typing import T_State
from utils.utils import scheduler, get_bot from utils.utils import scheduler, get_bot
from .data_source import get_epic_game from .data_source import get_epic_free
from models.group_remind import GroupRemind from utils.manager import group_manager
from nonebot.adapters.cqhttp.exception import ActionFailed
__plugin_name__ = "epic免费游戏提醒"
__plugin_usage__ = "用法:发送’epic‘"
__zx_plugin_name__ = "epic免费游戏"
__plugin_usage__ = """
usage:
可以不玩,不能没有,每日白嫖
指令:
epic
""".strip()
__plugin_des__ = "可以不玩,不能没有,每日白嫖"
__plugin_cmd__ = ["epic"]
__plugin_version__ = 0.1
__plugin_author__ = "AkashiCoin"
__plugin_settings__ = {
"level": 5,
"default_status": True,
"limit_superuser": False,
"cmd": ["epic"],
}
__plugin_task__ = {"epic_free_game": "epic免费游戏"}
epic = on_command("epic", priority=5, block=True) epic = on_command("epic", priority=5, block=True)
@epic.handle() @epic.handle()
async def _(bot: Bot, event: MessageEvent, state: T_State): async def handle(bot: Bot, event: MessageEvent, state: T_State):
result, code = await get_epic_game() msg_list, code = await get_epic_free(bot, event)
await epic.send(result) if code == 404:
await epic.send(msg_list)
elif isinstance(event, GroupMessageEvent):
await bot.send_group_forward_msg(group_id=event.group_id, messages=msg_list)
else:
for msg in msg_list:
await bot.send_private_msg(user_id=event.user_id, message=msg)
logger.info( logger.info(
f"(USER {event.user_id}, GROUP {event.group_id if isinstance(event, GroupMessageEvent) else 'private'})" f"(USER {event.user_id}, GROUP {event.group_id if isinstance(event, GroupMessageEvent) else 'private'})"
f" 获取epic免费游戏" f" 获取epic免费游戏"
@@ -33,13 +52,15 @@ async def _(bot: Bot, event: MessageEvent, state: T_State):
) )
async def _(): async def _():
bot = get_bot() bot = get_bot()
gl = await bot.get_group_list(self_id=bot.self_id) gl = await bot.get_group_list()
gl = [g["group_id"] for g in gl] gl = [g["group_id"] for g in gl]
for g in gl: for g in gl:
if await GroupRemind.get_status(g, "epic"): if await group_manager.check_group_task_status(g, "epic_free_game"):
result, code = await get_epic_game() try:
if code == 200: msg_list, code = await get_epic_free(bot, GroupMessageEvent)
try: if code == 200:
await bot.send_group_msg(group_id=g, message=result) await bot.send_group_forward_msg(group_id=g, messages=msg_list)
except ActionFailed: else:
logger.error(f"{g}群 epic免费游戏推送错误") await bot.send_group_msg(group_id=g, message=msg_list)
except Exception as e:
logger.error(f"GROUP {g} epic免费游戏推送错误 {type(e)}: {e}")
+154 -51
View File
@@ -1,56 +1,159 @@
import aiohttp from httpx import AsyncClient
import aiofiles from datetime import datetime
from utils.utils import get_local_proxy from nonebot.log import logger
import feedparser from nonebot.adapters.cqhttp import (
import platform Bot,
from utils.message_builder import image Event,
from configs.path_config import IMAGE_PATH GroupMessageEvent,
from utils.user_agent import get_user_agent )
from asyncio.exceptions import TimeoutError from configs.config import NICKNAME
from configs.config import RSSHUBAPP
if platform.system() == "Windows":
import asyncio
asyncio.set_event_loop_policy(asyncio.WindowsSelectorEventLoopPolicy())
url = f"{RSSHUBAPP}/epicgames/freegames" # 获取所有 Epic Game Store 促销游戏
# 方法参考:RSSHub /epicgames 路由
# https://github.com/DIYgod/RSSHub/blob/master/lib/routes/epicgames/index.js
async def get_epic_game():
epic_url = "https://www.epicgames.com/store/backend/graphql-proxy"
headers = {
"Referer": "https://www.epicgames.com/store/zh-CN/",
"Content-Type": "application/json; charset=utf-8",
}
data = {
"query": "query searchStoreQuery($allowCountries: String, $category: String, $count: Int, $country: String!, $keywords: String, $locale: String, $namespace: String, $sortBy: String, $sortDir: String, $start: Int, $tag: String, $withPrice: Boolean = false, $withPromotions: Boolean = false) {\n Catalog {\n searchStore(allowCountries: $allowCountries, category: $category, count: $count, country: $country, keywords: $keywords, locale: $locale, namespace: $namespace, sortBy: $sortBy, sortDir: $sortDir, start: $start, tag: $tag) {\n elements {\n title\n id\n namespace\n description\n effectiveDate\n keyImages {\n type\n url\n }\n seller {\n id\n name\n }\n productSlug\n urlSlug\n url\n items {\n id\n namespace\n }\n customAttributes {\n key\n value\n }\n categories {\n path\n }\n price(country: $country) @include(if: $withPrice) {\n totalPrice {\n discountPrice\n originalPrice\n voucherDiscount\n discount\n currencyCode\n currencyInfo {\n decimals\n }\n fmtPrice(locale: $locale) {\n originalPrice\n discountPrice\n intermediatePrice\n }\n }\n lineOffers {\n appliedRules {\n id\n endDate\n discountSetting {\n discountType\n }\n }\n }\n }\n promotions(category: $category) @include(if: $withPromotions) {\n promotionalOffers {\n promotionalOffers {\n startDate\n endDate\n discountSetting {\n discountType\n discountPercentage\n }\n }\n }\n upcomingPromotionalOffers {\n promotionalOffers {\n startDate\n endDate\n discountSetting {\n discountType\n discountPercentage\n }\n }\n }\n }\n }\n paging {\n count\n total\n }\n }\n }\n}\n",
"variables": {
"allowCountries": "CN",
"category": "freegames",
"count": 1000,
"country": "CN",
"locale": "zh-CN",
"sortBy": "effectiveDate",
"sortDir": "asc",
"withPrice": True,
"withPromotions": True,
},
}
async with AsyncClient(headers=headers) as client:
try:
res = await client.post(epic_url, json=data, timeout=10.0)
res_json = res.json()
games = res_json["data"]["Catalog"]["searchStore"]["elements"]
return games
except Exception as e:
logger.error(str(e))
return None
async def get_epic_game() -> "str, int": # 获取 Epic Game Store 免费游戏信息
result = "今天没有游戏可以白嫖了!" # 处理免费游戏的信息方法借鉴 pip 包 epicstore_api 示例
code = 999 # https://github.com/SD4RK/epicstore_api/blob/master/examples/free_games_example.py
try: async def get_epic_free(bot: Bot, event: Event):
async with aiohttp.ClientSession(headers=get_user_agent()) as session: games = await get_epic_game()
async with session.get(url, proxy=get_local_proxy(), timeout=7) as response: if not games:
data = feedparser.parse(await response.text())["entries"] return "Epic 可能又抽风啦,请稍后再试(", 404
if len(data) == 0: else:
return result msg_list = []
index = 0 for game in games:
for item in data: game_name = game["title"]
title = item["title"] game_corp = game["seller"]["name"]
img_url = item["summary"][ game_price = game["price"]["totalPrice"]["fmtPrice"]["originalPrice"]
item["summary"].find('src="') + 5 : item["summary"].rfind('"') # 赋初值以避免 local variable referenced before assignment
] game_dev, game_pub, game_thumbnail = (None, None, None)
async with session.get( try:
img_url, proxy=get_local_proxy(), timeout=7 game_promotions = game["promotions"]["promotionalOffers"]
) as res: upcoming_promotions = game["promotions"]["upcomingPromotionalOffers"]
async with aiofiles.open( if not game_promotions and upcoming_promotions:
IMAGE_PATH + f"temp/epic_{index}.jpg", "wb" # 促销暂未上线,但即将上线
) as f: promotion_data = upcoming_promotions[0]["promotionalOffers"][0]
await f.write(await res.read()) start_date_iso, end_date_iso = (
link = item["link"] promotion_data["startDate"][:-1],
result += ( promotion_data["endDate"][:-1],
image(f"epic_{index}.jpg", "temp")
+ f"\n【游戏】| {title}\n【链接】 | {link}\n"
) )
code = 200 # 删除字符串中最后一个 "Z" 使 Python datetime 可处理此时间
index += 1 start_date = datetime.fromisoformat(start_date_iso).strftime(
if result != "": "%b.%d %H:%M"
result = "epic限免游戏(速速白嫖):\n" + result )
else: end_date = datetime.fromisoformat(end_date_iso).strftime(
result = "今天没有游戏可以白嫖了!" "%b.%d %H:%M"
except TimeoutError: )
return "请求超时!", code if isinstance(event, GroupMessageEvent):
return result, code _message = "\n由 {} 公司发行的游戏 {} ({}) 在 UTC 时间 {} 即将推出免费游玩,预计截至 {}。".format(
game_corp, game_name, game_price, start_date, end_date
)
data = {
"type": "node",
"data": {
"name": f"这里是{NICKNAME}酱",
"uin": f"{bot.self_id}",
"content": _message,
},
}
msg_list.append(data)
else:
msg = "\n由 {} 公司发行的游戏 {} ({}) 在 UTC 时间 {} 即将推出免费游玩,预计截至 {}。".format(
game_corp, game_name, game_price, start_date, end_date
)
msg_list.append(msg)
else:
for image in game["keyImages"]:
if image["type"] == "Thumbnail":
game_thumbnail = image["url"]
for pair in game["customAttributes"]:
if pair["key"] == "developerName":
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"
)
# 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 isinstance(event, GroupMessageEvent):
_message = "[CQ:image,file={}]\n\nFREE now :: {} ({})\n{}\n此游戏由 {} 开发、{} 发行,将在 UTC 时间 {} 结束免费游玩,戳链接速度加入你的游戏库吧~\n{}\n".format(
game_thumbnail,
game_name,
game_price,
game_desp,
game_dev,
game_pub,
end_date,
game_url,
)
data = {
"type": "node",
"data": {
"name": f"这里是{NICKNAME}酱",
"uin": f"{bot.self_id}",
"content": _message,
},
}
msg_list.append(data)
else:
msg = "[CQ:image,file={}]\n\nFREE now :: {} ({})\n{}\n此游戏由 {} 开发、{} 发行,将在 UTC 时间 {} 结束免费游玩,戳链接速度加入你的游戏库吧~\n{}\n".format(
game_thumbnail,
game_name,
game_price,
game_desp,
game_dev,
game_pub,
end_date,
game_url,
)
msg_list.append(msg)
except TypeError as e:
# logger.info(str(e))
pass
return msg_list, 200
+18 -6
View File
@@ -6,12 +6,24 @@ from utils.message_builder import share
from services.log import logger from services.log import logger
__plugin_name = "假消息" __zx_plugin_name__ = "构造分享消息"
__plugin_usage__ = """
__plugin_usage__ = ( usage:
"用法:\n格式:假消息 [网址] [标题] [内容](可省) [图片](可省)\n" 自定义的分享消息构造
"示例:假消息 www.4399.com 我喜欢萝莉 为什么我喜欢... [图片]" 指令:
) 假消息 [网址] [标题] ?[内容] ?[图片]
示例:假消息 www.4399.com 我喜欢萝莉 为什么我喜欢... [图片]
""".strip()
__plugin_des__ = "自定义的分享消息构造"
__plugin_cmd__ = ["假消息 [网址] [标题] ?[内容] ?[图片]"]
__plugin_version__ = 0.1
__plugin_author__ = "HibiKier"
__plugin_settings__ = {
"level": 5,
"default_status": True,
"limit_superuser": False,
"cmd": ["假消息"],
}
fake_msg = on_command("假消息", priority=5, block=True) fake_msg = on_command("假消息", priority=5, block=True)
+40 -22
View File
@@ -9,9 +9,21 @@ from nonebot.typing import T_State
from nonebot.adapters.cqhttp import Bot, GroupMessageEvent from nonebot.adapters.cqhttp import Bot, GroupMessageEvent
import aiohttp import aiohttp
import aiofiles import aiofiles
from collections import defaultdict
from asyncio.exceptions import TimeoutError from asyncio.exceptions import TimeoutError
from configs.config import FUDU_PROBABILITY from configs.config import FUDU_PROBABILITY
from utils.manager import group_manager
__zx_plugin_name__ = "复读"
__plugin_usage__ = """
usage:
重复3次相同的消息时会复读
""".strip()
__plugin_des__ = "群友的本质是什么?是复读机哒!"
__plugin_type__ = ("被动相关",)
__plugin_version__ = 0.1
__plugin_author__ = "HibiKier"
__plugin_task__ = {"fudu": "复读"}
class Fudu: class Fudu:
@@ -20,39 +32,39 @@ class Fudu:
def append(self, key, content): def append(self, key, content):
self._create(key) self._create(key)
self.data[key]['data'].append(content) self.data[key]["data"].append(content)
def clear(self, key): def clear(self, key):
self._create(key) self._create(key)
self.data[key]['data'] = [] self.data[key]["data"] = []
self.data[key]['is_repeater'] = False self.data[key]["is_repeater"] = False
def size(self, key) -> int: def size(self, key) -> int:
self._create(key) self._create(key)
return len(self.data[key]['data']) return len(self.data[key]["data"])
def check(self, key, content) -> bool: def check(self, key, content) -> bool:
self._create(key) self._create(key)
return self.data[key]['data'][0] == content return self.data[key]["data"][0] == content
def get(self, key): def get(self, key):
self._create(key) self._create(key)
return self.data[key]['data'][0] return self.data[key]["data"][0]
def is_repeater(self, key): def is_repeater(self, key):
self._create(key) self._create(key)
return self.data[key]['is_repeater'] return self.data[key]["is_repeater"]
def set_repeater(self, key): def set_repeater(self, key):
self._create(key) self._create(key)
self.data[key]['is_repeater'] = True self.data[key]["is_repeater"] = True
def _create(self, key): def _create(self, key):
if self.data.get(key) is None: if self.data.get(key) is None:
self.data[key] = {'is_repeater': False, 'data': []} self.data[key] = {"is_repeater": False, "data": []}
_fudulist = Fudu() _fudu_list = Fudu()
fudu = on_message(permission=GROUP, priority=9) fudu = on_message(permission=GROUP, priority=9)
@@ -60,7 +72,11 @@ fudu = on_message(permission=GROUP, priority=9)
@fudu.handle() @fudu.handle()
async def _(bot: Bot, event: GroupMessageEvent, state: T_State): async def _(bot: Bot, event: GroupMessageEvent, state: T_State):
if event.is_tome() or state["_prefix"]["raw_command"]: if (
event.is_tome()
or state["_prefix"]["raw_command"]
or not await group_manager.check_group_task_status(event.group_id, "fudu")
):
return return
if get_message_text(event.json()): if get_message_text(event.json()):
if get_message_text(event.json()).find("@可爱的小真寻") != -1: if get_message_text(event.json()).find("@可爱的小真寻") != -1:
@@ -74,17 +90,20 @@ async def _(bot: Bot, event: GroupMessageEvent, state: T_State):
else: else:
img_hash = "" img_hash = ""
add_msg = msg + "|-|" + img_hash add_msg = msg + "|-|" + img_hash
if _fudulist.size(event.group_id) == 0: if _fudu_list.size(event.group_id) == 0:
_fudulist.append(event.group_id, add_msg) _fudu_list.append(event.group_id, add_msg)
elif _fudulist.check(event.group_id, add_msg): elif _fudu_list.check(event.group_id, add_msg):
_fudulist.append(event.group_id, add_msg) _fudu_list.append(event.group_id, add_msg)
else: else:
_fudulist.clear(event.group_id) _fudu_list.clear(event.group_id)
_fudulist.append(event.group_id, add_msg) _fudu_list.append(event.group_id, add_msg)
if _fudulist.size(event.group_id) > 2: if _fudu_list.size(event.group_id) > 2:
if random.random() < FUDU_PROBABILITY and not _fudulist.is_repeater(event.group_id): if random.random() < FUDU_PROBABILITY and not _fudu_list.is_repeater(
event.group_id
):
if random.random() < 0.2: if random.random() < 0.2:
await fudu.finish("打断施法!") await fudu.finish("打断施法!")
_fudu_list.set_repeater(event.group_id)
if imgs and msg: if imgs and msg:
rst = msg + image(f"compare_{event.group_id}_img.jpg", "temp") rst = msg + image(f"compare_{event.group_id}_img.jpg", "temp")
elif imgs: elif imgs:
@@ -95,7 +114,6 @@ async def _(bot: Bot, event: GroupMessageEvent, state: T_State):
rst = "" rst = ""
if rst: if rst:
await fudu.send(rst) await fudu.send(rst)
_fudulist.set_repeater(event.group_id)
async def get_fudu_img_hash(url, group_id): async def get_fudu_img_hash(url, group_id):
@@ -109,4 +127,4 @@ async def get_fudu_img_hash(url, group_id):
img_hash = get_img_hash(IMAGE_PATH + f"temp/compare_{group_id}_img.jpg") img_hash = get_img_hash(IMAGE_PATH + f"temp/compare_{group_id}_img.jpg")
return str(img_hash) return str(img_hash)
except TimeoutError: except TimeoutError:
return '' return ""
+3
View File
@@ -0,0 +1,3 @@
import nonebot
nonebot.load_plugins("plugins/genshin")
+37 -24
View File
@@ -1,25 +1,44 @@
from .alc import get_almanac_base64_str, load_data
import os
from utils.utils import get_bot, scheduler from utils.utils import get_bot, scheduler
from nonebot import on_command from nonebot import on_command
from models.level_user import LevelUser
from nonebot.typing import T_State from nonebot.typing import T_State
from nonebot.adapters.cqhttp import Bot, GroupMessageEvent, MessageEvent from nonebot.adapters.cqhttp import Bot, GroupMessageEvent, MessageEvent
from utils.message_builder import image
from services.log import logger from services.log import logger
from models.group_remind import GroupRemind from configs.path_config import IMAGE_PATH
from .data_source import get_alc_image
from utils.manager import group_manager
from pathlib import Path
__zx_plugin_name__ = "原神老黄历"
FILE_PATH = os.path.dirname(__file__) __plugin_usage__ = """
usage:
有时候也该迷信一回!特别是运气方面
指令:
原神黄历
""".strip()
__plugin_des__ = "有时候也该迷信一回!特别是运气方面"
__plugin_cmd__ = ["原神黄历"]
__plugin_type__ = ("原神相关",)
__plugin_version__ = 0.1
__plugin_author__ = "HibiKier"
__plugin_settings__ = {
"level": 5,
"default_status": True,
"limit_superuser": False,
"cmd": ["原神黄历", "原神老黄历"],
}
__plugin_task__ = {"genshin_alc": "原神黄历提醒"}
almanac = on_command("原神黄历", priority=5, block=True) almanac = on_command("原神黄历", priority=5, block=True)
reload = on_command("重载原神黄历数据", priority=5, block=True)
ALC_PATH = Path(IMAGE_PATH) / "genshin" / "alc"
ALC_PATH.mkdir(parents=True, exist_ok=True)
@almanac.handle() @almanac.handle()
async def _(bot: Bot, event: MessageEvent, state: T_State): async def _(bot: Bot, event: MessageEvent, state: T_State):
almanac_base64 = get_almanac_base64_str() alc_img = await get_alc_image(ALC_PATH)
mes = image(b64=almanac_base64) + "\n ※ 黄历数据来源于 genshin.pub" mes = alc_img + "\n ※ 黄历数据来源于 genshin.pub"
await almanac.send(mes) await almanac.send(mes)
logger.info( logger.info(
f"(USER {event.user_id}, GROUP {event.group_id if isinstance(event, GroupMessageEvent) else 'private'})" f"(USER {event.user_id}, GROUP {event.group_id if isinstance(event, GroupMessageEvent) else 'private'})"
@@ -27,13 +46,6 @@ async def _(bot: Bot, event: MessageEvent, state: T_State):
) )
@reload.handle()
async def _(bot: Bot, event: GroupMessageEvent, state: T_State):
if await LevelUser.check_level(event.user_id, event.group_id, 5):
load_data()
await reload.send("重载成功")
@scheduler.scheduled_job( @scheduler.scheduled_job(
"cron", "cron",
hour=10, hour=10,
@@ -42,10 +54,11 @@ async def _(bot: Bot, event: GroupMessageEvent, state: T_State):
async def _(): async def _():
# 每日提醒 # 每日提醒
bot = get_bot() bot = get_bot()
gl = await bot.get_group_list(self_id=bot.self_id) if bot:
gl = [g["group_id"] for g in gl] gl = await bot.get_group_list()
almanac_base64 = get_almanac_base64_str() gl = [g["group_id"] for g in gl]
mes = image(b64=almanac_base64) + "\n ※ 黄历数据来源于 genshin.pub" alc_img = await get_alc_image(ALC_PATH)
for gid in gl: mes = alc_img + "\n ※ 黄历数据来源于 genshin.pub"
if await GroupRemind.get_status(gid, "almanac"): for gid in gl:
await bot.send_group_msg(group_id=int(gid), message=mes) if await group_manager.check_group_task_status(gid, "genshin_alc"):
await bot.send_group_msg(group_id=int(gid), message=mes)
-123
View File
@@ -1,123 +0,0 @@
from PIL import Image, ImageDraw, ImageFont
from io import BytesIO
import os
import json
import random
import base64
import time
FILE_PATH = os.path.dirname(__file__)
FONT_PATH = os.path.join(FILE_PATH, "汉仪文黑.ttf")
data = {} # configs.json里的数据
almanac_data = {
# 生成的黄历base64字符串和黄历更新日期
"date": "",
"almanac_base64_str": ""
}
chinese = {"0": "", "1": "一", "2": "二", "3": "三", "4": "四", "5": "五", "6": "六", "7": "七", "8": "八", "9": "九"}
def month_to_chinese(month: str):
# 把日期数字转成中文数字
m = int(month)
if m < 10:
return chinese[month[-1]]
elif m < 20:
return "十" + chinese[month[-1]]
else:
return chinese[month[0]] + "十" + chinese[month[-1]]
def load_data():
# 载入config.json文件的数据
global data
with open(os.path.join(FILE_PATH, 'config.json'), 'r', encoding='UTF-8') as f:
data = json.load(f)
almanac_data["date"] = ""
almanac_data["almanac_base64_str"] = ""
load_data()
def seed_random_list(seed: str, l: list):
# 使用随机种子随机选择列表中的元素,相同的种子和列表将返回同样的输出
seed = seed + str(l)
random.seed(seed)
index = random.random() * len(l)
return l[int(index)]
def generate_almanac():
# 生成黄历图片,然后转换成base64保存到 almanac_data["almanac_base64_str"]
seed = time.strftime("%Y-%m-%d")
offset = 1
today_luck = []
l = list(data.keys())
while len(today_luck) < 6:
# 随机6个不同的运势放到 today_luck
r = seed_random_list(str(offset) + seed, l)
if r in today_luck:
offset += 1
else:
today_luck.append(r)
back = Image.open(os.path.join(FILE_PATH, "back.png"))
year = time.strftime("%Y")
month = month_to_chinese(time.strftime("%m")) + "月"
day = month_to_chinese(time.strftime("%d")) + "日"
draw = ImageDraw.Draw(back)
draw.text((118, 165), year, fill="#8d7650ff", font=ImageFont.truetype(FONT_PATH, size=30), anchor="mm",
align="center")
draw.text((260, 165), day, fill="#f7f8f2ff", font=ImageFont.truetype(FONT_PATH, size=35), anchor="mm",
align="center")
draw.text((410, 165), month, fill="#8d7650ff", font=ImageFont.truetype(FONT_PATH, size=30), anchor="mm",
align="center")
buff = Image.new("RGBA", (325, 160))
debuff = Image.new("RGBA", (325, 160))
buff_draw = ImageDraw.Draw(buff)
debuff_draw = ImageDraw.Draw(debuff)
for i in range(3):
buff_name = today_luck[i]
debuff_name = today_luck[(i + 3)]
buff_effect = seed_random_list(seed, data[buff_name]["buff"])
debuff_effect = seed_random_list(seed, data[debuff_name]["debuff"])
buff_draw.text((0, i * 53), buff_name, fill="#756141ff", font=ImageFont.truetype(FONT_PATH, size=25))
debuff_draw.text((0, i * 53), debuff_name, fill="#756141ff", font=ImageFont.truetype(FONT_PATH, size=25))
buff_draw.text((0, i * 53 + 28), buff_effect, fill="#b5b3acff", font=ImageFont.truetype(FONT_PATH, size=19))
debuff_draw.text((0, i * 53 + 28), debuff_effect, fill="#b5b3acff", font=ImageFont.truetype(FONT_PATH, size=19))
back.paste(buff, (150, 230), buff)
back.paste(debuff, (150, 400), debuff)
bio = BytesIO()
back.save(bio, format='PNG')
base64_str = base64.b64encode(bio.getvalue()).decode()
almanac_data["date"] = time.strftime("%Y-%m-%d")
almanac_data["almanac_base64_str"] = 'base64://' + base64_str
def get_almanac_base64_str():
# if almanac_data["date"] == time.strftime("%Y-%m-%d"):
# return almanac_data["almanac_base64_str"]
# else:
# generate_almanac()
# return almanac_data["almanac_base64_str"]
generate_almanac()
return almanac_data["almanac_base64_str"]
Binary file not shown.

Before

Width:  |  Height:  |  Size: 26 KiB

-70
View File
@@ -1,70 +0,0 @@
{
"抽卡":{
"buff": ["欧气满满,十连出金","出金不歪"],
"debuff": ["武器大师","保底出金","金色会是痛苦大剑"]
},
"刷世界boss":{
"buff": ["双攻双爆角斗士"],
"debuff": ["只有保底材料","贪生怕死角斗士"]
},
"刷风本":{
"buff": ["会有极品猎人套","会掉真正的少女心","治疗加成少女头"],
"debuff": ["勇往直前少女心","少女飘摇的杀意","少女暴怒的容颜"]
},
"刷火本":{
"buff": ["魔女帽子火伤杯","暴伤魔女帽!","火伤魔女心!"],
"debuff": ["幡 然 醒 悟","这么阴间的地方真的会有魔女套吗?","不务正业火魔女","会匹配到3个卢姥爷"]
},
"刷岩本":{
"buff": ["悠久的磐岩伴你左右","岩神的庇护常在"],
"debuff": ["防御流星杯,你值得拥有"]
},
"刷宗室":{
"buff": ["物理伤害骑士道,元素精通宗室套"],
"debuff": ["贪生怕死骑士道,物理伤害宗室杯"]
},
"刷冰本":{
"buff": ["双暴词条概率up"],
"debuff": ["防御力船帽,无人可及"]
},
"刷雷本":{
"buff": ["愿雷鸟伴你左右"],
"debuff": ["来表演一个只掉平雷套的绝活","风神忽悠雷凶兆"]
},
"锄大地":{
"buff": ["会掉一大堆紫色材料"],
"debuff": ["深渊法师爱你哟","会被冰水法控到死"]
},
"挖矿":{
"buff": ["开矿出双材料"],
"debuff": ["去别人世界会被拒"]
},
"刷天赋本":{
"buff": ["金色!我看到了金色的书!"],
"debuff": ["2蓝2绿不会变"]
},
"刷突破材料":{
"buff": ["金色!我看到了金色的材料!"],
"debuff": ["2蓝2绿不会变"]
},
"升级圣遗物":{
"buff": ["稀有词条跳跳跳","会双爆拉满"],
"debuff": ["女 仆 狂 喜","无中生有防御力","生命拉满","完美避开双爆"]
},
"打风魔龙":{
"buff": ["看我一箭一个风魔鸡","今天特瓦林可以给想要的突破材料","5金加原胚!"],
"debuff": ["会不小心掉下平台","不小心被地板烫死了"]
},
"打狼王":{
"buff": ["今天安德琉斯的心情不错,可以py一下","5金加原胚!"],
"debuff": ["狼尾巴*1"]
},
"打公子":{
"buff": ["今天可以和公子py想要的突破材料","5金加原胚!"],
"debuff": ["要角没有!要命一条!"]
}
}
+43
View File
@@ -0,0 +1,43 @@
from utils.browser import get_browser
from utils.message_builder import image
from datetime import datetime
from services.log import logger
from pathlib import Path
import os
url = "https://genshin.pub"
async def get_alc_image(path: Path):
date = datetime.now().date()
for file in os.listdir(path):
if f'{date}.png' != file:
file = path / file
file.unlink()
if f'{date}.png' in os.listdir(path):
return image(f'{date}.png', 'genshin/alc')
page = None
try:
browser = await get_browser()
page = await browser.new_page()
await page.goto(url, wait_until="networkidle", timeout=10000)
await page.set_viewport_size({"width": 2560, "height": 1080})
card = await page.query_selector('.GSAlmanacs_gs_almanacs__3qT_A')
await card.screenshot(path=path / f'{date}.png', timeout=100000)
except Exception as e:
logger.error(f'获取原神黄历发生错误..{type(e)}: {e}')
finally:
if page:
await page.close()
return image(f'{date}.png', 'genshin/alc')
+78 -34
View File
@@ -12,14 +12,39 @@ from nonebot.permission import SUPERUSER
from typing import List from typing import List
import os import os
import asyncio import asyncio
import time import time
__zx_plugin_name__ = "原神今日素材"
__plugin_usage__ = """
usage:
看看原神今天要刷什么
指令:
今日素材/今天素材
""".strip()
__plugin_superuser_usage__ = """
usage:
更新原神今日素材
指令:
更新原神今日素材
""".strip()
__plugin_des__ = "看看原神今天要刷什么"
__plugin_cmd__ = ["今日素材/今天素材", "更新原神今日素材 [_superuser]"]
__plugin_type__ = ("原神相关",)
__plugin_version__ = 0.1
__plugin_author__ = "HibiKier"
__plugin_settings__ = {
"level": 5,
"default_status": True,
"limit_superuser": False,
"cmd": ["今日素材", "今天素材"],
}
driver: Driver = nonebot.get_driver() driver: Driver = nonebot.get_driver()
material = on_command('今日素材', aliases={'今日材料', '今天素材', '今天材料'}, priority=5, block=True) material = on_command("今日素材", aliases={"今日材料", "今天素材", "今天材料"}, priority=5, block=True)
super_cmd = on_command('更新原神今日素材', permission=SUPERUSER, priority=1, block=True) super_cmd = on_command("更新原神今日素材", permission=SUPERUSER, priority=1, block=True)
@material.handle() @material.handle()
@@ -27,49 +52,62 @@ async def _(bot: Bot, event: MessageEvent, state: T_State):
if time.strftime("%w") == "0": if time.strftime("%w") == "0":
await material.send("今天是周日,所有材料副本都开放了。") await material.send("今天是周日,所有材料副本都开放了。")
return return
await material.send(Message(image('daily_material.png', 'genshin/material') + '\n※ 每日素材数据来源于 genshin.pub')) await material.send(
Message(
image("daily_material.png", "genshin/material")
+ "\n※ 每日素材数据来源于 genshin.pub"
)
)
logger.info( logger.info(
f"(USER {event.user_id}, GROUP {event.group_id if event.message_type != 'private' else 'private'})" f"(USER {event.user_id}, GROUP {event.group_id if event.message_type != 'private' else 'private'})"
f" 发送查看今日素材") f" 发送查看今日素材"
)
@super_cmd.handle() @super_cmd.handle()
async def _(bot: Bot, event: MessageEvent, state: T_State): async def _(bot: Bot, event: MessageEvent, state: T_State):
if await update_image(): if await update_image():
await super_cmd.send('更新成功...') await super_cmd.send("更新成功...")
logger.info(f'更新每日天赋素材成功...') logger.info(f"更新每日天赋素材成功...")
else: else:
await super_cmd.send(f'更新失败...') await super_cmd.send(f"更新失败...")
@driver.on_startup @driver.on_startup
async def update_image(): async def update_image():
page = None page = None
try: try:
if not os.path.exists(f'{IMAGE_PATH}/genshin/material'): if not os.path.exists(f"{IMAGE_PATH}/genshin/material"):
os.mkdir(f'{IMAGE_PATH}/genshin/material') os.mkdir(f"{IMAGE_PATH}/genshin/material")
for file in os.listdir(f'{IMAGE_PATH}/genshin/material'): for file in os.listdir(f"{IMAGE_PATH}/genshin/material"):
os.remove(f'{IMAGE_PATH}/genshin/material/{file}') os.remove(f"{IMAGE_PATH}/genshin/material/{file}")
browser = await get_browser() browser = await get_browser()
if not browser: if not browser:
logger.warning('获取 browser 失败,请部署至 linux 环境....') logger.warning("获取 browser 失败,请部署至 linux 环境....")
return False return False
url = 'https://genshin.pub/daily' url = "https://genshin.pub/daily"
page = await browser.new_page() page = await browser.new_page()
await page.goto(url, wait_until='networkidle', timeout=10000) await page.goto(url, wait_until="networkidle", timeout=10000)
await page.set_viewport_size({"width": 2560, "height": 1080}) await page.set_viewport_size({"width": 2560, "height": 1080})
await page.evaluate(""" await page.evaluate(
"""
document.getElementsByClassName('GSTitleBar_gs_titlebar__2IJqy')[0].remove(); document.getElementsByClassName('GSTitleBar_gs_titlebar__2IJqy')[0].remove();
e = document.getElementsByClassName('GSContainer_gs_container__2FbUz')[0]; e = document.getElementsByClassName('GSContainer_gs_container__2FbUz')[0];
e.setAttribute("style", "height:880px"); e.setAttribute("style", "height:880px");
""") """
)
await page.click("button") await page.click("button")
div = await page.query_selector(".GSContainer_content_box__1sIXz") div = await page.query_selector(".GSContainer_content_box__1sIXz")
for i, card in enumerate(await page.query_selector_all(".GSTraitCotainer_trait_section__1f3bc")): for i, card in enumerate(
await page.query_selector_all(".GSTraitCotainer_trait_section__1f3bc")
):
index = 0 index = 0
type_ = 'char' if not i else 'weapons' type_ = "char" if not i else "weapons"
for x in await card.query_selector_all("xpath=child::*"): for x in await card.query_selector_all("xpath=child::*"):
await x.screenshot(path=f'{IMAGE_PATH}/genshin/material/{type_}_{index}.png', timeout=100000) await x.screenshot(
path=f"{IMAGE_PATH}/genshin/material/{type_}_{index}.png",
timeout=100000,
)
# 下滑两次 # 下滑两次
for _ in range(3): for _ in range(3):
await div.press("PageDown") await div.press("PageDown")
@@ -77,13 +115,23 @@ async def update_image():
# 结束后上滑至顶 # 结束后上滑至顶
for _ in range(index * 3): for _ in range(index * 3):
await div.press("PageUp") await div.press("PageUp")
file_list = os.listdir(f'{IMAGE_PATH}/genshin/material') file_list = os.listdir(f"{IMAGE_PATH}/genshin/material")
char_imgs = [f'{IMAGE_PATH}/genshin/material/{x}' for x in file_list if x.startswith('char')] char_imgs = [
weapons_imgs = [f'{IMAGE_PATH}/genshin/material/{x}' for x in file_list if x.startswith('weapons')] f"{IMAGE_PATH}/genshin/material/{x}"
for x in file_list
if x.startswith("char")
]
weapons_imgs = [
f"{IMAGE_PATH}/genshin/material/{x}"
for x in file_list
if x.startswith("weapons")
]
char_imgs.sort() char_imgs.sort()
weapons_imgs.sort() weapons_imgs.sort()
height = await asyncio.get_event_loop().run_in_executor(None, get_background_height, weapons_imgs) height = await asyncio.get_event_loop().run_in_executor(
background_img = CreateImg(1200, height + 100, color='#f6f2ee') None, get_background_height, weapons_imgs
)
background_img = CreateImg(1200, height + 100, color="#f6f2ee")
current_width = 50 current_width = 50
for imgs in [char_imgs, weapons_imgs]: for imgs in [char_imgs, weapons_imgs]:
current_height = 20 current_height = 20
@@ -92,11 +140,11 @@ async def update_image():
background_img.paste(x, (current_width, current_height)) background_img.paste(x, (current_width, current_height))
current_height += x.size[1] current_height += x.size[1]
current_width += 600 current_width += 600
background_img.save(f'{IMAGE_PATH}/genshin/material/daily_material.png') background_img.save(f"{IMAGE_PATH}/genshin/material/daily_material.png")
await page.close() await page.close()
return True return True
except Exception as e: except Exception as e:
logger.error(f'原神每日素材更新出错... {type(e)}: {e}') logger.error(f"原神每日素材更新出错... {type(e)}: {e}")
if page: if page:
await page.close() await page.close()
return False return False
@@ -116,7 +164,7 @@ def get_background_height(weapons_imgs: List[str]) -> int:
@scheduler.scheduled_job( @scheduler.scheduled_job(
'cron', "cron",
hour=4, hour=4,
minute=1, minute=1,
) )
@@ -124,11 +172,7 @@ async def _():
for _ in range(5): for _ in range(5):
try: try:
await update_image() await update_image()
logger.info(f'更新每日天赋素材成功...') logger.info(f"更新每日天赋素材成功...")
break break
except Exception as e: except Exception as e:
logger.error(f'更新每日天赋素材出错 e:{e}') logger.error(f"更新每日天赋素材出错 e:{e}")
@@ -5,9 +5,24 @@ from nonebot import on_command
from utils.utils import get_message_text from utils.utils import get_message_text
from services.log import logger from services.log import logger
__plugin_name__ = "丘丘语翻译" __zx_plugin_name__ = "丘丘语翻译"
__plugin_usage__ = """
__plugin_usage__ = "用法:丘丘翻译 [消息]" usage:
异世界旅游小助手,仅支持丘丘语翻译至中文
指令:
丘丘语翻译/丘丘一下 [文本]
""".strip()
__plugin_des__ = "其实我听得懂丘丘人讲话"
__plugin_cmd__ = ["丘丘语翻译/丘丘一下 [文本]"]
__plugin_type__ = ("原神相关",)
__plugin_version__ = 0.1
__plugin_author__ = "HibiKier"
__plugin_settings__ = {
"level": 5,
"default_status": True,
"limit_superuser": False,
"cmd": ["丘丘语翻译", "丘丘一下"],
}
qiuqiu = on_command("丘丘语翻译", aliases={"丘丘一下", "丘丘翻译"}, priority=5, block=True) qiuqiu = on_command("丘丘语翻译", aliases={"丘丘一下", "丘丘翻译"}, priority=5, block=True)
@@ -1,10 +1,10 @@
from nonebot import on_command, on_regex from nonebot import on_command, on_regex
from nonebot.rule import to_me from .query_resource import get_resource_type_list, query_resource, init, check_resource_exists
from .query_resource import get_resource_type_list, query_resource, init
from utils.utils import get_message_text, scheduler from utils.utils import get_message_text, scheduler
from nonebot.adapters.cqhttp import Bot, MessageEvent, GroupMessageEvent, Message from nonebot.adapters.cqhttp import Bot, MessageEvent, GroupMessageEvent, Message
from nonebot.typing import T_State from nonebot.typing import T_State
from services.log import logger from services.log import logger
from configs.config import NICKNAME
from nonebot.permission import SUPERUSER from nonebot.permission import SUPERUSER
import re import re
@@ -13,28 +13,53 @@ try:
except ModuleNotFoundError: except ModuleNotFoundError:
import json import json
__plugin_name__ = "原神资源查询" __zx_plugin_name__ = "原神资源查询"
__plugin_usage__ = """
usage:
不需要打开网页,就能帮你生成资源图片
指令:
原神资源查询 [资源名称]
原神资源列表
[资源名称]在哪
哪有[资源名称]
""".strip()
__plugin_superuser_usage__ = """
usage:
更新原神资源信息
指令:
更新原神资源信息
""".strip()
__plugin_des__ = "原神大地图资源速速查看"
__plugin_cmd__ = ["原神资源查询 [资源名称]", "原神资源列表", "[资源名称]在哪/哪有[资源名称]", "更新原神资源信息 [_superuser]"]
__plugin_type__ = ("原神相关",)
__plugin_version__ = 0.1
__plugin_author__ = "HibiKier"
__plugin_settings__ = {
"level": 5,
"default_status": True,
"limit_superuser": False,
"cmd": ["原神资源查询", "原神资源列表"],
}
__plugin_usage__ = ( qr = on_command("原神资源查询", aliases={"原神资源查找"}, priority=5, block=True)
"用法:\n" "\t原神资源查询 [消息]\n" "\t原神资源列表\n" "\t[消息]在哪\n" "\t哪有[消息]\n" "[消息] = 资源名称"
)
qr = on_command("原神资源查询", priority=5, block=True)
qr_lst = on_command("原神资源列表", priority=5, block=True) qr_lst = on_command("原神资源列表", priority=5, block=True)
rex_qr = on_regex(".*?(在哪|在哪里|哪有|哪里有).*?", rule=to_me(), 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_command("更新原神资源信息", permission=SUPERUSER, priority=1, block=True)
@qr.handle() @qr.handle()
async def _(bot: Bot, event: MessageEvent, state: T_State): async def _(bot: Bot, event: MessageEvent, state: T_State):
resource_name = get_message_text(event.json()) resource_name = get_message_text(event.json())
await qr.send('正在生成位置....') if check_resource_exists(resource_name):
rst = await query_resource(resource_name) await qr.send("正在生成位置....")
await qr.send(Message(rst), at_sender=True) resource = await query_resource(resource_name)
logger.info( await qr.send(Message(resource), at_sender=True)
f"(USER {event.user_id}, GROUP {event.group_id if isinstance(event, GroupMessageEvent) else 'private'})" logger.info(
f" 查询原神材料:" + resource_name f"(USER {event.user_id}, GROUP {event.group_id if isinstance(event, GroupMessageEvent) else 'private'})"
) f" 查询原神材料:" + resource_name
)
else:
await qr.send(f"未查找到 {resource_name} 资源,可通过 “原神资源列表” 获取全部资源名称..")
@rex_qr.handle() @rex_qr.handle()
@@ -46,33 +71,33 @@ async def _(bot: Bot, event: MessageEvent, state: T_State):
else: else:
rs = re.search(".*?(哪有|哪里有)(.*)", msg) rs = re.search(".*?(哪有|哪里有)(.*)", msg)
resource_name = rs.group(2) if rs else "" resource_name = rs.group(2) if rs else ""
if resource_name: if check_resource_exists(resource_name):
await qr.send('正在生成位置....') await qr.send("正在生成位置....")
msg = await query_resource(resource_name) resource = await query_resource(resource_name)
await rex_qr.send(Message(msg), at_sender=True) if resource:
logger.info( await rex_qr.send(Message(resource), at_sender=True)
f"(USER {event.user_id}, GROUP {event.group_id if isinstance(event, GroupMessageEvent) else 'private'})" logger.info(
f" 查询原神材料:" + resource_name f"(USER {event.user_id}, GROUP {event.group_id if isinstance(event, GroupMessageEvent) else 'private'})"
) f" 查询原神材料:" + resource_name
)
@qr_lst.handle() @qr_lst.handle()
async def _(bot: Bot, event: MessageEvent, state: T_State): async def _(bot: Bot, event: MessageEvent, state: T_State):
mes_list = []
txt = get_resource_type_list() txt = get_resource_type_list()
txt_list = txt.split("\n") txt_list = txt.split("\n")
if event.message_type == "group": if isinstance(event, GroupMessageEvent):
mes_list = []
for txt in txt_list: for txt in txt_list:
data = { data = {
"type": "node", "type": "node",
"data": { "data": {
"name": f"这里是{list(bot.config.nickname)[0]}酱", "name": f"这里是{NICKNAME}酱",
"uin": f"{bot.self_id}", "uin": f"{bot.self_id}",
"content": txt, "content": txt,
}, },
} }
mes_list.append(data) mes_list.append(data)
if isinstance(event, GroupMessageEvent):
await bot.send_group_forward_msg(group_id=event.group_id, messages=mes_list) await bot.send_group_forward_msg(group_id=event.group_id, messages=mes_list)
else: else:
rst = "" rst = ""
@@ -87,17 +112,17 @@ async def _(bot: Bot, event: MessageEvent, state: T_State):
@update_info.handle() @update_info.handle()
async def _(bot: Bot, event: MessageEvent, state: T_State): async def _(bot: Bot, event: MessageEvent, state: T_State):
await init(True) await init(True)
await update_info.send('更新原神资源信息完成...') await update_info.send("更新原神资源信息完成...")
@scheduler.scheduled_job( @scheduler.scheduled_job(
'cron', "cron",
hour=5, hour=5,
minute=1, minute=1,
) )
async def _(): async def _():
try: try:
await init() await init()
logger.info(f'每日更新原神材料信息成功!') logger.info(f"每日更新原神材料信息成功!")
except Exception as e: except Exception as e:
logger.error(f'每日更新原神材料信息错误:{e}') logger.error(f"每日更新原神材料信息错误:{e}")
+3 -3
View File
@@ -1,5 +1,5 @@
from pathlib import Path from pathlib import Path
from configs.path_config import IMAGE_PATH, TXT_PATH from configs.path_config import IMAGE_PATH, TEXT_PATH
from utils.image_utils import CreateImg from utils.image_utils import CreateImg
from typing import Tuple, List from typing import Tuple, List
from math import sqrt, pow from math import sqrt, pow
@@ -12,8 +12,8 @@ except ModuleNotFoundError:
icon_path = Path(IMAGE_PATH) / "genshin" / "genshin_icon" icon_path = Path(IMAGE_PATH) / "genshin" / "genshin_icon"
map_path = Path(IMAGE_PATH) / "genshin" / "map" / "map.png" map_path = Path(IMAGE_PATH) / "genshin" / "map" / "map.png"
resource_label_file = Path(TXT_PATH) / "genshin" / "resource_label_file.json" resource_label_file = Path(TEXT_PATH) / "genshin" / "resource_label_file.json"
resource_point_file = Path(TXT_PATH) / "genshin" / "resource_point_file.json" resource_point_file = Path(TEXT_PATH) / "genshin" / "resource_point_file.json"
class Map: class Map:
@@ -1,5 +1,5 @@
from typing import Tuple, Optional, List from typing import Tuple, Optional, List
from configs.path_config import IMAGE_PATH, TXT_PATH from configs.path_config import IMAGE_PATH, TEXT_PATH
from PIL.Image import UnidentifiedImageError from PIL.Image import UnidentifiedImageError
from utils.message_builder import image from utils.message_builder import image
from services.log import logger from services.log import logger
@@ -30,9 +30,9 @@ MAP_URL = "https://api-static.mihoyo.com/common/map_user/ys_obc/v1/map/info?map_
icon_path = Path(IMAGE_PATH) / "genshin" / "genshin_icon" icon_path = Path(IMAGE_PATH) / "genshin" / "genshin_icon"
map_path = Path(IMAGE_PATH) / "genshin" / "map" map_path = Path(IMAGE_PATH) / "genshin" / "map"
resource_label_file = Path(TXT_PATH) / "genshin" / "resource_label_file.json" resource_label_file = Path(TEXT_PATH) / "genshin" / "resource_label_file.json"
resource_point_file = Path(TXT_PATH) / "genshin" / "resource_point_file.json" resource_point_file = Path(TEXT_PATH) / "genshin" / "resource_point_file.json"
resource_type_file = Path(TXT_PATH) / "genshin" / "resource_type_file.json" resource_type_file = Path(TEXT_PATH) / "genshin" / "resource_type_file.json"
# 地图中心坐标 # 地图中心坐标
CENTER_POINT: Optional[Tuple[int, int]] = None CENTER_POINT: Optional[Tuple[int, int]] = None
@@ -50,7 +50,8 @@ async def query_resource(resource_name: str) -> str:
resource_name = resource_name[:-2].strip() resource_name = resource_name[:-2].strip()
planning_route = True planning_route = True
if not resource_name or resource_name not in resource_name_list: if not resource_name or resource_name not in resource_name_list:
return f"未查找到 {resource_name} 资源,可通过 “原神资源列表” 获取全部资源名称.." # return f"未查找到 {resource_name} 资源,可通过 “原神资源列表” 获取全部资源名称.."
return ''
map_ = Map( map_ = Map(
resource_name, CENTER_POINT, planning_route=planning_route, ratio=MAP_RATIO resource_name, CENTER_POINT, planning_route=planning_route, ratio=MAP_RATIO
) )
@@ -81,23 +82,36 @@ def get_resource_type_list():
return mes return mes
def check_resource_exists(resource: str) -> bool:
"""
检查资源是否存在
:param resource: 资源名称
"""
resource = resource.replace('路径', '').replace('路线', '')
return resource in resource_name_list
@driver.on_startup @driver.on_startup
async def init(flag: bool = False): async def init(flag: bool = False):
global CENTER_POINT, resource_name_list global CENTER_POINT, resource_name_list
semaphore = asyncio.Semaphore(10) try:
async with aiohttp.ClientSession(headers=get_user_agent()) as session: semaphore = asyncio.Semaphore(10)
await download_map_init(session, semaphore, MAP_RATIO, flag) async with aiohttp.ClientSession(headers=get_user_agent()) as session:
await download_resource_data(session, semaphore) await download_map_init(session, semaphore, MAP_RATIO, flag)
await download_resource_type(session) await download_resource_data(session, semaphore)
if not CENTER_POINT: await download_resource_type(session)
CENTER_POINT = json.load(open(resource_label_file, "r", encoding="utf8"))[ if not CENTER_POINT:
"CENTER_POINT" CENTER_POINT = json.load(open(resource_label_file, "r", encoding="utf8"))[
] "CENTER_POINT"
with open(resource_type_file, "r", encoding="utf8") as f: ]
data = json.load(f) with open(resource_type_file, "r", encoding="utf8") as f:
for id_ in data: data = json.load(f)
for x in data[id_]["children"]: for id_ in data:
resource_name_list.append(x["name"]) for x in data[id_]["children"]:
resource_name_list.append(x["name"])
except TimeoutError:
logger.warning('原神资源查询信息初始化超时....')
pass
# 图标及位置资源 # 图标及位置资源
@@ -164,40 +178,18 @@ async def download_map_init(
if not _map.exists(): if not _map.exists():
# padding_w, padding_h = data['padding'] # padding_w, padding_h = data['padding']
data = data["slices"] data = data["slices"]
idx = 0 map_url = data[0][0]['url']
for x in data: await download_image(
idj = 0 map_url,
for j in x: f"{map_path}/map.png",
await download_image( session,
j["url"], semaphore,
f"{map_path}/{idx}_{idj}.png", force_flag=flag,
session,
semaphore,
force_flag=flag,
)
idj += 1
idx += 1
map_width, map_height = CreateImg(
0, 0, background=f"{map_path}/0_0.png"
).size
map_width = map_width * MAP_RATIO
map_height = map_height * MAP_RATIO
lens = len(
[x for x in os.listdir(f"{map_path}") if x.startswith("0")]
) )
background_image = CreateImg( map_file = CreateImg(
map_width * lens, map_height * lens, map_width, map_height 0, 0, background=f"{map_path}/map.png", ratio=MAP_RATIO
) )
for i in range(idx): map_file.save(f"{map_path}/map.png")
for j in range(idj):
x = CreateImg(
0,
0,
background=f"{map_path}/{i}_{j}.png",
ratio=MAP_RATIO,
)
background_image.paste(x)
background_image.save(f"{map_path}/map.png")
else: else:
logger.warning(f'获取原神地图失败 msg: {data["message"]}') logger.warning(f'获取原神地图失败 msg: {data["message"]}')
else: else:
@@ -1,197 +1,195 @@
from utils.message_builder import image from utils.message_builder import image
from utils.utils import scheduler, get_bot from utils.utils import scheduler, get_bot
from services.log import logger from nonebot import on_message
from models.group_remind import GroupRemind from services.log import logger
from models.group_info import GroupInfo from models.group_info import GroupInfo
from models.friend_user import FriendUser from models.friend_user import FriendUser
from nonebot.adapters.cqhttp.exception import ActionFailed from nonebot.adapters.cqhttp.exception import ActionFailed
from configs.config import NICKNAME from configs.config import NICKNAME
from utils.manager import group_manager
__name__ = "早晚安 [Hidden]"
__zx_plugin_name__ = "群定时任务相关 [Hidden]"
__plugin_version__ = 0.1
# 早上好 __plugin_author__ = "HibiKier"
@scheduler.scheduled_job( __plugin_task__ = {'zwa': '早晚安'}
"cron",
# year=None,
# month=None, x = on_message(priority=9, block=False)
# day=None,
# week=None,
# day_of_week="mon,tue,wed,thu,fri", # 早上好
hour=6, @scheduler.scheduled_job(
minute=1, "cron",
# second=None, hour=6,
# start_date=None, minute=1,
# end_date=None, )
# timezone=None, async def _():
) try:
async def _(): bot = get_bot()
try: gl = await bot.get_group_list()
bot = get_bot() gl = [g["group_id"] for g in gl]
gl = await bot.get_group_list(self_id=bot.self_id) for g in gl:
gl = [g["group_id"] for g in gl] if await group_manager.check_group_task_status(g, 'zwa'):
for g in gl: result = image("zao.jpg", "zhenxun")
if await GroupRemind.get_status(g, "zwa"): try:
result = image("zao.jpg", "zhenxun") await bot.send_group_msg(group_id=g, message="早上好" + result)
try: except ActionFailed:
await bot.send_group_msg(group_id=g, message="早上好" + result) logger.warning(f"{g} 群被禁言中,无法发送早安")
except ActionFailed: except Exception as e:
logger.warning(f"{g} 群被禁言中,无法发送早安") logger.error(f"早晚安错误 e:{e}")
except Exception as e:
logger.error(f"早晚安错误 e:{e}")
# 睡觉了
@scheduler.scheduled_job(
# 睡觉了 "cron",
@scheduler.scheduled_job( hour=23,
"cron", minute=59,
hour=23, )
minute=59, async def _():
) try:
async def _(): bot = get_bot()
try: gl = await bot.get_group_list()
bot = get_bot() gl = [g["group_id"] for g in gl]
gl = await bot.get_group_list(self_id=bot.self_id) for g in gl:
gl = [g["group_id"] for g in gl] if await group_manager.check_group_task_status(g, 'zwa'):
for g in gl: result = image("sleep.jpg", "zhenxun")
if await GroupRemind.get_status(g, "zwa"): try:
result = image("sleep.jpg", "zhenxun") await bot.send_group_msg(
try: group_id=g, message=f"{NICKNAME}要睡觉了,你们也要早点睡呀" + result
await bot.send_group_msg( )
group_id=g, message=f"{NICKNAME}要睡觉了,你们也要早点睡呀" + result except ActionFailed:
) logger.warning(f"{g} 群被禁言中,无法发送晚安")
except ActionFailed: except Exception as e:
logger.warning(f"{g} 群被禁言中,无法发送晚安") logger.error(f"早晚安错误 e:{e}")
except Exception as e:
logger.error(f"早晚安错误 e:{e}")
# 自动更新群组信息
@scheduler.scheduled_job(
# 自动更新群组信息 "cron",
@scheduler.scheduled_job( hour=3,
"cron", minute=1,
hour=3, )
minute=1, async def _():
) try:
async def _(): bot = get_bot()
try: gl = await bot.get_group_list()
bot = get_bot() gl = [g["group_id"] for g in gl]
gl = await bot.get_group_list(self_id=bot.self_id) for g in gl:
gl = [g["group_id"] for g in gl] group_info = await bot.get_group_info(group_id=g)
for g in gl: await GroupInfo.add_group_info(
group_info = await bot.get_group_info(group_id=g) group_info["group_id"],
await GroupInfo.add_group_info( group_info["group_name"],
group_info["group_id"], group_info["max_member_count"],
group_info["group_name"], group_info["member_count"],
group_info["max_member_count"], )
group_info["member_count"], logger.info(f"自动更新群组 {g} 信息成功")
) except Exception as e:
logger.info(f"自动更新群组 {g} 信息成功") logger.error(f"自动更新群组信息错误 e:{e}")
except Exception as e:
logger.error(f"自动更新群组信息错误 e:{e}")
# 自动更新好友信息
@scheduler.scheduled_job(
# 自动更新好友信息 "cron",
@scheduler.scheduled_job( hour=3,
"cron", minute=1,
hour=3, )
minute=1, async def _():
) try:
async def _(): bot = get_bot()
try: fl = await bot.get_friend_list()
bot = get_bot() for f in fl:
fl = await bot.get_friend_list(self_id=bot.self_id) if await FriendUser.add_friend_info(f["user_id"], f["nickname"]):
for f in fl: logger.info(f'自动更新好友 {f["user_id"]} 信息成功')
if await FriendUser.add_friend_info(f["user_id"], f["nickname"]): else:
logger.info(f'自动更新好友 {f["user_id"]} 信息成功') logger.warning(f'自动更新好友 {f["user_id"]} 信息失败')
else: except Exception as e:
logger.warning(f'自动更新好友 {f["user_id"]} 信息失败') logger.error(f"自动更新群组信息错误 e:{e}")
except Exception as e:
logger.error(f"自动更新群组信息错误 e:{e}")
# 一次性任务
# 固定时间触发,仅触发一次:
# 一次性任务 #
# 固定时间触发,仅触发一次: # from datetime import datetime
# #
# from datetime import datetime # @nonebot.scheduler.scheduled_job(
# # 'date',
# @nonebot.scheduler.scheduled_job( # run_date=datetime(2021, 1, 1, 0, 0),
# 'date', # # timezone=None,
# run_date=datetime(2021, 1, 1, 0, 0), # )
# # timezone=None, # async def _():
# ) # await bot.send_group_msg(group_id=123456,
# async def _(): # message="2021,新年快乐!")
# await bot.send_group_msg(group_id=123456,
# message="2021,新年快乐!") # 定期任务
# 从 start_date 开始到 end_date 结束,根据类似 Cron
# 定期任务 #
# 从 start_date 开始到 end_date 结束,根据类似 Cron # 的规则触发任务:
# #
# 的规则触发任务: # @nonebot.scheduler.scheduled_job(
# # 'cron',
# @nonebot.scheduler.scheduled_job( # # year=None,
# 'cron', # # month=None,
# # year=None, # # day=None,
# # month=None, # # week=None,
# # day=None, # day_of_week="mon,tue,wed,thu,fri",
# # week=None, # hour=7,
# day_of_week="mon,tue,wed,thu,fri", # # minute=None,
# hour=7, # # second=None,
# # minute=None, # # start_date=None,
# # second=None, # # end_date=None,
# # start_date=None, # # timezone=None,
# # end_date=None, # )
# # timezone=None, # async def _():
# ) # await bot.send_group_msg(group_id=123456,
# async def _(): # message="起床啦!")
# await bot.send_group_msg(group_id=123456,
# message="起床啦!") # 间隔任务
#
# 间隔任务 # interval 触发器
# #
# interval 触发器 # 从 start_date 开始,每间隔一段时间触发,到 end_date 结束:
# #
# 从 start_date 开始,每间隔一段时间触发,到 end_date 结束: # @nonebot.scheduler.scheduled_job(
# # 'interval',
# @nonebot.scheduler.scheduled_job( # # weeks=0,
# 'interval', # # days=0,
# # weeks=0, # # hours=0,
# # days=0, # minutes=5,
# # hours=0, # # seconds=0,
# minutes=5, # # start_date=time.now(),
# # seconds=0, # # end_date=None,
# # start_date=time.now(), # )
# # end_date=None, # async def _():
# ) # has_new_item = check_new_item()
# async def _(): # if has_new_item:
# has_new_item = check_new_item() # await bot.send_group_msg(group_id=123456,
# if has_new_item: # message="XX有更新啦!")
# await bot.send_group_msg(group_id=123456,
# message="XX有更新啦!")
# 动态的计划任务
# import datetime
# 动态的计划任务 #
# import datetime # from apscheduler.triggers.date import DateTrigger # 一次性触发器
# # # from apscheduler.triggers.cron import CronTrigger # 定期触发器
# from apscheduler.triggers.date import DateTrigger # 一次性触发器 # # from apscheduler.triggers.interval import IntervalTrigger # 间隔触发器
# # from apscheduler.triggers.cron import CronTrigger # 定期触发器 # from nonebot import on_command, scheduler
# # from apscheduler.triggers.interval import IntervalTrigger # 间隔触发器 #
# from nonebot import on_command, scheduler # @on_command('赖床')
# # async def _(session: CommandSession):
# @on_command('赖床') # await session.send('我会在5分钟后再喊你')
# async def _(session: CommandSession): #
# await session.send('我会在5分钟后再喊你') # # 制作一个“5分钟后”触发器
# # delta = datetime.timedelta(minutes=5)
# # 制作一个“5分钟后”触发器 # trigger = DateTrigger(
# delta = datetime.timedelta(minutes=5) # run_date=datetime.datetime.now() + delta
# trigger = DateTrigger( # )
# run_date=datetime.datetime.now() + delta #
# ) # # 添加任务
# # scheduler.add_job(
# # 添加任务 # func=session.send, # 要添加任务的函数,不要带参数
# scheduler.add_job( # trigger=trigger, # 触发器
# func=session.send, # 要添加任务的函数,不要带参数 # args=('不要再赖床啦!',), # 函数的参数列表,注意:只有一个值时,不能省略末尾的逗号
# trigger=trigger, # 触发器 # # kwargs=None,
# args=('不要再赖床啦!',), # 函数的参数列表,注意:只有一个值时,不能省略末尾的逗号 # misfire_grace_time=60, # 允许的误差时间,建议不要省略
# # kwargs=None, # # jobstore='default', # 任务储存库,在下一小节中说明
# misfire_grace_time=60, # 允许的误差时间,建议不要省略 # )
# # jobstore='default', # 任务储存库,在下一小节中说明
# )
+73 -59
View File
@@ -1,20 +1,20 @@
from nonebot import on_notice, on_request from nonebot import on_notice, on_request
from configs.path_config import IMAGE_PATH, DATA_PATH from configs.path_config import IMAGE_PATH, DATA_PATH
from utils.message_builder import image from utils.message_builder import image
import os
import random
from models.group_member_info import GroupInfoUser from models.group_member_info import GroupInfoUser
from datetime import datetime from datetime import datetime
from services.log import logger from services.log import logger
from models.group_remind import GroupRemind
from nonebot.adapters.cqhttp import ( from nonebot.adapters.cqhttp import (
Bot, Bot,
GroupIncreaseNoticeEvent, GroupIncreaseNoticeEvent,
GroupDecreaseNoticeEvent, GroupDecreaseNoticeEvent,
) )
from nonebot.adapters.cqhttp.exception import ActionFailed from nonebot.adapters.cqhttp.exception import ActionFailed
from pathlib import Path from utils.manager import group_manager, plugins2settings_manager
from models.group_info import GroupInfo from models.group_info import GroupInfo
from pathlib import Path
import random
import os
try: try:
import ujson as json import ujson as json
@@ -22,9 +22,13 @@ except ModuleNotFoundError:
import json import json
__plugin_name__ = "群事件处理 [Hidden]" __zx_plugin_name__ = "群事件处理 [Hidden]"
__plugin_version__ = 0.1
__usage__ = "用法:无" __plugin_author__ = "HibiKier"
__plugin_task__ = {
'group_welcome': '进群欢迎',
'refund_group_remind': '退群提醒'
}
# 群员增加处理 # 群员增加处理
@@ -37,46 +41,55 @@ add_group = on_request(priority=1, block=False)
@group_increase_handle.handle() @group_increase_handle.handle()
async def _(bot: Bot, event: GroupIncreaseNoticeEvent, state: dict): async def _(bot: Bot, event: GroupIncreaseNoticeEvent, state: dict):
join_time = datetime.now() if event.user_id == int(bot.self_id):
user_info = await bot.get_group_member_info( if event.group_id not in group_manager["group_manager"].keys():
group_id=event.group_id, user_id=event.user_id data = plugins2settings_manager.get_data()
) for plugin in data.keys():
if await GroupInfoUser.add_member_info( if not data[plugin]["default_status"]:
user_info["user_id"], group_manager.block_plugin(plugin, event.group_id)
user_info["group_id"],
user_info["nickname"],
join_time,
):
logger.info(f"用户{user_info['user_id']} 所属{user_info['group_id']} 更新成功")
else: else:
logger.info(f"用户{user_info['user_id']} 所属{user_info['group_id']} 更新失败") join_time = datetime.now()
user_info = await bot.get_group_member_info(
# 群欢迎消息 group_id=event.group_id, user_id=event.user_id
if await GroupRemind.get_status(event.group_id, "hy"):
msg = ""
img = ""
at_flag = False
custom_welcome_msg_json = (
Path() / "data" / "custom_welcome_msg" / "custom_welcome_msg.json"
) )
if custom_welcome_msg_json.exists(): if await GroupInfoUser.add_member_info(
data = json.load(open(custom_welcome_msg_json, "r")) user_info["user_id"],
if data.get(str(event.group_id)): user_info["group_id"],
msg = data[str(event.group_id)] user_info["nickname"],
if msg.find("[at]") != -1: join_time,
msg = msg.replace("[at]", "") ):
at_flag = True logger.info(f"用户{user_info['user_id']} 所属{user_info['group_id']} 更新成功")
if os.path.exists(DATA_PATH + f"custom_welcome_msg/{event.group_id}.jpg"):
img = image(abspath=DATA_PATH + f"custom_welcome_msg/{event.group_id}.jpg")
if msg or img:
await group_increase_handle.send(
"\n" + msg.strip() + img, at_sender=at_flag
)
else: else:
await group_increase_handle.send( logger.info(f"用户{user_info['user_id']} 所属{user_info['group_id']} 更新失败")
"新人快跑啊!!本群现状↓(快使用自定义!)"
+ image(random.choice(os.listdir(IMAGE_PATH + "qxz/")), "qxz") # 群欢迎消息
if await group_manager.check_group_task_status(event.group_id, 'group_welcome'):
msg = ""
img = ""
at_flag = False
custom_welcome_msg_json = (
Path() / "data" / "custom_welcome_msg" / "custom_welcome_msg.json"
) )
if custom_welcome_msg_json.exists():
data = json.load(open(custom_welcome_msg_json, "r"))
if data.get(str(event.group_id)):
msg = data[str(event.group_id)]
if msg.find("[at]") != -1:
msg = msg.replace("[at]", "")
at_flag = True
if os.path.exists(DATA_PATH + f"custom_welcome_msg/{event.group_id}.jpg"):
img = image(
abspath=DATA_PATH + f"custom_welcome_msg/{event.group_id}.jpg"
)
if msg or img:
await group_increase_handle.send(
"\n" + msg.strip() + img, at_sender=at_flag
)
else:
await group_increase_handle.send(
"新人快跑啊!!本群现状↓(快使用自定义!)"
+ image(random.choice(os.listdir(IMAGE_PATH + "qxz/")), "qxz")
)
@group_decrease_handle.handle() @group_decrease_handle.handle()
@@ -97,9 +110,9 @@ async def _(bot: Bot, event: GroupDecreaseNoticeEvent, state: dict):
await bot.send_private_msg( await bot.send_private_msg(
user_id=coffee, user_id=coffee,
message=f"****呜..一份踢出报告****\n" message=f"****呜..一份踢出报告****\n"
f"我被 {operator_name}({operator_id})\n" f"我被 {operator_name}({operator_id})\n"
f"踢出了 {group_name}({group_id})\n" f"踢出了 {group_name}({group_id})\n"
f"日期:{str(datetime.now()).split('.')[0]}", f"日期:{str(datetime.now()).split('.')[0]}",
) )
return return
try: try:
@@ -108,20 +121,21 @@ async def _(bot: Bot, event: GroupDecreaseNoticeEvent, state: dict):
).user_name ).user_name
except AttributeError: except AttributeError:
user_name = str(event.user_id) user_name = str(event.user_id)
rst = ""
if event.sub_type == "leave":
rst = f"{user_name}离开了我们..."
if event.sub_type == "kick":
operator = await bot.get_group_member_info(
user_id=event.operator_id, group_id=event.group_id
)
operator_name = operator["card"] if operator["card"] else operator["nickname"]
rst = f"{user_name} 被 {operator_name} 送走了."
if await GroupInfoUser.delete_member_info(event.user_id, event.group_id): if await GroupInfoUser.delete_member_info(event.user_id, event.group_id):
logger.info(f"用户{user_name}, qq={event.user_id} 所属{event.group_id} 删除成功") logger.info(f"用户{user_name}, qq={event.user_id} 所属{event.group_id} 删除成功")
else: else:
logger.info(f"用户{user_name}, qq={event.user_id} 所属{event.group_id} 删除失败") logger.info(f"用户{user_name}, qq={event.user_id} 所属{event.group_id} 删除失败")
try: if await group_manager.check_group_task_status(event.group_id, 'refund_group_remind'):
await group_decrease_handle.send(f"{rst}") rst = ""
except ActionFailed: if event.sub_type == "leave":
return rst = f"{user_name}离开了我们..."
if event.sub_type == "kick":
operator = await bot.get_group_member_info(
user_id=event.operator_id, group_id=event.group_id
)
operator_name = operator["card"] if operator["card"] else operator["nickname"]
rst = f"{user_name} 被 {operator_name} 送走了."
try:
await group_decrease_handle.send(f"{rst}")
except ActionFailed:
return
@@ -1,35 +1,35 @@
from nonebot import on_message from nonebot import on_message
from nonebot.adapters.cqhttp.permission import GROUP from nonebot.adapters.cqhttp.permission import GROUP
from nonebot.typing import T_State from nonebot.typing import T_State
from nonebot.adapters.cqhttp import Bot, GroupMessageEvent from nonebot.adapters.cqhttp import Bot, GroupMessageEvent
import time import time
from .data_source import cancel_all_notice, save_data, get_data, set_data_value from .data_source import cancel_all_notice, save_data, get_data, set_data_value
from services.log import logger from services.log import logger
__plugin_name__ = "查看群最后聊天时间 [Hidden]" __zx_plugin_name__ = "群聊最后聊天时间记录 [Hidden]"
__plugin_version__ = 0.1
__plugin_usage__ = "用法:无" __plugin_author__ = "HibiKier"
last_chat = on_message(priority=1, block=False, permission=GROUP) last_chat = on_message(priority=1, block=False, permission=GROUP)
@last_chat.handle() @last_chat.handle()
async def _(bot: Bot, event: GroupMessageEvent, state: T_State): async def _(bot: Bot, event: GroupMessageEvent, state: T_State):
time_data = await get_data() time_data = await get_data()
set_data_value(event.group_id, time.time()) set_data_value(event.group_id, time.time())
if event.group_id in time_data["_group"]: if event.group_id in time_data["_group"]:
time_data["_group"].remove(event.group_id) time_data["_group"].remove(event.group_id)
set_data_value("_group", time_data["_group"]) set_data_value("_group", time_data["_group"])
for key in time_data.keys(): for key in time_data.keys():
if key not in ["check_time", "_group"]: if key not in ["check_time", "_group"]:
if key not in time_data["_group"]: if key not in time_data["_group"]:
if time.time() - time_data[key] > 60 * 60 * 36: if time.time() - time_data[key] > 60 * 60 * 36:
await cancel_all_notice(key) await cancel_all_notice(key)
time_data["_group"].append(key) time_data["_group"].append(key)
set_data_value("_group", time_data["_group"]) set_data_value("_group", time_data["_group"])
logger.info(f"GROUP {event.group_id} 因群内发言时间大于36小时被取消全部通知") logger.info(f"GROUP {event.group_id} 因群内发言时间大于36小时被取消全部通知")
if time.time() - time_data["check_time"] > 60 * 60 * 1: if time.time() - time_data["check_time"] > 60 * 60 * 1:
set_data_value("check_time", time.time()) set_data_value("check_time", time.time())
save_data() save_data()
@@ -1,71 +1,67 @@
from configs.path_config import DATA_PATH from configs.path_config import DATA_PATH
from utils.utils import get_bot from utils.utils import get_bot
from models.group_remind import GroupRemind from datetime import datetime
from datetime import datetime import time
import time from services.log import logger
from services.log import logger from utils.manager import group_manager
try:
try: import ujson as json
import ujson as json except ModuleNotFoundError:
except ModuleNotFoundError: import json
import json
time_data = {}
time_data = {}
async def init():
async def init(): global time_data
global time_data bot = get_bot()
bot = get_bot() gl = await bot.get_group_list()
gl = await bot.get_group_list(self_id=bot.self_id) gl = [g["group_id"] for g in gl]
gl = [g["group_id"] for g in gl] data = read_data("group_last_chat_time.json")
data = read_data("group_last_chat_time.json") for g in gl:
for g in gl: if not data.get(g):
if not data.get(g): time_data[g] = time.time()
time_data[g] = time.time() if not time_data.get("check_time"):
if not time_data.get("check_time"): time_data["check_time"] = time.time()
time_data["check_time"] = time.time() if not time_data.get("_group"):
if not time_data.get("_group"): time_data["_group"] = []
time_data["_group"] = [] save_data()
save_data() return time_data
return time_data
def read_data(file_name: str):
def read_data(file_name: str): try:
try: with open(DATA_PATH + file_name, "r", encoding="utf8") as f:
with open(DATA_PATH + file_name, "r", encoding="utf8") as f: return json.load(f)
return json.load(f) except (ValueError, FileNotFoundError):
except (ValueError, FileNotFoundError): return {}
return {}
def save_data():
def save_data(): with open(DATA_PATH + "group_last_chat_time.json", "w") as f:
with open(DATA_PATH + "group_last_chat_time.json", "w") as f: json.dump(time_data, f, indent=4)
json.dump(time_data, f, indent=4) logger.info(
logger.info( f'自动存储 group_last_chat_time.json 时间:{str(datetime.now()).split(".")[0]}'
f'自动存储 group_last_chat_time.json 时间:{str(datetime.now()).split(".")[0]}' )
)
# 取消全部通知
command_list = ["zwa", "hy", "kxcz", "blpar", "epic", "pa", "gb"] async def cancel_all_notice(group_id):
group_id = int(group_id)
for command in group_manager.get_task_data():
# 取消全部通知 if await group_manager.check_group_task_status(group_id, command):
async def cancel_all_notice(group_id): await group_manager.close_group_task(group_id, command)
group_id = int(group_id) logger.info(f"关闭了 {group_id} 群的全部通知")
for command in command_list:
if await GroupRemind.get_status(group_id, command):
await GroupRemind.set_status(group_id, command, False) async def get_data():
logger.info(f"关闭了 {group_id} 群的全部通知") global time_data
if not time_data:
time_data = await init()
async def get_data(): return time_data
global time_data
if not time_data:
time_data = await init() def set_data_value(key, value):
return time_data global time_data
time_data[key] = value
def set_data_value(key, value):
global time_data
time_data[key] = value
+41 -11
View File
@@ -5,16 +5,28 @@ from services.log import logger
from nonebot.adapters.cqhttp import Bot, GroupMessageEvent, MessageEvent, GROUP from nonebot.adapters.cqhttp import Bot, GroupMessageEvent, MessageEvent, GROUP
from nonebot.typing import T_State from nonebot.typing import T_State
from nonebot.permission import SUPERUSER from nonebot.permission import SUPERUSER
from configs.config import plugins2info_dict, NICKNAME from configs.config import NICKNAME
from utils.static_data import group_manager from utils.manager import group_manager, plugins2settings_manager
try: try:
import ujson as json import ujson as json
except ModuleNotFoundError: except ModuleNotFoundError:
import json import json
__plugin_name__ = "群权限操作" __zx_plugin_name__ = "群权限操作 [Superuser]"
__plugin_usage__ = "区分权限功能" __plugin_usage__ = """
usage:
对群权限 | 群白名单 的操作
指令:
修改群权限 [group] [等级]
添加群白名单 *[group]
删除群白名单 *[group]
查看群白名单
""".strip()
__plugin_des__ = "对群权限 | 群白名单 的操作"
__plugin_cmd__ = ["修改群权限 [group] [等级]", "添加群白名单 *[group]", "删除群白名单 *[group]", "查看群白名单"]
__plugin_version__ = 0.1
__plugin_author__ = "HibiKier"
add_group_level = on_command("修改群权限", priority=1, permission=SUPERUSER, block=True) add_group_level = on_command("修改群权限", priority=1, permission=SUPERUSER, block=True)
@@ -33,10 +45,16 @@ manager_group_whitelist = on_command(
"添加群白名单", aliases={"删除群白名单"}, priority=1, permission=SUPERUSER, block=True "添加群白名单", aliases={"删除群白名单"}, priority=1, permission=SUPERUSER, block=True
) )
show_group_whitelist = on_command(
"查看群白名单", priority=1, permission=SUPERUSER, block=True
)
@add_group_level.handle() @add_group_level.handle()
async def _(bot: Bot, event: MessageEvent, state: T_State): async def _(bot: Bot, event: MessageEvent, state: T_State):
msg = get_message_text(event.json()) msg = get_message_text(event.json())
group_id = 0
level = 0
if not msg: if not msg:
await add_group_level.finish("用法:修改群权限 [group] [level]") await add_group_level.finish("用法:修改群权限 [group] [level]")
msg = msg.split(" ") msg = msg.split(" ")
@@ -50,19 +68,21 @@ async def _(bot: Bot, event: MessageEvent, state: T_State):
old_level = group_manager.get_group_level(group_id) old_level = group_manager.get_group_level(group_id)
group_manager.set_group_level(group_id, level) group_manager.set_group_level(group_id, level)
await add_group_level.send("修改成功...", at_sender=True) await add_group_level.send("修改成功...", at_sender=True)
await bot.send_group_msg( if level > -1:
group_id=int(group_id), message=f"管理员修改了此群权限:{old_level} -> {level}" await bot.send_group_msg(
) group_id=int(group_id), message=f"管理员修改了此群权限:{old_level} -> {level}"
)
logger.info(f"{event.user_id} 修改了 {group_id} 的权限:{level}") logger.info(f"{event.user_id} 修改了 {group_id} 的权限:{level}")
@my_group_level.handle() @my_group_level.handle()
async def _(bot: Bot, event: GroupMessageEvent, state: T_State): async def _(bot: Bot, event: GroupMessageEvent, state: T_State):
level = group_manager.get_group_level(str(event.group_id)) level = group_manager.get_group_level(event.group_id)
tmp = "" tmp = ""
for plugin in plugins2info_dict: data = plugins2settings_manager.get_data()
if plugins2info_dict[plugin]["level"] > level: for module in data:
plugin_name = plugins2info_dict[plugin]["cmd"][0] if data[module]["level"] > level:
plugin_name = data[module]["cmd"][0]
if plugin_name == "pixiv": if plugin_name == "pixiv":
plugin_name = "搜图 p站排行" plugin_name = "搜图 p站排行"
tmp += f"{plugin_name}\n" tmp += f"{plugin_name}\n"
@@ -100,3 +120,13 @@ async def _(bot: Bot, event: MessageEvent, state: T_State):
) )
else: else:
await manager_group_whitelist.send(f"添加失败,请检查{NICKNAME}是否已加入这些群聊或重复添加/删除群白单名") await manager_group_whitelist.send(f"添加失败,请检查{NICKNAME}是否已加入这些群聊或重复添加/删除群白单名")
@show_group_whitelist.handle()
async def _(bot: Bot, event: MessageEvent, state: T_State):
x = group_manager.get_group_white_list()
x = [str(g) for g in x]
if x:
await show_group_whitelist.send("目前的群白名单:\n" + "\n".join(x))
else:
await show_group_whitelist.send("没有任何群在群白名单...")
+17 -3
View File
@@ -12,9 +12,23 @@ try:
except ModuleNotFoundError: except ModuleNotFoundError:
import json import json
__plugin_name__ = "查看群欢迎消息" __zx_plugin_name__ = "查看群欢迎消息"
__plugin_usage__ = """
__plugin_usage__ = "无" usage:
查看当前的群欢迎消息
指令:
查看群欢迎消息
""".strip()
__plugin_des__ = "查看群欢迎消息"
__plugin_cmd__ = ["查看群欢迎消息"]
__plugin_version__ = 0.1
__plugin_author__ = "HibiKier"
__plugin_settings__ = {
"level": 5,
"default_status": True,
"limit_superuser": False,
"cmd": ["查看群欢迎消息"],
}
view_custom_welcome = on_command( view_custom_welcome = on_command(
"群欢迎消息", aliases={"查看群欢迎消息", "查看当前群欢迎消息"}, permission=GROUP, priority=5, block=True "群欢迎消息", aliases={"查看群欢迎消息", "查看当前群欢迎消息"}, permission=GROUP, priority=5, block=True
+44 -26
View File
@@ -1,43 +1,61 @@
from nonebot import on_command from nonebot import on_command
from nonebot.adapters.cqhttp import Bot, GroupMessageEvent, PrivateMessageEvent from nonebot.adapters.cqhttp import (
Bot,
MessageEvent,
)
from nonebot.typing import T_State from nonebot.typing import T_State
from nonebot.rule import to_me from nonebot.rule import to_me
from configs.path_config import DATA_PATH from configs.path_config import IMAGE_PATH
from utils.message_builder import image from utils.message_builder import image
import os from .data_source import create_help_img, get_plugin_help
from .data_source import create_help_img, create_group_help_img, get_plugin_help
from utils.utils import get_message_text from utils.utils import get_message_text
from pathlib import Path
__plugin_name__ = "帮助" __zx_plugin_name__ = "帮助"
if not os.path.exists(DATA_PATH + "group_help/"): help_image = Path(IMAGE_PATH) / "help.png"
os.mkdir(DATA_PATH + "group_help/") simple_help_image = Path(IMAGE_PATH) / "simple_help.png"
create_help_img() if help_image.exists():
for file in os.listdir(DATA_PATH + "group_help/"): help_image.unlink()
os.remove(DATA_PATH + "group_help/" + file) if simple_help_image.exists():
simple_help_image.unlink()
_help = on_command("功能", rule=to_me(), aliases={"help", "帮助"}, priority=1, block=True) _help = on_command("详细功能", rule=to_me(), aliases={"详细帮助"}, priority=1, block=True)
simple_help = on_command("功能", rule=to_me(), aliases={"help", "帮助"}, priority=1, block=True)
@_help.handle() @_help.handle()
async def _(bot: Bot, event: GroupMessageEvent, state: T_State): async def _(bot: Bot, event: MessageEvent, state: T_State):
msg = get_message_text(event.json()) if not help_image.exists():
if not msg: if help_image.exists():
if not os.path.exists(DATA_PATH + f"group_help/{event.group_id}.png"): help_image.unlink()
create_group_help_img(event.group_id) if simple_help_image.exists():
await _help.finish( simple_help_image.unlink()
image(abspath=DATA_PATH + f"group_help/{event.group_id}.png") await create_help_img(help_image, simple_help_image)
) await _help.finish(image("help.png"))
else:
await _help.finish(get_plugin_help(msg))
@_help.handle() @simple_help.handle()
async def _(bot: Bot, event: PrivateMessageEvent, state: T_State): async def _(bot: Bot, event: MessageEvent, state: T_State):
msg = get_message_text(event.json()) msg = get_message_text(event.json())
if not msg: is_super = False
await _help.finish(image("help.png")) if msg:
if '-super' in msg:
if str(event.user_id) in bot.config.superusers:
is_super = True
msg = msg.replace('-super', '')
msg = get_plugin_help(msg, is_super)
if msg:
await _help.send(image(b64=msg))
else:
await _help.send("没有此功能的帮助信息...")
else: else:
await _help.finish(get_plugin_help(msg)) if not simple_help_image.exists():
if help_image.exists():
help_image.unlink()
if simple_help_image.exists():
simple_help_image.unlink()
await create_help_img(help_image, simple_help_image)
await _help.finish(image("simple_help.png"))
-71
View File
@@ -1,71 +0,0 @@
from configs.config import NICKNAME
# 实用
utility_help = {
"update_pic": "一些对图片的操作 --> 指令:操作图片/图片/修改图片(包含 10 种图片操作)",
"search_buff_skin_price": "BUFF皮肤底价查询 --> 指令:查询皮肤(代理ip不得劲)",
"weather": "天气查询 --> 指令:xx天气",
"yiqing": "实时疫情数据 --> 指令:疫情查询/疫情/查询疫情",
"bt": "bt(磁力搜索){仅支持私聊,懂的都懂} --> 指令:bt",
"reimu": "老司机必备!{仅支持私聊,懂的都懂}-> 指令:上车",
"what_anime": "靠图识番 --> 指令:识番",
"nonebot_plugin_picsearcher": "以图搜图 --> 指令:识图/这是什么/上一张图是什么",
"search_anime": "找不到想看的动漫吗? --> 指令:搜番",
"songpicker2": "来一首歌听听? --> 指令:点歌",
"epic": "epic速速白嫖 --> 指令:epic",
"pixiv_r": "P站排行榜直接冲 --> 指令:p站排行(可含参数)",
"pixiv_s": "P站的图随便搜搜 --> 指令:搜图(可含参数)",
"pid_search": "通过PID搜索图片 --> 指令:p搜 [pid]",
"translate": "出国旅游助手(狗头) --> 指令:英翻/翻英/日翻/翻日/韩翻/翻韩",
"cover": "快捷的b站封面获取方式 --> 指令:b封面 [连接/av/bv/cv/直播id]",
}
# 娱乐
entertainment_help = {
"sign_in": "签到(影响色图和开箱) --> 指令:签到/我的签到/好感度排行/好感度总榜/好感度总榜[屏蔽我/显示我]",
"send_img": "发送图片 --> 指令:美图/萝莉/壁纸/(美图/萝莉/壁纸[id])/N张图xx(N<=9)",
"send_setu": "不要小看涩图啊混蛋! --> 指令:色图/色图[id]/n张色图/n张xx色图/查色图/...(请查看 色图 帮助)",
"white2black_img": "黑白草图 --> 指令:黑白图/黑白草图",
"coser": "三次元也不戳 --> 指令:coser",
"jitang": "不喝点什么不舒服 --> 指令:鸡汤/语录",
"send_dinggong_voice": "骂我(傲娇?) --> 指令:骂老子",
"poke": "戳一戳发送语音美图萝莉图不美哉?",
"open_cases": "模拟开箱(戒赌) --> 指令:开箱(N连开箱[N<=30])/我的开箱/群开箱统计/我的金色",
"luxun": "鲁迅说过 --> 指令:鲁迅说",
"fake_msg": "构造一个假消息 --> 指令:假消息",
"shop_handle": "商店系统(初始送100金币) --> 指令:商店/我的金币/购买道具/使用道具",
"draw_card_prts": "换个地方当非酋TvT... --> 指令:方舟一井/方舟N抽(0<N<300)",
"draw_card_genshin": "提瓦特是个好地方! --> 指令:原神一井/原神N抽(0<N<180)/原神武器/角色N抽/重置原神抽卡次数",
"draw_card_pretty": "赛马娘!!!! --> 指令:赛马娘一井/赛马娘N抽/赛马娘卡一井/赛马娘卡N抽(0<N<200)",
"draw_card_guardian": "坎公骑冠剑.... --> 指令:坎公骑冠剑一井/坎公骑冠剑武器一井/坎公骑冠剑N抽/坎公骑冠剑武器N抽(0<N<300)",
"draw_card_pcr": "公主连结(国服)来几发 --> 指令:pcr一井/pcrN抽(0<N<300)",
"draw_card_azur": "我跟方舟坐狱友 --> 指令:碧蓝轻型/重型/特型N抽(0<N<300)",
"draw_card_fgo": "你就是我的马斯塔嘛 --> 指令:fgoN抽(0<N<300)",
"draw_card_onmyoji": "普通的阴阳师召唤 --> 指令:阴阳师N抽(0<N<300)",
"one_friend": "我有一个朋友想问问... --> 指令:我有一个朋友想问问xxx(内容)",
"nickname": "区区昵称! --> 指令:以后叫我xx(昵称)/我是谁/取消昵称",
"almanac": "这是一张正经的黄历 --> 指令:原神黄历",
"material_remind": "看看原神今天要刷什么 --> 指令:今日素材/今日材料/今天素材/今天材料",
"qiu_qiu_translation": "这家伙到底在说什么? --> 指令:丘丘翻译/丘丘一下/丘丘语翻译",
"query_resource_points": "地图资源速速查看 --> 指令:原神资源查询xx[路径]/原神资源列表/哪里有xx/xx在哪(xx=资源名称)",
"russian": "紧张刺激的俄罗斯轮盘 --> 指令:俄罗斯轮盘帮助",
"gold_redbag": f"运气项目又来啦! --> 指令:塞红包/ 抢红包指令: 开/抢/戳一戳{NICKNAME}/ 退回:退回未抢完的红包",
"poetry": f"突然文艺的{NICKNAME}是否搞错了什么 --> 指令:念诗/来首诗/念首诗",
"comments_163": "生了个人,我很抱歉 --> 指令:到点了/12点了/网易云热评/网易云评论",
"pix_gallery": "偶尔也想看看美图? --> 指令:PIX [关键词/uid/pid:pid] [num]/查看pix图库 [关键词]/显示pix关键词",
'nbnhhsh': "会说话就多说点! --> 指令:nbnhhsh/能不能好好说话 [文本](空格划分)",
"roll": f"让{NICKNAME}来帮你决定吧! --> 指令:roll/ roll [文本](空格划分)",
"wbtop": f"刚买完瓜,在吃瓜现场 --> 指令:微博热搜/微博热搜 [数字]"
}
# 其他
other_help = [
"查看当前的群欢迎消息 --> 指令:群欢迎消息",
"这是一份正经的自我介绍 --> 指令:自我介绍",
"不得看看自己权力多大? --> 指令:我的权限",
"有人记得你是什么时候加入我们的 --> 指令:我的信息",
"让我看看更新了什么 --> 指令:更新信息",
f"{NICKNAME}给我把话收回去! --> 指令:撤回 [id](默认0)",
"群拥有的权限 --> 指令:查看群权限",
"数据统计可视化_1 --> 指令:功能调用统计/日功能调用统计/周功能调用统计/月功能调用统计",
"数据统计可视化_2 --> 指令:周功能调用统计 [功能名称]/月功能调用统计 [功能名称]",
"这里是PIX图库! --> 指令:添加pix关键词 [关键词]/添加pixuid/pid [UID/PID]/我的pix关键词",
]
+274 -165
View File
@@ -1,180 +1,289 @@
from utils.image_utils import CreateImg from utils.image_utils import CreateImg
from configs.path_config import IMAGE_PATH, DATA_PATH from configs.path_config import IMAGE_PATH
from utils.manager import plugins2settings_manager
from typing import Optional
from services.log import logger
from pathlib import Path from pathlib import Path
from .config import * from utils.utils import get_matchers
from configs.config import ( import random
INITIAL_OPEN_CASE_COUNT, import asyncio
INITIAL_SETU_PROBABILITY,
ADMIN_DEFAULT_AUTH,
)
from configs.config import plugins2info_dict, NICKNAME
from utils.static_data import group_manager
import nonebot import nonebot
import os
width = 1600 random_bk_path = Path(IMAGE_PATH) / "background" / "help" / "simple_help"
e_height = 0
u_height = 950 background = Path(IMAGE_PATH) / "background" / "0.png"
o_height = 1500
# f_height =
def create_help_img(): async def create_help_img(help_image: Path, simple_help_image: Path):
help_img_file = Path(IMAGE_PATH) / 'help.png' """
if help_img_file.exists(): 生成帮助图片
help_img_file.unlink() :param help_image: 图片路径
h = ( :param simple_help_image: 简易帮助图片路径
100 """
+ len(utility_help) * 24 return await asyncio.get_event_loop().run_in_executor(
+ len(entertainment_help) * 24 None, _create_help_img, help_image, simple_help_image
+ len(other_help) * 24
) * 2
A = CreateImg(width, h - 200, font_size=24)
e = CreateImg(width, len(entertainment_help) * 42, font_size=24)
rst = ""
i = 0
for cmd in entertainment_help:
rst += f"{i + 1}.{entertainment_help[cmd]}\n"
i += 1
e.text((10, 10), "娱乐功能:")
e.text((40, 40), rst)
u = CreateImg(width, len(utility_help) * 40 + 50, font_size=24, color="black")
rst = ""
i = 0
for cmd in utility_help:
rst += f"{i + 1}.{utility_help[cmd]}\n"
i += 1
u.text((10, 10), "实用功能:", fill=(255, 255, 255))
u.text((40, 40), rst, fill=(255, 255, 255))
o = CreateImg(width, len(other_help) * 40, font_size=24)
rst = ""
i = 0
for i in range(len(other_help)):
rst += f"{i + 1}.{other_help[i]}\n"
i += 1
o.text((10, 10), "其他功能:")
o.text((40, 40), rst)
A.paste(e, (0, 0))
A.paste(u, (0, u_height))
A.paste(o, (0, o_height))
A.text(
(10, h * 0.68),
f"大部分交互功能可以通过输入‘取消’,‘算了’来取消当前交互\n对{NICKNAME}说 “{NICKNAME}帮助 指令名” 获取对应详细帮助\n"
"可以通过 “滴滴滴- [消息]” 联系管理员(有趣的想法尽管来吧!<还有Bug和建议>)"
"\n[群管理员请看 管理员帮助(群主与管理员自带 5 级权限)]\n\n"
f"\t「如果{NICKNAME}回复了一些不符合人设的话,那是因为每日白嫖的图灵次数已用完,使用的是备用接口【QAQ】」",
) )
A.save(IMAGE_PATH + "help.png")
def _create_help_img(help_image: Path, simple_help_image: Path):
def create_group_help_img(group_id: int): """
group_id = str(group_id) 生成帮助图片
h = ( :param help_image: 图片路径
100 :param simple_help_image: 简易帮助图片路径
+ len(utility_help) * 24 """
+ len(entertainment_help) * 24 _matchers = get_matchers()
+ len(other_help) * 24 width = 0
) * 2 matchers_data = {}
A = CreateImg(width, h - 200, font_size=24) _des_tmp = {}
u = CreateImg(width, len(utility_help) * 40, font_size=24, color="black") _plugin_name_tmp = []
o = CreateImg(width, len(other_help) * 40, font_size=24) _tmp = []
e = CreateImg(width, len(entertainment_help) * 42, font_size=24) tmp_img = CreateImg(0, 0, plain_text="1", font_size=24)
rst = "" font_height = tmp_img.h
i = 1 for matcher in _matchers:
for cmd in entertainment_help.keys(): plugin_name = None
flag, dfg = parse_cmd(cmd, group_id) _plugin = nonebot.plugin.get_plugin(matcher.module)
if dfg: _module = _plugin.module
cmd = rcmd(dfg) try:
rst += f"【{flag}】{i}.{entertainment_help[cmd]}\n" plugin_name = _module.__getattribute__("__zx_plugin_name__")
i += 1 try:
e.text((10, 10), "娱乐功能:") plugin_des = _module.__getattribute__("__plugin_des__")
e.text((40, 40), rst) except AttributeError:
plugin_des = "_"
rst = "" if (
i = 1 "[hidden]" in plugin_name.lower()
for cmd in utility_help.keys(): or "[admin]" in plugin_name.lower()
flag, dfg = parse_cmd(cmd, group_id) or "[superuser]" in plugin_name.lower()
rst += f"【{flag}】{i}.{utility_help[cmd]}\n" or plugin_name in _plugin_name_tmp
i += 1 or plugin_name == "帮助"
u.text((10, 10), "实用功能:", fill=(255, 255, 255)) ):
u.text((40, 40), rst, fill=(255, 255, 255)) continue
plugin_type = ("normal",)
rst = "" text_type = 0
for i in range(len(other_help)): if plugins2settings_manager.get(
rst += f"{i + 1}.{other_help[i]}\n" matcher.module
o.text((10, 10), "其他功能:") ) and plugins2settings_manager[matcher.module].get("plugin_type"):
o.text((40, 40), rst) plugin_type = tuple(
plugins2settings_manager.get_plugin_data(matcher.module)[
A.paste(e, (0, 0)) "plugin_type"
A.paste(u, (0, u_height)) ]
A.paste(o, (0, o_height)) )
# A.text((width, 10), f'总开关【{"√" if data["总开关"] else "×"}】') else:
A.text( try:
(10, h * 0.68), plugin_type = _module.__getattribute__("__plugin_type__")
f"大部分交互功能可以通过输入‘取消’,‘算了’来取消当前交互\n对{NICKNAME}说 “{NICKNAME}帮助 指令名” 获取对应详细帮助\n" except AttributeError:
"可以通过 “滴滴滴- [消息]” 联系管理员(有趣的想法尽管来吧!<还有Bug和建议>)" pass
f"\n[群管理员请看 管理员帮助(群主与管理员自带 {ADMIN_DEFAULT_AUTH} 级权限)]", if len(plugin_type) > 1:
try:
text_type = int(plugin_type[1])
except ValueError as e:
logger.warning(f"生成列向帮助排列失败 {plugin_name}: {type(e)}: {e}")
plugin_type = plugin_type[0]
else:
plugin_type = plugin_type[0]
try:
plugin_cmd = _module.__getattribute__("__plugin_cmd__")
plugin_cmd = [x for x in plugin_cmd if "[_superuser]" not in x]
except AttributeError:
plugin_cmd = []
if plugin_type not in matchers_data.keys():
matchers_data[plugin_type] = {}
if plugin_des in _des_tmp.keys():
try:
matchers_data[plugin_type][_des_tmp[plugin_des]]["cmd"] = (
matchers_data[plugin_type][_des_tmp[plugin_des]]["cmd"]
+ plugin_cmd
)
except KeyError as e:
logger.warning(f"{type(e)}: {e}")
else:
matchers_data[plugin_type][plugin_name] = {
"des": plugin_des,
"cmd": plugin_cmd,
"text_type": text_type,
}
try:
if text_type == 0:
x = tmp_img.getsize(
f'{plugin_name}: {matchers_data[plugin_type][plugin_name]["des"]} ->'
+ " / ".join(matchers_data[plugin_type][plugin_name]["cmd"])
)[0]
width = width if width > x else x
except KeyError:
pass
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.module}: {plugin_name} 设置失败...e:{e}")
if plugin_name not in _plugin_name_tmp:
_plugin_name_tmp.append(plugin_name)
help_img_list = []
simple_help_img_list = []
types = list(matchers_data.keys())
types.sort()
ix = 0
for type_ in types:
keys = list(matchers_data[type_].keys())
keys.sort()
help_str = f"{type_ if type_ != 'normal' else '功能'}:\n\n"
simple_help_str = f"{type_ if type_ != 'normal' else '功能'}:\n\n"
for i, k in enumerate(keys):
simple_help_str += f"{i+1}.{k}\n"
if matchers_data[type_][k]["text_type"] == 1:
_x = tmp_img.getsize(
f"{i+1}".rjust(5)
+ f'.{k}: {matchers_data[type_][k]["des"]} {"->" if matchers_data[type_][k]["cmd"] else ""} '
)[0]
_str = (
f"{i+1}".rjust(5)
+ f'.{k}: {matchers_data[type_][k]["des"]} {"->" if matchers_data[type_][k]["cmd"] else ""} '
)
_str += matchers_data[type_][k]["cmd"][0] + "\n"
for c in matchers_data[type_][k]["cmd"][1:]:
_str += "".rjust(int(_x * 0.125) + 1) + f"{c}\n"
help_str += _str
else:
help_str += (
f"{i+1}".rjust(5)
+ f'.{k}: {matchers_data[type_][k]["des"]} {"->" if matchers_data[type_][k]["cmd"] else ""} '
+ " / ".join(matchers_data[type_][k]["cmd"])
+ "\n"
)
height = len(help_str.split("\n")) * (font_height + 5)
simple_height = len(simple_help_str.split("\n")) * (font_height + 5)
A = CreateImg(
width + 150, height, font_size=24, color="white" if not ix % 2 else "black"
)
A.text((10, 10), help_str, (255, 255, 255) if ix % 2 else (0, 0, 0))
simple_width = 0
for x in [tmp_img.getsize(x)[0] for x in simple_help_str.split("\n")]:
simple_width = simple_width if simple_width > x else x
bk = CreateImg(
simple_width + 20, simple_height, font_size=24, color="#6495ED"
)
B = CreateImg(
simple_width + 20,
simple_height,
font_size=24,
color="white" if not ix % 2 else "black",
)
B.text((10, 10), simple_help_str, (255, 255, 255) if ix % 2 else (0, 0, 0))
bk.paste(B, center_type="center")
bk.transparent(2)
ix += 1
help_img_list.append(A)
simple_help_img_list.append(bk)
height = 0
for img in help_img_list:
height += img.h
A = CreateImg(width + 150, height + 50, font_size=24)
A.text((10, 10), '* 注: ‘*’ 代表可有多个相同参数 ‘?’ 代表可省略该参数 *\n\n" "功能名: 功能简介 -> 指令\n\n')
current_height = 50
for img in help_img_list:
A.paste(img, (0, current_height))
current_height += img.h
A.save(help_image)
height = 0
width = 0
for img in simple_help_img_list:
if img.h > height:
height = img.h
width += img.w + 10
B = CreateImg(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 = CreateImg(
x,
x,
font_size=24,
color="#FFEFD5",
background=random_bk_path / bk,
) )
A.text( B.filter("GaussianBlur", 10)
(10, h * 0.77), _, B = get_max_width_or_paste(simple_help_img_list, B, True)
f"【注】「色图概率:好感度 + {int(INITIAL_SETU_PROBABILITY*100)}%\n" w = 10
f"\t\t每 3 点好感度 + 1次开箱,初始 {INITIAL_OPEN_CASE_COUNT} 次\n" h = 10
f"\t\t开启/关闭功能只需输入‘开启/关闭 指令名’(每个功能的第一个指令)」\n" for msg in ['目前支持的功能列表:', '可以通过 ‘帮助[功能名称]’ 来获取对应功能的使用方法', '或者使用 ‘详细帮助’ 来获取所有功能方法']:
f"\t\t示例:开启签到\n" text = CreateImg(
f"\t\t可以通过管理员开关自动发送消息(早晚安等)\n" 0,
f"\t\t^请查看管理员帮助^\n\n" 0,
f"\t「如果{NICKNAME}回复了一些不符合人设的话,那是因为每日白嫖的图灵次数已用完,使用的是备用接口【QAQ】」", plain_text=msg,
) font_size=24,
A.save(DATA_PATH + f"group_help/{group_id}.png") color=(255, 255, 255, 0),
font='yuanshen.ttf'
)
B.paste(text, (w, h), True)
h += 50
if msg == '目前支持的功能列表:':
w += 50
B.save(simple_help_image)
def parse_cmd(cmd, group_id): def get_max_width_or_paste(
flag = "√" simple_help_img_list: list, B: CreateImg = None, is_paste: bool = False
dfg = None ) -> "int, CreateImg":
if cmd.find("draw_card") != -1: """
lst = cmd.split("_") 获取最大宽度,或直接贴图
cmd = lst[0] + "_" + lst[1] :param simple_help_img_list: 简单帮助图片列表
dfg = lst[-1] :param B: 背景图
elif cmd == "pixiv_r": :param is_paste: 是否直接贴图
cmd = "pixiv" """
dfg = "r" current_width = 50
elif cmd == "pixiv_s": current_height = 180
cmd = "pixiv" max_width = simple_help_img_list[0].w
dfg = "s" for i in range(len(simple_help_img_list)):
if not group_manager.get_plugin_status(cmd, group_id): try:
flag = "×" if is_paste and B:
if cmd in ["bt", "reimu", "nickname"]: B.paste(simple_help_img_list[i], (current_width, current_height), True)
flag = "- " current_height += simple_help_img_list[i].h + 40
return flag, dfg 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 rcmd(dfg): def get_plugin_help(msg: str, is_super: bool = False) -> Optional[str]:
if dfg in [ """
"prts", 获取功能的帮助信息
"genshin", :param msg: 功能cmd
"pretty", :param is_super: 是否为超级用户
"guardian", """
"pcr", module = plugins2settings_manager.get_plugin_module(msg)
"azur", if module:
"onmyoji", try:
"fgo", plugin = nonebot.plugin.get_plugin(module)
]: if plugin:
return "draw_card_" + dfg if is_super:
if dfg == "r": result = plugin.module.__getattribute__(
return "pixiv_r" "__plugin_superuser_usage__"
if dfg == "s": )
return "pixiv_s" else:
result = plugin.module.__getattribute__("__plugin_usage__")
width = 0
def get_plugin_help(msg: str) -> str: for x in result.split("\n"):
plugin = None _width = len(x) * 24
for p in plugins2info_dict.keys(): width = width if width > _width else _width
if msg in plugins2info_dict[p]["cmd"]: height = len(result.split("\n")) * 45
plugin = nonebot.plugin.get_plugin(p) A = CreateImg(width, height, font_size=24)
break bk = CreateImg(
if plugin: width, height, background=Path(IMAGE_PATH) / "background" / "1.png"
result = plugin.module.__getattribute__("__plugin_usage__") )
return result A.paste(bk, alpha=True)
else: A.text((int(width * 0.048), int(height * 0.21)), result)
return "没有此功能的帮助信息..." return A.pic2bs4()
except AttributeError:
pass
return None
+248 -49
View File
@@ -1,29 +1,45 @@
from nonebot.matcher import Matcher from nonebot.matcher import Matcher
from nonebot.message import run_preprocessor, IgnoredException from nonebot.message import run_preprocessor, run_postprocessor, IgnoredException
from nonebot.adapters.cqhttp.exception import ActionFailed from nonebot.adapters.cqhttp.exception import ActionFailed
from models.group_member_info import GroupInfoUser
from utils.manager import (
plugins2cd_manager,
plugins2block_manager,
plugins2settings_manager,
admin_manager
)
from models.friend_user import FriendUser
from typing import Optional
from nonebot.typing import T_State from nonebot.typing import T_State
from nonebot.adapters.cqhttp import ( from nonebot.adapters.cqhttp import (
Bot, Bot,
Event,
MessageEvent, MessageEvent,
PrivateMessageEvent, PrivateMessageEvent,
GroupMessageEvent, GroupMessageEvent,
PokeNotifyEvent,
Message,
) )
from configs.config import ( from configs.config import (
BAN_RESULT, BAN_RESULT,
admin_plugins_auth,
MALICIOUS_BAN_TIME, MALICIOUS_BAN_TIME,
MALICIOUS_CHECK_TIME, MALICIOUS_CHECK_TIME,
MALICIOUS_BAN_COUNT, MALICIOUS_BAN_COUNT,
CHECK_NOTICE_INFO_CD, CHECK_NOTICE_INFO_CD,
plugins2info_dict,
) )
from models.ban_user import BanUser from models.ban_user import BanUser
from utils.utils import is_number, static_flmt, BanCheckLimiter from utils.utils import (
is_number,
static_flmt,
BanCheckLimiter,
FreqLimiter,
)
from utils.manager import withdraw_message_manager
from utils.message_builder import at from utils.message_builder import at
from services.log import logger from services.log import logger
from models.level_user import LevelUser from models.level_user import LevelUser
from utils.static_data import group_manager from utils.manager import group_manager
from utils.utils import FreqLimiter import asyncio
try: try:
import ujson as json import ujson as json
@@ -34,6 +50,14 @@ except ModuleNotFoundError:
# 检查是否被ban # 检查是否被ban
@run_preprocessor @run_preprocessor
async def _(matcher: Matcher, bot: Bot, event: MessageEvent, state: T_State): async def _(matcher: Matcher, bot: Bot, event: MessageEvent, state: T_State):
try:
if (
await BanUser.is_super_ban(event.user_id)
and str(event.user_id) not in bot.config.superusers
):
raise IgnoredException("用户处于超级黑名单中")
except AttributeError:
pass
if not isinstance(event, MessageEvent): if not isinstance(event, MessageEvent):
return return
if matcher.type == "message" and matcher.priority not in [1, 9]: if matcher.type == "message" and matcher.priority not in [1, 9]:
@@ -47,9 +71,9 @@ async def _(matcher: Matcher, bot: Bot, event: MessageEvent, state: T_State):
if time < 60: if time < 60:
time = str(time) + " 秒" time = str(time) + " 秒"
else: else:
time = str(int(time / 60)) + " 分" time = str(int(time / 60)) + " 分钟"
else: else:
time = str(time) + " 分" time = str(time) + " 分钟"
if isinstance(event, GroupMessageEvent): if isinstance(event, GroupMessageEvent):
if not static_flmt.check(event.user_id): if not static_flmt.check(event.user_id):
raise IgnoredException("用户处于黑名单中") raise IgnoredException("用户处于黑名单中")
@@ -60,7 +84,7 @@ async def _(matcher: Matcher, bot: Bot, event: MessageEvent, state: T_State):
group_id=event.group_id, group_id=event.group_id,
message=at(event.user_id) message=at(event.user_id)
+ BAN_RESULT + BAN_RESULT
+ f" 在..在 {time}后才会理你喔", + f" 在..在 {time} 后才会理你喔",
) )
except ActionFailed: except ActionFailed:
pass pass
@@ -91,15 +115,10 @@ async def _(matcher: Matcher, bot: Bot, event: GroupMessageEvent, state: T_State
return return
if matcher.type == "message" and matcher.priority not in [1, 9]: if matcher.type == "message" and matcher.priority not in [1, 9]:
if state["_prefix"]["raw_command"]: if state["_prefix"]["raw_command"]:
# print(state["_prefix"]["raw_command"])
if _blmt.check(f'{event.user_id}{state["_prefix"]["raw_command"]}'): if _blmt.check(f'{event.user_id}{state["_prefix"]["raw_command"]}'):
if await BanUser.ban(event.user_id, 9, MALICIOUS_BAN_TIME * 60): if await BanUser.ban(event.user_id, 9, MALICIOUS_BAN_TIME * 60):
logger.info(f"USER {event.user_id} 触发了恶意触发检测") logger.info(f"USER {event.user_id} 触发了恶意触发检测")
# await update_img.finish('检测到恶意触发命令,您将被封禁 30 分钟', at_sender=True) if isinstance(event, GroupMessageEvent):
if event.message_type == "group":
if not static_flmt.check(event.user_id):
return
static_flmt.start_cd(event.user_id)
try: try:
await bot.send_group_msg( await bot.send_group_msg(
group_id=event.group_id, group_id=event.group_id,
@@ -108,9 +127,6 @@ async def _(matcher: Matcher, bot: Bot, event: GroupMessageEvent, state: T_State
except ActionFailed: except ActionFailed:
pass pass
else: else:
if not static_flmt.check(event.user_id):
return
static_flmt.start_cd(event.user_id)
try: try:
await bot.send_private_msg( await bot.send_private_msg(
user_id=event.user_id, user_id=event.user_id,
@@ -126,23 +142,37 @@ _flmt = FreqLimiter(CHECK_NOTICE_INFO_CD)
_flmt_g = FreqLimiter(CHECK_NOTICE_INFO_CD) _flmt_g = FreqLimiter(CHECK_NOTICE_INFO_CD)
_flmt_s = FreqLimiter(CHECK_NOTICE_INFO_CD) _flmt_s = FreqLimiter(CHECK_NOTICE_INFO_CD)
_flmt_c = FreqLimiter(CHECK_NOTICE_INFO_CD) _flmt_c = FreqLimiter(CHECK_NOTICE_INFO_CD)
_exists_msg = {}
ignore_rst_module = ["ai", "poke"]
# 权限检测 # 权限检测
@run_preprocessor @run_preprocessor
async def _(matcher: Matcher, bot: Bot, event: MessageEvent, state: T_State): async def _(matcher: Matcher, bot: Bot, event: MessageEvent, state: T_State):
global _exists_msg
module = matcher.module
plugins2info_dict = plugins2settings_manager.get_data()
if ( if (
not isinstance(event, MessageEvent) (not isinstance(event, MessageEvent) and module != "poke")
or await BanUser.is_ban(event.user_id) or await BanUser.is_ban(event.user_id)
or str(event.user_id) in bot.config.superusers 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 return
module = matcher.module # 黑名单检测
if module in admin_plugins_auth.keys() and matcher.priority not in [1, 9]: if isinstance(event, GroupMessageEvent):
if group_manager.get_group_level(event.group_id) < 0:
raise IgnoredException("群黑名单")
if module in admin_manager.keys() and matcher.priority not in [1, 9]:
if isinstance(event, GroupMessageEvent): if isinstance(event, GroupMessageEvent):
# 个人权限 # 个人权限
if not await LevelUser.check_level( if not await LevelUser.check_level(
event.user_id, event.group_id, admin_plugins_auth[module] event.user_id, event.group_id, admin_manager.get_plugin_level(module)
): ):
try: try:
if _flmt.check(event.user_id): if _flmt.check(event.user_id):
@@ -150,72 +180,88 @@ async def _(matcher: Matcher, bot: Bot, event: MessageEvent, state: T_State):
await bot.send_group_msg( await bot.send_group_msg(
group_id=event.group_id, group_id=event.group_id,
message=f"{at(event.user_id)}你的权限不足喔,该功能需要的权限等级:" message=f"{at(event.user_id)}你的权限不足喔,该功能需要的权限等级:"
f"{admin_plugins_auth[module]}", f"{admin_manager.get_plugin_level(module)}",
) )
except ActionFailed: except ActionFailed:
pass pass
raise IgnoredException("权限不足") raise IgnoredException("权限不足")
else: else:
if not await LevelUser.check_level( if not await LevelUser.check_level(
event.user_id, 0, admin_plugins_auth[module] event.user_id, 0, admin_manager.get_plugin_level(module)
): ):
try: try:
await bot.send_private_msg( await bot.send_private_msg(
user_id=event.user_id, user_id=event.user_id,
message=f"你的权限不足喔,该功能需要的权限等级:{admin_plugins_auth[module]}", message=f"你的权限不足喔,该功能需要的权限等级:{admin_manager.get_plugin_level(module)}",
) )
except ActionFailed: except ActionFailed:
pass pass
raise IgnoredException("权限不足") raise IgnoredException("权限不足")
if module in plugins2info_dict.keys(): if module in plugins2info_dict.keys() and matcher.priority not in [1, 9]:
if isinstance(event, GroupMessageEvent): # 戳一戳单独判断
if isinstance(event, GroupMessageEvent) or (
isinstance(event, PokeNotifyEvent) and event.group_id
):
if _exists_msg.get(event.group_id) is None:
_exists_msg[event.group_id] = False
# 群权限 # 群权限
if plugins2info_dict[module]["level"] > group_manager.get_group_level( if plugins2info_dict[module]["level"] > group_manager.get_group_level(
str(event.group_id) event.group_id
): ):
try: try:
if _flmt_g.check(event.user_id): if _flmt_g.check(event.user_id) and module not in ignore_rst_module:
_flmt_g.start_cd(event.user_id) _flmt_g.start_cd(event.user_id)
await bot.send_group_msg( await bot.send_group_msg(
group_id=event.group_id, message="群权限不足..." group_id=event.group_id, message="群权限不足..."
) )
except ActionFailed: except ActionFailed:
pass pass
_exists_msg[event.group_id] = True
raise IgnoredException("群权限不足") raise IgnoredException("群权限不足")
# 插件状态 # 插件状态
if not group_manager.get_plugin_status(module, str(event.group_id)): if not group_manager.get_plugin_status(module, event.group_id):
try: try:
if _flmt_s.check(event.group_id): if module not in ignore_rst_module and _flmt_s.check(
event.group_id
):
_flmt_s.start_cd(event.group_id) _flmt_s.start_cd(event.group_id)
await bot.send_group_msg( await bot.send_group_msg(
group_id=event.group_id, message="该群未开启此功能.." group_id=event.group_id, message="该群未开启此功能.."
) )
except ActionFailed: except ActionFailed:
pass pass
_exists_msg[event.group_id] = True
print(123123)
raise IgnoredException("未开启此功能...") raise IgnoredException("未开启此功能...")
# 管理员禁用 # 管理员禁用
if not group_manager.get_plugin_status( if not group_manager.get_plugin_status(f"{module}:super", event.group_id):
f"{module}:super", str(event.group_id)
):
try: try:
if _flmt_s.check(event.group_id): if (
_flmt_s.check(event.group_id)
and module not in ignore_rst_module
):
_flmt_s.start_cd(event.group_id) _flmt_s.start_cd(event.group_id)
await bot.send_group_msg( await bot.send_group_msg(
group_id=event.group_id, message="管理员禁用了此群该功能..." group_id=event.group_id, message="管理员禁用了此群该功能..."
) )
except ActionFailed: except ActionFailed:
pass pass
_exists_msg[event.group_id] = True
raise IgnoredException("管理员禁用了此群该功能...") raise IgnoredException("管理员禁用了此群该功能...")
# 群聊禁用 # 群聊禁用
if not group_manager.get_plugin_status(module, block_type="group"): if not group_manager.get_plugin_status(module, block_type="group"):
try: try:
if _flmt_c.check(event.group_id): if (
_flmt_c.check(event.group_id)
and module not in ignore_rst_module
):
_flmt_c.start_cd(event.group_id) _flmt_c.start_cd(event.group_id)
await bot.send_group_msg( await bot.send_group_msg(
group_id=event.group_id, message="该功能在群聊中已被禁用..." group_id=event.group_id, message="该功能在群聊中已被禁用..."
) )
except ActionFailed: except ActionFailed:
pass pass
_exists_msg[event.group_id] = True
raise IgnoredException("该插件在群聊中已被禁用...") raise IgnoredException("该插件在群聊中已被禁用...")
else: else:
# 私聊禁用 # 私聊禁用
@@ -230,25 +276,166 @@ async def _(matcher: Matcher, bot: Bot, event: MessageEvent, state: T_State):
pass pass
raise IgnoredException("该插件在私聊中已被禁用...") raise IgnoredException("该插件在私聊中已被禁用...")
# 维护 # 维护
if not group_manager.get_plugin_status( if not group_manager.get_plugin_status(module, block_type="all"):
module, block_type="all" if isinstance(
) and not group_manager.check_group_is_white(event.group_id): event, GroupMessageEvent
) and group_manager.check_group_is_white(event.group_id):
return
try: try:
if _flmt_c.check(event.group_id): if isinstance(event, GroupMessageEvent):
_flmt_c.start_cd(event.group_id) if (
if isinstance(event, GroupMessageEvent): _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( await bot.send_group_msg(
group_id=event.group_id, message="此功能正在维护..." group_id=event.group_id, message="此功能正在维护..."
) )
else: else:
await bot.send_private_msg( await bot.send_private_msg(
user_id=event.user_id, message="此功能正在维护..." user_id=event.user_id, message="此功能正在维护..."
) )
except ActionFailed: except ActionFailed:
pass pass
if isinstance(event, GroupMessageEvent):
_exists_msg[event.group_id] = True
raise IgnoredException("此功能正在维护...") raise IgnoredException("此功能正在维护...")
# 命令cd 和 命令阻塞
@run_preprocessor
async def _(matcher: Matcher, bot: Bot, event: MessageEvent, state: T_State):
global _exists_msg
if not isinstance(event, MessageEvent) and matcher.module != "poke":
return
module = matcher.module
if isinstance(event, GroupMessageEvent) and _exists_msg.get(event.group_id) is None:
_exists_msg[event.group_id] = False
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_)
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)
async def send_msg(rst: str, bot: Bot, event: MessageEvent):
"""
发送信息
:param rst: pass
:param bot: pass
:param event: pass
"""
global _exists_msg
rst = await init_rst(rst, event)
try:
if isinstance(event, GroupMessageEvent):
_exists_msg[event.group_id] = True
await bot.send_group_msg(group_id=event.group_id, message=Message(rst))
else:
_exists_msg[event.user_id] = True
await bot.send_private_msg(user_id=event.user_id, message=Message(rst))
except ActionFailed:
pass
@run_postprocessor
async def _(
matcher: Matcher,
exception: Optional[Exception],
bot: Bot,
event: Event,
state: T_State,
):
if not isinstance(event, MessageEvent) and matcher.module != "poke":
return
module = matcher.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"]
if not (
(isinstance(event, GroupMessageEvent) and check_type == "private")
or (isinstance(event, PrivateMessageEvent) and check_type == "group")
):
block_type_ = event.user_id
if limit_type == "group" and isinstance(event, GroupMessageEvent):
block_type_ = event.group_id
plugins2block_manager.set_false(block_type_, module)
async def init_rst(rst: str, event: MessageEvent):
if "[uname]" in rst:
uname = event.sender.card if event.sender.card else 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
# 消息撤回
@run_postprocessor
async def _(
matcher: Matcher,
exception: Optional[Exception],
bot: Bot,
event: Event,
state: T_State,
):
tasks = []
for id_, time in withdraw_message_manager.data:
tasks.append(asyncio.ensure_future(_withdraw_message(bot, id_, time)))
withdraw_message_manager.remove((id_, time))
await asyncio.gather(*tasks)
async def _withdraw_message(bot: Bot, id_: int, time: int):
await asyncio.sleep(time)
await bot.delete_msg(message_id=id_, self_id=int(bot.self_id))
# 为什么AI会自己和自己聊天 # 为什么AI会自己和自己聊天
@run_preprocessor @run_preprocessor
async def _(matcher: Matcher, bot: Bot, event: PrivateMessageEvent, state: T_State): async def _(matcher: Matcher, bot: Bot, event: PrivateMessageEvent, state: T_State):
@@ -261,8 +448,20 @@ async def _(matcher: Matcher, bot: Bot, event: PrivateMessageEvent, state: T_Sta
# 有命令就别说话了 # 有命令就别说话了
@run_preprocessor @run_preprocessor
async def _(matcher: Matcher, bot: Bot, event: MessageEvent, state: T_State): async def _(matcher: Matcher, bot: Bot, event: MessageEvent, state: T_State):
global _exists_msg
if not isinstance(event, MessageEvent): if not isinstance(event, MessageEvent):
return return
if matcher.type == "message": if matcher.type == "message":
if state["_prefix"]["raw_command"] and matcher.module == "ai": if matcher.module == "ai":
raise IgnoredException("有命令就别说话了") if (
isinstance(event, GroupMessageEvent)
and _exists_msg.get(event.group_id) is True
):
_exists_msg[event.group_id] = False
raise IgnoredException("有命令就别说话了")
elif (
isinstance(event, PrivateMessageEvent)
and _exists_msg.get(event.user_id) is True
):
_exists_msg[event.user_id] = False
raise IgnoredException("有命令就别说话了")
+3
View File
@@ -0,0 +1,3 @@
import nonebot
nonebot.load_plugins("plugins/image_management")
@@ -1,86 +1,97 @@
from configs.path_config import IMAGE_PATH, TEMP_PATH from configs.path_config import IMAGE_PATH, TEMP_PATH
from utils.message_builder import image from utils.message_builder import image
from services.log import logger from services.log import logger
from nonebot import on_command from nonebot import on_command
from nonebot.rule import to_me from nonebot.rule import to_me
from nonebot.typing import T_State from nonebot.typing import T_State
from nonebot.adapters.cqhttp import Bot, MessageEvent, GroupMessageEvent from nonebot.adapters.cqhttp import Bot, MessageEvent, GroupMessageEvent
from configs.config import IMAGE_DIR_LIST from configs.config import DELETE_IMG_LEVEL
from utils.utils import is_number, cn2py from configs.config import IMAGE_DIR_LIST
from pathlib import Path from utils.utils import is_number, cn2py, get_message_text
import os from pathlib import Path
import os
__plugin_name__ = "删除图片"
__plugin_usage__ = ( __zx_plugin_name__ = "删除图片 [Admin]"
"删除图片帮助:\n\t" __plugin_usage__ = """
"1.查看列表 --> 指令: 删除图片 列表/目录\n\t" usage:
"2.删除图片 图库 id, 即在相应目录下删除图片\n\t\t示例: 删除图片 色图 1 " 删除图库指定图片
) 指令:
删除图片 [图库] [id]
查看图库
delete_img = on_command("删除图片", priority=5, rule=to_me(), block=True) 示例:删除图片 美图 666
""".strip()
__plugin_des__ = "不好看的图片删掉删掉!"
@delete_img.args_parser __plugin_cmd__ = ["删除图片 [图库] [id]", "查看公开图库"]
async def parse(bot: Bot, event: MessageEvent, state: T_State): __plugin_version__ = 0.1
if str(event.get_message()) in ["取消", "算了"]: __plugin_author__ = "HibiKier"
await delete_img.finish("已取消操作..", at_sender=True) __plugin_settings__ = {
if state["_current_key"] in ["path"]: "admin_level": DELETE_IMG_LEVEL
if str(event.get_message()) not in IMAGE_DIR_LIST: }
await delete_img.reject("此目录不正确,请重新输入目录!")
state[state["_current_key"]] = str(event.get_message())
if state["_current_key"] == "id": delete_img = on_command("删除图片", priority=5, rule=to_me(), block=True)
if not is_number(str(event.get_message())):
await delete_img.reject("id不正确!请重新输入数字...")
state[state["_current_key"]] = str(event.get_message()) @delete_img.args_parser
async def parse(bot: Bot, event: MessageEvent, state: T_State):
if get_message_text(event.json()) in ["取消", "算了"]:
@delete_img.handle() await delete_img.finish("已取消操作..", at_sender=True)
async def _(bot: Bot, event: MessageEvent, state: T_State): if state["_current_key"] in ["path"]:
raw_arg = str(event.get_message()).strip() if get_message_text(event.json()) not in IMAGE_DIR_LIST:
if raw_arg: await delete_img.reject("此目录不正确,请重新输入目录!")
args = raw_arg.split(" ") state[state["_current_key"]] = get_message_text(event.json())
if args[0] in ["帮助"]: if state["_current_key"] == "id":
await delete_img.finish(__plugin_usage__) if not is_number(get_message_text(event.json())):
if len(args) >= 2 and args[0] in IMAGE_DIR_LIST and is_number(args[1]): await delete_img.reject("id不正确!请重新输入数字...")
state["path"] = args[0] state[state["_current_key"]] = get_message_text(event.json())
state["id"] = args[1]
@delete_img.handle()
@delete_img.got("path", prompt="请输入要删除的目标图库?") async def _(bot: Bot, event: MessageEvent, state: T_State):
@delete_img.got("id", prompt="请输入要删除的图片id?") raw_arg = get_message_text(event.json()).strip()
async def arg_handle(bot: Bot, event: MessageEvent, state: T_State): if raw_arg:
path = cn2py(state["path"]) args = raw_arg.split(" ")
img_id = state["id"] if args[0] in ["帮助"]:
# path = IMAGE_PATH + path await delete_img.finish(__plugin_usage__)
path = Path(IMAGE_PATH) / path if len(args) >= 2 and args[0] in IMAGE_DIR_LIST and is_number(args[1]):
temp = Path(IMAGE_PATH) / 'temp' state["path"] = args[0]
max_id = len(os.listdir(path)) - 1 state["id"] = args[1]
if int(img_id) > max_id or int(img_id) < 0:
await delete_img.finish(f"Id超过上下限,上限:{max_id}", at_sender=True)
try: @delete_img.got("path", prompt="请输入要删除的目标图库?")
if os.path.exists(temp / "delete.jpg"): @delete_img.got("id", prompt="请输入要删除的图片id?")
os.remove(temp / "delete.jpg") async def arg_handle(bot: Bot, event: MessageEvent, state: T_State):
logger.info("删除图片 delete.jpg 成功") path = cn2py(state["path"])
except Exception as e: img_id = state["id"]
logger.warning(f"删除图片 delete.jpg 失败 e{e}") # path = IMAGE_PATH + path
try: path = Path(IMAGE_PATH) / path
os.rename(path / f"{img_id}.jpg", temp / "delete.jpg") temp = Path(IMAGE_PATH) / "temp"
logger.info(f"移动 {path}/{img_id}.jpg 移动成功") max_id = len(os.listdir(path)) - 1
except Exception as e: if int(img_id) > max_id or int(img_id) < 0:
logger.warning(f"{path}/{img_id}.jpg --> 移动失败 e:{e}") await delete_img.finish(f"Id超过上下限,上限:{max_id}", at_sender=True)
if not os.path.exists(path / f"{img_id}.jpg"): try:
try: if os.path.exists(temp / "delete.jpg"):
if int(img_id) != max_id: os.remove(temp / "delete.jpg")
os.rename(path / f"{max_id}.jpg", path / f"{img_id}.jpg") logger.info("删除图片 delete.jpg 成功")
except FileExistsError as e: except Exception as e:
logger.error(f"{path}/{max_id}.jpg 替换 {path}/{img_id}.jpg 失败 e:{e}") logger.warning(f"删除图片 delete.jpg 失败 e{e}")
logger.info(f"{path}/{max_id}.jpg 替换 {path}/{img_id}.jpg 成功") try:
logger.info( os.rename(path / f"{img_id}.jpg", temp / "delete.jpg")
f"USER {event.user_id} GROUP {event.group_id if isinstance(event, GroupMessageEvent) else 'private'}" logger.info(f"移动 {path}/{img_id}.jpg 移动成功")
f" -> id: {img_id} 删除成功" except Exception as e:
) logger.warning(f"{path}/{img_id}.jpg --> 移动失败 e:{e}")
await delete_img.finish( if not os.path.exists(path / f"{img_id}.jpg"):
f"id: {img_id} 删除成功" + image("delete.jpg", TEMP_PATH), at_sender=True try:
) if int(img_id) != max_id:
await delete_img.finish(f"id: {img_id} 删除失败!") os.rename(path / f"{max_id}.jpg", path / f"{img_id}.jpg")
except FileExistsError as e:
logger.error(f"{path}/{max_id}.jpg 替换 {path}/{img_id}.jpg 失败 e:{e}")
logger.info(f"{path}/{max_id}.jpg 替换 {path}/{img_id}.jpg 成功")
logger.info(
f"USER {event.user_id} GROUP {event.group_id if isinstance(event, GroupMessageEvent) else 'private'}"
f" -> id: {img_id} 删除成功"
)
await delete_img.finish(
f"id: {img_id} 删除成功" + image("delete.jpg", TEMP_PATH), at_sender=True
)
await delete_img.finish(f"id: {img_id} 删除失败!")
@@ -1,95 +1,105 @@
import os import os
from services.log import logger from services.log import logger
from nonebot import on_command from nonebot import on_command
from nonebot.rule import to_me from nonebot.rule import to_me
from nonebot.typing import T_State from nonebot.typing import T_State
from nonebot.adapters.cqhttp import Bot, MessageEvent, GroupMessageEvent from nonebot.adapters.cqhttp import Bot, MessageEvent, GroupMessageEvent
from configs.config import IMAGE_DIR_LIST from configs.config import IMAGE_DIR_LIST, MOVE_IMG_LEVEL
from utils.utils import is_number, cn2py from utils.utils import is_number, cn2py
from configs.path_config import IMAGE_PATH from configs.path_config import IMAGE_PATH
from pathlib import Path from pathlib import Path
__plugin_name__ = "移动图片" __zx_plugin_name__ = "移动图片 [Admin]"
__plugin_usage__ = ( __plugin_usage__ = """
"移动图片帮助:\n\t" usage:
"1.查看列表 --> 指令: 移动图片 列表/目录\n\t" 图库间的图片移动操作
"2.移动图片 源 目的 id\n\t\t示例: 移动图片 色图 美图 1234" 指令:
) 移动图片 [源图库] [目标图库] [id]
查看图库
示例:移动图片 萝莉 美图 234
move_img = on_command("移动图片", priority=5, rule=to_me(), block=True) """.strip()
__plugin_des__ = "图库间的图片移动操作"
__plugin_cmd__ = ["移动图片 [源图库] [目标图库] [id]", "查看公开图库"]
@move_img.args_parser __plugin_version__ = 0.1
async def parse(bot: Bot, event: MessageEvent, state: T_State): __plugin_author__ = "HibiKier"
if str(event.get_message()) in ["取消", "算了"]: __plugin_settings__ = {
await move_img.finish("已取消操作..", at_sender=True) "admin_level": MOVE_IMG_LEVEL
if state["_current_key"] in ["source_path", "destination_path"]: }
if str(event.get_message()) not in IMAGE_DIR_LIST:
await move_img.reject("此目录不正确,请重新输入目录!")
state[state["_current_key"]] = str(event.get_message()) move_img = on_command("移动图片", priority=5, rule=to_me(), block=True)
if state["_current_key"] == "id":
if not is_number(str(event.get_message())):
await move_img.reject("id不正确!请重新输入数字...") @move_img.args_parser
state[state["_current_key"]] = str(event.get_message()) async def parse(bot: Bot, event: MessageEvent, state: T_State):
if str(event.get_message()) in ["取消", "算了"]:
await move_img.finish("已取消操作..", at_sender=True)
@move_img.handle() if state["_current_key"] in ["source_path", "destination_path"]:
async def _(bot: Bot, event: MessageEvent, state: T_State): if str(event.get_message()) not in IMAGE_DIR_LIST:
raw_arg = str(event.get_message()).strip() await move_img.reject("此目录不正确,请重新输入目录!")
if raw_arg: state[state["_current_key"]] = str(event.get_message())
args = raw_arg.split(" ") if state["_current_key"] == "id":
if args[0] in ["帮助"]: if not is_number(str(event.get_message())):
await move_img.finish(__plugin_usage__) await move_img.reject("id不正确!请重新输入数字...")
if ( state[state["_current_key"]] = str(event.get_message())
len(args) >= 3
and args[0] in IMAGE_DIR_LIST
and args[1] in IMAGE_DIR_LIST @move_img.handle()
and is_number(args[2]) async def _(bot: Bot, event: MessageEvent, state: T_State):
): raw_arg = str(event.get_message()).strip()
state["source_path"] = args[0] if raw_arg:
state["destination_path"] = args[1] args = raw_arg.split(" ")
state["id"] = args[2] if args[0] in ["帮助"]:
else: await move_img.finish(__plugin_usage__)
await move_img.finish("参数错误,请重试", at_sender=True) if (
len(args) >= 3
and args[0] in IMAGE_DIR_LIST
@move_img.got("source_path", prompt="要从哪个图库移出?") and args[1] in IMAGE_DIR_LIST
@move_img.got("destination_path", prompt="要移动到哪个图库?") and is_number(args[2])
@move_img.got("id", prompt="要移动的图片id是?") ):
async def _(bot: Bot, event: MessageEvent, state: T_State): state["source_path"] = args[0]
img_id = state["id"] state["destination_path"] = args[1]
source_path = Path(IMAGE_PATH) / cn2py(state["source_path"]) state["id"] = args[2]
destination_path = Path(IMAGE_PATH) / cn2py(state["destination_path"]) else:
destination_path.mkdir(parents=True, exist_ok=True) await move_img.finish("参数错误,请重试", at_sender=True)
max_id = len(os.listdir(source_path)) - 1
des_max_id = len(os.listdir(destination_path))
if int(img_id) > max_id or int(img_id) < 0: @move_img.got("source_path", prompt="要从哪个图库移出?")
await move_img.finish(f"Id超过上下限,上限:{max_id}", at_sender=True) @move_img.got("destination_path", prompt="要移动到哪个图库?")
try: @move_img.got("id", prompt="要移动的图片id是?")
os.rename( async def _(bot: Bot, event: MessageEvent, state: T_State):
source_path / f"{img_id}.jpg", destination_path / f"{des_max_id}.jpg" img_id = state["id"]
) source_path = Path(IMAGE_PATH) / cn2py(state["source_path"])
logger.info( destination_path = Path(IMAGE_PATH) / cn2py(state["destination_path"])
f"移动 {source_path}/{img_id}.jpg ---> {destination_path}/{des_max_id} 移动成功" destination_path.mkdir(parents=True, exist_ok=True)
) max_id = len(os.listdir(source_path)) - 1
except Exception as e: des_max_id = len(os.listdir(destination_path))
logger.warning( if int(img_id) > max_id or int(img_id) < 0:
f"移动 {source_path}/{img_id}.jpg ---> {destination_path}/{des_max_id} 移动失败 e:{e}" await move_img.finish(f"Id超过上下限,上限:{max_id}", at_sender=True)
) try:
await move_img.finish(f"移动图片id:{img_id} 失败了...", at_sender=True) os.rename(
if max_id > 0: source_path / f"{img_id}.jpg", destination_path / f"{des_max_id}.jpg"
try: )
os.rename(source_path / f"{max_id}.jpg", source_path / f"{img_id}.jpg") logger.info(
logger.info(f"{source_path}/{max_id}.jpg 替换 {source_path}/{img_id}.jpg 成功") f"移动 {source_path}/{img_id}.jpg ---> {destination_path}/{des_max_id} 移动成功"
except Exception as e: )
logger.warning( except Exception as e:
f"{source_path}/{max_id}.jpg 替换 {source_path}/{img_id}.jpg 失败 e:{e}" logger.warning(
) f"移动 {source_path}/{img_id}.jpg ---> {destination_path}/{des_max_id} 移动失败 e:{e}"
await move_img.finish(f"替换图片id:{max_id} -> {img_id} 失败了...", at_sender=True) )
logger.info( await move_img.finish(f"移动图片id:{img_id} 失败了...", at_sender=True)
f"USER {event.user_id} GROUP {event.group_id if isinstance(event, GroupMessageEvent) else 'private'} ->" if max_id > 0:
f" {source_path} --> {destination_path} (id:{img_id}) 移动图片成功" try:
) os.rename(source_path / f"{max_id}.jpg", source_path / f"{img_id}.jpg")
await move_img.finish(f"移动图片 id:{img_id} --> id:{des_max_id}成功", at_sender=True) logger.info(f"{source_path}/{max_id}.jpg 替换 {source_path}/{img_id}.jpg 成功")
except Exception as e:
logger.warning(
f"{source_path}/{max_id}.jpg 替换 {source_path}/{img_id}.jpg 失败 e:{e}"
)
await move_img.finish(f"替换图片id:{max_id} -> {img_id} 失败了...", at_sender=True)
logger.info(
f"USER {event.user_id} GROUP {event.group_id if isinstance(event, GroupMessageEvent) else 'private'} ->"
f" {source_path} --> {destination_path} (id:{img_id}) 移动图片成功"
)
await move_img.finish(f"移动图片 id:{img_id} --> id:{des_max_id}成功", at_sender=True)
@@ -1,101 +1,127 @@
from nonebot import on_command from nonebot import on_command
from nonebot.rule import to_me from nonebot.rule import to_me
from nonebot.typing import T_State from nonebot.typing import T_State
from nonebot.adapters.cqhttp import Bot, MessageEvent, GroupMessageEvent from nonebot.adapters.cqhttp import Bot, MessageEvent, GroupMessageEvent
from utils.utils import get_message_imgs, get_message_text
from .data_source import upload_image_to_local from configs.config import IMAGE_DIR_LIST, UPLOAD_IMG_LEVEL
from utils.utils import get_message_imgs, get_message_text
__plugin_name__ = "上传图片" from .data_source import upload_image_to_local
__plugin_usage__ = (
"上传图片帮助:\n\t"
"1.查看列表 --> 指令: 上传图片 列表/目录\n\t" __zx_plugin_name__ = "上传图片 [Admin]"
"2.上传图片 [序号] [图片], 即在相应目录下添加图片\n\t\t示例: 上传图片 1 [图片]" __plugin_usage__ = """
) usage:
上传图片至指定图库
upload_img = on_command("上传图片", rule=to_me(), priority=5, block=True) 指令:
查看图库
continuous_upload_img = on_command('连续上传图片', rule=to_me(), priority=5, block=True) 上传图片 [图库] [图片]
连续上传图片 [图库]
示例:上传图片 美图 [图片]
@upload_img.args_parser * 连续上传图片可以通过发送 “stop” 表示停止收集发送的图片,可以开始上传 *
async def _(bot: Bot, event: MessageEvent, state: T_State): """.strip()
msg = get_message_text(event.json()) __plugin_des__ = "指定图库图片上传"
if msg in ["取消", "算了"]: __plugin_cmd__ = ["上传图片 [图库] [图片]", "连续上传图片 [图库]", "查看公开图库"]
await upload_img.finish("已取消操作..", at_sender=True) __plugin_version__ = 0.1
if state["_current_key"] in ["path"]: __plugin_author__ = "HibiKier"
if msg not in IMAGE_DIR_LIST: __plugin_settings__ = {"admin_level": UPLOAD_IMG_LEVEL}
await upload_img.reject("此目录不正确,请重新输入目录!")
state['path'] = msg upload_img = on_command("上传图片", rule=to_me(), priority=5, block=True)
if state["_current_key"] in ["imgs"]:
if not get_message_imgs(event.json()): continuous_upload_img = on_command("连续上传图片", rule=to_me(), priority=5, block=True)
await upload_img.reject("图呢图呢图呢图呢!GKD!")
state['imgs'] = get_message_imgs(event.json()) show_gallery = on_command("查看公开图库", priority=1, block=True)
@upload_img.handle() @show_gallery.handle()
async def _(bot: Bot, event: MessageEvent, state: T_State): async def _(bot: Bot, event: MessageEvent, state: T_State):
raw_arg = get_message_text(event.json()) x = '公开图库列表:\n'
img_list = get_message_imgs(event.json()) for i, e in enumerate(IMAGE_DIR_LIST):
if raw_arg: x += f'\t{i+1}.{e}\n'
if raw_arg in IMAGE_DIR_LIST: await show_gallery.send(x[:-1])
state["path"] = raw_arg
if img_list:
state["imgs"] = img_list @upload_img.args_parser
async def _(bot: Bot, event: MessageEvent, state: T_State):
msg = get_message_text(event.json())
@upload_img.got('path', prompt='要将图片上传至什么图库呢?') if msg in ["取消", "算了"]:
async def _(bot: Bot, event: MessageEvent, state: T_State): await upload_img.finish("已取消操作..", at_sender=True)
pass if state["_current_key"] in ["path"]:
if msg not in IMAGE_DIR_LIST:
await upload_img.reject("此目录不正确,请重新输入目录!")
@upload_img.got("imgs", prompt="图呢图呢图呢图呢!GKD!") state["path"] = msg
async def _(bot: Bot, event: MessageEvent, state: T_State): if state["_current_key"] in ["imgs"]:
path = state['path'] if not get_message_imgs(event.json()):
img_list = state['imgs'] await upload_img.reject("图呢图呢图呢图呢!GKD!")
group_id = 0 state["imgs"] = get_message_imgs(event.json())
if isinstance(event, GroupMessageEvent):
group_id = event.group_id
await upload_img.send(await upload_image_to_local(img_list, path, event.user_id, group_id)) @upload_img.handle()
async def _(bot: Bot, event: MessageEvent, state: T_State):
raw_arg = get_message_text(event.json())
@continuous_upload_img.args_parser img_list = get_message_imgs(event.json())
async def _(bot: Bot, event: MessageEvent, state: T_State): if raw_arg:
if str(event.get_message()) in ["取消", "算了"]: if raw_arg in IMAGE_DIR_LIST:
await continuous_upload_img.finish("已取消操作..", at_sender=True) state["path"] = raw_arg
if state["_current_key"] in ["path"]: if img_list:
if str(event.get_message()) not in IMAGE_DIR_LIST: state["imgs"] = img_list
await continuous_upload_img.reject("此目录不正确,请重新输入目录!")
state[state["_current_key"]] = str(event.get_message())
else: @upload_img.got("path", prompt="要将图片上传至什么图库呢?")
if get_message_text(event.json()) not in ['stop']: async def _(bot: Bot, event: MessageEvent, state: T_State):
img = get_message_imgs(event.json()) pass
if img:
state['tmp'].extend(img)
await continuous_upload_img.reject('图再来!!') @upload_img.got("imgs", prompt="图呢图呢图呢图呢!GKD!")
else: async def _(bot: Bot, event: MessageEvent, state: T_State):
state['imgs'] = state['tmp'] path = state["path"]
img_list = state["imgs"]
group_id = 0
@continuous_upload_img.handle() if isinstance(event, GroupMessageEvent):
async def _(bot: Bot, event: MessageEvent, state: T_State): group_id = event.group_id
path = get_message_imgs(event.json()) await upload_img.send(
if path in IMAGE_DIR_LIST: await upload_image_to_local(img_list, path, event.user_id, group_id)
state['path'] = path )
await continuous_upload_img.send('图来!!')
state['tmp'] = []
@continuous_upload_img.args_parser
async def _(bot: Bot, event: MessageEvent, state: T_State):
@continuous_upload_img.got("path", prompt="要将图片上传至什么图库呢?") if str(event.get_message()) in ["取消", "算了"]:
async def _(bot: Bot, event: MessageEvent, state: T_State): await continuous_upload_img.finish("已取消操作..", at_sender=True)
pass if state["_current_key"] in ["path"]:
if str(event.get_message()) not in IMAGE_DIR_LIST:
await continuous_upload_img.reject("此目录不正确,请重新输入目录!")
@continuous_upload_img.got("imgs", prompt="图呢图呢图呢图呢!GKD!") state[state["_current_key"]] = str(event.get_message())
async def _(bot: Bot, event: MessageEvent, state: T_State): else:
path = state['path'] if get_message_text(event.json()) not in ["stop"]:
img_list = state['imgs'] img = get_message_imgs(event.json())
group_id = 0 if img:
if isinstance(event, GroupMessageEvent): state["tmp"].extend(img)
group_id = event.group_id await continuous_upload_img.reject("图再来!!")
await continuous_upload_img.send(await upload_image_to_local(img_list, path, event.user_id, group_id)) else:
state["imgs"] = state["tmp"]
@continuous_upload_img.handle()
async def _(bot: Bot, event: MessageEvent, state: T_State):
path = get_message_imgs(event.json())
if path in IMAGE_DIR_LIST:
state["path"] = path
await continuous_upload_img.send("图来!!")
state["tmp"] = []
@continuous_upload_img.got("path", prompt="要将图片上传至什么图库呢?")
async def _(bot: Bot, event: MessageEvent, state: T_State):
pass
@continuous_upload_img.got("imgs", prompt="图呢图呢图呢图呢!GKD!")
async def _(bot: Bot, event: MessageEvent, state: T_State):
path = state["path"]
img_list = state["imgs"]
group_id = 0
if isinstance(event, GroupMessageEvent):
group_id = event.group_id
await continuous_upload_img.send(
await upload_image_to_local(img_list, path, event.user_id, group_id)
)
+36
View File
@@ -0,0 +1,36 @@
from .data_source import (
init_plugins_settings,
init_plugins_cd_limit,
init_plugins_block_limit,
init_group_manager,
)
from nonebot.adapters.cqhttp import Bot
from configs.path_config import DATA_PATH
from services.log import logger
from nonebot import Driver
import nonebot
__zx_plugin_name__ = "初始化插件数据 [Hidden]"
__plugin_version__ = 0.1
__plugin_author__ = "HibiKier"
driver: Driver = nonebot.get_driver()
@driver.on_startup
def _():
"""
初始化数据
"""
init_plugins_settings(DATA_PATH)
init_plugins_cd_limit(DATA_PATH)
init_plugins_block_limit(DATA_PATH)
logger.info("初始化数据完成...")
@driver.on_bot_connect
async def _(bot: Bot):
await init_group_manager()
+289
View File
@@ -0,0 +1,289 @@
from pathlib import Path
from ruamel.yaml import YAML, round_trip_load, round_trip_dump
from utils.manager import (
plugins2settings_manager,
plugins2cd_manager,
plugins2block_manager,
group_manager,
admin_manager
)
from services.db_context import db
from asyncpg.exceptions import DuplicateColumnError
from services.log import logger
from utils.utils import get_matchers
import nonebot
try:
import ujson as json
except ModuleNotFoundError:
import json
try:
from models.group_remind import GroupRemind
except ModuleNotFoundError:
pass
yaml = YAML(typ="safe")
def init_plugins_settings(data_path: str):
"""
初始化插件设置,从插件中获取 __zx_plugin_name__,__plugin_cmd__,__plugin_settings__
"""
plugins2config_file = Path(data_path) / "configs" / "plugins2settings.yaml"
plugins2config_file.parent.mkdir(exist_ok=True, parents=True)
_matchers = get_matchers()
_data = {}
if plugins2config_file.exists():
with open(plugins2config_file, "r", encoding="utf8") as f:
_data = yaml.load(f)
_data = _data["PluginSettings"] if _data else {}
_tmp_module = {}
_tmp = []
for matcher in _matchers:
if matcher.module in _data.keys():
plugins2settings_manager.add_plugin_settings(
matcher.module,
plugin_type=_data[matcher.module]["plugin_type"],
data_dict=_data[matcher.module],
)
if _data[matcher.module]['cmd']:
_tmp_module[matcher.module] = _data[matcher.module]['cmd'][0]
else:
_plugin = nonebot.plugin.get_plugin(matcher.module)
_module = _plugin.module
try:
plugin_name = _module.__getattribute__("__zx_plugin_name__")
if "[admin]" in plugin_name.lower():
try:
level = (_module.__getattribute__("__plugin_settings__"))['admin_level']
except (AttributeError, KeyError):
level = 5
admin_manager.add_admin_command(matcher.module, level)
if (
"[hidden]" in plugin_name.lower()
or "[admin]" in plugin_name.lower()
or "[superuser]" in plugin_name.lower()
or matcher.module in plugins2settings_manager.keys()
):
continue
except AttributeError:
if matcher.module not in _tmp:
logger.warning(
f"获取插件 {matcher.module} __zx_plugin_name__ 失败...,插件控制未加载."
)
else:
try:
_tmp_module[matcher.module] = plugin_name
plugin_settings = _module.__getattribute__("__plugin_settings__")
if plugin_settings['cmd'] and plugin_name not in plugin_settings['cmd']:
plugin_settings['cmd'].append(plugin_name)
if plugins2settings_manager.get(
matcher.module
) and plugins2settings_manager[matcher.module].get("plugin_type"):
plugin_type = tuple(
plugins2settings_manager.get_plugin_data(matcher.module)[
"plugin_type"
]
)
else:
try:
plugin_type = _module.__getattribute__("__plugin_type__")
except AttributeError:
plugin_type = ("normal",)
if plugin_settings and matcher.module:
plugins2settings_manager.add_plugin_settings(
matcher.module,
plugin_type=plugin_type,
data_dict=plugin_settings,
)
except AttributeError:
pass
_tmp.append(matcher.module)
_tmp_data = {"PluginSettings": plugins2settings_manager.get_data()}
with open(plugins2config_file, "w", encoding="utf8") as wf:
yaml.dump(_tmp_data, wf)
_data = round_trip_load(open(plugins2config_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(plugins2config_file, "w", encoding="utf8") as wf:
round_trip_dump(_data, wf)
logger.info(f"已成功加载 {len(plugins2settings_manager.get_data())} 个非限制插件.")
def init_plugins_cd_limit(data_path):
"""
加载 cd 限制
"""
plugins2cd_file = Path(data_path) / "configs" / "plugins2cd.yaml"
plugins2cd_file.parent.mkdir(exist_ok=True, parents=True)
_data = {}
_matchers = get_matchers()
for matcher in _matchers:
if not plugins2cd_manager.get_plugin_cd_data(matcher.module):
_plugin = nonebot.plugin.get_plugin(matcher.module)
_module = _plugin.module
try:
plugin_cd_limit = _module.__getattribute__("__plugin_cd_limit__")
plugins2cd_manager.add_cd_limit(
matcher.module, data_dict=plugin_cd_limit
)
except AttributeError:
pass
if plugins2cd_file.exists():
with open(plugins2cd_file, "r", encoding="utf8") as f:
_data = yaml.load(f)
_data = _data if _data else {}
if _data.get("PluginCdLimit"):
for plugin in _data["PluginCdLimit"].keys():
plugins2cd_manager.add_cd_limit(
plugin, data_dict=_data["PluginCdLimit"][plugin]
)
_tmp_data = {"PluginCdLimit": plugins2cd_manager.get_data()}
with open(plugins2cd_file, "w", encoding="utf8") as wf:
yaml.dump(_tmp_data, wf)
_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)
plugins2cd_manager.reload_cd_limit()
def init_plugins_block_limit(data_path):
"""
加载阻塞限制
"""
plugins2block_file = Path(data_path) / "configs" / "plugins2block.yaml"
plugins2block_file.parent.mkdir(exist_ok=True, parents=True)
_data = {}
_matchers = get_matchers()
for matcher in _matchers:
if not plugins2block_manager.get_plugin_block_data(matcher.module):
_plugin = nonebot.plugin.get_plugin(matcher.module)
_module = _plugin.module
try:
plugin_block_limit = _module.__getattribute__("__plugin_block_limit__")
plugins2block_manager.add_block_limit(
matcher.module, data_dict=plugin_block_limit
)
except AttributeError:
pass
if plugins2block_file.exists():
with open(plugins2block_file, "r", encoding="utf8") as f:
_data = yaml.load(f)
_data = _data if _data else {}
if _data.get("PluginBlockLimit"):
for plugin in _data["PluginBlockLimit"].keys():
plugins2block_manager.add_block_limit(
plugin, data_dict=_data["PluginBlockLimit"][plugin]
)
_tmp_data = {"PluginBlockLimit": plugins2block_manager.get_data()}
with open(plugins2block_file, "w", encoding="utf8") as wf:
yaml.dump(_tmp_data, wf)
_data = round_trip_load(open(plugins2block_file, encoding="utf8"))
_data["PluginBlockLimit"].yaml_set_start_comment(
"""# 用户调用阻塞
# 即 当用户调用此功能还未结束时
# 用发送消息阻止用户重复调用此命令直到该命令结束
# 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(plugins2block_file, "w", encoding="utf8") as wf:
round_trip_dump(_data, wf)
plugins2block_manager.reload_block_limit()
async def init_group_manager():
"""
旧数据格式替换为新格式
初始化数据
"""
old_group_level_file = Path() / "data" / "manager" / "group_level.json"
old_plugin_list_file = Path() / "data" / "manager" / "plugin_list.json"
if old_group_level_file.exists():
data = json.load(open(old_group_level_file, "r", encoding="utf8"))
for key in data.keys():
group = key
level = data[key]
group_manager.set_group_level(group, level)
old_group_level_file.unlink()
group_manager.save()
if old_plugin_list_file.exists():
data = json.load(open(old_plugin_list_file, "r", encoding="utf8"))
for plugin in data.keys():
for group in data[plugin].keys():
if group == "default" and not data[plugin]["default"]:
group_manager.block_plugin(plugin)
elif not data[plugin][group]:
group_manager.block_plugin(plugin, group)
old_plugin_list_file.unlink()
old_data_table = Path() / "models" / "group_remind.py"
try:
if old_data_table.exists():
b = {
"hy": "group_welcome",
"kxcz": "open_case_reset_remind",
"zwa": "zwa",
"blpar": "bilibili_parse",
"epic": "epic_free_game",
"pa": "pa",
"almanac": "genshin_alc",
}
for group in group_manager.get_data()["group_manager"]:
for remind in b:
try:
status = await GroupRemind.get_status(int(group), remind)
if status is not None:
if status:
await group_manager.open_group_task(group, b[remind])
logger.info(f"读取旧数据-->{group} 开启 {b[remind]}")
else:
await group_manager.close_group_task(group, b[remind])
logger.info(f"读取旧数据-->{group} 关闭 {b[remind]}")
except Exception as e:
pass
query = db.text("DROP TABLE group_reminds;")
await db.first(query)
old_data_table.unlink()
logger.info("旧数据读取完毕,删除了舍弃表 group_reminds...")
except (ModuleNotFoundError, DuplicateColumnError):
pass
group_manager.save()
+17 -3
View File
@@ -5,11 +5,25 @@ from nonebot.adapters.cqhttp import Bot, MessageEvent, GroupMessageEvent
from nonebot.typing import T_State from nonebot.typing import T_State
import aiohttp import aiohttp
from asyncio.exceptions import TimeoutError from asyncio.exceptions import TimeoutError
from configs.config import NICKNAME
__plugin_name__ = "鸡汤"
__plugin_usage__ = f"用法: 发送’鸡汤‘,{NICKNAME}亲自为你喝鸡汤"
__zx_plugin_name__ = "鸡汤"
__plugin_usage__ = """
usage:
不喝点什么感觉有点不舒服
指令:
鸡汤
""".strip()
__plugin_des__ = "喏,亲手为你煮的鸡汤"
__plugin_cmd__ = ["鸡汤"]
__plugin_version__ = 0.1
__plugin_author__ = "HibiKier"
__plugin_settings__ = {
"level": 5,
"default_status": True,
"limit_superuser": False,
"cmd": ["鸡汤", "毒鸡汤"],
}
url = "https://v2.alapi.cn/api/soul" url = "https://v2.alapi.cn/api/soul"
+44 -46
View File
@@ -1,28 +1,41 @@
from PIL import ImageFont, ImageDraw, Image from configs.path_config import IMAGE_PATH
import textwrap
from configs.path_config import IMAGE_PATH, TTF_PATH
from nonebot import on_command from nonebot import on_command
from nonebot.typing import T_State from nonebot.typing import T_State
from nonebot.adapters.cqhttp import Bot, MessageEvent, GroupMessageEvent from nonebot.adapters.cqhttp import Bot, MessageEvent, GroupMessageEvent
from utils.message_builder import image from utils.message_builder import image
from services.log import logger from services.log import logger
from utils.utils import UserExistLimiter, get_message_text from utils.utils import get_message_text
from utils.image_utils import pic2b64 from utils.image_utils import CreateImg
__plugin_name__ = "鲁迅说"
__plugin_usage__ = "用法:鲁迅说 [消息]"
_ulmt = UserExistLimiter()
__zx_plugin_name__ = "鲁迅说"
__plugin_usage__ = """
usage:
鲁迅说了啥?
指令:
鲁迅说 [文本]
""".strip()
__plugin_des__ = "鲁迅说他没说过这话!"
__plugin_cmd__ = ["鲁迅说"]
__plugin_version__ = 0.1
__plugin_author__ = "HibiKier"
__plugin_settings__ = {
"level": 5,
"default_status": True,
"limit_superuser": False,
"cmd": ["鲁迅说"],
}
__plugin_block_limit__ = {
"rst": "你的鲁迅正在说,等会"
}
luxun = on_command("鲁迅说过", aliases={"鲁迅说"}, priority=5, block=True) luxun = on_command("鲁迅说过", aliases={"鲁迅说"}, priority=5, block=True)
luxun_author = CreateImg(0, 0, plain_text="--鲁迅", font_size=30, font='msyh.ttf', font_color=(255, 255, 255))
@luxun.handle() @luxun.handle()
async def handle(bot: Bot, event: MessageEvent, state: T_State): async def handle(bot: Bot, event: MessageEvent, state: T_State):
if _ulmt.check(event.user_id):
await luxun.finish("你的鲁迅正在说,等会", at_sender=True)
args = get_message_text(event.json()) args = get_message_text(event.json())
if args: if args:
state["content"] = args if args else "烦了,不说了" state["content"] = args if args else "烦了,不说了"
@@ -30,39 +43,24 @@ async def handle(bot: Bot, event: MessageEvent, state: T_State):
@luxun.got("content", prompt="你让鲁迅说点啥?") @luxun.got("content", prompt="你让鲁迅说点啥?")
async def handle_event(bot: Bot, event: MessageEvent, state: T_State): async def handle_event(bot: Bot, event: MessageEvent, state: T_State):
filename = str(event.user_id) + "_.jpg"
content = state["content"].strip() content = state["content"].strip()
if content.startswith(",") or content.startswith(","): if content.startswith(",") or content.startswith(","):
content = content[1:] content = content[1:]
_ulmt.set_True(event.user_id) A = CreateImg(0, 0, font_size=37, background=f'{IMAGE_PATH}/other/luxun.jpg', font='msyh.ttf')
if len(content) > 20: x = ""
_ulmt.set_False(event.user_id) if len(content) > 40:
await luxun.finish("太长了, 鲁迅说不完!", at_sender=True) await luxun.finish('太长了,鲁迅说不完...')
else: while A.getsize(content)[0] > A.w - 50:
if len(content) >= 12: n = int(len(content) / 2)
content = content[:12] + "\n" + content[12:] x += content[:n] + '\n'
img = image(b64=process_pic(content, filename)) content = content[n:]
logger.info( x += content
f"USER {event.user_id} GROUP " if len(x.split('\n')) > 2:
f"{event.group_id if isinstance(event, GroupMessageEvent) else 'private'} 鲁迅说过 {content}" await luxun.finish('太长了,鲁迅说不完...')
) A.text((int((480 - A.getsize(x.split("\n")[0])[0]) / 2), 300), x, (255, 255, 255))
await luxun.send(img) A.paste(luxun_author, (320, 400), True)
_ulmt.set_False(event.user_id) await luxun.send(image(b64=A.pic2bs4()))
logger.info(
f"USER {event.user_id} GROUP "
def process_pic(content, filename) -> str: f"{event.group_id if isinstance(event, GroupMessageEvent) else 'private'} 鲁迅说过 {content}"
text = content )
para = textwrap.wrap(text, width=15)
MAX_W, MAX_H = 480, 280
bk_img = Image.open(IMAGE_PATH + "other/luxun.jpg")
font_path = TTF_PATH + "/msyh.ttf"
font = ImageFont.truetype(font_path, 37)
font2 = ImageFont.truetype(font_path, 30)
draw = ImageDraw.Draw(bk_img)
current_h, pad = 300, 10
for line in para:
w, h = draw.textsize(line, font=font)
draw.text(((MAX_W - w) / 2, current_h), line, font=font)
current_h += h + pad
draw.text((320, 400), "——鲁迅", font=font2, fill=(255, 255, 255))
return pic2b64(bk_img)
+29 -6
View File
@@ -9,26 +9,47 @@ from nonebot.adapters.cqhttp.exception import ActionFailed
from configs.path_config import DATA_PATH, IMAGE_PATH from configs.path_config import DATA_PATH, IMAGE_PATH
from utils.image_utils import get_img_hash from utils.image_utils import get_img_hash
from services.log import logger from services.log import logger
from configs.config import MUTE_LEVEL
import aiohttp import aiohttp
import aiofiles import aiofiles
from configs.config import MUTE_DEFAULT_COUNT, MUTE_DEFAULT_TIME, MUTE_DEFAULT_DURATION, NICKNAME from configs.config import (
MUTE_DEFAULT_COUNT,
MUTE_DEFAULT_TIME,
MUTE_DEFAULT_DURATION,
NICKNAME,
)
try: try:
import ujson as json import ujson as json
except ModuleNotFoundError: except ModuleNotFoundError:
import json import json
__plugin_name__ = "刷屏禁言"
__plugin_usage__ = "刷屏禁言检测" __zx_plugin_name__ = "刷屏禁言 [Admin]"
__plugin_usage__ = f"""
usage:
刷屏禁言相关操作,需要 {NICKNAME} 有群管理员权限
指令:
设置刷屏检测时间 [秒]
设置刷屏检测次数 [次数]
设置刷屏禁言时长 [分钟]
刷屏检测设置: 查看当前的刷屏检测设置
* 即 X 秒内发送同样消息 N 次,禁言 M 分钟 *
""".strip()
__plugin_des__ = "刷屏禁言相关操作"
__plugin_cmd__ = ["设置刷屏检测时间 [秒]", "设置刷屏检测次数 [次数]", "设置刷屏禁言时长 [分钟]", "刷屏检测设置"]
__plugin_version__ = 0.1
__plugin_author__ = "HibiKier"
__plugin_settings__ = {"admin_level": MUTE_LEVEL}
mute = on_message(priority=1, block=False) mute = on_message(priority=1, block=False)
mute_setting = on_command( mute_setting = on_command(
"mute_setting", "mute_setting",
aliases={"设置检测时间", "设置检测次数", "设置禁言时长", "刷屏检测设置"}, aliases={"设置刷屏检测时间", "设置刷屏检测次数", "设置刷屏禁言时长", "刷屏检测设置"},
permission=GROUP, permission=GROUP,
block=True, block=True,
priority=5,
) )
@@ -50,14 +71,16 @@ def save_data():
async def download_img_and_hash(url, group_id): async def download_img_and_hash(url, group_id):
try: try:
async with aiohttp.ClientSession() as session: async with aiohttp.ClientSession() as session:
async with session.get(url, proxy=get_local_proxy(), timeout=10) as response: async with session.get(
url, proxy=get_local_proxy(), timeout=10
) as response:
async with aiofiles.open( async with aiofiles.open(
IMAGE_PATH + f"temp/mute_{group_id}_img.jpg", "wb" IMAGE_PATH + f"temp/mute_{group_id}_img.jpg", "wb"
) as f: ) as f:
await f.write(await response.read()) await f.write(await response.read())
return str(get_img_hash(IMAGE_PATH + f"temp/mute_{group_id}_img.jpg")) return str(get_img_hash(IMAGE_PATH + f"temp/mute_{group_id}_img.jpg"))
except TimeoutError: except TimeoutError:
return '' return ""
mute_dict = {} mute_dict = {}
+13 -2
View File
@@ -6,8 +6,19 @@ from models.group_member_info import GroupInfoUser
from datetime import timedelta from datetime import timedelta
from models.level_user import LevelUser from models.level_user import LevelUser
__plugin_name__ = "更新群组成员列表 [Hidden]"
__plugin_usage__ = "用法:\n" "更新群员的信息" __zx_plugin_name__ = "个人信息权限查看"
__plugin_usage__ = """
usage:
个人信息权限查看
指令:
我的信息
我的权限
""".strip()
__plugin_des__ = "我们还记得你和你的权利"
__plugin_cmd__ = ["我的信息", "我的权限"]
__plugin_version__ = 0.1
__plugin_author__ = "HibiKier"
get_my_group_info = on_command("我的信息", permission=GROUP, priority=1, block=True) get_my_group_info = on_command("我的信息", permission=GROUP, priority=1, block=True)
+19 -2
View File
@@ -6,9 +6,24 @@ from services.log import logger
import ujson as json import ujson as json
import aiohttp import aiohttp
__plugin_name__ = "能不能好好说话"
__plugin_usage__ = "用法:\n nbnhhsh [文本]"
__zx_plugin_name__ = "能不能好好说话"
__plugin_usage__ = """
usage:
说人话
指令:
nbnhhsh [文本]
""".strip()
__plugin_des__ = "能不能好好说话,说人话"
__plugin_cmd__ = ["nbnhhsh [文本]"]
__plugin_version__ = 0.1
__plugin_author__ = "HibiKier"
__plugin_settings__ = {
"level": 5,
"default_status": True,
"limit_superuser": False,
"cmd": ["能不能好好说话", "nbnhhsh"],
}
HHSH_GUESS_URL = "https://lab.magiconch.com/api/nbnhhsh/guess" HHSH_GUESS_URL = "https://lab.magiconch.com/api/nbnhhsh/guess"
@@ -18,6 +33,8 @@ nbnhhsh = on_command("nbnhhsh", aliases={"能不能好好说话"}, priority=5, b
@nbnhhsh.handle() @nbnhhsh.handle()
async def _(bot: Bot, event: MessageEvent, state: T_State): async def _(bot: Bot, event: MessageEvent, state: T_State):
msg = get_message_text(event.json()) msg = get_message_text(event.json())
if not msg:
await nbnhhsh.finish('没话说就别说话!')
async with aiohttp.ClientSession( async with aiohttp.ClientSession(
headers={"content-type": "application/json"} headers={"content-type": "application/json"}
) as session: ) as session:
+12 -3
View File
@@ -11,9 +11,18 @@ from services.log import logger
from configs.config import NICKNAME from configs.config import NICKNAME
__plugin_name__ = "昵称系统" __zx_plugin_name__ = "昵称系统"
__plugin_usage__ = f"""
__plugin_usage__ = f"用法:\n以后叫我 [名称]\n{NICKNAME}我是谁" usage:
个人昵称系统,群聊 与 私聊 昵称相互独立
指令:
以后叫我 [昵称]
{NICKNAME}我是谁
""".strip()
__plugin_des__ = "区区昵称,才不想叫呢!"
__plugin_cmd__ = ["以后叫我 [昵称]", f"{NICKNAME}我是谁"]
__plugin_version__ = 0.1
__plugin_author__ = "HibiKier"
nickname = on_command( nickname = on_command(
"nickname", "nickname",
+27 -11
View File
@@ -19,16 +19,31 @@ from .trace import get_des as get_des_trace
from .yandex import get_des as get_des_yandex from .yandex import get_des as get_des_yandex
__plugin_name__ = "识图" __zx_plugin_name__ = "识图"
__plugin_usage__ = """
__plugin_usage__ = "用法:识图 [参数](默认nao) [图片]\n" "参数列表:\n" "\t1.nao\n" "\t2.asc" usage:
识别图片 [二次元图片]
指令:
识图 [图片]
""".strip()
__plugin_des__ = "以图搜图,看破本源"
__plugin_cmd__ = ["识图"]
__plugin_type__ = ("一些工具",)
__plugin_version__ = 0.1
__plugin_author__ = "synodriver"
__plugin_settings__ = {
"level": 5,
"default_status": True,
"limit_superuser": False,
"cmd": ["识图"],
}
async def get_des(url: str, mode: str, user_id: int): async def get_des(url: str, mode: str, user_id: int):
""" """
:param url: 图片链接 :param url: 图片链接
:param mode: 图源 :param mode: 图源
:return: :param user_id: 用户 id
""" """
if mode == "iqdb": if mode == "iqdb":
async for msg in get_des_iqdb(url): async for msg in get_des_iqdb(url):
@@ -57,8 +72,6 @@ setu = on_command("识图", aliases={"search"}, block=True, priority=5)
async def handle_first_receive(bot: Bot, event: MessageEvent, state: T_State): async def handle_first_receive(bot: Bot, event: MessageEvent, state: T_State):
msg = get_message_text(event.json()) msg = get_message_text(event.json())
imgs = get_message_imgs(event.json()) imgs = get_message_imgs(event.json())
if msg in ["帮助"]:
await setu.finish("示例:\n\t识图 (图片)\n\t识图asc (图片)")
if imgs: if imgs:
state["setu"] = imgs[0] state["setu"] = imgs[0]
if msg: if msg:
@@ -94,13 +107,16 @@ async def get_setu(bot: Bot, event: MessageEvent, state: T_State):
await bot.send(event=event, message="正在处理图片") await bot.send(event=event, message="正在处理图片")
idx = 1 idx = 1
async for msg in get_des(url, mod, event.user_id): async for msg in get_des(url, mod, event.user_id):
await bot.send(event=event, message=msg) if msg:
if idx == MAX_FIND_IMG_COUNT: await bot.send(event=event, message=msg)
break if idx == MAX_FIND_IMG_COUNT:
idx += 1 break
idx += 1
if id == 1:
await bot.send(event=event, message='没找着.')
logger.info( logger.info(
f"(USER {event.user_id}, GROUP " f"(USER {event.user_id}, GROUP "
f"{event.group_id if event.message_type != 'private' else 'private'}) 识图:{url}" f"{event.group_id if isinstance(event, GroupMessageEvent) else 'private'}) 识图:{url}"
) )
# image_data: List[Tuple] = await get_pic_from_url(url) # image_data: List[Tuple] = await get_pic_from_url(url)
# await setu.finish("hso") # await setu.finish("hso")
@@ -93,12 +93,13 @@ async def get_des(url: str, user_id: int):
return return
for pic in image_data: for pic in image_data:
# print(pic) # print(pic)
msg = await download_img(pic[0], user_id) \ if int(str(pic[1]).split('.')[0]) > 80:
+ f"\n相似度:{pic[1]}" \ msg = await download_img(pic[0], user_id) \
f"\n标题:{pic[2] if (str(pic[2]).strip() != 'Creator:' and len(str(pic[2]).split('-')) < 3) else '未知'}" \ + f"\n相似度:{pic[1]}" \
f"\nPID:{pic[3]}" \ f"\n标题:{pic[2] if (str(pic[2]).strip() != 'Creator:' and len(str(pic[2]).split('-')) < 3) else '未知'}" \
f"\nmember:{pic[4]}\n" f"\nPID:{pic[3]}" \
yield msg f"\nmember:{pic[4]}\n"
yield msg
pass pass
@@ -1,85 +0,0 @@
from typing import Any, Dict
from nonebot import get_driver, on_command
from nonebot.adapters.cqhttp import Bot, Event, GroupMessageEvent, PrivateMessageEvent
from nonebot.rule import to_me
from nonebot.typing import T_State, T_CalledAPIHook
from .config import Config
global_config = get_driver().config
withdraw_config = Config(**global_config.dict())
msg_ids = {}
max_size = withdraw_config.withdraw_max_size
__plugin_name__ = "撤回"
__plugin_usage__ = (
"用法:撤回 [消息位置](默认0)\n" "示例:\n" "\t撤回0 -> 撤回倒数第一条消息(即最新发送的消息)" "\t撤回1 -> 撤回倒数第2条消息"
)
def get_key(msg_type, id):
return f"{msg_type}_{id}"
async def save_msg_id(
bot: Bot, e: Exception, api: str, data: Dict[str, Any], result: Any
) -> T_CalledAPIHook:
try:
if api == "send_msg":
msg_type = data["message_type"]
id = data["group_id"] if msg_type == "group" else data["user_id"]
elif api == "send_private_msg":
msg_type = "private"
id = data["user_id"]
elif api == "send_group_msg":
msg_type = "group"
id = data["group_id"]
else:
return
key = get_key(msg_type, id)
msg_id = result["message_id"]
if key not in msg_ids:
msg_ids[key] = []
msg_ids[key].append(msg_id)
if len(msg_ids) > max_size:
msg_ids[key].pop(0)
except:
pass
Bot._called_api_hook.add(save_msg_id)
withdraw = on_command("withdraw", aliases={"撤回"}, rule=to_me(), priority=1, block=True)
@withdraw.handle()
async def _(bot: Bot, event: Event, state: T_State):
if isinstance(event, GroupMessageEvent):
msg_type = "group"
id = event.group_id
elif isinstance(event, PrivateMessageEvent):
msg_type = "private"
id = event.user_id
else:
return
key = get_key(msg_type, id)
num = event.get_plaintext().strip()
if not num:
num = 0
elif num.isdigit() and 0 <= int(num) < len(msg_ids[key]):
num = int(num)
else:
return
try:
idx = -num - 1
await bot.delete_msg(message_id=msg_ids[key][idx])
msg_ids[key].pop(idx)
except:
await withdraw.finish("撤回失败,可能已超时")
@@ -1,8 +0,0 @@
from pydantic import BaseSettings
class Config(BaseSettings):
withdraw_max_size: int = 50
class Config:
extra = 'ignore'
+36 -13
View File
@@ -1,31 +1,50 @@
import aiohttp import aiohttp
from utils.user_agent import get_user_agent
from io import BytesIO from io import BytesIO
from random import choice from random import choice
from nonebot import on_regex from nonebot import on_regex
from nonebot.typing import T_State from nonebot.typing import T_State
from nonebot.adapters.cqhttp import Bot, GroupMessageEvent from nonebot.adapters.cqhttp import Bot, GroupMessageEvent
from utils.utils import get_message_text, get_local_proxy, get_message_at from utils.utils import get_message_text, get_message_at
from utils.message_builder import image from utils.message_builder import image
import re import re
from utils.image_utils import CreateImg from utils.image_utils import CreateImg
from asyncio.exceptions import TimeoutError
__plugin_name__ = "我有一个朋友" __zx_plugin_name__ = "我有一个朋友"
__plugin_usage__ = """
__plugin_usage__ = "用法:我有一个朋友说/问 [消息] [at](不艾特则群员随机)" usage:
我有一个朋友他...,不知道是不是你
指令:
我有一个朋友想问问 [文本] ?[at]: 当at时你的朋友就是艾特对象
""".strip()
__plugin_des__ = "我有一个朋友想问问..."
__plugin_cmd__ = ["我有一个朋友想问问[文本] ?[at]"]
__plugin_version__ = 0.1
__plugin_author__ = "HibiKier"
__plugin_settings__ = {
"level": 5,
"default_status": True,
"limit_superuser": False,
"cmd": ["我有一个朋友想问问", "我有一个朋友"],
}
one_friend = on_regex( one_friend = on_regex(
"^我.*?朋友.*?(想问问|说|让我问问|想问|让我问|想知道|让我帮他问问|让我帮他问|让我帮忙问|让我帮忙问问|问).*", "^我.*?朋友.*?(想问问|说|让我问问|想问|让我问|想知道|让我帮他问问|让我帮他问|让我帮忙问|让我帮忙问问|问).*",
priority=5, priority=4,
block=True, block=True,
) )
async def get_pic(qq): async def get_pic(qq):
url = f"http://q1.qlogo.cn/g?b=qq&nk={qq}&s=100" url = f"http://q1.qlogo.cn/g?b=qq&nk={qq}&s=100"
async with aiohttp.ClientSession(headers=get_user_agent()) as session: async with aiohttp.ClientSession() as session:
async with session.get(url, proxy=get_local_proxy(), timeout=5) as response: for _ in range(3):
return await response.read() try:
async with session.get(url, timeout=5) as response:
return await response.read()
except TimeoutError:
pass
return None
@one_friend.handle() @one_friend.handle()
@@ -41,11 +60,11 @@ async def _(bot: Bot, event: GroupMessageEvent, state: T_State):
) )
] ]
) )
user_name = '朋友' user_name = "朋友"
else: else:
qq = qq[0] qq = qq[0]
at_user = await bot.get_group_member_info(group_id=event.group_id, user_id=qq) at_user = await bot.get_group_member_info(group_id=event.group_id, user_id=qq)
user_name = at_user['card'] if at_user['card'] else at_user['nickname'] user_name = at_user["card"] if at_user["card"] else at_user["nickname"]
msg = re.search( msg = re.search(
r"^我.*?朋友.*?(想问问|说|让我问问|想问|让我问|想知道|让我帮他问问|让我帮他问|让我帮忙问|让我帮忙问问|问)(.*)", msg r"^我.*?朋友.*?(想问问|说|让我问问|想问|让我问|想知道|让我帮他问问|让我帮他问|让我帮忙问|让我帮忙问问|问)(.*)", msg
) )
@@ -53,13 +72,17 @@ async def _(bot: Bot, event: GroupMessageEvent, state: T_State):
if not msg: if not msg:
msg = "都不知道问什么" msg = "都不知道问什么"
msg = msg.replace("他", "我").replace("她", "我").replace("它", "我") msg = msg.replace("他", "我").replace("她", "我").replace("它", "我")
ava = CreateImg(100, 100, background=BytesIO(await get_pic(qq))) x = await get_pic(qq)
if x:
ava = CreateImg(100, 100, background=BytesIO(await get_pic(qq)))
else:
ava = CreateImg(100, 100, color=(0, 0, 0))
ava.circle() ava.circle()
text = CreateImg(300, 30, font_size=30) text = CreateImg(300, 30, font_size=30)
text.text((0, 0), user_name) text.text((0, 0), user_name)
A = CreateImg(700, 150, font_size=25, color="white") A = CreateImg(700, 150, font_size=25, color="white")
A.paste(ava, (30, 25), True) A.paste(ava, (30, 25), True)
A.paste(text, (150, 40)) A.paste(text, (150, 38))
A.text((150, 85), msg, (125, 125, 125)) A.text((150, 85), msg, (125, 125, 125))
await one_friend.send(image(b64=A.pic2bs4())) await one_friend.send(image(b64=A.pic2bs4()))
+61 -30
View File
@@ -1,5 +1,7 @@
from typing import Type
from nonebot import on_command from nonebot import on_command
from utils.utils import FreqLimiter, scheduler, get_message_text, is_number from nonebot.matcher import Matcher
from utils.utils import scheduler, get_message_text, is_number
from nonebot.adapters.cqhttp.permission import GROUP from nonebot.adapters.cqhttp.permission import GROUP
from nonebot.typing import T_State from nonebot.typing import T_State
from nonebot.adapters.cqhttp import Bot, GroupMessageEvent, MessageEvent from nonebot.adapters.cqhttp import Bot, GroupMessageEvent, MessageEvent
@@ -15,25 +17,57 @@ from .open_cases_c import (
open_shilian_case, open_shilian_case,
) )
from .utils import util_get_buff_price, util_get_buff_img, update_count_daily from .utils import util_get_buff_price, util_get_buff_img, update_count_daily
from configs.config import NICKNAME
__plugin_name__ = "开箱" __zx_plugin_name__ = "开箱"
__plugin_usage__ = ( __plugin_usage__ = """
"用法:\n" usage:
"看看你的人品罢了\n" 看看你的人品罢了
"目前只支持\n\t" 模拟开箱,完美公布的真实概率,只想看看替你省了多少钱
"1.狂牙大行动武器箱\n\t" 指令:
"2.突围大行动武器箱\n\t" 开箱 ?[武器箱]
"3.命悬一线武器箱\n\t" [1-30]连开箱 ?[武器箱]
"4.裂空武器箱\n\t" 我的开箱
"5.光谱武器箱\n" 我的金色
f"示例:{NICKNAME}开箱 突围大行动(不输入指定武器箱则随机)\n" 群开箱统计
"示例:我的开箱(开箱统计)\n" * 不包含[武器箱]时随机开箱 *
"示例:群开箱统计\n" 目前支持的武器箱:
"示例:我的金色" 1.狂牙大行动武器箱
) 2.突围大行动武器箱
3.命悬一线武器箱
_flmt = FreqLimiter(3) 4.裂空武器箱
5.光谱武器箱
示例:开箱 命悬一线
""".strip()
__plugin_superuser_usage__ = """
usage:
更新皮肤指令
指令:
更新开箱图片 ?[武器箱]
更新开箱价格 ?[武器箱]
* 不指定武器箱时则全部更新 *
* 过多的爬取会导致账号API被封 *
""".strip()
__plugin_des__ = "csgo模拟开箱[戒赌]"
__plugin_cmd__ = [
"开箱 ?[武器箱]",
"[1-30]连开箱 ?[武器箱]",
"我的开箱",
"我的金色",
"群开箱统计",
"更新开箱图片 ?[武器箱] [_superuser]",
"更新开箱价格 ?[武器箱] [_superuser]",
]
__plugin_type__ = ("抽卡相关", 1)
__plugin_version__ = 0.1
__plugin_author__ = "HibiKier"
__plugin_settings__ = {
"level": 5,
"default_status": True,
"limit_superuser": False,
"cmd": ["csgo开箱", "开箱"],
}
__plugin_task__ = {"open_case_reset_remind": "每日开箱重置提醒"}
__plugin_cd_limit__ = {"rst": "着什么急啊,慢慢来!"}
cases_name = ["狂牙大行动", "突围大行动", "命悬一线", "裂空", "光谱"] cases_name = ["狂牙大行动", "突围大行动", "命悬一线", "裂空", "光谱"]
@@ -45,12 +79,8 @@ k_open_case = cases_matcher_group.on_command("开箱")
@k_open_case.handle() @k_open_case.handle()
async def _(bot: Bot, event: GroupMessageEvent, state: T_State): async def _(bot: Bot, event: GroupMessageEvent, state: T_State):
if str(event.get_message()).strip() in ["帮助"]:
await k_open_case.finish(__plugin_usage__)
if not _flmt.check(event.user_id):
await k_open_case.finish("着什么急啊,慢慢来!", at_sender=True)
_flmt.start_cd(event.user_id)
case_name = get_message_text(event.json()) case_name = get_message_text(event.json())
case_name = case_name.replace("武器箱", "").strip()
if case_name: if case_name:
result = await open_case(event.user_id, event.group_id, case_name) result = await open_case(event.user_id, event.group_id, case_name)
else: else:
@@ -91,14 +121,14 @@ async def _(bot: Bot, event: GroupMessageEvent, state: T_State):
) )
open_shilian = cases_matcher_group.on_regex(".*连开箱") open_shilian: Type[Matcher] = cases_matcher_group.on_regex(".*连开箱")
@open_shilian.handle() @open_shilian.handle()
async def _(bot: Bot, event: GroupMessageEvent, state: T_State): async def _(bot: Bot, event: GroupMessageEvent, state: T_State):
# if not _flmt.check(event.user_id): # if not _flmt.check(event.user_id):
# await k_open_case.finish('着什么急啊,慢慢来!', at_sender=True) # await k_open_case.finish('着什么急啊,慢慢来!', at_sender=True)
_flmt.start_cd(event.user_id) # _flmt.start_cd(event.user_id)
msg = get_message_text(event.json()) msg = get_message_text(event.json())
rs = re.search(r"(.*)连开箱(.*)", msg) rs = re.search(r"(.*)连开箱(.*)", msg)
if rs: if rs:
@@ -110,11 +140,12 @@ async def _(bot: Bot, event: GroupMessageEvent, state: T_State):
num = int(num) num = int(num)
if num > 30: if num > 30:
await open_shilian.finish("开箱次数不要超过30啊笨蛋!", at_sender=True) await open_shilian.finish("开箱次数不要超过30啊笨蛋!", at_sender=True)
if num < 0:
await open_shilian.finish("再负开箱就扣你明天开箱数了!", at_sender=True)
else: else:
await open_shilian.finish("必须要是数字切不要超过30啊笨蛋!中文也可!", at_sender=True) await open_shilian.finish("必须要是数字切不要超过30啊笨蛋!中文也可!", at_sender=True)
case_name = rs.group(2).strip() case_name = rs.group(2).strip()
if case_name.find("武器箱") != -1: case_name = case_name.replace("武器箱", "").strip()
case_name = case_name.replace("武器箱", "").strip()
if not case_name: if not case_name:
case_name = random.choice(cases_name) case_name = random.choice(cases_name)
elif case_name not in cases_name: elif case_name not in cases_name:
@@ -159,7 +190,7 @@ num_dict = {
} }
update_price = on_command("更新价格", priority=1, permission=SUPERUSER, block=True) update_price = on_command("更新开箱价格", priority=1, permission=SUPERUSER, block=True)
@update_price.handle() @update_price.handle()
@@ -167,7 +198,7 @@ async def _(bot: Bot, event: MessageEvent, state: T_State):
await update_price.send(await util_get_buff_price(str(event.get_message()))) await update_price.send(await util_get_buff_price(str(event.get_message())))
update_img = on_command("更新图片", priority=1, permission=SUPERUSER, block=True) update_img = on_command("更新开箱图片", priority=1, permission=SUPERUSER, block=True)
@update_img.handle() @update_img.handle()

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