140 Commits
main ... ntchat

Author SHA1 Message Date
42e8693463 🍱 更新xx用什么的数据集 2023-04-15 01:43:55 +08:00
92e032077c 🍱 更新收集的宝箱数据 2023-04-14 11:59:02 +08:00
00b1ebd71e 🍱 更新新圣遗物数据 2023-04-13 14:22:08 +08:00
de71e83be8 添加查询琦良良换xx圣遗物的支持 2023-04-13 01:09:38 +08:00
a398fcc0f3 🍱 更新原神3.6版本的metadata 2023-04-12 01:02:06 +08:00
11840e0f75 🍱 更新血量表等杂图资源 2023-04-09 00:56:25 +08:00
77a597cfca 🍱 更新原石预估素材 2023-04-02 00:28:44 +08:00
b128fd38b8 🍱 新增新角色的参考面板,优化体验 2023-04-01 00:49:53 +08:00
447b24dde9 🍱 更新参考面板版本深渊内容 2023-03-25 13:26:17 +08:00
72c36be0a9 🐛 修复材料 (#470) 2023-03-21 23:25:17 +08:00
2c07c490bb 🍱 更新原石预估 2023-03-14 00:23:47 +08:00
914ff0e83b 添加gsrc的图片版 (#467)
*  添加gsrc的图片版

* 🚨 auto fix by pre-commit-ci

*  添加`gsrc`的图片版

* 🚨 auto fix by pre-commit-ci

---------

Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
2023-03-12 22:54:02 +08:00
a9eacfbe0c 🐛 修改bot_id为正确数值 2023-03-10 09:46:38 +08:00
91f0ee82b9 新增导出v3数据 2023-03-09 23:23:46 +08:00
8727fb0bc1 新增gsrc (#463) 2023-03-08 22:41:14 +08:00
6cb7bd7ab8 🐛 修复查询白术的数据异常 2023-03-02 22:24:34 +08:00
efa924c1a8 🍱 补充部分原神3.6的数据资料 2023-03-02 21:49:58 +08:00
01b9d48b38 ️ 支持查询白术查询卡维,修复几个BUG,优化体验 2023-03-02 21:18:40 +08:00
b84764d907 🎨 回退查询深渊的更改 (#462) 2023-03-02 09:49:40 +08:00
236fbebd3b 🎨 删除通用获取成功log (#461) 2023-03-02 02:34:07 +08:00
a9600d85b7 🐛 修补MetaData, 调整BUG(#461) 2023-03-02 00:04:21 +08:00
b087dd7f24 🍱 更新原神3.5版本MetaData 2023-03-01 01:22:22 +08:00
083f55d852 🎨 更新切换api的API地址 2023-02-15 21:07:31 +08:00
5aec15e9f4 🐛 修复下载全部资源的问题 2023-02-14 23:26:28 +08:00
d952e1bf78 下载全部资源将会检查版本差异 2023-02-14 22:30:06 +08:00
2e4120bf89 🎨 优化抽卡记录导入 (#451)
* 🎨 优化抽卡记录导入

* 🚨 auto fix by pre-commit-ci

---------

Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
2023-02-14 14:09:13 +08:00
a325bfe4f2 🐛 修复部分情况下,扫码登陆无法获取正确UID 2023-02-11 16:17:46 +08:00
f9e84e5fdf 🎨 优化扫码登录流程 (#444)
* 🎨 优化扫码登录流程

* 🚨 auto fix by pre-commit-ci

* 🎨 优化扫码登录流程

* 🎨 优化扫码登录流程

* 🎨 优化扫码登录流程

* 🚨 auto fix by pre-commit-ci

---------

Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
2023-02-11 00:30:12 +08:00
c8588a2848 新增强制刷新抽卡记录 2023-02-09 02:03:31 +08:00
79d1e86200 📝 合并雷神和薙刀效果 2023-02-08 00:31:00 +08:00
40217dba09 🚑️ 修复删除配置项错误的问题 2023-02-07 00:09:35 +08:00
2cb0ccd452 🎨 移除失效配置、新增随机图配置 2023-02-06 23:47:35 +08:00
52b1c06125 🐛 修复查询雷神的数值错误 2023-02-06 23:40:31 +08:00
1f54160e2b 📝 更新查询收集的最大值 2023-02-05 22:05:36 +08:00
15fc075878 👽️ 更换随机图接口地址 (#441) 2023-02-05 22:02:46 +08:00
2a03b056da 🐛 修复群聊内gs开启&签到群推送 (#440)
* 🐛 修复群聊内设置&签到推送

* 🐛 修复群聊内设置&签到推送
2023-02-05 20:23:33 +08:00
6bf9cef4f4 📝 更新攻略、gs帮助、修复伤害参考bug 2023-02-04 19:12:47 +08:00
c3562ae49e 🎨 适配adapter-ntchat-0.3.5 (#438) 2023-02-04 16:54:26 +08:00
de312d2314 🎨 微调强制刷新界面` 2023-02-03 00:30:18 +08:00
db96d23876 🎨 修改强制刷新界面 2023-02-02 00:29:38 +08:00
ecb286b33f 👽 更新ENKA_API 2023-01-29 22:52:36 +08:00
a93787bfea 🐛 修复了一些已知问题 (#436)
* 🚨 修复了一些已知问题

* 🚨 auto fix by pre-commit-ci

* 🐛 修复UID的查找方式

* 🚨 auto fix by pre-commit-ci

* Update Character.py

* Update Fight.py

* Update get_xk_data.py

---------

Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
2023-01-29 21:58:15 +08:00
f93eb116f1 🐛 修复无法获取被@人问题 (#434)
* 🐛 修复无法获取被@人问题

* 🚨 auto fix by pre-commit-ci

---------

Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
2023-01-28 11:47:29 +08:00
3265e2cf0d 🍱 更新3.4血量表抗性表 2023-01-27 23:44:57 +08:00
2c4c3589fc 🐛 修复注册时间 2023-01-26 20:28:33 +08:00
77ea5118a2 🚨 修复一部分flake8警告 2023-01-26 19:31:30 +08:00
a7e0477f72 新增原神注册时间命令 (#431)
* 🎨 图片调整

* 🐛修复查询命令报错

适配器问题,“原图”功能无法使用

* Update requirements.txt

* 🚨 auto fix by pre-commit-ci

* 🐛修复无法导入抽卡记录的问题

* 🐛修复无法导入抽卡记录的问题

* 🚨 auto fix by pre-commit-ci

* 🐛修复“导入抽卡记录”

* 🚨 auto fix by pre-commit-ci

* 🐛修复抽卡记录导入

* 🐛修复抽卡记录导入

* 🚨 auto fix by pre-commit-ci

* 🐛 修复公告图片推送

* 🐛 修复定时公告推送的图片发送问题

Co-authored-by: MingxuanGame <MingxuanGame@outlook.com>

* 🐛 修复定时公告推送的图片发送问题

Co-authored-by: MingxuanGame <MingxuanGame@outlook.com>

* 🎨 修改convert_img错误

* 🐛 修复公告异常报错问题

* 新增原神注册时间命令

* 🐛修复获取公告时报错

*  新增原神注册时间命令

* 🚨 auto fix by pre-commit-ci

*  新增原神注册时间命令

注释Debug~

* 🚨 auto fix by pre-commit-ci

* fix

* 🚨 auto fix by pre-commit-ci

* 🎨 修改一部分代码

Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
Co-authored-by: MingxuanGame <MingxuanGame@outlook.com>
Co-authored-by: KimigaiiWuyi <444835641@qq.com>
2023-01-26 19:00:50 +08:00
fe59404ee9 🐛 修复查询心海的伤害计算和毕业度错误 2023-01-25 20:58:37 +08:00
402184496f 🎨扫码登陆增加风险提示 (#430) 2023-01-25 16:41:33 +08:00
6a3504026b 新增chbg文件夹以供查询角色的自定义图 2023-01-24 00:03:22 +08:00
59184adea7 新增查成就查委托 2023-01-23 15:56:28 +08:00
83fd57ec54 🍱 更新一部分成就资源 2023-01-23 15:21:41 +08:00
e6979b75be 🍱 更新艾尔海森瑶瑶的参考面板 2023-01-22 23:04:00 +08:00
7878f0e278 🍱 增加查询米卡的治疗量计算,修复旅行者BUG 2023-01-20 19:11:07 +08:00
e318984801 🐛 修复新角色基础数值错误的问题 2023-01-20 00:09:37 +08:00
d62ab6e341 🍱 更新3.5版本角色的别名和有效词条 2023-01-19 23:37:20 +08:00
d5a379d0ef 🍱 支持部分3.5版本的角色和武器的替换 2023-01-19 23:22:34 +08:00
02375b1284 🐛 修复签到 (#428) 2023-01-18 21:55:13 +08:00
2aaf5aa1c6 🍱 更新原神3.4版本MetaData 2023-01-18 02:00:31 +08:00
54701a448b 🐛 修复原神每日签到奖励物品名称获取错误的问题 (#428) 2023-01-17 22:25:39 +08:00
d74959b751 📝 修改ck帮助的说明 (#421) 2023-01-17 00:22:09 +08:00
d087e75e4e 🎨 补充_pass内容 2023-01-17 00:15:47 +08:00
79a4faf5c4 新增刷新全部CK(需SU用户) 2023-01-16 23:34:47 +08:00
38f08262d4 🐛 修复xx在哪的一部分使用体验 (#427) 2023-01-15 17:46:30 +08:00
13c5fe2eb0 新增刷新CK(需要用户绑定SK) 2023-01-15 00:53:54 +08:00
92844b1897 🎨 增加几个安柏计划API 2023-01-14 23:58:37 +08:00
2984f4e578 🐛 修复定时公告推送及深渊图片发送问题 (#426)
* 🎨 图片调整

* 🐛修复查询命令报错

适配器问题,“原图”功能无法使用

* Update requirements.txt

* 🚨 auto fix by pre-commit-ci

* 🐛修复无法导入抽卡记录的问题

* 🐛修复无法导入抽卡记录的问题

* 🚨 auto fix by pre-commit-ci

* 🐛修复“导入抽卡记录”

* 🚨 auto fix by pre-commit-ci

* 🐛修复抽卡记录导入

* 🐛修复抽卡记录导入

* 🚨 auto fix by pre-commit-ci

* 🐛 修复公告图片推送

* 🐛 修复定时公告推送的图片发送问题

Co-authored-by: MingxuanGame <MingxuanGame@outlook.com>

* 🐛 修复定时公告推送的图片发送问题

Co-authored-by: MingxuanGame <MingxuanGame@outlook.com>

* 🎨 修改convert_img错误

Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
Co-authored-by: MingxuanGame <MingxuanGame@outlook.com>
2023-01-14 18:55:43 +08:00
8407d556ee 🔥 remove workflows 2023-01-11 15:28:10 +08:00
d68cbc24fa 🐛修复抽卡记录导入 (#425)
* 🎨 图片调整

* 🐛修复查询命令报错

适配器问题,“原图”功能无法使用

* Update requirements.txt

* 🚨 auto fix by pre-commit-ci

* 🐛修复无法导入抽卡记录的问题

* 🐛修复无法导入抽卡记录的问题

* 🚨 auto fix by pre-commit-ci

* 🐛修复“导入抽卡记录”

* 🚨 auto fix by pre-commit-ci

* 🐛修复抽卡记录导入

* 🐛修复抽卡记录导入

* 🚨 auto fix by pre-commit-ci

Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
2023-01-11 15:27:26 +08:00
8032841408 🐛 修正暴击率低于0%时期望伤害低于普通值的问题 (#422)
* 修正暴击率低于0%时期望伤害低于普通值的问题

* 🚨 auto fix by pre-commit-ci

Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
2023-01-10 22:54:49 +08:00
9eaf184f19 🍱 更新原始预估3.4 2023-01-10 22:51:56 +08:00
a215bc16cb 🎨 修改convert_img方法 2023-01-08 00:59:14 +08:00
ed412e890c 🐛 ntchat问题修复 (#419)
* 🎨 图片调整

* 🐛修复查询命令报错

适配器问题,“原图”功能无法使用

* Update requirements.txt

* 🚨 auto fix by pre-commit-ci

Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
2023-01-08 00:53:54 +08:00
99ea01d9e4 🐛 ntchat问题修复 (#418)
* 🐛 修复资源点地图发送

* 🎨 将默认头像改为项目图标

* 🚨 auto fix by pre-commit-ci

Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
2023-01-07 23:33:13 +08:00
ad385bd77c 🐛 修复错误的sp_base 2023-01-06 23:16:44 +08:00
18fd5e2cd0 🐛 修改部分call_apintchat 2023-01-06 21:32:13 +08:00
cd2303d37d 🐛 修复定时签到的私聊推送 2023-01-06 00:11:55 +08:00
6ba82d4d1b 🍱 同步查询艾尔海森的倍率,新增查询夜兰的蒸发伤害 2023-01-05 23:50:38 +08:00
96227fe4be 🐛 修复特殊情况下几种默认反应倍率 2023-01-04 00:00:09 +08:00
fa35be809f 🐛 修复原神公告的部分详情出现https错误 2023-01-02 23:50:49 +08:00
9f7db4e5b2 🐛 修复清除原神公告红点 2023-01-01 01:33:19 +08:00
5c6008381b 📝 更新gs帮助 2022-12-30 00:30:23 +08:00
461911e186 🐛 修复错误的适配器导致的原神公告异常 2022-12-29 23:40:32 +08:00
ef1d7eb997 🐛 修复查询绫人等几个角色的伤害异常(#413) 2022-12-29 23:38:16 +08:00
8f2d3ae8e5 🐛 修复SQLModel导包问题 2022-12-28 23:56:26 +08:00
21c40bcfe5 🚨 fix lint warnings 2022-12-28 23:30:19 +08:00
fbb1cec548 新增原神公告 (#409) 2022-12-28 22:15:36 +08:00
0371014fd6 🚑 fix bbs coin error 2022-12-28 14:22:26 +08:00
d367197838 📌 更新依赖qrcode 2022-12-28 00:32:47 +08:00
4cfd3537f0 新增扫码登陆 2022-12-28 00:13:27 +08:00
a221356da4 新增版本深渊,后可跟版本号 2022-12-22 00:27:40 +08:00
5bc15948fe 增加hhw_api的相关方法 2022-12-21 23:01:22 +08:00
a8040c7321 🎨 调整添加CK时候的判定顺序,修复几个BUG 2022-12-20 00:43:33 +08:00
5cb62671fc 新增数据文件夹内bg文件夹,用于存放自定义图片 2022-12-19 00:05:02 +08:00
9b959b0f2c 新增查询探索, 重绘的查询收集 2022-12-18 20:02:26 +08:00
bb2b19ec22 🎨 添加draw_bar绘图方法,重绘收集样式 2022-12-18 19:49:08 +08:00
c5e1d9e88a 🐛 修复已知BUG、修改参考数值和角色BUFF错误 2022-12-17 19:18:39 +08:00
82842b25fc 🐛 修复已知BUG,优化报错提示(#405) 2022-12-17 00:09:26 +08:00
56700bf420 🎨 为多条推送信息添加换行符 (#403) 2022-12-14 12:12:48 +08:00
c3e8aaa6fc 🐛 修正国际服七圣召唤api (#402) 2022-12-14 12:00:57 +08:00
8c53148488 🐛 下载全部资源时判断是否为空文件名 2022-12-14 09:45:22 +08:00
96b40242e8 🐛 查询时额外输出问题 2022-12-14 00:08:17 +08:00
7df0a67f8d 攻略换用本地攻略源 2022-12-13 23:26:54 +08:00
bf17cc3caf 🎨 更新深渊概览API,添加自动下载攻略 2022-12-13 23:18:35 +08:00
a1002d91ec 🍱 补充3.3新角色的参考面板毕业度 2022-12-12 22:56:05 +08:00
9ae5605af0 📝 限制绑定uid位数, 补充gs帮助 2022-12-11 17:17:15 +08:00
f79dbd03b4 🎨 一些响应方式的修改 2022-12-11 15:28:49 +08:00
8c08e1c2d6 🎨 修改资源文件名 (#400)
* 修改资源文件名

统一命名,去除卡牌资源文件结尾的符号

* 🚨 auto fix by pre-commit-ci

Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
2022-12-11 14:08:09 +08:00
6e76e909d3 新增七圣召唤命令,后可跟UID 2022-12-11 13:56:58 +08:00
a39e3c7365 🎨 添加七圣召唤的API,修复一部分BUG 2022-12-11 13:50:28 +08:00
46444af78c 🍱 补充一些遗漏资源,修复几个BUG 2022-12-09 23:15:41 +08:00
166d530f35 🍱 补充一部分3.4的资源文件 2022-12-09 20:25:26 +08:00
571b9bd97a 新增原牌功能, 例如原牌狼末 2022-12-08 23:33:06 +08:00
18e5c968ad 🍱 增加一些资源文件 2022-12-08 23:21:53 +08:00
142995766e 📝 提高原神版本至3.3 2022-12-07 01:08:58 +08:00
9db963ea18 🍱 新增原神3.3版本的元数据 2022-12-07 01:06:51 +08:00
996a4e8c29 🐛 修复低星圣遗物的错误 2022-12-05 23:40:20 +08:00
4a7f822b94 🐛 修复抽卡记录平均UP错误和提纳里的毕业度 2022-12-04 17:05:56 +08:00
f04e8ccc48 🎨 优化一部分API访问 2022-12-04 13:55:40 +08:00
98907c5671 🐛 修复截获CK字段的错误 2022-12-03 00:04:48 +08:00
f6e4f8c129 🍱 补充版本规划3.3 2022-12-02 22:50:06 +08:00
f20c0b7e1f 🐛 修复深渊概览刷新失败 2022-12-02 22:47:05 +08:00
9f9106d788 🐛 修复未绑定UID时签到指令异常返回 2022-11-30 23:53:37 +08:00
cdab68347c 🐛 修复查询妮露的不正确毕业度(#397) 2022-11-29 22:02:25 +08:00
87885db01e 🎨 强制刷新支持使用@别人查询(#396) 2022-11-29 21:13:03 +08:00
90ce5b77fe 🎨 优化毕业度的计算值 2022-11-29 21:09:57 +08:00
dc4ba1811f 🎨 重做的毕业度计算 2022-11-29 01:48:36 +08:00
32f391e629 🚑️ 补充错误获取的CK 2022-11-26 23:32:33 +08:00
46a176283d 新增深渊统计(深渊概览) 2022-11-26 15:18:13 +08:00
7b15e44f9d 🐛 修复缓存获取CK的BUG 2022-11-26 15:11:17 +08:00
1204b5a5e7 🐛 修复仅修改命座的报错 2022-11-24 11:58:23 +08:00
b0b09cdc70 🐛 修复查询六命散兵换xx无法生效的问题 2022-11-24 00:34:22 +08:00
eb17cebc28 支持查询公子换香菱沙类似用法,并可以任意组合 2022-11-24 00:23:40 +08:00
d9eeca0ba8 💥 修改PROXY配置至config.json 2022-11-23 21:56:40 +08:00
7c30452e15 🐛 兼容国际服的ID至UID的转换, 尝试从绑定UID读取 2022-11-12 17:01:08 +08:00
9c7a51e749 👽️ 判断v2版本的米游社CK(#383) 2022-11-12 16:19:46 +08:00
bc9af2b8d6 🎨 改进一部分错误提示 2022-11-12 00:29:51 +08:00
2a92081968 👽️ 增加mr出现验证码的错误提示(#379) 2022-11-11 23:44:02 +08:00
1f4322b769 🎨 修改部分角色BUFF,修改部分UI表现 2022-11-11 22:50:46 +08:00
bbb301d72a 🎉 adapter-ntchat现在可用 2022-11-10 22:01:17 +08:00
84 changed files with 5319 additions and 2604 deletions

16
.dockerignore Normal file
View File

@ -0,0 +1,16 @@
# .git
.vscode
.env
.venv
env
venv
.pytest_cache
*.pyc
*.pyo
*.pyd
.Python
tests
GenshinUID.egg-info
build
__pycache__
.pytest_cache

7
.gitignore vendored
View File

@ -210,7 +210,7 @@ celerybeat.pid
*.sage.py
# Environments
.env
.env/
.venv
env/
venv/
@ -668,5 +668,6 @@ result.txt
### GenshinUID ###
GenshinUID/genshinuid_help/help.png
GenshinUID/genshinuid_map/map_data
.vscode/settings.json
.vscode/settings.json
### Debug ###
testnb2/

View File

@ -5,11 +5,6 @@ ci:
autoupdate_schedule: monthly
autoupdate_commit_msg: ":arrow_up: auto update by pre-commit-ci"
repos:
- repo: https://github.com/hadialqattan/pycln
rev: v2.1.2
hooks:
- id: pycln
- repo: https://github.com/pycqa/isort
rev: 5.11.5
hooks:
@ -25,17 +20,16 @@ repos:
hooks:
- id: flake8
- repo: https://github.com/hadialqattan/pycln
rev: v2.1.2
hooks:
- id: pycln
- repo: https://github.com/python-poetry/poetry
rev: 1.3.1
hooks:
- id: poetry-check
- id: poetry-lock
- id: poetry-export
args:
[
"-f",
"requirements.txt",
"--without-hashes",
"-o",
"requirements.txt",
]
args: ["-f", "requirements.txt", "--without-hashes", "-o", "requirements.txt"]
verbose: true

View File

@ -1,6 +1,6 @@
{
"python.languageServer": "Pylance",
"python.analysis.typeCheckingMode": "off",
"python.analysis.typeCheckingMode": "basic",
"cSpell.words": [
"audioid",
"enka",

43
Dockerfile Normal file
View File

@ -0,0 +1,43 @@
FROM python:3.9
# 1. 环境设置
# 使用镜像源
RUN pip config set global.index-url https://mirrors.aliyun.com/pypi/simple
RUN apt-get update && apt-get install -y tzdata
ENV TZ Asia/Shanghai
# 2. 安装 GenshinUID
WORKDIR /plugin
COPY ./pyproject.toml ./LICENSE ./README.md /plugin/
COPY ./GenshinUID /plugin/GenshinUID/
RUN pip install --editable "."
COPY ./.git /plugin/.git
# 3. 生成 nb2 项目
WORKDIR /nb2
RUN pip install tomlkit "cookiecutter>=2.1.1"
COPY ./deploy/cookiecutter.yml ./deploy/update_pyproject.py /nb2/
# RUN cookiecutter --config-file ./cookiecutter.yml https://github.com/nonebot/nb-cli.git --directory="nb_cli/project" --no-input
# 使用镜像源
RUN cookiecutter --config-file ./cookiecutter.yml https://ghproxy.com/https://github.com/nonebot/nb-cli.git --directory="nb_cli/project" --no-input
RUN python update_pyproject.py
COPY ./deploy/.env.dev /nb2/nb2/
WORKDIR /nb2/nb2
EXPOSE 8080
CMD python bot.py

View File

@ -0,0 +1,39 @@
from nonebot.log import logger
from nonebot import require, load_all_plugins, get_plugin_by_module_name
require('nonebot_plugin_apscheduler')
if get_plugin_by_module_name("GenshinUID"):
logger.info("推荐直接加载 GenshinUID 仓库文件夹")
load_all_plugins(
[
'GenshinUID.genshinuid_abyss',
'GenshinUID.genshinuid_adv',
'GenshinUID.genshinuid_ann',
'GenshinUID.genshinuid_gcg',
'GenshinUID.genshinuid_xkdata',
'GenshinUID.genshinuid_check',
'GenshinUID.genshinuid_collection',
'GenshinUID.genshinuid_config',
'GenshinUID.genshinuid_enka',
'GenshinUID.genshinuid_etcimg',
'GenshinUID.genshinuid_eventlist',
'GenshinUID.genshinuid_gachalog',
'GenshinUID.genshinuid_guide',
'GenshinUID.genshinuid_help',
'GenshinUID.genshinuid_map',
'GenshinUID.genshinuid_meta',
'GenshinUID.genshinuid_mhybbscoin',
'GenshinUID.genshinuid_mys',
'GenshinUID.genshinuid_note',
'GenshinUID.genshinuid_resin',
'GenshinUID.genshinuid_resource',
'GenshinUID.genshinuid_roleinfo',
'GenshinUID.genshinuid_signin',
'GenshinUID.genshinuid_user',
'GenshinUID.genshinuid_wikitext',
'GenshinUID.genshinuid_webconsole',
'GenshinUID.genshinuid_update',
],
[],
)

View File

@ -1,6 +0,0 @@
import hoshino
from hoshino import Service
sv = Service('genshinuid', bundle='genshin', help_='发送"原神帮助"查看详情')
hoshino_bot = hoshino.get_bot()
logger = sv.logger

5
GenshinUID/config.py Normal file
View File

@ -0,0 +1,5 @@
from nonebot import get_driver
config = get_driver().config
SUPERUSERS = {x for x in config.superusers}
priority = 2

View File

@ -1,59 +1,85 @@
import re
from typing import Any, Tuple
from hoshino.typing import CQEvent, HoshinoBot
from nonebot import on_regex
from nonebot.log import logger
from nonebot.matcher import Matcher
from nonebot.params import RegexGroup
from nonebot.adapters.ntchat import MessageSegment, TextMessageEvent
from ..base import sv, logger
from ..genshinuid_meta import register_menu
from .draw_abyss_card import draw_abyss_img
from ..utils.message.error_reply import UID_HINT
from ..utils.db_operation.db_operation import select_db
from ..utils.mhy_api.convert_mysid_to_uid import convert_mysid
from ..utils.exception.handle_exception import handle_exception
from ..utils.draw_image_tools.send_image_tool import convert_img
@sv.on_rex(
get_abyss_info = on_regex(
r'^(\[CQ:at,qq=[0-9]+\])?( )?'
r'(uid|查询|mys)?([0-9]+)?(上期)?(深渊|sy)'
r'(9|10|11|12|九|十|十一|十二)?(层)?'
r'(\[CQ:at,qq=[0-9]+\])?( )?$',
block=True,
)
async def send_abyss_info(bot: HoshinoBot, ev: CQEvent):
args = ev['match'].groups()
# 恢复了查询二字,和其他的查询打架下次解决
@get_abyss_info.handle()
@handle_exception('查询深渊信息')
@register_menu(
'查询深渊信息',
'查询(@某人)(上期)深渊(xx层)',
'查询你的或者指定人的深渊战绩',
detail_des=(
'介绍:\n'
'可以用来查看你的或者指定人的深渊战绩,可以指定层数,默认为最高层数\n'
'可以在命令文本后带一张图以自定义背景图\n'
' \n' # 如果想要空行,请在换行符前面打个空格,不然会忽略换行符
'指令:\n'
'- <ft color=(238,120,0)>{查询</ft>'
'<ft color=(125,125,125)>(@某人)</ft>'
'<ft color=(238,120,0)>|uid</ft><ft color=(0,148,200)>xx</ft>'
'<ft color=(238,120,0)>|mys</ft><ft color=(0,148,200)>xx</ft>'
'<ft color=(238,120,0)>}</ft>'
'<ft color=(125,125,125)>(上期)</ft>'
'<ft color=(238,120,0)>深渊</ft>'
'<ft color=(125,125,125)>(xx层)</ft>\n'
' \n'
'示例:\n'
'- <ft color=(238,120,0)>查询深渊</ft>\n'
'- <ft color=(238,120,0)>uid123456789上期深渊</ft>\n'
'- <ft color=(238,120,0)>查询</ft><ft color=(0,123,67)>@无疑Wuyi</ft> '
'<ft color=(238,120,0)>上期深渊12层</ft>'
),
)
async def send_abyss_info(
event: TextMessageEvent,
matcher: Matcher,
args: Tuple[Any, ...] = RegexGroup(),
):
logger.info('开始执行[查询深渊信息]')
logger.info('[查询深渊信息]参数: {}'.format(args))
at = re.search(r'\[CQ:at,qq=(\d*)]', str(ev.message))
logger.info(f'[查询深渊信息]参数: {args}')
qid = event.from_wxid
if event.at_user_list:
for user in event.at_user_list:
user = user.strip()
if user != "":
qid = user
if at:
qid = int(at.group(1))
else:
if ev.sender:
qid = int(ev.sender['user_id'])
else:
return
# 判断uid
if args[2] != 'mys':
if args[3] is None:
uid = await select_db(qid, mode='uid')
uid = str(uid)
elif len(args[3]) != 9:
return
else:
uid = args[3]
else:
if args[2] == 'mys':
uid = await convert_mysid(args[3])
logger.info('[查询深渊信息]uid: {}'.format(uid))
if '未找到绑定的UID' in uid:
await bot.send(ev, UID_HINT)
# 判断深渊期数
if args[4] is None:
schedule_type = '1'
elif args[3] is None:
uid = await select_db(qid, mode='uid')
uid = str(uid)
elif len(args[3]) != 9:
return
else:
schedule_type = '2'
logger.info('[查询深渊信息]深渊期数: {}'.format(schedule_type))
uid = args[3]
logger.info(f'[查询深渊信息]uid: {uid}')
if '未找到绑定的UID' in uid:
await matcher.finish(UID_HINT)
schedule_type = '1' if args[4] is None else '2'
logger.info(f'[查询深渊信息]深渊期数: {schedule_type}')
if args[6] in ['', '', '十一', '十二']:
floor = (
args[6]
@ -62,17 +88,17 @@ async def send_abyss_info(bot: HoshinoBot, ev: CQEvent):
.replace('十二', '12')
.replace('', '10')
)
else:
floor = args[6]
if floor is not None:
floor = int(floor)
logger.info('[查询深渊信息]深渊层数: {}'.format(floor))
logger.info(f'[查询深渊信息]深渊层数: {floor}')
im = await draw_abyss_img(uid, floor, schedule_type)
if isinstance(im, str):
await bot.send(ev, im)
await matcher.finish(im)
elif isinstance(im, bytes):
im = await convert_img(im)
await bot.send(ev, im)
await matcher.finish(MessageSegment.image(im))
else:
await bot.send(ev, '发生了未知错误,请联系管理员检查后台输出!')
await matcher.finish('发生了未知错误,请联系管理员检查后台输出!')

View File

@ -1,36 +1,34 @@
from hoshino.typing import CQEvent, HoshinoBot
from nonebot import on_command
from nonebot.log import logger
from nonebot.matcher import Matcher
from nonebot.params import CommandArg
from nonebot.adapters.ntchat import Message
from ..base import sv, logger
from ..config import priority
from .get_achi_desc import get_achi, get_daily_achi
from ..utils.exception.handle_exception import handle_exception
get_task_info = on_command('查委托', aliases={'委托'}, priority=priority)
get_achi_info = on_command('查成就', aliases={'成就'}, priority=priority)
@sv.on_prefix('查委托')
async def send_task_info(bot: HoshinoBot, ev: CQEvent):
if ev.message:
message = ev.message.extract_plain_text().replace(' ', '')
else:
@get_task_info.handle()
@handle_exception('查委托')
async def send_task_info(matcher: Matcher, args: Message = CommandArg()):
if not args:
return
if message == '':
return
name = str(message)
name = str(args[0])
logger.info(f'[查委托] 参数:{name}')
im = await get_daily_achi(name)
await bot.send(ev, im)
await matcher.finish(im)
@sv.on_prefix('查成就')
async def send_achi_info(bot: HoshinoBot, ev: CQEvent):
if ev.message:
message = ev.message.extract_plain_text().replace(' ', '')
else:
@get_achi_info.handle()
@handle_exception('查成就')
async def send_achi_info(matcher: Matcher, args: Message = CommandArg()):
if not args:
return
if message == '':
return
name = str(message)
name = str(args[0])
logger.info(f'[查成就] 参数:{name}')
im = await get_achi(name)
await bot.send(ev, im)
await matcher.finish(im)

View File

@ -1,19 +1,72 @@
from hoshino.typing import CQEvent, HoshinoBot
from typing import Any, Tuple
from ..base import sv
from nonebot import on_regex
from nonebot.matcher import Matcher
from nonebot.params import RegexGroup
from ..config import priority
from .get_adv import char_adv, weapon_adv
from ..genshinuid_meta import register_menu
from ..utils.alias.alias_to_char_name import alias_to_char_name
from ..utils.exception.handle_exception import handle_exception
get_char_adv = on_regex('([\u4e00-\u9fa5]+)(用什么|能用啥|怎么养)', priority=priority)
get_weapon_adv = on_regex(
'([\u4e00-\u9fa5]+)(能给谁|给谁用|要给谁|谁能用)', priority=priority
)
@sv.on_rex(r'([\u4e00-\u9fa5]+)(用什么|能用啥|怎么养)')
async def send_char_adv(bot: HoshinoBot, ev: CQEvent):
name = await alias_to_char_name(str(ev['match'].group(1)))
@get_char_adv.handle()
@handle_exception('建议')
@register_menu(
'角色配置推荐',
'xx用什么',
'查询角色武器/圣遗物推荐配置',
detail_des=(
'介绍:\n'
'可以查询某角色的武器/圣遗物推荐配置\n'
'支持部分角色别名\n'
' \n'
'指令:\n'
'- <ft color=(0,148,200)>[角色名]</ft>'
'<ft color=(238,120,0)>{用什么|能用啥|怎么养}</ft>\n'
' \n'
'示例:\n'
'- <ft color=(238,120,0)>钟离用什么</ft>\n'
'- <ft color=(238,120,0)>公子怎么养</ft>'
),
)
async def send_char_adv(
matcher: Matcher, args: Tuple[Any, ...] = RegexGroup()
):
name = await alias_to_char_name(str(args[0]))
im = await char_adv(name)
await bot.send(ev, im)
await matcher.finish(im)
@sv.on_rex(r'([\u4e00-\u9fa5]+)(能给谁|给谁用|要给谁|谁能用)')
async def send_weapon_adv(bot: HoshinoBot, ev: CQEvent):
name = await alias_to_char_name(str(ev['match'].group(1)))
@get_weapon_adv.handle()
@handle_exception('建议')
@register_menu(
'装备适用角色',
'xx能给谁',
'查询某武器/圣遗物能给谁用',
detail_des=(
'介绍:\n'
'可以通过武器/圣遗物名反查适用的角色\n'
'支持部分别名\n'
' \n'
'指令:\n'
'- <ft color=(0,148,200)>[武器名]</ft>'
'<ft color=(238,120,0)>{能给谁|给谁用|要给谁|谁能用}</ft>\n'
' \n'
'示例:\n'
'- <ft color=(238,120,0)>四风原典能给谁</ft>\n'
'- <ft color=(238,120,0)>千岩给谁用</ft>'
),
)
async def send_weapon_adv(
matcher: Matcher, args: Tuple[Any, ...] = RegexGroup()
):
name = await alias_to_char_name(str(args[0]))
im = await weapon_adv(name)
await bot.send(ev, im)
await matcher.finish(im)

View File

@ -1,90 +1,97 @@
import base64
import random
import asyncio
from nonebot import get_bot
from hoshino import Service, priv
from nonebot.log import logger
from nonebot.matcher import Matcher
from nonebot.params import CommandArg
from nonebot import get_bot, on_command
from nonebot.permission import SUPERUSER
from nonebot_plugin_apscheduler import scheduler
from nonebot.adapters.ntchat.message import Message
from nonebot.adapters.ntchat import (
MessageEvent,
MessageSegment,
TextMessageEvent,
)
from ..base import logger
from .util import black_ids
from ..config import priority
from .main import ann, consume_remind
from ..utils.nonebot2.rule import FullCommand
from ..utils.message.error_reply import UID_HINT
from ..utils.db_operation.db_operation import select_db
from ..genshinuid_config.default_config import string_config
from ..utils.draw_image_tools.send_image_tool import convert_img
from ..utils.exception.handle_exception import handle_exception
from .ann_card import sub_ann, unsub_ann, ann_list_card, ann_detail_card
sv_help = '''
原神公告
原神公告#ID
'''.strip()
sv = Service(
name='原神公告', # 功能名
use_priv=priv.NORMAL, # 使用权限
manage_priv=priv.ADMIN, # 管理权限
visible=True, # 可见性
enable_on_default=True, # 默认启用
# bundle = '娱乐', #分组归类
help_=sv_help, # 帮助说明
update_ann_scheduler = scheduler
get_ann_info = on_command('原神公告', priority=priority)
reg_ann = on_command(
'订阅原神公告', priority=priority, rule=FullCommand(), permission=SUPERUSER
)
prefix = '原神'
unreg_ann = on_command(
'取消订阅原神公告',
aliases={'取消原神公告', '退订原神公告'},
priority=priority,
rule=FullCommand(),
permission=SUPERUSER,
)
consume_ann = on_command('清除原神公告红点', priority=priority, rule=FullCommand())
@sv.on_prefix((f'{prefix}公告#', f'{prefix}公告'))
async def ann_(bot, ev):
ann_id = ev.message.extract_plain_text().strip()
@get_ann_info.handle()
@handle_exception('原神公告', '获取/发送原神公告失败')
async def send_ann_pic(
matcher: Matcher,
args: Message = CommandArg(),
):
ann_id = str(args).replace(' ', '').replace('#', '')
if not ann_id:
img = await ann_list_card()
img = await convert_img(img)
await bot.send(ev, img, at_sender=True)
return
await matcher.finish(MessageSegment.image(img))
if not ann_id.isdigit():
raise Exception("公告ID不正确")
try:
img = await ann_detail_card(int(ann_id))
img = await convert_img(img)
await bot.send(ev, img, at_sender=True)
except Exception as e:
await bot.finish(ev, str(e))
img = await ann_detail_card(int(ann_id))
await matcher.finish(MessageSegment.image(img))
@sv.on_fullmatch(f'订阅{prefix}公告')
async def sub_ann_(bot, ev):
if not priv.check_priv(ev, priv.ADMIN):
raise Exception("你没有权限开启原神公告推送")
try:
await bot.send(ev, sub_ann(ev.group_id))
except Exception as e:
await bot.finish(ev, str(e))
@reg_ann.handle()
@handle_exception('设置原神公告', '设置原神公告失败')
async def send_reg_ann(
event: TextMessageEvent,
matcher: Matcher,
):
await matcher.finish(sub_ann(event.room_wxid))
@sv.on_fullmatch((f'取消订阅{prefix}公告', f'取消{prefix}公告', f'退订{prefix}公告'))
async def unsub_ann_(bot, ev):
if not priv.check_priv(ev, priv.ADMIN):
raise Exception("你没有权限取消原神公告推送")
try:
await bot.send(ev, unsub_ann(ev.group_id))
except Exception as e:
await bot.finish(ev, str(e))
@unreg_ann.handle()
@handle_exception('取消原神公告', '取消设置原神公告失败')
async def send_unreg_ann(
event: TextMessageEvent,
matcher: Matcher,
):
await matcher.finish(unsub_ann(event.room_wxid))
@sv.on_fullmatch(f'取消{prefix}公告红点')
async def consume_remind_(bot, ev):
try:
qid = int(ev.sender['user_id'])
uid = await select_db(qid, mode='uid')
uid = str(uid)
if '未找到绑定的UID' in uid:
await bot.send(ev, UID_HINT)
return
await bot.send(ev, await consume_remind(uid), at_sender=True)
except Exception as e:
await bot.finish(ev, str(e))
@consume_ann.handle()
@handle_exception('取消原神公告红点', '取消红点失败')
async def send_consume_ann(
event: MessageEvent,
matcher: Matcher,
):
qid = event.from_wxid
uid = await select_db(qid, mode='uid')
uid = str(uid)
if '未找到绑定的UID' in uid:
await matcher.finish(UID_HINT)
await matcher.finish(await consume_remind(uid))
@sv.scheduled_job('cron', minute=10)
@update_ann_scheduler.scheduled_job('cron', minute=10)
async def check_ann():
await check_ann_state()
@ -93,11 +100,9 @@ async def check_ann_state():
logger.info('[原神公告] 定时任务: 原神公告查询..')
ids = string_config.get_config('Ann_Ids')
sub_list = string_config.get_config('Ann_Groups')
if not sub_list:
logger.info('没有群订阅, 取消获取数据')
return
if not ids:
ids = await ann().get_ann_ids()
if not ids:
@ -105,10 +110,9 @@ async def check_ann_state():
string_config.set_config('Ann_Ids', ids)
logger.info('初始成功, 将在下个轮询中更新.')
return
new_ids = await ann().get_ann_ids()
new_ann = set(ids) ^ set(new_ids)
new_ann = set(ids) ^ set(new_ids)
if not new_ann:
logger.info('[原神公告] 没有最新公告')
return
@ -117,17 +121,22 @@ async def check_ann_state():
if ann_id in black_ids:
continue
try:
img = await ann_detail_card(ann_id)
img = await convert_img(img)
img = await ann_detail_card(ann_id) # 防止抛出异常报错
bot = get_bot()
b64img = base64.b64encode(img)
for group in sub_list:
try:
await bot.send_group_msg(group_id=group, message=img)
await bot.call_api(
api='send_image',
to_wxid=str(group),
file_path="base64://" + b64img.decode(),
)
await asyncio.sleep(random.uniform(1, 3))
except Exception as e:
logger.exception(e)
except Exception as e:
logger.exception(str(e))
logger.exception(e)
logger.info('[原神公告] 推送完毕, 更新数据库')
string_config.set_config('Ann_Ids', new_ids)

View File

@ -1,10 +1,18 @@
import random
import asyncio
from hoshino.typing import CQEvent, HoshinoBot
from nonebot import on_command
from nonebot.log import logger
from nonebot.matcher import Matcher
from nonebot.permission import SUPERUSER
from nonebot_plugin_apscheduler import scheduler
from nonebot.adapters.ntchat import Bot, TextMessageEvent
from ..base import sv, logger
from ..config import priority
from .backup_data import data_backup
from ..genshinuid_meta import register_menu
from ..utils.nonebot2.rule import FullCommand
from ..utils.exception.handle_exception import handle_exception
from ..utils.db_operation.db_cache_and_check import check_db, check_stoken_db
from ..utils.db_operation.db_operation import delete_cookies, get_all_push_list
from ..utils.message.get_cqhttp_data import (
@ -12,35 +20,75 @@ from ..utils.message.get_cqhttp_data import (
get_group_member_list,
)
backup = on_command(
'gs清除缓存', rule=FullCommand(), priority=priority, permission=SUPERUSER
)
check = on_command(
'校验全部Cookies', rule=FullCommand(), priority=priority, permission=SUPERUSER
)
check_stoken = on_command(
'校验全部Stoken', rule=FullCommand(), priority=priority, permission=SUPERUSER
)
remove_invalid_user = on_command(
'清除无效用户', rule=FullCommand(), priority=priority, permission=SUPERUSER
)
@sv.scheduled_job('cron', hour=0)
backup_scheduler = scheduler
@backup_scheduler.scheduled_job('cron', hour=0)
async def daily_refresh_charData():
await data_backup()
@sv.on_fullmatch('gs清除缓存')
@backup.handle()
@handle_exception('清除缓存', '清除缓存错误')
@register_menu(
'清除缓存',
'清除缓存',
'清除插件产生的缓存数据',
trigger_method='超级用户指令',
detail_des=(
'介绍:\n'
'备份一份插件数据库后清除插件产生的文件与数据库缓存\n'
' \n'
'指令:\n'
'- <ft color=(238,120,0)>清除缓存</ft>'
),
)
async def send_backup_msg(
bot: HoshinoBot,
ev: CQEvent,
bot: Bot,
event: TextMessageEvent,
matcher: Matcher,
):
if ev.sender:
qid = int(ev.sender['user_id'])
else:
if not await SUPERUSER(bot, event):
return
if qid not in bot.config.SUPERUSERS:
return
await data_backup()
await bot.send(ev, '操作成功完成!')
await matcher.finish('操作成功完成!')
@sv.on_fullmatch('清除无效用户')
async def send_remove_invalid_user_msg(bot: HoshinoBot, ev: CQEvent):
if ev.sender:
qid = int(ev.sender['user_id'])
else:
return
if qid not in bot.config.SUPERUSERS:
@remove_invalid_user.handle()
@handle_exception('清除无效用户', '清除无效用户错误')
@register_menu(
'清除无效用户',
'清除无效用户',
'清除非好友或非推送群成员的数据',
trigger_method='超级用户指令',
detail_des=(
'介绍:\n'
'从数据库中删除掉 开启了私聊推送但不是Bot好友的用户 '
'以及 开启了群聊推送但不在推送目标群的用户 的数据\n'
' \n'
'指令:\n'
'- <ft color=(238,120,0)>清除无效用户</ft>'
),
)
async def send_remove_invalid_user_msg(
bot: Bot,
event: TextMessageEvent,
matcher: Matcher,
):
if not await SUPERUSER(bot, event):
return
im_list = []
invalid_user = {}
@ -62,40 +110,78 @@ async def send_remove_invalid_user_msg(bot: HoshinoBot, ev: CQEvent):
for uid in invalid_uid_list:
im_list.append(await delete_cookies(str(uid)))
logger.warning(f'无效UID已被删除: {uid}')
await bot.send(ev, f'已清理失效用户{len(im_list)}个!')
await matcher.finish(f'已清理失效用户{len(im_list)}个!')
# 群聊内 校验Cookies 是否正常的功能,不正常自动删掉
@sv.on_fullmatch('校验全部Cookies')
async def send_check_cookies(bot: HoshinoBot, ev: CQEvent):
@check.handle()
@handle_exception('Cookie校验', 'Cookie校验错误')
@register_menu(
'校验全部Cookies',
'校验全部Cookies',
'校验数据库内所有Cookies是否正常',
detail_des=(
'介绍:\n'
'校验数据库内所有Cookies是否正常不正常的会自动删除\n'
' \n'
'指令:\n'
'- <ft color=(238,120,0)>校验全部Cookies</ft>\n'
'注意<ft color=(238,120,0)>Cookies</ft>的'
'<ft color=(238,120,0)>C</ft>为大写'
),
)
async def send_check_cookie(bot: Bot, matcher: Matcher):
raw_mes = await check_db()
im = raw_mes[0]
await bot.send(ev, im)
await matcher.send(im)
for i in raw_mes[1]:
await bot.send_private_msg(
user_id=i[0],
message=(
'您绑定的Cookiesuid{})已失效,以下功能将会受到影响:\n'
'查看完整信息列表\n查看深渊配队\n自动签到/当前状态/每月统计\n'
'请及时重新绑定Cookies并重新开关相应功能。'
).format(i[1]),
await bot.call_api(
api='send_private_msg',
**{
'user_id': i[0],
'message': (
'绑定Cookiesuid{})已失效,以下功能将会受到影响:\n'
'查看完整信息列表\n查看深渊配队\n自动签到/当前状态/每月统计\n'
'请及时重新绑定Cookies并重新开关相应功能。'
).format(i[1]),
},
)
await asyncio.sleep(3 + random.randint(1, 3))
await matcher.finish()
# 群聊内 校验Stoken 是否正常的功能,不正常自动删掉
@sv.on_fullmatch('校验全部Stoken')
async def send_check_stoken(bot: HoshinoBot, ev: CQEvent):
@check_stoken.handle()
@handle_exception('Stoken校验', 'Stoken校验错误')
@register_menu(
'校验全部Stoken',
'校验全部Stoken',
'校验数据库内所有Stoken是否正常',
detail_des=(
'介绍:\n'
'校验数据库内所有Stoken是否正常不正常的会自动删除\n'
' \n'
'指令:\n'
'- <ft color=(238,120,0)>校验全部Stoken</ft>\n'
'注意<ft color=(238,120,0)>Stoken</ft>的<ft color=(238,120,0)>S</ft>为大写'
),
)
async def send_check_stoken(bot: Bot, matcher: Matcher):
raw_mes = await check_stoken_db()
im = raw_mes[0]
await bot.send(ev, im)
await matcher.send(im)
for i in raw_mes[1]:
await bot.send_private_msg(
user_id=i[0],
message=(
'您绑定的Stokenuid{})已失效,以下功能将会受到影响:\n'
'gs开启自动米游币开始获取米游币。\n'
'重新添加后需要重新开启自动米游币。'
).format(i[1]),
await bot.call_api(
api='send_private_msg',
**{
'user_id': i[0],
'message': (
'您绑定的Stokenuid{})已失效,以下功能将会受到影响:\n'
'gs开启自动米游币开始获取米游币。\n'
'重新添加后需要重新开启自动米游币。'
).format(i[1]),
},
)
await asyncio.sleep(3 + random.randint(1, 3))
await matcher.finish()

View File

@ -1,70 +1,99 @@
import re
from nonebot import on_command
from nonebot.log import logger
from nonebot.matcher import Matcher
from nonebot.params import CommandArg
from nonebot.adapters.ntchat.message import Message
from nonebot.adapters.ntchat import MessageSegment, TextMessageEvent
from hoshino.typing import CQEvent, HoshinoBot
from ..base import sv, logger
from ..genshinuid_meta import register_menu
from ..utils.data_convert.get_uid import get_uid
from ..utils.message.error_reply import UID_HINT
from ..utils.message.error_reply import * # noqa: F403,F401
from ..utils.draw_image_tools.send_image_tool import convert_img
from ..utils.exception.handle_exception import handle_exception
from .draw_collection_card import draw_explora_img, draw_collection_img
get_collection_info = on_command('查询收集', aliases={'收集', 'sj'}, block=True)
get_explora_info = on_command('查询探索', aliases={'探索', 'ts'}, block=True)
@sv.on_prefix(('查询收集', '收集', 'sj'))
async def send_collection_info(bot: HoshinoBot, ev: CQEvent):
raw_mes = ev.message.extract_plain_text().replace(' ', '')
@get_collection_info.handle()
@handle_exception('查询收集信息')
@register_menu(
'查询收集信息',
'查询(@某人)收集',
'查询你的或者指定人的宝箱收集度',
detail_des=(
'介绍:\n'
'可以用来查看你的或者指定人的宝箱收集度\n'
'可以在命令文本后带一张图以自定义背景图\n'
' \n'
'指令:\n'
'- <ft color=(238,120,0)>{查询</ft>'
'<ft color=(125,125,125)>(@某人)</ft>'
'<ft color=(238,120,0)>|uid</ft><ft color=(0,148,200)>xx</ft>'
'<ft color=(238,120,0)>|mys</ft><ft color=(0,148,200)>xx</ft>'
'<ft color=(238,120,0)>}</ft>'
'<ft color=(238,120,0)>{收集|宝箱|sj|bx}</ft>\n'
' \n'
'示例:\n'
'- <ft color=(238,120,0)>查询收集</ft>\n'
'- <ft color=(238,120,0)>uid123456789宝箱</ft>\n'
'- <ft color=(238,120,0)>查询</ft><ft color=(0,123,67)>@无疑Wuyi'
'</ft> <ft color=(238,120,0)>bx</ft>'
),
)
async def send_collection_info(
event: TextMessageEvent,
matcher: Matcher,
args: Message = CommandArg(),
):
logger.info('开始执行[查询收集信息]')
at = re.search(r'\[CQ:at,qq=(\d*)]', raw_mes)
if at:
qid = int(at.group(1))
logger.info('[查询收集信息]参数: {}'.format(args))
raw_mes = args.extract_plain_text().strip()
if event.at_user_list:
qid = event.at_user_list[0]
else:
if ev.sender:
qid = int(ev.sender['user_id'])
else:
return
qid = event.from_wxid
uid = await get_uid(qid, raw_mes)
logger.info('[查询收集信息]uid: {}'.format(uid))
if '未找到绑定的UID' in uid:
await bot.send(ev, UID_HINT)
await matcher.finish(UID_HINT)
im = await draw_collection_img(qid, uid)
if isinstance(im, str):
await bot.send(ev, im)
await matcher.finish(im)
elif isinstance(im, bytes):
im = await convert_img(im)
await bot.send(ev, im)
await matcher.finish(MessageSegment.image(im))
else:
await bot.send(ev, '发生了未知错误,请联系管理员检查后台输出!')
await matcher.finish('发生了未知错误,请联系管理员检查后台输出!')
@sv.on_prefix(('查询探索', '探索', 'ts'))
async def send_explora_info(bot: HoshinoBot, ev: CQEvent):
raw_mes = ev.message.extract_plain_text().replace(' ', '')
@get_explora_info.handle()
@handle_exception('查询探索信息')
async def send_explora_info(
event: TextMessageEvent,
matcher: Matcher,
args: Message = CommandArg(),
):
logger.info('开始执行[查询探索信息]')
at = re.search(r'\[CQ:at,qq=(\d*)]', raw_mes)
if at:
qid = int(at.group(1))
logger.info('[查询探索信息]参数: {}'.format(args))
raw_mes = args.extract_plain_text().strip()
if event.at_user_list:
qid = event.at_user_list[0]
else:
if ev.sender:
qid = int(ev.sender['user_id'])
else:
return
qid = event.from_wxid
uid = await get_uid(qid, raw_mes)
logger.info('[查询探索信息]uid: {}'.format(uid))
if '未找到绑定的UID' in uid:
await bot.send(ev, UID_HINT)
await matcher.finish(UID_HINT)
im = await draw_explora_img(qid, uid)
if isinstance(im, str):
await bot.send(ev, im)
await matcher.finish(im)
elif isinstance(im, bytes):
im = await convert_img(im)
await bot.send(ev, im)
await matcher.finish(MessageSegment.image(im))
else:
await bot.send(ev, '发生了未知错误,请联系管理员检查后台输出!')
await matcher.finish('发生了未知错误,请联系管理员检查后台输出!')

View File

@ -1,133 +1,211 @@
import re
from typing import Any, Tuple
from hoshino.typing import CQEvent, HoshinoBot
from nonebot.log import logger
from nonebot.matcher import Matcher
from nonebot.params import RegexGroup
from nonebot import on_regex, on_command
from nonebot.permission import SUPERUSER
from nonebot.adapters.ntchat import Bot, MessageSegment, TextMessageEvent
from ..base import sv, logger
from ..genshinuid_meta import register_menu
from ..utils.nonebot2.rule import FullCommand
from .draw_config_card import draw_config_img
from ..utils.message.error_reply import UID_HINT
from ..utils.db_operation.db_operation import select_db
from .set_config import set_push_value, set_config_func
from ..utils.message.error_reply import * # noqa: F403,F401
from ..utils.draw_image_tools.send_image_tool import convert_img
from ..utils.exception.handle_exception import handle_exception
open_and_close_switch = on_regex(
r'^(\[CQ:at,qq=[0-9]+\])?( )?'
r'(gs)(开启|关闭)(.*)'
r'(\[CQ:at,qq=[0-9]+\])?( )?$'
)
@sv.on_fullmatch('gs配置')
async def send_config_card(bot: HoshinoBot, ev: CQEvent):
logger.info('开始执行[gs配置]')
im = await draw_config_img()
if isinstance(im, str):
await bot.send(ev, im)
elif isinstance(im, bytes):
im = await convert_img(im)
await bot.send(ev, im)
else:
await bot.send(ev, '发生了未知错误,请联系管理员检查后台输出!')
@sv.on_rex(
push_config = on_regex(
r'^(\[CQ:at,qq=[0-9]+\])?( )?'
r'(gs)(设置)([\u4e00-\u9fffa-zA-Z]*)([0-9]*)'
r'(\[CQ:at,qq=[0-9]+\])?( )?$'
)
async def send_config_msg(bot: HoshinoBot, ev: CQEvent):
args = ev['match'].groups()
config_card = on_command('gs配置', rule=FullCommand())
@config_card.handle()
@handle_exception('发送配置表')
@register_menu(
'发送配置表',
'gs配置',
'查看插件当前配置项开关情况',
detail_des=(
'介绍:\n'
'查看插件当前配置项开关情况\n'
' \n'
'指令:\n'
'- <ft color=(238,120,0)>gs配置</ft>'
),
)
async def send_config_card(matcher: Matcher):
logger.info('开始执行[gs配置]')
im = await draw_config_img()
if isinstance(im, str):
await matcher.finish(im)
elif isinstance(im, bytes):
await matcher.finish(MessageSegment.image(im))
else:
await matcher.finish('发生了未知错误,请联系管理员检查后台输出!')
@push_config.handle()
@handle_exception('设置推送服务')
@register_menu(
'设置推送阈值',
'gs设置xx(@某人)',
'设置自己或指定人的推送服务阈值',
detail_des=(
'介绍:\n'
'设置某人的推送服务阈值\n'
'超级用户可以设置他人的推送服务阈值\n'
' \n'
'指令:\n'
'- <ft color=(238,120,0)>gs设置</ft>'
'<ft color=(0,148,200)>[服务名称][阈值]</ft>'
'<ft color=(125,125,125)>(@某人)</ft>\n'
' \n'
'示例:\n'
'- <ft color=(238,120,0)>gs设置推送140</ft>'
),
)
async def send_config_msg(
bot: Bot,
event: TextMessageEvent,
matcher: Matcher,
args: Tuple[Any, ...] = RegexGroup(),
):
logger.info('开始执行[设置阈值信息]')
logger.info('[设置阈值信息]参数: {}'.format(args))
wxid_list = []
wxid_list.append(event.from_wxid)
qid = event.from_wxid
if event.at_user_list:
for user in event.at_user_list:
user = user.strip()
if user != "" and await SUPERUSER(bot, event):
qid = user
else:
await matcher.finish(
MessageSegment.room_at_msg(
content="{$@}你没有权限操作别人的状态噢~", at_list=wxid_list
)
)
at = re.search(r'\[CQ:at,qq=(\d*)]', str(ev.message))
if at:
qid = int(at.group(1))
else:
if ev.sender:
qid = int(ev.sender['user_id'])
else:
return
if qid in bot.config.SUPERUSERS:
is_admin = True
else:
is_admin = False
if at and is_admin:
qid = at
elif at and at != qid:
await bot.send(ev, '你没有权限操作别人的状态噢~', at_sender=True)
logger.info('[设置阈值信息]qid: {}'.format(qid))
try:
uid = await select_db(qid, mode='uid')
except TypeError:
await bot.send(ev, UID_HINT)
return
await matcher.finish(
MessageSegment.room_at_msg(
content='{$@}' + UID_HINT, at_list=wxid_list
)
)
func = args[4].replace('阈值', '')
if args[5]:
try:
value = int(args[5])
except ValueError:
await bot.send(ev, '请输入数字哦~', at_sender=True)
return
await matcher.finish(
MessageSegment.room_at_msg(
content="{$@}请输入数字哦~", at_list=wxid_list
)
)
else:
await bot.send(ev, '请输入正确的阈值数字!', at_sender=True)
return
await matcher.finish(
MessageSegment.room_at_msg(
content="{$@}请输入正确的阈值数字!", at_list=wxid_list
)
)
logger.info('[设置阈值信息]func: {}, value: {}'.format(func, value))
im = await set_push_value(func, str(uid), value)
await bot.send(ev, im, at_sender=True)
await matcher.finish(
MessageSegment.room_at_msg(content="{$@}" + f"{im}", at_list=wxid_list)
)
# 开启 自动签到 和 推送树脂提醒 功能
@sv.on_rex(
r'^(\[CQ:at,qq=[0-9]+\])?( )?'
r'(gs)(开启|关闭)(.*)'
r'(\[CQ:at,qq=[0-9]+\])?( )?$'
@open_and_close_switch.handle()
@register_menu(
'开关推送服务',
'gs{开启|关闭}xx(@某人)',
'开关自己或指定人的推送服务状态',
detail_des=(
'介绍:\n'
'设置某人的推送服务开关状态\n'
'超级用户可以设置他人的推送服务状态\n'
' \n'
'指令:\n'
'- <ft color=(238,120,0)>gs{开启|关闭}</ft>'
'<ft color=(0,148,200)>[服务名称]</ft>'
'<ft color=(125,125,125)>(@某人)</ft>\n'
' \n'
'示例:\n'
'- <ft color=(238,120,0)>gs开启推送</ft>'
),
)
async def open_switch_func(bot: HoshinoBot, ev: CQEvent):
args = ev['match'].groups()
at = re.search(r'\[CQ:at,qq=(\d*)]', str(ev.message))
if at:
qid = int(at.group(1))
else:
if ev.sender:
qid = int(ev.sender['user_id'])
else:
return
async def open_switch_func(
bot: Bot,
event: TextMessageEvent,
matcher: Matcher,
args: Tuple[Any, ...] = RegexGroup(),
):
wxid_list = []
wxid_list.append(event.from_wxid)
qid = event.from_wxid
if event.at_user_list:
for user in event.at_user_list:
user = user.strip()
if user != "" and await SUPERUSER(bot, event):
qid = user
else:
await matcher.finish(
MessageSegment.room_at_msg(
content="{$@}你没有权限操作别人的状态噢~", at_list=wxid_list
)
)
config_name = args[4]
logger.info(f'[{qid}]尝试[{args[3]}]了[{config_name}]功能')
fake_seession = f'group_{event.room_wxid}_{event.from_wxid}'
if args[3] == '开启':
query = 'OPEN'
gid = str(ev.group_id) if ev.group_id else 'on'
gid = (
fake_seession.split('_')[1]
if len(fake_seession.split('_')) == 3
else 'on'
)
else:
query = 'CLOSED'
gid = 'off'
if qid in bot.config.SUPERUSERS:
is_admin = True
else:
is_admin = False
if at and is_admin:
qid = at
elif at and at != qid:
bot.send(ev, '你没有权限操作别人的状态噢~', at_sender=True)
return
try:
uid = await select_db(qid, mode='uid')
except TypeError:
await bot.send(ev, UID_HINT)
return
uid = await select_db(qid, mode='uid')
if uid is None or not isinstance(uid, str) or not uid.isdecimal():
await matcher.finish(
MessageSegment.room_at_msg(
content='{$@}' + UID_HINT, at_list=wxid_list
)
)
im = await set_config_func(
config_name=config_name,
uid=uid, # type: ignore
qid=qid, # type: ignore
uid=uid,
qid=str(qid),
option=gid,
query=query,
is_admin=is_admin,
is_admin=await SUPERUSER(bot, event),
)
await matcher.finish(
MessageSegment.room_at_msg(content="{$@}" + f"{im}", at_list=wxid_list)
)
await bot.send(ev, im, at_sender=True)

View File

@ -1,17 +1,15 @@
from hoshino.typing import CQEvent, HoshinoBot
from nonebot import on_command
from nonebot.matcher import Matcher
from nonebot.permission import SUPERUSER
from ..base import sv
from .export_data import export_v3
from ..utils.nonebot2.rule import FullCommand
export_v3_data = on_command('导出v3数据', permission=SUPERUSER, rule=FullCommand())
@sv.on_prefix(('导出v3数据'))
async def send_export_msg(bot: HoshinoBot, ev: CQEvent):
if ev.sender:
qid = int(ev.sender['user_id'])
else:
return
if qid not in bot.config.SUPERUSERS:
return
await bot.send(ev, '开始导出数据..可能需要一定时间!')
@export_v3_data.handle()
async def send_export_msg(matcher: Matcher):
await matcher.send('开始导出数据..可能需要一定时间!')
await export_v3()
await bot.send(ev, '导出数据成功!')
await matcher.send('导出数据成功!')

View File

@ -5,7 +5,7 @@ from ..utils.download_resource.RESOURCE_PATH import MAIN_PATH
from ..utils.db_operation.db_operation import get_all_bind, get_user_bind_data
DATA_PATH = MAIN_PATH / 'v3_data.json'
BOT_ID = 'onebot'
BOT_ID = 'ntchat'
RECOGNIZE_SERVER = {
'1': 'cn_gf01',
'2': 'cn_gf01',

View File

@ -3,104 +3,197 @@ import random
import asyncio
from typing import Tuple
from hoshino.typing import CQEvent, HoshinoBot
from nonebot import on_command
from nonebot.log import logger
from nonebot.matcher import Matcher
from nonebot.params import CommandArg
from nonebot.permission import SUPERUSER
from nonebot_plugin_apscheduler import scheduler
from nonebot.adapters.ntchat.message import Message
from nonebot.adapters.ntchat import (
Bot,
MessageEvent,
MessageSegment,
TextMessageEvent,
QuoteMessageEvent,
)
from ..base import sv, logger
from ..config import priority
from .get_enka_img import draw_enka_img
from ..genshinuid_meta import register_menu
from ..utils.nonebot2.rule import FullCommand
from .draw_char_rank import draw_cahrcard_list
from ..utils.message.error_reply import UID_HINT
from ..utils.enka_api.get_enka_data import switch_api
from ..utils.enka_api.enka_to_card import enka_to_card
from ..utils.enka_api.enka_to_data import enka_to_data
from ..utils.download_resource.RESOURCE_PATH import TEMP_PATH
from ..utils.draw_image_tools.send_image_tool import convert_img
from ..utils.exception.handle_exception import handle_exception
from ..utils.db_operation.db_operation import select_db, get_all_uid
refresh = on_command('强制刷新')
original_pic = on_command('原图', rule=FullCommand())
change_api = on_command('切换api', rule=FullCommand())
get_charcard_list = on_command('毕业度统计')
get_char_info = on_command(
'查询',
priority=priority,
)
AUTO_REFRESH = False
refresh_scheduler = scheduler
@sv.on_fullmatch('切换api')
async def send_change_api_info(bot: HoshinoBot, ev: CQEvent):
print(ev)
if ev.sender:
qid = int(ev.sender['user_id'])
else:
return
if qid not in bot.config.SUPERUSERS:
@change_api.handle()
@handle_exception('切换api')
@register_menu(
'切换API',
'切换api',
'切换获取角色面板时使用的API',
detail_des=(
'介绍:\n'
'切换获取角色面板时使用的Enka Network API\n'
' \n'
'指令:\n'
'- <ft color=(238,120,0)>切换api</ft>'
),
)
async def send_change_api_info(
bot: Bot,
event: MessageEvent,
matcher: Matcher,
):
if not await SUPERUSER(bot, event):
return
im = await switch_api()
await bot.send(ev, im)
await matcher.finish(im)
@sv.on_rex(r'^(\[CQ:reply,id=[0-9]+\])?( )?(\[CQ:at,qq=[0-9]+\])?( )?原图')
async def send_original_pic(bot: HoshinoBot, ev: CQEvent):
for msg in ev.message:
if msg['type'] == 'reply':
msg_id = msg['data']['id']
path = TEMP_PATH / f'{msg_id}.jpg'
if path.exists():
logger.info('[原图]访问图片: {}'.format(path))
with open(path, 'rb') as f:
im = await convert_img(f.read())
await bot.send(ev, im)
@sv.on_rex(
r'^(\[CQ:at,qq=[0-9]+\])?( )?'
r'(uid|查询|mys)([0-9]+)?'
r'([\u4e00-\u9fa5]+)'
r'(\[CQ:at,qq=[0-9]+\])?( )?',
@original_pic.handle()
@handle_exception('原图')
@register_menu(
'查看面板原图',
'原图',
'查看角色面板中原随机图',
trigger_method='回复+指令',
detail_des=(
'介绍:\n'
'查看开启随机图功能时角色面板中角色位置的原图,需要回复要查看原图的面板图片消息\n'
' \n'
'指令:\n'
'- <ft color=(238,120,0)>原图</ft>'
),
)
async def send_char_info(bot: HoshinoBot, ev: CQEvent):
args = ev['match'].groups()
if args[4] is None:
return
# 获取角色名
msg = ''.join(re.findall('[\u4e00-\u9fa5]', args[4]))
logger.info('开始执行[查询角色面板]')
logger.info('[查询角色面板]参数: {}'.format(args))
# 获取角色名
at = re.search(r'\[CQ:at,qq=(\d*)]', str(ev.message))
image = re.search(r'\[CQ:image,file=(.*),url=(.*)]', str(ev.message))
async def send_original_pic(
event: QuoteMessageEvent,
matcher: Matcher,
):
msg_id = event.quote_message_id
path = TEMP_PATH / f'{msg_id}.jpg'
if path.exists():
logger.info('[原图]访问图片: {}'.format(path))
with open(path, 'rb') as f:
await matcher.finish(MessageSegment.image(f.read()))
img = None
if image:
img = image.group(2)
if at:
qid = int(at.group(1))
else:
if ev.sender:
qid = int(ev.sender['user_id'])
else:
@get_char_info.handle()
@handle_exception('查询角色面板')
@register_menu(
'查询角色面板',
'查询(@某人)角色名',
'查询你的或者指定人的已缓存展柜角色的面板',
detail_des=(
'介绍:\n'
'可以用来查看你的或者指定人的已缓存展柜角色的面板\n'
'支持部分角色别名\n'
' \n'
'指令:\n'
'- <ft color=(238,120,0)>{查询</ft>'
'<ft color=(125,125,125)>(@某人)</ft>'
'<ft color=(238,120,0)>|uid</ft><ft color=(0,148,200)>xx</ft>'
'<ft color=(238,120,0)>|mys</ft><ft color=(0,148,200)>xx</ft>'
'<ft color=(238,120,0)>}</ft>'
'<ft color=(0,148,200)>[角色名]</ft>\n'
'后面可以跟 '
'<ft color=(238,120,0)>换</ft>'
'<ft color=(125,125,125)>(精{一|二|三|四|五})</ft>'
'<ft color=(0,148,200)>[武器名]</ft> '
'来更换展示的武器\n'
'可以跟 '
'<ft color=(125,125,125)>(成长)</ft><ft color=(238,120,0)>曲线</ft> '
'来查询该角色成长曲线\n'
' \n'
'示例:\n'
'- <ft color=(238,120,0)>查询宵宫</ft>\n'
'- <ft color=(238,120,0)>查询绫华换精五雾切</ft>\n'
'- <ft color=(238,120,0)>查询一斗成长曲线</ft>\n'
'- <ft color=(238,120,0)>查询</ft><ft color=(0,123,67)>@无疑Wuyi</ft>'
' <ft color=(238,120,0)>公子</ft>'
),
)
@register_menu(
'查询展柜角色',
'查询展柜角色',
'查询插件已缓存的展柜角色列表',
detail_des=(
'介绍:\n'
'查询插件当前已缓存的展柜角色列表\n'
' \n'
'指令:\n'
'- <ft color=(238,120,0)>查询展柜角色</ft>'
),
)
async def send_char_info(
event: TextMessageEvent,
matcher: Matcher,
args: Message = CommandArg(),
):
raw_mes = args.extract_plain_text().strip()
name = ''.join(re.findall('[\u4e00-\u9fa5]', raw_mes))
# 如果输入中(查询之后的内容)没有其他字符串(如角色名等)则忽略。
if not name:
return
# 若查询中有@则排除@后内容,排除后若@前没有 其他内容则不继续运行下方代码 防止与roleinfo查询打架
# 修改后@人应该在命令末尾 如:查询万叶@XXXX
if "@" in raw_mes:
raw_mes = raw_mes.split("@")[0]
if not raw_mes:
return
logger.info('[查询角色面板]QQ: {}'.format(qid))
logger.info('开始执行[查询角色面板]')
# 获取被@的Wxid排除""
qid = event.from_wxid
if event.at_user_list:
for user in event.at_user_list:
user = user.strip()
if user != "":
qid = user
logger.info('[查询角色面板]WXID: {}'.format(qid))
# 获取uid
if args[3] is None:
uid = re.findall(r'\d+', raw_mes.split("@")[0])
if uid:
uid = uid[0]
else:
uid = await select_db(qid, mode='uid')
uid = str(uid)
else:
uid = args[3]
logger.info('[查询角色面板]uid: {}'.format(uid))
if '未找到绑定的UID' in uid:
await bot.send(ev, UID_HINT)
await matcher.finish(UID_HINT)
im = await draw_enka_img(msg, uid, img)
im = await draw_enka_img(raw_mes, uid, None)
if isinstance(im, str):
await bot.send(ev, im)
await matcher.finish(im)
elif isinstance(im, Tuple):
img = await convert_img(im[0])
req = await bot.send(ev, img)
msg_id = req['message_id']
await matcher.send(MessageSegment.image(im[0]))
# msg_id = req['message_id']
if im[1]:
with open(TEMP_PATH / f'{msg_id}.jpg', 'wb') as f:
with open(TEMP_PATH / f'{uid}.jpg', 'wb') as f:
f.write(im[1])
else:
await bot.send(ev, '发生了未知错误,请联系管理员检查后台输出!')
await matcher.finish('发生了未知错误,请联系管理员检查后台输出!')
async def refresh_char_data():
@ -125,92 +218,119 @@ async def refresh_char_data():
return f'执行成功!共刷新{str(t)}个角色!'
@sv.scheduled_job('cron', hour='4')
@refresh_scheduler.scheduled_job('cron', hour='4')
async def daily_refresh_charData():
global AUTO_REFRESH
if AUTO_REFRESH:
await refresh_char_data()
@sv.on_prefix('强制刷新')
async def send_card_info(bot: HoshinoBot, ev: CQEvent):
if ev.message:
message = ev.message.extract_plain_text().replace(' ', '')
else:
return
at = re.search(r'\[CQ:at,qq=(\d*)]', str(ev.message))
if at:
qid = int(at.group(1))
message = message.replace(str(at), '')
else:
if ev.sender:
qid = int(ev.sender['user_id'])
else:
return
uid = re.findall(r'\d+', message) # str
@refresh.handle()
@handle_exception('强制刷新')
@register_menu(
'刷新展柜缓存',
'强制刷新',
'强制刷新你的或者指定人缓存的角色展柜数据',
detail_des=(
'指令:'
'<ft color=(238,120,0)>强制刷新</ft>'
'<ft color=(125,125,125)>(uid/全部数据)</ft>\n'
' \n'
'用于强制刷新你的或者指定人缓存的角色展柜数据\n'
'当刷新全部数据时需要机器人管理员权限\n'
' \n'
'示例:\n'
'<ft color=(238,120,0)>强制刷新</ft>\n'
'<ft color=(238,120,0)>强制刷新123456789</ft>\n'
'<ft color=(238,120,0)>强制刷新全部数据</ft>'
),
)
async def send_card_info(
bot: Bot,
matcher: Matcher,
event: TextMessageEvent,
args: Message = CommandArg(),
):
message = args.extract_plain_text().strip().replace(' ', '')
uid = re.findall(r'\d+', message.split("@")[0]) # str
m = ''.join(re.findall('[\u4e00-\u9fa5]', message))
# 获取被@的Wxid排除""
qid = event.from_wxid
if event.at_user_list:
for user in event.at_user_list:
user = user.strip()
if user != "":
qid = user
if len(uid) >= 1:
uid = uid[0]
else:
if m == '全部数据':
if qid in bot.config.SUPERUSERS:
await bot.send(ev, '开始刷新全部数据,这可能需要相当长的一段时间!!')
if await SUPERUSER(bot, event):
await matcher.send('开始刷新全部数据,这可能需要相当长的一段时间!!')
im = await refresh_char_data()
await bot.send(ev, str(im))
return
await matcher.finish(str(im))
else:
return
else:
uid = await select_db(qid, mode='uid')
uid = str(uid)
if '未找到绑定的UID' in uid:
await bot.send(ev, UID_HINT)
return
if not uid:
await matcher.finish(UID_HINT)
im = await enka_to_card(uid)
if isinstance(im, str):
await bot.send(ev, im)
await matcher.finish(im)
elif isinstance(im, bytes):
im = await convert_img(im)
await bot.send(ev, im)
await matcher.finish(MessageSegment.image(im))
else:
await bot.send(ev, '发生了未知错误,请联系管理员检查后台输出!')
await matcher.finish('发生了未知错误,请联系管理员检查后台输出!')
@sv.on_prefix('毕业度统计')
async def send_charcard_list(bot: HoshinoBot, ev: CQEvent):
if ev.message:
message = ev.message.extract_plain_text().replace(' ', '')
else:
return
at = re.search(r'\[CQ:at,qq=(\d*)]', str(ev.message))
if at:
qid = int(at.group(1))
message = message.replace(str(at), '')
else:
if ev.sender:
qid = int(ev.sender['user_id'])
else:
return
@get_charcard_list.handle()
@handle_exception('毕业度统计')
@register_menu(
'毕业度统计',
'毕业度统计',
'查看你或指定人已缓存的展柜角色毕业度',
detail_des=(
'指令:'
'<ft color=(238,120,0)>毕业度统计</ft>'
'<ft color=(125,125,125)>(@某人)</ft>\n'
' \n'
'可以查看你或指定人已缓存的所有展柜角色毕业度\n'
' \n'
'示例:\n'
'<ft color=(238,120,0)>毕业度统计</ft>\n'
'<ft color=(238,120,0)>毕业度统计</ft><ft color=(0,148,200)>@无疑Wuyi</ft>'
),
)
async def send_charcard_list(
event: TextMessageEvent,
matcher: Matcher,
args: Message = CommandArg(),
):
raw_mes = args.extract_plain_text().strip()
# 获取被@的Wxid排除""
qid = event.from_wxid
if event.at_user_list:
for user in event.at_user_list:
user = user.strip()
if user != "":
qid = user
# 获取uid
uid = re.findall(r'\d+', message)
uid = re.findall(r'\d+', raw_mes.split("@")[0])
if uid:
uid = uid[0]
else:
uid = await select_db(qid, mode='uid')
uid = str(uid)
im = await draw_cahrcard_list(str(uid), qid)
logger.info(f'UID{uid}获取角色数据成功!')
if isinstance(im, bytes):
im = await convert_img(im)
await bot.send(ev, im)
await matcher.finish(MessageSegment.image(im))
else:
await bot.send(ev, str(im))
await matcher.finish(str(im))

View File

@ -501,7 +501,7 @@ class Character:
# 重新计算加成值
# base_effect_list = [
# [limit_list, effect_attr, effect_value,effect_base]
# [limit_list, effect_attr, effect_value, effect_base]
# ]
for effect in base_effect_list:
prop = await self.get_buff_value(prop, *effect)
@ -614,8 +614,12 @@ class Character:
'type': '攻击力',
'plus': 1,
'value': [
f'''{int(i[:-1]) +
int(self.power_list["A霜华矢·霜华绽发伤害"]["value"][index][:-1])
f'''{
int(i[:-1]) + int(
self.power_list[
"A霜华矢·霜华绽发伤害"
]["value"][index][:-1]
)
}%'''
for index, i in enumerate(
self.power_list['A霜华矢命中伤害']['value']

View File

@ -216,8 +216,8 @@ class Fight:
if '前台' in char.power_list[char.power_name]['name']:
if char.char_name == '纳西妲':
em = char.fight_prop[f'{char.attack_type}_elementalMastery']
effect = f'''elementalMastery+
{0.25 * em if 0.25 * em <= 250 else 250}
effect = f'''
elementalMastery+{0.25 * em if 0.25 * em <= 250 else 250}
'''.strip()
effect_list.append(effect)

View File

@ -1,42 +1,119 @@
from pathlib import Path
from typing import Any, Tuple
from hoshino.typing import CQEvent, HoshinoBot
from nonebot.log import logger
from nonebot.matcher import Matcher
from nonebot import on_regex, on_command
from nonebot.params import CommandArg, RegexGroup
from nonebot.adapters.ntchat import MessageSegment
from nonebot.adapters.ntchat.message import Message
from ..base import sv, logger
from ..version import Genshin_version
from ..utils.draw_image_tools.send_image_tool import convert_img
from ..genshinuid_meta import register_menu
from ..utils.exception.handle_exception import handle_exception
get_primogems_data = on_command('版本规划', aliases={'原石预估'})
get_img_data = on_regex(r'^(查询)?(伤害乘区|血量表|抗性表|血量排行)$')
PRIMOGEMS_DATA_PATH = Path(__file__).parent / 'primogems_data'
IMG_PATH = Path(__file__).parent / 'img_data'
@sv.on_rex(r'^(版本规划|原石预估)(\S+)?$')
async def send_primogems_data(bot: HoshinoBot, ev: CQEvent):
args = ev['match'].groups()
@get_primogems_data.handle()
@handle_exception('版本规划')
@register_menu(
'版本原石规划',
'版本规划(版本号)',
'发送一张指定版本的原石规划图',
detail_des=(
'介绍:\n'
'发送一张指定版本的原石规划图\n'
' \n'
'指令:\n'
'- <ft color=(238,120,0)>版本规划</ft><ft color=(125,125,125)>(版本号)</ft>\n'
'- <ft color=(238,120,0)>原石预估</ft><ft color=(125,125,125)>(版本号)</ft>\n'
' \n'
'示例:\n'
'- <ft color=(238,120,0)>版本规划</ft>\n'
'- <ft color=(238,120,0)>版本规划3.0</ft>'
),
)
async def send_primogems_data(matcher: Matcher, args: Message = CommandArg()):
logger.info('开始执行[图片][版本规划]')
logger.info('[图片][版本规划]参数: {}'.format(args))
if args[1]:
path = PRIMOGEMS_DATA_PATH / f'{str(args[1])}.png'
if args:
path = PRIMOGEMS_DATA_PATH / f'{str(args)}.png'
if path.exists():
img = f'{str(args[1])}.png'
img = f'{str(args)}.png'
else:
return
await matcher.finish()
else:
img = f'{Genshin_version[:3]}.png'
primogems_img = PRIMOGEMS_DATA_PATH / img
logger.info('[图片][版本规划]访问图片: {}'.format(img))
primogems_img = await convert_img(primogems_img)
await bot.send(ev, primogems_img)
if primogems_img.exists():
logger.info('[图片][版本规划]访问图片: {}'.format(img))
with open(primogems_img, 'rb') as f:
await matcher.finish(MessageSegment.image(f.read()))
else:
await matcher.finish()
@sv.on_rex(r'^(查询)?(伤害乘区|血量表|抗性表|血量排行)$')
async def send_img_data(bot: HoshinoBot, ev: CQEvent):
args = ev['match'].groups()
@get_img_data.handle()
@handle_exception('杂图')
@register_menu(
'伤害乘区图',
'(查询)伤害乘区',
'发送一张理论伤害计算公式图',
detail_des=(
'介绍:\n'
'发送一张理论伤害计算公式图\n'
' \n'
'指令:\n'
'- <ft color=(125,125,125)>(查询)</ft><ft color=(238,120,0)>伤害乘区</ft>'
),
)
@register_menu(
'怪物血量表',
'(查询)血量表',
'发送一张原神怪物血量表图',
detail_des=(
'介绍:\n'
'发送一张原神怪物血量表图\n'
' \n'
'指令:\n'
'- <ft color=(125,125,125)>(查询)</ft><ft color=(238,120,0)>血量表</ft>'
),
)
@register_menu(
'怪物抗性表',
'(查询)抗性表',
'发送一张原神怪物抗性表图',
detail_des=(
'介绍:\n'
'发送一张原神怪物抗性表图\n'
' \n'
'指令:\n'
'- <ft color=(125,125,125)>(查询)</ft><ft color=(238,120,0)>抗性表</ft>'
),
)
@register_menu(
'怪物血量排行',
'(查询)血量排行',
'发送一张原神怪物血量排行图',
detail_des=(
'介绍:\n'
'发送一张原神怪物血量排行图\n'
' \n'
'指令:\n'
'- <ft color=(125,125,125)>(查询)</ft><ft color=(238,120,0)>血量排行</ft>'
),
)
async def send_img_data(
matcher: Matcher, args: Tuple[Any, ...] = RegexGroup()
):
logger.info('开始执行[图片][杂图]')
logger.info('[图片][杂图]参数: {}'.format(args))
img = IMG_PATH / f'{args[1]}.jpg'
if img.exists():
img = await convert_img(img)
await bot.send(ev, img)
else:
return
with open(img, 'rb') as f:
await matcher.finish(MessageSegment.image(f.read()))

View File

@ -1,24 +1,56 @@
from hoshino.typing import CQEvent, HoshinoBot
from nonebot import on_command
from nonebot.matcher import Matcher
from nonebot_plugin_apscheduler import scheduler
from nonebot.adapters.ntchat import MessageSegment
from ..base import sv
from ..config import priority
from ..genshinuid_meta import register_menu
from ..utils.nonebot2.rule import FullCommand
from .draw_event_img import get_event_img, get_all_event_img
from ..utils.draw_image_tools.send_image_tool import convert_img
from ..utils.exception.handle_exception import handle_exception
get_event = on_command('活动列表', priority=priority, rule=FullCommand())
get_gacha = on_command('卡池列表', priority=priority, rule=FullCommand())
@sv.scheduled_job('cron', hour='2')
@scheduler.scheduled_job('cron', hour='2')
async def draw_event():
await get_all_event_img()
@sv.on_fullmatch('活动列表')
async def send_events(bot: HoshinoBot, ev: CQEvent):
@get_event.handle()
@handle_exception('活动')
@register_menu(
'活动列表',
'活动列表',
'查询当前版本活动日程表',
detail_des=(
'介绍:\n'
'查询当前版本活动日程表\n'
' \n'
'指令:\n'
'- <ft color=(238,120,0)>活动列表</ft>'
),
)
async def send_events(matcher: Matcher):
img = await get_event_img('EVENT')
im = await convert_img(img)
await bot.send(ev, im)
await matcher.finish(MessageSegment.image(img))
@sv.on_fullmatch('卡池列表')
async def send_gachas(bot: HoshinoBot, ev: CQEvent):
@get_gacha.handle()
@handle_exception('活动')
@register_menu(
'卡池列表',
'卡池列表',
'查询当前版本卡池列表',
detail_des=(
'介绍:\n'
'查询当前版本卡池列表\n'
' \n'
'指令:\n'
'- <ft color=(238,120,0)>卡池列表</ft>'
),
)
async def send_gachas(matcher: Matcher):
img = await get_event_img('GACHA')
im = await convert_img(img)
await bot.send(ev, im)
await matcher.finish(MessageSegment.image(img))

Binary file not shown.

After

Width:  |  Height:  |  Size: 307 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 411 KiB

View File

@ -1,93 +1,169 @@
from nonebot import get_bot
from hoshino.typing import CQEvent, HoshinoBot, NoticeSession
import os
import asyncio
from nonebot.log import logger
from nonebot.rule import is_type
from nonebot.matcher import Matcher
from nonebot import get_bot, on_command, on_message
from nonebot.adapters.ntchat.permission import GROUP
from nonebot.adapters.ntchat import (
MessageSegment,
FileMessageEvent,
TextMessageEvent,
)
from ..base import sv, logger
from .get_gachalogs import save_gachalogs
from ..genshinuid_meta import register_menu
from ..utils.nonebot2.rule import FullCommand
from .draw_gachalogs import draw_gachalogs_img
from ..utils.message.error_reply import UID_HINT
from ..utils.db_operation.db_operation import select_db
from ..utils.draw_image_tools.send_image_tool import convert_img
from ..utils.exception.handle_exception import handle_exception
from .export_and_import import export_gachalogs, import_gachalogs
@sv.on_notice()
async def import_gacha_log_info(session: NoticeSession):
ev = session.event
if ev['notice_type'] != 'offline_file':
return
url = ev['file']['url']
name: str = ev['file']['name']
if not name.endswith('.json'):
return
qid = ev['user_id']
uid = await select_db(qid, mode='uid')
if not isinstance(uid, str) or '未找到绑定的UID' in uid:
await session.send(UID_HINT)
return
logger.info('开始执行[导入抽卡记录]')
im = await import_gachalogs(url, uid)
await session.send(im, at_sender=True)
get_gacha_log = on_command('刷新抽卡记录', aliases={'强制刷新抽卡记录'}, rule=FullCommand())
get_gacha_log_card = on_command('抽卡记录', rule=FullCommand())
import_gacha_log = on_message(block=False, rule=(is_type(FileMessageEvent)))
export_gacha_log = on_command('导出抽卡记录', rule=FullCommand(), permission=GROUP)
@sv.on_fullmatch('导出抽卡记录')
async def export_gacha_log_info(bot: HoshinoBot, ev: CQEvent):
@export_gacha_log.handle()
@handle_exception('导出抽卡记录')
@register_menu(
'导出抽卡记录',
'导出抽卡记录',
'导出符合UIGF规范的抽卡记录',
trigger_method='群聊指令',
detail_des=(
'介绍:\n'
'导出UIGF格式规范的json格式抽卡记录上传到群文件\n'
' \n'
'指令:\n'
'- <ft color=(238,120,0)>导出抽卡记录</ft>'
),
)
async def export_gacha_log_info(
event: TextMessageEvent,
matcher: Matcher,
):
logger.info('开始执行[导出抽卡记录]')
qid = int(ev.sender['user_id'])
gid = int(ev.group_id)
qid = event.from_wxid
gid = event.room_wxid
uid = await select_db(qid, mode='uid')
bot = get_bot()
if not isinstance(uid, str) or '未找到绑定的UID' in uid:
await bot.send(ev, UID_HINT)
return
await matcher.finish(UID_HINT)
raw_data = await export_gachalogs(uid)
if raw_data['retcode'] == 'ok':
await bot.call_action(
action='upload_group_file',
group_id=gid,
name=raw_data['name'],
file=raw_data['url'],
await bot.call_api(
'send_file',
to_wxid=gid,
file_path=raw_data['url'],
)
logger.info(f'[导出抽卡记录] UID{uid}成功!')
await bot.send(ev, '上传成功!')
await matcher.finish('上传成功!')
else:
logger.warning(f'[导出抽卡记录] UID{uid}失败!')
await bot.send(ev, '导出抽卡记录失败!')
await matcher.finish('导出抽卡记录失败!')
@sv.on_fullmatch('抽卡记录')
async def send_gacha_log_card_info(bot: HoshinoBot, ev: CQEvent):
@import_gacha_log.handle()
@handle_exception('导入抽卡记录')
@register_menu(
'导入抽卡记录',
'json格式文件',
'导入符合UIGF规范的抽卡记录',
trigger_method='私聊离线文件',
detail_des=(
'介绍:\n'
'导入UIGF格式规范的json格式抽卡记录到插件本地缓存\n'
' \n'
'触发方式:\n'
'- <ft color=(238,120,0)>私聊发送json格式的离线文件</ft>'
),
)
async def import_gacha_log_info(event: FileMessageEvent, matcher: Matcher):
await asyncio.sleep(2) # 等待下载文件,避免占用
# 检测文件是否存在并小于8MB
if (
os.path.exists(event.file)
and os.path.getsize(event.file) <= 8 * 1024 * 1024
and event.file_name.endswith(".json")
):
uid = await select_db(event.from_wxid, mode='uid')
if not isinstance(uid, str) or '未找到绑定的UID' in uid:
await matcher.finish(UID_HINT)
logger.info('开始执行[导入抽卡记录]')
im = await import_gachalogs(event.file, uid)
await matcher.finish(im)
else:
await matcher.finish()
@get_gacha_log_card.handle()
@handle_exception('抽卡记录')
@register_menu(
'查询抽卡记录',
'抽卡记录',
'查询你的原神抽卡记录',
detail_des=(
'介绍:\n'
'查询你的原神抽卡记录\n'
'需要<ft color=(238,120,0)>绑定Stoken</ft>\n'
' \n'
'指令:\n'
'<ft color=(238,120,0)>抽卡记录</ft>'
),
)
async def send_gacha_log_card_info(
event: TextMessageEvent,
matcher: Matcher,
):
logger.info('开始执行[抽卡记录]')
if ev.sender:
qid = int(ev.sender['user_id'])
else:
return
uid = await select_db(qid, mode='uid')
uid = await select_db(event.from_wxid, mode='uid')
if isinstance(uid, str):
im = await draw_gachalogs_img(uid, qid)
im = await draw_gachalogs_img(uid, event.from_wxid) # type: ignore
if isinstance(im, bytes):
im = await convert_img(im)
await bot.send(ev, im)
await matcher.finish(MessageSegment.image(im))
else:
await bot.send(ev, im)
await matcher.finish(im)
else:
await bot.send(ev, UID_HINT)
await matcher.finish(UID_HINT)
@sv.on_fullmatch(('刷新抽卡记录', '强制刷新抽卡记录'))
async def send_daily_info(bot: HoshinoBot, ev: CQEvent):
@get_gacha_log.handle()
@handle_exception('刷新抽卡记录')
@register_menu(
'刷新抽卡记录',
'刷新抽卡记录',
'刷新你的原神抽卡记录本地缓存',
detail_des=(
'介绍:\n'
'刷新你的原神抽卡记录本地缓存\n'
'需要<ft color=(238,120,0)>绑定Stoken</ft>\n'
' \n'
'指令:\n'
'- <ft color=(238,120,0)>刷新抽卡记录</ft>'
),
)
async def send_daily_info(
event: TextMessageEvent,
matcher: Matcher,
):
logger.info('开始执行[刷新抽卡记录]')
if ev.sender:
qid = int(ev.sender['user_id'])
else:
return
uid = await select_db(qid, mode='uid')
wxid_list = []
wxid_list.append(event.from_wxid)
uid = await select_db(event.from_wxid, mode='uid')
if isinstance(uid, str):
is_force = False
if ev.raw_message and str(ev.raw_message).startswith('强制'):
logger.info('[WARNING]本次为强制刷新')
if event.msg.startswith('强制'):
is_force = True
tip = '正在刷新抽卡记录,请耐心等待,不要重复发送命令。'
await matcher.send(
MessageSegment.room_at_msg(content=tip, at_list=wxid_list)
)
im = await save_gachalogs(uid, None, is_force)
await bot.send(ev, im)
await matcher.finish(im)
else:
await bot.send(ev, UID_HINT)
await matcher.finish(UID_HINT)

View File

@ -16,22 +16,30 @@ INT_TO_TYPE = {
async def import_gachalogs(history_url: str, uid: str) -> str:
history_data: dict = json.loads(get(history_url).text)
data_uid = history_data['info']['uid']
if data_uid != uid:
return f'该抽卡记录UID{data_uid}与你绑定UID{uid}不符合!'
raw_data = history_data['list']
result = {'新手祈愿': [], '常驻祈愿': [], '角色祈愿': [], '武器祈愿': []}
for item in raw_data:
item['uid'] = uid
item['item_id'] = ''
item['count'] = '1'
item['lang'] = 'zh-cn'
item['id'] = str(item['id'])
del item['uigf_gacha_type']
result[INT_TO_TYPE[item['gacha_type']]].append(item)
im = await save_gachalogs(uid, result)
return im
# 是否Json文件检测
try:
if not history_url.startswith(('http', 'https')):
with open(history_url, 'r', encoding='utf-8') as history_url_files:
history_data = json.load(history_url_files)
else:
history_data: dict = get(history_url).json()
data_uid = history_data['info']['uid']
if data_uid != uid:
return f'该抽卡记录UID{data_uid}与你绑定UID{uid}不符合!'
raw_data = history_data['list']
result = {'新手祈愿': [], '常驻祈愿': [], '角色祈愿': [], '武器祈愿': []}
for item in raw_data:
item['uid'] = uid
item['item_id'] = ''
item['count'] = '1'
item['lang'] = 'zh-cn'
item['id'] = str(item['id'])
del item['uigf_gacha_type']
result[INT_TO_TYPE[item['gacha_type']]].append(item)
im = await save_gachalogs(uid, result)
return im
except Exception:
return '导入失败请检查你的Json文件'
async def export_gachalogs(uid: str) -> dict:

View File

@ -1,49 +1,64 @@
import re
from hoshino.typing import CQEvent, HoshinoBot
from nonebot import on_command
from nonebot.log import logger
from nonebot.matcher import Matcher
from nonebot.params import CommandArg
from nonebot.adapters.ntchat.message import Message
from nonebot.adapters.ntchat import MessageSegment, TextMessageEvent
from ..base import sv, logger
from ..config import priority
from .draw_gcginfo import draw_gcg_info
from ..utils.message.error_reply import UID_HINT
from ..utils.db_operation.db_operation import select_db
from ..utils.draw_image_tools.send_image_tool import convert_img
from ..utils.message.error_reply import CK_HINT, UID_HINT
from ..utils.exception.handle_exception import handle_exception
get_gcg_info = on_command('七圣召唤', aliases={'七圣', '召唤'}, priority=priority)
@sv.on_prefix(('七圣召唤', '七圣', '召唤'))
async def send_gcg_pic(bot: HoshinoBot, ev: CQEvent):
if ev.message:
raw_mes = ev.message.extract_plain_text().replace(' ', '')
# 群聊内 每月统计 功能
@get_gcg_info.handle()
@handle_exception('七圣召唤', '获取/发送七圣召唤失败', '@未找到绑定信息\n' + CK_HINT)
async def send_gcg_pic(
event: TextMessageEvent,
matcher: Matcher,
args: Message = CommandArg(),
):
raw_mes = args.extract_plain_text().strip().replace(' ', '')
if "@" in raw_mes:
name = ''.join(re.findall('^[\u4e00-\u9fa5]+', raw_mes.split("@")[0]))
if name:
return
else:
return
at = re.search(r'\[CQ:at,qq=(\d*)]', str(ev.message))
name = ''.join(re.findall('^[\u4e00-\u9fa5]+', raw_mes))
if name:
return
logger.info('开始执行[七圣召唤]')
if at:
qid = int(at.group(1))
else:
qid = int(ev.sender['user_id'])
logger.info('[七圣召唤]QQ: {}'.format(qid))
qid = event.from_wxid
if event.at_user_list:
for user in event.at_user_list:
user = user.strip()
if user != "":
qid = user
logger.info('[七圣召唤]WXID: {}'.format(qid))
# 获取uid
uid = re.findall(r'\d+', raw_mes)
uid = re.findall(r'\d+', raw_mes.split("@")[0])
if uid:
uid = uid[0]
else:
uid = await select_db(qid, mode='uid')
uid = str(uid)
logger.info('[七圣召唤]uid: {}'.format(uid))
if '未找到绑定的UID' in uid:
await bot.send(ev, UID_HINT)
await matcher.finish(UID_HINT)
im = await draw_gcg_info(uid)
if isinstance(im, str):
await bot.send(ev, im)
await matcher.finish(im)
elif isinstance(im, bytes):
im = await convert_img(im)
await bot.send(ev, im)
await matcher.finish(MessageSegment.image(im))
else:
await bot.send(ev, '发生了未知错误,请联系管理员检查后台输出!')
await matcher.finish('发生了未知错误,请联系管理员检查后台输出!')

View File

@ -1,117 +1,134 @@
import asyncio
import threading
from typing import List
from pathlib import Path
from typing import Any, List, Tuple
from hoshino.typing import CQEvent, HoshinoBot
from nonebot.log import logger
from nonebot.matcher import Matcher
from nonebot import on_regex, on_command
from nonebot.params import CommandArg, RegexGroup
from nonebot.adapters.ntchat import MessageSegment
from nonebot.adapters.ntchat.message import Message
from ..base import sv, logger
from .get_card import get_gs_card
from .get_guide import get_gs_guide
from ..version import Genshin_version
from ..genshinuid_meta import register_menu
from .get_abyss_data import get_review, generate_data
from ..utils.alias.alias_to_char_name import alias_to_char_name
from ..utils.draw_image_tools.send_image_tool import convert_img
from ..utils.exception.handle_exception import handle_exception
get_guide_pic = on_regex('([\u4e00-\u9fa5]+)(推荐|攻略)')
get_bluekun_pic = on_command('参考面板')
get_card = on_command('原牌')
get_abyss = on_command('版本深渊')
IMG_PATH = Path(__file__).parent / 'img'
@sv.on_rex('([\u4e00-\u9fa5]+)(推荐|攻略)')
async def send_guide_pic(bot: HoshinoBot, ev: CQEvent):
if ev.message:
name = name = str(ev['match'].group(1))
else:
@get_guide_pic.handle()
@handle_exception('建议')
@register_menu(
'角色攻略',
'xx攻略',
'发送一张对应角色的西风驿站攻略图',
detail_des=(
'介绍:\n'
'发送一张对应角色的米游社西风驿站攻略图\n'
'支持部分角色别名\n'
' \n'
'指令:\n'
'- <ft color=(0,148,200)>[角色名]</ft>'
'<ft color=(238,120,0)>{推荐|攻略}</ft>\n'
' \n'
'示例:\n'
'- <ft color=(238,120,0)>钟离推荐</ft>\n'
'- <ft color=(238,120,0)>公子攻略</ft>'
),
)
async def send_guide_pic(
matcher: Matcher, args: Tuple[Any, ...] = RegexGroup()
):
if not args:
return
if name == '':
return
name = str(args[0])
im = await get_gs_guide(name)
if im:
logger.info('获得{}攻略成功!'.format(name))
im = await convert_img(im)
await bot.send(ev, im)
await matcher.finish(MessageSegment.image(im))
else:
logger.warning('未找到{}攻略图片'.format(name))
@sv.on_prefix('参考面板')
async def send_bluekun_pic(bot: HoshinoBot, ev: CQEvent):
if ev.message:
message = ev.message.extract_plain_text().replace(' ', '')
@get_bluekun_pic.handle()
@handle_exception('参考面板')
@register_menu(
'参考面板',
'参考面板[角色名/元素名]',
'发送一张对应角色/元素的参考面板图',
detail_des=(
'介绍:\n'
'发送一张对应角色/元素的参考面板图\n'
'支持部分角色别名\n'
' \n'
'指令:\n'
'- <ft color=(238,120,0)>参考面板</ft>'
'<ft color=(0,148,200)>[角色名/元素名]</ft>\n'
' \n'
'示例:\n'
'- <ft color=(238,120,0)>参考面板火</ft>\n'
'- <ft color=(238,120,0)>参考面板公子</ft>'
),
)
async def send_bluekun_pic(matcher: Matcher, args: Message = CommandArg()):
if str(args[0]) in ['', '', '', '', '', '', '']:
name = str(args[0])
else:
return
if message == '':
return
if str(message) in ['', '', '', '', '', '', '']:
name = str(message)
else:
name = await alias_to_char_name(str(message))
name = await alias_to_char_name(str(args[0]))
img = IMG_PATH / '{}.jpg'.format(name)
if img.exists():
img = await convert_img(img)
with open(img, 'rb') as f:
im = MessageSegment.image(f.read())
logger.info('获得{}参考面板图片成功!'.format(name))
await bot.send(ev, img)
await matcher.finish(im)
else:
logger.warning('未找到{}参考面板图片'.format(name))
@sv.on_prefix('原牌')
async def send_gscard_pic(bot: HoshinoBot, ev: CQEvent):
if ev.message:
name = ev.message.extract_plain_text().replace(' ', '')
else:
@get_card.handle()
@handle_exception('原牌')
async def send_gscard_pic(matcher: Matcher, args: Message = CommandArg()):
if not args:
return
if name == '':
return
name = str(args[0])
im = await get_gs_card(name)
if im:
logger.info('获得{}原牌成功!'.format(name))
im = await convert_img(im)
await bot.send(ev, im)
await matcher.finish(MessageSegment.image(im))
else:
logger.warning('未找到{}原牌图片'.format(name))
@sv.on_prefix('版本深渊')
async def send_abyss_review(bot: HoshinoBot, ev: CQEvent):
if ev.message:
version = ev.message.extract_plain_text().replace(' ', '')
else:
return
if version == '':
@get_abyss.handle()
@handle_exception('版本深渊')
async def send_abyss_review(
matcher: Matcher,
args: Message = CommandArg(),
):
if not args:
version = Genshin_version[:-2]
im = await get_review(version)
if isinstance(im, List):
mes = []
for msg in im:
mes.append(
{
'type': 'node',
'data': {
'name': '小仙',
'uin': '3399214199',
'content': msg,
},
}
)
await bot.send_group_forward_msg(group_id=ev.group_id, messages=mes)
elif isinstance(im, str):
await bot.send(ev, im)
elif isinstance(im, bytes):
im = await convert_img(im)
await bot.send(ev, im)
else:
await bot.send(ev, '发生了未知错误,请联系管理员检查后台输出!')
version = str(args[0])
im = await get_review(version)
if isinstance(im, List):
im = '\n'.join(im)
await matcher.finish(im)
elif isinstance(im, str):
await matcher.finish(im)
elif isinstance(im, bytes):
await matcher.finish(MessageSegment.image(im))
else:
await matcher.finish('发生了未知错误,请联系管理员检查后台输出!')
threading.Thread(

View File

@ -2,18 +2,37 @@ import asyncio
import threading
from pathlib import Path
from ..base import sv, logger
from nonebot import on_command
from nonebot.log import logger
from nonebot.matcher import Matcher
from nonebot.adapters.ntchat import MessageSegment
from .draw_help_card import draw_help_img
from ..utils.draw_image_tools.send_image_tool import convert_img
from ..genshinuid_meta import register_menu
from ..utils.exception.handle_exception import handle_exception
get_help = on_command('gs帮助')
HELP_IMG = Path(__file__).parent / 'help.png'
@sv.on_fullmatch(('gs帮助', 'genshin帮助', 'ys帮助', '原神帮助'))
async def send_guide_pic(bot, ev):
img = await convert_img(HELP_IMG)
@get_help.handle()
@handle_exception('建议')
@register_menu(
'插件帮助',
'gs帮助',
'查看插件功能帮助图',
detail_des=(
'介绍:\n' '查看插件功能帮助图\n' ' \n' '指令:\n' '- <ft color=(238,120,0)>gs帮助</ft>'
),
)
async def send_guide_pic(matcher: Matcher):
logger.info('获得gs帮助图片成功')
await bot.send(ev, img)
if HELP_IMG.exists():
with open(HELP_IMG, 'rb') as f:
await matcher.finish(MessageSegment.image(f.read()))
else:
await matcher.finish('帮助图不存在!')
threading.Thread(

View File

@ -1,8 +1,24 @@
from hoshino.typing import CQEvent, HoshinoBot
from typing import Any, Dict
from ..base import sv, logger
from nonebot.log import logger
from nonebot.matcher import Matcher
from nonebot.params import RegexDict
from nonebot import on_regex, on_command
from nonebot.permission import SUPERUSER
from nonebot.adapters.ntchat import Bot, MessageSegment, TextMessageEvent
from ..config import priority
from ..genshinuid_meta import register_menu
from ..utils.nonebot2.rule import FullCommand
from .draw_genshinmap_card import MAP_DATA, draw_genshin_map
from ..utils.draw_image_tools.send_image_tool import convert_img
from ..utils.exception.handle_exception import handle_exception
change_map = on_command('切换地图', block=True, rule=FullCommand())
find_map = on_regex(
r'^(?P<name>.*)(在哪里|在哪|哪里有|哪儿有|哪有|在哪儿)$', priority=priority
)
find_map2 = on_regex(r'^(哪里有|哪儿有|哪有)(?P<name>.*)$', priority=priority)
MAP_ID_LIST = [
'2', # 提瓦特
@ -18,13 +34,31 @@ MAP_CHN_NAME = {
}
@sv.on_fullmatch('切换地图')
async def send_change_map_msg(bot: HoshinoBot, ev: CQEvent):
if ev.sender:
qid = int(ev.sender['user_id'])
else:
return
if qid not in bot.config.SUPERUSERS:
@change_map.handle()
@handle_exception('切换地图')
@register_menu(
'切换地图',
'切换地图',
'切换查找资源点功能所使用的地图',
detail_des=(
'介绍:\n'
'切换查找资源点功能所使用的地图\n'
'指令按列表顺序轮流切换地图\n'
'目前可用地图:'
+ "".join(
[f"<ft color=(238,120,0)>{x}</ft>" for x in MAP_CHN_NAME.values()]
)
+ ' \n\n'
'指令:\n'
'- <ft color=(238,120,0)>切换地图</ft>'
),
)
async def send_change_map_msg(
bot: Bot,
event: TextMessageEvent,
matcher: Matcher,
):
if not await SUPERUSER(bot, event):
return
logger.info('[切换地图]正在执行...')
MAP_ID_LIST.append(MAP_ID_LIST[0])
@ -32,44 +66,66 @@ async def send_change_map_msg(bot: HoshinoBot, ev: CQEvent):
current = MAP_ID_LIST[0]
chn = MAP_CHN_NAME.get(current)
logger.info(f'[切换地图]当前地图为{chn}')
await bot.send(ev, f'切换到{chn}地图')
await matcher.finish(f'切换到{chn}地图')
@sv.on_rex(r'^(?P<name>.*)(在哪里|在哪|哪里有|哪儿有|哪有|在哪儿)$')
@sv.on_rex(r'^(哪里有|哪儿有|哪有)(?P<name>.*)$')
async def send_find_map_msg(bot: HoshinoBot, ev: CQEvent):
@find_map.handle()
@find_map2.handle()
@handle_exception('查找资源点')
@register_menu(
'查找资源点',
'xx在哪',
'查找指定资源在地图上的位置',
detail_des=(
'介绍:\n'
'在米游社大地图上查询某资源的位置\n'
'使用 <ft color=(238,120,0)>切换地图</ft> 指令来切换目标地图'
' \n'
'指令:\n'
'- <ft color=(0,148,200)>[资源名称]</ft>'
'<ft color=(238,120,0)>{在哪里|在哪|哪里有|哪儿有|哪有|在哪儿}</ft>\n'
'- <ft color=(238,120,0)>{哪里有|哪儿有|哪有}</ft>'
'<ft color=(0,148,200)>[资源名称]</ft>\n'
' \n'
'示例:\n'
'- <ft color=(238,120,0)>甜甜花在哪</ft>\n'
'- <ft color=(238,120,0)>哪有清心</ft>'
),
)
async def send_find_map_msg(
matcher: Matcher, args: Dict[str, Any] = RegexDict()
):
map_id = MAP_ID_LIST[0]
map_name = MAP_CHN_NAME[map_id]
args = ev['match'].groupdict().get('name')
logger.info(f'[查找资源点]正在执行...当前地图为{map_name}')
logger.info('[查找资源点]参数: {}'.format(args))
if not args:
if not (args and (name := args.get('name'))):
return
im = ''
if not MAP_DATA.exists():
MAP_DATA.mkdir()
resource_temp_path = MAP_DATA / f'{map_name}_{args}.jpg'
resource_temp_path = MAP_DATA / f'{map_name}_{name}.jpg'
if resource_temp_path.exists():
logger.info(f'本地已有{map_name}_{args}的资源点,直接发送...')
resource_temp = await convert_img(resource_temp_path)
await bot.send(ev, resource_temp)
logger.info(f'本地已有{map_name}_{name}的资源点,直接发送...')
with open(resource_temp_path, 'rb') as f:
await matcher.finish(MessageSegment.image(f.read()))
else:
# 放弃安慰剂回复
'''
await bot.send(
ev,
f'正在查找{args},可能需要比较久的时间...\n当前地图:{map_name}',
await matcher.send(
(
f'正在查找{name},可能需要比较久的时间...\n'
f'当前地图:{MAP_CHN_NAME.get(MAP_ID_LIST[0])}'
)
)
'''
logger.info('本地未缓存,正在渲染...')
im = await draw_genshin_map(args, map_id, map_name)
if isinstance(im, str):
return
# await bot.send(ev, im)
elif isinstance(im, bytes):
im = await convert_img(im)
await bot.send(ev, im)
im = await draw_genshin_map(name, map_id, map_name)
if isinstance(im, str):
await matcher.finish()
elif isinstance(im, bytes):
await matcher.finish(MessageSegment.image(im))
else:
await matcher.finish('查找失败!')

View File

@ -0,0 +1,54 @@
from typing import Optional
from nonebot.plugin import PluginMetadata
sub_menus = []
__plugin_meta__ = PluginMetadata(
name='GenshinUID',
description='基于NoneBot2的原神Uid查询/原神Wiki/米游社签到/树脂提醒插件',
usage=(
'发送 <ft color=(238,120,0)>gs帮助</ft> 可以获取帮助列表,也可以参考下面的表格\n'
'可以使用 <ft color=(238,120,0)>菜单 gsuid </ft>'
'<ft color=(0,148,200)>[序号]</ft> '
'指令获取某功能详细介绍\n'
' \n'
'菜单描述中的指令:\n'
'<ft color=(0,148,200)>[中括号及其中的内容]</ft>'
'或<ft color=(0,148,200)>用“xx”代表的内容</ft> '
'为<ft color=(238,120,0)>必选</ft>的参数,'
'请将它们替换为适当的值;\n'
'<ft color=(125,125,125)>(小括号及其中的内容)</ft> '
'为<ft color=(238,120,0)>可选</ft>参数,'
'可以省略;\n'
'<ft color=(238,120,0)>{大括号及其中的内容}</ft> '
'为<ft color=(238,120,0)>选择其一</ft>参数,'
'请将它们替换为用 <ft color=(238,120,0)>|</ft> 分割后括号中内容的其中一个值'
),
extra={'menu_data': sub_menus, 'menu_template': 'default'},
)
def register_menu_func(
func: str,
trigger_condition: str,
brief_des: str,
trigger_method: str = '指令',
detail_des: Optional[str] = None,
):
sub_menus.append(
{
'func': func,
'trigger_method': trigger_method,
'trigger_condition': trigger_condition,
'brief_des': brief_des,
'detail_des': detail_des or brief_des,
}
)
def register_menu(*args, **kwargs):
def decorator(f):
register_menu_func(*args, **kwargs)
return f
return decorator

View File

@ -1,35 +1,81 @@
import random
import asyncio
from nonebot import get_bot
from hoshino.typing import CQEvent, HoshinoBot
from nonebot.log import logger
from nonebot.matcher import Matcher
from nonebot import get_bot, on_command
from nonebot.permission import SUPERUSER
from nonebot_plugin_apscheduler import scheduler
from nonebot.adapters.ntchat import Bot, MessageEvent, MessageSegment
from ..base import sv, logger
from ..config import SUPERUSERS, priority
from ..genshinuid_meta import register_menu
from ..utils.nonebot2.rule import FullCommand
from ..utils.db_operation.db_operation import config_check
from ..utils.exception.handle_exception import handle_exception
from .daily_mihoyo_bbs_coin import mihoyo_coin, all_daily_mihoyo_bbs_coin
bbscoin_scheduler = scheduler
get_mihoyo_coin = on_command('开始获取米游币', priority=priority, rule=FullCommand())
all_bbscoin_recheck = on_command(
'全部重获取', priority=priority, rule=FullCommand()
)
# 获取米游币
@sv.on_fullmatch('开始获取米游币')
async def send_mihoyo_coin(bot: HoshinoBot, ev: CQEvent):
await bot.send(ev, '开始操作……', at_sender=True)
qid = int(ev.sender['user_id']) # type: ignore
@get_mihoyo_coin.handle()
@handle_exception('获取米游币')
@register_menu(
'手动获取米游币',
'开始获取米游币',
'手动触发米游社米游币任务',
detail_des=(
'介绍:\n'
'手动触发米游社获取米游币的任务\n'
' \n'
'指令:\n'
'- <ft color=(238,120,0)>开始获取米游币</ft>'
),
)
async def send_mihoyo_coin(event: MessageEvent, matcher: Matcher):
wxid_list = []
wxid_list.append(event.from_wxid)
await matcher.send(
MessageSegment.room_at_msg(content='{$@}开始操作……', at_list=wxid_list)
)
qid = event.from_wxid
im = await mihoyo_coin(qid)
await bot.send(ev, im, at_sender=True)
await matcher.finish(
MessageSegment.room_at_msg(content="{$@}" + f'{im}', at_list=wxid_list)
)
@sv.on_fullmatch('全部重获取')
async def bbs_recheck(bot: HoshinoBot, ev: CQEvent):
qid = int(ev.sender['user_id']) # type: ignore
if qid not in bot.config.SUPERUSERS:
@all_bbscoin_recheck.handle()
@handle_exception('米游币全部重获取')
@register_menu(
'重新获取米游币',
'全部重获取',
'重新运行所有自动获取米游币的任务',
detail_des=(
'介绍:\n'
'重新运行所有自动获取米游币的任务\n'
' \n'
'指令:\n'
'- <ft color=(238,120,0)>全部重获取</ft>'
),
)
async def bbs_recheck(bot: Bot, event: MessageEvent, matcher: Matcher):
if await SUPERUSER(bot, event):
await matcher.send('已开始执行!可能需要较久时间!')
await send_daily_mihoyo_bbs_sign()
await matcher.finish('执行完成!')
else:
return
await bot.send(ev, '已开始执行!可能需要较久时间!')
await send_daily_mihoyo_bbs_sign()
await bot.send(ev, '执行完成!')
# 每日一点十六分进行米游币获取
@sv.scheduled_job('cron', hour='1', minute='16')
@bbscoin_scheduler.scheduled_job('cron', hour='1', minute='16')
async def sign_at_night():
if await config_check('SchedMhyBBSCoin'):
await send_daily_mihoyo_bbs_sign()
@ -40,12 +86,12 @@ async def send_daily_mihoyo_bbs_sign():
im, im_private = await all_daily_mihoyo_bbs_coin()
if im_private:
for user_id in im_private:
await bot.send_private_msg(
user_id=user_id, message=im_private[user_id]
await bot.call_api(
'send_text', to_wxid=user_id, content=im_private[user_id]
)
await asyncio.sleep(5 + random.randint(1, 3))
if await config_check('PrivateReport'):
for qid in bot.config.SUPERUSERS:
await bot.send_private_msg(user_id=qid, message=im)
for qid in SUPERUSERS:
await bot.call_api(api='send_text', to_wxid=qid, content=im)
await asyncio.sleep(5 + random.randint(1, 3))
logger.info('米游币获取已结束。')

View File

@ -1,39 +1,105 @@
import asyncio
from typing import Any, Tuple
from hoshino.typing import CQEvent, HoshinoBot
from nonebot.log import logger
from nonebot.matcher import Matcher
from nonebot.params import RegexGroup
from nonebot import on_regex, on_command
from nonebot.adapters.ntchat import (
Bot,
MessageEvent,
MessageSegment,
TextMessageEvent,
)
from ..base import sv, logger
from ..config import priority
from .get_lots_data import get_lots_msg
from .get_meme_card import get_meme_img
from .get_mys_data import get_region_task, get_task_detail
from ..utils.draw_image_tools.send_image_tool import convert_img
from .get_mys_data import get_task_detail
from ..genshinuid_meta import register_menu
from ..utils.nonebot2.rule import FullCommand
from ..utils.exception.handle_exception import handle_exception
get_task_adv = on_regex(
r'^(原神任务|任务|任务详情|任务攻略)( )?([\u4e00-\u9fa5]+)( )?$', priority=priority
)
get_meme = on_command('抽表情', priority=priority, rule=FullCommand())
get_lots = on_command('御神签', priority=priority, rule=FullCommand())
@sv.on_rex(r'^(原神任务|任务|任务详情|任务攻略)( )?([\u4e00-\u9fa5]+)( )?$')
async def send_task_adv(bot: HoshinoBot, ev: CQEvent):
args = ev['match'].groups()
@get_task_adv.handle()
@handle_exception('任务攻略')
@register_menu(
'任务攻略',
'任务xx',
'查询某任务的攻略',
detail_des=(
'介绍:\n'
'查询指定任务或指定地区的攻略\n'
' \n'
'指令:\n'
'- <ft color=(238,120,0)>{原神任务|任务|任务详情|任务攻略}</ft>'
'<ft color=(238,120,0)>{</ft>'
'<ft color=(0,148,200)>[任务名]</ft>'
'<ft color=(238,120,0)>|须弥|层岩|海岛}</ft>\n'
' \n'
'示例:\n'
'- <ft color=(238,120,0)>任务神樱大祓</ft>\n'
'- <ft color=(238,120,0)>任务须弥</ft>'
),
)
async def send_task_adv(
bot: Bot,
event: TextMessageEvent,
matcher: Matcher,
args: Tuple[Any, ...] = RegexGroup(),
):
'''
if str(args[2]) in ['须弥', '层岩', '海岛']:
im = await get_region_task(str(args[2]))
for i in im:
await bot.send_group_forward_msg(group_id=ev.group_id, messages=i)
await bot.call_api(
'send_group_forward_msg', group_id=event.group_id, messages=i
)
await asyncio.sleep(1)
return
await matcher.finish()
else:
im = await get_task_detail(str(args[2]))
await bot.send(ev, im)
'''
im = await get_task_detail(str(args[2]))
await matcher.finish(im)
@sv.on_fullmatch('抽表情')
async def send_meme_card(bot: HoshinoBot, ev: CQEvent):
@get_meme.handle()
@handle_exception('抽表情')
@register_menu(
'抽表情',
'抽表情',
'随机发送一张札记角色表情',
detail_des=(
'介绍:\n'
'随机发送一张札记角色表情\n'
' \n'
'指令:\n'
'- <ft color=(238,120,0)>抽表情</ft>'
),
)
async def send_meme_card(matcher: Matcher):
logger.info('开始执行[抽表情]')
img = await get_meme_img()
img = await convert_img(img)
await bot.send(ev, img)
await matcher.finish(MessageSegment.image(img))
@sv.on_fullmatch('御神签')
async def send_lots_data(bot: HoshinoBot, ev: CQEvent):
qid = int(ev.sender['user_id'])
@get_lots.handle()
@handle_exception('御神签')
@register_menu(
'御神签',
'御神签',
'鸣神大社御神签',
detail_des=(
'介绍:\n' '抽一签鸣神大社御神签\n' ' \n' '指令:\n' '- <ft color=(238,120,0)>御神签</ft>'
),
)
async def send_lots_data(matcher: Matcher, event: MessageEvent):
qid = event.from_wxid
logger.info('开始执行[御神签]')
im = await get_lots_msg(qid)
await bot.send(ev, im)
await matcher.finish(im)

View File

@ -1,48 +1,96 @@
import re
from hoshino.typing import CQEvent, HoshinoBot
from nonebot import on_command
from nonebot.log import logger
from nonebot.matcher import Matcher
from nonebot.adapters.ntchat import MessageEvent, MessageSegment
from .note_text import award
from ..base import sv, logger
from ..config import priority
from .draw_note_card import draw_note_img
from ..utils.message.error_reply import UID_HINT
from ..genshinuid_meta import register_menu
from ..utils.nonebot2.rule import FullCommand
from ..utils.db_operation.db_operation import select_db
from ..utils.draw_image_tools.send_image_tool import convert_img
from ..utils.message.error_reply import CK_HINT, UID_HINT
from ..utils.exception.handle_exception import handle_exception
monthly_data = on_command('每月统计', priority=priority, rule=FullCommand())
get_genshin_info = on_command(
'当前信息', aliases={'zj', '札记', '原石札记'}, priority=priority, rule=FullCommand()
)
# 群聊内 每月统计 功能
@sv.on_fullmatch('每月统计')
async def send_monthly_data(bot: HoshinoBot, ev: CQEvent):
qid = int(ev.sender['user_id']) # type: ignore
@monthly_data.handle()
@handle_exception('每月统计', '获取/发送每月统计失败', '@未找到绑定信息\n' + CK_HINT)
@register_menu(
'文字札记',
'每月统计',
'文字形式米游社札记',
detail_des=(
'介绍:\n' '文字形式米游社札记\n' ' \n' '指令:\n' '- <ft color=(238,120,0)>每月统计</ft>'
),
)
async def send_monthly_data(
event: MessageEvent,
matcher: Matcher,
):
qid = event.from_wxid
wxid_list = []
wxid_list.append(event.from_wxid)
uid = await select_db(qid, mode='uid')
if isinstance(uid, str):
if '未找到绑定的UID' in uid:
await bot.send(ev, UID_HINT)
await matcher.finish(
MessageSegment.room_at_msg(
content='{$@}' + UID_HINT, at_list=wxid_list
)
)
else:
await bot.send(ev, '发生未知错误...')
await matcher.finish('发生未知错误...')
im = await award(uid)
await bot.send(ev, im, at_sender=True)
await matcher.finish(
MessageSegment.room_at_msg(content="{$@}" + f'{im}', at_list=wxid_list)
)
@sv.on_fullmatch(('当前信息', 'zj', '原石札记', '札记'))
async def send_monthly_pic(bot: HoshinoBot, ev: CQEvent):
logger.info('开始执行[每日信息]')
at = re.search(r'\[CQ:at,qq=(\d*)]', str(ev.message))
if at:
qid = int(at.group(1))
else:
if ev.sender:
qid = int(ev.sender['user_id'])
else:
return
logger.info('[每日信息]QQ号: {}'.format(qid))
# 群聊内 每月统计 功能
@get_genshin_info.handle()
@handle_exception('每月统计', '获取/发送每月统计失败', '@未找到绑定信息\n' + CK_HINT)
@register_menu(
'图片札记',
'当前信息',
'图片形式米游社札记',
detail_des=(
'介绍:\n'
'图片形式米游社札记\n'
' \n'
'指令:\n'
'- <ft color=(238,120,0)>当前信息</ft>\n'
'- <ft color=(125,125,125)>(原石)</ft><ft color=(238,120,0)>札记</ft>\n'
'- <ft color=(238,120,0)>zj</ft>'
),
)
async def send_monthly_pic(
event: MessageEvent,
matcher: Matcher,
):
qid = event.from_wxid
wxid_list = []
wxid_list.append(event.from_wxid)
uid = await select_db(qid, mode='uid')
logger.info('[每日信息]UID: {}'.format(uid))
im = await draw_note_img(str(uid))
if isinstance(im, str):
await bot.send(ev, im)
elif isinstance(im, bytes):
im = await convert_img(im)
await bot.send(ev, im)
if isinstance(uid, str):
if '未找到绑定的UID' in uid:
await matcher.finish(
MessageSegment.room_at_msg(
content='{$@}' + UID_HINT, at_list=wxid_list
)
)
else:
await bot.send(ev, '发生未知错误,请联系管理员检查后台输出!')
await matcher.finish('发生未知错误...')
logger.info(f'[原石札记] 开始绘制,UID: {uid}')
im = await draw_note_img(uid)
if isinstance(im, str):
await matcher.finish(im)
elif isinstance(im, bytes):
await matcher.finish(MessageSegment.image(im))
else:
await matcher.finish('发生了未知错误,请联系管理员检查后台输出!')

View File

@ -1,118 +1,134 @@
import re
import asyncio
from nonebot import get_bot
from hoshino.typing import CQEvent, HoshinoBot
from nonebot.log import logger
from nonebot.matcher import Matcher
from nonebot import get_bot, on_command
from nonebot_plugin_apscheduler import scheduler
from nonebot.adapters.ntchat import MessageSegment, TextMessageEvent
from ..base import sv, logger
from .notice import get_notice_list
from .resin_text import get_resin_text
from .draw_resin_card import get_resin_img
from ..genshinuid_meta import register_menu
from ..utils.nonebot2.rule import FullCommand
from ..utils.message.error_reply import UID_HINT
from ..utils.db_operation.db_operation import select_db
from ..utils.draw_image_tools.send_image_tool import convert_img
from ..utils.exception.handle_exception import handle_exception
notice_scheduler = scheduler
get_resin_info = on_command(
'每日',
aliases={'mr', '状态', '实时便笺', '便笺', '便签'},
block=True,
rule=FullCommand(),
)
get_daily_info = on_command('当前状态', rule=FullCommand())
@sv.on_fullmatch('当前状态')
async def send_daily_info(bot: HoshinoBot, ev: CQEvent):
@get_daily_info.handle()
@handle_exception('每日信息文字版')
@register_menu(
'文字实时便笺',
'当前信息',
'米游社实时便笺文字版',
detail_des=(
'介绍:\n'
'米游社实时便笺文字版\n'
' \n'
'指令:\n'
'- <ft color=(238,120,0)>当前状态</ft>'
),
)
async def send_daily_info(
event: TextMessageEvent,
matcher: Matcher,
):
logger.info('开始执行[每日信息文字版]')
at = re.search(r'\[CQ:at,qq=(\d*)]', str(ev.message))
if ev.sender:
qid = ev.sender['user_id']
else:
return
if at:
qid = at.group(1)
qid = event.from_wxid
if event.at_user_list:
for user in event.at_user_list:
user = user.strip()
if user != "":
qid = user
logger.info('[每日信息文字版]QQ号: {}'.format(qid))
uid: str = await select_db(qid, mode='uid') # type: ignore
logger.info('[每日信息文字版]UID: {}'.format(uid))
if '未找到绑定的UID' in uid:
await bot.send(ev, UID_HINT)
if not uid:
await matcher.finish(UID_HINT)
im = await get_resin_text(uid)
await bot.send(ev, im)
await matcher.finish(im)
@sv.scheduled_job('cron', minute='*/30')
@notice_scheduler.scheduled_job('cron', minute='*/30')
async def notice_job():
bot = get_bot()
result = await get_notice_list()
logger.info('[推送检查]完成!等待消息推送中...')
# 执行私聊推送
bot_ids = bot._wsr_api_clients.keys()
for qid in result[0]:
send_success = False
for sid in bot_ids:
try:
await bot.send_private_msg(
self_id=sid,
user_id=qid,
message=result[0][qid],
)
send_success = True
break
except Exception:
logger.info(f'[推送检查(轮推)] BOT {sid} 没有 {qid} 好友,已跳过')
if not send_success:
try:
await bot.call_api(
api='send_private_msg',
user_id=qid,
message=result[0][qid],
)
except Exception:
logger.warning(f'[推送检查] QQ {qid} 私聊推送失败!')
await asyncio.sleep(0.5)
logger.info('[推送检查] 私聊推送完成')
logger.info('[推送检查]私聊推送完成')
# 执行群聊推送
for group_id in result[1]:
send_success = False
for sid in bot_ids:
try:
await bot.send_group_msg(
self_id=sid,
group_id=group_id,
message=result[1][group_id],
)
send_success = True
break
except Exception:
logger.info(f'[推送检查(轮推)] BOT {sid} 没有 {group_id} 群,已跳过')
if not send_success:
logger.warning(f'[推送检查] 群 {group_id} 推送失败!')
try:
await bot.call_api(
api='send_group_msg',
group_id=group_id,
message=result[1][group_id],
)
except Exception:
logger.warning(f'[推送检查] 群 {group_id} 群聊推送失败!')
await asyncio.sleep(0.5)
logger.info('[推送检查] 群聊推送完成')
logger.info('[推送检查]群聊推送完成')
@sv.on_fullmatch(('执行推送检查任务'))
async def manual_notice_job(bot: HoshinoBot, ev: CQEvent):
if ev.sender:
qid = int(ev.sender['user_id'])
else:
return
if qid not in bot.config.SUPERUSERS:
return
await notice_job()
await bot.send(ev, '推送检查任务已执行')
@sv.on_fullmatch(('每日', 'mr', '实时便笺', '便笺', '便签'))
async def send_daily_info_pic(bot: HoshinoBot, ev: CQEvent):
@get_resin_info.handle()
@handle_exception('每日信息')
@register_menu(
'图片实时便笺',
'每日',
'图片形式米游社实时便笺',
detail_des=(
'介绍:\n'
'图片形式米游社实时便笺\n'
' \n'
'指令:\n'
'- <ft color=(238,120,0)>每日</ft>\n'
'- <ft color=(238,120,0)>mr</ft>\n'
'- <ft color=(238,120,0)>状态</ft>\n'
'- <ft color=(125,125,125)>(实时)</ft><ft color=(238,120,0)>便笺</ft>\n'
'- <ft color=(238,120,0)>便签</ft>'
),
)
async def send_uid_info(
event: TextMessageEvent,
matcher: Matcher,
):
logger.info('开始执行[每日信息]')
at = re.search(r'\[CQ:at,qq=(\d*)]', str(ev.message))
if at:
qid = int(at.group(1))
else:
if ev.sender:
qid = int(ev.sender['user_id'])
else:
return
qid = event.from_wxid
if event.at_user_list:
for user in event.at_user_list:
user = user.strip()
if user != "":
qid = user
logger.info('[每日信息]QQ号: {}'.format(qid))
im = await get_resin_img(qid)
im = await get_resin_img(qid) # type:ignore
if isinstance(im, str):
await bot.send(ev, im)
await matcher.finish(im)
elif isinstance(im, bytes):
im = await convert_img(im)
await bot.send(ev, im)
await matcher.finish(MessageSegment.image(im))
else:
await bot.send(ev, '发生了未知错误,请联系管理员检查后台输出!')
await matcher.finish('发生了未知错误,请联系管理员检查后台输出!')

View File

@ -1,25 +1,47 @@
import asyncio
import threading
from hoshino.typing import CQEvent, HoshinoBot
from nonebot import on_command
from nonebot.log import logger
from nonebot.matcher import Matcher
from nonebot.permission import SUPERUSER
from nonebot.adapters.ntchat import Bot, TextMessageEvent
from ..base import sv, logger
from ..genshinuid_meta import register_menu
from ..utils.nonebot2.rule import FullCommand
from ..utils.exception.handle_exception import handle_exception
from ..utils.download_resource.download_all_resource import (
download_all_resource,
)
download_resource = on_command('下载全部资源', rule=FullCommand())
@sv.on_fullmatch('下载全部资源')
async def send_download_resource_msg(bot: HoshinoBot, ev: CQEvent):
if ev.sender:
qid = ev.sender['user_id']
else:
@download_resource.handle()
@handle_exception('下载全部资源', '资源下载错误')
@register_menu(
'下载全部资源',
'下载全部资源',
'手动下载插件运行所需的资源',
trigger_method='管理员指令',
detail_des=(
'介绍:\n'
'手动下载插件正常运行所需的资源(一般每次启动会自动检查并下载)\n'
' \n'
'指令:\n'
'- <ft color=(238,120,0)>下载全部资源</ft>'
),
)
async def send_download_resource_msg(
bot: Bot,
event: TextMessageEvent,
matcher: Matcher,
):
if not await SUPERUSER(bot, event):
return
if qid not in bot.config.SUPERUSERS:
return
await bot.send(ev, '正在开始下载~可能需要较久的时间!')
await matcher.send('正在开始下载~可能需要较久的时间!')
im = await download_all_resource()
await bot.send(ev, im)
await matcher.finish(im)
async def startup():

View File

@ -1,27 +1,112 @@
import re
from hoshino.typing import CQEvent, HoshinoBot
from nonebot import on_command
from nonebot.log import logger
from nonebot.matcher import Matcher
from nonebot.params import CommandArg
from nonebot.adapters.ntchat.message import Message
from nonebot.adapters.ntchat import MessageSegment, TextMessageEvent
from ..base import sv, logger
from .get_regtime import calc_reg_time
from .draw_roleinfo_card import draw_pic
from ..genshinuid_meta import register_menu
from ..utils.nonebot2.rule import FullCommand
from ..utils.message.error_reply import UID_HINT
from ..utils.db_operation.db_operation import select_db
from ..utils.mhy_api.convert_mysid_to_uid import convert_mysid
from ..utils.draw_image_tools.send_image_tool import convert_img
from ..utils.exception.handle_exception import handle_exception
get_role_info = on_command('uid', aliases={'查询'})
get_reg_time = on_command(
'原神注册时间', aliases={'注册时间', '查询注册时间'}, rule=FullCommand()
)
@sv.on_prefix(('原神注册时间', '注册时间'))
async def regtime(bot, ev):
at = re.search(r'\[CQ:at,qq=(\d*)]', str(ev.message))
if at:
qid = int(at.group(1))
else:
if ev.sender:
qid = int(ev.sender['user_id'])
else:
@get_role_info.handle()
@handle_exception('查询角色信息')
@register_menu(
'查询帐号信息',
'查询',
'帐号基础数据与角色信息总览',
detail_des=(
'介绍:\n'
'查询帐号探索度、声望、宝箱收集、角色总览等等基础数据\n'
'未绑定CK时最多只能查询8个角色信息\n'
' \n'
'指令:\n'
'- <ft color=(238,120,0)>{查询|uid}</ft>'
'<ft color=(125,125,125)>({@某人|[UID]})</ft>\n'
'- <ft color=(238,120,0)>mys</ft>'
'<ft color=(125,125,125)>({@某人|[米游社ID]})</ft>\n'
'- <ft color=(238,120,0)>直接发送九位数UID</ft>\n'
' \n'
'示例:\n'
'- <ft color=(238,120,0)>查询</ft>\n'
'- <ft color=(238,120,0)>查询</ft><ft color=(0,123,67)>@无疑Wuyi</ft>\n'
'- <ft color=(238,120,0)>uid123456789</ft>\n'
'- <ft color=(238,120,0)>123456789</ft>'
),
)
async def send_role_info(
event: TextMessageEvent,
matcher: Matcher,
args: Message = CommandArg(),
):
raw_mes = args.extract_plain_text().strip().replace(' ', '')
# 若@在文本中 先无视@后面内容 如果@前面有内容则无视并停止执行下方命令 避免与enka查询打架
if "@" in raw_mes:
name = ''.join(re.findall('^[\u4e00-\u9fa5]+', raw_mes.split("@")[0]))
if name:
return
else:
name = ''.join(re.findall('^[\u4e00-\u9fa5]+', raw_mes))
if name:
return
qid = event.from_wxid
# 识别@的人 排除空
if event.at_user_list:
for user in event.at_user_list:
user = user.strip()
if user != "":
qid = user
# 获取uid
uid = re.findall(r'\d+', raw_mes.split("@")[0])
if uid:
uid = uid[0]
else:
uid = await select_db(qid, mode='uid')
uid = str(uid)
logger.info('开始执行[查询角色信息]')
logger.info('[查询角色信息]参数: {}'.format(args))
logger.info('[查询角色信息]uid: {}'.format(uid))
if '未找到绑定的UID' in uid:
await matcher.finish(UID_HINT)
im = await draw_pic(uid)
if isinstance(im, str):
await matcher.finish(im)
elif isinstance(im, bytes):
await matcher.finish(MessageSegment.image(im))
else:
await matcher.finish('发生了未知错误,请联系管理员检查后台输出!')
@get_reg_time.handle()
async def regtime(
event: TextMessageEvent,
matcher: Matcher,
):
qid = event.from_wxid
# 识别@的人 排除空
if event.at_user_list:
for user in event.at_user_list:
user = user.strip()
if user != "":
qid = user
uid = await select_db(qid, mode='uid')
uid = str(uid)
@ -30,57 +115,11 @@ async def regtime(bot, ev):
logger.info('[查询注册时间]uid: {}'.format(uid))
if '未找到绑定的UID' in uid:
await bot.send(ev, UID_HINT)
await matcher.finish(UID_HINT)
im = await calc_reg_time(uid)
await bot.send(ev, im)
@sv.on_rex(r'^()?()?()?([1256][0-9]{8})()?()?$')
@sv.on_rex(
r'^(\[CQ:at,qq=[0-9]+\])?( )?'
r'(uid|查询|mys)([0-9]+)?'
r'(\[CQ:at,qq=[0-9]+\])?( )?$',
)
async def send_role_info(bot: HoshinoBot, ev: CQEvent):
args = ev['match'].groups()
at = re.search(r'\[CQ:at,qq=(\d*)]', str(ev.message))
if at:
qid = int(at.group(1))
else:
if ev.sender:
qid = int(ev.sender['user_id'])
else:
return
# 判断uid
if args[2] != 'mys':
if args[3] is None:
if args[2] is None:
return
uid = await select_db(qid, mode='uid')
uid = str(uid)
elif len(args[3]) != 9:
return
else:
uid = args[3]
else:
uid = await convert_mysid(args[3])
logger.info('开始执行[查询角色信息]')
logger.info('[查询角色信息]参数: {}'.format(args))
logger.info('[查询角色信息]uid: {}'.format(uid))
if '未找到绑定的UID' in uid:
await bot.send(ev, UID_HINT)
im = await draw_pic(uid)
if isinstance(im, str):
await bot.send(ev, im)
elif isinstance(im, bytes):
im = await convert_img(im)
await bot.send(ev, im)
await matcher.finish(im)
else:
await bot.send(ev, '发生了未知错误,请联系管理员检查后台输出!')
await matcher.finish('发生了未知错误,请联系管理员检查后台输出!')

View File

@ -1,51 +1,91 @@
import random
import asyncio
from nonebot.log import logger
from nonebot.matcher import Matcher
from nonebot import get_bot, on_command
from nonebot.permission import SUPERUSER
from nonebot_plugin_apscheduler import scheduler
from nonebot.adapters.ntchat import Bot, MessageEvent, MessageSegment
from ..config import priority
try:
from sign import sign_in, daily_sign
except ImportError:
from .sign import sign_in, daily_sign
import asyncio
from nonebot import get_bot
from hoshino.typing import CQEvent, HoshinoBot
from ..base import sv, logger
from ..genshinuid_meta import register_menu
from ..utils.nonebot2.rule import FullCommand
from ..utils.exception.handle_exception import handle_exception
from ..utils.db_operation.db_operation import select_db, config_check
sign_scheduler = scheduler
get_sign = on_command('签到', priority=priority, rule=FullCommand())
all_recheck = on_command('全部重签', priority=priority, rule=FullCommand())
# 每日零点半执行米游社原神签到
@sv.scheduled_job('cron', hour='0', minute='30')
@sign_scheduler.scheduled_job('cron', hour='0', minute='30')
async def sign_at_night():
if await config_check('SchedSignin'):
await send_daily_sign()
# 群聊内 签到 功能
@sv.on_rex(r'^(gs|米游社)(签到)$')
async def get_sign_func(bot: HoshinoBot, ev: CQEvent):
@get_sign.handle()
@handle_exception('签到')
@register_menu(
'手动米游社原神签到',
'签到',
'手动触发米游社原神签到任务',
detail_des=(
'介绍:\n'
'手动触发米游社原神签到任务\n'
' \n'
'指令:\n'
'- <ft color=(238,120,0)>签到</ft>'
),
)
async def get_sign_func(
event: MessageEvent,
matcher: Matcher,
):
logger.info('开始执行[签到]')
qid = int(ev.sender['user_id']) # type: ignore
qid = event.from_wxid
wxid_list = []
wxid_list.append(event.from_wxid)
logger.info('[签到]QQ号: {}'.format(qid))
uid = await select_db(qid, mode='uid')
logger.info('[签到]UID: {}'.format(uid))
im = await sign_in(uid)
await bot.send(ev, im, at_sender=True)
await matcher.finish(
MessageSegment.room_at_msg(content="{$@}" + f'{im}', at_list=wxid_list)
)
@sv.on_fullmatch('全部重签')
async def recheck(bot: HoshinoBot, ev: CQEvent):
if ev.sender:
qid = int(ev.sender['user_id'])
else:
@all_recheck.handle()
@handle_exception('全部重签')
@register_menu(
'米游社原神重签到',
'全部重签',
'重新运行所有自动米游社原神签到任务',
detail_des=(
'介绍:\n'
'重新运行所有自动米游社原神签到任务\n'
' \n'
'指令:\n'
'- <ft color=(238,120,0)>全部重签</ft>'
),
)
async def recheck(bot: Bot, event: MessageEvent, matcher: Matcher):
if not await SUPERUSER(bot, event):
return
if qid not in bot.config.SUPERUSERS:
return
logger.info('开始执行[全部重签]')
await bot.send(ev, '已开始执行')
await matcher.send('已开始执行')
await send_daily_sign()
await bot.send(ev, '执行完成')
await matcher.finish('执行完成')
async def send_daily_sign():
@ -60,9 +100,10 @@ async def send_daily_sign():
# 执行私聊推送
for qid in private_msg_list:
try:
await bot.send_private_msg(
user_id=qid,
message=private_msg_list[qid],
await bot.call_api(
api='send_text',
to_wxid=qid,
content=private_msg_list[qid],
)
except Exception:
logger.warning(f'[每日全部签到] QQ {qid} 私聊推送失败!')
@ -84,12 +125,16 @@ async def send_daily_sign():
report,
)
else:
msg_title = group_msg_list[gid]['push_message']
msg_title = group_msg_list[gid]['push_message'].rstrip()
# 发送群消息
at_wxid = []
at_wxid.append(group_msg_list[gid]['wxid'])
try:
await bot.send_group_msg(
group_id=gid,
message=msg_title,
await bot.call_api(
api='send_room_at_msg',
to_wxid=gid,
at_list=at_wxid,
content=msg_title,
)
except Exception:
logger.warning(f'[每日全部签到]群 {gid} 推送失败!')

View File

@ -139,16 +139,18 @@ async def single_daily_sign(uid: str, gid: str, qid: str):
# 如果失败, 则添加到推送列表
if im.startswith('签到失败') or im.startswith('网络有点忙'):
# 不用MessageSegment.at(row[2]),因为不方便移植
message = f'[CQ:at,qq={qid}] {im}'
message = f'UID:{uid} | {im}'
group_msg_list[gid]['failed'] += 1
group_msg_list[gid]['push_message'] += '\n' + message
group_msg_list[gid]['push_message'] += message + '\n'
else:
group_msg_list[gid]['success'] += 1
# 没有开启简洁签到, 则每条消息都要携带@信息
else:
# 不用MessageSegment.at(row[2]),因为不方便移植
message = f'[CQ:at,qq={qid}] {im}'
group_msg_list[gid]['push_message'] += '\n' + message
# message = '{$@}' + f'UID:{uid} | {im}'
message = f'UID:{uid} | {im}'
group_msg_list[gid]['wxid'] = qid
group_msg_list[gid]['push_message'] += message + '\n'
group_msg_list[gid]['success'] -= 1

View File

@ -1,78 +1,131 @@
from nonebot import get_bot, on_startup
from hoshino.typing import CQEvent, HoshinoBot
from typing import Any, Tuple
from ..base import sv, logger
from nonebot.log import logger
from nonebot.matcher import Matcher
from nonebot.params import RegexGroup
from nonebot.permission import SUPERUSER
from nonebot import get_bot, on_regex, get_driver, on_command
from nonebot.adapters.ntchat import Bot, MessageEvent, MessageSegment
from ..genshinuid_meta import register_menu
from ..utils.nonebot2.rule import FullCommand
from .draw_update_log import draw_update_log_img
from .restart import restart_message, restart_genshinuid
from ..utils.draw_image_tools.send_image_tool import convert_img
gs_restart = on_command('gs重启', rule=FullCommand())
get_update_log = on_command('更新记录', rule=FullCommand())
gs_update = on_regex(
r'^(gs)(强行)?(强制)?(更新)$',
block=True,
)
@on_startup
async def check_msg():
try:
logger.info('检查遗留信息...')
update_log = await restart_message()
if update_log == {}:
return
bot = get_bot()
if update_log['send_type'] == 'group':
await bot.send_group_msg(
group_id=update_log['send_to'],
message=update_log['msg'],
)
else:
await bot.send_private_msg(
user_id=update_log['send_to'],
message=update_log['msg'],
)
logger.info('遗留信息检查完毕!')
except Exception:
logger.warning('遗留信息检查失败!')
driver = get_driver()
@sv.on_fullmatch('gs重启')
async def send_restart_msg(bot: HoshinoBot, ev: CQEvent):
if ev.sender:
qid = int(ev.sender['user_id'])
else:
@driver.on_bot_connect
async def _():
logger.info('检查遗留信息...')
bot = get_bot()
update_log = await restart_message()
if update_log == {}:
return
if qid not in bot.config.SUPERUSERS:
if update_log['send_type'] == 'group':
await bot.call_api(
api='send_room_at_msg',
to_wxid=update_log['send_to'],
content=update_log['msg'],
)
else:
await bot.call_api(
api='send_text',
to_wxid=update_log['send_to'],
content=update_log['msg'],
)
logger.info('遗留信息检查完毕!')
@get_update_log.handle()
@register_menu(
'更新记录',
'更新记录',
'查看插件最近的更新记录',
detail_des=(
'介绍:\n'
'查看插件最近的有效Git更新记录\n'
' \n'
'指令:\n'
'- <ft color=(238,120,0)>更新记录</ft>'
),
)
async def send_updatelog_msg(
matcher: Matcher,
):
im = await draw_update_log_img(is_update=False)
logger.info('正在执行[更新记录]...')
if isinstance(im, str):
await matcher.finish(im)
elif isinstance(im, bytes):
await matcher.finish(MessageSegment.image(im))
else:
await matcher.finish('发生了未知错误,请联系管理员检查后台输出!')
@gs_restart.handle()
@register_menu(
'重启Bot',
'gs重启',
'重启Bot框架',
trigger_method='超级用户指令',
detail_des=(
'介绍:\n' '重启Bot框架\n' ' \n' '指令:\n' '- <ft color=(238,120,0)>gs重启</ft>'
),
)
async def send_restart_msg(
bot: Bot,
event: MessageEvent,
matcher: Matcher,
):
if not await SUPERUSER(bot, event):
return
logger.warning('开始执行[重启]')
if ev.group_id:
send_id = str(ev.group_id)
qid = event.from_wxid
if len(event.get_session_id().split('_')) == 3:
send_id = event.get_session_id().split('_')[1]
send_type = 'group'
else:
send_id = qid
send_type = 'private'
await bot.send(ev, '正在执行[gs重启]...')
await matcher.send('正在执行[gs重启]...')
await restart_genshinuid(send_type, str(send_id))
@sv.on_fullmatch('更新记录')
async def send_updatelog_msg(bot: HoshinoBot, ev: CQEvent):
im = await draw_update_log_img(is_update=False)
logger.info('正在执行[更新记录]...')
if isinstance(im, str):
await bot.send(ev, im)
elif isinstance(im, bytes):
im = await convert_img(im)
await bot.send(ev, im)
else:
await bot.send(ev, '发生了未知错误,请联系管理员检查后台输出!')
@sv.on_rex(r'^(gs)(强行)?(强制)?(更新)$')
async def send_update_msg(bot: HoshinoBot, ev: CQEvent):
if ev.sender:
qid = int(ev.sender['user_id'])
else:
return
if qid not in bot.config.SUPERUSERS:
@gs_update.handle()
@register_menu(
'更新插件',
'gs更新',
'手动更新插件',
detail_des=(
'介绍:\n'
'手动更新插件(执行 git pull\n'
'每加上一个可选参数执行等级加1\n'
'当执行等级≥1时会还原上次更改等级≥2时会清空暂存\n'
' \n'
'指令:\n'
'- <ft color=(238,120,0)>gs</ft>'
'<ft color=(125,125,125)>(强行)(强制)</ft>'
'<ft color=(238,120,0)>更新</ft>'
),
)
async def send_update_msg(
bot: Bot,
event: MessageEvent,
matcher: Matcher,
args: Tuple[Any, ...] = RegexGroup(),
):
if not await SUPERUSER(bot, event):
return
args = ev['match'].groups()
logger.info('[gs更新] 正在执行 ...')
level = 2
if args[1] is None:
@ -80,12 +133,11 @@ async def send_update_msg(bot: HoshinoBot, ev: CQEvent):
if args[2] is None:
level -= 1
logger.info(f'[gs更新] 更新等级为{level}')
await bot.send(ev, f'开始执行[gs更新], 执行等级为{level}')
await matcher.send(f'开始执行[gs更新], 执行等级为{level}')
im = await draw_update_log_img(level)
if isinstance(im, str):
await bot.send(ev, im)
await matcher.finish(im)
elif isinstance(im, bytes):
im = await convert_img(im)
await bot.send(ev, im)
await matcher.finish(MessageSegment.image(im))
else:
await bot.send(ev, '发生了未知错误,请联系管理员检查后台输出!')
await matcher.finish('发生了未知错误,请联系管理员检查后台输出!')

View File

@ -8,7 +8,7 @@ from pathlib import Path
from ..utils.db_operation.db_operation import config_check
bot_start = Path().cwd() / 'run.py'
bot_start = Path().cwd() / 'bot.py'
restart_sh_path = Path().cwd() / 'gs_restart.sh'
update_log_path = Path(__file__).parent / 'update_log.json'
@ -23,17 +23,17 @@ async def get_restart_sh(extra: str) -> str:
async def restart_genshinuid(send_type: str, send_id: str) -> None:
pid = os.getpid()
extra = ''
if await config_check('UsePoetry'):
extra = 'poetry run '
extra += sys.executable
restart_sh = await get_restart_sh(extra)
with open(restart_sh_path, "w", encoding="utf8") as f:
f.write(restart_sh)
if platform.system() == 'Linux':
os.system(f'chmod +x {str(restart_sh_path)}')
os.system(f'chmod +x {str(bot_start)}')
if not restart_sh_path.exists():
with open(restart_sh_path, "w", encoding="utf8") as f:
f.write(restart_sh)
if platform.system() == 'Linux':
os.system(f'chmod +x {str(restart_sh_path)}')
os.system(f'chmod +x {str(bot_start)}')
now_time = time.strftime('%Y-%m-%d %H:%M:%S', time.localtime(time.time()))
update_log = {
'type': 'restart',
@ -45,11 +45,9 @@ async def restart_genshinuid(send_type: str, send_id: str) -> None:
with open(str(update_log_path), 'w', encoding='utf-8') as f:
json.dump(update_log, f)
if platform.system() == 'Linux':
subprocess.Popen(
f'kill -9 {pid} & {extra} {bot_start}',
shell=True,
)
os.execl(str(restart_sh_path), ' ')
else:
pid = os.getpid()
subprocess.Popen(
f'taskkill /F /PID {pid} & {extra} {bot_start}',
shell=True,

View File

@ -1,103 +1,258 @@
import hoshino
from hoshino.typing import CQEvent, HoshinoBot
import asyncio
from typing import Any, Tuple
from .topup import topup_
from ..base import sv, logger
from nonebot.log import logger
from nonebot.matcher import Matcher
from nonebot import on_regex, on_command
from nonebot.permission import SUPERUSER
from nonebot.params import CommandArg, RegexGroup
from nonebot.adapters.ntchat.message import Message
from nonebot.adapters.ntchat.permission import PRIVATE
from nonebot.adapters.ntchat import (
Bot,
MessageEvent,
MessageSegment,
TextMessageEvent,
)
from ..config import priority
from .topup import GOODS, topup_
from .qrlogin import qrcode_login
from .get_ck_help_msg import get_ck_help
from .draw_user_card import get_user_card
from ..utils.draw_image_tools.send_image_tool import convert_img
from ..genshinuid_meta import register_menu
from ..utils.nonebot2.rule import FullCommand
from .get_ck_help_msg import get_ck_help, get_qr_help
from ..utils.exception.handle_exception import handle_exception
from .add_ck import deal_ck, get_ck_by_stoken, get_ck_by_all_stoken
from ..utils.db_operation.db_operation import bind_db, delete_db, switch_db
hoshino_bot = hoshino.get_bot()
add_cookie = on_command('添加', permission=PRIVATE)
get_ck_msg = on_command(
'绑定ck说明',
aliases={'ck帮助', '绑定ck'},
block=True,
rule=FullCommand(),
)
get_qr_msg = on_command(
'扫码登录说明',
aliases={'扫码帮助', '登录帮助', '登陆帮助'},
block=True,
rule=FullCommand(),
)
bind_info = on_command(
'绑定信息', priority=priority, block=True, rule=FullCommand()
)
refresh_ck = on_command(
'刷新CK',
aliases={'刷新ck', '刷新Ck', '刷新Cookies'},
priority=priority,
block=True,
rule=FullCommand(),
)
refresh_all_ck = on_command(
'刷新全部CK',
aliases={'刷新全部ck', '刷新全部Ck', '刷新全部Cookies'},
priority=priority,
block=True,
rule=FullCommand(),
permission=SUPERUSER,
)
bind = on_regex(
r'^(绑定|切换|解绑|删除)(uid|UID|mys|MYS)([0-9]+)?$', priority=priority
)
get_qrcode_login = on_command(
'扫码登录',
aliases={'扫码登陆', '扫码登入'},
rule=FullCommand(),
)
get_topup = on_command(
'gsrc', priority=priority, block=True, aliases={'原神充值', 'pay'}
)
@sv.on_fullmatch(('刷新全部CK', '刷新全部ck', '刷新全部Ck', '刷新全部Cookies'))
async def send_refresh_allck_msg(bot: HoshinoBot, ev: CQEvent):
if ev.sender:
qid = int(ev.sender['user_id'])
else:
return
if qid not in bot.config.SUPERUSERS:
return
logger.info('开始执行[刷新全部ck]')
im = await get_ck_by_all_stoken()
if isinstance(im, bytes):
im = await convert_img(im)
await bot.send(ev, im)
@get_topup.handle()
async def send_topup(
matcher: Matcher, event: TextMessageEvent, args: Message = CommandArg()
):
# 获取被@的Wxid排除""
qid = event.from_wxid
if event.at_user_list:
for user in event.at_user_list:
user = user.strip()
if user != "":
qid = user
@sv.on_fullmatch(('刷新CK', '刷新ck', '刷新Ck', '刷新Cookies'))
async def send_refresh_ck_msg(bot: HoshinoBot, ev: CQEvent):
logger.info('开始执行[刷新ck]')
if ev.sender:
qid = ev.sender['user_id']
else:
return
im = await get_ck_by_stoken(qid)
if isinstance(im, bytes):
im = await convert_img(im)
await bot.send(ev, im)
@sv.on_fullmatch('绑定信息')
async def send_bind_card(bot: HoshinoBot, ev: CQEvent):
logger.info('开始执行[查询用户绑定状态]')
if ev.sender:
qid = ev.sender['user_id']
else:
return
im = await get_user_card(qid)
im = await convert_img(im)
logger.info('[查询用户绑定状态]完成!等待图片发送中...')
await bot.send(ev, im)
@sv.on_fullmatch(('扫码登陆', '扫码登录', '扫码登入'))
async def send_qr_card(bot: HoshinoBot, ev: CQEvent):
im = await qrcode_login(hoshino_bot, ev.group_id, ev.user_id)
if not im:
return
im = await deal_ck(im, ev.user_id) # type: ignore
if isinstance(im, bytes):
im = await convert_img(im)
await bot.send(ev, im)
@hoshino_bot.on_message('private') # type: ignore
async def send_add_ck_msg(ctx):
message = ctx['raw_message']
sid = int(ctx['self_id'])
userid = int(ctx['sender']['user_id'])
gid = 0
if message.startswith('添加'):
message = message.replace('添加', '').replace(' ', '')
im = await deal_ck(message, userid) # type: ignore
else:
return
if isinstance(im, bytes):
im = await convert_img(im)
await hoshino_bot.send_msg(
self_id=sid, user_id=userid, group_id=gid, message=im
goods_id, method = 0, "alipay"
for s in str(args).split():
# 支持指定支付方式
if "微信" in s or "wx" in s:
method = "weixin"
continue
if "支付宝" in s or "zfb" in s:
method = "alipay"
# 输入物品别名识别
for gId, gData in GOODS.items():
if (s == gId) or (s in gData["aliases"]):
goods_id = gId
break
group_id = event.room_wxid
await matcher.finish(
await topup_(matcher, qid, group_id, goods_id, method)
)
# 群聊内 绑定uid或者mysid 的命令会绑定至当前qq号上
@sv.on_rex(r'^(绑定|切换|解绑|删除)(uid|UID|mys|MYS)([0-9]+)?$')
async def send_link_uid_msg(bot: HoshinoBot, ev: CQEvent):
args = ev['match'].groups()
logger.info('开始执行[绑定/解绑用户信息]')
logger.info('[绑定/解绑]参数: {}'.format(str(args)))
if ev.sender:
qid = ev.sender['user_id']
else:
@refresh_all_ck.handle()
async def send_refresh_all_ck_msg(
matcher: Matcher,
):
logger.info('开始执行[刷新全部CK]')
im = await get_ck_by_all_stoken()
if isinstance(im, str):
await matcher.finish(im)
await matcher.finish(MessageSegment.image(im))
@refresh_ck.handle()
async def send_refresh_ck_msg(
event: MessageEvent,
matcher: Matcher,
):
logger.info('开始执行[刷新CK]')
qid = event.from_wxid
im = await get_ck_by_stoken(qid)
if isinstance(im, str):
await matcher.finish(im)
await matcher.finish(MessageSegment.image(im))
@get_qrcode_login.handle()
async def send_qrcode_login(
bot: Bot,
event: MessageEvent,
matcher: Matcher,
):
logger.info('开始执行[扫码登陆]')
qid = event.from_wxid
im = await qrcode_login(matcher, qid)
if not im:
return
im = await deal_ck(im, qid)
await matcher.finish(MessageSegment.image(im))
@bind_info.handle()
@register_menu(
'绑定状态',
'绑定信息',
'查询你绑定UID的绑定和推送状态',
detail_des=(
'介绍:\n'
'查询你绑定的UID列表以及它们的CK、SK绑定状态和推送设置\n'
' \n'
'指令:\n'
'- <ft color=(238,120,0)>绑定信息</ft>'
),
)
async def send_bind_card(
event: MessageEvent,
matcher: Matcher,
):
logger.info('开始执行[查询用户绑定状态]')
qid = event.from_wxid
im = await get_user_card(qid)
logger.info('[查询用户绑定状态]完成!等待图片发送中...')
await matcher.finish(MessageSegment.image(im))
@add_cookie.handle()
@handle_exception('Cookie', '校验失败请输入正确的Cookies')
@register_menu(
'绑定CK、SK',
'添加[CK或SK]',
'绑定你的Cookies以及Stoken',
trigger_method='好友私聊指令',
detail_des=(
'介绍:\n'
'绑定你的Cookies以及Stoken\n'
'Cookies (CK)米游社CookiesStoken (SK)米哈游通行证Cookies\n'
' \n'
'指令:\n'
'- <ft color=(238,120,0)>添加</ft><ft color=(0,148,200)>[CK或SK]</ft>'
),
)
async def send_add_ck_msg(
event: MessageEvent, matcher: Matcher, args: Message = CommandArg()
):
mes = args.extract_plain_text().strip().replace(' ', '')
qid = event.from_wxid
im = await deal_ck(mes, qid)
if isinstance(im, str):
await matcher.finish(im)
await matcher.finish(MessageSegment.image(im))
# 群聊内 绑定uid或者mysid 的命令会绑定至当前qq号上
@bind.handle()
@handle_exception('绑定ID', '绑定ID异常')
@register_menu(
'绑定UID',
'绑定xx',
'绑定原神UID或米游社UID',
detail_des=(
'介绍:\n'
'绑定原神UID或米游社UID\n'
' \n'
'指令:\n'
'- <ft color=(238,120,0)>绑定'
'{uid</ft><ft color=(0,148,200)>[原神UID]</ft>'
'<ft color=(238,120,0)>|mys</ft><ft color=(0,148,200)>[米游社UID]</ft>'
'<ft color=(238,120,0)>}</ft>'
),
)
@register_menu(
'解绑UID',
'解绑xx',
'解绑原神UID或米游社UID',
detail_des=(
'介绍:\n'
'解绑原神UID或米游社UID\n'
' \n'
'指令:\n'
'- <ft color=(238,120,0)>{解绑|删除}'
'{uid</ft><ft color=(0,148,200)>[原神UID]</ft>'
'<ft color=(238,120,0)>|mys</ft><ft color=(0,148,200)>[米游社UID]</ft>'
'<ft color=(238,120,0)>}</ft>'
),
)
@register_menu(
'切换UID',
'切换xx',
'切换当前原神UID或米游社UID',
detail_des=(
'介绍:\n'
'切换当前原神UID或米游社UID\n'
'绑定一个UID的情况下无法切换\n'
' \n'
'指令:\n'
'- <ft color=(238,120,0)>切换'
'{uid</ft><ft color=(0,148,200)>[原神UID]</ft>'
'<ft color=(238,120,0)>|mys</ft><ft color=(0,148,200)>[米游社UID]</ft>'
'<ft color=(238,120,0)>}</ft>'
),
)
async def send_link_uid_msg(
event: MessageEvent, matcher: Matcher, args: Tuple[Any, ...] = RegexGroup()
):
logger.info('开始执行[绑定/解绑用户信息]')
logger.info('[绑定/解绑]参数: {}'.format(args))
qid = event.from_wxid
wxid_list = []
wxid_list.append(event.from_wxid)
logger.info('[绑定/解绑]UserID: {}'.format(qid))
if args[0] in ('绑定'):
if args[2] is None:
await bot.send(ev, '请输入正确的uid或者mysid')
await matcher.finish('请输入正确的uid或者mysid')
if args[1] in ('uid', 'UID'):
im = await bind_db(qid, args[2])
@ -110,32 +265,23 @@ async def send_link_uid_msg(bot: HoshinoBot, ev: CQEvent):
im = await delete_db(qid, {'UID': args[2]})
else:
im = await delete_db(qid, {'MYSID': args[2]})
await bot.send(ev, im, at_sender=True)
@sv.on_fullmatch(('绑定ck说明', 'ck帮助', '绑定ck'))
async def send_ck_msg(bot: HoshinoBot, ev: CQEvent):
msg_list = await get_ck_help()
forward_msg = []
for msg in msg_list:
forward_msg.append(
{
"type": "node",
"data": {"name": "小冰", "uin": "2854196306", "content": msg},
}
)
await bot.send_group_forward_msg(
group_id=ev.group_id, messages=forward_msg
await matcher.finish(
MessageSegment.room_at_msg(content="{$@}" + f'{im}', at_list=wxid_list)
)
@sv.on_prefix(("gsrc", "原神充值"))
async def topup(bot: HoshinoBot, ev: CQEvent):
qid = ev.user_id
goods_id = ev.message.extract_plain_text()
if goods_id == "":
goods_id = 0
else:
goods_id = int(goods_id)
group_id = ev.group_id
await topup_(bot, qid, group_id, goods_id)
@get_ck_msg.handle()
async def send_ck_help(matcher: Matcher):
msg_list = await get_ck_help()
for msg in msg_list:
await matcher.send(msg)
await asyncio.sleep(0.5)
@get_qr_msg.handle()
async def send_qr_help(matcher: Matcher):
msg_list = await get_qr_help()
for msg in msg_list:
await matcher.send(msg)
await asyncio.sleep(0.5)
await asyncio.sleep(0.5)

View File

@ -55,7 +55,7 @@ async def get_ck_by_all_stoken():
return im
async def get_ck_by_stoken(qid: int):
async def get_ck_by_stoken(qid: str):
uid_list: List = await select_db(qid, mode='list') # type: ignore
uid_dict = {uid: qid for uid in uid_list}
im = await refresh_ck_by_uid_list(uid_dict)

View File

@ -0,0 +1,241 @@
from io import BytesIO
from time import strftime, localtime
from httpx import AsyncClient
from PIL import Image, ImageDraw, ImageFont
from ..utils.genshin_fonts.genshin_fonts import FONT_ORIGIN_PATH
def font(size: int) -> ImageFont.FreeTypeFont:
"""Pillow 绘制字体设置"""
return ImageFont.truetype(str(FONT_ORIGIN_PATH), size=size)
# 支付宝充值图片绘制
async def draw_ali(
uid,
item_info: str,
item_price: str,
item_order_no: str,
qrcode,
item_icon: str,
item_create_time: int,
item_id: str,
) -> bytes:
"""充值图片绘制"""
async with AsyncClient() as client:
_img = await client.get(item_icon, timeout=10.0)
itemImg = Image.open(BytesIO(_img.content)).convert("RGBA")
themeColor = "#1678ff"
warning = 20 if item_id == 'ys_chn_blessofmoon_tier5' else 0
res = Image.new("RGBA", (450, 530), themeColor)
drawer = ImageDraw.Draw(res)
resample = getattr(Image, "Resampling", Image).LANCZOS
# 头部矩形背景
drawer.rectangle(
(75, 50 - warning, 375 - 1, 150 - warning), fill="#E5F9FF", width=0
)
# 商品图片
item = itemImg.resize((90, 90), resample=resample)
res.paste(item, (80, 55 - warning), item)
# 二维码图片
qrcode = Image.open(qrcode)
qrcode = qrcode.resize((300, 300), resample=resample)
res.paste(qrcode, (75, 150 + warning), qrcode)
# 商品名称
drawer.text(
(
int(175 + (195 - font(25).getlength(item_info)) / 2),
int(70 - warning + (30 - font(25).getbbox(item_info)[-1]) / 2),
),
item_info,
fill="#000000",
font=font(25),
)
# 价格
drawer.text(
(
int(175 + (195 - font(25).getlength(item_price)) / 2),
int(105 - warning + (30 - font(25).getbbox(item_price)[-1]) / 2),
),
item_price,
fill="#000000",
font=font(25),
)
# 商品充值 UID
drawer.text(
(
int((460 - font(15).getlength(f"充值到 UID{uid}")) / 2),
int(
155
+ warning
+ (20 - font(15).getbbox(f"充值到 UID{uid}")[-1]) / 2
),
),
f"充值到 UID{uid}",
fill="#333333",
font=font(15),
)
# 月卡相关商品警告
if warning:
# 首部矩形背景
drawer.rectangle((75, 130, 375 - 1, 170), fill="#eeeeee", width=0)
# 转换警告文字
warning_text = "特殊情况将直接返还 330 创世结晶"
drawer.text(
(
int((450 - font(15).getlength(warning_text)) / 2),
int(130 + (40 - font(15).getbbox(warning_text)[-1]) / 2),
),
warning_text,
fill="#ff5652",
font=font(15),
)
# 图片生成时间
timestamp = strftime("%Y-%m-%d %H:%M:%S", localtime(int(item_create_time)))
drawer.text(
(int((460 - font(15).getlength(timestamp)) / 2), 425 + warning),
timestamp,
fill="#1678ff",
font=font(15),
)
# 账单信息
ticket = f"支付宝账单编号 {item_order_no}"
drawer.text(
(int((450 - font(15).getlength(ticket)) / 2), 460 + warning),
ticket,
fill="#ffffff",
font=font(15),
)
# 免责声明
drawer.text(
(
int(
(
410
- font(15).getlength(
"免责声明:该充值接口由米游社提供,不对充值结果负责。\n请在充值前仔细阅读米哈游的充值条款。"
)
/ 2
)
),
490 + warning,
),
"免责声明:该充值接口由米游社提供,不对充值结果负责。\n 请在充值前仔细阅读米哈游的充值条款。",
fill="#FFA500",
font=font(12),
)
buf = BytesIO()
res.convert("RGB").save(buf, format="PNG")
return buf.getvalue()
# 微信充值图片绘制
async def draw_wx(
uid,
item_info: str,
item_price: str,
item_order_no: str,
item_icon: str,
item_create_time: int,
item_id: str,
) -> bytes:
"""充值图片绘制"""
async with AsyncClient() as client:
_img = await client.get(item_icon, timeout=10.0)
itemImg = Image.open(BytesIO(_img.content)).convert("RGBA")
themeColor = "#29ac66"
warning = 20 if item_id == 'ys_chn_blessofmoon_tier5' else 0
res = Image.new("RGBA", (450, 200), themeColor)
drawer = ImageDraw.Draw(res)
resample = getattr(Image, "Resampling", Image).LANCZOS
left = (450 - 370) / 2 # (图片宽度 - 矩形宽度) / 2
top = (200 - 130) / 2 # (图片高度 - 矩形高度) / 2
# 头部矩形背景
drawer.rectangle(
(
left,
top - warning,
left + 370,
top + 130 - warning,
), # 使用新的左上角和右下角的坐标
fill="#E5F9FF", # 保留原有的填充色
width=0, # 保留原有的边框宽度
)
# 商品图片
item = itemImg.resize((110, 110), resample=resample)
res.paste(item, (55, 50 - warning), item)
# 商品名称
drawer.text(
(
int(175 + (215 - font(25).getlength(item_info)) / 2),
int(50 - warning + (30 - font(25).getbbox(item_info)[-1]) / 2),
),
item_info,
fill="#000000",
font=font(25),
)
# 价格
drawer.text(
(
int(180 + (195 - font(25).getlength(item_price)) / 2),
int(80 - warning + (30 - font(25).getbbox(item_price)[-1]) / 2),
),
item_price,
fill="#000000",
font=font(25),
)
# 商品充值 UID
drawer.text(
(
int(185 + (195 - font(15).getlength(f"充值到 UID{uid}")) / 2),
int(
120
- warning
+ (20 - font(15).getbbox(f"充值到 UID{uid}")[-1]) / 2
),
),
f"充值到 UID{uid}",
fill="#000000",
font=font(15),
)
# 图片生成时间
timestamp = strftime("%Y-%m-%d %H:%M:%S", localtime(int(item_create_time)))
drawer.text(
(int(185 + (195 - font(15).getlength(timestamp)) / 2), 140 - warning),
timestamp,
fill="#29ac66",
font=font(15),
)
if warning:
# 首部矩形背景
drawer.rectangle(
(left, top + 110, left + 370, top + 130), fill="#eeeeee", width=0
)
# 转换警告文字
warning_text = "特殊情况将直接返还 330 创世结晶"
drawer.text(
(
int((450 - font(15).getlength(warning_text)) / 2),
int(135 + (40 - font(15).getbbox(warning_text)[-1]) / 2),
),
warning_text,
fill="#ff5652",
font=font(15),
)
# 账单信息
ticket = f"微信支付账单编号 {item_order_no}"
drawer.text(
(int((450 - font(15).getlength(ticket)) / 2), 170),
ticket,
fill="#ffffff",
font=font(15),
)
buf = BytesIO()
res.convert("RGB").save(buf, format="PNG")
return buf.getvalue()

View File

@ -29,7 +29,7 @@ gs_font_15 = genshin_font_origin(15)
gs_font_26 = genshin_font_origin(26)
async def get_user_card(qid: int) -> bytes:
async def get_user_card(qid: str) -> bytes:
uid_list: List = await select_db(qid, 'list') # type: ignore
w, h = 500, len(uid_list) * 210 + 330
img = await get_simple_bg(w, h)

View File

@ -26,6 +26,10 @@ http://user.mihoyo.com/(国服)
然后在和机器人的私聊窗口,粘贴发送即可
'''
QRL = '''请在群里发送【扫码登录】并用米游社/原神进行扫码绑定
如果使用该功能请先绑定UID后再进行登录
注意不要乱扫他人二维码'''
async def get_ck_help() -> List:
msg_list = []
@ -34,3 +38,9 @@ async def get_ck_help() -> List:
msg_list.append(CK_URL)
msg_list.append(SK_URL)
return msg_list
async def get_qr_help() -> List:
msg_list = []
msg_list.append(QRL)
return msg_list

View File

@ -3,12 +3,15 @@ import json
import base64
import asyncio
from http.cookies import SimpleCookie
from typing import Any, Tuple, Union, Literal
from typing import Any, List, Tuple, Union, Literal, NoReturn
import qrcode
from nonebot.log import logger
from nonebot.matcher import Matcher
from qrcode.constants import ERROR_CORRECT_L
from nonebot.adapters.ntchat import MessageSegment
from ..utils.message.send_msg import send_forward_msg
from ..utils.message.error_reply import UID_HINT
from ..utils.db_operation.db_operation import select_db
from ..utils.mhy_api.get_mhy_data import (
check_qrcode,
@ -19,26 +22,27 @@ from ..utils.mhy_api.get_mhy_data import (
get_cookie_token_by_stoken,
)
disnote = '''免责声明:您将通过扫码完成获取米游社sk以及ck。
本Bot将不会保存您的登录状态
我方仅提供米游社查询及相关游戏内容服务
disnote = '''免责声明您将通过扫码完成获取米游社sk以及ck。
登录米游社相当于将账号授权于机器人,可能会带来未知的账号风险
若您的账号封禁、被盗等处罚与我方无关。
害怕风险请勿扫码!
'''
登录后即代表您同意机器人使用协议并知晓会带来未知的账号风险!
若想取消授权,请到米游社退出登录以清除机器人绑定的登录状态。
请用米游社/原神扫描下方二维码登录:'''
def get_qrcode_base64(url):
qr = qrcode.QRCode(
version=1,
error_correction=qrcode.constants.ERROR_CORRECT_L,
error_correction=ERROR_CORRECT_L,
box_size=10,
border=4,
)
qr.add_data(url)
qr.make(fit=True)
img = qr.make_image(fill_color="black", back_color="white")
img = qr.make_image(fill_color='black', back_color='white')
img_byte = io.BytesIO()
img.save(img_byte, format="PNG")
img.save(img_byte, format='PNG') # type: ignore
img_byte = img_byte.getvalue()
return base64.b64encode(img_byte).decode()
@ -50,50 +54,52 @@ async def refresh(
while True:
await asyncio.sleep(2)
status_data = await check_qrcode(
code_data["app_id"], code_data["ticket"], code_data["device"]
code_data['app_id'], code_data['ticket'], code_data['device']
)
if status_data["retcode"] != 0:
logger.warn("二维码已过期")
if status_data['retcode'] != 0:
logger.warning('二维码已过期')
return False, None
if status_data["data"]["stat"] == "Scanned":
if status_data['data']['stat'] == 'Scanned':
if not scanned:
logger.info("二维码已扫描")
logger.info('二维码已扫描')
scanned = True
continue
if status_data["data"]["stat"] == "Confirmed":
logger.info("二维码已确认")
# print(status_data["data"]["payload"]["raw"])
if status_data['data']['stat'] == 'Confirmed':
logger.info('二维码已确认')
break
return True, json.loads(status_data["data"]["payload"]["raw"])
return True, json.loads(status_data['data']['payload']['raw'])
async def qrcode_login(bot, group_id, user_id) -> str:
async def send_group_msg(msg: str):
await bot.call_action(
action='send_group_msg',
group_id=group_id,
message=msg,
async def qrcode_login(matcher: Matcher, user_id) -> str:
async def send_group_msg(msg: str, at_list: List) -> NoReturn:
await matcher.finish(
MessageSegment.room_at_msg(content=msg, at_list=at_list)
)
return ""
wxid_list = []
wxid_list.append(user_id)
code_data = await create_qrcode_url()
try:
im = []
qrc = f'[CQ:image,file=base64://{get_qrcode_base64(code_data["url"])}]'
im.append('请使用米游社扫描下方二维码登录:')
im.append(qrc)
im.append(disnote)
await send_forward_msg(bot, group_id, "扫码小助手", str(user_id), im)
await matcher.send(
MessageSegment.image(
f'base64://{get_qrcode_base64(code_data["url"])}'
)
)
await matcher.send(
MessageSegment.room_at_msg(
content='{$@}' + disnote, at_list=wxid_list
)
)
except Exception:
logger.warn(f"[扫码登录] {group_id} 图片发送失败")
logger.warning('[扫码登录] {user_id} 图片发送失败')
status, game_token_data = await refresh(code_data)
if status:
assert game_token_data is not None # 骗过 pyright
logger.info("game_token获取成功")
logger.info('[扫码登录]game_token获取成功')
cookie_token = await get_cookie_token(**game_token_data)
stoken_data = await get_stoken_by_game_token(
account_id=int(game_token_data["uid"]),
game_token=game_token_data["token"],
account_id=int(game_token_data['uid']),
game_token=game_token_data['token'],
)
account_id = game_token_data['uid']
stoken = stoken_data['data']['token']['token']
@ -103,7 +109,6 @@ async def qrcode_login(bot, group_id, user_id) -> str:
ck = ck['data']['cookie_token']
cookie_check = f'account_id={account_id};cookie_token={ck}'
get_uid = await get_mihoyo_bbs_info(account_id, cookie_check)
# 剔除除了原神之外的其他游戏
im = None
if get_uid:
for i in get_uid['data']['list']:
@ -112,17 +117,15 @@ async def qrcode_login(bot, group_id, user_id) -> str:
break
else:
im = f'你的米游社账号{account_id}尚未绑定原神账号,请前往米游社操作!'
return await send_group_msg(im)
await send_group_msg(im, wxid_list)
else:
im = '请求失败, 请稍后再试...'
return await send_group_msg(im)
await send_group_msg(im, wxid_list)
uid_bind = await select_db(user_id, mode='uid')
# 没有在gsuid绑定uid的情况
if uid_bind == "未找到绑定的UID~":
logger.warning('game_token获取失败')
im = '你还没有绑定uid请输入[绑定uid123456]绑定你的uid再发送[扫码登录]进行绑定'
return await send_group_msg(im)
# 比对gsuid数据库和扫码登陆获取到的uid
await matcher.finish(UID_HINT)
if str(uid_bind) == uid_check or str(uid_bind) == account_id:
return SimpleCookie(
{
@ -133,9 +136,12 @@ async def qrcode_login(bot, group_id, user_id) -> str:
}
).output(header='', sep=';')
else:
logger.warning('game_token获取失败')
im = 'game_token获取失败被非绑定指定uid用户扫取取消绑定请重新发送[扫码登录]登录账号'
logger.warning('game_token获取失败:非触发者本人扫码')
im = (
'{$@}' + f'检测到扫码登录UID{uid_check}与绑定UID{uid_bind}不同,'
'gametoken获取失败请重新发送[扫码登录]进行登录'
)
await send_group_msg(im, wxid_list)
else:
logger.warning('game_token获取失败')
im = 'game_token获取失败二维码已过期'
return await send_group_msg(im)
await send_group_msg('{$@}game_token获取失败二维码已过期', wxid_list)

View File

@ -2,24 +2,79 @@ import io
import base64
import asyncio
import traceback
from typing import List, NoReturn
import qrcode
from nonebot.log import logger
from nonebot.matcher import Matcher
from qrcode.constants import ERROR_CORRECT_L
from nonebot.adapters.ntchat import MessageSegment
from ..utils.message.send_msg import send_forward_msg
from .draw_topup_img import draw_wx, draw_ali
from ..utils.message.error_reply import UID_HINT
from ..utils.db_operation.db_operation import select_db
from ..utils.mhy_api.get_mhy_data import topup, checkorder, fetchgoods
disnote = '''免责声明:
该充值接口由米游社提供,不对充值结果负责。
请在充值前仔细阅读米哈游的充值条款。
'''
disnote = '''免责声明:该充值接口由米游社提供,不对充值结果负责。
请在充值前仔细阅读米哈游的充值条款。'''
GOODS = {
0: {
"title": "创世结晶×60",
"aliases": ["创世结晶x60", "结晶×60", "结晶x60", "创世结晶60", "结晶60"],
},
1: {
"title": "创世结晶×300",
"aliases": ["创世结晶x300", "结晶×300", "结晶x300", "创世结晶300", "结晶300", "30"],
},
2: {
"title": "创世结晶×980",
"aliases": ["创世结晶x980", "结晶×980", "结晶x980", "创世结晶980", "结晶980", "98"],
},
3: {
"title": "创世结晶×1980",
"aliases": [
"创世结晶x1980",
"结晶×1980",
"结晶x1980",
"创世结晶1980",
"结晶1980",
"198",
],
},
4: {
"title": "创世结晶×3280",
"aliases": [
"创世结晶x3280",
"结晶×3280",
"结晶x3280",
"创世结晶3280",
"结晶3280",
"328",
],
},
5: {
"title": "创世结晶×6480",
"aliases": [
"创世结晶x6480",
"结晶×6480",
"结晶x6480",
"创世结晶6480",
"结晶6480",
"648",
],
},
6: {
"title": "空月祝福",
"aliases": ["空月", "祝福", "月卡", "小月卡"],
},
}
def get_qrcode_base64(url):
qr = qrcode.QRCode(
version=1,
error_correction=qrcode.constants.ERROR_CORRECT_L,
error_correction=ERROR_CORRECT_L,
box_size=10,
border=4,
)
@ -27,12 +82,12 @@ def get_qrcode_base64(url):
qr.make(fit=True)
img = qr.make_image(fill_color="black", back_color="white")
img_byte = io.BytesIO()
img.save(img_byte, format="PNG")
img.save(img_byte, format="PNG") # type: ignore
img_byte = img_byte.getvalue()
return base64.b64encode(img_byte).decode()
async def refresh(order, uid):
async def refresh(order, uid, oid):
times = 0
while True:
await asyncio.sleep(5)
@ -40,53 +95,91 @@ async def refresh(order, uid):
if order_status != 900:
pass
else:
return "支付成功"
return "{$@}" + f"支付成功!\nUID:{uid}\n订单号:{oid}"
times += 1
if times > 60:
return "支付超时"
return "{$@}" + f"支付超时!\nUID:{uid}\n订单号:{oid}"
async def topup_(bot, qid, group_id, goods_id):
async def send_group_msg(msg: str):
await bot.call_action(
action='send_group_msg',
group_id=group_id,
message=msg,
async def topup_(matcher: Matcher, qid, group_id, goods_id, method):
async def send_group_msg(msg: str, at_list: List) -> NoReturn:
await matcher.finish(
MessageSegment.room_at_msg(content=msg, at_list=at_list)
)
return ""
wxid_list = []
wxid_list.append(qid)
uid = await select_db(qid, mode="uid")
if uid is None:
await send_group_msg("未绑定米游社账号")
return
await send_group_msg("{$@}" + UID_HINT, wxid_list)
fetchgoods_data = await fetchgoods()
try:
goods_data = fetchgoods_data[goods_id]
except Exception:
await send_group_msg("商品不存在,最大为" + str(len(fetchgoods_data) - 1))
return
try:
order = await topup(uid, goods_data)
except Exception:
await send_group_msg("出错了,可能是cookie过期或接口出错")
logger.warn(f"[充值] {group_id} {qid}")
return
try:
im = []
b64_data = get_qrcode_base64(order["encode_order"])
qrc = f'[CQ:image,file=base64://{b64_data}]'
im.append(f"充值uid{uid}")
im.append(
f"商品名称:{goods_data['goods_name']}×{str(goods_data['goods_unit'])}"
if int(goods_data['goods_unit']) > 0
else goods_data["goods_name"],
except Exception as e:
logger.warning(f'[充值]错误 {e}')
await send_group_msg(
"{$@}商品不存在,最大为" + str(len(fetchgoods_data) - 1), wxid_list
)
im.append(f'商品价格:{int(order["amount"])/100}')
im.append(f"订单号:{order['order_no']}")
im.append(disnote)
im.append(qrc)
await send_forward_msg(bot, group_id, "小助手", str(qid), im)
except Exception:
try:
order = await topup(uid, goods_data, method)
except Exception as e:
logger.warning(f"[充值] {group_id} {qid} 错误:{e}")
await send_group_msg("{$@}出错了,可能是Cookie过期或接口出错。", wxid_list)
try:
b64_data = get_qrcode_base64(order["encode_order"])
img_b64decode = base64.b64decode(b64_data)
qrimage = io.BytesIO(img_b64decode) # 二维码
item_icon_url: str = goods_data['goods_icon'] # 图标
item_id: str = goods_data['goods_id'] # 商品内部id
item_pay_url: str = order['encode_order'] # 支付链接
item_name_full = (
f"{goods_data['goods_name']}×{str(goods_data['goods_unit'])}"
)
# 物品名字(非月卡)
item_name: str = (
item_name_full
if int(goods_data['goods_unit']) > 0
else goods_data["goods_name"]
)
# 物品名字
item_price: str = order["currency"] + str(
int(order["amount"]) / 100
) # 价格
item_order_no: str = order['order_no'] # 订单号
item_create_time: int = order['create_time'] # 创建时间
if method == 'alipay':
img_data = await draw_ali(
uid,
item_name,
item_price,
item_order_no,
qrimage,
item_icon_url,
item_create_time,
item_id,
)
return MessageSegment.image(img_data)
else:
img_data = await draw_wx(
uid,
item_name,
item_price,
item_order_no,
item_icon_url,
item_create_time,
item_id,
)
msg_text = '{$@}' + f' UID{uid}\n{item_name}\n{item_create_time}'
msg_text2 = msg_text + f'\n\nZF{item_pay_url}\n\n{disnote}'
at_msg = MessageSegment.room_at_msg(
content=msg_text2, at_list=wxid_list
)
return MessageSegment.image(img_data) + at_msg
except Exception as e:
traceback.print_exc()
logger.warn(f"[充值] {group_id} 图片发送失败")
await send_group_msg(await refresh(order, uid))
logger.warning(f"[充值] {group_id} 图片发送失败: {e}")
await send_group_msg(
await refresh(order, uid, order['order_no']), wxid_list
)

View File

@ -1,10 +1,11 @@
from sqlmodel import SQLModel
from nonebot import get_driver
from nonebot.log import logger
from nonebot import get_bot, on_startup
from ..utils.db_operation.db_operation import config_check
config = get_bot().config
driver = get_driver()
config = driver.config
async def run_webconsole():
@ -16,22 +17,28 @@ async def run_webconsole():
user_auth_i18n.set_language('zh_CN')
# 导入app
from .mount_app import auth, site
try:
from .mount_app import auth, site
except RuntimeError:
logger.warning("当前 Driver 非 ReverseDriverWebConsole 已禁用")
return
logger.info('尝试挂载WebConsole')
await site.db.async_run_sync(
SQLModel.metadata.create_all, is_session=False # type: ignore
)
# 创建默认测试用户, 请及时修改密码!!!
await auth.create_role_user()
logger.info(
('WebConsole挂载成功' f'http://{config.HOST}:{config.PORT}/genshinuid')
logger.opt(colors=True).info(
(
'WebConsole挂载成功'
f'<blue>http://{config.host}:{config.port}/genshinuid</blue>'
)
)
@on_startup
@driver.on_startup
async def start_check():
if await config_check('OpenWeb'):
await run_webconsole()

View File

@ -1,5 +1,6 @@
import fastapi_amis_admin
from fastapi import Request
from nonebot.typing import overrides
from fastapi_user_auth import admin as user_auth_admin
from fastapi_amis_admin.admin import admin as amis_admin
from fastapi_amis_admin.utils.translation import i18n as _
@ -17,7 +18,6 @@ from fastapi_amis_admin.amis.components import (
ButtonToolbar,
)
from .nb2 import overrides
from ..version import GenshinUID_version
login_html = '''
@ -109,7 +109,7 @@ def attach_page_head(page: Page) -> Page:
Html(html=login_html),
Grid(
columns=[
{'body': [page.body], 'lg': 2, 'md': 4, 'valign': 'middle'}
{'body': [page.body], 'lg': 3, 'md': 4, 'valign': 'middle'}
],
align='center',
valign='middle',

View File

@ -1,15 +1,24 @@
# flake8: noqa
import platform
import contextlib
from pathlib import Path
from typing import Any, Set, Dict, List, Callable
from typing import (
TYPE_CHECKING,
Any,
Set,
List,
Union,
Callable,
Optional,
cast,
)
from quart import Quart
from nonebot import get_bot
import fastapi_amis_admin
from pydantic import BaseModel
from sqlmodel import Relationship
from sqlalchemy.orm import backref
from fastapi import FastAPI, Request
import fastapi_amis_admin # noqa: F401
from nonebot import get_app, get_driver
from fastapi_amis_admin import amis, admin
from fastapi_amis_admin.crud import BaseApiOut
from sqlalchemy.ext.asyncio import AsyncEngine
@ -40,8 +49,8 @@ from fastapi_amis_admin.amis.components import (
from ..version import GenshinUID_version
from ..genshinuid_user.add_ck import _deal_ck
from .login_page import amis_admin, user_auth_admin # noqa
from ..utils.db_operation.database.db_config import DATABASE_URL
from .login_page import amis_admin, user_auth_admin # noqa # 不要删
from ..utils.db_operation.database.models import (
Config,
UidData,
@ -50,21 +59,32 @@ from ..utils.db_operation.database.models import (
NewCookiesTable,
)
if TYPE_CHECKING:
from quart import Quart
from asgiref.typing import Scope, HTTPScope, WebSocketScope
AppType = Union[FastAPI, 'Quart']
class FastApiMiddleware:
def __init__(self, app: Quart, fastapi: FastAPI, routes: Set[str]) -> None:
def __init__(
self, app: 'Quart', fastapi: FastAPI, routes: Set[str]
) -> None:
self.fastapi = fastapi
self.app = app.asgi_app
self.routes = routes
async def __call__(
self, scope: Dict[str, Any], receive: Callable, send: Callable
self, scope: 'Scope', receive: Callable, send: Callable
) -> None:
if scope['type'] in ('http', 'websocket'):
scope = cast(Union['HTTPScope', 'WebSocketScope'], scope)
for path in self.routes:
if scope['path'].startswith(path):
return await self.fastapi(scope, receive, send)
return await self.app(scope, receive, send)
return await self.fastapi(
scope, receive, send # type: ignore
)
return await self.app(scope, receive, send) # type: ignore
# 自定义后台管理站点
@ -89,8 +109,28 @@ class GenshinUIDAdminSite(AuthAdminSite):
return app
# 创建FastAPI应用
app = FastAPI()
def get_asgi() -> Optional[AppType]:
try:
return get_app()
except AssertionError:
return None
# 从 nb 获取 FastAPI
app = get_asgi()
if app is None:
raise RuntimeError('App is not ReverseDriver')
with contextlib.suppress(ImportError):
from quart import Quart # noqa: F811
if isinstance(app, Quart):
fastapi = FastAPI()
app.asgi_app = FastApiMiddleware(app, fastapi, {'/genshinuid'}) # type: ignore
app = fastapi
app = cast(FastAPI, app)
settings = Settings(
database_url_async=DATABASE_URL,
database_url='',
@ -99,8 +139,6 @@ settings = Settings(
site_title='GenshinUID网页控制台',
language='zh_CN',
)
quart = get_bot().server_app
quart.asgi_app = FastApiMiddleware(quart, app, {'/genshinuid'}) # type: ignore
# 显示主键
@ -152,16 +190,13 @@ admin.BaseModelAdmin.get_create_form = patched_get_create_form
# 创建AdminSite实例
site = GenshinUIDAdminSite(settings)
auth = site.auth
config = get_bot().config
if float(fastapi_amis_admin.__version__[:3]) >= 0.3:
class MyUser(User, table=True):
point: float = Field(default=0, title=_('Point')) # type: ignore
phone: str = Field(None, title=_('Tel'), max_length=15) # type: ignore
parent_id: int = Field(
None, title=_('Parent'), foreign_key='auth_user.id' # type: ignore
)
parent_id: int = Field(None, title=_('Parent'), foreign_key='auth_user.id') # type: ignore
children: List['User'] = Relationship(
sa_relationship_kwargs=dict(
backref=backref('parent', remote_side='User.id'),
@ -170,9 +205,10 @@ if float(fastapi_amis_admin.__version__[:3]) >= 0.3:
auth.user_model = MyUser
config = get_driver().config
app.add_middleware(
CORSMiddleware,
allow_origins=[f'http://{config.HOST}:{config.PORT}/genshinuid'],
allow_origins=[f'http://{config.host}:{config.port}'],
allow_credentials=True,
allow_methods=['*'],
allow_headers=['*'],
@ -180,12 +216,17 @@ app.add_middleware(
gsuid_webconsole_help = '''
## 初次使用
欢迎进入网页控制台!
Admin账户可以通过左侧的选项进入不同的数据库直接修改,**首次登陆的Admin账户别忘了修改你的密码!**
普通账户可以通过左侧的选项进行绑定CK或者SK
未来还会加入更多功能!
## 丨我该如何获取Cookies[#92](https://github.com/KimigaiiWuyi/GenshinUID/issues/92)[@RemKeeper](https://github.com/RemKeeper)
```js
var cookie = document.cookie;
var Str_Num = cookie.indexOf('_MHYUUID=');
@ -198,12 +239,16 @@ if (ask == true) {
msg = 'Cancel'
}
```
1. 复制上面全部代码,然后打开[米游社BBS](https://bbs.mihoyo.com/ys/)
2. 在页面上右键检查或者Ctrl+Shift+i
3. 选择控制台Console粘贴回车在弹出的窗口点确认点完自动复制
4. 然后在和机器人的私聊窗口,粘贴发送即可
**警告Cookies属于个人隐私其效用相当于账号密码请勿随意公开**
## 丨获取米游社Stoken([AutoMihoyoBBS](https://github.com/Womsxd/AutoMihoyoBBS#%E8%8E%B7%E5%8F%96%E7%B1%B3%E6%B8%B8%E7%A4%BECookie))
```js
var cookie = document.cookie;
var ask = confirm('Cookie:' + cookie + '按确认然后粘贴至Cookies或者Login_ticket选框内');
@ -214,10 +259,12 @@ if (ask == true) {
msg = 'Cancel'
}
```
1. 复制上面全部代码,然后打开[米游社账户登录界面](http://user.mihoyo.com/)
2. 在页面上右键检查或者Ctrl+Shift+i
3. 选择控制台Console粘贴回车在弹出的窗口点确认点完自动复制
4. 然后在和机器人的私聊窗口,粘贴发送即可
**警告Cookies属于个人隐私其效用相当于账号密码请勿随意公开**
## 获取CK通则

View File

@ -1,15 +0,0 @@
from typing import TypeVar, Callable
T_Wrapped = TypeVar("T_Wrapped", bound=Callable)
def overrides(InterfaceClass: object) -> Callable[[T_Wrapped], T_Wrapped]:
"""标记一个方法为父类 interface 的 implement"""
def overrider(func: T_Wrapped) -> T_Wrapped:
assert func.__name__ in dir(
InterfaceClass
), f"Error method: {func.__name__}"
return func
return overrider

View File

@ -1,161 +1,269 @@
import re
from nonebot import MessageSegment
from hoshino.typing import CQEvent, HoshinoBot
from nonebot import on_command
from nonebot.matcher import Matcher
from nonebot.params import CommandArg
from nonebot.adapters.ntchat.message import Message
from ..base import sv
from ..config import priority
from ..genshinuid_meta import register_menu
from ..utils.exception.handle_exception import handle_exception
from .get_wiki_text import (
char_wiki,
audio_wiki,
foods_wiki,
weapon_wiki,
enemies_wiki,
artifacts_wiki,
)
get_weapon = on_command('武器', priority=priority)
get_char = on_command('角色', priority=priority)
get_cost = on_command('材料', priority=priority)
get_polar = on_command('命座', priority=priority)
get_talents = on_command('天赋', priority=priority)
get_enemies = on_command('原魔', priority=priority)
get_audio = on_command('语音', priority=priority)
get_artifacts = on_command('圣遗物', priority=priority)
get_food = on_command('食物', priority=priority)
@sv.on_prefix('语音')
async def send_audio(bot: HoshinoBot, ev: CQEvent):
if ev.message:
message = ev.message.extract_plain_text().replace(' ', '')
else:
return
if message == '':
return
# 语音暂时不支持
'''
@get_audio.handle()
@handle_exception('语音', '语音发送失败可能是FFmpeg环境未配置。')
@register_menu(
'角色语音',
'语音[ID]',
'获取角色语音',
detail_des=(
'介绍:\n'
'获取角色语音\n'
'获取语言ID列表请使用指令 <ft color=(238,120,0)>语音列表</ft>\n'
' \n'
'指令:\n'
'- <ft color=(238,120,0)>语音{列表|</ft>'
'<ft color=(0,148,200)>[语音ID]</ft>'
'<ft color=(238,120,0)>}</ft>'
),
)
async def send_audio(matcher: Matcher, args: Message = CommandArg()):
message = args.extract_plain_text().strip().replace(' ', '')
name = ''.join(re.findall('[\u4e00-\u9fa5]', message))
im = await audio_wiki(name, message)
if name == '列表':
await bot.send(ev, MessageSegment.image(im))
await matcher.finish(MessageSegment.image(im))
else:
if isinstance(im, str):
await bot.send(ev, im)
await matcher.finish(im)
else:
await bot.send(ev, MessageSegment.record(im))
await matcher.finish(MessageSegment.record(im))
'''
@sv.on_prefix('原魔')
async def send_enemies(bot: HoshinoBot, ev: CQEvent):
if ev.message:
message = ev.message.extract_plain_text().replace(' ', '')
else:
return
if message == '':
return
@get_enemies.handle()
@handle_exception('怪物')
@register_menu(
'怪物图鉴',
'怪物xx',
'获取怪物Wiki',
detail_des=(
'介绍:\n'
'获取怪物Wiki\n'
' \n'
'指令:\n'
'- <ft color=(238,120,0)>怪物</ft><ft color=(0,148,200)>[怪物名称]</ft>'
),
)
async def send_enemies(matcher: Matcher, args: Message = CommandArg()):
message = args.extract_plain_text().strip().replace(' ', '')
im = await enemies_wiki(message)
await bot.send(ev, im)
await matcher.finish(im)
@sv.on_prefix('食物')
async def send_food(bot: HoshinoBot, ev: CQEvent):
if ev.message:
message = ev.message.extract_plain_text().replace(' ', '')
else:
return
if message == '':
return
@get_food.handle()
@handle_exception('食物')
@register_menu(
'食物图鉴',
'食物xx',
'获取食物Wiki',
detail_des=(
'介绍:\n'
'获取食物Wiki\n'
' \n'
'指令:\n'
'- <ft color=(238,120,0)>食物</ft><ft color=(0,148,200)>[食物名称]</ft>'
),
)
async def send_food(matcher: Matcher, args: Message = CommandArg()):
message = args.extract_plain_text().strip().replace(' ', '')
im = await foods_wiki(message)
await bot.send(ev, im)
await matcher.finish(im)
@sv.on_prefix('圣遗物')
async def send_artifacts(bot: HoshinoBot, ev: CQEvent):
if ev.message:
message = ev.message.extract_plain_text().replace(' ', '')
else:
return
if message == '':
return
@get_artifacts.handle()
@handle_exception('圣遗物')
@register_menu(
'圣遗物图鉴',
'圣遗物xx',
'获取怪物Wiki',
detail_des=(
'介绍:\n'
'获取圣遗物Wiki\n'
' \n'
'指令:\n'
'- <ft color=(238,120,0)>圣遗物</ft><ft color=(0,148,200)>[圣遗物名称]</ft>'
),
)
async def send_artifacts(matcher: Matcher, args: Message = CommandArg()):
message = args.extract_plain_text().strip().replace(' ', '')
im = await artifacts_wiki(message)
await bot.send(ev, im)
await matcher.finish(im)
@sv.on_prefix('武器')
async def send_weapon(bot: HoshinoBot, ev: CQEvent):
if ev.message:
message = ev.message.extract_plain_text().replace(' ', '')
else:
return
if message == '':
return
@get_weapon.handle()
@handle_exception('武器')
@register_menu(
'武器图鉴',
'武器xx',
'获取武器Wiki',
detail_des=(
'介绍:\n'
'获取武器Wiki\n'
' \n'
'指令:\n'
'- <ft color=(238,120,0)>武器</ft><ft color=(0,148,200)>[武器名称]</ft>'
'<ft color=(125,125,125)>(级数)</ft>\n'
' \n'
'示例:\n'
'- <ft color=(238,120,0)>武器雾切</ft>\n'
'- <ft color=(238,120,0)>武器无工之剑90</ft>'
),
)
async def send_weapon(matcher: Matcher, args: Message = CommandArg()):
message = args.extract_plain_text().strip().replace(' ', '')
name = ''.join(re.findall('[\u4e00-\u9fa5]', message))
level = re.findall(r'\d+', message)
if len(level) == 1:
im = await weapon_wiki(name, level=level[0])
else:
im = await weapon_wiki(name)
await bot.send(ev, im)
await matcher.finish(im)
@sv.on_prefix('天赋')
async def send_talents(bot: HoshinoBot, ev: CQEvent):
if ev.message:
message = ev.message.extract_plain_text().replace(' ', '')
else:
return
if message == '':
return
# 天赋暂时不支持
'''
@get_talents.handle()
@handle_exception('天赋')
@register_menu(
'天赋效果',
'天赋[角色][天赋序号]',
'查询角色天赋技能效果',
detail_des=(
'介绍:\n'
'查询角色天赋技能效果\n'
' \n'
'指令:\n'
'- <ft color=(238,120,0)>天赋</ft>'
'<ft color=(0,148,200)>[角色名称][天赋序号]</ft>\n'
' \n'
'示例:\n'
'- <ft color=(238,120,0)>天赋绫华2</ft>'
),
)
async def send_talents(
bot: Bot,
matcher: Matcher,
args: Message = CommandArg(),
):
message = args.extract_plain_text().strip().replace(' ', '')
name = ''.join(re.findall('[\u4e00-\u9fa5]', message))
num = re.findall(r'\d+', message)
if len(num) == 1:
im = await char_wiki(name, 'talents', num[0])
if isinstance(im, list):
await bot.send_group_forward_msg(group_id=ev.group_id, messages=im)
return
await bot.call_api(
'send_group_forward_msg', group_id=event.group_id, messages=im
)
await matcher.finish()
else:
im = '参数不正确。'
await bot.send(ev, im)
await matcher.finish(im)
'''
@sv.on_prefix('角色')
async def send_char(bot: HoshinoBot, ev: CQEvent):
if ev.message:
message = ev.message.extract_plain_text().replace(' ', '')
else:
return
if message == '':
return
@get_char.handle()
@handle_exception('角色')
@register_menu(
'角色图鉴',
'角色xx',
'获取角色Wiki',
detail_des=(
'介绍:\n'
'获取角色Wiki\n'
' \n'
'指令:\n'
'- <ft color=(238,120,0)>角色</ft><ft color=(0,148,200)>[角色名称]</ft>'
'<ft color=(125,125,125)>(等级)</ft>\n'
' \n'
'示例:\n'
'- <ft color=(238,120,0)>角色可莉</ft>\n'
'- <ft color=(238,120,0)>角色可莉90</ft>'
),
)
async def send_char(matcher: Matcher, args: Message = CommandArg()):
message = args.extract_plain_text().strip().replace(' ', '')
name = ''.join(re.findall('[\u4e00-\u9fa5]', message))
level = re.findall(r'\d+', message)
if len(level) == 1:
im = await char_wiki(name, 'char', level=level[0])
else:
im = await char_wiki(name)
await bot.send(ev, im)
await matcher.finish(im)
@sv.on_prefix('材料')
async def send_cost(bot: HoshinoBot, ev: CQEvent):
if ev.message:
message = ev.message.extract_plain_text().replace(' ', '')
else:
return
if message == '':
return
@get_cost.handle()
@handle_exception('材料')
@register_menu(
'材料图鉴',
'材料xx',
'获取材料Wiki',
detail_des=(
'介绍:\n'
'获取材料Wiki\n'
' \n'
'指令:\n'
'- <ft color=(238,120,0)>材料</ft><ft color=(0,148,200)>[材料名称]</ft>'
),
)
async def send_cost(matcher: Matcher, args: Message = CommandArg()):
message = args.extract_plain_text().strip().replace(' ', '')
im = await char_wiki(message, 'costs')
await bot.send(ev, im)
await matcher.finish(im)
@sv.on_prefix('命座')
async def send_polar(bot: HoshinoBot, ev: CQEvent):
if ev.message:
message = ev.message.extract_plain_text().replace(' ', '')
else:
return
if message == '':
return
@get_polar.handle()
@handle_exception('命座')
@register_menu(
'角色命座图鉴',
'命座[角色][等级]',
'获取角色命座Wiki',
detail_des=(
'介绍:\n'
'获取角色命座Wiki\n'
' \n'
'指令:\n'
'- <ft color=(238,120,0)>命座</ft>'
'<ft color=(0,148,200)>[角色名称][命座等级]</ft>\n'
' \n'
'示例:\n'
'- <ft color=(238,120,0)>命座胡桃1</ft>'
),
)
async def send_polar(matcher: Matcher, args: Message = CommandArg()):
message = args.extract_plain_text().strip().replace(' ', '')
num = int(re.findall(r'\d+', message)[0]) # str
m = ''.join(re.findall('[\u4e00-\u9fa5]', message))
if num <= 0 or num > 6:
await bot.send(ev, '你家{}{}命?'.format(m, num))
await get_polar.finish('你家{}{}命?'.format(m, num))
return
im = await char_wiki(m, 'constellations', num)
await bot.send(ev, im)
await matcher.finish(im)

View File

@ -2,22 +2,34 @@ import random
import asyncio
import threading
from ..base import sv, logger
from nonebot import on_command
from nonebot.log import logger
from nonebot.matcher import Matcher
from nonebot_plugin_apscheduler import scheduler
from nonebot.adapters.ntchat import MessageSegment
from .draw_abyss_total import TOTAL_IMG, draw_xk_abyss_img
from ..utils.draw_image_tools.send_image_tool import convert_img
from ..utils.exception.handle_exception import handle_exception
draw_total_scheduler = scheduler
get_abyss_total = on_command('深渊概览', aliases={'深渊统计', '深渊使用率'})
@sv.scheduled_job('interval', hours=3)
@draw_total_scheduler.scheduled_job('interval', hours=3)
async def scheduled_draw_abyss():
await asyncio.sleep(random.randint(0, 60))
await draw_xk_abyss_img()
@sv.on_fullmatch(('深渊概览', '深渊统计', '深渊使用率'))
async def send_abyss_pic(bot, ev):
img = await convert_img(TOTAL_IMG)
logger.info('获得gs帮助图片成功')
await bot.send(ev, img)
@get_abyss_total.handle()
@handle_exception('深渊概览')
async def send_guide_pic(matcher: Matcher):
if TOTAL_IMG.exists():
logger.info('获得深渊概览成功!')
with open(TOTAL_IMG, 'rb') as f:
await matcher.finish(MessageSegment.image(f.read()))
else:
await matcher.finish('深渊概览图不存在!')
threading.Thread(

View File

@ -11,7 +11,7 @@ class UidDataDAL:
def __init__(self, db_session: Session):
self.db_session = db_session
async def get_user_data(self, userid: int) -> Optional[UidData]:
async def get_user_data(self, userid: str) -> Optional[UidData]:
"""
:说明:
获取用户的绑定信息
@ -37,7 +37,7 @@ class UidDataDAL:
bind_list.append(item.__dict__)
return bind_list
async def user_exists(self, userid: int) -> bool:
async def user_exists(self, userid: str) -> bool:
"""
:说明:
是否存在用户的绑定信息
@ -67,7 +67,7 @@ class UidDataDAL:
uid_list.extend(item.UID.split("_"))
return uid_list
async def get_uid_list(self, userid: int) -> List[str]:
async def get_uid_list(self, userid: str) -> List[str]:
"""
:说明:
获得用户的绑定UID列表
@ -83,7 +83,7 @@ class UidDataDAL:
return uid_list
return []
async def get_uid(self, userid: int) -> str:
async def get_uid(self, userid: str) -> str:
"""
:说明:
获得用户绑定的UID
@ -98,14 +98,14 @@ class UidDataDAL:
else:
return '未找到绑定的UID~'
async def switch_uid(self, userid: int, uid: Optional[str] = None) -> str:
async def switch_uid(self, userid: str, uid: Optional[str] = None) -> str:
"""
:说明:
切换绑定的UID列表,绑定一个UID的情况下返回无法切换
切换前 -> 12_13_14
切换后 -> 13_12_14
:参数:
* userid (int): QQ号。
* userid (int): WX号。
* uid (str): 切换的UID, 可选, 不传入的话自动切换下一个。
:返回:
* msg (str): 回调信息。
@ -126,7 +126,7 @@ class UidDataDAL:
else:
return '你还没有绑定过UID噢~'
async def get_mysid_list(self, userid: int) -> List[str]:
async def get_mysid_list(self, userid: str) -> List[str]:
"""
:说明:
获得用户的绑定MYSID列表
@ -142,7 +142,7 @@ class UidDataDAL:
return mys_list
return []
async def get_mysid(self, userid: int) -> str:
async def get_mysid(self, userid: str) -> str:
"""
:说明:
获得用户绑定的MYSID
@ -157,7 +157,7 @@ class UidDataDAL:
else:
return "未找到绑定的MYSID~"
async def get_anyid(self, userid: int) -> str:
async def get_anyid(self, userid: str) -> str:
"""
:说明:
获得用户绑定的ID信息
@ -176,7 +176,7 @@ class UidDataDAL:
else:
return '未找到绑定的ID信息~'
async def bind_db(self, userid: int, data: dict) -> str:
async def bind_db(self, userid: str, data: dict) -> str:
"""
:说明:
绑定UID或者MYSID
@ -229,7 +229,7 @@ class UidDataDAL:
await self.db_session.flush() # type: ignore
return _bind
async def delete_db(self, userid: int, data: Optional[dict]) -> str:
async def delete_db(self, userid: str, data: Optional[dict]) -> str:
"""
:说明:
删除用户的绑定UID或者MYSID
@ -284,7 +284,7 @@ class UidDataDAL:
async def update_db(
self,
userid: int,
userid: str,
data: Optional[dict],
):
"""

View File

@ -33,7 +33,7 @@ class PushData(SQLModel, table=True):
class UidData(SQLModel, table=True):
__table_args__ = {'keep_existing': True}
USERID: int = Field(default=100000000, primary_key=True, title='QQ')
USERID: str = Field(default='WX_100001', primary_key=True, title='WX')
UID: Optional[str] = Field(title='UID')
MYSID: Optional[str] = Field(title='米游社通行证(废弃)')
@ -42,7 +42,7 @@ class NewCookiesTable(SQLModel, table=True):
__table_args__ = {'keep_existing': True}
UID: int = Field(default=100000000, primary_key=True, title='UID')
Cookies: str = Field(title='CK')
QID: int = Field(title='QQ号')
QID: str = Field(title='QQ号')
StatusA: str = Field(title='全局推送开关')
StatusB: str = Field(title='自动签到')
StatusC: str = Field(title='自动米游币')

View File

@ -27,7 +27,9 @@ async def check_db():
try:
if int(str(row[0])[0]) < 6:
aid = re.search(r'account_id=(\d*)', row[1])
mihoyo_id_data = aid.group(0).split('=') # type: ignore
if not aid:
raise Exception('cannot match account_id')
mihoyo_id_data = aid.group(0).split('=')
mihoyo_id = mihoyo_id_data[1]
mys_data = await get_mihoyo_bbs_info(mihoyo_id, row[1])
for i in mys_data['data']['list']:

View File

@ -92,7 +92,7 @@ async def get_all_stoken() -> List:
async def select_db(
userid: int, mode: str = 'auto'
userid: str, mode: str = 'auto'
) -> Union[List[str], str, None]:
"""
:说明:
@ -126,14 +126,14 @@ async def select_db(
return im
async def switch_db(userid: int, uid: Optional[str] = None) -> str:
async def switch_db(userid: str, uid: Optional[str] = None) -> str:
"""
:说明:
切换绑定的UID列表,绑定一个UID的情况下返回无法切换
切换前 -> 12_13_14
切换后 -> 13_14_12
:参数:
* userid (str): QQ号。
* userid (str): WX号。
:返回:
* im (str): 回调信息。
"""
@ -144,7 +144,7 @@ async def switch_db(userid: int, uid: Optional[str] = None) -> str:
return im
async def delete_db(userid: int, data: dict) -> str:
async def delete_db(userid: str, data: dict) -> str:
"""
:说明:
删除当前绑定的UID信息

View File

@ -1,9 +1,7 @@
import sys
from pathlib import Path
import hoshino
MAIN_PATH = Path(hoshino.config.RES_DIR) / 'GenshinUID'
MAIN_PATH = Path() / 'data' / 'GenshinUID'
sys.path.append(str(MAIN_PATH))
CONFIG_PATH = MAIN_PATH / 'config.json'
RESOURCE_PATH = MAIN_PATH / 'resource'

View File

@ -184,10 +184,7 @@ async def get_level_pic(level: int) -> Image.Image:
async def get_qq_avatar(
qid: Optional[Union[int, str]] = None, avatar_url: Optional[str] = None
) -> Image.Image:
if qid:
avatar_url = f'http://q1.qlogo.cn/g?b=qq&nk={qid}&s=640'
elif avatar_url is None:
avatar_url = 'https://q1.qlogo.cn/g?b=qq&nk=3399214199&s=640'
avatar_url = 'https://s2.loli.net/2022/01/31/kwCIl3cF1Z2GxnR.png'
char_pic = Image.open(BytesIO(get(avatar_url).content)).convert('RGBA')
return char_pic

View File

@ -52,4 +52,4 @@ async def convert_img(
else:
async with aiofiles.open(img, 'rb') as fp:
img = await fp.read()
return f'[CQ:image,file=base64://{b64encode(img).decode()}]'
return f'base64://{b64encode(img).decode()}'

View File

@ -1,53 +1,56 @@
# flake8: noqa
# from nonebot.log import logger
# from aiocqhttp.exceptions import ActionFailed
# from hoshino.typing import List, CQEvent, Iterable
from functools import wraps
from typing import Optional
# FIXME: - 在 Hoshino 中可用
# def handle_exception(name: str, log_msg: str = None, fail_msg: str = None): # type: ignore
# """
# :说明:
# 捕获命令执行过程中发生的异常并回报。
# :参数:
# * ``name: str``: 项目的名称。
# * ``log_msg: str = None``: 自定义捕获异常后在日志中留存的信息。留空则使用默认信息。
# * ``fail_msg: str = None``: 自定义捕获异常后向用户回报的信息,仅在提供自定义日志信息时有效。开头带@则艾特用户。留空则与日志信息相同。
# """
from nonebot.log import logger
from nonebot.matcher import Matcher
from nonebot.exception import FinishedException
# def wrapper(func):
# @wraps(func)
# async def inner(
# log_msg: str = log_msg, fail_msg: str = fail_msg, **kwargs
# ):
# print(kwargs)
# matcher: CQEvent = kwargs['matcher']
# try:
# await func(**kwargs)
# except ActionFailed as e:
# # 此为bot本身由于风控或网络问题发不出消息并非代码本身出问题
# await matcher.send(f'发送消息失败{e.result["wording"]}')
# logger.exception(f'发送{name}消息失败')
# except Exception as e:
# # 如果e内包含敏感信息应在此处进行处理
# if 'SQL' in str(e):
# e = '数据库操作失败!可能正在[校验全部Cookies]\n敏感信息已抹去...请等待一段时间后重试!'
# # 代码本身出问题
# if log_msg:
# if not fail_msg:
# fail_msg = log_msg
# if fail_msg[0] == '@':
# await matcher.send(
# f'{fail_msg[1:]}\n错误信息为{e}', at_sender=True
# )
# else:
# await matcher.send(f'{fail_msg}\n错误信息为{e}')
# if log_msg[0] == '@':
# log_msg = log_msg[1:]
# logger.exception(log_msg)
# else:
# await matcher.send(f'发生错误 {e},请检查后台输出。')
# logger.exception(f'获取{name}信息错误')
# return inner
def handle_exception(
name: str, log_msg: Optional[str] = None, fail_msg: Optional[str] = None
):
"""
:说明:
捕获命令执行过程中发生的异常并回报。
:参数:
* ``name: str``: 项目的名称。
* ``log_msg: str = None``: 自定义捕获异常后在日志中留存的信息。留空则使用默认信息。
* ``fail_msg: str = None``: 自定义捕获异常后向用户回报的信息,仅在提供自定义日志信息时有效。
开头带@则艾特用户。留空则与日志信息相同。
"""
# return wrapper
def wrapper(func):
@wraps(func)
async def inner(
log_msg: Optional[str] = log_msg,
fail_msg: Optional[str] = fail_msg,
**kwargs,
):
matcher: Matcher = kwargs['matcher']
try:
await func(**kwargs)
except FinishedException:
# `finish` 会抛出此异常,应予以抛出而不处理
raise
except Exception as e:
# 如果e内包含敏感信息应在此处进行处理
if 'SQL' in str(e):
e = '数据库操作失败!可能正在[校验全部Cookies]\n敏感信息已抹去...请等待一段时间后重试!'
# 代码本身出问题
if log_msg:
if not fail_msg:
fail_msg = log_msg
if fail_msg[0] == '@':
await matcher.send(f'{fail_msg[1:]}\n错误信息为{e}')
else:
await matcher.send(f'{fail_msg}\n错误信息为{e}')
if log_msg[0] == '@':
log_msg = log_msg[1:]
logger.exception(log_msg)
else:
await matcher.send(f'发生错误 {e},请检查后台输出。')
logger.exception(f'获取{name}信息错误')
return inner
return wrapper

View File

@ -1,18 +1,20 @@
from typing import List
from nonebot.adapters.ntchat import Bot
async def get_all_friend_list(bot) -> List[int]:
async def get_all_friend_list(bot: Bot) -> List[int]:
friend_list = []
friend_dict_list = await bot.call_action(action='get_friend_list')
friend_dict_list = await bot.call_api('get_contacts')
for friend in friend_dict_list:
friend_list.append(friend['user_id'])
return friend_list
async def get_group_member_list(bot, group_id: int) -> List[int]:
async def get_group_member_list(bot: Bot, group_id: int) -> List[int]:
member_list = []
group_member_list = await bot.call_api(
action='get_group_member_list', group_id=group_id
'get_room_members', group_id=group_id
)
for group_member in group_member_list:
member_list.append(group_member['user_id'])

View File

@ -1,9 +1,11 @@
from typing import Union
from nonebot.adapters.onebot.v11 import MessageEvent
# https://v2.nonebot.dev/docs/advanced/di/dependency-injection#class-%E4%BD%9C%E4%B8%BA%E4%BE%9D%E8%B5%96
class ImageAndAt:
def __init__(self, event):
def __init__(self, event: MessageEvent):
self.images = []
self.at = []
for i in event.message:

View File

@ -1,12 +1,12 @@
from typing import List
from ...base import hoshino_bot
from nonebot.adapters.qqguild import Bot
# 发送聊天记录
async def send_forward_msg(
bot: hoshino_bot,
group_id: int,
bot: Bot,
userid: int,
name: str,
uin: str,
msgs: List[str],
@ -18,6 +18,6 @@ async def send_forward_msg(
}
messages = [to_json(msg) for msg in msgs]
await bot.call_action(
action='send_group_forward_msg', group_id=group_id, messages=messages
await bot.call_api(
"send_private_forward_msg", user_id=userid, messages=messages
)

View File

@ -877,7 +877,7 @@ async def fetchgoods():
return goodslist["data"]["goods_list"]
async def topup(uid, goods):
async def topup(uid, goods, method):
device_id = str(uuid.uuid4())
HEADER = copy.deepcopy(_HEADER)
HEADER["Cookie"] = await owner_cookies(uid)
@ -902,7 +902,7 @@ async def topup(uid, goods):
"price_tier": goods["tier_id"],
# "price_tier": "Tier_1",
"currency": "CNY",
"pay_plat": "alipay",
"pay_plat": method,
}
data = {"order": order, "sign": gen_payment_sign(order)}
HEADER["x-rpc-device_id"] = device_id

View File

@ -0,0 +1,15 @@
from nonebot.rule import Rule
from nonebot.params import Depends, CommandArg
from nonebot.adapters.ntchat.message import Message
async def full_command(arg: Message = CommandArg()) -> bool:
return not bool(str(arg))
def FullCommand() -> Rule:
return Rule(full_command)
def FullCommandDepend():
return Depends(full_command)

View File

@ -0,0 +1,15 @@
from typing import List
from nonebot import get_driver
def get_superusers() -> List[int]:
config = get_driver().config
superusers: List[int] = []
for su in config.superusers:
if "onebot:" in su:
# su[7:] == onebot:
su = su[7:]
if su.isdigit():
superusers.append(int(su))
return superusers

View File

@ -3,11 +3,11 @@ import shutil
from typing import List
from pathlib import Path
import hoshino
from nonebot.log import logger
from nonebot import load_plugins
from nonebot import require, load_plugins
dir_ = Path(__file__).parent
require('nonebot_plugin_apscheduler')
def copy_and_delete_files():
@ -27,7 +27,7 @@ def copy_and_delete_files():
elif path.name in {"resource", "player"}:
dst = Path().joinpath(
(
hoshino.config.RES_DIR + "/GenshinUID/"
"data/GenshinUID/"
f"{'players' if path.name == 'player' else path.name}"
)
)
@ -37,7 +37,7 @@ def copy_and_delete_files():
dst,
dirs_exist_ok=True,
)
logger.info(f"复制文件夹 {path} -> {dst} 成功")
logger.success(f"复制文件夹 {path} -> {dst} 成功")
except shutil.Error as e:
failed_src: List[Path] = []
exc: str
@ -56,10 +56,10 @@ def copy_and_delete_files():
else:
try:
shutil.rmtree(path)
logger.info(f"删除文件夹 {path} 成功")
logger.success(f"删除文件夹 {path} 成功")
except OSError as e:
logger.warning(f"删除文件夹 {path} 失败:{e.strerror}")
copy_and_delete_files()
load_plugins(str(dir_ / 'GenshinUID'), 'hoshino.modules.GenshinUID.GenshinUID')
load_plugins(str(dir_ / "GenshinUID"))

14
deploy/.env.dev Normal file
View File

@ -0,0 +1,14 @@
# 配置 NoneBot2 监听的 IP/主机名(不建议修改)
HOST=0.0.0.0
# 配置 NoneBot2 监听的端口(不建议修改)
PORT=8080
# 配置 NoneBot 超级用户例如你的QQ号
SUPERUSERS=["123456789"]
# 配置命令起始字符
COMMAND_START=["/", ""]
# 建议别删下面这行
FASTAPI_RELOAD=false
# 建议别删上面这行
更多配置参见https://v2.nonebot.dev/docs/tutorial/configuration

14
deploy/cookiecutter.yml Normal file
View File

@ -0,0 +1,14 @@
default_context:
py: py
project_name: nb2
use_src: true
adapters:
- builtin:
- module_name: "nonebot.adapters.onebot.v11"
project_link: "nonebot-adapter-onebot"
name: "OneBot V11"
desc: "OneBot V11 协议"
author: "yanyongyu"
homepage: "https://onebot.adapters.nonebot.dev/"
tags: []
is_official: true

View File

@ -0,0 +1,21 @@
from typing import List
import tomlkit
def main():
with open("./nb2/pyproject.toml", "r", encoding="utf-8") as f:
data = tomlkit.load(f)
plugins: List[str] = data["tool"]["nonebot"]["plugins"] # type: ignore
if "GenshinUID" in plugins:
return
plugins.append("GenshinUID")
with open("./nb2/pyproject.toml", "w", encoding="utf-8") as f:
tomlkit.dump(data, f)
print("Update pyproject.toml done.")
if __name__ == "__main__":
main()

1772
poetry.lock generated

File diff suppressed because it is too large Load Diff

View File

@ -1,34 +1,3 @@
[tool.poetry]
name = "GenshinUID"
version = "0.1.0"
description = "基于HoshinoBot/NoneBot2/QQ官方频道Bot(PythonSDK)的原神Uid查询/原神Wiki/米社签到/树脂提醒插件"
authors = ["KimigaiiWuyi <444835641@qq.com>"]
license = "GPL-3.0-or-later"
[tool.poetry.dependencies]
python = "^3.8"
httpx = "^0.23.0"
beautifulsoup4 = "^4.11.1"
lxml = "^4.9.1"
openpyxl = "^3.0.10"
aiohttp = "^3.8.1"
SQLAlchemy = "^1.4.39"
Pillow = "^9.2.0"
aiosqlite = "^0.17.0"
sqlmodel = "^0.0.8"
GitPython = "^3.1.27"
fastapi_amis_admin = "^0.2.1"
fastapi_user_auth = "^0.2.1"
pydantic = "^1.10.2"
aiofiles = "^22.1.0"
qrcode = {extras = ["pil"], version = "^7.3.1"}
[tool.poetry.dev-dependencies]
flake8 = "^4.0.1"
black = "^22.6.0"
isort = "^5.11.5"
pre-commit = "^2.20.0"
[tool.black]
line-length = 79
target-version = ["py38", "py39", "py310"]
@ -45,10 +14,63 @@ skip_gitignore = true
force_sort_within_sections = true
extra_standard_library = ["typing_extensions"]
[tool.pytest.ini_options]
asyncio_mode = "auto"
[build-system]
requires = ["poetry-core>=1.0.0"]
build-backend = "poetry.core.masonry.api"
[tool.poetry]
name = "GenshinUID"
version = "3.1.0"
description = "基于HoshinoBot/NoneBot2/QQ官方频道/微信Bot的原神面板查询/原神Wiki/米社签到/树脂提醒插件"
authors = ["KimigaiiWuyi <444835641@qq.com>"]
license = "GPL-3.0-or-later"
readme = "README.md"
homepage = "https://github.com/KimigaiiWuyi/GenshinUID/tree/ntchat"
repository = "https://github.com/KimigaiiWuyi/GenshinUID"
documentation = "https://github.com/KimigaiiWuyi/GenshinUID/wiki"
packages = [
{ include = "GenshinUID" }
]
exclude = ["tests", "deploy"]
[tool.poetry.urls]
"Bug Tracker" = "https://github.com/KimigaiiWuyi/GenshinUID/issues"
[tool.poetry.dependencies]
python = "^3.8.1"
nonebot2 = ">=2.0.0b4"
nonebot-adapter-ntchat = ">=0.3.4"
httpx = ">=0.23.0"
beautifulsoup4 = ">=4.11.1"
lxml = ">=4.9.2"
openpyxl = ">=3.0.10"
aiohttp = ">=3.8.1"
sqlalchemy = ">=1.4.39"
pillow = ">=9.2.0"
aiosqlite = ">=0.17.0"
nonebot-plugin-apscheduler = ">=0.1.4"
aiofiles = ">=0.8.0"
sqlmodel = ">=0.0.8"
gitpython = ">=3.1.27"
fastapi-amis-admin = ">=0.2.1"
fastapi-user-auth = ">=0.2.1"
qrcode = {extras = ["pil"], version = "^7.3.1"}
[[tool.poetry.source]]
name = 'aliyun.mirrors'
url = "http://mirrors.aliyun.com/pypi/simple/"
name = "USTC"
url = "https://pypi.mirrors.ustc.edu.cn/simple"
default = false
secondary = true
[tool.poetry.group.dev.dependencies]
flake8 = "^6.0.0"
black = "^22.12.0"
isort = "^5.11.5"
pre-commit = "^2.21.0"
nonebug = "^0.2.2"
pytest = "^7.2.0"
pytest-asyncio = "^0.20.3"
pycln = "^2.1.2"

View File

@ -1,50 +1,64 @@
--trusted-host mirrors.aliyun.com
--extra-index-url http://mirrors.aliyun.com/pypi/simple
aiofiles==22.1.0 ; python_version >= "3.8" and python_version < "4.0"
aiohttp==3.8.3 ; python_version >= "3.8" and python_version < "4.0"
aiosignal==1.3.1 ; python_version >= "3.8" and python_version < "4.0"
aiosqlite==0.17.0 ; python_version >= "3.8" and python_version < "4.0"
anyio==3.6.2 ; python_version >= "3.8" and python_version < "4.0"
async-timeout==4.0.2 ; python_version >= "3.8" and python_version < "4.0"
attrs==22.2.0 ; python_version >= "3.8" and python_version < "4.0"
bcrypt==4.0.1 ; python_version >= "3.8" and python_version < "4.0"
beautifulsoup4==4.11.1 ; python_version >= "3.8" and python_version < "4.0"
certifi==2022.12.7 ; python_version >= "3.8" and python_version < "4.0"
charset-normalizer==2.1.1 ; python_version >= "3.8" and python_version < "4.0"
colorama==0.4.6 ; python_version >= "3.8" and python_version < "4.0" and platform_system == "Windows"
dnspython==2.2.1 ; python_version >= "3.8" and python_version < "4.0"
email-validator==1.3.0 ; python_version >= "3.8" and python_version < "4.0"
et-xmlfile==1.1.0 ; python_version >= "3.8" and python_version < "4.0"
fastapi-amis-admin==0.2.5 ; python_version >= "3.8" and python_version < "4.0"
fastapi-user-auth==0.2.4 ; python_version >= "3.8" and python_version < "4.0"
fastapi==0.88.0 ; python_version >= "3.8" and python_version < "4.0"
frozenlist==1.3.3 ; python_version >= "3.8" and python_version < "4.0"
gitdb==4.0.10 ; python_version >= "3.8" and python_version < "4.0"
gitpython==3.1.30 ; python_version >= "3.8" and python_version < "4.0"
greenlet==2.0.1 ; python_version >= "3.8" and (platform_machine == "aarch64" or platform_machine == "ppc64le" or platform_machine == "x86_64" or platform_machine == "amd64" or platform_machine == "AMD64" or platform_machine == "win32" or platform_machine == "WIN32") and python_version < "4.0"
h11==0.14.0 ; python_version >= "3.8" and python_version < "4.0"
httpcore==0.16.3 ; python_version >= "3.8" and python_version < "4.0"
httpx==0.23.1 ; python_version >= "3.8" and python_version < "4.0"
idna==3.4 ; python_version >= "3.8" and python_version < "4.0"
lxml==4.9.2 ; python_version >= "3.8" and python_version < "4.0"
multidict==6.0.4 ; python_version >= "3.8" and python_version < "4.0"
openpyxl==3.0.10 ; python_version >= "3.8" and python_version < "4.0"
passlib==1.7.4 ; python_version >= "3.8" and python_version < "4.0"
pillow==9.3.0 ; python_version >= "3.8" and python_version < "4.0"
pydantic==1.10.4 ; python_version >= "3.8" and python_version < "4.0"
python-multipart==0.0.5 ; python_version >= "3.8" and python_version < "4.0"
qrcode[pil]==7.3.1 ; python_version >= "3.8" and python_version < "4.0"
rfc3986[idna2008]==1.5.0 ; python_version >= "3.8" and python_version < "4.0"
six==1.16.0 ; python_version >= "3.8" and python_version < "4.0"
smmap==5.0.0 ; python_version >= "3.8" and python_version < "4.0"
sniffio==1.3.0 ; python_version >= "3.8" and python_version < "4.0"
soupsieve==2.3.2.post1 ; python_version >= "3.8" and python_version < "4.0"
sqlalchemy-database==0.0.11 ; python_version >= "3.8" and python_version < "4.0"
sqlalchemy2-stubs==0.0.2a31 ; python_version >= "3.8" and python_version < "4.0"
sqlalchemy==1.4.41 ; python_version >= "3.8" and python_version < "4.0"
sqlmodel==0.0.8 ; python_version >= "3.8" and python_version < "4.0"
sqlmodelx==0.0.4 ; python_version >= "3.8" and python_version < "4.0"
starlette==0.22.0 ; python_version >= "3.8" and python_version < "4.0"
typing-extensions==4.4.0 ; python_version >= "3.8" and python_version < "4.0"
yarl==1.8.2 ; python_version >= "3.8" and python_version < "4.0"
aiofiles==23.1.0 ; python_full_version >= "3.8.1" and python_version < "4.0"
aiohttp==3.8.4 ; python_full_version >= "3.8.1" and python_full_version < "4.0.0"
aiosignal==1.3.1 ; python_full_version >= "3.8.1" and python_full_version < "4.0.0"
aiosqlite==0.18.0 ; python_full_version >= "3.8.1" and python_full_version < "4.0.0"
anyio==3.6.2 ; python_full_version >= "3.8.1" and python_full_version < "4.0.0"
apscheduler==3.10.1 ; python_full_version >= "3.8.1" and python_version < "4.0"
async-timeout==4.0.2 ; python_full_version >= "3.8.1" and python_full_version < "4.0.0"
attrs==22.2.0 ; python_full_version >= "3.8.1" and python_full_version < "4.0.0"
backports-zoneinfo==0.2.1 ; python_full_version >= "3.8.1" and python_version < "3.9"
bcrypt==4.0.1 ; python_full_version >= "3.8.1" and python_full_version < "4.0.0"
beautifulsoup4==4.11.2 ; python_full_version >= "3.8.1" and python_full_version < "4.0.0"
certifi==2022.12.7 ; python_full_version >= "3.8.1" and python_full_version < "4.0.0"
charset-normalizer==3.1.0 ; python_full_version >= "3.8.1" and python_full_version < "4.0.0"
colorama==0.4.6 ; python_full_version >= "3.8.1" and python_full_version < "4.0.0" and platform_system == "Windows" or python_full_version >= "3.8.1" and python_version < "4.0" and sys_platform == "win32"
dnspython==2.3.0 ; python_full_version >= "3.8.1" and python_version < "4.0"
email-validator==1.3.1 ; python_full_version >= "3.8.1" and python_full_version < "4.0.0"
et-xmlfile==1.1.0 ; python_full_version >= "3.8.1" and python_full_version < "4.0.0"
fastapi-amis-admin==0.5.0 ; python_full_version >= "3.8.1" and python_full_version < "4.0.0"
fastapi-user-auth==0.5.0 ; python_full_version >= "3.8.1" and python_full_version < "4.0.0"
fastapi==0.93.0 ; python_full_version >= "3.8.1" and python_full_version < "4.0.0"
frozenlist==1.3.3 ; python_full_version >= "3.8.1" and python_full_version < "4.0.0"
gitdb==4.0.10 ; python_full_version >= "3.8.1" and python_full_version < "4.0.0"
gitpython==3.1.31 ; python_full_version >= "3.8.1" and python_full_version < "4.0.0"
greenlet==2.0.2 ; python_full_version >= "3.8.1" and (platform_machine == "aarch64" or platform_machine == "ppc64le" or platform_machine == "x86_64" or platform_machine == "amd64" or platform_machine == "AMD64" or platform_machine == "win32" or platform_machine == "WIN32") and python_full_version < "4.0.0"
h11==0.12.0 ; python_full_version >= "3.8.1" and python_full_version < "4.0.0"
httpcore==0.15.0 ; python_full_version >= "3.8.1" and python_full_version < "4.0.0"
httpx==0.23.0 ; python_full_version >= "3.8.1" and python_full_version < "4.0.0"
idna==3.4 ; python_full_version >= "3.8.1" and python_version < "4.0"
loguru==0.6.0 ; python_full_version >= "3.8.1" and python_version < "4.0"
lxml==4.9.2 ; python_full_version >= "3.8.1" and python_full_version < "4.0.0"
multidict==6.0.4 ; python_full_version >= "3.8.1" and python_version < "4.0"
nonebot-adapter-ntchat==0.3.5 ; python_full_version >= "3.8.1" and python_full_version < "4.0.0"
nonebot-plugin-apscheduler==0.2.0 ; python_full_version >= "3.8.1" and python_version < "4.0"
nonebot2==2.0.0rc3 ; python_full_version >= "3.8.1" and python_version < "4.0"
openpyxl==3.1.1 ; python_full_version >= "3.8.1" and python_full_version < "4.0.0"
passlib==1.7.4 ; python_full_version >= "3.8.1" and python_full_version < "4.0.0"
pillow==9.4.0 ; python_full_version >= "3.8.1" and python_full_version < "4.0.0"
pydantic==1.10.6 ; python_full_version >= "3.8.1" and python_full_version < "4.0.0"
pydantic[dotenv]==1.10.6 ; python_full_version >= "3.8.1" and python_version < "4.0"
pygtrie==2.5.0 ; python_full_version >= "3.8.1" and python_version < "4.0"
pypng==0.20220715.0 ; python_full_version >= "3.8.1" and python_full_version < "4.0.0"
python-dotenv==1.0.0 ; python_full_version >= "3.8.1" and python_version < "4.0"
python-multipart==0.0.6 ; python_full_version >= "3.8.1" and python_full_version < "4.0.0"
pytz-deprecation-shim==0.1.0.post0 ; python_full_version >= "3.8.1" and python_version < "4.0"
pytz==2022.7.1 ; python_full_version >= "3.8.1" and python_version < "4.0"
qrcode[pil]==7.4.2 ; python_full_version >= "3.8.1" and python_full_version < "4.0.0"
rfc3986[idna2008]==1.5.0 ; python_full_version >= "3.8.1" and python_full_version < "4.0.0"
setuptools==67.6.0 ; python_full_version >= "3.8.1" and python_version < "4.0"
six==1.16.0 ; python_full_version >= "3.8.1" and python_version < "4.0"
smmap==5.0.0 ; python_full_version >= "3.8.1" and python_full_version < "4.0.0"
sniffio==1.3.0 ; python_full_version >= "3.8.1" and python_full_version < "4.0.0"
soupsieve==2.4 ; python_full_version >= "3.8.1" and python_full_version < "4.0.0"
sqlalchemy-database==0.1.0 ; python_full_version >= "3.8.1" and python_full_version < "4.0.0"
sqlalchemy2-stubs==0.0.2a32 ; python_full_version >= "3.8.1" and python_full_version < "4.0.0"
sqlalchemy==1.4.41 ; python_full_version >= "3.8.1" and python_full_version < "4.0.0"
sqlmodel==0.0.8 ; python_full_version >= "3.8.1" and python_full_version < "4.0.0"
sqlmodelx==0.0.4 ; python_full_version >= "3.8.1" and python_full_version < "4.0.0"
starlette==0.25.0 ; python_full_version >= "3.8.1" and python_full_version < "4.0.0"
tomlkit==0.11.6 ; python_full_version >= "3.8.1" and python_version < "4.0"
typing-extensions==4.5.0 ; python_full_version >= "3.8.1" and python_version < "4.0"
tzdata==2022.7 ; python_full_version >= "3.8.1" and python_version < "4.0"
tzlocal==4.2 ; python_full_version >= "3.8.1" and python_version < "4.0"
win32-setctime==1.1.0 ; python_full_version >= "3.8.1" and python_version < "4.0" and sys_platform == "win32"
yarl==1.8.2 ; python_full_version >= "3.8.1" and python_version < "4.0"

1
tests/.env Normal file
View File

@ -0,0 +1 @@
ENVIRONMENT=test

4
tests/.env.test Normal file
View File

@ -0,0 +1,4 @@
LOG_LEVEL=TRACE
NICKNAME=["test"]
SUPERUSERS=["10000"]
COMMAND_START=["", "/"]

34
tests/conftest.py Normal file
View File

@ -0,0 +1,34 @@
from typing import TYPE_CHECKING, Optional
import pytest
if TYPE_CHECKING:
from nonebot.plugin import Plugin
@pytest.fixture
def load_metadata(nonebug_init: None) -> Optional["Plugin"]:
import nonebot
return nonebot.load_plugin("GenshinUID.genshinuid_meta")
@pytest.fixture
def load_adv(nonebug_init: None) -> Optional["Plugin"]:
import nonebot
return nonebot.load_plugin("GenshinUID.genshinuid_adv")
@pytest.fixture
def load_etc(nonebug_init: None) -> Optional["Plugin"]:
import nonebot
return nonebot.load_plugin("GenshinUID.genshinuid_etcimg")
@pytest.fixture
def load_guide(nonebug_init: None) -> Optional["Plugin"]:
import nonebot
return nonebot.load_plugin("GenshinUID.genshinuid_guide")

25
tests/test_adv.py Normal file
View File

@ -0,0 +1,25 @@
import pytest
from nonebug import App
MESSAGE = """「安柏」
-=-=-=-=-=-=-=-=-=-
推荐5★武器阿莫斯之弓、天空之翼、终末嗟叹之诗
推荐4★武器绝弦
推荐圣遗物搭配:
[昔日宗室之仪]四件套"""
@pytest.mark.asyncio
@pytest.mark.parametrize(argnames="alias", argvalues=["安柏用什么", "安柏怎么养"])
async def test_adv_char_chat(app: App, alias, load_adv: None):
from utils import make_event
from nonebot.adapters.onebot.v11 import Bot, Message
from GenshinUID.genshinuid_adv import get_char_adv
async with app.test_matcher(get_char_adv) as ctx:
bot = ctx.create_bot(base=Bot)
event = make_event(message=Message(alias))
ctx.receive_event(bot, event)
ctx.should_call_send(event, MESSAGE, True)
ctx.should_finished()

121
tests/test_etc.py Normal file
View File

@ -0,0 +1,121 @@
from pathlib import Path
import pytest
from nonebug import App
@pytest.mark.asyncio
@pytest.mark.parametrize(argnames="alias", argvalues=["版本规划", "原石预估"])
async def test_get_primogems_data(app: App, alias, load_etc: None):
from utils import make_event
from nonebot.adapters.onebot.v11 import Bot, Message, MessageSegment
from GenshinUID.version import Genshin_version
from GenshinUID.genshinuid_etcimg import get_primogems_data
with open(
Path(
(
"../GenshinUID/genshinuid_etcimg"
f"/primogems_data/{Genshin_version[:3]}.png"
)
),
"rb",
) as f:
data = f.read()
async with app.test_matcher(get_primogems_data) as ctx:
bot = ctx.create_bot(base=Bot)
event = make_event(message=Message(alias))
ctx.receive_event(bot, event)
ctx.should_call_send(
event,
MessageSegment.image(data),
True,
)
ctx.should_finished()
@pytest.mark.asyncio
async def test_primogems_version(app: App, load_etc: None):
from utils import make_event
from nonebot.adapters.onebot.v11 import Bot, Message, MessageSegment
from GenshinUID.version import Genshin_version
from GenshinUID.genshinuid_etcimg import get_primogems_data
with open(
Path(
(
"../GenshinUID/genshinuid_etcimg"
f"/primogems_data/{Genshin_version[:3]}.png"
)
),
"rb",
) as f:
data = f.read()
async with app.test_matcher(get_primogems_data) as ctx:
bot = ctx.create_bot(base=Bot)
event = make_event(message=Message(f"版本规划{Genshin_version[:3]}"))
ctx.receive_event(bot, event)
ctx.should_call_send(
event,
MessageSegment.image(data),
True,
)
ctx.should_finished()
@pytest.mark.asyncio
async def test_primogems_failed(app: App, load_etc: None):
from utils import make_event
from nonebot.adapters.onebot.v11 import Bot, Message
from GenshinUID.genshinuid_etcimg import get_primogems_data
# primogems_img.exists() is False
async with app.test_matcher(get_primogems_data) as ctx:
bot = ctx.create_bot(base=Bot)
event = make_event(message=Message("版本规划1.0"))
ctx.receive_event(bot, event)
ctx.should_finished()
# str(args) in version is False
async with app.test_matcher(get_primogems_data) as ctx:
event = make_event(message=Message("版本规划abc"))
ctx.receive_event(bot, event)
ctx.should_finished()
@pytest.mark.asyncio
@pytest.mark.parametrize(
argnames="alias",
argvalues=[
"伤害乘区",
"血量表",
"抗性表",
"血量排行",
"伤害乘区",
],
)
async def test_get_img_data(app: App, alias, load_etc: None):
from utils import make_event
from nonebot.adapters.onebot.v11 import Bot, Message, MessageSegment
from GenshinUID.genshinuid_etcimg import get_img_data
with open(
Path(f"../GenshinUID/genshinuid_etcimg/img_data/{alias}.jpg"),
"rb",
) as f:
data = f.read()
async with app.test_matcher(get_img_data) as ctx:
bot = ctx.create_bot(base=Bot)
event = make_event(message=Message(f"查询{alias}"))
ctx.receive_event(bot, event)
ctx.should_call_send(event, MessageSegment.image(data), True)
ctx.should_finished()

123
tests/test_guide.py Normal file
View File

@ -0,0 +1,123 @@
from pathlib import Path
import pytest
from nonebug import App
@pytest.mark.asyncio
@pytest.mark.parametrize(
argnames="alias",
argvalues=[
"钟离推荐",
"钟离攻略",
"岩王爷攻略", # alias
],
)
async def test_get_guide_pic(app: App, alias, load_guide: None):
from utils import make_event
from nonebot.adapters.onebot.v11 import Bot, Message, MessageSegment
from GenshinUID.genshinuid_guide import get_guide_pic
async with app.test_matcher(get_guide_pic) as ctx:
bot = ctx.create_bot(base=Bot)
event = make_event(message=Message(alias))
ctx.receive_event(bot, event)
ctx.should_call_send(
event,
MessageSegment.image(
"https://file.microgg.cn/MiniGG/guide/钟离.jpg"
),
True,
)
ctx.should_finished()
@pytest.mark.asyncio
async def test_get_guide_pic_traveler(app: App, load_guide: None):
from utils import make_event
from nonebot.adapters.onebot.v11 import Bot, Message, MessageSegment
from GenshinUID.genshinuid_guide import get_guide_pic
async with app.test_matcher(get_guide_pic) as ctx:
bot = ctx.create_bot(base=Bot)
event = make_event(message=Message("旅行者风推荐"))
ctx.receive_event(bot, event)
ctx.should_call_send(
event,
MessageSegment.image(
"https://file.microgg.cn/MiniGG/guide/旅行者-风.jpg"
),
True,
)
ctx.should_finished()
@pytest.mark.asyncio
async def test_get_guide_pic_failed(app: App, load_guide: None):
from utils import make_event
from nonebot.adapters.onebot.v11 import Bot, Message
from GenshinUID.genshinuid_guide import get_guide_pic
async with app.test_matcher(get_guide_pic) as ctx:
bot = ctx.create_bot(base=Bot)
event = make_event(message=Message("蔡徐坤攻略"))
ctx.receive_event(bot, event)
ctx.should_finished()
@pytest.mark.asyncio
@pytest.mark.parametrize(
argnames="alias",
argvalues=[
"参考面板钟离",
"参考面板岩王爷", # alias
],
)
async def test_get_bluekun_pic(app: App, alias, load_guide: None):
from utils import make_event
from nonebot.adapters.onebot.v11 import Bot, Message, MessageSegment
from GenshinUID.genshinuid_guide import get_bluekun_pic
with open(Path("../GenshinUID/genshinuid_guide/img/钟离.jpg"), "rb") as f:
data = f.read()
async with app.test_matcher(get_bluekun_pic) as ctx:
bot = ctx.create_bot(base=Bot)
event = make_event(message=Message(alias))
ctx.receive_event(bot, event)
ctx.should_call_send(event, MessageSegment.image(data), True)
ctx.should_finished()
@pytest.mark.asyncio
@pytest.mark.parametrize(
argnames="alias",
argvalues=[
"",
"",
],
)
async def test_get_bluekun_pic_element(app: App, alias, load_guide: None):
from utils import make_event
from nonebot.adapters.onebot.v11 import Bot, Message, MessageSegment
from GenshinUID.genshinuid_guide import get_bluekun_pic
with open(
Path(f"../GenshinUID/genshinuid_guide/img/{alias}.jpg"), "rb"
) as f:
data = f.read()
async with app.test_matcher(get_bluekun_pic) as ctx:
bot = ctx.create_bot(base=Bot)
event = make_event(message=Message(f"参考面板{alias}"))
ctx.receive_event(bot, event)
ctx.should_call_send(event, MessageSegment.image(data), True)
ctx.should_finished()

35
tests/test_metadata.py Normal file
View File

@ -0,0 +1,35 @@
import pytest
from nonebug import App
@pytest.mark.asyncio
async def test_metadata(app: App, load_metadata: None):
from GenshinUID.genshinuid_meta import __plugin_meta__
assert __plugin_meta__.name == 'GenshinUID'
assert (
__plugin_meta__.description == '基于NoneBot2的原神Uid查询/原神Wiki/米游社签到/树脂提醒插件'
)
@pytest.mark.asyncio
async def test_register_menu(app: App, load_metadata: None):
from GenshinUID.genshinuid_meta import sub_menus, register_menu
@register_menu(
'test',
'trigger',
'test register_menu',
trigger_method='114514',
detail_des='test register_menu',
)
async def _example() -> None:
pass
assert len(sub_menus) == 1
menu = sub_menus[0]
assert menu['func'] == 'test'
assert menu['trigger_condition'] == 'trigger'
assert menu['trigger_method'] == '114514'
assert menu['brief_des'] == 'test register_menu'
assert menu['detail_des'] == 'test register_menu'

109
tests/utils.py Normal file
View File

@ -0,0 +1,109 @@
# https://gist.github.com/38e4df0f6f7879d9324b15ac910229ac
from typing import Any, Dict, Optional, cast
from nonebot.adapters.onebot.v11 import Adapter, Message
from nonebot.adapters.onebot.v11.event import Event, Sender
def make_sender(
user_id: int = 10001,
nickname: str = "test",
sex: str = "unknown",
age: int = 1,
card: str = "test",
area: str = "北京",
level: str = "1",
role: str = "owner",
title: str = "test",
):
"""
:说明:
生成发送人信息类型。
https://github.com/botuniverse/onebot-11/blob/master/event/message.md
:参数:
* ``user_id: int = 10001``: 发送者 QQ 号。
* ``nickname: str = "test"``: 昵称。
* ``sex: str = "unknown"``: 性别,`male` 或 `female` 或 `unknown`。
* ``age: int = 1``: 年龄。
* ``card: str = "test"``: 群名片/备注。
* ``area: str = "北京"``: 地区。
* ``level: str = "1"``: 成员等级。
* ``role: str = "owner"``: 角色,`owner` 或 `admin` 或 `member`。
* ``title: str = "test"``: 专属头衔。
"""
return Sender(
user_id=user_id,
nickname=nickname,
sex=sex,
age=age,
card=card,
area=area,
level=level,
role=role,
title=title,
)
def make_event(
sender: Sender = make_sender(),
message: Optional[Message] = None,
time: int = 0,
self_id: int = 10000,
post_type: str = "message",
message_type: str = "private",
sub_type: str = "friend",
group_id: int = 10002,
anonymous: Optional[Dict[str, Any]] = None,
message_id: int = 1,
user_id: int = 10001,
font: int = 0,
**kwargs,
) -> Event:
"""
:说明:
根据消息类型自动生成事件。
https://github.com/botuniverse/onebot-11/blob/master/event/message.md
:参数:
* ``time: int = 0``: 事件发生的时间戳。
* ``self_id: int = 10000``: 收到事件的机器人 QQ 号。
* ``post_type: str = "message"``: 上报类型。
* ``message_type: str = "private"``: 消息类型,`friend` 是私聊消息,`group是群消息`。
* ``sub_type: str = "friend"``: 消息子类型:
* 私聊消息:如果是好友则是 `friend`,如果是群临时会话则是 `group`。
* 群消息:正常消息是 normal匿名消息是 anonymous系统提示如「管理员已禁止群内匿名聊天」是 notice
* ``message_id: int = 1``: 消息 ID。
* ``user_id: int = 10001``: 发送者 QQ 号。
* ``message: Message = None``: 消息内容。
* ``font: int = 0``: 字体。
* ``sender: Sender = None``: 发送人信息。
"""
raw_message = message.extract_plain_text() if message else ''
return cast(
Event,
Adapter.json_to_event(
{
"time": time,
"self_id": self_id,
"post_type": post_type,
"message_type": message_type,
"sub_type": sub_type,
"message_id": message_id,
"user_id": user_id,
"message": message,
"raw_message": raw_message,
"font": font,
"sender": sender,
"group_id": group_id,
"anonymous": anonymous,
**kwargs,
},
str(self_id),
),
)