diff --git a/README.md b/README.md
index 25a86aa6..d4e3154d 100644
--- a/README.md
+++ b/README.md
@@ -20,7 +20,7 @@
## 未完成的文档
-[传送门](https://hibikier.github.io/zhenxun_bot/)
+
[传送门](https://hibikier.github.io/zhenxun_bot/)
## 真寻的帮助
请对真寻说: '真寻帮助' or '管理员帮助' or '超级用户帮助' or '真寻帮助 指令'
@@ -51,9 +51,11 @@
- [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] B站主播/UP/番剧订阅
@@ -89,6 +91,8 @@
- [x] 通过PID获取图片
- [x] 功能统计可视化
+- [x] 关于
+
### 已实现的管理员功能
- [x] 更新群组成员信息
- [x] 95%的群功能开关
@@ -101,6 +105,7 @@
- [x] 删除图片 (同上)
- [x] 群内B站订阅
- [x] 群词条
+- [x] 休息吧/醒来
### 已实现的超级用户功能
- [x] 添加/删除权限(是真寻的管理员权限,不是群管理员)
@@ -126,6 +131,11 @@
- [x] 功能开关(更多设置)
- [x] 功能状态
- [x] b了
+- [x] 执行sql
+- [x] 重载配置
+- [x] 清理临时数据
+- [x] 增删群认证
+- [x] 同意/拒绝好友/群聊请求
#### 超级用户的被动技能
- [x] 邀请入群提醒(别人邀请真寻入群)
@@ -152,127 +162,6 @@
- [x] 定时更新权限
-## 功能具体指令
-
-功能具体指令说明
-
-### 常用功能
-
-** [*]:表示该参数可有多个 [?]:表示参数可选 **
-
-| 功能 | 指令 | 说明
-| ----------------------| :--------------------------------------:| :------------------------:
-| 签到 | 签到/我的签到/好感度排行/好感度总榜/好感度总榜\[显示我/屏蔽我] | 普通的签到插件,可以获得好感度和金币
好感度影响开箱次数和涩图触发概率,金币用于购买道具,俄罗斯轮盘赌注以及金币红包
好感度总榜,显示所有群的群员好感度排行,可通过命令好感度总榜\[显示我/屏蔽我] 来设置是否隐藏
-| 发送图片 | 美图/壁纸/萝莉 \[id]?(默认随机)/\[num]张图\[keyword] | 发送指定文件夹下的图片
示例:萝莉->发送img文件夹下luoli文件夹下的图片
在线搜索一些不色的图,示例:3张图米浴
-| 色图 | 色图/色图xx/n张色图/n张xx的色图/查色图(查询本地色图信息)/色图r【n<10】 | 色图r返回10张r18色图(仅私聊),并限制每日次数(默认5次)
其他示例:色图 真寻
5张真寻的色图
-| 黑白草图 | 黑白草图/黑白图 \[文字] \[图片] | 整活生成器,示例:黑白图 我喜欢真寻 \[图片]
-| coser | coser/cos/括丝 | coser图片,说实话挺失望的,太色了
-| 骂我 | 骂我 | 就是发送钉宫的语音罢了
-| 戳一戳 | 戳一戳 | 随机发送钉宫语音 or 美图 or 萝莉图 or 文本
-| 模拟开箱 | 开箱 \[武器箱名称](默认随机)/N连开箱 \[武器箱名称](默认随机)/我的开箱/群开箱统计/我的金色 | 当不指定武器箱时默认随机,此功能需要先在/open_cases/config.py中编写指定武器箱数据,然后提前爬取价格,使用超级用户命令更新cookie后,再使用命令更新xx武器箱【注:未设置爬取频率,可能会被禁用api(请谨慎!!用小号!!)】
-| 鲁迅说过 | 鲁迅说过 \[文本] | 示例:鲁迅说过 真寻世界第一可爱
-| 假消息 | 假消息 \[网址] \[标题] \[内容]? \[图片]? | 构造虚假的分享消息
-| 商店系统 | 商店/我的金币/购买道具 \[名称或序号] \[数量](默认1)/使用道具 \[名称或序号] | 示例:
购买道具 1 3
购买道具 好感度双倍加持卡Ⅰ 3
使用道具 1
使用道具 好感度双倍加持卡Ⅰ
-| 抽卡系统 | 原神/明日方舟/赛马娘/坎公骑冠剑/碧蓝航线/阴阳师/公主连结(pcr)/FGO N抽/一井 | 详细帮助请查看: [nonebot_plugin_gamedraw](https://github.com/HibiKier/nonebot_plugin_gamedraw)
示例:原神90抽
-| 我有一个朋友 | 我有一个朋友他说/想问问/\[文本] | 会将文本中的(他,她,它)替换成 '我'
示例:我有一个朋友想问问他喜不喜欢真寻
-| 昵称系统 | 以后叫我\[昵称]/以后请叫我\[昵称]/我是谁/我叫什么 | 此昵称会替换与真寻聊天中 '你' 的名称(群名片),群与群与私聊的昵称相互独立
-| 原神黄历 | 原神黄历 | 查看今日原神黄历,含有每日10:25的定时任务
-| 原神材料 | 今日素材 | 发送可莉特调的截图
-| 丘丘语翻译 | 丘丘翻译/丘丘一下/丘丘语翻译 | 示例:丘丘一下 mimi
-| 原神资源查询 | 原神资源查询 \[资源名称] \[路线]?/\[资源名称]在哪/哪里有\[资源名称]/原神资源列表 | 如果资源名称末尾添加‘路线’的话将生成残缺缺缺版的优先路径
示例:嘟嘟莲在哪
原神资源查询嘟嘟莲路线
-| 俄罗斯轮盘 | 装弹\[子弹数] \[金额](默认200)/开枪/结算/我的战绩/胜场排行/败场排行/欧洲人排行/慈善家排行 | 紧张刺激的群内小游戏,使用每日签到的金币作为赌注,具体玩法请发送 真寻帮助 俄罗斯轮盘
-| 红包系统 | 塞红包\[金额] \[数量](默认5)/抢/开/戳一戳/退回 | 仿微信明日方舟红包的样式(pil拼图大师!),每个红包金额随机生成,最多会是红包总金额的1/3,退回用于退回一分钟后还未开完的红包
-| 金币排行 | 金币排行 | 字面意思
-|网易云热评 | 到点了/12点了/网易云热评/网易云评论 | 防下塔
-| 古诗 | 念诗/念首诗/来首诗 | 突然文艺起来了
-|微博热搜 | 微博热搜/微博热搜[序号]? | 快捷热搜查询方式
-| pil对图片的操作 | 修改尺寸/等比压缩/旋转图片/水平翻转/铅笔滤镜/模糊效果/锐化效果/高斯模糊/边缘检测/底色替换 | 选项较多,请直接发送 真寻图片帮助
-| BUFF皮肤底价查询 | 查询皮肤 \[武器名称] \[皮肤名称] | 网络不友好的话会经常超时
示例:查询皮肤 沙漠之鹰 印花集
-| 天气查询 | \[城市]天气 | 非常常见的插件,第一个入门插件
-| 疫情查询 | 疫情/查询疫情 \[城市名或省份名] | 示例:疫情杭州
-| bt磁力搜索 | bt \[关键词] \[页数]?(默认第1页) | 该功能仅仅提供给私聊,因为可以搜到一些色色的东西,示例:bt钢铁侠 5
-| 上车 | 略 | 直接查看真寻帮助 上车,每日限制次数(默认5)
-| 以图识番 | 识番 \[图片] | 以图搜翻,图片越清晰越完整正确率越高
-| 以图搜图 | 识图 (asc)? \[图片] | 参数asc更换搜索引擎为ascii2d,默认为saucenao
-| 点歌 | 点歌 \[歌名] | 网易云点歌小助手
-| 搜番 | 搜番 \[关键字] | 群聊只返回5个结果,私聊返回20个结果
-| epic白嫖游戏通知 | epic | 通知你又到了白嫖游戏的时候,可以不玩,不能没有
-| P站排行榜 | p站排行 \[排行类型参数]? \[数量]? \[日期]?| 9种不同排行榜,r18类型仅可私聊,通过参数选择,查看真寻帮助p站排行
示例:p站排行榜 1 9 2018-4-25
-| 搜图 | 搜图 \[关键词] \[数量]? \[排序方式]? \[r18]?| r18仅可私聊,查看真寻帮助搜图
-| 通过PID搜索图片 | p搜 [pid] | 在群内使用此功能会在30秒内撤回
-| 翻译 | 英翻/日翻/韩翻/翻韩/翻日/翻英 \[文本] | 三种语言互相翻译
-| 获取b站视频封面 | b封面 [链接/av/bv/cv/直播id] | 快捷的封面获取方式
-| 群欢迎消息 | 群欢迎消息/查看群欢迎消息/查看当前群欢迎消息 | 查看给真寻设置的群欢迎消息
-| 自我介绍 | 自我介绍 | 没错,一份正经的真寻自我介绍
-| 我的权限 | 我的权限 | 真寻内部定义的一套权限系统
-| 我的信息 | 我的信息 | 唯一的作用就是看看什么时候加入群
-| 撤回 | 撤回 \[消息位置]?(默认为最新一条消息) | 按顺序撤回发送的消息,示例:撤回 1
-| 滴滴滴- | 滴滴滴- \[文本] | 用于用户联系真寻的超级用户
-|功能调用统计可视化 | 功能调用统计(自记录以来的功能调用统计)
周功能调用统计 [plugin_name]
月功能调用统计 [plugin_name]| 当plugin_name为空时为7天或30内的所有功能统计
-| pix | pix/PIX [tags/uid/pid:pid] [num] | 无参数时随机查看pix图库的图片(无r18),num数量默认=1,tags:查看相关tags图片,uid:查找相关画师图片,pid:pid:指定查看pid图片
示例:pix原神 3
pix23493844
pixpid:29429933
-| 添加pix关键词/uid/pid | 添加pix关键词/uid/pid *[关键词/uid/pid]| 添加关键词或uid或pid用于下次搜索,关键词搜索相关tag,uid会收录作者下收藏符合标准的作品,pid收录单张作品
示例:添加pix关键词 原神
添加pixuid 123441
添加pixpid 2748937|
-| 查看pix图库 | 查看pix图库 [tags] | 查看已收录的tag相关图片数量
示例:查看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
示例:.ban@笨蛋 1 50
-| 刷屏检测相关 | 5 | 刷屏检测设置/设置检测时间 \[文本]/设置检测次数 \[文本]/设置禁言时长 \[分钟]| 非常讨厌刷屏的人,打算给他们一点教训
-| 上传图片 | 6 | 上传图片 \[图库] \[图片]... | 上传图片至指定图库,虽然并不打算开放给群员,但还是写了,支持批量图片
示例:上传图片 美图 \[图片]..
-| 删除图片 | 6 | 删除图片 [图库] \[图片id] | 通过指定本地图片id来删除指定图库的图片
示例:删除图片 美图 1
-| 移动图片 | 6 | 移动图片 \[移出的图库] \[移入的图库] \[图片id] | 移动指定图库中的图片到指定的新图库中,移入的图片id更改为移入图库的最后一位,移除的图库中原本图片的id又最后一位图片替代
示例:移动图片 美图 萝莉 22
-|B站订阅 | 5 | 功能同上,就是在群中有权限限制 | 略
-
-### 超级用户功能
-
-| 功能 | 指令 | 说明
-| ----------------------| :--------------------------------------:| :------------------------:
-| 权限增删 | 添加/删除权限 \[at] \[level]
添加/删除权限 [qq] [group] [level] | 用于添加或修改权限等级,且该权限不会被自动更新取消
-| 所有群组/好友 | 所有群组/好友 | 查看真寻添加的群组与好友
-| 广播 | 广播- [文本] | 广播所有群组
-| 更新色图 | 更新色图 | 更新群友搜索色图时保存的url
-| 回复 | /t | 命令较多,请查看/t帮助,省略群号则私聊用户(必须要有用户的好友)
-| 更新cookie | 更新cookie \[cookie] | 用于更新开箱数据和查询buff皮肤
-| 开启广播通知 | 开启/关闭广播通知 \[群号] | 用于开启/关闭是否对某些群进行广播(上边的广播方法)
-| 退群 | 退群 \[群号] | 用于退出某一些群
-| 检查系统状态 | 自检 | 略
-| 更新好友/群组信息| 更新好友/群组信息 | 包含自动更新,被t出群等等有更好的可视信息
-| 重载卡池 | 略 | 重载抽卡的游戏卡池,请查看 \[nonebot_plugin_gamedraw](https://github.com/HibiKier/nonebot_plugin_gamedraw)
-| 添加商品 | 添加商品 \[名称]-\[价格]-\[描述]-\[折扣](小数)?-\[限时时间](分钟)? | 为真寻的商店添加一点点道具
示例:添加商品-昏睡红茶-300-一杯上好的奇怪红茶-0.9-60
-| 删除商品 | 删除商品 \[名称或序号] | 在真寻的商店中删除一点东西
-| 修改商品 | 修改商品 -name \[名称或序号] -price \[价格] -des \[描述] -discount \[折扣] -time \[限时]| 注意空格,不需要的参数可以不加
示例:修改商品 -name 1 -price 900 【修改序号为1的商品的价格为900】
-| 节日红包 | 节日红包 \[金额] \[数量] \[祝福语]? \[群号]?... | 群号支持批量,使用空格隔开,不使用群号则对所有群发送节日红包,节日红包有效时间为24小时,祝福语默认为“恭喜发财 大吉大利”
示例:节日红包 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更新全部
示例:更新pix关键词
更新pix关键词uid 8
更新pix关键词pid:83457477
num:倒叙更新数量
-|删除pix图片 | 删除pix图片 [*pid] [-b]? | [-b]参数为删除的同时加入黑名单(不再更新),虽然是pid,但是_p也可以
示例:删除pix图片3458344 8235234_p1 -b
-|显示pix关键词 | 显示pix关键词 | 与普通功能相同,额外显示待收录和黑名单
-|pix检测更新 | pix检测更新 [update]? | 检测从未更新过的pid或uid,-update参数将在检测后直接更新未更新过的pid或uid
示例:pix检测更新 update
-| 检查更新真寻 | 检查更新真寻 | 不再需要麻烦的clone第一步
-| 关闭功能 | 关闭[功能] [group/private]/*[群号]? | 关闭色图:维护功能(关闭总开关)
关闭色图 g:在群内限制功能
关闭色图 p:在私聊限制功能
关闭色图 1234678:禁用12345678的色图功能
-| 群白名单 | 添加/删除群白名单 *[群号] / 查看群白名单 | 白名单内的群不会受到 维护 限制
-| 功能状态 | 功能状态 | 查看功能开关状态
-| pix | pix [-s/-r] [keyword] | 可以通过pix -s 查看图库的涩图,pix -r查看图库的r18图,支持搜索,当然,pix图库只区分了r18和非r18,如果-s查询到不色的图也问题不大
-| 重载插件配置 |重载插件配置 |用于生效手动修改配文件
-|帮助 -super | 帮助[功能] -super | 显示该插件的超级用户帮助
-
-
-
## 详细配置请前往文档,以下为最简部署和配置,如果你有基础并学习过nonebot2的话
@@ -321,6 +210,29 @@ python bot.py
## 更新
+### 2021/12/16 \[v0.0.7.0]
+
+* 提供了真寻群聊功能总开关和对应默认配置项,命令:休息吧 醒来
+* 新增原神玩家查询,原神便笺查询
+* 群功能管理提供全部开启/关闭命令:开启/关闭全部功能
+* 提供主要数据自动备份,且提供自定义配置项
+* 提供命令:关于,用于介绍Bot之类的
+* 新增命令exec,用于执行sql语句
+* 签到提供参数 "all",用于签到所有群聊
+* Ban提醒提供cd
+* 本地图库提供配置项SHOW_ID,用于设置发送图片时是否显示id
+* 色图和PIX提供配置项SHOW_INFO,用于设置发送图片时是否显示图片信息
+* 所有被动技能提供了进群默认状态配置项
+
+* 修复添加权限第二种添加形式无法正确添加正确的权限
+* 修复签到获取好感度卡时金币不会增加
+* 修复当红包数量不合法时依旧扣除金币
+* 修复金币红包再次使用塞红包时无法正确退回上次未开完的金币
+* 修复 滴滴滴- 只包含图片时不会发送至管理员
+
+* 修改了权限插件加载顺序防止小概率优先加载权限插件引起报错
+* 本地图库新图库会统一建立在resource/img/image_management文件夹下,如果该文件夹内未找到图库,会从上级目录查找(即:resource/img/)
+
### 2021/12/1 \[v0.0.6.5/6]
* 群权限-1时超级用户命令依旧生效
diff --git a/basic_plugins/admin_bot_manage/data_source.py b/basic_plugins/admin_bot_manage/data_source.py
index 345ad630..6d32cebc 100755
--- a/basic_plugins/admin_bot_manage/data_source.py
+++ b/basic_plugins/admin_bot_manage/data_source.py
@@ -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 "那我先睡觉了..."
diff --git a/basic_plugins/admin_bot_manage/rule.py b/basic_plugins/admin_bot_manage/rule.py
index 397b9c8d..2139213d 100755
--- a/basic_plugins/admin_bot_manage/rule.py
+++ b/basic_plugins/admin_bot_manage/rule.py
@@ -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]}")
diff --git a/basic_plugins/admin_bot_manage/switch_rule.py b/basic_plugins/admin_bot_manage/switch_rule.py
index 7d580610..7c3bf3a7 100755
--- a/basic_plugins/admin_bot_manage/switch_rule.py
+++ b/basic_plugins/admin_bot_manage/switch_rule.py
@@ -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)}")
diff --git a/basic_plugins/admin_help/data_source.py b/basic_plugins/admin_help/data_source.py
index 65862fdb..5535ae98 100755
--- a/basic_plugins/admin_help/data_source.py
+++ b/basic_plugins/admin_help/data_source.py
@@ -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)
diff --git a/basic_plugins/apscheduler/__init__.py b/basic_plugins/apscheduler/__init__.py
index a12aa80e..487025b6 100755
--- a/basic_plugins/apscheduler/__init__.py
+++ b/basic_plugins/apscheduler/__init__.py
@@ -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}")
+
+
# 一次性任务
# 固定时间触发,仅触发一次:
#
diff --git a/basic_plugins/broadcast/__init__.py b/basic_plugins/broadcast/__init__.py
index 328d73c7..97f129b4 100755
--- a/basic_plugins/broadcast/__init__.py
+++ b/basic_plugins/broadcast/__init__.py
@@ -7,6 +7,7 @@ from utils.utils import get_message_text, get_message_imgs
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)
diff --git a/basic_plugins/group_handle/__init__.py b/basic_plugins/group_handle/__init__.py
index ff390d09..59abf8cb 100755
--- a/basic_plugins/group_handle/__init__.py
+++ b/basic_plugins/group_handle/__init__.py
@@ -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"))
diff --git a/basic_plugins/help/data_source.py b/basic_plugins/help/data_source.py
index 6168180d..e10fc521 100755
--- a/basic_plugins/help/data_source.py
+++ b/basic_plugins/help/data_source.py
@@ -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:
@@ -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",
diff --git a/basic_plugins/hooks/__init__.py b/basic_plugins/hooks/__init__.py
index f33bd813..9ca94805 100755
--- a/basic_plugins/hooks/__init__.py
+++ b/basic_plugins/hooks/__init__.py
@@ -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")
diff --git a/basic_plugins/hooks/auth_hook.py b/basic_plugins/hooks/auth_hook.py
index aaa173da..7e9403ce 100755
--- a/basic_plugins/hooks/auth_hook.py
+++ b/basic_plugins/hooks/auth_hook.py
@@ -13,7 +13,7 @@ from nonebot.adapters.cqhttp import (
Bot,
MessageEvent,
GroupMessageEvent,
- PokeNotifyEvent,
+ PokeNotifyEvent
)
from configs.config import Config
from models.ban_user import BanUser
@@ -37,16 +37,19 @@ 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
+ 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)
@@ -59,10 +62,19 @@ async def _(matcher: Matcher, bot: Bot, event: MessageEvent, state: T_State):
return
except AttributeError:
pass
- # 群黑名单检测
- if isinstance(event, GroupMessageEvent):
- if group_manager.get_group_level(event.group_id) < 0:
- raise IgnoredException("群黑名单")
+ # 群黑名单检测 群总开关检测
+ 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):
# 个人权限
diff --git a/basic_plugins/hooks/ban_hook.py b/basic_plugins/hooks/ban_hook.py
index ea59ef0a..b3f2ac7e 100755
--- a/basic_plugins/hooks/ban_hook.py
+++ b/basic_plugins/hooks/ban_hook.py
@@ -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)
diff --git a/basic_plugins/nickname.py b/basic_plugins/nickname.py
index c4ff30db..bb8ef1e0 100755
--- a/basic_plugins/nickname.py
+++ b/basic_plugins/nickname.py
@@ -25,7 +25,7 @@ __plugin_version__ = 0.1
__plugin_author__ = "HibiKier"
__plugin_configs__ = {
"BLACK_WORD": {
- "value": ["爸", "妈", "爹", "爷"],
+ "value": ["爸", "爹", "爷"],
"help": "昵称所屏蔽的关键词,会被替换为 *",
"default_value": None
}
diff --git a/basic_plugins/super_cmd/exec_sql.py b/basic_plugins/super_cmd/exec_sql.py
new file mode 100644
index 00000000..4b44f251
--- /dev/null
+++ b/basic_plugins/super_cmd/exec_sql.py
@@ -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}")
diff --git a/basic_plugins/super_cmd/set_admin_permissions.py b/basic_plugins/super_cmd/set_admin_permissions.py
index a375af6e..f488e3fa 100755
--- a/basic_plugins/super_cmd/set_admin_permissions.py
+++ b/basic_plugins/super_cmd/set_admin_permissions.py
@@ -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,7 +55,7 @@ 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:
@@ -63,10 +63,6 @@ async def _(bot: Bot, event: GroupMessageEvent, state: T_State):
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:
diff --git a/basic_plugins/super_help/data_source.py b/basic_plugins/super_help/data_source.py
index 730ff152..4138bf5c 100755
--- a/basic_plugins/super_help/data_source.py
+++ b/basic_plugins/super_help/data_source.py
@@ -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)
diff --git a/bot.py b/bot.py
index c8f1a34b..ca579075 100644
--- a/bot.py
+++ b/bot.py
@@ -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__":
diff --git a/configs/config.py b/configs/config.py
index ead748f6..70ff7a1e 100644
--- a/configs/config.py
+++ b/configs/config.py
@@ -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")
diff --git a/configs/utils/__init__.py b/configs/utils/__init__.py
index 7bf96d5c..739d2118 100644
--- a/configs/utils/__init__.py
+++ b/configs/utils/__init__.py
@@ -105,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():
@@ -118,6 +119,8 @@ 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]:
@@ -170,4 +173,3 @@ class ConfigsManager:
def __getitem__(self, key):
return self._data[key]
-
diff --git a/models/friend_user.py b/models/friend_user.py
index 69d31ef5..ef7742f7 100755
--- a/models/friend_user.py
+++ b/models/friend_user.py
@@ -83,8 +83,9 @@ class FriendUser(db.Model):
if user.nickname:
_tmp = ""
black_word = Config.get_config("nickname", "BLACK_WORD")
- for x in user.nickname:
- _tmp += "*" if x in black_word else x
+ if black_word:
+ for x in user.nickname:
+ _tmp += "*" if x in black_word else x
return _tmp
return ""
diff --git a/models/group_member_info.py b/models/group_member_info.py
index df032d8b..6db45bab 100755
--- a/models/group_member_info.py
+++ b/models/group_member_info.py
@@ -156,8 +156,9 @@ class GroupInfoUser(db.Model):
if user.nickname:
_tmp = ""
black_word = Config.get_config("nickname", "BLACK_WORD")
- for x in user.nickname:
- _tmp += "*" if x in black_word else x
+ if black_word:
+ for x in user.nickname:
+ _tmp += "*" if x in black_word else x
return _tmp
return ""
diff --git a/models/sign_group_user.py b/models/sign_group_user.py
index d605c164..1c86677c 100755
--- a/models/sign_group_user.py
+++ b/models/sign_group_user.py
@@ -1,5 +1,5 @@
from datetime import datetime
-
+from typing import List
from services.db_context import db
@@ -26,6 +26,14 @@ class SignGroupUser(db.Model):
async def ensure(
cls, user_qq: int, belonging_group: int, for_update: bool = False
) -> "SignGroupUser":
+ """
+ 说明:
+ 获取签到用户
+ 参数:
+ :param user_qq: 用户qq
+ :param belonging_group: 所在群聊
+ :param for_update: 是否存在修改数据
+ """
query = cls.query.where(
(cls.user_qq == user_qq) & (cls.belonging_group == belonging_group)
)
@@ -40,8 +48,28 @@ class SignGroupUser(db.Model):
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,
diff --git a/plugins/about.py b/plugins/about.py
new file mode 100644
index 00000000..d6833922
--- /dev/null
+++ b/plugins/about.py
@@ -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)
diff --git a/plugins/ai/utils.py b/plugins/ai/utils.py
index ebd4f6d8..3b00941b 100755
--- a/plugins/ai/utils.py
+++ b/plugins/ai/utils.py
@@ -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:
diff --git a/plugins/alapi/data_source.py b/plugins/alapi/data_source.py
index ad853dec..26296c65 100755
--- a/plugins/alapi/data_source.py
+++ b/plugins/alapi/data_source.py
@@ -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)
diff --git a/plugins/bilibili_sub/utils.py b/plugins/bilibili_sub/utils.py
index 869944f2..42699de1 100755
--- a/plugins/bilibili_sub/utils.py
+++ b/plugins/bilibili_sub/utils.py
@@ -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")
diff --git a/plugins/c_song/music_163.py b/plugins/c_song/music_163.py
index 8552d06f..91fe774b 100755
--- a/plugins/c_song/music_163.py
+++ b/plugins/c_song/music_163.py
@@ -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):
diff --git a/plugins/check/data_source.py b/plugins/check/data_source.py
index c77865cf..b843b8c8 100755
--- a/plugins/check/data_source.py
+++ b/plugins/check/data_source.py
@@ -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()
diff --git a/plugins/check_zhenxun_update/data_source.py b/plugins/check_zhenxun_update/data_source.py
index 795b6a75..77cb0cca 100755
--- a/plugins/check_zhenxun_update/data_source.py
+++ b/plugins/check_zhenxun_update/data_source.py
@@ -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(
diff --git a/plugins/dialogue/__init__.py b/plugins/dialogue/__init__.py
index e8ad4c1e..2d003ab2 100755
--- a/plugins/dialogue/__init__.py
+++ b/plugins/dialogue/__init__.py
@@ -65,7 +65,7 @@ async def _(bot: Bot, event: MessageEvent, state: T_State):
img_msg = _text("")
for img in get_message_imgs(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
diff --git a/plugins/draw_card/util.py b/plugins/draw_card/util.py
index 0f8bf8cf..759771d2 100755
--- a/plugins/draw_card/util.py
+++ b/plugins/draw_card/util.py
@@ -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
diff --git a/plugins/epic/__init__.py b/plugins/epic/__init__.py
index a2fa2c2a..c54e5c82 100755
--- a/plugins/epic/__init__.py
+++ b/plugins/epic/__init__.py
@@ -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}")
diff --git a/plugins/fudu.py b/plugins/fudu.py
index 8e483e9e..475e3ab7 100755
--- a/plugins/fudu.py
+++ b/plugins/fudu.py
@@ -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:
diff --git a/plugins/genshin/almanac/__init__.py b/plugins/genshin/almanac/__init__.py
index 894f28e3..743a1d00 100755
--- a/plugins/genshin/almanac/__init__.py
+++ b/plugins/genshin/almanac/__init__.py
@@ -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)
diff --git a/plugins/genshin/material_remind/__init__.py b/plugins/genshin/material_remind/__init__.py
index 54925dde..223428d7 100755
--- a/plugins/genshin/material_remind/__init__.py
+++ b/plugins/genshin/material_remind/__init__.py
@@ -2,7 +2,7 @@ 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
@@ -131,12 +131,12 @@ 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
@@ -154,8 +154,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])
diff --git a/plugins/genshin/query_resource_points/map.py b/plugins/genshin/query_resource_points/map.py
index 449dbfc4..59c837e5 100755
--- a/plugins/genshin/query_resource_points/map.py
+++ b/plugins/genshin/query_resource_points/map.py
@@ -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",
diff --git a/plugins/genshin/query_resource_points/query_resource.py b/plugins/genshin/query_resource_points/query_resource.py
index 8c131832..7f06a903 100755
--- a/plugins/genshin/query_resource_points/query_resource.py
+++ b/plugins/genshin/query_resource_points/query_resource.py
@@ -3,7 +3,7 @@ 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
@@ -186,11 +186,11 @@ async def download_map_init(
force_flag=flag,
)
idx += 1
- _w, h = CreateImg(0, 0, background=f"{map_path}/0.png", ratio=MAP_RATIO).size
+ _w, h = BuildImage(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)
+ map_file = BuildImage(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.paste(BuildImage(0, 0, background=f"{map_path}/{i}.png", ratio=MAP_RATIO))
map_file.save(f"{map_path}/map.png")
else:
logger.warning(f'获取原神地图失败 msg: {data["message"]}')
@@ -230,10 +230,10 @@ async def download_resource_type():
# 初始化资源图标
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)
diff --git a/plugins/genshin/query_user/__init__.py b/plugins/genshin/query_user/__init__.py
new file mode 100644
index 00000000..0ac73fe1
--- /dev/null
+++ b/plugins/genshin/query_user/__init__.py
@@ -0,0 +1,26 @@
+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",
+ "client_type",
+ "5"
+)
+
+nonebot.load_plugins("plugins/genshin/query_user")
+
+
+
diff --git a/plugins/genshin/query_user/bind/__init__.py b/plugins/genshin/query_user/bind/__init__.py
new file mode 100644
index 00000000..ebf75495
--- /dev/null
+++ b/plugins/genshin/query_user/bind/__init__.py
@@ -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]
+ 示例:原神绑定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/登录后按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('"'):
+ msg = msg[1:]
+ if 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("该用户数据不存在..")
diff --git a/plugins/genshin/query_user/models/__init__.py b/plugins/genshin/query_user/models/__init__.py
new file mode 100644
index 00000000..fc5820ae
--- /dev/null
+++ b/plugins/genshin/query_user/models/__init__.py
@@ -0,0 +1,210 @@
+from services.db_context import db
+from typing import Optional, Union
+
+
+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) # 自动签到
+
+ _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_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_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")
+ print(uid, cookie)
+ if not cookie and not flag:
+ cookie = await cls.get_query_cookie(uid)
+ print(uid, cookie)
+ return cookie
+
+ @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
+
+ @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()
diff --git a/plugins/genshin/query_user/query_memo/__init__.py b/plugins/genshin/query_user/query_memo/__init__.py
new file mode 100644
index 00000000..13d1fad0
--- /dev/null
+++ b/plugins/genshin/query_user/query_memo/__init__.py
@@ -0,0 +1,53 @@
+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
+from ..models import Genshin
+
+
+__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": ["原神便笺查询"],
+}
+
+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'}) 使用原神便笺查询 uid:{uid}"
+ )
+ else:
+ await query_memo_matcher.send("未查询到数据...")
+
+
+
+
diff --git a/plugins/genshin/query_user/query_memo/data_source.py b/plugins/genshin/query_user/query_memo/data_source.py
new file mode 100644
index 00000000..686da4c8
--- /dev/null
+++ b/plugins/genshin/query_user/query_memo/data_source.py
@@ -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] == "1" or uid[0] == "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.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.info(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())
diff --git a/plugins/genshin/query_user/query_role/__init__.py b/plugins/genshin/query_user/query_role/__init__.py
new file mode 100644
index 00000000..d28775b0
--- /dev/null
+++ b/plugins/genshin/query_user/query_role/__init__.py
@@ -0,0 +1,61 @@
+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": ["原神玩家查询"],
+}
+
+
+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("查询失败..")
+
+
+
+
diff --git a/plugins/genshin/query_user/query_role/data_source.py b/plugins/genshin/query_user/query_role/data_source.py
new file mode 100644
index 00000000..76b11265
--- /dev/null
+++ b/plugins/genshin/query_user/query_role/data_source.py
@@ -0,0 +1,249 @@
+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(char_data_list, _x)
+ 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.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"],
+ }
+ 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
diff --git a/plugins/genshin/query_user/query_role/draw_image.py b/plugins/genshin/query_user/query_role/draw_image.py
new file mode 100644
index 00000000..556e1cae
--- /dev/null
+++ b/plugins/genshin/query_user/query_role/draw_image.py
@@ -0,0 +1,530 @@
+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:用户头像
+ """
+ x = 450 if char_detailed_dict else 330
+ char_height = (
+ len(char_data_list) / 7
+ if len(char_data_list) % 7 == 0
+ else len(char_data_list) / 7 + 1
+ )
+ foot = BuildImage(1700, 87, background=image_path / "head.png")
+ head = BuildImage(1700, 87, background=image_path / "head.png")
+ head.rotate(180)
+ middle = BuildImage(
+ 1700, int(1600 + 200 + x * char_height), 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))
+ 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, _h = get_char_data_image(char_data_list, char_detailed_dict)
+ top_bk = BuildImage(user_image.w, user_image.h + home_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)
+ if A.h - top_bk.h - bar.h - _h > 200:
+ _h = A.h - top_bk.h - bar.h - _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.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: 家园列表
+ """
+ region = BuildImage(
+ 550, 1050, 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, 980), 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, 980), 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, 1050, 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),
+ )
+ 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: 角色武器
+ """
+ x = 420 if char_detailed_dict else 350
+ _h = x * int(
+ len(char_data_list) / 7
+ if len(char_data_list) % 7 == 0
+ else len(char_data_list) / 7 + 1
+ )
+ 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 += x
+ idx += 1
+ char_img = image_path / "chars" / f'{char["name"]}.png'
+ char_bk = BuildImage(
+ 270,
+ 500 if char_detailed_dict else 400,
+ 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
+ return region, _h
+
+
+async def init_image(char_data_list: List[Dict], char_detailed_dict: dict):
+ """
+ 下载头像
+ :param char_data_list: 角色列表
+ :param char_detailed_dict: 角色武器
+ """
+ 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
+ )
diff --git a/plugins/genshin/query_user/reset_today_query_user_data/__init__.py b/plugins/genshin/query_user/reset_today_query_user_data/__init__.py
new file mode 100644
index 00000000..0c2c24d0
--- /dev/null
+++ b/plugins/genshin/query_user/reset_today_query_user_data/__init__.py
@@ -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}")
+
diff --git a/plugins/genshin/query_user/utils/__init__.py b/plugins/genshin/query_user/utils/__init__.py
new file mode 100644
index 00000000..7cbd9ded
--- /dev/null
+++ b/plugins/genshin/query_user/utils/__init__.py
@@ -0,0 +1,35 @@
+from configs.config import Config
+import json
+import time
+import random
+import hashlib
+
+
+def _md5(text):
+ md5 = hashlib.md5()
+ md5.update(text.encode())
+ return md5.hexdigest()
+
+
+def get_ds(q: str = "", b: dict = None):
+ 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
+
+
+element_mastery = {
+ "anemo": "风",
+ "pyro": "火",
+ "geo": "岩",
+ "electro": "雷",
+ "cryo": "冰",
+ "hydro": "水",
+ "dendro": "草",
+ "none": "无",
+}
diff --git a/plugins/gold_redbag/__init__.py b/plugins/gold_redbag/__init__.py
index 2ffae551..c11b7437 100755
--- a/plugins/gold_redbag/__init__.py
+++ b/plugins/gold_redbag/__init__.py
@@ -115,6 +115,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,11 +138,11 @@ 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']
num = int(num)
if num > group_member_num:
diff --git a/plugins/gold_redbag/data_source.py b/plugins/gold_redbag/data_source.py
index 419cfe3b..06f96980 100755
--- a/plugins/gold_redbag/data_source.py
+++ b/plugins/gold_redbag/data_source.py
@@ -1,6 +1,6 @@
from models.bag_user import BagUser
from utils.utils import is_number, get_user_avatar
-from utils.image_utils import CreateImg
+from utils.image_utils import BuildImage
from configs.path_config import IMAGE_PATH
from .model import RedbagUser
import random
@@ -44,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)
@@ -61,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))
diff --git a/plugins/image_management/__init__.py b/plugins/image_management/__init__.py
index 81121c6e..9a4464db 100755
--- a/plugins/image_management/__init__.py
+++ b/plugins/image_management/__init__.py
@@ -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")
diff --git a/plugins/image_management/delete_image/__init__.py b/plugins/image_management/delete_image/__init__.py
index 45b08da3..c4e314fa 100755
--- a/plugins/image_management/delete_image/__init__.py
+++ b/plugins/image_management/delete_image/__init__.py
@@ -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:
diff --git a/plugins/image_management/move_image/__init__.py b/plugins/image_management/move_image/__init__.py
index 8522f213..1ae25012 100755
--- a/plugins/image_management/move_image/__init__.py
+++ b/plugins/image_management/move_image/__init__.py
@@ -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))
diff --git a/plugins/image_management/send_image/__init__.py b/plugins/image_management/send_image/__init__.py
index 04a9fa90..e352a717 100755
--- a/plugins/image_management/send_image/__init__.py
+++ b/plugins/image_management/send_image/__init__.py
@@ -6,6 +6,7 @@ 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
import random
@@ -16,7 +17,7 @@ try:
except ModuleNotFoundError:
import json
-__zx_plugin_name__ = "发送本地图库图片"
+__zx_plugin_name__ = "本地图库"
__plugin_usage__ = f"""
usage:
发送指定图库下的随机或指定id图片
@@ -37,49 +38,61 @@ __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)
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"]) + "/"
+ path = _path / cn2py(state["_prefix"]["raw_command"])
if state["_prefix"]["raw_command"] 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(state["_prefix"]["raw_command"])).exists():
+ path = Path(IMAGE_PATH) / cn2py(state["_prefix"]["raw_command"])
+ else:
+ path.mkdir(parents=True, exist_ok=True)
+ length = len(os.listdir(path))
if length == 0:
- logger.warning(f"图库 {path} 为空,调用取消!")
+ logger.warning(f'图库 {cn2py(state["_prefix"]["raw_command"])} 为空,调用取消!')
await send_img.finish("该图库中没有图片噢")
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(state['_prefix']['raw_command'])}:"
+ 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 +101,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(state['_prefix']['raw_command'])} 失败"
)
await send_img.finish(f"不想给你看Ov|")
diff --git a/plugins/image_management/upload_image/data_source.py b/plugins/image_management/upload_image/data_source.py
index 3ce0a399..1ac655ab 100755
--- a/plugins/image_management/upload_image/data_source.py
+++ b/plugins/image_management/upload_image/data_source.py
@@ -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"
)
diff --git a/plugins/luxun/__init__.py b/plugins/luxun/__init__.py
index d3e58e23..f988901b 100755
--- a/plugins/luxun/__init__.py
+++ b/plugins/luxun/__init__.py
@@ -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('太长了,鲁迅说不完...')
diff --git a/plugins/one_friend/__init__.py b/plugins/one_friend/__init__.py
index f1526bef..67dd0937 100755
--- a/plugins/one_friend/__init__.py
+++ b/plugins/one_friend/__init__.py
@@ -6,7 +6,7 @@ from nonebot.adapters.cqhttp import Bot, GroupMessageEvent
from utils.utils import get_message_text, get_message_at, get_user_avatar
from utils.message_builder import image
import re
-from utils.image_utils import CreateImg
+from utils.image_utils import BuildImage
__zx_plugin_name__ = "我有一个朋友"
__plugin_usage__ = """
@@ -60,13 +60,13 @@ async def _(bot: Bot, event: GroupMessageEvent, state: T_State):
msg = msg.replace("他", "我").replace("她", "我").replace("它", "我")
x = await get_user_avatar(qq)
if x:
- ava = CreateImg(200, 100, background=BytesIO(x))
+ ava = BuildImage(200, 100, background=BytesIO(x))
else:
- ava = CreateImg(200, 100, color=(0, 0, 0))
+ ava = BuildImage(200, 100, color=(0, 0, 0))
ava.circle()
- text = CreateImg(300, 30, font_size=30)
+ text = BuildImage(300, 30, font_size=30)
text.text((0, 0), user_name)
- A = CreateImg(700, 150, font_size=25, color="white")
+ A = BuildImage(700, 150, font_size=25, color="white")
A.paste(ava, (30, 25), True)
A.paste(text, (150, 38))
A.text((150, 85), msg, (125, 125, 125))
diff --git a/plugins/open_cases/__init__.py b/plugins/open_cases/__init__.py
index 155881d4..6639fe6e 100755
--- a/plugins/open_cases/__init__.py
+++ b/plugins/open_cases/__init__.py
@@ -18,6 +18,7 @@ from .open_cases_c import (
open_shilian_case,
)
from .utils import util_get_buff_price, util_get_buff_img, update_count_daily
+from configs.config import Config
__zx_plugin_name__ = "开箱"
__plugin_usage__ = """
@@ -84,6 +85,14 @@ __plugin_configs__ = {
"BUFF_PROXY": {"value": None, "help": "使用代理访问BUFF"},
}
+Config.add_plugin_config(
+ "_task",
+ "DEFAULT_OPEN_CASE_RESET_REMIND",
+ True,
+ help_="被动 每日开箱重置提醒 进群默认开关状态",
+ default_value=True,
+)
+
cases_name = ["狂牙大行动", "突围大行动", "命悬一线", "裂空", "光谱"]
cases_matcher_group = MatcherGroup(priority=5, permission=GROUP, block=True)
diff --git a/plugins/open_cases/open_cases_c.py b/plugins/open_cases/open_cases_c.py
index b8ff2fe6..0322dc23 100755
--- a/plugins/open_cases/open_cases_c.py
+++ b/plugins/open_cases/open_cases_c.py
@@ -10,7 +10,7 @@ import random
from .utils import get_price
from .models.buff_prices import BuffPrice
from PIL import Image
-from utils.image_utils import alpha2white_pil, CreateImg
+from utils.image_utils import alpha2white_pil, BuildImage
from configs.path_config import IMAGE_PATH
import asyncio
from utils.utils import cn2py
@@ -239,7 +239,7 @@ async def open_shilian_case(user_qq: int, group: int, case_name: str, num: int =
):
skin_name += "".join(i)
# img = image(skin_name, "cases/" + case, "png")
- wImg = CreateImg(200, 270, 200, 200)
+ wImg = BuildImage(200, 270, 200, 200)
wImg.paste(
alpha2white_pil(
Image.open(IMAGE_PATH + f"cases/{case}/{skin_name}.png").resize(
@@ -263,7 +263,7 @@ async def open_shilian_case(user_qq: int, group: int, case_name: str, num: int =
logger.warning(
f"USER {user_qq} GROUP {group} 开启{case_name}武器箱 {num} 次, 价格:{uplist[10]}, 数据更新失败"
)
- # markImg = CreateImg(1000, h, 200, 270)
+ # markImg = BuildImage(1000, h, 200, 270)
# for img in img_list:
# markImg.paste(img)
markImg = await asyncio.get_event_loop().run_in_executor(
@@ -284,7 +284,7 @@ async def open_shilian_case(user_qq: int, group: int, case_name: str, num: int =
def paste_markImg(h: int, img_list: list):
- markImg = CreateImg(1000, h, 200, 270)
+ markImg = BuildImage(1000, h, 200, 270)
for img in img_list:
markImg.paste(img)
return markImg
@@ -399,7 +399,7 @@ async def my_knifes_name(user_id: int, group: int):
def _pst_my_knife(w, h, knifes_list):
- A = CreateImg(w, h, 540, 600)
+ A = BuildImage(w, h, 540, 600)
for knife in knifes_list:
case = knife.split("||")[0]
knife = knife.split("||")[1]
@@ -415,7 +415,7 @@ def _pst_my_knife(w, h, knifes_list):
style=pypinyin.NORMAL,
):
skin_name += "".join(i)
- knife_img = CreateImg(470, 600, 470, 470, font_size=20)
+ knife_img = BuildImage(470, 600, 470, 470, font_size=20)
knife_img.paste(
alpha2white_pil(
Image.open(IMAGE_PATH + f"cases/{case}/{skin_name}.png").resize(
diff --git a/plugins/open_cases/utils.py b/plugins/open_cases/utils.py
index cb5b4d71..77313d51 100755
--- a/plugins/open_cases/utils.py
+++ b/plugins/open_cases/utils.py
@@ -8,10 +8,10 @@ import os
from services.log import logger
from utils.utils import get_bot
from asyncio.exceptions import TimeoutError
-import pypinyin
from nonebot.adapters.cqhttp.exception import ActionFailed
from configs.config import Config
from utils.manager import group_manager
+from .config import *
url = "https://buff.163.com/api/market/goods"
# proxies = 'http://49.75.59.242:3128'
diff --git a/plugins/parse_bilibili_json.py b/plugins/parse_bilibili_json.py
index 82172717..4811f63a 100755
--- a/plugins/parse_bilibili_json.py
+++ b/plugins/parse_bilibili_json.py
@@ -8,10 +8,11 @@ from nonebot.adapters.cqhttp.permission import GROUP
from bilibili_api import video
from utils.message_builder import image
from nonebot.adapters.cqhttp.exception import ActionFailed
-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
from utils.http_utils import AsyncHttpx
+from configs.config import Config
import asyncio
import time
from bilibili_api import settings
@@ -25,10 +26,18 @@ usage:
B站转发解析,解析b站分享信息,支持bv,bilibili链接,b站手机端转发卡片,cv,b23.tv,且5分钟内不解析相同url
""".strip()
__plugin_des__ = "B站转发解析"
-__plugin_type__ = ("被动相关",)
+__plugin_type__ = ("其他",)
__plugin_version__ = 0.1
__plugin_author__ = "HibiKier"
__plugin_task__ = {"bilibili_parse": "b站转发解析"}
+Config.add_plugin_config(
+ "_task",
+ "DEFAULT_BILIBILI_PARSE",
+ True,
+ help_="被动 B站转发解析 进群默认开关状态",
+ default_value=True,
+)
+
if get_local_proxy():
settings.proxy = get_local_proxy()
@@ -154,5 +163,5 @@ async def _(bot: Bot, event: GroupMessageEvent, state: T_State):
def resize(path: str):
- A = CreateImg(0, 0, background=path, ratio=0.5)
+ A = BuildImage(0, 0, background=path, ratio=0.5)
A.save(path)
diff --git a/plugins/pid_search.py b/plugins/pid_search.py
index 43bf19b8..251a4f53 100755
--- a/plugins/pid_search.py
+++ b/plugins/pid_search.py
@@ -69,6 +69,8 @@ async def _(bot: Bot, event: MessageEvent, state: T_State):
data = (await AsyncHttpx.get(url, params=params, timeout=5)).json()
except TimeoutError:
pass
+ except Exception as e:
+ await pid_search.finish(f"发生了一些错误..{type(e)}:{e}")
else:
if not data["width"] and not data["height"]:
await pid_search.finish(f"没有搜索到 PID:{pid} 的图片", at_sender=True)
diff --git a/plugins/pix_gallery/__init__.py b/plugins/pix_gallery/__init__.py
index 8e71f3b2..a846c5f0 100755
--- a/plugins/pix_gallery/__init__.py
+++ b/plugins/pix_gallery/__init__.py
@@ -57,6 +57,14 @@ Config.add_plugin_config(
default_value=10
)
+Config.add_plugin_config(
+ "pix",
+ "SHOW_INFO",
+ True,
+ help_="是否显示图片的基本信息,如PID等",
+ default_value=True
+)
+
nonebot.load_plugins("plugins/pix_gallery")
diff --git a/plugins/pix_gallery/data_source.py b/plugins/pix_gallery/data_source.py
index 37bb3fb8..d6116799 100755
--- a/plugins/pix_gallery/data_source.py
+++ b/plugins/pix_gallery/data_source.py
@@ -5,7 +5,7 @@ from asyncio.exceptions import TimeoutError
from .model.pixiv import Pixiv
from typing import List, Optional
from utils.utils import change_pixiv_image_links
-from utils.image_utils import CreateImg
+from utils.image_utils import BuildImage
from utils.http_utils import AsyncHttpx
from services.log import logger
from configs.config import Config
@@ -347,16 +347,16 @@ def gen_keyword_pic(
del img_data["_n_pid"]
del img_data["_n_uid"]
current_width = 0
- A = CreateImg(img_width, 1100)
+ A = BuildImage(img_width, 1100)
for x in list(img_data.keys()):
if img_data[x]["data"]:
- img = CreateImg(img_data[x]["width"] * 200, 1100, 200, 1100, font_size=40)
+ img = BuildImage(img_data[x]["width"] * 200, 1100, 200, 1100, font_size=40)
start_index = 0
end_index = 40
total_index = img_data[x]["width"] * 40
for _ in range(img_data[x]["width"]):
- tmp = CreateImg(198, 1100, font_size=20)
- text_img = CreateImg(198, 100, font_size=50)
+ tmp = BuildImage(198, 1100, font_size=20)
+ text_img = BuildImage(198, 100, font_size=50)
key_str = "\n".join(
[key for key in img_data[x]["data"][start_index:end_index]]
)
@@ -370,7 +370,7 @@ def gen_keyword_pic(
end_index = (
end_index + 40 if end_index + 40 <= total_index else total_index
)
- background_img = CreateImg(200, 1100, color="#FFE4C4")
+ background_img = BuildImage(200, 1100, color="#FFE4C4")
background_img.paste(tmp, (1, 1))
img.paste(background_img)
A.paste(img, (current_width, 0))
diff --git a/plugins/pix_gallery/pix.py b/plugins/pix_gallery/pix.py
index 04c1bfc0..ea283959 100755
--- a/plugins/pix_gallery/pix.py
+++ b/plugins/pix_gallery/pix.py
@@ -143,14 +143,17 @@ async def _(bot: Bot, event: MessageEvent, state: T_State):
uid = img.uid
_img = await get_image(img_url, event.user_id)
if _img:
- msg_id = await pix.send(
- Message(
- f"title:{title}\n"
- f"author:{author}\n"
- f"PID:{pid}\nUID:{uid}\n"
- f"{image(_img, 'temp')}"
+ if Config.get_config("pix", "SHOW_INFO"):
+ msg_id = await pix.send(
+ Message(
+ f"title:{title}\n"
+ f"author:{author}\n"
+ f"PID:{pid}\nUID:{uid}\n"
+ f"{image(_img, 'temp')}"
+ )
)
- )
+ else:
+ msg_id = await pix.send(image(_img, 'temp'))
logger.info(
f"(USER {event.user_id}, GROUP {event.group_id if isinstance(event, GroupMessageEvent) else 'private'})"
f" 查看PIX图库PID: {pid}"
diff --git a/plugins/poke/__init__.py b/plugins/poke/__init__.py
index 393981eb..b75606a9 100755
--- a/plugins/poke/__init__.py
+++ b/plugins/poke/__init__.py
@@ -16,7 +16,7 @@ usage:
戳一戳随机掉落语音或美图萝莉图
""".strip()
__plugin_des__ = "戳一戳发送语音美图萝莉图不美哉?"
-__plugin_type__ = ("被动相关",)
+__plugin_type__ = ("其他",)
__plugin_version__ = 0.1
__plugin_author__ = "HibiKier"
__plugin_settings__ = {
diff --git a/plugins/russian/data_source.py b/plugins/russian/data_source.py
index 35d12623..765cbaca 100755
--- a/plugins/russian/data_source.py
+++ b/plugins/russian/data_source.py
@@ -1,10 +1,10 @@
from .model import RussianUser
from typing import Optional
from utils.data_utils import init_rank
-from utils.image_utils import CreateMat
+from utils.image_utils import BuildMat
-async def rank(group_id: int, itype: str, num: int) -> Optional[CreateMat]:
+async def rank(group_id: int, itype: str, num: int) -> Optional[BuildMat]:
all_users = await RussianUser.get_all_user(group_id)
all_user_id = [user.user_qq for user in all_users]
if itype == 'win_rank':
diff --git a/plugins/send_setu_/send_setu/__init__.py b/plugins/send_setu_/send_setu/__init__.py
index 16b439b4..464626ab 100755
--- a/plugins/send_setu_/send_setu/__init__.py
+++ b/plugins/send_setu_/send_setu/__init__.py
@@ -98,6 +98,11 @@ __plugin_configs__ = {
"value": 10,
"help": "色图下载超时限制(秒)",
"default_value": 10
+ },
+ "SHOW_INFO": {
+ "value": True,
+ "help": "是否显示色图的基本信息,如PID等",
+ "default_value": True
}
}
Config.add_plugin_config(
diff --git a/plugins/send_setu_/send_setu/data_source.py b/plugins/send_setu_/send_setu/data_source.py
index cdc590ae..418813ed 100755
--- a/plugins/send_setu_/send_setu/data_source.py
+++ b/plugins/send_setu_/send_setu/data_source.py
@@ -183,13 +183,15 @@ def gen_message(setu_image: Setu, img_msg: bool = False) -> str:
title = setu_image.title
author = setu_image.author
pid = setu_image.pid
- return (
- f"id:{local_id}\n"
- f"title:{title}\n"
- f"author:{author}\n"
- f"PID:{pid}\n"
- f"{image(f'{local_id}', f'{r18_path if setu_image.is_r18 else path}') if img_msg else ''}"
- )
+ if Config.get_config("send_setu", "SHOW_INFO"):
+ return (
+ f"id:{local_id}\n"
+ f"title:{title}\n"
+ f"author:{author}\n"
+ f"PID:{pid}\n"
+ f"{image(f'{local_id}', f'{r18_path if setu_image.is_r18 else path}') if img_msg else ''}"
+ )
+ return f"{image(f'{local_id}', f'{r18_path if setu_image.is_r18 else path}') if img_msg else ''}"
# 罗翔老师!
diff --git a/plugins/shop/shop_handle/data_source.py b/plugins/shop/shop_handle/data_source.py
index 915839d4..0c4f4ac6 100755
--- a/plugins/shop/shop_handle/data_source.py
+++ b/plugins/shop/shop_handle/data_source.py
@@ -1,5 +1,5 @@
from ..models.goods_info import GoodsInfo
-from utils.image_utils import CreateImg
+from utils.image_utils import BuildImage
from utils.utils import is_number
from configs.path_config import IMAGE_PATH
from configs.config import Config
@@ -37,9 +37,9 @@ async def create_shop_help():
w = 1000
h = 400 + len(goods_lst) * 40
h = 1000 if h < 1000 else h
- shop_logo = CreateImg(100, 100, background=f"{IMAGE_PATH}/other/shop_text.png")
- shop = CreateImg(w, h, font_size=20)
- zhenxun_img = CreateImg(525, 581, background=f"{IMAGE_PATH}/zhenxun/toukan_2.png")
+ shop_logo = BuildImage(100, 100, background=f"{IMAGE_PATH}/other/shop_text.png")
+ shop = BuildImage(w, h, font_size=20)
+ zhenxun_img = BuildImage(525, 581, background=f"{IMAGE_PATH}/zhenxun/toukan_2.png")
shop.paste(zhenxun_img, (780, 100))
shop.paste(shop_logo, (450, 30), True)
shop.text(
diff --git a/plugins/sign_in/__init__.py b/plugins/sign_in/__init__.py
index cece39ab..5b578616 100755
--- a/plugins/sign_in/__init__.py
+++ b/plugins/sign_in/__init__.py
@@ -3,6 +3,7 @@ from .group_user_checkin import (
group_user_check,
group_impression_rank,
impression_rank,
+ check_in_all
)
from nonebot.typing import T_State
from nonebot.adapters.cqhttp import Bot, GroupMessageEvent
@@ -45,26 +46,18 @@ __plugin_settings__ = {
}
__plugin_cd_limit__ = {}
__plugin_configs__ = {
- "MAX_SIGN_GOLD": {
- "value": 200,
- "help": "签到好感度加成额外获得的最大金币数",
- "default_value": 200
- },
- "SIGN_CARD1_PROB": {
- "value": 0.2,
- "help": "签到好感度双倍加持卡Ⅰ掉落概率",
- "default_value": 0.2
- },
+ "MAX_SIGN_GOLD": {"value": 200, "help": "签到好感度加成额外获得的最大金币数", "default_value": 200},
+ "SIGN_CARD1_PROB": {"value": 0.2, "help": "签到好感度双倍加持卡Ⅰ掉落概率", "default_value": 0.2},
"SIGN_CARD2_PROB": {
"value": 0.09,
"help": "签到好感度双倍加持卡Ⅱ掉落概率",
- "default_value": 0.09
+ "default_value": 0.09,
},
"SIGN_CARD3_PROB": {
"value": 0.05,
"help": "签到好感度双倍加持卡Ⅲ掉落概率",
- "default_value": 0.05
- }
+ "default_value": 0.05,
+ },
}
@@ -98,6 +91,9 @@ async def _(bot: Bot, event: GroupMessageEvent, state: T_State):
await group_user_check_in(nickname, event.user_id, event.group_id),
at_sender=True,
)
+ if get_message_text(event.json()) == "all":
+ await check_in_all(nickname, event.user_id)
+
@my_sign.handle()
@@ -142,12 +138,12 @@ async def _(bot: Bot, event: GroupMessageEvent, state: T_State):
@scheduler.scheduled_job(
- 'interval',
+ "interval",
hours=1,
)
async def _():
try:
clear_sign_data_pic()
- logger.info('清理日常签到图片数据数据完成....')
+ logger.info("清理日常签到图片数据数据完成....")
except Exception as e:
- logger.error(f'清理日常签到图片数据数据失败..{type(e)}: {e}')
+ logger.error(f"清理日常签到图片数据数据失败..{type(e)}: {e}")
diff --git a/plugins/sign_in/group_user_checkin.py b/plugins/sign_in/group_user_checkin.py
index a811843f..12ec7aa0 100755
--- a/plugins/sign_in/group_user_checkin.py
+++ b/plugins/sign_in/group_user_checkin.py
@@ -4,7 +4,7 @@ from models.group_member_info import GroupInfoUser
from models.bag_user import BagUser
from configs.config import NICKNAME
from nonebot.adapters.cqhttp import MessageSegment
-from utils.image_utils import CreateImg, CreateMat
+from utils.image_utils import BuildImage, BuildMat
from services.db_context import db
from .utils import get_card, SIGN_TODAY_CARD_PATH
from typing import Optional
@@ -39,6 +39,26 @@ async def group_user_check_in(
return await _handle_check_in(nickname, user_qq, group, present) # ok
+async def check_in_all(nickname: str, user_qq: int):
+ """
+ 说明:
+ 签到所有群
+ 参数:
+ :param nickname: 昵称
+ :param user_qq: 用户qq
+ """
+ async with db.transaction():
+ present = datetime.now()
+ for u in await SignGroupUser.get_user_all_data(user_qq):
+ group = u.belonging_group
+ if not ((
+ u.checkin_time_last + timedelta(hours=8)
+ ).date() >= present.date() or f"{u}_{group}_sign_{datetime.now().date()}" in os.listdir(
+ SIGN_TODAY_CARD_PATH
+ )):
+ await _handle_check_in(nickname, user_qq, group, present)
+
+
async def _handle_check_in(
nickname: str, user_qq: int, group: int, present: datetime
) -> MessageSegment:
@@ -84,7 +104,7 @@ async def group_user_check(nickname: str, user_qq: int, group: int) -> MessageSe
return await get_card(user, nickname, None, gold, "", is_card_view=True)
-async def group_impression_rank(group: int, num: int) -> Optional[CreateMat]:
+async def group_impression_rank(group: int, num: int) -> Optional[BuildMat]:
user_qq_list, impression_list, _ = await SignGroupUser.get_all_impression(group)
return await init_rank("好感度排行榜", user_qq_list, impression_list, group, num)
@@ -141,9 +161,9 @@ async def _pst(users: list, impressions: list, groups: list):
count = math.ceil(lens / 33)
width = 10
idx = 0
- A = CreateImg(1740, 3300, color="#FFE4C4")
+ A = BuildImage(1740, 3300, color="#FFE4C4")
for _ in range(count):
- col_img = CreateImg(550, 3300, 550, 100, color="#FFE4C4")
+ col_img = BuildImage(550, 3300, 550, 100, color="#FFE4C4")
for _ in range(33 if int(lens / 33) >= 1 else lens % 33 - 1):
idx += 1
if idx > 100:
@@ -164,13 +184,13 @@ async def _pst(users: list, impressions: list, groups: list):
user_name = user_name if len(user_name) < 11 else user_name[:10] + "..."
ava = await get_user_avatar(user)
if ava:
- ava = CreateImg(
+ ava = BuildImage(
50, 50, background=BytesIO(ava)
)
else:
- ava = CreateImg(50, 50, color="white")
+ ava = BuildImage(50, 50, color="white")
ava.circle()
- bk = CreateImg(550, 100, color="#FFE4C4", font_size=30)
+ bk = BuildImage(550, 100, color="#FFE4C4", font_size=30)
font_w, font_h = bk.getsize(f"{idx}")
bk.text((5, int((100 - font_h) / 2)), f"{idx}.")
bk.paste(ava, (55, int((100 - 50) / 2)), True)
@@ -180,7 +200,7 @@ async def _pst(users: list, impressions: list, groups: list):
A.paste(col_img, (width, 0))
lens -= 33
width += 580
- W = CreateImg(1740, 3700, color="#FFE4C4", font_size=130)
+ W = BuildImage(1740, 3700, color="#FFE4C4", font_size=130)
W.paste(A, (0, 260))
font_w, font_h = W.getsize(f"{NICKNAME}的好感度总榜")
W.text((int((1740 - font_w) / 2), int((260 - font_h) / 2)), f"{NICKNAME}的好感度总榜")
diff --git a/plugins/sign_in/utils.py b/plugins/sign_in/utils.py
index 4f5a8fb0..25975713 100755
--- a/plugins/sign_in/utils.py
+++ b/plugins/sign_in/utils.py
@@ -12,7 +12,7 @@ from models.sign_group_user import SignGroupUser
from models.group_member_info import GroupInfoUser
from nonebot.adapters.cqhttp import MessageSegment
from utils.utils import get_user_avatar
-from utils.image_utils import CreateImg
+from utils.image_utils import BuildImage
from utils.message_builder import image
from configs.config import NICKNAME
from pathlib import Path
@@ -112,18 +112,18 @@ def _generate_card(
is_double: bool = False,
is_card_view: bool = False,
) -> MessageSegment:
- ava_bk = CreateImg(140, 140, is_alpha=True)
- ava_border = CreateImg(
+ ava_bk = BuildImage(140, 140, is_alpha=True)
+ ava_border = BuildImage(
140,
140,
background=SIGN_BORDER_PATH / "ava_border_01.png",
)
- ava = CreateImg(102, 102, background=ava_bytes)
+ ava = BuildImage(102, 102, background=ava_bytes)
ava.circle()
ava_bk.paste(ava, center_type="center")
ava_bk.paste(ava_border, alpha=True, center_type="center")
- info_img = CreateImg(250, 150, color=(255, 255, 255, 0), font_size=15)
+ info_img = BuildImage(250, 150, color=(255, 255, 255, 0), font_size=15)
level, next_impression, previous_impression = get_level_and_next_impression(
user.impression
)
@@ -131,8 +131,8 @@ def _generate_card(
info_img.text((0, 20), f"· {NICKNAME}对你的态度:{level2attitude[level]}")
info_img.text((0, 40), f"· 距离升级还差 {next_impression - user.impression:.2f} 好感度")
- bar_bk = CreateImg(220, 20, background=SIGN_RESOURCE_PATH / "bar_white.png")
- bar = CreateImg(220, 20, background=SIGN_RESOURCE_PATH / "bar.png")
+ bar_bk = BuildImage(220, 20, background=SIGN_RESOURCE_PATH / "bar_white.png")
+ bar = BuildImage(220, 20, background=SIGN_RESOURCE_PATH / "bar.png")
bar_bk.paste(
bar,
(
@@ -150,7 +150,7 @@ def _generate_card(
font_size = 30
if "好感度双倍加持卡" in gift:
font_size = 20
- gift_border = CreateImg(
+ gift_border = BuildImage(
270,
100,
background=SIGN_BORDER_PATH / "gift_border_02.png",
@@ -158,20 +158,20 @@ def _generate_card(
)
gift_border.text((0, 0), gift, center_type="center")
- bk = CreateImg(
+ bk = BuildImage(
876,
424,
background=SIGN_BACKGROUND_PATH
/ random.choice(os.listdir(SIGN_BACKGROUND_PATH)),
font_size=25,
)
- A = CreateImg(876, 274, background=SIGN_RESOURCE_PATH / "white.png")
- line = CreateImg(2, 180, color="black")
+ A = BuildImage(876, 274, background=SIGN_RESOURCE_PATH / "white.png")
+ line = BuildImage(2, 180, color="black")
A.transparent(2)
A.paste(ava_bk, (25, 80), True)
A.paste(line, (200, 70))
- nickname_img = CreateImg(
+ nickname_img = BuildImage(
0,
0,
plain_text=nickname,
@@ -184,7 +184,7 @@ def _generate_card(
uid = uid[:4] + " " + uid[4:8] + " " + uid[8:]
else:
uid = "XXXX XXXX XXXX"
- uid_img = CreateImg(
+ uid_img = BuildImage(
0,
0,
plain_text=f"UID: {uid}",
@@ -192,7 +192,7 @@ def _generate_card(
font_size=30,
font_color=(255, 255, 255),
)
- sign_day_img = CreateImg(
+ sign_day_img = BuildImage(
0,
0,
plain_text=f"{user.checkin_count}",
@@ -200,17 +200,17 @@ def _generate_card(
font_size=40,
font_color=(211, 64, 33),
)
- lik_text1_img = CreateImg(
+ lik_text1_img = BuildImage(
0, 0, plain_text="当前", color=(255, 255, 255, 0), font_size=20
)
- lik_text2_img = CreateImg(
+ lik_text2_img = BuildImage(
0,
0,
plain_text=f"好感度:{user.impression:.2f}",
color=(255, 255, 255, 0),
font_size=30,
)
- watermark = CreateImg(
+ watermark = BuildImage(
0,
0,
plain_text=f"{NICKNAME}@{datetime.now().year}",
@@ -218,15 +218,15 @@ def _generate_card(
font_size=15,
font_color=(155, 155, 155),
)
- today_data = CreateImg(300, 300, color=(255, 255, 255, 0), font_size=20)
+ today_data = BuildImage(300, 300, color=(255, 255, 255, 0), font_size=20)
if is_card_view:
- today_sign_text_img = CreateImg(
+ today_sign_text_img = BuildImage(
0, 0, plain_text="", color=(255, 255, 255, 0), font_size=30
)
if impression_list:
impression_list.sort(reverse=True)
index = impression_list.index(user.impression)
- rank_img = CreateImg(
+ rank_img = BuildImage(
0,
0,
plain_text=f"* 此群好感排名第 {index + 1} 位",
@@ -247,7 +247,7 @@ def _generate_card(
_type = "view"
else:
A.paste(gift_border, (570, 140), True)
- today_sign_text_img = CreateImg(
+ today_sign_text_img = BuildImage(
0, 0, plain_text="今日签到", color=(255, 255, 255, 0), font_size=30
)
if is_double:
@@ -262,7 +262,7 @@ def _generate_card(
hour = current_date.hour
minute = current_date.minute
second = current_date.second
- data_img = CreateImg(
+ data_img = BuildImage(
0,
0,
plain_text=f"时间:{data} {weekdays[week]} {hour}:{minute}:{second}",
@@ -296,14 +296,14 @@ def generate_progress_bar_pic():
bg_2 = (254, 1, 254)
bg_1 = (0, 245, 246)
- bk = CreateImg(1000, 50, is_alpha=True)
- img_x = CreateImg(50, 50, color=bg_2)
+ bk = BuildImage(1000, 50, is_alpha=True)
+ img_x = BuildImage(50, 50, color=bg_2)
img_x.circle()
img_x.crop((25, 0, 50, 50))
- img_y = CreateImg(50, 50, color=bg_1)
+ img_y = BuildImage(50, 50, color=bg_1)
img_y.circle()
img_y.crop((0, 0, 25, 50))
- A = CreateImg(950, 50)
+ A = BuildImage(950, 50)
width, height = A.size
step_r = (bg_2[0] - bg_1[0]) / width
@@ -321,12 +321,12 @@ def generate_progress_bar_pic():
bk.paste(img_x, (975, 0), True)
bk.save(SIGN_RESOURCE_PATH / "bar.png")
- A = CreateImg(950, 50)
- bk = CreateImg(1000, 50, is_alpha=True)
- img_x = CreateImg(50, 50)
+ A = BuildImage(950, 50)
+ bk = BuildImage(1000, 50, is_alpha=True)
+ img_x = BuildImage(50, 50)
img_x.circle()
img_x.crop((25, 0, 50, 50))
- img_y = CreateImg(50, 50)
+ img_y = BuildImage(50, 50)
img_y.circle()
img_y.crop((0, 0, 25, 50))
bk.paste(img_y, (0, 0), True)
diff --git a/plugins/statistics/statistics_handle.py b/plugins/statistics/statistics_handle.py
index f44308c8..e54e8a8f 100755
--- a/plugins/statistics/statistics_handle.py
+++ b/plugins/statistics/statistics_handle.py
@@ -5,7 +5,7 @@ from nonebot.typing import T_State
from pathlib import Path
from configs.path_config import DATA_PATH, IMAGE_PATH
from utils.utils import get_message_text
-from utils.image_utils import CreateMat
+from utils.image_utils import BuildMat
from utils.message_builder import image
from utils.manager import plugins2settings_manager
import asyncio
@@ -202,7 +202,7 @@ async def generate_statistics_img(
except KeyError:
count.append(0)
week_lst = ["7" if i == "0" else i for i in week_lst]
- bar_graph = CreateMat(
+ bar_graph = BuildMat(
y=count,
mat_type="line",
title=f"{name} 周 {plugin} 功能调用统计【为7天统计】",
@@ -226,7 +226,7 @@ async def generate_statistics_img(
day_lst.append(i)
count = [data[str(day_lst[i])][plugin] for i in range(30)]
day_lst = [str(x + 1) for x in day_lst]
- bar_graph = CreateMat(
+ bar_graph = BuildMat(
y=count,
mat_type="line",
title=f"{name} 月 {plugin} 功能调用统计【为30天统计】",
@@ -246,12 +246,12 @@ async def generate_statistics_img(
return bar_graph.pic2bs4()
-async def init_bar_graph(data: dict, title: str) -> CreateMat:
+async def init_bar_graph(data: dict, title: str) -> BuildMat:
return await asyncio.get_event_loop().run_in_executor(None, _init_bar_graph, data, title)
-def _init_bar_graph(data: dict, title: str) -> CreateMat:
- bar_graph = CreateMat(
+def _init_bar_graph(data: dict, title: str) -> BuildMat:
+ bar_graph = BuildMat(
y=[data[x] for x in data.keys() if data[x] != 0],
mat_type="barh",
title=title,
diff --git a/plugins/update_picture.py b/plugins/update_picture.py
index f197a5a3..13b16ad5 100755
--- a/plugins/update_picture.py
+++ b/plugins/update_picture.py
@@ -9,7 +9,7 @@ from nonebot.typing import T_State
from utils.utils import get_message_imgs
from pathlib import Path
from utils.utils import is_number, get_message_text
-from utils.image_utils import CreateImg, pic2b64
+from utils.image_utils import BuildImage, pic2b64
from configs.config import NICKNAME
from utils.http_utils import AsyncHttpx
import cv2
@@ -83,7 +83,7 @@ for i in range(len(method_list)):
method_oper.append(method_list[i])
method_oper.append(str(i + 1))
-update_img_help = CreateImg(960, 700, font_size=24)
+update_img_help = BuildImage(960, 700, font_size=24)
update_img_help.text((10, 10), __plugin_usage__)
update_img_help.save(IMAGE_PATH + "update_img_help.png")
diff --git a/plugins/white2black_image.py b/plugins/white2black_image.py
index 9a5bf7f2..40637ba5 100755
--- a/plugins/white2black_image.py
+++ b/plugins/white2black_image.py
@@ -4,7 +4,7 @@ from nonebot import on_command
from utils.utils import get_message_imgs, get_message_text, is_chinese
from utils.message_builder import image
from configs.path_config import TEMP_PATH
-from utils.image_utils import CreateImg
+from utils.image_utils import BuildImage
from services.log import logger
from utils.http_utils import AsyncHttpx
from pathlib import Path
@@ -57,12 +57,12 @@ async def _(bot: Bot, event: MessageEvent, state: T_State):
):
await w2b_img.finish("下载图片失败...请稍后再试...")
msg = await get_translate(msg)
- w2b = CreateImg(0, 0, background=Path(TEMP_PATH) / f"{event.user_id}_w2b.png")
+ w2b = BuildImage(0, 0, background=Path(TEMP_PATH) / f"{event.user_id}_w2b.png")
w2b.convert("L")
msg_sp = msg.split("<|>")
w, h = w2b.size
add_h, font_size = init_h_font_size(h)
- bg = CreateImg(w, h + add_h, color="black", font_size=font_size)
+ bg = BuildImage(w, h + add_h, color="black", font_size=font_size)
bg.paste(w2b)
chinese_msg = formalization_msg(msg)
if not bg.check_font_size(chinese_msg):
@@ -84,7 +84,7 @@ async def _(bot: Bot, event: MessageEvent, state: T_State):
)
-def centered_text(img: CreateImg, text: str, add_h: int):
+def centered_text(img: BuildImage, text: str, add_h: int):
top_h = img.h - add_h + (img.h / 100)
bottom_h = img.h - (img.h / 100)
text_sp = text.split("<|>")
diff --git a/plugins/word_bank/word_hanlde.py b/plugins/word_bank/word_hanlde.py
index 881209fa..5bbcb77c 100644
--- a/plugins/word_bank/word_hanlde.py
+++ b/plugins/word_bank/word_hanlde.py
@@ -59,11 +59,11 @@ show_word = on_command("显示词条", aliases={"查看词条"}, priority=5, blo
@add_word.handle()
async def _(bot: Bot, event: GroupMessageEvent, state: T_State):
msg = str(event.get_message()).strip()
- r = re.search(r"^问(.+)\s?答(.*)", msg)
+ r = re.search(r"^问(.+)\s?答([\s\S]*)", msg)
if not r:
await add_word.finish("未检测到词条问题...")
problem = r.group(1).strip()
- answer = r.group(2).strip()
+ answer = msg.split('答', maxsplit=1)[-1]
if not answer:
await add_word.finish("未检测到词条回答...")
idx = 0
diff --git a/plugins/yiqing/__init__.py b/plugins/yiqing/__init__.py
index 8c8926fe..29674c2e 100755
--- a/plugins/yiqing/__init__.py
+++ b/plugins/yiqing/__init__.py
@@ -54,4 +54,4 @@ async def _(bot: Bot, event: MessageEvent, state: T_State):
f"{event.group_id if isinstance(event, GroupMessageEvent) else 'private'}) 查询疫情失败"
)
else:
- await yiqing.send(f"{NICKNAME}只支持国内的疫情查询喔...")
+ await yiqing.send(f"{NICKNAME}没有查到{msg}的疫情查询...")
diff --git a/resources/img/genshin/genshin_card/chars_ava/10000002.png b/resources/img/genshin/genshin_card/chars_ava/10000002.png
new file mode 100644
index 00000000..97caa00a
Binary files /dev/null and b/resources/img/genshin/genshin_card/chars_ava/10000002.png differ
diff --git a/resources/img/genshin/genshin_card/chars_ava/10000003.png b/resources/img/genshin/genshin_card/chars_ava/10000003.png
new file mode 100644
index 00000000..175185c2
Binary files /dev/null and b/resources/img/genshin/genshin_card/chars_ava/10000003.png differ
diff --git a/resources/img/genshin/genshin_card/chars_ava/10000005.png b/resources/img/genshin/genshin_card/chars_ava/10000005.png
new file mode 100644
index 00000000..bba7aa28
Binary files /dev/null and b/resources/img/genshin/genshin_card/chars_ava/10000005.png differ
diff --git a/resources/img/genshin/genshin_card/chars_ava/10000006.png b/resources/img/genshin/genshin_card/chars_ava/10000006.png
new file mode 100644
index 00000000..6b023e88
Binary files /dev/null and b/resources/img/genshin/genshin_card/chars_ava/10000006.png differ
diff --git a/resources/img/genshin/genshin_card/chars_ava/10000007.png b/resources/img/genshin/genshin_card/chars_ava/10000007.png
new file mode 100644
index 00000000..c05d9c3b
Binary files /dev/null and b/resources/img/genshin/genshin_card/chars_ava/10000007.png differ
diff --git a/resources/img/genshin/genshin_card/chars_ava/10000014.png b/resources/img/genshin/genshin_card/chars_ava/10000014.png
new file mode 100644
index 00000000..a77ade17
Binary files /dev/null and b/resources/img/genshin/genshin_card/chars_ava/10000014.png differ
diff --git a/resources/img/genshin/genshin_card/chars_ava/10000015.png b/resources/img/genshin/genshin_card/chars_ava/10000015.png
new file mode 100644
index 00000000..ed537b67
Binary files /dev/null and b/resources/img/genshin/genshin_card/chars_ava/10000015.png differ
diff --git a/resources/img/genshin/genshin_card/chars_ava/10000016.png b/resources/img/genshin/genshin_card/chars_ava/10000016.png
new file mode 100644
index 00000000..3e5611b5
Binary files /dev/null and b/resources/img/genshin/genshin_card/chars_ava/10000016.png differ
diff --git a/resources/img/genshin/genshin_card/chars_ava/10000020.png b/resources/img/genshin/genshin_card/chars_ava/10000020.png
new file mode 100644
index 00000000..e7162ea4
Binary files /dev/null and b/resources/img/genshin/genshin_card/chars_ava/10000020.png differ
diff --git a/resources/img/genshin/genshin_card/chars_ava/10000021.png b/resources/img/genshin/genshin_card/chars_ava/10000021.png
new file mode 100644
index 00000000..a147187b
Binary files /dev/null and b/resources/img/genshin/genshin_card/chars_ava/10000021.png differ
diff --git a/resources/img/genshin/genshin_card/chars_ava/10000022.png b/resources/img/genshin/genshin_card/chars_ava/10000022.png
new file mode 100644
index 00000000..1882eaa1
Binary files /dev/null and b/resources/img/genshin/genshin_card/chars_ava/10000022.png differ
diff --git a/resources/img/genshin/genshin_card/chars_ava/10000023.png b/resources/img/genshin/genshin_card/chars_ava/10000023.png
new file mode 100644
index 00000000..8c6baa8c
Binary files /dev/null and b/resources/img/genshin/genshin_card/chars_ava/10000023.png differ
diff --git a/resources/img/genshin/genshin_card/chars_ava/10000024.png b/resources/img/genshin/genshin_card/chars_ava/10000024.png
new file mode 100644
index 00000000..5f1e9023
Binary files /dev/null and b/resources/img/genshin/genshin_card/chars_ava/10000024.png differ
diff --git a/resources/img/genshin/genshin_card/chars_ava/10000025.png b/resources/img/genshin/genshin_card/chars_ava/10000025.png
new file mode 100644
index 00000000..a70ce00e
Binary files /dev/null and b/resources/img/genshin/genshin_card/chars_ava/10000025.png differ
diff --git a/resources/img/genshin/genshin_card/chars_ava/10000026.png b/resources/img/genshin/genshin_card/chars_ava/10000026.png
new file mode 100644
index 00000000..65054966
Binary files /dev/null and b/resources/img/genshin/genshin_card/chars_ava/10000026.png differ
diff --git a/resources/img/genshin/genshin_card/chars_ava/10000027.png b/resources/img/genshin/genshin_card/chars_ava/10000027.png
new file mode 100644
index 00000000..0a772f9c
Binary files /dev/null and b/resources/img/genshin/genshin_card/chars_ava/10000027.png differ
diff --git a/resources/img/genshin/genshin_card/chars_ava/10000029.png b/resources/img/genshin/genshin_card/chars_ava/10000029.png
new file mode 100644
index 00000000..bf77d740
Binary files /dev/null and b/resources/img/genshin/genshin_card/chars_ava/10000029.png differ
diff --git a/resources/img/genshin/genshin_card/chars_ava/10000030.png b/resources/img/genshin/genshin_card/chars_ava/10000030.png
new file mode 100644
index 00000000..a2722639
Binary files /dev/null and b/resources/img/genshin/genshin_card/chars_ava/10000030.png differ
diff --git a/resources/img/genshin/genshin_card/chars_ava/10000031.png b/resources/img/genshin/genshin_card/chars_ava/10000031.png
new file mode 100644
index 00000000..2cbd7952
Binary files /dev/null and b/resources/img/genshin/genshin_card/chars_ava/10000031.png differ
diff --git a/resources/img/genshin/genshin_card/chars_ava/10000032.png b/resources/img/genshin/genshin_card/chars_ava/10000032.png
new file mode 100644
index 00000000..30726c17
Binary files /dev/null and b/resources/img/genshin/genshin_card/chars_ava/10000032.png differ
diff --git a/resources/img/genshin/genshin_card/chars_ava/10000033.png b/resources/img/genshin/genshin_card/chars_ava/10000033.png
new file mode 100644
index 00000000..6aef4cc4
Binary files /dev/null and b/resources/img/genshin/genshin_card/chars_ava/10000033.png differ
diff --git a/resources/img/genshin/genshin_card/chars_ava/10000034.png b/resources/img/genshin/genshin_card/chars_ava/10000034.png
new file mode 100644
index 00000000..2cfa0575
Binary files /dev/null and b/resources/img/genshin/genshin_card/chars_ava/10000034.png differ
diff --git a/resources/img/genshin/genshin_card/chars_ava/10000035.png b/resources/img/genshin/genshin_card/chars_ava/10000035.png
new file mode 100644
index 00000000..f0765f04
Binary files /dev/null and b/resources/img/genshin/genshin_card/chars_ava/10000035.png differ
diff --git a/resources/img/genshin/genshin_card/chars_ava/10000036.png b/resources/img/genshin/genshin_card/chars_ava/10000036.png
new file mode 100644
index 00000000..a49e462d
Binary files /dev/null and b/resources/img/genshin/genshin_card/chars_ava/10000036.png differ
diff --git a/resources/img/genshin/genshin_card/chars_ava/10000037.png b/resources/img/genshin/genshin_card/chars_ava/10000037.png
new file mode 100644
index 00000000..34bf0ca8
Binary files /dev/null and b/resources/img/genshin/genshin_card/chars_ava/10000037.png differ
diff --git a/resources/img/genshin/genshin_card/chars_ava/10000038.png b/resources/img/genshin/genshin_card/chars_ava/10000038.png
new file mode 100644
index 00000000..e923d5e8
Binary files /dev/null and b/resources/img/genshin/genshin_card/chars_ava/10000038.png differ
diff --git a/resources/img/genshin/genshin_card/chars_ava/10000039.png b/resources/img/genshin/genshin_card/chars_ava/10000039.png
new file mode 100644
index 00000000..d2d99dfc
Binary files /dev/null and b/resources/img/genshin/genshin_card/chars_ava/10000039.png differ
diff --git a/resources/img/genshin/genshin_card/chars_ava/10000041.png b/resources/img/genshin/genshin_card/chars_ava/10000041.png
new file mode 100644
index 00000000..eb2595c6
Binary files /dev/null and b/resources/img/genshin/genshin_card/chars_ava/10000041.png differ
diff --git a/resources/img/genshin/genshin_card/chars_ava/10000042.png b/resources/img/genshin/genshin_card/chars_ava/10000042.png
new file mode 100644
index 00000000..a4fb807c
Binary files /dev/null and b/resources/img/genshin/genshin_card/chars_ava/10000042.png differ
diff --git a/resources/img/genshin/genshin_card/chars_ava/10000043.png b/resources/img/genshin/genshin_card/chars_ava/10000043.png
new file mode 100644
index 00000000..0b50bcfd
Binary files /dev/null and b/resources/img/genshin/genshin_card/chars_ava/10000043.png differ
diff --git a/resources/img/genshin/genshin_card/chars_ava/10000044.png b/resources/img/genshin/genshin_card/chars_ava/10000044.png
new file mode 100644
index 00000000..5c234088
Binary files /dev/null and b/resources/img/genshin/genshin_card/chars_ava/10000044.png differ
diff --git a/resources/img/genshin/genshin_card/chars_ava/10000045.png b/resources/img/genshin/genshin_card/chars_ava/10000045.png
new file mode 100644
index 00000000..8a8808dc
Binary files /dev/null and b/resources/img/genshin/genshin_card/chars_ava/10000045.png differ
diff --git a/resources/img/genshin/genshin_card/chars_ava/10000046.png b/resources/img/genshin/genshin_card/chars_ava/10000046.png
new file mode 100644
index 00000000..bd8fd03a
Binary files /dev/null and b/resources/img/genshin/genshin_card/chars_ava/10000046.png differ
diff --git a/resources/img/genshin/genshin_card/chars_ava/10000047.png b/resources/img/genshin/genshin_card/chars_ava/10000047.png
new file mode 100644
index 00000000..c6b19bc6
Binary files /dev/null and b/resources/img/genshin/genshin_card/chars_ava/10000047.png differ
diff --git a/resources/img/genshin/genshin_card/chars_ava/10000048.png b/resources/img/genshin/genshin_card/chars_ava/10000048.png
new file mode 100644
index 00000000..d98790e6
Binary files /dev/null and b/resources/img/genshin/genshin_card/chars_ava/10000048.png differ
diff --git a/resources/img/genshin/genshin_card/chars_ava/10000049.png b/resources/img/genshin/genshin_card/chars_ava/10000049.png
new file mode 100644
index 00000000..f14cd9c9
Binary files /dev/null and b/resources/img/genshin/genshin_card/chars_ava/10000049.png differ
diff --git a/resources/img/genshin/genshin_card/chars_ava/10000050.png b/resources/img/genshin/genshin_card/chars_ava/10000050.png
new file mode 100644
index 00000000..7e0d43b3
Binary files /dev/null and b/resources/img/genshin/genshin_card/chars_ava/10000050.png differ
diff --git a/resources/img/genshin/genshin_card/chars_ava/10000051.png b/resources/img/genshin/genshin_card/chars_ava/10000051.png
new file mode 100644
index 00000000..35dcc104
Binary files /dev/null and b/resources/img/genshin/genshin_card/chars_ava/10000051.png differ
diff --git a/resources/img/genshin/genshin_card/chars_ava/10000052.png b/resources/img/genshin/genshin_card/chars_ava/10000052.png
new file mode 100644
index 00000000..4c7247de
Binary files /dev/null and b/resources/img/genshin/genshin_card/chars_ava/10000052.png differ
diff --git a/resources/img/genshin/genshin_card/chars_ava/10000053.png b/resources/img/genshin/genshin_card/chars_ava/10000053.png
new file mode 100644
index 00000000..8f5d584f
Binary files /dev/null and b/resources/img/genshin/genshin_card/chars_ava/10000053.png differ
diff --git a/resources/img/genshin/genshin_card/chars_ava/10000054.png b/resources/img/genshin/genshin_card/chars_ava/10000054.png
new file mode 100644
index 00000000..cef2d5e0
Binary files /dev/null and b/resources/img/genshin/genshin_card/chars_ava/10000054.png differ
diff --git a/resources/img/genshin/genshin_card/chars_ava/10000056.png b/resources/img/genshin/genshin_card/chars_ava/10000056.png
new file mode 100644
index 00000000..b39125d6
Binary files /dev/null and b/resources/img/genshin/genshin_card/chars_ava/10000056.png differ
diff --git a/resources/img/genshin/genshin_card/chars_ava/10000062.png b/resources/img/genshin/genshin_card/chars_ava/10000062.png
new file mode 100644
index 00000000..7e0d43b3
Binary files /dev/null and b/resources/img/genshin/genshin_card/chars_ava/10000062.png differ
diff --git a/resources/img/genshin/genshin_card/cover.png b/resources/img/genshin/genshin_card/cover.png
new file mode 100644
index 00000000..63638182
Binary files /dev/null and b/resources/img/genshin/genshin_card/cover.png differ
diff --git a/resources/img/genshin/genshin_card/element.png b/resources/img/genshin/genshin_card/element.png
new file mode 100644
index 00000000..0b1491bd
Binary files /dev/null and b/resources/img/genshin/genshin_card/element.png differ
diff --git a/resources/img/genshin/genshin_card/head.png b/resources/img/genshin/genshin_card/head.png
new file mode 100644
index 00000000..e72b4584
Binary files /dev/null and b/resources/img/genshin/genshin_card/head.png differ
diff --git a/resources/img/genshin/genshin_card/homes/lock.png b/resources/img/genshin/genshin_card/homes/lock.png
new file mode 100644
index 00000000..eca4c6d5
Binary files /dev/null and b/resources/img/genshin/genshin_card/homes/lock.png differ
diff --git a/resources/img/genshin/genshin_card/logo/璃月.png b/resources/img/genshin/genshin_card/logo/璃月.png
new file mode 100644
index 00000000..fc2754b8
Binary files /dev/null and b/resources/img/genshin/genshin_card/logo/璃月.png differ
diff --git a/resources/img/genshin/genshin_card/logo/稻妻.png b/resources/img/genshin/genshin_card/logo/稻妻.png
new file mode 100644
index 00000000..086a720f
Binary files /dev/null and b/resources/img/genshin/genshin_card/logo/稻妻.png differ
diff --git a/resources/img/genshin/genshin_card/logo/蒙德.png b/resources/img/genshin/genshin_card/logo/蒙德.png
new file mode 100644
index 00000000..0272b710
Binary files /dev/null and b/resources/img/genshin/genshin_card/logo/蒙德.png differ
diff --git a/resources/img/genshin/genshin_card/logo/龙脊雪山.png b/resources/img/genshin/genshin_card/logo/龙脊雪山.png
new file mode 100644
index 00000000..77e5afeb
Binary files /dev/null and b/resources/img/genshin/genshin_card/logo/龙脊雪山.png differ
diff --git a/resources/img/genshin/genshin_card/middle.png b/resources/img/genshin/genshin_card/middle.png
new file mode 100644
index 00000000..2b9f12b6
Binary files /dev/null and b/resources/img/genshin/genshin_card/middle.png differ
diff --git a/resources/img/genshin/genshin_memo/resin.png b/resources/img/genshin/genshin_memo/resin.png
new file mode 100644
index 00000000..7eef3780
Binary files /dev/null and b/resources/img/genshin/genshin_memo/resin.png differ
diff --git a/resources/img/genshin/genshin_memo/resin_discount.png b/resources/img/genshin/genshin_memo/resin_discount.png
new file mode 100644
index 00000000..810e0af0
Binary files /dev/null and b/resources/img/genshin/genshin_memo/resin_discount.png differ
diff --git a/resources/img/genshin/genshin_memo/task.png b/resources/img/genshin/genshin_memo/task.png
new file mode 100644
index 00000000..a3b415ef
Binary files /dev/null and b/resources/img/genshin/genshin_memo/task.png differ
diff --git a/update_info.json b/update_info.json
index 94dde42f..54aedb5a 100644
--- a/update_info.json
+++ b/update_info.json
@@ -5,8 +5,9 @@
"basic_plugins",
"utils",
"services",
- "configs/utils"
+ "configs/utils",
+ "bot.py"
],
- "add_file": [],
+ "add_file": ["resources/img/genshin/genshin_card", "resources/img/genshin/genshin_memo"],
"delete_file": []
}
diff --git a/utils/browser.py b/utils/browser.py
index f5563ce6..944ba379 100755
--- a/utils/browser.py
+++ b/utils/browser.py
@@ -22,7 +22,7 @@ async def init(**kwargs) -> Optional[Browser]:
_browser = await browser.chromium.launch(**kwargs)
return _browser
except NotImplementedError:
- logger.warning("win环境下 初始化playwright失败....请替换环境至linux")
+ logger.warning("win环境下 初始化playwright失败,相关功能将被限制....")
return None
diff --git a/utils/data_utils.py b/utils/data_utils.py
index cc4a7baf..ce7f9a33 100755
--- a/utils/data_utils.py
+++ b/utils/data_utils.py
@@ -1,5 +1,5 @@
from models.group_member_info import GroupInfoUser
-from utils.image_utils import CreateMat
+from utils.image_utils import BuildMat
from configs.path_config import IMAGE_PATH
from typing import List, Union
import asyncio
@@ -8,7 +8,7 @@ import os
async def init_rank(
title: str, all_user_id: List[int], all_user_data: List[int], group_id: int, total_count: int = 10
-) -> CreateMat:
+) -> BuildMat:
"""
说明:
初始化通用的数据排行榜
@@ -43,14 +43,14 @@ async def init_rank(
def _init_rank_graph(
title: str, _uname_lst: List[str], _num_lst: List[Union[int, float]]
-) -> CreateMat:
+) -> BuildMat:
"""
生成排行榜统计图
:param title: 排行榜标题
:param _uname_lst: 用户名列表
:param _num_lst: 数值列表
"""
- image = CreateMat(
+ image = BuildMat(
y=_num_lst,
y_name="* 可以在命令后添加数字来指定排行人数 至多 50 *",
mat_type="barh",
diff --git a/utils/http_utils.py b/utils/http_utils.py
index 630c5d7c..460b4832 100644
--- a/utils/http_utils.py
+++ b/utils/http_utils.py
@@ -33,17 +33,20 @@ class AsyncHttpx:
proxy: Dict[str, str] = None,
allow_redirects: bool = True,
timeout: Optional[int] = 30,
+ **kwargs,
) -> Response:
"""
- Get
- :param url: url
- :param params: params
- :param headers: 请求头
- :param cookies: cookies
- :param use_proxy: 使用默认代理
- :param proxy: 指定代理
- :param allow_redirects: allow_redirects
- :param timeout: 超时时间
+ 说明:
+ Get
+ 参数:
+ :param url: url
+ :param params: params
+ :param headers: 请求头
+ :param cookies: cookies
+ :param use_proxy: 使用默认代理
+ :param proxy: 指定代理
+ :param allow_redirects: allow_redirects
+ :param timeout: 超时时间
"""
if not headers:
headers = get_user_agent()
@@ -56,6 +59,7 @@ class AsyncHttpx:
cookies=cookies,
allow_redirects=allow_redirects,
timeout=timeout,
+ **kwargs
)
@classmethod
@@ -74,21 +78,24 @@ class AsyncHttpx:
cookies: Optional[Dict[str, str]] = None,
allow_redirects: bool = True,
timeout: Optional[int] = 30,
+ **kwargs,
) -> Response:
"""
- Post
- :param url: url
- :param data: data
- :param content: content
- :param files: files
- :param use_proxy: 是否默认代理
- :param proxy: 指定代理
- :param json: json
- :param params: params
- :param headers: 请求头
- :param cookies: cookies
- :param allow_redirects: allow_redirects
- :param timeout: 超时时间
+ 说明:
+ Post
+ 参数:
+ :param url: url
+ :param data: data
+ :param content: content
+ :param files: files
+ :param use_proxy: 是否默认代理
+ :param proxy: 指定代理
+ :param json: json
+ :param params: params
+ :param headers: 请求头
+ :param cookies: cookies
+ :param allow_redirects: allow_redirects
+ :param timeout: 超时时间
"""
if not headers:
headers = get_user_agent()
@@ -105,6 +112,7 @@ class AsyncHttpx:
cookies=cookies,
allow_redirects=allow_redirects,
timeout=timeout,
+ **kwargs,
)
@classmethod
@@ -119,17 +127,20 @@ class AsyncHttpx:
headers: Optional[Dict[str, str]] = None,
cookies: Optional[Dict[str, str]] = None,
timeout: Optional[int] = 30,
+ **kwargs,
) -> bool:
"""
- 下载文件
- :param url: url
- :param path: 存储路径
- :param params: params
- :param use_proxy: 使用代理
- :param proxy: 指定代理
- :param headers: 请求头
- :param cookies: cookies
- :param timeout: 超时时间
+ 说明:
+ 下载文件
+ 参数:
+ :param url: url
+ :param path: 存储路径
+ :param params: params
+ :param use_proxy: 使用代理
+ :param proxy: 指定代理
+ :param headers: 请求头
+ :param cookies: cookies
+ :param timeout: 超时时间
"""
if isinstance(path, str):
path = Path(path)
@@ -146,18 +157,19 @@ class AsyncHttpx:
use_proxy=use_proxy,
proxy=proxy,
timeout=timeout,
+ **kwargs,
)
).content
async with aiofiles.open(path, "wb") as wf:
await wf.write(content)
- logger.info(f"下载图片 {url} 成功.. Path:{path.absolute()}")
+ logger.info(f"下载 {url} 成功.. Path:{path.absolute()}")
return True
except (TimeoutError, ConnectTimeout):
pass
else:
- logger.error(f"下载图片 {url} 下载超时.. Path:{path.absolute()}")
+ logger.error(f"下载 {url} 下载超时.. Path:{path.absolute()}")
except Exception as e:
- logger.error(f"下载图片 {url} 未知错误 {type(e)}:{e}.. Path:{path.absolute()}")
+ logger.error(f"下载 {url} 未知错误 {type(e)}:{e}.. Path:{path.absolute()}")
return False
@classmethod
@@ -173,19 +185,21 @@ class AsyncHttpx:
headers: Optional[Dict[str, str]] = None,
cookies: Optional[Dict[str, str]] = None,
timeout: Optional[int] = 30,
+ **kwargs,
) -> List[bool]:
"""
- 分组同时下载文件
- :param url_list: url列表
- :param path_list: 存储路径列表
- :param limit_async_number: 限制同时请求数量
- :param params: params
- :param use_proxy: 使用代理
- :param proxy: 指定代理
- :param headers: 请求头
- :param cookies: cookies
- :param timeout: 超时时间
- :return:
+ 说明:
+ 分组同时下载文件
+ 参数:
+ :param url_list: url列表
+ :param path_list: 存储路径列表
+ :param limit_async_number: 限制同时请求数量
+ :param params: params
+ :param use_proxy: 使用代理
+ :param proxy: 指定代理
+ :param headers: 请求头
+ :param cookies: cookies
+ :param timeout: 超时时间
"""
if n := len(url_list) != len(path_list):
raise UrlPathNumberNotEqual(
@@ -222,7 +236,8 @@ class AsyncHttpx:
cookies=cookies,
use_proxy=use_proxy,
timeout=timeout,
- proxy=proxy
+ proxy=proxy,
+ ** kwargs,
)
)
)
@@ -235,14 +250,16 @@ class AsyncHttpx:
class AsyncPlaywright:
@classmethod
- async def _new_page(cls, user_agent: Optional[str] = None) -> Page:
+ async def _new_page(cls, user_agent: Optional[str] = None, **kwargs) -> Page:
"""
- 获取一个新页面
- :param user_agent: 请求头
+ 说明:
+ 获取一个新页面
+ 参数:
+ :param user_agent: 请求头
"""
browser = await get_browser()
if browser:
- return await browser.new_page(user_agent=user_agent)
+ return await browser.new_page(user_agent=user_agent, **kwargs)
raise BrowserIsNone("获取Browser失败...")
@classmethod
@@ -255,17 +272,20 @@ class AsyncPlaywright:
Literal["domcontentloaded", "load", "networkidle"]
] = "networkidle",
referer: str = None,
+ **kwargs
) -> Optional[Page]:
"""
- goto
- :param url: 网址
- :param timeout: 超时限制
- :param wait_until: 等待类型
- :param referer:
+ 说明:
+ goto
+ 参数:
+ :param url: 网址
+ :param timeout: 超时限制
+ :param wait_until: 等待类型
+ :param referer:
"""
page = None
try:
- page = await cls._new_page()
+ page = await cls._new_page(**kwargs)
await page.goto(url, timeout=timeout, wait_until=wait_until, referer=referer)
return page
except Exception as e:
@@ -288,17 +308,20 @@ class AsyncPlaywright:
] = "networkidle",
timeout: float = None,
type_: Literal["jpeg", "png"] = None,
+ **kwargs
) -> Optional[MessageSegment]:
"""
- 截图,该方法仅用于简单快捷截图,复杂截图请操作 page
- :param url: 网址
- :param path: 存储路径
- :param element: 元素选择
- :param sleep: 延迟截取
- :param viewport_size: 窗口大小
- :param wait_until: 等待类型
- :param timeout: 超时限制
- :param type_: 保存类型
+ 说明:
+ 截图,该方法仅用于简单快捷截图,复杂截图请操作 page
+ 参数:
+ :param url: 网址
+ :param path: 存储路径
+ :param element: 元素选择
+ :param sleep: 延迟截取
+ :param viewport_size: 窗口大小
+ :param wait_until: 等待类型
+ :param timeout: 超时限制
+ :param type_: 保存类型
"""
page = None
if viewport_size is None:
@@ -306,7 +329,7 @@ class AsyncPlaywright:
if isinstance(path, str):
path = Path(path)
try:
- page = await cls.goto(url, wait_until=wait_until)
+ page = await cls.goto(url, wait_until=wait_until, **kwargs)
await page.set_viewport_size(viewport_size)
if sleep:
await asyncio.sleep(sleep)
diff --git a/utils/image_utils.py b/utils/image_utils.py
index 50096e9f..ed04e603 100755
--- a/utils/image_utils.py
+++ b/utils/image_utils.py
@@ -1,3 +1,4 @@
+import asyncio
from configs.path_config import IMAGE_PATH, FONT_PATH
from PIL import Image, ImageFile, ImageDraw, ImageFont, ImageFilter
from imagehash import ImageHash
@@ -135,7 +136,7 @@ def is_valid(file: str) -> bool:
return valid
-class CreateImg:
+class BuildImage:
"""
快捷生成图片与操作图片的工具类
"""
@@ -219,10 +220,34 @@ class CreateImg:
if plain_text:
fill = font_color if font_color else (0, 0, 0)
self.text((0, 0), plain_text, fill)
+ try:
+ self.loop = asyncio.get_event_loop()
+ except RuntimeError:
+ new_loop = asyncio.new_event_loop()
+ asyncio.set_event_loop(new_loop)
+ self.loop = asyncio.get_event_loop()
+
+ async def apaste(
+ self,
+ img: "BuildImage" or Image,
+ pos: Tuple[int, int] = None,
+ alpha: bool = False,
+ center_type: Optional[Literal["center", "by_height", "by_width"]] = None,
+ ):
+ """
+ 说明:
+ 异步 贴图
+ 参数:
+ :param img: 已打开的图片文件,可以为 BuildImage 或 Image
+ :param pos: 贴图位置(左上角)
+ :param alpha: 图片背景是否为透明
+ :param center_type: 居中类型,可能的值 center: 完全居中,by_width: 水平居中,by_height: 垂直居中
+ """
+ await self.loop.run_in_executor(None, self.paste, img, pos, alpha, center_type)
def paste(
self,
- img: "CreateImg" or Image,
+ img: "BuildImage" or Image,
pos: Tuple[int, int] = None,
alpha: bool = False,
center_type: Optional[Literal["center", "by_height", "by_width"]] = None,
@@ -231,7 +256,7 @@ class CreateImg:
说明:
贴图
参数:
- :param img: 已打开的图片文件,可以为 CreateImg 或 Image
+ :param img: 已打开的图片文件,可以为 BuildImage 或 Image
:param pos: 贴图位置(左上角)
:param alpha: 图片背景是否为透明
:param center_type: 居中类型,可能的值 center: 完全居中,by_width: 水平居中,by_height: 垂直居中
@@ -254,7 +279,7 @@ class CreateImg:
width = pos[0]
height = int((self.h - img.h) / 2)
pos = (width, height)
- if isinstance(img, CreateImg):
+ if isinstance(img, BuildImage):
img = img.markImg
if self.current_w == self.w:
self.current_w = 0
@@ -280,6 +305,18 @@ class CreateImg:
"""
return self.font.getsize(msg)
+ async def apoint(
+ self, pos: Tuple[int, int], fill: Optional[Tuple[int, int, int]] = None
+ ):
+ """
+ 说明:
+ 异步 绘制多个或单独的像素
+ 参数:
+ :param pos: 坐标
+ :param fill: 填错颜色
+ """
+ await self.loop.run_in_executor(None, self.point, pos, fill)
+
def point(self, pos: Tuple[int, int], fill: Optional[Tuple[int, int, int]] = None):
"""
说明:
@@ -290,6 +327,24 @@ class CreateImg:
"""
self.draw.point(pos, fill=fill)
+ async def aellipse(
+ self,
+ pos: Tuple[int, int, int, int],
+ fill: Optional[Tuple[int, int, int]] = None,
+ outline: Optional[Tuple[int, int, int]] = None,
+ width: int = 1,
+ ):
+ """
+ 说明:
+ 异步 绘制圆
+ 参数:
+ :param pos: 坐标范围
+ :param fill: 填充颜色
+ :param outline: 描线颜色
+ :param width: 描线宽度
+ """
+ await self.loop.run_in_executor(None, self.ellipse, pos, fill, outline, width)
+
def ellipse(
self,
pos: Tuple[int, int, int, int],
@@ -308,6 +363,24 @@ class CreateImg:
"""
self.draw.ellipse(pos, fill, outline, width)
+ async def atext(
+ self,
+ pos: Tuple[int, int],
+ text: str,
+ fill: Tuple[int, int, int] = (0, 0, 0),
+ center_type: Optional[Literal["center", "by_height", "by_width"]] = None,
+ ):
+ """
+ 说明:
+ 异步 在图片上添加文字
+ 参数:
+ :param pos: 文字位置
+ :param text: 文字内容
+ :param fill: 文字颜色
+ :param center_type: 居中类型,可能的值 center: 完全居中,by_width: 水平居中,by_height: 垂直居中
+ """
+ await self.loop.run_in_executor(None, self.text, pos, text, fill, center_type)
+
def text(
self,
pos: Tuple[int, int],
@@ -343,6 +416,15 @@ class CreateImg:
pos = (w, h)
self.draw.text(pos, text, fill=fill, font=self.font)
+ async def asave(self, path: Union[str, Path]):
+ """
+ 说明:
+ 异步 保存图片
+ 参数:
+ :param path: 图片路径
+ """
+ await self.loop.run_in_executor(None, self.save, path)
+
def save(self, path: Union[str, Path]):
"""
说明:
@@ -361,6 +443,17 @@ class CreateImg:
"""
self.markImg.show(self.markImg)
+ async def aresize(self, ratio: float = 0, w: int = 0, h: int = 0):
+ """
+ 说明:
+ 异步 压缩图片
+ 参数:
+ :param ratio: 压缩倍率
+ :param w: 压缩图片宽度至 w
+ :param h: 压缩图片高度至 h
+ """
+ await self.loop.run_in_executor(None, self.resize, ratio, w, h)
+
def resize(self, ratio: float = 0, w: int = 0, h: int = 0):
"""
说明:
@@ -380,6 +473,15 @@ class CreateImg:
self.size = self.w, self.h
self.draw = ImageDraw.Draw(self.markImg)
+ async def acrop(self, box: Tuple[int, int, int, int]):
+ """
+ 说明:
+ 异步 裁剪图片
+ 参数:
+ :param box: 左上角坐标,右下角坐标 (left, upper, right, lower)
+ """
+ await self.loop.run_in_executor(None, self.crop, box)
+
def crop(self, box: Tuple[int, int, int, int]):
"""
说明:
@@ -401,6 +503,16 @@ class CreateImg:
"""
return self.font.getsize(word)[0] > self.w
+ async def atransparent(self, alpha_ratio: float = 1, n: int = 0):
+ """
+ 说明:
+ 异步 图片透明化
+ 参数:
+ :param alpha_ratio: 透明化程度
+ :param n: 透明化大小内边距
+ """
+ await self.loop.run_in_executor(None, self.transparent, alpha_ratio, n)
+
def transparent(self, alpha_ratio: float = 1, n: int = 0):
"""
说明:
@@ -421,7 +533,7 @@ class CreateImg:
def pic2bs4(self) -> str:
"""
说明:
- CreateImg 转 base64
+ BuildImage 转 base64
"""
buf = BytesIO()
self.markImg.save(buf, format="PNG")
@@ -437,6 +549,24 @@ class CreateImg:
"""
self.markImg = self.markImg.convert(type_)
+ async def arectangle(
+ self,
+ xy: Tuple[int, int, int, int],
+ fill: Optional[Tuple[int, int, int]] = None,
+ outline: str = None,
+ width: int = 1,
+ ):
+ """
+ 说明:
+ 异步 画框
+ 参数:
+ :param xy: 坐标
+ :param fill: 填充颜色
+ :param outline: 轮廓颜色
+ :param width: 线宽
+ """
+ await self.loop.run_in_executor(None, self.rectangle, xy, fill, outline, width)
+
def rectangle(
self,
xy: Tuple[int, int, int, int],
@@ -455,6 +585,54 @@ class CreateImg:
"""
self.draw.rectangle(xy, fill, outline, width)
+ async def apolygon(
+ self,
+ xy: List[Tuple[int, int]],
+ fill: Tuple[int, int, int] = (0, 0, 0),
+ outline: int = 1,
+ ):
+ """
+ 说明:
+ 异步 画多边形
+ 参数:
+ :param xy: 坐标
+ :param fill: 颜色
+ :param outline: 线宽
+ """
+ await self.loop.run_in_executor(None, self.polygon, xy, fill, outline)
+
+ def polygon(
+ self,
+ xy: List[Tuple[int, int]],
+ fill: Tuple[int, int, int] = (0, 0, 0),
+ outline: int = 1,
+ ):
+ """
+ 说明:
+ 画多边形
+ 参数:
+ :param xy: 坐标
+ :param fill: 颜色
+ :param outline: 线宽
+ """
+ self.draw.polygon(xy, fill, outline)
+
+ async def aline(
+ self,
+ xy: Tuple[int, int, int, int],
+ fill: Optional[Tuple[int, int, int]] = None,
+ width: int = 1,
+ ):
+ """
+ 说明:
+ 异步 画线
+ 参数:
+ :param xy: 坐标
+ :param fill: 填充
+ :param width: 线宽
+ """
+ await self.loop.run_in_executor(None, self.line, xy, fill, width)
+
def line(
self,
xy: Tuple[int, int, int, int],
@@ -471,10 +649,17 @@ class CreateImg:
"""
self.draw.line(xy, fill, width)
+ async def acircle(self):
+ """
+ 说明:
+ 异步 将 BuildImage 图片变为圆形
+ """
+ await self.loop.run_in_executor(None, self.circle)
+
def circle(self):
"""
说明:
- 将 CreateImg 图片变为圆形
+ 将 BuildImage 图片变为圆形
"""
self.convert("RGBA")
r2 = min(self.w, self.h)
@@ -494,6 +679,15 @@ class CreateImg:
pim_b[i - (r - r3), j - (r - r3)] = pim_a[i, j]
self.markImg = imb
+ async def acircle_corner(self, radii: int = 30):
+ """
+ 说明:
+ 异步 矩形四角变圆
+ 参数:
+ :param radii: 半径
+ """
+ await self.loop.run_in_executor(None, self.circle_corner, radii)
+
def circle_corner(self, radii: int = 30):
"""
说明:
@@ -516,6 +710,16 @@ class CreateImg:
alpha.paste(circle.crop((0, radii, radii, radii * 2)), (0, h - radii))
self.markImg.putalpha(alpha)
+ async def arotate(self, angle: int, expand: bool = False):
+ """
+ 说明:
+ 异步 旋转图片
+ 参数:
+ :param angle: 角度
+ :param expand: 放大图片适应角度
+ """
+ await self.loop.run_in_executor(None, self.rotate, angle, expand)
+
def rotate(self, angle: int, expand: bool = False):
"""
说明:
@@ -526,6 +730,15 @@ class CreateImg:
"""
self.markImg = self.markImg.rotate(angle, expand=expand)
+ async def atranspose(self, angle: int):
+ """
+ 说明:
+ 异步 旋转图片(包括边框)
+ 参数:
+ :param angle: 角度
+ """
+ await self.loop.run_in_executor(None, self.transpose, angle)
+
def transpose(self, angle: int):
"""
说明:
@@ -535,11 +748,23 @@ class CreateImg:
"""
self.markImg.transpose(angle)
+ async def afilter(self, filter_: str, aud: int = None):
+ """
+ 说明:
+ 异步 图片变化
+ 参数:
+ :param filter_: 变化效果
+ :param aud: 利率
+ """
+ await self.loop.run_in_executor(None, self.filter, filter_, aud)
+
def filter(self, filter_: str, aud: int = None):
"""
- 图片变化
- :param filter_: 变化效果
- :param aud: 利率
+ 说明:
+ 图片变化
+ 参数:
+ :param filter_: 变化效果
+ :param aud: 利率
"""
_x = None
if filter_ == "GaussianBlur": # 高斯模糊
@@ -564,9 +789,9 @@ class CreateImg:
self.markImg = self.markImg.getchannel(type_)
-class CreateMat:
+class BuildMat:
"""
- 针对 折线图/柱状图,基于 CreateImg 编写的 非常难用的 自定义画图工具
+ 针对 折线图/柱状图,基于 BuildImage 编写的 非常难用的 自定义画图工具
目前仅支持 正整数
"""
@@ -592,7 +817,7 @@ class CreateMat:
):
"""
说明:
- 初始化 CreateMat
+ 初始化 BuildMat
参数:
:param y: 坐标值
:param mat_type: 图像类型 可能的值:[line]: 折线图,[bar]: 柱状图,[barh]: 横向柱状图
@@ -802,7 +1027,7 @@ class CreateMat:
:param y: 坐标点
:param display_num: 显示该点的值
"""
- _black_point = CreateImg(7, 7, color=random.choice(self.bar_color))
+ _black_point = BuildImage(7, 7, color=random.choice(self.bar_color))
_black_point.circle()
x_interval = self._x_interval
current_w = self.padding_w + x_interval
@@ -887,7 +1112,7 @@ class CreateMat:
if i != len(y):
bar_color = random.choice(self.bar_color)
if is_barh:
- A = CreateImg(
+ A = BuildImage(
int(y[i] * self._p * self._deviation),
self._bar_width,
color=bar_color,
@@ -900,7 +1125,7 @@ class CreateMat:
),
)
else:
- A = CreateImg(
+ A = BuildImage(
self._bar_width,
int(y[i] * self._p * self._deviation),
color=bar_color,
@@ -925,7 +1150,7 @@ class CreateMat:
y_index: List[Union[str, int, float]] = None,
font_size: Optional[int] = None,
is_grid: bool = False,
- ) -> CreateImg:
+ ) -> BuildImage:
"""
说明:
初始化图像,生成xy轴
@@ -940,15 +1165,15 @@ class CreateMat:
padding_h = self.padding_h
line_length = self.line_length
background = random.choice(self.background) if self.background else None
- A = CreateImg(
+ A = BuildImage(
self.w, self.h, font_size=font_size, font=self.font, background=background
)
if background:
- _tmp = CreateImg(self.w, self.h)
+ _tmp = BuildImage(self.w, self.h)
_tmp.transparent(2)
A.paste(_tmp, alpha=True)
if self.title:
- title = CreateImg(
+ title = BuildImage(
0,
0,
plain_text=self.title,
@@ -985,14 +1210,14 @@ class CreateMat:
y_index = tmp
_interval = self._y_interval
current_w = padding_w + _interval
- _text_font = CreateImg(0, 0, font_size=self.font_size, font=self.font)
+ _text_font = BuildImage(0, 0, font_size=self.font_size, font=self.font)
_grid = self.line_length if is_grid else 10
x_rotate_height = 0
for _x in x_index:
- _p = CreateImg(1, _grid, color="#a9a9a9")
+ _p = BuildImage(1, _grid, color="#a9a9a9")
A.paste(_p, (current_w, padding_h + line_length - _grid))
w = int(_text_font.getsize(f"{_x}")[0] / 2)
- text = CreateImg(
+ text = BuildImage(
0,
0,
plain_text=f"{_x}",
@@ -1006,13 +1231,13 @@ class CreateMat:
x_rotate_height = text.h
_interval = self._x_interval if self.mat_type == "barh" else self._y_interval
current_h = padding_h + line_length - _interval
- _text_font = CreateImg(0, 0, font_size=self.font_size, font=self.font)
+ _text_font = BuildImage(0, 0, font_size=self.font_size, font=self.font)
for _y in y_index:
- _p = CreateImg(_grid, 1, color="#a9a9a9")
+ _p = BuildImage(_grid, 1, color="#a9a9a9")
A.paste(_p, (padding_w + 2, current_h))
w, h = _text_font.getsize(f"{_y}")
h = int(h / 2)
- text = CreateImg(
+ text = BuildImage(
0,
0,
plain_text=f"{_y}",
@@ -1022,7 +1247,7 @@ class CreateMat:
)
idx = 0
while text.size[0] > self.padding_w - 10 and idx < 3:
- text = CreateImg(
+ text = BuildImage(
0,
0,
plain_text=f"{_y}",
diff --git a/utils/manager/data_class.py b/utils/manager/data_class.py
index 8e78d913..7fcadb52 100755
--- a/utils/manager/data_class.py
+++ b/utils/manager/data_class.py
@@ -19,7 +19,11 @@ class StaticData:
if file.exists():
with open(file, "r", encoding="utf8") as f:
if file.name.endswith("json"):
- self._data: dict = json.load(f)
+ try:
+ self._data: dict = json.load(f)
+ except ValueError:
+ if f.read().strip():
+ raise ValueError(f"{file} 文件加载错误,请检查文件内容格式.")
elif file.name.endswith("yaml"):
self._data = yaml.load(f)
diff --git a/utils/manager/group_manager.py b/utils/manager/group_manager.py
index 332be9fb..b450c403 100755
--- a/utils/manager/group_manager.py
+++ b/utils/manager/group_manager.py
@@ -10,10 +10,14 @@ Config.add_plugin_config(
"group_manager", "DEFAULT_GROUP_LEVEL", 5, help_="默认群权限", default_value=5
)
+Config.add_plugin_config(
+ "group_manager", "DEFAULT_GROUP_BOT_STATUS", True, help_="默认进群总开关状态", default_value=True
+)
+
class GroupManager(StaticData):
"""
- 群权限 | 功能 | 聊天时间 管理器
+ 群权限 | 功能 | 总开关 | 聊天时间 管理器
"""
def __init__(self, file: Path):
@@ -45,6 +49,41 @@ class GroupManager(StaticData):
"""
self._set_plugin_status(module, "unblock", group_id)
+ def turn_on_group_bot_status(self, group_id: int):
+ """
+ 说明:
+ 开启群bot开关
+ 参数:
+ :param group_id: 群号
+ """
+ self._set_group_bot_status(group_id, True)
+
+ def shutdown_group_bot_status(self, group_id: int):
+ """
+ 说明:
+ 关闭群bot开关
+ 参数:
+ :param group_id: 群号
+ """
+ self._set_group_bot_status(group_id, False)
+
+ def check_group_bot_status(self, group_id: int) -> bool:
+ """
+ 说明:
+ 检查群聊bot总开关状态
+ 参数:
+ :param group_id: 说明
+ """
+ group_id = str(group_id)
+ if not self._data["group_manager"].get(group_id):
+ self._init_group(group_id)
+ if self._data["group_manager"][group_id].get("status") is None:
+ default_group_bot_status = Config.get_config("group_manager", "DEFAULT_GROUP_BOT_STATUS")
+ if default_group_bot_status:
+ default_group_bot_status = True
+ self._data["group_manager"][group_id]["status"] = default_group_bot_status
+ return self._data["group_manager"][group_id]["status"]
+
def set_group_level(self, group_id: int, level: int):
"""
说明:
@@ -125,8 +164,10 @@ class GroupManager(StaticData):
def delete_group(self, group_id: int):
"""
- 删除群配置
- :param group_id: 群号
+ 说明:
+ 删除群配置
+ 参数:
+ :param group_id: 群号
"""
if group_id in self._data["group_manager"]:
del self._data["group_manager"][str(group_id)]
@@ -136,25 +177,31 @@ class GroupManager(StaticData):
async def open_group_task(self, group_id: int, task: str):
"""
- 开启群被动技能
- :param group_id: 群号
- :param task: 被动技能名称
+ 说明:
+ 开启群被动技能
+ 参数:
+ :param group_id: 群号
+ :param task: 被动技能名称
"""
await self._set_group_task_status(group_id, task, True)
async def close_group_task(self, group_id: int, task: str):
"""
- 关闭群被动技能
- :param group_id: 群号
- :param task: 被动技能名称
+ 说明:
+ 关闭群被动技能
+ 参数:
+ :param group_id: 群号
+ :param task: 被动技能名称
"""
await self._set_group_task_status(group_id, task, False)
async def check_group_task_status(self, group_id: int, task: str) -> bool:
"""
- 查看群被动技能状态
- :param group_id: 群号
- :param task: 被动技能名称
+ 说明:
+ 查看群被动技能状态
+ 参数:
+ :param group_id: 群号
+ :param task: 被动技能名称
"""
group_id = str(group_id)
if (
@@ -167,14 +214,17 @@ class GroupManager(StaticData):
def get_task_data(self) -> Dict[str, str]:
"""
- 获取所有被动任务
+ 说明:
+ 获取所有被动任务
"""
return self._task
async def group_task_status(self, group_id: int) -> str:
"""
- 查看群被全部动技能状态
- :param group_id: 群号
+ 说明:
+ 查看群被全部动技能状态
+ 参数:
+ :param group_id: 群号
"""
x = "[群被动技能]:\n"
group_id = str(group_id)
@@ -186,10 +236,12 @@ class GroupManager(StaticData):
async def _set_group_task_status(self, group_id: int, task: str, status: bool):
"""
- 管理群被动技能状态
- :param group_id: 群号
- :param task: 被动技能
- :param status: 状态
+ 说明:
+ 管理群被动技能状态
+ 参数:
+ :param group_id: 群号
+ :param task: 被动技能
+ :param status: 状态
"""
group_id = str(group_id)
if not self._data["group_manager"].get(group_id):
@@ -205,7 +257,8 @@ class GroupManager(StaticData):
async def init_group_task(self, group_id: Optional[Union[int, str]] = None):
"""
- 初始化群聊 被动技能 状态
+ 说明:
+ 初始化群聊 被动技能 状态
"""
if not self._task:
for matcher in get_matchers():
@@ -238,7 +291,8 @@ class GroupManager(StaticData):
):
self._data["group_manager"][group_id]["group_task_status"][
task
- ] = True
+ ] = Config.get_config('_task', f'DEFAULT_{task}', default=True)
+ print(task, Config.get_config('_task', f'DEFAULT_{task}'))
for task in list(
self._data["group_manager"][group_id]["group_task_status"]
):
@@ -271,7 +325,7 @@ class GroupManager(StaticData):
else:
if module in self._data["group_manager"][group_id]["close_plugins"]:
self._data["group_manager"][group_id]["close_plugins"].remove(module)
- self.save()
+ self.save()
def _init_group(self, group_id: str):
"""
@@ -281,18 +335,37 @@ class GroupManager(StaticData):
:param group_id: 群号
"""
default_group_level = Config.get_config("group_manager", "DEFAULT_GROUP_LEVEL")
- if not default_group_level:
+ if default_group_level is None:
default_group_level = 5
+ default_group_bot_status = Config.get_config("group_manager", "DEFAULT_GROUP_BOT_STATUS")
+ if default_group_bot_status:
+ default_group_bot_status = True
if not self._data["group_manager"].get(group_id):
self._data["group_manager"][group_id] = {
"level": default_group_level,
+ "status": default_group_bot_status,
"close_plugins": [],
"group_task_status": {},
}
+ def _set_group_bot_status(self, group_id: Union[int, str], status: bool):
+ """
+ 说明:
+ 设置群聊bot总开关
+ 参数:
+ :param group_id: 群号
+ :param status: 开关状态
+ """
+ group_id = str(group_id)
+ if not self._data["group_manager"].get(group_id):
+ self._init_group(group_id)
+ self._data["group_manager"][group_id]["status"] = status
+ self.save()
+
def get_super_old_data(self) -> Optional[dict]:
"""
- 获取旧数据,平时使用请不要调用
+ 说明:
+ 获取旧数据,平时使用请不要调用
"""
if self._data["super"].get("close_plugins"):
_x = self._data["super"].get("close_plugins")
diff --git a/utils/manager/requests_manager.py b/utils/manager/requests_manager.py
index 1842bbd3..9085520c 100755
--- a/utils/manager/requests_manager.py
+++ b/utils/manager/requests_manager.py
@@ -3,7 +3,7 @@ from nonebot.adapters.cqhttp import Bot
from nonebot.adapters.cqhttp.exception import ActionFailed
from services.log import logger
from typing import Optional
-from utils.image_utils import CreateImg
+from utils.image_utils import BuildImage
from utils.utils import get_user_avatar
from pathlib import Path
from io import BytesIO
@@ -128,27 +128,27 @@ class RequestManager(StaticData):
comment = data[id_]["comment"] if type_ == "private" else ""
from_ = data[id_]["from"]
sex = data[id_]["sex"]
- ava = CreateImg(
+ ava = BuildImage(
80, 80, background=BytesIO(await get_user_avatar(data[id_]["id"]))
)
ava.circle()
- age_bk = CreateImg(
+ age_bk = BuildImage(
len(str(age)) * 10 - 5,
15,
color="#04CAF7" if sex == "male" else "#F983C1",
)
age_bk.text((3, 1), f"{age}", fill=(255, 255, 255))
- x = CreateImg(
+ x = BuildImage(
90, 32, font_size=15, color="#EEEFF4", font="HYWenHei-85W.ttf"
)
x.text((0, 0), "同意/拒绝", center_type="center")
x.circle_corner(10)
- A = CreateImg(500, 100, font_size=24, font="msyh.ttf")
+ A = BuildImage(500, 100, font_size=24, font="msyh.ttf")
A.paste(ava, (15, 0), alpha=True, center_type="by_height")
A.text((120, 15), nickname)
A.paste(age_bk, (120, 50), True)
A.paste(
- CreateImg(
+ BuildImage(
200,
0,
font_size=12,
@@ -160,7 +160,7 @@ class RequestManager(StaticData):
)
if type_ == "private":
A.paste(
- CreateImg(
+ BuildImage(
200,
0,
font_size=12,
@@ -172,7 +172,7 @@ class RequestManager(StaticData):
)
else:
A.paste(
- CreateImg(
+ BuildImage(
200,
0,
font_size=12,
@@ -184,7 +184,7 @@ class RequestManager(StaticData):
)
A.paste(x, (380, 35), True)
A.paste(
- CreateImg(
+ BuildImage(
0,
0,
plain_text=f"id:{id_}",
@@ -195,10 +195,10 @@ class RequestManager(StaticData):
True,
)
img_list.append(A)
- A = CreateImg(500, len(img_list) * 100, 500, 100)
+ A = BuildImage(500, len(img_list) * 100, 500, 100)
for img in img_list:
A.paste(img)
- bk = CreateImg(A.w, A.h + 50, color="#F8F9FB", font_size=20)
+ bk = BuildImage(A.w, A.h + 50, color="#F8F9FB", font_size=20)
bk.paste(A, (0, 50))
bk.text(
(15, 13), "好友请求" if type_ == "private" else "群聊请求", fill=(140, 140, 143)
diff --git a/utils/manager/resources_manager.py b/utils/manager/resources_manager.py
index c517003b..07c02e13 100755
--- a/utils/manager/resources_manager.py
+++ b/utils/manager/resources_manager.py
@@ -69,11 +69,11 @@ class ResourcesManager(StaticData):
for module in self._data.keys():
for source_path in self._data[module].keys():
move_path = Path(self._data[module][source_path])
- source_path = Path(source_path)
- file_name = source_path.name
- move_path = move_path / file_name
- move_path.mkdir(exist_ok=True, parents=True)
try:
+ source_path = Path(source_path)
+ file_name = source_path.name
+ move_path = move_path / file_name
+ move_path.mkdir(exist_ok=True, parents=True)
if source_path.exists():
if move_path.exists():
shutil.rmtree(str(move_path.absolute()), ignore_errors=True)