mirror of
https://github.com/zhenxun-org/zhenxun_bot.git
synced 2026-09-29 00:32:06 +08:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
0ea740aa7c | ||
|
|
5f118471cb | ||
|
|
504f78dd49 | ||
|
|
72723c7d1f | ||
|
|
6a74053853 | ||
|
|
af0b4987a4 | ||
|
|
775140cf79 | ||
|
|
6a8a796add | ||
|
|
8e4bda3760 | ||
|
|
a36e8145a8 | ||
|
|
3cbd899daa | ||
|
|
30f356a2c7 | ||
|
|
1138a115ce | ||
|
|
c397da75e7 | ||
|
|
1d141c9313 | ||
|
|
a4cc365af8 | ||
|
|
4e551bec0d | ||
|
|
e03086062d | ||
|
|
270a5a5e20 | ||
|
|
bda9133ed6 | ||
|
|
749f475f2c | ||
|
|
e38394948c | ||
|
|
51fc4cbb6a | ||
|
|
9a976cda37 | ||
|
|
e42521f0a3 | ||
|
|
0756d39ac5 | ||
|
|
4595bdcee4 | ||
|
|
38160065cc | ||
|
|
3d04f013cd | ||
|
|
8e1a416be3 | ||
|
|
a98bc42f1a | ||
|
|
6c93769838 | ||
|
|
d2dccfebc5 | ||
|
|
82fe59e469 | ||
|
|
afc05e1731 | ||
|
|
0206b0a522 | ||
|
|
0e1e5a5ebe | ||
|
|
9ad088ed6a | ||
|
|
34da04bba5 | ||
|
|
09ce812faf | ||
|
|
1dc8cb2a98 | ||
|
|
e73c509823 | ||
|
|
e70a85400f | ||
|
|
f7aecf8eed | ||
|
|
cc2645aa9d | ||
|
|
52d23c4a8d | ||
|
|
1e57ddb9ee | ||
|
|
7ae883d921 | ||
|
|
27e97214bd | ||
|
|
6321a9e7e0 | ||
|
|
a9eadca46c | ||
|
|
76bef8a725 | ||
|
|
308b76dc7d | ||
|
|
31d4d29f3e | ||
|
|
016a7e87ea | ||
|
|
4ce88c7bfc | ||
|
|
f570bffcf7 | ||
|
|
3bb9aaf637 | ||
|
|
cf7a64c934 | ||
|
|
c466603193 | ||
|
|
bc53f329a9 | ||
|
|
4a5cc5153f | ||
|
|
8697670fe1 | ||
|
|
64d8543af9 | ||
|
|
482cc242cb | ||
|
|
2051e8183c | ||
|
|
d183903917 | ||
|
|
5c695899a1 | ||
|
|
708e912069 | ||
|
|
db1f4b2b52 | ||
|
|
1508782f49 | ||
|
|
445b830683 | ||
|
|
1a17e88142 | ||
|
|
c0ec2e7856 | ||
|
|
9e962bd0d1 | ||
|
|
7efcff13bb | ||
|
|
e70d0df949 | ||
|
|
fcaa469edd | ||
|
|
51508182ef | ||
|
|
27e78c1626 | ||
|
|
0b4d37a0b6 | ||
|
|
ddbebba6f1 | ||
|
|
7f296348e4 | ||
|
|
ca78e32d97 | ||
|
|
4efc561f4d | ||
|
|
40fed798a1 | ||
|
|
0876afc08c | ||
|
|
ba4c4d06ff | ||
|
|
f64c30af48 | ||
|
|
bb0cea2dd3 | ||
|
|
3c120ce602 | ||
|
|
6151dfb161 | ||
|
|
f05d4ec931 | ||
|
|
e16afc3fc2 | ||
|
|
1b8753fead | ||
|
|
ba960d1ced | ||
|
|
41b60b6152 | ||
|
|
70ddce713c | ||
|
|
e15622748b | ||
|
|
96687021ff | ||
|
|
3c0a7ef510 | ||
|
|
4d84c9fb3f | ||
|
|
aa35a16610 | ||
|
|
a380fcb680 | ||
|
|
e544afe475 | ||
|
|
c089388323 | ||
|
|
e97a043031 | ||
|
|
d1b6dfe577 | ||
|
|
9189fd411e | ||
|
|
432eba80a9 | ||
|
|
db39612c54 | ||
|
|
5794c82a2d | ||
|
|
acf875a886 | ||
|
|
79185bd90f | ||
|
|
d695c30988 | ||
|
|
a1308fbdc9 | ||
|
|
e4f2b761d6 | ||
|
|
6ff6009a70 | ||
|
|
dadeb4006e | ||
|
|
ee0ff762e5 | ||
|
|
1f01ba1d10 | ||
|
|
62965fe1df | ||
|
|
e29eb4cce5 | ||
|
|
699791e318 | ||
|
|
946fbbec3c | ||
|
|
d2aae0103d | ||
|
|
ab06e38b7c | ||
|
|
bd531c85b1 | ||
|
|
8a0ca3c2e5 | ||
|
|
97317ed2c6 | ||
|
|
cda9aae8a3 | ||
|
|
a33e785043 | ||
|
|
833e266a1f | ||
|
|
1bb9b2cb3f | ||
|
|
7ba129b3bf | ||
|
|
1e2c9ca28a | ||
|
|
c6f37fd05f | ||
|
|
b6b569fff9 | ||
|
|
ac57c016a6 | ||
|
|
28fdd9de95 | ||
|
|
1ec946aaa9 | ||
|
|
d6110e3c6f | ||
|
|
4c5302bd4c | ||
|
|
21ee3d5c3e | ||
|
|
e5c036cee8 | ||
|
|
6f82488af7 | ||
|
|
57a7903c63 | ||
|
|
27b89b333a | ||
|
|
ce2c4e7492 | ||
|
|
cad98a2a33 | ||
|
|
40da48e685 | ||
|
|
1c393e5c3e | ||
|
|
667c143c74 | ||
|
|
bc089c18ce | ||
|
|
24b730c0e2 | ||
|
|
9ed9e9b973 | ||
|
|
5b0027aa3d | ||
|
|
810c5e4e8b | ||
|
|
907534958f | ||
|
|
f08a18ab07 | ||
|
|
40549d5ca7 | ||
|
|
3e67edc506 | ||
|
|
9c56a81f65 | ||
|
|
a9a636c9ae | ||
|
|
98374dcd74 | ||
|
|
10ca6eec91 | ||
|
|
216d32cf21 | ||
|
|
248fae4fe6 | ||
|
|
b479b6839d | ||
|
|
1a4e1e9515 | ||
|
|
e5160a4ee8 | ||
|
|
56ea3964a7 | ||
|
|
ad73778286 | ||
|
|
3e548f48c8 | ||
|
|
ae466931fa | ||
|
|
68052b526f | ||
|
|
903b5b88a7 | ||
|
|
007823d6b2 | ||
|
|
db5c4fb349 | ||
|
|
c37901290a | ||
|
|
da67f1f6f8 | ||
|
|
5172fa69e5 | ||
|
|
dd7280cd0d | ||
|
|
0d0f522c88 | ||
|
|
5429e6ed5c | ||
|
|
9db69fa05b | ||
|
|
3b8d52bcb5 | ||
|
|
807f39a336 | ||
|
|
fea2f30461 | ||
|
|
5d97e3f95f | ||
|
|
63e6ca3e99 | ||
|
|
b11590ec3c | ||
|
|
3a43ed6919 | ||
|
|
15ce9c91fb | ||
|
|
0e8d7537d4 | ||
|
|
1fca0787be | ||
|
|
af30285c00 | ||
|
|
a84cab7afd | ||
|
|
593573ddfb | ||
|
|
e9e9f81bdb | ||
|
|
61f624841b | ||
|
|
4bb38a2730 | ||
|
|
d4ec0df00c | ||
|
|
c8a7d855f8 | ||
|
|
73a126dcd5 | ||
|
|
0736533389 | ||
|
|
2695d658ab | ||
|
|
947166e793 | ||
|
|
80c49a7cf5 | ||
|
|
55bfa8f669 | ||
|
|
74ade14f5e | ||
|
|
c9b5218ebc | ||
|
|
b324b2bd65 | ||
|
|
35c038efa2 | ||
|
|
caa404d58d | ||
|
|
830103b9d5 | ||
|
|
6e18379597 | ||
|
|
6effbacb28 | ||
|
|
a20a1a0234 | ||
|
|
3667724061 | ||
|
|
785179e4a7 | ||
|
|
715338f821 | ||
|
|
7d5a82ab6d | ||
|
|
e764cfcc1c | ||
|
|
363acf37bc | ||
|
|
5fd687c1e4 | ||
|
|
dbedcbb93d | ||
|
|
9e9611f11f | ||
|
|
44002838da | ||
|
|
4daab8459f | ||
|
|
5a002f75cc | ||
|
|
a2180a1365 | ||
|
|
7b836acf36 | ||
|
|
a112a561cc | ||
|
|
6dcc2a69f4 | ||
|
|
74e9c55a09 | ||
|
|
d4d6e736a4 | ||
|
|
5cf9938fa2 | ||
|
|
72ad0ad983 | ||
|
|
690750d91e | ||
|
|
9f1dfc4b9c | ||
|
|
05eab5307f | ||
|
|
b0fe70c960 | ||
|
|
09d7992bc4 | ||
|
|
c832a432c1 | ||
|
|
41c22f4314 | ||
|
|
252d45c79b | ||
|
|
ae8a214a3a | ||
|
|
86ce6d7664 | ||
|
|
67e96d6106 | ||
|
|
24d9ec2b75 | ||
|
|
501e47c6ec | ||
|
|
5534a5a5c2 | ||
|
|
7246aabf40 | ||
|
|
604ffae87e | ||
|
|
2d5c16456b | ||
|
|
d44f7633dc | ||
|
|
469ac4035c | ||
|
|
dab218a6d7 | ||
|
|
d6fc675d57 | ||
|
|
04148f7365 | ||
|
|
4c34ac570f | ||
|
|
fbdb8758a3 | ||
|
|
2d08747d70 | ||
|
|
0b4bacbbd1 | ||
|
|
e0412c7edb | ||
|
|
38501cebf1 | ||
|
|
ee45d0217e | ||
|
|
558f362919 | ||
|
|
fb9db2f50c | ||
|
|
374b7b7261 |
+17
@@ -138,9 +138,26 @@ dmypy.json
|
||||
# Cython debug symbols
|
||||
cython_debug/
|
||||
|
||||
demo.py
|
||||
test.py
|
||||
server_ip.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/
|
||||
|
||||
@@ -128,7 +128,7 @@
|
||||
- [x] 移动图片 (同上)
|
||||
- [x] 删除图片 (同上)
|
||||
- [x] 群内B站订阅
|
||||
- [x] 群词条
|
||||
- [x] 词条设置
|
||||
- [x] 休息吧/醒来
|
||||
|
||||
### 已实现的超级用户功能
|
||||
@@ -235,18 +235,272 @@ python bot.py
|
||||
|
||||
|
||||
## 使用Docker
|
||||
__Docker 最新版本由 [Sakuracio](https://github.com/Sakuracio) 提供__
|
||||
#### GitHub:[Sakuracio/zhenxun_bot_docker](https://github.com/Sakuracio/zhenxun_bot_docker)
|
||||
#### DockerHub:[hibikier/zhenxun_bot](https://hub.docker.com/r/hibikier/zhenxun_bot)
|
||||
__Docker 单机版(仅真寻Bot)__
|
||||
**点击下方的 GitHub 徽标查看教程**
|
||||
[](https://github.com/Sakuracio/zhenxun_bot_docker)
|
||||
[](https://hub.docker.com/r/hibikier/zhenxun_bot)
|
||||
__Docker 全量版(包含 真寻Bot PostgreSQL数据库 go-cqhttp webui等)__
|
||||
[](https://github.com/SinKy-Yan/zhenxunbot-docker)
|
||||
[](https://hub.docker.com/r/jyishit/zhenxun_bot)
|
||||
**点击上方的 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/5/29 \[v0.1.5.7]
|
||||
### 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动态提供直链
|
||||
|
||||
### 2022/7/23
|
||||
|
||||
* 优化开箱次数判断
|
||||
|
||||
### 2022/7/16
|
||||
|
||||
* cos提供连发命令
|
||||
* 修复B站视频解析中b23解析错误
|
||||
|
||||
### 2022/7/13
|
||||
|
||||
* B站订阅提供配置项`LIVE_MSG_AT_ALL`,`UP_MSG_AT_ALL`:控制UP动态投稿和直播是否AT全体
|
||||
* 修改了Omega图库检测的数据库文件名
|
||||
* game_utils提供了Game类
|
||||
|
||||
### 2022/7/7
|
||||
|
||||
* 微博热搜选择单条热搜时也会检测时效性 [@pull/891](https://github.com/HibiKier/zhenxun_bot/pull/891)
|
||||
|
||||
### 2022/7/4
|
||||
|
||||
* 修复商品未设置限购时无法购买
|
||||
* 修复B站订阅UP动态 [@pull/886](https://github.com/HibiKier/zhenxun_bot/pull/886)
|
||||
* 修复色图命令带tag时配置项`MAX_ONCE_NUM2FORWARD`无法生效
|
||||
|
||||
### 2022/7/3 \[v0.1.6.1]
|
||||
|
||||
* BagUser提供方法`check_props`方法,用于判断用户是否含有该道具并是否使用
|
||||
* 优化商品显示图片,提供限制,限购,折扣提示
|
||||
* 修复图库内图片无法被连续删除的问题 [@pull/879](https://github.com/HibiKier/zhenxun_bot/pull/879)
|
||||
* 色图提供配置项`MAX_ONCE_NUM2FORWARD`:群聊中单次发送图片数量达到指定时使用合并转发
|
||||
* 优化修复了商品修改命令
|
||||
* B站订阅UP动态改为详情页截图
|
||||
|
||||
### 2022/6/28
|
||||
|
||||
* 修复色图插件api修改无法搜索图片 [@pull/866](https://github.com/HibiKier/zhenxun_bot/pull/866)
|
||||
|
||||
### 2022/6/26
|
||||
|
||||
* 替换了bt URL
|
||||
* PIX当使用pid查询图片时,会发送该pid下所有图片
|
||||
* 商店提供了每日购买次数限制
|
||||
|
||||
### 2022/6/24
|
||||
|
||||
* 对WSL虚拟机 代理 添加写法 [@pull/847](https://github.com/HibiKier/zhenxun_bot/pull/847)
|
||||
|
||||
### 2022/6/21
|
||||
|
||||
* update parse_bilibili [@pull/840](https://github.com/HibiKier/zhenxun_bot/pull/840)
|
||||
|
||||
### 2022/6/19 \[v0.1.6.0]
|
||||
|
||||
* 暂时使用hook修复webui中plugins2setting修改时会改变plugins2setting.cmd为字符串
|
||||
* 修复原神树脂重复提醒的bug [@pull/828](https://github.com/HibiKier/zhenxun_bot/pull/828)
|
||||
* 词条优化问题展示,修改含机器人昵称和@逻辑 [@pull/835](https://github.com/HibiKier/zhenxun_bot/pull/835)
|
||||
|
||||
### 2022/6/18
|
||||
|
||||
* 修复昵称系统`BLACK_WORD`为空时造成报错
|
||||
* 修复特殊头像时背景透明化出错
|
||||
* 修复text2image纯文本时换行时颜色不统一
|
||||
* 优化webUI,当有插件出错时不会影响其他插件显示
|
||||
* 优化Config类型注释 [@pull/830](https://github.com/HibiKier/zhenxun_bot/pull/830)
|
||||
* 优化browser
|
||||
|
||||
### 2022/6/17
|
||||
|
||||
* 修复wordbank的一些小问题 @pull/820
|
||||
|
||||
### 2022/6/14
|
||||
|
||||
* AsyncHttpx提供可关闭 ssl 验证参数 [@pull/812](https://github.com/HibiKier/zhenxun_bot/pull/812)
|
||||
* fix: 自动更新群组 [@pull/810](https://github.com/HibiKier/zhenxun_bot/pull/810)
|
||||
* fix: 修复添加词条时对answer中图片的处理 [@pull/806](https://github.com/HibiKier/zhenxun_bot/pull/806)
|
||||
* 修复pix中指定num无法生效
|
||||
* 替换了bt URL
|
||||
|
||||
### 2022/6/13
|
||||
|
||||
* Windows 重启适配 [@pull/804](https://github.com/HibiKier/zhenxun_bot/pull/804)
|
||||
* 色图等提供修改md5方法 [@pull/800](https://github.com/HibiKier/zhenxun_bot/pull/800)
|
||||
* webui中token.json文件写入补充 [@pull/798](https://github.com/HibiKier/zhenxun_bot/pull/798)
|
||||
* 修复识图某些图片可能会出错
|
||||
|
||||
### 2022/6/11
|
||||
|
||||
* pix当只有一个tag时且为数字时可以通过添加'#'防止判定为数量
|
||||
|
||||
### 2022/6/9
|
||||
|
||||
* 修复b站订阅同群二人以上相同订阅时发送多次信息
|
||||
* 修复超级用户帮助中缺少了 ‘插件商店’ 相关帮助
|
||||
* 昵称系统提供了详细帮助
|
||||
|
||||
### 2022/6/5 \[v0.1.5.9]
|
||||
|
||||
* webui修复plugin2setting中cmd从list转变为str
|
||||
* 当命令`我的金币`被风控时将以图片形式发送
|
||||
* fix gold_redbag [@pull/763](https://github.com/HibiKier/zhenxun_bot/pull/763)
|
||||
* 金币红包功能增加更多封面 [@pull/764](https://github.com/HibiKier/zhenxun_bot/pull/764)
|
||||
|
||||
### 2022/6/3
|
||||
|
||||
* 插件仓库在已安装插件边上会提示\[已安装]
|
||||
* 修复ShopRegister kwargs某些字段无效
|
||||
* 调整了一下查看所有请求中的年龄绘制 [@pull/745](https://github.com/HibiKier/zhenxun_bot/pull/745)
|
||||
* 修复原神树脂提醒bug [@pull/756](https://github.com/HibiKier/zhenxun_bot/pull/756)
|
||||
|
||||
### 2022/5/31
|
||||
|
||||
* 修复开启/关闭全部功能时帮助图片未重绘 [@pull/721](https://github.com/HibiKier/zhenxun_bot/pull/721)
|
||||
* bot_friend_group将group_handle.修改为friend_handle. [@pull/711](https://github.com/HibiKier/zhenxun_bot/pull/711)
|
||||
* 修复发红包图片背景未透明化;修复原神树脂提醒参数错误 [@pull/712](https://github.com/HibiKier/zhenxun_bot/pull/712)
|
||||
* 修复抽卡插件:方舟更新UP池信息时,若公告的第一个池子过期会导致无法更新UP池信息 [@pull/707](https://github.com/HibiKier/zhenxun_bot/pull/707)
|
||||
* 商店插件判断是否有requirements.txt文件 [@pull/705](https://github.com/HibiKier/zhenxun_bot/pull/705)
|
||||
* 删除原神玩家查询api返回变更的多余键值
|
||||
* 优化了text2image方法
|
||||
|
||||
### 2022/5/29 \[v0.1.5.8]
|
||||
|
||||
* 修复错误的resource路径会删除IMAGE_PATH等重要文件夹
|
||||
* 提供了真寻适配仓库的插件 安装/卸载 操作
|
||||
* 暂时关闭了插件资源清空
|
||||
* 通过指令安装插件时会在插件目录下生成plugin_info.json记录当前插件信息
|
||||
|
||||
### 2022/5/28
|
||||
|
||||
@@ -258,7 +512,6 @@ __Docker 最新版本由 [Sakuracio](https://github.com/Sakuracio) 提供__
|
||||
* 新增自动更新插件```UPDATE_REMIND```:新版本提醒,原配置项```AUTO_UPDATE_ZHENXUN```改为自动更新升级
|
||||
* black_word新增当群权限为-1时不再检测该群
|
||||
* 修复非超级用户绑定原神cookie会被black_word阻拦
|
||||
* 修复webui中plugins2setting修改时会改变plugins2setting.cmd为字符串
|
||||
* 修复微博热搜报错,发红包小bug [@pull/688](https://github.com/HibiKier/zhenxun_bot/pull/688)
|
||||
* 更多的中文提示
|
||||
|
||||
@@ -449,7 +702,7 @@ __Docker 最新版本由 [Sakuracio](https://github.com/Sakuracio) 提供__
|
||||
* 修复点歌无法正确发送
|
||||
* 修复我有一个朋友有时文本会包含CQ码
|
||||
* 修复群欢消息被动控制文本未删除 [@pull/124](https://github.com/HibiKier/zhenxun_bot/pull/124)
|
||||
* message_builder.image不再提供参数:abspath
|
||||
* message_builder.image不再提供参数:abspath
|
||||
|
||||
### 2022/2/23
|
||||
|
||||
@@ -586,9 +839,8 @@ __Docker 最新版本由 [Sakuracio](https://github.com/Sakuracio) 提供__
|
||||
|
||||
__..... 更多更新信息请查看文档__
|
||||
|
||||
|
||||
## Todo
|
||||
- [ ] web管理
|
||||
- [x] web管理
|
||||
|
||||
## 感谢
|
||||
[botuniverse / onebot](https://github.com/botuniverse/onebot) :超棒的机器人协议
|
||||
|
||||
+1
-1
@@ -1 +1 @@
|
||||
__version__: v0.1.5.6
|
||||
__version__: v0.1.6.3-fix1
|
||||
|
||||
@@ -118,6 +118,8 @@ async def change_group_switch(cmd: str, group_id: int, is_super: bool = False):
|
||||
group_manager.unblock_plugin(f, group_id)
|
||||
else:
|
||||
group_manager.block_plugin(f, group_id)
|
||||
if group_help_file.exists():
|
||||
group_help_file.unlink()
|
||||
return f"已 {status} 全部功能!"
|
||||
if cmd in [task_data[x] for x in task_data.keys()]:
|
||||
type_ = "task"
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -4,9 +4,8 @@ from nonebot import on_message
|
||||
from services.log import logger
|
||||
from models.group_info import GroupInfo
|
||||
from models.friend_user import FriendUser
|
||||
from nonebot.adapters.onebot.v11.exception import ActionFailed
|
||||
from nonebot.adapters.onebot.v11 import ActionFailed
|
||||
from configs.config import NICKNAME, Config
|
||||
from utils.manager import group_manager
|
||||
from pathlib import Path
|
||||
import shutil
|
||||
|
||||
@@ -42,7 +41,7 @@ Config.add_plugin_config(
|
||||
)
|
||||
|
||||
|
||||
cx = on_message(priority=9, block=False)
|
||||
cx = on_message(priority=9999, block=False, rule=lambda: False)
|
||||
|
||||
|
||||
# 早上好
|
||||
@@ -61,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}")
|
||||
|
||||
@@ -84,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}")
|
||||
|
||||
@@ -107,6 +106,7 @@ async def _():
|
||||
group_info["group_name"],
|
||||
group_info["max_member_count"],
|
||||
group_info["member_count"],
|
||||
1
|
||||
)
|
||||
logger.info(f"自动更新群组 {g} 信息成功")
|
||||
except Exception as 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)}"
|
||||
|
||||
@@ -7,10 +7,10 @@ from datetime import datetime
|
||||
from services.log import logger
|
||||
from nonebot.adapters.onebot.v11 import (
|
||||
Bot,
|
||||
ActionFailed,
|
||||
GroupIncreaseNoticeEvent,
|
||||
GroupDecreaseNoticeEvent,
|
||||
)
|
||||
from nonebot.adapters.onebot.v11.exception import ActionFailed
|
||||
from utils.manager import group_manager, plugins2settings_manager, requests_manager
|
||||
from configs.config import NICKNAME
|
||||
from models.group_info import GroupInfo
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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"):
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
from nonebot.matcher import Matcher
|
||||
from nonebot.message import run_preprocessor, run_postprocessor, IgnoredException
|
||||
from nonebot.adapters.onebot.v11.exception import ActionFailed
|
||||
from models.friend_user import FriendUser
|
||||
from models.group_member_info import GroupInfoUser
|
||||
from models.bag_user import BagUser
|
||||
@@ -23,6 +22,7 @@ from nonebot.typing import T_State
|
||||
from typing import Optional
|
||||
from nonebot.adapters.onebot.v11 import (
|
||||
Bot,
|
||||
ActionFailed,
|
||||
MessageEvent,
|
||||
GroupMessageEvent,
|
||||
PokeNotifyEvent,
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
from nonebot.matcher import Matcher
|
||||
from nonebot.message import run_preprocessor, IgnoredException
|
||||
from nonebot.adapters.onebot.v11.exception import ActionFailed
|
||||
from nonebot.typing import T_State
|
||||
from nonebot.adapters.onebot.v11 import (
|
||||
Bot,
|
||||
Event,
|
||||
MessageEvent,
|
||||
ActionFailed,
|
||||
PokeNotifyEvent,
|
||||
GroupMessageEvent,
|
||||
)
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
from nonebot.matcher import Matcher
|
||||
from nonebot.message import run_preprocessor, IgnoredException
|
||||
from nonebot.adapters.onebot.v11.exception import ActionFailed
|
||||
from nonebot.typing import T_State
|
||||
from nonebot.adapters.onebot.v11 import (
|
||||
Bot,
|
||||
ActionFailed,
|
||||
MessageEvent,
|
||||
GroupMessageEvent,
|
||||
)
|
||||
|
||||
@@ -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("[[To_Img\|?(.*?)[[")):
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -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 (
|
||||
(
|
||||
|
||||
@@ -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
|
||||
@@ -40,7 +39,7 @@ def init_none_plugin_count_manager():
|
||||
plugins2block_manager.save()
|
||||
plugins_manager.delete(module)
|
||||
plugins_manager.save()
|
||||
resources_manager.remove_resource(module)
|
||||
# resources_manager.remove_resource(module)
|
||||
none_plugin_count_manager.delete(module)
|
||||
logger.info(f"{module}:{plugin_name} 插件疑似已删除,清除对应插件数据...")
|
||||
except Exception as e:
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
from nonebot import on_request, on_message
|
||||
from nonebot.adapters.onebot.v11 import (
|
||||
Bot,
|
||||
ActionFailed,
|
||||
FriendRequestEvent,
|
||||
GroupRequestEvent,
|
||||
MessageEvent,
|
||||
@@ -8,7 +9,6 @@ from nonebot.adapters.onebot.v11 import (
|
||||
from models.friend_user import FriendUser
|
||||
from datetime import datetime
|
||||
from configs.config import NICKNAME, Config
|
||||
from nonebot.adapters.onebot.v11.exception import ActionFailed
|
||||
from utils.manager import requests_manager
|
||||
from models.group_info import GroupInfo
|
||||
from utils.utils import scheduler
|
||||
@@ -25,7 +25,7 @@ __plugin_configs__ = {
|
||||
|
||||
friend_req = on_request(priority=5, block=True)
|
||||
group_req = on_request(priority=5, block=True)
|
||||
x = on_message(priority=999, block=False)
|
||||
x = on_message(priority=999, block=False, rule=lambda: False)
|
||||
|
||||
exists_data = {"private": {}, "group": {}}
|
||||
|
||||
|
||||
@@ -22,6 +22,12 @@ __plugin_des__ = "区区昵称,才不想叫呢!"
|
||||
__plugin_cmd__ = ["以后叫我 [昵称]", f"{NICKNAME}我是谁"]
|
||||
__plugin_version__ = 0.1
|
||||
__plugin_author__ = "HibiKier"
|
||||
__plugin_settings__ = {
|
||||
"level": 5,
|
||||
"default_status": True,
|
||||
"limit_superuser": False,
|
||||
"cmd": ["昵称", "昵称系统"],
|
||||
}
|
||||
__plugin_configs__ = {
|
||||
"BLACK_WORD": {
|
||||
"value": ["爸", "爹", "爷", "父亲"],
|
||||
@@ -57,8 +63,9 @@ async def _(bot: Bot, event: MessageEvent, arg: Message = CommandArg()):
|
||||
await nickname.finish("笨蛋!休想占用我的名字!#", at_sender=True)
|
||||
_tmp = ""
|
||||
black_word = Config.get_config("nickname", "BLACK_WORD")
|
||||
for x in msg:
|
||||
_tmp += "*" if x in black_word else x
|
||||
if black_word:
|
||||
for x in msg:
|
||||
_tmp += "*" if x in black_word else x
|
||||
msg = _tmp
|
||||
if isinstance(event, GroupMessageEvent):
|
||||
if await GroupInfoUser.set_group_member_nickname(
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
from nonebot import on_command, on_regex
|
||||
|
||||
from .data_source import show_plugin_repo, install_plugin, uninstall_plugin
|
||||
from .data_source import show_plugin_repo, install_plugin, uninstall_plugin, download_json
|
||||
from services.log import logger
|
||||
from nonebot.adapters.onebot.v11 import Bot, GroupMessageEvent, Message, MessageEvent
|
||||
from nonebot.params import CommandArg
|
||||
@@ -15,16 +15,19 @@ usage:
|
||||
下载安装插件
|
||||
指令:
|
||||
查看插件仓库
|
||||
安装插件 [name/id]
|
||||
更新插件仓库
|
||||
安装插件 [name/id] (重新安装等同于更新)
|
||||
卸载插件 [name/id]
|
||||
""".strip()
|
||||
__plugin_des__ = "从真寻适配仓库中下载插件"
|
||||
__plugin_cmd__ = [""]
|
||||
__plugin_cmd__ = ["查看插件仓库", "更新插件仓库", "安装插件 [name/id]", "卸载插件 [name/id]"]
|
||||
__plugin_version__ = 0.1
|
||||
__plugin_author__ = "HibiKier"
|
||||
|
||||
show_repo = on_regex("^查看插件仓库$", priority=1, block=True, permission=SUPERUSER)
|
||||
|
||||
update_repo = on_regex("^更新插件仓库$", priority=1, block=True, permission=SUPERUSER)
|
||||
|
||||
install_plugin_matcher = on_command("安装插件", priority=1, block=True, permission=SUPERUSER)
|
||||
|
||||
uninstall_plugin_matcher = on_command("卸载插件", priority=1, block=True, permission=SUPERUSER)
|
||||
@@ -44,6 +47,14 @@ async def _(bot: Bot, event: MessageEvent, arg: Message = CommandArg()):
|
||||
await install_plugin_matcher.send(msg)
|
||||
|
||||
|
||||
@update_repo.handle()
|
||||
async def _(bot: Bot, event: MessageEvent):
|
||||
code = await download_json()
|
||||
if code == 200:
|
||||
await update_repo.finish("更新插件仓库信息成功!")
|
||||
await update_repo.send("更新插件仓库信息失败!")
|
||||
|
||||
|
||||
@show_repo.handle()
|
||||
async def _(bot: Bot, event: MessageEvent):
|
||||
msg = await show_plugin_repo()
|
||||
|
||||
@@ -3,6 +3,7 @@ import shutil
|
||||
import zipfile
|
||||
from pathlib import Path
|
||||
from typing import Union, Tuple
|
||||
from utils.manager import plugins_manager
|
||||
|
||||
import ujson as json
|
||||
|
||||
@@ -71,9 +72,16 @@ async def install_plugin(name: str) -> str:
|
||||
os.system(
|
||||
f"poetry run pip install -r {(extensive_plugin_path / f'{name}' / 'pyproject.toml').absolute()}"
|
||||
)
|
||||
elif "requirements.txt" in os.listdir(extensive_plugin_path / f"{name}"):
|
||||
tmp = "检测到该插件含有额外依赖,当前安装无法保证依赖完全安装成功。"
|
||||
os.system(
|
||||
f"poetry run pip install -r {(extensive_plugin_path / f'{name}' / 'requirements.txt').absolute()}"
|
||||
)
|
||||
with open(extensive_plugin_path / f"{name}" / "plugin_info.json", "w") as f:
|
||||
json.dump(data[name], f, ensure_ascii=False, indent=4)
|
||||
logger.debug("移动插件文件夹完成...")
|
||||
logger.info(f"成功安装插件 {name} 成功!\n{tmp}")
|
||||
return f"成功安装插件 {name},请重启bot!"
|
||||
return f"成功安装插件 {name},请重启真寻!"
|
||||
except Exception as e:
|
||||
logger.error(f"安装插件 {name} 失败 {type(e)}:{e}")
|
||||
return f"安装插件 {name} 失败 {type(e)}:{e}"
|
||||
@@ -108,6 +116,7 @@ async def show_plugin_repo() -> Union[int, str]:
|
||||
if code != 200:
|
||||
return code
|
||||
plugin_info = json.load(open(plugin_json, "r", encoding="utf8"))
|
||||
load_plugin_list = plugins_manager.get_data().keys()
|
||||
image_list = []
|
||||
w, h = 0, 0
|
||||
line_height = 10
|
||||
@@ -121,11 +130,17 @@ async def show_plugin_repo() -> Union[int, str]:
|
||||
"download_url": plugin_info[key]["download_url"],
|
||||
"github_url": plugin_info[key]["github_url"],
|
||||
}
|
||||
status = ""
|
||||
version = ""
|
||||
if key in load_plugin_list:
|
||||
status = "<f font_color=#1a7e30>[已安装]</f>"
|
||||
version = f"<f font_color=#1a7e30>[{plugins_manager.get_data()[key]['version']}]</f>"
|
||||
s = (
|
||||
f'id:{i+1}\n名称:{plugin_info[key]["plugin_name"]}\n'
|
||||
f'模块:{key}\n'
|
||||
f'id:{i+1}\n名称:{plugin_info[key]["plugin_name"]}'
|
||||
f' \t\t{status}\n'
|
||||
f"模块:{key}\n"
|
||||
f'作者:{plugin_info[key]["author"]}\n'
|
||||
f'版本:{plugin_info[key]["version"]}\n'
|
||||
f'版本:{plugin_info[key]["version"]} \t\t{version}\n'
|
||||
f'简介:{plugin_info[key]["introduction"]}\n'
|
||||
f"-------------------"
|
||||
)
|
||||
@@ -133,10 +148,10 @@ async def show_plugin_repo() -> Union[int, str]:
|
||||
w = w if w > img.w else img.w
|
||||
h += img.h + line_height
|
||||
image_list.append(img)
|
||||
A = BuildImage(w + 100, h + 100, color="#f9f6f2")
|
||||
cur_h = 50
|
||||
A = BuildImage(w + 50, h + 50, color="#f9f6f2")
|
||||
cur_h = 25
|
||||
for img in image_list:
|
||||
await A.apaste(img, (50, cur_h))
|
||||
await A.apaste(img, (25, cur_h))
|
||||
cur_h += img.h + line_height
|
||||
return A.pic2bs4()
|
||||
|
||||
|
||||
+29
-26
@@ -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,15 +111,38 @@ 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"
|
||||
), # 新增纯文本
|
||||
(
|
||||
"ALTER TABLE goods_info ADD daily_limit Integer DEFAULT 0;",
|
||||
"goods_info"
|
||||
), # 新增纯文本
|
||||
(
|
||||
"ALTER TABLE goods_info ADD daily_purchase_limit Json DEFAULT '{}';",
|
||||
"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}")
|
||||
@@ -128,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):
|
||||
|
||||
@@ -77,7 +77,11 @@ async def _(event: GroupMessageEvent, arg: Message = CommandArg()):
|
||||
await BagUser.get_gold(event.user_id, event.group_id)
|
||||
) < goods.goods_price * num * goods.goods_discount:
|
||||
await buy.finish("您的金币好像不太够哦", at_sender=True)
|
||||
flag, n = await GoodsInfo.check_user_daily_purchase(goods, event.user_id, event.group_id, num)
|
||||
if flag:
|
||||
await buy.finish(f"该次购买将超过每日次数限制,目前该道具还可以购买{n}次哦", at_sender=True)
|
||||
if await BagUser.buy_property(event.user_id, event.group_id, goods, num):
|
||||
await GoodsInfo.add_user_daily_purchase(goods, event.user_id, event.group_id, num)
|
||||
await buy.send(
|
||||
f"花费 {goods.goods_price * num * goods.goods_discount} 金币购买 {goods.goods_name} ×{num} 成功!",
|
||||
at_sender=True,
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
from nonebot import on_command
|
||||
from nonebot.adapters.onebot.v11 import GroupMessageEvent, Message
|
||||
from nonebot.adapters.onebot.v11 import GroupMessageEvent, Message, ActionFailed
|
||||
from nonebot.params import CommandArg
|
||||
from nonebot.adapters.onebot.v11.permission import GROUP
|
||||
from utils.data_utils import init_rank
|
||||
from models.bag_user import BagUser
|
||||
from utils.image_utils import text2image
|
||||
from utils.message_builder import image
|
||||
from utils.utils import is_number
|
||||
|
||||
@@ -34,7 +35,11 @@ gold_rank = on_command("金币排行", priority=5, block=True, permission=GROUP)
|
||||
|
||||
@my_gold.handle()
|
||||
async def _(event: GroupMessageEvent):
|
||||
await my_gold.finish(await BagUser.get_user_total_gold(event.user_id, event.group_id))
|
||||
msg = await BagUser.get_user_total_gold(event.user_id, event.group_id)
|
||||
try:
|
||||
await my_gold.send(msg)
|
||||
except ActionFailed:
|
||||
await my_gold.send(image(b64=(await text2image(msg, color="#f9f6f2", padding=10)).pic2bs4()))
|
||||
|
||||
|
||||
@gold_rank.handle()
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
from .data_source import create_shop_help, delete_goods, update_goods, register_goods, parse_goods_info
|
||||
from .data_source import create_shop_help, delete_goods, update_goods, register_goods, parse_goods_info, GoodsInfo
|
||||
from nonebot.adapters.onebot.v11 import MessageEvent, Message
|
||||
from nonebot import on_command
|
||||
from configs.path_config import IMAGE_PATH
|
||||
from utils.message_builder import image
|
||||
from nonebot.permission import SUPERUSER
|
||||
from utils.utils import is_number
|
||||
from utils.utils import is_number, scheduler
|
||||
from nonebot.params import CommandArg
|
||||
from nonebot.plugin import export
|
||||
from services.log import logger
|
||||
@@ -128,6 +128,18 @@ async def _(event: MessageEvent, arg: Message = CommandArg()):
|
||||
await shop_update_goods.send(f"修改商品 {name} 成功了...\n{text}", at_sender=True)
|
||||
logger.info(f"USER {event.user_id} 修改商品 {name} 数据 {text} 成功")
|
||||
else:
|
||||
await shop_update_goods.send(f"修改商品 {name} 失败了...", at_sender=True)
|
||||
await shop_update_goods.send(name, at_sender=True)
|
||||
logger.info(f"USER {event.user_id} 修改商品 {name} 数据 {text} 失败")
|
||||
|
||||
|
||||
@scheduler.scheduled_job(
|
||||
"cron",
|
||||
hour=0,
|
||||
minute=0,
|
||||
)
|
||||
async def _():
|
||||
try:
|
||||
await GoodsInfo.reset_daily_purchase()
|
||||
logger.info("商品每日限购次数重置成功...")
|
||||
except Exception as e:
|
||||
logger.error(f"商品每日限购次数重置发生错误 {type(e)}:{e}")
|
||||
|
||||
@@ -1,9 +1,11 @@
|
||||
from PIL import Image
|
||||
|
||||
from models.goods_info import GoodsInfo
|
||||
from utils.image_utils import BuildImage
|
||||
from models.sign_group_user import SignGroupUser
|
||||
from utils.utils import is_number
|
||||
from configs.path_config import IMAGE_PATH
|
||||
from typing import Optional, Union
|
||||
from typing import Optional, Union, Tuple
|
||||
from configs.config import Config
|
||||
from nonebot import Driver
|
||||
from nonebot.plugin import require
|
||||
@@ -32,6 +34,7 @@ async def init_default_shop_goods():
|
||||
"下次签到双倍好感度概率 + 30%(金币才是真命天子!)(同类商品将覆盖)",
|
||||
),
|
||||
load_status=Config.get_config("shop", "IMPORT_DEFAULT_SHOP_GOODS"),
|
||||
daily_limit=(10, 20, 30),
|
||||
** {"好感度双倍加持卡Ⅰ_prob": 0.1, "好感度双倍加持卡Ⅱ_prob": 0.2, "好感度双倍加持卡Ⅲ_prob": 0.3},
|
||||
)
|
||||
async def sign_card(user_id: int, group_id: int, prob: float):
|
||||
@@ -62,8 +65,9 @@ async def create_shop_help() -> str:
|
||||
_list.append(goods)
|
||||
A = BuildImage(1000, h, color="#f9f6f2")
|
||||
current_h = 0
|
||||
total_n = 0
|
||||
for goods in _list:
|
||||
bk = BuildImage(700, 80, font_size=15, color="#f9f6f2", font="CJGaoDeGuo.otf")
|
||||
bk = BuildImage(1180, 80, font_size=15, color="#f9f6f2", font="CJGaoDeGuo.otf")
|
||||
goods_image = BuildImage(
|
||||
600, 80, font_size=20, color="#a29ad6", font="CJGaoDeGuo.otf"
|
||||
)
|
||||
@@ -75,16 +79,27 @@ async def create_shop_help() -> str:
|
||||
)
|
||||
await name_image.aline((380, -5, 280, 45), "#a29ad6", 5)
|
||||
await name_image.atext((390, 0), "售价:", center_type="by_height")
|
||||
await name_image.atext(
|
||||
(440, 0), str(goods.goods_price), (255, 255, 255), center_type="by_height"
|
||||
)
|
||||
if goods.goods_discount != 1:
|
||||
discount_price = int(goods.goods_discount * goods.goods_price)
|
||||
old_price_image = BuildImage(0, 0, plain_text=str(goods.goods_price), font_color=(194, 194, 194), font="CJGaoDeGuo.otf", font_size=15)
|
||||
await old_price_image.aline((0, int(old_price_image.h / 2), old_price_image.w + 1, int(old_price_image.h / 2)), (0, 0, 0))
|
||||
await name_image.apaste(
|
||||
old_price_image, (440, 0), True
|
||||
)
|
||||
await name_image.atext(
|
||||
(440, 15), str(discount_price), (255, 255, 255)
|
||||
)
|
||||
else:
|
||||
await name_image.atext(
|
||||
(440, 0), str(goods.goods_price), (255, 255, 255), center_type="by_height"
|
||||
)
|
||||
await name_image.atext(
|
||||
(
|
||||
440
|
||||
+ BuildImage(0, 0, plain_text=str(goods.goods_price), font_size=25).w,
|
||||
0,
|
||||
),
|
||||
" 金币",
|
||||
f" 金币",
|
||||
center_type="by_height",
|
||||
)
|
||||
await name_image.acircle_corner(5)
|
||||
@@ -92,15 +107,18 @@ async def create_shop_help() -> str:
|
||||
await goods_image.atext((15, 50), f"简介:{goods.goods_description}")
|
||||
await goods_image.acircle_corner(20)
|
||||
await bk.apaste(goods_image, alpha=True)
|
||||
n = 0
|
||||
_w = 550
|
||||
# 添加限时图标和时间
|
||||
if goods.goods_limit_time > 0:
|
||||
n += 140
|
||||
_limit_time_logo = BuildImage(
|
||||
40, 40, background=f"{IMAGE_PATH}/other/time.png"
|
||||
)
|
||||
await bk.apaste(_limit_time_logo, (600, 0), True)
|
||||
await bk.apaste(_limit_time_logo, (_w + 50, 0), True)
|
||||
await bk.apaste(
|
||||
BuildImage(0, 0, plain_text="限时!", font_size=23, font="CJGaoDeGuo.otf"),
|
||||
(640, 10),
|
||||
(_w + 90, 10),
|
||||
True,
|
||||
)
|
||||
limit_time = time.strftime(
|
||||
@@ -109,28 +127,66 @@ async def create_shop_help() -> str:
|
||||
y_m_d = limit_time[0]
|
||||
_h_m = limit_time[1].split(":")
|
||||
h_m = _h_m[0] + "时 " + _h_m[1] + "分"
|
||||
await bk.atext((605, 38), str(y_m_d))
|
||||
await bk.atext((615, 57), str(h_m))
|
||||
await bk.aline((550, -1, 710, -1), "#a29ad6", 5)
|
||||
await bk.aline((550, 80, 710, 80), "#a29ad6", 5)
|
||||
await bk.atext((_w + 55, 38), str(y_m_d))
|
||||
await bk.atext((_w + 65, 57), str(h_m))
|
||||
_w += 140
|
||||
if goods.goods_discount != 1:
|
||||
n += 140
|
||||
_discount_logo = BuildImage(30, 30, background=f"{IMAGE_PATH}/other/discount.png")
|
||||
await bk.apaste(_discount_logo, (_w + 50, 10), True)
|
||||
await bk.apaste(
|
||||
BuildImage(0, 0, plain_text="折扣!", font_size=23, font="CJGaoDeGuo.otf"),
|
||||
(_w + 90, 15),
|
||||
True,
|
||||
)
|
||||
await bk.apaste(
|
||||
BuildImage(0, 0, plain_text=f"{10 * goods.goods_discount:.1f} 折", font_size=30, font="CJGaoDeGuo.otf", font_color=(85, 156, 75)),
|
||||
(_w + 50, 44),
|
||||
True,
|
||||
)
|
||||
_w += 140
|
||||
if goods.daily_limit != 0:
|
||||
n += 140
|
||||
_daily_limit_logo = BuildImage(35, 35, background=f"{IMAGE_PATH}/other/daily_limit.png")
|
||||
await bk.apaste(_daily_limit_logo, (_w + 50, 10), True)
|
||||
await bk.apaste(
|
||||
BuildImage(0, 0, plain_text="限购!", font_size=23, font="CJGaoDeGuo.otf"),
|
||||
(_w + 90, 20),
|
||||
True,
|
||||
)
|
||||
await bk.apaste(
|
||||
BuildImage(0, 0, plain_text=f"{goods.daily_limit}", font_size=30, font="CJGaoDeGuo.otf"),
|
||||
(_w + 72, 45),
|
||||
True,
|
||||
)
|
||||
if total_n < n:
|
||||
total_n = n
|
||||
if n:
|
||||
await bk.aline((550, -1, 550 + n, -1), "#a29ad6", 5)
|
||||
await bk.aline((550, 80, 550 + n, 80), "#a29ad6", 5)
|
||||
|
||||
# 添加限时图标和时间
|
||||
idx += 1
|
||||
await A.apaste(bk, (0, current_h), True)
|
||||
current_h += 90
|
||||
w = 1000
|
||||
w = 850
|
||||
if total_n:
|
||||
w += total_n
|
||||
h = A.h + 230 + 100
|
||||
h = 1000 if h < 1000 else h
|
||||
shop_logo = BuildImage(100, 100, background=f"{IMAGE_PATH}/other/shop_text.png")
|
||||
shop = BuildImage(w, h, font_size=20, color="#f9f6f2")
|
||||
shop.paste(A, (20, 230))
|
||||
zx_img = BuildImage(0, 0, background=f"{IMAGE_PATH}/zhenxun/toukan.png")
|
||||
zx_img = BuildImage(0, 0, background=f"{IMAGE_PATH}/zhenxun/toukan_3.png")
|
||||
zx_img.transpose(Image.FLIP_LEFT_RIGHT)
|
||||
zx_img.replace_color_tran(((240, 240, 240), (255, 255, 255)), (249, 246, 242))
|
||||
await shop.apaste(zx_img, (780, 100))
|
||||
await shop.apaste(zx_img, (0, 100))
|
||||
shop.paste(A, (20 + zx_img.w, 230))
|
||||
await shop.apaste(shop_logo, (450, 30), True)
|
||||
shop.text(
|
||||
(int((1000 - shop.getsize("注【通过 序号 或者 商品名称 购买】")[0]) / 2), 170),
|
||||
"注【通过 序号 或者 商品名称 购买】",
|
||||
)
|
||||
shop.text((20, h - 100), "神秘药水\t\t售价:9999999金币\n\t\t鬼知道会有什么效果~")
|
||||
shop.text((20 + zx_img.w, h - 100), "神秘药水\t\t售价:9999999金币\n\t\t鬼知道会有什么效果~")
|
||||
return shop.pic2bs4()
|
||||
|
||||
|
||||
@@ -140,6 +196,7 @@ async def register_goods(
|
||||
des: str,
|
||||
discount: Optional[float] = 1,
|
||||
limit_time: Optional[int] = 0,
|
||||
daily_limit: Optional[int] = 0,
|
||||
) -> bool:
|
||||
"""
|
||||
添加商品
|
||||
@@ -151,6 +208,7 @@ async def register_goods(
|
||||
:param des: 商品简介
|
||||
:param discount: 商品折扣
|
||||
:param limit_time: 商品限时销售时间,单位为小时
|
||||
:param daily_limit: 每日购买次数限制
|
||||
:return: 是否添加成功
|
||||
"""
|
||||
if not await GoodsInfo.get_goods_info(name):
|
||||
@@ -162,7 +220,7 @@ async def register_goods(
|
||||
else 0
|
||||
)
|
||||
return await GoodsInfo.add_goods(
|
||||
name, int(price), des, float(discount), limit_time
|
||||
name, int(price), des, float(discount), limit_time, daily_limit
|
||||
)
|
||||
return False
|
||||
|
||||
@@ -192,7 +250,7 @@ async def delete_goods(name: str, id_: int) -> "str, str, int":
|
||||
|
||||
|
||||
# 更新商品信息
|
||||
async def update_goods(**kwargs) -> "str, str, int":
|
||||
async def update_goods(**kwargs) -> Tuple[bool, str, str]:
|
||||
"""
|
||||
更新商品信息
|
||||
:param kwargs: kwargs
|
||||
@@ -202,17 +260,18 @@ async def update_goods(**kwargs) -> "str, str, int":
|
||||
goods_lst = await GoodsInfo.get_all_goods()
|
||||
if is_number(kwargs["name"]):
|
||||
if int(kwargs["name"]) < 1 or int(kwargs["name"]) > len(goods_lst):
|
||||
return "序号错误,没有该序号的商品...", "", 999
|
||||
return False, "序号错误,没有该序号的商品...", ""
|
||||
goods = goods_lst[int(kwargs["name"]) - 1]
|
||||
else:
|
||||
goods = await GoodsInfo.get_goods_info(kwargs["name"])
|
||||
if not goods:
|
||||
return "名称错误,没有该名称的商品...", "", 999
|
||||
name = goods.goods_name
|
||||
return False, "名称错误,没有该名称的商品...", ""
|
||||
name: str = goods.goods_name
|
||||
price = goods.goods_price
|
||||
des = goods.goods_description
|
||||
discount = goods.goods_discount
|
||||
limit_time = goods.goods_limit_time
|
||||
daily_limit = goods.daily_limit
|
||||
new_time = 0
|
||||
tmp = ""
|
||||
if kwargs.get("price"):
|
||||
@@ -229,24 +288,25 @@ async def update_goods(**kwargs) -> "str, str, int":
|
||||
new_time = time.strftime(
|
||||
"%Y-%m-%d %H:%M:%S",
|
||||
time.localtime(time.time() + kwargs["limit_time"] * 60 * 60),
|
||||
)
|
||||
tmp += f"限时至: {new_time}\n"
|
||||
) if kwargs["limit_time"] != 0 else 0
|
||||
tmp += f"限时至: {new_time}\n" if new_time else "取消了限时\n"
|
||||
limit_time = kwargs["limit_time"]
|
||||
return (
|
||||
await GoodsInfo.update_goods(
|
||||
name,
|
||||
int(price),
|
||||
des,
|
||||
float(discount),
|
||||
int(
|
||||
time.time() + limit_time * 60 * 60
|
||||
if limit_time != 0 and new_time
|
||||
else 0
|
||||
),
|
||||
),
|
||||
if kwargs.get("daily_limit"):
|
||||
tmp += f'每日购买限制:{daily_limit} --> {kwargs["daily_limit"]}\n' if daily_limit else "取消了购买限制\n"
|
||||
daily_limit = int(kwargs["daily_limit"])
|
||||
await GoodsInfo.update_goods(
|
||||
name,
|
||||
tmp[:-1],
|
||||
int(price),
|
||||
des,
|
||||
float(discount),
|
||||
int(
|
||||
time.time() + limit_time * 60 * 60
|
||||
if limit_time != 0 and new_time
|
||||
else 0
|
||||
),
|
||||
daily_limit
|
||||
)
|
||||
return True, name, tmp[:-1],
|
||||
|
||||
|
||||
def parse_goods_info(msg: str) -> Union[dict, str]:
|
||||
@@ -276,6 +336,10 @@ def parse_goods_info(msg: str) -> Union[dict, str]:
|
||||
data["discount"] = sp[1]
|
||||
elif sp[0] == "limit_time":
|
||||
if not is_number(sp[1]) or float(sp[1]) < 0:
|
||||
return "limit_time参数不合法,必须大于0!"
|
||||
return "limit_time参数不合法,必须为数字且大于0!"
|
||||
data["limit_time"] = sp[1]
|
||||
elif sp[0] == "daily_limit":
|
||||
if not is_number(sp[1]) or float(sp[1]) < 0:
|
||||
return "daily_limit参数不合法,必须为数字且大于0!"
|
||||
data["daily_limit"] = sp[1]
|
||||
return data
|
||||
|
||||
@@ -26,7 +26,7 @@ class GoodsUseFuncManager:
|
||||
判断商品使用方法是否被注册
|
||||
:param goods_name: 商品名称
|
||||
"""
|
||||
return bool(self._data.get(goods_name))
|
||||
return bool(self ._data.get(goods_name))
|
||||
|
||||
def get_max_num_limit(self, goods_name: str) -> int:
|
||||
"""
|
||||
|
||||
@@ -119,18 +119,18 @@ async def _(bot: Bot, cmd: Tuple[str, ...] = Command(), arg: Message = CommandAr
|
||||
)
|
||||
flag = await requests_manager.approve(bot, id_, "group")
|
||||
else:
|
||||
await friend_handle.send("同意群聊请求失败,未找到此id的请求..")
|
||||
await group_handle.send("同意群聊请求失败,未找到此id的请求..")
|
||||
else:
|
||||
flag = await requests_manager.refused(bot, id_, "group")
|
||||
if flag == 1:
|
||||
await friend_handle.send(f"{cmd[:2]}群聊请求失败,该请求已失效..")
|
||||
await group_handle.send(f"{cmd[:2]}群聊请求失败,该请求已失效..")
|
||||
requests_manager.delete_request(id_, "group")
|
||||
elif flag == 2:
|
||||
await friend_handle.send(f"{cmd[:2]}群聊请求失败,未找到此id的请求..")
|
||||
await group_handle.send(f"{cmd[:2]}群聊请求失败,未找到此id的请求..")
|
||||
else:
|
||||
await friend_handle.send(f"{cmd[:2]}群聊请求成功!")
|
||||
await group_handle.send(f"{cmd[:2]}群聊请求成功!")
|
||||
else:
|
||||
await friend_handle.send("id必须为纯数字!")
|
||||
await group_handle.send("id必须为纯数字!")
|
||||
|
||||
|
||||
@cls_request.handle()
|
||||
@@ -150,4 +150,4 @@ async def _():
|
||||
@clear_request.handle()
|
||||
async def _():
|
||||
requests_manager.clear()
|
||||
await cls_request.send("已清空所有好友/群聊请求..")
|
||||
await clear_request.send("已清空所有好友/群聊请求..")
|
||||
@@ -1,4 +1,11 @@
|
||||
from nonebot.adapters.onebot.v11 import Bot, MessageEvent, GROUP, GroupMessageEvent, Message
|
||||
from nonebot.adapters.onebot.v11 import (
|
||||
Bot,
|
||||
MessageEvent,
|
||||
GROUP,
|
||||
GroupMessageEvent,
|
||||
Message,
|
||||
ActionFailed,
|
||||
)
|
||||
from nonebot import on_command, on_regex
|
||||
from nonebot.permission import SUPERUSER
|
||||
from nonebot.typing import T_State
|
||||
@@ -8,7 +15,6 @@ from utils.manager import group_manager, plugins2settings_manager
|
||||
from models.group_info import GroupInfo
|
||||
from services.log import logger
|
||||
from configs.config import NICKNAME
|
||||
from nonebot.adapters.onebot.v11.exception import ActionFailed
|
||||
from nonebot.params import Command, CommandArg
|
||||
from typing import Tuple
|
||||
|
||||
@@ -137,9 +143,7 @@ async def _():
|
||||
async def _(bot: Bot, cmd: Tuple[str, ...] = Command(), arg: Message = CommandArg()):
|
||||
cmd = cmd[0]
|
||||
msg = arg.extract_plain_text().strip().split()
|
||||
all_group = [
|
||||
g["group_id"] for g in await bot.get_group_list()
|
||||
]
|
||||
all_group = [g["group_id"] for g in await bot.get_group_list()]
|
||||
group_list = []
|
||||
for group in msg:
|
||||
if is_number(group) and int(group) in all_group:
|
||||
@@ -151,9 +155,7 @@ async def _(bot: Bot, cmd: Tuple[str, ...] = Command(), arg: Message = CommandAr
|
||||
else:
|
||||
group_manager.delete_group_white_list(group)
|
||||
group_list = [str(x) for x in group_list]
|
||||
await manager_group_whitelist.send(
|
||||
"已成功将 " + "\n".join(group_list) + " " + cmd
|
||||
)
|
||||
await manager_group_whitelist.send("已成功将 " + "\n".join(group_list) + " " + cmd)
|
||||
else:
|
||||
await manager_group_whitelist.send(f"添加失败,请检查{NICKNAME}是否已加入这些群聊或重复添加/删除群白单名")
|
||||
|
||||
@@ -199,6 +201,4 @@ async def _(bot: Bot, cmd: Tuple[str, ...] = Command(), arg: Message = CommandAr
|
||||
else:
|
||||
if await GroupInfo.get_group_info(group_id):
|
||||
await GroupInfo.set_group_flag(group_id, 0)
|
||||
await group_auth.send(
|
||||
f'已为 {group_id} {cmd[:2]}群认证..'
|
||||
)
|
||||
await group_auth.send(f"已为 {group_id} {cmd[:2]}群认证..")
|
||||
|
||||
@@ -2,6 +2,10 @@ from typing import Optional
|
||||
from pathlib import Path
|
||||
from .utils import ConfigsManager
|
||||
|
||||
import platform
|
||||
if platform.system() == "Linux":
|
||||
import os
|
||||
hostip = os.popen("cat /etc/resolv.conf | grep nameserver | awk '{ print $2 }'").read().replace("\n","")
|
||||
|
||||
# 回复消息名称
|
||||
NICKNAME: str = "小真寻"
|
||||
@@ -18,6 +22,7 @@ port: str = "" # 数据库端口
|
||||
database: str = "" # 数据库名称
|
||||
|
||||
# 代理,例如 "http://127.0.0.1:7890"
|
||||
# 如果是WLS 可以 f"http://{hostip}:7890" 使用寄主机的代理
|
||||
SYSTEM_PROXY: Optional[str] = None # 全局代理
|
||||
|
||||
|
||||
|
||||
+12
-10
@@ -94,12 +94,14 @@ class ConfigsManager:
|
||||
del self._data[module]
|
||||
self.save()
|
||||
|
||||
def set_config(self, module: str, key: str, value: str):
|
||||
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 auto_save: 自动保存
|
||||
:param save_simple_data: 保存至config.yaml
|
||||
"""
|
||||
if module in self._data.keys():
|
||||
if (
|
||||
@@ -108,7 +110,8 @@ class ConfigsManager:
|
||||
):
|
||||
self._data[module][key]["value"] = value
|
||||
self._simple_data[module][key] = value
|
||||
self.save()
|
||||
if auto_save:
|
||||
self.save(save_simple_data=save_simple_data)
|
||||
|
||||
def set_help(self, module: str, key: str, help_: str):
|
||||
"""
|
||||
@@ -122,7 +125,7 @@ class ConfigsManager:
|
||||
self._data[module][key]["help"] = help_
|
||||
self.save()
|
||||
|
||||
def set_default_value(self, module: str, key: str, value: str):
|
||||
def set_default_value(self, module: str, key: str, value: Any):
|
||||
"""
|
||||
设置配置默认值
|
||||
:param module: 模块名
|
||||
@@ -199,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 |
Binary file not shown.
|
Before Width: | Height: | Size: 694 KiB After Width: | Height: | Size: 326 KiB |
+19
-19
@@ -22,9 +22,9 @@ class BagUser(db.Model):
|
||||
@classmethod
|
||||
async def get_user_total_gold(cls, user_qq: int, group_id: int) -> str:
|
||||
"""
|
||||
说明:
|
||||
说明:
|
||||
获取金币概况
|
||||
参数:
|
||||
参数:
|
||||
:param user_qq: qq号
|
||||
:param group_id: 所在群号
|
||||
"""
|
||||
@@ -44,9 +44,9 @@ class BagUser(db.Model):
|
||||
@classmethod
|
||||
async def get_gold(cls, user_qq: int, group_id: int) -> int:
|
||||
"""
|
||||
说明:
|
||||
说明:
|
||||
获取当前金币
|
||||
参数:
|
||||
参数:
|
||||
:param user_qq: qq号
|
||||
:param group_id: 所在群号
|
||||
"""
|
||||
@@ -64,9 +64,9 @@ class BagUser(db.Model):
|
||||
@classmethod
|
||||
async def get_property(cls, user_qq: int, group_id: int) -> Dict[str, int]:
|
||||
"""
|
||||
说明:
|
||||
说明:
|
||||
获取当前道具
|
||||
参数:
|
||||
参数:
|
||||
:param user_qq: qq号
|
||||
:param group_id: 所在群号
|
||||
"""
|
||||
@@ -84,9 +84,9 @@ class BagUser(db.Model):
|
||||
@classmethod
|
||||
async def add_gold(cls, user_qq: int, group_id: int, num: int):
|
||||
"""
|
||||
说明:
|
||||
说明:
|
||||
增加金币
|
||||
参数:
|
||||
参数:
|
||||
:param user_qq: qq号
|
||||
:param group_id: 所在群号
|
||||
:param num: 金币数量
|
||||
@@ -112,9 +112,9 @@ class BagUser(db.Model):
|
||||
@classmethod
|
||||
async def spend_gold(cls, user_qq: int, group_id: int, num: int):
|
||||
"""
|
||||
说明:
|
||||
说明:
|
||||
花费金币
|
||||
参数:
|
||||
参数:
|
||||
:param user_qq: qq号
|
||||
:param group_id: 所在群号
|
||||
:param num: 金币数量
|
||||
@@ -140,9 +140,9 @@ class BagUser(db.Model):
|
||||
@classmethod
|
||||
async def add_property(cls, user_qq: int, group_id: int, name: str):
|
||||
"""
|
||||
说明:
|
||||
说明:
|
||||
增加道具
|
||||
参数:
|
||||
参数:
|
||||
:param user_qq: qq号
|
||||
:param group_id: 所在群号
|
||||
:param name: 道具名称
|
||||
@@ -165,9 +165,9 @@ class BagUser(db.Model):
|
||||
cls, user_qq: int, group_id: int, name: str, num: int = 1
|
||||
) -> bool:
|
||||
"""
|
||||
说明:
|
||||
说明:
|
||||
使用/删除 道具
|
||||
参数:
|
||||
参数:
|
||||
:param user_qq: qq号
|
||||
:param group_id: 所在群号
|
||||
:param name: 道具名称
|
||||
@@ -192,9 +192,9 @@ class BagUser(db.Model):
|
||||
cls, user_qq: int, group_id: int, goods: "GoodsInfo", goods_num: int
|
||||
) -> bool:
|
||||
"""
|
||||
说明:
|
||||
说明:
|
||||
购买道具
|
||||
参数:
|
||||
参数:
|
||||
:param user_qq: 用户qq
|
||||
:param group_id: 所在群聊
|
||||
:param goods: 商品
|
||||
@@ -209,14 +209,14 @@ class BagUser(db.Model):
|
||||
return True
|
||||
except Exception as e:
|
||||
logger.error(f"buy_property 发生错误 {type(e)}:{e}")
|
||||
return False
|
||||
return False
|
||||
|
||||
@classmethod
|
||||
async def get_all_users(cls, group_id: Optional[int] = None) -> List["BagUser"]:
|
||||
"""
|
||||
说明:
|
||||
说明:
|
||||
获取所有用户数据
|
||||
参数:
|
||||
参数:
|
||||
:param group_id: 群号
|
||||
"""
|
||||
if not group_id:
|
||||
|
||||
+12
-12
@@ -15,9 +15,9 @@ class BanUser(db.Model):
|
||||
@classmethod
|
||||
async def check_ban_level(cls, user_qq: int, level: int) -> bool:
|
||||
"""
|
||||
说明:
|
||||
说明:
|
||||
检测ban掉目标的用户与unban用户的权限等级大小
|
||||
参数:
|
||||
参数:
|
||||
:param user_qq: unban用户的qq号
|
||||
:param level: ban掉目标用户的权限等级
|
||||
"""
|
||||
@@ -31,9 +31,9 @@ class BanUser(db.Model):
|
||||
@classmethod
|
||||
async def check_ban_time(cls, user_qq: int) -> str:
|
||||
"""
|
||||
说明:
|
||||
说明:
|
||||
检测用户被ban时长
|
||||
参数:
|
||||
参数:
|
||||
:param user_qq: qq号
|
||||
"""
|
||||
query = cls.query.where((cls.user_qq == user_qq))
|
||||
@@ -49,9 +49,9 @@ class BanUser(db.Model):
|
||||
@classmethod
|
||||
async def is_ban(cls, user_qq: int) -> bool:
|
||||
"""
|
||||
说明:
|
||||
说明:
|
||||
判断用户是否被ban
|
||||
参数:
|
||||
参数:
|
||||
:param user_qq: qq号
|
||||
"""
|
||||
if await cls.check_ban_time(user_qq):
|
||||
@@ -63,9 +63,9 @@ class BanUser(db.Model):
|
||||
@classmethod
|
||||
async def is_super_ban(cls, user_qq: int) -> bool:
|
||||
"""
|
||||
说明:
|
||||
说明:
|
||||
判断用户是否被ban
|
||||
参数:
|
||||
参数:
|
||||
:param user_qq: qq号
|
||||
"""
|
||||
user = await cls.query.where((cls.user_qq == user_qq)).gino.first()
|
||||
@@ -77,9 +77,9 @@ class BanUser(db.Model):
|
||||
@classmethod
|
||||
async def ban(cls, user_qq: int, ban_level: int, duration: int) -> bool:
|
||||
"""
|
||||
说明:
|
||||
说明:
|
||||
ban掉目标用户
|
||||
参数:
|
||||
参数:
|
||||
:param user_qq: 目标用户qq号
|
||||
:param ban_level: 使用ban命令用户的权限
|
||||
:param duration: ban时长,秒
|
||||
@@ -104,9 +104,9 @@ class BanUser(db.Model):
|
||||
@classmethod
|
||||
async def unban(cls, user_qq: int) -> bool:
|
||||
"""
|
||||
说明:
|
||||
说明:
|
||||
unban用户
|
||||
参数:
|
||||
参数:
|
||||
:param user_qq: qq号
|
||||
"""
|
||||
query = cls.query.where((cls.user_qq == user_qq))
|
||||
|
||||
+18
-18
@@ -28,9 +28,9 @@ class ChatHistory(db.Model):
|
||||
days: Optional[int] = None,
|
||||
) -> List["ChatHistory"]:
|
||||
"""
|
||||
说明:
|
||||
说明:
|
||||
获取用户消息
|
||||
参数:
|
||||
参数:
|
||||
:param uid: 用户qq
|
||||
:param msg_type: 消息类型,私聊或群聊
|
||||
:param days: 限制日期
|
||||
@@ -46,9 +46,9 @@ class ChatHistory(db.Model):
|
||||
date_scope: Tuple[datetime, datetime] = None,
|
||||
) -> List["ChatHistory"]:
|
||||
"""
|
||||
说明:
|
||||
说明:
|
||||
获取群聊指定用户聊天记录
|
||||
参数:
|
||||
参数:
|
||||
:param uid: qq
|
||||
:param gid: 群号
|
||||
:param limit: 获取数量
|
||||
@@ -63,9 +63,9 @@ class ChatHistory(db.Model):
|
||||
@classmethod
|
||||
async def get_group_user_msg_count(cls, uid: int, gid: int) -> Optional[int]:
|
||||
"""
|
||||
说明:
|
||||
说明:
|
||||
查询群聊指定用户的聊天记录数量
|
||||
参数:
|
||||
参数:
|
||||
:param uid: qq
|
||||
:param gid: 群号
|
||||
"""
|
||||
@@ -86,9 +86,9 @@ class ChatHistory(db.Model):
|
||||
date_scope: Optional[Tuple[datetime, datetime]] = None,
|
||||
) -> Optional[Tuple[int, int]]:
|
||||
"""
|
||||
说明:
|
||||
说明:
|
||||
获取排行数据
|
||||
参数:
|
||||
参数:
|
||||
:param gid: 群号
|
||||
:param limit: 获取数量
|
||||
:param order: 排序类型,desc,des
|
||||
@@ -103,9 +103,9 @@ class ChatHistory(db.Model):
|
||||
@classmethod
|
||||
async def get_group_first_msg_datetime(cls, gid: int) -> Optional[datetime]:
|
||||
"""
|
||||
说明:
|
||||
说明:
|
||||
获取群第一条记录消息时间
|
||||
参数:
|
||||
参数:
|
||||
:param gid:
|
||||
"""
|
||||
if (
|
||||
@@ -124,9 +124,9 @@ class ChatHistory(db.Model):
|
||||
days: Optional[int] = None,
|
||||
) -> int:
|
||||
"""
|
||||
说明:
|
||||
说明:
|
||||
获取用户消息数量
|
||||
参数:
|
||||
参数:
|
||||
:param uid: 用户qq
|
||||
:param msg_type: 消息类型,私聊或群聊
|
||||
:param days: 限制日期
|
||||
@@ -142,9 +142,9 @@ class ChatHistory(db.Model):
|
||||
days: Optional[int] = None,
|
||||
) -> List["ChatHistory"]:
|
||||
"""
|
||||
说明:
|
||||
说明:
|
||||
获取群聊消息
|
||||
参数:
|
||||
参数:
|
||||
:param gid: 用户qq
|
||||
:param days: 限制日期
|
||||
"""
|
||||
@@ -157,9 +157,9 @@ class ChatHistory(db.Model):
|
||||
days: Optional[int] = None,
|
||||
) -> List["ChatHistory"]:
|
||||
"""
|
||||
说明:
|
||||
说明:
|
||||
获取群聊消息数量
|
||||
参数:
|
||||
参数:
|
||||
:param gid: 用户qq
|
||||
:param days: 限制日期
|
||||
"""
|
||||
@@ -178,9 +178,9 @@ class ChatHistory(db.Model):
|
||||
is_select_count: bool = False,
|
||||
):
|
||||
"""
|
||||
说明:
|
||||
说明:
|
||||
获取消息查询query
|
||||
参数:
|
||||
参数:
|
||||
:param uid: 用户qq
|
||||
:param gid: 群号
|
||||
:param type_: 类型,私聊或群聊
|
||||
|
||||
+10
-10
@@ -15,9 +15,9 @@ class FriendUser(db.Model):
|
||||
@classmethod
|
||||
async def get_user_name(cls, user_id: int) -> str:
|
||||
"""
|
||||
说明:
|
||||
说明:
|
||||
获取好友用户名称
|
||||
参数:
|
||||
参数:
|
||||
:param user_id: qq号
|
||||
"""
|
||||
query = cls.query.where(cls.user_id == user_id)
|
||||
@@ -30,9 +30,9 @@ class FriendUser(db.Model):
|
||||
@classmethod
|
||||
async def add_friend_info(cls, user_id: int, user_name: str) -> bool:
|
||||
"""
|
||||
说明:
|
||||
说明:
|
||||
添加好友信息
|
||||
参数:
|
||||
参数:
|
||||
:param user_id: qq号
|
||||
:param user_name: 用户名称
|
||||
"""
|
||||
@@ -55,9 +55,9 @@ class FriendUser(db.Model):
|
||||
@classmethod
|
||||
async def delete_friend_info(cls, user_id: int) -> bool:
|
||||
"""
|
||||
说明:
|
||||
说明:
|
||||
删除好友信息
|
||||
参数:
|
||||
参数:
|
||||
:param user_id: qq号
|
||||
"""
|
||||
try:
|
||||
@@ -72,9 +72,9 @@ class FriendUser(db.Model):
|
||||
@classmethod
|
||||
async def get_friend_nickname(cls, user_id: int) -> str:
|
||||
"""
|
||||
说明:
|
||||
说明:
|
||||
获取用户昵称
|
||||
参数:
|
||||
参数:
|
||||
:param user_id: qq号
|
||||
"""
|
||||
query = cls.query.where(cls.user_id == user_id)
|
||||
@@ -92,9 +92,9 @@ class FriendUser(db.Model):
|
||||
@classmethod
|
||||
async def set_friend_nickname(cls, user_id: int, nickname: str) -> bool:
|
||||
"""
|
||||
说明:
|
||||
说明:
|
||||
设置用户昵称
|
||||
参数:
|
||||
参数:
|
||||
:param user_id: qq号
|
||||
:param nickname: 昵称
|
||||
"""
|
||||
|
||||
+102
-35
@@ -1,5 +1,5 @@
|
||||
from services.db_context import db
|
||||
from typing import Optional, List
|
||||
from typing import Optional, List, Tuple
|
||||
from services.log import logger
|
||||
|
||||
|
||||
@@ -12,27 +12,33 @@ class GoodsInfo(db.Model):
|
||||
goods_description = db.Column(db.TEXT(), nullable=False) # 商品描述
|
||||
goods_discount = db.Column(db.Numeric(scale=3, asdecimal=False), default=1) # 打折
|
||||
goods_limit_time = db.Column(db.BigInteger(), default=0) # 限时
|
||||
daily_limit = db.Column(db.Integer(), nullable=False, default=0) # 每日购买限制
|
||||
daily_purchase_limit = db.Column(
|
||||
db.JSON(), nullable=False, default={}
|
||||
) # 每日购买限制数据存储
|
||||
|
||||
_idx1 = db.Index("goods_group_users_idx1", "goods_name", unique=True)
|
||||
|
||||
@classmethod
|
||||
async def add_goods(
|
||||
cls,
|
||||
goods_name: str,
|
||||
goods_price: int,
|
||||
goods_description: str,
|
||||
goods_discount: float = 1,
|
||||
goods_limit_time: int = 0,
|
||||
cls,
|
||||
goods_name: str,
|
||||
goods_price: int,
|
||||
goods_description: str,
|
||||
goods_discount: float = 1,
|
||||
goods_limit_time: int = 0,
|
||||
daily_limit: int = 0,
|
||||
) -> bool:
|
||||
"""
|
||||
说明:
|
||||
说明:
|
||||
添加商品
|
||||
参数:
|
||||
参数:
|
||||
:param goods_name: 商品名称
|
||||
:param goods_price: 商品价格
|
||||
:param goods_description: 商品简介
|
||||
:param goods_discount: 商品折扣
|
||||
:param goods_limit_time: 商品限时
|
||||
:param daily_limit: 每日购买限制
|
||||
"""
|
||||
try:
|
||||
if not await cls.get_goods_info(goods_name):
|
||||
@@ -42,6 +48,7 @@ class GoodsInfo(db.Model):
|
||||
goods_description=goods_description,
|
||||
goods_discount=goods_discount,
|
||||
goods_limit_time=goods_limit_time,
|
||||
daily_limit=daily_limit,
|
||||
)
|
||||
return True
|
||||
except Exception as e:
|
||||
@@ -51,15 +58,15 @@ class GoodsInfo(db.Model):
|
||||
@classmethod
|
||||
async def delete_goods(cls, goods_name: str) -> bool:
|
||||
"""
|
||||
说明:
|
||||
说明:
|
||||
删除商品
|
||||
参数:
|
||||
参数:
|
||||
:param goods_name: 商品名称
|
||||
"""
|
||||
query = (
|
||||
await cls.query.where(cls.goods_name == goods_name)
|
||||
.with_for_update()
|
||||
.gino.first()
|
||||
.with_for_update()
|
||||
.gino.first()
|
||||
)
|
||||
if not query:
|
||||
return False
|
||||
@@ -68,50 +75,51 @@ class GoodsInfo(db.Model):
|
||||
|
||||
@classmethod
|
||||
async def update_goods(
|
||||
cls,
|
||||
goods_name: str,
|
||||
goods_price: Optional[int] = None,
|
||||
goods_description: Optional[str] = None,
|
||||
goods_discount: Optional[float] = None,
|
||||
goods_limit_time: Optional[int] = None,
|
||||
cls,
|
||||
goods_name: str,
|
||||
goods_price: Optional[int] = None,
|
||||
goods_description: Optional[str] = None,
|
||||
goods_discount: Optional[float] = None,
|
||||
goods_limit_time: Optional[int] = None,
|
||||
daily_limit: Optional[int] = None
|
||||
) -> bool:
|
||||
"""
|
||||
说明:
|
||||
说明:
|
||||
更新商品信息
|
||||
参数:
|
||||
参数:
|
||||
:param goods_name: 商品名称
|
||||
:param goods_price: 商品价格
|
||||
:param goods_description: 商品简介
|
||||
:param goods_discount: 商品折扣
|
||||
:param goods_limit_time: 商品限时时间
|
||||
:param daily_limit: 每日次数限制
|
||||
"""
|
||||
try:
|
||||
query = (
|
||||
await cls.query.where(cls.goods_name == goods_name)
|
||||
.with_for_update()
|
||||
.gino.first()
|
||||
.with_for_update()
|
||||
.gino.first()
|
||||
)
|
||||
if not query:
|
||||
return False
|
||||
if goods_price:
|
||||
await query.update(goods_price=goods_price).apply()
|
||||
if goods_description:
|
||||
await query.update(goods_description=goods_description).apply()
|
||||
if goods_discount:
|
||||
await query.update(goods_discount=goods_discount).apply()
|
||||
if goods_limit_time:
|
||||
await query.update(goods_limit_time=goods_limit_time).apply()
|
||||
await query.update(
|
||||
goods_price=goods_price or query.goods_price,
|
||||
goods_description=goods_description or query.goods_description,
|
||||
goods_discount=goods_discount or query.goods_discount,
|
||||
goods_limit_time=goods_limit_time if goods_limit_time is not None else query.goods_limit_time,
|
||||
daily_limit=daily_limit if daily_limit is not None else query.daily_limit,
|
||||
).apply()
|
||||
return True
|
||||
except Exception as e:
|
||||
logger.error(f"GoodsInfo update_goods 发生错误 {type(e)}:{e}")
|
||||
return False
|
||||
return False
|
||||
|
||||
@classmethod
|
||||
async def get_goods_info(cls, goods_name: str) -> "GoodsInfo":
|
||||
"""
|
||||
说明:
|
||||
说明:
|
||||
获取商品对象
|
||||
参数:
|
||||
参数:
|
||||
:param goods_name: 商品名称
|
||||
"""
|
||||
return await cls.query.where(cls.goods_name == goods_name).gino.first()
|
||||
@@ -119,7 +127,7 @@ class GoodsInfo(db.Model):
|
||||
@classmethod
|
||||
async def get_all_goods(cls) -> List["GoodsInfo"]:
|
||||
"""
|
||||
说明:
|
||||
说明:
|
||||
获得全部有序商品对象
|
||||
"""
|
||||
query = await cls.query.gino.all()
|
||||
@@ -130,3 +138,62 @@ class GoodsInfo(db.Model):
|
||||
goods_lst.append([x for x in query if x.id == min_id][0])
|
||||
id_lst.remove(min_id)
|
||||
return goods_lst
|
||||
|
||||
@classmethod
|
||||
async def add_user_daily_purchase(
|
||||
cls, goods: "GoodsInfo", user_id: int, group_id: int, num: int = 1
|
||||
):
|
||||
"""
|
||||
说明:
|
||||
添加用户明日购买限制
|
||||
参数:
|
||||
:param goods: 商品
|
||||
:param user_id: 用户id
|
||||
:param group_id: 群号
|
||||
:param num: 数量
|
||||
"""
|
||||
user_id = str(user_id)
|
||||
group_id = str(group_id)
|
||||
if goods and goods.daily_limit and goods.daily_limit > 0:
|
||||
if not goods.daily_purchase_limit.get(group_id):
|
||||
goods.daily_purchase_limit[group_id] = {}
|
||||
if not goods.daily_purchase_limit[group_id].get(user_id):
|
||||
goods.daily_purchase_limit[group_id][user_id] = 0
|
||||
goods.daily_purchase_limit[group_id][user_id] += num
|
||||
await goods.update(daily_purchase_limit=goods.daily_purchase_limit).apply()
|
||||
|
||||
@classmethod
|
||||
async def check_user_daily_purchase(
|
||||
cls, goods: "GoodsInfo", user_id: int, group_id: int, num: int = 1
|
||||
) -> Tuple[bool, int]:
|
||||
"""
|
||||
说明:
|
||||
检测用户每日购买上限
|
||||
参数:
|
||||
:param goods: 商品
|
||||
:param user_id: 用户id
|
||||
:param group_id: 群号
|
||||
:param num: 数量
|
||||
"""
|
||||
user_id = str(user_id)
|
||||
group_id = str(group_id)
|
||||
if goods and goods.daily_limit > 0:
|
||||
if (
|
||||
not goods.daily_limit
|
||||
or not goods.daily_purchase_limit.get(group_id)
|
||||
or not goods.daily_purchase_limit[group_id].get(user_id)
|
||||
):
|
||||
return goods.daily_limit - num < 0, goods.daily_limit
|
||||
if goods.daily_purchase_limit[group_id][user_id] + num > goods.daily_limit:
|
||||
return (
|
||||
True,
|
||||
goods.daily_limit - goods.daily_purchase_limit[group_id][user_id],
|
||||
)
|
||||
return False, 0
|
||||
|
||||
@classmethod
|
||||
async def reset_daily_purchase(cls):
|
||||
"""
|
||||
重置每次次数限制
|
||||
"""
|
||||
await cls.update.values(daily_purchase_limit={}).gino.status()
|
||||
|
||||
@@ -17,9 +17,9 @@ class GroupInfo(db.Model):
|
||||
@classmethod
|
||||
async def get_group_info(cls, group_id: int) -> "GroupInfo":
|
||||
"""
|
||||
说明:
|
||||
说明:
|
||||
获取群信息
|
||||
参数:
|
||||
参数:
|
||||
:param group_id: 群号
|
||||
"""
|
||||
query = cls.query.where(cls.group_id == group_id)
|
||||
@@ -35,9 +35,9 @@ class GroupInfo(db.Model):
|
||||
group_flag: Optional[int] = None,
|
||||
) -> bool:
|
||||
"""
|
||||
说明:
|
||||
说明:
|
||||
添加群信息
|
||||
参数:
|
||||
参数:
|
||||
:param group_id: 群号
|
||||
:param group_name: 群名称
|
||||
:param max_member_count: 群员最大数量
|
||||
@@ -74,9 +74,9 @@ class GroupInfo(db.Model):
|
||||
@classmethod
|
||||
async def delete_group_info(cls, group_id: int):
|
||||
"""
|
||||
说明:
|
||||
说明:
|
||||
删除群信息
|
||||
参数:
|
||||
参数:
|
||||
:param group_id: 群号
|
||||
"""
|
||||
await cls.delete.where(cls.group_id == group_id).gino.status()
|
||||
@@ -84,7 +84,7 @@ class GroupInfo(db.Model):
|
||||
@classmethod
|
||||
async def get_all_group(cls) -> List["GroupInfo"]:
|
||||
"""
|
||||
说明:
|
||||
说明:
|
||||
获取所有群对象
|
||||
"""
|
||||
query = await cls.query.gino.all()
|
||||
|
||||
+14
-14
@@ -27,9 +27,9 @@ class GroupInfoUser(db.Model):
|
||||
uid: Optional[int] = None,
|
||||
) -> bool:
|
||||
"""
|
||||
说明:
|
||||
说明:
|
||||
添加群内用户信息
|
||||
参数:
|
||||
参数:
|
||||
:param user_qq: qq号
|
||||
:param group_id: 群号
|
||||
:param user_name: 用户名称
|
||||
@@ -57,9 +57,9 @@ class GroupInfoUser(db.Model):
|
||||
cls, user_qq: int, group_id: int
|
||||
) -> "GroupInfoUser":
|
||||
"""
|
||||
说明:
|
||||
说明:
|
||||
查询群员信息
|
||||
参数:
|
||||
参数:
|
||||
:param user_qq: qq号
|
||||
:param group_id: 群号
|
||||
"""
|
||||
@@ -71,9 +71,9 @@ class GroupInfoUser(db.Model):
|
||||
@classmethod
|
||||
async def delete_member_info(cls, user_qq: int, group_id: int) -> bool:
|
||||
"""
|
||||
说明:
|
||||
说明:
|
||||
删除群员信息
|
||||
参数:
|
||||
参数:
|
||||
:param user_qq: qq号
|
||||
:param group_id: 群号
|
||||
"""
|
||||
@@ -96,9 +96,9 @@ class GroupInfoUser(db.Model):
|
||||
@classmethod
|
||||
async def get_group_member_id_list(cls, group_id: int) -> List[int]:
|
||||
"""
|
||||
说明:
|
||||
说明:
|
||||
获取该群所有用户qq
|
||||
参数:
|
||||
参数:
|
||||
:param group_id: 群号
|
||||
"""
|
||||
member_list = []
|
||||
@@ -112,9 +112,9 @@ class GroupInfoUser(db.Model):
|
||||
cls, user_qq: int, group_id: int, nickname: str
|
||||
) -> bool:
|
||||
"""
|
||||
说明:
|
||||
说明:
|
||||
设置群员在该群内的昵称
|
||||
参数:
|
||||
参数:
|
||||
:param user_qq: qq号
|
||||
:param group_id: 群号
|
||||
:param nickname: 昵称
|
||||
@@ -131,9 +131,9 @@ class GroupInfoUser(db.Model):
|
||||
@classmethod
|
||||
async def get_user_all_group(cls, user_qq: int) -> List[int]:
|
||||
"""
|
||||
说明:
|
||||
说明:
|
||||
获取该用户所在的所有群聊
|
||||
参数:
|
||||
参数:
|
||||
:param user_qq: 用户qq
|
||||
"""
|
||||
query = await cls.query.where(cls.user_qq == user_qq).gino.all()
|
||||
@@ -144,9 +144,9 @@ class GroupInfoUser(db.Model):
|
||||
@classmethod
|
||||
async def get_group_member_nickname(cls, user_qq: int, group_id: int) -> str:
|
||||
"""
|
||||
说明:
|
||||
说明:
|
||||
获取用户在该群的昵称
|
||||
参数:
|
||||
参数:
|
||||
:param user_qq: qq号
|
||||
:param group_id: 群号
|
||||
"""
|
||||
|
||||
+10
-10
@@ -17,9 +17,9 @@ class LevelUser(db.Model):
|
||||
@classmethod
|
||||
async def get_user_level(cls, user_qq: int, group_id: int) -> int:
|
||||
"""
|
||||
说明:
|
||||
说明:
|
||||
获取用户在群内的等级
|
||||
参数:
|
||||
参数:
|
||||
:param user_qq: qq号
|
||||
:param group_id: 群号
|
||||
"""
|
||||
@@ -35,9 +35,9 @@ class LevelUser(db.Model):
|
||||
cls, user_qq: int, group_id: int, level: int, group_flag: int = 0
|
||||
) -> bool:
|
||||
"""
|
||||
说明:
|
||||
说明:
|
||||
设置用户在群内的权限
|
||||
参数:
|
||||
参数:
|
||||
:param user_qq: qq号
|
||||
:param group_id: 群号
|
||||
:param level: 权限等级
|
||||
@@ -64,9 +64,9 @@ class LevelUser(db.Model):
|
||||
@classmethod
|
||||
async def delete_level(cls, user_qq: int, group_id: int) -> bool:
|
||||
"""
|
||||
说明:
|
||||
说明:
|
||||
删除用户权限
|
||||
参数:
|
||||
参数:
|
||||
:param user_qq: qq号
|
||||
:param group_id: 群号
|
||||
"""
|
||||
@@ -82,9 +82,9 @@ class LevelUser(db.Model):
|
||||
@classmethod
|
||||
async def check_level(cls, user_qq: int, group_id: int, level: int) -> bool:
|
||||
"""
|
||||
说明:
|
||||
说明:
|
||||
检查用户权限等级是否大于 level
|
||||
参数:
|
||||
参数:
|
||||
:param user_qq: qq号
|
||||
:param group_id: 群号
|
||||
:param level: 权限等级
|
||||
@@ -112,9 +112,9 @@ class LevelUser(db.Model):
|
||||
@classmethod
|
||||
async def is_group_flag(cls, user_qq: int, group_id: int) -> bool:
|
||||
"""
|
||||
说明:
|
||||
说明:
|
||||
检测是否会被自动更新刷新权限
|
||||
参数:
|
||||
参数:
|
||||
:param user_qq: qq号
|
||||
:param group_id: 群号
|
||||
"""
|
||||
|
||||
@@ -80,9 +80,9 @@ class SignGroupUser(db.Model):
|
||||
@classmethod
|
||||
async def get_all_impression(cls, group_id: int) -> "list, list, list":
|
||||
"""
|
||||
说明:
|
||||
说明:
|
||||
获取该群所有用户 id 及对应 好感度
|
||||
参数:
|
||||
参数:
|
||||
:param group_id: 群号
|
||||
"""
|
||||
impression_list = []
|
||||
|
||||
@@ -1,6 +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.image_utils import text2image
|
||||
from utils.message_builder import image
|
||||
from .data_source import (
|
||||
add_live_sub,
|
||||
delete_sub,
|
||||
@@ -12,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站订阅"
|
||||
@@ -50,12 +54,22 @@ __plugin_configs__ = {
|
||||
"value": 5,
|
||||
"help": "群内bilibili订阅需要管理的权限",
|
||||
"default_value": 5,
|
||||
}
|
||||
},
|
||||
"LIVE_MSG_AT_ALL": {
|
||||
"value": False,
|
||||
"help": "直播提醒是否AT全体(仅在真寻是管理员时生效)",
|
||||
"default_value": False,
|
||||
},
|
||||
"UP_MSG_AT_ALL": {
|
||||
"value": False,
|
||||
"help": "UP动态投稿提醒是否AT全体(仅在真寻是管理员时生效)",
|
||||
"default_value": False,
|
||||
},
|
||||
}
|
||||
|
||||
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()
|
||||
|
||||
@@ -144,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}"
|
||||
)
|
||||
@@ -195,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()
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
# 推送
|
||||
@@ -207,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):
|
||||
@@ -228,13 +248,28 @@ async def send_sub_msg(rst: str, sub: BilibiliSub, bot: Bot):
|
||||
:param sub: BilibiliSub
|
||||
:param bot: Bot
|
||||
"""
|
||||
temp_group = []
|
||||
if rst:
|
||||
for x in sub.sub_users.split(",")[:-1]:
|
||||
try:
|
||||
if ":" in x:
|
||||
await bot.send_group_msg(
|
||||
group_id=int(x.split(":")[1]), message=Message(rst)
|
||||
)
|
||||
if ":" in x and x.split(":")[1] not in temp_group:
|
||||
group_id = int(x.split(":")[1])
|
||||
temp_group.append(group_id)
|
||||
if (
|
||||
await bot.get_group_member_info(
|
||||
group_id=group_id, user_id=int(bot.self_id), no_cache=True
|
||||
)
|
||||
)["role"] in ["owner", "admin"]:
|
||||
if (
|
||||
sub.sub_type == "live"
|
||||
and Config.get_config("bilibili_sub", "LIVE_MSG_AT_ALL")
|
||||
) or (
|
||||
sub.sub_type == "up"
|
||||
and Config.get_config("bilibili_sub", "UP_MSG_AT_ALL")
|
||||
):
|
||||
rst = "[CQ:at,qq=all]\n" + 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:
|
||||
|
||||
@@ -1,6 +1,10 @@
|
||||
from bilireq.exceptions import ResponseCodeError
|
||||
from nonebot.adapters.onebot.v11 import MessageSegment
|
||||
|
||||
from utils.manager import resources_manager
|
||||
from asyncio.exceptions import TimeoutError
|
||||
|
||||
from utils.utils import get_bot
|
||||
from .model import BilibiliSub
|
||||
from bilireq.live import get_room_info_by_id
|
||||
from .utils import get_meta
|
||||
@@ -8,7 +12,7 @@ from utils.message_builder import image
|
||||
from bilireq.user import get_user_info
|
||||
from bilireq import dynamic
|
||||
from .utils import get_videos
|
||||
from typing import Optional
|
||||
from typing import Optional, Tuple
|
||||
from configs.path_config import IMAGE_PATH
|
||||
from datetime import datetime
|
||||
from utils.browser import get_browser
|
||||
@@ -35,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 "添加订阅失败..."
|
||||
@@ -205,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 "发生了预料之外的错误..请稍后再试或联系管理员....."
|
||||
@@ -227,6 +232,7 @@ async def _get_live_status(id_: int) -> Optional[str]:
|
||||
await BilibiliSub.update_sub_info(id_, live_status=live_status)
|
||||
if sub.live_status == 0 and live_status == 1:
|
||||
return (
|
||||
f""
|
||||
f"{image(cover)}\n"
|
||||
f"{sub.uname} 开播啦!\n"
|
||||
f"标题:{title}\n"
|
||||
@@ -238,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_)
|
||||
@@ -246,26 +252,27 @@ 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 = 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"]
|
||||
rst = ""
|
||||
if dynamic_img:
|
||||
await BilibiliSub.update_sub_info(id_, dynamic_upload_time=dynamic_upload_time)
|
||||
rst += f"{uname} 发布了动态!\n" f"{dynamic_img}\n"
|
||||
rst += f"{uname} 发布了动态!\n" f"{dynamic_img}\n{link}"
|
||||
if (
|
||||
latest_video_created
|
||||
and _user.latest_video_created
|
||||
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
|
||||
)
|
||||
@@ -276,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
|
||||
|
||||
|
||||
@@ -304,7 +311,7 @@ async def _get_season_status(id_) -> Optional[str]:
|
||||
|
||||
async def get_user_dynamic(
|
||||
uid: int, local_user: BilibiliSub
|
||||
) -> "Optional[MessageSegment], int":
|
||||
) -> Tuple[Optional[MessageSegment], int, str]:
|
||||
"""
|
||||
获取用户动态
|
||||
:param uid: 用户uid
|
||||
@@ -316,33 +323,50 @@ async def get_user_dynamic(
|
||||
browser = await get_browser()
|
||||
if dynamic_info.get("cards") and browser:
|
||||
dynamic_upload_time = dynamic_info["cards"][0]["desc"]["timestamp"]
|
||||
dynamic_id = dynamic_info["cards"][0]["desc"]["dynamic_id"]
|
||||
if local_user.dynamic_upload_time < dynamic_upload_time:
|
||||
page = await browser.new_page()
|
||||
context = await browser.new_context()
|
||||
page = await context.new_page()
|
||||
try:
|
||||
await page.goto(
|
||||
f"https://space.bilibili.com/{local_user.uid}/dynamic",
|
||||
f"https://t.bilibili.com/{dynamic_id}",
|
||||
wait_until="networkidle",
|
||||
timeout=10000,
|
||||
)
|
||||
await page.set_viewport_size({"width": 2560, "height": 1080, "timeout": 10000*20}) # timeout: 200s
|
||||
# await page.set_viewport_size({"width": 2560, "height": 1080, "timeout": 10000*20}) # timeout: 200s
|
||||
# 删除置顶
|
||||
# await page.evaluate(
|
||||
# """
|
||||
# xs = document.getElementsByClassName('bili-dyn-item__tag');
|
||||
# for (x of xs) {
|
||||
# x.parentNode.parentNode.remove();
|
||||
# }
|
||||
# """
|
||||
# )
|
||||
# async with page.expect_popup() as popup_info:
|
||||
# await page.locator(".bili-rich-text__content").click()
|
||||
# details_page = await popup_info.value
|
||||
await page.set_viewport_size(
|
||||
{"width": 2560, "height": 1080, "timeout": 10000 * 20}
|
||||
)
|
||||
await page.wait_for_selector(".panel-area")
|
||||
await page.evaluate(
|
||||
"""
|
||||
xs = document.getElementsByClassName('bili-dyn-item__tag');
|
||||
for (x of xs) {
|
||||
x.parentNode.parentNode.remove();
|
||||
}
|
||||
xs = document.getElementById('internationalHeader');
|
||||
xs.remove();
|
||||
xs = document.getElementsByClassName('panel-area')
|
||||
xs[0].remove();
|
||||
"""
|
||||
)
|
||||
card = page.locator(".bili-dyn-list__item").first
|
||||
card = page.locator(".detail-card")
|
||||
await card.wait_for()
|
||||
# 截图并保存
|
||||
await card.screenshot(
|
||||
path=dynamic_path / f"{local_user.sub_id}_{dynamic_upload_time}.jpg",
|
||||
)
|
||||
except Exception as e:
|
||||
logger.error(f"B站订阅:获取用户动态 发送错误 {type(e)}:{e}")
|
||||
finally:
|
||||
await context.close()
|
||||
await page.close()
|
||||
return (
|
||||
image(
|
||||
@@ -350,8 +374,9 @@ async def get_user_dynamic(
|
||||
"bilibili_sub/dynamic",
|
||||
),
|
||||
dynamic_upload_time,
|
||||
f"https://t.bilibili.com/{dynamic_id}"
|
||||
)
|
||||
return None, None
|
||||
return None, 0, ''
|
||||
|
||||
|
||||
class SubManager:
|
||||
|
||||
@@ -47,9 +47,9 @@ class BilibiliSub(db.Model):
|
||||
season_update_time: Optional[datetime] = None,
|
||||
) -> bool:
|
||||
"""
|
||||
说明:
|
||||
说明:
|
||||
添加订阅
|
||||
参数:
|
||||
参数:
|
||||
:param sub_id: 订阅名称,房间号,番剧号等
|
||||
:param sub_type: 订阅类型
|
||||
:param sub_user: 订阅此条目的用户
|
||||
@@ -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
|
||||
@@ -110,9 +109,9 @@ class BilibiliSub(db.Model):
|
||||
@classmethod
|
||||
async def delete_bilibili_sub(cls, sub_id: int, sub_user: str) -> bool:
|
||||
"""
|
||||
说明:
|
||||
说明:
|
||||
删除订阅
|
||||
参数:
|
||||
参数:
|
||||
:param sub_id: 订阅名称
|
||||
:param sub_user: 删除此条目的用户
|
||||
"""
|
||||
@@ -140,9 +139,9 @@ class BilibiliSub(db.Model):
|
||||
@classmethod
|
||||
async def get_sub(cls, sub_id: int) -> Optional["BilibiliSub"]:
|
||||
"""
|
||||
说明:
|
||||
说明:
|
||||
获取订阅对象
|
||||
参数:
|
||||
参数:
|
||||
:param sub_id: 订阅 id
|
||||
"""
|
||||
return await cls.query.where(cls.sub_id == sub_id).gino.first()
|
||||
@@ -173,9 +172,9 @@ class BilibiliSub(db.Model):
|
||||
season_update_time: Optional[datetime] = None,
|
||||
) -> bool:
|
||||
"""
|
||||
说明:
|
||||
说明:
|
||||
更新订阅信息
|
||||
参数:
|
||||
参数:
|
||||
:param sub_id: 订阅名称,房间号,番剧号等
|
||||
:param live_short_id: 直接短 id
|
||||
:param live_status: 主播开播状态
|
||||
@@ -232,7 +231,7 @@ class BilibiliSub(db.Model):
|
||||
cls,
|
||||
) -> "List[BilibiliSub], List[BilibiliSub], List[BilibiliSub]":
|
||||
"""
|
||||
说明:
|
||||
说明:
|
||||
分类获取所有数据
|
||||
"""
|
||||
live_data = []
|
||||
|
||||
+10
-10
@@ -25,9 +25,9 @@ class BlackWord(db.Model):
|
||||
punish_level: int,
|
||||
):
|
||||
"""
|
||||
说明:
|
||||
说明:
|
||||
添加用户发送的敏感词
|
||||
参数:
|
||||
参数:
|
||||
:param user_qq: 用户id
|
||||
:param group_id: 群号
|
||||
:param black_word: 黑名单词汇
|
||||
@@ -52,9 +52,9 @@ class BlackWord(db.Model):
|
||||
id_: Optional[int] = None,
|
||||
) -> bool:
|
||||
"""
|
||||
说明:
|
||||
说明:
|
||||
设置处罚
|
||||
参数:
|
||||
参数:
|
||||
:param user_qq: 用户id
|
||||
:param punish: 处罚
|
||||
:param black_word: 黑名单词汇
|
||||
@@ -81,9 +81,9 @@ class BlackWord(db.Model):
|
||||
cls, user_qq: int, days: int = 7, punish_level: Optional[int] = None
|
||||
) -> int:
|
||||
"""
|
||||
说明:
|
||||
说明:
|
||||
获取用户规定周期内的犯事次数
|
||||
参数:
|
||||
参数:
|
||||
:param user_qq: 用户qq
|
||||
:param days: 周期天数
|
||||
:param punish_level: 惩罚等级
|
||||
@@ -101,9 +101,9 @@ class BlackWord(db.Model):
|
||||
@classmethod
|
||||
async def get_user_punish_level(cls, user_qq: int, days: int = 7) -> Optional[int]:
|
||||
"""
|
||||
说明:
|
||||
说明:
|
||||
获取用户最近一次的惩罚记录等级
|
||||
参数:
|
||||
参数:
|
||||
:param user_qq: 用户qq
|
||||
:param days: 周期天数
|
||||
"""
|
||||
@@ -125,9 +125,9 @@ class BlackWord(db.Model):
|
||||
date_type: str = "=",
|
||||
) -> List["BlackWord"]:
|
||||
"""
|
||||
说明:
|
||||
说明:
|
||||
通过指定条件查询数据
|
||||
参数:
|
||||
参数:
|
||||
:param user_qq: 用户qq
|
||||
:param group_id: 群号
|
||||
:param date: 日期
|
||||
|
||||
@@ -6,7 +6,7 @@ from configs.config import Config
|
||||
from pathlib import Path
|
||||
from services.log import logger
|
||||
from models.ban_user import BanUser
|
||||
from nonebot.adapters.onebot.v11.exception import ActionFailed
|
||||
from nonebot.adapters.onebot.v11 import ActionFailed
|
||||
from models.group_member_info import GroupInfoUser
|
||||
from utils.http_utils import AsyncHttpx
|
||||
import random
|
||||
|
||||
@@ -80,8 +80,8 @@ async def _(
|
||||
except TimeoutError:
|
||||
await bt.finish(f"搜索 {keyword} 超时...")
|
||||
except Exception as e:
|
||||
await bt.finish(f"bt 其他未知错误..")
|
||||
logger.error(f"bt 错误 {type(e)}:{e}")
|
||||
await bt.finish(f"bt 其他未知错误..")
|
||||
if not send_flag:
|
||||
await bt.send(f"{keyword} 未搜索到...")
|
||||
logger.info(f"USER {event.user_id} BT搜索 {keyword} 第 {page} 页")
|
||||
|
||||
@@ -9,7 +9,7 @@ import platform
|
||||
# asyncio.set_event_loop_policy(asyncio.WindowsSelectorEventLoopPolicy())
|
||||
|
||||
|
||||
url = "http://www.eclzz.win"
|
||||
url = "http://www.eclzz.cool"
|
||||
|
||||
|
||||
async def get_bt_info(keyword: str, page: int):
|
||||
|
||||
@@ -5,7 +5,7 @@ from .data_source import check_update, get_latest_version_data
|
||||
from services.log import logger
|
||||
from utils.utils import scheduler, get_bot
|
||||
from pathlib import Path
|
||||
from configs.config import Config
|
||||
from configs.config import NICKNAME, Config
|
||||
from nonebot.rule import to_me
|
||||
from nonebot.params import ArgStr
|
||||
import platform
|
||||
@@ -27,7 +27,7 @@ __plugin_author__ = "HibiKier"
|
||||
__plugin_configs__ = {
|
||||
"UPDATE_REMIND": {
|
||||
"value": True,
|
||||
"help": "真寻是否自动检查更新",
|
||||
"help": "真寻是否检测版本状态",
|
||||
"default": True,
|
||||
},
|
||||
"AUTO_UPDATE_ZHENXUN": {
|
||||
@@ -71,18 +71,17 @@ async def _(bot: Bot):
|
||||
)
|
||||
|
||||
|
||||
@restart.handle()
|
||||
async def _():
|
||||
if str(platform.system()).lower() == "windows":
|
||||
await restart.finish("暂无windows重启脚本...")
|
||||
|
||||
|
||||
@restart.got("flag", prompt="确定是否重启真寻?确定请回复[是|好|确定](重启失败咱们将失去联系,请谨慎!)")
|
||||
@restart.got("flag", prompt=f"确定是否重启{NICKNAME}?确定请回复[是|好|确定](重启失败咱们将失去联系,请谨慎!)")
|
||||
async def _(flag: str = ArgStr("flag")):
|
||||
if flag.lower() in ["true", "是", "好", "确定", "确定是"]:
|
||||
await restart.send("开始重启真寻..请稍等...")
|
||||
await restart.send(f"开始重启{NICKNAME}..请稍等...")
|
||||
open("is_restart", "w")
|
||||
os.system("./restart.sh")
|
||||
if str(platform.system()).lower() == "windows":
|
||||
import sys
|
||||
python = sys.executable
|
||||
os.execl(python, python, *sys.argv)
|
||||
else:
|
||||
os.system("./restart.sh")
|
||||
else:
|
||||
await restart.send("已取消操作...")
|
||||
|
||||
@@ -126,5 +125,5 @@ async def _():
|
||||
if code == 200:
|
||||
await bot.send_private_msg(
|
||||
user_id=int(list(bot.config.superusers)[0]),
|
||||
message=f"更新完毕,请重启真寻....",
|
||||
message=f"更新完毕,请重启{NICKNAME}....",
|
||||
)
|
||||
|
||||
@@ -162,7 +162,7 @@ def _file_handle(latest_version: str) -> str:
|
||||
if backup_file.exists():
|
||||
backup_file.unlink()
|
||||
if file not in [config_file, config_path_file]:
|
||||
os.rename(file.absolute(), backup_file.absolute())
|
||||
shutil.move(file.absolute(), backup_file.absolute())
|
||||
else:
|
||||
with open(file, "r", encoding="utf8") as rf:
|
||||
data = rf.read()
|
||||
@@ -174,7 +174,7 @@ def _file_handle(latest_version: str) -> str:
|
||||
old_file = Path() / file
|
||||
if old_file not in [config_file, config_path_file] and file != "configs":
|
||||
if not old_file.exists() and new_file.exists():
|
||||
os.rename(new_file.absolute(), old_file.absolute())
|
||||
shutil.move(new_file.absolute(), old_file.absolute())
|
||||
logger.info(f"已更新文件:{file}")
|
||||
# except Exception as e:
|
||||
# error = f'{type(e)}:{e}'
|
||||
@@ -189,6 +189,9 @@ def _file_handle(latest_version: str) -> str:
|
||||
local_update_info_file.unlink()
|
||||
with open(_version_file, "w", encoding="utf8") as f:
|
||||
f.write(f"__version__: {latest_version}")
|
||||
os.system(
|
||||
f"poetry run pip install -r {(Path() / 'pyproject.toml').absolute()}"
|
||||
)
|
||||
return error
|
||||
|
||||
|
||||
|
||||
+23
-15
@@ -1,4 +1,7 @@
|
||||
from typing import Tuple, Any
|
||||
|
||||
from nonebot import on_regex
|
||||
from nonebot.params import RegexGroup
|
||||
from nonebot.typing import T_State
|
||||
from nonebot.adapters.onebot.v11 import Bot, MessageEvent
|
||||
from utils.message_builder import image
|
||||
@@ -11,7 +14,9 @@ __plugin_usage__ = """
|
||||
usage:
|
||||
三次元也不戳,嘿嘿嘿
|
||||
指令:
|
||||
cos/coser
|
||||
?N连cos/coser
|
||||
示例:cos
|
||||
示例:5连cos (单次请求张数小于9)
|
||||
""".strip()
|
||||
__plugin_des__ = "三次元也不戳,嘿嘿嘿"
|
||||
__plugin_cmd__ = ["cos/coser"]
|
||||
@@ -31,21 +36,24 @@ __plugin_configs__ = {
|
||||
},
|
||||
}
|
||||
|
||||
coser = on_regex("^(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, state: T_State):
|
||||
try:
|
||||
msg_id = await coser.send(image(url))
|
||||
withdraw_message_manager.withdraw_message(
|
||||
event,
|
||||
msg_id["message_id"],
|
||||
Config.get_config("coser", "WITHDRAW_COS_MESSAGE"),
|
||||
)
|
||||
except Exception as e:
|
||||
await coser.send("你cos给我看!")
|
||||
logger.error(f"coser 发送了未知错误 {type(e)}:{e}")
|
||||
async def _(event: MessageEvent, reg_group: Tuple[Any, ...] = RegexGroup()):
|
||||
num = reg_group[0] or 1
|
||||
for _ in range(int(num)):
|
||||
try:
|
||||
msg_id = await coser.send(image(url))
|
||||
withdraw_message_manager.withdraw_message(
|
||||
event,
|
||||
msg_id["message_id"],
|
||||
Config.get_config("coser", "WITHDRAW_COS_MESSAGE"),
|
||||
)
|
||||
except Exception as e:
|
||||
await coser.send("你cos给我看!")
|
||||
logger.error(f"coser 发送了未知错误 {type(e)}:{e}")
|
||||
|
||||
@@ -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="",
|
||||
|
||||
@@ -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")
|
||||
|
||||
@@ -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,
|
||||
)
|
||||
)
|
||||
|
||||
@@ -288,7 +288,10 @@ class PrtsHandle(BaseHandle[Operator]):
|
||||
up_chars.append(
|
||||
UpChar(name=name, star=star, limited=False, zoom=zoom)
|
||||
)
|
||||
break # 这里break会导致个问题:如果一个公告里有两个池子,会漏掉下面的池子,比如 5.19 的定向寻访。但目前我也没啥好想法解决
|
||||
if start_time <= datetime.now() <= end_time: # 跳过过时的卡池
|
||||
break # 这里break会导致个问题:如果一个公告里有两个池子,会漏掉下面的池子,比如 5.19 的定向寻访。但目前我也没啥好想法解决
|
||||
chars: List[str] = [] # 查找下一个卡池之前清空 chars
|
||||
up_chars = [] # 查找下一个卡池之前清空 up_chars
|
||||
if title and start_time and end_time:
|
||||
if start_time <= datetime.now() <= end_time:
|
||||
self.UP_EVENT = UpEvent(
|
||||
|
||||
+38
-19
@@ -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
@@ -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()
|
||||
|
||||
@@ -30,7 +30,7 @@ class Map:
|
||||
ratio: float = 1,
|
||||
):
|
||||
"""
|
||||
参数:
|
||||
参数:
|
||||
:param resource_name: 资源名称
|
||||
:param center_point: 中心点
|
||||
:param deviation: 坐标误差
|
||||
@@ -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,15 +19,18 @@ 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)
|
||||
|
||||
@classmethod
|
||||
async def add_uid(cls, user_qq: int, uid: int):
|
||||
"""
|
||||
说明:
|
||||
说明:
|
||||
添加一个uid
|
||||
参数:
|
||||
参数:
|
||||
:param user_qq: 用户qq
|
||||
:param uid: 原神uid
|
||||
"""
|
||||
@@ -44,9 +47,9 @@ class Genshin(db.Model):
|
||||
@classmethod
|
||||
async def set_mys_id(cls, uid: int, mys_id: int) -> bool:
|
||||
"""
|
||||
说明:
|
||||
说明:
|
||||
设置米游社id
|
||||
参数:
|
||||
参数:
|
||||
:param uid: 原神uid
|
||||
:param mys_id: 米游社id
|
||||
"""
|
||||
@@ -60,9 +63,9 @@ class Genshin(db.Model):
|
||||
@classmethod
|
||||
async def set_bind_group(cls, uid: int, bind_group) -> bool:
|
||||
"""
|
||||
说明:
|
||||
说明:
|
||||
绑定group_id,除私聊外的提醒将在此群发送
|
||||
参数:
|
||||
参数:
|
||||
:param uid: uid
|
||||
:param bind_group: 群号
|
||||
"""
|
||||
@@ -76,9 +79,9 @@ class Genshin(db.Model):
|
||||
@classmethod
|
||||
async def get_bind_group(cls, uid: int) -> Optional[int]:
|
||||
"""
|
||||
说明:
|
||||
说明:
|
||||
获取用户绑定的群聊
|
||||
参数:
|
||||
参数:
|
||||
:param uid: uid
|
||||
"""
|
||||
user = await cls.query.where(cls.uid == uid).gino.first()
|
||||
@@ -89,9 +92,9 @@ class Genshin(db.Model):
|
||||
@classmethod
|
||||
async def set_cookie(cls, uid: int, cookie: str) -> bool:
|
||||
"""
|
||||
说明:
|
||||
说明:
|
||||
设置cookie
|
||||
参数:
|
||||
参数:
|
||||
:param uid: 原神uid
|
||||
:param cookie: 米游社id
|
||||
"""
|
||||
@@ -105,9 +108,9 @@ class Genshin(db.Model):
|
||||
@classmethod
|
||||
async def set_resin_remind(cls, uid: int, flag: bool) -> bool:
|
||||
"""
|
||||
说明:
|
||||
说明:
|
||||
设置体力提醒
|
||||
参数:
|
||||
参数:
|
||||
:param uid: 原神uid
|
||||
:param flag: 开关状态
|
||||
"""
|
||||
@@ -121,9 +124,9 @@ class Genshin(db.Model):
|
||||
@classmethod
|
||||
async def set_user_resin_recovery_time(cls, uid: int, date: datetime):
|
||||
"""
|
||||
说明:
|
||||
说明:
|
||||
设置体力完成时间
|
||||
参数:
|
||||
参数:
|
||||
:param uid: uid
|
||||
:param date: 提醒日期
|
||||
"""
|
||||
@@ -134,9 +137,9 @@ class Genshin(db.Model):
|
||||
@classmethod
|
||||
async def get_user_resin_recovery_time(cls, uid: int) -> Optional[datetime]:
|
||||
"""
|
||||
说明:
|
||||
说明:
|
||||
获取体力完成时间
|
||||
参数:
|
||||
参数:
|
||||
:param uid: uid
|
||||
"""
|
||||
u = await cls.query.where(cls.uid == uid).gino.first()
|
||||
@@ -147,7 +150,7 @@ class Genshin(db.Model):
|
||||
@classmethod
|
||||
async def get_all_resin_remind_user(cls) -> List["Genshin"]:
|
||||
"""
|
||||
说明:
|
||||
说明:
|
||||
获取所有开启体力提醒的用户
|
||||
"""
|
||||
return await cls.query.where(cls.resin_remind == True).gino.all()
|
||||
@@ -155,9 +158,9 @@ class Genshin(db.Model):
|
||||
@classmethod
|
||||
async def clear_resin_remind_time(cls, uid: int) -> bool:
|
||||
"""
|
||||
说明:
|
||||
说明:
|
||||
清空提醒日期
|
||||
参数:
|
||||
参数:
|
||||
:param uid: uid
|
||||
"""
|
||||
user = await cls.query.where(cls.uid == uid).gino.first()
|
||||
@@ -169,9 +172,9 @@ class Genshin(db.Model):
|
||||
@classmethod
|
||||
async def set_auto_sign(cls, uid: int, flag: bool) -> bool:
|
||||
"""
|
||||
说明:
|
||||
说明:
|
||||
设置米游社/原神自动签到
|
||||
参数:
|
||||
参数:
|
||||
:param uid: 原神uid
|
||||
:param flag: 开关状态
|
||||
"""
|
||||
@@ -185,7 +188,7 @@ class Genshin(db.Model):
|
||||
@classmethod
|
||||
async def get_all_auto_sign_user(cls) -> List["Genshin"]:
|
||||
"""
|
||||
说明:
|
||||
说明:
|
||||
获取所有开启自动签到的用户
|
||||
"""
|
||||
return await cls.query.where(cls.auto_sign == True).gino.all()
|
||||
@@ -193,7 +196,7 @@ class Genshin(db.Model):
|
||||
@classmethod
|
||||
async def get_all_sign_user(cls) -> List["Genshin"]:
|
||||
"""
|
||||
说明:
|
||||
说明:
|
||||
获取 原神 所有今日签到用户
|
||||
"""
|
||||
return await cls.query.where(cls.auto_sign_time != None).gino.all()
|
||||
@@ -201,9 +204,9 @@ class Genshin(db.Model):
|
||||
@classmethod
|
||||
async def clear_sign_time(cls, uid: int) -> bool:
|
||||
"""
|
||||
说明:
|
||||
说明:
|
||||
清空签到日期
|
||||
参数:
|
||||
参数:
|
||||
:param uid: uid
|
||||
"""
|
||||
user = await cls.query.where(cls.uid == uid).gino.first()
|
||||
@@ -215,9 +218,9 @@ class Genshin(db.Model):
|
||||
@classmethod
|
||||
async def random_sign_time(cls, uid: int) -> Optional[datetime]:
|
||||
"""
|
||||
说明:
|
||||
说明:
|
||||
随机签到时间
|
||||
说明:
|
||||
说明:
|
||||
:param uid: uid
|
||||
"""
|
||||
query = cls.query.where(cls.uid == uid).with_for_update()
|
||||
@@ -245,9 +248,9 @@ class Genshin(db.Model):
|
||||
@classmethod
|
||||
async def get_query_cookie(cls, uid: int) -> Optional[str]:
|
||||
"""
|
||||
说明:
|
||||
说明:
|
||||
获取查询角色信息cookie
|
||||
参数:
|
||||
参数:
|
||||
:param uid: 原神uid
|
||||
"""
|
||||
# 查找用户今日是否已经查找过,防止重复
|
||||
@@ -265,9 +268,9 @@ class Genshin(db.Model):
|
||||
@classmethod
|
||||
async def get_user_cookie(cls, uid: int, flag: bool = False) -> Optional[str]:
|
||||
"""
|
||||
说明:
|
||||
说明:
|
||||
获取用户cookie
|
||||
参数:
|
||||
参数:
|
||||
:param uid:原神uid
|
||||
:param flag:必须使用自己的cookie
|
||||
"""
|
||||
@@ -279,9 +282,9 @@ class Genshin(db.Model):
|
||||
@classmethod
|
||||
async def get_user_by_qq(cls, user_qq: int) -> Optional["Genshin"]:
|
||||
"""
|
||||
说明:
|
||||
说明:
|
||||
通过qq获取用户对象
|
||||
参数:
|
||||
参数:
|
||||
:param user_qq: qq
|
||||
"""
|
||||
return await cls.query.where(cls.user_qq == user_qq).gino.first()
|
||||
@@ -289,9 +292,9 @@ class Genshin(db.Model):
|
||||
@classmethod
|
||||
async def get_user_by_uid(cls, uid: int) -> Optional["Genshin"]:
|
||||
"""
|
||||
说明:
|
||||
说明:
|
||||
通过uid获取用户对象
|
||||
参数:
|
||||
参数:
|
||||
:param uid: qq
|
||||
"""
|
||||
return await cls.query.where(cls.uid == uid).gino.first()
|
||||
@@ -299,9 +302,9 @@ class Genshin(db.Model):
|
||||
@classmethod
|
||||
async def get_user_uid(cls, user_qq: int) -> Optional[int]:
|
||||
"""
|
||||
说明:
|
||||
说明:
|
||||
获取用户uid
|
||||
参数:
|
||||
参数:
|
||||
:param user_qq:用户qq
|
||||
"""
|
||||
return await cls._get_user_data(user_qq, None, "uid")
|
||||
@@ -311,7 +314,7 @@ class Genshin(db.Model):
|
||||
"""
|
||||
说嘛:
|
||||
获取用户米游社id
|
||||
参数:
|
||||
参数:
|
||||
:param uid:原神id
|
||||
"""
|
||||
return await cls._get_user_data(None, uid, "mys_id")
|
||||
@@ -319,9 +322,9 @@ class Genshin(db.Model):
|
||||
@classmethod
|
||||
async def delete_user_cookie(cls, uid: int):
|
||||
"""
|
||||
说明:
|
||||
说明:
|
||||
删除用户cookie
|
||||
参数:
|
||||
参数:
|
||||
:param uid: 原神uid
|
||||
"""
|
||||
query = cls.query.where(cls.uid == uid).with_for_update()
|
||||
@@ -332,9 +335,9 @@ class Genshin(db.Model):
|
||||
@classmethod
|
||||
async def delete_user(cls, user_qq: int):
|
||||
"""
|
||||
说明:
|
||||
说明:
|
||||
删除用户数据
|
||||
参数:
|
||||
参数:
|
||||
:param user_qq: 用户qq
|
||||
"""
|
||||
query = cls.query.where(cls.user_qq == user_qq).with_for_update()
|
||||
@@ -347,9 +350,9 @@ class Genshin(db.Model):
|
||||
@classmethod
|
||||
async def _add_query_uid(cls, uid: int, cookie_uid: int):
|
||||
"""
|
||||
说明:
|
||||
说明:
|
||||
添加每日查询重复uid的cookie
|
||||
参数:
|
||||
参数:
|
||||
:param uid: 原神uid
|
||||
:param cookie_uid: cookie的uid
|
||||
"""
|
||||
@@ -362,9 +365,9 @@ class Genshin(db.Model):
|
||||
cls, user_qq: Optional[int], uid: Optional[int], type_: str
|
||||
) -> Optional[Union[int, str]]:
|
||||
"""
|
||||
说明:
|
||||
说明:
|
||||
获取用户数据
|
||||
参数:
|
||||
参数:
|
||||
:param user_qq: 用户qq
|
||||
:param uid: uid
|
||||
:param type_: 数据类型
|
||||
@@ -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
|
||||
|
||||
@@ -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={}®ion={}&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={}®ion={}&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={}®ion={}&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")
|
||||
|
||||
|
||||
@@ -182,7 +182,7 @@ def parsed_data(
|
||||
role_data = {
|
||||
"active_day_number": data["stats"]["active_day_number"], # 活跃天数
|
||||
"achievement_number": data["stats"]["achievement_number"], # 达成成就数量
|
||||
"win_rate": data["stats"]["win_rate"],
|
||||
# "win_rate": data["stats"]["win_rate"],
|
||||
"anemoculus_number": data["stats"]["anemoculus_number"], # 风神瞳已收集
|
||||
"geoculus_number": data["stats"]["geoculus_number"], # 岩神瞳已收集
|
||||
"avatar_number": data["stats"]["avatar_number"], # 获得角色数量
|
||||
@@ -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)
|
||||
|
||||
@@ -86,30 +86,28 @@ async def _():
|
||||
if await Genshin.get_user_resin_recovery_time(u.uid) > datetime.now(
|
||||
pytz.timezone("Asia/Shanghai")
|
||||
):
|
||||
date = await Genshin.get_user_resin_recovery_time(u.uid)
|
||||
scheduler.add_job(
|
||||
_remind,
|
||||
"date",
|
||||
run_date=date.replace(microsecond=0),
|
||||
id=f"genshin_resin_remind_{u.uid}_{u.user_qq}",
|
||||
args=[u.user_qq, u.uid],
|
||||
)
|
||||
# date = await Genshin.get_user_resin_recovery_time(u.uid) # 不能要,因为可能在这期间用户使用了树脂
|
||||
add_job(u.user_qq, u.uid)
|
||||
# scheduler.add_job(
|
||||
# _remind,
|
||||
# "date",
|
||||
# run_date=date.replace(microsecond=0),
|
||||
# id=f"genshin_resin_remind_{u.uid}_{u.user_qq}",
|
||||
# args=[u.user_qq, u.uid],
|
||||
# )
|
||||
logger.info(
|
||||
f"genshin_resin_remind add_job:USER:{u.user_qq} UID:{u.uid} "
|
||||
f"{date} 原神树脂提醒"
|
||||
f"genshin_resin_remind add_job:USER:{u.user_qq} UID:{u.uid}启动原神树脂提醒 "
|
||||
)
|
||||
else:
|
||||
await Genshin.clear_resin_remind_time(u.uid)
|
||||
add_job(u.user_qq, u.uid)
|
||||
logger.info(
|
||||
f"genshin_resin_remind add_job CHECK:USER:{u.user_qq} UID:{u.uid} "
|
||||
f"{date} 原神树脂提醒"
|
||||
f"genshin_resin_remind add_job CHECK:USER:{u.user_qq} UID:{u.uid}启动原神树脂提醒 "
|
||||
)
|
||||
else:
|
||||
add_job(u.user_qq, u.uid)
|
||||
logger.info(
|
||||
f"genshin_resin_remind add_job CHECK:USER:{u.user_qq} UID:{u.uid} "
|
||||
f"{date} 原神树脂提醒"
|
||||
f"genshin_resin_remind add_job CHECK:USER:{u.user_qq} UID:{u.uid}启动原神树脂提醒 "
|
||||
)
|
||||
|
||||
|
||||
@@ -144,17 +142,19 @@ async def _remind(user_id: int, uid: str):
|
||||
now = datetime.now(pytz.timezone("Asia/Shanghai"))
|
||||
next_time = None
|
||||
if code == 200:
|
||||
current_resin = data["current_resin"] # 当前树脂
|
||||
max_resin = data["max_resin"] # 最大树脂
|
||||
current_resin = int(data["current_resin"]) # 当前树脂
|
||||
max_resin = int(data["max_resin"]) # 最大树脂
|
||||
msg = f"你的已经存了 {current_resin} 个树脂了!不要忘记刷掉!"
|
||||
# resin_recovery_time = data["resin_recovery_time"] # 树脂全部回复时间
|
||||
if current_resin < max_resin:
|
||||
user_manager.remove(uid)
|
||||
user_manager.remove_overflow(uid)
|
||||
if max_resin - 40 < current_resin <= max_resin - 20:
|
||||
next_time = now + timedelta(minutes=(max_resin - 20 - current_resin + 1) * 8, seconds=10)
|
||||
elif current_resin < max_resin:
|
||||
next_time = now + timedelta(minutes=(max_resin - current_resin) * 8, seconds=10)
|
||||
if current_resin < max_resin - 40:
|
||||
next_time = now + timedelta(minutes=(max_resin - 40 - current_resin) * 8)
|
||||
elif max_resin - 40 <= current_resin < max_resin - 20:
|
||||
next_time = now + timedelta(minutes=(max_resin - 20 - current_resin) * 8)
|
||||
elif max_resin - 20 <= current_resin < max_resin:
|
||||
next_time = now + timedelta(minutes=(max_resin - current_resin) * 8)
|
||||
elif current_resin == max_resin:
|
||||
custom_overflow_resin = Config.get_config("resin_remind", "CUSTOM_RESIN_OVERFLOW_REMIND")
|
||||
if user_manager.is_overflow(uid) and custom_overflow_resin:
|
||||
@@ -164,6 +164,7 @@ async def _remind(user_id: int, uid: str):
|
||||
msg = f"你的树脂都溢出 {custom_overflow_resin} 个了!浪费可耻!"
|
||||
else:
|
||||
next_time = now + timedelta(minutes=40 * 8 + random.randint(5, 50))
|
||||
|
||||
if not user_manager.exists(uid) and current_resin >= max_resin - 40:
|
||||
if current_resin == max_resin:
|
||||
user_manager.append(uid)
|
||||
@@ -187,6 +188,7 @@ async def _remind(user_id: int, uid: str):
|
||||
)
|
||||
except ActionFailed as e:
|
||||
logger.error(f"树脂提醒推送发生错误 {type(e)}:{e}")
|
||||
|
||||
if not next_time:
|
||||
if user_manager.check(uid) and Config.get_config("resin_remind", "AUTO_CLOSE_QUERY_FAIL_RESIN_REMIND"):
|
||||
await Genshin.set_resin_remind(int(uid), False)
|
||||
@@ -197,10 +199,13 @@ async def _remind(user_id: int, uid: str):
|
||||
user_manager.remove_error_count(uid)
|
||||
await Genshin.set_user_resin_recovery_time(int(uid), next_time)
|
||||
scheduler.add_job(
|
||||
_remind,
|
||||
_remind,
|
||||
"date",
|
||||
run_date=next_time,
|
||||
id=f"genshin_resin_remind_{uid}_{user_id}",
|
||||
args=[user_id, uid],
|
||||
)
|
||||
logger.info(
|
||||
f"genshin_resin_remind add_job:USER:{user_id} UID:{uid} "
|
||||
f"{next_time} 原神树脂提醒"
|
||||
)
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
from nonebot import on_command, on_notice
|
||||
from nonebot.adapters.onebot.v11 import (
|
||||
Bot,
|
||||
ActionFailed,
|
||||
GroupMessageEvent,
|
||||
PokeNotifyEvent,
|
||||
Message
|
||||
@@ -24,7 +25,6 @@ from nonebot.rule import to_me
|
||||
from datetime import datetime, timedelta
|
||||
from configs.config import NICKNAME
|
||||
from apscheduler.jobstores.base import JobLookupError
|
||||
from nonebot.adapters.onebot.v11.exception import ActionFailed
|
||||
from nonebot.params import CommandArg
|
||||
import random
|
||||
import time
|
||||
@@ -124,7 +124,7 @@ async def _(bot: Bot, event: GroupMessageEvent, arg: Message = CommandArg()):
|
||||
redbag_data[event.group_id]["amount"]
|
||||
- redbag_data[event.group_id]["open_amount"]
|
||||
)
|
||||
await return_gold(event.user_id, event.group_id, amount)
|
||||
await return_gold(redbag_data[event.group_id]["user_id"], event.group_id, amount)
|
||||
await gold_redbag.send(
|
||||
f'{redbag_data[event.group_id]["nickname"]}的红包过时未开完,退还{amount}金币...'
|
||||
)
|
||||
|
||||
@@ -61,7 +61,8 @@ async def generate_open_redbag_pic(user_id: int, send_user_nickname: str, amount
|
||||
async def _generate_open_redbag_pic(user_id: int, send_user_nickname: str, amount: int, text: str):
|
||||
send_user_nickname += '的红包'
|
||||
amount = str(amount)
|
||||
head = BuildImage(1000, 980, font_size=30, background=f'{IMAGE_PATH}/prts/redbag_12.png')
|
||||
random_redbag = random.choice(os.listdir(f"{IMAGE_PATH}/prts/redbag_1"))
|
||||
head = BuildImage(1000, 980, font_size=30, background=f'{IMAGE_PATH}/prts/redbag_1/{random_redbag}')
|
||||
size = BuildImage(0, 0, font_size=50).getsize(send_user_nickname)
|
||||
# QQ头像
|
||||
ava_bk = BuildImage(100 + size[0], 66, is_alpha=True, font_size=50)
|
||||
@@ -70,13 +71,13 @@ async def _generate_open_redbag_pic(user_id: int, send_user_nickname: str, amoun
|
||||
ava_bk.text((100, 7), send_user_nickname)
|
||||
# ava_bk.show()
|
||||
ava_bk_w, ava_bk_h = ava_bk.size
|
||||
head.paste(ava_bk, (int((1000 - ava_bk_w) / 2), 300))
|
||||
head.paste(ava_bk, (int((1000 - ava_bk_w) / 2), 300), alpha=True)
|
||||
# 金额
|
||||
size = BuildImage(0, 0, font_size=150).getsize(amount)
|
||||
price = BuildImage(size[0], size[1], is_alpha=True, font_size=150)
|
||||
price.text((0, 0), amount, fill=(209, 171, 108))
|
||||
# 金币中文
|
||||
head.paste(price, (int((1000 - size[0]) / 2) - 50, 460))
|
||||
head.paste(price, (int((1000 - size[0]) / 2) - 50, 460), alpha=True)
|
||||
head.text((int((1000 - size[0]) / 2 + size[0]) - 50, 500 + size[1] - 70), '金币', fill=(209, 171, 108))
|
||||
# 剩余数量和金额
|
||||
head.text((350, 900), text, (198, 198, 198))
|
||||
|
||||
@@ -18,9 +18,9 @@ class RedbagUser(db.Model):
|
||||
@classmethod
|
||||
async def add_redbag_data(cls, user_qq: int, group_id: int, itype: str, money: int):
|
||||
"""
|
||||
说明:
|
||||
说明:
|
||||
添加收发红包数据
|
||||
参数:
|
||||
参数:
|
||||
:param user_qq: qq号
|
||||
:param group_id: 群号
|
||||
:param itype: 收或发
|
||||
@@ -45,9 +45,9 @@ class RedbagUser(db.Model):
|
||||
@classmethod
|
||||
async def ensure(cls, user_qq: int, group_id: int) -> bool:
|
||||
"""
|
||||
说明:
|
||||
说明:
|
||||
获取用户对象
|
||||
参数:
|
||||
参数:
|
||||
:param user_qq: qq号
|
||||
:param group_id: 群号
|
||||
"""
|
||||
@@ -61,9 +61,9 @@ class RedbagUser(db.Model):
|
||||
@classmethod
|
||||
async def get_user_all(cls, group_id: int = None) -> List["RedbagUser"]:
|
||||
"""
|
||||
说明:
|
||||
说明:
|
||||
获取所有用户对象
|
||||
参数:
|
||||
参数:
|
||||
:param group_id: 群号
|
||||
"""
|
||||
if not group_id:
|
||||
|
||||
@@ -65,8 +65,8 @@ async def arg_handle(
|
||||
if int(img_id) > max_id or int(img_id) < 0:
|
||||
await delete_img.finish(f"Id超过上下限,上限:{max_id}", at_sender=True)
|
||||
try:
|
||||
if (TEMP_PATH / "delete.jpg").exists():
|
||||
(TEMP_PATH / "delete.jpg").unlink()
|
||||
if (TEMP_PATH / f"{event.user_id}_delete.jpg").exists():
|
||||
(TEMP_PATH / f"{event.user_id}_delete.jpg").unlink()
|
||||
logger.info(f"删除{cn2py(state['path'])}图片 {img_id}.jpg 成功")
|
||||
except Exception as e:
|
||||
logger.warning(f"删除图片 delete.jpg 失败 e{e}")
|
||||
|
||||
@@ -36,7 +36,7 @@ __plugin_settings__ = {
|
||||
"limit_superuser": False,
|
||||
"cmd": ["发送图片"] + Config.get_config("image_management", "IMAGE_DIR_LIST"),
|
||||
}
|
||||
__plugin_resources__ = {"pa": IMAGE_PATH}
|
||||
__plugin_resources__ = {"pa": IMAGE_PATH / "pa"}
|
||||
|
||||
Config.add_plugin_config(
|
||||
"_task",
|
||||
|
||||
+20
-15
@@ -1,8 +1,11 @@
|
||||
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
|
||||
from nonebot.adapters.onebot.v11 import Bot, GroupMessageEvent, Message, ActionFailed
|
||||
from nonebot.adapters.onebot.v11.permission import GROUP
|
||||
from utils.utils import is_number, get_message_img, get_message_text
|
||||
from nonebot.adapters.onebot.v11.exception import ActionFailed
|
||||
from configs.path_config import DATA_PATH, TEMP_PATH
|
||||
from utils.image_utils import get_img_hash
|
||||
from services.log import logger
|
||||
@@ -71,12 +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 = {}
|
||||
@@ -90,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] = {
|
||||
@@ -133,11 +132,19 @@ async def _(bot: Bot, event: GroupMessageEvent):
|
||||
|
||||
|
||||
@mute_setting.handle()
|
||||
async def _(event: GroupMessageEvent, cmd: Tuple[str, ...] = Command(), arg: Message = CommandArg()):
|
||||
async def _(
|
||||
event: GroupMessageEvent,
|
||||
cmd: Tuple[str, ...] = Command(),
|
||||
arg: Message = CommandArg(),
|
||||
):
|
||||
global mute_data
|
||||
group_id = str(event.group_id)
|
||||
if not mute_data.get(group_id):
|
||||
mute_data[group_id] = {"count": Config.get_config("mute", "MUTE_DEFAULT_COUNT"), "time": Config.get_config("mute", "MUTE_DEFAULT_TIME"), "duration": Config.get_config("mute", "MUTE_DEFAULT_DURATION")}
|
||||
mute_data[group_id] = {
|
||||
"count": Config.get_config("mute", "MUTE_DEFAULT_COUNT"),
|
||||
"time": Config.get_config("mute", "MUTE_DEFAULT_TIME"),
|
||||
"duration": Config.get_config("mute", "MUTE_DEFAULT_DURATION"),
|
||||
}
|
||||
msg = arg.extract_plain_text().strip()
|
||||
if cmd[0] == "刷屏检测设置":
|
||||
await mute_setting.finish(
|
||||
@@ -157,8 +164,6 @@ async def _(event: GroupMessageEvent, cmd: Tuple[str, ...] = Command(), arg: Mes
|
||||
if cmd[0] == "设置刷屏禁言时长":
|
||||
mute_data[group_id]["duration"] = int(msg)
|
||||
msg += " 分钟"
|
||||
await mute_setting.send(f'刷屏检测:{cmd[0]}为 {msg}')
|
||||
logger.info(
|
||||
f'USER {event.user_id} GROUP {group_id} {cmd[0]}:{msg}'
|
||||
)
|
||||
await mute_setting.send(f"刷屏检测:{cmd[0]}为 {msg}")
|
||||
logger.info(f"USER {event.user_id} GROUP {group_id} {cmd[0]}:{msg}")
|
||||
save_data()
|
||||
|
||||
@@ -27,7 +27,7 @@ __plugin_settings__ = {
|
||||
}
|
||||
|
||||
one_friend = on_regex(
|
||||
"^我.*?朋友.*?[想问问|说|让我问问|想问|让我问|想知道|让我帮他问问|让我帮他问|让我帮忙问|让我帮忙问问|问](.*)",
|
||||
"^我.{0,4}朋友.{0,2}(?:想问问|说|让我问问|想问|让我问|想知道|让我帮他问问|让我帮他问|让我帮忙问|让我帮忙问问|问)(.{0,30})$",
|
||||
priority=4,
|
||||
block=True,
|
||||
)
|
||||
|
||||
@@ -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()):
|
||||
|
||||
@@ -31,7 +31,7 @@ async def open_case(user_qq: int, group: int, case_name: str = "狂牙大行动"
|
||||
async with db.transaction():
|
||||
user = await OpenCasesUser.ensure(user_qq, group, for_update=True)
|
||||
# 一天次数上限
|
||||
if user.today_open_total == int(
|
||||
if user.today_open_total > int(
|
||||
Config.get_config("open_cases", "INITIAL_OPEN_CASE_COUNT")
|
||||
+ int(impression)
|
||||
/ Config.get_config("open_cases", "EACH_IMPRESSION_ADD_COUNT")
|
||||
|
||||
@@ -7,7 +7,7 @@ from .models.open_cases_user import OpenCasesUser
|
||||
from services.log import logger
|
||||
from utils.utils import get_bot, cn2py
|
||||
from asyncio.exceptions import TimeoutError
|
||||
from nonebot.adapters.onebot.v11.exception import ActionFailed
|
||||
from nonebot.adapters.onebot.v11 import ActionFailed
|
||||
from configs.config import Config
|
||||
from utils.manager import group_manager
|
||||
from .config import *
|
||||
|
||||
+163
-166
@@ -1,166 +1,163 @@
|
||||
# from nonebot import on_message
|
||||
# from services.log import logger
|
||||
# from nonebot.adapters.onebot.v11 import GroupMessageEvent
|
||||
# from utils.utils import get_message_json, get_local_proxy, is_number, get_message_text
|
||||
# from nonebot.adapters.onebot.v11.permission import GROUP
|
||||
# from utils.message_builder import image
|
||||
# from nonebot.adapters.onebot.v11.exception import ActionFailed
|
||||
# from utils.image_utils import BuildImage
|
||||
# from utils.browser import get_browser
|
||||
# from configs.path_config import IMAGE_PATH
|
||||
# from utils.http_utils import AsyncHttpx
|
||||
# from configs.config import Config
|
||||
# from utils.user_agent import get_user_agent
|
||||
# import aiohttp
|
||||
# import asyncio
|
||||
# import time
|
||||
# import ujson as json
|
||||
#
|
||||
#
|
||||
# __zx_plugin_name__ = "B站转发解析"
|
||||
# __plugin_usage__ = """
|
||||
# usage:
|
||||
# B站转发解析,解析b站分享信息,支持bv,bilibili链接,b站手机端转发卡片,cv,b23.tv,且5分钟内不解析相同url
|
||||
# """.strip()
|
||||
# __plugin_des__ = "B站转发解析"
|
||||
# __plugin_type__ = ("其他",)
|
||||
# __plugin_version__ = 0.1
|
||||
# __plugin_author__ = "HibiKier"
|
||||
# __plugin_task__ = {"bilibili_parse": "b站转发解析"}
|
||||
# Config.add_plugin_config(
|
||||
# "_task",
|
||||
# "DEFAULT_BILIBILI_PARSE",
|
||||
# True,
|
||||
# help_="被动 B站转发解析 进群默认开关状态",
|
||||
# default_value=True,
|
||||
# )
|
||||
#
|
||||
#
|
||||
# parse_bilibili_json = on_message(priority=1, permission=GROUP, block=False)
|
||||
#
|
||||
# _tmp = {}
|
||||
#
|
||||
#
|
||||
# @parse_bilibili_json.handle()
|
||||
# async def _(event: GroupMessageEvent):
|
||||
# vd_info = None
|
||||
# url = None
|
||||
# if get_message_json(event.json()):
|
||||
# try:
|
||||
# data = json.loads(get_message_json(event.json())[0]["data"])
|
||||
# except (IndexError, KeyError):
|
||||
# data = None
|
||||
# if data:
|
||||
# # 转发视频
|
||||
# if data.get("desc") == "哔哩哔哩":
|
||||
# async with aiohttp.ClientSession(
|
||||
# headers=get_user_agent()
|
||||
# ) as session:
|
||||
# async with session.get(
|
||||
# data["meta"]["detail_1"]["qqdocurl"],
|
||||
# proxy=get_local_proxy(),
|
||||
# timeout=7,
|
||||
# ) as response:
|
||||
# url = str(response.url).split("?")[0]
|
||||
# bvid = url.split("/")[-1]
|
||||
# vd_info = await video.Video(bvid=bvid).get_info()
|
||||
# # response = await AsyncHttpx.get(
|
||||
# # data["meta"]["detail_1"]["qqdocurl"], timeout=7
|
||||
# # )
|
||||
# # url = str(response.url).split("?")[0]
|
||||
# # bvid = url.split("/")[-1]
|
||||
# # vd_info = await video.Video(bvid=bvid).get_info()
|
||||
# # 转发专栏
|
||||
# if (
|
||||
# data.get("meta")
|
||||
# and data["meta"].get("news")
|
||||
# and data["meta"]["news"].get("desc") == "哔哩哔哩专栏"
|
||||
# ):
|
||||
# url = data["meta"]["news"]["jumpUrl"]
|
||||
# page = None
|
||||
# try:
|
||||
# browser = await get_browser()
|
||||
# if not browser:
|
||||
# return
|
||||
# page = await browser.new_page(
|
||||
# user_agent="Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36"
|
||||
# " (KHTML, like Gecko) Chrome/93.0.4530.0 Safari/537.36"
|
||||
# )
|
||||
# await page.goto(url, wait_until="networkidle", timeout=10000)
|
||||
# await page.set_viewport_size({"width": 2560, "height": 1080})
|
||||
# await page.click("#app > div")
|
||||
# div = await page.query_selector("#app > div")
|
||||
# await div.screenshot(
|
||||
# path=f"{IMAGE_PATH}/temp/cv_{event.user_id}.png",
|
||||
# timeout=100000,
|
||||
# )
|
||||
# await asyncio.get_event_loop().run_in_executor(
|
||||
# None, resize, f"{IMAGE_PATH}/temp/cv_{event.user_id}.png"
|
||||
# )
|
||||
# await parse_bilibili_json.send(
|
||||
# "[[_task|bilibili_parse]]" + image(f"cv_{event.user_id}.png", "temp")
|
||||
# )
|
||||
# await page.close()
|
||||
# logger.info(
|
||||
# f"USER {event.user_id} GROUP {event.group_id} 解析bilibili转发 {url}"
|
||||
# )
|
||||
# except Exception as e:
|
||||
# logger.error(f"尝试解析bilibili专栏 {url} 失败 {type(e)}:{e}")
|
||||
# if page:
|
||||
# await page.close()
|
||||
# return
|
||||
# # BV
|
||||
# if msg := get_message_text(event.json()):
|
||||
# if "BV" in msg:
|
||||
# index = msg.find("BV")
|
||||
# if len(msg[index + 2 :]) >= 10:
|
||||
# msg = msg[index : index + 12]
|
||||
# url = f"https://www.bilibili.com/video/{msg}"
|
||||
# vd_info = await video.Video(bvid=msg).get_info()
|
||||
# elif "av" in msg:
|
||||
# index = msg.find("av")
|
||||
# if len(msg[index + 2 :]) >= 9:
|
||||
# msg = msg[index + 2 : index + 11]
|
||||
# if is_number(msg):
|
||||
# url = f"https://www.bilibili.com/video/{msg}"
|
||||
# vd_info = await video.Video(aid=int(msg)).get_info()
|
||||
# elif "https://b23.tv" in msg:
|
||||
# url = "https://" + msg[msg.find("b23.tv") : msg.find("b23.tv") + 13]
|
||||
# res = await AsyncHttpx.get(url, timeout=7)
|
||||
# url = str(res.url).split("?")[0]
|
||||
# bvid = url.split("/")[-1]
|
||||
# vd_info = await video.Video(bvid=bvid).get_info()
|
||||
# if vd_info:
|
||||
# if (
|
||||
# url in _tmp.keys() and time.time() - _tmp[url] > 30
|
||||
# ) or url not in _tmp.keys():
|
||||
# _tmp[url] = time.time()
|
||||
# aid = vd_info["aid"]
|
||||
# title = vd_info["title"]
|
||||
# author = vd_info["owner"]["name"]
|
||||
# reply = vd_info["stat"]["reply"] # 回复
|
||||
# favorite = vd_info["stat"]["favorite"] # 收藏
|
||||
# coin = vd_info["stat"]["coin"] # 投币
|
||||
# # like = vd_info['stat']['like'] # 点赞
|
||||
# # danmu = vd_info['stat']['danmaku'] # 弹幕
|
||||
# date = time.strftime("%Y-%m-%d", time.localtime(vd_info["ctime"]))
|
||||
# try:
|
||||
# await parse_bilibili_json.send(
|
||||
# "[[_task|bilibili_parse]]" +
|
||||
# image(vd_info["pic"]) + f"\nav{aid}\n标题:{title}\n"
|
||||
# f"UP:{author}\n"
|
||||
# f"上传日期:{date}\n"
|
||||
# f"回复:{reply},收藏:{favorite},投币:{coin}\n"
|
||||
# f"{url}"
|
||||
# )
|
||||
# except ActionFailed:
|
||||
# logger.warning(f"{event.group_id} 发送bilibili解析失败")
|
||||
# else:
|
||||
# logger.info(
|
||||
# f"USER {event.user_id} GROUP {event.group_id} 解析bilibili转发 {url}"
|
||||
# )
|
||||
#
|
||||
#
|
||||
# def resize(path: str):
|
||||
# A = BuildImage(0, 0, background=path, ratio=0.5)
|
||||
# A.save(path)
|
||||
from nonebot import on_message
|
||||
from services.log import logger
|
||||
from nonebot.adapters.onebot.v11 import GroupMessageEvent, ActionFailed
|
||||
from utils.utils import get_message_json, get_local_proxy, is_number, get_message_text
|
||||
from nonebot.adapters.onebot.v11.permission import GROUP
|
||||
from utils.message_builder import image
|
||||
from utils.image_utils import BuildImage
|
||||
from utils.browser import get_browser
|
||||
from configs.path_config import IMAGE_PATH
|
||||
from utils.http_utils import AsyncHttpx
|
||||
from configs.config import Config
|
||||
from utils.user_agent import get_user_agent
|
||||
import aiohttp
|
||||
import asyncio
|
||||
import time
|
||||
import ujson as json
|
||||
from bilireq import video
|
||||
__zx_plugin_name__ = "B站转发解析"
|
||||
__plugin_usage__ = """
|
||||
usage:
|
||||
B站转发解析,解析b站分享信息,支持bv,bilibili链接,b站手机端转发卡片,cv,b23.tv,且5分钟内不解析相同url
|
||||
""".strip()
|
||||
__plugin_des__ = "B站转发解析"
|
||||
__plugin_type__ = ("其他",)
|
||||
__plugin_version__ = 0.1
|
||||
__plugin_author__ = "HibiKier"
|
||||
__plugin_task__ = {"bilibili_parse": "b站转发解析"}
|
||||
Config.add_plugin_config(
|
||||
"_task",
|
||||
"DEFAULT_BILIBILI_PARSE",
|
||||
True,
|
||||
help_="被动 B站转发解析 进群默认开关状态",
|
||||
default_value=True,
|
||||
)
|
||||
|
||||
|
||||
parse_bilibili_json = on_message(priority=1, permission=GROUP, block=False)
|
||||
|
||||
_tmp = {}
|
||||
|
||||
|
||||
@parse_bilibili_json.handle()
|
||||
async def _(event: GroupMessageEvent):
|
||||
vd_info = None
|
||||
url = None
|
||||
if get_message_json(event.json()):
|
||||
try:
|
||||
data = json.loads(get_message_json(event.json())[0]["data"])
|
||||
except (IndexError, KeyError):
|
||||
data = None
|
||||
if data:
|
||||
# 转发视频
|
||||
if data.get("desc") == "哔哩哔哩":
|
||||
async with aiohttp.ClientSession(
|
||||
headers=get_user_agent()
|
||||
) as session:
|
||||
async with session.get(
|
||||
data["meta"]["detail_1"]["qqdocurl"],
|
||||
timeout=7,
|
||||
) as response:
|
||||
url = str(response.url).split("?")[0]
|
||||
bvid = url.split("/")[-1]
|
||||
vd_info = await video.get_video_base_info(bvid)
|
||||
# 转发专栏
|
||||
if (
|
||||
data.get("meta")
|
||||
and data["meta"].get("news")
|
||||
and data["meta"]["news"].get("desc") == "哔哩哔哩专栏"
|
||||
):
|
||||
url = data["meta"]["news"]["jumpUrl"]
|
||||
page = None
|
||||
try:
|
||||
browser = await get_browser()
|
||||
if not browser:
|
||||
return
|
||||
page = await browser.new_page(
|
||||
user_agent="Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36"
|
||||
" (KHTML, like Gecko) Chrome/93.0.4530.0 Safari/537.36"
|
||||
)
|
||||
await page.goto(url, wait_until="networkidle", timeout=10000)
|
||||
await page.set_viewport_size({"width": 2560, "height": 1080})
|
||||
await page.click("#app > div")
|
||||
div = await page.query_selector("#app > div")
|
||||
await div.screenshot(
|
||||
path=f"{IMAGE_PATH}/temp/cv_{event.user_id}.png",
|
||||
timeout=100000,
|
||||
)
|
||||
await asyncio.get_event_loop().run_in_executor(
|
||||
None, resize, f"{IMAGE_PATH}/temp/cv_{event.user_id}.png"
|
||||
)
|
||||
await parse_bilibili_json.send(
|
||||
"[[_task|bilibili_parse]]" + image(f"cv_{event.user_id}.png", "temp")
|
||||
)
|
||||
await page.close()
|
||||
logger.info(
|
||||
f"USER {event.user_id} GROUP {event.group_id} 解析bilibili转发 {url}"
|
||||
)
|
||||
except Exception as e:
|
||||
logger.error(f"尝试解析bilibili专栏 {url} 失败 {type(e)}:{e}")
|
||||
if page:
|
||||
await page.close()
|
||||
return
|
||||
# BV
|
||||
if msg := get_message_text(event.json()):
|
||||
if "BV" in msg:
|
||||
index = msg.find("BV")
|
||||
if len(msg[index + 2 :]) >= 10:
|
||||
msg = msg[index : index + 12]
|
||||
url = f"https://www.bilibili.com/video/{msg}"
|
||||
vd_info = await video.get_video_base_info(msg)
|
||||
elif "av" in msg:
|
||||
index = msg.find("av")
|
||||
if len(msg[index + 2 :]) >= 1:
|
||||
msg = msg[index + 2 : index + 11]
|
||||
if is_number(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(
|
||||
headers=get_user_agent()
|
||||
) as session:
|
||||
async with session.get(
|
||||
url,
|
||||
timeout=7,
|
||||
) as response:
|
||||
url = str(response.url).split("?")[0]
|
||||
bvid = url.split("/")[-1]
|
||||
vd_info = await video.get_video_base_info(bvid)
|
||||
if vd_info:
|
||||
if (
|
||||
url in _tmp.keys() and time.time() - _tmp[url] > 30
|
||||
) or url not in _tmp.keys():
|
||||
_tmp[url] = time.time()
|
||||
aid = vd_info["aid"]
|
||||
title = vd_info["title"]
|
||||
author = vd_info["owner"]["name"]
|
||||
reply = vd_info["stat"]["reply"] # 回复
|
||||
favorite = vd_info["stat"]["favorite"] # 收藏
|
||||
coin = vd_info["stat"]["coin"] # 投币
|
||||
# like = vd_info['stat']['like'] # 点赞
|
||||
# danmu = vd_info['stat']['danmaku'] # 弹幕
|
||||
date = time.strftime("%Y-%m-%d", time.localtime(vd_info["ctime"]))
|
||||
try:
|
||||
await parse_bilibili_json.send(
|
||||
"[[_task|bilibili_parse]]" +
|
||||
image(vd_info["pic"]) + f"\nav{aid}\n标题:{title}\n"
|
||||
f"UP:{author}\n"
|
||||
f"上传日期:{date}\n"
|
||||
f"回复:{reply},收藏:{favorite},投币:{coin}\n"
|
||||
f"{url}"
|
||||
)
|
||||
except ActionFailed:
|
||||
logger.warning(f"{event.group_id} 发送bilibili解析失败")
|
||||
else:
|
||||
logger.info(
|
||||
f"USER {event.user_id} GROUP {event.group_id} 解析bilibili转发 {url}"
|
||||
)
|
||||
|
||||
|
||||
def resize(path: str):
|
||||
A = BuildImage(0, 0, background=path, ratio=0.5)
|
||||
A.save(path)
|
||||
|
||||
@@ -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")
|
||||
|
||||
|
||||
|
||||
@@ -4,7 +4,7 @@ from asyncio.locks import Semaphore
|
||||
from asyncio.exceptions import TimeoutError
|
||||
from ._model.pixiv import Pixiv
|
||||
from typing import List, Optional
|
||||
from utils.utils import change_pixiv_image_links
|
||||
from utils.utils import change_pixiv_image_links, change_img_md5
|
||||
from utils.image_utils import BuildImage
|
||||
from utils.http_utils import AsyncHttpx
|
||||
from services.log import logger
|
||||
@@ -238,6 +238,7 @@ async def get_image(img_url: str, user_id: int) -> Optional[str]:
|
||||
TEMP_PATH / f"pix_{user_id}_{img_url.split('/')[-1][:-4]}.jpg", "wb"
|
||||
) as f:
|
||||
await f.write(response.content)
|
||||
change_img_md5(TEMP_PATH / f"pix_{user_id}_{img_url.split('/')[-1][:-4]}.jpg")
|
||||
return TEMP_PATH / f"pix_{user_id}_{img_url.split('/')[-1][:-4]}.jpg"
|
||||
except TimeoutError:
|
||||
logger.warning(f"PIX:{img_url} 图片下载超时...")
|
||||
|
||||
@@ -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,15 +30,14 @@ class OmegaPixivIllusts(db.Model):
|
||||
url: str,
|
||||
uid: int,
|
||||
uname: str,
|
||||
classified: int,
|
||||
nsfw_tag: int,
|
||||
tags: str,
|
||||
created_at: datetime,
|
||||
updated_at: datetime,
|
||||
):
|
||||
"""
|
||||
说明:
|
||||
说明:
|
||||
添加图片信息
|
||||
参数:
|
||||
参数:
|
||||
:param pid: pid
|
||||
:param title: 标题
|
||||
:param width: 宽度
|
||||
@@ -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,
|
||||
)
|
||||
@@ -78,9 +75,9 @@ class OmegaPixivIllusts(db.Model):
|
||||
num: int = 100
|
||||
) -> List[Optional["OmegaPixivIllusts"]]:
|
||||
"""
|
||||
说明:
|
||||
说明:
|
||||
查找符合条件的图片
|
||||
参数:
|
||||
参数:
|
||||
:param keywords: 关键词
|
||||
:param uid: 画师uid
|
||||
:param pid: 图片pid
|
||||
@@ -104,20 +101,20 @@ class OmegaPixivIllusts(db.Model):
|
||||
@classmethod
|
||||
async def check_exists(cls, pid: int) -> bool:
|
||||
"""
|
||||
说明:
|
||||
说明:
|
||||
检测pid是否已存在
|
||||
参数:
|
||||
参数:
|
||||
:param pid: 图片PID
|
||||
"""
|
||||
query = await cls.query.where(cls.pid == pid).gino.all()
|
||||
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)在图库中的数量
|
||||
参数:
|
||||
参数:
|
||||
:param tags: 关键词/Tag
|
||||
"""
|
||||
setattr(OmegaPixivIllusts, 'count', db.func.count(cls.pid).label('count'))
|
||||
@@ -133,7 +130,7 @@ class OmegaPixivIllusts(db.Model):
|
||||
@classmethod
|
||||
async def get_all_pid(cls) -> List[int]:
|
||||
"""
|
||||
说明:
|
||||
说明:
|
||||
获取所有图片PID
|
||||
"""
|
||||
data = await cls.select('pid').gino.all()
|
||||
|
||||
@@ -38,9 +38,9 @@ class Pixiv(db.Model):
|
||||
tags: str,
|
||||
):
|
||||
"""
|
||||
说明:
|
||||
说明:
|
||||
添加图片信息
|
||||
参数:
|
||||
参数:
|
||||
:param pid: pid
|
||||
:param title: 标题
|
||||
:param width: 宽度
|
||||
@@ -74,9 +74,9 @@ class Pixiv(db.Model):
|
||||
@classmethod
|
||||
async def remove_image_data(cls, pid: int, img_p: str) -> bool:
|
||||
"""
|
||||
说明:
|
||||
说明:
|
||||
删除图片数据
|
||||
参数:
|
||||
参数:
|
||||
:param pid: 图片pid
|
||||
:param img_p: 图片pid的张数,如:p0,p1
|
||||
"""
|
||||
@@ -94,7 +94,7 @@ class Pixiv(db.Model):
|
||||
@classmethod
|
||||
async def get_all_pid(cls) -> List[int]:
|
||||
"""
|
||||
说明:
|
||||
说明:
|
||||
获取所有PID
|
||||
"""
|
||||
query = await cls.query.select("pid").gino.first()
|
||||
@@ -112,9 +112,9 @@ class Pixiv(db.Model):
|
||||
num: int = 100
|
||||
) -> List[Optional["Pixiv"]]:
|
||||
"""
|
||||
说明:
|
||||
说明:
|
||||
查找符合条件的图片
|
||||
参数:
|
||||
参数:
|
||||
:param keywords: 关键词
|
||||
:param uid: 画师uid
|
||||
:param pid: 图片pid
|
||||
@@ -140,9 +140,9 @@ class Pixiv(db.Model):
|
||||
@classmethod
|
||||
async def check_exists(cls, pid: int, img_p: str) -> bool:
|
||||
"""
|
||||
说明:
|
||||
说明:
|
||||
检测pid是否已存在
|
||||
参数:
|
||||
参数:
|
||||
:param pid: 图片PID
|
||||
:param img_p: 张数
|
||||
"""
|
||||
@@ -154,9 +154,9 @@ class Pixiv(db.Model):
|
||||
@classmethod
|
||||
async def get_keyword_num(cls, tags: List[str] = None) -> "int, int":
|
||||
"""
|
||||
说明:
|
||||
说明:
|
||||
获取相关关键词(keyword, tag)在图库中的数量
|
||||
参数:
|
||||
参数:
|
||||
:param tags: 关键词/Tag
|
||||
"""
|
||||
setattr(Pixiv, 'count', db.func.count(cls.pid).label('count'))
|
||||
|
||||
@@ -19,9 +19,9 @@ class PixivKeywordUser(db.Model):
|
||||
cls, user_qq: int, group_id: int, keyword: str, superusers: Set[str]
|
||||
) -> bool:
|
||||
"""
|
||||
说明:
|
||||
说明:
|
||||
添加搜图的关键词
|
||||
参数:
|
||||
参数:
|
||||
:param user_qq: qq号
|
||||
:param group_id: 群号
|
||||
:param keyword: 关键词
|
||||
@@ -38,9 +38,9 @@ class PixivKeywordUser(db.Model):
|
||||
@classmethod
|
||||
async def delete_keyword(cls, keyword: str) -> bool:
|
||||
"""
|
||||
说明:
|
||||
说明:
|
||||
删除关键词
|
||||
参数:
|
||||
参数:
|
||||
:param keyword: 关键词
|
||||
"""
|
||||
if await cls._check_keyword_exists(keyword):
|
||||
@@ -53,9 +53,9 @@ class PixivKeywordUser(db.Model):
|
||||
@classmethod
|
||||
async def set_keyword_pass(cls, keyword: str, is_pass: bool) -> "int, int":
|
||||
"""
|
||||
说明:
|
||||
说明:
|
||||
通过或禁用关键词
|
||||
参数:
|
||||
参数:
|
||||
:param keyword: 关键词
|
||||
:param is_pass: 通过状态
|
||||
"""
|
||||
@@ -71,7 +71,7 @@ class PixivKeywordUser(db.Model):
|
||||
@classmethod
|
||||
async def get_all_user_dict(cls) -> dict:
|
||||
"""
|
||||
说明:
|
||||
说明:
|
||||
获取关键词数据库各个用户贡献的关键词字典
|
||||
"""
|
||||
tmp = {}
|
||||
@@ -85,7 +85,7 @@ class PixivKeywordUser(db.Model):
|
||||
@classmethod
|
||||
async def get_current_keyword(cls) -> "List[str], List[str]":
|
||||
"""
|
||||
说明:
|
||||
说明:
|
||||
获取当前通过与未通过的关键词
|
||||
"""
|
||||
pass_keyword = []
|
||||
@@ -101,7 +101,7 @@ class PixivKeywordUser(db.Model):
|
||||
@classmethod
|
||||
async def get_black_pid(cls) -> List[str]:
|
||||
"""
|
||||
说明:
|
||||
说明:
|
||||
获取黑名单PID
|
||||
"""
|
||||
black_pid = []
|
||||
@@ -113,9 +113,9 @@ class PixivKeywordUser(db.Model):
|
||||
@classmethod
|
||||
async def _check_keyword_exists(cls, keyword: str) -> bool:
|
||||
"""
|
||||
说明:
|
||||
说明:
|
||||
检测关键词是否已存在
|
||||
参数:
|
||||
参数:
|
||||
:param keyword: 关键词
|
||||
"""
|
||||
current_keyword = []
|
||||
|
||||
@@ -20,6 +20,11 @@ usage:
|
||||
pix ?*[tags]: 通过 tag 获取相似图片,不含tag时随机抽取
|
||||
pid [uid]: 通过uid获取图片
|
||||
pix pid[pid]: 查看图库中指定pid图片
|
||||
示例:pix 萝莉 白丝
|
||||
示例:pix 萝莉 白丝 10 (10为数量)
|
||||
示例:pix #02 (当tag只有1个tag且为数字时,使用#标记,否则将被判定为数量)
|
||||
示例:pix 34582394 (查询指定uid图片)
|
||||
示例:pix pid:12323423 (查询指定pid图片)
|
||||
""".strip()
|
||||
__plugin_superuser_usage__ = """
|
||||
usage:
|
||||
@@ -88,9 +93,12 @@ async def _(bot: Bot, event: MessageEvent, arg: Message = CommandArg()):
|
||||
nsfw_tag == 2 and not Config.get_config("pix", "ALLOW_GROUP_R18")
|
||||
):
|
||||
await pix.finish("你不能看这些噢,这些都是是留给管理员看的...")
|
||||
if n := len(x) == 1 and is_number(x[0]) and int(x[0]) < 100:
|
||||
num = int(x[0])
|
||||
keyword = ""
|
||||
if (n := len(x)) == 1:
|
||||
if is_number(x[0]) and int(x[0]) < 100:
|
||||
num = int(x[0])
|
||||
keyword = ""
|
||||
elif x[0].startswith("#"):
|
||||
keyword = x[0][1:]
|
||||
elif n > 1:
|
||||
if is_number(x[-1]):
|
||||
num = int(x[-1])
|
||||
@@ -124,6 +132,7 @@ async def _(bot: Bot, event: MessageEvent, arg: Message = CommandArg()):
|
||||
all_image = await OmegaPixivIllusts.query_images(
|
||||
pid=int(pid), nsfw_tag=nsfw_tag
|
||||
)
|
||||
num = len(all_image)
|
||||
else:
|
||||
tmp = await Pixiv.query_images(
|
||||
x, r18=1 if nsfw_tag == 2 else 0, num=pix_num
|
||||
@@ -140,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):
|
||||
|
||||
@@ -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} 张"
|
||||
@@ -184,7 +183,7 @@ async def _():
|
||||
logger.info(f"添加OmegaPixivIllusts图库数据已存在 ---> pid:{pid}")
|
||||
omega_pixiv_illusts = None
|
||||
for file in os.listdir("."):
|
||||
if "omega_pixiv_illusts" in file and ".sql" in file:
|
||||
if "omega_pixiv_artwork" in file and ".sql" in file:
|
||||
omega_pixiv_illusts = Path() / file
|
||||
if omega_pixiv_illusts:
|
||||
with open(omega_pixiv_illusts, "r", encoding="utf8") as f:
|
||||
@@ -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))
|
||||
)
|
||||
|
||||
@@ -1,11 +1,10 @@
|
||||
from nonebot.adapters.onebot.v11 import Bot, MessageEvent, GroupMessageEvent, Message
|
||||
from nonebot.adapters.onebot.v11 import Bot, MessageEvent, GroupMessageEvent, Message, NetworkError
|
||||
from nonebot.matcher import Matcher
|
||||
from nonebot.params import CommandArg
|
||||
from nonebot import on_command
|
||||
from utils.utils import is_number
|
||||
from .data_source import get_pixiv_urls, download_pixiv_imgs, search_pixiv_urls
|
||||
from services.log import logger
|
||||
from nonebot.adapters.onebot.v11.exception import NetworkError
|
||||
from asyncio.exceptions import TimeoutError
|
||||
from utils.message_builder import custom_forward_msg
|
||||
from configs.config import Config
|
||||
@@ -18,7 +17,7 @@ __zx_plugin_name__ = "P站排行/搜图"
|
||||
__plugin_usage__ = """
|
||||
usage:
|
||||
P站排行:
|
||||
可选参数:
|
||||
可选参数:
|
||||
类型:
|
||||
1. 日排行
|
||||
2. 周排行
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
from configs.path_config import IMAGE_PATH
|
||||
from utils.message_builder import image
|
||||
from utils.utils import change_img_md5
|
||||
from asyncio.exceptions import TimeoutError
|
||||
from configs.config import Config
|
||||
from utils.http_utils import AsyncHttpx
|
||||
@@ -143,6 +144,7 @@ async def download_pixiv_imgs(
|
||||
file,
|
||||
timeout=Config.get_config("pixiv_rank_search", "TIMEOUT"),
|
||||
):
|
||||
change_img_md5(file)
|
||||
if forward_msg_index is not None:
|
||||
result += image(
|
||||
f"{user_id}_{forward_msg_index}_{index}_pixiv.jpg",
|
||||
|
||||
@@ -64,7 +64,7 @@ async def _poke_event(event: PokeNotifyEvent):
|
||||
rand = random.random()
|
||||
path = random.choice(["luoli", "meitu"])
|
||||
if rand <= 0.3 and len(os.listdir(IMAGE_PATH / "image_management" / path)) > 0:
|
||||
index = random.randint(0, len(os.listdir(IMAGE_PATH / "image_management" / path)))
|
||||
index = random.randint(0, len(os.listdir(IMAGE_PATH / "image_management" / path)) - 1)
|
||||
result = f"id:{index}" + image(f"{index}.jpg", "image_management/" + path)
|
||||
await poke_.send(result)
|
||||
logger.info(f"USER {event.user_id} 戳了戳我 回复: {result} {result}")
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user