nonebot-plugin-maimaidx 3.1.2__tar.gz → 3.1.5__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (93) hide show
  1. {nonebot_plugin_maimaidx-3.1.2 → nonebot_plugin_maimaidx-3.1.5}/PKG-INFO +14 -26
  2. {nonebot_plugin_maimaidx-3.1.2 → nonebot_plugin_maimaidx-3.1.5}/README.md +12 -24
  3. {nonebot_plugin_maimaidx-3.1.2 → nonebot_plugin_maimaidx-3.1.5}/nonebot_plugin_maimaidx/__init__.py +2 -10
  4. {nonebot_plugin_maimaidx-3.1.2 → nonebot_plugin_maimaidx-3.1.5}/nonebot_plugin_maimaidx/commands/__init__.py +0 -1
  5. {nonebot_plugin_maimaidx-3.1.2 → nonebot_plugin_maimaidx-3.1.5}/nonebot_plugin_maimaidx/commands/depend.py +8 -2
  6. {nonebot_plugin_maimaidx-3.1.2 → nonebot_plugin_maimaidx-3.1.5}/nonebot_plugin_maimaidx/commands/mai_base.py +30 -32
  7. {nonebot_plugin_maimaidx-3.1.2 → nonebot_plugin_maimaidx-3.1.5}/nonebot_plugin_maimaidx/commands/mai_score.py +49 -40
  8. {nonebot_plugin_maimaidx-3.1.2 → nonebot_plugin_maimaidx-3.1.5}/nonebot_plugin_maimaidx/commands/mai_table.py +18 -18
  9. {nonebot_plugin_maimaidx-3.1.2 → nonebot_plugin_maimaidx-3.1.5}/nonebot_plugin_maimaidx/config.py +0 -1
  10. {nonebot_plugin_maimaidx-3.1.2 → nonebot_plugin_maimaidx-3.1.5}/nonebot_plugin_maimaidx/constants.py +14 -1
  11. {nonebot_plugin_maimaidx-3.1.2 → nonebot_plugin_maimaidx-3.1.5}/nonebot_plugin_maimaidx/core/clients/divingfish/client.py +9 -65
  12. {nonebot_plugin_maimaidx-3.1.2 → nonebot_plugin_maimaidx-3.1.5}/nonebot_plugin_maimaidx/core/clients/divingfish/models/__init__.py +8 -18
  13. nonebot_plugin_maimaidx-3.1.5/nonebot_plugin_maimaidx/core/clients/divingfish/models/records.py +18 -0
  14. nonebot_plugin_maimaidx-3.1.5/nonebot_plugin_maimaidx/core/clients/divingfish/models/score.py +57 -0
  15. {nonebot_plugin_maimaidx-3.1.2 → nonebot_plugin_maimaidx-3.1.5}/nonebot_plugin_maimaidx/core/handler.py +69 -72
  16. {nonebot_plugin_maimaidx-3.1.2 → nonebot_plugin_maimaidx-3.1.5}/nonebot_plugin_maimaidx/core/handler_error.py +13 -8
  17. {nonebot_plugin_maimaidx-3.1.2 → nonebot_plugin_maimaidx-3.1.5}/nonebot_plugin_maimaidx/core/image/rating_table.py +37 -28
  18. {nonebot_plugin_maimaidx-3.1.2 → nonebot_plugin_maimaidx-3.1.5}/nonebot_plugin_maimaidx/core/merge/__init__.py +6 -3
  19. {nonebot_plugin_maimaidx-3.1.2 → nonebot_plugin_maimaidx-3.1.5}/nonebot_plugin_maimaidx/core/merge/models/score.py +3 -2
  20. {nonebot_plugin_maimaidx-3.1.2 → nonebot_plugin_maimaidx-3.1.5}/nonebot_plugin_maimaidx/core/merge/play_result.py +5 -7
  21. {nonebot_plugin_maimaidx-3.1.2 → nonebot_plugin_maimaidx-3.1.5}/nonebot_plugin_maimaidx/core/merge/player.py +22 -5
  22. nonebot_plugin_maimaidx-3.1.5/nonebot_plugin_maimaidx/core/oauth/__init__.py +59 -0
  23. nonebot_plugin_maimaidx-3.1.2/nonebot_plugin_maimaidx/commands/oauth_message.py → nonebot_plugin_maimaidx-3.1.5/nonebot_plugin_maimaidx/core/oauth/message.py +2 -2
  24. {nonebot_plugin_maimaidx-3.1.2/nonebot_plugin_maimaidx/core → nonebot_plugin_maimaidx-3.1.5/nonebot_plugin_maimaidx/core/oauth}/pending_binding.py +1 -1
  25. {nonebot_plugin_maimaidx-3.1.2 → nonebot_plugin_maimaidx-3.1.5}/nonebot_plugin_maimaidx.egg-info/PKG-INFO +14 -26
  26. {nonebot_plugin_maimaidx-3.1.2 → nonebot_plugin_maimaidx-3.1.5}/nonebot_plugin_maimaidx.egg-info/SOURCES.txt +6 -4
  27. {nonebot_plugin_maimaidx-3.1.2 → nonebot_plugin_maimaidx-3.1.5}/nonebot_plugin_maimaidx.egg-info/requires.txt +1 -1
  28. {nonebot_plugin_maimaidx-3.1.2 → nonebot_plugin_maimaidx-3.1.5}/pyproject.toml +2 -2
  29. nonebot_plugin_maimaidx-3.1.2/nonebot_plugin_maimaidx/core/clients/divingfish/models/score.py +0 -83
  30. {nonebot_plugin_maimaidx-3.1.2 → nonebot_plugin_maimaidx-3.1.5}/LICENSE +0 -0
  31. {nonebot_plugin_maimaidx-3.1.2 → nonebot_plugin_maimaidx-3.1.5}/nonebot_plugin_maimaidx/commands/mai_alias.py +0 -0
  32. {nonebot_plugin_maimaidx-3.1.2 → nonebot_plugin_maimaidx-3.1.5}/nonebot_plugin_maimaidx/commands/mai_guess.py +0 -0
  33. {nonebot_plugin_maimaidx-3.1.2 → nonebot_plugin_maimaidx-3.1.5}/nonebot_plugin_maimaidx/commands/mai_search.py +0 -0
  34. {nonebot_plugin_maimaidx-3.1.2 → nonebot_plugin_maimaidx-3.1.5}/nonebot_plugin_maimaidx/core/alias_sse_push.py +0 -0
  35. {nonebot_plugin_maimaidx-3.1.2 → nonebot_plugin_maimaidx-3.1.5}/nonebot_plugin_maimaidx/core/clients/divingfish/exceptions.py +0 -0
  36. {nonebot_plugin_maimaidx-3.1.2 → nonebot_plugin_maimaidx-3.1.5}/nonebot_plugin_maimaidx/core/clients/divingfish/models/music.py +0 -0
  37. {nonebot_plugin_maimaidx-3.1.2 → nonebot_plugin_maimaidx-3.1.5}/nonebot_plugin_maimaidx/core/clients/divingfish/models/oauth.py +0 -0
  38. {nonebot_plugin_maimaidx-3.1.2 → nonebot_plugin_maimaidx-3.1.5}/nonebot_plugin_maimaidx/core/clients/divingfish/oauth.py +0 -0
  39. {nonebot_plugin_maimaidx-3.1.2 → nonebot_plugin_maimaidx-3.1.5}/nonebot_plugin_maimaidx/core/clients/exceptions.py +0 -0
  40. {nonebot_plugin_maimaidx-3.1.2 → nonebot_plugin_maimaidx-3.1.5}/nonebot_plugin_maimaidx/core/clients/http.py +0 -0
  41. {nonebot_plugin_maimaidx-3.1.2 → nonebot_plugin_maimaidx-3.1.5}/nonebot_plugin_maimaidx/core/clients/lxns/client.py +0 -0
  42. {nonebot_plugin_maimaidx-3.1.2 → nonebot_plugin_maimaidx-3.1.5}/nonebot_plugin_maimaidx/core/clients/lxns/exceptions.py +0 -0
  43. {nonebot_plugin_maimaidx-3.1.2 → nonebot_plugin_maimaidx-3.1.5}/nonebot_plugin_maimaidx/core/clients/lxns/models/__init__.py +0 -0
  44. {nonebot_plugin_maimaidx-3.1.2 → nonebot_plugin_maimaidx-3.1.5}/nonebot_plugin_maimaidx/core/clients/lxns/models/base.py +0 -0
  45. {nonebot_plugin_maimaidx-3.1.2 → nonebot_plugin_maimaidx-3.1.5}/nonebot_plugin_maimaidx/core/clients/lxns/models/collection.py +0 -0
  46. {nonebot_plugin_maimaidx-3.1.2 → nonebot_plugin_maimaidx-3.1.5}/nonebot_plugin_maimaidx/core/clients/lxns/models/enum.py +0 -0
  47. {nonebot_plugin_maimaidx-3.1.2 → nonebot_plugin_maimaidx-3.1.5}/nonebot_plugin_maimaidx/core/clients/lxns/models/music.py +0 -0
  48. {nonebot_plugin_maimaidx-3.1.2 → nonebot_plugin_maimaidx-3.1.5}/nonebot_plugin_maimaidx/core/clients/lxns/models/oauth.py +0 -0
  49. {nonebot_plugin_maimaidx-3.1.2 → nonebot_plugin_maimaidx-3.1.5}/nonebot_plugin_maimaidx/core/clients/lxns/models/player.py +0 -0
  50. {nonebot_plugin_maimaidx-3.1.2 → nonebot_plugin_maimaidx-3.1.5}/nonebot_plugin_maimaidx/core/clients/lxns/models/score.py +0 -0
  51. {nonebot_plugin_maimaidx-3.1.2 → nonebot_plugin_maimaidx-3.1.5}/nonebot_plugin_maimaidx/core/clients/yuzuchan/client.py +0 -0
  52. {nonebot_plugin_maimaidx-3.1.2 → nonebot_plugin_maimaidx-3.1.5}/nonebot_plugin_maimaidx/core/clients/yuzuchan/exceptions.py +0 -0
  53. {nonebot_plugin_maimaidx-3.1.2 → nonebot_plugin_maimaidx-3.1.5}/nonebot_plugin_maimaidx/core/clients/yuzuchan/models/__init__.py +0 -0
  54. {nonebot_plugin_maimaidx-3.1.2 → nonebot_plugin_maimaidx-3.1.5}/nonebot_plugin_maimaidx/core/clients/yuzuchan/models/alias.py +0 -0
  55. {nonebot_plugin_maimaidx-3.1.2 → nonebot_plugin_maimaidx-3.1.5}/nonebot_plugin_maimaidx/core/clients/yuzuchan/models/enum.py +0 -0
  56. {nonebot_plugin_maimaidx-3.1.2 → nonebot_plugin_maimaidx-3.1.5}/nonebot_plugin_maimaidx/core/clients/yuzuchan/models/message.py +0 -0
  57. {nonebot_plugin_maimaidx-3.1.2 → nonebot_plugin_maimaidx-3.1.5}/nonebot_plugin_maimaidx/core/clients/yuzuchan/models/sse.py +0 -0
  58. {nonebot_plugin_maimaidx-3.1.2 → nonebot_plugin_maimaidx-3.1.5}/nonebot_plugin_maimaidx/core/clients/yuzuchan/models/status.py +0 -0
  59. {nonebot_plugin_maimaidx-3.1.2 → nonebot_plugin_maimaidx-3.1.5}/nonebot_plugin_maimaidx/core/database/qq.py +0 -0
  60. {nonebot_plugin_maimaidx-3.1.2 → nonebot_plugin_maimaidx-3.1.5}/nonebot_plugin_maimaidx/core/image/__init__.py +0 -0
  61. {nonebot_plugin_maimaidx-3.1.2 → nonebot_plugin_maimaidx-3.1.5}/nonebot_plugin_maimaidx/core/image/assets.py +0 -0
  62. {nonebot_plugin_maimaidx-3.1.2 → nonebot_plugin_maimaidx-3.1.5}/nonebot_plugin_maimaidx/core/image/base.py +0 -0
  63. {nonebot_plugin_maimaidx-3.1.2 → nonebot_plugin_maimaidx-3.1.5}/nonebot_plugin_maimaidx/core/image/best50.py +0 -0
  64. {nonebot_plugin_maimaidx-3.1.2 → nonebot_plugin_maimaidx-3.1.5}/nonebot_plugin_maimaidx/core/image/chart.py +0 -0
  65. {nonebot_plugin_maimaidx-3.1.2 → nonebot_plugin_maimaidx-3.1.5}/nonebot_plugin_maimaidx/core/image/info.py +0 -0
  66. {nonebot_plugin_maimaidx-3.1.2 → nonebot_plugin_maimaidx-3.1.5}/nonebot_plugin_maimaidx/core/image/plate_table.py +0 -0
  67. {nonebot_plugin_maimaidx-3.1.2 → nonebot_plugin_maimaidx-3.1.5}/nonebot_plugin_maimaidx/core/image/score.py +0 -0
  68. {nonebot_plugin_maimaidx-3.1.2 → nonebot_plugin_maimaidx-3.1.5}/nonebot_plugin_maimaidx/core/image/song.py +0 -0
  69. {nonebot_plugin_maimaidx-3.1.2 → nonebot_plugin_maimaidx-3.1.5}/nonebot_plugin_maimaidx/core/image/tools.py +0 -0
  70. {nonebot_plugin_maimaidx-3.1.2 → nonebot_plugin_maimaidx-3.1.5}/nonebot_plugin_maimaidx/core/image/update_table.py +0 -0
  71. {nonebot_plugin_maimaidx-3.1.2 → nonebot_plugin_maimaidx-3.1.5}/nonebot_plugin_maimaidx/core/merge/alias.py +0 -0
  72. {nonebot_plugin_maimaidx-3.1.2 → nonebot_plugin_maimaidx-3.1.5}/nonebot_plugin_maimaidx/core/merge/alias_list.py +0 -0
  73. {nonebot_plugin_maimaidx-3.1.2 → nonebot_plugin_maimaidx-3.1.5}/nonebot_plugin_maimaidx/core/merge/models/__init__.py +0 -0
  74. {nonebot_plugin_maimaidx-3.1.2 → nonebot_plugin_maimaidx-3.1.5}/nonebot_plugin_maimaidx/core/merge/models/alias.py +0 -0
  75. {nonebot_plugin_maimaidx-3.1.2 → nonebot_plugin_maimaidx-3.1.5}/nonebot_plugin_maimaidx/core/merge/models/best50.py +0 -0
  76. {nonebot_plugin_maimaidx-3.1.2 → nonebot_plugin_maimaidx-3.1.5}/nonebot_plugin_maimaidx/core/merge/models/enum.py +0 -0
  77. {nonebot_plugin_maimaidx-3.1.2 → nonebot_plugin_maimaidx-3.1.5}/nonebot_plugin_maimaidx/core/merge/models/guess.py +0 -0
  78. {nonebot_plugin_maimaidx-3.1.2 → nonebot_plugin_maimaidx-3.1.5}/nonebot_plugin_maimaidx/core/merge/models/player.py +0 -0
  79. {nonebot_plugin_maimaidx-3.1.2 → nonebot_plugin_maimaidx-3.1.5}/nonebot_plugin_maimaidx/core/merge/models/song.py +0 -0
  80. {nonebot_plugin_maimaidx-3.1.2 → nonebot_plugin_maimaidx-3.1.5}/nonebot_plugin_maimaidx/core/merge/music_list.py +0 -0
  81. {nonebot_plugin_maimaidx-3.1.2/nonebot_plugin_maimaidx/core → nonebot_plugin_maimaidx-3.1.5/nonebot_plugin_maimaidx/core/oauth}/divingfish_oauth.py +0 -0
  82. {nonebot_plugin_maimaidx-3.1.2/nonebot_plugin_maimaidx/core → nonebot_plugin_maimaidx-3.1.5/nonebot_plugin_maimaidx/core/oauth}/lxns_oauth.py +0 -0
  83. {nonebot_plugin_maimaidx-3.1.2 → nonebot_plugin_maimaidx-3.1.5}/nonebot_plugin_maimaidx/core/service/__init__.py +0 -0
  84. {nonebot_plugin_maimaidx-3.1.2 → nonebot_plugin_maimaidx-3.1.5}/nonebot_plugin_maimaidx/core/service/diving_fish.py +0 -0
  85. {nonebot_plugin_maimaidx-3.1.2 → nonebot_plugin_maimaidx-3.1.5}/nonebot_plugin_maimaidx/core/service/lxns.py +0 -0
  86. {nonebot_plugin_maimaidx-3.1.2 → nonebot_plugin_maimaidx-3.1.5}/nonebot_plugin_maimaidx/core/service/yuzuchan.py +0 -0
  87. {nonebot_plugin_maimaidx-3.1.2 → nonebot_plugin_maimaidx-3.1.5}/nonebot_plugin_maimaidx/core/tool.py +0 -0
  88. {nonebot_plugin_maimaidx-3.1.2 → nonebot_plugin_maimaidx-3.1.5}/nonebot_plugin_maimaidx/core/utils/calc.py +0 -0
  89. {nonebot_plugin_maimaidx-3.1.2 → nonebot_plugin_maimaidx-3.1.5}/nonebot_plugin_maimaidx/maimaidxhelp.png +0 -0
  90. {nonebot_plugin_maimaidx-3.1.2 → nonebot_plugin_maimaidx-3.1.5}/nonebot_plugin_maimaidx/resources.py +0 -0
  91. {nonebot_plugin_maimaidx-3.1.2 → nonebot_plugin_maimaidx-3.1.5}/nonebot_plugin_maimaidx.egg-info/dependency_links.txt +0 -0
  92. {nonebot_plugin_maimaidx-3.1.2 → nonebot_plugin_maimaidx-3.1.5}/nonebot_plugin_maimaidx.egg-info/top_level.txt +0 -0
  93. {nonebot_plugin_maimaidx-3.1.2 → nonebot_plugin_maimaidx-3.1.5}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: nonebot-plugin-maimaidx
3
- Version: 3.1.2
3
+ Version: 3.1.5
4
4
  Summary: maimaidx plugin for nonebot2
5
5
  Author-email: Yuri-YuzuChaN <806235364@qq.com>
6
6
  License-Expression: MIT
@@ -14,7 +14,7 @@ License-File: LICENSE
14
14
  Requires-Dist: nonebot2<3.0.0,>=2.0.0
15
15
  Requires-Dist: nonebot-adapter-onebot<3.0.0,>=2.2.2
16
16
  Requires-Dist: nonebot-plugin-apscheduler<1.0.0,>=0.3.0
17
- Requires-Dist: aiofiles<25.0.0,>=23.2.1
17
+ Requires-Dist: aiofiles>=23.2.1
18
18
  Requires-Dist: httpx<1.0.0,>=0.23.1
19
19
  Requires-Dist: pydantic<3.0.0,>=2.0.0
20
20
  Requires-Dist: pyecharts<3.0.0,>=2.0.0
@@ -40,6 +40,17 @@ Dynamic: license-file
40
40
 
41
41
  ## 重要更新
42
42
 
43
+ **2026-11-02**
44
+
45
+ 1. 新版本已完全废弃水鱼开发者Token,请前往水鱼 [开发者控制台](https://auth.diving-fish.com/console) 申请新应用,使用OAuth认证,未配置或者未授权仅可使用 `b50` 指令
46
+ 2. 修改了水鱼查分器的请求方式,移除废弃API端点
47
+ 3. 修复使用水鱼查分器查询b50无法显示 `镜` 牌的问题,牌子可自行修改 `plate_version` 文件夹内的所有 `镜` 牌为繁体字的 `鏡`。以下压缩包包含新的曲目 `魔法仕掛けのファンタジア` 曲绘,新的 `鏡` 牌和 `廻` 牌文件
48
+ - [cloudreve](https://cloud.yuzuchan.moe/f/x3f3/Resource%203.1.5%20UPDATE.7z)
49
+ - [onedrive](https://yuzuai-my.sharepoint.com/:u:/g/personal/yuzu_yuzuchan_moe/IQBfiyOSmOXEQ6BU4NFfZdxmATlmua1cd1zGUlW3txCwTy8?e=uwrb3h)
50
+ - [openlist](https://share.yuzuchan.moe/d/downloads/Resource%203.1.5%20UPDATE.7z?sign=lpaSBMx8zCkfIYcyWWVVPu5ozDfs45_1-RMLVeAynH4=:0)
51
+ 4. 修复 `我要上分` 指令计算问题
52
+ 5. 修复合并别名的时,出现不存在的曲目ID
53
+
43
54
  **2026-08-20**
44
55
 
45
56
  1. 水鱼查分器新增 `OAuth` 认证,请根据 [配置](#配置) 示例填入相关 后续开发者Token将被废弃
@@ -48,26 +59,6 @@ Dynamic: license-file
48
59
  4. 移除了申请别名通过和拒绝的推送
49
60
  5. 修改部分绘图
50
61
 
51
- **2026-06-09**
52
-
53
- ### 现仅支持 `Python 3.10+` !!!
54
-
55
- #### 最好在更新 `舞萌DX 2026` 后再使用该版本
56
-
57
- 1. 更新支持 `舞萌DX2026`,应该是最后一次大改了
58
- 2. 新支持了 `落雪查分器`
59
- 3. 新功能:
60
- - 新增 `舞`,`霸者` 牌子绘图
61
- - 新增进度绘图
62
- - 新增查询曲目过多时的绘图
63
- - 新增切换主题功能
64
- - 新增切换查分器功能
65
- - 新增 `ap50` 指令(仅限落雪查分器)
66
- - 新增 `lxbind`,`主题`,`数据源` 指令
67
- - 新增 `牌子条件` 指令
68
- 4. 修改了别名推送的发送方式,防止刷屏
69
- 5. 修复了非常多的 BUG
70
-
71
62
  ## 温馨提示
72
63
 
73
64
  **请务必看完 `README.MD` 所有内容**
@@ -120,8 +111,8 @@ Dynamic: license-file
120
111
  DIVINGFISH_CLIENT_SECRET= # OAuth 应用秘钥
121
112
  DIVINGFISH_SCOPE="" # OAuth 权限,根据应用权限进行填写,多个权限用空格隔开,默认值为 `"prober.profile.read"`
122
113
  DIVINGFISH_AUTH_URL= # 水鱼账号地址,一般不需要填写,默认为 `https://auth.diving-fish.com`
123
- DIVINGFISH_TOKEN= # 开发者 token,已弃用,见下方说明
124
114
  DIVINGFISH_PROBER_PROXY=false # 是否使用中转访问水鱼查分器,适用于境外服务器
115
+ DIVINGFISH_TOKEN= # 开发者 token,已弃用,见下方说明
125
116
 
126
117
  # lxns # 落雪查分器配置,均未填写将无法使用落雪查分器
127
118
  LXNS_DEV_TOKEN= # 开发者 token
@@ -131,9 +122,6 @@ Dynamic: license-file
131
122
  LXNS_BIND_PRIVATE_ONLY=false # 是否仅允许私聊完成 OAuth 绑定,默认允许群聊和私聊
132
123
  ```
133
124
 
134
- > [!NOTE]
135
- > 使用水鱼 OAuth 绑定时,用户发送 `dfbind`(或「绑定水鱼」),BOT 会返回一条授权链接,用户打开并确认页面上显示的绑定身份后点击「同意授权」即可,**不需要把授权码回贴给 BOT**。绑定关系与授权范围保存在水鱼服务端,BOT 只保管应用凭据,不保存任何用户令牌;用户可随时在 https://auth.diving-fish.com/apps 撤销授权。未绑定的用户仍可使用 `b50` 指令。
136
-
137
125
  > [!WARNING]
138
126
  > `DIVINGFISH_TOKEN`(开发者 token)已被水鱼查分器弃用:它能按 QQ 号读取任意用户的成绩,用户从未对 BOT 做过授权,也无法撤销。水鱼已停止签发新的开发者 token,并将在过渡期后关闭该鉴权方式。请改为申请 OAuth 应用并配置 `DIVINGFISH_CLIENT_ID` 与 `DIVINGFISH_CLIENT_SECRET`。两者同时配置时优先使用 OAuth 授权。
139
127
 
@@ -12,6 +12,17 @@
12
12
 
13
13
  ## 重要更新
14
14
 
15
+ **2026-11-02**
16
+
17
+ 1. 新版本已完全废弃水鱼开发者Token,请前往水鱼 [开发者控制台](https://auth.diving-fish.com/console) 申请新应用,使用OAuth认证,未配置或者未授权仅可使用 `b50` 指令
18
+ 2. 修改了水鱼查分器的请求方式,移除废弃API端点
19
+ 3. 修复使用水鱼查分器查询b50无法显示 `镜` 牌的问题,牌子可自行修改 `plate_version` 文件夹内的所有 `镜` 牌为繁体字的 `鏡`。以下压缩包包含新的曲目 `魔法仕掛けのファンタジア` 曲绘,新的 `鏡` 牌和 `廻` 牌文件
20
+ - [cloudreve](https://cloud.yuzuchan.moe/f/x3f3/Resource%203.1.5%20UPDATE.7z)
21
+ - [onedrive](https://yuzuai-my.sharepoint.com/:u:/g/personal/yuzu_yuzuchan_moe/IQBfiyOSmOXEQ6BU4NFfZdxmATlmua1cd1zGUlW3txCwTy8?e=uwrb3h)
22
+ - [openlist](https://share.yuzuchan.moe/d/downloads/Resource%203.1.5%20UPDATE.7z?sign=lpaSBMx8zCkfIYcyWWVVPu5ozDfs45_1-RMLVeAynH4=:0)
23
+ 4. 修复 `我要上分` 指令计算问题
24
+ 5. 修复合并别名的时,出现不存在的曲目ID
25
+
15
26
  **2026-08-20**
16
27
 
17
28
  1. 水鱼查分器新增 `OAuth` 认证,请根据 [配置](#配置) 示例填入相关 后续开发者Token将被废弃
@@ -20,26 +31,6 @@
20
31
  4. 移除了申请别名通过和拒绝的推送
21
32
  5. 修改部分绘图
22
33
 
23
- **2026-06-09**
24
-
25
- ### 现仅支持 `Python 3.10+` !!!
26
-
27
- #### 最好在更新 `舞萌DX 2026` 后再使用该版本
28
-
29
- 1. 更新支持 `舞萌DX2026`,应该是最后一次大改了
30
- 2. 新支持了 `落雪查分器`
31
- 3. 新功能:
32
- - 新增 `舞`,`霸者` 牌子绘图
33
- - 新增进度绘图
34
- - 新增查询曲目过多时的绘图
35
- - 新增切换主题功能
36
- - 新增切换查分器功能
37
- - 新增 `ap50` 指令(仅限落雪查分器)
38
- - 新增 `lxbind`,`主题`,`数据源` 指令
39
- - 新增 `牌子条件` 指令
40
- 4. 修改了别名推送的发送方式,防止刷屏
41
- 5. 修复了非常多的 BUG
42
-
43
34
  ## 温馨提示
44
35
 
45
36
  **请务必看完 `README.MD` 所有内容**
@@ -92,8 +83,8 @@
92
83
  DIVINGFISH_CLIENT_SECRET= # OAuth 应用秘钥
93
84
  DIVINGFISH_SCOPE="" # OAuth 权限,根据应用权限进行填写,多个权限用空格隔开,默认值为 `"prober.profile.read"`
94
85
  DIVINGFISH_AUTH_URL= # 水鱼账号地址,一般不需要填写,默认为 `https://auth.diving-fish.com`
95
- DIVINGFISH_TOKEN= # 开发者 token,已弃用,见下方说明
96
86
  DIVINGFISH_PROBER_PROXY=false # 是否使用中转访问水鱼查分器,适用于境外服务器
87
+ DIVINGFISH_TOKEN= # 开发者 token,已弃用,见下方说明
97
88
 
98
89
  # lxns # 落雪查分器配置,均未填写将无法使用落雪查分器
99
90
  LXNS_DEV_TOKEN= # 开发者 token
@@ -103,9 +94,6 @@
103
94
  LXNS_BIND_PRIVATE_ONLY=false # 是否仅允许私聊完成 OAuth 绑定,默认允许群聊和私聊
104
95
  ```
105
96
 
106
- > [!NOTE]
107
- > 使用水鱼 OAuth 绑定时,用户发送 `dfbind`(或「绑定水鱼」),BOT 会返回一条授权链接,用户打开并确认页面上显示的绑定身份后点击「同意授权」即可,**不需要把授权码回贴给 BOT**。绑定关系与授权范围保存在水鱼服务端,BOT 只保管应用凭据,不保存任何用户令牌;用户可随时在 https://auth.diving-fish.com/apps 撤销授权。未绑定的用户仍可使用 `b50` 指令。
108
-
109
97
  > [!WARNING]
110
98
  > `DIVINGFISH_TOKEN`(开发者 token)已被水鱼查分器弃用:它能按 QQ 号读取任意用户的成绩,用户从未对 BOT 做过授权,也无法撤销。水鱼已停止签发新的开发者 token,并将在过渡期后关闭该鉴权方式。请改为申请 OAuth 应用并配置 `DIVINGFISH_CLIENT_ID` 与 `DIVINGFISH_CLIENT_SECRET`。两者同时配置时优先使用 OAuth 授权。
111
99
 
@@ -64,19 +64,11 @@ async def get_music():
64
64
  log.opt(colors=True).info(
65
65
  "水鱼查分器已启用「<g>OAuth 授权</g>」,用户需使用「绑定水鱼」指令授权"
66
66
  )
67
- if dfconfig.divingfish_token:
68
- log.opt(colors=True).warning(
69
- "<y>已配置水鱼开发者Token,OAuth 授权优先,该配置项可以移除</y>"
70
- )
71
- elif dfconfig.divingfish_token:
72
- log.opt(colors=True).warning(
73
- "<y>水鱼开发者Token 已弃用,将在过渡期后停止工作,"
74
- "请配置 DIVINGFISH_CLIENT_ID 与 DIVINGFISH_CLIENT_SECRET 改用 OAuth 授权</y>"
75
- )
76
67
  else:
77
68
  log.opt(colors=True).warning(
78
- "<r>未配置水鱼查分器 OAuth 应用,查分模块只能使用「b50」指令</r>"
69
+ "水鱼查分器未配置「<g>OAuth 授权</g>」,用户仅可使用「b50」指令"
79
70
  )
71
+
80
72
  if lxnsconfig.lxns_dev_token is None:
81
73
  log.opt(colors=True).warning(
82
74
  "<r>未配置落雪查分器开发者Token,无法使用落雪数据源</r>"
@@ -5,4 +5,3 @@ from . import mai_guess as mai_guess
5
5
  from . import mai_score as mai_score
6
6
  from . import mai_search as mai_search
7
7
  from . import mai_table as mai_table
8
- from . import oauth_message as oauth_message
@@ -134,11 +134,17 @@ async def process_regex(
134
134
  case [ds] if is_float(ds):
135
135
  ds1 = ds2 = float(ds)
136
136
  case [ds1_raw, ds2_raw] if is_float(ds1_raw) and is_float(ds2_raw):
137
- ds1, ds2 = float(ds1_raw), float(ds2_raw)
137
+ # 帮助里的「定数」「页数」写法:第一个比第二个大时按页数处理
138
+ if float(ds1_raw) > float(ds2_raw):
139
+ ds1 = ds2 = float(ds1_raw)
140
+ page = max(1, int(float(ds2_raw)))
141
+ else:
142
+ ds1, ds2 = float(ds1_raw), float(ds2_raw)
138
143
  case [ds1_raw, ds2_raw, p_raw] if (
139
144
  is_float(ds1_raw) and is_float(ds2_raw) and p_raw.isdigit()
140
145
  ):
141
- ds1, ds2, page = float(ds1_raw), float(ds2_raw), int(p_raw)
146
+ ds1, ds2 = float(ds1_raw), float(ds2_raw)
147
+ page = max(1, int(p_raw))
142
148
  case _:
143
149
  await matcher.finish(
144
150
  (
@@ -27,7 +27,6 @@ from ..core.clients.divingfish.exceptions import (
27
27
  from ..core.clients.divingfish.oauth import REVOKE_URL, binding_label
28
28
  from ..core.clients.exceptions import HTTPError, UnknownError
29
29
  from ..core.database.qq import User, update_user
30
- from ..core.divingfish_oauth import extract_confirmation_code
31
30
  from ..core.handler import (
32
31
  bind_divingfish,
33
32
  bind_lxns,
@@ -38,22 +37,8 @@ from ..core.handler import (
38
37
  get_mai_what,
39
38
  )
40
39
  from ..core.image.tools import image_to_base64, song_chart
41
- from ..core.lxns_oauth import (
42
- extract_authorization_code,
43
- is_binding_channel_allowed,
44
- )
45
40
  from ..core.merge.models import ServiceName, Theme
46
- from ..core.pending_binding import PendingBindingStore
47
- from ..core.service import mai
48
- from ..core.tool import qqhash
49
- from ..resources import Root
50
- from .depend import (
51
- GetOrCreateSender,
52
- GetOrCreateUser,
53
- GetUserAndAuth,
54
- GetUserAndAuthOrNone,
55
- )
56
- from .oauth_message import (
41
+ from ..core.oauth import (
57
42
  BINDING_TEMPORARY_FAILED_MSG,
58
43
  DIVINGFISH_AUTHORIZE_MSG,
59
44
  DIVINGFISH_BIND_FAILED_MSG,
@@ -70,6 +55,19 @@ from .oauth_message import (
70
55
  LXNS_AUTHORIZE_MSG,
71
56
  LXNS_ERROR,
72
57
  OAUTH_FAILED_MSG,
58
+ PendingBindingStore,
59
+ extract_authorization_code,
60
+ extract_confirmation_code,
61
+ is_binding_channel_allowed,
62
+ )
63
+ from ..core.service import mai
64
+ from ..core.tool import qqhash
65
+ from ..resources import Root
66
+ from .depend import (
67
+ GetOrCreateSender,
68
+ GetOrCreateUser,
69
+ GetUserAndAuth,
70
+ GetUserAndAuthOrNone,
73
71
  )
74
72
 
75
73
  pending_bindings = PendingBindingStore()
@@ -115,6 +113,22 @@ async def complete_lxns_binding(user: User, code: str) -> tuple[str, bool]:
115
113
  return result, result == "授权完成。"
116
114
 
117
115
 
116
+ async def complete_divingfish(qqid: int, code: str) -> tuple[str, bool]:
117
+ try:
118
+ await complete_divingfish_binding(qqid, code)
119
+ except DivingFishBindingMismatchError:
120
+ # 码有效,但兑出来的授权不是这个 QQ 的。多半是把别人转发来的码
121
+ # 当成自己的用了——照实说清楚,别让他以为是自己操作错了
122
+ log.warning("水鱼确认码与发起绑定的用户不一致")
123
+ return DIVINGFISH_MISMATCH_MSG, False
124
+ except DivingFishConfirmationCodeError:
125
+ return DIVINGFISH_CODE_FAILED_MSG, False
126
+ except (HTTPError, HTTPXError, UnknownError, ValidationError) as error:
127
+ log.warning(f"水鱼绑定暂时失败:{type(error).__name__}")
128
+ return DIVINGFISH_CODE_TEMPORARY_FAILED_MSG, False
129
+ return DIVINGFISH_BIND_SUCCESS_MSG, True
130
+
131
+
118
132
  update_data = on_command("更新maimai数据", permission=SUPERUSER)
119
133
  help = on_command("帮助maimaiDX", aliases={"帮助maimaidx"})
120
134
  maimaidxrepo = on_command("项目地址maimaiDX", aliases={"项目地址maimaidx"})
@@ -234,22 +248,6 @@ async def _(
234
248
  await bind_code.finish(result, reply_message=True)
235
249
 
236
250
 
237
- async def complete_divingfish(qqid: int, code: str) -> tuple[str, bool]:
238
- try:
239
- await complete_divingfish_binding(qqid, code)
240
- except DivingFishBindingMismatchError:
241
- # 码有效,但兑出来的授权不是这个 QQ 的。多半是把别人转发来的码
242
- # 当成自己的用了——照实说清楚,别让他以为是自己操作错了
243
- log.warning("水鱼确认码与发起绑定的用户不一致")
244
- return DIVINGFISH_MISMATCH_MSG, False
245
- except DivingFishConfirmationCodeError:
246
- return DIVINGFISH_CODE_FAILED_MSG, False
247
- except (HTTPError, HTTPXError, UnknownError, ValidationError) as error:
248
- log.warning(f"水鱼绑定暂时失败:{type(error).__name__}")
249
- return DIVINGFISH_CODE_TEMPORARY_FAILED_MSG, False
250
- return DIVINGFISH_BIND_SUCCESS_MSG, True
251
-
252
-
253
251
  @df_bind.handle()
254
252
  async def _(
255
253
  event: GroupMessageEvent | PrivateMessageEvent,
@@ -10,7 +10,7 @@ from ..config import log
10
10
  from ..core.database.qq import User
11
11
  from ..core.handler import draw_best50, draw_play_data, draw_song_global_data
12
12
  from ..core.image.tools import text_to_bytes_io
13
- from ..core.merge.models import ServiceName
13
+ from ..core.merge.models import Song
14
14
  from ..core.service import mai
15
15
  from .depend import GetUserAndAuth
16
16
 
@@ -29,39 +29,58 @@ async def _(
29
29
  user: User = Depends(GetUserAndAuth),
30
30
  ):
31
31
  username = message.extract_plain_text().strip()
32
- if (is_ap := isinstance(matcher, ap50)) and user.service == ServiceName.DIVINGFISH:
33
- await matcher.finish("仅落雪查分器支持AP50指令", reply_message=True)
34
- result = await draw_best50(user, username=username, all_perfect=is_ap)
32
+ result = await draw_best50(
33
+ user, username=username, all_perfect=isinstance(matcher, ap50)
34
+ )
35
35
  await matcher.send(result, reply_message=True)
36
36
 
37
37
 
38
+ async def search_song(matcher: Matcher, data: str) -> Song | None:
39
+ if data.isdigit() and (by_id := mai.total_list.by_id(int(data))):
40
+ song = by_id
41
+ elif by_t := mai.total_list.by_name(data):
42
+ song = by_t
43
+ elif alias := mai.total_alias_list.by_alias(data):
44
+ if not alias:
45
+ await matcher.finish("未找到曲目", reply_message=True)
46
+ elif len(alias) != 1:
47
+ msg = "找到相同别名的曲目,请使用以下ID查询:\n"
48
+ for songs in alias:
49
+ msg += f"{songs.song_id}:{songs.alias[0]}\n"
50
+ await matcher.finish(msg.strip(), reply_message=True)
51
+ else:
52
+ song = mai.total_list.by_id(alias[0].song_id)
53
+ else:
54
+ by_t = mai.total_list.filter(title=data)
55
+ if (tn := len(by_t)) == 1:
56
+ song = by_t[0]
57
+ elif tn <= 5 and tn > 1:
58
+ msg = f"未找到别名为「{data}」的歌曲,但找到「{tn}」个相似标题的曲目:\n"
59
+ for s in sorted(by_t, key=lambda x: int(x.song_id)):
60
+ msg += f"{f'「{s.song_id}」':<7} {s.song_name}\n"
61
+ msg += "※ 请使用「info xxxxx」查询指定曲目"
62
+ await matcher.finish(msg, reply_message=True)
63
+ elif tn > 5:
64
+ await matcher.finish("查询相似的曲目过多,请缩小范围。", reply_message=True)
65
+ else:
66
+ song = None
67
+
68
+ return song
69
+
70
+
38
71
  @info.handle()
39
72
  async def _(
40
73
  message: Message = CommandArg(),
41
74
  user: User = Depends(GetUserAndAuth),
42
75
  ):
43
- data = message.extract_plain_text().strip()
44
- if not data:
76
+ args = message.extract_plain_text().strip().lower()
77
+ if not args:
45
78
  await info.finish("请输入曲目id或曲名", reply_message=True)
46
79
 
47
- if data.isdigit() and (by_id := mai.total_list.by_id(int(data))):
48
- song = by_id
49
- elif by_t := mai.total_list.by_name(data):
50
- song = by_t
51
- else:
52
- aliases = mai.total_alias_list.by_alias(data)
53
- if not aliases:
54
- await info.finish("未找到曲目", reply_message=True)
55
- elif len(aliases) != 1:
56
- msg = "找到相同别名的曲目,请使用以下ID查询:\n"
57
- for alias in aliases:
58
- msg += f"{alias.song_id}:{alias.alias[0]}\n"
59
- await info.finish(msg.strip(), reply_message=True)
60
- else:
61
- song_id = aliases[0].song_id
62
- song = mai.total_list.by_id(song_id)
63
- if song is None:
64
- await info.finish("未找到曲目", reply_message=True)
80
+ song = await search_song(info, args)
81
+
82
+ if song is None:
83
+ await info.finish("未找到曲目", reply_message=True)
65
84
 
66
85
  result = await draw_play_data(user, song)
67
86
  await info.send(result, reply_message=True)
@@ -69,7 +88,7 @@ async def _(
69
88
 
70
89
  @ginfo.handle()
71
90
  async def _(message: Message = CommandArg()):
72
- args = message.extract_plain_text().strip()
91
+ args = message.extract_plain_text().strip().lower()
73
92
  if not args:
74
93
  await ginfo.finish("请输入曲目id或曲名", reply_message=True)
75
94
 
@@ -80,21 +99,11 @@ async def _(message: Message = CommandArg()):
80
99
  args = args[1:].strip()
81
100
  if not args:
82
101
  await ginfo.finish("请输入曲目id或曲名", reply_message=True)
83
- if args.isdigit() and (by_id := mai.total_list.by_id(int(args))):
84
- song = by_id
85
- elif by_t := mai.total_list.by_name(args):
86
- song = by_t
87
- else:
88
- alias = mai.total_alias_list.by_alias(args)
89
- if not alias:
90
- await ginfo.finish("未找到曲目", reply_message=True)
91
- elif len(alias) != 1:
92
- msg = "找到相同别名的曲目,请使用以下ID查询:\n"
93
- for songs in alias:
94
- msg += f"{songs.song_id}:{songs.alias[0]}\n"
95
- await ginfo.finish(msg.strip(), reply_message=True)
96
- else:
97
- song = mai.total_list.by_id(alias[0].song_id)
102
+
103
+ song = await search_song(ginfo, args)
104
+ if song is None:
105
+ await ginfo.finish("未找到曲目", reply_message=True)
106
+
98
107
  if level_index >= len(song.difficulties):
99
108
  await ginfo.finish("该乐曲没有这个等级", reply_message=True)
100
109
  stats = song.difficulties[level_index].stats
@@ -8,7 +8,14 @@ from nonebot.params import Depends, RegexMatched
8
8
  from nonebot.permission import SUPERUSER
9
9
  from PIL import Image
10
10
 
11
- from ..constants import COMBO_PLUS, LEVEL_LIST, PLATE_CN, RANK_PLUS, SYNC_PLUS
11
+ from ..constants import (
12
+ CB_SYNC_MAP,
13
+ COMBO_PLUS,
14
+ LEVEL_LIST,
15
+ PLATE_CN,
16
+ RANK_PLUS,
17
+ SYNC_PLUS,
18
+ )
12
19
  from ..core.database.qq import User
13
20
  from ..core.handler import (
14
21
  draw_level_progress,
@@ -26,7 +33,7 @@ from .depend import GetUserAndAuth
26
33
 
27
34
  RATING_PATTERN = r"^([0-9]+\+?)((s+|ap|fc|fs|fdx)\+?)?\s?完成表$"
28
35
  TABLE_PATTERN = (
29
- r"^([真超檄橙暁晓桃櫻樱紫菫堇白雪輝辉舞霸熊華华爽煌星宙祭祝双宴镜彩])"
36
+ r"^([真超檄橙暁晓桃櫻樱紫菫堇白雪輝辉舞霸熊華华爽煌星宙祭祝双宴镜鏡彩])"
30
37
  r"([極极将舞神者]舞?){}表?\s?([0-9]+)?$"
31
38
  )
32
39
  LEVEL_PATTERN = r"^([0-9]+\+?)\s?((?:a+|b+|c|d|s+|ap|fc|fs|fdx)\+?)\s?([\u4e00-\u9fa5]+)?\s?进度\s?([0-9]+)?$"
@@ -70,11 +77,11 @@ async def _():
70
77
 
71
78
  @rating_table.handle()
72
79
  async def _(match: Match[str] = RegexMatched()):
73
- rating = match.group(1).strip()
74
- if rating in LEVEL_LIST[:6]:
80
+ level = match.group(1).strip()
81
+ if level in LEVEL_LIST[:6]:
75
82
  result = "只支持查询lv7-15的定数表。"
76
- elif rating in LEVEL_LIST[6:]:
77
- result = draw_rating_table_text(rating)
83
+ elif level in LEVEL_LIST[6:]:
84
+ result = draw_rating_table_text(level)
78
85
  else:
79
86
  result = "无法识别的定数。"
80
87
  await rating_table.send(result, reply_message=True)
@@ -82,19 +89,12 @@ async def _(match: Match[str] = RegexMatched()):
82
89
 
83
90
  @rating_table_pfm.handle()
84
91
  async def _(match: Match[str] = RegexMatched(), user: User = Depends(GetUserAndAuth)):
85
- ra = match.group(1)
86
- plan = match.group(2)
87
- if ra in LEVEL_LIST[:6]:
92
+ level = match.group(1)
93
+ plan = (match.group(2) or "").lower()
94
+ if level in LEVEL_LIST[:6]:
88
95
  await rating_table_pfm.finish("只支持查询lv7-15的完成表。", reply_message=True)
89
- elif ra in LEVEL_LIST[6:]:
90
- if plan and plan.lower() not in COMBO_PLUS:
91
- await rating_table_pfm.finish(
92
- "完成表目前仅支持「fc」「ap」计划,例如「13fc完成表」「13ap完成表」。",
93
- reply_message=True,
94
- )
95
- pic = await draw_rating_table(
96
- user, ra, bool(plan and plan.lower() in COMBO_PLUS)
97
- )
96
+ elif level in LEVEL_LIST[6:]:
97
+ pic = await draw_rating_table(user, level, CB_SYNC_MAP.get(plan))
98
98
  await rating_table_pfm.finish(pic, reply_message=True)
99
99
  else:
100
100
  await rating_table_pfm.finish("无法识别的定数。", reply_message=True)
@@ -24,7 +24,6 @@ class BaseConfig(BaseModel):
24
24
 
25
25
  class DivingFishConfig(BaseModel):
26
26
  divingfish_prober_proxy: bool = False
27
- divingfish_token: str | None = None
28
27
  divingfish_client_id: str | None = None
29
28
  divingfish_client_secret: str | None = None
30
29
  divingfish_auth_url: str = "https://auth.diving-fish.com"
@@ -83,6 +83,17 @@ SYNC_MAP = {
83
83
  "sync": "Sync",
84
84
  }
85
85
 
86
+ CB_SYNC_MAP: dict[str, tuple[str, ...]] = {
87
+ "fc": ("fc", "fcp", "ap", "app"),
88
+ "fc+": ("fcp", "ap", "app"),
89
+ "ap": ("ap", "app"),
90
+ "ap+": ("app",),
91
+ "fs": ("fs", "fsp", "fsd", "fsdp"),
92
+ "fs+": ("fsp", "fsd", "fsdp"),
93
+ "fdx": ("fsd", "fsdp"),
94
+ "fdx+": ("fsdp",),
95
+ }
96
+
86
97
  DIFFS = ["Basic", "Advanced", "Expert", "Master", "Re:Master"]
87
98
  LEVEL_LIST = [
88
99
  "1",
@@ -174,6 +185,7 @@ DX_VERSION = {
174
185
  "双": "maimai でらっくす BUDDiES",
175
186
  "宴": "maimai でらっくす BUDDiES PLUS",
176
187
  "镜": "maimai でらっくす PRiSM",
188
+ "鏡": "maimai でらっくす PRiSM",
177
189
  "彩": "maimai でらっくす PRiSM PLUS",
178
190
  # "丸": "maimai でらっくす CiRCLE"
179
191
  # "": "maimai でらっくす CiRCLE PLUS"
@@ -215,11 +227,12 @@ VERSION_MAP = {
215
227
  "双": ([DX_VERSION["双"]], "双&宴"),
216
228
  "宴": ([DX_VERSION["双"]], "双&宴"),
217
229
  "镜": ([DX_VERSION["镜"]], "镜"),
230
+ "鏡": ([DX_VERSION["鏡"]], "鏡"),
218
231
  "彩": ([DX_VERSION["彩"]], "彩"),
219
232
  # "丸": ([DX_VERSION["丸"]], "丸"),
220
233
  # "": ([DX_VERSION["丸"]], "丸")
221
234
  }
222
- PLATE_CN = {"晓": "暁", "樱": "櫻", "堇": "菫", "辉": "輝", "华": "華"}
235
+ PLATE_CN = {"晓": "暁", "樱": "櫻", "堇": "菫", "辉": "輝", "华": "華", "镜": "鏡"}
223
236
  CATEGORY = {
224
237
  "流行&动漫": "anime",
225
238
  "舞萌": "maimai",
@@ -12,7 +12,7 @@ from .exceptions import (
12
12
  DivingFishUserDisabledQueryError,
13
13
  DivingFishUserNotFoundError,
14
14
  )
15
- from .models import PlayInfoDefault, PlayInfoDev, UserInfo, UserInfoDev, UserRanking
15
+ from .models import RecordsQueryRequest, UserAllRecords, UserInfo, UserRanking
16
16
  from .oauth import get_access_token
17
17
 
18
18
 
@@ -21,14 +21,8 @@ class DivingFishAPI(ApiClient):
21
21
  base_url = "https://maimai.diving-fish.com/api/maimaidxprober"
22
22
 
23
23
  def __init__(self, qqid: int | None = None, username: str | None = None):
24
- #: 授权模式下查谁由令牌决定,请求里不再携带 `qq`,也不需要开发者 token
25
24
  self.oauth = dfconfig.oauth_enabled and qqid is not None and not username
26
- super().__init__(
27
- base_url=self.base_url,
28
- headers=None
29
- if self.oauth or not dfconfig.divingfish_token
30
- else {"developer-token": dfconfig.divingfish_token},
31
- )
25
+ super().__init__(base_url=self.base_url)
32
26
  self.qqid = qqid
33
27
  self._retried = False
34
28
  self.json = {}
@@ -36,7 +30,6 @@ class DivingFishAPI(ApiClient):
36
30
  self.json["qq"] = qqid
37
31
  if username:
38
32
  self.json["username"] = username
39
- self.json.pop("qq", None)
40
33
 
41
34
  def _handle_error(self, resp: Response):
42
35
  if resp.status_code == 200:
@@ -113,67 +106,18 @@ class DivingFishAPI(ApiClient):
113
106
  result = await self._request_data("POST", "/query/player", json=self.json)
114
107
  return UserInfo.model_validate(result)
115
108
 
116
- async def query_user_plate(self, version: list[str]) -> list[PlayInfoDefault]:
117
- """
118
- 请求用户数据
119
-
120
- Params:
121
- `version`: 版本
122
- Returns:
123
- `List[PlayInfoDefault]` 数据列表
124
- """
125
- if self.oauth:
126
- result = await self._request_oauth(
127
- "POST", "/player/plate", json={"version": version}
128
- )
129
- else:
130
- self.json["version"] = version
131
- result = await self._request_data("POST", "/query/plate", json=self.json)
132
-
133
- return [PlayInfoDefault.model_validate(d) for d in result["verlist"]]
134
-
135
- async def query_user_records(self) -> UserInfoDev:
109
+ async def query_user_records(
110
+ self, query: RecordsQueryRequest | None = None
111
+ ) -> UserAllRecords:
136
112
  """
137
113
  获取用户所有成绩
138
114
 
139
115
  Returns:
140
- `UserInfoDev` 用户信息
116
+ `UserAllRecords` 用户信息
141
117
  """
142
- if self.oauth:
143
- result = await self._request_oauth("GET", "/player/records")
144
- else:
145
- result = await self._request_data(
146
- "GET", "/dev/player/records", params=self.json
147
- )
148
- return UserInfoDev.model_validate(result)
149
-
150
- async def query_user_record(
151
- self, *, song_id: str | int | list[str | int]
152
- ) -> list[PlayInfoDev]:
153
- """
154
- 获取用户指定曲目成绩
155
-
156
- Params:
157
- `song_id`: 曲目id,可以为单个ID或者列表
158
- Returns:
159
- `List[PlayInfoDev]` 成绩列表
160
- """
161
- if not isinstance(song_id, list):
162
- song_id = [song_id]
163
-
164
- if self.oauth:
165
- result = await self._request_oauth(
166
- "POST", "/player/record", json={"music_id": song_id}
167
- )
168
- else:
169
- self.json["music_id"] = song_id
170
- result = await self._request_data(
171
- "POST", "/dev/player/record", json=self.json
172
- )
173
- if result == {}:
174
- return []
175
-
176
- return [PlayInfoDev.model_validate(d) for k, v in result.items() for d in v]
118
+ params = query.model_dump(exclude_none=True) if query is not None else None
119
+ result = await self._request_oauth("GET", "/player/records", params=params)
120
+ return UserAllRecords.model_validate(result)
177
121
 
178
122
  async def rating_ranking(self) -> list[UserRanking]:
179
123
  """