Compare commits

...
97 Commits
Author SHA1 Message Date
HibiKier 30bcef32e4 Update README.md 2023-03-12 21:56:57 +08:00
HibiKier 33d5cd6b5c fix bug 2023-03-12 21:53:15 +08:00
HibiKier c574459485 UPDATE README.MD 2023-03-12 19:56:37 +08:00
HibiKier ca9933a489 Merge pull request #1301 from YuS1aN/main
色图bug修复、增加指令
2023-03-12 19:55:21 +08:00
HibiKier 429b7e38b5 Update data_source.py 2023-03-12 19:54:11 +08:00
HibiKier 8c7110ae85 UPDATE README.MD 2023-03-12 19:50:24 +08:00
HibiKier a43a7b5f75 更新开箱指令 2023-03-12 19:50:08 +08:00
HibiKier 5b9218251e Merge pull request #1299 from LambdaYH/patch-7
签到卡片中今日增加的好感度被显示成总好感度了
2023-03-09 19:42:05 +08:00
HibiKier 461ca0af32 Merge pull request #1302 from LambdaYH/patch-8
更正sql语句
2023-03-09 19:38:49 +08:00
Cinte 97443a11e2 Update bag_user.py 2023-03-08 10:13:16 +08:00
sean 5aff21aab2 1. 修复某些情况下会发出两张图片的问题
2. 限制色图连发图片数量
3. 连发正则新增指令
2023-03-07 22:50:04 +08:00
HibiKier 1cbe807b05 fix bug 2023-03-06 22:52:15 +08:00
Cinte d8ea0c6baf Update utils.py 2023-03-06 13:00:11 +08:00
HibiKier d6ef64c70a fix bug 2023-03-05 23:19:54 +08:00
HibiKier b31fbf5810 修复开箱BUG 2023-03-05 23:11:04 +08:00
HibiKier f2ab119397 modified: README.md 2023-03-05 21:00:28 +08:00
HibiKier 33e42be32d 更新开箱会记录箱子数据以及开箱时箱子价格加入花费 2023-03-05 21:00:05 +08:00
HibiKier 8c92756493 fix bug 2023-03-04 22:53:36 +08:00
HibiKier c2ca09867a fix bug 2023-03-04 21:12:21 +08:00
HibiKier a1eb981fd4 UPDATE README.MD 2023-03-04 20:38:05 +08:00
HibiKier e32f086b8a 新增开箱日志以及自动更新武器箱 2023-03-04 20:32:03 +08:00
HibiKier bdfdbbcaeb 重写翻译,使用百度翻译API 2023-03-04 17:51:18 +08:00
HibiKier 3cdabb7d81 修复词条单图片/表情/at无法添加 2023-03-02 22:45:37 +08:00
HibiKier 422ed2ff51 fix bug 2023-03-02 21:59:07 +08:00
HibiKier 6e76ffb60f 修复词云 2023-03-02 21:01:11 +08:00
HibiKier eaa809931c 修复我的签到签到图片 2023-03-02 20:47:23 +08:00
HibiKier 4163435c84 更正BuffSkin添加语句 2023-03-02 19:33:08 +08:00
HibiKier 99263ab36a Merge pull request #1287 from yajiwa/main
修复签到
2023-03-02 19:30:33 +08:00
HibiKier 0d76fa8560 Merge pull request #1288 from LambdaYH/patch-6
修复config.yaml中把False也当成None的问题
2023-03-02 19:29:41 +08:00
Cinte c3644576b8 顺便把写错的type改回来 2023-03-02 18:31:36 +08:00
Cinte 325e345b4e Update __init__.py 2023-03-02 18:17:07 +08:00
yajiwa 3b7972454d no message 2023-03-02 11:31:42 +08:00
yajiwa 6af5e42bf0 fix sign_in bug 2023-03-02 11:29:54 +08:00
HibiKier edc68921c9 修复消息统计 2023-03-01 23:24:56 +08:00
HibiKier 83c3f44ae7 重写开箱更新箱子,允许更新目前所有箱子的皮肤 2023-03-01 22:54:03 +08:00
HibiKier 6d7e79e9de Merge pull request #1282 from MobiusT/main
修复同意群聊请求以及添加群认证 更新变成查询的问题
2023-02-28 20:08:40 +08:00
HibiKier 401029bd27 Merge pull request #1283 from LambdaYH/main
把Config的type字段默认类型由str改为None
2023-02-28 20:07:03 +08:00
LambdaYH 33a0477fab 把Config的type字段默认类型由str改为None 2023-02-27 21:39:13 +08:00
HibiKier 6826c10a09 fix bug 2023-02-27 19:44:47 +08:00
HibiKier 8133b61ebd Config提供type字段确定配置项类型 2023-02-26 22:17:26 +08:00
MobiusT 898d7b40b3 补充群认证同样问题 2023-02-26 20:42:50 +08:00
MobiusT 5a9beb2e5c 修复同意群聊请求出错 2023-02-26 20:28:10 +08:00
HibiKier a63fda8a0d UPDATE README.MD 2023-02-25 17:42:08 +08:00
HibiKier 0baf043f74 Merge pull request #1275 from unknownsno/main
修复ys查询,尘歌壶背景尺寸与内容不匹配的问题
2023-02-25 16:59:41 +08:00
HibiKier cbc32a79d2 Merge pull request #1274 from yajiwa/main
update coser
2023-02-25 16:51:28 +08:00
HibiKier df5a68bcec fix bug 2023-02-25 16:42:09 +08:00
HibiKier 1ca6a67a24 Update __init__.py 2023-02-23 21:01:12 +08:00
unknownsno f27a537f70 修复ys查询,尘歌壶背景尺寸与内容不匹配的问题 2023-02-22 22:48:24 +08:00
yajiwa aa76fae386 update coser 2023-02-22 17:51:51 +08:00
HibiKier d35d53825c Merge branch 'main' of https://github.com/HibiKier/zhenxun_bot 2023-02-21 20:41:38 +08:00
HibiKier 7f743141fb modified: models/level_user.py 2023-02-21 20:40:51 +08:00
HibiKier fa88a08d8e UPDATE pypoetry.toml 2023-02-21 20:38:19 +08:00
HibiKier 06d0bb1ab2 Update level_user.py 2023-02-21 20:34:45 +08:00
HibiKier 3c0f35b3b7 UPDATE pyproject.toml 2023-02-21 20:32:38 +08:00
HibiKier 396d9c1a65 Merge branch 'main' of https://github.com/HibiKier/zhenxun_bot 2023-02-20 22:14:54 +08:00
HibiKier e41f3a4ce7 char_history新增_run_script 2023-02-20 22:14:40 +08:00
HibiKier 8f9e4d6e4f Merge pull request #1270 from MobiusT/main
chat_history部分字段调整为可null
2023-02-20 22:09:25 +08:00
HibiKier d0764f0865 Update chat_history.py 2023-02-20 22:08:01 +08:00
MobiusT ab32eedbb9 chat_history部分字段调整为可null
字段group_id、text、plain_text修改为可空
使新建表结构与旧版基本一致
并且修复以下报错
CMD[定时任务] 定时批量添加聊天记录 || 错误 <class 'ValueError'>: group_id is non nullable field, but null was passed
2023-02-20 21:09:19 +08:00
HibiKier 73f858e071 Merge pull request #1266 from yajiwa/main
修复b站订阅添加失败的问题
2023-02-20 19:11:02 +08:00
yajiwa 73b4a1254c fix bilibili_sub bug 2023-02-20 00:04:05 +08:00
HibiKier fbf82d999f 修改BUG 2023-02-19 17:17:55 +08:00
HibiKier 1f68481256 modified: README.md
modified:   basic_plugins/super_cmd/reload_setting.py
2023-02-19 12:33:22 +08:00
HibiKier 876cfb421a 执行_run_script添加try 2023-02-19 12:29:51 +08:00
HibiKier c97f1bd750 Merge branch 'main' of https://github.com/HibiKier/zhenxun_bot 2023-02-19 00:08:45 +08:00
HibiKier e4dec9f0de word_bank执行添加to_me字段 2023-02-19 00:08:26 +08:00
HibiKier 282b4c102f Update README.md 2023-02-18 23:03:39 +08:00
HibiKier 37e0003810 Update config.py 2023-02-18 23:03:30 +08:00
HibiKier 42683e4f09 Merge branch 'main' of https://github.com/HibiKier/zhenxun_bot 2023-02-18 22:58:56 +08:00
HibiKier 6352ea7c6f update README.md 2023-02-18 22:57:40 +08:00
HibiKier 40c10e672c Merge pull request #1258 from McElaga/main
细节优化,原神今日素材重写
2023-02-18 22:56:43 +08:00
HibiKier fef85190c5 Merge branch 'main' into main 2023-02-18 22:56:25 +08:00
HibiKier eea1be0fc1 Merge pull request #1257 from yajiwa/main
解决开红包经常误触的问题
2023-02-18 22:53:13 +08:00
HibiKier 633862d121 Merge pull request #1263 from CRAZYShimakaze/main
修复插件帮助命令不生效的问题
2023-02-18 22:05:58 +08:00
HibiKier 86ac770971 modified: README.md 2023-02-18 18:48:36 +08:00
HibiKier 7685b95031 modified: basic_plugins/admin_bot_manage/_data_source.py
modified:   basic_plugins/admin_bot_manage/admin_config.py
	modified:   basic_plugins/admin_bot_manage/custom_welcome_message.py
	modified:   basic_plugins/admin_bot_manage/timing_task.py
	modified:   basic_plugins/apscheduler/__init__.py
	modified:   basic_plugins/ban/__init__.py
	modified:   basic_plugins/ban/data_source.py
	modified:   basic_plugins/chat_history/chat_message.py
	modified:   basic_plugins/chat_history/chat_message_handle.py
	modified:   basic_plugins/group_handle/__init__.py
	modified:   basic_plugins/hooks/_utils.py
	modified:   basic_plugins/hooks/ban_hook.py
	modified:   basic_plugins/hooks/chkdsk_hook.py
	modified:   basic_plugins/init_plugin_config/__init__.py
	deleted:    basic_plugins/init_plugin_config/init_group_manager.py
	modified:   basic_plugins/invite_manager/__init__.py
	new file:   basic_plugins/invite_manager/utils.py
	modified:   basic_plugins/nickname.py
	modified:   basic_plugins/plugin_shop/__init__.py
	modified:   basic_plugins/plugin_shop/data_source.py
	modified:   basic_plugins/scripts.py
	modified:   basic_plugins/shop/__init__.py
	modified:   basic_plugins/shop/buy.py
	modified:   basic_plugins/shop/gold.py
	modified:   basic_plugins/shop/my_props/__init__.py
	deleted:    basic_plugins/shop/reset_today_gold.py
	modified:   basic_plugins/shop/shop_handle/__init__.py
	modified:   basic_plugins/shop/shop_handle/data_source.py
	modified:   basic_plugins/shop/use/__init__.py
	modified:   basic_plugins/shop/use/data_source.py
	modified:   basic_plugins/super_cmd/__init__.py
	modified:   basic_plugins/super_cmd/bot_friend_group.py
	modified:   basic_plugins/super_cmd/clear_data.py
	modified:   basic_plugins/super_cmd/exec_sql.py
	modified:   basic_plugins/super_cmd/manager_group.py
	modified:   basic_plugins/super_cmd/reload_setting.py
	modified:   basic_plugins/super_cmd/set_admin_permissions.py
	deleted:    basic_plugins/super_cmd/super_task_switch.py
	modified:   basic_plugins/super_cmd/update_friend_group_info.py
	modified:   basic_plugins/super_help/__init__.py
	modified:   basic_plugins/update_info.py
	modified:   configs/config.py
	modified:   configs/utils/__init__.py
	modified:   models/bag_user.py
	modified:   models/ban_user.py
	modified:   models/chat_history.py
	modified:   models/friend_user.py
	modified:   models/goods_info.py
	modified:   models/group_info.py
	modified:   models/group_member_info.py
	modified:   models/level_user.py
	modified:   models/sign_group_user.py
	modified:   models/user_shop_gold_log.py
	modified:   plugins/aconfig/__init__.py
	modified:   plugins/ai/__init__.py
	modified:   plugins/ai/data_source.py
	modified:   plugins/bilibili_sub/__init__.py
	modified:   plugins/bilibili_sub/data_source.py
	modified:   plugins/bilibili_sub/model.py
	modified:   plugins/black_word/__init__.py
	modified:   plugins/black_word/model.py
	modified:   plugins/black_word/utils.py
	modified:   plugins/bt/data_source.py
	modified:   plugins/genshin/almanac/__init__.py
	modified:   plugins/genshin/material_remind/__init__.py
	modified:   plugins/genshin/query_user/_models/__init__.py
	modified:   plugins/genshin/query_user/_utils/__init__.py
	modified:   plugins/genshin/query_user/bind/__init__.py
	modified:   plugins/genshin/query_user/genshin_sign/__init__.py
	modified:   plugins/genshin/query_user/genshin_sign/data_source.py
	modified:   plugins/genshin/query_user/genshin_sign/init_task.py
	modified:   plugins/genshin/query_user/mihoyobbs_sign/__init__.py
	modified:   plugins/genshin/query_user/query_memo/__init__.py
	modified:   plugins/genshin/query_user/query_memo/data_source.py
	modified:   plugins/genshin/query_user/query_role/__init__.py
	modified:   plugins/genshin/query_user/query_role/data_source.py
	modified:   plugins/genshin/query_user/reset_today_query_user_data/__init__.py
	modified:   plugins/genshin/query_user/resin_remind/__init__.py
	modified:   plugins/genshin/query_user/resin_remind/init_task.py
	modified:   plugins/gold_redbag/model.py
	modified:   plugins/image_management/send_image/__init__.py
	modified:   plugins/my_info/__init__.py
	modified:   plugins/open_cases/models/buff_prices.py
	modified:   plugins/open_cases/models/open_cases_user.py
	modified:   plugins/open_cases/open_cases_c.py
	modified:   plugins/open_cases/utils.py
	modified:   plugins/parse_bilibili_json.py
	modified:   plugins/pid_search.py
	modified:   plugins/pix_gallery/__init__.py
	modified:   plugins/pix_gallery/_data_source.py
	modified:   plugins/pix_gallery/_model/omega_pixiv_illusts.py
	modified:   plugins/pix_gallery/_model/pixiv.py
	modified:   plugins/pix_gallery/_model/pixiv_keyword_user.py
	modified:   plugins/pix_gallery/pix_add_keyword.py
	modified:   plugins/pix_gallery/pix_pass_del_keyword.py
	modified:   plugins/pix_gallery/pix_show_info.py
	modified:   plugins/pix_gallery/pix_update.py
	modified:   plugins/pixiv_rank_search/data_source.py
	modified:   plugins/poke/__init__.py
	modified:   plugins/russian/__init__.py
	modified:   plugins/russian/data_source.py
	modified:   plugins/russian/model.py
	modified:   plugins/send_dinggong_voice/__init__.py
	modified:   plugins/send_setu_/_model.py
	modified:   plugins/send_setu_/send_setu/__init__.py
	modified:   plugins/send_setu_/send_setu/data_source.py
	modified:   plugins/send_setu_/update_setu/data_source.py
	modified:   plugins/sign_in/goods_register.py
	modified:   plugins/sign_in/group_user_checkin.py
	modified:   plugins/sign_in/random_event.py
	modified:   plugins/sign_in/utils.py
	modified:   plugins/statistics/_model.py
	modified:   plugins/statistics/statistics_handle.py
	modified:   plugins/statistics/statistics_hook.py
	modified:   plugins/update_picture.py
	modified:   plugins/web_ui/api/request.py
	modified:   plugins/word_bank/_model.py
	deleted:    plugins/word_bank/_old_model.py
	modified:   plugins/word_bank/_rule.py
	modified:   plugins/word_bank/word_handle.py
	modified:   plugins/word_clouds/data_source.py
	modified:   resources/image/sign/sign_res/bar.png
	modified:   resources/image/sign/sign_res/bar_white.png
	modified:   services/db_context.py
	modified:   services/log.py
	modified:   utils/browser.py
	modified:   utils/data_utils.py
	modified:   utils/depends/__init__.py
	modified:   utils/http_utils.py
	modified:   utils/image_utils.py
	modified:   utils/manager/admin_manager.py
	modified:   utils/message_builder.py
	modified:   utils/utils.py
2023-02-18 18:46:54 +08:00
HibiKier a6105e8a1b modified: .gitignore 2023-02-18 17:51:22 +08:00
CRAZYShimakaze 9feea3ba1d 修复插件帮助命令不生效的问题 2023-02-16 22:59:53 +08:00
McElaga 22f415abfa 重新原神素材 2023-02-09 22:11:28 +08:00
McElaga c99292d327 重写原神今日素材 2023-02-09 22:07:08 +08:00
McElaga 362ef519b7 细节优化 2023-02-09 21:59:40 +08:00
yajiwa a053c120c4 update gold_redbag 2023-02-06 02:06:36 +08:00
HibiKier 4d133ed2df Merge pull request #1249 from noahzark/patch-3
修复B站转发卡片BUG
2023-02-05 17:28:20 +08:00
HibiKier d2cf18e11d Merge pull request #1244 from AkashiCoin/patch-1
替换pixiv反向代理地址
2023-02-05 16:37:57 +08:00
Feliciano Long 5ddfd2b5b9 修复B站转发卡片BUG
1. 最新URL `https://b23.tv/nz4tMtX`
返回
```
HTTP/2 301 
date: Tue, 31 Jan 2023 09:43:07 GMT
content-type: text/html; charset=utf-8
content-length: 781
set-cookie: ******; path=/; expires=Mon, 27 Oct 2025 09:43:07 GMT; domain=.bilibili.com
set-cookie: b_nut=1675158187; path=/; expires=Wed, 31 Jan 2024 09:43:07 GMT; domain=.bilibili.com
location: /video/BV1CM4y197A4/?buvid=******
vary: Origin,Accept-Encoding
idc: shjd
x-cache-webcdn: BYPASS from blzone07
```

这时候 url 会变成 `/video/BV1CM4y197A4/` 需要去除末尾的/来提取BVID

2. 某些转发(比如ios客户端)目前QQ小程序的desc为空,需要在prompt里检测
```json
[CQ:json,data={"app":"com.tencent.miniapp_01"&#44;"appID":"100951776"&#44;"bthirdappforward":true&#44;"bthirdappforwardforbackendswitch":true&#44;"config":{"autoSize":0&#44;"ctime":1675159871&#44;"forward":1&#44;"height":0&#44;"token":"d1dda06f69b12d511972e87ec49b2a19"&#44;"type":"normal"&#44;"width":0}&#44;"desc":""&#44;"extra":{"app_type":1&#44;"appid":100951776&#44;"uin":28696786}&#44;"meta":{"detail_1":{"appType":0&#44;"appid":"1109937557"&#44;"desc":"网络中挖到国防光缆vs现实"&#44;"gamePoints":""&#44;"gamePointsUrl":""&#44;"host":{"nick":"Noahzark"&#44;"uin":28696786}&#44;"icon":"http://miniapp.gtimg.cn/public/appicon/432b76be3a548fc128acaa6c1ec90131_200.jpg"&#44;"preview":"https://pic.ugcimg.cn/07cc5adddb5e6ef3cc27326a12b2df17/jpg1"&#44;"qqdocurl":"https://b23.tv/******"&#44;"scene":1036&#44;"shareTemplateData":{}&#44;"shareTemplateId":"8C8E89B49BE609866298ADDFF2DBABA4"&#44;"showLittleTail":""&#44;"title":"哔哩哔哩"&#44;"url":"m.q.qq.com/a/s/f6362a996fd107b3d4dbedce09c17d92"}}&#44;"prompt":"&#91;QQ小程序&#93;哔哩哔哩"&#44;"ver":"1.0.1.42"&#44;"view":"view_8C8E89B49BE609866298ADDFF2DBABA4"}]
```
2023-01-31 18:13:52 +08:00
AkashiCoin 71264bffa1 替换pixiv反向代理地址 2023-01-27 22:45:01 +08:00
HibiKier f39005f5e1 Update README.md 2023-01-06 17:31:31 +08:00
HibiKier 9454308ba7 允许真寻自身触发命令,提供配置项 self_message:STATUS 2022-12-31 18:54:17 +08:00
HibiKier 51dd622e20 Merge remote-tracking branch 'origin/main' 2022-12-31 18:10:58 +08:00
HibiKier 05de55cc6b 原神黄历改为PIL 2022-12-31 18:10:48 +08:00
HibiKier db441c8406 Add files via upload 2022-12-31 17:16:54 +08:00
HibiKier 295c535002 Add files via upload 2022-12-31 17:16:28 +08:00
HibiKier b0606feff3 原神黄历改为PIL 2022-12-31 17:15:14 +08:00
HibiKier 2070014021 修复词条在某些回答下出错 2022-12-31 14:59:56 +08:00
HibiKier def53aa22e Merge pull request #1226 from newton-miku/main
修复epic报错,优化简介
2022-12-31 14:15:05 +08:00
newton_miku a3d10385a0 修复epic报错,优化简介
1.修复了因报错UnboundLocalError: local variable 'game_desp' referenced before assignment而导致的发送失败
2.修复因简介过长导致的win版QQ在群聊中无法查看发送的合并转发消息的问题(疑似因为单条消息过长?测试时安卓QQ无此异常)(当存在简短介绍时,使用简短介绍)
2022-12-31 03:30:43 +08:00
HibiKier cb7527a44b UPDATE UPDATE_INFO 2022-12-27 12:39:36 +08:00
414 changed files with 8482 additions and 7733 deletions
+5 -1
View File
@@ -156,10 +156,14 @@ data/
.env.dev
/resources/text/
/resources/image/
/resources/temp/
!/resources/template/
configs/config.py
/configs/config.py
configs/config.yaml
./.env
./.env.dev
plugins/csgo_server/
plugins/activity/
!/resources/image/genshin/alc/back.png
!/data/genshin_alc/
.vscode/launch.json
+256 -139
View File
@@ -5,9 +5,12 @@
![maven](https://img.shields.io/badge/go--cqhttp-1.0.0-red)
# 绪山真寻Bot
****
此项目基于 Nonebot2 和 go-cqhttp 开发,以 postgresql 作为数据库的QQ群娱乐机器人
## 关于
用爱发电,某些功能学习借鉴了大佬们的代码,因为绪山真寻实在太可爱了因此开发了
绪山真寻bot,实现了一些对群友的娱乐功能和实用功能(大概)。
@@ -18,9 +21,11 @@
[//]: # (是老手!讨论插件开发,nonebot2开发,可以加入[ <strong>[真寻酱的技术群]&#40;https://jq.qq.com/?_wv=1027&k=u8PgBkMZ&#41; </strong>])
## 声明
此项目仅用于学习交流,请勿用于非法用途
# Nonebot2
<img style="height: 200px;width: 200px;" src="https://camo.githubusercontent.com/0ef71e86056da694c540790aa4a4e314396884d6c4fdb95362a7538b27a1b034/68747470733a2f2f76322e6e6f6e65626f742e6465762f6c6f676f2e706e67">
非常 [ **[NICE](https://github.com/nonebot/nonebot2)** ] 的OneBot框架
@@ -30,18 +35,23 @@
# [传送门](https://hibikier.github.io/zhenxun_bot/)
## 真寻的帮助
请对真寻说: '真寻帮助' or '管理员帮助' or '超级用户帮助' or '真寻帮助 指令'
## 普通帮助图片
![x](https://raw.githubusercontent.com/HibiKier/zhenxun_bot/main/docs_image/help.png)
## HTML版帮助图片
![x](https://raw.githubusercontent.com/HibiKier/zhenxun_bot/main/docs_image/html_help.png)
## Web UI
[zhenxun_bot_webui](https://github.com/HibiKier/zhenxun_bot_webui)
## 一键安装脚本
[zhenxun_bot-deploy](https://github.com/AkashiCoin/zhenxun_bot-deploy)
## 提供符合真寻标准的插件仓库
@@ -50,152 +60,164 @@
## 来点优点?
* 实现了许多功能,且提供了大量功能管理命令
* 通过Config配置项将所有插件配置统计保存至config.yaml,利于统一用户修改
* 方便增删插件,原生nonebot2 matcher,不需要额外修改,仅仅通过简单的配置属性就可以生成`帮助图片`和`帮助信息`
* 提供了cd,阻塞,每日次数等限制,仅仅通过简单的属性就可以生成一个限制,例如:`__plugin_cd_limit__`
* __..... 更多详细请通过`传送门`查看文档!__
* 实现了许多功能,且提供了大量功能管理命令
* 通过Config配置项将所有插件配置统计保存至config.yaml,利于统一用户修改
* 方便增删插件,原生nonebot2 matcher,不需要额外修改,仅仅通过简单的配置属性就可以生成`帮助图片`和`帮助信息`
* 提供了cd,阻塞,每日次数等限制,仅仅通过简单的属性就可以生成一个限制,例如:`__plugin_cd_limit__`
* __..... 更多详细请通过`传送门`查看文档!__
## 功能列表
<details>
<summary>已实现的功能</summary>
### 已实现的常用功能
- [x] 昵称系统(群与群与私聊分开.)
- [x] 图灵AI(会把'你'等关键字替换为你的昵称),且带有 [AnimeThesaurus](https://github.com/Kyomotoi/AnimeThesaurus),够味
- [x] 签到/我的签到/好感度排行/好感度总排行(影响色图概率和开箱次数,支持配置)
- [x] 发送某文件夹下的随机图片(支持自定义,默认:美图,萝莉,壁纸)
- [x] 色图(这不是基础功能嘛喂)
- [x] coser
- [x] 黑白草图生成器
- [x] 鸡汤/语录
- [x] 骂我(钉宫语音)
- [x] 戳一戳(概率发送美图,钉宫语音或者戳回去)
- [x] 模拟开箱/我的开箱/群开箱统计/我的金色/设置cookie(csgo,内置爬虫脚本,需要提前抓取数据和图片,需要session,可能需要代理,阿里云服务器等ip也许已经被ban了(我无代理访问失败),如果访问太多账号API调用可能被禁止访问api!)
- [x] 鲁迅说过
- [x] 构造假消息(自定义的分享链接)
- [x] 商店/我的金币/购买道具/使用道具
- [x] 8种手游抽卡 (查看 [nonebot_plugin_gamedraw](https://github.com/HibiKier/nonebot_plugin_gamedraw))
- [x] 我有一个朋友想问问..(借鉴pcrbot插件)
- [x] 原神黄历
- [x] 原神今日素材
- [x] 原神资源查询 (借鉴[Genshin_Impact_bot](https://github.com/H-K-Y/Genshin_Impact_bot)插件)
- [x] 原神便笺查询
- [x] 原神玩家查询
- [x] 原神树脂提醒
- [x] 原神签到/自动签到
- [x] 金币红包
- [x] 微博热搜
- [x] B站主播/UP/番剧订阅
- [x] pil对图片的一些操作
- [x] BUFF饰品底价查询(需要session)
- [x] 天气查询
- [x] 疫情查询
- [x] bt磁力搜索(咳咳,这功能我想dddd)
- [x] reimu搜索(上车) (使用[XUN_Langskip](https://github.com/Angel-Hair/XUN_Bot)的插件)
- [x] 靠图识番 (使用[XUN_Langskip](https://github.com/Angel-Hair/XUN_Bot)的插件)
- [x] 以图搜图 (使用[nonebot_plugin_picsearcher](https://github.com/synodriver/nonebot_plugin_picsearcher)插件)
- [x] 搜番
- [x] 点歌 [nonebot_plugin_songpicker2](https://github.com/maxesisn/nonebot_plugin_songpicker2)插件(删除了选歌和评论)
- [x] epic免费游戏
- [x] p站排行榜
- [x] p站搜图
- [x] 翻译(日英韩)
- [x] pix图库(一个自己的图库,含有增删查改,黑名单等命令)
* [x] 昵称系统(群与群与私聊分开.)
- [x] 查看当前群欢迎消息
- [x] 查看该群自己的权限
- [x] 我的信息(只是为了看看什么时候入群)
- [x] 更新信息(如果继续更新的话)
- [x] go-cqhttp最新版下载和上传(不需要请删除)
- [x] 撤回
- [x] 滴滴滴-(用户对超级用户发送消息)
- [x] 金币红包/金币排行
- [x] 俄罗斯轮盘/胜场排行/败场排行/欧洲人排行/慈善家排行
- [x] 网易云热评
- [x] 念首古诗
- [x] 获取b站视频封面
- [x] 通过PID获取图片
- [x] 功能统计可视化
- [x] 词云
- [x] 关于
* [x] 图灵AI(会把'你'等关键字替换为你的昵称),且带有 [AnimeThesaurus](https://github.com/Kyomotoi/AnimeThesaurus),够味
* [x] 签到/我的签到/好感度排行/好感度总排行(影响色图概率和开箱次数,支持配置)
* [x] 发送某文件夹下的随机图片(支持自定义,默认:美图,萝莉,壁纸)
* [x] 色图(这不是基础功能嘛喂)
* [x] coser
* [x] 黑白草图生成器
* [x] 鸡汤/语录
* [x] 骂我(钉宫语音)
* [x] 戳一戳(概率发送美图,钉宫语音或者戳回去)
* [x] 模拟开箱/我的开箱/群开箱统计/我的金色/设置cookie(csgo,内置爬虫脚本,需要提前抓取数据和图片,需要session,可能需要代理,阿里云服务器等ip也许已经被ban了(我无代理访问失败),如果访问太多账号API调用可能被禁止访问api!)
* [x] 鲁迅说过
* [x] 构造假消息(自定义的分享链接)
* [x] 商店/我的金币/购买道具/使用道具
* [x] 8种手游抽卡 (查看 [nonebot_plugin_gamedraw](https://github.com/HibiKier/nonebot_plugin_gamedraw))
* [x] 我有一个朋友想问问..(借鉴pcrbot插件)
* [x] 原神黄历
* [x] 原神今日素材
* [x] 原神资源查询 (借鉴[Genshin_Impact_bot](https://github.com/H-K-Y/Genshin_Impact_bot)插件)
* [x] 原神便笺查询
* [x] 原神玩家查询
* [x] 原神树脂提醒
* [x] 原神签到/自动签到
* [x] 金币红包
* [x] 微博热搜
* [x] B站主播/UP/番剧订阅
* [x] pil对图片的一些操作
* [x] BUFF饰品底价查询(需要session)
* [x] 天气查询
* [x] 疫情查询
* [x] bt磁力搜索(咳咳,这功能我想dddd)
* [x] reimu搜索(上车) (使用[XUN_Langskip](https://github.com/Angel-Hair/XUN_Bot)的插件)
* [x] 靠图识番 (使用[XUN_Langskip](https://github.com/Angel-Hair/XUN_Bot)的插件)
* [x] 以图搜图 (使用[nonebot_plugin_picsearcher](https://github.com/synodriver/nonebot_plugin_picsearcher)插件)
* [x] 搜番
* [x] 点歌 [nonebot_plugin_songpicker2](https://github.com/maxesisn/nonebot_plugin_songpicker2)插件(删除了选歌和评论)
* [x] epic免费游戏
* [x] p站排行榜
* [x] p站搜图
* [x] 翻译(日英韩)
* [x] pix图库(一个自己的图库,含有增删查改,黑名单等命令)
* [x] 查看当前群欢迎消息
* [x] 查看该群自己的权限
* [x] 我的信息(只是为了看看什么时候入群)
* [x] 更新信息(如果继续更新的话)
* [x] go-cqhttp最新版下载和上传(不需要请删除)
* [x] 撤回
* [x] 滴滴滴-(用户对超级用户发送消息)
* [x] 金币红包/金币排行
* [x] 俄罗斯轮盘/胜场排行/败场排行/欧洲人排行/慈善家排行
* [x] 网易云热评
* [x] 念首古诗
* [x] 获取b站视频封面
* [x] 通过PID获取图片
* [x] 功能统计可视化
* [x] 词云
* [x] 关于
### 已实现的管理员功能
- [x] 更新群组成员信息
- [x] 95%的群功能开关
- [x] 查看群内被动技能状态
- [x] 自定义群欢迎消息(是真寻的不是管家的!)
- [x] .ban/.unban(支持设置ban时长)= 黑白名单
- [x] 刷屏禁言相关:刷屏检测设置/设置禁言时长/设置检测次数
- [x] 上传图片/连续上传图片 (上传图片至指定图库)
- [x] 移动图片 (同上)
- [x] 删除图片 (同上)
- [x] 群内B站订阅
- [x] 词条设置
- [x] 休息吧/醒来
* [x] 更新群组成员信息
* [x] 95%的群功能开关
* [x] 查看群内被动技能状态
* [x] 自定义群欢迎消息(是真寻的不是管家的!)
* [x] .ban/.unban(支持设置ban时长)= 黑白名单
* [x] 刷屏禁言相关:刷屏检测设置/设置禁言时长/设置检测次数
* [x] 上传图片/连续上传图片 (上传图片至指定图库)
* [x] 移动图片 (同上)
* [x] 删除图片 (同上)
* [x] 群内B站订阅
* [x] 词条设置
* [x] 休息吧/醒来
### 已实现的超级用户功能
- [x] 添加/删除权限(是真寻的管理员权限,不是群管理员)
- [x] 开启/关闭指定群的广播通知
- [x] 广播
- [x] 自检(检查系统状态)
- [x] 所有群组/所有好友
- [x] 退出指定群
- [x] 更新好友信息/更新群信息
- [x] /t(对用户进行回复或发送消息)
- [x] 上传/删除/修改商品(需要编写对应的商品功能)
- [x] 节日红包发送
- [x] 修改群权限
- [x] ban
- [x] 更新色图
- [x] 更新价格/更加图片(csgo开箱)
- [x] 重载原神/方舟/赛马娘/坎公骑冠剑卡池
- [x] 更新原神今日素材/更新原神资源信息
- [x] PIX相关操作
- [x] 检查更新真寻
- [x] 重启
- [x] 添加/删除/查看群白名单
- [x] 功能开关(更多设置)
- [x] 功能状态
- [x] b了
- [x] 执行sql
- [x] 重载配置
- [x] 清理临时数据
- [x] 增删群认证
- [x] 同意/拒绝好友/群聊请求
- [x] 配置重载
* [x] 添加/删除权限(是真寻的管理员权限,不是群管理员)
* [x] 开启/关闭指定群的广播通知
* [x] 广播
* [x] 自检(检查系统状态)
* [x] 所有群组/所有好友
* [x] 退出指定群
* [x] 更新好友信息/更新群信息
* [x] /t(对用户进行回复或发送消息)
* [x] 上传/删除/修改商品(需要编写对应的商品功能)
* [x] 节日红包发送
* [x] 修改群权限
* [x] ban
* [x] 更新色图
* [x] 更新价格/更加图片(csgo开箱)
* [x] 重载原神/方舟/赛马娘/坎公骑冠剑卡池
* [x] 更新原神今日素材/更新原神资源信息
* [x] PIX相关操作
* [x] 检查更新真寻
* [x] 重启
* [x] 添加/删除/查看群白名单
* [x] 功能开关(更多设置)
* [x] 功能状态
* [x] b了
* [x] 执行sql
* [x] 重载配置
* [x] 清理临时数据
* [x] 增删群认证
* [x] 同意/拒绝好友/群聊请求
* [x] 配置重载
#### 超级用户的被动技能
- [x] 邀请入群提醒(别人邀请真寻入群)
- [x] 添加好友提醒(别人添加真寻好友)
* [x] 邀请入群提醒(别人邀请真寻入群)
* [x] 添加好友提醒(别人添加真寻好友)
### 已实现的被动技能
- [x] 进群欢迎消息
- [x] 群早晚安
- [x] 每日开箱重置提醒
- [x] b站转发解析(解析b站分享信息,支持bv,bilibili链接,b站手机端转发卡片,cv,b23.tv),且5分钟内不解析相同url
- [x] 丢人爬(爬表情包)
- [x] epic通知(每日发送epic免费游戏链接)
- [x] 原神黄历提醒
- [x] 复读
### 已实现的看不见的技能!
- [x] 刷屏禁言检测
- [x] 功能调用统计
- [x] 检测恶意触发命令(将被最高权限ban掉30分钟,只有最高权限(9级)可以进行unban)
- [x] 自动同意好友请求,加群请求将会提醒管理员,退群提示,加群欢迎等等
- [x] 群聊时间检测(当群聊最后一人发言时间大于当前36小时后将关闭该群所有通知(即被动技能))
- [x] 群管理员监控,自动为新晋管理员增加权限,为失去群管理员的用户删除权限
- [x] 群权限系统
- [x] 定时更新权限
- [x] 自动配置重载
* [x] 进群欢迎消息
* [x] 群早晚安
* [x] 每日开箱重置提醒
* [x] b站转发解析(解析b站分享信息,支持bv,bilibili链接,b站手机端转发卡片,cv,b23.tv),且5分钟内不解析相同url
* [x] 丢人爬(爬表情包)
* [x] epic通知(每日发送epic免费游戏链接)
* [x] 原神黄历提醒
* [x] 复读
### 已实现的看不见的技能
* [x] 刷屏禁言检测
* [x] 功能调用统计
* [x] 检测恶意触发命令(将被最高权限ban掉30分钟,只有最高权限(9级)可以进行unban)
* [x] 自动同意好友请求,加群请求将会提醒管理员,退群提示,加群欢迎等等
* [x] 群聊时间检测(当群聊最后一人发言时间大于当前36小时后将关闭该群所有通知(即被动技能))
* [x] 群管理员监控,自动为新晋管理员增加权限,为失去群管理员的用户删除权限
* [x] 群权限系统
* [x] 定时更新权限
* [x] 自动配置重载
</details>
## 详细配置请前往文档,以下为最简部署和配置,如果你有基础并学习过nonebot2的话
## 简单部署
```
@@ -238,26 +260,38 @@ python bot.py
```
## 使用Docker
__Docker 单机版(仅真寻Bot)__
**Docker 单机版(仅真寻Bot)**
**点击下方的 GitHub 徽标查看教程**
[![Github](https://shields.io/badge/GITHUB-Sakuracio-4476AF?logo=github&style=for-the-badge)](https://github.com/Sakuracio/zhenxun_bot_docker)
[![DOCKER](https://shields.io/badge/docker-hibikier/zhenxun_bot-4476AF?logo=docker&style=for-the-badge)](https://hub.docker.com/r/hibikier/zhenxun_bot)
__Docker 全量版(包含 真寻Bot PostgreSQL数据库 go-cqhttp webui等)__
**Docker 全量版(包含 真寻Bot PostgreSQL数据库 go-cqhttp webui等)**
[![Github](https://shields.io/badge/GITHUB-SinKy--Yan-4476AF?logo=github&style=for-the-badge)](https://github.com/SinKy-Yan/zhenxunbot-docker)
[![DOCKER](https://shields.io/badge/docker-jyishit/zhenxun_bot-4476AF?logo=docker&style=for-the-badge)](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|)
(可以告诉我你的 **github** 地址,我偷偷换掉0v|)
[shenqi](https://afdian.net/u/fa923a8cfe3d11eba61752540025c377)
[A_Kyuu](https://afdian.net/u/b83954fc2c1211eba9eb52540025c377)
[疯狂混沌](https://afdian.net/u/789a2f9200cd11edb38352540025c377)
[投冥](https://afdian.net/a/144514mm)
[茶喵](https://afdian.net/u/fd22382eac4d11ecbfc652540025c377)
[AemokpaTNR](https://afdian.net/u/1169bb8c8a9611edb0c152540025c377)
[爱发电用户_wrxn](https://afdian.net/u/4aa03d20db4311ecb1e752540025c377)
[qqw](https://afdian.net/u/b71db4e2cc3e11ebb76652540025c377)
[溫一壺月光下酒](https://afdian.net/u/ad667a5c650c11ed89bf52540025c377)
[伝木](https://afdian.net/u/246b80683f9511edba7552540025c377)
[阿奎](https://afdian.net/u/da41f72845d511ed930d52540025c377)
[醉梦尘逸](https://afdian.net/u/bc11d2683cd011ed99b552540025c377)
[Abc](https://afdian.net/u/870dc10a3cd311ed828852540025c377)
@@ -280,25 +314,106 @@ PS: **ARM平台** 请使用全量版 同时 **如果你的机器 RAM < 1G 可能
[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)
[爱发电用户_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)
[大魔王](https://github.com/xipesoy)
[CopilotLaLaLa](https://github.com/CopilotLaLaLa)
[嘿小欧](https://afdian.net/u/daa4bec4f24911ec82e552540025c377)
[嘿小欧](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>
## 更新
### 2023/3/12 \[v0.1.6.7]
* 新增`更新武器箱ALL`命令来更新所有武器箱
* 新增`查看武器箱`命令
* 色图bug修复、增加指令 [@pull/1301](https://github.com/HibiKier/zhenxun_bot/pull/1301)
### 2023/3/9
* 更正sql语句 [@pull/1302](https://github.com/HibiKier/zhenxun_bot/pull/1302)
* 修改签到卡片中签到增加好感度显示错误 [@pull/1299](https://github.com/HibiKier/zhenxun_bot/pull/1299)
### 2023/3/5
* 更新开箱会记录箱子数据以及开箱时箱子价格加入花费
* 修复开箱BUG
### 2023/3/4
* 重写翻译,使用百度翻译API
* 新增开箱日志以及自动更新武器箱
### 2023/3/2
* 修复config.yaml中把False也当成None的问题 [@pull/1288](https://github.com/HibiKier/zhenxun_bot/pull/1288)
* 删除道具表无用字段(props) [@pull/1287](https://github.com/HibiKier/zhenxun_bot/pull/1287)
* 修复词云
* 修复我的签到签到图片
* 更正BuffSkin添加语句
* 修复词条单图片/表情/at无法添加
### 2022/3/1
* 重写开箱更新箱子,允许更新目前所有箱子的皮肤
* 修复消息统计
### 2023/2/28
* 把Config的type字段默认类型由str改为None [@pull/1283](https://github.com/HibiKier/zhenxun_bot/pull/1283)
* 修复同意群聊请求以及添加群认证 更新变成查询的问题 [@pull/1282](https://github.com/HibiKier/zhenxun_bot/pull/1282)
### 2023/2/26
* Config提供`type`字段确定配置项类型
* 重写开箱功能
### 2023/2/25
* 修复ys查询,尘歌壶背景尺寸与内容不匹配的问题 [@pull/1270](https://github.com/HibiKier/zhenxun_bot/pull/1275)
* 更换cos url [@pull/1270](https://github.com/HibiKier/zhenxun_bot/pull/1274)
### 2023/2/20
* chat_history部分字段调整为可null [@pull/1270](https://github.com/HibiKier/zhenxun_bot/pull/1270)
### 2023/2/19
* 修正了`重载插件`的帮助提示
* 修改BUG
### 2023/2/18
* 数据库舍弃`gino`使用`tortoise`
* 昵称提供命令`全局昵称设置`
* `manager_group`群管理操作中`退群`,`修改群权限`,`添加/删除群白名单`,`添加/删除群认证`在群聊中使用命令时且未指定群聊时,默认指定当前群聊
* 修复插件帮助命令不生效的问题 [@pull/1263](https://github.com/HibiKier/zhenxun_bot/pull/1263)
* 解决开红包经常误触的问题,有红包和未领取的时候才会触发“开”命令 [@pull/1257](https://github.com/HibiKier/zhenxun_bot/pull/1257)
* 细节优化,原神今日素材重写 [@pull/1258](https://github.com/HibiKier/zhenxun_bot/pull/1258)
### 2023/1/31
* 修复B站转发卡片BUG [@pull/1249](https://github.com/HibiKier/zhenxun_bot/pull/1249)
### 2023/1/27
* 替换pixiv反向代理地址 [@pull/1244](https://github.com/HibiKier/zhenxun_bot/pull/1244)
### 2022/12/31
* 修复epic报错,优化简介 [@pull/1226](https://github.com/HibiKier/zhenxun_bot/pull/1226)
* 修复词条在某些回答下出错
* 原神黄历改为PIL
* 允许真寻自身触发命令,提供配置项 `self_message:STATUS`
### 2022/12/27 \[v0.1.6.6]
* 添加权限检查依赖注入
@@ -450,7 +565,7 @@ PS: **ARM平台** 请使用全量版 同时 **如果你的机器 RAM < 1G 可能
### 2022/9/27
* 更新b站转发解析 [@pull/1117](https://github.com/HibiKier/zhenxun_bot/pull/1117)
* 更新b站转发解析 [@pull/1117](https://github.com/HibiKier/zhenxun_bot/pull/1117)
### 2022/9/24
@@ -549,12 +664,14 @@ PS: **ARM平台** 请使用全量版 同时 **如果你的机器 RAM < 1G 可能
<br>
__..... 更多更新信息请查看文档__
**..... 更多更新信息请查看文档**
## Todo
- [x] web管理
* [x] web管理
## 感谢
[botuniverse / onebot](https://github.com/botuniverse/onebot) :超棒的机器人协议
[Mrs4s / go-cqhttp](https://github.com/Mrs4s/go-cqhttp) :cqhttp的golang实现,轻量、原生跨平台.
[nonebot / nonebot2](https://github.com/nonebot/nonebot2) :跨平台Python异步机器人框架
+1 -1
View File
@@ -1 +1 @@
__version__: v0.1.6.6
__version__: v0.1.6.7
+5 -2
View File
@@ -1,8 +1,8 @@
from pathlib import Path
from configs.config import Config
import nonebot
from configs.config import Config
Config.add_plugin_config(
"admin_bot_manage:custom_welcome_message",
@@ -11,6 +11,7 @@ Config.add_plugin_config(
name="群管理员操作",
help_="设置群欢迎消息权限",
default_value=2,
type=int,
)
Config.add_plugin_config(
@@ -19,6 +20,7 @@ Config.add_plugin_config(
2,
help_="开关群功能权限",
default_value=2,
type=int,
)
Config.add_plugin_config(
@@ -26,7 +28,8 @@ Config.add_plugin_config(
"ADMIN_DEFAULT_AUTH",
5,
help_="默认群管理员权限",
default_value=5
default_value=5,
type=int,
)
nonebot.load_plugins(str(Path(__file__).parent.resolve()))
+67 -64
View File
@@ -1,29 +1,29 @@
import asyncio
import os
import time
from datetime import datetime
from pathlib import Path
from typing import List
import ujson as json
from nonebot.adapters.onebot.v11.message import MessageSegment
from services.log import logger
from configs.config import Config
from configs.path_config import DATA_PATH, IMAGE_PATH
from models.group_member_info import GroupInfoUser
from models.level_user import LevelUser
from services.log import logger
from utils.http_utils import AsyncHttpx
from utils.image_utils import BuildImage
from utils.manager import group_manager, plugins2settings_manager, plugins_manager
from utils.message_builder import image
from utils.utils import get_bot, get_matchers
from pathlib import Path
from models.group_member_info import GroupInfoUser
from datetime import datetime
from services.db_context import db
from models.level_user import LevelUser
from configs.config import Config
from utils.manager import group_manager, plugins2settings_manager, plugins_manager
from utils.image_utils import BuildImage
from utils.http_utils import AsyncHttpx
import asyncio
import time
import os
import ujson as json
custom_welcome_msg_json = (
Path() / "data" / "custom_welcome_msg" / "custom_welcome_msg.json"
)
ICON_PATH = IMAGE_PATH / 'other'
ICON_PATH = IMAGE_PATH / "other"
async def group_current_status(group_id: int) -> str:
@@ -38,7 +38,9 @@ async def group_current_status(group_id: int) -> str:
for i, task in enumerate(_data):
name = _data[task]
name_image = BuildImage(0, 0, plain_text=f"{i+1}.{name}", font_size=20)
bk = BuildImage(name_image.w + 200, name_image.h + 20, color=(103, 177, 109), font_size=15)
bk = BuildImage(
name_image.w + 200, name_image.h + 20, color=(103, 177, 109), font_size=15
)
await bk.apaste(name_image, (10, 0), True, "by_height")
a_icon = BuildImage(40, 40, background=ICON_PATH / "btn_false.png")
if group_manager.check_group_task_status(group_id, task):
@@ -123,7 +125,7 @@ def change_global_task_status(cmd: str) -> str:
task_data = group_manager.get_task_data()
status = cmd[:2]
_cmd = cmd[4:]
if '全部被动' in cmd:
if "全部被动" in cmd:
for task in task_data:
if status == "开启":
group_manager.open_global_task(task)
@@ -134,7 +136,7 @@ def change_global_task_status(cmd: str) -> str:
else:
modules = [x for x in task_data if task_data[x].lower() == _cmd.lower()]
if not modules:
return '未查询到该被动任务'
return "未查询到该被动任务"
if status == "开启":
group_manager.open_global_task(modules[0])
else:
@@ -301,50 +303,53 @@ async def update_member_info(group_id: int, remind_superuser: bool = False) -> b
# try:
for user_info in _group_user_list:
nickname = user_info["card"] or user_info["nickname"]
async with db.transaction():
# 更新权限
if user_info["role"] in [
"owner",
"admin",
] and not await LevelUser.is_group_flag(user_info["user_id"], group_id):
await LevelUser.set_level(
user_info["user_id"],
user_info["group_id"],
Config.get_config("admin_bot_manage", "ADMIN_DEFAULT_AUTH"),
)
if str(user_info["user_id"]) in bot.config.superusers:
await LevelUser.set_level(
user_info["user_id"], user_info["group_id"], 9
)
user = await GroupInfoUser.get_member_info(
user_info["user_id"], user_info["group_id"]
)
if user:
if user.user_name != nickname:
await user.update(user_name=nickname).apply()
logger.info(
f"用户{user_info['user_id']} 所属{user_info['group_id']} 更新群昵称成功"
)
_exist_member_list.append(int(user_info["user_id"]))
continue
join_time = datetime.strptime(
time.strftime(
"%Y-%m-%d %H:%M:%S", time.localtime(user_info["join_time"])
),
"%Y-%m-%d %H:%M:%S",
)
if await GroupInfoUser.add_member_info(
# 更新权限
if user_info["role"] in [
"owner",
"admin",
] and not await LevelUser.is_group_flag(user_info["user_id"], group_id):
await LevelUser.set_level(
user_info["user_id"],
user_info["group_id"],
nickname,
join_time,
):
_exist_member_list.append(int(user_info["user_id"]))
logger.info(f"用户{user_info['user_id']} 所属{user_info['group_id']} 更新成功")
else:
_error_member_list.append(
f"用户{user_info['user_id']} 所属{user_info['group_id']} 更新失败\n"
Config.get_config("admin_bot_manage", "ADMIN_DEFAULT_AUTH"),
)
if str(user_info["user_id"]) in bot.config.superusers:
await LevelUser.set_level(user_info["user_id"], user_info["group_id"], 9)
user = await GroupInfoUser.get_or_none(
user_qq=user_info["user_id"], group_id=user_info["group_id"]
)
if user:
if user.user_name != nickname:
user.user_name = nickname
await user.save(update_fields=["user_name"])
logger.info(
f"用户{user_info['user_id']} 所属{user_info['group_id']} 更新群昵称成功"
)
_exist_member_list.append(int(user_info["user_id"]))
continue
join_time = datetime.strptime(
time.strftime("%Y-%m-%d %H:%M:%S", time.localtime(user_info["join_time"])),
"%Y-%m-%d %H:%M:%S",
)
user, _ = await GroupInfoUser.get_or_create(
user_qq=user_info["user_id"],
group_id=user_info["group_id"],
defaults={
"user_name": nickname,
},
)
# await GroupInfoUser.update_or_create(
# user_qq=user_info["user_id"],
# group_id=user_info["group_id"],
# defaults={
# "user_name": nickname,
# # "user_join_time": datetime.now().replace(tzinfo=None),
# },
# )
user.user_join_time = join_time
await user.save()
_exist_member_list.append(int(user_info["user_id"]))
logger.info("更新成功", "更新成员信息", user_info["user_id"], user_info["group_id"])
_del_member_list = list(
set(_exist_member_list).difference(
set(await GroupInfoUser.get_group_member_id_list(group_id))
@@ -352,10 +357,8 @@ async def update_member_info(group_id: int, remind_superuser: bool = False) -> b
)
if _del_member_list:
for del_user in _del_member_list:
if await GroupInfoUser.delete_member_info(del_user, group_id):
logger.info(f"退群用户{del_user} 所属{group_id} 已删除")
else:
logger.info(f"退群用户{del_user} 所属{group_id} 删除失败")
await GroupInfoUser.filter(user_qq=del_user, group_id=group_id).delete()
logger.info(f"退群用户{del_user} 所属{group_id} 已删除")
if _error_member_list and remind_superuser:
result = ""
for error_user in _error_member_list:
@@ -1,10 +1,10 @@
from nonebot import on_notice
from services.log import logger
from nonebot.adapters.onebot.v11 import GroupAdminNoticeEvent
from models.level_user import LevelUser
from models.group_member_info import GroupInfoUser
from configs.config import Config
from configs.config import Config
from models.group_member_info import GroupInfoUser
from models.level_user import LevelUser
from services.log import logger
__zx_plugin_name__ = "群管理员变动监测 [Hidden]"
__plugin_version__ = 0.1
@@ -16,11 +16,11 @@ admin_notice = on_notice(priority=5)
@admin_notice.handle()
async def _(event: GroupAdminNoticeEvent):
try:
nickname = (
await GroupInfoUser.get_member_info(event.user_id, event.group_id)
).user_name
except AttributeError:
if user := await GroupInfoUser.filter(
user_qq=event.user_id, group_id=event.group_id
).first():
nickname = user.nickname
else:
nickname = event.user_id
if event.sub_type == "set":
await LevelUser.set_level(
@@ -1,14 +1,14 @@
from typing import List
from configs.config import Config
from nonebot import on_command
from nonebot.adapters.onebot.v11 import GroupMessageEvent, Message
from nonebot.params import CommandArg
from ._data_source import custom_group_welcome
from nonebot.adapters.onebot.v11.permission import GROUP
from configs.config import Config
from nonebot.params import CommandArg
from services.log import logger
from utils.depends import ImageList
from ._data_source import custom_group_welcome
__zx_plugin_name__ = "自定义进群欢迎消息 [Admin]"
__plugin_usage__ = """
@@ -19,12 +19,14 @@ usage:
Note:可以通过[at]来确认是否艾特新成员
示例:自定义进群欢迎消息 欢迎你[at]
""".strip()
__plugin_des__ = '简易的自定义群欢迎消息'
__plugin_cmd__ = ['自定义群欢迎消息 ?[文本] ?[图片]']
__plugin_des__ = "简易的自定义群欢迎消息"
__plugin_cmd__ = ["自定义群欢迎消息 ?[文本] ?[图片]"]
__plugin_version__ = 0.1
__plugin_author__ = 'HibiKier'
__plugin_author__ = "HibiKier"
__plugin_settings__ = {
"admin_level": Config.get_config("admin_bot_manage", "SET_GROUP_WELCOME_MESSAGE_LEVEL"),
"admin_level": Config.get_config(
"admin_bot_manage", "SET_GROUP_WELCOME_MESSAGE_LEVEL"
),
}
custom_welcome = on_command(
@@ -37,7 +39,9 @@ custom_welcome = on_command(
@custom_welcome.handle()
async def _(event: GroupMessageEvent, arg: Message = CommandArg(), img: List[str] = ImageList()):
async def _(
event: GroupMessageEvent, arg: Message = CommandArg(), img: List[str] = ImageList()
):
msg = arg.extract_plain_text().strip()
if not msg and not img:
await custom_welcome.finish(__plugin_usage__)
+10 -9
View File
@@ -1,15 +1,16 @@
from utils.utils import scheduler, get_bot
from ._data_source import update_member_info
from services.log import logger
from models.group_info import GroupInfo
from asyncpg.exceptions import ConnectionDoesNotExistError, UndefinedColumnError
from models.group_info import GroupInfo
from services.log import logger
from utils.utils import get_bot, scheduler
__zx_plugin_name__ = '管理方面定时任务 [Hidden]'
__plugin_usage__ = '无'
__plugin_des__ = '成员信息和管理权限的定时更新'
from ._data_source import update_member_info
__zx_plugin_name__ = "管理方面定时任务 [Hidden]"
__plugin_usage__ = "无"
__plugin_des__ = "成员信息和管理权限的定时更新"
__plugin_version__ = 0.1
__plugin_author__ = 'HibiKier'
__plugin_author__ = "HibiKier"
# 自动更新群员信息
@@ -42,7 +43,7 @@ async def _():
if bot:
gl = await bot.get_group_list()
gl = [g["group_id"] for g in gl]
all_group = [x.group_id for x in await GroupInfo.get_all_group()]
all_group = [x.group_id for x in await GroupInfo.all()]
for g in gl:
if g not in all_group:
await update_member_info(g, False)
+64 -71
View File
@@ -1,43 +1,48 @@
from utils.message_builder import image
from utils.utils import scheduler, get_bot
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 import ActionFailed
from configs.config import NICKNAME, Config
from pathlib import Path
import shutil
from pathlib import Path
from typing import List
import nonebot
from nonebot import on_message
from nonebot.adapters.onebot.v11 import ActionFailed
from configs.config import NICKNAME, Config
from configs.path_config import IMAGE_PATH
from models.friend_user import FriendUser
from models.group_info import GroupInfo
from services.log import logger
from utils.message_builder import image
from utils.utils import broadcast_group, get_bot, scheduler
__zx_plugin_name__ = "定时任务相关 [Hidden]"
__plugin_version__ = 0.1
__plugin_author__ = "HibiKier"
__plugin_task__ = {'zwa': '早晚安'}
__plugin_task__ = {"zwa": "早晚安"}
Config.add_plugin_config(
"_task",
"DEFAULT_ZWA",
True,
help_="被动 早晚安 进群默认开关状态",
default_value=True,
"_task", "DEFAULT_ZWA", True, help_="被动 早晚安 进群默认开关状态", default_value=True, type=bool
)
Config.add_plugin_config(
"_backup",
"BACKUP_FLAG",
True,
help_="是否开启文件备份",
default_value=True
"_backup", "BACKUP_FLAG", True, help_="是否开启文件备份", default_value=True, type=bool
)
Config.add_plugin_config(
"_backup",
"BACKUP_DIR_OR_FILE",
['data/black_word', 'data/configs', 'data/statistics', 'data/word_bank', 'data/manager', 'configs'],
[
"data/black_word",
"data/configs",
"data/statistics",
"data/word_bank",
"data/manager",
"configs",
],
name="文件备份",
help_="备份的文件夹或文件",
default_value=[]
default_value=[],
type=List[str],
)
@@ -51,18 +56,9 @@ cx = on_message(priority=9999, block=False, rule=lambda: False)
minute=1,
)
async def _():
try:
bot = get_bot()
gl = await bot.get_group_list()
gl = [g["group_id"] for g in gl]
for g in gl:
result = image("zao.jpg", "zhenxun")
try:
await bot.send_group_msg(group_id=g, message="[[_task|zwa]]早上好" + result)
except ActionFailed:
logger.warning(f"{g} 发送早安失败")
except Exception as e:
logger.error(f"早晚安错误 e:{e}")
img = image(IMAGE_PATH / "zhenxun" / "zao.jpg")
await broadcast_group("[[_task|zwa]]早上好" + img, log_cmd="被动早晚安")
logger.info("每日早安发送")
# 睡觉了
@@ -72,20 +68,11 @@ async def _():
minute=59,
)
async def _():
try:
bot = get_bot()
gl = await bot.get_group_list()
gl = [g["group_id"] for g in gl]
for g in gl:
result = image("sleep.jpg", "zhenxun")
try:
await bot.send_group_msg(
group_id=g, message=f"[[_task|zwa]]{NICKNAME}要睡觉了,你们也要早点睡呀" + result
)
except ActionFailed:
logger.warning(f"{g} 发送晚安失败")
except Exception as e:
logger.error(f"早晚安错误 e:{e}")
img = image(IMAGE_PATH / "zhenxun" / "sleep.jpg")
await broadcast_group(
f"[[_task|zwa]]{NICKNAME}要睡觉了,你们也要早点睡呀" + img, log_cmd="被动早晚安"
)
logger.info("每日晚安发送")
# 自动更新群组信息
@@ -95,22 +82,27 @@ async def _():
minute=1,
)
async def _():
try:
bot = get_bot()
gl = await bot.get_group_list()
gl = [g["group_id"] for g in gl]
for g in gl:
group_info = await bot.get_group_info(group_id=g)
await GroupInfo.add_group_info(
group_info["group_id"],
group_info["group_name"],
group_info["max_member_count"],
group_info["member_count"],
1
)
logger.info(f"自动更新群组 {g} 信息成功")
except Exception as e:
logger.error(f"自动更新群组信息错误 e:{e}")
bots = nonebot.get_bots()
_used_group = []
for bot in bots.values():
try:
group_list = await bot.get_group_list()
gl = [g["group_id"] for g in group_list if g["group_id"] not in _used_group]
for g in gl:
_used_group.append(g)
group_info = await bot.get_group_info(group_id=g)
await GroupInfo.update_or_create(
group_id=group_info["group_id"],
defaults={
"group_name": group_info["group_name"],
"max_member_count": group_info["max_member_count"],
"member_count": group_info["member_count"],
"group_flag": 1,
},
)
logger.info(f"自动更新群组信息成功", group_id=g)
except Exception as e:
logger.error(f"Bot: {bot.self_id} 自动更新群组信息", e=e)
# 自动更新好友信息
@@ -124,10 +116,10 @@ async def _():
bot = get_bot()
fl = await bot.get_friend_list()
for f in fl:
if await FriendUser.add_friend_info(f["user_id"], f["nickname"]):
logger.info(f'自动更新好友 {f["user_id"]} 信息成功')
else:
logger.warning(f'自动更新好友 {f["user_id"]} 信息失败')
await FriendUser.create(user_id=f["user_id"], user_name=f["nickname"])
logger.info(f'自动更新好友 {f["user_id"]} 信息成功')
# else:
# logger.warning(f'自动更新好友 {f["user_id"]} 信息失败')
except Exception as e:
logger.error(f"自动更新群组信息错误 e:{e}")
@@ -140,7 +132,7 @@ async def _():
)
async def _():
if Config.get_config("_backup", "BACKUP_FLAG"):
_backup_path = Path() / 'backup'
_backup_path = Path() / "backup"
_backup_path.mkdir(exist_ok=True, parents=True)
for x in Config.get_config("_backup", "BACKUP_DIR_OR_FILE"):
try:
@@ -155,12 +147,13 @@ async def _():
if _p.exists():
_p.unlink()
shutil.copy(x, _p)
logger.info(f'已完成自动备份:{x}')
logger.info(f"已完成自动备份:{x}")
except Exception as e:
logger.error(f"自动备份文件 {x} 发生错误 {type(e)}:{e}")
# 一次性任务
# 固定时间触发,仅触发一次:
#
# from datetime import datetime
+72 -65
View File
@@ -1,16 +1,22 @@
from typing import Tuple, List
from typing import List
from nonebot import on_command
from nonebot.adapters.onebot.v11 import (
Bot,
GroupMessageEvent,
Message,
MessageEvent,
PrivateMessageEvent,
)
from nonebot.params import CommandArg
from nonebot.permission import SUPERUSER
from configs.config import NICKNAME, Config
from models.ban_user import BanUser
from models.level_user import LevelUser
from nonebot import on_command
from nonebot.adapters.onebot.v11 import (Bot, GroupMessageEvent, Message,
MessageEvent, PrivateMessageEvent)
from nonebot.params import Command, CommandArg
from nonebot.permission import SUPERUSER
from services.log import logger
from utils.depends import AtList
from utils.utils import get_message_at, is_number
from utils.depends import AtList, OneCommand
from utils.utils import is_number
from .data_source import a_ban, parse_ban_time
@@ -50,6 +56,7 @@ __plugin_configs__ = {
"value": 5,
"help": "ban/unban所需要的管理员权限等级",
"default_value": 5,
"type": int
}
}
@@ -68,46 +75,38 @@ super_ban = on_command("b了", permission=SUPERUSER, priority=5, block=True)
async def _(
bot: Bot,
event: GroupMessageEvent,
cmd: Tuple[str, ...] = Command(),
cmd: str = OneCommand(),
arg: Message = CommandArg(),
qq: List[int] = AtList()
at_list: List[int] = AtList(),
):
cmd = cmd[0]
result = ""
if qq:
qq = qq[0]
user_name = await bot.get_group_member_info(group_id=event.group_id, user_id=qq)
user_name = user_name["card"] or user_name["nickname"]
if at_list:
qq = at_list[0]
user = await bot.get_group_member_info(group_id=event.group_id, user_id=qq)
user_name = user["card"] or user["nickname"]
msg = arg.extract_plain_text().strip()
time = parse_ban_time(msg)
if isinstance(time, str):
await ban.finish(time, at_sender=True)
user_level = await LevelUser.get_user_level(event.user_id, event.group_id)
is_not_superuser = str(event.user_id) not in bot.config.superusers
if cmd in [".ban", "/ban"]:
if (
await LevelUser.get_user_level(event.user_id, event.group_id)
<= await LevelUser.get_user_level(qq, event.group_id)
and str(event.user_id) not in bot.config.superusers
):
at_user_level = await LevelUser.get_user_level(qq, event.group_id)
if user_level <= at_user_level and is_not_superuser:
await ban.finish(
f"您的权限等级比对方低或相等, {NICKNAME}不能为您使用此功能!",
at_sender=True,
)
logger.info(f"用户封禁 时长: {time}", cmd, event.user_id, event.group_id, qq)
result = await a_ban(qq, time, user_name, event)
else:
if (
await BanUser.check_ban_level(
qq, await LevelUser.get_user_level(event.user_id, event.group_id)
)
and str(event.user_id) not in bot.config.superusers
):
if await BanUser.check_ban_level(qq, user_level) and is_not_superuser:
await ban.finish(
f"ban掉 {user_name} 的管理员权限比您高,无法进行unban", at_sender=True
)
if await BanUser.unban(qq):
logger.info(
f"USER {event.user_id} GROUP {event.group_id} 将 USER {qq} 解禁"
)
result = f"已经把 {user_name} 从黑名单中删除了!"
logger.info(f"解除用户封禁", cmd, event.user_id, event.group_id, qq)
result = f"已经将 {user_name} 从黑名单中删除了!"
else:
result = f"{user_name} 不在黑名单!"
else:
@@ -119,54 +118,62 @@ async def _(
async def _(
bot: Bot,
event: PrivateMessageEvent,
cmd: Tuple[str, ...] = Command(),
cmd: str = OneCommand(),
arg: Message = CommandArg(),
):
cmd = cmd[0]
msg = arg.extract_plain_text().strip()
if msg:
if str(event.user_id) in bot.config.superusers:
msg_splt = msg.split()
if is_number(msg_splt[0]):
qq = int(msg_splt[0])
msg = msg_splt[1:]
if cmd in [".ban", "/ban"]:
time = parse_ban_time(" ".join(msg))
if isinstance(time, str):
await ban.finish(time)
result = await a_ban(qq, time, str(qq), event, 9)
else:
if await BanUser.unban(qq):
logger.info(f"USER {event.user_id} 将 USER {qq} 解禁")
result = f"已经把 {qq} 从黑名单中删除了!"
else:
result = f"{qq} 不在黑名单!"
await ban.send(result)
if msg and str(event.user_id) in bot.config.superusers:
msg_split = msg.split()
if msg_split and is_number(msg_split[0]):
qq = int(msg_split[0])
param = msg_split[1:]
if cmd in [".ban", "/ban"]:
time = parse_ban_time(" ".join(param))
if isinstance(time, str):
logger.info(time, cmd, event.user_id, target=qq)
await ban.finish(time)
result = await a_ban(qq, time, str(qq), event, 9)
else:
await ban.finish(
"qq号必须是数字!\n格式:.ban [qq] [hour]? [minute]?", at_sender=True
)
if await BanUser.unban(qq):
result = f"已经把 {qq} 从黑名单中删除了!"
else:
result = f"{qq} 不在黑名单!"
await ban.send(result)
logger.info(result, cmd, event.user_id, target=qq)
else:
await ban.send("参数不正确!\n格式:.ban [qq] [hour]? [minute]?", at_sender=True)
@super_ban.handle()
async def _(bot: Bot, event: MessageEvent, arg: Message = CommandArg()):
async def _(
bot: Bot,
event: MessageEvent,
cmd: str = OneCommand(),
arg: Message = CommandArg(),
at_list: List[int] = AtList(),
):
user_name = ""
qq = None
if isinstance(event, GroupMessageEvent):
qq = get_message_at(event.json())
if qq:
qq = qq[0]
if at_list:
qq = at_list[0]
user = await bot.get_group_member_info(group_id=event.group_id, user_id=qq)
user_name = user["card"] or user["nickname"]
else:
qq = arg.extract_plain_text().strip()
if not is_number(qq):
msg = arg.extract_plain_text().strip()
if not is_number(msg):
await super_ban.finish("对象qq必须为纯数字...")
qq = int(qq)
user_name = qq
qq = int(msg)
user_name = msg
if qq:
if not await BanUser.ban(qq, 10, 99999999):
await BanUser.unban(qq)
await BanUser.ban(qq, 10, 99999999)
await BanUser.ban(qq, 10, 99999999)
await ban.send(f"已将 {user_name} 拉入黑名单!")
logger.info(
f"已将 {user_name} 拉入黑名单!",
cmd,
event.user_id,
event.group_id if isinstance(event, GroupMessageEvent) else None,
qq,
)
else:
await super_ban.send("需要添加被super ban的对象,可以使用at或者指定qq..")
await super_ban.send("需要提供被super ban的对象,可以使用at或者指定qq...")
+41 -26
View File
@@ -1,10 +1,12 @@
from nonebot.adapters.onebot.v11 import MessageEvent, GroupMessageEvent
from typing import Optional, Union
from nonebot.adapters.onebot.v11 import GroupMessageEvent, MessageEvent
from configs.config import NICKNAME
from models.level_user import LevelUser
from utils.utils import is_number
from models.ban_user import BanUser
from models.level_user import LevelUser
from services.log import logger
from typing import Union
from utils.utils import is_number
def parse_ban_time(msg: str) -> Union[int, str]:
@@ -12,21 +14,31 @@ def parse_ban_time(msg: str) -> Union[int, str]:
解析ban时长
:param msg: 文本消息
"""
if not msg:
return -1
msg = msg.split()
if len(msg) == 1:
if not is_number(msg[0].strip()):
return "参数必须是数字!"
return int(msg[0]) * 60 * 60
else:
if not is_number(msg[0].strip()) or not is_number(msg[1].strip()):
return "参数必须是数字!"
return int(msg[0]) * 60 * 60 + int(msg[1]) * 60
try:
if not msg:
return -1
msg_split = msg.split()
if len(msg_split) == 1:
if not is_number(msg_split[0].strip()):
return "参数必须是数字!"
return int(msg_split[0]) * 60 * 60
else:
if not is_number(msg_split[0].strip()) or not is_number(
msg_split[1].strip()
):
return "参数必须是数字!"
return int(msg_split[0]) * 60 * 60 + int(msg_split[1]) * 60
except ValueError as e:
logger.error("解析ban时长错误", ".ban", e=e)
return "时长不可以带小数点!"
async def a_ban(
qq: int, time: int, user_name: str, event: MessageEvent, ban_level: int = None
qq: int,
time: int,
user_name: str,
event: MessageEvent,
ban_level: Optional[int] = None,
) -> str:
"""
ban
@@ -36,12 +48,15 @@ async def a_ban(
:param event: event
:param ban_level: ban级别
"""
group_id = None
if isinstance(event, GroupMessageEvent):
group_id = event.group_id
ban_level = await LevelUser.get_user_level(event.user_id, event.group_id)
if not ban_level:
return "未查询到ban级用户权限"
if await BanUser.ban(qq, ban_level, time):
logger.info(
f"USER {event.user_id} GROUP"
f" {event.group_id if isinstance(event, GroupMessageEvent) else ''} 将 USER {qq} 封禁 时长 {time / 60} 分钟"
f"将 [Target]({qq})封禁 时长 {time / 60} 分钟", ".ban", event.user_id, group_id
)
result = f"已经将 {user_name} 加入{NICKNAME}的黑名单了!"
if time != -1:
@@ -49,14 +64,14 @@ async def a_ban(
else:
result += f"将在 ∞ 分钟后解封"
else:
time = await BanUser.check_ban_time(qq)
if is_number(time):
time = abs(int(time))
if time < 60:
time = str(time) + " 秒"
ban_time = await BanUser.check_ban_time(qq)
if isinstance(ban_time, int):
ban_time = abs(float(ban_time))
if ban_time < 60:
ban_time = str(ban_time) + " 秒"
else:
time = str(int(time / 60)) + " 分钟"
ban_time = str(int(ban_time / 60)) + " 分钟"
else:
time += " 分钟"
result = f"{user_name} 已在黑名单!预计 {time}后解封"
ban_time += " 分钟"
result = f"{user_name} 已在黑名单!预计 {ban_time}后解封"
return result
+11 -9
View File
@@ -1,14 +1,15 @@
from nonebot.adapters.onebot.v11 import Bot, Message, MessageEvent
from nonebot import on_command
from nonebot.permission import SUPERUSER
from nonebot.params import CommandArg
from utils.utils import get_message_img
from services.log import logger
from utils.message_builder import image
from utils.manager import group_manager
from configs.config import Config
import asyncio
from nonebot import on_command
from nonebot.adapters.onebot.v11 import Bot, Message, MessageEvent
from nonebot.params import CommandArg
from nonebot.permission import SUPERUSER
from configs.config import Config
from services.log import logger
from utils.manager import group_manager
from utils.message_builder import image
from utils.utils import get_message_img
__zx_plugin_name__ = "广播 [Superuser]"
__plugin_usage__ = """
@@ -28,6 +29,7 @@ Config.add_plugin_config(
True,
help_="被动 广播 进群默认开关状态",
default_value=True,
type=bool,
)
broadcast = on_command("广播-", priority=1, permission=SUPERUSER, block=True)
+41 -8
View File
@@ -1,8 +1,11 @@
from configs.config import Config
from models.chat_history import ChatHistory
from nonebot import on_message
from nonebot.adapters.onebot.v11 import GroupMessageEvent, MessageEvent
from configs.config import Config
from models.chat_history import ChatHistory
from services.log import logger
from utils.depends import PlaintText
from utils.utils import scheduler
from ._rule import rule
@@ -12,21 +15,51 @@ __plugin_author__ = "HibiKier"
Config.add_plugin_config(
"chat_history", "FLAG", True, help_="是否开启消息自从存储", name="消息存储", default_value=True
"chat_history",
"FLAG",
True,
help_="是否开启消息自从存储",
name="消息存储",
default_value=True,
type=bool,
)
chat_history = on_message(rule=rule, priority=1, block=False)
TEMP_LIST = []
@chat_history.handle()
async def _(event: MessageEvent, msg: str = PlaintText()):
group_id = None
if isinstance(event, GroupMessageEvent):
await ChatHistory.add_chat_msg(
event.user_id, event.group_id, str(event.get_message()), msg
)
else:
await ChatHistory.add_chat_msg(event.user_id, None, str(event.get_message()), msg)
group_id = event.group_id
TEMP_LIST.append(
{
"user_qq": event.user_id,
"group_id": group_id,
"text": str(event.get_message()),
"plain_text": msg,
}
)
@scheduler.scheduled_job(
"interval",
minutes=1,
)
async def _():
try:
message_list = TEMP_LIST.copy()
TEMP_LIST.clear()
if message_list:
model_list = [ChatHistory(**x) for x in message_list]
await ChatHistory.bulk_create(model_list)
logger.debug(f"批量添加聊天记录 {len(message_list)} 条", "定时任务")
except Exception as e:
logger.error(f"定时批量添加聊天记录", "定时任务", e=e)
# @test.handle()
@@ -1,16 +1,16 @@
from datetime import datetime, timedelta
from typing import Any, Tuple
import pytz
from models.chat_history import ChatHistory
from models.group_member_info import GroupInfoUser
from nonebot import on_regex
from nonebot.adapters.onebot.v11 import GroupMessageEvent
from nonebot.params import RegexGroup
from utils.image_utils import BuildImage, text2image
from utils.utils import is_number
from utils.message_builder import image
from typing import Tuple, Any
from models.chat_history import ChatHistory
from models.group_member_info import GroupInfoUser
from utils.image_utils import BuildImage, text2image
from utils.message_builder import image
from utils.utils import is_number
__zx_plugin_name__ = "消息统计"
__plugin_usage__ = """
@@ -29,12 +29,7 @@ usage:
消息统计n=15
""".strip()
__plugin_des__ = "发言消息排行"
__plugin_cmd__ = [
"消息统计",
"周消息统计",
"月消息统计",
"日消息统计"
]
__plugin_cmd__ = ["消息统计", "周消息统计", "月消息统计", "日消息统计"]
__plugin_type__ = ("数据统计", 1)
__plugin_version__ = 0.1
__plugin_author__ = "HibiKier"
@@ -44,7 +39,9 @@ __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()
@@ -56,7 +53,9 @@ async def _(event: GroupMessageEvent, reg_group: Tuple[Any, ...] = RegexGroup())
if num and is_number(num) and 10 < int(num) < 50:
num = int(num)
time_now = datetime.now()
zero_today = time_now - timedelta(hours=time_now.hour, minutes=time_now.minute, seconds=time_now.second)
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 ["周"]:
@@ -70,9 +69,9 @@ async def _(event: GroupMessageEvent, reg_group: Tuple[Any, ...] = RegexGroup())
num_str = "发言次数:\n\n"
idx = 1
for uid, num in rank_data:
try:
user_name = (await GroupInfoUser.get_member_info(uid, gid)).user_name
except AttributeError:
if user := await GroupInfoUser.filter(user_qq=uid, group_id=gid).first():
user_name = user.user_name
else:
user_name = uid
name += f"\t{idx}.{user_name} \n\n"
num_str += f"\t{num}\n\n"
+66 -50
View File
@@ -1,26 +1,27 @@
from nonebot import on_notice, on_request
from configs.path_config import IMAGE_PATH, DATA_PATH
from models.level_user import LevelUser
from utils.message_builder import image
from models.group_member_info import GroupInfoUser
from datetime import datetime
from services.log import logger
from nonebot.adapters.onebot.v11 import (
Bot,
ActionFailed,
GroupIncreaseNoticeEvent,
GroupDecreaseNoticeEvent,
)
from utils.manager import group_manager, plugins2settings_manager, requests_manager
from configs.config import NICKNAME
from models.group_info import GroupInfo
from utils.utils import FreqLimiter
from configs.config import Config
from pathlib import Path
import random
import os
import ujson as json
import random
from datetime import datetime
from pathlib import Path
import ujson as json
from nonebot import on_notice, on_request
from nonebot.adapters.onebot.v11 import (
ActionFailed,
Bot,
GroupDecreaseNoticeEvent,
GroupIncreaseNoticeEvent,
)
from configs.config import NICKNAME, Config
from configs.path_config import DATA_PATH, IMAGE_PATH
from models.group_info import GroupInfo
from models.group_member_info import GroupInfoUser
from models.level_user import LevelUser
from services.log import logger
from utils.depends import GetConfig
from utils.manager import group_manager, plugins2settings_manager, requests_manager
from utils.message_builder import image
from utils.utils import FreqLimiter
__zx_plugin_name__ = "群事件处理 [Hidden]"
__plugin_version__ = 0.1
@@ -30,10 +31,10 @@ Config.add_plugin_config(
"invite_manager", "message", f"请不要未经同意就拉{NICKNAME}入群!告辞!", help_="强制拉群后进群回复的内容.."
)
Config.add_plugin_config(
"invite_manager", "flag", True, help_="被强制拉群后是否直接退出", default_value=True
"invite_manager", "flag", True, help_="被强制拉群后是否直接退出", default_value=True, type=bool
)
Config.add_plugin_config(
"invite_manager", "welcome_msg_cd", 5, help_="群欢迎消息cd", default_value=5
"invite_manager", "welcome_msg_cd", 5, help_="群欢迎消息cd", default_value=5, type=int
)
Config.add_plugin_config(
"_task",
@@ -41,6 +42,7 @@ Config.add_plugin_config(
True,
help_="被动 进群欢迎 进群默认开关状态",
default_value=True,
type=bool,
)
Config.add_plugin_config(
"_task",
@@ -48,10 +50,11 @@ Config.add_plugin_config(
True,
help_="被动 退群提醒 进群默认开关状态",
default_value=True,
type=bool,
)
_flmt = FreqLimiter(Config.get_config("invite_manager", "welcome_msg_cd"))
_flmt = FreqLimiter(Config.get_config("invite_manager", "welcome_msg_cd") or 5)
# 群员增加处理
@@ -65,7 +68,7 @@ add_group = on_request(priority=1, block=False)
@group_increase_handle.handle()
async def _(bot: Bot, event: GroupIncreaseNoticeEvent):
if event.user_id == int(bot.self_id):
group = await GroupInfo.get_group_info(event.group_id)
group = await GroupInfo.filter(group_id=event.group_id).first()
# 群聊不存在或被强制拉群,退出该群
if (not group or group.group_flag == 0) and Config.get_config(
"invite_manager", "flag"
@@ -115,15 +118,21 @@ async def _(bot: Bot, event: GroupIncreaseNoticeEvent):
user_info = await bot.get_group_member_info(
group_id=event.group_id, user_id=event.user_id
)
if await GroupInfoUser.add_member_info(
user_info["user_id"],
user_info["group_id"],
user_info["nickname"],
join_time,
):
logger.info(f"用户{user_info['user_id']} 所属{user_info['group_id']} 更新成功")
else:
logger.info(f"用户{user_info['user_id']} 所属{user_info['group_id']} 更新失败")
user, _ = await GroupInfoUser.get_or_create(
user_qq=user_info["user_id"],
group_id=user_info["group_id"],
defaults={
"user_name": user_info["nickname"],
},
)
user.user_join_time = join_time
await user.save()
# await GroupInfoUser.update_or_create(
# user_qq=user_info["user_id"],
# group_id=user_info["group_id"],
# defaults={"user_name": user_info["nickname"], "user_join_time": join_time},
# )
logger.info(f"用户{user_info['user_id']} 所属{user_info['group_id']} 更新成功")
# 群欢迎消息
if _flmt.check(event.group_id):
@@ -152,7 +161,11 @@ async def _(bot: Bot, event: GroupIncreaseNoticeEvent):
else:
await group_increase_handle.send(
"[[_task|group_welcome]]新人快跑啊!!本群现状↓(快使用自定义!)"
+ image(random.choice(os.listdir(IMAGE_PATH / "qxz")), "qxz")
+ image(
IMAGE_PATH
/ "qxz"
/ random.choice(os.listdir(IMAGE_PATH / "qxz"))
)
)
@@ -162,13 +175,13 @@ async def _(bot: Bot, event: GroupDecreaseNoticeEvent):
if event.sub_type == "kick_me":
group_id = event.group_id
operator_id = event.operator_id
try:
operator_name = (
await GroupInfoUser.get_member_info(event.operator_id, event.group_id)
).user_name
except AttributeError:
if user := await GroupInfoUser.get_or_none(
user_qq=event.operator_id, group_id=event.group_id
):
operator_name = user.user_name
else:
operator_name = "None"
group = await GroupInfo.get_group_info(group_id)
group = await GroupInfo.filter(group_id=group_id).first()
group_name = group.group_name if group else ""
coffee = int(list(bot.config.superusers)[0])
await bot.send_private_msg(
@@ -182,16 +195,19 @@ async def _(bot: Bot, event: GroupDecreaseNoticeEvent):
if event.user_id == int(bot.self_id):
group_manager.delete_group(event.group_id)
return
try:
user_name = (
await GroupInfoUser.get_member_info(event.user_id, event.group_id)
).user_name
except AttributeError:
user_name = str(event.user_id)
if await GroupInfoUser.delete_member_info(event.user_id, event.group_id):
logger.info(f"用户{user_name}, qq={event.user_id} 所属{event.group_id} 删除成功")
if user := await GroupInfoUser.get_or_none(
user_qq=event.user_id, group_id=event.group_id
):
user_name = user.user_name
else:
logger.info(f"用户{user_name}, qq={event.user_id} 所属{event.group_id} 删除失败")
user_name = f"{event.user_id}"
await GroupInfoUser.filter(user_qq=event.user_id, group_id=event.group_id).delete()
logger.info(
f"名称: {user_name} 退出群聊",
"group_decrease_handle",
event.user_id,
event.group_id,
)
rst = ""
if event.sub_type == "leave":
rst = f"{user_name}离开了我们..."
-1
View File
@@ -228,7 +228,6 @@ class HelpImageBuild:
for msg in [
"目前支持的功能列表:",
"可以通过 ‘帮助[功能名称]’ 来获取对应功能的使用方法",
"或者使用 ‘详细帮助’ 来获取所有功能方法",
]:
text = BuildImage(
0,
+7 -10
View File
@@ -1,13 +1,13 @@
from configs.config import Config
Config.add_plugin_config(
"hook",
"CHECK_NOTICE_INFO_CD",
300,
name="基础hook配置",
help_="群检测,个人权限检测等各种检测提示信息cd",
default_value=300
default_value=300,
type=int,
)
Config.add_plugin_config(
@@ -15,7 +15,8 @@ Config.add_plugin_config(
"MALICIOUS_BAN_TIME",
30,
help_="恶意命令触发检测触发后ban的时长(分钟)",
default_value=30
default_value=30,
type=int,
)
Config.add_plugin_config(
@@ -23,14 +24,10 @@ Config.add_plugin_config(
"MALICIOUS_CHECK_TIME",
5,
help_="恶意命令触发检测规定时间内(秒)",
default_value=5
default_value=5,
type=int,
)
Config.add_plugin_config(
"hook",
"MALICIOUS_BAN_COUNT",
6,
help_="恶意命令触发检测最大触发次数",
default_value=6
"hook", "MALICIOUS_BAN_COUNT", 6, help_="恶意命令触发检测最大触发次数", default_value=6, type=int
)
+39 -25
View File
@@ -1,39 +1,40 @@
import time
from typing import Optional
import nonebot
from nonebot.adapters.onebot.v11 import (
GroupMessageEvent,
PrivateMessageEvent,
Bot,
Event,
MessageEvent,
GroupMessageEvent,
Message,
MessageEvent,
PokeNotifyEvent,
PrivateMessageEvent,
)
from nonebot.exception import ActionFailed, IgnoredException
from nonebot.internal.matcher import Matcher
from configs.config import Config
from models.bag_user import BagUser
from models.ban_user import BanUser
from models.friend_user import FriendUser
from models.group_member_info import GroupInfoUser
from models.level_user import LevelUser
from models.user_shop_gold_log import UserShopGoldLog
from services.log import logger
from utils.decorator import Singleton
from utils.manager import (
plugins2block_manager,
StaticData,
plugins2settings_manager,
group_manager,
admin_manager,
plugins_manager,
group_manager,
plugins2block_manager,
plugins2cd_manager,
plugins2count_manager,
plugins2settings_manager,
plugins_manager,
)
from utils.message_builder import at
from utils.utils import FreqLimiter
from configs.config import Config
import time
ignore_rst_module = ["ai", "poke", "dialogue"]
@@ -97,11 +98,11 @@ async def send_msg(msg: str, bot: Bot, event: MessageEvent):
msg = msg.replace("[uname]", uname)
if "[nickname]" in msg:
if isinstance(event, GroupMessageEvent):
nickname = await GroupInfoUser.get_group_member_nickname(
nickname = await GroupInfoUser.get_user_nickname(
event.user_id, event.group_id
)
else:
nickname = await FriendUser.get_friend_nickname(event.user_id)
nickname = await FriendUser.get_user_nickname(event.user_id)
msg = msg.replace("[nickname]", nickname)
if "[at]" in msg and isinstance(event, GroupMessageEvent):
msg = msg.replace("[at]", str(at(event.user_id)))
@@ -142,16 +143,19 @@ class AuthChecker:
:param event: event
"""
try:
plugin_name = matcher.plugin_name
cost_gold = await self.auth_cost(plugin_name, bot, event)
if str(event.user_id) not in bot.config.superusers:
await self.auth_basic(plugin_name, bot, event)
self.auth_group(plugin_name, bot, event)
await self.auth_admin(plugin_name, matcher, bot, event)
await self.auth_plugin(plugin_name, matcher, bot, event)
await self.auth_limit(plugin_name, bot, event)
if cost_gold:
await BagUser.spend_gold(event.user_id, event.group_id, cost_gold)
if plugin_name := matcher.plugin_name:
cost_gold = await self.auth_cost(plugin_name, bot, event)
user_id = getattr(event, "user_id", None)
if user_id and str(user_id) not in bot.config.superusers:
await self.auth_basic(plugin_name, bot, event)
self.auth_group(plugin_name, bot, event)
await self.auth_admin(plugin_name, matcher, bot, event)
await self.auth_plugin(plugin_name, matcher, bot, event)
await self.auth_limit(plugin_name, bot, event)
if cost_gold:
await BagUser.spend_gold(
event.user_id, event.group_id, cost_gold
)
except IsSuperuserException:
return
@@ -221,7 +225,9 @@ class AuthChecker:
else:
plugins2count_manager.increase(plugin_name, count_type_)
async def auth_plugin(self, plugin_name: str, matcher: Matcher, bot: Bot, event: Event):
async def auth_plugin(
self, plugin_name: str, matcher: Matcher, bot: Bot, event: Event
):
"""
说明:
插件状态
@@ -345,6 +351,7 @@ class AuthChecker:
and plugin_name not in ignore_rst_module
):
self._flmt_c.start_cd(event.group_id)
logger.info(f"{event.user_id} ||XXXXXX: {matcher.module}")
await bot.send_group_msg(
group_id=event.group_id, message="此功能正在维护..."
)
@@ -364,7 +371,9 @@ class AuthChecker:
set_block_limit_false(event, plugin_name)
raise IgnoredException("此功能正在维护...")
async def auth_admin(self, plugin_name: str, matcher: Matcher, bot: Bot, event: Event):
async def auth_admin(
self, plugin_name: str, matcher: Matcher, bot: Bot, event: Event
):
"""
说明:
管理员命令 个人权限
@@ -504,8 +513,13 @@ class AuthChecker:
await BagUser.spend_gold(
event.user_id, event.group_id, psm.cost_gold
)
await UserShopGoldLog.add_shop_log(
event.user_id, event.group_id, 2, plugin_name, psm.cost_gold, 1
await UserShopGoldLog.create(
user_qq=event.user_id,
group_id=event.group_id,
type=2,
name=plugin_name,
num=1,
spend_gold=psm.cost_gold,
)
cost_gold = psm.cost_gold
return cost_gold
+10 -9
View File
@@ -1,20 +1,21 @@
from nonebot.matcher import Matcher
from nonebot.message import run_preprocessor, IgnoredException
from nonebot.typing import T_State
from nonebot.adapters.onebot.v11 import (
ActionFailed,
Bot,
Event,
MessageEvent,
ActionFailed,
PokeNotifyEvent,
GroupMessageEvent,
MessageEvent,
PokeNotifyEvent,
)
from nonebot.matcher import Matcher
from nonebot.message import IgnoredException, run_preprocessor
from nonebot.typing import T_State
from configs.config import Config
from models.ban_user import BanUser
from utils.utils import is_number, static_flmt, FreqLimiter
from utils.message_builder import at
from ._utils import ignore_rst_module, other_limit_plugins
from utils.utils import FreqLimiter, is_number, static_flmt
from ._utils import ignore_rst_module, other_limit_plugins
Config.add_plugin_config(
"hook",
@@ -49,7 +50,7 @@ async def _(matcher: Matcher, bot: Bot, event: Event, state: T_State):
and str(event.user_id) not in bot.config.superusers
):
time = await BanUser.check_ban_time(event.user_id)
if is_number(time):
if isinstance(time, int):
time = abs(int(time))
if time < 60:
time = str(time) + " 秒"
+3 -3
View File
@@ -28,12 +28,12 @@ async def _(matcher: Matcher, bot: Bot, event: GroupMessageEvent, state: T_State
if matcher.type == "message" and matcher.priority not in [1, 999]:
if state["_prefix"]["raw_command"]:
if _blmt.check(f'{event.user_id}{state["_prefix"]["raw_command"]}'):
if await BanUser.ban(
await BanUser.ban(
event.user_id,
9,
Config.get_config("hook", "MALICIOUS_BAN_TIME") * 60,
):
logger.info(f"USER {event.user_id} 触发了恶意触发检测")
)
logger.info(f"USER {event.user_id} 触发了恶意触发检测")
if isinstance(event, GroupMessageEvent):
try:
await bot.send_group_msg(
+13 -17
View File
@@ -1,23 +1,23 @@
import nonebot
from nonebot import Driver
from nonebot.adapters.onebot.v11 import Bot
from configs.path_config import DATA_PATH
from .init_group_manager import init_group_manager, group_manager
from services.log import logger
from .check_plugin_status import check_plugin_status
from .init import init
from .init_none_plugin_count_manager import init_none_plugin_count_manager
from .init_plugin_info import init_plugin_info
from .init_plugins_config import init_plugins_config
from .init_plugins_data import init_plugins_data, plugins_manager
from .init_none_plugin_count_manager import init_none_plugin_count_manager
from .init_plugins_resources import init_plugins_resources
from .init_plugins_settings import init_plugins_settings
from .init_plugin_info import init_plugin_info
from .init_plugins_limit import (
init_plugins_block_limit,
init_plugins_count_limit,
init_plugins_cd_limit,
init_plugins_count_limit,
)
from .init import init
from .check_plugin_status import check_plugin_status
from nonebot.adapters.onebot.v11 import Bot
from services.log import logger
from nonebot import Driver
import nonebot
from .init_plugins_resources import init_plugins_resources
from .init_plugins_settings import init_plugins_settings
__zx_plugin_name__ = "初始化插件数据 [Hidden]"
__plugin_version__ = 0.1
@@ -44,10 +44,6 @@ async def _():
init_plugins_config()
init_plugins_resources()
init_none_plugin_count_manager()
# x = group_manager.get_super_old_data()
# if x:
# for key in x.keys():
# plugins_manager.block_plugin(key, block_type=x[key])
if _flag:
raise Exception("首次运行,已在configs目录下生成配置文件config.yaml,修改后重启即可...")
logger.info("初始化数据完成...")
@@ -1,73 +0,0 @@
from pathlib import Path
from utils.manager import group_manager
from services.db_context import db
from asyncpg.exceptions import DuplicateColumnError
from services.log import logger
try:
import ujson as json
except ModuleNotFoundError:
import json
try:
from models.group_remind import GroupRemind
except ModuleNotFoundError:
pass
async def init_group_manager():
"""
旧数据格式替换为新格式
初始化数据
"""
old_group_level_file = Path() / "data" / "manager" / "group_level.json"
old_plugin_list_file = Path() / "data" / "manager" / "plugin_list.json"
if old_group_level_file.exists():
data = json.load(open(old_group_level_file, "r", encoding="utf8"))
for key in data.keys():
group = key
level = data[key]
group_manager.set_group_level(group, level)
old_group_level_file.unlink()
group_manager.save()
if old_plugin_list_file.exists():
data = json.load(open(old_plugin_list_file, "r", encoding="utf8"))
for plugin in data.keys():
for group in data[plugin].keys():
if group == "default" and not data[plugin]["default"]:
group_manager.block_plugin(plugin)
elif not data[plugin][group]:
group_manager.block_plugin(plugin, group)
old_plugin_list_file.unlink()
old_data_table = Path() / "models" / "group_remind.py"
try:
if old_data_table.exists():
b = {
"hy": "group_welcome",
"kxcz": "open_case_reset_remind",
"zwa": "zwa",
"blpar": "bilibili_parse",
"epic": "epic_free_game",
"pa": "pa",
"almanac": "genshin_alc",
}
for group in group_manager.get_data()["group_manager"]:
for remind in b:
try:
status = await GroupRemind.get_status(int(group), remind)
if status is not None:
if status:
await group_manager.open_group_task(group, b[remind])
logger.info(f"读取旧数据-->{group} 开启 {b[remind]}")
else:
await group_manager.close_group_task(group, b[remind])
logger.info(f"读取旧数据-->{group} 关闭 {b[remind]}")
except Exception as e:
pass
query = db.text("DROP TABLE group_reminds;")
await db.first(query)
old_data_table.unlink()
logger.info("旧数据读取完毕,删除了舍弃表 group_reminds...")
except (ModuleNotFoundError, DuplicateColumnError):
pass
group_manager.save()
@@ -1,14 +1,26 @@
import random
from types import ModuleType
from typing import Any
from typing import Any, Dict
from configs.config import Config
from services import logger
from utils.manager import (plugin_data_manager, plugins2block_manager,
plugins2cd_manager, plugins2count_manager,
plugins2settings_manager, plugins_manager)
from utils.manager.models import (Plugin, PluginBlock, PluginCd, PluginCount,
PluginData, PluginSetting, PluginType)
from utils.manager import (
plugin_data_manager,
plugins2block_manager,
plugins2cd_manager,
plugins2count_manager,
plugins2settings_manager,
plugins_manager,
)
from utils.manager.models import (
Plugin,
PluginBlock,
PluginCd,
PluginCount,
PluginData,
PluginSetting,
PluginType,
)
from utils.utils import get_matchers
@@ -21,104 +33,115 @@ def get_attr(module: ModuleType, name: str, default: Any = None) -> Any:
:param name: name
:param default: default
"""
return getattr(module, name) if hasattr(module, name) else default
return getattr(module, name, None) or default
def init_plugin_info():
for matcher in [x for x in get_matchers(True) if x.plugin]:
for matcher in [x for x in get_matchers(True)]:
try:
plugin = matcher.plugin
metadata = plugin.metadata
extra = metadata.extra if metadata else {}
if hasattr(plugin, "module"):
module = plugin.module
plugin_model = matcher.plugin_name
plugin_name = (
metadata.name
if metadata and metadata.name
else get_attr(module, "__zx_plugin_name__", matcher.plugin_name)
)
if not plugin_name:
logger.warning(f"配置文件 模块:{plugin_model} 获取 plugin_name 失败...")
continue
if "[Admin]" in plugin_name:
plugin_type = PluginType.ADMIN
plugin_name = plugin_name.replace("[Admin]", "").strip()
elif "[Hidden]" in plugin_name:
plugin_type = PluginType.HIDDEN
plugin_name = plugin_name.replace("[Hidden]", "").strip()
elif "[Superuser]" in plugin_name:
plugin_type = PluginType.SUPERUSER
plugin_name = plugin_name.replace("[Superuser]", "").strip()
else:
plugin_type = PluginType.NORMAL
plugin_usage = (
metadata.usage
if metadata and metadata.usage
else get_attr(module, "__plugin_usage__")
)
plugin_des = (
metadata.description
if metadata and metadata.description
else get_attr(module, "__plugin_des__")
)
menu_type = get_attr(module, "__plugin_type__") or ("normal",)
plugin_setting = get_attr(module, "__plugin_settings__")
if plugin_setting:
plugin_setting = PluginSetting(**plugin_setting)
plugin_setting.plugin_type = menu_type
plugin_superuser_usage = get_attr(module, "__plugin_super_usage__")
plugin_task = get_attr(module, "__plugin_task__")
plugin_version = extra.get("__plugin_version__") or get_attr(
module, "__plugin_version__"
)
plugin_author = extra.get("__plugin_author__") or get_attr(
module, "__plugin_author__"
)
plugin_cd = get_attr(module, "__plugin_cd_limit__")
if plugin_cd:
plugin_cd = PluginCd(**plugin_cd)
plugin_block = get_attr(module, "__plugin_block_limit__")
if plugin_block:
plugin_block = PluginBlock(**plugin_block)
plugin_count = get_attr(module, "__plugin_count_limit__")
if plugin_count:
plugin_count = PluginCount(**plugin_count)
plugin_resources = get_attr(module, "__plugin_resources__")
plugin_configs = get_attr(module, "__plugin_configs__")
if settings := plugins2settings_manager.get(plugin_model):
plugin_setting = settings
if plugin_cd_limit := plugins2cd_manager.get(plugin_model):
plugin_cd = plugin_cd_limit
if plugin_block_limit := plugins2block_manager.get(plugin_model):
plugin_block = plugin_block_limit
if plugin_count_limit := plugins2count_manager.get(plugin_model):
plugin_count = plugin_count_limit
if plugin_cfg := Config.get(plugin_model):
plugin_configs = plugin_cfg
plugin_status = plugins_manager.get(plugin_model)
if not plugin_status:
plugin_status = Plugin(plugin_name=plugin_model)
plugin_status.author = plugin_author
plugin_status.version = plugin_version
plugin_data = PluginData(
model=plugin_model,
name=plugin_name.strip(),
plugin_type=plugin_type,
usage=plugin_usage,
superuser_usage=plugin_superuser_usage,
des=plugin_des,
task=plugin_task,
menu_type=menu_type,
plugin_setting=plugin_setting,
plugin_cd=plugin_cd,
plugin_block=plugin_block,
plugin_count=plugin_count,
plugin_resources=plugin_resources,
plugin_configs=plugin_configs,
plugin_status=plugin_status,
)
plugin_data_manager.add_plugin_info(plugin_data)
if (plugin := matcher.plugin) and matcher.plugin_name:
metadata = plugin.metadata
extra = metadata.extra if metadata else {}
if hasattr(plugin, "module"):
module = plugin.module
plugin_model = matcher.plugin_name
plugin_name = (
metadata.name
if metadata and metadata.name
else get_attr(module, "__zx_plugin_name__", matcher.plugin_name)
)
if not plugin_name:
logger.warning(f"配置文件 模块:{plugin_model} 获取 plugin_name 失败...")
continue
if "[Admin]" in plugin_name:
plugin_type = PluginType.ADMIN
plugin_name = plugin_name.replace("[Admin]", "").strip()
elif "[Hidden]" in plugin_name:
plugin_type = PluginType.HIDDEN
plugin_name = plugin_name.replace("[Hidden]", "").strip()
elif "[Superuser]" in plugin_name:
plugin_type = PluginType.SUPERUSER
plugin_name = plugin_name.replace("[Superuser]", "").strip()
else:
plugin_type = PluginType.NORMAL
plugin_usage = (
metadata.usage
if metadata and metadata.usage
else get_attr(module, "__plugin_usage__")
)
plugin_des = (
metadata.description
if metadata and metadata.description
else get_attr(module, "__plugin_des__")
)
menu_type = get_attr(module, "__plugin_type__") or ("normal",)
plugin_setting = get_attr(module, "__plugin_settings__")
if plugin_setting:
plugin_setting = PluginSetting(**plugin_setting)
plugin_setting.plugin_type = menu_type
plugin_superuser_usage = get_attr(module, "__plugin_super_usage__")
plugin_task = get_attr(module, "__plugin_task__")
plugin_version = extra.get("__plugin_version__") or get_attr(
module, "__plugin_version__"
)
plugin_author = extra.get("__plugin_author__") or get_attr(
module, "__plugin_author__"
)
plugin_cd = get_attr(module, "__plugin_cd_limit__")
if plugin_cd:
plugin_cd = PluginCd(**plugin_cd)
plugin_block = get_attr(module, "__plugin_block_limit__")
if plugin_block:
plugin_block = PluginBlock(**plugin_block)
plugin_count = get_attr(module, "__plugin_count_limit__")
if plugin_count:
plugin_count = PluginCount(**plugin_count)
plugin_resources = get_attr(module, "__plugin_resources__")
plugin_configs = get_attr(module, "__plugin_configs__")
if settings := plugins2settings_manager.get(plugin_model):
plugin_setting = settings
if plugin_cd_limit := plugins2cd_manager.get(plugin_model):
plugin_cd = plugin_cd_limit
if plugin_block_limit := plugins2block_manager.get(plugin_model):
plugin_block = plugin_block_limit
if plugin_count_limit := plugins2count_manager.get(plugin_model):
plugin_count = plugin_count_limit
if plugin_cfg := Config.get(plugin_model):
if plugin_configs:
for config_name in plugin_configs:
config: Dict[str, Any] = plugin_configs[config_name] # type: ignore
Config.add_plugin_config(
plugin_model,
config_name,
config.get("value"),
help_=config.get("help"),
default_value=config.get("default_value"),
type=config.get("type"),
)
plugin_configs = plugin_cfg.configs
plugin_status = plugins_manager.get(plugin_model)
if not plugin_status:
plugin_status = Plugin(plugin_name=plugin_model)
plugin_status.author = plugin_author
plugin_status.version = plugin_version
plugin_data = PluginData(
model=plugin_model,
name=plugin_name.strip(),
plugin_type=plugin_type,
usage=plugin_usage,
superuser_usage=plugin_superuser_usage,
des=plugin_des,
task=plugin_task,
menu_type=menu_type,
plugin_setting=plugin_setting,
plugin_cd=plugin_cd,
plugin_block=plugin_block,
plugin_count=plugin_count,
plugin_resources=plugin_resources,
plugin_configs=plugin_configs, # type: ignore
plugin_status=plugin_status,
)
plugin_data_manager.add_plugin_info(plugin_data)
except Exception as e:
logger.error(f"构造插件数据失败 {matcher.plugin_name} - {type(e)}:{e}")
logger.error(f"构造插件数据失败 {matcher.plugin_name}", e=e)
@@ -1,13 +1,14 @@
from pathlib import Path
from ruamel.yaml import round_trip_load, round_trip_dump, YAML
from utils.manager import admin_manager, plugins_manager, plugin_data_manager
from ruamel import yaml
from ruamel.yaml import YAML, round_trip_dump, round_trip_load
from configs.config import Config
from configs.path_config import DATA_PATH
from services.log import logger
from utils.manager import admin_manager, plugin_data_manager, plugins_manager
from utils.text_utils import prompt2cn
from utils.utils import get_matchers
from configs.path_config import DATA_PATH
from ruamel import yaml
_yaml = YAML(typ="safe")
@@ -20,47 +21,60 @@ def init_plugins_config():
_data = Config.get_data()
# 优先使用 metadata 数据
for matcher in get_matchers(True):
if plugin_data := plugin_data_manager.get(matcher.plugin_name):
# 插件配置版本更新或为Version为None或不在存储配置内,当使用metadata时,必定更新
if plugin_data.plugin_configs and (
isinstance(plugin_data.plugin_status.version, str)
or (
plugin_data.plugin_status.version is None
if matcher.plugin_name:
if plugin_data := plugin_data_manager.get(matcher.plugin_name):
# 插件配置版本更新或为Version为None或不在存储配置内,当使用metadata时,必定更新
version = plugin_data.plugin_status.version
config = _data.get(matcher.plugin_name)
plugin = plugins_manager.get(matcher.plugin_name)
if plugin_data.plugin_configs and (
isinstance(version, str)
or (
_data.get(matcher.plugin_name)
and _data[matcher.plugin_name].keys()
!= plugin_data.plugin_configs.keys()
version is None
or (
config
and config.configs.keys()
!= plugin_data.plugin_configs.keys()
)
or version > int(plugin.version or 0)
or matcher.plugin_name not in _data.keys()
)
or plugin_data.plugin_status.version
> plugins_manager.get(matcher.plugin_name).version
or matcher.plugin_name not in _data.keys()
)
):
plugin_configs = plugin_data.plugin_configs
for key in plugin_configs:
if isinstance(plugin_data.plugin_configs[key], dict):
):
plugin_configs = plugin_data.plugin_configs
for key in plugin_configs:
if isinstance(plugin_data.plugin_configs[key], dict):
Config.add_plugin_config(
matcher.plugin_name,
key,
plugin_configs[key].get("value"),
help_=plugin_configs[key].get("help"),
default_value=plugin_configs[key].get("default_value"),
_override=True,
type=plugin_configs[key].get("type"),
)
else:
config = plugin_configs[key]
Config.add_plugin_config(
matcher.plugin_name,
key,
config.value,
name=config.name,
help_=config.help,
default_value=config.default_value,
_override=True,
type=config.type,
)
elif plugin_configs := _data.get(matcher.plugin_name):
for key in plugin_configs.configs:
Config.add_plugin_config(
matcher.plugin_name,
key,
plugin_configs[key].get("value"),
help_=plugin_configs[key].get("help"),
default_value=plugin_configs[key].get("default_value"),
plugin_configs.configs[key].value,
help_=plugin_configs.configs[key].help,
default_value=plugin_configs.configs[key].default_value,
_override=True,
type=plugin_configs.configs[key].type,
)
else:
Config.add_plugin_config(
matcher.plugin_name, key, plugin_configs[key]
)
elif plugin_configs := _data.get(matcher.plugin_name):
for key in plugin_configs:
Config.add_plugin_config(
matcher.plugin_name,
key,
plugin_configs[key]["value"],
help_=plugin_configs[key]["help"],
default_value=plugin_configs[key]["default_value"],
_override=True,
)
if not Config.is_empty():
Config.save()
_data = round_trip_load(open(plugins2config_file, encoding="utf8"))
@@ -96,7 +110,7 @@ def _replace_config():
# 数据替换
for plugin in Config.keys():
_tmp_data[plugin] = {}
for k in Config[plugin].keys():
for k in Config[plugin].configs.keys():
try:
if _data.get(plugin) and k in _data[plugin].keys():
Config.set_config(plugin, k, _data[plugin][k])
@@ -126,14 +140,14 @@ def _replace_config():
rst = ""
plugin_name = None
try:
plugin_data = Config.get(plugin)
for x in list(Config.get(plugin).keys()):
try:
_x = plugin_data[x].get("name")
if _x:
plugin_name = _x
except AttributeError:
pass
if config_group := Config.get(plugin):
for key in list(config_group.configs.keys()):
try:
if config := config_group.configs[key]:
if config.name:
plugin_name = config.name
except AttributeError:
pass
except (KeyError, AttributeError):
plugin_name = None
if not plugin_name:
@@ -149,7 +163,7 @@ def _replace_config():
)
rst += plugin_name + "\n"
for k in _data[plugin].keys():
rst += f'{k}: {Config[plugin][k]["help"]}' + "\n"
rst += f"{k}: {Config[plugin].configs[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)
@@ -9,13 +9,13 @@ def init_plugins_settings():
"""
初始化插件设置,从插件中获取 __zx_plugin_name__,__plugin_cmd__,__plugin_settings__
"""
for x in plugins2settings_manager.keys():
try:
_plugin = nonebot.plugin.get_plugin(x)
_module = _plugin.module
_module.__getattribute__("__zx_plugin_name__")
except (KeyError, AttributeError) as e:
logger.warning(f"配置文件 模块:{x} 获取 plugin_name 失败...{e}")
# for x in plugins2settings_manager.keys():
# try:
# _plugin = nonebot.plugin.get_plugin(x)
# _module = _plugin.module
# _module.__getattribute__("__zx_plugin_name__")
# except (KeyError, AttributeError) as e:
# logger.warning(f"配置文件 模块:{x} 获取 plugin_name 失败...{e}")
for matcher in get_matchers(True):
try:
if matcher.plugin_name not in plugins2settings_manager.keys():
@@ -27,6 +27,8 @@ def init_plugins_settings():
else:
if plugin_data := plugin_data_manager.get(matcher.plugin_name):
if plugin_settings := plugin_data.plugin_setting:
if (name := _module.__getattribute__("__zx_plugin_name__")) not in plugin_settings.cmd:
plugin_settings.cmd.append(name)
# 管理员命令
if plugin_data.plugin_type == PluginType.ADMIN:
admin_manager.add_admin_plugin_settings(
+108 -91
View File
@@ -1,127 +1,145 @@
from nonebot import on_request, on_message
import asyncio
import re
import time
from datetime import datetime
from nonebot import on_message, on_request
from nonebot.adapters.onebot.v11 import (
Bot,
ActionFailed,
Bot,
FriendRequestEvent,
GroupRequestEvent,
MessageEvent,
)
from models.friend_user import FriendUser
from datetime import datetime
from configs.config import NICKNAME, Config
from utils.manager import requests_manager
from models.friend_user import FriendUser
from models.group_info import GroupInfo
from services.log import logger
from utils.manager import requests_manager
from utils.utils import scheduler
import asyncio
import time
import re
from .utils import time_manager
__zx_plugin_name__ = "好友群聊处理请求 [Hidden]"
__plugin_version__ = 0.1
__plugin_author__ = "HibiKier"
__plugin_configs__ = {
"AUTO_ADD_FRIEND": {"value": False, "help": "是否自动同意好友添加", "default_value": False}
"AUTO_ADD_FRIEND": {
"value": False,
"help": "是否自动同意好友添加",
"default_value": False,
"type": bool,
}
}
friend_req = on_request(priority=5, block=True)
group_req = on_request(priority=5, block=True)
x = on_message(priority=999, block=False, rule=lambda: False)
exists_data = {"private": {}, "group": {}}
@friend_req.handle()
async def _(bot: Bot, event: FriendRequestEvent):
global exists_data
if exists_data["private"].get(event.user_id):
if time.time() - exists_data["private"][event.user_id] < 60 * 5:
return
exists_data["private"][event.user_id] = time.time()
user = await bot.get_stranger_info(user_id=event.user_id)
nickname = user["nickname"]
sex = user["sex"]
age = str(user["age"])
comment = event.comment
await bot.send_private_msg(
user_id=int(list(bot.config.superusers)[0]),
message=f"*****一份好友申请*****\n"
f"昵称:{nickname}({event.user_id})\n"
f"自动同意:{'√' if Config.get_config('invite_manager', 'AUTO_ADD_FRIEND') else '×'}\n"
f"日期:{str(datetime.now()).split('.')[0]}\n"
f"备注:{event.comment}",
)
if Config.get_config("invite_manager", "AUTO_ADD_FRIEND"):
await bot.set_friend_add_request(flag=event.flag, approve=True)
await FriendUser.add_friend_info(user["user_id"], user["nickname"])
else:
requests_manager.add_request(
event.user_id,
"private",
event.flag,
nickname=nickname,
sex=sex,
age=age,
comment=comment,
if time_manager.add_user_request(event.user_id):
logger.debug(f"收录 用户[{event.user_id}] 好友请求", "好友请求")
user = await bot.get_stranger_info(user_id=event.user_id)
nickname = user["nickname"]
sex = user["sex"]
age = str(user["age"])
comment = event.comment
await bot.send_private_msg(
user_id=int(list(bot.config.superusers)[0]),
message=f"*****一份好友申请*****\n"
f"昵称:{nickname}({event.user_id})\n"
f"自动同意:{'√' if Config.get_config('invite_manager', 'AUTO_ADD_FRIEND') else '×'}\n"
f"日期:{str(datetime.now()).split('.')[0]}\n"
f"备注:{event.comment}",
)
if Config.get_config("invite_manager", "AUTO_ADD_FRIEND"):
logger.debug(f"已开启好友请求自动同意,成功通过该请求", "好友请求", target=event.user_id)
await bot.set_friend_add_request(flag=event.flag, approve=True)
await FriendUser.create(user_id=user["user_id"], user_name=user["nickname"])
else:
requests_manager.add_request(
event.user_id,
"private",
event.flag,
nickname=nickname,
sex=sex,
age=age,
comment=comment,
)
else:
logger.debug(f"好友请求五分钟内重复, 已忽略", "好友请求", target=event.user_id)
@group_req.handle()
async def _(bot: Bot, event: GroupRequestEvent):
global exists_data
# 邀请
if event.sub_type == "invite":
if str(event.user_id) in bot.config.superusers:
try:
if await GroupInfo.get_group_info(event.group_id):
await GroupInfo.set_group_flag(event.group_id, 1)
else:
group_info = await bot.get_group_info(group_id=event.group_id)
await GroupInfo.add_group_info(
group_info["group_id"],
group_info["group_name"],
group_info["max_member_count"],
group_info["member_count"],
1,
)
logger.debug(
f"超级用户自动同意加入群聊", "群聊请求", event.user_id, target=event.group_id
)
await bot.set_group_add_request(
flag=event.flag, sub_type="invite", approve=True
)
except ActionFailed:
pass
group_info = await bot.get_group_info(group_id=event.group_id)
await GroupInfo.update_or_create(
group_id=group_info["group_id"],
defaults={
"group_name": group_info["group_name"],
"max_member_count": group_info["max_member_count"],
"member_count": group_info["member_count"],
"group_flag": 1,
},
)
except ActionFailed as e:
logger.error(
"超级用户自动同意加入群聊发生错误",
"群聊请求",
event.user_id,
target=event.group_id,
e=e,
)
else:
user = await bot.get_stranger_info(user_id=event.user_id)
sex = user["sex"]
age = str(user["age"])
if exists_data["group"].get(f"{event.user_id}:{event.group_id}"):
if (
time.time()
- exists_data["group"][f"{event.user_id}:{event.group_id}"]
< 60 * 5
):
return
exists_data["group"][f"{event.user_id}:{event.group_id}"] = time.time()
nickname = await FriendUser.get_user_name(event.user_id)
await bot.send_private_msg(
user_id=int(list(bot.config.superusers)[0]),
message=f"*****一份入群申请*****\n"
f"申请人:{nickname}({event.user_id})\n"
f"群聊:{event.group_id}\n"
f"邀请日期:{str(datetime.now()).split('.')[0]}",
)
await bot.send_private_msg(
user_id=event.user_id,
message=f"想要邀请我偷偷入群嘛~已经提醒{NICKNAME}的管理员大人了\n"
"请确保已经群主或群管理沟通过!\n"
"等待管理员处理吧!",
)
requests_manager.add_request(
event.user_id,
"group",
event.flag,
invite_group=event.group_id,
nickname=nickname,
sex=sex,
age=age,
)
if time_manager.add_group_request(event.user_id, event.group_id):
logger.debug(
f"收录 用户[{event.user_id}] 群聊[{event.group_id}] 群聊请求", "群聊请求"
)
user = await bot.get_stranger_info(user_id=event.user_id)
sex = user["sex"]
age = str(user["age"])
nickname = await FriendUser.get_user_name(event.user_id)
await bot.send_private_msg(
user_id=int(list(bot.config.superusers)[0]),
message=f"*****一份入群申请*****\n"
f"申请人:{nickname}({event.user_id})\n"
f"群聊:{event.group_id}\n"
f"邀请日期:{datetime.now().replace(microsecond=0)}",
)
await bot.send_private_msg(
user_id=event.user_id,
message=f"想要邀请我偷偷入群嘛~已经提醒{NICKNAME}的管理员大人了\n"
"请确保已经群主或群管理沟通过!\n"
"等待管理员处理吧!",
)
requests_manager.add_request(
event.user_id,
"group",
event.flag,
invite_group=event.group_id,
nickname=nickname,
sex=sex,
age=age,
)
else:
logger.debug(
f"群聊请求五分钟内重复, 已忽略",
"群聊请求",
target=f"{event.user_id}:{event.group_id}",
)
@x.handle()
@@ -145,5 +163,4 @@ async def _(event: MessageEvent):
minutes=5,
)
async def _():
global exists_data
exists_data = {"private": {}, "group": {}}
time_manager.clear()
+87
View File
@@ -0,0 +1,87 @@
import time
from dataclasses import dataclass
from typing import Dict
@dataclass
class PrivateRequest:
"""
好友请求
"""
user_id: int
time: float = time.time()
@dataclass
class GroupRequest:
"""
群聊请求
"""
user_id: int
group_id: int
time: float = time.time()
class RequestTimeManage:
"""
过滤五分钟以内的重复请求
"""
def __init__(self):
self._group: Dict[str, GroupRequest] = {}
self._user: Dict[int, PrivateRequest] = {}
def add_user_request(self, user_id: int) -> bool:
"""
添加请求时间
Args:
user_id (int): 用户id
Returns:
bool: 是否满足时间
"""
if user := self._user.get(user_id):
if time.time() - user.time < 60 * 5:
return False
self._user[user_id] = PrivateRequest(user_id)
return True
def add_group_request(self, user_id: int, group_id: int) -> bool:
"""
添加请求时间
Args:
user_id (int): 用户id
group_id (int): 邀请群聊
Returns:
bool: 是否满足时间
"""
key = f"{user_id}:{group_id}"
if group := self._group.get(key):
if time.time() - group.time < 60 * 5:
return False
self._group[key] = GroupRequest(user_id=user_id, group_id=group_id)
return True
def clear(self):
"""
清理过期五分钟请求
"""
now = time.time()
for user_id in self._user:
if now - self._user[user_id].time < 60 * 5:
del self._user[user_id]
for key in self._group:
if now - self._group[key].time < 60 * 5:
del self._group[key]
time_manager = RequestTimeManage()
+131 -131
View File
@@ -1,121 +1,163 @@
from nonebot.adapters.onebot.v11 import Bot, GroupMessageEvent, PrivateMessageEvent, Message, MessageEvent
from nonebot import on_command
from nonebot.typing import T_State
from nonebot.rule import to_me
from models.group_member_info import GroupInfoUser
from models.friend_user import FriendUser
from models.ban_user import BanUser
from services.log import logger
from configs.config import NICKNAME, Config
from nonebot.params import CommandArg
import random
from typing import Any, List, Tuple
from nonebot import on_command, on_regex
from nonebot.adapters.onebot.v11 import Bot, GroupMessageEvent, Message, MessageEvent
from nonebot.internal.matcher import Matcher
from nonebot.internal.params import Depends
from nonebot.params import CommandArg, RegexGroup
from nonebot.rule import to_me
from configs.config import NICKNAME
from models.ban_user import BanUser
from models.friend_user import FriendUser
from models.group_member_info import GroupInfoUser
from services.log import logger
from utils.depends import GetConfig
__zx_plugin_name__ = "昵称系统"
__plugin_usage__ = f"""
usage:
个人昵称系统,群聊 与 私聊 昵称相互独立
个人昵称,将替换真寻称呼你的名称,群聊 与 私聊 昵称相互独立,全局昵称设置将更改您目前所有群聊中及私聊的昵称
指令:
以后叫我 [昵称]
以后叫我 [昵称]: 设置当前群聊/私聊的昵称
全局昵称设置 [昵称]: 设置当前所有群聊和私聊的昵称
{NICKNAME}我是谁
""".strip()
__plugin_des__ = "区区昵称,才不想叫呢!"
__plugin_cmd__ = ["以后叫我 [昵称]", f"{NICKNAME}我是谁"]
__plugin_cmd__ = ["以后叫我 [昵称]", f"{NICKNAME}我是谁", "全局昵称设置 [昵称]"]
__plugin_version__ = 0.1
__plugin_author__ = "HibiKier"
__plugin_settings__ = {
"level": 5,
"default_status": True,
"limit_superuser": False,
"cmd": ["昵称", "昵称系统"],
"cmd": ["昵称"],
}
__plugin_configs__ = {
"BLACK_WORD": {
"value": ["爸", "爹", "爷", "父亲"],
"help": "昵称所屏蔽的关键词,会被替换为 *",
"default_value": None
"value": ["爸", "爹", "爷", "父"],
"help": "昵称所屏蔽的关键词,已设置的昵称会被替换为 *,未设置的昵称会在设置时提示",
"default_value": None,
"type": List[str],
}
}
nickname = on_command(
"nickname",
aliases={"以后叫我", "以后请叫我", "称呼我", "以后请称呼我", "以后称呼我", "叫我", "请叫我"},
nickname = on_regex(
"(?:以后)?(?:叫我|请叫我|称呼我)(.*)",
rule=to_me(),
priority=5,
block=True,
)
my_nickname = on_command(
"my_name", aliases={"我叫什么", "我是谁", "我的名字"}, rule=to_me(), priority=5, block=True
"我叫什么", aliases={"我是谁", "我的名字"}, rule=to_me(), priority=5, block=True
)
global_nickname = on_regex("设置全局昵称(.*)", rule=to_me(), priority=5, block=True)
cancel_nickname = on_command("取消昵称", rule=to_me(), priority=5, block=True)
@nickname.handle()
async def _(bot: Bot, event: MessageEvent, arg: Message = CommandArg()):
msg = arg.extract_plain_text().strip()
if not msg:
await nickname.finish("叫你空白?叫你虚空?叫你无名??", at_sender=True)
if len(msg) > 10:
await nickname.finish("昵称可不能超过10个字!", at_sender=True)
if msg in bot.config.superusers:
await nickname.finish("笨蛋!休想占用我的名字!#", at_sender=True)
_tmp = ""
black_word = Config.get_config("nickname", "BLACK_WORD")
if black_word:
for x in msg:
_tmp += "*" if x in black_word else x
msg = _tmp
def CheckNickname():
"""
说明:
检查名称是否合法
"""
async def dependency(
bot: Bot,
matcher: Matcher,
event: MessageEvent,
reg_group: Tuple[Any, ...] = RegexGroup(),
black_word: Any = GetConfig(config="BLACK_WORD"),
):
(msg,) = reg_group
logger.debug(f"昵称检查: {msg}", "昵称设置", event.user_id)
if not msg:
await matcher.finish("叫你空白?叫你虚空?叫你无名??", at_sender=True)
if str(event.user_id) in bot.config.superusers:
logger.debug(f"超级用户设置昵称, 跳过合法检测: {msg}", "昵称设置", event.user_id)
return
if len(msg) > 20:
await nickname.finish("昵称可不能超过20个字!", at_sender=True)
if msg in bot.config.nickname:
await nickname.finish("笨蛋!休想占用我的名字!#", at_sender=True)
if black_word:
for x in msg:
if x in black_word:
logger.debug("昵称设置禁止字符: [{x}]", "昵称设置", event.user_id)
await nickname.finish(f"字符 [{x}] 为禁止字符!", at_sender=True)
for word in black_word:
if word in msg:
logger.debug("昵称设置禁止字符: [{word}]", "昵称设置", event.user_id)
await nickname.finish(f"字符 [{word}] 为禁止字符!", at_sender=True)
return Depends(dependency)
@global_nickname.handle(parameterless=[CheckNickname()])
async def _(
event: MessageEvent,
reg_group: Tuple[Any, ...] = RegexGroup(),
):
(msg,) = reg_group
await FriendUser.set_user_nickname(event.user_id, msg)
await GroupInfoUser.filter(user_qq=event.user_id).update(nickname=msg)
logger.info(f"设置全局昵称成功: {msg}", "设置全局昵称", event.user_id)
await global_nickname.send(f"设置全局昵称成功!亲爱的{msg}")
@nickname.handle(parameterless=[CheckNickname()])
async def _(
event: MessageEvent,
reg_group: Tuple[Any, ...] = RegexGroup(),
):
(msg,) = reg_group
if isinstance(event, GroupMessageEvent):
if await GroupInfoUser.set_group_member_nickname(
event.user_id, event.group_id, msg
):
if len(msg) < 5:
if random.random() < 0.3:
msg = "~".join(msg)
await nickname.send(
random.choice(
[
f"好啦好啦,我知道啦,{msg},以后就这么叫你吧",
f"嗯嗯,{NICKNAME}记住你的昵称了哦,{msg}",
f"好突然,突然要叫你昵称什么的...{msg}..",
f"{NICKNAME}会好好记住{msg}的,放心吧",
f"好..好.,那窝以后就叫你{msg}了.",
]
)
await GroupInfoUser.set_user_nickname(event.user_id, event.group_id, msg)
if len(msg) < 5:
if random.random() < 0.3:
msg = "~".join(msg)
await nickname.send(
random.choice(
[
f"好啦好啦,我知道啦,{msg},以后就这么叫你吧",
f"嗯嗯,{NICKNAME}记住你的昵称了哦,{msg}",
f"好突然,突然要叫你昵称什么的...{msg}..",
f"{NICKNAME}会好好记住{msg}的,放心吧",
f"好..好.,那窝以后就叫你{msg}了.",
]
)
logger.info(f"USER {event.user_id} GROUP {event.group_id} 设置群昵称 {msg}")
else:
await nickname.send("设置昵称失败,请更新群组成员信息!", at_sender=True)
logger.warning(f"USER {event.user_id} GROUP {event.group_id} 设置群昵称 {msg} 失败")
)
logger.info(f"设置群昵称成功: {msg}", "昵称设置", event.user_id, event.group_id)
else:
if await FriendUser.set_friend_nickname(event.user_id, msg):
await nickname.send(
random.choice(
[
f"好啦好啦,我知道啦,{msg},以后就这么叫你吧",
f"嗯嗯,{NICKNAME}记住你的昵称了哦,{msg}",
f"好突然,突然要叫你昵称什么的...{msg}..",
f"{NICKNAME}会好好记住{msg}的,放心吧",
f"好..好.,那窝以后就叫你{msg}了.",
]
)
await FriendUser.set_user_nickname(event.user_id, msg)
await nickname.send(
random.choice(
[
f"好啦好啦,我知道啦,{msg},以后就这么叫你吧",
f"嗯嗯,{NICKNAME}记住你的昵称了哦,{msg}",
f"好突然,突然要叫你昵称什么的...{msg}..",
f"{NICKNAME}会好好记住{msg}的,放心吧",
f"好..好.,那窝以后就叫你{msg}了.",
]
)
logger.info(f"USER {event.user_id} 设置昵称 {msg}")
else:
await nickname.send("设置昵称失败了,明天再来试一试!或联系管理员更新好友!", at_sender=True)
logger.warning(f"USER {event.user_id} 设置昵称 {msg} 失败")
)
logger.info(f"设置私聊昵称成功: {msg}", "昵称设置", event.user_id)
@my_nickname.handle()
async def _(event: GroupMessageEvent):
try:
nickname_ = await GroupInfoUser.get_group_member_nickname(
event.user_id, event.group_id
)
except AttributeError:
nickname_ = ""
async def _(event: MessageEvent):
nickname_ = None
card = None
if isinstance(event, GroupMessageEvent):
nickname_ = await GroupInfoUser.get_user_nickname(event.user_id, event.group_id)
card = event.sender.card or event.sender.nickname
else:
nickname_ = await FriendUser.get_user_nickname(event.user_id)
card = event.sender.nickname
if nickname_:
await my_nickname.send(
random.choice(
@@ -130,44 +172,20 @@ async def _(event: GroupMessageEvent):
)
)
else:
nickname_ = event.sender.card or event.sender.nickname
await my_nickname.send(
random.choice(
["没..没有昵称嘛,{}", "啊,你是{}啊,我想叫你的昵称!", "是{}啊,有什么事吗?", "你是{}?"]
).format(nickname_)
)
@my_nickname.handle()
async def _(bot: Bot, event: PrivateMessageEvent, state: T_State):
nickname_ = await FriendUser.get_friend_nickname(event.user_id)
if nickname_:
await my_nickname.send(
random.choice(
[
f"我肯定记得你啊,你是{nickname_}啊",
f"我不会忘记你的,你也不要忘记我!{nickname_}",
f"哼哼,{NICKNAME}记忆力可是很好的,{nickname_}",
f"嗯?你是失忆了嘛...{nickname_}..",
f"不要小看{NICKNAME}的记忆力啊!笨蛋{nickname_}!QAQ",
f"哎?{nickname_}..怎么了吗..突然这样问..",
]
)
)
else:
nickname_ = (await bot.get_stranger_info(user_id=event.user_id))["nickname"]
await my_nickname.send(
random.choice(
["没..没有昵称嘛,{}", "啊,你是{}啊,我想叫你的昵称!", "是{}啊,有什么事吗?", "你是{}?"]
).format(nickname_)
).format(card)
)
@cancel_nickname.handle()
async def _(event: GroupMessageEvent):
nickname_ = await GroupInfoUser.get_group_member_nickname(
event.user_id, event.group_id
)
async def _(event: MessageEvent):
nickname_ = None
if isinstance(event, GroupMessageEvent):
nickname_ = await GroupInfoUser.get_user_nickname(event.user_id, event.group_id)
else:
nickname_ = await FriendUser.get_user_nickname(event.user_id)
if nickname_:
await cancel_nickname.send(
random.choice(
@@ -180,28 +198,10 @@ async def _(event: GroupMessageEvent):
]
)
)
await GroupInfoUser.set_group_member_nickname(event.user_id, event.group_id, "")
await BanUser.ban(event.user_id, 9, 60)
else:
await cancel_nickname.send("你在做梦吗?你没有昵称啊", at_sender=True)
@cancel_nickname.handle()
async def _(event: PrivateMessageEvent):
nickname_ = await FriendUser.get_friend_nickname(event.user_id)
if nickname_:
await cancel_nickname.send(
random.choice(
[
f"呜..{NICKNAME}睡一觉就会忘记的..和梦一样..{nickname_}",
f"窝知道了..{nickname_}..",
f"是{NICKNAME}哪里做的不好嘛..好吧..晚安{nickname_}",
f"呃,{nickname_},下次我绝对绝对绝对不会再忘记你!",
f"可..可恶!{nickname_}!太可恶了!呜",
]
)
)
await FriendUser.get_user_name(event.user_id)
if isinstance(event, GroupMessageEvent):
await GroupInfoUser.set_user_nickname(event.user_id, event.group_id, "")
else:
await FriendUser.set_user_nickname(event.user_id, "")
await BanUser.ban(event.user_id, 9, 60)
else:
await cancel_nickname.send("你在做梦吗?你没有昵称啊", at_sender=True)
+25 -18
View File
@@ -1,13 +1,17 @@
from nonebot import on_command, on_regex
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
from utils.message_builder import image
from nonebot.permission import SUPERUSER
from services.log import logger
from utils.message_builder import image
from .data_source import (
download_json,
install_plugin,
show_plugin_repo,
uninstall_plugin,
)
__zx_plugin_name__ = "插件商店 [Superuser]"
__plugin_usage__ = """
@@ -28,23 +32,29 @@ show_repo = on_regex("^查看插件仓库$", priority=1, block=True, permission=
update_repo = on_regex("^更新插件仓库$", priority=1, block=True, permission=SUPERUSER)
install_plugin_matcher = on_command("安装插件", 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)
uninstall_plugin_matcher = on_command(
"卸载插件", priority=1, block=True, permission=SUPERUSER
)
@install_plugin_matcher.handle()
async def _(bot: Bot, event: MessageEvent, arg: Message = CommandArg()):
arg = arg.extract_plain_text().strip()
msg = await install_plugin(arg)
name = arg.extract_plain_text().strip()
msg = await install_plugin(name)
await install_plugin_matcher.send(msg)
logger.info(f"安装插件: {name}", "安装插件", event.user_id)
@uninstall_plugin_matcher.handle()
async def _(bot: Bot, event: MessageEvent, arg: Message = CommandArg()):
arg = arg.extract_plain_text().strip()
msg = await uninstall_plugin(arg)
name = arg.extract_plain_text().strip()
msg = await uninstall_plugin(name)
await install_plugin_matcher.send(msg)
logger.info(f"卸载插件: {name}", "卸载插件", event.user_id)
@update_repo.handle()
@@ -53,16 +63,13 @@ async def _(bot: Bot, event: MessageEvent):
if code == 200:
await update_repo.finish("更新插件仓库信息成功!")
await update_repo.send("更新插件仓库信息失败!")
logger.info("更新插件仓库信息", "更新插件仓库信息", event.user_id)
@show_repo.handle()
async def _(bot: Bot, event: MessageEvent):
msg = await show_plugin_repo()
if isinstance(msg, int):
await show_repo.finish("文件下载失败或解压失败..")
await show_repo.send(image(b64=msg))
logger.info(
f"(USER {event.user_id}, GROUP {event.group_id if isinstance(event, GroupMessageEvent) else 'private'})"
f" 查看插件仓库"
)
await show_repo.send(image(msg))
logger.info("查看插件仓库", "查看插件仓库", event.user_id)
+17 -15
View File
@@ -2,15 +2,15 @@ import os
import shutil
import zipfile
from pathlib import Path
from typing import Union, Tuple
from utils.manager import plugins_manager
from typing import Tuple, Union
import ujson as json
from configs.path_config import DATA_PATH, TEMP_PATH
from services import logger
from utils.http_utils import AsyncHttpx
from configs.path_config import TEMP_PATH, DATA_PATH
from utils.image_utils import text2image, BuildImage
from utils.image_utils import BuildImage, text2image
from utils.manager import plugins_manager
from utils.utils import is_number
path = DATA_PATH / "plugin_shop"
@@ -48,7 +48,7 @@ async def install_plugin(name: str) -> str:
if zip_file.exists():
zip_file.unlink()
if await AsyncHttpx.download_file(url, zip_file):
logger.debug("开始解压插件压缩包...")
logger.debug("开始解压插件压缩包...", "安装插件", target=name)
# 解压
zf = zipfile.ZipFile(zip_file, "r")
extract_path = TEMP_PATH / f"{name}"
@@ -58,32 +58,34 @@ async def install_plugin(name: str) -> str:
for file in zf.namelist():
zf.extract(file, extract_path)
zf.close()
logger.debug("解压插件压缩包完成...")
logger.debug("开始移动插件文件夹...")
logger.debug("解压插件压缩包完成...", "安装插件", target=name)
logger.debug("开始移动插件文件夹...", "安装插件", target=name)
if (extensive_plugin_path / f"{name}").exists():
logger.debug("extensive_plugin目录下文件夹已存在,删除该目录插件文件夹...")
logger.debug(
"extensive_plugin目录下文件夹已存在,删除该目录插件文件夹...", "安装插件", target=name
)
shutil.rmtree(
(extensive_plugin_path / f"{name}").absolute(), ignore_errors=True
)
extract_path.rename(extensive_plugin_path / f"{name}")
tmp = ""
prompt = ""
if "pyproject.toml" in os.listdir(extensive_plugin_path / f"{name}"):
tmp = "检测到该插件含有额外依赖,当前安装无法保证依赖完全安装成功。"
prompt = "检测到该插件含有额外依赖,当前安装无法保证依赖完全安装成功。"
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 = "检测到该插件含有额外依赖,当前安装无法保证依赖完全安装成功。"
prompt = "检测到该插件含有额外依赖,当前安装无法保证依赖完全安装成功。"
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}")
logger.debug("移动插件文件夹完成...", "安装插件", target=name)
logger.info(f"成功安装插件 {name} 成功!\n{prompt}", "安装插件", target=name)
return f"成功安装插件 {name},请重启真寻!"
except Exception as e:
logger.error(f"安装插件 {name} 失败 {type(e)}:{e}")
logger.error(f"安装插失败", "安装插件", target=name, e=e)
return f"安装插件 {name} 失败 {type(e)}:{e}"
@@ -138,7 +140,7 @@ async def show_plugin_repo() -> Union[int, str]:
version = f"<f font_color=#1a7e30>[{plugins_data[key].version}]</f>"
s = (
f'id:{i+1}\n名称:{plugin_info[key]["plugin_name"]}'
f' \t\t{status}\n'
f" \t\t{status}\n"
f"模块:{key}\n"
f'作者:{plugin_info[key]["author"]}\n'
f'版本:{plugin_info[key]["version"]} \t\t{version}\n'
+138 -143
View File
@@ -1,23 +1,16 @@
import random
from asyncpg.exceptions import (
DuplicateColumnError,
UndefinedColumnError,
PostgresSyntaxError,
)
from nonebot import Driver
from services.db_context import db
from models.group_info import GroupInfo
from models.bag_user import BagUser
from nonebot.adapters.onebot.v11 import Bot
from services.log import logger
from configs.path_config import TEXT_PATH
from asyncio.exceptions import TimeoutError
from typing import List
from utils.http_utils import AsyncHttpx
from utils.utils import GDict
from utils.utils import scheduler
import nonebot
from nonebot.adapters.onebot.v11 import Bot
from nonebot.drivers import Driver
from configs.path_config import TEXT_PATH
from models.bag_user import BagUser
from models.group_info import GroupInfo
from services.log import logger
from utils.http_utils import AsyncHttpx
from utils.utils import GDict, scheduler
try:
import ujson as json
@@ -38,6 +31,7 @@ async def update_city():
data = {}
if not china_city.exists():
try:
logger.debug("开始更新城市列表...")
res = await AsyncHttpx.get(
"http://www.weather.com.cn/data/city3jdata/china.html", timeout=5
)
@@ -56,103 +50,97 @@ async def update_city():
with open(china_city, "w", encoding="utf8") as f:
json.dump(data, f, indent=4, ensure_ascii=False)
logger.info("自动更新城市列表完成.....")
except TimeoutError:
logger.warning("自动更新城市列表超时.....")
except ValueError:
logger.warning("自动城市列表失败.....")
except TimeoutError as e:
logger.warning("自动更新城市列表超时...", e=e)
except ValueError as e:
logger.warning("自动城市列表失败.....", e=e)
except Exception as e:
logger.error(f"自动城市列表未知错误 {type(e)}:{e}")
logger.error(f"自动城市列表未知错误", e=e)
@driver.on_startup
async def _():
"""
数据库表结构变换
"""
_flag = []
sql_str = [
(
"ALTER TABLE group_info ADD group_flag Integer NOT NULL DEFAULT 0;",
"group_info",
), # group_info表添加一个group_flag
(
"ALTER TABLE bag_users rename belonging_group To group_id;",
"bag_users",
), # 将 bag_users 的 belonging_group 改为 group_id
(
"ALTER TABLE group_info_users rename belonging_group To group_id;",
"group_info_users",
),
(
"ALTER TABLE sign_group_users rename belonging_group To group_id;",
"sign_group_users",
),
(
"ALTER TABLE open_cases_users rename belonging_group To group_id;",
"open_cases_users",
),
(
"ALTER TABLE bag_users ADD property json NOT NULL DEFAULT '{}';",
"bag_users",
), # bag_users 新增字段 property 替代 props
(
"ALTER TABLE genshin ADD auto_sign_time timestamp with time zone;",
"genshin"
), # 新增原神自动签到字段
(
"ALTER TABLE genshin ADD resin_remind boolean DEFAULT False;",
"genshin"
), # 新增原神自动签到字段
(
"ALTER TABLE genshin ADD resin_recovery_time timestamp with time zone;",
"genshin"
), # 新增原神自动签到字段
(
"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 + GDict.get('run_sql', []):
try:
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}")
_flag.append(flag)
except (DuplicateColumnError, UndefinedColumnError):
pass
except PostgresSyntaxError:
logger.error(f"语法错误:执行sql失败:{sql}")
# bag_user 将文本转为字典格式
await __database_script(_flag)
# @driver.on_startup
# async def _():
# """
# 数据库表结构变换
# """
# _flag = []
# sql_str = [
# (
# "ALTER TABLE group_info ADD group_flag Integer NOT NULL DEFAULT 0;",
# "group_info",
# ), # group_info表添加一个group_flag
# (
# "ALTER TABLE bag_users rename belonging_group To group_id;",
# "bag_users",
# ), # 将 bag_users 的 belonging_group 改为 group_id
# (
# "ALTER TABLE group_info_users rename belonging_group To group_id;",
# "group_info_users",
# ),
# (
# "ALTER TABLE sign_group_users rename belonging_group To group_id;",
# "sign_group_users",
# ),
# (
# "ALTER TABLE open_cases_users rename belonging_group To group_id;",
# "open_cases_users",
# ),
# (
# "ALTER TABLE bag_users ADD property json NOT NULL DEFAULT '{}';",
# "bag_users",
# ), # bag_users 新增字段 property 替代 props
# (
# "ALTER TABLE genshin ADD auto_sign_time timestamp with time zone;",
# "genshin",
# ), # 新增原神自动签到字段
# (
# "ALTER TABLE genshin ADD resin_remind boolean DEFAULT False;",
# "genshin",
# ), # 新增原神自动签到字段
# (
# "ALTER TABLE genshin ADD resin_recovery_time timestamp with time zone;",
# "genshin",
# ), # 新增原神自动签到字段
# ("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 + GDict.get("run_sql", []):
# try:
# 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}")
# _flag.append(flag)
# except (DuplicateColumnError, UndefinedColumnError):
# pass
# except PostgresSyntaxError:
# logger.error(f"语法错误:执行sql失败:{sql}")
# bag_user 将文本转为字典格式
# await __database_script(_flag)
@driver.on_bot_connect
@@ -161,51 +149,58 @@ async def _(bot: Bot):
版本某些需要的变换
"""
# 清空不存在的群聊信息,并将已所有已存在的群聊group_flag设置为1(认证所有已存在的群)
if not await GroupInfo.get_group_info(114514):
if not await GroupInfo.get_or_none(group_id=114514):
# 标识符,该功能只需执行一次
await GroupInfo.add_group_info(114514, "114514", 114514, 114514, 1)
await GroupInfo.create(
group_id=114514,
group_name="114514",
max_member_count=114514,
member_count=114514,
group_flag=1,
)
group_list = await bot.get_group_list()
group_list = [g["group_id"] for g in group_list]
_gl = [x.group_id for x in await GroupInfo.get_all_group()]
_gl = [x.group_id for x in await GroupInfo.all()]
if 114514 in _gl:
_gl.remove(114514)
for group_id in _gl:
if group_id in group_list:
if await GroupInfo.get_group_info(group_id):
await GroupInfo.set_group_flag(group_id, 1)
if group := await GroupInfo.get_or_none(group_id=group_id):
group.group_flag = 1
await group.save(update_fields=["group_flag"])
else:
group_info = await bot.get_group_info(group_id=group_id)
await GroupInfo.add_group_info(
group_info["group_id"],
group_info["group_name"],
group_info["max_member_count"],
group_info["member_count"],
1,
await GroupInfo.create(
group_id=group_info["group_id"],
group_name=group_info["group_name"],
max_member_count=group_info["max_member_count"],
member_count=group_info["member_count"],
group_flag=1,
)
logger.info(f"已将群聊 {group_id} 添加认证...")
logger.info(f"已添加群认证...", group_id=group_id)
else:
await GroupInfo.delete_group_info(group_id)
logger.info(f"移除不存在的群聊信息:{group_id}")
await GroupInfo.filter(group_id=group_id).delete()
logger.info(f"移除不存在的群聊信息", group_id=group_id)
async def __database_script(_flag: List[str]):
# bag_user 将文本转为字典格式
if "bag_users" in _flag:
for x in await BagUser.get_all_users():
props = {}
if x.props:
for prop in [p for p in x.props.split(",") if p]:
if props.get(prop):
props[prop] += 1
else:
props[prop] = 1
logger.info(
f"__database_script USER {x.user_qq} GROUP {x.group_id} 更新数据 {props}"
)
await x.update(
property=props,
props="",
).apply()
# async def __database_script(_flag: List[str]):
# # bag_user 将文本转为字典格式
# if "bag_users" in _flag:
# for x in await BagUser.get_all_users():
# props = {}
# if x.props:
# for prop in [p for p in x.props.split(",") if p]:
# if props.get(prop):
# props[prop] += 1
# else:
# props[prop] = 1
# logger.info(
# f"__database_script USER {x.user_qq} GROUP {x.group_id} 更新数据 {props}"
# )
# await x.update(
# property=props,
# props="",
# ).apply()
# 自动更新城市列表
+9 -5
View File
@@ -1,7 +1,10 @@
from configs.config import Config
from nonebot import Driver
from utils.decorator.shop import shop_register
from pathlib import Path
import nonebot
from nonebot.drivers import Driver
from configs.config import Config
from utils.decorator.shop import shop_register
driver: Driver = nonebot.get_driver()
@@ -11,11 +14,12 @@ Config.add_plugin_config(
"IMPORT_DEFAULT_SHOP_GOODS",
True,
help_="导入商店自带的三个商品",
default_value=True
default_value=True,
type=bool,
)
nonebot.load_plugins("basic_plugins/shop")
nonebot.load_plugins(str(Path(__file__).parent.resolve()))
@driver.on_bot_connect
+47 -47
View File
@@ -1,16 +1,15 @@
from nonebot import on_command
from models.user_shop_gold_log import UserShopGoldLog
from services.log import logger
from nonebot.adapters.onebot.v11 import GroupMessageEvent, Message
from nonebot.params import CommandArg
from utils.utils import is_number
from models.bag_user import BagUser
from services.db_context import db
from nonebot.adapters.onebot.v11.permission import GROUP
from models.goods_info import GoodsInfo
import time
from nonebot import on_command
from nonebot.adapters.onebot.v11 import GroupMessageEvent, Message
from nonebot.adapters.onebot.v11.permission import GROUP
from nonebot.params import CommandArg
from models.bag_user import BagUser
from models.goods_info import GoodsInfo
from models.user_shop_gold_log import UserShopGoldLog
from services.log import logger
from utils.utils import is_number
__zx_plugin_name__ = "商店 - 购买道具"
__plugin_usage__ = """
@@ -71,39 +70,40 @@ async def _(event: GroupMessageEvent, arg: Message = CommandArg()):
await buy.finish("请输入正确的商品名称!")
else:
await buy.finish("请输入正确的商品名称!", at_sender=True)
async with db.transaction():
if (
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,
)
logger.info(
f"USER {event.user_id} GROUP {event.group_id} "
f"花费 {goods.goods_price*num} 金币购买 {goods.goods_name} ×{num} 成功!"
)
await UserShopGoldLog.add_shop_log(
event.user_id,
event.group_id,
0,
goods.goods_name,
num,
goods.goods_price * num * goods.goods_discount,
)
else:
await buy.send(f"{goods.goods_name} 购买失败!", at_sender=True)
logger.info(
f"USER {event.user_id} GROUP {event.group_id} "
f"花费 {goods.goods_price * num * goods.goods_discount} 金币购买 {goods.goods_name} ×{num} 失败!"
)
if (
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)
spend_gold = int(goods.goods_discount * goods.goods_price * num)
await BagUser.spend_gold(event.user_id, event.group_id, spend_gold)
await BagUser.add_property(event.user_id, event.group_id, goods.goods_name, 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,
)
logger.info(
f"花费 {goods.goods_price*num} 金币购买 {goods.goods_name} ×{num} 成功!",
"购买道具",
event.user_id,
event.group_id,
)
await UserShopGoldLog.create(
user_qq=event.user_id,
group_id=event.group_id,
type=0,
name=goods.goods_name,
num=num,
spend_gold=goods.goods_price * num * goods.goods_discount,
)
# else:
# await buy.send(f"{goods.goods_name} 购买失败!", at_sender=True)
# logger.info(
# f"USER {event.user_id} GROUP {event.group_id} "
# f"花费 {goods.goods_price * num * goods.goods_discount} 金币购买 {goods.goods_name} ×{num} 失败!"
# )
+11 -6
View File
@@ -1,9 +1,10 @@
from nonebot import on_command
from nonebot.adapters.onebot.v11 import GroupMessageEvent, Message, ActionFailed
from nonebot.params import CommandArg
from nonebot.adapters.onebot.v11 import ActionFailed, GroupMessageEvent, Message
from nonebot.adapters.onebot.v11.permission import GROUP
from utils.data_utils import init_rank
from nonebot.params import CommandArg
from models.bag_user import BagUser
from utils.data_utils import init_rank
from utils.image_utils import text2image
from utils.message_builder import image
from utils.utils import is_number
@@ -39,7 +40,9 @@ async def _(event: GroupMessageEvent):
try:
await my_gold.send(msg)
except ActionFailed:
await my_gold.send(image(b64=(await text2image(msg, color="#f9f6f2", padding=10)).pic2bs4()))
await my_gold.send(
image(b64=(await text2image(msg, color="#f9f6f2", padding=10)).pic2bs4())
)
@gold_rank.handle()
@@ -49,9 +52,11 @@ async def _(event: GroupMessageEvent, arg: Message = CommandArg()):
num = int(num)
else:
num = 10
all_users = await BagUser.get_all_users(event.group_id)
all_users = await BagUser.filter(group_id=event.group_id)
all_user_id = [user.user_qq for user in all_users]
all_user_data = [user.gold for user in all_users]
rank_image = await init_rank("金币排行", all_user_id, all_user_data, event.group_id, num)
rank_image = await init_rank(
"金币排行", all_user_id, all_user_data, event.group_id, num
)
if rank_image:
await gold_rank.finish(image(b64=rank_image.pic2bs4()))
+6 -10
View File
@@ -1,12 +1,12 @@
from nonebot import on_command
from utils.message_builder import image
from ._data_source import create_bag_image
from services.log import logger
from nonebot.adapters.onebot.v11 import GroupMessageEvent
from models.bag_user import BagUser
from nonebot.adapters.onebot.v11.permission import GROUP
from models.bag_user import BagUser
from services.log import logger
from utils.message_builder import image
from ._data_source import create_bag_image
__zx_plugin_name__ = "商店 - 我的道具"
__plugin_usage__ = """
@@ -17,7 +17,7 @@ usage:
""".strip()
__plugin_des__ = "商店 - 我的道具"
__plugin_cmd__ = ["我的道具"]
__plugin_type__ = ('商店',)
__plugin_type__ = ("商店",)
__plugin_version__ = 0.1
__plugin_author__ = "HibiKier"
__plugin_settings__ = {
@@ -36,10 +36,6 @@ async def _(event: GroupMessageEvent):
props = await BagUser.get_property(event.user_id, event.group_id)
if props:
await my_props.send(image(b64=await create_bag_image(props)))
# rst = ""
# for i, p in enumerate(props.keys()):
# rst += f"{i+1}.{p}\t×{props[p]}\n"
# await my_props.send("\n" + rst[:-1], at_sender=True)
logger.info(f"USER {event.user_id} GROUP {event.group_id} 查看我的道具")
else:
await my_props.finish("您的背包里没有任何的道具噢~", at_sender=True)
-26
View File
@@ -1,26 +0,0 @@
from utils.utils import scheduler
from models.bag_user import BagUser
from services.log import logger
__zx_plugin_name__ = "每日金币重置 [Hidden]"
__plugin_version__ = 0.1
__plugin_author__ = "HibiKier"
# 重置每日金币
@scheduler.scheduled_job(
"cron",
hour=0,
minute=1,
)
async def _():
try:
user_list = await BagUser.get_all_users()
for user in user_list:
await user.update(
get_today_gold=0,
spend_today_gold=0,
).apply()
except Exception as e:
logger.error(f"重置每日金币错误 e:{e}")
+44 -21
View File
@@ -1,14 +1,24 @@
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, scheduler
from nonebot.params import CommandArg
from services.log import logger
import os
from nonebot import on_command
from nonebot.adapters.onebot.v11 import Message, MessageEvent
from nonebot.params import CommandArg
from nonebot.permission import SUPERUSER
from configs.path_config import IMAGE_PATH
from models.bag_user import BagUser
from services.log import logger
from utils.message_builder import image
from utils.utils import is_number, scheduler
from .data_source import (
GoodsInfo,
create_shop_help,
delete_goods,
parse_goods_info,
register_goods,
update_goods,
)
__zx_plugin_name__ = "商店"
__plugin_usage__ = """
@@ -37,7 +47,7 @@ __plugin_cmd__ = [
"删除商品 [名称或序号] [_superuser]",
"修改商品 name:[名称或序号] price:[价格] des:[描述] discount:[折扣] limit_time:[限时] [_superuser]",
]
__plugin_type__ = ('商店',)
__plugin_type__ = ("商店",)
__plugin_version__ = 0.1
__plugin_author__ = "HibiKier"
__plugin_settings__ = {
@@ -46,9 +56,7 @@ __plugin_settings__ = {
"limit_superuser": False,
"cmd": ["商店"],
}
__plugin_block_limit__ = {
"limit_type": "group"
}
__plugin_block_limit__ = {"limit_type": "group"}
shop_help = on_command("商店", priority=5, block=True)
@@ -75,12 +83,15 @@ async def _(event: MessageEvent, arg: Message = CommandArg()):
if not data.get("name") or not data.get("price") or not data.get("des"):
await shop_add_goods.finish("name:price:des 参数不可缺少!")
if await register_goods(**data):
await shop_add_goods.send(f"添加商品 {data['name']} 成功!\n"
f"名称:{data['name']}\n"
f"价格:{data['price']}金币\n"
f"简介:{data['des']}\n"
f"折扣:{data.get('discount')}\n"
f"限时:{data.get('limit_time')}", at_sender=True)
await shop_add_goods.send(
f"添加商品 {data['name']} 成功!\n"
f"名称:{data['name']}\n"
f"价格:{data['price']}金币\n"
f"简介:{data['des']}\n"
f"折扣:{data.get('discount')}\n"
f"限时:{data.get('limit_time')}",
at_sender=True,
)
logger.info(f"USER {event.user_id} 添加商品 {msg} 成功")
else:
await shop_add_goods.send(f"添加商品 {msg} 失败了...", at_sender=True)
@@ -133,7 +144,19 @@ async def _(event: MessageEvent, arg: Message = CommandArg()):
)
async def _():
try:
await GoodsInfo.reset_daily_purchase()
await GoodsInfo.all().update(daily_purchase_limit={})
logger.info("商品每日限购次数重置成功...")
except Exception as e:
logger.error(f"商品每日限购次数重置发生错误 {type(e)}:{e}")
logger.error(f"商品每日限购次数重置出错 {type(e)}:{e}")
@scheduler.scheduled_job(
"cron",
hour=0,
minute=1,
)
async def _():
try:
await BagUser.all().update(get_today_gold=0, spend_today_gold=0)
except Exception as e:
logger.error(f"重置每日金币", "定时任务", e=e)
+108 -46
View File
@@ -1,18 +1,18 @@
import time
from typing import Optional, Tuple, Union
from PIL import Image
from configs.path_config import IMAGE_PATH
from models.goods_info import GoodsInfo
from utils.image_utils import BuildImage, text2image
from utils.utils import is_number
from configs.path_config import IMAGE_PATH
from typing import Optional, Union, Tuple
from utils.utils import GDict
import time
from utils.utils import GDict, is_number
icon_path = IMAGE_PATH / 'shop_icon'
icon_path = IMAGE_PATH / "shop_icon"
GDict['run_sql'].append("ALTER TABLE goods_info ADD is_passive boolean DEFAULT False;")
GDict['run_sql'].append("ALTER TABLE goods_info ADD icon VARCHAR(255);")
GDict["run_sql"].append("ALTER TABLE goods_info ADD is_passive boolean DEFAULT False;")
GDict["run_sql"].append("ALTER TABLE goods_info ADD icon VARCHAR(255);")
# 创建商店界面
@@ -43,17 +43,31 @@ async def create_shop_help() -> str:
await name_image.atext((390, 0), "售价:", 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
old_price_image = BuildImage(
0,
0,
plain_text=str(goods.goods_price),
font_color=(194, 194, 194),
font="CJGaoDeGuo.otf",
font_size=15,
)
await name_image.atext(
(440, 15), str(discount_price), (255, 255, 255)
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"
(440, 0),
str(goods.goods_price),
(255, 255, 255),
center_type="by_height",
)
await name_image.atext(
(
@@ -68,38 +82,48 @@ async def create_shop_help() -> str:
font_img = BuildImage(
600, 80, font_size=20, color="#a29ad6", font="CJGaoDeGuo.otf"
)
p = font_img.getsize('简介:')[0] + 20
p = font_img.getsize("简介:")[0] + 20
if goods.goods_description:
des_list = goods.goods_description.split('\n')
desc = ''
des_list = goods.goods_description.split("\n")
desc = ""
for des in des_list:
if font_img.getsize(des)[0] > font_img.w - p - 20:
msg = ''
tmp = ''
msg = ""
tmp = ""
for i in range(len(des)):
if font_img.getsize(tmp)[0] < font_img.w - p - 20:
tmp += des[i]
else:
msg += tmp + '\n'
msg += tmp + "\n"
tmp = des[i]
desc += msg
if tmp:
desc += tmp
else:
desc += des + '\n'
if desc[-1] == '\n':
desc += des + "\n"
if desc[-1] == "\n":
desc = desc[:-1]
des_image = await text2image(desc, color="#a29ad6")
goods_image = BuildImage(
600, (50 + des_image.h) if des_image else 50, font_size=20, color="#a29ad6", font="CJGaoDeGuo.otf"
600,
(50 + des_image.h) if des_image else 50,
font_size=20,
color="#a29ad6",
font="CJGaoDeGuo.otf",
)
if des_image:
await goods_image.atext((15, 50), '简介:')
await goods_image.atext((15, 50), "简介:")
await goods_image.apaste(des_image, (p, 50))
await name_image.acircle_corner(5)
await goods_image.apaste(name_image, (0, 5), True, center_type="by_width")
await goods_image.acircle_corner(20)
bk = BuildImage(1180, (50 + des_image.h) if des_image else 50, font_size=15, color="#f9f6f2", font="CJGaoDeGuo.otf")
bk = BuildImage(
1180,
(50 + des_image.h) if des_image else 50,
font_size=15,
color="#f9f6f2",
font="CJGaoDeGuo.otf",
)
if goods.icon and (icon_path / goods.icon).exists():
icon = BuildImage(70, 70, background=icon_path / goods.icon)
await bk.apaste(icon)
@@ -129,7 +153,9 @@ async def create_shop_help() -> str:
_w += 140
if goods.goods_discount != 1:
n += 140
_discount_logo = BuildImage(30, 30, background=f"{IMAGE_PATH}/other/discount.png")
_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"),
@@ -137,14 +163,23 @@ async def create_shop_help() -> str:
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)),
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")
_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"),
@@ -152,7 +187,13 @@ async def create_shop_help() -> str:
True,
)
await bk.apaste(
BuildImage(0, 0, plain_text=f"{goods.daily_limit}", font_size=30, font="CJGaoDeGuo.otf"),
BuildImage(
0,
0,
plain_text=f"{goods.daily_limit}",
font_size=30,
font="CJGaoDeGuo.otf",
),
(_w + 72, 45),
True,
)
@@ -220,22 +261,30 @@ async def register_goods(
:param icon: 图标
:return: 是否添加成功
"""
if not await GoodsInfo.get_goods_info(name):
limit_time = float(limit_time) if limit_time else limit_time
if not await GoodsInfo.get_or_none(goods_name=name):
limit_time_ = float(limit_time) if limit_time else limit_time
discount = discount if discount is not None else 1
limit_time = (
int(time.time() + limit_time * 60 * 60)
if limit_time is not None and limit_time != 0
limit_time_ = (
int(time.time() + limit_time_ * 60 * 60)
if limit_time_ is not None and limit_time_ != 0
else 0
)
return await GoodsInfo.add_goods(
name, int(price), des, float(discount), limit_time, daily_limit, is_passive, icon
await GoodsInfo.create(
goods_name=name,
goods_price=int(price),
goods_description=des,
goods_discount=float(discount),
goods_limit_time=limit_time_,
daily_limit=daily_limit,
is_passive=is_passive,
icon=icon,
)
return True
return False
# 删除商品
async def delete_goods(name: str, id_: int) -> "str, str, int":
async def delete_goods(name: str, id_: int) -> Tuple[str, str, int]:
"""
删除商品
:param name: 商品名称
@@ -256,6 +305,7 @@ async def delete_goods(name: str, id_: int) -> "str, str, int":
return f"删除商品 {name} 成功!", name, 200
else:
return f"删除商品 {name} 失败!", name, 999
return "获取商品失败", "", 999
# 更新商品信息
@@ -272,7 +322,7 @@ async def update_goods(**kwargs) -> Tuple[bool, str, str]:
return False, "序号错误,没有该序号的商品...", ""
goods = goods_lst[int(kwargs["name"]) - 1]
else:
goods = await GoodsInfo.get_goods_info(kwargs["name"])
goods = await GoodsInfo.filter(goods_name=kwargs["name"]).first()
if not goods:
return False, "名称错误,没有该名称的商品...", ""
name: str = goods.goods_name
@@ -295,14 +345,22 @@ async def update_goods(**kwargs) -> Tuple[bool, str, str]:
discount = kwargs["discount"]
if kwargs.get("limit_time"):
kwargs["limit_time"] = float(kwargs["limit_time"])
new_time = time.strftime(
"%Y-%m-%d %H:%M:%S",
time.localtime(time.time() + kwargs["limit_time"] * 60 * 60),
) if kwargs["limit_time"] != 0 else 0
new_time = (
time.strftime(
"%Y-%m-%d %H:%M:%S",
time.localtime(time.time() + kwargs["limit_time"] * 60 * 60),
)
if kwargs["limit_time"] != 0
else 0
)
tmp += f"限时至: {new_time}\n" if new_time else "取消了限时\n"
limit_time = kwargs["limit_time"]
if kwargs.get("daily_limit"):
tmp += f'每日购买限制:{daily_limit} --> {kwargs["daily_limit"]}\n' if daily_limit else "取消了购买限制\n"
tmp += (
f'每日购买限制:{daily_limit} --> {kwargs["daily_limit"]}\n'
if daily_limit
else "取消了购买限制\n"
)
daily_limit = int(kwargs["daily_limit"])
if kwargs.get("is_passive"):
tmp += f'被动道具:{is_passive} --> {kwargs["is_passive"]}\n'
@@ -318,9 +376,13 @@ async def update_goods(**kwargs) -> Tuple[bool, str, str]:
else 0
),
daily_limit,
is_passive
is_passive,
)
return (
True,
name,
tmp[:-1],
)
return True, name, tmp[:-1],
def parse_goods_info(msg: str) -> Union[dict, str]:
+25 -15
View File
@@ -1,19 +1,17 @@
from typing import Any, Tuple
from nonebot import on_command, on_regex
from models.user_shop_gold_log import UserShopGoldLog
from services.log import logger
from nonebot.adapters.onebot.v11 import Bot, GroupMessageEvent, Message
from nonebot.adapters.onebot.v11.permission import GROUP
from nonebot.params import CommandArg, RegexGroup
from models.bag_user import BagUser
from models.user_shop_gold_log import UserShopGoldLog
from services.log import logger
from utils.decorator.shop import NotMeetUseConditionsException
from utils.utils import is_number
from models.bag_user import BagUser
from nonebot.adapters.onebot.v11.permission import GROUP
from services.db_context import db
from .data_source import effect, register_use, func_manager, build_params
from .data_source import build_params, effect, func_manager, register_use
__zx_plugin_name__ = "商店 - 使用道具"
__plugin_usage__ = """
@@ -75,17 +73,25 @@ async def _(bot: Bot, event: GroupMessageEvent, arg: Message = CommandArg()):
if prop_n not in property_.keys():
await use_props.finish("道具名称错误!", at_sender=True)
name = prop_n
if not name:
await use_props.finish("未获取到道具名称", at_sender=True)
_user_prop_count = property_[name]
if num > _user_prop_count:
await use_props.finish(f"道具数量不足,无法使用{num}次!")
if num > (n := func_manager.get_max_num_limit(name)):
await use_props.finish(f"该道具单次只能使用 {n} 个!")
model, kwargs = build_params(bot, event, name, num, text)
try:
await func_manager.run_handle(type_="before_handle", param=model, **kwargs)
except NotMeetUseConditionsException as e:
await use_props.finish(e.get_info(), at_sender=True)
async with db.transaction():
model, kwargs = build_params(bot, event, name, num, text)
except KeyError:
logger.warning(f"{name} 未注册使用函数")
await use_props.finish(f"{name} 未注册使用方法")
else:
try:
await func_manager.run_handle(
type_="before_handle", param=model, **kwargs
)
except NotMeetUseConditionsException as e:
await use_props.finish(e.get_info(), at_sender=True)
if await BagUser.delete_property(event.user_id, event.group_id, name, num):
if func_manager.check_send_success_message(name):
await use_props.send(f"使用道具 {name} {num} 次成功!", at_sender=True)
@@ -94,14 +100,18 @@ async def _(bot: Bot, event: GroupMessageEvent, arg: Message = CommandArg()):
logger.info(
f"USER {event.user_id} GROUP {event.group_id} 使用道具 {name} {num} 次成功"
)
await UserShopGoldLog.add_shop_log(
event.user_id, event.group_id, 1, name, num
await UserShopGoldLog.create(
user_qq=event.user_id,
group_id=event.group_id,
type=1,
name=name,
num=num,
)
else:
await use_props.send(f"使用道具 {name} {num} 次失败!", at_sender=True)
logger.info(
f"USER {event.user_id} GROUP {event.group_id} 使用道具 {name} {num} 次失败"
)
await func_manager.run_handle(type_="after_handle", param=model, **kwargs)
await func_manager.run_handle(type_="after_handle", param=model, **kwargs)
else:
await use_props.send("您的背包里没有任何的道具噢", at_sender=True)
+1 -2
View File
@@ -108,7 +108,7 @@ class GoodsUseFuncManager:
)
async def run_handle(self, goods_name: str, type_: str, param: ShopParam, **kwargs):
if self._data[goods_name].get(type_):
if self._data.get(goods_name) and self._data[goods_name].get(type_):
for func in self._data[goods_name].get(type_):
args = inspect.signature(func).parameters
if args and list(args.keys())[0] != "kwargs":
@@ -170,7 +170,6 @@ def build_params(
:param goods_name: 商品名称
:param num: 数量
:param text: 其他信息
:return:
"""
_kwargs = func_manager.get_kwargs(goods_name)
return (
+1 -7
View File
@@ -1,11 +1,5 @@
import nonebot
from pathlib import Path
import nonebot
nonebot.load_plugins(str(Path(__file__).parent.resolve()))
+27 -22
View File
@@ -1,14 +1,17 @@
from nonebot import on_command
from nonebot.permission import SUPERUSER
from nonebot.adapters.onebot.v11 import Bot, Message
from nonebot.params import Command, CommandArg
from typing import Tuple
from nonebot import on_command
from nonebot.adapters.onebot.v11 import Bot, Message, MessageEvent
from nonebot.params import Command, CommandArg
from nonebot.permission import SUPERUSER
from nonebot.rule import to_me
from utils.utils import is_number
from models.group_info import GroupInfo
from services.log import logger
from utils.depends import OneCommand
from utils.manager import requests_manager
from utils.message_builder import image
from models.group_info import GroupInfo
from utils.utils import is_number
__zx_plugin_name__ = "显示所有好友群组 [Superuser]"
__plugin_usage__ = """
@@ -76,8 +79,7 @@ async def _(bot: Bot):
@friend_handle.handle()
async def _(bot: Bot, cmd: Tuple[str, ...] = Command(), arg: Message = CommandArg()):
cmd = cmd[0]
async def _(bot: Bot, cmd: str = OneCommand(), arg: Message = CommandArg()):
id_ = arg.extract_plain_text().strip()
if is_number(id_):
id_ = int(id_)
@@ -97,8 +99,9 @@ async def _(bot: Bot, cmd: Tuple[str, ...] = Command(), arg: Message = CommandAr
@group_handle.handle()
async def _(bot: Bot, cmd: Tuple[str, ...] = Command(), arg: Message = CommandArg()):
cmd = cmd[0]
async def _(
bot: Bot, event: MessageEvent, cmd: str = OneCommand(), arg: Message = CommandArg()
):
id_ = arg.extract_plain_text().strip()
flag = None
if is_number(id_):
@@ -106,20 +109,22 @@ async def _(bot: Bot, cmd: Tuple[str, ...] = Command(), arg: Message = CommandAr
if cmd[:2] == "同意":
rid = requests_manager.get_group_id(id_)
if rid:
if await GroupInfo.get_group_info(rid):
await GroupInfo.set_group_flag(rid, 1)
if group := await GroupInfo.get_or_none(group_id=rid):
group.group_flag = 1
await group.save(update_fields=["group_flag"])
else:
group_info = await bot.get_group_info(group_id=rid)
await GroupInfo.add_group_info(
rid,
group_info["group_name"],
group_info["max_member_count"],
group_info["member_count"],
1
await GroupInfo.create(
group_id=rid,
group_name=group_info["group_name"],
max_member_count=group_info["max_member_count"],
member_count=group_info["member_count"],
group_flag=1,
)
flag = await requests_manager.approve(bot, id_, "group")
else:
await group_handle.send("同意群聊请求失败,未找到此id的请求..")
logger.info("同意群聊请求失败,未找到此id的请求..", cmd, event.user_id)
else:
flag = await requests_manager.refused(bot, id_, "group")
if flag == 1:
@@ -139,15 +144,15 @@ async def _():
for type_ in ["private", "group"]:
msg = await requests_manager.show(type_)
if msg:
_str += image(b64=msg)
_str += image(msg)
else:
_str += "没有任何好友请求.." if type_ == "private" else "没有任何群聊请求.."
if type_ == "private":
_str += '\n--------------------\n'
_str += "\n--------------------\n"
await cls_request.send(Message(_str))
@clear_request.handle()
async def _():
requests_manager.clear()
await clear_request.send("已清空所有好友/群聊请求..")
await clear_request.send("已清空所有好友/群聊请求..")
+26 -17
View File
@@ -1,13 +1,15 @@
import asyncio
import os
import time
from nonebot import on_command
from nonebot.permission import SUPERUSER
from configs.path_config import TEMP_PATH
from nonebot.rule import to_me
from utils.utils import scheduler
from configs.path_config import TEMP_PATH
from services.log import logger
from utils.manager import resources_manager
import asyncio
import time
import os
from utils.utils import scheduler
__zx_plugin_name__ = "清理临时数据 [Superuser]"
__plugin_usage__ = """
@@ -37,22 +39,29 @@ async def _():
await clear_data.send("开始清理临时数据....")
size = await asyncio.get_event_loop().run_in_executor(None, _clear_data)
await clear_data.send("共清理了 {:.2f}MB 的数据...".format(size / 1024 / 1024))
logger.info("清理临时数据完成," + "共清理了 {:.2f}MB 的数据...".format(size / 1024 / 1024))
def _clear_data() -> float:
logger.debug("开始清理临时文件...")
size = 0
for dir_ in resources_manager.get_temp_data_dir():
if dir_.exists():
for file in os.listdir(dir_):
file = dir_ / file
if file.is_file():
try:
if time.time() - os.path.getatime(file) > 300:
file_size = os.path.getsize(file)
file.unlink()
size += file_size
except Exception as e:
logger.error(f"清理临时数据错误...{type(e)}:{e}")
dir_list = [dir_ for dir_ in resources_manager.get_temp_data_dir() if dir_.exists()]
for dir_ in dir_list:
logger.debug(f"尝试清理文件夹: {dir_.absolute()}", "清理临时数据")
dir_size = 0
for file in os.listdir(dir_):
file = dir_ / file
if file.is_file():
try:
if time.time() - os.path.getatime(file) > 10:
file_size = os.path.getsize(file)
file.unlink()
size += file_size
dir_size += file_size
logger.debug(f"移除临时文件: {file.absolute()}", "清理临时数据")
except Exception as e:
logger.error(f"清理临时数据错误,临时文件夹: {dir_.absolute()}...", "清理临时数据", e=e)
logger.debug("清理临时文件夹大小: {:.2f}MB".format(size / 1024 / 1024), "清理临时数据")
return float(size)
+58 -52
View File
@@ -1,13 +1,15 @@
from nonebot.adapters.onebot.v11 import Message, Bot, MessageEvent
import asyncio
from nonebot import on_command
from nonebot.adapters.onebot.v11 import Bot, GroupMessageEvent, Message, MessageEvent
from nonebot.params import CommandArg
from nonebot.permission import SUPERUSER
from nonebot.rule import to_me
from utils.message_builder import custom_forward_msg
from services.db_context import db
from nonebot.params import CommandArg
from services.log import logger
from tortoise import Tortoise
import asyncio
from services.db_context import TestSQL
from services.log import logger
from utils.message_builder import custom_forward_msg
__zx_plugin_name__ = "执行sql [Superuser]"
__plugin_usage__ = """
@@ -18,9 +20,7 @@ usage:
查看所有表
""".strip()
__plugin_des__ = "执行一段sql语句"
__plugin_cmd__ = [
"exec [sql语句]",
]
__plugin_cmd__ = ["exec [sql语句]", "查看所有表"]
__plugin_version__ = 0.2
__plugin_author__ = "HibiKier"
@@ -32,55 +32,61 @@ tables = on_command("查看所有表", rule=to_me(), permission=SUPERUSER, prior
@exec_.handle()
async def _(bot: Bot, event: MessageEvent, arg: Message = CommandArg()):
sql = arg.extract_plain_text().strip()
async with db.transaction():
if not sql:
await exec_.finish("未接受到sql语句")
db = Tortoise.get_connection("default")
# try:
# 判断是否为SELECT语句
if sql.lower().startswith("select"):
pass
# # 分割语句
try:
# 判断是否为SELECT语句
if sql.lower().startswith("select"):
# 分割语句
try:
page = int(sql.split(" ")[-1]) - 1
sql_list = sql.split(" ")[:-1]
except ValueError:
page = 0
sql_list = sql.split(" ")
# 拼接语句
sql = " ".join(sql_list)
query = db.text(sql)
res = await db.all(query)
msg_list = [f"第{page+1}页查询结果:"]
# logger.info(res)
# 获取所有字段
keys = res[0].keys()
# 每页10条
for i in res[page * 10 : (page + 1) * 10]:
msg = ""
for key in keys:
msg += f"{key}: {i[key]}\n"
msg += f"第{page+1}页第{res.index(i)+1}条"
msg_list.append(msg)
# 检查是私聊还是群聊
try:
forward_msg_list = custom_forward_msg(msg_list, bot.self_id)
await bot.send_group_forward_msg(group_id=event.group_id, messages=forward_msg_list)
except:
for msg in msg_list:
await exec_.send(msg)
await asyncio.sleep(0.2)
return
query = db.text(sql)
await db.first(query)
await exec_.send("执行 sql 语句成功.")
except Exception as e:
await exec_.send(f"执行 sql 语句失败 {type(e)}:{e}")
logger.error(f"执行 sql 语句失败 {type(e)}:{e}")
page = int(sql.split(" ")[-1]) - 1
sql_list = sql.split(" ")[:-1]
except ValueError:
page = 0
sql_list = sql.split(" ")
# 拼接语句
sql = " ".join(sql_list)
res = await db.execute_query_dict(sql)
msg_list = [f"第{page+1}页查询结果:"]
# logger.info(res)
# 获取所有字段
keys = res[0].keys()
# 每页10条
for i in res[page * 10 : (page + 1) * 10]:
msg = ""
for key in keys:
msg += f"{key}: {i[key]}\n"
msg += f"第{page+1}页第{res.index(i)+1}条"
msg_list.append(msg)
# 检查是私聊还是群聊
if isinstance(event, GroupMessageEvent):
forward_msg_list = custom_forward_msg(msg_list, bot.self_id)
await bot.send_group_forward_msg(
group_id=event.group_id, messages=forward_msg_list
)
else:
for msg in msg_list:
await exec_.send(msg)
await asyncio.sleep(0.2)
return
else:
await TestSQL.raw(sql)
await exec_.send("执行 sql 语句成功.")
# except Exception as e:
# await exec_.send(f"执行 sql 语句失败 {type(e)}:{e}")
# logger.error(f"执行 sql 语句失败 {type(e)}:{e}")
@tables.handle()
async def _(bot: Bot, event: MessageEvent):
# 获取所有表
query = db.text("select tablename from pg_tables where schemaname = 'public'")
res = await db.all(query)
db = Tortoise.get_connection("default")
query = await db.execute_query_dict(
"select tablename from pg_tables where schemaname = 'public'"
)
msg = "数据库中的所有表名:\n"
for tablename in res:
for tablename in query:
msg += str(tablename["tablename"]) + "\n"
await tables.finish(msg)
+125 -85
View File
@@ -1,35 +1,39 @@
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
from nonebot.rule import to_me
from utils.utils import is_number
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.params import Command, CommandArg
from typing import Tuple
from nonebot import on_command, on_regex
from nonebot.adapters.onebot.v11 import (
GROUP,
Bot,
GroupMessageEvent,
Message,
MessageEvent,
)
from nonebot.params import Command, CommandArg
from nonebot.permission import SUPERUSER
from nonebot.rule import to_me
from configs.config import NICKNAME
from models.group_info import GroupInfo
from services.log import logger
from utils.depends import OneCommand
from utils.image_utils import text2image
from utils.manager import group_manager, plugins2settings_manager
from utils.message_builder import image
from utils.utils import is_number
__zx_plugin_name__ = "管理群操作 [Superuser]"
__plugin_usage__ = """
usage:
群权限 | 群白名单 | 退出群 操作
指令:
退群 [group_id]
退群,添加/删除群白名单,添加/删除群认证,当在群聊中这五个命令且没有指定群号时,默认指定当前群聊
指令:
退群 ?[group_id]
修改群权限 [group_id] [等级]
添加群白名单 *[group_id]
删除群白名单 *[group_id]
添加群认证 *[group_id]
删除群认证 *[group_id]
修改群权限 [等级]: 该命令仅在群聊时生效,默认修改当前群聊
添加群白名单 ?*[group_id]
删除群白名单 ?*[group_id]
添加群认证 ?*[group_id]
删除群认证 ?*[group_id]
查看群白名单
""".strip()
__plugin_des__ = "管理群操作"
@@ -53,7 +57,7 @@ my_group_level = on_command(
"查看群权限", aliases={"群权限"}, priority=5, permission=GROUP, block=True
)
what_up_group_level = on_regex(
".*?(提高|提升|升高|增加|加上)(.*?)群权限.*?",
"(:?提高|提升|升高|增加|加上).*?群权限",
rule=to_me(),
priority=5,
permission=GROUP,
@@ -73,47 +77,57 @@ group_auth = on_command(
@del_group.handle()
async def _(bot: Bot, arg: Message = CommandArg()):
async def _(bot: Bot, event: MessageEvent, arg: Message = CommandArg()):
group_id = arg.extract_plain_text().strip()
if not group_id and isinstance(event, GroupMessageEvent):
group_id = event.group_id
if group_id:
if is_number(group_id):
group_list = [x["group_id"] for x in await bot.get_group_list()]
group_id = int(group_id)
if group_id not in group_list:
logger.debug("群聊不存在", "退群", event.user_id, target=group_id)
await del_group.finish(f"{NICKNAME}未在该群聊中...")
try:
await bot.set_group_leave(group_id=int(group_id))
logger.info(f"退出群聊 {group_id} 成功")
await bot.set_group_leave(group_id=group_id)
logger.info(f"{NICKNAME}退出群聊成功", "退群", event.user_id, target=group_id)
await del_group.send(f"退出群聊 {group_id} 成功", at_sender=True)
group_manager.delete_group(int(group_id))
await GroupInfo.delete_group_info(int(group_id))
group_manager.delete_group(group_id)
await GroupInfo.filter(group_id=group_id).delete()
except Exception as e:
logger.info(f"退出群聊 {group_id} 失败 e:{e}")
logger.error(f"退出群聊失败", "退群", event.user_id, target=group_id, e=e)
await del_group.send(f"退出群聊 {group_id} 失败", at_sender=True)
else:
await del_group.finish(f"请输入正确的群号", at_sender=True)
await del_group.send(f"请输入正确的群号", at_sender=True)
else:
await del_group.finish(f"请输入群号", at_sender=True)
await del_group.send(f"请输入群号", at_sender=True)
@add_group_level.handle()
async def _(bot: Bot, event: MessageEvent, arg: Message = CommandArg()):
msg = arg.extract_plain_text().strip()
msg = msg.split()
group_id = 0
level = 0
if isinstance(event, GroupMessageEvent) and len(msg) == 1:
msg = [event.group_id, msg[0]]
if not msg:
await add_group_level.finish("用法:修改群权限 [group] [level]")
msg = msg.split()
await add_group_level.finish("缺失参数...")
if len(msg) < 2:
await add_group_level.finish("参数不完全..[group] [level]")
await add_group_level.finish("缺失参数...")
if is_number(msg[0]) and is_number(msg[1]):
group_id = msg[0]
group_id = int(msg[0])
level = int(msg[1])
else:
await add_group_level.finish("参数错误...group和level必须是数字..")
await add_group_level.finish("参数错误...群号和等级必须是数字..")
old_level = group_manager.get_group_level(group_id)
group_manager.set_group_level(group_id, level)
await add_group_level.send("修改成功...", at_sender=True)
if level > -1:
await bot.send_group_msg(
group_id=int(group_id), message=f"管理员修改了此群权限:{old_level} -> {level}"
group_id=group_id, message=f"管理员修改了此群权限:{old_level} -> {level}"
)
logger.info(f"{event.user_id} 修改了 {group_id} 的权限:{level}")
logger.info(f"修改群权限:{level}", "修改群权限", event.user_id, target=group_id)
@my_group_level.handle()
@@ -127,78 +141,104 @@ async def _(event: GroupMessageEvent):
if plugin_name == "pixiv":
plugin_name = "搜图 p站排行"
tmp += f"{plugin_name}\n"
if tmp:
tmp = "\n目前无法使用的功能:\n" + tmp
await my_group_level.finish(f"当前群权限:{level}{tmp}")
if not tmp:
await my_group_level.finish(f"当前群权限:{level}")
await my_group_level.finish(
f"当前群权限:{level}\n目前无法使用的功能:\n"
+ image(await text2image(tmp, padding=10, color="#f9f6f2"))
)
@what_up_group_level.handle()
async def _():
await what_up_group_level.finish(
f"[此功能用于防止内鬼,如果引起不便那真是抱歉了]\n" f"目前提高群权限的方法:\n" f"\t1.管理员修改权限"
f"[此功能用于防止内鬼,如果引起不便那真是抱歉了]\n" f"目前提高群权限的方法:\n" f"\t1.超级管理员修改权限"
)
@manager_group_whitelist.handle()
async def _(bot: Bot, cmd: Tuple[str, ...] = Command(), arg: Message = CommandArg()):
cmd = cmd[0]
async def _(
bot: Bot, event: MessageEvent, cmd: str = OneCommand(), arg: Message = CommandArg()
):
msg = arg.extract_plain_text().strip().split()
if not msg and isinstance(event, GroupMessageEvent):
msg = [event.group_id]
if not msg:
await manager_group_whitelist.finish("请输入群号")
all_group = [g["group_id"] for g in await bot.get_group_list()]
error_group = []
group_list = []
for group in msg:
if is_number(group) and int(group) in all_group:
group_list.append(int(group))
for group_id in msg:
if is_number(group_id) and int(group_id) in all_group:
group_list.append(int(group_id))
else:
logger.debug(f"群号不合法或不存在", cmd, target=group_id)
error_group.append(group_id)
if group_list:
for group in group_list:
for group_id in group_list:
if cmd in ["添加群白名单"]:
group_manager.add_group_white_list(group)
group_manager.add_group_white_list(group_id)
else:
group_manager.delete_group_white_list(group)
group_manager.delete_group_white_list(group_id)
group_list = [str(x) for x in group_list]
await manager_group_whitelist.send("已成功将 " + "\n".join(group_list) + " " + cmd)
else:
await manager_group_whitelist.send(f"添加失败,请检查{NICKNAME}是否已加入这些群聊或重复添加/删除群白单名")
group_manager.save()
if error_group:
await manager_group_whitelist.send("以下群聊不合法或不存在:\n" + "\n".join(error_group))
@show_group_whitelist.handle()
async def _():
x = group_manager.get_group_white_list()
x = [str(g) for g in x]
if x:
await show_group_whitelist.send("目前的群白名单:\n" + "\n".join(x))
else:
await show_group_whitelist.send("没有任何群在群白名单...")
group = [str(g) for g in group_manager.get_group_white_list()]
if not group:
await show_group_whitelist.finish("没有任何群在群白名单...")
await show_group_whitelist.send("目前的群白名单:\n" + "\n".join(group))
@group_auth.handle()
async def _(bot: Bot, cmd: Tuple[str, ...] = Command(), arg: Message = CommandArg()):
cmd = cmd[0]
async def _(
bot: Bot, event: MessageEvent, cmd: str = OneCommand(), arg: Message = CommandArg()
):
msg = arg.extract_plain_text().strip().split()
if isinstance(event, GroupMessageEvent) and not msg:
msg = [event.group_id]
if not msg:
await manager_group_whitelist.finish("请输入群号")
error_group = []
all_group = [g["group_id"] for g in await bot.get_group_list()]
for group_id in msg:
if not is_number(group_id):
await group_auth.send(f"{group_id}非纯数字,已跳过该项..")
group_id = int(group_id)
if cmd[:2] == "添加":
if await GroupInfo.get_group_info(group_id):
await GroupInfo.set_group_flag(group_id, 1)
else:
if is_number(group_id) and group_id in all_group:
if cmd[:2] == "添加":
try:
group_info = await bot.get_group_info(group_id=group_id)
except ActionFailed:
group_info = {
"group_id": group_id,
"group_name": "_",
"max_member_count": -1,
"member_count": -1,
}
await GroupInfo.add_group_info(
group_info["group_id"],
group_info["group_name"],
group_info["max_member_count"],
group_info["member_count"],
1,
)
await GroupInfo.update_or_create(
group_id=group_info["group_id"],
defaults={
"group_flag": 1,
"group_name": group_info["group_name"],
"max_member_count": group_info["max_member_count"],
"member_count": group_info["member_count"],
},
)
except Exception as e:
await group_auth.send(f"添加群认证 {group_id} 发生错误!")
logger.error(f"添加群认证发生错误", cmd, target=group_id, e=e)
else:
await group_auth.send(f"已为 {group_id} {cmd[:2]}群认证..")
logger.info(f"添加群认证成功", cmd, target=group_id)
else:
if group := await GroupInfo.filter(group_id=group_id).first():
await group.update_or_create(
group_id=group_id, defaults={"group_flag": 0}
)
await group_auth.send(f"已删除 {group_id} 群认证..")
logger.info(f"删除群认证成功", cmd, target=group_id)
else:
await group_auth.send(f"未查找到群聊: {group_id}")
logger.info(f"未找到群聊", cmd, target=group_id)
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]}群认证..")
logger.debug(f"群号不合法或不存在", cmd, target=group_id)
error_group.append(str(group_id))
if error_group:
await manager_group_whitelist.send("以下群聊不合法或不存在:\n" + "\n".join(error_group))
+16 -24
View File
@@ -1,45 +1,37 @@
from nonebot import on_command
from nonebot.permission import SUPERUSER
from nonebot.rule import to_me
from utils.manager import (
plugins2cd_manager,
plugins2settings_manager,
plugins2block_manager,
group_manager,
)
from configs.config import Config
from services.log import logger
from utils.manager import (
group_manager,
plugins2block_manager,
plugins2cd_manager,
plugins2settings_manager,
)
from utils.utils import scheduler
__zx_plugin_name__ = "重载插件配置 [Superuser]"
__zx_plugin_name__ = "重载配置 [Superuser]"
__plugin_usage__ = """
usage:
重载插件配置
重载配置
plugins2settings,
plugins2cd
plugins2block
group_manager
指令:
重载插件配置
重载配置
""".strip()
__plugin_des__ = "重载插件配置"
__plugin_des__ = "重载配置"
__plugin_cmd__ = [
"重载插件配置",
"重载配置",
]
__plugin_version__ = 0.1
__plugin_version__ = 0.2
__plugin_author__ = "HibiKier"
__plugin_configs__ = {
"AUTO_RELOAD": {
"value": False,
"help": "自动重载配置文件",
"default_value": False
},
"AUTO_RELOAD_TIME": {
"value": 180,
"help": "控制自动重载配置文件时长",
"default_value": 180
}
"AUTO_RELOAD": {"value": False, "help": "自动重载配置文件", "default_value": False, "type": bool},
"AUTO_RELOAD_TIME": {"value": 180, "help": "控制自动重载配置文件时长", "default_value": 180, "type": int},
}
@@ -59,7 +51,7 @@ async def _():
@scheduler.scheduled_job(
'interval',
"interval",
seconds=Config.get_config("reload_setting", "AUTO_RELOAD_TIME", 180),
)
async def _():
@@ -1,12 +1,16 @@
from typing import List, Tuple
from nonebot import on_command
from nonebot.adapters.onebot.v11 import Bot, GroupMessageEvent, Message, MessageEvent
from nonebot.exception import ActionFailed
from nonebot.params import CommandArg
from nonebot.permission import SUPERUSER
from models.level_user import LevelUser
from nonebot.adapters.onebot.v11 import Bot, MessageEvent, Message, GroupMessageEvent
from utils.utils import get_message_at, is_number
from services.log import logger
from utils.depends import AtList, OneCommand
from utils.message_builder import at
from nonebot.params import Command, CommandArg
from typing import Tuple
from utils.utils import is_number
__zx_plugin_name__ = "用户权限管理 [Superuser]"
__plugin_usage__ = """
@@ -16,12 +20,14 @@ usage:
添加权限 [at] [权限]
添加权限 [qq] [group_id] [权限]
删除权限 [at]
删除权限 [qq] [group_id]
""".strip()
__plugin_des__ = "增删改用户的权限"
__plugin_cmd__ = [
"添加权限 [at] [权限]",
"添加权限 [qq] [group_id] [权限]",
"删除权限 [at]",
"删除权限 [qq] [group_id]",
]
__plugin_version__ = 0.1
__plugin_author__ = "HibiKier"
@@ -40,21 +46,22 @@ super_cmd = on_command(
async def _(
bot: Bot,
event: MessageEvent,
cmd: Tuple[str, ...] = Command(),
cmd: str = OneCommand(),
arg: Message = CommandArg(),
at_list: List[int] = AtList(),
):
group_id = event.group_id if isinstance(event, GroupMessageEvent) else -1
level = None
args = arg.extract_plain_text().strip().split()
qq = get_message_at(event.json())
flag = 2
qq = None
try:
if qq:
qq = qq[0]
if cmd[0][:2] == "添加" and args and is_number(args[0]):
if at_list:
qq = at_list[0]
if cmd[:2] == "添加" and args and is_number(args[0]):
level = int(args[0])
else:
if cmd[0][:2] == "添加":
if cmd[:2] == "添加":
if (
len(args) > 2
and is_number(args[0])
@@ -69,33 +76,39 @@ async def _(
qq = int(args[0])
group_id = int(args[1])
flag = 1
level = -1 if cmd[0][:2] == "删除" else level
level = -1 if cmd[:2] == "删除" else level
if group_id == -1 or not level or not qq:
raise IndexError()
except IndexError:
await super_cmd.finish(__plugin_usage__)
if not qq:
await super_cmd.finish("未指定对象...")
try:
if cmd[0][:2] == "添加":
if await LevelUser.set_level(qq, group_id, level, 1):
result = f"添加管理成功, 权限: {level}"
else:
result = f"管理已存在, 更新权限: {level}"
if cmd[:2] == "添加":
await LevelUser.set_level(qq, group_id, level, 1)
result = f"设置权限成功, 权限: {level}"
else:
if await LevelUser.delete_level(qq, event.group_id):
if await LevelUser.delete_level(qq, group_id):
result = "删除管理成功!"
else:
result = "该账号无管理权限!"
if flag == 2:
await super_cmd.send(result)
elif flag == 1:
await bot.send_group_msg(
group_id=group_id,
message=Message(
f"{at(qq)}管理员修改了你的权限"
f"\n--------\n你当前的权限等级:{level if level != -1 else 0}"
),
)
try:
await bot.send_group_msg(
group_id=group_id,
message=Message(
f"{at(qq)}管理员修改了你的权限"
f"\n--------\n你当前的权限等级:{level if level != -1 else 0}"
),
)
except ActionFailed:
pass
await super_cmd.send("修改成功")
logger.info(
f"修改权限: {level if level != -1 else 0}", cmd, event.user_id, group_id, qq
)
except Exception as e:
await super_cmd.send("执行指令失败!")
logger.error(f"执行指令失败 e:{e}")
logger.error(f"执行指令失败", cmd, event.user_id, group_id, qq, e=e)
@@ -1,58 +0,0 @@
from nonebot import on_command
from nonebot.permission import SUPERUSER
from nonebot.adapters.onebot.v11 import Bot, MessageEvent, Message
from nonebot.rule import to_me
from utils.utils import is_number
from services.log import logger
from utils.manager import group_manager
from nonebot.params import Command, CommandArg
from typing import Tuple
__zx_plugin_name__ = "超级用户被动开关 [Superuser]"
__plugin_usage__ = """
usage:
超级用户被动开关
指令:
开启/关闭广播通知
""".strip()
__plugin_des__ = "超级用户被动开关"
__plugin_cmd__ = [
"开启/关闭广播通知",
]
__plugin_version__ = 0.1
__plugin_author__ = "HibiKier"
oc_gb = on_command(
"开启广播通知",
aliases={"关闭广播通知"},
rule=to_me(),
permission=SUPERUSER,
priority=1,
block=True,
)
@oc_gb.handle()
async def _(bot: Bot, event: MessageEvent, cmd: Tuple[str, ...] = Command(), arg: Message = CommandArg()):
cmd = cmd[0]
group = arg.extract_plain_text().strip()
if group:
if is_number(group):
group = int(group)
for g in await bot.get_group_list():
if g["group_id"] == group:
break
else:
await oc_gb.finish("没有加入这个群...", at_sender=True)
if cmd == "开启广播通知":
logger.info(f"USER {event.user_id} 开启了 GROUP {group} 的广播")
await oc_gb.finish(await group_manager.open_group_task(group, "broadcast",), at_sender=True)
else:
logger.info(f"USER {event.user_id} 关闭了 GROUP {group} 的广播")
await oc_gb.finish(await group_manager.close_group_task(group, "broadcast"), at_sender=True)
else:
await oc_gb.finish("请输入正确的群号", at_sender=True)
else:
await oc_gb.finish("请输入要关闭广播的群号", at_sender=True)
@@ -1,11 +1,11 @@
from nonebot import on_command
from nonebot.adapters.onebot.v11 import Bot, MessageEvent
from nonebot.permission import SUPERUSER
from nonebot.rule import to_me
from utils.utils import get_bot
from services.log import logger
from models.group_info import GroupInfo
from models.friend_user import FriendUser
from models.friend_user import FriendUser
from models.group_info import GroupInfo
from services.log import logger
__zx_plugin_name__ = "更新群/好友信息 [Superuser]"
__plugin_usage__ = """
@@ -32,39 +32,46 @@ update_friend_info = on_command(
@update_group_info.handle()
async def _():
bot = get_bot()
async def _(bot: Bot, event: MessageEvent):
gl = await bot.get_group_list()
gl = [g["group_id"] for g in gl]
num = 0
rst = ""
for g in gl:
group_info = await bot.get_group_info(group_id=g)
if await GroupInfo.add_group_info(
group_info["group_id"],
group_info["group_name"],
group_info["max_member_count"],
group_info["member_count"],
1
):
try:
group_info = await bot.get_group_info(group_id=g)
await GroupInfo.update_or_create(
group_id=group_info["group_id"],
defaults={
"group_name": group_info["group_name"],
"max_member_count": group_info["max_member_count"],
"member_count": group_info["member_count"],
},
)
num += 1
logger.info(f"自动更新群组 {g} 信息成功")
else:
logger.info(f"自动更新群组 {g} 信息失败")
rst += f"{g} 更新失败\n"
await update_group_info.send(f"成功更新了 {num} 个群的信息\n{rst[:-1]}")
logger.debug(
"群聊信息更新成功", "更新群信息", event.user_id, target=group_info["group_id"]
)
except Exception as e:
logger.error(f"更新群聊信息失败", "更新群信息", event.user_id, target=g, e=e)
await update_group_info.send(f"成功更新了 {len(gl)} 个群的信息")
logger.info(f"更新群聊信息完成,共更新了 {len(gl)} 个群的信息", "更新群信息", event.user_id)
@update_friend_info.handle()
async def _():
async def _(bot: Bot, event: MessageEvent):
num = 0
rst = ""
fl = await get_bot().get_friend_list()
error_list = []
fl = await bot.get_friend_list()
for f in fl:
if await FriendUser.add_friend_info(f["user_id"], f["nickname"]):
logger.info(f'自动更新好友 {f["user_id"]} 信息成功')
try:
await FriendUser.update_or_create(
user_id=f["user_id"], defaults={"nickname": f["nickname"]}
)
logger.debug(f"更新好友信息成功", "更新好友信息", event.user_id, target=f["user_id"])
num += 1
else:
logger.warning(f'自动更新好友 {f["user_id"]} 信息失败')
rst += f'{f["user_id"]} 更新失败\n'
await update_friend_info.send(f"成功更新了 {num} 个好友的信息\n{rst[:-1]}")
except Exception as e:
logger.error(f"更新好友信息失败", "更新好友信息", event.user_id, target=f["user_id"], e=e)
await update_friend_info.send(f"成功更新了 {num} 个好友的信息")
if error_list:
await update_friend_info.send(f"以下好友更新失败:\n" + "\n".join(error_list))
logger.info(f"更新好友信息完成,共更新了 {num} 个群的信息", "更新好友信息", event.user_id)
+4 -4
View File
@@ -1,11 +1,12 @@
from nonebot import on_command
from nonebot.permission import SUPERUSER
from nonebot.rule import to_me
from utils.message_builder import image
from .data_source import create_help_image, SUPERUSER_HELP_IMAGE
from .data_source import SUPERUSER_HELP_IMAGE, create_help_image
__zx_plugin_name__ = '超级用户帮助 [Superuser]'
__zx_plugin_name__ = "超级用户帮助 [Superuser]"
if SUPERUSER_HELP_IMAGE.exists():
@@ -20,5 +21,4 @@ super_help = on_command(
async def _():
if not SUPERUSER_HELP_IMAGE.exists():
await create_help_image()
x = image(SUPERUSER_HELP_IMAGE)
await super_help.finish(x)
await super_help.finish(image(SUPERUSER_HELP_IMAGE))
+2 -3
View File
@@ -1,6 +1,6 @@
from nonebot import on_command
from utils.message_builder import image
from utils.message_builder import image
__zx_plugin_name__ = "更新信息"
__plugin_usage__ = """
@@ -26,8 +26,7 @@ update_info = on_command("更新信息", aliases={"更新日志"}, priority=5, b
@update_info.handle()
async def _():
img = image("update_info.png")
if img:
if img := image("update_info.png"):
await update_info.finish(image("update_info.png"))
else:
await update_info.finish("目前没有更新信息哦")
+12 -5
View File
@@ -1,20 +1,27 @@
from typing import Optional
import platform
from pathlib import Path
from typing import Optional
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","")
hostip = (
os.popen("cat /etc/resolv.conf | grep nameserver | awk '{ print $2 }'")
.read()
.replace("\n", "")
)
# 回复消息名称
NICKNAME: str = "小真寻"
# 数据库(必要)
# 如果填写了bind就不需要再填写后面的字段了#)
# 示例:"bind": "postgresql://user:password@127.0.0.1:5432/database"
# 示例:"bind": "postgres://user:password@127.0.0.1:5432/database"
bind: str = "" # 数据库连接链接
sql_name: str = "postgresql"
sql_name: str = "postgres"
user: str = "" # 数据用户名
password: str = "" # 数据库密码
address: str = "" # 数据库地址
+187 -100
View File
@@ -1,10 +1,53 @@
import copy
from typing import Optional, Any, Union
from pathlib import Path
from ruamel.yaml import YAML
from typing import Any, Callable, Dict, Optional, Type, Union
import cattrs
from pydantic import BaseModel
from ruamel import yaml
from ruamel.yaml import YAML
from ruamel.yaml.scanner import ScannerError
from services.log import logger
class Config(BaseModel):
"""
配置项
"""
value: Any
"""配置项值"""
name: Optional[str]
"""插件名称"""
help: Optional[str]
"""配置注解"""
default_value: Optional[Any] = None
"""默认值"""
level_module: Optional[str]
"""受权限模块"""
type: Any = None
"""参数类型"""
arg_parser: Optional[Callable] = None
"""参数解析"""
class ConfigGroup(BaseModel):
"""
配置组
"""
module: str
"""模块名"""
configs: Dict[str, Config] = {}
"""配置项列表"""
class NoSuchConfig(Exception):
pass
class ConfigsManager:
"""
@@ -12,35 +55,25 @@ class ConfigsManager:
"""
def __init__(self, file: Path):
self._data: dict = {}
self._data: Dict[str, ConfigGroup] = {}
self._simple_data: dict = {}
self._admin_level_data = []
self._simple_file = Path() / "configs" / "config.yaml"
_yaml = YAML()
if file:
file.parent.mkdir(exist_ok=True, parents=True)
self.file = file
_yaml = YAML()
if file.exists():
with open(file, "r", encoding="utf8") as f:
self._data = _yaml.load(f)
if not self._data:
self.file.unlink()
raise ValueError(
"配置文件为空!\n"
"***********************************************************\n"
"****** 配置文件 plugins2config.yaml 为空,已删除,请重启 ******\n"
"***********************************************************"
)
if self._simple_file.exists():
try:
with open(self._simple_file, "r", encoding="utf8") as f:
self._simple_data = _yaml.load(f)
except ScannerError as e:
raise ScannerError(
f"{e}\n**********************************************\n"
f"****** 可能为config.yaml配置文件填写不规范 ******\n"
f"**********************************************"
)
self.load_data()
if self._simple_file.exists():
try:
with open(self._simple_file, "r", encoding="utf8") as f:
self._simple_data = _yaml.load(f)
except ScannerError as e:
raise ScannerError(
f"{e}\n**********************************************\n"
f"****** 可能为config.yaml配置文件填写不规范 ******\n"
f"**********************************************"
)
def add_plugin_config(
self,
@@ -50,7 +83,9 @@ class ConfigsManager:
*,
name: Optional[str] = None,
help_: Optional[str] = None,
default_value: Optional[str] = None,
default_value: Optional[Any] = None,
type: Optional[Type] = None,
arg_parser: Optional[Callable] = None,
_override: bool = False,
):
"""
@@ -61,41 +96,49 @@ class ConfigsManager:
:param name: 插件名称
:param help_: 配置注解
:param default_value: 默认值
:param _override: 覆盖前值
:param _override: 强制覆盖值
"""
if (
not (module in self._data.keys() and self._data[module].get(key))
or _override
):
if not module or not key:
raise ValueError("add_plugin_config: module和key不能为为空")
if module in self._data and (config := self._data[module].configs.get(key)):
config.help = help_
config.arg_parser = arg_parser
config.type = type
if _override:
config.value = value
config.name = name
config.default_value = default_value
else:
_module = None
if ":" in module:
module = module.split(":")
_module = module[-1]
module = module[0]
module_split = module.split(":")
if len(module_split) < 2:
raise ValueError(f"module: {module} 填写错误")
_module = module_split[-1]
module = module_split[0]
if "[LEVEL]" in key and _module:
key = key.replace("[LEVEL]", "").strip()
self._admin_level_data.append((_module, value))
if self._data.get(module) is None:
self._data[module] = {}
key = key.upper()
self._data[module][key] = {
"value": value,
"name": name.strip() if isinstance(name, str) else name,
"help": help_.strip() if isinstance(help_, str) else help_,
"default_value": default_value,
"level_module": _module,
}
if not self._data.get(module):
self._data[module] = ConfigGroup(module=module)
self._data[module].configs[key] = Config(
value=value,
name=name,
help=help_,
default_value=default_value,
level_module=_module,
type=type,
)
def remove_plugin_config(self, module: str):
"""
为插件删除一个配置
:param module: 模块名
"""
if module in self._data.keys():
del self._data[module]
self.save()
def set_config(self, module: str, key: str, value: Any, auto_save: bool = False, save_simple_data: bool = True):
def set_config(
self,
module: str,
key: str,
value: Any,
auto_save: bool = False,
save_simple_data: bool = True,
):
"""
设置配置值
:param module: 模块名
@@ -104,40 +147,16 @@ class ConfigsManager:
:param auto_save: 自动保存
:param save_simple_data: 保存至config.yaml
"""
if module in self._data.keys():
if module in self._data:
if (
self._data[module].get(key) is not None
and self._data[module][key] != value
self._data[module].configs.get(key)
and self._data[module].configs[key] != value
):
self._data[module][key]["value"] = value
self._data[module].configs[key].value = value
self._simple_data[module][key] = value
if auto_save:
self.save(save_simple_data=save_simple_data)
def set_help(self, module: str, key: str, help_: str):
"""
设置配置注释
:param module: 模块名
:param key: 配置名称
:param help_: 注释文本
"""
if module in self._data.keys():
if self._data[module].get(key) is not None:
self._data[module][key]["help"] = help_
self.save()
def set_default_value(self, module: str, key: str, value: Any):
"""
设置配置默认值
:param module: 模块名
:param key: 配置名称
:param value: 值
"""
if module in self._data.keys():
if self._data[module].get(key) is not None:
self._data[module][key]["default_value"] = value
self.save()
def get_config(
self, module: str, key: str, default: Optional[Any] = None
) -> Optional[Any]:
@@ -147,37 +166,68 @@ class ConfigsManager:
:param key: 配置名称
:param default: 没有key值内容的默认返回值
"""
logger.debug(
f"尝试获取配置 MODULE: [<u><y>{module}</y></u>] | KEY: [<u><y>{key}</y></u>]"
)
key = key.upper()
value = None
if module in self._data.keys():
for key in [key, f"{key} [LEVEL]"]:
if self._data[module].get(key) is not None:
if self._data[module][key]["value"] is None:
return self._data[module][key]["default_value"]
return self._data[module][key]["value"]
if default is not None:
return default
return None
config = self._data[module].configs.get(key)
if not config:
config = self._data[module].configs.get(f"{key} [LEVEL]")
if not config:
raise NoSuchConfig(f"未查询到配置项 MODULE: [ {module} ] | KEY: [ {key} ]")
if config.arg_parser:
value = config.arg_parser(value or config.default_value)
else:
try:
if config.value is not None:
value = (
cattrs.structure(config.value, config.type)
if config.type
else config.value
)
else:
if config.default_value is not None:
value = (
cattrs.structure(config.default_value, config.type)
if config.type
else config.default_value
)
except Exception as e:
logger.warning(
f"配置项类型转换 MODULE: [<u><y>{module}</y></u>] | KEY: [<u><y>{key}</y></u>]",
e=e,
)
value = config.value or config.default_value
if value is None:
value = default
logger.debug(
f"获取配置 MODULE: [<u><y>{module}</y></u>] | KEY: [<u><y>{key}</y></u>] -> [<u><c>{value}</c></u>]"
)
return value
def get_level2module(self, module: str, key: str) -> Optional[str]:
"""
获取指定key所绑定的module,一般为权限等级
获取指定key所绑定的module,一般为权限等级
:param module: 模块名
:param key: 配置名称
:return:
"""
if self._data.get(module) is not None:
if self._data[module].get(key) is not None:
return self._data[module][key].get("level_module")
if config := self._data[module].configs.get(key):
return config.level_module
def get(self, key: str):
def get(self, key: str) -> Optional[ConfigGroup]:
"""
获取插件配置数据
:param key: 名称
"""
if key in self._data.keys():
return self._data[key]
return self._data.get(key)
def save(self, path: Union[str, Path] = None, save_simple_data: bool = False):
def save(
self, path: Optional[Union[str, Path]] = None, save_simple_data: bool = False
):
"""
保存数据
:param path: 路径
@@ -192,10 +242,17 @@ class ConfigsManager:
Dumper=yaml.RoundTripDumper,
allow_unicode=True,
)
path = path if path else self.file
path = path or self.file
data = {}
for module in self._data:
data[module] = {}
for config in self._data[module].configs:
value = self._data[module].configs[config].dict()
del value["type"]
data[module][config] = value
with open(path, "w", encoding="utf8") as f:
yaml.dump(
self._data, f, indent=2, Dumper=yaml.RoundTripDumper, allow_unicode=True
data, f, indent=2, Dumper=yaml.RoundTripDumper, allow_unicode=True
)
def reload(self):
@@ -208,16 +265,46 @@ class ConfigsManager:
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._data[key].configs[k].value = self._simple_data[key][k]
self.save()
def load_data(self):
"""
加载数据
Raises:
ValueError: _description_
"""
if self.file.exists():
_yaml = YAML()
with open(self.file, "r", encoding="utf8") as f:
temp_data = _yaml.load(f)
if not temp_data:
self.file.unlink()
raise ValueError(
"配置文件为空!\n"
"***********************************************************\n"
"****** 配置文件 plugins2config.yaml 为空,已删除,请重启 ******\n"
"***********************************************************"
)
count = 0
for module in temp_data:
config_group = ConfigGroup(module=module)
for config in temp_data[module]:
config_group.configs[config] = Config(**temp_data[module][config])
count += 1
self._data[module] = config_group
logger.info(
f"加载配置完成,共加载 <u><y>{len(temp_data)}</y></u> 个配置组及对应 <u><y>{count}</y></u> 个配置项"
)
def get_admin_level_data(self):
"""
获取管理插件等级
"""
return self._admin_level_data
def get_data(self):
def get_data(self) -> Dict[str, ConfigGroup]:
return copy.deepcopy(self._data)
def is_empty(self) -> bool:
+66
View File
@@ -0,0 +1,66 @@
{
"抽卡":{
"buff": ["欧气满满,十连出金","出金不歪"],
"debuff": ["武器大师","保底出金","金色会是痛苦大剑"]
},
"刷世界boss":{
"buff": ["双攻双爆角斗士"],
"debuff": ["只有保底材料","贪生怕死角斗士"]
},
"刷风本":{
"buff": ["会有极品猎人套","会掉真正的少女心","治疗加成少女头"],
"debuff": ["勇往直前少女心","少女飘摇的杀意","少女暴怒的容颜"]
},
"刷火本":{
"buff": ["魔女帽子火伤杯","暴伤魔女帽!","火伤魔女心!"],
"debuff": ["幡 然 醒 悟","这么阴间的地方真的会有魔女套吗?","不务正业火魔女","会匹配到3个卢姥爷"]
},
"刷岩本":{
"buff": ["悠久的磐岩伴你左右","岩神的庇护常在"],
"debuff": ["防御流星杯,你值得拥有"]
},
"刷宗室":{
"buff": ["物理伤害骑士道,元素精通宗室套"],
"debuff": ["贪生怕死骑士道,物理伤害宗室杯"]
},
"刷冰本":{
"buff": ["双暴词条概率up"],
"debuff": ["防御力船帽,无人可及"]
},
"刷雷本":{
"buff": ["愿雷鸟伴你左右"],
"debuff": ["来表演一个只掉平雷套的绝活","风神忽悠雷凶兆"]
},
"锄大地":{
"buff": ["会掉一大堆紫色材料"],
"debuff": ["深渊法师爱你哟","会被冰水法控到死"]
},
"挖矿":{
"buff": ["开矿出双材料"],
"debuff": ["去别人世界会被拒"]
},
"刷天赋本":{
"buff": ["金色!我看到了金色的书!"],
"debuff": ["2蓝2绿不会变"]
},
"刷突破材料":{
"buff": ["金色!我看到了金色的材料!"],
"debuff": ["2蓝2绿不会变"]
},
"升级圣遗物":{
"buff": ["稀有词条跳跳跳","会双爆拉满"],
"debuff": ["女 仆 狂 喜","无中生有防御力","生命拉满","完美避开双爆"]
},
"打风魔龙":{
"buff": ["看我一箭一个风魔鸡","今天特瓦林可以给想要的突破材料","5金加原胚!"],
"debuff": ["会不小心掉下平台","不小心被地板烫死了"]
},
"打狼王":{
"buff": ["今天安德琉斯的心情不错,可以py一下","5金加原胚!"],
"debuff": ["狼尾巴*1"]
},
"打公子":{
"buff": ["今天可以和公子py想要的突破材料","5金加原胚!"],
"debuff": ["要角没有!要命一条!"]
}
}
+79 -144
View File
@@ -1,24 +1,37 @@
from services.db_context import db
from typing import Dict
from typing import Optional, List
from services.log import logger
from tortoise import fields
from services.db_context import Model
from .goods_info import GoodsInfo
class BagUser(db.Model):
__tablename__ = "bag_users"
id = db.Column(db.Integer(), primary_key=True)
user_qq = db.Column(db.BigInteger(), nullable=False)
group_id = db.Column(db.BigInteger(), nullable=False)
gold = db.Column(db.Integer(), default=100)
props = db.Column(db.TEXT(), nullable=False, default="") # 旧道具字段(废弃)
spend_total_gold = db.Column(db.Integer(), default=0)
get_total_gold = db.Column(db.Integer(), default=0)
get_today_gold = db.Column(db.Integer(), default=0)
spend_today_gold = db.Column(db.Integer(), default=0)
property = db.Column(db.JSON(), nullable=False, default={}) # 新道具字段
class BagUser(Model):
_idx1 = db.Index("bag_group_users_idx1", "user_qq", "group_id", unique=True)
id = fields.IntField(pk=True, generated=True, auto_increment=True)
"""自增id"""
user_qq = fields.BigIntField()
"""用户id"""
group_id = fields.BigIntField()
"""群聊id"""
gold = fields.IntField(default=100)
"""金币数量"""
spend_total_gold = fields.IntField(default=0)
"""花费金币总数"""
get_total_gold = fields.IntField(default=0)
"""获取金币总数"""
get_today_gold = fields.IntField(default=0)
"""今日获取金币"""
spend_today_gold = fields.IntField(default=0)
"""今日获取金币"""
property: Dict[str, int] = fields.JSONField(default={})
"""道具"""
class Meta:
table = "bag_users"
table_description = "用户道具数据表"
unique_together = ("user_qq", "group_id")
@classmethod
async def get_user_total_gold(cls, user_qq: int, group_id: int) -> str:
@@ -29,13 +42,7 @@ class BagUser(db.Model):
:param user_qq: qq号
:param group_id: 所在群号
"""
query = cls.query.where((cls.user_qq == user_qq) & (cls.group_id == group_id))
user = await query.gino.first()
if not user:
user = await cls.create(
user_qq=user_qq,
group_id=group_id,
)
user, _ = await cls.get_or_create(user_qq=user_qq, group_id=group_id)
return (
f"当前金币:{user.gold}\n今日获取金币:{user.get_today_gold}\n今日花费金币:{user.spend_today_gold}"
f"\n今日收益:{user.get_today_gold - user.spend_today_gold}"
@@ -51,19 +58,13 @@ class BagUser(db.Model):
:param user_qq: qq号
:param group_id: 所在群号
"""
query = cls.query.where((cls.user_qq == user_qq) & (cls.group_id == group_id))
user = await query.gino.first()
if user:
return user.gold
else:
await cls.create(
user_qq=user_qq,
group_id=group_id,
)
return 100
user, _ = await cls.get_or_create(user_qq=user_qq, group_id=group_id)
return user.gold
@classmethod
async def get_property(cls, user_qq: int, group_id: int, only_active: bool = False) -> Dict[str, int]:
async def get_property(
cls, user_qq: int, group_id: int, only_active: bool = False
) -> Dict[str, int]:
"""
说明:
获取当前道具
@@ -72,22 +73,18 @@ class BagUser(db.Model):
:param group_id: 所在群号
:param only_active: 仅仅获取主动使用的道具
"""
query = cls.query.where((cls.user_qq == user_qq) & (cls.group_id == group_id))
user = await query.gino.first()
if user:
if only_active and user.property:
data = {}
name_list = [x.goods_name for x in await GoodsInfo.get_all_goods() if not x.is_passive]
for key in [x for x in user.property.keys() if x in name_list]:
data[key] = user.property[key]
return data
return user.property
else:
await cls.create(
user_qq=user_qq,
group_id=group_id,
)
return {}
user, _ = await cls.get_or_create(user_qq=user_qq, group_id=group_id)
if only_active and user.property:
data = {}
name_list = [
x.goods_name
for x in await GoodsInfo.get_all_goods()
if not x.is_passive
]
for key in [x for x in user.property if x in name_list]:
data[key] = user.property[key]
return data
return user.property
@classmethod
async def add_gold(cls, user_qq: int, group_id: int, num: int):
@@ -99,23 +96,11 @@ class BagUser(db.Model):
:param group_id: 所在群号
:param num: 金币数量
"""
query = cls.query.where((cls.user_qq == user_qq) & (cls.group_id == group_id))
query = query.with_for_update()
user = await query.gino.first()
if user:
await user.update(
gold=user.gold + num,
get_total_gold=user.get_total_gold + num,
get_today_gold=user.get_today_gold + num,
).apply()
else:
await cls.create(
user_qq=user_qq,
group_id=group_id,
gold=100 + num,
get_total_gold=num,
get_today_gold=num,
)
user, _ = await cls.get_or_create(user_qq=user_qq, group_id=group_id)
user.gold = user.gold + num
user.get_total_gold = user.get_total_gold + num
user.get_today_gold = user.get_today_gold + num
await user.save(update_fields=["gold", "get_today_gold", "get_total_gold"])
@classmethod
async def spend_gold(cls, user_qq: int, group_id: int, num: int):
@@ -127,26 +112,14 @@ class BagUser(db.Model):
:param group_id: 所在群号
:param num: 金币数量
"""
query = cls.query.where((cls.user_qq == user_qq) & (cls.group_id == group_id))
query = query.with_for_update()
user = await query.gino.first()
if user:
await user.update(
gold=user.gold - num,
spend_total_gold=user.spend_total_gold + num,
spend_today_gold=user.spend_today_gold + num,
).apply()
else:
await cls.create(
user_qq=user_qq,
group_id=group_id,
gold=100 - num,
spend_total_gold=num,
spend_today_gold=num,
)
user, _ = await cls.get_or_create(user_qq=user_qq, group_id=group_id)
user.gold = user.gold - num
user.spend_total_gold = user.spend_total_gold + num
user.spend_today_gold = user.spend_today_gold + num
await user.save(update_fields=["gold", "spend_total_gold", "spend_today_gold"])
@classmethod
async def add_property(cls, user_qq: int, group_id: int, name: str):
async def add_property(cls, user_qq: int, group_id: int, name: str, num: int = 1):
"""
说明:
增加道具
@@ -154,19 +127,15 @@ class BagUser(db.Model):
:param user_qq: qq号
:param group_id: 所在群号
:param name: 道具名称
:param num: 道具数量
"""
query = cls.query.where((cls.user_qq == user_qq) & (cls.group_id == group_id))
query = query.with_for_update()
user = await query.gino.first()
if user:
p = user.property
if p.get(name) is None:
p[name] = 1
else:
p[name] += 1
await user.update(property=p).apply()
else:
await cls.create(user_qq=user_qq, group_id=group_id, property={name: 1})
user, _ = await cls.get_or_create(user_qq=user_qq, group_id=group_id)
property_ = user.property
if property_.get(name) is None:
property_[name] = 0
property_[name] += num
user.property = property_
await user.save(update_fields=["property"])
@classmethod
async def delete_property(
@@ -181,54 +150,20 @@ class BagUser(db.Model):
:param name: 道具名称
:param num: 使用个数
"""
query = cls.query.where((cls.user_qq == user_qq) & (cls.group_id == group_id))
query = query.with_for_update()
user = await query.gino.first()
if user:
property_ = user.property
if name in property_:
if property_.get(name) == num:
del property_[name]
else:
property_[name] -= num
await user.update(property=property_).apply()
return True
return False
@classmethod
async def buy_property(
cls, user_qq: int, group_id: int, goods: "GoodsInfo", goods_num: int
) -> bool:
"""
说明:
购买道具
参数:
:param user_qq: 用户qq
:param group_id: 所在群聊
:param goods: 商品
:param goods_num: 商品数量
"""
try:
# 折扣后金币
spend_gold = goods.goods_discount * goods.goods_price * goods_num
await BagUser.spend_gold(user_qq, group_id, spend_gold)
for _ in range(goods_num):
await BagUser.add_property(user_qq, group_id, goods.goods_name)
user, _ = await cls.get_or_create(user_qq=user_qq, group_id=group_id)
property_ = user.property
if name in property_:
if (n := property_.get(name, 0)) < num:
return False
if n == num:
del property_[name]
else:
property_[name] -= num
await user.save(update_fields=["property"])
return True
except Exception as e:
logger.error(f"buy_property 发生错误 {type(e)}:{e}")
return False
@classmethod
async def get_all_users(cls, group_id: Optional[int] = None) -> List["BagUser"]:
"""
说明:
获取所有用户数据
参数:
:param group_id: 群号
"""
if not group_id:
query = await cls.query.gino.all()
else:
query = await cls.query.where((cls.group_id == group_id)).gino.all()
return query
async def _run_script(cls):
await cls.raw("ALTER TABLE bag_users DROP props;")
"""删除 props 字段"""
+58 -53
View File
@@ -1,16 +1,26 @@
from services.db_context import db
import time
from typing import Union
from tortoise import fields
from services.db_context import Model
from services.log import logger
class BanUser(db.Model):
__tablename__ = "ban_users"
class BanUser(Model):
user_qq = db.Column(db.BigInteger(), nullable=False, primary_key=True)
ban_level = db.Column(db.Integer(), nullable=False)
ban_time = db.Column(db.BigInteger())
duration = db.Column(db.BigInteger())
user_qq = fields.IntField(pk=True)
"""用户id"""
ban_level = fields.IntField()
"""使用ban命令的用户等级"""
ban_time = fields.BigIntField()
"""ban开始的时间"""
duration = fields.BigIntField()
"""ban时长"""
_idx1 = db.Index("ban_group_users_idx1", "user_qq", unique=True)
class Meta:
table = "ban_users"
table_description = ".ban/b了 封禁人员数据表"
@classmethod
async def check_ban_level(cls, user_qq: int, level: int) -> bool:
@@ -21,30 +31,34 @@ class BanUser(db.Model):
:param user_qq: unban用户的qq号
:param level: ban掉目标用户的权限等级
"""
user = await cls.query.where((cls.user_qq == user_qq)).gino.first()
if not user:
return False
if user.ban_level > level:
return True
user = await cls.filter(user_qq=user_qq).first()
if user:
logger.debug(
f"检测用户被ban等级,user_level: {user.ban_level},level: {level}",
target=user_qq,
)
return bool(user and user.ban_level > level)
return False
@classmethod
async def check_ban_time(cls, user_qq: int) -> str:
async def check_ban_time(cls, user_qq: int) -> Union[str, int]:
"""
说明:
检测用户被ban时长
参数:
:param user_qq: qq号
"""
query = cls.query.where((cls.user_qq == user_qq))
user = await query.gino.first()
if not user:
return ""
if time.time() - (user.ban_time + user.duration) > 0 and user.duration != -1:
return ""
if user.duration == -1:
return "∞"
return time.time() - user.ban_time - user.duration
logger.debug(f"获取用户ban时长", target=user_qq)
if user := await cls.filter(user_qq=user_qq).first():
if (
time.time() - (user.ban_time + user.duration) > 0
and user.duration != -1
):
return ""
if user.duration == -1:
return "∞"
return int(time.time() - user.ban_time - user.duration)
return ""
@classmethod
async def is_ban(cls, user_qq: int) -> bool:
@@ -54,28 +68,29 @@ class BanUser(db.Model):
参数:
:param user_qq: qq号
"""
logger.debug(f"检测是否被ban", target=user_qq)
if await cls.check_ban_time(user_qq):
return True
else:
await cls.unban(user_qq)
return False
return False
@classmethod
async def is_super_ban(cls, user_qq: int) -> bool:
"""
说明:
判断用户是否被ban
判断用户是否被超级用户ban / b了
参数:
:param user_qq: qq号
"""
user = await cls.query.where((cls.user_qq == user_qq)).gino.first()
if not user:
return False
if user.ban_level == 10:
return True
logger.debug(f"检测是否被超级用户权限封禁", target=user_qq)
if user := await cls.filter(user_qq=user_qq).first():
if user.ban_level == 10:
return True
return False
@classmethod
async def ban(cls, user_qq: int, ban_level: int, duration: int) -> bool:
async def ban(cls, user_qq: int, ban_level: int, duration: int):
"""
说明:
ban掉目标用户
@@ -84,22 +99,15 @@ class BanUser(db.Model):
:param ban_level: 使用ban命令用户的权限
:param duration: ban时长,秒
"""
query = cls.query.where((cls.user_qq == user_qq))
query = query.with_for_update()
user = await query.gino.first()
if not await cls.check_ban_time(user_qq):
logger.debug(f"封禁用户,等级:{ban_level},时长: {duration}", target=user_qq)
if await cls.filter(user_qq=user_qq).first():
await cls.unban(user_qq)
user = None
if user is None:
await cls.create(
user_qq=user_qq,
ban_level=ban_level,
ban_time=time.time(),
duration=duration,
)
return True
else:
return False
await cls.create(
user_qq=user_qq,
ban_level=ban_level,
ban_time=time.time(),
duration=duration,
)
@classmethod
async def unban(cls, user_qq: int) -> bool:
@@ -109,11 +117,8 @@ class BanUser(db.Model):
参数:
:param user_qq: qq号
"""
query = cls.query.where((cls.user_qq == user_qq))
query = query.with_for_update()
user = await query.gino.first()
if user is None:
return False
else:
await cls.delete.where((cls.user_qq == user_qq)).gino.status()
if user := await cls.filter(user_qq=user_qq).first():
logger.debug("解除封禁", target=user_qq)
await user.delete()
return True
return False
+57 -154
View File
@@ -1,81 +1,30 @@
from datetime import datetime, timedelta
from typing import List, Literal, Optional, Tuple, Union
from typing import Any, List, Literal, Optional, Tuple, Union
from services.db_context import db
from tortoise import fields
from tortoise.functions import Count
from services.db_context import Model
class ChatHistory(db.Model):
__tablename__ = "chat_history"
class ChatHistory(Model):
id = db.Column(db.Integer(), primary_key=True)
user_qq = db.Column(db.BigInteger(), nullable=False)
group_id = db.Column(db.BigInteger())
text = db.Column(db.Text())
plain_text = db.Column(db.Text())
create_time = db.Column(db.DateTime(timezone=True), nullable=False)
id = fields.IntField(pk=True, generated=True, auto_increment=True)
"""自增id"""
user_qq = fields.BigIntField()
"""用户id"""
group_id = fields.BigIntField(null=True)
"""群聊id"""
text = fields.TextField(null=True)
"""文本内容"""
plain_text = fields.TextField(null=True)
"""纯文本"""
create_time = fields.DatetimeField(auto_now_add=True)
"""创建时间"""
@classmethod
async def add_chat_msg(cls, user_qq: int, group_id: Optional[int], text: str, plain_text: str):
await cls.create(
user_qq=user_qq, group_id=group_id, text=text, plain_text=plain_text, create_time=datetime.now()
)
@classmethod
async def get_user_msg(
cls,
uid: int,
msg_type: Optional[Literal["private", "group"]],
days: Optional[int] = None,
) -> List["ChatHistory"]:
"""
说明:
获取用户消息
参数:
:param uid: 用户qq
:param msg_type: 消息类型,私聊或群聊
:param days: 限制日期
"""
return await cls._get_msg(uid, None, "user", msg_type, days).gino.all()
@classmethod
async def get_group_user_msg(
cls,
uid: int,
gid: int,
limit: int = 10,
date_scope: Tuple[datetime, datetime] = None,
) -> List["ChatHistory"]:
"""
说明:
获取群聊指定用户聊天记录
参数:
:param uid: qq
:param gid: 群号
:param limit: 获取数量
:param date_scope: 日期范围,默认None为全搜索
"""
return (
await cls._get_msg(uid, gid, "group", days=date_scope)
.limit(limit)
.gino.all()
)
@classmethod
async def get_group_user_msg_count(cls, uid: int, gid: int) -> Optional[int]:
"""
说明:
查询群聊指定用户的聊天记录数量
参数:
:param uid: qq
:param gid: 群号
"""
if x := await db.first(
db.text(
f"SELECT COUNT(id) as sum FROM public.chat_history WHERE user_qq = {uid} AND group_id = {gid}"
)
):
return x[0]
return None
class Meta:
table = "chat_history"
table_description = "聊天记录数据表"
@classmethod
async def get_group_msg_rank(
@@ -84,7 +33,7 @@ class ChatHistory(db.Model):
limit: int = 10,
order: str = "DESC",
date_scope: Optional[Tuple[datetime, datetime]] = None,
) -> Optional[Tuple[int, int]]:
) -> List["ChatHistory"]:
"""
说明:
获取排行数据
@@ -94,89 +43,42 @@ class ChatHistory(db.Model):
:param order: 排序类型,desc,des
:param date_scope: 日期范围
"""
sql = f"SELECT user_qq, COUNT(id) as sum FROM public.chat_history WHERE group_id = {gid} "
o = "-" if order == "DESC" else ""
query = cls.filter(group_id=gid)
if date_scope:
sql += f"AND create_time BETWEEN '{date_scope[0]}' AND '{date_scope[1]}' "
sql += f"GROUP BY user_qq ORDER BY sum {order if order and order.upper() != 'DES' else ''} LIMIT {limit}"
return await db.all(db.text(sql))
query = query.filter(create_time__range=date_scope)
return list(
await query.annotate(count=Count("user_qq"))
.order_by(o + "count")
.group_by("user_qq")
.limit(limit)
.values_list("user_qq", "count")
) # type: ignore
@classmethod
async def get_group_first_msg_datetime(cls, gid: int) -> Optional[datetime]:
async def get_group_first_msg_datetime(cls, group_id: int) -> Optional[datetime]:
"""
说明:
获取群第一条记录消息时间
参数:
:param gid:
:param group_id: 群聊id
"""
if (
msg := await cls.query.where(cls.group_id == gid)
.order_by(cls.create_time)
.gino.first()
message := await cls.filter(group_id=group_id)
.order_by("create_time")
.first()
):
return msg.create_time
return None
return message.create_time
@classmethod
async def get_user_msg_count(
cls,
uid: int,
msg_type: Optional[Literal["private", "group"]],
days: Optional[int] = None,
) -> int:
"""
说明:
获取用户消息数量
参数:
:param uid: 用户qq
:param msg_type: 消息类型,私聊或群聊
:param days: 限制日期
"""
return (
await cls._get_msg(uid, None, "user", msg_type, days, True).gino.first()
)[0]
@classmethod
async def get_group_msg(
cls,
gid: int,
days: Optional[int] = None,
) -> List["ChatHistory"]:
"""
说明:
获取群聊消息
参数:
:param gid: 用户qq
:param days: 限制日期
"""
return await cls._get_msg(None, gid, "group", None, days).gino.all()
@classmethod
async def get_group_msg_count(
cls,
gid: int,
days: Optional[int] = None,
) -> List["ChatHistory"]:
"""
说明:
获取群聊消息数量
参数:
:param gid: 用户qq
:param days: 限制日期
"""
return (await cls._get_msg(None, gid, "group", None, days, True).gino.first())[
0
]
@classmethod
def _get_msg(
async def get_message(
cls,
uid: Optional[int],
gid: Optional[int],
type_: Literal["user", "group"],
msg_type: Optional[Literal["private", "group"]] = None,
days: Optional[Union[int, Tuple[datetime, datetime]]] = None,
is_select_count: bool = False,
):
) -> List["ChatHistory"]:
"""
说明:
获取消息查询query
@@ -187,28 +89,29 @@ class ChatHistory(db.Model):
:param msg_type: 消息类型,用户或群聊
:param days: 限制日期
"""
if is_select_count:
setattr(ChatHistory, "count", db.func.count(cls.id).label("count"))
query = cls.select("count")
else:
query = cls.query
if type_ == "user":
query = query.where(cls.user_qq == uid)
query = cls.filter(user_qq=uid)
if msg_type == "private":
query = query.where(cls.group_id == None)
query = query.filter(group_id__isnull=True)
elif msg_type == "group":
query = query.where(cls.group_id != None)
query = query.filter(group_id__not_isnull=True)
else:
query = query.where(cls.group_id == gid)
query = cls.filter(group_id=gid)
if uid:
query = query.where(cls.user_qq == uid)
query = query.filter(user_qq=uid)
if days:
if isinstance(days, int):
query = query.where(
cls.create_time >= datetime.now() - timedelta(days=days)
query = query.filter(
create_time__gte=datetime.now() - timedelta(days=days)
)
elif isinstance(days, tuple):
query = query.where(cls.create_time >= days[0]).where(
cls.create_time <= days[1]
)
return query
query = query.filter(create_time__range=days)
return await query.all() # type: ignore
@classmethod
async def _run_script(cls):
return [
"alter table chat_history alter group_id drop not null;", # 允许 group_id 为空
"alter table chat_history alter text drop not null;", # 允许 text 为空
"alter table chat_history alter plain_text drop not null;", # 允许 plain_text 为空
]
+22 -77
View File
@@ -1,16 +1,23 @@
from services.db_context import db
from tortoise import fields
from configs.config import Config
from services.db_context import Model
class FriendUser(db.Model):
__tablename__ = "friend_users"
class FriendUser(Model):
id = db.Column(db.Integer(), primary_key=True)
user_id = db.Column(db.BigInteger(), nullable=False)
user_name = db.Column(db.Unicode(), nullable=False, default="")
nickname = db.Column(db.Unicode())
id = fields.IntField(pk=True, generated=True, auto_increment=True)
"""自增id"""
user_id = fields.BigIntField(unique=True)
"""用户id"""
user_name = fields.CharField(max_length=255, default="")
"""用户名称"""
nickname = fields.CharField(max_length=255, null=True)
"""私聊下自定义昵称"""
_idx1 = db.Index("friend_users_idx1", "user_id", unique=True)
class Meta:
table = "friend_users"
table_description = "好友信息数据表"
@classmethod
async def get_user_name(cls, user_id: int) -> str:
@@ -20,77 +27,29 @@ class FriendUser(db.Model):
参数:
:param user_id: qq号
"""
query = cls.query.where(cls.user_id == user_id)
user = await query.gino.first()
if user:
if user := await cls.get_or_none(user_id=user_id):
return user.user_name
else:
return ""
return ""
@classmethod
async def add_friend_info(cls, user_id: int, user_name: str) -> bool:
"""
说明:
添加好友信息
参数:
:param user_id: qq号
:param user_name: 用户名称
"""
try:
query = cls.query.where(cls.user_id == user_id)
user = await query.with_for_update().gino.first()
if not user:
await cls.create(
user_id=user_id,
user_name=user_name,
)
else:
await user.update(
user_name=user_name,
).apply()
return True
except Exception:
return False
@classmethod
async def delete_friend_info(cls, user_id: int) -> bool:
"""
说明:
删除好友信息
参数:
:param user_id: qq号
"""
try:
query = cls.query.where(cls.user_id == user_id)
user = await query.with_for_update().gino.first()
if user:
await user.delete()
return True
except Exception:
return False
@classmethod
async def get_friend_nickname(cls, user_id: int) -> str:
async def get_user_nickname(cls, user_id: int) -> str:
"""
说明:
获取用户昵称
参数:
:param user_id: qq号
"""
query = cls.query.where(cls.user_id == user_id)
user = await query.gino.first()
if user:
if user := await cls.get_or_none(user_id=user_id):
if user.nickname:
_tmp = ""
black_word = Config.get_config("nickname", "BLACK_WORD")
if black_word:
if black_word := Config.get_config("nickname", "BLACK_WORD"):
for x in user.nickname:
_tmp += "*" if x in black_word else x
return _tmp
return ""
@classmethod
async def set_friend_nickname(cls, user_id: int, nickname: str) -> bool:
async def set_user_nickname(cls, user_id: int, nickname: str):
"""
说明:
设置用户昵称
@@ -98,18 +57,4 @@ class FriendUser(db.Model):
:param user_id: qq号
:param nickname: 昵称
"""
try:
query = cls.query.where(cls.user_id == user_id)
user = await query.with_for_update().gino.first()
if not user:
await cls.create(
user_id=user_id,
nickname=nickname,
)
else:
await user.update(
nickname=nickname,
).apply()
return True
except Exception:
return False
await cls.update_or_create(user_id=user_id, defaults={"nickname": nickname})
+90 -107
View File
@@ -1,38 +1,50 @@
from services.db_context import db
from typing import Optional, List, Tuple
from services.log import logger
from typing import Dict, List, Optional, Tuple
from tortoise import fields
from services.db_context import Model
class GoodsInfo(db.Model):
class GoodsInfo(Model):
__tablename__ = "goods_info"
id = db.Column(db.Integer(), primary_key=True)
goods_name = db.Column(db.TEXT(), nullable=False) # 名称
goods_price = db.Column(db.Integer(), nullable=False) # 价格
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={}
) # 每日购买限制数据存储
is_passive = db.Column(db.Boolean(), nullable=False, default=0) # 是否为被动
icon = db.Column(db.String(), default=0) # 图标
id = fields.IntField(pk=True, generated=True, auto_increment=True)
"""自增id"""
goods_name = fields.CharField(255, unique=True)
"""商品名称"""
goods_price = fields.IntField()
"""价格"""
goods_description = fields.TextField()
"""描述"""
goods_discount = fields.FloatField(default=1)
"""折扣"""
goods_limit_time = fields.BigIntField(default=0)
"""限时"""
daily_limit = fields.IntField(default=0)
"""每日限购"""
daily_purchase_limit: Dict[str, Dict[str, int]] = fields.JSONField(default={})
"""用户限购记录"""
is_passive = fields.BooleanField(default=False)
"""是否为被动道具"""
icon = fields.TextField(null=True)
"""图标路径"""
_idx1 = db.Index("goods_group_users_idx1", "goods_name", unique=True)
class Meta:
table = "goods_info"
table_description = "商品数据表"
@classmethod
async def add_goods(
cls,
goods_name: str,
goods_price: int,
goods_description: str,
goods_discount: float = 1,
goods_limit_time: int = 0,
daily_limit: int = 0,
is_passive: bool = False,
icon: Optional[str] = None,
) -> bool:
cls,
goods_name: str,
goods_price: int,
goods_description: str,
goods_discount: float = 1,
goods_limit_time: int = 0,
daily_limit: int = 0,
is_passive: bool = False,
icon: Optional[str] = None,
):
"""
说明:
添加商品
@@ -46,22 +58,17 @@ class GoodsInfo(db.Model):
:param is_passive: 是否为被动道具
:param icon: 图标
"""
try:
if not await cls.get_goods_info(goods_name):
await cls.create(
goods_name=goods_name,
goods_price=goods_price,
goods_description=goods_description,
goods_discount=goods_discount,
goods_limit_time=goods_limit_time,
daily_limit=daily_limit,
is_passive=is_passive,
icon=icon
)
return True
except Exception as e:
logger.error(f"GoodsInfo add_goods {goods_name} 发生错误 {type(e)}:{e}")
return False
if not await cls.filter(goods_name=goods_name).first():
await cls.create(
goods_name=goods_name,
goods_price=goods_price,
goods_description=goods_description,
goods_discount=goods_discount,
goods_limit_time=goods_limit_time,
daily_limit=daily_limit,
is_passive=is_passive,
icon=icon,
)
@classmethod
async def delete_goods(cls, goods_name: str) -> bool:
@@ -71,28 +78,23 @@ class GoodsInfo(db.Model):
参数:
:param goods_name: 商品名称
"""
query = (
await cls.query.where(cls.goods_name == goods_name)
.with_for_update()
.gino.first()
)
if not query:
return False
await query.delete()
return True
if goods := await cls.get_or_none(goods_name=goods_name):
await goods.delete()
return True
return False
@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,
daily_limit: Optional[int] = None,
is_passive: Optional[bool] = None,
icon: Optional[str] = None,
) -> bool:
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,
is_passive: Optional[bool] = None,
icon: Optional[str] = None,
):
"""
说明:
更新商品信息
@@ -106,37 +108,25 @@ class GoodsInfo(db.Model):
:param is_passive: 是否为被动
:param icon: 图标
"""
try:
query = (
await cls.query.where(cls.goods_name == goods_name)
.with_for_update()
.gino.first()
if goods := await cls.get_or_none(goods_name=goods_name):
await cls.update_or_create(
goods_name=goods_name,
defaults={
"goods_price": goods_price or goods.goods_price,
"goods_description": goods_description or goods.goods_description,
"goods_discount": goods_discount or goods.goods_discount,
"goods_limit_time": goods_limit_time
if goods_limit_time is not None
else goods.goods_limit_time,
"daily_limit": daily_limit
if daily_limit is not None
else goods.daily_limit,
"is_passive": is_passive
if is_passive is not None
else goods.is_passive,
"icon": icon or goods.icon,
},
)
if not query:
return False
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,
is_passive=is_passive if is_passive is not None else query.is_passive,
icon=icon or query.icon
).apply()
return True
except Exception as e:
logger.error(f"GoodsInfo update_goods 发生错误 {type(e)}:{e}")
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()
@classmethod
async def get_all_goods(cls) -> List["GoodsInfo"]:
@@ -144,7 +134,7 @@ class GoodsInfo(db.Model):
说明:
获得全部有序商品对象
"""
query = await cls.query.gino.all()
query = await cls.all()
id_lst = [x.id for x in query]
goods_lst = []
for _ in range(len(query)):
@@ -155,7 +145,7 @@ class GoodsInfo(db.Model):
@classmethod
async def add_user_daily_purchase(
cls, goods: "GoodsInfo", user_id: int, group_id: int, num: int = 1
cls, goods: "GoodsInfo", user_id_: int, group_id_: int, num: int = 1
):
"""
说明:
@@ -166,19 +156,19 @@ class GoodsInfo(db.Model):
:param group_id: 群号
:param num: 数量
"""
user_id = str(user_id)
group_id = str(group_id)
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()
await goods.save(update_fields=["daily_purchase_limit"])
@classmethod
async def check_user_daily_purchase(
cls, goods: "GoodsInfo", user_id: int, group_id: int, num: int = 1
cls, goods: "GoodsInfo", user_id_: int, group_id_: int, num: int = 1
) -> Tuple[bool, int]:
"""
说明:
@@ -189,8 +179,8 @@ class GoodsInfo(db.Model):
:param group_id: 群号
:param num: 数量
"""
user_id = str(user_id)
group_id = str(group_id)
user_id = str(user_id_)
group_id = str(group_id_)
if goods and goods.daily_limit > 0:
if (
not goods.daily_limit
@@ -204,10 +194,3 @@ class GoodsInfo(db.Model):
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 -104
View File
@@ -1,111 +1,24 @@
from services.db_context import db
from services.log import logger
from typing import List, Optional
from tortoise import fields
class GroupInfo(db.Model):
__tablename__ = "group_info"
from services.db_context import Model
from services.log import logger
group_id = db.Column(db.BigInteger(), nullable=False, primary_key=True)
group_name = db.Column(db.Unicode(), nullable=False, default="")
max_member_count = db.Column(db.Integer(), nullable=False, default=0)
member_count = db.Column(db.Integer(), nullable=False, default=0)
group_flag = db.Column(db.Integer(), nullable=False, default=0)
_idx1 = db.Index("group_info_idx1", "group_id", unique=True)
class GroupInfo(Model):
@classmethod
async def get_group_info(cls, group_id: int) -> "GroupInfo":
"""
说明:
获取群信息
参数:
:param group_id: 群号
"""
query = cls.query.where(cls.group_id == group_id)
return await query.gino.first()
group_id = fields.BigIntField(pk=True)
"""群聊id"""
group_name = fields.TextField(default="")
"""群聊名称"""
max_member_count = fields.IntField(default=0)
"""最大人数"""
member_count = fields.IntField(default=0)
"""当前人数"""
group_flag: int = fields.IntField(default=0)
"""群认证标记"""
@classmethod
async def add_group_info(
cls,
group_id: int,
group_name: str,
max_member_count: int,
member_count: int,
group_flag: Optional[int] = None,
) -> bool:
"""
说明:
添加群信息
参数:
:param group_id: 群号
:param group_name: 群名称
:param max_member_count: 群员最大数量
:param member_count: 群员数量
:param group_flag: 群认证,0为未认证,1为认证
"""
try:
group = (
await cls.query.where(cls.group_id == group_id)
.with_for_update()
.gino.first()
)
if group:
await group.update(
group_name=group_name,
max_member_count=max_member_count,
member_count=member_count,
).apply()
if group_flag is not None:
await group.update(group_flag=group_flag).apply()
else:
await cls.create(
group_id=group_id,
group_name=group_name,
max_member_count=max_member_count,
member_count=member_count,
group_flag=group_flag,
)
return True
except Exception as e:
logger.info(f"GroupInfo 调用 add_group_info 发生错误 {type(e)}:{e}")
return False
@classmethod
async def delete_group_info(cls, group_id: int):
"""
说明:
删除群信息
参数:
:param group_id: 群号
"""
await cls.delete.where(cls.group_id == group_id).gino.status()
@classmethod
async def get_all_group(cls) -> List["GroupInfo"]:
"""
说明:
获取所有群对象
"""
query = await cls.query.gino.all()
return query
@classmethod
async def set_group_flag(cls, group_id: int, group_flag: int) -> bool:
"""
设置群认证
:param group_id: 群号
:param group_flag: 群认证,0为未认证,1为认证
"""
group = (
await cls.query.where(cls.group_id == group_id)
.with_for_update()
.gino.first()
)
if group:
if group.group_flag != group_flag:
await group.update(
group_flag=group_flag,
).apply()
return True
return False
class Meta:
table = "group_info"
table_description = "群聊信息表"
+63 -141
View File
@@ -1,116 +1,49 @@
from datetime import datetime
from typing import List, Optional, Set
from tortoise import fields
from configs.config import Config
from services.db_context import db
from typing import List, Optional
from services.db_context import Model
from services.log import logger
class GroupInfoUser(db.Model):
__tablename__ = "group_info_users"
class GroupInfoUser(Model):
id = db.Column(db.Integer(), primary_key=True)
user_qq = db.Column(db.BigInteger(), nullable=False)
user_name = db.Column(db.Unicode(), nullable=False)
group_id = db.Column(db.BigInteger(), nullable=False)
user_join_time = db.Column(db.DateTime(), nullable=False)
nickname = db.Column(db.Unicode())
uid = db.Column(db.BigInteger())
id = fields.IntField(pk=True, generated=True, auto_increment=True)
"""自增id"""
user_qq = fields.BigIntField()
"""用户id"""
user_name = fields.CharField(255, default="")
"""用户昵称"""
group_id = fields.BigIntField()
"""群聊id"""
user_join_time: datetime = fields.DatetimeField(null=True)
"""用户入群时间"""
nickname = fields.CharField(255, null=True)
"""群聊昵称"""
uid = fields.BigIntField(null=True)
"""用户uid"""
_idx1 = db.Index("info_group_users_idx1", "user_qq", "group_id", unique=True)
class Meta:
table = "group_info_users"
table_description = "群员信息数据表"
unique_together = ("user_qq", "group_id")
@classmethod
async def add_member_info(
cls,
user_qq: int,
group_id: int,
user_name: str,
user_join_time: datetime,
uid: Optional[int] = None,
) -> bool:
"""
说明:
添加群内用户信息
参数:
:param user_qq: qq号
:param group_id: 群号
:param user_name: 用户名称
:param user_join_time: 入群时间
:param uid: 用户唯一 id(自动生成)
"""
query = cls.query.where(
(cls.user_qq == user_qq) & (cls.group_id == group_id)
)
try:
if not await query.gino.first():
await cls.create(
user_qq=user_qq,
user_name=user_name,
group_id=group_id,
user_join_time=user_join_time,
uid=uid
)
return True
except Exception:
return False
@classmethod
async def get_member_info(
cls, user_qq: int, group_id: int
) -> "GroupInfoUser":
"""
说明:
查询群员信息
参数:
:param user_qq: qq号
:param group_id: 群号
"""
query = cls.query.where(
(cls.user_qq == user_qq) & (cls.group_id == group_id)
)
return await query.gino.first()
@classmethod
async def delete_member_info(cls, user_qq: int, group_id: int) -> bool:
"""
说明:
删除群员信息
参数:
:param user_qq: qq号
:param group_id: 群号
"""
query = cls.query.where(
(cls.user_qq == user_qq) & (cls.group_id == group_id)
)
query = query.with_for_update()
user = await query.gino.first()
try:
if user is None:
return True
else:
await cls.delete.where(
(cls.user_qq == user_qq) & (cls.group_id == group_id)
).gino.status()
return True
except Exception:
return False
@classmethod
async def get_group_member_id_list(cls, group_id: int) -> List[int]:
async def get_group_member_id_list(cls, group_id: int) -> Set[int]:
"""
说明:
获取该群所有用户qq
参数:
:param group_id: 群号
"""
member_list = []
query = cls.query.where((cls.group_id == group_id))
for user in await query.gino.all():
member_list.append(user.user_qq)
return member_list
return set(
await cls.filter(group_id=group_id).values_list("user_qq", flat=True)
) # type: ignore
@classmethod
async def set_group_member_nickname(
cls, user_qq: int, group_id: int, nickname: str
) -> bool:
async def set_user_nickname(cls, user_qq: int, group_id: int, nickname: str):
"""
说明:
设置群员在该群内的昵称
@@ -119,14 +52,11 @@ class GroupInfoUser(db.Model):
:param group_id: 群号
:param nickname: 昵称
"""
query = cls.query.where(
(cls.user_qq == user_qq) & (cls.group_id == group_id)
await cls.update_or_create(
user_qq=user_qq,
group_id=group_id,
defaults={"nickname": nickname},
)
user = await query.with_for_update().gino.first()
if user:
await user.update(nickname=nickname).apply()
return True
return False
@classmethod
async def get_user_all_group(cls, user_qq: int) -> List[int]:
@@ -136,13 +66,12 @@ class GroupInfoUser(db.Model):
参数:
:param user_qq: 用户qq
"""
query = await cls.query.where(cls.user_qq == user_qq).gino.all()
if query:
query = [x.group_id for x in query]
return query
return list(
await cls.filter(user_qq=user_qq).values_list("group_id", flat=True)
) # type: ignore
@classmethod
async def get_group_member_nickname(cls, user_qq: int, group_id: int) -> str:
async def get_user_nickname(cls, user_qq: int, group_id: int) -> str:
"""
说明:
获取用户在该群的昵称
@@ -150,46 +79,39 @@ class GroupInfoUser(db.Model):
:param user_qq: qq号
:param group_id: 群号
"""
query = cls.query.where(
(cls.user_qq == user_qq) & (cls.group_id == group_id)
)
user = await query.gino.first()
if user:
if user := await cls.get_or_none(user_qq=user_qq, group_id=group_id):
if user.nickname:
_tmp = ""
black_word = Config.get_config("nickname", "BLACK_WORD")
if black_word:
nickname = ""
if black_word := Config.get_config("nickname", "BLACK_WORD"):
for x in user.nickname:
_tmp += "*" if x in black_word else x
return _tmp
nickname += "*" if x in black_word else x
return nickname
return user.nickname
return ""
@classmethod
async def get_group_member_uid(cls, user_qq: int, group_id: int) -> Optional[str]:
query = cls.query.where(
(cls.user_qq == user_qq) & (cls.group_id == group_id)
async def get_group_member_uid(cls, user_qq: int, group_id: int) -> Optional[int]:
logger.debug(
f"GroupInfoUser 尝试获取 用户[<u><e>{user_qq}</e></u>] 群聊[<u><e>{group_id}</e></u>] UID"
)
user = await query.gino.first()
_max_uid = cls.query.where((cls.user_qq == 114514) & (cls.group_id == 114514)).with_for_update()
_max_uid_user = await _max_uid.gino.first()
user, _ = await cls.get_or_create(user_qq=user_qq, group_id=group_id)
_max_uid_user, _ = await cls.get_or_create(user_qq=114514, group_id=114514)
_max_uid = _max_uid_user.uid
if not user or not user.uid:
all_user = await cls.query.where(cls.user_qq == user_qq).gino.all()
if not user.uid:
all_user = await cls.filter(user_qq=user_qq).all()
for x in all_user:
if x.uid:
return x.uid
else:
if not user:
await GroupInfoUser.add_member_info(user_qq, group_id, '', datetime.min)
user = await cls.query.where(
(cls.user_qq == user_qq) & (cls.group_id == group_id)
).gino.first()
await user.update(
uid=_max_uid + 1,
).apply()
await _max_uid_user.update(
uid=_max_uid + 1,
).apply()
return user.uid if user and user.uid else None
user.uid = _max_uid + 1
_max_uid_user.uid = _max_uid + 1
await cls.bulk_update([user, _max_uid_user], ["uid"])
logger.debug(
f"GroupInfoUser 获取 用户[<u><e>{user_qq}</e></u>] 群聊[<u><e>{group_id}</e></u>] UID: {user.uid}"
)
return user.uid
@classmethod
async def _run_script(cls):
return [
"alter table group_info_users alter user_join_time drop not null;" # 允许 user_join_time 为空
]
+37 -66
View File
@@ -1,18 +1,25 @@
from asyncpg import UniqueViolationError
from tortoise import fields
from services.db_context import db
from services.db_context import Model
class LevelUser(db.Model):
__tablename__ = "level_users"
class LevelUser(Model):
id = db.Column(db.Integer(), primary_key=True)
user_qq = db.Column(db.BigInteger(), nullable=False)
group_id = db.Column(db.BigInteger(), nullable=False)
user_level = db.Column(db.BigInteger(), nullable=False)
group_flag = db.Column(db.Integer(), nullable=False, default=0)
id = fields.IntField(pk=True, generated=True, auto_increment=True)
"""自增id"""
user_qq = fields.BigIntField()
"""用户id"""
group_id = fields.BigIntField()
"""群聊id"""
user_level = fields.BigIntField()
"""用户权限等级"""
group_flag = fields.IntField(default=0)
"""特殊标记,是否随群管理员变更而设置权限"""
_idx1 = db.Index("level_group_users_idx1", "user_qq", "group_id", unique=True)
class Meta:
table = "level_users"
table_description = "用户权限数据库"
unique_together = ("user_qq", "group_id")
@classmethod
async def get_user_level(cls, user_qq: int, group_id: int) -> int:
@@ -23,17 +30,14 @@ class LevelUser(db.Model):
:param user_qq: qq号
:param group_id: 群号
"""
query = cls.query.where((cls.user_qq == user_qq) & (cls.group_id == group_id))
user = await query.gino.first()
if user:
if user := await cls.get_or_none(user_qq=user_qq, group_id=group_id):
return user.user_level
else:
return -1
return -1
@classmethod
async def set_level(
cls, user_qq: int, group_id: int, level: int, group_flag: int = 0
) -> bool:
):
"""
说明:
设置用户在群内的权限
@@ -43,23 +47,11 @@ class LevelUser(db.Model):
:param level: 权限等级
:param group_flag: 是否被自动更新刷新权限 0:是,1:否
"""
query = cls.query.where((cls.user_qq == user_qq) & (cls.group_id == group_id))
query = query.with_for_update()
user = await query.gino.first()
try:
if not user:
await cls.create(
user_qq=user_qq,
group_id=group_id,
user_level=level,
group_flag=group_flag,
)
return True
else:
await user.update(user_level=level, group_flag=group_flag).apply()
return False
except UniqueViolationError:
return False
await cls.update_or_create(
user_qq=user_qq,
group_id=group_id,
defaults={"user_level": level, "group_flag": group_flag},
)
@classmethod
async def delete_level(cls, user_qq: int, group_id: int) -> bool:
@@ -70,14 +62,10 @@ class LevelUser(db.Model):
:param user_qq: qq号
:param group_id: 群号
"""
query = cls.query.where((cls.user_qq == user_qq) & (cls.group_id == group_id))
query = query.with_for_update()
user = await query.gino.first()
if user is None:
return False
else:
if user := await cls.get_or_none(user_qq=user_qq, group_id=group_id):
await user.delete()
return True
return False
@classmethod
async def check_level(cls, user_qq: int, group_id: int, level: int) -> bool:
@@ -89,25 +77,14 @@ class LevelUser(db.Model):
:param group_id: 群号
:param level: 权限等级
"""
if group_id != 0:
query = cls.query.where(
(cls.user_qq == user_qq) & (cls.group_id == group_id)
)
user = await query.gino.first()
if user is None:
return False
user_level = user.user_level
if group_id:
if user := await cls.get_or_none(user_qq=user_qq, group_id=group_id):
return user.user_level >= level
else:
query = cls.query.where(cls.user_qq == user_qq)
highest_level = 0
for user in await query.gino.all():
if user.user_level > highest_level:
highest_level = user.user_level
user_level = highest_level
if user_level >= level:
return True
else:
return False
user_list = await cls.filter(user_qq=user_qq).all()
user = max(user_list, key=lambda x: x.user_level)
return user.user_level >= level
return False
@classmethod
async def is_group_flag(cls, user_qq: int, group_id: int) -> bool:
@@ -118,12 +95,6 @@ class LevelUser(db.Model):
:param user_qq: qq号
:param group_id: 群号
"""
user = await cls.query.where(
(cls.user_qq == user_qq) & (cls.group_id == group_id)
).gino.first()
if not user:
return False
if user.group_flag == 1:
return True
else:
return False
if user := await cls.get_or_none(user_qq=user_qq, group_id=group_id):
return user.group_flag == 1
return False
+47 -75
View File
@@ -1,99 +1,71 @@
from datetime import datetime
from typing import List
from services.db_context import db
from typing import List, Literal, Optional, Tuple
from tortoise import fields
from services.db_context import Model
class SignGroupUser(db.Model):
__tablename__ = "sign_group_users"
class SignGroupUser(Model):
id = db.Column(db.Integer(), primary_key=True)
user_qq = db.Column(db.BigInteger(), nullable=False)
group_id = db.Column(db.BigInteger(), nullable=False)
checkin_count = db.Column(db.Integer(), nullable=False)
checkin_time_last = db.Column(db.DateTime(timezone=True), nullable=False)
impression = db.Column(db.Numeric(scale=3, asdecimal=False), nullable=False)
add_probability = db.Column(
db.Numeric(scale=3, asdecimal=False), nullable=False, default=0
)
specify_probability = db.Column(
db.Numeric(scale=3, asdecimal=False), nullable=False, default=0
)
id = fields.IntField(pk=True, generated=True, auto_increment=True)
"""自增id"""
user_qq = fields.BigIntField()
"""用户id"""
group_id = fields.BigIntField()
"""群聊id"""
checkin_count = fields.IntField(default=0)
"""签到次数"""
checkin_time_last = fields.DatetimeField(auto_now=True)
"""最后签到时间"""
impression = fields.DecimalField(10, 3, default=0)
"""好感度"""
add_probability = fields.DecimalField(10, 3, default=0)
"""双倍签到增加概率"""
specify_probability = fields.DecimalField(10, 3, default=0)
"""使用指定双倍概率"""
# specify_probability = fields.DecimalField(10, 3, default=0)
_idx1 = db.Index("sign_group_users_idx1", "user_qq", "group_id", unique=True)
class Meta:
table = "sign_group_users"
table_description = "群员签到数据表"
unique_together = ("user_qq", "group_id")
@classmethod
async def ensure(
cls, user_qq: int, group_id: int, for_update: bool = False
) -> "SignGroupUser":
"""
说明:
获取签到用户
参数:
:param user_qq: 用户qq
:param group_id: 所在群聊
:param for_update: 是否存在修改数据
"""
query = cls.query.where(
(cls.user_qq == user_qq) & (cls.group_id == group_id)
)
if for_update:
query = query.with_for_update()
user = await query.gino.first()
return user or await cls.create(
user_qq=user_qq,
group_id=group_id,
checkin_count=0,
checkin_time_last=datetime.min, # 从未签到过
impression=0,
)
@classmethod
async def get_user_all_data(cls, user_qq: int) -> List["SignGroupUser"]:
"""
说明:
获取某用户所有数据
参数:
:param user_qq: 用户qq
"""
query = cls.query.where(cls.user_qq == user_qq)
query = query.with_for_update()
return await query.gino.all()
@classmethod
async def sign(cls, user: "SignGroupUser", impression: float, checkin_time_last: datetime):
async def sign(cls, user: "SignGroupUser", impression: float):
"""
说明:
签到
说明:
:param user: 用户
:param impression: 增加的好感度
:param checkin_time_last: 签到时间
"""
await user.update(
checkin_count=user.checkin_count + 1,
checkin_time_last=checkin_time_last,
impression=user.impression + impression,
add_probability=0,
specify_probability=0,
).apply()
user.checkin_count = user.checkin_count + 1
user.add_probability = 0
user.specify_probability = 0
user.impression = float(user.impression) + impression
await user.save()
@classmethod
async def get_all_impression(cls, group_id: int) -> "list, list, list":
async def get_all_impression(
cls, group_id: Optional[int]
) -> Tuple[List[int], List[int], List[float]]:
"""
说明:
获取该群所有用户 id 及对应 好感度
参数:
:param group_id: 群号
"""
impression_list = []
user_qq_list = []
user_group = []
if group_id:
query = cls.query.where(cls.group_id == group_id)
query = cls.filter(group_id=group_id)
else:
query = cls.query
for user in await query.gino.all():
impression_list.append(user.impression)
user_qq_list.append(user.user_qq)
user_group.append(user.group_id)
return user_qq_list, impression_list, user_group
query = cls
value_list = await query.all().values_list("user_qq", "group_id", "impression") # type: ignore
qq_list = []
group_list = []
impression_list = []
for value in value_list:
qq_list.append(value[0])
group_list.append(value[1])
impression_list.append(float(value[2]))
return qq_list, impression_list, group_list
+23 -53
View File
@@ -1,59 +1,29 @@
from datetime import datetime
from services.db_context import db
from tortoise import fields
from services.db_context import Model
class UserShopGoldLog(db.Model):
__tablename__ = "user_shop_gold_log"
id = db.Column(db.Integer(), primary_key=True)
user_qq = db.Column(db.BigInteger(), nullable=False)
group_id = db.Column(db.BigInteger(), nullable=False)
type = db.Column(db.Integer(), nullable=False) # 0: 购买,1: 使用,2: 插件
name = db.Column(db.String())
spend_gold = db.Column(db.Integer(), nullable=False)
num = db.Column(db.Integer(), nullable=False)
create_time = db.Column(db.DateTime(timezone=True), nullable=False)
class UserShopGoldLog(Model):
@classmethod
async def add_shop_log(
cls,
user_qq: int,
group_id: int,
type_: int,
name: str,
num: int,
spend_gold: int = 0,
):
"""
说明:
添加商店购买或使用日志
参数:
:param user_qq: qq号
:param group_id: 所在群号
:param type_: 类型
:param name: 商品名称
:param num: 数量
:param spend_gold: 花费金币
"""
await cls.create(
user_qq=user_qq,
group_id=group_id,
type=type_,
name=name,
num=num,
spend_gold=spend_gold,
create_time=datetime.now(),
)
id = fields.IntField(pk=True, generated=True, auto_increment=True)
"""自增id"""
user_qq = fields.BigIntField()
"""用户id"""
group_id = fields.BigIntField()
"""群聊id"""
type = fields.IntField()
"""金币使用类型 0: 购买, 1: 使用, 2: 插件"""
name = fields.CharField(255)
"""商品/插件 名称"""
spend_gold = fields.IntField(default=0)
"""花费金币"""
num = fields.IntField()
"""数量"""
create_time = fields.DatetimeField(auto_now_add=True)
"""创建时间"""
@classmethod
async def get_user_log(cls, user_qq: int, group_id: int) -> "UserShopGoldLog":
"""
说明:
获取用户日志
参数:
:param user_qq: qq号
:param group_id: 所在群号
"""
return await cls.query.where(
(cls.user_qq == user_qq) & (cls.group_qq == group_id)
).first()
class Meta:
table = "user_shop_gold_log"
table_description = "金币使用日志表"
+14 -15
View File
@@ -1,20 +1,20 @@
from utils.message_builder import image
from configs.path_config import IMAGE_PATH
from nonebot import on_command
from nonebot.rule import to_me
import os
import random
from nonebot import on_command, on_keyword
from nonebot.adapters.onebot.v11 import GroupMessageEvent
from nonebot.adapters.onebot.v11.permission import GROUP
from utils.utils import FreqLimiter
from configs.config import NICKNAME
import random
from nonebot import on_keyword
import os
from nonebot.rule import to_me
from configs.config import NICKNAME
from configs.path_config import IMAGE_PATH
from utils.message_builder import image
from utils.utils import FreqLimiter
__zx_plugin_name__ = "基本设置 [Hidden]"
__plugin_usage__ = "用法: 无"
__plugin_version__ = 0.1
__plugin_author__ = 'HibiKier'
__plugin_author__ = "HibiKier"
_flmt = FreqLimiter(300)
@@ -29,7 +29,7 @@ async def _(event: GroupMessageEvent):
return
_flmt.start_cd(event.group_id)
await config_play_game.finish(
image(random.choice(os.listdir(IMAGE_PATH / "dayouxi")), "dayouxi")
image(IMAGE_PATH / random.choice(os.listdir(IMAGE_PATH / "dayouxi")))
)
@@ -40,14 +40,14 @@ self_introduction = on_command(
@self_introduction.handle()
async def _():
if NICKNAME.find('真寻') != -1:
if NICKNAME.find("真寻") != -1:
result = (
"我叫绪山真寻\n"
"你们可以叫我真寻,小真寻,哪怕你们叫我小寻子我也能接受!\n"
"年龄的话我还是个**岁初中生(至少现在是)\n"
"身高保密!!!(也就比美波里(姐姐..(妹妹))矮一点)\n"
"我生日是在3月6号, 能记住的话我会很高兴的\n现在是自宅警备系的现役JC\n"
"最好的朋友是椛!\n" + image("zhenxun")
"最好的朋友是椛!\n" + image("zhenxun.jpg")
)
await self_introduction.finish(result)
@@ -57,5 +57,4 @@ my_wife = on_keyword({"老婆"}, rule=to_me(), priority=5, block=True)
@my_wife.handle()
async def _():
await my_wife.finish(image("laopo.jpg", "other"))
await my_wife.finish(image(IMAGE_PATH / "other" / "laopo.jpg"))
+15 -13
View File
@@ -1,18 +1,16 @@
from typing import List
from nonebot import on_message
from nonebot.adapters.onebot.v11 import (
Bot,
GroupMessageEvent,
Message,
MessageEvent,
)
from nonebot.adapters.onebot.v11 import Bot, GroupMessageEvent, Message, MessageEvent
from nonebot.rule import to_me
from configs.config import NICKNAME, Config
from models.friend_user import FriendUser
from models.group_member_info import GroupInfoUser
from services.log import logger
from utils.utils import get_message_img, get_message_text
from .data_source import get_chat_result, hello, no_result
from configs.config import NICKNAME, Config
__zx_plugin_name__ = "AI"
__plugin_usage__ = f"""
@@ -26,12 +24,18 @@ __plugin_settings__ = {
"cmd": ["Ai", "ai", "AI", "aI"],
}
__plugin_configs__ = {
"TL_KEY": {"value": [], "help": "图灵Key"},
"ALAPI_AI_CHECK": {"value": False, "help": "是否检测青云客骂娘回复", "default_value": False},
"TL_KEY": {"value": [], "help": "图灵Key", "type": List[str]},
"ALAPI_AI_CHECK": {
"value": False,
"help": "是否检测青云客骂娘回复",
"default_value": False,
"type": bool,
},
"TEXT_FILTER": {
"value": ["鸡", "口交"],
"help": "文本过滤器,将敏感词更改为*",
"default_value": [],
"type": List[str],
},
}
Config.add_plugin_config(
@@ -61,11 +65,9 @@ async def _(bot: Bot, event: MessageEvent):
await ai.finish(hello())
img = img[0] if img else ""
if isinstance(event, GroupMessageEvent):
nickname = await GroupInfoUser.get_group_member_nickname(
event.user_id, event.group_id
)
nickname = await GroupInfoUser.get_user_nickname(event.user_id, event.group_id)
else:
nickname = await FriendUser.get_friend_nickname(event.user_id)
nickname = await FriendUser.get_user_nickname(event.user_id)
if not nickname:
if isinstance(event, GroupMessageEvent):
nickname = event.sender.card or event.sender.nickname
+21 -18
View File
@@ -1,11 +1,13 @@
import os
import random
import re
from utils.http_utils import AsyncHttpx
from configs.path_config import IMAGE_PATH, DATA_PATH
from configs.config import NICKNAME, Config
from configs.path_config import DATA_PATH, IMAGE_PATH
from services.log import logger
from utils.message_builder import image, face
from configs.config import Config, NICKNAME
from utils.http_utils import AsyncHttpx
from utils.message_builder import face, image
from .utils import ai_message_manager
try:
@@ -124,7 +126,9 @@ async def xie_ai(text: str) -> str:
:param text: 问题
:return: 青云可回复
"""
res = await AsyncHttpx.get(f"http://api.qingyunke.com/api.php?key=free&appid=0&msg={text}")
res = await AsyncHttpx.get(
f"http://api.qingyunke.com/api.php?key=free&appid=0&msg={text}"
)
content = ""
try:
data = json.loads(res.text)
@@ -176,9 +180,9 @@ def hello() -> str:
)
img = random.choice(os.listdir(IMAGE_PATH / "zai"))
if img[-4:] == ".gif":
result += image(img, "zai")
result += image(IMAGE_PATH / "zai" / img)
else:
result += image(img, "zai")
result += image(IMAGE_PATH / "zai" / img)
return result
@@ -187,17 +191,16 @@ def no_result() -> str:
"""
没有回答时的回复
"""
return (
random.choice(
[
"你在说啥子?",
f"纯洁的{NICKNAME}没听懂",
"下次再告诉你(下次一定)",
"你觉得我听懂了吗?嗯?",
"我!不!知!道!",
]
)
+ image(random.choice(os.listdir(IMAGE_PATH / "noresult")), "noresult")
return random.choice(
[
"你在说啥子?",
f"纯洁的{NICKNAME}没听懂",
"下次再告诉你(下次一定)",
"你觉得我听懂了吗?嗯?",
"我!不!知!道!",
]
) + image(
IMAGE_PATH / "noresult" / random.choice(os.listdir(IMAGE_PATH / "noresult"))
)
+5 -8
View File
@@ -1,14 +1,11 @@
from configs.config import Config
from pathlib import Path
import nonebot
from configs.config import Config
Config.add_plugin_config(
"alapi",
"ALAPI_TOKEN",
None,
help_="在https://admin.alapi.cn/user/login登录后获取token"
"alapi", "ALAPI_TOKEN", None, help_="在https://admin.alapi.cn/user/login登录后获取token"
)
nonebot.load_plugins("plugins/alapi")
nonebot.load_plugins(str(Path(__file__).parent.resolve()))
+35 -31
View File
@@ -1,28 +1,29 @@
from nonebot import on_command, on_regex
from nonebot.typing import T_State
from nonebot.adapters.onebot.v11 import Bot, MessageEvent, GroupMessageEvent, Message
from typing import Any, Optional, Tuple
import nonebot
from nonebot import Driver, on_command, on_regex
from nonebot.adapters.onebot.v11 import Bot, GroupMessageEvent, Message, MessageEvent
from nonebot.params import ArgStr, CommandArg, RegexGroup
from nonebot.typing import T_State
from configs.config import Config
from models.level_user import LevelUser
from services.log import logger
from utils.image_utils import text2image
from utils.manager import group_manager
from utils.message_builder import image
from utils.utils import get_bot, is_number, scheduler
from .data_source import (
BilibiliSub,
SubManager,
add_live_sub,
delete_sub,
add_up_sub,
add_season_sub,
add_up_sub,
delete_sub,
get_media_id,
get_sub_status,
SubManager,
BilibiliSub,
)
from models.level_user import LevelUser
from utils.manager import group_manager
from configs.config import Config
from utils.utils import is_number, scheduler, get_bot
from typing import Optional, Tuple, Any
from services.log import logger
from nonebot import Driver
from nonebot.params import CommandArg, ArgStr, RegexGroup
import nonebot
__zx_plugin_name__ = "B站订阅"
__plugin_usage__ = """
@@ -54,16 +55,19 @@ __plugin_configs__ = {
"value": 5,
"help": "群内bilibili订阅需要管理的权限",
"default_value": 5,
"type": int,
},
"LIVE_MSG_AT_ALL": {
"value": False,
"help": "直播提醒是否AT全体(仅在真寻是管理员时生效)",
"default_value": False,
"type": bool,
},
"UP_MSG_AT_ALL": {
"value": False,
"help": "UP动态投稿提醒是否AT全体(仅在真寻是管理员时生效)",
"default_value": False,
"type": bool,
},
}
@@ -168,20 +172,20 @@ async def _(
sub_user: str = ArgStr("sub_user"),
):
if sub_type in ["主播", "直播"]:
result = await BilibiliSub.delete_bilibili_sub(int(id_),sub_user,"live")
result = await BilibiliSub.delete_bilibili_sub(int(id_), sub_user, "live")
elif sub_type.lower() in ["up", "用户"]:
result = await BilibiliSub.delete_bilibili_sub(int(id_),sub_user,"up")
else: result = await BilibiliSub.delete_bilibili_sub(int(id_),sub_user)
result = await BilibiliSub.delete_bilibili_sub(int(id_), sub_user, "up")
else:
result = await BilibiliSub.delete_bilibili_sub(int(id_), sub_user)
if result:
await del_sub.send(f"删除订阅id:{id_} 成功...")
logger.info(
f"(USER {event.user_id}, GROUP "
f"{event.group_id if isinstance(event, GroupMessageEvent) else 'private'})"
f" 删除订阅 {id_}"
)
f"(USER {event.user_id}, GROUP "
f"{event.group_id if isinstance(event, GroupMessageEvent) else 'private'})"
f" 删除订阅 {id_}"
)
else:
await del_sub.send(f"删除订阅id:{id_} 失败...")
@show_sub_info.handle()
@@ -190,7 +194,7 @@ async def _(event: MessageEvent):
id_ = f"{event.group_id}"
else:
id_ = f"{event.user_id}"
data = await BilibiliSub.get_sub_data(id_)
data = await BilibiliSub.filter(sub_users__contains=id_).all()
live_rst = ""
up_rst = ""
season_rst = ""
@@ -217,11 +221,9 @@ async def _(event: MessageEvent):
)
await show_sub_info.send(
image(
b64=(
await text2image(
live_rst + up_rst + season_rst, padding=10, color="#f9f6f2"
)
).pic2bs4()
await text2image(
live_rst + up_rst + season_rst, padding=10, color="#f9f6f2"
)
)
)
@@ -277,7 +279,9 @@ async def send_sub_msg(rst: str, sub: BilibiliSub, bot: Bot):
):
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))
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:
+98 -138
View File
@@ -1,34 +1,33 @@
from bilireq.exceptions import ResponseCodeError
from nonebot.adapters.onebot.v11 import MessageSegment
from utils.manager import resources_manager
import random
from asyncio.exceptions import TimeoutError
from datetime import datetime
from typing import Optional, Tuple
from utils.utils import get_bot
from .model import BilibiliSub
from bilireq.live import get_room_info_by_id
from .utils import get_meta, get_user_card
from utils.message_builder import image
from bilireq.user import get_videos
# from .utils import get_videos
from bilireq import dynamic
from typing import Optional, Tuple
from configs.path_config import IMAGE_PATH
from datetime import datetime
from utils.browser import get_browser
from services.db_context import db
from bilireq.exceptions import ResponseCodeError
from bilireq.live import get_room_info_by_id
from bilireq.user import get_videos
from nonebot.adapters.onebot.v11 import MessageSegment
from configs.path_config import IMAGE_PATH, TEMP_PATH
from services.log import logger
from utils.http_utils import AsyncHttpx
import random
from utils.browser import get_browser
from utils.http_utils import AsyncHttpx, AsyncPlaywright
from utils.manager import resources_manager
from utils.message_builder import image
from utils.utils import get_bot
from .model import BilibiliSub
from .utils import get_meta, get_user_card
SEARCH_URL = "https://api.bilibili.com/x/web-interface/search/all/v2"
DYNAMIC_PATH = IMAGE_PATH / "bilibili_sub" / "dynamic"
DYNAMIC_PATH.mkdir(exist_ok=True, parents=True)
bilibili_search_url = "https://api.bilibili.com/x/web-interface/search/all/v2"
dynamic_path = IMAGE_PATH / "bilibili_sub" / "dynamic"
dynamic_path.mkdir(exist_ok=True, parents=True)
resources_manager.add_temp_dir(dynamic_path)
resources_manager.add_temp_dir(DYNAMIC_PATH)
async def add_live_sub(live_id: int, sub_user: str) -> str:
@@ -49,7 +48,7 @@ async def add_live_sub(live_id: int, sub_user: str) -> str:
short_id = live_info["short_id"]
title = live_info["title"]
live_status = live_info["live_status"]
if await BilibiliSub.add_bilibili_sub(
if await BilibiliSub.sub_handle(
room_id,
"live",
sub_user,
@@ -58,14 +57,14 @@ async def add_live_sub(live_id: int, sub_user: str) -> str:
live_status=live_status,
):
await _get_up_status(room_id)
uname = (await BilibiliSub.get_sub(room_id)).uname
uname = (await BilibiliSub.get_or_none(sub_id=room_id)).uname
return (
"已成功订阅主播:\n"
f"\ttitle:{title}\n"
f"\tname: {uname}\n"
f"\tlive_id:{room_id}\n"
f"\tuid:{uid}"
)
)
else:
return "添加订阅失败..."
except Exception as e:
@@ -80,35 +79,34 @@ async def add_up_sub(uid: int, sub_user: str) -> str:
:param sub_user: 订阅用户
"""
try:
async with db.transaction():
try:
"""bilibili_api.user库中User类的get_user_info改为bilireq.user库的get_user_info方法"""
user_info = await get_user_card(uid)
except ResponseCodeError:
return f"未找到UpId:{uid} 的信息,请检查Id是否正确"
uname = user_info["name"]
"""bilibili_api.user库中User类的get_dynamics改为bilireq.dynamic库的get_user_dynamics方法"""
dynamic_info = await dynamic.get_user_dynamics(uid)
dynamic_upload_time = 0
if dynamic_info.get("cards"):
dynamic_upload_time = dynamic_info["cards"][0]["desc"]["timestamp"]
"""bilibili_api.user库中User类的get_videos改为bilireq.user库的get_videos方法"""
video_info = await get_videos(uid)
latest_video_created = 0
if video_info["list"].get("vlist"):
latest_video_created = video_info["list"]["vlist"][0]["created"]
if await BilibiliSub.add_bilibili_sub(
uid,
"up",
sub_user,
uid=uid,
uname=uname,
dynamic_upload_time=dynamic_upload_time,
latest_video_created=latest_video_created,
):
return "已成功订阅UP:\n" f"\tname: {uname}\n" f"\tuid:{uid}"
else:
return "添加订阅失败..."
try:
"""bilibili_api.user库中User类的get_user_info改为bilireq.user库的get_user_info方法"""
user_info = await get_user_card(uid)
except ResponseCodeError:
return f"未找到UpId:{uid} 的信息,请检查Id是否正确"
uname = user_info["name"]
"""bilibili_api.user库中User类的get_dynamics改为bilireq.dynamic库的get_user_dynamics方法"""
dynamic_info = await dynamic.get_user_dynamics(uid)
dynamic_upload_time = 0
if dynamic_info.get("cards"):
dynamic_upload_time = dynamic_info["cards"][0]["desc"]["timestamp"]
"""bilibili_api.user库中User类的get_videos改为bilireq.user库的get_videos方法"""
video_info = await get_videos(uid)
latest_video_created = 0
if video_info["list"].get("vlist"):
latest_video_created = video_info["list"]["vlist"][0]["created"]
if await BilibiliSub.sub_handle(
uid,
"up",
sub_user,
uid=uid,
uname=uname,
dynamic_upload_time=dynamic_upload_time,
latest_video_created=latest_video_created,
):
return "已成功订阅UP:\n" f"\tname: {uname}\n" f"\tuid:{uid}"
else:
return "添加订阅失败..."
except Exception as e:
logger.error(f"订阅Up uid:{uid} 发生了错误 {type(e)}:{e}")
return "添加订阅失败..."
@@ -121,30 +119,29 @@ async def add_season_sub(media_id: int, sub_user: str) -> str:
:param sub_user: 订阅用户
"""
try:
async with db.transaction():
try:
"""bilibili_api.bangumi库中get_meta改为bilireq.bangumi库的get_meta方法"""
season_info = await get_meta(media_id)
except ResponseCodeError:
return f"未找到media_id:{media_id} 的信息,请检查Id是否正确"
season_id = season_info["media"]["season_id"]
season_current_episode = season_info["media"]["new_ep"]["index"]
season_name = season_info["media"]["title"]
if await BilibiliSub.add_bilibili_sub(
media_id,
"season",
sub_user,
season_name=season_name,
season_id=season_id,
season_current_episode=season_current_episode,
):
return (
"已成功订阅番剧:\n"
f"\ttitle: {season_name}\n"
f"\tcurrent_episode: {season_current_episode}"
)
else:
return "添加订阅失败..."
try:
"""bilibili_api.bangumi库中get_meta改为bilireq.bangumi库的get_meta方法"""
season_info = await get_meta(media_id)
except ResponseCodeError:
return f"未找到media_id:{media_id} 的信息,请检查Id是否正确"
season_id = season_info["media"]["season_id"]
season_current_episode = season_info["media"]["new_ep"]["index"]
season_name = season_info["media"]["title"]
if await BilibiliSub.sub_handle(
media_id,
"season",
sub_user,
season_name=season_name,
season_id=season_id,
season_current_episode=season_current_episode,
):
return (
"已成功订阅番剧:\n"
f"\ttitle: {season_name}\n"
f"\tcurrent_episode: {season_current_episode}"
)
else:
return "添加订阅失败..."
except Exception as e:
logger.error(f"订阅番剧 media_id:{media_id} 发生了错误 {type(e)}:{e}")
return "添加订阅失败..."
@@ -171,9 +168,7 @@ async def get_media_id(keyword: str) -> dict:
for _ in range(3):
try:
_season_data = {}
response = await AsyncHttpx.get(
bilibili_search_url, params=params, timeout=5
)
response = await AsyncHttpx.get(SEARCH_URL, params=params, timeout=5)
if response.status_code == 200:
data = response.json()
if data.get("data"):
@@ -227,9 +222,9 @@ async def _get_live_status(id_: int) -> Optional[str]:
room_id = live_info["room_id"]
live_status = live_info["live_status"]
cover = live_info["user_cover"]
sub = await BilibiliSub.get_sub(id_)
sub = await BilibiliSub.get_or_none(sub_id=id_)
if sub.live_status != live_status:
await BilibiliSub.update_sub_info(id_, live_status=live_status)
await BilibiliSub.sub_handle(id_, live_status=live_status)
if sub.live_status in [0, 2] and live_status == 1:
return (
f""
@@ -247,7 +242,7 @@ async def _get_up_status(id_: int) -> Optional[str]:
:param id_: 订阅 id
:return:
"""
_user = await BilibiliSub.get_sub(id_)
_user = await BilibiliSub.get_or_none(sub_id=id_)
"""bilibili_api.user库中User类的get_user_info改为bilireq.user库的get_user_info方法"""
user_info = await get_user_card(_user.uid)
uname = user_info["name"]
@@ -257,14 +252,14 @@ async def _get_up_status(id_: int) -> Optional[str]:
video = None
dividing_line = "\n-------------\n"
if _user.uname != uname:
await BilibiliSub.update_sub_info(id_, uname=uname)
await BilibiliSub.sub_handle(id_, uname=uname)
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)
await BilibiliSub.sub_handle(id_, dynamic_upload_time=dynamic_upload_time)
rst += f"{uname} 发布了动态!\n" f"{dynamic_img}\n{link}"
if (
latest_video_created
@@ -273,9 +268,7 @@ async def _get_up_status(id_: int) -> Optional[str]:
and _user.latest_video_created < latest_video_created
):
rst = rst + dividing_line if rst else rst
await BilibiliSub.update_sub_info(
id_, latest_video_created=latest_video_created
)
await BilibiliSub.sub_handle(id_, latest_video_created=latest_video_created)
rst += (
f'{image(video["pic"])}\n'
f"{uname} 投稿了新视频啦\n"
@@ -295,10 +288,10 @@ async def _get_season_status(id_) -> Optional[str]:
"""bilibili_api.bangumi库中get_meta改为bilireq.bangumi库的get_meta方法"""
season_info = await get_meta(id_)
title = season_info["media"]["title"]
_idx = (await BilibiliSub.get_sub(id_)).season_current_episode
_idx = (await BilibiliSub.get_or_none(sub_id=id_)).season_current_episode
new_ep = season_info["media"]["new_ep"]["index"]
if new_ep != _idx:
await BilibiliSub.update_sub_info(
await BilibiliSub.sub_handle(
id_, season_current_episode=new_ep, season_update_time=datetime.now()
)
return (
@@ -320,55 +313,22 @@ async def get_user_dynamic(
"""
"""bilibili_api.user库中User类的get_dynamics改为bilireq.dynamic库的get_user_dynamics方法"""
dynamic_info = await dynamic.get_user_dynamics(uid)
browser = await get_browser()
if dynamic_info.get("cards") and browser:
if dynamic_info.get("cards"):
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:
context = await browser.new_context()
page = await context.new_page()
try:
await page.goto(
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.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(".bili-dyn-item__main")
card = page.locator(".bili-dyn-item__main")
await card.wait_for()
await card.screenshot(
path=dynamic_path / f"{local_user.sub_id}_{dynamic_upload_time}.jpg",
)
except Exception as e:
logger.error(f"B站订阅:获取用户动态 发送错误 {type(e)}:{e}")
finally:
await context.close()
await page.close()
return (
image(
f"{local_user.sub_id}_{dynamic_upload_time}.jpg",
"bilibili_sub/dynamic",
),
dynamic_upload_time,
f"https://t.bilibili.com/{dynamic_id}"
image = await AsyncPlaywright.screenshot(
f"https://t.bilibili.com/{dynamic_id}",
DYNAMIC_PATH / f"sub_{local_user.sub_id}.png",
".bili-dyn-item__main",
wait_until="networkidle",
)
return None, 0, ''
return (
image,
dynamic_upload_time,
f"https://t.bilibili.com/{dynamic_id}",
)
return None, 0, ""
class SubManager:
+112 -186
View File
@@ -1,43 +1,58 @@
from services.log import logger
from services.db_context import db
from datetime import datetime
from typing import Optional, List
from typing import List, Optional, Tuple
from tortoise import fields
from services.db_context import Model
from services.log import logger
class BilibiliSub(db.Model):
__tablename__ = "bilibili_sub"
class BilibiliSub(Model):
id = db.Column(db.Integer(), primary_key=True)
sub_id = db.Column(db.Integer(), nullable=False)
sub_type = db.Column(db.String(), nullable=False)
# 订阅用户
sub_users = db.Column(db.String(), nullable=False)
# 直播
live_short_id = db.Column(db.Integer())
live_status = db.Column(db.Integer)
# 主播/UP
uid = db.Column(db.BigInteger())
uname = db.Column(db.String())
latest_video_created = db.Column(db.BigInteger()) # 视频上传时间
dynamic_upload_time = db.Column(db.BigInteger(), default=0) # 动态发布时间
# 番剧
season_name = db.Column(db.String())
season_id = db.Column(db.Integer())
season_current_episode = db.Column(db.String())
season_update_time = db.Column(db.DateTime())
id = fields.IntField(pk=True, generated=True, auto_increment=True)
"""自增id"""
sub_id = fields.IntField()
"""订阅id"""
sub_type = fields.CharField(255)
"""订阅类型"""
sub_users = fields.TextField()
"""订阅用户"""
live_short_id = fields.IntField(null=True)
"""直播短id"""
live_status = fields.IntField(null=True)
"""直播状态 0: 停播 1: 直播"""
uid = fields.BigIntField(null=True)
"""主播/UP UID"""
uname = fields.CharField(255, null=True)
"""主播/UP 名称"""
latest_video_created = fields.BigIntField(null=True)
"""最后视频上传时间"""
dynamic_upload_time = fields.BigIntField(null=True, default=0)
"""动态发布时间"""
season_name = fields.CharField(255, null=True)
"""番剧名称"""
season_id = fields.IntField(null=True)
"""番剧id"""
season_current_episode = fields.CharField(255, null=True)
"""番剧最新集数"""
season_update_time = fields.DateField(null=True)
"""番剧更新日期"""
_idx1 = db.Index("bilibili_sub_idx1", "sub_id", "sub_type", unique=True)
class Meta:
table = "bilibili_sub"
table_description = "B站订阅数据表"
unique_together = ("sub_id", "sub_type")
@classmethod
async def add_bilibili_sub(
async def sub_handle(
cls,
sub_id: int,
sub_type: str,
sub_user: str,
sub_type: Optional[str] = None,
sub_user: str = "",
*,
live_short_id: Optional[int] = None,
live_status: Optional[int] = None,
dynamic_upload_time: Optional[int] = None,
dynamic_upload_time: int = 0,
uid: Optional[int] = None,
uname: Optional[str] = None,
latest_video_created: Optional[int] = None,
@@ -64,50 +79,60 @@ class BilibiliSub(db.Model):
:param season_current_episode: 番剧最新集数
:param season_update_time: 番剧更新时间
"""
try:
query = (
await cls.query.where( (cls.sub_id == sub_id) & (cls.sub_type == sub_type) )
.with_for_update()
.gino.first()
)
# try:
data = {
"sub_type": sub_type,
"sub_user": sub_user,
"live_short_id": live_short_id,
"live_status": live_status,
"dynamic_upload_time": dynamic_upload_time,
"uid": uid,
"uname": uname,
"latest_video_created": latest_video_created,
"season_name": season_name,
"season_id": season_id,
"season_current_episode": season_current_episode,
"season_update_time": season_update_time,
}
if 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
except Exception as e:
logger.info(f"bilibili_sub 添加订阅错误 {type(e)}: {e}")
return False
sub = None
if sub_type:
sub = await cls.get_or_none(sub_id=sub_id, sub_type=sub_type)
else:
sub = await cls.get_or_none(sub_id=sub_id)
if sub:
sub_users = sub.sub_users + sub_user
data["sub_type"] = sub_type or sub.sub_type
data["sub_users"] = sub_users
data["live_short_id"] = live_short_id or sub.live_short_id
data["live_status"] = (
live_status if live_status is not None else sub.live_status
)
data["dynamic_upload_time"] = dynamic_upload_time or sub.dynamic_upload_time
data["uid"] = uid or sub.uid
data["uname"] = uname or sub.uname
data["latest_video_created"] = (
latest_video_created or sub.latest_video_created
)
data["season_name"] = season_name or sub.season_name
data["season_id"] = season_id or sub.season_id
data["season_current_episode"] = (
season_current_episode or sub.season_current_episode
)
data["season_update_time"] = season_update_time or sub.season_update_time
else:
await cls.create(sub_id=sub_id, sub_type=sub_type, sub_users=sub_user)
await cls.update_or_create(sub_id=sub_id, defaults=data)
return True
# except Exception as e:
# logger.info(f"bilibili_sub 添加订阅错误 {type(e)}: {e}")
# return False
@classmethod
async def delete_bilibili_sub(cls, sub_id: int, sub_user: str,sub_type: Optional[str] = None) -> bool:
async def delete_bilibili_sub(
cls, sub_id: int, sub_user: str, sub_type: Optional[str] = None
) -> bool:
"""
说明:
删除订阅
@@ -116,129 +141,30 @@ class BilibiliSub(db.Model):
:param sub_user: 删除此条目的用户
"""
try:
async with db.transaction():
if sub_type:
query = (
await cls.query.where(
(cls.sub_id == sub_id) & (cls.sub_users.contains(sub_user) & (cls.sub_type == sub_type))
)
.with_for_update()
.gino.first()
)
else:
query = (
await cls.query.where(
(cls.sub_id == sub_id) & (cls.sub_users.contains(sub_user))
)
.with_for_update()
.gino.first()
)
if not query:
return False
await query.update(
sub_users=query.sub_users.replace(f"{sub_user},", "")
).apply()
if not query.sub_users.strip():
await query.delete()
return True
if sub_type:
sub = await cls.filter(
sub_id=sub_id, sub_type=sub_type, sub_users__contains=sub_user
).first()
else:
sub = await cls.filter(
sub_id=sub_id, sub_users__contains=sub_user
).first()
if not sub:
return False
sub.sub_users = sub.sub_users.replace(f"{sub_user},", "")
if sub.sub_users.strip():
await sub.save(update_fields=["sub_users"])
else:
await sub.delete()
return True
except Exception as e:
logger.info(f"bilibili_sub 删除订阅错误 {type(e)}: {e}")
return False
@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()
@classmethod
async def get_sub_data(cls, id_: str) -> List["BilibiliSub"]:
"""
获取 id_ 订阅的所有内容
:param id_: id
"""
query = cls.query.where(cls.sub_users.contains(id_))
return await query.gino.all()
@classmethod
async def update_sub_info(
cls,
sub_id: int,
*,
live_short_id: Optional[int] = None,
live_status: Optional[int] = None,
dynamic_upload_time: Optional[int] = None,
uid: Optional[int] = None,
uname: Optional[str] = None,
latest_video_created: Optional[int] = None,
season_name: Optional[str] = None,
season_id: Optional[int] = None,
season_current_episode: Optional[str] = None,
season_update_time: Optional[datetime] = None,
) -> bool:
"""
说明:
更新订阅信息
参数:
:param sub_id: 订阅名称,房间号,番剧号等
:param live_short_id: 直接短 id
:param live_status: 主播开播状态
:param dynamic_upload_time: 主播/UP最新动态时间
:param uid: 主播/UP uid
:param uname: 用户名称
:param latest_video_created: 最新视频上传时间
:param season_name: 番剧名称
:param season_id: 番剧 season_id
:param season_current_episode: 番剧最新集数
:param season_update_time: 番剧更新时间
"""
try:
async with db.transaction():
sub = (
await cls.query.where(cls.sub_id == sub_id)
.with_for_update()
.gino.first()
)
if sub:
await sub.update(
live_short_id=live_short_id
if live_short_id is not None
else sub.live_short_id,
live_status=live_status
if live_status is not None
else sub.live_status,
dynamic_upload_time=dynamic_upload_time
if dynamic_upload_time is not None
else sub.dynamic_upload_time,
uid=uid if uid is not None else sub.uid,
uname=uname if uname is not None else sub.uname,
latest_video_created=latest_video_created
if latest_video_created is not None
else sub.latest_video_created,
season_update_time=season_update_time
if season_update_time is not None
else sub.season_update_time,
season_current_episode=season_current_episode
if season_current_episode is not None
else sub.season_current_episode,
season_id=season_id if season_id is not None else sub.season_id,
season_name=season_name
if season_name is not None
else sub.season_name,
).apply()
return True
except Exception as e:
logger.info(f"bilibili_sub 更新订阅错误 {type(e)}: {e}")
return False
@classmethod
async def get_all_sub_data(
cls,
) -> "List[BilibiliSub], List[BilibiliSub], List[BilibiliSub]":
) -> Tuple[List["BilibiliSub"], List["BilibiliSub"], List["BilibiliSub"]]:
"""
说明:
分类获取所有数据
@@ -246,7 +172,7 @@ class BilibiliSub(db.Model):
live_data = []
up_data = []
season_data = []
query = await cls.query.gino.all()
query = await cls.all()
for x in query:
if x.sub_type == "live":
live_data.append(x)
+53 -30
View File
@@ -1,28 +1,30 @@
from datetime import datetime
from typing import Any, List, Tuple
from nonebot import on_command, on_message, on_regex
from nonebot.adapters.onebot.v11 import (
Bot,
Event,
MessageEvent,
GroupMessageEvent,
Message,
Bot,
MessageEvent,
)
from nonebot.matcher import Matcher
from nonebot.message import run_preprocessor
from nonebot.params import CommandArg, RegexGroup
from nonebot.permission import SUPERUSER
from configs.config import NICKNAME, Config
from models.ban_user import BanUser
from services.log import logger
from utils.image_utils import BuildImage
from utils.manager import group_manager
from utils.utils import get_message_text, is_number
from nonebot.params import RegexGroup, CommandArg
from .utils import black_word_manager
from nonebot import on_command, on_message, on_regex
from configs.config import Config, NICKNAME
from nonebot.permission import SUPERUSER
from .data_source import show_black_text_image, set_user_punish
from services.log import logger
from models.ban_user import BanUser
from datetime import datetime
from utils.message_builder import image
from .model import BlackWord
from typing import Tuple, Any
from utils.utils import get_message_text, is_number
from .data_source import set_user_punish, show_black_text_image
from .model import BlackWord
from .utils import black_word_manager
__zx_plugin_name__ = "敏感词检测"
__plugin_usage__ = """
@@ -38,7 +40,7 @@ usage:
设置惩罚id需要通过 '记录名单u:xxxxxxxx' 获取
指令:
记录名单
设置惩罚 [user_id] [id] [punish_level]
设置惩罚 [user_id] [下标] [惩罚等级]
示例:记录名单
示例:记录名单u:12345678
示例:设置惩罚 12345678 1 4
@@ -54,7 +56,13 @@ __plugin_settings__ = {
Config.add_plugin_config(
"black_word", "CYCLE_DAYS", 30, name="敏感词检测与惩罚", help_="黑名单词汇记录周期", default_value=30
"black_word",
"CYCLE_DAYS",
30,
name="敏感词检测与惩罚",
help_="黑名单词汇记录周期",
default_value=30,
type=int,
)
Config.add_plugin_config(
@@ -63,10 +71,11 @@ Config.add_plugin_config(
[5, 1, 1, 1, 1],
help_="各个级别惩罚的容忍次数,依次为:1, 2, 3, 4, 5",
default_value=[5, 1, 1, 1, 1],
type=List[int],
)
Config.add_plugin_config(
"black_word", "AUTO_PUNISH", True, help_="是否启动自动惩罚机制", default_value=True
"black_word", "AUTO_PUNISH", True, help_="是否启动自动惩罚机制", default_value=True, type=bool
)
# Config.add_plugin_config(
@@ -79,6 +88,7 @@ Config.add_plugin_config(
360,
help_="Union[int, List[int, int]]Ban时长(分钟),四级惩罚,可以为指定数字或指定列表区间(随机),例如 [30, 360]",
default_value=360,
type=int,
)
Config.add_plugin_config(
@@ -87,6 +97,7 @@ Config.add_plugin_config(
7,
help_="Union[int, List[int, int]]Ban时长(天),三级惩罚,可以为指定数字或指定列表区间(随机),例如 [7, 30]",
default_value=360,
type=int,
)
Config.add_plugin_config(
@@ -103,6 +114,7 @@ Config.add_plugin_config(
True,
help_="自动提级机制,当周期内处罚次数大于某一特定值就提升惩罚等级",
default_value=True,
type=bool,
)
Config.add_plugin_config(
@@ -111,6 +123,7 @@ Config.add_plugin_config(
3,
help_="在CYCLE_DAYS周期内触发指定惩罚次数后提升惩罚等级",
default_value=3,
type=int,
)
Config.add_plugin_config(
@@ -119,6 +132,7 @@ Config.add_plugin_config(
False,
help_="当未检测到已收录的敏感词时,开启ALAPI文本检测并将疑似文本发送给超级用户",
default_value=False,
type=bool,
)
Config.add_plugin_config(
@@ -127,6 +141,7 @@ Config.add_plugin_config(
True,
help_="当文本包含任意敏感词时,停止向下级插件传递,即不触发ai",
default_value=True,
type=bool,
)
message_matcher = on_message(priority=1, block=False)
@@ -150,24 +165,32 @@ async def _(
matcher: Matcher,
event: Event,
):
msg = get_message_text(event.json())
if (
isinstance(event, MessageEvent)
and event.is_tome()
and matcher.plugin_name == "black_word"
and not await BanUser.is_ban(event.user_id)
and str(event.user_id) not in bot.config.superusers
and not get_message_text(event.json()).startswith("原神绑定")
and not msg.startswith("原神绑定")
):
# 屏蔽群权限-1的群
if isinstance(event, GroupMessageEvent) and group_manager.get_group_level(event.group_id) < 0:
return
user_id = event.user_id
group_id = event.group_id if isinstance(event, GroupMessageEvent) else None
msg = get_message_text(event.json())
if await black_word_manager.check(user_id, group_id, msg) and Config.get_config(
"black_word", "CONTAIN_BLACK_STOP_PROPAGATION"
# if str(event.user_id) not in bot.config.superusers:
# return logger.debug(f"超级用户跳过黑名单词汇检查 Message: {msg}", target=event.user_id)
if (
event.is_tome()
and matcher.plugin_name == "black_word"
and not await BanUser.is_ban(event.user_id)
):
matcher.stop_propagation()
# 屏蔽群权限-1的群
if (
isinstance(event, GroupMessageEvent)
and group_manager.get_group_level(event.group_id) < 0
):
return
user_id = event.user_id
group_id = event.group_id if isinstance(event, GroupMessageEvent) else None
msg = get_message_text(event.json())
if await black_word_manager.check(
user_id, group_id, msg
) and Config.get_config("black_word", "CONTAIN_BLACK_STOP_PROPAGATION"):
matcher.stop_propagation()
@show_black.handle()
+56 -62
View File
@@ -1,47 +1,34 @@
from services.db_context import db
from typing import Optional, List
from datetime import datetime, timedelta
from typing import List, Optional
from tortoise import fields
from services.db_context import Model
class BlackWord(db.Model):
__tablename__ = "black_word"
class BlackWord(Model):
# __tablename__ = "black_word"
id = db.Column(db.Integer(), primary_key=True, autoincrement=True)
user_qq = db.Column(db.BigInteger(), nullable=False, primary_key=True)
group_id = db.Column(db.BigInteger())
plant_text = db.Column(db.String())
black_word = db.Column(db.String())
punish = db.Column(db.String(), default="")
punish_level = db.Column(db.Integer())
create_time = db.Column(db.DateTime(timezone=True), nullable=False)
id = fields.IntField(pk=True, generated=True, auto_increment=True)
"""自增id"""
user_qq = fields.BigIntField()
"""用户id"""
group_id = fields.BigIntField(null=True)
"""群聊id"""
plant_text = fields.TextField()
"""检测文本"""
black_word = fields.TextField()
"""黑名单词语"""
punish = fields.TextField(default="")
"""惩罚内容"""
punish_level = fields.IntField()
"""惩罚等级"""
create_time = fields.DatetimeField(auto_now_add=True)
"""创建时间"""
@classmethod
async def add_user_black_word(
cls,
user_qq: int,
group_id: Optional[int],
black_word: str,
plant_text: str,
punish_level: int,
):
"""
说明:
添加用户发送的敏感词
参数:
:param user_qq: 用户id
:param group_id: 群号
:param black_word: 黑名单词汇
:param plant_text: 消息文本
:param punish_level: 惩罚等级
"""
await cls.create(
user_qq=user_qq,
group_id=group_id,
plant_text=plant_text,
black_word=black_word,
punish_level=punish_level,
create_time=datetime.now(),
)
class Meta:
table = "black_word"
table_description = "惩罚机制数据表"
@classmethod
async def set_user_punish(
@@ -63,17 +50,21 @@ class BlackWord(db.Model):
user = None
if (not black_word and not id_) or not punish:
return False
query = cls.query.where(cls.user_qq == user_qq).with_for_update()
if black_word:
user = await query.where(cls.black_word == black_word).order_by(cls.id.desc()).gino.first()
user = (
await cls.filter(user_qq=user_qq, black_word=black_word)
.order_by("id")
.first()
)
elif id_:
user_list = await query.gino.all()
user_list = await cls.filter(user_qq=user_qq).order_by("id").all()
if len(user_list) == 0 or (id_ < 0 or id_ > len(user_list)):
return False
user = user_list[id_]
if not user:
return False
await user.update(punish=cls.punish + punish + " ").apply()
user.punish = f"{user.punish}{punish} "
await user.save(update_fields=["punish"])
return True
@classmethod
@@ -88,15 +79,14 @@ class BlackWord(db.Model):
:param days: 周期天数
:param punish_level: 惩罚等级
"""
setattr(BlackWord, "count", db.func.count(cls.id).label("count"))
query = cls.select("count").where(
(cls.user_qq == user_qq)
& (cls.punish_level != -1)
& (cls.create_time > datetime.now() - timedelta(days=days))
query = cls.filter(
user_qq=user_qq,
create_time__gte=datetime.now() - timedelta(days=days),
punish_level__not_in=[-1],
)
if punish_level is not None:
query = query.where(cls.punish_level == punish_level)
return (await query.gino.first())[0]
query = query.filter(punish_level=punish_level)
return await query.count()
@classmethod
async def get_user_punish_level(cls, user_qq: int, days: int = 7) -> Optional[int]:
@@ -108,12 +98,14 @@ class BlackWord(db.Model):
:param days: 周期天数
"""
if (
query := await cls.query.where(cls.user_qq == user_qq)
.where(cls.create_time > datetime.now() - timedelta(days=days))
.order_by(cls.id.desc())
.gino.first()
user := await cls.filter(
user_qq=user_qq,
create_time__gte=datetime.now() - timedelta(days=days),
)
.order_by("id")
.first()
):
return query.punish_level
return user.punish_level
return None
@classmethod
@@ -133,16 +125,18 @@ class BlackWord(db.Model):
:param date: 日期
:param date_type: 日期查询类型
"""
query = cls.query
query = cls
if user_qq:
query = query.where(cls.user_qq == user_qq)
query = query.filter(user_qq=user_qq)
if group_id:
query = query.where(cls.group_id == group_id)
query = query.filter(group_id=group_id)
if date:
if date_type == "=":
query = query.where(cls.create_time == date)
query = query.filter(
create_time__range=[date, date + timedelta(days=1)]
)
elif date_type == ">":
query = query.where(cls.create_time > date)
query = query.filter(create_time__gte=date)
elif date_type == "<":
query = query.where(cls.create_time < date)
return await query.gino.all()
query = query.filter(create_time__lte=date)
return await query.order_by("id").all()
+37 -23
View File
@@ -1,15 +1,18 @@
from utils.utils import cn2py, get_bot
from configs.path_config import DATA_PATH
from typing import Optional, Union, Tuple
from .model import BlackWord
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 import ActionFailed
from models.group_member_info import GroupInfoUser
from utils.http_utils import AsyncHttpx
import random
from pathlib import Path
from typing import Optional, Tuple, Union
from nonebot.adapters.onebot.v11 import ActionFailed
from configs.config import Config
from configs.path_config import DATA_PATH
from models.ban_user import BanUser
from models.group_member_info import GroupInfoUser
from services.log import logger
from utils.http_utils import AsyncHttpx
from utils.utils import cn2py, get_bot
from .model import BlackWord
try:
import ujson as json
@@ -49,7 +52,7 @@ class BlackWordManager:
"hanbi",
"hanpi",
"laji",
"fw"
"fw",
],
"5": [],
}
@@ -94,9 +97,9 @@ class BlackWordManager:
user_id, group_id, data[0], message, int(data[1])
)
return True
if Config.get_config(
"black_word", "ALAPI_CHECK_FLAG"
) and not await check_text(message):
if Config.get_config("black_word", "ALAPI_CHECK_FLAG") and not await check_text(
message
):
await send_msg(
0, None, f"USER {user_id} GROUP {group_id} ALAPI 疑似检测:{message}"
)
@@ -146,8 +149,12 @@ async def _add_user_black_word(
"black_word", "AUTO_ADD_PUNISH_LEVEL"
) and user_count > Config.get_config("black_word", "ADD_PUNISH_LEVEL_TO_COUNT"):
punish_level -= 1
await BlackWord.add_user_black_word(
user_id, group_id, black_word, message, punish_level
await BlackWord.create(
user_qq=user_id,
group_id=group_id,
plant_text=message,
black_word=black_word,
punish_level=punish_level,
)
logger.info(
f"已将 USER {user_id} GROUP {group_id} 添加至黑名单词汇记录 Black_word:{black_word} Plant_text:{message}"
@@ -172,7 +179,9 @@ async def _punish_handle(
# 用户周期内触发punish_level级惩罚的次数
user_count = await BlackWord.get_user_count(user_id, cycle_days, punish_level)
# 获取最近一次的惩罚等级,将在此基础上增加
punish_level = await BlackWord.get_user_punish_level(user_id, cycle_days) or punish_level
punish_level = (
await BlackWord.get_user_punish_level(user_id, cycle_days) or punish_level
)
# 容忍次数:List[int]
tolerate_count = Config.get_config("black_word", "TOLERATE_COUNT")
if not tolerate_count or len(tolerate_count) < 5:
@@ -226,15 +235,17 @@ async def _get_punish(
ban_4_duration = Config.get_config("black_word", "BAN_4_DURATION")
# 口头警告内容
warning_result = Config.get_config("black_word", "WARNING_RESULT")
try:
uname = (await GroupInfoUser.get_member_info(user_id, group_id)).user_name
except AttributeError:
if user := await GroupInfoUser.get_or_none(user_qq=user_id, group_id=group_id):
uname = user.user_name
else:
uname = user_id
# 永久ban
if id_ == 1:
if str(user_id) not in bot.config.superusers:
await BanUser.ban(user_id, 10, 99999999)
await send_msg(user_id, group_id, f"BlackWordChecker 永久ban USER {uname}({user_id})")
await send_msg(
user_id, group_id, f"BlackWordChecker 永久ban USER {uname}({user_id})"
)
logger.info(f"BlackWord 永久封禁 USER {user_id}...")
# 删除好友(有的话
elif id_ == 2:
@@ -319,4 +330,7 @@ async def check_text(text: str) -> bool:
return True
black_word_manager = BlackWordManager(DATA_PATH / "black_word" / "black_word.json", DATA_PATH / "black_word" / "black_py.json")
black_word_manager = BlackWordManager(
DATA_PATH / "black_word" / "black_word.json",
DATA_PATH / "black_word" / "black_py.json",
)
+11 -7
View File
@@ -1,13 +1,16 @@
from nonebot import on_command
from .data_source import get_bt_info
from services.log import logger
from nonebot.adapters.onebot.v11 import PrivateMessageEvent, Message
from nonebot.adapters.onebot.v11.permission import PRIVATE
from asyncio.exceptions import TimeoutError
from utils.utils import is_number
from nonebot.params import CommandArg, ArgStr
from nonebot import on_command
from nonebot.adapters.onebot.v11 import Message, PrivateMessageEvent
from nonebot.adapters.onebot.v11.permission import PRIVATE
from nonebot.params import ArgStr, CommandArg
from nonebot.typing import T_State
from services.log import logger
from utils.utils import is_number
from .data_source import get_bt_info
__zx_plugin_name__ = "磁力搜索"
__plugin_usage__ = """
usage:
@@ -34,6 +37,7 @@ __plugin_configs__ = {
"value": 10,
"help": "单次BT搜索返回最大消息数量",
"default_value": 10,
"type": int,
}
}
+3 -12
View File
@@ -1,13 +1,7 @@
from utils.http_utils import AsyncHttpx
from configs.config import Config
from bs4 import BeautifulSoup
import platform
# if platform.system() == "Windows":
# import asyncio
#
# asyncio.set_event_loop_policy(asyncio.WindowsSelectorEventLoopPolicy())
from configs.config import Config
from utils.http_utils import AsyncHttpx
url = "http://www.eclzz.love"
@@ -28,10 +22,7 @@ async def get_bt_info(keyword: str, page: int):
for item in item_lst[:bt_max_num]:
divs = item.find_all("div")
title = (
str(divs[0].find("a").text)
.replace("<em>", "")
.replace("</em>", "")
.strip()
str(divs[0].find("a").text).replace("<em>", "").replace("</em>", "").strip()
)
spans = divs[2].find_all("span")
type_ = spans[0].text
+23 -21
View File
@@ -1,16 +1,18 @@
from nonebot.adapters.onebot.v11 import Bot, MessageEvent
from nonebot.permission import SUPERUSER
from nonebot import on_command
from .data_source import check_update, get_latest_version_data
from services.log import logger
from utils.utils import scheduler, get_bot
from pathlib import Path
from configs.config import NICKNAME, Config
from nonebot.rule import to_me
from nonebot.params import ArgStr
import platform
import os
import platform
from pathlib import Path
from nonebot import on_command
from nonebot.adapters.onebot.v11 import Bot, MessageEvent
from nonebot.params import ArgStr
from nonebot.permission import SUPERUSER
from nonebot.rule import to_me
from configs.config import NICKNAME, Config
from services.log import logger
from utils.utils import get_bot, scheduler
from .data_source import check_update, get_latest_version_data
__zx_plugin_name__ = "自动更新 [Superuser]"
__plugin_usage__ = """
@@ -29,12 +31,14 @@ __plugin_configs__ = {
"value": True,
"help": "真寻是否检测版本状态",
"default": True,
"type": bool,
},
"AUTO_UPDATE_ZHENXUN": {
"value": False,
"help": "真寻是否自动检查更新",
"default": False,
}
"type": bool,
},
}
update_zhenxun = on_command("检查更新真寻", permission=SUPERUSER, priority=1, block=True)
@@ -54,21 +58,19 @@ async def _(bot: Bot, event: MessageEvent):
try:
code, error = await check_update(bot)
if error:
logger.error(f"更新真寻未知错误 {error}")
logger.error(f"错误: {error}", "检查更新真寻")
await bot.send_private_msg(
user_id=event.user_id, message=f"更新真寻未知错误 {error}"
)
except Exception as e:
logger.error(f"更新真寻未知错误 {type(e)}:{e}")
logger.error(f"更新真寻未知错误", "检查更新真寻", e=e)
await bot.send_private_msg(
user_id=event.user_id,
message=f"更新真寻未知错误 {type(e)}:{e}",
message=f"更新真寻未知错误 {type(e)}: {e}",
)
else:
if code == 200:
await bot.send_private_msg(
user_id=event.user_id, message=f"更新完毕,请重启真寻...."
)
await bot.send_private_msg(user_id=event.user_id, message=f"更新完毕,请重启真寻....")
@restart.got("flag", prompt=f"确定是否重启{NICKNAME}?确定请回复[是|好|确定](重启失败咱们将失去联系,请谨慎!)")
@@ -78,6 +80,7 @@ async def _(flag: str = ArgStr("flag")):
open("is_restart", "w")
if str(platform.system()).lower() == "windows":
import sys
python = sys.executable
os.execl(python, python, *sys.argv)
else:
@@ -105,12 +108,11 @@ async def _():
data = await get_latest_version_data()
if data:
latest_version = data["name"]
if _version != latest_version:
if _version.lower() != latest_version.lower():
bot = get_bot()
await bot.send_private_msg(
user_id=int(list(bot.config.superusers)[0]),
message=f"检测到真寻版本更新\n"
f"当前版本:{_version},最新版本:{latest_version}",
message=f"检测到真寻版本更新\n" f"当前版本:{_version},最新版本:{latest_version}",
)
if Config.get_config("check_zhenxun_update", "AUTO_UPDATE_ZHENXUN"):
try:
+40 -38
View File
@@ -1,18 +1,20 @@
from nonebot.adapters.onebot.v11 import Bot, Message
from utils.image_utils import BuildImage
from configs.path_config import IMAGE_PATH
from utils.message_builder import image
from utils.http_utils import AsyncHttpx
from typing import List
from services.log import logger
from pathlib import Path
import ujson as json
import nonebot
import asyncio
import platform
import tarfile
import shutil
import os
import platform
import shutil
import tarfile
from pathlib import Path
from typing import List, Tuple
import nonebot
import ujson as json
from nonebot.adapters.onebot.v11 import Bot, Message
from configs.path_config import IMAGE_PATH
from services.log import logger
from utils.http_utils import AsyncHttpx
from utils.image_utils import BuildImage
from utils.message_builder import image
# if str(platform.system()).lower() == "windows":
# policy = asyncio.WindowsSelectorEventLoopPolicy()
@@ -55,7 +57,7 @@ async def remind(bot: Bot):
is_restart_file.unlink()
async def check_update(bot: Bot) -> 'int, str':
async def check_update(bot: Bot) -> Tuple[int, str]:
logger.info("开始检查更新真寻酱....")
_version = "v0.0.0"
if _version_file.exists():
@@ -73,7 +75,7 @@ async def check_update(bot: Bot) -> 'int, str':
message=f"检测真寻已更新,当前版本:{_version},最新版本:{latest_version}\n" f"开始更新.....",
)
logger.info(f"开始下载真寻最新版文件....")
tar_gz_url = (await AsyncHttpx.get(tar_gz_url)).headers.get('Location')
tar_gz_url = (await AsyncHttpx.get(tar_gz_url)).headers.get("Location")
if await AsyncHttpx.download_file(tar_gz_url, zhenxun_latest_tar_gz):
logger.info("下载真寻最新版文件完成....")
error = await asyncio.get_event_loop().run_in_executor(
@@ -85,23 +87,25 @@ async def check_update(bot: Bot) -> 'int, str':
logger.info("开始获取真寻更新日志.....")
update_info = data["body"]
width = 0
height = len(update_info.split('\n')) * 24
height = len(update_info.split("\n")) * 24
A = BuildImage(width, height, font_size=20)
for m in update_info.split('\n'):
for m in update_info.split("\n"):
w, h = A.getsize(m)
if w > width:
width = w
A = BuildImage(width + 50, height, font_size=20)
A.text((10, 10), update_info)
A.save(f'{IMAGE_PATH}/update_info.png')
A.save(f"{IMAGE_PATH}/update_info.png")
await bot.send_private_msg(
user_id=int(list(bot.config.superusers)[0]),
message=Message(f"真寻更新完成,版本:{_version} -> {latest_version}\n"
f"更新日期:{data['created_at']}\n"
f"更新日志:\n"
f"{image('update_info.png')}"),
message=Message(
f"真寻更新完成,版本:{_version} -> {latest_version}\n"
f"更新日期:{data['created_at']}\n"
f"更新日志:\n"
f"{image('update_info.png')}"
),
)
return 200, ''
return 200, ""
else:
logger.warning(f"下载真寻最新版本失败...版本号:{latest_version}")
await bot.send_private_msg(
@@ -119,7 +123,7 @@ async def check_update(bot: Bot) -> 'int, str':
await bot.send_private_msg(
user_id=int(list(bot.config.superusers)[0]), message=f"自动获取真寻版本失败...."
)
return 999, ''
return 999, ""
def _file_handle(latest_version: str) -> str:
@@ -128,7 +132,7 @@ def _file_handle(latest_version: str) -> str:
if backup_dir.exists():
shutil.rmtree(backup_dir)
tf = None
error = ''
error = ""
# try:
backup_dir.mkdir(exist_ok=True, parents=True)
logger.info("开始解压真寻文件压缩包....")
@@ -143,16 +147,16 @@ def _file_handle(latest_version: str) -> str:
delete_file = update_info["delete_file"]
config_file = Path() / "configs" / "config.py"
config_path_file = Path() / "configs" / "path_config.py"
for file in [config_file.name]:
tmp = ""
new_file = Path(zhenxun_latest_file) / "configs" / file
old_file = Path() / "configs" / file
new_lines = open(new_file, "r", encoding="utf8").readlines()
old_lines = open(old_file, "r", encoding="utf8").readlines()
for nl in new_lines:
tmp += check_old_lines(old_lines, nl)
with open(old_file, "w", encoding="utf8") as f:
f.write(tmp)
# for file in [config_file.name]:
# tmp = ""
# new_file = Path(zhenxun_latest_file) / "configs" / file
# old_file = Path() / "configs" / file
# new_lines = open(new_file, "r", encoding="utf8").readlines()
# old_lines = open(old_file, "r", encoding="utf8").readlines()
# for nl in new_lines:
# tmp += check_old_lines(old_lines, nl)
# with open(old_file, "w", encoding="utf8") as f:
# f.write(tmp)
for file in delete_file + update_file:
if file != "configs":
file = Path() / file
@@ -189,9 +193,7 @@ 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()}"
)
os.system(f"poetry run pip install -r {(Path() / 'pyproject.toml').absolute()}")
return error
+14 -8
View File
@@ -1,13 +1,16 @@
from typing import Tuple, Any
import time
from typing import Any, Tuple
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
from services.log import logger
from utils.manager import withdraw_message_manager
from nonebot.params import RegexGroup
from configs.config import Config
from configs.path_config import TEMP_PATH
from services.log import logger
from utils.http_utils import AsyncHttpx
from utils.manager import withdraw_message_manager
from utils.message_builder import image
__zx_plugin_name__ = "coser"
__plugin_usage__ = """
@@ -33,6 +36,7 @@ __plugin_configs__ = {
"value": (0, 1),
"help": "自动撤回,参1:延迟撤回色图时间(秒),0 为关闭 | 参2:监控聊天类型,0(私聊) 1(群聊) 2(群聊+私聊)",
"default_value": (0, 1),
"type": Tuple[int, int],
},
}
@@ -40,15 +44,17 @@ coser = on_regex(r"^(\d)?连?(cos|COS|coser|括丝)$", priority=5, block=True)
# 纯cos,较慢:https://picture.yinux.workers.dev
# 比较杂,有福利姬,较快:https://api.jrsgslb.cn/cos/url.php?return=img
url = "https://picture.yinux.workers.dev/"
url = "https://picture.yinux.workers.dev"
@coser.handle()
async def _(event: MessageEvent, reg_group: Tuple[Any, ...] = RegexGroup()):
num = reg_group[0] or 1
for _ in range(int(num)):
path = TEMP_PATH / f"cos_cc{int(time.time())}.jpeg"
try:
msg_id = await coser.send(image(url))
await AsyncHttpx.download_file(url, path)
msg_id = await coser.send(image(path))
withdraw_message_manager.withdraw_message(
event,
msg_id["message_id"],
+19 -5
View File
@@ -1,9 +1,11 @@
import nonebot
from pathlib import Path
import nonebot
from nonebot.log import logger
from pydantic import BaseModel, Extra, ValidationError
from configs.config import Config as AConfig
from configs.path_config import IMAGE_PATH, DATA_PATH
from configs.path_config import DATA_PATH, IMAGE_PATH
try:
import ujson as json
@@ -147,9 +149,20 @@ for game_flag, game_name in zip(
"FGO_FLAG",
"ONMYOJI_FLAG",
"PCR_TAI",
"BA_FLAG"
"BA_FLAG",
],
[
"明日方舟",
"原神",
"赛马娘",
"坎公骑冠剑",
"公主连结",
"碧蓝航线",
"命运-冠位指定(FGO)",
"阴阳师",
"pcr台服卡池",
"碧蓝档案",
],
["明日方舟", "原神", "赛马娘", "坎公骑冠剑", "公主连结", "碧蓝航线", "命运-冠位指定(FGO)", "阴阳师", "pcr台服卡池", "碧蓝档案"],
):
AConfig.add_plugin_config(
"draw_card",
@@ -158,9 +171,10 @@ for game_flag, game_name in zip(
name="游戏抽卡",
help_=f"{game_name} 抽卡开关",
default_value=True,
type=bool,
)
AConfig.add_plugin_config(
"draw_card", "SEMAPHORE", 5, help_=f"异步数据下载数量限制", default_value=5
"draw_card", "SEMAPHORE", 5, help_=f"异步数据下载数量限制", default_value=5, type=int
)
+8 -5
View File
@@ -1,11 +1,13 @@
from nonebot import on_regex
from services.log import logger
from nonebot.adapters.onebot.v11 import Bot, MessageEvent, GroupMessageEvent
from nonebot.adapters.onebot.v11 import Bot, GroupMessageEvent, MessageEvent
from nonebot.typing import T_State
from utils.utils import scheduler, get_bot
from .data_source import get_epic_free
from utils.manager import group_manager
from configs.config import Config
from services.log import logger
from utils.manager import group_manager
from utils.utils import get_bot, scheduler
from .data_source import get_epic_free
__zx_plugin_name__ = "epic免费游戏"
__plugin_usage__ = """
@@ -31,6 +33,7 @@ Config.add_plugin_config(
True,
help_="被动 epic免费游戏 进群默认开关状态",
default_value=True,
type=bool,
)
epic = on_regex("^epic$", priority=5, block=True)
+10 -4
View File
@@ -112,9 +112,15 @@ async def get_epic_free(bot: Bot, type_event: str):
if pair["key"] == "publisherName":
game_pub = pair["value"]
if game.get("productSlug"):
gamesDesp = await get_epic_game_desp(game["productSlug"])
#game_desp = game["description"]
game_desp = gamesDesp["description"]
gamesDesp = await get_epic_game_desp(game["productSlug"])
try:
#是否存在简短的介绍
if "shortDescription" in gamesDesp:
game_desp = gamesDesp["shortDescription"]
except KeyError:
game_desp = gamesDesp["description"]
else:
game_desp = game["description"]
try:
end_date_iso = game["promotions"]["promotionalOffers"][0][
"promotionalOffers"
@@ -187,4 +193,4 @@ async def get_epic_free(bot: Bot, type_event: str):
except TypeError as e:
# logger.info(str(e))
pass
return msg_list, 200
return msg_list, 200
+20 -19
View File
@@ -1,16 +1,16 @@
from utils.image_utils import get_img_hash
from nonebot.adapters.onebot.v11.permission import GROUP
from configs.path_config import TEMP_PATH
import random
from utils.message_builder import image
from nonebot import on_message
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
from nonebot import on_message
from nonebot.adapters.onebot.v11 import GroupMessageEvent
from nonebot.adapters.onebot.v11.permission import GROUP
from configs.config import NICKNAME, Config
from configs.path_config import TEMP_PATH
from services.log import logger
from utils.http_utils import AsyncHttpx
from utils.image_utils import get_img_hash
from utils.message_builder import image
from utils.utils import get_message_img, get_message_text
__zx_plugin_name__ = "复读"
__plugin_usage__ = """
@@ -23,14 +23,15 @@ __plugin_version__ = 0.1
__plugin_author__ = "HibiKier"
__plugin_task__ = {"fudu": "复读"}
__plugin_configs__ = {
"FUDU_PROBABILITY": {"value": 0.7, "help": "复读概率", "default_value": 0.7}
"FUDU_PROBABILITY": {
"value": 0.7,
"help": "复读概率",
"default_value": 0.7,
"type": float,
}
}
Config.add_plugin_config(
"_task",
"DEFAULT_FUDU",
True,
help_="被动 复读 进群默认开关状态",
default_value=True,
"_task", "DEFAULT_FUDU", True, help_="被动 复读 进群默认开关状态", default_value=True, type=bool
)
@@ -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], event.group_id)
else:
img_hash = ""
add_msg = msg + "|-|" + img_hash
@@ -107,7 +108,7 @@ async def _(event: GroupMessageEvent):
) and not _fudu_list.is_repeater(event.group_id):
if random.random() < 0.2:
if msg.endswith("打断施法!"):
await fudu.finish("[[_task|fudu]]打断" + msg)
await fudu.finish("[[_task|fudu]]打断" + msg)
else:
await fudu.finish("[[_task|fudu]]打断施法!")
_fudu_list.set_repeater(event.group_id)
+19 -24
View File
@@ -1,11 +1,14 @@
from utils.utils import get_bot, scheduler
from nonebot import on_command
from nonebot.adapters.onebot.v11 import GroupMessageEvent, MessageEvent
from services.log import logger
from configs.path_config import IMAGE_PATH
from .data_source import get_alc_image
from utils.manager import group_manager
from configs.config import Config
from configs.path_config import IMAGE_PATH
from services.log import logger
from utils.manager import group_manager
from utils.message_builder import image
from utils.utils import get_bot, scheduler
from ._data_source import build_alc_image
__zx_plugin_name__ = "原神老黄历"
__plugin_usage__ = """
@@ -33,27 +36,19 @@ Config.add_plugin_config(
True,
help_="被动 原神黄历提醒 进群默认开关状态",
default_value=True,
type=bool,
)
almanac = on_command("原神黄历", priority=15, block=True)
ALC_PATH = IMAGE_PATH / "genshin" / "alc"
ALC_PATH.mkdir(parents=True, exist_ok=True)
almanac = on_command("原神黄历", priority=5, block=True)
@almanac.handle()
async def _(event: MessageEvent,):
alc_img = await get_alc_image(ALC_PATH)
if alc_img:
mes = alc_img + "\n ※ 黄历数据来源于 genshin.pub"
await almanac.send(mes)
logger.info(
f"(USER {event.user_id}, GROUP {event.group_id if isinstance(event, GroupMessageEvent) else 'private'})"
f" 发送查看原神黄历"
)
else:
await almanac.send("黄历图片下载失败...")
async def _(event: MessageEvent):
await almanac.send(image(b64=await build_alc_image()))
logger.info(
f"(USER {event.user_id}, GROUP {event.group_id if isinstance(event, GroupMessageEvent) else 'private'})"
f" 发送查看原神黄历"
)
@scheduler.scheduled_job(
@@ -67,9 +62,9 @@ async def _():
if bot:
gl = await bot.get_group_list()
gl = [g["group_id"] for g in gl]
alc_img = await get_alc_image(ALC_PATH)
alc_img = image(b64=await build_alc_image())
if alc_img:
mes = "[[_task|genshin_alc]]" + alc_img + "\n ※ 黄历数据来源于 genshin.pub"
mes = "[[_task|genshin_alc]]" + alc_img
for gid in gl:
if group_manager.check_group_task_status(gid, "genshin_alc"):
await bot.send_group_msg(group_id=int(gid), message="" + mes)
await bot.send_group_msg(group_id=int(gid), message=mes)
+129
View File
@@ -0,0 +1,129 @@
import os
import random
from dataclasses import dataclass
from datetime import datetime
from typing import List, Tuple, Union
import ujson as json
from configs.path_config import DATA_PATH, IMAGE_PATH
from utils.image_utils import BuildImage
CONFIG_PATH = DATA_PATH / "genshin_alc" / "config.json"
ALC_PATH = IMAGE_PATH / "genshin" / "alc"
ALC_PATH.mkdir(exist_ok=True, parents=True)
BACKGROUND_PATH = ALC_PATH / "back.png"
chinese = {
"0": "十",
"1": "一",
"2": "二",
"3": "三",
"4": "四",
"5": "五",
"6": "六",
"7": "七",
"8": "八",
"9": "九",
}
@dataclass
class Fortune:
title: str
desc: str
def random_fortune() -> Tuple[List[Fortune], List[Fortune]]:
"""
说明:
随机运势
"""
data = json.load(CONFIG_PATH.open("r", encoding="utf8"))
fortune_data = {}
good_fortune = []
bad_fortune = []
while len(fortune_data) < 6:
r = random.choice(list(data.keys()))
if r not in fortune_data:
fortune_data[r] = data[r]
for i, k in enumerate(fortune_data):
if i < 3:
good_fortune.append(
Fortune(title=k, desc=random.choice(fortune_data[k]["buff"]))
)
else:
bad_fortune.append(
Fortune(title=k, desc=random.choice(fortune_data[k]["debuff"]))
)
return good_fortune, bad_fortune
def int2cn(v: Union[str, int]):
"""
说明:
数字转中文
参数:
:param v: str
"""
return "".join([chinese[x] for x in str(v)])
async def build_alc_image() -> str:
"""
说明:
构造今日运势图片
"""
for file in os.listdir(ALC_PATH):
if file not in ["back.png", f"{datetime.now().date()}.png"]:
(ALC_PATH / file).unlink()
path = ALC_PATH / f"{datetime.now().date()}.png"
if path.exists():
return BuildImage(0, 0, background=path).pic2bs4()
good_fortune, bad_fortune = random_fortune()
background = BuildImage(
0, 0, background=BACKGROUND_PATH, font="HYWenHei-85W.ttf", font_size=30
)
now = datetime.now()
await background.atext((78, 145), str(now.year), fill="#8d7650ff")
month = str(now.month)
month_w = 358
if now.month < 10:
month_w = 373
elif now.month != 10:
month = "0" + month[-1]
await background.atext((month_w, 145), f"{int2cn(month)}月", fill="#8d7650ff")
day = str(now.day)
if now.day > 10 and day[-1] != "0":
day = day[0] + "0" + day[-1]
day_str = f"{int2cn(day)}日"
day_w = 193
if (n := len(day_str)) == 3:
day_w = 207
elif n == 2:
day_w = 228
await background.atext(
(day_w, 145), f"{int2cn(day)}日", fill="#f7f8f2ff", font_size=35
)
fortune_h = 230
for fortune in good_fortune:
await background.atext(
(150, fortune_h), fortune.title, fill="#756141ff", font_size=25
)
await background.atext(
(150, fortune_h + 28), fortune.desc, fill="#b5b3acff", font_size=19
)
fortune_h += 55
fortune_h += 4
for fortune in bad_fortune:
await background.atext(
(150, fortune_h), fortune.title, fill="#756141ff", font_size=25
)
await background.atext(
(150, fortune_h + 28), fortune.desc, fill="#b5b3acff", font_size=19
)
fortune_h += 55
await background.asave(path)
return background.pic2bs4()
-26
View File
@@ -1,26 +0,0 @@
from utils.message_builder import image
from datetime import datetime
from pathlib import Path
from utils.http_utils import AsyncPlaywright
from nonebot.adapters.onebot.v11 import MessageSegment
from typing import Optional
import os
url = "https://genshin.pub"
async def get_alc_image(path: Path) -> Optional[MessageSegment]:
"""
截取黄历
:param path: 存储路径
"""
date = datetime.now().date()
for file in os.listdir(path):
if f"{date}.png" != file:
file = path / file
file.unlink()
if f"{date}.png" in os.listdir(path):
return image(f"{date}.png", "genshin/alc")
return await AsyncPlaywright.screenshot(
url, path / f"{date}.png", ".GSAlmanacs_gs_almanacs__3qT_A"
)
+22 -72
View File
@@ -1,20 +1,21 @@
from nonebot import on_command, Driver
from nonebot.adapters.onebot.v11 import MessageEvent, Message, GroupMessageEvent
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
import nonebot
from services.log import logger
from nonebot.permission import SUPERUSER
from typing import List
from datetime import datetime, timedelta
import os
import asyncio
import os
import time
from datetime import datetime, timedelta
from typing import List
import nonebot
from nonebot import Driver, on_command
from nonebot.adapters.onebot.v11 import GroupMessageEvent, Message, MessageEvent
from nonebot.permission import SUPERUSER
__zx_plugin_name__ = "原神今日素材"
from configs.path_config import IMAGE_PATH
from services.log import logger
from utils.browser import get_browser
from utils.image_utils import BuildImage
from utils.message_builder import image
__zx_plugin_name__ = "今日素材"
__plugin_usage__ = """
usage:
看看原神今天要刷什么
@@ -57,7 +58,7 @@ async def _(event: MessageEvent):
await material.send(
Message(
image(f"{file_name}.png", "genshin/material")
+ "\n※ 每日素材数据来源于 genshin.pub"
+ "\n※ 每日素材数据来源于米游社"
)
)
logger.info(
@@ -86,63 +87,14 @@ async def update_image():
if not browser:
logger.warning("获取 browser 失败,请部署至 linux 环境....")
return False
url = "https://genshin.pub/daily"
page = await browser.new_page()
await page.goto(url, wait_until="networkidle", timeout=10000)
await page.set_viewport_size({"width": 2560, "height": 1080})
await page.evaluate(
"""
document.getElementsByClassName('GSTitleBar_gs_titlebar__2IJqy')[0].remove();
e = document.getElementsByClassName('GSContainer_gs_container__2FbUz')[0];
e.setAttribute("style", "height:880px");
"""
)
await page.click("button")
div = await page.query_selector(".GSContainer_content_box__1sIXz")
for i, card in enumerate(
await page.query_selector_all(".GSTraitCotainer_trait_section__1f3bc")
):
index = 0
type_ = "char" if not i else "weapons"
for x in await card.query_selector_all("xpath=child::*"):
await x.screenshot(
path=f"{IMAGE_PATH}/genshin/material/{type_}_{index}.png",
timeout=100000,
)
# 下滑两次
for _ in range(3):
await div.press("PageDown")
index += 1
# 结束后上滑至顶
for _ in range(index * 3):
await div.press("PageUp")
file_list = os.listdir(f"{IMAGE_PATH}/genshin/material")
char_img = [
f"{IMAGE_PATH}/genshin/material/{x}"
for x in file_list
if x.startswith("char")
]
weapons_img = [
f"{IMAGE_PATH}/genshin/material/{x}"
for x in file_list
if x.startswith("weapons")
]
char_img.sort()
weapons_img.sort()
height = await asyncio.get_event_loop().run_in_executor(
None, get_background_height, weapons_img
)
background_img = BuildImage(1200, height + 100, color="#f6f2ee")
current_width = 50
for img_list in [char_img, weapons_img]:
current_height = 20
for img in img_list:
x = BuildImage(0, 0, background=img)
background_img.paste(x, (current_width, current_height))
current_height += x.size[1]
current_width += 600
# url = "https://genshin.pub/daily"
url = "https://bbs.mihoyo.com/ys/obc/channel/map/193"
page = await browser.new_page(viewport={'width': 860, 'height': 3000})
await page.goto(url)
await page.wait_for_timeout(3000)
file_name = str((datetime.now() - timedelta(hours=4)).date())
background_img.save(f"{IMAGE_PATH}/genshin/material/{file_name}.png")
# background_img.save(f"{IMAGE_PATH}/genshin/material/{file_name}.png")
await page.locator('//*[@id="__layout"]/div/div[2]/div[2]/div/div[1]/div[2]/div/div').screenshot(path=f"{IMAGE_PATH}/genshin/material/{file_name}.png")
await page.close()
return True
except Exception as e:
@@ -163,5 +115,3 @@ def get_background_height(weapons_img: List[str]) -> int:
last_weapon.save(weapons_img[-1])
return height
+56 -445
View File
@@ -1,219 +1,49 @@
from services.db_context import db
from typing import Optional, Union, List
from datetime import datetime, timedelta
import random
from datetime import datetime, timedelta
from typing import Optional
import pytz
from tortoise import fields
from tortoise.contrib.postgres.functions import Random
from services.db_context import Model
class Genshin(db.Model):
__tablename__ = "genshin"
class Genshin(Model):
id = db.Column(db.Integer(), primary_key=True)
user_qq = db.Column(db.BigInteger(), nullable=False)
uid = db.Column(db.BigInteger())
mys_id = db.Column(db.BigInteger())
cookie = db.Column(db.String(), default="")
today_query_uid = db.Column(db.String(), default="") # 该cookie今日查询的uid
auto_sign = db.Column(db.Boolean(), default=False)
auto_sign_time = db.Column(db.DateTime(timezone=True))
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="")
id = fields.IntField(pk=True, generated=True, auto_increment=True)
"""自增id"""
user_qq = fields.BigIntField()
"""用户id"""
uid = fields.BigIntField()
"""uid"""
mys_id: int = fields.BigIntField(null=True)
"""米游社id"""
cookie: str = fields.TextField(default="")
"""米游社cookie"""
auto_sign = fields.BooleanField(default=False)
"""是否自动签到"""
today_query_uid = fields.TextField(default="")
"""cookie今日查询uid"""
auto_sign_time = fields.DatetimeField(null=True)
"""签到日期时间"""
resin_remind = fields.BooleanField(default=False)
"""树脂提醒"""
resin_recovery_time = fields.DatetimeField(null=True)
"""满树脂提醒日期"""
bind_group: int = fields.BigIntField(null=True)
"""发送提示 绑定群聊"""
login_ticket = fields.TextField(default="")
"""login_ticket"""
stuid: str = fields.TextField(default="")
"""stuid"""
stoken: str = fields.TextField(default="")
"""stoken"""
_idx1 = db.Index("genshin_uid_idx1", "user_qq", "uid", unique=True)
@classmethod
async def add_uid(cls, user_qq: int, uid: int):
"""
说明:
添加一个uid
参数:
:param user_qq: 用户qq
:param uid: 原神uid
"""
query = cls.query.where((cls.user_qq == user_qq) & (cls.uid == uid))
user = await query.gino.first()
if not user:
await cls.create(
user_qq=user_qq,
uid=uid,
)
return True
return False
@classmethod
async def set_mys_id(cls, uid: int, mys_id: int) -> bool:
"""
说明:
设置米游社id
参数:
:param uid: 原神uid
:param mys_id: 米游社id
"""
query = cls.query.where(cls.uid == uid).with_for_update()
user = await query.gino.first()
if user:
await user.update(mys_id=mys_id).apply()
return True
return False
@classmethod
async def set_bind_group(cls, uid: int, bind_group) -> bool:
"""
说明:
绑定group_id,除私聊外的提醒将在此群发送
参数:
:param uid: uid
:param bind_group: 群号
"""
query = cls.query.where(cls.uid == uid).with_for_update()
user = await query.gino.first()
if user:
await user.update(bind_group=bind_group).apply()
return True
return False
@classmethod
async def get_bind_group(cls, uid: int) -> Optional[int]:
"""
说明:
获取用户绑定的群聊
参数:
:param uid: uid
"""
user = await cls.query.where(cls.uid == uid).gino.first()
if user:
return user.bind_group
return None
@classmethod
async def set_cookie(cls, uid: int, cookie: str) -> bool:
"""
说明:
设置cookie
参数:
:param uid: 原神uid
:param cookie: 米游社id
"""
query = cls.query.where(cls.uid == uid).with_for_update()
user = await query.gino.first()
if user:
await user.update(cookie=cookie).apply()
return True
return False
@classmethod
async def set_resin_remind(cls, uid: int, flag: bool) -> bool:
"""
说明:
设置体力提醒
参数:
:param uid: 原神uid
:param flag: 开关状态
"""
query = cls.query.where(cls.uid == uid).with_for_update()
user = await query.gino.first()
if user:
await user.update(resin_remind=flag).apply()
return True
return False
@classmethod
async def set_user_resin_recovery_time(cls, uid: int, date: datetime):
"""
说明:
设置体力完成时间
参数:
:param uid: uid
:param date: 提醒日期
"""
u = await cls.query.where(cls.uid == uid).gino.first()
if u:
await u.update(resin_recovery_time=date).apply()
@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()
if u:
return u.resin_recovery_time.astimezone(pytz.timezone("Asia/Shanghai"))
return None
@classmethod
async def get_all_resin_remind_user(cls) -> List["Genshin"]:
"""
说明:
获取所有开启体力提醒的用户
"""
return await cls.query.where(cls.resin_remind == True).gino.all()
@classmethod
async def clear_resin_remind_time(cls, uid: int) -> bool:
"""
说明:
清空提醒日期
参数:
:param uid: uid
"""
user = await cls.query.where(cls.uid == uid).gino.first()
if user:
await user.update(resin_recovery_time=None).apply()
return True
return False
@classmethod
async def set_auto_sign(cls, uid: int, flag: bool) -> bool:
"""
说明:
设置米游社/原神自动签到
参数:
:param uid: 原神uid
:param flag: 开关状态
"""
query = cls.query.where(cls.uid == uid).with_for_update()
user = await query.gino.first()
if user:
await user.update(auto_sign=flag).apply()
return True
return False
@classmethod
async def get_all_auto_sign_user(cls) -> List["Genshin"]:
"""
说明:
获取所有开启自动签到的用户
"""
return await cls.query.where(cls.auto_sign == True).gino.all()
@classmethod
async def get_all_sign_user(cls) -> List["Genshin"]:
"""
说明:
获取 原神 所有今日签到用户
"""
return await cls.query.where(cls.auto_sign_time != None).gino.all()
@classmethod
async def clear_sign_time(cls, uid: int) -> bool:
"""
说明:
清空签到日期
参数:
:param uid: uid
"""
user = await cls.query.where(cls.uid == uid).gino.first()
if user:
await user.update(auto_sign_time=None).apply()
return True
return False
class Meta:
table = "genshin"
table_description = "原神数据表"
unique_together = ("user_qq", "uid")
@classmethod
async def random_sign_time(cls, uid: int) -> Optional[datetime]:
@@ -223,262 +53,43 @@ class Genshin(db.Model):
说明:
:param uid: uid
"""
query = cls.query.where(cls.uid == uid).with_for_update()
user = await query.gino.first()
user = await cls.get_or_none(uid=uid)
if user and user.cookie:
if user.auto_sign_time and user.auto_sign_time.astimezone(
pytz.timezone("Asia/Shanghai")
pytz.timezone("Asia/Shanghai")
) - timedelta(seconds=2) >= datetime.now(pytz.timezone("Asia/Shanghai")):
return user.auto_sign_time.astimezone(pytz.timezone("Asia/Shanghai"))
hours = int(str(datetime.now()).split()[1].split(":")[0])
minutes = int(str(datetime.now()).split()[1].split(":")[1])
date = (
datetime.now()
+ timedelta(days=1)
- timedelta(hours=hours)
- timedelta(minutes=minutes - 1)
datetime.now()
+ timedelta(days=1)
- timedelta(hours=hours)
- timedelta(minutes=minutes - 1)
)
random_hours = random.randint(0, 22)
random_minutes = random.randint(1, 59)
date += timedelta(hours=random_hours) + timedelta(minutes=random_minutes)
await user.update(auto_sign_time=date).apply()
user.auto_sign_time = date
await user.save(update_fields=["auto_sign_time"])
return date
return None
@classmethod
async def get_query_cookie(cls, uid: int) -> Optional[str]:
async def random_cookie(cls, uid: int) -> Optional[str]:
"""
说明:
获取查询角色信息cookie
随机获取查询角色信息cookie
参数:
:param uid: 原神uid
"""
# 查找用户今日是否已经查找过,防止重复
query = cls.query.where(cls.today_query_uid.contains(str(uid)))
x = await query.gino.first()
if x:
await cls._add_query_uid(uid, uid)
return x.cookie
for u in await cls.query.where(cls.cookie != "").order_by(db.func.random()).gino.all():
if not u.today_query_uid or len(u.today_query_uid[:-1].split()) < 30:
await cls._add_query_uid(uid, u.uid)
return u.cookie
return None
@classmethod
async def get_user_cookie(cls, uid: int, flag: bool = False) -> Optional[str]:
"""
说明:
获取用户cookie
参数:
:param uid:原神uid
:param flag:必须使用自己的cookie
"""
cookie = await cls._get_user_data(None, uid, "cookie")
if not cookie and not flag:
cookie = await cls.get_query_cookie(uid)
return cookie
@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()
@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()
@classmethod
async def get_user_uid(cls, user_qq: int) -> Optional[int]:
"""
说明:
获取用户uid
参数:
:param user_qq:用户qq
"""
return await cls._get_user_data(user_qq, None, "uid")
@classmethod
async def get_user_mys_id(cls, uid: int) -> Optional[int]:
"""
说嘛:
获取用户米游社id
参数:
:param uid:原神id
"""
return await cls._get_user_data(None, uid, "mys_id")
@classmethod
async def delete_user_cookie(cls, uid: int):
"""
说明:
删除用户cookie
参数:
:param uid: 原神uid
"""
query = cls.query.where(cls.uid == uid).with_for_update()
user = await query.gino.first()
user = await cls.get_or_none(today_query_uid__contains=str(uid))
if user:
await user.update(cookie="").apply()
@classmethod
async def delete_user(cls, user_qq: int):
"""
说明:
删除用户数据
参数:
:param user_qq: 用户qq
"""
query = cls.query.where(cls.user_qq == user_qq).with_for_update()
user = await query.gino.first()
if not user:
return False
await user.delete()
return True
@classmethod
async def _add_query_uid(cls, uid: int, cookie_uid: int):
"""
说明:
添加每日查询重复uid的cookie
参数:
:param uid: 原神uid
:param cookie_uid: cookie的uid
"""
query = cls.query.where(cls.uid == cookie_uid).with_for_update()
user = await query.gino.first()
await user.update(today_query_uid=user.today_query_uid + f"{uid} ").apply()
@classmethod
async def _get_user_data(
cls, user_qq: Optional[int], uid: Optional[int], type_: str
) -> Optional[Union[int, str]]:
"""
说明:
获取用户数据
参数:
:param user_qq: 用户qq
:param uid: uid
:param type_: 数据类型
"""
if type_ == "uid":
user = await cls.query.where(cls.user_qq == user_qq).gino.first()
return user.uid if user else None
user = await cls.query.where(cls.uid == uid).gino.first()
if not user:
return None
if type_ == "mys_id":
return user.mys_id
elif type_ == "cookie":
return user.cookie
return None
@classmethod
async def reset_today_query_uid(cls):
for u in await cls.query.with_for_update().gino.all():
if u.today_query_uid:
await u.update(today_query_uid="").apply()
@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
for user in await cls.filter(cookie__not="").annotate(rand=Random()).all():
if not user.today_query_uid or len(user.today_query_uid[:-1].split()) < 30:
user.today_query_uid = user.today_query_uid + f"{uid} "
await user.save(update_fields=["today_query_uid"])
return user.cookie
return None
@@ -1,9 +1,10 @@
from configs.config import Config
import json
import time
import random
import hashlib
import json
import random
import string
import time
from configs.config import Config
def _md5(text):
@@ -15,7 +16,7 @@ def _md5(text):
def get_old_ds() -> str:
n = Config.get_config("genshin", "n")
i = str(int(time.time()))
r = ''.join(random.sample(string.ascii_lowercase + string.digits, 6))
r = "".join(random.sample(string.ascii_lowercase + string.digits, 6))
c = _md5("salt=" + n + "&t=" + i + "&r=" + r)
return i + "," + r + "," + c
@@ -33,7 +34,7 @@ def get_ds(q: str = "", b: dict = None) -> str:
def random_hex(length: int) -> str:
result = hex(random.randint(0, 16 ** length)).replace("0x", "").upper()
result = hex(random.randint(0, 16**length)).replace("0x", "").upper()
if len(result) < length:
result = "0" * (length - len(result)) + result
return result
+61 -48
View File
@@ -1,13 +1,16 @@
from nonebot import on_command
from nonebot.adapters.onebot.v11 import MessageEvent, GroupMessageEvent, Message
from utils.utils import is_number
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
from typing import Tuple
from nonebot import on_command
from nonebot.adapters.onebot.v11 import GroupMessageEvent, Message, MessageEvent
from nonebot.params import Command, CommandArg
from services.log import logger
from utils.depends import OneCommand
from utils.http_utils import AsyncHttpx
from utils.utils import is_number
from .._models import Genshin
__zx_plugin_name__ = "原神绑定"
__plugin_usage__ = """
@@ -43,82 +46,94 @@ 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={}"
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()):
cmd = cmd[0]
async def _(event: MessageEvent, cmd: str = OneCommand(), arg: Message = CommandArg()):
msg = arg.extract_plain_text().strip()
user = await Genshin.get_or_none(user_qq=event.user_id)
if cmd in ["原神绑定uid", "原神绑定米游社id"]:
if not is_number(msg):
await bind.finish("uid/id必须为纯数字!", at_senders=True)
msg = int(msg)
if cmd == "原神绑定uid":
uid = await Genshin.get_user_uid(event.user_id)
if uid:
await bind.finish(f"您已绑定过uid:{uid},如果希望更换uid,请先发送原神解绑")
flag = await Genshin.add_uid(event.user_id, msg)
if not flag:
if user:
await bind.finish(f"您已绑定过uid:{user.uid},如果希望更换uid,请先发送原神解绑")
if await Genshin.get_or_none(user_qq=event.user_id, uid=msg):
await bind.finish("添加失败,该uid可能已存在...")
user = await Genshin.create(user_qq=event.user_id, uid=msg)
_x = f"已成功添加原神uid:{msg}"
elif cmd == "原神绑定米游社id":
uid = await Genshin.get_user_uid(event.user_id)
if not uid:
if not user:
await bind.finish("请先绑定原神uid..")
await Genshin.set_mys_id(uid, msg)
_x = f"已成功为uid:{uid} 设置米游社id:{msg}"
user.mys_id = int(msg)
_x = f"已成功为uid:{user.uid} 设置米游社id:{msg}"
else:
if not msg:
await bind.finish("""私聊发送!!
await bind.finish(
"""私聊发送!!
1.以无痕模式打开浏览器(Edge请新建InPrivate窗口)
2.打开http://bbs.mihoyo.com/ys/并登陆
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""")
5.私聊发送:原神绑定cookie 刚刚复制的cookie"""
)
if isinstance(event, GroupMessageEvent):
await bind.finish("请立即撤回你的消息并私聊发送!")
uid = await Genshin.get_user_uid(event.user_id)
if not uid:
if not user:
await bind.finish("请先绑定原神uid..")
if msg.startswith('"') or msg.startswith("'"):
msg = msg[1:]
if msg.endswith('"') or msg.endswith("'"):
msg = msg[:-1]
await Genshin.set_cookie(uid, msg)
cookie = msg
# 用: 代替=, ,代替;
cookie = '{"' + cookie.replace('=', '": "').replace("; ", '","') + '"}'
print(cookie)
cookie = '{"' + cookie.replace("=", '": "').replace("; ", '","') + '"}'
# print(cookie)
cookie_json = json.loads(cookie)
print(cookie_json)
if 'login_ticket' not in cookie_json:
# print(cookie_json)
if "login_ticket" not in cookie_json:
await bind.finish("请发送正确完整的cookie!")
login_ticket = cookie_json['login_ticket']
user.cookie = str(msg)
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)
# 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 = 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)
user.stoken = stoken
user.stuid = stuid
user.login_ticket = login_ticket
# except Exception as e:
# await bind.finish("获取登陆信息失败,请检查cookie是否正确或更新cookie")
elif data["data"]["msg"] == "登录信息已失效,请重新登录":
await bind.finish("登录信息失效,请重新获取最新cookie进行绑定")
_x = f"已成功为uid:{uid} 设置cookie"
_x = f"已成功为uid:{user.uid} 设置cookie"
if isinstance(event, GroupMessageEvent):
await Genshin.set_bind_group(uid, event.group_id)
user.bind_group = event.group_id
if user:
await user.save(
update_fields=[
"mys_id",
"cookie",
"stoken",
"stuid",
"login_ticket",
"bind_group",
]
)
await bind.send(_x)
logger.info(
f"(USER {event.user_id}, "
@@ -129,12 +144,10 @@ async def _(event: MessageEvent, cmd: Tuple[str, ...] = Command(), arg: Message
@unbind.handle()
async def _(event: MessageEvent):
if await Genshin.delete_user(event.user_id):
await unbind.send("用户数据删除成功...")
logger.info(
f"(USER {event.user_id}, GROUP "
f"{event.group_id if isinstance(event, GroupMessageEvent) else 'private'})"
f"原神解绑"
)
else:
await unbind.send("该用户数据不存在..")
await Genshin.filter(user_qq=event.user_id).delete()
await unbind.send("用户数据删除成功...")
logger.info(
f"(USER {event.user_id}, GROUP "
f"{event.group_id if isinstance(event, GroupMessageEvent) else 'private'})"
f"原神解绑"
)
@@ -1,14 +1,17 @@
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
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 apscheduler.jobstores.base import JobLookupError
from nonebot import on_command
from nonebot.adapters.onebot.v11 import GroupMessageEvent, MessageEvent
from nonebot.params import Command
from services.log import logger
from utils.depends import OneCommand
from .._models import Genshin
from ..mihoyobbs_sign import mihoyobbs_sign
from .data_source import genshin_sign, get_sign_reward_list
from .init_task import _sign, add_job, scheduler
__zx_plugin_name__ = "原神自动签到"
__plugin_usage__ = """
@@ -39,45 +42,46 @@ genshin_matcher = on_command(
@genshin_matcher.handle()
async def _(event: MessageEvent, cmd: Tuple[str, ...] = Command()):
cmd = cmd[0]
uid = await Genshin.get_user_uid(event.user_id)
async def _(event: MessageEvent, cmd: str = OneCommand()):
user = await Genshin.get_or_none(user_qq=event.user_id)
if not user:
await genshin_matcher.finish("请先绑定user.uid...")
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为" + user.cookie)
if not user.uid or not user.cookie:
await genshin_matcher.finish("请先绑定user.uid和cookie!")
# if "account_id" not in await Genshin.get_user_cookie(user.uid, True):
# await genshin_matcher.finish("请更新cookie!")
if cmd == "原神我硬签":
try:
await genshin_matcher.send("正在进行签到...", at_sender=True)
msg = await genshin_sign(uid)
msg = await genshin_sign(user.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} 原神签到"
f"GROUP {event.group_id if isinstance(event, GroupMessageEvent) else 'private'}) UID:{user.uid} 原神签到"
)
logger.info(msg)
# 硬签,移除定时任务
try:
for i in range(3):
scheduler.remove_job(f"genshin_auto_sign_{uid}_{event.user_id}_{i}",)
scheduler.remove_job(
f"genshin_auto_sign_{user.uid}_{event.user_id}_{i}",
)
except JobLookupError:
pass
u = await Genshin.get_user_by_uid(uid)
if u and u.auto_sign:
await u.clear_sign_time(uid)
next_date = await Genshin.random_sign_time(uid)
add_job(event.user_id, uid, next_date)
if user.auto_sign:
user.auto_sign_time = None
next_date = await Genshin.random_sign_time(user.uid)
add_job(event.user_id, user.uid, next_date)
msg += f"\n{return_data}\n因开启自动签到\n下一次签到时间为:{next_date.replace(microsecond=0)}"
except Exception as e:
msg = "原神签到失败..请尝试检查cookie或报告至管理员!"
logger.info(
f"(USER {event.user_id}, "
f"GROUP {event.group_id if isinstance(event, GroupMessageEvent) else 'private'}) UID:{uid} 原神签到发生错误 "
f"GROUP {event.group_id if isinstance(event, GroupMessageEvent) else 'private'}) UID:{user.uid} 原神签到发生错误 "
f"{type(e)}:{e}"
)
msg = msg or "请检查cookie是否更新!"
@@ -85,13 +89,16 @@ async def _(event: MessageEvent, cmd: Tuple[str, ...] = Command()):
else:
for i in range(3):
try:
scheduler.remove_job(f"genshin_auto_sign_{uid}_{event.user_id}_{i}")
scheduler.remove_job(
f"genshin_auto_sign_{user.uid}_{event.user_id}_{i}"
)
except JobLookupError:
pass
if cmd[0] == "开":
await Genshin.set_auto_sign(uid, True)
next_date = await Genshin.random_sign_time(uid)
add_job(event.user_id, uid, next_date)
next_date = await Genshin.random_sign_time(user.uid)
user.auto_sign = True
user.auto_sign_time = next_date
add_job(event.user_id, user.uid, next_date)
await genshin_matcher.send(
f"已开启原神自动签到!\n下一次签到时间为:{next_date.replace(microsecond=0)}",
at_sender=True,
@@ -102,11 +109,13 @@ async def _(event: MessageEvent, cmd: Tuple[str, ...] = Command()):
f" 开启原神自动签到"
)
else:
await Genshin.set_auto_sign(uid, False)
await Genshin.clear_sign_time(uid)
user.auto_sign = False
user.auto_sign_time = None
await genshin_matcher.send(f"已关闭原神自动签到!", at_sender=True)
logger.info(
f"(USER {event.user_id}, GROUP "
f"{event.group_id if isinstance(event, GroupMessageEvent) else 'private'})"
f" 关闭原神自动签到"
)
if user:
await user.save(update_fields=["auto_sign_time", "auto_sign"])
@@ -1,14 +1,16 @@
from utils.http_utils import AsyncHttpx
from configs.config import Config
from services.log import logger
from ..mihoyobbs_sign.setting import *
from .._models import Genshin
from typing import Optional, Dict
import hashlib
import random
import string
import uuid
import time
import uuid
from typing import Dict, Optional
from configs.config import Config
from services.log import logger
from utils.http_utils import AsyncHttpx
from .._models import Genshin
from ..mihoyobbs_sign.setting import *
async def genshin_sign(uid: int) -> Optional[str]:
@@ -28,10 +30,10 @@ async def genshin_sign(uid: int) -> Optional[str]:
sign_list = await get_sign_reward_list()
get_reward = sign_list["data"]["awards"][
int(sign_info["total_sign_day"]) - 1
]["name"]
]["name"]
reward_num = sign_list["data"]["awards"][
int(sign_info["total_sign_day"]) - 1
]["cnt"]
]["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"]:
@@ -64,7 +66,7 @@ def timestamp() -> int:
def random_text(num: int) -> str:
return ''.join(random.sample(string.ascii_lowercase + string.digits, num))
return "".join(random.sample(string.ascii_lowercase + string.digits, num))
def md5(text: str) -> str:
@@ -75,8 +77,7 @@ def md5(text: str) -> str:
# 生成一个device id
def get_device_id(cookie) -> str:
return str(uuid.uuid3(uuid.NAMESPACE_URL, cookie)).replace(
'-', '').upper()
return str(uuid.uuid3(uuid.NAMESPACE_URL, cookie)).replace("-", "").upper()
async def _sign(uid: int, server_id: str = "cn_gf01") -> Optional[Dict[str, str]]:
@@ -88,18 +89,20 @@ 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=genshin_Signurl,
headers=headers,
json={"act_id": genshin_Act_id, "uid": uid, "region": server_id},
)
return req.json()
if user := await Genshin.get_or_none(uid=uid):
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"] = user.cookie
headers["x-rpc-device_id"] = get_device_id(user.cookie)
req = await AsyncHttpx.post(
url=genshin_Signurl,
headers=headers,
json={"act_id": genshin_Act_id, "uid": uid, "region": server_id},
)
return req.json()
except Exception as e:
logger.error(f"米游社签到发生错误 UID:{uid} {type(e)}:{e}")
return None
@@ -129,17 +132,22 @@ async def _get_sign_info(uid: int, server_id: str = "cn_gf01"):
if str(uid)[0] == "5":
server_id = "cn_qd01"
try:
req = await AsyncHttpx.get(
url=f"https://api-takumi.mihoyo.com/event/bbs_sign_reward/info?act_id=e202009291139501&region={server_id}&uid={uid}",
headers={
"x-rpc-app_version": str(Config.get_config("genshin", "mhyVersion")),
"Cookie": await Genshin.get_user_cookie(int(uid), True),
"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": str(Config.get_config("genshin", "client_type")),
"Referer": "https://webstatic.mihoyo.com/",
},
)
return req.json()
if user := await Genshin.get_or_none(uid=uid):
req = await AsyncHttpx.get(
url=f"https://api-takumi.mihoyo.com/event/bbs_sign_reward/info?act_id=e202009291139501&region={server_id}&uid={uid}",
headers={
"x-rpc-app_version": str(
Config.get_config("genshin", "mhyVersion")
),
"Cookie": user.cookie,
"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": str(
Config.get_config("genshin", "client_type")
),
"Referer": "https://webstatic.mihoyo.com/",
},
)
return req.json()
except Exception as e:
logger.error(f"获取签到信息发生错误 UID:{uid} {type(e)}:{e}")
return None
@@ -1,17 +1,19 @@
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
from utils.utils import scheduler, get_bot
from apscheduler.jobstores.base import ConflictingIdError
from .._models import Genshin
from datetime import datetime, timedelta
from nonebot import Driver
import nonebot
import random
import pytz
from datetime import datetime, timedelta
import nonebot
import pytz
from apscheduler.jobstores.base import ConflictingIdError
from nonebot import Driver
from models.group_member_info import GroupInfoUser
from services.log import logger
from utils.message_builder import at
from utils.utils import get_bot, scheduler
from .._models import Genshin
from ..mihoyobbs_sign import mihoyobbs_sign
from .data_source import genshin_sign
driver: Driver = nonebot.get_driver()
@@ -21,7 +23,7 @@ async def _():
"""
启动时分配定时任务
"""
g_list = await Genshin.get_all_auto_sign_user()
g_list = await Genshin.filter(auto_sign=True).all()
for u in g_list:
if u.auto_sign_time:
if date := await Genshin.random_sign_time(u.uid):
@@ -33,7 +35,8 @@ async def _():
args=[u.user_qq, u.uid, 0],
)
logger.info(
f"genshin_sign add_job:USER:{u.user_qq} UID:{u.uid} " f"{date} 原神自动签到"
f"genshin_sign add_job:USER:{u.user_qq} UID:{u.uid} "
f"{date} 原神自动签到"
)
@@ -110,10 +113,12 @@ async def _sign(user_id: int, uid: int, count: int):
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)):
group_list = await GroupInfoUser.get_user_all_group(user_id)
if group_list:
group_id = group_list[0]
await bot.send_group_msg(
group_id=group_id, message=at(user_id) + msg
)
if user := await Genshin.get_or_none(uid=uid):
group_id = user.bind_group
if not group_id:
if group_list := await GroupInfoUser.get_user_all_group(user_id):
group_id = group_list[0]
if group_id:
await bot.send_group_msg(
group_id=group_id, message=at(user_id) + msg
)
@@ -1,14 +1,16 @@
from nonebot.adapters.onebot.v11 import MessageEvent
from typing import Tuple
from nonebot import on_command
from nonebot.adapters.onebot.v11 import MessageEvent
from nonebot.params import 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:
@@ -28,12 +30,10 @@ __plugin_settings__ = {
"level": 5,
"default_status": True,
"limit_superuser": False,
"cmd": ["原神签到"],
"cmd": ["米游社签到"],
}
mihoyobbs_matcher = on_command(
"米游社签到", aliases={"米游社我硬签"}, priority=5, block=True
)
mihoyobbs_matcher = on_command("米游社签到", aliases={"米游社我硬签"}, priority=5, block=True)
@mihoyobbs_matcher.handle()
@@ -47,24 +47,29 @@ async def _(event: MessageEvent, cmd: Tuple[str, ...] = Command()):
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):
user = await Genshin.get_or_none(user_qq=user_id)
if not user or not user.uid or not user.cookie:
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)
bbs = mihoyobbs.Mihoyobbs(stuid=user.stuid, stoken=user.stoken, cookie=user.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}个米游币")
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)
# print("开始签到")
# print(mihoyobbs.today_have_get_coins)
while mihoyobbs.today_get_coins != 0 and i < 3:
# if i > 0:
await bbs.refresh_list()
@@ -74,8 +79,12 @@ async def mihoyobbs_sign(user_id):
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_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
@@ -1,9 +1,10 @@
from nonebot import on_command
from nonebot.adapters.onebot.v11 import MessageEvent, GroupMessageEvent
from services.log import logger
from .data_source import get_user_memo, get_memo
from .._models import Genshin
from nonebot.adapters.onebot.v11 import GroupMessageEvent, MessageEvent
from services.log import logger
from .._models import Genshin
from .data_source import get_memo, get_user_memo
__zx_plugin_name__ = "原神便笺查询"
__plugin_usage__ = """
@@ -27,30 +28,27 @@ __plugin_settings__ = {
__plugin_block_limit__ = {}
query_memo_matcher = on_command("原神便签查询", aliases={"原神便笺查询", "yss"}, priority=5, block=True)
query_memo_matcher = on_command(
"原神便签查询", aliases={"原神便笺查询", "yss"}, priority=5, block=True
)
@query_memo_matcher.handle()
async def _(event: MessageEvent):
uid = await Genshin.get_user_uid(event.user_id)
if not uid or not await Genshin.get_user_cookie(uid, True):
user = await Genshin.get_or_none(user_qq=event.user_id)
if not user or not user.uid or not user.cookie:
await query_memo_matcher.finish("请先绑定uid和cookie!")
if isinstance(event, GroupMessageEvent):
uname = event.sender.card or event.sender.nickname
else:
uname = event.sender.nickname
data = await get_user_memo(event.user_id, uid, uname)
data = await get_user_memo(event.user_id, user.uid, uname)
if data:
await query_memo_matcher.send(data)
logger.info(
f"(USER {event.user_id}, "
f"GROUP {event.group_id if isinstance(event, GroupMessageEvent) else 'private'}) "
f"使用原神便笺查询 uid:{uid}"
f"使用原神便笺查询 uid:{user.uid}"
)
else:
await query_memo_matcher.send("未查询到数据...")
@@ -1,20 +1,22 @@
from typing import Optional, Union
from nonebot.adapters.onebot.v11 import MessageSegment
from configs.config import Config
from asyncio.exceptions import TimeoutError
from services.log import logger
from configs.path_config import IMAGE_PATH
from utils.image_utils import BuildImage
from utils.http_utils import AsyncHttpx
from utils.utils import get_user_avatar
from utils.message_builder import image
from .._utils import get_ds
from .._models import Genshin
from io import BytesIO
from nonebot import Driver
import asyncio
import nonebot
from asyncio.exceptions import TimeoutError
from io import BytesIO
from typing import Optional, Tuple, Union
import nonebot
from nonebot import Driver
from nonebot.adapters.onebot.v11 import MessageSegment
from configs.config import Config
from configs.path_config import IMAGE_PATH
from services.log import logger
from utils.http_utils import AsyncHttpx
from utils.image_utils import BuildImage
from utils.message_builder import image
from utils.utils import get_user_avatar
from .._models import Genshin
from .._utils import get_ds
driver: Driver = nonebot.get_driver()
@@ -26,10 +28,7 @@ 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", "chengehu.png",
"zhibian.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",
@@ -45,8 +44,9 @@ 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"
@@ -57,21 +57,17 @@ async def get_user_memo(user_id: int, uid: int,
return await parse_data_and_draw(user_id, uid, server_id, uname)
async def get_memo(uid: str, server_id: str) -> "Union[str, dict], int":
async def get_memo(uid: str, server_id: str) -> Tuple[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))
"Cookie": await Genshin.random_cookie(uid),
},
)
data = req.json()
@@ -85,18 +81,13 @@ 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,
75,
color="#E0D9D1",
font="HYWenHei-85W.ttf",
font_size=20)
text_bk = BuildImage(350,
75,
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(
@@ -119,15 +110,14 @@ def create_border(image_name: str, content: str, notice_text: str,
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
@@ -138,11 +128,13 @@ async def parse_data_and_draw(user_id: int, uid: str, server_id: str,
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"] # 树脂全部回复时间
@@ -157,33 +149,24 @@ def _parse_data_and_draw(data: dict, user_avatar: BytesIO, uid: int,
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_state = data["transformer"]["recovery_time"]["reached"] # 参量质变仪状态
transformer_recovery_time = data["transformer"]["recovery_time"]["Day"] # 参量质变仪回复时间
transformer_recovery_hour = data["transformer"]["recovery_time"][
"Hour"] # 参量质变仪回复时间
"Hour"
] # 参量质变仪回复时间
coin_minute, coin_second = divmod(int(coin_recovery_time), 60)
coin_hour, coin_minute = divmod(coin_minute, 60)
#print(data)
# print(data)
minute, second = divmod(int(resin_recovery_time), 60)
hour, minute = divmod(minute, 60)
A = BuildImage(1030,
570,
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,
)
@@ -225,40 +208,39 @@ def _parse_data_and_draw(data: dict, user_avatar: BytesIO, uid: int,
"chengehu.png",
"洞天财翁-洞天宝钱",
"洞天财翁已达到存储上限"
if current_coin == max_coin else f"{coin_hour}小时{coin_minute}分钟后存满",
if current_coin == max_coin
else f"{coin_hour}小时{coin_minute}分钟后存满",
f"{current_coin}/{max_coin}",
)
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 "冷却中",
"不存在"
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_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,
82,
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)
@@ -1,11 +1,13 @@
from httpx import ConnectError
from nonebot import on_command
from nonebot.adapters.onebot.v11 import MessageEvent, GroupMessageEvent, Message
from services.log import logger
from .data_source import query_role_data
from .._models import Genshin
from utils.utils import is_number
from nonebot.adapters.onebot.v11 import GroupMessageEvent, Message, MessageEvent
from nonebot.params import CommandArg
from services.log import logger
from utils.utils import is_number
from .._models import Genshin
from .data_source import query_role_data
__zx_plugin_name__ = "原神玩家查询"
__plugin_usage__ = """
@@ -29,7 +31,9 @@ __plugin_settings__ = {
__plugin_block_limit__ = {}
query_role_info_matcher = on_command("原神玩家查询", aliases={"原神玩家查找", "ys"}, priority=5, block=True)
query_role_info_matcher = on_command(
"原神玩家查询", aliases={"原神玩家查找", "ys"}, priority=5, block=True
)
@query_role_info_matcher.handle()
@@ -39,24 +43,25 @@ async def _(event: MessageEvent, arg: Message = CommandArg()):
if not is_number(msg):
await query_role_info_matcher.finish("查询uid必须为数字!")
msg = int(msg)
if not msg:
uid = await Genshin.get_user_uid(event.user_id)
uid = None
user = await Genshin.get_or_none(user_qq=event.user_id)
if not msg and user:
uid = user.uid
else:
uid = msg
if not uid: # or not await Genshin.get_user_cookie(uid):
await query_role_info_matcher.finish("请先绑定uid和cookie!")
nickname = event.sender.card or event.sender.nickname
mys_id = await Genshin.get_user_mys_id(uid)
data = await query_role_data(event.user_id, uid, mys_id, nickname)
if data:
await query_role_info_matcher.send(data)
logger.info(
f"(USER {event.user_id}, "
f"GROUP {event.group_id if isinstance(event, GroupMessageEvent) else 'private'}) 使用原神玩家查询 uid:{uid}"
)
else:
await query_role_info_matcher.send("查询失败..")
mys_id = user.mys_id if user else None
try:
data = await query_role_data(event.user_id, uid, mys_id, nickname)
if data:
await query_role_info_matcher.send(data)
logger.info(
f"(USER {event.user_id}, "
f"GROUP {event.group_id if isinstance(event, GroupMessageEvent) else 'private'}) 使用原神玩家查询 uid:{uid}"
)
else:
await query_role_info_matcher.send("查询失败..")
except ConnectError:
await query_role_info_matcher.send("网络出小差啦~")
@@ -1,11 +1,14 @@
from typing import Optional, List, Dict, Union
from .draw_image import init_image, get_genshin_image
from typing import Dict, List, Optional, Tuple, Union
from nonebot.adapters.onebot.v11 import MessageSegment
from .._utils import get_ds, element_mastery
from configs.config import Config
from services.log import logger
from utils.http_utils import AsyncHttpx
from configs.config import Config
from .._models import Genshin
from .._utils import element_mastery, get_ds
from .draw_image import get_genshin_image, init_image
try:
import ujson as json
@@ -96,66 +99,66 @@ b=body q=query
"""
async def get_info(uid_: str, server_id: str) -> "Optional[Union[dict, str]], int":
try:
req = await AsyncHttpx.get(
url=f"https://api-takumi-record.mihoyo.com/game_record/app/genshin/api/index?server={server_id}&role_id={uid_}",
headers={
"Accept": "application/json, text/plain, */*",
"DS": get_ds(f"role_id={uid_}&server={server_id}"),
"Origin": "https://webstatic.mihoyo.com",
"x-rpc-app_version": Config.get_config("genshin", "mhyVersion"),
"User-Agent": "Mozilla/5.0 (Linux; Android 9; Unspecified Device) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/39.0.0.0 Mobile Safari/537.36 miHoYoBBS/2.2.0",
"x-rpc-client_type": Config.get_config("genshin", "client_type"),
"Referer": "https://webstatic.mihoyo.com/app/community-game-records/index.html?v=6",
"Accept-Encoding": "gzip, deflate",
"Accept-Language": "zh-CN,en-US;q=0.8",
"X-Requested-With": "com.mihoyo.hyperion",
"Cookie": await Genshin.get_user_cookie(int(uid_))
},
)
data = req.json()
if data["message"] == "OK":
return data["data"], 200
return data["message"], 999
except Exception as e:
logger.error(f"访问失败,请重试! {type(e)}: {e}")
async def get_info(uid_: str, server_id: str) -> Tuple[Optional[Union[dict, str]], int]:
# try:
req = await AsyncHttpx.get(
url=f"https://api-takumi-record.mihoyo.com/game_record/app/genshin/api/index?server={server_id}&role_id={uid_}",
headers={
"Accept": "application/json, text/plain, */*",
"DS": get_ds(f"role_id={uid_}&server={server_id}"),
"Origin": "https://webstatic.mihoyo.com",
"x-rpc-app_version": Config.get_config("genshin", "mhyVersion"),
"User-Agent": "Mozilla/5.0 (Linux; Android 9; Unspecified Device) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/39.0.0.0 Mobile Safari/537.36 miHoYoBBS/2.2.0",
"x-rpc-client_type": Config.get_config("genshin", "client_type"),
"Referer": "https://webstatic.mihoyo.com/app/community-game-records/index.html?v=6",
"Accept-Encoding": "gzip, deflate",
"Accept-Language": "zh-CN,en-US;q=0.8",
"X-Requested-With": "com.mihoyo.hyperion",
"Cookie": await Genshin.random_cookie(uid_),
},
)
data = req.json()
if data["message"] == "OK":
return data["data"], 200
return data["message"], 999
# except Exception as e:
# logger.error(f"访问失败,请重试! {type(e)}: {e}")
return None, -1
async def get_character(
uid: str, character_ids: List[str], server_id="cn_gf01"
) -> Optional[dict]:
try:
req = await AsyncHttpx.post(
url="https://api-takumi-record.mihoyo.com/game_record/app/genshin/api/character",
headers={
"Accept": "application/json, text/plain, */*",
"DS": get_ds(
"",
{
"character_ids": character_ids,
"role_id": uid,
"server": server_id,
},
),
"Origin": "https://webstatic.mihoyo.com",
"Cookie": await Genshin.get_user_cookie(int(uid)),
"x-rpc-app_version": Config.get_config("genshin", "mhyVersion"),
"User-Agent": "Mozilla/5.0 (iPhone; CPU iPhone OS 13_2_3 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) miHoYoBBS/2.11.1",
"x-rpc-client_type": "5",
"Referer": "https://webstatic.mihoyo.com/",
"Accept-Encoding": "gzip, deflate",
"Accept-Language": "zh-CN,en-US;q=0.8",
"X-Requested-With": "com.mihoyo.hyperion",
},
json={"character_ids": character_ids, "role_id": uid, "server": server_id},
)
data = req.json()
if data["message"] == "OK":
return data["data"]
except Exception as e:
logger.error(f"访问失败,请重试! {type(e)}: {e}")
# try:
req = await AsyncHttpx.post(
url="https://api-takumi-record.mihoyo.com/game_record/app/genshin/api/character",
headers={
"Accept": "application/json, text/plain, */*",
"DS": get_ds(
"",
{
"character_ids": character_ids,
"role_id": uid,
"server": server_id,
},
),
"Origin": "https://webstatic.mihoyo.com",
"Cookie": await Genshin.random_cookie(uid),
"x-rpc-app_version": Config.get_config("genshin", "mhyVersion"),
"User-Agent": "Mozilla/5.0 (iPhone; CPU iPhone OS 13_2_3 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) miHoYoBBS/2.11.1",
"x-rpc-client_type": "5",
"Referer": "https://webstatic.mihoyo.com/",
"Accept-Encoding": "gzip, deflate",
"Accept-Language": "zh-CN,en-US;q=0.8",
"X-Requested-With": "com.mihoyo.hyperion",
},
json={"character_ids": character_ids, "role_id": uid, "server": server_id},
)
data = req.json()
if data["message"] == "OK":
return data["data"]
# except Exception as e:
# logger.error(f"访问失败,请重试! {type(e)}: {e}")
return None
@@ -205,7 +208,7 @@ def parsed_data(
"image": world["icon"],
"name": world["name"],
"offerings": world["offerings"],
"icon": world["icon"]
"icon": world["icon"],
}
world_data_dict[world["name"]] = _x
home_data_list = []
@@ -231,21 +234,21 @@ async def get_mys_data(uid: str, mys_id: Optional[str]) -> Optional[List[Dict]]:
:param mys_id: 米游社id
"""
if mys_id:
try:
req = await AsyncHttpx.get(
url=f"https://api-takumi-record.mihoyo.com/game_record/card/wapi/getGameRecordCard?uid={mys_id}",
headers={
"DS": get_ds(f"uid={mys_id}"),
"x-rpc-app_version": Config.get_config("genshin", "mhyVersion"),
"User-Agent": "Mozilla/5.0 (iPhone; CPU iPhone OS 13_2_3 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) miHoYoBBS/2.11.1",
"x-rpc-client_type": "5",
"Referer": "https://webstatic.mihoyo.com/",
"Cookie": await Genshin.get_user_cookie(int(uid))
},
)
data = req.json()
if data["message"] == "OK":
return data["data"]["list"]
except Exception as e:
logger.error(f"访问失败,请重试! {type(e)}: {e}")
# try:
req = await AsyncHttpx.get(
url=f"https://api-takumi-record.mihoyo.com/game_record/card/wapi/getGameRecordCard?uid={mys_id}",
headers={
"DS": get_ds(f"uid={mys_id}"),
"x-rpc-app_version": Config.get_config("genshin", "mhyVersion"),
"User-Agent": "Mozilla/5.0 (iPhone; CPU iPhone OS 13_2_3 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) miHoYoBBS/2.11.1",
"x-rpc-client_type": "5",
"Referer": "https://webstatic.mihoyo.com/",
"Cookie": await Genshin.random_cookie(uid),
},
)
data = req.json()
if data["message"] == "OK":
return data["data"]["list"]
# except Exception as e:
# logger.error(f"访问失败,请重试! {type(e)}: {e}")
return None
@@ -234,7 +234,10 @@ def get_home_data_image(home_data_list: List[Dict]) -> BuildImage:
画出家园数据
:param home_data_list: 家园列表
"""
h = 130 + 340 * len(home_data_list)
homes = os.listdir(image_path / "homes")
homes.remove("lock.png")
homes.sort()
h = 130 + 340 * len(homes)
region = BuildImage(
550, h, color="#E3DBD1", font="HYWenHei-85W.ttf", font_size=40
)
@@ -250,9 +253,6 @@ def get_home_data_image(home_data_list: List[Dict]) -> BuildImage:
region.text((0, region.h - 70), f"仙力: 0", center_type="by_width")
region.circle_corner(30)
height = 100
homes = os.listdir(image_path / "homes")
homes.remove("lock.png")
homes.sort()
unlock_home = [x["name"] for x in home_data_list]
for i, file in enumerate(homes):
home_img = image_path / "homes" / file
@@ -1,6 +1,7 @@
from utils.utils import scheduler
from .._models import Genshin
from services.log import logger
from utils.utils import scheduler
from .._models import Genshin
@scheduler.scheduled_job(
@@ -10,7 +11,7 @@ from services.log import logger
)
async def _():
try:
await Genshin.reset_today_query_uid()
await Genshin.all().update(today_query_uid="")
logger.warning(f"重置原神查询记录成功..")
except Exception as e:
logger.error(f"重置原神查询记录失败. {type(e)}:{e}")

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