Compare commits

..
121 Commits
Author SHA1 Message Date
HibiKier 0ea740aa7c Update __version__ 2022-09-16 19:35:33 +08:00
HibiKier 5f118471cb 修复一些问题 2022-09-16 19:29:05 +08:00
HibiKier 504f78dd49 Merge pull request #1090 from NumberSir/main
fix: bilibili_sub, azur_draw_card
2022-09-16 19:14:19 +08:00
Number_Sir 72723c7d1f 修复碧蓝航线抽卡错误 2022-09-16 12:55:22 +08:00
Number_Sir 6a74053853 bilibili_sub更新检查失败时改为logger通知,不在群里发消息 2022-09-16 12:43:22 +08:00
HibiKier af0b4987a4 Merge pull request #1067 from DDS-DS/main
fix:bt url打开失败
2022-09-10 00:20:44 +08:00
HibiKier 775140cf79 自定义群欢迎消息参数不完全时提示报错 2022-09-09 22:41:39 +08:00
HibiKier 6a8a796add 添加插件数据初始化判断 2022-09-08 20:13:26 +08:00
DDS-DS 8e4bda3760 fix bt url打开失败 2022-09-08 01:35:41 +08:00
HibiKier a36e8145a8 旧词条提供图片迁移 2022-09-04 22:05:56 +08:00
HibiKier 3cbd899daa 修复原神资源查询地图api数据变更导致更新的地图不完全 2022-09-03 18:17:42 +08:00
HibiKier 30f356a2c7 Merge remote-tracking branch 'origin/main' 2022-09-03 17:30:21 +08:00
HibiKier 1138a115ce B站订阅在群里中任意群管理员可以统一管理 2022-09-03 17:30:03 +08:00
HibiKier c397da75e7 Merge pull request #1033 from q56535/main
av号覆盖全面,且修复av号链接
2022-09-03 17:14:22 +08:00
HibiKier 1d141c9313 Merge pull request #1059 from unknownsno/main
fix bug
2022-09-03 16:56:49 +08:00
q56535 a4cc365af8 Merge branch 'HibiKier:main' into main 2022-09-03 16:44:57 +08:00
HibiKier 4e551bec0d 修复词条含有CQ回答的模糊匹配无法被解析 2022-09-03 16:16:33 +08:00
unknwonsno e03086062d fix bug 2022-09-03 14:43:13 +08:00
HibiKier 270a5a5e20 Merge pull request #1053 from unknownsno/main
原神玩家查询增加须弥地区
2022-09-03 14:24:18 +08:00
unknwonsno bda9133ed6 原神玩家查询增加须弥地区 2022-08-31 22:22:00 +08:00
HibiKier 749f475f2c UPDATE README.md 2022-08-27 18:25:30 +08:00
HibiKier e38394948c Merge pull request #1044 from zyj3421/main
修复签到积分双倍后,日志记录获得积分变4倍问题
2022-08-27 18:22:35 +08:00
zyj2134 51fc4cbb6a 修复签到积分双倍后,日志记录获得积分变4倍问题 2022-08-26 22:49:51 +08:00
zyj2134 9a976cda37 Merge remote-tracking branch 'origin/main' 2022-08-26 22:39:48 +08:00
HibiKier e42521f0a3 修复词条关键词"问"前空格问题 2022-08-26 19:39:23 +08:00
HibiKier 0756d39ac5 修复群管理员无法添加词条 2022-08-26 19:26:09 +08:00
zyj2134 4595bdcee4 Merge remote-tracking branch 'origin/main'
# Conflicts:
#	README.md
#	plugins/bilibili_sub/__init__.py
2022-08-25 23:00:49 +08:00
HibiKier 38160065cc UPDATE README.md 2022-08-23 23:02:06 +08:00
HibiKier 3d04f013cd Merge pull request #1032 from LambdaYH/p1
修了下模糊匹配 issue#1026
2022-08-23 20:21:25 +08:00
q56535 8e1a416be3 av号覆盖全面 2022-08-23 13:43:43 +08:00
LambdaYH a98bc42f1a fix https://github.com/HibiKier/zhenxun_bot/issues/1026 2022-08-23 13:16:14 +08:00
HibiKier 6c93769838 UPDATE README.md 2022-08-22 22:51:36 +08:00
HibiKier d2dccfebc5 取消配置替换定时任务,统一存储 2022-08-22 22:49:35 +08:00
HibiKier 82fe59e469 修复首次安装时词条旧表出错 2022-08-22 20:59:08 +08:00
HibiKier afc05e1731 Merge remote-tracking branch 'origin/main' 2022-08-22 20:48:57 +08:00
HibiKier 0206b0a522 UPDATE README.md 2022-08-22 20:47:32 +08:00
HibiKier 0e1e5a5ebe Merge pull request #1012 from LambdaYH/patch-4
修复词库问答几个问题
2022-08-22 20:45:31 +08:00
HibiKier 9ad088ed6a Merge pull request #1010 from CRAZYShimakaze/main
修正尘歌壶和质变仪图片获取地址
2022-08-22 20:43:22 +08:00
HibiKier 34da04bba5 Merge pull request #1014 from xiayingLIULI/main
对米游社cookie进行判断,整合米游社签到信息
2022-08-22 20:41:32 +08:00
HibiKier 09ce812faf Update __init__.py 2022-08-22 20:39:03 +08:00
xiayingLIULI 1dc8cb2a98 修复米游社签到在无用户cookie的情况下执行的错误 2022-08-22 13:45:04 +08:00
xiayingLIULI e73c509823 修复米游社签到在无用户cookie的情况下执行的错误 2022-08-22 13:33:49 +08:00
Cinte e70a85400f 查看词条的id范围判断少了个等号 2022-08-22 12:56:35 +08:00
Cinte f7aecf8eed Update word_handle.py 2022-08-22 12:37:06 +08:00
Cinte cc2645aa9d Update word_handle.py 2022-08-22 12:34:21 +08:00
xiayingLIULI 52d23c4a8d 修复能在群聊中查看我的cookie 2022-08-22 11:44:21 +08:00
Cinte 1e57ddb9ee 处理at后自动接空格的情况,problem中允许单空白字符号 & 修复二次元触发 2022-08-22 11:14:04 +08:00
Cinte 7ae883d921 使用check构造的problem而不在message_handle中重新构造 2022-08-22 10:14:59 +08:00
Cinte 27e97214bd 仅仅是检查图片hash就将图片下载到磁盘是否有些浪费io,于是改成了内存中直接计算 2022-08-22 09:49:33 +08:00
LambdaYH 6321a9e7e0 使用gino提供的方法,避免构建sql字符串时出现非法查询语句 2022-08-22 00:49:30 +08:00
xiayingLIULI a9eadca46c 对米游社cookie进行判断,整合米游社签到信息 2022-08-22 00:42:00 +08:00
xiayingLIULI 76bef8a725 对米游社cookie进行判断,整合米游社签到信息 2022-08-22 00:40:51 +08:00
LambdaYH 308b76dc7d _rule中使用messagesegment中未转义的字符串,而添加词条时使用了转义后的字符串,导致二者不一致 2022-08-22 00:38:25 +08:00
LambdaYH 31d4d29f3e 修复匹配时候忽视了word_type的约束 2022-08-21 23:47:21 +08:00
LambdaYH 016a7e87ea Merge branch 'HibiKier-main' into patch-4 2022-08-21 23:46:46 +08:00
LambdaYH 4ce88c7bfc merge 2022-08-21 23:46:38 +08:00
LambdaYH f570bffcf7 修复匹配时候忽视了word_type的约束 2022-08-21 23:42:46 +08:00
LambdaYH 3bb9aaf637 添加正则词条前先检查正则有效性 & 正则词条先进行转义,不然符号都乱了 2022-08-21 23:21:34 +08:00
LambdaYH cf7a64c934 fix: 问答为[at + 文本]的情况时无法正常添加 2022-08-21 22:24:06 +08:00
LambdaYH c466603193 Fix at messages with suffixed text 2022-08-21 21:58:31 +08:00
CRAZY bc53f329a9 修正尘歌壶和质变仪图片获取地址 2022-08-21 20:33:06 +08:00
CRAZY 4a5cc5153f 修正质变仪图片获取地址 2022-08-21 20:11:44 +08:00
HibiKier 8697670fe1 UPDATE README.md 2022-08-21 18:43:58 +08:00
HibiKier 64d8543af9 UPDATE __version__ 2022-08-21 18:42:16 +08:00
HibiKier 482cc242cb UPDATE README.md 2022-08-21 18:41:13 +08:00
HibiKier 2051e8183c 词条模糊匹配使用contains方法 2022-08-21 18:32:24 +08:00
HibiKier d183903917 update README.md 2022-08-21 18:15:18 +08:00
HibiKier 5c695899a1 将配置替换加入单次定时任务,提高启动速度 2022-08-21 18:09:31 +08:00
HibiKier 708e912069 Merge pull request #1008 from LambdaYH/patch-4
fix: WordBank.check()
2022-08-21 16:57:22 +08:00
Cinte db1f4b2b52 fix: WordBank.check() 2022-08-21 16:54:28 +08:00
HibiKier 1508782f49 提供全局字典GDict,适配omega 13w张图的数据结构表 2022-08-21 16:47:50 +08:00
HibiKier 445b830683 开箱提供重置开箱命令 2022-08-21 14:10:49 +08:00
HibiKier 1a17e88142 b站订阅直播订阅支持短号 2022-08-21 13:57:48 +08:00
HibiKier c0ec2e7856 b站订阅直播订阅支持短号 2022-08-21 13:57:17 +08:00
HibiKier 9e962bd0d1 修正词条加载路径 2022-08-21 13:44:06 +08:00
HibiKier 7efcff13bb backup 2022-08-21 13:37:03 +08:00
zyj2134 e70d0df949 Merge remote-tracking branch 'origin/main' 2022-08-19 23:49:16 +08:00
HibiKier fcaa469edd Merge pull request #996 from shinianj/patch-1
Update README.md
2022-08-16 08:46:10 +08:00
shinianj 51508182ef Update README.md 2022-08-16 00:38:14 +08:00
HibiKier 27e78c1626 update README.md 2022-08-14 22:11:36 +08:00
HibiKier 0b4d37a0b6 修复订阅主播时动态获取的id是直播间id 2022-08-14 21:31:57 +08:00
HibiKier ddbebba6f1 修复epic未获取时间时出错 2022-08-14 21:18:29 +08:00
zyj2134 7f296348e4 Merge remote-tracking branch 'origin/main' 2022-08-09 22:20:32 +08:00
HibiKier ca78e32d97 Update README.md 2022-08-08 04:23:05 +08:00
HibiKier 4efc561f4d Merge pull request #969 from DDS-DS/main
修复赛马娘重载卡池失败的问题
2022-08-08 04:22:24 +08:00
HibiKier 40fed798a1 Update data_source.py 2022-08-07 01:10:19 +08:00
HibiKier 0876afc08c Update README.md 2022-08-06 20:40:10 +08:00
HibiKier ba4c4d06ff Merge pull request #971 from HDU-Nbsp/main
修复了原神自动签到返回invalid request的问题,新增查看我的cookie命令
2022-08-06 20:39:30 +08:00
HibiKier f64c30af48 Update __init__.py 2022-08-06 20:39:16 +08:00
HDU_Nbsp bb0cea2dd3 修复了原神自动签到返回invalid request的问题,新增查看我的cookie命令 2022-08-05 22:32:15 +08:00
dingshan 3c120ce602 增加备注说明 2022-08-05 15:49:21 +08:00
dingshan 6151dfb161 修复赛马娘重载卡池失败的问题 2022-08-05 15:42:53 +08:00
dingshan f05d4ec931 修复赛马娘重载卡池失败的问题 2022-08-05 15:35:45 +08:00
HibiKier e16afc3fc2 update README.md 2022-08-03 20:26:41 +08:00
HibiKier 1b8753fead Merge pull request #961 from Gaomengkai/main
修复 bili动态链接在投稿视频时URL和分割线连在一起
2022-08-03 20:20:29 +08:00
HibiKier ba960d1ced Merge pull request #957 from monsterxcn/patch-1
更新 Epic 免费游戏商城链接拼接规则
2022-08-03 20:14:13 +08:00
Merky Gao 41b60b6152 修复 bili动态链接在投稿视频时URL和分割线连在一起
比如up发布一条动态后,qq显示的链接事[https://t.bilibili.com/123456789012345678-------------],会把后面的分隔符算在链接里边,导致直接点开后404(*^_^*). だから……お願い!
2022-08-01 11:11:34 +08:00
github-actions[bot] 70ddce713c Auto generate poetry.lock 2022-08-01 00:23:45 +00:00
HibiKier e15622748b Update pyproject.toml 2022-08-01 08:21:30 +08:00
HibiKier 96687021ff Merge pull request #958 from yajiwa/main
Update poetry.lock
2022-08-01 08:21:12 +08:00
yajiwa 3c0a7ef510 Update poetry.lock 2022-07-31 22:22:54 +08:00
HibiKier 4d84c9fb3f 修复metadata中version判断 2022-07-31 18:00:44 +08:00
HibiKier aa35a16610 修复pix无图时不会提醒而是出错 2022-07-31 17:48:54 +08:00
HibiKier a380fcb680 修复webui元祖类型无法正确转换类型 2022-07-31 17:45:14 +08:00
Monst.x e544afe475 更新 Epic 免费游戏商城链接拼接规则 2022-07-31 17:41:10 +08:00
HibiKier c089388323 适配metadata 2022-07-31 17:30:29 +08:00
HibiKier e97a043031 Merge pull request #953 from tanzhi77/main
发言记录统计添加日消息统计
2022-07-30 10:55:50 +08:00
HibiKier d1b6dfe577 Merge pull request #951 from yajiwa/main
替换了cos和bt的url
2022-07-30 10:53:49 +08:00
HibiKier 9189fd411e Update __init__.py 2022-07-29 19:50:21 +08:00
弹指 432eba80a9 发言记录统计添加日消息统计 2022-07-29 19:08:16 +08:00
yajiwa db39612c54 update bt 2022-07-28 16:49:28 +08:00
yajiwa 5794c82a2d update coser 2022-07-28 16:48:45 +08:00
yajiwa acf875a886 Merge branch 'HibiKier:main' into main 2022-07-28 14:42:47 +08:00
zyj2134 a1308fbdc9 Merge remote-tracking branch 'origin/main' 2022-07-24 22:16:51 +08:00
yajiwa e4f2b761d6 Merge branch 'HibiKier:main' into main 2022-07-24 18:51:09 +08:00
yajiwa ee0ff762e5 Merge branch 'HibiKier:main' into main 2022-07-23 18:02:24 +08:00
zyj2134 946fbbec3c Merge remote-tracking branch 'origin/main' 2022-07-17 15:34:18 +08:00
zyj2134 bd531c85b1 Merge remote-tracking branch 'origin/main'
# Conflicts:
#	README.md
2022-07-14 20:44:12 +08:00
HibiKier cda9aae8a3 Update README.md 2022-07-07 22:02:33 +08:00
HibiKier a33e785043 Merge pull request #891 from yajiwa/main
更新微博热搜
2022-07-07 22:01:53 +08:00
yajiwa 833e266a1f update wbtop 2022-07-06 04:59:54 +08:00
77 changed files with 3167 additions and 1792 deletions
+17 -1
View File
@@ -138,10 +138,26 @@ dmypy.json
# Cython debug symbols
cython_debug/
demo.py
test.py
server_ip.py
game_utils.py
member_activity_handle.py
Yu-Gi-Oh/
csgo/
fantasy_card/
data/
log/
backup/
extensive_plugin/
test/
bot.py
data/
.env
.env.dev
resources/
!/configs/config.py
!/configs/config.yaml
!/.env
!/.env.dev
!/plugins/csgo_server/
!/plugins/activity/
+127 -6
View File
@@ -128,7 +128,7 @@
- [x] 移动图片 (同上)
- [x] 删除图片 (同上)
- [x] 群内B站订阅
- [x] 群词条
- [x] 词条设置
- [x] 休息吧/醒来
### 已实现的超级用户功能
@@ -245,8 +245,133 @@ __Docker 全量版(包含 真寻Bot PostgreSQL数据库 go-cqhttp webui等)_
**点击上方的 GitHub 徽标查看教程**
PS: **ARM平台** 请使用全量版 同时 **如果你的机器 RAM < 1G 可能无法正常启动全量版容器**
## [爱发电](https://afdian.net/@HibiKier)
<details>
<summary>爱发电 以及 感谢投喂 </summary>
<img width="365px" height="450px" src="https://user-images.githubusercontent.com/45528451/175059389-cfeb8174-fa07-4939-80ab-a039087a50f6.png">
### 感谢名单
(可以告诉我你的 __github__ 地址,我偷偷换掉0v|)
[腊条](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)
[爱发电用户_c58s](https://afdian.net/u/a6ad8dda195e11ed9a4152540025c377)
[爱发电用户_eNr9](https://afdian.net/u/05fdb41c0c9a11ed814952540025c377)
[MangataAkihi](https://github.com/Sakuracio)
[炀](https://afdian.net/u/69b76e9ec77b11ec874f52540025c377)
[爱发电用户_Bc6j](https://afdian.net/u/8546be24f44111eca64052540025c377)
[大魔王](https://github.com/xipesoy)
[CopilotLaLaLa](https://github.com/CopilotLaLaLa)
[嘿小欧](https://afdian.net/u/daa4bec4f24911ec82e552540025c377)
[回忆的秋千](https://afdian.net/u/e315d9c6f14f11ecbeef52540025c377)
[十年くん](https://github.com/shinianj)
[哇](https://afdian.net/u/9b266244f23911eca19052540025c377)
[yajiwa](https://github.com/yajiwa)
[爆金币](https://afdian.net/u/0d78879ef23711ecb22452540025c377)
</details>
## 更新
### 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导入
* 适配omega 13w张图的数据结构表(建议删表重导)
* 除首次启动外将配置替换加入单次定时任务,加快启动速度
* fix: WordBank.check() [@pull/1008](https://github.com/HibiKier/zhenxun_bot/pull/1008)
* 改进插件 `我有一个朋友`,避免触发过于频繁 [@pull/1001](https://github.com/HibiKier/zhenxun_bot/pull/1001)
* 原神便笺新增洞天宝钱和参量质变仪提示 [@pull/1005](https://github.com/HibiKier/zhenxun_bot/pull/1005)
* 新增米游社签到功能,自动领取(白嫖)米游币 [@pull/991](https://github.com/HibiKier/zhenxun_bot/pull/991)
### 2022/8/14
* 修复epic未获取到时间时出错
* 修复订阅主播时动态获取的id是直播间id
### 2022/8/8
* 修复赛马娘重载卡池失败的问题 [@pull/969](https://github.com/HibiKier/zhenxun_bot/pull/969)
### 2022/8/3
* 修复 bili动态链接在投稿视频时URL和分割线连在一起 [@pull/951](https://github.com/HibiKier/zhenxun_bot/pull/961)
* 更新 Epic 免费游戏商城链接拼接规则 [@pull/957](https://github.com/HibiKier/zhenxun_bot/pull/957)
### 2022/8/6
* 修复了原神自动签到返回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动态提供直链
@@ -715,11 +840,7 @@ PS: **ARM平台** 请使用全量版 同时 **如果你的机器 RAM < 1G 可能
__..... 更多更新信息请查看文档__
## Todo
- [ ] web管理
## 爱发电
<img width="365px" height="450px" src="https://user-images.githubusercontent.com/45528451/175059389-cfeb8174-fa07-4939-80ab-a039087a50f6.png">
- [x] web管理
## 感谢
[botuniverse / onebot](https://github.com/botuniverse/onebot) :超棒的机器人协议
+1 -1
View File
@@ -1 +1 @@
__version__: v0.1.6.2
__version__: v0.1.6.3-fix1
@@ -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,
+2 -2
View File
@@ -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}")
@@ -16,11 +16,12 @@ __zx_plugin_name__ = "消息统计"
__plugin_usage__ = """
usage:
发言记录统计
regex:(周|月)?消息排行(des|DES)?(n=[0-9]{1,2})?
regex:(周|月|日)?消息排行(des|DES)?(n=[0-9]{1,2})?
指令:
消息统计?(des)?(n=?)
周消息统计?(des)?(n=?)
月消息统计?(des)?(n=?)
日消息统计?(des)?(n=?)
示例:
消息统计
消息统计des
@@ -31,7 +32,8 @@ __plugin_des__ = "发言消息排行"
__plugin_cmd__ = [
"消息统计",
"周消息统计",
"月消息统计"
"月消息统计",
"日消息统计"
]
__plugin_type__ = ("数据统计", 1)
__plugin_version__ = 0.1
@@ -42,7 +44,7 @@ __plugin_settings__ = {
}
msg_handler = on_regex(r"^(周|月)?消息统计(des|DES)?(n=[0-9]{1,2})?$", priority=5, block=True)
msg_handler = on_regex(r"^(周|月|日)?消息统计(des|DES)?(n=[0-9]{1,2})?$", priority=5, block=True)
@msg_handler.handle()
@@ -53,10 +55,14 @@ async def _(event: GroupMessageEvent, reg_group: Tuple[Any, ...] = RegexGroup())
num = num.split("=")[-1] if num else 10
if num and is_number(num) and 10 < int(num) < 50:
num = int(num)
if date in ["周"]:
date_scope = (datetime.now() - timedelta(days=7), datetime.now())
time_now = datetime.now()
zero_today = time_now - timedelta(hours=time_now.hour, minutes=time_now.minute, seconds=time_now.second)
if date in ["日"]:
date_scope = (zero_today, time_now)
elif date in ["周"]:
date_scope = (time_now - timedelta(days=7), time_now)
elif date in ["月"]:
date_scope = (datetime.now() - timedelta(days=30), datetime.now())
date_scope = (time_now - timedelta(days=30), time_now)
if rank_data := await ChatHistory.get_group_msg_rank(
gid, num, order or "DESC", date_scope
):
@@ -79,7 +85,7 @@ async def _(event: GroupMessageEvent, reg_group: Tuple[Any, ...] = RegexGroup())
pytz.timezone("Asia/Shanghai")
).replace(microsecond=0)
else:
date_scope = datetime.now().replace(microsecond=0)
date_scope = time_now.replace(microsecond=0)
date_str = f"日期:{date_scope} - 至今"
else:
date_str = f"日期:{date_scope[0].replace(microsecond=0)} - {date_scope[1].replace(microsecond=0)}"
+1 -1
View File
@@ -44,7 +44,7 @@ async def _(bot: Bot, event: MessageEvent, state: T_State):
@simple_help.handle()
async def _(bot: Bot, event: MessageEvent, state: T_State, arg: Message = CommandArg()):
async def _(bot: Bot, event: MessageEvent, arg: Message = CommandArg()):
msg = arg.extract_plain_text().strip()
is_super = False
if msg:
+7 -5
View File
@@ -44,7 +44,7 @@ def _create_help_img(
:param help_image: 图片路径
:param simple_help_image: 简易帮助图片路径
"""
_matchers = get_matchers()
_matchers = get_matchers(True)
width = 0
matchers_data = {}
_des_tmp = {}
@@ -55,15 +55,16 @@ def _create_help_img(
# 插件分类
for matcher in _matchers:
plugin_name = None
_plugin = nonebot.plugin.get_plugin(matcher.plugin_name)
_plugin = matcher.plugin
metadata = _plugin.metadata
if not _plugin:
logger.warning(f"获取 功能:{matcher.plugin_name} 失败...")
continue
_module = _plugin.module
try:
plugin_name = _module.__getattribute__("__zx_plugin_name__")
plugin_name = metadata.name if metadata else _module.__getattribute__("__zx_plugin_name__")
try:
plugin_des = _module.__getattribute__("__plugin_des__")
plugin_des = metadata.description if metadata else _module.__getattribute__("__plugin_des__")
except AttributeError:
plugin_des = "_"
if (
@@ -343,13 +344,14 @@ def get_plugin_help(msg: str, is_super: bool = False) -> Optional[str]:
if module:
try:
plugin = nonebot.plugin.get_plugin(module)
metadata = plugin.metadata
if plugin:
if is_super:
result = plugin.module.__getattribute__(
"__plugin_superuser_usage__"
)
else:
result = plugin.module.__getattribute__("__plugin_usage__")
result = metadata.usage if metadata else plugin.module.__getattribute__("__plugin_usage__")
if result:
width = 0
for x in result.split("\n"):
-8
View File
@@ -2,15 +2,12 @@ from nonebot.matcher import Matcher
from nonebot.message import run_preprocessor, IgnoredException
from nonebot.typing import T_State
from ._utils import status_message_manager
from utils.image_utils import text2image
from typing import Dict, Any
from nonebot.adapters.onebot.v11 import (
Bot,
MessageEvent,
PrivateMessageEvent,
GroupMessageEvent,
)
import re
# 为什么AI会自己和自己聊天
@@ -41,11 +38,6 @@ async def _(matcher: Matcher, bot: Bot, event: MessageEvent, state: T_State):
status_message_manager.delete(event.user_id)
raise IgnoredException("有命令就别说话了")
# @Bot.on_calling_api
# async def handle_api_call(bot: Bot, api: str, data: Dict[str, Any]):
# if api in ["send_msg", "send_group_msg", "send_private_msg"]:
# msg = str(data["message"])
# if (r := re.search("\[\[To_Img\|?(.*?)]]", msg)) or (r := re.search("&#91;&#91;To_Img\|?(.*?)&#91;&#91;")):
+1 -1
View File
@@ -6,7 +6,7 @@ import re
@Bot.on_calling_api
async def handle_api_call(bot: Bot, api: str, data: Dict[str, Any]):
async def _(bot: Bot, api: str, data: Dict[str, Any]):
r = None
if (
(
+1 -2
View File
@@ -14,7 +14,6 @@ from .check_plugin_status import check_plugin_status
from nonebot.adapters.onebot.v11 import Bot
from configs.path_config import DATA_PATH
from services.log import logger
from pathlib import Path
from nonebot import Driver
import nonebot
@@ -28,7 +27,7 @@ driver: Driver = nonebot.get_driver()
@driver.on_startup
def _():
async def _():
"""
初始化数据
"""
@@ -5,7 +5,6 @@ from utils.manager import (
plugins2settings_manager,
plugins2block_manager,
plugins_manager,
resources_manager
)
from services.log import logger
from utils.utils import get_matchers
@@ -1,3 +1,5 @@
import asyncio
from datetime import datetime, timedelta
from pathlib import Path
from ruamel.yaml import round_trip_load, round_trip_dump, YAML
from utils.manager import admin_manager, plugins_manager
@@ -5,8 +7,8 @@ from configs.config import Config
from services.log import logger
from utils.text_utils import prompt2cn
from utils.utils import get_matchers
from utils.utils import scheduler
from ruamel import yaml
import nonebot
_yaml = YAML(typ="safe")
@@ -21,23 +23,39 @@ def init_plugins_config(data_path):
_data = {}
if plugins2config_file.exists():
_data = _yaml.load(open(plugins2config_file, "r", encoding="utf8"))
_matchers = get_matchers()
_matchers = get_matchers(True)
# 优先使用 metadata 数据
for matcher in _matchers:
_plugin = nonebot.plugin.get_plugin(matcher.plugin_name)
_plugin = matcher.plugin
if not _plugin:
continue
metadata = _plugin.metadata
try:
_module = _plugin.module
except AttributeError:
continue
try:
plugin_version = _module.__getattribute__("__plugin_version__")
except AttributeError:
plugin_version = None
try:
plugin_configs = _module.__getattribute__("__plugin_configs__")
except AttributeError:
continue
# 插件配置版本更新或为Version为None或不在存储配置内
if (
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 and metadata.config:
plugin_configs = {}
for key, value in metadata.config.__fields__.items():
plugin_configs[key.upper()] = {
"value": value.default,
"default_value": value.default
}
else:
try:
plugin_configs = _module.__getattribute__("__plugin_configs__")
except AttributeError:
continue
# 插件配置版本更新或为Version为None或不在存储配置内,当使用metadata时,必定更新
if isinstance(plugin_version, str) or (
plugin_version is None
or (
_data.get(matcher.plugin_name)
@@ -91,78 +109,96 @@ def init_plugins_config(data_path):
round_trip_dump(
_data, wf, indent=2, Dumper=yaml.RoundTripDumper, allow_unicode=True
)
# 再开始读取用户配置
user_config_file = Path() / "configs" / "config.yaml"
_data = {}
_tmp_data = {}
if user_config_file.exists():
with open(user_config_file, "r", encoding="utf8") as f:
_data = _yaml.load(f)
# 数据替换
for plugin in Config.keys():
_tmp_data[plugin] = {}
for k in Config[plugin].keys():
try:
if _data.get(plugin) and k in _data[plugin].keys():
Config.set_config(plugin, k, _data[plugin][k])
if level2module := Config.get_level2module(plugin, k):
try:
admin_manager.set_admin_level(
level2module, _data[plugin][k]
)
except KeyError:
logger.warning(
f"{level2module} 设置权限等级失败:{_data[plugin][k]}"
)
_tmp_data[plugin][k] = Config.get_config(plugin, k)
except AttributeError as e:
raise AttributeError(
f"{e}\n" + prompt2cn("可能为config.yaml配置文件填写不规范", 46)
)
Config.save()
temp_file = Path() / "configs" / "temp_config.yaml"
try:
with open(temp_file, "w", encoding="utf8") as wf:
yaml.dump(
_tmp_data, wf, Dumper=yaml.RoundTripDumper, allow_unicode=True
)
with open(temp_file, "r", encoding="utf8") as rf:
_data = round_trip_load(rf)
# 添加注释
for plugin in _data.keys():
rst = ""
plugin_name = None
try:
plugin_data = Config.get(plugin)
for x in list(Config.get(plugin).keys()):
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"
# )
def _replace_config():
"""
说明:
定时任务加载的配置读取替换
"""
# 再开始读取用户配置
user_config_file = Path() / "configs" / "config.yaml"
_data = {}
_tmp_data = {}
if user_config_file.exists():
with open(user_config_file, "r", encoding="utf8") as f:
_data = _yaml.load(f)
# 数据替换
for plugin in Config.keys():
_tmp_data[plugin] = {}
for k in Config[plugin].keys():
try:
if _data.get(plugin) and k in _data[plugin].keys():
Config.set_config(plugin, k, _data[plugin][k])
if level2module := Config.get_level2module(plugin, k):
try:
_x = plugin_data[x].get("name")
if _x:
plugin_name = _x
except AttributeError:
pass
except (KeyError, AttributeError):
plugin_name = None
if not plugin_name:
admin_manager.set_admin_level(
level2module, _data[plugin][k]
)
except KeyError:
logger.warning(
f"{level2module} 设置权限等级失败:{_data[plugin][k]}"
)
_tmp_data[plugin][k] = Config.get_config(plugin, k)
except AttributeError as e:
raise AttributeError(
f"{e}\n" + prompt2cn("可能为config.yaml配置文件填写不规范", 46)
)
Config.save()
temp_file = Path() / "configs" / "temp_config.yaml"
try:
with open(temp_file, "w", encoding="utf8") as wf:
yaml.dump(
_tmp_data, wf, Dumper=yaml.RoundTripDumper, allow_unicode=True
)
with open(temp_file, "r", encoding="utf8") as rf:
_data = round_trip_load(rf)
# 添加注释
for plugin in _data.keys():
rst = ""
plugin_name = None
try:
plugin_data = Config.get(plugin)
for x in list(Config.get(plugin).keys()):
try:
plugin_name = plugins_manager.get(plugin)["plugin_name"]
except (AttributeError, TypeError):
plugin_name = plugin
plugin_name = (
plugin_name.replace("[Hidden]", "")
.replace("[Superuser]", "")
.replace("[Admin]", "")
.strip()
)
rst += plugin_name + "\n"
for k in _data[plugin].keys():
rst += f'{k}: {Config[plugin][k]["help"]}' + "\n"
_data[plugin].yaml_set_start_comment(rst[:-1], indent=2)
with open(Path() / "configs" / "config.yaml", "w", encoding="utf8") as wf:
round_trip_dump(
_data, wf, Dumper=yaml.RoundTripDumper, allow_unicode=True
)
except Exception as e:
logger.error(f"生成简易配置注释错误 {type(e)}:{e}")
if temp_file.exists():
temp_file.unlink()
_x = plugin_data[x].get("name")
if _x:
plugin_name = _x
except AttributeError:
pass
except (KeyError, AttributeError):
plugin_name = None
if not plugin_name:
try:
plugin_name = plugins_manager.get(plugin)["plugin_name"]
except (AttributeError, TypeError):
plugin_name = plugin
plugin_name = (
plugin_name.replace("[Hidden]", "")
.replace("[Superuser]", "")
.replace("[Admin]", "")
.strip()
)
rst += plugin_name + "\n"
for k in _data[plugin].keys():
rst += f'{k}: {Config[plugin][k]["help"]}' + "\n"
_data[plugin].yaml_set_start_comment(rst[:-1], indent=2)
with open(Path() / "configs" / "config.yaml", "w", encoding="utf8") as wf:
round_trip_dump(
_data, wf, Dumper=yaml.RoundTripDumper, allow_unicode=True
)
except Exception as e:
logger.error(f"生成简易配置注释错误 {type(e)}:{e}")
if temp_file.exists():
temp_file.unlink()
@@ -22,9 +22,12 @@ def init_plugins_data(data_path):
_data = {}
if plugin2data_file.exists():
_data = json.load(open(plugin2data_file, "r", encoding="utf8"))
_matchers = get_matchers()
_matchers = get_matchers(True)
for matcher in _matchers:
_plugin = nonebot.plugin.get_plugin(matcher.plugin_name)
_plugin = matcher.plugin
if not _plugin:
continue
metadata = _plugin.metadata
try:
_module = _plugin.module
except AttributeError:
@@ -40,18 +43,28 @@ def init_plugins_data(data_path):
matcher.plugin_name, "version", plugin_data.get("version")
)
else:
try:
plugin_version = _module.__getattribute__("__plugin_version__")
except AttributeError:
plugin_version = None
try:
plugin_name = _module.__getattribute__("__zx_plugin_name__")
except AttributeError:
plugin_name = matcher.plugin_name
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_author = _module.__getattribute__("__plugin_author__")
except AttributeError:
plugin_author = None
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():
@@ -61,7 +74,8 @@ def init_plugins_data(data_path):
author=plugin_author,
version=plugin_version,
)
elif plugins_manager[matcher.plugin_name]["version"] is None or (
# 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"]
):
@@ -26,18 +26,8 @@ def init_plugins_resources():
else:
path = Path(_module.__getattribute__("__file__")).parent
for resource in resources.keys():
resources_manager.add_resource(matcher.plugin_name, path / resource, resources[resource])
resources_manager.add_resource(
matcher.plugin_name, path / resource, resources[resource]
)
resources_manager.save()
resources_manager.start_move()
@@ -16,63 +16,77 @@ def init_plugins_settings(data_path: str):
"""
plugins2settings_file = data_path / "configs" / "plugins2settings.yaml"
plugins2settings_file.parent.mkdir(exist_ok=True, parents=True)
_matchers = get_matchers()
_matchers = get_matchers(True)
_tmp_module = {}
_tmp = []
for x in plugins2settings_manager.keys():
try:
_plugin = nonebot.plugin.get_plugin(x)
_module = _plugin.module
plugin_name = _module.__getattribute__("__zx_plugin_name__")
metadata = _plugin.metadata
plugin_name = (
metadata.name
if metadata
else _module.__getattribute__("__zx_plugin_name__")
)
_tmp_module[x] = plugin_name
except (KeyError, AttributeError) as e:
logger.warning(f"配置文件 模块:{x} 获取 plugin_name 失败...{e}")
_tmp_module[x] = ""
for matcher in _matchers:
if matcher.plugin_name not in plugins2settings_manager.keys():
_plugin = nonebot.plugin.get_plugin(matcher.plugin_name)
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:
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
_module = _plugin.module
except AttributeError:
if matcher.plugin_name not in _tmp:
logger.warning(
f"获取插件 {matcher.plugin_name} __zx_plugin_name__ 失败...,插件控制未加载."
)
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:
_tmp_module[matcher.plugin_name] = plugin_name
plugin_settings = _module.__getattribute__(
"__plugin_settings__"
)
if plugin_settings.get('cost_gold') is None:
plugin_settings['cost_gold'] = 0
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"]
@@ -90,9 +104,7 @@ def init_plugins_settings(data_path: str):
)
else:
try:
plugin_type = _module.__getattribute__(
"__plugin_type__"
)
plugin_type = _module.__getattribute__("__plugin_type__")
except AttributeError:
plugin_type = ("normal",)
if plugin_settings and matcher.plugin_name:
@@ -101,8 +113,8 @@ def init_plugins_settings(data_path: str):
plugin_type=plugin_type,
**plugin_settings,
)
except AttributeError:
pass
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:
+21 -26
View File
@@ -1,3 +1,5 @@
import random
from asyncpg.exceptions import (
DuplicateColumnError,
UndefinedColumnError,
@@ -13,6 +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 scheduler
import nonebot
@@ -108,6 +111,18 @@ async def _():
"ALTER TABLE genshin ADD bind_group Integer;",
"genshin"
), # 新增原神群号绑定字段
(
"ALTER TABLE genshin ADD login_ticket VARCHAR(255) DEFAULT '';",
"genshin"
), # 新增米游社login_ticket绑定字段
(
"ALTER TABLE genshin ADD stuid VARCHAR(255) DEFAULT '';",
"genshin"
), # 新增米游社stuid绑定字段
(
"ALTER TABLE genshin ADD stoken VARCHAR(255) DEFAULT '';",
"genshin"
), # 新增米游社stoken绑定字段
(
"ALTER TABLE chat_history ADD plain_text Text;",
"chat_history"
@@ -121,10 +136,13 @@ async def _():
"goods_info"
), # 新增纯文本
]
for sql in sql_str:
for sql in sql_str + GDict.get('run_sql', []):
try:
flag = sql[1]
sql = sql[0]
if isinstance(sql, str):
flag = f'{random.randint(1, 10000)}'
else:
flag = sql[1]
sql = sql[0]
query = db.text(sql)
await db.first(query)
logger.info(f"完成sql操作:{sql}")
@@ -136,29 +154,6 @@ async def _():
# bag_user 将文本转为字典格式
await __database_script(_flag)
# 完成后
end_sql_str = [
# "ALTER TABLE bag_users DROP COLUMN props;" # 删除 bag_users 的 props 字段(还不到时候)
]
for sql in end_sql_str:
try:
query = db.text(sql)
await db.first(query)
logger.info(f"完成执行sql操作:{sql}")
except (DuplicateColumnError, UndefinedColumnError):
pass
except PostgresSyntaxError:
logger.error(f"语法错误:执行sql失败:{sql}")
# str2json_sql = ["alter table bag_users alter COLUMN props type json USING props::json;"] # 字段类型替换
# rename_sql = 'alter table {} rename {} to {};' # 字段更名
# for sql in str2json_sql:
# try:
# query = db.text(sql)
# await db.first(query)
# except DuplicateColumnError:
# pass
@driver.on_bot_connect
async def _(bot: Bot):
+11 -10
View File
@@ -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,7 +110,8 @@ class ConfigsManager:
):
self._data[module][key]["value"] = value
self._simple_data[module][key] = value
self.save(save_simple_data = save_simple_data)
if auto_save:
self.save(save_simple_data=save_simple_data)
def set_help(self, module: str, key: str, help_: str):
"""
@@ -200,13 +202,12 @@ class ConfigsManager:
重新加载配置文件
"""
_yaml = YAML()
temp_file = Path() / "configs" / "config.yaml"
if temp_file.exists():
with open(temp_file, "r", encoding="utf8") as f:
temp = _yaml.load(f)
for key in temp.keys():
for k in temp[key].keys():
self._data[key][k]["value"] = temp[key][k]
if self._simple_file.exists():
with open(self._simple_file, "r", encoding="utf8") as f:
self._simple_data = _yaml.load(f)
for key in self._simple_data.keys():
for k in self._simple_data[key].keys():
self._data[key][k]["value"] = self._simple_data[key][k]
self.save()
def get_admin_level_data(self):
Binary file not shown.

After

Width:  |  Height:  |  Size: 694 KiB

BIN
View File
Binary file not shown.

Before

Width:  |  Height:  |  Size: 694 KiB

After

Width:  |  Height:  |  Size: 326 KiB

+33 -24
View File
@@ -1,8 +1,9 @@
from nonebot import on_command
from nonebot import on_command, on_regex
from nonebot.typing import T_State
from nonebot.adapters.onebot.v11 import Bot, MessageEvent, GroupMessageEvent, Message
from utils.message_builder import at
from utils.image_utils import text2image
from utils.message_builder import image
from .data_source import (
add_live_sub,
delete_sub,
@@ -14,12 +15,13 @@ 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
from typing import Optional, Tuple, Any
from services.log import logger
from nonebot import Driver
from nonebot.params import CommandArg, ArgStr
from nonebot.params import CommandArg, ArgStr, RegexGroup
import nonebot
__zx_plugin_name__ = "B站订阅"
@@ -66,8 +68,8 @@ __plugin_configs__ = {
}
add_sub = on_command("添加订阅", priority=5, block=True)
del_sub = on_command("删除订阅", priority=5, block=True)
show_sub_info = on_command("查看订阅", priority=5, block=True)
del_sub = on_regex(r"^删除订阅(\d+)$", priority=5, block=True)
show_sub_info = on_regex("^查看订阅$", priority=5, block=True)
driver: Driver = nonebot.get_driver()
@@ -156,12 +158,10 @@ async def _(
@del_sub.handle()
async def _(event: MessageEvent, arg: Message = CommandArg()):
msg = arg.extract_plain_text().strip()
if not is_number(msg):
await del_sub.finish("Id必须为数字!", at_sender=True)
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}"
)
@@ -207,7 +207,15 @@ async def _(event: MessageEvent):
live_rst = (
"该群目前没有任何订阅..." if isinstance(event, GroupMessageEvent) else "您目前没有任何订阅..."
)
await show_sub_info.send(live_rst + up_rst + season_rst)
await show_sub_info.send(
image(
b64=(
await text2image(
live_rst + up_rst + season_rst, padding=10, color="#f9f6f2"
)
).pic2bs4()
)
)
# 推送
@@ -219,18 +227,18 @@ async def _():
bot = get_bot()
sub = None
if bot:
try:
await sub_manager.reload_sub_data()
sub = await sub_manager.random_sub_data()
if sub:
logger.debug(f"Bilibili订阅开始检测:{sub.sub_id}")
rst = await get_sub_status(sub.sub_id, sub.sub_type)
# try:
await sub_manager.reload_sub_data()
sub = await sub_manager.random_sub_data()
if sub:
logger.debug(f"Bilibili订阅开始检测:{sub.sub_id}")
rst = await get_sub_status(sub.sub_id, sub.sub_type)
await send_sub_msg(rst, sub, bot)
if sub.sub_type == "live":
rst = await get_sub_status(sub.sub_id, "up")
await send_sub_msg(rst, sub, bot)
if sub.sub_type == "live":
rst = await get_sub_status(sub.sub_id, "up")
await send_sub_msg(rst, sub, bot)
except Exception as e:
logger.error(f"B站订阅推送发生错误 sub_id:{sub.sub_id if sub else 0} {type(e)}:{e}")
# except Exception as e:
# logger.error(f"B站订阅推送发生错误 sub_id:{sub.sub_id if sub else 0} {type(e)}:{e}")
async def send_sub_msg(rst: str, sub: BilibiliSub, bot: Bot):
@@ -260,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:
+37 -35
View File
@@ -39,36 +39,35 @@ async def add_live_sub(live_id: int, sub_user: str) -> str:
:return:
"""
try:
async with db.transaction():
try:
"""bilibili_api.live库的LiveRoom类中get_room_info改为bilireq.live库的get_room_info_by_id方法"""
live_info = await get_room_info_by_id(live_id)
except ResponseCodeError:
return f"未找到房间号Id:{live_id} 的信息,请检查Id是否正确"
uid = live_info["uid"]
room_id = live_info["room_id"]
short_id = live_info["short_id"]
title = live_info["title"]
live_status = live_info["live_status"]
if await BilibiliSub.add_bilibili_sub(
room_id,
"live",
sub_user,
uid=uid,
live_short_id=short_id,
live_status=live_status,
):
await _get_up_status(live_id)
uname = (await BilibiliSub.get_sub(live_id)).uname
return (
"已成功订阅主播:\n"
f"\ttitle:{title}\n"
f"\tname: {uname}\n"
f"\tlive_id:{live_id}\n"
f"\tuid:{uid}"
try:
"""bilibili_api.live库的LiveRoom类中get_room_info改为bilireq.live库的get_room_info_by_id方法"""
live_info = await get_room_info_by_id(live_id)
except ResponseCodeError:
return f"未找到房间号Id:{live_id} 的信息,请检查Id是否正确"
uid = live_info["uid"]
room_id = live_info["room_id"]
short_id = live_info["short_id"]
title = live_info["title"]
live_status = live_info["live_status"]
if await BilibiliSub.add_bilibili_sub(
room_id,
"live",
sub_user,
uid=uid,
live_short_id=short_id,
live_status=live_status,
):
await _get_up_status(room_id)
uname = (await BilibiliSub.get_sub(room_id)).uname
return (
"已成功订阅主播:\n"
f"\ttitle:{title}\n"
f"\tname: {uname}\n"
f"\tlive_id:{room_id}\n"
f"\tuid:{uid}"
)
else:
return "添加订阅失败..."
else:
return "添加订阅失败..."
except Exception as e:
logger.error(f"订阅主播live_id:{live_id} 发生了错误 {type(e)}:{e}")
return "添加订阅失败..."
@@ -209,7 +208,9 @@ async def get_sub_status(id_: int, sub_type: str) -> Optional[str]:
elif sub_type == "season":
return await _get_season_status(id_)
except ResponseCodeError:
return f"Id:{id_} 获取信息失败...请检查订阅Id是否存在或稍后再试..."
logger.error(f"Id:{id_} 获取信息失败...请检查订阅Id是否存在或稍后再试...")
return None
# return f"Id:{id_} 获取信息失败...请检查订阅Id是否存在或稍后再试..."
# except Exception as e:
# logger.error(f"获取订阅状态发生预料之外的错误 id_:{id_} {type(e)}:{e}")
# return "发生了预料之外的错误..请稍后再试或联系管理员....."
@@ -243,7 +244,7 @@ async def _get_live_status(id_: int) -> Optional[str]:
async def _get_up_status(id_: int) -> Optional[str]:
"""
获取用户投稿状态
:param id_: 用户 id
:param id_: 订阅 id
:return:
"""
_user = await BilibiliSub.get_sub(id_)
@@ -251,12 +252,13 @@ async def _get_up_status(id_: int) -> Optional[str]:
user_info = await get_user_info(_user.uid)
uname = user_info["name"]
"""bilibili_api.user库中User类的get_videos改为bilireq.user库的get_videos方法"""
video_info = await get_videos(id_)
video_info = await get_videos(_user.uid)
latest_video_created = 0
video = None
dividing_line = "\n-------------\n"
if _user.uname != uname:
await BilibiliSub.update_sub_info(id_, uname=uname)
dynamic_img, dynamic_upload_time, link = await get_user_dynamic(id_, _user)
dynamic_img, dynamic_upload_time, link = await get_user_dynamic(_user.uid, _user)
if video_info["list"].get("vlist"):
video = video_info["list"]["vlist"][0]
latest_video_created = video["created"]
@@ -270,7 +272,7 @@ async def _get_up_status(id_: int) -> Optional[str]:
and video
and _user.latest_video_created < latest_video_created
):
rst = rst + "-------------\n" if rst else rst
rst = rst + dividing_line if rst else rst
await BilibiliSub.update_sub_info(
id_, latest_video_created=latest_video_created
)
@@ -281,7 +283,7 @@ async def _get_up_status(id_: int) -> Optional[str]:
f'Bvid:{video["bvid"]}\n'
f'直链:https://www.bilibili.com/video/{video["bvid"]}'
)
rst = None if rst == "-------------\n" else rst
rst = None if rst == dividing_line else rst
return rst
+36 -37
View File
@@ -65,44 +65,43 @@ class BilibiliSub(db.Model):
:param season_update_time: 番剧更新时间
"""
try:
async with db.transaction():
query = (
await cls.query.where(cls.sub_id == sub_id)
.with_for_update()
.gino.first()
query = (
await cls.query.where(cls.sub_id == sub_id)
.with_for_update()
.gino.first()
)
sub_user = sub_user if sub_user[-1] == "," else f"{sub_user},"
if query:
if sub_user not in query.sub_users:
sub_users = query.sub_users + sub_user
await query.update(sub_users=sub_users).apply()
else:
sub = await cls.create(
sub_id=sub_id, sub_type=sub_type, sub_users=sub_user
)
sub_user = sub_user if sub_user[-1] == "," else f"{sub_user},"
if query:
if sub_user not in query.sub_users:
sub_users = query.sub_users + sub_user
await query.update(sub_users=sub_users).apply()
else:
sub = await cls.create(
sub_id=sub_id, sub_type=sub_type, sub_users=sub_user
)
await sub.update(
live_short_id=live_short_id
if live_short_id
else sub.live_short_id,
live_status=live_status if live_status else sub.live_status,
dynamic_upload_time=dynamic_upload_time
if dynamic_upload_time
else sub.dynamic_upload_time,
uid=uid if uid else sub.uid,
uname=uname if uname else sub.uname,
latest_video_created=latest_video_created
if latest_video_created
else sub.latest_video_created,
season_update_time=season_update_time
if season_update_time
else sub.season_update_time,
season_current_episode=season_current_episode
if season_current_episode
else sub.season_current_episode,
season_id=season_id if season_id else sub.season_id,
season_name=season_name if season_name else sub.season_name,
).apply()
return True
await sub.update(
live_short_id=live_short_id
if live_short_id
else sub.live_short_id,
live_status=live_status if live_status else sub.live_status,
dynamic_upload_time=dynamic_upload_time
if dynamic_upload_time
else sub.dynamic_upload_time,
uid=uid if uid else sub.uid,
uname=uname if uname else sub.uname,
latest_video_created=latest_video_created
if latest_video_created
else sub.latest_video_created,
season_update_time=season_update_time
if season_update_time
else sub.season_update_time,
season_current_episode=season_current_episode
if season_current_episode
else sub.season_current_episode,
season_id=season_id if season_id else sub.season_id,
season_name=season_name if season_name else sub.season_name,
).apply()
return True
except Exception as e:
logger.info(f"bilibili_sub 添加订阅错误 {type(e)}: {e}")
return False
+1 -1
View File
@@ -9,7 +9,7 @@ import platform
# asyncio.set_event_loop_policy(asyncio.WindowsSelectorEventLoopPolicy())
url = "http://www.eclzz.zone/"
url = "http://www.eclzz.cool"
async def get_bt_info(keyword: str, page: int):
+5 -4
View File
@@ -36,14 +36,15 @@ __plugin_configs__ = {
},
}
coser = on_regex(r"^(\d)连?(cos|COS|coser|括丝)$", priority=5, block=True)
coser = on_regex(r"^(\d)?连?(cos|COS|coser|括丝)$", priority=5, block=True)
url = "https://api.iyk0.com/cos"
# 纯cos,较慢:https://picture.yinux.workers.dev
# 比较杂,有福利姬,较快:https://api.jrsgslb.cn/cos/url.php?return=img
url = "https://picture.yinux.workers.dev/"
@coser.handle()
async def _(bot: Bot, event: MessageEvent, reg_group: Tuple[Any, ...] = RegexGroup()):
async def _(event: MessageEvent, reg_group: Tuple[Any, ...] = RegexGroup()):
num = reg_group[0] or 1
for _ in range(int(num)):
try:
+26 -56
View File
@@ -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
View File
@@ -50,6 +50,7 @@ class UpEvent(BaseModel):
start_time: Optional[datetime] # 开始时间
end_time: Optional[datetime] # 结束时间
up_char: List[UpChar] # up对象
up_name: str = "" # up名称
TC = TypeVar("TC", bound="BaseData")
+17 -10
View File
@@ -350,48 +350,52 @@ class PrettyHandle(BaseHandle[PrettyData]):
char_img = ""
card_img = ""
up_chars = []
up_chars_name = []
up_cards = []
up_cards_name = []
soup = BeautifulSoup(result, "lxml")
heads = soup.find_all("span", {"class": "mw-headline"})
for head in heads:
if "时间" in head.text:
if "时间" in head.text or "期间" in head.text:
time = head.find_next("p").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("a", {"class": "image"}).find("img")[
char_img = head.find_next("center").find("img")[
"src"
]
lines = str(head.find_next("p").text).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 chars:
for char in set(chars): # list去重
star = char.count("★")
name = re.split(r"[()]", char)[-2].strip()
name = re.split(r"[【】]", char)[-2].strip()
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("a", {"class": "image"}).find("img")[
card_img = head.find_next("center").find("img")[
"src"
]
lines = str(head.find_next("p").text).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:
star = 3 if "SSR" in card else 2 if "SR" in card else 1
name = re.split(r"[()]", card)[-2].strip()
name = re.split(r"[【】]", card)[-2].strip()
up_cards.append(
UpChar(name=name, star=star, limited=False, zoom=70)
)
up_cards_name.append(name)
if start_time and end_time:
if start_time <= datetime.now() <= end_time:
self.UP_CHAR = UpEvent(
@@ -400,6 +404,7 @@ class PrettyHandle(BaseHandle[PrettyData]):
start_time=start_time,
end_time=end_time,
up_char=up_chars,
up_name=up_chars_name,
)
self.UP_CARD = UpEvent(
title=title,
@@ -407,6 +412,7 @@ class PrettyHandle(BaseHandle[PrettyData]):
start_time=start_time,
end_time=end_time,
up_char=up_cards,
up_name=up_cards_name,
)
self.dump_up_char()
logger.info(f"成功获取{self.game_name_cn}当前up信息...当前up池: {title}")
@@ -418,9 +424,10 @@ class PrettyHandle(BaseHandle[PrettyData]):
self.load_up_char()
if self.UP_CHAR and self.UP_CARD:
return Message(
Message.template("重载成功!\n当前UP池子:{}{:image}{:image}").format(
self.UP_CHAR.title,
Message.template("重载成功!\n当前UP池子:{}{:image}\n当前支援卡池子:{}{:image}").format(
self.UP_CHAR.up_name,
self.UP_CHAR.pool_img,
self.UP_CARD.up_name,
self.UP_CARD.pool_img,
)
)
+38 -19
View File
@@ -7,7 +7,7 @@ from utils.http_utils import AsyncHttpx
# 获取所有 Epic Game Store 促销游戏
# 方法参考:RSSHub /epicgames 路由
# https://github.com/DIYgod/RSSHub/blob/master/lib/routes/epicgames/index.js
# https://github.com/DIYgod/RSSHub/blob/master/lib/v2/epicgames/index.js
async def get_epic_game():
epic_url = "https://store-site-backend-static-ipv4.ak.epicgames.com/freeGamesPromotions?locale=zh-CN&country=CN&allowCountries=CN"
headers = {
@@ -39,7 +39,7 @@ async def get_epic_free(bot: Bot, type_event: str):
game_corp = game["seller"]["name"]
game_price = game["price"]["totalPrice"]["fmtPrice"]["originalPrice"]
# 赋初值以避免 local variable referenced before assignment
game_dev, game_pub, game_thumbnail = (None, None, None)
game_thumbnail, game_dev, game_pub = None, game_corp, game_corp
try:
game_promotions = game["promotions"]["promotionalOffers"]
upcoming_promotions = game["promotions"]["upcomingPromotionalOffers"]
@@ -95,25 +95,44 @@ async def get_epic_free(bot: Bot, type_event: str):
game_dev = pair["value"]
if pair["key"] == "publisherName":
game_pub = pair["value"]
# 如 game['customAttributes'] 未找到则均使用 game_corp 值
game_dev = game_dev if game_dev is not None else game_corp
game_pub = game_pub if game_pub is not None else game_corp
game_desp = game["description"]
end_date_iso = game["promotions"]["promotionalOffers"][0][
"promotionalOffers"
][0]["endDate"][:-1]
end_date = datetime.fromisoformat(end_date_iso).strftime(
"%b.%d %H:%M"
)
try:
end_date_iso = game["promotions"]["promotionalOffers"][0][
"promotionalOffers"
][0]["endDate"][:-1]
end_date = datetime.fromisoformat(end_date_iso).strftime(
"%b.%d %H:%M"
)
except IndexError:
end_date = '未知'
# API 返回不包含游戏商店 URL,此处自行拼接,可能出现少数游戏 404 请反馈
game_url_part = (
(game["productSlug"].replace("/home", ""))
if ("/home" in game["productSlug"])
else game["productSlug"]
)
game_url = "https://www.epicgames.com/store/zh-CN/p/{}".format(
game_url_part
)
if game.get("productSlug"):
game_url = "https://store.epicgames.com/zh-CN/p/{}".format(
game["productSlug"].replace("/home", "")
)
elif game.get("url"):
game_url = game["url"]
else:
slugs = (
[
x["pageSlug"]
for x in game.get("offerMappings", [])
if x.get("pageType") == "productHome"
]
+ [
x["pageSlug"]
for x in game.get("catalogNs", {}).get("mappings", [])
if x.get("pageType") == "productHome"
]
+ [
x["value"]
for x in game.get("customAttributes", [])
if "productSlug" in x.get("key")
]
)
game_url = "https://store.epicgames.com/zh-CN{}".format(
f"/p/{slugs[0]}" if len(slugs) else ""
)
if type_event == "Group":
_message = "[CQ:image,file={}]\n\nFREE now :: {} ({})\n{}\n此游戏由 {} 开发、{} 发行,将在 UTC 时间 {} 结束免费游玩,戳链接速度加入你的游戏库吧~\n{}\n".format(
game_thumbnail,
+6 -15
View File
@@ -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 -1
View File
@@ -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"{TEXT_PATH}/genshin_map_{rand}.png")
return rand
# 资源数量
@@ -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}")
# 下载资源类型数据
@@ -19,6 +19,9 @@ class Genshin(db.Model):
resin_remind = db.Column(db.Boolean(), default=False) # 树脂提醒
resin_recovery_time = db.Column(db.DateTime(timezone=True)) # 满树脂提醒日期
bind_group = db.Column(db.BigInteger())
login_ticket = db.Column(db.String(), default="")
stuid = db.Column(db.String(), default="")
stoken = db.Column(db.String(), default="")
_idx1 = db.Index("genshin_uid_idx1", "user_qq", "uid", unique=True)
@@ -386,3 +389,96 @@ class Genshin(db.Model):
for u in await cls.query.with_for_update().gino.all():
if u.today_query_uid:
await u.update(today_query_uid="").apply()
@classmethod
async def set_stuid(cls, uid: int, stuid: str) -> bool:
"""
说明:
设置stuid
参数:
:param uid: 原神uid
:param stuid: stuid
"""
query = cls.query.where(cls.uid == uid).with_for_update()
user = await query.gino.first()
if user:
await user.update(stuid=stuid).apply()
return True
return False
@classmethod
async def set_stoken(cls, uid: int, stoken: str) -> bool:
"""
说明:
设置stoken
参数:
:param uid: 原神uid
:param stoken: stoken
"""
query = cls.query.where(cls.uid == uid).with_for_update()
user = await query.gino.first()
if user:
await user.update(stoken=stoken).apply()
return True
return False
@classmethod
async def set_login_ticket(cls, uid: int, login_ticket: str) -> bool:
"""
说明:
设置login_ticket
参数:
:param uid: 原神uid
:param login_ticket: login_ticket
"""
query = cls.query.where(cls.uid == uid).with_for_update()
user = await query.gino.first()
if user:
await user.update(login_ticket=login_ticket).apply()
return True
return False
# 获取login_ticket
@classmethod
async def get_login_ticket(cls, uid: int) -> Optional[str]:
"""
说明:
获取login_ticket
参数:
:param uid: 原神uid
"""
query = cls.query.where(cls.uid == uid)
user = await query.gino.first()
if user:
return user.login_ticket
return None
# 获取stuid
@classmethod
async def get_stuid(cls, uid: int) -> Optional[str]:
"""
说明:
获取stuid
参数:
:param uid: 原神uid
"""
query = cls.query.where(cls.uid == uid)
user = await query.gino.first()
if user:
return user.stuid
return None
# 获取stoken
@classmethod
async def get_stoken(cls, uid: int) -> Optional[str]:
"""
说明:
获取stoken
参数:
:param uid: 原神uid
"""
query = cls.query.where(cls.uid == uid)
user = await query.gino.first()
if user:
return user.stoken
return None
+42 -3
View File
@@ -5,6 +5,8 @@ from .._models import Genshin
from services.log import logger
from nonebot.params import CommandArg, Command
from typing import Tuple
from utils.http_utils import AsyncHttpx
import json
__zx_plugin_name__ = "原神绑定"
@@ -39,6 +41,10 @@ bind = on_command(
unbind = on_command("原神解绑", priority=5, block=True)
web_Api = "https://api-takumi.mihoyo.com"
bbs_Cookie_url = "https://webapi.account.mihoyo.com/Api/cookie_accountinfo_by_loginticket?login_ticket={}"
bbs_Cookie_url2 = web_Api + "/auth/api/getMultiTokenByLoginTicket?login_ticket={}&token_types=3&uid={}"
@bind.handle()
async def _(event: MessageEvent, cmd: Tuple[str, ...] = Command(), arg: Message = CommandArg()):
@@ -64,9 +70,13 @@ async def _(event: MessageEvent, cmd: Tuple[str, ...] = Command(), arg: Message
_x = f"已成功为uid:{uid} 设置米游社id:{msg}"
else:
if not msg:
await bind.finish(
"私聊发送!!\n打开 https://bbs.mihoyo.com/ys/\n登录后按F12点击控制台输入document.cookie复制输出的内容即可"
)
await bind.finish("""私聊发送!!
1.以无痕模式打开浏览器(Edge请新建InPrivate窗口)
2.打开http://bbs.mihoyo.com/ys/并登陆
3.登陆后打开http://user.mihoyo.com/进行登陆
4.按下F12,打开控制台,输入以下命令:
var cookie=document.cookie;var ask=confirm('Cookie:'+cookie+'\\n\\nDo you want to copy the cookie to the clipboard?');if(ask==true){copy(cookie);msg=cookie}else{msg='Cancel'}
5.私聊发送:原神绑定cookie 刚刚复制的cookie""")
if isinstance(event, GroupMessageEvent):
await bind.finish("请立即撤回你的消息并私聊发送!")
uid = await Genshin.get_user_uid(event.user_id)
@@ -77,6 +87,35 @@ async def _(event: MessageEvent, cmd: Tuple[str, ...] = Command(), arg: Message
if msg.endswith('"') or msg.endswith("'"):
msg = msg[:-1]
await Genshin.set_cookie(uid, msg)
cookie = msg
# 用: 代替=, ,代替;
cookie = '{"' + cookie.replace('=', '": "').replace("; ", '","') + '"}'
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))
res.encoding = "utf-8"
data = json.loads(res.text)
print(data)
if "成功" in data["data"]["msg"]:
stuid = str(data["data"]["cookie_info"]["account_id"])
res = await AsyncHttpx.get(url=bbs_Cookie_url2.format(
login_ticket, stuid))
res.encoding = "utf-8"
data = json.loads(res.text)
stoken = data["data"]["list"][0]["token"]
# await Genshin.set_cookie(uid, cookie)
await Genshin.set_stoken(uid, stoken)
await Genshin.set_stuid(uid, stuid)
await Genshin.set_login_ticket(uid, login_ticket)
# except Exception as e:
# await bind.finish("获取登陆信息失败,请检查cookie是否正确或更新cookie")
elif data["data"]["msg"] == "登录信息已失效,请重新登录":
await bind.finish("登录信息失效,请重新获取最新cookie进行绑定")
_x = f"已成功为uid:{uid} 设置cookie"
if isinstance(event, GroupMessageEvent):
await Genshin.set_bind_group(uid, event.group_id)
@@ -1,4 +1,5 @@
from .data_source import get_sign_reward_list, genshin_sign
from ..mihoyobbs_sign import mihoyobbs_sign
from nonebot.adapters.onebot.v11 import MessageEvent, GroupMessageEvent
from nonebot import on_command
from services.log import logger
@@ -14,15 +15,15 @@ __plugin_usage__ = """
usage:
米游社原神签到,需要uid以及cookie
且在第二天自动排序签到时间
# 不听,就要手动签到!(使用命令 “原神我硬签 or 米游社我硬签”
# 不听,就要手动签到!(使用命令 “原神我硬签
指令:
开/关原神自动签到
原神我硬签
""".strip()
__plugin_des__ = "原神懒人签到"
__plugin_cmd__ = ["开启/关闭原神自动签到", "原神我硬签"]
__plugin_cmd__ = ["开启/关闭原神自动签到", "原神我硬签", "查看我的cookie"]
__plugin_type__ = ("原神相关",)
__plugin_version__ = 0.1
__plugin_version__ = 0.2
__plugin_author__ = "HibiKier"
__plugin_settings__ = {
"level": 5,
@@ -33,7 +34,7 @@ __plugin_settings__ = {
genshin_matcher = on_command(
"开原神自动签到", aliases={"关原神自动签到", "原神我硬签"}, priority=5, block=True
"开原神自动签到", aliases={"关原神自动签到", "原神我硬签", "查看我的cookie"}, priority=5, block=True
)
@@ -41,17 +42,25 @@ genshin_matcher = on_command(
async def _(event: MessageEvent, cmd: Tuple[str, ...] = Command()):
cmd = cmd[0]
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!")
if "account_id" not in await Genshin.get_user_cookie(uid, True):
await genshin_matcher.finish("请更新cookie!")
# if "account_id" not in await Genshin.get_user_cookie(uid, True):
# 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)
logger.info(
f"(USER {event.user_id}, "
f"GROUP {event.group_id if isinstance(event, GroupMessageEvent) else 'private'}) UID:{uid} 原神签到"
)
logger.info(msg)
# 硬签,移除定时任务
try:
for i in range(3):
@@ -63,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下一次签到时间为:{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(
@@ -1,9 +1,14 @@
from utils.http_utils import AsyncHttpx
from configs.config import Config
from services.log import logger
from .._utils import random_hex, get_old_ds
from ..mihoyobbs_sign.setting import *
from .._models import Genshin
from typing import Optional, Dict
import hashlib
import random
import string
import uuid
import time
async def genshin_sign(uid: int) -> Optional[str]:
@@ -16,22 +21,62 @@ async def genshin_sign(uid: int) -> Optional[str]:
return "签到失败..."
status = data["message"]
if status == "OK":
sign_info = await _get_sign_info(uid)
if sign_info:
sign_info = sign_info["data"]
sign_list = await get_sign_reward_list()
get_reward = sign_list["data"]["awards"][
int(sign_info["total_sign_day"]) - 1
]["name"]
reward_num = sign_list["data"]["awards"][
int(sign_info["total_sign_day"]) - 1
]["cnt"]
get_im = f"本次签到获得:{get_reward}x{reward_num}"
if status == "OK" and sign_info["is_sign"]:
return f"\n原神签到成功!\n{get_im}\n本月漏签次数:{sign_info['sign_cnt_missed']}"
try:
sign_info = await _get_sign_info(uid)
if sign_info:
sign_info = sign_info["data"]
sign_list = await get_sign_reward_list()
get_reward = sign_list["data"]["awards"][
int(sign_info["total_sign_day"]) - 1
]["name"]
reward_num = sign_list["data"]["awards"][
int(sign_info["total_sign_day"]) - 1
]["cnt"]
get_im = f"本次签到获得:{get_reward}x{reward_num}"
logger.info("get_im:" + get_im + "\nsign_info:" + str(sign_info))
if status == "OK" and sign_info["is_sign"]:
return f"原神签到成功!\n{get_im}\n本月漏签次数:{sign_info['sign_cnt_missed']}"
except Exception as e:
logger.error(f"原神签到发生错误 UID:{str(data)}")
return f"原神签到发生错误: {str(data)}"
else:
return status
return None
if data["data"]["risk_code"] == 375:
return "原神签到失败\n账号可能被风控,请前往米游社手动签到!"
return str(data)
# 获取请求Header里的DS 当web为true则生成网页端的DS
def get_ds(web: bool) -> str:
if web:
n = mihoyobbs_Salt_web
else:
n = mihoyobbs_Salt
i = str(timestamp())
r = random_text(6)
c = md5("salt=" + n + "&t=" + i + "&r=" + r)
return f"{i},{r},{c}"
# 时间戳
def timestamp() -> int:
return int(time.time())
def random_text(num: int) -> str:
return ''.join(random.sample(string.ascii_lowercase + string.digits, num))
def md5(text: str) -> str:
md5 = hashlib.md5()
md5.update(text.encode())
return md5.hexdigest()
# 生成一个device id
def get_device_id(cookie) -> str:
return str(uuid.uuid3(uuid.NAMESPACE_URL, cookie)).replace(
'-', '').upper()
async def _sign(uid: int, server_id: str = "cn_gf01") -> Optional[Dict[str, str]]:
@@ -43,20 +88,16 @@ async def _sign(uid: int, server_id: str = "cn_gf01") -> Optional[Dict[str, str]
if str(uid)[0] == "5":
server_id = "cn_qd01"
try:
cookie = await Genshin.get_user_cookie(uid, True)
headers['DS'] = get_ds(web=True)
headers['Referer'] = 'https://webstatic.mihoyo.com/bbs/event/signin-ys/index.html?bbs_auth_required=true' \
f'&act_id={genshin_Act_id}&utm_source=bbs&utm_medium=mys&utm_campaign=icon'
headers['Cookie'] = cookie
headers['x-rpc-device_id'] = get_device_id(cookie)
req = await AsyncHttpx.post(
url="https://api-takumi.mihoyo.com/event/bbs_sign_reward/sign",
headers={
"User_Agent": "Mozilla/5.0 (Linux; Android 10; MIX 2 Build/QKQ1.190825.002; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/83.0.4103.101 Mobile Safari/537.36 miHoYoBBS/2.3.0",
"Cookie": await Genshin.get_user_cookie(int(uid), True),
"x-rpc-device_id": random_hex(32),
"Origin": "https://webstatic.mihoyo.com",
"X_Requested_With": "com.mihoyo.hyperion",
"DS": get_old_ds(),
"x-rpc-client_type": "5",
"Referer": "https://webstatic.mihoyo.com/bbs/event/signin-ys/index.html?bbs_auth_required=true&act_id=e202009291139501&utm_source=bbs&utm_medium=mys&utm_campaign=icon",
"x-rpc-app_version": "2.3.0",
},
json={"act_id": "e202009291139501", "uid": uid, "region": server_id},
url=genshin_Signurl,
headers=headers,
json={"act_id": genshin_Act_id, "uid": uid, "region": server_id},
)
return req.json()
except Exception as e:
@@ -1,4 +1,5 @@
from .data_source import genshin_sign
from ..mihoyobbs_sign import mihoyobbs_sign
from models.group_member_info import GroupInfoUser
from utils.message_builder import at
from services.log import logger
@@ -57,6 +58,11 @@ async def _sign(user_id: int, uid: int, count: int):
:param uid: uid
:param count: 执行次数
"""
try:
return_data = await mihoyobbs_sign(user_id)
except Exception as e:
logger.error(f"mihoyobbs_sign error:{e}")
return_data = "米游社签到失败,请尝试发送'米游社签到'进行手动签到"
if count < 3:
try:
msg = await genshin_sign(uid)
@@ -101,6 +107,7 @@ async def _sign(user_id: int, uid: int, count: int):
bot = get_bot()
if bot:
if user_id in [x["user_id"] for x in await bot.get_friend_list()]:
await bot.send_private_msg(user_id=user_id, message=return_data)
await bot.send_private_msg(user_id=user_id, message=msg)
else:
if not (group_id := await Genshin.get_bind_group(uid)):
@@ -0,0 +1,81 @@
from nonebot.adapters.onebot.v11 import MessageEvent
from nonebot import on_command
from services.log import logger
# from .init_task import add_job, scheduler, _sign
# from apscheduler.jobstores.base import JobLookupError
from .._models import Genshin
from nonebot.params import Command
from typing import Tuple
from .mihoyobbs import *
__zx_plugin_name__ = "米游社自动签到"
__plugin_usage__ = """
usage:
发送'米游社签到'或绑定原神自动签到
即可手动/自动进行米游社签到
(若启用了原神自动签到会在签到原神同时完成米游币领取)
--> 每天白嫖90-110米游币不香吗
注:需要重新绑定原神cookie!!!
遇到问题请提issue或@作者
""".strip()
__plugin_des__ = "米游社自动签到任务"
__plugin_cmd__ = ["米游社签到", "米游社我硬签"]
__plugin_type__ = ("原神相关",)
__plugin_version__ = 0.1
__plugin_author__ = "HDU_Nbsp"
__plugin_settings__ = {
"level": 5,
"default_status": True,
"limit_superuser": False,
"cmd": ["原神签到"],
}
mihoyobbs_matcher = on_command(
"米游社签到", aliases={"米游社我硬签"}, priority=5, block=True
)
@mihoyobbs_matcher.handle()
async def _(event: MessageEvent, cmd: Tuple[str, ...] = Command()):
await mihoyobbs_matcher.send("提交米游社签到申请", at_sender=True)
return_data = await mihoyobbs_sign(event.user_id)
if return_data:
await mihoyobbs_matcher.finish(return_data, at_sender=True)
else:
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)
bbs = mihoyobbs.Mihoyobbs(stuid=stuid, stoken=stoken, cookie=cookie)
await bbs.init()
return_data = ""
if bbs.Task_do["bbs_Sign"] and bbs.Task_do["bbs_Read_posts"] and bbs.Task_do["bbs_Like_posts"] and \
bbs.Task_do["bbs_Share"]:
return_data += f"今天的米游社签到任务已经全部完成了!\n" \
f"一共获得{mihoyobbs.today_have_get_coins}个米游币\n目前有{mihoyobbs.Have_coins}个米游币"
logger.info(f"今天已经全部完成了!一共获得{mihoyobbs.today_have_get_coins}个米游币,目前有{mihoyobbs.Have_coins}个米游币")
else:
i = 0
print("开始签到")
print(mihoyobbs.today_have_get_coins)
while mihoyobbs.today_get_coins != 0 and i < 3:
# if i > 0:
await bbs.refresh_list()
await bbs.signing()
await bbs.read_posts()
await bbs.like_posts()
await bbs.share_post()
await bbs.get_tasks_list()
i += 1
return_data += "\n" + f"今天已经获得{mihoyobbs.today_have_get_coins}个米游币\n" \
f"还能获得{mihoyobbs.today_get_coins}个米游币\n目前有{mihoyobbs.Have_coins}个米游币"
logger.info(f"今天已经获得{mihoyobbs.today_have_get_coins}个米游币,"
f"还能获得{mihoyobbs.today_get_coins}个米游币,目前有{mihoyobbs.Have_coins}个米游币")
return return_data
@@ -0,0 +1,6 @@
class CookieError(Exception):
def __init__(self, info):
self.info = info
def __str__(self):
return repr(self.info)
@@ -0,0 +1,193 @@
from services.log import logger
from .error import CookieError
from utils.http_utils import AsyncHttpx
from .setting import *
from .tools import *
import json
today_get_coins = 0
today_have_get_coins = 0 # 这个变量以后可能会用上,先留着了
Have_coins = 0
class Mihoyobbs:
def __init__(self, stuid: str, stoken: str, cookie: str) -> None:
self.postsList = None
self.headers = {
"DS": get_ds(web=False),
"cookie": f'stuid={stuid};stoken={stoken}',
"x-rpc-client_type": mihoyobbs_Client_type,
"x-rpc-app_version": mihoyobbs_Version,
"x-rpc-sys_version": "6.0.1",
"x-rpc-channel": "miyousheluodi",
"x-rpc-device_id": get_device_id(cookie=cookie),
"x-rpc-device_name": random_text(random.randint(1, 10)),
"x-rpc-device_model": "Mi 10",
"Referer": "https://app.mihoyo.com",
"Host": "bbs-api.mihoyo.com",
"User-Agent": "okhttp/4.8.0"
}
self.Task_do = {
"bbs_Sign": False,
"bbs_Read_posts": False,
"bbs_Read_posts_num": 3,
"bbs_Like_posts": False,
"bbs_Like_posts_num": 5,
"bbs_Share": False
}
async def init(self):
await self.get_tasks_list()
# 如果这三个任务都做了就没必要获取帖子了
if self.Task_do["bbs_Read_posts"] and self.Task_do["bbs_Like_posts"] and self.Task_do["bbs_Share"]:
pass
else:
self.postsList = await self.get_list()
async def refresh_list(self) -> None:
self.postsList = await self.get_list()
# 获取任务列表,用来判断做了哪些任务
async def get_tasks_list(self):
global today_get_coins
global today_have_get_coins
global Have_coins
logger.info("正在获取任务列表")
req = await AsyncHttpx.get(url=bbs_Tasks_list, headers=self.headers)
data = req.json()
if "err" in data["message"] or data["retcode"] == -100:
logger.error("获取任务列表失败,你的cookie可能已过期,请重新设置cookie。")
raise CookieError('Cookie expires')
else:
today_get_coins = data["data"]["can_get_points"]
today_have_get_coins = data["data"]["already_received_points"]
Have_coins = data["data"]["total_points"]
# 如果当日可获取米游币数量为0直接判断全部任务都完成了
if today_get_coins == 0:
self.Task_do["bbs_Sign"] = True
self.Task_do["bbs_Read_posts"] = True
self.Task_do["bbs_Like_posts"] = True
self.Task_do["bbs_Share"] = True
else:
# 如果第0个大于或等于62则直接判定任务没做
if data["data"]["states"][0]["mission_id"] >= 62:
logger.info(f"今天可以获得{today_get_coins}个米游币")
pass
else:
logger.info(f"还有任务未完成,今天还能获得{today_get_coins}米游币")
for i in data["data"]["states"]:
# 58是讨论区签到
if i["mission_id"] == 58:
if i["is_get_award"]:
self.Task_do["bbs_Sign"] = True
# 59是看帖子
elif i["mission_id"] == 59:
if i["is_get_award"]:
self.Task_do["bbs_Read_posts"] = True
else:
self.Task_do["bbs_Read_posts_num"] -= i["happened_times"]
# 60是给帖子点赞
elif i["mission_id"] == 60:
if i["is_get_award"]:
self.Task_do["bbs_Like_posts"] = True
else:
self.Task_do["bbs_Like_posts_num"] -= i["happened_times"]
# 61是分享帖子
elif i["mission_id"] == 61:
if i["is_get_award"]:
self.Task_do["bbs_Share"] = True
# 分享帖子,是最后一个任务,到这里了下面都是一次性任务,直接跳出循环
break
# 获取要帖子列表
async def get_list(self) -> list:
temp_list = []
logger.info("正在获取帖子列表......")
req = await AsyncHttpx.get(url=bbs_List_url.format(mihoyobbs_List_Use[0]["forumId"]),
headers=self.headers)
data = req.json()["data"]["list"]
for n in range(5):
r_l = random.choice(data)
while r_l["post"]["subject"] in str(temp_list):
r_l = random.choice(data)
temp_list.append([r_l["post"]["post_id"], r_l["post"]["subject"]])
# temp_list.append([data["data"]["list"][n]["post"]["post_id"], data["data"]["list"][n]["post"]["subject"]])
logger.info("已获取{}个帖子".format(len(temp_list)))
return temp_list
# 进行签到操作
async def signing(self):
if self.Task_do["bbs_Sign"]:
logger.info("讨论区任务已经完成过了~")
else:
logger.info("正在签到......")
header = {}
header.update(self.headers)
for i in mihoyobbs_List_Use:
header["DS"] = get_ds2("", json.dumps({"gids": i["id"]}))
req = await AsyncHttpx.post(url=bbs_Sign_url, json={"gids": i["id"]}, headers=header)
data = req.json()
if "err" not in data["message"]:
logger.info(str(i["name"] + data["message"]))
time.sleep(random.randint(2, 8))
else:
logger.error("签到失败,你的cookie可能已过期,请重新设置cookie。")
raise CookieError('Cookie expires')
# 看帖子
async def read_posts(self):
if self.Task_do["bbs_Read_posts"]:
logger.info("看帖任务已经完成过了~")
else:
logger.info("正在看帖......")
for i in range(self.Task_do["bbs_Read_posts_num"]):
req = await AsyncHttpx.get(url=bbs_Detail_url.format(self.postsList[i][0]), headers=self.headers)
data = req.json()
if data["message"] == "OK":
logger.debug("看帖:{} 成功".format(self.postsList[i][1]))
time.sleep(random.randint(2, 8))
# 点赞
async def like_posts(self):
if self.Task_do["bbs_Like_posts"]:
logger.info("点赞任务已经完成过了~")
else:
logger.info("正在点赞......")
for i in range(self.Task_do["bbs_Like_posts_num"]):
req = await AsyncHttpx.post(url=bbs_Like_url, headers=self.headers,
json={"post_id": self.postsList[i][0], "is_cancel": False})
data = req.json()
if data["message"] == "OK":
logger.debug("点赞:{} 成功".format(self.postsList[i][1]))
# 判断取消点赞是否打开
# if config.config["mihoyobbs"]["un_like"] :
# time.sleep(random.randint(2, 8))
# req = httpx.post(url=bbs_Like_url, headers=self.headers,
# json={"post_id": self.postsList[i][0], "is_cancel": True})
# data = req.json()
# if data["message"] == "OK":
# logger.debug("取消点赞:{} 成功".format(self.postsList[i][1]))
time.sleep(random.randint(2, 8))
# 分享操作
async def share_post(self):
if self.Task_do["bbs_Share"]:
logger.info("分享任务已经完成过了~")
else:
logger.info("正在执行分享任务......")
for i in range(3):
req = await AsyncHttpx.get(url=bbs_Share_url.format(self.postsList[0][0]), headers=self.headers)
data = req.json()
if data["message"] == "OK":
logger.debug("分享:{} 成功".format(self.postsList[0][1]))
logger.info("分享任务执行成功......")
break
else:
logger.debug(f"分享任务执行失败,正在执行第{i + 2}次,共3次")
time.sleep(random.randint(2, 8))
time.sleep(random.randint(2, 8))
@@ -0,0 +1,124 @@
# 米游社的Salt
mihoyobbs_Salt = "z8DRIUjNDT7IT5IZXvrUAxyupA1peND9"
mihoyobbs_Salt2 = "t0qEgfub6cvueAPgR5m9aQWWVciEer7v"
mihoyobbs_Salt_web = "9nQiU3AV0rJSIBWgdynfoGMGKaklfbM7"
# 米游社的版本
mihoyobbs_Version = "2.34.1" # Slat和Version相互对应
# 米游社的客户端类型
mihoyobbs_Client_type = "2" # 1为ios 2为安卓
mihoyobbs_Client_type_web = "5" # 4为pc web 5为mobile web
# 米游社的分区列表
mihoyobbs_List = [{
"id": "1",
"forumId": "1",
"name": "崩坏3",
"url": "https://bbs.mihoyo.com/bh3/"
}, {
"id": "2",
"forumId": "26",
"name": "原神",
"url": "https://bbs.mihoyo.com/ys/"
}, {
"id": "3",
"forumId": "30",
"name": "崩坏2",
"url": "https://bbs.mihoyo.com/bh2/"
}, {
"id": "4",
"forumId": "37",
"name": "未定事件簿",
"url": "https://bbs.mihoyo.com/wd/"
}, {
"id": "5",
"forumId": "34",
"name": "大别野",
"url": "https://bbs.mihoyo.com/dby/"
}, {
"id": "6",
"forumId": "52",
"name": "崩坏:星穹铁道",
"url": "https://bbs.mihoyo.com/sr/"
}, {
"id": "8",
"forumId": "57",
"name": "绝区零",
"url": "https://bbs.mihoyo.com/zzz/"
}]
game_id2name = {
"bh2_cn": "崩坏2",
"bh3_cn": "崩坏3",
"nxx_cn": "未定事件簿",
"hk4e_cn": "原神",
}
# Config Load之后run里面进行列表的选择
mihoyobbs_List_Use = [{
"id": "2",
"forumId": "26",
"name": "原神",
"url": "https://bbs.mihoyo.com/ys/"
},
# 不玩原神可以把签到讨论区换为大别墅
# {
# "id": "5",
# "forumId": "34",
# "name": "大别野",
# "url": "https://bbs.mihoyo.com/dby/"
# }
]
# 游戏签到的请求头
headers = {
'Accept': 'application/json, text/plain, */*',
'DS': "",
'Origin': 'https://webstatic.mihoyo.com',
'x-rpc-app_version': mihoyobbs_Version,
'User-Agent': 'Mozilla/5.0 (Linux; Android 12; Unspecified Device) AppleWebKit/537.36 (KHTML, like Gecko) '
f'Version/4.0 Chrome/103.0.5060.129 Mobile Safari/537.36 miHoYoBBS/{mihoyobbs_Version}',
'x-rpc-client_type': mihoyobbs_Client_type_web,
'Referer': '',
'Accept-Encoding': 'gzip, deflate',
'Accept-Language': 'zh-CN,en-US;q=0.8',
'X-Requested-With': 'com.mihoyo.hyperion',
"Cookie": "",
'x-rpc-device_id': ""
}
# 通用设置
bbs_Api = "https://bbs-api.mihoyo.com"
web_Api = "https://api-takumi.mihoyo.com"
account_Info_url = web_Api + "/binding/api/getUserGameRolesByCookie?game_biz="
# 米游社的API列表
bbs_Cookie_url = "https://webapi.account.mihoyo.com/Api/cookie_accountinfo_by_loginticket?login_ticket={}"
bbs_Cookie_url2 = web_Api + "/auth/api/getMultiTokenByLoginTicket?login_ticket={}&token_types=3&uid={}"
bbs_Tasks_list = bbs_Api + "/apihub/sapi/getUserMissionsState" # 获取任务列表
bbs_Sign_url = bbs_Api + "/apihub/app/api/signIn" # post
bbs_List_url = bbs_Api + "/post/api/getForumPostList?forum_id={}&is_good=false&is_hot=false&page_size=20&sort_type=1"
bbs_Detail_url = bbs_Api + "/post/api/getPostFull?post_id={}"
bbs_Share_url = bbs_Api + "/apihub/api/getShareConf?entity_id={}&entity_type=1"
bbs_Like_url = bbs_Api + "/apihub/sapi/upvotePost" # post json
# 崩坏2自动签到相关的相关设置
honkai2_Act_id = "e202203291431091"
honkai2_checkin_rewards = f'{web_Api}/event/luna/home?lang=zh-cn&act_id={honkai2_Act_id}'
honkai2_Is_signurl = web_Api + "/event/luna/info?lang=zh-cn&act_id={}&region={}&uid={}"
honkai2_Sign_url = web_Api + "/event/luna/sign"
# 崩坏3自动签到相关的设置
honkai3rd_Act_id = "e202207181446311"
honkai3rd_checkin_rewards = f'{web_Api}/event/luna/home?lang=zh-cn&act_id={honkai3rd_Act_id}'
honkai3rd_Is_signurl = web_Api + "/event/luna/info?lang=zh-cn&act_id={}&region={}&uid={}"
honkai3rd_Sign_url = web_Api + "/event/luna/sign"
# 未定事件簿自动签到相关设置
tearsofthemis_Act_id = "e202202251749321"
tearsofthemis_checkin_rewards = f'{web_Api}/event/luna/home?lang=zh-cn&act_id={tearsofthemis_Act_id}'
tearsofthemis_Is_signurl = honkai2_Is_signurl
tearsofthemis_Sign_url = honkai2_Sign_url # 和二崩完全一致
# 原神自动签到相关的设置
genshin_Act_id = "e202009291139501"
genshin_checkin_rewards = f'{web_Api}/event/bbs_sign_reward/home?act_id={genshin_Act_id}'
genshin_Is_signurl = web_Api + "/event/bbs_sign_reward/info?act_id={}&region={}&uid={}"
genshin_Signurl = web_Api + "/event/bbs_sign_reward/sign"
@@ -0,0 +1,65 @@
import uuid
import time
import random
import string
import hashlib
from .setting import *
# md5计算
def md5(text: str) -> str:
md5 = hashlib.md5()
md5.update(text.encode())
return md5.hexdigest()
# 随机文本
def random_text(num: int) -> str:
return ''.join(random.sample(string.ascii_lowercase + string.digits, num))
# 时间戳
def timestamp() -> int:
return int(time.time())
# 获取请求Header里的DS 当web为true则生成网页端的DS
def get_ds(web: bool) -> str:
if web:
n = mihoyobbs_Salt_web
else:
n = mihoyobbs_Salt
i = str(timestamp())
r = random_text(6)
c = md5("salt=" + n + "&t=" + i + "&r=" + r)
return f"{i},{r},{c}"
# 获取请求Header里的DS(版本2) 这个版本ds之前见到都是查询接口里的
def get_ds2(q: str, b: str) -> str:
n = mihoyobbs_Salt2
i = str(timestamp())
r = str(random.randint(100001, 200000))
add = f'&b={b}&q={q}'
c = md5("salt=" + n + "&t=" + i + "&r=" + r + add)
return f"{i},{r},{c}"
# 生成一个device id
def get_device_id(cookie) -> str:
return str(uuid.uuid3(uuid.NAMESPACE_URL, cookie))
# 获取签到的奖励名称
def get_item(raw_data: dict) -> str:
temp_name = raw_data["name"]
temp_cnt = raw_data["cnt"]
return f"{temp_name}x{temp_cnt}"
# 获取明天早晨0点的时间戳
def next_day() -> int:
now_time = int(time.time())
next_day_time = now_time - now_time % 86400 + time.timezone + 86400
return next_day_time
@@ -26,12 +26,17 @@ memo_path.mkdir(exist_ok=True, parents=True)
@driver.on_startup
async def _():
for name, url in zip(
["resin.png", "task.png", "resin_discount.png"],
[
"resin.png", "task.png", "resin_discount.png", "chengehu.png",
"zhibian.png"
],
[
"https://upload-bbs.mihoyo.com/upload/2021/09/29/8819732/54266243c7d15ba31690c8f5d63cc3c6_71491376413333325"
"20.png?x-oss-process=image//resize,s_600/quality,q_80/auto-orient,0/interlace,1/format,png",
"https://patchwiki.biligame.com/images/ys/thumb/c/cc/6k6kuj1kte6m1n7hexqfrn92z6h4yhh.png/60px-委托任务logo.png",
"https://patchwiki.biligame.com/images/ys/d/d9/t1hv6wpucbwucgkhjntmzroh90nmcdv.png",
"https://s3.bmp.ovh/imgs/2022/08/21/3a3b2e6c22e305ff.png",
"https://s3.bmp.ovh/imgs/2022/08/21/c2d7ace21e1d46cf.png",
],
):
file = memo_path / name
@@ -40,7 +45,8 @@ async def _():
logger.info(f"已下载原神便签资源 -> {file}...")
async def get_user_memo(user_id: int, uid: int, uname: str) -> Optional[Union[str, MessageSegment]]:
async def get_user_memo(user_id: int, uid: int,
uname: str) -> Optional[Union[str, MessageSegment]]:
uid = str(uid)
if uid[0] in ["1", "2"]:
server_id = "cn_gf01"
@@ -54,12 +60,16 @@ async def get_user_memo(user_id: int, uid: int, uname: str) -> Optional[Union[st
async def get_memo(uid: str, server_id: str) -> "Union[str, dict], int":
try:
req = await AsyncHttpx.get(
url=f"https://api-takumi-record.mihoyo.com/game_record/app/genshin/api/dailyNote?server={server_id}&role_id={uid}",
url=
f"https://api-takumi-record.mihoyo.com/game_record/app/genshin/api/dailyNote?server={server_id}&role_id={uid}",
headers={
"DS": get_ds(f"role_id={uid}&server={server_id}"),
"x-rpc-app_version": Config.get_config("genshin", "mhyVersion"),
"User-Agent": "Mozilla/5.0 (iPhone; CPU iPhone OS 13_2_3 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) miHoYoBBS/2.11.1",
"x-rpc-client_type": Config.get_config("genshin", "client_type"),
"x-rpc-app_version": Config.get_config("genshin",
"mhyVersion"),
"User-Agent":
"Mozilla/5.0 (iPhone; CPU iPhone OS 13_2_3 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) miHoYoBBS/2.11.1",
"x-rpc-client_type": Config.get_config("genshin",
"client_type"),
"Referer": "https://webstatic.mihoyo.com/",
"Cookie": await Genshin.get_user_cookie(int(uid))
},
@@ -75,11 +85,18 @@ async def get_memo(uid: str, server_id: str) -> "Union[str, dict], int":
return "发生了一些错误,请稍后再试", 998
def create_border(
image_name: str, content: str, notice_text: str, value: str
) -> BuildImage:
border = BuildImage(500, 100, color="#E0D9D1", font="HYWenHei-85W.ttf", font_size=20)
text_bk = BuildImage(350, 96, color="#F5F1EB", font_size=23, font="HYWenHei-85W.ttf")
def create_border(image_name: str, content: str, notice_text: str,
value: str) -> BuildImage:
border = BuildImage(500,
75,
color="#E0D9D1",
font="HYWenHei-85W.ttf",
font_size=20)
text_bk = BuildImage(350,
75,
color="#F5F1EB",
font_size=23,
font="HYWenHei-85W.ttf")
_x = 70 if image_name == "resin.png" else 50
_px = 10 if image_name == "resin.png" else 20
text_bk.paste(
@@ -88,7 +105,7 @@ def create_border(
True,
center_type="by_height",
)
text_bk.text((87, 20), content)
text_bk.text((87, 15), content)
text_bk.paste(
BuildImage(
0,
@@ -98,18 +115,19 @@ def create_border(
font="HYWenHei-85W.ttf",
font_size=17,
),
(87, 50),
(87, 45),
True,
)
font_width, _ = border.getsize(value)
border.text((350 + 76 - int(font_width / 2), 0), value, center_type="by_height")
border.text((350 + 76 - int(font_width / 2), 0),
value,
center_type="by_height")
border.paste(text_bk, (2, 0), center_type="by_height")
return border
async def parse_data_and_draw(
user_id: int, uid: str, server_id: str, uname: str
) -> Union[str, MessageSegment]:
async def parse_data_and_draw(user_id: int, uid: str, server_id: str,
uname: str) -> Union[str, MessageSegment]:
data, code = await get_memo(uid, server_id)
if code != 200:
return data
@@ -120,13 +138,11 @@ async def parse_data_and_draw(
if not role_avatar.exists():
await AsyncHttpx.download_file(x["avatar_side_icon"], role_avatar)
return await asyncio.get_event_loop().run_in_executor(
None, _parse_data_and_draw, data, user_avatar, uid, uname
)
None, _parse_data_and_draw, data, user_avatar, uid, uname)
def _parse_data_and_draw(
data: dict, user_avatar: BytesIO, uid: int, uname: str
) -> Union[str, MessageSegment]:
def _parse_data_and_draw(data: dict, user_avatar: BytesIO, uid: int,
uname: str) -> Union[str, MessageSegment]:
current_resin = data["current_resin"] # 当前树脂
max_resin = data["max_resin"] # 最大树脂
resin_recovery_time = data["resin_recovery_time"] # 树脂全部回复时间
@@ -137,17 +153,37 @@ def _parse_data_and_draw(
current_expedition_num = data["current_expedition_num"] # 当前挖矿人数
max_expedition_num = data["max_expedition_num"] # 每日挖矿最大人数
expeditions = data["expeditions"] # 挖矿详情
current_coin = data["current_home_coin"] # 当前宝钱
max_coin = data["max_home_coin"] # 最大宝钱
coin_recovery_time = data["home_coin_recovery_time"] # 宝钱全部回复时间
transformer_available = data["transformer"]["obtained"] # 参量质变仪可获取
transformer_state = data["transformer"]["recovery_time"][
"reached"] # 参量质变仪状态
transformer_recovery_time = data["transformer"]["recovery_time"][
"Day"] # 参量质变仪回复时间
transformer_recovery_hour = data["transformer"]["recovery_time"][
"Hour"] # 参量质变仪回复时间
coin_minute, coin_second = divmod(int(coin_recovery_time), 60)
coin_hour, coin_minute = divmod(coin_minute, 60)
#print(data)
minute, second = divmod(int(resin_recovery_time), 60)
hour, minute = divmod(minute, 60)
A = BuildImage(1030, 520, color="#f1e9e1", font_size=15, font="HYWenHei-85W.ttf")
A = BuildImage(1030,
570,
color="#f1e9e1",
font_size=15,
font="HYWenHei-85W.ttf")
A.text((10, 15), "原神便笺 | Create By ZhenXun", (198, 186, 177))
ava = BuildImage(100, 100, background=user_avatar)
ava.circle()
A.paste(ava, (40, 40), True)
A.paste(
BuildImage(0, 0, plain_text=uname, font_size=20, font="HYWenHei-85W.ttf"),
BuildImage(0,
0,
plain_text=uname,
font_size=20,
font="HYWenHei-85W.ttf"),
(160, 62),
True,
)
@@ -177,26 +213,52 @@ def _parse_data_and_draw(
"今日委托已全部完成" if finished_task_num == total_task_num else "今日委托完成数量不足",
f"{finished_task_num}/{total_task_num}",
)
A.paste(border, (10, 265))
A.paste(border, (10, 235))
border = create_border(
"resin_discount.png",
"值得铭记的强敌",
"本周剩余消耗减半次数",
f"{remain_resin_discount_num}/{resin_discount_num_limit}",
)
A.paste(border, (10, 375))
expeditions_border = BuildImage(
470, 430, color="#E0D9D1", font="HYWenHei-85W.ttf", font_size=20
A.paste(border, (10, 315))
border = create_border(
"chengehu.png",
"洞天财翁-洞天宝钱",
"洞天财翁已达到存储上限"
if current_coin == max_coin else f"{coin_hour}小时{coin_minute}分钟后存满",
f"{current_coin}/{max_coin}",
)
expeditions_text = BuildImage(
466, 426, color="#F5F1EB", font_size=23, font="HYWenHei-85W.ttf"
A.paste(border, (10, 395))
border = create_border(
"zhibian.png",
"参量质变仪",
"不存在" if not transformer_available else
"已准备完成 " if transformer_state else f"{transformer_recovery_hour}小时后可使用" if not transformer_recovery_time else f"{transformer_recovery_time}天后可使用",
"不存在" if not transformer_available else
"可使用" if transformer_state else "冷却中",
)
A.paste(border, (10, 475))
expeditions_border = BuildImage(470,
510,
color="#E0D9D1",
font="HYWenHei-85W.ttf",
font_size=20)
expeditions_text = BuildImage(466,
506,
color="#F5F1EB",
font_size=23,
font="HYWenHei-85W.ttf")
expeditions_text.text(
(5, 5), f"探索派遣限制{current_expedition_num}/{max_expedition_num}", (100, 100, 98)
)
(5, 5), f"探索派遣限制{current_expedition_num}/{max_expedition_num}",
(100, 100, 98))
h = 45
for x in expeditions:
_bk = BuildImage(400, 66, color="#ECE3D8", font="HYWenHei-85W.ttf", font_size=21)
_bk = BuildImage(400,
82,
color="#ECE3D8",
font="HYWenHei-85W.ttf",
font_size=21)
file_name = x["avatar_side_icon"].split("_")[-1]
role_avatar = memo_path / "role_avatar" / file_name
_ava_img = BuildImage(75, 75, background=role_avatar)
@@ -227,7 +289,7 @@ def _parse_data_and_draw(
_bk.circle_corner(20)
expeditions_text.paste(_bk, (25, h), True)
h += 75
h += 75 + 16
expeditions_border.paste(expeditions_text, center_type="center")
@@ -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)
+7 -5
View File
@@ -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] = {
+1 -1
View File
@@ -27,7 +27,7 @@ __plugin_settings__ = {
}
one_friend = on_regex(
"^我.*?朋友.*?[想问问|说|让我问问|想问|让我问|想知道|让我帮他问问|让我帮他问|让我帮忙问|让我帮忙问问|问](.*)",
"^我.{0,4}朋友.{0,2}(?:想问问|说|让我问问|想问|让我问|想知道|让我帮他问问|让我帮他问|让我帮忙问|让我帮忙问问|问)(.{0,30})$",
priority=4,
block=True,
)
+8
View File
@@ -43,6 +43,7 @@ usage:
__plugin_superuser_usage__ = """
usage:
更新皮肤指令
重置开箱: 重置今日开箱所有次数
指令:
更新开箱图片 ?[武器箱]
更新开箱价格 ?[武器箱]
@@ -100,6 +101,13 @@ 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.handle()
async def _(event: GroupMessageEvent):
await update_count_daily()
@k_open_case.handle()
async def _(event: GroupMessageEvent, arg: Message = CommandArg()):
+3 -3
View File
@@ -110,11 +110,11 @@ 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}"
vd_info = await video.get_video_base_info(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]
async with aiohttp.ClientSession(
+3
View File
@@ -1,4 +1,5 @@
from configs.config import Config
from utils.manager import GDict
import nonebot
@@ -60,6 +61,8 @@ Config.add_plugin_config(
default_value=True
)
GDict['run_sql'].append("ALTER TABLE omega_pixiv_illusts ADD classified Integer;")
nonebot.load_plugins("plugins/pix_gallery")
@@ -1,5 +1,4 @@
from typing import Optional, List
from datetime import datetime
from typing import Optional, List, Tuple
from services.db_context import db
@@ -12,13 +11,12 @@ class OmegaPixivIllusts(db.Model):
uid = db.Column(db.BigInteger(), nullable=False)
title = db.Column(db.String(), nullable=False)
uname = db.Column(db.String(), nullable=False)
classified = db.Column(db.Integer(), nullable=False)
nsfw_tag = db.Column(db.Integer(), nullable=False)
width = db.Column(db.Integer(), nullable=False)
height = db.Column(db.Integer(), nullable=False)
tags = db.Column(db.String(), nullable=False)
url = db.Column(db.String(), nullable=False)
created_at = db.Column(db.DateTime(timezone=True))
updated_at = db.Column(db.DateTime(timezone=True))
_idx1 = db.Index("omega_pixiv_illusts_idx1", "pid", "url", unique=True)
@@ -32,10 +30,9 @@ class OmegaPixivIllusts(db.Model):
url: str,
uid: int,
uname: str,
classified: int,
nsfw_tag: int,
tags: str,
created_at: datetime,
updated_at: datetime,
):
"""
说明:
@@ -48,10 +45,9 @@ class OmegaPixivIllusts(db.Model):
:param url: url链接
:param uid: 作者uid
:param uname: 作者名称
:param nsfw_tag: nsfw标签, 0=safe, 1=setu. 2=r18
:param classified: 标记标签, 0=未标记, 1=已人工标记或从可信已标记来源获取
:param nsfw_tag: nsfw标签,-1=未标记, 0=safe, 1=setu. 2=r18
:param tags: 相关tag
:param created_at: 创建日期
:param updated_at: 更新日期
"""
if not await cls.check_exists(pid):
await cls.create(
@@ -62,6 +58,7 @@ class OmegaPixivIllusts(db.Model):
url=url,
uid=uid,
uname=uname,
classified=classified,
nsfw_tag=nsfw_tag,
tags=tags,
)
@@ -113,7 +110,7 @@ class OmegaPixivIllusts(db.Model):
return bool(query)
@classmethod
async def get_keyword_num(cls, tags: List[str] = None) -> "int, int, int":
async def get_keyword_num(cls, tags: List[str] = None) -> Tuple[int, int, int]:
"""
说明:
获取相关关键词(keyword, tag)在图库中的数量
+2 -2
View File
@@ -149,8 +149,8 @@ async def _(bot: Bot, event: MessageEvent, arg: Message = CommandArg()):
for _ in range(num):
img_url = None
author = None
# if not all_image:
# await pix.finish("坏了...发完了,没图了...")
if not all_image:
await pix.finish("坏了...发完了,没图了...")
img = random.choice(all_image)
all_image.remove(img)
if isinstance(img, OmegaPixivIllusts):
+29 -29
View File
@@ -1,3 +1,5 @@
import re
from nonebot import on_command
from utils.utils import is_number
from nonebot.permission import SUPERUSER
@@ -11,7 +13,6 @@ import time
from services.log import logger
from pathlib import Path
from typing import List
from datetime import datetime
import asyncio
import os
@@ -144,38 +145,36 @@ async def _(arg: Message = CommandArg()):
@check_omega.handle()
async def _():
async def _tasks(line: str, all_pid: List[int], length: int, index: int):
data = line.split("VALUES", maxsplit=1)[-1].strip()
if data.startswith("("):
data = data[1:]
if data.endswith(");"):
data = data[:-2]
x = data.split(maxsplit=3)
pid = int(x[1][:-1].strip())
data = line.split("VALUES", maxsplit=1)[-1].strip()[1:-2]
num_list = re.findall(r'(\d+)', data)
pid = int(num_list[1])
uid = int(num_list[2])
id_ = 3
while num_list[id_] not in ['0', '1']:
id_ += 1
classified = int(num_list[id_])
nsfw_tag = int(num_list[id_ + 1])
width = int(num_list[id_ + 2])
height = int(num_list[id_ + 3])
str_list = re.findall(r"'(.*?)',", data)
title = str_list[0]
uname = str_list[1]
tags = str_list[2]
url = str_list[3]
if pid in all_pid:
logger.info(f"添加OmegaPixivIllusts图库数据已存在 ---> pid:{pid}")
return
uid = int(x[2][:-1].strip())
x = x[3].split(", '")
title = x[0].strip()[1:-1]
tmp = x[1].split(", ")
author = tmp[0].strip()[:-1]
nsfw_tag = int(tmp[1])
width = int(tmp[2])
height = int(tmp[3])
tags = x[2][:-1]
url = x[3][:-1]
if await OmegaPixivIllusts.add_image_data(
pid,
title,
width,
height,
url,
uid,
author,
nsfw_tag,
tags,
datetime.min,
datetime.min,
pid=pid,
title=title,
width=width,
height=height,
url=url,
uid=uid,
nsfw_tag=nsfw_tag,
tags=tags,
uname=uname,
classified=classified
):
logger.info(
f"成功添加OmegaPixivIllusts图库数据 pid:{pid} 本次预计存储 {length} 张,已更新第 {index} 张"
@@ -197,6 +196,7 @@ async def _():
for line in lines:
if "INSERT INTO" in line.upper():
index += 1
logger.info(f'line: {line} 加入更新计划')
tasks.append(
asyncio.ensure_future(_tasks(line, all_pid, length, index))
)
+2 -2
View File
@@ -1,4 +1,4 @@
from nonebot import on_command
from nonebot import on_regex
from services.log import logger
from nonebot.adapters.onebot.v11 import Bot, MessageEvent, GroupMessageEvent
from nonebot.typing import T_State
@@ -24,7 +24,7 @@ __plugin_settings__ = {
}
quotations = on_command("语录", aliases={"二次元", "二次元语录"}, priority=5, block=True)
quotations = on_regex("^(语录|二次元)$", priority=5, block=True)
url = "https://international.v1.hitokoto.cn/?c=a"
+6 -10
View File
@@ -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)
-2
View File
@@ -24,8 +24,6 @@ from io import BytesIO
import asyncio
import random
import nonebot
import time
import locale
import os
+22 -20
View File
@@ -40,28 +40,30 @@ wbtop_data = []
async def _(event: MessageEvent, arg: Message = CommandArg()):
global wbtop_data
msg = arg.extract_plain_text().strip()
if not wbtop_data or not msg:
if wbtop_data:
now_time = datetime.datetime.now()
if now_time > wbtop_data["time"] + datetime.timedelta(minutes=5):
data, code = await get_wbtop(wbtop_url)
if code != 200:
await wbtop.finish(data, at_sender=True)
wbtop_data = data
else:
if wbtop_data:
now_time = datetime.datetime.now()
if now_time > wbtop_data["time"] + datetime.timedelta(minutes=5):
data, code = await get_wbtop(wbtop_url)
if code != 200:
await wbtop.finish(data, at_sender=True)
else:
wbtop_data = data
else:
data, code = await get_wbtop(wbtop_url)
if code != 200:
await wbtop.finish(data, at_sender=True)
else:
wbtop_data = data
if not msg:
img = await asyncio.get_event_loop().run_in_executor(
None, gen_wbtop_pic, wbtop_data["data"]
)
await wbtop.send(img)
logger.info(
f"(USER {event.user_id}, GROUP {event.group_id if isinstance(event, GroupMessageEvent) else 'private'})"
f" 查询微博热搜"
)
if not msg:
img = await asyncio.get_event_loop().run_in_executor(
None, gen_wbtop_pic, wbtop_data["data"]
)
await wbtop.send(img)
logger.info(
f"(USER {event.user_id}, GROUP {event.group_id if isinstance(event, GroupMessageEvent) else 'private'})"
f" 查询微博热搜"
)
if is_number(msg) and 0 < int(msg) <= 50:
url = wbtop_data["data"][int(msg) - 1]["url"]
await wbtop.send("开始截取数据...")
@@ -72,7 +74,7 @@ async def _(event: MessageEvent, arg: Message = CommandArg()):
wait_time=12
)
if img:
await wbtop.send(img)
await wbtop.finish(img)
else:
await wbtop.send("发生了一些错误.....")
await wbtop.finish("发生了一些错误.....")
+5 -2
View File
@@ -111,6 +111,9 @@ def _(plugin: Plugin, user: User = Depends(token_to_user)) -> Result:
try:
if plugin.plugin_config:
for c in plugin.plugin_config:
if not c.value:
Config.set_config(plugin.model, c.key, None)
continue
if str(c.value).lower() in ["true", "false"] and (
c.default_value is None or isinstance(c.default_value, bool)
):
@@ -124,8 +127,8 @@ def _(plugin: Plugin, user: User = Depends(token_to_user)) -> Result:
) or isinstance(c.default_value, float):
c.value = float(c.value)
elif isinstance(c.value, str) and (
isinstance(Config.get_config(plugin.model, c.key, c.value), list)
or isinstance(c.default_value, list)
isinstance(Config.get_config(plugin.model, c.key, c.value), (list, tuple))
or isinstance(c.default_value, (list, tuple))
):
default_value = Config.get_config(plugin.model, c.key, c.value)
c.value = c.value.split(",")
-21
View File
@@ -10,25 +10,4 @@ Config.add_plugin_config(
default_value=5
)
Config.add_plugin_config(
"word_bank",
"WORD_BANK_FUZZY",
False,
help_="模糊匹配",
default_value=False
)
Config.add_plugin_config(
"word_bank",
"WORD_BANK_KEY",
True,
help_="关键字匹配",
default_value=True
)
Config.add_plugin_config(
"word_bank",
"WORD_BANK_MIX",
25,
help_="查看词条时图片内最多显示条数",
default_value=25
)
nonebot.load_plugins("plugins/word_bank")
+23
View File
@@ -0,0 +1,23 @@
scope2int = {
"全局": 0,
"群聊": 1,
"私聊": 2,
}
type2int = {
"精准": 0,
"模糊": 1,
"正则": 2,
"图片": 3,
}
int2type = {
0: "精准",
1: "模糊",
2: "正则",
3: "图片",
}
+226 -45
View File
@@ -1,54 +1,235 @@
from .model import WordBank
from typing import Union
import random
import time
from pathlib import Path
from nonebot.adapters.onebot.v11 import Message, MessageSegment
from services import logger
from utils.image_utils import text2image
from utils.message_builder import image
from ._model import WordBank
from typing import Optional, Tuple, Union, List, Any
from utils.utils import is_number
import nonebot
driver = nonebot.get_driver()
class WordBankBuilder:
async def get_problem_str(
id_: Union[str, int], group_id: Optional[int] = None, word_scope: int = 1
) -> Tuple[str, int]:
"""
说明:
通过id获取问题字符串
参数:
:param id_: 下标
:param group_id: 群号
:param word_scope: 获取类型
"""
if word_scope in [0, 2]:
all_problem = await WordBank.get_problem_by_scope(word_scope)
else:
all_problem = await WordBank.get_group_all_problem(group_id)
if id_.startswith("id:"):
id_ = id_.split(":")[-1]
if not is_number(id_) or int(id_) < 0 or int(id_) > len(all_problem):
return "id必须为数字且在范围内", 999
return all_problem[int(id_)][0], 200
def __init__(self, user_id: int, group_id: int, problem: str):
self._data = {
"user_id": user_id,
"group_id": group_id}
self.problem = problem
def set_placeholder(self, id_: int, placeholder: Union[str, int]):
"""
设置占位符
:param id_: 站位id
:param placeholder: 占位符内容
"""
if self._data.get("placeholder") is None:
self._data["placeholder"] = []
self._data["placeholder"].append((id_, placeholder))
async def update_word(params: str, group_id: Optional[int] = None, word_scope: int = 1) -> str:
"""
说明:
修改群词条
参数:
:param params: 参数
:param group_id: 群号
:param word_scope: 词条范围
"""
return await word_handle(params, group_id, "update", word_scope)
async def delete_word(params: str, group_id: Optional[int] = None, word_scope: int = 1) -> str:
"""
说明:
删除群词条
参数:
:param params: 参数
:param group_id: 群号
:param word_scope: 词条范围
"""
return await word_handle(params, group_id, "delete", word_scope)
async def word_handle(params: str, group_id: Optional[int], type_: str, word_scope: int = 0) -> str:
"""
说明:
词条操作
参数:
:param params: 参数
:param group_id: 群号
:param type_: 类型
:param word_scope: 词条范围
"""
params = params.split()
problem = params[0]
if problem.startswith("id:"):
problem, code = await get_problem_str(problem, group_id, word_scope)
if code != 200:
return problem
if type_ == "delete":
index = params[1] if len(params) > 1 else None
if index:
answer_num = len(await WordBank.get_problem_all_answer(problem, group_id))
if not is_number(index) or int(index) < 0 or int(index) > answer_num:
return "指定回答下标id必须为数字且在范围内"
index = int(index)
await WordBank.delete_group_problem(problem, group_id, index, word_scope)
return "删除词条成功"
if type_ == "update":
replace_str = params[1]
await WordBank.update_group_problem(problem, replace_str, group_id, word_scope=word_scope)
return "修改词条成功"
async def show_word(
problem: str,
id_: Optional[int],
gid: Optional[int],
group_id: Optional[int] = None,
word_scope: Optional[int] = None,
) -> Union[str, List[Union[str, Message]]]:
if problem:
msg_list = []
if word_scope is not None:
problem = (await WordBank.get_problem_by_scope(word_scope))[id_][0]
id_ = None
_problem_list = await WordBank.get_problem_all_answer(
problem, id_ if id_ is not None else gid, group_id if gid is None else None, word_scope
)
for index, msg in enumerate(_problem_list):
if isinstance(msg, Message):
temp = ""
for seg in msg:
if seg.type == "text":
temp += seg
elif seg.type == "face":
temp += f"[face:{seg.data.id}]"
elif seg.type == "at":
temp += f'[at:{seg.data["qq"]}]'
elif seg.type == "image":
temp += f"[image]"
msg += temp
msg_list.append(f"{index}." + msg if isinstance(msg, str) else msg[1])
msg_list = [
f'词条:{problem or (f"id: {id_}" if id_ is not None else f"gid: {gid}")} 的回答'
] + msg_list
return msg_list
else:
if group_id:
_problem_list = await WordBank.get_group_all_problem(group_id)
else:
_problem_list = await WordBank.get_problem_by_scope(word_scope)
global_problem_list = await WordBank.get_problem_by_scope(0)
if not _problem_list and not global_problem_list:
return "未收录任何词条.."
msg_list = await build_message(_problem_list)
global_msg_list = await build_message(global_problem_list)
if global_msg_list:
msg_list.append("###以下为全局词条###")
msg_list = msg_list + global_msg_list
return msg_list
async def build_message(_problem_list: List[Tuple[Any, Union[MessageSegment, str]]]):
index = 0
str_temp_list = []
msg_list = []
temp_str = ""
for _, problem in _problem_list:
if len(temp_str.split("\n")) > 50:
img = await text2image(
temp_str,
padding=10,
color="#f9f6f2",
)
msg_list.append(image(b64=img.pic2bs4()))
temp_str = ""
if isinstance(problem, str):
if problem not in str_temp_list:
str_temp_list.append(problem)
temp_str += f"{index}. {problem}\n"
else:
if temp_str:
img = await text2image(
temp_str,
padding=10,
color="#f9f6f2",
)
msg_list.append(image(b64=img.pic2bs4()))
temp_str = ""
msg_list.append(f"{index}." + problem)
index += 1
if temp_str:
img = await text2image(
temp_str,
padding=10,
color="#f9f6f2",
)
msg_list.append(image(b64=img.pic2bs4()))
return msg_list
@driver.on_startup
async def _():
try:
from ._old_model import WordBank as OldWordBank
except ModuleNotFoundError:
return
if await WordBank.get_group_all_problem(0):
return
logger.info('开始迁移词条 纯文本 数据')
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}')
def set_answer(self, answer: str):
"""
设置回答
:param answer: 回答
"""
self._data["answer"] = answer
def set_problem(self, problem: str):
"""
设置问题
:param problem: 问题
"""
self._data["problem"] = problem
async def save(self, search_type):
user_id = self._data["user_id"]
group_id = self._data["group_id"]
problem = self._data["problem"]
answer = self._data["answer"]
placeholder = self._data.get("placeholder")
return await WordBank.add_problem_answer(user_id, group_id, search_type, problem, answer, placeholder)
async def update(self, index):
user_id = self._data["user_id"]
group_id = self._data["group_id"]
problem = self._data["problem"]
answer = self._data["answer"]
placeholder = self._data.get("placeholder")
return await WordBank.update_problem_answer(user_id, group_id, problem, answer, index, placeholder)
def __str__(self):
return str(self._data)
+523
View File
@@ -0,0 +1,523 @@
import time
from nonebot.internal.adapter.template import MessageTemplate
from nonebot.adapters.onebot.v11 import (
Message,
MessageEvent,
GroupMessageEvent,
MessageSegment,
)
from services.db_context import db
from typing import Optional, List, Union, Tuple, Any
from datetime import datetime
from configs.path_config import DATA_PATH
import random
from ._config import int2type
from utils.image_utils import get_img_hash
from utils.http_utils import AsyncHttpx
import re
from utils.message_builder import image, face, at
from utils.utils import get_message_img
path = DATA_PATH / "word_bank"
class WordBank(db.Model):
__tablename__ = "word_bank2"
id = db.Column(db.Integer(), primary_key=True)
user_qq = db.Column(db.BigInteger(), nullable=False)
group_id = db.Column(db.Integer())
word_scope = db.Column(
db.Integer(), nullable=False, default=0
) # 生效范围 0: 全局 1: 群聊 2: 私聊
word_type = db.Column(
db.Integer(), nullable=False, default=0
) # 词条类型 0: 完全匹配 1: 模糊 2: 正则 3: 图片
status = db.Column(db.Boolean(), nullable=False, default=True) # 词条状态
problem = db.Column(db.String(), nullable=False) # 问题,为图片时使用图片hash
answer = db.Column(db.String(), nullable=False) # 回答
placeholder = db.Column(db.String()) # 占位符
image_path = db.Column(db.String()) # 使用图片作为问题时图片存储的路径
create_time = db.Column(db.DateTime(), nullable=False)
update_time = db.Column(db.DateTime(), nullable=False)
@classmethod
async def exists(
cls,
user_id: Optional[int],
group_id: Optional[int],
problem: str,
answer: Optional[str],
word_scope: Optional[int] = None,
word_type: Optional[int] = None,
) -> bool:
"""
说明:
检测问题是否存在
参数:
:param user_id: 用户id
:param group_id: 群号
:param problem: 问题
:param answer: 回答
:param word_scope: 词条范围
:param word_type: 词条类型
"""
query = cls.query.where(cls.problem == problem)
if user_id:
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:
query = query.where(cls.word_scope == word_scope)
return bool(await query.gino.first())
@classmethod
async def add_problem_answer(
cls,
user_id: int,
group_id: Optional[int],
word_scope: int,
word_type: int,
problem: Union[str, Message],
answer: Union[str, Message],
):
"""
说明:
添加或新增一个问答
参数:
:param user_id: 用户id
:param group_id: 群号
:param word_scope: 词条范围,
:param word_type: 词条类型,
:param problem: 问题
:param answer: 回答
"""
# 对图片做额外处理
image_path = None
if word_type == 3:
url = get_message_img(problem)[0]
_file = (
path / "problem" / f"{group_id}" / f"{user_id}_{int(time.time())}.jpg"
)
_file.parent.mkdir(exist_ok=True, parents=True)
await AsyncHttpx.download_file(url, _file)
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)
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(
cls, answer: Union[str, Message], user_id: int, group_id: int
) -> Tuple[str, List[Any]]:
"""
说明:
将CQ码转化为占位符
参数:
:param answer: 回答内容
:param user_id: 用户id
:param group_id: 群号
"""
if isinstance(answer, str):
return answer, []
_list = []
text = ""
index = 0
for seg in answer:
if isinstance(seg, str):
text += seg
elif seg.type == "text":
text += seg.data["text"]
elif seg.type == "face":
text += f"[face:placeholder_{index}]"
_list.append(seg.data['id'])
elif seg.type == "at":
text += f"[at:placeholder_{index}]"
_list.append(seg.data["qq"])
else:
text += f"[image:placeholder_{index}]"
index += 1
t = int(time.time())
_file = path / "answer" / f"{group_id}" / f"{user_id}_{t}.jpg"
_file.parent.mkdir(exist_ok=True, parents=True)
await AsyncHttpx.download_file(seg.data["url"], _file)
_list.append(f"answer/{group_id}/{user_id}_{t}.jpg")
return text, _list
@classmethod
async def _format2answer(
cls,
problem: str,
answer: Union[str, Message],
user_id: int,
group_id: int,
query: Optional["WordBank"] = None,
) -> Union[str, Message]:
"""
说明:
将占位符转换为CQ码
参数:
:param problem: 问题内容
:param answer: 回答内容
:param user_id: 用户id
:param group_id: 群号
"""
if query:
answer = query.answer
else:
query = await cls.query.where(
(cls.problem == problem)
& (cls.user_qq == user_id)
& (cls.group_id == group_id)
& (cls.answer == answer)
).gino.first()
if query and query.placeholder:
type_list = re.findall(rf"\[(.*):placeholder_.*]", answer)
temp_answer = re.sub(rf"\[(.*):placeholder_.*]", "{}", answer)
seg_list = []
for t, p in zip(type_list, query.placeholder.split(",")):
if t == "image":
seg_list.append(image(path / p))
elif t == "face":
seg_list.append(face(p))
elif t == "at":
seg_list.append(at(p))
return MessageTemplate(temp_answer, Message).format(*seg_list)
return answer
@classmethod
async def check(
cls,
event: MessageEvent,
problem: str,
word_scope: Optional[int] = None,
word_type: Optional[int] = None,
) -> Optional[Any]:
"""
说明:
检测是否包含该问题并获取所有回答
参数:
:param event: event
:param problem: 问题内容
:param word_scope: 词条范围
:param word_type: 词条类型
"""
query = cls.query
sql_text = "SELECT * FROM public.word_bank2 where 1 = 1"
# 救命!!没找到gino的正则表达式方法,暂时使用sql语句
if isinstance(event, GroupMessageEvent):
if word_scope:
query = query.where(cls.word_scope == word_scope)
sql_text += f" and word_scope = {word_scope}"
else:
query = query.where(
(cls.group_id == event.group_id) | (cls.word_scope == 0)
)
sql_text += f" and (group_id = {event.group_id} or word_scope = 0)"
else:
query = query.where((cls.word_scope == 2) | (cls.word_scope == 0))
sql_text += f" and (word_scope = 2 or word_scope = 0)"
if word_type:
query = query.where(cls.word_scope == word_type)
sql_text += f" and word_scope = {word_scope}"
# 完全匹配
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 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;"
),
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;")
# ):
# return sql_text + f" and word_type = 1 and word_scope != 999 and '{problem}' ~ problem;"
# return None
@classmethod
async def get_answer(
cls,
event: MessageEvent,
problem: str,
word_scope: Optional[int] = None,
word_type: Optional[int] = None,
) -> Optional[Union[str, Message]]:
"""
说明:
根据问题内容获取随机回答
参数:
:param event: event
:param problem: 问题内容
:param word_scope: 词条范围
:param word_type: 词条类型
"""
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), problem=problem)
answer = random.choice(answer_list)
return (
await cls._format2answer(answer[6], answer[7], answer[1], answer[2])
if answer.placeholder
else answer.answer
)
else:
answer_list = await query.gino.all()
answer = random.choice(answer_list)
return (
await cls._format2answer(
problem, answer.answer, answer.user_qq, answer.group_id
)
if answer.placeholder
else answer.answer
)
@classmethod
async def get_problem_all_answer(
cls,
problem: str,
index: Optional[int] = None,
group_id: Optional[int] = None,
word_scope: Optional[int] = 0,
) -> List[Union[str, Message]]:
"""
说明:
获取指定问题所有回答
参数:
:param problem: 问题
:param index: 下标
:param group_id: 群号
:param word_scope: 词条范围
"""
if index is not None:
if group_id:
problem = (await cls.query.where(cls.group_id == group_id).gino.all())[
index
]
else:
problem = (
await cls.query.where(
cls.word_scope == (word_scope or 0)
).gino.all()
)[index]
problem = problem.problem
answer = cls.query.where(cls.problem == problem)
if group_id:
answer = answer.where(cls.group_id == group_id)
return [
await cls._format2answer("", "", 0, 0, x) for x in (await answer.gino.all())
]
@classmethod
async def delete_group_problem(
cls,
problem: str,
group_id: int,
index: Optional[int] = None,
word_scope: int = 1,
):
"""
说明:
删除指定问题全部或指定回答
参数:
:param problem: 问题文本
:param group_id: 群号
:param index: 回答下标
:param word_scope: 词条范围
"""
if index is not None:
if group_id:
query = await cls.query.where(
(cls.group_id == group_id) & (cls.problem == problem)
).gino.all()
else:
query = await cls.query.where(
(cls.word_scope == 0) & (cls.problem == problem)
).gino.all()
await query[index].delete()
else:
if group_id:
await WordBank.delete.where(
(cls.group_id == group_id) & (cls.problem == problem)
).gino.status()
else:
await WordBank.delete.where(
(cls.word_scope == word_scope) & (cls.problem == problem)
).gino.status()
@classmethod
async def update_group_problem(
cls,
problem: str,
replace_str: str,
group_id: int,
index: Optional[int] = None,
word_scope: int = 1,
):
"""
说明:
修改词条问题
参数:
:param problem: 问题
:param replace_str: 替换问题
:param group_id: 群号
:param index: 下标
:param word_scope: 词条范围
"""
if index is not None:
if group_id:
query = await cls.query.where(
(cls.group_id == group_id) & (cls.problem == problem)
).gino.all()
else:
query = await cls.query.where(
(cls.word_scope == word_scope) & (cls.problem == problem)
).gino.all()
await query[index].update(problem=replace_str).apply()
else:
if group_id:
await WordBank.update.values(problem=replace_str).where(
(cls.group_id == group_id) & (cls.problem == problem)
).gino.status()
else:
await WordBank.update.values(problem=replace_str).where(
(cls.word_scope == word_scope) & (cls.problem == problem)
).gino.status()
@classmethod
async def get_group_all_problem(
cls, group_id: int
) -> List[Tuple[Any, Union[MessageSegment, str]]]:
"""
说明:
获取群聊所有词条
参数:
:param group_id: 群号
"""
return cls._handle_problem(
await cls.query.where(cls.group_id == group_id).gino.all()
)
@classmethod
async def get_problem_by_scope(cls, word_scope: int):
"""
说明:
通过词条范围获取词条
参数:
:param word_scope: 词条范围
"""
return cls._handle_problem(
await cls.query.where(cls.word_scope == word_scope).gino.all()
)
@classmethod
async def get_problem_by_type(cls, word_type: int):
"""
说明:
通过词条类型获取词条
参数:
:param word_type: 词条类型
"""
return cls._handle_problem(
await cls.query.where(cls.word_type == word_type).gino.all()
)
@classmethod
def _handle_problem(cls, msg_list: List[Union[str, MessageSegment]]):
"""
说明:
格式化处理问题
参数:
:param msg_list: 消息列表
"""
_tmp = []
problem_list = []
for q in msg_list:
if q.problem not in _tmp:
problem = (
q.problem,
image(path / q.image_path)
if q.image_path
else f"[{int2type[q.word_type]}] " + q.problem,
)
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),
)
+20
View File
@@ -0,0 +1,20 @@
from services.db_context import db
from typing import List
class WordBank(db.Model):
__tablename__ = "word_bank"
user_qq = db.Column(db.BigInteger(), nullable=False)
group_id = db.Column(db.Integer())
search_type = db.Column(db.Integer(), nullable=False, default=0)
problem = db.Column(db.String(), nullable=False)
answer = db.Column(db.String(), nullable=False)
format = db.Column(db.String())
create_time = db.Column(db.DateTime(), nullable=False)
update_time = db.Column(db.DateTime(), nullable=False)
@classmethod
async def get_all(cls) -> List['WordBank']:
return await cls.query.gino.all()
+33 -15
View File
@@ -1,18 +1,36 @@
import re
from nonebot.adapters.onebot.v11 import GroupMessageEvent, Event
from utils.utils import get_message_img_file
from .model import WordBank
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 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: Event) -> bool:
if isinstance(event, GroupMessageEvent):
msg = event.raw_message
list_img = get_message_img_file(event.json())
if list_img:
for img_file in list_img:
strinfo = re.compile(f"{img_file},.*?]")
msg = strinfo.sub(f'{img_file}]', msg)
strinfo_face = re.compile(f",type=sticker]")
msg = strinfo_face.sub(f']', msg)
return bool(await WordBank.check(event.group_id, msg,))
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)
problem = text
if not text and len(img) == 1:
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']}]"
elif seg.type == 'text':
temp += seg.data["text"]
problem = temp
if problem and (await WordBank.check(event, problem) is not None):
state["problem"] = problem
return True
return False
+12 -133
View File
@@ -1,14 +1,10 @@
from utils.message_builder import image, at, face
from typing import Tuple
from services import logger
from ._rule import check
from .model import WordBank
from ._model import WordBank
from configs.path_config import DATA_PATH
from nonebot.adapters.onebot.v11 import GroupMessageEvent
from utils.utils import get_message_at, get_message_img, change_img_md5
from nonebot.adapters.onebot.v11 import GroupMessageEvent, MessageEvent
from nonebot import on_message
from models.group_member_info import GroupInfoUser
from utils.utils import get_message_img_file, is_number
import re
from nonebot.typing import T_State
__zx_plugin_name__ = "词库问答回复操作 [Hidden]"
@@ -19,129 +15,12 @@ message_handle = on_message(priority=6, block=True, rule=check)
@message_handle.handle()
async def _(event: GroupMessageEvent):
msg = event.raw_message
list_img = get_message_img_file(event.json())
if list_img:
for img_file in list_img:
strinfo = re.compile(f"{img_file},.*?]")
msg = strinfo.sub(f'{img_file}]', msg)
strinfo_face = re.compile(f",type=sticker]")
msg = strinfo_face.sub(f']', msg)
q = await WordBank.check(event.group_id, msg, )
await message_handle.send(await get_one_answer(event, q.format, q.answer))
# 处理单条回答
async def get_one_answer(event, format: str, _answer: str, all: bool = True) -> str:
path = data_dir / f"{event.group_id}"
placeholder_list = (
[
(x.split("<_s>")[0], x.split("<_s>")[1])
for x in format.split("<format>")[:-1]
]
if format
else []
)
answer = ""
_a = _answer
if not placeholder_list:
answer = _a
return answer
else:
for idx, placeholder in placeholder_list:
if placeholder.endswith("jpg"):
change_img_md5(path / placeholder)
answer += _a[: _a.find(f"[__placeholder_{idx}]")] + image(
path / placeholder
)
else:
if all:
answer += _a[: _a.find(f"[__placeholder_{idx}]")] + at(int(placeholder))
else:
q = await GroupInfoUser.get_member_info(
int(placeholder), event.group_id)
answer += _a[: _a.find(f"[__placeholder_{idx}]")] + "@" + q.user_name
_a = _a[_a.find(f"[__placeholder_{idx}]") + len(f"[__placeholder_{idx}]"):]
return answer + _a
# 处理单条问题
async def get_one_problem(event, problem: str, ) -> Tuple[str, str]:
strinfo = re.compile(f",subType=\d")
problem = strinfo.sub('', problem)
_problem = problem
_p = problem
problem = ''
for img in get_message_img(event.json()):
_x = img.split("?")[0]
r = re.search(rf"\[CQ:image,file=(.*),url={_x}.*?]", _p)
if r:
_problem = _problem.replace(
rf",url={img}",
f"",
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(
f"(USER {event.user_id}, GROUP "
f"{event.group_id if isinstance(event, GroupMessageEvent) else 'private'})"
f" 触发词条 {problem}"
)
problem += _p[: _p.find(f"[CQ:image,file={r.group(1)},url={img}]")] + image(img)
_p = _p[
_p.find(f"[CQ:image,file={r.group(1)},url={img}]") + len(f"[CQ:image,file={r.group(1)},url={img}]"):]
for at_ in get_message_at(event.json()):
r = re.search(rf"\[CQ:at,qq={at_}]", problem)
if r:
q = await GroupInfoUser.get_member_info(
int(at_), event.group_id)
problem += _p[: _p.find(f"[CQ:at,qq={at_}]")] + "@" + q.user_name
_p = _p[_p.find(f"[CQ:at,qq={at_}]") + len(f"[CQ:at,qq={at_}]"):]
return _problem, problem + _p
# 显示单条数据库问题
async def get_one_image_problem(event, problem: str) -> str:
path = data_dir / f"{event.group_id}" / "problem"
placeholder_list = []
idx = 0
img_list = re.findall(rf"\[CQ:image,file=(.*?)]", problem)
at_list = re.findall(rf"\[CQ:at,qq=(.*?)]", problem)
if img_list:
for img in img_list:
problem = problem.replace(f'[CQ:image,file={img}]', f'[__placeholder_{idx}]', 1)
placeholder_list.append([idx, img])
idx += 1
if at_list:
for ats in at_list:
problem = problem.replace(f'[CQ:at,qq={ats}]', f'[__placeholder_{idx}]', 1)
placeholder_list.append([idx, ats])
idx += 1
_p = problem
problem = ''
if not placeholder_list:
problem = _p
return problem
else:
for idx, placeholder in placeholder_list:
if is_number(placeholder):
q = await GroupInfoUser.get_member_info(
int(placeholder), event.group_id)
problem += _p[: _p.find(f"[__placeholder_{idx}]")] + "@" + q.user_name
else:
problem += _p[: _p.find(f"[__placeholder_{idx}]")] + image(
path / f"{placeholder}.jpg"
)
_p = _p[_p.find(f"[__placeholder_{idx}]") + len(f"[__placeholder_{idx}]"):]
return problem + _p
# 替换cq码
async def replace_cq(group_id, msg: str, is_face: bool = True) -> str:
strinfo_img = re.compile(f"\[CQ:image.*?]")
msg = strinfo_img.sub('[图片]', msg)
at_list = re.findall(rf"\[CQ:at,qq=(.*?)]", msg)
if at_list:
for ats in at_list:
q = await GroupInfoUser.get_member_info(
int(ats), group_id)
msg = msg.replace(f'[CQ:at,qq={ats}]', "@" + q.user_name)
if is_face:
strinfo_face = re.compile(f"\[CQ:face,id=.*?]")
msg = strinfo_face.sub('[表情]', msg)
return msg
-269
View File
@@ -1,269 +0,0 @@
from services.db_context import db
from typing import Optional, List, Union, Tuple
from datetime import datetime
from configs.path_config import DATA_PATH
import random
from configs.config import Config
class WordBank(db.Model):
__tablename__ = "word_bank"
user_qq = db.Column(db.BigInteger(), nullable=False)
group_id = db.Column(db.Integer())
search_type = db.Column(db.Integer(), nullable=False, default=0)
problem = db.Column(db.String(), nullable=False)
answer = db.Column(db.String(), nullable=False)
format = db.Column(db.String())
create_time = db.Column(db.DateTime(), nullable=False)
update_time = db.Column(db.DateTime(), nullable=False)
@classmethod
async def add_problem_answer(
cls,
user_id: int,
group_id: Optional[int],
search_type: [int],
problem: str,
answer: str,
format_: Optional[List[Tuple[int, Union[int, str]]]],
) -> bool:
"""
添加或新增一个问答
:param user_id: 用户id
:param group_id: 群号
:search_type: 问题类型,
:param problem: 问题
:param answer: 回答
:param format_: 格式化数据
"""
_str = None
if format_:
_str = ""
for x, y in format_:
_str += f"{x}<_s>{y}<format>"
return await cls._problem_answer_handle(
user_id, group_id, problem, "add", search_type=search_type, answer=answer, format_=_str
)
@classmethod
async def delete_problem_answer(
cls, user_id: int, group_id: Optional[int], problem: str, index: Optional[int]
) -> str:
"""
删除某问题一个或全部回答
:param user_id: 用户id
:param group_id: 群号
:param problem: 问题
:param index: 回答下标
"""
return await cls._problem_answer_handle(
user_id, group_id, problem, "delete", index=index
)
@classmethod
async def update_problem_answer(
cls,
user_id: int,
group_id: Optional[int],
problem: str,
answer: str,
index: Optional[int],
format_: Optional[List[Tuple[int, Union[int, str]]]],
) -> str:
"""
修改某问题一个或全部回答
:param user_id: 用户id
:param group_id: 群号
:param problem: 问题
:param index: 回答下标
"""
_str = None
if format_:
_str = ""
for x, y in format_:
_str += f"{x}<_s>{y}<format>"
return await cls._problem_answer_handle(
user_id, group_id, problem, "update", answer=answer, index=index, format_=_str
)
@classmethod
async def get_problem_answer(
cls, user_id: int, group_id: Optional[int], problem: str
) -> List[str]:
"""
获取问题的所有回答
:param user_id: 用户id
:param group_id: 群号
:param problem: 问题
"""
return await cls._problem_answer_handle(user_id, group_id, problem, "get")
@classmethod
async def get_group_all_answer(cls, group_id: int, problem: str) -> List[str]:
"""
获取群聊指定词条所有回答
:param group_id: 群号
:param problem: 问题
"""
q = await cls.query.where(
(cls.group_id == group_id) & (cls.problem == problem)
).gino.all()
return [(x.answer, x.format) for x in q] if q else None
@classmethod
async def get_group_all_problem(cls, group_id: int) -> List[str]:
"""
获取群聊所有词条
:param group_id: 群号
"""
q = await cls.query.where(cls.group_id == group_id).gino.all()
q = [x.problem for x in q]
q.sort()
_tmp = []
for problem in q:
_tmp.append(problem)
return list(set(_tmp))
@classmethod
async def check(cls, group_id: int, problem: str) -> Optional["WordBank"]:
"""
检测词条并随机返回
:param group_id: 群号
:param problem: 问题
"""
if problem:
FUZZY = Config.get_config("word_bank", "WORD_BANK_FUZZY")
KEY = Config.get_config("word_bank", "WORD_BANK_KEY")
q = await cls.query.where(
(cls.group_id == group_id) & (cls.problem == problem)
).gino.all()
if KEY and FUZZY:
q_fuzzy = await cls.query.where(
(cls.group_id == group_id) & (cls.search_type == 2) & (
cls.problem.contains(f'{problem}'))).gino.all()
q_key = await cls.query.where((cls.group_id == group_id) & (cls.search_type == 1)).gino.all()
q_key = [x for x in q_key if str(x.problem) in (problem)]
q += q_fuzzy + q_key
elif FUZZY:
q_fuzzy = await cls.query.where(
(cls.group_id == group_id) & (cls.search_type == 2) & (
cls.problem.contains(f'{problem}'))).gino.all()
q += q_fuzzy
elif KEY:
q_key = await cls.query.where((cls.group_id == group_id) & (cls.search_type == 1)).gino.all()
q_key = [x for x in q_key if str(x.problem) in (problem)]
q += q_key
else:
return None
return random.choice(q) if q else None
@classmethod
async def _problem_answer_handle(
cls,
user_id: int,
group_id: Optional[int],
problem: str,
type_: str,
*,
search_type: [int] = 0,
answer: Optional[str] = None,
index: Optional[int] = None,
format_: Optional[str] = None,
) -> Union[List[Union[str, Tuple[str, str]]], bool, str]:
"""
添加或新增一个问答
:param user_id: 用户id
:param group_id: 群号
:param problem: 问题
:param type_: 操作类型
:param answer: 回答
:param format_: 格式化数据
"""
if problem.startswith("id:"):
problem_index = int(problem.split(":")[-1])
q = await cls.get_group_all_problem(group_id)
if not q:
return []
if len(q) > problem_index:
problem = q[problem_index]
if group_id:
q = cls.query.where((cls.group_id == group_id) & (cls.problem == problem))
else:
q = cls.query.where((cls.user_qq == user_id) & (cls.problem == problem))
if type_ == "add":
q = await q.where((cls.answer == answer) & (cls.search_type == search_type)).gino.all()
try:
if not q or ".jpg" in format_:
await cls.create(
user_qq=user_id,
group_id=group_id,
search_type=search_type,
problem=problem,
answer=answer,
format=format_,
create_time=datetime.now().replace(microsecond=0),
update_time=datetime.now().replace(microsecond=0),
)
except:
return False
return True
elif type_ == "delete":
q = await q.with_for_update().gino.all()
if q:
path = DATA_PATH / "word_bank" / f"{group_id}"
if index is not None:
q = [q[index]]
answer = "\n".join([x.answer for x in q])
for x in q:
format_ = x.format
if format_:
for sp in format_.split("<format>")[:-1]:
_, image_name = sp.split("<_s>")
if image_name.endswith("jpg"):
_path = path / image_name
if _path.exists():
_path.unlink()
await cls.delete.where(
(cls.update_time == x.update_time)
& (cls.problem == problem)
& (cls.answer == x.answer)
& (cls.group_id == group_id)
).gino.status()
return answer
elif type_ == "update":
new_format = format_
new_answer = answer
q = await q.with_for_update().gino.all()
if q:
path = DATA_PATH / "word_bank" / f"{group_id}"
if index is not None:
q = [q[index]]
else:
q = [q[0]]
for x in q:
format_ = x.format
if format_:
for sp in format_.split("<format>")[:-1]:
_, image_name = sp.split("<_s>")
if image_name.endswith("jpg"):
_path = path / image_name
if _path.exists():
_path.unlink()
await cls.update.values(answer=new_answer,
format=new_format,
update_time=datetime.now().replace(microsecond=0), ).where(
(cls.problem == problem)
& (cls.answer == x.answer)
& (cls.group_id == group_id)
& (cls.group_id == group_id)
& (cls.update_time == x.update_time)
).gino.status()
return True
elif type_ == "get":
q = await q.gino.all()
if q:
return [(x.answer, x.format.split("<format>")[:-1]) for x in q]
return False
+318
View File
@@ -0,0 +1,318 @@
import re
from typing import Tuple, Any, Optional
from nonebot.internal.params import Arg, ArgStr
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, unescape
from nonebot import on_command, on_regex
from configs.config import Config
from ._data_source import delete_word, update_word, show_word
from ._config import scope2int, type2int
__zx_plugin_name__ = "词库问答 [Admin]"
__plugin_usage__ = r"""
usage:
对指定问题的随机回答,对相同问题可以设置多个不同回答
删除词条后每个词条的id可能会变化,请查看后再删除
更推荐使用id方式删除
问题回答支持的CQ:at, face, image
查看词条命令:群聊时为 群词条+全局词条,私聊时为 私聊词条+全局词条
添加词条正则:添加词条(模糊|正则|图片)?问\s*?(\S*\s?\S*)\s*?答\s?(\S*)
指令:
添加词条 ?[模糊|正则|图片]问...答...:添加问答词条,可重复添加相同问题的不同回答
删除词条 [问题/下标] ?[下标]:删除指定词条指定或全部回答
修改词条 [问题/下标] [新问题]:修改词条问题
查看词条 ?[问题/下标]:查看全部词条或对应词条回答
示例:添加词条问图片答嗨嗨嗨
[图片]...
示例:添加词条@萝莉 我来啦
示例:添加词条问谁是萝莉答是我
示例:删除词条 谁是萝莉
示例:删除词条 谁是萝莉 0
示例:删除词条 id:0 1
示例:修改词条 谁是萝莉 是你
示例:修改词条 id:0 是你
示例:查看词条
示例:查看词条 谁是萝莉
示例:查看词条 id:0 (群/私聊词条)
示例:查看词条 gid:0 (全局词条)
""".strip()
__plugin_superuser_usage__ = r"""
usage:
在私聊中超级用户额外设置
指令:
(全局|私聊)?添加词条\s*?(模糊|正则|图片)?问\s*?(\S*\s?\S*)\s*?答\s?(\S*):添加问答词条,可重复添加相同问题的不同回答
全局添加词条
私聊添加词条
(私聊情况下)删除词条: 删除私聊词条
(私聊情况下)删除全局词条
(私聊情况下)修改词条: 修改词条私聊词条
(私聊情况下)修改全局词条
用法与普通用法相同
""".strip()
__plugin_des__ = "自定义词条内容随机回复"
__plugin_cmd__ = [
"添加词条 ?[模糊/关键字]问...答..",
"删除词条 [问题/下标] ?[下标]",
"修改词条 [问题/下标] ?[下标/新回答] [新回答]",
"查看词条 ?[问题/下标]",
]
__plugin_version__ = 0.3
__plugin_author__ = "HibiKier & yajiwa"
__plugin_settings__ = {
"admin_level": Config.get_config("word_bank", "WORD_BANK_LEVEL [LEVEL]"),
"cmd": ["词库问答", "添加词条", "删除词条", "修改词条", "查看词条"],
}
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*?答\s?(\S*)", priority=5, block=True
)
delete_word_matcher = on_command("删除词条", aliases={'删除全局词条'}, priority=5, block=True)
update_word_matcher = on_command("修改词条", aliases={'修改全局词条'}, priority=5, block=True)
show_word_matcher = on_command("显示词条", aliases={"查看词条"}, priority=5, block=True)
@add_word.handle()
async def _(
bot: Bot,
event: MessageEvent,
state: T_State,
reg_group: Tuple[Any, ...] = RegexGroup(),
):
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 (
word_scope
and word_scope in ["全局", "私聊"]
and str(event.user_id) not in bot.config.superusers
):
await add_word.finish("权限不足,无法添加该范围词条")
if (not problem or not problem.strip()) and word_type != "图片":
await add_word.finish("词条问题不能为空!")
if (not answer or not answer.strip()) and not len(get_message_img(event.message)):
await add_word.finish("词条回答不能为空!")
if word_type != "图片":
state["problem_image"] = "YES"
answer = event.message
# 对at问题对额外处理
if get_message_at(event.message):
for index, seg in enumerate(event.message):
if seg.type == 'text' and '答' in str(seg):
_problem = event.message[:index]
answer = event.message[index:]
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):
temp += g
elif g.type == 'text':
temp += g.data['text']
elif g.type == 'at':
temp += f"[at:{g.data['qq']}]"
problem = temp
break
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
state["answer"] = answer
@add_word.got("problem_image", prompt="请发送该回答设置的问题图片")
async def _(
event: MessageEvent,
word_scope: Optional[str] = ArgStr("word_scope"),
word_type: Optional[str] = ArgStr("word_type"),
problem: Optional[str] = ArgStr("problem"),
answer: Message = Arg("answer"),
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,
scope2int[word_scope] if word_scope else 1,
type2int[word_type] if word_type else 0,
problem or problem_image,
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'})"
f" 添加词条 {problem} 发生错误 {type(e)}: {e} "
)
await add_word.finish(f"添加词条 {problem} 发生错误!")
await add_word.send("添加词条 " + (problem or problem_image) + " 成功!")
logger.info(
f"(USER {event.user_id}, GROUP "
f"{event.group_id if isinstance(event, GroupMessageEvent) else 'private'})"
f" 添加词条 {problem} 成功!"
)
@delete_word_matcher.handle()
async def _(event: GroupMessageEvent, arg: Message = CommandArg()):
if not (msg := arg.extract_plain_text().strip()):
await delete_word_matcher.finish("此命令之后需要跟随指定词条,通过“显示词条“查看")
result = await delete_word(msg, event.group_id)
await delete_word_matcher.send(result)
logger.info(
f"(USER {event.user_id}, GROUP "
f"{event.group_id})"
f" 删除词条:" + msg
)
@delete_word_matcher.handle()
async def _(bot: Bot, event: PrivateMessageEvent, arg: Message = CommandArg(), cmd: Tuple[str, ...] = Command()):
if str(event.user_id) not in bot.config.superusers:
await delete_word_matcher.finish("权限不足捏!")
if not (msg := arg.extract_plain_text().strip()):
await delete_word_matcher.finish("此命令之后需要跟随指定词条,通过“显示词条“查看")
result = await delete_word(msg, word_scope=2 if cmd[0] == '删除词条' else 0)
await delete_word_matcher.send(result)
logger.info(
f"(USER {event.user_id})"
f" 删除词条:" + msg
)
@update_word_matcher.handle()
async def _(event: GroupMessageEvent, arg: Message = CommandArg()):
if not (msg := arg.extract_plain_text().strip()):
await update_word_matcher.finish("此命令之后需要跟随指定词条,通过“显示词条“查看")
if len(msg.split()) < 2:
await update_word_matcher.finish("此命令需要两个参数,请查看帮助")
result = await update_word(msg, event.group_id)
await update_word_matcher.send(result)
logger.info(
f"(USER {event.user_id}, GROUP "
f"{event.group_id})"
f" 更新词条词条:" + msg
)
@update_word_matcher.handle()
async def _(bot: Bot, event: PrivateMessageEvent, arg: Message = CommandArg(), cmd: Tuple[str, ...] = Command()):
if str(event.user_id) not in bot.config.superusers:
await delete_word_matcher.finish("权限不足捏!")
if not (msg := arg.extract_plain_text().strip()):
await update_word_matcher.finish("此命令之后需要跟随指定词条,通过“显示词条“查看")
if len(msg.split()) < 2:
await update_word_matcher.finish("此命令需要两个参数,请查看帮助")
result = await update_word(msg, word_scope=2 if cmd[0] == '修改词条' else 0)
await update_word_matcher.send(result)
logger.info(
f"(USER {event.user_id})"
f" 更新词条词条:" + msg
)
@show_word_matcher.handle()
async def _(bot: Bot, event: GroupMessageEvent, arg: Message = CommandArg()):
if problem := arg.extract_plain_text().strip():
id_ = None
gid = None
if problem.startswith("id:"):
id_ = problem.split(":")[-1]
if (
not is_number(id_)
or int(id_) < 0
or int(id_)
>= len(await WordBank.get_group_all_problem(event.group_id))
):
await show_word_matcher.finish("id必须为数字且在范围内")
id_ = int(id_)
if problem.startswith("gid:"):
gid = problem.split(":")[-1]
if (
not is_number(gid)
or int(gid) < 0
or int(gid)
>= len(await WordBank.get_problem_by_scope(0))
):
await show_word_matcher.finish("gid必须为数字且在范围内")
gid = int(gid)
msg_list = await show_word(problem, id_, gid, None if gid else event.group_id)
else:
msg_list = await show_word(problem, None, None, event.group_id)
if isinstance(msg_list, str):
await show_word_matcher.send(msg_list)
else:
await bot.send_group_forward_msg(
group_id=event.group_id, messages=custom_forward_msg(msg_list, bot.self_id)
)
logger.info(
f"(USER {event.user_id}, GROUP "
f"{event.group_id if isinstance(event, GroupMessageEvent) else 'private'})"
f" 发送查看词条回答:" + problem
)
@show_word_matcher.handle()
async def _(event: PrivateMessageEvent, arg: Message = CommandArg()):
if problem := arg.extract_plain_text().strip():
id_ = None
gid = None
if problem.startswith("id:"):
id_ = problem.split(":")[-1]
if (
not is_number(id_)
or int(id_) < 0
or int(id_)
> len(await WordBank.get_problem_by_scope(2))
):
await show_word_matcher.finish("id必须为数字且在范围内")
id_ = int(id_)
if problem.startswith("gid:"):
gid = problem.split(":")[-1]
if (
not is_number(gid)
or int(gid) < 0
or int(gid)
> len(await WordBank.get_problem_by_scope(0))
):
await show_word_matcher.finish("gid必须为数字且在范围内")
gid = int(gid)
msg_list = await show_word(problem, id_, gid, word_scope=2 if id_ is not None else None)
else:
msg_list = await show_word(problem, None, None, word_scope=2)
if isinstance(msg_list, str):
await show_word_matcher.send(msg_list)
else:
t = ""
for msg in msg_list:
t += msg + '\n'
await show_word_matcher.send(t[:-1])
logger.info(
f"(USER {event.user_id}, GROUP "
f"private)"
f" 发送查看词条回答:" + problem
)
-311
View File
@@ -1,311 +0,0 @@
from utils.utils import get_message_at, is_number, get_message_img
from nonebot.params import CommandArg
from services.log import logger
from configs.path_config import DATA_PATH
from utils.http_utils import AsyncHttpx
from ._data_source import WordBankBuilder
from utils.message_builder import image
from utils.image_utils import text2image
from .message_handle import get_one_answer, get_one_problem, get_one_image_problem, replace_cq
from .model import WordBank
from nonebot.adapters.onebot.v11 import (
Bot,
GroupMessageEvent,
Message
)
from nonebot import on_command
import random
import os
import re
from configs.config import NICKNAME, Config
from models.group_member_info import GroupInfoUser
__zx_plugin_name__ = "词库问答 [Admin]"
__plugin_usage__ = """
usage:
对指定问题的随机回答,对相同问题可以设置多个不同回答
删除词条后每个词条的id可能会变化,请查看后再删除
指令:
添加词条 ?[模糊/关键字|词]...答...:添加问答词条,可重复添加相同问题的不同回答
删除词条 [问题/下标] ?[下标]:删除指定词条指定或全部回答
修改词条 [问题/下标] ?[下标/新回答] [新回答]:修改指定词条指定回答默认修改为第一条
查看词条 ?[问题/下标]:查看全部词条或对应词条回答
示例:添加词条问谁是萝莉答是我
示例:删除词条 谁是萝莉
示例:删除词条 谁是萝莉 0
示例:删除词条 id:0
示例:修改词条 谁是萝莉 是你
示例:修改词条 谁是萝莉 0 是你
示例:修改词条 id:0 是你
示例:查看词条
示例:查看词条 谁是萝莉
示例:查看词条 id:0
""".strip()
__plugin_des__ = "自定义词条内容随机回复"
__plugin_cmd__ = [
"添加词条 ?[模糊/关键字]问...答..",
"删除词条 [问题/下标] ?[下标]",
"修改词条 [问题/下标] ?[下标/新回答] [新回答]",
"查看词条 ?[问题/下标]",
]
__plugin_version__ = 0.3
__plugin_author__ = "HibiKier & yajiwa"
__plugin_settings__ = {
"admin_level": Config.get_config("word_bank", "WORD_BANK_LEVEL [LEVEL]"),
"cmd": ["词库问答", "添加词条", "删除词条", "修改词条", "查看词条"],
}
data_dir = DATA_PATH / "word_bank"
data_dir.mkdir(parents=True, exist_ok=True)
add_word = on_command("添加词条", priority=5, block=True)
delete_word = on_command("删除词条", priority=5, block=True)
update_word = on_command("修改词条", priority=5, block=True)
show_word = on_command("显示词条", aliases={"查看词条"}, priority=5, block=True)
@add_word.handle()
async def _(event: GroupMessageEvent, arg: Message = CommandArg()):
msg = str(arg)
r = re.search(r"问(.+)\s?答([\s\S]*)", msg)
if not r:
await add_word.finish("未检测到词条问题...")
problem = r.group(1).strip()
if not problem:
await add_word.finish("未检测到词条问题...")
answer = msg.split("答", maxsplit=1)[-1]
if not answer:
await add_word.finish("未检测到词条回答...")
idx = 0
_problem = problem
search_type = 0
if re.search("^关键字|词(.*)", msg):
search_type = 1
elif re.search("^模糊(.*)", msg):
search_type = 2
_builder = await get__builder(event, _problem, answer, idx)
if await _builder.save(search_type):
logger.info(f"已保存词条 问:{_builder.problem} 答:{answer}")
await add_word.send("已保存词条:" + _builder.problem)
else:
await delete_word.send("保存失败,可能是回答重复")
@delete_word.handle()
async def _(event: GroupMessageEvent, arg: Message = CommandArg()):
msg = str(arg)
if not msg:
await delete_word.finish("此命令之后需要跟随指定词条,通过“显示词条“查看")
index = None
_sp_msg = msg.split()
if len(_sp_msg) > 1:
if is_number(_sp_msg[-1]):
index = int(_sp_msg[-1])
msg = " ".join(_sp_msg[:-1])
problem = msg
if problem.startswith("id:"):
x = problem.split(":")[-1]
if not is_number(x) or int(x) < 0:
await delete_word.finish("id必须为数字且符合规范!")
p = await WordBank.get_group_all_problem(event.group_id)
if p:
problem = p[int(x)]
try:
_problem, problem = await get_one_problem(event, problem)
if answer := await WordBank.delete_problem_answer(
event.user_id, event.group_id, _problem, index
):
await delete_word.send(Message(
"删除词条成功:\n问" + await replace_cq(event.group_id, problem, False) + f"\n回答:\n" + await replace_cq(
event.group_id, answer, False) + "\n"))
logger.info(
f"(USER {event.user_id}, GROUP "
f"{event.group_id if isinstance(event, GroupMessageEvent) else 'private'})"
f" 删除词条: {problem}"
)
else:
await delete_word.send("删除词条:" + problem + "失败,可能该词条不存在")
except IndexError:
await delete_word.send("指定下标错误...请通过查看词条来确定..")
@update_word.handle()
async def _(event: GroupMessageEvent, arg: Message = CommandArg()):
msg = str(arg)
if not msg:
await update_word.finish("此命令之后需要跟随指定词条,通过“显示词条“查看")
index = None
new_answer = None
problem = None
_sp_msg = msg.split()
len_msg = len(_sp_msg)
if 1 < len_msg:
problem = "".join(_sp_msg[0])
if len_msg == 3:
if is_number(_sp_msg[1]):
index = int(_sp_msg[1])
new_answer = "".join(_sp_msg[2:])
else:
new_answer = "".join(_sp_msg[1:])
else:
await update_word.finish("此命令之后需要跟随修改内容")
idx = 0
_problem = problem
_builder = await get__builder(event, _problem, new_answer, idx)
try:
if await _builder.update(index):
await update_word.send(f"修改词条成功:" + _builder.problem)
logger.info(
f"(USER {event.user_id}, GROUP "
f"{event.group_id if isinstance(event, GroupMessageEvent) else 'private'})"
f" 修改词条: {problem}"
)
else:
await update_word.send(f"修改词条:" + _builder.problem + f"失败,可能该词条不存在")
except IndexError:
await update_word.send("指定下标错误...请通过查看词条来确定..")
@show_word.handle()
async def _(bot: Bot, event: GroupMessageEvent, arg: Message = CommandArg()):
msg = str(arg).strip()
if not msg:
_problem_list = await WordBank.get_group_all_problem(event.group_id)
if not _problem_list:
await show_word.finish("该群未收录任replace_cq何词条..")
_problem_list = [f"\t{i}. {await replace_cq(event.group_id, x)}" for i, x in enumerate(_problem_list)]
long_problem_list = len(_problem_list)
max_line = Config.get_config("word_bank", "WORD_BANK_MIX")
if long_problem_list > max_line:
pic_list = []
mes_list = []
img_nu = long_problem_list // max_line
one_msg = "该群已收录的词条:"
await show_word.send(one_msg)
for i in range(img_nu + 1):
if _problem_list:
one_img = image(
b64=(await text2image("\n".join(_problem_list[:max_line]),
padding=10,
color="#f9f6f2",
)).pic2bs4()
)
if img_nu > 2:
pic_list.append(one_img)
else:
await show_word.send(one_img)
del _problem_list[:max_line]
if pic_list:
for img in pic_list:
data = {
"type": "node",
"data": {"name": f"{NICKNAME}", "uin": f"{bot.self_id}", "content": img},
}
mes_list.append(data)
await bot.send_group_forward_msg(group_id=event.group_id, messages=mes_list)
else:
await show_word.send(
image(
b64=(await text2image(
"该群已收录的词条:\n\n" + "\n".join(_problem_list),
padding=10,
color="#f9f6f2",
)).pic2bs4()
)
)
else:
_answer_list = []
if msg.startswith("id:"):
x = msg.split(":")[-1]
if not is_number(x) or int(x) < 0:
return await delete_word.finish("id必须为数字且符合规范!")
p = await WordBank.get_group_all_problem(event.group_id)
if p:
_problem = p[int(x)]
_answer_list = await WordBank.get_group_all_answer(event.group_id, _problem)
msg += '问' + await get_one_image_problem(event, _problem)
else:
_problem, msg = await get_one_problem(event, msg)
_answer_list = await WordBank.get_group_all_answer(event.group_id, _problem)
if not _answer_list:
await show_word.send("未收录该词条...")
else:
# 解析图片和@
_answer_img_nu_list = [await get_one_answer(event, format, answer, False) for answer, format in
_answer_list]
word_nu = len(_answer_img_nu_list)
img_nu = 0
answer = "词条" + msg + "\n回答:"
for i, x, in enumerate(_answer_img_nu_list):
r = re.findall(rf"\[CQ:image,file=", str(x))
if r:
img_nu += len(r)
answer += "\n" + f"{i}." + x
if (img_nu > 2 and word_nu > 5) or word_nu > 10 or img_nu > 4:
data = {
"type": "node",
"data": {"name": f"{NICKNAME}", "uin": f"{bot.self_id}", "content": answer},
}
await bot.send_group_forward_msg(group_id=event.group_id, messages=data)
else:
await show_word.send(answer)
# await show_word.send(f"词条 {msg} 回答:\n" + "\n".join(_answer_list))
async def get__builder(event, _problem: str, answer: str, idx: int):
(data_dir / f"{event.group_id}").mkdir(exist_ok=True, parents=True)
(data_dir / f"{event.group_id}" / "problem").mkdir(exist_ok=True, parents=True)
_builder = WordBankBuilder(event.user_id, event.group_id, _problem)
problem = ''
_p = _problem
for at_ in get_message_at(event.json()):
r = re.search(rf"\[CQ:at,qq={at_}]", answer)
if r:
answer = answer.replace(f"[CQ:at,qq={at_}]", f"[__placeholder_{idx}]", 1)
_builder.set_placeholder(idx, at_)
idx += 1
r_problem = re.search(rf"\[CQ:at,qq={at_}]", _problem)
if r_problem:
q = await GroupInfoUser.get_member_info(
int(at_), event.group_id)
problem += _p[: _p.find(f"[CQ:at,qq={at_}]")] + "@" + q.user_name
_p = _p[_p.find(f"[CQ:at,qq={at_}]") + len(f"[CQ:at,qq={at_}]"):]
for img in get_message_img(event.json()):
_x = img.split("?")[0]
_x_list = img.split("?")
r = re.search(rf"\[CQ:image,file=(.*),url={_x}.*?]", answer)
if r:
rand = random.randint(1, 10000) + random.randint(1, 114514)
for _ in range(10):
if f"__placeholder_{rand}_{idx}.jpg" not in os.listdir(data_dir / f"{event.group_id}"):
break
rand = random.randint(1, 10000) + random.randint(1, 114514)
strinfo = re.compile(f"\[CQ:image,file={r.group(1)},.*url={_x_list[0]}\?{_x_list[1]}.*?]")
answer = strinfo.sub(f"[__placeholder_{idx}]", answer)
await AsyncHttpx.download_file(
img, data_dir / f"{event.group_id}" / f"__placeholder_{rand}_{idx}.jpg"
)
_builder.set_placeholder(idx, f"__placeholder_{rand}_{idx}.jpg")
idx += 1
r_problem = re.search(rf"\[CQ:image,file=(.*?)(,subType=\d)?,url={_x}.*?]", _p)
if r_problem:
strinfo = re.compile(f"(,subType=\d)?,url={_x_list[0]}\?{_x_list[1]}.*?]")
_problem = strinfo.sub(f"]", _problem)
_p = strinfo.sub(f"]", _p)
problem += _p[: _p.find(f"[CQ:image,file={r_problem.group(1)}]")] + image(img)
_p = _p[_p.find(f"[CQ:image,file={r_problem.group(1)}]") + len(f"[CQ:image,file={r_problem.group(1)}]"):]
problem_img = r_problem.group(1)
if f"{problem_img}.jpg" not in os.listdir(data_dir / f"{event.group_id}" / f"problem"):
await AsyncHttpx.download_file(
img, data_dir / f"{event.group_id}" / f"problem" / f"{problem_img}.jpg"
)
_builder.set_answer(answer)
_builder.set_problem(_problem)
_builder.problem = problem + _p
return _builder
Generated
+377 -332
View File
@@ -89,7 +89,7 @@ python-versions = ">=3.5.3"
[[package]]
name = "asyncpg"
version = "0.25.0"
version = "0.26.0"
description = "An asyncio PostgreSQL driver"
category = "main"
optional = false
@@ -102,17 +102,17 @@ test = ["pycodestyle (>=2.7.0,<2.8.0)", "flake8 (>=3.9.2,<3.10.0)", "uvloop (>=0
[[package]]
name = "attrs"
version = "21.4.0"
version = "22.1.0"
description = "Classes Without Boilerplate"
category = "main"
optional = false
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*"
python-versions = ">=3.5"
[package.extras]
dev = ["coverage[toml] (>=5.0.2)", "hypothesis", "pympler", "pytest (>=4.3.0)", "six", "mypy", "pytest-mypy-plugins", "zope.interface", "furo", "sphinx", "sphinx-notfound-page", "pre-commit", "cloudpickle"]
dev = ["coverage[toml] (>=5.0.2)", "hypothesis", "pympler", "pytest (>=4.3.0)", "mypy (>=0.900,!=0.940)", "pytest-mypy-plugins", "zope.interface", "furo", "sphinx", "sphinx-notfound-page", "pre-commit", "cloudpickle"]
docs = ["furo", "sphinx", "zope.interface", "sphinx-notfound-page"]
tests = ["coverage[toml] (>=5.0.2)", "hypothesis", "pympler", "pytest (>=4.3.0)", "six", "mypy", "pytest-mypy-plugins", "zope.interface", "cloudpickle"]
tests_no_zope = ["coverage[toml] (>=5.0.2)", "hypothesis", "pympler", "pytest (>=4.3.0)", "six", "mypy", "pytest-mypy-plugins", "cloudpickle"]
tests = ["coverage[toml] (>=5.0.2)", "hypothesis", "pympler", "pytest (>=4.3.0)", "mypy (>=0.900,!=0.940)", "pytest-mypy-plugins", "zope.interface", "cloudpickle"]
tests_no_zope = ["coverage[toml] (>=5.0.2)", "hypothesis", "pympler", "pytest (>=4.3.0)", "mypy (>=0.900,!=0.940)", "pytest-mypy-plugins", "cloudpickle"]
[[package]]
name = "backports.zoneinfo"
@@ -159,7 +159,7 @@ qrcode = ["qrcode[pil] (>=7.3.1,<8.0.0)"]
[[package]]
name = "black"
version = "22.3.0"
version = "22.6.0"
description = "The uncompromising code formatter."
category = "main"
optional = false
@@ -170,7 +170,7 @@ click = ">=8.0.0"
mypy-extensions = ">=0.4.3"
pathspec = ">=0.9.0"
platformdirs = ">=2"
tomli = {version = ">=1.1.0", markers = "python_version < \"3.11\""}
tomli = {version = ">=1.1.0", markers = "python_full_version < \"3.11.0a7\""}
typing-extensions = {version = ">=3.10.0.0", markers = "python_version < \"3.10\""}
[package.extras]
@@ -235,7 +235,7 @@ optional = false
python-versions = "*"
[package.extras]
test = ["flake8 (==3.7.8)", "hypothesis (==3.55.3)"]
test = ["hypothesis (==3.55.3)", "flake8 (==3.7.8)"]
[[package]]
name = "cycler"
@@ -260,13 +260,13 @@ regex = "<2019.02.19 || >2019.02.19,<2021.8.27 || >2021.8.27,<2022.3.15"
tzlocal = "*"
[package.extras]
calendars = ["convertdate", "hijri-converter", "convertdate"]
fasttext = ["fasttext"]
langdetect = ["langdetect"]
fasttext = ["fasttext"]
calendars = ["convertdate", "hijri-converter", "convertdate"]
[[package]]
name = "ecdsa"
version = "0.17.0"
version = "0.18.0"
description = "ECDSA cryptographic signature library (pure python)"
category = "main"
optional = false
@@ -321,7 +321,7 @@ sgmllib3k = "*"
[[package]]
name = "fonttools"
version = "4.33.3"
version = "4.34.4"
description = "Tools to manipulate font files"
category = "main"
optional = false
@@ -354,11 +354,11 @@ asyncpg = ">=0.18,<1.0"
SQLAlchemy = ">=1.2.16,<1.4"
[package.extras]
starlette = ["gino-starlette (>=0.1.1,<0.2.0)"]
aiohttp = ["gino-aiohttp (>=0.1.0,<0.2.0)"]
tornado = ["gino-tornado (>=0.1.0,<0.2.0)"]
sanic = ["gino-sanic (>=0.1.0,<0.2.0)"]
quart = ["gino-quart (>=0.1.0,<0.2.0)"]
sanic = ["gino-sanic (>=0.1.0,<0.2.0)"]
tornado = ["gino-tornado (>=0.1.0,<0.2.0)"]
aiohttp = ["gino-aiohttp (>=0.1.0,<0.2.0)"]
starlette = ["gino-starlette (>=0.1.1,<0.2.0)"]
[[package]]
name = "greenlet"
@@ -373,7 +373,7 @@ docs = ["sphinx"]
[[package]]
name = "grpcio"
version = "1.46.3"
version = "1.48.0"
description = "HTTP/2-based RPC framework"
category = "main"
optional = false
@@ -383,7 +383,7 @@ python-versions = ">=3.6"
six = ">=1.5.2"
[package.extras]
protobuf = ["grpcio-tools (>=1.46.3)"]
protobuf = ["grpcio-tools (>=1.48.0)"]
[[package]]
name = "h11"
@@ -475,7 +475,7 @@ python-versions = "*"
[[package]]
name = "kiwisolver"
version = "1.4.3"
version = "1.4.4"
description = "A fast implementation of the Cassowary constraint solver"
category = "main"
optional = false
@@ -494,7 +494,7 @@ colorama = {version = ">=0.3.4", markers = "sys_platform == \"win32\""}
win32-setctime = {version = ">=1.0.0", markers = "sys_platform == \"win32\""}
[package.extras]
dev = ["colorama (>=0.3.4)", "docutils (==0.16)", "flake8 (>=3.7.7)", "tox (>=3.9.0)", "pytest (>=4.6.2)", "pytest-cov (>=2.7.1)", "black (>=19.10b0)", "isort (>=5.1.1)", "Sphinx (>=4.1.1)", "sphinx-autobuild (>=0.7.1)", "sphinx-rtd-theme (>=0.4.3)"]
dev = ["sphinx-rtd-theme (>=0.4.3)", "sphinx-autobuild (>=0.7.1)", "Sphinx (>=4.1.1)", "isort (>=5.1.1)", "black (>=19.10b0)", "pytest-cov (>=2.7.1)", "pytest (>=4.6.2)", "tox (>=3.9.0)", "flake8 (>=3.7.7)", "docutils (==0.16)", "colorama (>=0.3.4)"]
[[package]]
name = "lxml"
@@ -604,7 +604,7 @@ httpx = ["httpx[http2] (>=0.20.0,<1.0.0)"]
[[package]]
name = "numpy"
version = "1.22.4"
version = "1.23.1"
description = "NumPy is the fundamental package for array computing with Python."
category = "main"
optional = false
@@ -647,14 +647,14 @@ python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,>=2.7"
[[package]]
name = "pillow"
version = "9.1.1"
version = "9.2.0"
description = "Python Imaging Library (Fork)"
category = "main"
optional = false
python-versions = ">=3.7"
[package.extras]
docs = ["olefile", "sphinx (>=2.4)", "sphinx-copybutton", "sphinx-issues (>=3.0.1)", "sphinx-removed-in", "sphinx-rtd-theme (>=1.0)", "sphinxext-opengraph"]
docs = ["furo", "olefile", "sphinx (>=2.4)", "sphinx-copybutton", "sphinx-issues (>=3.0.1)", "sphinx-removed-in", "sphinxext-opengraph"]
tests = ["check-manifest", "coverage", "defusedxml", "markdown2", "olefile", "packaging", "pyroma", "pytest", "pytest-cov", "pytest-timeout"]
[[package]]
@@ -671,7 +671,7 @@ test = ["appdirs (==1.4.4)", "pytest-cov (>=2.7)", "pytest-mock (>=3.6)", "pytes
[[package]]
name = "playwright"
version = "1.22.0"
version = "1.24.0"
description = "A high-level API to automate web browsers"
category = "main"
optional = false
@@ -755,7 +755,7 @@ python-versions = ">=3.6"
[[package]]
name = "pygtrie"
version = "2.4.2"
version = "2.5.0"
description = "A pure Python trie data structure implementation."
category = "main"
optional = false
@@ -905,7 +905,7 @@ idna2008 = ["idna"]
[[package]]
name = "rich"
version = "12.4.4"
version = "12.5.1"
description = "Render rich text, tables, progress bars, syntax highlighting, markdown and more to the terminal"
category = "main"
optional = false
@@ -921,7 +921,7 @@ jupyter = ["ipywidgets (>=7.5.1,<8.0.0)"]
[[package]]
name = "rsa"
version = "4.8"
version = "4.9"
description = "Pure-Python RSA implementation"
category = "main"
optional = false
@@ -966,15 +966,16 @@ numpy = ">=1.16.5"
[[package]]
name = "setuptools-scm"
version = "6.4.2"
version = "7.0.5"
description = "the blessed package to manage your versions by scm tags"
category = "main"
optional = false
python-versions = ">=3.6"
python-versions = ">=3.7"
[package.dependencies]
packaging = ">=20.0"
tomli = ">=1.0.0"
typing-extensions = "*"
[package.extras]
test = ["pytest (>=6.2)", "virtualenv (>20)"]
@@ -1057,7 +1058,7 @@ python-versions = ">=3.7"
[[package]]
name = "tomlkit"
version = "0.11.0"
version = "0.11.1"
description = "Style preserving TOML library"
category = "main"
optional = false
@@ -1065,7 +1066,7 @@ python-versions = ">=3.6,<4.0"
[[package]]
name = "typing-extensions"
version = "4.2.0"
version = "4.3.0"
description = "Backported and Experimental Type Hints for Python 3.7+"
category = "main"
optional = false
@@ -1098,7 +1099,7 @@ test = ["pytest-mock (>=3.3)", "pytest (>=4.3)"]
[[package]]
name = "ujson"
version = "5.3.0"
version = "5.4.0"
description = "Ultra fast JSON encoder and decoder for Python"
category = "main"
optional = false
@@ -1168,11 +1169,11 @@ optional = false
python-versions = ">=3.5"
[package.extras]
dev = ["pytest (>=4.6.2)", "black (>=19.3b0)"]
dev = ["black (>=19.3b0)", "pytest (>=4.6.2)"]
[[package]]
name = "wordcloud"
version = "1.8.1"
version = "1.8.2.2"
description = "A little word cloud generator"
category = "main"
optional = false
@@ -1206,7 +1207,7 @@ python-versions = "*"
[metadata]
lock-version = "1.1"
python-versions = "^3.8"
content-hash = "85256064b3b90b3e9a4ef068edec137c7b4ee8b08c0f2dbbe925390e286f045d"
content-hash = "efbf24a685398d0ecf8de274d72f15a1337200482b4a9095716579794ea4aecb"
[metadata.files]
aiofiles = [
@@ -1269,36 +1270,36 @@ async-timeout = [
{file = "async_timeout-3.0.1-py3-none-any.whl", hash = "sha256:4291ca197d287d274d0b6cb5d6f8f8f82d434ed288f962539ff18cc9012f9ea3"},
]
asyncpg = [
{file = "asyncpg-0.25.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:bf5e3408a14a17d480f36ebaf0401a12ff6ae5457fdf45e4e2775c51cc9517d3"},
{file = "asyncpg-0.25.0-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:2bc197fc4aca2fd24f60241057998124012469d2e414aed3f992579db0c88e3a"},
{file = "asyncpg-0.25.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:1a70783f6ffa34cc7dd2de20a873181414a34fd35a4a208a1f1a7f9f695e4ec4"},
{file = "asyncpg-0.25.0-cp310-cp310-win32.whl", hash = "sha256:43cde84e996a3afe75f325a68300093425c2f47d340c0fc8912765cf24a1c095"},
{file = "asyncpg-0.25.0-cp310-cp310-win_amd64.whl", hash = "sha256:56d88d7ef4341412cd9c68efba323a4519c916979ba91b95d4c08799d2ff0c09"},
{file = "asyncpg-0.25.0-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:a84d30e6f850bac0876990bcd207362778e2208df0bee8be8da9f1558255e634"},
{file = "asyncpg-0.25.0-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:beaecc52ad39614f6ca2e48c3ca15d56e24a2c15cbfdcb764a4320cc45f02fd5"},
{file = "asyncpg-0.25.0-cp36-cp36m-musllinux_1_1_x86_64.whl", hash = "sha256:6f8f5fc975246eda83da8031a14004b9197f510c41511018e7b1bedde6968e92"},
{file = "asyncpg-0.25.0-cp36-cp36m-win32.whl", hash = "sha256:ddb4c3263a8d63dcde3d2c4ac1c25206bfeb31fa83bd70fd539e10f87739dee4"},
{file = "asyncpg-0.25.0-cp36-cp36m-win_amd64.whl", hash = "sha256:bf6dc9b55b9113f39eaa2057337ce3f9ef7de99a053b8a16360395ce588925cd"},
{file = "asyncpg-0.25.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:acb311722352152936e58a8ee3c5b8e791b24e84cd7d777c414ff05b3530ca68"},
{file = "asyncpg-0.25.0-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:0a61fb196ce4dae2f2fa26eb20a778db21bbee484d2e798cb3cc988de13bdd1b"},
{file = "asyncpg-0.25.0-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:2633331cbc8429030b4f20f712f8d0fbba57fa8555ee9b2f45f981b81328b256"},
{file = "asyncpg-0.25.0-cp37-cp37m-win32.whl", hash = "sha256:863d36eba4a7caa853fd7d83fad5fd5306f050cc2fe6e54fbe10cdb30420e5e9"},
{file = "asyncpg-0.25.0-cp37-cp37m-win_amd64.whl", hash = "sha256:fe471ccd915b739ca65e2e4dbd92a11b44a5b37f2e38f70827a1c147dafe0fa8"},
{file = "asyncpg-0.25.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:72a1e12ea0cf7c1e02794b697e3ca967b2360eaa2ce5d4bfdd8604ec2d6b774b"},
{file = "asyncpg-0.25.0-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:4327f691b1bdb222df27841938b3e04c14068166b3a97491bec2cb982f49f03e"},
{file = "asyncpg-0.25.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:739bbd7f89a2b2f6bc44cb8bf967dab12c5bc714fcbe96e68d512be45ecdf962"},
{file = "asyncpg-0.25.0-cp38-cp38-win32.whl", hash = "sha256:18d49e2d93a7139a2fdbd113e320cc47075049997268a61bfbe0dde680c55471"},
{file = "asyncpg-0.25.0-cp38-cp38-win_amd64.whl", hash = "sha256:191fe6341385b7fdea7dbdcf47fd6db3fd198827dcc1f2b228476d13c05a03c6"},
{file = "asyncpg-0.25.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:52fab7f1b2c29e187dd8781fce896249500cf055b63471ad66332e537e9b5f7e"},
{file = "asyncpg-0.25.0-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:a738f1b2876f30d710d3dc1e7858160a0afe1603ba16bf5f391f5316eb0ed855"},
{file = "asyncpg-0.25.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:5e4105f57ad1e8fbc8b1e535d8fcefa6ce6c71081228f08680c6dea24384ff0e"},
{file = "asyncpg-0.25.0-cp39-cp39-win32.whl", hash = "sha256:f55918ded7b85723a5eaeb34e86e7b9280d4474be67df853ab5a7fa0cc7c6bf2"},
{file = "asyncpg-0.25.0-cp39-cp39-win_amd64.whl", hash = "sha256:649e2966d98cc48d0646d9a4e29abecd8b59d38d55c256d5c857f6b27b7407ac"},
{file = "asyncpg-0.25.0.tar.gz", hash = "sha256:63f8e6a69733b285497c2855464a34de657f2cccd25aeaeeb5071872e9382540"},
{file = "asyncpg-0.26.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:2ed3880b3aec8bda90548218fe0914d251d641f798382eda39a17abfc4910af0"},
{file = "asyncpg-0.26.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e5bd99ee7a00e87df97b804f178f31086e88c8106aca9703b1d7be5078999e68"},
{file = "asyncpg-0.26.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:868a71704262834065ca7113d80b1f679609e2df77d837747e3d92150dd5a39b"},
{file = "asyncpg-0.26.0-cp310-cp310-win32.whl", hash = "sha256:838e4acd72da370ad07243898e886e93d3c0c9413f4444d600ba60a5cc206014"},
{file = "asyncpg-0.26.0-cp310-cp310-win_amd64.whl", hash = "sha256:a254d09a3a989cc1839ba2c34448b879cdd017b528a0cda142c92fbb6c13d957"},
{file = "asyncpg-0.26.0-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:3ecbe8ed3af4c739addbfbd78f7752866cce2c4e9cc3f953556e4960349ae360"},
{file = "asyncpg-0.26.0-cp36-cp36m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f3ce7d8c0ab4639bbf872439eba86ef62dd030b245ad0e17c8c675d93d7a6b2d"},
{file = "asyncpg-0.26.0-cp36-cp36m-musllinux_1_1_x86_64.whl", hash = "sha256:7129bd809990fd119e8b2b9982e80be7712bb6041cd082be3e415e60e5e2e98f"},
{file = "asyncpg-0.26.0-cp36-cp36m-win32.whl", hash = "sha256:03f44926fa7ff7ccd59e98f05c7e227e9de15332a7da5bbcef3654bf468ee597"},
{file = "asyncpg-0.26.0-cp36-cp36m-win_amd64.whl", hash = "sha256:b1f7b173af649b85126429e11a628d01a5b75973d2a55d64dba19ad8f0e9f904"},
{file = "asyncpg-0.26.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:efe056fd22fc6ed5c1ab353b6510808409566daac4e6f105e2043797f17b8dad"},
{file = "asyncpg-0.26.0-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d96cf93e01df9fb03cef5f62346587805e6c0ca6f654c23b8d35315bdc69af59"},
{file = "asyncpg-0.26.0-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:235205b60d4d014921f7b1cdca0e19669a9a8978f7606b3eb8237ca95f8e716e"},
{file = "asyncpg-0.26.0-cp37-cp37m-win32.whl", hash = "sha256:0de408626cfc811ef04f372debfcdd5e4ab5aeb358f2ff14d1bdc246ed6272b5"},
{file = "asyncpg-0.26.0-cp37-cp37m-win_amd64.whl", hash = "sha256:f92d501bf213b16fabad4fbb0061398d2bceae30ddc228e7314c28dcc6641b79"},
{file = "asyncpg-0.26.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:9acb22a7b6bcca0d80982dce3d67f267d43e960544fb5dd934fd3abe20c48014"},
{file = "asyncpg-0.26.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e550d8185f2c4725c1e8d3c555fe668b41bd092143012ddcc5343889e1c2a13d"},
{file = "asyncpg-0.26.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:050e339694f8c5d9aebcf326ca26f6622ef23963a6a3a4f97aeefc743954afd5"},
{file = "asyncpg-0.26.0-cp38-cp38-win32.whl", hash = "sha256:b0c3f39ebfac06848ba3f1e280cb1fada7cc1229538e3dad3146e8d1f9deb92a"},
{file = "asyncpg-0.26.0-cp38-cp38-win_amd64.whl", hash = "sha256:49fc7220334cc31d14866a0b77a575d6a5945c0fa3bb67f17304e8b838e2a02b"},
{file = "asyncpg-0.26.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:d156e53b329e187e2dbfca8c28c999210045c45ef22a200b50de9b9e520c2694"},
{file = "asyncpg-0.26.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4b4051012ca75defa9a1dc6b78185ca58cdc3a247187eb76a6bcf55dfaa2fad4"},
{file = "asyncpg-0.26.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:6d60f15a0ac18c54a6ca6507c28599c06e2e87a0901e7b548f15243d71905b18"},
{file = "asyncpg-0.26.0-cp39-cp39-win32.whl", hash = "sha256:ede1a3a2c377fe12a3930f4b4dd5340e8b32929541d5db027a21816852723438"},
{file = "asyncpg-0.26.0-cp39-cp39-win_amd64.whl", hash = "sha256:8e1e79f0253cbd51fc43c4d0ce8804e46ee71f6c173fdc75606662ad18756b52"},
{file = "asyncpg-0.26.0.tar.gz", hash = "sha256:77e684a24fee17ba3e487ca982d0259ed17bae1af68006f4cf284b23ba20ea2c"},
]
attrs = [
{file = "attrs-21.4.0-py2.py3-none-any.whl", hash = "sha256:2d27e3784d7a565d36ab851fe94887c5eccd6a463168875832a1be79c82828b4"},
{file = "attrs-21.4.0.tar.gz", hash = "sha256:626ba8234211db98e869df76230a137c4c40a12d72445c45d5f5b716f076e2fd"},
{file = "attrs-22.1.0-py2.py3-none-any.whl", hash = "sha256:86efa402f67bf2df34f51a335487cf46b1ec130d02b8d39fd248abfd30da551c"},
{file = "attrs-22.1.0.tar.gz", hash = "sha256:29adc2665447e5191d0e7c568fde78b21f9672d344281d0c6e1ab085429b22b6"},
]
"backports.zoneinfo" = [
{file = "backports.zoneinfo-0.2.1-cp36-cp36m-macosx_10_14_x86_64.whl", hash = "sha256:da6013fd84a690242c310d77ddb8441a559e9cb3d3d59ebac9aca1a57b2e18bc"},
@@ -1328,29 +1329,29 @@ bilireq = [
{file = "bilireq-0.2.0.post1.tar.gz", hash = "sha256:f731a5768c7e3159b1e0d5b02b3fdb58939ed24d6cd23a428ab1df20fbbf62cc"},
]
black = [
{file = "black-22.3.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:2497f9c2386572e28921fa8bec7be3e51de6801f7459dffd6e62492531c47e09"},
{file = "black-22.3.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:5795a0375eb87bfe902e80e0c8cfaedf8af4d49694d69161e5bd3206c18618bb"},
{file = "black-22.3.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:e3556168e2e5c49629f7b0f377070240bd5511e45e25a4497bb0073d9dda776a"},
{file = "black-22.3.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:67c8301ec94e3bcc8906740fe071391bce40a862b7be0b86fb5382beefecd968"},
{file = "black-22.3.0-cp310-cp310-win_amd64.whl", hash = "sha256:fd57160949179ec517d32ac2ac898b5f20d68ed1a9c977346efbac9c2f1e779d"},
{file = "black-22.3.0-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:cc1e1de68c8e5444e8f94c3670bb48a2beef0e91dddfd4fcc29595ebd90bb9ce"},
{file = "black-22.3.0-cp36-cp36m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6d2fc92002d44746d3e7db7cf9313cf4452f43e9ea77a2c939defce3b10b5c82"},
{file = "black-22.3.0-cp36-cp36m-win_amd64.whl", hash = "sha256:a6342964b43a99dbc72f72812bf88cad8f0217ae9acb47c0d4f141a6416d2d7b"},
{file = "black-22.3.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:328efc0cc70ccb23429d6be184a15ce613f676bdfc85e5fe8ea2a9354b4e9015"},
{file = "black-22.3.0-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:06f9d8846f2340dfac80ceb20200ea5d1b3f181dd0556b47af4e8e0b24fa0a6b"},
{file = "black-22.3.0-cp37-cp37m-win_amd64.whl", hash = "sha256:ad4efa5fad66b903b4a5f96d91461d90b9507a812b3c5de657d544215bb7877a"},
{file = "black-22.3.0-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:e8477ec6bbfe0312c128e74644ac8a02ca06bcdb8982d4ee06f209be28cdf163"},
{file = "black-22.3.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:637a4014c63fbf42a692d22b55d8ad6968a946b4a6ebc385c5505d9625b6a464"},
{file = "black-22.3.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:863714200ada56cbc366dc9ae5291ceb936573155f8bf8e9de92aef51f3ad0f0"},
{file = "black-22.3.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:10dbe6e6d2988049b4655b2b739f98785a884d4d6b85bc35133a8fb9a2233176"},
{file = "black-22.3.0-cp38-cp38-win_amd64.whl", hash = "sha256:cee3e11161dde1b2a33a904b850b0899e0424cc331b7295f2a9698e79f9a69a0"},
{file = "black-22.3.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:5891ef8abc06576985de8fa88e95ab70641de6c1fca97e2a15820a9b69e51b20"},
{file = "black-22.3.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:30d78ba6bf080eeaf0b7b875d924b15cd46fec5fd044ddfbad38c8ea9171043a"},
{file = "black-22.3.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:ee8f1f7228cce7dffc2b464f07ce769f478968bfb3dd1254a4c2eeed84928aad"},
{file = "black-22.3.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6ee227b696ca60dd1c507be80a6bc849a5a6ab57ac7352aad1ffec9e8b805f21"},
{file = "black-22.3.0-cp39-cp39-win_amd64.whl", hash = "sha256:9b542ced1ec0ceeff5b37d69838106a6348e60db7b8fdd245294dc1d26136265"},
{file = "black-22.3.0-py3-none-any.whl", hash = "sha256:bc58025940a896d7e5356952228b68f793cf5fcb342be703c3a2669a1488cb72"},
{file = "black-22.3.0.tar.gz", hash = "sha256:35020b8886c022ced9282b51b5a875b6d1ab0c387b31a065b84db7c33085ca79"},
{file = "black-22.6.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:f586c26118bc6e714ec58c09df0157fe2d9ee195c764f630eb0d8e7ccce72e69"},
{file = "black-22.6.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:b270a168d69edb8b7ed32c193ef10fd27844e5c60852039599f9184460ce0807"},
{file = "black-22.6.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:6797f58943fceb1c461fb572edbe828d811e719c24e03375fd25170ada53825e"},
{file = "black-22.6.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c85928b9d5f83b23cee7d0efcb310172412fbf7cb9d9ce963bd67fd141781def"},
{file = "black-22.6.0-cp310-cp310-win_amd64.whl", hash = "sha256:f6fe02afde060bbeef044af7996f335fbe90b039ccf3f5eb8f16df8b20f77666"},
{file = "black-22.6.0-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:cfaf3895a9634e882bf9d2363fed5af8888802d670f58b279b0bece00e9a872d"},
{file = "black-22.6.0-cp36-cp36m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:94783f636bca89f11eb5d50437e8e17fbc6a929a628d82304c80fa9cd945f256"},
{file = "black-22.6.0-cp36-cp36m-win_amd64.whl", hash = "sha256:2ea29072e954a4d55a2ff58971b83365eba5d3d357352a07a7a4df0d95f51c78"},
{file = "black-22.6.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:e439798f819d49ba1c0bd9664427a05aab79bfba777a6db94fd4e56fae0cb849"},
{file = "black-22.6.0-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:187d96c5e713f441a5829e77120c269b6514418f4513a390b0499b0987f2ff1c"},
{file = "black-22.6.0-cp37-cp37m-win_amd64.whl", hash = "sha256:074458dc2f6e0d3dab7928d4417bb6957bb834434516f21514138437accdbe90"},
{file = "black-22.6.0-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:a218d7e5856f91d20f04e931b6f16d15356db1c846ee55f01bac297a705ca24f"},
{file = "black-22.6.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:568ac3c465b1c8b34b61cd7a4e349e93f91abf0f9371eda1cf87194663ab684e"},
{file = "black-22.6.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:6c1734ab264b8f7929cef8ae5f900b85d579e6cbfde09d7387da8f04771b51c6"},
{file = "black-22.6.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c9a3ac16efe9ec7d7381ddebcc022119794872abce99475345c5a61aa18c45ad"},
{file = "black-22.6.0-cp38-cp38-win_amd64.whl", hash = "sha256:b9fd45787ba8aa3f5e0a0a98920c1012c884622c6c920dbe98dbd05bc7c70fbf"},
{file = "black-22.6.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:7ba9be198ecca5031cd78745780d65a3f75a34b2ff9be5837045dce55db83d1c"},
{file = "black-22.6.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:a3db5b6409b96d9bd543323b23ef32a1a2b06416d525d27e0f67e74f1446c8f2"},
{file = "black-22.6.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:560558527e52ce8afba936fcce93a7411ab40c7d5fe8c2463e279e843c0328ee"},
{file = "black-22.6.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b154e6bbde1e79ea3260c4b40c0b7b3109ffcdf7bc4ebf8859169a6af72cd70b"},
{file = "black-22.6.0-cp39-cp39-win_amd64.whl", hash = "sha256:4af5bc0e1f96be5ae9bd7aaec219c901a94d6caa2484c21983d043371c733fc4"},
{file = "black-22.6.0-py3-none-any.whl", hash = "sha256:ac609cf8ef5e7115ddd07d85d988d074ed00e10fbc3445aee393e70164a2219c"},
{file = "black-22.6.0.tar.gz", hash = "sha256:6c6d39e28aed379aec40da1c65434c77d75e65bb59a1e1c283de545fb4e7c6c9"},
]
certifi = [
{file = "certifi-2022.6.15-py3-none-any.whl", hash = "sha256:fe86415d55e84719d75f8b69414f6438ac3547d2078ab91b67e779ef69378412"},
@@ -1385,8 +1386,8 @@ dateparser = [
{file = "dateparser-1.1.1.tar.gz", hash = "sha256:038196b1f12c7397e38aad3d61588833257f6f552baa63a1499e6987fa8d42d9"},
]
ecdsa = [
{file = "ecdsa-0.17.0-py2.py3-none-any.whl", hash = "sha256:5cf31d5b33743abe0dfc28999036c849a69d548f994b535e527ee3cb7f3ef676"},
{file = "ecdsa-0.17.0.tar.gz", hash = "sha256:b9f500bb439e4153d0330610f5d26baaf18d17b8ced1bc54410d189385ea68aa"},
{file = "ecdsa-0.18.0-py2.py3-none-any.whl", hash = "sha256:80600258e7ed2f16b9aa1d7c295bd70194109ad5a30fdee0eaeefef1d4c559dd"},
{file = "ecdsa-0.18.0.tar.gz", hash = "sha256:190348041559e21b22a1d65cee485282ca11a6f81d503fddb84d5017e9ed1e49"},
]
emoji = [
{file = "emoji-1.7.0.tar.gz", hash = "sha256:65c54533ea3c78f30d0729288998715f418d7467de89ec258a31c0ce8660a1d1"},
@@ -1400,8 +1401,8 @@ feedparser = [
{file = "feedparser-6.0.10.tar.gz", hash = "sha256:27da485f4637ce7163cdeab13a80312b93b7d0c1b775bef4a47629a3110bca51"},
]
fonttools = [
{file = "fonttools-4.33.3-py3-none-any.whl", hash = "sha256:f829c579a8678fa939a1d9e9894d01941db869de44390adb49ce67055a06cc2a"},
{file = "fonttools-4.33.3.zip", hash = "sha256:c0fdcfa8ceebd7c1b2021240bd46ef77aa8e7408cf10434be55df52384865f8e"},
{file = "fonttools-4.34.4-py3-none-any.whl", hash = "sha256:d73f25b283cd8033367451122aa868a23de0734757a01984e4b30b18b9050c72"},
{file = "fonttools-4.34.4.zip", hash = "sha256:9a1c52488045cd6c6491fd07711a380f932466e317cb8e016fc4e99dc7eac2f0"},
]
gino = [
{file = "gino-1.0.1-py3-none-any.whl", hash = "sha256:56df57cfdefbaf897a7c4897c265a0e91a8cca80716fb64f7d3cf6d501fdfb3d"},
@@ -1465,60 +1466,52 @@ greenlet = [
{file = "greenlet-1.1.2.tar.gz", hash = "sha256:e30f5ea4ae2346e62cedde8794a56858a67b878dd79f7df76a0767e356b1744a"},
]
grpcio = [
{file = "grpcio-1.46.3-cp310-cp310-linux_armv7l.whl", hash = "sha256:4c05dbc164c2d3015109292ffeed68292807a6cb1225f9a36699bf2166634908"},
{file = "grpcio-1.46.3-cp310-cp310-macosx_10_10_universal2.whl", hash = "sha256:c6a460b6aaf43428d48fececad864cc562458b944df80568e490d985d8576292"},
{file = "grpcio-1.46.3-cp310-cp310-manylinux_2_17_aarch64.whl", hash = "sha256:707b85fa0cf606a9ab02246bd3142c76e154f1c30f00f7346b2afa3d0b315d5a"},
{file = "grpcio-1.46.3-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:8c63e7c61c0b06f838e8f45ffd3a7c68a520c4c026b2e0e8b1ad29c456d0f859"},
{file = "grpcio-1.46.3-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c6fe85e5873d9784ab82cf261d9fc07ed67a4459ba69fbe1187ef8b8e3d9e30e"},
{file = "grpcio-1.46.3-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:df980c4901a92ca649e18036ff67c7c8cad239b2759c2472694f7ab0f0b4ffb9"},
{file = "grpcio-1.46.3-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:7b59982e405159385d5796aa1e0817ec83affb3eb4c2a5b7ca39413d17d7e332"},
{file = "grpcio-1.46.3-cp310-cp310-win32.whl", hash = "sha256:6d51fa98bd40d4593f819a3fec8a078a192958d24f84c3daf15b5ad7705d4c48"},
{file = "grpcio-1.46.3-cp310-cp310-win_amd64.whl", hash = "sha256:e9bba429eb743471715e6dadf006a70a77cb6afb065aa4a6eaa9efd76b09e336"},
{file = "grpcio-1.46.3-cp36-cp36m-linux_armv7l.whl", hash = "sha256:a898b0f13bda2dfe786952cc1ea705762fa6c3ae799b4bb0525d7821605ae968"},
{file = "grpcio-1.46.3-cp36-cp36m-macosx_10_10_x86_64.whl", hash = "sha256:9014aee70e29911008d2f388011cabf2c7fe4fe29918ce5f71513a660494069a"},
{file = "grpcio-1.46.3-cp36-cp36m-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:9c97106134de70f8323b12738ac0adf0615688b69253002910d0c5d42d202a77"},
{file = "grpcio-1.46.3-cp36-cp36m-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:d41ea8efb87b1ae4e576b13d94f2b470297a1495ae6b2c9d1047952731bf168f"},
{file = "grpcio-1.46.3-cp36-cp36m-manylinux_2_17_aarch64.whl", hash = "sha256:ab18e85082003d7883a4d069065436e61cb27c2c2150e7965ce93658f17bc8da"},
{file = "grpcio-1.46.3-cp36-cp36m-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:307ff1d6237d5c383196660a12db021c20280227f9f4423d88d6b2ab20c8b1d0"},
{file = "grpcio-1.46.3-cp36-cp36m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8c9106ef35239767b3aa9dc1a79856ad499655f853fca9f92f9dd3182d646627"},
{file = "grpcio-1.46.3-cp36-cp36m-musllinux_1_1_i686.whl", hash = "sha256:e0ae8e8523308bf7ab0b7d6aa686011de59b19fb06abb253f302d0b5da2a5905"},
{file = "grpcio-1.46.3-cp36-cp36m-musllinux_1_1_x86_64.whl", hash = "sha256:4fd0aa30a938893060defd5f222604461db55f9a81a028b154479b91deac7074"},
{file = "grpcio-1.46.3-cp36-cp36m-win32.whl", hash = "sha256:f7637b55662e56a64c07846bc0d2da6232a6e893b22c39790f2e41d03ac1a826"},
{file = "grpcio-1.46.3-cp36-cp36m-win_amd64.whl", hash = "sha256:97801afa96a819f911d030b490dbea95b246de02433bac69c5acf150081686e4"},
{file = "grpcio-1.46.3-cp37-cp37m-linux_armv7l.whl", hash = "sha256:3585a6fa3d97fc8f030bbf0e88185b5eb345a340f6732e165d5c22df54de5bc6"},
{file = "grpcio-1.46.3-cp37-cp37m-macosx_10_10_x86_64.whl", hash = "sha256:dc6d15cbcceaebaacf2994280ed1c01d42b5772059b30afd8a76152e9d23daa4"},
{file = "grpcio-1.46.3-cp37-cp37m-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:e0486485d59d5865149010966ef3df99c5df97ab8b01f10e26f8759d6e10fafc"},
{file = "grpcio-1.46.3-cp37-cp37m-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:5210ec7a1638daa61da16487fbfafb3dbb7b8cd44382d9262316bbb58a5b1cf7"},
{file = "grpcio-1.46.3-cp37-cp37m-manylinux_2_17_aarch64.whl", hash = "sha256:e278fa30d2b5652f7e43970c86ad34c639146443553678b746909aae204924dc"},
{file = "grpcio-1.46.3-cp37-cp37m-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7d4148f1f76516b01cccf2273b45bc706847f1560ccb55aa6e29df851e9ca8cc"},
{file = "grpcio-1.46.3-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:01f3f7a6cdb111cf276ffff9c892fa32624e03999bac809d3f3d8321d98b6855"},
{file = "grpcio-1.46.3-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:91aaccbe1c035ad2bcd1b8a25cebd11839070eb70fb6573e9d0197ddbca5d96b"},
{file = "grpcio-1.46.3-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:26136c19d96e2138f04412101f3730d66f5f1515dc912ac0d415587c8208d826"},
{file = "grpcio-1.46.3-cp37-cp37m-win32.whl", hash = "sha256:a8f40dafcdc3e0e378387953528eaf4e35758161f3b10d96199f12b11afbe2c2"},
{file = "grpcio-1.46.3-cp37-cp37m-win_amd64.whl", hash = "sha256:a6bb52df85a4bd6d3bad16b4e7cc43efe95469b74a856c87a2c5bef496c9147f"},
{file = "grpcio-1.46.3-cp38-cp38-linux_armv7l.whl", hash = "sha256:2334ceeab4084e80433693451452cba26afc1607a7974133af3b3635fc8aa935"},
{file = "grpcio-1.46.3-cp38-cp38-macosx_10_10_x86_64.whl", hash = "sha256:2c96a6103caec84985bb2cffac2b261f8cac2641e7a70d4b43b7d08754a6cfe7"},
{file = "grpcio-1.46.3-cp38-cp38-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:7a39d39da8855b03be2d7348387986bab6a322031fcc8b04fa5e72355e7b13a1"},
{file = "grpcio-1.46.3-cp38-cp38-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:4caf87a06de88e3611a4610c57ef55b78801843d1f5a9e5fd6b75e887dad3340"},
{file = "grpcio-1.46.3-cp38-cp38-manylinux_2_17_aarch64.whl", hash = "sha256:ffbbb228e6fc6f85b34aac428eb76b4fc6591d771e487ce46eb16b4b7e18b91d"},
{file = "grpcio-1.46.3-cp38-cp38-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0c89ae010c57333dd3c692e0892199a59df1ddfd467cdfea31f98331d0e8cf87"},
{file = "grpcio-1.46.3-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:34b206cdf78dd1c14d93e10e7308750c36b4e6754d579895cba74341875e2fb5"},
{file = "grpcio-1.46.3-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:a19b3ecdb8ddf60e4b034def27636065e49ac1ee3c85854a16353cf52c2afd83"},
{file = "grpcio-1.46.3-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:aac6e66acae82be5c99a0a40ab8f5733d7df76a04f242cf42ecc34cfb1e947bd"},
{file = "grpcio-1.46.3-cp38-cp38-win32.whl", hash = "sha256:aff6d961d6bc5e34e12e148383671f8da5d17e47ed606ec15f483def3053b206"},
{file = "grpcio-1.46.3-cp38-cp38-win_amd64.whl", hash = "sha256:71d46c2f3c0512bac3d658af3193e3d645c96123af56bd07a8416474c69df2cf"},
{file = "grpcio-1.46.3-cp39-cp39-linux_armv7l.whl", hash = "sha256:5969f63f3cf92538f83f26949d393d9fc59de670f47cf7c2a0e1e0d30b770294"},
{file = "grpcio-1.46.3-cp39-cp39-macosx_10_10_x86_64.whl", hash = "sha256:5f8134d4a7e76c8c6644bd3ce728b9894933575155d02c09922986d5d8d6e48c"},
{file = "grpcio-1.46.3-cp39-cp39-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:53fff69fd4d315adddda226e7b71804d1f12adf3a4162126dc520725624a483a"},
{file = "grpcio-1.46.3-cp39-cp39-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:3af2cc4e41f87d3b57f624b1b14321c1d0f030b191da60f9eeeda5448d83240c"},
{file = "grpcio-1.46.3-cp39-cp39-manylinux_2_17_aarch64.whl", hash = "sha256:5fb7779ae01c20c4fad5831e98003b3f036acfe6b77697d6a9baa0f9a7f14daf"},
{file = "grpcio-1.46.3-cp39-cp39-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:56636ebf8db63ba50d272dfd73c92538950525120311676246f8f6a81b0aa144"},
{file = "grpcio-1.46.3-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0a5012ba00cf8b7ce9e6ac2312ace0b0e16fe9502c18340c8c3ecb734a759831"},
{file = "grpcio-1.46.3-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:be1679d814a292a701f45df324e25b060435dd13159e9b08a16e2a2396c4391c"},
{file = "grpcio-1.46.3-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:4faaba7db078a0001a8c1a4370d56dc454c03b4613b6acec01f14b90c8dd03cf"},
{file = "grpcio-1.46.3-cp39-cp39-win32.whl", hash = "sha256:f5c6393fa645183ae858ebfbf72ab94e7ebafb5cd849dcf4ae8c53a83cce4e24"},
{file = "grpcio-1.46.3-cp39-cp39-win_amd64.whl", hash = "sha256:158b90d4f1354f40e435f4c866057acc29a4364b214c31049c8b8c903646fbab"},
{file = "grpcio-1.46.3.tar.gz", hash = "sha256:4b8fd8b1cd553635274b83cd984f0755e6779886eca53c1c71d48215962eb689"},
{file = "grpcio-1.48.0-cp310-cp310-linux_armv7l.whl", hash = "sha256:4a049a032144641ed5d073535c0dc69eb6029187cc729a66946c86dcc8eec3a1"},
{file = "grpcio-1.48.0-cp310-cp310-macosx_10_10_x86_64.whl", hash = "sha256:f8bc76f5cd95f5476e5285fe5d3704a9332586a569fbbccef551b0b6f7a270f9"},
{file = "grpcio-1.48.0-cp310-cp310-manylinux_2_17_aarch64.whl", hash = "sha256:448d397fe88e9fef8170f019b86abdc4d554ae311aaf4dbff1532fde227d3308"},
{file = "grpcio-1.48.0-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:8f9b6b6f7c83869d2316c5d13f953381881a16741275a34ec5ed5762f11b206e"},
{file = "grpcio-1.48.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5bd8541c4b6b43c9024496d30b4a12346325d3a17a1f3c80ad8924caed1e35c3"},
{file = "grpcio-1.48.0-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:877d33aeba05ae0b9e81761a694914ed33613f655c35f6bbcf4ebbcb984e0167"},
{file = "grpcio-1.48.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:cd01a8201fd8ab2ce496f7e65975da1f1e629eac8eea84ead0fd77e32e4350cd"},
{file = "grpcio-1.48.0-cp310-cp310-win32.whl", hash = "sha256:0388da923dff58ba7f711233e41c2b749b5817b8e0f137a107672d9c15a1009c"},
{file = "grpcio-1.48.0-cp310-cp310-win_amd64.whl", hash = "sha256:8dcffdb8921fd88857ae350fd579277a5f9315351e89ed9094ef28927a46d40d"},
{file = "grpcio-1.48.0-cp36-cp36m-linux_armv7l.whl", hash = "sha256:2138c50331232f56178c2b36dcfa6ad67aad705fe410955f3b2a53d722191b89"},
{file = "grpcio-1.48.0-cp36-cp36m-macosx_10_10_x86_64.whl", hash = "sha256:af2d80f142da2a6af45204a5ca2374e2747af07a99de54a1164111e169a761ff"},
{file = "grpcio-1.48.0-cp36-cp36m-manylinux_2_17_aarch64.whl", hash = "sha256:59284bd4cdf47c147c26d91aca693765318d524328f6ece2a1a0b85a12a362af"},
{file = "grpcio-1.48.0-cp36-cp36m-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:cc3ebfe356c0c6750379cd194bf2b7e5d1d2f29db1832358f05a73e9290db98c"},
{file = "grpcio-1.48.0-cp36-cp36m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dc2619a31339e1c53731f54761f1a2cb865d3421f690e00ef3e92f90d2a0c5ae"},
{file = "grpcio-1.48.0-cp36-cp36m-musllinux_1_1_i686.whl", hash = "sha256:7df637405de328a54c1c8c08a3206f974c7a577730f90644af4c3400b7bfde2d"},
{file = "grpcio-1.48.0-cp36-cp36m-musllinux_1_1_x86_64.whl", hash = "sha256:9e73b95969a579798bfbeb85d376695cce5172357fb52e450467ceb8e7365152"},
{file = "grpcio-1.48.0-cp36-cp36m-win32.whl", hash = "sha256:059e9d58b5aba7fb9eabe3a4d2ac49e1dcbc2b54b0f166f6475e40b7f4435343"},
{file = "grpcio-1.48.0-cp36-cp36m-win_amd64.whl", hash = "sha256:7cebcf645170f0c82ef71769544f9ac4515993a4d367f5900aba2eb4ecd2a32f"},
{file = "grpcio-1.48.0-cp37-cp37m-linux_armv7l.whl", hash = "sha256:8af3a8845df35b838104d6fb1ae7f4969d248cf037fa2794916d31e917346f72"},
{file = "grpcio-1.48.0-cp37-cp37m-macosx_10_10_x86_64.whl", hash = "sha256:a1ef40975ec9ced6c17ce7fbec9825823da782fa606f0b92392646ff3886f198"},
{file = "grpcio-1.48.0-cp37-cp37m-manylinux_2_17_aarch64.whl", hash = "sha256:7cccbf6db31f2a78e1909047ff69620f94a4e6e53251858e9502fbbff5714b48"},
{file = "grpcio-1.48.0-cp37-cp37m-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1f3f142579f58def64c0850f0bb0eb1b425ae885f5669dda5b73ade64ad2b753"},
{file = "grpcio-1.48.0-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:656c6f6f7b815bca3054780b8cdfa1e4e37cd36c887a48558d00c2cf85f31697"},
{file = "grpcio-1.48.0-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:cba4538e8a2ef123ea570e7b1d62162e158963c2471e35d79eb9690c971a10c0"},
{file = "grpcio-1.48.0-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:9daa67820fafceec6194ed1686c1783816e62d6756ff301ba93e682948836846"},
{file = "grpcio-1.48.0-cp37-cp37m-win32.whl", hash = "sha256:7ec264a7fb413e0c804a7a48a6f7d7212742955a60724c44d793da35a8f30873"},
{file = "grpcio-1.48.0-cp37-cp37m-win_amd64.whl", hash = "sha256:a2b1b33b92359388b8164807313dcbb3317101b038a5d54342982560329d958f"},
{file = "grpcio-1.48.0-cp38-cp38-linux_armv7l.whl", hash = "sha256:7b820696a5ce7b98f459f234698cb323f89b355373789188efa126d7f47a2a92"},
{file = "grpcio-1.48.0-cp38-cp38-macosx_10_10_x86_64.whl", hash = "sha256:e4dfae66ebc165c46c5b7048eb554472ee72fbaab2c2c2da7f9b1621c81e077c"},
{file = "grpcio-1.48.0-cp38-cp38-manylinux_2_17_aarch64.whl", hash = "sha256:f7115038edce33b494e0138b0bd31a2eb6595d45e2eed23be46bc32886feb741"},
{file = "grpcio-1.48.0-cp38-cp38-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b4e996282238943ca114628255be61980e38b25f73a08ae2ffd02b63eaf70d3a"},
{file = "grpcio-1.48.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:13dad31f5155fa555d393511cc8108c41b1b5b54dc4c24c27d4694ddd7a78fad"},
{file = "grpcio-1.48.0-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:c84b9d90b2641963de98b35bb7a2a51f78119fe5bd00ef27246ba9f4f0835e36"},
{file = "grpcio-1.48.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:41b65166779d7dafac4c98380ac19f690f1c5fe18083a71d370df87b24dd30ff"},
{file = "grpcio-1.48.0-cp38-cp38-win32.whl", hash = "sha256:b890e5f5fbc21cb994894f73ecb2faaa66697d8debcb228a5adb0622b9bec3b2"},
{file = "grpcio-1.48.0-cp38-cp38-win_amd64.whl", hash = "sha256:5fe3af539d2f50891ed93aed3064ffbcc38bf848aa3f7ed1fbedcce139c57302"},
{file = "grpcio-1.48.0-cp39-cp39-linux_armv7l.whl", hash = "sha256:a4ed57f4e3d91259551e6765782b22d9e8b8178fec43ebf8e1b2c392c4ced37b"},
{file = "grpcio-1.48.0-cp39-cp39-macosx_10_10_x86_64.whl", hash = "sha256:60843d8184e171886dd7a93d6672e2ef0b08dfd4f88da7421c10b46b6e031ac4"},
{file = "grpcio-1.48.0-cp39-cp39-manylinux_2_17_aarch64.whl", hash = "sha256:0ecba22f25ccde2442be7e7dd7fa746905d628f03312b4a0c9961f0d99771f53"},
{file = "grpcio-1.48.0-cp39-cp39-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:34f5917f0c49a04633dc12d483c8aee6f6d9f69133b700214d3703f72a72f501"},
{file = "grpcio-1.48.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f4c4ad8ad7e2cf3a272cbc96734d56635e6543939022f17e0c4487f7d2a45bf9"},
{file = "grpcio-1.48.0-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:111fb2f5f4a069f331ae23106145fd16dd4e1112ca223858a922068614dac6d2"},
{file = "grpcio-1.48.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:beb0573daa49889efcfea0a6e995b4f39d481aa1b94e1257617406ef417b56a6"},
{file = "grpcio-1.48.0-cp39-cp39-win32.whl", hash = "sha256:ce70254a082cb767217b2fdee374cc79199d338d46140753438cd6d67c609b2f"},
{file = "grpcio-1.48.0-cp39-cp39-win_amd64.whl", hash = "sha256:ae3fd135666448058fe277d93c10e0f18345fbcbb015c4642de2fa3db6f0c205"},
{file = "grpcio-1.48.0.tar.gz", hash = "sha256:eaf4bb73819863440727195411ab3b5c304f6663625e66f348e91ebe0a039306"},
]
h11 = [
{file = "h11-0.12.0-py3-none-any.whl", hash = "sha256:36a3cb8c0a032f56e2da7084577878a035d3b61d104230d4bd49c0c6b555a9c6"},
@@ -1579,49 +1572,49 @@ jieba = [
{file = "jieba-0.42.1.tar.gz", hash = "sha256:055ca12f62674fafed09427f176506079bc135638a14e23e25be909131928db2"},
]
kiwisolver = [
{file = "kiwisolver-1.4.3-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:fd2842a0faed9ab9aba0922c951906132d9384be89690570f0ed18cd4f20e658"},
{file = "kiwisolver-1.4.3-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:caa59e2cae0e23b1e225447d7a9ddb0f982f42a6a22d497a484dfe62a06f7c0e"},
{file = "kiwisolver-1.4.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:1d2c744aeedce22c122bb42d176b4aa6d063202a05a4abdacb3e413c214b3694"},
{file = "kiwisolver-1.4.3-cp310-cp310-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:afe173ac2646c2636305ab820cc0380b22a00a7bca4290452e7166b4f4fa49d0"},
{file = "kiwisolver-1.4.3-cp310-cp310-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:40240da438c0ebfe2aa76dd04b844effac6679423df61adbe3437d32f23468d9"},
{file = "kiwisolver-1.4.3-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:21a3a98f0a21fc602663ca9bce2b12a4114891bdeba2dea1e9ad84db59892fca"},
{file = "kiwisolver-1.4.3-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:51078855a16b7a4984ed2067b54e35803d18bca9861cb60c60f6234b50869a56"},
{file = "kiwisolver-1.4.3-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c16635f8dddbeb1b827977d0b00d07b644b040aeb9ff8607a9fc0997afa3e567"},
{file = "kiwisolver-1.4.3-cp310-cp310-win32.whl", hash = "sha256:2d76780d9c65c7529cedd49fa4802d713e60798d8dc3b0d5b12a0a8f38cca51c"},
{file = "kiwisolver-1.4.3-cp310-cp310-win_amd64.whl", hash = "sha256:3a297d77b3d6979693f5948df02b89431ae3645ec95865e351fb45578031bdae"},
{file = "kiwisolver-1.4.3-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:ca3eefb02ef17257fae8b8555c85e7c1efdfd777f671384b0e4ef27409b02720"},
{file = "kiwisolver-1.4.3-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d248c46c0aa406695bda2abf99632db991f8b3a6d46018721a2892312a99f069"},
{file = "kiwisolver-1.4.3-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:cb55258931448d61e2d50187de4ee66fc9d9f34908b524949b8b2b93d0c57136"},
{file = "kiwisolver-1.4.3-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:86bcf0009f2012847a688f2f4f9b16203ca4c835979a02549aa0595d9f457cc8"},
{file = "kiwisolver-1.4.3-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:e7cf940af5fee00a92e281eb157abe8770227a5255207818ea9a34e54a29f5b2"},
{file = "kiwisolver-1.4.3-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:dd22085446f3eca990d12a0878eeb5199dc9553b2e71716bfe7bed9915a472ab"},
{file = "kiwisolver-1.4.3-cp37-cp37m-win32.whl", hash = "sha256:d2578e5149ff49878934debfacf5c743fab49eca5ecdb983d0b218e1e554c498"},
{file = "kiwisolver-1.4.3-cp37-cp37m-win_amd64.whl", hash = "sha256:5fb73cc8a34baba1dfa546ae83b9c248ef6150c238b06fc53d2773685b67ec67"},
{file = "kiwisolver-1.4.3-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:f70f3d028794e31cf9d1a822914efc935aadb2438ec4e8d4871d95eb1ce032d6"},
{file = "kiwisolver-1.4.3-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:71af5b43e4fa286a35110fc5bb740fdeae2b36ca79fbcf0a54237485baeee8be"},
{file = "kiwisolver-1.4.3-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:26b5a70bdab09e6a2f40babc4f8f992e3771751e144bda1938084c70d3001c09"},
{file = "kiwisolver-1.4.3-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1858ad3cb686eccc7c6b7c5eac846a1cfd45aacb5811b2cf575e80b208f5622a"},
{file = "kiwisolver-1.4.3-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:4dc350cb65fe4e3f737d50f0465fa6ea0dcae0e5722b7edf5d5b0a0e3cd2c3c7"},
{file = "kiwisolver-1.4.3-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:007799c7fa934646318fc128b033bb6e6baabe7fbad521bfb2279aac26225cd7"},
{file = "kiwisolver-1.4.3-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:46fb56fde006b7ef5f8eaa3698299b0ea47444238b869ff3ced1426aa9fedcb5"},
{file = "kiwisolver-1.4.3-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:b9eb88593159a53a5ee0b0159daee531ff7dd9c87fa78f5d807ca059c7eb1b2b"},
{file = "kiwisolver-1.4.3-cp38-cp38-win32.whl", hash = "sha256:3b1dcbc49923ac3c973184a82832e1f018dec643b1e054867d04a3a22255ec6a"},
{file = "kiwisolver-1.4.3-cp38-cp38-win_amd64.whl", hash = "sha256:7118ca592d25b2957ff7b662bc0fe4f4c2b5d5b27814b9b1bc9f2fb249a970e7"},
{file = "kiwisolver-1.4.3-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:747190fcdadc377263223f8f72b038381b3b549a8a3df5baf4d067da4749b046"},
{file = "kiwisolver-1.4.3-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:fd628e63ffdba0112e3ddf1b1e9f3db29dd8262345138e08f4938acbc6d0805a"},
{file = "kiwisolver-1.4.3-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:22ccba48abae827a0f952a78a7b1a7ff01866131e5bbe1f826ce9bda406bf051"},
{file = "kiwisolver-1.4.3-cp39-cp39-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:af24b21c2283ca69c416a8a42cde9764dc36c63d3389645d28c69b0e93db3cd7"},
{file = "kiwisolver-1.4.3-cp39-cp39-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:547111ef7cf13d73546c2de97ce434935626c897bdec96a578ca100b5fcd694b"},
{file = "kiwisolver-1.4.3-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:84f85adfebd7d3c3db649efdf73659e1677a2cf3fa6e2556a3f373578af14bf7"},
{file = "kiwisolver-1.4.3-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ffd7cf165ff71afb202b3f36daafbf298932bee325aac9f58e1c9cd55838bef0"},
{file = "kiwisolver-1.4.3-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:6b3136eecf7e1b4a4d23e4b19d6c4e7a8e0b42d55f30444e3c529700cdacaa0d"},
{file = "kiwisolver-1.4.3-cp39-cp39-win32.whl", hash = "sha256:46c6e5018ba31d5ee7582f323d8661498a154dea1117486a571db4c244531f24"},
{file = "kiwisolver-1.4.3-cp39-cp39-win_amd64.whl", hash = "sha256:8395064d63b26947fa2c9faeea9c3eee35e52148c5339c37987e1d96fbf009b3"},
{file = "kiwisolver-1.4.3-pp37-pypy37_pp73-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:325fa1b15098e44fe4590a6c5c09a212ca10c6ebb5d96f7447d675f6c8340e4e"},
{file = "kiwisolver-1.4.3-pp37-pypy37_pp73-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:654280c5f41831ddcc5a331c0e3ce2e480bbc3d7c93c18ecf6236313aae2d61a"},
{file = "kiwisolver-1.4.3-pp37-pypy37_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1ae7aa0784aeadfbd693c27993727792fbe1455b84d49970bad5886b42976b18"},
{file = "kiwisolver-1.4.3-pp37-pypy37_pp73-win_amd64.whl", hash = "sha256:130c6c35eded399d3967cf8a542c20b671f5ba85bd6f210f8b939f868360e9eb"},
{file = "kiwisolver-1.4.3.tar.gz", hash = "sha256:ab8a15c2750ae8d53e31f77a94f846d0a00772240f1c12817411fa2344351f86"},
{file = "kiwisolver-1.4.4-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:2f5e60fabb7343a836360c4f0919b8cd0d6dbf08ad2ca6b9cf90bf0c76a3c4f6"},
{file = "kiwisolver-1.4.4-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:10ee06759482c78bdb864f4109886dff7b8a56529bc1609d4f1112b93fe6423c"},
{file = "kiwisolver-1.4.4-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:c79ebe8f3676a4c6630fd3f777f3cfecf9289666c84e775a67d1d358578dc2e3"},
{file = "kiwisolver-1.4.4-cp310-cp310-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:abbe9fa13da955feb8202e215c4018f4bb57469b1b78c7a4c5c7b93001699938"},
{file = "kiwisolver-1.4.4-cp310-cp310-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:7577c1987baa3adc4b3c62c33bd1118c3ef5c8ddef36f0f2c950ae0b199e100d"},
{file = "kiwisolver-1.4.4-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f8ad8285b01b0d4695102546b342b493b3ccc6781fc28c8c6a1bb63e95d22f09"},
{file = "kiwisolver-1.4.4-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8ed58b8acf29798b036d347791141767ccf65eee7f26bde03a71c944449e53de"},
{file = "kiwisolver-1.4.4-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a68b62a02953b9841730db7797422f983935aeefceb1679f0fc85cbfbd311c32"},
{file = "kiwisolver-1.4.4-cp310-cp310-win32.whl", hash = "sha256:e92a513161077b53447160b9bd8f522edfbed4bd9759e4c18ab05d7ef7e49408"},
{file = "kiwisolver-1.4.4-cp310-cp310-win_amd64.whl", hash = "sha256:3fe20f63c9ecee44560d0e7f116b3a747a5d7203376abeea292ab3152334d004"},
{file = "kiwisolver-1.4.4-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:62ac9cc684da4cf1778d07a89bf5f81b35834cb96ca523d3a7fb32509380cbf6"},
{file = "kiwisolver-1.4.4-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:41dae968a94b1ef1897cb322b39360a0812661dba7c682aa45098eb8e193dbdf"},
{file = "kiwisolver-1.4.4-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:02f79693ec433cb4b5f51694e8477ae83b3205768a6fb48ffba60549080e295b"},
{file = "kiwisolver-1.4.4-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d0611a0a2a518464c05ddd5a3a1a0e856ccc10e67079bb17f265ad19ab3c7597"},
{file = "kiwisolver-1.4.4-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:db5283d90da4174865d520e7366801a93777201e91e79bacbac6e6927cbceede"},
{file = "kiwisolver-1.4.4-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:1041feb4cda8708ce73bb4dcb9ce1ccf49d553bf87c3954bdfa46f0c3f77252c"},
{file = "kiwisolver-1.4.4-cp37-cp37m-win32.whl", hash = "sha256:a553dadda40fef6bfa1456dc4be49b113aa92c2a9a9e8711e955618cd69622e3"},
{file = "kiwisolver-1.4.4-cp37-cp37m-win_amd64.whl", hash = "sha256:03baab2d6b4a54ddbb43bba1a3a2d1627e82d205c5cf8f4c924dc49284b87166"},
{file = "kiwisolver-1.4.4-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:841293b17ad704d70c578f1f0013c890e219952169ce8a24ebc063eecf775454"},
{file = "kiwisolver-1.4.4-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:f4f270de01dd3e129a72efad823da90cc4d6aafb64c410c9033aba70db9f1ff0"},
{file = "kiwisolver-1.4.4-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:f9f39e2f049db33a908319cf46624a569b36983c7c78318e9726a4cb8923b26c"},
{file = "kiwisolver-1.4.4-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c97528e64cb9ebeff9701e7938653a9951922f2a38bd847787d4a8e498cc83ae"},
{file = "kiwisolver-1.4.4-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1d1573129aa0fd901076e2bfb4275a35f5b7aa60fbfb984499d661ec950320b0"},
{file = "kiwisolver-1.4.4-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:ad881edc7ccb9d65b0224f4e4d05a1e85cf62d73aab798943df6d48ab0cd79a1"},
{file = "kiwisolver-1.4.4-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:b428ef021242344340460fa4c9185d0b1f66fbdbfecc6c63eff4b7c29fad429d"},
{file = "kiwisolver-1.4.4-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:2e407cb4bd5a13984a6c2c0fe1845e4e41e96f183e5e5cd4d77a857d9693494c"},
{file = "kiwisolver-1.4.4-cp38-cp38-win32.whl", hash = "sha256:75facbe9606748f43428fc91a43edb46c7ff68889b91fa31f53b58894503a191"},
{file = "kiwisolver-1.4.4-cp38-cp38-win_amd64.whl", hash = "sha256:5bce61af018b0cb2055e0e72e7d65290d822d3feee430b7b8203d8a855e78766"},
{file = "kiwisolver-1.4.4-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:8c808594c88a025d4e322d5bb549282c93c8e1ba71b790f539567932722d7bd8"},
{file = "kiwisolver-1.4.4-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:f0a71d85ecdd570ded8ac3d1c0f480842f49a40beb423bb8014539a9f32a5897"},
{file = "kiwisolver-1.4.4-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:b533558eae785e33e8c148a8d9921692a9fe5aa516efbdff8606e7d87b9d5824"},
{file = "kiwisolver-1.4.4-cp39-cp39-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:efda5fc8cc1c61e4f639b8067d118e742b812c930f708e6667a5ce0d13499e29"},
{file = "kiwisolver-1.4.4-cp39-cp39-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:7c43e1e1206cd421cd92e6b3280d4385d41d7166b3ed577ac20444b6995a445f"},
{file = "kiwisolver-1.4.4-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bc8d3bd6c72b2dd9decf16ce70e20abcb3274ba01b4e1c96031e0c4067d1e7cd"},
{file = "kiwisolver-1.4.4-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:4ea39b0ccc4f5d803e3337dd46bcce60b702be4d86fd0b3d7531ef10fd99a1ac"},
{file = "kiwisolver-1.4.4-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:968f44fdbf6dd757d12920d63b566eeb4d5b395fd2d00d29d7ef00a00582aac9"},
{file = "kiwisolver-1.4.4-cp39-cp39-win32.whl", hash = "sha256:da7e547706e69e45d95e116e6939488d62174e033b763ab1496b4c29b76fabea"},
{file = "kiwisolver-1.4.4-cp39-cp39-win_amd64.whl", hash = "sha256:ba59c92039ec0a66103b1d5fe588fa546373587a7d68f5c96f743c3396afc04b"},
{file = "kiwisolver-1.4.4-pp37-pypy37_pp73-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:91672bacaa030f92fc2f43b620d7b337fd9a5af28b0d6ed3f77afc43c4a64b5a"},
{file = "kiwisolver-1.4.4-pp37-pypy37_pp73-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:787518a6789009c159453da4d6b683f468ef7a65bbde796bcea803ccf191058d"},
{file = "kiwisolver-1.4.4-pp37-pypy37_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:da152d8cdcab0e56e4f45eb08b9aea6455845ec83172092f09b0e077ece2cf7a"},
{file = "kiwisolver-1.4.4-pp37-pypy37_pp73-win_amd64.whl", hash = "sha256:ecb1fa0db7bf4cff9dac752abb19505a233c7f16684c5826d1f11ebd9472b871"},
{file = "kiwisolver-1.4.4.tar.gz", hash = "sha256:d41997519fcba4a1e46eb4a2fe31bc12f0ff957b2b81bac28db24744f333e955"},
]
loguru = [
{file = "loguru-0.6.0-py3-none-any.whl", hash = "sha256:4e2414d534a2ab57573365b3e6d0234dfb1d84b68b7f3b948e6fb743860a77c3"},
@@ -1858,28 +1851,28 @@ nonebot2 = [
{file = "nonebot2-2.0.0b4.tar.gz", hash = "sha256:fe57242138b3ab7019770566c23f7b39c53e0159b81ec7bd5b34cf6b1ff9eb13"},
]
numpy = [
{file = "numpy-1.22.4-cp310-cp310-macosx_10_14_x86_64.whl", hash = "sha256:ba9ead61dfb5d971d77b6c131a9dbee62294a932bf6a356e48c75ae684e635b3"},
{file = "numpy-1.22.4-cp310-cp310-macosx_10_15_x86_64.whl", hash = "sha256:1ce7ab2053e36c0a71e7a13a7475bd3b1f54750b4b433adc96313e127b870887"},
{file = "numpy-1.22.4-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:7228ad13744f63575b3a972d7ee4fd61815b2879998e70930d4ccf9ec721dce0"},
{file = "numpy-1.22.4-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:43a8ca7391b626b4c4fe20aefe79fec683279e31e7c79716863b4b25021e0e74"},
{file = "numpy-1.22.4-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a911e317e8c826ea632205e63ed8507e0dc877dcdc49744584dfc363df9ca08c"},
{file = "numpy-1.22.4-cp310-cp310-win32.whl", hash = "sha256:9ce7df0abeabe7fbd8ccbf343dc0db72f68549856b863ae3dd580255d009648e"},
{file = "numpy-1.22.4-cp310-cp310-win_amd64.whl", hash = "sha256:3e1ffa4748168e1cc8d3cde93f006fe92b5421396221a02f2274aab6ac83b077"},
{file = "numpy-1.22.4-cp38-cp38-macosx_10_15_x86_64.whl", hash = "sha256:59d55e634968b8f77d3fd674a3cf0b96e85147cd6556ec64ade018f27e9479e1"},
{file = "numpy-1.22.4-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:c1d937820db6e43bec43e8d016b9b3165dcb42892ea9f106c70fb13d430ffe72"},
{file = "numpy-1.22.4-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d4c5d5eb2ec8da0b4f50c9a843393971f31f1d60be87e0fb0917a49133d257d6"},
{file = "numpy-1.22.4-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:64f56fc53a2d18b1924abd15745e30d82a5782b2cab3429aceecc6875bd5add0"},
{file = "numpy-1.22.4-cp38-cp38-win32.whl", hash = "sha256:fb7a980c81dd932381f8228a426df8aeb70d59bbcda2af075b627bbc50207cba"},
{file = "numpy-1.22.4-cp38-cp38-win_amd64.whl", hash = "sha256:e96d7f3096a36c8754207ab89d4b3282ba7b49ea140e4973591852c77d09eb76"},
{file = "numpy-1.22.4-cp39-cp39-macosx_10_14_x86_64.whl", hash = "sha256:4c6036521f11a731ce0648f10c18ae66d7143865f19f7299943c985cdc95afb5"},
{file = "numpy-1.22.4-cp39-cp39-macosx_10_15_x86_64.whl", hash = "sha256:b89bf9b94b3d624e7bb480344e91f68c1c6c75f026ed6755955117de00917a7c"},
{file = "numpy-1.22.4-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:2d487e06ecbf1dc2f18e7efce82ded4f705f4bd0cd02677ffccfb39e5c284c7e"},
{file = "numpy-1.22.4-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f3eb268dbd5cfaffd9448113539e44e2dd1c5ca9ce25576f7c04a5453edc26fa"},
{file = "numpy-1.22.4-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:37431a77ceb9307c28382c9773da9f306435135fae6b80b62a11c53cfedd8802"},
{file = "numpy-1.22.4-cp39-cp39-win32.whl", hash = "sha256:cc7f00008eb7d3f2489fca6f334ec19ca63e31371be28fd5dad955b16ec285bd"},
{file = "numpy-1.22.4-cp39-cp39-win_amd64.whl", hash = "sha256:f0725df166cf4785c0bc4cbfb320203182b1ecd30fee6e541c8752a92df6aa32"},
{file = "numpy-1.22.4-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0791fbd1e43bf74b3502133207e378901272f3c156c4df4954cad833b1380207"},
{file = "numpy-1.22.4.zip", hash = "sha256:425b390e4619f58d8526b3dcf656dde069133ae5c240229821f01b5f44ea07af"},
{file = "numpy-1.23.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:b15c3f1ed08df4980e02cc79ee058b788a3d0bef2fb3c9ca90bb8cbd5b8a3a04"},
{file = "numpy-1.23.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:9ce242162015b7e88092dccd0e854548c0926b75c7924a3495e02c6067aba1f5"},
{file = "numpy-1.23.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e0d7447679ae9a7124385ccf0ea990bb85bb869cef217e2ea6c844b6a6855073"},
{file = "numpy-1.23.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3119daed207e9410eaf57dcf9591fdc68045f60483d94956bee0bfdcba790953"},
{file = "numpy-1.23.1-cp310-cp310-win32.whl", hash = "sha256:3ab67966c8d45d55a2bdf40701536af6443763907086c0a6d1232688e27e5447"},
{file = "numpy-1.23.1-cp310-cp310-win_amd64.whl", hash = "sha256:1865fdf51446839ca3fffaab172461f2b781163f6f395f1aed256b1ddc253622"},
{file = "numpy-1.23.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:aeba539285dcf0a1ba755945865ec61240ede5432df41d6e29fab305f4384db2"},
{file = "numpy-1.23.1-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:7e8229f3687cdadba2c4faef39204feb51ef7c1a9b669247d49a24f3e2e1617c"},
{file = "numpy-1.23.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:68b69f52e6545af010b76516f5daaef6173e73353e3295c5cb9f96c35d755641"},
{file = "numpy-1.23.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1408c3527a74a0209c781ac82bde2182b0f0bf54dea6e6a363fe0cc4488a7ce7"},
{file = "numpy-1.23.1-cp38-cp38-win32.whl", hash = "sha256:47f10ab202fe4d8495ff484b5561c65dd59177949ca07975663f4494f7269e3e"},
{file = "numpy-1.23.1-cp38-cp38-win_amd64.whl", hash = "sha256:37e5ebebb0eb54c5b4a9b04e6f3018e16b8ef257d26c8945925ba8105008e645"},
{file = "numpy-1.23.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:173f28921b15d341afadf6c3898a34f20a0569e4ad5435297ba262ee8941e77b"},
{file = "numpy-1.23.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:876f60de09734fbcb4e27a97c9a286b51284df1326b1ac5f1bf0ad3678236b22"},
{file = "numpy-1.23.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:35590b9c33c0f1c9732b3231bb6a72d1e4f77872390c47d50a615686ae7ed3fd"},
{file = "numpy-1.23.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a35c4e64dfca659fe4d0f1421fc0f05b8ed1ca8c46fb73d9e5a7f175f85696bb"},
{file = "numpy-1.23.1-cp39-cp39-win32.whl", hash = "sha256:c2f91f88230042a130ceb1b496932aa717dcbd665350beb821534c5c7e15881c"},
{file = "numpy-1.23.1-cp39-cp39-win_amd64.whl", hash = "sha256:37ece2bd095e9781a7156852e43d18044fd0d742934833335599c583618181b9"},
{file = "numpy-1.23.1-pp38-pypy38_pp73-macosx_10_9_x86_64.whl", hash = "sha256:8002574a6b46ac3b5739a003b5233376aeac5163e5dcd43dd7ad062f3e186129"},
{file = "numpy-1.23.1-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5d732d17b8a9061540a10fda5bfeabca5785700ab5469a5e9b93aca5e2d3a5fb"},
{file = "numpy-1.23.1-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:55df0f7483b822855af67e38fb3a526e787adf189383b4934305565d71c4b148"},
{file = "numpy-1.23.1.tar.gz", hash = "sha256:d748ef349bfef2e1194b59da37ed5a29c19ea8d7e6342019921ba2ba4fd8b624"},
]
opencv-python = [
{file = "opencv-python-4.6.0.66.tar.gz", hash = "sha256:c5bfae41ad4031e66bb10ec4a0a2ffd3e514d092652781e8b1ac98d1b59f1158"},
@@ -1899,57 +1892,77 @@ pathspec = [
{file = "pathspec-0.9.0.tar.gz", hash = "sha256:e564499435a2673d586f6b2130bb5b95f04a3ba06f81b8f895b651a3c76aabb1"},
]
pillow = [
{file = "Pillow-9.1.1-cp310-cp310-macosx_10_10_x86_64.whl", hash = "sha256:42dfefbef90eb67c10c45a73a9bc1599d4dac920f7dfcbf4ec6b80cb620757fe"},
{file = "Pillow-9.1.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:ffde4c6fabb52891d81606411cbfaf77756e3b561b566efd270b3ed3791fde4e"},
{file = "Pillow-9.1.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9c857532c719fb30fafabd2371ce9b7031812ff3889d75273827633bca0c4602"},
{file = "Pillow-9.1.1-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:59789a7d06c742e9d13b883d5e3569188c16acb02eeed2510fd3bfdbc1bd1530"},
{file = "Pillow-9.1.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4d45dbe4b21a9679c3e8b3f7f4f42a45a7d3ddff8a4a16109dff0e1da30a35b2"},
{file = "Pillow-9.1.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:e9ed59d1b6ee837f4515b9584f3d26cf0388b742a11ecdae0d9237a94505d03a"},
{file = "Pillow-9.1.1-cp310-cp310-win32.whl", hash = "sha256:b3fe2ff1e1715d4475d7e2c3e8dabd7c025f4410f79513b4ff2de3d51ce0fa9c"},
{file = "Pillow-9.1.1-cp310-cp310-win_amd64.whl", hash = "sha256:5b650dbbc0969a4e226d98a0b440c2f07a850896aed9266b6fedc0f7e7834108"},
{file = "Pillow-9.1.1-cp37-cp37m-macosx_10_10_x86_64.whl", hash = "sha256:0b4d5ad2cd3a1f0d1df882d926b37dbb2ab6c823ae21d041b46910c8f8cd844b"},
{file = "Pillow-9.1.1-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9370d6744d379f2de5d7fa95cdbd3a4d92f0b0ef29609b4b1687f16bc197063d"},
{file = "Pillow-9.1.1-cp37-cp37m-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b761727ed7d593e49671d1827044b942dd2f4caae6e51bab144d4accf8244a84"},
{file = "Pillow-9.1.1-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8a66fe50386162df2da701b3722781cbe90ce043e7d53c1fd6bd801bca6b48d4"},
{file = "Pillow-9.1.1-cp37-cp37m-win32.whl", hash = "sha256:2b291cab8a888658d72b575a03e340509b6b050b62db1f5539dd5cd18fd50578"},
{file = "Pillow-9.1.1-cp37-cp37m-win_amd64.whl", hash = "sha256:1d4331aeb12f6b3791911a6da82de72257a99ad99726ed6b63f481c0184b6fb9"},
{file = "Pillow-9.1.1-cp38-cp38-macosx_10_10_x86_64.whl", hash = "sha256:8844217cdf66eabe39567118f229e275f0727e9195635a15e0e4b9227458daaf"},
{file = "Pillow-9.1.1-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:b6617221ff08fbd3b7a811950b5c3f9367f6e941b86259843eab77c8e3d2b56b"},
{file = "Pillow-9.1.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:20d514c989fa28e73a5adbddd7a171afa5824710d0ab06d4e1234195d2a2e546"},
{file = "Pillow-9.1.1-cp38-cp38-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:088df396b047477dd1bbc7de6e22f58400dae2f21310d9e2ec2933b2ef7dfa4f"},
{file = "Pillow-9.1.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:53c27bd452e0f1bc4bfed07ceb235663a1df7c74df08e37fd6b03eb89454946a"},
{file = "Pillow-9.1.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:3f6c1716c473ebd1649663bf3b42702d0d53e27af8b64642be0dd3598c761fb1"},
{file = "Pillow-9.1.1-cp38-cp38-win32.whl", hash = "sha256:c67db410508b9de9c4694c57ed754b65a460e4812126e87f5052ecf23a011a54"},
{file = "Pillow-9.1.1-cp38-cp38-win_amd64.whl", hash = "sha256:f054b020c4d7e9786ae0404278ea318768eb123403b18453e28e47cdb7a0a4bf"},
{file = "Pillow-9.1.1-cp39-cp39-macosx_10_10_x86_64.whl", hash = "sha256:c17770a62a71718a74b7548098a74cd6880be16bcfff5f937f900ead90ca8e92"},
{file = "Pillow-9.1.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:f3f6a6034140e9e17e9abc175fc7a266a6e63652028e157750bd98e804a8ed9a"},
{file = "Pillow-9.1.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f372d0f08eff1475ef426344efe42493f71f377ec52237bf153c5713de987251"},
{file = "Pillow-9.1.1-cp39-cp39-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:09e67ef6e430f90caa093528bd758b0616f8165e57ed8d8ce014ae32df6a831d"},
{file = "Pillow-9.1.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:66daa16952d5bf0c9d5389c5e9df562922a59bd16d77e2a276e575d32e38afd1"},
{file = "Pillow-9.1.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:d78ca526a559fb84faaaf84da2dd4addef5edb109db8b81677c0bb1aad342601"},
{file = "Pillow-9.1.1-cp39-cp39-win32.whl", hash = "sha256:55e74faf8359ddda43fee01bffbc5bd99d96ea508d8a08c527099e84eb708f45"},
{file = "Pillow-9.1.1-cp39-cp39-win_amd64.whl", hash = "sha256:7c150dbbb4a94ea4825d1e5f2c5501af7141ea95825fadd7829f9b11c97aaf6c"},
{file = "Pillow-9.1.1-pp37-pypy37_pp73-macosx_10_10_x86_64.whl", hash = "sha256:769a7f131a2f43752455cc72f9f7a093c3ff3856bf976c5fb53a59d0ccc704f6"},
{file = "Pillow-9.1.1-pp37-pypy37_pp73-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:488f3383cf5159907d48d32957ac6f9ea85ccdcc296c14eca1a4e396ecc32098"},
{file = "Pillow-9.1.1-pp37-pypy37_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0b525a356680022b0af53385944026d3486fc8c013638cf9900eb87c866afb4c"},
{file = "Pillow-9.1.1-pp38-pypy38_pp73-macosx_10_10_x86_64.whl", hash = "sha256:6e760cf01259a1c0a50f3c845f9cad1af30577fd8b670339b1659c6d0e7a41dd"},
{file = "Pillow-9.1.1-pp38-pypy38_pp73-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a4165205a13b16a29e1ac57efeee6be2dfd5b5408122d59ef2145bc3239fa340"},
{file = "Pillow-9.1.1-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:937a54e5694684f74dcbf6e24cc453bfc5b33940216ddd8f4cd8f0f79167f765"},
{file = "Pillow-9.1.1-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:baf3be0b9446a4083cc0c5bb9f9c964034be5374b5bc09757be89f5d2fa247b8"},
{file = "Pillow-9.1.1.tar.gz", hash = "sha256:7502539939b53d7565f3d11d87c78e7ec900d3c72945d4ee0e2f250d598309a0"},
{file = "Pillow-9.2.0-cp310-cp310-macosx_10_10_x86_64.whl", hash = "sha256:a9c9bc489f8ab30906d7a85afac4b4944a572a7432e00698a7239f44a44e6efb"},
{file = "Pillow-9.2.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:510cef4a3f401c246cfd8227b300828715dd055463cdca6176c2e4036df8bd4f"},
{file = "Pillow-9.2.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7888310f6214f19ab2b6df90f3f06afa3df7ef7355fc025e78a3044737fab1f5"},
{file = "Pillow-9.2.0-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:831e648102c82f152e14c1a0938689dbb22480c548c8d4b8b248b3e50967b88c"},
{file = "Pillow-9.2.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1cc1d2451e8a3b4bfdb9caf745b58e6c7a77d2e469159b0d527a4554d73694d1"},
{file = "Pillow-9.2.0-cp310-cp310-manylinux_2_28_aarch64.whl", hash = "sha256:136659638f61a251e8ed3b331fc6ccd124590eeff539de57c5f80ef3a9594e58"},
{file = "Pillow-9.2.0-cp310-cp310-manylinux_2_28_x86_64.whl", hash = "sha256:6e8c66f70fb539301e064f6478d7453e820d8a2c631da948a23384865cd95544"},
{file = "Pillow-9.2.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:37ff6b522a26d0538b753f0b4e8e164fdada12db6c6f00f62145d732d8a3152e"},
{file = "Pillow-9.2.0-cp310-cp310-win32.whl", hash = "sha256:c79698d4cd9318d9481d89a77e2d3fcaeff5486be641e60a4b49f3d2ecca4e28"},
{file = "Pillow-9.2.0-cp310-cp310-win_amd64.whl", hash = "sha256:254164c57bab4b459f14c64e93df11eff5ded575192c294a0c49270f22c5d93d"},
{file = "Pillow-9.2.0-cp311-cp311-macosx_10_10_universal2.whl", hash = "sha256:408673ed75594933714482501fe97e055a42996087eeca7e5d06e33218d05aa8"},
{file = "Pillow-9.2.0-cp311-cp311-macosx_11_0_universal2.whl", hash = "sha256:727dd1389bc5cb9827cbd1f9d40d2c2a1a0c9b32dd2261db522d22a604a6eec9"},
{file = "Pillow-9.2.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:50dff9cc21826d2977ef2d2a205504034e3a4563ca6f5db739b0d1026658e004"},
{file = "Pillow-9.2.0-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:cb6259196a589123d755380b65127ddc60f4c64b21fc3bb46ce3a6ea663659b0"},
{file = "Pillow-9.2.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7b0554af24df2bf96618dac71ddada02420f946be943b181108cac55a7a2dcd4"},
{file = "Pillow-9.2.0-cp311-cp311-manylinux_2_28_aarch64.whl", hash = "sha256:15928f824870535c85dbf949c09d6ae7d3d6ac2d6efec80f3227f73eefba741c"},
{file = "Pillow-9.2.0-cp311-cp311-manylinux_2_28_x86_64.whl", hash = "sha256:bdd0de2d64688ecae88dd8935012c4a72681e5df632af903a1dca8c5e7aa871a"},
{file = "Pillow-9.2.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:d5b87da55a08acb586bad5c3aa3b86505f559b84f39035b233d5bf844b0834b1"},
{file = "Pillow-9.2.0-cp311-cp311-win32.whl", hash = "sha256:b6d5e92df2b77665e07ddb2e4dbd6d644b78e4c0d2e9272a852627cdba0d75cf"},
{file = "Pillow-9.2.0-cp311-cp311-win_amd64.whl", hash = "sha256:6bf088c1ce160f50ea40764f825ec9b72ed9da25346216b91361eef8ad1b8f8c"},
{file = "Pillow-9.2.0-cp37-cp37m-macosx_10_10_x86_64.whl", hash = "sha256:2c58b24e3a63efd22554c676d81b0e57f80e0a7d3a5874a7e14ce90ec40d3069"},
{file = "Pillow-9.2.0-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:eef7592281f7c174d3d6cbfbb7ee5984a671fcd77e3fc78e973d492e9bf0eb3f"},
{file = "Pillow-9.2.0-cp37-cp37m-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:dcd7b9c7139dc8258d164b55696ecd16c04607f1cc33ba7af86613881ffe4ac8"},
{file = "Pillow-9.2.0-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a138441e95562b3c078746a22f8fca8ff1c22c014f856278bdbdd89ca36cff1b"},
{file = "Pillow-9.2.0-cp37-cp37m-manylinux_2_28_aarch64.whl", hash = "sha256:93689632949aff41199090eff5474f3990b6823404e45d66a5d44304e9cdc467"},
{file = "Pillow-9.2.0-cp37-cp37m-manylinux_2_28_x86_64.whl", hash = "sha256:f3fac744f9b540148fa7715a435d2283b71f68bfb6d4aae24482a890aed18b59"},
{file = "Pillow-9.2.0-cp37-cp37m-win32.whl", hash = "sha256:fa768eff5f9f958270b081bb33581b4b569faabf8774726b283edb06617101dc"},
{file = "Pillow-9.2.0-cp37-cp37m-win_amd64.whl", hash = "sha256:69bd1a15d7ba3694631e00df8de65a8cb031911ca11f44929c97fe05eb9b6c1d"},
{file = "Pillow-9.2.0-cp38-cp38-macosx_10_10_x86_64.whl", hash = "sha256:030e3460861488e249731c3e7ab59b07c7853838ff3b8e16aac9561bb345da14"},
{file = "Pillow-9.2.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:74a04183e6e64930b667d321524e3c5361094bb4af9083db5c301db64cd341f3"},
{file = "Pillow-9.2.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2d33a11f601213dcd5718109c09a52c2a1c893e7461f0be2d6febc2879ec2402"},
{file = "Pillow-9.2.0-cp38-cp38-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1fd6f5e3c0e4697fa7eb45b6e93996299f3feee73a3175fa451f49a74d092b9f"},
{file = "Pillow-9.2.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a647c0d4478b995c5e54615a2e5360ccedd2f85e70ab57fbe817ca613d5e63b8"},
{file = "Pillow-9.2.0-cp38-cp38-manylinux_2_28_aarch64.whl", hash = "sha256:4134d3f1ba5f15027ff5c04296f13328fecd46921424084516bdb1b2548e66ff"},
{file = "Pillow-9.2.0-cp38-cp38-manylinux_2_28_x86_64.whl", hash = "sha256:bc431b065722a5ad1dfb4df354fb9333b7a582a5ee39a90e6ffff688d72f27a1"},
{file = "Pillow-9.2.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:1536ad017a9f789430fb6b8be8bf99d2f214c76502becc196c6f2d9a75b01b76"},
{file = "Pillow-9.2.0-cp38-cp38-win32.whl", hash = "sha256:2ad0d4df0f5ef2247e27fc790d5c9b5a0af8ade9ba340db4a73bb1a4a3e5fb4f"},
{file = "Pillow-9.2.0-cp38-cp38-win_amd64.whl", hash = "sha256:ec52c351b35ca269cb1f8069d610fc45c5bd38c3e91f9ab4cbbf0aebc136d9c8"},
{file = "Pillow-9.2.0-cp39-cp39-macosx_10_10_x86_64.whl", hash = "sha256:0ed2c4ef2451de908c90436d6e8092e13a43992f1860275b4d8082667fbb2ffc"},
{file = "Pillow-9.2.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:4ad2f835e0ad81d1689f1b7e3fbac7b01bb8777d5a985c8962bedee0cc6d43da"},
{file = "Pillow-9.2.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ea98f633d45f7e815db648fd7ff0f19e328302ac36427343e4432c84432e7ff4"},
{file = "Pillow-9.2.0-cp39-cp39-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7761afe0126d046974a01e030ae7529ed0ca6a196de3ec6937c11df0df1bc91c"},
{file = "Pillow-9.2.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9a54614049a18a2d6fe156e68e188da02a046a4a93cf24f373bffd977e943421"},
{file = "Pillow-9.2.0-cp39-cp39-manylinux_2_28_aarch64.whl", hash = "sha256:5aed7dde98403cd91d86a1115c78d8145c83078e864c1de1064f52e6feb61b20"},
{file = "Pillow-9.2.0-cp39-cp39-manylinux_2_28_x86_64.whl", hash = "sha256:13b725463f32df1bfeacbf3dd197fb358ae8ebcd8c5548faa75126ea425ccb60"},
{file = "Pillow-9.2.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:808add66ea764ed97d44dda1ac4f2cfec4c1867d9efb16a33d158be79f32b8a4"},
{file = "Pillow-9.2.0-cp39-cp39-win32.whl", hash = "sha256:337a74fd2f291c607d220c793a8135273c4c2ab001b03e601c36766005f36885"},
{file = "Pillow-9.2.0-cp39-cp39-win_amd64.whl", hash = "sha256:fac2d65901fb0fdf20363fbd345c01958a742f2dc62a8dd4495af66e3ff502a4"},
{file = "Pillow-9.2.0-pp37-pypy37_pp73-macosx_10_10_x86_64.whl", hash = "sha256:ad2277b185ebce47a63f4dc6302e30f05762b688f8dc3de55dbae4651872cdf3"},
{file = "Pillow-9.2.0-pp37-pypy37_pp73-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7c7b502bc34f6e32ba022b4a209638f9e097d7a9098104ae420eb8186217ebbb"},
{file = "Pillow-9.2.0-pp37-pypy37_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3d1f14f5f691f55e1b47f824ca4fdcb4b19b4323fe43cc7bb105988cad7496be"},
{file = "Pillow-9.2.0-pp37-pypy37_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:dfe4c1fedfde4e2fbc009d5ad420647f7730d719786388b7de0999bf32c0d9fd"},
{file = "Pillow-9.2.0-pp38-pypy38_pp73-macosx_10_10_x86_64.whl", hash = "sha256:f07f1f00e22b231dd3d9b9208692042e29792d6bd4f6639415d2f23158a80013"},
{file = "Pillow-9.2.0-pp38-pypy38_pp73-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1802f34298f5ba11d55e5bb09c31997dc0c6aed919658dfdf0198a2fe75d5490"},
{file = "Pillow-9.2.0-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:17d4cafe22f050b46d983b71c707162d63d796a1235cdf8b9d7a112e97b15bac"},
{file = "Pillow-9.2.0-pp38-pypy38_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:96b5e6874431df16aee0c1ba237574cb6dff1dcb173798faa6a9d8b399a05d0e"},
{file = "Pillow-9.2.0-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:0030fdbd926fb85844b8b92e2f9449ba89607231d3dd597a21ae72dc7fe26927"},
{file = "Pillow-9.2.0.tar.gz", hash = "sha256:75e636fd3e0fb872693f23ccb8a5ff2cd578801251f3a4f6854c6a5d437d3c04"},
]
platformdirs = [
{file = "platformdirs-2.5.2-py3-none-any.whl", hash = "sha256:027d8e83a2d7de06bbac4e5ef7e023c02b863d7ea5d079477e722bb41ab25788"},
{file = "platformdirs-2.5.2.tar.gz", hash = "sha256:58c8abb07dcb441e6ee4b11d8df0ac856038f944ab98b7be6b27b2a3c7feef19"},
]
playwright = [
{file = "playwright-1.22.0-py3-none-macosx_10_13_x86_64.whl", hash = "sha256:9506d582e1a36aa19b06f5b5f8ae154265ec2fc5039217cfe8dfd66edb1a7563"},
{file = "playwright-1.22.0-py3-none-macosx_11_0_arm64.whl", hash = "sha256:cd2e87b4d2a3d736bbd4a85ed7638c577dfc8098bcabd92d248440d51beac50b"},
{file = "playwright-1.22.0-py3-none-macosx_11_0_universal2.whl", hash = "sha256:a36dfaf9be0228985b7b001887f66dc61e8300970c0602ae9d5b191da510a982"},
{file = "playwright-1.22.0-py3-none-manylinux1_x86_64.whl", hash = "sha256:78ddf51d0c08d94edb6d2ddf7b262f0d87a1c0ab5e121bfefa1945214c308f95"},
{file = "playwright-1.22.0-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:217b2f623527ded15d602ccca5138395a1677c67399c4494844d5341420d34e0"},
{file = "playwright-1.22.0-py3-none-win32.whl", hash = "sha256:a4977211414532a525a057f8d461a7277fdfefa20eb577b2e0247523815ba521"},
{file = "playwright-1.22.0-py3-none-win_amd64.whl", hash = "sha256:bc7827fcc037a9a6f571328c524d50bfd46db4ebdc415ab8b8efafae30c1d597"},
{file = "playwright-1.24.0-py3-none-macosx_10_13_x86_64.whl", hash = "sha256:942c938a8fc8d5daa01cf4e2ca7dfd4cad9216e3e0af88992c083dd324f9210e"},
{file = "playwright-1.24.0-py3-none-macosx_11_0_arm64.whl", hash = "sha256:fbc74dd91c0c00d282d340b6d08d22010279cbe84052afe0f0854dc1e3a51315"},
{file = "playwright-1.24.0-py3-none-macosx_11_0_universal2.whl", hash = "sha256:976e59736502e28efe7bddb77fc9f01d3ccad9bf556c82e4208c194eea8c1eeb"},
{file = "playwright-1.24.0-py3-none-manylinux1_x86_64.whl", hash = "sha256:9caf9680f1791df94d0be2bfc9fcdc793efcb1986564c76f10accac13cc23bd4"},
{file = "playwright-1.24.0-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2060bdf6b3137f45e1cd5e1b7d70081cca82381b09251868aebfc01542e68a2d"},
{file = "playwright-1.24.0-py3-none-win32.whl", hash = "sha256:f496bb2ddd36698c85ff9ad3183953d0583afa952b35a1c74096e158b4735651"},
{file = "playwright-1.24.0-py3-none-win_amd64.whl", hash = "sha256:e4b876c05e773fd4d179cf15942bd7ac33fa7a0884d939485ade15695c70bfd4"},
]
proces = [
{file = "proces-0.1.2-py3-none-any.whl", hash = "sha256:82b765c70ebe790aae03159617710d03bcec3c98d6c7190f126bf307eecd0fd3"},
@@ -2076,7 +2089,7 @@ pygments = [
{file = "Pygments-2.12.0.tar.gz", hash = "sha256:5eb116118f9612ff1ee89ac96437bb6b49e8f04d8a13b514ba26f620208e26eb"},
]
pygtrie = [
{file = "pygtrie-2.4.2.tar.gz", hash = "sha256:43205559d28863358dbbf25045029f58e2ab357317a59b11f11ade278ac64692"},
{file = "pygtrie-2.5.0.tar.gz", hash = "sha256:203514ad826eb403dab1d2e2ddd034e0d1534bbe4dbe0213bb0593f66beba4e2"},
]
pyparsing = [
{file = "pyparsing-3.0.9-py3-none-any.whl", hash = "sha256:5026bae9a10eeaefb61dab2f09052b9f4307d44aee4eda64b309723d8d206bbc"},
@@ -2256,12 +2269,12 @@ rfc3986 = [
{file = "rfc3986-1.5.0.tar.gz", hash = "sha256:270aaf10d87d0d4e095063c65bf3ddbc6ee3d0b226328ce21e036f946e421835"},
]
rich = [
{file = "rich-12.4.4-py3-none-any.whl", hash = "sha256:d2bbd99c320a2532ac71ff6a3164867884357da3e3301f0240090c5d2fdac7ec"},
{file = "rich-12.4.4.tar.gz", hash = "sha256:4c586de507202505346f3e32d1363eb9ed6932f0c2f63184dea88983ff4971e2"},
{file = "rich-12.5.1-py3-none-any.whl", hash = "sha256:2eb4e6894cde1e017976d2975ac210ef515d7548bc595ba20e195fb9628acdeb"},
{file = "rich-12.5.1.tar.gz", hash = "sha256:63a5c5ce3673d3d5fbbf23cd87e11ab84b6b451436f1b7f19ec54b6bc36ed7ca"},
]
rsa = [
{file = "rsa-4.8-py3-none-any.whl", hash = "sha256:95c5d300c4e879ee69708c428ba566c59478fd653cc3a22243eeb8ed846950bb"},
{file = "rsa-4.8.tar.gz", hash = "sha256:5c6bd9dc7a543b7fe4304a631f8a8a3b674e2bbfc49c2ae96200cdbe55df6b17"},
{file = "rsa-4.9-py3-none-any.whl", hash = "sha256:90260d9058e514786967344d0ef75fa8727eed8a7d2e43ce9f4bcf1b536174f7"},
{file = "rsa-4.9.tar.gz", hash = "sha256:e38464a49c6c85d7f1351b0126661487a7e0a14a50f1675ec50eb34d4f20ef21"},
]
"ruamel.yaml" = [
{file = "ruamel.yaml-0.17.21-py3-none-any.whl", hash = "sha256:742b35d3d665023981bd6d16b3d24248ce5df75fdb4e2924e93a05c1f8b61ca7"},
@@ -2316,8 +2329,8 @@ scipy = [
{file = "scipy-1.6.1.tar.gz", hash = "sha256:c4fceb864890b6168e79b0e714c585dbe2fd4222768ee90bc1aa0f8218691b11"},
]
setuptools-scm = [
{file = "setuptools_scm-6.4.2-py3-none-any.whl", hash = "sha256:acea13255093849de7ccb11af9e1fb8bde7067783450cee9ef7a93139bddf6d4"},
{file = "setuptools_scm-6.4.2.tar.gz", hash = "sha256:6833ac65c6ed9711a4d5d2266f8024cfa07c533a0e55f4c12f6eff280a5a9e30"},
{file = "setuptools_scm-7.0.5-py3-none-any.whl", hash = "sha256:7930f720905e03ccd1e1d821db521bff7ec2ac9cf0ceb6552dd73d24a45d3b02"},
{file = "setuptools_scm-7.0.5.tar.gz", hash = "sha256:031e13af771d6f892b941adb6ea04545bbf91ebc5ce68c78aaf3fff6e1fb4844"},
]
sgmllib3k = [
{file = "sgmllib3k-1.0.0.tar.gz", hash = "sha256:7868fb1c8bfa764c1ac563d3cf369c381d1325d36124933a726f29fcdaa812e9"},
@@ -2379,12 +2392,12 @@ tomli = [
{file = "tomli-2.0.1.tar.gz", hash = "sha256:de526c12914f0c550d15924c62d72abc48d6fe7364aa87328337a31007fe8a4f"},
]
tomlkit = [
{file = "tomlkit-0.11.0-py3-none-any.whl", hash = "sha256:0f4050db66fd445b885778900ce4dd9aea8c90c4721141fde0d6ade893820ef1"},
{file = "tomlkit-0.11.0.tar.gz", hash = "sha256:71ceb10c0eefd8b8f11fe34e8a51ad07812cb1dc3de23247425fbc9ddc47b9dd"},
{file = "tomlkit-0.11.1-py3-none-any.whl", hash = "sha256:1c5bebdf19d5051e2e1de6cf70adfc5948d47221f097fcff7a3ffc91e953eaf5"},
{file = "tomlkit-0.11.1.tar.gz", hash = "sha256:61901f81ff4017951119cd0d1ed9b7af31c821d6845c8c477587bbdcd5e5854e"},
]
typing-extensions = [
{file = "typing_extensions-4.2.0-py3-none-any.whl", hash = "sha256:6657594ee297170d19f67d55c05852a874e7eb634f4f753dbd667855e07c1708"},
{file = "typing_extensions-4.2.0.tar.gz", hash = "sha256:f1c24655a0da0d1b67f07e17a5e6b2a105894e6824b92096378bb3668ef02376"},
{file = "typing_extensions-4.3.0-py3-none-any.whl", hash = "sha256:25642c956049920a5aa49edcdd6ab1e06d7e5d467fc00e0506c44ac86fbfca02"},
{file = "typing_extensions-4.3.0.tar.gz", hash = "sha256:e6d2677a32f47fc7eb2795db1dd15c1f34eff616bcaf2cfb5e997f854fa1c4a6"},
]
tzdata = [
{file = "tzdata-2022.1-py2.py3-none-any.whl", hash = "sha256:238e70234214138ed7b4e8a0fab0e5e13872edab3be586ab8198c407620e2ab9"},
@@ -2395,56 +2408,56 @@ tzlocal = [
{file = "tzlocal-4.2.tar.gz", hash = "sha256:ee5842fa3a795f023514ac2d801c4a81d1743bbe642e3940143326b3a00addd7"},
]
ujson = [
{file = "ujson-5.3.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:a933b3a238a48162c382e0ac338b97663d044b0485021b6670565a81e7b7ec98"},
{file = "ujson-5.3.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:612015c6e5a9bf041b89f1eaa8ab8682469b3a745a00c7c95bbbee8080f6b346"},
{file = "ujson-5.3.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a720b6eff73415249a3dd02e2b1b337de31bb9fa8220bd572dffba23066e538c"},
{file = "ujson-5.3.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c1408ea1704017289c3023928065233b90953aae3e1d7d06d6d6db667e9fe159"},
{file = "ujson-5.3.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:5192505798a5734a85c763eff11e6f6072d3595c337b52f72922b4e22fe66e2e"},
{file = "ujson-5.3.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:bad1471ccfa8d100a0bc513c6db587c38de99384f2aa54eec1016a131d63d3d9"},
{file = "ujson-5.3.0-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:b926f2f7a266db8f2c46498f0c2c9fcc7e53c8e0fa8bff7f08ad9c044723a2ec"},
{file = "ujson-5.3.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:ed9809bc36292e0d3632d50aae497b5827c1a2e07158f7d4d5c53e8e8662bf66"},
{file = "ujson-5.3.0-cp310-cp310-win32.whl", hash = "sha256:522b1d60872bb6368c14ac538adb55ca9d6c39a7a962832819ef1aafb3446ff5"},
{file = "ujson-5.3.0-cp310-cp310-win_amd64.whl", hash = "sha256:a609bb1cdda9748e6a8363039926dee5ea2bcc073412279615560b967f92a524"},
{file = "ujson-5.3.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:7455fc3d69315149b95fd011c01496a5e9442c9e7c4d202bed87c5c2e449ed05"},
{file = "ujson-5.3.0-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:865225a85e4ce48754d0036fdc0eb796b4aaf4f1e928f0efb9b4e1c081647a4c"},
{file = "ujson-5.3.0-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d553f31bceda492c2bda37f48873820d28f07608ae14409c5e9d6c3aa6694840"},
{file = "ujson-5.3.0-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a014531468b78c031aa04e5ca8b64385a6edb48a2e66ebf11093213c678fc383"},
{file = "ujson-5.3.0-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:b3e6431812d8008dce7b2546b1276f649f6c9aa44617762ebd3529a25092816c"},
{file = "ujson-5.3.0-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:089965f964d17905c48cdca88b982d525165e549b438ac86f194c6a9d852fd69"},
{file = "ujson-5.3.0-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:ca5eced4ae4ba1e2c9539fca6451694d31e0243de2acfcd6965e2b6e159ba29b"},
{file = "ujson-5.3.0-cp37-cp37m-win32.whl", hash = "sha256:a4fe193050b519ace09f7d053def30b99deadf650c18a8a874ea0f6c9a2992bc"},
{file = "ujson-5.3.0-cp37-cp37m-win_amd64.whl", hash = "sha256:e7961c493a982c03cffc9ce4dc2b23bed1375352296f946cc36ddeb5145fa62c"},
{file = "ujson-5.3.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:34592a3c9370745b093ebca60aee6d32f8e7abe3d5c12d54c7dba0b2f81cd863"},
{file = "ujson-5.3.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:510c3705b29bc3753ec9e6073b99000160320c1cf6e035884295401acb474dfa"},
{file = "ujson-5.3.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:034c07399dff35385ecc53caf9b1f12b3e203834de27b723daeb2cbb3e02ee7f"},
{file = "ujson-5.3.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5a87e1c05f1efc23c67bfa26be79f12c1f59f71a586b396068d5cf7eb78a2635"},
{file = "ujson-5.3.0-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:972c1850cc52e57ccdea70e3c069e2da5c6090e3ee18d167dff2618a8d7dd127"},
{file = "ujson-5.3.0-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:d45e86101a5cddd295d5870b02244fc87ecd9b8936f440acbd2bb30b4c1fe23c"},
{file = "ujson-5.3.0-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:decd32e8d7f934dde484e43431f60b069e87bb30a3a7e186cb6bd69caa0418f3"},
{file = "ujson-5.3.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:8c734982d6560356c173817576a1f3fa074a2d2b993e63bffa69105ae9ec144b"},
{file = "ujson-5.3.0-cp38-cp38-win32.whl", hash = "sha256:563b7ed1e789f763410c49e6fab51d61982eb94088b25338e65b89ad20b6b107"},
{file = "ujson-5.3.0-cp38-cp38-win_amd64.whl", hash = "sha256:8a2cbb044bc6e6764b9a089a2079432b8bd576dbff5faa808b562a8f3c97452b"},
{file = "ujson-5.3.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:6c5d19fbdd29d5080926c863ba89591a2d3dbf592ea35b456cb2996004433d11"},
{file = "ujson-5.3.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:4dc79db757b0dfa23a111a4573827a6ef57de65dbe8cdb202e45cf9ddf06aad5"},
{file = "ujson-5.3.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5700a179abacbdc8609737e595a598b7f107cd68615ded3f922f4c0d4b6009d6"},
{file = "ujson-5.3.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:287dea79473ce4941598c45dc34f9f692d48d7863b451541c5ce960ab54465fb"},
{file = "ujson-5.3.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:151faa9085c10351a04aea959a2bc25dfa2e21af26d9b614a221d045b7923ea4"},
{file = "ujson-5.3.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:285082924747958aa69e1dc2146c01db6b0921a0bb04b595beefe7fcffaffaf9"},
{file = "ujson-5.3.0-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:8dd74570fe59c738d4dc12d44eb89538b0b01fae9dda6cfe3ff3f6934877cf35"},
{file = "ujson-5.3.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:6aba1e39ffdd83ec14832ea25bbb18266fea46bc69b8c0acbd996495826c0e6f"},
{file = "ujson-5.3.0-cp39-cp39-win32.whl", hash = "sha256:1358621686ddfda55171fc98c171bf5b1a80ce4d444134b70e1e449925fa014f"},
{file = "ujson-5.3.0-cp39-cp39-win_amd64.whl", hash = "sha256:d1fab398734634f4b412512ed230d45522fc9f3dd9ca169f579474a491f662aa"},
{file = "ujson-5.3.0-pp37-pypy37_pp73-macosx_10_9_x86_64.whl", hash = "sha256:d4830c8df958c45c16dfc43c8353403efd7f1a8e39b91a7e0e848d55b7fa8b48"},
{file = "ujson-5.3.0-pp37-pypy37_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:48bed7c1f95484644a2cc658efff4d1e75b8c806f6ef2b5c815f59e1cbe0d039"},
{file = "ujson-5.3.0-pp37-pypy37_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2db7cbe415d7329b9bff029a83851d1077836ec728fe1c32be34c9c3a5017ab2"},
{file = "ujson-5.3.0-pp37-pypy37_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:73636001055667bbcc6a73b232da1d272f68a49a1f192efbe99e99ddf8ef1d21"},
{file = "ujson-5.3.0-pp37-pypy37_pp73-win_amd64.whl", hash = "sha256:47bf966e1041ae8e568d7e8eb421d72d0521c30c28306b76c256832553e316c6"},
{file = "ujson-5.3.0-pp38-pypy38_pp73-macosx_10_9_x86_64.whl", hash = "sha256:66f857d8b8d7ea44e3fd5f2b7e471334f24b735423729771f5a7a7f69ab645ed"},
{file = "ujson-5.3.0-pp38-pypy38_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7d2cb50aa526032b8812975c3832058763ee50e1dc3a1302431ed9d0922c3a1b"},
{file = "ujson-5.3.0-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f615ee181b813c8f50a57d55354d0c0304a0be066962efdbef6f44517b26e3b2"},
{file = "ujson-5.3.0-pp38-pypy38_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c5696c99a7dd567566c18490e8e346b2657967feb1e3c2004e91dbb253db0894"},
{file = "ujson-5.3.0-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:a68d5a8a46712ffe86db8ae1b4311714db534725521c71fd4c9e1cd062dae9a4"},
{file = "ujson-5.3.0.tar.gz", hash = "sha256:ab938777b3ac0372231ee654a7f6a13787e587b1ca268d8aa7e6fb6846e477d0"},
{file = "ujson-5.4.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:511aa641a5b91d19280183b134fb6c473039d4dd82e987ac810cffba783521ac"},
{file = "ujson-5.4.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:b045ca5497a950cc3492840adb3bcb3b9e305ed6599ed14c6aeaa08011aa463f"},
{file = "ujson-5.4.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:aa00b746138835271653b0c3da171d2a8b510c579381f71e8b8e03484d50d825"},
{file = "ujson-5.4.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:91edcf9978ee401119e9c8589376ae37fd3e6e75ee365c49385cb005eaff1535"},
{file = "ujson-5.4.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:05e411627e5d6ee773232960ca7307e66017f78e3fa74f7e95c3a8cc5cb05415"},
{file = "ujson-5.4.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:7d4c9ccd30e621e714ec24ca911ad8873567dc1ac1e5e914405ea9dd16b9d40c"},
{file = "ujson-5.4.0-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:dd0d4ec694cab8a0a4d85f45f81ae0065465c4670f0db72ba48d6c4e7ae42834"},
{file = "ujson-5.4.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:aaa77af91df3f71858a1f792c74d3f2d3abf3875f93ab1a2b9a24b3797743b02"},
{file = "ujson-5.4.0-cp310-cp310-win32.whl", hash = "sha256:fbea46c0fbc1c3bc8f957afd8dbb25b4ea3a356e18ee6dd79ace6cf32bd4cff7"},
{file = "ujson-5.4.0-cp310-cp310-win_amd64.whl", hash = "sha256:2d98248f1df1e1aab67e0374ab98945dd36bc1764753d71fd8aea5f296360b76"},
{file = "ujson-5.4.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:f5c547d49a7e9d3f231e9323171bbbbcef63173fb007a2787cd4f05ac6269315"},
{file = "ujson-5.4.0-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:39bb702ca1612253b5e4b6004e0f20208c98a446606aa351f9a7ba5ceaff0eb8"},
{file = "ujson-5.4.0-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:381c97d326d1ec569d318cc0ae83940ea2df125ede1000871680fefd5b7fdea9"},
{file = "ujson-5.4.0-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3a0707f381f97e1287c0dbf94d95bd6c0bbf6e4eeeaa656f0076b7883010c818"},
{file = "ujson-5.4.0-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:6a20f2f6e8818c1ab89dd4be6bbad3fc2ddb15287f89e7ea35f3eb849afebbd9"},
{file = "ujson-5.4.0-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:8cd6117e33233f2de6bc896eea6a5a59b58a37db08f371157264e0ec5e51c76a"},
{file = "ujson-5.4.0-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:31bdb6d771d5ef6d37134b42211500bfe176c55d399f3317e569783dc42ed38e"},
{file = "ujson-5.4.0-cp37-cp37m-win32.whl", hash = "sha256:baa76a6f707a6d22437fe9c7ec9719672fb04d4d9435a3e80ee9b1aaeb2089d9"},
{file = "ujson-5.4.0-cp37-cp37m-win_amd64.whl", hash = "sha256:ee29cf5cfc1e841708297633e1ce749aa851fb96830bbe51f2e5940741ff2441"},
{file = "ujson-5.4.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:7e12272361e9722777c83b3f5b0bb91d402531f36e80c6e5fafb6acb89e897e3"},
{file = "ujson-5.4.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:3212847d3885bfd4f5fd56cdc37645a8f8e8a80d6cb569505da22fd9eb0e1a02"},
{file = "ujson-5.4.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0bcde3135265ecdd5714a7de4fdc167925390d7b17ca325e59980f4114c962b8"},
{file = "ujson-5.4.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0551c1ba0bc9e05b69d9c18266dbc93252b5fa3cd9940051bc88a0dd33607b19"},
{file = "ujson-5.4.0-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:13297a7d501f9c8c53e409d4fa57cc574e4fbfbe8807ef2c4c7ce2e3ec933a85"},
{file = "ujson-5.4.0-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:2974b17bc522ef86d98b498959d82f03c02e07d9eb08746026415298f4a4bca3"},
{file = "ujson-5.4.0-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:5df8b6369ee5ee2685fcc917f6c46b34e599c6e9a512fada6dfd752b909fa06a"},
{file = "ujson-5.4.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:754f422aba8db8201a1073f25e2f732effc6471f8755708b16e6ebf19dd23634"},
{file = "ujson-5.4.0-cp38-cp38-win32.whl", hash = "sha256:ea7fbc540bc04d5b05e5cd54e60ee8745ac665eedf2bad2ba9d12d5c7a7b7d2e"},
{file = "ujson-5.4.0-cp38-cp38-win_amd64.whl", hash = "sha256:8d472efa9c92e1b2933a22d2f1dbd5237087997136b24ac2b913bf4e8be03135"},
{file = "ujson-5.4.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:e2a9ddb5c6d1427056b8d62a1a172a18ae522b14d9ba5996b8281b09cba87edd"},
{file = "ujson-5.4.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:1120c8263f7d85e89533a2b46d80cc6def15114772010ede4d197739e111dba6"},
{file = "ujson-5.4.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:400e4ca8a59f71398e8fa56c4d2d6f535e2a121ddb57284ec15752ffce2dd63a"},
{file = "ujson-5.4.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e844be0831042aa91e847e5ab03bddd1089ab1a8dd0a1bf90411abf864f058b2"},
{file = "ujson-5.4.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0b46aee21e5d75426c4058dfdb42f7e7b1d130c664ee5027a8dbbc50872dc32b"},
{file = "ujson-5.4.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:326a96324ed9215b0bc9f1a5af324fb33900b6b0901516bcc421475d6596de0d"},
{file = "ujson-5.4.0-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:fd82932aaa224abd7d01e823b77aef9970f5ac1695027331d99e7f5fda9d37f5"},
{file = "ujson-5.4.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:8cce79ce47c37132373fbdf55b683883c262a3a60763130e080b8394c1201d32"},
{file = "ujson-5.4.0-cp39-cp39-win32.whl", hash = "sha256:191f88d5865740497b9827ef9b7c12f37a79872ac984e09f0901a10024019380"},
{file = "ujson-5.4.0-cp39-cp39-win_amd64.whl", hash = "sha256:68c7f753aec490c6566fd3cd301887c413ac3a588316e446f30a4134ac665668"},
{file = "ujson-5.4.0-pp37-pypy37_pp73-macosx_10_9_x86_64.whl", hash = "sha256:1a2e645325f844f9c890c9d956fc2d35ca91f38c857278238ef6516c2f99cf7c"},
{file = "ujson-5.4.0-pp37-pypy37_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cec010d318a0238b1333ea9f40d5603d374cc026c29c4471e2661712c6682da1"},
{file = "ujson-5.4.0-pp37-pypy37_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b40a3757a563ef77c3f2f9ea1732c2924e8b3b2bda3fa89513f949472ad40b6e"},
{file = "ujson-5.4.0-pp37-pypy37_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:67f4e2fa81e1d99c01e7b1978ab0cbf3c9a8b663f683a709f87baad110d5b940"},
{file = "ujson-5.4.0-pp37-pypy37_pp73-win_amd64.whl", hash = "sha256:9ae1d0094ce730e39e09656bc14074d9573cdd80adec1a55b06d8bf1f9613a01"},
{file = "ujson-5.4.0-pp38-pypy38_pp73-macosx_10_9_x86_64.whl", hash = "sha256:784dbd12925845a3f0757a956447e2fd31418abb5aeaebf3aca1203195f16fd1"},
{file = "ujson-5.4.0-pp38-pypy38_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:422653083c6df6cec17fdb5d6106c209aad9b0c94131c53b073980403db22167"},
{file = "ujson-5.4.0-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e91947fda8354ea7faf698b084ebcdbabd239e7b15d8436fb74394f59a207ac9"},
{file = "ujson-5.4.0-pp38-pypy38_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ef985eb2770900a485431910bd3f333b56d1a34b65f8c26a6ed8e8adf55f98d9"},
{file = "ujson-5.4.0-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:025758cf6561af6986d77cd4af9367ab56dde5c7c50f13f59e6964b4b25df73e"},
{file = "ujson-5.4.0.tar.gz", hash = "sha256:6b953e09441e307504130755e5bd6b15850178d591f66292bba4608c4f7f9b00"},
]
uvicorn = [
{file = "uvicorn-0.17.6-py3-none-any.whl", hash = "sha256:19e2a0e96c9ac5581c01eb1a79a7d2f72bb479691acd2b8921fce48ed5b961a6"},
@@ -2527,22 +2540,54 @@ win32-setctime = [
{file = "win32_setctime-1.1.0.tar.gz", hash = "sha256:15cf5750465118d6929ae4de4eb46e8edae9a5634350c01ba582df868e932cb2"},
]
wordcloud = [
{file = "wordcloud-1.8.1-cp27-cp27m-macosx_10_6_x86_64.whl", hash = "sha256:2e85b1a6b8211436d47f902837c866c682c5d00046706f1d8e72cc0cd9210d64"},
{file = "wordcloud-1.8.1-cp27-cp27m-manylinux1_x86_64.whl", hash = "sha256:e0d3f03e4f27faf543859b4bcd229d320361d065cf4786f149081adea4ee68e8"},
{file = "wordcloud-1.8.1-cp27-cp27m-win32.whl", hash = "sha256:a02b27400bc4b2e4efdd8377e8cd138496b6b6e15a082d9c7b7c7b74a9ee8a55"},
{file = "wordcloud-1.8.1-cp27-cp27m-win_amd64.whl", hash = "sha256:6450f67c207f2ab4513c4aac803226427b39f7fc8d3498c0b41a03834b2fc426"},
{file = "wordcloud-1.8.1-cp27-cp27mu-manylinux1_x86_64.whl", hash = "sha256:f49f5213e978fb5d8ff8f6e6067de657e81ca863cab549b198579f58e1430e7c"},
{file = "wordcloud-1.8.1-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:41dafafe3768675ba8285d21ac68bd53191b50da85aba2a7fb35964d1649156f"},
{file = "wordcloud-1.8.1-cp36-cp36m-win32.whl", hash = "sha256:cd8c44a21a3207da813c7b7e486ed41fe517eb7b8535deef945437f4cea2b245"},
{file = "wordcloud-1.8.1-cp36-cp36m-win_amd64.whl", hash = "sha256:2d5f63cf1f65126d6d1b8d55acae15961c5bd55e6ef8978bf269831dafcd1408"},
{file = "wordcloud-1.8.1-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:6ff0ca777c801cd2a38deef30019f02fe35df1be9c31f6c301a34bd45006e26b"},
{file = "wordcloud-1.8.1-cp37-cp37m-win32.whl", hash = "sha256:9ed09d2d2916514845e42b0cc5dd86f65d4f0f89ab4bf164cc9a628d9db5a76b"},
{file = "wordcloud-1.8.1-cp37-cp37m-win_amd64.whl", hash = "sha256:2e1fc5991f52a0e191873025f5d8cf852eb1c39f2579d43e6d2589dc04d620d9"},
{file = "wordcloud-1.8.1-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:daa7d72f0004fcfea5f649f1683dbe47c4a9c5f721aabeb41278a4ee33f21424"},
{file = "wordcloud-1.8.1-cp38-cp38-win32.whl", hash = "sha256:a4a626df47eacec207988e1ffdb5bd16b2b62ac83ea2442a0043080c0c5103ac"},
{file = "wordcloud-1.8.1-cp38-cp38-win_amd64.whl", hash = "sha256:f190eb3efcc17765a4ed9d78001fdb713988886b93e54b2758fcf4f6c519ad51"},
{file = "wordcloud-1.8.1-cp39-cp39-manylinux1_x86_64.whl", hash = "sha256:90e9cff2c6939b5e1fa87d2a2c5d79c8ac2eafc4c9b9bc1bcb865ca5fde7e758"},
{file = "wordcloud-1.8.1.tar.gz", hash = "sha256:e6ef771aac17c1cf8558c8d5ef025796184066d7b78f8118aefe011fb0d22952"},
{file = "wordcloud-1.8.2.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:2a90f512c1c3c2a483d9d12b06a80124cecf813ec7a49b328dce6181c5440f2d"},
{file = "wordcloud-1.8.2.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e6376f09543416d03844313d7e6e39eb0a42547e07001ac50be93fbd4c6c4431"},
{file = "wordcloud-1.8.2.2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:334318eb345eafc3494d81fbbe11823fc945833e6a77cf05c7a6a1b88c1e1db0"},
{file = "wordcloud-1.8.2.2-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:c162814bc0ca2f1436e553ef8c451009c689f5a3f04be8acb5f1df7389b2dbc2"},
{file = "wordcloud-1.8.2.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:66bd735eb869a769806154067da01ecaeb4ce165d3c523c306e8ea7d376a1706"},
{file = "wordcloud-1.8.2.2-cp310-cp310-win32.whl", hash = "sha256:7344df98ea8556a4f8e12924cf0b2e2d7055b1565440fd0c2a453c2b7e1e240b"},
{file = "wordcloud-1.8.2.2-cp310-cp310-win_amd64.whl", hash = "sha256:bc2974162313e3702cfd314401c943bb94add4ae64a4d7d4ecdf93863133b5e1"},
{file = "wordcloud-1.8.2.2-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:90174d2154d554e186e4db14121315bb0e2cb4b0c88d0ca0f8f99831f672cfea"},
{file = "wordcloud-1.8.2.2-cp36-cp36m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:948a947960ce688088e7617c9f82c26a0992c1e4e28a4de1f9a0afa736102556"},
{file = "wordcloud-1.8.2.2-cp36-cp36m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:52acd837fdd26239352f77be1491148b36e77f3c31ce7a9fe232664bfadd1a2c"},
{file = "wordcloud-1.8.2.2-cp36-cp36m-musllinux_1_1_i686.whl", hash = "sha256:a17055e10baee8e5f19d2b72c474d59dfb45660f66b92d3468fef44f0cc67b37"},
{file = "wordcloud-1.8.2.2-cp36-cp36m-musllinux_1_1_x86_64.whl", hash = "sha256:7b0a7e43b961ea7be13d7d7397fd131f8e795e1b36a23fdcab914a14d7c50def"},
{file = "wordcloud-1.8.2.2-cp36-cp36m-win32.whl", hash = "sha256:3d4da63b8f315caf3f715b23ab0a05d43139b9395e5e78b7e12b7ed2701d3a8e"},
{file = "wordcloud-1.8.2.2-cp36-cp36m-win_amd64.whl", hash = "sha256:ccd027f089df42d9d99f0a2d227823f1248d49826b82dc942e6066a16b825d99"},
{file = "wordcloud-1.8.2.2-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:61b292af5d88e719a641bb832280b81902df24a39936e6cf87ae0b6ea7910262"},
{file = "wordcloud-1.8.2.2-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:43a71a15ac8f721f425d1030633f26a8037ddb767fe9bb45647fe5ed94b92eda"},
{file = "wordcloud-1.8.2.2-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:14f7d34c25ddad412fac32760bd5d6d1565a98821f6dd021cf6617203b0db5ae"},
{file = "wordcloud-1.8.2.2-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:cf81862e9ff7c7abca1fb883ee60d3274b1ce1b30f99cd07cc29c5f5d95cbab8"},
{file = "wordcloud-1.8.2.2-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:3639eeb7c8b09f71376c6369277fb78d2dd8c96648034c3c25ee0f34f3e9249c"},
{file = "wordcloud-1.8.2.2-cp37-cp37m-win32.whl", hash = "sha256:d2a966bdebdb4eab57ce4ce9a31e96b8772c0e5c46f7e334cb2e8afa59297d8a"},
{file = "wordcloud-1.8.2.2-cp37-cp37m-win_amd64.whl", hash = "sha256:b8fc88ce6bd9edb514473f10ea4fb14ef78fbd5266b56a614939c5a8a0256283"},
{file = "wordcloud-1.8.2.2-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:9595200d2e30ba161f585dfc5bf568010e76adc7c7bbc5d7149f0bf3b96996aa"},
{file = "wordcloud-1.8.2.2-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5b6573eaab49a3682822ea7208077ed5b81210daa3ddb41a9d4879f9803d7b6b"},
{file = "wordcloud-1.8.2.2-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:55f6416f9958042fe92d751962a3dfd83e583828504d9f6e8f886e034b3354d0"},
{file = "wordcloud-1.8.2.2-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:4580f1cbf42b3a0565b140d580d6e249230373b8fa31e4491620af1caec45e92"},
{file = "wordcloud-1.8.2.2-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:3e3f01635def27b971f178b8bc3438f3be09d91f72ab3b51b90a1ae16e4028d8"},
{file = "wordcloud-1.8.2.2-cp38-cp38-win32.whl", hash = "sha256:b4e4dcd8c1c2483c2c45082d1e9cd600924d84b52154324cce5579a3dac33c46"},
{file = "wordcloud-1.8.2.2-cp38-cp38-win_amd64.whl", hash = "sha256:31c09d77a01b3d5638f2e0e043cfcb0d90f1c8b4d12864132e1d421dc525467f"},
{file = "wordcloud-1.8.2.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:f6377f570838661ade045793e100f506a4e1f92ddac23455470a03617e30d2a8"},
{file = "wordcloud-1.8.2.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3b50378e7cb7b9a1990ff3a10dfcad968185a1fbfd99de68c436245bbd5cd669"},
{file = "wordcloud-1.8.2.2-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d76e1939ca45907dcdf8b17fd64d5a392534f42a90b6194e806f08b65ed73948"},
{file = "wordcloud-1.8.2.2-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:c70fd9ff86a4370d0ba9aede977440635a859b3c6a86693e178f2ecb4369e153"},
{file = "wordcloud-1.8.2.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:c1fa72f4f94d7236eb6a1a86cfcfa6df292411047320c8deb173cac379a87c2a"},
{file = "wordcloud-1.8.2.2-cp39-cp39-win32.whl", hash = "sha256:55224971bd7c3ba2a1038dd5aefa0559b3a320bf6f0b864c0469db5dfa7d587b"},
{file = "wordcloud-1.8.2.2-cp39-cp39-win_amd64.whl", hash = "sha256:a3e6c77da50d1f5263281c72db8c2cf5893b2dd624ce684910b7830eb7069163"},
{file = "wordcloud-1.8.2.2-pp37-pypy37_pp73-macosx_10_9_x86_64.whl", hash = "sha256:be045984d2cdb4f181c7aff24001a97ade10d04b1098ac5c859748e2802263f4"},
{file = "wordcloud-1.8.2.2-pp37-pypy37_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:15da7c4ff531b19527fb605da11e747d418a4d81d78503c34fb11babf3196b7a"},
{file = "wordcloud-1.8.2.2-pp37-pypy37_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:dd6d3f253f163ce21f6efc2cc675caf16ce474977de925b0adcdae9b97fe8659"},
{file = "wordcloud-1.8.2.2-pp37-pypy37_pp73-win_amd64.whl", hash = "sha256:47f329426a0952a4509d4d0c8b84867f8d11e65cb81ee852ebcfad588598cda8"},
{file = "wordcloud-1.8.2.2-pp38-pypy38_pp73-macosx_10_9_x86_64.whl", hash = "sha256:001a0b4483fe4ec26302b306c1590a4c44a99483c2da8232012ac62177296594"},
{file = "wordcloud-1.8.2.2-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d512c9867a43cbbfa51661ea2d7e3e84c4309ef29ee1be3e10b64f9d3353bd70"},
{file = "wordcloud-1.8.2.2-pp38-pypy38_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c887681e9353e559f25421bfd3b358990ae80ed77c42f28eed810ab180047578"},
{file = "wordcloud-1.8.2.2-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:aa0ddb25cde58232131072870f139d6c737d74d7a3b6a80fcb9721f3b9b2e7bb"},
{file = "wordcloud-1.8.2.2-pp39-pypy39_pp73-macosx_10_9_x86_64.whl", hash = "sha256:16ed864495573094e5b64f424d66d9030a638569182ad7c5b2c8e11ab78a2541"},
{file = "wordcloud-1.8.2.2-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:19c444200056b3d75b5d84a6cc018d6491da1841462546663ea3a88300db03f1"},
{file = "wordcloud-1.8.2.2-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1dc69a57a349272119ae633b8d6bcc9cde600aeb4342261e12ce169badcd28ca"},
{file = "wordcloud-1.8.2.2-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:3c39bd66f172f56257f09cec8fe246cd11ef36e9e16eeabe88933788d983c6a1"},
{file = "wordcloud-1.8.2.2.tar.gz", hash = "sha256:523db887e47e840eb5c2e60428243bb1d7439fdc60f89626b17bafa1be64459c"},
]
yarl = [
{file = "yarl-1.7.2-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:f2a8508f7350512434e41065684076f640ecce176d262a7d54f0da41d99c5a95"},
+1 -1
View File
@@ -12,7 +12,7 @@ url = "https://mirrors.aliyun.com/pypi/simple/"
[tool.poetry.dependencies]
python = "^3.8"
nonebot2 = "^2.0.0-beta.2"
nonebot2 = "^2.0.0-beta.4"
nonebot-adapter-onebot = "^2.0.0-beta.1"
aiofiles = "^0.8.0"
aiohttp = "3.7.4.post0"
Binary file not shown.
+1 -4
View File
@@ -8,10 +8,7 @@
"configs/path_config.py",
"configs/utils",
"poetry.lock",
"pyproject.toml",
"resources/font",
"resources/image/zhenxun",
"resources/image/other"
"pyproject.toml"
],
"add_file": [],
"delete_file": []
+7 -7
View File
@@ -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]:
"""
+5
View File
@@ -14,6 +14,11 @@ 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"
@@ -37,7 +37,6 @@ class Plugins2settingsManager(StaticData):
limit_superuser: Optional[bool] = False,
plugin_type: Tuple[Union[str, int]] = ("normal",),
cost_gold: int = 0,
**kwargs
):
"""
添加一个插件设置
+28 -2
View File
@@ -181,13 +181,20 @@ def get_bot() -> Optional[Bot]:
return None
def get_matchers() -> List[Type[Matcher]]:
def get_matchers(distinct: bool = False) -> List[Type[Matcher]]:
"""
获取所有插件
说明:
获取所有matcher
参数:
distinct: 去重
"""
_matchers = []
temp = []
for i in matchers.keys():
for matcher in matchers[i]:
if distinct and matcher.plugin_name in temp:
continue
temp.append(matcher.plugin_name)
_matchers.append(matcher)
return _matchers
@@ -231,6 +238,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]:
"""
说明: