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)