mirror of
https://github.com/zhenxun-org/zhenxun_bot.git
synced 2026-09-30 01:00:02 +08:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
1ce4537605 | ||
|
|
701812ed9d | ||
|
|
8d9e81827b | ||
|
|
f51a2816b4 | ||
|
|
3d4a7950c5 | ||
|
|
ba6668f144 | ||
|
|
e1f7a77d15 | ||
|
|
64e2c512d3 | ||
|
|
6082e68a1f | ||
|
|
f135d48e55 | ||
|
|
751fc8fdb4 | ||
|
|
df73e79ee4 | ||
|
|
fa6cfdb609 | ||
|
|
b057a05520 | ||
|
|
cb0938721c | ||
|
|
4f3f8de734 | ||
|
|
5ba303aada | ||
|
|
bd42fefe4e | ||
|
|
d24c081973 | ||
|
|
1b9eddd905 | ||
|
|
0bfe398dc4 | ||
|
|
e9216a472f | ||
|
|
8b93ec0df6 | ||
|
|
7497706a0d | ||
|
|
6e41cb0630 | ||
|
|
62d99e7e90 | ||
|
|
43cddb3580 | ||
|
|
c9c1beda75 | ||
|
|
3ee9ae5fb8 | ||
|
|
ea6344fae1 | ||
|
|
6f54e91f5c | ||
|
|
27fb3aaa19 | ||
|
|
86d736745d | ||
|
|
821fe21c2b | ||
|
|
7f4aa518dc | ||
|
|
8cf589e433 | ||
|
|
105a29cd70 | ||
|
|
cb6a5e81e7 | ||
|
|
9c073383ad | ||
|
|
5e0756825d | ||
|
|
933523ae6d | ||
|
|
ed6b05605c | ||
|
|
71a9c34620 | ||
|
|
e29f2cb920 | ||
|
|
d47dd88d6c | ||
|
|
fb34bbce7a | ||
|
|
100236da83 | ||
|
|
a45a3002d5 | ||
|
|
9214a4c978 | ||
|
|
70761dc222 | ||
|
|
32e8136f71 | ||
|
|
8b20d99b2c | ||
|
|
1174b5cc7f | ||
|
|
ec448cfdfb | ||
|
|
f3e44b1baf | ||
|
|
0ea740aa7c | ||
|
|
5f118471cb | ||
|
|
504f78dd49 | ||
|
|
72723c7d1f | ||
|
|
6a74053853 | ||
|
|
af0b4987a4 | ||
|
|
775140cf79 | ||
|
|
6a8a796add | ||
|
|
8e4bda3760 | ||
|
|
a36e8145a8 | ||
|
|
3cbd899daa | ||
|
|
30f356a2c7 | ||
|
|
1138a115ce | ||
|
|
c397da75e7 | ||
|
|
1d141c9313 | ||
|
|
a4cc365af8 | ||
|
|
4e551bec0d | ||
|
|
e03086062d | ||
|
|
270a5a5e20 | ||
|
|
bda9133ed6 | ||
|
|
749f475f2c | ||
|
|
e38394948c | ||
|
|
51fc4cbb6a | ||
|
|
9a976cda37 | ||
|
|
e42521f0a3 | ||
|
|
0756d39ac5 | ||
|
|
4595bdcee4 | ||
|
|
38160065cc | ||
|
|
3d04f013cd | ||
|
|
8e1a416be3 | ||
|
|
a98bc42f1a | ||
|
|
6c93769838 | ||
|
|
d2dccfebc5 | ||
|
|
82fe59e469 | ||
|
|
afc05e1731 | ||
|
|
0206b0a522 | ||
|
|
0e1e5a5ebe | ||
|
|
9ad088ed6a | ||
|
|
34da04bba5 | ||
|
|
09ce812faf | ||
|
|
1dc8cb2a98 | ||
|
|
e73c509823 | ||
|
|
e70a85400f | ||
|
|
f7aecf8eed | ||
|
|
cc2645aa9d | ||
|
|
52d23c4a8d | ||
|
|
1e57ddb9ee | ||
|
|
7ae883d921 | ||
|
|
27e97214bd | ||
|
|
6321a9e7e0 | ||
|
|
a9eadca46c | ||
|
|
76bef8a725 | ||
|
|
308b76dc7d | ||
|
|
31d4d29f3e | ||
|
|
016a7e87ea | ||
|
|
4ce88c7bfc | ||
|
|
f570bffcf7 | ||
|
|
3bb9aaf637 | ||
|
|
cf7a64c934 | ||
|
|
c466603193 | ||
|
|
bc53f329a9 | ||
|
|
4a5cc5153f | ||
|
|
e70d0df949 | ||
|
|
fcaa469edd | ||
|
|
51508182ef | ||
|
|
27e78c1626 | ||
|
|
0b4d37a0b6 | ||
|
|
ddbebba6f1 | ||
|
|
7f296348e4 | ||
|
|
ca78e32d97 | ||
|
|
4efc561f4d | ||
|
|
3c120ce602 | ||
|
|
6151dfb161 | ||
|
|
f05d4ec931 | ||
|
|
a1308fbdc9 | ||
|
|
946fbbec3c | ||
|
|
bd531c85b1 | ||
|
|
cda9aae8a3 | ||
|
|
a33e785043 |
@@ -1,38 +0,0 @@
|
||||
name: Generate poetry.lock
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- "main"
|
||||
paths:
|
||||
- 'pyproject.toml'
|
||||
|
||||
jobs:
|
||||
poetry_lock:
|
||||
name: Generate
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v2
|
||||
- name: Set up Python
|
||||
uses: he0119/setup-python@main
|
||||
with:
|
||||
python-version: "3.9"
|
||||
- name: poetry lock
|
||||
run: |
|
||||
rm -f poetry.lock
|
||||
sed -i.bak "8,11d" pyproject.toml
|
||||
poetry lock
|
||||
mv pyproject.toml.bak pyproject.toml
|
||||
# commit
|
||||
- name: commit file
|
||||
run: |
|
||||
git config --local user.email "github-actions[bot]@users.noreply.github.com"
|
||||
git config --local user.name "github-actions[bot]"
|
||||
git add poetry.lock
|
||||
git commit -m "Auto generate poetry.lock" -a
|
||||
# push
|
||||
- name: push changes
|
||||
uses: ad-m/github-push-action@master
|
||||
with:
|
||||
github_token: ${{ secrets.GITHUB_TOKEN }}
|
||||
@@ -159,3 +159,5 @@ resources/
|
||||
!/configs/config.yaml
|
||||
!/.env
|
||||
!/.env.dev
|
||||
!/plugins/csgo_server/
|
||||
!/plugins/activity/
|
||||
|
||||
@@ -13,7 +13,9 @@
|
||||
|
||||
如果该项目的图片等等侵犯猫豆腐老师权益请联系我删除!
|
||||
|
||||
如果希望有个地方讨论绪山真寻Bot,或者有问题或建议,可以发送issues或加入[ <strong>[是真寻酱哒](https://jq.qq.com/?_wv=1027&k=u8PgBkMZ) </strong>]
|
||||
是新手!希望有个地方讨论绪山真寻Bot,或者有问题或建议,可以发送issues或加入[ <strong>[是真寻酱哒(萌新版)](https://jq.qq.com/?_wv=1027&k=u8PgBkMZ) </strong>]
|
||||
|
||||
[//]: # (是老手!讨论插件开发,nonebot2开发,可以加入[ <strong>[真寻酱的技术群](https://jq.qq.com/?_wv=1027&k=u8PgBkMZ) </strong>])
|
||||
|
||||
## 声明
|
||||
此项目仅用于学习交流,请勿用于非法用途
|
||||
@@ -252,6 +254,22 @@ PS: **ARM平台** 请使用全量版 同时 **如果你的机器 RAM < 1G 可能
|
||||
|
||||
### 感谢名单
|
||||
(可以告诉我你的 __github__ 地址,我偷偷换掉0v|)
|
||||
[Yui](https://afdian.net/u/870dc10a3cd311ed828852540025c377)
|
||||
[本喵无敌哒](https://afdian.net/u/dffaa9005bc911ebb69b52540025c377)
|
||||
[椎名冬羽](https://afdian.net/u/ca1ebd64395e11ed81b452540025c377)
|
||||
[kaito](https://afdian.net/u/a055e20a498811eab1f052540025c377)
|
||||
[笑柒XIAO_Q7](https://afdian.net/u/4696db5c529111ec84ea52540025c377)
|
||||
[请问一份爱多少钱](https://afdian.net/u/f57ef6602dbd11ed977f52540025c377)
|
||||
[咸鱼鱼鱼鱼](https://afdian.net/u/8e39b9a400e011ed9f4a52540025c377)
|
||||
[Kafka](https://afdian.net/u/41d66798ef6911ecbc5952540025c377)
|
||||
[墨然](https://afdian.net/u/8aa5874a644d11eb8a6752540025c377)
|
||||
[爱发电用户_T9e4](https://afdian.net/u/2ad1bb82f3a711eca22852540025c377)
|
||||
[笑柒XIAO_Q7](https://afdian.net/u/4696db5c529111ec84ea52540025c377)
|
||||
[noahzark](https://afdian.net/a/noahzark)
|
||||
[腊条](https://afdian.net/u/f739c4d69eca11eba94b52540025c377)
|
||||
[ze roller](https://afdian.net/u/0e599e96257211ed805152540025c377)
|
||||
[爱发电用户_4jrf](https://afdian.net/u/6b2cdcc817c611ed949152540025c377)
|
||||
[爱发电用户_TBsd](https://afdian.net/u/db638b60217911ed9efd52540025c377)
|
||||
[烟寒若雨](https://afdian.net/u/067bd2161eec11eda62b52540025c377)
|
||||
[ln](https://afdian.net/u/b51914ba1c6611ed8a4e52540025c377)
|
||||
[爱发电用户_b9S4](https://afdian.net/u/3d8f30581a2911edba6d52540025c377)
|
||||
@@ -259,7 +277,7 @@ PS: **ARM平台** 请使用全量版 同时 **如果你的机器 RAM < 1G 可能
|
||||
[爱发电用户_eNr9](https://afdian.net/u/05fdb41c0c9a11ed814952540025c377)
|
||||
[MangataAkihi](https://github.com/Sakuracio)
|
||||
[炀](https://afdian.net/u/69b76e9ec77b11ec874f52540025c377)
|
||||
[爱发电用户_Bc6j](https://afdian.net/u/8546be24f44111eca64052540025c377)
|
||||
[爱发电用户_Bc6j](https://afdian.net/u/8546be24f44111eca64052540025c377)
|
||||
[大魔王](https://github.com/xipesoy)
|
||||
[CopilotLaLaLa](https://github.com/CopilotLaLaLa)
|
||||
[嘿小欧](https://afdian.net/u/daa4bec4f24911ec82e552540025c377)
|
||||
@@ -275,13 +293,109 @@ PS: **ARM平台** 请使用全量版 同时 **如果你的机器 RAM < 1G 可能
|
||||
|
||||
## 更新
|
||||
|
||||
### 2022/10/16 \[v0.1.6.4]
|
||||
|
||||
* 修改商店道具icon可以为空
|
||||
|
||||
### 2022/10/15
|
||||
|
||||
* nonebot2版本更新为rc1
|
||||
* 我的道具改为图片形式
|
||||
* 商品添加图标与是否为被动道具(被动道具无法被主动使用)
|
||||
* 商品添加使用前方法和使用后方法(类似hook),使用方法具体查看文档或签到商品文件中注册的例子
|
||||
* 新增用户使用道具,花费金币(包括插件)及用途记录
|
||||
* 更细致的金币使用依赖注入
|
||||
* 更多的依赖注入(包含图片获取等等..
|
||||
* 修复我的道具仅有被动或主动道具时图片显示错误
|
||||
* 色图插件p站反向代理失效 [@pull/1139](https://github.com/HibiKier/zhenxun_bot/pull/1139)
|
||||
|
||||
### 2022/10/9
|
||||
|
||||
* 修复碧蓝档案角色获取问题,换源 [@pull/1124](https://github.com/HibiKier/zhenxun_bot/pull/1124)
|
||||
|
||||
### 2022/10/7
|
||||
|
||||
* 修复 B 站请求返回 -401 错误 [@pull/1119](https://github.com/HibiKier/zhenxun_bot/pull/1119)
|
||||
* 关闭功能与被动时不再区分大小写,同名时仅被动关闭操作生效
|
||||
|
||||
### 2022/9/30
|
||||
|
||||
* 修改重置开箱的使用权限 [@pull/1118](https://github.com/HibiKier/zhenxun_bot/pull/1118)
|
||||
|
||||
### 2022/9/27
|
||||
|
||||
* 更新b站转发解析 [@pull/1117](https://github.com/HibiKier/zhenxun_bot/pull/1117)
|
||||
|
||||
### 2022/9/24
|
||||
|
||||
* 修复b站订阅 [@pull/1112](https://github.com/HibiKier/zhenxun_bot/pull/1112)
|
||||
* fix: 重载赛马娘卡池失败 [@pull/1114](https://github.com/HibiKier/zhenxun_bot/pull/1114)
|
||||
|
||||
### 2022/9/19
|
||||
|
||||
* 更换bilibili_sub获取用户昵称用的API&尝试修了一下get_video() [@pull/1097](https://github.com/HibiKier/zhenxun_bot/pull/1097)
|
||||
* 修复csgo每日开箱可以多开一次
|
||||
|
||||
### 2022/9/18
|
||||
|
||||
* 修复 bilireq 版本过低导致 B 站视频解析错误 [@pull/1090](https://github.com/HibiKier/zhenxun_bot/pull/1096)
|
||||
|
||||
### 2022/9/16
|
||||
|
||||
* fix: bilibili_sub, azur_draw_card [@pull/1090](https://github.com/HibiKier/zhenxun_bot/pull/1090)
|
||||
* 修复原神资源查询查询完毕后图片存储错误
|
||||
* b站订阅发送 与 b站订阅 使用相同开关,即:关闭b站订阅
|
||||
|
||||
### 2022/9/10
|
||||
|
||||
* 自定义群欢迎消息参数不完全时提示报错
|
||||
* 修改bt插件的url地址 [@pull/1067](https://github.com/HibiKier/zhenxun_bot/pull/1067)
|
||||
|
||||
### 2022/9/8
|
||||
|
||||
* 添加插件数据初始化判断
|
||||
|
||||
### 2022/9/4
|
||||
|
||||
* 旧词条提供图片迁移(需要重新获取old_model文件,并将数据库中user_qq为0的数据删除)
|
||||
|
||||
### 2022/9/3
|
||||
|
||||
* 原神玩家查询增加须弥地区 [@pull/1053](https://github.com/HibiKier/zhenxun_bot/pull/1053)
|
||||
* av号覆盖全面,且修复av号链接 [@pull/1033](https://github.com/HibiKier/zhenxun_bot/pull/1033)
|
||||
* 修复词条含有CQ回答的模糊匹配无法被解析
|
||||
* 禁言检测图片在内存中获取图片hash
|
||||
* B站订阅在群里中任意群管理员可以统一管理(原来为管理员1无法删除管理员2的订阅)
|
||||
* 修复原神资源查询地图api数据变更导致更新的地图不完全
|
||||
|
||||
### 2022/8/27
|
||||
|
||||
* 修复签到积分双倍后,日志记录获得积分变4倍问题 [@pull/1044](https://github.com/HibiKier/zhenxun_bot/pull/1044)
|
||||
|
||||
### 2022/8/26
|
||||
|
||||
* 修复群管理员无法添加词条
|
||||
* 修复词条关键词"问"前空格问题
|
||||
|
||||
### 2022/8/23
|
||||
|
||||
* 修了下模糊匹配 issue#1026 [@pull/1026](https://github.com/HibiKier/zhenxun_bot/pull/1026)
|
||||
|
||||
### 2022/8/22
|
||||
|
||||
* 修复首次安装时词条旧表出错(因为根本就没有这张表!)
|
||||
* 取消配置替换定时任务,统一存储
|
||||
* 对米游社cookie进行判断,整合米游社签到信息 [@pull/1014](https://github.com/HibiKier/zhenxun_bot/pull/1014)
|
||||
* 修正尘歌壶和质变仪图片获取地址 [@pull/1010](https://github.com/HibiKier/zhenxun_bot/pull/1010)
|
||||
* 修复词库问答 **很多** 问题[@pull/1012](https://github.com/HibiKier/zhenxun_bot/pull/1012)
|
||||
|
||||
### 2022/8/21 \[v0.1.6.3]
|
||||
|
||||
* 重构群词条,改为词库Plus,增加 精准|模糊|正则 问题匹配,问题与回答均支持at,image,face,超级用户额外提供 全局|私聊 词库设置,数据迁移目前只提供了问题和回答都是纯文本的词条
|
||||
* 修复b站转发解析av号无法解析
|
||||
* B站订阅直播订阅支持短号
|
||||
* 开箱提供重置开箱命令,重置今日所有开箱数据(重置次数,并不会删除今日已开箱记录)
|
||||
* 提供全局字典GDict,通过from utils.manager import GDict导入
|
||||
* 提供全局字典GDict,通过from utils.utils import GDict导入
|
||||
* 适配omega 13w张图的数据结构表(建议删表重导)
|
||||
* 除首次启动外将配置替换加入单次定时任务,加快启动速度
|
||||
* fix: WordBank.check() [@pull/1008](https://github.com/HibiKier/zhenxun_bot/pull/1008)
|
||||
@@ -307,487 +421,12 @@ PS: **ARM平台** 请使用全量版 同时 **如果你的机器 RAM < 1G 可能
|
||||
|
||||
* 修复了原神自动签到返回invalid request的问题,新增查看我的cookie命令 [@pull/971](https://github.com/HibiKier/zhenxun_bot/pull/971)
|
||||
|
||||
### 2022/7/31
|
||||
|
||||
* 对nonebot.beta4中PluginMeta进行解析
|
||||
* 修改webui元祖类型无法正确转换类型
|
||||
* 查看订阅以图片形式发送
|
||||
* 修复pix无图时不会提醒而是出错
|
||||
|
||||
### 2022/7/30
|
||||
|
||||
* 替换了cos和bt的url [@pull/951](https://github.com/HibiKier/zhenxun_bot/pull/951)
|
||||
* 发言记录统计添加日消息统计 [@pull/953](https://github.com/HibiKier/zhenxun_bot/pull/953)
|
||||
|
||||
### 2022/7/24 \[v0.1.6.2]
|
||||
|
||||
* 订阅up动态提供直链
|
||||
|
||||
### 2022/7/23
|
||||
|
||||
* 优化开箱次数判断
|
||||
|
||||
### 2022/7/16
|
||||
|
||||
* cos提供连发命令
|
||||
* 修复B站视频解析中b23解析错误
|
||||
|
||||
### 2022/7/13
|
||||
|
||||
* B站订阅提供配置项`LIVE_MSG_AT_ALL`,`UP_MSG_AT_ALL`:控制UP动态投稿和直播是否AT全体
|
||||
* 修改了Omega图库检测的数据库文件名
|
||||
* game_utils提供了Game类
|
||||
|
||||
### 2022/7/7
|
||||
|
||||
* 微博热搜选择单条热搜时也会检测时效性 [@pull/891](https://github.com/HibiKier/zhenxun_bot/pull/891)
|
||||
|
||||
### 2022/7/4
|
||||
|
||||
* 修复商品未设置限购时无法购买
|
||||
* 修复B站订阅UP动态 [@pull/886](https://github.com/HibiKier/zhenxun_bot/pull/886)
|
||||
* 修复色图命令带tag时配置项`MAX_ONCE_NUM2FORWARD`无法生效
|
||||
|
||||
### 2022/7/3 \[v0.1.6.1]
|
||||
|
||||
* BagUser提供方法`check_props`方法,用于判断用户是否含有该道具并是否使用
|
||||
* 优化商品显示图片,提供限制,限购,折扣提示
|
||||
* 修复图库内图片无法被连续删除的问题 [@pull/879](https://github.com/HibiKier/zhenxun_bot/pull/879)
|
||||
* 色图提供配置项`MAX_ONCE_NUM2FORWARD`:群聊中单次发送图片数量达到指定时使用合并转发
|
||||
* 优化修复了商品修改命令
|
||||
* B站订阅UP动态改为详情页截图
|
||||
|
||||
### 2022/6/28
|
||||
|
||||
* 修复色图插件api修改无法搜索图片 [@pull/866](https://github.com/HibiKier/zhenxun_bot/pull/866)
|
||||
|
||||
### 2022/6/26
|
||||
|
||||
* 替换了bt URL
|
||||
* PIX当使用pid查询图片时,会发送该pid下所有图片
|
||||
* 商店提供了每日购买次数限制
|
||||
|
||||
### 2022/6/24
|
||||
|
||||
* 对WSL虚拟机 代理 添加写法 [@pull/847](https://github.com/HibiKier/zhenxun_bot/pull/847)
|
||||
|
||||
### 2022/6/21
|
||||
|
||||
* update parse_bilibili [@pull/840](https://github.com/HibiKier/zhenxun_bot/pull/840)
|
||||
|
||||
### 2022/6/19 \[v0.1.6.0]
|
||||
|
||||
* 暂时使用hook修复webui中plugins2setting修改时会改变plugins2setting.cmd为字符串
|
||||
* 修复原神树脂重复提醒的bug [@pull/828](https://github.com/HibiKier/zhenxun_bot/pull/828)
|
||||
* 词条优化问题展示,修改含机器人昵称和@逻辑 [@pull/835](https://github.com/HibiKier/zhenxun_bot/pull/835)
|
||||
|
||||
### 2022/6/18
|
||||
|
||||
* 修复昵称系统`BLACK_WORD`为空时造成报错
|
||||
* 修复特殊头像时背景透明化出错
|
||||
* 修复text2image纯文本时换行时颜色不统一
|
||||
* 优化webUI,当有插件出错时不会影响其他插件显示
|
||||
* 优化Config类型注释 [@pull/830](https://github.com/HibiKier/zhenxun_bot/pull/830)
|
||||
* 优化browser
|
||||
|
||||
### 2022/6/17
|
||||
|
||||
* 修复wordbank的一些小问题 @pull/820
|
||||
|
||||
### 2022/6/14
|
||||
|
||||
* AsyncHttpx提供可关闭 ssl 验证参数 [@pull/812](https://github.com/HibiKier/zhenxun_bot/pull/812)
|
||||
* fix: 自动更新群组 [@pull/810](https://github.com/HibiKier/zhenxun_bot/pull/810)
|
||||
* fix: 修复添加词条时对answer中图片的处理 [@pull/806](https://github.com/HibiKier/zhenxun_bot/pull/806)
|
||||
* 修复pix中指定num无法生效
|
||||
* 替换了bt URL
|
||||
|
||||
### 2022/6/13
|
||||
|
||||
* Windows 重启适配 [@pull/804](https://github.com/HibiKier/zhenxun_bot/pull/804)
|
||||
* 色图等提供修改md5方法 [@pull/800](https://github.com/HibiKier/zhenxun_bot/pull/800)
|
||||
* webui中token.json文件写入补充 [@pull/798](https://github.com/HibiKier/zhenxun_bot/pull/798)
|
||||
* 修复识图某些图片可能会出错
|
||||
|
||||
### 2022/6/11
|
||||
|
||||
* pix当只有一个tag时且为数字时可以通过添加'#'防止判定为数量
|
||||
|
||||
### 2022/6/9
|
||||
|
||||
* 修复b站订阅同群二人以上相同订阅时发送多次信息
|
||||
* 修复超级用户帮助中缺少了 ‘插件商店’ 相关帮助
|
||||
* 昵称系统提供了详细帮助
|
||||
|
||||
### 2022/6/5 \[v0.1.5.9]
|
||||
|
||||
* webui修复plugin2setting中cmd从list转变为str
|
||||
* 当命令`我的金币`被风控时将以图片形式发送
|
||||
* fix gold_redbag [@pull/763](https://github.com/HibiKier/zhenxun_bot/pull/763)
|
||||
* 金币红包功能增加更多封面 [@pull/764](https://github.com/HibiKier/zhenxun_bot/pull/764)
|
||||
|
||||
### 2022/6/3
|
||||
|
||||
* 插件仓库在已安装插件边上会提示\[已安装]
|
||||
* 修复ShopRegister kwargs某些字段无效
|
||||
* 调整了一下查看所有请求中的年龄绘制 [@pull/745](https://github.com/HibiKier/zhenxun_bot/pull/745)
|
||||
* 修复原神树脂提醒bug [@pull/756](https://github.com/HibiKier/zhenxun_bot/pull/756)
|
||||
|
||||
### 2022/5/31
|
||||
|
||||
* 修复开启/关闭全部功能时帮助图片未重绘 [@pull/721](https://github.com/HibiKier/zhenxun_bot/pull/721)
|
||||
* bot_friend_group将group_handle.修改为friend_handle. [@pull/711](https://github.com/HibiKier/zhenxun_bot/pull/711)
|
||||
* 修复发红包图片背景未透明化;修复原神树脂提醒参数错误 [@pull/712](https://github.com/HibiKier/zhenxun_bot/pull/712)
|
||||
* 修复抽卡插件:方舟更新UP池信息时,若公告的第一个池子过期会导致无法更新UP池信息 [@pull/707](https://github.com/HibiKier/zhenxun_bot/pull/707)
|
||||
* 商店插件判断是否有requirements.txt文件 [@pull/705](https://github.com/HibiKier/zhenxun_bot/pull/705)
|
||||
* 删除原神玩家查询api返回变更的多余键值
|
||||
* 优化了text2image方法
|
||||
|
||||
### 2022/5/29 \[v0.1.5.8]
|
||||
|
||||
* 提供了真寻适配仓库的插件 安装/卸载 操作
|
||||
* 暂时关闭了插件资源清空
|
||||
* 通过指令安装插件时会在插件目录下生成plugin_info.json记录当前插件信息
|
||||
|
||||
### 2022/5/28
|
||||
|
||||
* 修复私聊无法添加昵称
|
||||
* 修复原神玩家查询层岩巨渊地下矿区没开时报错
|
||||
* 修复 ```休息吧``` 无法阻断戳一戳
|
||||
* 当图库无图片时,戳一戳将略过发送图片
|
||||
* 新增搜图提供配置项```ALLOW_GROUP_R18```:允许在群聊中使用r18参数
|
||||
* 新增自动更新插件```UPDATE_REMIND```:新版本提醒,原配置项```AUTO_UPDATE_ZHENXUN```改为自动更新升级
|
||||
* black_word新增当群权限为-1时不再检测该群
|
||||
* 修复非超级用户绑定原神cookie会被black_word阻拦
|
||||
* 修复微博热搜报错,发红包小bug [@pull/688](https://github.com/HibiKier/zhenxun_bot/pull/688)
|
||||
* 更多的中文提示
|
||||
|
||||
### 2022/5/26
|
||||
|
||||
* 修复\[滴滴滴]会被转义成[滴滴滴]导致无法触发的问题
|
||||
* 将一些错误以中文提示输出
|
||||
* 更新BT搜索源地址 [@pull/668](https://github.com/HibiKier/zhenxun_bot/pull/668)
|
||||
* 更新抽卡插件 [@pull/667](https://github.com/HibiKier/zhenxun_bot/pull/667)
|
||||
|
||||
### 2022/5/25
|
||||
|
||||
* 修复webui中CountLimit字段limit_type类型错误
|
||||
* 修改nickname插件:一处文案错误,添加敏感词 [@pull/624](https://github.com/HibiKier/zhenxun_bot/pull/624)
|
||||
* gamedraw的ba卡池搬运了过来并且进行了真寻的适配 [@pull/617](https://github.com/HibiKier/zhenxun_bot/pull/617)
|
||||
* feat: stream downloading and progress [@pull/607](https://github.com/HibiKier/zhenxun_bot/pull/607)
|
||||
* 修改翻译插件,添加缺失的冒号 [@pull/602](https://github.com/HibiKier/zhenxun_bot/pull/602)
|
||||
* 修复碧蓝航线/明日方舟up池解析出错的问题 [@pull/610](https://github.com/HibiKier/zhenxun_bot/pull/602)
|
||||
|
||||
### 2022/5/24
|
||||
|
||||
* fix: 修正了签到时日期时间的显示不补齐零的bug (符合日期时间表示法 ISO 8601)[@pull/600](https://github.com/HibiKier/zhenxun_bot/pull/600)
|
||||
* 更新"微博热搜"接口 [@pull/579](https://github.com/HibiKier/zhenxun_bot/pull/579)
|
||||
* refactor&fix(manager): modify argument [@pull/576](https://github.com/HibiKier/zhenxun_bot/pull/576)
|
||||
* 修复复读不能复读图片的问题 [@pull/573](https://github.com/HibiKier/zhenxun_bot/pull/573)
|
||||
* 修复抽卡插件:方舟抽卡的抽取和显示问题 [@pull/581](https://github.com/HibiKier/zhenxun_bot/pull/581)
|
||||
|
||||
### 2022/5/23 \[v0.1.5.6]
|
||||
|
||||
* 修复"清除已删除插件数据" [@pull/545](https://github.com/HibiKier/zhenxun_bot/pull/545)
|
||||
* 修复有置顶的up主B站动态获取失败 [@pull/552](https://github.com/HibiKier/zhenxun_bot/pull/552)
|
||||
* 添加pixiv搜图多关键词支持;修复p站搜图数量参数问题 [@pull/441](https://github.com/HibiKier/zhenxun_bot/pull/441)
|
||||
* 修复开箱更新价格错误传参
|
||||
* 修复pix无法正确查询uid
|
||||
* 新增色图插件添加配置项```ALLOW_GROUP_R18```:允许群聊中使用色图r
|
||||
* 新增PIX插件添加配置项```ALLOW_GROUP_SETU```:允许非超级用户使用-s参数
|
||||
* 新增PIX插件添加配置项```ALLOW_GROUP_R18```:允许非超级用户使用-r参数
|
||||
|
||||
### 2022/5/22 \[v0.1.5.4]
|
||||
|
||||
* 使用action自动更新poetry.lock [@pull/515](https://github.com/HibiKier/zhenxun_bot/pull/515)
|
||||
* fix(bilibili_sub): card is None and timeout [@pull/516](https://github.com/HibiKier/zhenxun_bot/pull/516)
|
||||
* 修复了epic有时获取新免费游戏消息时获取不到图片
|
||||
* 修复好感度满时签到出错(虽然是不可能满的
|
||||
* 修复原神资源图标下载路径错误
|
||||
* 修复自动更新群组可能失败
|
||||
|
||||
### 2022/5/21
|
||||
|
||||
* 修复搜番无结果时报错无正确反馈
|
||||
* 解锁了windows上无法使用playwright的限制
|
||||
* 修复p搜对应pid有多张图时出错,改为连续发送图片
|
||||
* 修复p搜对数字的错误判断
|
||||
* 修复添加商品折扣无法正确添加
|
||||
* 修复了bilibili订阅直播间订阅up名称不一致的问题
|
||||
* 修复原神玩家查询没开地图时报错
|
||||
* 最低priority修改为 999
|
||||
* 修复刷屏检测失效
|
||||
* 修复刷屏检测设置命令无法生效
|
||||
* 优化刷屏显示设置禁言时长显示,并改为分钟
|
||||
* 修复了多连开箱无法指定武器箱
|
||||
* 修复识番链接无法正确获取
|
||||
* 新增真寻入群时即刻刷新权限
|
||||
* 提高了微博热搜截图的等待时间
|
||||
|
||||
### 2022/5/19
|
||||
|
||||
* fix: mihoyo bbs api changed [@pull/357](https://github.com/HibiKier/zhenxun_bot/pull/357)
|
||||
* Add word_clouds [@pull/265](https://github.com/HibiKier/zhenxun_bot/pull/265)
|
||||
* Fix wrong live streamer name [@pull/284](https://github.com/HibiKier/zhenxun_bot/pull/284)
|
||||
|
||||
### 2022/5/16
|
||||
|
||||
* 词条支持图片和@问题 [@pull/160](https://github.com/HibiKier/zhenxun_bot/pull/160)
|
||||
|
||||
### 2022/5/15
|
||||
|
||||
* 修复了商店商品无法正确添加
|
||||
* 修复了多张色图无法正确发送
|
||||
|
||||
### 2022/5/14
|
||||
|
||||
* 修复B站动态生成失败的问题 [@pull/159](https://github.com/HibiKier/zhenxun_bot/pull/159)
|
||||
|
||||
### 2022/5/11
|
||||
|
||||
* fix: 更改p搜api,解决p搜无法使用的问题 [@pull/155](https://github.com/HibiKier/zhenxun_bot/pull/155)
|
||||
|
||||
### 2022/5/9 \[v0.1.5.3]
|
||||
|
||||
* 替换了疫情API
|
||||
* 修复了私聊.ban/.unban出错
|
||||
|
||||
### 2022/5/5
|
||||
|
||||
* 修改bilibili_sub插件在windows平台下报错 [@pull/153](https://github.com/HibiKier/zhenxun_bot/pull/153)
|
||||
|
||||
### 2022/5/3 \[v0.1.5.2]
|
||||
|
||||
* 商品使用函数可以添加特定参数,例如:user_id, group_id, ShopParam等以及自己提供的参数
|
||||
* 添加商品注册装饰器shop_register
|
||||
* 修复商品函数kwargs无法获取参数值
|
||||
|
||||
|
||||
### 2022/5/1
|
||||
|
||||
* 删除了`group_last_chat`插件(该功能可由`chat_history`替代
|
||||
* 新增敏感词检测(全新反击系统,是时候重拳出击了
|
||||
|
||||
### 2022/4/26
|
||||
|
||||
* 修复了群白名单无法正确添加
|
||||
* 优化了管理员帮助图片,背景图层将位于最下层
|
||||
* 修复了树脂140时不断提醒(未测试
|
||||
* 新增了消息记录的消息排行
|
||||
* WebUI新增CPU,内存,磁盘监控
|
||||
* WebUI新增资源文件夹统计可视化
|
||||
|
||||
### 2022/4/12
|
||||
|
||||
* 修复b了命令私聊出错
|
||||
|
||||
### 2022/4/10 \[v0.1.4.7]
|
||||
|
||||
* 新增消息记录模块
|
||||
* 丰富处理请求操作提示
|
||||
* web ui新增配置项修改
|
||||
|
||||
### 2022/4/9
|
||||
|
||||
* fix: 更新问题,戳一戳图片路径问题 [@pull/144](https://github.com/HibiKier/zhenxun_bot/pull/144)
|
||||
|
||||
### 2022/4/8
|
||||
|
||||
* 修复原神玩家查询
|
||||
|
||||
### 2022/4/6
|
||||
|
||||
* update search_type [@pull/143](https://github.com/HibiKier/zhenxun_bot/pull/143)
|
||||
|
||||
### 2022/4/5 \[v0.1.4.6]
|
||||
|
||||
* 修复web修改插件后帮助图片生成错误
|
||||
|
||||
### 2022/4/4 \[v0.1.4.5]
|
||||
|
||||
* 替换了bt搜索URL
|
||||
* 优化使用playwright的相关代码
|
||||
* 原神玩家查询新增层岩巨渊探索
|
||||
* 修复原神便笺角色头像黑框
|
||||
* 修复同意群聊请求错误
|
||||
* 提供webui方面的api
|
||||
* 新增web-ui(前端简易管理页面插件)插件
|
||||
|
||||
### 2022/3/21
|
||||
|
||||
* 修复statistics_handle.py乱码
|
||||
|
||||
### 2022/3/18 \[v0.1.4.4]
|
||||
|
||||
* 修复戳一戳无法功能关闭与ban禁用
|
||||
* 新增图片搜索 search_image
|
||||
|
||||
### 2022/3/7
|
||||
|
||||
* 优化增删权限插件
|
||||
|
||||
### 2022/3/6
|
||||
|
||||
* 修复树脂提醒无法开启
|
||||
* 修复p搜图片路径错误
|
||||
|
||||
### 2022/3/3 \[v0.1.4.3]
|
||||
|
||||
* 修复手动同意群聊请求依旧退出
|
||||
|
||||
### 2022/3/1 \[v0.1.4.2]
|
||||
|
||||
* 0.1.4内容
|
||||
|
||||
### 2022/2/27 \[v0.1.4.1]
|
||||
|
||||
* 优化抽卡
|
||||
|
||||
### 2022/2/25 \[v0.1.4]
|
||||
|
||||
* PIX提供配置MAX_ONCE_NUM2FORWARD:当单次发送图片超过指定张数且在群聊时,将转为合并消息
|
||||
* 修复点歌无法正确发送
|
||||
* 修复我有一个朋友有时文本会包含CQ码
|
||||
* 修复群欢消息被动控制文本未删除 [@pull/124](https://github.com/HibiKier/zhenxun_bot/pull/124)
|
||||
* message_builder.image不再提供参数:abspath
|
||||
|
||||
### 2022/2/23
|
||||
|
||||
* 插件状态将从已加载插件模块中读取
|
||||
* 修复b站订阅插件订阅失败问题
|
||||
* 修复重启命令无法使用
|
||||
|
||||
### 2022/2/21 \[v0.1.3.2]
|
||||
|
||||
* 群权限为-1时,超级用户发送的命令依旧生效
|
||||
* 当群权限为-1时,被动技能也将不会发送
|
||||
* 修复功能开关,b站转发解析,复读 ignore无法使用
|
||||
* 修复色图下载文件名与路径错误
|
||||
* 修复被动技能提醒有时无法删除控制文本
|
||||
|
||||
|
||||
### 2022/2/20 \[v0.1.3.1]
|
||||
|
||||
* 修复pix下载临时文件目录错误
|
||||
* 修复AI,天气,发送图片ignore导致无法使用
|
||||
* 修复纯文本被动技能提醒有时无法删除控制文本
|
||||
|
||||
### 2022/2/19 \[v0.1.3] (nonebot beta2!)
|
||||
|
||||
* 由于nonebot升级版本,提供更新建议(__该次升级将会导致nonebot.beta1以下的插件无法使用__
|
||||
* 保证services,utils,configs,plugins,basic_plugins,文件夹均为最新
|
||||
* 根目录有pyproject.toml与poetry.lock
|
||||
* 执行命令:
|
||||
* pip3 install poetry
|
||||
* poetry install
|
||||
* poetry shell
|
||||
* playwright install chromium
|
||||
* python3 bot.py
|
||||
|
||||
|
||||
* 适配nonebot.beta2
|
||||
* 删除图片搜索 nonebot_plugin_picsearcher
|
||||
* 替换cos api
|
||||
* 原神签到树脂提醒新增绑定群里,在某群绑定uid就会在某群发送提醒信息(有好友则私聊,需要重新绑定uid
|
||||
* 修改update_info.json
|
||||
* 修复原神资源查询下载数据失败时导致报错
|
||||
* 优化BuildImage.circle()锯齿问题 [@pull/109](https://github.com/HibiKier/zhenxun_bot/pull/109)
|
||||
* epic restful 替换 [@pull/119](https://github.com/HibiKier/zhenxun_bot/pull/119)
|
||||
* fix: 修复远古时期残留的epic推送问题 [@pull/122](https://github.com/HibiKier/zhenxun_bot/pull/122)
|
||||
|
||||
### 2022/2/11
|
||||
|
||||
* 修复pix不使用反代无法下载图片
|
||||
|
||||
### 2022/2/10 \[v0.1.1]
|
||||
|
||||
* 修复购买道具出错
|
||||
|
||||
### 2022/2/9 \[v0.1]
|
||||
|
||||
* 新增原神自动签到和手动签到
|
||||
* 新增原神树脂提醒
|
||||
* 新增手动重载Config.yaml命令以及重载配置定时任务(极少部分帮助或配置可能需要重启
|
||||
* 修改了发送本地图库的matcher,改为on_message
|
||||
* register_use可以通过返回值发送消息
|
||||
* 修复修改商品时限制时间出错
|
||||
* 修复超时商品依旧可以被购买
|
||||
|
||||
### 2022/1/16 \[v0.0.9.0]
|
||||
|
||||
* Ai提供文本敏感词过滤器
|
||||
* 疫情插件适配新版腾讯API
|
||||
* 修复/t回复带空格切分
|
||||
* 修复原神玩家查询缺少渊下宫和稻妻家园以及角色不完全
|
||||
* 修复方法 text2image 中 padding 和 font 无法对纯文本生效
|
||||
* 修复签到图片中信息并未使用配置文件中的色图概率
|
||||
* 修改原神大地图合成方式,改为先压缩再合成
|
||||
* bag_user弃用字段props(该字段会在下次更新删除),使用新字段property
|
||||
* 数据库中所有belonging_group统一修改为group_id
|
||||
* 商店将registered_use和register_goods更名为register_use何register_goods
|
||||
* 商品注册提供了kwargs参数提供:
|
||||
* bot
|
||||
* event
|
||||
* 特殊字段
|
||||
* “send_success_msg”(发送成功的交互信息->即:使用道具 {name} {num} 次成功)
|
||||
* “_max_num_limit”(该道具单次使用的最多个数,默认1)
|
||||
|
||||
### 2022/1/5 \[v0.0.8.2]
|
||||
|
||||
* 提供金币消费hook,可在plugins2settings.yaml中配置该功能需要消费的金币
|
||||
* 商店插件将作为内置插件移动至basic_plugins
|
||||
* 商店插件通过export提供了方法,不需要修改商店插件代码添加商品数据和生效方法
|
||||
* 修改了hook插件顺序,主要以auth_hook为主
|
||||
* 修改商店图片样式
|
||||
* 取消每次启动更新城市列表(首次除外),采用定时更新,加快bot启动速度
|
||||
* 取消每次启动时截取今日素材,采用调用时截取保存,加快bot启动速度
|
||||
* 更新色图时当图片404时会删除并替换
|
||||
* 疫情消息回复改为图片
|
||||
* 修复商店折扣和限时时间无法生效
|
||||
* 修复原神玩家查询尘歌壶缺少图片
|
||||
|
||||
### 2021/12/26
|
||||
|
||||
* 修复群词条问题 空格 会被录入导致不断回复
|
||||
* 修复米游社app替换api导致无法正常查询
|
||||
|
||||
### 2021/12/24
|
||||
|
||||
* 支持国际疫情数据查询 [@pull/99](https://github.com/HibiKier/zhenxun_bot/pull/99)
|
||||
|
||||
### 2021/12/20
|
||||
|
||||
* 只有发布小于存储时间的新动态/视频的时候才获取并推送 [@pull/96](https://github.com/HibiKier/zhenxun_bot/pull/96)
|
||||
|
||||
### 2021/12/16 \[v0.0.7.0]
|
||||
|
||||
* 提供了真寻群聊功能总开关和对应默认配置项,命令:休息吧 醒来
|
||||
* 新增原神玩家查询,原神便笺查询
|
||||
* 群功能管理提供全部开启/关闭命令:开启/关闭全部功能
|
||||
* 提供主要数据自动备份,且提供自定义配置项
|
||||
* 提供命令:关于,用于介绍Bot之类的
|
||||
* 新增命令exec,用于执行sql语句
|
||||
* 签到提供参数 "all",用于签到所有群聊
|
||||
* Ban提醒提供cd
|
||||
* 本地图库提供配置项SHOW_ID,用于设置发送图片时是否显示id
|
||||
* 色图和PIX提供配置项SHOW_INFO,用于设置发送图片时是否显示图片信息
|
||||
* 所有被动技能提供了进群默认状态配置项
|
||||
* 修复添加权限第二种添加形式无法正确添加正确的权限
|
||||
* 修复签到获取好感度卡时金币不会增加
|
||||
* 修复当红包数量不合法时依旧扣除金币
|
||||
* 修复金币红包再次使用塞红包时无法正确退回上次未开完的金币
|
||||
* 修复 滴滴滴- 只包含图片时不会发送至管理员
|
||||
* 修复添加权限等级错误
|
||||
* 修复群词条以bot名称为开头时无法正确触发
|
||||
* 修改了权限插件加载顺序防止小概率优先加载权限插件引起报错
|
||||
* 本地图库新图库会统一建立在resource/img/image_management文件夹下,如果该文件夹内未找到图库,会从上级目录查找(即:resource/img/)
|
||||
|
||||
<br>
|
||||
|
||||
__..... 更多更新信息请查看文档__
|
||||
|
||||
## Todo
|
||||
- [ ] web管理
|
||||
- [x] web管理
|
||||
|
||||
## 感谢
|
||||
[botuniverse / onebot](https://github.com/botuniverse/onebot) :超棒的机器人协议
|
||||
|
||||
+1
-1
@@ -1 +1 @@
|
||||
__version__: v0.1.6.3
|
||||
__version__: v0.1.6.4
|
||||
|
||||
@@ -87,13 +87,13 @@ task_data = None
|
||||
|
||||
|
||||
async def change_group_switch(cmd: str, group_id: int, is_super: bool = False):
|
||||
global task_data
|
||||
"""
|
||||
修改群功能状态
|
||||
:param cmd: 功能名称
|
||||
:param group_id: 群号
|
||||
:param is_super: 是否位超级用户,超级用户用于私聊开关功能状态
|
||||
"""
|
||||
global task_data
|
||||
if not task_data:
|
||||
task_data = group_manager.get_task_data()
|
||||
group_help_file = DATA_PATH / "group_help" / f"{group_id}.png"
|
||||
@@ -121,9 +121,9 @@ async def change_group_switch(cmd: str, group_id: int, is_super: bool = False):
|
||||
if group_help_file.exists():
|
||||
group_help_file.unlink()
|
||||
return f"已 {status} 全部功能!"
|
||||
if cmd in [task_data[x] for x in task_data.keys()]:
|
||||
if cmd.lower() in [task_data[x].lower() for x in task_data.keys()]:
|
||||
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].lower() == cmd.lower()]
|
||||
for module in modules:
|
||||
if is_super:
|
||||
module = f"{module}:super"
|
||||
|
||||
@@ -36,11 +36,11 @@ custom_welcome = on_command(
|
||||
|
||||
@custom_welcome.handle()
|
||||
async def _(event: GroupMessageEvent, arg: Message = CommandArg()):
|
||||
msg = arg.extract_plain_text().strip()
|
||||
img = get_message_img(event.json())
|
||||
if not msg and not img:
|
||||
await custom_welcome.finish(__plugin_usage__)
|
||||
try:
|
||||
msg = arg.extract_plain_text().strip()
|
||||
img = get_message_img(event.json())
|
||||
if not msg and not img:
|
||||
await custom_welcome.finish(__plugin_usage__)
|
||||
await custom_welcome.send(
|
||||
await custom_group_welcome(msg, img, event.user_id, event.group_id),
|
||||
at_sender=True,
|
||||
|
||||
@@ -60,7 +60,7 @@ async def _():
|
||||
try:
|
||||
await bot.send_group_msg(group_id=g, message="[[_task|zwa]]早上好" + result)
|
||||
except ActionFailed:
|
||||
logger.warning(f"{g} 群被禁言中,无法发送早安")
|
||||
logger.warning(f"{g} 发送早安失败")
|
||||
except Exception as e:
|
||||
logger.error(f"早晚安错误 e:{e}")
|
||||
|
||||
@@ -83,7 +83,7 @@ async def _():
|
||||
group_id=g, message=f"[[_task|zwa]]{NICKNAME}要睡觉了,你们也要早点睡呀" + result
|
||||
)
|
||||
except ActionFailed:
|
||||
logger.warning(f"{g} 群被禁言中,无法发送晚安")
|
||||
logger.warning(f"{g} 发送晚安失败")
|
||||
except Exception as e:
|
||||
logger.error(f"早晚安错误 e:{e}")
|
||||
|
||||
|
||||
@@ -56,10 +56,10 @@ def _create_help_img(
|
||||
for matcher in _matchers:
|
||||
plugin_name = None
|
||||
_plugin = matcher.plugin
|
||||
metadata = _plugin.metadata
|
||||
if not _plugin:
|
||||
logger.warning(f"获取 功能:{matcher.plugin_name} 失败...")
|
||||
continue
|
||||
metadata = _plugin.metadata
|
||||
_module = _plugin.module
|
||||
try:
|
||||
plugin_name = metadata.name if metadata else _module.__getattribute__("__zx_plugin_name__")
|
||||
|
||||
@@ -3,6 +3,7 @@ from nonebot.message import run_preprocessor, run_postprocessor, IgnoredExceptio
|
||||
from models.friend_user import FriendUser
|
||||
from models.group_member_info import GroupInfoUser
|
||||
from models.bag_user import BagUser
|
||||
from models.user_shop_gold_log import UserShopGoldLog
|
||||
from utils.manager import (
|
||||
plugins2settings_manager,
|
||||
admin_manager,
|
||||
@@ -59,6 +60,7 @@ async def _(matcher: Matcher, bot: Bot, event: Event, state: T_State):
|
||||
if await BagUser.get_gold(event.user_id, event.group_id) < cost_gold:
|
||||
await send_msg(f"金币不足..该功能需要{cost_gold}金币..", bot, event)
|
||||
raise IgnoredException(f"{module} 金币限制...")
|
||||
await UserShopGoldLog.add_shop_log(event.user_id, event.group_id, 2, matcher.plugin_name, cost_gold, 1)
|
||||
# 当插件不阻塞超级用户时,超级用户提前扣除金币
|
||||
if (
|
||||
str(event.user_id) in bot.config.superusers
|
||||
|
||||
@@ -27,6 +27,8 @@ def init_plugins_config(data_path):
|
||||
# 优先使用 metadata 数据
|
||||
for matcher in _matchers:
|
||||
_plugin = matcher.plugin
|
||||
if not _plugin:
|
||||
continue
|
||||
metadata = _plugin.metadata
|
||||
try:
|
||||
_module = _plugin.module
|
||||
@@ -108,16 +110,16 @@ def init_plugins_config(data_path):
|
||||
_data, wf, indent=2, Dumper=yaml.RoundTripDumper, allow_unicode=True
|
||||
)
|
||||
user_config_file = Path() / "configs" / "config.yaml"
|
||||
if not user_config_file.exists():
|
||||
_replace_config()
|
||||
else:
|
||||
logger.info('五分钟后将进行配置数据替换,请注意...')
|
||||
scheduler.add_job(
|
||||
_replace_config,
|
||||
"date",
|
||||
run_date=datetime.now() + timedelta(minutes=5),
|
||||
id=f"_replace_config"
|
||||
)
|
||||
# if not user_config_file.exists():
|
||||
_replace_config()
|
||||
# else:
|
||||
# logger.info('五分钟后将进行配置数据替换,请注意...')
|
||||
# scheduler.add_job(
|
||||
# _replace_config,
|
||||
# "date",
|
||||
# run_date=datetime.now() + timedelta(minutes=5),
|
||||
# id=f"_replace_config"
|
||||
# )
|
||||
|
||||
|
||||
def _replace_config():
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
from pathlib import Path
|
||||
|
||||
from ruamel.yaml import YAML
|
||||
from utils.manager import plugins_manager
|
||||
from utils.utils import get_matchers
|
||||
import nonebot
|
||||
from services.log import logger
|
||||
|
||||
try:
|
||||
import ujson as json
|
||||
@@ -25,6 +25,8 @@ def init_plugins_data(data_path):
|
||||
_matchers = get_matchers(True)
|
||||
for matcher in _matchers:
|
||||
_plugin = matcher.plugin
|
||||
if not _plugin:
|
||||
continue
|
||||
metadata = _plugin.metadata
|
||||
try:
|
||||
_module = _plugin.module
|
||||
@@ -41,54 +43,57 @@ def init_plugins_data(data_path):
|
||||
matcher.plugin_name, "version", plugin_data.get("version")
|
||||
)
|
||||
else:
|
||||
plugin_version = None
|
||||
if metadata:
|
||||
plugin_version = metadata.extra.get("version")
|
||||
if not plugin_version:
|
||||
try:
|
||||
plugin_version = None
|
||||
if metadata:
|
||||
plugin_version = metadata.extra.get("version")
|
||||
if not plugin_version:
|
||||
try:
|
||||
plugin_version = _module.__getattribute__("__plugin_version__")
|
||||
except AttributeError:
|
||||
pass
|
||||
if metadata:
|
||||
plugin_name = metadata.name
|
||||
else:
|
||||
try:
|
||||
plugin_name = _module.__getattribute__("__zx_plugin_name__")
|
||||
except AttributeError:
|
||||
plugin_name = matcher.plugin_name
|
||||
plugin_author = None
|
||||
if metadata:
|
||||
plugin_author = metadata.extra.get('author')
|
||||
try:
|
||||
plugin_version = _module.__getattribute__("__plugin_version__")
|
||||
plugin_author = _module.__getattribute__("__plugin_author__")
|
||||
except AttributeError:
|
||||
pass
|
||||
if metadata:
|
||||
plugin_name = metadata.name
|
||||
else:
|
||||
try:
|
||||
plugin_name = _module.__getattribute__("__zx_plugin_name__")
|
||||
except AttributeError:
|
||||
plugin_name = matcher.plugin_name
|
||||
plugin_author = None
|
||||
if metadata:
|
||||
plugin_author = metadata.extra.get('author')
|
||||
try:
|
||||
plugin_author = _module.__getattribute__("__plugin_author__")
|
||||
except AttributeError:
|
||||
pass
|
||||
if matcher.plugin_name in plugins_manager.keys():
|
||||
plugins_manager.set_module_data(matcher.plugin_name, "error", False)
|
||||
if matcher.plugin_name not in plugins_manager.keys():
|
||||
plugins_manager.add_plugin_data(
|
||||
matcher.plugin_name,
|
||||
plugin_name=plugin_name,
|
||||
author=plugin_author,
|
||||
version=plugin_version,
|
||||
)
|
||||
# metadata不检测version
|
||||
elif isinstance(plugin_version, str) or plugins_manager[matcher.plugin_name]["version"] is None or (
|
||||
plugin_version is not None
|
||||
and plugin_version > plugins_manager[matcher.plugin_name]["version"]
|
||||
):
|
||||
plugins_manager.set_module_data(
|
||||
matcher.plugin_name, "plugin_name", plugin_name
|
||||
)
|
||||
plugins_manager.set_module_data(matcher.plugin_name, "author", plugin_author)
|
||||
plugins_manager.set_module_data(
|
||||
matcher.plugin_name, "version", plugin_version
|
||||
)
|
||||
if matcher.plugin_name in _data.keys():
|
||||
plugins_manager.set_module_data(
|
||||
matcher.plugin_name, "error", _data[matcher.plugin_name]["error"]
|
||||
)
|
||||
plugins_manager.set_module_data(
|
||||
matcher.plugin_name, "plugin_name", _data[matcher.plugin_name]["plugin_name"]
|
||||
)
|
||||
if matcher.plugin_name in plugins_manager.keys():
|
||||
plugins_manager.set_module_data(matcher.plugin_name, "error", False)
|
||||
if matcher.plugin_name not in plugins_manager.keys():
|
||||
plugins_manager.add_plugin_data(
|
||||
matcher.plugin_name,
|
||||
plugin_name=plugin_name,
|
||||
author=plugin_author,
|
||||
version=plugin_version,
|
||||
)
|
||||
# metadata不检测version
|
||||
elif isinstance(plugin_version, str) or plugins_manager[matcher.plugin_name]["version"] is None or (
|
||||
plugin_version is not None
|
||||
and plugin_version > float(plugins_manager[matcher.plugin_name]["version"])
|
||||
):
|
||||
plugins_manager.set_module_data(
|
||||
matcher.plugin_name, "plugin_name", plugin_name
|
||||
)
|
||||
plugins_manager.set_module_data(matcher.plugin_name, "author", plugin_author)
|
||||
plugins_manager.set_module_data(
|
||||
matcher.plugin_name, "version", plugin_version
|
||||
)
|
||||
if matcher.plugin_name in _data.keys():
|
||||
plugins_manager.set_module_data(
|
||||
matcher.plugin_name, "error", _data[matcher.plugin_name]["error"]
|
||||
)
|
||||
plugins_manager.set_module_data(
|
||||
matcher.plugin_name, "plugin_name", _data[matcher.plugin_name]["plugin_name"]
|
||||
)
|
||||
except Exception as e:
|
||||
logger.error(f"插件数据 {matcher.plugin_name} 加载发生错误 {type(e)}:{e}")
|
||||
plugins_manager.save()
|
||||
|
||||
@@ -34,82 +34,87 @@ def init_plugins_settings(data_path: str):
|
||||
logger.warning(f"配置文件 模块:{x} 获取 plugin_name 失败...{e}")
|
||||
_tmp_module[x] = ""
|
||||
for matcher in _matchers:
|
||||
if matcher.plugin_name not in plugins2settings_manager.keys():
|
||||
_plugin = matcher.plugin
|
||||
metadata = _plugin.metadata
|
||||
try:
|
||||
_module = _plugin.module
|
||||
except AttributeError:
|
||||
logger.warning(f"插件 {matcher.plugin_name} 加载失败...,插件控制未加载.")
|
||||
else:
|
||||
try:
|
||||
if matcher.plugin_name not in plugins2settings_manager.keys():
|
||||
_plugin = matcher.plugin
|
||||
if not _plugin:
|
||||
continue
|
||||
metadata = _plugin.metadata
|
||||
try:
|
||||
if metadata:
|
||||
plugin_name = metadata.name
|
||||
_module = _plugin.module
|
||||
except AttributeError:
|
||||
logger.warning(f"插件 {matcher.plugin_name} 加载失败...,插件控制未加载.")
|
||||
else:
|
||||
try:
|
||||
if metadata:
|
||||
plugin_name = metadata.name
|
||||
else:
|
||||
plugin_name = _module.__getattribute__("__zx_plugin_name__")
|
||||
if "[admin]" in plugin_name.lower():
|
||||
try:
|
||||
admin_settings = _module.__getattribute__(
|
||||
"__plugin_settings__"
|
||||
)
|
||||
level = admin_settings["admin_level"]
|
||||
cmd = admin_settings.get("cmd")
|
||||
except (AttributeError, KeyError):
|
||||
level = 5
|
||||
cmd = None
|
||||
if level is None:
|
||||
level = 5
|
||||
admin_manager.add_admin_plugin_settings(
|
||||
matcher.plugin_name, cmd, level
|
||||
)
|
||||
if (
|
||||
"[hidden]" in plugin_name.lower()
|
||||
or "[admin]" in plugin_name.lower()
|
||||
or "[superuser]" in plugin_name.lower()
|
||||
or matcher.plugin_name in plugins2settings_manager.keys()
|
||||
):
|
||||
continue
|
||||
except AttributeError:
|
||||
if matcher.plugin_name not in _tmp:
|
||||
logger.warning(
|
||||
f"获取插件 {matcher.plugin_name} __zx_plugin_name__ 失败...,插件控制未加载."
|
||||
)
|
||||
else:
|
||||
plugin_name = _module.__getattribute__("__zx_plugin_name__")
|
||||
if "[admin]" in plugin_name.lower():
|
||||
_tmp_module[matcher.plugin_name] = plugin_name
|
||||
try:
|
||||
admin_settings = _module.__getattribute__(
|
||||
plugin_settings = _module.__getattribute__(
|
||||
"__plugin_settings__"
|
||||
)
|
||||
level = admin_settings["admin_level"]
|
||||
cmd = admin_settings.get("cmd")
|
||||
except (AttributeError, KeyError):
|
||||
level = 5
|
||||
cmd = None
|
||||
if level is None:
|
||||
level = 5
|
||||
admin_manager.add_admin_plugin_settings(
|
||||
matcher.plugin_name, cmd, level
|
||||
)
|
||||
if (
|
||||
"[hidden]" in plugin_name.lower()
|
||||
or "[admin]" in plugin_name.lower()
|
||||
or "[superuser]" in plugin_name.lower()
|
||||
or matcher.plugin_name in plugins2settings_manager.keys()
|
||||
):
|
||||
continue
|
||||
except AttributeError:
|
||||
if matcher.plugin_name not in _tmp:
|
||||
logger.warning(
|
||||
f"获取插件 {matcher.plugin_name} __zx_plugin_name__ 失败...,插件控制未加载."
|
||||
)
|
||||
else:
|
||||
_tmp_module[matcher.plugin_name] = plugin_name
|
||||
try:
|
||||
plugin_settings = _module.__getattribute__(
|
||||
"__plugin_settings__"
|
||||
)
|
||||
except AttributeError:
|
||||
plugin_settings = {"cmd": [matcher.plugin_name, plugin_name]}
|
||||
if not plugin_settings.get("cost_gold"):
|
||||
plugin_settings["cost_gold"] = 0
|
||||
if (
|
||||
plugin_settings.get("cmd") is not None
|
||||
and plugin_name not in plugin_settings["cmd"]
|
||||
):
|
||||
plugin_settings["cmd"].append(plugin_name)
|
||||
if plugins2settings_manager.get(
|
||||
matcher.plugin_name
|
||||
) and plugins2settings_manager[matcher.plugin_name].get(
|
||||
"plugin_type"
|
||||
):
|
||||
plugin_type = tuple(
|
||||
plugins2settings_manager.get_plugin_data(
|
||||
matcher.plugin_name
|
||||
)["plugin_type"]
|
||||
)
|
||||
else:
|
||||
try:
|
||||
plugin_type = _module.__getattribute__("__plugin_type__")
|
||||
except AttributeError:
|
||||
plugin_type = ("normal",)
|
||||
if plugin_settings and matcher.plugin_name:
|
||||
plugins2settings_manager.add_plugin_settings(
|
||||
matcher.plugin_name,
|
||||
plugin_type=plugin_type,
|
||||
**plugin_settings,
|
||||
)
|
||||
plugin_settings = {"cmd": [matcher.plugin_name, plugin_name]}
|
||||
if not plugin_settings.get("cost_gold"):
|
||||
plugin_settings["cost_gold"] = 0
|
||||
if (
|
||||
plugin_settings.get("cmd") is not None
|
||||
and plugin_name not in plugin_settings["cmd"]
|
||||
):
|
||||
plugin_settings["cmd"].append(plugin_name)
|
||||
if plugins2settings_manager.get(
|
||||
matcher.plugin_name
|
||||
) and plugins2settings_manager[matcher.plugin_name].get(
|
||||
"plugin_type"
|
||||
):
|
||||
plugin_type = tuple(
|
||||
plugins2settings_manager.get_plugin_data(
|
||||
matcher.plugin_name
|
||||
)["plugin_type"]
|
||||
)
|
||||
else:
|
||||
try:
|
||||
plugin_type = _module.__getattribute__("__plugin_type__")
|
||||
except AttributeError:
|
||||
plugin_type = ("normal",)
|
||||
if plugin_settings and matcher.plugin_name:
|
||||
plugins2settings_manager.add_plugin_settings(
|
||||
matcher.plugin_name,
|
||||
plugin_type=plugin_type,
|
||||
**plugin_settings,
|
||||
)
|
||||
except Exception as e:
|
||||
logger.error(f'{matcher.plugin_name} 初始化 plugin_settings 发生错误 {type(e)}:{e}')
|
||||
_tmp.append(matcher.plugin_name)
|
||||
_tmp_data = {"PluginSettings": plugins2settings_manager.get_data()}
|
||||
with open(plugins2settings_file, "w", encoding="utf8") as wf:
|
||||
|
||||
@@ -15,7 +15,7 @@ from configs.path_config import TEXT_PATH
|
||||
from asyncio.exceptions import TimeoutError
|
||||
from typing import List
|
||||
from utils.http_utils import AsyncHttpx
|
||||
from utils.manager import GDict
|
||||
from utils.utils import GDict
|
||||
from utils.utils import scheduler
|
||||
import nonebot
|
||||
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
from nonebot import on_command
|
||||
|
||||
from models.user_shop_gold_log import UserShopGoldLog
|
||||
from services.log import logger
|
||||
from nonebot.adapters.onebot.v11 import GroupMessageEvent, Message
|
||||
from nonebot.params import CommandArg
|
||||
@@ -46,10 +48,7 @@ async def _(event: GroupMessageEvent, arg: Message = CommandArg()):
|
||||
for x in await GoodsInfo.get_all_goods()
|
||||
if x.goods_limit_time > time.time() or x.goods_limit_time == 0
|
||||
]
|
||||
goods_name_list = [
|
||||
x.goods_name
|
||||
for x in goods_list
|
||||
]
|
||||
goods_name_list = [x.goods_name for x in goods_list]
|
||||
msg = arg.extract_plain_text().strip().split()
|
||||
num = 1
|
||||
if len(msg) > 1:
|
||||
@@ -77,11 +76,15 @@ async def _(event: GroupMessageEvent, arg: Message = CommandArg()):
|
||||
await BagUser.get_gold(event.user_id, event.group_id)
|
||||
) < goods.goods_price * num * goods.goods_discount:
|
||||
await buy.finish("您的金币好像不太够哦", at_sender=True)
|
||||
flag, n = await GoodsInfo.check_user_daily_purchase(goods, event.user_id, event.group_id, num)
|
||||
flag, n = await GoodsInfo.check_user_daily_purchase(
|
||||
goods, event.user_id, event.group_id, num
|
||||
)
|
||||
if flag:
|
||||
await buy.finish(f"该次购买将超过每日次数限制,目前该道具还可以购买{n}次哦", at_sender=True)
|
||||
if await BagUser.buy_property(event.user_id, event.group_id, goods, num):
|
||||
await GoodsInfo.add_user_daily_purchase(goods, event.user_id, event.group_id, num)
|
||||
await GoodsInfo.add_user_daily_purchase(
|
||||
goods, event.user_id, event.group_id, num
|
||||
)
|
||||
await buy.send(
|
||||
f"花费 {goods.goods_price * num * goods.goods_discount} 金币购买 {goods.goods_name} ×{num} 成功!",
|
||||
at_sender=True,
|
||||
@@ -90,6 +93,14 @@ async def _(event: GroupMessageEvent, arg: Message = CommandArg()):
|
||||
f"USER {event.user_id} GROUP {event.group_id} "
|
||||
f"花费 {goods.goods_price*num} 金币购买 {goods.goods_name} ×{num} 成功!"
|
||||
)
|
||||
await UserShopGoldLog.add_shop_log(
|
||||
event.user_id,
|
||||
event.group_id,
|
||||
0,
|
||||
goods.goods_name,
|
||||
num,
|
||||
goods.goods_price * num * goods.goods_discount,
|
||||
)
|
||||
else:
|
||||
await buy.send(f"{goods.goods_name} 购买失败!", at_sender=True)
|
||||
logger.info(
|
||||
|
||||
@@ -1,4 +1,7 @@
|
||||
from nonebot import on_command
|
||||
|
||||
from utils.message_builder import image
|
||||
from ._data_source import create_bag_image
|
||||
from services.log import logger
|
||||
from nonebot.adapters.onebot.v11 import GroupMessageEvent
|
||||
from models.bag_user import BagUser
|
||||
@@ -32,10 +35,11 @@ my_props = on_command("我的道具", priority=5, block=True, permission=GROUP)
|
||||
async def _(event: GroupMessageEvent):
|
||||
props = await BagUser.get_property(event.user_id, event.group_id)
|
||||
if props:
|
||||
rst = ""
|
||||
for i, p in enumerate(props.keys()):
|
||||
rst += f"{i+1}.{p}\t×{props[p]}\n"
|
||||
await my_props.send("\n" + rst[:-1], at_sender=True)
|
||||
await my_props.send(image(b64=await create_bag_image(props)))
|
||||
# rst = ""
|
||||
# for i, p in enumerate(props.keys()):
|
||||
# rst += f"{i+1}.{p}\t×{props[p]}\n"
|
||||
# await my_props.send("\n" + rst[:-1], at_sender=True)
|
||||
logger.info(f"USER {event.user_id} GROUP {event.group_id} 查看我的道具")
|
||||
else:
|
||||
await my_props.finish("您的背包里没有任何的道具噢~", at_sender=True)
|
||||
@@ -0,0 +1,121 @@
|
||||
from typing import Dict, List, Optional
|
||||
|
||||
from models.bag_user import BagUser
|
||||
from models.goods_info import GoodsInfo
|
||||
from utils.image_utils import BuildImage
|
||||
from configs.path_config import IMAGE_PATH
|
||||
|
||||
|
||||
icon_path = IMAGE_PATH / "shop_icon"
|
||||
|
||||
|
||||
async def create_bag_image(props: Dict[str, int]):
|
||||
"""
|
||||
说明:
|
||||
创建背包道具图片
|
||||
参数:
|
||||
:param props: 道具仓库字典
|
||||
"""
|
||||
goods_list = await GoodsInfo.get_all_goods()
|
||||
active_props = await _init_prop(props, [x for x in goods_list if not x.is_passive])
|
||||
passive_props = await _init_prop(props, [x for x in goods_list if x.is_passive])
|
||||
active_w = 0
|
||||
active_h = 0
|
||||
passive_w = 0
|
||||
passive_h = 0
|
||||
if active_props:
|
||||
img = BuildImage(
|
||||
active_props.w,
|
||||
active_props.h + 70,
|
||||
font="CJGaoDeGuo.otf",
|
||||
font_size=30,
|
||||
color="#f9f6f2",
|
||||
)
|
||||
await img.apaste(active_props, (0, 70))
|
||||
await img.atext((0, 30), "主动道具")
|
||||
active_props = img
|
||||
active_w = img.w
|
||||
active_h = img.h
|
||||
if passive_props:
|
||||
img = BuildImage(
|
||||
passive_props.w,
|
||||
passive_props.h + 70,
|
||||
font="CJGaoDeGuo.otf",
|
||||
font_size=30,
|
||||
color="#f9f6f2",
|
||||
)
|
||||
await img.apaste(passive_props, (0, 70))
|
||||
await img.atext((0, 30), "被动道具")
|
||||
passive_props = img
|
||||
passive_w = img.w
|
||||
passive_h = img.h
|
||||
A = BuildImage(
|
||||
active_w + passive_w + 100,
|
||||
max(active_h, passive_h) + 60,
|
||||
font="CJGaoDeGuo.otf",
|
||||
font_size=30,
|
||||
color="#f9f6f2",
|
||||
)
|
||||
curr_w = 50
|
||||
if active_props:
|
||||
await A.apaste(active_props, (curr_w, 0))
|
||||
curr_w += active_props.w + 10
|
||||
if passive_props:
|
||||
await A.apaste(passive_props, (curr_w, 0))
|
||||
if active_props and passive_props:
|
||||
await A.aline(
|
||||
(active_props.w + 45, 70, active_props.w + 45, A.h - 20), fill=(0, 0, 0)
|
||||
)
|
||||
return A.pic2bs4()
|
||||
|
||||
|
||||
async def _init_prop(
|
||||
props: Dict[str, int], _props: List[GoodsInfo]
|
||||
) -> Optional[BuildImage]:
|
||||
"""
|
||||
说明:
|
||||
构造道具列表图片
|
||||
参数:
|
||||
:param props: 道具仓库字典
|
||||
:param _props: 道具列表
|
||||
"""
|
||||
active_name = [x.goods_name for x in _props]
|
||||
name_list = [x for x in props.keys() if x in active_name]
|
||||
if not name_list:
|
||||
return None
|
||||
temp_img = BuildImage(0, 0, font_size=20)
|
||||
image_list = []
|
||||
num_list = []
|
||||
for i, name in enumerate(name_list):
|
||||
img = BuildImage(
|
||||
temp_img.getsize(name)[0] + 50,
|
||||
30,
|
||||
font="msyh.ttf",
|
||||
font_size=20,
|
||||
color="#f9f6f2",
|
||||
)
|
||||
await img.atext((30, 5), f"{i + 1}.{name}")
|
||||
goods = [x for x in _props if x.goods_name == name][0]
|
||||
if goods.icon and (icon_path / goods.icon).exists():
|
||||
icon = BuildImage(30, 30, background=icon_path / goods.icon)
|
||||
await img.apaste(icon, alpha=True)
|
||||
image_list.append(img)
|
||||
num_list.append(
|
||||
BuildImage(
|
||||
30, 30, font_size=20, font="msyh.ttf", plain_text=f"×{props[name]}"
|
||||
)
|
||||
)
|
||||
max_w = 0
|
||||
num_max_w = 0
|
||||
h = 0
|
||||
for img, num in zip(image_list, num_list):
|
||||
h += img.h
|
||||
max_w = max_w if max_w > img.w else img.w
|
||||
num_max_w = num_max_w if num_max_w > num.w else num.w
|
||||
A = BuildImage(max_w + num_max_w + 30, h, color="#f9f6f2")
|
||||
curr_h = 0
|
||||
for img, num in zip(image_list, num_list):
|
||||
await A.apaste(img, (0, curr_h))
|
||||
await A.apaste(num, (max_w + 20, curr_h + 5), True)
|
||||
curr_h += img.h
|
||||
return A
|
||||
@@ -6,7 +6,6 @@ from utils.message_builder import image
|
||||
from nonebot.permission import SUPERUSER
|
||||
from utils.utils import is_number, scheduler
|
||||
from nonebot.params import CommandArg
|
||||
from nonebot.plugin import export
|
||||
from services.log import logger
|
||||
import os
|
||||
|
||||
@@ -51,11 +50,6 @@ __plugin_block_limit__ = {
|
||||
"limit_type": "group"
|
||||
}
|
||||
|
||||
# 导出方法供其他插件使用
|
||||
export = export()
|
||||
export.register_goods = register_goods
|
||||
export.delete_goods = delete_goods
|
||||
export.update_goods = update_goods
|
||||
|
||||
shop_help = on_command("商店", priority=5, block=True)
|
||||
|
||||
|
||||
@@ -2,49 +2,17 @@ from PIL import Image
|
||||
|
||||
from models.goods_info import GoodsInfo
|
||||
from utils.image_utils import BuildImage
|
||||
from models.sign_group_user import SignGroupUser
|
||||
from utils.utils import is_number
|
||||
from configs.path_config import IMAGE_PATH
|
||||
from typing import Optional, Union, Tuple
|
||||
from configs.config import Config
|
||||
from nonebot import Driver
|
||||
from nonebot.plugin import require
|
||||
from utils.decorator.shop import shop_register
|
||||
import nonebot
|
||||
from utils.utils import GDict
|
||||
import time
|
||||
|
||||
driver: Driver = nonebot.get_driver()
|
||||
icon_path = IMAGE_PATH / 'shop_icon'
|
||||
|
||||
|
||||
use = require("use")
|
||||
|
||||
|
||||
@driver.on_startup
|
||||
async def init_default_shop_goods():
|
||||
"""
|
||||
导入内置的三个商品
|
||||
"""
|
||||
|
||||
@shop_register(
|
||||
name=("好感度双倍加持卡Ⅰ", "好感度双倍加持卡Ⅱ", "好感度双倍加持卡Ⅲ"),
|
||||
price=(30, 150, 250),
|
||||
des=(
|
||||
"下次签到双倍好感度概率 + 10%(谁才是真命天子?)(同类商品将覆盖)",
|
||||
"下次签到双倍好感度概率 + 20%(平平庸庸)(同类商品将覆盖)",
|
||||
"下次签到双倍好感度概率 + 30%(金币才是真命天子!)(同类商品将覆盖)",
|
||||
),
|
||||
load_status=Config.get_config("shop", "IMPORT_DEFAULT_SHOP_GOODS"),
|
||||
daily_limit=(10, 20, 30),
|
||||
** {"好感度双倍加持卡Ⅰ_prob": 0.1, "好感度双倍加持卡Ⅱ_prob": 0.2, "好感度双倍加持卡Ⅲ_prob": 0.3},
|
||||
)
|
||||
async def sign_card(user_id: int, group_id: int, prob: float):
|
||||
user = await SignGroupUser.ensure(user_id, group_id)
|
||||
await user.update(add_probability=prob).apply()
|
||||
|
||||
|
||||
@driver.on_bot_connect
|
||||
async def _():
|
||||
await shop_register.load_register()
|
||||
GDict['run_sql'].append("ALTER TABLE goods_info ADD is_passive boolean DEFAULT False;")
|
||||
GDict['run_sql'].append("ALTER TABLE goods_info ADD icon VARCHAR(255);")
|
||||
|
||||
|
||||
# 创建商店界面
|
||||
@@ -63,7 +31,7 @@ async def create_shop_help() -> str:
|
||||
if goods.goods_limit_time == 0 or time.time() < goods.goods_limit_time:
|
||||
h += len(goods.goods_description.strip().split("\n")) * font_h + 80
|
||||
_list.append(goods)
|
||||
A = BuildImage(1000, h, color="#f9f6f2")
|
||||
A = BuildImage(1100, h, color="#f9f6f2")
|
||||
current_h = 0
|
||||
total_n = 0
|
||||
for goods in _list:
|
||||
@@ -106,9 +74,12 @@ async def create_shop_help() -> str:
|
||||
await goods_image.apaste(name_image, (0, 5), True, center_type="by_width")
|
||||
await goods_image.atext((15, 50), f"简介:{goods.goods_description}")
|
||||
await goods_image.acircle_corner(20)
|
||||
await bk.apaste(goods_image, alpha=True)
|
||||
if goods.icon and (icon_path / goods.icon).exists():
|
||||
icon = BuildImage(100, 100, background=icon_path / goods.icon)
|
||||
await bk.apaste(icon)
|
||||
await bk.apaste(goods_image, (100, 0), alpha=True)
|
||||
n = 0
|
||||
_w = 550
|
||||
_w = 650
|
||||
# 添加限时图标和时间
|
||||
if goods.goods_limit_time > 0:
|
||||
n += 140
|
||||
@@ -162,14 +133,14 @@ async def create_shop_help() -> str:
|
||||
if total_n < n:
|
||||
total_n = n
|
||||
if n:
|
||||
await bk.aline((550, -1, 550 + n, -1), "#a29ad6", 5)
|
||||
await bk.aline((550, 80, 550 + n, 80), "#a29ad6", 5)
|
||||
await bk.aline((650, -1, 650 + n, -1), "#a29ad6", 5)
|
||||
await bk.aline((650, 80, 650 + n, 80), "#a29ad6", 5)
|
||||
|
||||
# 添加限时图标和时间
|
||||
idx += 1
|
||||
await A.apaste(bk, (0, current_h), True)
|
||||
current_h += 90
|
||||
w = 850
|
||||
w = 950
|
||||
if total_n:
|
||||
w += total_n
|
||||
h = A.h + 230 + 100
|
||||
@@ -197,6 +168,8 @@ async def register_goods(
|
||||
discount: Optional[float] = 1,
|
||||
limit_time: Optional[int] = 0,
|
||||
daily_limit: Optional[int] = 0,
|
||||
is_passive: Optional[bool] = False,
|
||||
icon: Optional[str] = None,
|
||||
) -> bool:
|
||||
"""
|
||||
添加商品
|
||||
@@ -209,6 +182,8 @@ async def register_goods(
|
||||
:param discount: 商品折扣
|
||||
:param limit_time: 商品限时销售时间,单位为小时
|
||||
:param daily_limit: 每日购买次数限制
|
||||
:param is_passive: 是否为被动
|
||||
:param icon: 图标
|
||||
:return: 是否添加成功
|
||||
"""
|
||||
if not await GoodsInfo.get_goods_info(name):
|
||||
@@ -220,7 +195,7 @@ async def register_goods(
|
||||
else 0
|
||||
)
|
||||
return await GoodsInfo.add_goods(
|
||||
name, int(price), des, float(discount), limit_time, daily_limit
|
||||
name, int(price), des, float(discount), limit_time, daily_limit, is_passive, icon
|
||||
)
|
||||
return False
|
||||
|
||||
@@ -272,6 +247,7 @@ async def update_goods(**kwargs) -> Tuple[bool, str, str]:
|
||||
discount = goods.goods_discount
|
||||
limit_time = goods.goods_limit_time
|
||||
daily_limit = goods.daily_limit
|
||||
is_passive = goods.is_passive
|
||||
new_time = 0
|
||||
tmp = ""
|
||||
if kwargs.get("price"):
|
||||
@@ -294,6 +270,9 @@ async def update_goods(**kwargs) -> Tuple[bool, str, str]:
|
||||
if kwargs.get("daily_limit"):
|
||||
tmp += f'每日购买限制:{daily_limit} --> {kwargs["daily_limit"]}\n' if daily_limit else "取消了购买限制\n"
|
||||
daily_limit = int(kwargs["daily_limit"])
|
||||
if kwargs.get("is_passive"):
|
||||
tmp += f'被动道具:{is_passive} --> {kwargs["is_passive"]}\n'
|
||||
des = kwargs["is_passive"]
|
||||
await GoodsInfo.update_goods(
|
||||
name,
|
||||
int(price),
|
||||
@@ -304,7 +283,8 @@ async def update_goods(**kwargs) -> Tuple[bool, str, str]:
|
||||
if limit_time != 0 and new_time
|
||||
else 0
|
||||
),
|
||||
daily_limit
|
||||
daily_limit,
|
||||
is_passive
|
||||
)
|
||||
return True, name, tmp[:-1],
|
||||
|
||||
|
||||
@@ -1,13 +1,16 @@
|
||||
from nonebot import on_command
|
||||
|
||||
from models.user_shop_gold_log import UserShopGoldLog
|
||||
from services.log import logger
|
||||
from nonebot.adapters.onebot.v11 import Bot, GroupMessageEvent, Message
|
||||
from nonebot.params import CommandArg
|
||||
|
||||
from utils.decorator.shop import NotMeetUseConditionsException
|
||||
from utils.utils import is_number
|
||||
from models.bag_user import BagUser
|
||||
from nonebot.adapters.onebot.v11.permission import GROUP
|
||||
from services.db_context import db
|
||||
from nonebot.plugin import export
|
||||
from .data_source import effect, register_use, func_manager
|
||||
from .data_source import effect, register_use, func_manager, build_params
|
||||
|
||||
|
||||
__zx_plugin_name__ = "商店 - 使用道具"
|
||||
@@ -30,9 +33,6 @@ __plugin_settings__ = {
|
||||
"cmd": ["商店", "使用道具"],
|
||||
}
|
||||
|
||||
# 导出方法供其他插件使用
|
||||
export = export()
|
||||
export.register_use = register_use
|
||||
|
||||
use_props = on_command("使用道具", priority=5, block=True, permission=GROUP)
|
||||
|
||||
@@ -45,23 +45,29 @@ async def _(bot: Bot, event: GroupMessageEvent, arg: Message = CommandArg()):
|
||||
if len(msg_sp) > 1 and is_number(msg_sp[-1]) and int(msg_sp[-1]) > 0:
|
||||
num = int(msg.split()[-1])
|
||||
msg = " ".join(msg.split()[:-1])
|
||||
property_ = await BagUser.get_property(event.user_id, event.group_id)
|
||||
property_ = await BagUser.get_property(event.user_id, event.group_id, True)
|
||||
if property_:
|
||||
async with db.transaction():
|
||||
if is_number(msg):
|
||||
if 0 < int(msg) <= len(property_):
|
||||
name = list(property_.keys())[int(msg) - 1]
|
||||
else:
|
||||
await use_props.finish("仔细看看自己的道具仓库有没有这个道具?", at_sender=True)
|
||||
name = None
|
||||
if is_number(msg):
|
||||
if 0 < int(msg) <= len(property_):
|
||||
name = list(property_.keys())[int(msg) - 1]
|
||||
else:
|
||||
if msg not in property_.keys():
|
||||
await use_props.finish("道具名称错误!", at_sender=True)
|
||||
name = msg
|
||||
_user_prop_count = property_[name]
|
||||
if num > _user_prop_count:
|
||||
await use_props.finish(f"道具数量不足,无法使用{num}次!")
|
||||
if num > (n := func_manager.get_max_num_limit(name)):
|
||||
await use_props.finish(f"该道具单次只能使用 {n} 个!")
|
||||
await use_props.finish("仔细看看自己的道具仓库有没有这个道具?", at_sender=True)
|
||||
else:
|
||||
if msg not in property_.keys():
|
||||
await use_props.finish("道具名称错误!", at_sender=True)
|
||||
name = msg
|
||||
_user_prop_count = property_[name]
|
||||
if num > _user_prop_count:
|
||||
await use_props.finish(f"道具数量不足,无法使用{num}次!")
|
||||
if num > (n := func_manager.get_max_num_limit(name)):
|
||||
await use_props.finish(f"该道具单次只能使用 {n} 个!")
|
||||
model, kwargs = build_params(bot, event, name, num)
|
||||
try:
|
||||
await func_manager.run_handle(type_="before_handle", param=model, **kwargs)
|
||||
except NotMeetUseConditionsException as e:
|
||||
await use_props.finish(e.get_info(), at_sender=True)
|
||||
async with db.transaction():
|
||||
if await BagUser.delete_property(
|
||||
event.user_id, event.group_id, name, num
|
||||
):
|
||||
@@ -72,10 +78,12 @@ async def _(bot: Bot, event: GroupMessageEvent, arg: Message = CommandArg()):
|
||||
logger.info(
|
||||
f"USER {event.user_id} GROUP {event.group_id} 使用道具 {name} {num} 次成功"
|
||||
)
|
||||
await UserShopGoldLog.add_shop_log(event.user_id, event.group_id, 1, name, num)
|
||||
else:
|
||||
await use_props.send(f"使用道具 {name} {num} 次失败!", at_sender=True)
|
||||
logger.info(
|
||||
f"USER {event.user_id} GROUP {event.group_id} 使用道具 {name} {num} 次失败"
|
||||
)
|
||||
await func_manager.run_handle(type_="after_handle", param=model, **kwargs)
|
||||
else:
|
||||
await use_props.send("您的背包里没有任何的道具噢", at_sender=True)
|
||||
|
||||
@@ -3,7 +3,7 @@ from services.log import logger
|
||||
from nonebot.adapters.onebot.v11 import Bot
|
||||
from pydantic import create_model
|
||||
from utils.models import ShopParam
|
||||
from typing import Optional, Union
|
||||
from typing import Optional, Union, Callable, List, Tuple, Dict, Any
|
||||
from types import MappingProxyType
|
||||
import inspect
|
||||
import asyncio
|
||||
@@ -13,6 +13,30 @@ class GoodsUseFuncManager:
|
||||
def __init__(self):
|
||||
self._data = {}
|
||||
|
||||
def register_use_before_handle(self, goods_name: str, fun_list: List[Callable]):
|
||||
"""
|
||||
说明:
|
||||
注册商品使用前函数
|
||||
参数:
|
||||
:param goods_name: 商品名称
|
||||
:param fun_list: 函数列表
|
||||
"""
|
||||
if fun_list:
|
||||
self._data[goods_name]["before_handle"] = fun_list
|
||||
logger.info(f"register_use_before_handle 成功注册商品:{goods_name} 的{len(fun_list)}个使用前函数")
|
||||
|
||||
def register_use_after_handle(self, goods_name: str, fun_list: List[Callable]):
|
||||
"""
|
||||
说明:
|
||||
注册商品使用后函数
|
||||
参数:
|
||||
:param goods_name: 商品名称
|
||||
:param fun_list: 函数列表
|
||||
"""
|
||||
if fun_list:
|
||||
self._data[goods_name]["after_handle"] = fun_list
|
||||
logger.info(f"register_use_after_handle 成功注册商品:{goods_name} 的{len(fun_list)}个使用后函数")
|
||||
|
||||
def register_use(self, goods_name: str, **kwargs):
|
||||
"""
|
||||
注册商品使用方法
|
||||
@@ -26,7 +50,7 @@ class GoodsUseFuncManager:
|
||||
判断商品使用方法是否被注册
|
||||
:param goods_name: 商品名称
|
||||
"""
|
||||
return bool(self ._data.get(goods_name))
|
||||
return bool(self._data.get(goods_name))
|
||||
|
||||
def get_max_num_limit(self, goods_name: str) -> int:
|
||||
"""
|
||||
@@ -37,6 +61,21 @@ class GoodsUseFuncManager:
|
||||
return self._data[goods_name]["kwargs"]["max_num_limit"]
|
||||
return 1
|
||||
|
||||
def _parse_args(self, args_: MappingProxyType, param: ShopParam, **kwargs):
|
||||
param_list_ = []
|
||||
_bot = param.bot
|
||||
param.bot = None
|
||||
param_json = param.dict()
|
||||
param_json["bot"] = _bot
|
||||
for par in args_.keys():
|
||||
if par in ["shop_param"]:
|
||||
param_list_.append(param)
|
||||
elif par not in ["args", "kwargs"]:
|
||||
param_list_.append(param_json.get(par))
|
||||
if kwargs.get(par) is not None:
|
||||
del kwargs[par]
|
||||
return param_list_
|
||||
|
||||
async def use(
|
||||
self, param: ShopParam, **kwargs
|
||||
) -> Optional[Union[str, MessageSegment]]:
|
||||
@@ -45,31 +84,18 @@ class GoodsUseFuncManager:
|
||||
:param param: BaseModel
|
||||
:param kwargs: kwargs
|
||||
"""
|
||||
def parse_args(args_: MappingProxyType):
|
||||
param_list_ = []
|
||||
_bot = param.bot
|
||||
param.bot = None
|
||||
param_json = param.dict()
|
||||
param_json["bot"] = _bot
|
||||
for par in args_.keys():
|
||||
if par in ["shop_param"]:
|
||||
param_list_.append(param)
|
||||
elif par not in ["args", "kwargs"]:
|
||||
param_list_.append(param_json.get(par))
|
||||
if kwargs.get(par) is not None:
|
||||
del kwargs[par]
|
||||
return param_list_
|
||||
goods_name = param.goods_name
|
||||
if self.exists(goods_name):
|
||||
# 使用方法
|
||||
args = inspect.signature(self._data[goods_name]["func"]).parameters
|
||||
if args and list(args.keys())[0] != "kwargs":
|
||||
if asyncio.iscoroutinefunction(self._data[goods_name]["func"]):
|
||||
return await self._data[goods_name]["func"](
|
||||
*parse_args(args)
|
||||
*self._parse_args(args, param, **kwargs)
|
||||
)
|
||||
else:
|
||||
return self._data[goods_name]["func"](
|
||||
*parse_args(args)
|
||||
*self._parse_args(args, param, **kwargs)
|
||||
)
|
||||
else:
|
||||
if asyncio.iscoroutinefunction(self._data[goods_name]["func"]):
|
||||
@@ -81,6 +107,21 @@ class GoodsUseFuncManager:
|
||||
**kwargs,
|
||||
)
|
||||
|
||||
async def run_handle(self, goods_name: str, type_: str, param: ShopParam, **kwargs):
|
||||
if self._data[goods_name].get(type_):
|
||||
for func in self._data[goods_name].get(type_):
|
||||
args = inspect.signature(func).parameters
|
||||
if args and list(args.keys())[0] != "kwargs":
|
||||
if asyncio.iscoroutinefunction(func):
|
||||
await func(*self._parse_args(args, param, **kwargs))
|
||||
else:
|
||||
func(*self._parse_args(args, param, **kwargs))
|
||||
else:
|
||||
if asyncio.iscoroutinefunction(func):
|
||||
await func(**kwargs)
|
||||
else:
|
||||
func(**kwargs)
|
||||
|
||||
def check_send_success_message(self, goods_name: str) -> bool:
|
||||
"""
|
||||
检查是否发送使用成功信息
|
||||
@@ -115,6 +156,34 @@ class GoodsUseFuncManager:
|
||||
func_manager = GoodsUseFuncManager()
|
||||
|
||||
|
||||
def build_params(
|
||||
bot: Bot, event: GroupMessageEvent, goods_name: str, num: int
|
||||
) -> Tuple[ShopParam, Dict[str, Any]]:
|
||||
"""
|
||||
说明:
|
||||
构造参数
|
||||
参数:
|
||||
:param bot: bot
|
||||
:param event: event
|
||||
:param goods_name: 商品名称
|
||||
:param num: 数量
|
||||
:return:
|
||||
"""
|
||||
_kwargs = func_manager.get_kwargs(goods_name)
|
||||
return (
|
||||
func_manager.init_model(goods_name, bot, event, num),
|
||||
{
|
||||
**_kwargs,
|
||||
"_bot": bot,
|
||||
"event": event,
|
||||
"group_id": event.group_id,
|
||||
"user_id": event.user_id,
|
||||
"num": num,
|
||||
"goods_name": goods_name,
|
||||
},
|
||||
)
|
||||
|
||||
|
||||
async def effect(
|
||||
bot: Bot, event: GroupMessageEvent, goods_name: str, num: int
|
||||
) -> Optional[Union[str, MessageSegment]]:
|
||||
@@ -130,24 +199,14 @@ async def effect(
|
||||
# try:
|
||||
if func_manager.exists(goods_name):
|
||||
_kwargs = func_manager.get_kwargs(goods_name)
|
||||
return await func_manager.use(
|
||||
func_manager.init_model(goods_name, bot, event, num),
|
||||
**{
|
||||
**_kwargs,
|
||||
"_bot": bot,
|
||||
"event": event,
|
||||
"group_id": event.group_id,
|
||||
"user_id": event.user_id,
|
||||
"num": num,
|
||||
"goods_name": goods_name,
|
||||
},
|
||||
)
|
||||
model, kwargs = build_params(bot, event, goods_name, num)
|
||||
return await func_manager.use(model, **kwargs)
|
||||
# except Exception as e:
|
||||
# logger.error(f"use 商品生效函数effect 发生错误 {type(e)}:{e}")
|
||||
return None
|
||||
|
||||
|
||||
def register_use(goods_name: str, func, **kwargs):
|
||||
def register_use(goods_name: str, func: Callable, **kwargs):
|
||||
"""
|
||||
注册商品使用方法
|
||||
:param goods_name: 商品名称
|
||||
|
||||
@@ -94,13 +94,14 @@ class ConfigsManager:
|
||||
del self._data[module]
|
||||
self.save()
|
||||
|
||||
def set_config(self, module: str, key: str, value: Any, save_simple_data: bool = False):
|
||||
def set_config(self, module: str, key: str, value: Any, auto_save: bool = False, save_simple_data: bool = True):
|
||||
"""
|
||||
设置配置值
|
||||
:param module: 模块名
|
||||
:param key: 配置名称
|
||||
:param value: 值
|
||||
:param save_simple_data: 同时保存至config.yaml
|
||||
:param auto_save: 自动保存
|
||||
:param save_simple_data: 保存至config.yaml
|
||||
"""
|
||||
if module in self._data.keys():
|
||||
if (
|
||||
@@ -109,6 +110,7 @@ class ConfigsManager:
|
||||
):
|
||||
self._data[module][key]["value"] = value
|
||||
self._simple_data[module][key] = value
|
||||
if auto_save:
|
||||
self.save(save_simple_data=save_simple_data)
|
||||
|
||||
def set_help(self, module: str, key: str, help_: str):
|
||||
@@ -145,18 +147,12 @@ class ConfigsManager:
|
||||
:param default: 没有key值内容的默认返回值
|
||||
"""
|
||||
key = key.upper()
|
||||
# 优先使用simple_data
|
||||
if module in self._simple_data.keys():
|
||||
if module in self._data.keys():
|
||||
for key in [key, f"{key} [LEVEL]"]:
|
||||
if self._simple_data[module].get(key) is not None:
|
||||
return self._simple_data[module][key]
|
||||
# if module in self._data.keys():
|
||||
# if module in self._data.keys():
|
||||
# for key in [key, f"{key} [LEVEL]"]:
|
||||
# if self._data[module].get(key) is not None:
|
||||
# if self._data[module][key]["value"] is None:
|
||||
# return self._data[module][key]["value"]
|
||||
# return self._data[module][key]["default_value"]
|
||||
if self._data[module].get(key) is not None:
|
||||
if self._data[module][key]["value"] is None:
|
||||
return self._data[module][key]["default_value"]
|
||||
return self._data[module][key]["value"]
|
||||
if default is not None:
|
||||
return default
|
||||
return None
|
||||
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 694 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 694 KiB After Width: | Height: | Size: 326 KiB |
+9
-1
@@ -2,6 +2,7 @@ from services.db_context import db
|
||||
from typing import Dict
|
||||
from typing import Optional, List
|
||||
from services.log import logger
|
||||
from .goods_info import GoodsInfo
|
||||
|
||||
|
||||
class BagUser(db.Model):
|
||||
@@ -62,17 +63,24 @@ class BagUser(db.Model):
|
||||
return 100
|
||||
|
||||
@classmethod
|
||||
async def get_property(cls, user_qq: int, group_id: int) -> Dict[str, int]:
|
||||
async def get_property(cls, user_qq: int, group_id: int, only_active: bool = False) -> Dict[str, int]:
|
||||
"""
|
||||
说明:
|
||||
获取当前道具
|
||||
参数:
|
||||
:param user_qq: qq号
|
||||
:param group_id: 所在群号
|
||||
:param only_active: 仅仅获取主动使用的道具
|
||||
"""
|
||||
query = cls.query.where((cls.user_qq == user_qq) & (cls.group_id == group_id))
|
||||
user = await query.gino.first()
|
||||
if user:
|
||||
if only_active and user.property:
|
||||
data = {}
|
||||
name_list = [x.goods_name for x in await GoodsInfo.get_all_goods() if not x.is_passive]
|
||||
for key in [x for x in user.property.keys() if x in name_list]:
|
||||
data[key] = user.property[key]
|
||||
return data
|
||||
return user.property
|
||||
else:
|
||||
await cls.create(
|
||||
|
||||
+16
-2
@@ -16,6 +16,8 @@ class GoodsInfo(db.Model):
|
||||
daily_purchase_limit = db.Column(
|
||||
db.JSON(), nullable=False, default={}
|
||||
) # 每日购买限制数据存储
|
||||
is_passive = db.Column(db.Boolean(), nullable=False, default=0) # 是否为被动
|
||||
icon = db.Column(db.String(), default=0) # 图标
|
||||
|
||||
_idx1 = db.Index("goods_group_users_idx1", "goods_name", unique=True)
|
||||
|
||||
@@ -28,6 +30,8 @@ class GoodsInfo(db.Model):
|
||||
goods_discount: float = 1,
|
||||
goods_limit_time: int = 0,
|
||||
daily_limit: int = 0,
|
||||
is_passive: bool = False,
|
||||
icon: Optional[str] = None,
|
||||
) -> bool:
|
||||
"""
|
||||
说明:
|
||||
@@ -39,6 +43,8 @@ class GoodsInfo(db.Model):
|
||||
:param goods_discount: 商品折扣
|
||||
:param goods_limit_time: 商品限时
|
||||
:param daily_limit: 每日购买限制
|
||||
:param is_passive: 是否为被动道具
|
||||
:param icon: 图标
|
||||
"""
|
||||
try:
|
||||
if not await cls.get_goods_info(goods_name):
|
||||
@@ -49,10 +55,12 @@ class GoodsInfo(db.Model):
|
||||
goods_discount=goods_discount,
|
||||
goods_limit_time=goods_limit_time,
|
||||
daily_limit=daily_limit,
|
||||
is_passive=is_passive,
|
||||
icon=icon
|
||||
)
|
||||
return True
|
||||
except Exception as e:
|
||||
logger.error(f"GoodsInfo add_goods 发生错误 {type(e)}:{e}")
|
||||
logger.error(f"GoodsInfo add_goods {goods_name} 发生错误 {type(e)}:{e}")
|
||||
return False
|
||||
|
||||
@classmethod
|
||||
@@ -81,7 +89,9 @@ class GoodsInfo(db.Model):
|
||||
goods_description: Optional[str] = None,
|
||||
goods_discount: Optional[float] = None,
|
||||
goods_limit_time: Optional[int] = None,
|
||||
daily_limit: Optional[int] = None
|
||||
daily_limit: Optional[int] = None,
|
||||
is_passive: Optional[bool] = None,
|
||||
icon: Optional[str] = None,
|
||||
) -> bool:
|
||||
"""
|
||||
说明:
|
||||
@@ -93,6 +103,8 @@ class GoodsInfo(db.Model):
|
||||
:param goods_discount: 商品折扣
|
||||
:param goods_limit_time: 商品限时时间
|
||||
:param daily_limit: 每日次数限制
|
||||
:param is_passive: 是否为被动
|
||||
:param icon: 图标
|
||||
"""
|
||||
try:
|
||||
query = (
|
||||
@@ -108,6 +120,8 @@ class GoodsInfo(db.Model):
|
||||
goods_discount=goods_discount or query.goods_discount,
|
||||
goods_limit_time=goods_limit_time if goods_limit_time is not None else query.goods_limit_time,
|
||||
daily_limit=daily_limit if daily_limit is not None else query.daily_limit,
|
||||
is_passive=is_passive if is_passive is not None else query.is_passive,
|
||||
icon=icon or query.icon
|
||||
).apply()
|
||||
return True
|
||||
except Exception as e:
|
||||
|
||||
@@ -0,0 +1,59 @@
|
||||
from datetime import datetime
|
||||
|
||||
from services.db_context import db
|
||||
|
||||
|
||||
class UserShopGoldLog(db.Model):
|
||||
__tablename__ = "user_shop_gold_log"
|
||||
id = db.Column(db.Integer(), primary_key=True)
|
||||
user_qq = db.Column(db.BigInteger(), nullable=False)
|
||||
group_id = db.Column(db.BigInteger(), nullable=False)
|
||||
type = db.Column(db.Integer(), nullable=False) # 0: 购买,1: 使用,2: 插件
|
||||
name = db.Column(db.String())
|
||||
spend_gold = db.Column(db.Integer(), nullable=False)
|
||||
num = db.Column(db.Integer(), nullable=False)
|
||||
create_time = db.Column(db.DateTime(timezone=True), nullable=False)
|
||||
|
||||
@classmethod
|
||||
async def add_shop_log(
|
||||
cls,
|
||||
user_qq: int,
|
||||
group_id: int,
|
||||
type_: int,
|
||||
name: str,
|
||||
num: int,
|
||||
spend_gold: int = 0,
|
||||
):
|
||||
"""
|
||||
说明:
|
||||
添加商店购买或使用日志
|
||||
参数:
|
||||
:param user_qq: qq号
|
||||
:param group_id: 所在群号
|
||||
:param type_: 类型
|
||||
:param name: 商品名称
|
||||
:param num: 数量
|
||||
:param spend_gold: 花费金币
|
||||
"""
|
||||
await cls.create(
|
||||
user_qq=user_qq,
|
||||
group_id=group_id,
|
||||
type=type_,
|
||||
name=name,
|
||||
num=num,
|
||||
spend_gold=spend_gold,
|
||||
create_time=datetime.now(),
|
||||
)
|
||||
|
||||
@classmethod
|
||||
async def get_user_log(cls, user_qq: int, group_id: int) -> "UserShopGoldLog":
|
||||
"""
|
||||
说明:
|
||||
获取用户日志
|
||||
参数:
|
||||
:param user_qq: qq号
|
||||
:param group_id: 所在群号
|
||||
"""
|
||||
return await cls.query.where(
|
||||
(cls.user_qq == user_qq) & (cls.group_qq == group_id)
|
||||
).first()
|
||||
@@ -15,6 +15,7 @@ from .data_source import (
|
||||
BilibiliSub,
|
||||
)
|
||||
from models.level_user import LevelUser
|
||||
from utils.manager import group_manager
|
||||
from configs.config import Config
|
||||
from utils.utils import is_number, scheduler, get_bot
|
||||
from typing import Optional, Tuple, Any
|
||||
@@ -160,7 +161,7 @@ async def _(
|
||||
async def _(event: MessageEvent, reg_group: Tuple[Any, ...] = RegexGroup()):
|
||||
msg = reg_group[0]
|
||||
id_ = (
|
||||
f"{event.user_id}:{event.group_id}"
|
||||
f"{event.group_id}"
|
||||
if isinstance(event, GroupMessageEvent)
|
||||
else f"{event.user_id}"
|
||||
)
|
||||
@@ -267,7 +268,8 @@ async def send_sub_msg(rst: str, sub: BilibiliSub, bot: Bot):
|
||||
and Config.get_config("bilibili_sub", "UP_MSG_AT_ALL")
|
||||
):
|
||||
rst = "[CQ:at,qq=all]\n" + rst
|
||||
await bot.send_group_msg(group_id=group_id, message=Message(rst))
|
||||
if group_manager.get_plugin_status("bilibili_sub", group_id):
|
||||
await bot.send_group_msg(group_id=group_id, message=Message(rst))
|
||||
else:
|
||||
await bot.send_private_msg(user_id=int(x), message=Message(rst))
|
||||
except Exception as e:
|
||||
|
||||
@@ -7,11 +7,11 @@ from asyncio.exceptions import TimeoutError
|
||||
from utils.utils import get_bot
|
||||
from .model import BilibiliSub
|
||||
from bilireq.live import get_room_info_by_id
|
||||
from .utils import get_meta
|
||||
from .utils import get_meta, get_user_card
|
||||
from utils.message_builder import image
|
||||
from bilireq.user import get_user_info
|
||||
from bilireq.user import get_videos
|
||||
# from .utils import get_videos
|
||||
from bilireq import dynamic
|
||||
from .utils import get_videos
|
||||
from typing import Optional, Tuple
|
||||
from configs.path_config import IMAGE_PATH
|
||||
from datetime import datetime
|
||||
@@ -83,7 +83,7 @@ async def add_up_sub(uid: int, sub_user: str) -> str:
|
||||
async with db.transaction():
|
||||
try:
|
||||
"""bilibili_api.user库中User类的get_user_info改为bilireq.user库的get_user_info方法"""
|
||||
user_info = await get_user_info(uid)
|
||||
user_info = await get_user_card(uid)
|
||||
except ResponseCodeError:
|
||||
return f"未找到UpId:{uid} 的信息,请检查Id是否正确"
|
||||
uname = user_info["name"]
|
||||
@@ -207,8 +207,10 @@ async def get_sub_status(id_: int, sub_type: str) -> Optional[str]:
|
||||
return await _get_up_status(id_)
|
||||
elif sub_type == "season":
|
||||
return await _get_season_status(id_)
|
||||
except ResponseCodeError:
|
||||
return f"Id:{id_} 获取信息失败...请检查订阅Id是否存在或稍后再试..."
|
||||
except ResponseCodeError as msg:
|
||||
logger.info(f"Id:{id_} 获取信息失败...{msg}")
|
||||
return None
|
||||
# return f"Id:{id_} 获取信息失败...请检查订阅Id是否存在或稍后再试..."
|
||||
# except Exception as e:
|
||||
# logger.error(f"获取订阅状态发生预料之外的错误 id_:{id_} {type(e)}:{e}")
|
||||
# return "发生了预料之外的错误..请稍后再试或联系管理员....."
|
||||
@@ -247,7 +249,7 @@ async def _get_up_status(id_: int) -> Optional[str]:
|
||||
"""
|
||||
_user = await BilibiliSub.get_sub(id_)
|
||||
"""bilibili_api.user库中User类的get_user_info改为bilireq.user库的get_user_info方法"""
|
||||
user_info = await get_user_info(_user.uid)
|
||||
user_info = await get_user_card(_user.uid)
|
||||
uname = user_info["name"]
|
||||
"""bilibili_api.user库中User类的get_videos改为bilireq.user库的get_videos方法"""
|
||||
video_info = await get_videos(_user.uid)
|
||||
@@ -347,16 +349,8 @@ async def get_user_dynamic(
|
||||
await page.set_viewport_size(
|
||||
{"width": 2560, "height": 1080, "timeout": 10000 * 20}
|
||||
)
|
||||
await page.wait_for_selector(".panel-area")
|
||||
await page.evaluate(
|
||||
"""
|
||||
xs = document.getElementById('internationalHeader');
|
||||
xs.remove();
|
||||
xs = document.getElementsByClassName('panel-area')
|
||||
xs[0].remove();
|
||||
"""
|
||||
)
|
||||
card = page.locator(".detail-card")
|
||||
await page.wait_for_selector(".bili-dyn-item__main")
|
||||
card = page.locator(".bili-dyn-item__main")
|
||||
await card.wait_for()
|
||||
await card.screenshot(
|
||||
path=dynamic_path / f"{local_user.sub_id}_{dynamic_upload_time}.jpg",
|
||||
|
||||
@@ -1,14 +1,15 @@
|
||||
from utils.image_utils import BuildImage
|
||||
from configs.path_config import IMAGE_PATH
|
||||
from utils.http_utils import AsyncHttpx
|
||||
from utils.http_utils import AsyncHttpx, get_user_agent
|
||||
# from bilibili_api import user
|
||||
from bilireq.user import get_user_info
|
||||
from httpx import AsyncClient
|
||||
from io import BytesIO
|
||||
|
||||
|
||||
BORDER_PATH = IMAGE_PATH / "border"
|
||||
BORDER_PATH.mkdir(parents=True, exist_ok=True)
|
||||
|
||||
BASE_URL = "https://api.bilibili.com"
|
||||
|
||||
async def get_pic(url: str) -> bytes:
|
||||
"""
|
||||
@@ -78,7 +79,6 @@ async def get_meta(media_id: int, auth=None, reqtype="both", **kwargs):
|
||||
"""
|
||||
from bilireq.utils import get
|
||||
|
||||
BASE_URL = "https://api.bilibili.com"
|
||||
url = f"{BASE_URL}/pgc/review/user"
|
||||
params = {"media_id": media_id}
|
||||
raw_json = await get(url, raw=True, params=params, auth=auth, reqtype=reqtype, **kwargs)
|
||||
@@ -86,8 +86,7 @@ async def get_meta(media_id: int, auth=None, reqtype="both", **kwargs):
|
||||
|
||||
|
||||
async def get_videos(
|
||||
uid: int, tid: int = 0, pn: int = 1, keyword: str = "", order: str = "pubdate",
|
||||
*, auth=None, reqtype="both", **kwargs
|
||||
uid: int, tid: int = 0, pn: int = 1, keyword: str = "", order: str = "pubdate"
|
||||
):
|
||||
"""
|
||||
获取用户投该视频信息
|
||||
@@ -100,15 +99,45 @@ async def get_videos(
|
||||
:param keyword: 搜索关键词
|
||||
:param order: 排序方式,可以为 “pubdate(上传日期从新到旧), stow(收藏从多到少), click(播放量从多到少)”
|
||||
"""
|
||||
from bilireq.utils import get
|
||||
BASE_URL = "https://api.bilibili.com"
|
||||
from bilireq.utils import ResponseCodeError
|
||||
|
||||
url = f"{BASE_URL}/x/space/arc/search"
|
||||
params = {
|
||||
"mid": uid,
|
||||
"ps": 30,
|
||||
"tid": tid,
|
||||
"pn": pn,
|
||||
"keyword": keyword,
|
||||
"order": order
|
||||
}
|
||||
return await get(url, params=params, auth=auth, reqtype=reqtype, **kwargs)
|
||||
headers = get_user_agent()
|
||||
headers["Referer"] = f"https://space.bilibili.com/{uid}/video"
|
||||
async with AsyncClient() as client:
|
||||
r = await client.head(
|
||||
"https://space.bilibili.com",
|
||||
headers=headers,
|
||||
)
|
||||
params = {
|
||||
"mid": uid,
|
||||
"ps": 30,
|
||||
"tid": tid,
|
||||
"pn": pn,
|
||||
"keyword": keyword,
|
||||
"order": order,
|
||||
}
|
||||
raw_json = (
|
||||
await client.get(url, params=params, headers=headers, cookies=r.cookies)
|
||||
).json()
|
||||
if raw_json["code"] != 0:
|
||||
raise ResponseCodeError(
|
||||
code=raw_json["code"],
|
||||
msg=raw_json["message"],
|
||||
data=raw_json.get("data", None),
|
||||
)
|
||||
return raw_json["data"]
|
||||
|
||||
async def get_user_card(mid, photo: bool = False, auth=None, reqtype="both", **kwargs):
|
||||
from bilireq.utils import get
|
||||
|
||||
url = f"{BASE_URL}/x/web-interface/card"
|
||||
return (
|
||||
await get(
|
||||
url,
|
||||
params={"mid": mid, "photo": photo},
|
||||
auth=auth,
|
||||
reqtype=reqtype,
|
||||
**kwargs,
|
||||
)
|
||||
)["card"]
|
||||
|
||||
@@ -9,7 +9,7 @@ import platform
|
||||
# asyncio.set_event_loop_policy(asyncio.WindowsSelectorEventLoopPolicy())
|
||||
|
||||
|
||||
url = "http://www.eclzz.plus"
|
||||
url = "http://www.eclzz.cool"
|
||||
|
||||
|
||||
async def get_bt_info(keyword: str, page: int):
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
from nonebot.adapters.onebot.v11 import Bot
|
||||
from nonebot.adapters.onebot.v11 import Bot, MessageEvent
|
||||
from nonebot.permission import SUPERUSER
|
||||
from nonebot import on_command
|
||||
from .data_source import check_update, get_latest_version_data
|
||||
@@ -50,24 +50,24 @@ restart = on_command(
|
||||
|
||||
|
||||
@update_zhenxun.handle()
|
||||
async def _(bot: Bot):
|
||||
async def _(bot: Bot, event: MessageEvent):
|
||||
try:
|
||||
code, error = await check_update(bot)
|
||||
if error:
|
||||
logger.error(f"更新真寻未知错误 {error}")
|
||||
await bot.send_private_msg(
|
||||
user_id=int(list(bot.config.superusers)[0]), message=f"更新真寻未知错误 {error}"
|
||||
user_id=event.user_id, message=f"更新真寻未知错误 {error}"
|
||||
)
|
||||
except Exception as e:
|
||||
logger.error(f"更新真寻未知错误 {type(e)}:{e}")
|
||||
await bot.send_private_msg(
|
||||
user_id=int(list(bot.config.superusers)[0]),
|
||||
user_id=event.user_id,
|
||||
message=f"更新真寻未知错误 {type(e)}:{e}",
|
||||
)
|
||||
else:
|
||||
if code == 200:
|
||||
await bot.send_private_msg(
|
||||
user_id=int(list(bot.config.superusers)[0]), message=f"更新完毕,请重启真寻...."
|
||||
user_id=event.user_id, message=f"更新完毕,请重启真寻...."
|
||||
)
|
||||
|
||||
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
import contextlib
|
||||
import random
|
||||
import dateparser
|
||||
from lxml import etree
|
||||
from typing import List, Optional
|
||||
from typing import List, Optional, Tuple
|
||||
from urllib.parse import unquote
|
||||
from pydantic import ValidationError
|
||||
from nonebot.log import logger
|
||||
@@ -12,7 +13,6 @@ from .base_handle import BaseHandle, BaseData, UpEvent as _UpEvent, UpChar as _U
|
||||
from ..config import draw_config
|
||||
from ..util import remove_prohibited_str, cn2py
|
||||
from utils.image_utils import BuildImage
|
||||
import asyncio
|
||||
|
||||
try:
|
||||
import ujson as json
|
||||
@@ -29,7 +29,7 @@ class AzurChar(BaseData):
|
||||
|
||||
|
||||
class UpChar(_UpChar):
|
||||
type_: str # 舰娘类型
|
||||
type_: str # 舰娘类型
|
||||
|
||||
|
||||
class UpEvent(_UpEvent):
|
||||
@@ -53,7 +53,9 @@ class AzurHandle(BaseHandle[AzurChar]):
|
||||
type_ = ["维修", "潜艇", "重巡", "轻航", "航母"]
|
||||
up_pool_flag = pool_name == "活动"
|
||||
# Up
|
||||
up_ship = [x for x in self.UP_EVENT.up_char if x.zoom > 0]
|
||||
up_ship = (
|
||||
[x for x in self.UP_EVENT.up_char if x.zoom > 0] if self.UP_EVENT else []
|
||||
)
|
||||
# print(up_ship)
|
||||
acquire_char = None
|
||||
if up_ship and up_pool_flag:
|
||||
@@ -61,19 +63,15 @@ class AzurHandle(BaseHandle[AzurChar]):
|
||||
# 初始化概率
|
||||
cur_ = up_ship[0].zoom / 100
|
||||
for i in range(len(up_ship)):
|
||||
try:
|
||||
up_zoom.append((cur_, cur_ + up_ship[i+1].zoom / 100))
|
||||
cur_ += up_ship[i+1].zoom / 100
|
||||
except IndexError:
|
||||
pass
|
||||
with contextlib.suppress(IndexError):
|
||||
up_zoom.append((cur_, cur_ + up_ship[i + 1].zoom / 100))
|
||||
cur_ += up_ship[i + 1].zoom / 100
|
||||
rand = random.random()
|
||||
# 抽取up
|
||||
for i, zoom in enumerate(up_zoom):
|
||||
if zoom[0] <= rand <= zoom[1]:
|
||||
try:
|
||||
with contextlib.suppress(IndexError):
|
||||
acquire_char = [x for x in self.ALL_CHAR if x.name == up_ship[i].name][0]
|
||||
except IndexError:
|
||||
pass
|
||||
# 没有up或者未抽取到up
|
||||
if not acquire_char:
|
||||
star = self.get_star(
|
||||
@@ -92,9 +90,6 @@ class AzurHandle(BaseHandle[AzurChar]):
|
||||
])
|
||||
return acquire_char
|
||||
|
||||
# async def draw(self, count: int, **kwargs) -> Message:
|
||||
# return await asyncio.get_event_loop().run_in_executor(None, self._draw, count)
|
||||
|
||||
async def draw(self, count: int, **kwargs) -> Message:
|
||||
index2card = self.get_cards(count, **kwargs)
|
||||
cards = [card[0] for card in index2card]
|
||||
@@ -162,22 +157,22 @@ class AzurHandle(BaseHandle[AzurChar]):
|
||||
return
|
||||
dom = etree.HTML(result, etree.HTMLParser())
|
||||
contents = dom.xpath(
|
||||
"//div[@class='resp-tabs-container']/div[@class='resp-tab-content']"
|
||||
"//div[@class='mw-body-content mw-content-ltr']/div[@class='mw-parser-output']"
|
||||
)
|
||||
for index, content in enumerate(contents):
|
||||
char_list = content.xpath("./table/tbody/tr[2]/td/div/div/div/div")
|
||||
char_list = content.xpath("./div[@id='CardSelectTr']/div")
|
||||
for char in char_list:
|
||||
try:
|
||||
name = char.xpath("./a/@title")[0]
|
||||
frame = char.xpath("./div/a/img/@alt")[0]
|
||||
avatar = char.xpath("./a/img/@srcset")[0]
|
||||
name = char.xpath("./div/a/@title")[0]
|
||||
frame = char.xpath("./div/div/a/img/@alt")[0]
|
||||
avatar = char.xpath("./div/a/img/@srcset")[0]
|
||||
except IndexError:
|
||||
continue
|
||||
member_dict = {
|
||||
"名称": remove_prohibited_str(name),
|
||||
"头像": unquote(str(avatar).split(" ")[-2]),
|
||||
"星级": self.parse_star(frame),
|
||||
"类型": self.parse_type(index),
|
||||
"类型": char.xpath("./@data-param1")[0].split(",")[1],
|
||||
}
|
||||
info[member_dict["名称"]] = member_dict
|
||||
# 更新额外信息
|
||||
@@ -225,48 +220,21 @@ class AzurHandle(BaseHandle[AzurChar]):
|
||||
|
||||
@staticmethod
|
||||
def parse_star(star: str) -> int:
|
||||
if star in ["舰娘头像外框普通.png", "舰娘头像外框白色.png"]:
|
||||
if star in {"舰娘头像外框普通.png", "舰娘头像外框白色.png"}:
|
||||
return 1
|
||||
elif star in ["舰娘头像外框稀有.png", "舰娘头像外框蓝色.png"]:
|
||||
elif star in {"舰娘头像外框稀有.png", "舰娘头像外框蓝色.png"}:
|
||||
return 2
|
||||
elif star in ["舰娘头像外框精锐.png", "舰娘头像外框紫色.png"]:
|
||||
elif star in {"舰娘头像外框精锐.png", "舰娘头像外框紫色.png"}:
|
||||
return 3
|
||||
elif star in ["舰娘头像外框超稀有.png", "舰娘头像外框金色.png"]:
|
||||
elif star in {"舰娘头像外框超稀有.png", "舰娘头像外框金色.png"}:
|
||||
return 4
|
||||
elif star in ["舰娘头像外框海上传奇.png", "舰娘头像外框彩色.png"]:
|
||||
elif star in {"舰娘头像外框海上传奇.png", "舰娘头像外框彩色.png"}:
|
||||
return 5
|
||||
elif star in [
|
||||
"舰娘头像外框最高方案.png",
|
||||
"舰娘头像外框决战方案.png",
|
||||
"舰娘头像外框超稀有META.png",
|
||||
"舰娘头像外框精锐META.png",
|
||||
]:
|
||||
elif star in {"舰娘头像外框最高方案.png", "舰娘头像外框决战方案.png", "舰娘头像外框超稀有META.png", "舰娘头像外框精锐META.png"}:
|
||||
return 6
|
||||
else:
|
||||
return 6
|
||||
|
||||
@staticmethod
|
||||
def parse_type(index: int) -> str:
|
||||
azur_types = [
|
||||
"驱逐",
|
||||
"轻巡",
|
||||
"重巡",
|
||||
"超巡",
|
||||
"战巡",
|
||||
"战列",
|
||||
"航母",
|
||||
"航站",
|
||||
"轻航",
|
||||
"重炮",
|
||||
"维修",
|
||||
"潜艇",
|
||||
"运输",
|
||||
]
|
||||
try:
|
||||
return azur_types[index]
|
||||
except IndexError:
|
||||
return azur_types[0]
|
||||
|
||||
async def update_up_char(self):
|
||||
url = "https://wiki.biligame.com/blhx/游戏活动表"
|
||||
result = await self.get_url(url)
|
||||
@@ -290,7 +258,7 @@ class AzurHandle(BaseHandle[AzurChar]):
|
||||
up_chars = []
|
||||
for ship in ships:
|
||||
name = ship.xpath("./tbody/tr/td[2]/p/a/@title")[0]
|
||||
type_ = ship.xpath("./tbody/tr/td[2]/p/small/text()")[0] # 舰船类型
|
||||
type_ = ship.xpath("./tbody/tr/td[2]/p/small/text()")[0] # 舰船类型
|
||||
try:
|
||||
p = float(str(ship.xpath(".//sup/text()")[0]).strip("%"))
|
||||
except (IndexError, ValueError):
|
||||
@@ -298,7 +266,9 @@ class AzurHandle(BaseHandle[AzurChar]):
|
||||
star = self.parse_star(
|
||||
ship.xpath("./tbody/tr/td[1]/div/div/div/a/img/@alt")[0]
|
||||
)
|
||||
up_chars.append(UpChar(name=name, star=star, limited=False, zoom=p, type_=type_))
|
||||
up_chars.append(
|
||||
UpChar(name=name, star=star, limited=False, zoom=p, type_=type_)
|
||||
)
|
||||
self.UP_EVENT = UpEvent(
|
||||
title=title,
|
||||
pool_img="",
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
import random
|
||||
import json
|
||||
from utils.http_utils import AsyncHttpx
|
||||
from lxml import etree
|
||||
from typing import List, Tuple
|
||||
from PIL import ImageDraw
|
||||
@@ -107,27 +109,70 @@ class BaHandle(BaseHandle[BaChar]):
|
||||
else:
|
||||
return 1
|
||||
|
||||
# Bwiki 待恢复
|
||||
# async def _update_info(self):
|
||||
# info = {}
|
||||
# url = "https://wiki.biligame.com/bluearchive/学生筛选"
|
||||
# result = await self.get_url(url)
|
||||
# if not result:
|
||||
# logger.warning(f"更新 {self.game_name_cn} 出错")
|
||||
# return
|
||||
# else:
|
||||
# dom = etree.HTML(result, etree.HTMLParser())
|
||||
# char_list = dom.xpath("//div[@class='filters']/table[2]/tbody/tr")
|
||||
# for char in char_list:
|
||||
# try:
|
||||
# name = char.xpath("./td[2]/a/div/text()")[0]
|
||||
# avatar = char.xpath("./td[1]/div/div/a/img/@data-src")[0]
|
||||
# star_pic = char.xpath("./td[4]/img/@alt")[0]
|
||||
# except IndexError:
|
||||
# continue
|
||||
# member_dict = {
|
||||
# "头像": unquote(str(avatar)),
|
||||
# "名称": remove_prohibited_str(name),
|
||||
# "星级": self.title2star(star_pic),
|
||||
# }
|
||||
# info[member_dict["名称"]] = member_dict
|
||||
# self.dump_data(info)
|
||||
# logger.info(f"{self.game_name_cn} 更新成功")
|
||||
# # 下载头像
|
||||
# for value in info.values():
|
||||
# await self.download_img(value["头像"], value["名称"])
|
||||
# # 下载星星
|
||||
# await self.download_img(
|
||||
# "https://patchwiki.biligame.com/images/bluearchive/thumb/e/e0/82nj2x9sxko473g7782r14fztd4zyky.png/15px-Star-1.png",
|
||||
# "star-1",
|
||||
# )
|
||||
# await self.download_img(
|
||||
# "https://patchwiki.biligame.com/images/bluearchive/thumb/0/0b/msaff2g0zk6nlyl1rrn7n1ri4yobcqc.png/30px-Star-2.png",
|
||||
# "star-2",
|
||||
# )
|
||||
# await self.download_img(
|
||||
# "https://patchwiki.biligame.com/images/bluearchive/thumb/8/8a/577yv79x1rwxk8efdccpblo0lozl158.png/46px-Star-3.png",
|
||||
# "star-3"
|
||||
# )
|
||||
|
||||
# 数据来源:SCHALE DB
|
||||
async def _update_info(self):
|
||||
info = {}
|
||||
url = "https://wiki.biligame.com/bluearchive/学生筛选"
|
||||
result = await self.get_url(url)
|
||||
if not result:
|
||||
url = "https://lonqie.github.io/SchaleDB/data/cn/students.min.json?v=49"
|
||||
result = await AsyncHttpx.get(url)
|
||||
if result.status_code != 200:
|
||||
logger.warning(f"更新 {self.game_name_cn} 出错")
|
||||
return
|
||||
else:
|
||||
dom = etree.HTML(result, etree.HTMLParser())
|
||||
char_list = dom.xpath("//div[@class='filters']/table[2]/tbody/tr")
|
||||
char_list = json.loads(result.text)
|
||||
for char in char_list:
|
||||
try:
|
||||
name = char.xpath("./td[2]/a/div/text()")[0]
|
||||
avatar = char.xpath("./td[1]/div/div/a/img/@data-src")[0]
|
||||
star_pic = char.xpath("./td[4]/img/@alt")[0]
|
||||
name = char.get("Name")
|
||||
avatar = "https://lonqie.github.io/SchaleDB/images/student/icon/"+char.get("CollectionTexture")+".png"
|
||||
star = char.get("StarGrade")
|
||||
except IndexError:
|
||||
continue
|
||||
member_dict = {
|
||||
"头像": unquote(str(avatar)),
|
||||
"名称": remove_prohibited_str(name),
|
||||
"星级": self.title2star(star_pic),
|
||||
"星级": star,
|
||||
}
|
||||
info[member_dict["名称"]] = member_dict
|
||||
self.dump_data(info)
|
||||
@@ -147,4 +192,5 @@ class BaHandle(BaseHandle[BaChar]):
|
||||
await self.download_img(
|
||||
"https://patchwiki.biligame.com/images/bluearchive/thumb/8/8a/577yv79x1rwxk8efdccpblo0lozl158.png/46px-Star-3.png",
|
||||
"star-3"
|
||||
)
|
||||
)
|
||||
|
||||
@@ -331,9 +331,7 @@ class PrettyHandle(BaseHandle[PrettyData]):
|
||||
try:
|
||||
title = announcement.xpath("./@title")[0]
|
||||
url = "https://wiki.biligame.com/" + announcement.xpath("./@href")[0]
|
||||
if re.match(r".*?\d{8}$", title) or re.match(
|
||||
r"^\d{1,2}月\d{1,2}日.*?", title
|
||||
):
|
||||
if "卡池" in title:
|
||||
break
|
||||
except IndexError:
|
||||
continue
|
||||
@@ -353,45 +351,42 @@ class PrettyHandle(BaseHandle[PrettyData]):
|
||||
up_chars_name = []
|
||||
up_cards = []
|
||||
up_cards_name = []
|
||||
soup = BeautifulSoup(result, "lxml")
|
||||
heads = soup.find_all("span", {"class": "mw-headline"})
|
||||
dom = etree.HTML(result, etree.HTMLParser())
|
||||
content = dom.xpath('//*[@id="mw-content-text"]/div/div/div[1]/div')[0]
|
||||
heads = content.xpath("./div")
|
||||
for head in heads:
|
||||
if "时间" in head.text or "期间" in head.text:
|
||||
time = head.find_next("p").text.split("\n")[0]
|
||||
if "时间" in head.find("./img").tail or "期间" in head.find("./img").tail:
|
||||
time = content.xpath("./p")[1].text.split("\n")[0]
|
||||
if "~" in time:
|
||||
start, end = time.split("~")
|
||||
start_time = dateparser.parse(start)
|
||||
end_time = dateparser.parse(end)
|
||||
elif "赛马娘" in head.text:
|
||||
char_img = head.find_next("center").find("img")[
|
||||
"src"
|
||||
]
|
||||
lines = str(head.find_next("p").text).split("\n")
|
||||
elif "赛马娘" in head.find("./img").tail:
|
||||
char_img = content.xpath("./center")[1].find("./a/img").get("src")
|
||||
lines = content.xpath("./p")[2].xpath("string(.)").split("\n")
|
||||
chars = [
|
||||
line
|
||||
for line in lines
|
||||
if "★" in line and "【" in line and "】" in line
|
||||
if "★" in line and "[" in line and "]" in line
|
||||
]
|
||||
for char in set(chars): # list去重
|
||||
star = char.count("★")
|
||||
name = re.split(r"[【】]", char)[-2].strip()
|
||||
name = re.split("[ ]",char)[-1]
|
||||
up_chars.append(
|
||||
UpChar(name=name, star=star, limited=False, zoom=70)
|
||||
)
|
||||
up_chars_name.append(name)
|
||||
elif "支援卡" in head.text:
|
||||
card_img = head.find_next("center").find("img")[
|
||||
"src"
|
||||
]
|
||||
lines = str(head.find_next("p").text).split("\n")
|
||||
elif "支援卡" in head.find("./img").tail:
|
||||
card_img = content.xpath("./center")[2].find("./a/img").get("src")
|
||||
lines = content.xpath("./p")[3].xpath("string(.)").split("\n")
|
||||
cards = [
|
||||
line
|
||||
for line in lines
|
||||
if "R" in line and "【" in line and "】" in line
|
||||
if "R" in line and "[" in line and "]" in line
|
||||
]
|
||||
for card in cards:
|
||||
for card in set(cards):
|
||||
star = 3 if "SSR" in card else 2 if "SR" in card else 1
|
||||
name = re.split(r"[【】]", card)[-2].strip()
|
||||
name = re.split("[ ]",card)[-1]
|
||||
up_cards.append(
|
||||
UpChar(name=name, star=star, limited=False, zoom=70)
|
||||
)
|
||||
@@ -404,7 +399,7 @@ class PrettyHandle(BaseHandle[PrettyData]):
|
||||
start_time=start_time,
|
||||
end_time=end_time,
|
||||
up_char=up_chars,
|
||||
up_name=up_chars_name,
|
||||
up_name=",".join(up_chars_name),
|
||||
)
|
||||
self.UP_CARD = UpEvent(
|
||||
title=title,
|
||||
@@ -412,7 +407,7 @@ class PrettyHandle(BaseHandle[PrettyData]):
|
||||
start_time=start_time,
|
||||
end_time=end_time,
|
||||
up_char=up_cards,
|
||||
up_name=up_cards_name,
|
||||
up_name=",".join(up_cards_name),
|
||||
)
|
||||
self.dump_up_char()
|
||||
logger.info(f"成功获取{self.game_name_cn}当前up信息...当前up池: {title}")
|
||||
@@ -424,10 +419,8 @@ class PrettyHandle(BaseHandle[PrettyData]):
|
||||
self.load_up_char()
|
||||
if self.UP_CHAR and self.UP_CARD:
|
||||
return Message(
|
||||
Message.template("重载成功!\n当前UP池子:{}{:image}\n当前支援卡池子:{}{:image}").format(
|
||||
Message.template("重载成功!\n当前UP池子:{}\n当前支援卡池子:{}").format(
|
||||
self.UP_CHAR.up_name,
|
||||
self.UP_CHAR.pool_img,
|
||||
self.UP_CARD.up_name,
|
||||
self.UP_CARD.pool_img,
|
||||
)
|
||||
)
|
||||
|
||||
+6
-15
@@ -1,6 +1,8 @@
|
||||
from io import BytesIO
|
||||
import imagehash
|
||||
from PIL import Image
|
||||
from nonebot.adapters.onebot.v11.permission import GROUP
|
||||
from configs.path_config import TEMP_PATH
|
||||
from utils.image_utils import get_img_hash
|
||||
import random
|
||||
from utils.message_builder import image
|
||||
from nonebot import on_message
|
||||
@@ -8,7 +10,6 @@ from utils.utils import get_message_img, get_message_text
|
||||
from nonebot.adapters.onebot.v11 import GroupMessageEvent
|
||||
from configs.config import Config
|
||||
from utils.http_utils import AsyncHttpx
|
||||
from services.log import logger
|
||||
from configs.config import NICKNAME
|
||||
|
||||
|
||||
@@ -90,7 +91,7 @@ async def _(event: GroupMessageEvent):
|
||||
if not img and not msg:
|
||||
return
|
||||
if img:
|
||||
img_hash = await get_fudu_img_hash(img[0], event.group_id)
|
||||
img_hash = await get_fudu_img_hash(img[0])
|
||||
else:
|
||||
img_hash = ""
|
||||
add_msg = msg + "|-|" + img_hash
|
||||
@@ -123,15 +124,5 @@ async def _(event: GroupMessageEvent):
|
||||
await fudu.finish("[[_task|fudu]]" + rst)
|
||||
|
||||
|
||||
async def get_fudu_img_hash(url, group_id):
|
||||
try:
|
||||
if await AsyncHttpx.download_file(
|
||||
url, TEMP_PATH / f"compare_{group_id}_img.jpg"
|
||||
):
|
||||
img_hash = get_img_hash(TEMP_PATH / f"compare_{group_id}_img.jpg")
|
||||
return str(img_hash)
|
||||
else:
|
||||
logger.warning(f"复读下载图片失败...")
|
||||
except Exception as e:
|
||||
logger.warning(f"复读读取图片Hash出错 {type(e)}:{e}")
|
||||
return ""
|
||||
async def get_fudu_img_hash(url):
|
||||
return str(imagehash.average_hash(Image.open(BytesIO((await AsyncHttpx.get(url)).content))))
|
||||
|
||||
@@ -47,7 +47,7 @@ __plugin_block_limit__ = {
|
||||
qr = on_command("原神资源查询", aliases={"原神资源查找"}, priority=5, block=True)
|
||||
qr_lst = on_command("原神资源列表", priority=5, block=True)
|
||||
rex_qr = on_regex(".*?(在哪|在哪里|哪有|哪里有).*?", priority=5, block=True)
|
||||
update_info = on_command("更新原神资源信息", permission=SUPERUSER, priority=1, block=True)
|
||||
update_info = on_regex("^更新原神资源信息$", permission=SUPERUSER, priority=1, block=True)
|
||||
|
||||
|
||||
@qr.handle()
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
from configs.path_config import IMAGE_PATH, TEXT_PATH
|
||||
from configs.path_config import IMAGE_PATH, TEXT_PATH, TEMP_PATH
|
||||
from utils.image_utils import BuildImage
|
||||
from typing import Tuple, List
|
||||
from math import sqrt, pow
|
||||
@@ -118,7 +118,7 @@ class Map:
|
||||
self._generate_best_route()
|
||||
self.map.crop((min_width, min_height, max_width, max_height))
|
||||
rand = random.randint(1, 10000)
|
||||
self.map.save(f"{IMAGE_PATH}/temp/genshin_map_{rand}.png")
|
||||
self.map.save(f"{TEMP_PATH}/genshin_map_{rand}.png")
|
||||
return rand
|
||||
|
||||
# 资源数量
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
from pathlib import Path
|
||||
from typing import Tuple, Optional, List
|
||||
from configs.path_config import IMAGE_PATH, TEXT_PATH
|
||||
from configs.path_config import IMAGE_PATH, TEXT_PATH, TEMP_PATH
|
||||
from utils.message_builder import image
|
||||
from services.log import logger
|
||||
from utils.image_utils import BuildImage
|
||||
@@ -57,7 +57,7 @@ async def query_resource(resource_name: str) -> str:
|
||||
None, map_.generate_resource_icon_in_map
|
||||
)
|
||||
return (
|
||||
f"{image(f'genshin_map_{rand}.png', 'temp')}"
|
||||
f"{image(TEMP_PATH / f'genshin_map_{rand}.png')}"
|
||||
f"\n\n※ {resource_name} 一共找到 {count} 个位置点\n※ 数据来源于米游社wiki"
|
||||
)
|
||||
|
||||
@@ -172,28 +172,29 @@ async def download_map_init(semaphore: Semaphore, flag: bool = False):
|
||||
if data["message"] == "OK":
|
||||
data = json.loads(data["data"]["info"]["detail"])
|
||||
CENTER_POINT = (data["origin"][0], data["origin"][1])
|
||||
if not _map.exists():
|
||||
if not _map.exists() or flag:
|
||||
data = data["slices"]
|
||||
idx = 0
|
||||
for _map_data in data[0]:
|
||||
map_url = _map_data["url"]
|
||||
await download_image(
|
||||
map_url,
|
||||
map_path / f"{idx}.png",
|
||||
semaphore,
|
||||
force_flag=flag,
|
||||
)
|
||||
BuildImage(
|
||||
0, 0, background=f"{map_path}/{idx}.png", ratio=MAP_RATIO
|
||||
).save()
|
||||
idx += 1
|
||||
_w, h = BuildImage(0, 0, background=f"{map_path}/0.png").size
|
||||
w = _w * len(os.listdir(map_path))
|
||||
map_file = BuildImage(w, h, _w, h, ratio=MAP_RATIO)
|
||||
w_len = len(data[0])
|
||||
h_len = len(data)
|
||||
for _map_data in data:
|
||||
for _map in _map_data:
|
||||
map_url = _map["url"]
|
||||
await download_image(
|
||||
map_url,
|
||||
map_path / f"{idx}.png",
|
||||
semaphore,
|
||||
force_flag=flag,
|
||||
)
|
||||
BuildImage(
|
||||
0, 0, background=f"{map_path}/{idx}.png", ratio=MAP_RATIO
|
||||
).save()
|
||||
idx += 1
|
||||
w, h = BuildImage(0, 0, background=f"{map_path}/0.png").size
|
||||
map_file = BuildImage(w * w_len, h * h_len, w, h, ratio=MAP_RATIO)
|
||||
for i in range(idx):
|
||||
map_file.paste(
|
||||
BuildImage(0, 0, background=f"{map_path}/{i}.png")
|
||||
)
|
||||
img = BuildImage(0, 0, background=f"{map_path}/{i}.png")
|
||||
await map_file.apaste(img)
|
||||
map_file.save(f"{map_path}/map.png")
|
||||
else:
|
||||
logger.warning(f'获取原神地图失败 msg: {data["message"]}')
|
||||
@@ -202,7 +203,7 @@ async def download_map_init(semaphore: Semaphore, flag: bool = False):
|
||||
except (TimeoutError, ConnectTimeout):
|
||||
logger.warning("下载原神地图数据超时....")
|
||||
except Exception as e:
|
||||
logger.error(f"下载原神地图数据超时 {type(e)}:{e}")
|
||||
logger.error(f"下载原神地图数据失败 {type(e)}:{e}")
|
||||
|
||||
|
||||
# 下载资源类型数据
|
||||
|
||||
@@ -93,6 +93,8 @@ async def _(event: MessageEvent, cmd: Tuple[str, ...] = Command(), arg: Message
|
||||
print(cookie)
|
||||
cookie_json = json.loads(cookie)
|
||||
print(cookie_json)
|
||||
if 'login_ticket' not in cookie_json:
|
||||
await bind.finish("请发送正确完整的cookie!")
|
||||
login_ticket = cookie_json['login_ticket']
|
||||
# try:
|
||||
res = await AsyncHttpx.get(url=bbs_Cookie_url.format(login_ticket))
|
||||
|
||||
@@ -44,6 +44,8 @@ async def _(event: MessageEvent, cmd: Tuple[str, ...] = Command()):
|
||||
uid = await Genshin.get_user_uid(event.user_id)
|
||||
if cmd == "查看我的cookie":
|
||||
my_cookie = await Genshin.get_user_cookie(uid, True)
|
||||
if isinstance(event, GroupMessageEvent):
|
||||
await genshin_matcher.finish("请私聊查看您的cookie!")
|
||||
await genshin_matcher.finish("您的cookie为" + my_cookie)
|
||||
if not uid or not await Genshin.get_user_cookie(uid, True):
|
||||
await genshin_matcher.finish("请先绑定uid和cookie!")
|
||||
@@ -51,9 +53,9 @@ async def _(event: MessageEvent, cmd: Tuple[str, ...] = Command()):
|
||||
# await genshin_matcher.finish("请更新cookie!")
|
||||
if cmd == "原神我硬签":
|
||||
try:
|
||||
await genshin_matcher.send("正在进行签到...", at_sender=True)
|
||||
msg = await genshin_sign(uid)
|
||||
return_data = await mihoyobbs_sign(event.user_id)
|
||||
await genshin_matcher.send(return_data)
|
||||
logger.info(
|
||||
f"(USER {event.user_id}, "
|
||||
f"GROUP {event.group_id if isinstance(event, GroupMessageEvent) else 'private'}) UID:{uid} 原神签到"
|
||||
@@ -70,7 +72,7 @@ async def _(event: MessageEvent, cmd: Tuple[str, ...] = Command()):
|
||||
await u.clear_sign_time(uid)
|
||||
next_date = await Genshin.random_sign_time(uid)
|
||||
add_job(event.user_id, uid, next_date)
|
||||
msg += f"\n因开启自动签到\n下一次签到时间为:{next_date.replace(microsecond=0)}"
|
||||
msg += f"\n{return_data}\n因开启自动签到\n下一次签到时间为:{next_date.replace(microsecond=0)}"
|
||||
except Exception as e:
|
||||
msg = "原神签到失败..请尝试检查cookie或报告至管理员!"
|
||||
logger.info(
|
||||
|
||||
@@ -38,16 +38,18 @@ mihoyobbs_matcher = on_command(
|
||||
|
||||
@mihoyobbs_matcher.handle()
|
||||
async def _(event: MessageEvent, cmd: Tuple[str, ...] = Command()):
|
||||
await mihoyobbs_matcher.send("提交米游社签到申请")
|
||||
await mihoyobbs_matcher.send("提交米游社签到申请", at_sender=True)
|
||||
return_data = await mihoyobbs_sign(event.user_id)
|
||||
if return_data:
|
||||
await mihoyobbs_matcher.finish(return_data)
|
||||
await mihoyobbs_matcher.finish(return_data, at_sender=True)
|
||||
else:
|
||||
await mihoyobbs_matcher.finish("米游社签到失败,请查看控制台输出")
|
||||
await mihoyobbs_matcher.finish("米游社签到失败,请查看控制台输出", at_sender=True)
|
||||
|
||||
|
||||
async def mihoyobbs_sign(user_id):
|
||||
uid = await Genshin.get_user_uid(user_id)
|
||||
if not uid or not await Genshin.get_user_cookie(uid, True):
|
||||
await mihoyobbs_matcher.finish("请先绑定uid和cookie!", at_sender=True)
|
||||
stuid = await Genshin.get_stuid(uid)
|
||||
stoken = await Genshin.get_stoken(uid)
|
||||
cookie = await Genshin.get_user_cookie(uid)
|
||||
|
||||
@@ -3,7 +3,6 @@ from nonebot.adapters.onebot.v11 import MessageEvent, GroupMessageEvent
|
||||
from services.log import logger
|
||||
from .data_source import get_user_memo, get_memo
|
||||
from .._models import Genshin
|
||||
from nonebot.plugin import export
|
||||
|
||||
|
||||
__zx_plugin_name__ = "原神便笺查询"
|
||||
@@ -28,10 +27,6 @@ __plugin_settings__ = {
|
||||
__plugin_block_limit__ = {}
|
||||
|
||||
|
||||
export = export()
|
||||
|
||||
export.get_memo = get_memo
|
||||
|
||||
query_memo_matcher = on_command("原神便签查询", aliases={"原神便笺查询", "yss"}, priority=5, block=True)
|
||||
|
||||
|
||||
|
||||
@@ -35,8 +35,8 @@ async def _():
|
||||
"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",
|
||||
"https://s3.bmp.ovh/imgs/2022/07/24/28d9338c7da4bcb2.png",
|
||||
"https://genshin.honeyhunterworld.com/img/gadget/i_3016.png",
|
||||
"https://s3.bmp.ovh/imgs/2022/08/21/3a3b2e6c22e305ff.png",
|
||||
"https://s3.bmp.ovh/imgs/2022/08/21/c2d7ace21e1d46cf.png",
|
||||
],
|
||||
):
|
||||
file = memo_path / name
|
||||
|
||||
@@ -195,6 +195,7 @@ def parsed_data(
|
||||
"magic_chest_number": data["stats"]["magic_chest_number"], # 奇馈宝箱
|
||||
"common_chest_number": data["stats"]["common_chest_number"], # 普通宝箱
|
||||
"electroculus_number": data["stats"]["electroculus_number"], # 雷神瞳已收集
|
||||
"dendroculus_number": data["stats"]["dendroculus_number"], # 草神瞳已收集
|
||||
}
|
||||
world_data_dict = {}
|
||||
for world in data["world_explorations"]:
|
||||
|
||||
@@ -129,7 +129,7 @@ def get_user_data_image(
|
||||
"""
|
||||
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 = BuildImage(1440, 560, color="#E3DBD1", font="HYWenHei-85W.ttf")
|
||||
region.circle_corner(30)
|
||||
uname_img = BuildImage(
|
||||
0,
|
||||
@@ -163,36 +163,42 @@ def get_user_data_image(
|
||||
/ 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)
|
||||
region.paste(uname_img, (int(170 + uid_img.w / 2 - uname_img.w / 2), 365), True)
|
||||
region.paste(uid_img, (170, 415), True)
|
||||
region.paste(ava_bk, (int(550 / 2 - ava_bk.w / 2), 100), True)
|
||||
data_img = BuildImage(
|
||||
800, 400, color="#E3DBD1", font="HYWenHei-85W.ttf", font_size=40
|
||||
800, 510, 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["way_point_number"],
|
||||
],
|
||||
[
|
||||
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"],
|
||||
role_data["dendroculus_number"],
|
||||
],
|
||||
[
|
||||
role_data["domain_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"],
|
||||
],
|
||||
]
|
||||
for key, value in zip(keys, values):
|
||||
@@ -215,7 +221,7 @@ def get_user_data_image(
|
||||
)
|
||||
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))
|
||||
_tmp_data_img.paste(tmp_, ((_width + 15) if keys.index(key) == 1 else _width, 0))
|
||||
_width += 200
|
||||
data_img.paste(_tmp_data_img, (0, _height))
|
||||
_height += _tmp_data_img.h - 70
|
||||
@@ -228,7 +234,7 @@ def get_home_data_image(home_data_list: List[Dict]) -> BuildImage:
|
||||
画出家园数据
|
||||
:param home_data_list: 家园列表
|
||||
"""
|
||||
h = 130 + 300 * 4
|
||||
h = 130 + 340 * 4
|
||||
region = BuildImage(
|
||||
550, h, color="#E3DBD1", font="HYWenHei-85W.ttf", font_size=40
|
||||
)
|
||||
@@ -287,7 +293,7 @@ def get_home_data_image(home_data_list: List[Dict]) -> BuildImage:
|
||||
x.paste(black_img, alpha=True, center_type="center")
|
||||
x.circle_corner(50)
|
||||
region.paste(x, (0, height), True, "by_width")
|
||||
height += 300
|
||||
height += 340
|
||||
return region
|
||||
|
||||
|
||||
@@ -297,9 +303,9 @@ def get_country_data_image(world_data_dict: Dict) -> BuildImage:
|
||||
:param world_data_dict: 国家数据字典
|
||||
"""
|
||||
# 层岩巨渊 和 地下矿区 算一个
|
||||
region = BuildImage(790, 267 * (len(world_data_dict) - 1), color="#F9F6F2")
|
||||
region = BuildImage(790, 267 * ((len(world_data_dict) - 1) if world_data_dict.get("层岩巨渊·地下矿区") else len(world_data_dict)), color="#F9F6F2")
|
||||
height = 0
|
||||
for country in ["蒙德", "龙脊雪山", "璃月", "层岩巨渊", "稻妻", "渊下宫"]:
|
||||
for country in ["蒙德", "龙脊雪山", "璃月", "层岩巨渊", "稻妻", "渊下宫", "须弥"]:
|
||||
if not world_data_dict.get(country):
|
||||
continue
|
||||
x = BuildImage(790, 250, color="#3A4467")
|
||||
@@ -311,15 +317,15 @@ def get_country_data_image(world_data_dict: Dict) -> BuildImage:
|
||||
)
|
||||
content_bk.paste(logo, (50, 0), True, "by_height")
|
||||
if country in ["蒙德", "璃月"]:
|
||||
content_bk.text((300, 40), "探索", fill=(239, 211, 114))
|
||||
content_bk.text((300, 40), "蒙德探索" if country == "蒙德" else "璃月探索", fill=(239, 211, 114))
|
||||
content_bk.text(
|
||||
(450, 40),
|
||||
(500, 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((300, 120), "蒙德声望" if country == "蒙德" else "璃月声望", fill=(239, 211, 114))
|
||||
content_bk.text(
|
||||
(450, 120),
|
||||
(500, 120),
|
||||
f"Lv.{world_data_dict[country]['level']}",
|
||||
fill=(255, 255, 255),
|
||||
)
|
||||
@@ -344,45 +350,65 @@ def get_country_data_image(world_data_dict: Dict) -> BuildImage:
|
||||
fill=(255, 255, 255),
|
||||
)
|
||||
elif country in ["龙脊雪山"]:
|
||||
content_bk.text((300, 40), "探索", fill=(239, 211, 114))
|
||||
content_bk.text((300, 40), "雪山探索", fill=(239, 211, 114))
|
||||
content_bk.text(
|
||||
(450, 40),
|
||||
(500, 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((300, 120), "忍冬之树", fill=(239, 211, 114))
|
||||
content_bk.text(
|
||||
(450, 120),
|
||||
(500, 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((300, 20), "稻妻探索", fill=(239, 211, 114))
|
||||
content_bk.text(
|
||||
(450, 20),
|
||||
(500, 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((300, 85), "稻妻声望", fill=(239, 211, 114))
|
||||
content_bk.text(
|
||||
(450, 85),
|
||||
(500, 85),
|
||||
f"Lv.{world_data_dict[country]['level']}",
|
||||
fill=(255, 255, 255),
|
||||
)
|
||||
content_bk.text((300, 150), "神樱", fill=(239, 211, 114))
|
||||
content_bk.text((300, 150), "神樱眷顾", fill=(239, 211, 114))
|
||||
content_bk.text(
|
||||
(450, 150),
|
||||
(500, 150),
|
||||
f"Lv.{world_data_dict[country]['offerings'][0]['level']}",
|
||||
fill=(255, 255, 255),
|
||||
)
|
||||
elif country in ["渊下宫"]:
|
||||
content_bk.text((300, 0), "探索", fill=(239, 211, 114), center_type="by_height")
|
||||
content_bk.text((300, 0), "渊下宫探索", fill=(239, 211, 114), center_type="by_height")
|
||||
content_bk.text(
|
||||
(450, 20),
|
||||
(530, 20),
|
||||
f"{world_data_dict[country]['exploration_percentage'] / 10}%",
|
||||
fill=(255, 255, 255),
|
||||
center_type="by_height",
|
||||
)
|
||||
elif country in ["须弥"]:
|
||||
content_bk.text((300, 20), "须弥探索", fill=(239, 211, 114))
|
||||
content_bk.text(
|
||||
(500, 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(
|
||||
(500, 85),
|
||||
f"Lv.{world_data_dict[country]['level']}",
|
||||
fill=(255, 255, 255),
|
||||
)
|
||||
content_bk.text((300, 150), "梦之树", fill=(239, 211, 114))
|
||||
content_bk.text(
|
||||
(500, 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)
|
||||
|
||||
@@ -19,7 +19,9 @@ import pytz
|
||||
driver: Driver = nonebot.get_driver()
|
||||
|
||||
|
||||
get_memo = require("query_memo").get_memo
|
||||
require("query_memo")
|
||||
|
||||
from ..query_memo import get_memo
|
||||
|
||||
|
||||
global_map = {}
|
||||
|
||||
+7
-5
@@ -1,3 +1,7 @@
|
||||
from io import BytesIO
|
||||
|
||||
import imagehash
|
||||
from PIL import Image
|
||||
from nonebot import on_message, on_command
|
||||
from nonebot.adapters.onebot.v11 import Bot, GroupMessageEvent, Message, ActionFailed
|
||||
from nonebot.adapters.onebot.v11.permission import GROUP
|
||||
@@ -70,10 +74,8 @@ def save_data():
|
||||
json.dump(mute_data, f, indent=4)
|
||||
|
||||
|
||||
async def download_img_and_hash(url, group_id) -> str:
|
||||
if await AsyncHttpx.download_file(url, TEMP_PATH / f"mute_{group_id}_img.jpg"):
|
||||
return str(get_img_hash(TEMP_PATH / f"mute_{group_id}_img.jpg"))
|
||||
return ""
|
||||
async def download_img_and_hash(url) -> str:
|
||||
return str(imagehash.average_hash(Image.open(BytesIO((await AsyncHttpx.get(url)).content))))
|
||||
|
||||
|
||||
mute_dict = {}
|
||||
@@ -87,7 +89,7 @@ async def _(bot: Bot, event: GroupMessageEvent):
|
||||
img_list = get_message_img(event.json())
|
||||
img_hash = ""
|
||||
for img in img_list:
|
||||
img_hash += await download_img_and_hash(img, event.group_id)
|
||||
img_hash += await download_img_and_hash(img)
|
||||
msg += img_hash
|
||||
if not mute_data.get(group_id):
|
||||
mute_data[group_id] = {
|
||||
|
||||
@@ -101,7 +101,7 @@ cases_matcher_group = MatcherGroup(priority=5, permission=GROUP, block=True)
|
||||
|
||||
k_open_case = cases_matcher_group.on_command("开箱")
|
||||
|
||||
reload_count = cases_matcher_group.on_command("重置开箱")
|
||||
reload_count = cases_matcher_group.on_command("重置开箱",permission=SUPERUSER)
|
||||
|
||||
|
||||
@reload_count.handle()
|
||||
|
||||
@@ -31,7 +31,7 @@ async def open_case(user_qq: int, group: int, case_name: str = "狂牙大行动"
|
||||
async with db.transaction():
|
||||
user = await OpenCasesUser.ensure(user_qq, group, for_update=True)
|
||||
# 一天次数上限
|
||||
if user.today_open_total > int(
|
||||
if user.today_open_total >= int(
|
||||
Config.get_config("open_cases", "INITIAL_OPEN_CASE_COUNT")
|
||||
+ int(impression)
|
||||
/ Config.get_config("open_cases", "EACH_IMPRESSION_ADD_COUNT")
|
||||
@@ -155,7 +155,7 @@ async def open_shilian_case(user_qq: int, group: int, case_name: str, num: int =
|
||||
Config.get_config("open_cases", "INITIAL_OPEN_CASE_COUNT")
|
||||
+ int(impression) / Config.get_config("open_cases", "EACH_IMPRESSION_ADD_COUNT")
|
||||
)
|
||||
if user.today_open_total == max_count:
|
||||
if user.today_open_total >= max_count:
|
||||
return _handle_is_MAX_COUNT()
|
||||
if max_count - user.today_open_total < num:
|
||||
return (
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
from nonebot import on_message
|
||||
from services.log import logger
|
||||
from nonebot.adapters.onebot.v11 import GroupMessageEvent, ActionFailed
|
||||
|
||||
from utils.manager import group_manager
|
||||
from utils.utils import get_message_json, get_local_proxy, is_number, get_message_text
|
||||
from nonebot.adapters.onebot.v11.permission import GROUP
|
||||
from utils.message_builder import image
|
||||
@@ -34,7 +36,10 @@ Config.add_plugin_config(
|
||||
)
|
||||
|
||||
|
||||
parse_bilibili_json = on_message(priority=1, permission=GROUP, block=False)
|
||||
async def plugin_on_checker(event: GroupMessageEvent) -> bool:
|
||||
return group_manager.get_plugin_status("parse_bilibili_json", event.group_id)
|
||||
|
||||
parse_bilibili_json = on_message(priority=1, permission=GROUP, block=False, rule=plugin_on_checker)
|
||||
|
||||
_tmp = {}
|
||||
|
||||
@@ -110,10 +115,10 @@ async def _(event: GroupMessageEvent):
|
||||
vd_info = await video.get_video_base_info(msg)
|
||||
elif "av" in msg:
|
||||
index = msg.find("av")
|
||||
if len(msg[index + 2 :]) >= 9:
|
||||
if len(msg[index + 2 :]) >= 1:
|
||||
msg = msg[index + 2 : index + 11]
|
||||
if is_number(msg):
|
||||
url = f"https://www.bilibili.com/video/{msg}"
|
||||
url = f"https://www.bilibili.com/video/av{msg}"
|
||||
vd_info = await video.get_video_base_info('av' + msg)
|
||||
elif "https://b23.tv" in msg:
|
||||
url = "https://" + msg[msg.find("b23.tv"): msg.find("b23.tv") + 14]
|
||||
@@ -124,7 +129,7 @@ async def _(event: GroupMessageEvent):
|
||||
url,
|
||||
timeout=7,
|
||||
) as response:
|
||||
url = str(response.url).split("?")[0]
|
||||
url = (str(response.url).split("?")[0]).strip("/")
|
||||
bvid = url.split("/")[-1]
|
||||
vd_info = await video.get_video_base_info(bvid)
|
||||
if vd_info:
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
from configs.config import Config
|
||||
from utils.manager import GDict
|
||||
from utils.utils import GDict
|
||||
import nonebot
|
||||
|
||||
|
||||
|
||||
@@ -24,7 +24,7 @@ __plugin_settings__ = {
|
||||
}
|
||||
|
||||
|
||||
quotations = on_regex("^[语录|二次元]$", priority=5, block=True)
|
||||
quotations = on_regex("^(语录|二次元)$", priority=5, block=True)
|
||||
|
||||
url = "https://international.v1.hitokoto.cn/?c=a"
|
||||
|
||||
|
||||
@@ -12,6 +12,7 @@ from .._model import Setu
|
||||
import asyncio
|
||||
import os
|
||||
import random
|
||||
import re
|
||||
|
||||
try:
|
||||
import ujson as json
|
||||
@@ -22,6 +23,7 @@ except ModuleNotFoundError:
|
||||
url = "https://api.lolicon.app/setu/v2"
|
||||
path = "_setu"
|
||||
r18_path = "_r18"
|
||||
host_pattern = re.compile(r"https?://([^/]+)")
|
||||
|
||||
|
||||
# 获取url
|
||||
@@ -89,7 +91,9 @@ async def search_online_setu(
|
||||
"""
|
||||
ws_url = Config.get_config("pixiv", "PIXIV_NGINX_URL")
|
||||
if ws_url:
|
||||
url_ = url_.replace("i.pximg.net", ws_url).replace("i.pixiv.cat", ws_url)
|
||||
host_match = re.match(host_pattern, url_)
|
||||
host = host_match.group(1)
|
||||
url_ = url_.replace(host, ws_url)
|
||||
index = random.randint(1, 100000) if id_ is None else id_
|
||||
path_ = IMAGE_PATH / path_ if path_ else TEMP_PATH
|
||||
file_name = f"{index}_temp_setu.jpg" if path_ == TEMP_PATH else f"{index}.jpg"
|
||||
|
||||
@@ -18,6 +18,7 @@ from configs.path_config import DATA_PATH
|
||||
from services.log import logger
|
||||
from .utils import clear_sign_data_pic
|
||||
from utils.utils import is_number
|
||||
from .goods_register import driver
|
||||
|
||||
try:
|
||||
import ujson as json
|
||||
|
||||
@@ -0,0 +1,60 @@
|
||||
|
||||
from models.sign_group_user import SignGroupUser
|
||||
from configs.config import Config
|
||||
from nonebot import Driver
|
||||
from utils.decorator.shop import shop_register, NotMeetUseConditionsException
|
||||
import nonebot
|
||||
|
||||
driver: Driver = nonebot.get_driver()
|
||||
|
||||
|
||||
@driver.on_startup
|
||||
async def _():
|
||||
"""
|
||||
导入内置的三个商品
|
||||
"""
|
||||
|
||||
@shop_register(
|
||||
name=("好感度双倍加持卡Ⅰ", "好感度双倍加持卡Ⅱ", "好感度双倍加持卡Ⅲ"),
|
||||
price=(30, 150, 250),
|
||||
des=(
|
||||
"下次签到双倍好感度概率 + 10%(谁才是真命天子?)(同类商品将覆盖)",
|
||||
"下次签到双倍好感度概率 + 20%(平平庸庸)(同类商品将覆盖)",
|
||||
"下次签到双倍好感度概率 + 30%(金币才是真命天子!)(同类商品将覆盖)",
|
||||
),
|
||||
load_status=Config.get_config("shop", "IMPORT_DEFAULT_SHOP_GOODS"),
|
||||
icon=("favorability_card_1.png", "favorability_card_2.png", "favorability_card_3.png"),
|
||||
** {"好感度双倍加持卡Ⅰ_prob": 0.1, "好感度双倍加持卡Ⅱ_prob": 0.2, "好感度双倍加持卡Ⅲ_prob": 0.3},
|
||||
)
|
||||
async def sign_card(user_id: int, group_id: int, prob: float):
|
||||
user = await SignGroupUser.ensure(user_id, group_id)
|
||||
await user.update(add_probability=prob).apply()
|
||||
|
||||
@shop_register(
|
||||
name="测试道具A",
|
||||
price=99,
|
||||
des="随便侧而出",
|
||||
load_status=False,
|
||||
icon="sword.png",
|
||||
)
|
||||
async def _(user_id: int, group_id: int):
|
||||
print(user_id, group_id, '使用测试道具')
|
||||
|
||||
@shop_register.before_handle(name="测试道具A", load_status=False)
|
||||
async def _(user_id: int, group_id: int):
|
||||
print(user_id, group_id, '第一个使用前函数(before handle)')
|
||||
|
||||
@shop_register.before_handle(name="测试道具A", load_status=False)
|
||||
async def _(user_id: int, group_id: int):
|
||||
print(user_id, group_id, '第二个使用前函数(before handle)222')
|
||||
raise NotMeetUseConditionsException("太笨了!") # 抛出异常,阻断使用,并返回信息
|
||||
|
||||
@shop_register.after_handle(name="测试道具A", load_status=False)
|
||||
async def _(user_id: int, group_id: int):
|
||||
print(user_id, group_id, '第一个使用后函数(after handle)')
|
||||
|
||||
|
||||
@driver.on_bot_connect
|
||||
async def _():
|
||||
await shop_register.load_register()
|
||||
|
||||
@@ -81,19 +81,15 @@ async def _handle_check_in(
|
||||
await BagUser.add_gold(user_qq, group, gold)
|
||||
await BagUser.add_property(user_qq, group, gift)
|
||||
gift += ' + 1'
|
||||
|
||||
logger.info(
|
||||
f"(USER {user.user_qq}, GROUP {user.group_id})"
|
||||
f" CHECKED IN successfully. score: {user.impression:.2f} "
|
||||
f"(+{impression_added:.2f}).获取金币:{gold + gift if gift == 'gold' else gold}"
|
||||
)
|
||||
if critx2 + add_probability > 0.97 or critx2 < specify_probability:
|
||||
logger.info(
|
||||
f"(USER {user.user_qq}, GROUP {user.group_id})"
|
||||
f" CHECKED IN successfully. score: {user.impression:.2f} "
|
||||
f"(+{impression_added * 2:.2f}).获取金币:{gold + gift if gift == 'gold' else gold}"
|
||||
)
|
||||
return await get_card(user, nickname, impression_added, gold, gift, True)
|
||||
else:
|
||||
logger.info(
|
||||
f"(USER {user.user_qq}, GROUP {user.group_id})"
|
||||
f" CHECKED IN successfully. score: {user.impression:.2f} "
|
||||
f"(+{impression_added:.2f}).获取金币:{gold + gift if gift == 'gold' else gold}"
|
||||
)
|
||||
return await get_card(user, nickname, impression_added, gold, gift)
|
||||
|
||||
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
import random
|
||||
import time
|
||||
from pathlib import Path
|
||||
|
||||
from nonebot.adapters.onebot.v11 import Message, MessageSegment
|
||||
@@ -187,20 +189,45 @@ async def _():
|
||||
if await WordBank.get_group_all_problem(0):
|
||||
return
|
||||
logger.info('开始迁移词条 纯文本 数据')
|
||||
word_list = await OldWordBank.get_all()
|
||||
for word in word_list:
|
||||
problem: str = word.problem
|
||||
user_id = word.user_qq
|
||||
group_id = word.group_id
|
||||
format_ = word.format
|
||||
answer = word.answer
|
||||
# 仅对纯文本做处理
|
||||
if '[CQ' not in problem and '[CQ' not in answer and '[_to_me' not in problem:
|
||||
if not format_:
|
||||
await WordBank.add_problem_answer(user_id, group_id, 1, 0, problem, answer)
|
||||
await WordBank.add_problem_answer(0, 0, 999, 0, '_[OK', '_[OK')
|
||||
logger.info('词条 纯文本 数据迁移完成')
|
||||
(Path() / 'plugins' / 'word_bank' / '_old_model.py').unlink()
|
||||
try:
|
||||
word_list = await OldWordBank.get_all()
|
||||
new_answer_path = Path() / 'data' / 'word_bank' / 'answer'
|
||||
new_problem_path = Path() / 'data' / 'word_bank' / 'problem'
|
||||
new_answer_path.mkdir(exist_ok=True, parents=True)
|
||||
for word in word_list:
|
||||
problem: str = word.problem
|
||||
user_id = word.user_qq
|
||||
group_id = word.group_id
|
||||
format_ = word.format
|
||||
answer = word.answer
|
||||
# 仅对纯文本做处理
|
||||
if '[CQ' not in problem and '[CQ' not in answer and '[_to_me' not in problem:
|
||||
if not format_:
|
||||
await WordBank.add_problem_answer(user_id, group_id, 1, 0, problem, answer)
|
||||
else:
|
||||
placeholder = []
|
||||
for m in format_.split('<format>'):
|
||||
x = m.split('<_s>')
|
||||
if x[0]:
|
||||
idx, file_name = x[0], x[1]
|
||||
if 'jpg' in file_name:
|
||||
answer = answer.replace(f'[__placeholder_{idx}]', f'[image:placeholder_{idx}]')
|
||||
file = Path() / 'data' / 'word_bank' / f'{group_id}' / file_name
|
||||
rand = int(time.time()) + random.randint(1, 100000)
|
||||
if file.exists():
|
||||
new_file = new_answer_path / f'{group_id}' / f'{user_id}_{rand}.jpg'
|
||||
new_file.parent.mkdir(exist_ok=True, parents=True)
|
||||
with open(file, 'rb') as rb:
|
||||
with open(new_file, 'wb') as wb:
|
||||
wb.write(rb.read())
|
||||
# file.rename(new_file)
|
||||
placeholder.append(f'answer/{group_id}/{user_id}_{rand}.jpg')
|
||||
await WordBank._move(user_id, group_id, problem, answer, ",".join(placeholder))
|
||||
await WordBank.add_problem_answer(0, 0, 999, 0, '_[OK', '_[OK')
|
||||
logger.info('词条 纯文本 数据迁移完成')
|
||||
(Path() / 'plugins' / 'word_bank' / '_old_model.py').unlink()
|
||||
except Exception as e:
|
||||
logger.warning(f'迁移词条发生错误,如果为首次安装请无视 {type(e)}:{e}')
|
||||
|
||||
|
||||
|
||||
|
||||
+84
-22
@@ -48,6 +48,7 @@ class WordBank(db.Model):
|
||||
user_id: Optional[int],
|
||||
group_id: Optional[int],
|
||||
problem: str,
|
||||
answer: Optional[str],
|
||||
word_scope: Optional[int] = None,
|
||||
word_type: Optional[int] = None,
|
||||
) -> bool:
|
||||
@@ -58,6 +59,7 @@ class WordBank(db.Model):
|
||||
:param user_id: 用户id
|
||||
:param group_id: 群号
|
||||
:param problem: 问题
|
||||
:param answer: 回答
|
||||
:param word_scope: 词条范围
|
||||
:param word_type: 词条类型
|
||||
"""
|
||||
@@ -66,6 +68,8 @@ class WordBank(db.Model):
|
||||
query = query.where(cls.user_qq == user_id)
|
||||
if group_id:
|
||||
query = query.where(cls.group_id == group_id)
|
||||
if answer:
|
||||
query = query.where(cls.answer == answer)
|
||||
if word_type:
|
||||
query = query.where(cls.word_type == word_type)
|
||||
if word_scope:
|
||||
@@ -105,19 +109,20 @@ class WordBank(db.Model):
|
||||
problem = str(get_img_hash(_file))
|
||||
image_path = f"problem/{group_id}/{user_id}_{int(time.time())}.jpg"
|
||||
answer, _list = await cls._answer2format(answer, user_id, group_id)
|
||||
await cls.create(
|
||||
user_qq=user_id,
|
||||
group_id=group_id,
|
||||
word_scope=word_scope,
|
||||
word_type=word_type,
|
||||
status=True,
|
||||
problem=problem,
|
||||
answer=answer,
|
||||
image_path=image_path,
|
||||
placeholder=",".join(_list),
|
||||
create_time=datetime.now().replace(microsecond=0),
|
||||
update_time=datetime.now().replace(microsecond=0),
|
||||
)
|
||||
if not await cls.exists(user_id, group_id, problem, answer, word_scope, word_type):
|
||||
await cls.create(
|
||||
user_qq=user_id,
|
||||
group_id=group_id,
|
||||
word_scope=word_scope,
|
||||
word_type=word_type,
|
||||
status=True,
|
||||
problem=problem,
|
||||
answer=answer,
|
||||
image_path=image_path,
|
||||
placeholder=",".join(_list),
|
||||
create_time=datetime.now().replace(microsecond=0),
|
||||
update_time=datetime.now().replace(microsecond=0),
|
||||
)
|
||||
|
||||
@classmethod
|
||||
async def _answer2format(
|
||||
@@ -143,7 +148,7 @@ class WordBank(db.Model):
|
||||
text += seg.data["text"]
|
||||
elif seg.type == "face":
|
||||
text += f"[face:placeholder_{index}]"
|
||||
_list.append(seg.data.id)
|
||||
_list.append(seg.data['id'])
|
||||
elif seg.type == "at":
|
||||
text += f"[at:placeholder_{index}]"
|
||||
_list.append(seg.data["qq"])
|
||||
@@ -234,16 +239,36 @@ class WordBank(db.Model):
|
||||
query = query.where(cls.word_scope == word_type)
|
||||
sql_text += f" and word_scope = {word_scope}"
|
||||
# 完全匹配
|
||||
if await query.where(cls.problem == problem).gino.first():
|
||||
return query.where(cls.problem == problem)
|
||||
if await query.where(
|
||||
((cls.word_type == 0) | (cls.word_type == 3)) & (cls.problem == problem)
|
||||
).gino.first():
|
||||
return query.where(
|
||||
((cls.word_type == 0) | (cls.word_type == 3)) & (cls.problem == problem)
|
||||
)
|
||||
# 模糊匹配
|
||||
if await query.where(cls.problem.contains(problem)).gino.first():
|
||||
return query.where(cls.problem.contains(problem))
|
||||
if await db.first(
|
||||
db.text(
|
||||
sql_text
|
||||
+ f" and word_type = 1 and :problem like '%' || problem || '%';"
|
||||
),
|
||||
problem=problem,
|
||||
):
|
||||
return (
|
||||
sql_text
|
||||
+ f" and word_type = 1 and :problem like '%' || problem || '%';"
|
||||
)
|
||||
# 正则匹配
|
||||
if await db.first(
|
||||
db.text(sql_text + f" and word_type = 2 and word_scope != 999 and '{problem}' ~ problem;")
|
||||
db.text(
|
||||
sql_text
|
||||
+ f" and word_type = 2 and word_scope != 999 and :problem ~ problem;"
|
||||
),
|
||||
problem=problem,
|
||||
):
|
||||
return sql_text + f" and word_type = 2 and word_scope != 999 and '{problem}' ~ problem;"
|
||||
return (
|
||||
sql_text
|
||||
+ f" and word_type = 2 and word_scope != 999 and :problem ~ problem;"
|
||||
)
|
||||
# if await db.first(
|
||||
# db.text(sql_text + f" and word_type = 1 and word_scope != 999 and '{problem}' ~ problem;")
|
||||
# ):
|
||||
@@ -270,10 +295,10 @@ class WordBank(db.Model):
|
||||
query = await cls.check(event, problem, word_scope, word_type)
|
||||
if query is not None:
|
||||
if isinstance(query, str):
|
||||
answer_list = await db.all(db.text(query))
|
||||
answer_list = await db.all(db.text(query), problem=problem)
|
||||
answer = random.choice(answer_list)
|
||||
return (
|
||||
await cls._format2answer(problem, answer[7], answer[1], answer[2])
|
||||
await cls._format2answer(answer[6], answer[7], answer[1], answer[2])
|
||||
if answer.placeholder
|
||||
else answer.answer
|
||||
)
|
||||
@@ -459,3 +484,40 @@ class WordBank(db.Model):
|
||||
problem_list.append(problem)
|
||||
_tmp.append(q.problem)
|
||||
return problem_list
|
||||
|
||||
@classmethod
|
||||
async def _move(
|
||||
cls,
|
||||
user_id: int,
|
||||
group_id: Optional[int],
|
||||
problem: Union[str, Message],
|
||||
answer: Union[str, Message],
|
||||
placeholder: str,
|
||||
):
|
||||
"""
|
||||
说明:
|
||||
旧词条图片移动方法
|
||||
参数:
|
||||
:param user_id: 用户id
|
||||
:param group_id: 群号
|
||||
:param problem: 问题
|
||||
:param answer: 回答
|
||||
:param placeholder: 占位符
|
||||
"""
|
||||
word_scope = 0
|
||||
word_type = 0
|
||||
# 对图片做额外处理
|
||||
if not await cls.exists(user_id, group_id, problem, answer, word_scope, word_type):
|
||||
await cls.create(
|
||||
user_qq=user_id,
|
||||
group_id=group_id,
|
||||
word_scope=word_scope,
|
||||
word_type=word_type,
|
||||
status=True,
|
||||
problem=problem,
|
||||
answer=answer,
|
||||
image_path=None,
|
||||
placeholder=placeholder,
|
||||
create_time=datetime.now().replace(microsecond=0),
|
||||
update_time=datetime.now().replace(microsecond=0),
|
||||
)
|
||||
|
||||
+16
-11
@@ -1,31 +1,36 @@
|
||||
import random
|
||||
import imagehash
|
||||
from PIL import Image
|
||||
from io import BytesIO
|
||||
from httpx import TimeoutException
|
||||
|
||||
from nonebot.typing import T_State
|
||||
from nonebot.adapters.onebot.v11 import MessageEvent
|
||||
|
||||
from configs.path_config import TEMP_PATH
|
||||
from utils.image_utils import get_img_hash
|
||||
from utils.utils import get_message_text, get_message_img, get_message_at
|
||||
from ._model import WordBank
|
||||
from utils.http_utils import AsyncHttpx
|
||||
|
||||
|
||||
async def check(event: MessageEvent) -> bool:
|
||||
async def check(event: MessageEvent, state: T_State) -> bool:
|
||||
text = get_message_text(event.message)
|
||||
img = get_message_img(event.message)
|
||||
at = get_message_at(event.message)
|
||||
rand = random.randint(1, 100)
|
||||
problem = text
|
||||
if not text and len(img) == 1:
|
||||
if await AsyncHttpx.download_file(img[0], TEMP_PATH / f"{event.user_id}_{rand}_word_bank_check.jpg"):
|
||||
problem = str(get_img_hash(TEMP_PATH / f"{event.user_id}_{rand}_word_bank_check.jpg"))
|
||||
try:
|
||||
r = await AsyncHttpx.get(img[0])
|
||||
problem = str(imagehash.average_hash(Image.open(BytesIO(r.content))))
|
||||
except TimeoutException:
|
||||
pass
|
||||
if at:
|
||||
temp = ''
|
||||
for seg in event.message:
|
||||
if seg.type == 'at':
|
||||
temp += f"[at:{seg.data['qq']}]"
|
||||
else:
|
||||
temp += seg
|
||||
elif seg.type == 'text':
|
||||
temp += seg.data["text"]
|
||||
problem = temp
|
||||
if problem:
|
||||
return await WordBank.check(event, problem) is not None
|
||||
if problem and (await WordBank.check(event, problem) is not None):
|
||||
state["problem"] = problem
|
||||
return True
|
||||
return False
|
||||
|
||||
@@ -1,14 +1,10 @@
|
||||
import random
|
||||
|
||||
from services import logger
|
||||
from utils.image_utils import get_img_hash
|
||||
from ._rule import check
|
||||
from ._model import WordBank
|
||||
from configs.path_config import DATA_PATH, TEMP_PATH
|
||||
from configs.path_config import DATA_PATH
|
||||
from nonebot.adapters.onebot.v11 import GroupMessageEvent, MessageEvent
|
||||
from utils.utils import get_message_img, get_message_text, get_message_at
|
||||
from nonebot import on_message
|
||||
from utils.http_utils import AsyncHttpx
|
||||
from nonebot.typing import T_State
|
||||
|
||||
__zx_plugin_name__ = "词库问答回复操作 [Hidden]"
|
||||
|
||||
@@ -19,26 +15,8 @@ message_handle = on_message(priority=6, block=True, rule=check)
|
||||
|
||||
|
||||
@message_handle.handle()
|
||||
async def _(event: MessageEvent):
|
||||
text = get_message_text(event.message)
|
||||
img = get_message_img(event.message)
|
||||
at = get_message_at(event.message)
|
||||
problem = None
|
||||
if not text and img and len(img) == 1:
|
||||
rand = random.randint(1, 10000)
|
||||
if await AsyncHttpx.download_file(img[0], TEMP_PATH / f"{event.user_id}_{rand}_word_bank.jpg"):
|
||||
problem = str(get_img_hash(TEMP_PATH / f"{event.user_id}_{rand}_word_bank.jpg"))
|
||||
elif at:
|
||||
temp = ''
|
||||
for seg in event.message:
|
||||
if seg.type == 'at':
|
||||
temp += f"[at:{seg.data['qq']}]"
|
||||
else:
|
||||
temp += seg
|
||||
problem = temp
|
||||
elif text:
|
||||
problem = text
|
||||
if problem:
|
||||
async def _(event: MessageEvent, state: T_State):
|
||||
if problem := state.get("problem"):
|
||||
if msg := await WordBank.get_answer(event, problem):
|
||||
await message_handle.send(msg)
|
||||
logger.info(
|
||||
@@ -46,6 +24,3 @@ async def _(event: MessageEvent):
|
||||
f"{event.group_id if isinstance(event, GroupMessageEvent) else 'private'})"
|
||||
f" 触发词条 {problem}"
|
||||
)
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import re
|
||||
from typing import Tuple, Any, Optional
|
||||
|
||||
from nonebot.internal.params import Arg, ArgStr
|
||||
@@ -5,11 +6,12 @@ from nonebot.typing import T_State
|
||||
|
||||
from utils.utils import get_message_at, is_number, get_message_img
|
||||
from nonebot.params import CommandArg, RegexGroup, Command
|
||||
from nonebot.exception import FinishedException
|
||||
from services.log import logger
|
||||
from configs.path_config import DATA_PATH
|
||||
from utils.message_builder import custom_forward_msg
|
||||
from ._model import WordBank
|
||||
from nonebot.adapters.onebot.v11 import Bot, GroupMessageEvent, Message, MessageEvent, PrivateMessageEvent
|
||||
from nonebot.adapters.onebot.v11 import Bot, GroupMessageEvent, Message, MessageEvent, PrivateMessageEvent, unescape
|
||||
from nonebot import on_command, on_regex
|
||||
from configs.config import Config
|
||||
from ._data_source import delete_word, update_word, show_word
|
||||
@@ -23,7 +25,7 @@ usage:
|
||||
更推荐使用id方式删除
|
||||
问题回答支持的CQ:at, face, image
|
||||
查看词条命令:群聊时为 群词条+全局词条,私聊时为 私聊词条+全局词条
|
||||
添加词条正则:添加词条(模糊|正则|图片)?问\s*?(\S*)\s*?答\s?(\S*)
|
||||
添加词条正则:添加词条(模糊|正则|图片)?问\s*?(\S*\s?\S*)\s*?答\s?(\S*)
|
||||
指令:
|
||||
添加词条 ?[模糊|正则|图片]问...答...:添加问答词条,可重复添加相同问题的不同回答
|
||||
删除词条 [问题/下标] ?[下标]:删除指定词条指定或全部回答
|
||||
@@ -47,7 +49,7 @@ __plugin_superuser_usage__ = r"""
|
||||
usage:
|
||||
在私聊中超级用户额外设置
|
||||
指令:
|
||||
(全局|私聊)?添加词条\s*?(模糊|正则|图片)?问\s*?(\S*)\s*?答\s?(\S*):添加问答词条,可重复添加相同问题的不同回答
|
||||
(全局|私聊)?添加词条\s*?(模糊|正则|图片)?问\s*?(\S*\s?\S*)\s*?答\s?(\S*):添加问答词条,可重复添加相同问题的不同回答
|
||||
全局添加词条
|
||||
私聊添加词条
|
||||
(私聊情况下)删除词条: 删除私聊词条
|
||||
@@ -74,7 +76,7 @@ data_dir = DATA_PATH / "word_bank"
|
||||
data_dir.mkdir(parents=True, exist_ok=True)
|
||||
|
||||
add_word = on_regex(
|
||||
r"^(全局|私聊)?添加词条\s*?(模糊|正则|图片)?问\s*?(\S*)\s*?答\s?(\S*)", priority=5, block=True
|
||||
r"^(全局|私聊)?添加词条\s*?(模糊|正则|图片)?问\s*?(\S*\s?\S*)\s*?答\s?(\S*)", priority=5, block=True
|
||||
)
|
||||
|
||||
delete_word_matcher = on_command("删除词条", aliases={'删除全局词条'}, priority=5, block=True)
|
||||
@@ -91,7 +93,7 @@ async def _(
|
||||
state: T_State,
|
||||
reg_group: Tuple[Any, ...] = RegexGroup(),
|
||||
):
|
||||
if str(event.user_id) not in bot.config.superusers:
|
||||
if isinstance(event, PrivateMessageEvent) and str(event.user_id) not in bot.config.superusers:
|
||||
await add_word.finish('权限不足捏')
|
||||
word_scope, word_type, problem, answer = reg_group
|
||||
if (
|
||||
@@ -113,18 +115,22 @@ async def _(
|
||||
if seg.type == 'text' and '答' in str(seg):
|
||||
_problem = event.message[:index]
|
||||
answer = event.message[index:]
|
||||
answer[0] = str(answer[0])[str(answer).index('答')+1:]
|
||||
_problem[0] = str(_problem[0])[str(_problem).index('问')+1:]
|
||||
answer[0] = str(answer[0])[str(answer[0]).index('答')+1:]
|
||||
_problem[0] = str(_problem[0])[str(_problem[0]).index('问')+1:]
|
||||
if _problem[-1].type != 'at' or seg.data['text'][:seg.data['text'].index('答')].lstrip():
|
||||
_problem.append(seg.data['text'][:seg.data['text'].index('答')])
|
||||
temp = ''
|
||||
for g in _problem:
|
||||
if isinstance(g, str) or g.type == 'text':
|
||||
if isinstance(g, str):
|
||||
temp += g
|
||||
elif g.type == 'text':
|
||||
temp += g.data['text']
|
||||
elif g.type == 'at':
|
||||
temp += f"[at:{g.data['qq']}]"
|
||||
problem = temp
|
||||
break
|
||||
index = len((word_scope or "") + "添加词条" + (word_type or "") + problem) + 1
|
||||
event.message[0] = event.message[0].data["text"][index + 1 :].strip()
|
||||
problem = unescape(problem)
|
||||
event.message[0] = event.message[0].data["text"].split('答', maxsplit=1)[-1].strip()
|
||||
state["word_scope"] = word_scope
|
||||
state["word_type"] = word_type
|
||||
state["problem"] = problem
|
||||
@@ -141,6 +147,11 @@ async def _(
|
||||
problem_image: Message = Arg("problem_image"),
|
||||
):
|
||||
try:
|
||||
if word_type == "正则":
|
||||
try:
|
||||
re.compile(problem)
|
||||
except re.error:
|
||||
await add_word.finish(f"添加词条失败,正则表达式 {problem} 非法!")
|
||||
await WordBank.add_problem_answer(
|
||||
event.user_id,
|
||||
event.group_id if isinstance(event, GroupMessageEvent) and (not word_scope or word_scope == '1') else 0,
|
||||
@@ -150,6 +161,8 @@ async def _(
|
||||
answer,
|
||||
)
|
||||
except Exception as e:
|
||||
if isinstance(e, FinishedException):
|
||||
await add_word.finish()
|
||||
logger.error(
|
||||
f"(USER {event.user_id}, GROUP "
|
||||
f"{event.group_id if isinstance(event, GroupMessageEvent) else 'private'})"
|
||||
@@ -233,7 +246,7 @@ async def _(bot: Bot, event: GroupMessageEvent, arg: Message = CommandArg()):
|
||||
not is_number(id_)
|
||||
or int(id_) < 0
|
||||
or int(id_)
|
||||
> len(await WordBank.get_group_all_problem(event.group_id))
|
||||
>= len(await WordBank.get_group_all_problem(event.group_id))
|
||||
):
|
||||
await show_word_matcher.finish("id必须为数字且在范围内")
|
||||
id_ = int(id_)
|
||||
@@ -243,7 +256,7 @@ async def _(bot: Bot, event: GroupMessageEvent, arg: Message = CommandArg()):
|
||||
not is_number(gid)
|
||||
or int(gid) < 0
|
||||
or int(gid)
|
||||
> len(await WordBank.get_problem_by_scope(0))
|
||||
>= len(await WordBank.get_problem_by_scope(0))
|
||||
):
|
||||
await show_word_matcher.finish("gid必须为数字且在范围内")
|
||||
gid = int(gid)
|
||||
|
||||
Generated
+1541
-700
File diff suppressed because it is too large
Load Diff
+4
-4
@@ -12,18 +12,16 @@ url = "https://mirrors.aliyun.com/pypi/simple/"
|
||||
|
||||
[tool.poetry.dependencies]
|
||||
python = "^3.8"
|
||||
nonebot2 = "^2.0.0-beta.4"
|
||||
nonebot2 = "^2.0.0rc1"
|
||||
nonebot-adapter-onebot = "^2.0.0-beta.1"
|
||||
aiofiles = "^0.8.0"
|
||||
aiohttp = "3.7.4.post0"
|
||||
beautifulsoup4 = "4.9.3"
|
||||
feedparser = "^6.0.8"
|
||||
gino = "^1.0.1"
|
||||
httpx = "^0.23.0"
|
||||
ImageHash = "^4.2.1"
|
||||
jieba = "^0.42.1"
|
||||
lxml = "4.6.5"
|
||||
nonebot-plugin-apscheduler = "^0.1.2"
|
||||
opencv-python = "^4.5.5"
|
||||
Pillow = "^9.0.1"
|
||||
playwright = "^1.18.2"
|
||||
@@ -39,10 +37,12 @@ dateparser = "^1.1.0"
|
||||
cn2an = "^0.5.16"
|
||||
python-jose = "^3.3.0"
|
||||
python-multipart = "^0.0.5"
|
||||
bilireq = "^0.2.0"
|
||||
bilireq = "^0.2.3.post0"
|
||||
emoji = "^1.7.0"
|
||||
wordcloud = "^1.8.1"
|
||||
rich = "^12.4.3"
|
||||
gino = "^1.0.1"
|
||||
nonebot-plugin-apscheduler = "^0.2.0"
|
||||
|
||||
[tool.poetry.dev-dependencies]
|
||||
|
||||
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 6.9 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 6.9 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 6.9 KiB |
+3
-1
@@ -10,6 +10,8 @@
|
||||
"poetry.lock",
|
||||
"pyproject.toml"
|
||||
],
|
||||
"add_file": [],
|
||||
"add_file": [
|
||||
"resources/image/shop_icon"
|
||||
],
|
||||
"delete_file": []
|
||||
}
|
||||
+97
-47
@@ -1,17 +1,52 @@
|
||||
from typing import Callable, Union, Tuple
|
||||
from typing import Callable, Union, Tuple, Optional
|
||||
from nonebot.adapters.onebot.v11 import MessageSegment, Message
|
||||
from nonebot.plugin import require
|
||||
|
||||
|
||||
use = require("use")
|
||||
shop = require("shop_handle")
|
||||
|
||||
|
||||
class ShopRegister(dict):
|
||||
def __init__(self, *args, **kwargs):
|
||||
super(ShopRegister, self).__init__(*args, **kwargs)
|
||||
self._data = {}
|
||||
self._flag = True
|
||||
|
||||
def before_handle(self, name: Union[str, Tuple[str, ...]], load_status: bool = True):
|
||||
"""
|
||||
说明:
|
||||
使用前检查方法
|
||||
参数:
|
||||
:param name: 道具名称
|
||||
:param load_status: 加载状态
|
||||
"""
|
||||
def register_before_handle(name_list: Tuple[str, ...], func: Callable):
|
||||
if load_status:
|
||||
for name_ in name_list:
|
||||
if not self._data[name_]:
|
||||
self._data[name_] = {}
|
||||
if not self._data[name_].get('before_handle'):
|
||||
self._data[name_]['before_handle'] = []
|
||||
self._data[name]['before_handle'].append(func)
|
||||
_name = (name,) if isinstance(name, str) else name
|
||||
return lambda func: register_before_handle(_name, func)
|
||||
|
||||
def after_handle(self, name: Union[str, Tuple[str, ...]], load_status: bool = True):
|
||||
"""
|
||||
说明:
|
||||
使用后执行方法
|
||||
参数:
|
||||
:param name: 道具名称
|
||||
:param load_status: 加载状态
|
||||
"""
|
||||
def register_after_handle(name_list: Tuple[str, ...], func: Callable):
|
||||
if load_status:
|
||||
for name_ in name_list:
|
||||
if not self._data[name_]:
|
||||
self._data[name_] = {}
|
||||
if not self._data[name_].get('after_handle'):
|
||||
self._data[name_]['after_handle'] = []
|
||||
self._data[name_]['after_handle'].append(func)
|
||||
_name = (name,) if isinstance(name, str) else name
|
||||
return lambda func: register_after_handle(_name, func)
|
||||
|
||||
def register(
|
||||
self,
|
||||
name: Tuple[str, ...],
|
||||
@@ -21,13 +56,15 @@ class ShopRegister(dict):
|
||||
limit_time: Tuple[int, ...],
|
||||
load_status: Tuple[bool, ...],
|
||||
daily_limit: Tuple[int, ...],
|
||||
is_passive: Tuple[bool, ...],
|
||||
icon: Tuple[str, ...],
|
||||
**kwargs,
|
||||
):
|
||||
def add_register_item(func: Callable):
|
||||
if name in self._data.keys():
|
||||
raise ValueError("该商品已注册,请替换其他名称!")
|
||||
for n, p, d, dd, l, s, dl in zip(
|
||||
name, price, des, discount, limit_time, load_status, daily_limit
|
||||
for n, p, d, dd, l, s, dl, pa, i in zip(
|
||||
name, price, des, discount, limit_time, load_status, daily_limit, is_passive, icon
|
||||
):
|
||||
if s:
|
||||
_temp_kwargs = {}
|
||||
@@ -36,46 +73,60 @@ class ShopRegister(dict):
|
||||
_temp_kwargs[key.split("_", maxsplit=1)[-1]] = value
|
||||
else:
|
||||
_temp_kwargs[key] = value
|
||||
self._data[n] = {
|
||||
temp = self._data.get(n, {})
|
||||
temp.update({
|
||||
"price": p,
|
||||
"des": d,
|
||||
"discount": dd,
|
||||
"limit_time": l,
|
||||
"daily_limit": dl,
|
||||
"icon": i,
|
||||
"is_passive": pa,
|
||||
"func": func,
|
||||
"kwargs": _temp_kwargs,
|
||||
}
|
||||
})
|
||||
self._data[n] = temp
|
||||
return func
|
||||
|
||||
return lambda func: add_register_item(func)
|
||||
|
||||
async def load_register(self):
|
||||
require("use")
|
||||
require("shop_handle")
|
||||
from basic_plugins.shop.use.data_source import register_use, func_manager
|
||||
from basic_plugins.shop.shop_handle.data_source import register_goods
|
||||
# 统一进行注册
|
||||
if self._flag:
|
||||
# 只进行一次注册
|
||||
self._flag = False
|
||||
for name in self._data.keys():
|
||||
await shop.register_goods(
|
||||
await register_goods(
|
||||
name,
|
||||
self._data[name]["price"],
|
||||
self._data[name]["des"],
|
||||
self._data[name]["discount"],
|
||||
self._data[name]["limit_time"],
|
||||
self._data[name]["daily_limit"],
|
||||
self._data[name]["is_passive"],
|
||||
self._data[name]["icon"],
|
||||
)
|
||||
use.register_use(
|
||||
register_use(
|
||||
name, self._data[name]["func"], **self._data[name]["kwargs"]
|
||||
)
|
||||
func_manager.register_use_before_handle(name, self._data[name].get('before_handle', []))
|
||||
func_manager.register_use_after_handle(name, self._data[name].get('after_handle', []))
|
||||
|
||||
def __call__(
|
||||
self,
|
||||
name: Union[str, Tuple[str, ...]],
|
||||
price: Union[float, Tuple[float, ...]],
|
||||
des: Union[str, Tuple[str, ...]],
|
||||
discount: Union[float, Tuple[float, ...]] = 1,
|
||||
limit_time: Union[int, Tuple[int, ...]] = 0,
|
||||
load_status: Union[bool, Tuple[bool, ...]] = True,
|
||||
daily_limit: Union[int, Tuple[int, ...]] = 0,
|
||||
name: Union[str, Tuple[str, ...]], # 名称
|
||||
price: Union[float, Tuple[float, ...]], # 价格
|
||||
des: Union[str, Tuple[str, ...]], # 简介
|
||||
discount: Union[float, Tuple[float, ...]] = 1, # 折扣
|
||||
limit_time: Union[int, Tuple[int, ...]] = 0, # 限时
|
||||
load_status: Union[bool, Tuple[bool, ...]] = True, # 加载状态
|
||||
daily_limit: Union[int, Tuple[int, ...]] = 0, # 每日限购
|
||||
is_passive: Union[bool, Tuple[bool, ...]] = False, # 被动道具(无法被'使用道具'命令消耗)
|
||||
icon: Union[str, Tuple[str, ...]] = False, # 图标
|
||||
**kwargs,
|
||||
):
|
||||
_tuple_list = []
|
||||
@@ -89,35 +140,15 @@ class ShopRegister(dict):
|
||||
f"注册商品 {name} 中 name,price,des,discount,limit_time,load_status,daily_limit 数量不符!"
|
||||
)
|
||||
_current_len = _current_len if _current_len > -1 else 1
|
||||
_name = name if isinstance(name, tuple) else (name,)
|
||||
_price = (
|
||||
price
|
||||
if isinstance(price, tuple)
|
||||
else tuple([price for _ in range(_current_len)])
|
||||
)
|
||||
_discount = (
|
||||
discount
|
||||
if isinstance(discount, tuple)
|
||||
else tuple([discount for _ in range(_current_len)])
|
||||
)
|
||||
_limit_time = (
|
||||
limit_time
|
||||
if isinstance(limit_time, tuple)
|
||||
else tuple([limit_time for _ in range(_current_len)])
|
||||
)
|
||||
_des = (
|
||||
des if isinstance(des, tuple) else tuple([des for _ in range(_current_len)])
|
||||
)
|
||||
_load_status = (
|
||||
load_status
|
||||
if isinstance(load_status, tuple)
|
||||
else tuple([load_status for _ in range(_current_len)])
|
||||
)
|
||||
_daily_limit = (
|
||||
daily_limit
|
||||
if isinstance(daily_limit, tuple)
|
||||
else tuple([daily_limit for _ in range(_current_len)])
|
||||
)
|
||||
_name = self.__get(name, _current_len)
|
||||
_price = self.__get(price, _current_len)
|
||||
_discount = self.__get(discount, _current_len)
|
||||
_limit_time = self.__get(limit_time, _current_len)
|
||||
_des = self.__get(des, _current_len)
|
||||
_load_status = self.__get(load_status, _current_len)
|
||||
_daily_limit = self.__get(daily_limit, _current_len)
|
||||
_is_passive = self.__get(is_passive, _current_len)
|
||||
_icon = self.__get(icon, _current_len)
|
||||
return self.register(
|
||||
_name,
|
||||
_price,
|
||||
@@ -126,9 +157,14 @@ class ShopRegister(dict):
|
||||
_limit_time,
|
||||
_load_status,
|
||||
_daily_limit,
|
||||
_is_passive,
|
||||
_icon,
|
||||
**kwargs,
|
||||
)
|
||||
|
||||
def __get(self, value, _current_len):
|
||||
return value if isinstance(value, tuple) else tuple([value for _ in range(_current_len)])
|
||||
|
||||
def __setitem__(self, key, value):
|
||||
self._data[key] = value
|
||||
|
||||
@@ -151,4 +187,18 @@ class ShopRegister(dict):
|
||||
return self._data.items()
|
||||
|
||||
|
||||
class NotMeetUseConditionsException(Exception):
|
||||
|
||||
"""
|
||||
不满足条件异常类
|
||||
"""
|
||||
|
||||
def __init__(self, info: Optional[Union[str, MessageSegment, Message]]):
|
||||
super().__init__(self)
|
||||
self._info = info
|
||||
|
||||
def get_info(self):
|
||||
return self._info
|
||||
|
||||
|
||||
shop_register = ShopRegister()
|
||||
|
||||
@@ -0,0 +1,84 @@
|
||||
from typing import List, Callable, Optional
|
||||
|
||||
from nonebot.adapters.onebot.v11 import GroupMessageEvent, MessageEvent
|
||||
from nonebot.internal.matcher import Matcher
|
||||
from nonebot.internal.params import Depends
|
||||
from models.user_shop_gold_log import UserShopGoldLog
|
||||
from models.bag_user import BagUser
|
||||
from utils.message_builder import at
|
||||
from utils.utils import get_message_at, get_message_face, get_message_img, get_message_text
|
||||
|
||||
|
||||
def cost_gold(gold: int):
|
||||
"""
|
||||
说明:
|
||||
插件方法调用使用金币
|
||||
参数:
|
||||
:param gold: 金币数量
|
||||
"""
|
||||
async def dependency(matcher: Matcher, event: GroupMessageEvent):
|
||||
if (await BagUser.get_gold(event.user_id, event.group_id)) < gold:
|
||||
await matcher.finish(at(event.user_id) + f"金币不足..该功能需要{gold}金币..")
|
||||
await BagUser.spend_gold(event.user_id, event.group_id, gold)
|
||||
await UserShopGoldLog.add_shop_log(event.user_id, event.group_id, 2, matcher.plugin_name, gold, 1)
|
||||
|
||||
return Depends(dependency)
|
||||
|
||||
|
||||
async def _match(matcher: Matcher, event: MessageEvent, msg: Optional[str], func: Callable):
|
||||
_list = func(event.message)
|
||||
if not _list and msg:
|
||||
await matcher.finish(msg)
|
||||
return _list
|
||||
|
||||
|
||||
def ImageList(msg: Optional[str] = None) -> List[str]:
|
||||
"""
|
||||
说明:
|
||||
获取图片列表,含有msg时不能为空,为空时提示并结束事件
|
||||
参数:
|
||||
:param msg: 提示文本
|
||||
"""
|
||||
async def dependency(matcher: Matcher, event: MessageEvent):
|
||||
return await _match(matcher, event, msg, get_message_img)
|
||||
|
||||
return Depends(dependency)
|
||||
|
||||
|
||||
def AtList(msg: Optional[str] = None) -> List[str]:
|
||||
"""
|
||||
说明:
|
||||
获取at列表,含有msg时不能为空,为空时提示并结束事件
|
||||
参数:
|
||||
:param msg: 提示文本
|
||||
"""
|
||||
async def dependency(matcher: Matcher, event: MessageEvent):
|
||||
return await _match(matcher, event, msg, get_message_at)
|
||||
|
||||
return Depends(dependency)
|
||||
|
||||
|
||||
def FaceList(msg: Optional[str] = None) -> List[str]:
|
||||
"""
|
||||
说明:
|
||||
获取face列表,含有msg时不能为空,为空时提示并结束事件
|
||||
参数:
|
||||
:param msg: 提示文本
|
||||
"""
|
||||
async def dependency(matcher: Matcher, event: MessageEvent):
|
||||
return await _match(matcher, event, msg, get_message_face)
|
||||
|
||||
return Depends(dependency)
|
||||
|
||||
|
||||
def PlaintText(msg: Optional[str] = None) -> str:
|
||||
"""
|
||||
说明:
|
||||
获取纯文本且,含有msg时不能为空,为空时提示并结束事件
|
||||
参数:
|
||||
:param msg: 提示文本
|
||||
"""
|
||||
async def dependency(matcher: Matcher, event: MessageEvent):
|
||||
return await _match(matcher, event, msg, get_message_text)
|
||||
|
||||
return Depends(dependency)
|
||||
|
||||
@@ -178,8 +178,8 @@ class BuildImage:
|
||||
self.h = int(h)
|
||||
self.paste_image_width = int(paste_image_width)
|
||||
self.paste_image_height = int(paste_image_height)
|
||||
self.current_w = 0
|
||||
self.current_h = 0
|
||||
self._current_w = 0
|
||||
self._current_h = 0
|
||||
self.font = ImageFont.truetype(str(FONT_PATH / font), int(font_size))
|
||||
if not plain_text and not color:
|
||||
color = (255, 255, 255)
|
||||
@@ -287,11 +287,11 @@ class BuildImage:
|
||||
pos = (width, height)
|
||||
if isinstance(img, BuildImage):
|
||||
img = img.markImg
|
||||
if self.current_w == self.w:
|
||||
self.current_w = 0
|
||||
self.current_h += self.paste_image_height
|
||||
if self._current_w >= self.w:
|
||||
self._current_w = 0
|
||||
self._current_h += self.paste_image_height
|
||||
if not pos:
|
||||
pos = (self.current_w, self.current_h)
|
||||
pos = (self._current_w, self._current_h)
|
||||
if alpha:
|
||||
try:
|
||||
self.markImg.paste(img, pos, img)
|
||||
@@ -300,7 +300,7 @@ class BuildImage:
|
||||
self.markImg.paste(img, pos, img)
|
||||
else:
|
||||
self.markImg.paste(img, pos)
|
||||
self.current_w += self.paste_image_width
|
||||
self._current_w += self.paste_image_width
|
||||
|
||||
def getsize(self, msg: str) -> Tuple[int, int]:
|
||||
"""
|
||||
|
||||
@@ -14,11 +14,6 @@ from .requests_manager import RequestManager
|
||||
from configs.path_config import DATA_PATH
|
||||
|
||||
|
||||
# 全局字典
|
||||
GDict = {
|
||||
"run_sql": [] # 需要启动前运行的sql语句
|
||||
}
|
||||
|
||||
# 群功能开关 | 群被动技能 | 群权限 管理
|
||||
group_manager: Optional[GroupManager] = GroupManager(
|
||||
DATA_PATH / "manager" / "group_manager.json"
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import io
|
||||
from pathlib import Path
|
||||
from typing import List, Union
|
||||
|
||||
@@ -26,7 +27,7 @@ def image(
|
||||
return MessageSegment.image(file)
|
||||
logger.warning(f"图片 {file.absolute()}缺失...")
|
||||
return ""
|
||||
elif isinstance(file, bytes):
|
||||
elif isinstance(file, (bytes, io.BytesIO)):
|
||||
return MessageSegment.image(file)
|
||||
elif b64:
|
||||
return MessageSegment.image(b64 if "base64://" in b64 else "base64://" + b64)
|
||||
|
||||
+34
-6
@@ -18,8 +18,17 @@ try:
|
||||
except ModuleNotFoundError:
|
||||
import json
|
||||
|
||||
require("nonebot_plugin_apscheduler")
|
||||
from nonebot_plugin_apscheduler import scheduler
|
||||
|
||||
scheduler = require("nonebot_plugin_apscheduler").scheduler
|
||||
scheduler = scheduler
|
||||
|
||||
# 全局字典
|
||||
GDict = {
|
||||
"run_sql": [], # 需要启动前运行的sql语句
|
||||
"_shop_before_handle": {}, # 商品使用前函数
|
||||
"_shop_after_handle": {}, # 商品使用后函数
|
||||
}
|
||||
|
||||
|
||||
class CountLimiter:
|
||||
@@ -110,8 +119,8 @@ class BanCheckLimiter:
|
||||
self.mint[key] = 0
|
||||
return False
|
||||
if (
|
||||
self.mint[key] >= self.default_count
|
||||
and time.time() - self.mtime[key] < self.default_check_time
|
||||
self.mint[key] >= self.default_count
|
||||
and time.time() - self.mtime[key] < self.default_check_time
|
||||
):
|
||||
self.mtime[key] = time.time()
|
||||
self.mint[key] = 0
|
||||
@@ -238,6 +247,25 @@ def get_message_img(data: Union[str, Message]) -> List[str]:
|
||||
return img_list
|
||||
|
||||
|
||||
def get_message_face(data: Union[str, Message]) -> List[str]:
|
||||
"""
|
||||
说明:
|
||||
获取消息中所有的 face Id
|
||||
参数:
|
||||
:param data: event.json()
|
||||
"""
|
||||
face_list = []
|
||||
if isinstance(data, str):
|
||||
data = json.loads(data)
|
||||
for msg in data["message"]:
|
||||
if msg["type"] == "face":
|
||||
face_list.append(msg["data"]["id"])
|
||||
else:
|
||||
for seg in data["face"]:
|
||||
face_list.append(seg.data["id"])
|
||||
return face_list
|
||||
|
||||
|
||||
def get_message_img_file(data: Union[str, Message]) -> List[str]:
|
||||
"""
|
||||
说明:
|
||||
@@ -383,7 +411,7 @@ def cn2py(word: str) -> str:
|
||||
|
||||
|
||||
def change_pixiv_image_links(
|
||||
url: str, size: Optional[str] = None, nginx_url: Optional[str] = None
|
||||
url: str, size: Optional[str] = None, nginx_url: Optional[str] = None
|
||||
):
|
||||
"""
|
||||
说明:
|
||||
@@ -403,8 +431,8 @@ def change_pixiv_image_links(
|
||||
if nginx_url:
|
||||
url = (
|
||||
url.replace("i.pximg.net", nginx_url)
|
||||
.replace("i.pixiv.cat", nginx_url)
|
||||
.replace("_webp", "")
|
||||
.replace("i.pixiv.cat", nginx_url)
|
||||
.replace("_webp", "")
|
||||
)
|
||||
return url
|
||||
|
||||
|
||||
Reference in New Issue
Block a user