update 0.0.7.0

This commit is contained in:
HibiKier 2021-12-16 11:16:28 +08:00
parent b892617824
commit 2653f67193
141 changed files with 2693 additions and 547 deletions

158
README.md
View File

@ -20,7 +20,7 @@
## 未完成的文档 ## 未完成的文档
[传送门](https://hibikier.github.io/zhenxun_bot/) <h1>[传送门](https://hibikier.github.io/zhenxun_bot/)</h1>
## 真寻的帮助 ## 真寻的帮助
请对真寻说: '真寻帮助' or '管理员帮助' or '超级用户帮助' or '真寻帮助 指令' 请对真寻说: '真寻帮助' or '管理员帮助' or '超级用户帮助' or '真寻帮助 指令'
@ -51,9 +51,11 @@
- [x] 商店/我的金币/购买道具/使用道具 - [x] 商店/我的金币/购买道具/使用道具
- [x] 8种手游抽卡 (查看 [nonebot_plugin_gamedraw](https://github.com/HibiKier/nonebot_plugin_gamedraw)) - [x] 8种手游抽卡 (查看 [nonebot_plugin_gamedraw](https://github.com/HibiKier/nonebot_plugin_gamedraw))
- [x] 我有一个朋友想问问..借鉴pcrbot插件 - [x] 我有一个朋友想问问..借鉴pcrbot插件
- [x] 原神黄历 (使用[Genshin_Impact_bot](https://github.com/H-K-Y/Genshin_Impact_bot)插件) - [x] 原神黄历
- [x] 原神今日素材 - [x] 原神今日素材
- [x] 原神资源查询 (借鉴[Genshin_Impact_bot](https://github.com/H-K-Y/Genshin_Impact_bot)插件) - [x] 原神资源查询 (借鉴[Genshin_Impact_bot](https://github.com/H-K-Y/Genshin_Impact_bot)插件)
- [x] 原神便笺查询
- [x] 原神玩家查询
- [x] 金币红包 - [x] 金币红包
- [x] 微博热搜 - [x] 微博热搜
- [x] B站主播/UP/番剧订阅 - [x] B站主播/UP/番剧订阅
@ -89,6 +91,8 @@
- [x] 通过PID获取图片 - [x] 通过PID获取图片
- [x] 功能统计可视化 - [x] 功能统计可视化
- [x] 关于
### 已实现的管理员功能 ### 已实现的管理员功能
- [x] 更新群组成员信息 - [x] 更新群组成员信息
- [x] 95%的群功能开关 - [x] 95%的群功能开关
@ -101,6 +105,7 @@
- [x] 删除图片 (同上) - [x] 删除图片 (同上)
- [x] 群内B站订阅 - [x] 群内B站订阅
- [x] 群词条 - [x] 群词条
- [x] 休息吧/醒来
### 已实现的超级用户功能 ### 已实现的超级用户功能
- [x] 添加/删除权限(是真寻的管理员权限,不是群管理员) - [x] 添加/删除权限(是真寻的管理员权限,不是群管理员)
@ -126,6 +131,11 @@
- [x] 功能开关(更多设置) - [x] 功能开关(更多设置)
- [x] 功能状态 - [x] 功能状态
- [x] b了 - [x] b了
- [x] 执行sql
- [x] 重载配置
- [x] 清理临时数据
- [x] 增删群认证
- [x] 同意/拒绝好友/群聊请求
#### 超级用户的被动技能 #### 超级用户的被动技能
- [x] 邀请入群提醒(别人邀请真寻入群) - [x] 邀请入群提醒(别人邀请真寻入群)
@ -152,127 +162,6 @@
- [x] 定时更新权限 - [x] 定时更新权限
</details> </details>
## 功能具体指令
<details>
<summary>功能具体指令说明</summary>
### 常用功能
** [*]:表示该参数可有多个 [?]:表示参数可选 **
| 功能 | 指令 | 说明
| ----------------------| :--------------------------------------:| :------------------------:
| 签到 | 签到/我的签到/好感度排行/好感度总榜/好感度总榜\[显示我/屏蔽我] | 普通的签到插件,可以获得好感度和金币<br>好感度影响开箱次数和涩图触发概率,金币用于购买道具,俄罗斯轮盘赌注以及金币红包<br>好感度总榜,显示所有群的群员好感度排行,可通过命令好感度总榜\[显示我/屏蔽我] 来设置是否隐藏
| 发送图片 | 美图/壁纸/萝莉 \[id]?(默认随机)/\[num]张图\[keyword] | 发送指定文件夹下的图片<br>示例:萝莉->发送img文件夹下luoli文件夹下的图片<br>在线搜索一些不色的图示例3张图米浴
| 色图 | 色图/色图xx/n张色图/n张xx的色图/查色图(查询本地色图信息)/色图r【n<10 | 色图r返回10张r18色图(仅私聊)并限制每日次数(默认5次)<br>其他示例:色图 真寻 <br>5张真寻的色图
| 黑白草图 | 黑白草图/黑白图 \[文字] \[图片] | 整活生成器,示例:黑白图 我喜欢真寻 \[图片]
| coser | coser/cos/括丝 | coser图片说实话挺失望的太色了
| 骂我 | 骂我 | 就是发送钉宫的语音罢了
| 戳一戳 | 戳一戳 | 随机发送钉宫语音 or 美图 or 萝莉图 or 文本
| 模拟开箱 | 开箱 \[武器箱名称](默认随机)/N连开箱 \[武器箱名称](默认随机)/我的开箱/群开箱统计/我的金色 | 当不指定武器箱时默认随机,此功能需要先在/open_cases/config.py中编写指定武器箱数据然后提前爬取价格使用超级用户命令更新cookie后再使用命令更新xx武器箱【注未设置爬取频率可能会被禁用api请谨慎用小号
| 鲁迅说过 | 鲁迅说过 \[文本] | 示例:鲁迅说过 真寻世界第一可爱
| 假消息 | 假消息 \[网址] \[标题] \[内容]? \[图片]? | 构造虚假的分享消息
| 商店系统 | 商店/我的金币/购买道具 \[名称或序号] \[数量](默认1)/使用道具 \[名称或序号] | 示例:<br>购买道具 1 3<br>购买道具 好感度双倍加持卡Ⅰ 3<br>使用道具 1<br>使用道具 好感度双倍加持卡Ⅰ
| 抽卡系统 | 原神/明日方舟/赛马娘/坎公骑冠剑/碧蓝航线/阴阳师/公主连结(pcr)/FGO N抽/一井 | 详细帮助请查看: [nonebot_plugin_gamedraw](https://github.com/HibiKier/nonebot_plugin_gamedraw) <br>示例原神90抽
| 我有一个朋友 | 我有一个朋友他说/想问问/\[文本] | 会将文本中的(他,她,它)替换成 '我'<br>示例:我有一个朋友想问问他喜不喜欢真寻
| 昵称系统 | 以后叫我\[昵称]/以后请叫我\[昵称]/我是谁/我叫什么 | 此昵称会替换与真寻聊天中 '你' 的名称(群名片),群与群与私聊的昵称相互独立
| 原神黄历 | 原神黄历 | 查看今日原神黄历含有每日10:25的定时任务
| 原神材料 | 今日素材 | 发送可莉特调的截图
| 丘丘语翻译 | 丘丘翻译/丘丘一下/丘丘语翻译 | 示例:丘丘一下 mimi
| 原神资源查询 | 原神资源查询 \[资源名称] \[路线]?/\[资源名称]在哪/哪里有\[资源名称]/原神资源列表 | 如果资源名称末尾添加‘路线’的话将生成残缺缺缺版的优先路径<br>示例:嘟嘟莲在哪<br>原神资源查询嘟嘟莲路线
| 俄罗斯轮盘 | 装弹\[子弹数] \[金额](默认200)/开枪/结算/我的战绩/胜场排行/败场排行/欧洲人排行/慈善家排行 | 紧张刺激的群内小游戏,使用每日签到的金币作为赌注,具体玩法请发送 真寻帮助 俄罗斯轮盘
| 红包系统 | 塞红包\[金额] \[数量](默认5)/抢/开/戳一戳/退回 | 仿微信明日方舟红包的样式pil拼图大师每个红包金额随机生成最多会是红包总金额的1/3退回用于退回一分钟后还未开完的红包
| 金币排行 | 金币排行 | 字面意思
|网易云热评 | 到点了/12点了/网易云热评/网易云评论 | 防下塔
| 古诗 | 念诗/念首诗/来首诗 | 突然文艺起来了
|微博热搜 | 微博热搜/微博热搜[序号]? | 快捷热搜查询方式
| pil对图片的操作 | 修改尺寸/等比压缩/旋转图片/水平翻转/铅笔滤镜/模糊效果/锐化效果/高斯模糊/边缘检测/底色替换 | 选项较多,请直接发送 真寻图片帮助
| BUFF皮肤底价查询 | 查询皮肤 \[武器名称] \[皮肤名称] | 网络不友好的话会经常超时<br>示例:查询皮肤 沙漠之鹰 印花集
| 天气查询 | \[城市]天气 | 非常常见的插件,第一个入门插件
| 疫情查询 | 疫情/查询疫情 \[城市名或省份名] | 示例:疫情杭州
| bt磁力搜索 | bt \[关键词] \[页数]?(默认第1页) | 该功能仅仅提供给私聊因为可以搜到一些色色的东西示例bt钢铁侠 5
| 上车 | 略 | 直接查看真寻帮助 上车,每日限制次数(默认5)
| 以图识番 | 识番 \[图片] | 以图搜翻,图片越清晰越完整正确率越高
| 以图搜图 | 识图 (asc)? \[图片] | 参数asc更换搜索引擎为ascii2d默认为saucenao
| 点歌 | 点歌 \[歌名] | 网易云点歌小助手
| 搜番 | 搜番 \[关键字] | 群聊只返回5个结果私聊返回20个结果
| epic白嫖游戏通知 | epic | 通知你又到了白嫖游戏的时候,可以不玩,不能没有
| P站排行榜 | p站排行 \[排行类型参数]? \[数量]? \[日期]?| 9种不同排行榜r18类型仅可私聊通过参数选择查看真寻帮助p站排行<br>示例p站排行榜 1 9 2018-4-25
| 搜图 | 搜图 \[关键词] \[数量]? \[排序方式]? \[r18]?| r18仅可私聊查看真寻帮助搜图
| 通过PID搜索图片 | p搜 [pid] | 在群内使用此功能会在30秒内撤回
| 翻译 | 英翻/日翻/韩翻/翻韩/翻日/翻英 \[文本] | 三种语言互相翻译
| 获取b站视频封面 | b封面 [链接/av/bv/cv/直播id] | 快捷的封面获取方式
| 群欢迎消息 | 群欢迎消息/查看群欢迎消息/查看当前群欢迎消息 | 查看给真寻设置的群欢迎消息
| 自我介绍 | 自我介绍 | 没错,一份正经的真寻自我介绍
| 我的权限 | 我的权限 | 真寻内部定义的一套权限系统
| 我的信息 | 我的信息 | 唯一的作用就是看看什么时候加入群
| 撤回 | 撤回 \[消息位置]?(默认为最新一条消息) | 按顺序撤回发送的消息,示例:撤回 1
| 滴滴滴- | 滴滴滴- \[文本] | 用于用户联系真寻的超级用户
|功能调用统计可视化 | 功能调用统计(自记录以来的功能调用统计)<br>周功能调用统计 [plugin_name]<br>月功能调用统计 [plugin_name]| 当plugin_name为空时为7天或30内的所有功能统计
| pix | pix/PIX [tags/uid/pid:pid] [num] | 无参数时随机查看pix图库的图片(无r18)num数量默认=1tags查看相关tags图片uid查找相关画师图片pid:pid:指定查看pid图片<br>示例pix原神 3<br>pix23493844<br>pixpid:29429933
| 添加pix关键词/uid/pid | 添加pix关键词/uid/pid *[关键词/uid/pid]| 添加关键词或uid或pid用于下次搜索关键词搜索相关taguid会收录作者下收藏符合标准的作品pid收录单张作品<br>示例添加pix关键词 原神<br> 添加pixuid 123441<br>添加pixpid 2748937|
| 查看pix图库 | 查看pix图库 [tags] | 查看已收录的tag相关图片数量<br>示例查看pix图库 原神 莫娜
|显示pix关键词 | 显示pix关键词 | 查看已收录的所有关键词/UID/PID
| b了 | b了 [at] | 使真寻完全忽略一个用户的所有信息
| B站订阅 | 添加订阅 [主播/UP/番剧] [id/链接/番名] / 删除订阅 [id] / 查看订阅 | 可以通过直接间链接或主播间id添加订阅主播开播提醒动态和投稿可以通过番名番剧的id或者番剧链接添加番剧订阅(是番剧idmd开头不是集数id,ep开头的)更新, 可以通过UP个人id或UP主页链接订阅UP动态和投稿
### 管理员功能
**群主与群管理员默认5级权限**
| 功能 | 权限等级 | 指令 | 说明
| -------------| --------------| :--------------------------------------:| :------------------------:
| 更新群组成员信息 | 1 | 更新群组成员信息/更新群组成员列表 | 存储群员的基本信息,虽然有自动更新,但备个命令以防万一
| 群功能开关 | 2 | 开启/关闭\[指令名]功能 | 群帮助中左边带有√的功能都可以通过此命令开启或关闭,示例:开启色图
| 查看群被动技能 | 2 | 群通知状态 | 详细请查看被动技能列表
| 被动技能开关 | 2 | 开启/关闭被动技能 | 有时候花里胡哨通知也会很烦人
| 自定义群欢迎消息 | 2 | 自定义群欢迎消息 \[文本] \[图片] | 文本和图片至少需要一个,在文本内添加"\[at]"字符串可以用来设置艾特进群的新群员
| 黑名单 | 5 | .ban/.unban \[at] \[小时]? \[分钟]?| 不提供具体时间的话则ban掉永久且权限低的用户无法unban高权限用户的ban同级权限也无法进行ban/unban <br>示例:.ban@笨蛋 1 50
| 刷屏检测相关 | 5 | 刷屏检测设置/设置检测时间 \[文本]/设置检测次数 \[文本]/设置禁言时长 \[分钟]| 非常讨厌刷屏的人,打算给他们一点教训
| 上传图片 | 6 | 上传图片 \[图库] \[图片]... | 上传图片至指定图库,虽然并不打算开放给群员,但还是写了,支持批量图片<br>示例:上传图片 美图 \[图片]..
| 删除图片 | 6 | 删除图片 [图库] \[图片id] | 通过指定本地图片id来删除指定图库的图片<br>示例:删除图片 美图 1
| 移动图片 | 6 | 移动图片 \[移出的图库] \[移入的图库] \[图片id] | 移动指定图库中的图片到指定的新图库中移入的图片id更改为移入图库的最后一位移除的图库中原本图片的id又最后一位图片替代<br>示例:移动图片 美图 萝莉 22
|B站订阅 | 5 | 功能同上,就是在群中有权限限制 | 略
### 超级用户功能
| 功能 | 指令 | 说明
| ----------------------| :--------------------------------------:| :------------------------:
| 权限增删 | 添加/删除权限 \[at] \[level]<br>添加/删除权限 [qq] [group] [level] | 用于添加或修改权限等级,且该权限不会被自动更新取消
| 所有群组/好友 | 所有群组/好友 | 查看真寻添加的群组与好友
| 广播 | 广播- [文本] | 广播所有群组
| 更新色图 | 更新色图 | 更新群友搜索色图时保存的url
| 回复 | /t | 命令较多,请查看/t帮助省略群号则私聊用户(必须要有用户的好友)
| 更新cookie | 更新cookie \[cookie] | 用于更新开箱数据和查询buff皮肤
| 开启广播通知 | 开启/关闭广播通知 \[群号] | 用于开启/关闭是否对某些群进行广播(上边的广播方法)
| 退群 | 退群 \[群号] | 用于退出某一些群
| 检查系统状态 | 自检 | 略
| 更新好友/群组信息| 更新好友/群组信息 | 包含自动更新被t出群等等有更好的可视信息
| 重载卡池 | 略 | 重载抽卡的游戏卡池,请查看 \[nonebot_plugin_gamedraw](https://github.com/HibiKier/nonebot_plugin_gamedraw)
| 添加商品 | 添加商品 \[名称]-\[价格]-\[描述]-\[折扣](小数)?-\[限时时间](分钟)? | 为真寻的商店添加一点点道具<br>示例:添加商品-昏睡红茶-300-一杯上好的奇怪红茶-0.9-60
| 删除商品 | 删除商品 \[名称或序号] | 在真寻的商店中删除一点东西
| 修改商品 | 修改商品 -name \[名称或序号] -price \[价格] -des \[描述] -discount \[折扣] -time \[限时]| 注意空格,不需要的参数可以不加<br>示例:修改商品 -name 1 -price 900 【修改序号为1的商品的价格为900】
| 节日红包 | 节日红包 \[金额] \[数量] \[祝福语]? \[群号]?... | 群号支持批量使用空格隔开不使用群号则对所有群发送节日红包节日红包有效时间为24小时祝福语默认为“恭喜发财 大吉大利”<br>示例:节日红包 10000 15 真寻真可爱 123324423 23423423
| 修改群权限 | 修改群权限 \[group] \[level] | 所以说这功能是对内鬼的无奈默认群权限为5默认无法使用 色图/coser/p站排行/搜图这些功能都要9级权限
|更新原神今日素材| 更新原神今日素材 | 自动更新原神每日素材失败时可以手动触发
|更新原神资源信息| 更新原神资源信息 | 除了每日自动更新的资源外,额外更新大地图
| 清理数据 | 清理数据 | 清理 temprarr18_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]?| 更新keyworduidpid或指定uidpid未指定时则更新全部当num未指定时为keyword/uid/pid更新全部<br>示例更新pix关键词<br>更新pix关键词uid 8<br>更新pix关键词pid:83457477<br>num:倒叙更新数量
|删除pix图片 | 删除pix图片 [*pid] [-b]? | [-b]参数为删除的同时加入黑名单(不再更新)虽然是pid但是_p也可以<br>示例删除pix图片3458344 8235234_p1 -b
|显示pix关键词 | 显示pix关键词 | 与普通功能相同,额外显示待收录和黑名单
|pix检测更新 | pix检测更新 [update]? | 检测从未更新过的pid或uid-update参数将在检测后直接更新未更新过的pid或uid<br>示例pix检测更新 update
| 检查更新真寻 | 检查更新真寻 | 不再需要麻烦的clone第一步
| 关闭功能 | 关闭[功能] [group/private]/*[群号]? | 关闭色图:维护功能(关闭总开关)<br>关闭色图 g在群内限制功能<br>关闭色图 p在私聊限制功能<br>关闭色图 1234678禁用12345678的色图功能
| 群白名单 | 添加/删除群白名单 *[群号] / 查看群白名单 | 白名单内的群不会受到 维护 限制
| 功能状态 | 功能状态 | 查看功能开关状态
| pix | pix [-s/-r] [keyword] | 可以通过pix -s 查看图库的涩图pix -r查看图库的r18图支持搜索当然pix图库只区分了r18和非r18如果-s查询到不色的图也问题不大
| 重载插件配置 |重载插件配置 |用于生效手动修改配文件
|帮助 -super | 帮助[功能] -super | 显示该插件的超级用户帮助
</details>
## 详细配置请前往文档以下为最简部署和配置如果你有基础并学习过nonebot2的话 ## 详细配置请前往文档以下为最简部署和配置如果你有基础并学习过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] ### 2021/12/1 \[v0.0.6.5/6]
* 群权限-1时超级用户命令依旧生效 * 群权限-1时超级用户命令依旧生效

View File

@ -11,7 +11,7 @@ from services.db_context import db
from models.level_user import LevelUser from models.level_user import LevelUser
from configs.config import Config from configs.config import Config
from utils.manager import group_manager, plugins2settings_manager, plugins_manager from utils.manager import group_manager, plugins2settings_manager, plugins_manager
from utils.image_utils import CreateImg from utils.image_utils import BuildImage
from utils.http_utils import AsyncHttpx from utils.http_utils import AsyncHttpx
import asyncio import asyncio
import time import time
@ -72,7 +72,9 @@ async def custom_group_welcome(
logger.info(f"USER {user_id} GROUP {group_id} 更换群欢迎消息 {msg}") logger.info(f"USER {user_id} GROUP {group_id} 更换群欢迎消息 {msg}")
result += msg result += msg
if img: if img:
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") img_result = image(abspath=DATA_PATH + f"custom_welcome_msg/{group_id}.jpg")
logger.info(f"USER {user_id} GROUP {group_id} 更换群欢迎消息图片") logger.info(f"USER {user_id} GROUP {group_id} 更换群欢迎消息图片")
except Exception as e: except Exception as e:
@ -107,7 +109,16 @@ async def change_group_switch(cmd: str, group_id: int, is_super: bool = False):
else: else:
if await group_manager.check_group_task_status(group_id, task): if await group_manager.check_group_task_status(group_id, task):
await group_manager.close_group_task(group_id, task) await group_manager.close_group_task(group_id, task)
if group_help_file.exists():
group_help_file.unlink()
return f"{status} 全部被动技能!" return f"{status} 全部被动技能!"
if cmd == "全部功能":
for f in plugins2settings_manager.get_data():
if status == "开启":
group_manager.unblock_plugin(f, group_id)
else:
group_manager.block_plugin(f, group_id)
return f"{status} 全部功能!"
if cmd in [task_data[x] for x in task_data.keys()]: if cmd in [task_data[x] for x in task_data.keys()]:
type_ = "task" type_ = "task"
modules = [x for x in task_data.keys() if task_data[x] == cmd] modules = [x for x in task_data.keys() if task_data[x] == cmd]
@ -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): if not group_manager.get_plugin_status(module, group_id):
return f"功能 {cmd} 正处于关闭状态!不要重复关闭." return f"功能 {cmd} 正处于关闭状态!不要重复关闭."
group_manager.block_plugin(module, group_id) group_manager.block_plugin(module, group_id)
if group_help_file.exists(): if group_help_file.exists():
group_help_file.unlink() group_help_file.unlink()
if is_super: if is_super:
for file in os.listdir(Path(DATA_PATH) / 'group_help'): for file in os.listdir(Path(DATA_PATH) / "group_help"):
file = Path(DATA_PATH) / 'group_help' / file file = Path(DATA_PATH) / "group_help" / file
file.unlink() file.unlink()
else: else:
_help_image = Path(DATA_PATH) / 'group_help' / f"{group_id}.png" _help_image = Path(DATA_PATH) / "group_help" / f"{group_id}.png"
if _help_image.exists(): if _help_image.exists():
_help_image.unlink() _help_image.unlink()
return f"{status} {cmd} 功能!" return f"{status} {cmd} 功能!"
@ -158,8 +169,8 @@ def set_plugin_status(cmd: str, block_type: str = "all"):
plugins_manager.unblock_plugin(module) plugins_manager.unblock_plugin(module)
else: else:
plugins_manager.block_plugin(module, block_type=block_type) plugins_manager.block_plugin(module, block_type=block_type)
for file in os.listdir(Path(DATA_PATH) / 'group_help'): for file in os.listdir(Path(DATA_PATH) / "group_help"):
file = Path(DATA_PATH) / 'group_help' / file file = Path(DATA_PATH) / "group_help" / file
file.unlink() file.unlink()
@ -195,11 +206,11 @@ def _get_plugin_status() -> MessageSegment:
rst += "\n" rst += "\n"
flag_str += f"{flag}\n" flag_str += f"{flag}\n"
height = len(rst.split("\n")) * 24 height = len(rst.split("\n")) * 24
a = CreateImg(250, height, font_size=20) a = BuildImage(250, height, font_size=20)
a.text((10, 10), rst) a.text((10, 10), rst)
b = CreateImg(200, height, font_size=20) b = BuildImage(200, height, font_size=20)
b.text((10, 10), flag_str) b.text((10, 10), flag_str)
A = CreateImg(500, height) A = BuildImage(500, height)
A.paste(a) A.paste(a)
A.paste(b, (270, 0)) A.paste(b, (270, 0))
return image(b64=A.pic2bs4()) return image(b64=A.pic2bs4())
@ -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] user_id=int(list(bot.config.superusers)[0]), message=result[:-1]
) )
return True return True
def set_group_bot_status(group_id: int, status: bool) -> str:
"""
设置群聊bot开关状态
:param group_id: 群号
:param status: 状态
"""
if status:
if group_manager.check_group_bot_status(group_id):
return "我还醒着呢!"
group_manager.turn_on_group_bot_status(group_id)
return "呜..醒来了..."
else:
group_manager.shutdown_group_bot_status(group_id)
# for x in group_manager.get_task_data():
# group_manager.close_group_task(group_id, x)
return "那我先睡觉了..."

View File

@ -14,10 +14,10 @@ def switch_rule(bot: Bot, event: Event, state: T_State) -> bool:
:param event: pass :param event: pass
:param state: pass :param state: pass
""" """
global cmd
try: try:
if not cmd: if not cmd:
cmd.append('关闭全部被动') cmd = ["关闭全部被动", "开启全部被动", "开启全部功能", "关闭全部功能"]
cmd.append('开启全部被动')
_data = group_manager.get_task_data() _data = group_manager.get_task_data()
for key in _data: for key in _data:
cmd.append(f"开启{_data[key]}") cmd.append(f"开启{_data[key]}")

View File

@ -1,4 +1,4 @@
from nonebot import on_command, on_message from nonebot import on_command, on_message, on_regex
from nonebot.typing import T_State from nonebot.typing import T_State
from nonebot.adapters.cqhttp import Bot, GroupMessageEvent, MessageEvent, GROUP from nonebot.adapters.cqhttp import Bot, GroupMessageEvent, MessageEvent, GROUP
from .data_source import ( from .data_source import (
@ -6,12 +6,14 @@ from .data_source import (
set_plugin_status, set_plugin_status,
get_plugin_status, get_plugin_status,
group_current_status, group_current_status,
set_group_bot_status
) )
from services.log import logger from services.log import logger
from configs.config import NICKNAME, Config from configs.config import NICKNAME, Config
from utils.utils import get_message_text, is_number from utils.utils import get_message_text, is_number
from nonebot.permission import SUPERUSER from nonebot.permission import SUPERUSER
from .rule import switch_rule from .rule import switch_rule
import re
__zx_plugin_name__ = "群功能开关 [Admin]" __zx_plugin_name__ = "群功能开关 [Admin]"
@ -24,6 +26,7 @@ usage
群被动状态 群被动状态
开启全部被动 开启全部被动
关闭全部被动 关闭全部被动
醒来/休息吧
示例开启/关闭色图 示例开启/关闭色图
""".strip() """.strip()
__plugin_superuser_usage__ = """ __plugin_superuser_usage__ = """
@ -40,6 +43,7 @@ __plugin_cmd__ = [
"群被动状态", "群被动状态",
"开启全部被动", "开启全部被动",
"关闭全部被动", "关闭全部被动",
"醒来/休息吧",
"功能状态 [_superuser]", "功能状态 [_superuser]",
"开启/关闭[功能] [group] [_superuser]", "开启/关闭[功能] [group] [_superuser]",
"开启/关闭[功能] ['private'/'group'] [_superuser]", "开启/关闭[功能] ['private'/'group'] [_superuser]",
@ -57,6 +61,8 @@ plugins_status = on_command("功能状态", permission=SUPERUSER, priority=5, bl
group_task_status = on_command("群被动状态", permission=GROUP, priority=5, block=True) group_task_status = on_command("群被动状态", permission=GROUP, priority=5, block=True)
group_status = on_regex("^(休息吧|醒来)$", permission=GROUP, priority=5, block=True)
@switch_rule_matcher.handle() @switch_rule_matcher.handle()
async def _(bot: Bot, event: MessageEvent, state: T_State): async def _(bot: Bot, event: MessageEvent, state: T_State):
@ -105,3 +111,15 @@ async def _(bot: Bot, event: MessageEvent, state: T_State):
@group_task_status.handle() @group_task_status.handle()
async def _(bot: Bot, event: GroupMessageEvent, state: T_State): async def _(bot: Bot, event: GroupMessageEvent, state: T_State):
await group_task_status.send(await group_current_status(event.group_id)) await group_task_status.send(await group_current_status(event.group_id))
@group_status.handle()
async def _(bot: Bot, event: GroupMessageEvent, state: T_State):
r = re.search("^(休息吧|醒来)$", get_message_text(event.json()))
if r:
if r.group(1) == "休息吧":
msg = set_group_bot_status(event.group_id, False)
else:
msg = set_group_bot_status(event.group_id, True)
await group_status.send(msg)
logger.info(f"USER {event.user_id} GROUP {event.group_id} 使用总开关命令:{r.group(1)}")

View File

@ -1,4 +1,4 @@
from utils.image_utils import CreateImg from utils.image_utils import BuildImage
from configs.path_config import IMAGE_PATH from configs.path_config import IMAGE_PATH
from services.log import logger from services.log import logger
from utils.utils import get_matchers from utils.utils import get_matchers
@ -84,8 +84,8 @@ def _create_help_image():
for i, x in enumerate(task_data.keys()): for i, x in enumerate(task_data.keys()):
help_str += f'{i+1}.开启/关闭{task_data[x]}\n\n' help_str += f'{i+1}.开启/关闭{task_data[x]}\n\n'
height = len(help_str.split("\n")) * 33 height = len(help_str.split("\n")) * 33
A = CreateImg(width, height, font_size=24) A = BuildImage(width, height, font_size=24)
_background = CreateImg(width, height, background=background) _background = BuildImage(width, height, background=background)
A.text((150, 110), help_str) A.text((150, 110), help_str)
A.paste(_background, alpha=True) A.paste(_background, alpha=True)
A.save(admin_help_image) A.save(admin_help_image)

View File

@ -5,8 +5,10 @@ from services.log import logger
from models.group_info import GroupInfo from models.group_info import GroupInfo
from models.friend_user import FriendUser from models.friend_user import FriendUser
from nonebot.adapters.cqhttp.exception import ActionFailed from nonebot.adapters.cqhttp.exception import ActionFailed
from configs.config import NICKNAME from configs.config import NICKNAME, Config
from utils.manager import group_manager from utils.manager import group_manager
from pathlib import Path
import shutil
__zx_plugin_name__ = "定时任务相关 [Hidden]" __zx_plugin_name__ = "定时任务相关 [Hidden]"
__plugin_version__ = 0.1 __plugin_version__ = 0.1
@ -14,7 +16,33 @@ __plugin_author__ = "HibiKier"
__plugin_task__ = {'zwa': '早晚安'} __plugin_task__ = {'zwa': '早晚安'}
x = on_message(priority=9, block=False) Config.add_plugin_config(
"_task",
"DEFAULT_ZWA",
True,
help_="被动 早晚安 进群默认开关状态",
default_value=True,
)
Config.add_plugin_config(
"_backup",
"BACKUP_FLAG",
True,
help_="是否开启文件备份",
default_value=True
)
Config.add_plugin_config(
"_backup",
"BACKUP_DIR_OR_FILE",
['data/black_word', 'data/configs', 'data/statistics', 'data/word_bank', 'data/manager', 'configs'],
name="文件备份",
help_="备份的文件夹或文件",
default_value=[]
)
cx = on_message(priority=9, block=False)
# 早上好 # 早上好
@ -106,6 +134,34 @@ async def _():
logger.error(f"自动更新群组信息错误 e:{e}") logger.error(f"自动更新群组信息错误 e:{e}")
# 自动备份
@scheduler.scheduled_job(
"cron",
hour=3,
minute=25,
)
async def _():
if Config.get_config("_backup", "BACKUP_FLAG"):
_backup_path = Path() / 'backup'
_backup_path.mkdir(exist_ok=True, parents=True)
for x in Config.get_config("_backup", "BACKUP_DIR_OR_FILE"):
try:
path = Path(x)
_p = _backup_path / x
if path.exists():
if path.is_dir():
if _p.exists():
shutil.rmtree(_p, ignore_errors=True)
shutil.copytree(x, _p)
else:
if _p.exists():
_p.unlink()
shutil.copy(x, _p)
logger.info(f'已完成自动备份:{x}')
except Exception as e:
logger.error(f"自动备份文件 {x} 发生错误 {type(e)}:{e}")
# 一次性任务 # 一次性任务
# 固定时间触发,仅触发一次: # 固定时间触发,仅触发一次:
# #

View File

@ -7,6 +7,7 @@ from utils.utils import get_message_text, get_message_imgs
from services.log import logger from services.log import logger
from utils.message_builder import image from utils.message_builder import image
from utils.manager import group_manager from utils.manager import group_manager
from configs.config import Config
__zx_plugin_name__ = "广播 [Superuser]" __zx_plugin_name__ = "广播 [Superuser]"
__plugin_usage__ = """ __plugin_usage__ = """
@ -20,6 +21,13 @@ __plugin_cmd__ = ["广播-"]
__plugin_version__ = 0.1 __plugin_version__ = 0.1
__plugin_author__ = "HibiKier" __plugin_author__ = "HibiKier"
__plugin_task__ = {"broadcast": "广播"} __plugin_task__ = {"broadcast": "广播"}
Config.add_plugin_config(
"_task",
"DEFAULT_BROADCAST",
True,
help_="被动 广播 进群默认开关状态",
default_value=True,
)
broadcast = on_command("广播-", priority=1, permission=SUPERUSER, block=True) broadcast = on_command("广播-", priority=1, permission=SUPERUSER, block=True)

View File

@ -38,6 +38,22 @@ Config.add_plugin_config(
Config.add_plugin_config( Config.add_plugin_config(
"invite_manager", "welcome_msg_cd", 5, help_="群欢迎消息cd", default_value=5 "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")) _flmt = FreqLimiter(Config.get_config("invite_manager", "welcome_msg_cd"))

View File

@ -1,4 +1,4 @@
from utils.image_utils import CreateImg from utils.image_utils import BuildImage
from configs.path_config import IMAGE_PATH from configs.path_config import IMAGE_PATH
from utils.manager import ( from utils.manager import (
plugins2settings_manager, plugins2settings_manager,
@ -50,7 +50,7 @@ def _create_help_img(
_des_tmp = {} _des_tmp = {}
_plugin_name_tmp = [] _plugin_name_tmp = []
_tmp = [] _tmp = []
tmp_img = CreateImg(0, 0, plain_text="1", font_size=24) tmp_img = BuildImage(0, 0, plain_text="1", font_size=24)
font_height = tmp_img.h font_height = tmp_img.h
# 插件分类 # 插件分类
for matcher in _matchers: for matcher in _matchers:
@ -181,7 +181,7 @@ def _create_help_img(
) )
height = len(help_str.split("\n")) * (font_height + 5) height = len(help_str.split("\n")) * (font_height + 5)
simple_height = len(simple_help_str.split("\n")) * (font_height + 5) simple_height = len(simple_help_str.split("\n")) * (font_height + 5)
A = CreateImg( A = BuildImage(
width + 150, height, font_size=24, color="white" if not ix % 2 else "black" width + 150, height, font_size=24, color="white" if not ix % 2 else "black"
) )
A.text((10, 10), help_str, (255, 255, 255) if ix % 2 else (0, 0, 0)) A.text((10, 10), help_str, (255, 255, 255) if ix % 2 else (0, 0, 0))
@ -192,8 +192,8 @@ def _create_help_img(
for x in simple_help_str.split("\n") for x in simple_help_str.split("\n")
]: ]:
simple_width = simple_width if simple_width > x else x simple_width = simple_width if simple_width > x else x
bk = CreateImg(simple_width + 20, simple_height, font_size=24, color="#6495ED") bk = BuildImage(simple_width + 20, simple_height, font_size=24, color="#6495ED")
B = CreateImg( B = BuildImage(
simple_width + 20, simple_width + 20,
simple_height, simple_height,
font_size=24, font_size=24,
@ -236,7 +236,7 @@ def _create_help_img(
for img in help_img_list: for img in help_img_list:
height += img.h height += img.h
if not group_id: if not group_id:
A = CreateImg(width + 150, height + 50, font_size=24) A = BuildImage(width + 150, height + 50, font_size=24)
A.text( A.text(
(10, 10), '* 注: * 代表可有多个相同参数 ? 代表可省略该参数 *\n\n" "功能名: 功能简介 -> 指令\n\n' (10, 10), '* 注: * 代表可有多个相同参数 ? 代表可省略该参数 *\n\n" "功能名: 功能简介 -> 指令\n\n'
) )
@ -253,14 +253,14 @@ def _create_help_img(
if img.h > height: if img.h > height:
height = img.h height = img.h
width += img.w + 10 width += img.w + 10
B = CreateImg(width + 100, height + 250, font_size=24) B = BuildImage(width + 100, height + 250, font_size=24)
width, _ = get_max_width_or_paste(simple_help_img_list, B) width, _ = get_max_width_or_paste(simple_help_img_list, B)
bk = None bk = None
random_bk = os.listdir(random_bk_path) random_bk = os.listdir(random_bk_path)
if random_bk: if random_bk:
bk = random.choice(random_bk) bk = random.choice(random_bk)
x = max(width + 50, height + 250) x = max(width + 50, height + 250)
B = CreateImg( B = BuildImage(
x, x,
x, x,
font_size=24, font_size=24,
@ -272,7 +272,7 @@ def _create_help_img(
w = 10 w = 10
h = 10 h = 10
for msg in ["目前支持的功能列表:", "可以通过 ‘帮助[功能名称] 来获取对应功能的使用方法", "或者使用 ‘详细帮助’ 来获取所有功能方法"]: for msg in ["目前支持的功能列表:", "可以通过 ‘帮助[功能名称] 来获取对应功能的使用方法", "或者使用 ‘详细帮助’ 来获取所有功能方法"]:
text = CreateImg( text = BuildImage(
0, 0,
0, 0,
plain_text=msg, plain_text=msg,
@ -284,7 +284,7 @@ def _create_help_img(
if msg == "目前支持的功能列表:": if msg == "目前支持的功能列表:":
w += 50 w += 50
B.paste( B.paste(
CreateImg( BuildImage(
0, 0,
0, 0,
plain_text="注: 红字代表功能被群管理员禁用,红线代表功能正在维护", plain_text="注: 红字代表功能被群管理员禁用,红线代表功能正在维护",
@ -299,8 +299,8 @@ def _create_help_img(
def get_max_width_or_paste( def get_max_width_or_paste(
simple_help_img_list: list, B: CreateImg = None, is_paste: bool = False simple_help_img_list: list, B: BuildImage = None, is_paste: bool = False
) -> "int, CreateImg": ) -> "int, BuildImage":
""" """
获取最大宽度或直接贴图 获取最大宽度或直接贴图
:param simple_help_img_list: 简单帮助图片列表 :param simple_help_img_list: 简单帮助图片列表
@ -353,8 +353,8 @@ def get_plugin_help(msg: str, is_super: bool = False) -> Optional[str]:
_width = len(x) * 24 _width = len(x) * 24
width = width if width > _width else _width width = width if width > _width else _width
height = len(result.split("\n")) * 45 height = len(result.split("\n")) * 45
A = CreateImg(width, height, font_size=24) A = BuildImage(width, height, font_size=24)
bk = CreateImg( bk = BuildImage(
width, width,
height, height,
background=Path(IMAGE_PATH) / "background" / "1.png", background=Path(IMAGE_PATH) / "background" / "1.png",

View File

@ -1,5 +1,4 @@
from configs.config import Config from configs.config import Config
import nonebot
Config.add_plugin_config( Config.add_plugin_config(
@ -35,4 +34,3 @@ Config.add_plugin_config(
default_value=6 default_value=6
) )
nonebot.load_plugins("basic_plugins/hooks")

View File

@ -13,7 +13,7 @@ from nonebot.adapters.cqhttp import (
Bot, Bot,
MessageEvent, MessageEvent,
GroupMessageEvent, GroupMessageEvent,
PokeNotifyEvent, PokeNotifyEvent
) )
from configs.config import Config from configs.config import Config
from models.ban_user import BanUser 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): async def _(matcher: Matcher, bot: Bot, event: MessageEvent, state: T_State):
module = matcher.module module = matcher.module
plugins2info_dict = plugins2settings_manager.get_data() plugins2info_dict = plugins2settings_manager.get_data()
if ( try:
(not isinstance(event, MessageEvent) and module != "poke") if (
or await BanUser.is_ban(event.user_id) (not isinstance(event, MessageEvent) and module != "poke")
and str(event.user_id) not in bot.config.superusers or await BanUser.is_ban(event.user_id)
) or ( and str(event.user_id) not in bot.config.superusers
str(event.user_id) in bot.config.superusers ) or (
and plugins2info_dict.get(module) str(event.user_id) in bot.config.superusers
and not plugins2info_dict[module]["limit_superuser"] and plugins2info_dict.get(module)
): and not plugins2info_dict[module]["limit_superuser"]
return ):
return
except AttributeError:
pass
# 超级用户命令 # 超级用户命令
try: try:
_plugin = nonebot.plugin.get_plugin(module) _plugin = nonebot.plugin.get_plugin(module)
@ -59,10 +62,19 @@ async def _(matcher: Matcher, bot: Bot, event: MessageEvent, state: T_State):
return return
except AttributeError: except AttributeError:
pass pass
# 群黑名单检测 # 群黑名单检测 群总开关检测
if isinstance(event, GroupMessageEvent): if isinstance(event, GroupMessageEvent) or matcher.module == "poke":
if group_manager.get_group_level(event.group_id) < 0: try:
raise IgnoredException("群黑名单") if group_manager.get_group_level(event.group_id) < 0:
raise IgnoredException("群黑名单")
if not group_manager.check_group_bot_status(event.group_id):
try:
if str(event.get_message()) != "醒来":
raise IgnoredException("功能总开关关闭状态")
except ValueError:
raise IgnoredException("功能总开关关闭状态")
except AttributeError:
pass
if module in admin_manager.keys() and matcher.priority not in [1, 9]: if module in admin_manager.keys() and matcher.priority not in [1, 9]:
if isinstance(event, GroupMessageEvent): if isinstance(event, GroupMessageEvent):
# 个人权限 # 个人权限

View File

@ -9,7 +9,7 @@ from nonebot.adapters.cqhttp import (
) )
from configs.config import Config from configs.config import Config
from models.ban_user import BanUser from models.ban_user import BanUser
from utils.utils import is_number, static_flmt from utils.utils import is_number, static_flmt, FreqLimiter
from utils.message_builder import at from utils.message_builder import at
@ -20,6 +20,8 @@ Config.add_plugin_config(
help_="对被ban用户发送的消息", help_="对被ban用户发送的消息",
) )
_flmt = FreqLimiter(300)
# 检查是否被ban # 检查是否被ban
@run_preprocessor @run_preprocessor
@ -55,7 +57,8 @@ async def _(matcher: Matcher, bot: Bot, event: MessageEvent, state: T_State):
if matcher.priority != 9: if matcher.priority != 9:
try: try:
ban_result = Config.get_config("hook", "BAN_RESULT") ban_result = Config.get_config("hook", "BAN_RESULT")
if ban_result: if ban_result and _flmt.check(event.user_id):
_flmt.start_cd(event.user_id)
await bot.send_group_msg( await bot.send_group_msg(
group_id=event.group_id, group_id=event.group_id,
message=at(event.user_id) message=at(event.user_id)

View File

@ -25,7 +25,7 @@ __plugin_version__ = 0.1
__plugin_author__ = "HibiKier" __plugin_author__ = "HibiKier"
__plugin_configs__ = { __plugin_configs__ = {
"BLACK_WORD": { "BLACK_WORD": {
"value": ["", "", "", ""], "value": ["", "", ""],
"help": "昵称所屏蔽的关键词,会被替换为 *", "help": "昵称所屏蔽的关键词,会被替换为 *",
"default_value": None "default_value": None
} }

View File

@ -0,0 +1,38 @@
from nonebot import on_command
from nonebot.permission import SUPERUSER
from nonebot.typing import T_State
from nonebot.adapters.cqhttp import Bot, MessageEvent
from nonebot.rule import to_me
from services.db_context import db
from utils.utils import get_message_text
from services.log import logger
__zx_plugin_name__ = "执行sql [Superuser]"
__plugin_usage__ = """
usage
执行一段sql语句
指令
exec [sql语句]
""".strip()
__plugin_des__ = "执行一段sql语句"
__plugin_cmd__ = [
"exec [sql语句]",
]
__plugin_version__ = 0.1
__plugin_author__ = "HibiKier"
exec_ = on_command("exec", rule=to_me(), permission=SUPERUSER, priority=1, block=True)
@exec_.handle()
async def _(bot: Bot, event: MessageEvent, state: T_State):
sql = get_message_text(event.json())
async with db.transaction():
try:
query = db.text(sql)
await db.first(query)
await exec_.send("执行 sql 语句成功.")
except Exception as e:
await exec_.send(f"执行 sql 语句失败 {type(e)}{e}")
logger.error(f"执行 sql 语句失败 {type(e)}{e}")

View File

@ -40,7 +40,7 @@ async def _(bot: Bot, event: GroupMessageEvent, state: T_State):
group_id = -1 group_id = -1
level = 0 level = 0
try: try:
args = get_message_text(event.json()).strip().split() args = get_message_text(event.json()).split()
qq = get_message_at(event.json()) qq = get_message_at(event.json())
flag = -1 flag = -1
if not qq: if not qq:
@ -55,7 +55,7 @@ async def _(bot: Bot, event: GroupMessageEvent, state: T_State):
else: else:
await super_cmd.finish( await super_cmd.finish(
"权限参数不完全\n\t格式:添加/删除权限 [at] [level]" "权限参数不完全\n\t格式:添加/删除权限 [at] [level]"
"\n\t格式:添加/删除权限 [qq] [group] [level]", "\n\t格式:添加/删除权限 [qq] [group_id] [level]",
at_sender=True, at_sender=True,
) )
else: else:
@ -63,10 +63,6 @@ async def _(bot: Bot, event: GroupMessageEvent, state: T_State):
group_id = event.group_id group_id = event.group_id
flag = 2 flag = 2
if state["_prefix"]["raw_command"][:2] == "添加": if state["_prefix"]["raw_command"][:2] == "添加":
if is_number(args[0]):
level = int(args[0])
else:
await super_cmd.finish("权限等级必须是数字!", at_sender=True)
if await LevelUser.set_level(qq, group_id, level, 1): if await LevelUser.set_level(qq, group_id, level, 1):
result = "添加管理成功, 权限: " + str(level) result = "添加管理成功, 权限: " + str(level)
else: else:

View File

@ -1,4 +1,4 @@
from utils.image_utils import CreateImg from utils.image_utils import BuildImage
from configs.path_config import IMAGE_PATH from configs.path_config import IMAGE_PATH
from services.log import logger from services.log import logger
from utils.utils import get_matchers from utils.utils import get_matchers
@ -32,7 +32,7 @@ def _create_help_image():
_plugin_name_list = [] _plugin_name_list = []
width = 0 width = 0
help_str = "超级用户帮助\n\n* 注: * 代表可有多个相同参数 ? 代表可省略该参数 *\n\n" help_str = "超级用户帮助\n\n* 注: * 代表可有多个相同参数 ? 代表可省略该参数 *\n\n"
tmp_img = CreateImg(0, 0, plain_text='1', font_size=24) tmp_img = BuildImage(0, 0, plain_text='1', font_size=24)
for matcher in _matchers: for matcher in _matchers:
plugin_name = "" plugin_name = ""
try: try:
@ -72,8 +72,8 @@ def _create_help_image():
) )
height = len(help_str.split("\n")) * 33 height = len(help_str.split("\n")) * 33
width += 500 width += 500
A = CreateImg(width, height, font_size=24) A = BuildImage(width, height, font_size=24)
_background = CreateImg(width, height, background=background) _background = BuildImage(width, height, background=background)
A.text((300, 140), help_str) A.text((300, 140), help_str)
A.paste(_background, alpha=True) A.paste(_background, alpha=True)
A.save(superuser_help_image) A.save(superuser_help_image)

4
bot.py
View File

@ -9,10 +9,12 @@ driver.register_adapter("cqhttp", CQHTTPBot)
config = driver.config config = driver.config
driver.on_startup(init) driver.on_startup(init)
driver.on_shutdown(disconnect) driver.on_shutdown(disconnect)
# 优先加载定时任务插件 # 优先加载定时任务
nonebot.load_plugin("nonebot_plugin_apscheduler") nonebot.load_plugin("nonebot_plugin_apscheduler")
nonebot.load_plugins("basic_plugins") nonebot.load_plugins("basic_plugins")
nonebot.load_plugins("plugins") nonebot.load_plugins("plugins")
# 最后加载权限控制
nonebot.load_plugins("basic_plugins/hooks")
if __name__ == "__main__": if __name__ == "__main__":

View File

@ -17,8 +17,8 @@ address: str = "" # 数据库地址
port: str = "" # 数据库端口 port: str = "" # 数据库端口
database: str = "" # 数据库名称 database: str = "" # 数据库名称
# 全局代理,例如 "http://127.0.0.1:7890" # 代理,例如 "http://127.0.0.1:7890"
SYSTEM_PROXY: Optional[str] = None SYSTEM_PROXY: Optional[str] = None # 全局代理
Config = ConfigsManager(Path() / "data" / "configs" / "plugins2config.yaml") Config = ConfigsManager(Path() / "data" / "configs" / "plugins2config.yaml")

View File

@ -105,11 +105,12 @@ class ConfigsManager:
if self._data[module].get(key) is not None: if self._data[module].get(key) is not None:
self._data[module][key]["default_value"] = value self._data[module][key]["default_value"] = value
def get_config(self, module: str, key: str) -> Optional[Any]: def get_config(self, module: str, key: str, default: Optional[Any] = None) -> Optional[Any]:
""" """
获取指定配置值 获取指定配置值
:param module: 模块名 :param module: 模块名
:param key: 配置名称 :param key: 配置名称
:param default: 没有key值内容的默认返回值
""" """
key = key.upper() key = key.upper()
if module in self._data.keys(): if module in self._data.keys():
@ -118,6 +119,8 @@ class ConfigsManager:
if self._data[module][key]["value"] is None: if self._data[module][key]["value"] is None:
return self._data[module][key]["default_value"] return self._data[module][key]["default_value"]
return self._data[module][key]["value"] return self._data[module][key]["value"]
if default is not None:
return default
return None return None
def get_level2module(self, module: str, key: str) -> Optional[str]: def get_level2module(self, module: str, key: str) -> Optional[str]:
@ -170,4 +173,3 @@ class ConfigsManager:
def __getitem__(self, key): def __getitem__(self, key):
return self._data[key] return self._data[key]

View File

@ -83,8 +83,9 @@ class FriendUser(db.Model):
if user.nickname: if user.nickname:
_tmp = "" _tmp = ""
black_word = Config.get_config("nickname", "BLACK_WORD") black_word = Config.get_config("nickname", "BLACK_WORD")
for x in user.nickname: if black_word:
_tmp += "*" if x in black_word else x for x in user.nickname:
_tmp += "*" if x in black_word else x
return _tmp return _tmp
return "" return ""

View File

@ -156,8 +156,9 @@ class GroupInfoUser(db.Model):
if user.nickname: if user.nickname:
_tmp = "" _tmp = ""
black_word = Config.get_config("nickname", "BLACK_WORD") black_word = Config.get_config("nickname", "BLACK_WORD")
for x in user.nickname: if black_word:
_tmp += "*" if x in black_word else x for x in user.nickname:
_tmp += "*" if x in black_word else x
return _tmp return _tmp
return "" return ""

View File

@ -1,5 +1,5 @@
from datetime import datetime from datetime import datetime
from typing import List
from services.db_context import db from services.db_context import db
@ -26,6 +26,14 @@ class SignGroupUser(db.Model):
async def ensure( async def ensure(
cls, user_qq: int, belonging_group: int, for_update: bool = False cls, user_qq: int, belonging_group: int, for_update: bool = False
) -> "SignGroupUser": ) -> "SignGroupUser":
"""
说明:
获取签到用户
参数:
:param user_qq: 用户qq
:param belonging_group: 所在群聊
:param for_update: 是否存在修改数据
"""
query = cls.query.where( query = cls.query.where(
(cls.user_qq == user_qq) & (cls.belonging_group == belonging_group) (cls.user_qq == user_qq) & (cls.belonging_group == belonging_group)
) )
@ -40,8 +48,28 @@ class SignGroupUser(db.Model):
impression=0, impression=0,
) )
@classmethod
async def get_user_all_data(cls, user_qq: int) -> List["SignGroupUser"]:
"""
说明:
获取某用户所有数据
参数:
:param user_qq: 用户qq
"""
query = cls.query.where(cls.user_qq == user_qq)
query = query.with_for_update()
return await query.gino.all()
@classmethod @classmethod
async def sign(cls, user: "SignGroupUser", impression: float, checkin_time_last: datetime): async def sign(cls, user: "SignGroupUser", impression: float, checkin_time_last: datetime):
"""
说明:
签到
说明:
:param user: 用户
:param impression: 增加的好感度
:param checkin_time_last: 签到时间
"""
await user.update( await user.update(
checkin_count=user.checkin_count + 1, checkin_count=user.checkin_count + 1,
checkin_time_last=checkin_time_last, checkin_time_last=checkin_time_last,

45
plugins/about.py Normal file
View File

@ -0,0 +1,45 @@
from nonebot import on_regex
from nonebot.adapters.cqhttp import Bot, MessageEvent
from nonebot.typing import T_State
from nonebot.rule import to_me
from pathlib import Path
__zx_plugin_name__ = "关于"
__plugin_usage__ = """
usage
想要更加了解真寻吗
指令
关于
""".strip()
__plugin_des__ = "想要更加了解真寻吗"
__plugin_cmd__ = ["关于"]
__plugin_version__ = 0.1
__plugin_type__ = ("其他",)
__plugin_author__ = "HibiKier"
__plugin_settings__ = {
"level": 1,
"default_status": True,
"limit_superuser": False,
"cmd": ["关于"],
}
about = on_regex("^关于$", priority=5, block=True, rule=to_me())
@about.handle()
async def _(bot: Bot, event: MessageEvent, state: T_State):
ver_file = Path() / '__version__'
version = None
if ver_file.exists():
with open(ver_file, 'r', encoding='utf8') as f:
version = f.read().split(':')[-1].strip()
msg = f"""
绪山真寻Bot
版本{version}
简介基于Nonebot2与go-cqhttp开发是一个非常可爱的Bot呀希望与大家要好好相处
项目地址https://github.com/HibiKier/zhenxun_bot
文档地址https://hibikier.github.io/zhenxun_bot/
""".strip()
await about.send(msg)

View File

@ -75,7 +75,10 @@ class AiMessageManager(StaticData):
:param user_id: 用户id :param user_id: 用户id
:param nickname: 用户昵称 :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 return None
msg = await self._get_user_repeat_message_result(user_id) msg = await self._get_user_repeat_message_result(user_id)
if not msg: if not msg:

View File

@ -1,5 +1,5 @@
from nonebot.adapters.cqhttp import MessageSegment from nonebot.adapters.cqhttp import MessageSegment
from utils.image_utils import CreateImg from utils.image_utils import BuildImage
from utils.message_builder import image from utils.message_builder import image
from configs.path_config import IMAGE_PATH from configs.path_config import IMAGE_PATH
from typing import Optional from typing import Optional
@ -33,14 +33,14 @@ def gen_wbtop_pic(data: dict) -> MessageSegment:
生成微博热搜图片 生成微博热搜图片
:param data: 微博热搜数据 :param data: 微博热搜数据
""" """
bk = CreateImg(700, 32 * 50 + 280, 700, 32, color="#797979") bk = BuildImage(700, 32 * 50 + 280, 700, 32, color="#797979")
wbtop_bk = CreateImg(700, 280, background=f"{IMAGE_PATH}/other/webtop.png") wbtop_bk = BuildImage(700, 280, background=f"{IMAGE_PATH}/other/webtop.png")
bk.paste(wbtop_bk) bk.paste(wbtop_bk)
text_bk = CreateImg(700, 32 * 50, 700, 32, color="#797979") text_bk = BuildImage(700, 32 * 50, 700, 32, color="#797979")
for i, data in enumerate(data): for i, data in enumerate(data):
title = f"{i+1}. {data['hot_word']}" title = f"{i+1}. {data['hot_word']}"
hot = data["hot_word_num"] hot = data["hot_word_num"]
img = CreateImg(700, 30, font_size=20) img = BuildImage(700, 30, font_size=20)
w, h = img.getsize(title) w, h = img.getsize(title)
img.text((10, int((30 - h) / 2)), title) img.text((10, int((30 - h) / 2)), title)
img.text((580, int((30 - h) / 2)), hot) img.text((580, int((30 - h) / 2)), hot)

View File

@ -1,4 +1,4 @@
from utils.image_utils import CreateImg from utils.image_utils import BuildImage
from configs.path_config import IMAGE_PATH from configs.path_config import IMAGE_PATH
from utils.http_utils import AsyncHttpx from utils.http_utils import AsyncHttpx
from pathlib import Path 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"] sex = user_info["sex"]
face = user_info["face"] face = user_info["face"]
sign = user_info["sign"] sign = user_info["sign"]
ava = CreateImg(100, 100, background=BytesIO(await get_pic(face))) ava = BuildImage(100, 100, background=BytesIO(await get_pic(face)))
ava.circle() ava.circle()
cover = CreateImg(470, 265, background=BytesIO(await get_pic(cover))) cover = BuildImage(470, 265, background=BytesIO(await get_pic(cover)))
print() print()
def _create_live_des_image( def _create_live_des_image(
title: str, title: str,
cover: CreateImg, cover: BuildImage,
tags: str, tags: str,
des: str, des: str,
user_name: str, user_name: str,
sex: str, sex: str,
sign: str, sign: str,
ava: CreateImg, ava: BuildImage,
): ):
""" """
生成主播简介图片 生成主播简介图片
@ -66,6 +66,6 @@ def _create_live_des_image(
border = BORDER_PATH / "0.png" border = BORDER_PATH / "0.png"
border_img = None border_img = None
if border.exists(): if border.exists():
border_img = CreateImg(1772, 2657, background=border) border_img = BuildImage(1772, 2657, background=border)
bk = CreateImg(1772, 2657, font_size=30) bk = BuildImage(1772, 2657, font_size=30)
bk.paste(cover, (0, 100), center_type="by_width") bk.paste(cover, (0, 100), center_type="by_width")

View File

@ -23,7 +23,10 @@ async def search_song(song_name: str):
async def get_song_id(song_name: str) -> int: async def get_song_id(song_name: str) -> int:
""" """ """ """
r = await search_song(song_name) 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): async def get_song_info(songId: int):

View File

@ -2,7 +2,7 @@ import psutil
import time import time
from datetime import datetime from datetime import datetime
from utils.http_utils import AsyncHttpx 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 configs.path_config import IMAGE_PATH
from pathlib import Path from pathlib import Path
import asyncio import asyncio
@ -49,7 +49,7 @@ class Check:
async def show(self): async def show(self):
await self.check_all() await self.check_all()
A = CreateImg(0, 0, font_size=24) A = BuildImage(0, 0, font_size=24)
rst = ( rst = (
f'[Time] {str(datetime.now()).split(".")[0]}\n' f'[Time] {str(datetime.now()).split(".")[0]}\n'
f"-----System-----\n" f"-----System-----\n"
@ -69,10 +69,10 @@ class Check:
if w > width: if w > width:
width = w width = w
height += 30 height += 30
A = CreateImg(width + 50, height + 10, font_size=24, font="HWZhongSong.ttf") A = BuildImage(width + 50, height + 10, font_size=24, font="HWZhongSong.ttf")
A.transparent(1) A.transparent(1)
A.text((10, 10), rst) A.text((10, 10), rst)
_x = max(width, height) _x = max(width, height)
bk = CreateImg(_x + 100, _x + 100, background=Path(IMAGE_PATH) / "background" / "check" / "0.jpg") bk = BuildImage(_x + 100, _x + 100, background=Path(IMAGE_PATH) / "background" / "check" / "0.jpg")
bk.paste(A, alpha=True, center_type='center') bk.paste(A, alpha=True, center_type='center')
return bk.pic2bs4() return bk.pic2bs4()

View File

@ -1,5 +1,5 @@
from nonebot.adapters.cqhttp import Bot, Message 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 configs.path_config import IMAGE_PATH
from utils.message_builder import image from utils.message_builder import image
from utils.http_utils import AsyncHttpx from utils.http_utils import AsyncHttpx
@ -85,12 +85,12 @@ async def check_update(bot: Bot) -> 'int, str':
update_info = data["body"] update_info = data["body"]
width = 0 width = 0
height = len(update_info.split('\n')) * 24 height = len(update_info.split('\n')) * 24
A = CreateImg(width, height, font_size=20) A = BuildImage(width, height, font_size=20)
for m in update_info.split('\n'): for m in update_info.split('\n'):
w, h = A.getsize(m) w, h = A.getsize(m)
if w > width: if w > width:
width = w 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.text((10, 10), update_info)
A.save(f'{IMAGE_PATH}/update_info.png') A.save(f'{IMAGE_PATH}/update_info.png')
await bot.send_private_msg( await bot.send_private_msg(

View File

@ -65,7 +65,7 @@ async def _(bot: Bot, event: MessageEvent, state: T_State):
img_msg = _text("") img_msg = _text("")
for img in get_message_imgs(event.json()): for img in get_message_imgs(event.json()):
img_msg += image(img) img_msg += image(img)
if not text or text in ["帮助"]: if not text and not img_msg:
await dialogue.send("请发送[滴滴滴]+您要说的内容~", at_sender=True) await dialogue.send("请发送[滴滴滴]+您要说的内容~", at_sender=True)
else: else:
group_id = 0 group_id = 0

View File

@ -6,7 +6,7 @@ from configs.path_config import IMAGE_PATH
from utils.http_utils import AsyncHttpx from utils.http_utils import AsyncHttpx
import nonebot import nonebot
import pypinyin import pypinyin
from utils.image_utils import CreateImg from utils.image_utils import BuildImage
import platform import platform
from services.log import logger from services.log import logger
import random import random
@ -98,37 +98,37 @@ async def generate_img(card_set: Union[Set[BaseData], List[BaseData]], game_name
num = 0 num = 0
for n in star_list: for n in star_list:
num += n num += n
A = CreateImg(w, h) A = BuildImage(w, h)
A.paste(card_img) A.paste(card_img)
return A.pic2bs4() return A.pic2bs4()
def _pst(h: int, img_list: list, game_name: str, background_list: list): 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 idx = 0
for img in img_list: for img in img_list:
try: try:
if game_name == 'prts': if game_name == 'prts':
bk = CreateImg(100, 100, color=background_list[idx]) bk = BuildImage(100, 100, color=background_list[idx])
b = CreateImg(94, 94, background=img) b = BuildImage(94, 94, background=img)
bk.paste(b, (3, 3)) bk.paste(b, (3, 3))
b = bk b = bk
elif game_name == 'azur' and background_list: elif game_name == 'azur' and background_list:
bk = CreateImg(100, 100, background=background_list[idx]) bk = BuildImage(100, 100, background=background_list[idx])
b = CreateImg(98, 90, background=img) b = BuildImage(98, 90, background=img)
bk.paste(b, (1, 5)) bk.paste(b, (1, 5))
b = bk b = bk
else: else:
try: try:
b = CreateImg(100, 100, background=img) b = BuildImage(100, 100, background=img)
except UnidentifiedImageError as e: except UnidentifiedImageError as e:
logger.warning(f'无法识别图片 已删除图片,下次更新重新下载... e{e}') logger.warning(f'无法识别图片 已删除图片,下次更新重新下载... e{e}')
if os.path.exists(img): if os.path.exists(img):
os.remove(img) os.remove(img)
b = CreateImg(100, 100, color='black') b = BuildImage(100, 100, color='black')
except FileNotFoundError: except FileNotFoundError:
logger.warning(f'{img} not exists') logger.warning(f'{img} not exists')
b = CreateImg(100, 100, color='black') b = BuildImage(100, 100, color='black')
card_img.paste(b) card_img.paste(b)
idx += 1 idx += 1
return card_img return card_img

View File

@ -5,6 +5,7 @@ from nonebot.typing import T_State
from utils.utils import scheduler, get_bot from utils.utils import scheduler, get_bot
from .data_source import get_epic_free from .data_source import get_epic_free
from utils.manager import group_manager from utils.manager import group_manager
from configs.config import Config
__zx_plugin_name__ = "epic免费游戏" __zx_plugin_name__ = "epic免费游戏"
__plugin_usage__ = """ __plugin_usage__ = """
@ -24,6 +25,14 @@ __plugin_settings__ = {
"cmd": ["epic"], "cmd": ["epic"],
} }
__plugin_task__ = {"epic_free_game": "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) 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"): if await group_manager.check_group_task_status(g, "epic_free_game"):
try: try:
msg_list, code = await get_epic_free(bot, GroupMessageEvent) 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) 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: except Exception as e:
logger.error(f"GROUP {g} epic免费游戏推送错误 {type(e)}: {e}") logger.error(f"GROUP {g} epic免费游戏推送错误 {type(e)}: {e}")

View File

@ -19,13 +19,20 @@ usage
重复3次相同的消息时会复读 重复3次相同的消息时会复读
""".strip() """.strip()
__plugin_des__ = "群友的本质是什么?是复读机哒!" __plugin_des__ = "群友的本质是什么?是复读机哒!"
__plugin_type__ = ("被动相关",) __plugin_type__ = ("其他",)
__plugin_version__ = 0.1 __plugin_version__ = 0.1
__plugin_author__ = "HibiKier" __plugin_author__ = "HibiKier"
__plugin_task__ = {"fudu": "复读"} __plugin_task__ = {"fudu": "复读"}
__plugin_configs__ = { __plugin_configs__ = {
"FUDU_PROBABILITY": {"value": 0.7, "help": "复读概率", "default_value": 0.7} "FUDU_PROBABILITY": {"value": 0.7, "help": "复读概率", "default_value": 0.7}
} }
Config.add_plugin_config(
"_task",
"DEFAULT_FUDU",
True,
help_="被动 复读 进群默认开关状态",
default_value=True,
)
class Fudu: class Fudu:

View File

@ -6,6 +6,7 @@ from services.log import logger
from configs.path_config import IMAGE_PATH from configs.path_config import IMAGE_PATH
from .data_source import get_alc_image from .data_source import get_alc_image
from utils.manager import group_manager from utils.manager import group_manager
from configs.config import Config
from pathlib import Path from pathlib import Path
__zx_plugin_name__ = "原神老黄历" __zx_plugin_name__ = "原神老黄历"
@ -28,6 +29,14 @@ __plugin_settings__ = {
} }
__plugin_task__ = {"genshin_alc": "原神黄历提醒"} __plugin_task__ = {"genshin_alc": "原神黄历提醒"}
Config.add_plugin_config(
"_task",
"DEFAULT_GENSHIN_ALC",
True,
help_="被动 原神黄历提醒 进群默认开关状态",
default_value=True,
)
almanac = on_command("原神黄历", priority=5, block=True) almanac = on_command("原神黄历", priority=5, block=True)

View File

@ -2,7 +2,7 @@ from nonebot import on_command, Driver
from nonebot.typing import T_State from nonebot.typing import T_State
from nonebot.adapters.cqhttp import Bot, MessageEvent, Message, GroupMessageEvent from nonebot.adapters.cqhttp import Bot, MessageEvent, Message, GroupMessageEvent
from utils.message_builder import image 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 utils.browser import get_browser
from configs.path_config import IMAGE_PATH from configs.path_config import IMAGE_PATH
import nonebot import nonebot
@ -131,12 +131,12 @@ async def update_image():
height = await asyncio.get_event_loop().run_in_executor( height = await asyncio.get_event_loop().run_in_executor(
None, get_background_height, weapons_imgs 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 current_width = 50
for imgs in [char_imgs, weapons_imgs]: for imgs in [char_imgs, weapons_imgs]:
current_height = 20 current_height = 20
for img in imgs: for img in imgs:
x = CreateImg(0, 0, background=img) x = BuildImage(0, 0, background=img)
background_img.paste(x, (current_width, current_height)) background_img.paste(x, (current_width, current_height))
current_height += x.size[1] current_height += x.size[1]
current_width += 600 current_width += 600
@ -154,8 +154,8 @@ async def update_image():
def get_background_height(weapons_imgs: List[str]) -> int: def get_background_height(weapons_imgs: List[str]) -> int:
height = 0 height = 0
for weapons in weapons_imgs: for weapons in weapons_imgs:
height += CreateImg(0, 0, background=weapons).size[1] height += BuildImage(0, 0, background=weapons).size[1]
last_weapon = CreateImg(0, 0, background=weapons_imgs[-1]) last_weapon = BuildImage(0, 0, background=weapons_imgs[-1])
w, h = last_weapon.size w, h = last_weapon.size
last_weapon.crop((0, 0, w, h - 10)) last_weapon.crop((0, 0, w, h - 10))
last_weapon.save(weapons_imgs[-1]) last_weapon.save(weapons_imgs[-1])

View File

@ -1,6 +1,6 @@
from pathlib import Path from pathlib import Path
from configs.path_config import IMAGE_PATH, TEXT_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 typing import Tuple, List
from math import sqrt, pow from math import sqrt, pow
import random import random
@ -39,7 +39,7 @@ class Map:
:param planning_route: 是否规划最佳线路 :param planning_route: 是否规划最佳线路
:param ratio: 压缩比率 :param ratio: 压缩比率
""" """
self.map = CreateImg(0, 0, background=map_path) self.map = BuildImage(0, 0, background=map_path)
self.resource_name = resource_name self.resource_name = resource_name
self.center_x = center_point[0] self.center_x = center_point[0]
self.center_y = center_point[1] self.center_y = center_point[1]
@ -225,13 +225,13 @@ class Map:
# self.map.line(xy, (255, 0, 0), width=3) # 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" icon = icon_path / f"{id_}.png"
if icon.exists(): if icon.exists():
return CreateImg( return BuildImage(
int(50 * self.ratio), int(50 * self.ratio), background=icon int(50 * self.ratio), int(50 * self.ratio), background=icon
) )
return CreateImg( return BuildImage(
int(50 * self.ratio), int(50 * self.ratio),
int(50 * self.ratio), int(50 * self.ratio),
background=f"{icon_path}/box.png", background=f"{icon_path}/box.png",

View File

@ -3,7 +3,7 @@ from configs.path_config import IMAGE_PATH, TEXT_PATH
from PIL.Image import UnidentifiedImageError from PIL.Image import UnidentifiedImageError
from utils.message_builder import image from utils.message_builder import image
from services.log import logger from services.log import logger
from utils.image_utils import CreateImg from utils.image_utils import BuildImage
from asyncio.exceptions import TimeoutError from asyncio.exceptions import TimeoutError
from asyncio import Semaphore from asyncio import Semaphore
from utils.image_utils import is_valid from utils.image_utils import is_valid
@ -186,11 +186,11 @@ async def download_map_init(
force_flag=flag, force_flag=flag,
) )
idx += 1 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)) 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): 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") map_file.save(f"{map_path}/map.png")
else: else:
logger.warning(f'获取原神地图失败 msg: {data["message"]}') logger.warning(f'获取原神地图失败 msg: {data["message"]}')
@ -230,10 +230,10 @@ async def download_resource_type():
# 初始化资源图标 # 初始化资源图标
def gen_icon(icon: str): def gen_icon(icon: str):
A = CreateImg(0, 0, background=f"{icon_path}/box.png") A = BuildImage(0, 0, background=f"{icon_path}/box.png")
B = CreateImg(0, 0, background=f"{icon_path}/box_alpha.png") B = BuildImage(0, 0, background=f"{icon_path}/box_alpha.png")
icon_ = icon_path / f"{icon}" icon_ = icon_path / f"{icon}"
icon_img = CreateImg(115, 115, background=icon_) icon_img = BuildImage(115, 115, background=icon_)
icon_img.circle() icon_img.circle()
B.paste(icon_img, (17, 10), True) B.paste(icon_img, (17, 10), True)
B.paste(A, alpha=True) B.paste(A, alpha=True)

View File

@ -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")

View File

@ -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("该用户数据不存在..")

View File

@ -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()

View File

@ -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("未查询到数据...")

View File

@ -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())

View File

@ -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("查询失败..")

View File

@ -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

View File

@ -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
)

View File

@ -0,0 +1,17 @@
from utils.utils import scheduler
from ..models import Genshin
from services.log import logger
@scheduler.scheduled_job(
"cron",
hour=0,
minute=1,
)
async def _():
try:
await Genshin.reset_today_query_uid()
logger.warning(f"重置原神查询记录成功..")
except Exception as e:
logger.error(f"重置原神查询记录失败. {type(e)}:{e}")

View File

@ -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": "",
}

View File

@ -115,6 +115,7 @@ async def _(bot: Bot, event: GroupMessageEvent, state: T_State):
redbag_data[event.group_id]["amount"] redbag_data[event.group_id]["amount"]
- redbag_data[event.group_id]["open_amount"] - redbag_data[event.group_id]["open_amount"]
) )
await return_gold(event.user_id, event.group_id, amount)
await gold_redbag.send( await gold_redbag.send(
f'{redbag_data[event.group_id]["nickname"]}的红包过时未开完,退还{amount}金币...' f'{redbag_data[event.group_id]["nickname"]}的红包过时未开完,退还{amount}金币...'
) )
@ -137,11 +138,11 @@ async def _(bot: Bot, event: GroupMessageEvent, state: T_State):
else: else:
amount = msg[0] amount = msg[0]
num = msg[1] 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) flag, amount = await check_gold(event.user_id, event.group_id, amount)
if not flag: if not flag:
await gold_redbag.finish(amount, at_sender=True) await gold_redbag.finish(amount, at_sender=True)
if not is_number(num) or int(num) < 1:
await gold_redbag.finish("红包个数给我输正确啊!", at_sender=True)
group_member_num = (await bot.get_group_info(group_id=event.group_id))['member_count'] group_member_num = (await bot.get_group_info(group_id=event.group_id))['member_count']
num = int(num) num = int(num)
if num > group_member_num: if num > group_member_num:

View File

@ -1,6 +1,6 @@
from models.bag_user import BagUser from models.bag_user import BagUser
from utils.utils import is_number, get_user_avatar 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 configs.path_config import IMAGE_PATH
from .model import RedbagUser from .model import RedbagUser
import random 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 = '恭喜发财 大吉大利'): async def generate_send_redbag_pic(user_id: int, msg: str = '恭喜发财 大吉大利'):
random_redbag = random.choice(os.listdir(f"{IMAGE_PATH}/prts/redbag_2")) 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}') redbag = BuildImage(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))) ava = BuildImage(65, 65, background=BytesIO(await get_user_avatar(user_id)))
await asyncio.get_event_loop().run_in_executor(None, ava.circle) 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.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) 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): async def _generate_open_redbag_pic(user_id: int, send_user_nickname: str, amount: int, text: str):
send_user_nickname += '的红包' send_user_nickname += '的红包'
amount = str(amount) amount = str(amount)
head = CreateImg(1000, 980, font_size=30, background=f'{IMAGE_PATH}/prts/redbag_12.png') head = BuildImage(1000, 980, font_size=30, background=f'{IMAGE_PATH}/prts/redbag_12.png')
size = CreateImg(0, 0, font_size=50).getsize(send_user_nickname) size = BuildImage(0, 0, font_size=50).getsize(send_user_nickname)
# QQ头像 # QQ头像
ava_bk = CreateImg(100 + size[0], 66, color='white', font_size=50) ava_bk = BuildImage(100 + size[0], 66, color='white', font_size=50)
ava = CreateImg(66, 66, background=BytesIO(await get_user_avatar(user_id))) ava = BuildImage(66, 66, background=BytesIO(await get_user_avatar(user_id)))
ava_bk.paste(ava) ava_bk.paste(ava)
ava_bk.text((100, 7), send_user_nickname) ava_bk.text((100, 7), send_user_nickname)
# ava_bk.show() # ava_bk.show()
ava_bk_w, ava_bk_h = ava_bk.size ava_bk_w, ava_bk_h = ava_bk.size
head.paste(ava_bk, (int((1000 - ava_bk_w) / 2), 300)) head.paste(ava_bk, (int((1000 - ava_bk_w) / 2), 300))
# 金额 # 金额
size = CreateImg(0, 0, font_size=150).getsize(amount) size = BuildImage(0, 0, font_size=150).getsize(amount)
price = CreateImg(size[0], size[1], font_size=150) price = BuildImage(size[0], size[1], font_size=150)
price.text((0, 0), amount, fill=(209, 171, 108)) price.text((0, 0), amount, fill=(209, 171, 108))
# 金币中文 # 金币中文
head.paste(price, (int((1000 - size[0]) / 2) - 50, 460)) head.paste(price, (int((1000 - size[0]) / 2) - 50, 460))

View File

@ -1,4 +1,6 @@
from configs.config import Config from configs.config import Config
from configs.path_config import IMAGE_PATH
from pathlib import Path
import nonebot import nonebot
@ -44,5 +46,16 @@ Config.add_plugin_config(
default_value=6, 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") nonebot.load_plugins("plugins/image_management")

View File

@ -31,12 +31,17 @@ __plugin_settings__ = {
delete_img = on_command("删除图片", priority=5, rule=to_me(), block=True) delete_img = on_command("删除图片", priority=5, rule=to_me(), block=True)
_path = Path(IMAGE_PATH) / "image_management"
@delete_img.args_parser @delete_img.args_parser
async def parse(bot: Bot, event: MessageEvent, state: T_State): async def parse(bot: Bot, event: MessageEvent, state: T_State):
if get_message_text(event.json()) in ["取消", "算了"]: if get_message_text(event.json()) in ["取消", "算了"]:
await delete_img.finish("已取消操作..", at_sender=True) await delete_img.finish("已取消操作..", at_sender=True)
if state["_current_key"] in ["path"]: 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("此目录不正确,请重新输入目录!") await delete_img.reject("此目录不正确,请重新输入目录!")
state[state["_current_key"]] = get_message_text(event.json()) state[state["_current_key"]] = get_message_text(event.json())
if state["_current_key"] == "id": if state["_current_key"] == "id":
@ -52,7 +57,11 @@ async def _(bot: Bot, event: MessageEvent, state: T_State):
args = raw_arg.split(" ") args = raw_arg.split(" ")
if args[0] in ["帮助"]: if args[0] in ["帮助"]:
await delete_img.finish(__plugin_usage__) 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["path"] = args[0]
state["id"] = args[1] 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("path", prompt="请输入要删除的目标图库?")
@delete_img.got("id", prompt="请输入要删除的图片id") @delete_img.got("id", prompt="请输入要删除的图片id")
async def arg_handle(bot: Bot, event: MessageEvent, state: T_State): async def arg_handle(bot: Bot, event: MessageEvent, state: T_State):
path = cn2py(state["path"])
img_id = state["id"] img_id = state["id"]
# path = IMAGE_PATH + path path = _path / cn2py(state["path"])
path = Path(IMAGE_PATH) / path if not path.exists() and (path.parent.parent / cn2py(state["path"])).exists():
temp = Path(IMAGE_PATH) / "temp" path = path.parent.parent / cn2py(state["path"])
temp = Path(TEMP_PATH)
max_id = len(os.listdir(path)) - 1 max_id = len(os.listdir(path)) - 1
if int(img_id) > max_id or int(img_id) < 0: if int(img_id) > max_id or int(img_id) < 0:
await delete_img.finish(f"Id超过上下限上限{max_id}", at_sender=True) await delete_img.finish(f"Id超过上下限上限{max_id}", at_sender=True)
try: try:
if os.path.exists(temp / "delete.jpg"): if (temp / "delete.jpg").exists():
os.remove(temp / "delete.jpg") (temp / "delete.jpg").unlink()
logger.info("删除图片 delete.jpg 成功") logger.info(f"删除{cn2py(state['path'])}图片 {img_id}.jpg 成功")
except Exception as e: except Exception as e:
logger.warning(f"删除图片 delete.jpg 失败 e{e}") logger.warning(f"删除图片 delete.jpg 失败 e{e}")
try: try:

View File

@ -31,6 +31,9 @@ __plugin_settings__ = {
move_img = on_command("移动图片", priority=5, rule=to_me(), block=True) move_img = on_command("移动图片", priority=5, rule=to_me(), block=True)
_path = Path(IMAGE_PATH) / "image_management"
@move_img.args_parser @move_img.args_parser
async def parse(bot: Bot, event: MessageEvent, state: T_State): async def parse(bot: Bot, event: MessageEvent, state: T_State):
if str(event.get_message()) in ["取消", "算了"]: 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是") @move_img.got("id", prompt="要移动的图片id是")
async def _(bot: Bot, event: MessageEvent, state: T_State): async def _(bot: Bot, event: MessageEvent, state: T_State):
img_id = state["id"] img_id = state["id"]
source_path = Path(IMAGE_PATH) / cn2py(state["source_path"]) source_path = _path / cn2py(state["source_path"])
destination_path = Path(IMAGE_PATH) / cn2py(state["destination_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) destination_path.mkdir(parents=True, exist_ok=True)
max_id = len(os.listdir(source_path)) - 1 max_id = len(os.listdir(source_path)) - 1
des_max_id = len(os.listdir(destination_path)) des_max_id = len(os.listdir(destination_path))

View File

@ -6,6 +6,7 @@ from services.log import logger
from nonebot.typing import T_State from nonebot.typing import T_State
from nonebot.adapters.cqhttp import Bot, MessageEvent, GroupMessageEvent from nonebot.adapters.cqhttp import Bot, MessageEvent, GroupMessageEvent
from utils.utils import FreqLimiter, cn2py from utils.utils import FreqLimiter, cn2py
from pathlib import Path
from configs.config import Config from configs.config import Config
from utils.manager import group_manager, withdraw_message_manager from utils.manager import group_manager, withdraw_message_manager
import random import random
@ -16,7 +17,7 @@ try:
except ModuleNotFoundError: except ModuleNotFoundError:
import json import json
__zx_plugin_name__ = "发送本地图库图片" __zx_plugin_name__ = "本地图库"
__plugin_usage__ = f""" __plugin_usage__ = f"""
usage usage
发送指定图库下的随机或指定id图片 发送指定图库下的随机或指定id图片
@ -37,49 +38,61 @@ __plugin_settings__ = {
"cmd": ["发送图片"] + Config.get_config("image_management", "IMAGE_DIR_LIST"), "cmd": ["发送图片"] + Config.get_config("image_management", "IMAGE_DIR_LIST"),
} }
__plugin_task__ = {"pa": "丢人爬"} __plugin_task__ = {"pa": "丢人爬"}
__plugin_resources__ = { __plugin_resources__ = {"pa": IMAGE_PATH}
"pa": IMAGE_PATH
} Config.add_plugin_config(
"_task",
"DEFAULT_PA",
True,
help_="被动 爬 进群默认开关状态",
default_value=True,
)
_flmt = FreqLimiter(1) _flmt = FreqLimiter(1)
cmd = set(Config.get_config("image_management", "IMAGE_DIR_LIST")) cmd = set(Config.get_config("image_management", "IMAGE_DIR_LIST"))
# print(cmd)
send_img = on_command("img", aliases=cmd, priority=5, block=True) send_img = on_command("img", aliases=cmd, priority=5, block=True)
pa = on_keyword({"丢人爬", "爪巴"}, priority=5, block=True) pa = on_keyword({"丢人爬", "爪巴"}, priority=5, block=True)
pa_reg = on_regex("^爬$", 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() @send_img.handle()
async def _(bot: Bot, event: MessageEvent, state: T_State): async def _(bot: Bot, event: MessageEvent, state: T_State):
img_id = get_message_text(event.json()) 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( if state["_prefix"]["raw_command"] in Config.get_config(
"image_management", "IMAGE_DIR_LIST" "image_management", "IMAGE_DIR_LIST"
): ):
if not os.path.exists(f"{IMAGE_PATH}/{path}/"): if not path.exists() and (path.parent.parent / cn2py(state["_prefix"]["raw_command"])).exists():
os.mkdir(f"{IMAGE_PATH}/{path}/") path = Path(IMAGE_PATH) / cn2py(state["_prefix"]["raw_command"])
length = len(os.listdir(IMAGE_PATH + path)) else:
path.mkdir(parents=True, exist_ok=True)
length = len(os.listdir(path))
if length == 0: if length == 0:
logger.warning(f"图库 {path} 为空,调用取消!") logger.warning(f'图库 {cn2py(state["_prefix"]["raw_command"])} 为空,调用取消!')
await send_img.finish("该图库中没有图片噢") await send_img.finish("该图库中没有图片噢")
index = img_id if img_id else str(random.randint(0, length - 1)) index = img_id if img_id else str(random.randint(0, length - 1))
if not is_number(index): if not is_number(index):
return return
if int(index) > length - 1 or int(index) < 0: if int(index) > length - 1 or int(index) < 0:
await send_img.finish(f"超过当前上下限!({length - 1})") await send_img.finish(f"超过当前上下限!({length - 1})")
result = image(f"{index}.jpg", path) result = image(path / f"{index}.jpg")
if result: if result:
logger.info( logger.info(
f"(USER {event.user_id}, GROUP " 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 + 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( withdraw_message_manager.withdraw_message(
event, event,
msg_id, msg_id,
@ -88,7 +101,8 @@ async def _(bot: Bot, event: MessageEvent, state: T_State):
else: else:
logger.info( logger.info(
f"(USER {event.user_id}, GROUP " 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|") await send_img.finish(f"不想给你看Ov|")

View File

@ -8,11 +8,16 @@ from utils.http_utils import AsyncHttpx
import os import os
_path = Path(IMAGE_PATH) / "image_management"
async def upload_image_to_local( 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: ) -> str:
_path = path _path_name = path_
path = Path(IMAGE_PATH) / cn2py(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) path.mkdir(parents=True, exist_ok=True)
img_id = len(os.listdir(path)) img_id = len(os.listdir(path))
failed_list = [] failed_list = []
@ -28,15 +33,15 @@ async def upload_image_to_local(
failed_result += str(img) + "\n" failed_result += str(img) + "\n"
logger.info( logger.info(
f"USER {user_id} GROUP {group_id}" 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: if failed_list:
return ( 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" f"依次的Id为{success_id[:-1]}\n上传失败:{failed_result[:-1]}\n{NICKNAME}感谢您对图库的扩充!WW"
) )
else: else:
return ( return (
f"这次一共为 {_path}库 添加了 {len(img_list)} 张图片\n依次的Id为" f"这次一共为 {_path_name}库 添加了 {len(img_list)} 张图片\n依次的Id为"
f"{success_id[:-1]}\n{NICKNAME}感谢您对图库的扩充!WW" f"{success_id[:-1]}\n{NICKNAME}感谢您对图库的扩充!WW"
) )

View File

@ -5,7 +5,7 @@ from nonebot.adapters.cqhttp import Bot, MessageEvent, GroupMessageEvent
from utils.message_builder import image from utils.message_builder import image
from services.log import logger from services.log import logger
from utils.utils import get_message_text from utils.utils import get_message_text
from utils.image_utils import CreateImg from utils.image_utils import BuildImage
__zx_plugin_name__ = "鲁迅说" __zx_plugin_name__ = "鲁迅说"
__plugin_usage__ = """ __plugin_usage__ = """
@ -31,7 +31,7 @@ __plugin_block_limit__ = {
luxun = on_command("鲁迅说过", aliases={"鲁迅说"}, priority=5, block=True) luxun = on_command("鲁迅说过", aliases={"鲁迅说"}, priority=5, block=True)
luxun_author = CreateImg(0, 0, plain_text="--鲁迅", font_size=30, font='msyh.ttf', font_color=(255, 255, 255)) luxun_author = BuildImage(0, 0, plain_text="--鲁迅", font_size=30, font='msyh.ttf', font_color=(255, 255, 255))
@luxun.handle() @luxun.handle()
@ -46,7 +46,7 @@ async def handle_event(bot: Bot, event: MessageEvent, state: T_State):
content = state["content"].strip() content = state["content"].strip()
if content.startswith(",") or content.startswith(""): if content.startswith(",") or content.startswith(""):
content = content[1:] content = content[1:]
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 = "" x = ""
if len(content) > 40: if len(content) > 40:
await luxun.finish('太长了,鲁迅说不完...') await luxun.finish('太长了,鲁迅说不完...')

View File

@ -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.utils import get_message_text, get_message_at, get_user_avatar
from utils.message_builder import image from utils.message_builder import image
import re import re
from utils.image_utils import CreateImg from utils.image_utils import BuildImage
__zx_plugin_name__ = "我有一个朋友" __zx_plugin_name__ = "我有一个朋友"
__plugin_usage__ = """ __plugin_usage__ = """
@ -60,13 +60,13 @@ async def _(bot: Bot, event: GroupMessageEvent, state: T_State):
msg = msg.replace("", "").replace("", "").replace("", "") msg = msg.replace("", "").replace("", "").replace("", "")
x = await get_user_avatar(qq) x = await get_user_avatar(qq)
if x: if x:
ava = CreateImg(200, 100, background=BytesIO(x)) ava = BuildImage(200, 100, background=BytesIO(x))
else: else:
ava = CreateImg(200, 100, color=(0, 0, 0)) ava = BuildImage(200, 100, color=(0, 0, 0))
ava.circle() ava.circle()
text = CreateImg(300, 30, font_size=30) text = BuildImage(300, 30, font_size=30)
text.text((0, 0), user_name) text.text((0, 0), user_name)
A = CreateImg(700, 150, font_size=25, color="white") A = BuildImage(700, 150, font_size=25, color="white")
A.paste(ava, (30, 25), True) A.paste(ava, (30, 25), True)
A.paste(text, (150, 38)) A.paste(text, (150, 38))
A.text((150, 85), msg, (125, 125, 125)) A.text((150, 85), msg, (125, 125, 125))

View File

@ -18,6 +18,7 @@ from .open_cases_c import (
open_shilian_case, open_shilian_case,
) )
from .utils import util_get_buff_price, util_get_buff_img, update_count_daily from .utils import util_get_buff_price, util_get_buff_img, update_count_daily
from configs.config import Config
__zx_plugin_name__ = "开箱" __zx_plugin_name__ = "开箱"
__plugin_usage__ = """ __plugin_usage__ = """
@ -84,6 +85,14 @@ __plugin_configs__ = {
"BUFF_PROXY": {"value": None, "help": "使用代理访问BUFF"}, "BUFF_PROXY": {"value": None, "help": "使用代理访问BUFF"},
} }
Config.add_plugin_config(
"_task",
"DEFAULT_OPEN_CASE_RESET_REMIND",
True,
help_="被动 每日开箱重置提醒 进群默认开关状态",
default_value=True,
)
cases_name = ["狂牙大行动", "突围大行动", "命悬一线", "裂空", "光谱"] cases_name = ["狂牙大行动", "突围大行动", "命悬一线", "裂空", "光谱"]
cases_matcher_group = MatcherGroup(priority=5, permission=GROUP, block=True) cases_matcher_group = MatcherGroup(priority=5, permission=GROUP, block=True)

View File

@ -10,7 +10,7 @@ import random
from .utils import get_price from .utils import get_price
from .models.buff_prices import BuffPrice from .models.buff_prices import BuffPrice
from PIL import Image 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 from configs.path_config import IMAGE_PATH
import asyncio import asyncio
from utils.utils import cn2py 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) skin_name += "".join(i)
# img = image(skin_name, "cases/" + case, "png") # img = image(skin_name, "cases/" + case, "png")
wImg = CreateImg(200, 270, 200, 200) wImg = BuildImage(200, 270, 200, 200)
wImg.paste( wImg.paste(
alpha2white_pil( alpha2white_pil(
Image.open(IMAGE_PATH + f"cases/{case}/{skin_name}.png").resize( 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( logger.warning(
f"USER {user_qq} GROUP {group} 开启{case_name}武器箱 {num} 次, 价格:{uplist[10]} 数据更新失败" 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: # for img in img_list:
# markImg.paste(img) # markImg.paste(img)
markImg = await asyncio.get_event_loop().run_in_executor( 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): 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: for img in img_list:
markImg.paste(img) markImg.paste(img)
return markImg return markImg
@ -399,7 +399,7 @@ async def my_knifes_name(user_id: int, group: int):
def _pst_my_knife(w, h, knifes_list): 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: for knife in knifes_list:
case = knife.split("||")[0] case = knife.split("||")[0]
knife = knife.split("||")[1] knife = knife.split("||")[1]
@ -415,7 +415,7 @@ def _pst_my_knife(w, h, knifes_list):
style=pypinyin.NORMAL, style=pypinyin.NORMAL,
): ):
skin_name += "".join(i) 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( knife_img.paste(
alpha2white_pil( alpha2white_pil(
Image.open(IMAGE_PATH + f"cases/{case}/{skin_name}.png").resize( Image.open(IMAGE_PATH + f"cases/{case}/{skin_name}.png").resize(

View File

@ -8,10 +8,10 @@ import os
from services.log import logger from services.log import logger
from utils.utils import get_bot from utils.utils import get_bot
from asyncio.exceptions import TimeoutError from asyncio.exceptions import TimeoutError
import pypinyin
from nonebot.adapters.cqhttp.exception import ActionFailed from nonebot.adapters.cqhttp.exception import ActionFailed
from configs.config import Config from configs.config import Config
from utils.manager import group_manager from utils.manager import group_manager
from .config import *
url = "https://buff.163.com/api/market/goods" url = "https://buff.163.com/api/market/goods"
# proxies = 'http://49.75.59.242:3128' # proxies = 'http://49.75.59.242:3128'

View File

@ -8,10 +8,11 @@ from nonebot.adapters.cqhttp.permission import GROUP
from bilibili_api import video from bilibili_api import video
from utils.message_builder import image from utils.message_builder import image
from nonebot.adapters.cqhttp.exception import ActionFailed 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 utils.browser import get_browser
from configs.path_config import IMAGE_PATH from configs.path_config import IMAGE_PATH
from utils.http_utils import AsyncHttpx from utils.http_utils import AsyncHttpx
from configs.config import Config
import asyncio import asyncio
import time import time
from bilibili_api import settings from bilibili_api import settings
@ -25,10 +26,18 @@ usage
B站转发解析解析b站分享信息支持bvbilibili链接b站手机端转发卡片cvb23.tv且5分钟内不解析相同url B站转发解析解析b站分享信息支持bvbilibili链接b站手机端转发卡片cvb23.tv且5分钟内不解析相同url
""".strip() """.strip()
__plugin_des__ = "B站转发解析" __plugin_des__ = "B站转发解析"
__plugin_type__ = ("被动相关",) __plugin_type__ = ("其他",)
__plugin_version__ = 0.1 __plugin_version__ = 0.1
__plugin_author__ = "HibiKier" __plugin_author__ = "HibiKier"
__plugin_task__ = {"bilibili_parse": "b站转发解析"} __plugin_task__ = {"bilibili_parse": "b站转发解析"}
Config.add_plugin_config(
"_task",
"DEFAULT_BILIBILI_PARSE",
True,
help_="被动 B站转发解析 进群默认开关状态",
default_value=True,
)
if get_local_proxy(): if get_local_proxy():
settings.proxy = 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): 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) A.save(path)

View File

@ -69,6 +69,8 @@ async def _(bot: Bot, event: MessageEvent, state: T_State):
data = (await AsyncHttpx.get(url, params=params, timeout=5)).json() data = (await AsyncHttpx.get(url, params=params, timeout=5)).json()
except TimeoutError: except TimeoutError:
pass pass
except Exception as e:
await pid_search.finish(f"发生了一些错误..{type(e)}{e}")
else: else:
if not data["width"] and not data["height"]: if not data["width"] and not data["height"]:
await pid_search.finish(f"没有搜索到 PID{pid} 的图片", at_sender=True) await pid_search.finish(f"没有搜索到 PID{pid} 的图片", at_sender=True)

View File

@ -57,6 +57,14 @@ Config.add_plugin_config(
default_value=10 default_value=10
) )
Config.add_plugin_config(
"pix",
"SHOW_INFO",
True,
help_="是否显示图片的基本信息如PID等",
default_value=True
)
nonebot.load_plugins("plugins/pix_gallery") nonebot.load_plugins("plugins/pix_gallery")

View File

@ -5,7 +5,7 @@ from asyncio.exceptions import TimeoutError
from .model.pixiv import Pixiv from .model.pixiv import Pixiv
from typing import List, Optional from typing import List, Optional
from utils.utils import change_pixiv_image_links 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 utils.http_utils import AsyncHttpx
from services.log import logger from services.log import logger
from configs.config import Config from configs.config import Config
@ -347,16 +347,16 @@ def gen_keyword_pic(
del img_data["_n_pid"] del img_data["_n_pid"]
del img_data["_n_uid"] del img_data["_n_uid"]
current_width = 0 current_width = 0
A = CreateImg(img_width, 1100) A = BuildImage(img_width, 1100)
for x in list(img_data.keys()): for x in list(img_data.keys()):
if img_data[x]["data"]: 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 start_index = 0
end_index = 40 end_index = 40
total_index = img_data[x]["width"] * 40 total_index = img_data[x]["width"] * 40
for _ in range(img_data[x]["width"]): for _ in range(img_data[x]["width"]):
tmp = CreateImg(198, 1100, font_size=20) tmp = BuildImage(198, 1100, font_size=20)
text_img = CreateImg(198, 100, font_size=50) text_img = BuildImage(198, 100, font_size=50)
key_str = "\n".join( key_str = "\n".join(
[key for key in img_data[x]["data"][start_index:end_index]] [key for key in img_data[x]["data"][start_index:end_index]]
) )
@ -370,7 +370,7 @@ def gen_keyword_pic(
end_index = ( end_index = (
end_index + 40 if end_index + 40 <= total_index else total_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)) background_img.paste(tmp, (1, 1))
img.paste(background_img) img.paste(background_img)
A.paste(img, (current_width, 0)) A.paste(img, (current_width, 0))

View File

@ -143,14 +143,17 @@ async def _(bot: Bot, event: MessageEvent, state: T_State):
uid = img.uid uid = img.uid
_img = await get_image(img_url, event.user_id) _img = await get_image(img_url, event.user_id)
if _img: if _img:
msg_id = await pix.send( if Config.get_config("pix", "SHOW_INFO"):
Message( msg_id = await pix.send(
f"title{title}\n" Message(
f"author{author}\n" f"title{title}\n"
f"PID{pid}\nUID{uid}\n" f"author{author}\n"
f"{image(_img, 'temp')}" f"PID{pid}\nUID{uid}\n"
f"{image(_img, 'temp')}"
)
) )
) else:
msg_id = await pix.send(image(_img, 'temp'))
logger.info( logger.info(
f"(USER {event.user_id}, GROUP {event.group_id if isinstance(event, GroupMessageEvent) else 'private'})" f"(USER {event.user_id}, GROUP {event.group_id if isinstance(event, GroupMessageEvent) else 'private'})"
f" 查看PIX图库PID: {pid}" f" 查看PIX图库PID: {pid}"

View File

@ -16,7 +16,7 @@ usage
戳一戳随机掉落语音或美图萝莉图 戳一戳随机掉落语音或美图萝莉图
""".strip() """.strip()
__plugin_des__ = "戳一戳发送语音美图萝莉图不美哉?" __plugin_des__ = "戳一戳发送语音美图萝莉图不美哉?"
__plugin_type__ = ("被动相关",) __plugin_type__ = ("其他",)
__plugin_version__ = 0.1 __plugin_version__ = 0.1
__plugin_author__ = "HibiKier" __plugin_author__ = "HibiKier"
__plugin_settings__ = { __plugin_settings__ = {

View File

@ -1,10 +1,10 @@
from .model import RussianUser from .model import RussianUser
from typing import Optional from typing import Optional
from utils.data_utils import init_rank 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_users = await RussianUser.get_all_user(group_id)
all_user_id = [user.user_qq for user in all_users] all_user_id = [user.user_qq for user in all_users]
if itype == 'win_rank': if itype == 'win_rank':

View File

@ -98,6 +98,11 @@ __plugin_configs__ = {
"value": 10, "value": 10,
"help": "色图下载超时限制(秒)", "help": "色图下载超时限制(秒)",
"default_value": 10 "default_value": 10
},
"SHOW_INFO": {
"value": True,
"help": "是否显示色图的基本信息如PID等",
"default_value": True
} }
} }
Config.add_plugin_config( Config.add_plugin_config(

View File

@ -183,13 +183,15 @@ def gen_message(setu_image: Setu, img_msg: bool = False) -> str:
title = setu_image.title title = setu_image.title
author = setu_image.author author = setu_image.author
pid = setu_image.pid pid = setu_image.pid
return ( if Config.get_config("send_setu", "SHOW_INFO"):
f"id{local_id}\n" return (
f"title{title}\n" f"id{local_id}\n"
f"author{author}\n" f"title{title}\n"
f"PID{pid}\n" f"author{author}\n"
f"{image(f'{local_id}', f'{r18_path if setu_image.is_r18 else path}') if img_msg else ''}" 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 ''}"
# 罗翔老师! # 罗翔老师!

View File

@ -1,5 +1,5 @@
from ..models.goods_info import GoodsInfo 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 utils.utils import is_number
from configs.path_config import IMAGE_PATH from configs.path_config import IMAGE_PATH
from configs.config import Config from configs.config import Config
@ -37,9 +37,9 @@ async def create_shop_help():
w = 1000 w = 1000
h = 400 + len(goods_lst) * 40 h = 400 + len(goods_lst) * 40
h = 1000 if h < 1000 else h h = 1000 if h < 1000 else h
shop_logo = CreateImg(100, 100, background=f"{IMAGE_PATH}/other/shop_text.png") shop_logo = BuildImage(100, 100, background=f"{IMAGE_PATH}/other/shop_text.png")
shop = CreateImg(w, h, font_size=20) shop = BuildImage(w, h, font_size=20)
zhenxun_img = CreateImg(525, 581, background=f"{IMAGE_PATH}/zhenxun/toukan_2.png") zhenxun_img = BuildImage(525, 581, background=f"{IMAGE_PATH}/zhenxun/toukan_2.png")
shop.paste(zhenxun_img, (780, 100)) shop.paste(zhenxun_img, (780, 100))
shop.paste(shop_logo, (450, 30), True) shop.paste(shop_logo, (450, 30), True)
shop.text( shop.text(

View File

@ -3,6 +3,7 @@ from .group_user_checkin import (
group_user_check, group_user_check,
group_impression_rank, group_impression_rank,
impression_rank, impression_rank,
check_in_all
) )
from nonebot.typing import T_State from nonebot.typing import T_State
from nonebot.adapters.cqhttp import Bot, GroupMessageEvent from nonebot.adapters.cqhttp import Bot, GroupMessageEvent
@ -45,26 +46,18 @@ __plugin_settings__ = {
} }
__plugin_cd_limit__ = {} __plugin_cd_limit__ = {}
__plugin_configs__ = { __plugin_configs__ = {
"MAX_SIGN_GOLD": { "MAX_SIGN_GOLD": {"value": 200, "help": "签到好感度加成额外获得的最大金币数", "default_value": 200},
"value": 200, "SIGN_CARD1_PROB": {"value": 0.2, "help": "签到好感度双倍加持卡Ⅰ掉落概率", "default_value": 0.2},
"help": "签到好感度加成额外获得的最大金币数",
"default_value": 200
},
"SIGN_CARD1_PROB": {
"value": 0.2,
"help": "签到好感度双倍加持卡Ⅰ掉落概率",
"default_value": 0.2
},
"SIGN_CARD2_PROB": { "SIGN_CARD2_PROB": {
"value": 0.09, "value": 0.09,
"help": "签到好感度双倍加持卡Ⅱ掉落概率", "help": "签到好感度双倍加持卡Ⅱ掉落概率",
"default_value": 0.09 "default_value": 0.09,
}, },
"SIGN_CARD3_PROB": { "SIGN_CARD3_PROB": {
"value": 0.05, "value": 0.05,
"help": "签到好感度双倍加持卡Ⅲ掉落概率", "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), await group_user_check_in(nickname, event.user_id, event.group_id),
at_sender=True, at_sender=True,
) )
if get_message_text(event.json()) == "all":
await check_in_all(nickname, event.user_id)
@my_sign.handle() @my_sign.handle()
@ -142,12 +138,12 @@ async def _(bot: Bot, event: GroupMessageEvent, state: T_State):
@scheduler.scheduled_job( @scheduler.scheduled_job(
'interval', "interval",
hours=1, hours=1,
) )
async def _(): async def _():
try: try:
clear_sign_data_pic() clear_sign_data_pic()
logger.info('清理日常签到图片数据数据完成....') logger.info("清理日常签到图片数据数据完成....")
except Exception as e: except Exception as e:
logger.error(f'清理日常签到图片数据数据失败..{type(e)}: {e}') logger.error(f"清理日常签到图片数据数据失败..{type(e)}: {e}")

View File

@ -4,7 +4,7 @@ from models.group_member_info import GroupInfoUser
from models.bag_user import BagUser from models.bag_user import BagUser
from configs.config import NICKNAME from configs.config import NICKNAME
from nonebot.adapters.cqhttp import MessageSegment 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 services.db_context import db
from .utils import get_card, SIGN_TODAY_CARD_PATH from .utils import get_card, SIGN_TODAY_CARD_PATH
from typing import Optional 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 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( async def _handle_check_in(
nickname: str, user_qq: int, group: int, present: datetime nickname: str, user_qq: int, group: int, present: datetime
) -> MessageSegment: ) -> 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) 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) user_qq_list, impression_list, _ = await SignGroupUser.get_all_impression(group)
return await init_rank("好感度排行榜", user_qq_list, impression_list, group, num) 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) count = math.ceil(lens / 33)
width = 10 width = 10
idx = 0 idx = 0
A = CreateImg(1740, 3300, color="#FFE4C4") A = BuildImage(1740, 3300, color="#FFE4C4")
for _ in range(count): 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): for _ in range(33 if int(lens / 33) >= 1 else lens % 33 - 1):
idx += 1 idx += 1
if idx > 100: 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] + "..." user_name = user_name if len(user_name) < 11 else user_name[:10] + "..."
ava = await get_user_avatar(user) ava = await get_user_avatar(user)
if ava: if ava:
ava = CreateImg( ava = BuildImage(
50, 50, background=BytesIO(ava) 50, 50, background=BytesIO(ava)
) )
else: else:
ava = CreateImg(50, 50, color="white") ava = BuildImage(50, 50, color="white")
ava.circle() 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}") font_w, font_h = bk.getsize(f"{idx}")
bk.text((5, int((100 - font_h) / 2)), f"{idx}.") bk.text((5, int((100 - font_h) / 2)), f"{idx}.")
bk.paste(ava, (55, int((100 - 50) / 2)), True) 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)) A.paste(col_img, (width, 0))
lens -= 33 lens -= 33
width += 580 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)) W.paste(A, (0, 260))
font_w, font_h = W.getsize(f"{NICKNAME}的好感度总榜") font_w, font_h = W.getsize(f"{NICKNAME}的好感度总榜")
W.text((int((1740 - font_w) / 2), int((260 - font_h) / 2)), f"{NICKNAME}的好感度总榜") W.text((int((1740 - font_w) / 2), int((260 - font_h) / 2)), f"{NICKNAME}的好感度总榜")

View File

@ -12,7 +12,7 @@ from models.sign_group_user import SignGroupUser
from models.group_member_info import GroupInfoUser from models.group_member_info import GroupInfoUser
from nonebot.adapters.cqhttp import MessageSegment from nonebot.adapters.cqhttp import MessageSegment
from utils.utils import get_user_avatar 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 utils.message_builder import image
from configs.config import NICKNAME from configs.config import NICKNAME
from pathlib import Path from pathlib import Path
@ -112,18 +112,18 @@ def _generate_card(
is_double: bool = False, is_double: bool = False,
is_card_view: bool = False, is_card_view: bool = False,
) -> MessageSegment: ) -> MessageSegment:
ava_bk = CreateImg(140, 140, is_alpha=True) ava_bk = BuildImage(140, 140, is_alpha=True)
ava_border = CreateImg( ava_border = BuildImage(
140, 140,
140, 140,
background=SIGN_BORDER_PATH / "ava_border_01.png", 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.circle()
ava_bk.paste(ava, center_type="center") ava_bk.paste(ava, center_type="center")
ava_bk.paste(ava_border, alpha=True, 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( level, next_impression, previous_impression = get_level_and_next_impression(
user.impression user.impression
) )
@ -131,8 +131,8 @@ def _generate_card(
info_img.text((0, 20), f"· {NICKNAME}对你的态度:{level2attitude[level]}") info_img.text((0, 20), f"· {NICKNAME}对你的态度:{level2attitude[level]}")
info_img.text((0, 40), f"· 距离升级还差 {next_impression - user.impression:.2f} 好感度") info_img.text((0, 40), f"· 距离升级还差 {next_impression - user.impression:.2f} 好感度")
bar_bk = CreateImg(220, 20, background=SIGN_RESOURCE_PATH / "bar_white.png") bar_bk = BuildImage(220, 20, background=SIGN_RESOURCE_PATH / "bar_white.png")
bar = CreateImg(220, 20, background=SIGN_RESOURCE_PATH / "bar.png") bar = BuildImage(220, 20, background=SIGN_RESOURCE_PATH / "bar.png")
bar_bk.paste( bar_bk.paste(
bar, bar,
( (
@ -150,7 +150,7 @@ def _generate_card(
font_size = 30 font_size = 30
if "好感度双倍加持卡" in gift: if "好感度双倍加持卡" in gift:
font_size = 20 font_size = 20
gift_border = CreateImg( gift_border = BuildImage(
270, 270,
100, 100,
background=SIGN_BORDER_PATH / "gift_border_02.png", background=SIGN_BORDER_PATH / "gift_border_02.png",
@ -158,20 +158,20 @@ def _generate_card(
) )
gift_border.text((0, 0), gift, center_type="center") gift_border.text((0, 0), gift, center_type="center")
bk = CreateImg( bk = BuildImage(
876, 876,
424, 424,
background=SIGN_BACKGROUND_PATH background=SIGN_BACKGROUND_PATH
/ random.choice(os.listdir(SIGN_BACKGROUND_PATH)), / random.choice(os.listdir(SIGN_BACKGROUND_PATH)),
font_size=25, font_size=25,
) )
A = CreateImg(876, 274, background=SIGN_RESOURCE_PATH / "white.png") A = BuildImage(876, 274, background=SIGN_RESOURCE_PATH / "white.png")
line = CreateImg(2, 180, color="black") line = BuildImage(2, 180, color="black")
A.transparent(2) A.transparent(2)
A.paste(ava_bk, (25, 80), True) A.paste(ava_bk, (25, 80), True)
A.paste(line, (200, 70)) A.paste(line, (200, 70))
nickname_img = CreateImg( nickname_img = BuildImage(
0, 0,
0, 0,
plain_text=nickname, plain_text=nickname,
@ -184,7 +184,7 @@ def _generate_card(
uid = uid[:4] + " " + uid[4:8] + " " + uid[8:] uid = uid[:4] + " " + uid[4:8] + " " + uid[8:]
else: else:
uid = "XXXX XXXX XXXX" uid = "XXXX XXXX XXXX"
uid_img = CreateImg( uid_img = BuildImage(
0, 0,
0, 0,
plain_text=f"UID: {uid}", plain_text=f"UID: {uid}",
@ -192,7 +192,7 @@ def _generate_card(
font_size=30, font_size=30,
font_color=(255, 255, 255), font_color=(255, 255, 255),
) )
sign_day_img = CreateImg( sign_day_img = BuildImage(
0, 0,
0, 0,
plain_text=f"{user.checkin_count}", plain_text=f"{user.checkin_count}",
@ -200,17 +200,17 @@ def _generate_card(
font_size=40, font_size=40,
font_color=(211, 64, 33), 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 0, 0, plain_text="当前", color=(255, 255, 255, 0), font_size=20
) )
lik_text2_img = CreateImg( lik_text2_img = BuildImage(
0, 0,
0, 0,
plain_text=f"好感度:{user.impression:.2f}", plain_text=f"好感度:{user.impression:.2f}",
color=(255, 255, 255, 0), color=(255, 255, 255, 0),
font_size=30, font_size=30,
) )
watermark = CreateImg( watermark = BuildImage(
0, 0,
0, 0,
plain_text=f"{NICKNAME}@{datetime.now().year}", plain_text=f"{NICKNAME}@{datetime.now().year}",
@ -218,15 +218,15 @@ def _generate_card(
font_size=15, font_size=15,
font_color=(155, 155, 155), 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: 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 0, 0, plain_text="", color=(255, 255, 255, 0), font_size=30
) )
if impression_list: if impression_list:
impression_list.sort(reverse=True) impression_list.sort(reverse=True)
index = impression_list.index(user.impression) index = impression_list.index(user.impression)
rank_img = CreateImg( rank_img = BuildImage(
0, 0,
0, 0,
plain_text=f"* 此群好感排名第 {index + 1}", plain_text=f"* 此群好感排名第 {index + 1}",
@ -247,7 +247,7 @@ def _generate_card(
_type = "view" _type = "view"
else: else:
A.paste(gift_border, (570, 140), True) 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 0, 0, plain_text="今日签到", color=(255, 255, 255, 0), font_size=30
) )
if is_double: if is_double:
@ -262,7 +262,7 @@ def _generate_card(
hour = current_date.hour hour = current_date.hour
minute = current_date.minute minute = current_date.minute
second = current_date.second second = current_date.second
data_img = CreateImg( data_img = BuildImage(
0, 0,
0, 0,
plain_text=f"时间:{data} {weekdays[week]} {hour}:{minute}:{second}", plain_text=f"时间:{data} {weekdays[week]} {hour}:{minute}:{second}",
@ -296,14 +296,14 @@ def generate_progress_bar_pic():
bg_2 = (254, 1, 254) bg_2 = (254, 1, 254)
bg_1 = (0, 245, 246) bg_1 = (0, 245, 246)
bk = CreateImg(1000, 50, is_alpha=True) bk = BuildImage(1000, 50, is_alpha=True)
img_x = CreateImg(50, 50, color=bg_2) img_x = BuildImage(50, 50, color=bg_2)
img_x.circle() img_x.circle()
img_x.crop((25, 0, 50, 50)) 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.circle()
img_y.crop((0, 0, 25, 50)) img_y.crop((0, 0, 25, 50))
A = CreateImg(950, 50) A = BuildImage(950, 50)
width, height = A.size width, height = A.size
step_r = (bg_2[0] - bg_1[0]) / width 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.paste(img_x, (975, 0), True)
bk.save(SIGN_RESOURCE_PATH / "bar.png") bk.save(SIGN_RESOURCE_PATH / "bar.png")
A = CreateImg(950, 50) A = BuildImage(950, 50)
bk = CreateImg(1000, 50, is_alpha=True) bk = BuildImage(1000, 50, is_alpha=True)
img_x = CreateImg(50, 50) img_x = BuildImage(50, 50)
img_x.circle() img_x.circle()
img_x.crop((25, 0, 50, 50)) img_x.crop((25, 0, 50, 50))
img_y = CreateImg(50, 50) img_y = BuildImage(50, 50)
img_y.circle() img_y.circle()
img_y.crop((0, 0, 25, 50)) img_y.crop((0, 0, 25, 50))
bk.paste(img_y, (0, 0), True) bk.paste(img_y, (0, 0), True)

View File

@ -5,7 +5,7 @@ from nonebot.typing import T_State
from pathlib import Path from pathlib import Path
from configs.path_config import DATA_PATH, IMAGE_PATH from configs.path_config import DATA_PATH, IMAGE_PATH
from utils.utils import get_message_text 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.message_builder import image
from utils.manager import plugins2settings_manager from utils.manager import plugins2settings_manager
import asyncio import asyncio
@ -202,7 +202,7 @@ async def generate_statistics_img(
except KeyError: except KeyError:
count.append(0) count.append(0)
week_lst = ["7" if i == "0" else i for i in week_lst] week_lst = ["7" if i == "0" else i for i in week_lst]
bar_graph = CreateMat( bar_graph = BuildMat(
y=count, y=count,
mat_type="line", mat_type="line",
title=f"{name}{plugin} 功能调用统计【为7天统计】", title=f"{name}{plugin} 功能调用统计【为7天统计】",
@ -226,7 +226,7 @@ async def generate_statistics_img(
day_lst.append(i) day_lst.append(i)
count = [data[str(day_lst[i])][plugin] for i in range(30)] count = [data[str(day_lst[i])][plugin] for i in range(30)]
day_lst = [str(x + 1) for x in day_lst] day_lst = [str(x + 1) for x in day_lst]
bar_graph = CreateMat( bar_graph = BuildMat(
y=count, y=count,
mat_type="line", mat_type="line",
title=f"{name}{plugin} 功能调用统计【为30天统计】", title=f"{name}{plugin} 功能调用统计【为30天统计】",
@ -246,12 +246,12 @@ async def generate_statistics_img(
return bar_graph.pic2bs4() 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) return await asyncio.get_event_loop().run_in_executor(None, _init_bar_graph, data, title)
def _init_bar_graph(data: dict, title: str) -> CreateMat: def _init_bar_graph(data: dict, title: str) -> BuildMat:
bar_graph = CreateMat( bar_graph = BuildMat(
y=[data[x] for x in data.keys() if data[x] != 0], y=[data[x] for x in data.keys() if data[x] != 0],
mat_type="barh", mat_type="barh",
title=title, title=title,

View File

@ -9,7 +9,7 @@ from nonebot.typing import T_State
from utils.utils import get_message_imgs from utils.utils import get_message_imgs
from pathlib import Path from pathlib import Path
from utils.utils import is_number, get_message_text 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 configs.config import NICKNAME
from utils.http_utils import AsyncHttpx from utils.http_utils import AsyncHttpx
import cv2 import cv2
@ -83,7 +83,7 @@ for i in range(len(method_list)):
method_oper.append(method_list[i]) method_oper.append(method_list[i])
method_oper.append(str(i + 1)) 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.text((10, 10), __plugin_usage__)
update_img_help.save(IMAGE_PATH + "update_img_help.png") update_img_help.save(IMAGE_PATH + "update_img_help.png")

View File

@ -4,7 +4,7 @@ from nonebot import on_command
from utils.utils import get_message_imgs, get_message_text, is_chinese from utils.utils import get_message_imgs, get_message_text, is_chinese
from utils.message_builder import image from utils.message_builder import image
from configs.path_config import TEMP_PATH 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 services.log import logger
from utils.http_utils import AsyncHttpx from utils.http_utils import AsyncHttpx
from pathlib import Path from pathlib import Path
@ -57,12 +57,12 @@ async def _(bot: Bot, event: MessageEvent, state: T_State):
): ):
await w2b_img.finish("下载图片失败...请稍后再试...") await w2b_img.finish("下载图片失败...请稍后再试...")
msg = await get_translate(msg) 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") w2b.convert("L")
msg_sp = msg.split("<|>") msg_sp = msg.split("<|>")
w, h = w2b.size w, h = w2b.size
add_h, font_size = init_h_font_size(h) 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) bg.paste(w2b)
chinese_msg = formalization_msg(msg) chinese_msg = formalization_msg(msg)
if not bg.check_font_size(chinese_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) top_h = img.h - add_h + (img.h / 100)
bottom_h = img.h - (img.h / 100) bottom_h = img.h - (img.h / 100)
text_sp = text.split("<|>") text_sp = text.split("<|>")

View File

@ -59,11 +59,11 @@ show_word = on_command("显示词条", aliases={"查看词条"}, priority=5, blo
@add_word.handle() @add_word.handle()
async def _(bot: Bot, event: GroupMessageEvent, state: T_State): async def _(bot: Bot, event: GroupMessageEvent, state: T_State):
msg = str(event.get_message()).strip() msg = str(event.get_message()).strip()
r = re.search(r"^问(.+)\s?答(.*)", msg) r = re.search(r"^问(.+)\s?答([\s\S]*)", msg)
if not r: if not r:
await add_word.finish("未检测到词条问题...") await add_word.finish("未检测到词条问题...")
problem = r.group(1).strip() problem = r.group(1).strip()
answer = r.group(2).strip() answer = msg.split('', maxsplit=1)[-1]
if not answer: if not answer:
await add_word.finish("未检测到词条回答...") await add_word.finish("未检测到词条回答...")
idx = 0 idx = 0

View File

@ -54,4 +54,4 @@ async def _(bot: Bot, event: MessageEvent, state: T_State):
f"{event.group_id if isinstance(event, GroupMessageEvent) else 'private'}) 查询疫情失败" f"{event.group_id if isinstance(event, GroupMessageEvent) else 'private'}) 查询疫情失败"
) )
else: else:
await yiqing.send(f"{NICKNAME}只支持国内的疫情查询喔...") await yiqing.send(f"{NICKNAME}没有查到{msg}的疫情查询...")

Binary file not shown.

After

Width:  |  Height:  |  Size: 37 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 34 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 31 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 41 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 32 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 35 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 39 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 37 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 42 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 36 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 34 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 32 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 38 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 30 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 37 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 34 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 39 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 31 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 39 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 34 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 36 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 36 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 39 KiB

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