Compare commits

..
333 Commits
Author SHA1 Message Date
HibiKier 216d32cf21 Update README.md 2022-06-19 23:16:11 +08:00
HibiKier 248fae4fe6 Update __version__ 2022-06-19 23:15:40 +08:00
HibiKier b479b6839d Update README.md 2022-06-19 22:29:44 +08:00
HibiKier 1a4e1e9515 Merge pull request #835 from yajiwa/main
词条优化问题展示,修改含机器人昵称和@逻辑
2022-06-19 22:29:20 +08:00
yajiwa e5160a4ee8 Merge branch 'HibiKier:main' into main 2022-06-19 17:26:13 +08:00
yajiwa 56ea3964a7 update word_bank 2022-06-19 17:24:50 +08:00
HibiKier ad73778286 update README.md 2022-06-19 16:55:02 +08:00
HibiKier 3e548f48c8 Merge pull request #828 from McElaga/main
修复原神树脂重复提醒的bug
2022-06-19 16:54:25 +08:00
HibiKier ae466931fa fix webui 2022-06-19 16:44:37 +08:00
HibiKier 68052b526f update image_utils 2022-06-18 15:23:36 +08:00
HibiKier 903b5b88a7 update image_utils 2022-06-18 15:09:55 +08:00
HibiKier 007823d6b2 update README.md 2022-06-18 15:03:05 +08:00
HibiKier db5c4fb349 Merge pull request #830 from po-lan/main
我不太会提交单个文件,我就把俄罗斯轮盘的文件 改回去了,再提交的
2022-06-18 15:00:46 +08:00
HibiKier c37901290a fix nickname 2022-06-18 14:57:54 +08:00
HibiKier da67f1f6f8 update webui 2022-06-18 14:48:46 +08:00
HibiKier 5172fa69e5 fix web ui 2022-06-18 14:31:44 +08:00
po-lan dd7280cd0d 恢复 原先的文件 2022-06-18 14:08:59 +08:00
McElaga 0d0f522c88 Merge branch 'HibiKier:main' into main 2022-06-18 09:27:12 +08:00
McElaga 5429e6ed5c Merge branch 'main' of https://github.com/McElaga/zhenxun_bot 2022-06-18 09:15:24 +08:00
McElaga 9db69fa05b 修复一点逻辑小问题 2022-06-18 09:15:19 +08:00
HibiKier 3b8d52bcb5 Update README.md 2022-06-17 18:50:37 +08:00
HibiKier 807f39a336 Update README.md 2022-06-17 18:50:15 +08:00
HibiKier fea2f30461 Merge pull request #820 from LambdaYH/patch-2
修复wordbank的一些小问题
2022-06-17 18:49:42 +08:00
po-lan 5d97e3f95f Update __init__.py 2022-06-16 16:15:39 +08:00
po-lan 63e6ca3e99 Update __init__.py 2022-06-16 15:40:46 +08:00
po-lan b11590ec3c ban掉被击毙的人一段时间 2022-06-16 15:38:20 +08:00
po-lan 3a43ed6919 如果需要 保存到 config 可以少调用一次 2022-06-16 14:38:41 +08:00
po-lan 15ce9c91fb Update __init__.py 2022-06-16 14:25:22 +08:00
LambdaYH 0e8d7537d4 fix wordbank 2022-06-16 00:43:32 +08:00
Cinte 1fca0787be fix: 修复之前更改正则引入的问题 2022-06-15 23:16:39 +08:00
HibiKier af30285c00 Update browser.py 2022-06-15 10:08:43 +08:00
HibiKier a84cab7afd replace bt url 2022-06-14 20:40:11 +08:00
HibiKier 593573ddfb fix pix 2022-06-14 19:48:32 +08:00
HibiKier e9e9f81bdb Merge pull request #806 from LambdaYH/patch-2
fix: 修复添加词条时对answer中图片的处理
2022-06-14 19:31:53 +08:00
HibiKier 61f624841b Merge pull request #810 from LambdaYH/patch-3
fix: 自动更新群组
2022-06-14 19:31:18 +08:00
HibiKier 4bb38a2730 Merge pull request #812 from po-lan/main
可关闭 ssl 验证
2022-06-14 19:22:25 +08:00
HibiKier d4ec0df00c Update http_utils.py 2022-06-14 19:22:12 +08:00
po-lan c8a7d855f8 Update http_utils.py 2022-06-14 18:47:43 +08:00
Cinte 73a126dcd5 fix: 自动更新群组 2022-06-14 09:18:03 +08:00
HibiKier 0736533389 fix search_image 2022-06-13 22:16:45 +08:00
Cinte 2695d658ab fix: 修复添加词条时对answer中图片的处理 2022-06-13 22:11:18 +08:00
HibiKier 947166e793 Merge pull request #804 from po-lan/main
Windows 重启适配
2022-06-13 20:20:30 +08:00
po-lan 80c49a7cf5 Update __init__.py 2022-06-13 15:34:28 +08:00
po-lan 55bfa8f669 Update __init__.py
Windows 重启适配
2022-06-13 15:24:28 +08:00
HibiKier 74ade14f5e Update README.md 2022-06-13 11:07:34 +08:00
HibiKier c9b5218ebc Merge pull request #798 from CopilotLaLaLa/main
webui中token.json文件写入补充
2022-06-13 11:07:11 +08:00
HibiKier b324b2bd65 Merge pull request #800 from yajiwa/main
update setu_img
2022-06-13 11:06:20 +08:00
HibiKier 35c038efa2 Update utils.py 2022-06-13 11:05:16 +08:00
HibiKier caa404d58d Update utils.py 2022-06-13 11:03:21 +08:00
yajiwa 830103b9d5 update change_img_md5 2022-06-13 10:59:29 +08:00
yajiwa 6e18379597 update setu_img 2022-06-13 10:08:51 +08:00
yajiwa 6effbacb28 add change_img_md5 2022-06-13 10:08:09 +08:00
CopilotLaLaLa a20a1a0234 同步原仓库 2022-06-12 15:23:10 +00:00
CopilotLaLaLa 3667724061 token_json文件写入 2022-06-12 15:16:07 +00:00
HibiKier 785179e4a7 update pix 2022-06-11 17:57:19 +08:00
HibiKier 715338f821 Merge pull request #791 from mmmjie/main
两处BUG的修复
2022-06-10 21:31:51 +08:00
mmmjie 7d5a82ab6d Update update_picture.py
对于temp/temp的错误路径,减少了一个temp
TEMP_PATH 已经指向了一个temp了
2022-06-10 00:37:18 +08:00
mmmjie e764cfcc1c Update white2black_image.py
随着7b836ac 对utils/image_utils.py 的改动
调整此插件对于 utils/image_utils.py改动的引用部分
2022-06-10 00:35:57 +08:00
HibiKier 363acf37bc fix bug and update 2022-06-09 20:39:48 +08:00
HibiKier 5fd687c1e4 Merge pull request #778 from SinKy-Yan/main
fix auto update bug
2022-06-09 19:37:26 +08:00
HibiKier dbedcbb93d Update pyproject.toml 2022-06-08 11:51:32 +08:00
HibiKier 9e9611f11f Update README.md 2022-06-08 11:43:10 +08:00
HibiKier 44002838da Merge pull request #779 from MingxuanGame/main
chore(deps): update `nonebot-adapter-onebot`
2022-06-08 11:42:43 +08:00
MingxuanGame 4daab8459f chore(deps): update nonebot-adapter-onebot 2022-06-07 11:57:34 +00:00
HibiKier 5a002f75cc Merge pull request #777 from LambdaYH/patch-1
fix image_utils.py
2022-06-07 19:27:31 +08:00
源源球球✨ a2180a1365 fix auto update bug 2022-06-07 10:19:23 +00:00
Cinte 7b836acf36 fix image_utils.py
https://github.com/HibiKier/zhenxun_bot/commit/ae8a214a3a5c08a51cacd1dc37583c0b32e6332f
之前这个commit里面看到函数参数有写着int类型就把`font_size`类型转换给去了,结果导致了部分地方输入float类型后报错。现在改回来
2022-06-07 17:06:16 +08:00
HibiKier a112a561cc Update README.md 2022-06-05 20:30:49 +08:00
HibiKier 6dcc2a69f4 Update README.md 2022-06-05 19:56:42 +08:00
HibiKier 74e9c55a09 update v0.1.5.8 2022-06-05 19:51:23 +08:00
HibiKier d4d6e736a4 Merge pull request #764 from McElaga/main
金币红包功能增加更多封面
2022-06-05 19:08:51 +08:00
HibiKier 5cf9938fa2 Merge pull request #763 from RShock/main
fix gold_redbag
2022-06-05 19:04:40 +08:00
McElaga 72ad0ad983 commit on main 2022-06-04 12:34:18 +08:00
咕咕型小r 690750d91e fix gold_redbag 2022-06-04 12:09:05 +08:00
HibiKier 9f1dfc4b9c Merge pull request #756 from McElaga/main
修复原神树脂提醒bug(已实测)
2022-06-03 16:15:50 +08:00
HibiKier 05eab5307f Update README.md 2022-06-03 16:13:29 +08:00
HibiKier b0fe70c960 Merge pull request #745 from LambdaYH/main
稍微调整了一下查看所有请求中的年龄绘制
2022-06-03 16:07:01 +08:00
McElaga 09d7992bc4 comit on main 2022-06-03 12:09:15 +08:00
McElaga c832a432c1 commit on main 2022-06-03 11:52:40 +08:00
HibiKier 41c22f4314 fix ShopRegister 2022-06-03 01:14:34 +08:00
HibiKier 252d45c79b fix webui update plugin_shop 2022-06-03 00:59:00 +08:00
Cinte ae8a214a3a Update image_utils.py 2022-06-02 10:06:55 +08:00
LambdaYH 86ce6d7664 fix: 稍微调整了一下查看所有请求中的年龄绘制 2022-06-02 10:02:03 +08:00
McElaga 67e96d6106 commit on main 2022-06-02 08:55:24 +08:00
HibiKier 24d9ec2b75 Update __init__.py 2022-06-01 11:05:12 +08:00
HibiKier 501e47c6ec fix image2text 2022-05-31 20:45:28 +08:00
HibiKier 5534a5a5c2 Merge pull request #707 from HiDolen/main
修复抽卡插件:方舟更新UP池信息时,若公告的第一个池子过期会导致无法更新UP池信息
2022-05-31 20:14:48 +08:00
HibiKier 7246aabf40 Merge pull request #712 from McElaga/main
修复发红包图片背景未透明化;修复原神树脂提醒参数错误
2022-05-31 20:07:35 +08:00
HibiKier 604ffae87e Merge pull request #711 from LambdaYH/patch-1
bot_friend_group将group_handle.修改为friend_handle.
2022-05-31 20:06:12 +08:00
HibiKier 2d5c16456b Merge pull request #721 from LambdaYH/patch-2
修复开启/关闭全部功能时帮助图片未重绘
2022-05-31 20:01:39 +08:00
Cinte d44f7633dc 修复开启/关闭全部功能时帮助图片未重绘 2022-05-30 20:43:48 +08:00
Cinte 469ac4035c 大概是把group_handle写成了friend_handle 2022-05-30 11:27:14 +08:00
McElaga dab218a6d7 commit on main 2022-05-30 11:14:47 +08:00
Dolen d6fc675d57 修复抽卡插件:避免被跳过的卡池信息污染后面的卡池信息 2022-05-30 11:11:49 +08:00
McElaga 04148f7365 commit on main 2022-05-30 11:00:19 +08:00
Dolen 4c34ac570f 修复抽卡插件:方舟更新UP池信息时,若公告的第一个池子过期会导致无法更新UP池信息 2022-05-29 23:13:36 +08:00
HibiKier fbdb8758a3 Update README.md 2022-05-29 22:21:26 +08:00
HibiKier 2d08747d70 Merge pull request #705 from yajiwa/main
商店插件判断是否有requirements.txt文件
2022-05-29 22:21:03 +08:00
yajiwa 0b4bacbbd1 update plugin_shop 2022-05-29 21:57:01 +08:00
yajiwa e0412c7edb Merge branch 'HibiKier:main' into main 2022-05-29 21:03:12 +08:00
HibiKier 38501cebf1 Update data_source.py 2022-05-29 20:56:03 +08:00
HibiKier ee45d0217e Update __init__.py 2022-05-29 20:55:32 +08:00
HibiKier 558f362919 update v0.1.5.8 2022-05-29 20:47:18 +08:00
HibiKier fb9db2f50c fix 2022-05-29 19:57:57 +08:00
HibiKier c1862e9c68 fix bt 2022-05-29 19:15:29 +08:00
HibiKier 731bde8e28 update v0.1.5.7 2022-05-29 19:14:36 +08:00
HibiKier 47c87f8694 update db_context 2022-05-28 22:50:23 +08:00
HibiKier 5558cdacc3 Update README.md 2022-05-28 21:09:58 +08:00
HibiKier 4650d244a5 Merge pull request #688 from McElaga/main
修复微博热搜报错,发红包小bug
2022-05-28 21:08:17 +08:00
HibiKier c65b8aae07 update webui 2022-05-28 20:57:08 +08:00
HibiKier 7ba2fb9584 update black_word 2022-05-28 20:55:14 +08:00
HibiKier 9a2cfd0c9b zhenxun_update add config 2022-05-28 18:28:38 +08:00
HibiKier a7d61b1f2b add config 2022-05-28 18:12:08 +08:00
HibiKier 7ab185f7d7 fix poke 2022-05-28 17:48:28 +08:00
HibiKier 724541d691 fix bug 2022-05-28 17:22:51 +08:00
McElaga baa6cda9c2 commit on main 2022-05-28 10:31:27 +08:00
HibiKier 9c987e9aad log2cn 2022-05-26 22:49:48 +08:00
HibiKier 134974ea9c Merge pull request #667 from NumberSir/main
更新抽卡插件
2022-05-26 22:43:11 +08:00
HibiKier 2d946958e3 Merge pull request #668 from HanakoLuna/patch-1
更新BT搜索源地址
2022-05-26 22:42:37 +08:00
Lya_Yami ffe010e9ce 更新BT搜索源地址 2022-05-26 20:47:12 +08:00
HibiKier 1c67252b0c Update README.md 2022-05-26 20:26:14 +08:00
HibiKier 60c1be25d4 Merge pull request #643 from LambdaYH/patch-1
修复[滴滴滴]会被转义成[滴滴滴]导致无法触发的问题
2022-05-26 20:25:45 +08:00
Number_Sir 18323a0916 更新抽卡插件
修复了方舟抽卡图文不符的问题
修复了方舟许多三星抽不出来的问题
修复了方舟重载卡池失败的问题
2022-05-26 20:07:09 +08:00
HibiKier 9d98abded9 Merge pull request #660 from Sakuracio/main
Replace URL
2022-05-26 19:49:23 +08:00
MangataAkihi b06a742b8c Update README.md 2022-05-26 17:20:37 +08:00
github-actions[bot] 87fe44f188 Auto generate poetry.lock 2022-05-26 09:18:16 +00:00
Cinte 96f6ef1693 修复[滴滴滴]会被转义成[滴滴滴]导致无法触发的问题 2022-05-26 09:59:45 +08:00
github-actions[bot] 374b7b7261 Auto generate poetry.lock 2022-05-25 23:42:48 +00:00
HibiKier 23c4141e55 Update README.md 2022-05-25 20:53:09 +08:00
HibiKier c21200c2a9 Merge pull request #610 from yajiwa/main
修复碧蓝航线/明日方舟up池解析出错的问题
2022-05-25 20:52:58 +08:00
HibiKier 4a8b94259d Merge pull request #602 from HiDolen/main-patch
修改翻译插件,添加缺失的冒号
2022-05-25 20:49:51 +08:00
HibiKier 28a58df27b Merge pull request #607 from MingxuanGame/main
feat: stream downloading and progress
2022-05-25 20:49:20 +08:00
HibiKier caaf486bbe Merge branch 'main' into main 2022-05-25 20:49:07 +08:00
HibiKier 0a972a399e Merge pull request #617 from mouhensama/main
把gamedraw的ba卡池搬运了过来并且进行了真寻的适配。
2022-05-25 20:44:07 +08:00
HibiKier 245dad1239 Merge pull request #624 from HiDolen/main-patch2
修改nickname插件:一处文案错误,添加敏感词
2022-05-25 20:42:51 +08:00
Dolen a6b6a2fbff 修改nickname插件:一处文案错误 2022-05-25 17:21:00 +08:00
Dolen 53000a949b 修改nickname插件:添加一个敏感词 2022-05-25 17:03:44 +08:00
Dolen eae1dd59a7 修改nickname插件:一处文案错误 2022-05-25 17:02:09 +08:00
HibiKier e6347a76a8 Update issue-template.md 2022-05-25 14:44:28 +08:00
HibiKier 13e0fb3df7 Delete bug-issue-template.md 2022-05-25 13:42:22 +08:00
HibiKier 3f1b23ef8a Update issue templates 2022-05-25 13:41:35 +08:00
HibiKier acae7f0658 Create ISSUE_TEMPLATE.md 2022-05-25 13:37:29 +08:00
HibiKier a9456b1bd6 Merge pull request #608 from AkashiCoin/main
Use Action to automatically generate poetry.lock
2022-05-25 13:35:39 +08:00
mouhensama e4d5a230d2 把nonebot_plugin_gamedraw的ba卡池搬运了过来并且进行了真寻的适配。 2022-05-25 11:47:08 +08:00
HibiKier fedf44b9fa Update README.md 2022-05-25 10:54:52 +08:00
HibiKier 451db104e8 Update config.py 2022-05-25 08:52:22 +08:00
HibiKier 9ac4c7f4e3 Update README.md 2022-05-25 08:52:15 +08:00
HibiKier 7cf1c6f016 Update README.md 2022-05-25 08:41:59 +08:00
yajiwa 6dd87ee301 fix draw_card 2022-05-25 07:02:42 +08:00
MingxuanGame 96837fa8db fix: rich from dev dependent to main dependent 2022-05-25 01:19:12 +08:00
MingxuanGame d816e76962 fix: rich from dev dependent to main dependent 2022-05-25 01:13:27 +08:00
AkashiCoin 310945d9cd Merge branch 'HibiKier:main' into main 2022-05-25 01:01:11 +08:00
MingxuanGame 1aba480495 feat: stream downloading and progress 2022-05-25 00:56:41 +08:00
HibiKier 07407749fe Update README.md 2022-05-24 22:05:38 +08:00
HibiKier dfab70dd90 Merge pull request #581 from HiDolen/main
修复抽卡插件:方舟抽卡的抽取和显示问题
2022-05-24 22:05:13 +08:00
Dolen 3b15469c98 修改翻译插件,发送消息添加冒号 2022-05-24 08:59:14 -05:00
HibiKier e4590800c2 Update README.md 2022-05-24 21:20:59 +08:00
HibiKier c63d4b8ad0 Merge pull request #573 from jk6589jk/patch-2
修复复读不能复读图片的问题
2022-05-24 21:19:41 +08:00
HibiKier 1fb913e2ed Merge pull request #576 from MingxuanGame/main
refactor&fix(manager): modify argument
2022-05-24 21:18:22 +08:00
HibiKier 72ace2dab7 Merge pull request #579 from yajiwa/main
更新"微博热搜"接口
2022-05-24 21:14:51 +08:00
HibiKier 3bcef18629 Merge pull request #600 from sdy623/main
bugfix: 修正了签到时日期时间的显示不补齐零的bug (符合日期时间表示法 ISO 8601)
2022-05-24 21:13:33 +08:00
sdy623 c91758519c bugfix: 修正了签到时日期时间的显示不补齐零的bug (符合日期时间表示法 ISO 8601) 2022-05-24 20:19:32 +08:00
jk6589jk 9c8f2e7aa0 Update fudu.py 2022-05-24 14:01:08 +08:00
Dolen 5f377af6d5 修复抽卡插件:方舟抽卡的抽取和显示问题 2022-05-24 09:13:10 +08:00
yajiwa 570b5b1766 update wbtop 2022-05-24 06:01:23 +08:00
MingxuanGame c4d53d58eb refactor&fix(manager): modify argument 2022-05-24 01:27:55 +08:00
jk6589jk ed65033cf1 修复复读不能复读图片的问题
IMAGE_PATH = Path() / "resources" / "image"
TEMP_PATH = Path() / "resources" / "temp"
image()默认读取在 resource/img 目录下,读写路径不同,导致找不到图片

打断打断施法!,rst.endswith("打断施法!")在复读内容含有图片时会报错
2022-05-23 22:06:53 +08:00
HibiKier 41836bde26 Update README.md 2022-05-23 22:05:19 +08:00
HibiKier 8381858742 Update README.md 2022-05-23 22:02:49 +08:00
HibiKier 1040bd0d58 update v0.1.5.6 2022-05-23 22:01:56 +08:00
HibiKier 404e47332e Merge pull request #441 from with9/main
添加pixiv搜图多关键词支持;修复p站搜图数量参数问题
2022-05-23 21:52:33 +08:00
HibiKier 3db128785e Update __init__.py 2022-05-23 21:52:08 +08:00
HibiKier 2189ea11df Update README.md 2022-05-23 19:46:30 +08:00
HibiKier 49b9bd65b0 Merge pull request #552 from jk6589jk/patch-1
修复有置顶的up主B站动态获取失败
2022-05-23 19:45:59 +08:00
jk6589jk b3fdb1ff58 修复B站动态获取失败
bili-dyn-item__tag的父项的父项才是完整的一条置顶动态
2022-05-23 12:59:53 +08:00
HibiKier c79e3fdcf0 Update README.md 2022-05-23 07:52:27 +08:00
HibiKier d7509f7030 Merge pull request #545 from yajiwa/main
修复"清除已删除插件数据"
2022-05-23 07:51:54 +08:00
yajiwa 5c99b55502 fix none_plugin_count 2022-05-23 02:35:57 +08:00
yajiwa fd255f022b Merge branch 'HibiKier:main' into main 2022-05-23 02:27:00 +08:00
HibiKier 41642589ae Update __version__ 2022-05-22 20:56:03 +08:00
HibiKier 619fef3bff update README.md 2022-05-22 20:52:59 +08:00
HibiKier 38c0da0cf8 update v0.1.5.4 2022-05-22 20:51:35 +08:00
HibiKier b241a58e84 update check_update 2022-05-22 19:04:58 +08:00
HibiKier 35c9585b22 delete WindowsSelectorEventLoopPolicy 2022-05-22 19:03:22 +08:00
HibiKier d2fe95cc05 fix query_resource_points and sign_in 2022-05-22 18:08:06 +08:00
HibiKier 92e60ba141 fix epic 2022-05-22 15:22:57 +08:00
HibiKier 2356527791 update word_clouds 2022-05-22 12:51:53 +08:00
HibiKier cab0f7b522 update change_pixiv_image_links 2022-05-22 12:49:32 +08:00
github-actions[bot] 8d3f101a68 Auto generate poetry.lock 2022-05-21 18:38:05 +00:00
AkashiCoin c8ac2b6281 Update Generate.yml 2022-05-22 02:35:20 +08:00
AkashiCoin d7d8efd305 Merge branch 'HibiKier:main' into main 2022-05-22 02:04:34 +08:00
HibiKier a4bc04c958 Update pid_search.py 2022-05-22 01:46:52 +08:00
AkashiCoin 5fc208c95a Create Generate.yml 2022-05-22 00:29:54 +08:00
HibiKier 02743d5955 Merge pull request #384 from AkashiCoin/main
Update anime.json of Kyomotoi/AnimeThesaurus
2022-05-21 23:40:13 +08:00
HibiKier a7ffebea35 Merge pull request #503 from yajiwa/main
修复命令不唯一的问题
2022-05-21 23:39:39 +08:00
HibiKier c511ef50a2 update wbtop 2022-05-21 23:34:48 +08:00
HibiKier 82902e0cde update invite_manager 2022-05-21 22:40:05 +08:00
AkashiCoin 1de5ae09bc Merge branch 'HibiKier:main' into main 2022-05-21 20:46:04 +08:00
yajiwa 8b735f1cb5 fix word_clouds 2022-05-21 20:08:53 +08:00
HibiKier 33d1334174 fix what_anime 2022-05-21 19:33:39 +08:00
HibiKier 324b58c21b update README.md 2022-05-21 16:42:19 +08:00
HibiKier b7b1651709 fix what_anime 2022-05-21 16:39:06 +08:00
HibiKier b5a4a6a9ff fix mute and open_case 2022-05-21 15:58:58 +08:00
HibiKier fe2c82b03c fix mute and open_case 2022-05-21 15:58:29 +08:00
HibiKier 81e4bca9ef fix mute 2022-05-21 13:46:29 +08:00
HibiKier c0d055b825 add get_message_img_file 2022-05-21 13:20:58 +08:00
HibiKier 188ae38525 fix bug 2022-05-21 13:15:53 +08:00
HibiKier 86a8fe7096 Merge pull request #462 from yajiwa/main
Update poetry file
2022-05-21 11:47:50 +08:00
yajiwa 0bb2b45f5c Update poetry.lock 2022-05-21 00:23:41 +08:00
yajiwa a17d8731c8 Update pyproject.toml 2022-05-21 00:22:44 +08:00
with9 9cdd2326c8 添加pixiv搜图多关键词支持;修复p站搜图数量参数问题 2022-05-20 19:35:18 +08:00
AkashiCoin d75653631d Merge branch 'HibiKier:main' into main 2022-05-19 23:21:02 +08:00
AkashiCoin e234c8906e Update anime.json 2022-05-19 23:20:00 +08:00
HibiKier 57baea18b5 Merge pull request #348 from sihuan/fix-bilibili_sub
Fix wrong live streamer name, fixes #284
2022-05-19 22:11:59 +08:00
HibiKier 0f3efe29c4 Merge pull request #357 from MingxuanGame/main
fix: mihoyo bbs api changed
2022-05-19 22:04:36 +08:00
HibiKier 89b87a38b3 Merge pull request #342 from yajiwa/main
fix bug
2022-05-19 21:03:23 +08:00
MingxuanGame c52b7ca2e9 fix: mihoyo bbs api changed 2022-05-19 17:47:20 +08:00
SiHuan 371b55dc9e Fix wrong live streamer name, fixes #284 2022-05-19 16:03:33 +08:00
yajiwa 53e572353c fix bug 2022-05-19 15:14:20 +08:00
yajiwa 3ae71c74e9 x 2022-05-19 15:10:42 +08:00
HibiKier cb643c04f7 Merge pull request #265 from yajiwa/main
Add word_clouds
2022-05-19 09:27:54 +08:00
yajiwa faf9ee159b update wordclouds url 2022-05-18 23:10:12 +08:00
yajiwa ffb3b2d73e Add word_clouds 2022-05-18 20:31:14 +08:00
HibiKier ffcc327089 Merge pull request #252 from yajiwa/main
Add wordclouds file
2022-05-18 20:04:13 +08:00
yajiwa dfd5a6a86a Add wordclouds file 2022-05-18 18:45:48 +08:00
HibiKier d43f89b067 Update README.md 2022-05-17 19:52:33 +08:00
HibiKier f15c3ab6a2 Update README.md 2022-05-16 19:20:01 +08:00
HibiKier e27809298e Merge pull request #160 from yajiwa/yajiwa
update word_bank
2022-05-16 19:19:20 +08:00
HibiKier 4b3e28e14b fix shop setu 2022-05-15 21:10:35 +08:00
yajiwa 71ce9153ed no message 2022-05-14 15:54:10 +08:00
yajiwa 25d63966de update word_bank 2022-05-14 15:47:33 +08:00
yajiwa 98afbb54b6 add get imgfile 2022-05-14 15:41:44 +08:00
HibiKier 03a51ca5dd Update README.md 2022-05-14 14:48:11 +08:00
HibiKier 428481b2d5 Merge pull request #159 from noahzark/patch-2
修复B站动态生成失败的问题
2022-05-14 14:47:30 +08:00
Feliciano Long f87c60e6db 修复B站动态生成失败的问题
B站动态样式更改
1. card已变更为bili-dyn-list__item
2. 置顶消息变成了一个tag(此处存疑,暂未发现其他tag
2022-05-13 19:38:24 +08:00
HibiKier 6d824e38bc Update README.md 2022-05-11 22:45:20 +08:00
HibiKier f0fd5bfb9e Merge pull request #155 from AkashiCoin/main
fix: 更改p搜api,解决p搜无法使用的问题
2022-05-11 22:44:32 +08:00
HibiKier 33374df671 Update pyproject.toml 2022-05-10 22:25:43 +08:00
AkashiCoin c035b1b19c Update pid_search.py 2022-05-10 18:47:39 +08:00
HibiKier b3b09ed666 update v0.1.5.3 2022-05-09 21:42:24 +08:00
HibiKier 8029c00a02 Update README.md 2022-05-05 20:32:22 +08:00
HibiKier a2c0921013 Merge pull request #153 from NumberSir/main
修改bilibili_sub插件在windows平台下报错
2022-05-05 20:32:17 +08:00
Number_Sir 31cdabc229 修改bilibili_sub插件在windows平台下报错
改用bilireq库,同时补充部分bilireq暂无的方法如获取番剧、获取用户投稿视频
2022-05-04 17:02:56 +08:00
HibiKier 1059a0aae0 Update README.md 2022-05-03 15:40:19 +08:00
HibiKier e6576e7f40 Update README.md 2022-05-03 15:34:47 +08:00
HibiKier 5837144095 Update data_source.py 2022-05-03 15:31:34 +08:00
HibiKier d30a5c4207 Update data_source.py 2022-05-03 10:03:27 +08:00
HibiKier 7d169b0e5e Update README.md 2022-05-03 09:46:06 +08:00
HibiKier 1f54226673 update ShopRegister 2022-05-03 09:43:38 +08:00
HibiKier 901a90ff13 update v0.1.5.1 2022-05-03 02:03:06 +08:00
HibiKier 56573d1d34 update black_word 2022-05-01 15:53:52 +08:00
HibiKier b0ab78340a Update chat_message_handle.py 2022-04-26 15:16:26 +08:00
HibiKier 93539be492 update v0.1.5.0 2022-04-26 14:45:04 +08:00
HibiKier c551e21766 Update __version__ 2022-04-12 00:51:37 +08:00
HibiKier ed342af97d Update README.md 2022-04-12 00:51:25 +08:00
HibiKier 17b87a2cb1 Update README.md 2022-04-12 00:50:55 +08:00
HibiKier 69ca02834d Update __init__.py 2022-04-12 00:50:19 +08:00
HibiKier 6cb4099a2e Update README.md 2022-04-10 22:57:36 +08:00
HibiKier e75a4966b2 Update __init__.py 2022-04-10 22:57:05 +08:00
HibiKier d55cba49bd Update data_source.py 2022-04-10 22:31:07 +08:00
HibiKier 0215abac30 update v0.1.4.7 2022-04-10 22:19:50 +08:00
HibiKier 2dcac5f009 Update README.md 2022-04-09 18:09:22 +08:00
HibiKier 0d8c64f7d1 Merge pull request #144 from AkashiCoin/main
fix: 更新问题,戳一戳图片路径问题
2022-04-09 18:08:49 +08:00
AkashiCoin 55626645fb fix: 更新问题,戳一戳图片路径问题 2022-04-09 14:56:30 +08:00
HibiKier f7925fa259 update genshin.query_role 2022-04-08 01:09:29 +08:00
HibiKier c814ae08b1 Update README.md 2022-04-06 18:52:01 +08:00
HibiKier 24666368f7 Merge pull request #143 from yajiwa/main
update search_type
2022-04-06 18:50:59 +08:00
yajiwa a77e85b62d fix bug 2022-04-06 13:17:08 +08:00
yajiwa 6319c7fce1 fix 2022-04-05 22:38:43 +08:00
yajiwa a45b95d5d4 update search_type 2022-04-05 22:15:47 +08:00
HibiKier 188f6f0f99 Update README.md 2022-04-05 19:42:20 +08:00
HibiKier a72f126d8f fix web_ui api 2022-04-05 13:14:29 +08:00
HibiKier 33eb1b83a8 Update README.md 2022-04-04 23:52:24 +08:00
HibiKier 2d9ebe2e6d Merge pull request #141 from yajiwa/main
Update word_bank plus
2022-04-04 23:50:34 +08:00
yajiwa 262b098dec Merge branch 'HibiKier:main' into main 2022-04-04 22:04:27 +08:00
HibiKier 79d88065aa Update README.md 2022-04-04 20:56:02 +08:00
HibiKier 0f59895c3b update v0.1.4.5 2022-04-04 20:33:37 +08:00
yajiwa 610d588e34 Merge branch 'zhenxun_yajiwa' 2022-04-03 20:17:08 +08:00
yajiwa f99abb2f98 fix config 2022-04-03 20:11:00 +08:00
yajiwa b2b7560d05 Update word_bank_plus 2022-04-03 20:05:18 +08:00
yajiwa 07c16e5238 Merge pull request #1 from yajiwa/zhenxun_yajiwa
Update Modify the entry
2022-04-01 23:56:57 +08:00
yajiwa 1741ea7d26 Update Modify the entry 2022-04-01 23:54:40 +08:00
HibiKier b5ce4fab31 Merge pull request #135 from Sakuracio/main
fix GB18030 to UTF-8.
2022-03-28 00:04:32 +08:00
MangataAkihi 062e4c2062 fix GB18030 to UTF-8. 2022-03-26 23:43:21 +08:00
MangataAkihi 0adb66a8bf fix GB18030 to UTF-8. 2022-03-26 23:43:06 +08:00
HibiKier 2aad517112 fix statistics_handle 2022-03-21 18:43:06 +08:00
HibiKier 14d1512b1e update v0.1.4.4 2022-03-18 20:55:05 +08:00
HibiKier 3f533516af Update README.md 2022-03-07 20:46:48 +08:00
HibiKier f1354308fd Update set_admin_permissions.py 2022-03-07 20:46:13 +08:00
HibiKier c06248e46b Update README.md 2022-03-06 18:00:58 +08:00
HibiKier 9c8eaed938 Update pid_search.py 2022-03-06 18:00:35 +08:00
HibiKier 2081302e32 Update README.md 2022-03-06 17:04:34 +08:00
HibiKier 678fc19f2d Update __init__.py 2022-03-06 17:02:59 +08:00
HibiKier adc2757e8e Merge pull request #128 from Sakuracio/main
去除背景图上的(白点?
2022-03-03 21:18:43 +08:00
Sakuracio 82a1261371 去除背景图上的(白点? 2022-03-03 00:38:54 +08:00
HibiKier 067da9dc57 update 0.1.4.3 2022-03-03 00:18:59 +08:00
HibiKier 0eb9f9fc6f Update README.md 2022-03-01 21:02:36 +08:00
HibiKier aee1340714 Update README.md 2022-03-01 21:02:18 +08:00
HibiKier a72a9bd8cb Update __version__ 2022-03-01 21:01:52 +08:00
HibiKier 7250410653 Update __version__ 2022-03-01 20:59:52 +08:00
HibiKier 12f55800b0 x 2022-03-01 20:26:11 +08:00
HibiKier 53b375b4b2 Add files via upload 2022-02-27 18:26:39 +08:00
HibiKier fba27abe29 Update pyproject.toml 2022-02-27 17:56:56 +08:00
HibiKier 4caa3dec04 Update poetry.lock 2022-02-27 17:56:26 +08:00
HibiKier 516b0f5ccb Add files via upload 2022-02-27 17:34:19 +08:00
HibiKier b5098be08e Update __version__ 2022-02-27 17:32:40 +08:00
HibiKier 5b08a4650c Update README.md 2022-02-27 17:31:51 +08:00
HibiKier eb02157b94 Add files via upload 2022-02-27 17:30:26 +08:00
HibiKier 222e1719a4 Delete plugins/draw_card directory 2022-02-27 17:29:57 +08:00
HibiKier 3c8983e753 Update task_hook.py 2022-02-26 00:11:26 +08:00
HibiKier 1fe41dd823 Update __version__ 2022-02-25 23:21:03 +08:00
HibiKier e8df59203c Update README.md 2022-02-25 23:17:42 +08:00
HibiKier 0f6690606b Update README.md 2022-02-25 22:50:24 +08:00
HibiKier a0115309dd Merge pull request #124 from Nova-Noir/main
Fixed error in custom_welcome_msg and message_builder
2022-02-24 16:12:39 +08:00
HibiKier ca18d45786 Update message_builder.py 2022-02-24 16:12:25 +08:00
NovaNo1r 76df21036a Fixed error in custom_welcome_msg and message_builder 2022-02-23 21:45:53 -08:00
HibiKier c674d581b7 Update README.md 2022-02-23 13:52:10 +08:00
HibiKier fc53c3c487 Update _data_source.py 2022-02-23 13:51:19 +08:00
HibiKier 7a4e31df80 Update __init__.py 2022-02-23 13:49:57 +08:00
HibiKier f1d308d559 Update __init__.py 2022-02-23 13:49:21 +08:00
HibiKier b7914bac05 Update README.md 2022-02-21 19:05:42 +08:00
HibiKier 8c40d88cc4 Update .gitignore 2022-02-21 18:20:43 +08:00
HibiKier b4ed6dcb5d Delete server_ip.py 2022-02-21 18:18:30 +08:00
HibiKier 0d3bd8a89c fix bug 2022-02-21 15:59:17 +08:00
HibiKier 87f0189abd Update README.md 2022-02-20 11:39:00 +08:00
HibiKier 1c17ac1f9d Update __init__.py 2022-02-20 11:06:32 +08:00
HibiKier f038763041 fix bug 2022-02-20 11:06:04 +08:00
HibiKier eaf6d54757 Update README.md 2022-02-19 23:24:21 +08:00
HibiKier d7368c83bc Update query_resource.py 2022-02-19 20:12:51 +08:00
HibiKier 4011620da1 Update README.md 2022-02-19 20:06:53 +08:00
HibiKier 6e0b91531e Update __version__ 2022-02-19 20:05:36 +08:00
HibiKier b8ca1d68aa Update update_info.json 2022-02-19 20:04:19 +08:00
HibiKier 2b9cda40a0 Update query_resource.py 2022-02-19 20:03:40 +08:00
757 changed files with 12335 additions and 6091 deletions
+23
View File
@@ -0,0 +1,23 @@
---
name: Issue template
about: " issue template's purpose here."
title: ''
labels: ''
assignees: ''
---
### 系统版本:Ubuntu 20.04
### 真寻版本:0.1.5.3
### 错误截图
[img]
### 日志截图
[img]
### 错误说明
发生了xx错误...
+38
View File
@@ -0,0 +1,38 @@
name: Generate poetry.lock
on:
push:
branches:
- "main"
paths:
- 'pyproject.toml'
jobs:
poetry_lock:
name: Generate
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v2
- name: Set up Python
uses: he0119/setup-python@main
with:
python-version: "3.9"
- name: poetry lock
run: |
rm -f poetry.lock
sed -i.bak "8,11d" pyproject.toml
poetry lock
mv pyproject.toml.bak pyproject.toml
# commit
- name: commit file
run: |
git config --local user.email "github-actions[bot]@users.noreply.github.com"
git config --local user.name "github-actions[bot]"
git add poetry.lock
git commit -m "Auto generate poetry.lock" -a
# push
- name: push changes
uses: ad-m/github-push-action@master
with:
github_token: ${{ secrets.GITHUB_TOKEN }}
+9
View File
@@ -0,0 +1,9 @@
### 系统版本:Ubuntu 20.04
### 真寻版本:0.1.5.3
### 错误截图
[img]
### 错误说明
发生了xx错误...
+2 -3
View File
@@ -139,10 +139,9 @@ dmypy.json
cython_debug/
test.py
server.py
server_ip.py
game_utils.py
member_activity_handle.py
Yu-Gi-Oh/
search_image/
black_word/
csgo/
fantasy_card/
+329 -13
View File
@@ -18,9 +18,10 @@
## 声明
此项目仅用于学习交流,请勿用于非法用途
## Gitee
# Nonebot2
<img style="height: 200px;width: 200px;" src="https://camo.githubusercontent.com/0ef71e86056da694c540790aa4a4e314396884d6c4fdb95362a7538b27a1b034/68747470733a2f2f76322e6e6f6e65626f742e6465762f6c6f676f2e706e67">
# [Gitee](https://gitee.com/two_Dimension/zhenxun_bot)
非常 [ **[NICE](https://github.com/nonebot/nonebot2)** ] 的OneBot框架
## 未完成的文档
@@ -32,6 +33,12 @@
## 普通帮助图片
![x](https://github.com/HibiKier/zhenxun_bot/blob/0.0.8.2/docs_image/3238573864-836268675-E2FFBB2AC143EAF4DDDF150438508721.png)
## Web UI
[zhenxun_bot_webui](https://github.com/HibiKier/zhenxun_bot_webui)
## 一键安装脚本
[zhenxun_bot-deploy](https://github.com/AkashiCoin/zhenxun_bot-deploy)
## 提供符合真寻标准的插件仓库
[AkashiCoin/nonebot_plugins_zhenxun_bot](https://github.com/AkashiCoin/nonebot_plugins_zhenxun_bot)
@@ -107,7 +114,7 @@
- [x] 获取b站视频封面
- [x] 通过PID获取图片
- [x] 功能统计可视化
- [x] 词云
- [x] 关于
### 已实现的管理员功能
@@ -191,7 +198,7 @@
# 配置gocq
在 https://github.com/Mrs4s/go-cqhttp 下载Releases最新版本,运行后选择反向代理,
后将gocq的配置文件config.yml中的universal改为universal: ws://127.0.0.1:8080/cqhttp/ws
后将gocq的配置文件config.yml中的universal改为universal: ws://127.0.0.1:8080/onebot/v11/ws
# 获取代码
git clone https://github.com/HibiKier/zhenxun_bot.git
@@ -229,33 +236,342 @@ python bot.py
## 使用Docker
__Docker 最新版本由 [Sakuracio](https://github.com/Sakuracio) 提供__
#### GitHub:[Sakuracio/zxenv](https://github.com/Sakuracio/zxenv)
#### GitHub:[Sakuracio/zhenxun_bot_docker](https://github.com/Sakuracio/zhenxun_bot_docker)
#### DockerHub:[hibikier/zhenxun_bot](https://hub.docker.com/r/hibikier/zhenxun_bot)
## 更新
### 2022/2/19 \[v0.1.2] (nonebot beta2!)
### 2022/6/19 \[v0.1.6.0]
* 暂时使用hook修复webui中plugins2setting修改时会改变plugins2setting.cmd为字符串
* 修复原神树脂重复提醒的bug [@pull/828](https://github.com/HibiKier/zhenxun_bot/pull/828)
* 词条优化问题展示,修改含机器人昵称和@逻辑 [@pull/835](https://github.com/HibiKier/zhenxun_bot/pull/835)
### 2022/6/18
* 修复昵称系统`BLACK_WORD`为空时造成报错
* 修复特殊头像时背景透明化出错
* 修复text2image纯文本时换行时颜色不统一
* 优化webUI,当有插件出错时不会影响其他插件显示
* 优化Config类型注释 [@pull/830](https://github.com/HibiKier/zhenxun_bot/pull/830)
* 优化browser
### 2022/6/17
* 修复wordbank的一些小问题 @pull/820
### 2022/6/14
* AsyncHttpx提供可关闭 ssl 验证参数 [@pull/812](https://github.com/HibiKier/zhenxun_bot/pull/812)
* fix: 自动更新群组 [@pull/810](https://github.com/HibiKier/zhenxun_bot/pull/810)
* fix: 修复添加词条时对answer中图片的处理 [@pull/806](https://github.com/HibiKier/zhenxun_bot/pull/806)
* 修复pix中指定num无法生效
* 替换了bt URL
### 2022/6/13
* Windows 重启适配 [@pull/804](https://github.com/HibiKier/zhenxun_bot/pull/804)
* 色图等提供修改md5方法 [@pull/800](https://github.com/HibiKier/zhenxun_bot/pull/800)
* webui中token.json文件写入补充 [@pull/798](https://github.com/HibiKier/zhenxun_bot/pull/798)
* 修复识图某些图片可能会出错
### 2022/6/11
* pix当只有一个tag时且为数字时可以通过添加'#'防止判定为数量
### 2022/6/9
* 修复b站订阅同群二人以上相同订阅时发送多次信息
* 修复超级用户帮助中缺少了 ‘插件商店’ 相关帮助
* 昵称系统提供了详细帮助
### 2022/6/5 \[v0.1.5.9]
* webui修复plugin2setting中cmd从list转变为str
* 当命令`我的金币`被风控时将以图片形式发送
* fix gold_redbag [@pull/763](https://github.com/HibiKier/zhenxun_bot/pull/763)
* 金币红包功能增加更多封面 [@pull/764](https://github.com/HibiKier/zhenxun_bot/pull/764)
### 2022/6/3
* 插件仓库在已安装插件边上会提示\[已安装]
* 修复ShopRegister kwargs某些字段无效
* 调整了一下查看所有请求中的年龄绘制 [@pull/745](https://github.com/HibiKier/zhenxun_bot/pull/745)
* 修复原神树脂提醒bug [@pull/756](https://github.com/HibiKier/zhenxun_bot/pull/756)
### 2022/5/31
* 修复开启/关闭全部功能时帮助图片未重绘 [@pull/721](https://github.com/HibiKier/zhenxun_bot/pull/721)
* bot_friend_group将group_handle.修改为friend_handle. [@pull/711](https://github.com/HibiKier/zhenxun_bot/pull/711)
* 修复发红包图片背景未透明化;修复原神树脂提醒参数错误 [@pull/712](https://github.com/HibiKier/zhenxun_bot/pull/712)
* 修复抽卡插件:方舟更新UP池信息时,若公告的第一个池子过期会导致无法更新UP池信息 [@pull/707](https://github.com/HibiKier/zhenxun_bot/pull/707)
* 商店插件判断是否有requirements.txt文件 [@pull/705](https://github.com/HibiKier/zhenxun_bot/pull/705)
* 删除原神玩家查询api返回变更的多余键值
* 优化了text2image方法
### 2022/5/29 \[v0.1.5.8]
* 提供了真寻适配仓库的插件 安装/卸载 操作
* 暂时关闭了插件资源清空
* 通过指令安装插件时会在插件目录下生成plugin_info.json记录当前插件信息
### 2022/5/28
* 修复私聊无法添加昵称
* 修复原神玩家查询层岩巨渊地下矿区没开时报错
* 修复 ```休息吧``` 无法阻断戳一戳
* 当图库无图片时,戳一戳将略过发送图片
* 新增搜图提供配置项```ALLOW_GROUP_R18```:允许在群聊中使用r18参数
* 新增自动更新插件```UPDATE_REMIND```:新版本提醒,原配置项```AUTO_UPDATE_ZHENXUN```改为自动更新升级
* black_word新增当群权限为-1时不再检测该群
* 修复非超级用户绑定原神cookie会被black_word阻拦
* 修复微博热搜报错,发红包小bug [@pull/688](https://github.com/HibiKier/zhenxun_bot/pull/688)
* 更多的中文提示
### 2022/5/26
* 修复\[滴滴滴]会被转义成&#91;滴滴滴&#93;导致无法触发的问题
* 将一些错误以中文提示输出
* 更新BT搜索源地址 [@pull/668](https://github.com/HibiKier/zhenxun_bot/pull/668)
* 更新抽卡插件 [@pull/667](https://github.com/HibiKier/zhenxun_bot/pull/667)
### 2022/5/25
* 修复webui中CountLimit字段limit_type类型错误
* 修改nickname插件:一处文案错误,添加敏感词 [@pull/624](https://github.com/HibiKier/zhenxun_bot/pull/624)
* gamedraw的ba卡池搬运了过来并且进行了真寻的适配 [@pull/617](https://github.com/HibiKier/zhenxun_bot/pull/617)
* feat: stream downloading and progress [@pull/607](https://github.com/HibiKier/zhenxun_bot/pull/607)
* 修改翻译插件,添加缺失的冒号 [@pull/602](https://github.com/HibiKier/zhenxun_bot/pull/602)
* 修复碧蓝航线/明日方舟up池解析出错的问题 [@pull/610](https://github.com/HibiKier/zhenxun_bot/pull/602)
### 2022/5/24
* fix: 修正了签到时日期时间的显示不补齐零的bug (符合日期时间表示法 ISO 8601)[@pull/600](https://github.com/HibiKier/zhenxun_bot/pull/600)
* 更新"微博热搜"接口 [@pull/579](https://github.com/HibiKier/zhenxun_bot/pull/579)
* refactor&fix(manager): modify argument [@pull/576](https://github.com/HibiKier/zhenxun_bot/pull/576)
* 修复复读不能复读图片的问题 [@pull/573](https://github.com/HibiKier/zhenxun_bot/pull/573)
* 修复抽卡插件:方舟抽卡的抽取和显示问题 [@pull/581](https://github.com/HibiKier/zhenxun_bot/pull/581)
### 2022/5/23 \[v0.1.5.6]
* 修复"清除已删除插件数据" [@pull/545](https://github.com/HibiKier/zhenxun_bot/pull/545)
* 修复有置顶的up主B站动态获取失败 [@pull/552](https://github.com/HibiKier/zhenxun_bot/pull/552)
* 添加pixiv搜图多关键词支持;修复p站搜图数量参数问题 [@pull/441](https://github.com/HibiKier/zhenxun_bot/pull/441)
* 修复开箱更新价格错误传参
* 修复pix无法正确查询uid
* 新增色图插件添加配置项```ALLOW_GROUP_R18```:允许群聊中使用色图r
* 新增PIX插件添加配置项```ALLOW_GROUP_SETU```:允许非超级用户使用-s参数
* 新增PIX插件添加配置项```ALLOW_GROUP_R18```:允许非超级用户使用-r参数
### 2022/5/22 \[v0.1.5.4]
* 使用action自动更新poetry.lock [@pull/515](https://github.com/HibiKier/zhenxun_bot/pull/515)
* fix(bilibili_sub): card is None and timeout [@pull/516](https://github.com/HibiKier/zhenxun_bot/pull/516)
* 修复了epic有时获取新免费游戏消息时获取不到图片
* 修复好感度满时签到出错(虽然是不可能满的
* 修复原神资源图标下载路径错误
* 修复自动更新群组可能失败
### 2022/5/21
* 修复搜番无结果时报错无正确反馈
* 解锁了windows上无法使用playwright的限制
* 修复p搜对应pid有多张图时出错,改为连续发送图片
* 修复p搜对数字的错误判断
* 修复添加商品折扣无法正确添加
* 修复了bilibili订阅直播间订阅up名称不一致的问题
* 修复原神玩家查询没开地图时报错
* 最低priority修改为 999
* 修复刷屏检测失效
* 修复刷屏检测设置命令无法生效
* 优化刷屏显示设置禁言时长显示,并改为分钟
* 修复了多连开箱无法指定武器箱
* 修复识番链接无法正确获取
* 新增真寻入群时即刻刷新权限
* 提高了微博热搜截图的等待时间
### 2022/5/19
* fix: mihoyo bbs api changed [@pull/357](https://github.com/HibiKier/zhenxun_bot/pull/357)
* Add word_clouds [@pull/265](https://github.com/HibiKier/zhenxun_bot/pull/265)
* Fix wrong live streamer name [@pull/284](https://github.com/HibiKier/zhenxun_bot/pull/284)
### 2022/5/16
* 词条支持图片和@问题 [@pull/160](https://github.com/HibiKier/zhenxun_bot/pull/160)
### 2022/5/15
* 修复了商店商品无法正确添加
* 修复了多张色图无法正确发送
### 2022/5/14
* 修复B站动态生成失败的问题 [@pull/159](https://github.com/HibiKier/zhenxun_bot/pull/159)
### 2022/5/11
* fix: 更改p搜api,解决p搜无法使用的问题 [@pull/155](https://github.com/HibiKier/zhenxun_bot/pull/155)
### 2022/5/9 \[v0.1.5.3]
* 替换了疫情API
* 修复了私聊.ban/.unban出错
### 2022/5/5
* 修改bilibili_sub插件在windows平台下报错 [@pull/153](https://github.com/HibiKier/zhenxun_bot/pull/153)
### 2022/5/3 \[v0.1.5.2]
* 商品使用函数可以添加特定参数,例如:user_id, group_id, ShopParam等以及自己提供的参数
* 添加商品注册装饰器shop_register
* 修复商品函数kwargs无法获取参数值
### 2022/5/1
* 删除了`group_last_chat`插件(该功能可由`chat_history`替代
* 新增敏感词检测(全新反击系统,是时候重拳出击了
### 2022/4/26
* 修复了群白名单无法正确添加
* 优化了管理员帮助图片,背景图层将位于最下层
* 修复了树脂140时不断提醒(未测试
* 新增了消息记录的消息排行
* WebUI新增CPU,内存,磁盘监控
* WebUI新增资源文件夹统计可视化
### 2022/4/12
* 修复b了命令私聊出错
### 2022/4/10 \[v0.1.4.7]
* 新增消息记录模块
* 丰富处理请求操作提示
* web ui新增配置项修改
### 2022/4/9
* fix: 更新问题,戳一戳图片路径问题 [@pull/144](https://github.com/HibiKier/zhenxun_bot/pull/144)
### 2022/4/8
* 修复原神玩家查询
### 2022/4/6
* update search_type [@pull/143](https://github.com/HibiKier/zhenxun_bot/pull/143)
### 2022/4/5 \[v0.1.4.6]
* 修复web修改插件后帮助图片生成错误
### 2022/4/4 \[v0.1.4.5]
* 替换了bt搜索URL
* 优化使用playwright的相关代码
* 原神玩家查询新增层岩巨渊探索
* 修复原神便笺角色头像黑框
* 修复同意群聊请求错误
* 提供webui方面的api
* 新增web-ui(前端简易管理页面插件)插件
### 2022/3/21
* 修复statistics_handle.py乱码
### 2022/3/18 \[v0.1.4.4]
* 修复戳一戳无法功能关闭与ban禁用
* 新增图片搜索 search_image
### 2022/3/7
* 优化增删权限插件
### 2022/3/6
* 修复树脂提醒无法开启
* 修复p搜图片路径错误
### 2022/3/3 \[v0.1.4.3]
* 修复手动同意群聊请求依旧退出
### 2022/3/1 \[v0.1.4.2]
* 0.1.4内容
### 2022/2/27 \[v0.1.4.1]
* 优化抽卡
### 2022/2/25 \[v0.1.4]
* PIX提供配置MAX_ONCE_NUM2FORWARD:当单次发送图片超过指定张数且在群聊时,将转为合并消息
* 修复点歌无法正确发送
* 修复我有一个朋友有时文本会包含CQ码
* 修复群欢消息被动控制文本未删除 [@pull/124](https://github.com/HibiKier/zhenxun_bot/pull/124)
* message_builder.image不再提供参数:abspath
### 2022/2/23
* 插件状态将从已加载插件模块中读取
* 修复b站订阅插件订阅失败问题
* 修复重启命令无法使用
### 2022/2/21 \[v0.1.3.2]
* 群权限为-1时,超级用户发送的命令依旧生效
* 当群权限为-1时,被动技能也将不会发送
* 修复功能开关,b站转发解析,复读 ignore无法使用
* 修复色图下载文件名与路径错误
* 修复被动技能提醒有时无法删除控制文本
### 2022/2/20 \[v0.1.3.1]
* 修复pix下载临时文件目录错误
* 修复AI,天气,发送图片ignore导致无法使用
* 修复纯文本被动技能提醒有时无法删除控制文本
### 2022/2/19 \[v0.1.3] (nonebot beta2!)
* 由于nonebot升级版本,提供更新建议(__该次升级将会导致nonebot.beta1以下的插件无法使用__
* 保证services,utils,configs,plugins,basic_plugins,文件夹均为最新
* 根目录有pyproject.toml与poetry.lock
* 执行命令:
* pip3 install poetry
* poetry install
* poetry shell
* playwright install chromium
* python3 bot.py
* 适配nonebot.beta2
* 删除图片搜索 nonebot_plugin_picsearcher
* 新增图片搜索 search_image
* 替换cos api
* 原神签到树脂提醒新增绑定群里,在某群绑定uid就会在某群发送提醒信息(有好友则私聊,需要重新绑定uid
* 修改update_info.json
* 修复原神资源查询下载数据失败时导致报错
* 优化BuildImage.circle()锯齿问题 [@pull/109](https://github.com/HibiKier/zhenxun_bot/pull/109)
* epic restful 替换 [@pull/119](https://github.com/HibiKier/zhenxun_bot/pull/119)
* fix: 修复远古时期残留的epic推送问题 [@pull/122](https://github.com/HibiKier/zhenxun_bot/pull/122)
### 2021/2/11
### 2022/2/11
* 修复pix不使用反代无法下载图片
### 2021/2/10 \[v0.1.1]
### 2022/2/10 \[v0.1.1]
* 修复购买道具出错
### 2021/2/9 \[v0.1]
### 2022/2/9 \[v0.1]
* 新增原神自动签到和手动签到
* 新增原神树脂提醒
@@ -265,7 +581,7 @@ __Docker 最新版本由 [Sakuracio](https://github.com/Sakuracio) 提供__
* 修复修改商品时限制时间出错
* 修复超时商品依旧可以被购买
### 2021/1/16 \[v0.0.9.0]
### 2022/1/16 \[v0.0.9.0]
* Ai提供文本敏感词过滤器
* 疫情插件适配新版腾讯API
@@ -284,7 +600,7 @@ __Docker 最新版本由 [Sakuracio](https://github.com/Sakuracio) 提供__
* “send_success_msg”(发送成功的交互信息->即:使用道具 {name} {num} 次成功)
* “_max_num_limit”(该道具单次使用的最多个数,默认1)
### 2021/1/5 \[v0.0.8.2]
### 2022/1/5 \[v0.0.8.2]
* 提供金币消费hook,可在plugins2settings.yaml中配置该功能需要消费的金币
* 商店插件将作为内置插件移动至basic_plugins
@@ -355,4 +671,4 @@ __..... 更多更新信息请查看文档__
[NothAmor / nonebot2_luxun_says](https://github.com/NothAmor/nonebot2_luxun_says) :基于nonebot2机器人框架的鲁迅说插件
[Kyomotoi / AnimeThesaurus](https://github.com/Kyomotoi/AnimeThesaurus) :一个~~特二刺螈~~(文爱)的适用于任何bot的词库
[Ailitonia / omega-miya](https://github.com/Ailitonia/omega-miya) :基于nonebot2的qq机器人
[KimigaiiWuyi / GenshinUID]("https://github.com/KimigaiiWuyi/GenshinUID") :一个基于HoshinoBot/NoneBot2的原神UID查询插件
[KimigaiiWuyi / GenshinUID](https://github.com/KimigaiiWuyi/GenshinUID) :一个基于HoshinoBot/NoneBot2的原神UID查询插件
+1 -1
View File
@@ -1 +1 @@
__version__: v0.1.2
__version__: v0.1.6.0
+28 -24
View File
@@ -3,7 +3,7 @@ from nonebot.adapters.onebot.v11.message import MessageSegment
from services.log import logger
from configs.path_config import DATA_PATH
from utils.message_builder import image
from utils.utils import get_bot
from utils.utils import get_bot, get_matchers
from pathlib import Path
from models.group_member_info import GroupInfoUser
from datetime import datetime
@@ -75,7 +75,7 @@ async def custom_group_welcome(
await AsyncHttpx.download_file(
img, DATA_PATH / "custom_welcome_msg" / f"{group_id}.jpg"
)
img_result = image(abspath=DATA_PATH / "custom_welcome_msg" / f"{group_id}.jpg")
img_result = image(DATA_PATH / "custom_welcome_msg" / f"{group_id}.jpg")
logger.info(f"USER {user_id} GROUP {group_id} 更换群欢迎消息图片")
except Exception as e:
logger.error(f"GROUP {group_id} 替换群消息失败 e:{e}")
@@ -118,6 +118,8 @@ async def change_group_switch(cmd: str, group_id: int, is_super: bool = False):
group_manager.unblock_plugin(f, group_id)
else:
group_manager.block_plugin(f, group_id)
if group_help_file.exists():
group_help_file.unlink()
return f"已 {status} 全部功能!"
if cmd in [task_data[x] for x in task_data.keys()]:
type_ = "task"
@@ -187,24 +189,28 @@ def _get_plugin_status() -> MessageSegment:
"""
rst = "\t功能\n"
flag_str = "状态".rjust(4) + "\n"
for module in plugins_manager.get_data():
flag = plugins_manager.get_plugin_block_type(module)
flag = flag.upper() + " CLOSE" if flag else "OPEN"
try:
plugin_name = plugins_manager.get(module)["plugin_name"]
if (
"[Hidden]" in plugin_name
or "[Admin]" in plugin_name
or "[Superuser]" in plugin_name
):
continue
rst += f"{plugin_name}"
except KeyError:
rst += f"{module}"
if plugins_manager.get(module)["error"]:
rst += "[ERROR]"
rst += "\n"
flag_str += f"{flag}\n"
tmp_name = []
for matcher in get_matchers():
if matcher.plugin_name not in tmp_name:
tmp_name.append(matcher.plugin_name)
module = matcher.plugin_name
flag = plugins_manager.get_plugin_block_type(module)
flag = flag.upper() + " CLOSE" if flag else "OPEN"
try:
plugin_name = plugins_manager.get(module)["plugin_name"]
if (
"[Hidden]" in plugin_name
or "[Admin]" in plugin_name
or "[Superuser]" in plugin_name
):
continue
rst += f"{plugin_name}"
except KeyError:
rst += f"{module}"
if plugins_manager.get(module)["error"]:
rst += "[ERROR]"
rst += "\n"
flag_str += f"{flag}\n"
height = len(rst.split("\n")) * 24
a = BuildImage(250, height, font_size=20)
a.text((10, 10), rst)
@@ -228,10 +234,7 @@ async def update_member_info(group_id: int, remind_superuser: bool = False) -> b
_exist_member_list = []
# try:
for user_info in _group_user_list:
if user_info["card"] == "":
nickname = user_info["nickname"]
else:
nickname = user_info["card"]
nickname = user_info["card"] or user_info["nickname"]
async with db.transaction():
# 更新权限
if (
@@ -317,3 +320,4 @@ def set_group_bot_status(group_id: int, status: bool) -> str:
# for x in group_manager.get_task_data():
# group_manager.close_group_task(group_id, x)
return "那我先睡觉了..."
+12 -6
View File
@@ -24,14 +24,20 @@ def switch_rule(event: Event) -> bool:
_data = plugins2settings_manager.get_data()
for key in _data:
try:
for x in _data[key]["cmd"]:
cmd.append(f"开启{x}")
cmd.append(f"关闭{x}")
cmd.append(f"开启 {x}")
cmd.append(f"关闭 {x}")
if isinstance(_data[key]["cmd"], list):
for x in _data[key]["cmd"]:
cmd.append(f"开启{x}")
cmd.append(f"关闭{x}")
cmd.append(f"开启 {x}")
cmd.append(f"关闭 {x}")
else:
cmd.append(f"开启{key}")
cmd.append(f"关闭{key}")
cmd.append(f"开启 {key}")
cmd.append(f"关闭 {key}")
except KeyError:
pass
msg = get_message_text(event.json())
msg = get_message_text(event.json()).split()
msg = msg[0] if msg else ""
return msg in cmd
except Exception as e:
@@ -1,7 +1,6 @@
from nonebot.adapters.onebot.v11 import Bot, GroupMessageEvent, MessageEvent, GROUP, Message
from nonebot.adapters.onebot.v11 import Bot, GroupMessageEvent, MessageEvent, GROUP
from nonebot import on_command, on_message, on_regex
from nonebot.typing import T_State
from nonebot.params import CommandArg
from nonebot.params import RegexGroup
from ._data_source import (
change_group_switch,
set_plugin_status,
@@ -13,6 +12,7 @@ from services.log import logger
from configs.config import NICKNAME, Config
from utils.utils import get_message_text, is_number
from nonebot.permission import SUPERUSER
from typing import Tuple, Any
from .rule import switch_rule
@@ -65,14 +65,14 @@ group_status = on_regex("^(休息吧|醒来)$", permission=GROUP, priority=5, bl
@switch_rule_matcher.handle()
async def _(bot: Bot, event: MessageEvent, arg: Message = CommandArg()):
_cmd = arg.extract_plain_text().strip().split()[0]
async def _(bot: Bot, event: MessageEvent):
_cmd = get_message_text(event.json()).split()[0]
if isinstance(event, GroupMessageEvent):
await switch_rule_matcher.send(await change_group_switch(_cmd, event.group_id))
logger.info(f"USER {event.user_id} GROUP {event.group_id} 使用群功能管理命令 {_cmd}")
else:
if str(event.user_id) in bot.config.superusers:
block_type = " ".join(arg.extract_plain_text().strip().split()[1:])
block_type = " ".join(get_message_text(event.json()).split()[1:])
block_type = block_type if block_type else "a"
if is_number(block_type):
if not int(block_type) in [
@@ -114,8 +114,8 @@ async def _(event: GroupMessageEvent):
@group_status.handle()
async def _(event: GroupMessageEvent, state: T_State):
cmd = state["_matched_groups"][0]
async def _(event: GroupMessageEvent, reg_group: Tuple[Any, ...] = RegexGroup()):
cmd = reg_group[0]
if cmd == "休息吧":
msg = set_group_bot_status(event.group_id, False)
else:
@@ -2,6 +2,7 @@ from nonebot import on_command, on_notice
from nonebot.adapters.onebot.v11 import Bot, GroupMessageEvent, GROUP, GroupIncreaseNoticeEvent
from ._data_source import update_member_info
__zx_plugin_name__ = "更新群组成员列表 [Admin]"
__plugin_usage__ = """
usage:
+5 -8
View File
@@ -5,7 +5,6 @@ from utils.utils import get_matchers
from utils.manager import group_manager
from nonebot.adapters.onebot.v11 import Bot
from nonebot import Driver
import asyncio
import nonebot
@@ -27,12 +26,10 @@ async def create_help_image():
"""
创建管理员帮助图片
"""
await asyncio.get_event_loop().run_in_executor(
None, _create_help_image
)
await _create_help_image()
def _create_help_image():
async def _create_help_image():
"""
创建管理员帮助图片
"""
@@ -85,9 +82,9 @@ def _create_help_image():
height = len(help_str.split("\n")) * 33
A = BuildImage(width, height, font_size=24)
_background = BuildImage(width, height, background=background)
A.text((150, 110), help_str)
A.paste(_background, alpha=True)
A.save(admin_help_image)
await A.apaste(_background, alpha=True)
await A.atext((150, 110), help_str)
await A.asave(admin_help_image)
logger.info(f'已成功加载 {len(_plugin_name_list)} 条管理员命令')
+2 -1
View File
@@ -4,7 +4,7 @@ from nonebot import on_message
from services.log import logger
from models.group_info import GroupInfo
from models.friend_user import FriendUser
from nonebot.adapters.onebot.v11.exception import ActionFailed
from nonebot.adapters.onebot.v11 import ActionFailed
from configs.config import NICKNAME, Config
from utils.manager import group_manager
from pathlib import Path
@@ -107,6 +107,7 @@ async def _():
group_info["group_name"],
group_info["max_member_count"],
group_info["member_count"],
1
)
logger.info(f"自动更新群组 {g} 信息成功")
except Exception as e:
+46 -30
View File
@@ -1,15 +1,17 @@
from nonebot.adapters.onebot.v11 import GroupMessageEvent, PrivateMessageEvent, MessageEvent, Message, Bot
from nonebot.params import CommandArg, Command
from nonebot import on_command
from typing import Tuple
from configs.config import NICKNAME, Config
from models.ban_user import BanUser
from models.level_user import LevelUser
from typing import Tuple
from utils.utils import get_message_at, is_number
from configs.config import NICKNAME, Config
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 .data_source import parse_ban_time, a_ban
from services.log import logger
from utils.utils import get_message_at, is_number
from .data_source import a_ban, parse_ban_time
__zx_plugin_name__ = "封禁Ban用户 [Admin]"
__plugin_usage__ = """
@@ -34,19 +36,19 @@ usage:
示例:b了 1234567
示例:.ban 12345567
""".strip()
__plugin_des__ = '你被逮捕了!丢进小黑屋!'
__plugin_cmd__ = ['.ban [at] ?[小时] ?[分钟]', '.unban [at]', 'b了 [at] [_superuser]']
__plugin_des__ = "你被逮捕了!丢进小黑屋!"
__plugin_cmd__ = [".ban [at] ?[小时] ?[分钟]", ".unban [at]", "b了 [at] [_superuser]"]
__plugin_version__ = 0.1
__plugin_author__ = 'HibiKier'
__plugin_author__ = "HibiKier"
__plugin_settings__ = {
"admin_level": Config.get_config("ban", "BAN_LEVEL"),
"cmd": ['.ban', '.unban', 'ban', 'unban']
"cmd": [".ban", ".unban", "ban", "unban"],
}
__plugin_configs__ = {
"BAN_LEVEL [LEVEL]": {
"value": 5,
"help": "ban/unban所需要的管理员权限等级",
"default_value": 5
"default_value": 5,
}
}
@@ -58,18 +60,23 @@ ban = on_command(
block=True,
)
super_ban = on_command('b了', permission=SUPERUSER, priority=5, block=True)
super_ban = on_command("b了", permission=SUPERUSER, priority=5, block=True)
@ban.handle()
async def _(bot: Bot, event: GroupMessageEvent, cmd: Tuple[str, ...] = Command(), arg: Message = CommandArg()):
async def _(
bot: Bot,
event: GroupMessageEvent,
cmd: Tuple[str, ...] = Command(),
arg: Message = CommandArg(),
):
cmd = cmd[0]
result = ""
qq = get_message_at(event.json())
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']
user_name = user_name["card"] or user_name["nickname"]
msg = arg.extract_plain_text().strip()
time = parse_ban_time(msg)
if isinstance(time, str):
@@ -108,49 +115,58 @@ async def _(bot: Bot, event: GroupMessageEvent, cmd: Tuple[str, ...] = Command()
@ban.handle()
async def _(bot: Bot, event: PrivateMessageEvent, cmd: Tuple[str, ...] = Command(), arg: Message = CommandArg()):
async def _(
bot: Bot,
event: PrivateMessageEvent,
cmd: Tuple[str, ...] = Command(),
arg: Message = CommandArg(),
):
cmd = cmd[0]
msg = arg.extract_plain_text().strip()
if msg:
if str(event.user_id) in bot.config.superusers:
if is_number(arg.extract_plain_text().strip().split()[0]):
qq = int(msg[0])
msg = msg[1:]
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(msg)
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} 解禁"
)
logger.info(f"USER {event.user_id} 将 USER {qq} 解禁")
result = f"已经把 {qq} 从黑名单中删除了!"
else:
result = f"{qq} 不在黑名单!"
await ban.send(result)
else:
await ban.finish('qq号必须是数字!\n格式:.ban [qq] [hour]? [minute]?', at_sender=True)
await ban.finish(
"qq号必须是数字!\n格式:.ban [qq] [hour]? [minute]?", at_sender=True
)
@super_ban.handle()
async def _(bot: Bot, event: MessageEvent, arg: Message = CommandArg()):
user_name = ""
if isinstance(event, GroupMessageEvent):
qq = get_message_at(event.json())
if qq:
qq = qq[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):
await super_ban.finish("对象qq必须为纯数字...")
qq = [qq]
qq = int(qq)
user_name = qq
if qq:
qq = qq[0]
user = await bot.get_group_member_info(group_id=event.group_id, user_id=qq)
user_name = user['card'] or user['nickname']
if not await BanUser.ban(qq, 10, 99999999):
await BanUser.unban(qq)
await BanUser.ban(qq, 10, 99999999)
await ban.send(f"已将 {user_name} 拉入黑名单!")
else:
await super_ban.send('需要添加被super ban的对象,可以使用at或者指定qq..')
await super_ban.send("需要添加被super ban的对象,可以使用at或者指定qq..")
+5 -12
View File
@@ -25,7 +25,9 @@ def parse_ban_time(msg: str) -> Union[int, str]:
return int(msg[0]) * 60 * 60 + int(msg[1]) * 60
async def a_ban(qq: int, time: int, user_name: str, event: MessageEvent, ban_level: int = None) -> str:
async def a_ban(
qq: int, time: int, user_name: str, event: MessageEvent, ban_level: int = None
) -> str:
"""
ban
:param qq: qq
@@ -38,7 +40,8 @@ async def a_ban(qq: int, time: int, user_name: str, event: MessageEvent, ban_lev
ban_level = await LevelUser.get_user_level(event.user_id, event.group_id)
if await BanUser.ban(qq, ban_level, time):
logger.info(
f"USER {event.user_id} GROUP {event.group_id} 将 USER {qq} 封禁 时长 {time / 60} 分钟"
f"USER {event.user_id} GROUP"
f" {event.group_id if isinstance(event, GroupMessageEvent) else ''} 将 USER {qq} 封禁 时长 {time / 60} 分钟"
)
result = f"已经将 {user_name} 加入{NICKNAME}的黑名单了!"
if time != -1:
@@ -57,13 +60,3 @@ async def a_ban(qq: int, time: int, user_name: str, event: MessageEvent, ban_lev
time += " 分钟"
result = f"{user_name} 已在黑名单!预计 {time}后解封"
return result
+3
View File
@@ -0,0 +1,3 @@
import nonebot
nonebot.load_plugins("basic_plugins/chat_history")
+6
View File
@@ -0,0 +1,6 @@
from nonebot.adapters.onebot.v11 import Event, MessageEvent
from configs.config import Config
def rule(event: Event) -> bool:
return Config.get_config("chat_history", "FLAG") and isinstance(event, MessageEvent)
@@ -0,0 +1,39 @@
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 utils.utils import get_message_text
from ._rule import rule
__zx_plugin_name__ = "消息存储 [Hidden]"
__plugin_version__ = 0.1
__plugin_author__ = "HibiKier"
Config.add_plugin_config(
"chat_history", "FLAG", True, help_="是否开启消息自从存储", name="消息存储", default_value=True
)
chat_history = on_message(rule=rule, priority=1, block=False)
@chat_history.handle()
async def _(event: MessageEvent):
if isinstance(event, GroupMessageEvent):
await ChatHistory.add_chat_msg(
event.user_id, event.group_id, str(event.get_message()), get_message_text(event.json())
)
else:
await ChatHistory.add_chat_msg(event.user_id, None, str(event.get_message()), get_message_text(event.json()))
# @test.handle()
# async def _(event: MessageEvent):
# print(await ChatHistory.get_user_msg(event.user_id, "private"))
# print(await ChatHistory.get_user_msg_count(event.user_id, "private"))
# print(await ChatHistory.get_user_msg(event.user_id, "group"))
# print(await ChatHistory.get_user_msg_count(event.user_id, "group"))
# print(await ChatHistory.get_group_msg(event.group_id))
# print(await ChatHistory.get_group_msg_count(event.group_id))
@@ -0,0 +1,108 @@
from datetime import datetime, timedelta
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
__zx_plugin_name__ = "消息统计"
__plugin_usage__ = """
usage:
发言记录统计
regex:(周|月)?消息排行(des|DES)?(n=[0-9]{1,2})?
指令:
消息统计?(des)?(n=?)
周消息统计?(des)?(n=?)
月消息统计?(des)?(n=?)
示例:
消息统计
消息统计des
消息统计DESn=15
消息统计n=15
""".strip()
__plugin_des__ = "发言消息排行"
__plugin_cmd__ = [
"消息统计",
"周消息统计",
"月消息统计"
]
__plugin_type__ = ("数据统计", 1)
__plugin_version__ = 0.1
__plugin_author__ = "HibiKier"
__plugin_settings__ = {
"level": 5,
"cmd": ["消息统计"],
}
msg_handler = on_regex(r"^(周|月)?消息统计(des|DES)?(n=[0-9]{1,2})?$", priority=5, block=True)
@msg_handler.handle()
async def _(event: GroupMessageEvent, reg_group: Tuple[Any, ...] = RegexGroup()):
gid = event.group_id
date_scope = None
date, order, num = reg_group
num = num.split("=")[-1] if num else 10
if num and is_number(num) and 10 < int(num) < 50:
num = int(num)
if date in ["周"]:
date_scope = (datetime.now() - timedelta(days=7), datetime.now())
elif date in ["月"]:
date_scope = (datetime.now() - timedelta(days=30), datetime.now())
if rank_data := await ChatHistory.get_group_msg_rank(
gid, num, order or "DESC", date_scope
):
name = "昵称:\n\n"
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:
user_name = uid
name += f"\t{idx}.{user_name} \n\n"
num_str += f"\t{num}\n\n"
idx += 1
name_img = await text2image(name.strip(), padding=10, color="#f9f6f2")
num_img = await text2image(num_str.strip(), padding=10, color="#f9f6f2")
if not date_scope:
if date_scope := await ChatHistory.get_group_first_msg_datetime(gid):
date_scope = date_scope.astimezone(
pytz.timezone("Asia/Shanghai")
).replace(microsecond=0)
else:
date_scope = datetime.now().replace(microsecond=0)
date_str = f"日期:{date_scope} - 至今"
else:
date_str = f"日期:{date_scope[0].replace(microsecond=0)} - {date_scope[1].replace(microsecond=0)}"
date_w = BuildImage(0, 0, font_size=15).getsize(date_str)[0]
img_w = date_w if date_w > name_img.w + num_img.w else name_img.w + num_img.w
A = BuildImage(
img_w + 15,
num_img.h + 30,
color="#f9f6f2",
font="CJGaoDeGuo.otf",
font_size=15,
)
await A.atext((10, 10), date_str)
await A.apaste(name_img, (0, 30))
await A.apaste(num_img, (name_img.w, 30))
await msg_handler.send(image(b64=A.pic2bs4()))
# @test.handle()
# async def _(event: MessageEvent):
# print(await ChatHistory.get_user_msg(event.user_id, "private"))
# print(await ChatHistory.get_user_msg_count(event.user_id, "private"))
# print(await ChatHistory.get_user_msg(event.user_id, "group"))
# print(await ChatHistory.get_user_msg_count(event.user_id, "group"))
# print(await ChatHistory.get_group_msg(event.group_id))
# print(await ChatHistory.get_group_msg_count(event.group_id))
+20 -4
View File
@@ -1,15 +1,16 @@
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 nonebot.adapters.onebot.v11.exception import ActionFailed
from utils.manager import group_manager, plugins2settings_manager, requests_manager
from configs.config import NICKNAME
from models.group_info import GroupInfo
@@ -96,6 +97,23 @@ async def _(bot: Bot, event: GroupIncreaseNoticeEvent):
for plugin in data.keys():
if not data[plugin]["default_status"]:
group_manager.block_plugin(plugin, event.group_id)
# 即刻刷新权限
for user_info in await bot.get_group_member_list(group_id=event.group_id):
if user_info["role"] in [
"owner",
"admin",
] and not await LevelUser.is_group_flag(
user_info["user_id"], event.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
)
else:
join_time = datetime.now()
user_info = await bot.get_group_member_info(
@@ -128,9 +146,7 @@ async def _(bot: Bot, event: GroupIncreaseNoticeEvent):
msg = msg.replace("[at]", "")
at_flag = True
if (DATA_PATH / "custom_welcome_msg" / f"{event.group_id}.jpg").exists():
img = image(
abspath=DATA_PATH / "custom_welcome_msg" / f"{event.group_id}.jpg"
)
img = image(DATA_PATH / "custom_welcome_msg" / f"{event.group_id}.jpg")
if msg or img:
msg = msg.strip() + img
msg = "\n" + msg if at_flag else msg
+5 -2
View File
@@ -56,6 +56,9 @@ def _create_help_img(
for matcher in _matchers:
plugin_name = None
_plugin = nonebot.plugin.get_plugin(matcher.plugin_name)
if not _plugin:
logger.warning(f"获取 功能:{matcher.plugin_name} 失败...")
continue
_module = _plugin.module
try:
plugin_name = _module.__getattribute__("__zx_plugin_name__")
@@ -277,7 +280,7 @@ def _create_help_img(
0,
plain_text=msg,
font_size=24,
font="yuanshen.ttf",
font="HYWenHei-85W.ttf",
)
B.paste(text, (w, h), True)
h += 50
@@ -289,7 +292,7 @@ def _create_help_img(
0,
plain_text="注: 红字代表功能被群管理员禁用,红线代表功能正在维护",
font_size=24,
font="yuanshen.ttf",
font="HYWenHei-85W.ttf",
font_color=(231, 74, 57)
),
(300, 10),
+4
View File
@@ -2,6 +2,10 @@ from nonebot.adapters.onebot.v11 import GroupMessageEvent, PrivateMessageEvent
from utils.manager import plugins2block_manager, StaticData
import time
ignore_rst_module = ["ai", "poke", "dialogue"]
other_limit_plugins = ["poke"]
class StatusMessageManager(StaticData):
+65 -50
View File
@@ -1,6 +1,5 @@
from nonebot.matcher import Matcher
from nonebot.message import run_preprocessor, run_postprocessor, IgnoredException
from nonebot.adapters.onebot.v11.exception import ActionFailed
from models.friend_user import FriendUser
from models.group_member_info import GroupInfoUser
from models.bag_user import BagUser
@@ -11,19 +10,25 @@ from utils.manager import (
plugins_manager,
plugins2cd_manager,
plugins2block_manager,
plugins2count_manager
plugins2count_manager,
)
from ._utils import (
set_block_limit_false,
status_message_manager,
ignore_rst_module,
other_limit_plugins,
)
from ._utils import set_block_limit_false, status_message_manager
from nonebot.typing import T_State
from typing import Optional
from nonebot.adapters.onebot.v11 import (
Bot,
ActionFailed,
MessageEvent,
GroupMessageEvent,
PokeNotifyEvent,
PrivateMessageEvent,
Message,
Event
Event,
)
from configs.config import Config
from models.ban_user import BanUser
@@ -37,35 +42,38 @@ _flmt_g = FreqLimiter(Config.get_config("hook", "CHECK_NOTICE_INFO_CD"))
_flmt_s = FreqLimiter(Config.get_config("hook", "CHECK_NOTICE_INFO_CD"))
_flmt_c = FreqLimiter(Config.get_config("hook", "CHECK_NOTICE_INFO_CD"))
ignore_rst_module = ["ai", "poke", "dialogue"]
# 权限检测
@run_preprocessor
async def _(matcher: Matcher, bot: Bot, event: MessageEvent, state: T_State):
async def _(matcher: Matcher, bot: Bot, event: Event, state: T_State):
module = matcher.plugin_name
plugins2info_dict = plugins2settings_manager.get_data()
# 功能的金币检测 #######################################
# 功能的金币检测 #######################################
# 功能的金币检测 #######################################
cost_gold = 0
if isinstance(event, GroupMessageEvent) and plugins2settings_manager.get_plugin_data(module).get('cost_gold'):
cost_gold = plugins2settings_manager.get_plugin_data(module).get('cost_gold')
if isinstance(
event, GroupMessageEvent
) and plugins2settings_manager.get_plugin_data(module).get("cost_gold"):
cost_gold = plugins2settings_manager.get_plugin_data(module).get("cost_gold")
if await BagUser.get_gold(event.user_id, event.group_id) < cost_gold:
await send_msg(f"金币不足..该功能需要{cost_gold}金币..", bot, event)
raise IgnoredException(f"{module} 金币限制...")
# 当插件不阻塞超级用户时,超级用户提前扣除金币
if str(event.user_id) in bot.config.superusers and not plugins2info_dict[module]["limit_superuser"]:
if (
str(event.user_id) in bot.config.superusers
and not plugins2info_dict[module]["limit_superuser"]
):
await BagUser.spend_gold(event.user_id, event.group_id, cost_gold)
try:
if (
(not isinstance(event, MessageEvent) and module != "poke")
or await BanUser.is_ban(event.user_id)
and str(event.user_id) not in bot.config.superusers
(not isinstance(event, MessageEvent) and module not in other_limit_plugins)
or await BanUser.is_ban(event.user_id)
and str(event.user_id) not in bot.config.superusers
) or (
str(event.user_id) in bot.config.superusers
and plugins2info_dict.get(module)
and not plugins2info_dict[module]["limit_superuser"]
str(event.user_id) in bot.config.superusers
and plugins2info_dict.get(module)
and not plugins2info_dict[module]["limit_superuser"]
):
return
except AttributeError:
@@ -76,16 +84,22 @@ async def _(matcher: Matcher, bot: Bot, event: MessageEvent, state: T_State):
_module = _plugin.module
plugin_name = _module.__getattribute__("__zx_plugin_name__")
if (
"[superuser]" in plugin_name.lower()
and str(event.user_id) in bot.config.superusers
"[superuser]" in plugin_name.lower()
and str(event.user_id) in bot.config.superusers
):
return
except AttributeError:
pass
# 群黑名单检测 群总开关检测
if isinstance(event, GroupMessageEvent) or matcher.plugin_name == "poke":
if (
isinstance(event, GroupMessageEvent)
or matcher.plugin_name in other_limit_plugins
):
try:
if group_manager.get_group_level(event.group_id) < 0:
if (
group_manager.get_group_level(event.group_id) < 0
and str(event.user_id) not in bot.config.superusers
):
raise IgnoredException("群黑名单")
if not group_manager.check_group_bot_status(event.group_id):
try:
@@ -95,16 +109,16 @@ async def _(matcher: Matcher, bot: Bot, event: MessageEvent, state: T_State):
raise IgnoredException("功能总开关关闭状态")
except AttributeError:
pass
if module in admin_manager.keys() and matcher.priority not in [1, 9]:
if module in admin_manager.keys() and matcher.priority not in [1, 999]:
if isinstance(event, GroupMessageEvent):
# 个人权限
if (
not await LevelUser.check_level(
event.user_id,
event.group_id,
admin_manager.get_plugin_level(module),
)
and admin_manager.get_plugin_level(module) > 0
not await LevelUser.check_level(
event.user_id,
event.group_id,
admin_manager.get_plugin_level(module),
)
and admin_manager.get_plugin_level(module) > 0
):
try:
if _flmt.check(event.user_id):
@@ -112,7 +126,7 @@ async def _(matcher: Matcher, bot: Bot, event: MessageEvent, state: T_State):
await bot.send_group_msg(
group_id=event.group_id,
message=f"{at(event.user_id)}你的权限不足喔,该功能需要的权限等级:"
f"{admin_manager.get_plugin_level(module)}",
f"{admin_manager.get_plugin_level(module)}",
)
except ActionFailed:
pass
@@ -122,7 +136,7 @@ async def _(matcher: Matcher, bot: Bot, event: MessageEvent, state: T_State):
raise IgnoredException("权限不足")
else:
if not await LevelUser.check_level(
event.user_id, 0, admin_manager.get_plugin_level(module)
event.user_id, 0, admin_manager.get_plugin_level(module)
):
try:
await bot.send_private_msg(
@@ -135,15 +149,17 @@ async def _(matcher: Matcher, bot: Bot, event: MessageEvent, state: T_State):
if event.is_tome():
status_message_manager.add(event.user_id)
raise IgnoredException("权限不足")
if module in plugins2info_dict.keys() and matcher.priority not in [1, 9]:
if module in plugins2info_dict.keys() and matcher.priority not in [1, 999]:
# 戳一戳单独判断
if isinstance(event, GroupMessageEvent) or (
isinstance(event, PokeNotifyEvent) and event.group_id
if (
isinstance(event, GroupMessageEvent)
or isinstance(event, PokeNotifyEvent)
or matcher.plugin_name in other_limit_plugins
):
if status_message_manager.get(event.group_id) is None:
status_message_manager.delete(event.group_id)
if plugins2info_dict[module]["level"] > group_manager.get_group_level(
event.group_id
event.group_id
):
try:
if _flmt_g.check(event.user_id) and module not in ignore_rst_module:
@@ -161,7 +177,7 @@ async def _(matcher: Matcher, bot: Bot, event: MessageEvent, state: T_State):
if not group_manager.get_plugin_status(module, event.group_id):
try:
if module not in ignore_rst_module and _flmt_s.check(
event.group_id
event.group_id
):
_flmt_s.start_cd(event.group_id)
await bot.send_group_msg(
@@ -177,8 +193,8 @@ async def _(matcher: Matcher, bot: Bot, event: MessageEvent, state: T_State):
if not group_manager.get_plugin_status(f"{module}:super", event.group_id):
try:
if (
_flmt_s.check(event.group_id)
and module not in ignore_rst_module
_flmt_s.check(event.group_id)
and module not in ignore_rst_module
):
_flmt_s.start_cd(event.group_id)
await bot.send_group_msg(
@@ -194,8 +210,8 @@ async def _(matcher: Matcher, bot: Bot, event: MessageEvent, state: T_State):
if not plugins_manager.get_plugin_status(module, block_type="group"):
try:
if (
_flmt_c.check(event.group_id)
and module not in ignore_rst_module
_flmt_c.check(event.group_id)
and module not in ignore_rst_module
):
_flmt_c.start_cd(event.group_id)
await bot.send_group_msg(
@@ -225,14 +241,14 @@ async def _(matcher: Matcher, bot: Bot, event: MessageEvent, state: T_State):
# 维护
if not plugins_manager.get_plugin_status(module, block_type="all"):
if isinstance(
event, GroupMessageEvent
event, GroupMessageEvent
) and group_manager.check_group_is_white(event.group_id):
return
try:
if isinstance(event, GroupMessageEvent):
if (
_flmt_c.check(event.group_id)
and module not in ignore_rst_module
_flmt_c.check(event.group_id)
and module not in ignore_rst_module
):
_flmt_c.start_cd(event.group_id)
await bot.send_group_msg(
@@ -267,9 +283,9 @@ async def _(matcher: Matcher, bot: Bot, event: MessageEvent, state: T_State):
limit_type = plugin_cd_data["limit_type"]
rst = plugin_cd_data["rst"]
if (
(isinstance(event, PrivateMessageEvent) and check_type == "private")
or (isinstance(event, GroupMessageEvent) and check_type == "group")
or plugins2cd_manager.get_plugin_data(module).get("check_type") == "all"
(isinstance(event, PrivateMessageEvent) and check_type == "private")
or (isinstance(event, GroupMessageEvent) and check_type == "group")
or plugins2cd_manager.get_plugin_data(module).get("check_type") == "all"
):
cd_type_ = event.user_id
if limit_type == "group" and isinstance(event, GroupMessageEvent):
@@ -288,9 +304,9 @@ async def _(matcher: Matcher, bot: Bot, event: MessageEvent, state: T_State):
limit_type = plugin_block_data["limit_type"]
rst = plugin_block_data["rst"]
if (
(isinstance(event, PrivateMessageEvent) and check_type == "private")
or (isinstance(event, GroupMessageEvent) and check_type == "group")
or check_type == "all"
(isinstance(event, PrivateMessageEvent) and check_type == "private")
or (isinstance(event, GroupMessageEvent) and check_type == "group")
or check_type == "all"
):
block_type_ = event.user_id
if limit_type == "group" and isinstance(event, GroupMessageEvent):
@@ -304,8 +320,8 @@ async def _(matcher: Matcher, bot: Bot, event: MessageEvent, state: T_State):
plugins2block_manager.set_true(block_type_, module)
# Count
if (
plugins2count_manager.check_plugin_count_status(module)
and event.user_id not in bot.config.superusers
plugins2count_manager.check_plugin_count_status(module)
and event.user_id not in bot.config.superusers
):
plugin_count_data = plugins2count_manager.get_plugin_count_data(module)
limit_type = plugin_count_data["limit_type"]
@@ -375,4 +391,3 @@ async def init_rst(rst: str, event: MessageEvent):
if "[at]" in rst and isinstance(event, GroupMessageEvent):
rst = rst.replace("[at]", str(at(event.user_id)))
return rst
+25 -17
View File
@@ -1,16 +1,19 @@
from nonebot.matcher import Matcher
from nonebot.message import run_preprocessor, IgnoredException
from nonebot.adapters.onebot.v11.exception import ActionFailed
from nonebot.typing import T_State
from nonebot.adapters.onebot.v11 import (
Bot,
Event,
MessageEvent,
ActionFailed,
PokeNotifyEvent,
GroupMessageEvent,
)
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
Config.add_plugin_config(
@@ -25,18 +28,19 @@ _flmt = FreqLimiter(300)
# 检查是否被ban
@run_preprocessor
async def _(matcher: Matcher, bot: Bot, event: MessageEvent, state: T_State):
try:
if (
await BanUser.is_super_ban(event.user_id)
and str(event.user_id) not in bot.config.superusers
):
raise IgnoredException("用户处于超级黑名单中")
except AttributeError:
pass
if not isinstance(event, MessageEvent):
return
if matcher.type == "message" and matcher.priority not in [1, 9]:
async def _(matcher: Matcher, bot: Bot, event: Event, state: T_State):
if (
(isinstance(event, MessageEvent) or isinstance(event, PokeNotifyEvent))
and matcher.priority not in [1, 999]
) or matcher.plugin_name in other_limit_plugins:
try:
if (
await BanUser.is_super_ban(event.user_id)
and str(event.user_id) not in bot.config.superusers
):
raise IgnoredException("用户处于超级黑名单中")
except AttributeError:
pass
if (
await BanUser.is_ban(event.user_id)
and str(event.user_id) not in bot.config.superusers
@@ -54,10 +58,14 @@ async def _(matcher: Matcher, bot: Bot, event: MessageEvent, state: T_State):
if not static_flmt.check(event.user_id):
raise IgnoredException("用户处于黑名单中")
static_flmt.start_cd(event.user_id)
if matcher.priority != 9:
if matcher.priority != 999:
try:
ban_result = Config.get_config("hook", "BAN_RESULT")
if ban_result and _flmt.check(event.user_id):
if (
ban_result
and _flmt.check(event.user_id)
and matcher.plugin_name not in ignore_rst_module
):
_flmt.start_cd(event.user_id)
await bot.send_group_msg(
group_id=event.group_id,
@@ -71,10 +79,10 @@ async def _(matcher: Matcher, bot: Bot, event: MessageEvent, state: T_State):
if not static_flmt.check(event.user_id):
raise IgnoredException("用户处于黑名单中")
static_flmt.start_cd(event.user_id)
if matcher.priority != 9:
if matcher.priority != 999:
try:
ban_result = Config.get_config("hook", "BAN_RESULT")
if ban_result:
if ban_result and matcher.plugin_name not in ignore_rst_module:
await bot.send_private_msg(
user_id=event.user_id,
message=at(event.user_id)
+2 -2
View File
@@ -1,9 +1,9 @@
from nonebot.matcher import Matcher
from nonebot.message import run_preprocessor, IgnoredException
from nonebot.adapters.onebot.v11.exception import ActionFailed
from nonebot.typing import T_State
from nonebot.adapters.onebot.v11 import (
Bot,
ActionFailed,
MessageEvent,
GroupMessageEvent,
)
@@ -25,7 +25,7 @@ _blmt = BanCheckLimiter(
async def _(matcher: Matcher, bot: Bot, event: GroupMessageEvent, state: T_State):
if not isinstance(event, MessageEvent):
return
if matcher.type == "message" and matcher.priority not in [1, 9]:
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(
+11
View File
@@ -2,12 +2,15 @@ from nonebot.matcher import Matcher
from nonebot.message import run_preprocessor, IgnoredException
from nonebot.typing import T_State
from ._utils import status_message_manager
from utils.image_utils import text2image
from typing import Dict, Any
from nonebot.adapters.onebot.v11 import (
Bot,
MessageEvent,
PrivateMessageEvent,
GroupMessageEvent,
)
import re
# 为什么AI会自己和自己聊天
@@ -38,3 +41,11 @@ async def _(matcher: Matcher, bot: Bot, event: MessageEvent, state: T_State):
status_message_manager.delete(event.user_id)
raise IgnoredException("有命令就别说话了")
# @Bot.on_calling_api
# async def handle_api_call(bot: Bot, api: str, data: Dict[str, Any]):
# if api in ["send_msg", "send_group_msg", "send_private_msg"]:
# msg = str(data["message"])
# if (r := re.search("\[\[To_Img\|?(.*?)]]", msg)) or (r := re.search("&#91;&#91;To_Img\|?(.*?)&#91;&#91;")):
+31 -8
View File
@@ -1,7 +1,6 @@
from nonebot.exception import MockApiException
from nonebot.adapters.onebot.v11 import Bot, MessageSegment
from nonebot.adapters.onebot.v11 import Bot, Message
from utils.manager import group_manager
from utils.utils import get_message_text
from typing import Dict, Any
import re
@@ -10,15 +9,39 @@ import re
async def handle_api_call(bot: Bot, api: str, data: Dict[str, Any]):
r = None
if (
(api == "send_msg" and data["message"] == "group_id" or api == "send_group_msg")
and (r := re.search("^\[\[_task\|(.*)]]", get_message_text(data["message"])))
(
(api == "send_msg" and data.get("message_type") == "group")
or api == "send_group_msg"
)
and (
(
r := re.search(
"^\[\[_task\|(.*)]]",
data["message"].strip()
if isinstance(data["message"], str)
else str(data["message"]["text"]).strip(),
)
)
or (
r := re.search(
"^&#91;&#91;_task\|(.*)&#93;&#93;",
data["message"].strip()
if isinstance(data["message"], str)
else str(data["message"]["text"]).strip(),
)
)
)
and r.group(1) in group_manager.get_task_data().keys()
):
task = r.group(1)
group_id = data["group_id"]
if not await group_manager.check_group_task_status(group_id, task):
if group_manager.get_group_level(
group_id
) < 0 or not await group_manager.check_group_task_status(group_id, task):
raise MockApiException(f"被动技能 {task} 处于关闭状态...")
else:
msg = str(data["message"][0])
msg = msg.replace(f"&#91;&#91;_task|{task}&#93;&#93;", "")
data["message"][0] = MessageSegment.text(msg)
msg = str(data["message"]).strip()
msg = msg.replace(f"&#91;&#91;_task|{task}&#93;&#93;", "").replace(
f"[[_task|{task}]]", ""
)
data["message"] = Message(msg)
@@ -20,26 +20,36 @@ def init_none_plugin_count_manager():
"""
清除已删除插件数据
"""
modules = [x.module for x in get_matchers()]
for module in none_plugin_count_manager.keys():
if module not in modules:
none_plugin_count_manager.add_count(module)
else:
none_plugin_count_manager.reset(module)
if none_plugin_count_manager.check(module):
modules = [x.plugin_name for x in get_matchers()]
plugins_manager_list = list(plugins_manager.keys())
for module in plugins_manager_list:
if module not in modules or none_plugin_count_manager.check(module):
try:
plugin_name = plugins_manager.get(module)["plugin_name"]
except (AttributeError, KeyError):
plugin_name = ""
try:
plugins2settings_manager.delete(module)
plugins2count_manager.delete(module)
plugins2cd_manager.delete(module)
plugins2block_manager.delete(module)
plugins_manager.delete(module)
resources_manager.remove_resource(module)
logger.info(f"{module}:{plugin_name} 插件疑似已删除,清除对应插件数据...")
except Exception as e:
logger.error(
f"{module}:{plugin_name} 插件疑似已删除,清除对应插件数据失败...{type(e)}:{e}"
)
if none_plugin_count_manager.check(module):
try:
plugins2settings_manager.delete(module)
plugins2settings_manager.save()
plugins2count_manager.delete(module)
plugins2count_manager.save()
plugins2cd_manager.delete(module)
plugins2cd_manager.save()
plugins2block_manager.delete(module)
plugins2block_manager.save()
plugins_manager.delete(module)
plugins_manager.save()
# resources_manager.remove_resource(module)
none_plugin_count_manager.delete(module)
logger.info(f"{module}:{plugin_name} 插件疑似已删除,清除对应插件数据...")
except Exception as e:
logger.exception(
f"{module}:{plugin_name} 插件疑似已删除,清除对应插件数据失败...{type(e)}:{e}")
else:
none_plugin_count_manager.add_count(module)
logger.info(
f"{module}:{plugin_name} 插件疑似已删除,加载{none_plugin_count_manager._max_count}次失败后将清除对应插件数据,当前次数:{none_plugin_count_manager._data[module]}")
else:
none_plugin_count_manager.reset(module)
none_plugin_count_manager.save()
@@ -3,6 +3,7 @@ from ruamel.yaml import round_trip_load, round_trip_dump, YAML
from utils.manager import admin_manager, plugins_manager
from configs.config import Config
from services.log import logger
from utils.text_utils import prompt2cn
from utils.utils import get_matchers
from ruamel import yaml
import nonebot
@@ -56,7 +57,9 @@ def init_plugins_config(data_path):
_override=True,
)
else:
Config.add_plugin_config(matcher.plugin_name, key, plugin_configs[key])
Config.add_plugin_config(
matcher.plugin_name, key, plugin_configs[key]
)
else:
plugin_configs = _data[matcher.plugin_name]
for key in plugin_configs:
@@ -79,7 +82,10 @@ def init_plugins_config(data_path):
_data[plugin].yaml_set_start_comment(plugin_name, indent=2)
# 初始化未设置的管理员权限等级
for k, v in Config.get_admin_level_data():
admin_manager.set_admin_level(k, v)
try:
admin_manager.set_admin_level(k, v)
except KeyError as e:
raise KeyError(f"{e} ****** 请检查是否有插件加载失败 ******")
# 存完插件基本设置
with open(plugins2config_file, "w", encoding="utf8") as wf:
round_trip_dump(
@@ -96,14 +102,23 @@ def init_plugins_config(data_path):
for plugin in Config.keys():
_tmp_data[plugin] = {}
for k in Config[plugin].keys():
if _data.get(plugin) and k in _data[plugin].keys():
Config.set_config(plugin, k, _data[plugin][k])
if level2module := Config.get_level2module(plugin, k):
try:
admin_manager.set_admin_level(level2module, _data[plugin][k])
except KeyError:
logger.warning(f"{level2module} 设置权限等级失败:{_data[plugin][k]}")
_tmp_data[plugin][k] = Config.get_config(plugin, k)
try:
if _data.get(plugin) and k in _data[plugin].keys():
Config.set_config(plugin, k, _data[plugin][k])
if level2module := Config.get_level2module(plugin, k):
try:
admin_manager.set_admin_level(
level2module, _data[plugin][k]
)
except KeyError:
logger.warning(
f"{level2module} 设置权限等级失败:{_data[plugin][k]}"
)
_tmp_data[plugin][k] = Config.get_config(plugin, k)
except AttributeError as e:
raise AttributeError(
f"{e}\n" + prompt2cn("可能为config.yaml配置文件填写不规范", 46)
)
Config.save()
temp_file = Path() / "configs" / "temp_config.yaml"
try:
@@ -28,7 +28,7 @@ def init_plugins_cd_limit(data_path):
_module = _plugin.module
plugin_cd_limit = _module.__getattribute__("__plugin_cd_limit__")
plugins2cd_manager.add_cd_limit(
matcher.plugin_name, data_dict=plugin_cd_limit
matcher.plugin_name, **plugin_cd_limit
)
except AttributeError:
pass
@@ -76,7 +76,7 @@ def init_plugins_block_limit(data_path):
_module = _plugin.module
plugin_block_limit = _module.__getattribute__("__plugin_block_limit__")
plugins2block_manager.add_block_limit(
matcher.plugin_name, data_dict=plugin_block_limit
matcher.plugin_name, **plugin_block_limit
)
except AttributeError:
pass
@@ -124,7 +124,7 @@ def init_plugins_count_limit(data_path):
_module = _plugin.module
plugin_count_limit = _module.__getattribute__("__plugin_count_limit__")
plugins2count_manager.add_count_limit(
matcher.plugin_name, data_dict=plugin_count_limit
matcher.plugin_name, **plugin_count_limit
)
except AttributeError:
pass
@@ -74,7 +74,7 @@ def init_plugins_settings(data_path: str):
if plugin_settings.get('cost_gold') is None:
plugin_settings['cost_gold'] = 0
if (
plugin_settings["cmd"] is not None
plugin_settings.get("cmd") is not None
and plugin_name not in plugin_settings["cmd"]
):
plugin_settings["cmd"].append(plugin_name)
+2 -2
View File
@@ -1,6 +1,7 @@
from nonebot import on_request, on_message
from nonebot.adapters.onebot.v11 import (
Bot,
ActionFailed,
FriendRequestEvent,
GroupRequestEvent,
MessageEvent,
@@ -8,7 +9,6 @@ from nonebot.adapters.onebot.v11 import (
from models.friend_user import FriendUser
from datetime import datetime
from configs.config import NICKNAME, Config
from nonebot.adapters.onebot.v11.exception import ActionFailed
from utils.manager import requests_manager
from models.group_info import GroupInfo
from utils.utils import scheduler
@@ -25,7 +25,7 @@ __plugin_configs__ = {
friend_req = on_request(priority=5, block=True)
group_req = on_request(priority=5, block=True)
x = on_message(priority=9, block=False)
x = on_message(priority=999, block=False)
exists_data = {"private": {}, "group": {}}
+14 -7
View File
@@ -1,4 +1,4 @@
from nonebot.adapters.onebot.v11 import Bot, GroupMessageEvent, PrivateMessageEvent, Message
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
@@ -22,9 +22,15 @@ __plugin_des__ = "区区昵称,才不想叫呢!"
__plugin_cmd__ = ["以后叫我 [昵称]", f"{NICKNAME}我是谁"]
__plugin_version__ = 0.1
__plugin_author__ = "HibiKier"
__plugin_settings__ = {
"level": 5,
"default_status": True,
"limit_superuser": False,
"cmd": ["昵称", "昵称系统"],
}
__plugin_configs__ = {
"BLACK_WORD": {
"value": ["爸", "爹", "爷"],
"value": ["爸", "爹", "爷", "父亲"],
"help": "昵称所屏蔽的关键词,会被替换为 *",
"default_value": None
}
@@ -47,7 +53,7 @@ cancel_nickname = on_command("取消昵称", rule=to_me(), priority=5, block=Tru
@nickname.handle()
async def _(bot: Bot, event: GroupMessageEvent, arg: Message = CommandArg()):
async def _(bot: Bot, event: MessageEvent, arg: Message = CommandArg()):
msg = arg.extract_plain_text().strip()
if not msg:
await nickname.finish("叫你空白?叫你虚空?叫你无名??", at_sender=True)
@@ -57,8 +63,9 @@ async def _(bot: Bot, event: GroupMessageEvent, arg: Message = CommandArg()):
await nickname.finish("笨蛋!休想占用我的名字!#", at_sender=True)
_tmp = ""
black_word = Config.get_config("nickname", "BLACK_WORD")
for x in msg:
_tmp += "*" if x in black_word else x
if black_word:
for x in msg:
_tmp += "*" if x in black_word else x
msg = _tmp
if isinstance(event, GroupMessageEvent):
if await GroupInfoUser.set_group_member_nickname(
@@ -73,7 +80,7 @@ async def _(bot: Bot, event: GroupMessageEvent, arg: Message = CommandArg()):
f"好啦好啦,我知道啦,{msg},以后就这么叫你吧",
f"嗯嗯,{NICKNAME}记住你的昵称了哦,{msg}",
f"好突然,突然要叫你昵称什么的...{msg}..",
f"{NICKNAME}会好好记住的{msg}的,放心吧",
f"{NICKNAME}会好好记住{msg}的,放心吧",
f"好..好.,那窝以后就叫你{msg}了.",
]
)
@@ -90,7 +97,7 @@ async def _(bot: Bot, event: GroupMessageEvent, arg: Message = CommandArg()):
f"好啦好啦,我知道啦,{msg},以后就这么叫你吧",
f"嗯嗯,{NICKNAME}记住你的昵称了哦,{msg}",
f"好突然,突然要叫你昵称什么的...{msg}..",
f"{NICKNAME}会好好记住的{msg}的,放心吧",
f"{NICKNAME}会好好记住{msg}的,放心吧",
f"好..好.,那窝以后就叫你{msg}了.",
]
)
+68
View File
@@ -0,0 +1,68 @@
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
__zx_plugin_name__ = "插件商店 [Superuser]"
__plugin_usage__ = """
usage:
下载安装插件
指令:
查看插件仓库
更新插件仓库
安装插件 [name/id] (重新安装等同于更新)
卸载插件 [name/id]
""".strip()
__plugin_des__ = "从真寻适配仓库中下载插件"
__plugin_cmd__ = ["查看插件仓库", "更新插件仓库", "安装插件 [name/id]", "卸载插件 [name/id]"]
__plugin_version__ = 0.1
__plugin_author__ = "HibiKier"
show_repo = on_regex("^查看插件仓库$", priority=1, block=True, permission=SUPERUSER)
update_repo = on_regex("^更新插件仓库$", priority=1, block=True, permission=SUPERUSER)
install_plugin_matcher = on_command("安装插件", priority=1, block=True, permission=SUPERUSER)
uninstall_plugin_matcher = on_command("卸载插件", priority=1, block=True, permission=SUPERUSER)
@install_plugin_matcher.handle()
async def _(bot: Bot, event: MessageEvent, arg: Message = CommandArg()):
arg = arg.extract_plain_text().strip()
msg = await install_plugin(arg)
await install_plugin_matcher.send(msg)
@uninstall_plugin_matcher.handle()
async def _(bot: Bot, event: MessageEvent, arg: Message = CommandArg()):
arg = arg.extract_plain_text().strip()
msg = await uninstall_plugin(arg)
await install_plugin_matcher.send(msg)
@update_repo.handle()
async def _(bot: Bot, event: MessageEvent):
code = await download_json()
if code == 200:
await update_repo.finish("更新插件仓库信息成功!")
await update_repo.send("更新插件仓库信息失败!")
@show_repo.handle()
async def _(bot: Bot, event: MessageEvent):
msg = await show_plugin_repo()
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" 查看插件仓库"
)
+198
View File
@@ -0,0 +1,198 @@
import os
import shutil
import zipfile
from pathlib import Path
from typing import Union, Tuple
from utils.manager import plugins_manager
import ujson as json
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.utils import is_number
path = DATA_PATH / "plugin_shop"
if not path.exists():
path.mkdir(parents=True, exist_ok=True)
repo_json_url = "https://github.com/zhenxun-org/nonebot_plugins_zhenxun_bot/archive/refs/heads/index.zip"
repo_zip_path = TEMP_PATH / "plugin_repo_json.zip"
plugin_json = path / "zhenxun_plugins.json"
extensive_plugin_path = Path() / "extensive_plugin"
path = DATA_PATH / "plugin_shop"
if not path.exists():
path.mkdir(parents=True, exist_ok=True)
if not extensive_plugin_path.exists():
extensive_plugin_path.mkdir(parents=True, exist_ok=True)
data = {}
async def install_plugin(name: str) -> str:
"""
安装插件
:param name: 插件名或下标
"""
try:
if is_number(name):
name, code = await get_plugin_name(int(name))
if code != 200:
return name
plugin_url = data[name]["download_url"]
url = (await AsyncHttpx.get(plugin_url)).headers.get("Location")
zip_file = TEMP_PATH / f"{name}.zip"
if zip_file.exists():
zip_file.unlink()
if await AsyncHttpx.download_file(url, zip_file):
logger.debug("开始解压插件压缩包...")
# 解压
zf = zipfile.ZipFile(zip_file, "r")
extract_path = TEMP_PATH / f"{name}"
if extract_path.exists():
shutil.rmtree(extract_path.absolute(), ignore_errors=True)
extract_path.mkdir(exist_ok=True, parents=True)
for file in zf.namelist():
zf.extract(file, extract_path)
zf.close()
logger.debug("解压插件压缩包完成...")
logger.debug("开始移动插件文件夹...")
if (extensive_plugin_path / f"{name}").exists():
logger.debug("extensive_plugin目录下文件夹已存在,删除该目录插件文件夹...")
shutil.rmtree(
(extensive_plugin_path / f"{name}").absolute(), ignore_errors=True
)
extract_path.rename(extensive_plugin_path / f"{name}")
tmp = ""
if "pyproject.toml" in os.listdir(extensive_plugin_path / f"{name}"):
tmp = "检测到该插件含有额外依赖,当前安装无法保证依赖完全安装成功。"
os.system(
f"poetry run pip install -r {(extensive_plugin_path / f'{name}' / 'pyproject.toml').absolute()}"
)
elif "requirements.txt" in os.listdir(extensive_plugin_path / f"{name}"):
tmp = "检测到该插件含有额外依赖,当前安装无法保证依赖完全安装成功。"
os.system(
f"poetry run pip install -r {(extensive_plugin_path / f'{name}' / 'requirements.txt').absolute()}"
)
with open(extensive_plugin_path / f"{name}" / "plugin_info.json", "w") as f:
json.dump(data[name], f, ensure_ascii=False, indent=4)
logger.debug("移动插件文件夹完成...")
logger.info(f"成功安装插件 {name} 成功!\n{tmp}")
return f"成功安装插件 {name},请重启真寻!"
except Exception as e:
logger.error(f"安装插件 {name} 失败 {type(e)}:{e}")
return f"安装插件 {name} 失败 {type(e)}:{e}"
async def uninstall_plugin(name: str) -> str:
"""
删除插件
:param name: 插件名或下标
"""
try:
if is_number(name):
name, code = await get_plugin_name(int(name))
if code != 200:
return name
if name not in os.listdir(extensive_plugin_path):
return f"未安装 {name} 插件!"
shutil.rmtree((extensive_plugin_path / name).absolute(), ignore_errors=True)
logger.info(f"插件 {name} 删除成功!")
return f"插件 {name} 删除成功!"
except Exception as e:
logger.error(f"删除插件 {name} 失败 {type(e)}:{e}")
return f"删除插件 {name} 失败 {type(e)}:{e}"
async def show_plugin_repo() -> Union[int, str]:
"""
获取插件仓库数据并格式化
"""
if not plugin_json.exists():
code = await download_json()
if code != 200:
return code
plugin_info = json.load(open(plugin_json, "r", encoding="utf8"))
load_plugin_list = plugins_manager.get_data().keys()
image_list = []
w, h = 0, 0
line_height = 10
for i, key in enumerate(plugin_info.keys()):
data[key] = {
"名称": plugin_info[key]["plugin_name"],
"模块": key,
"作者": plugin_info[key]["author"],
"版本": plugin_info[key]["version"],
"简介": plugin_info[key]["introduction"],
"download_url": plugin_info[key]["download_url"],
"github_url": plugin_info[key]["github_url"],
}
status = ""
version = ""
if key in load_plugin_list:
status = "<f font_color=#1a7e30>[已安装]</f>"
version = f"<f font_color=#1a7e30>[{plugins_manager.get_data()[key]['version']}]</f>"
s = (
f'id:{i+1}\n名称:{plugin_info[key]["plugin_name"]}'
f' \t\t{status}\n'
f"模块:{key}\n"
f'作者:{plugin_info[key]["author"]}\n'
f'版本:{plugin_info[key]["version"]} \t\t{version}\n'
f'简介:{plugin_info[key]["introduction"]}\n'
f"-------------------"
)
img = await text2image(s, font_size=20, color="#f9f6f2")
w = w if w > img.w else img.w
h += img.h + line_height
image_list.append(img)
A = BuildImage(w + 50, h + 50, color="#f9f6f2")
cur_h = 25
for img in image_list:
await A.apaste(img, (25, cur_h))
cur_h += img.h + line_height
return A.pic2bs4()
async def download_json() -> int:
"""
下载插件库json文件
"""
try:
url = (await AsyncHttpx.get(repo_json_url)).headers.get("Location")
if repo_zip_path.exists():
repo_zip_path.unlink()
if await AsyncHttpx.download_file(url, repo_zip_path):
zf = zipfile.ZipFile(repo_zip_path, "r")
extract_path = path / "temp"
for file in zf.namelist():
zf.extract(file, extract_path)
zf.close()
if plugin_json.exists():
plugin_json.unlink()
(
extract_path
/ "nonebot_plugins_zhenxun_bot-index"
/ "zhenxun_plugins.json"
).rename(plugin_json)
shutil.rmtree(extract_path.absolute(), ignore_errors=True)
return 200
except Exception as e:
logger.error(f"下载插件库压缩包失败或解压失败 {type(e)}:{e}")
return 999
async def get_plugin_name(name: int) -> Tuple[str, int]:
"""
通过下标获取插件名
:param name: 下标
"""
name = int(name)
if not data:
await show_plugin_repo()
if name < 1 or name > len(data.keys()):
return "下标超过上下限!", 999
name = list(data.keys())[name - 1]
return name, 200
+4
View File
@@ -108,6 +108,10 @@ async def _():
"ALTER TABLE genshin ADD bind_group Integer;",
"genshin"
), # 新增原神群号绑定字段
(
"ALTER TABLE chat_history ADD plain_text Text;",
"chat_history"
), # 新增纯文本
]
for sql in sql_str:
try:
+7 -2
View File
@@ -1,9 +1,10 @@
from nonebot import on_command
from nonebot.adapters.onebot.v11 import GroupMessageEvent, Message
from nonebot.adapters.onebot.v11 import GroupMessageEvent, Message, ActionFailed
from nonebot.params import CommandArg
from nonebot.adapters.onebot.v11.permission import GROUP
from utils.data_utils import init_rank
from models.bag_user import BagUser
from utils.image_utils import text2image
from utils.message_builder import image
from utils.utils import is_number
@@ -34,7 +35,11 @@ gold_rank = on_command("金币排行", priority=5, block=True, permission=GROUP)
@my_gold.handle()
async def _(event: GroupMessageEvent):
await my_gold.finish(await BagUser.get_user_total_gold(event.user_id, event.group_id))
msg = await BagUser.get_user_total_gold(event.user_id, event.group_id)
try:
await my_gold.send(msg)
except ActionFailed:
await my_gold.send(image(b64=(await text2image(msg, color="#f9f6f2", padding=10)).pic2bs4()))
@gold_rank.handle()
+54 -42
View File
@@ -7,11 +7,13 @@ from typing import Optional, Union
from configs.config import Config
from nonebot import Driver
from nonebot.plugin import require
from utils.decorator.shop import shop_register
import nonebot
import time
driver: Driver = nonebot.get_driver()
use = require("use")
@@ -20,24 +22,26 @@ async def init_default_shop_goods():
"""
导入内置的三个商品
"""
async def sign_card(**kwargs):
user_id = kwargs['user_id']
group_id = kwargs['group_id']
prob = kwargs["prob"]
@shop_register(
name=("好感度双倍加持卡Ⅰ", "好感度双倍加持卡Ⅱ", "好感度双倍加持卡Ⅲ"),
price=(30, 150, 250),
des=(
"下次签到双倍好感度概率 + 10%(谁才是真命天子?)(同类商品将覆盖)",
"下次签到双倍好感度概率 + 20%(平平庸庸)(同类商品将覆盖)",
"下次签到双倍好感度概率 + 30%(金币才是真命天子!)(同类商品将覆盖)",
),
load_status=Config.get_config("shop", "IMPORT_DEFAULT_SHOP_GOODS"),
** {"好感度双倍加持卡Ⅰ_prob": 0.1, "好感度双倍加持卡Ⅱ_prob": 0.2, "好感度双倍加持卡Ⅲ_prob": 0.3},
)
async def sign_card(user_id: int, group_id: int, prob: float):
user = await SignGroupUser.ensure(user_id, group_id)
await user.update(add_probability=prob).apply()
if Config.get_config("shop", "IMPORT_DEFAULT_SHOP_GOODS"):
await register_goods(
"好感度双倍加持卡Ⅰ", 30, "下次签到双倍好感度概率 + 10%(谁才是真命天子?)(同类商品将覆盖)"
)
use.register_use("好感度双倍加持卡Ⅰ", sign_card, **{"prob": 0.1})
await register_goods("好感度双倍加持卡Ⅱ", 150, "下次签到双倍好感度概率 + 20%(平平庸庸)(同类商品将覆盖)")
use.register_use("好感度双倍加持卡Ⅱ", sign_card, **{"prob": 0.2})
await register_goods(
"好感度双倍加持卡Ⅲ", 250, "下次签到双倍好感度概率 + 30%(金币才是真命天子!)(同类商品将覆盖)"
)
use.register_use("好感度双倍加持卡Ⅲ", sign_card, **{"prob": 0.3})
@driver.on_bot_connect
async def _():
await shop_register.load_register()
# 创建商店界面
@@ -59,9 +63,7 @@ async def create_shop_help() -> str:
A = BuildImage(1000, h, color="#f9f6f2")
current_h = 0
for goods in _list:
bk = BuildImage(
700, 80, font_size=15, color="#f9f6f2", font="CJGaoDeGuo.otf"
)
bk = BuildImage(700, 80, font_size=15, color="#f9f6f2", font="CJGaoDeGuo.otf")
goods_image = BuildImage(
600, 80, font_size=20, color="#a29ad6", font="CJGaoDeGuo.otf"
)
@@ -92,10 +94,18 @@ async def create_shop_help() -> str:
await bk.apaste(goods_image, alpha=True)
# 添加限时图标和时间
if goods.goods_limit_time > 0:
_limit_time_logo = BuildImage(40, 40, background=f"{IMAGE_PATH}/other/time.png")
_limit_time_logo = BuildImage(
40, 40, background=f"{IMAGE_PATH}/other/time.png"
)
await bk.apaste(_limit_time_logo, (600, 0), True)
await bk.apaste(BuildImage(0, 0, plain_text="限时!", font_size=23, font="CJGaoDeGuo.otf"), (640, 10), True)
limit_time = time.strftime("%Y-%m-%d %H:%M", time.localtime(goods.goods_limit_time)).split()
await bk.apaste(
BuildImage(0, 0, plain_text="限时!", font_size=23, font="CJGaoDeGuo.otf"),
(640, 10),
True,
)
limit_time = time.strftime(
"%Y-%m-%d %H:%M", time.localtime(goods.goods_limit_time)
).split()
y_m_d = limit_time[0]
_h_m = limit_time[1].split(":")
h_m = _h_m[0] + "时 " + _h_m[1] + "分"
@@ -125,13 +135,12 @@ async def create_shop_help() -> str:
async def register_goods(
name: str,
price: int,
des: str,
discount: Optional[float] = 1,
limit_time: Optional[int] = 0,
**kwargs,
):
name: str,
price: int,
des: str,
discount: Optional[float] = 1,
limit_time: Optional[int] = 0,
) -> bool:
"""
添加商品
例如: 折扣:可选参数↓ 限时时间:可选,单位为小时
@@ -142,21 +151,20 @@ async def register_goods(
:param des: 商品简介
:param discount: 商品折扣
:param limit_time: 商品限时销售时间,单位为小时
:param kwargs: kwargs
:return: 是否添加成功
"""
if kwargs:
name = kwargs.get("name")
price = kwargs.get("price")
des = kwargs.get("des")
discount = kwargs.get("discount")
limit_time = kwargs.get("time_limit")
limit_time = float(limit_time) if limit_time else limit_time
discount = discount if discount is None else 1
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
)
if not await GoodsInfo.get_goods_info(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
else 0
)
return await GoodsInfo.add_goods(
name, int(price), des, float(discount), limit_time
)
return False
# 删除商品
@@ -230,7 +238,11 @@ async def update_goods(**kwargs) -> "str, str, int":
int(price),
des,
float(discount),
int(time.time() + limit_time * 60 * 60 if limit_time != 0 and new_time else 0),
int(
time.time() + limit_time * 60 * 60
if limit_time != 0 and new_time
else 0
),
),
name,
tmp[:-1],
+81 -32
View File
@@ -1,7 +1,11 @@
from nonebot.adapters.onebot.v11 import GroupMessageEvent, MessageSegment
from services.log import logger
from nonebot.adapters.onebot.v11 import Bot
from pydantic import create_model
from utils.models import ShopParam
from typing import Optional, Union
from types import MappingProxyType
import inspect
import asyncio
@@ -22,7 +26,7 @@ class GoodsUseFuncManager:
判断商品使用方法是否被注册
:param goods_name: 商品名称
"""
return bool(self._data.get(goods_name))
return bool(self ._data.get(goods_name))
def get_max_num_limit(self, goods_name: str) -> int:
"""
@@ -30,24 +34,52 @@ class GoodsUseFuncManager:
:param goods_name: 商品名称
"""
if self.exists(goods_name):
return self._data[goods_name]["kwargs"]["_max_num_limit"]
return self._data[goods_name]["kwargs"]["max_num_limit"]
return 1
async def use(self, **kwargs) -> Optional[Union[str, MessageSegment]]:
async def use(
self, param: ShopParam, **kwargs
) -> Optional[Union[str, MessageSegment]]:
"""
使用道具
:param param: BaseModel
:param kwargs: kwargs
"""
goods_name = kwargs.get("goods_name")
def parse_args(args_: MappingProxyType):
param_list_ = []
_bot = param.bot
param.bot = None
param_json = param.dict()
param_json["bot"] = _bot
for par in args_.keys():
if par in ["shop_param"]:
param_list_.append(param)
elif par not in ["args", "kwargs"]:
param_list_.append(param_json.get(par))
if kwargs.get(par) is not None:
del kwargs[par]
return param_list_
goods_name = param.goods_name
if self.exists(goods_name):
if asyncio.iscoroutinefunction(self._data[goods_name]["func"]):
return await self._data[goods_name]["func"](
**kwargs,
)
args = inspect.signature(self._data[goods_name]["func"]).parameters
if args and list(args.keys())[0] != "kwargs":
if asyncio.iscoroutinefunction(self._data[goods_name]["func"]):
return await self._data[goods_name]["func"](
*parse_args(args)
)
else:
return self._data[goods_name]["func"](
*parse_args(args)
)
else:
return self._data[goods_name]["func"](
**kwargs,
)
if asyncio.iscoroutinefunction(self._data[goods_name]["func"]):
return await self._data[goods_name]["func"](
**kwargs,
)
else:
return self._data[goods_name]["func"](
**kwargs,
)
def check_send_success_message(self, goods_name: str) -> bool:
"""
@@ -67,6 +99,18 @@ class GoodsUseFuncManager:
return self._data[goods_name]["kwargs"]
return {}
def init_model(self, goods_name: str, bot: Bot, event: GroupMessageEvent, num: int):
return self._data[goods_name]["model"](
**{
"goods_name": goods_name,
"bot": bot,
"event": event,
"user_id": event.user_id,
"group_id": event.group_id,
"num": num,
}
)
func_manager = GoodsUseFuncManager()
@@ -83,22 +127,23 @@ async def effect(
:return: 使用是否成功
"""
# 优先使用注册的商品插件
try:
if func_manager.exists(goods_name):
_kwargs = func_manager.get_kwargs(goods_name)
return await func_manager.use(
**{
**_kwargs,
"_bot": bot,
"event": event,
"group_id": event.group_id,
"user_id": event.user_id,
"num": num,
"goods_name": goods_name,
}
)
except Exception as e:
logger.error(f"use 商品生效函数effect 发生错误 {type(e)}:{e}")
# try:
if func_manager.exists(goods_name):
_kwargs = func_manager.get_kwargs(goods_name)
return await func_manager.use(
func_manager.init_model(goods_name, bot, event, num),
**{
**_kwargs,
"_bot": bot,
"event": event,
"group_id": event.group_id,
"user_id": event.user_id,
"num": num,
"goods_name": goods_name,
},
)
# except Exception as e:
# logger.error(f"use 商品生效函数effect 发生错误 {type(e)}:{e}")
return None
@@ -112,10 +157,14 @@ def register_use(goods_name: str, func, **kwargs):
if func_manager.exists(goods_name):
raise ValueError("该商品使用函数已被注册!")
# 发送使用成功信息
if kwargs.get("send_success_msg") is None:
kwargs["send_success_msg"] = True
kwargs["_max_num_limit"] = (
kwargs.get("_max_num_limit") if kwargs.get("_max_num_limit") else 1
kwargs["send_success_msg"] = kwargs.get("send_success_msg", True)
kwargs["max_num_limit"] = kwargs.get("max_num_limit", 1)
func_manager.register_use(
goods_name,
**{
"func": func,
"model": create_model(f"{goods_name}_model", __base__=ShopParam, **kwargs),
"kwargs": kwargs,
},
)
func_manager.register_use(goods_name, **{"func": func, "kwargs": kwargs})
logger.info(f"register_use 成功注册商品:{goods_name} 的使用函数")
+23 -17
View File
@@ -82,15 +82,16 @@ async def _(bot: Bot, cmd: Tuple[str, ...] = Command(), arg: Message = CommandAr
if is_number(id_):
id_ = int(id_)
if cmd[:2] == "同意":
if await requests_manager.approve(bot, id_, "private"):
await friend_handle.send("同意好友请求成功..")
else:
await friend_handle.send("同意好友请求失败,可能是未找到此id的请求..")
flag = await requests_manager.approve(bot, id_, "private")
else:
if await requests_manager.refused(bot, id_, "private"):
await friend_handle.send("拒绝好友请求成功..")
else:
await friend_handle.send("拒绝好友请求失败,可能是未找到此id的请求..")
flag = await requests_manager.refused(bot, id_, "private")
if flag == 1:
await friend_handle.send(f"{cmd[:2]}好友请求失败,该请求已失效..")
requests_manager.delete_request(id_, "private")
elif flag == 2:
await friend_handle.send(f"{cmd[:2]}好友请求失败,未找到此id的请求..")
else:
await friend_handle.send(f"{cmd[:2]}好友请求成功!")
else:
await friend_handle.send("id必须为纯数字!")
@@ -99,12 +100,12 @@ async def _(bot: Bot, cmd: Tuple[str, ...] = Command(), arg: Message = CommandAr
async def _(bot: Bot, cmd: Tuple[str, ...] = Command(), arg: Message = CommandArg()):
cmd = cmd[0]
id_ = arg.extract_plain_text().strip()
flag = None
if is_number(id_):
id_ = int(id_)
if cmd[:2] == "同意":
rid = await requests_manager.approve(bot, id_, "group")
rid = requests_manager.get_group_id(id_)
if rid:
await friend_handle.send("同意群聊请求成功..")
if await GroupInfo.get_group_info(rid):
await GroupInfo.set_group_flag(rid, 1)
else:
@@ -116,15 +117,20 @@ async def _(bot: Bot, cmd: Tuple[str, ...] = Command(), arg: Message = CommandAr
group_info["member_count"],
1
)
flag = await requests_manager.approve(bot, id_, "group")
else:
await friend_handle.send("同意群聊请求失败,可能是未找到此id的请求..")
await group_handle.send("同意群聊请求失败,未找到此id的请求..")
else:
if await requests_manager.refused(bot, id_, "group"):
await friend_handle.send("拒绝群聊请求成功..")
else:
await friend_handle.send("拒绝群聊请求失败,可能是未找到此id的请求..")
flag = await requests_manager.refused(bot, id_, "group")
if flag == 1:
await group_handle.send(f"{cmd[:2]}群聊请求失败,该请求已失效..")
requests_manager.delete_request(id_, "group")
elif flag == 2:
await group_handle.send(f"{cmd[:2]}群聊请求失败,未找到此id的请求..")
else:
await group_handle.send(f"{cmd[:2]}群聊请求成功!")
else:
await friend_handle.send("id必须为纯数字!")
await group_handle.send("id必须为纯数字!")
@cls_request.handle()
@@ -144,4 +150,4 @@ async def _():
@clear_request.handle()
async def _():
requests_manager.clear()
await cls_request.send("已清空所有好友/群聊请求..")
await clear_request.send("已清空所有好友/群聊请求..")
+13 -13
View File
@@ -1,4 +1,11 @@
from nonebot.adapters.onebot.v11 import Bot, MessageEvent, GROUP, GroupMessageEvent, Message
from nonebot.adapters.onebot.v11 import (
Bot,
MessageEvent,
GROUP,
GroupMessageEvent,
Message,
ActionFailed,
)
from nonebot import on_command, on_regex
from nonebot.permission import SUPERUSER
from nonebot.typing import T_State
@@ -8,7 +15,6 @@ from utils.manager import group_manager, plugins2settings_manager
from models.group_info import GroupInfo
from services.log import logger
from configs.config import NICKNAME
from nonebot.adapters.onebot.v11.exception import ActionFailed
from nonebot.params import Command, CommandArg
from typing import Tuple
@@ -92,7 +98,7 @@ async def _(bot: Bot, event: MessageEvent, arg: Message = CommandArg()):
level = 0
if not msg:
await add_group_level.finish("用法:修改群权限 [group] [level]")
msg = msg.split(" ")
msg = msg.split()
if len(msg) < 2:
await add_group_level.finish("参数不完全..[group] [level]")
if is_number(msg[0]) and is_number(msg[1]):
@@ -136,10 +142,8 @@ async def _():
@manager_group_whitelist.handle()
async def _(bot: Bot, cmd: Tuple[str, ...] = Command(), arg: Message = CommandArg()):
cmd = cmd[0]
msg = arg.extract_plain_text().strip()
all_group = [
g["group_id"] for g in await bot.get_group_list()
]
msg = arg.extract_plain_text().strip().split()
all_group = [g["group_id"] for g in await bot.get_group_list()]
group_list = []
for group in msg:
if is_number(group) and int(group) in all_group:
@@ -151,9 +155,7 @@ async def _(bot: Bot, cmd: Tuple[str, ...] = Command(), arg: Message = CommandAr
else:
group_manager.delete_group_white_list(group)
group_list = [str(x) for x in group_list]
await manager_group_whitelist.send(
"已成功将 " + "\n".join(group_list) + " " + cmd
)
await manager_group_whitelist.send("已成功将 " + "\n".join(group_list) + " " + cmd)
else:
await manager_group_whitelist.send(f"添加失败,请检查{NICKNAME}是否已加入这些群聊或重复添加/删除群白单名")
@@ -199,6 +201,4 @@ async def _(bot: Bot, cmd: Tuple[str, ...] = Command(), arg: Message = CommandAr
else:
if await GroupInfo.get_group_info(group_id):
await GroupInfo.set_group_flag(group_id, 0)
await group_auth.send(
f'已为 {group_id} {cmd[:2]}群认证..'
)
await group_auth.send(f"已为 {group_id} {cmd[:2]}群认证..")
@@ -1,7 +1,7 @@
from nonebot import on_command
from nonebot.permission import SUPERUSER
from models.level_user import LevelUser
from nonebot.adapters.onebot.v11 import Bot, GroupMessageEvent, Message
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.message_builder import at
@@ -37,41 +37,49 @@ super_cmd = on_command(
@super_cmd.handle()
async def _(bot: Bot, event: GroupMessageEvent, cmd: Tuple[str, ...] = Command(), arg: Message = CommandArg()):
cmd = cmd[0]
group_id = -1
level = 0
async def _(
bot: Bot,
event: MessageEvent,
cmd: Tuple[str, ...] = Command(),
arg: Message = CommandArg(),
):
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
try:
args = arg.extract_plain_text().strip().split()
qq = get_message_at(event.json())
flag = -1
if not qq:
if len(args) > 2:
if is_number(args[0]) and is_number(args[1]) and is_number(args[2]):
if qq:
qq = qq[0]
if cmd[0][:2] == "添加" and args and is_number(args[0]):
level = int(args[0])
else:
if cmd[0][:2] == "添加":
if (
len(args) > 2
and is_number(args[0])
and is_number(args[1])
and is_number(args[2])
):
qq = int(args[0])
group_id = int(args[1])
level = int(args[2])
flag = 1
else:
await super_cmd.finish("所有参数必须是数字!", at_sender=True)
else:
await super_cmd.finish(
"权限参数不完全\n\t格式:添加/删除权限 [at] [level]"
"\n\t格式:添加/删除权限 [qq] [group_id] [level]",
at_sender=True,
)
else:
if not is_number(args[0]):
await super_cmd.finish("所有参数必须是数字!", at_sender=True)
level = int(args[0])
qq = qq[0]
group_id = event.group_id
flag = 2
if cmd[:2] == "添加":
if len(args) > 1 and is_number(args[0]) and is_number(args[1]):
qq = int(args[0])
group_id = int(args[1])
flag = 1
level = -1 if cmd[0][:2] == "删除" else level
if group_id == -1 or not level or not qq:
raise IndexError()
except IndexError:
await super_cmd.finish(__plugin_usage__)
try:
if cmd[0][:2] == "添加":
if await LevelUser.set_level(qq, group_id, level, 1):
result = "添加管理成功, 权限: " + str(level)
result = f"添加管理成功, 权限: {level}"
else:
result = "管理已存在, 更新权限: " + str(level)
result = f"管理已存在, 更新权限: {level}"
else:
if await LevelUser.delete_level(qq, event.group_id):
result = "删除管理成功!"
@@ -82,7 +90,10 @@ async def _(bot: Bot, event: GroupMessageEvent, cmd: Tuple[str, ...] = Command()
elif flag == 1:
await bot.send_group_msg(
group_id=group_id,
message=Message(f"{at(qq)}管理员修改了你的权限" f"\n--------\n你当前的权限等级:{level}"),
message=Message(
f"{at(qq)}管理员修改了你的权限"
f"\n--------\n你当前的权限等级:{level if level != -1 else 0}"
),
)
await super_cmd.send("修改成功")
except Exception as e:
@@ -45,6 +45,7 @@ async def _():
group_info["group_name"],
group_info["max_member_count"],
group_info["member_count"],
1
):
num += 1
logger.info(f"自动更新群组 {g} 信息成功")
+1
View File
@@ -12,6 +12,7 @@ driver.on_shutdown(disconnect)
nonebot.load_plugin("nonebot_plugin_apscheduler")
nonebot.load_plugins("basic_plugins")
nonebot.load_plugins("plugins")
nonebot.load_plugins("extensive_plugin")
# 最后加载权限控制
nonebot.load_plugins("basic_plugins/hooks")
+48 -6
View File
@@ -2,6 +2,7 @@ from typing import Optional, Any, Union
from pathlib import Path
from ruamel.yaml import YAML
from ruamel import yaml
from ruamel.yaml.scanner import ScannerError
class ConfigsManager:
@@ -11,14 +12,34 @@ class ConfigsManager:
def __init__(self, file: Path):
self._data: dict = {}
self._simple_data: dict = {}
self._admin_level_data = []
self._simple_file = Path() / "configs" / "config.yaml"
if file:
file.parent.mkdir(exist_ok=True, parents=True)
self.file = file
_yaml = YAML()
if file.exists():
_yaml = YAML()
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"**********************************************"
)
def add_plugin_config(
self,
@@ -71,17 +92,24 @@ class ConfigsManager:
"""
if module in self._data.keys():
del self._data[module]
self.save()
def set_config(self, module: str, key: str, value: str):
def set_config(self, module: str, key: str, value: Any , save_simple_data: bool = False):
"""
设置配置值
:param module: 模块名
:param key: 配置名称
:param value: 值
:param save_simple_data: 同时保存至config.yaml
"""
if module in self._data.keys():
if self._data[module].get(key) is not None:
if (
self._data[module].get(key) is not None
and self._data[module][key] != value
):
self._data[module][key]["value"] = value
self._simple_data[module][key] = value
self.save(save_simple_data = save_simple_data)
def set_help(self, module: str, key: str, help_: str):
"""
@@ -93,8 +121,9 @@ class ConfigsManager:
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: str):
def set_default_value(self, module: str, key: str, value: Any):
"""
设置配置默认值
:param module: 模块名
@@ -104,8 +133,11 @@ class ConfigsManager:
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]:
def get_config(
self, module: str, key: str, default: Optional[Any] = None
) -> Optional[Any]:
"""
获取指定配置值
:param module: 模块名
@@ -142,11 +174,21 @@ class ConfigsManager:
if key in self._data.keys():
return self._data[key]
def save(self, path: Union[str, Path] = None):
def save(self, path: Union[str, Path] = None, save_simple_data: bool = False):
"""
保存数据
:param path: 路径
:param save_simple_data: 同时保存至config.yaml
"""
if save_simple_data:
with open(self._simple_file, "w", encoding="utf8") as f:
yaml.dump(
self._simple_data,
f,
indent=2,
Dumper=yaml.RoundTripDumper,
allow_unicode=True,
)
path = path if path else self.file
with open(path, "w", encoding="utf8") as f:
yaml.dump(
+1450 -155
View File
File diff suppressed because it is too large Load Diff
+214
View File
@@ -0,0 +1,214 @@
from datetime import datetime, timedelta
from typing import List, Literal, Optional, Tuple, Union
from services.db_context import db
class ChatHistory(db.Model):
__tablename__ = "chat_history"
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)
@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
@classmethod
async def get_group_msg_rank(
cls,
gid: int,
limit: int = 10,
order: str = "DESC",
date_scope: Optional[Tuple[datetime, datetime]] = None,
) -> Optional[Tuple[int, int]]:
"""
说明:
获取排行数据
参数:
:param gid: 群号
:param limit: 获取数量
:param order: 排序类型,desc,des
:param date_scope: 日期范围
"""
sql = f"SELECT user_qq, COUNT(id) as sum FROM public.chat_history WHERE 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))
@classmethod
async def get_group_first_msg_datetime(cls, gid: int) -> Optional[datetime]:
"""
说明:
获取群第一条记录消息时间
参数:
:param gid:
"""
if (
msg := await cls.query.where(cls.group_id == gid)
.order_by(cls.create_time)
.gino.first()
):
return msg.create_time
return None
@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(
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,
):
"""
说明:
获取消息查询query
参数:
:param uid: 用户qq
:param gid: 群号
:param type_: 类型,私聊或群聊
: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)
if msg_type == "private":
query = query.where(cls.group_id == None)
elif msg_type == "group":
query = query.where(cls.group_id != None)
else:
query = query.where(cls.group_id == gid)
if uid:
query = query.where(cls.user_qq == uid)
if days:
if isinstance(days, int):
query = query.where(
cls.create_time >= 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
+1 -2
View File
@@ -114,8 +114,7 @@ class GoodsInfo(db.Model):
参数:
:param goods_name: 商品名称
"""
query = await cls.query.where(cls.goods_name == goods_name).gino.first()
return query
return await cls.query.where(cls.goods_name == goods_name).gino.first()
@classmethod
async def get_all_goods(cls) -> List["GoodsInfo"]:
+15 -10
View File
@@ -1,3 +1,5 @@
from asyncpg import UniqueViolationError
from services.db_context import db
@@ -44,16 +46,19 @@ class LevelUser(db.Model):
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:
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()
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
@classmethod
+5 -5
View File
@@ -6,11 +6,10 @@ from nonebot.adapters.onebot.v11 import (
MessageEvent,
)
from nonebot.rule import to_me
from nonebot.params import CommandArg
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
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
@@ -38,12 +37,12 @@ Config.add_plugin_config(
"alapi", "ALAPI_TOKEN", None, help_="在 https://admin.alapi.cn/user/login 登录后获取token"
)
ai = on_message(rule=to_me(), priority=8)
ai = on_message(rule=to_me(), priority=998)
@ai.handle()
async def _(bot: Bot, event: MessageEvent, arg: Message = CommandArg()):
msg = arg.extract_plain_text().strip()
async def _(bot: Bot, event: MessageEvent):
msg = get_message_text(event.json())
img = get_message_img(event.json())
if "CQ:xml" in str(event.get_message()):
return
@@ -77,6 +76,7 @@ async def _(bot: Bot, event: MessageEvent, arg: Message = CommandArg()):
f"问题:{msg} ---- 回答:{result}"
)
if result:
result = str(result)
for t in Config.get_config("ai", "TEXT_FILTER"):
result = result.replace(t, "*")
await ai.finish(Message(result))
+34 -30
View File
@@ -57,7 +57,7 @@ async def get_chat_result(text: str, img_url: str, user_id: int, nickname: str)
if random.random() < 0.2:
if nickname.find("大人") == -1:
nickname += "大~人~"
rst = rst.replace("小主人", nickname).replace("小朋友", nickname)
rst = str(rst).replace("小主人", nickname).replace("小朋友", nickname)
ai_message_manager.add_result(user_id, rst)
return rst
@@ -126,35 +126,39 @@ async def xie_ai(text: str) -> str:
"""
res = await AsyncHttpx.get(f"http://api.qingyunke.com/api.php?key=free&appid=0&msg={text}")
content = ""
data = json.loads(res.text)
if data["result"] == 0:
content = data["content"]
if "菲菲" in content:
content = content.replace("菲菲", NICKNAME)
if "艳儿" in content:
content = content.replace("艳儿", NICKNAME)
if "公众号" in content:
content = ""
if "{br}" in content:
content = content.replace("{br}", "\n")
if "提示" in content:
content = content[: content.find("提示")]
if "淘宝" in content or "taobao.com" in content:
return ""
while True:
r = re.search("{face:(.*)}", content)
if r:
id_ = r.group(1)
content = content.replace(
"{" + f"face:{id_}" + "}", str(face(int(id_)))
)
else:
break
return (
content
if not content and not Config.get_config("ai", "ALAPI_AI_CHECK")
else await check_text(content)
)
try:
data = json.loads(res.text)
if data["result"] == 0:
content = data["content"]
if "菲菲" in content:
content = content.replace("菲菲", NICKNAME)
if "艳儿" in content:
content = content.replace("艳儿", NICKNAME)
if "公众号" in content:
content = ""
if "{br}" in content:
content = content.replace("{br}", "\n")
if "提示" in content:
content = content[: content.find("提示")]
if "淘宝" in content or "taobao.com" in content:
return ""
while True:
r = re.search("{face:(.*)}", content)
if r:
id_ = r.group(1)
content = content.replace(
"{" + f"face:{id_}" + "}", str(face(int(id_)))
)
else:
break
return (
content
if not content and not Config.get_config("ai", "ALAPI_AI_CHECK")
else await check_text(content)
)
except Exception as e:
logger.error(f"Ai xie_ai 发生错误 {type(e)}:{e}")
return ""
def hello() -> str:
+2 -25
View File
@@ -1,7 +1,3 @@
from nonebot.adapters.onebot.v11 import MessageSegment
from utils.image_utils import BuildImage
from utils.message_builder import image
from configs.path_config import IMAGE_PATH
from typing import Optional, Tuple, Union
from configs.config import Config
from utils.http_utils import AsyncHttpx
@@ -23,27 +19,8 @@ async def get_data(url: str, params: Optional[dict] = None) -> Tuple[Union[dict,
return "没有搜索到...", 997
return data, 200
else:
if data["code"] == 101:
return "缺失ALAPI TOKEN,请在配置文件中填写!", 999
return f'发生了错误...code:{data["code"]}', 999
except TimeoutError:
return "超时了....", 998
def gen_wbtop_pic(data: dict) -> MessageSegment:
"""
生成微博热搜图片
:param data: 微博热搜数据
"""
bk = BuildImage(700, 32 * 50 + 280, 700, 32, color="#797979")
wbtop_bk = BuildImage(700, 280, background=f"{IMAGE_PATH}/other/webtop.png")
bk.paste(wbtop_bk)
text_bk = BuildImage(700, 32 * 50, 700, 32, color="#797979")
for i, data in enumerate(data):
title = f"{i+1}. {data['hot_word']}"
hot = data["hot_word_num"]
img = BuildImage(700, 30, font_size=20)
w, h = img.getsize(title)
img.text((10, int((30 - h) / 2)), title)
img.text((580, int((30 - h) / 2)), hot)
text_bk.paste(img)
bk.paste(text_bk, (0, 280))
return image(b64=bk.pic2bs4())
+22 -12
View File
@@ -17,7 +17,7 @@ from utils.utils import is_number, scheduler, get_bot
from typing import Optional
from services.log import logger
from nonebot import Driver
from nonebot.params import CommandArg, ArgPlainText
from nonebot.params import CommandArg, ArgStr
import nonebot
__zx_plugin_name__ = "B站订阅"
@@ -38,7 +38,7 @@ usage:
__plugin_des__ = "非常便利的B站订阅通知"
__plugin_cmd__ = ["添加订阅 [主播/UP/番剧] [id/链接/番名]", "删除订阅 [id]", "查看订阅"]
__plugin_version__ = 0.1
__plugin_author__ = "HibiKier"
__plugin_author__ = "HibiKier & NumberSir"
__plugin_settings__ = {
"level": 5,
"default_status": True,
@@ -112,14 +112,22 @@ async def _(event: MessageEvent, state: T_State, arg: Message = CommandArg()):
state["id"] = int(id_)
@add_sub.got("sub_type")
@add_sub.got("sub_user")
@add_sub.got("id")
async def _(event: MessageEvent, state: T_State, id_: str = ArgPlainText("id")):
season_data = state["season_data"]
if not is_number(id_) or int(id_) < 1 or int(id_) > len(season_data):
await add_sub.reject_arg("id", "Id必须为数字且在范围内!请重新输入...")
id_ = season_data[int(id_) - 1]["media_id"]
sub_type = state["sub_type"]
sub_user = state["sub_user"]
async def _(
event: MessageEvent,
state: T_State,
id_: str = ArgStr("id"),
sub_type: str = ArgStr("sub_type"),
sub_user: str = ArgStr("sub_user"),
):
if sub_type in ["season", "动漫", "番剧"] and state.get("season_data"):
season_data = state["season_data"]
if not is_number(id_) or int(id_) < 1 or int(id_) > len(season_data):
await add_sub.reject_arg("id", "Id必须为数字且在范围内!请重新输入...")
id_ = season_data[int(id_) - 1]["media_id"]
id_ = int(id_)
if sub_type in ["主播", "直播"]:
await add_sub.send(await add_live_sub(id_, sub_user))
elif sub_type.lower() in ["up", "用户"]:
@@ -203,7 +211,7 @@ async def _():
await sub_manager.reload_sub_data()
sub = await sub_manager.random_sub_data()
if sub:
logger.info(f"Bilibili订阅开始检测:{sub.sub_id}")
logger.debug(f"Bilibili订阅开始检测:{sub.sub_id}")
rst = await get_sub_status(sub.sub_id, sub.sub_type)
await send_sub_msg(rst, sub, bot)
if sub.sub_type == "live":
@@ -220,12 +228,14 @@ async def send_sub_msg(rst: str, sub: BilibiliSub, bot: Bot):
:param sub: BilibiliSub
:param bot: Bot
"""
temp_group = []
if rst:
for x in sub.sub_users.split(",")[:-1]:
try:
if ":" in x:
if ":" in x and x.split(":")[1] not in temp_group:
temp_group.append(x.split(":")[1])
await bot.send_group_msg(
group_id=int(x.split(":")[1]), message=Message(rst)
group_id=int(x.split(":")[1]), message=Message(rst)
)
else:
await bot.send_private_msg(user_id=int(x), message=Message(rst))
+59 -48
View File
@@ -1,12 +1,13 @@
from bilibili_api.exceptions.ResponseCodeException import ResponseCodeException
from bilireq.exceptions import ResponseCodeError
from utils.manager import resources_manager
from asyncio.exceptions import TimeoutError
from .model import BilibiliSub
from bilibili_api.live import LiveRoom
from bilibili_api import bangumi
from bilireq.live import get_room_info_by_id
from .utils import get_meta
from utils.message_builder import image
from bilibili_api.user import User
from bilibili_api import user
from bilireq.user import get_user_info
from bilireq import dynamic
from .utils import get_videos
from typing import Optional
from configs.path_config import IMAGE_PATH
from datetime import datetime
@@ -36,9 +37,9 @@ async def add_live_sub(live_id: int, sub_user: str) -> str:
try:
async with db.transaction():
try:
live = LiveRoom(live_id)
live_info = (await live.get_room_info())["room_info"]
except ResponseCodeException:
"""bilibili_api.live库的LiveRoom类中get_room_info改为bilireq.live库的get_room_info_by_id方法"""
live_info = await get_room_info_by_id(live_id)
except ResponseCodeError:
return f"未找到房间号Id:{live_id} 的信息,请检查Id是否正确"
uid = live_info["uid"]
room_id = live_info["room_id"]
@@ -78,16 +79,18 @@ async def add_up_sub(uid: int, sub_user: str) -> str:
try:
async with db.transaction():
try:
u = user.User(uid)
user_info = await u.get_user_info()
except ResponseCodeException:
"""bilibili_api.user库中User类的get_user_info改为bilireq.user库的get_user_info方法"""
user_info = await get_user_info(uid)
except ResponseCodeError:
return f"未找到UpId:{uid} 的信息,请检查Id是否正确"
uname = user_info["name"]
dynamic_info = await u.get_dynamics(0)
"""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"]
video_info = await u.get_videos()
"""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"]
@@ -117,8 +120,9 @@ async def add_season_sub(media_id: int, sub_user: str) -> str:
try:
async with db.transaction():
try:
season_info = await bangumi.get_meta(media_id)
except ResponseCodeException:
"""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"]
@@ -200,7 +204,7 @@ async def get_sub_status(id_: int, sub_type: str) -> Optional[str]:
return await _get_up_status(id_)
elif sub_type == "season":
return await _get_season_status(id_)
except ResponseCodeException:
except ResponseCodeError:
return f"Id:{id_} 获取信息失败...请检查订阅Id是否存在或稍后再试..."
# except Exception as e:
# logger.error(f"获取订阅状态发生预料之外的错误 id_:{id_} {type(e)}:{e}")
@@ -212,12 +216,12 @@ async def _get_live_status(id_: int) -> Optional[str]:
获取直播订阅状态
:param id_: 直播间 id
"""
live = LiveRoom(id_)
live_info = (await live.get_room_info())["room_info"]
"""bilibili_api.live库的LiveRoom类中get_room_info改为bilireq.live库的get_room_info_by_id方法"""
live_info = await get_room_info_by_id(id_)
title = live_info["title"]
room_id = live_info["room_id"]
live_status = live_info["live_status"]
cover = live_info["cover"]
cover = live_info["user_cover"]
sub = await BilibiliSub.get_sub(id_)
if sub.live_status != live_status:
await BilibiliSub.update_sub_info(id_, live_status=live_status)
@@ -238,15 +242,16 @@ async def _get_up_status(id_: int) -> Optional[str]:
:return:
"""
_user = await BilibiliSub.get_sub(id_)
u = user.User(_user.uid)
user_info = await u.get_user_info()
"""bilibili_api.user库中User类的get_user_info改为bilireq.user库的get_user_info方法"""
user_info = await get_user_info(_user.uid)
uname = user_info["name"]
video_info = await u.get_videos()
"""bilibili_api.user库中User类的get_videos改为bilireq.user库的get_videos方法"""
video_info = await get_videos(id_)
latest_video_created = 0
video = None
if _user.uname != uname:
await BilibiliSub.update_sub_info(id_, uname=uname)
dynamic_img, dynamic_upload_time = await get_user_dynamic(u, _user)
dynamic_img, dynamic_upload_time = await get_user_dynamic(id_, _user)
if video_info["list"].get("vlist"):
video = video_info["list"]["vlist"][0]
latest_video_created = video["created"]
@@ -280,7 +285,8 @@ async def _get_season_status(id_) -> Optional[str]:
获取 番剧 更新状态
:param id_: 番剧 id
"""
season_info = await bangumi.get_meta(id_)
"""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
new_ep = season_info["media"]["new_ep"]["index"]
@@ -297,42 +303,47 @@ async def _get_season_status(id_) -> Optional[str]:
async def get_user_dynamic(
u: User, local_user: BilibiliSub
uid: int, local_user: BilibiliSub
) -> "Optional[MessageSegment], int":
"""
获取用户动态
:param u: 用户类
:param uid: 用户uid
:param local_user: 数据库存储的用户数据
:return: 最新动态截图与时间
"""
dynamic_info = await u.get_dynamics(0)
"""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:
dynamic_upload_time = dynamic_info["cards"][0]["desc"]["timestamp"]
if local_user.dynamic_upload_time < dynamic_upload_time:
page = await browser.new_page()
await page.goto(
f"https://space.bilibili.com/{local_user.uid}/dynamic",
wait_until="networkidle",
timeout=10000,
)
await page.set_viewport_size({"width": 2560, "height": 1080})
# 删除置顶
await page.evaluate(
try:
await page.goto(
f"https://space.bilibili.com/{local_user.uid}/dynamic",
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();
}
"""
xs = document.getElementsByClassName('first-card-with-title');
for (x of xs) {
x.remove();
}
"""
)
card = await page.query_selector(".card")
# 截图并保存
await card.screenshot(
path=dynamic_path / f"{local_user.sub_id}_{dynamic_upload_time}.jpg",
timeout=100000,
)
await page.close()
)
card = page.locator(".bili-dyn-list__item").first
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 page.close()
return (
image(
f"{local_user.sub_id}_{dynamic_upload_time}.jpg",
+48 -4
View File
@@ -1,7 +1,8 @@
from utils.image_utils import BuildImage
from configs.path_config import IMAGE_PATH
from utils.http_utils import AsyncHttpx
from bilibili_api import user
# from bilibili_api import user
from bilireq.user import get_user_info
from io import BytesIO
@@ -28,8 +29,8 @@ async def create_live_des_image(uid: int, title: str, cover: str, tags: str, des
:param des: 直播简介
:return:
"""
u = user.User(uid)
user_info = await u.get_user_info()
user_info = await get_user_info(uid)
name = user_info["name"]
sex = user_info["sex"]
face = user_info["face"]
@@ -37,7 +38,6 @@ async def create_live_des_image(uid: int, title: str, cover: str, tags: str, des
ava = BuildImage(100, 100, background=BytesIO(await get_pic(face)))
ava.circle()
cover = BuildImage(470, 265, background=BytesIO(await get_pic(cover)))
print()
def _create_live_des_image(
@@ -68,3 +68,47 @@ def _create_live_des_image(
border_img = BuildImage(1772, 2657, background=border)
bk = BuildImage(1772, 2657, font_size=30)
bk.paste(cover, (0, 100), center_type="by_width")
async def get_meta(media_id: int, auth=None, reqtype="both", **kwargs):
"""
根据番剧 ID 获取番剧元数据信息,
作为bilibili_api和bilireq的替代品。
如果bilireq.bangumi更新了,可以转为调用bilireq.bangumi的get_meta方法,两者完全一致。
"""
from bilireq.utils import get
BASE_URL = "https://api.bilibili.com"
url = f"{BASE_URL}/pgc/review/user"
params = {"media_id": media_id}
raw_json = await get(url, raw=True, params=params, auth=auth, reqtype=reqtype, **kwargs)
return raw_json['result']
async def get_videos(
uid: int, tid: int = 0, pn: int = 1, keyword: str = "", order: str = "pubdate",
*, auth=None, reqtype="both", **kwargs
):
"""
获取用户投该视频信息
作为bilibili_api和bilireq的替代品。
如果bilireq.user更新了,可以转为调用bilireq.user的get_videos方法,两者完全一致。
:param uid: 用户 UID
:param tid: 分区 ID
:param pn: 页码
:param keyword: 搜索关键词
:param order: 排序方式,可以为 “pubdate(上传日期从新到旧), stow(收藏从多到少), click(播放量从多到少)”
"""
from bilireq.utils import get
BASE_URL = "https://api.bilibili.com"
url = f"{BASE_URL}/x/space/arc/search"
params = {
"mid": uid,
"ps": 30,
"tid": tid,
"pn": pn,
"keyword": keyword,
"order": order
}
return await get(url, params=params, auth=auth, reqtype=reqtype, **kwargs)
+253
View File
@@ -0,0 +1,253 @@
from nonebot.adapters.onebot.v11 import (
Event,
MessageEvent,
GroupMessageEvent,
Message,
Bot,
)
from nonebot.matcher import Matcher
from nonebot.message import run_preprocessor
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
__zx_plugin_name__ = "敏感词检测"
__plugin_usage__ = """
usage:
注意你的发言!
指令:
惩罚机制
""".strip()
__plugin_superuser_usage__ = """
usage:
查看和设置惩罚
Regex:^记录名单(u:\d*)?(g:\d*)?(d[=><]\d*-\d{1,2}-\d{1,2})?$
设置惩罚id需要通过 '记录名单u:xxxxxxxx' 获取
指令:
记录名单
设置惩罚 [user_id] [id] [punish_level]
示例:记录名单
示例:记录名单u:12345678
示例:设置惩罚 12345678 1 4
""".strip()
__plugin_des__ = "请注意你的发言!!"
__plugin_type__ = ("其他",)
__plugin_version__ = 0.1
__plugin_author__ = "HibiKier"
__plugin_cmd__ = ["惩罚机制", "记录名单 [_superuser]", "设置惩罚 [_superuser]"]
__plugin_settings__ = {
"cmd": ["敏感词检测"],
}
Config.add_plugin_config(
"black_word", "CYCLE_DAYS", 30, name="敏感词检测与惩罚", help_="黑名单词汇记录周期", default_value=30
)
Config.add_plugin_config(
"black_word",
"TOLERATE_COUNT",
[5, 1, 1, 1, 1],
help_="各个级别惩罚的容忍次数,依次为:1, 2, 3, 4, 5",
default_value=[5, 1, 1, 1, 1],
)
Config.add_plugin_config(
"black_word", "AUTO_PUNISH", True, help_="是否启动自动惩罚机制", default_value=True
)
# Config.add_plugin_config(
# "black_word", "IGNORE_GROUP", [], help_="退出群聊惩罚中忽略的群聊,即不会退出的群聊", default_value=[]
# )
Config.add_plugin_config(
"black_word",
"BAN_4_DURATION",
360,
help_="Union[int, List[int, int]]Ban时长(分钟),四级惩罚,可以为指定数字或指定列表区间(随机),例如 [30, 360]",
default_value=360,
)
Config.add_plugin_config(
"black_word",
"BAN_3_DURATION",
7,
help_="Union[int, List[int, int]]Ban时长(天),三级惩罚,可以为指定数字或指定列表区间(随机),例如 [7, 30]",
default_value=360,
)
Config.add_plugin_config(
"black_word",
"WARNING_RESULT",
f"请注意对{NICKNAME}的发言内容",
help_="口头警告内容",
default_value=f"请注意对{NICKNAME}的发言内容",
)
Config.add_plugin_config(
"black_word",
"AUTO_ADD_PUNISH_LEVEL",
True,
help_="自动提级机制,当周期内处罚次数大于某一特定值就提升惩罚等级",
default_value=True,
)
Config.add_plugin_config(
"black_word",
"ADD_PUNISH_LEVEL_TO_COUNT",
3,
help_="在CYCLE_DAYS周期内触发指定惩罚次数后提升惩罚等级",
default_value=3,
)
Config.add_plugin_config(
"black_word",
"ALAPI_CHECK_FLAG",
False,
help_="当未检测到已收录的敏感词时,开启ALAPI文本检测并将疑似文本发送给超级用户",
default_value=False,
)
Config.add_plugin_config(
"black_word",
"CONTAIN_BLACK_STOP_PROPAGATION",
True,
help_="当文本包含任意敏感词时,停止向下级插件传递,即不触发ai",
default_value=True,
)
message_matcher = on_message(priority=1, block=False)
set_punish = on_command("设置惩罚", priority=1, permission=SUPERUSER, block=True)
show_black = on_regex(
r"^记录名单(u:\d*)?(g:\d*)?(d[=><]\d*-\d{1,2}-\d{1,2})?$",
priority=1,
permission=SUPERUSER,
block=True,
)
show_punish = on_command("惩罚机制", aliases={"敏感词检测"}, priority=1, block=True)
# 黑名单词汇检测
@run_preprocessor
async def _(
bot: Bot,
matcher: Matcher,
event: Event,
):
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("原神绑定")
):
# 屏蔽群权限-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()
async def _(bot: Bot, reg_group: Tuple[Any, ...] = RegexGroup()):
user_id, group_id, date = reg_group
date_type = "="
if date:
date_type = date[1]
date = date[2:]
try:
date = datetime.strptime(date, "%Y-%m-%d")
except ValueError:
await show_black.finish("日期格式错误,需要:年-月-日")
pic = await show_black_text_image(
bot,
int(user_id.split(":")[1]) if user_id else None,
int(group_id.split(":")[1]) if group_id else None,
date,
date_type,
)
await show_black.send(image(b64=pic.pic2bs4()))
@show_punish.handle()
async def _():
text = f"""
** 惩罚机制 **
惩罚前包含容忍机制,在指定周期内会容忍偶尔少次数的敏感词只会进行警告提醒
多次触发同级惩罚会使惩罚等级提高,即惩罚自动提级机制
目前公开的惩罚等级:
1级:永久ban
2级:删除好友
3级:ban指定/随机天数
4级:ban指定/随机时长
5级:警告
备注:
该功能为测试阶段,如果你有被误封情况,请联系管理员,会从数据库中提取出你的数据进行审核后判断
目前该功能暂不完善,部分情况会由管理员鉴定,请注意对真寻的发言
关于敏感词:
记住不要骂{NICKNAME}就对了!
""".strip()
max_width = 0
for m in text.split("\n"):
max_width = len(m) * 20 if len(m) * 20 > max_width else max_width
max_height = len(text.split("\n")) * 24
A = BuildImage(
max_width, max_height, font="CJGaoDeGuo.otf", font_size=24, color="#E3DBD1"
)
A.text((10, 10), text)
await show_punish.send(image(b64=A.pic2bs4()))
@set_punish.handle()
async def _(event: MessageEvent, arg: Message = CommandArg()):
msg = arg.extract_plain_text().strip().split()
if (
len(msg) < 3
or not is_number(msg[0])
or not is_number(msg[1])
or not is_number(msg[2])
):
await set_punish.finish("参数错误,请查看帮助...", at_sender=True)
uid = int(msg[0])
id_ = int(msg[1])
punish_level = int(msg[2])
rst = await set_user_punish(uid, id_, punish_level)
await set_punish.send(rst)
logger.info(
f"USER {event.user_id} 设置惩罚 uid:{uid} id_:{id_} punish_level:{punish_level} --> {rst}"
)
+118
View File
@@ -0,0 +1,118 @@
from nonebot.adapters.onebot.v11 import Bot
from utils.image_utils import BuildImage, text2image
from services.log import logger
from typing import Optional
from datetime import datetime
from .model import BlackWord
from .utils import _get_punish, Config
async def show_black_text_image(
bot: Bot,
user: Optional[int],
group_id: Optional[int],
date: Optional[datetime],
data_type: str = "=",
) -> BuildImage:
"""
展示记录名单
:param bot: bot
:param user: 用户qq
:param group_id: 群聊
:param date: 日期
:param data_type: 日期搜索类型
:return:
"""
data = await BlackWord.get_black_data(user, group_id, date, data_type)
A = BuildImage(0, 0, color="#f9f6f2", font_size=20)
image_list = []
friend_str = await bot.get_friend_list()
id_str = ""
uname_str = ""
uid_str = ""
gid_str = ""
plant_text_str = ""
black_word_str = ""
punish_str = ""
punish_level_str = ""
create_time_str = ""
for i, x in enumerate(data):
try:
if x.group_id:
user_name = (
await bot.get_group_member_info(
group_id=x.group_id, user_id=x.user_qq
)
)["card"]
else:
user_name = [
u["nickname"] for u in friend_str if u["user_id"] == x.user_qq
][0]
except Exception as e:
logger.warning(
f"show_black_text_image 获取 USER {x.user_qq} user_name 失败 {type(e)}:{e}"
)
user_name = x.user_qq
id_str += f"{i}\n"
uname_str += f"{user_name}\n"
uid_str += f"{x.user_qq}\n"
gid_str += f"{x.group_id}\n"
plant_text = " ".join(x.plant_text.split("\n"))
if A.getsize(plant_text)[0] > 200:
plant_text = plant_text[:20] + "..."
plant_text_str += f"{plant_text}\n"
black_word_str += f"{x.black_word}\n"
punish_str += f"{x.punish}\n"
punish_level_str += f"{x.punish_level}\n"
create_time_str += f"{x.create_time.replace(microsecond=0)}\n"
_tmp_img = BuildImage(0, 0, font_size=35, font="CJGaoDeGuo.otf")
for s, type_ in [
(id_str, "Id"),
(uname_str, "昵称"),
(uid_str, "UID"),
(gid_str, "GID"),
(plant_text_str, "文本"),
(black_word_str, "检测"),
(punish_str, "惩罚"),
(punish_level_str, "等级"),
(create_time_str, "记录日期"),
]:
img = await text2image(s, color="#f9f6f2", _add_height=2.1)
w = _tmp_img.getsize(type_)[0] if _tmp_img.getsize(type_)[0] > img.w else img.w
A = BuildImage(w + 11, img.h + 50, color="#f9f6f2", font_size=35, font="CJGaoDeGuo.otf")
await A.atext((10, 10), type_)
await A.apaste(img, (0, 50))
image_list.append(A)
horizontal_line = []
w, h = 0, 0
for img in image_list:
w += img.w + 20
h = img.h if img.h > h else h
horizontal_line.append(img.w)
A = BuildImage(w, h, color="#f9f6f2")
current_w = 0
for img in image_list:
await A.apaste(img, (current_w, 0))
current_w += img.w + 20
return A
async def set_user_punish(user_id: int, id_: int, punish_level: int) -> str:
"""
设置惩罚
:param user_id: 用户id
:param id_: 记录下标
:param punish_level: 惩罚等级
"""
result = await _get_punish(punish_level, user_id)
punish = {
1: "永久ban",
2: "删除好友",
3: f"ban {result} 天",
4: f"ban {result} 分钟",
5: "口头警告"
}
if await BlackWord.set_user_punish(user_id, punish[punish_level], id_=id_):
return f"已对 USER {user_id} 进行 {punish[punish_level]} 处罚。"
else:
return "操作失败,可能未找到用户,id或敏感词"
+148
View File
@@ -0,0 +1,148 @@
from services.db_context import db
from typing import Optional, List
from datetime import datetime, timedelta
class BlackWord(db.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)
@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(),
)
@classmethod
async def set_user_punish(
cls,
user_qq: int,
punish: str,
black_word: Optional[str] = None,
id_: Optional[int] = None,
) -> bool:
"""
说明:
设置处罚
参数:
:param user_qq: 用户id
:param punish: 处罚
:param black_word: 黑名单词汇
:param id_: 记录下标
"""
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()
elif id_:
user_list = await query.gino.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()
return True
@classmethod
async def get_user_count(
cls, user_qq: int, days: int = 7, punish_level: Optional[int] = None
) -> int:
"""
说明:
获取用户规定周期内的犯事次数
参数:
:param user_qq: 用户qq
: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))
)
if punish_level is not None:
query = query.where(cls.punish_level == punish_level)
return (await query.gino.first())[0]
@classmethod
async def get_user_punish_level(cls, user_qq: int, days: int = 7) -> Optional[int]:
"""
说明:
获取用户最近一次的惩罚记录等级
参数:
:param user_qq: 用户qq
: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()
):
return query.punish_level
return None
@classmethod
async def get_black_data(
cls,
user_qq: Optional[int],
group_id: Optional[int],
date: Optional[datetime],
date_type: str = "=",
) -> List["BlackWord"]:
"""
说明:
通过指定条件查询数据
参数:
:param user_qq: 用户qq
:param group_id: 群号
:param date: 日期
:param date_type: 日期查询类型
"""
query = cls.query
if user_qq:
query = query.where(cls.user_qq == user_qq)
if group_id:
query = query.where(cls.group_id == group_id)
if date:
if date_type == "=":
query = query.where(cls.create_time == date)
elif date_type == ">":
query = query.where(cls.create_time > date)
elif date_type == "<":
query = query.where(cls.create_time < date)
return await query.gino.all()
+322
View File
@@ -0,0 +1,322 @@
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
try:
import ujson as json
except ModuleNotFoundError:
import json
class BlackWordManager:
"""
敏感词管理( 拒绝恶意
"""
def __init__(self, word_file: Path, py_file: Path):
self._word_list = {
"1": [],
"2": [],
"3": [],
"4": ["sb", "nmsl", "mdzz", "2b", "jb", "操", "废物", "憨憨", "cnm", "rnm"],
"5": [],
}
self._py_list = {
"1": [],
"2": [],
"3": [],
"4": [
"shabi",
"wocaonima",
"sima",
"sabi",
"zhizhang",
"naocan",
"caonima",
"rinima",
"simadongxi",
"simawanyi",
"hanbi",
"hanpi",
"laji",
"fw"
],
"5": [],
}
word_file.parent.mkdir(parents=True, exist_ok=True)
if word_file.exists():
# 清空默认配置
with open(word_file, "r", encoding="utf8") as f:
self._word_list = json.load(f)
else:
with open(word_file, "w", encoding="utf8") as f:
json.dump(
self._word_list,
f,
ensure_ascii=False,
indent=4,
)
if py_file.exists():
# 清空默认配置
with open(py_file, "r", encoding="utf8") as f:
self._py_list = json.load(f)
else:
with open(py_file, "w", encoding="utf8") as f:
json.dump(
self._py_list,
f,
ensure_ascii=False,
indent=4,
)
async def check(
self, user_id: int, group_id: Optional[int], message: str
) -> Optional[Union[str, bool]]:
"""
检查是否包含黑名单词汇
:param user_id: 用户id
:param group_id: 群号
:param message: 消息
"""
if data := self._check(message):
if data[0]:
await _add_user_black_word(
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):
await send_msg(
0, None, f"USER {user_id} GROUP {group_id} ALAPI 疑似检测:{message}"
)
return False
def _check(self, message: str) -> Tuple[Optional[str], int]:
"""
检测文本是否违规
:param message: 检测消息
"""
# 移除空格
message = message.replace(" ", "")
py_msg = cn2py(message).lower()
# 完全匹配
for x in [self._word_list, self._py_list]:
for level in x:
if message in x[level] or py_msg in x[level]:
return message if message in x[level] else py_msg, level
# 模糊匹配
for x in [self._word_list, self._py_list]:
for level in x:
for m in x[level]:
if m in message or m in py_msg:
return m, -1
return None, 0
async def _add_user_black_word(
user_id: int,
group_id: Optional[int],
black_word: str,
message: str,
punish_level: int,
):
"""
添加敏感词数据
:param user_id: 用户id
:param group_id: 群号
:param black_word: 触发的黑名单词汇
:param message: 原始文本
:param punish_level: 惩罚等级
"""
cycle_days = Config.get_config("black_word", "CYCLE_DAYS") or 7
user_count = await BlackWord.get_user_count(user_id, cycle_days, punish_level)
# 周期内超过次数直接提升惩罚
if Config.get_config(
"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
)
logger.info(
f"已将 USER {user_id} GROUP {group_id} 添加至黑名单词汇记录 Black_word:{black_word} Plant_text:{message}"
)
# 自动惩罚
if Config.get_config("black_word", "AUTO_PUNISH") and punish_level != -1:
await _punish_handle(user_id, group_id, punish_level, black_word)
async def _punish_handle(
user_id: int, group_id: Optional[int], punish_level: int, black_word: str
):
"""
惩罚措施,级别越低惩罚越严
:param user_id: 用户id
:param group_id: 群号
:param black_word: 触发的黑名单词汇
"""
logger.info(f"BlackWord USER {user_id} 触发 {punish_level} 级惩罚...")
# 周期天数
cycle_days = Config.get_config("black_word", "CYCLE_DAYS") or 7
# 用户周期内触发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
# 容忍次数:List[int]
tolerate_count = Config.get_config("black_word", "TOLERATE_COUNT")
if not tolerate_count or len(tolerate_count) < 5:
tolerate_count = [5, 2, 2, 2, 2]
if punish_level == 1 and user_count > tolerate_count[punish_level - 1]:
# 永久ban
await _get_punish(1, user_id, group_id)
await BlackWord.set_user_punish(user_id, "永久ban 删除好友", black_word)
elif punish_level == 2 and user_count > tolerate_count[punish_level - 1]:
# 删除好友
await _get_punish(2, user_id, group_id)
await BlackWord.set_user_punish(user_id, "删除好友", black_word)
elif punish_level == 3 and user_count > tolerate_count[punish_level - 1]:
# 永久ban
ban_day = await _get_punish(3, user_id, group_id)
await BlackWord.set_user_punish(user_id, f"ban {ban_day} 天", black_word)
elif punish_level == 4 and user_count > tolerate_count[punish_level - 1]:
# ban指定时长
ban_time = await _get_punish(4, user_id, group_id)
await BlackWord.set_user_punish(user_id, f"ban {ban_time} 分钟", black_word)
elif punish_level == 5 and user_count > tolerate_count[punish_level - 1]:
# 口头警告
warning_result = await _get_punish(5, user_id, group_id)
await BlackWord.set_user_punish(user_id, f"口头警告:{warning_result}", black_word)
else:
await BlackWord.set_user_punish(user_id, f"提示!", black_word)
await send_msg(
user_id,
group_id,
f"BlackWordChecker:该条发言已被记录,目前你在{cycle_days}天内的发表{punish_level}级"
f"言论记录次数为:{user_count}次,请注意你的发言\n"
f"* 如果你不清楚惩罚机制,请发送“惩罚机制” *",
)
async def _get_punish(
id_: int, user_id: int, group_id: Optional[int] = None
) -> Optional[Union[int, str]]:
"""
通过id_获取惩罚
:param id_: id
:param user_id: 用户id
:param group_id: 群号
"""
bot = get_bot()
# 忽略的群聊
# _ignore_group = Config.get_config("black_word", "IGNORE_GROUP")
# 处罚 id 4 ban 时间:int,List[int]
ban_3_duration = Config.get_config("black_word", "BAN_3_DURATION")
# 处罚 id 4 ban 时间:int,List[int]
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:
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})")
logger.info(f"BlackWord 永久封禁 USER {user_id}...")
# 删除好友(有的话
elif id_ == 2:
if str(user_id) not in bot.config.superusers:
try:
await bot.delete_friend(user_id=user_id)
await send_msg(
user_id, group_id, f"BlackWordChecker 删除好友 USER {uname}({user_id})"
)
logger.info(f"BlackWord 删除好友 {user_id}...")
except ActionFailed:
pass
# 封禁用户指定时间,默认7天
elif id_ == 3:
if isinstance(ban_3_duration, list):
ban_3_duration = random.randint(ban_3_duration[0], ban_3_duration[1])
await BanUser.ban(user_id, 9, ban_4_duration * 60 * 60 * 24)
await send_msg(
user_id,
group_id,
f"BlackWordChecker 对用户 USER {uname}({user_id}) 进行封禁 {ban_3_duration} 天处罚。",
)
logger.info(f"BlackWord 封禁 USER {uname}({user_id}) {ban_3_duration} 天...")
return ban_3_duration
# 封禁用户指定时间,默认360分钟
elif id_ == 4:
if isinstance(ban_4_duration, list):
ban_4_duration = random.randint(ban_4_duration[0], ban_4_duration[1])
await BanUser.ban(user_id, 9, ban_4_duration * 60)
await send_msg(
user_id,
group_id,
f"BlackWordChecker 对用户 USER {uname}({user_id}) 进行封禁 {ban_4_duration} 分钟处罚。",
)
logger.info(f"BlackWord 封禁 USER {uname}({user_id}) {ban_4_duration} 分钟...")
return ban_4_duration
# 口头警告
elif id_ == 5:
if group_id:
await bot.send_group_msg(group_id=group_id, message=warning_result)
else:
await bot.send_private_msg(user_id=user_id, message=warning_result)
logger.info(f"BlackWord 口头警告 USER {user_id}")
return warning_result
return None
async def send_msg(user_id: int, group_id: Optional[int], message: str):
"""
发送消息
:param user_id: user_id
:param group_id: group_id
:param message: message
"""
bot = get_bot()
if not user_id:
user_id = int(list(bot.config.superusers)[0])
if group_id:
await bot.send_group_msg(group_id=group_id, message=message)
else:
await bot.send_private_msg(user_id=user_id, message=message)
async def check_text(text: str) -> bool:
"""
ALAPI文本检测,检测输入违规
:param text: 回复
"""
if not Config.get_config("alapi", "ALAPI_TOKEN"):
return True
params = {"token": Config.get_config("alapi", "ALAPI_TOKEN"), "text": text}
try:
data = (
await AsyncHttpx.get(
"https://v2.alapi.cn/api/censor/text", timeout=4, params=params
)
).json()
if data["code"] == 200:
return data["data"]["conclusion_type"] == 2
except Exception as e:
logger.error(f"检测违规文本错误...{type(e)}:{e}")
return True
black_word_manager = BlackWordManager(DATA_PATH / "black_word" / "black_word.json", DATA_PATH / "black_word" / "black_py.json")
+1 -1
View File
@@ -80,8 +80,8 @@ async def _(
except TimeoutError:
await bt.finish(f"搜索 {keyword} 超时...")
except Exception as e:
await bt.finish(f"bt 其他未知错误..")
logger.error(f"bt 错误 {type(e)}:{e}")
await bt.finish(f"bt 其他未知错误..")
if not send_flag:
await bt.send(f"{keyword} 未搜索到...")
logger.info(f"USER {event.user_id} BT搜索 {keyword} 第 {page} 页")
+6 -6
View File
@@ -3,13 +3,13 @@ from configs.config import Config
from bs4 import BeautifulSoup
import platform
if platform.system() == "Windows":
import asyncio
asyncio.set_event_loop_policy(asyncio.WindowsSelectorEventLoopPolicy())
# if platform.system() == "Windows":
# import asyncio
#
# asyncio.set_event_loop_policy(asyncio.WindowsSelectorEventLoopPolicy())
url = "http://www.eclzz.world"
url = "http://www.eclzz.one/"
async def get_bt_info(keyword: str, page: int):
@@ -19,7 +19,7 @@ async def get_bt_info(keyword: str, page: int):
:param page: 页数
"""
text = (await AsyncHttpx.get(f"{url}/s/{keyword}_rel_{page}.html", timeout=5)).text
if text.find("大约0条结果") != -1:
if "大约0条结果" in text:
return
soup = BeautifulSoup(text, "lxml")
item_lst = soup.find_all("div", {"class": "search-item"})
-58
View File
@@ -1,58 +0,0 @@
from .music_163 import get_song_id, get_song_info
from nonebot.adapters.onebot.v11 import Bot, Event, GroupMessageEvent, Message
from nonebot.params import CommandArg
from nonebot.typing import T_State
from services.log import logger
from nonebot import on_command
__zx_plugin_name__ = "点歌"
__plugin_usage__ = """
usage:
在线点歌
指令:
点歌 [歌名]
""".strip()
__plugin_des__ = "为你点播了一首曾经的歌"
__plugin_cmd__ = ["点歌 [歌名]"]
__plugin_type__ = ("一些工具",)
__plugin_version__ = 0.1
__plugin_author__ = "HibiKier"
__plugin_settings__ = {
"level": 5,
"default_status": True,
"limit_superuser": False,
"cmd": ["点歌"],
}
music = on_command("点歌", priority=5, block=True)
@music.handle()
async def handle_first_receive(state: T_State, arg: Message = CommandArg()):
args = arg.extract_plain_text().strip()
if args:
state["song_name"] = args
@music.got("song_name", prompt="歌名是?")
async def _(bot: Bot, event: Event, state: T_State):
song = state["song_name"]
song_id = await get_song_id(song)
if not song_id:
await music.finish("没有找到这首歌!", at_sender=True)
for _ in range(3):
song_content = [{"type": "music", "data": {"type": 163, "id": song_id}}]
logger.info(
f"(USER {event.user_id}, GROUP "
f"{event.group_id if isinstance(event, GroupMessageEvent) else 'private'})"
f" 点歌 :{song}"
)
await music.finish(song_content)
else:
await music.finish("网易云繁忙...")
+32 -27
View File
@@ -1,13 +1,13 @@
from nonebot.adapters.onebot.v11 import Bot
from nonebot.typing import T_State
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 Config
from configs.config import NICKNAME, Config
from nonebot.rule import to_me
from nonebot.params import ArgStr
import platform
import os
@@ -25,6 +25,11 @@ __plugin_cmd__ = ["检查更新真寻", "重启"]
__plugin_version__ = 0.1
__plugin_author__ = "HibiKier"
__plugin_configs__ = {
"UPDATE_REMIND": {
"value": True,
"help": "真寻是否检测版本状态",
"default": True,
},
"AUTO_UPDATE_ZHENXUN": {
"value": False,
"help": "真寻是否自动检查更新",
@@ -66,19 +71,18 @@ async def _(bot: Bot):
)
@restart.handle()
async def _():
if str(platform.system()).lower() == "windows":
await restart.finish("暂无windows重启脚本...")
@restart.got("flag", prompt="确定是否重启真寻?(重启失败咱们将失去联系,请谨慎!)")
async def _(state: T_State):
flag = state["flag"]
@restart.got("flag", prompt=f"确定是否重启{NICKNAME}?确定请回复[是|好|确定](重启失败咱们将失去联系,请谨慎!)")
async def _(flag: str = ArgStr("flag")):
if flag.lower() in ["true", "是", "好", "确定", "确定是"]:
await restart.send("开始重启真寻..请稍等...")
await restart.send(f"开始重启{NICKNAME}..请稍等...")
open("is_restart", "w")
os.system("./restart.sh")
if str(platform.system()).lower() == "windows":
import sys
python = sys.executable
os.execl(python, python, *sys.argv)
else:
os.system("./restart.sh")
else:
await restart.send("已取消操作...")
@@ -89,7 +93,7 @@ async def _(state: T_State):
minute=0,
)
async def _():
if Config.get_config("check_zhenxun_update", "AUTO_UPDATE_ZHENXUN"):
if Config.get_config("check_zhenxun_update", "UPDATE_REMIND"):
_version = "v0.0.0"
_version_file = Path() / "__version__"
if _version_file.exists():
@@ -109,17 +113,18 @@ async def _():
message=f"检测到真寻版本更新\n"
f"当前版本:{_version},最新版本:{latest_version}",
)
# try:
# code = await check_update(bot)
# except Exception as e:
# logger.error(f"更新真寻未知错误 {type(e)}:{e}")
# await bot.send_private_msg(
# user_id=int(list(bot.config.superusers)[0]),
# message=f"更新真寻未知错误 {type(e)}:{e}\n",
# )
# else:
# if code == 200:
# await bot.send_private_msg(
# user_id=int(list(bot.config.superusers)[0]),
# message=f"更新完毕,请重启真寻....",
# )
if Config.get_config("check_zhenxun_update", "AUTO_UPDATE_ZHENXUN"):
try:
code = await check_update(bot)
except Exception as e:
logger.error(f"更新真寻未知错误 {type(e)}:{e}")
await bot.send_private_msg(
user_id=int(list(bot.config.superusers)[0]),
message=f"更新真寻未知错误 {type(e)}:{e}\n",
)
else:
if code == 200:
await bot.send_private_msg(
user_id=int(list(bot.config.superusers)[0]),
message=f"更新完毕,请重启{NICKNAME}....",
)
+9 -5
View File
@@ -14,9 +14,9 @@ import tarfile
import shutil
import os
if str(platform.system()).lower() == "windows":
policy = asyncio.WindowsSelectorEventLoopPolicy()
asyncio.set_event_loop_policy(policy)
# if str(platform.system()).lower() == "windows":
# policy = asyncio.WindowsSelectorEventLoopPolicy()
# asyncio.set_event_loop_policy(policy)
driver = nonebot.get_driver()
@@ -73,6 +73,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')
if await AsyncHttpx.download_file(tar_gz_url, zhenxun_latest_tar_gz):
logger.info("下载真寻最新版文件完成....")
error = await asyncio.get_event_loop().run_in_executor(
@@ -161,7 +162,7 @@ def _file_handle(latest_version: str) -> str:
if backup_file.exists():
backup_file.unlink()
if file not in [config_file, config_path_file]:
os.rename(file.absolute(), backup_file.absolute())
shutil.move(file.absolute(), backup_file.absolute())
else:
with open(file, "r", encoding="utf8") as rf:
data = rf.read()
@@ -173,7 +174,7 @@ def _file_handle(latest_version: str) -> str:
old_file = Path() / file
if old_file not in [config_file, config_path_file] and file != "configs":
if not old_file.exists() and new_file.exists():
os.rename(new_file.absolute(), old_file.absolute())
shutil.move(new_file.absolute(), old_file.absolute())
logger.info(f"已更新文件:{file}")
# except Exception as e:
# error = f'{type(e)}:{e}'
@@ -188,6 +189,9 @@ def _file_handle(latest_version: str) -> str:
local_update_info_file.unlink()
with open(_version_file, "w", encoding="utf8") as f:
f.write(f"__version__: {latest_version}")
os.system(
f"poetry run pip install -r {(Path() / 'pyproject.toml').absolute()}"
)
return error
+1 -1
View File
@@ -53,7 +53,7 @@ __plugin_settings__ = {
dialogue_data = {}
dialogue = on_command("[滴滴滴]", aliases={"滴滴滴-"}, priority=5, block=True)
dialogue = on_command("&#91;滴滴滴&#93;", aliases={"滴滴滴-"}, priority=5, block=True)
reply = on_command("/t", priority=1, permission=SUPERUSER, block=True)
Executable → Regular
+182 -331
View File
@@ -1,43 +1,50 @@
from nonebot import on_regex, on_keyword
from nonebot.adapters.onebot.v11 import Bot, MessageEvent, Message
from nonebot.permission import SUPERUSER
from nonebot.typing import T_State
from nonebot.params import RegexGroup
from utils.utils import scheduler
from .genshin_handle import genshin_draw, update_genshin_info, reset_count, reload_genshin_pool
from .prts_handle import update_prts_info, prts_draw, reload_prts_pool
from .pretty_handle import update_pretty_info, pretty_draw, reload_pretty_pool
from .guardian_handle import update_guardian_info, guardian_draw, reload_guardian_pool
from .pcr_handle import update_pcr_info, pcr_draw
from .azur_handle import update_azur_info, azur_draw
from .fgo_handle import update_fgo_info, fgo_draw
from .onmyoji_handle import update_onmyoji_info, onmyoji_draw
from .update_game_info import update_info
from .util import is_number, check_num
from .rule import is_switch
from .config import draw_config
from .async_update_game_info import async_update_game
from typing import Tuple, Any
import re
import asyncio
import traceback
from cn2an import cn2an
from dataclasses import dataclass
from typing import Optional, Set, Tuple
import nonebot
from nonebot import on_regex, on_keyword
from nonebot.adapters.onebot.v11 import MessageEvent
from nonebot.log import logger
from nonebot.matcher import Matcher
from nonebot.params import RegexGroup
from nonebot.permission import SUPERUSER
from nonebot.typing import T_Handler
from nonebot_plugin_apscheduler import scheduler
from .handles.base_handle import BaseHandle
from .handles.azur_handle import AzurHandle
from .handles.fgo_handle import FgoHandle
from .handles.genshin_handle import GenshinHandle
from .handles.guardian_handle import GuardianHandle
from .handles.onmyoji_handle import OnmyojiHandle
from .handles.pcr_handle import PcrHandle
from .handles.pretty_handle import PrettyHandle
from .handles.prts_handle import PrtsHandle
from .handles.ba_handle import BaHandle
from .config import draw_config
__zx_plugin_name__ = "游戏抽卡"
__plugin_usage__ = """
usage:
模拟赛马娘,原神,明日方舟,坎公骑冠剑,公主连结(国/台),碧蓝航线,FGO,阴阳师进行抽卡
模拟赛马娘,原神,明日方舟,坎公骑冠剑,公主连结(国/台),碧蓝航线,FGO,阴阳师,碧蓝档案进行抽卡
指令:
原神[1-300]抽: 原神常驻池
原神角色[1-300]抽: 原神角色UP池子
原神武器[1-300]抽: 原神武器UP池子
原神[1-180]抽: 原神常驻池
原神角色[1-180]抽: 原神角色UP池子
原神武器[1-180]抽: 原神武器UP池子
重置原神抽卡: 清空当前卡池的抽卡次数[即从0开始计算UP概率]
方舟[1-300]抽: 方舟卡池,当有当期UP时指向UP池
赛马娘[1-200]抽: 赛马娘卡池,当有当期UP时指向UP池
坎公骑冠剑[1-300]抽: 坎公骑冠剑卡池,当有当期UP时指向UP池
pcr/公主连接[1-300]抽: 公主连接卡池
碧蓝航线/碧蓝[重型/轻型/特型][1-300]抽: 碧蓝航线重型/轻型/特型卡池
碧蓝航线/碧蓝[重型/轻型/特型/活动][1-300]抽: 碧蓝航线重型/轻型/特型/活动卡池
fgo[1-300]抽: fgo卡池
阴阳师[1-300]抽: 阴阳师卡池
ba/碧蓝档案[1-200]抽:碧蓝档案卡池
* 以上指令可以通过 XX一井 来指定最大抽取数量 *
* 示例:原神一井 *
""".strip()
@@ -58,9 +65,9 @@ usage:
""".strip()
__plugin_des__ = "就算是模拟抽卡也不能改变自己是个非酋"
__plugin_cmd__ = [
"原神[1-300]抽",
"原神角色[1-300]抽",
"原神武器[1-300]抽",
"原神[1-180]抽",
"原神角色[1-180]抽",
"原神武器[1-180]抽",
"重置原神抽卡",
"方舟[1-300]抽",
"赛马娘[1-200]抽",
@@ -68,6 +75,7 @@ __plugin_cmd__ = [
"pcr/公主连接[1-300]抽",
"fgo[1-300]抽",
"阴阳师[1-300]抽",
"碧蓝档案[1-200]抽",
"更新方舟信息 [_superuser]",
"重载方舟卡池 [_superuser]",
"更新原神信息 [_superuser]",
@@ -78,6 +86,7 @@ __plugin_cmd__ = [
"更新碧蓝航线信息 [_superuser]",
"更新fgo信息 [_superuser]",
"更新阴阳师信息 [_superuser]",
"更新碧蓝档案信息 [_superuser]",
]
__plugin_type__ = ("抽卡相关", 1)
__plugin_version__ = 0.1
@@ -89,326 +98,168 @@ __plugin_settings__ = {
"cmd": ["游戏抽卡", "抽卡"],
}
prts = on_regex(r'.*?方舟([1-9|一][0-9]{0,2}[抽|井|连])', rule=is_switch('prts'), priority=5, block=True)
prts_update = on_keyword({'更新方舟信息', '更新明日方舟信息'}, permission=SUPERUSER, priority=1, block=True)
prts_up_reload = on_keyword({'重载方舟卡池'}, priority=1, block=True)
genshin = on_regex(r'.*?原神(武器|角色)?池?[1-9|一][0-9]{0,2}[抽|井|连]', rule=is_switch('genshin'), priority=5, block=True)
genshin_update = on_keyword({'更新原神信息'}, permission=SUPERUSER, priority=1, block=True)
genshin_reset = on_keyword({'重置原神抽卡'}, priority=1, block=True)
genshin_up_reload = on_keyword({'重载原神卡池'}, priority=1, block=True)
pretty = on_regex(r'.*?马娘卡?[1-9|一][0-9]{0,2}[抽|井|连]', rule=is_switch('pretty'), priority=5, block=True)
pretty_update = on_keyword({'更新马娘信息', '更新赛马娘信息'}, permission=SUPERUSER, priority=1, block=True)
pretty_up_reload = on_keyword({'重载赛马娘卡池'}, priority=1, block=True)
guardian = on_regex(r'.*?坎公骑冠剑武?器?[1-9|一][0-9]{0,2}[抽|井|连]', rule=is_switch('guardian'), priority=5, block=True)
guardian_update = on_keyword({'更新坎公骑冠剑信息'}, permission=SUPERUSER, priority=1, block=True)
guardian_up_reload = on_keyword({'重载坎公骑冠剑卡池'}, priority=1, block=True)
pcr = on_regex(r'.*?(pcr|公主连结|公主连接|公主链接|公主焊接)[1-9|一][0-9]{0,2}[抽|井|连]', rule=is_switch('pcr'), priority=5, block=True)
pcr_update = on_keyword({'更新pcr信息', '更新公主连结信息'}, permission=SUPERUSER, priority=1, block=True)
azur = on_regex(r'.*?碧蓝航?线?(轻型|重型|特型)池?[1-9|一][0-9]{0,2}[抽|连]', rule=is_switch('azur'), priority=5, block=True)
azur_update = on_keyword({'更新碧蓝信息', '更新碧蓝航线信息'}, permission=SUPERUSER, priority=1, block=True)
fgo = on_regex(r'.*?fgo[1-9|一][0-9]{0,2}[抽|连]', rule=is_switch('fgo'), priority=5, block=True)
fgo_update = on_keyword({'更新fgo信息'}, permission=SUPERUSER, priority=1, block=True)
onmyoji = on_regex(r'.*?阴阳师[1-9|一][0-9]{0,2}[抽|连]', rule=is_switch('onmyoji'), priority=5, block=True)
onmyoji_update = on_keyword({'更新阴阳师信息'}, permission=SUPERUSER, priority=1, block=True)
@dataclass
class Game:
keywords: Set[str]
handle: BaseHandle
flag: bool
max_count: int = 300 # 一次最大抽卡数
reload_time: Optional[int] = None # 重载UP池时间(小时)
@prts.handle()
async def _(bot: Bot, event: MessageEvent, state: T_State, reg: Tuple[Any, ...] = RegexGroup()):
msg = str(event.get_message()).strip()
print(reg)
if msg in ['方舟一井', '方舟1井']:
num = 300
else:
rmsg = re.search(r'.*?方舟(.*)[抽|连]', msg)
if rmsg:
num, flag = check_num(rmsg.group(1), 300)
if not flag:
await prts.finish(num, at_sender=True)
else:
return
await prts.send(await prts_draw(int(num)), at_sender=True)
games = (
Game({"azur", "碧蓝航线"}, AzurHandle(), draw_config.AZUR_FLAG),
Game({"fgo", "命运冠位指定"}, FgoHandle(), draw_config.FGO_FLAG),
Game(
{"genshin", "原神"},
GenshinHandle(),
draw_config.GENSHIN_FLAG,
max_count=180,
reload_time=18,
),
Game(
{"guardian", "坎公骑冠剑"},
GuardianHandle(),
draw_config.GUARDIAN_FLAG,
reload_time=4,
),
Game({"onmyoji", "阴阳师"}, OnmyojiHandle(), draw_config.ONMYOJI_FLAG),
Game({"pcr", "公主连结", "公主连接", "公主链接", "公主焊接"}, PcrHandle(), draw_config.PCR_FLAG),
Game(
{"pretty", "马娘", "赛马娘"},
PrettyHandle(),
draw_config.PRETTY_FLAG,
max_count=200,
reload_time=4,
),
Game({"prts", "方舟", "明日方舟"}, PrtsHandle(), draw_config.PRTS_FLAG, reload_time=4),
Game(
{"ba","碧蓝档案"},BaHandle(),
draw_config.BA_FLAG,
max_count=200,
),
)
@prts_up_reload.handle()
async def _(bot: Bot, event: MessageEvent, state: T_State):
text = await reload_prts_pool()
await prts_up_reload.finish(Message(f'重载完成!\n{text}'))
@genshin.handle()
async def _(bot: Bot, event: MessageEvent, state: T_State):
msg = str(event.get_message()).strip()
rmsg = re.search(r'.*?原神(武器|角色)?池?(.*)[抽|井|连]', msg)
if rmsg:
pool_name = rmsg.group(1)
if pool_name == '武器':
pool_name = 'arms'
elif pool_name == '角色':
pool_name = 'char'
else:
pool_name = ''
num = rmsg.group(2)
if msg.find('一井') != -1 or msg.find('1井') != -1:
num = 180
else:
num, flag = check_num(num, 180)
if not flag:
await genshin.finish(num, at_sender=True)
else:
return
await genshin.send(await genshin_draw(event.user_id, int(num), pool_name), at_sender=True)
@genshin_up_reload.handle()
async def _(bot: Bot, event: MessageEvent, state: T_State):
text = await reload_genshin_pool()
await genshin_reset.finish(Message(f'重载成功!\n{text}'))
@genshin_reset.handle()
async def _(bot: Bot, event: MessageEvent, state: T_State):
reset_count(event.user_id)
await genshin_reset.send('重置了原神抽卡次数', at_sender=True)
@pretty.handle()
async def _(bot: Bot, event: MessageEvent, state: T_State):
msg = str(event.get_message()).strip()
if msg.find('1井') != -1 or msg.find('一井') != -1:
num = 200
if msg.find("卡") == -1:
pool_name = 'char'
else:
pool_name = 'card'
else:
rmsg = re.search(r'.*?马娘(.*)[抽|连]', msg)
if rmsg:
num = rmsg.group(1)
if num[0] == '卡':
num = num[1:]
pool_name = 'card'
def create_matchers():
def draw_handler(game: Game) -> T_Handler:
async def handler(
matcher: Matcher, event: MessageEvent, args: Tuple[str, ...] = RegexGroup()
):
pool_name, num, unit = args
if num == "单":
num = 1
else:
pool_name = 'char'
num, flag = check_num(num, 200)
if not flag:
await pretty.finish(num, at_sender=True)
else:
return
await pretty.send(await pretty_draw(int(num), pool_name), at_sender=True)
try:
num = int(cn2an(num, mode="smart"))
except ValueError:
await matcher.finish("必!须!是!数!字!")
if unit == "井":
num *= game.max_count
if num < 1:
await matcher.finish("虚空抽卡???")
elif num > game.max_count:
await matcher.finish("一井都满不足不了你嘛!快爬开!")
pool_name = (
pool_name.replace("池", "")
.replace("武器", "arms")
.replace("角色", "char")
.replace("卡牌", "card")
.replace("卡", "card")
)
try:
res = await game.handle.draw(num, pool_name=pool_name, user_id=event.user_id)
except:
logger.warning(traceback.format_exc())
await matcher.finish("出错了...")
await matcher.finish(res, at_sender=True)
return handler
def update_handler(game: Game) -> T_Handler:
async def handler(matcher: Matcher):
await game.handle.update_info()
await matcher.finish("更新完成!")
return handler
def reload_handler(game: Game) -> T_Handler:
async def handler(matcher: Matcher):
res = await game.handle.reload_pool()
if res:
await matcher.finish(res)
return handler
def reset_handler(game: Game) -> T_Handler:
async def handler(matcher: Matcher, event: MessageEvent):
if game.handle.reset_count(event.user_id):
await matcher.finish("重置成功!")
return handler
def scheduled_job(game: Game) -> T_Handler:
async def handler():
await game.handle.reload_pool()
return handler
for game in games:
pool_pattern = r"([^\s单0-9零一二三四五六七八九百十]{0,3})"
num_pattern = r"(单|[0-9零一二三四五六七八九百十]{1,3})"
unit_pattern = r"([抽|井|连])"
draw_regex = r".*?(?:{})\s*{}\s*{}\s*{}".format(
"|".join(game.keywords), pool_pattern, num_pattern, unit_pattern
)
update_keywords = {f"更新{keyword}信息" for keyword in game.keywords}
reload_keywords = {f"重载{keyword}卡池" for keyword in game.keywords}
reset_keywords = {f"重置{keyword}抽卡" for keyword in game.keywords}
if game.flag:
on_regex(draw_regex, priority=5, block=True).append_handler(
draw_handler(game)
)
on_keyword(
update_keywords, permission=SUPERUSER, priority=1, block=True
).append_handler(update_handler(game))
on_keyword(reload_keywords, priority=1, block=True).append_handler(
reload_handler(game)
)
on_keyword(reset_keywords, priority=1, block=True).append_handler(
reset_handler(game)
)
if game.reload_time:
scheduler.add_job(
scheduled_job(game), trigger="cron", hour=game.reload_time, minute=1
)
@pretty_up_reload.handle()
async def _(bot: Bot, event: MessageEvent, state: T_State):
text = await reload_pretty_pool()
await genshin_reset.finish(Message(f'重载成功!\n{text}'))
@guardian.handle()
async def _(bot: Bot, event: MessageEvent, state: T_State):
msg = str(event.get_message()).strip()
pool_name = 'char'
if msg.find('1井') != -1 or msg.find('一井') != -1:
num = 300
if msg.find('武器') != -1:
pool_name = 'arms'
else:
rmsg = re.search(r'.*?坎公骑冠剑(.*)[抽|连]', msg)
if rmsg:
num = rmsg.group(1)
if num.find('武器') != -1:
pool_name = 'arms'
num = num.replace('武器', '')
num, flag = check_num(num, 300)
if not flag:
await guardian.finish(num, at_sender=True)
else:
return
await guardian.send(await guardian_draw(int(num), pool_name), at_sender=True)
@guardian_up_reload.handle()
async def _(bot: Bot, event: MessageEvent, state: T_State):
text = await reload_guardian_pool()
await genshin_reset.finish(Message(f'重载成功!\n{text}'))
@pcr.handle()
async def _(bot: Bot, event: MessageEvent, state: T_State):
msg = str(event.get_message()).strip()
if msg.find('1井') != -1 or msg.find('一井') != -1:
num = 300
else:
rmsg = re.search(r'.*?(pcr|公主连结)(.*)[抽|井|连]', msg)
if rmsg:
num, flag = check_num(rmsg.group(2), 300)
if not flag:
await pcr.finish(num, at_sender=True)
else:
return
await pcr.send(await pcr_draw(int(num)), at_sender=True)
@azur.handle()
async def _(bot: Bot, event: MessageEvent, state: T_State):
msg = str(event.get_message()).strip()
rmsg = re.search(r'.*?碧蓝航?线?(轻型|重型|特型)池?(.*)[抽|连]', msg)
if rmsg:
pool_name = rmsg.group(1)
num, flag = check_num(rmsg.group(2), 300)
if not flag:
await azur.finish(num, at_sender=True)
else:
return
await azur.send(await azur_draw(int(num), pool_name), at_sender=True)
@fgo.handle()
async def _(bot: Bot, event: MessageEvent, state: T_State):
msg = str(event.get_message()).strip()
rmsg = re.search(r'.*?fgo(.*)[抽|连]', msg)
if rmsg:
num, flag = check_num(rmsg.group(1), 300)
if not flag:
await fgo.finish(num, at_sender=True)
else:
return
await fgo.send(await fgo_draw(int(num)), at_sender=True)
@onmyoji.handle()
async def _(bot: Bot, event: MessageEvent, state: T_State):
msg = str(event.get_message()).strip()
rmsg = re.search(r'.*?阴阳师(.*)[抽|连]', msg)
if rmsg:
num, flag = check_num(rmsg.group(1), 300)
if not flag:
await onmyoji.finish(num, at_sender=True)
else:
return
await onmyoji.send(await onmyoji_draw(int(num)), at_sender=True)
@prts_update.handle()
async def _(bot: Bot, event: MessageEvent, state: T_State):
await update_prts_info()
await prts_update.finish('更新完成!')
@genshin_update.handle()
async def _(bot: Bot, event: MessageEvent, state: T_State):
await update_genshin_info()
await genshin_update.finish('更新完成!')
@pretty_update.handle()
async def _(bot: Bot, event: MessageEvent, state: T_State):
await update_pretty_info()
await genshin_update.finish('更新完成!')
@guardian_update.handle()
async def _(bot: Bot, event: MessageEvent, state: T_State):
await update_guardian_info()
await genshin_update.finish('更新完成!')
@pcr_update.handle()
async def _(bot: Bot, event: MessageEvent, state: T_State):
await update_pcr_info()
await genshin_update.finish('更新完成!')
@azur_update.handle()
async def _(bot: Bot, event: MessageEvent, state: T_State):
await update_azur_info()
await genshin_update.finish('更新完成!')
@fgo_update.handle()
async def _(bot: Bot, event: MessageEvent, state: T_State):
await update_fgo_info()
await genshin_update.finish('更新完成!')
@onmyoji_update.handle()
async def _(bot: Bot, event: MessageEvent, state: T_State):
await update_onmyoji_info()
await genshin_update.finish('更新完成!')
create_matchers()
# 更新资源
@scheduler.scheduled_job(
'cron',
"cron",
hour=4,
minute=1,
)
async def _():
tasks = []
if draw_config.PRTS_FLAG:
tasks.append(asyncio.ensure_future(update_prts_info()))
if draw_config.GENSHIN_FLAG:
tasks.append(asyncio.ensure_future(update_genshin_info()))
if draw_config.PRETTY_FLAG:
tasks.append(asyncio.ensure_future(update_pretty_info()))
if draw_config.GUARDIAN_FLAG:
tasks.append(asyncio.ensure_future(update_guardian_info()))
if draw_config.PCR_FLAG:
tasks.append(asyncio.ensure_future(update_pcr_info()))
if draw_config.AZUR_FLAG:
tasks.append(asyncio.ensure_future(update_azur_info()))
if draw_config.FGO_FLAG:
tasks.append(asyncio.ensure_future(update_fgo_info()))
if draw_config.ONMYOJI_FLAG:
tasks.append(asyncio.ensure_future(update_onmyoji_info()))
for game in games:
if game.flag:
tasks.append(asyncio.ensure_future(game.handle.update_info()))
await asyncio.gather(*tasks)
# 每天四点重载方舟up卡池
@scheduler.scheduled_job(
'cron',
hour=4,
minute=1,
)
driver = nonebot.get_driver()
@driver.on_startup
async def _():
if draw_config.PRTS_FLAG:
await reload_prts_pool()
# 每天四点重载赛马娘up卡池
@scheduler.scheduled_job(
'cron',
hour=4,
minute=1,
)
async def _():
if draw_config.PRETTY_FLAG:
await reload_pretty_pool()
# 每天下午六点点重载原神up卡池
@scheduler.scheduled_job(
'cron',
hour=18,
minute=1,
)
async def _():
if draw_config.PRTS_FLAG:
await reload_genshin_pool()
# 重载坎公骑冠剑卡池
@scheduler.scheduled_job(
'cron',
hour=4,
minute=1,
)
async def _():
if draw_config.GUARDIAN_FLAG:
await reload_guardian_pool()
tasks = []
for game in games:
if game.flag:
game.handle.init_data()
if not game.handle.data_exists():
tasks.append(asyncio.ensure_future(game.handle.update_info()))
await asyncio.gather(*tasks)
-395
View File
@@ -1,395 +0,0 @@
from bs4 import BeautifulSoup
import re
from datetime import datetime, timedelta
from .config import DRAW_DATA_PATH
from asyncio.exceptions import TimeoutError
from nonebot.log import logger
from utils.http_utils import AsyncHttpx
try:
import ujson as json
except ModuleNotFoundError:
import json
headers = {'User-Agent': '"Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 5.1; TencentTraveler 4.0)"'}
prts_up_char = DRAW_DATA_PATH / "draw_card_up" / "prts_up_char.json"
genshin_up_char = DRAW_DATA_PATH / "draw_card_up" / "genshin_up_char.json"
pretty_up_char = DRAW_DATA_PATH / "draw_card_up" / "pretty_up_char.json"
guardian_up_char = DRAW_DATA_PATH / "draw_card_up" / "guardian_up_char.json"
prts_url = "https://ak.hypergryph.com/news.html"
genshin_url = "https://wiki.biligame.com/ys/%E7%A5%88%E6%84%BF"
pretty_url = "https://wiki.biligame.com/umamusume/%E5%85%AC%E5%91%8A"
guardian_url = "https://wiki.biligame.com/gt/%E9%A6%96%E9%A1%B5"
# 是否过时
def is_expired(data: dict):
times = data['time'].split('-')
for i in range(len(times)):
times[i] = str(datetime.now().year) + '-' + times[i].split('日')[0].strip().replace('月', '-')
start_date = datetime.strptime(times[0], '%Y-%m-%d').date()
end_date = datetime.strptime(times[1], '%Y-%m-%d').date()
now = datetime.now().date()
return not start_date <= now <= end_date
# 检查写入
def check_write(data: dict, up_char_file):
try:
if is_expired(data['char']):
for x in list(data.keys()):
data[x]['title'] = ''
else:
with open(up_char_file, 'w', encoding='utf8') as f:
json.dump(data, f, indent=4, ensure_ascii=False)
if not up_char_file.exists():
with open(up_char_file, 'w', encoding='utf8') as f:
json.dump(data, f, indent=4, ensure_ascii=False)
else:
with open(up_char_file, 'r', encoding='utf8') as f:
old_data = json.load(f)
if is_expired(old_data['char']):
return old_data
else:
with open(up_char_file, 'w', encoding='utf8') as f:
json.dump(data, f, indent=4, ensure_ascii=False)
except ValueError:
pass
return data
class PrtsAnnouncement:
def __init__(self):
self.game_name = '明日方舟'
async def _get_announcement_text(self):
res = await AsyncHttpx.get(prts_url, timeout=7)
soup = BeautifulSoup(res.text, 'lxml')
ol = soup.find('ol', {'class': 'articleList active', 'data-category-key': 'LATEST'})
for li in ol:
type_ = li.find('span', {'class': 'articleItemCate'}).text
if type_ == '活动':
a = li.find('a')['href']
return (await AsyncHttpx.get(f'https://ak.hypergryph.com{a}', timeout=7)).text
async def update_up_char(self):
prts_up_char.parent.mkdir(parents=True, exist_ok=True)
if prts_up_char.exists():
with open(prts_up_char, 'r', encoding='utf8') as f:
data = json.load(f)
if not data.get('char'):
prts_up_char.unlink()
try:
data = {'char': {'up_char': {'6': {}, '5': {}, '4': {}}, 'title': '', 'time': '', 'pool_img': ''}}
text = await self._get_announcement_text()
soup = BeautifulSoup(text, 'lxml')
content = soup.find('div', {'class': 'article-content'})
contents = [x for x in content.contents if x.text or str(x).find('img') != -1]
start_index = -1
end_index = -1
for i in range(len(contents)):
if str(contents[i]).startswith('<p>'):
r = re.search('(.*)(寻访|复刻).*?开启', contents[i].text)
if r:
if str(contents[i+3].text).find('★') != -1:
img = contents[i-1].find('img')
if img:
data['char']['pool_img'] = img['src']
start_index = i
for j in range(i, len(contents)):
if str(contents[j]).find('注意') != -1:
end_index = j
break
break
contents = contents[start_index: end_index]
title = contents[0].text
data['char']['title'] = title[title.find('【'): title.find('】') + 1]
data['char']['time'] = str(contents[1].text).split(':', maxsplit=1)[1]
for p in contents[2:]:
p = str(p.text)
r = None
if p.find('★') != -1:
if p.find('权值') == -1:
r = re.search(r'.*?:(.*)(占(.*)★.*?的(.*)%)', p)
else:
r = re.search(r'.*?:(.*)(在(.*)★.*?以(.*)倍权值.*?)', p)
star = r.group(2)
if r:
chars = r.group(1)
if chars.find('/') != -1:
chars = chars.strip().split('/')
elif chars.find('\\') != -1:
chars = chars.strip().split('\\')
else:
chars = chars.split('\n')
chars = [x.replace('[限定]', '').strip() for x in chars]
probability = r.group(3)
probability = probability if int(probability) > 10 else f'权{probability}'
for char in chars:
if char.strip():
data['char']['up_char'][star][char.strip()] = probability
except TimeoutError:
logger.warning(f'更新明日方舟UP池信息超时...')
if prts_up_char.exists():
with open(prts_up_char, 'r', encoding='utf8') as f:
data = json.load(f)
except Exception as e:
logger.error(f'更新明日方舟未知错误 e:{e}')
if prts_up_char.exists():
with open(prts_up_char, 'r', encoding='utf8') as f:
data = json.load(f)
return check_write(data, prts_up_char)
class GenshinAnnouncement:
def __init__(self):
self.game_name = '原神'
async def _get_announcement_text(self):
return (await AsyncHttpx.get(genshin_url, timeout=7)).text
async def update_up_char(self):
genshin_up_char.parent.mkdir(exist_ok=True, parents=True)
data = {
'char': {'up_char': {'5': {}, '4': {}}, 'title': '', 'time': '', 'pool_img': ''},
'arms': {'up_char': {'5': {}, '4': {}}, 'title': '', 'time': '', 'pool_img': ''}
}
text = await self._get_announcement_text()
soup = BeautifulSoup(text, 'lxml')
try:
div = soup.find_all('div', {'class': 'row'})[1]
tables = div.find_all('table', {'class': 'wikitable'})
for table in tables:
trs = table.find('tbody').find_all('tr')
pool_img = trs[0].find('th').find('img')
if pool_img['title'].find('角色活动') == -1:
type_ = 'arms'
else:
type_ = 'char'
try:
data[type_]['pool_img'] = str(pool_img['srcset']).split(' ')[0]
except KeyError:
data[type_]['pool_img'] = pool_img['src']
data[type_]['title'] = str(pool_img['title']).split(f'期{"角色" if type_ == "char" else "武器"}')[0][:-3]
data[type_]['time'] = trs[1].find('td').text
if data[type_]['time'][-1] == '\n':
data[type_]['time'] = data[type_]['time'][:-1]
if '版本更新后' in data[type_]['time']:
sp = data[type_]['time'].split('~')
end_time = datetime.strptime(sp[1].strip(), "%Y/%m/%d %H:%M")
start_time = end_time - timedelta(days=20)
data[type_]['time'] = start_time.strftime('%Y/%m/%d') + ' ~ ' + end_time.strftime('%Y/%m/%d')
tmp = ''
for tm in data[type_]['time'].split('~'):
date_time_sp = tm.split('/')
date_time_sp[2] = date_time_sp[2].strip().replace(' ', '日 ')
tmp += date_time_sp[1] + '月' + date_time_sp[2] + ' - '
data[type_]['time'] = tmp[:-2].strip()
for a in trs[2].find('td').find_all('a'):
char_name = a['title']
data[type_]['up_char']['5'][char_name] = "50"
for a in trs[3].find('td').find_all('a'):
char_name = a['title']
data[type_]['up_char']['4'][char_name] = "50"
except TimeoutError:
logger.warning(f'更新原神UP池信息超时...')
if genshin_up_char.exists():
with open(genshin_up_char, 'r', encoding='utf8') as f:
data = json.load(f)
except Exception as e:
logger.error(f'更新原神UP失败,疑似UP池已结束, e:{e}')
if genshin_up_char.exists():
with open(genshin_up_char, 'r', encoding='utf8') as f:
data = json.load(f)
data['char']['title'] = ''
data['arms']['title'] = ''
with open(genshin_up_char, 'w', encoding='utf8') as wf:
json.dump(data, wf, ensure_ascii=False, indent=4)
return data
return check_write(data, genshin_up_char)
class PrettyAnnouncement:
def __init__(self):
self.game_name = '赛马娘'
async def _get_announcement_text(self):
res = await AsyncHttpx.get(pretty_url, timeout=7)
soup = BeautifulSoup(res.text, 'lxml')
divs = soup.find('div', {'id': 'mw-content-text'}).find('div').find_all('div')
for div in divs:
a = div.find('a')
try:
title = a['title']
except (KeyError, TypeError):
continue
if title.find('新角色追加') != -1:
url = a['href']
break
return (await AsyncHttpx.get(f'https://wiki.biligame.com/{url}', timeout=7)).text, title[:-2]
async def update_up_char(self):
data = {
'char': {'up_char': {'3': {}, '2': {}, '1': {}}, 'title': '', 'time': '', 'pool_img': ''},
'card': {'up_char': {'3': {}, '2': {}, '1': {}}, 'title': '', 'time': '', 'pool_img': ''}
}
try:
text, title = await self._get_announcement_text()
soup = BeautifulSoup(text, 'lxml')
context = soup.find('div', {'class': 'toc-sticky'})
if not context:
context = soup.find('div', {'class': 'mw-parser-output'})
data['char']['title'] = title
data['card']['title'] = title
for big in context.find_all('big'):
r = re.search(r'\d{1,2}/\d{1,2} \d{1,2}:\d{1,2}', str(big.text))
if r:
time = str(big.text)
break
else:
logger.warning('赛马娘UP无法找到活动日期....取消更新UP池子...')
return
time = time.replace('~', '-').replace('/', '月').split(' ')
time = time[0] + '日 ' + time[1] + ' - ' + time[3] + '日 ' + time[4]
data['char']['time'] = time
data['card']['time'] = time
for p in context.find_all('p'):
if str(p).find('当期UP赛马娘') != -1 and str(p).find('■') != -1:
if not data['char']['pool_img']:
try:
data['char']['pool_img'] = p.find('img')['src']
except TypeError:
for center in context.find_all('center'):
try:
img = center.find('img')
if img and str(img['alt']).find('新马娘') != -1 and str(img['alt']).find('总览') == 1:
data['char']['pool_img'] = img['src']
except (TypeError, KeyError):
pass
r = re.findall(r'.*?当期UP赛马娘([\s\S]*)<奖励内容>.*?', str(p))
if r:
for x in r:
x = str(x).split('\n')
for msg in x:
if msg.find('★') != -1:
msg = msg.replace('<br/>', '')
char_name = msg[msg.find('['):].strip()
if (star := len(msg[:msg.find('[')].strip())) == 3:
data['char']['up_char']['3'][char_name] = '70'
elif star == 2:
data['char']['up_char']['2'][char_name] = '70'
elif star == 1:
data['char']['up_char']['1'][char_name] = '70'
if str(p).find('(当期UP对象)') != -1 and str(p).find('赛马娘') == -1 and str(p).find('■') != -1:
# data['card']['pool_img'] = p.find('img')['src']
if not data['char']['pool_img']:
try:
data['char']['pool_img'] = p.find('img')['src']
except TypeError:
for center in context.find_all('center'):
try:
img = center.find('img')
if img and str(img['alt']).find('新卡') != -1 and str(img['alt']).find('总览') == 1:
data['card']['pool_img'] = img['src']
except (TypeError, KeyError):
pass
r = re.search(r'■全?新?支援卡(当期UP对象)([\s\S]*)</p>', str(p))
if r:
rmsg = r.group(1).strip()
rmsg = rmsg.split('<br/>')
rmsg = [x for x in rmsg if x]
for x in rmsg:
x = x.replace('\n', '').replace('・', '')
star = x[:x.find('[')].strip()
char_name = x[x.find('['):].strip()
if star == 'SSR':
data['card']['up_char']['3'][char_name] = '70'
if star == 'SR':
data['card']['up_char']['2'][char_name] = '70'
if star == 'R':
data['card']['up_char']['1'][char_name] = '70'
# 日文->中文
with open(DRAW_DATA_PATH / 'pretty_card.json', 'r', encoding='utf8') as f:
all_data = json.load(f)
for star in data['card']['up_char'].keys():
for name in list(data['card']['up_char'][star].keys()):
char_name = name.split(']')[1].strip()
tp_name = name[name.find('['): name.find(']') + 1].strip().replace('[', '【').replace(']', '】')
for x in all_data.keys():
if all_data[x]['名称'].find(tp_name) != -1 and all_data[x]['关联角色'] == char_name:
data['card']['up_char'][star].pop(name)
data['card']['up_char'][star][all_data[x]['中文名']] = '70'
except TimeoutError:
logger.warning(f'更新赛马娘UP池信息超时...')
if pretty_up_char.exists():
with open(pretty_up_char, 'r', encoding='utf8') as f:
data = json.load(f)
except Exception as e:
logger.error(f'赛马娘up更新未知错误 {type(e)}:{e}')
if pretty_up_char.exists():
with open(pretty_up_char, 'r', encoding='utf8') as f:
data = json.load(f)
return check_write(data, pretty_up_char)
class GuardianAnnouncement:
def __init__(self):
self.game_name = '坎公骑冠剑'
async def _get_announcement_text(self):
return (await AsyncHttpx.get(guardian_url, timeout=7)).text
async def update_up_char(self):
data = {
'char': {'up_char': {'3': {}}, 'title': '', 'time': '', 'pool_img': ''},
'arms': {'up_char': {'5': {}}, 'title': '', 'time': '', 'pool_img': ''}
}
try:
text = await self._get_announcement_text()
soup = BeautifulSoup(text, 'lxml')
context = soup.select('div.col-sm-3:nth-child(3) > div:nth-child(2) > div:nth-child(1) '
'> div:nth-child(2) > div:nth-child(3) > font:nth-child(1)')[0]
title = context.find('p').find('b').text
tmp = title.split(',')
time = ''
for msg in tmp:
r = re.search(r'[从|至](.*)(开始|结束)', msg)
if r:
time += r.group(1).strip() + ' - '
time = time[:-3]
title = time.split(' - ')[0] + 'UP卡池'
data['char']['title'] = title
data['arms']['title'] = title
data['char']['time'] = time
data['arms']['time'] = time
start_idx = -1
end_idx = -1
index = 0
divs = context.find_all('div')
for x in divs:
if x.text == '角色':
start_idx = index
if x.text == '武器':
end_idx = index
break
index += 1
for x in divs[start_idx + 1: end_idx]:
name = x.find('p').find_all('a')[-1].text
data['char']['up_char']['3'][name] = '0'
for x in divs[end_idx + 1:]:
name = x.find('p').find_all('a')[-1].text
data['arms']['up_char']['5'][name] = '0'
except TimeoutError:
logger.warning(f'更新坎公骑冠剑UP池信息超时...')
if guardian_up_char.exists():
with open(guardian_up_char, 'r', encoding='utf8') as f:
data = json.load(f)
except Exception as e:
logger.error(f'坎公骑冠剑up更新未知错误 {type(e)}:{e}')
return check_write(data, guardian_up_char)
@@ -1,63 +0,0 @@
import asyncio
import nonebot
from nonebot.log import logger
from .pcr_handle import update_pcr_info, init_pcr_data
from .azur_handle import update_azur_info, init_azur_data
from .prts_handle import update_prts_info, init_prts_data
from .pretty_handle import update_pretty_info, init_pretty_data
from .guardian_handle import update_guardian_info, init_guardian_data
from .genshin_handle import update_genshin_info, init_genshin_data
from .fgo_handle import update_fgo_info, init_fgo_data
from .onmyoji_handle import update_onmyoji_info, init_onmyoji_data
from .config import draw_config, DRAW_DATA_PATH
driver = nonebot.get_driver()
@driver.on_startup
async def async_update_game():
tasks = []
init_lst = [init_pcr_data, init_pretty_data, init_azur_data, init_prts_data, init_genshin_data, init_guardian_data,
init_fgo_data, init_onmyoji_data]
if draw_config.PRTS_FLAG and not (DRAW_DATA_PATH / 'prts.json').exists():
tasks.append(asyncio.ensure_future(update_prts_info()))
init_lst.remove(init_prts_data)
if draw_config.PRETTY_FLAG and (not (DRAW_DATA_PATH / 'pretty.json').exists() or
not (DRAW_DATA_PATH / 'pretty_card.json').exists()):
tasks.append(asyncio.ensure_future(update_pretty_info()))
init_lst.remove(init_pretty_data)
if draw_config.GUARDIAN_FLAG and not (DRAW_DATA_PATH / 'guardian.json').exists():
tasks.append(asyncio.ensure_future(update_guardian_info()))
init_lst.remove(init_guardian_data)
if draw_config.PCR_FLAG and not (DRAW_DATA_PATH / 'pcr.json').exists():
tasks.append(asyncio.ensure_future(update_pcr_info()))
init_lst.remove(init_pcr_data)
if draw_config.GENSHIN_FLAG and (not (DRAW_DATA_PATH / 'genshin.json').exists() or
not (DRAW_DATA_PATH / 'genshin_arms.json').exists()):
tasks.append(asyncio.ensure_future(update_genshin_info()))
init_lst.remove(init_genshin_data)
if draw_config.AZUR_FLAG and not (DRAW_DATA_PATH / 'azur.json').exists():
tasks.append(asyncio.ensure_future(update_azur_info()))
init_lst.remove(init_azur_data)
if draw_config.FGO_FLAG and (not (DRAW_DATA_PATH / 'fgo.json').exists() or
not (DRAW_DATA_PATH / 'fgo_card.json').exists()):
tasks.append(asyncio.ensure_future(update_fgo_info()))
init_lst.remove(init_fgo_data)
if draw_config.ONMYOJI_FLAG and not (DRAW_DATA_PATH / 'onmyoji.json').exists():
tasks.append(asyncio.ensure_future(update_onmyoji_info()))
init_lst.remove(init_onmyoji_data)
try:
await asyncio.gather(*tasks)
for func in init_lst:
await func()
except asyncio.exceptions.CancelledError:
logger.warning('更新异常:CancelledError,再次更新...')
await async_update_game()
-62
View File
@@ -1,62 +0,0 @@
from nonebot.adapters.onebot.v11 import MessageSegment
import random
from .update_game_simple_info import update_simple_info
from .util import generate_img, init_star_rst, BaseData, set_list, get_star, max_card, format_card_information
from .config import draw_config, DRAW_DATA_PATH
from dataclasses import dataclass
from .init_card_pool import init_game_pool
try:
import ujson as json
except ModuleNotFoundError:
import json
ALL_CHAR = []
@dataclass
class AzurChar(BaseData):
type_: str # 舰娘类型
async def azur_draw(count: int, pool_name: str):
# 0 1 2
cnlist = ['金', '紫', '蓝', '白']
star_list = [0, 0, 0, 0]
char_list, char_dict, max_star_list, star_list, max_star_index_list = \
format_card_information(count, star_list, _get_azur_card, pool_name, guaranteed=False)
rst = init_star_rst(star_list, cnlist, max_star_list, max_star_index_list)
if count > 90:
char_list = set_list(char_list)
return MessageSegment.image("base64://" + await generate_img(char_list, 'azur', star_list)) \
+ '\n' + rst[:-1] + '\n' + max_card(char_dict)
async def update_azur_info():
global ALL_CHAR
url = 'https://wiki.biligame.com/blhx/舰娘图鉴'
data, code = await update_simple_info(url, 'azur')
if code == 200:
ALL_CHAR = init_game_pool('azur', data, AzurChar)
async def init_azur_data():
global ALL_CHAR
if draw_config.AZUR_FLAG:
with (DRAW_DATA_PATH / 'azur.json').open('r', encoding='utf8') as f:
azur_dict = json.load(f)
ALL_CHAR = init_game_pool('azur', azur_dict, AzurChar)
# 抽取卡池
def _get_azur_card(pool_name: str, mode: int = 1):
global ALL_CHAR
azur_config = draw_config.azur
if pool_name == '轻型':
type_ = ['驱逐', '轻巡', '维修']
elif pool_name == '重型':
type_ = ['重巡', '战列', '战巡', '重炮']
else:
type_ = ['维修', '潜艇', '重巡', '轻航', '航母']
star = get_star([4, 3, 2, 1], [azur_config.AZUR_FOUR_P, azur_config.AZUR_THREE_P, azur_config.AZUR_TWO_P, azur_config.AZUR_ONE_P])
chars = [x for x in ALL_CHAR if x.star == star and x.type_ in type_ and not x.limited]
return random.choice(chars), 4 - star
Executable → Regular
+66 -53
View File
@@ -1,8 +1,9 @@
import nonebot
from pathlib import Path
from nonebot.log import logger
from pydantic import BaseModel, Extra, ValidationError
from configs.path_config import IMAGE_PATH, DATA_PATH
from configs.config import Config
from pydantic import BaseModel, Extra
from configs.config import Config as AConfig
from configs.path_config import DATA_PATH
try:
import ujson as json
@@ -15,8 +16,8 @@ class GenshinConfig(BaseModel, extra=Extra.ignore):
GENSHIN_FIVE_P: float = 0.006
GENSHIN_FOUR_P: float = 0.051
GENSHIN_THREE_P: float = 0.43
GENSHIN_G_FIVE_P: float = 0.13
GENSHIN_G_FOUR_P: float = 0.016
GENSHIN_G_FIVE_P: float = 0.016
GENSHIN_G_FOUR_P: float = 0.13
I72_ADD: float = 0.0585
@@ -86,44 +87,28 @@ class OnmyojiConfig(BaseModel, extra=Extra.ignore):
ONMYOJI_SR: float = 0.2
ONMYOJI_R: float = 0.7875
#碧蓝档案
class BaConfig(BaseModel, extra=Extra.ignore):
BA_THREE_P: float = 0.025
BA_TWO_P: float = 0.185
BA_ONE_P: float = 0.79
BA_G_TWO_P: float = 0.975
class PathDict(BaseModel, extra=Extra.ignore):
genshin: str = "原神"
prts: str = "明日方舟"
pretty: str = "赛马娘"
guardian: str = "坎公骑冠剑"
pcr: str = "公主连结"
azur: str = "碧蓝航线"
fgo: str = "命运-冠位指定"
onmyoji: str = "阴阳师"
class DrawConfig(BaseModel, extra=Extra.ignore):
class Config(BaseModel, extra=Extra.ignore):
# 开关
PRTS_FLAG: bool = Config.get_config("draw_card", "PRTS_FLAG")
GENSHIN_FLAG: bool = Config.get_config("draw_card", "GENSHIN_FLAG")
PRETTY_FLAG: bool = Config.get_config("draw_card", "PRETTY_FLAG")
GUARDIAN_FLAG: bool = Config.get_config("draw_card", "GUARDIAN_FLAG")
PCR_FLAG: bool = Config.get_config("draw_card", "PCR_FLAG")
AZUR_FLAG: bool = Config.get_config("draw_card", "AZUR_FLAG")
FGO_FLAG: bool = Config.get_config("draw_card", "FGO_FLAG")
ONMYOJI_FLAG: bool = Config.get_config("draw_card", "ONMYOJI_FLAG")
PRTS_FLAG: bool = AConfig.get_config("draw_card", "PRTS_FLAG")
GENSHIN_FLAG: bool = AConfig.get_config("draw_card", "GENSHIN_FLAG")
PRETTY_FLAG: bool = AConfig.get_config("draw_card", "PRETTY_FLAG")
GUARDIAN_FLAG: bool = AConfig.get_config("draw_card", "GUARDIAN_FLAG")
PCR_FLAG: bool = AConfig.get_config("draw_card", "PCR_FLAG")
AZUR_FLAG: bool = AConfig.get_config("draw_card", "AZUR_FLAG")
FGO_FLAG: bool = AConfig.get_config("draw_card", "FGO_FLAG")
ONMYOJI_FLAG: bool = AConfig.get_config("draw_card", "ONMYOJI_FLAG")
BA_FLAG: bool = AConfig.get_config("draw_card", "BA_FLAG")
# 其他配置
PCR_TAI: bool = Config.get_config("draw_card", "PCR_TAI")
SEMAPHORE: int = Config.get_config("draw_card", "SEMAPHORE")
# 路径
path_dict: dict = {
"genshin": "原神",
"prts": "明日方舟",
"pretty": "赛马娘",
"guardian": "坎公骑冠剑",
"pcr": "公主连结",
"azur": "碧蓝航线",
"fgo": "命运-冠位指定",
"onmyoji": "阴阳师",
}
PCR_TAI: bool = AConfig.get_config("draw_card", "PCR_TAI")
SEMAPHORE: int = AConfig.get_config("draw_card", "SEMAPHORE")
# 抽卡概率
prts: PrtsConfig = PrtsConfig()
@@ -134,30 +119,58 @@ class DrawConfig(BaseModel, extra=Extra.ignore):
azur: AzurConfig = AzurConfig()
fgo: FgoConfig = FgoConfig()
onmyoji: OnmyojiConfig = OnmyojiConfig()
ba: BaConfig = BaConfig()
driver = nonebot.get_driver()
global_config = driver.config
DRAW_DATA_PATH = DATA_PATH / "draw_card"
DRAW_IMAGE_PATH = IMAGE_PATH / "draw_card"
# DRAW_PATH = Path(draw_path) if draw_path else Path("data/draw_card").absolute()
config_path = DRAW_DATA_PATH / "draw_card_config" / "draw_card_config.json"
draw_config: Config = DrawConfig()
DRAW_PATH = DATA_PATH / "draw_card"
config_path = DRAW_PATH / "draw_card_config" / "draw_card_config.json"
draw_config: Config = Config()
for game_flag, game_name in zip(
[
"PRTS_FLAG",
"GENSHIN_FLAG",
"PRETTY_FLAG",
"GUARDIAN_FLAG",
"PCR_FLAG",
"AZUR_FLAG",
"FGO_FLAG",
"ONMYOJI_FLAG",
"PCR_TAI",
"BA_FLAG"
],
["明日方舟", "原神", "赛马娘", "坎公骑冠剑", "公主连结", "碧蓝航线", "命运-冠位指定(FGO)", "阴阳师", "pcr台服卡池","碧蓝档案"],
):
AConfig.add_plugin_config(
"draw_card",
game_flag,
True,
name="游戏抽卡",
help_=f"{game_name} 抽卡开关",
default_value=True,
)
AConfig.add_plugin_config(
"draw_card", "SEMAPHORE", 5, help_=f"异步数据下载数量限制", default_value=5
)
@driver.on_startup
def check_config():
global draw_config
draw_config = Config()
if not config_path.exists():
config_path.parent.mkdir(parents=True, exist_ok=True)
draw_config = DrawConfig()
draw_config = Config()
logger.warning("draw_card:配置文件不存在,已重新生成配置文件.....")
json.dump(
draw_config.dict(),
config_path.open("w", encoding="utf8"),
indent=4,
ensure_ascii=False,
)
with config_path.open("w", encoding="utf8") as fp:
json.dump(
draw_config.dict(),
fp,
indent=4,
ensure_ascii=False,
)
+117 -61
View File
@@ -1,4 +1,12 @@
from typing import Optional, Union
from typing import Optional
from pydantic import BaseModel
import time
class BaseUserCount(BaseModel):
count: int = 1 # 当前抽卡次数
time_: int = time.time() # 抽卡时间,当超过一定时间时将重置抽卡次数
timeout: int = 60 # 超时时间60秒
class DrawCountManager:
@@ -6,7 +14,9 @@ class DrawCountManager:
抽卡统计保底
"""
def __init__(self, game_draw_count_rule: tuple, star2name: tuple):
def __init__(
self, game_draw_count_rule: tuple, star2name: tuple, max_draw_count: int
):
"""
初始化保底统计
@@ -14,26 +24,43 @@ class DrawCountManager:
抽卡保底需要的次数和返回的对应名称,例如星级等
:param game_draw_count_rule:抽卡规则
:param star2name:星级对应的名称
:param max_draw_count:最大累计抽卡次数,当下次单次抽卡超过该次数时将会清空数据
"""
# 只有保底
self._data = {}
self._guarantee_tuple = game_draw_count_rule
self._star2name = star2name
self._max_draw_count = max_draw_count
def increase(self, key: int, value: int = 1):
"""
用户抽卡次数加1
"""
if self._data.get(key) is None:
self._data[key] = {
"count": value,
}
for x in range(len(self._guarantee_tuple)):
self._data[key][f"count_{x}"] = 0
self._data[key] = BaseUserCount()
else:
self._data[key][f"count"] += value
if self._data[key][f"count"] > self._guarantee_tuple[-1]:
self._data[key][f"count"] = self._data[key][f"count"] % self._guarantee_tuple[-1]
self._data[key].count += 1
def get_max_guarantee(self):
"""
获取最大保底抽卡次数
"""
return self._guarantee_tuple[-1]
def get_user_count(self, key: int) -> int:
"""
获取当前抽卡次数
"""
return self._data[key].count
def update_time(self, key: int):
"""
更新抽卡时间
"""
self._data[key].time_ = time.time()
def reset(self, key: int):
"""
@@ -41,73 +68,102 @@ class DrawCountManager:
"""
del self._data[key]
def set_count(self, key: int, type_: int, count: int):
if self._data.get(key):
self._data[key][f"count_{type_}"] = count
def check(self, key: int, *args) -> Optional[Union[str, int]]:
"""
是否保底
"""
pass
def get_user_count(self, key: int, type_: Optional[int] = None) -> int:
"""
获取用户当前抽卡次数
"""
if self._data.get(key):
if type_ is None:
return self._data[key]["count"]
return self._data[key][f"count_{type_}"]
return 0
def record_count(self, key: int, type_: int):
"""
抽出对应星级后记录当前次数
"""
if self._data.get(key):
self._data[key][f"count_{type_}"] = self._data[key]["count"]
class GenshinCountManager(DrawCountManager):
class UserCount(BaseUserCount):
five_index: int = 0 # 获取五星时的抽卡次数
four_index: int = 0 # 获取四星时的抽卡次数
is_up: bool = False # 下次五星是否必定为up
def increase(self, key: int, value: int = 1):
"""
用户抽卡次数加1
"""
if self._data.get(key) is None:
self._data[key] = {
"is_up": False,
"count": value,
}
for x in range(len(self._guarantee_tuple)):
self._data[key][f"count_{x}"] = 0
self._data[key] = self.UserCount()
else:
self._data[key][f"count"] += value
if self._data[key][f"count"] > self._guarantee_tuple[-1]:
self._data[key][f"count"] = self._data[key][f"count"] % 180
self._data[key].count += 1
def set_is_up(self, key: int, value: bool):
if self._data.get(key):
self._data[key]["is_up"] = value
"""
设置下次是否必定up
"""
self._data[key].is_up = value
def is_up(self, key: int) -> bool:
if self._data.get(key):
return self._data[key]["is_up"]
return False
"""
判断该次保底是否必定为up
"""
return self._data[key].is_up
def check(self, key: int, *args) -> Optional[Union[str, int]]:
def get_user_five_index(self, key: int) -> int:
"""
获取用户上次获取五星的次数
"""
return self._data[key].five_index
def get_user_four_index(self, key: int) -> int:
"""
获取用户上次获取四星的次数
"""
return self._data[key].four_index
def mark_five_index(self, key: int):
"""
标记用户该次次数为五星
"""
self._data[key].five_index = self._data[key].count
def mark_four_index(self, key: int):
"""
标记用户该次次数为四星
"""
self._data[key].four_index = self._data[key].count
def check_timeout(self, key: int):
"""
检查用户距离上次抽卡是否超时
"""
if key in self._data.keys() and self._is_timeout(key):
del self._data[key]
def check_count(self, key: int, count: int):
"""
检查用户该次抽卡次数累计是否超过最大限制次数
"""
if (
key in self._data.keys()
and self._data[key].count + count > self._max_draw_count
):
del self._data[key]
def _is_timeout(self, key: int) -> bool:
return time.time() - self._data[key].time_ > self._data[key].timeout
def get_user_guarantee_count(self, key: int) -> int:
return (
self.get_max_guarantee()
- (
(
self._data[key].count % self.get_max_guarantee()
if self._data[key].count > 0
else 0
)
- self._data[key].five_index
)
) % self.get_max_guarantee() or self.get_max_guarantee()
def check(self, key: int) -> Optional[int]:
"""
是否保底
"""
# print(self._data)
if self._data.get(key):
for i in [1, 0]:
count = self._data[key]["count"]
if count - self._data[key][f"count_{i}"] == self._guarantee_tuple[i]:
if i in [2, 1]:
# print("clean four count")
self._data[key][f"count_0"] = self._data[key]['count']
self._data[key][f"count_{i}"] = self._data[key]['count']
return self._star2name[i]
user: GenshinCountManager.UserCount = self._data[key]
if user.count - user.five_index == 90:
user.five_index = 90
return 5
if user.count - user.four_index == 10:
user.four_index = user.count
return 4
return None
-111
View File
@@ -1,111 +0,0 @@
from nonebot.adapters.onebot.v11 import MessageSegment
import random
from .update_game_requests_info import update_requests_info
from .util import generate_img, init_star_rst, BaseData, set_list, get_star, max_card
from .config import DRAW_DATA_PATH, draw_config
from dataclasses import dataclass
from .init_card_pool import init_game_pool
try:
import ujson as json
except ModuleNotFoundError:
import json
ALL_CHAR = []
ALL_CARD = []
@dataclass
class FgoChar(BaseData):
pass
async def fgo_draw(count: int):
# 0 1 2
cnlist = ['★★★★★', '★★★★', '★★★']
obj_list, obj_dict, max_star_list, star_list, max_star_index_list = _format_card_information(count)
rst = init_star_rst(star_list, cnlist, max_star_list, max_star_index_list)
if count > 90:
obj_list = set_list(obj_list)
return MessageSegment.image("base64://" + await generate_img(obj_list, 'fgo', star_list)) \
+ '\n' + rst[:-1] + '\n' + max_card(obj_dict)
async def update_fgo_info():
global ALL_CHAR, ALL_CARD
data, code = await update_requests_info('fgo')
if code == 200:
ALL_CHAR = init_game_pool('fgo', data, FgoChar)
data, code = await update_requests_info('fgo_card')
if code == 200:
ALL_CARD = init_game_pool('fgo_card', data, FgoChar)
async def init_fgo_data():
global ALL_CHAR, ALL_CARD
if draw_config.FGO_FLAG:
with (DRAW_DATA_PATH / 'fgo.json').open('r', encoding='utf8') as f:
fgo_dict = json.load(f)
ALL_CHAR = init_game_pool('fgo', fgo_dict, FgoChar)
with (DRAW_DATA_PATH / 'fgo_card.json').open('r', encoding='utf8') as f:
fgo_dict = json.load(f)
ALL_CARD = init_game_pool('fgo', fgo_dict, FgoChar)
# 抽取卡池
def _get_fgo_card(mode: int = 1):
global ALL_CHAR, ALL_CARD
fgo_config = draw_config.fgo
if mode == 1:
star = get_star([8, 7, 6, 5, 4, 3], [fgo_config.FGO_SERVANT_FIVE_P, fgo_config.FGO_SERVANT_FOUR_P, fgo_config.FGO_SERVANT_THREE_P,
fgo_config.FGO_CARD_FIVE_P, fgo_config.FGO_CARD_FOUR_P, fgo_config.FGO_CARD_THREE_P])
elif mode == 2:
star = get_star([5, 4], [fgo_config.FGO_CARD_FIVE_P, fgo_config.FGO_CARD_FOUR_P])
else:
star = get_star([8, 7, 6], [fgo_config.FGO_SERVANT_FIVE_P, fgo_config.FGO_SERVANT_FOUR_P, fgo_config.FGO_SERVANT_THREE_P])
if star > 5:
type_ = 'servant'
star -= 3
chars = [x for x in ALL_CHAR if x.star == star if not x.limited]
else:
type_ = 'card'
chars = [x for x in ALL_CARD if x.star == star if not x.limited]
return random.choice(chars), 5 - star, type_
# 整理数据
def _format_card_information(count: int):
max_star_lst = [] # 获取的最高星级角色列表
max_index_lst = [] # 获取最高星级角色的次数
star_list = [0, 0, 0]
obj_list = [] # 获取所有角色
obj_dict = {} # 获取角色次数字典
servant_count = 0 # 保底计算
card_count = 0 # 保底计算
for i in range(count):
servant_count += 1
card_count += 1
# 四星卡片保底
if card_count == 9:
obj, code, type_ = _get_fgo_card(2)
# 三星从者保底
elif servant_count == 10:
obj, code, type_ = _get_fgo_card(3)
_count = 0
# 普通抽
else:
obj, code, type_ = _get_fgo_card()
star_list[code] += 1
if type_ == 'card' and code < 2:
card_count = 0
if type_ == 'servant':
servant_count = 0
if code == 0:
max_star_lst.append(obj.name)
max_index_lst.append(i)
try:
obj_dict[obj.name] += 1
except KeyError:
obj_dict[obj.name] = 1
obj_list.append(obj)
return obj_list, obj_dict, max_star_lst, star_list, max_index_lst
-288
View File
@@ -1,288 +0,0 @@
from nonebot.adapters.onebot.v11 import MessageSegment, Message
import random
from .update_game_info import update_info
from .util import (
generate_img,
init_rst,
BaseData,
set_list,
get_star,
init_up_char,
)
from .config import DRAW_DATA_PATH, draw_config
from .count_manager import GenshinCountManager
from dataclasses import dataclass
from .init_card_pool import init_game_pool
from .announcement import GenshinAnnouncement
try:
import ujson as json
except ModuleNotFoundError:
import json
announcement = GenshinAnnouncement()
draw_count_manager = GenshinCountManager((10, 90), ("4", "5"))
# genshin_five = {}
# genshin_count = {}
# genshin_pl_count = {}
ALL_CHAR = []
ALL_ARMS = []
UP_CHAR = []
UP_ARMS = []
_CURRENT_CHAR_POOL_TITLE = ""
_CURRENT_ARMS_POOL_TITLE = ""
POOL_IMG = ""
@dataclass
class GenshinChar(BaseData):
pass
async def genshin_draw(user_id: int, count: int, pool_name: str):
# 0 1 2
star = ["★★★★★", "★★★★", "★★★"]
(
char_list,
five_dict,
star_num_list,
) = _format_card_information(count, user_id, pool_name)
title = ""
up_type = []
up_list = []
if pool_name == "char" and _CURRENT_CHAR_POOL_TITLE:
up_type = UP_CHAR
title = _CURRENT_CHAR_POOL_TITLE
elif pool_name == "arms" and _CURRENT_ARMS_POOL_TITLE:
up_type = UP_ARMS
title = _CURRENT_ARMS_POOL_TITLE
tmp = ""
if up_type:
for x in up_type:
for operator in x.operators:
up_list.append(operator)
if x.star == 5:
tmp += f'五星UP:{" ".join(x.operators)} \n'
elif x.star == 4:
tmp += f'四星UP:{" ".join(x.operators)}'
rst = init_rst(five_dict, star_num_list, star, up_list)
pool_info = f"当前up池:{title}\n{tmp}" if title else ""
if count > 90:
char_list = set_list(char_list)
return (
pool_info
+ "\n"
+ MessageSegment.image(
"base64://" + await generate_img(char_list, "genshin", star_num_list)
)
+ rst
+ f'\n距离保底发还剩 {draw_count_manager.get_user_count(user_id, 1) % 90} 抽'
+ "\n【五星:0.6%,四星:5.1%\n第72抽开始五星概率每抽加0.585%】"
)
async def update_genshin_info():
global ALL_CHAR, ALL_ARMS
url = "https://wiki.biligame.com/ys/角色筛选"
data, code = await update_info(url, "genshin")
if code == 200:
ALL_CHAR = init_game_pool("genshin", data, GenshinChar)
url = "https://wiki.biligame.com/ys/武器图鉴"
data, code = await update_info(
url,
"genshin_arms",
)
if code == 200:
ALL_ARMS = init_game_pool("genshin_arms", data, GenshinChar)
await _genshin_init_up_char()
async def init_genshin_data():
global ALL_CHAR, ALL_ARMS
if draw_config.GENSHIN_FLAG:
if not (DRAW_DATA_PATH / "genshin.json").exists() or not (DRAW_DATA_PATH / "genshin_arms.json").exists():
await update_genshin_info()
else:
with (DRAW_DATA_PATH / "genshin.json").open("r", encoding="utf8") as f:
genshin_dict = json.load(f)
with (DRAW_DATA_PATH / "genshin_arms.json").open("r", encoding="utf8") as f:
genshin_ARMS_dict = json.load(f)
ALL_CHAR = init_game_pool("genshin", genshin_dict, GenshinChar)
ALL_ARMS = init_game_pool("genshin_arms", genshin_ARMS_dict, GenshinChar)
await _genshin_init_up_char()
# 抽取卡池
def _get_genshin_card(mode: int = 1, pool_name: str = "", add: float = 0.0, is_up: bool = False):
"""
mode 1:普通抽 2:四星保底 3:五星保底
"""
global ALL_ARMS, ALL_CHAR, UP_ARMS, UP_CHAR, _CURRENT_ARMS_POOL_TITLE, _CURRENT_CHAR_POOL_TITLE
genshin_config = draw_config.genshin
if mode == 1:
star = get_star(
[5, 4, 3], [genshin_config.GENSHIN_FIVE_P + add, genshin_config.GENSHIN_FOUR_P, genshin_config.GENSHIN_THREE_P]
)
elif mode == 2:
star = get_star([5, 4], [genshin_config.GENSHIN_G_FIVE_P + add, genshin_config.GENSHIN_G_FOUR_P])
else:
star = 5
if pool_name == "char":
data_lst = UP_CHAR
flag = _CURRENT_CHAR_POOL_TITLE
type_all_lst = ALL_CHAR + [
x for x in ALL_ARMS if x.star == star and x.star < 5
]
elif pool_name == "arms":
data_lst = UP_ARMS
flag = _CURRENT_ARMS_POOL_TITLE
type_all_lst = ALL_ARMS + [
x for x in ALL_CHAR if x.star == star and x.star < 5
]
else:
data_lst = ""
flag = ""
type_all_lst = ""
all_lst = ALL_ARMS + ALL_CHAR
# 是否UP
try:
if flag and star > 3:
# 获取up角色列表
up_char_lst = [x.operators for x in data_lst if x.star == star][0]
# print(up_char_lst)
# 成功获取up角色
if random.random() < 0.5 or is_up:
up_char_name = random.choice(up_char_lst)
acquire_char = [x for x in all_lst if x.name == up_char_name][0]
else:
# 无up
all_char_lst = [
x
for x in type_all_lst
if x.star == star and x.name not in up_char_lst and not x.limited
]
acquire_char = random.choice(all_char_lst)
else:
chars = [x for x in all_lst if x.star == star and not x.limited]
acquire_char = random.choice(chars)
except IndexError:
chars = [x for x in all_lst if x.star == star and not x.limited]
acquire_char = random.choice(chars)
return acquire_char, 5 - star
def _format_card_information(_count: int, user_id, pool_name):
char_list = [] # 获取角色列表
star_num_list = [0, 0, 0] # 各个星级数量
five_dict = {} # 五星数量
add = 0.0
pool = UP_CHAR if pool_name == 'char' else UP_ARMS
for _ in range(_count):
draw_count_manager.increase(user_id)
star = draw_count_manager.check(user_id)
if (draw_count_manager.get_user_count(user_id) - draw_count_manager.get_user_count(user_id, 1)) % 90 >= 72:
add += draw_config.genshin.I72_ADD
if star:
star = int(star)
if star == 4:
char, code = _get_genshin_card(2, pool_name, add=add)
draw_count_manager.record_count(user_id, 0)
else:
char, code = _get_genshin_card(3, pool_name, add, draw_count_manager.is_up(user_id))
else:
char, code = _get_genshin_card(1, pool_name, add, draw_count_manager.is_up(user_id))
if code == 0:
add = 0
if not five_dict.get(char.name):
five_dict[char.name] = [draw_count_manager.get_user_count(user_id)]
else:
five_dict[char.name].append(draw_count_manager.get_user_count(user_id) % 90)
draw_count_manager.set_count(user_id, 2, draw_count_manager.get_user_count(user_id, 1))
draw_count_manager.record_count(user_id, 0)
draw_count_manager.record_count(user_id, 1)
if char.name not in [x.operators for x in pool if x.star == 5][0]:
draw_count_manager.set_is_up(user_id, True)
else:
draw_count_manager.set_is_up(user_id, False)
star_num_list[code] += 1
char_list.append(char)
return char_list, five_dict, star_num_list
# if genshin_count.get(user_id) and _count <= 90:
# f_count = genshin_count[user_id]
# else:
# f_count = 0
# if genshin_pl_count.get(user_id) and _count <= 90:
# count = genshin_pl_count[user_id]
# else:
# count = 0
# for i in range(_count):
# count += 1
# f_count += 1
# # 十连保底
# if count == 10 and f_count != 90:
# if f_count >= 72:
# add += I72_ADD
# char, code = _get_genshin_card(2, pool_name, add=add)
# count = 0
# # 大保底
# elif f_count == 90:
# char, code = _get_genshin_card(3, pool_name)
# else:
# if f_count >= 72:
# add += I72_ADD
# char, code = _get_genshin_card(pool_name=pool_name, add=add)
# if code == 1:
# count = 0
# star_list[code] += 1
# if code == 0:
# if _count <= 90:
# genshin_five[user_id] = f_count
# add = 0.0
# f_count = 0
# five_list.append(char.name)
# five_index_list.append(i)
# try:
# five_dict[char.name] += 1
# except KeyError:
# five_dict[char.name] = 1
# char_list.append(char)
# if _count <= 90:
# genshin_count[user_id] = f_count
# genshin_pl_count[user_id] = count
# return char_list, five_list, five_index_list, five_dict, star_list
def reset_count(user_id: int):
draw_count_manager.reset(user_id)
# 获取up和概率
async def _genshin_init_up_char():
global _CURRENT_CHAR_POOL_TITLE, _CURRENT_ARMS_POOL_TITLE, UP_CHAR, UP_ARMS, POOL_IMG
(
_CURRENT_CHAR_POOL_TITLE,
_CURRENT_ARMS_POOL_TITLE,
POOL_IMG,
UP_CHAR,
UP_ARMS,
) = await init_up_char(announcement)
async def reload_genshin_pool():
await _genshin_init_up_char()
return Message(
f"当前UP池子:{_CURRENT_CHAR_POOL_TITLE} & {_CURRENT_ARMS_POOL_TITLE} {POOL_IMG}"
)
-159
View File
@@ -1,159 +0,0 @@
from nonebot.adapters.onebot.v11 import MessageSegment, Message
from .update_game_info import update_info
from .util import init_star_rst, generate_img, max_card, BaseData,\
set_list, get_star, format_card_information, init_up_char
import random
from .config import DRAW_DATA_PATH, draw_config
from dataclasses import dataclass
from .init_card_pool import init_game_pool
from .announcement import GuardianAnnouncement
try:
import ujson as json
except ModuleNotFoundError:
import json
announcement = GuardianAnnouncement()
ALL_CHAR = []
ALL_ARMS = []
_CURRENT_CHAR_POOL_TITLE = ''
_CURRENT_ARMS_POOL_TITLE = ''
UP_CHAR = []
UP_ARMS = []
POOL_IMG = ''
@dataclass
class GuardianChar(BaseData):
pass
@dataclass
class GuardianArms(BaseData):
pass
async def guardian_draw(count: int, pool_name):
if pool_name == 'arms':
cnlist = ['★★★★★', '★★★★', '★★★', '★★']
star_list = [0, 0, 0, 0]
else:
cnlist = ['★★★', '★★', '★']
star_list = [0, 0, 0]
title = ''
up_type = []
up_list = []
if pool_name == 'char' and _CURRENT_CHAR_POOL_TITLE:
up_type = UP_CHAR
title = _CURRENT_CHAR_POOL_TITLE
elif pool_name == 'arms' and _CURRENT_ARMS_POOL_TITLE:
up_type = UP_ARMS
title = _CURRENT_ARMS_POOL_TITLE
tmp = ''
if up_type:
for x in up_type:
for operator in x.operators:
up_list.append(operator)
if pool_name == 'char':
if x.star == 3:
tmp += f'三星UP:{" ".join(x.operators)} \n'
else:
if x.star == 5:
tmp += f'五星UP:{" ".join(x.operators)}'
obj_list, obj_dict, max_list, star_list, max_index_list = format_card_information(count, star_list,
_get_guardian_card, pool_name)
rst = init_star_rst(star_list, cnlist, max_list, max_index_list, up_list)
pool_info = f'当前up池:{title}\n{tmp}' if title else ''
if count > 90:
obj_list = set_list(obj_list)
return pool_info + '\n' + MessageSegment.image(
"base64://" + await generate_img(obj_list, 'guardian', star_list)) \
+ '\n' + rst[:-1] + '\n' + max_card(obj_dict)
async def update_guardian_info():
global ALL_CHAR, ALL_ARMS
url = 'https://wiki.biligame.com/gt/英雄筛选表'
data, code = await update_info(url, 'guardian')
if code == 200:
ALL_CHAR = init_game_pool('guardian', data, GuardianChar)
url = 'https://wiki.biligame.com/gt/武器'
tmp, code_1 = await update_info(url, 'guardian_arms')
url = 'https://wiki.biligame.com/gt/盾牌'
data, code_2 = await update_info(url, 'guardian_arms')
if code_1 == 200 and code_2 == 200:
data.update(tmp)
ALL_ARMS = init_game_pool('guardian_arms', data, GuardianArms)
await _guardian_init_up_char()
async def init_guardian_data():
global ALL_CHAR, ALL_ARMS
if draw_config.GUARDIAN_FLAG:
if not (DRAW_DATA_PATH / 'guardian.json').exists() or not (DRAW_DATA_PATH / 'guardian_arms.json').exists():
await update_guardian_info()
else:
with (DRAW_DATA_PATH / 'guardian.json').open('r', encoding='utf8') as f:
guardian_char_dict = json.load(f)
with (DRAW_DATA_PATH / 'guardian_arms.json').open('r', encoding='utf8') as f:
guardian_arms_dict = json.load(f)
ALL_CHAR = init_game_pool('guardian', guardian_char_dict, GuardianChar)
ALL_ARMS = init_game_pool('guardian_arms', guardian_arms_dict, GuardianArms)
await _guardian_init_up_char()
# 抽取卡池
def _get_guardian_card(pool_name: str = '', mode: int = 1):
global ALL_ARMS, ALL_CHAR, UP_ARMS, UP_CHAR, _CURRENT_ARMS_POOL_TITLE, _CURRENT_CHAR_POOL_TITLE
guardian_config = draw_config.guardian
if pool_name == 'char':
if mode == 1:
star = get_star([3, 2, 1], [guardian_config.GUARDIAN_THREE_CHAR_P, guardian_config.GUARDIAN_TWO_CHAR_P, guardian_config.GUARDIAN_ONE_CHAR_P])
else:
star = get_star([3, 2], [guardian_config.GUARDIAN_THREE_CHAR_P, guardian_config.GUARDIAN_TWO_CHAR_P])
up_lst = UP_CHAR
flag = _CURRENT_CHAR_POOL_TITLE
_max_star = 3
all_data = ALL_CHAR
else:
if mode == 1:
star = get_star([5, 4, 3, 2], [guardian_config.GUARDIAN_FIVE_ARMS_P, guardian_config.GUARDIAN_FOUR_ARMS_P,
guardian_config.GUARDIAN_THREE_ARMS_P, guardian_config.GUARDIAN_TWO_ARMS_P])
else:
star = get_star([5, 4], [guardian_config.GUARDIAN_FIVE_ARMS_P, guardian_config.GUARDIAN_FOUR_ARMS_P])
up_lst = UP_ARMS
flag = _CURRENT_ARMS_POOL_TITLE
_max_star = 5
all_data = ALL_ARMS
# 是否UP
if flag and star == _max_star and pool_name:
# 获取up角色列表
up_char_lst = [x.operators for x in up_lst if x.star == star][0]
# 成功获取up角色
if random.random() < 0.5:
up_char_name = random.choice(up_char_lst)
acquire_char = [x for x in all_data if x.name == up_char_name][0]
else:
# 无up
all_char_lst = [x for x in all_data if x.star == star and x.name not in up_char_lst and not x.limited]
acquire_char = random.choice(all_char_lst)
else:
chars = [x for x in all_data if x.star == star and not x.limited]
acquire_char = random.choice(chars)
return acquire_char, _max_star - star
# 获取up和概率
async def _guardian_init_up_char():
global _CURRENT_CHAR_POOL_TITLE, _CURRENT_ARMS_POOL_TITLE, UP_CHAR, UP_ARMS, POOL_IMG
_CURRENT_CHAR_POOL_TITLE, _CURRENT_ARMS_POOL_TITLE, POOL_IMG, UP_CHAR, UP_ARMS = await init_up_char(announcement)
async def reload_guardian_pool():
await _guardian_init_up_char()
return Message(f'当前UP池子:{_CURRENT_CHAR_POOL_TITLE} & {_CURRENT_ARMS_POOL_TITLE}')
+317
View File
@@ -0,0 +1,317 @@
import random
import dateparser
from lxml import etree
from typing import List, Optional
from urllib.parse import unquote
from pydantic import ValidationError
from nonebot.log import logger
from nonebot.adapters.onebot.v11 import Message
from utils.message_builder import image
from .base_handle import BaseHandle, BaseData, UpEvent as _UpEvent, UpChar as _UpChar
from ..config import draw_config
from ..util import remove_prohibited_str, cn2py
from utils.image_utils import BuildImage
import asyncio
try:
import ujson as json
except ModuleNotFoundError:
import json
class AzurChar(BaseData):
type_: str # 舰娘类型
@property
def star_str(self) -> str:
return ["白", "蓝", "紫", "金"][self.star - 1]
class UpChar(_UpChar):
type_: str # 舰娘类型
class UpEvent(_UpEvent):
up_char: List[UpChar] # up对象
class AzurHandle(BaseHandle[AzurChar]):
def __init__(self):
super().__init__("azur", "碧蓝航线")
self.max_star = 4
self.config = draw_config.azur
self.ALL_CHAR: List[AzurChar] = []
self.UP_EVENT: Optional[UpEvent] = None
def get_card(self, pool_name: str, **kwargs) -> AzurChar:
if pool_name == "轻型":
type_ = ["驱逐", "轻巡", "维修"]
elif pool_name == "重型":
type_ = ["重巡", "战列", "战巡", "重炮"]
else:
type_ = ["维修", "潜艇", "重巡", "轻航", "航母"]
up_pool_flag = pool_name == "活动"
# Up
up_ship = [x for x in self.UP_EVENT.up_char if x.zoom > 0]
# print(up_ship)
acquire_char = None
if up_ship and up_pool_flag:
up_zoom = [(0, up_ship[0].zoom / 100)]
# 初始化概率
cur_ = up_ship[0].zoom / 100
for i in range(len(up_ship)):
try:
up_zoom.append((cur_, cur_ + up_ship[i+1].zoom / 100))
cur_ += up_ship[i+1].zoom / 100
except IndexError:
pass
rand = random.random()
# 抽取up
for i, zoom in enumerate(up_zoom):
if zoom[0] <= rand <= zoom[1]:
try:
acquire_char = [x for x in self.ALL_CHAR if x.name == up_ship[i].name][0]
except IndexError:
pass
# 没有up或者未抽取到up
if not acquire_char:
star = self.get_star(
[4, 3, 2, 1],
[
self.config.AZUR_FOUR_P,
self.config.AZUR_THREE_P,
self.config.AZUR_TWO_P,
self.config.AZUR_ONE_P,
],
)
acquire_char = random.choice([
x
for x in self.ALL_CHAR
if x.star == star and x.type_ in type_ and not x.limited
])
return acquire_char
# async def draw(self, count: int, **kwargs) -> Message:
# return await asyncio.get_event_loop().run_in_executor(None, self._draw, count)
async def draw(self, count: int, **kwargs) -> Message:
index2card = self.get_cards(count, **kwargs)
cards = [card[0] for card in index2card]
up_list = [x.name for x in self.UP_EVENT.up_char] if self.UP_EVENT.up_char else []
result = self.format_result(index2card, **{**kwargs, "up_list": up_list})
return image(b64=self.generate_img(cards).pic2bs4()) + result
def generate_card_img(self, card: AzurChar) -> BuildImage:
sep_w = 5
sep_t = 5
sep_b = 20
w = 100
h = 100
bg = BuildImage(w + sep_w * 2, h + sep_t + sep_b, font="msyh.ttf")
frame_path = self.img_path / f"{card.star}_star.png"
frame = BuildImage(w, h, background=frame_path)
img_path = self.img_path / f"{cn2py(card.name)}.png"
img = BuildImage(w, h, background=img_path)
# 加圆角
img.circle_corner(6)
bg.paste(img, (sep_w, sep_t), alpha=True)
bg.paste(frame, (sep_w, sep_t), alpha=True)
bg.circle_corner(6)
# 加名字
text = card.name[:6] + "..." if len(card.name) > 7 else card.name
text_w, text_h = bg.getsize(text)
bg.text(
(sep_w + (w - text_w) / 2, h + sep_t + (sep_b - text_h) / 2),
text,
fill=["#808080", "#3b8bff", "#8000ff", "#c90", "#ee494c"][card.star - 1],
)
return bg
def _init_data(self):
self.ALL_CHAR = [
AzurChar(
name=value["名称"],
star=int(value["星级"]),
limited="可以建造" not in value["获取途径"],
type_=value["类型"],
)
for value in self.load_data().values()
]
self.load_up_char()
def load_up_char(self):
try:
data = self.load_data(f"draw_card_up/{self.game_name}_up_char.json")
self.UP_EVENT = UpEvent.parse_obj(data.get("char", {}))
except ValidationError:
logger.warning(f"{self.game_name}_up_char 解析出错")
def dump_up_char(self):
if self.UP_EVENT:
data = {"char": json.loads(self.UP_EVENT.json())}
self.dump_data(data, f"draw_card_up/{self.game_name}_up_char.json")
async def _update_info(self):
info = {}
# 更新图鉴
url = "https://wiki.biligame.com/blhx/舰娘图鉴"
result = await self.get_url(url)
if not result:
logger.warning(f"更新 {self.game_name_cn} 出错")
return
dom = etree.HTML(result, etree.HTMLParser())
contents = dom.xpath(
"//div[@class='resp-tabs-container']/div[@class='resp-tab-content']"
)
for index, content in enumerate(contents):
char_list = content.xpath("./table/tbody/tr[2]/td/div/div/div/div")
for char in char_list:
try:
name = char.xpath("./a/@title")[0]
frame = char.xpath("./div/a/img/@alt")[0]
avatar = char.xpath("./a/img/@srcset")[0]
except IndexError:
continue
member_dict = {
"名称": remove_prohibited_str(name),
"头像": unquote(str(avatar).split(" ")[-2]),
"星级": self.parse_star(frame),
"类型": self.parse_type(index),
}
info[member_dict["名称"]] = member_dict
# 更新额外信息
for key in info.keys():
url = f"https://wiki.biligame.com/blhx/{key}"
result = await self.get_url(url)
if not result:
info[key]["获取途径"] = []
logger.warning(f"{self.game_name_cn} 获取额外信息错误 {key}")
continue
try:
dom = etree.HTML(result, etree.HTMLParser())
time = dom.xpath(
"//table[@class='wikitable sv-general']/tbody[1]/tr[4]/td[2]//text()"
)[0]
sources = []
if "无法建造" in time:
sources.append("无法建造")
elif "活动已关闭" in time:
sources.append("活动限定")
else:
sources.append("可以建造")
info[key]["获取途径"] = sources
except IndexError:
info[key]["获取途径"] = []
logger.warning(f"{self.game_name_cn} 获取额外信息错误 {key}")
self.dump_data(info)
logger.info(f"{self.game_name_cn} 更新成功")
# 下载头像
for value in info.values():
await self.download_img(value["头像"], value["名称"])
# 下载头像框
idx = 1
BLHX_URL = "https://patchwiki.biligame.com/images/blhx"
for url in [
"/1/15/pxho13xsnkyb546tftvh49etzdh74cf.png",
"/a/a9/k8t7nx6c8pan5vyr8z21txp45jxeo66.png",
"/a/a5/5whkzvt200zwhhx0h0iz9qo1kldnidj.png",
"/a/a2/ptog1j220x5q02hytpwc8al7f229qk9.png",
"/6/6d/qqv5oy3xs40d3055cco6bsm0j4k4gzk.png",
]:
await self.download_img(BLHX_URL + url, f"{idx}_star")
idx += 1
await self.update_up_char()
@staticmethod
def parse_star(star: str) -> int:
if star in ["舰娘头像外框普通.png", "舰娘头像外框白色.png"]:
return 1
elif star in ["舰娘头像外框稀有.png", "舰娘头像外框蓝色.png"]:
return 2
elif star in ["舰娘头像外框精锐.png", "舰娘头像外框紫色.png"]:
return 3
elif star in ["舰娘头像外框超稀有.png", "舰娘头像外框金色.png"]:
return 4
elif star in ["舰娘头像外框海上传奇.png", "舰娘头像外框彩色.png"]:
return 5
elif star in [
"舰娘头像外框最高方案.png",
"舰娘头像外框决战方案.png",
"舰娘头像外框超稀有META.png",
"舰娘头像外框精锐META.png",
]:
return 6
else:
return 6
@staticmethod
def parse_type(index: int) -> str:
azur_types = [
"驱逐",
"轻巡",
"重巡",
"超巡",
"战巡",
"战列",
"航母",
"航站",
"轻航",
"重炮",
"维修",
"潜艇",
"运输",
]
try:
return azur_types[index]
except IndexError:
return azur_types[0]
async def update_up_char(self):
url = "https://wiki.biligame.com/blhx/游戏活动表"
result = await self.get_url(url)
if not result:
logger.warning(f"{self.game_name_cn}获取活动表出错")
return
try:
dom = etree.HTML(result, etree.HTMLParser())
dd = dom.xpath("//div[@class='timeline2']/dl/dd/a")[0]
url = "https://wiki.biligame.com" + dd.xpath("./@href")[0]
title = dd.xpath("string(.)")
result = await self.get_url(url)
if not result:
logger.warning(f"{self.game_name_cn}获取活动页面出错")
return
dom = etree.HTML(result, etree.HTMLParser())
timer = dom.xpath("//span[@class='eventTimer']")[0]
start_time = dateparser.parse(timer.xpath("./@data-start")[0])
end_time = dateparser.parse(timer.xpath("./@data-end")[0])
ships = dom.xpath("//table[@class='shipinfo']")
up_chars = []
for ship in ships:
name = ship.xpath("./tbody/tr/td[2]/p/a/@title")[0]
type_ = ship.xpath("./tbody/tr/td[2]/p/small/text()")[0] # 舰船类型
try:
p = float(str(ship.xpath(".//sup/text()")[0]).strip("%"))
except (IndexError, ValueError):
p = 0
star = self.parse_star(
ship.xpath("./tbody/tr/td[1]/div/div/div/a/img/@alt")[0]
)
up_chars.append(UpChar(name=name, star=star, limited=False, zoom=p, type_=type_))
self.UP_EVENT = UpEvent(
title=title,
pool_img="",
start_time=start_time,
end_time=end_time,
up_char=up_chars,
)
self.dump_up_char()
except Exception as e:
logger.warning(f"{self.game_name_cn}UP更新出错 {type(e)}:{e}")
async def _reload_pool(self) -> Optional[Message]:
await self.update_up_char()
self.load_up_char()
if self.UP_EVENT:
return Message(f"重载成功!\n当前活动:{self.UP_EVENT.title}")
+150
View File
@@ -0,0 +1,150 @@
import random
from lxml import etree
from typing import List, Tuple
from PIL import ImageDraw
from urllib.parse import unquote
from nonebot.log import logger
from .base_handle import BaseHandle, BaseData
from ..config import draw_config
from ..util import remove_prohibited_str, cn2py, load_font
from utils.image_utils import BuildImage
class BaChar(BaseData):
pass
class BaHandle(BaseHandle[BaChar]):
def __init__(self):
super().__init__("ba", "碧蓝档案")
self.max_star = 3
self.config = draw_config.ba
self.ALL_CHAR: List[BaChar] = []
def get_card(self, mode: int = 1) -> BaChar:
if mode == 2:
star = self.get_star(
[3, 2], [self.config.BA_THREE_P, self.config.BA_G_TWO_P]
)
else:
star = self.get_star(
[3, 2, 1],
[self.config.BA_THREE_P, self.config.BA_TWO_P, self.config.BA_ONE_P],
)
chars = [x for x in self.ALL_CHAR if x.star == star and not x.limited]
return random.choice(chars)
def get_cards(self, count: int, **kwargs) -> List[Tuple[BaChar, int]]:
card_list = []
card_count = 0 # 保底计算
for i in range(count):
card_count += 1
# 十连保底
if card_count == 10:
card = self.get_card(2)
card_count = 0
else:
card = self.get_card(1)
if card.star > self.max_star - 2:
card_count = 0
card_list.append((card, i + 1))
return card_list
def generate_card_img(self, card: BaChar) -> BuildImage:
sep_w = 5
sep_h = 5
star_h = 15
img_w = 90
img_h = 100
font_h = 20
bar_h = 20
bar_w = 90
bg = BuildImage(img_w + sep_w * 2, img_h + font_h + sep_h * 2, color="#EFF2F5")
img_path = str(self.img_path / f"{cn2py(card.name)}.png")
img = BuildImage(img_w, img_h, background=img_path)
bar = BuildImage(bar_w, bar_h, color="#6495ED")
bg.paste(img, (sep_w, sep_h), alpha=True)
bg.paste(bar, (sep_w, img_h - bar_h + sep_h), alpha=True)
if (card.star == 1):
star_path = str(self.img_path / "star-1.png")
star_w = 15
elif (card.star == 2):
star_path = str(self.img_path / "star-2.png")
star_w = 30
else:
star_path = str(self.img_path / "star-3.png")
star_w = 45
star = BuildImage(star_w, star_h, background=star_path)
bg.paste(star, (img_w // 2 - 15 * (card.star - 1) // 2, img_h - star_h), alpha=True)
text = card.name[:5] + "..." if len(card.name) > 6 else card.name
font = load_font(fontsize=14)
text_w, text_h = font.getsize(text)
draw = ImageDraw.Draw(bg.markImg)
draw.text(
(sep_w + (img_w - text_w) / 2, sep_h + img_h + (font_h - text_h) / 2),
text,
font=font,
fill="gray",
)
return bg
def _init_data(self):
self.ALL_CHAR = [
BaChar(
name=value["名称"],
star=int(value["星级"]),
limited=True if "(" in key else False,
)
for key, value in self.load_data().items()
]
def title2star(self, title: int):
if title == 'Star-3.png':
return 3
elif title == 'Star-2.png':
return 2
else:
return 1
async def _update_info(self):
info = {}
url = "https://wiki.biligame.com/bluearchive/学生筛选"
result = await self.get_url(url)
if not result:
logger.warning(f"更新 {self.game_name_cn} 出错")
return
else:
dom = etree.HTML(result, etree.HTMLParser())
char_list = dom.xpath("//div[@class='filters']/table[2]/tbody/tr")
for char in char_list:
try:
name = char.xpath("./td[2]/a/div/text()")[0]
avatar = char.xpath("./td[1]/div/div/a/img/@data-src")[0]
star_pic = char.xpath("./td[4]/img/@alt")[0]
except IndexError:
continue
member_dict = {
"头像": unquote(str(avatar)),
"名称": remove_prohibited_str(name),
"星级": self.title2star(star_pic),
}
info[member_dict["名称"]] = member_dict
self.dump_data(info)
logger.info(f"{self.game_name_cn} 更新成功")
# 下载头像
for value in info.values():
await self.download_img(value["头像"], value["名称"])
# 下载星星
await self.download_img(
"https://patchwiki.biligame.com/images/bluearchive/thumb/e/e0/82nj2x9sxko473g7782r14fztd4zyky.png/15px-Star-1.png",
"star-1",
)
await self.download_img(
"https://patchwiki.biligame.com/images/bluearchive/thumb/0/0b/msaff2g0zk6nlyl1rrn7n1ri4yobcqc.png/30px-Star-2.png",
"star-2",
)
await self.download_img(
"https://patchwiki.biligame.com/images/bluearchive/thumb/8/8a/577yv79x1rwxk8efdccpblo0lozl158.png/46px-Star-3.png",
"star-3"
)
+298
View File
@@ -0,0 +1,298 @@
import math
import random
import aiohttp
import asyncio
import aiofiles
from PIL import Image
from datetime import datetime
from pydantic import BaseModel, Extra
from asyncio.exceptions import TimeoutError
from typing import Dict, List, Optional, TypeVar, Generic, Tuple
from nonebot.adapters.onebot.v11 import Message
from configs.path_config import IMAGE_PATH
from utils.message_builder import image
from nonebot.log import logger
import asyncio
try:
import ujson as json
except ModuleNotFoundError:
import json
from utils.image_utils import BuildImage
from ..config import DRAW_PATH, draw_config
from ..util import cn2py, circled_number
class BaseData(BaseModel, extra=Extra.ignore):
name: str # 名字
star: int # 星级
limited: bool # 限定
def __eq__(self, other: "BaseData"):
return self.name == other.name
def __hash__(self):
return hash(self.name)
@property
def star_str(self) -> str:
return "".join(["★" for _ in range(self.star)])
class UpChar(BaseData):
zoom: float # up提升倍率
class UpEvent(BaseModel):
title: str # up池标题
pool_img: str # up池封面
start_time: Optional[datetime] # 开始时间
end_time: Optional[datetime] # 结束时间
up_char: List[UpChar] # up对象
TC = TypeVar("TC", bound="BaseData")
class BaseHandle(Generic[TC]):
def __init__(self, game_name: str, game_name_cn: str, game_card_color: str = "#ffffff"):
self.game_name = game_name
self.game_name_cn = game_name_cn
self.max_star = 1 # 最大星级
self.data_path = DRAW_PATH
self.img_path = IMAGE_PATH / f"draw_card/{self.game_name}"
self.up_path = DRAW_PATH / "draw_card_up"
self.img_path.mkdir(parents=True, exist_ok=True)
self.up_path.mkdir(parents=True, exist_ok=True)
self.data_files: List[str] = [f"{self.game_name}.json"]
self.game_card_color: str = game_card_color
async def draw(self, count: int, **kwargs) -> Message:
return await asyncio.get_event_loop().run_in_executor(None, self._draw, count)
def _draw(self, count: int, **kwargs) -> Message:
index2card = self.get_cards(count, **kwargs)
cards = [card[0] for card in index2card]
result = self.format_result(index2card)
return image(b64=self.generate_img(cards).pic2bs4()) + result
# 抽取卡池
def get_card(self, **kwargs) -> TC:
raise NotImplementedError
def get_cards(self, count: int, **kwargs) -> List[Tuple[TC, int]]:
return [(self.get_card(**kwargs), i) for i in range(count)]
# 获取星级
@staticmethod
def get_star(star_list: List[int], probability_list: List[float]) -> int:
return random.choices(star_list, weights=probability_list, k=1)[0]
def format_result(self, index2card: List[Tuple[TC, int]], **kwargs) -> str:
card_list = [card[0] for card in index2card]
results = [
self.format_star_result(card_list, **kwargs),
self.format_max_star(index2card, **kwargs),
self.format_max_card(card_list, **kwargs),
]
results = [rst for rst in results if rst]
return "\n".join(results)
def format_star_result(self, card_list: List[TC], **kwargs) -> str:
star_dict: Dict[str, int] = {} # 记录星级及其次数
card_list_sorted = sorted(card_list, key=lambda c: c.star, reverse=True)
for card in card_list_sorted:
try:
star_dict[card.star_str] += 1
except KeyError:
star_dict[card.star_str] = 1
rst = ""
for star_str, count in star_dict.items():
rst += f"[{star_str}×{count}] "
return rst.strip()
def format_max_star(
self, card_list: List[Tuple[TC, int]], up_list: List[str] = [], **kwargs
) -> str:
up_list = up_list or kwargs.get("up_list", [])
rst = ""
for card, index in card_list:
if card.star == self.max_star:
if card.name in up_list:
rst += f"第 {index} 抽获取UP {card.name}\n"
else:
rst += f"第 {index} 抽获取 {card.name}\n"
return rst.strip()
def format_max_card(self, card_list: List[TC], **kwargs) -> str:
card_dict: Dict[TC, int] = {} # 记录卡牌抽取次数
for card in card_list:
try:
card_dict[card] += 1
except KeyError:
card_dict[card] = 1
max_count = max(card_dict.values())
max_card = list(card_dict.keys())[list(card_dict.values()).index(max_count)]
if max_count <= 1:
return ""
return f"抽取到最多的是{max_card.name},共抽取了{max_count}次"
def generate_img(
self,
cards: List[TC],
num_per_line: int = 5,
max_per_line: Tuple[int, int] = (40, 10),
) -> BuildImage:
"""
生成统计图片
:param cards: 卡牌列表
:param num_per_line: 单行角色显示数量
:param max_per_line: 当card_list超过一定数值时,更改单行数量
"""
if len(cards) > max_per_line[0]:
num_per_line = max_per_line[1]
if len(cards) > 90:
card_dict: Dict[TC, int] = {} # 记录卡牌抽取次数
for card in cards:
try:
card_dict[card] += 1
except KeyError:
card_dict[card] = 1
card_list = list(card_dict)
num_list = list(card_dict.values())
else:
card_list = cards
num_list = [1] * len(cards)
card_imgs: List[BuildImage] = []
for card, num in zip(card_list, num_list):
card_img = self.generate_card_img(card)
# 数量 > 1 时加数字上标
if num > 1:
label = circled_number(num)
label_w = int(min(card_img.w, card_img.h) / 7)
label = label.resize(
(
int(label_w * label.width / label.height),
label_w,
),
Image.ANTIALIAS,
)
card_img.paste(label, alpha=True)
card_imgs.append(card_img)
img_w = card_imgs[0].w
img_h = card_imgs[0].h
if len(card_imgs) < num_per_line:
w = img_w * len(card_imgs)
else:
w = img_w * num_per_line
h = img_h * math.ceil(len(card_imgs) / num_per_line)
img = BuildImage(w, h, img_w, img_h, color=self.game_card_color)
for card_img in card_imgs:
img.paste(card_img)
return img
def generate_card_img(self, card: TC) -> BuildImage:
img = str(self.img_path / f"{cn2py(card.name)}.png")
return BuildImage(100, 100, background=img)
def load_data(self, filename: str = "") -> dict:
if not filename:
filename = f"{self.game_name}.json"
filepath = self.data_path / filename
if not filepath.exists():
return {}
with filepath.open("r", encoding="utf8") as f:
return json.load(f)
def dump_data(self, data: dict, filename: str = ""):
if not filename:
filename = f"{self.game_name}.json"
filepath = self.data_path / filename
with filepath.open("w", encoding="utf8") as f:
json.dump(data, f, ensure_ascii=False, indent=4)
def data_exists(self) -> bool:
for file in self.data_files:
if not (self.data_path / file).exists():
return False
return True
def _init_data(self):
raise NotImplementedError
def init_data(self):
try:
self._init_data()
except Exception as e:
logger.warning(f"{self.game_name_cn} 导入角色数据错误:{type(e)}:{e}")
async def _update_info(self):
raise NotImplementedError
def client(self) -> aiohttp.ClientSession:
headers = {
"User-Agent": '"Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 5.1; TencentTraveler 4.0)"'
}
return aiohttp.ClientSession(headers=headers)
async def update_info(self):
try:
async with asyncio.Semaphore(draw_config.SEMAPHORE):
async with self.client() as session:
self.session = session
await self._update_info()
except Exception as e:
logger.warning(f"{self.game_name_cn} 更新数据错误:{type(e)}:{e}")
self.init_data()
async def get_url(self, url: str) -> str:
result = ""
retry = 5
for i in range(retry):
try:
async with self.session.get(url, timeout=10) as response:
result = await response.text()
break
except TimeoutError:
logger.warning(f"访问 {url} 超时, 重试 {i + 1}/{retry}")
await asyncio.sleep(1)
return result
async def download_img(self, url: str, name: str) -> bool:
img_path = self.img_path / f"{cn2py(name)}.png"
if img_path.exists():
return True
try:
async with self.session.get(url, timeout=10) as response:
async with aiofiles.open(str(img_path), "wb") as f:
await f.write(await response.read())
return True
except TimeoutError:
logger.warning(f"下载 {self.game_name_cn} 图片超时,名称:{name},url:{url}")
return False
except:
logger.warning(f"下载 {self.game_name_cn} 链接错误,名称:{name},url:{url}")
return False
async def _reload_pool(self) -> Optional[Message]:
return None
async def reload_pool(self) -> Optional[Message]:
try:
async with self.client() as session:
self.session = session
return await self._reload_pool()
except Exception as e:
logger.warning(f"{self.game_name_cn} 重载UP池错误:{type(e)}:{e}")
def reset_count(self, user_id: int) -> bool:
return False
+221
View File
@@ -0,0 +1,221 @@
import random
from lxml import etree
from typing import List, Tuple
from PIL import ImageDraw
from nonebot.log import logger
try:
import ujson as json
except ModuleNotFoundError:
import json
from .base_handle import BaseHandle, BaseData
from ..config import draw_config
from ..util import remove_prohibited_str, cn2py, load_font
from utils.image_utils import BuildImage
class FgoData(BaseData):
pass
class FgoChar(FgoData):
pass
class FgoCard(FgoData):
pass
class FgoHandle(BaseHandle[FgoData]):
def __init__(self):
super().__init__("fgo", "命运-冠位指定")
self.data_files.append("fgo_card.json")
self.max_star = 5
self.config = draw_config.fgo
self.ALL_CHAR: List[FgoChar] = []
self.ALL_CARD: List[FgoCard] = []
def get_card(self, mode: int = 1) -> FgoData:
if mode == 1:
star = self.get_star(
[8, 7, 6, 5, 4, 3],
[
self.config.FGO_SERVANT_FIVE_P,
self.config.FGO_SERVANT_FOUR_P,
self.config.FGO_SERVANT_THREE_P,
self.config.FGO_CARD_FIVE_P,
self.config.FGO_CARD_FOUR_P,
self.config.FGO_CARD_THREE_P,
],
)
elif mode == 2:
star = self.get_star(
[5, 4], [self.config.FGO_CARD_FIVE_P, self.config.FGO_CARD_FOUR_P]
)
else:
star = self.get_star(
[8, 7, 6],
[
self.config.FGO_SERVANT_FIVE_P,
self.config.FGO_SERVANT_FOUR_P,
self.config.FGO_SERVANT_THREE_P,
],
)
if star > 5:
star -= 3
chars = [x for x in self.ALL_CHAR if x.star == star and not x.limited]
else:
chars = [x for x in self.ALL_CARD if x.star == star and not x.limited]
return random.choice(chars)
def get_cards(self, count: int, **kwargs) -> List[Tuple[FgoData, int]]:
card_list = [] # 获取所有角色
servant_count = 0 # 保底计算
card_count = 0 # 保底计算
for i in range(count):
servant_count += 1
card_count += 1
if card_count == 9: # 四星卡片保底
mode = 2
elif servant_count == 10: # 三星从者保底
mode = 3
else: # 普通抽
mode = 1
card = self.get_card(mode)
if isinstance(card, FgoCard) and card.star > self.max_star - 2:
card_count = 0
if isinstance(card, FgoChar):
servant_count = 0
card_list.append((card, i + 1))
return card_list
def generate_card_img(self, card: FgoData) -> BuildImage:
sep_w = 5
sep_t = 5
sep_b = 20
w = 128
h = 140
bg = BuildImage(w + sep_w * 2, h + sep_t + sep_b)
img_path = str(self.img_path / f"{cn2py(card.name)}.png")
img = BuildImage(w, h, background=img_path)
bg.paste(img, (sep_w, sep_t), alpha=True)
# 加名字
text = card.name[:6] + "..." if len(card.name) > 7 else card.name
font = load_font(fontsize=16)
text_w, text_h = font.getsize(text)
draw = ImageDraw.Draw(bg.markImg)
draw.text(
(sep_w + (w - text_w) / 2, h + sep_t + (sep_b - text_h) / 2),
text,
font=font,
fill="gray",
)
return bg
def _init_data(self):
self.ALL_CHAR = [
FgoChar(
name=value["名称"],
star=int(value["星级"]),
limited=True
if not ("圣晶石召唤" in value["入手方式"] or "圣晶石召唤(Story卡池)" in value["入手方式"])
else False,
)
for value in self.load_data().values()
]
self.ALL_CARD = [
FgoCard(name=value["名称"], star=int(value["星级"]), limited=False)
for value in self.load_data("fgo_card.json").values()
]
async def _update_info(self):
# fgo.json
fgo_info = {}
for i in range(500):
url = f"http://fgo.vgtime.com/servant/ajax?card=&wd=&ids=&sort=12777&o=desc&pn={i}"
result = await self.get_url(url)
if not result:
logger.warning(f"更新 {self.game_name_cn} page {i} 出错")
continue
fgo_data = json.loads(result)
if int(fgo_data["nums"]) <= 0:
break
for x in fgo_data["data"]:
name = remove_prohibited_str(x["name"])
member_dict = {
"id": x["id"],
"card_id": x["charid"],
"头像": x["icon"],
"名称": remove_prohibited_str(x["name"]),
"职阶": x["classes"],
"星级": int(x["star"]),
"hp": x["lvmax4hp"],
"atk": x["lvmax4atk"],
"card_quick": x["cardquick"],
"card_arts": x["cardarts"],
"card_buster": x["cardbuster"],
"宝具": x["tprop"],
}
fgo_info[name] = member_dict
# 更新额外信息
for key in fgo_info.keys():
url = f'http://fgo.vgtime.com/servant/{fgo_info[key]["id"]}'
result = await self.get_url(url)
if not result:
fgo_info[key]["入手方式"] = ["圣晶石召唤"]
logger.warning(f"{self.game_name_cn} 获取额外信息错误 {key}")
continue
try:
dom = etree.HTML(result, etree.HTMLParser())
obtain = dom.xpath(
"//table[contains(string(.),'入手方式')]/tr[8]/td[3]/text()"
)[0]
obtain = str(obtain).strip()
if "限时活动免费获取 活动结束后无法获得" in obtain:
obtain = ["活动获取"]
elif "非限时UP无法获得" in obtain:
obtain = ["限时召唤"]
else:
if "&" in obtain:
obtain = obtain.split("&")
else:
obtain = obtain.split(" ")
obtain = [s.strip() for s in obtain if s.strip()]
fgo_info[key]["入手方式"] = obtain
except IndexError:
fgo_info[key]["入手方式"] = ["圣晶石召唤"]
logger.warning(f"{self.game_name_cn} 获取额外信息错误 {key}")
self.dump_data(fgo_info)
logger.info(f"{self.game_name_cn} 更新成功")
# fgo_card.json
fgo_card_info = {}
for i in range(500):
url = f"http://fgo.vgtime.com/equipment/ajax?wd=&ids=&sort=12958&o=desc&pn={i}"
result = await self.get_url(url)
if not result:
logger.warning(f"更新 {self.game_name_cn}卡牌 page {i} 出错")
continue
fgo_data = json.loads(result)
if int(fgo_data["nums"]) <= 0:
break
for x in fgo_data["data"]:
name = remove_prohibited_str(x["name"])
member_dict = {
"id": x["id"],
"card_id": x["equipid"],
"头像": x["icon"],
"名称": name,
"星级": int(x["star"]),
"hp": x["lvmax_hp"],
"atk": x["lvmax_atk"],
"skill_e": str(x["skill_e"]).split("<br />")[:-1],
}
fgo_card_info[name] = member_dict
self.dump_data(fgo_card_info, "fgo_card.json")
logger.info(f"{self.game_name_cn} 卡牌更新成功")
# 下载头像
for value in fgo_info.values():
await self.download_img(value["头像"], value["名称"])
for value in fgo_card_info.values():
await self.download_img(value["头像"], value["名称"])
+429
View File
@@ -0,0 +1,429 @@
import random
import dateparser
from lxml import etree
from PIL import Image, ImageDraw
from urllib.parse import unquote
from typing import List, Optional, Tuple
from pydantic import ValidationError
from datetime import datetime, timedelta
from nonebot.adapters.onebot.v11 import Message
from utils.message_builder import image
from nonebot.log import logger
import asyncio
try:
import ujson as json
except ModuleNotFoundError:
import json
from .base_handle import BaseHandle, BaseData, UpChar, UpEvent
from ..config import draw_config
from ..count_manager import GenshinCountManager
from ..util import remove_prohibited_str, cn2py, load_font
from utils.image_utils import BuildImage
class GenshinData(BaseData):
pass
class GenshinChar(GenshinData):
pass
class GenshinArms(GenshinData):
pass
class GenshinHandle(BaseHandle[GenshinData]):
def __init__(self):
super().__init__("genshin", "原神", "#ebebeb")
self.data_files.append("genshin_arms.json")
self.max_star = 5
self.config = draw_config.genshin
self.ALL_CHAR: List[GenshinData] = []
self.ALL_ARMS: List[GenshinData] = []
self.UP_CHAR: Optional[UpEvent] = None
self.UP_ARMS: Optional[UpEvent] = None
self.count_manager = GenshinCountManager((10, 90), ("4", "5"), 180)
# 抽取卡池
def get_card(
self, pool_name: str, mode: int = 1, add: float = 0.0, is_up: bool = False
):
"""
mode 1:普通抽 2:四星保底 3:五星保底
"""
if mode == 1:
star = self.get_star(
[5, 4, 3],
[
self.config.GENSHIN_FIVE_P + add,
self.config.GENSHIN_FOUR_P,
self.config.GENSHIN_THREE_P,
],
)
elif mode == 2:
star = self.get_star(
[5, 4],
[self.config.GENSHIN_G_FIVE_P + add, self.config.GENSHIN_G_FOUR_P],
)
else:
star = 5
if pool_name == "char":
up_event = self.UP_CHAR
all_list = self.ALL_CHAR + [
x for x in self.ALL_ARMS if x.star == star and x.star < 5
]
elif pool_name == "arms":
up_event = self.UP_ARMS
all_list = self.ALL_ARMS + [
x for x in self.ALL_CHAR if x.star == star and x.star < 5
]
else:
up_event = None
all_list = self.ALL_ARMS + self.ALL_CHAR
acquire_char = None
# 是否UP
if up_event and star > 3:
# 获取up角色列表
up_list = [x.name for x in up_event.up_char if x.star == star]
# 成功获取up角色
if random.random() < 0.5 or is_up:
up_name = random.choice(up_list)
try:
acquire_char = [x for x in all_list if x.name == up_name][0]
except IndexError:
pass
if not acquire_char:
chars = [x for x in all_list if x.star == star and not x.limited]
acquire_char = random.choice(chars)
return acquire_char
def get_cards(
self, count: int, user_id: int, pool_name: str
) -> List[Tuple[GenshinData, int]]:
card_list = [] # 获取角色列表
add = 0.0
count_manager = self.count_manager
count_manager.check_timeout(user_id) # 检查上次抽卡次数是否超时
count_manager.check_count(user_id, count) # 检查次数累计
pool = self.UP_CHAR if pool_name == "char" else self.UP_ARMS
for i in range(count):
count_manager.increase(user_id)
star = count_manager.check(user_id) # 是否有四星或五星保底
if (
count_manager.get_user_count(user_id)
- count_manager.get_user_five_index(user_id)
) % count_manager.get_max_guarantee() >= 72:
add += draw_config.genshin.I72_ADD
if star:
if star == 4:
card = self.get_card(pool_name, 2, add=add)
else:
card = self.get_card(
pool_name, 3, add, count_manager.is_up(user_id)
)
else:
card = self.get_card(pool_name, 1, add, count_manager.is_up(user_id))
# print(f"{count_manager.get_user_count(user_id)}:",
# count_manager.get_user_five_index(user_id), star, card.star, add)
# 四星角色
if card.star == 4:
count_manager.mark_four_index(user_id)
# 五星角色
elif card.star == self.max_star:
add = 0
count_manager.mark_five_index(user_id) # 记录五星保底
count_manager.mark_four_index(user_id) # 记录四星保底
if pool and card.name in [
x.name for x in pool.up_char if x.star == self.max_star
]:
count_manager.set_is_up(user_id, True)
else:
count_manager.set_is_up(user_id, False)
card_list.append((card, count_manager.get_user_count(user_id)))
count_manager.update_time(user_id)
return card_list
def generate_card_img(self, card: GenshinData) -> BuildImage:
sep_w = 10
sep_h = 5
frame_w = 112
frame_h = 132
img_w = 106
img_h = 106
bg = BuildImage(frame_w + sep_w * 2, frame_h + sep_h * 2, color="#EBEBEB")
frame_path = str(self.img_path / "avatar_frame.png")
frame = Image.open(frame_path)
# 加名字
text = card.name
font = load_font(fontsize=14)
text_w, text_h = font.getsize(text)
draw = ImageDraw.Draw(frame)
draw.text(
((frame_w - text_w) / 2, frame_h - 15 - text_h / 2),
text,
font=font,
fill="gray",
)
img_path = str(self.img_path / f"{cn2py(card.name)}.png")
img = BuildImage(img_w, img_h, background=img_path)
if isinstance(card, GenshinArms):
# 武器卡背景不是透明的,切去上方两个圆弧
r = 12
circle = Image.new("L", (r * 2, r * 2), 0)
alpha = Image.new("L", img.size, 255)
alpha.paste(circle, (-r - 3, -r - 3)) # 左上角
alpha.paste(circle, (img_h - r + 3, -r - 3)) # 右上角
img.markImg.putalpha(alpha)
star_path = str(self.img_path / f"{card.star}_star.png")
star = Image.open(star_path)
bg.paste(frame, (sep_w, sep_h), alpha=True)
bg.paste(img, (sep_w + 3, sep_h + 3), alpha=True)
bg.paste(star, (sep_w + int((frame_w - star.width) / 2), sep_h - 6), alpha=True)
return bg
def format_pool_info(self, pool_name: str) -> str:
info = ""
up_event = None
if pool_name == "char":
up_event = self.UP_CHAR
elif pool_name == "arms":
up_event = self.UP_ARMS
if up_event:
star5_list = [x.name for x in up_event.up_char if x.star == 5]
star4_list = [x.name for x in up_event.up_char if x.star == 4]
if star5_list:
info += f"五星UP:{' '.join(star5_list)}\n"
if star4_list:
info += f"四星UP:{' '.join(star4_list)}\n"
info = f"当前up池:{up_event.title}\n{info}"
return info.strip()
async def draw(self, count: int, user_id: int, pool_name: str = "", **kwargs) -> Message:
return await asyncio.get_event_loop().run_in_executor(None, self._draw, count, user_id, pool_name)
def _draw(self, count: int, user_id: int, pool_name: str = "", **kwargs) -> Message:
index2cards = self.get_cards(count, user_id, pool_name)
cards = [card[0] for card in index2cards]
up_event = None
if pool_name == "char":
up_event = self.UP_CHAR
elif pool_name == "arms":
up_event = self.UP_ARMS
up_list = [x.name for x in up_event.up_char] if up_event else []
result = self.format_star_result(cards)
result += (
"\n" + max_star_str
if (max_star_str := self.format_max_star(index2cards, up_list=up_list))
else ""
)
result += f"\n距离保底发还剩 {self.count_manager.get_user_guarantee_count(user_id)} 抽"
# result += "\n【五星:0.6%,四星:5.1%,第72抽开始五星概率每抽加0.585%】"
pool_info = self.format_pool_info(pool_name)
img = self.generate_img(cards)
bk = BuildImage(img.w, img.h + 50, font_size=20, color="#ebebeb")
bk.paste(img)
bk.text((0, img.h + 10), "【五星:0.6%,四星:5.1%,第72抽开始五星概率每抽加0.585%】")
return pool_info + image(b64=bk.pic2bs4()) + result
def _init_data(self):
self.ALL_CHAR = [
GenshinChar(
name=value["名称"],
star=int(value["星级"]),
limited=value["常驻/限定"] == "限定UP",
)
for key, value in self.load_data().items()
if "旅行者" not in key
]
self.ALL_ARMS = [
GenshinArms(
name=value["名称"],
star=int(value["星级"]),
limited="祈愿" not in value["获取途径"],
)
for value in self.load_data("genshin_arms.json").values()
]
self.load_up_char()
def load_up_char(self):
try:
data = self.load_data(f"draw_card_up/{self.game_name}_up_char.json")
self.UP_CHAR = UpEvent.parse_obj(data.get("char", {}))
self.UP_ARMS = UpEvent.parse_obj(data.get("arms", {}))
except ValidationError:
logger.warning(f"{self.game_name}_up_char 解析出错")
def dump_up_char(self):
if self.UP_CHAR and self.UP_ARMS:
data = {
"char": json.loads(self.UP_CHAR.json()),
"arms": json.loads(self.UP_ARMS.json()),
}
self.dump_data(data, f"draw_card_up/{self.game_name}_up_char.json")
async def _update_info(self):
# genshin.json
char_info = {}
url = "https://wiki.biligame.com/ys/角色筛选"
result = await self.get_url(url)
if not result:
logger.warning(f"更新 {self.game_name_cn} 出错")
else:
dom = etree.HTML(result, etree.HTMLParser())
char_list = dom.xpath("//table[@id='CardSelectTr']/tbody/tr")
for char in char_list:
try:
name = char.xpath("./td[1]/a/@title")[0]
avatar = char.xpath("./td[1]/a/img/@srcset")[0]
star = char.xpath("./td[3]/text()")[0]
except IndexError:
continue
member_dict = {
"头像": unquote(str(avatar).split(" ")[-2]),
"名称": remove_prohibited_str(name),
"星级": int(str(star).strip()[:1]),
}
char_info[member_dict["名称"]] = member_dict
# 更新额外信息
for key in char_info.keys():
result = await self.get_url(f"https://wiki.biligame.com/ys/{key}")
if not result:
char_info[key]["常驻/限定"] = "未知"
logger.warning(f"{self.game_name_cn} 获取额外信息错误 {key}")
continue
try:
dom = etree.HTML(result, etree.HTMLParser())
limit = dom.xpath(
"//table[contains(string(.),'常驻/限定')]/tbody/tr[6]/td/text()"
)[0]
char_info[key]["常驻/限定"] = str(limit).strip()
except IndexError:
char_info[key]["常驻/限定"] = "未知"
logger.warning(f"{self.game_name_cn} 获取额外信息错误 {key}")
self.dump_data(char_info)
logger.info(f"{self.game_name_cn} 更新成功")
# genshin_arms.json
arms_info = {}
url = "https://wiki.biligame.com/ys/武器图鉴"
result = await self.get_url(url)
if not result:
logger.warning(f"更新 {self.game_name_cn} 出错")
else:
dom = etree.HTML(result, etree.HTMLParser())
char_list = dom.xpath("//table[@id='CardSelectTr']/tbody/tr")
for char in char_list:
try:
name = char.xpath("./td[1]/a/@title")[0]
avatar = char.xpath("./td[1]/a/img/@srcset")[0]
star = char.xpath("./td[4]/img/@alt")[0]
sources = str(char.xpath("./td[5]/text()")[0]).split(",")
except IndexError:
continue
member_dict = {
"头像": unquote(str(avatar).split(" ")[-2]),
"名称": remove_prohibited_str(name),
"星级": int(str(star).strip()[:1]),
"获取途径": [s.strip() for s in sources if s.strip()],
}
arms_info[member_dict["名称"]] = member_dict
self.dump_data(arms_info, "genshin_arms.json")
logger.info(f"{self.game_name_cn} 武器更新成功")
# 下载头像
for value in char_info.values():
await self.download_img(value["头像"], value["名称"])
for value in arms_info.values():
await self.download_img(value["头像"], value["名称"])
# 下载星星
idx = 1
YS_URL = "https://patchwiki.biligame.com/images/ys"
for url in [
"/1/13/7xzg7tgf8dsr2hjpmdbm5gn9wvzt2on.png",
"/b/bc/sd2ige6d7lvj7ugfumue3yjg8gyi0d1.png",
"/e/ec/l3mnhy56pyailhn3v7r873htf2nofau.png",
"/9/9c/sklp02ffk3aqszzvh8k1c3139s0awpd.png",
"/c/c7/qu6xcndgj6t14oxvv7yz2warcukqv1m.png",
]:
await self.download_img(YS_URL + url, f"{idx}_star")
idx += 1
# 下载头像框
await self.download_img(
YS_URL + "/2/2e/opbcst4xbtcq0i4lwerucmosawn29ti.png", f"avatar_frame"
)
await self.update_up_char()
async def update_up_char(self):
url = "https://wiki.biligame.com/ys/祈愿"
result = await self.get_url(url)
if not result:
logger.warning(f"{self.game_name_cn}获取祈愿页面出错")
return
dom = etree.HTML(result, etree.HTMLParser())
tables = dom.xpath(
"//div[@class='mw-parser-output']/div[@class='row']/div/table[@class='wikitable']/tbody"
)
if not tables or len(tables) < 2:
logger.warning(f"{self.game_name_cn}获取活动祈愿出错")
return
try:
for index, table in enumerate(tables):
title = table.xpath("./tr[1]/th/img/@title")[0]
title = str(title).split("」")[0] + "」" if "」" in title else title
pool_img = str(table.xpath("./tr[1]/th/img/@srcset")[0]).split(" ")[-2]
time = table.xpath("./tr[2]/td/text()")[0]
star5_list = table.xpath("./tr[3]/td/a/@title")
star4_list = table.xpath("./tr[4]/td/a/@title")
start, end = str(time).split("~")
start_time = dateparser.parse(start)
end_time = dateparser.parse(end)
if not start_time and end_time:
start_time = end_time - timedelta(days=20)
if start_time and end_time and start_time <= datetime.now() <= end_time:
up_event = UpEvent(
title=title,
pool_img=pool_img,
start_time=start_time,
end_time=end_time,
up_char=[
UpChar(name=name, star=5, limited=False, zoom=50)
for name in star5_list
]
+ [
UpChar(name=name, star=4, limited=False, zoom=50)
for name in star4_list
],
)
if index == 0:
self.UP_CHAR = up_event
elif index == 1:
self.UP_ARMS = up_event
if self.UP_CHAR and self.UP_ARMS:
self.dump_up_char()
logger.info(
f"成功获取{self.game_name_cn}当前up信息...当前up池: {self.UP_CHAR.title} & {self.UP_ARMS.title}"
)
except Exception as e:
logger.warning(f"{self.game_name_cn}UP更新出错 {type(e)}:{e}")
def reset_count(self, user_id: int) -> bool:
self.count_manager.reset(user_id)
return True
async def _reload_pool(self) -> Optional[Message]:
await self.update_up_char()
self.load_up_char()
if self.UP_CHAR and self.UP_ARMS:
return Message(
Message.template("重载成功!\n当前UP池子:{} & {}{:image}{:image}").format(
self.UP_CHAR.title,
self.UP_ARMS.title,
self.UP_CHAR.pool_img,
self.UP_ARMS.pool_img,
)
)
@@ -0,0 +1,400 @@
import re
import random
import dateparser
from lxml import etree
from PIL import ImageDraw
from datetime import datetime
from urllib.parse import unquote
from typing import List, Optional, Tuple
from pydantic import ValidationError
from nonebot.adapters.onebot.v11 import Message
from utils.message_builder import image
from nonebot.log import logger
import asyncio
try:
import ujson as json
except ModuleNotFoundError:
import json
from .base_handle import BaseHandle, BaseData, UpChar, UpEvent
from ..config import draw_config
from ..util import remove_prohibited_str, cn2py, load_font
from utils.image_utils import BuildImage
class GuardianData(BaseData):
pass
class GuardianChar(GuardianData):
pass
class GuardianArms(GuardianData):
pass
class GuardianHandle(BaseHandle[GuardianData]):
def __init__(self):
super().__init__("guardian", "坎公骑冠剑")
self.data_files.append("guardian_arms.json")
self.config = draw_config.guardian
self.ALL_CHAR: List[GuardianChar] = []
self.ALL_ARMS: List[GuardianArms] = []
self.UP_CHAR: Optional[UpEvent] = None
self.UP_ARMS: Optional[UpEvent] = None
def get_card(self, pool_name: str, mode: int = 1) -> GuardianData:
if pool_name == "char":
if mode == 1:
star = self.get_star(
[3, 2, 1],
[
self.config.GUARDIAN_THREE_CHAR_P,
self.config.GUARDIAN_TWO_CHAR_P,
self.config.GUARDIAN_ONE_CHAR_P,
],
)
else:
star = self.get_star(
[3, 2],
[
self.config.GUARDIAN_THREE_CHAR_P,
self.config.GUARDIAN_TWO_CHAR_P,
],
)
up_event = self.UP_CHAR
self.max_star = 3
all_data = self.ALL_CHAR
else:
if mode == 1:
star = self.get_star(
[5, 4, 3, 2],
[
self.config.GUARDIAN_FIVE_ARMS_P,
self.config.GUARDIAN_FOUR_ARMS_P,
self.config.GUARDIAN_THREE_ARMS_P,
self.config.GUARDIAN_TWO_ARMS_P,
],
)
else:
star = self.get_star(
[5, 4],
[
self.config.GUARDIAN_FIVE_ARMS_P,
self.config.GUARDIAN_FOUR_ARMS_P,
],
)
up_event = self.UP_ARMS
self.max_star = 5
all_data = self.ALL_ARMS
acquire_char = None
# 是否UP
if up_event and star == self.max_star and pool_name:
# 获取up角色列表
up_list = [x.name for x in up_event.up_char if x.star == star]
# 成功获取up角色
if random.random() < 0.5:
up_name = random.choice(up_list)
try:
acquire_char = [x for x in all_data if x.name == up_name][0]
except IndexError:
pass
if not acquire_char:
chars = [x for x in all_data if x.star == star and not x.limited]
acquire_char = random.choice(chars)
return acquire_char
def get_cards(self, count: int, pool_name: str) -> List[Tuple[GuardianData, int]]:
card_list = []
card_count = 0 # 保底计算
for i in range(count):
card_count += 1
# 十连保底
if card_count == 10:
card = self.get_card(pool_name, 2)
card_count = 0
else:
card = self.get_card(pool_name, 1)
if card.star > self.max_star - 2:
card_count = 0
card_list.append((card, i + 1))
return card_list
def format_pool_info(self, pool_name: str) -> str:
info = ""
up_event = self.UP_CHAR if pool_name == "char" else self.UP_ARMS
if up_event:
if pool_name == "char":
up_list = [x.name for x in up_event.up_char if x.star == 3]
info += f'三星UP:{" ".join(up_list)}\n'
else:
up_list = [x.name for x in up_event.up_char if x.star == 5]
info += f'五星UP:{" ".join(up_list)}\n'
info = f"当前up池:{up_event.title}\n{info}"
return info.strip()
async def draw(self, count: int, pool_name: str, **kwargs) -> Message:
return await asyncio.get_event_loop().run_in_executor(None, self._draw, count, pool_name)
def _draw(self, count: int, pool_name: str, **kwargs) -> Message:
index2card = self.get_cards(count, pool_name)
cards = [card[0] for card in index2card]
up_event = self.UP_CHAR if pool_name == "char" else self.UP_ARMS
up_list = [x.name for x in up_event.up_char] if up_event else []
result = self.format_result(index2card, up_list=up_list)
pool_info = self.format_pool_info(pool_name)
return pool_info + image(b64=self.generate_img(cards).pic2bs4()) + result
def generate_card_img(self, card: GuardianData) -> BuildImage:
sep_w = 1
sep_h = 1
block_w = 170
block_h = 90
img_w = 90
img_h = 90
if isinstance(card, GuardianChar):
block_color = "#2e2923"
font_color = "#e2ccad"
star_w = 90
star_h = 30
star_name = f"{card.star}_star.png"
frame_path = ""
else:
block_color = "#EEE4D5"
font_color = "#A65400"
star_w = 45
star_h = 45
star_name = f"{card.star}_star_rank.png"
frame_path = str(self.img_path / "avatar_frame.png")
bg = BuildImage(block_w + sep_w * 2, block_h + sep_h * 2, color="#F6F4ED")
block = BuildImage(block_w, block_h, color=block_color)
star_path = str(self.img_path / star_name)
star = BuildImage(star_w, star_h, background=star_path)
img_path = str(self.img_path / f"{cn2py(card.name)}.png")
img = BuildImage(img_w, img_h, background=img_path)
block.paste(img, (0, 0), alpha=True)
if frame_path:
frame = BuildImage(img_w, img_h, background=frame_path)
block.paste(frame, (0, 0), alpha=True)
block.paste(
star,
(int((block_w + img_w - star_w) / 2), block_h - star_h - 30),
alpha=True,
)
# 加名字
text = card.name[:4] + "..." if len(card.name) > 5 else card.name
font = load_font(fontsize=14)
text_w, _ = font.getsize(text)
draw = ImageDraw.Draw(block.markImg)
draw.text(
((block_w + img_w - text_w) / 2, 55),
text,
font=font,
fill=font_color,
)
bg.paste(block, (sep_w, sep_h))
return bg
def _init_data(self):
self.ALL_CHAR = [
GuardianChar(name=value["名称"], star=int(value["星级"]), limited=False)
for value in self.load_data().values()
]
self.ALL_ARMS = [
GuardianArms(name=value["名称"], star=int(value["星级"]), limited=False)
for value in self.load_data("guardian_arms.json").values()
]
self.load_up_char()
def load_up_char(self):
try:
data = self.load_data(f"draw_card_up/{self.game_name}_up_char.json")
self.UP_CHAR = UpEvent.parse_obj(data.get("char", {}))
self.UP_ARMS = UpEvent.parse_obj(data.get("arms", {}))
except ValidationError:
logger.warning(f"{self.game_name}_up_char 解析出错")
def dump_up_char(self):
if self.UP_CHAR and self.UP_ARMS:
data = {
"char": json.loads(self.UP_CHAR.json()),
"arms": json.loads(self.UP_ARMS.json()),
}
self.dump_data(data, f"draw_card_up/{self.game_name}_up_char.json")
async def _update_info(self):
# guardian.json
guardian_info = {}
url = "https://wiki.biligame.com/gt/英雄筛选表"
result = await self.get_url(url)
if not result:
logger.warning(f"更新 {self.game_name_cn} 出错")
else:
dom = etree.HTML(result, etree.HTMLParser())
char_list = dom.xpath("//table[@id='CardSelectTr']/tbody/tr")
for char in char_list:
try:
name = char.xpath("./td[1]/a/@title")[0]
avatar = char.xpath("./td[1]/a/img/@src")[0]
star = char.xpath("./td[1]/span/img/@alt")[0]
except IndexError:
continue
member_dict = {
"头像": unquote(str(avatar)),
"名称": remove_prohibited_str(name),
"星级": int(str(star).split(" ")[0].replace("Rank", "")),
}
guardian_info[member_dict["名称"]] = member_dict
self.dump_data(guardian_info)
logger.info(f"{self.game_name_cn} 更新成功")
# guardian_arms.json
guardian_arms_info = {}
url = "https://wiki.biligame.com/gt/武器"
result = await self.get_url(url)
if not result:
logger.warning(f"更新 {self.game_name_cn} 武器出错")
else:
dom = etree.HTML(result, etree.HTMLParser())
char_list = dom.xpath(
"//div[@class='resp-tabs-container']/div[1]/div/table[2]/tbody/tr"
)
for char in char_list:
try:
name = char.xpath("./td[2]/a/@title")[0]
avatar = char.xpath("./td[1]/div/div/div/a/img/@src")[0]
star = char.xpath("./td[3]/text()")[0]
except IndexError:
continue
member_dict = {
"头像": unquote(str(avatar)),
"名称": remove_prohibited_str(name),
"星级": int(str(star).strip()),
}
guardian_arms_info[member_dict["名称"]] = member_dict
self.dump_data(guardian_arms_info, "guardian_arms.json")
logger.info(f"{self.game_name_cn} 武器更新成功")
url = "https://wiki.biligame.com/gt/盾牌"
result = await self.get_url(url)
if not result:
logger.warning(f"更新 {self.game_name_cn} 盾牌出错")
else:
dom = etree.HTML(result, etree.HTMLParser())
char_list = dom.xpath(
"//div[@class='resp-tabs-container']/div[2]/div/table[1]/tbody/tr"
)
for char in char_list:
try:
name = char.xpath("./td[2]/a/@title")[0]
avatar = char.xpath("./td[1]/div/div/div/a/img/@src")[0]
star = char.xpath("./td[3]/text()")[0]
except IndexError:
continue
member_dict = {
"头像": unquote(str(avatar)),
"名称": remove_prohibited_str(name),
"星级": int(str(star).strip()),
}
guardian_arms_info[member_dict["名称"]] = member_dict
self.dump_data(guardian_arms_info, "guardian_arms.json")
logger.info(f"{self.game_name_cn} 盾牌更新成功")
# 下载头像
for value in guardian_info.values():
await self.download_img(value["头像"], value["名称"])
for value in guardian_arms_info.values():
await self.download_img(value["头像"], value["名称"])
# 下载星星
idx = 1
GT_URL = "https://patchwiki.biligame.com/images/gt"
for url in [
"/4/4b/ardr3bi2yf95u4zomm263tc1vke6i3i.png",
"/5/55/6vow7lh76gzus6b2g9cfn325d1sugca.png",
"/b/b9/du8egrd2vyewg0cuyra9t8jh0srl0ds.png",
]:
await self.download_img(GT_URL + url, f"{idx}_star")
idx += 1
# 另一种星星
idx = 1
for url in [
"/6/66/4e2tfa9kvhfcbikzlyei76i9crva145.png",
"/1/10/r9ihsuvycgvsseyneqz4xs22t53026m.png",
"/7/7a/o0k86ru9k915y04azc26hilxead7xp1.png",
"/c/c9/rxz99asysz0rg391j3b02ta09mnpa7v.png",
"/2/2a/sfxz0ucv1s6ewxveycz9mnmrqs2rw60.png",
]:
await self.download_img(GT_URL + url, f"{idx}_star_rank")
idx += 1
# 头像框
await self.download_img(
GT_URL + "/8/8e/ogbqslbhuykjhnc8trtoa0p0nhfzohs.png", f"avatar_frame"
)
await self.update_up_char()
async def update_up_char(self):
url = "https://wiki.biligame.com/gt/首页"
result = await self.get_url(url)
if not result:
logger.warning(f"{self.game_name_cn}获取公告出错")
return
try:
dom = etree.HTML(result, etree.HTMLParser())
announcement = dom.xpath(
"//div[@class='mw-parser-output']/div/div[3]/div[2]/div/div[2]/div[3]"
)[0]
title = announcement.xpath("./font/p/b/text()")[0]
match = re.search(r"从(.*?)开始.*?至(.*?)结束", title)
if not match:
logger.warning(f"{self.game_name_cn}找不到UP时间")
return
start, end = match.groups()
start_time = dateparser.parse(start.replace("月", "/").replace("日", ""))
end_time = dateparser.parse(end.replace("月", "/").replace("日", ""))
if not (start_time and end_time) or not (
start_time <= datetime.now() <= end_time
):
return
divs = announcement.xpath("./font/div")
char_index = 0
arms_index = 0
for index, div in enumerate(divs):
if div.xpath("string(.)") == "角色":
char_index = index
elif div.xpath("string(.)") == "武器":
arms_index = index
chars = divs[char_index + 1 : arms_index]
arms = divs[arms_index + 1 :]
up_chars = []
up_arms = []
for char in chars:
name = char.xpath("./p/a/@title")[0]
up_chars.append(UpChar(name=name, star=3, limited=False, zoom=0))
for arm in arms:
name = arm.xpath("./p/a/@title")[0]
up_arms.append(UpChar(name=name, star=5, limited=False, zoom=0))
self.UP_CHAR = UpEvent(
title=title,
pool_img="",
start_time=start_time,
end_time=end_time,
up_char=up_chars,
)
self.UP_ARMS = UpEvent(
title=title,
pool_img="",
start_time=start_time,
end_time=end_time,
up_char=up_arms,
)
self.dump_up_char()
logger.info(f"成功获取{self.game_name_cn}当前up信息...当前up池: {title}")
except Exception as e:
logger.warning(f"{self.game_name_cn}UP更新出错 {type(e)}:{e}")
async def _reload_pool(self) -> Optional[Message]:
await self.update_up_char()
self.load_up_char()
if self.UP_CHAR and self.UP_ARMS:
return Message(f"重载成功!\n当前UP池子:{self.UP_CHAR.title}")
+179
View File
@@ -0,0 +1,179 @@
import random
from lxml import etree
from typing import List, Tuple
from nonebot.log import logger
from PIL import Image, ImageDraw
from PIL.Image import Image as IMG
try:
import ujson as json
except ModuleNotFoundError:
import json
from .base_handle import BaseHandle, BaseData
from ..config import draw_config
from ..util import remove_prohibited_str, cn2py, load_font
from utils.image_utils import BuildImage
class OnmyojiChar(BaseData):
@property
def star_str(self) -> str:
return ["N", "R", "SR", "SSR", "SP"][self.star - 1]
class OnmyojiHandle(BaseHandle[OnmyojiChar]):
def __init__(self):
super().__init__("onmyoji", "阴阳师")
self.max_star = 5
self.config = draw_config.onmyoji
self.ALL_CHAR: List[OnmyojiChar] = []
def get_card(self, **kwargs) -> OnmyojiChar:
star = self.get_star(
[5, 4, 3, 2],
[
self.config.ONMYOJI_SP,
self.config.ONMYOJI_SSR,
self.config.ONMYOJI_SR,
self.config.ONMYOJI_R,
],
)
chars = [x for x in self.ALL_CHAR if x.star == star and not x.limited]
return random.choice(chars)
def format_max_star(self, card_list: List[Tuple[OnmyojiChar, int]]) -> str:
rst = ""
for card, index in card_list:
if card.star == self.max_star:
rst += f"第 {index} 抽获取SP {card.name}\n"
elif card.star == self.max_star - 1:
rst += f"第 {index} 抽获取SSR {card.name}\n"
return rst.strip()
@staticmethod
def star_label(star: int) -> IMG:
text, color1, color2 = [
("N", "#7E7E82", "#F5F6F7"),
("R", "#014FA8", "#37C6FD"),
("SR", "#6E0AA4", "#E94EFD"),
("SSR", "#E5511D", "#FAF905"),
("SP", "#FA1F2D", "#FFBBAF"),
][star - 1]
w = 200
h = 110
# 制作渐变色图片
base = Image.new("RGBA", (w, h), color1)
top = Image.new("RGBA", (w, h), color2)
mask = Image.new("L", (w, h))
mask_data = []
for y in range(h):
mask_data.extend([int(255 * (y / h))] * w)
mask.putdata(mask_data)
base.paste(top, (0, 0), mask)
# 透明图层
font = load_font("gorga.otf", 100)
alpha = Image.new("L", (w, h))
draw = ImageDraw.Draw(alpha)
draw.text((20, -30), text, fill="white", font=font)
base.putalpha(alpha)
# stroke
bg = Image.new("RGBA", (w, h))
draw = ImageDraw.Draw(bg)
draw.text(
(20, -30),
text,
font=font,
fill="gray",
stroke_width=3,
stroke_fill="gray",
)
bg.paste(base, (0, 0), base)
return bg
def generate_img(self, card_list: List[OnmyojiChar]) -> BuildImage:
return super().generate_img(card_list, num_per_line=10)
def generate_card_img(self, card: OnmyojiChar) -> BuildImage:
bg = BuildImage(73, 240, color="#F1EFE9")
img_path = str(self.img_path / f"{cn2py(card.name)}_mark_btn.png")
img = BuildImage(0, 0, background=img_path)
img = Image.open(img_path).convert("RGBA")
label = self.star_label(card.star).resize((60, 33), Image.ANTIALIAS)
bg.paste(img, (0, 0), alpha=True)
bg.paste(label, (0, 135), alpha=True)
font = load_font("msyh.ttf", 16)
draw = ImageDraw.Draw(bg.markImg)
text = "\n".join([t for t in card.name[:4]])
_, text_h = font.getsize_multiline(text, spacing=0)
draw.text(
(40, 150 + (90 - text_h) / 2), text, font=font, fill="gray", spacing=0
)
return bg
def _init_data(self):
self.ALL_CHAR = [
OnmyojiChar(
name=value["名称"],
star=["N", "R", "SR", "SSR", "SP"].index(value["星级"]) + 1,
limited=True
if key
in [
"奴良陆生",
"卖药郎",
"鬼灯",
"阿香",
"蜜桃&芥子",
"犬夜叉",
"杀生丸",
"桔梗",
"朽木露琪亚",
"黑崎一护",
"灶门祢豆子",
"灶门炭治郎",
]
else False,
)
for key, value in self.load_data().items()
]
async def _update_info(self):
info = {}
url = "https://yys.res.netease.com/pc/zt/20161108171335/js/app/all_shishen.json?v74="
result = await self.get_url(url)
if not result:
logger.warning(f"更新 {self.game_name_cn} 出错")
return
data = json.loads(result)
for x in data:
name = remove_prohibited_str(x["name"])
member_dict = {
"id": x["id"],
"名称": name,
"星级": x["level"],
}
info[name] = member_dict
# logger.info(f"{name} is update...")
# 更新头像
for key in info.keys():
url = f'https://yys.163.com/shishen/{info[key]["id"]}.html'
result = await self.get_url(url)
if not result:
info[key]["头像"] = ""
continue
try:
dom = etree.HTML(result, etree.HTMLParser())
avatar = dom.xpath("//div[@class='pic_wrap']/img/@src")[0]
avatar = "https:" + avatar
info[key]["头像"] = avatar
except IndexError:
info[key]["头像"] = ""
logger.warning(f"{self.game_name_cn} 获取头像错误 {key}")
self.dump_data(info)
logger.info(f"{self.game_name_cn} 更新成功")
# 下载头像
for value in info.values():
await self.download_img(value["头像"], value["名称"])
# 下载书签形式的头像
url = f"https://yys.res.netease.com/pc/zt/20161108171335/data/mark_btn/{value['id']}.png"
await self.download_img(url, value["名称"] + "_mark_btn")
+147
View File
@@ -0,0 +1,147 @@
import random
from lxml import etree
from typing import List, Tuple
from PIL import ImageDraw
from urllib.parse import unquote
from nonebot.log import logger
from .base_handle import BaseHandle, BaseData
from ..config import draw_config
from ..util import remove_prohibited_str, cn2py, load_font
from utils.image_utils import BuildImage
class PcrChar(BaseData):
pass
class PcrHandle(BaseHandle[PcrChar]):
def __init__(self):
super().__init__("pcr", "公主连结")
self.max_star = 3
self.config = draw_config.pcr
self.ALL_CHAR: List[PcrChar] = []
def get_card(self, mode: int = 1) -> PcrChar:
if mode == 2:
star = self.get_star(
[3, 2], [self.config.PCR_G_THREE_P, self.config.PCR_G_TWO_P]
)
else:
star = self.get_star(
[3, 2, 1],
[self.config.PCR_THREE_P, self.config.PCR_TWO_P, self.config.PCR_ONE_P],
)
chars = [x for x in self.ALL_CHAR if x.star == star and not x.limited]
return random.choice(chars)
def get_cards(self, count: int, **kwargs) -> List[Tuple[PcrChar, int]]:
card_list = []
card_count = 0 # 保底计算
for i in range(count):
card_count += 1
# 十连保底
if card_count == 10:
card = self.get_card(2)
card_count = 0
else:
card = self.get_card(1)
if card.star > self.max_star - 2:
card_count = 0
card_list.append((card, i + 1))
return card_list
def generate_card_img(self, card: PcrChar) -> BuildImage:
sep_w = 5
sep_h = 5
star_h = 15
img_w = 90
img_h = 90
font_h = 20
bg = BuildImage(img_w + sep_w * 2, img_h + font_h + sep_h * 2, color="#EFF2F5")
star_path = str(self.img_path / "star.png")
star = BuildImage(star_h, star_h, background=star_path)
img_path = str(self.img_path / f"{cn2py(card.name)}.png")
img = BuildImage(img_w, img_h, background=img_path)
bg.paste(img, (sep_w, sep_h), alpha=True)
for i in range(card.star):
bg.paste(star, (sep_w + img_w - star_h * (i + 1), sep_h), alpha=True)
# 加名字
text = card.name[:5] + "..." if len(card.name) > 6 else card.name
font = load_font(fontsize=14)
text_w, text_h = font.getsize(text)
draw = ImageDraw.Draw(bg.markImg)
draw.text(
(sep_w + (img_w - text_w) / 2, sep_h + img_h + (font_h - text_h) / 2),
text,
font=font,
fill="gray",
)
return bg
def _init_data(self):
self.ALL_CHAR = [
PcrChar(
name=value["名称"],
star=int(value["星级"]),
limited=True if "(" in key else False,
)
for key, value in self.load_data().items()
]
async def _update_info(self):
info = {}
if draw_config.PCR_TAI:
url = "https://wiki.biligame.com/pcr/角色图鉴"
result = await self.get_url(url)
if not result:
logger.warning(f"更新 {self.game_name_cn} 出错")
return
dom = etree.HTML(result, etree.HTMLParser())
char_list = dom.xpath(
"//div[@class='resp-tab-content']/div[@class='unit-icon']"
)
for char in char_list:
try:
name = char.xpath("./a/@title")[0]
avatar = char.xpath("./a/img/@srcset")[0]
star = len(char.xpath("./div[1]/img"))
except IndexError:
continue
member_dict = {
"头像": unquote(str(avatar).split(" ")[-2]),
"名称": remove_prohibited_str(name),
"星级": star,
}
info[member_dict["名称"]] = member_dict
else:
url = "https://wiki.biligame.com/pcr/角色筛选表"
result = await self.get_url(url)
if not result:
logger.warning(f"更新 {self.game_name_cn} 出错")
return
dom = etree.HTML(result, etree.HTMLParser())
char_list = dom.xpath("//table[@id='CardSelectTr']/tbody/tr")
for char in char_list:
try:
name = char.xpath("./td[1]/a/@title")[0]
avatar = char.xpath("./td[1]/a/img/@srcset")[0]
star = char.xpath("./td[4]/text()")[0]
except IndexError:
continue
member_dict = {
"头像": unquote(str(avatar).split(" ")[-2]),
"名称": remove_prohibited_str(name),
"星级": int(str(star).strip()),
}
info[member_dict["名称"]] = member_dict
self.dump_data(info)
logger.info(f"{self.game_name_cn} 更新成功")
# 下载头像
for value in info.values():
await self.download_img(value["头像"], value["名称"])
# 下载星星
await self.download_img(
"https://patchwiki.biligame.com/images/pcr/0/02/s75ys2ecqhu2xbdw1wf1v9ccscnvi5g.png",
"star",
)
+426
View File
@@ -0,0 +1,426 @@
import re
import random
import dateparser
from lxml import etree
from PIL import ImageDraw
from bs4 import BeautifulSoup
from datetime import datetime
from urllib.parse import unquote
from typing import List, Optional, Tuple
from pydantic import ValidationError
from nonebot.adapters.onebot.v11 import Message
from utils.message_builder import image
from nonebot.log import logger
import asyncio
try:
import ujson as json
except ModuleNotFoundError:
import json
from .base_handle import BaseHandle, BaseData, UpChar, UpEvent
from ..config import draw_config
from ..util import remove_prohibited_str, cn2py, load_font
from utils.image_utils import BuildImage
class PrettyData(BaseData):
pass
class PrettyChar(PrettyData):
pass
class PrettyCard(PrettyData):
@property
def star_str(self) -> str:
return ["R", "SR", "SSR"][self.star - 1]
class PrettyHandle(BaseHandle[PrettyData]):
def __init__(self):
super().__init__("pretty", "赛马娘", "#eff2f5")
self.data_files.append("pretty_card.json")
self.max_star = 3
self.config = draw_config.pretty
self.ALL_CHAR: List[PrettyChar] = []
self.ALL_CARD: List[PrettyCard] = []
self.UP_CHAR: Optional[UpEvent] = None
self.UP_CARD: Optional[UpEvent] = None
def get_card(self, pool_name: str, mode: int = 1) -> PrettyData:
if mode == 1:
star = self.get_star(
[3, 2, 1],
[
self.config.PRETTY_THREE_P,
self.config.PRETTY_TWO_P,
self.config.PRETTY_ONE_P,
],
)
else:
star = self.get_star(
[3, 2], [self.config.PRETTY_THREE_P, self.config.PRETTY_TWO_P]
)
up_pool = None
if pool_name == "char":
up_pool = self.UP_CHAR
all_list = self.ALL_CHAR
else:
up_pool = self.UP_CARD
all_list = self.ALL_CARD
all_char = [x for x in all_list if x.star == star and not x.limited]
acquire_char = None
# 有UP池子
if up_pool and star in [x.star for x in up_pool.up_char]:
up_list = [x.name for x in up_pool.up_char if x.star == star]
# 抽到UP
if random.random() < 1 / len(all_char) * (0.7 / 0.1385):
up_name = random.choice(up_list)
try:
acquire_char = [x for x in all_list if x.name == up_name][0]
except IndexError:
pass
if not acquire_char:
acquire_char = random.choice(all_char)
return acquire_char
def get_cards(self, count: int, pool_name: str) -> List[Tuple[PrettyData, int]]:
card_list = []
card_count = 0 # 保底计算
for i in range(count):
card_count += 1
# 十连保底
if card_count == 10:
card = self.get_card(pool_name, 2)
card_count = 0
else:
card = self.get_card(pool_name, 1)
if card.star > self.max_star - 2:
card_count = 0
card_list.append((card, i + 1))
return card_list
def format_pool_info(self, pool_name: str) -> str:
info = ""
up_event = self.UP_CHAR if pool_name == "char" else self.UP_CARD
if up_event:
star3_list = [x.name for x in up_event.up_char if x.star == 3]
star2_list = [x.name for x in up_event.up_char if x.star == 2]
star1_list = [x.name for x in up_event.up_char if x.star == 1]
if star3_list:
if pool_name == "char":
info += f'三星UP:{" ".join(star3_list)}\n'
else:
info += f'SSR UP:{" ".join(star3_list)}\n'
if star2_list:
if pool_name == "char":
info += f'二星UP:{" ".join(star2_list)}\n'
else:
info += f'SR UP:{" ".join(star2_list)}\n'
if star1_list:
if pool_name == "char":
info += f'一星UP:{" ".join(star1_list)}\n'
else:
info += f'R UP:{" ".join(star1_list)}\n'
info = f"当前up池:{up_event.title}\n{info}"
return info.strip()
async def draw(self, count: int, pool_name: str, **kwargs) -> Message:
return await asyncio.get_event_loop().run_in_executor(None, self._draw, count, pool_name)
def _draw(self, count: int, pool_name: str, **kwargs) -> Message:
pool_name = "char" if not pool_name else pool_name
index2card = self.get_cards(count, pool_name)
cards = [card[0] for card in index2card]
up_event = self.UP_CHAR if pool_name == "char" else self.UP_CARD
up_list = [x.name for x in up_event.up_char] if up_event else []
result = self.format_result(index2card, up_list=up_list)
pool_info = self.format_pool_info(pool_name)
return pool_info + image(b64=self.generate_img(cards).pic2bs4()) + result
def generate_card_img(self, card: PrettyData) -> BuildImage:
if isinstance(card, PrettyChar):
star_h = 30
img_w = 200
img_h = 219
font_h = 50
bg = BuildImage(img_w, img_h + font_h, color="#EFF2F5")
star_path = str(self.img_path / "star.png")
star = BuildImage(star_h, star_h, background=star_path)
img_path = str(self.img_path / f"{cn2py(card.name)}.png")
img = BuildImage(img_w, img_h, background=img_path)
star_w = star_h * card.star
for i in range(card.star):
bg.paste(star, (int((img_w - star_w) / 2) + star_h * i, 0), alpha=True)
bg.paste(img, (0, 0), alpha=True)
# 加名字
text = card.name[:5] + "..." if len(card.name) > 6 else card.name
font = load_font(fontsize=30)
text_w, _ = font.getsize(text)
draw = ImageDraw.Draw(bg.markImg)
draw.text(
((img_w - text_w) / 2, img_h),
text,
font=font,
fill="gray",
)
return bg
else:
sep_w = 10
img_w = 200
img_h = 267
font_h = 75
bg = BuildImage(img_w + sep_w * 2, img_h + font_h, color="#EFF2F5")
label_path = str(self.img_path / f"{card.star}_label.png")
label = BuildImage(40, 40, background=label_path)
img_path = str(self.img_path / f"{cn2py(card.name)}.png")
img = BuildImage(img_w, img_h, background=img_path)
bg.paste(img, (sep_w, 0), alpha=True)
bg.paste(label, (30, 3), alpha=True)
# 加名字
text = ""
texts = []
font = load_font(fontsize=25)
for t in card.name:
if font.getsize(text + t)[0] > 190:
texts.append(text)
text = ""
if len(texts) >= 2:
texts[-1] += "..."
break
else:
text += t
if text:
texts.append(text)
text = "\n".join(texts)
text_w, _ = font.getsize_multiline(text)
draw = ImageDraw.Draw(bg.markImg)
draw.text(
((img_w - text_w) / 2, img_h),
text,
font=font,
align="center",
fill="gray",
)
return bg
def _init_data(self):
self.ALL_CHAR = [
PrettyChar(
name=value["名称"],
star=int(value["初始星级"]),
limited=False,
)
for value in self.load_data().values()
]
self.ALL_CARD = [
PrettyCard(
name=value["中文名"],
star=["R", "SR", "SSR"].index(value["稀有度"]) + 1,
limited=True if "卡池" not in value["获取方式"] else False,
)
for value in self.load_data("pretty_card.json").values()
]
self.load_up_char()
def load_up_char(self):
try:
data = self.load_data(f"draw_card_up/{self.game_name}_up_char.json")
self.UP_CHAR = UpEvent.parse_obj(data.get("char", {}))
self.UP_CARD = UpEvent.parse_obj(data.get("card", {}))
except ValidationError:
logger.warning(f"{self.game_name}_up_char 解析出错")
def dump_up_char(self):
if self.UP_CHAR and self.UP_CARD:
data = {
"char": json.loads(self.UP_CHAR.json()),
"card": json.loads(self.UP_CARD.json()),
}
self.dump_data(data, f"draw_card_up/{self.game_name}_up_char.json")
async def _update_info(self):
# pretty.json
pretty_info = {}
url = "https://wiki.biligame.com/umamusume/赛马娘图鉴"
result = await self.get_url(url)
if not result:
logger.warning(f"更新 {self.game_name_cn} 出错")
else:
dom = etree.HTML(result, etree.HTMLParser())
char_list = dom.xpath("//table[@id='CardSelectTr']/tbody/tr")
for char in char_list:
try:
name = char.xpath("./td[1]/a/@title")[0]
avatar = char.xpath("./td[1]/a/img/@srcset")[0]
star = len(char.xpath("./td[3]/img"))
except IndexError:
continue
member_dict = {
"头像": unquote(str(avatar).split(" ")[-2]),
"名称": remove_prohibited_str(name),
"初始星级": star,
}
pretty_info[member_dict["名称"]] = member_dict
self.dump_data(pretty_info)
logger.info(f"{self.game_name_cn} 更新成功")
# pretty_card.json
pretty_card_info = {}
url = "https://wiki.biligame.com/umamusume/支援卡图鉴"
result = await self.get_url(url)
if not result:
logger.warning(f"更新 {self.game_name_cn} 卡牌出错")
else:
dom = etree.HTML(result, etree.HTMLParser())
char_list = dom.xpath("//table[@id='CardSelectTr']/tbody/tr")
for char in char_list:
try:
name = char.xpath("./td[1]/div/a/@title")[0]
name_cn = char.xpath("./td[3]/a/text()")[0]
avatar = char.xpath("./td[1]/div/a/img/@srcset")[0]
star = str(char.xpath("./td[5]/text()")[0]).strip()
sources = str(char.xpath("./td[7]/text()")[0]).strip()
except IndexError:
continue
member_dict = {
"头像": unquote(str(avatar).split(" ")[-2]),
"名称": remove_prohibited_str(name),
"中文名": remove_prohibited_str(name_cn),
"稀有度": star,
"获取方式": [sources] if sources else [],
}
pretty_card_info[member_dict["中文名"]] = member_dict
self.dump_data(pretty_card_info, "pretty_card.json")
logger.info(f"{self.game_name_cn} 卡牌更新成功")
# 下载头像
for value in pretty_info.values():
await self.download_img(value["头像"], value["名称"])
for value in pretty_card_info.values():
await self.download_img(value["头像"], value["中文名"])
# 下载星星
PRETTY_URL = "https://patchwiki.biligame.com/images/umamusume"
await self.download_img(
PRETTY_URL + "/1/13/e1hwjz4vmhtvk8wlyb7c0x3ld1s2ata.png", "star"
)
# 下载稀有度标志
idx = 1
for url in [
"/f/f7/afqs7h4snmvovsrlifq5ib8vlpu2wvk.png",
"/3/3b/d1jmpwrsk4irkes1gdvoos4ic6rmuht.png",
"/0/06/q23szwkbtd7pfkqrk3wcjlxxt9z595o.png",
]:
await self.download_img(PRETTY_URL + url, f"{idx}_label")
idx += 1
await self.update_up_char()
async def update_up_char(self):
announcement_url = "https://wiki.biligame.com/umamusume/公告"
result = await self.get_url(announcement_url)
if not result:
logger.warning(f"{self.game_name_cn}获取公告出错")
return
dom = etree.HTML(result, etree.HTMLParser())
announcements = dom.xpath("//div[@id='mw-content-text']/div/div/span/a")
title = ""
url = ""
for announcement in announcements:
try:
title = announcement.xpath("./@title")[0]
url = "https://wiki.biligame.com/" + announcement.xpath("./@href")[0]
if re.match(r".*?\d{8}$", title) or re.match(
r"^\d{1,2}月\d{1,2}日.*?", title
):
break
except IndexError:
continue
if not title:
logger.warning(f"{self.game_name_cn}未找到新UP公告")
return
result = await self.get_url(url)
if not result:
logger.warning(f"{self.game_name_cn}获取UP公告出错")
return
try:
start_time = None
end_time = None
char_img = ""
card_img = ""
up_chars = []
up_cards = []
soup = BeautifulSoup(result, "lxml")
heads = soup.find_all("span", {"class": "mw-headline"})
for head in heads:
if "时间" in head.text:
time = head.find_next("p").text.split("\n")[0]
if "~" in time:
start, end = time.split("~")
start_time = dateparser.parse(start)
end_time = dateparser.parse(end)
elif "赛马娘" in head.text:
char_img = head.find_next("a", {"class": "image"}).find("img")[
"src"
]
lines = str(head.find_next("p").text).split("\n")
chars = [
line
for line in lines
if "★" in line and "(" in line and ")" in line
]
for char in chars:
star = char.count("★")
name = re.split(r"[()]", char)[-2].strip()
up_chars.append(
UpChar(name=name, star=star, limited=False, zoom=70)
)
elif "支援卡" in head.text:
card_img = head.find_next("a", {"class": "image"}).find("img")[
"src"
]
lines = str(head.find_next("p").text).split("\n")
cards = [
line
for line in lines
if "R" in line and "(" in line and ")" in line
]
for card in cards:
star = 3 if "SSR" in card else 2 if "SR" in card else 1
name = re.split(r"[()]", card)[-2].strip()
up_cards.append(
UpChar(name=name, star=star, limited=False, zoom=70)
)
if start_time and end_time:
if start_time <= datetime.now() <= end_time:
self.UP_CHAR = UpEvent(
title=title,
pool_img=char_img,
start_time=start_time,
end_time=end_time,
up_char=up_chars,
)
self.UP_CARD = UpEvent(
title=title,
pool_img=card_img,
start_time=start_time,
end_time=end_time,
up_char=up_cards,
)
self.dump_up_char()
logger.info(f"成功获取{self.game_name_cn}当前up信息...当前up池: {title}")
except Exception as e:
logger.warning(f"{self.game_name_cn}UP更新出错 {type(e)}:{e}")
async def _reload_pool(self) -> Optional[Message]:
await self.update_up_char()
self.load_up_char()
if self.UP_CHAR and self.UP_CARD:
return Message(
Message.template("重载成功!\n当前UP池子:{}{:image}{:image}").format(
self.UP_CHAR.title,
self.UP_CHAR.pool_img,
self.UP_CARD.pool_img,
)
)
+314
View File
@@ -0,0 +1,314 @@
import re
import random
import dateparser
from lxml import etree
from PIL import ImageDraw
from datetime import datetime
from urllib.parse import unquote
from typing import List, Optional, Tuple
from pydantic import ValidationError
from nonebot.adapters.onebot.v11 import Message
from utils.message_builder import image
from nonebot.log import logger
import asyncio
try:
import ujson as json
except ModuleNotFoundError:
import json
from .base_handle import BaseHandle, BaseData, UpChar, UpEvent
from ..config import draw_config
from ..util import remove_prohibited_str, cn2py, load_font
from utils.image_utils import BuildImage
class Operator(BaseData):
recruit_only: bool # 公招限定
event_only: bool # 活动获得干员
# special_only: bool # 升变/异格干员
class PrtsHandle(BaseHandle[Operator]):
def __init__(self):
super().__init__(game_name="prts", game_name_cn="明日方舟")
self.max_star = 6
self.game_card_color = "#eff2f5"
self.config = draw_config.prts
self.ALL_OPERATOR: List[Operator] = []
self.UP_EVENT: Optional[UpEvent] = None
def get_card(self, add: float) -> Operator:
star = self.get_star(
star_list=[6, 5, 4, 3],
probability_list=[
self.config.PRTS_SIX_P + add,
self.config.PRTS_FIVE_P,
self.config.PRTS_FOUR_P,
self.config.PRTS_THREE_P,
],
)
all_operators = [
x
for x in self.ALL_OPERATOR
if x.star == star and not any([x.limited, x.recruit_only, x.event_only])
]
acquire_operator = None
if self.UP_EVENT:
up_operators = [x for x in self.UP_EVENT.up_char if x.star == star]
# UPs
try:
zooms = [x.zoom for x in up_operators]
zoom_sum = sum(zooms)
if random.random() < zoom_sum:
up_name = random.choices(up_operators, weights=zooms, k=1)[0].name
acquire_operator = [
x for x in self.ALL_OPERATOR if x.name == up_name
][0]
except IndexError:
pass
if not acquire_operator:
acquire_operator = random.choice(all_operators)
return acquire_operator
def get_cards(self, count: int, **kwargs) -> List[Tuple[Operator, int]]:
card_list = [] # 获取所有角色
add = 0.0
count_idx = 0
for i in range(count):
count_idx += 1
card = self.get_card(add)
if card.star == self.max_star:
add = 0.0
count_idx = 0
elif count_idx > 50:
add += 0.02
card_list.append((card, i + 1))
return card_list
def format_pool_info(self) -> str:
info = ""
if self.UP_EVENT:
star6_list = [x.name for x in self.UP_EVENT.up_char if x.star == 6]
star5_list = [x.name for x in self.UP_EVENT.up_char if x.star == 5]
star4_list = [x.name for x in self.UP_EVENT.up_char if x.star == 4]
if star6_list:
info += f"六星UP:{' '.join(star6_list)}\n"
if star5_list:
info += f"五星UP:{' '.join(star5_list)}\n"
if star4_list:
info += f"四星UP:{' '.join(star4_list)}\n"
info = f"当前up池: {self.UP_EVENT.title}\n{info}"
return info.strip()
async def draw(self, count: int, **kwargs) -> Message:
return await asyncio.get_event_loop().run_in_executor(None, self._draw, count)
def _draw(self, count: int, **kwargs) -> Message:
index2card = self.get_cards(count)
"""这里cards修复了抽卡图文不符的bug"""
cards = [card[0] for card in index2card]
up_list = [x.name for x in self.UP_EVENT.up_char] if self.UP_EVENT else []
result = self.format_result(index2card, up_list=up_list)
pool_info = self.format_pool_info()
return pool_info + image(b64=self.generate_img(cards).pic2bs4()) + result
def generate_card_img(self, card: Operator) -> BuildImage:
sep_w = 5
sep_h = 5
star_h = 15
img_w = 120
img_h = 120
font_h = 20
bg = BuildImage(img_w + sep_w * 2, img_h + font_h + sep_h * 2, color="#EFF2F5")
star_path = str(self.img_path / "star.png")
star = BuildImage(star_h, star_h, background=star_path)
img_path = str(self.img_path / f"{cn2py(card.name)}.png")
img = BuildImage(img_w, img_h, background=img_path)
bg.paste(img, (sep_w, sep_h), alpha=True)
for i in range(card.star):
bg.paste(star, (sep_w + img_w - 5 - star_h * (i + 1), sep_h), alpha=True)
# 加名字
text = card.name[:7] + "..." if len(card.name) > 8 else card.name
font = load_font(fontsize=16)
text_w, text_h = font.getsize(text)
draw = ImageDraw.Draw(bg.markImg)
draw.text(
(sep_w + (img_w - text_w) / 2, sep_h + img_h + (font_h - text_h) / 2),
text,
font=font,
fill="gray",
)
return bg
def _init_data(self):
self.ALL_OPERATOR = [
Operator(
name=value["名称"],
star=int(value["星级"]),
limited="干员寻访" not in value["获取途径"],
recruit_only=True
if "干员寻访" not in value["获取途径"] and "公开招募" in value["获取途径"]
else False,
event_only=True if "活动获取" in value["获取途径"] else False,
)
for key, value in self.load_data().items()
if "阿米娅" not in key
]
self.load_up_char()
def load_up_char(self):
try:
data = self.load_data(f"draw_card_up/{self.game_name}_up_char.json")
"""这里的 waring 有点模糊,更新游戏信息时没有up池的情况下也会报错,所以细分了一下"""
if not data:
logger.warning(f"当前无UP池或 {self.game_name}_up_char.json 文件不存在")
else:
self.UP_EVENT = UpEvent.parse_obj(data.get("char", {}))
except ValidationError:
logger.warning(f"{self.game_name}_up_char 解析出错")
def dump_up_char(self):
if self.UP_EVENT:
data = {"char": json.loads(self.UP_EVENT.json())}
self.dump_data(data, f"draw_card_up/{self.game_name}_up_char.json")
async def _update_info(self):
info = {}
url = "https://wiki.biligame.com/arknights/干员数据表"
result = await self.get_url(url)
if not result:
logger.warning(f"更新 {self.game_name_cn} 出错")
return
dom = etree.HTML(result, etree.HTMLParser())
char_list = dom.xpath("//table[@id='CardSelectTr']/tbody/tr")
for char in char_list:
try:
avatar = char.xpath("./td[1]/div/div/div/a/img/@srcset")[0]
name = char.xpath("./td[2]/a/text()")[0]
star = char.xpath("./td[5]/text()")[0]
"""这里sources修好了干员获取标签有问题的bug,如三星只能抽到卡缇就是这个原因"""
sources = [_.strip('\n') for _ in char.xpath("./td[8]/text()")]
except IndexError:
continue
member_dict = {
"头像": unquote(str(avatar).split(" ")[-2]),
"名称": remove_prohibited_str(str(name).strip()),
"星级": int(str(star).strip()),
"获取途径": sources,
}
info[member_dict["名称"]] = member_dict
self.dump_data(info)
logger.info(f"{self.game_name_cn} 更新成功")
# 下载头像
for value in info.values():
await self.download_img(value["头像"], value["名称"])
# 下载星星
await self.download_img(
"https://patchwiki.biligame.com/images/pcr/0/02/s75ys2ecqhu2xbdw1wf1v9ccscnvi5g.png",
"star",
)
await self.update_up_char()
async def update_up_char(self):
"""重载卡池"""
announcement_url = "https://ak.hypergryph.com/news.html"
result = await self.get_url(announcement_url)
if not result:
logger.warning(f"{self.game_name_cn}获取公告出错")
return
dom = etree.HTML(result, etree.HTMLParser())
activity_urls = dom.xpath(
"//ol[@class='articleList' and @data-category-key='ACTIVITY']/li/a/@href"
)
start_time = None
end_time = None
up_chars = []
pool_img = ""
for activity_url in activity_urls[:10]: # 减少响应时间, 10个就够了
activity_url = f"https://ak.hypergryph.com{activity_url}"
result = await self.get_url(activity_url)
if not result:
logger.warning(f"{self.game_name_cn}获取公告 {activity_url} 出错")
continue
"""因为鹰角的前端太自由了,这里重写了匹配规则以尽可能避免因为前端乱七八糟而导致的重载失败"""
dom = etree.HTML(result, etree.HTMLParser())
contents = dom.xpath(
"//div[@class='article-content']/p/text() | //div[@class='article-content']/p/span/text() | //div[@class='article-content']/div[@class='media-wrap image-wrap']/img/@src"
)
title = ""
time = ""
chars: List[str] = []
for index, content in enumerate(contents):
if re.search("(.*)(寻访|复刻).*?开启", content):
title = re.split(r"[【】]", content)
title = "".join(title[1:-1]) if "-" in title else title[1]
lines = [contents[index-2+_] for _ in range(8)] # 从 -2 开始是因为xpath获取的时间有的会在寻访开启这一句之前
lines.append("") # 防止IndexError,加个空字符串
for idx, line in enumerate(lines):
match = re.search(
r"(\d{1,2}月\d{1,2}日.*?-.*?\d{1,2}月\d{1,2}日.*?$)", line
)
if match:
time = match.group(1)
"""因为 <p> 的诡异排版,所以有了下面的一段"""
if ("★★" in line and "%" in line) or ("★★" in line and "%" in lines[idx + 1]):
chars.append(line) if ("★★" in line and "%" in line) else chars.append(line + lines[idx + 1])
if not time:
continue
start, end = time.replace("月", "/").replace("日", " ").split("-")[:2] # 日替换为空格是因为有日后面不接空格的情况,导致 split 出问题
start_time = dateparser.parse(start)
end_time = dateparser.parse(end)
pool_img = contents[index-2]
r"""两类格式:用/分割,用\分割;★+(概率)+名字,★+名字+(概率)"""
for char in chars:
star = char.split("(")[0].count("★")
name = re.split(r"[:(]", char)[1] if "★(" not in char else re.split("):", char)[1] # 有的括号在前面有的在后面
if "\\" in name:
names = name.split("\\")
elif "/" in name:
names = name.split("/")
else:
names = [name] # 既有用/分割的,又有用\分割的
names = [name.replace("[限定]", "").strip() for name in names]
if "权值" in char:
match = re.search(r"(在.*?以.*?(\d+).*?倍权值.*?)", char)
else:
match = re.search(r"(占.*?的.*?(\d+).*?%)", char)
zoom = 1
if match:
zoom = float(match.group(1))
zoom = zoom / 100 if zoom > 10 else zoom
for name in names:
up_chars.append(
UpChar(name=name, star=star, limited=False, zoom=zoom)
)
if start_time <= datetime.now() <= end_time: # 跳过过时的卡池
break # 这里break会导致个问题:如果一个公告里有两个池子,会漏掉下面的池子,比如 5.19 的定向寻访。但目前我也没啥好想法解决
chars: List[str] = [] # 查找下一个卡池之前清空 chars
up_chars = [] # 查找下一个卡池之前清空 up_chars
if title and start_time and end_time:
if start_time <= datetime.now() <= end_time:
self.UP_EVENT = UpEvent(
title=title,
pool_img=pool_img,
start_time=start_time,
end_time=end_time,
up_char=up_chars,
)
self.dump_up_char()
logger.info(f"成功获取{self.game_name_cn}当前up信息...当前up池: {title}")
break
async def _reload_pool(self) -> Optional[Message]:
await self.update_up_char()
self.load_up_char()
if self.UP_EVENT:
return f"重载成功!\n当前UP池子:{self.UP_EVENT.title}" + image(
self.UP_EVENT.pool_img
)
-204
View File
@@ -1,204 +0,0 @@
from typing import Any
from .config import DRAW_DATA_PATH
from .util import is_number
from nonebot.log import logger
try:
import ujson as json
except ModuleNotFoundError:
import json
def init_game_pool(game: str, data: dict, operator: Any):
tmp_lst = []
if game == "prts":
for key in data.keys():
limited = False
recruit_only = False
event_only = False
if "限定寻访" in data[key]["获取途径"]:
limited = True
if "干员寻访" not in data[key]["获取途径"] and "公开招募" in data[key]["获取途径"]:
recruit_only = True
if "活动获取" in data[key]["获取途径"]:
event_only = True
if "干员寻访" not in data[key]["获取途径"] and "凭证交易所" == data[key]["获取途径"][0]:
limited = True
if "干员寻访" not in data[key]["获取途径"] and "信用累计奖励" == data[key]["获取途径"][0]:
limited = True
if key.find("阿米娅") != -1:
continue
try:
tmp_lst.append(
operator(
name=key,
star=int(data[key]["星级"]),
limited=limited,
recruit_only=recruit_only,
event_only=event_only,
)
)
except Exception as e:
logger.warning(f"明日方舟导入角色 {key} 数据错误:{type(e)}:{e}")
if game == "genshin":
for key in data.keys():
if key.find("旅行者") != -1:
continue
limited = False
if data[key]["常驻/限定"] == "限定UP":
limited = True
try:
tmp_lst.append(
operator(name=key, star=int(data[key]["稀有度"][:1]), limited=limited)
)
except Exception as e:
logger.warning(f"原神导入角色 {key} 数据错误:{type(e)}:{e}")
if game == "genshin_arms":
for key in data.keys():
if data[key]["获取途径"].find("祈愿") != -1:
limited = False
if data[key]["获取途径"].find("限定祈愿") != -1:
limited = True
try:
tmp_lst.append(
operator(
name=key, star=int(data[key]["稀有度"][:1]), limited=limited
)
)
except Exception as e:
logger.warning(f"原神导入武器 {key} 数据错误:{type(e)}:{e}")
if game == "pretty":
for key in data.keys():
try:
tmp_lst.append(
operator(name=key, star=data[key]["初始星级"], limited=False)
)
except Exception as e:
logger.warning(f"赛马娘导入角色 {key} 数据错误:{type(e)}:{e}")
if game == "pretty_card":
for key in data.keys():
limited = False
if "卡池" not in data[key]["获取方式"]:
limited = True
if not data[key]["获取方式"]:
limited = False
try:
tmp_lst.append(
operator(
name=data[key]["中文名"],
star=len(data[key]["稀有度"]),
limited=limited,
)
)
except Exception as e:
logger.warning(f"赛马娘导入卡片 {key} 数据错误:{type(e)}:{e}")
if game in ["guardian", "guardian_arms"]:
for key in data.keys():
tmp_lst.append(
operator(name=data[key]["名称"], star=int(data[key]["星级"]), limited=False)
)
if game == "pcr":
for key in data.keys():
limited = False
if key.find("(") != -1:
limited = True
try:
tmp_lst.append(
operator(
name=data[key]["名称"], star=int(data[key]["星级"]), limited=limited
)
)
except Exception as e:
logger.warning(f"公主连接导入角色 {key} 数据错误:{type(e)}:{e}")
if game == "azur":
for key in data.keys():
if is_number(data[key]["星级"]):
limited = False
if "可以建造" not in data[key]["获取途径"]:
limited = True
try:
tmp_lst.append(
operator(
name=data[key]["名称"],
star=int(data[key]["星级"]),
limited=limited,
type_=data[key]["类型"],
)
)
except Exception as e:
logger.warning(f"碧蓝航线导入角色 {key} 数据错误:{type(e)}:{e}")
if game in ["fgo", "fgo_card"]:
for key in data.keys():
limited = False
try:
if (
"圣晶石召唤" not in data[key]["入手方式"]
and "圣晶石召唤(Story卡池)" not in data[key]["入手方式"]
):
limited = True
except KeyError:
pass
try:
tmp_lst.append(
operator(
name=data[key]["名称"], star=int(data[key]["星级"]), limited=limited
)
)
except Exception as e:
logger.warning(f"FGO导入角色 {key} 数据错误:{type(e)}:{e}")
if game == "onmyoji":
for key in data.keys():
limited = False
if key in [
"奴良陆生",
"卖药郎",
"鬼灯",
"阿香",
"蜜桃&芥子",
"犬夜叉",
"杀生丸",
"桔梗",
"朽木露琪亚",
"黑崎一护",
"灶门祢豆子",
"灶门炭治郎",
]:
limited = True
try:
tmp_lst.append(
operator(
name=data[key]["名称"], star=data[key]["星级"], limited=limited
)
)
except Exception as e:
logger.warning(f"阴阳师导入角色 {key} 数据错误:{type(e)}:{e}")
# print(tmp_lst)
char_name_lst = [x.name for x in tmp_lst]
up_char_file = (
DRAW_DATA_PATH
/ f"draw_card"
/ "draw_card_up"
/ f"{game.split('_')[0]}_up_char.json"
)
if up_char_file.exists():
data = json.load(open(up_char_file, "r", encoding="utf8"))
if len(game.split("_")) == 1:
key = "char"
else:
key = list(data.keys())[1]
for x in data[key]["up_char"]:
for char in data[key]["up_char"][x]:
if char not in char_name_lst:
if game.find("prts") != -1:
tmp_lst.append(
operator(
name=char,
star=int(x),
recruit_only=False,
event_only=False,
limited=False,
)
)
else:
tmp_lst.append(operator(name=char, star=int(x), limited=False))
return tmp_lst
-81
View File
@@ -1,81 +0,0 @@
from nonebot.adapters.onebot.v11 import MessageSegment
import random
from .update_game_requests_info import update_requests_info
from .util import generate_img, init_star_rst, BaseData, set_list, get_star, max_card
from .config import DRAW_DATA_PATH, draw_config
from dataclasses import dataclass
from .init_card_pool import init_game_pool
try:
import ujson as json
except ModuleNotFoundError:
import json
ALL_CHAR = []
@dataclass
class OnmyojiChar(BaseData):
pass
async def onmyoji_draw(count: int):
# 0 1 2
cnlist = ['SP', 'SSR', 'SR', 'R']
obj_list, obj_dict, star_list, rst = format_card_information(count)
rst = init_star_rst(star_list, cnlist, [], []) + rst
if count > 90:
obj_list = set_list(obj_list)
return MessageSegment.image("base64://" + await generate_img(obj_list, 'onmyoji', star_list)) \
+ '\n' + rst[:-1] + '\n' + max_card(obj_dict)
async def update_onmyoji_info():
global ALL_CHAR
data, code = await update_requests_info('onmyoji')
if code == 200:
ALL_CHAR = init_game_pool('onmyoji', data, OnmyojiChar)
async def init_onmyoji_data():
global ALL_CHAR
if draw_config.ONMYOJI_FLAG:
with (DRAW_DATA_PATH / 'onmyoji.json').open('r', encoding='utf8') as f:
azur_dict = json.load(f)
ALL_CHAR = init_game_pool('onmyoji', azur_dict, OnmyojiChar)
onmyoji_star = {
5: 'SP',
4: 'SSR',
3: 'SR',
2: 'R',
}
# 抽取卡池
def _get_onmyoji_card():
global ALL_CHAR
onmyoji_config = draw_config.onmyoji
star = get_star([5, 4, 3, 2], [onmyoji_config.ONMYOJI_SP, onmyoji_config.ONMYOJI_SSR, onmyoji_config.ONMYOJI_SR, onmyoji_config.ONMYOJI_R])
chars = [x for x in ALL_CHAR if x.star == onmyoji_star[star] and not x.limited]
return random.choice(chars), 5 - star
def format_card_information(count: int):
star_list = [0, 0, 0, 0]
obj_list = [] # 获取所有角色
obj_dict = {} # 获取角色次数字典
rst = ''
for i in range(count):
obj, code = _get_onmyoji_card()
star_list[code] += 1
if code == 0:
rst += f'第 {i+1} 抽获取SP {obj.name}\n'
elif code == 1:
rst += f'第 {i+1} 抽获取SSR {obj.name}\n'
try:
obj_dict[obj.name] += 1
except KeyError:
obj_dict[obj.name] = 1
obj_list.append(obj)
return obj_list, obj_dict, star_list, rst
-93
View File
@@ -1,93 +0,0 @@
from nonebot.adapters.onebot.v11 import MessageSegment
import random
from .update_game_info import update_info
from .update_game_simple_info import update_simple_info
from .util import generate_img, init_star_rst, BaseData, set_list, get_star, max_card
from .config import DRAW_DATA_PATH, draw_config
from dataclasses import dataclass
from .init_card_pool import init_game_pool
try:
import ujson as json
except ModuleNotFoundError:
import json
ALL_CHAR = []
@dataclass
class PcrChar(BaseData):
pass
async def pcr_draw(count: int):
# 0 1 2
cnlist = ['★★★', '★★', '★']
char_list, three_list, three_index_list, char_dict, star_list = _format_card_information(count)
rst = init_star_rst(star_list, cnlist, three_list, three_index_list)
if count > 90:
char_list = set_list(char_list)
return MessageSegment.image("base64://" + await generate_img(char_list, 'pcr', star_list)) \
+ '\n' + rst[:-1] + '\n' + max_card(char_dict)
async def update_pcr_info():
global ALL_CHAR
if draw_config.PCR_TAI:
url = 'https://wiki.biligame.com/pcr/角色图鉴'
data, code = await update_simple_info(url, 'pcr')
else:
url = 'https://wiki.biligame.com/pcr/角色筛选表'
data, code = await update_info(url, 'pcr')
if code == 200:
ALL_CHAR = init_game_pool('pcr', data, PcrChar)
async def init_pcr_data():
global ALL_CHAR
if draw_config.PCR_FLAG:
with (DRAW_DATA_PATH / 'pcr.json').open('r', encoding='utf8') as f:
pcr_dict = json.load(f)
ALL_CHAR = init_game_pool('pcr', pcr_dict, PcrChar)
# 抽取卡池
def _get_pcr_card(mode: int = 1):
global ALL_CHAR
pcr_config = draw_config.pcr
if mode == 2:
star = get_star([3, 2], [pcr_config.PCR_G_THREE_P, pcr_config.PCR_G_TWO_P])
else:
star = get_star([3, 2, 1], [pcr_config.PCR_THREE_P, pcr_config.PCR_TWO_P, pcr_config.PCR_ONE_P])
chars = [x for x in ALL_CHAR if x.star == star and not x.limited]
return random.choice(chars), 3 - star
def _format_card_information(_count: int):
char_list = []
star_list = [0, 0, 0]
three_index_list = []
three_list = []
char_dict = {}
# 保底计算
count = 0
for i in range(_count):
count += 1
# 十连保底
if count == 10:
char, code = _get_pcr_card(2)
count = 0
else:
char, code = _get_pcr_card()
if code < 2:
count = 0
star_list[code] += 1
if code == 0:
three_list.append(char.name)
three_index_list.append(i)
try:
char_dict[char.name] += 1
except KeyError:
char_dict[char.name] = 1
char_list.append(char)
return char_list, three_list, three_index_list, char_dict, star_list
-148
View File
@@ -1,148 +0,0 @@
from nonebot.adapters.onebot.v11 import MessageSegment
from .announcement import PrettyAnnouncement
from .update_game_info import update_info
from .util import init_star_rst, generate_img, max_card, BaseData, \
set_list, get_star, format_card_information, init_up_char
import random
from .config import DRAW_DATA_PATH, draw_config
from dataclasses import dataclass
from .init_card_pool import init_game_pool
try:
import ujson as json
except ModuleNotFoundError:
import json
announcement = PrettyAnnouncement()
ALL_CHAR = []
ALL_CARD = []
_CURRENT_CHAR_POOL_TITLE = ""
_CURRENT_CARD_POOL_TITLE = ""
UP_CHAR = []
UP_CARD = []
POOL_IMG = []
@dataclass
class PrettyChar(BaseData):
pass
async def pretty_draw(count: int, pool_name):
if pool_name == 'card':
cnlist = ['SSR', 'SR', 'R']
else:
cnlist = ['★★★', '★★', '★']
star_list = [0, 0, 0]
obj_list, obj_dict, three_list, star_list, three_olist = format_card_information(count, star_list,
_get_pretty_card, pool_name)
up_type = []
up_list = []
title = ''
if pool_name == 'char' and _CURRENT_CHAR_POOL_TITLE:
up_type = UP_CHAR
title = _CURRENT_CHAR_POOL_TITLE
elif pool_name == 'card' and _CURRENT_CARD_POOL_TITLE:
up_type = UP_CARD
title = _CURRENT_CARD_POOL_TITLE
tmp = ''
if up_type:
for x in up_type:
for operator in x.operators:
up_list.append(operator.split(']')[1] if pool_name == 'char' else operator)
if x.star == 3:
if pool_name == 'char':
tmp += f'三星UP:{" ".join(x.operators)} \n'
else:
tmp += f'SSR UP:{" ".join(x.operators)} \n'
elif x.star == 2:
if pool_name == 'char':
tmp += f'二星UP:{" ".join(x.operators)} \n'
else:
tmp += f'SR UP:{" ".join(x.operators)} \n'
elif x.star == 1:
if pool_name == 'char':
tmp += f'一星UP:{" ".join(x.operators)} '
else:
tmp += f'R UP:{" ".join(x.operators)} '
tmp = tmp[:-1] if tmp and tmp[-1] == '\n' else tmp
pool_info = f'当前up池:{title}\n{tmp}' if title else ''
rst = init_star_rst(star_list, cnlist, three_list, three_olist, up_list)
if count > 90:
obj_list = set_list(obj_list)
return pool_info + MessageSegment.image(
"base64://" + await generate_img(obj_list, 'pretty', star_list)) \
+ '\n' + rst[:-1] + '\n' + max_card(obj_dict)
async def update_pretty_info():
global ALL_CHAR, ALL_CARD
url = 'https://wiki.biligame.com/umamusume/赛马娘图鉴'
data, code = await update_info(url, 'pretty')
if code == 200:
ALL_CHAR = init_game_pool('pretty', data, PrettyChar)
url = 'https://wiki.biligame.com/umamusume/支援卡图鉴'
data, code = await update_info(url, 'pretty_card')
if code == 200:
ALL_CARD = init_game_pool('pretty_card', data, PrettyChar)
await _pretty_init_up_char()
async def init_pretty_data():
global ALL_CHAR, ALL_CARD
if draw_config.PRETTY_FLAG:
with (DRAW_DATA_PATH / 'pretty.json').open('r', encoding='utf8') as f:
pretty_char_dict = json.load(f)
with (DRAW_DATA_PATH / 'pretty_card.json').open('r', encoding='utf8') as f:
pretty_card_dict = json.load(f)
ALL_CHAR = init_game_pool('pretty', pretty_char_dict, PrettyChar)
ALL_CARD = init_game_pool('pretty_card', pretty_card_dict, PrettyChar)
await _pretty_init_up_char()
# 抽取卡池
def _get_pretty_card(pool_name: str, mode: int = 1):
global ALL_CHAR, ALL_CARD, _CURRENT_CHAR_POOL_TITLE, _CURRENT_CARD_POOL_TITLE
pretty_config = draw_config.pretty
if mode == 1:
star = get_star([3, 2, 1], [pretty_config.PRETTY_THREE_P, pretty_config.PRETTY_TWO_P, pretty_config.PRETTY_ONE_P])
else:
star = get_star([3, 2], [pretty_config.PRETTY_THREE_P, pretty_config.PRETTY_TWO_P])
if pool_name == 'card':
title = _CURRENT_CARD_POOL_TITLE
up_data = UP_CARD
data = ALL_CARD
else:
title = _CURRENT_CHAR_POOL_TITLE
up_data = UP_CHAR
data = ALL_CHAR
# 有UP池子
if title and star in [x.star for x in up_data]:
all_char_lst = [x for x in data if x.star == star and not x.limited]
# 抽到UP
if random.random() < 1 / len(all_char_lst) * (0.7 / 0.1385):
all_up_star = [x.operators for x in up_data if x.star == star][0]
acquire_operator = random.choice(all_up_star)
if pool_name == 'char':
acquire_operator = acquire_operator.split(']')[1]
acquire_operator = [x for x in data if x.name == acquire_operator][0]
else:
acquire_operator = random.choice([x for x in data if x.star == star and not x.limited])
else:
acquire_operator = random.choice([x for x in data if x.star == star and not x.limited])
return acquire_operator, 3 - star
# 获取up和概率
async def _pretty_init_up_char():
global _CURRENT_CHAR_POOL_TITLE, _CURRENT_CARD_POOL_TITLE, UP_CHAR, UP_CARD, POOL_IMG
_CURRENT_CHAR_POOL_TITLE, _CURRENT_CARD_POOL_TITLE, POOL_IMG, UP_CHAR, UP_CARD = await init_up_char(announcement)
async def reload_pretty_pool():
await _pretty_init_up_char()
return f'当前UP池子:{_CURRENT_CHAR_POOL_TITLE} & {_CURRENT_CARD_POOL_TITLE} {POOL_IMG}'
-171
View File
@@ -1,171 +0,0 @@
from nonebot.adapters.onebot.v11 import MessageSegment, Message
import random
from .config import DRAW_DATA_PATH, draw_config
from .update_game_info import update_info
from .util import generate_img, init_star_rst, max_card, BaseData, UpEvent, set_list, get_star
from .init_card_pool import init_game_pool
from .announcement import PrtsAnnouncement
from dataclasses import dataclass
from nonebot.log import logger
try:
import ujson as json
except ModuleNotFoundError:
import json
announcement = PrtsAnnouncement()
prts_dict = {}
UP_OPERATOR = []
ALL_OPERATOR = []
_CURRENT_POOL_TITLE = ''
POOL_IMG = ''
@dataclass
class Operator(BaseData):
recruit_only: bool # 公招限定
event_only: bool # 活动获得干员
# special_only: bool # 升变/异格干员
async def prts_draw(count: int = 300):
cnlist = ['★★★★★★', '★★★★★', '★★★★', '★★★']
star_list = [0, 0, 0, 0]
operator_list, operator_dict, six_list, star_list, six_index_list = format_card_information(count, star_list)
up_list = []
tmp = ''
if _CURRENT_POOL_TITLE:
for x in UP_OPERATOR:
for operator in x.operators:
up_list.append(operator)
if x.star == 6:
tmp += f'六星UP:{" ".join(x.operators)} \n'
elif x.star == 5:
tmp += f'五星UP:{" ".join(x.operators)} \n'
elif x.star == 4:
tmp += f'四星UP:{" ".join(x.operators)}'
rst = init_star_rst(star_list, cnlist, six_list, six_index_list, up_list)
if count > 90:
operator_list = set_list(operator_list)
pool_info = f"当前up池: {_CURRENT_POOL_TITLE}\n{tmp}" if _CURRENT_POOL_TITLE else ""
return pool_info + MessageSegment.image(
"base64://" + await generate_img(operator_list, 'prts', star_list)) \
+ '\n' + rst[:-1] + '\n' + max_card(operator_dict)
async def update_prts_info():
global prts_dict, ALL_OPERATOR
url = 'https://wiki.biligame.com/arknights/干员数据表'
data, code = await update_info(url, 'prts', ['头像', '名称', '阵营', '星级', '性别', '是否感染', '获取途径', '初始生命', '初始防御',
'初始法抗', '再部署', '部署费用', '阻挡数', '攻击速度', '标签'])
if code == 200:
prts_dict = data
ALL_OPERATOR = init_game_pool('prts', prts_dict, Operator)
await _init_up_char()
async def init_prts_data():
global prts_dict, ALL_OPERATOR
if draw_config.PRTS_FLAG:
with (DRAW_DATA_PATH / 'prts.json').open('r', encoding='utf8') as f:
prts_dict = json.load(f)
ALL_OPERATOR = init_game_pool('prts', prts_dict, Operator)
await _init_up_char()
# 抽取干员
def _get_operator_card(add: float):
prts_config = draw_config.prts
star = get_star([6, 5, 4, 3], [prts_config.PRTS_SIX_P + add, prts_config.PRTS_FIVE_P, prts_config.PRTS_FOUR_P, prts_config.PRTS_THREE_P])
if _CURRENT_POOL_TITLE:
zooms = [x.zoom for x in UP_OPERATOR if x.star == star]
zoom = 0
weight = 0
# 分配概率和权重
for z in zooms:
if z < 1:
zoom = z
else:
weight = z
up_operator_name = ""
# UPs
try:
if 0 < zoom:
up_operators = [x.operators for x in UP_OPERATOR if x.star == star and x.zoom < 1][0]
up_operator_name = random.choice(up_operators)
acquire_operator = [x for x in ALL_OPERATOR if x.name == up_operator_name][0]
else:
all_star_operators = [x for x in ALL_OPERATOR if x.star == star
and not any([x.limited, x.event_only, x.recruit_only])]
weight_up_operators = [x.operators for x in UP_OPERATOR if x.star == star and x.zoom > 1]
# 权重
if weight_up_operators and random.random() < 1.0 / float(len(all_star_operators)) * weight:
up_operator_name = random.choice(weight_up_operators[0])
acquire_operator = [x for x in ALL_OPERATOR if x.name == up_operator_name][0]
else:
acquire_operator = random.choice(all_star_operators)
except IndexError:
acquire_operator = Operator(up_operator_name, star, True, False, False)
else:
acquire_operator = random.choice([x for x in ALL_OPERATOR if x.star == star
and not any([x.limited, x.event_only, x.recruit_only])])
return acquire_operator, 6 - star
# 整理数据
def format_card_information(count: int, star_list: list):
max_star_lst = [] # 获取的最高星级角色列表
max_index_lst = [] # 获取最高星级角色的次数
obj_list = [] # 获取所有角色
obj_dict = {} # 获取角色次数字典
add = 0.0
count_idx = 0
for i in range(count):
count_idx += 1
obj, code = _get_operator_card(add)
star_list[code] += 1
if code == 0:
max_star_lst.append(obj.name)
max_index_lst.append(i)
add = 0.0
count_idx = 0
elif count_idx > 50:
add += 0.02
try:
obj_dict[obj.name] += 1
except KeyError:
obj_dict[obj.name] = 1
obj_list.append(obj)
return obj_list, obj_dict, max_star_lst, star_list, max_index_lst
# 获取up干员和概率
async def _init_up_char():
global _CURRENT_POOL_TITLE, POOL_IMG, UP_OPERATOR
UP_OPERATOR = []
up_char_dict = await announcement.update_up_char()
_CURRENT_POOL_TITLE = up_char_dict['char']['title']
if _CURRENT_POOL_TITLE:
POOL_IMG = MessageSegment.image(up_char_dict['char']['pool_img'])
up_char_dict = up_char_dict['char']['up_char']
logger.info(f'成功获取明日方舟当前up信息...当前up池: {_CURRENT_POOL_TITLE}')
average_dict = {'6': {}, '5': {}, '4': {}}
for star in up_char_dict.keys():
for key in up_char_dict[star].keys():
if average_dict[star].get(up_char_dict[star][key]):
average_dict[star][up_char_dict[star][key]].append(key)
else:
average_dict[star][up_char_dict[star][key]] = [key]
for star in average_dict.keys():
for str_zoom in average_dict[star].keys():
if str_zoom[0] == '权':
zoom = float(str_zoom[1:])
else:
zoom = float(str_zoom) / 100
UP_OPERATOR.append(UpEvent(star=int(star), operators=average_dict[star][str_zoom], zoom=zoom))
async def reload_prts_pool():
await _init_up_char()
return Message(f'当前UP池:{_CURRENT_POOL_TITLE} {POOL_IMG}')
Executable → Regular
-29
View File
@@ -1,29 +0,0 @@
from nonebot.rule import Rule
from nonebot.adapters.onebot.v11 import Bot, MessageEvent
from nonebot.typing import T_State
from .config import draw_config
def is_switch(game_name: str) -> Rule:
async def _is_switch(bot: Bot, event: MessageEvent, state: T_State) -> bool:
if game_name == 'prts':
return draw_config.PRTS_FLAG
if game_name == 'genshin':
return draw_config.GENSHIN_FLAG
if game_name == 'pretty':
return draw_config.PRETTY_FLAG
if game_name == 'guardian':
return draw_config.GUARDIAN_FLAG
if game_name == 'pcr':
return draw_config.PCR_FLAG
if game_name == 'azur':
return draw_config.AZUR_FLAG
if game_name == 'fgo':
return draw_config.FGO_FLAG
if game_name == 'onmyoji':
return draw_config.ONMYOJI_FLAG
else:
return False
return Rule(_is_switch)
-295
View File
@@ -1,295 +0,0 @@
from typing import Tuple
from .config import DRAW_DATA_PATH
from asyncio.exceptions import TimeoutError
from bs4 import BeautifulSoup
from .util import download_img
from urllib.parse import unquote
from .util import remove_prohibited_str
from utils.http_utils import AsyncHttpx
from nonebot.log import logger
import bs4
import re
try:
import ujson as json
except ModuleNotFoundError:
import json
headers = {'User-Agent': '"Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 5.1; TencentTraveler 4.0)"'}
async def update_info(url: str, game_name: str, info_list: list = None) -> Tuple[dict, int]:
info_path = DRAW_DATA_PATH / f"{game_name}.json"
try:
with info_path.open('r', encoding='utf8') as f:
data = json.load(f)
except (ValueError, FileNotFoundError):
data = {}
try:
response = await AsyncHttpx.get(url, timeout=7)
soup = BeautifulSoup(response.text, 'lxml')
_tbody = get_tbody(soup, game_name, url)
trs = _tbody.find_all('tr')
att_dict, start_index, index = init_attr(game_name)
if game_name == 'guardian':
start_index = 1
if game_name == 'azur':
start_index = 0
for th in trs[0].find_all('th')[start_index:]:
text = th.text
if text[-1] == '\n':
text = text[:-1]
att_dict[text] = index
index += 1
for tr in trs[1:]:
member_dict = {}
tds = tr.find_all('td')
if not info_list:
info_list = att_dict.keys()
for key in info_list:
key, attr = parse_key(key, game_name)
td = tds[att_dict[key]]
last_tag = unquote(_find_last_tag(td, attr, game_name), 'utf-8')
member_dict[key] = last_tag
member_dict = intermediate_check(member_dict, key, game_name, td)
avatar_img = await _modify_avatar_url(game_name, member_dict["名称"])
member_dict['头像'] = avatar_img if avatar_img else member_dict['头像']
member_dict, name = replace_update_name(member_dict, game_name)
await download_img(member_dict['头像'], game_name, name)
data[name] = member_dict
logger.info(f'{name} is update...')
data = await _last_check(data, game_name)
except TimeoutError:
logger.warning(f'更新 {game_name} 超时...')
return {}, 999
with info_path.open('w', encoding='utf8') as wf:
wf.write(json.dumps(data, ensure_ascii=False, indent=4))
return data, 200
def _find_last_tag(element: bs4.element.Tag, attr: str, game_name: str) -> str:
last_tag = []
for des in element.descendants:
last_tag.append(des)
if len(last_tag) == 1 and last_tag[0] == '\n':
last_tag = ''
elif last_tag[-1] == '\n':
last_tag = last_tag[-2]
else:
last_tag = last_tag[-1]
if attr and str(last_tag):
last_tag = last_tag[attr]
elif str(last_tag).find('<img') != -1:
if last_tag.get('srcset'):
last_tag = str(last_tag.get('srcset')).strip().split(' ')[-2].strip()
else:
last_tag = last_tag['src']
else:
last_tag = str(last_tag)
if str(last_tag) and str(last_tag)[-1] == '\n':
last_tag = str(last_tag)[:-1]
if game_name not in ['pretty', 'pretty_card', 'guardian'] and last_tag.find('http') == -1:
last_tag = last_tag.split('.')[0]
return last_tag
# 获取大图(小图快爬)
async def _modify_avatar_url(game_name: str, char_name: str):
if game_name == 'prts':
res = await AsyncHttpx.get(f'https://wiki.biligame.com/arknights/{char_name}', timeout=7)
soup = BeautifulSoup(res.text, 'lxml')
try:
img_url = str(soup.find('img', {'class': 'img-bg'})['srcset']).split(' ')[-2]
except KeyError:
img_url = str(soup.find('img', {'class': 'img-bg'})['src'])
return img_url
if game_name == 'genshin':
return None
if game_name == 'pretty_card':
res = await AsyncHttpx.get(f'https://wiki.biligame.com/umamusume/{char_name}', timeout=7)
soup = BeautifulSoup(res.text, 'lxml')
try:
img_url = soup.find('div', {'class': 'support_card-left'}).find('div').find('img').get('src')
return img_url
except AttributeError:
logger.warning("pretty_card 获取大图像失败")
return None
if game_name == 'guardian':
# 未上传图片太多,换成像素图
# async with session.get(f'https://wiki.biligame.com/gt/{char_name}', timeout=7) as res:
# soup = BeautifulSoup(await res.text(), 'lxml')
# soup = soup.find('table', {'class': 'wikitable'}).find('tbody').find('tr')
# try:
# img_url = str(soup.find('img', {'class': 'img-kk'})['srcset']).split(' ')[-2]
# except KeyError:
# img_url = str(soup.find('img', {'class': 'img-kk'})['src'])
# except TypeError:
# print(f'{char_name} 图片还未上传,跳过...')
# img_url = ''
# return img_url
return None
# 数据最后处理(是否需要额外数据或处理数据)
async def _last_check(data: dict, game_name: str):
# if game_name == 'prts':
# tasks = []
# for key in data.keys():
# tasks.append(asyncio.ensure_future(_async_update_prts_extra_info(key, session)))
# asyResult = await asyncio.gather(*tasks)
# for x in asyResult:
# for key in x.keys():
# data[key]['获取途径'] = x[key]['获取途径']
if game_name == 'genshin':
for key in data.keys():
res = await AsyncHttpx.get(f'https://wiki.biligame.com/ys/{key}', timeout=7)
soup = BeautifulSoup(res.text, 'lxml')
_trs = ''
for table in soup.find_all('table', {'class': 'wikitable'}):
if str(table).find('常驻/限定') != -1:
_trs = table.find('tbody').find_all('tr')
break
for tr in _trs:
data[key]['常驻/限定'] = '未知'
if str(tr).find('限定UP') != -1:
data[key]['常驻/限定'] = '限定UP'
logger.info(f'原神获取额外数据 {key}...{data[key]["常驻/限定"]}')
break
elif str(tr).find('常驻UP') != -1:
data[key]['常驻/限定'] = '常驻UP'
logger.info(f'原神获取额外数据 {key}...{data[key]["常驻/限定"]}')
break
if game_name == 'pretty':
for keys in data.keys():
for key in data[keys].keys():
r = re.search(r'.*?40px-(.*)图标.png', str(data[keys][key]))
if r:
data[keys][key] = r.group(1)
logger.info(f'赛马娘额外修改数据...{keys}[{key}]=> {r.group(1)}')
if game_name == 'guardian':
for keys in data.keys():
for key in data[keys].keys():
r = re.search(r'.*?-star_(.*).png', str(data[keys][key]))
if r:
data[keys][key] = r.group(1)
logger.info(f'坎公骑士剑额外修改数据...{keys}[{key}] => {r.group(1)}')
return data
# 对抓取每行数据是否需要额外处理?
def intermediate_check(member_dict: dict, key: str, game_name: str, td: bs4.element.Tag):
if game_name == "genshin_arms":
if key == "稀有度":
member_dict["稀有度"] = td.find("img")["alt"].split('.')[0]
if game_name == 'prts':
if key == '获取途径':
msg = re.search('<td.*?>([\\s\\S]*)</td>', str(td)).group(1).strip()
msg = msg[:-1] if msg and msg[-1] == '\n' else msg
if msg.find('<a') != -1:
for a in td.find_all('a'):
msg = msg.replace(str(a), a.text)
member_dict['获取途径'] = msg.split('<br/>')
if game_name == 'pretty':
if key == '初始星级':
member_dict['初始星级'] = len(td.find_all('img'))
if game_name == 'pretty_card':
if key == '获取方式':
obtain = []
for x in str(td.text).replace('\n', '').strip().split('、'):
if x:
obtain.append(x)
member_dict['获取方式'] = obtain
if game_name == 'guardian':
if key == '头像':
member_dict['星级'] = str(td.find('span').find('img')['alt'])[-5]
try:
member_dict['头像'] = str(td.find('img')['srcset']).split(' ')[0]
except KeyError:
member_dict['头像'] = str(td.find('img')['src'])
return member_dict
def init_attr(game_name: str):
att_dict = {'头像': 0, '名称': 1}
start_index = 2
index = 2
if game_name == 'guardian':
att_dict = {'头像': 0, '名称': 0}
start_index = 1
index = 1
return att_dict, start_index, index
# 解析key
def parse_key(key: str, game_name):
attr = ''
if game_name == 'genshin_arms':
if key.find('.') != -1:
key = key.split('.')
attr = key[-1]
key = key[0]
return key, attr
# 拿到名称
def replace_update_name(member_dict: dict, game_name: str):
name = member_dict['名称']
if game_name == 'pretty_card':
name = member_dict['中文名']
name = remove_prohibited_str(name)
member_dict['中文名'] = name
else:
name = remove_prohibited_str(name)
member_dict['名称'] = name
return member_dict, name
# 拿到tbody,不同游戏tbody可能不同
def get_tbody(soup: bs4.BeautifulSoup, game_name: str, url: str):
max_count = 0
_tbody = None
if game_name == 'guardian_arms':
if url[-2:] == '盾牌':
div = soup.find('div', {'class': 'resp-tabs-container'}).find_all('div', {'class': 'resp-tab-content'})[1]
_tbody = div.find('tbody')
else:
div = soup.find('div', {'class': 'resp-tabs-container'}).find_all('div', {'class': 'resp-tab-content'})[0]
_tbody = div.find('table', {'id': 'CardSelectTr'}).find('tbody')
else:
for tbody in soup.find_all('tbody'):
if len(tbody.find_all('tr')) > max_count:
_tbody = tbody
max_count = len(tbody.find_all('tr'))
return _tbody
# async def _async_update_prts_extra_info(key: str, session: aiohttp.ClientSession):
# for i in range(10):
# try:
# async with session.get(f'https://wiki.biligame.com/arknights/{key}', timeout=7) as res:
# soup = BeautifulSoup(await res.text(), 'lxml')
# obtain = str(soup.find('table', {'class': 'wikitable'}).find('tbody').find_all('td')[-1])
# obtain = re.search(r'<td.*?>([\s\S]*)</.*?', obtain).group(1)
# obtain = obtain[:-1] if obtain[-1] == '\n' else obtain
# if obtain.find('<br/>'):
# obtain = obtain.split('<br/>')
# elif obtain.find('<br>'):
# obtain = obtain.split('<br>')
# for i in range(len(obtain)):
# if obtain[i].find('<a') != -1:
# text = ''
# for msg in obtain[i].split('</a>'):
# r = re.search('>(.*)', msg)
# if r:
# text += r.group(1) + ' '
# obtain[i] = obtain[i].split('<a')[0] + text[:-1] + obtain[i].split('</a>')[-1]
# print(f'明日方舟获取额外信息 {key}...{obtain}')
# x = {key: {}}
# x[key]['获取途径'] = obtain
# return x
# except TimeoutError:
# print(f'访问 https://wiki.biligame.com/arknights/{key} 第 {i}次 超时...已再次访问')
# return {}
@@ -1,156 +0,0 @@
from .config import DRAW_DATA_PATH, draw_config
from asyncio.exceptions import TimeoutError
from .util import download_img
from bs4 import BeautifulSoup
from .util import remove_prohibited_str
from utils.http_utils import AsyncHttpx
from nonebot.log import logger
import asyncio
try:
import ujson as json
except ModuleNotFoundError:
import json
headers = {'User-Agent': '"Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 5.1; TencentTraveler 4.0)"'}
async def update_requests_info(game_name: str):
info_path = DRAW_DATA_PATH / f"{game_name}.json"
try:
with info_path.open('r', encoding='utf8') as f:
data = json.load(f)
except (ValueError, FileNotFoundError):
data = {}
try:
if game_name in ['fgo', 'fgo_card']:
if game_name == 'fgo':
url = 'http://fgo.vgtime.com/servant/ajax?card=&wd=&ids=&sort=12777&o=desc&pn='
else:
url = 'http://fgo.vgtime.com/equipment/ajax?wd=&ids=&sort=12958&o=desc&pn='
for i in range(9999):
response = await AsyncHttpx.get(f'{url}{i}', timeout=7)
fgo_data = json.loads(response.text)
if int(fgo_data['nums']) == 0:
break
for x in fgo_data['data']:
x['name'] = remove_prohibited_str(x['name'])
key = x['name']
data = add_to_data(data, x, game_name)
await download_img(data[key]['头像'], game_name, key)
logger.info(f'{key} is update...')
if game_name == 'onmyoji':
url = 'https://yys.res.netease.com/pc/zt/20161108171335/js/app/all_shishen.json?v74='
response = await AsyncHttpx.get(url, timeout=7)
onmyoji_data = response.json()
for x in onmyoji_data:
x['name'] = remove_prohibited_str(x['name'])
key = x['name']
data = add_to_data(data, x, game_name)
logger.info(f'{key} is update...')
data = await _last_check(data, game_name)
except TimeoutError:
logger.warning(f'更新 {game_name} 超时...')
return {}, 999
except Exception as e:
logger.warning(f'更新 {game_name} 失败 {type(e)}:{e}...')
return {}, 999
with info_path.open('w', encoding='utf8') as wf:
json.dump(data, wf, ensure_ascii=False, indent=4)
return data, 200
# 添加到字典
def add_to_data(data: dict, x: dict, game_name: str) -> dict:
member_dict = {}
if game_name == 'fgo':
member_dict = {
'id': x['id'],
'card_id': x['charid'],
'头像': x['icon'],
'名称': x['name'],
'职阶': x['classes'],
'星级': x['star'],
'hp': x['lvmax4hp'],
'atk': x['lvmax4atk'],
'card_quick': x['cardquick'],
'card_arts': x['cardarts'],
'card_buster': x['cardbuster'],
'宝具': x['tprop'],
}
if game_name == 'fgo_card':
member_dict = {
'id': x['id'],
'card_id': x['equipid'],
'头像': x['icon'],
'名称': x['name'],
'星级': x['star'],
'hp': x['lvmax_hp'],
'atk': x['lvmax_atk'],
'skill_e': x['skill_e'].split('<br />')[: -1],
}
if game_name == 'onmyoji':
member_dict = {
'id': x['id'],
'名称': x['name'],
'星级': x['level'],
}
data[member_dict['名称']] = member_dict
return data
# 获取额外数据
async def _last_check(data: dict, game_name: str) -> dict:
if game_name == 'fgo':
url = 'http://fgo.vgtime.com/servant/'
tasks = []
semaphore = asyncio.Semaphore(draw_config.SEMAPHORE)
for key in data.keys():
tasks.append(asyncio.ensure_future(
_async_update_fgo_extra_info(url, key, data[key]['id'], semaphore)))
result = await asyncio.gather(*tasks)
for x in result:
for key in x.keys():
data[key]['入手方式'] = x[key]['入手方式']
if game_name == 'onmyoji':
url = 'https://yys.163.com/shishen/{}.html'
for key in data.keys():
response = await AsyncHttpx.get(f'{url.format(data[key]["id"])}', timeout=7)
soup = BeautifulSoup(response.text, 'lxml')
data[key]['头像'] = "https:" + soup.find('div', {'class': 'pic_wrap'}).find('img')['src']
await download_img(data[key]['头像'], game_name, key)
return data
async def _async_update_fgo_extra_info(url: str, key: str, _id: str, semaphore):
# 防止访问超时
async with semaphore:
for i in range(10):
try:
response = await AsyncHttpx.get(f'{url}{_id}', timeout=7)
soup = BeautifulSoup(response.text, 'lxml')
obtain = soup.find('table', {'class': 'uk-table uk-codex-table'}).find_all('td')[-1].text
if obtain.find('限时活动免费获取 活动结束后无法获得') != -1:
obtain = ['活动获取']
elif obtain.find('非限时UP无法获得') != -1:
obtain = ['限时召唤']
else:
if obtain.find('&') != -1:
obtain = obtain.strip().split('&')
else:
obtain = obtain.strip().split(' ')
logger.info(f'Fgo获取额外信息 {key}....{obtain}')
x = {key: {}}
x[key]['入手方式'] = obtain
return x
except TimeoutError:
logger.warning(f'访问{url}{_id} 第 {i}次 超时...已再次访问')
except Exception as e:
logger.warning(f'访问{url}{_id} 第 {i}次 发生错误:{e}...已再次访问')
return {}
@@ -1,225 +0,0 @@
from typing import Tuple
from .config import DRAW_DATA_PATH, draw_config
from asyncio.exceptions import TimeoutError
from bs4 import BeautifulSoup
from .util import download_img
from .util import remove_prohibited_str
from urllib.parse import unquote
from utils.http_utils import AsyncHttpx
from nonebot.log import logger
import bs4
import asyncio
try:
import ujson as json
except ModuleNotFoundError:
import json
headers = {
"User-Agent": '"Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 5.1; TencentTraveler 4.0)"'
}
async def update_simple_info(url: str, game_name: str) -> Tuple[dict, int]:
info_path = DRAW_DATA_PATH / f"{game_name}.json"
try:
with info_path.open("r", encoding="utf8") as f:
data = json.load(f)
except (ValueError, FileNotFoundError):
data = {}
try:
response = await AsyncHttpx.get(url, timeout=7)
soup = BeautifulSoup(response.text, "lxml")
divs = get_char_divs(soup, game_name)
for div in divs:
type_lst = get_type_lst(div, game_name)
index = 0
for char_lst in type_lst:
try:
contents = get_char_lst_contents(char_lst, game_name)
except AttributeError:
continue
for char in contents[1:]:
try:
data = await retrieve_char_data(
char, game_name, data, index
)
except AttributeError:
continue
index += 1
data = await _last_check(data, game_name)
except TimeoutError:
logger.warning(f"更新 {game_name} 超时...")
return {}, 999
with info_path.open("w", encoding="utf8") as wf:
wf.write(json.dumps(data, ensure_ascii=False, indent=4))
return data, 200
# 获取所有包含需要图片的divs
def get_char_divs(soup: bs4.BeautifulSoup, game_name: str) -> bs4.element.ResultSet:
# if game_name == "pcr":
# return soup.find_all("div", {"class": "tabbertab"})
if game_name in ["azur", "pcr"]:
return soup.find_all("div", {"class": "resp-tabs"})
# 拿到所有类型
def get_type_lst(div: bs4.element.Tag, game_name: str):
if game_name in ["pcr", "azur"]:
return div.find("div", {"class": "resp-tabs-container"}).find_all(
"div", {"class": "resp-tab-content"}
)
# 获取所有角色div
def get_char_lst_contents(char_lst: bs4.element.Tag, game_name: str):
contents = []
# print(len(char_lst.find_all('tr')))
if game_name == "pcr":
contents = char_lst.contents
if game_name == "azur":
contents = char_lst.find("table").find("tbody").contents[-1].find("td").contents
return [x for x in contents if x != "\n"]
# 额外数据
async def _last_check(
data: dict, game_name: str
) -> dict:
if game_name == "azur":
idx = 1
for url in [
"https://patchwiki.biligame.com/images/blhx/thumb/1/15/pxho13xsnkyb546tftvh49etzdh74cf.png/60px"
"-舰娘头像外框普通.png",
"https://patchwiki.biligame.com/images/blhx/thumb/a/a9/k8t7nx6c8pan5vyr8z21txp45jxeo66.png/60px"
"-舰娘头像外框稀有.png",
"https://patchwiki.biligame.com/images/blhx/thumb/a/a5/5whkzvt200zwhhx0h0iz9qo1kldnidj.png/60px"
"-舰娘头像外框精锐.png",
"https://patchwiki.biligame.com/images/blhx/thumb/a/a2/ptog1j220x5q02hytpwc8al7f229qk9.png/60px-"
"舰娘头像外框超稀有.png",
]:
await download_img(url, "azur", f"{idx}_star")
idx += 1
tasks = []
semaphore = asyncio.Semaphore(draw_config.SEMAPHORE)
for key in data.keys():
tasks.append(
asyncio.ensure_future(
_async_update_azur_extra_info(key, semaphore)
)
)
result = await asyncio.gather(*tasks)
for x in result:
for key in x.keys():
data[key]["获取途径"] = x[key]["获取途径"]
return data
azur_type = {
"0": "驱逐",
"1": "轻巡",
"2": "重巡",
"3": "超巡",
"4": "战巡",
"5": "战列",
"6": "航母",
"7": "航站",
"8": "轻航",
"9": "重炮",
"10": "维修",
"11": "潜艇",
"12": "运输",
}
# 整理数据
async def retrieve_char_data(
char: bs4.element.Tag,
game_name: str,
data: dict,
index: int = 0,
) -> dict:
member_dict = {}
if game_name == "pcr":
member_dict = {
"头像": unquote(char.find("a").find("img")["src"]),
"名称": remove_prohibited_str(char.find("a")["title"]),
"星级": 3 - index,
}
if game_name == "azur":
char = char.find("div").find("div").find("div")
avatar_img = char.find("a").find("img")
char = char.find("div")
try:
member_dict["名称"] = remove_prohibited_str(char.find("a")["title"])
except TypeError:
member_dict["名称"] = char.find("a")["title"][:-4]
try:
member_dict["头像"] = unquote(str(avatar_img["srcset"]).split(" ")[-2])
except KeyError:
member_dict["头像"] = unquote(str(avatar_img["src"]).split(" ")[-2])
except TypeError:
member_dict["头像"] = "img link not find..."
logger.warning(f'{member_dict["名称"]} 图片缺失....')
star = char.find("img")["alt"]
if star == "舰娘头像外框普通.png":
star = 1
elif star == "舰娘头像外框稀有.png":
star = 2
elif star == "舰娘头像外框精锐.png":
star = 3
elif star == "舰娘头像外框超稀有.png":
star = 4
elif star == "舰娘头像外框海上传奇.png":
star = 5
elif star in [
"舰娘头像外框最高方案.png",
"舰娘头像外框决战方案.png",
"舰娘头像外框超稀有META.png",
"舰娘头像外框精锐META.png",
]:
star = 6
else:
star = 6
member_dict["星级"] = star
member_dict["类型"] = azur_type[str(index)]
await download_img(member_dict["头像"], game_name, member_dict["名称"])
data[member_dict["名称"]] = member_dict
logger.info(f'{member_dict["名称"]} is update...')
return data
async def _async_update_azur_extra_info(
key: str, semaphore
):
if key[-1] == "改":
return {key: {"获取途径": ["无法建造"]}}
async with semaphore:
for i in range(20):
try:
res = await AsyncHttpx.get(f"https://wiki.biligame.com/blhx/{key}", timeout=7)
soup = BeautifulSoup(res.text, "lxml")
try:
construction_time = str(
soup.find("table", {"class": "wikitable sv-general"}).find(
"tbody"
)
)
x = {key: {"获取途径": []}}
if construction_time.find("无法建造") != -1:
x[key]["获取途径"].append("无法建造")
elif construction_time.find("活动已关闭") != -1:
x[key]["获取途径"].append("活动限定")
else:
x[key]["获取途径"].append("可以建造")
logger.info(f'碧蓝航线获取额外信息 {key}...{x[key]["获取途径"]}')
except AttributeError:
x = {key: {"获取途径": []}}
logger.warning(f"碧蓝航线获取额外信息错误 {key}...{[]}")
return x
except TimeoutError:
logger.warning(
f"访问 https://wiki.biligame.com/blhx/{key} 第 {i}次 超时...已再次访问"
)
return {}
Executable → Regular
+39 -341
View File
@@ -1,347 +1,16 @@
import platform
from asyncio.exceptions import TimeoutError
from utils.utils import cn2py
from utils.http_utils import AsyncHttpx
from typing import List, Tuple, Union, Set
from .config import draw_config, DRAW_IMAGE_PATH
import nonebot
from PIL import UnidentifiedImageError
from utils.image_utils import BuildImage
from nonebot.adapters.onebot.v11 import MessageSegment
from nonebot.log import logger
import random
from dataclasses import dataclass
import os
import asyncio
import pypinyin
from PIL.ImageFont import FreeTypeFont
from PIL import Image, ImageDraw, ImageFont
from PIL.Image import Image as IMG
from configs.path_config import FONT_PATH
driver = nonebot.get_driver()
loop = asyncio.get_event_loop()
@dataclass
class BaseData:
name: str
star: int
limited: bool # 限定
@dataclass
class UpEvent:
star: int # 对应up星级
operators: List[BaseData] # 干员列表
zoom: float # up提升倍率
async def download_img(url: str, path: str, name: str) -> bool:
path = path.split("_")[0]
codename = cn2py(name)
img_path = DRAW_IMAGE_PATH / f"{path}" / f"{codename}.png"
if not img_path.exists():
try:
if await AsyncHttpx.download_file(url, img_path):
logger.info(
f"下载 {draw_config.path_dict[path]} 图片成功,名称:{name},url:{url}"
)
return True
except TimeoutError:
logger.warning(f"下载 {draw_config.path_dict[path]} 图片超时,名称:{name},url:{url}")
# logger.info(f'{path_dict[path]} 图片 {name} 已存在')
return False
@driver.on_startup
def _check_dir():
for dir_name in draw_config.path_dict.keys():
dir_path = DRAW_IMAGE_PATH / f"{dir_name}"
if not dir_path.exists():
dir_path.mkdir(parents=True, exist_ok=True)
async def generate_img(
card_set: Union[Set[BaseData], List[BaseData]], game_name: str, star_list: list
) -> str:
# try:
img_list = []
background_list = []
for x in card_set:
if game_name == "prts":
if x.star == 6:
background_list.append("#FFD700")
elif x.star == 5:
background_list.append("#DAA520")
elif x.star == 4:
background_list.append("#9370D8")
else:
background_list.append("white")
img_path = DRAW_IMAGE_PATH / f"{game_name}" / f"{x.star}_star.png"
if game_name == "azur":
if img_path.exists():
background_list.append(str(img_path))
py_name = cn2py(x.name)
img_list.append(str(DRAW_IMAGE_PATH / f"{game_name}" / f"{py_name}.png"))
img_len = len(img_list)
w = 100 * 10
if img_len <= 10:
w = 100 * img_len
h = 100
elif img_len % 10 == 0:
h = 100 * int(img_len / 10)
else:
h = 100 * int(img_len / 10) + 100
card_img = await asyncio.get_event_loop().run_in_executor(
None, _pst, h, img_list, game_name, background_list
)
num = 0
for n in star_list:
num += n
A = BuildImage(w, h)
A.paste(card_img)
return A.pic2bs4()
def _pst(h: int, img_list: list, game_name: str, background_list: list):
card_img = BuildImage(100 * 10, h, 100, 100)
idx = 0
for img in img_list:
try:
if game_name == "prts":
bk = BuildImage(100, 100, color=background_list[idx])
b = BuildImage(94, 94, background=img)
bk.paste(b, (3, 3))
b = bk
elif game_name == "azur" and background_list:
bk = BuildImage(100, 100, background=background_list[idx])
b = BuildImage(98, 90, background=img)
bk.paste(b, (1, 5))
b = bk
else:
try:
b = BuildImage(100, 100, background=img)
except UnidentifiedImageError as e:
logger.warning(f"无法识别图片 已删除图片,下次更新重新下载... e:{e}")
if os.path.exists(img):
os.remove(img)
b = BuildImage(100, 100, color="black")
except FileNotFoundError:
logger.warning(f"{img} not exists")
b = BuildImage(100, 100, color="black")
card_img.paste(b)
idx += 1
return card_img
# 初始化输出数据
def init_star_rst(
star_list: list,
cnlist: list,
max_star_list: list,
max_star_index_list: list,
up_list: list = None,
) -> str:
if not up_list:
up_list = []
rst = ""
for i in range(len(star_list)):
if star_list[i]:
rst += f"[{cnlist[i]}×{star_list[i]}] "
rst += "\n"
for i in range(len(max_star_list)):
if max_star_list[i] in up_list:
rst += f"第 {max_star_index_list[i]+1} 抽获取UP {max_star_list[i]}\n"
else:
rst += f"第 {max_star_index_list[i]+1} 抽获取 {max_star_list[i]}\n"
return rst
# 更好的初始化
def init_rst(
max_star_char_dict: dict,
star_num_list: List[int],
star: List[str],
up_list: list = None,
):
# print(max_star_char_dict)
# print(star_num_list)
# print(up_list)
up_list = up_list if up_list else []
rst = ""
for i in range(len(star_num_list)):
if star_num_list[i]:
rst += f"[{star[i]}×{star_num_list[i]}] "
rst += "\n"
_tmp = []
for name in max_star_char_dict.keys():
_tmp += max_star_char_dict[name]
for index in sorted(_tmp):
for name in max_star_char_dict.keys():
if index in max_star_char_dict[name]:
if name in up_list:
rst += f"第 {index} 抽获取UP {name}\n"
else:
rst += f"第 {index} 抽获取 {name}\n"
print(rst)
return rst[:-1] if rst else ""
def max_card(_dict: dict):
_max_value = max(_dict.values())
_max_user = list(_dict.keys())[list(_dict.values()).index(_max_value)]
return f"抽取到最多的是{_max_user},共抽取了{_max_value}次"
# ThreeHighest = nlargest(3, operator_dict, key=operator_dict.get)
# rst = '最喜欢你的前三位是干员是:\n'
# for name in ThreeHighest:
# rst += f'{name} 共投了 {operator_dict[name]} 份简历\n'
# return rst[:-1]
# 获取up和概率
async def init_up_char(announcement):
UP_CHAR = []
UP_ARMS = []
tmp = ""
up_char_dict = await announcement.update_up_char()
for x in list(up_char_dict.keys()):
tmp += up_char_dict[x]["title"] + "[\n]"
tmp = tmp.split("[\n]")
_CURRENT_CHAR_POOL_TITLE = tmp[0]
if len(up_char_dict) > 1:
_CURRENT_ARMS_POOL_TITLE = tmp[1]
else:
_CURRENT_ARMS_POOL_TITLE = ""
POOL_IMG = ""
x = [x for x in list(up_char_dict.keys())]
if _CURRENT_CHAR_POOL_TITLE:
POOL_IMG += MessageSegment.image(up_char_dict[x[0]]["pool_img"])
try:
if _CURRENT_ARMS_POOL_TITLE:
POOL_IMG += MessageSegment.image(up_char_dict[x[1]]["pool_img"])
except (IndexError, KeyError):
pass
logger.info(
f"成功获取{announcement.game_name}当前up信息...当前up池: {_CURRENT_CHAR_POOL_TITLE} & {_CURRENT_ARMS_POOL_TITLE}"
)
for key in up_char_dict.keys():
for star in up_char_dict[key]["up_char"].keys():
up_char_lst = []
for char in up_char_dict[key]["up_char"][star].keys():
up_char_lst.append(char)
if up_char_lst:
if key == "char":
UP_CHAR.append(
UpEvent(star=int(star), operators=up_char_lst, zoom=0)
)
else:
UP_ARMS.append(
UpEvent(star=int(star), operators=up_char_lst, zoom=0)
)
return (
_CURRENT_CHAR_POOL_TITLE,
_CURRENT_ARMS_POOL_TITLE,
POOL_IMG,
UP_CHAR,
UP_ARMS,
)
def is_number(s) -> bool:
try:
float(s)
return True
except ValueError:
pass
try:
import unicodedata
unicodedata.numeric(s)
return True
except (TypeError, ValueError):
pass
return False
def set_list(lst: List[BaseData]) -> list:
tmp = []
name_lst = []
for x in lst:
if x.name not in name_lst:
tmp.append(x)
name_lst.append(x.name)
return tmp
# 获取星级
def get_star(star_lst: List[int], probability_lst: List[float]) -> int:
rand = random.random()
add = 0
tmp_lst = [(0, probability_lst[0])]
for i in range(1, len(probability_lst) - 1):
add += probability_lst[i - 1]
tmp_lst.append((tmp_lst[i - 1][1], probability_lst[i] + add))
tmp_lst.append((tmp_lst[-1][1], 1))
for i in range(len(tmp_lst)):
if tmp_lst[i][0] <= rand <= tmp_lst[i][1]:
return star_lst[i]
# 整理数据
def format_card_information(
count: int, star_list: List[int], func, pool_name: str = "", guaranteed: bool = True
):
max_star_lst = [] # 获取的最高星级角色列表
max_index_lst = [] # 获取最高星级角色的次数
obj_list = [] # 获取所有角色
obj_dict = {} # 获取角色次数字典
_count = -1
if guaranteed:
_count = 0
for i in range(count):
if guaranteed:
_count += 1
if pool_name:
if _count == 10:
obj, code = func(pool_name, 2)
_count = 0
else:
obj, code = func(pool_name)
else:
if _count == 10:
obj, code = func(mode=2)
_count = 0
else:
obj, code = func()
star_list[code] += 1
if code == 0:
max_star_lst.append(obj.name)
max_index_lst.append(i)
if guaranteed:
_count = 0
if code == 1:
if guaranteed:
_count = 0
try:
obj_dict[obj.name] += 1
except KeyError:
obj_dict[obj.name] = 1
obj_list.append(obj)
return obj_list, obj_dict, max_star_lst, star_list, max_index_lst
# 检测次数是否合法
def check_num(num: str, max_num: int) -> Tuple[str, bool]:
if is_number(num):
try:
num = int(num)
except ValueError:
return "必!须!是!数!字!", False
if num > max_num:
return "一井都满不足不了你嘛!快爬开!", False
if num < 1:
return "虚空抽卡???", False
else:
return str(num), True
def cn2py(word) -> str:
temp = ""
for i in pypinyin.pinyin(word, style=pypinyin.NORMAL):
temp += "".join(i)
return temp
# 移除windows和linux下特殊字符
@@ -355,3 +24,32 @@ def remove_prohibited_str(name: str) -> str:
else:
name = name.replace("/", "\\")
return name
def load_font(font_name: str = "msyh.ttf", fontsize: int = 16) -> FreeTypeFont:
return ImageFont.truetype(
str(FONT_PATH / f"{font_name}"), fontsize, encoding="utf-8"
)
def circled_number(num: int) -> IMG:
font = load_font(fontsize=450)
text = str(num)
text_w = font.getsize(text)[0]
w = 240 + text_w
w = w if w >= 500 else 500
img = Image.new("RGBA", (w, 500))
draw = ImageDraw.Draw(img)
draw.ellipse(((0, 0), (500, 500)), fill="red")
draw.ellipse(((w - 500, 0), (w, 500)), fill="red")
draw.rectangle(((250, 0), (w - 250, 500)), fill="red")
draw.text(
(120, -60),
text,
font=font,
fill="white",
stroke_width=10,
stroke_fill="white",
)
return img
+1 -1
View File
@@ -72,6 +72,6 @@ async def _():
if msg_list and code == 200:
await bot.send_group_forward_msg(group_id=g, messages=msg_list)
else:
bot.send_group_msg(group_id=g, messages=msg_list)
await bot.send_group_msg(group_id=g)
except Exception as e:
logger.error(f"GROUP {g} epic免费游戏推送错误 {type(e)}: {e}")
+25 -38
View File
@@ -1,58 +1,34 @@
from httpx import AsyncClient
from datetime import datetime
from nonebot.log import logger
from nonebot.adapters.cqhttp import Bot
from nonebot.adapters.onebot.v11 import Bot
from configs.config import NICKNAME
from utils.http_utils import AsyncHttpx
# 获取所有 Epic Game Store 促销游戏
# 方法参考:RSSHub /epicgames 路由
# https://github.com/DIYgod/RSSHub/blob/master/lib/routes/epicgames/index.js
async def get_epic_game():
# 现在没用 graphql 辣
"""prv_graphql Code
epic_url = "https://www.epicgames.com/store/backend/graphql-proxy"
headers = {
"Referer": "https://www.epicgames.com/store/zh-CN/",
"Content-Type": "application/json; charset=utf-8",
}
data = {
"query": "query searchStoreQuery($allowCountries: String, $category: String, $count: Int, $country: String!, $keywords: String, $locale: String, $namespace: String, $sortBy: String, $sortDir: String, $start: Int, $tag: String, $withPrice: Boolean = false, $withPromotions: Boolean = false) {\n Catalog {\n searchStore(allowCountries: $allowCountries, category: $category, count: $count, country: $country, keywords: $keywords, locale: $locale, namespace: $namespace, sortBy: $sortBy, sortDir: $sortDir, start: $start, tag: $tag) {\n elements {\n title\n id\n namespace\n description\n effectiveDate\n keyImages {\n type\n url\n }\n seller {\n id\n name\n }\n productSlug\n urlSlug\n url\n items {\n id\n namespace\n }\n customAttributes {\n key\n value\n }\n categories {\n path\n }\n price(country: $country) @include(if: $withPrice) {\n totalPrice {\n discountPrice\n originalPrice\n voucherDiscount\n discount\n currencyCode\n currencyInfo {\n decimals\n }\n fmtPrice(locale: $locale) {\n originalPrice\n discountPrice\n intermediatePrice\n }\n }\n lineOffers {\n appliedRules {\n id\n endDate\n discountSetting {\n discountType\n }\n }\n }\n }\n promotions(category: $category) @include(if: $withPromotions) {\n promotionalOffers {\n promotionalOffers {\n startDate\n endDate\n discountSetting {\n discountType\n discountPercentage\n }\n }\n }\n upcomingPromotionalOffers {\n promotionalOffers {\n startDate\n endDate\n discountSetting {\n discountType\n discountPercentage\n }\n }\n }\n }\n }\n paging {\n count\n total\n }\n }\n }\n}\n",
"variables": {
"allowCountries": "CN",
"category": "freegames",
"count": 1000,
"country": "CN",
"locale": "zh-CN",
"sortBy": "effectiveDate",
"sortDir": "asc",
"withPrice": True,
"withPromotions": True,
},
}
"""
epic_url = "https://store-site-backend-static-ipv4.ak.epicgames.com/freeGamesPromotions?locale=zh-CN&country=CN&allowCountries=CN"
headers = {
"Referer": "https://www.epicgames.com/store/zh-CN/",
"Content-Type": "application/json; charset=utf-8",
"User-Agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/98.0.4758.80 Safari/537.36",
}
async with AsyncClient(headers=headers) as client:
try:
res = await client.get(epic_url, timeout=10.0)
res_json = res.json()
games = res_json["data"]["Catalog"]["searchStore"]["elements"]
return games
except Exception as e:
logger.error(str(e))
return None
try:
res = await AsyncHttpx.get(epic_url, headers=headers, timeout=10)
res_json = res.json()
games = res_json["data"]["Catalog"]["searchStore"]["elements"]
return games
except Exception as e:
logger.error(f"Epic 访问接口错误 {type(e)}:{e}")
return None
# 获取 Epic Game Store 免费游戏信息
# 处理免费游戏的信息方法借鉴 pip 包 epicstore_api 示例
# https://github.com/SD4RK/epicstore_api/blob/master/examples/free_games_example.py
async def get_epic_free(bot: Bot, Type_Event: str):
async def get_epic_free(bot: Bot, type_event: str):
games = await get_epic_game()
if not games:
return "Epic 可能又抽风啦,请稍后再试(", 404
@@ -81,7 +57,7 @@ async def get_epic_free(bot: Bot, Type_Event: str):
end_date = datetime.fromisoformat(end_date_iso).strftime(
"%b.%d %H:%M"
)
if Type_Event == "Group":
if type_event == "Group":
_message = "\n由 {} 公司发行的游戏 {} ({}) 在 UTC 时间 {} 即将推出免费游玩,预计截至 {}。".format(
game_corp, game_name, game_price, start_date, end_date
)
@@ -101,8 +77,19 @@ async def get_epic_free(bot: Bot, Type_Event: str):
msg_list.append(msg)
else:
for image in game["keyImages"]:
if image["type"] == "Thumbnail":
if (
image.get("url")
and not game_thumbnail
and image["type"]
in [
"Thumbnail",
"VaultOpened",
"DieselStoreFrontWide",
"OfferImageWide",
]
):
game_thumbnail = image["url"]
break
for pair in game["customAttributes"]:
if pair["key"] == "developerName":
game_dev = pair["value"]
@@ -127,7 +114,7 @@ async def get_epic_free(bot: Bot, Type_Event: str):
game_url = "https://www.epicgames.com/store/zh-CN/p/{}".format(
game_url_part
)
if Type_Event == "Group":
if type_event == "Group":
_message = "[CQ:image,file={}]\n\nFREE now :: {} ({})\n{}\n此游戏由 {} 开发、{} 发行,将在 UTC 时间 {} 结束免费游玩,戳链接速度加入你的游戏库吧~\n{}\n".format(
game_thumbnail,
game_name,
+17 -22
View File
@@ -4,14 +4,12 @@ from utils.image_utils import get_img_hash
import random
from utils.message_builder import image
from nonebot import on_message
from utils.utils import get_message_img
from nonebot.params import CommandArg, Command
from nonebot.adapters.onebot.v11 import GroupMessageEvent, 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 utils.manager import group_manager
from services.log import logger
from typing import Tuple
from configs.config import NICKNAME
__zx_plugin_name__ = "复读"
@@ -77,22 +75,18 @@ class Fudu:
_fudu_list = Fudu()
fudu = on_message(permission=GROUP, priority=9)
fudu = on_message(permission=GROUP, priority=999)
@fudu.handle()
async def _(event: GroupMessageEvent, cmd: Tuple[str, ...] = Command(), arg: Message = CommandArg()):
if (
event.is_tome()
or cmd
or not await group_manager.check_group_task_status(event.group_id, "fudu")
):
async def _(event: GroupMessageEvent):
if event.is_tome():
return
if arg.extract_plain_text().strip():
if arg.extract_plain_text().strip().find("@可爱的小真寻") != -1:
if msg := get_message_text(event.json()):
if msg.startswith(f"@可爱的{NICKNAME}"):
await fudu.finish("复制粘贴的虚空艾特?", at_sender=True)
img = get_message_img(event.json())
msg = arg.extract_plain_text().strip()
msg = get_message_text(event.json())
if not img and not msg:
return
if img:
@@ -112,20 +106,21 @@ async def _(event: GroupMessageEvent, cmd: Tuple[str, ...] = Command(), arg: Mes
"fudu", "FUDU_PROBABILITY"
) and not _fudu_list.is_repeater(event.group_id):
if random.random() < 0.2:
await fudu.finish("打断施法!")
if msg.endswith("打断施法!"):
await fudu.finish("[[_task|fudu]]打断" + msg)
else:
await fudu.finish("[[_task|fudu]]打断施法!")
_fudu_list.set_repeater(event.group_id)
if img and msg:
rst = msg + image(f"compare_{event.group_id}_img.jpg", "temp")
rst = msg + image(TEMP_PATH / f"compare_{event.group_id}_img.jpg")
elif img:
rst = image(f"compare_{event.group_id}_img.jpg", "temp")
rst = image(TEMP_PATH / f"compare_{event.group_id}_img.jpg")
elif msg:
rst = msg
else:
rst = ""
if rst:
if rst.endswith("打断施法!"):
rst = "打断" + rst
await fudu.send(rst)
await fudu.finish("[[_task|fudu]]" + rst)
async def get_fudu_img_hash(url, group_id):
@@ -139,4 +134,4 @@ async def get_fudu_img_hash(url, group_id):
logger.warning(f"复读下载图片失败...")
except Exception as e:
logger.warning(f"复读读取图片Hash出错 {type(e)}:{e}")
return ""
return ""
+1 -1
View File
@@ -35,7 +35,7 @@ Config.add_plugin_config(
default_value=True,
)
almanac = on_command("原神黄历", priority=5, block=True)
almanac = on_command("原神黄历", priority=15, block=True)
ALC_PATH = IMAGE_PATH / "genshin" / "alc"
@@ -1,6 +1,6 @@
from pathlib import Path
from typing import Tuple, Optional, List
from configs.path_config import IMAGE_PATH, TEXT_PATH
from PIL.Image import UnidentifiedImageError
from utils.message_builder import image
from services.log import logger
from utils.image_utils import BuildImage
@@ -48,7 +48,7 @@ async def query_resource(resource_name: str) -> str:
planning_route = True
if not resource_name or resource_name not in resource_name_list:
# return f"未查找到 {resource_name} 资源,可通过 “原神资源列表” 获取全部资源名称.."
return ''
return ""
map_ = Map(
resource_name, CENTER_POINT, planning_route=planning_route, ratio=MAP_RATIO
)
@@ -84,7 +84,7 @@ def check_resource_exists(resource: str) -> bool:
检查资源是否存在
:param resource: 资源名称
"""
resource = resource.replace('路径', '').replace('路线', '')
resource = resource.replace("路径", "").replace("路线", "")
return resource in resource_name_list
@@ -97,16 +97,18 @@ async def init(flag: bool = False):
await download_resource_data(semaphore)
await download_resource_type()
if not CENTER_POINT:
CENTER_POINT = json.load(open(resource_label_file, "r", encoding="utf8"))[
"CENTER_POINT"
]
with open(resource_type_file, "r", encoding="utf8") as f:
data = json.load(f)
for id_ in data:
for x in data[id_]["children"]:
resource_name_list.append(x["name"])
if resource_label_file.exists():
CENTER_POINT = json.load(
open(resource_label_file, "r", encoding="utf8")
)["CENTER_POINT"]
if resource_label_file.exists():
with open(resource_type_file, "r", encoding="utf8") as f:
data = json.load(f)
for id_ in data:
for x in data[id_]["children"]:
resource_name_list.append(x["name"])
except TimeoutError:
logger.warning('原神资源查询信息初始化超时....')
logger.warning("原神资源查询信息初始化超时....")
# 图标及位置资源
@@ -114,7 +116,7 @@ async def download_resource_data(semaphore: Semaphore):
icon_path.mkdir(parents=True, exist_ok=True)
resource_label_file.parent.mkdir(parents=True, exist_ok=True)
try:
response = await AsyncHttpx.get(POINT_LIST_URL, timeout=5)
response = await AsyncHttpx.get(POINT_LIST_URL, timeout=10)
if response.status_code == 200:
data = response.json()
if data["message"] == "OK":
@@ -135,7 +137,7 @@ async def download_resource_data(semaphore: Semaphore):
asyncio.ensure_future(
download_image(
img_url,
f"{icon_path}/{id_}.png",
icon_path / f"{id_}.png",
semaphore,
True,
)
@@ -157,16 +159,14 @@ async def download_resource_data(semaphore: Semaphore):
# 下载原神地图并拼图
async def download_map_init(
semaphore: Semaphore, flag: bool = False
):
async def download_map_init(semaphore: Semaphore, flag: bool = False):
global CENTER_POINT, MAP_RATIO
map_path.mkdir(exist_ok=True, parents=True)
_map = map_path / "map.png"
if _map.exists() and os.path.getsize(_map) > 1024 * 1024 * 30:
_map.unlink()
try:
response = await AsyncHttpx.get(MAP_URL, timeout=5)
response = await AsyncHttpx.get(MAP_URL, timeout=10)
if response.status_code == 200:
data = response.json()
if data["message"] == "OK":
@@ -176,20 +176,24 @@ async def download_map_init(
data = data["slices"]
idx = 0
for _map_data in data[0]:
map_url = _map_data['url']
map_url = _map_data["url"]
await download_image(
map_url,
f"{map_path}/{idx}.png",
map_path / f"{idx}.png",
semaphore,
force_flag=flag,
)
BuildImage(0, 0, background=f"{map_path}/{idx}.png", ratio=MAP_RATIO).save()
BuildImage(
0, 0, background=f"{map_path}/{idx}.png", ratio=MAP_RATIO
).save()
idx += 1
_w, h = BuildImage(0, 0, background=f"{map_path}/0.png").size
w = _w * len(os.listdir(map_path))
map_file = BuildImage(w, h, _w, h, ratio=MAP_RATIO)
for i in range(idx):
map_file.paste(BuildImage(0, 0, background=f"{map_path}/{i}.png"))
map_file.paste(
BuildImage(0, 0, background=f"{map_path}/{i}.png")
)
map_file.save(f"{map_path}/map.png")
else:
logger.warning(f'获取原神地图失败 msg: {data["message"]}')
@@ -205,7 +209,7 @@ async def download_map_init(
async def download_resource_type():
resource_type_file.parent.mkdir(parents=True, exist_ok=True)
try:
response = await AsyncHttpx.get(LABEL_URL, timeout=5)
response = await AsyncHttpx.get(LABEL_URL, timeout=10)
if response.status_code == 200:
data = response.json()
if data["message"] == "OK":
@@ -224,15 +228,14 @@ async def download_resource_type():
except (TimeoutError, ConnectTimeout):
logger.warning("下载原神资源类型数据超时....")
except Exception as e:
logger.error(f"载原神资源类型数据超时 {type(e)}:{e}")
logger.error(f"载原神资源类型数据错误 {type(e)}:{e}")
# 初始化资源图标
def gen_icon(icon: str):
def gen_icon(icon: Path):
A = BuildImage(0, 0, background=f"{icon_path}/box.png")
B = BuildImage(0, 0, background=f"{icon_path}/box_alpha.png")
icon_ = icon_path / f"{icon}"
icon_img = BuildImage(115, 115, background=icon_)
icon_img = BuildImage(115, 115, background=icon)
icon_img.circle()
B.paste(icon_img, (17, 10), True)
B.paste(A, alpha=True)
@@ -243,22 +246,22 @@ def gen_icon(icon: str):
# 下载图片
async def download_image(
img_url: str,
path: str,
path: Path,
semaphore: Semaphore,
gen_flag: bool = False,
force_flag: bool = False,
):
async with semaphore:
try:
if not os.path.exists(path) or not is_valid or force_flag:
if await AsyncHttpx.download_file(img_url, path, timeout=5):
if not path.exists() or not is_valid(path) or force_flag:
if await AsyncHttpx.download_file(img_url, path, timeout=10):
logger.info(f"下载原神资源图标:{img_url}")
if gen_flag:
gen_icon(path)
else:
logger.info(f"下载原神资源图标:{img_url} 失败,等待下次更新...")
except UnidentifiedImageError:
logger.warning(f"原神图片打开错误..已删除,等待下次更新... file: {path}")
except Exception as e:
logger.warning(f"原神图片错误..已删除,等待下次更新... file: {path} {type(e)}:{e}")
if os.path.exists(path):
os.remove(path)
@@ -254,10 +254,9 @@ class Genshin(db.Model):
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 [
x for x in await cls.query.order_by(db.func.random()).gino.all() if 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
@@ -356,7 +355,7 @@ class Genshin(db.Model):
"""
query = cls.query.where(cls.uid == cookie_uid).with_for_update()
user = await query.gino.first()
await user.update(today_query_uid=cls.today_query_uid + f"{uid} ").apply()
await user.update(today_query_uid=user.today_query_uid + f"{uid} ").apply()
@classmethod
async def _get_user_data(
@@ -23,17 +23,17 @@ async def _():
g_list = await Genshin.get_all_auto_sign_user()
for u in g_list:
if u.auto_sign_time:
date = await Genshin.random_sign_time(u.uid)
scheduler.add_job(
_sign,
"date",
run_date=date.replace(microsecond=0),
id=f"genshin_auto_sign_{u.uid}_{u.user_qq}_0",
args=[u.user_qq, u.uid, 0],
)
logger.info(
f"genshin_sign add_job:USER:{u.user_qq} UID:{u.uid} " f"{date} 原神自动签到"
)
if date := await Genshin.random_sign_time(u.uid):
scheduler.add_job(
_sign,
"date",
run_date=date.replace(microsecond=0),
id=f"genshin_auto_sign_{u.uid}_{u.user_qq}_0",
args=[u.user_qq, u.uid, 0],
)
logger.info(
f"genshin_sign add_job:USER:{u.user_qq} UID:{u.uid} " f"{date} 原神自动签到"
)
def add_job(user_id: int, uid: int, date: datetime):

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