Compare commits

...
79 Commits
Author SHA1 Message Date
HibiKier 5ae41e1d38 Update __init__.py 2022-01-16 18:37:14 +08:00
HibiKier 8022ae1fb1 Update README.md 2022-01-16 15:20:48 +08:00
HibiKier 0163fc1d88 Update draw_image.py 2022-01-16 15:16:19 +08:00
HibiKier 5d9ab40e35 Update __version__ 2022-01-16 15:00:16 +08:00
HibiKier b1ca7930bb Update README.md 2022-01-16 14:54:31 +08:00
HibiKier 40111261ed Update README.md 2022-01-16 14:54:16 +08:00
HibiKier 4cb91b47e6 update v0.0.9.0 2022-01-16 14:52:50 +08:00
HibiKier 30b56583bd update v0.0.8.0 2022-01-05 22:32:59 +08:00
HibiKier 5e5168f804 fix genshin 2021-12-26 22:14:01 +08:00
HibiKier 4a8122829e Update README.md 2021-12-24 10:23:35 +08:00
HibiKier 52ba2cf89a Merge pull request #99 from yzyyz1387/fix_zx
支持国际疫情数据查询
2021-12-24 10:22:45 +08:00
HibiKier 58957cdf44 Update other_than.py 2021-12-24 10:20:57 +08:00
HibiKier 71f860a500 Update other_than.py 2021-12-24 10:10:48 +08:00
HibiKier 9b4cde819e Update __init__.py 2021-12-24 10:09:53 +08:00
yzyyz1387 99637de1de International information inquiry is supported 2021-12-24 04:08:07 +08:00
yzyyz1387 19dbd3f2b0 International information inquiry is supported 2021-12-24 04:06:56 +08:00
yzyyz1387 7567794f74 International information inquiry is supported 2021-12-24 04:05:44 +08:00
yzyyz1387 df0d15c1b3 International information inquiry is supported 2021-12-24 04:03:26 +08:00
HibiKier 9a84c02bb5 Merge pull request #97 from BalconyJH/patch-1
Update __version__
2021-12-22 12:07:53 +08:00
BalconyJH e623c4f30f Update __version__ 2021-12-21 22:52:45 +08:00
HibiKier 3fd7acffb2 Update README.md 2021-12-20 20:41:54 +08:00
HibiKier 71f8c16c26 Merge pull request #96 from noahzark/patch-1
只有当真的发布新动态/视频的时候才获取并推送
2021-12-20 20:40:42 +08:00
Feliciano Long b64699c4ab 只有当真的发布新动态/视频的时候才获取并推送
1. 当up删除动态的时候,老动态会变成新的,此时不应该重复推送老动态
2. 2021-12-20 b站服务器不稳定,动态列表刷新混乱,造成重复推送
2021-12-20 17:48:05 +08:00
HibiKier 786d62bb2f Update README.md 2021-12-16 20:52:55 +08:00
HibiKier ac48d3d542 Update README.md 2021-12-16 19:23:30 +08:00
HibiKier c4d9523ec2 Update set_admin_permissions.py 2021-12-16 19:22:46 +08:00
HibiKier cb6549c297 Update prts_handle.py 2021-12-16 11:31:15 +08:00
HibiKier 29816ada2a Update README.md 2021-12-16 11:29:12 +08:00
HibiKier dd12bdc028 Update README.md 2021-12-16 11:17:27 +08:00
HibiKier 2a52c0711b Update README.md 2021-12-16 11:17:04 +08:00
HibiKier 2653f67193 update 0.0.7.0 2021-12-16 11:16:28 +08:00
HibiKier b892617824 Update group_user_checkin.py 2021-12-07 12:20:14 +08:00
HibiKier a6fb05c91d Update README.md 2021-12-01 16:25:44 +08:00
HibiKier ccfb9403c8 Update update_info.json 2021-12-01 16:21:58 +08:00
HibiKier 66fa194c7f Update data_source.py 2021-12-01 16:21:29 +08:00
HibiKier 65d8fae9ca Update http_utils.py 2021-12-01 15:20:32 +08:00
HibiKier 58acb14859 Update requirements.txt 2021-12-01 14:53:45 +08:00
HibiKier b63c05a509 Update requirements.txt 2021-12-01 14:17:11 +08:00
HibiKi 2a1a2ee8c5 update 0.0.6.5 2021-12-01 14:03:34 +08:00
HibiKier 8c4b515093 Update update_info.json 2021-12-01 10:53:15 +08:00
HibiKier 6d43989a4d Update update_info.json 2021-12-01 10:52:28 +08:00
HibiKier fb1f15bcb4 Update update_game_simple_info.py 2021-11-29 21:30:06 +08:00
HibiKier 81ad585a18 Update update_game_requests_info.py 2021-11-29 21:29:48 +08:00
HibiKier eb3bd2b4a6 Update update_game_info.py 2021-11-29 21:29:29 +08:00
HibiKier e401914790 Update README.md 2021-11-29 13:11:34 +08:00
HibiKier fef5dadaba Update __version__ 2021-11-29 13:10:43 +08:00
HibiKi c1348d08ed update 0.0.6.4 2021-11-29 13:09:47 +08:00
HibiKier fbd7c568d9 Update pix_update.py 2021-11-24 18:26:52 +08:00
HibiKier 2941774913 Update __init__.py 2021-11-24 18:26:38 +08:00
HibiKier b96d910401 Update __version__ 2021-11-24 14:40:53 +08:00
HibiKier 9275d0782b Update README.md 2021-11-24 14:40:39 +08:00
HibiKier 7acc6cf6af Update data_source.py 2021-11-24 14:33:10 +08:00
HibiKier d380a9aacc Update data_source.py 2021-11-24 14:32:46 +08:00
HibiKier 495377da13 Update README.md 2021-11-24 10:25:00 +08:00
HibiKier 218eed4068 Add files via upload 2021-11-24 10:24:40 +08:00
HibiKier 1e9c99dec1 Delete plugins/search_image directory 2021-11-24 10:07:47 +08:00
HibiKier 0326bc2831 Update __init__.py 2021-11-24 09:56:50 +08:00
HibiKier aed770a761 Update README.md 2021-11-23 21:48:11 +08:00
HibiKi 3de487fb24 update 0.0.6.2 2021-11-23 21:44:59 +08:00
HibiKier c802dfda22 Update README.md 2021-11-19 15:47:10 +08:00
HibiKier 3a4378e4c6 Update __init__.py 2021-11-18 14:41:54 +08:00
HibiKier 955a1f8a7f Update __init__.py 2021-11-18 14:41:13 +08:00
HibiKier ab69e5c477 Create README.md 2021-11-17 11:34:40 +08:00
HibiKier 55e42e8334 Update README.md 2021-11-17 11:33:26 +08:00
HibiKier a0f59bc2c2 Update README.md 2021-11-14 17:27:26 +08:00
HibiKier 66dabd0c8a Update plugins_manager.py 2021-11-14 17:25:45 +08:00
HibiKier 915d585e3a Merge pull request #77 from AkashiCoin/main
修复有免费游戏时不能获取免费游戏的问题
2021-11-13 16:12:49 +08:00
HibiKier 979080d281 Merge branch 'main' into main 2021-11-13 16:12:41 +08:00
HibiKier 335f76c532 Update data_source.py 2021-11-12 16:13:16 +08:00
AkashiCoin b50b934a12 Update data_source.py 2021-11-10 22:26:57 +08:00
AkashiCoin 6a1684d277 Update __init__.py 2021-11-10 22:26:29 +08:00
HibiKier 4926e4d69c Update pix_update.py 2021-11-10 21:33:10 +08:00
HibiKier b29a26551a Update README.md 2021-11-10 21:32:54 +08:00
HibiKier d1c1d93ab7 fix 2021-11-10 21:27:49 +08:00
HibiKier 907df4253c Update __version__ 2021-11-10 20:06:44 +08:00
HibiKier 658152fdc9 Update jitang.py 2021-11-10 16:45:19 +08:00
HibiKier 1a68ede632 Update README.md 2021-11-09 18:05:36 +08:00
HibiKier b70851b810 Update __init__.py 2021-11-09 18:04:33 +08:00
HibiKier 17506691a8 Update __init__.py 2021-11-07 20:45:34 +08:00
338 changed files with 18794 additions and 15517 deletions
+148
View File
@@ -0,0 +1,148 @@
### Python template
# Byte-compiled / optimized / DLL files
__pycache__/
*.py[cod]
*$py.class
# C extensions
*.so
# Distribution / packaging
.Python
build/
develop-eggs/
dist/
downloads/
eggs/
.eggs/
lib/
lib64/
parts/
sdist/
var/
wheels/
share/python-wheels/
*.egg-info/
.installed.cfg
*.egg
MANIFEST
# PyInstaller
# Usually these files are written by a python script from a template
# before PyInstaller builds the exe, so as to inject date/other infos into it.
*.manifest
*.spec
# Installer logs
pip-log.txt
pip-delete-this-directory.txt
# Unit test / coverage reports
htmlcov/
.tox/
.nox/
.coverage
.coverage.*
.cache
nosetests.xml
coverage.xml
*.cover
*.py,cover
.hypothesis/
.pytest_cache/
cover/
# Translations
*.mo
*.pot
# Django stuff:
*.log
local_settings.py
db.sqlite3
db.sqlite3-journal
# Flask stuff:
instance/
.webassets-cache
# Scrapy stuff:
.scrapy
# Sphinx documentation
docs/_build/
# PyBuilder
.pybuilder/
target/
# Jupyter Notebook
.ipynb_checkpoints
# IPython
profile_default/
ipython_config.py
# pyenv
# For a library or package, you might want to ignore these files since the code is
# intended to run in multiple environments; otherwise, check them in:
# .python-version
# pipenv
# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
# However, in case of collaboration, if having platform-specific dependencies or dependencies
# having no cross-platform support, pipenv may install dependencies that don't work, or not
# install all needed dependencies.
#Pipfile.lock
# PEP 582; used by e.g. github.com/David-OConnor/pyflow
__pypackages__/
# Celery stuff
celerybeat-schedule
celerybeat.pid
# SageMath parsed files
*.sage.py
# Environments
.env
.venv
env/
venv/
ENV/
env.bak/
venv.bak/
# Spyder project settings
.spyderproject
.spyproject
# Rope project settings
.ropeproject
# mkdocs documentation
/site
# mypy
.mypy_cache/
.dmypy.json
dmypy.json
# Pyre type checker
.pyre/
# pytype static type analyzer
.pytype/
# Cython debug symbols
cython_debug/
test.py
server.py
member_activity_handle.py
Yu-Gi-Oh/
search_image/
black_word/
csgo/
fantasy_card/
+108 -420
View File
@@ -1,4 +1,4 @@
<div align=center><img width="320" height="320" src="https://raw.githubusercontent.com/HibiKier/zhenxun_bot/main/docs/zhenxun.jpg"/></div> <div align=center><img width="320" height="320" src="https://raw.githubusercontent.com/HibiKier/zhenxun_bot/main/logo.png"/></div>
![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)
@@ -20,11 +20,26 @@
## 未完成的文档 ## 未完成的文档
[传送门](https://hibikier.github.io/zhenxun_bot/) # [传送门](https://hibikier.github.io/zhenxun_bot/)
## 真寻的帮助 ## 真寻的帮助
请对真寻说: '真寻帮助' or '管理员帮助' or '超级用户帮助' or '真寻帮助 指令' 请对真寻说: '真寻帮助' or '管理员帮助' or '超级用户帮助' or '真寻帮助 指令'
## 普通帮助图片
![x](https://github.com/HibiKier/zhenxun_bot/blob/0.0.8.2/docs_image/3238573864-836268675-E2FFBB2AC143EAF4DDDF150438508721.png)
## 提供符合真寻标准的插件仓库
[AkashiCoin/nonebot_plugins_zhenxun_bot](https://github.com/AkashiCoin/nonebot_plugins_zhenxun_bot)
## 来点优点?
一.作为bot:
* 实现了许多功能,且提供了大量功能管理命令
* 通过Config配置项将所有插件配置统计保存至config.yaml,利于统一用户修改
* 方便增删插件,原生nonebot2 matcher,不需要额外修改,仅仅通过简单的配置属性就可以生成`帮助图片`和`帮助信息`
* 提供了cd,阻塞,每日次数等限制,仅仅通过简单的属性就可以生成一个限制,例如:`__plugin_cd_limit__`
* __..... 更多详细请通过`传送门`查看文档!__
## 功能列表 ## 功能列表
<details> <details>
@@ -47,9 +62,11 @@
- [x] 商店/我的金币/购买道具/使用道具 - [x] 商店/我的金币/购买道具/使用道具
- [x] 8种手游抽卡 (查看 [nonebot_plugin_gamedraw](https://github.com/HibiKier/nonebot_plugin_gamedraw)) - [x] 8种手游抽卡 (查看 [nonebot_plugin_gamedraw](https://github.com/HibiKier/nonebot_plugin_gamedraw))
- [x] 我有一个朋友想问问..(借鉴pcrbot插件) - [x] 我有一个朋友想问问..(借鉴pcrbot插件)
- [x] 原神黄历 (使用[Genshin_Impact_bot](https://github.com/H-K-Y/Genshin_Impact_bot)插件) - [x] 原神黄历
- [x] 原神今日素材 - [x] 原神今日素材
- [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] 微博热搜 - [x] 微博热搜
- [x] B站主播/UP/番剧订阅 - [x] B站主播/UP/番剧订阅
@@ -85,6 +102,8 @@
- [x] 通过PID获取图片 - [x] 通过PID获取图片
- [x] 功能统计可视化 - [x] 功能统计可视化
- [x] 关于
### 已实现的管理员功能 ### 已实现的管理员功能
- [x] 更新群组成员信息 - [x] 更新群组成员信息
- [x] 95%的群功能开关 - [x] 95%的群功能开关
@@ -96,6 +115,8 @@
- [x] 移动图片 (同上) - [x] 移动图片 (同上)
- [x] 删除图片 (同上) - [x] 删除图片 (同上)
- [x] 群内B站订阅 - [x] 群内B站订阅
- [x] 群词条
- [x] 休息吧/醒来
### 已实现的超级用户功能 ### 已实现的超级用户功能
- [x] 添加/删除权限(是真寻的管理员权限,不是群管理员) - [x] 添加/删除权限(是真寻的管理员权限,不是群管理员)
@@ -121,6 +142,11 @@
- [x] 功能开关(更多设置) - [x] 功能开关(更多设置)
- [x] 功能状态 - [x] 功能状态
- [x] b了 - [x] b了
- [x] 执行sql
- [x] 重载配置
- [x] 清理临时数据
- [x] 增删群认证
- [x] 同意/拒绝好友/群聊请求
#### 超级用户的被动技能 #### 超级用户的被动技能
- [x] 邀请入群提醒(别人邀请真寻入群) - [x] 邀请入群提醒(别人邀请真寻入群)
@@ -147,129 +173,10 @@
- [x] 定时更新权限 - [x] 定时更新权限
</details> </details>
## 功能具体指令 ## 详细配置请前往文档,以下为最简部署和配置,如果你有基础并学习过nonebot2的话
<details>
<summary>功能具体指令说明</summary>
### 常用功能
** [*]:表示该参数可有多个 [?]:表示参数可选 **
| 功能 | 指令 | 说明
| ----------------------| :--------------------------------------:| :------------------------:
| 签到 | 签到/我的签到/好感度排行/好感度总榜/好感度总榜\[显示我/屏蔽我] | 普通的签到插件,可以获得好感度和金币<br>好感度影响开箱次数和涩图触发概率,金币用于购买道具,俄罗斯轮盘赌注以及金币红包<br>好感度总榜,显示所有群的群员好感度排行,可通过命令好感度总榜\[显示我/屏蔽我] 来设置是否隐藏
| 发送图片 | 美图/壁纸/萝莉 \[id]?(默认随机)/\[num]张图\[keyword] | 发送指定文件夹下的图片<br>示例:萝莉->发送img文件夹下luoli文件夹下的图片<br>在线搜索一些不色的图,示例:3张图米浴
| 色图 | 色图/色图xx/n张色图/n张xx的色图/查色图(查询本地色图信息)/色图r【n<10】 | 色图r返回10张r18色图(仅私聊),并限制每日次数(默认5次)<br>其他示例:色图 真寻 <br>5张真寻的色图
| 黑白草图 | 黑白草图/黑白图 \[文字] \[图片] | 整活生成器,示例:黑白图 我喜欢真寻 \[图片]
| coser | coser/cos/括丝 | coser图片,说实话挺失望的,太色了
| 骂我 | 骂我 | 就是发送钉宫的语音罢了
| 戳一戳 | 戳一戳 | 随机发送钉宫语音 or 美图 or 萝莉图 or 文本
| 模拟开箱 | 开箱 \[武器箱名称](默认随机)/N连开箱 \[武器箱名称](默认随机)/我的开箱/群开箱统计/我的金色 | 当不指定武器箱时默认随机,此功能需要先在/open_cases/config.py中编写指定武器箱数据,然后提前爬取价格,使用超级用户命令更新cookie后,再使用命令更新xx武器箱【注:未设置爬取频率,可能会被禁用api(请谨慎!!用小号!!)】
| 鲁迅说过 | 鲁迅说过 \[文本] | 示例:鲁迅说过 真寻世界第一可爱
| 假消息 | 假消息 \[网址] \[标题] \[内容]? \[图片]? | 构造虚假的分享消息
| 商店系统 | 商店/我的金币/购买道具 \[名称或序号] \[数量](默认1)/使用道具 \[名称或序号] | 示例:<br>购买道具 1 3<br>购买道具 好感度双倍加持卡Ⅰ 3<br>使用道具 1<br>使用道具 好感度双倍加持卡Ⅰ
| 抽卡系统 | 原神/明日方舟/赛马娘/坎公骑冠剑/碧蓝航线/阴阳师/公主连结(pcr)/FGO N抽/一井 | 详细帮助请查看: [nonebot_plugin_gamedraw](https://github.com/HibiKier/nonebot_plugin_gamedraw) <br>示例:原神90抽
| 我有一个朋友 | 我有一个朋友他说/想问问/\[文本] | 会将文本中的(他,她,它)替换成 '我'<br>示例:我有一个朋友想问问他喜不喜欢真寻
| 昵称系统 | 以后叫我\[昵称]/以后请叫我\[昵称]/我是谁/我叫什么 | 此昵称会替换与真寻聊天中 '你' 的名称(群名片),群与群与私聊的昵称相互独立
| 原神黄历 | 原神黄历 | 查看今日原神黄历,含有每日10:25的定时任务
| 原神材料 | 今日素材 | 发送可莉特调的截图
| 丘丘语翻译 | 丘丘翻译/丘丘一下/丘丘语翻译 | 示例:丘丘一下 mimi
| 原神资源查询 | 原神资源查询 \[资源名称] \[路线]?/\[资源名称]在哪/哪里有\[资源名称]/原神资源列表 | 如果资源名称末尾添加‘路线’的话将生成残缺缺缺版的优先路径<br>示例:嘟嘟莲在哪<br>原神资源查询嘟嘟莲路线
| 俄罗斯轮盘 | 装弹\[子弹数] \[金额](默认200)/开枪/结算/我的战绩/胜场排行/败场排行/欧洲人排行/慈善家排行 | 紧张刺激的群内小游戏,使用每日签到的金币作为赌注,具体玩法请发送 真寻帮助 俄罗斯轮盘
| 红包系统 | 塞红包\[金额] \[数量](默认5)/抢/开/戳一戳/退回 | 仿微信明日方舟红包的样式(pil拼图大师!),每个红包金额随机生成,最多会是红包总金额的1/3,退回用于退回一分钟后还未开完的红包
| 金币排行 | 金币排行 | 字面意思
|网易云热评 | 到点了/12点了/网易云热评/网易云评论 | 防下塔
| 古诗 | 念诗/念首诗/来首诗 | 突然文艺起来了
|微博热搜 | 微博热搜/微博热搜[序号]? | 快捷热搜查询方式
| pil对图片的操作 | 修改尺寸/等比压缩/旋转图片/水平翻转/铅笔滤镜/模糊效果/锐化效果/高斯模糊/边缘检测/底色替换 | 选项较多,请直接发送 真寻图片帮助
| BUFF皮肤底价查询 | 查询皮肤 \[武器名称] \[皮肤名称] | 网络不友好的话会经常超时<br>示例:查询皮肤 沙漠之鹰 印花集
| 天气查询 | \[城市]天气 | 非常常见的插件,第一个入门插件
| 疫情查询 | 疫情/查询疫情 \[城市名或省份名] | 示例:疫情杭州
| bt磁力搜索 | bt \[关键词] \[页数]?(默认第1页) | 该功能仅仅提供给私聊,因为可以搜到一些色色的东西,示例:bt钢铁侠 5
| 上车 | 略 | 直接查看真寻帮助 上车,每日限制次数(默认5)
| 以图识番 | 识番 \[图片] | 以图搜翻,图片越清晰越完整正确率越高
| 以图搜图 | 识图 (asc)? \[图片] | 参数asc更换搜索引擎为ascii2d,默认为saucenao
| 点歌 | 点歌 \[歌名] | 网易云点歌小助手
| 搜番 | 搜番 \[关键字] | 群聊只返回5个结果,私聊返回20个结果
| epic白嫖游戏通知 | epic | 通知你又到了白嫖游戏的时候,可以不玩,不能没有
| P站排行榜 | p站排行 \[排行类型参数]? \[数量]? \[日期]?| 9种不同排行榜,r18类型仅可私聊,通过参数选择,查看真寻帮助p站排行<br>示例:p站排行榜 1 9 2018-4-25
| 搜图 | 搜图 \[关键词] \[数量]? \[排序方式]? \[r18]?| r18仅可私聊,查看真寻帮助搜图
| 通过PID搜索图片 | p搜 [pid] | 在群内使用此功能会在30秒内撤回
| 翻译 | 英翻/日翻/韩翻/翻韩/翻日/翻英 \[文本] | 三种语言互相翻译
| 获取b站视频封面 | b封面 [链接/av/bv/cv/直播id] | 快捷的封面获取方式
| 群欢迎消息 | 群欢迎消息/查看群欢迎消息/查看当前群欢迎消息 | 查看给真寻设置的群欢迎消息
| 自我介绍 | 自我介绍 | 没错,一份正经的真寻自我介绍
| 我的权限 | 我的权限 | 真寻内部定义的一套权限系统
| 我的信息 | 我的信息 | 唯一的作用就是看看什么时候加入群
| 撤回 | 撤回 \[消息位置]?(默认为最新一条消息) | 按顺序撤回发送的消息,示例:撤回 1
| 滴滴滴- | 滴滴滴- \[文本] | 用于用户联系真寻的超级用户
|功能调用统计可视化 | 功能调用统计(自记录以来的功能调用统计)<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关键词/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关键词 | 查看已收录的所有关键词/UID/PID
| b了 | b了 [at] | 使真寻完全忽略一个用户的所有信息
| B站订阅 | 添加订阅 [主播/UP/番剧] [id/链接/番名] / 删除订阅 [id] / 查看订阅 | 可以通过直接间链接或主播间id添加订阅主播开播提醒,动态和投稿,可以通过番名,番剧的id,或者番剧链接添加番剧订阅(是番剧id,md开头,不是集数id,ep开头的)更新, 可以通过UP个人id或UP主页链接订阅UP动态和投稿
### 管理员功能
**群主与群管理员默认5级权限**
| 功能 | 权限等级 | 指令 | 说明
| -------------| --------------| :--------------------------------------:| :------------------------:
| 更新群组成员信息 | 1 | 更新群组成员信息/更新群组成员列表 | 存储群员的基本信息,虽然有自动更新,但备个命令以防万一
| 群功能开关 | 2 | 开启/关闭\[指令名]功能 | 群帮助中左边带有√的功能都可以通过此命令开启或关闭,示例:开启色图
| 查看群被动技能 | 2 | 群通知状态 | 详细请查看被动技能列表
| 被动技能开关 | 2 | 开启/关闭被动技能 | 有时候花里胡哨通知也会很烦人
| 自定义群欢迎消息 | 2 | 自定义群欢迎消息 \[文本] \[图片] | 文本和图片至少需要一个,在文本内添加"\[at]"字符串可以用来设置艾特进群的新群员
| 黑名单 | 5 | .ban/.unban \[at] \[小时]? \[分钟]?| 不提供具体时间的话则ban掉永久,且权限低的用户无法unban高权限用户的ban,同级权限也无法进行ban/unban <br>示例:.ban@笨蛋 1 50
| 刷屏检测相关 | 5 | 刷屏检测设置/设置检测时间 \[文本]/设置检测次数 \[文本]/设置禁言时长 \[分钟]| 非常讨厌刷屏的人,打算给他们一点教训
| 上传图片 | 6 | 上传图片 \[图库] \[图片]... | 上传图片至指定图库,虽然并不打算开放给群员,但还是写了,支持批量图片<br>示例:上传图片 美图 \[图片]..
| 删除图片 | 6 | 删除图片 [图库] \[图片id] | 通过指定本地图片id来删除指定图库的图片<br>示例:删除图片 美图 1
| 移动图片 | 6 | 移动图片 \[移出的图库] \[移入的图库] \[图片id] | 移动指定图库中的图片到指定的新图库中,移入的图片id更改为移入图库的最后一位,移除的图库中原本图片的id又最后一位图片替代<br>示例:移动图片 美图 萝莉 22
|B站订阅 | 5 | 功能同上,就是在群中有权限限制 | 略
### 超级用户功能
| 功能 | 指令 | 说明
| ----------------------| :--------------------------------------:| :------------------------:
| 权限增删 | 添加/删除权限 \[at] \[level]<br>添加/删除权限 [qq] [group] [level] | 用于添加或修改权限等级,且该权限不会被自动更新取消
| 所有群组/好友 | 所有群组/好友 | 查看真寻添加的群组与好友
| 广播 | 广播- [文本] | 广播所有群组
| 更新色图 | 更新色图 | 更新群友搜索色图时保存的url
| 回复 | /t | 命令较多,请查看/t帮助,省略群号则私聊用户(必须要有用户的好友)
| 更新cookie | 更新cookie \[cookie] | 用于更新开箱数据和查询buff皮肤
| 开启广播通知 | 开启/关闭广播通知 \[群号] | 用于开启/关闭是否对某些群进行广播(上边的广播方法)
| 退群 | 退群 \[群号] | 用于退出某一些群
| 检查系统状态 | 自检 | 略
| 更新好友/群组信息| 更新好友/群组信息 | 包含自动更新,被t出群等等有更好的可视信息
| 重载卡池 | 略 | 重载抽卡的游戏卡池,请查看 \[nonebot_plugin_gamedraw](https://github.com/HibiKier/nonebot_plugin_gamedraw)
| 添加商品 | 添加商品 \[名称]-\[价格]-\[描述]-\[折扣](小数)?-\[限时时间](分钟)? | 为真寻的商店添加一点点道具<br>示例:添加商品-昏睡红茶-300-一杯上好的奇怪红茶-0.9-60
| 删除商品 | 删除商品 \[名称或序号] | 在真寻的商店中删除一点东西
| 修改商品 | 修改商品 -name \[名称或序号] -price \[价格] -des \[描述] -discount \[折扣] -time \[限时]| 注意空格,不需要的参数可以不加<br>示例:修改商品 -name 1 -price 900 【修改序号为1的商品的价格为900】
| 节日红包 | 节日红包 \[金额] \[数量] \[祝福语]? \[群号]?... | 群号支持批量,使用空格隔开,不使用群号则对所有群发送节日红包,节日红包有效时间为24小时,祝福语默认为“恭喜发财 大吉大利”<br>示例:节日红包 10000 15 真寻真可爱 123324423 23423423
| 修改群权限 | 修改群权限 \[group] \[level] | 所以说这功能是对内鬼的无奈,默认群权限为5,默认无法使用 色图/coser/p站排行/搜图(这些功能都要9级权限)
|更新原神今日素材| 更新原神今日素材 | 自动更新原神每日素材失败时可以手动触发
|更新原神资源信息| 更新原神资源信息 | 除了每日自动更新的资源外,额外更新大地图
| 清理数据 | 清理数据 | 清理 temp,rar,r18_rar 文件夹的文件数据
|添加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/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关键词 | 与普通功能相同,额外显示待收录和黑名单
|pix检测更新 | pix检测更新 [update]? | 检测从未更新过的pid或uid,-update参数将在检测后直接更新未更新过的pid或uid<br>示例:pix检测更新 update
| 检查更新真寻 | 检查更新真寻 | 不再需要麻烦的clone第一步
| 关闭功能 | 关闭[功能] [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>
## 部署 ## 简单部署
``` ```
@@ -294,8 +201,7 @@ pip install -r requirements.txt
python bot.py python bot.py
``` ```
## 配置 ## 简单配置
在 configs/config.py 中的 USE_CONFIG_FILE,默认为False
``` ```
1.在.env.dev文件中 1.在.env.dev文件中
@@ -303,12 +209,10 @@ python bot.py
SUPERUSERS = [""] # 填写你的QQ SUPERUSERS = [""] # 填写你的QQ
2.在configs/config.py文件中 2.在configs/config.py文件中
必填: * 数据库配置
1. API KEY
2.数据库配置
在./configs/config.py中配置基本配置(除API KEY ,数据库和代理外都含有默认值) 3.在configs/config.yaml文件中 # 该文件需要启动一次后生成
在./configs/path_config.py配置路径(含有默认配置) * 修改插件配置项
``` ```
@@ -317,311 +221,95 @@ python bot.py
## 更新 ## 更新
### 2021/11/5 ### 2021/1/16 \[v0.0.9.0]
* 修复ai没有图灵key时报错 * Ai提供文本敏感词过滤器
* 提供图片路径resource/img/background/check * 疫情插件适配新版腾讯API
* 修复/t回复带空格切分
* 修复原神玩家查询缺少渊下宫和稻妻家园以及角色不完全
* 修复方法 text2image 中 padding 和 font 无法对纯文本生效
* 修复签到图片中信息并未使用配置文件中的色图概率
* 修改原神大地图合成方式,改为先压缩再合成
* bag_user弃用字段props(该字段会在下次更新删除),使用新字段property
* 数据库中所有belonging_group统一修改为group_id
* 商店将registered_use和register_goods更名为register_use何register_goods
* 商品注册提供了kwargs参数提供:
* bot
* event
* 特殊字段
* “send_success_msg”(发送成功的交互信息->即:使用道具 {name} {num} 次成功)
* “_max_num_limit”(该道具单次使用的最多个数,默认1)
### 2021/11/4 ### 2021/1/5 \[v0.0.8.2]
* 通用排行榜改用图片消息,且可以自定义排行榜人数 * 提供金币消费hook,可在plugins2settings.yaml中配置该功能需要消费的金币
* 优化CreateMat排行榜数据显示 * 商店插件将作为内置插件移动至basic_plugins
* 修复了pix更新多余参数导致失败的问题 * 商店插件通过export提供了方法,不需要修改商店插件代码添加商品数据和生效方法
* 修复滴滴滴-注入风险 * 修改了hook插件顺序,主要以auth_hook为主
* 修复无法正常关闭滴滴滴,戳一戳 * 修改商店图片样式
* 添加了发送图片撤回配置项WITHDRAW_IMAGE_TIME * 取消每次启动更新城市列表(首次除外),采用定时更新,加快bot启动速度
* 修复了天气regex文本过长时会正则匹配过久导致nb卡顿 * 取消每次启动时截取今日素材,采用调用时截取保存,加快bot启动速度
* message_build新增custom_forward_msg用于快捷生成转发消息 * 更新色图时当图片404时会删除并替换
* 插件配置改为yaml存储,新增Config,用于获取和新增插件配置 * 疫情消息回复改为图片
* 新增 当插件加载失败时,会发送消息提醒超级用户,且在功能状态中对应失败插件写上[ERROR] * 修复商店折扣和限时时间无法生效
* 修复当查看-spuer插件帮助时无法正确回复 * 修复原神玩家查询尘歌壶缺少图片
* 群内帮助图片会随群内功能开关和插件总开关变化
* 自检改为图像形式
* 更新色图删除了rar_setu,r18_rar和rar文件夹,压缩将统一在temp文件夹
* 更新色图只有在有更新数量或报错时才会提醒超级用户
* 群欢迎消息加入cd
* 加入资源管理resources_manager
* 新增 好友请求/群聊邀请 控制命令
### 2021/10/15 ### 2021/12/26
* 适配了原神资源查询米游社地图返回的新格式 * 修复群词条问题 空格 会被录入导致不断回复
* 修复米游社app替换api导致无法正常查询
### 2021/10/8 ### 2021/12/24
* 修复疫情省份查询失效 * 支持国际疫情数据查询 [@pull/99](https://github.com/HibiKier/zhenxun_bot/pull/99)
* 修复功能调用统计全局下统计可能发生错误
### 2021/10/4 ### 2021/12/20
* 修复了功能调用统计失效问题 * 只有发布小于存储时间的新动态/视频的时候才获取并推送 [@pull/96](https://github.com/HibiKier/zhenxun_bot/pull/96)
* 当色图库中没有色图时,会在线搜索色图而不是‘没找到符合条件的色图...’
* 快速更新权限再给超级用户发送错误日志
* 修复疫情未加载省份城市无法正常使用
### 2021/10/3 ### 2021/12/16 \[v0.0.7.0]
* 对插件进行分离 * 提供了真寻群聊功能总开关和对应默认配置项,命令:休息吧 醒来
* 重写了插件与限制管理器以及帮助获取 * 新增原神玩家查询,原神便笺查询
* 修改一些插件目录和数据存储目录 * 群功能管理提供全部开启/关闭命令:开启/关闭全部功能
* 插件通用配置与限制数据将以ymal文件存储 \[路径:data/configs] * 提供主要数据自动备份,且提供自定义配置项
* 所有商店相关操作调用统计合并为商店(包括之前已经保存的数据,会先进行备份) * 提供命令:关于,用于介绍Bot之类的
* 简化了点歌的代码相关 * 新增命令exec,用于执行sql语句
* 修复了碧蓝航线抽卡新框导致报错无法正常初始化 * 签到提供参数 "all",用于签到所有群聊
* 修复了P站排行/搜图在PC端无法正常显示 * Ban提醒提供cd
* 添加了插件对超级用户是否限制的配置 ‘limit_superuser’ * 本地图库提供配置项SHOW_ID,用于设置发送图片时是否显示id
* 添加命令 ‘重载插件配置’,用于生效手动修改配文件 * 色图和PIX提供配置项SHOW_INFO,用于设置发送图片时是否显示图片信息
* 超级用户帮助可以添加 -super 来显示该插件的超级用户帮助,示例:帮助.ban -super * 所有被动技能提供了进群默认状态配置项
* 原神黄历改为网页截图 * 修复添加权限第二种添加形式无法正确添加正确的权限
* 修改了鲁迅说逻辑结构 * 修复签到获取好感度卡时金币不会增加
* 修改了统计图表样式,改为自定义CreateMat * 修复当红包数量不合法时依旧扣除金币
* 节日红包不再被24小时限制,群内多个节日红包将会覆盖 * 修复金币红包再次使用塞红包时无法正确退回上次未开完的金币
* 当群权限为-1时,不会对群发送修改权限通知,并屏蔽此群一切命令(包括提醒) * 修复 滴滴滴- 只包含图片时不会发送至管理员
* 修复了红包数量可以过大或为负数,红包数量大于群员数量时会修改为群员数量 * 修复添加权限等级错误
* 修复了负数开箱 * 修复群词条以bot名称为开头时无法正确触发
* 签到最低好感度设置为0.01 [@pull/53](https://github.com/HibiKier/zhenxun_bot/pull/53) * 修改了权限插件加载顺序防止小概率优先加载权限插件引起报错
* pip安装新依赖 ruamel.yaml * 本地图库新图库会统一建立在resource/img/image_management文件夹下,如果该文件夹内未找到图库,会从上级目录查找(即:resource/img/)
* 修复功能 EPIC [@pull/58](https://github.com/HibiKier/zhenxun_bot/pull/58)
### 2021/9/10 <br>
* 修复撤回消息有时无法正确获取消息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
* 新增配置CHECK_NOTICE_INFO_CD,修改群权限,个人权限检测各种检测的提示消息cd
* 新增功能‘连续上传图片’功能,直到输入‘stop’停止
* 新增功能维护时白名单以及对应命令(白名单中的群聊不受维护限制)
* 新增ALAPI功能,微博热搜,可以通过序号来查看部分热搜内容
* 新增配置NICKNAME,偶尔也想换换名字的说(自我介绍仅当NICKNAME=真寻时生效)
* 提供 更新信息 命令,可以使群员查看更新内容(可开关,与其他功能无异,但不会被统计,该命令图片由自动更新生成)
* 超级用户可以通过私聊来对指定qq进行ban/unban
* 超级用户帮助改为图片形式
* 公开图库删除‘色图’
* 群权限检测,个人权限检测,功能开关检测合并,权限检测顺序:个人权限 > 群权限 > 插件开关 >超级用户禁用某群插件 > 超级用户限制群里插件 > 插件维护检测
* 重写群功能管理,超级用户可对群/私聊分别禁用,也可禁用指定群指定功能,新增命令‘功能状态’,超级用户关闭功能提供参数(默认ALL):group/g(群聊),private/p(私聊)
* 超级用户不会被任何权限等检测阻挡
* 不会重复复读,复读消息只会发送一次
* b站转发解析支持b23.tv,www.bilibili.com链接,cv专栏(playwright截图,压缩倍率0.5,较慢且文字可能不清晰,后优化)
* 我有一个朋友功能,当艾特一个对象时,‘朋友’改为艾特对象的群名片或昵称
* 修复‘上传/删除/移动图片’目录不正确
* 修复天气功能,当城市名在‘天气’后时报错
* 修复配置INITIAL_SETU_PROBABILITY不生效
### 2021/8/10
* 重复的好友请求和邀群提示在5分钟内不会重复提示
* 疫情会优先检查城市,城市省份市区重名时请添加‘市’
* 添加命令‘原神资源查找’,‘设置cookie’
* 添加配置AUTO_UPDATE_ZHENXUN,是否自动更新真寻,默认True
* 添加配置MAX_RUSSIAN_BET_GOLD,俄罗斯轮盘赌注最大金额,默认1000
* 检查更新真寻定时任务时间改为12 : 00
* 添加功能能不能好好说话(nbnhhsh)
* 添加功能随机roll,无参为数字,有参为随机参数
* 添加linux重启脚本以及重启命令‘重启’(建议首次生成restart.sh先查看命令是否正确)
* 修复管理员功能的权限检测
* 修复丢人爬开关
### 2021/8/6
* 天气查询会优先遍历城市
* 添加自动更新真寻命令
##### 如果你的版本为 2021/8/4,可以直接复制plugins/check_zhenxun_update后,通过指令来更新真寻
### 2021/8/4
* 修改天气与疫情城市数据,改为api获取,丰富疫情的回复消息
* 原神资源查询,大地图将被压缩至9M,且启动时当大地图存在时不再自动更新地图
* 下载数据库内色图时将直接存储至_setu,不再存储至临时文件
* 重复的好友请求或邀请请求在一定时间不会重复发送提醒
* 添加每日自动清理临时图片定时任务
* 修复金币排行显示
* 修复无法正常关闭戳一戳功能
### 2021/7/30
* 重构代码,进行优化,添加注释,删除冗余代码,降低代码耦合
* 添加功能:PIX(一套快捷的pixiv存图命令,自建图库存储url等信息?意在获取自己或群友xp的图)
* 添加功能:清理临时图片文件(temp/rar/r18_rar文件夹)
* 添加额外定时任务(5分钟检测一次),解决加入新群时无法及时为管理员提供权限
* 添加配置ALAPI_AI_CHECK,开关AI回复文本检测
* 添加配置IMPORT_DEFAULT_SHOP_GOODS,控制是否导入内置的三个商品(好感度加持卡ⅠⅡⅢ)
* 添加配置ONLY_USE_LOCAL_SETU,仅仅使用本地色图(有的话),提升速度,但无法在线搜索色图和保存链接
* 添加配置WITHDRAW_SETU_TIME,是否需要延迟撤回色图,可配置仅群里,私聊或全部
* 好友请求,入群请求,滴滴滴-,/t,被踢出群提醒,的提示消息更加丰富
* 彻底重写原神资源查找,添加规划路线(路线残缺缺缺缺版,有空补)添加命令‘更新原神资源信息’,强制更新地图等资源
* 优化色图和P站排行/搜图检测用户是否正在触发命令代码
* 当群最后发言大于36小时,也会关闭广播通知
* 功能维护时超级用户依然可以调用(苦了谁都不能苦了自己)
* 修复获取赛马娘UP公告
* 重写 色图/更新色图
* 色图数据存储改为数据库,启动时会更新之前的色图数据(有的话),更新完毕后会删除原数据文件,如果需要保留请提前备份,
* lolicon api改为v2
* 取消r18次数限制
* 单次搜索至多保存100条链接
* 添加定时撤回
* 暂时取消上传/删除色图
*
* 更新建议(不要替换你的data和resources文件夹!)
* 删除configs,plugins,services,utils,models文件夹重新clone
* 删除多余文件夹,resources/img/genshin/seek_god_eye
* 清空resources/img/genshin/genshin_icon文件夹,仅保留box.png和box_alpha.png
* 替换bot.py
### 2021/7/27
* 原神今日素材改为单张截图+拼图,更新文件utils/img_utils.py及plugins/genshin/material_remind/__init__.py
### 2021/7/26
* 修复原神今日素材稻妻城开放后截图不完整的问题
### 2021/7/14
* 原神今日素材自动更新时间由 00:01 -> 04:01 [#issues7](https://github.com/HibiKier/zhenxun_bot/issues/7)
* 小问题的修复和优化
### 2021/7/12
* 修复开箱功能单抽出金时存储格式错误导致 ‘我的金色’ 无法正常发送图片
* 小问题的修复和优化
### 2021/7/6
* 识番功能 trace.moe 替换为新API(旧API已失效)
* 小问题的修复和优化
### 2021/6/30
* 将plugin2name和plugin2level合并为plugin2info
* util改为utils(。。!)
* 修复当用户发送速度极快时开箱会突破每日限制
* 新增功能:通过PID获取图片
* 发送图片新增功能:搜索图片
* 功能统计可视化
* 新增命令:好感度总排行
* 原神每日素材改为从"可莉特调"截图,提供命令‘更新原神每日素材’和定时任务
* 修复月功能统计错误的问题
### 2021/6/24
* 添加了一些ALAPI:网易云热评,获取b站视频封面,古诗(需要填写ALAPI_TOKEN)
* 如果填写了ALAPI_TOKEN,将会检测备用接口回复的文本是否合规
* 优化了色图,当搜索色图下载失败时,会从本地色图库中发送相关tag色图
* 当网易云点歌繁忙时会尝试多次点歌
### 2021/6/23
* 添加功能:群权限(所以说内鬼都快爬,可以在configs/config.py中修改各个功能的权限等级)
* 优化了数据统计,将以7天,30天为周期,为将来更方便实现数据可视化
* 更新坎公骑冠剑UP卡池
* 修复赛马娘UP卡池
* 修复一些小问题
### 2021/6/18
* 修复p站排行,搜图因网络问题爆炸时没有具体回复
* 更换色图显示方式为 id,title,author,pid
* 修复修改商品后商品顺序改变
* 滴滴滴- 和 /t支持图片回复
* 将/t回复更加简单(可以通过序号),且可以直接发送群
* 修复bt功能无法交互
### 2021/6/17
* 修复p站排行,搜图因网络问题爆炸时没有具体回复
* 更换色图显示方式为 id,title,author,pid
### 2021/6/15
* 修改了‘帮助’功能,具体为‘帮助 指令名’,未指定指令名时则为查看全部功能列表
* 修改了色图的存储数据格式
* 色图功能搜索的色图改为随机从urls中随机抽取
* 将商品数据存储入数据库,提供 '增加/删除/修改商品' 指令
* 商店列表图片不再使用固定背景图,改为直接拼图
* 增加功能:俄罗斯轮盘/胜场排行/败场排行/欧洲人排行/慈善家排行
* 增加功能:金币红包(节日红包与群红包相互独立)
* 金币排行
* 重写一个朋友插件
* 其他微小调整
### 2021/6/4
* 重写BT功能
* 进行一些BUG修复和微小调整
* 添加撤回功能[nonebot-plugin-withdraw](https://github.com/MeetWq/nonebot-plugin-withdraw)
* 为色图功能添加额外的 上传色图 和 删除色图方法(影响hash)
### 2021/5/26
* 将语录源更换为一言api
## Todo ## Todo
- [ ] 提供更多对插件的控制
- [ ] 明日方舟卡片式的签到..(大概)
- [ ] 更多的群管理功能
- [ ] 数据清理控制
- [ ] docker容器 - [ ] docker容器
- [ ] web管理
## 感谢 ## 感谢
[Onebot](https://github.com/howmanybots/onebot) [botuniverse / onebot](https://github.com/botuniverse/onebot) :超棒的机器人协议
[go-cqhttp](https://github.com/Mrs4s/go-cqhttp) [Mrs4s / go-cqhttp](https://github.com/Mrs4s/go-cqhttp) :cqhttp的golang实现,轻量、原生跨平台.
[nonebot2](https://github.com/nonebot/nonebot2) [nonebot / nonebot2](https://github.com/nonebot/nonebot2) :跨平台Python异步机器人框架
[XUN_Langskip](https://github.com/Angel-Hair/XUN_Bot) [Angel-Hair / XUN_Bot](https://github.com/Angel-Hair/XUN_Bot) :一个基于NoneBot和酷Q的功能性QQ机器人
[cappuccilo_plugins](https://github.com/pcrbot/cappuccilo_plugins#%E7%94%9F%E6%88%90%E5%99%A8%E6%8F%92%E4%BB%B6) [pcrbot / cappuccilo_plugins](https://github.com/pcrbot/cappuccilo_plugins) :hoshino插件合集
[nonebot-plugin-withdraw](https://github.com/MeetWq/nonebot-plugin-withdraw) [MeetWq /nonebot-plugin-withdraw](https://github.com/MeetWq/nonebot-plugin-withdraw) :A simple withdraw plugin for Nonebot2
[nonebot_plugin_songpicker2](https://github.com/maxesisn/nonebot_plugin_songpicker2) [maxesisn / nonebot_plugin_songpicker2](https://github.com/maxesisn/nonebot_plugin_songpicker2) :适用于nonebot2的点歌插件
[nonebot_plugin_manager](https://github.com/Jigsaw111/nonebot_plugin_manager) [nonepkg / nonebot-plugin-manager](https://github.com/nonepkg/nonebot-plugin-manager) :Nonebot Plugin Manager base on import hook
[Genshin_Impact_bot](https://github.com/H-K-Y/Genshin_Impact_bot) [H-K-Y / Genshin_Impact_bot](https://github.com/H-K-Y/Genshin_Impact_bot) :原神bot,这是一个基于nonebot和HoshinoBot的原神娱乐及信息查询插件
[nonebot2_luxun_says](https://github.com/NothAmor/nonebot2_luxun_says) [NothAmor / nonebot2_luxun_says](https://github.com/NothAmor/nonebot2_luxun_says) :基于nonebot2机器人框架的鲁迅说插件
[AnimeThesaurus](https://github.com/Kyomotoi/AnimeThesaurus) [Kyomotoi / AnimeThesaurus](https://github.com/Kyomotoi/AnimeThesaurus) :一个~~特二刺螈~~(文爱)的适用于任何bot的词库
[omega-miya](https://github.com/Ailitonia/omega-miya) [Ailitonia / omega-miya](https://github.com/Ailitonia/omega-miya) :基于nonebot2的qq机器人
[KimigaiiWuyi / GenshinUID]("https://github.com/KimigaiiWuyi/GenshinUID") :一个基于HoshinoBot/NoneBot2的原神UID查询插件
+1 -1
View File
@@ -1 +1 @@
__version__: v0.0.5.1 __version__: v0.0.9.0
Regular → Executable
View File
+1 -1
View File
@@ -4,7 +4,7 @@ import nonebot
Config.add_plugin_config( Config.add_plugin_config(
"admin_bot_manage:custom_welcome_message", "admin_bot_manage:custom_welcome_message",
"SET_GROUP_WELCOME_MESSAGE_LEVEL", "SET_GROUP_WELCOME_MESSAGE_LEVEL [LEVEL]",
2, 2,
name="群管理员操作", name="群管理员操作",
help_="设置群欢迎消息权限", help_="设置群欢迎消息权限",
View File
+2 -2
View File
@@ -1,5 +1,5 @@
from nonebot import on_command from nonebot import on_command
from utils.utils import get_message_text, get_message_imgs from utils.utils import get_message_text, get_message_img
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 .data_source import custom_group_welcome from .data_source import custom_group_welcome
@@ -38,7 +38,7 @@ custom_welcome = on_command(
async def _(bot: Bot, event: GroupMessageEvent, state: T_State): async def _(bot: Bot, event: GroupMessageEvent, state: T_State):
try: try:
msg = get_message_text(event.json()) msg = get_message_text(event.json())
imgs = get_message_imgs(event.json()) imgs = get_message_img(event.json())
if not msg and not imgs: if not msg and not imgs:
await custom_welcome.finish(__plugin_usage__) await custom_welcome.finish(__plugin_usage__)
await custom_welcome.send( await custom_welcome.send(
+42 -19
View File
@@ -11,9 +11,8 @@ from services.db_context import db
from models.level_user import LevelUser from models.level_user import LevelUser
from configs.config import Config from configs.config import Config
from utils.manager import group_manager, plugins2settings_manager, plugins_manager from utils.manager import group_manager, plugins2settings_manager, plugins_manager
from utils.image_utils import CreateImg from utils.image_utils import BuildImage
import aiofiles from utils.http_utils import AsyncHttpx
import aiohttp
import asyncio import asyncio
import time import time
import os import os
@@ -73,12 +72,9 @@ async def custom_group_welcome(
logger.info(f"USER {user_id} GROUP {group_id} 更换群欢迎消息 {msg}") logger.info(f"USER {user_id} GROUP {group_id} 更换群欢迎消息 {msg}")
result += msg result += msg
if img: if img:
async with aiohttp.ClientSession() as session: await AsyncHttpx.download_file(
async with session.get(img, proxy=get_local_proxy()) as response: img, DATA_PATH + f"custom_welcome_msg/{group_id}.jpg"
async with aiofiles.open( )
DATA_PATH + f"custom_welcome_msg/{group_id}.jpg", "wb"
) as f:
await f.write(await response.read())
img_result = image(abspath=DATA_PATH + f"custom_welcome_msg/{group_id}.jpg") img_result = image(abspath=DATA_PATH + f"custom_welcome_msg/{group_id}.jpg")
logger.info(f"USER {user_id} GROUP {group_id} 更换群欢迎消息图片") logger.info(f"USER {user_id} GROUP {group_id} 更换群欢迎消息图片")
except Exception as e: except Exception as e:
@@ -113,7 +109,16 @@ async def change_group_switch(cmd: str, group_id: int, is_super: bool = False):
else: else:
if await group_manager.check_group_task_status(group_id, task): if await group_manager.check_group_task_status(group_id, task):
await group_manager.close_group_task(group_id, task) await group_manager.close_group_task(group_id, task)
if group_help_file.exists():
group_help_file.unlink()
return f"已 {status} 全部被动技能!" return f"已 {status} 全部被动技能!"
if cmd == "全部功能":
for f in plugins2settings_manager.get_data():
if status == "开启":
group_manager.unblock_plugin(f, group_id)
else:
group_manager.block_plugin(f, group_id)
return f"已 {status} 全部功能!"
if cmd in [task_data[x] for x in task_data.keys()]: if cmd in [task_data[x] for x in task_data.keys()]:
type_ = "task" type_ = "task"
modules = [x for x in task_data.keys() if task_data[x] == cmd] modules = [x for x in task_data.keys() if task_data[x] == cmd]
@@ -138,14 +143,14 @@ async def change_group_switch(cmd: str, group_id: int, is_super: bool = False):
if not group_manager.get_plugin_status(module, group_id): if not group_manager.get_plugin_status(module, group_id):
return f"功能 {cmd} 正处于关闭状态!不要重复关闭." return f"功能 {cmd} 正处于关闭状态!不要重复关闭."
group_manager.block_plugin(module, 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()
if is_super: if is_super:
for file in os.listdir(Path(DATA_PATH) / 'group_help'): for file in os.listdir(Path(DATA_PATH) / "group_help"):
file = Path(DATA_PATH) / 'group_help' / file file = Path(DATA_PATH) / "group_help" / file
file.unlink() file.unlink()
else: else:
_help_image = Path(DATA_PATH) / 'group_help' / f"{group_id}.png" _help_image = Path(DATA_PATH) / "group_help" / f"{group_id}.png"
if _help_image.exists(): if _help_image.exists():
_help_image.unlink() _help_image.unlink()
return f"{status} {cmd} 功能!" return f"{status} {cmd} 功能!"
@@ -164,8 +169,8 @@ def set_plugin_status(cmd: str, block_type: str = "all"):
plugins_manager.unblock_plugin(module) plugins_manager.unblock_plugin(module)
else: else:
plugins_manager.block_plugin(module, block_type=block_type) plugins_manager.block_plugin(module, block_type=block_type)
for file in os.listdir(Path(DATA_PATH) / 'group_help'): for file in os.listdir(Path(DATA_PATH) / "group_help"):
file = Path(DATA_PATH) / 'group_help' / file file = Path(DATA_PATH) / "group_help" / file
file.unlink() file.unlink()
@@ -201,11 +206,11 @@ def _get_plugin_status() -> MessageSegment:
rst += "\n" rst += "\n"
flag_str += f"{flag}\n" flag_str += f"{flag}\n"
height = len(rst.split("\n")) * 24 height = len(rst.split("\n")) * 24
a = CreateImg(250, height, font_size=20) a = BuildImage(250, height, font_size=20)
a.text((10, 10), rst) a.text((10, 10), rst)
b = CreateImg(200, height, font_size=20) b = BuildImage(200, height, font_size=20)
b.text((10, 10), flag_str) b.text((10, 10), flag_str)
A = CreateImg(500, height) A = BuildImage(500, height)
A.paste(a) A.paste(a)
A.paste(b, (270, 0)) A.paste(b, (270, 0))
return image(b64=A.pic2bs4()) return image(b64=A.pic2bs4())
@@ -294,3 +299,21 @@ async def update_member_info(group_id: int, remind_superuser: bool = False) -> b
user_id=int(list(bot.config.superusers)[0]), message=result[:-1] user_id=int(list(bot.config.superusers)[0]), message=result[:-1]
) )
return True return True
def set_group_bot_status(group_id: int, status: bool) -> str:
"""
设置群聊bot开关状态
:param group_id: 群号
:param status: 状态
"""
if status:
if group_manager.check_group_bot_status(group_id):
return "我还醒着呢!"
group_manager.turn_on_group_bot_status(group_id)
return "呜..醒来了..."
else:
group_manager.shutdown_group_bot_status(group_id)
# for x in group_manager.get_task_data():
# group_manager.close_group_task(group_id, x)
return "那我先睡觉了..."
+2 -2
View File
@@ -14,10 +14,10 @@ def switch_rule(bot: Bot, event: Event, state: T_State) -> bool:
:param event: pass :param event: pass
:param state: pass :param state: pass
""" """
global cmd
try: try:
if not cmd: if not cmd:
cmd.append('关闭全部被动') cmd = ["关闭全部被动", "开启全部被动", "开启全部功能", "关闭全部功能"]
cmd.append('开启全部被动')
_data = group_manager.get_task_data() _data = group_manager.get_task_data()
for key in _data: for key in _data:
cmd.append(f"开启{_data[key]}") cmd.append(f"开启{_data[key]}")
+19 -1
View File
@@ -1,4 +1,4 @@
from nonebot import on_command, on_message from nonebot import on_command, on_message, on_regex
from nonebot.typing import T_State from nonebot.typing import T_State
from nonebot.adapters.cqhttp import Bot, GroupMessageEvent, MessageEvent, GROUP from nonebot.adapters.cqhttp import Bot, GroupMessageEvent, MessageEvent, GROUP
from .data_source import ( from .data_source import (
@@ -6,12 +6,14 @@ from .data_source import (
set_plugin_status, set_plugin_status,
get_plugin_status, get_plugin_status,
group_current_status, group_current_status,
set_group_bot_status
) )
from services.log import logger from services.log import logger
from configs.config import NICKNAME, Config from configs.config import NICKNAME, Config
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 from .rule import switch_rule
import re
__zx_plugin_name__ = "群功能开关 [Admin]" __zx_plugin_name__ = "群功能开关 [Admin]"
@@ -24,6 +26,7 @@ usage:
群被动状态 群被动状态
开启全部被动 开启全部被动
关闭全部被动 关闭全部被动
醒来/休息吧
示例:开启/关闭色图 示例:开启/关闭色图
""".strip() """.strip()
__plugin_superuser_usage__ = """ __plugin_superuser_usage__ = """
@@ -40,6 +43,7 @@ __plugin_cmd__ = [
"群被动状态", "群被动状态",
"开启全部被动", "开启全部被动",
"关闭全部被动", "关闭全部被动",
"醒来/休息吧",
"功能状态 [_superuser]", "功能状态 [_superuser]",
"开启/关闭[功能] [group] [_superuser]", "开启/关闭[功能] [group] [_superuser]",
"开启/关闭[功能] ['private'/'group'] [_superuser]", "开启/关闭[功能] ['private'/'group'] [_superuser]",
@@ -57,6 +61,8 @@ plugins_status = on_command("功能状态", permission=SUPERUSER, priority=5, bl
group_task_status = on_command("群被动状态", permission=GROUP, priority=5, block=True) group_task_status = on_command("群被动状态", permission=GROUP, priority=5, block=True)
group_status = on_regex("^(休息吧|醒来)$", permission=GROUP, priority=5, block=True)
@switch_rule_matcher.handle() @switch_rule_matcher.handle()
async def _(bot: Bot, event: MessageEvent, state: T_State): async def _(bot: Bot, event: MessageEvent, state: T_State):
@@ -105,3 +111,15 @@ async def _(bot: Bot, event: MessageEvent, state: T_State):
@group_task_status.handle() @group_task_status.handle()
async def _(bot: Bot, event: GroupMessageEvent, state: T_State): async def _(bot: Bot, event: GroupMessageEvent, state: T_State):
await group_task_status.send(await group_current_status(event.group_id)) await group_task_status.send(await group_current_status(event.group_id))
@group_status.handle()
async def _(bot: Bot, event: GroupMessageEvent, state: T_State):
r = re.search("^(休息吧|醒来)$", get_message_text(event.json()))
if r:
if r.group(1) == "休息吧":
msg = set_group_bot_status(event.group_id, False)
else:
msg = set_group_bot_status(event.group_id, True)
await group_status.send(msg)
logger.info(f"USER {event.user_id} GROUP {event.group_id} 使用总开关命令:{r.group(1)}")
View File
View File
View File
+3 -3
View File
@@ -1,4 +1,4 @@
from utils.image_utils import CreateImg from utils.image_utils import BuildImage
from configs.path_config import IMAGE_PATH from configs.path_config import IMAGE_PATH
from services.log import logger from services.log import logger
from utils.utils import get_matchers from utils.utils import get_matchers
@@ -84,8 +84,8 @@ def _create_help_image():
for i, x in enumerate(task_data.keys()): for i, x in enumerate(task_data.keys()):
help_str += f'{i+1}.开启/关闭{task_data[x]}\n\n' help_str += f'{i+1}.开启/关闭{task_data[x]}\n\n'
height = len(help_str.split("\n")) * 33 height = len(help_str.split("\n")) * 33
A = CreateImg(width, height, font_size=24) A = BuildImage(width, height, font_size=24)
_background = CreateImg(width, height, background=background) _background = BuildImage(width, height, background=background)
A.text((150, 110), help_str) A.text((150, 110), help_str)
A.paste(_background, alpha=True) A.paste(_background, alpha=True)
A.save(admin_help_image) A.save(admin_help_image)
+58 -2
View File
@@ -5,8 +5,10 @@ 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, Config
from utils.manager import group_manager from utils.manager import group_manager
from pathlib import Path
import shutil
__zx_plugin_name__ = "定时任务相关 [Hidden]" __zx_plugin_name__ = "定时任务相关 [Hidden]"
__plugin_version__ = 0.1 __plugin_version__ = 0.1
@@ -14,7 +16,33 @@ __plugin_author__ = "HibiKier"
__plugin_task__ = {'zwa': '早晚安'} __plugin_task__ = {'zwa': '早晚安'}
x = on_message(priority=9, block=False) Config.add_plugin_config(
"_task",
"DEFAULT_ZWA",
True,
help_="被动 早晚安 进群默认开关状态",
default_value=True,
)
Config.add_plugin_config(
"_backup",
"BACKUP_FLAG",
True,
help_="是否开启文件备份",
default_value=True
)
Config.add_plugin_config(
"_backup",
"BACKUP_DIR_OR_FILE",
['data/black_word', 'data/configs', 'data/statistics', 'data/word_bank', 'data/manager', 'configs'],
name="文件备份",
help_="备份的文件夹或文件",
default_value=[]
)
cx = on_message(priority=9, block=False)
# 早上好 # 早上好
@@ -106,6 +134,34 @@ async def _():
logger.error(f"自动更新群组信息错误 e:{e}") logger.error(f"自动更新群组信息错误 e:{e}")
# 自动备份
@scheduler.scheduled_job(
"cron",
hour=3,
minute=25,
)
async def _():
if Config.get_config("_backup", "BACKUP_FLAG"):
_backup_path = Path() / 'backup'
_backup_path.mkdir(exist_ok=True, parents=True)
for x in Config.get_config("_backup", "BACKUP_DIR_OR_FILE"):
try:
path = Path(x)
_p = _backup_path / x
if path.exists():
if path.is_dir():
if _p.exists():
shutil.rmtree(_p, ignore_errors=True)
shutil.copytree(x, _p)
else:
if _p.exists():
_p.unlink()
shutil.copy(x, _p)
logger.info(f'已完成自动备份:{x}')
except Exception as e:
logger.error(f"自动备份文件 {x} 发生错误 {type(e)}:{e}")
# 一次性任务 # 一次性任务
# 固定时间触发,仅触发一次: # 固定时间触发,仅触发一次:
# #
Regular → Executable
+15 -5
View File
@@ -3,7 +3,7 @@ from models.ban_user import BanUser
from models.level_user import LevelUser 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, MessageEvent
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, Config from configs.config import NICKNAME, Config
from nonebot.permission import SUPERUSER from nonebot.permission import SUPERUSER
@@ -24,10 +24,14 @@ usage:
""".strip() """.strip()
__plugin_superuser_usage__ = """ __plugin_superuser_usage__ = """
usage: usage:
屏蔽用户消息,相当于最上级.ban b了=屏蔽用户消息,相当于最上级.ban
跨群ban以及跨群b了
指令: 指令:
b了 [at] b了 [at/qq]
.ban [user_id] ?[小时] ?[分钟]
示例:b了 @user 示例:b了 @user
示例:b了 1234567
示例:.ban 12345567
""".strip() """.strip()
__plugin_des__ = '你被逮捕了!丢进小黑屋!' __plugin_des__ = '你被逮捕了!丢进小黑屋!'
__plugin_cmd__ = ['.ban [at] ?[小时] ?[分钟]', '.unban [at]', 'b了 [at] [_superuser]'] __plugin_cmd__ = ['.ban [at] ?[小时] ?[分钟]', '.unban [at]', 'b了 [at] [_superuser]']
@@ -182,8 +186,14 @@ async def _(bot: Bot, event: PrivateMessageEvent, state: T_State):
@super_ban.handle() @super_ban.handle()
async def _(bot: Bot, event: GroupMessageEvent, state: T_State): async def _(bot: Bot, event: MessageEvent, state: T_State):
qq = get_message_at(event.json()) if isinstance(event, GroupMessageEvent):
qq = get_message_at(event.json())
else:
qq = get_message_text(event.json())
if not is_number(qq):
await super_ban.finish("对象qq必须为纯数字...")
qq = [qq]
if qq: if qq:
qq = qq[0] qq = qq[0]
user = await bot.get_group_member_info(group_id=event.group_id, user_id=qq) user = await bot.get_group_member_info(group_id=event.group_id, user_id=qq)
+10 -2
View File
@@ -3,10 +3,11 @@ from nonebot.typing import T_State
from nonebot.adapters import Bot, Event from nonebot.adapters import Bot, Event
from nonebot.permission import SUPERUSER 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_img
from services.log import logger from services.log import logger
from utils.message_builder import image from utils.message_builder import image
from utils.manager import group_manager from utils.manager import group_manager
from configs.config import Config
__zx_plugin_name__ = "广播 [Superuser]" __zx_plugin_name__ = "广播 [Superuser]"
__plugin_usage__ = """ __plugin_usage__ = """
@@ -20,6 +21,13 @@ __plugin_cmd__ = ["广播-"]
__plugin_version__ = 0.1 __plugin_version__ = 0.1
__plugin_author__ = "HibiKier" __plugin_author__ = "HibiKier"
__plugin_task__ = {"broadcast": "广播"} __plugin_task__ = {"broadcast": "广播"}
Config.add_plugin_config(
"_task",
"DEFAULT_BROADCAST",
True,
help_="被动 广播 进群默认开关状态",
default_value=True,
)
broadcast = on_command("广播-", priority=1, permission=SUPERUSER, block=True) broadcast = on_command("广播-", priority=1, permission=SUPERUSER, block=True)
@@ -27,7 +35,7 @@ broadcast = on_command("广播-", priority=1, permission=SUPERUSER, block=True)
@broadcast.handle() @broadcast.handle()
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_img(event.json())
rst = "" rst = ""
for img in imgs: for img in imgs:
rst += image(img) rst += image(img)
+22 -6
View File
@@ -30,15 +30,31 @@ __plugin_version__ = 0.1
__plugin_author__ = "HibiKier" __plugin_author__ = "HibiKier"
__plugin_task__ = {"group_welcome": "进群欢迎", "refund_group_remind": "退群提醒"} __plugin_task__ = {"group_welcome": "进群欢迎", "refund_group_remind": "退群提醒"}
Config.add_plugin_config( Config.add_plugin_config(
"auto_invite", "message", f"请不要未经同意就拉{NICKNAME}入群!告辞!", help_="强制拉群后进群回复的内容.." "invite_manager", "message", f"请不要未经同意就拉{NICKNAME}入群!告辞!", help_="强制拉群后进群回复的内容.."
) )
Config.add_plugin_config( Config.add_plugin_config(
"auto_invite", "flag", True, help_="被强制拉群后是否直接退出", default_value=True "invite_manager", "flag", True, help_="被强制拉群后是否直接退出", default_value=True
) )
Config.add_plugin_config( Config.add_plugin_config(
"auto_invite", "welcome_msg_cd", 5, help_="群欢迎消息cd", default_value=5 "invite_manager", "welcome_msg_cd", 5, help_="群欢迎消息cd", default_value=5
) )
_flmt = FreqLimiter(Config.get_config("auto_invite", "welcome_msg_cd")) Config.add_plugin_config(
"_task",
"DEFAULT_GROUP_WELCOME",
True,
help_="被动 进群欢迎 进群默认开关状态",
default_value=True,
)
Config.add_plugin_config(
"_task",
"DEFAULT_REFUND_GROUP_REMIND",
True,
help_="被动 退群提醒 进群默认开关状态",
default_value=True,
)
_flmt = FreqLimiter(Config.get_config("invite_manager", "welcome_msg_cd"))
# 群员增加处理 # 群员增加处理
@@ -55,10 +71,10 @@ async def _(bot: Bot, event: GroupIncreaseNoticeEvent, state: dict):
group = await GroupInfo.get_group_info(event.group_id) group = await GroupInfo.get_group_info(event.group_id)
# 群聊不存在或被强制拉群,退出该群 # 群聊不存在或被强制拉群,退出该群
if (not group or group.group_flag == 0) and Config.get_config( if (not group or group.group_flag == 0) and Config.get_config(
"auto_invite", "flag" "invite_manager", "flag"
): ):
try: try:
msg = Config.get_config("auto_invite", "message") msg = Config.get_config("invite_manager", "message")
if msg: if msg:
await bot.send_group_msg(group_id=event.group_id, message=msg) await bot.send_group_msg(group_id=event.group_id, message=msg)
await bot.set_group_leave(group_id=event.group_id) await bot.set_group_leave(group_id=event.group_id)
Regular → Executable
View File
+18 -18
View File
@@ -1,4 +1,4 @@
from utils.image_utils import CreateImg from utils.image_utils import BuildImage
from configs.path_config import IMAGE_PATH from configs.path_config import IMAGE_PATH
from utils.manager import ( from utils.manager import (
plugins2settings_manager, plugins2settings_manager,
@@ -50,7 +50,7 @@ def _create_help_img(
_des_tmp = {} _des_tmp = {}
_plugin_name_tmp = [] _plugin_name_tmp = []
_tmp = [] _tmp = []
tmp_img = CreateImg(0, 0, plain_text="1", font_size=24) tmp_img = BuildImage(0, 0, plain_text="1", font_size=24)
font_height = tmp_img.h font_height = tmp_img.h
# 插件分类 # 插件分类
for matcher in _matchers: for matcher in _matchers:
@@ -111,7 +111,7 @@ def _create_help_img(
logger.warning(f"{type(e)}: {e}") logger.warning(f"{type(e)}: {e}")
else: else:
matchers_data[plugin_type][plugin_name] = { matchers_data[plugin_type][plugin_name] = {
"module": matcher.module, "modules": matcher.module,
"des": plugin_des, "des": plugin_des,
"cmd": plugin_cmd, "cmd": plugin_cmd,
"text_type": text_type, "text_type": text_type,
@@ -147,16 +147,16 @@ def _create_help_img(
# 禁用flag # 禁用flag
flag = True flag = True
if plugins_manager.get_plugin_status( if plugins_manager.get_plugin_status(
matchers_data[type_][k]["module"], "all" matchers_data[type_][k]["modules"], "all"
): ):
flag = False flag = False
if group_id: if group_id:
flag = flag and plugins_manager.get_plugin_status( flag = flag and plugins_manager.get_plugin_status(
matchers_data[type_][k]["module"], "group" matchers_data[type_][k]["modules"], "group"
) )
simple_help_str += ( simple_help_str += (
f"{i+1}.{k}<|_|~|>" f"{i+1}.{k}<|_|~|>"
f"{group_manager.get_plugin_status(matchers_data[type_][k]['module'], group_id) if group_id else '_'}|" f"{group_manager.get_plugin_status(matchers_data[type_][k]['modules'], group_id) if group_id else '_'}|"
f"{flag}\n" f"{flag}\n"
) )
if matchers_data[type_][k]["text_type"] == 1: if matchers_data[type_][k]["text_type"] == 1:
@@ -181,7 +181,7 @@ def _create_help_img(
) )
height = len(help_str.split("\n")) * (font_height + 5) height = len(help_str.split("\n")) * (font_height + 5)
simple_height = len(simple_help_str.split("\n")) * (font_height + 5) simple_height = len(simple_help_str.split("\n")) * (font_height + 5)
A = CreateImg( A = BuildImage(
width + 150, height, font_size=24, color="white" if not ix % 2 else "black" 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)) A.text((10, 10), help_str, (255, 255, 255) if ix % 2 else (0, 0, 0))
@@ -192,8 +192,8 @@ def _create_help_img(
for x in simple_help_str.split("\n") for x in simple_help_str.split("\n")
]: ]:
simple_width = simple_width if simple_width > x else x simple_width = simple_width if simple_width > x else x
bk = CreateImg(simple_width + 20, simple_height, font_size=24, color="#6495ED") bk = BuildImage(simple_width + 20, simple_height, font_size=24, color="#6495ED")
B = CreateImg( B = BuildImage(
simple_width + 20, simple_width + 20,
simple_height, simple_height,
font_size=24, font_size=24,
@@ -236,7 +236,7 @@ def _create_help_img(
for img in help_img_list: for img in help_img_list:
height += img.h height += img.h
if not group_id: if not group_id:
A = CreateImg(width + 150, height + 50, font_size=24) A = BuildImage(width + 150, height + 50, font_size=24)
A.text( A.text(
(10, 10), '* 注: ‘*’ 代表可有多个相同参数 ‘?’ 代表可省略该参数 *\n\n" "功能名: 功能简介 -> 指令\n\n' (10, 10), '* 注: ‘*’ 代表可有多个相同参数 ‘?’ 代表可省略该参数 *\n\n" "功能名: 功能简介 -> 指令\n\n'
) )
@@ -253,14 +253,14 @@ def _create_help_img(
if img.h > height: if img.h > height:
height = img.h height = img.h
width += img.w + 10 width += img.w + 10
B = CreateImg(width + 100, height + 250, font_size=24) B = BuildImage(width + 100, height + 250, font_size=24)
width, _ = get_max_width_or_paste(simple_help_img_list, B) width, _ = get_max_width_or_paste(simple_help_img_list, B)
bk = None bk = None
random_bk = os.listdir(random_bk_path) random_bk = os.listdir(random_bk_path)
if random_bk: if random_bk:
bk = random.choice(random_bk) bk = random.choice(random_bk)
x = max(width + 50, height + 250) x = max(width + 50, height + 250)
B = CreateImg( B = BuildImage(
x, x,
x, x,
font_size=24, font_size=24,
@@ -272,7 +272,7 @@ def _create_help_img(
w = 10 w = 10
h = 10 h = 10
for msg in ["目前支持的功能列表:", "可以通过 ‘帮助[功能名称]’ 来获取对应功能的使用方法", "或者使用 ‘详细帮助’ 来获取所有功能方法"]: for msg in ["目前支持的功能列表:", "可以通过 ‘帮助[功能名称]’ 来获取对应功能的使用方法", "或者使用 ‘详细帮助’ 来获取所有功能方法"]:
text = CreateImg( text = BuildImage(
0, 0,
0, 0,
plain_text=msg, plain_text=msg,
@@ -284,7 +284,7 @@ def _create_help_img(
if msg == "目前支持的功能列表:": if msg == "目前支持的功能列表:":
w += 50 w += 50
B.paste( B.paste(
CreateImg( BuildImage(
0, 0,
0, 0,
plain_text="注: 红字代表功能被群管理员禁用,红线代表功能正在维护", plain_text="注: 红字代表功能被群管理员禁用,红线代表功能正在维护",
@@ -299,8 +299,8 @@ def _create_help_img(
def get_max_width_or_paste( def get_max_width_or_paste(
simple_help_img_list: list, B: CreateImg = None, is_paste: bool = False simple_help_img_list: list, B: BuildImage = None, is_paste: bool = False
) -> "int, CreateImg": ) -> "int, BuildImage":
""" """
获取最大宽度,或直接贴图 获取最大宽度,或直接贴图
:param simple_help_img_list: 简单帮助图片列表 :param simple_help_img_list: 简单帮助图片列表
@@ -353,8 +353,8 @@ def get_plugin_help(msg: str, is_super: bool = False) -> Optional[str]:
_width = len(x) * 24 _width = len(x) * 24
width = width if width > _width else _width width = width if width > _width else _width
height = len(result.split("\n")) * 45 height = len(result.split("\n")) * 45
A = CreateImg(width, height, font_size=24) A = BuildImage(width, height, font_size=24)
bk = CreateImg( bk = BuildImage(
width, width,
height, height,
background=Path(IMAGE_PATH) / "background" / "1.png", background=Path(IMAGE_PATH) / "background" / "1.png",
Regular → Executable
-2
View File
@@ -1,5 +1,4 @@
from configs.config import Config from configs.config import Config
import nonebot
Config.add_plugin_config( Config.add_plugin_config(
@@ -35,4 +34,3 @@ Config.add_plugin_config(
default_value=6 default_value=6
) )
nonebot.load_plugins("basic_plugins/hooks")
Regular → Executable
+203 -35
View File
@@ -1,33 +1,42 @@
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.friend_user import FriendUser
from models.group_member_info import GroupInfoUser
from models.bag_user import BagUser
from utils.manager import ( from utils.manager import (
plugins2settings_manager, plugins2settings_manager,
admin_manager, admin_manager,
group_manager, group_manager,
plugins_manager, plugins_manager,
plugins2cd_manager,
plugins2block_manager,
plugins2count_manager
) )
from .utils import set_block_limit_false, status_message_manager from .utils import set_block_limit_false, status_message_manager
from nonebot.typing import T_State from nonebot.typing import T_State
from typing import Optional
from nonebot.adapters.cqhttp import ( from nonebot.adapters.cqhttp import (
Bot, Bot,
MessageEvent, MessageEvent,
GroupMessageEvent, GroupMessageEvent,
PokeNotifyEvent, PokeNotifyEvent,
PrivateMessageEvent,
Message,
Event
) )
from configs.config import Config from configs.config import Config
from models.ban_user import BanUser from models.ban_user import BanUser
from utils.utils import FreqLimiter from utils.utils import FreqLimiter
from utils.message_builder import at from utils.message_builder import at
from models.level_user import LevelUser from models.level_user import LevelUser
import nonebot
_flmt = FreqLimiter(Config.get_config("hook", "CHECK_NOTICE_INFO_CD")) _flmt = FreqLimiter(Config.get_config("hook", "CHECK_NOTICE_INFO_CD"))
_flmt_g = FreqLimiter(Config.get_config("hook", "CHECK_NOTICE_INFO_CD")) _flmt_g = FreqLimiter(Config.get_config("hook", "CHECK_NOTICE_INFO_CD"))
_flmt_s = FreqLimiter(Config.get_config("hook", "CHECK_NOTICE_INFO_CD")) _flmt_s = FreqLimiter(Config.get_config("hook", "CHECK_NOTICE_INFO_CD"))
_flmt_c = FreqLimiter(Config.get_config("hook", "CHECK_NOTICE_INFO_CD")) _flmt_c = FreqLimiter(Config.get_config("hook", "CHECK_NOTICE_INFO_CD"))
ignore_rst_module = ["ai", "poke", "dialogue"] ignore_rst_module = ["ai", "poke", "dialogue"]
@@ -36,30 +45,66 @@ ignore_rst_module = ["ai", "poke", "dialogue"]
async def _(matcher: Matcher, bot: Bot, event: MessageEvent, state: T_State): async def _(matcher: Matcher, bot: Bot, event: MessageEvent, state: T_State):
module = matcher.module module = matcher.module
plugins2info_dict = plugins2settings_manager.get_data() plugins2info_dict = plugins2settings_manager.get_data()
if ( # 功能的金币检测 #######################################
(not isinstance(event, MessageEvent) and module != "poke") # 功能的金币检测 #######################################
or await BanUser.is_ban(event.user_id) # 功能的金币检测 #######################################
and str(event.user_id) not in bot.config.superusers cost_gold = 0
) or ( if isinstance(event, GroupMessageEvent) and plugins2settings_manager.get_plugin_data(module).get('cost_gold'):
str(event.user_id) in bot.config.superusers cost_gold = plugins2settings_manager.get_plugin_data(module).get('cost_gold')
and plugins2info_dict.get(module) if await BagUser.get_gold(event.user_id, event.group_id) < cost_gold:
and not plugins2info_dict[module]["limit_superuser"] await send_msg(f"金币不足..该功能需要{cost_gold}金币..", bot, event)
): raise IgnoredException(f"{module} 金币限制...")
return # 当插件不阻塞超级用户时,超级用户提前扣除金币
# 群黑名单检测 if str(event.user_id) in bot.config.superusers and not plugins2info_dict[module]["limit_superuser"]:
if isinstance(event, GroupMessageEvent): await BagUser.spend_gold(event.user_id, event.group_id, cost_gold)
if group_manager.get_group_level(event.group_id) < 0: try:
raise IgnoredException("群黑名单") if (
(not isinstance(event, MessageEvent) and module != "poke")
or await BanUser.is_ban(event.user_id)
and str(event.user_id) not in bot.config.superusers
) or (
str(event.user_id) in bot.config.superusers
and plugins2info_dict.get(module)
and not plugins2info_dict[module]["limit_superuser"]
):
return
except AttributeError:
pass
# 超级用户命令
try:
_plugin = nonebot.plugin.get_plugin(module)
_module = _plugin.module
plugin_name = _module.__getattribute__("__zx_plugin_name__")
if (
"[superuser]" in plugin_name.lower()
and str(event.user_id) in bot.config.superusers
):
return
except AttributeError:
pass
# 群黑名单检测 群总开关检测
if isinstance(event, GroupMessageEvent) or matcher.module == "poke":
try:
if group_manager.get_group_level(event.group_id) < 0:
raise IgnoredException("群黑名单")
if not group_manager.check_group_bot_status(event.group_id):
try:
if str(event.get_message()) != "醒来":
raise IgnoredException("功能总开关关闭状态")
except ValueError:
raise IgnoredException("功能总开关关闭状态")
except AttributeError:
pass
if module in admin_manager.keys() and matcher.priority not in [1, 9]: if module in admin_manager.keys() and matcher.priority not in [1, 9]:
if isinstance(event, GroupMessageEvent): if isinstance(event, GroupMessageEvent):
# 个人权限 # 个人权限
if ( if (
not await LevelUser.check_level( not await LevelUser.check_level(
event.user_id, event.user_id,
event.group_id, event.group_id,
admin_manager.get_plugin_level(module), admin_manager.get_plugin_level(module),
) )
and admin_manager.get_plugin_level(module) > 0 and admin_manager.get_plugin_level(module) > 0
): ):
try: try:
if _flmt.check(event.user_id): if _flmt.check(event.user_id):
@@ -67,7 +112,7 @@ 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_manager.get_plugin_level(module)}", f"{admin_manager.get_plugin_level(module)}",
) )
except ActionFailed: except ActionFailed:
pass pass
@@ -77,7 +122,7 @@ async def _(matcher: Matcher, bot: Bot, event: MessageEvent, state: T_State):
raise IgnoredException("权限不足") raise IgnoredException("权限不足")
else: else:
if not await LevelUser.check_level( if not await LevelUser.check_level(
event.user_id, 0, admin_manager.get_plugin_level(module) event.user_id, 0, admin_manager.get_plugin_level(module)
): ):
try: try:
await bot.send_private_msg( await bot.send_private_msg(
@@ -93,12 +138,12 @@ async def _(matcher: Matcher, bot: Bot, event: MessageEvent, state: T_State):
if module in plugins2info_dict.keys() and matcher.priority not in [1, 9]: if module in plugins2info_dict.keys() and matcher.priority not in [1, 9]:
# 戳一戳单独判断 # 戳一戳单独判断
if isinstance(event, GroupMessageEvent) or ( if isinstance(event, GroupMessageEvent) or (
isinstance(event, PokeNotifyEvent) and event.group_id isinstance(event, PokeNotifyEvent) and event.group_id
): ):
if status_message_manager.get(event.group_id) is None: if status_message_manager.get(event.group_id) is None:
status_message_manager.delete(event.group_id) status_message_manager.delete(event.group_id)
if plugins2info_dict[module]["level"] > group_manager.get_group_level( if plugins2info_dict[module]["level"] > group_manager.get_group_level(
event.group_id event.group_id
): ):
try: try:
if _flmt_g.check(event.user_id) and module not in ignore_rst_module: if _flmt_g.check(event.user_id) and module not in ignore_rst_module:
@@ -116,7 +161,7 @@ async def _(matcher: Matcher, bot: Bot, event: MessageEvent, state: T_State):
if not group_manager.get_plugin_status(module, event.group_id): if not group_manager.get_plugin_status(module, event.group_id):
try: try:
if module not in ignore_rst_module and _flmt_s.check( if module not in ignore_rst_module and _flmt_s.check(
event.group_id 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(
@@ -132,8 +177,8 @@ async def _(matcher: Matcher, bot: Bot, event: MessageEvent, state: T_State):
if not group_manager.get_plugin_status(f"{module}:super", event.group_id): if not group_manager.get_plugin_status(f"{module}:super", event.group_id):
try: try:
if ( if (
_flmt_s.check(event.group_id) _flmt_s.check(event.group_id)
and module not in ignore_rst_module 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(
@@ -149,8 +194,8 @@ async def _(matcher: Matcher, bot: Bot, event: MessageEvent, state: T_State):
if not plugins_manager.get_plugin_status(module, block_type="group"): if not plugins_manager.get_plugin_status(module, block_type="group"):
try: try:
if ( if (
_flmt_c.check(event.group_id) _flmt_c.check(event.group_id)
and module not in ignore_rst_module 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(
@@ -180,14 +225,14 @@ async def _(matcher: Matcher, bot: Bot, event: MessageEvent, state: T_State):
# 维护 # 维护
if not plugins_manager.get_plugin_status(module, block_type="all"): if not plugins_manager.get_plugin_status(module, block_type="all"):
if isinstance( if isinstance(
event, GroupMessageEvent event, GroupMessageEvent
) and group_manager.check_group_is_white(event.group_id): ) and group_manager.check_group_is_white(event.group_id):
return return
try: try:
if isinstance(event, GroupMessageEvent): if isinstance(event, GroupMessageEvent):
if ( if (
_flmt_c.check(event.group_id) _flmt_c.check(event.group_id)
and module not in ignore_rst_module 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(
@@ -208,3 +253,126 @@ async def _(matcher: Matcher, bot: Bot, event: MessageEvent, state: T_State):
status_message_manager.add(id_) status_message_manager.add(id_)
set_block_limit_false(event, module) set_block_limit_false(event, module)
raise IgnoredException("此功能正在维护...") raise IgnoredException("此功能正在维护...")
# 以下为限制检测 #######################################################
# 以下为限制检测 #######################################################
# 以下为限制检测 #######################################################
# 以下为限制检测 #######################################################
# 以下为限制检测 #######################################################
# 以下为限制检测 #######################################################
# Cd
if plugins2cd_manager.check_plugin_cd_status(module):
plugin_cd_data = plugins2cd_manager.get_plugin_cd_data(module)
check_type = plugin_cd_data["check_type"]
limit_type = plugin_cd_data["limit_type"]
rst = plugin_cd_data["rst"]
if (
(isinstance(event, PrivateMessageEvent) and check_type == "private")
or (isinstance(event, GroupMessageEvent) and check_type == "group")
or plugins2cd_manager.get_plugin_data(module).get("check_type") == "all"
):
cd_type_ = event.user_id
if limit_type == "group" and isinstance(event, GroupMessageEvent):
cd_type_ = event.group_id
if not plugins2cd_manager.check(module, cd_type_):
if rst:
rst = await init_rst(rst, event)
await send_msg(rst, bot, event)
raise IgnoredException(f"{module} 正在cd中...")
else:
plugins2cd_manager.start_cd(module, cd_type_)
# Block
if plugins2block_manager.check_plugin_block_status(module):
plugin_block_data = plugins2block_manager.get_plugin_block_data(module)
check_type = plugin_block_data["check_type"]
limit_type = plugin_block_data["limit_type"]
rst = plugin_block_data["rst"]
if (
(isinstance(event, PrivateMessageEvent) and check_type == "private")
or (isinstance(event, GroupMessageEvent) and check_type == "group")
or check_type == "all"
):
block_type_ = event.user_id
if limit_type == "group" and isinstance(event, GroupMessageEvent):
block_type_ = event.group_id
if plugins2block_manager.check(block_type_, module):
if rst:
rst = await init_rst(rst, event)
await send_msg(rst, bot, event)
raise IgnoredException(f"{event.user_id}正在调用{module}....")
else:
plugins2block_manager.set_true(block_type_, module)
# Count
if (
plugins2count_manager.check_plugin_count_status(module)
and event.user_id not in bot.config.superusers
):
plugin_count_data = plugins2count_manager.get_plugin_count_data(module)
limit_type = plugin_count_data["limit_type"]
rst = plugin_count_data["rst"]
count_type_ = event.user_id
if limit_type == "group" and isinstance(event, GroupMessageEvent):
count_type_ = event.group_id
if not plugins2count_manager.check(module, count_type_):
if rst:
rst = await init_rst(rst, event)
await send_msg(rst, bot, event)
raise IgnoredException(f"{module} count次数限制...")
else:
plugins2count_manager.increase(module, count_type_)
# 功能花费的金币 #######################################
# 功能花费的金币 #######################################
if cost_gold:
await BagUser.spend_gold(event.user_id, event.group_id, cost_gold)
async def send_msg(rst: str, bot: Bot, event: MessageEvent):
"""
发送信息
:param rst: pass
:param bot: pass
:param event: pass
"""
rst = await init_rst(rst, event)
try:
if isinstance(event, GroupMessageEvent):
status_message_manager.add(event.group_id)
await bot.send_group_msg(group_id=event.group_id, message=Message(rst))
else:
status_message_manager.add(event.user_id)
await bot.send_private_msg(user_id=event.user_id, message=Message(rst))
except ActionFailed:
pass
# 解除命令block阻塞
@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
set_block_limit_false(event, 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
Regular → Executable
+5 -2
View File
@@ -9,7 +9,7 @@ from nonebot.adapters.cqhttp import (
) )
from configs.config import Config from configs.config import Config
from models.ban_user import BanUser from models.ban_user import BanUser
from utils.utils import is_number, static_flmt from utils.utils import is_number, static_flmt, FreqLimiter
from utils.message_builder import at from utils.message_builder import at
@@ -20,6 +20,8 @@ Config.add_plugin_config(
help_="对被ban用户发送的消息", help_="对被ban用户发送的消息",
) )
_flmt = FreqLimiter(300)
# 检查是否被ban # 检查是否被ban
@run_preprocessor @run_preprocessor
@@ -55,7 +57,8 @@ async def _(matcher: Matcher, bot: Bot, event: MessageEvent, state: T_State):
if matcher.priority != 9: if matcher.priority != 9:
try: try:
ban_result = Config.get_config("hook", "BAN_RESULT") ban_result = Config.get_config("hook", "BAN_RESULT")
if ban_result: if ban_result and _flmt.check(event.user_id):
_flmt.start_cd(event.user_id)
await bot.send_group_msg( await bot.send_group_msg(
group_id=event.group_id, group_id=event.group_id,
message=at(event.user_id) message=at(event.user_id)
View File
-146
View File
@@ -1,146 +0,0 @@
from nonebot.matcher import Matcher
from nonebot.message import run_preprocessor, run_postprocessor, IgnoredException
from nonebot.adapters.cqhttp.exception import ActionFailed
from models.friend_user import FriendUser
from models.group_member_info import GroupInfoUser
from utils.message_builder import at
from .utils import status_message_manager, set_block_limit_false
from utils.manager import (
plugins2cd_manager,
plugins2block_manager,
plugins2count_manager,
)
from typing import Optional
from nonebot.typing import T_State
from nonebot.adapters.cqhttp import (
Bot,
Event,
MessageEvent,
PrivateMessageEvent,
GroupMessageEvent,
Message,
)
# 命令cd | 命令阻塞 | 命令次数
@run_preprocessor
async def _(matcher: Matcher, bot: Bot, event: MessageEvent, state: T_State):
if not isinstance(event, MessageEvent) and matcher.module != "poke":
return
module = matcher.module
if (
isinstance(event, GroupMessageEvent)
and status_message_manager.get(event.group_id) is None
):
status_message_manager.delete(event.group_id)
# Count
if (
plugins2count_manager.check_plugin_count_status(module)
and event.user_id not in bot.config.superusers
):
plugin_count_data = plugins2count_manager.get_plugin_count_data(module)
limit_type = plugin_count_data["limit_type"]
rst = plugin_count_data["rst"]
count_type_ = event.user_id
if limit_type == "group" and isinstance(event, GroupMessageEvent):
count_type_ = event.group_id
if not plugins2count_manager.check(module, count_type_):
if rst:
rst = await init_rst(rst, event)
await send_msg(rst, bot, event)
raise IgnoredException(f"{module} count次数限制...")
else:
plugins2count_manager.increase(module, count_type_)
# Cd
if plugins2cd_manager.check_plugin_cd_status(module):
plugin_cd_data = plugins2cd_manager.get_plugin_cd_data(module)
check_type = plugin_cd_data["check_type"]
limit_type = plugin_cd_data["limit_type"]
rst = plugin_cd_data["rst"]
if (
(isinstance(event, PrivateMessageEvent) and check_type == "private")
or (isinstance(event, GroupMessageEvent) and check_type == "group")
or plugins2cd_manager.get_plugin_data(module).get("check_type") == "all"
):
cd_type_ = event.user_id
if limit_type == "group" and isinstance(event, GroupMessageEvent):
cd_type_ = event.group_id
if not plugins2cd_manager.check(module, cd_type_):
if rst:
rst = await init_rst(rst, event)
await send_msg(rst, bot, event)
raise IgnoredException(f"{module} 正在cd中...")
else:
plugins2cd_manager.start_cd(module, cd_type_)
# Block
if plugins2block_manager.check_plugin_block_status(module):
plugin_block_data = plugins2block_manager.get_plugin_block_data(module)
check_type = plugin_block_data["check_type"]
limit_type = plugin_block_data["limit_type"]
rst = plugin_block_data["rst"]
if (
(isinstance(event, PrivateMessageEvent) and check_type == "private")
or (isinstance(event, GroupMessageEvent) and check_type == "group")
or check_type == "all"
):
block_type_ = event.user_id
if limit_type == "group" and isinstance(event, GroupMessageEvent):
block_type_ = event.group_id
if plugins2block_manager.check(block_type_, module):
if rst:
rst = await init_rst(rst, event)
await send_msg(rst, bot, event)
raise IgnoredException(f"{event.user_id}正在调用{module}....")
else:
plugins2block_manager.set_true(block_type_, module)
async def send_msg(rst: str, bot: Bot, event: MessageEvent):
"""
发送信息
:param rst: pass
:param bot: pass
:param event: pass
"""
rst = await init_rst(rst, event)
try:
if isinstance(event, GroupMessageEvent):
status_message_manager.add(event.group_id)
await bot.send_group_msg(group_id=event.group_id, message=Message(rst))
else:
status_message_manager.add(event.user_id)
await bot.send_private_msg(user_id=event.user_id, message=Message(rst))
except ActionFailed:
pass
# 解除命令block阻塞
@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
set_block_limit_false(event, 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
View File
Regular → Executable
View File
View File
+10
View File
@@ -9,10 +9,13 @@ from .init_plugins_limit import (
init_plugins_count_limit, init_plugins_count_limit,
init_plugins_cd_limit, init_plugins_cd_limit,
) )
from .init import init
from .check_plugin_status import check_plugin_status from .check_plugin_status import check_plugin_status
from utils.manager import admin_manager
from nonebot.adapters.cqhttp import Bot from nonebot.adapters.cqhttp import Bot
from configs.path_config import DATA_PATH from configs.path_config import DATA_PATH
from services.log import logger from services.log import logger
from pathlib import Path
from nonebot import Driver from nonebot import Driver
import nonebot import nonebot
@@ -30,6 +33,11 @@ def _():
""" """
初始化数据 初始化数据
""" """
_flag = False
config_file = Path(DATA_PATH) / "configs" / "plugins2config.yaml"
if not config_file.exists():
_flag = True
init()
init_plugins_settings(DATA_PATH) init_plugins_settings(DATA_PATH)
init_plugins_cd_limit(DATA_PATH) init_plugins_cd_limit(DATA_PATH)
init_plugins_block_limit(DATA_PATH) init_plugins_block_limit(DATA_PATH)
@@ -42,6 +50,8 @@ def _():
if x: if x:
for key in x.keys(): for key in x.keys():
plugins_manager.block_plugin(key, block_type=x[key]) plugins_manager.block_plugin(key, block_type=x[key])
if _flag:
raise Exception("首次运行,已在configs目录下生成配置文件config.yaml,修改后重启即可...")
logger.info("初始化数据完成...") logger.info("初始化数据完成...")
View File
+14
View File
@@ -0,0 +1,14 @@
from utils.manager import plugins2settings_manager
def init():
if plugins2settings_manager.get("update_pic"):
plugins2settings_manager["update_picture"] = plugins2settings_manager["update_pic"]
plugins2settings_manager.delete("update_pic")
if plugins2settings_manager.get("white2black_img"):
plugins2settings_manager["white2black_image"] = plugins2settings_manager["white2black_img"]
plugins2settings_manager.delete("white2black_img")
if plugins2settings_manager.get("send_img"):
plugins2settings_manager["send_image"] = plugins2settings_manager["send_img"]
plugins2settings_manager.delete("send_img")
View File
View File
+3
View File
@@ -92,11 +92,14 @@ def init_plugins_config(data_path):
if user_config_file.exists(): if user_config_file.exists():
with open(user_config_file, "r", encoding="utf8") as f: with open(user_config_file, "r", encoding="utf8") as f:
_data = _yaml.load(f) _data = _yaml.load(f)
# 数据替换
for plugin in Config.keys(): for plugin in Config.keys():
_tmp_data[plugin] = {} _tmp_data[plugin] = {}
for k in Config[plugin].keys(): for k in Config[plugin].keys():
if _data.get(plugin) and k in _data[plugin].keys(): if _data.get(plugin) and k in _data[plugin].keys():
Config.set_config(plugin, k, _data[plugin][k]) Config.set_config(plugin, k, _data[plugin][k])
if level2module := Config.get_level2module(plugin, k):
admin_manager.set_admin_level(level2module, _data[plugin][k])
_tmp_data[plugin][k] = Config.get_config(plugin, k) _tmp_data[plugin][k] = Config.get_config(plugin, k)
Config.save() Config.save()
temp_file = Path() / "configs" / "temp_config.yaml" temp_file = Path() / "configs" / "temp_config.yaml"
View File
View File
View File
+15 -17
View File
@@ -17,23 +17,19 @@ def init_plugins_settings(data_path: str):
plugins2settings_file = Path(data_path) / "configs" / "plugins2settings.yaml" plugins2settings_file = Path(data_path) / "configs" / "plugins2settings.yaml"
plugins2settings_file.parent.mkdir(exist_ok=True, parents=True) plugins2settings_file.parent.mkdir(exist_ok=True, parents=True)
_matchers = get_matchers() _matchers = get_matchers()
_data = {}
if plugins2settings_file.exists():
with open(plugins2settings_file, "r", encoding="utf8") as f:
_data = _yaml.load(f)
_data = _data["PluginSettings"] if _data else {}
_tmp_module = {} _tmp_module = {}
_tmp = [] _tmp = []
for x in plugins2settings_manager.keys():
try:
_plugin = nonebot.plugin.get_plugin(x)
_module = _plugin.module
plugin_name = _module.__getattribute__("__zx_plugin_name__")
_tmp_module[x] = plugin_name
except (KeyError, AttributeError) as e:
logger.warning(f"配置文件 模块:{x} 获取 plugin_name 失败...{e}")
_tmp_module[x] = ""
for matcher in _matchers: for matcher in _matchers:
if matcher.module in _data.keys(): if matcher.module not in plugins2settings_manager.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) _plugin = nonebot.plugin.get_plugin(matcher.module)
try: try:
_module = _plugin.module _module = _plugin.module
@@ -52,7 +48,7 @@ def init_plugins_settings(data_path: str):
except (AttributeError, KeyError): except (AttributeError, KeyError):
level = 5 level = 5
cmd = None cmd = None
if not level: if level is None:
level = 5 level = 5
admin_manager.add_admin_plugin_settings( admin_manager.add_admin_plugin_settings(
matcher.module, cmd, level matcher.module, cmd, level
@@ -75,8 +71,10 @@ def init_plugins_settings(data_path: str):
plugin_settings = _module.__getattribute__( plugin_settings = _module.__getattribute__(
"__plugin_settings__" "__plugin_settings__"
) )
if plugin_settings.get('cost_gold') is None:
plugin_settings['cost_gold'] = 0
if ( if (
plugin_settings["cmd"] plugin_settings["cmd"] is not None
and plugin_name not in plugin_settings["cmd"] and plugin_name not in plugin_settings["cmd"]
): ):
plugin_settings["cmd"].append(plugin_name) plugin_settings["cmd"].append(plugin_name)
@@ -101,7 +99,7 @@ def init_plugins_settings(data_path: str):
plugins2settings_manager.add_plugin_settings( plugins2settings_manager.add_plugin_settings(
matcher.module, matcher.module,
plugin_type=plugin_type, plugin_type=plugin_type,
data_dict=plugin_settings, **plugin_settings,
) )
except AttributeError: except AttributeError:
pass pass
@@ -25,14 +25,13 @@ __plugin_configs__ = {
friend_req = on_request(priority=5, block=True) friend_req = on_request(priority=5, block=True)
group_req = on_request(priority=5, block=True) group_req = on_request(priority=5, block=True)
x = on_message(priority=5, block=False) x = on_message(priority=9, block=False)
exists_data = {"private": {}, "group": {}} exists_data = {"private": {}, "group": {}}
@friend_req.handle() @friend_req.handle()
async def _(bot: Bot, event: FriendRequestEvent, state: dict): async def _(bot: Bot, event: FriendRequestEvent, state: dict):
print(event)
global exists_data global exists_data
if exists_data["private"].get(event.user_id): if exists_data["private"].get(event.user_id):
if time.time() - exists_data["private"][event.user_id] < 60 * 5: if time.time() - exists_data["private"][event.user_id] < 60 * 5:
@@ -47,11 +46,11 @@ async def _(bot: Bot, event: FriendRequestEvent, state: dict):
user_id=int(list(bot.config.superusers)[0]), user_id=int(list(bot.config.superusers)[0]),
message=f"*****一份好友申请*****\n" message=f"*****一份好友申请*****\n"
f"昵称:{nickname}({event.user_id})\n" f"昵称:{nickname}({event.user_id})\n"
f"自动同意:{'√' if Config.get_config('auto_invite', 'AUTO_ADD_FRIEND') else '×'}\n" f"自动同意:{'√' if Config.get_config('invite_manager', 'AUTO_ADD_FRIEND') else '×'}\n"
f"日期:{str(datetime.now()).split('.')[0]}\n" f"日期:{str(datetime.now()).split('.')[0]}\n"
f"备注:{event.comment}", f"备注:{event.comment}",
) )
if Config.get_config("auto_invite", "AUTO_ADD_FRIEND"): if Config.get_config("invite_manager", "AUTO_ADD_FRIEND"):
await bot.set_friend_add_request(flag=event.flag, approve=True) await bot.set_friend_add_request(flag=event.flag, approve=True)
await FriendUser.add_friend_info(user["user_id"], user["nickname"]) await FriendUser.add_friend_info(user["user_id"], user["nickname"])
else: else:
@@ -72,9 +71,6 @@ async def _(bot: Bot, event: GroupRequestEvent, state: dict):
if event.sub_type == "invite": if event.sub_type == "invite":
if str(event.user_id) in bot.config.superusers: if str(event.user_id) in bot.config.superusers:
try: try:
await bot.set_group_add_request(
flag=event.flag, sub_type="invite", approve=True
)
if await GroupInfo.get_group_info(event.group_id): if await GroupInfo.get_group_info(event.group_id):
await GroupInfo.set_group_flag(event.group_id, 1) await GroupInfo.set_group_flag(event.group_id, 1)
else: else:
@@ -86,6 +82,9 @@ async def _(bot: Bot, event: GroupRequestEvent, state: dict):
group_info["member_count"], group_info["member_count"],
1, 1,
) )
await bot.set_group_add_request(
flag=event.flag, sub_type="invite", approve=True
)
except ActionFailed: except ActionFailed:
pass pass
else: else:
Regular → Executable
+51 -44
View File
@@ -8,7 +8,7 @@ from models.friend_user import FriendUser
import random import random
from models.ban_user import BanUser from models.ban_user import BanUser
from services.log import logger from services.log import logger
from configs.config import NICKNAME from configs.config import NICKNAME, Config
__zx_plugin_name__ = "昵称系统" __zx_plugin_name__ = "昵称系统"
@@ -23,6 +23,13 @@ __plugin_des__ = "区区昵称,才不想叫呢!"
__plugin_cmd__ = ["以后叫我 [昵称]", f"{NICKNAME}我是谁"] __plugin_cmd__ = ["以后叫我 [昵称]", f"{NICKNAME}我是谁"]
__plugin_version__ = 0.1 __plugin_version__ = 0.1
__plugin_author__ = "HibiKier" __plugin_author__ = "HibiKier"
__plugin_configs__ = {
"BLACK_WORD": {
"value": ["爸", "爹", "爷"],
"help": "昵称所屏蔽的关键词,会被替换为 *",
"default_value": None
}
}
nickname = on_command( nickname = on_command(
"nickname", "nickname",
@@ -47,52 +54,52 @@ async def _(bot: Bot, event: GroupMessageEvent, state: T_State):
await nickname.finish("叫你空白?叫你虚空?叫你无名??", at_sender=True) await nickname.finish("叫你空白?叫你虚空?叫你无名??", at_sender=True)
if len(msg) > 10: if len(msg) > 10:
await nickname.finish("昵称可不能超过10个字!", at_sender=True) await nickname.finish("昵称可不能超过10个字!", at_sender=True)
if await GroupInfoUser.set_group_member_nickname( if msg in bot.config.superusers:
event.user_id, event.group_id, msg await nickname.finish("笨蛋!休想占用我的名字!#", at_sender=True)
): _tmp = ""
if len(msg) < 5: black_word = Config.get_config("nickname", "BLACK_WORD")
if random.random() < 0.5: for x in msg:
msg = "~".join(msg) _tmp += "*" if x in black_word else x
await nickname.send( msg = _tmp
random.choice( if isinstance(event, GroupMessageEvent):
[ if await GroupInfoUser.set_group_member_nickname(
f"好啦好啦,我知道啦,{msg},以后就这么叫你吧", event.user_id, event.group_id, msg
f"嗯嗯,{NICKNAME}记住你的昵称了哦,{msg}", ):
f"好突然,突然要叫你昵称什么的...{msg}..", if len(msg) < 5:
f"{NICKNAME}会好好记住的{msg}的,放心吧", if random.random() < 0.3:
f"好..好.,那窝以后就叫你{msg}了.", msg = "~".join(msg)
] await nickname.send(
random.choice(
[
f"好啦好啦,我知道啦,{msg},以后就这么叫你吧",
f"嗯嗯,{NICKNAME}记住你的昵称了哦,{msg}",
f"好突然,突然要叫你昵称什么的...{msg}..",
f"{NICKNAME}会好好记住的{msg}的,放心吧",
f"好..好.,那窝以后就叫你{msg}了.",
]
)
) )
) logger.info(f"USER {event.user_id} GROUP {event.group_id} 设置群昵称 {msg}")
logger.info(f"USER {event.user_id} GROUP {event.group_id} 设置群昵称 {msg}") else:
await nickname.send("设置昵称失败,请更新群组成员信息!", at_sender=True)
logger.warning(f"USER {event.user_id} GROUP {event.group_id} 设置群昵称 {msg} 失败")
else: else:
await nickname.send("设置昵称失败,请更新群组成员信息!", at_sender=True) if await FriendUser.set_friend_nickname(event.user_id, msg):
logger.warning(f"USER {event.user_id} GROUP {event.group_id} 设置群昵称 {msg} 失败") await nickname.send(
random.choice(
[
@nickname.handle() f"好啦好啦,我知道啦,{msg},以后就这么叫你吧",
async def _(bot: Bot, event: PrivateMessageEvent, state: T_State): f"嗯嗯,{NICKNAME}记住你的昵称了哦,{msg}",
msg = get_message_text(event.json()) f"好突然,突然要叫你昵称什么的...{msg}..",
if not msg: f"{NICKNAME}会好好记住的{msg}的,放心吧",
await nickname.finish("叫你空白?叫你虚空?叫你无名??", at_sender=True) f"好..好.,那窝以后就叫你{msg}了.",
if len(msg) > 10: ]
await nickname.finish("不要超过10个字!", at_sender=True) )
if await FriendUser.set_friend_nickname(event.user_id, msg):
await nickname.send(
random.choice(
[
f"好啦好啦,我知道啦,{msg},以后就这么叫你吧",
f"嗯嗯,{NICKNAME}记住你的昵称了哦,{msg}",
f"好突然,突然要叫你昵称什么的...{msg}..",
f"{NICKNAME}会好好记住的{msg}的,放心吧",
f"好..好.,那窝以后就叫你{msg}了.",
]
) )
) logger.info(f"USER {event.user_id} 设置昵称 {msg}")
logger.info(f"USER {event.user_id} 设置昵称 {msg}") else:
else: await nickname.send("设置昵称失败了,明天再来试一试!或联系管理员更新好友!", at_sender=True)
await nickname.send("设置昵称失败了,明天再来试一试!或联系管理员更新好友!", at_sender=True) logger.warning(f"USER {event.user_id} 设置昵称 {msg} 失败")
logger.warning(f"USER {event.user_id} 设置昵称 {msg} 失败")
@my_nickname.handle() @my_nickname.handle()
Regular → Executable
+115 -30
View File
@@ -1,14 +1,20 @@
from asyncpg.exceptions import (
DuplicateColumnError,
UndefinedColumnError,
PostgresSyntaxError,
)
from nonebot import Driver from nonebot import Driver
from services.db_context import db from services.db_context import db
from asyncpg.exceptions import DuplicateColumnError
from models.group_info import GroupInfo from models.group_info import GroupInfo
from models.bag_user import BagUser
from nonebot.adapters.cqhttp import Bot from nonebot.adapters.cqhttp import Bot
from utils.user_agent import get_user_agent
from services.log import logger from services.log import logger
from configs.path_config import TEXT_PATH from configs.path_config import TEXT_PATH
from asyncio.exceptions import TimeoutError from asyncio.exceptions import TimeoutError
from typing import List
from utils.http_utils import AsyncHttpx
from utils.utils import scheduler
from pathlib import Path from pathlib import Path
import aiohttp
import nonebot import nonebot
try: try:
@@ -25,30 +31,35 @@ async def update_city():
""" """
部分插件需要中国省份城市 部分插件需要中国省份城市
这里直接更新,避免插件内代码重复 这里直接更新,避免插件内代码重复
:return:
""" """
china_city = Path(TEXT_PATH) / "china_city.json" china_city = Path(TEXT_PATH) / "china_city.json"
data = {} data = {}
try: if not china_city.exists():
async with aiohttp.ClientSession(headers=get_user_agent()) as session: try:
async with session.get( res = await AsyncHttpx.get(
"http://www.weather.com.cn/data/city3jdata/china.html", timeout=5 "http://www.weather.com.cn/data/city3jdata/china.html", timeout=5
) as res: )
provinces_data = json.loads(await res.text(encoding="utf8")) res.encoding = "utf8"
provinces_data = json.loads(res.text)
for province in provinces_data.keys(): for province in provinces_data.keys():
data[provinces_data[province]] = [] data[provinces_data[province]] = []
async with session.get( res = await AsyncHttpx.get(
f"http://www.weather.com.cn/data/city3jdata/provshi/{province}.html", f"http://www.weather.com.cn/data/city3jdata/provshi/{province}.html",
timeout=5, timeout=5,
) as res: )
city_data = json.loads(await res.text(encoding="utf8")) res.encoding = "utf8"
for city in city_data.keys(): city_data = json.loads(res.text)
data[provinces_data[province]].append(city_data[city]) for city in city_data.keys():
with open(china_city, "w", encoding="utf8") as f: data[provinces_data[province]].append(city_data[city])
json.dump(data, f, indent=4, ensure_ascii=False) with open(china_city, "w", encoding="utf8") as f:
logger.info("自动更新城市列表完成.....") json.dump(data, f, indent=4, ensure_ascii=False)
except TimeoutError: logger.info("自动更新城市列表完成.....")
logger.info("自动更新城市列表超时.....") except TimeoutError:
logger.warning("自动更新城市列表超时.....")
except ValueError:
logger.warning("自动城市列表失败.....")
except Exception as e:
logger.error(f"自动城市列表未知错误 {type(e)}:{e}")
@driver.on_startup @driver.on_startup
@@ -56,15 +67,70 @@ async def _():
""" """
数据库表结构变换 数据库表结构变换
""" """
_flag = []
sql_str = [ sql_str = [
"ALTER TABLE group_info ADD group_flag Integer NOT NULL DEFAULT 0;" # group_info表添加一个group_flag (
"ALTER TABLE group_info ADD group_flag Integer NOT NULL DEFAULT 0;",
"group_info",
), # group_info表添加一个group_flag
(
"ALTER TABLE bag_users rename belonging_group To group_id;",
"bag_users",
), # 将 bag_users 的 belonging_group 改为 group_id
(
"ALTER TABLE group_info_users rename belonging_group To group_id;",
"group_info_users",
),
(
"ALTER TABLE sign_group_users rename belonging_group To group_id;",
"sign_group_users",
),
(
"ALTER TABLE open_cases_users rename belonging_group To group_id;",
"open_cases_users",
),
(
"ALTER TABLE bag_users ADD property json NOT NULL DEFAULT '{}';",
"bag_users",
), # bag_users 新增字段 property 替代 props
] ]
for sql in sql_str: for sql in sql_str:
try: try:
flag = sql[1]
sql = sql[0]
query = db.text(sql) query = db.text(sql)
await db.first(query) await db.first(query)
except DuplicateColumnError: logger.info(f"完成sql操作:{sql}")
_flag.append(flag)
except (DuplicateColumnError, UndefinedColumnError):
pass pass
except PostgresSyntaxError:
logger.error(f"语法错误:执行sql失败:{sql}")
# bag_user 将文本转为字典格式
await __database_script(_flag)
# 完成后
end_sql_str = [
# "ALTER TABLE bag_users DROP COLUMN props;" # 删除 bag_users 的 props 字段(还不到时候)
]
for sql in end_sql_str:
try:
query = db.text(sql)
await db.first(query)
logger.info(f"完成执行sql操作:{sql}")
except (DuplicateColumnError, UndefinedColumnError):
pass
except PostgresSyntaxError:
logger.error(f"语法错误:执行sql失败:{sql}")
# str2json_sql = ["alter table bag_users alter COLUMN props type json USING props::json;"] # 字段类型替换
# rename_sql = 'alter table {} rename {} to {};' # 字段更名
# for sql in str2json_sql:
# try:
# query = db.text(sql)
# await db.first(query)
# except DuplicateColumnError:
# pass
@driver.on_bot_connect @driver.on_bot_connect
@@ -75,13 +141,7 @@ async def _(bot: Bot):
# 清空不存在的群聊信息,并将已所有已存在的群聊group_flag设置为1(认证所有已存在的群) # 清空不存在的群聊信息,并将已所有已存在的群聊group_flag设置为1(认证所有已存在的群)
if not await GroupInfo.get_group_info(114514): if not await GroupInfo.get_group_info(114514):
# 标识符,该功能只需执行一次 # 标识符,该功能只需执行一次
await GroupInfo.add_group_info( await GroupInfo.add_group_info(114514, "114514", 114514, 114514, 1)
114514,
"114514",
114514,
114514,
1
)
group_list = await bot.get_group_list() group_list = await bot.get_group_list()
group_list = [g["group_id"] for g in group_list] group_list = [g["group_id"] for g in group_list]
_gl = [x.group_id for x in await GroupInfo.get_all_group()] _gl = [x.group_id for x in await GroupInfo.get_all_group()]
@@ -98,7 +158,7 @@ async def _(bot: Bot):
group_info["group_name"], group_info["group_name"],
group_info["max_member_count"], group_info["max_member_count"],
group_info["member_count"], group_info["member_count"],
1 1,
) )
logger.info(f"已将群聊 {group_id} 添加认证...") logger.info(f"已将群聊 {group_id} 添加认证...")
else: else:
@@ -106,6 +166,31 @@ async def _(bot: Bot):
logger.info(f"移除不存在的群聊信息:{group_id}") logger.info(f"移除不存在的群聊信息:{group_id}")
async def __database_script(_flag: List[str]):
# bag_user 将文本转为字典格式
if "bag_users" in _flag:
for x in await BagUser.get_all_users():
props = {}
if x.props:
for prop in [p for p in x.props.split(",") if p]:
if props.get(prop):
props[prop] += 1
else:
props[prop] = 1
logger.info(
f"__database_script USER {x.user_qq} GROUP {x.group_id} 更新数据 {props}"
)
await x.update(
property=props,
props="",
).apply()
# 自动更新城市列表
@scheduler.scheduled_job(
"cron",
hour=6,
minute=1,
)
async def _():
await update_city()
@@ -11,4 +11,4 @@ Config.add_plugin_config(
) )
nonebot.load_plugins("plugins/shop") nonebot.load_plugins("basic_plugins/shop")
@@ -6,7 +6,7 @@ from utils.utils import get_message_text, is_number
from models.bag_user import BagUser from models.bag_user import BagUser
from services.db_context import db from services.db_context import db
from nonebot.adapters.cqhttp.permission import GROUP from nonebot.adapters.cqhttp.permission import GROUP
from .models.goods_info import GoodsInfo from models.goods_info import GoodsInfo
__zx_plugin_name__ = "商店 - 购买道具" __zx_plugin_name__ = "商店 - 购买道具"
@@ -29,6 +29,9 @@ __plugin_settings__ = {
"limit_superuser": False, "limit_superuser": False,
"cmd": ["商店", "购买道具"], "cmd": ["商店", "购买道具"],
} }
__plugin_cd_limit__ = {
"cd": 3
}
buy = on_command("购买", aliases={"购买道具"}, priority=5, block=True, permission=GROUP) buy = on_command("购买", aliases={"购买道具"}, priority=5, block=True, permission=GROUP)
@@ -36,17 +39,18 @@ buy = on_command("购买", aliases={"购买道具"}, priority=5, block=True, per
@buy.handle() @buy.handle()
async def _(bot: Bot, event: GroupMessageEvent, state: T_State): async def _(bot: Bot, event: GroupMessageEvent, state: T_State):
goods = None
if get_message_text(event.json()) in ["神秘药水"]: if get_message_text(event.json()) in ["神秘药水"]:
await buy.finish("你们看看就好啦,这是不可能卖给你们的~", at_sender=True) await buy.finish("你们看看就好啦,这是不可能卖给你们的~", at_sender=True)
goods_lst = await GoodsInfo.get_all_goods() goods_lst = await GoodsInfo.get_all_goods()
goods_name_lst = [x.goods_name for x in goods_lst] goods_name_lst = [x.goods_name for x in goods_lst]
msg = get_message_text(event.json()).strip().split(" ") msg = get_message_text(event.json()).split()
num = 1 num = 1
if len(msg) > 1: if len(msg) > 1:
if is_number(msg[1]): if is_number(msg[1]) and int(msg[1]) > 0:
num = int(msg[1]) num = int(msg[1])
else: else:
await buy.finish("购买的数量要是数字!", at_sender=True) await buy.finish("购买的数量要是数字且大于0!", at_sender=True)
# print(msg, num) # print(msg, num)
if is_number(msg[0]): if is_number(msg[0]):
msg = int(msg[0]) msg = int(msg[0])
@@ -66,15 +70,11 @@ async def _(bot: Bot, event: GroupMessageEvent, state: T_State):
async with db.transaction(): async with db.transaction():
if ( if (
await BagUser.get_gold(event.user_id, event.group_id) await BagUser.get_gold(event.user_id, event.group_id)
) < goods.goods_price * num: ) < goods.goods_price * num * goods.goods_discount:
await buy.finish("您的金币好像不太够哦", at_sender=True) await buy.finish("您的金币好像不太够哦", at_sender=True)
if await BagUser.spend_gold( if await BagUser.buy_property(event.user_id, event.group_id, goods, num):
event.user_id, event.group_id, goods.goods_price * num
):
for _ in range(num):
await BagUser.add_props(event.user_id, event.group_id, goods.goods_name)
await buy.send( await buy.send(
f"花费 {goods.goods_price*num} 金币购买 {goods.goods_name} ×{num} 成功!", f"花费 {goods.goods_price * num * goods.goods_discount} 金币购买 {goods.goods_name} ×{num} 成功!",
at_sender=True, at_sender=True,
) )
logger.info( logger.info(
@@ -85,5 +85,5 @@ async def _(bot: Bot, event: GroupMessageEvent, state: T_State):
await buy.send(f"{goods.goods_name} 购买失败!", at_sender=True) await buy.send(f"{goods.goods_name} 购买失败!", at_sender=True)
logger.info( logger.info(
f"USER {event.user_id} GROUP {event.group_id} " f"USER {event.user_id} GROUP {event.group_id} "
f"花费 {goods.goods_price*num} 金币购买 {goods.goods_name} ×{num} 失败!" f"花费 {goods.goods_price * num * goods.goods_discount} 金币购买 {goods.goods_name} ×{num} 失败!"
) )
@@ -34,7 +34,7 @@ gold_rank = on_command("金币排行", priority=5, block=True, permission=GROUP)
@my_gold.handle() @my_gold.handle()
async def _(bot: Bot, event: GroupMessageEvent, state: T_State): async def _(bot: Bot, event: GroupMessageEvent, state: T_State):
await my_gold.finish(await BagUser.get_my_total_gold(event.user_id, event.group_id)) await my_gold.finish(await BagUser.get_user_total_gold(event.user_id, event.group_id))
@gold_rank.handle() @gold_rank.handle()
@@ -31,21 +31,11 @@ my_props = on_command("我的道具", priority=5, block=True, permission=GROUP)
@my_props.handle() @my_props.handle()
async def _(bot: Bot, event: GroupMessageEvent, state: T_State): async def _(bot: Bot, event: GroupMessageEvent, state: T_State):
props = await BagUser.get_props(event.user_id, event.group_id) props = await BagUser.get_property(event.user_id, event.group_id)
if props: if props:
pname_list = []
pnum_list = []
rst = "" rst = ""
props = props[:-1].split(",") for i, p in enumerate(props.keys()):
for p in props: rst += f"{i+1}.{p}\t×{props[p]}\n"
if p != "":
if p in pname_list:
pnum_list[pname_list.index(p)] += 1
else:
pname_list.append(p)
pnum_list.append(1)
for i in range(len(pname_list)):
rst += f"{i+1}.{pname_list[i]}\t×{pnum_list[i]}\n"
await my_props.send("\n" + rst[:-1], at_sender=True) await my_props.send("\n" + rst[:-1], at_sender=True)
logger.info(f"USER {event.user_id} GROUP {event.group_id} 查看我的道具") logger.info(f"USER {event.user_id} GROUP {event.group_id} 查看我的道具")
else: else:
+133
View File
@@ -0,0 +1,133 @@
from nonebot import on_command
from nonebot.adapters.cqhttp import Bot, GroupMessageEvent, MessageEvent
from nonebot.typing import T_State
from configs.path_config import IMAGE_PATH
from utils.message_builder import image
from .data_source import create_shop_help, delete_goods, update_goods, register_goods, parse_goods_info
from nonebot.permission import SUPERUSER
from utils.utils import get_message_text, is_number
from nonebot.plugin import export
from services.log import logger
import os
__zx_plugin_name__ = "商店"
__plugin_usage__ = """
usage:
商店项目,这可不是奸商
指令:
商店
""".strip()
__plugin_superuser_usage__ = """
usage:
商品操作
指令:
添加商品 name:[名称] price:[价格] des:[描述] ?discount:[折扣](小数) ?limit_time:[限时时间](小时)
删除商品 [名称或序号]
修改商品 name:[名称或序号] price:[价格] des:[描述] discount:[折扣] limit_time:[限时]
示例:添加商品 name:萝莉酒杯 price:9999 des:普通的酒杯,但是里面.. discount:0.4 limit_time:90
示例:添加商品 name:可疑的药 price:5 des:效果未知
示例:删除商品 2
示例:修改商品 name:1 price:900 修改序号为1的商品的价格为900
* 修改商品只需添加需要值即可 *
""".strip()
__plugin_des__ = "商店系统[金币回收计划]"
__plugin_cmd__ = [
"商店",
"添加商品 name:[名称] price:[价格] des:[描述] ?discount:[折扣](小数) ?limit_time:[限时时间](小时)) [_superuser]",
"删除商品 [名称或序号] [_superuser]",
"修改商品 name:[名称或序号] price:[价格] des:[描述] discount:[折扣] limit_time:[限时] [_superuser]",
]
__plugin_type__ = ('商店',)
__plugin_version__ = 0.1
__plugin_author__ = "HibiKier"
__plugin_settings__ = {
"level": 5,
"default_status": True,
"limit_superuser": False,
"cmd": ["商店"],
}
__plugin_block_limit__ = {
"limit_type": "group"
}
# 导出方法供其他插件使用
export = export()
export.register_goods = register_goods
export.delete_goods = delete_goods
export.update_goods = update_goods
shop_help = on_command("商店", priority=5, block=True)
shop_add_goods = on_command("添加商品", priority=5, permission=SUPERUSER, block=True)
shop_del_goods = on_command("删除商品", priority=5, permission=SUPERUSER, block=True)
shop_update_goods = on_command("修改商品", priority=5, permission=SUPERUSER, block=True)
@shop_help.handle()
async def _(bot: Bot, event: GroupMessageEvent, state: T_State):
await shop_help.send(image(b64=await create_shop_help()))
@shop_add_goods.handle()
async def _(bot: Bot, event: MessageEvent, state: T_State):
msg = get_message_text(event.json())
if msg:
data = parse_goods_info(msg)
if isinstance(data, str):
await shop_add_goods.finish(data)
if not data.get("name") or not data.get("price") or not data.get("des"):
await shop_add_goods.finish("name:price:des 参数不可缺少!")
if await register_goods(**data):
await shop_add_goods.send(f"添加商品 {data['name']} 成功!\n"
f"名称:{data['name']}\n"
f"价格:{data['price']}金币\n"
f"简介:{data['des']}\n"
f"折扣:{data.get('discount')}\n"
f"限时:{data.get('limit_time')}", at_sender=True)
logger.info(f"USER {event.user_id} 添加商品 {msg} 成功")
else:
await shop_add_goods.send(f"添加商品 {msg} 失败了...", at_sender=True)
logger.warning(f"USER {event.user_id} 添加商品 {msg} 失败")
@shop_del_goods.handle()
async def _(bot: Bot, event: MessageEvent, state: T_State):
msg = get_message_text(event.json())
if msg:
name = ""
id_ = 0
if is_number(msg):
id_ = int(msg)
else:
name = msg
rst, goods_name, code = await delete_goods(name, id_)
if code == 200:
await shop_del_goods.send(f"删除商品 {goods_name} 成功了...", at_sender=True)
if os.path.exists(f"{IMAGE_PATH}/shop_help.png"):
os.remove(f"{IMAGE_PATH}/shop_help.png")
logger.info(f"USER {event.user_id} 删除商品 {goods_name} 成功")
else:
await shop_del_goods.send(f"删除商品 {goods_name} 失败了...", at_sender=True)
logger.info(f"USER {event.user_id} 删除商品 {goods_name} 失败")
@shop_update_goods.handle()
async def _(bot: Bot, event: MessageEvent, state: T_State):
msg = get_message_text(event.json())
if msg:
data = parse_goods_info(msg)
if isinstance(data, str):
await shop_add_goods.finish(data)
if not data.get("name"):
await shop_add_goods.finish("name 参数不可缺少!")
flag, name, text = await update_goods(**data)
if flag:
await shop_update_goods.send(f"修改商品 {name} 成功了...\n{text}", at_sender=True)
logger.info(f"USER {event.user_id} 修改商品 {name} 数据 {text} 成功")
else:
await shop_update_goods.send(f"修改商品 {name} 失败了...", at_sender=True)
logger.info(f"USER {event.user_id} 修改商品 {name} 数据 {text} 失败")
@@ -0,0 +1,269 @@
from models.goods_info import GoodsInfo
from utils.image_utils import BuildImage
from models.sign_group_user import SignGroupUser
from utils.utils import is_number
from configs.path_config import IMAGE_PATH
from typing import Optional, Union
from configs.config import Config
from nonebot import Driver
from nonebot.plugin import require
import nonebot
import time
driver: Driver = nonebot.get_driver()
use = require("use")
@driver.on_startup
async def init_default_shop_goods():
"""
导入内置的三个商品
"""
async def sign_card(**kwargs):
user_id = kwargs['user_id']
group_id = kwargs['group_id']
prob = kwargs["prob"]
user = await SignGroupUser.ensure(user_id, group_id)
await user.update(add_probability=prob).apply()
if Config.get_config("shop", "IMPORT_DEFAULT_SHOP_GOODS"):
await register_goods(
"好感度双倍加持卡Ⅰ", 30, "下次签到双倍好感度概率 + 10%(谁才是真命天子?)(同类商品将覆盖)"
)
use.register_use("好感度双倍加持卡Ⅰ", sign_card, **{"prob": 0.1})
await register_goods("好感度双倍加持卡Ⅱ", 150, "下次签到双倍好感度概率 + 20%(平平庸庸)(同类商品将覆盖)")
use.register_use("好感度双倍加持卡Ⅱ", sign_card, **{"prob": 0.2})
await register_goods(
"好感度双倍加持卡Ⅲ", 250, "下次签到双倍好感度概率 + 30%(金币才是真命天子!)(同类商品将覆盖)"
)
use.register_use("好感度双倍加持卡Ⅲ", sign_card, **{"prob": 0.3})
# 创建商店界面
async def create_shop_help() -> str:
"""
制作商店图片
:return: 图片base64
"""
goods_lst = await GoodsInfo.get_all_goods()
idx = 1
_dc = {}
font_h = BuildImage(0, 0).getsize("正")[1]
h = 10
_list = []
for goods in goods_lst:
if goods.goods_limit_time == 0 or time.time() < goods.goods_limit_time:
h += len(goods.goods_description.strip().split("\n")) * font_h + 80
_list.append(goods)
A = BuildImage(1000, h, color="#f9f6f2")
current_h = 0
for goods in _list:
bk = BuildImage(
700, 80, font_size=15, color="#f9f6f2", font="CJGaoDeGuo.otf"
)
goods_image = BuildImage(
600, 80, font_size=20, color="#a29ad6", font="CJGaoDeGuo.otf"
)
name_image = BuildImage(
580, 40, font_size=25, color="#e67b6b", font="CJGaoDeGuo.otf"
)
await name_image.atext(
(15, 0), f"{idx}.{goods.goods_name}", center_type="by_height"
)
await name_image.aline((380, -5, 280, 45), "#a29ad6", 5)
await name_image.atext((390, 0), "售价:", center_type="by_height")
await name_image.atext(
(440, 0), str(goods.goods_price), (255, 255, 255), center_type="by_height"
)
await name_image.atext(
(
440
+ BuildImage(0, 0, plain_text=str(goods.goods_price), font_size=25).w,
0,
),
" 金币",
center_type="by_height",
)
await name_image.acircle_corner(5)
await goods_image.apaste(name_image, (0, 5), True, center_type="by_width")
await goods_image.atext((15, 50), f"简介:{goods.goods_description}")
await goods_image.acircle_corner(20)
await bk.apaste(goods_image, alpha=True)
# 添加限时图标和时间
if goods.goods_limit_time > 0:
_limit_time_logo = BuildImage(40, 40, background=f"{IMAGE_PATH}/other/time.png")
await bk.apaste(_limit_time_logo, (600, 0), True)
await bk.apaste(BuildImage(0, 0, plain_text="限时!", font_size=23, font="CJGaoDeGuo.otf"), (640, 10), True)
limit_time = time.strftime("%Y-%m-%d %H:%M", time.localtime(goods.goods_limit_time)).split()
y_m_d = limit_time[0]
_h_m = limit_time[1].split(":")
h_m = _h_m[0] + "时 " + _h_m[1] + "分"
await bk.atext((605, 38), str(y_m_d))
await bk.atext((615, 57), str(h_m))
await bk.aline((550, -1, 710, -1), "#a29ad6", 5)
await bk.aline((550, 80, 710, 80), "#a29ad6", 5)
idx += 1
await A.apaste(bk, (0, current_h), True)
current_h += 90
w = 1000
h = A.h + 230 + 100
h = 1000 if h < 1000 else h
shop_logo = BuildImage(100, 100, background=f"{IMAGE_PATH}/other/shop_text.png")
shop = BuildImage(w, h, font_size=20, color="#f9f6f2")
shop.paste(A, (20, 230))
zx_img = BuildImage(0, 0, background=f"{IMAGE_PATH}/zhenxun/toukan.png")
zx_img.replace_color_tran(((240, 240, 240), (255, 255, 255)), (249, 246, 242))
await shop.apaste(zx_img, (780, 100))
await shop.apaste(shop_logo, (450, 30), True)
shop.text(
(int((1000 - shop.getsize("注【通过 序号 或者 商品名称 购买】")[0]) / 2), 170),
"注【通过 序号 或者 商品名称 购买】",
)
shop.text((20, h - 100), "神秘药水\t\t售价:9999999金币\n\t\t鬼知道会有什么效果~")
return shop.pic2bs4()
async def register_goods(
name: str,
price: int,
des: str,
discount: Optional[float] = 1,
limit_time: Optional[int] = 0,
**kwargs,
):
"""
添加商品
例如: 折扣:可选参数↓ 限时时间:可选,单位为小时
添加商品 name:萝莉酒杯 price:9999 des:普通的酒杯,但是里面.. discount:0.4 limit_time:90
添加商品 name:可疑的药 price:5 des:效果未知
:param name: 商品名称
:param price: 商品价格
:param des: 商品简介
:param discount: 商品折扣
:param limit_time: 商品限时销售时间,单位为小时
:param kwargs: kwargs
:return: 是否添加成功
"""
if kwargs:
name = kwargs.get("name")
price = kwargs.get("price")
des = kwargs.get("des")
discount = kwargs.get("discount")
limit_time = kwargs.get("time_limit")
limit_time = float(limit_time) if limit_time else limit_time
discount = discount if discount is None else 1
limit_time = int(time.time() + limit_time * 60 * 60) if limit_time is not None and limit_time != 0 else 0
return await GoodsInfo.add_goods(
name, int(price), des, float(discount), limit_time
)
# 删除商品
async def delete_goods(name: str, id_: int) -> "str, str, int":
"""
删除商品
:param name: 商品名称
:param id_: 商品id
:return: 删除状况
"""
goods_lst = await GoodsInfo.get_all_goods()
if id_:
if id_ < 1 or id_ > len(goods_lst):
return "序号错误,没有该序号商品...", "", 999
goods_name = goods_lst[id_ - 1].goods_name
if await GoodsInfo.delete_goods(goods_name):
return f"删除商品 {goods_name} 成功!", goods_name, 200
else:
return f"删除商品 {goods_name} 失败!", goods_name, 999
if name:
if await GoodsInfo.delete_goods(name):
return f"删除商品 {name} 成功!", name, 200
else:
return f"删除商品 {name} 失败!", name, 999
# 更新商品信息
async def update_goods(**kwargs) -> "str, str, int":
"""
更新商品信息
:param kwargs: kwargs
:return: 更新状况
"""
if kwargs:
goods_lst = await GoodsInfo.get_all_goods()
if is_number(kwargs["name"]):
if int(kwargs["name"]) < 1 or int(kwargs["name"]) > len(goods_lst):
return "序号错误,没有该序号的商品...", "", 999
goods = goods_lst[int(kwargs["name"]) - 1]
else:
goods = await GoodsInfo.get_goods_info(kwargs["name"])
if not goods:
return "名称错误,没有该名称的商品...", "", 999
name = goods.goods_name
price = goods.goods_price
des = goods.goods_description
discount = goods.goods_discount
limit_time = goods.goods_limit_time
new_time = 0
tmp = ""
if kwargs.get("price"):
tmp += f'价格:{price} --> {kwargs["price"]}\n'
price = kwargs["price"]
if kwargs.get("des"):
tmp += f'描述:{des} --> {kwargs["des"]}\n'
des = kwargs["des"]
if kwargs.get("discount"):
tmp += f'折扣:{discount} --> {kwargs["discount"]}\n'
discount = kwargs["discount"]
if kwargs.get("limit_time"):
new_time = time.strftime(
"%Y-%m-%d %H:%M:%S",
time.localtime(time.time() + int(kwargs["limit_time"] * 60 * 60)),
)
tmp += f"折扣至: {new_time}\n"
limit_time = kwargs["limit_time"]
return (
await GoodsInfo.update_goods(
name,
int(price),
des,
float(discount),
int(time.time() + limit_time * 60 * 60 if limit_time != 0 and new_time else 0),
),
name,
tmp[:-1],
)
def parse_goods_info(msg: str) -> Union[dict, str]:
"""
解析格式数据
:param msg: 消息
:return: 解析完毕的数据data
"""
if "name:" not in msg:
return "必须指定修改的商品名称或序号!"
data = {}
for x in msg.split():
sp = x.split(":", maxsplit=1)
if str(sp[1]).strip():
sp[1] = sp[1].strip()
print(sp)
if sp[0] == "name":
data["name"] = sp[1]
elif sp[0] == "price":
if not is_number(sp[1]) or int(sp[1]) < 0:
return "price参数不合法,必须大于等于0!"
data["price"] = sp[1]
elif sp[0] == "des":
data["des"] = sp[1]
elif sp[0] == "discount":
if not is_number(sp[1]) or float(sp[1]) < 0:
return "discount参数不合法,必须大于0!"
data["discount"] = sp[1]
elif sp[0] == "limit_time":
if not is_number(sp[1]) or float(sp[1]) < 0:
return "limit_time参数不合法,必须大于0!"
data["limit_time"] = sp[1]
return data
@@ -6,7 +6,8 @@ from utils.utils import is_number, get_message_text
from models.bag_user import BagUser from models.bag_user import BagUser
from nonebot.adapters.cqhttp.permission import GROUP from nonebot.adapters.cqhttp.permission import GROUP
from services.db_context import db from services.db_context import db
from .data_source import effect from nonebot.plugin import export
from .data_source import effect, register_use, func_manager
__zx_plugin_name__ = "商店 - 使用道具" __zx_plugin_name__ = "商店 - 使用道具"
@@ -14,7 +15,7 @@ __plugin_usage__ = """
usage: usage:
普通的使用道具 普通的使用道具
指令: 指令:
使用道具 [序号或道具名称] 使用道具 [序号或道具名称] ?[数量]=1
* 序号以 ”我的道具“ 为准 * * 序号以 ”我的道具“ 为准 *
""".strip() """.strip()
__plugin_des__ = "商店 - 使用道具" __plugin_des__ = "商店 - 使用道具"
@@ -29,6 +30,10 @@ __plugin_settings__ = {
"cmd": ["商店", "使用道具"], "cmd": ["商店", "使用道具"],
} }
# 导出方法供其他插件使用
export = export()
export.register_use = register_use
use_props = on_command("使用道具", priority=5, block=True, permission=GROUP) use_props = on_command("使用道具", priority=5, block=True, permission=GROUP)
@@ -37,35 +42,40 @@ async def _(bot: Bot, event: GroupMessageEvent, state: T_State):
msg = get_message_text(event.json()) msg = get_message_text(event.json())
if msg in ["", "帮助"]: if msg in ["", "帮助"]:
await use_props.finish(__plugin_usage__) await use_props.finish(__plugin_usage__)
props = await BagUser.get_props(event.user_id, event.group_id) num = 1
if props: msg_sp = msg.split()
if len(msg_sp) > 1 and is_number(msg_sp[-1]) and int(msg_sp[-1]) > 0:
num = int(msg.split()[-1])
msg = " ".join(msg.split()[:-1])
property_ = await BagUser.get_property(event.user_id, event.group_id)
if property_:
async with db.transaction(): async with db.transaction():
pname_list = []
props = props[:-1].split(",")
for p in props:
if p != "":
if p not in pname_list:
pname_list.append(p)
if is_number(msg): if is_number(msg):
if 0 < int(msg) <= len(pname_list): if 0 < int(msg) <= len(property_):
name = pname_list[int(msg) - 1] name = list(property_.keys())[int(msg) - 1]
else: else:
await use_props.finish("仔细看看自己的道具仓库有没有这个道具?", at_sender=True) await use_props.finish("仔细看看自己的道具仓库有没有这个道具?", at_sender=True)
else: else:
if msg not in pname_list: if msg not in property_.keys():
await use_props.finish("道具名称错误!", at_sender=True) await use_props.finish("道具名称错误!", at_sender=True)
name = msg name = msg
if await BagUser.del_props( _user_prop_count = property_[name]
event.user_id, event.group_id, name if num > _user_prop_count:
) and await effect(event.user_id, event.group_id, name): await use_props.finish(f"道具数量不足,无法使用{num}次!")
await use_props.send(f"使用道具 {name} 成功!", at_sender=True) if num > (n := func_manager.get_max_num_limit(name)):
await use_props.finish(f"该道具单次只能使用 {n} 个!")
if await BagUser.delete_property(
event.user_id, event.group_id, name, num
) and await effect(bot, event, name, num):
if func_manager.check_send_success_message(name):
await use_props.send(f"使用道具 {name} {num} 次成功!", at_sender=True)
logger.info( logger.info(
f"USER {event.user_id} GROUP {event.group_id} 使用道具 {name} 成功" f"USER {event.user_id} GROUP {event.group_id} 使用道具 {name} {num} 次成功"
) )
else: else:
await use_props.send(f"使用道具 {name} 失败!", at_sender=True) await use_props.send(f"使用道具 {name} {num} 次失败!", at_sender=True)
logger.info( logger.info(
f"USER {event.user_id} GROUP {event.group_id} 使用道具 {name} 失败" f"USER {event.user_id} GROUP {event.group_id} 使用道具 {name} {num} 次失败"
) )
else: else:
await use_props.send("您的背包里没有任何的道具噢~", at_sender=True) await use_props.send("您的背包里没有任何的道具噢", at_sender=True)
+119
View File
@@ -0,0 +1,119 @@
from nonebot.adapters.cqhttp import GroupMessageEvent
from services.log import logger
from nonebot.adapters.cqhttp import Bot
import asyncio
class GoodsUseFuncManager:
def __init__(self):
self._data = {}
def register_use(self, goods_name: str, **kwargs):
"""
注册商品使用方法
:param goods_name: 商品名称
:param kwargs: kwargs
"""
self._data[goods_name] = kwargs
def exists(self, goods_name: str) -> bool:
"""
判断商品使用方法是否被注册
:param goods_name: 商品名称
"""
return bool(self._data.get(goods_name))
def get_max_num_limit(self, goods_name: str) -> int:
"""
获取单次商品使用数量
:param goods_name: 商品名称
"""
if self.exists(goods_name):
return self._data[goods_name]["kwargs"]["_max_num_limit"]
return 1
async def use(self, **kwargs):
"""
使用道具
:param kwargs: kwargs
"""
goods_name = kwargs.get("goods_name")
if self.exists(goods_name):
if asyncio.iscoroutinefunction(self._data[goods_name]["func"]):
await self._data[goods_name]["func"](
**kwargs,
)
else:
self._data[goods_name]["func"](
**kwargs,
)
def check_send_success_message(self, goods_name: str) -> bool:
"""
检查是否发送使用成功信息
:param goods_name: 商品名称
"""
if self.exists(goods_name):
return bool(self._data[goods_name]["kwargs"]["send_success_msg"])
return False
def get_kwargs(self, goods_name: str) -> dict:
"""
获取商品使用方法的kwargs
:param goods_name: 商品名称
"""
if self.exists(goods_name):
return self._data[goods_name]["kwargs"]
return {}
func_manager = GoodsUseFuncManager()
async def effect(bot: Bot, event: GroupMessageEvent, goods_name: str, num: int) -> bool:
"""
商品生效
:param bot: Bot
:param event: GroupMessageEvent
:param goods_name: 商品名称
:param num: 使用数量
:return: 使用是否成功
"""
# 优先使用注册的商品插件
try:
if func_manager.exists(goods_name):
_kwargs = func_manager.get_kwargs(goods_name)
await func_manager.use(
**{
**_kwargs,
"_bot": bot,
"event": event,
"group_id": event.group_id,
"user_id": event.user_id,
"num": num,
"goods_name": goods_name,
}
)
return True
except Exception as e:
logger.error(f"use 商品生效函数effect 发生错误 {type(e)}:{e}")
return False
def register_use(goods_name: str, func, **kwargs):
"""
注册商品使用方法
:param goods_name: 商品名称
:param func: 使用函数
:param kwargs: kwargs
"""
if func_manager.exists(goods_name):
raise ValueError("该商品使用函数已被注册!")
# 发送使用成功信息
if kwargs.get("send_success_msg") is None:
kwargs["send_success_msg"] = True
kwargs["_max_num_limit"] = (
kwargs.get("_max_num_limit") if kwargs.get("_max_num_limit") else 1
)
func_manager.register_use(goods_name, **{"func": func, "kwargs": kwargs})
logger.info(f"register_use 成功注册商品:{goods_name} 的使用函数")
View File
View File
View File
View File
+38
View File
@@ -0,0 +1,38 @@
from nonebot import on_command
from nonebot.permission import SUPERUSER
from nonebot.typing import T_State
from nonebot.adapters.cqhttp import Bot, MessageEvent
from nonebot.rule import to_me
from services.db_context import db
from utils.utils import get_message_text
from services.log import logger
__zx_plugin_name__ = "执行sql [Superuser]"
__plugin_usage__ = """
usage:
执行一段sql语句
指令:
exec [sql语句]
""".strip()
__plugin_des__ = "执行一段sql语句"
__plugin_cmd__ = [
"exec [sql语句]",
]
__plugin_version__ = 0.1
__plugin_author__ = "HibiKier"
exec_ = on_command("exec", rule=to_me(), permission=SUPERUSER, priority=1, block=True)
@exec_.handle()
async def _(bot: Bot, event: MessageEvent, state: T_State):
sql = get_message_text(event.json())
async with db.transaction():
try:
query = db.text(sql)
await db.first(query)
await exec_.send("执行 sql 语句成功.")
except Exception as e:
await exec_.send(f"执行 sql 语句失败 {type(e)}:{e}")
logger.error(f"执行 sql 语句失败 {type(e)}:{e}")
View File
+5 -1
View File
@@ -15,6 +15,10 @@ __zx_plugin_name__ = "重载插件配置 [Superuser]"
__plugin_usage__ = """ __plugin_usage__ = """
usage: usage:
重载插件配置 重载插件配置
plugins2settings,
plugins2cd
plugins2block
group_manager
指令: 指令:
重载插件配置 重载插件配置
""".strip() """.strip()
@@ -27,7 +31,7 @@ __plugin_author__ = "HibiKier"
reload_plugins_manager = on_command( reload_plugins_manager = on_command(
"重载插件限制", rule=to_me(), permission=SUPERUSER, priority=1, block=True "重载配置", rule=to_me(), permission=SUPERUSER, priority=1, block=True
) )
+5 -6
View File
@@ -40,7 +40,7 @@ async def _(bot: Bot, event: GroupMessageEvent, state: T_State):
group_id = -1 group_id = -1
level = 0 level = 0
try: try:
args = get_message_text(event.json()).strip().split() args = get_message_text(event.json()).split()
qq = get_message_at(event.json()) qq = get_message_at(event.json())
flag = -1 flag = -1
if not qq: if not qq:
@@ -55,18 +55,17 @@ async def _(bot: Bot, event: GroupMessageEvent, state: T_State):
else: else:
await super_cmd.finish( await super_cmd.finish(
"权限参数不完全\n\t格式:添加/删除权限 [at] [level]" "权限参数不完全\n\t格式:添加/删除权限 [at] [level]"
"\n\t格式:添加/删除权限 [qq] [group] [level]", "\n\t格式:添加/删除权限 [qq] [group_id] [level]",
at_sender=True, at_sender=True,
) )
else: else:
if not is_number(args[0]):
await super_cmd.finish("所有参数必须是数字!", at_sender=True)
level = int(args[0])
qq = qq[0] qq = qq[0]
group_id = event.group_id group_id = event.group_id
flag = 2 flag = 2
if state["_prefix"]["raw_command"][:2] == "添加": if state["_prefix"]["raw_command"][:2] == "添加":
if is_number(args[0]):
level = int(args[0])
else:
await super_cmd.finish("权限等级必须是数字!", at_sender=True)
if await LevelUser.set_level(qq, group_id, level, 1): if await LevelUser.set_level(qq, group_id, level, 1):
result = "添加管理成功, 权限: " + str(level) result = "添加管理成功, 权限: " + str(level)
else: else:
View File
View File
View File
+4 -4
View File
@@ -1,4 +1,4 @@
from utils.image_utils import CreateImg from utils.image_utils import BuildImage
from configs.path_config import IMAGE_PATH from configs.path_config import IMAGE_PATH
from services.log import logger from services.log import logger
from utils.utils import get_matchers from utils.utils import get_matchers
@@ -32,7 +32,7 @@ def _create_help_image():
_plugin_name_list = [] _plugin_name_list = []
width = 0 width = 0
help_str = "超级用户帮助\n\n* 注: ‘*’ 代表可有多个相同参数 ‘?’ 代表可省略该参数 *\n\n" help_str = "超级用户帮助\n\n* 注: ‘*’ 代表可有多个相同参数 ‘?’ 代表可省略该参数 *\n\n"
tmp_img = CreateImg(0, 0, plain_text='1', font_size=24) tmp_img = BuildImage(0, 0, plain_text='1', font_size=24)
for matcher in _matchers: for matcher in _matchers:
plugin_name = "" plugin_name = ""
try: try:
@@ -72,8 +72,8 @@ def _create_help_image():
) )
height = len(help_str.split("\n")) * 33 height = len(help_str.split("\n")) * 33
width += 500 width += 500
A = CreateImg(width, height, font_size=24) A = BuildImage(width, height, font_size=24)
_background = CreateImg(width, height, background=background) _background = BuildImage(width, height, background=background)
A.text((300, 140), help_str) A.text((300, 140), help_str)
A.paste(_background, alpha=True) A.paste(_background, alpha=True)
A.save(superuser_help_image) A.save(superuser_help_image)
Regular → Executable
View File
+3 -1
View File
@@ -9,10 +9,12 @@ 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_plugin("nonebot_plugin_apscheduler") nonebot.load_plugin("nonebot_plugin_apscheduler")
nonebot.load_plugins("basic_plugins") nonebot.load_plugins("basic_plugins")
nonebot.load_plugins("plugins") nonebot.load_plugins("plugins")
# 最后加载权限控制
nonebot.load_plugins("basic_plugins/hooks")
if __name__ == "__main__": if __name__ == "__main__":
+2 -2
View File
@@ -17,8 +17,8 @@ address: str = "" # 数据库地址
port: str = "" # 数据库端口 port: str = "" # 数据库端口
database: str = "" # 数据库名称 database: str = "" # 数据库名称
# 全局代理,例如 "http://127.0.0.1:7890" # 代理,例如 "http://127.0.0.1:7890"
SYSTEM_PROXY: Optional[str] = None SYSTEM_PROXY: Optional[str] = None # 全局代理
Config = ConfigsManager(Path() / "data" / "configs" / "plugins2config.yaml") Config = ConfigsManager(Path() / "data" / "configs" / "plugins2config.yaml")
+22 -6
View File
@@ -29,7 +29,7 @@ class ConfigsManager:
name: Optional[str] = None, name: Optional[str] = None,
help_: Optional[str] = None, help_: Optional[str] = None,
default_value: Optional[str] = None, default_value: Optional[str] = None,
_override: bool = False _override: bool = False,
): ):
""" """
为插件添加一个配置,不会被覆盖,只有第一个生效 为插件添加一个配置,不会被覆盖,只有第一个生效
@@ -61,6 +61,7 @@ class ConfigsManager:
"name": name.strip() if isinstance(name, str) else name, "name": name.strip() if isinstance(name, str) else name,
"help": help_.strip() if isinstance(help_, str) else help_, "help": help_.strip() if isinstance(help_, str) else help_,
"default_value": default_value, "default_value": default_value,
"level_module": _module,
} }
def remove_plugin_config(self, module: str): def remove_plugin_config(self, module: str):
@@ -104,20 +105,35 @@ class ConfigsManager:
if self._data[module].get(key) is not None: if self._data[module].get(key) is not None:
self._data[module][key]["default_value"] = value self._data[module][key]["default_value"] = value
def get_config(self, module: str, key: str) -> Optional[Any]: def get_config(self, module: str, key: str, default: Optional[Any] = None) -> Optional[Any]:
""" """
获取指定配置值 获取指定配置值
:param module: 模块名 :param module: 模块名
:param key: 配置名称 :param key: 配置名称
:param default: 没有key值内容的默认返回值
""" """
key = key.upper() key = key.upper()
if module in self._data.keys(): if module in self._data.keys():
if self._data[module].get(key) is not None: for key in [key, f"{key} [LEVEL]"]:
if self._data[module][key]["value"] is None: if self._data[module].get(key) is not None:
return self._data[module][key]["default_value"] if self._data[module][key]["value"] is None:
return self._data[module][key]["value"] return self._data[module][key]["default_value"]
return self._data[module][key]["value"]
if default is not None:
return default
return None return None
def get_level2module(self, module: str, key: str) -> Optional[str]:
"""
获取指定key所绑定的module,一般为权限等级
:param module: 模块名
:param key: 配置名称
:return:
"""
if self._data.get(module) is not None:
if self._data[module].get(key) is not None:
return self._data[module][key].get("level_module")
def get(self, key: str): def get(self, key: str):
""" """
获取插件配置数据 获取插件配置数据
Binary file not shown.

After

Width:  |  Height:  |  Size: 772 KiB

BIN
View File
Binary file not shown.

After

Width:  |  Height:  |  Size: 694 KiB

Regular → Executable
+105 -110
View File
@@ -1,38 +1,39 @@
from services.db_context import db from services.db_context import db
from typing import Dict
from typing import Optional, List from typing import Optional, List
from services.log import logger
class BagUser(db.Model): class BagUser(db.Model):
__tablename__ = "bag_users" __tablename__ = "bag_users"
id = db.Column(db.Integer(), primary_key=True) id = db.Column(db.Integer(), primary_key=True)
user_qq = db.Column(db.BigInteger(), nullable=False) user_qq = db.Column(db.BigInteger(), nullable=False)
belonging_group = db.Column(db.BigInteger(), nullable=False) group_id = db.Column(db.BigInteger(), nullable=False)
gold = db.Column(db.Integer(), default=100) gold = db.Column(db.Integer(), default=100)
props = db.Column(db.TEXT(), nullable=False, default="") props = db.Column(db.TEXT(), nullable=False, default="") # 旧道具字段(废弃)
spend_total_gold = db.Column(db.Integer(), default=0) spend_total_gold = db.Column(db.Integer(), default=0)
get_total_gold = db.Column(db.Integer(), default=0) get_total_gold = db.Column(db.Integer(), default=0)
get_today_gold = db.Column(db.Integer(), default=0) get_today_gold = db.Column(db.Integer(), default=0)
spend_today_gold = db.Column(db.Integer(), default=0) spend_today_gold = db.Column(db.Integer(), default=0)
property = db.Column(db.JSON(), nullable=False, default={}) # 新道具字段
_idx1 = db.Index("bag_group_users_idx1", "user_qq", "belonging_group", unique=True) _idx1 = db.Index("bag_group_users_idx1", "user_qq", "group_id", unique=True)
@classmethod @classmethod
async def get_my_total_gold(cls, user_qq: int, belonging_group: int) -> str: async def get_user_total_gold(cls, user_qq: int, group_id: int) -> str:
""" """
说明: 说明:
获取金币概况 获取金币概况
参数: 参数:
:param user_qq: qq号 :param user_qq: qq号
:param belonging_group: 所在群号 :param group_id: 所在群号
""" """
query = cls.query.where( query = cls.query.where((cls.user_qq == user_qq) & (cls.group_id == group_id))
(cls.user_qq == user_qq) & (cls.belonging_group == belonging_group)
)
user = await query.gino.first() user = await query.gino.first()
if not user: if not user:
user = await cls.create( user = await cls.create(
user_qq=user_qq, user_qq=user_qq,
belonging_group=belonging_group, group_id=group_id,
) )
return ( return (
f"当前金币:{user.gold}\n今日获取金币:{user.get_today_gold}\n今日花费金币:{user.spend_today_gold}" f"当前金币:{user.gold}\n今日获取金币:{user.get_today_gold}\n今日花费金币:{user.spend_today_gold}"
@@ -41,179 +42,173 @@ class BagUser(db.Model):
) )
@classmethod @classmethod
async def get_gold(cls, user_qq: int, belonging_group: int) -> int: async def get_gold(cls, user_qq: int, group_id: int) -> int:
""" """
说明: 说明:
获取当前金币 获取当前金币
参数: 参数:
:param user_qq: qq号 :param user_qq: qq号
:param belonging_group: 所在群号 :param group_id: 所在群号
""" """
query = cls.query.where( query = cls.query.where((cls.user_qq == user_qq) & (cls.group_id == group_id))
(cls.user_qq == user_qq) & (cls.belonging_group == belonging_group)
)
user = await query.gino.first() user = await query.gino.first()
if user: if user:
return user.gold return user.gold
else: else:
await cls.create( await cls.create(
user_qq=user_qq, user_qq=user_qq,
belonging_group=belonging_group, group_id=group_id,
) )
return 100 return 100
@classmethod @classmethod
async def get_props(cls, user_qq: int, belonging_group: int) -> str: async def get_property(cls, user_qq: int, group_id: int) -> Dict[str, int]:
""" """
说明: 说明:
获取当前道具 获取当前道具
参数: 参数:
:param user_qq: qq号 :param user_qq: qq号
:param belonging_group: 所在群号 :param group_id: 所在群号
""" """
query = cls.query.where( query = cls.query.where((cls.user_qq == user_qq) & (cls.group_id == group_id))
(cls.user_qq == user_qq) & (cls.belonging_group == belonging_group)
)
user = await query.gino.first() user = await query.gino.first()
if user: if user:
return user.props return user.property
else: else:
await cls.create( await cls.create(
user_qq=user_qq, user_qq=user_qq,
belonging_group=belonging_group, group_id=group_id,
) )
return "" return {}
@classmethod @classmethod
async def add_gold(cls, user_qq: int, belonging_group: int, num: int) -> bool: async def add_gold(cls, user_qq: int, group_id: int, num: int):
""" """
说明: 说明:
增加金币 增加金币
参数: 参数:
:param user_qq: qq号 :param user_qq: qq号
:param belonging_group: 所在群号 :param group_id: 所在群号
:param num: 金币数量 :param num: 金币数量
""" """
query = cls.query.where( query = cls.query.where((cls.user_qq == user_qq) & (cls.group_id == group_id))
(cls.user_qq == user_qq) & (cls.belonging_group == belonging_group)
)
query = query.with_for_update() query = query.with_for_update()
user = await query.gino.first() user = await query.gino.first()
try: if user:
if user: await user.update(
await user.update( gold=user.gold + num,
gold=user.gold + num, get_total_gold=user.get_total_gold + num,
get_total_gold=user.get_total_gold + num, get_today_gold=user.get_today_gold + num,
get_today_gold=user.get_today_gold + num, ).apply()
).apply() else:
else: await cls.create(
await cls.create( user_qq=user_qq,
user_qq=user_qq, group_id=group_id,
belonging_group=belonging_group, gold=100 + num,
gold=100 + num, get_total_gold=num,
get_total_gold=num, get_today_gold=num,
get_today_gold=num, )
)
return True
except Exception:
return False
@classmethod @classmethod
async def spend_gold(cls, user_qq: int, belonging_group: int, num: int) -> bool: async def spend_gold(cls, user_qq: int, group_id: int, num: int):
""" """
说明: 说明:
花费金币 花费金币
参数: 参数:
:param user_qq: qq号 :param user_qq: qq号
:param belonging_group: 所在群号 :param group_id: 所在群号
:param num: 金币数量 :param num: 金币数量
""" """
query = cls.query.where( query = cls.query.where((cls.user_qq == user_qq) & (cls.group_id == group_id))
(cls.user_qq == user_qq) & (cls.belonging_group == belonging_group)
)
query = query.with_for_update() query = query.with_for_update()
user = await query.gino.first() user = await query.gino.first()
try: if user:
if user: await user.update(
await user.update( gold=user.gold - num,
gold=user.gold - num, spend_total_gold=user.spend_total_gold + num,
spend_total_gold=user.spend_total_gold + num, spend_today_gold=user.spend_today_gold + num,
spend_today_gold=user.spend_today_gold + num, ).apply()
).apply() else:
else: await cls.create(
await cls.create( user_qq=user_qq,
user_qq=user_qq, group_id=group_id,
belonging_group=belonging_group, gold=100 - num,
gold=100 - num, spend_total_gold=num,
spend_total_gold=num, spend_today_gold=num,
spend_today_gold=num, )
)
return True
except Exception:
return False
@classmethod @classmethod
async def add_props(cls, user_qq: int, belonging_group: int, name: str) -> bool: async def add_property(cls, user_qq: int, group_id: int, name: str):
""" """
说明: 说明:
增加道具 增加道具
参数: 参数:
:param user_qq: qq号 :param user_qq: qq号
:param belonging_group: 所在群号 :param group_id: 所在群号
:param name: 道具名称 :param name: 道具名称
""" """
query = cls.query.where( query = cls.query.where((cls.user_qq == user_qq) & (cls.group_id == group_id))
(cls.user_qq == user_qq) & (cls.belonging_group == belonging_group)
)
query = query.with_for_update() query = query.with_for_update()
user = await query.gino.first() user = await query.gino.first()
try: if user:
if user: p = user.property
await user.update(props=user.props + f"{name},").apply() if p.get(name) is None:
p[name] = 1
else: else:
await cls.create( p[name] += 1
user_qq=user_qq, belonging_group=belonging_group, props=f"{name}," await user.update(property=p).apply()
) else:
return True await cls.create(user_qq=user_qq, group_id=group_id, property={name: 1})
except Exception:
return False
@classmethod @classmethod
async def del_props(cls, user_qq: int, belonging_group: int, name: str) -> bool: async def delete_property(
cls, user_qq: int, group_id: int, name: str, num: int = 1
) -> bool:
""" """
说明: 说明:
使用道具 使用/删除 道具
参数: 参数:
:param user_qq: qq号 :param user_qq: qq号
:param belonging_group: 所在群号 :param group_id: 所在群号
:param name: 道具名称 :param name: 道具名称
:param num: 使用个数
""" """
query = cls.query.where( query = cls.query.where((cls.user_qq == user_qq) & (cls.group_id == group_id))
(cls.user_qq == user_qq) & (cls.belonging_group == belonging_group)
)
query = query.with_for_update() query = query.with_for_update()
user = await query.gino.first() user = await query.gino.first()
try: if user:
if user: property_ = user.property
rst = "" if name in property_:
props = user.props if property_.get(name) == num:
if props.find(name) != -1: del property_[name]
props = props.split(",")
try:
index = props.index(name)
except ValueError:
return False
props = props[:index] + props[index + 1 :]
for p in props:
if p != "":
rst += p + ","
await user.update(props=rst).apply()
return True
else: else:
return False property_[name] -= num
else: await user.update(property=property_).apply()
return False return True
except Exception: return False
@classmethod
async def buy_property(
cls, user_qq: int, group_id: int, goods: "GoodsInfo", goods_num: int
) -> bool:
"""
说明:
购买道具
参数:
:param user_qq: 用户qq
:param group_id: 所在群聊
:param goods: 商品
:param goods_num: 商品数量
"""
try:
# 折扣后金币
spend_gold = goods.goods_discount * goods.goods_price * goods_num
await BagUser.spend_gold(user_qq, group_id, spend_gold)
for _ in range(goods_num):
await BagUser.add_property(user_qq, group_id, goods.goods_name)
return True
except Exception as e:
logger.error(f"buy_property 发生错误 {type(e)}:{e}")
return False return False
@classmethod @classmethod
@@ -227,5 +222,5 @@ class BagUser(db.Model):
if not group_id: if not group_id:
query = await cls.query.gino.all() query = await cls.query.gino.all()
else: else:
query = await cls.query.where((cls.belonging_group == group_id)).gino.all() query = await cls.query.where((cls.group_id == group_id)).gino.all()
return query return query
Regular → Executable
+1 -1
View File
@@ -82,7 +82,7 @@ class BanUser(db.Model):
参数: 参数:
:param user_qq: 目标用户qq号 :param user_qq: 目标用户qq号
:param ban_level: 使用ban命令用户的权限 :param ban_level: 使用ban命令用户的权限
:param duration: ban时长 :param duration: ban时长,秒
""" """
query = cls.query.where((cls.user_qq == user_qq)) query = cls.query.where((cls.user_qq == user_qq))
query = query.with_for_update() query = query.with_for_update()
Regular → Executable
+7 -1
View File
@@ -1,4 +1,5 @@
from services.db_context import db from services.db_context import db
from configs.config import Config
class FriendUser(db.Model): class FriendUser(db.Model):
@@ -80,7 +81,12 @@ class FriendUser(db.Model):
user = await query.gino.first() user = await query.gino.first()
if user: if user:
if user.nickname: if user.nickname:
return user.nickname _tmp = ""
black_word = Config.get_config("nickname", "BLACK_WORD")
if black_word:
for x in user.nickname:
_tmp += "*" if x in black_word else x
return _tmp
return "" return ""
@classmethod @classmethod
@@ -1,5 +1,6 @@
from services.db_context import db from services.db_context import db
from typing import Optional, List from typing import Optional, List
from services.log import logger
class GoodsInfo(db.Model): class GoodsInfo(db.Model):
@@ -34,16 +35,18 @@ class GoodsInfo(db.Model):
:param goods_limit_time: 商品限时 :param goods_limit_time: 商品限时
""" """
try: try:
await cls.create( if not await cls.get_goods_info(goods_name):
goods_name=goods_name, await cls.create(
goods_price=goods_price, goods_name=goods_name,
goods_description=goods_description, goods_price=goods_price,
goods_discount=goods_discount, goods_description=goods_description,
goods_limit_time=goods_limit_time, goods_discount=goods_discount,
) goods_limit_time=goods_limit_time,
return True )
except Exception: return True
return False except Exception as e:
logger.error(f"GoodsInfo add_goods 发生错误 {type(e)}:{e}")
return False
@classmethod @classmethod
async def delete_goods(cls, goods_name: str) -> bool: async def delete_goods(cls, goods_name: str) -> bool:
@@ -99,7 +102,8 @@ class GoodsInfo(db.Model):
if goods_limit_time: if goods_limit_time:
await query.update(goods_limit_time=goods_limit_time).apply() await query.update(goods_limit_time=goods_limit_time).apply()
return True return True
except Exception: except Exception as e:
logger.error(f"GoodsInfo update_goods 发生错误 {type(e)}:{e}")
return False return False
@classmethod @classmethod
Regular → Executable
View File
Regular → Executable
+45 -29
View File
@@ -1,5 +1,5 @@
from datetime import datetime from datetime import datetime
from configs.config import Config
from services.db_context import db from services.db_context import db
from typing import List, Optional from typing import List, Optional
@@ -10,18 +10,18 @@ class GroupInfoUser(db.Model):
id = db.Column(db.Integer(), primary_key=True) id = db.Column(db.Integer(), primary_key=True)
user_qq = db.Column(db.BigInteger(), nullable=False) user_qq = db.Column(db.BigInteger(), nullable=False)
user_name = db.Column(db.Unicode(), nullable=False) user_name = db.Column(db.Unicode(), nullable=False)
belonging_group = db.Column(db.BigInteger(), nullable=False) group_id = 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()) 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", "group_id", unique=True)
@classmethod @classmethod
async def add_member_info( async def add_member_info(
cls, cls,
user_qq: int, user_qq: int,
belonging_group: int, group_id: int,
user_name: str, user_name: str,
user_join_time: datetime, user_join_time: datetime,
uid: Optional[int] = None, uid: Optional[int] = None,
@@ -31,20 +31,20 @@ class GroupInfoUser(db.Model):
添加群内用户信息 添加群内用户信息
参数: 参数:
:param user_qq: qq号 :param user_qq: qq号
:param belonging_group: 群号 :param group_id: 群号
:param user_name: 用户名称 :param user_name: 用户名称
:param user_join_time: 入群时间 :param user_join_time: 入群时间
:param uid: 用户唯一 id(自动生成) :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.group_id == group_id)
) )
try: try:
if not await query.gino.first(): if not await query.gino.first():
await cls.create( await cls.create(
user_qq=user_qq, user_qq=user_qq,
user_name=user_name, user_name=user_name,
belonging_group=belonging_group, group_id=group_id,
user_join_time=user_join_time, user_join_time=user_join_time,
uid=uid uid=uid
) )
@@ -54,31 +54,31 @@ class GroupInfoUser(db.Model):
@classmethod @classmethod
async def get_member_info( async def get_member_info(
cls, user_qq: int, belonging_group: int cls, user_qq: int, group_id: int
) -> "GroupInfoUser": ) -> "GroupInfoUser":
""" """
说明: 说明:
查询群员信息 查询群员信息
参数: 参数:
:param user_qq: qq号 :param user_qq: qq号
:param belonging_group: 群号 :param group_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.group_id == group_id)
) )
return await query.gino.first() return await query.gino.first()
@classmethod @classmethod
async def delete_member_info(cls, user_qq: int, belonging_group: int) -> bool: async def delete_member_info(cls, user_qq: int, group_id: int) -> bool:
""" """
说明: 说明:
删除群员信息 删除群员信息
参数: 参数:
:param user_qq: qq号 :param user_qq: qq号
:param belonging_group: 群号 :param group_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.group_id == group_id)
) )
query = query.with_for_update() query = query.with_for_update()
user = await query.gino.first() user = await query.gino.first()
@@ -87,40 +87,40 @@ class GroupInfoUser(db.Model):
return True return True
else: else:
await cls.delete.where( await cls.delete.where(
(cls.user_qq == user_qq) & (cls.belonging_group == belonging_group) (cls.user_qq == user_qq) & (cls.group_id == group_id)
).gino.status() ).gino.status()
return True return True
except Exception: except Exception:
return False return False
@classmethod @classmethod
async def get_group_member_id_list(cls, belonging_group: int) -> List[int]: async def get_group_member_id_list(cls, group_id: int) -> List[int]:
""" """
说明: 说明:
获取该群所有用户qq 获取该群所有用户qq
参数: 参数:
:param belonging_group: 群号 :param group_id: 群号
""" """
member_list = [] member_list = []
query = cls.query.where((cls.belonging_group == belonging_group)) query = cls.query.where((cls.group_id == group_id))
for user in await query.gino.all(): for user in await query.gino.all():
member_list.append(user.user_qq) member_list.append(user.user_qq)
return member_list return member_list
@classmethod @classmethod
async def set_group_member_nickname( async def set_group_member_nickname(
cls, user_qq: int, belonging_group: int, nickname: str cls, user_qq: int, group_id: int, nickname: str
) -> bool: ) -> bool:
""" """
说明: 说明:
设置群员在该群内的昵称 设置群员在该群内的昵称
参数: 参数:
:param user_qq: qq号 :param user_qq: qq号
:param belonging_group: 群号 :param group_id: 群号
:param nickname: 昵称 :param nickname: 昵称
""" """
query = cls.query.where( query = cls.query.where(
(cls.user_qq == user_qq) & (cls.belonging_group == belonging_group) (cls.user_qq == user_qq) & (cls.group_id == group_id)
) )
user = await query.with_for_update().gino.first() user = await query.with_for_update().gino.first()
if user: if user:
@@ -129,30 +129,46 @@ class GroupInfoUser(db.Model):
return False return False
@classmethod @classmethod
async def get_group_member_nickname(cls, user_qq: int, belonging_group: int) -> str: async def get_user_all_group(cls, user_qq: int) -> List[int]:
"""
获取该用户所在的所有群聊
:param user_qq: 用户qq
"""
query = await cls.query.where(cls.user_qq == user_qq).gino.all()
if query:
query = [x.group_id for x in query]
return query
@classmethod
async def get_group_member_nickname(cls, user_qq: int, group_id: int) -> str:
""" """
说明: 说明:
获取用户在该群的昵称 获取用户在该群的昵称
参数: 参数:
:param user_qq: qq号 :param user_qq: qq号
:param belonging_group: 群号 :param group_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.group_id == group_id)
) )
user = await query.gino.first() user = await query.gino.first()
if user: if user:
if user.nickname: if user.nickname:
return user.nickname _tmp = ""
black_word = Config.get_config("nickname", "BLACK_WORD")
if black_word:
for x in user.nickname:
_tmp += "*" if x in black_word else x
return _tmp
return "" return ""
@classmethod @classmethod
async def get_group_member_uid(cls, user_qq: int, belonging_group: int) -> Optional[str]: async def get_group_member_uid(cls, user_qq: int, group_id: int) -> Optional[str]:
query = cls.query.where( query = cls.query.where(
(cls.user_qq == user_qq) & (cls.belonging_group == belonging_group) (cls.user_qq == user_qq) & (cls.group_id == group_id)
) )
user = await query.gino.first() user = await query.gino.first()
_max_uid = cls.query.where((cls.user_qq == 114514) & (cls.belonging_group == 114514)).with_for_update() _max_uid = cls.query.where((cls.user_qq == 114514) & (cls.group_id == 114514)).with_for_update()
_max_uid_user = await _max_uid.gino.first() _max_uid_user = await _max_uid.gino.first()
_max_uid = _max_uid_user.uid _max_uid = _max_uid_user.uid
if not user or not user.uid: if not user or not user.uid:
@@ -162,9 +178,9 @@ class GroupInfoUser(db.Model):
return x.uid return x.uid
else: else:
if not user: if not user:
await GroupInfoUser.add_member_info(user_qq, belonging_group, '', datetime.min) await GroupInfoUser.add_member_info(user_qq, group_id, '', datetime.min)
user = await cls.query.where( user = await cls.query.where(
(cls.user_qq == user_qq) & (cls.belonging_group == belonging_group) (cls.user_qq == user_qq) & (cls.group_id == group_id)
).gino.first() ).gino.first()
await user.update( await user.update(
uid=_max_uid + 1, uid=_max_uid + 1,
Regular → Executable
View File
Regular → Executable
+39 -12
View File
@@ -1,5 +1,5 @@
from datetime import datetime from datetime import datetime
from typing import List
from services.db_context import db from services.db_context import db
@@ -8,8 +8,7 @@ class SignGroupUser(db.Model):
id = db.Column(db.Integer(), primary_key=True) id = db.Column(db.Integer(), primary_key=True)
user_qq = db.Column(db.BigInteger(), nullable=False) user_qq = db.Column(db.BigInteger(), nullable=False)
belonging_group = db.Column(db.BigInteger(), nullable=False) group_id = db.Column(db.BigInteger(), nullable=False)
checkin_count = db.Column(db.Integer(), nullable=False) checkin_count = db.Column(db.Integer(), nullable=False)
checkin_time_last = db.Column(db.DateTime(timezone=True), nullable=False) checkin_time_last = db.Column(db.DateTime(timezone=True), nullable=False)
impression = db.Column(db.Numeric(scale=3, asdecimal=False), nullable=False) impression = db.Column(db.Numeric(scale=3, asdecimal=False), nullable=False)
@@ -20,28 +19,56 @@ class SignGroupUser(db.Model):
db.Numeric(scale=3, asdecimal=False), nullable=False, default=0 db.Numeric(scale=3, asdecimal=False), nullable=False, default=0
) )
_idx1 = db.Index("sign_group_users_idx1", "user_qq", "belonging_group", unique=True) _idx1 = db.Index("sign_group_users_idx1", "user_qq", "group_id", unique=True)
@classmethod @classmethod
async def ensure( async def ensure(
cls, user_qq: int, belonging_group: int, for_update: bool = False cls, user_qq: int, group_id: int, for_update: bool = False
) -> "SignGroupUser": ) -> "SignGroupUser":
"""
说明:
获取签到用户
参数:
:param user_qq: 用户qq
:param group_id: 所在群聊
:param for_update: 是否存在修改数据
"""
query = cls.query.where( query = cls.query.where(
(cls.user_qq == user_qq) & (cls.belonging_group == belonging_group) (cls.user_qq == user_qq) & (cls.group_id == group_id)
) )
if for_update: if for_update:
query = query.with_for_update() query = query.with_for_update()
user = await query.gino.first() user = await query.gino.first()
return user or await cls.create( return user or await cls.create(
user_qq=user_qq, user_qq=user_qq,
belonging_group=belonging_group, group_id=group_id,
checkin_count=0, checkin_count=0,
checkin_time_last=datetime.min, # 从未签到过 checkin_time_last=datetime.min, # 从未签到过
impression=0, impression=0,
) )
@classmethod
async def get_user_all_data(cls, user_qq: int) -> List["SignGroupUser"]:
"""
说明:
获取某用户所有数据
参数:
:param user_qq: 用户qq
"""
query = cls.query.where(cls.user_qq == user_qq)
query = query.with_for_update()
return await query.gino.all()
@classmethod @classmethod
async def sign(cls, user: "SignGroupUser", impression: float, checkin_time_last: datetime): async def sign(cls, user: "SignGroupUser", impression: float, checkin_time_last: datetime):
"""
说明:
签到
说明:
:param user: 用户
:param impression: 增加的好感度
:param checkin_time_last: 签到时间
"""
await user.update( await user.update(
checkin_count=user.checkin_count + 1, checkin_count=user.checkin_count + 1,
checkin_time_last=checkin_time_last, checkin_time_last=checkin_time_last,
@@ -51,22 +78,22 @@ class SignGroupUser(db.Model):
).apply() ).apply()
@classmethod @classmethod
async def get_all_impression(cls, belonging_group: int) -> "list, list, list": async def get_all_impression(cls, group_id: int) -> "list, list, list":
""" """
说明: 说明:
获取该群所有用户 id 及对应 好感度 获取该群所有用户 id 及对应 好感度
参数: 参数:
:param belonging_group: 群号 :param group_id: 群号
""" """
impression_list = [] impression_list = []
user_qq_list = [] user_qq_list = []
user_group = [] user_group = []
if belonging_group: if group_id:
query = cls.query.where(cls.belonging_group == belonging_group) query = cls.query.where(cls.group_id == group_id)
else: else:
query = cls.query query = cls.query
for user in await query.gino.all(): for user in await query.gino.all():
impression_list.append(user.impression) impression_list.append(user.impression)
user_qq_list.append(user.user_qq) user_qq_list.append(user.user_qq)
user_group.append(user.belonging_group) user_group.append(user.group_id)
return user_qq_list, impression_list, user_group return user_qq_list, impression_list, user_group
View File
Regular → Executable
View File
+45
View File
@@ -0,0 +1,45 @@
from nonebot import on_regex
from nonebot.adapters.cqhttp import Bot, MessageEvent
from nonebot.typing import T_State
from nonebot.rule import to_me
from pathlib import Path
__zx_plugin_name__ = "关于"
__plugin_usage__ = """
usage:
想要更加了解真寻吗
指令:
关于
""".strip()
__plugin_des__ = "想要更加了解真寻吗"
__plugin_cmd__ = ["关于"]
__plugin_version__ = 0.1
__plugin_type__ = ("其他",)
__plugin_author__ = "HibiKier"
__plugin_settings__ = {
"level": 1,
"default_status": True,
"limit_superuser": False,
"cmd": ["关于"],
}
about = on_regex("^关于$", priority=5, block=True, rule=to_me())
@about.handle()
async def _(bot: Bot, event: MessageEvent, state: T_State):
ver_file = Path() / '__version__'
version = None
if ver_file.exists():
with open(ver_file, 'r', encoding='utf8') as f:
version = f.read().split(':')[-1].strip()
msg = f"""
『绪山真寻Bot』
版本:{version}
简介:基于Nonebot2与go-cqhttp开发,是一个非常可爱的Bot呀,希望与大家要好好相处
项目地址:https://github.com/HibiKier/zhenxun_bot
文档地址:https://hibikier.github.io/zhenxun_bot/
""".strip()
await about.send(msg)
Regular → Executable
View File
Regular → Executable
+14 -19
View File
@@ -11,7 +11,7 @@ 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 services.log import logger
from utils.utils import get_message_text, get_message_imgs from utils.utils import get_message_text, get_message_img
from .data_source import get_chat_result, hello, no_result from .data_source import get_chat_result, hello, no_result
from configs.config import NICKNAME, Config from configs.config import NICKNAME, Config
@@ -21,29 +21,22 @@ usage:
与{NICKNAME}普普通通的对话吧! 与{NICKNAME}普普通通的对话吧!
""" """
__plugin_version__ = 0.1 __plugin_version__ = 0.1
__plugin_author__ = 'HibiKier' __plugin_author__ = "HibiKier"
__plugin_settings__ = { __plugin_settings__ = {
"level": 5, "level": 5,
"default_status": True,
"limit_superuser": False,
"cmd": ["Ai", "ai", "AI", "aI"], "cmd": ["Ai", "ai", "AI", "aI"],
} }
__plugin_configs__ = { __plugin_configs__ = {
"TL_KEY": { "TL_KEY": {"value": [], "help": "图灵Key"},
"value": [], "ALAPI_AI_CHECK": {"value": False, "help": "是否检测青云客骂娘回复", "default_value": False},
"help": "图灵Key" "TEXT_FILTER": {
"value": ["鸡", "口交"],
"help": "文本过滤器,将敏感词更改为*",
"default_value": [],
}, },
"ALAPI_AI_CHECK": {
"value": False,
"help": "是否检测青云客骂娘回复",
"default_value": False
}
} }
Config.add_plugin_config( Config.add_plugin_config(
"alapi", "alapi", "ALAPI_TOKEN", None, help_="在https://admin.alapi.cn/user/login登录后获取token"
"ALAPI_TOKEN",
None,
help_="在https://admin.alapi.cn/user/login登录后获取token"
) )
ai = on_message(rule=to_me(), priority=8) ai = on_message(rule=to_me(), priority=8)
@@ -52,11 +45,11 @@ ai = on_message(rule=to_me(), priority=8)
@ai.handle() @ai.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())
imgs = get_message_imgs(event.json()) img = get_message_img(event.json())
if "CQ:xml" in str(event.get_message()): if "CQ:xml" in str(event.get_message()):
return return
# 打招呼 # 打招呼
if (not msg and not imgs) or msg in [ if (not msg and not img) or msg in [
"你好啊", "你好啊",
"你好", "你好",
"在吗", "在吗",
@@ -67,7 +60,7 @@ async def _(bot: Bot, event: MessageEvent, state: T_State):
"在", "在",
]: ]:
await ai.finish(hello()) await ai.finish(hello())
img = imgs[0] if imgs else "" img = img[0] if img else ""
if isinstance(event, GroupMessageEvent): if isinstance(event, GroupMessageEvent):
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
@@ -85,6 +78,8 @@ async def _(bot: Bot, event: MessageEvent, state: T_State):
f"问题:{msg} ---- 回答:{result}" f"问题:{msg} ---- 回答:{result}"
) )
if result: if result:
for t in Config.get_config("ai", "TEXT_FILTER"):
result = result.replace(t, "*")
await ai.finish(Message(result)) await ai.finish(Message(result))
else: else:
await ai.finish(no_result()) await ai.finish(no_result())
Regular → Executable
+64 -65
View File
@@ -1,13 +1,12 @@
import os import os
import random import random
import re import re
from utils.http_utils import AsyncHttpx
import aiohttp
from aiohttp.client import ClientSession
from configs.path_config import IMAGE_PATH, DATA_PATH from configs.path_config import IMAGE_PATH, DATA_PATH
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 configs.config import Config, NICKNAME from configs.config import Config, NICKNAME
from .utils import ai_message_manager
try: try:
import ujson as json import ujson as json
@@ -26,7 +25,7 @@ anime_data = json.load(open(DATA_PATH + "anime.json", "r", encoding="utf8"))
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 返回值,顺序:图灵 -> 青云客 获取 AI 返回值,顺序: 特殊回复 -> 图灵 -> 青云客
:param text: 问题 :param text: 问题
:param img_url: 图片链接 :param img_url: 图片链接
:param user_id: 用户id :param user_id: 用户id
@@ -34,6 +33,11 @@ async def get_chat_result(text: str, img_url: str, user_id: int, nickname: str)
:return: 回答 :return: 回答
""" """
global index global index
ai_message_manager.add_message(user_id, text)
special_rst = await ai_message_manager.get_result(user_id, nickname)
if special_rst:
ai_message_manager.add_result(user_id, special_rst)
return special_rst
if index == 5: if index == 5:
index = 0 index = 0
if len(text) < 6 and random.random() < 0.6: if len(text) < 6 and random.random() < 0.6:
@@ -41,10 +45,9 @@ async def get_chat_result(text: str, img_url: str, user_id: int, nickname: str)
for key in keys: for key in keys:
if text.find(key) != -1: if text.find(key) != -1:
return random.choice(anime_data[key]).replace("你", nickname) return random.choice(anime_data[key]).replace("你", nickname)
async with aiohttp.ClientSession() as sess: rst = await tu_ling(text, img_url, user_id)
rst = await tu_ling(text, img_url, user_id, sess) if not rst:
if not rst: rst = await xie_ai(text)
rst = await xie_ai(text, sess)
if not rst: if not rst:
return no_result() return no_result()
if nickname: if nickname:
@@ -55,23 +58,24 @@ async def get_chat_result(text: str, img_url: str, user_id: int, nickname: str)
if nickname.find("大人") == -1: if nickname.find("大人") == -1:
nickname += "大~人~" nickname += "大~人~"
rst = rst.replace("小主人", nickname).replace("小朋友", nickname) rst = rst.replace("小主人", nickname).replace("小朋友", nickname)
ai_message_manager.add_result(user_id, rst)
return rst return rst
# 图灵接口 # 图灵接口
async def tu_ling(text: str, img_url: str, user_id: int, sess: ClientSession) -> str: async def tu_ling(text: str, img_url: str, user_id: int) -> str:
""" """
获取图灵接口的回复 获取图灵接口的回复
:param text: 问题 :param text: 问题
:param img_url: 图片链接 :param img_url: 图片链接
:param user_id: 用户id :param user_id: 用户id
:param sess: AIOHTTP SESSION
:return: 图灵回复 :return: 图灵回复
""" """
global index global index
TL_KEY = Config.get_config("ai", "TL_KEY") TL_KEY = Config.get_config("ai", "TL_KEY")
req = None
if not TL_KEY: if not TL_KEY:
return '' return ""
try: try:
if text: if text:
req = { req = {
@@ -98,62 +102,59 @@ async def tu_ling(text: str, img_url: str, user_id: int, sess: ClientSession) ->
index = 0 index = 0
return "" return ""
text = "" text = ""
async with sess.post(url, json=req) as response: response = await AsyncHttpx.post(url, json=req)
if response.status != 200: if response.status_code != 200:
return no_result() return no_result()
resp_payload = json.loads(await response.text()) resp_payload = json.loads(response.text)
if int(resp_payload["intent"]["code"]) in [4003]: if int(resp_payload["intent"]["code"]) in [4003]:
return "" return ""
if resp_payload["results"]: if resp_payload["results"]:
for result in resp_payload["results"]: for result in resp_payload["results"]:
if result["resultType"] == "text": if result["resultType"] == "text":
text = result["values"]["text"] text = result["values"]["text"]
if "请求次数超过" in text: if "请求次数超过" in text:
text = "" text = ""
return text return text
# 屑 AI # 屑 AI
async def xie_ai(text: str, sess: ClientSession) -> str: async def xie_ai(text: str) -> str:
""" """
获取青云客回复 获取青云客回复
:param text: 问题 :param text: 问题
:param sess: AIOHTTP SESSION
:return: 青云可回复 :return: 青云可回复
""" """
async with sess.get( res = await AsyncHttpx.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}" content = ""
) as res: data = json.loads(res.text)
content = "" if data["result"] == 0:
data = json.loads(await res.text()) content = data["content"]
if data["result"] == 0: if "菲菲" in content:
content = data["content"] content = content.replace("菲菲", NICKNAME)
if "菲菲" in content: if "艳儿" in content:
content = content.replace("菲菲", NICKNAME) content = content.replace("艳儿", NICKNAME)
if "艳儿" in content: if "公众号" in content:
content = content.replace("艳儿", NICKNAME) content = ""
if "公众号" in content: if "{br}" in content:
content = "" content = content.replace("{br}", "\n")
if "{br}" in content: if "提示" in content:
content = content.replace("{br}", "\n") content = content[: content.find("提示")]
if "提示" in content: if "淘宝" in content:
content = content[: content.find("提示")] return ""
if "淘宝" in content: while True:
return "" r = re.search("{face:(.*)}", content)
while True: if r:
r = re.search("{face:(.*)}", content) id_ = r.group(1)
if r: content = content.replace(
id_ = r.group(1) "{" + f"face:{id_}" + "}", str(face(int(id_)))
content = content.replace( )
"{" + f"face:{id_}" + "}", str(face(int(id_))) else:
) break
else: return (
break content
return ( if not content and not Config.get_config("ai", "ALAPI_AI_CHECK")
content else await check_text(content)
if not content and not Config.get_config("ai", "ALAPI_AI_CHECK") )
else await check_text(content, sess)
)
def hello() -> str: def hello() -> str:
@@ -196,21 +197,19 @@ def no_result() -> str:
) )
async def check_text(text: str, sess: ClientSession) -> str: async def check_text(text: str) -> str:
""" """
ALAPI文本检测,主要针对青云客API,检测为恶俗文本改为无回复的回答 ALAPI文本检测,主要针对青云客API,检测为恶俗文本改为无回复的回答
:param text: 回复 :param text: 回复
:param sess: AIOHTTP SESSION
""" """
if not Config.get_config("alapi", "ALAPI_TOKEN"): if not Config.get_config("alapi", "ALAPI_TOKEN"):
return text return text
params = {"token": Config.get_config("alapi", "ALAPI_TOKEN"), "text": text} params = {"token": Config.get_config("alapi", "ALAPI_TOKEN"), "text": text}
try: try:
async with sess.get(check_url, timeout=2, params=params) as response: data = (await AsyncHttpx.get(check_url, timeout=2, params=params)).json()
data = await response.json() if data["code"] == 200:
if data["code"] == 200: if data["data"]["conclusion_type"] == 2:
if data["data"]["conclusion_type"] == 2: return ""
return ""
except Exception as e: except Exception as e:
logger.error(f"检测违规文本错误...{type(e)}:{e}") logger.error(f"检测违规文本错误...{type(e)}:{e}")
return text return text
+140
View File
@@ -0,0 +1,140 @@
from utils.manager import StaticData
from configs.config import NICKNAME
from models.ban_user import BanUser
from typing import Optional
import random
import time
class AiMessageManager(StaticData):
def __init__(self):
super().__init__(None)
self._same_message = [
"为什么要发一样的话?",
"请不要再重复对我说一句话了,不然我就要生气了!",
"别再发这句话了,我已经知道了...",
"你是只会说这一句话吗?",
"[*],你发我也发!",
"[uname],[*]",
f"救命!有笨蛋一直给{NICKNAME}发一样的话!",
"这句话你已经给我发了{}次了,再发就生气!",
]
self._repeat_message = [
f"请不要学{NICKNAME}说话",
f"为什么要一直学{NICKNAME}说话?",
"你再学!你再学我就生气了!",
f"呜呜,你是想欺负{NICKNAME}嘛..",
"[uname]不要再学我说话了!",
"再学我说话,我就把你拉进黑名单(生气",
"你再学![uname]是个笨蛋!",
"你已经学我说话{}次了!别再学了!",
]
def add_message(self, user_id: int, message: str):
"""
添加用户消息
:param user_id: 用户id
:param message: 消息内容
"""
if message:
if self._data.get(user_id) is None:
self._data[user_id] = {
"time": time.time(),
"message": [],
"result": [],
"repeat_count": 0,
}
if time.time() - self._data[user_id]["time"] > 60 * 10:
self._data[user_id]["message"].clear()
self._data[user_id]["time"] = time.time()
self._data[user_id]["message"].append(message.strip())
def add_result(self, user_id: int, message: str):
"""
添加回复用户的消息
:param user_id: 用户id
:param message: 回复消息内容
"""
if message:
if self._data.get(user_id) is None:
self._data[user_id] = {
"time": time.time(),
"message": [],
"result": [],
"repeat_count": 0,
}
if time.time() - self._data[user_id]["time"] > 60 * 10:
self._data[user_id]["result"].clear()
self._data[user_id]["repeat_count"] = 0
self._data[user_id]["time"] = time.time()
self._data[user_id]["result"].append(message.strip())
async def get_result(self, user_id: int, nickname: str) -> Optional[str]:
"""
特殊消息特殊回复
:param user_id: 用户id
:param nickname: 用户昵称
"""
try:
if len(self._data[user_id]["message"]) < 2:
return None
except KeyError:
return None
msg = await self._get_user_repeat_message_result(user_id)
if not msg:
msg = await self._get_user_same_message_result(user_id)
if msg:
if "[uname]" in msg:
msg = msg.replace("[uname]", nickname)
if not msg.startswith("生气了!你好烦,闭嘴!") and "[*]" in msg:
msg = msg.replace("[*]", self._data[user_id]["message"][-1])
return msg
async def _get_user_same_message_result(self, user_id: int) -> Optional[str]:
"""
重复消息回复
:param user_id: 用户id
"""
msg = self._data[user_id]["message"][-1]
cnt = 0
_tmp = self._data[user_id]["message"][:-1]
_tmp.reverse()
for s in _tmp:
if s == msg:
cnt += 1
else:
break
if cnt > 1:
if random.random() < 0.5 and cnt > 3:
rand = random.randint(60, 300)
await BanUser.ban(user_id, 9, rand)
self._data[user_id]["message"].clear()
return f"生气了!你好烦,闭嘴!给我老实安静{rand}秒"
return random.choice(self._same_message).format(cnt)
return None
async def _get_user_repeat_message_result(self, user_id: int) -> Optional[str]:
"""
复读真寻的消息回复
:param user_id: 用户id
"""
msg = self._data[user_id]["message"][-1]
if self._data[user_id]["result"]:
rst = self._data[user_id]["result"][-1]
else:
return None
if msg == rst:
self._data[user_id]["repeat_count"] += 1
cnt = self._data[user_id]["repeat_count"]
if cnt > 1:
if random.random() < 0.5 and cnt > 3:
rand = random.randint(60, 300)
await BanUser.ban(user_id, 9, rand)
self._data[user_id]["result"].clear()
self._data[user_id]["repeat_count"] = 0
return f"生气了!你好烦,闭嘴!给我老实安静{rand}秒"
return random.choice(self._repeat_message).format(cnt)
return None
ai_message_manager = AiMessageManager()
Regular → Executable
+4 -1
View File
@@ -1,7 +1,6 @@
from configs.config import Config from configs.config import Config
import nonebot import nonebot
nonebot.load_plugins("plugins/alapi")
Config.add_plugin_config( Config.add_plugin_config(
"alapi", "alapi",
@@ -9,3 +8,7 @@ Config.add_plugin_config(
None, None,
help_="在https://admin.alapi.cn/user/login登录后获取token" help_="在https://admin.alapi.cn/user/login登录后获取token"
) )
nonebot.load_plugins("plugins/alapi")
Regular → Executable
View File
Regular → Executable
View File
Regular → Executable
+16 -18
View File
@@ -1,10 +1,10 @@
from nonebot.adapters.cqhttp import MessageSegment from nonebot.adapters.cqhttp import MessageSegment
from utils.image_utils import CreateImg from utils.image_utils import BuildImage
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 Optional from typing import Optional
from configs.config import Config from configs.config import Config
import aiohttp from utils.http_utils import AsyncHttpx
async def get_data(url: str, params: Optional[dict] = None) -> "Union[dict, str], int": async def get_data(url: str, params: Optional[dict] = None) -> "Union[dict, str], int":
@@ -16,18 +16,16 @@ async def get_data(url: str, params: Optional[dict] = None) -> "Union[dict, str]
if not params: if not params:
params = {} params = {}
params["token"] = Config.get_config("alapi", "ALAPI_TOKEN") params["token"] = Config.get_config("alapi", "ALAPI_TOKEN")
async with aiohttp.ClientSession() as session: try:
try: data = (await AsyncHttpx.get(url, params=params, timeout=5)).json()
async with session.get(url, timeout=2, params=params) as response: if data["code"] == 200:
data = await response.json() if not data["data"]:
if data["code"] == 200: return "没有搜索到...", 997
if not data["data"]: return data, 200
return "没有搜索到...", 997 else:
return data, 200 return f'发生了错误...code:{data["code"]}', 999
else: except TimeoutError:
return f'发生了错误...code:{data["code"]}', 999 return "超时了....", 998
except TimeoutError:
return "超时了....", 998
def gen_wbtop_pic(data: dict) -> MessageSegment: def gen_wbtop_pic(data: dict) -> MessageSegment:
@@ -35,14 +33,14 @@ def gen_wbtop_pic(data: dict) -> MessageSegment:
生成微博热搜图片 生成微博热搜图片
:param data: 微博热搜数据 :param data: 微博热搜数据
""" """
bk = CreateImg(700, 32 * 50 + 280, 700, 32, color="#797979") bk = BuildImage(700, 32 * 50 + 280, 700, 32, color="#797979")
wbtop_bk = CreateImg(700, 280, background=f"{IMAGE_PATH}/other/webtop.png") wbtop_bk = BuildImage(700, 280, background=f"{IMAGE_PATH}/other/webtop.png")
bk.paste(wbtop_bk) bk.paste(wbtop_bk)
text_bk = CreateImg(700, 32 * 50, 700, 32, color="#797979") text_bk = BuildImage(700, 32 * 50, 700, 32, color="#797979")
for i, data in enumerate(data): for i, data in enumerate(data):
title = f"{i+1}. {data['hot_word']}" title = f"{i+1}. {data['hot_word']}"
hot = data["hot_word_num"] hot = data["hot_word_num"]
img = CreateImg(700, 30, font_size=20) img = BuildImage(700, 30, font_size=20)
w, h = img.getsize(title) w, h = img.getsize(title)
img.text((10, int((30 - h) / 2)), title) img.text((10, int((30 - h) / 2)), title)
img.text((580, int((30 - h) / 2)), hot) img.text((580, int((30 - h) / 2)), hot)
+48
View File
@@ -0,0 +1,48 @@
from nonebot import on_command
from services.log import logger
from nonebot.adapters.cqhttp import Bot, MessageEvent, GroupMessageEvent
from nonebot.typing import T_State
from utils.http_utils import AsyncHttpx
from configs.config import Config
from .data_source import get_data
__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"
jitang = on_command("鸡汤", aliases={"毒鸡汤"}, priority=5, block=True)
@jitang.handle()
async def _(bot: Bot, event: MessageEvent, state: T_State):
try:
data, code = await get_data(url)
if code != 200:
await jitang.finish(data, at_sender=True)
await jitang.send(data["data"]["content"])
logger.info(
f"(USER {event.user_id}, GROUP "
f"{event.group_id if isinstance(event, GroupMessageEvent) else 'private'})"
f" 发送鸡汤:" + data["data"]["content"]
)
except Exception as e:
await jitang.send("鸡汤煮坏掉了...")
logger.error(f"鸡汤煮坏掉了 {type(e)}:{e}")
Regular → Executable
View File
Regular → Executable
+25 -30
View File
@@ -3,13 +3,13 @@ from nonebot.adapters.cqhttp import Bot, MessageEvent, GroupMessageEvent
from nonebot.typing import T_State from nonebot.typing import T_State
from services.log import logger from services.log import logger
from .data_source import get_data, gen_wbtop_pic from .data_source import get_data, gen_wbtop_pic
from utils.browser import get_browser
from utils.utils import get_message_text, is_number from utils.utils import get_message_text, is_number
from configs.path_config import IMAGE_PATH from configs.path_config import IMAGE_PATH
from utils.message_builder import image from utils.message_builder import image
from utils.http_utils import AsyncPlaywright
import asyncio import asyncio
__zx_plugin_name__ = '微博热搜' __zx_plugin_name__ = "微博热搜"
__plugin_usage__ = """ __plugin_usage__ = """
usage: usage:
在QQ上吃个瓜 在QQ上吃个瓜
@@ -18,21 +18,21 @@ usage:
微博热搜 [id]:截图该热搜页面 微博热搜 [id]:截图该热搜页面
示例:微博热搜 5 示例:微博热搜 5
""".strip() """.strip()
__plugin_des__ = '刚买完瓜,在吃瓜现场' __plugin_des__ = "刚买完瓜,在吃瓜现场"
__plugin_cmd__ = ['微博热搜', '微博热搜 [id]'] __plugin_cmd__ = ["微博热搜", "微博热搜 [id]"]
__plugin_version__ = 0.1 __plugin_version__ = 0.1
__plugin_author__ = 'HibiKier' __plugin_author__ = "HibiKier"
__plugin_settings__ = { __plugin_settings__ = {
"level": 5, "level": 5,
"default_status": True, "default_status": True,
"limit_superuser": False, "limit_superuser": False,
"cmd": ['微博热搜'], "cmd": ["微博热搜"],
} }
wbtop = on_command("wbtop", aliases={'微博热搜'}, priority=5, block=True) wbtop = on_command("wbtop", aliases={"微博热搜"}, priority=5, block=True)
wbtop_url = 'https://v2.alapi.cn/api/new/wbtop' wbtop_url = "https://v2.alapi.cn/api/new/wbtop"
wbtop_data = [] wbtop_data = []
@@ -45,32 +45,27 @@ async def _(bot: Bot, event: MessageEvent, state: T_State):
data, code = await get_data(wbtop_url) data, code = await get_data(wbtop_url)
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"]
if not msg: if not msg:
img = await asyncio.get_event_loop().run_in_executor(None, gen_wbtop_pic, wbtop_data) img = await asyncio.get_event_loop().run_in_executor(
None, gen_wbtop_pic, wbtop_data
)
await wbtop.send(img) await wbtop.send(img)
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" 查询微博热搜") 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()
page = None
try: try:
if not browser: await wbtop.send("开始截取数据...")
logger.warning('获取 browser 失败,请部署至 linux 环境....') img = await AsyncPlaywright.screenshot(
await wbtop.finish('获取 browser 对象失败...') url,
page = await browser.new_page() f"{IMAGE_PATH}/temp/wbtop_{event.user_id}.png",
await page.goto(url, wait_until='networkidle', timeout=10000) "#pl_feedlist_index",
await page.set_viewport_size({"width": 2560, "height": 1080}) sleep=5
await asyncio.sleep(5) )
div = await page.query_selector("#pl_feedlist_index") await wbtop.send(img)
await div.screenshot(path=f'{IMAGE_PATH}/temp/wbtop_{event.user_id}.png', timeout=100000)
await page.close()
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: await wbtop.send("发生了一些错误.....")
await page.close()
await wbtop.send('发生了一些错误.....')
Regular → Executable
+10 -7
View File
@@ -9,7 +9,7 @@ from .data_source import (
get_media_id, get_media_id,
get_sub_status, get_sub_status,
SubManager, SubManager,
BilibiliSub BilibiliSub,
) )
from models.level_user import LevelUser from models.level_user import LevelUser
from configs.config import Config from configs.config import Config
@@ -163,11 +163,10 @@ async def _(bot: Bot, event: MessageEvent, state: T_State):
@show_sub_info.handle() @show_sub_info.handle()
async def _(bot: Bot, event: MessageEvent, state: T_State): async def _(bot: Bot, event: MessageEvent, state: T_State):
id_ = ( if isinstance(event, GroupMessageEvent):
f"{event.user_id}:{event.group_id}" id_ = f"{event.group_id}"
if isinstance(event, GroupMessageEvent) else:
else f"{event.user_id}" id_ = f"{event.user_id}"
)
data = await BilibiliSub.get_sub_data(id_) data = await BilibiliSub.get_sub_data(id_)
live_rst = "" live_rst = ""
up_rst = "" up_rst = ""
@@ -181,6 +180,7 @@ async def _(bot: Bot, event: MessageEvent, state: T_State):
up_rst += f"\tUP:{x.uname}\n" f"\tuid:{x.uid}\n" f"------------------\n" up_rst += f"\tUP:{x.uname}\n" f"\tuid:{x.uid}\n" f"------------------\n"
if x.sub_type == "season": if x.sub_type == "season":
season_rst += ( season_rst += (
f"\t番剧id:{x.sub_id}\n"
f"\t番名:{x.season_name}\n" f"\t番名:{x.season_name}\n"
f"\t当前集数:{x.season_current_episode}\n" f"\t当前集数:{x.season_current_episode}\n"
f"------------------\n" f"------------------\n"
@@ -189,7 +189,9 @@ async def _(bot: Bot, event: MessageEvent, state: T_State):
up_rst = "当前订阅的UP:\n" + up_rst if up_rst else up_rst up_rst = "当前订阅的UP:\n" + up_rst if up_rst else up_rst
season_rst = "当前订阅的番剧:\n" + season_rst if season_rst else season_rst season_rst = "当前订阅的番剧:\n" + season_rst if season_rst else season_rst
if not live_rst and not up_rst and not season_rst: if not live_rst and not up_rst and not season_rst:
live_rst = "您目前没有任何订阅..." live_rst = (
"该群目前没有任何订阅..." if isinstance(event, GroupMessageEvent) else "您目前没有任何订阅..."
)
await show_sub_info.send(live_rst + up_rst + season_rst) await show_sub_info.send(live_rst + up_rst + season_rst)
@@ -206,6 +208,7 @@ async def _():
await sub_manager.reload_sub_data() await sub_manager.reload_sub_data()
sub = await sub_manager.random_sub_data() sub = await sub_manager.random_sub_data()
if sub: if sub:
logger.info(f"Bilibili订阅开始检测:{sub.sub_id}")
rst = await get_sub_status(sub.sub_id, sub.sub_type) rst = await get_sub_status(sub.sub_id, sub.sub_type)
await send_sub_msg(rst, sub, bot) await send_sub_msg(rst, sub, bot)
if sub.sub_type == "live": if sub.sub_type == "live":
+32 -28
View File
@@ -14,7 +14,7 @@ from datetime import datetime
from utils.browser import get_browser from utils.browser import get_browser
from services.db_context import db from services.db_context import db
from services.log import logger from services.log import logger
import aiohttp from utils.http_utils import AsyncHttpx
import random import random
@@ -150,7 +150,7 @@ async def delete_sub(sub_id: str, sub_user: str) -> str:
:param sub_id: 订阅 id :param sub_id: 订阅 id
:param sub_user: 订阅用户 id # 7384933:private or 7384933:2342344(group) :param sub_user: 订阅用户 id # 7384933:private or 7384933:2342344(group)
""" """
if await BilibiliSub.delete_bilibili_sub(sub_id, sub_user): if await BilibiliSub.delete_bilibili_sub(int(sub_id), sub_user):
return f"已成功取消订阅:{sub_id}" return f"已成功取消订阅:{sub_id}"
else: else:
return f"取消订阅:{sub_id} 失败,请检查是否订阅过该Id...." return f"取消订阅:{sub_id} 失败,请检查是否订阅过该Id...."
@@ -162,30 +162,29 @@ async def get_media_id(keyword: str) -> dict:
:param keyword: 番剧名称 :param keyword: 番剧名称
""" """
params = {"keyword": keyword} params = {"keyword": keyword}
async with aiohttp.ClientSession() as session: for _ in range(3):
for _ in range(3): try:
try: _season_data = {}
_season_data = {} response = await AsyncHttpx.get(
async with session.get( bilibili_search_url, params=params, timeout=5
bilibili_search_url, timeout=5, params=params )
) as response: if response.status_code == 200:
if response.status == 200: data = response.json()
data = await response.json() if data.get("data"):
if data.get("data"): for item in data["data"]["result"]:
for item in data["data"]["result"]: if item["result_type"] == "media_bangumi":
if item["result_type"] == "media_bangumi": idx = 0
idx = 0 for x in item["data"]:
for x in item["data"]: _season_data[idx] = {
_season_data[idx] = { "media_id": x["media_id"],
"media_id": x["media_id"], "title": x["title"]
"title": x["title"] .replace('<em class="keyword">', "")
.replace('<em class="keyword">', "") .replace("</em>", ""),
.replace("</em>", ""), }
} idx += 1
idx += 1 return _season_data
return _season_data except TimeoutError:
except TimeoutError: pass
pass
return {} return {}
@@ -257,7 +256,12 @@ async def _get_up_status(id_: int) -> Optional[str]:
if dynamic_img: if dynamic_img:
await BilibiliSub.update_sub_info(id_, dynamic_upload_time=dynamic_upload_time) await BilibiliSub.update_sub_info(id_, dynamic_upload_time=dynamic_upload_time)
rst += f"{uname} 发布了动态!\n" f"{dynamic_img}\n" rst += f"{uname} 发布了动态!\n" f"{dynamic_img}\n"
if _user.latest_video_created != latest_video_created and video: if (
latest_video_created
and _user.latest_video_created
and video
and _user.latest_video_created < latest_video_created
):
rst = rst + "-------------\n" if rst else rst rst = rst + "-------------\n" if rst else rst
await BilibiliSub.update_sub_info( await BilibiliSub.update_sub_info(
id_, latest_video_created=latest_video_created id_, latest_video_created=latest_video_created
@@ -307,7 +311,7 @@ async def get_user_dynamic(
browser = await get_browser() browser = await get_browser()
if dynamic_info.get("cards") and browser: if dynamic_info.get("cards") and browser:
dynamic_upload_time = dynamic_info["cards"][0]["desc"]["timestamp"] dynamic_upload_time = dynamic_info["cards"][0]["desc"]["timestamp"]
if local_user.dynamic_upload_time != dynamic_upload_time: if local_user.dynamic_upload_time < dynamic_upload_time:
page = await browser.new_page() page = await browser.new_page()
await page.goto( await page.goto(
f"https://space.bilibili.com/{local_user.uid}/dynamic", f"https://space.bilibili.com/{local_user.uid}/dynamic",
Regular → Executable
View File
Regular → Executable
+24 -25
View File
@@ -1,12 +1,12 @@
from utils.image_utils import CreateImg from utils.image_utils import BuildImage
from configs.path_config import IMAGE_PATH from configs.path_config import IMAGE_PATH
from utils.http_utils import AsyncHttpx
from pathlib import Path from pathlib import Path
from bilibili_api import user from bilibili_api import user
from io import BytesIO from io import BytesIO
import aiohttp
BORDER_PATH = Path(IMAGE_PATH) / 'border' BORDER_PATH = Path(IMAGE_PATH) / "border"
BORDER_PATH.mkdir(parents=True, exist_ok=True) BORDER_PATH.mkdir(parents=True, exist_ok=True)
@@ -16,9 +16,7 @@ async def get_pic(url: str) -> bytes:
:param url: 图像链接 :param url: 图像链接
:return: 图像二进制 :return: 图像二进制
""" """
async with aiohttp.ClientSession() as session: return (await AsyncHttpx.get(url, timeout=10)).content
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): async def create_live_des_image(uid: int, title: str, cover: str, tags: str, des: str):
@@ -33,17 +31,26 @@ async def create_live_des_image(uid: int, title: str, cover: str, tags: str, des
""" """
u = user.User(uid) u = user.User(uid)
user_info = await u.get_user_info() user_info = await u.get_user_info()
name = user_info['name'] name = user_info["name"]
sex = user_info['sex'] sex = user_info["sex"]
face = user_info['face'] face = user_info["face"]
sign = user_info['sign'] sign = user_info["sign"]
ava = CreateImg(100, 100, background=BytesIO(await get_pic(face))) ava = BuildImage(100, 100, background=BytesIO(await get_pic(face)))
ava.circle() ava.circle()
cover = CreateImg(470, 265, background=BytesIO(await get_pic(cover))) cover = BuildImage(470, 265, background=BytesIO(await get_pic(cover)))
print() print()
def _create_live_des_image(title: str, cover: CreateImg, tags: str, des: str, user_name: str, sex: str, sign: str, ava: CreateImg): def _create_live_des_image(
title: str,
cover: BuildImage,
tags: str,
des: str,
user_name: str,
sex: str,
sign: str,
ava: BuildImage,
):
""" """
生成主播简介图片 生成主播简介图片
:param title: 直播间标题 :param title: 直播间标题
@@ -56,17 +63,9 @@ def _create_live_des_image(title: str, cover: CreateImg, tags: str, des: str, us
:param ava: 主播头像 :param ava: 主播头像
:return: :return:
""" """
border = BORDER_PATH / '0.png' border = BORDER_PATH / "0.png"
border_img = None border_img = None
if border.exists(): if border.exists():
border_img = CreateImg(1772, 2657, background=border) border_img = BuildImage(1772, 2657, background=border)
bk = CreateImg(1772, 2657, font_size=30) bk = BuildImage(1772, 2657, font_size=30)
bk.paste(cover, (0, 100), center_type='by_width') bk.paste(cover, (0, 100), center_type="by_width")
Regular → Executable
+1 -4
View File
@@ -7,7 +7,6 @@ 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 asyncio.exceptions import TimeoutError from asyncio.exceptions import TimeoutError
from aiohttp.client_exceptions import ServerDisconnectedError
__zx_plugin_name__ = "磁力搜索" __zx_plugin_name__ = "磁力搜索"
__plugin_usage__ = """ __plugin_usage__ = """
@@ -89,11 +88,9 @@ async def _(bot: Bot, event: PrivateMessageEvent, state: T_State):
send_flag = True send_flag = True
except TimeoutError: except TimeoutError:
await bt.finish(f"搜索 {keyword} 超时...") await bt.finish(f"搜索 {keyword} 超时...")
except ServerDisconnectedError:
await bt.finish(f"搜索 {keyword} 连接失败")
except Exception as e: except Exception as e:
await bt.finish(f"bt 其他未知错误..") await bt.finish(f"bt 其他未知错误..")
logger.error(f"bt 错误 e:{e}") logger.error(f"bt 错误 {type(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} 页")
Regular → Executable
+35 -34
View File
@@ -1,8 +1,6 @@
from utils.user_agent import get_user_agent from utils.http_utils import AsyncHttpx
import aiohttp
from configs.config import Config from configs.config import Config
from bs4 import BeautifulSoup from bs4 import BeautifulSoup
from utils.utils import get_local_proxy
import platform import platform
if platform.system() == "Windows": if platform.system() == "Windows":
@@ -15,36 +13,39 @@ url = "http://www.eclzz.world"
async def get_bt_info(keyword: str, page: str): async def get_bt_info(keyword: str, page: str):
async with aiohttp.ClientSession(headers=get_user_agent()) as session: """
async with session.get( 获取资源信息
f"{url}/s/{keyword}_rel_{page}.html", proxy=get_local_proxy(), timeout=5 :param keyword: 关键词
) as response: :param page: 页数
text = await response.text() """
if text.find("大约0条结果") != -1: text = (await AsyncHttpx.get(f"{url}/s/{keyword}_rel_{page}.html", timeout=5)).text
return if text.find("大约0条结果") != -1:
soup = BeautifulSoup(text, "lxml") return
item_lst = soup.find_all("div", {"class": "search-item"}) soup = BeautifulSoup(text, "lxml")
bt_max_num = Config.get_config("bt", "BT_MAX_NUM") item_lst = soup.find_all("div", {"class": "search-item"})
bt_max_num = bt_max_num if bt_max_num < len(item_lst) else len(item_lst) bt_max_num = Config.get_config("bt", "BT_MAX_NUM")
for item in item_lst[:bt_max_num]: bt_max_num = bt_max_num if bt_max_num < len(item_lst) else len(item_lst)
divs = item.find_all("div") for item in item_lst[:bt_max_num]:
title = ( divs = item.find_all("div")
str(divs[0].find("a").text) title = (
.replace("<em>", "") str(divs[0].find("a").text)
.replace("</em>", "") .replace("<em>", "")
.strip() .replace("</em>", "")
) .strip()
spans = divs[2].find_all("span") )
itype = spans[0].text spans = divs[2].find_all("span")
create_time = spans[1].find("b").text itype = spans[0].text
file_size = spans[2].find("b").text create_time = spans[1].find("b").text
link = await get_download_link(divs[0].find("a")["href"], session) file_size = spans[2].find("b").text
yield title, itype, create_time, file_size, link link = await get_download_link(divs[0].find("a")["href"])
yield title, itype, create_time, file_size, link
async def get_download_link(_url: str, session) -> str: async def get_download_link(_url: str) -> str:
async with session.get( """
f"{url}{_url}", proxy=get_local_proxy(), timeout=30 获取资源下载地址
) as response: :param _url: 链接
soup = BeautifulSoup(await response.text(), "lxml") """
return soup.find("a", {"id": "down-url"})["href"] text = (await AsyncHttpx.get(f"{url}{_url}")).text
soup = BeautifulSoup(text, "lxml")
return soup.find("a", {"id": "down-url"})["href"]
Regular → Executable
+6 -6
View File
@@ -25,22 +25,22 @@ __plugin_settings__ = {
} }
songpicker = on_command("点歌", priority=5, block=True) music = on_command("点歌", priority=5, block=True)
@songpicker.handle() @music.handle()
async def handle_first_receive(bot: Bot, event: Event, state: T_State): async def handle_first_receive(bot: Bot, event: Event, state: T_State):
args = str(event.get_message()).strip() args = str(event.get_message()).strip()
if args: if args:
state["song_name"] = args state["song_name"] = args
@songpicker.got("song_name", prompt="歌名是?") @music.got("song_name", prompt="歌名是?")
async def _(bot: Bot, event: Event, state: T_State): async def _(bot: Bot, event: Event, state: T_State):
song = state["song_name"] song = state["song_name"]
song_id = await get_song_id(song) song_id = await get_song_id(song)
if not song_id: if not song_id:
await songpicker.finish("没有找到这首歌!", at_sender=True) await music.finish("没有找到这首歌!", at_sender=True)
for _ in range(3): for _ in range(3):
song_content = [{"type": "music", "data": {"type": 163, "id": song_id}}] song_content = [{"type": "music", "data": {"type": 163, "id": song_id}}]
logger.info( logger.info(
@@ -48,9 +48,9 @@ async def _(bot: Bot, event: Event, state: T_State):
f"{event.group_id if isinstance(event, GroupMessageEvent) else 'private'})" f"{event.group_id if isinstance(event, GroupMessageEvent) else 'private'})"
f" 点歌 :{song}" f" 点歌 :{song}"
) )
await songpicker.finish(song_content) await music.finish(song_content)
else: else:
await songpicker.finish("网易云繁忙...") await music.finish("网易云繁忙...")
Regular → Executable
+25 -31
View File
@@ -1,4 +1,4 @@
import aiohttp from utils.http_utils import AsyncHttpx
import json import json
@@ -7,41 +7,35 @@ cookies = {"appver": "2.0.2"}
async def search_song(song_name: str): async def search_song(song_name: str):
async with aiohttp.ClientSession( """
headers=headers, cookies=cookies 搜索歌曲
) as session: :param song_name: 歌名
async with session.post( """
f"http://music.163.com/api/search/get/", r = await AsyncHttpx.post(
data={"s": song_name, "limit": 1, "type": 1, "offset": 0}, f"http://music.163.com/api/search/get/",
) as r: data={"s": song_name, "limit": 1, "type": 1, "offset": 0},
if r.status != 200: )
return None if r.status_code != 200:
r = await r.text() return None
return json.loads(r) return json.loads(r.text)
async def get_song_id(songName: str) -> int: async def get_song_id(song_name: str) -> int:
""" """ """
根据用户输入的songName 获取候选songId列表 [默认songId数量:5] r = await search_song(song_name)
""" try:
r = await search_song(songName) return r["result"]["songs"][0]["id"]
return r["result"]["songs"][0]["id"] except KeyError:
return 0
async def get_song_info(songId: int): async def get_song_info(songId: int):
""" """
获取歌曲信息 获取歌曲信息
""" """
async with aiohttp.ClientSession( r = await AsyncHttpx.post(
headers=headers, cookies=cookies f"http://music.163.com/api/song/detail/?id={songId}&ids=%5B{songId}%5D",
) as session: )
async with session.post( if r.status_code != 200:
f"http://music.163.com/api/song/detail/?id={songId}&ids=%5B{songId}%5D", return None
) as r: return json.loads(r.text)
if r.status != 200:
return None
r = await r.text()
return json.loads(r)
Regular → Executable
View File
Regular → Executable
+15 -26
View File
@@ -1,12 +1,8 @@
import psutil import psutil
import aiohttp
import time import time
from datetime import datetime from datetime import datetime
from utils.user_agent import get_user_agent from utils.http_utils import AsyncHttpx
from asyncio.exceptions import TimeoutError from utils.image_utils import BuildImage
from aiohttp.client_exceptions import ClientConnectorError
from utils.utils import get_local_proxy
from utils.image_utils import CreateImg
from configs.path_config import IMAGE_PATH from configs.path_config import IMAGE_PATH
from pathlib import Path from pathlib import Path
import asyncio import asyncio
@@ -34,23 +30,16 @@ class Check:
self.disk = psutil.disk_usage("/").percent self.disk = psutil.disk_usage("/").percent
async def check_network(self): async def check_network(self):
async with aiohttp.ClientSession(headers=get_user_agent()) as session: try:
try: await AsyncHttpx.get("https://www.baidu.com/", timeout=5)
async with session.get( except Exception as e:
"https://www.baidu.com/", proxy=get_local_proxy(), timeout=3 logger.warning(f"访问BaiDu失败... {type(e)}: {e}")
) as response: self.baidu = 404
pass try:
except (TimeoutError, ClientConnectorError) as e: await AsyncHttpx.get("https://www.google.com/", timeout=5)
logger.warning(f"访问BaiDu失败... e: {e}") except Exception as e:
self.baidu = 404 logger.warning(f"访问Google失败... {type(e)}: {e}")
try: self.google = 404
async with session.get(
"https://www.google.com/", proxy=get_local_proxy(), timeout=3
) as response:
pass
except (TimeoutError, ClientConnectorError) as e:
logger.warning(f"访问Google失败... e: {e}")
self.google = 404
def check_user(self): def check_user(self):
rst = "" rst = ""
@@ -60,7 +49,7 @@ class Check:
async def show(self): async def show(self):
await self.check_all() await self.check_all()
A = CreateImg(0, 0, font_size=24) A = BuildImage(0, 0, font_size=24)
rst = ( rst = (
f'[Time] {str(datetime.now()).split(".")[0]}\n' f'[Time] {str(datetime.now()).split(".")[0]}\n'
f"-----System-----\n" f"-----System-----\n"
@@ -80,10 +69,10 @@ class Check:
if w > width: if w > width:
width = w width = w
height += 30 height += 30
A = CreateImg(width + 50, height + 10, font_size=24, font="HWZhongSong.ttf") A = BuildImage(width + 50, height + 10, font_size=24, font="HWZhongSong.ttf")
A.transparent(1) A.transparent(1)
A.text((10, 10), rst) A.text((10, 10), rst)
_x = max(width, height) _x = max(width, height)
bk = CreateImg(_x + 100, _x + 100, background=Path(IMAGE_PATH) / "background" / "check" / "0.jpg") bk = BuildImage(_x + 100, _x + 100, background=Path(IMAGE_PATH) / "background" / "check" / "0.jpg")
bk.paste(A, alpha=True, center_type='center') bk.paste(A, alpha=True, center_type='center')
return bk.pic2bs4() return bk.pic2bs4()

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