Compare commits

...
54 Commits
Author SHA1 Message Date
HibiKier bc903d3813 Update __init__.py 2022-02-09 22:28:49 +08:00
HibiKier 80fc17b610 Update __version__ 2022-02-09 21:44:09 +08:00
HibiKier 0efb2bbc60 update v0.1 2022-02-09 20:05:49 +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
228 changed files with 6299 additions and 2652 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/
+114 -445
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/nonebot-2.0.0-yellow)
@@ -20,15 +20,26 @@
## 未完成的文档
[传送门](https://hibikier.github.io/zhenxun_bot/)
# [传送门](https://hibikier.github.io/zhenxun_bot/)
## 真寻的帮助
请对真寻说: '真寻帮助' 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>
@@ -51,9 +62,13 @@
- [x] 商店/我的金币/购买道具/使用道具
- [x] 8种手游抽卡 (查看 [nonebot_plugin_gamedraw](https://github.com/HibiKier/nonebot_plugin_gamedraw))
- [x] 我有一个朋友想问问..(借鉴pcrbot插件)
- [x] 原神黄历 (使用[Genshin_Impact_bot](https://github.com/H-K-Y/Genshin_Impact_bot)插件)
- [x] 原神黄历
- [x] 原神今日素材
- [x] 原神资源查询 (借鉴[Genshin_Impact_bot](https://github.com/H-K-Y/Genshin_Impact_bot)插件)
- [x] 原神便笺查询
- [x] 原神玩家查询
- [x] 原神树脂提醒
- [x] 原神签到/自动签到
- [x] 金币红包
- [x] 微博热搜
- [x] B站主播/UP/番剧订阅
@@ -89,6 +104,8 @@
- [x] 通过PID获取图片
- [x] 功能统计可视化
- [x] 关于
### 已实现的管理员功能
- [x] 更新群组成员信息
- [x] 95%的群功能开关
@@ -101,6 +118,7 @@
- [x] 删除图片 (同上)
- [x] 群内B站订阅
- [x] 群词条
- [x] 休息吧/醒来
### 已实现的超级用户功能
- [x] 添加/删除权限(是真寻的管理员权限,不是群管理员)
@@ -126,6 +144,12 @@
- [x] 功能开关(更多设置)
- [x] 功能状态
- [x] b了
- [x] 执行sql
- [x] 重载配置
- [x] 清理临时数据
- [x] 增删群认证
- [x] 同意/拒绝好友/群聊请求
- [x] 配置重载
#### 超级用户的被动技能
- [x] 邀请入群提醒(别人邀请真寻入群)
@@ -150,127 +174,7 @@
- [x] 群管理员监控,自动为新晋管理员增加权限,为失去群管理员的用户删除权限
- [x] 群权限系统
- [x] 定时更新权限
</details>
## 功能具体指令
<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 | 显示该插件的超级用户帮助
- [x] 自动配置重载
</details>
## 详细配置请前往文档,以下为最简部署和配置,如果你有基础并学习过nonebot2的话
@@ -317,349 +221,114 @@ python bot.py
```
## 使用Docker
__Docker 最新版本由 [Sakuracio](https://github.com/Sakuracio) 提供__
#### GitHub:[Sakuracio/zxenv](https://github.com/Sakuracio/zxenv)
#### DockerHub:[hibikier/zhenxun_bot](https://hub.docker.com/r/hibikier/zhenxun_bot)
## 更新
### 2021/11/23 \[v0.0.6.2]
### 2021/2/9 \[v0.1]
* 替换cos API
* 提供私聊b了,即跨群b了用户
* 修复游戏抽卡导入角色失败(原神)
* 修复无Pixiv代理时报错
* 将项目中大部分aiohttp替换为httpx
* 删除了丘丘人翻译插件
* 新增群词条
* 修复游戏抽卡碧蓝航线bwiki格式更改导致获取报错
* 首次启动会生成配置文件后停止程序,配置后再次启动即可
* 新增原神自动签到和手动签到
* 新增原神树脂提醒
* 新增手动重载Config.yaml命令以及重载配置定时任务(极少部分帮助或配置可能需要重启
* 修改了发送本地图库的matcher,改为on_message
* register_use可以通过返回值发送消息
* 修复修改商品时限制时间出错
* 修复超时商品依旧可以被购买
### 2021/11/18
### 2021/1/16 \[v0.0.9.0]
* 修复超级用户无法正确拉真寻入群
* Ai提供文本敏感词过滤器
* 疫情插件适配新版腾讯API
* 修复/t回复带空格切分
* 修复原神玩家查询缺少渊下宫和稻妻家园以及角色不完全
* 修复方法 text2image 中 padding 和 font 无法对纯文本生效
* 修复签到图片中信息并未使用配置文件中的色图概率
* 修改原神大地图合成方式,改为先压缩再合成
* bag_user弃用字段props(该字段会在下次更新删除),使用新字段property
* 数据库中所有belonging_group统一修改为group_id
* 商店将registered_use和register_goods更名为register_use何register_goods
* 商品注册提供了kwargs参数提供:
* bot
* event
* 特殊字段
* “send_success_msg”(发送成功的交互信息->即:使用道具 {name} {num} 次成功)
* “_max_num_limit”(该道具单次使用的最多个数,默认1)
### 2021/11/14
* 修复功能总开关无法正确开启
### 2021/1/5 \[v0.0.8.2]
### 2021/11/12
* 提供金币消费hook,可在plugins2settings.yaml中配置该功能需要消费的金币
* 商店插件将作为内置插件移动至basic_plugins
* 商店插件通过export提供了方法,不需要修改商店插件代码添加商品数据和生效方法
* 修改了hook插件顺序,主要以auth_hook为主
* 修改商店图片样式
* 取消每次启动更新城市列表(首次除外),采用定时更新,加快bot启动速度
* 取消每次启动时截取今日素材,采用调用时截取保存,加快bot启动速度
* 更新色图时当图片404时会删除并替换
* 疫情消息回复改为图片
* 修复商店折扣和限时时间无法生效
* 修复原神玩家查询尘歌壶缺少图片
* 修复PIX无法url无法正确获取
### 2021/12/26
### 2021/11/10
* 修复群词条问题 空格 会被录入导致不断回复
* 修复米游社app替换api导致无法正常查询
* 修复PIX表重复创建导致首次无法运行
* 检测Omage图库改为命令方式:检测omega图库
### 2021/12/24
### 2021/11/9
* 支持国际疫情数据查询 [@pull/99](https://github.com/HibiKier/zhenxun_bot/pull/99)
* 修复管理员帮助无法正常响应
* 修复被ban时会一直回复被ban提醒
### 2021/12/20
### 2021/11/5
* 只有发布小于存储时间的新动态/视频的时候才获取并推送 [@pull/96](https://github.com/HibiKier/zhenxun_bot/pull/96)
* 修复ai没有图灵key时报错
* 提供图片路径resource/img/background/check
### 2021/12/16 \[v0.0.7.0]
### 2021/11/4
* 提供了真寻群聊功能总开关和对应默认配置项,命令:休息吧 醒来
* 新增原神玩家查询,原神便笺查询
* 群功能管理提供全部开启/关闭命令:开启/关闭全部功能
* 提供主要数据自动备份,且提供自定义配置项
* 提供命令:关于,用于介绍Bot之类的
* 新增命令exec,用于执行sql语句
* 签到提供参数 "all",用于签到所有群聊
* Ban提醒提供cd
* 本地图库提供配置项SHOW_ID,用于设置发送图片时是否显示id
* 色图和PIX提供配置项SHOW_INFO,用于设置发送图片时是否显示图片信息
* 所有被动技能提供了进群默认状态配置项
* 修复添加权限第二种添加形式无法正确添加正确的权限
* 修复签到获取好感度卡时金币不会增加
* 修复当红包数量不合法时依旧扣除金币
* 修复金币红包再次使用塞红包时无法正确退回上次未开完的金币
* 修复 滴滴滴- 只包含图片时不会发送至管理员
* 修复添加权限等级错误
* 修复群词条以bot名称为开头时无法正确触发
* 修改了权限插件加载顺序防止小概率优先加载权限插件引起报错
* 本地图库新图库会统一建立在resource/img/image_management文件夹下,如果该文件夹内未找到图库,会从上级目录查找(即:resource/img/)
* 通用排行榜改用图片消息,且可以自定义排行榜人数
* 优化CreateMat排行榜数据显示
* 修复了pix更新多余参数导致失败的问题
* 修复滴滴滴-注入风险
* 修复无法正常关闭滴滴滴,戳一戳
* 添加了发送图片撤回配置项WITHDRAW_IMAGE_TIME
* 修复了天气regex文本过长时会正则匹配过久导致nb卡顿
* message_build新增custom_forward_msg用于快捷生成转发消息
* 插件配置改为yaml存储,新增Config,用于获取和新增插件配置
* 新增 当插件加载失败时,会发送消息提醒超级用户,且在功能状态中对应失败插件写上[ERROR]
* 修复当查看-spuer插件帮助时无法正确回复
* 群内帮助图片会随群内功能开关和插件总开关变化
* 自检改为图像形式
* 更新色图删除了rar_setu,r18_rar和rar文件夹,压缩将统一在temp文件夹
* 更新色图只有在有更新数量或报错时才会提醒超级用户
* 群欢迎消息加入cd
* 加入资源管理resources_manager
* 新增 好友请求/群聊邀请 控制命令
<br>
### 2021/10/15
* 适配了原神资源查询米游社地图返回的新格式
### 2021/10/8
* 修复疫情省份查询失效
* 修复功能调用统计全局下统计可能发生错误
### 2021/10/4
* 修复了功能调用统计失效问题
* 当色图库中没有色图时,会在线搜索色图而不是‘没找到符合条件的色图...’
* 快速更新权限再给超级用户发送错误日志
* 修复疫情未加载省份城市无法正常使用
### 2021/10/3
* 对插件进行分离
* 重写了插件与限制管理器以及帮助获取
* 修改一些插件目录和数据存储目录
* 插件通用配置与限制数据将以ymal文件存储 \[路径:data/configs]
* 所有商店相关操作调用统计合并为商店(包括之前已经保存的数据,会先进行备份)
* 简化了点歌的代码相关
* 修复了碧蓝航线抽卡新框导致报错无法正常初始化
* 修复了P站排行/搜图在PC端无法正常显示
* 添加了插件对超级用户是否限制的配置 ‘limit_superuser’
* 添加命令 ‘重载插件配置’,用于生效手动修改配文件
* 超级用户帮助可以添加 -super 来显示该插件的超级用户帮助,示例:帮助.ban -super
* 原神黄历改为网页截图
* 修改了鲁迅说逻辑结构
* 修改了统计图表样式,改为自定义CreateMat
* 节日红包不再被24小时限制,群内多个节日红包将会覆盖
* 当群权限为-1时,不会对群发送修改权限通知,并屏蔽此群一切命令(包括提醒)
* 修复了红包数量可以过大或为负数,红包数量大于群员数量时会修改为群员数量
* 修复了负数开箱
* 签到最低好感度设置为0.01 [@pull/53](https://github.com/HibiKier/zhenxun_bot/pull/53)
* pip安装新依赖 ruamel.yaml
* 修复功能 EPIC [@pull/58](https://github.com/HibiKier/zhenxun_bot/pull/58)
### 2021/9/10
* 修复撤回消息有时无法正确获取消息id
### 2021/9/9
* 替换coser API
* 修复签到uid可能不默认为0
* 修复签到可能重复的问题
* 修复无订阅时递归出错
* 启用了plugins2info_dict, plugins2cd_dict, plugins2exists_dict配置文件,通过USE_CONFIG_FILE=True开启
* 修复涩图local_id会被固定为50
* 优化图库数量查询
* 修复原神大地图过大无法打开报错
* 修复无法显示正确的涩图上限
### 2021/9/7
* 修改 update_info.json
* 修改 更新信息 图片大小
* 修复 查看订阅 命令 UP和番剧无法正常显示
* 修复订阅推送无法正确推送
* 修复搜图返回列表为空时无法正确回复 [@pull/40](https://github.com/HibiKier/zhenxun_bot/pull/40)
### 2021/9/5
* 添加配置PIX_IMAGE_SIZE,调整PIX下载图片大小,当设置的图片404时,改为原图
* 新增配置DEFAULT_GROUP_LEVEL,默认群等级
* 新增超级用户功能 super ban,将屏蔽被ban用户的所有消息,指令:b了
* b站转发解析支持纯BV号解析,且五分钟内不会解析相同url
* 俄罗斯轮盘新增 连胜/最高连胜/连败/最高连败 纪录,新增 最高连胜排行榜/最高连败排行榜
* 增加扩展图库 OmegaPixivIllusts,不想自己找图的人福音([Ailitonia](https://github.com/Ailitonia) 佬的高质量精品手筛图库)([传送门](https://github.com/Ailitonia/omega-miya/blob/master/archive_data/db_pixiv.7z) ),可以手动导入图库,也可以将解压文件放在bot.py同级目录重启bot
* 增加配置PIX_OMEGA_PIXIV_RATIO,PIX功能发送PIX图库和扩展图库OmegaPixivIllusts图片的比例,如果没有使用扩展图库OmegaPixivIllusts,请设置为(10, 0)
* 增加配置WITHDRAW_PIX_TIME,用于配置在开关PIX图片在群私聊的自动撤回
* 上传图库cases, 开箱 也可以连抽(未更新过没有价格)
* 新增命令 查看群白名单
* plugins2info_dict新增键"default_status",设置加入新群时功能的默认开关状态
* 增加配置plugins2exists_dict,可自定义是否阻塞某命令同时触发多次
* 增加配置plugins2cd_dict,可自定义为命令添加cd
* 新增B站订阅(直播/番剧/UP)[测试],提供命令:添加订阅 [主播/UP/番剧] [id/链接/番名],删除订阅 [id],查看订阅
* 优化pix和色图的数据库查询
* 触发已关闭的功能的正则时不再触发ai
* 更换coser API
* PIX搜索pid功能在群聊无法搜索PIX图库的r18和OmegaPixivIllusts的r15以及r18,超级用户除外
* PIX单次搜索的图片张数超级用户限制为至多30张,普通用户10张
* PIX超级用户新增-s,-r,可以通过pix -s 查看图库的涩图,pix -r查看图库的r18图,支持搜索,当然,pix图库只区分了r18和非r18,如果-s查询到不色的图也问题不大
* 优化P站排行和搜图,现在需要艾特,改为使用HIBIAPI,在群内时将使用合并消息(群聊搜图会屏蔽R-18)
* win10下playwright相关功能无法使用,但是不再需要删除文件
* 签到大改,优化签到方式与逻辑,改为图片形式发送,有概率额外获得随机道具(好感度有加成)
* 修改撤回功能,改为回复撤回,回复发送撤回
* 更改logging为loguru
* 删除了 发送图片 中的 [N]张图[keyword] 功能
* 修复私聊 关闭[功能] 默认不为 全部 而要添加参数 ‘a’
* 修复0权限用户可以修改禁言检测相关设置
### 2021/8/17
* 新增配置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
- [ ] 提供更多对插件的控制
- [ ] 明日方舟卡片式的签到..(大概)
- [ ] 更多的群管理功能
- [ ] 数据清理控制
- [ ] docker容器
- [ ] web管理
## 感谢
[Onebot](https://github.com/howmanybots/onebot)
[go-cqhttp](https://github.com/Mrs4s/go-cqhttp)
[nonebot2](https://github.com/nonebot/nonebot2)
[XUN_Langskip](https://github.com/Angel-Hair/XUN_Bot)
[cappuccilo_plugins](https://github.com/pcrbot/cappuccilo_plugins#%E7%94%9F%E6%88%90%E5%99%A8%E6%8F%92%E4%BB%B6)
[nonebot-plugin-withdraw](https://github.com/MeetWq/nonebot-plugin-withdraw)
[nonebot_plugin_songpicker2](https://github.com/maxesisn/nonebot_plugin_songpicker2)
[nonebot_plugin_manager](https://github.com/Jigsaw111/nonebot_plugin_manager)
[Genshin_Impact_bot](https://github.com/H-K-Y/Genshin_Impact_bot)
[nonebot2_luxun_says](https://github.com/NothAmor/nonebot2_luxun_says)
[AnimeThesaurus](https://github.com/Kyomotoi/AnimeThesaurus)
[omega-miya](https://github.com/Ailitonia/omega-miya)
[botuniverse / onebot](https://github.com/botuniverse/onebot) :超棒的机器人协议
[Mrs4s / go-cqhttp](https://github.com/Mrs4s/go-cqhttp) :cqhttp的golang实现,轻量、原生跨平台.
[nonebot / nonebot2](https://github.com/nonebot/nonebot2) :跨平台Python异步机器人框架
[Angel-Hair / XUN_Bot](https://github.com/Angel-Hair/XUN_Bot) :一个基于NoneBot和酷Q的功能性QQ机器人
[pcrbot / cappuccilo_plugins](https://github.com/pcrbot/cappuccilo_plugins) :hoshino插件合集
[MeetWq /nonebot-plugin-withdraw](https://github.com/MeetWq/nonebot-plugin-withdraw) :A simple withdraw plugin for Nonebot2
[maxesisn / nonebot_plugin_songpicker2](https://github.com/maxesisn/nonebot_plugin_songpicker2) :适用于nonebot2的点歌插件
[nonepkg / nonebot-plugin-manager](https://github.com/nonepkg/nonebot-plugin-manager) :Nonebot Plugin Manager base on import hook
[H-K-Y / Genshin_Impact_bot](https://github.com/H-K-Y/Genshin_Impact_bot) :原神bot,这是一个基于nonebot和HoshinoBot的原神娱乐及信息查询插件
[NothAmor / nonebot2_luxun_says](https://github.com/NothAmor/nonebot2_luxun_says) :基于nonebot2机器人框架的鲁迅说插件
[Kyomotoi / AnimeThesaurus](https://github.com/Kyomotoi/AnimeThesaurus) :一个~~特二刺螈~~(文爱)的适用于任何bot的词库
[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.6.2
__version__: v0.1.0
@@ -1,5 +1,5 @@
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.adapters.cqhttp import Bot, GroupMessageEvent
from .data_source import custom_group_welcome
@@ -38,7 +38,7 @@ custom_welcome = on_command(
async def _(bot: Bot, event: GroupMessageEvent, state: T_State):
try:
msg = get_message_text(event.json())
imgs = get_message_imgs(event.json())
imgs = get_message_img(event.json())
if not msg and not imgs:
await custom_welcome.finish(__plugin_usage__)
await custom_welcome.send(
+41 -12
View File
@@ -11,7 +11,7 @@ from services.db_context import db
from models.level_user import LevelUser
from configs.config import Config
from utils.manager import group_manager, plugins2settings_manager, plugins_manager
from utils.image_utils import CreateImg
from utils.image_utils import BuildImage
from utils.http_utils import AsyncHttpx
import asyncio
import time
@@ -72,7 +72,9 @@ async def custom_group_welcome(
logger.info(f"USER {user_id} GROUP {group_id} 更换群欢迎消息 {msg}")
result += msg
if img:
await AsyncHttpx.download_file(img, DATA_PATH + f"custom_welcome_msg/{group_id}.jpg")
await AsyncHttpx.download_file(
img, 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} 更换群欢迎消息图片")
except Exception as e:
@@ -107,7 +109,16 @@ async def change_group_switch(cmd: str, group_id: int, is_super: bool = False):
else:
if await group_manager.check_group_task_status(group_id, task):
await group_manager.close_group_task(group_id, task)
if group_help_file.exists():
group_help_file.unlink()
return f"已 {status} 全部被动技能!"
if cmd == "全部功能":
for f in plugins2settings_manager.get_data():
if status == "开启":
group_manager.unblock_plugin(f, group_id)
else:
group_manager.block_plugin(f, group_id)
return f"已 {status} 全部功能!"
if cmd in [task_data[x] for x in task_data.keys()]:
type_ = "task"
modules = [x for x in task_data.keys() if task_data[x] == cmd]
@@ -132,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):
return f"功能 {cmd} 正处于关闭状态!不要重复关闭."
group_manager.block_plugin(module, group_id)
if group_help_file.exists():
group_help_file.unlink()
if group_help_file.exists():
group_help_file.unlink()
if is_super:
for file in os.listdir(Path(DATA_PATH) / 'group_help'):
file = Path(DATA_PATH) / 'group_help' / file
for file in os.listdir(Path(DATA_PATH) / "group_help"):
file = Path(DATA_PATH) / "group_help" / file
file.unlink()
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():
_help_image.unlink()
return f"{status} {cmd} 功能!"
@@ -158,8 +169,8 @@ def set_plugin_status(cmd: str, block_type: str = "all"):
plugins_manager.unblock_plugin(module)
else:
plugins_manager.block_plugin(module, block_type=block_type)
for file in os.listdir(Path(DATA_PATH) / 'group_help'):
file = Path(DATA_PATH) / 'group_help' / file
for file in os.listdir(Path(DATA_PATH) / "group_help"):
file = Path(DATA_PATH) / "group_help" / file
file.unlink()
@@ -195,11 +206,11 @@ def _get_plugin_status() -> MessageSegment:
rst += "\n"
flag_str += f"{flag}\n"
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)
b = CreateImg(200, height, font_size=20)
b = BuildImage(200, height, font_size=20)
b.text((10, 10), flag_str)
A = CreateImg(500, height)
A = BuildImage(500, height)
A.paste(a)
A.paste(b, (270, 0))
return image(b64=A.pic2bs4())
@@ -288,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]
)
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 state: pass
"""
global cmd
try:
if not cmd:
cmd.append('关闭全部被动')
cmd.append('开启全部被动')
cmd = ["关闭全部被动", "开启全部被动", "开启全部功能", "关闭全部功能"]
_data = group_manager.get_task_data()
for key in _data:
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.adapters.cqhttp import Bot, GroupMessageEvent, MessageEvent, GROUP
from .data_source import (
@@ -6,12 +6,14 @@ from .data_source import (
set_plugin_status,
get_plugin_status,
group_current_status,
set_group_bot_status
)
from services.log import logger
from configs.config import NICKNAME, Config
from utils.utils import get_message_text, is_number
from nonebot.permission import SUPERUSER
from .rule import switch_rule
import re
__zx_plugin_name__ = "群功能开关 [Admin]"
@@ -24,6 +26,7 @@ usage:
群被动状态
开启全部被动
关闭全部被动
醒来/休息吧
示例:开启/关闭色图
""".strip()
__plugin_superuser_usage__ = """
@@ -40,6 +43,7 @@ __plugin_cmd__ = [
"群被动状态",
"开启全部被动",
"关闭全部被动",
"醒来/休息吧",
"功能状态 [_superuser]",
"开启/关闭[功能] [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_status = on_regex("^(休息吧|醒来)$", permission=GROUP, priority=5, block=True)
@switch_rule_matcher.handle()
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()
async def _(bot: Bot, event: GroupMessageEvent, state: T_State):
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)}")
+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 services.log import logger
from utils.utils import get_matchers
@@ -84,8 +84,8 @@ def _create_help_image():
for i, x in enumerate(task_data.keys()):
help_str += f'{i+1}.开启/关闭{task_data[x]}\n\n'
height = len(help_str.split("\n")) * 33
A = CreateImg(width, height, font_size=24)
_background = CreateImg(width, height, background=background)
A = BuildImage(width, height, font_size=24)
_background = BuildImage(width, height, background=background)
A.text((150, 110), help_str)
A.paste(_background, alpha=True)
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.friend_user import FriendUser
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 pathlib import Path
import shutil
__zx_plugin_name__ = "定时任务相关 [Hidden]"
__plugin_version__ = 0.1
@@ -14,7 +16,33 @@ __plugin_author__ = "HibiKier"
__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}")
# 自动备份
@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}")
# 一次性任务
# 固定时间触发,仅触发一次:
#
+10 -2
View File
@@ -3,10 +3,11 @@ from nonebot.typing import T_State
from nonebot.adapters import Bot, Event
from nonebot.permission import SUPERUSER
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 utils.message_builder import image
from utils.manager import group_manager
from configs.config import Config
__zx_plugin_name__ = "广播 [Superuser]"
__plugin_usage__ = """
@@ -20,6 +21,13 @@ __plugin_cmd__ = ["广播-"]
__plugin_version__ = 0.1
__plugin_author__ = "HibiKier"
__plugin_task__ = {"broadcast": "广播"}
Config.add_plugin_config(
"_task",
"DEFAULT_BROADCAST",
True,
help_="被动 广播 进群默认开关状态",
default_value=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()
async def _(bot: Bot, event: Event, state: T_State):
msg = get_message_text(event.json())
imgs = get_message_imgs(event.json())
imgs = get_message_img(event.json())
rst = ""
for img in imgs:
rst += image(img)
+16
View File
@@ -38,6 +38,22 @@ Config.add_plugin_config(
Config.add_plugin_config(
"invite_manager", "welcome_msg_cd", 5, help_="群欢迎消息cd", default_value=5
)
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"))
+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 utils.manager import (
plugins2settings_manager,
@@ -50,7 +50,7 @@ def _create_help_img(
_des_tmp = {}
_plugin_name_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
# 插件分类
for matcher in _matchers:
@@ -111,7 +111,7 @@ def _create_help_img(
logger.warning(f"{type(e)}: {e}")
else:
matchers_data[plugin_type][plugin_name] = {
"module": matcher.module,
"modules": matcher.module,
"des": plugin_des,
"cmd": plugin_cmd,
"text_type": text_type,
@@ -147,16 +147,16 @@ def _create_help_img(
# 禁用flag
flag = True
if plugins_manager.get_plugin_status(
matchers_data[type_][k]["module"], "all"
matchers_data[type_][k]["modules"], "all"
):
flag = False
if group_id:
flag = flag and plugins_manager.get_plugin_status(
matchers_data[type_][k]["module"], "group"
matchers_data[type_][k]["modules"], "group"
)
simple_help_str += (
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"
)
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)
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"
)
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")
]:
simple_width = simple_width if simple_width > x else x
bk = CreateImg(simple_width + 20, simple_height, font_size=24, color="#6495ED")
B = CreateImg(
bk = BuildImage(simple_width + 20, simple_height, font_size=24, color="#6495ED")
B = BuildImage(
simple_width + 20,
simple_height,
font_size=24,
@@ -236,7 +236,7 @@ def _create_help_img(
for img in help_img_list:
height += img.h
if not group_id:
A = CreateImg(width + 150, height + 50, font_size=24)
A = BuildImage(width + 150, height + 50, font_size=24)
A.text(
(10, 10), '* 注: ‘*’ 代表可有多个相同参数 ‘?’ 代表可省略该参数 *\n\n" "功能名: 功能简介 -> 指令\n\n'
)
@@ -253,14 +253,14 @@ def _create_help_img(
if img.h > height:
height = img.h
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)
bk = None
random_bk = os.listdir(random_bk_path)
if random_bk:
bk = random.choice(random_bk)
x = max(width + 50, height + 250)
B = CreateImg(
B = BuildImage(
x,
x,
font_size=24,
@@ -272,7 +272,7 @@ def _create_help_img(
w = 10
h = 10
for msg in ["目前支持的功能列表:", "可以通过 ‘帮助[功能名称]’ 来获取对应功能的使用方法", "或者使用 ‘详细帮助’ 来获取所有功能方法"]:
text = CreateImg(
text = BuildImage(
0,
0,
plain_text=msg,
@@ -284,7 +284,7 @@ def _create_help_img(
if msg == "目前支持的功能列表:":
w += 50
B.paste(
CreateImg(
BuildImage(
0,
0,
plain_text="注: 红字代表功能被群管理员禁用,红线代表功能正在维护",
@@ -299,8 +299,8 @@ def _create_help_img(
def get_max_width_or_paste(
simple_help_img_list: list, B: CreateImg = None, is_paste: bool = False
) -> "int, CreateImg":
simple_help_img_list: list, B: BuildImage = None, is_paste: bool = False
) -> "int, BuildImage":
"""
获取最大宽度,或直接贴图
: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 = width if width > _width else _width
height = len(result.split("\n")) * 45
A = CreateImg(width, height, font_size=24)
bk = CreateImg(
A = BuildImage(width, height, font_size=24)
bk = BuildImage(
width,
height,
background=Path(IMAGE_PATH) / "background" / "1.png",
-2
View File
@@ -1,5 +1,4 @@
from configs.config import Config
import nonebot
Config.add_plugin_config(
@@ -35,4 +34,3 @@ Config.add_plugin_config(
default_value=6
)
nonebot.load_plugins("basic_plugins/hooks")
+203 -35
View File
@@ -1,33 +1,42 @@
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 models.friend_user import FriendUser
from models.group_member_info import GroupInfoUser
from models.bag_user import BagUser
from utils.manager import (
plugins2settings_manager,
admin_manager,
group_manager,
plugins_manager,
plugins2cd_manager,
plugins2block_manager,
plugins2count_manager
)
from .utils import set_block_limit_false, status_message_manager
from nonebot.typing import T_State
from typing import Optional
from nonebot.adapters.cqhttp import (
Bot,
MessageEvent,
GroupMessageEvent,
PokeNotifyEvent,
PrivateMessageEvent,
Message,
Event
)
from configs.config import Config
from models.ban_user import BanUser
from utils.utils import FreqLimiter
from utils.message_builder import at
from models.level_user import LevelUser
import nonebot
_flmt = FreqLimiter(Config.get_config("hook", "CHECK_NOTICE_INFO_CD"))
_flmt_g = FreqLimiter(Config.get_config("hook", "CHECK_NOTICE_INFO_CD"))
_flmt_s = FreqLimiter(Config.get_config("hook", "CHECK_NOTICE_INFO_CD"))
_flmt_c = FreqLimiter(Config.get_config("hook", "CHECK_NOTICE_INFO_CD"))
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):
module = matcher.module
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
) or (
str(event.user_id) in bot.config.superusers
and plugins2info_dict.get(module)
and not plugins2info_dict[module]["limit_superuser"]
):
return
# 群黑名单检测
if isinstance(event, GroupMessageEvent):
if group_manager.get_group_level(event.group_id) < 0:
raise IgnoredException("群黑名单")
# 功能的金币检测 #######################################
# 功能的金币检测 #######################################
# 功能的金币检测 #######################################
cost_gold = 0
if isinstance(event, GroupMessageEvent) and plugins2settings_manager.get_plugin_data(module).get('cost_gold'):
cost_gold = plugins2settings_manager.get_plugin_data(module).get('cost_gold')
if await BagUser.get_gold(event.user_id, event.group_id) < cost_gold:
await send_msg(f"金币不足..该功能需要{cost_gold}金币..", bot, event)
raise IgnoredException(f"{module} 金币限制...")
# 当插件不阻塞超级用户时,超级用户提前扣除金币
if str(event.user_id) in bot.config.superusers and not plugins2info_dict[module]["limit_superuser"]:
await BagUser.spend_gold(event.user_id, event.group_id, cost_gold)
try:
if (
(not isinstance(event, MessageEvent) and module != "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 isinstance(event, GroupMessageEvent):
# 个人权限
if (
not await LevelUser.check_level(
event.user_id,
event.group_id,
admin_manager.get_plugin_level(module),
)
and admin_manager.get_plugin_level(module) > 0
not await LevelUser.check_level(
event.user_id,
event.group_id,
admin_manager.get_plugin_level(module),
)
and admin_manager.get_plugin_level(module) > 0
):
try:
if _flmt.check(event.user_id):
@@ -67,7 +112,7 @@ async def _(matcher: Matcher, bot: Bot, event: MessageEvent, state: T_State):
await bot.send_group_msg(
group_id=event.group_id,
message=f"{at(event.user_id)}你的权限不足喔,该功能需要的权限等级:"
f"{admin_manager.get_plugin_level(module)}",
f"{admin_manager.get_plugin_level(module)}",
)
except ActionFailed:
pass
@@ -77,7 +122,7 @@ async def _(matcher: Matcher, bot: Bot, event: MessageEvent, state: T_State):
raise IgnoredException("权限不足")
else:
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:
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 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:
status_message_manager.delete(event.group_id)
if plugins2info_dict[module]["level"] > group_manager.get_group_level(
event.group_id
event.group_id
):
try:
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):
try:
if module not in ignore_rst_module and _flmt_s.check(
event.group_id
event.group_id
):
_flmt_s.start_cd(event.group_id)
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):
try:
if (
_flmt_s.check(event.group_id)
and module not in ignore_rst_module
_flmt_s.check(event.group_id)
and module not in ignore_rst_module
):
_flmt_s.start_cd(event.group_id)
await bot.send_group_msg(
@@ -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"):
try:
if (
_flmt_c.check(event.group_id)
and module not in ignore_rst_module
_flmt_c.check(event.group_id)
and module not in ignore_rst_module
):
_flmt_c.start_cd(event.group_id)
await bot.send_group_msg(
@@ -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 isinstance(
event, GroupMessageEvent
event, GroupMessageEvent
) and group_manager.check_group_is_white(event.group_id):
return
try:
if isinstance(event, GroupMessageEvent):
if (
_flmt_c.check(event.group_id)
and module not in ignore_rst_module
_flmt_c.check(event.group_id)
and module not in ignore_rst_module
):
_flmt_c.start_cd(event.group_id)
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_)
set_block_limit_false(event, module)
raise IgnoredException("此功能正在维护...")
# 以下为限制检测 #######################################################
# 以下为限制检测 #######################################################
# 以下为限制检测 #######################################################
# 以下为限制检测 #######################################################
# 以下为限制检测 #######################################################
# 以下为限制检测 #######################################################
# Cd
if plugins2cd_manager.check_plugin_cd_status(module):
plugin_cd_data = plugins2cd_manager.get_plugin_cd_data(module)
check_type = plugin_cd_data["check_type"]
limit_type = plugin_cd_data["limit_type"]
rst = plugin_cd_data["rst"]
if (
(isinstance(event, PrivateMessageEvent) and check_type == "private")
or (isinstance(event, GroupMessageEvent) and check_type == "group")
or plugins2cd_manager.get_plugin_data(module).get("check_type") == "all"
):
cd_type_ = event.user_id
if limit_type == "group" and isinstance(event, GroupMessageEvent):
cd_type_ = event.group_id
if not plugins2cd_manager.check(module, cd_type_):
if rst:
rst = await init_rst(rst, event)
await send_msg(rst, bot, event)
raise IgnoredException(f"{module} 正在cd中...")
else:
plugins2cd_manager.start_cd(module, cd_type_)
# Block
if plugins2block_manager.check_plugin_block_status(module):
plugin_block_data = plugins2block_manager.get_plugin_block_data(module)
check_type = plugin_block_data["check_type"]
limit_type = plugin_block_data["limit_type"]
rst = plugin_block_data["rst"]
if (
(isinstance(event, PrivateMessageEvent) and check_type == "private")
or (isinstance(event, GroupMessageEvent) and check_type == "group")
or check_type == "all"
):
block_type_ = event.user_id
if limit_type == "group" and isinstance(event, GroupMessageEvent):
block_type_ = event.group_id
if plugins2block_manager.check(block_type_, module):
if rst:
rst = await init_rst(rst, event)
await send_msg(rst, bot, event)
raise IgnoredException(f"{event.user_id}正在调用{module}....")
else:
plugins2block_manager.set_true(block_type_, module)
# Count
if (
plugins2count_manager.check_plugin_count_status(module)
and event.user_id not in bot.config.superusers
):
plugin_count_data = plugins2count_manager.get_plugin_count_data(module)
limit_type = plugin_count_data["limit_type"]
rst = plugin_count_data["rst"]
count_type_ = event.user_id
if limit_type == "group" and isinstance(event, GroupMessageEvent):
count_type_ = event.group_id
if not plugins2count_manager.check(module, count_type_):
if rst:
rst = await init_rst(rst, event)
await send_msg(rst, bot, event)
raise IgnoredException(f"{module} count次数限制...")
else:
plugins2count_manager.increase(module, count_type_)
# 功能花费的金币 #######################################
# 功能花费的金币 #######################################
if cost_gold:
await BagUser.spend_gold(event.user_id, event.group_id, cost_gold)
async def send_msg(rst: str, bot: Bot, event: MessageEvent):
"""
发送信息
:param rst: pass
:param bot: pass
:param event: pass
"""
rst = await init_rst(rst, event)
try:
if isinstance(event, GroupMessageEvent):
status_message_manager.add(event.group_id)
await bot.send_group_msg(group_id=event.group_id, message=Message(rst))
else:
status_message_manager.add(event.user_id)
await bot.send_private_msg(user_id=event.user_id, message=Message(rst))
except ActionFailed:
pass
# 解除命令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
+5 -2
View File
@@ -9,7 +9,7 @@ from nonebot.adapters.cqhttp import (
)
from configs.config import Config
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
@@ -20,6 +20,8 @@ Config.add_plugin_config(
help_="对被ban用户发送的消息",
)
_flmt = FreqLimiter(300)
# 检查是否被ban
@run_preprocessor
@@ -55,7 +57,8 @@ async def _(matcher: Matcher, bot: Bot, event: MessageEvent, state: T_State):
if matcher.priority != 9:
try:
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(
group_id=event.group_id,
message=at(event.user_id)
-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
@@ -92,11 +92,14 @@ def init_plugins_config(data_path):
if user_config_file.exists():
with open(user_config_file, "r", encoding="utf8") as f:
_data = _yaml.load(f)
# 数据替换
for plugin in Config.keys():
_tmp_data[plugin] = {}
for k in Config[plugin].keys():
if _data.get(plugin) and k in _data[plugin].keys():
Config.set_config(plugin, k, _data[plugin][k])
if level2module := Config.get_level2module(plugin, k):
admin_manager.set_admin_level(level2module, _data[plugin][k])
_tmp_data[plugin][k] = Config.get_config(plugin, k)
Config.save()
temp_file = Path() / "configs" / "temp_config.yaml"
@@ -26,7 +26,7 @@ def init_plugins_settings(data_path: str):
plugin_name = _module.__getattribute__("__zx_plugin_name__")
_tmp_module[x] = plugin_name
except (KeyError, AttributeError) as e:
logger.error(f"配置文件 模块:{x} 获取 plugin_name 失败...{e}")
logger.warning(f"配置文件 模块:{x} 获取 plugin_name 失败...{e}")
_tmp_module[x] = ""
for matcher in _matchers:
if matcher.module not in plugins2settings_manager.keys():
@@ -71,6 +71,8 @@ def init_plugins_settings(data_path: str):
plugin_settings = _module.__getattribute__(
"__plugin_settings__"
)
if plugin_settings.get('cost_gold') is None:
plugin_settings['cost_gold'] = 0
if (
plugin_settings["cmd"] is not None
and plugin_name not in plugin_settings["cmd"]
@@ -97,7 +99,7 @@ def init_plugins_settings(data_path: str):
plugins2settings_manager.add_plugin_settings(
matcher.module,
plugin_type=plugin_type,
data_dict=plugin_settings,
**plugin_settings,
)
except AttributeError:
pass
+51 -44
View File
@@ -8,7 +8,7 @@ from models.friend_user import FriendUser
import random
from models.ban_user import BanUser
from services.log import logger
from configs.config import NICKNAME
from configs.config import NICKNAME, Config
__zx_plugin_name__ = "昵称系统"
@@ -23,6 +23,13 @@ __plugin_des__ = "区区昵称,才不想叫呢!"
__plugin_cmd__ = ["以后叫我 [昵称]", f"{NICKNAME}我是谁"]
__plugin_version__ = 0.1
__plugin_author__ = "HibiKier"
__plugin_configs__ = {
"BLACK_WORD": {
"value": ["爸", "爹", "爷"],
"help": "昵称所屏蔽的关键词,会被替换为 *",
"default_value": None
}
}
nickname = on_command(
"nickname",
@@ -47,52 +54,52 @@ async def _(bot: Bot, event: GroupMessageEvent, state: T_State):
await nickname.finish("叫你空白?叫你虚空?叫你无名??", at_sender=True)
if len(msg) > 10:
await nickname.finish("昵称可不能超过10个字!", at_sender=True)
if await GroupInfoUser.set_group_member_nickname(
event.user_id, event.group_id, msg
):
if len(msg) < 5:
if random.random() < 0.5:
msg = "~".join(msg)
await nickname.send(
random.choice(
[
f"好啦好啦,我知道啦,{msg},以后就这么叫你吧",
f"嗯嗯,{NICKNAME}记住你的昵称了哦,{msg}",
f"好突然,突然要叫你昵称什么的...{msg}..",
f"{NICKNAME}会好好记住的{msg}的,放心吧",
f"好..好.,那窝以后就叫你{msg}了.",
]
if msg in bot.config.superusers:
await nickname.finish("笨蛋!休想占用我的名字!#", at_sender=True)
_tmp = ""
black_word = Config.get_config("nickname", "BLACK_WORD")
for x in msg:
_tmp += "*" if x in black_word else x
msg = _tmp
if isinstance(event, GroupMessageEvent):
if await GroupInfoUser.set_group_member_nickname(
event.user_id, event.group_id, msg
):
if len(msg) < 5:
if random.random() < 0.3:
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:
await nickname.send("设置昵称失败,请更新群组成员信息!", at_sender=True)
logger.warning(f"USER {event.user_id} GROUP {event.group_id} 设置群昵称 {msg} 失败")
@nickname.handle()
async def _(bot: Bot, event: PrivateMessageEvent, state: T_State):
msg = get_message_text(event.json())
if not msg:
await nickname.finish("叫你空白?叫你虚空?叫你无名??", at_sender=True)
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}了.",
]
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}")
else:
await nickname.send("设置昵称失败了,明天再来试一试!或联系管理员更新好友!", at_sender=True)
logger.warning(f"USER {event.user_id} 设置昵称 {msg} 失败")
logger.info(f"USER {event.user_id} 设置昵称 {msg}")
else:
await nickname.send("设置昵称失败了,明天再来试一试!或联系管理员更新好友!", at_sender=True)
logger.warning(f"USER {event.user_id} 设置昵称 {msg} 失败")
@my_nickname.handle()
+130 -25
View File
@@ -1,12 +1,19 @@
from asyncpg.exceptions import (
DuplicateColumnError,
UndefinedColumnError,
PostgresSyntaxError,
)
from nonebot import Driver
from services.db_context import db
from asyncpg.exceptions import DuplicateColumnError
from models.group_info import GroupInfo
from models.bag_user import BagUser
from nonebot.adapters.cqhttp import Bot
from services.log import logger
from configs.path_config import TEXT_PATH
from asyncio.exceptions import TimeoutError
from typing import List
from utils.http_utils import AsyncHttpx
from utils.utils import scheduler
from pathlib import Path
import nonebot
@@ -27,31 +34,32 @@ async def update_city():
"""
china_city = Path(TEXT_PATH) / "china_city.json"
data = {}
try:
res = await AsyncHttpx.get(
"http://www.weather.com.cn/data/city3jdata/china.html", timeout=5
)
res.encoding = "utf8"
provinces_data = json.loads(res.text)
for province in provinces_data.keys():
data[provinces_data[province]] = []
if not china_city.exists():
try:
res = await AsyncHttpx.get(
f"http://www.weather.com.cn/data/city3jdata/provshi/{province}.html",
timeout=5,
"http://www.weather.com.cn/data/city3jdata/china.html", timeout=5
)
res.encoding = "utf8"
city_data = json.loads(res.text)
for city in city_data.keys():
data[provinces_data[province]].append(city_data[city])
with open(china_city, "w", encoding="utf8") as f:
json.dump(data, f, indent=4, ensure_ascii=False)
logger.info("自动更新城市列表完成.....")
except TimeoutError:
logger.warning("自动更新城市列表超时.....")
except ValueError:
logger.warning("自动城市列表失败.....")
except Exception as e:
logger.error(f"自动城市列表未知错误 {type(e)}:{e}")
provinces_data = json.loads(res.text)
for province in provinces_data.keys():
data[provinces_data[province]] = []
res = await AsyncHttpx.get(
f"http://www.weather.com.cn/data/city3jdata/provshi/{province}.html",
timeout=5,
)
res.encoding = "utf8"
city_data = json.loads(res.text)
for city in city_data.keys():
data[provinces_data[province]].append(city_data[city])
with open(china_city, "w", encoding="utf8") as f:
json.dump(data, f, indent=4, ensure_ascii=False)
logger.info("自动更新城市列表完成.....")
except TimeoutError:
logger.warning("自动更新城市列表超时.....")
except ValueError:
logger.warning("自动城市列表失败.....")
except Exception as e:
logger.error(f"自动城市列表未知错误 {type(e)}:{e}")
@driver.on_startup
@@ -59,15 +67,82 @@ async def _():
"""
数据库表结构变换
"""
_flag = []
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
(
"ALTER TABLE genshin ADD auto_sign_time timestamp with time zone;",
"genshin"
), # 新增原神自动签到字段
(
"ALTER TABLE genshin ADD resin_remind boolean DEFAULT False;",
"genshin"
), # 新增原神自动签到字段
(
"ALTER TABLE genshin ADD resin_recovery_time timestamp with time zone;",
"genshin"
), # 新增原神自动签到字段
]
for sql in sql_str:
try:
flag = sql[1]
sql = sql[0]
query = db.text(sql)
await db.first(query)
except DuplicateColumnError:
logger.info(f"完成sql操作:{sql}")
_flag.append(flag)
except (DuplicateColumnError, UndefinedColumnError):
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
@@ -101,3 +176,33 @@ async def _(bot: Bot):
else:
await GroupInfo.delete_group_info(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()
+1 -1
View File
@@ -11,4 +11,4 @@ Config.add_plugin_config(
)
nonebot.load_plugins("plugins/shop")
nonebot.load_plugins("basic_plugins/shop")
+90 -89
View File
@@ -1,89 +1,90 @@
from nonebot import on_command
from services.log import logger
from nonebot.adapters.cqhttp import Bot, GroupMessageEvent
from nonebot.typing import T_State
from utils.utils import get_message_text, is_number
from models.bag_user import BagUser
from services.db_context import db
from nonebot.adapters.cqhttp.permission import GROUP
from .models.goods_info import GoodsInfo
__zx_plugin_name__ = "商店 - 购买道具"
__plugin_usage__ = """
usage:
购买道具
指令:
购买 [序号或名称] ?[数量=1]
示例:购买 好感双倍加持卡Ⅰ
示例:购买 1 4
""".strip()
__plugin_des__ = "商店 - 购买道具"
__plugin_cmd__ = ["购买 [序号或名称] ?[数量=1]"]
__plugin_type__ = ('商店',)
__plugin_version__ = 0.1
__plugin_author__ = "HibiKier"
__plugin_settings__ = {
"level": 5,
"default_status": True,
"limit_superuser": False,
"cmd": ["商店", "购买道具"],
}
buy = on_command("购买", aliases={"购买道具"}, priority=5, block=True, permission=GROUP)
@buy.handle()
async def _(bot: Bot, event: GroupMessageEvent, state: T_State):
if get_message_text(event.json()) in ["神秘药水"]:
await buy.finish("你们看看就好啦,这是不可能卖给你们的~", at_sender=True)
goods_lst = await GoodsInfo.get_all_goods()
goods_name_lst = [x.goods_name for x in goods_lst]
msg = get_message_text(event.json()).strip().split(" ")
num = 1
if len(msg) > 1:
if is_number(msg[1]):
num = int(msg[1])
else:
await buy.finish("购买的数量要是数字!", at_sender=True)
# print(msg, num)
if is_number(msg[0]):
msg = int(msg[0])
if msg > len(goods_lst) or msg < 1:
await buy.finish("请输入正确的商品id!", at_sender=True)
goods = goods_lst[msg - 1]
else:
if msg[0] in goods_name_lst:
for i in range(len(goods_name_lst)):
if msg[0] == goods_name_lst[i]:
goods = goods_lst[i]
break
else:
await buy.finish("请输入正确的商品名称!")
else:
await buy.finish("请输入正确的商品名称!", at_sender=True)
async with db.transaction():
if (
await BagUser.get_gold(event.user_id, event.group_id)
) < goods.goods_price * num:
await buy.finish("您的金币好像不太够哦", at_sender=True)
if await BagUser.spend_gold(
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(
f"花费 {goods.goods_price*num} 金币购买 {goods.goods_name} ×{num} 成功!",
at_sender=True,
)
logger.info(
f"USER {event.user_id} GROUP {event.group_id} "
f"花费 {goods.goods_price*num} 金币购买 {goods.goods_name} ×{num} 成功!"
)
else:
await buy.send(f"{goods.goods_name} 购买失败!", at_sender=True)
logger.info(
f"USER {event.user_id} GROUP {event.group_id} "
f"花费 {goods.goods_price*num} 金币购买 {goods.goods_name} ×{num} 失败!"
)
from nonebot import on_command
from services.log import logger
from nonebot.adapters.cqhttp import Bot, GroupMessageEvent
from nonebot.typing import T_State
from utils.utils import get_message_text, is_number
from models.bag_user import BagUser
from services.db_context import db
from nonebot.adapters.cqhttp.permission import GROUP
from models.goods_info import GoodsInfo
import time
__zx_plugin_name__ = "商店 - 购买道具"
__plugin_usage__ = """
usage:
购买道具
指令:
购买 [序号或名称] ?[数量=1]
示例:购买 好感双倍加持卡Ⅰ
示例:购买 1 4
""".strip()
__plugin_des__ = "商店 - 购买道具"
__plugin_cmd__ = ["购买 [序号或名称] ?[数量=1]"]
__plugin_type__ = ("商店",)
__plugin_version__ = 0.1
__plugin_author__ = "HibiKier"
__plugin_settings__ = {
"level": 5,
"default_status": True,
"limit_superuser": False,
"cmd": ["商店", "购买道具"],
}
__plugin_cd_limit__ = {"cd": 3}
buy = on_command("购买", aliases={"购买道具"}, priority=5, block=True, permission=GROUP)
@buy.handle()
async def _(bot: Bot, event: GroupMessageEvent, state: T_State):
goods = None
if get_message_text(event.json()) in ["神秘药水"]:
await buy.finish("你们看看就好啦,这是不可能卖给你们的~", at_sender=True)
goods_name_lst = [
x.goods_name
for x in await GoodsInfo.get_all_goods()
if x.goods_limit_time > time.time() or x.goods_limit_time == 0
]
msg = get_message_text(event.json()).split()
num = 1
if len(msg) > 1:
if is_number(msg[1]) and int(msg[1]) > 0:
num = int(msg[1])
else:
await buy.finish("购买的数量要是数字且大于0!", at_sender=True)
if is_number(msg[0]):
msg = int(msg[0])
if msg > len(goods_name_lst) or msg < 1:
await buy.finish("请输入正确的商品id!", at_sender=True)
goods = goods_name_lst[msg - 1]
else:
if msg[0] in goods_name_lst:
for i in range(len(goods_name_lst)):
if msg[0] == goods_name_lst[i]:
goods = goods_name_lst[i]
break
else:
await buy.finish("请输入正确的商品名称!")
else:
await buy.finish("请输入正确的商品名称!", at_sender=True)
async with db.transaction():
if (
await BagUser.get_gold(event.user_id, event.group_id)
) < goods.goods_price * num * goods.goods_discount:
await buy.finish("您的金币好像不太够哦", at_sender=True)
if await BagUser.buy_property(event.user_id, event.group_id, goods, num):
await buy.send(
f"花费 {goods.goods_price * num * goods.goods_discount} 金币购买 {goods.goods_name} ×{num} 成功!",
at_sender=True,
)
logger.info(
f"USER {event.user_id} GROUP {event.group_id} "
f"花费 {goods.goods_price*num} 金币购买 {goods.goods_name} ×{num} 成功!"
)
else:
await buy.send(f"{goods.goods_name} 购买失败!", at_sender=True)
logger.info(
f"USER {event.user_id} GROUP {event.group_id} "
f"花费 {goods.goods_price * num * goods.goods_discount} 金币购买 {goods.goods_name} ×{num} 失败!"
)
+1 -1
View File
@@ -34,7 +34,7 @@ gold_rank = on_command("金币排行", priority=5, block=True, permission=GROUP)
@my_gold.handle()
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()
+42 -52
View File
@@ -1,52 +1,42 @@
from nonebot import on_command
from services.log import logger
from nonebot.adapters.cqhttp import Bot, GroupMessageEvent
from nonebot.typing import T_State
from models.bag_user import BagUser
from nonebot.adapters.cqhttp.permission import GROUP
__zx_plugin_name__ = "商店 - 我的道具"
__plugin_usage__ = """
usage:
我的道具
指令:
我的道具
""".strip()
__plugin_des__ = "商店 - 我的道具"
__plugin_cmd__ = ["我的道具"]
__plugin_type__ = ('商店',)
__plugin_version__ = 0.1
__plugin_author__ = "HibiKier"
__plugin_settings__ = {
"level": 5,
"default_status": True,
"limit_superuser": False,
"cmd": ["商店", "我的道具"],
}
my_props = on_command("我的道具", priority=5, block=True, permission=GROUP)
@my_props.handle()
async def _(bot: Bot, event: GroupMessageEvent, state: T_State):
props = await BagUser.get_props(event.user_id, event.group_id)
if props:
pname_list = []
pnum_list = []
rst = ""
props = props[:-1].split(",")
for p in props:
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)
logger.info(f"USER {event.user_id} GROUP {event.group_id} 查看我的道具")
else:
await my_props.finish("您的背包里没有任何的道具噢~", at_sender=True)
from nonebot import on_command
from services.log import logger
from nonebot.adapters.cqhttp import Bot, GroupMessageEvent
from nonebot.typing import T_State
from models.bag_user import BagUser
from nonebot.adapters.cqhttp.permission import GROUP
__zx_plugin_name__ = "商店 - 我的道具"
__plugin_usage__ = """
usage:
我的道具
指令:
我的道具
""".strip()
__plugin_des__ = "商店 - 我的道具"
__plugin_cmd__ = ["我的道具"]
__plugin_type__ = ('商店',)
__plugin_version__ = 0.1
__plugin_author__ = "HibiKier"
__plugin_settings__ = {
"level": 5,
"default_status": True,
"limit_superuser": False,
"cmd": ["商店", "我的道具"],
}
my_props = on_command("我的道具", priority=5, block=True, permission=GROUP)
@my_props.handle()
async def _(bot: Bot, event: GroupMessageEvent, state: T_State):
props = await BagUser.get_property(event.user_id, event.group_id)
if props:
rst = ""
for i, p in enumerate(props.keys()):
rst += f"{i+1}.{p}\t×{props[p]}\n"
await my_props.send("\n" + rst[:-1], at_sender=True)
logger.info(f"USER {event.user_id} GROUP {event.group_id} 查看我的道具")
else:
await my_props.finish("您的背包里没有任何的道具噢~", at_sender=True)
+26 -26
View File
@@ -1,26 +1,26 @@
from utils.utils import scheduler
from models.bag_user import BagUser
from services.log import logger
__zx_plugin_name__ = "每日金币重置 [Hidden]"
__plugin_version__ = 0.1
__plugin_author__ = "HibiKier"
# 重置每日金币
@scheduler.scheduled_job(
"cron",
hour=0,
minute=1,
)
async def _():
try:
user_list = await BagUser.get_all_users()
for user in user_list:
await user.update(
get_today_gold=0,
spend_today_gold=0,
).apply()
except Exception as e:
logger.error(f"重置每日金币错误 e:{e}")
from utils.utils import scheduler
from models.bag_user import BagUser
from services.log import logger
__zx_plugin_name__ = "每日金币重置 [Hidden]"
__plugin_version__ = 0.1
__plugin_author__ = "HibiKier"
# 重置每日金币
@scheduler.scheduled_job(
"cron",
hour=0,
minute=1,
)
async def _():
try:
user_list = await BagUser.get_all_users()
for user in user_list:
await user.update(
get_today_gold=0,
spend_today_gold=0,
).apply()
except Exception as e:
logger.error(f"重置每日金币错误 e:{e}")
+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"):
kwargs["limit_time"] = float(kwargs["limit_time"])
new_time = time.strftime(
"%Y-%m-%d %H:%M:%S",
time.localtime(time.time() + 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()
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
+83 -71
View File
@@ -1,71 +1,83 @@
from nonebot import on_command
from services.log import logger
from nonebot.adapters.cqhttp import Bot, GroupMessageEvent
from nonebot.typing import T_State
from utils.utils import is_number, get_message_text
from models.bag_user import BagUser
from nonebot.adapters.cqhttp.permission import GROUP
from services.db_context import db
from .data_source import effect
__zx_plugin_name__ = "商店 - 使用道具"
__plugin_usage__ = """
usage:
普通的使用道具
指令:
使用道具 [序号或道具名称]
* 序号以 ”我的道具“ 为准 *
""".strip()
__plugin_des__ = "商店 - 使用道具"
__plugin_cmd__ = ["使用道具 [序号或道具名称]"]
__plugin_type__ = ('商店',)
__plugin_version__ = 0.1
__plugin_author__ = "HibiKier"
__plugin_settings__ = {
"level": 5,
"default_status": True,
"limit_superuser": False,
"cmd": ["商店", "使用道具"],
}
use_props = on_command("使用道具", priority=5, block=True, permission=GROUP)
@use_props.handle()
async def _(bot: Bot, event: GroupMessageEvent, state: T_State):
msg = get_message_text(event.json())
if msg in ["", "帮助"]:
await use_props.finish(__plugin_usage__)
props = await BagUser.get_props(event.user_id, event.group_id)
if props:
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 0 < int(msg) <= len(pname_list):
name = pname_list[int(msg) - 1]
else:
await use_props.finish("仔细看看自己的道具仓库有没有这个道具?", at_sender=True)
else:
if msg not in pname_list:
await use_props.finish("道具名称错误!", at_sender=True)
name = msg
if await BagUser.del_props(
event.user_id, event.group_id, name
) and await effect(event.user_id, event.group_id, name):
await use_props.send(f"使用道具 {name} 成功!", at_sender=True)
logger.info(
f"USER {event.user_id} GROUP {event.group_id} 使用道具 {name} 成功"
)
else:
await use_props.send(f"使用道具 {name} 失败!", at_sender=True)
logger.info(
f"USER {event.user_id} GROUP {event.group_id} 使用道具 {name} 失败"
)
else:
await use_props.send("您的背包里没有任何的道具噢~", at_sender=True)
from nonebot import on_command
from services.log import logger
from nonebot.adapters.cqhttp import Bot, GroupMessageEvent
from nonebot.typing import T_State
from utils.utils import is_number, get_message_text
from models.bag_user import BagUser
from nonebot.adapters.cqhttp.permission import GROUP
from services.db_context import db
from nonebot.plugin import export
from .data_source import effect, register_use, func_manager
__zx_plugin_name__ = "商店 - 使用道具"
__plugin_usage__ = """
usage:
普通的使用道具
指令:
使用道具 [序号或道具名称] ?[数量]=1
* 序号以 ”我的道具“ 为准 *
""".strip()
__plugin_des__ = "商店 - 使用道具"
__plugin_cmd__ = ["使用道具 [序号或道具名称]"]
__plugin_type__ = ('商店',)
__plugin_version__ = 0.1
__plugin_author__ = "HibiKier"
__plugin_settings__ = {
"level": 5,
"default_status": True,
"limit_superuser": False,
"cmd": ["商店", "使用道具"],
}
# 导出方法供其他插件使用
export = export()
export.register_use = register_use
use_props = on_command("使用道具", priority=5, block=True, permission=GROUP)
@use_props.handle()
async def _(bot: Bot, event: GroupMessageEvent, state: T_State):
msg = get_message_text(event.json())
if msg in ["", "帮助"]:
await use_props.finish(__plugin_usage__)
num = 1
msg_sp = msg.split()
if len(msg_sp) > 1 and is_number(msg_sp[-1]) and int(msg_sp[-1]) > 0:
num = int(msg.split()[-1])
msg = " ".join(msg.split()[:-1])
property_ = await BagUser.get_property(event.user_id, event.group_id)
if property_:
async with db.transaction():
if is_number(msg):
if 0 < int(msg) <= len(property_):
name = list(property_.keys())[int(msg) - 1]
else:
await use_props.finish("仔细看看自己的道具仓库有没有这个道具?", at_sender=True)
else:
if msg not in property_.keys():
await use_props.finish("道具名称错误!", at_sender=True)
name = msg
_user_prop_count = property_[name]
if num > _user_prop_count:
await use_props.finish(f"道具数量不足,无法使用{num}次!")
if num > (n := func_manager.get_max_num_limit(name)):
await use_props.finish(f"该道具单次只能使用 {n} 个!")
if await BagUser.delete_property(
event.user_id, event.group_id, name, num
):
if func_manager.check_send_success_message(name):
await use_props.send(f"使用道具 {name} {num} 次成功!", at_sender=True)
if msg := await effect(bot, event, name, num):
await use_props.send(msg, at_sender=True)
logger.info(
f"USER {event.user_id} GROUP {event.group_id} 使用道具 {name} {num} 次成功"
)
else:
await use_props.send(f"使用道具 {name} {num} 次失败!", at_sender=True)
logger.info(
f"USER {event.user_id} GROUP {event.group_id} 使用道具 {name} {num} 次失败"
)
else:
await use_props.send("您的背包里没有任何的道具噢", at_sender=True)
+121
View File
@@ -0,0 +1,121 @@
from nonebot.adapters.cqhttp import GroupMessageEvent, MessageSegment
from services.log import logger
from nonebot.adapters.cqhttp import Bot
from typing import Optional, Union
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) -> Optional[Union[str, MessageSegment]]:
"""
使用道具
:param kwargs: kwargs
"""
goods_name = kwargs.get("goods_name")
if self.exists(goods_name):
if asyncio.iscoroutinefunction(self._data[goods_name]["func"]):
return await self._data[goods_name]["func"](
**kwargs,
)
else:
return 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
) -> Optional[Union[str, MessageSegment]]:
"""
商品生效
: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)
return 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,
}
)
except Exception as e:
logger.error(f"use 商品生效函数effect 发生错误 {type(e)}:{e}")
return None
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} 的使用函数")
+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}")
+35 -1
View File
@@ -8,13 +8,20 @@ from utils.manager import (
group_manager,
)
from nonebot.typing import T_State
from configs.config import Config
from services.log import logger
from nonebot.adapters.cqhttp import Bot, MessageEvent
from utils.utils import scheduler
__zx_plugin_name__ = "重载插件配置 [Superuser]"
__plugin_usage__ = """
usage:
重载插件配置
plugins2settings,
plugins2cd
plugins2block
group_manager
指令:
重载插件配置
""".strip()
@@ -24,10 +31,22 @@ __plugin_cmd__ = [
]
__plugin_version__ = 0.1
__plugin_author__ = "HibiKier"
__plugin_configs__ = {
"AUTO_RELOAD": {
"value": False,
"help": "自动重载配置文件",
"default_value": False
},
"AUTO_RELOAD_TIME": {
"value": 180,
"help": "控制自动重载配置文件时长",
"default_value": 180
}
}
reload_plugins_manager = on_command(
"重载插件限制", rule=to_me(), permission=SUPERUSER, priority=1, block=True
"重载配置", rule=to_me(), permission=SUPERUSER, priority=1, block=True
)
@@ -37,4 +56,19 @@ async def _(bot: Bot, event: MessageEvent, state: T_State):
plugins2cd_manager.reload()
plugins2block_manager.reload()
group_manager.reload()
Config.reload()
await reload_plugins_manager.send("重载完成...")
@scheduler.scheduled_job(
'interval',
seconds=Config.get_config("reload_setting", "AUTO_RELOAD_TIME", 180),
)
async def _():
if Config.get_config("reload_setting", "AUTO_RELOAD"):
plugins2settings_manager.reload()
plugins2cd_manager.reload()
plugins2block_manager.reload()
group_manager.reload()
Config.reload()
logger.debug("已自动重载所有配置文件...")
@@ -40,7 +40,7 @@ async def _(bot: Bot, event: GroupMessageEvent, state: T_State):
group_id = -1
level = 0
try:
args = get_message_text(event.json()).strip().split()
args = get_message_text(event.json()).split()
qq = get_message_at(event.json())
flag = -1
if not qq:
@@ -55,18 +55,17 @@ async def _(bot: Bot, event: GroupMessageEvent, state: T_State):
else:
await super_cmd.finish(
"权限参数不完全\n\t格式:添加/删除权限 [at] [level]"
"\n\t格式:添加/删除权限 [qq] [group] [level]",
"\n\t格式:添加/删除权限 [qq] [group_id] [level]",
at_sender=True,
)
else:
if not is_number(args[0]):
await super_cmd.finish("所有参数必须是数字!", at_sender=True)
level = int(args[0])
qq = qq[0]
group_id = event.group_id
flag = 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):
result = "添加管理成功, 权限: " + str(level)
else:
+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 services.log import logger
from utils.utils import get_matchers
@@ -32,7 +32,7 @@ def _create_help_image():
_plugin_name_list = []
width = 0
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:
plugin_name = ""
try:
@@ -72,8 +72,8 @@ def _create_help_image():
)
height = len(help_str.split("\n")) * 33
width += 500
A = CreateImg(width, height, font_size=24)
_background = CreateImg(width, height, background=background)
A = BuildImage(width, height, font_size=24)
_background = BuildImage(width, height, background=background)
A.text((300, 140), help_str)
A.paste(_background, alpha=True)
A.save(superuser_help_image)
+3 -1
View File
@@ -9,10 +9,12 @@ driver.register_adapter("cqhttp", CQHTTPBot)
config = driver.config
driver.on_startup(init)
driver.on_shutdown(disconnect)
# 优先加载定时任务插件
# 优先加载定时任务
nonebot.load_plugin("nonebot_plugin_apscheduler")
nonebot.load_plugins("basic_plugins")
nonebot.load_plugins("plugins")
# 最后加载权限控制
nonebot.load_plugins("basic_plugins/hooks")
if __name__ == "__main__":
+2 -2
View File
@@ -17,8 +17,8 @@ address: str = "" # 数据库地址
port: str = "" # 数据库端口
database: str = "" # 数据库名称
# 全局代理,例如 "http://127.0.0.1:7890"
SYSTEM_PROXY: Optional[str] = None
# 代理,例如 "http://127.0.0.1:7890"
SYSTEM_PROXY: Optional[str] = None # 全局代理
Config = ConfigsManager(Path() / "data" / "configs" / "plugins2config.yaml")
+17 -2
View File
@@ -29,7 +29,7 @@ class ConfigsManager:
name: Optional[str] = None,
help_: 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,
"help": help_.strip() if isinstance(help_, str) else help_,
"default_value": default_value,
"level_module": _module,
}
def remove_plugin_config(self, module: str):
@@ -104,11 +105,12 @@ class ConfigsManager:
if self._data[module].get(key) is not None:
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 key: 配置名称
:param default: 没有key值内容的默认返回值
"""
key = key.upper()
if module in self._data.keys():
@@ -117,8 +119,21 @@ class ConfigsManager:
if self._data[module][key]["value"] is None:
return self._data[module][key]["default_value"]
return self._data[module][key]["value"]
if default is not None:
return default
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):
"""
获取插件配置数据
Binary file not shown.

After

Width:  |  Height:  |  Size: 772 KiB

BIN
View File
Binary file not shown.

After

Width:  |  Height:  |  Size: 694 KiB

+105 -110
View File
@@ -1,38 +1,39 @@
from services.db_context import db
from typing import Dict
from typing import Optional, List
from services.log import logger
class BagUser(db.Model):
__tablename__ = "bag_users"
id = db.Column(db.Integer(), primary_key=True)
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)
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)
get_total_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)
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
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 belonging_group: 所在群号
:param group_id: 所在群号
"""
query = cls.query.where(
(cls.user_qq == user_qq) & (cls.belonging_group == belonging_group)
)
query = cls.query.where((cls.user_qq == user_qq) & (cls.group_id == group_id))
user = await query.gino.first()
if not user:
user = await cls.create(
user_qq=user_qq,
belonging_group=belonging_group,
group_id=group_id,
)
return (
f"当前金币:{user.gold}\n今日获取金币:{user.get_today_gold}\n今日花费金币:{user.spend_today_gold}"
@@ -41,179 +42,173 @@ class BagUser(db.Model):
)
@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 belonging_group: 所在群号
:param group_id: 所在群号
"""
query = cls.query.where(
(cls.user_qq == user_qq) & (cls.belonging_group == belonging_group)
)
query = cls.query.where((cls.user_qq == user_qq) & (cls.group_id == group_id))
user = await query.gino.first()
if user:
return user.gold
else:
await cls.create(
user_qq=user_qq,
belonging_group=belonging_group,
group_id=group_id,
)
return 100
@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 belonging_group: 所在群号
:param group_id: 所在群号
"""
query = cls.query.where(
(cls.user_qq == user_qq) & (cls.belonging_group == belonging_group)
)
query = cls.query.where((cls.user_qq == user_qq) & (cls.group_id == group_id))
user = await query.gino.first()
if user:
return user.props
return user.property
else:
await cls.create(
user_qq=user_qq,
belonging_group=belonging_group,
group_id=group_id,
)
return ""
return {}
@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 belonging_group: 所在群号
:param group_id: 所在群号
:param num: 金币数量
"""
query = cls.query.where(
(cls.user_qq == user_qq) & (cls.belonging_group == belonging_group)
)
query = cls.query.where((cls.user_qq == user_qq) & (cls.group_id == group_id))
query = query.with_for_update()
user = await query.gino.first()
try:
if user:
await user.update(
gold=user.gold + num,
get_total_gold=user.get_total_gold + num,
get_today_gold=user.get_today_gold + num,
).apply()
else:
await cls.create(
user_qq=user_qq,
belonging_group=belonging_group,
gold=100 + num,
get_total_gold=num,
get_today_gold=num,
)
return True
except Exception:
return False
if user:
await user.update(
gold=user.gold + num,
get_total_gold=user.get_total_gold + num,
get_today_gold=user.get_today_gold + num,
).apply()
else:
await cls.create(
user_qq=user_qq,
group_id=group_id,
gold=100 + num,
get_total_gold=num,
get_today_gold=num,
)
@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 belonging_group: 所在群号
:param group_id: 所在群号
:param num: 金币数量
"""
query = cls.query.where(
(cls.user_qq == user_qq) & (cls.belonging_group == belonging_group)
)
query = cls.query.where((cls.user_qq == user_qq) & (cls.group_id == group_id))
query = query.with_for_update()
user = await query.gino.first()
try:
if user:
await user.update(
gold=user.gold - num,
spend_total_gold=user.spend_total_gold + num,
spend_today_gold=user.spend_today_gold + num,
).apply()
else:
await cls.create(
user_qq=user_qq,
belonging_group=belonging_group,
gold=100 - num,
spend_total_gold=num,
spend_today_gold=num,
)
return True
except Exception:
return False
if user:
await user.update(
gold=user.gold - num,
spend_total_gold=user.spend_total_gold + num,
spend_today_gold=user.spend_today_gold + num,
).apply()
else:
await cls.create(
user_qq=user_qq,
group_id=group_id,
gold=100 - num,
spend_total_gold=num,
spend_today_gold=num,
)
@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 belonging_group: 所在群号
:param group_id: 所在群号
:param name: 道具名称
"""
query = cls.query.where(
(cls.user_qq == user_qq) & (cls.belonging_group == belonging_group)
)
query = cls.query.where((cls.user_qq == user_qq) & (cls.group_id == group_id))
query = query.with_for_update()
user = await query.gino.first()
try:
if user:
await user.update(props=user.props + f"{name},").apply()
if user:
p = user.property
if p.get(name) is None:
p[name] = 1
else:
await cls.create(
user_qq=user_qq, belonging_group=belonging_group, props=f"{name},"
)
return True
except Exception:
return False
p[name] += 1
await user.update(property=p).apply()
else:
await cls.create(user_qq=user_qq, group_id=group_id, property={name: 1})
@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 belonging_group: 所在群号
:param group_id: 所在群号
:param name: 道具名称
:param num: 使用个数
"""
query = cls.query.where(
(cls.user_qq == user_qq) & (cls.belonging_group == belonging_group)
)
query = cls.query.where((cls.user_qq == user_qq) & (cls.group_id == group_id))
query = query.with_for_update()
user = await query.gino.first()
try:
if user:
rst = ""
props = user.props
if props.find(name) != -1:
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
if user:
property_ = user.property
if name in property_:
if property_.get(name) == num:
del property_[name]
else:
return False
else:
return False
except Exception:
property_[name] -= num
await user.update(property=property_).apply()
return True
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
@classmethod
@@ -227,5 +222,5 @@ class BagUser(db.Model):
if not group_id:
query = await cls.query.gino.all()
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
+7 -1
View File
@@ -1,4 +1,5 @@
from services.db_context import db
from configs.config import Config
class FriendUser(db.Model):
@@ -80,7 +81,12 @@ class FriendUser(db.Model):
user = await query.gino.first()
if user:
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 ""
@classmethod
+15 -11
View File
@@ -1,5 +1,6 @@
from services.db_context import db
from typing import Optional, List
from services.log import logger
class GoodsInfo(db.Model):
@@ -34,16 +35,18 @@ class GoodsInfo(db.Model):
:param goods_limit_time: 商品限时
"""
try:
await cls.create(
goods_name=goods_name,
goods_price=goods_price,
goods_description=goods_description,
goods_discount=goods_discount,
goods_limit_time=goods_limit_time,
)
return True
except Exception:
return False
if not await cls.get_goods_info(goods_name):
await cls.create(
goods_name=goods_name,
goods_price=goods_price,
goods_description=goods_description,
goods_discount=goods_discount,
goods_limit_time=goods_limit_time,
)
return True
except Exception as e:
logger.error(f"GoodsInfo add_goods 发生错误 {type(e)}:{e}")
return False
@classmethod
async def delete_goods(cls, goods_name: str) -> bool:
@@ -99,7 +102,8 @@ class GoodsInfo(db.Model):
if goods_limit_time:
await query.update(goods_limit_time=goods_limit_time).apply()
return True
except Exception:
except Exception as e:
logger.error(f"GoodsInfo update_goods 发生错误 {type(e)}:{e}")
return False
@classmethod
+47 -29
View File
@@ -1,5 +1,5 @@
from datetime import datetime
from configs.config import Config
from services.db_context import db
from typing import List, Optional
@@ -10,18 +10,18 @@ class GroupInfoUser(db.Model):
id = db.Column(db.Integer(), primary_key=True)
user_qq = db.Column(db.BigInteger(), 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)
nickname = db.Column(db.Unicode())
uid = db.Column(db.BigInteger())
_idx1 = db.Index("info_group_users_idx1", "user_qq", "belonging_group", unique=True)
_idx1 = db.Index("info_group_users_idx1", "user_qq", "group_id", unique=True)
@classmethod
async def add_member_info(
cls,
user_qq: int,
belonging_group: int,
group_id: int,
user_name: str,
user_join_time: datetime,
uid: Optional[int] = None,
@@ -31,20 +31,20 @@ class GroupInfoUser(db.Model):
添加群内用户信息
参数:
:param user_qq: qq号
:param belonging_group: 群号
:param group_id: 群号
:param user_name: 用户名称
:param user_join_time: 入群时间
:param uid: 用户唯一 id(自动生成)
"""
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:
if not await query.gino.first():
await cls.create(
user_qq=user_qq,
user_name=user_name,
belonging_group=belonging_group,
group_id=group_id,
user_join_time=user_join_time,
uid=uid
)
@@ -54,31 +54,31 @@ class GroupInfoUser(db.Model):
@classmethod
async def get_member_info(
cls, user_qq: int, belonging_group: int
cls, user_qq: int, group_id: int
) -> "GroupInfoUser":
"""
说明:
查询群员信息
参数:
:param user_qq: qq号
:param belonging_group: 群号
:param group_id: 群号
"""
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()
@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 belonging_group: 群号
:param group_id: 群号
"""
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()
user = await query.gino.first()
@@ -87,40 +87,40 @@ class GroupInfoUser(db.Model):
return True
else:
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()
return True
except Exception:
return False
@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
参数:
:param belonging_group: 群号
:param group_id: 群号
"""
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():
member_list.append(user.user_qq)
return member_list
@classmethod
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:
"""
说明:
设置群员在该群内的昵称
参数:
:param user_qq: qq号
:param belonging_group: 群号
:param group_id: 群号
:param nickname: 昵称
"""
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()
if user:
@@ -129,30 +129,48 @@ class GroupInfoUser(db.Model):
return False
@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 belonging_group: 群号
:param group_id: 群号
"""
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()
if user:
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 ""
@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(
(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()
_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 = _max_uid_user.uid
if not user or not user.uid:
@@ -162,9 +180,9 @@ class GroupInfoUser(db.Model):
return x.uid
else:
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(
(cls.user_qq == user_qq) & (cls.belonging_group == belonging_group)
(cls.user_qq == user_qq) & (cls.group_id == group_id)
).gino.first()
await user.update(
uid=_max_uid + 1,
+39 -12
View File
@@ -1,5 +1,5 @@
from datetime import datetime
from typing import List
from services.db_context import db
@@ -8,8 +8,7 @@ class SignGroupUser(db.Model):
id = db.Column(db.Integer(), primary_key=True)
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_time_last = db.Column(db.DateTime(timezone=True), 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
)
_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
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":
"""
说明:
获取签到用户
参数:
:param user_qq: 用户qq
:param group_id: 所在群聊
:param for_update: 是否存在修改数据
"""
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:
query = query.with_for_update()
user = await query.gino.first()
return user or await cls.create(
user_qq=user_qq,
belonging_group=belonging_group,
group_id=group_id,
checkin_count=0,
checkin_time_last=datetime.min, # 从未签到过
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
async def sign(cls, user: "SignGroupUser", impression: float, checkin_time_last: datetime):
"""
说明:
签到
说明:
:param user: 用户
:param impression: 增加的好感度
:param checkin_time_last: 签到时间
"""
await user.update(
checkin_count=user.checkin_count + 1,
checkin_time_last=checkin_time_last,
@@ -51,22 +78,22 @@ class SignGroupUser(db.Model):
).apply()
@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 及对应 好感度
参数:
:param belonging_group: 群号
:param group_id: 群号
"""
impression_list = []
user_qq_list = []
user_group = []
if belonging_group:
query = cls.query.where(cls.belonging_group == belonging_group)
if group_id:
query = cls.query.where(cls.group_id == group_id)
else:
query = cls.query
for user in await query.gino.all():
impression_list.append(user.impression)
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
+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)
+14 -17
View File
@@ -11,7 +11,7 @@ from nonebot.typing import T_State
from models.friend_user import FriendUser
from models.group_member_info import GroupInfoUser
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 configs.config import NICKNAME, Config
@@ -21,27 +21,22 @@ usage:
与{NICKNAME}普普通通的对话吧!
"""
__plugin_version__ = 0.1
__plugin_author__ = 'HibiKier'
__plugin_author__ = "HibiKier"
__plugin_settings__ = {
"level": 5,
"cmd": ["Ai", "ai", "AI", "aI"],
}
__plugin_configs__ = {
"TL_KEY": {
"value": [],
"help": "图灵Key"
"TL_KEY": {"value": [], "help": "图灵Key"},
"ALAPI_AI_CHECK": {"value": False, "help": "是否检测青云客骂娘回复", "default_value": False},
"TEXT_FILTER": {
"value": ["鸡", "口交"],
"help": "文本过滤器,将敏感词更改为*",
"default_value": [],
},
"ALAPI_AI_CHECK": {
"value": False,
"help": "是否检测青云客骂娘回复",
"default_value": False
}
}
Config.add_plugin_config(
"alapi",
"ALAPI_TOKEN",
None,
help_="在https://admin.alapi.cn/user/login登录后获取token"
"alapi", "ALAPI_TOKEN", None, help_="在https://admin.alapi.cn/user/login登录后获取token"
)
ai = on_message(rule=to_me(), priority=8)
@@ -50,11 +45,11 @@ ai = on_message(rule=to_me(), priority=8)
@ai.handle()
async def _(bot: Bot, event: MessageEvent, state: T_State):
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()):
return
# 打招呼
if (not msg and not imgs) or msg in [
if (not msg and not img) or msg in [
"你好啊",
"你好",
"在吗",
@@ -65,7 +60,7 @@ async def _(bot: Bot, event: MessageEvent, state: T_State):
"在",
]:
await ai.finish(hello())
img = imgs[0] if imgs else ""
img = img[0] if img else ""
if isinstance(event, GroupMessageEvent):
nickname = await GroupInfoUser.get_group_member_nickname(
event.user_id, event.group_id
@@ -83,6 +78,8 @@ async def _(bot: Bot, event: MessageEvent, state: T_State):
f"问题:{msg} ---- 回答:{result}"
)
if result:
for t in Config.get_config("ai", "TEXT_FILTER"):
result = result.replace(t, "*")
await ai.finish(Message(result))
else:
await ai.finish(no_result())
-1
View File
@@ -59,7 +59,6 @@ async def get_chat_result(text: str, img_url: str, user_id: int, nickname: str)
nickname += "大~人~"
rst = rst.replace("小主人", nickname).replace("小朋友", nickname)
ai_message_manager.add_result(user_id, rst)
print(ai_message_manager)
return rst
+4 -1
View File
@@ -75,7 +75,10 @@ class AiMessageManager(StaticData):
:param user_id: 用户id
:param nickname: 用户昵称
"""
if len(self._data[user_id]["message"]) < 2:
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:
+4 -1
View File
@@ -1,7 +1,6 @@
from configs.config import Config
import nonebot
nonebot.load_plugins("plugins/alapi")
Config.add_plugin_config(
"alapi",
@@ -9,3 +8,7 @@ Config.add_plugin_config(
None,
help_="在https://admin.alapi.cn/user/login登录后获取token"
)
nonebot.load_plugins("plugins/alapi")
+5 -5
View File
@@ -1,5 +1,5 @@
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 configs.path_config import IMAGE_PATH
from typing import Optional
@@ -33,14 +33,14 @@ def gen_wbtop_pic(data: dict) -> MessageSegment:
生成微博热搜图片
:param data: 微博热搜数据
"""
bk = CreateImg(700, 32 * 50 + 280, 700, 32, color="#797979")
wbtop_bk = CreateImg(700, 280, background=f"{IMAGE_PATH}/other/webtop.png")
bk = BuildImage(700, 32 * 50 + 280, 700, 32, color="#797979")
wbtop_bk = BuildImage(700, 280, background=f"{IMAGE_PATH}/other/webtop.png")
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):
title = f"{i+1}. {data['hot_word']}"
hot = data["hot_word_num"]
img = CreateImg(700, 30, font_size=20)
img = BuildImage(700, 30, font_size=20)
w, h = img.getsize(title)
img.text((10, int((30 - h) / 2)), title)
img.text((580, int((30 - h) / 2)), hot)
+9 -7
View File
@@ -9,7 +9,7 @@ from .data_source import (
get_media_id,
get_sub_status,
SubManager,
BilibiliSub
BilibiliSub,
)
from models.level_user import LevelUser
from configs.config import Config
@@ -163,11 +163,10 @@ async def _(bot: Bot, event: MessageEvent, state: T_State):
@show_sub_info.handle()
async def _(bot: Bot, event: MessageEvent, state: T_State):
id_ = (
f"{event.user_id}:{event.group_id}"
if isinstance(event, GroupMessageEvent)
else f"{event.user_id}"
)
if isinstance(event, GroupMessageEvent):
id_ = f"{event.group_id}"
else:
id_ = f"{event.user_id}"
data = await BilibiliSub.get_sub_data(id_)
live_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"
if x.sub_type == "season":
season_rst += (
f"\t番剧id:{x.sub_id}\n"
f"\t番名:{x.season_name}\n"
f"\t当前集数:{x.season_current_episode}\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
season_rst = "当前订阅的番剧:\n" + season_rst if season_rst else season_rst
if not live_rst and not up_rst and not season_rst:
live_rst = "您目前没有任何订阅..."
live_rst = (
"该群目前没有任何订阅..." if isinstance(event, GroupMessageEvent) else "您目前没有任何订阅..."
)
await show_sub_info.send(live_rst + up_rst + season_rst)
+11 -5
View File
@@ -165,7 +165,9 @@ async def get_media_id(keyword: str) -> dict:
for _ in range(3):
try:
_season_data = {}
response = await AsyncHttpx.get(bilibili_search_url, params=params, timeout=5)
response = await AsyncHttpx.get(
bilibili_search_url, params=params, timeout=5
)
if response.status_code == 200:
data = response.json()
if data.get("data"):
@@ -191,7 +193,6 @@ async def get_sub_status(id_: int, sub_type: str) -> Optional[str]:
获取订阅状态
:param id_: 订阅 id
:param sub_type: 订阅类型
:return:
"""
try:
if sub_type == "live":
@@ -201,7 +202,7 @@ async def get_sub_status(id_: int, sub_type: str) -> Optional[str]:
elif sub_type == "season":
return await _get_season_status(id_)
except ResponseCodeException:
return "获取信息失败...请检查订阅Id是否存在或稍后再试..."
return f"Id:{id_} 获取信息失败...请检查订阅Id是否存在或稍后再试..."
# except Exception as e:
# logger.error(f"获取订阅状态发生预料之外的错误 id_:{id_} {type(e)}:{e}")
# return "发生了预料之外的错误..请稍后再试或联系管理员....."
@@ -254,7 +255,12 @@ async def _get_up_status(id_: int) -> Optional[str]:
if dynamic_img:
await BilibiliSub.update_sub_info(id_, dynamic_upload_time=dynamic_upload_time)
rst += f"{uname} 发布了动态!\n" f"{dynamic_img}\n"
if _user.latest_video_created != latest_video_created and video:
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
await BilibiliSub.update_sub_info(
id_, latest_video_created=latest_video_created
@@ -304,7 +310,7 @@ async def get_user_dynamic(
browser = await get_browser()
if dynamic_info.get("cards") and browser:
dynamic_upload_time = dynamic_info["cards"][0]["desc"]["timestamp"]
if local_user.dynamic_upload_time != dynamic_upload_time:
if local_user.dynamic_upload_time < dynamic_upload_time:
page = await browser.new_page()
await page.goto(
f"https://space.bilibili.com/{local_user.uid}/dynamic",
+7 -7
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 utils.http_utils import AsyncHttpx
from pathlib import Path
@@ -35,21 +35,21 @@ async def create_live_des_image(uid: int, title: str, cover: str, tags: str, des
sex = user_info["sex"]
face = user_info["face"]
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()
cover = CreateImg(470, 265, background=BytesIO(await get_pic(cover)))
cover = BuildImage(470, 265, background=BytesIO(await get_pic(cover)))
print()
def _create_live_des_image(
title: str,
cover: CreateImg,
cover: BuildImage,
tags: str,
des: str,
user_name: str,
sex: str,
sign: str,
ava: CreateImg,
ava: BuildImage,
):
"""
生成主播简介图片
@@ -66,6 +66,6 @@ def _create_live_des_image(
border = BORDER_PATH / "0.png"
border_img = None
if border.exists():
border_img = CreateImg(1772, 2657, background=border)
bk = CreateImg(1772, 2657, font_size=30)
border_img = BuildImage(1772, 2657, background=border)
bk = BuildImage(1772, 2657, font_size=30)
bk.paste(cover, (0, 100), center_type="by_width")
+4 -1
View File
@@ -23,7 +23,10 @@ async def search_song(song_name: str):
async def get_song_id(song_name: str) -> int:
""" """
r = await search_song(song_name)
return r["result"]["songs"][0]["id"]
try:
return r["result"]["songs"][0]["id"]
except KeyError:
return 0
async def get_song_info(songId: int):
+4 -4
View File
@@ -2,7 +2,7 @@ import psutil
import time
from datetime import datetime
from utils.http_utils import AsyncHttpx
from utils.image_utils import CreateImg
from utils.image_utils import BuildImage
from configs.path_config import IMAGE_PATH
from pathlib import Path
import asyncio
@@ -49,7 +49,7 @@ class Check:
async def show(self):
await self.check_all()
A = CreateImg(0, 0, font_size=24)
A = BuildImage(0, 0, font_size=24)
rst = (
f'[Time] {str(datetime.now()).split(".")[0]}\n'
f"-----System-----\n"
@@ -69,10 +69,10 @@ class Check:
if w > width:
width = w
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.text((10, 10), rst)
_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')
return bk.pic2bs4()
+14 -14
View File
@@ -110,17 +110,17 @@ async def _():
f"当前版本:{_version},最新版本:{latest_version}\n"
f"尝试自动更新...",
)
try:
code = await check_update(bot)
except Exception as e:
logger.error(f"更新真寻未知错误 {type(e)}:{e}")
await bot.send_private_msg(
user_id=int(list(bot.config.superusers)[0]),
message=f"更新真寻未知错误 {type(e)}:{e}\n",
)
else:
if code == 200:
await bot.send_private_msg(
user_id=int(list(bot.config.superusers)[0]),
message=f"更新完毕,请重启真寻....",
)
# try:
# code = await check_update(bot)
# except Exception as e:
# logger.error(f"更新真寻未知错误 {type(e)}:{e}")
# await bot.send_private_msg(
# user_id=int(list(bot.config.superusers)[0]),
# message=f"更新真寻未知错误 {type(e)}:{e}\n",
# )
# else:
# if code == 200:
# await bot.send_private_msg(
# user_id=int(list(bot.config.superusers)[0]),
# message=f"更新完毕,请重启真寻....",
# )
+38 -35
View File
@@ -1,5 +1,5 @@
from nonebot.adapters.cqhttp import Bot, Message
from utils.image_utils import CreateImg
from utils.image_utils import BuildImage
from configs.path_config import IMAGE_PATH
from utils.message_builder import image
from utils.http_utils import AsyncHttpx
@@ -85,12 +85,12 @@ async def check_update(bot: Bot) -> 'int, str':
update_info = data["body"]
width = 0
height = len(update_info.split('\n')) * 24
A = CreateImg(width, height, font_size=20)
A = BuildImage(width, height, font_size=20)
for m in update_info.split('\n'):
w, h = A.getsize(m)
if w > width:
width = w
A = CreateImg(width + 50, height, font_size=20)
A = BuildImage(width + 50, height, font_size=20)
A.text((10, 10), update_info)
A.save(f'{IMAGE_PATH}/update_info.png')
await bot.send_private_msg(
@@ -128,21 +128,32 @@ def _file_handle(latest_version: str) -> str:
shutil.rmtree(backup_dir)
tf = None
error = ''
try:
backup_dir.mkdir(exist_ok=True, parents=True)
logger.info("开始解压真寻文件压缩包....")
tf = tarfile.open(zhenxun_latest_tar_gz)
tf.extractall(temp_dir)
logger.info("解压真寻文件压缩包完成....")
zhenxun_latest_file = Path(temp_dir) / os.listdir(temp_dir)[0]
update_info_file = Path(zhenxun_latest_file) / "update_info.json"
update_info = json.load(open(update_info_file, "r", encoding="utf8"))
update_file = update_info["update_file"]
add_file = update_info["add_file"]
delete_file = update_info["delete_file"]
config_file = Path() / "configs" / "config.py"
config_path_file = Path() / "configs" / "config_path.py"
for file in delete_file + update_file:
# try:
backup_dir.mkdir(exist_ok=True, parents=True)
logger.info("开始解压真寻文件压缩包....")
tf = tarfile.open(zhenxun_latest_tar_gz)
tf.extractall(temp_dir)
logger.info("解压真寻文件压缩包完成....")
zhenxun_latest_file = Path(temp_dir) / os.listdir(temp_dir)[0]
update_info_file = Path(zhenxun_latest_file) / "update_info.json"
update_info = json.load(open(update_info_file, "r", encoding="utf8"))
update_file = update_info["update_file"]
add_file = update_info["add_file"]
delete_file = update_info["delete_file"]
config_file = Path() / "configs" / "config.py"
config_path_file = Path() / "configs" / "path_config.py"
for file in [config_file.name]:
tmp = ""
new_file = Path(zhenxun_latest_file) / "configs" / file
old_file = Path() / "configs" / file
new_lines = open(new_file, "r", encoding="utf8").readlines()
old_lines = open(old_file, "r", encoding="utf8").readlines()
for nl in new_lines:
tmp += check_old_lines(old_lines, nl)
with open(old_file, "w", encoding="utf8") as f:
f.write(tmp)
for file in delete_file + update_file:
if file != "configs":
file = Path() / file
backup_file = Path(backup_dir) / file
if file.exists():
@@ -157,23 +168,15 @@ def _file_handle(latest_version: str) -> str:
with open(backup_file, "w", encoding="utf8") as wf:
wf.write(data)
logger.info(f"已备份文件:{file}")
for file in add_file + update_file:
new_file = Path(zhenxun_latest_file) / file
old_file = Path() / file
if old_file not in [config_file, config_path_file]:
if not old_file.exists() and new_file.exists():
os.rename(new_file.absolute(), old_file.absolute())
logger.info(f"已更新文件:{file}")
else:
tmp = ""
new_lines = open(new_file, "r", encoding="utf8").readlines()
old_lines = open(old_file, "r", encoding="utf8").readlines()
for nl in new_lines:
tmp += check_old_lines(old_lines, nl)
with open(file, "w", encoding="utf8") as f:
f.write(tmp)
except Exception as e:
error = f'{type(e)}:{e}'
for file in add_file + update_file:
new_file = Path(zhenxun_latest_file) / file
old_file = Path() / file
if old_file not in [config_file, config_path_file] and file != "configs":
if not old_file.exists() and new_file.exists():
os.rename(new_file.absolute(), old_file.absolute())
logger.info(f"已更新文件:{file}")
# except Exception as e:
# error = f'{type(e)}:{e}'
if tf:
tf.close()
if temp_dir.exists():
+15 -1
View File
@@ -3,6 +3,8 @@ from nonebot.typing import T_State
from nonebot.adapters.cqhttp import Bot, MessageEvent
from utils.message_builder import image
from services.log import logger
from utils.manager import withdraw_message_manager
from configs.config import Config
__zx_plugin_name__ = "coser"
__plugin_usage__ = """
@@ -21,6 +23,13 @@ __plugin_settings__ = {
"limit_superuser": False,
"cmd": ["cos", "coser", "括丝", "COS", "Cos", "cOS", "coS"],
}
__plugin_configs__ = {
"WITHDRAW_COS_MESSAGE": {
"value": (0, 1),
"help": "自动撤回,参1:延迟撤回色图时间(秒),0 为关闭 | 参2:监控聊天类型,0(私聊) 1(群聊) 2(群聊+私聊)",
"default_value": (0, 1),
},
}
coser = on_command(
"cos", aliases={"coser", "括丝", "COS", "Cos", "cOS", "coS"}, priority=5, block=True
@@ -33,7 +42,12 @@ url = "http://iw233.cn/API/cos.php"
@coser.handle()
async def _(bot: Bot, event: MessageEvent, state: T_State):
try:
await coser.send(image(url))
msg_id = await coser.send(image(url))
withdraw_message_manager.withdraw_message(
event,
msg_id["message_id"],
Config.get_config("coser", "WITHDRAW_COS_MESSAGE"),
)
except Exception as e:
await coser.send("你cos给我看!")
logger.error(f"coser 发送了未知错误 {type(e)}:{e}")
+8 -8
View File
@@ -2,7 +2,7 @@ from nonebot import on_command
from nonebot.typing import T_State
from nonebot.adapters.cqhttp import Bot, MessageEvent, Message, GroupMessageEvent
from nonebot.permission import SUPERUSER
from utils.utils import get_message_text, is_number, get_message_imgs
from utils.utils import get_message_text, is_number, get_message_img
from utils.message_builder import image
from utils.message_builder import text as _text
from services.log import logger
@@ -63,9 +63,9 @@ async def _(bot: Bot, event: MessageEvent, state: T_State):
coffee = int(list(bot.config.superusers)[0])
text = get_message_text(event.json())
img_msg = _text("")
for img in get_message_imgs(event.json()):
for img in get_message_img(event.json()):
img_msg += image(img)
if not text or text in ["帮助"]:
if not text and not img_msg:
await dialogue.send("请发送[滴滴滴]+您要说的内容~", at_sender=True)
else:
group_id = 0
@@ -127,26 +127,26 @@ async def _(bot: Bot, event: MessageEvent, state: T_State):
id_ = msg[0]
user_id = dialogue_data[id_]["user_id"]
group_id = dialogue_data[id_]["group_id"]
text = msg[1]
text = " ".join(msg[1:])
dialogue_data.pop(id_)
else:
user_id = 0
if is_number(msg[1]):
group_id = int(msg[1])
text = msg[2]
text = " ".join(msg[2:])
else:
await reply.finish("群号错误...", at_sender=True)
else:
user_id = int(msg[0])
if is_number(msg[1]) and len(msg[1]) > 5:
group_id = int(msg[1])
text = msg[2]
text = " ".join(msg[2:])
else:
group_id = 0
text = msg[1]
text = " ".join(msg[1:])
else:
await reply.finish("第一参数,请输入数字.....", at_sender=True)
for img in get_message_imgs(event.json()):
for img in get_message_img(event.json()):
text += image(img)
if group_id:
if user_id:
+3 -3
View File
@@ -35,12 +35,12 @@ def is_expired(data: dict):
now = datetime.now().date()
except ValueError:
return False
return start_date <= now <= end_date
return not start_date <= now <= end_date
# 检查写入
def check_write(data: dict, up_char_file):
if not is_expired(data['char']):
if is_expired(data['char']):
for x in list(data.keys()):
data[x]['title'] = ''
else:
@@ -175,7 +175,7 @@ class GenshinAnnouncement:
data[itype]['time'] = data[itype]['time'][:-1]
if '版本更新后' in data[itype]['time']:
sp = data[itype]['time'].split('~')
end_time = datetime.strptime(sp[1].strip(), "%Y/%m/%d %H:%M:%S")
end_time = datetime.strptime(sp[1].strip(), "%Y/%m/%d %H:%M")
start_time = end_time - timedelta(days=20)
data[itype]['time'] = start_time.strftime('%Y/%m/%d') + ' ~ ' + end_time.strftime('%Y/%m/%d')
tmp = ''
+117 -63
View File
@@ -1,11 +1,25 @@
import os
from nonebot.adapters.cqhttp import MessageSegment, Message
import nonebot
import random
from .update_game_info import update_info
from .util import generate_img, init_star_rst, BaseData, set_list, get_star, init_up_char
from .config import GENSHIN_FIVE_P, GENSHIN_FOUR_P, GENSHIN_G_FIVE_P, GENSHIN_G_FOUR_P, GENSHIN_THREE_P, I72_ADD, \
DRAW_PATH, GENSHIN_FLAG
from .util import (
generate_img,
init_star_rst,
BaseData,
set_list,
get_star,
init_up_char,
)
from .config import (
GENSHIN_FIVE_P,
GENSHIN_FOUR_P,
GENSHIN_G_FIVE_P,
GENSHIN_G_FOUR_P,
GENSHIN_THREE_P,
I72_ADD,
DRAW_PATH,
GENSHIN_FLAG,
)
from dataclasses import dataclass
from .init_card_pool import init_game_pool
from .announcement import GenshinAnnouncement
@@ -15,7 +29,6 @@ try:
except ModuleNotFoundError:
import json
driver: nonebot.Driver = nonebot.get_driver()
announcement = GenshinAnnouncement()
@@ -29,9 +42,9 @@ ALL_ARMS = []
UP_CHAR = []
UP_ARMS = []
_CURRENT_CHAR_POOL_TITLE = ''
_CURRENT_ARMS_POOL_TITLE = ''
POOL_IMG = ''
_CURRENT_CHAR_POOL_TITLE = ""
_CURRENT_ARMS_POOL_TITLE = ""
POOL_IMG = ""
@dataclass
@@ -41,19 +54,25 @@ class GenshinChar(BaseData):
async def genshin_draw(user_id: int, count: int, pool_name: str):
# 0 1 2
cnlist = ['★★★★★', '★★★★', '★★★']
char_list, five_list, five_index_list, char_dict, star_list = _format_card_information(count, user_id, pool_name)
temp = ''
title = ''
cnlist = ["★★★★★", "★★★★", "★★★"]
(
char_list,
five_list,
five_index_list,
char_dict,
star_list,
) = _format_card_information(count, user_id, pool_name)
temp = ""
title = ""
up_type = []
up_list = []
if pool_name == 'char' and _CURRENT_CHAR_POOL_TITLE:
if pool_name == "char" and _CURRENT_CHAR_POOL_TITLE:
up_type = UP_CHAR
title = _CURRENT_CHAR_POOL_TITLE
elif pool_name == 'arms' and _CURRENT_ARMS_POOL_TITLE:
elif pool_name == "arms" and _CURRENT_ARMS_POOL_TITLE:
up_type = UP_ARMS
title = _CURRENT_ARMS_POOL_TITLE
tmp = ''
tmp = ""
if up_type:
for x in up_type:
for operator in x.operators:
@@ -63,80 +82,105 @@ async def genshin_draw(user_id: int, count: int, pool_name: str):
elif x.star == 4:
tmp += f'四星UP:{" ".join(x.operators)}'
rst = init_star_rst(star_list, cnlist, five_list, five_index_list, up_list)
pool_info = f'当前up池:{title}\n{tmp}' if title else ''
pool_info = f"当前up池:{title}\n{tmp}" if title else ""
if count > 90:
char_list = set_list(char_list)
return pool_info + '\n' + MessageSegment.image(
"base64://" + await generate_img(char_list, 'genshin', star_list)) + '\n' + rst[:-1] + \
temp[:-1] + f'\n距离保底发还剩 {90 - genshin_count[user_id] if genshin_count.get(user_id) else "^"} 抽' \
+ "\n【五星:0.6%,四星:5.1%\n第72抽开始五星概率每抽加0.585%】"
return (
pool_info
+ "\n"
+ MessageSegment.image(
"base64://" + await generate_img(char_list, "genshin", star_list)
)
+ "\n"
+ rst[:-1]
+ temp[:-1]
+ f'\n距离保底发还剩 {90 - genshin_count[user_id] if genshin_count.get(user_id) else "^"} 抽'
+ "\n【五星:0.6%,四星:5.1%\n第72抽开始五星概率每抽加0.585%】"
)
async def update_genshin_info():
global ALL_CHAR, ALL_ARMS
url = 'https://wiki.biligame.com/ys/角色筛选'
data, code = await update_info(url, 'genshin')
url = "https://wiki.biligame.com/ys/角色筛选"
data, code = await update_info(url, "genshin")
if code == 200:
ALL_CHAR = init_game_pool('genshin', data, GenshinChar)
url = 'https://wiki.biligame.com/ys/武器图鉴'
data, code = await update_info(url, 'genshin_arms', ['头像', '名称', '类型', '稀有度.alt',
'获取途径', '初始基础属性1', '初始基础属性2',
'攻击力(MAX)', '副属性(MAX)', '技能'])
ALL_CHAR = init_game_pool("genshin", data, GenshinChar)
url = "https://wiki.biligame.com/ys/武器图鉴"
data, code = await update_info(
url,
"genshin_arms",
)
if code == 200:
ALL_ARMS = init_game_pool('genshin_arms', data, GenshinChar)
ALL_ARMS = init_game_pool("genshin_arms", data, GenshinChar)
await _genshin_init_up_char()
async def init_genshin_data():
global ALL_CHAR, ALL_ARMS
if GENSHIN_FLAG:
if not os.path.exists(DRAW_PATH + 'genshin.json') or not os.path.exists(DRAW_PATH + 'genshin_arms.json'):
if not os.path.exists(DRAW_PATH + "genshin.json") or not os.path.exists(
DRAW_PATH + "genshin_arms.json"
):
await update_genshin_info()
else:
with open(DRAW_PATH + 'genshin.json', 'r', encoding='utf8') as f:
with open(DRAW_PATH + "genshin.json", "r", encoding="utf8") as f:
genshin_dict = json.load(f)
with open(DRAW_PATH + 'genshin_arms.json', 'r', encoding='utf8') as f:
with open(DRAW_PATH + "genshin_arms.json", "r", encoding="utf8") as f:
genshin_ARMS_dict = json.load(f)
ALL_CHAR = init_game_pool('genshin', genshin_dict, GenshinChar)
ALL_ARMS = init_game_pool('genshin_arms', genshin_ARMS_dict, GenshinChar)
ALL_CHAR = init_game_pool("genshin", genshin_dict, GenshinChar)
ALL_ARMS = init_game_pool("genshin_arms", genshin_ARMS_dict, GenshinChar)
await _genshin_init_up_char()
# 抽取卡池
def _get_genshin_card(mode: int = 1, pool_name: str = '', add: float = 0.0):
def _get_genshin_card(mode: int = 1, pool_name: str = "", add: float = 0.0):
global ALL_ARMS, ALL_CHAR, UP_ARMS, UP_CHAR, _CURRENT_ARMS_POOL_TITLE, _CURRENT_CHAR_POOL_TITLE
if mode == 1:
star = get_star([5, 4, 3], [GENSHIN_FIVE_P + add, GENSHIN_FOUR_P, GENSHIN_THREE_P])
star = get_star(
[5, 4, 3], [GENSHIN_FIVE_P + add, GENSHIN_FOUR_P, GENSHIN_THREE_P]
)
elif mode == 2:
star = get_star([5, 4], [GENSHIN_G_FIVE_P + add, GENSHIN_G_FOUR_P])
else:
star = 5
if pool_name == 'char':
if pool_name == "char":
data_lst = UP_CHAR
flag = _CURRENT_CHAR_POOL_TITLE
itype_all_lst = ALL_CHAR + [x for x in ALL_ARMS if x.star == star and x.star < 5]
elif pool_name == 'arms':
itype_all_lst = ALL_CHAR + [
x for x in ALL_ARMS if x.star == star and x.star < 5
]
elif pool_name == "arms":
data_lst = UP_ARMS
flag = _CURRENT_ARMS_POOL_TITLE
itype_all_lst = ALL_ARMS + [x for x in ALL_CHAR if x.star == star and x.star < 5]
itype_all_lst = ALL_ARMS + [
x for x in ALL_CHAR if x.star == star and x.star < 5
]
else:
data_lst = ''
flag = ''
itype_all_lst = ''
data_lst = ""
flag = ""
itype_all_lst = ""
all_lst = ALL_ARMS + ALL_CHAR
# 是否UP
if flag and star > 3 and pool_name:
# 获取up角色列表
up_char_lst = [x.operators for x in data_lst if x.star == star][0]
# 成功获取up角色
if random.random() < 0.5:
up_char_name = random.choice(up_char_lst)
acquire_char = [x for x in all_lst if x.name == up_char_name][0]
try:
if flag and star > 3 and pool_name:
# 获取up角色列表
up_char_lst = [x.operators for x in data_lst if x.star == star][0]
# 成功获取up角色
if random.random() < 0.5:
up_char_name = random.choice(up_char_lst)
acquire_char = [x for x in all_lst if x.name == up_char_name][0]
else:
# 无up
all_char_lst = [
x
for x in itype_all_lst
if x.star == star and x.name not in up_char_lst and not x.limited
]
acquire_char = random.choice(all_char_lst)
else:
# 无up
all_char_lst = [x for x in itype_all_lst if x.star == star and x.name not in up_char_lst and not x.limited]
acquire_char = random.choice(all_char_lst)
else:
chars = [x for x in all_lst if x.star == star and not x.limited]
acquire_char = random.choice(chars)
except IndexError:
chars = [x for x in all_lst if x.star == star and not x.limited]
acquire_char = random.choice(chars)
return acquire_char, 5 - star
@@ -148,12 +192,14 @@ def _format_card_information(_count: int, user_id, pool_name):
five_index_list = []
five_list = []
five_dict = {}
_start_add_count = 72 if pool_name == "char" else 62
_x = 90 if pool_name == "char" else 80 # 保底
add = 0.0
if genshin_count.get(user_id) and _count <= 90:
if genshin_count.get(user_id) and _count <= _x:
f_count = genshin_count[user_id]
else:
f_count = 0
if genshin_pl_count.get(user_id) and _count <= 90:
if genshin_pl_count.get(user_id) and _count <= _x:
count = genshin_pl_count[user_id]
else:
count = 0
@@ -161,23 +207,23 @@ def _format_card_information(_count: int, user_id, pool_name):
count += 1
f_count += 1
# 十连保底
if count == 10 and f_count != 90:
if f_count >= 72:
if count == 10 and f_count != _x:
if f_count >= _start_add_count:
add += I72_ADD
char, code = _get_genshin_card(2, pool_name, add=add)
count = 0
# 大保底
elif f_count == 90:
elif f_count == _x:
char, code = _get_genshin_card(3, pool_name)
else:
if f_count >= 72:
if f_count >= _start_add_count:
add += I72_ADD
char, code = _get_genshin_card(pool_name=pool_name, add=add)
if code == 1:
count = 0
star_list[code] += 1
if code == 0:
if _count <= 90:
if _count <= _x:
genshin_five[user_id] = f_count
add = 0.0
f_count = 0
@@ -188,7 +234,7 @@ def _format_card_information(_count: int, user_id, pool_name):
except KeyError:
five_dict[char.name] = 1
char_list.append(char)
if _count <= 90:
if _count <= _x:
genshin_count[user_id] = f_count
genshin_pl_count[user_id] = count
return char_list, five_list, five_index_list, five_dict, star_list
@@ -202,9 +248,17 @@ def reset_count(user_id: int):
# 获取up和概率
async def _genshin_init_up_char():
global _CURRENT_CHAR_POOL_TITLE, _CURRENT_ARMS_POOL_TITLE, UP_CHAR, UP_ARMS, POOL_IMG
_CURRENT_CHAR_POOL_TITLE, _CURRENT_ARMS_POOL_TITLE, POOL_IMG, UP_CHAR, UP_ARMS = await init_up_char(announcement)
(
_CURRENT_CHAR_POOL_TITLE,
_CURRENT_ARMS_POOL_TITLE,
POOL_IMG,
UP_CHAR,
UP_ARMS,
) = await init_up_char(announcement)
async def reload_genshin_pool():
await _genshin_init_up_char()
return Message(f'当前UP池子:{_CURRENT_CHAR_POOL_TITLE} & {_CURRENT_ARMS_POOL_TITLE} {POOL_IMG}')
return Message(
f"当前UP池子:{_CURRENT_CHAR_POOL_TITLE} & {_CURRENT_ARMS_POOL_TITLE} {POOL_IMG}"
)
-2
View File
@@ -1,6 +1,5 @@
import os
import nonebot
from nonebot.adapters.cqhttp import MessageSegment, Message
from .update_game_info import update_info
from .announcement import GuardianAnnouncement
@@ -18,7 +17,6 @@ try:
except ModuleNotFoundError:
import json
driver: nonebot.Driver = nonebot.get_driver()
announcement = GuardianAnnouncement()
-2
View File
@@ -1,5 +1,4 @@
import nonebot
from .update_game_info import update_info
from .announcement import PrettyAnnouncement
from .util import init_star_rst, generate_img, max_card, BaseData, \
@@ -15,7 +14,6 @@ try:
except ModuleNotFoundError:
import json
driver: nonebot.Driver = nonebot.get_driver()
announcement = PrettyAnnouncement()
+17 -15
View File
@@ -1,6 +1,5 @@
from nonebot.adapters.cqhttp import MessageSegment, Message
import nonebot
import random
from .config import PRTS_FIVE_P, PRTS_FOUR_P, PRTS_SIX_P, PRTS_THREE_P, DRAW_PATH, PRTS_FLAG
from .update_game_info import update_info
@@ -15,7 +14,6 @@ try:
except ModuleNotFoundError:
import json
driver: nonebot.Driver = nonebot.get_driver()
announcement = PrtsAnnouncement()
@@ -93,22 +91,26 @@ def _get_operator_card(add: float):
zoom = z
else:
weight = z
up_operator_name = ""
# UP
if random.random() < zoom:
up_operators = [x.operators for x in UP_OPERATOR if x.star == star and x.zoom < 1][0]
up_operator_name = random.choice(up_operators)
# print(up_operator_name)
acquire_operator = [x for x in ALL_OPERATOR if x.name == up_operator_name][0]
else:
all_star_operators = [x for x in ALL_OPERATOR if x.star == star
and not any([x.limited, x.event_only, x.recruit_only])]
weight_up_operators = [x.operators for x in UP_OPERATOR if x.star == star and x.zoom > 1]
# 权重
if weight_up_operators and random.random() < 1.0 / float(len(all_star_operators)) * weight:
up_operator_name = random.choice(weight_up_operators[0])
try:
if random.random() < zoom:
up_operators = [x.operators for x in UP_OPERATOR if x.star == star and x.zoom < 1][0]
up_operator_name = random.choice(up_operators)
# print(up_operator_name)
acquire_operator = [x for x in ALL_OPERATOR if x.name == up_operator_name][0]
else:
acquire_operator = random.choice(all_star_operators)
all_star_operators = [x for x in ALL_OPERATOR if x.star == star
and not any([x.limited, x.event_only, x.recruit_only])]
weight_up_operators = [x.operators for x in UP_OPERATOR if x.star == star and x.zoom > 1]
# 权重
if weight_up_operators and random.random() < 1.0 / float(len(all_star_operators)) * weight:
up_operator_name = random.choice(weight_up_operators[0])
acquire_operator = [x for x in ALL_OPERATOR if x.name == up_operator_name][0]
else:
acquire_operator = random.choice(all_star_operators)
except IndexError:
acquire_operator = Operator(up_operator_name, star, True, False, False)
else:
acquire_operator = random.choice([x for x in ALL_OPERATOR if x.star == star
and not any([x.limited, x.event_only, x.recruit_only])])
+9 -2
View File
@@ -6,6 +6,7 @@ from urllib.parse import unquote
from services.log import logger
from .util import remove_prohibited_str
from utils.http_utils import AsyncHttpx
from httpx import ConnectTimeout, CloseError
import bs4
import re
try:
@@ -57,9 +58,12 @@ async def update_info(url: str, game_name: str, info_list: list = None) -> 'dict
data[name] = member_dict
logger.info(f'{name} is update...')
data = await _last_check(data, game_name)
except TimeoutError:
except (TimeoutError, ConnectTimeout, CloseError):
logger.warning(f'更新 {game_name} 超时...')
return {}, 999
except Exception as e:
logger.error(f"更新 {game_name} 未知错误 {type(e)}:{e}")
return {}, 998
with open(DRAW_PATH + f'{game_name}.json', 'w', encoding='utf8') as wf:
wf.write(json.dumps(data, ensure_ascii=False, indent=4))
return data, 200
@@ -175,10 +179,13 @@ async def _last_check(data: dict, game_name: str):
# 对抓取每行数据是否需要额外处理?
def intermediate_check(member_dict: dict, key: str, game_name: str, td: bs4.element.Tag):
if game_name == "genshin_arms":
if key == "稀有度":
member_dict["稀有度"] = td.find("img")["alt"].split('.')[0]
if game_name == 'prts':
if key == '获取途径':
msg = re.search('<td.*?>([\\s\\S]*)</td>', str(td)).group(1).strip()
msg = msg[:-1] if msg[-1] == '\n' else msg
msg = msg[:-1] if msg and msg[-1] == '\n' else msg
if msg.find('<a') != -1:
for a in td.find_all('a'):
msg = msg.replace(str(a), a.text)
@@ -5,6 +5,7 @@ from bs4 import BeautifulSoup
from .util import remove_prohibited_str
from utils.http_utils import AsyncHttpx
from services.log import logger
from httpx import ConnectTimeout, CloseError
import asyncio
try:
@@ -50,9 +51,12 @@ async def update_requests_info(game_name: str):
data = add_to_data(data, x, game_name)
logger.info(f"{key} is update...")
data = await _last_check(data, game_name)
except TimeoutError:
except (TimeoutError, ConnectTimeout, CloseError):
logger.warning(f"更新 {game_name} 超时...")
return {}, 999
except Exception as e:
logger.error(f"更新 {game_name} 未知错误 {type(e)}:{e}")
return {}, 998
with open(DRAW_PATH + f"{game_name}.json", "w", encoding="utf8") as wf:
json.dump(data, wf, ensure_ascii=False, indent=4)
return data, 200
@@ -150,6 +154,8 @@ async def _async_update_fgo_extra_info(url: str, key: str, _id: str, semaphore):
x = {key: {}}
x[key]["入手方式"] = obtain
return x
except TimeoutError:
except (TimeoutError, ConnectTimeout, CloseError):
logger.warning(f"访问{url}{_id} 第 {i}次 超时...已再次访问")
except Exception as e:
logger.error(f"访问{url}{_id} 第 {i}次 未知错误 {type(e)}:{e}...已再次访问")
return {}
+12 -6
View File
@@ -6,6 +6,7 @@ from .util import remove_prohibited_str
from urllib.parse import unquote
from services.log import logger
from utils.http_utils import AsyncHttpx
from httpx import ConnectTimeout, CloseError
import bs4
import asyncio
@@ -36,10 +37,13 @@ async def update_simple_info(url: str, game_name: str) -> 'dict, int':
except AttributeError:
continue
for char in contents[1:]:
data = await retrieve_char_data(char, game_name, data, index)
try:
data = await retrieve_char_data(char, game_name, data, index)
except AttributeError:
continue
index += 1
data = await _last_check(data, game_name)
except TimeoutError:
except (TimeoutError, ConnectTimeout, CloseError):
logger.warning(f'更新 {game_name} 超时...')
return {}, 999
with open(DRAW_PATH + f'{game_name}.json', 'w', encoding='utf8') as wf:
@@ -127,9 +131,9 @@ async def retrieve_char_data(char: bs4.element.Tag, game_name: str, data: dict,
'名称': remove_prohibited_str(char.find('a')['title']),
'星级': 3 - index}
if game_name == 'azur':
print(char)
char = char.find('div').find('div').find('div').find('div')
char = char.find('div').find('div').find('div')
avatar_img = char.find('a').find('img')
char = char.find('div')
try:
member_dict['名称'] = remove_prohibited_str(char.find('a')['title'])
except TypeError:
@@ -186,7 +190,9 @@ async def _async_update_azur_extra_info(key: str, semaphore):
x = {key: {'获取途径': []}}
logger.warning(f'碧蓝航线获取额外信息错误 {key}...{[]}')
return x
except TimeoutError:
logger.warning(f'访问 https://wiki.biligame.com/blhx/{key} 第 {i}次 超时...已再次访问')
except (TimeoutError, ConnectTimeout, CloseError):
logger.warning(f'访问 https://wiki.biligame.com/blhx/{key} 第 {i}次 超时/失败...已再次访问')
except Exception as e:
logger.error(f'访问 https://wiki.biligame.com/blhx/{key} 第 {i}次 未知错误 {type(e)}:{e}...已再次访问')
return {}
+10 -10
View File
@@ -6,7 +6,7 @@ from configs.path_config import IMAGE_PATH
from utils.http_utils import AsyncHttpx
import nonebot
import pypinyin
from utils.image_utils import CreateImg
from utils.image_utils import BuildImage
import platform
from services.log import logger
import random
@@ -98,37 +98,37 @@ async def generate_img(card_set: Union[Set[BaseData], List[BaseData]], game_name
num = 0
for n in star_list:
num += n
A = CreateImg(w, h)
A = BuildImage(w, h)
A.paste(card_img)
return A.pic2bs4()
def _pst(h: int, img_list: list, game_name: str, background_list: list):
card_img = CreateImg(100 * 10, h, 100, 100)
card_img = BuildImage(100 * 10, h, 100, 100)
idx = 0
for img in img_list:
try:
if game_name == 'prts':
bk = CreateImg(100, 100, color=background_list[idx])
b = CreateImg(94, 94, background=img)
bk = BuildImage(100, 100, color=background_list[idx])
b = BuildImage(94, 94, background=img)
bk.paste(b, (3, 3))
b = bk
elif game_name == 'azur' and background_list:
bk = CreateImg(100, 100, background=background_list[idx])
b = CreateImg(98, 90, background=img)
bk = BuildImage(100, 100, background=background_list[idx])
b = BuildImage(98, 90, background=img)
bk.paste(b, (1, 5))
b = bk
else:
try:
b = CreateImg(100, 100, background=img)
b = BuildImage(100, 100, background=img)
except UnidentifiedImageError as e:
logger.warning(f'无法识别图片 已删除图片,下次更新重新下载... e:{e}')
if os.path.exists(img):
os.remove(img)
b = CreateImg(100, 100, color='black')
b = BuildImage(100, 100, color='black')
except FileNotFoundError:
logger.warning(f'{img} not exists')
b = CreateImg(100, 100, color='black')
b = BuildImage(100, 100, color='black')
card_img.paste(b)
idx += 1
return card_img
+10 -3
View File
@@ -5,6 +5,7 @@ from nonebot.typing import T_State
from utils.utils import scheduler, get_bot
from .data_source import get_epic_free
from utils.manager import group_manager
from configs.config import Config
__zx_plugin_name__ = "epic免费游戏"
__plugin_usage__ = """
@@ -24,6 +25,14 @@ __plugin_settings__ = {
"cmd": ["epic"],
}
__plugin_task__ = {"epic_free_game": "epic免费游戏"}
Config.add_plugin_config(
"_task",
"DEFAULT_EPIC_FREE_GAME",
True,
help_="被动 epic免费游戏 进群默认开关状态",
default_value=True,
)
epic = on_command("epic", priority=5, block=True)
@@ -58,9 +67,7 @@ async def _():
if await group_manager.check_group_task_status(g, "epic_free_game"):
try:
msg_list, code = await get_epic_free(bot, GroupMessageEvent)
if code == 200:
if msg_list and code == 200:
await bot.send_group_forward_msg(group_id=g, messages=msg_list)
else:
await bot.send_group_msg(group_id=g, message=msg_list)
except Exception as e:
logger.error(f"GROUP {g} epic免费游戏推送错误 {type(e)}: {e}")
+2 -2
View File
@@ -1,7 +1,7 @@
from nonebot.typing import T_State
from nonebot.adapters.cqhttp import Bot, MessageEvent, GroupMessageEvent
from nonebot import on_command
from utils.utils import get_message_imgs, get_message_text
from utils.utils import get_message_img, get_message_text
from utils.message_builder import share
from services.log import logger
@@ -32,7 +32,7 @@ fake_msg = on_command("假消息", priority=5, block=True)
@fake_msg.handle()
async def _(bot: Bot, event: MessageEvent, state: T_State):
msg = get_message_text(event.json()).split(" ")
img = get_message_imgs(event.json())
img = get_message_img(event.json())
if len(msg) > 1:
if len(msg) == 2:
url = msg[0]
+17 -8
View File
@@ -4,7 +4,7 @@ from utils.image_utils import get_img_hash
import random
from utils.message_builder import image
from nonebot import on_message
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.adapters.cqhttp import Bot, GroupMessageEvent
from configs.config import Config
@@ -19,13 +19,20 @@ usage:
重复3次相同的消息时会复读
""".strip()
__plugin_des__ = "群友的本质是什么?是复读机哒!"
__plugin_type__ = ("被动相关",)
__plugin_type__ = ("其他",)
__plugin_version__ = 0.1
__plugin_author__ = "HibiKier"
__plugin_task__ = {"fudu": "复读"}
__plugin_configs__ = {
"FUDU_PROBABILITY": {"value": 0.7, "help": "复读概率", "default_value": 0.7}
}
Config.add_plugin_config(
"_task",
"DEFAULT_FUDU",
True,
help_="被动 复读 进群默认开关状态",
default_value=True,
)
class Fudu:
@@ -83,12 +90,12 @@ async def _(bot: Bot, event: GroupMessageEvent, state: T_State):
if get_message_text(event.json()):
if get_message_text(event.json()).find("@可爱的小真寻") != -1:
await fudu.finish("复制粘贴的虚空艾特?", at_sender=True)
imgs = get_message_imgs(event.json())
img = get_message_img(event.json())
msg = get_message_text(event.json())
if not imgs and not msg:
if not img and not msg:
return
if imgs:
img_hash = await get_fudu_img_hash(imgs[0], event.group_id)
if img:
img_hash = await get_fudu_img_hash(img[0], event.group_id)
else:
img_hash = ""
add_msg = msg + "|-|" + img_hash
@@ -106,15 +113,17 @@ async def _(bot: Bot, event: GroupMessageEvent, state: T_State):
if random.random() < 0.2:
await fudu.finish("打断施法!")
_fudu_list.set_repeater(event.group_id)
if imgs and msg:
if img and msg:
rst = msg + image(f"compare_{event.group_id}_img.jpg", "temp")
elif imgs:
elif img:
rst = image(f"compare_{event.group_id}_img.jpg", "temp")
elif msg:
rst = msg
else:
rst = ""
if rst:
if rst.endswith("打断施法!"):
rst = "打断" + rst
await fudu.send(rst)
+10 -1
View File
@@ -6,6 +6,7 @@ from services.log import logger
from configs.path_config import IMAGE_PATH
from .data_source import get_alc_image
from utils.manager import group_manager
from configs.config import Config
from pathlib import Path
__zx_plugin_name__ = "原神老黄历"
@@ -28,6 +29,14 @@ __plugin_settings__ = {
}
__plugin_task__ = {"genshin_alc": "原神黄历提醒"}
Config.add_plugin_config(
"_task",
"DEFAULT_GENSHIN_ALC",
True,
help_="被动 原神黄历提醒 进群默认开关状态",
default_value=True,
)
almanac = on_command("原神黄历", priority=5, block=True)
@@ -65,4 +74,4 @@ async def _():
mes = alc_img + "\n ※ 黄历数据来源于 genshin.pub"
for gid in gl:
if await group_manager.check_group_task_status(gid, "genshin_alc"):
await bot.send_group_msg(group_id=int(gid), message=mes)
await bot.send_group_msg(group_id=int(gid), message="" + mes)
+13 -22
View File
@@ -2,14 +2,15 @@ from nonebot import on_command, Driver
from nonebot.typing import T_State
from nonebot.adapters.cqhttp import Bot, MessageEvent, Message, GroupMessageEvent
from utils.message_builder import image
from utils.image_utils import CreateImg
from utils.image_utils import BuildImage
from utils.browser import get_browser
from configs.path_config import IMAGE_PATH
import nonebot
from services.log import logger
from utils.utils import scheduler
from nonebot.permission import SUPERUSER
from pathlib import Path
from typing import List
from datetime import datetime, timedelta
import os
import asyncio
import time
@@ -52,9 +53,12 @@ async def _(bot: Bot, event: MessageEvent, state: T_State):
if time.strftime("%w") == "0":
await material.send("今天是周日,所有材料副本都开放了。")
return
file_name = str((datetime.now() - timedelta(hours=4)).date())
if not (Path(IMAGE_PATH) / "genshin" / "material" / f"{file_name}.png").exists():
await update_image()
await material.send(
Message(
image("daily_material.png", "genshin/material")
image(f"{file_name}.png", "genshin/material")
+ "\n※ 每日素材数据来源于 genshin.pub"
)
)
@@ -73,7 +77,6 @@ async def _(bot: Bot, event: MessageEvent, state: T_State):
await super_cmd.send(f"更新失败...")
@driver.on_startup
async def update_image():
page = None
try:
@@ -131,16 +134,17 @@ async def update_image():
height = await asyncio.get_event_loop().run_in_executor(
None, get_background_height, weapons_imgs
)
background_img = CreateImg(1200, height + 100, color="#f6f2ee")
background_img = BuildImage(1200, height + 100, color="#f6f2ee")
current_width = 50
for imgs in [char_imgs, weapons_imgs]:
current_height = 20
for img in imgs:
x = CreateImg(0, 0, background=img)
x = BuildImage(0, 0, background=img)
background_img.paste(x, (current_width, current_height))
current_height += x.size[1]
current_width += 600
background_img.save(f"{IMAGE_PATH}/genshin/material/daily_material.png")
file_name = str((datetime.now() - timedelta(hours=4)).date())
background_img.save(f"{IMAGE_PATH}/genshin/material/{file_name}.png")
await page.close()
return True
except Exception as e:
@@ -154,8 +158,8 @@ async def update_image():
def get_background_height(weapons_imgs: List[str]) -> int:
height = 0
for weapons in weapons_imgs:
height += CreateImg(0, 0, background=weapons).size[1]
last_weapon = CreateImg(0, 0, background=weapons_imgs[-1])
height += BuildImage(0, 0, background=weapons).size[1]
last_weapon = BuildImage(0, 0, background=weapons_imgs[-1])
w, h = last_weapon.size
last_weapon.crop((0, 0, w, h - 10))
last_weapon.save(weapons_imgs[-1])
@@ -163,16 +167,3 @@ def get_background_height(weapons_imgs: List[str]) -> int:
return height
@scheduler.scheduled_job(
"cron",
hour=4,
minute=1,
)
async def _():
for _ in range(5):
try:
await update_image()
logger.info(f"更新每日天赋素材成功...")
break
except Exception as e:
logger.error(f"更新每日天赋素材出错 e:{e}")
@@ -40,6 +40,9 @@ __plugin_settings__ = {
"limit_superuser": False,
"cmd": ["原神资源查询", "原神资源列表"],
}
__plugin_block_limit__ = {
"rst": "您有资源正在查询!"
}
qr = on_command("原神资源查询", aliases={"原神资源查找"}, priority=5, block=True)
qr_lst = on_command("原神资源列表", priority=5, block=True)
+5 -5
View File
@@ -1,6 +1,6 @@
from pathlib import Path
from configs.path_config import IMAGE_PATH, TEXT_PATH
from utils.image_utils import CreateImg
from utils.image_utils import BuildImage
from typing import Tuple, List
from math import sqrt, pow
import random
@@ -39,7 +39,7 @@ class Map:
:param planning_route: 是否规划最佳线路
:param ratio: 压缩比率
"""
self.map = CreateImg(0, 0, background=map_path)
self.map = BuildImage(0, 0, background=map_path)
self.resource_name = resource_name
self.center_x = center_point[0]
self.center_y = center_point[1]
@@ -225,13 +225,13 @@ class Map:
# self.map.line(xy, (255, 0, 0), width=3)
# 获取资源图标
def _get_icon_image(self, id_: int) -> "CreateImg":
def _get_icon_image(self, id_: int) -> "BuildImage":
icon = icon_path / f"{id_}.png"
if icon.exists():
return CreateImg(
return BuildImage(
int(50 * self.ratio), int(50 * self.ratio), background=icon
)
return CreateImg(
return BuildImage(
int(50 * self.ratio),
int(50 * self.ratio),
background=f"{icon_path}/box.png",
@@ -3,11 +3,12 @@ from configs.path_config import IMAGE_PATH, TEXT_PATH
from PIL.Image import UnidentifiedImageError
from utils.message_builder import image
from services.log import logger
from utils.image_utils import CreateImg
from utils.image_utils import BuildImage
from asyncio.exceptions import TimeoutError
from asyncio import Semaphore
from utils.image_utils import is_valid
from utils.http_utils import AsyncHttpx
from httpx import ConnectTimeout
from pathlib import Path
from .map import Map
import asyncio
@@ -107,7 +108,6 @@ async def init(flag: bool = False):
resource_name_list.append(x["name"])
except TimeoutError:
logger.warning('原神资源查询信息初始化超时....')
pass
# 图标及位置资源
@@ -150,9 +150,11 @@ async def download_resource_data(semaphore: Semaphore):
logger.warning(f'获取原神资源失败 msg: {data["message"]}')
else:
logger.warning(f"获取原神资源失败 code:{response.status_code}")
except TimeoutError:
except (TimeoutError, ConnectTimeout):
logger.warning("获取原神资源数据超时...已再次尝试...")
await download_resource_data(semaphore)
except Exception as e:
logger.error(f"获取原神资源数据未知错误 {type(e)}:{e}")
# 下载原神地图并拼图
@@ -164,63 +166,74 @@ async def download_map_init(
_map = map_path / "map.png"
if _map.exists() and os.path.getsize(_map) > 1024 * 1024 * 30:
_map.unlink()
response = await AsyncHttpx.get(MAP_URL)
if response.status_code == 200:
data = response.json()
if data["message"] == "OK":
data = json.loads(data["data"]["info"]["detail"])
CENTER_POINT = (data["origin"][0], data["origin"][1])
if not _map.exists():
data = data["slices"]
idx = 0
for _map_data in data[0]:
map_url = _map_data['url']
await download_image(
map_url,
f"{map_path}/{idx}.png",
semaphore,
force_flag=flag,
)
idx += 1
_w, h = CreateImg(0, 0, background=f"{map_path}/0.png", ratio=MAP_RATIO).size
w = _w * len(os.listdir(map_path))
map_file = CreateImg(w, h, _w, h, ratio=MAP_RATIO)
for i in range(idx):
map_file.paste(CreateImg(0, 0, background=f"{map_path}/{i}.png", ratio=MAP_RATIO))
map_file.save(f"{map_path}/map.png")
try:
response = await AsyncHttpx.get(MAP_URL)
if response.status_code == 200:
data = response.json()
if data["message"] == "OK":
data = json.loads(data["data"]["info"]["detail"])
CENTER_POINT = (data["origin"][0], data["origin"][1])
if not _map.exists():
data = data["slices"]
idx = 0
for _map_data in data[0]:
map_url = _map_data['url']
await download_image(
map_url,
f"{map_path}/{idx}.png",
semaphore,
force_flag=flag,
)
BuildImage(0, 0, background=f"{map_path}/{idx}.png", ratio=MAP_RATIO).save()
idx += 1
_w, h = BuildImage(0, 0, background=f"{map_path}/0.png").size
w = _w * len(os.listdir(map_path))
map_file = BuildImage(w, h, _w, h, ratio=MAP_RATIO)
for i in range(idx):
map_file.paste(BuildImage(0, 0, background=f"{map_path}/{i}.png"))
map_file.save(f"{map_path}/map.png")
else:
logger.warning(f'获取原神地图失败 msg: {data["message"]}')
else:
logger.warning(f'获取原神地图失败 msg: {data["message"]}')
else:
logger.warning(f"获取原神地图失败 code:{response.status}")
logger.warning(f"获取原神地图失败 code:{response.status_code}")
except (TimeoutError, ConnectTimeout):
logger.warning("下载原神地图数据超时....")
except Exception as e:
logger.error(f"下载原神地图数据超时 {type(e)}:{e}")
# 下载资源类型数据
async def download_resource_type():
resource_type_file.parent.mkdir(parents=True, exist_ok=True)
response = await AsyncHttpx.get(LABEL_URL)
if response.status_code == 200:
data = response.json()
if data["message"] == "OK":
data = data["data"]["tree"]
resource_data = {}
for x in data:
id_ = x["id"]
resource_data[id_] = x
with open(resource_type_file, "w", encoding="utf8") as f:
json.dump(resource_data, f, ensure_ascii=False, indent=4)
logger.info(f"更新原神资源类型成功...")
try:
response = await AsyncHttpx.get(LABEL_URL)
if response.status_code == 200:
data = response.json()
if data["message"] == "OK":
data = data["data"]["tree"]
resource_data = {}
for x in data:
id_ = x["id"]
resource_data[id_] = x
with open(resource_type_file, "w", encoding="utf8") as f:
json.dump(resource_data, f, ensure_ascii=False, indent=4)
logger.info(f"更新原神资源类型成功...")
else:
logger.warning(f'获取原神资源类型失败 msg: {data["message"]}')
else:
logger.warning(f'获取原神资源类型失败 msg: {data["message"]}')
else:
logger.warning(f"获取原神资源类型失败 code:{response.status_code}")
logger.warning(f"获取原神资源类型失败 code:{response.status_code}")
except (TimeoutError, ConnectTimeout):
logger.warning("下载原神资源类型数据超时....")
except Exception as e:
logger.error(f"载原神资源类型数据超时 {type(e)}:{e}")
# 初始化资源图标
def gen_icon(icon: str):
A = CreateImg(0, 0, background=f"{icon_path}/box.png")
B = CreateImg(0, 0, background=f"{icon_path}/box_alpha.png")
A = BuildImage(0, 0, background=f"{icon_path}/box.png")
B = BuildImage(0, 0, background=f"{icon_path}/box_alpha.png")
icon_ = icon_path / f"{icon}"
icon_img = CreateImg(115, 115, background=icon_)
icon_img = BuildImage(115, 115, background=icon_)
icon_img.circle()
B.paste(icon_img, (17, 10), True)
B.paste(A, alpha=True)
+33
View File
@@ -0,0 +1,33 @@
from configs.config import Config
import nonebot
Config.add_plugin_config(
"genshin",
"mhyVersion",
"2.11.1"
)
Config.add_plugin_config(
"genshin",
"salt",
"xV8v4Qu54lUKrEYFZkJhB8cuOh9Asafs"
)
Config.add_plugin_config(
"genshin",
"n",
"h8w582wxwgqvahcdkpvdhbh2w9casgfl"
)
Config.add_plugin_config(
"genshin",
"client_type",
"5"
)
nonebot.load_plugins("plugins/genshin/query_user")
@@ -0,0 +1,97 @@
from nonebot import on_command
from nonebot.typing import T_State
from nonebot.adapters.cqhttp import Bot, MessageEvent, GroupMessageEvent
from utils.utils import get_message_text, is_number
from ..models import Genshin
from services.log import logger
__zx_plugin_name__ = "原神绑定"
__plugin_usage__ = """
usage:
绑定原神uid等数据,cookie极为重要,请谨慎绑定
** 如果对拥有者不熟悉,并不建议添加cookie **
该项目只会对cookie用于”米游社签到“,“原神玩家查询”,“原神便笺查询”
指令:
原神绑定uid [uid]
原神绑定米游社id [mys_id]
原神绑定cookie [cookie] # 该绑定请私聊
原神解绑
示例:原神绑定uid 92342233
如果不明白怎么获取cookie请输入“原神绑定cookie”。
""".strip()
__plugin_des__ = "绑定自己的原神uid等"
__plugin_cmd__ = ["原神绑定uid [uid]", "原神绑定米游社id [mys_id]", "原神绑定cookie [cookie]", "原神解绑"]
__plugin_type__ = ("原神相关",)
__plugin_version__ = 0.1
__plugin_author__ = "HibiKier"
__plugin_settings__ = {
"level": 5,
"default_status": True,
"limit_superuser": False,
"cmd": ["原神绑定"],
}
bind = on_command(
"原神绑定uid", aliases={"原神绑定米游社id", "原神绑定cookie"}, priority=5, block=True
)
unbind = on_command("原神解绑", priority=5, block=True)
@bind.handle()
async def _(bot: Bot, event: MessageEvent, state: T_State):
msg = get_message_text(event.json())
if state["_prefix"]["raw_command"] in ["原神绑定uid", "原神绑定米游社id"]:
if not is_number(msg):
await bind.finish("uid/id必须为纯数字!", at_senders=True)
msg = int(msg)
if state["_prefix"]["raw_command"] == "原神绑定uid":
uid = await Genshin.get_user_uid(event.user_id)
if uid:
await bind.finish(f"您已绑定过uid:{uid},如果希望更换uid,请先发送原神解绑")
flag = await Genshin.add_uid(event.user_id, msg)
if not flag:
await bind.finish("添加失败,该uid可能已存在...")
_x = f"已成功添加原神uid:{msg}"
elif state["_prefix"]["raw_command"] == "原神绑定米游社id":
uid = await Genshin.get_user_uid(event.user_id)
if not uid:
await bind.finish("请先绑定原神uid..")
await Genshin.set_mys_id(uid, msg)
_x = f"已成功为uid:{uid} 设置米游社id:{msg}"
else:
if not msg:
await bind.finish(
"私聊发送!!\n打开https://bbs.mihoyo.com/ys/\n登录后按F12点击控制台输入document.cookie复制输出的内容即可"
)
if isinstance(event, GroupMessageEvent):
await bind.finish("请立即撤回你的消息并私聊发送!")
uid = await Genshin.get_user_uid(event.user_id)
if not uid:
await bind.finish("请先绑定原神uid..")
if msg.startswith('"') or msg.startswith("'"):
msg = msg[1:]
if msg.endswith('"') or msg.endswith("'"):
msg = msg[:-1]
await Genshin.set_cookie(uid, msg)
_x = f"已成功为uid:{uid} 设置cookie"
await bind.send(_x)
logger.info(
f"(USER {event.user_id}, "
f"GROUP {event.group_id if isinstance(event, GroupMessageEvent) else 'private'})"
f" {state['_prefix']['raw_command']}:{msg}"
)
@unbind.handle()
async def _(bot: Bot, event: MessageEvent, state: T_State):
if await Genshin.delete_user(event.user_id):
await unbind.send("用户数据删除成功...")
logger.info(
f"(USER {event.user_id}, GROUP "
f"{event.group_id if isinstance(event, GroupMessageEvent) else 'private'})"
f"原神解绑"
)
else:
await unbind.send("该用户数据不存在..")
@@ -0,0 +1,101 @@
from .data_source import get_sign_reward_list, genshin_sign
from nonebot.adapters.cqhttp import Bot, MessageEvent, GroupMessageEvent
from nonebot import on_command
from nonebot.typing import T_State
from services.log import logger
from .init_task import add_job, scheduler, _sign
from apscheduler.jobstores.base import JobLookupError
from ..models import Genshin
__zx_plugin_name__ = "原神自动签到"
__plugin_usage__ = """
usage:
米游社原神签到,需要uid以及cookie
且在第二天自动排序签到时间
# 不听,就要手动签到!(使用命令 “原神我硬签 or 米游社我硬签”
指令:
开/关原神自动签到
原神我硬签
""".strip()
__plugin_des__ = "原神懒人签到"
__plugin_cmd__ = ["开/关原神自动签到", "原神我硬签"]
__plugin_type__ = ("原神相关",)
__plugin_version__ = 0.1
__plugin_author__ = "HibiKier"
__plugin_settings__ = {
"level": 5,
"default_status": True,
"limit_superuser": False,
"cmd": ["原神签到"],
}
genshin_matcher = on_command(
"开原神自动签到", aliases={"关原神自动签到", "原神我硬签"}, priority=5, block=True
)
@genshin_matcher.handle()
async def _(bot: Bot, event: MessageEvent, state: T_State):
uid = await Genshin.get_user_uid(event.user_id)
if not uid or not await Genshin.get_user_cookie(uid, True):
await genshin_matcher.finish("请先绑定uid和cookie!")
if "account_id" not in await Genshin.get_user_cookie(uid, True):
await genshin_matcher.finish("请更新cookie!")
if state["_prefix"]["raw_command"] == "原神我硬签":
try:
msg = await genshin_sign(uid)
logger.info(
f"(USER {event.user_id}, "
f"GROUP {event.group_id if isinstance(event, GroupMessageEvent) else 'private'}) UID:{uid} 原神签到"
)
# 硬签,移除定时任务
try:
for i in range(3):
scheduler.remove_job(f"genshin_auto_sign_{uid}_{event.user_id}_{i}",)
except JobLookupError:
pass
u = await Genshin.get_user_by_uid(uid)
if u and u.auto_sign:
await u.clear_sign_time(uid)
next_date = await Genshin.random_sign_time(uid)
add_job(event.user_id, uid, next_date)
msg += f"因开启自动签到\n下一次签到时间为:{next_date.replace(microsecond=0)}"
except Exception as e:
msg = "原神签到失败..请尝试检查cookie或报告至管理员!"
logger.info(
f"(USER {event.user_id}, "
f"GROUP {event.group_id if isinstance(event, GroupMessageEvent) else 'private'}) UID:{uid} 原神签到发生错误 "
f"{type(e)}:{e}"
)
msg = msg or "请检查cookie是否更新!"
await genshin_matcher.send(msg, at_sender=True)
else:
for i in range(3):
try:
scheduler.remove_job(f"genshin_auto_sign_{uid}_{event.user_id}_{i}")
except JobLookupError:
pass
if state["_prefix"]["raw_command"][0] == "开":
await Genshin.set_auto_sign(uid, True)
next_date = await Genshin.random_sign_time(uid)
add_job(event.user_id, uid, next_date)
await genshin_matcher.send(
f"已开启原神自动签到!\n下一次签到时间为:{next_date.replace(microsecond=0)}",
at_sender=True,
)
logger.info(
f"(USER {event.user_id}, GROUP "
f"{event.group_id if isinstance(event, GroupMessageEvent) else 'private'})"
f" 开启原神自动签到"
)
else:
await Genshin.set_auto_sign(uid, False)
await Genshin.clear_sign_time(uid)
await genshin_matcher.send(f"已关闭原神自动签到!", at_sender=True)
logger.info(
f"(USER {event.user_id}, GROUP "
f"{event.group_id if isinstance(event, GroupMessageEvent) else 'private'})"
f" 关闭原神自动签到"
)
@@ -0,0 +1,104 @@
from utils.http_utils import AsyncHttpx
from configs.config import Config
from services.log import logger
from ..utils import random_hex, get_old_ds
from ..models import Genshin
from typing import Optional, Dict
async def genshin_sign(uid: int) -> Optional[str]:
"""
原神签到信息
:param uid: uid
"""
data = await _sign(uid)
if not data:
return "签到失败..."
status = data["message"]
if status == "OK":
sign_info = await _get_sign_info(uid)
if sign_info:
sign_info = sign_info["data"]
sign_list = await get_sign_reward_list()
get_reward = sign_list["data"]["awards"][
int(sign_info["total_sign_day"]) - 1
]["name"]
reward_num = sign_list["data"]["awards"][
int(sign_info["total_sign_day"]) - 1
]["cnt"]
get_im = f"本次签到获得:{get_reward}x{reward_num}"
if status == "OK" and sign_info["is_sign"]:
return f"\n原神签到成功!\n{get_im}\n本月漏签次数:{sign_info['sign_cnt_missed']}"
else:
return status
return None
async def _sign(uid: int, server_id: str = "cn_gf01") -> Optional[Dict[str, str]]:
"""
米游社签到
:param uid: uid
:param server_id: 服务器id
"""
if str(uid)[0] == "5":
server_id = "cn_qd01"
try:
req = await AsyncHttpx.post(
url="https://api-takumi.mihoyo.com/event/bbs_sign_reward/sign",
headers={
"User_Agent": "Mozilla/5.0 (Linux; Android 10; MIX 2 Build/QKQ1.190825.002; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/83.0.4103.101 Mobile Safari/537.36 miHoYoBBS/2.3.0",
"Cookie": await Genshin.get_user_cookie(int(uid), True),
"x-rpc-device_id": random_hex(32),
"Origin": "https://webstatic.mihoyo.com",
"X_Requested_With": "com.mihoyo.hyperion",
"DS": get_old_ds(),
"x-rpc-client_type": "5",
"Referer": "https://webstatic.mihoyo.com/bbs/event/signin-ys/index.html?bbs_auth_required=true&act_id=e202009291139501&utm_source=bbs&utm_medium=mys&utm_campaign=icon",
"x-rpc-app_version": "2.3.0",
},
json={"act_id": "e202009291139501", "uid": uid, "region": server_id},
)
return req.json()
except Exception as e:
logger.error(f"米游社签到发生错误 UID:{uid} {type(e)}:{e}")
return None
async def get_sign_reward_list():
"""
获取签到奖励列表
"""
try:
req = await AsyncHttpx.get(
url="https://api-takumi.mihoyo.com/event/bbs_sign_reward/home?act_id=e202009291139501",
headers={
"x-rpc-app_version": str(Config.get_config("genshin", "mhyVersion")),
"User-Agent": "Mozilla/5.0 (iPhone; CPU iPhone OS 13_2_3 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) miHoYoBBS/2.11.1",
"x-rpc-client_type": str(Config.get_config("genshin", "client_type")),
"Referer": "https://webstatic.mihoyo.com/",
},
)
return req.json()
except Exception as e:
logger.error(f"获取签到奖励列表发生错误 {type(e)}:{e}")
return None
async def _get_sign_info(uid: int, server_id: str = "cn_gf01"):
if str(uid)[0] == "5":
server_id = "cn_qd01"
try:
req = await AsyncHttpx.get(
url=f"https://api-takumi.mihoyo.com/event/bbs_sign_reward/info?act_id=e202009291139501&region={server_id}&uid={uid}",
headers={
"x-rpc-app_version": str(Config.get_config("genshin", "mhyVersion")),
"Cookie": await Genshin.get_user_cookie(int(uid), True),
"User-Agent": "Mozilla/5.0 (iPhone; CPU iPhone OS 13_2_3 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) miHoYoBBS/2.11.1",
"x-rpc-client_type": str(Config.get_config("genshin", "client_type")),
"Referer": "https://webstatic.mihoyo.com/",
},
)
return req.json()
except Exception as e:
logger.error(f"获取签到信息发生错误 UID:{uid} {type(e)}:{e}")
return None
@@ -0,0 +1,110 @@
from .data_source import genshin_sign
from models.group_member_info import GroupInfoUser
from utils.message_builder import at
from services.log import logger
from utils.utils import scheduler, get_bot
from apscheduler.jobstores.base import ConflictingIdError
from ..models import Genshin
from datetime import datetime, timedelta
from nonebot import Driver
import nonebot
import random
import pytz
driver: Driver = nonebot.get_driver()
@driver.on_startup
async def _():
"""
启动时分配定时任务
"""
g_list = await Genshin.get_all_auto_sign_user()
for u in g_list:
if u.auto_sign_time:
date = await Genshin.random_sign_time(u.uid)
scheduler.add_job(
_sign,
"date",
run_date=date.replace(microsecond=0),
id=f"genshin_auto_sign_{u.uid}_{u.user_qq}_0",
args=[u.user_qq, u.uid, 0],
)
logger.info(
f"genshin_sign add_job:USER:{u.user_qq} UID:{u.uid} " f"{date} 原神自动签到"
)
def add_job(user_id: int, uid: int, date: datetime):
try:
scheduler.add_job(
_sign,
"date",
run_date=date.replace(microsecond=0),
id=f"genshin_auto_sign_{uid}_{user_id}_0",
args=[user_id, uid, 0],
)
logger.debug(f"genshin_sign add_job:{date.replace(microsecond=0)} 原神自动签到")
except ConflictingIdError:
pass
async def _sign(user_id: int, uid: int, count: int):
"""
执行签到任务
:param user_id: 用户id
:param uid: uid
:param count: 执行次数
"""
if count < 3:
try:
msg = await genshin_sign(uid)
next_time = await Genshin.random_sign_time(uid)
msg += f"\n下一次签到时间为:{next_time.replace(microsecond=0)}"
logger.info(f"USER:{user_id} UID:{uid} 原神自动签到任务发生成功...")
try:
scheduler.add_job(
_sign,
"date",
run_date=next_time.replace(microsecond=0),
id=f"genshin_auto_sign_{uid}_{user_id}_0",
args=[user_id, uid, 0],
)
except ConflictingIdError:
msg += "\n定时任务设定失败..."
except Exception as e:
logger.error(f"USER:{user_id} UID:{uid} 原神自动签到任务发生错误 {type(e)}:{e}")
msg = None
if not msg:
now = datetime.now(pytz.timezone("Asia/Shanghai"))
if now.hour < 23:
random_hours = random.randint(1, 23 - now.hour)
next_time = now + timedelta(hours=random_hours)
scheduler.add_job(
_sign,
"date",
run_date=next_time.replace(microsecond=0),
id=f"genshin_auto_sign_{uid}_{user_id}_{count}",
args=[user_id, uid, count + 1],
)
msg = (
f"{now.replace(microsecond=0)} 原神"
f"签到失败,将在 {next_time.replace(microsecond=0)} 时重试!"
)
else:
msg = "今日原神签到失败,请手动签到..."
logger.debug(f"USER:{user_id} UID:{uid} 原神今日签到失败...")
else:
msg = "今日原神自动签到重试次数已达到3次,请手动签到。"
logger.debug(f"USER:{user_id} UID:{uid} 原神今日签到失败次数打到 3 次...")
bot = get_bot()
if bot:
if user_id in [x["user_id"] for x in await bot.get_friend_list()]:
await bot.send_private_msg(user_id=user_id, message=msg)
else:
group_list = await GroupInfoUser.get_user_all_group(user_id)
if group_list:
await bot.send_group_msg(
group_id=group_list[0], message=at(user_id) + msg
)
@@ -0,0 +1,359 @@
from services.db_context import db
from typing import Optional, Union, List
from datetime import datetime, timedelta
import random
import pytz
class Genshin(db.Model):
__tablename__ = "genshin"
id = db.Column(db.Integer(), primary_key=True)
user_qq = db.Column(db.BigInteger(), nullable=False)
uid = db.Column(db.BigInteger())
mys_id = db.Column(db.BigInteger())
cookie = db.Column(db.String(), default="")
today_query_uid = db.Column(db.String(), default="") # 该cookie今日查询的uid
auto_sign = db.Column(db.Boolean(), default=False)
auto_sign_time = db.Column(db.DateTime(timezone=True))
resin_remind = db.Column(db.Boolean(), default=False) # 树脂提醒
resin_recovery_time = db.Column(db.DateTime(timezone=True)) # 满树脂提醒日期
_idx1 = db.Index("genshin_uid_idx1", "user_qq", "uid", unique=True)
@classmethod
async def add_uid(cls, user_qq: int, uid: int):
"""
说明:
添加一个uid
参数:
:param user_qq: 用户qq
:param uid: 原神uid
"""
query = cls.query.where((cls.user_qq == user_qq) & (cls.uid == uid))
user = await query.gino.first()
if not user:
await cls.create(
user_qq=user_qq,
uid=uid,
)
return True
return False
@classmethod
async def set_mys_id(cls, uid: int, mys_id: int) -> bool:
"""
说明:
设置米游社id
参数:
:param uid: 原神uid
:param mys_id: 米游社id
"""
query = cls.query.where(cls.uid == uid).with_for_update()
user = await query.gino.first()
if user:
await user.update(mys_id=mys_id).apply()
return True
return False
@classmethod
async def set_cookie(cls, uid: int, cookie: str) -> bool:
"""
说明:
设置cookie
参数:
:param uid: 原神uid
:param cookie: 米游社id
"""
query = cls.query.where(cls.uid == uid).with_for_update()
user = await query.gino.first()
if user:
await user.update(cookie=cookie).apply()
return True
return False
@classmethod
async def set_resin_remind(cls, uid: int, flag: bool) -> bool:
"""
说明:
设置体力提醒
参数:
:param uid: 原神uid
:param flag: 开关状态
"""
query = cls.query.where(cls.uid == uid).with_for_update()
user = await query.gino.first()
if user:
await user.update(resin_remind=flag).apply()
return True
return False
@classmethod
async def set_user_resin_recovery_time(cls, uid: int, date: datetime):
"""
说明:
设置体力完成时间
参数:
:param uid: uid
:param date: 提醒日期
"""
u = await cls.query.where(cls.uid == uid).gino.first()
if u:
await u.update(resin_recovery_time=date).apply()
@classmethod
async def get_user_resin_recovery_time(cls, uid: int) -> Optional[datetime]:
"""
说明:
获取体力完成时间
参数:
:param uid: uid
"""
u = await cls.query.where(cls.uid == uid).gino.first()
if u:
return u.resin_recovery_time.astimezone(pytz.timezone("Asia/Shanghai"))
return None
@classmethod
async def get_all_resin_remind_user(cls) -> List["Genshin"]:
"""
说明:
获取所有开启体力提醒的用户
"""
return await cls.query.where(cls.resin_remind == True).gino.all()
@classmethod
async def clear_resin_remind_time(cls, uid: int) -> bool:
"""
说明:
清空提醒日期
参数:
:param uid: uid
"""
user = await cls.query.where(cls.uid == uid).gino.first()
if user:
await user.update(resin_recovery_time=None).apply()
return True
return False
@classmethod
async def set_auto_sign(cls, uid: int, flag: bool) -> bool:
"""
说明:
设置米游社/原神自动签到
参数:
:param uid: 原神uid
:param flag: 开关状态
"""
query = cls.query.where(cls.uid == uid).with_for_update()
user = await query.gino.first()
if user:
await user.update(auto_sign=flag).apply()
return True
return False
@classmethod
async def get_all_auto_sign_user(cls) -> List["Genshin"]:
"""
说明:
获取所有开启自动签到的用户
"""
return await cls.query.where(cls.auto_sign == True).gino.all()
@classmethod
async def get_all_sign_user(cls) -> List["Genshin"]:
"""
说明:
获取 原神 所有今日签到用户
"""
return await cls.query.where(cls.auto_sign_time != None).gino.all()
@classmethod
async def clear_sign_time(cls, uid: int) -> bool:
"""
说明:
清空签到日期
参数:
:param uid: uid
"""
user = await cls.query.where(cls.uid == uid).gino.first()
if user:
await user.update(auto_sign_time=None).apply()
return True
return False
@classmethod
async def random_sign_time(cls, uid: int) -> Optional[datetime]:
"""
说明:
随机签到时间
说明:
:param uid: uid
"""
query = cls.query.where(cls.uid == uid).with_for_update()
user = await query.gino.first()
if user and user.cookie:
if user.auto_sign_time and user.auto_sign_time.astimezone(
pytz.timezone("Asia/Shanghai")
) - timedelta(seconds=2) >= datetime.now(pytz.timezone("Asia/Shanghai")):
return user.auto_sign_time.astimezone(pytz.timezone("Asia/Shanghai"))
hours = int(str(datetime.now()).split()[1].split(":")[0])
minutes = int(str(datetime.now()).split()[1].split(":")[1])
date = (
datetime.now()
+ timedelta(days=1)
- timedelta(hours=hours)
- timedelta(minutes=minutes - 1)
)
random_hours = random.randint(0, 22)
random_minutes = random.randint(1, 59)
date += timedelta(hours=random_hours) + timedelta(minutes=random_minutes)
await user.update(auto_sign_time=date).apply()
return date
return None
@classmethod
async def get_query_cookie(cls, uid: int) -> Optional[str]:
"""
说明:
获取查询角色信息cookie
参数:
:param uid: 原神uid
"""
# 查找用户今日是否已经查找过,防止重复
query = cls.query.where(cls.today_query_uid.contains(str(uid)))
x = await query.gino.first()
if x:
return x.cookie
for u in [
x for x in await cls.query.order_by(db.func.random()).gino.all() if x.cookie
]:
if not u.today_query_uid or len(u.today_query_uid[:-1].split()) < 30:
await cls._add_query_uid(uid, u.uid)
return u.cookie
return None
@classmethod
async def get_user_cookie(cls, uid: int, flag: bool = False) -> Optional[str]:
"""
说明:
获取用户cookie
参数:
:param uid:原神uid
:param flag:必须使用自己的cookie
"""
cookie = await cls._get_user_data(None, uid, "cookie")
if not cookie and not flag:
cookie = await cls.get_query_cookie(uid)
return cookie
@classmethod
async def get_user_by_qq(cls, user_qq: int) -> Optional["Genshin"]:
"""
说明:
通过qq获取用户对象
参数:
:param user_qq: qq
"""
return await cls.query.where(cls.user_qq == user_qq).gino.first()
@classmethod
async def get_user_by_uid(cls, uid: int) -> Optional["Genshin"]:
"""
说明:
通过uid获取用户对象
参数:
:param uid: qq
"""
return await cls.query.where(cls.uid == uid).gino.first()
@classmethod
async def get_user_uid(cls, user_qq: int) -> Optional[int]:
"""
说明:
获取用户uid
参数:
:param user_qq:用户qq
"""
return await cls._get_user_data(user_qq, None, "uid")
@classmethod
async def get_user_mys_id(cls, uid: int) -> Optional[int]:
"""
说嘛:
获取用户米游社id
参数:
:param uid:原神id
"""
return await cls._get_user_data(None, uid, "mys_id")
@classmethod
async def delete_user_cookie(cls, uid: int):
"""
说明:
删除用户cookie
参数:
:param uid: 原神uid
"""
query = cls.query.where(cls.uid == uid).with_for_update()
user = await query.gino.first()
if user:
await user.update(cookie="").apply()
@classmethod
async def delete_user(cls, user_qq: int):
"""
说明:
删除用户数据
参数:
:param user_qq: 用户qq
"""
query = cls.query.where(cls.user_qq == user_qq).with_for_update()
user = await query.gino.first()
if not user:
return False
await user.delete()
return True
@classmethod
async def _add_query_uid(cls, uid: int, cookie_uid: int):
"""
说明:
添加每日查询重复uid的cookie
参数:
:param uid: 原神uid
:param cookie_uid: cookie的uid
"""
query = cls.query.where(cls.uid == cookie_uid).with_for_update()
user = await query.gino.first()
await user.update(today_query_uid=cls.today_query_uid + f"{uid} ").apply()
@classmethod
async def _get_user_data(
cls, user_qq: Optional[int], uid: Optional[int], type_: str
) -> Optional[Union[int, str]]:
"""
说明:
获取用户数据
参数:
:param user_qq: 用户qq
:param uid: uid
:param type_: 数据类型
"""
if type_ == "uid":
user = await cls.query.where(cls.user_qq == user_qq).gino.first()
return user.uid if user else None
user = await cls.query.where(cls.uid == uid).gino.first()
if not user:
return None
if type_ == "mys_id":
return user.mys_id
elif type_ == "cookie":
return user.cookie
return None
@classmethod
async def reset_today_query_uid(cls):
for u in await cls.query.with_for_update().gino.all():
if u.today_query_uid:
await u.update(today_query_uid="").apply()
@@ -0,0 +1,62 @@
from nonebot import on_command
from nonebot.typing import T_State
from nonebot.adapters.cqhttp import Bot, MessageEvent, GroupMessageEvent
from services.log import logger
from .data_source import get_user_memo, get_memo
from ..models import Genshin
from nonebot.plugin import export
__zx_plugin_name__ = "原神便笺查询"
__plugin_usage__ = """
usage:
通过指定cookie和uid查询事实数据
指令:
原神便笺查询/yss
示例:原神便笺查询 92342233
""".strip()
__plugin_des__ = "不能浪费丝毫体力"
__plugin_cmd__ = ["原神便笺查询/yss"]
__plugin_type__ = ("原神相关",)
__plugin_version__ = 0.1
__plugin_author__ = "HibiKier"
__plugin_settings__ = {
"level": 5,
"default_status": True,
"limit_superuser": False,
"cmd": ["原神便笺查询"],
}
__plugin_block_limit__ = {}
export = export()
export.get_memo = get_memo
query_memo_matcher = on_command("原神便签查询", aliases={"原神便笺查询", "yss"}, priority=5, block=True)
@query_memo_matcher.handle()
async def _(bot: Bot, event: MessageEvent, state: T_State):
uid = await Genshin.get_user_uid(event.user_id)
if not uid or not await Genshin.get_user_cookie(uid, True):
await query_memo_matcher.finish("请先绑定uid和cookie!")
if isinstance(event, GroupMessageEvent):
uname = event.sender.card or event.sender.nickname
else:
uname = event.sender.nickname
data = await get_user_memo(event.user_id, uid, uname)
if data:
await query_memo_matcher.send(data)
logger.info(
f"(USER {event.user_id}, "
f"GROUP {event.group_id if isinstance(event, GroupMessageEvent) else 'private'}) "
f"使用原神便笺查询 uid:{uid}"
)
else:
await query_memo_matcher.send("未查询到数据...")
@@ -0,0 +1,237 @@
from typing import Optional, Union
from nonebot.adapters.cqhttp import MessageSegment
from configs.config import Config
from asyncio.exceptions import TimeoutError
from services.log import logger
from configs.path_config import IMAGE_PATH
from utils.image_utils import BuildImage
from utils.http_utils import AsyncHttpx
from utils.utils import get_user_avatar
from utils.message_builder import image
from ..utils import get_ds
from ..models import Genshin
from io import BytesIO
from pathlib import Path
from nonebot import Driver
import asyncio
import nonebot
driver: Driver = nonebot.get_driver()
memo_path = Path(IMAGE_PATH) / "genshin" / "genshin_memo"
memo_path.mkdir(exist_ok=True, parents=True)
@driver.on_startup
async def _():
for name, url in zip(
["resin.png", "task.png", "resin_discount.png"],
[
"https://upload-bbs.mihoyo.com/upload/2021/09/29/8819732/54266243c7d15ba31690c8f5d63cc3c6_71491376413333325"
"20.png?x-oss-process=image//resize,s_600/quality,q_80/auto-orient,0/interlace,1/format,png",
"https://patchwiki.biligame.com/images/ys/thumb/c/cc/6k6kuj1kte6m1n7hexqfrn92z6h4yhh.png/60px-委托任务logo.png",
"https://patchwiki.biligame.com/images/ys/d/d9/t1hv6wpucbwucgkhjntmzroh90nmcdv.png",
],
):
file = memo_path / name
if not file.exists():
await AsyncHttpx.download_file(url, file)
logger.info(f"已下载原神便签资源 -> {file}...")
async def get_user_memo(user_id: int, uid: int, uname: str) -> Optional[Union[str, MessageSegment]]:
uid = str(uid)
if uid[0] in ["1", "2"]:
server_id = "cn_gf01"
elif uid[0] == "5":
server_id = "cn_qd01"
else:
return None
return await parse_data_and_draw(user_id, uid, server_id, uname)
async def get_memo(uid: str, server_id: str) -> "Union[str, dict], int":
try:
req = await AsyncHttpx.get(
url=f"https://api-takumi-record.mihoyo.com/game_record/app/genshin/api/dailyNote?server={server_id}&role_id={uid}",
headers={
"DS": get_ds(f"role_id={uid}&server={server_id}"),
"x-rpc-app_version": Config.get_config("genshin", "mhyVersion"),
"User-Agent": "Mozilla/5.0 (iPhone; CPU iPhone OS 13_2_3 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) miHoYoBBS/2.11.1",
"x-rpc-client_type": Config.get_config("genshin", "client_type"),
"Referer": "https://webstatic.mihoyo.com/",
"Cookie": await Genshin.get_user_cookie(int(uid))
},
)
data = req.json()
if data["message"] == "OK":
return data["data"], 200
return data["message"], 999
except TimeoutError:
return "访问超时,请稍后再试", 997
except Exception as e:
logger.error(f"便签查询获取失败未知错误 {e}:{e}")
return "发生了一些错误,请稍后再试", 998
def create_border(
image_name: str, content: str, notice_text: str, value: str
) -> BuildImage:
border = BuildImage(500, 100, color="#E0D9D1", font="HYWenHei-85W.ttf", font_size=20)
text_bk = BuildImage(350, 96, color="#F5F1EB", font_size=23, font="HYWenHei-85W.ttf")
_x = 70 if image_name == "resin.png" else 50
_px = 10 if image_name == "resin.png" else 20
text_bk.paste(
BuildImage(_x, _x, background=memo_path / image_name),
(_px, 0),
True,
center_type="by_height",
)
text_bk.text((87, 20), content)
text_bk.paste(
BuildImage(
0,
0,
plain_text=notice_text,
font_color=(203, 189, 175),
font="HYWenHei-85W.ttf",
font_size=17,
),
(87, 50),
True,
)
font_width, _ = border.getsize(value)
border.text((350 + 76 - int(font_width / 2), 0), value, center_type="by_height")
border.paste(text_bk, (2, 0), center_type="by_height")
return border
async def parse_data_and_draw(
user_id: int, uid: str, server_id: str, uname: str
) -> Union[str, MessageSegment]:
data, code = await get_memo(uid, server_id)
if code != 200:
return data
user_avatar = BytesIO(await get_user_avatar(user_id))
for x in data["expeditions"]:
file_name = x["avatar_side_icon"].split("_")[-1]
role_avatar = memo_path / "role_avatar" / file_name
if not role_avatar.exists():
await AsyncHttpx.download_file(x["avatar_side_icon"], role_avatar)
return await asyncio.get_event_loop().run_in_executor(
None, _parse_data_and_draw, data, user_avatar, uid, uname
)
def _parse_data_and_draw(
data: dict, user_avatar: BytesIO, uid: int, uname: str
) -> Union[str, MessageSegment]:
current_resin = data["current_resin"] # 当前树脂
max_resin = data["max_resin"] # 最大树脂
resin_recovery_time = data["resin_recovery_time"] # 树脂全部回复时间
finished_task_num = data["finished_task_num"] # 完成的每日任务
total_task_num = data["total_task_num"] # 每日任务总数
remain_resin_discount_num = data["remain_resin_discount_num"] # 值得铭记的强敌总数
resin_discount_num_limit = data["resin_discount_num_limit"] # 剩余值得铭记的强敌
current_expedition_num = data["current_expedition_num"] # 当前挖矿人数
max_expedition_num = data["max_expedition_num"] # 每日挖矿最大人数
expeditions = data["expeditions"] # 挖矿详情
minute, second = divmod(int(resin_recovery_time), 60)
hour, minute = divmod(minute, 60)
A = BuildImage(1030, 520, color="#f1e9e1", font_size=15, font="HYWenHei-85W.ttf")
A.text((10, 15), "原神便笺 | Create By ZhenXun", (198, 186, 177))
ava = BuildImage(100, 100, background=user_avatar)
ava.circle()
A.paste(ava, (40, 40), True)
A.paste(
BuildImage(0, 0, plain_text=uname, font_size=20, font="HYWenHei-85W.ttf"),
(160, 62),
True,
)
A.paste(
BuildImage(
0,
0,
plain_text=f"UID:{uid}",
font_size=15,
font="HYWenHei-85W.ttf",
font_color=(21, 167, 89),
),
(160, 92),
True,
)
border = create_border(
"resin.png",
"原粹树脂",
"将在{:0>2d}:{:0>2d}:{:0>2d}秒后全部恢复".format(hour, minute, second),
f"{current_resin}/{max_resin}",
)
A.paste(border, (10, 155))
border = create_border(
"task.png",
"每日委托",
"今日委托已全部完成" if finished_task_num == total_task_num else "今日委托完成数量不足",
f"{finished_task_num}/{total_task_num}",
)
A.paste(border, (10, 265))
border = create_border(
"resin_discount.png",
"值得铭记的强敌",
"本周剩余消耗减半次数",
f"{remain_resin_discount_num}/{resin_discount_num_limit}",
)
A.paste(border, (10, 375))
expeditions_border = BuildImage(
470, 430, color="#E0D9D1", font="HYWenHei-85W.ttf", font_size=20
)
expeditions_text = BuildImage(
466, 426, color="#F5F1EB", font_size=23, font="HYWenHei-85W.ttf"
)
expeditions_text.text(
(5, 5), f"探索派遣限制{current_expedition_num}/{max_expedition_num}", (100, 100, 98)
)
h = 45
for x in expeditions:
_bk = BuildImage(400, 66, color="#ECE3D8", font="HYWenHei-85W.ttf", font_size=21)
file_name = x["avatar_side_icon"].split("_")[-1]
role_avatar = memo_path / "role_avatar" / file_name
_ava_img = BuildImage(75, 75, background=role_avatar)
_ava_img.circle()
if x["status"] == "Finished":
msg = "探索完成"
font_color = (146, 188, 63)
_circle_color = (146, 188, 63)
else:
minute, second = divmod(int(x["remained_time"]), 60)
hour, minute = divmod(minute, 60)
font_color = (193, 180, 167)
msg = "还剩{:0>2d}小时{:0>2d}分钟{:0>2d}秒".format(hour, minute, second)
_circle_color = "#DE9C58"
_circle_bk = BuildImage(60, 60)
_circle_bk.circle()
a_circle = BuildImage(55, 55, color=_circle_color)
a_circle.circle()
b_circle = BuildImage(47, 47)
b_circle.circle()
a_circle.paste(b_circle, (4, 4), alpha=True)
_circle_bk.paste(a_circle, (4, 4), alpha=True)
_bk.paste(_circle_bk, (25, 0), True, center_type="by_height")
_bk.paste(_ava_img, (19, -13), True)
_bk.text((100, 0), msg, font_color, "by_height")
_bk.circle_corner(20)
expeditions_text.paste(_bk, (25, h), True)
h += 75
expeditions_border.paste(expeditions_text, center_type="center")
A.paste(expeditions_border, (550, 45))
return image(b64=A.pic2bs4())
@@ -0,0 +1,62 @@
from nonebot import on_command
from nonebot.typing import T_State
from nonebot.adapters.cqhttp import Bot, MessageEvent, GroupMessageEvent
from services.log import logger
from .data_source import query_role_data
from ..models import Genshin
from utils.utils import get_message_text, is_number
__zx_plugin_name__ = "原神玩家查询"
__plugin_usage__ = """
usage:
通过uid查询原神玩家信息
指令:
原神玩家查询/ys ?[uid]
示例:原神玩家查询 92342233
""".strip()
__plugin_des__ = "请问你们有几个肝?"
__plugin_cmd__ = ["原神玩家查询/ys"]
__plugin_type__ = ("原神相关",)
__plugin_version__ = 0.1
__plugin_author__ = "HibiKier"
__plugin_settings__ = {
"level": 5,
"default_status": True,
"limit_superuser": False,
"cmd": ["原神玩家查询"],
}
__plugin_block_limit__ = {}
query_role_info_matcher = on_command("原神玩家查询", aliases={"原神玩家查找", "ys"}, priority=5, block=True)
@query_role_info_matcher.handle()
async def _(bot: Bot, event: MessageEvent, state: T_State):
msg = get_message_text(event.json())
if msg:
if not is_number(msg):
await query_role_info_matcher.finish("查询uid必须为数字!")
msg = int(msg)
if not msg:
uid = await Genshin.get_user_uid(event.user_id)
else:
uid = msg
if not uid: # or not await Genshin.get_user_cookie(uid):
await query_role_info_matcher.finish("请先绑定uid和cookie!")
nickname = event.sender.card if event.sender.card else event.sender.nickname
mys_id = await Genshin.get_user_mys_id(uid)
data = await query_role_data(event.user_id, uid, mys_id, nickname)
if data:
await query_role_info_matcher.send(data)
logger.info(
f"(USER {event.user_id}, "
f"GROUP {event.group_id if isinstance(event, GroupMessageEvent) else 'private'}) 使用原神玩家查询 uid:{uid}"
)
else:
await query_role_info_matcher.send("查询失败..")
@@ -0,0 +1,250 @@
from typing import Optional, List, Dict, Union
from .draw_image import init_image, get_genshin_image
from nonebot.adapters.cqhttp import MessageSegment
from ..utils import get_ds, element_mastery
from services.log import logger
from utils.http_utils import AsyncHttpx
from configs.config import Config
from ..models import Genshin
try:
import ujson as json
except ModuleNotFoundError:
import json
async def query_role_data(
user_id: int, uid: int, mys_id: Optional[str] = None, nickname: Optional[str] = None
) -> Optional[Union[MessageSegment, str]]:
uid = str(uid)
if uid[0] == "1" or uid[0] == "2":
server_id = "cn_gf01"
elif uid[0] == "5":
server_id = "cn_qd01"
else:
return None
return await get_image(user_id, uid, server_id, mys_id, nickname)
async def get_image(
user_id: int,
uid: str,
server_id: str,
mys_id: Optional[str] = None,
nickname: Optional[str] = None,
) -> Optional[Union[MessageSegment, str]]:
"""
生成图片
:param user_id:用户qq
:param uid: 用户uid
:param server_id: 服务器
:param mys_id: 米游社id
:param nickname: QQ昵称
:return:
"""
data, code = await get_info(uid, server_id)
if code != 200:
return data
if data:
char_data_list, role_data, world_data_dict, home_data_list = parsed_data(data)
mys_data = await get_mys_data(uid, mys_id)
if mys_data:
nickname = None
if char_data_list:
char_detailed_data = await get_character(
uid, [x["id"] for x in char_data_list], server_id
)
_x = {}
if char_detailed_data:
for char in char_detailed_data["avatars"]:
_x[char["name"]] = {
"weapon": char["weapon"]["name"],
"weapon_image": char["weapon"]["icon"],
"level": char["weapon"]["level"],
"affix_level": char["weapon"]["affix_level"],
}
await init_image(world_data_dict, char_data_list, _x, home_data_list)
return await get_genshin_image(
user_id,
uid,
char_data_list,
role_data,
world_data_dict,
home_data_list,
_x,
mys_data,
nickname,
)
return "未找到用户数据..."
# Github-@lulu666lulu https://github.com/Azure99/GenshinPlayerQuery/issues/20
"""
{body:"",query:{"action_ticket": undefined, "game_biz": "hk4e_cn”}}
对应 https://api-takumi.mihoyo.com/binding/api/getUserGameRolesByCookie?game_biz=hk4e_cn //查询米哈游账号下绑定的游戏(game_biz可留空)
{body:"",query:{"uid": 12345(被查询账号米哈游uid)}}
对应 https://api-takumi.mihoyo.com/game_record/app/card/wapi/getGameRecordCard?uid=
{body:"",query:{'role_id': '查询账号的uid(游戏里的)' ,'server': '游戏服务器'}}
对应 https://api-takumi.mihoyo.com/game_record/app/genshin/api/index?server= server信息 &role_id= 游戏uid
{body:"",query:{'role_id': '查询账号的uid(游戏里的)' , 'schedule_type': 1(我这边只看到出现过1和2), 'server': 'cn_gf01'}}
对应 https://api-takumi.mihoyo.com/game_record/app/genshin/api/spiralAbyss?schedule_type=1&server= server信息 &role_id= 游戏uid
{body:"",query:{game_id: 2(目前我知道有崩坏3是1原神是2)}}
对应 https://api-takumi.mihoyo.com/game_record/app/card/wapi/getAnnouncement?game_id= 这个是公告api
b=body q=query
其中b只在post的时候有内容,q只在get的时候有内容
"""
async def get_info(uid_: str, server_id: str) -> "Optional[Union[dict, str]], int":
try:
req = await AsyncHttpx.get(
url=f"https://api-takumi-record.mihoyo.com/game_record/app/genshin/api/index?server={server_id}&role_id={uid_}",
headers={
"Accept": "application/json, text/plain, */*",
"DS": get_ds(f"role_id={uid_}&server={server_id}"),
"Origin": "https://webstatic.mihoyo.com",
"x-rpc-app_version": Config.get_config("genshin", "mhyVersion"),
"User-Agent": "Mozilla/5.0 (Linux; Android 9; Unspecified Device) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/39.0.0.0 Mobile Safari/537.36 miHoYoBBS/2.2.0",
"x-rpc-client_type": Config.get_config("genshin", "client_type"),
"Referer": "https://webstatic.mihoyo.com/app/community-game-records/index.html?v=6",
"Accept-Encoding": "gzip, deflate",
"Accept-Language": "zh-CN,en-US;q=0.8",
"X-Requested-With": "com.mihoyo.hyperion",
"Cookie": await Genshin.get_user_cookie(int(uid_))
},
)
data = req.json()
if data["message"] == "OK":
return data["data"], 200
return data["message"], 999
except Exception as e:
logger.error(f"访问失败,请重试! {type(e)}: {e}")
return None, -1
async def get_character(
uid: str, character_ids: List[str], server_id="cn_gf01"
) -> Optional[dict]:
try:
req = await AsyncHttpx.post(
url="https://api-takumi.mihoyo.com/game_record/app/genshin/api/character",
headers={
"Accept": "application/json, text/plain, */*",
"DS": get_ds(
"",
{
"character_ids": character_ids,
"role_id": uid,
"server": server_id,
},
),
"Origin": "https://webstatic.mihoyo.com",
"Cookie": await Genshin.get_user_cookie(int(uid)),
"x-rpc-app_version": Config.get_config("genshin", "mhyVersion"),
"User-Agent": "Mozilla/5.0 (iPhone; CPU iPhone OS 13_2_3 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) miHoYoBBS/2.11.1",
"x-rpc-client_type": "5",
"Referer": "https://webstatic.mihoyo.com/",
"Accept-Encoding": "gzip, deflate",
"Accept-Language": "zh-CN,en-US;q=0.8",
"X-Requested-With": "com.mihoyo.hyperion",
},
json={"character_ids": character_ids, "role_id": uid, "server": server_id},
)
data = req.json()
if data["message"] == "OK":
return data["data"]
except Exception as e:
logger.error(f"访问失败,请重试! {type(e)}: {e}")
return None
def parsed_data(
data: dict,
) -> "Optional[List[Dict[str, str]]], Dict[str, str], Optional[List[Dict[str, str]]], Optional[List[Dict[str, str]]]":
"""
解析数据
:param data: 数据
"""
char_data_list = []
for char in data["avatars"]:
_x = {
"id": char["id"],
"image": char["image"],
"name": char["name"],
"element": element_mastery[char["element"].lower()],
"fetter": char["fetter"],
"level": char["level"],
"rarity": char["rarity"],
"actived_constellation_num": char["actived_constellation_num"],
}
char_data_list.append(_x)
role_data = {
"active_day_number": data["stats"]["active_day_number"], # 活跃天数
"achievement_number": data["stats"]["achievement_number"], # 达成成就数量
"win_rate": data["stats"]["win_rate"],
"anemoculus_number": data["stats"]["anemoculus_number"], # 风神瞳已收集
"geoculus_number": data["stats"]["geoculus_number"], # 岩神瞳已收集
"avatar_number": data["stats"]["avatar_number"], # 获得角色数量
"way_point_number": data["stats"]["way_point_number"], # 传送点已解锁
"domain_number": data["stats"]["domain_number"], # 秘境解锁数量
"spiral_abyss": data["stats"]["spiral_abyss"], # 深渊当期进度
"precious_chest_number": data["stats"]["precious_chest_number"], # 珍贵宝箱
"luxurious_chest_number": data["stats"]["luxurious_chest_number"], # 华丽宝箱
"exquisite_chest_number": data["stats"]["exquisite_chest_number"], # 精致宝箱
"magic_chest_number": data["stats"]["magic_chest_number"], # 奇馈宝箱
"common_chest_number": data["stats"]["common_chest_number"], # 普通宝箱
"electroculus_number": data["stats"]["electroculus_number"], # 雷神瞳已收集
}
world_data_dict = {}
for world in data["world_explorations"]:
_x = {
"level": world["level"], # 声望等级
"exploration_percentage": world["exploration_percentage"], # 探索进度
"image": world["icon"],
"name": world["name"],
"offerings": world["offerings"],
"icon": world["icon"]
}
world_data_dict[world["name"]] = _x
home_data_list = []
for home in data["homes"]:
_x = {
"level": home["level"], # 最大信任等级
"visit_num": home["visit_num"], # 最高历史访客数
"comfort_num": home["comfort_num"], # 最高洞天仙力
"item_num": home["item_num"], # 已获得摆件数量
"name": home["name"],
"icon": home["icon"],
"comfort_level_name": home["comfort_level_name"],
"comfort_level_icon": home["comfort_level_icon"],
}
home_data_list.append(_x)
return char_data_list, role_data, world_data_dict, home_data_list
async def get_mys_data(uid: str, mys_id: Optional[str]) -> Optional[List[Dict]]:
"""
获取用户米游社数据
:param uid: 原神uid
:param mys_id: 米游社id
"""
if mys_id:
try:
req = await AsyncHttpx.get(
url=f"https://api-takumi.mihoyo.com/game_record/card/wapi/getGameRecordCard?uid={mys_id}",
headers={
"DS": get_ds(f"uid={mys_id}"),
"x-rpc-app_version": Config.get_config("genshin", "mhyVersion"),
"User-Agent": "Mozilla/5.0 (iPhone; CPU iPhone OS 13_2_3 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) miHoYoBBS/2.11.1",
"x-rpc-client_type": "5",
"Referer": "https://webstatic.mihoyo.com/",
"Cookie": await Genshin.get_user_cookie(int(uid))
},
)
data = req.json()
if data["message"] == "OK":
return data["data"]["list"]
except Exception as e:
logger.error(f"访问失败,请重试! {type(e)}: {e}")
return None
@@ -0,0 +1,547 @@
from configs.path_config import IMAGE_PATH
from pathlib import Path
from utils.image_utils import BuildImage
from typing import List, Dict, Optional
from utils.message_builder import image
from nonebot.adapters.cqhttp import MessageSegment
from utils.http_utils import AsyncHttpx
from utils.utils import get_user_avatar
from io import BytesIO
import random
import asyncio
import os
image_path = Path(IMAGE_PATH) / "genshin" / "genshin_card"
async def get_genshin_image(
user_id: int,
uid: str,
char_data_list: List[Dict],
role_data: Dict,
world_data_dict: Dict,
home_data_list: List[Dict],
char_detailed_dict: dict = None,
mys_data: Optional[List[Dict]] = None,
nickname: Optional[str] = None,
) -> MessageSegment:
"""
生成图片数据
:param user_id:用户qq
:param uid: 原神uid
:param char_data_list: 角色列表
:param role_data: 玩家数据
:param world_data_dict: 国家数据字典
:param home_data_list: 家园列表
:param char_detailed_dict: 角色武器字典
:param mys_data: 用户米游社数据
:param nickname: 用户昵称
"""
user_ava = BytesIO(await get_user_avatar(user_id))
return await asyncio.get_event_loop().run_in_executor(
None,
_get_genshin_image,
uid,
char_data_list,
role_data,
world_data_dict,
home_data_list,
char_detailed_dict,
mys_data,
nickname,
user_ava,
)
def _get_genshin_image(
uid: str,
char_data_list: List[Dict],
role_data: Dict,
world_data_dict: Dict,
home_data_list: List[Dict],
char_detailed_dict: dict = None,
mys_data: Optional[Dict] = None,
nickname: Optional[str] = None,
user_ava: Optional[BytesIO] = None,
) -> MessageSegment:
"""
生成图片数据
:param uid: 原神uid
:param char_data_list: 角色列表
:param role_data: 玩家数据
:param world_data_dict: 国家数据字典
:param home_data_list: 家园列表
:param char_detailed_dict: 角色武器字典
:param mys_data: 用户米游社数据
:param nickname: 用户昵称
:param user_ava:用户头像
"""
user_image = get_user_data_image(uid, role_data, mys_data, nickname, user_ava)
home_image = get_home_data_image(home_data_list)
country_image = get_country_data_image(world_data_dict)
char_image = get_char_data_image(char_data_list, char_detailed_dict)
top_bk = BuildImage(user_image.w, user_image.h + max([home_image.h, country_image.h]) + 100, color="#F9F6F2")
top_bk.paste(user_image, alpha=True)
top_bk.paste(home_image, (0, user_image.h + 50), alpha=True)
top_bk.paste(country_image, (home_image.w + 100, user_image.h + 50), alpha=True)
bar = BuildImage(1600, 200, font_size=50, color="#F9F6F2", font="HYWenHei-85W.ttf")
bar.text((50, 10), "角色背包", (104, 103, 101))
bar.line((50, 90, 1550, 90), (227, 219, 209), width=10)
foot = BuildImage(1700, 87, background=image_path / "head.png")
head = BuildImage(1700, 87, background=image_path / "head.png")
head.rotate(180)
middle = BuildImage(
1700, top_bk.h + bar.h + char_image.h, background=image_path / "middle.png"
)
A = BuildImage(middle.w, middle.h + foot.h + head.h)
A.paste(head, (-5, 0), True)
A.paste(middle, (0, head.h), True)
A.paste(foot, (0, head.h + middle.h), True)
A.crop((0, 0, A.w - 5, A.h))
if A.h - top_bk.h - bar.h - char_image.h > 200:
_h = A.h - top_bk.h - bar.h - char_image.h - 200
A.crop((0, 0, A.w, A.h - _h))
A.paste(foot, (0, A.h - 87))
A.paste(top_bk, (0, 100), center_type="by_width")
A.paste(bar, (50, top_bk.h + 80))
A.paste(char_image, (0, top_bk.h + bar.h + 10), center_type="by_width")
rand = random.randint(1, 10000)
A.resize(0.8)
A.save(Path(IMAGE_PATH) / "temp" / f"genshin_user_card_{rand}.png")
return image(f"genshin_user_card_{rand}.png", "temp")
def get_user_data_image(
uid: str,
role_data: Dict,
mys_data: Optional[Dict] = None,
nickname: Optional[str] = None,
user_ava: Optional[BytesIO] = None,
) -> BuildImage:
"""
画出玩家基本数据
:param uid: 原神uid
:param role_data: 玩家数据
:param mys_data: 玩家米游社数据
:param nickname: 用户昵称
:param user_ava:用户头像
"""
if mys_data:
nickname = [x["nickname"] for x in mys_data if x["game_id"] == 2][0]
region = BuildImage(1440, 450, color="#E3DBD1", font="HYWenHei-85W.ttf")
region.circle_corner(30)
uname_img = BuildImage(
0,
0,
plain_text=nickname,
font_size=40,
color=(255, 255, 255, 0),
font="HYWenHei-85W.ttf",
)
uid_img = BuildImage(
0,
0,
plain_text=f"UID: {uid}",
font_size=25,
color=(255, 255, 255, 0),
font="HYWenHei-85W.ttf",
font_color=(21, 167, 89),
)
ava_bk = BuildImage(270, 270, background=image_path / "cover.png")
# 用户头像
if user_ava:
ava_img = BuildImage(200, 200, background=user_ava)
ava_img.circle()
ava_bk.paste(ava_img, alpha=True, center_type="center")
else:
ava_img = BuildImage(
245,
245,
background=image_path
/ "chars_ava"
/ random.choice(os.listdir(image_path / "chars_ava")),
)
ava_bk.paste(ava_img, (12, 16), alpha=True)
region.paste(uname_img, (int(170 + uid_img.w / 2 - uname_img.w / 2), 305), True)
region.paste(uid_img, (170, 355), True)
region.paste(ava_bk, (int(550 / 2 - ava_bk.w / 2), 40), True)
data_img = BuildImage(
800, 400, color="#E3DBD1", font="HYWenHei-85W.ttf", font_size=40
)
_height = 0
keys = [
["活跃天数", "成就达成", "获得角色", "深境螺旋"],
["华丽宝箱", "珍贵宝箱", "精致宝箱", "普通宝箱"],
["奇馈宝箱", "风神瞳", "岩神瞳", "雷神瞳"],
]
values = [
[
role_data["active_day_number"],
role_data["achievement_number"],
role_data["avatar_number"],
role_data["spiral_abyss"],
],
[
role_data["luxurious_chest_number"],
role_data["precious_chest_number"],
role_data["exquisite_chest_number"],
role_data["common_chest_number"],
],
[
role_data["magic_chest_number"],
role_data["anemoculus_number"],
role_data["geoculus_number"],
role_data["electroculus_number"],
],
]
for key, value in zip(keys, values):
_tmp_data_img = BuildImage(
800, 200, color="#E3DBD1", font="HYWenHei-85W.ttf", font_size=40
)
_width = 10
for k, v in zip(key, value):
t_ = BuildImage(
0,
0,
plain_text=k,
color=(255, 255, 255, 0),
font_color=(138, 143, 143),
font="HYWenHei-85W.ttf",
font_size=30,
)
tmp_ = BuildImage(
t_.w, t_.h + 70, color="#E3DBD1", font="HYWenHei-85W.ttf", font_size=40
)
tmp_.text((0, 0), str(v), center_type="by_width")
tmp_.paste(t_, (0, 50), True, "by_width")
_tmp_data_img.paste(tmp_, (_width if len(key) > 3 else _width + 15, 0))
_width += 200
data_img.paste(_tmp_data_img, (0, _height))
_height += _tmp_data_img.h - 70
region.paste(data_img, (510, 50))
return region
def get_home_data_image(home_data_list: List[Dict]) -> BuildImage:
"""
画出家园数据
:param home_data_list: 家园列表
"""
h = 130 + 300 * 4
region = BuildImage(
550, h, color="#E3DBD1", font="HYWenHei-85W.ttf", font_size=40
)
try:
region.text(
(0, 30), f'尘歌壶 Lv.{home_data_list[0]["level"]}', center_type="by_width"
)
region.text(
(0, region.h - 70), f'仙力: {home_data_list[0]["comfort_num"]}', center_type="by_width"
)
except (IndexError, KeyError):
region.text((0, 30), f"尘歌壶 Lv.0", center_type="by_width")
region.text((0, region.h - 70), f"仙力: 0", center_type="by_width")
region.circle_corner(30)
height = 100
homes = os.listdir(image_path / "homes")
homes.remove("lock.png")
homes.sort()
unlock_home = [x["name"] for x in home_data_list]
for i, file in enumerate(homes):
home_img = image_path / "homes" / file
x = BuildImage(500, 250, background=home_img)
if file.split(".")[0] not in unlock_home:
black_img = BuildImage(500, 250, color="black")
lock_img = BuildImage(0, 0, background=image_path / "homes" / "lock.png")
black_img.circle_corner(50)
black_img.transparent(1)
black_img.paste(lock_img, alpha=True, center_type="center")
x.paste(black_img, alpha=True)
else:
black_img = BuildImage(
500, 150, color="black", font="HYWenHei-85W.ttf", font_size=40
)
black_img.text((55, 55), file.split(".")[0], fill=(226, 211, 146))
black_img.transparent(1)
text_img = BuildImage(
0,
0,
plain_text="洞天等级",
font="HYWenHei-85W.ttf",
font_color=(203, 200, 184),
font_size=35,
color=(255, 255, 255, 0),
)
level_img = BuildImage(
0,
0,
plain_text=f'{home_data_list[0]["comfort_level_name"]}',
font="HYWenHei-85W.ttf",
font_color=(211, 213, 207),
font_size=30,
color=(255, 255, 255, 0),
)
black_img.paste(text_img, (270, 25), True)
black_img.paste(level_img, (278, 85), True)
x.paste(black_img, alpha=True, center_type="center")
x.circle_corner(50)
region.paste(x, (0, height), True, "by_width")
height += 300
return region
def get_country_data_image(world_data_dict: Dict) -> BuildImage:
"""
画出国家探索供奉等图像
:param world_data_dict: 国家数据字典
"""
region = BuildImage(790, 267 * len(world_data_dict), color="#F9F6F2")
height = 0
for country in ["蒙德", "龙脊雪山", "璃月", "稻妻", "渊下宫"]:
x = BuildImage(790, 250, color="#3A4467")
logo = BuildImage(180, 180, background=image_path / "logo" / f"{country}.png")
tmp_bk = BuildImage(770, 230, color="#606779")
tmp_bk.circle_corner(10)
content_bk = BuildImage(
755, 215, color="#3A4467", font_size=40, font="HYWenHei-85W.ttf"
)
content_bk.paste(logo, (50, 0), True, "by_height")
if country in ["蒙德", "璃月"]:
content_bk.text((300, 40), "探索", fill=(239, 211, 114))
content_bk.text(
(450, 40),
f"{world_data_dict[country]['exploration_percentage'] / 10}%",
fill=(255, 255, 255),
)
content_bk.text((300, 120), "声望", fill=(239, 211, 114))
content_bk.text(
(450, 120),
f"Lv.{world_data_dict[country]['level']}",
fill=(255, 255, 255),
)
elif country in ["龙脊雪山"]:
content_bk.text((300, 40), "探索", fill=(239, 211, 114))
content_bk.text(
(450, 40),
f"{world_data_dict[country]['exploration_percentage'] / 10}%",
fill=(255, 255, 255),
)
content_bk.text((300, 120), "供奉", fill=(239, 211, 114))
content_bk.text(
(450, 120),
f"Lv.{world_data_dict[country]['offerings'][0]['level']}",
fill=(255, 255, 255),
)
elif country in ["稻妻"]:
content_bk.text((300, 20), "探索", fill=(239, 211, 114))
content_bk.text(
(450, 20),
f"{world_data_dict[country]['exploration_percentage'] / 10}%",
fill=(255, 255, 255),
)
content_bk.text((300, 85), "声望", fill=(239, 211, 114))
content_bk.text(
(450, 85),
f"Lv.{world_data_dict[country]['level']}",
fill=(255, 255, 255),
)
content_bk.text((300, 150), "神樱", fill=(239, 211, 114))
content_bk.text(
(450, 150),
f"Lv.{world_data_dict[country]['offerings'][0]['level']}",
fill=(255, 255, 255),
)
elif country in ["渊下宫"]:
content_bk.text((300, 0), "探索", fill=(239, 211, 114), center_type="by_height")
content_bk.text(
(450, 20),
f"{world_data_dict[country]['exploration_percentage'] / 10}%",
fill=(255, 255, 255),
center_type="by_height",
)
x.paste(tmp_bk, alpha=True, center_type="center")
x.paste(content_bk, alpha=True, center_type="center")
x.circle_corner(20)
region.paste(x, (0, height), center_type="by_width")
height += 267
return region
def get_char_data_image(
char_data_list: List[Dict], char_detailed_dict: dict
) -> "BuildImage, int":
"""
画出角色列表
:param char_data_list: 角色列表
:param char_detailed_dict: 角色武器
"""
lens = len(char_data_list) / 7 if len(char_data_list) % 7 == 0 else len(char_data_list) / 7 + 1
x = 500
_h = int(x * lens)
region = BuildImage(
1600,
_h,
color="#F9F6F2",
)
width = 120
height = 0
idx = 0
for char in char_data_list:
if width + 230 > 1550:
width = 120
height += 420
idx += 1
char_img = image_path / "chars" / f'{char["name"]}.png'
char_bk = BuildImage(
270,
500,
background=image_path / "element.png",
font="HYWenHei-85W.ttf",
font_size=35,
)
char_img = BuildImage(0, 0, background=char_img)
actived_constellation_num = BuildImage(
0,
0,
plain_text=f"命之座: {char['actived_constellation_num']}层",
font="HYWenHei-85W.ttf",
font_size=25,
color=(255, 255, 255, 0),
)
level = BuildImage(
0,
0,
plain_text=f"Lv.{char['level']}",
font="HYWenHei-85W.ttf",
font_size=30,
color=(255, 255, 255, 0),
font_color=(21, 167, 89),
)
love_log = BuildImage(
0,
0,
plain_text="♥",
font="HWZhongSong.ttf",
font_size=40,
color=(255, 255, 255, 0),
font_color=(232, 31, 168),
)
fetter = BuildImage(
0,
0,
plain_text=f'{char["fetter"]}',
font="HYWenHei-85W.ttf",
font_size=30,
color=(255, 255, 255, 0),
font_color=(232, 31, 168),
)
if char_detailed_dict.get(char["name"]):
weapon = BuildImage(
100,
100,
background=image_path
/ "weapons"
/ f'{char_detailed_dict[char["name"]]["weapon"]}.png',
)
weapon_name = BuildImage(
0,
0,
plain_text=f"{char_detailed_dict[char['name']]['weapon']}",
font="HYWenHei-85W.ttf",
font_size=25,
color=(255, 255, 255, 0),
)
weapon_affix_level = BuildImage(
0,
0,
plain_text=f"精炼: {char_detailed_dict[char['name']]['affix_level']}",
font="HYWenHei-85W.ttf",
font_size=20,
color=(255, 255, 255, 0),
)
weapon_level = BuildImage(
0,
0,
plain_text=f"Lv.{char_detailed_dict[char['name']]['level']}",
font="HYWenHei-85W.ttf",
font_size=25,
color=(255, 255, 255, 0),
font_color=(21, 167, 89),
)
char_bk.paste(weapon, (20, 380), True)
char_bk.paste(
weapon_name,
(100 + int((char_bk.w - 22 - weapon.w - weapon_name.w) / 2 - 10), 390),
True,
)
char_bk.paste(
weapon_affix_level,
(
(
100
+ int(
(char_bk.w - 10 - weapon.w - weapon_affix_level.w) / 2 - 10
),
420,
)
),
True,
)
char_bk.paste(
weapon_level,
(
(
100
+ int((char_bk.w - 10 - weapon.w - weapon_level.w) / 2 - 10),
450,
)
),
True,
)
char_bk.paste(char_img, (0, 5), alpha=True, center_type="by_width")
char_bk.text((0, 270), char["name"], center_type="by_width")
char_bk.paste(actived_constellation_num, (0, 310), True, "by_width")
char_bk.paste(level, (60, 340), True)
char_bk.paste(love_log, (155, 330), True)
char_bk.paste(fetter, (180, 340), True)
char_bk.resize(0.8)
region.paste(char_bk, (width, height), True)
width += 230
region.crop((0, 0, region.w, height + 430))
return region
async def init_image(world_data_dict: Dict[str, Dict[str, str]], char_data_list: List[Dict[str, str]], char_detailed_dict: dict, home_data_list: List[Dict]):
"""
下载头像
:param world_data_dict: 地图标志
:param char_data_list: 角色列表
:param char_detailed_dict: 角色武器
:param home_data_list: 家园列表
"""
for world in world_data_dict:
file = image_path / "logo" / f'{world_data_dict[world]["name"]}.png'
file.parent.mkdir(parents=True, exist_ok=True)
if not file.exists():
await AsyncHttpx.download_file(world_data_dict[world]["icon"], file)
for char in char_data_list:
file = image_path / "chars" / f'{char["name"]}.png'
file.parent.mkdir(parents=True, exist_ok=True)
if not file.exists():
await AsyncHttpx.download_file(char["image"], file)
for char in char_detailed_dict.keys():
file = image_path / "weapons" / f'{char_detailed_dict[char]["weapon"]}.png'
file.parent.mkdir(parents=True, exist_ok=True)
if not file.exists():
await AsyncHttpx.download_file(
char_detailed_dict[char]["weapon_image"], file
)
for home in home_data_list:
file = image_path / "homes" / f'{home["name"]}.png'
file.parent.mkdir(parents=True, exist_ok=True)
if not file.exists():
await AsyncHttpx.download_file(
home["icon"], file
)
@@ -0,0 +1,17 @@
from utils.utils import scheduler
from ..models import Genshin
from services.log import logger
@scheduler.scheduled_job(
"cron",
hour=0,
minute=1,
)
async def _():
try:
await Genshin.reset_today_query_uid()
logger.warning(f"重置原神查询记录成功..")
except Exception as e:
logger.error(f"重置原神查询记录失败. {type(e)}:{e}")
@@ -0,0 +1,83 @@
from nonebot import on_command
from nonebot.typing import T_State
from nonebot.adapters.cqhttp import Bot, MessageEvent, GroupMessageEvent
from apscheduler.jobstores.base import JobLookupError
from services.log import logger
from .init_task import scheduler, add_job
from ..models import Genshin
from datetime import datetime
import random
import asyncio
import pytz
__zx_plugin_name__ = "原神树脂提醒"
__plugin_usage__ = """
usage:
即将满树脂的提醒
指令:
开原神树脂提醒
关原神树脂提醒
""".strip()
__plugin_des__ = "时时刻刻警醒你!"
__plugin_cmd__ = ["开原神树脂提醒", "关原神树脂提醒"]
__plugin_type__ = ("原神相关",)
__plugin_version__ = 0.1
__plugin_author__ = "HibiKier"
__plugin_settings__ = {
"level": 5,
"default_status": True,
"limit_superuser": False,
"cmd": ["原神树脂提醒", "关原神树脂提醒", "开原神树脂提醒"],
}
resin_remind = on_command("开原神树脂提醒", aliases={"关原神树脂提醒"}, priority=5, block=True)
@resin_remind.handle()
async def _(bot: Bot, event: MessageEvent, state: T_State):
uid = await Genshin.get_user_uid(event.user_id)
if not uid or not await Genshin.get_user_cookie(uid, True):
await resin_remind.finish("请先绑定uid和cookie!")
try:
scheduler.remove_job(f"genshin_resin_remind_{uid}_{event.user_id}")
except JobLookupError:
pass
if state["_prefix"]["raw_command"][0] == "开":
await Genshin.set_resin_remind(uid, True)
add_job(event.user_id, uid)
logger.info(
f"(USER {event.user_id}, GROUP "
f"{event.group_id if isinstance(event, GroupMessageEvent) else 'private'})"
f" 开启原神体力提醒"
)
await resin_remind.send("开启原神树脂提醒成功!", at_sender=True)
else:
await Genshin.set_resin_remind(uid, False)
await Genshin.clear_resin_remind_time(uid)
logger.info(
f"(USER {event.user_id}, GROUP "
f"{event.group_id if isinstance(event, GroupMessageEvent) else 'private'})"
f" 关闭原神体力提醒"
)
await resin_remind.send("已关闭原神树脂提醒..", at_sender=True)
@scheduler.scheduled_job(
"interval",
minutes=30,
)
async def _():
for u in await Genshin.get_all_resin_remind_user():
if u.resin_recovery_time:
if await Genshin.get_user_resin_recovery_time(u.uid) < datetime.now(
pytz.timezone("Asia/Shanghai")
):
await Genshin.clear_resin_remind_time(u.uid)
elif (
await Genshin.get_user_resin_recovery_time(u.uid)
- datetime.now(pytz.timezone("Asia/Shanghai"))
).seconds > 360:
continue
add_job(u.user_qq, u.uid)
await asyncio.sleep(random.randint(10, 30))
@@ -0,0 +1,123 @@
from utils.utils import get_bot, scheduler
from utils.message_builder import at
from models.group_member_info import GroupInfoUser
from apscheduler.jobstores.base import ConflictingIdError
from nonebot import Driver
from ..models import Genshin
from datetime import datetime, timedelta
from services.log import logger
from nonebot.plugin import require
import time
import nonebot
import pytz
driver: Driver = nonebot.get_driver()
get_memo = require('query_memo').get_memo
class UserManager:
def __init__(self):
self._data = []
def append(self, o: str):
if o not in self._data:
self._data.append(o)
def remove(self, o: str):
if o in self._data:
self._data.remove(o)
def exists(self, o: str):
return o in self._data
user_manager = UserManager()
@driver.on_startup
async def _():
"""
启动时分配定时任务
"""
g_list = await Genshin.get_all_resin_remind_user()
for u in g_list:
if u.resin_recovery_time and await Genshin.get_user_resin_recovery_time(
u.uid
) > datetime.now(pytz.timezone("Asia/Shanghai")):
date = await Genshin.get_user_resin_recovery_time(u.uid)
scheduler.add_job(
_remind,
"date",
run_date=date.replace(microsecond=0),
id=f"genshin_resin_remind_{u.uid}_{u.user_qq}",
args=[u.user_qq, u.uid],
)
logger.info(
f"genshin_resin_remind add_job:USER:{u.user_qq} UID:{u.uid} "
f"{date} 原神树脂提醒"
)
def add_job(user_id: int, uid: int):
date = datetime.now(pytz.timezone("Asia/Shanghai")) + timedelta(seconds=30)
try:
scheduler.add_job(
_remind,
"date",
run_date=date.replace(microsecond=0),
id=f"genshin_resin_remind_{uid}_{user_id}",
args=[user_id, uid],
)
except ConflictingIdError:
pass
async def _remind(user_id: int, uid: str):
uid = str(uid)
if uid[0] in ["1", "2"]:
server_id = "cn_gf01"
elif uid[0] == "5":
server_id = "cn_qd01"
else:
return
data, code = await get_memo(uid, server_id)
if code == 200:
current_resin = data["current_resin"] # 当前树脂
max_resin = data["max_resin"] # 最大树脂
resin_recovery_time = data["resin_recovery_time"] # 树脂全部回复时间
if max_resin - current_resin > 5:
user_manager.remove(uid)
next_time = datetime.strptime(time.strftime(
"%Y-%m-%d %H:%M:%S", time.localtime(time.time() + float(resin_recovery_time))
), "%Y-%m-%d %H:%M:%S")
await Genshin.set_user_resin_recovery_time(int(uid), next_time)
scheduler.add_job(
_remind,
"date",
run_date=next_time,
id=f"genshin_resin_remind_{uid}_{user_id}",
args=[user_id, uid],
)
logger.info(f"genshin_resin_remind add_job:{next_time.replace(microsecond=0)} 原神树脂提醒")
else:
if not user_manager.exists(uid):
user_manager.append(uid)
bot = get_bot()
if bot:
if user_id in [x["user_id"] for x in await bot.get_friend_list()]:
await bot.send_private_msg(
user_id=user_id,
message=f"树脂已经 {current_resin} 个啦" f",马上就要溢出了!快快刷掉刷掉!",
)
else:
group_list = await GroupInfoUser.get_user_all_group(user_id)
if group_list:
await bot.send_group_msg(
group_id=group_list[0],
message=at(user_id) + f"树脂已经 {current_resin} 个啦"
f",马上就要溢出了!快快刷掉刷掉!",
)
@@ -0,0 +1,51 @@
from configs.config import Config
import json
import time
import random
import hashlib
import string
def _md5(text):
md5 = hashlib.md5()
md5.update(text.encode())
return md5.hexdigest()
def get_old_ds() -> str:
n = Config.get_config("genshin", "n")
i = str(int(time.time()))
r = ''.join(random.sample(string.ascii_lowercase + string.digits, 6))
c = _md5("salt=" + n + "&t=" + i + "&r=" + r)
return i + "," + r + "," + c
def get_ds(q: str = "", b: dict = None) -> str:
if b:
br = json.dumps(b)
else:
br = ""
s = Config.get_config("genshin", "salt")
t = str(int(time.time()))
r = str(random.randint(100000, 200000))
c = _md5("salt=" + s + "&t=" + t + "&r=" + r + "&b=" + br + "&q=" + q)
return t + "," + r + "," + c
def random_hex(length: int) -> str:
result = hex(random.randint(0, 16 ** length)).replace("0x", "").upper()
if len(result) < length:
result = "0" * (length - len(result)) + result
return result
element_mastery = {
"anemo": "风",
"pyro": "火",
"geo": "岩",
"electro": "雷",
"cryo": "冰",
"hydro": "水",
"dendro": "草",
"none": "无",
}
+25 -11
View File
@@ -41,8 +41,19 @@ usage:
示例:塞红包 1000
示例:塞红包 1000 10
""".strip()
__plugin_superuser_usage__ = """
usage:
节日全群红包指令
指令:
节日红包 [金额] [数量] ?[祝福语] ?[指定群]
""".strip()
__plugin_des__ = "运气项目又来了"
__plugin_cmd__ = ["塞红包 [金币数] ?[红包数=5]", "开/抢", "退回"]
__plugin_cmd__ = [
"塞红包 [金币数] ?[红包数=5]",
"开/抢",
"退回",
"节日红包 [金额] [数量] ?[祝福语] ?[指定群] [_superuser]",
]
__plugin_version__ = 0.1
__plugin_author__ = "HibiKier"
__plugin_settings__ = {
@@ -51,9 +62,7 @@ __plugin_settings__ = {
"limit_superuser": False,
"cmd": ["金币红包", "塞红包"],
}
__plugin_resources__ = {
"prts": IMAGE_PATH
}
__plugin_resources__ = {"prts": IMAGE_PATH}
gold_redbag = on_command(
"塞红包", aliases={"金币红包"}, priority=5, block=True, permission=GROUP
@@ -115,6 +124,7 @@ async def _(bot: Bot, event: GroupMessageEvent, state: T_State):
redbag_data[event.group_id]["amount"]
- redbag_data[event.group_id]["open_amount"]
)
await return_gold(event.user_id, event.group_id, amount)
await gold_redbag.send(
f'{redbag_data[event.group_id]["nickname"]}的红包过时未开完,退还{amount}金币...'
)
@@ -137,15 +147,17 @@ async def _(bot: Bot, event: GroupMessageEvent, state: T_State):
else:
amount = msg[0]
num = msg[1]
if not is_number(num) or int(num) < 1:
await gold_redbag.finish("红包个数给我输正确啊!", at_sender=True)
flag, amount = await check_gold(event.user_id, event.group_id, amount)
if not flag:
await gold_redbag.finish(amount, at_sender=True)
if not is_number(num) or int(num) < 1:
await gold_redbag.finish("红包个数给我输正确啊!", at_sender=True)
group_member_num = (await bot.get_group_info(group_id=event.group_id))['member_count']
group_member_num = (await bot.get_group_info(group_id=event.group_id))[
"member_count"
]
num = int(num)
if num > group_member_num:
await gold_redbag.send('你发的红包数量也太多了,已经为你修改成与本群人数相同的红包数量...')
await gold_redbag.send("你发的红包数量也太多了,已经为你修改成与本群人数相同的红包数量...")
num = group_member_num
nickname = event.sender.card if event.sender.card else event.sender.nickname
flag, result = init_redbag(
@@ -180,7 +192,7 @@ async def _(bot: Bot, event: GroupMessageEvent, state: T_State):
.replace("。", "")
)
if msg:
if '红包' not in msg:
if "红包" not in msg:
return
flag1 = True
flag2 = True
@@ -305,11 +317,13 @@ async def _(bot: Bot, event: MessageEvent, state: T_State):
await end_festive_redbag(bot, g)
except JobLookupError:
pass
init_redbag(int(bot.self_id), g, f"{NICKNAME}", amount, num, int(bot.self_id), 1)
init_redbag(
int(bot.self_id), g, f"{NICKNAME}", amount, num, int(bot.self_id), 1
)
scheduler.add_job(
end_festive_redbag,
"date",
run_date=str(datetime.now() + timedelta(hours=24)).split(".")[0],
run_date=(datetime.now() + timedelta(hours=24)).replace(microsecond=0),
id=f"festive_redbag_{g}",
args=[bot, g],
)
+10 -11
View File
@@ -1,7 +1,6 @@
from models.bag_user import BagUser
from utils.utils import is_number, get_local_proxy, get_user_avatar
from utils.image_utils import CreateImg
from utils.user_agent import get_user_agent
from utils.utils import is_number, get_user_avatar
from utils.image_utils import BuildImage
from configs.path_config import IMAGE_PATH
from .model import RedbagUser
import random
@@ -45,8 +44,8 @@ async def open_redbag(user_id: int, group_id: int, redbag_data: dict):
# 随机红包图片
async def generate_send_redbag_pic(user_id: int, msg: str = '恭喜发财 大吉大利'):
random_redbag = random.choice(os.listdir(f"{IMAGE_PATH}/prts/redbag_2"))
redbag = CreateImg(0, 0, font_size=38, background=f'{IMAGE_PATH}/prts/redbag_2/{random_redbag}')
ava = CreateImg(65, 65, background=BytesIO(await get_user_avatar(user_id)))
redbag = BuildImage(0, 0, font_size=38, background=f'{IMAGE_PATH}/prts/redbag_2/{random_redbag}')
ava = BuildImage(65, 65, background=BytesIO(await get_user_avatar(user_id)))
await asyncio.get_event_loop().run_in_executor(None, ava.circle)
redbag.text((int((redbag.size[0] - redbag.getsize(msg)[0]) / 2), 210), msg, (240, 218, 164))
redbag.paste(ava, (int((redbag.size[0] - ava.size[0])/2), 130), True)
@@ -62,19 +61,19 @@ async def generate_open_redbag_pic(user_id: int, send_user_nickname: str, amount
async def _generate_open_redbag_pic(user_id: int, send_user_nickname: str, amount: int, text: str):
send_user_nickname += '的红包'
amount = str(amount)
head = CreateImg(1000, 980, font_size=30, background=f'{IMAGE_PATH}/prts/redbag_12.png')
size = CreateImg(0, 0, font_size=50).getsize(send_user_nickname)
head = BuildImage(1000, 980, font_size=30, background=f'{IMAGE_PATH}/prts/redbag_12.png')
size = BuildImage(0, 0, font_size=50).getsize(send_user_nickname)
# QQ头像
ava_bk = CreateImg(100 + size[0], 66, color='white', font_size=50)
ava = CreateImg(66, 66, background=BytesIO(await get_user_avatar(user_id)))
ava_bk = BuildImage(100 + size[0], 66, color='white', font_size=50)
ava = BuildImage(66, 66, background=BytesIO(await get_user_avatar(user_id)))
ava_bk.paste(ava)
ava_bk.text((100, 7), send_user_nickname)
# ava_bk.show()
ava_bk_w, ava_bk_h = ava_bk.size
head.paste(ava_bk, (int((1000 - ava_bk_w) / 2), 300))
# 金额
size = CreateImg(0, 0, font_size=150).getsize(amount)
price = CreateImg(size[0], size[1], font_size=150)
size = BuildImage(0, 0, font_size=150).getsize(amount)
price = BuildImage(size[0], size[1], font_size=150)
price.text((0, 0), amount, fill=(209, 171, 108))
# 金币中文
head.paste(price, (int((1000 - size[0]) / 2) - 50, 460))
+13
View File
@@ -1,4 +1,6 @@
from configs.config import Config
from configs.path_config import IMAGE_PATH
from pathlib import Path
import nonebot
@@ -44,5 +46,16 @@ Config.add_plugin_config(
default_value=6,
)
Config.add_plugin_config(
"image_management",
"SHOW_ID",
True,
help_="是否消息显示图片下标id",
default_value=True
)
(Path(IMAGE_PATH) / "image_management").mkdir(parents=True, exist_ok=True)
nonebot.load_plugins("plugins/image_management")
@@ -31,12 +31,17 @@ __plugin_settings__ = {
delete_img = on_command("删除图片", priority=5, rule=to_me(), block=True)
_path = Path(IMAGE_PATH) / "image_management"
@delete_img.args_parser
async def parse(bot: Bot, event: MessageEvent, state: T_State):
if get_message_text(event.json()) in ["取消", "算了"]:
await delete_img.finish("已取消操作..", at_sender=True)
if state["_current_key"] in ["path"]:
if get_message_text(event.json()) not in Config.get_config("image_management", "IMAGE_DIR_LIST"):
if get_message_text(event.json()) not in Config.get_config(
"image_management", "IMAGE_DIR_LIST"
):
await delete_img.reject("此目录不正确,请重新输入目录!")
state[state["_current_key"]] = get_message_text(event.json())
if state["_current_key"] == "id":
@@ -52,7 +57,11 @@ async def _(bot: Bot, event: MessageEvent, state: T_State):
args = raw_arg.split(" ")
if args[0] in ["帮助"]:
await delete_img.finish(__plugin_usage__)
if len(args) >= 2 and args[0] in Config.get_config("image_management", "IMAGE_DIR_LIST") and is_number(args[1]):
if (
len(args) >= 2
and args[0] in Config.get_config("image_management", "IMAGE_DIR_LIST")
and is_number(args[1])
):
state["path"] = args[0]
state["id"] = args[1]
@@ -60,18 +69,18 @@ async def _(bot: Bot, event: MessageEvent, state: T_State):
@delete_img.got("path", prompt="请输入要删除的目标图库?")
@delete_img.got("id", prompt="请输入要删除的图片id?")
async def arg_handle(bot: Bot, event: MessageEvent, state: T_State):
path = cn2py(state["path"])
img_id = state["id"]
# path = IMAGE_PATH + path
path = Path(IMAGE_PATH) / path
temp = Path(IMAGE_PATH) / "temp"
path = _path / cn2py(state["path"])
if not path.exists() and (path.parent.parent / cn2py(state["path"])).exists():
path = path.parent.parent / cn2py(state["path"])
temp = Path(TEMP_PATH)
max_id = len(os.listdir(path)) - 1
if int(img_id) > max_id or int(img_id) < 0:
await delete_img.finish(f"Id超过上下限,上限:{max_id}", at_sender=True)
try:
if os.path.exists(temp / "delete.jpg"):
os.remove(temp / "delete.jpg")
logger.info("删除图片 delete.jpg 成功")
if (temp / "delete.jpg").exists():
(temp / "delete.jpg").unlink()
logger.info(f"删除{cn2py(state['path'])}图片 {img_id}.jpg 成功")
except Exception as e:
logger.warning(f"删除图片 delete.jpg 失败 e{e}")
try:
@@ -31,6 +31,9 @@ __plugin_settings__ = {
move_img = on_command("移动图片", priority=5, rule=to_me(), block=True)
_path = Path(IMAGE_PATH) / "image_management"
@move_img.args_parser
async def parse(bot: Bot, event: MessageEvent, state: T_State):
if str(event.get_message()) in ["取消", "算了"]:
@@ -72,8 +75,20 @@ async def _(bot: Bot, event: MessageEvent, state: T_State):
@move_img.got("id", prompt="要移动的图片id是?")
async def _(bot: Bot, event: MessageEvent, state: T_State):
img_id = state["id"]
source_path = Path(IMAGE_PATH) / cn2py(state["source_path"])
destination_path = Path(IMAGE_PATH) / cn2py(state["destination_path"])
source_path = _path / cn2py(state["source_path"])
destination_path = _path / cn2py(state["destination_path"])
if (
not source_path.exists()
and (source_path.parent.parent / cn2py(state["source_path"])).exists()
):
source_path = source_path.parent.parent / cn2py(state["source_path"])
if (
not destination_path.exists()
and (destination_path.parent.parent / cn2py(state["destination_path"])).exists()
):
destination_path = destination_path.parent.parent / cn2py(
state["destination_path"]
)
destination_path.mkdir(parents=True, exist_ok=True)
max_id = len(os.listdir(source_path)) - 1
des_max_id = len(os.listdir(destination_path))
+44 -24
View File
@@ -1,4 +1,4 @@
from nonebot import on_command, on_keyword, on_regex
from nonebot import on_message, on_keyword, on_regex
from configs.path_config import IMAGE_PATH
from utils.message_builder import image
from utils.utils import get_message_text, is_number
@@ -6,8 +6,10 @@ from services.log import logger
from nonebot.typing import T_State
from nonebot.adapters.cqhttp import Bot, MessageEvent, GroupMessageEvent
from utils.utils import FreqLimiter, cn2py
from pathlib import Path
from configs.config import Config
from utils.manager import group_manager, withdraw_message_manager
from .rule import rule
import random
import os
@@ -16,10 +18,10 @@ try:
except ModuleNotFoundError:
import json
__zx_plugin_name__ = "发送本地图库图片"
__zx_plugin_name__ = "本地图库"
__plugin_usage__ = f"""
usage:
发送指定图库下的随机或指定id图片
发送指定图库下的随机或指定id图片genshin_memo
指令:
{Config.get_config("image_management", "IMAGE_DIR_LIST")} ?[id]
示例:美图
@@ -37,49 +39,66 @@ __plugin_settings__ = {
"cmd": ["发送图片"] + Config.get_config("image_management", "IMAGE_DIR_LIST"),
}
__plugin_task__ = {"pa": "丢人爬"}
__plugin_resources__ = {
"pa": IMAGE_PATH
}
__plugin_resources__ = {"pa": IMAGE_PATH}
Config.add_plugin_config(
"_task",
"DEFAULT_PA",
True,
help_="被动 爬 进群默认开关状态",
default_value=True,
)
_flmt = FreqLimiter(1)
cmd = set(Config.get_config("image_management", "IMAGE_DIR_LIST"))
# print(cmd)
send_img = on_command("img", aliases=cmd, priority=5, block=True)
send_img = on_message(priority=5, rule=rule, block=True)
pa = on_keyword({"丢人爬", "爪巴"}, priority=5, block=True)
pa_reg = on_regex("^爬$", priority=5, block=True)
search_url = "https://api.fantasyzone.cc/tu/search.php"
_path = Path(IMAGE_PATH) / "image_management"
@send_img.handle()
async def _(bot: Bot, event: MessageEvent, state: T_State):
img_id = get_message_text(event.json())
path = cn2py(state["_prefix"]["raw_command"]) + "/"
if state["_prefix"]["raw_command"] in Config.get_config(
msg = get_message_text(event.json()).split()
gallery = msg[0]
if gallery not in Config.get_config("image_management", "IMAGE_DIR_LIST"):
return
img_id = None
if len(msg) > 1:
img_id = msg[1]
path = _path / cn2py(gallery)
if gallery in Config.get_config(
"image_management", "IMAGE_DIR_LIST"
):
if not os.path.exists(f"{IMAGE_PATH}/{path}/"):
os.mkdir(f"{IMAGE_PATH}/{path}/")
length = len(os.listdir(IMAGE_PATH + path))
if not path.exists() and (path.parent.parent / cn2py(gallery)).exists():
path = Path(IMAGE_PATH) / cn2py(gallery)
else:
path.mkdir(parents=True, exist_ok=True)
length = len(os.listdir(path))
if length == 0:
logger.warning(f"图库 {path} 为空,调用取消!")
logger.warning(f'图库 {cn2py(gallery)} 为空,调用取消!')
await send_img.finish("该图库中没有图片噢")
index = img_id if img_id else str(random.randint(0, length))
index = img_id if img_id else str(random.randint(0, length - 1))
if not is_number(index):
return
if int(index) > length - 1 or int(index) < 0:
await send_img.finish(f"超过当前上下限!({length - 1})")
result = image(f"{index}.jpg", path)
result = image(path / f"{index}.jpg")
if result:
logger.info(
f"(USER {event.user_id}, GROUP "
f"{event.group_id if isinstance(event, GroupMessageEvent) else 'private'}) 发送{path}:"
f"{event.group_id if isinstance(event, GroupMessageEvent) else 'private'}) "
f"发送{cn2py(gallery)}:"
+ result
)
msg_id = await send_img.send(f"id:{index}" + result)
msg_id = await send_img.send(
f"id:{index}" + result
if Config.get_config("image_management", "SHOW_ID")
else "" + result
)
withdraw_message_manager.withdraw_message(
event,
msg_id,
@@ -88,7 +107,8 @@ async def _(bot: Bot, event: MessageEvent, state: T_State):
else:
logger.info(
f"(USER {event.user_id}, GROUP "
f"{event.group_id if isinstance(event, GroupMessageEvent) else 'private'}) 发送 {path} 失败"
f"{event.group_id if isinstance(event, GroupMessageEvent) else 'private'}) "
f"发送 {cn2py(gallery)} 失败"
)
await send_img.finish(f"不想给你看Ov|")
@@ -121,4 +141,4 @@ async def _(bot: Bot, event: MessageEvent, state: T_State):
return
if _flmt.check(event.user_id):
_flmt.start_cd(event.user_id)
await pa.finish(image(random.choice(os.listdir(IMAGE_PATH + "pa")), "pa"))
await pa_reg.finish(image(random.choice(os.listdir(IMAGE_PATH + "pa")), "pa"))
@@ -0,0 +1,18 @@
from nonebot.adapters.cqhttp import Bot, Event
from nonebot.typing import T_State
from utils.utils import get_message_text
from configs.config import Config
def rule(bot: Bot, event: Event, state: T_State) -> bool:
"""
检测文本是否是关闭功能命令
:param bot: pass
:param event: pass
:param state: pass
"""
msg = get_message_text(event.json())
for x in Config.get_config("image_management", "IMAGE_DIR_LIST"):
if msg.startswith(x):
return True
return False
@@ -3,7 +3,7 @@ from nonebot.rule import to_me
from nonebot.typing import T_State
from nonebot.adapters.cqhttp import Bot, MessageEvent, GroupMessageEvent
from configs.config import Config
from utils.utils import get_message_imgs, get_message_text
from utils.utils import get_message_img, get_message_text
from .data_source import upload_image_to_local
@@ -22,7 +22,7 @@ __plugin_des__ = "指定图库图片上传"
__plugin_cmd__ = ["上传图片 [图库] [图片]", "连续上传图片 [图库]", "查看公开图库"]
__plugin_version__ = 0.1
__plugin_author__ = "HibiKier"
__plugin_settings__ = {"admin_level": Config.get_config("image_management", "DELETE_IMAGE_LEVEL")}
__plugin_settings__ = {"admin_level": Config.get_config("image_management", "UPLOAD_IMAGE_LEVEL")}
upload_img = on_command("上传图片", rule=to_me(), priority=5, block=True)
@@ -49,15 +49,15 @@ async def _(bot: Bot, event: MessageEvent, state: T_State):
await upload_img.reject("此目录不正确,请重新输入目录!")
state["path"] = msg
if state["_current_key"] in ["imgs"]:
if not get_message_imgs(event.json()):
if not get_message_img(event.json()):
await upload_img.reject("图呢图呢图呢图呢!GKD!")
state["imgs"] = get_message_imgs(event.json())
state["imgs"] = get_message_img(event.json())
@upload_img.handle()
async def _(bot: Bot, event: MessageEvent, state: T_State):
raw_arg = get_message_text(event.json())
img_list = get_message_imgs(event.json())
img_list = get_message_img(event.json())
if raw_arg:
if raw_arg in Config.get_config("image_management", "IMAGE_DIR_LIST"):
state["path"] = raw_arg
@@ -92,7 +92,7 @@ async def _(bot: Bot, event: MessageEvent, state: T_State):
state[state["_current_key"]] = str(event.get_message())
else:
if get_message_text(event.json()) not in ["stop"]:
img = get_message_imgs(event.json())
img = get_message_img(event.json())
if img:
state["tmp"].extend(img)
await continuous_upload_img.reject("图再来!!")
@@ -102,10 +102,10 @@ async def _(bot: Bot, event: MessageEvent, state: T_State):
@continuous_upload_img.handle()
async def _(bot: Bot, event: MessageEvent, state: T_State):
path = get_message_imgs(event.json())
path = get_message_img(event.json())
if path in Config.get_config("image_management", "IMAGE_DIR_LIST"):
state["path"] = path
await continuous_upload_img.send("图来!!")
await continuous_upload_img.send("图来!!【停止请发送 ‘stop’ 开始上传】")
state["tmp"] = []
@@ -8,11 +8,16 @@ from utils.http_utils import AsyncHttpx
import os
_path = Path(IMAGE_PATH) / "image_management"
async def upload_image_to_local(
img_list: List[str], path: str, user_id: int, group_id: int = 0
img_list: List[str], path_: str, user_id: int, group_id: int = 0
) -> str:
_path = path
path = Path(IMAGE_PATH) / cn2py(path)
_path_name = path_
path = _path / cn2py(path_)
if not path.exists() and (path.parent.parent / cn2py(path_)).exists():
path = path.parent.parent / cn2py(path_)
path.mkdir(parents=True, exist_ok=True)
img_id = len(os.listdir(path))
failed_list = []
@@ -28,15 +33,15 @@ async def upload_image_to_local(
failed_result += str(img) + "\n"
logger.info(
f"USER {user_id} GROUP {group_id}"
f" 上传图片至 {_path} 共 {len(img_list)} 张,失败 {len(failed_list)} 张,id={success_id[:-1]}"
f" 上传图片至 {_path_name} 共 {len(img_list)} 张,失败 {len(failed_list)} 张,id={success_id[:-1]}"
)
if failed_list:
return (
f"这次一共为 {_path}库 添加了 {len(img_list) - len(failed_list)} 张图片\n"
f"这次一共为 {_path_name}库 添加了 {len(img_list) - len(failed_list)} 张图片\n"
f"依次的Id为:{success_id[:-1]}\n上传失败:{failed_result[:-1]}\n{NICKNAME}感谢您对图库的扩充!WW"
)
else:
return (
f"这次一共为 {_path}库 添加了 {len(img_list)} 张图片\n依次的Id为:"
f"这次一共为 {_path_name}库 添加了 {len(img_list)} 张图片\n依次的Id为:"
f"{success_id[:-1]}\n{NICKNAME}感谢您对图库的扩充!WW"
)
+3 -3
View File
@@ -5,7 +5,7 @@ from nonebot.adapters.cqhttp import Bot, MessageEvent, GroupMessageEvent
from utils.message_builder import image
from services.log import logger
from utils.utils import get_message_text
from utils.image_utils import CreateImg
from utils.image_utils import BuildImage
__zx_plugin_name__ = "鲁迅说"
__plugin_usage__ = """
@@ -31,7 +31,7 @@ __plugin_block_limit__ = {
luxun = on_command("鲁迅说过", aliases={"鲁迅说"}, priority=5, block=True)
luxun_author = CreateImg(0, 0, plain_text="--鲁迅", font_size=30, font='msyh.ttf', font_color=(255, 255, 255))
luxun_author = BuildImage(0, 0, plain_text="--鲁迅", font_size=30, font='msyh.ttf', font_color=(255, 255, 255))
@luxun.handle()
@@ -46,7 +46,7 @@ async def handle_event(bot: Bot, event: MessageEvent, state: T_State):
content = state["content"].strip()
if content.startswith(",") or content.startswith(","):
content = content[1:]
A = CreateImg(0, 0, font_size=37, background=f'{IMAGE_PATH}/other/luxun.jpg', font='msyh.ttf')
A = BuildImage(0, 0, font_size=37, background=f'{IMAGE_PATH}/other/luxun.jpg', font='msyh.ttf')
x = ""
if len(content) > 40:
await luxun.finish('太长了,鲁迅说不完...')

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