@lf_maple/koishi-plugin-phi-plugin-full 0.1.80

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 (629) hide show
  1. package/LICENSE +22 -0
  2. package/core/.github/ISSUE_TEMPLATE/bug.yaml +133 -0
  3. package/core/.github/ISSUE_TEMPLATE/config.yml +1 -0
  4. package/core/.github/ISSUE_TEMPLATE/enhancement.yml +44 -0
  5. package/core/.vscode/c_cpp_properties.json +18 -0
  6. package/core/.vscode/launch.json +24 -0
  7. package/core/.vscode/settings.json +59 -0
  8. package/core/CHANGELOG.md +184 -0
  9. package/core/LICENSE +674 -0
  10. package/core/README.md +330 -0
  11. package/core/README_en.md +285 -0
  12. package/core/apps/Dan.js +129 -0
  13. package/core/apps/RankList.js +322 -0
  14. package/core/apps/aliasProposal.js +223 -0
  15. package/core/apps/apiSetting.js +592 -0
  16. package/core/apps/b19.js +1329 -0
  17. package/core/apps/botClient.js +64 -0
  18. package/core/apps/chart.js +692 -0
  19. package/core/apps/guessGame/guessIll.js +821 -0
  20. package/core/apps/guessGame/guessLetter.js +831 -0
  21. package/core/apps/guessGame/guessTips.js +254 -0
  22. package/core/apps/guessGame/letterGameUtils.js +74 -0
  23. package/core/apps/guessGame.js +214 -0
  24. package/core/apps/help.js +117 -0
  25. package/core/apps/manage.js +288 -0
  26. package/core/apps/market.js +243 -0
  27. package/core/apps/money.js +1064 -0
  28. package/core/apps/phisong.js +1418 -0
  29. package/core/apps/session.js +664 -0
  30. package/core/apps/setting.js +468 -0
  31. package/core/apps/update.js +541 -0
  32. package/core/apps/user.js +968 -0
  33. package/core/components/Config.js +249 -0
  34. package/core/components/Data.js +304 -0
  35. package/core/components/FileWatcherRegistry.js +102 -0
  36. package/core/components/Logger.js +11 -0
  37. package/core/components/ProcessCleanup.js +113 -0
  38. package/core/components/Version.js +56 -0
  39. package/core/components/YamlReader.js +157 -0
  40. package/core/components/baseClass.js +287 -0
  41. package/core/components/common.js +47 -0
  42. package/core/components/index.js +13 -0
  43. package/core/components/platform/index.js +79 -0
  44. package/core/components/platform/koishi.js +800 -0
  45. package/core/components/platform/koishiDatabaseRedis.js +391 -0
  46. package/core/components/platform/memoryRedis.js +209 -0
  47. package/core/components/platform/types.d.ts +272 -0
  48. package/core/components/platform/yunzai.js +604 -0
  49. package/core/components/segment.js +4 -0
  50. package/core/config/config//350/277/231/351/207/214/346/230/257/351/205/215/347/275/256/346/226/207/344/273/266/347/233/256/345/275/225.txt +0 -0
  51. package/core/config/default_config/banGroup.yaml +14 -0
  52. package/core/config/default_config/config.yaml +159 -0
  53. package/core/config/default_config/nickconfig.yaml +6 -0
  54. package/core/config/default_config/otherinfo.yaml +51 -0
  55. package/core/guoba.support.js +597 -0
  56. package/core/index.js +85 -0
  57. package/core/lib/AES.js +40 -0
  58. package/core/lib/Base64.js +22 -0
  59. package/core/lib/ByteReader.js +168 -0
  60. package/core/lib/GameProgress.js +37 -0
  61. package/core/lib/GameRecord.js +47 -0
  62. package/core/lib/GameSettings.js +24 -0
  63. package/core/lib/GameUser.js +19 -0
  64. package/core/lib/Level.js +6 -0
  65. package/core/lib/LevelRecord.js +9 -0
  66. package/core/lib/PhigrosUser.js +135 -0
  67. package/core/lib/SaveManager.js +117 -0
  68. package/core/lib/Summary.js +47 -0
  69. package/core/lib/TapTap/CompleteQRCodeData.js +27 -0
  70. package/core/lib/TapTap/LCHelper.js +81 -0
  71. package/core/lib/TapTap/TapTapHelper.js +170 -0
  72. package/core/lib/TapTap/getQRcode.js +61 -0
  73. package/core/lib/Util.js +30 -0
  74. package/core/lib/getQRcode.js +73 -0
  75. package/core/lib/http.js +105 -0
  76. package/core/model/README.md +3 -0
  77. package/core/model/api/aliasProposalService.js +151 -0
  78. package/core/model/api/apiVersion.js +67 -0
  79. package/core/model/api/autoSeekApi.js +165 -0
  80. package/core/model/api/botApiAuth.js +208 -0
  81. package/core/model/api/botSyncService.js +78 -0
  82. package/core/model/api/gameRecordPayload.js +24 -0
  83. package/core/model/api/makeRequest.js +1004 -0
  84. package/core/model/api/makeRequestFnc.js +17 -0
  85. package/core/model/api/phiApiClient.js +53 -0
  86. package/core/model/api/phiApiErrors.js +121 -0
  87. package/core/model/filesystem/getFile.js +256 -0
  88. package/core/model/filesystem/path.js +84 -0
  89. package/core/model/game/Chart.js +57 -0
  90. package/core/model/game/LevelRecordInfo.js +109 -0
  91. package/core/model/game/SongsInfo.js +46 -0
  92. package/core/model/game/b30Analysis.js +93 -0
  93. package/core/model/game/chartParser/Chart.js +23 -0
  94. package/core/model/game/chartParser/JudgeLine.js +38 -0
  95. package/core/model/game/chartParser/base/JudgeLineEvent.js +99 -0
  96. package/core/model/game/chartParser/base/Note.js +44 -0
  97. package/core/model/game/chartParser/base/SpeedEvent.js +25 -0
  98. package/core/model/game/chartParser/base/baseType.js +2 -0
  99. package/core/model/game/constNum.js +215 -0
  100. package/core/model/game/fCompute.js +711 -0
  101. package/core/model/game/getChartTag.js +140 -0
  102. package/core/model/game/getComment.js +132 -0
  103. package/core/model/game/getInfo.js +1069 -0
  104. package/core/model/game/getRksRank.js +77 -0
  105. package/core/model/game/markdown.js +77 -0
  106. package/core/model/integrations/Vika.js +103 -0
  107. package/core/model/integrations/getInfoFromTap.js +80 -0
  108. package/core/model/render/getPic.js +79 -0
  109. package/core/model/render/picmodle.js +496 -0
  110. package/core/model/render/progress-bar.js +44 -0
  111. package/core/model/render/puppeteer.js +431 -0
  112. package/core/model/render/renderPressureHistory.js +71 -0
  113. package/core/model/render/send.js +94 -0
  114. package/core/model/save/Save.js +953 -0
  115. package/core/model/save/analyzeSaveHistory.js +413 -0
  116. package/core/model/save/getBackup.js +328 -0
  117. package/core/model/save/getSave.js +90 -0
  118. package/core/model/save/getSaveFromApi.js +46 -0
  119. package/core/model/save/saveHistory.js +586 -0
  120. package/core/model/save/scoreHistory.js +101 -0
  121. package/core/model/theme/catalog.js +192 -0
  122. package/core/model/theme/installGuard.js +275 -0
  123. package/core/model/theme/installLock.js +174 -0
  124. package/core/model/theme/installer.js +398 -0
  125. package/core/model/theme/manager.js +408 -0
  126. package/core/model/theme/marketClient.js +294 -0
  127. package/core/model/theme/paths.js +42 -0
  128. package/core/model/theme/policy.js +54 -0
  129. package/core/model/theme/recovery.js +167 -0
  130. package/core/model/theme/useService.js +187 -0
  131. package/core/model/type/aliasProposal.d.ts +45 -0
  132. package/core/model/type/type.js +420 -0
  133. package/core/model/user/apiPermission.js +62 -0
  134. package/core/model/user/getBanGroup.js +120 -0
  135. package/core/model/user/getNotes.js +48 -0
  136. package/core/model/user/pluginData.js +101 -0
  137. package/core/model/user/userCredentialStore.js +154 -0
  138. package/core/model/user/userCredentials.js +860 -0
  139. package/core/package.json +60 -0
  140. package/core/resources/0608badSave/save.json +1 -0
  141. package/core/resources/LICENSE +201 -0
  142. package/core/resources/html/analyzeSaveHistory/analyzeSaveHistory.art +220 -0
  143. package/core/resources/html/analyzeSaveHistory/analyzeSaveHistory.css +345 -0
  144. package/core/resources/html/analyzeSaveHistory/analyzeSaveHistory.html +434 -0
  145. package/core/resources/html/arcgrosB19/arcgros.png +0 -0
  146. package/core/resources/html/arcgrosB19/arcgrosB19-orz.html +276 -0
  147. package/core/resources/html/arcgrosB19/arcgrosB19.art +168 -0
  148. package/core/resources/html/arcgrosB19/arcgrosB19.css +637 -0
  149. package/core/resources/html/atlas/atlas-orz.html +313 -0
  150. package/core/resources/html/atlas/atlas.art +194 -0
  151. package/core/resources/html/atlas/atlas.css +695 -0
  152. package/core/resources/html/avatar/-SURREALISM-.png +0 -0
  153. package/core/resources/html/avatar/Alkaid.png +0 -0
  154. package/core/resources/html/avatar/Aphasia.png +0 -0
  155. package/core/resources/html/avatar/Artificial Existence.png +0 -0
  156. package/core/resources/html/avatar/Bonus Time.png +0 -0
  157. package/core/resources/html/avatar/Burn.png +0 -0
  158. package/core/resources/html/avatar/Cereris1.png +0 -0
  159. package/core/resources/html/avatar/Cereris2.png +0 -0
  160. package/core/resources/html/avatar/Cereris3.png +0 -0
  161. package/core/resources/html/avatar/Christmas -2019.png +0 -0
  162. package/core/resources/html/avatar/Cipher1.png +0 -0
  163. package/core/resources/html/avatar/Class Memories.png +0 -0
  164. package/core/resources/html/avatar/Credits.png +0 -0
  165. package/core/resources/html/avatar/Dash.png +0 -0
  166. package/core/resources/html/avatar/Dlyrotz.png +0 -0
  167. package/core/resources/html/avatar/Doppelganger1.png +0 -0
  168. package/core/resources/html/avatar/Doppelganger2.png +0 -0
  169. package/core/resources/html/avatar/Drop it.png +0 -0
  170. package/core/resources/html/avatar/Energy Synergy Matrix1.png +0 -0
  171. package/core/resources/html/avatar/Energy Synergy Matrix2.png +0 -0
  172. package/core/resources/html/avatar/Energy Synergy Matrix3.png +0 -0
  173. package/core/resources/html/avatar/Engine x Start!! (melody mix).png +0 -0
  174. package/core/resources/html/avatar/Engine x Start!! extra 1 -by cycats.png +0 -0
  175. package/core/resources/html/avatar/Eradication Catastrophe.png +0 -0
  176. package/core/resources/html/avatar/FULi AUTO SHOOTER extra 1 -by cycats.png +0 -0
  177. package/core/resources/html/avatar/FULi AUTO SHOOTER.png +0 -0
  178. package/core/resources/html/avatar/Feast/350/277/234/344/270/234/344/271/213/345/256/264-Special1.png +0 -0
  179. package/core/resources/html/avatar/Feast/350/277/234/344/270/234/344/271/213/345/256/264-Special2.png +0 -0
  180. package/core/resources/html/avatar/Feast/350/277/234/344/270/234/344/271/213/345/256/2641.png +0 -0
  181. package/core/resources/html/avatar/Feast/350/277/234/344/270/234/344/271/213/345/256/2642.png +0 -0
  182. package/core/resources/html/avatar/Feast/350/277/234/344/270/234/344/271/213/345/256/2643.png +0 -0
  183. package/core/resources/html/avatar/Feast/350/277/234/344/270/234/344/271/213/345/256/2644.png +0 -0
  184. package/core/resources/html/avatar/Feast/350/277/234/344/270/234/344/271/213/345/256/2645.png +0 -0
  185. package/core/resources/html/avatar/Feast/350/277/234/344/270/234/344/271/213/345/256/2646.png +0 -0
  186. package/core/resources/html/avatar/Feast/350/277/234/344/270/234/344/271/213/345/256/2647.png +0 -0
  187. package/core/resources/html/avatar/Feast/350/277/234/344/270/234/344/271/213/345/256/2648.png +0 -0
  188. package/core/resources/html/avatar/Feast/350/277/234/344/270/234/344/271/213/345/256/2649.png +0 -0
  189. package/core/resources/html/avatar/Find_Me.png +0 -0
  190. package/core/resources/html/avatar/Geopelia 1 -by cycats.png +0 -0
  191. package/core/resources/html/avatar/Geopelia 2 -by cycats.png +0 -0
  192. package/core/resources/html/avatar/Geopelia 3 -by /344/275/225/345/221/265.png +0 -0
  193. package/core/resources/html/avatar/Geopelia in WEPLAY -by /346/265/267/344/272/247.png +0 -0
  194. package/core/resources/html/avatar/Gino-AprilFool.png +0 -0
  195. package/core/resources/html/avatar/Glaciaxion.png +0 -0
  196. package/core/resources/html/avatar/HumaN.png +0 -0
  197. package/core/resources/html/avatar/Indelible Scar.png +0 -0
  198. package/core/resources/html/avatar/Introduction.png +0 -0
  199. package/core/resources/html/avatar/Khronostasis Katharsis.png +0 -0
  200. package/core/resources/html/avatar/MARENOL.png +0 -0
  201. package/core/resources/html/avatar/Marenol extra 1 -by /345/226/265n/350/221/265.png +0 -0
  202. package/core/resources/html/avatar/Non-Melodic Ragez (MUG Edit).png +0 -0
  203. package/core/resources/html/avatar/Non-Melodic Ragez extra 1 -by cycats.png +0 -0
  204. package/core/resources/html/avatar/Oblivion +0 -0
  205. package/core/resources/html/avatar/OblivionPHIN.png +0 -0
  206. package/core/resources/html/avatar/Para.png +0 -0
  207. package/core/resources/html/avatar/Pixel Rebelz1.png +0 -0
  208. package/core/resources/html/avatar/Pixel Rebelz2.png +0 -0
  209. package/core/resources/html/avatar/Pixel Rebelz3.png +0 -0
  210. package/core/resources/html/avatar/RIPPER extra 1 -by cycats.png +0 -0
  211. package/core/resources/html/avatar/RIPPER extra 2 -by /344/275/225/345/221/265.png +0 -0
  212. package/core/resources/html/avatar/Sein.png +0 -0
  213. package/core/resources/html/avatar/ShineAfter-/346/260/264/351/235/222-Special.png +0 -0
  214. package/core/resources/html/avatar/ShineAfter-/346/260/264/351/235/222-Special2.png +0 -0
  215. package/core/resources/html/avatar/ShineAfter-/346/260/264/351/235/222-/346/231/256/351/200/232.png +0 -0
  216. package/core/resources/html/avatar/ShineAfter-/346/260/264/351/235/222-/346/233/262/347/273/230Ver.png +0 -0
  217. package/core/resources/html/avatar/ShineAfter-/351/270/240-Special.png +0 -0
  218. package/core/resources/html/avatar/ShineAfter-/351/270/240-/346/231/256/351/200/232.png +0 -0
  219. package/core/resources/html/avatar/ShineAfter-/351/270/240-/346/233/262/347/273/230Ver.png +0 -0
  220. package/core/resources/html/avatar/ShineAfter-/351/270/240/345/222/214/346/260/264/351/235/222-Special.png +0 -0
  221. package/core/resources/html/avatar/Snow Desert.png +0 -0
  222. package/core/resources/html/avatar/Sparkle New Life.png +0 -0
  223. package/core/resources/html/avatar/Sultan Rage.png +0 -0
  224. package/core/resources/html/avatar/Winter /342/206/221cube/342/206/223.png +0 -0
  225. package/core/resources/html/avatar/Xenolith.png +0 -0
  226. package/core/resources/html/avatar/[PRAW].png +0 -0
  227. package/core/resources/html/avatar/dB doll.png +0 -0
  228. package/core/resources/html/avatar/ripper.png +0 -0
  229. package/core/resources/html/avatar//343/202/202/343/201/272/343/202/202/343/201/2721.png +0 -0
  230. package/core/resources/html/avatar//343/202/202/343/201/272/343/202/202/343/201/2722.png +0 -0
  231. package/core/resources/html/avatar//344/270/207/345/220/250/345/214/277/345/220/215/344/277/241.png +0 -0
  232. package/core/resources/html/avatar//344/272/221/345/245/263/345/255/251 extra 1 -by /345/276/241/345/235/202/346/236/234/345/255/220.png +0 -0
  233. package/core/resources/html/avatar//344/272/221/345/245/263/345/255/251.png +0 -0
  234. package/core/resources/html/avatar//345/205/211.png +0 -0
  235. package/core/resources/html/avatar//345/274/200/345/277/203/347/227/2051.png +0 -0
  236. package/core/resources/html/avatar//345/274/200/345/277/203/347/227/2052.png +0 -0
  237. package/core/resources/html/avatar//346/260/264/351/235/222-/344/270/200/345/221/250/345/271/264/347/233/270/350/201/232.png +0 -0
  238. package/core/resources/html/avatar//346/267/267/344/271/261-Confusion.png +0 -0
  239. package/core/resources/html/avatar//347/213/202/345/226/234/350/230/255/350/210/2361.png +0 -0
  240. package/core/resources/html/avatar//347/213/202/345/226/234/350/230/255/350/210/2362.png +0 -0
  241. package/core/resources/html/avatar//347/223/267/345/262/201-Special.png +0 -0
  242. package/core/resources/html/avatar//347/223/267/345/262/2011.png +0 -0
  243. package/core/resources/html/avatar//347/223/267/345/262/2012.png +0 -0
  244. package/core/resources/html/avatar//347/223/267/345/262/2013.png +0 -0
  245. package/core/resources/html/avatar//347/223/267/345/262/2014.png +0 -0
  246. package/core/resources/html/avatar//347/223/267/345/262/2015.png +0 -0
  247. package/core/resources/html/avatar//347/223/267/345/262/2016.png +0 -0
  248. package/core/resources/html/avatar//347/223/267/345/262/2017.png +0 -0
  249. package/core/resources/html/avatar//347/233/217/350/214/227-Special.png +0 -0
  250. package/core/resources/html/avatar//347/233/217/350/214/2271.png +0 -0
  251. package/core/resources/html/avatar//347/233/217/350/214/2272.png +0 -0
  252. package/core/resources/html/avatar//347/233/217/350/214/2273.png +0 -0
  253. package/core/resources/html/avatar//350/217/257/347/201/257/347/210/2611.png +0 -0
  254. package/core/resources/html/avatar//350/217/257/347/201/257/347/210/2612.png +0 -0
  255. package/core/resources/html/avatar//350/220/244/347/201/253/350/231/253/343/201/256/346/200/2501.png +0 -0
  256. package/core/resources/html/avatar//350/220/244/347/201/253/350/231/253/343/201/256/346/200/2502.png +0 -0
  257. package/core/resources/html/avatar//351/235/222/350/212/275-Special.png +0 -0
  258. package/core/resources/html/avatar//351/235/222/350/212/275.png +0 -0
  259. package/core/resources/html/avatar//351/243/216/345/261/277.png +0 -0
  260. package/core/resources/html/avatar//351/270/240-AprilFool.png +0 -0
  261. package/core/resources/html/avatar//351/270/240-/344/270/200/345/221/250/345/271/264/347/233/270/350/201/232.png +0 -0
  262. package/core/resources/html/b19/b19.art +490 -0
  263. package/core/resources/html/b19/b19.css +1261 -0
  264. package/core/resources/html/b19/b19666.art +350 -0
  265. package/core/resources/html/b19/b19666.css +1409 -0
  266. package/core/resources/html/b19/b19666.html +481 -0
  267. package/core/resources/html/b19/b19title.css +120 -0
  268. package/core/resources/html/b19/best19.html +853 -0
  269. package/core/resources/html/b19/dss2-orz.html +428 -0
  270. package/core/resources/html/b19/dss2.art +298 -0
  271. package/core/resources/html/b19/dss2.css +776 -0
  272. package/core/resources/html/b19/res/666.jpeg +0 -0
  273. package/core/resources/html/chap/chap-orz.html +93 -0
  274. package/core/resources/html/chap/chap.art +87 -0
  275. package/core/resources/html/chap/chap.css +365 -0
  276. package/core/resources/html/chartImg/chartImg.art +169 -0
  277. package/core/resources/html/chartImg/chartImg.css +439 -0
  278. package/core/resources/html/chartImg/echarts-wordcloud.min.js +3 -0
  279. package/core/resources/html/chartImg/echarts.js +85796 -0
  280. package/core/resources/html/chartImg/img/Drag.png +0 -0
  281. package/core/resources/html/chartImg/img/DragHL.png +0 -0
  282. package/core/resources/html/chartImg/img/Flick.png +0 -0
  283. package/core/resources/html/chartImg/img/FlickHL.png +0 -0
  284. package/core/resources/html/chartImg/img/Hold.png +0 -0
  285. package/core/resources/html/chartImg/img/HoldHL.png +0 -0
  286. package/core/resources/html/chartImg/img/Hold_End.png +0 -0
  287. package/core/resources/html/chartImg/img/Hold_Head.png +0 -0
  288. package/core/resources/html/chartImg/img/Hold_HeadHL.png +0 -0
  289. package/core/resources/html/chartImg/img/Tap.png +0 -0
  290. package/core/resources/html/chartImg/img/TapHL.png +0 -0
  291. package/core/resources/html/chartInfo/chartInfo-orz.html +250 -0
  292. package/core/resources/html/chartInfo/chartInfo.art +222 -0
  293. package/core/resources/html/chartInfo/chartInfo.css +265 -0
  294. package/core/resources/html/chartInfo/echarts-wordcloud.min.js +3 -0
  295. package/core/resources/html/chartInfo/echarts.js +85796 -0
  296. package/core/resources/html/clg/clg-orz.html +218 -0
  297. package/core/resources/html/clg/clg.art +88 -0
  298. package/core/resources/html/clg/clg.css +230 -0
  299. package/core/resources/html/clg/demo.jpg +0 -0
  300. package/core/resources/html/common/common.css +319 -0
  301. package/core/resources/html/common/css/playerInfo.css +136 -0
  302. package/core/resources/html/common/font/Aldrich-Regular.ttf +0 -0
  303. package/core/resources/html/common/font/HIMALAYA.TTF +0 -0
  304. package/core/resources/html/common/font/NotoColorEmoji-Regular.ttf +0 -0
  305. package/core/resources/html/common/font/NotoSans-Regular.ttf +0 -0
  306. package/core/resources/html/common/font/NotoSansArabic.ttf +0 -0
  307. package/core/resources/html/common/font/NotoSansCanadianAboriginal.ttf +0 -0
  308. package/core/resources/html/common/font/NotoSansJP.ttf +0 -0
  309. package/core/resources/html/common/font/NotoSansKannada.ttf +0 -0
  310. package/core/resources/html/common/font/NotoSansMath-Regular.ttf +0 -0
  311. package/core/resources/html/common/font/NotoSansSymbols2.ttf +0 -0
  312. package/core/resources/html/common/font/phi.ttf +0 -0
  313. package/core/resources/html/common/font//345/220/236/345/274/245/346/201/260/344/277/212.ttf +0 -0
  314. package/core/resources/html/common/layout/default.art +324 -0
  315. package/core/resources/html/common/layout/elem.art +21 -0
  316. package/core/resources/html/common/theme/snow/snow.css +17 -0
  317. package/core/resources/html/common/theme/snow/snow.js +19 -0
  318. package/core/resources/html/common/theme/star/star.js +201 -0
  319. package/core/resources/html/common/theme/topText/topText.css +15 -0
  320. package/core/resources/html/common/theme/topText/topText.js +30 -0
  321. package/core/resources/html/difficultyHistory/difficultyHistory-orz.html +361 -0
  322. package/core/resources/html/difficultyHistory/difficultyHistory.art +102 -0
  323. package/core/resources/html/difficultyHistory/difficultyHistory.css +205 -0
  324. package/core/resources/html/guess/guess-orz.html +50 -0
  325. package/core/resources/html/guess/guess.art +51 -0
  326. package/core/resources/html/guess/guess.css +33 -0
  327. package/core/resources/html/help/help-orz.html +73 -0
  328. package/core/resources/html/help/help.art +84 -0
  329. package/core/resources/html/help/help.css +266 -0
  330. package/core/resources/html/historyB30/historyB30-orz.html +168 -0
  331. package/core/resources/html/historyB30/historyB30.art +143 -0
  332. package/core/resources/html/historyB30/historyB30.css +270 -0
  333. package/core/resources/html/ill/ill-orz.html +21 -0
  334. package/core/resources/html/ill/ill.art +23 -0
  335. package/core/resources/html/ill/ill.css +79 -0
  336. package/core/resources/html/jrrp/ShineAfter.removebg.png +0 -0
  337. package/core/resources/html/jrrp/jrrp-orz.html +68 -0
  338. package/core/resources/html/jrrp/jrrp.art +58 -0
  339. package/core/resources/html/jrrp/jrrp.css +251 -0
  340. package/core/resources/html/list/list-orz.html +35 -0
  341. package/core/resources/html/list/list.art +67 -0
  342. package/core/resources/html/list/list.css +259 -0
  343. package/core/resources/html/lvsco/demo.jpg +0 -0
  344. package/core/resources/html/lvsco/lvsco-orz.html +334 -0
  345. package/core/resources/html/lvsco/lvsco.art +314 -0
  346. package/core/resources/html/lvsco/lvsco.css +706 -0
  347. package/core/resources/html/market/detail.art +44 -0
  348. package/core/resources/html/market/market.art +86 -0
  349. package/core/resources/html/market/market.css +64 -0
  350. package/core/resources/html/newSong/newSong.art +34 -0
  351. package/core/resources/html/newSong/newSong.css +48 -0
  352. package/core/resources/html/newSong/newSong.html +23 -0
  353. package/core/resources/html/newnotice/newnotice.art +47 -0
  354. package/core/resources/html/newnotice/newnotice.css +83 -0
  355. package/core/resources/html/otherimg/0.png +0 -0
  356. package/core/resources/html/otherimg/1.png +0 -0
  357. package/core/resources/html/otherimg/2.png +0 -0
  358. package/core/resources/html/otherimg/3.png +0 -0
  359. package/core/resources/html/otherimg/4.png +0 -0
  360. package/core/resources/html/otherimg/5.png +0 -0
  361. package/core/resources/html/otherimg/A.png +0 -0
  362. package/core/resources/html/otherimg/B.png +0 -0
  363. package/core/resources/html/otherimg/C.png +0 -0
  364. package/core/resources/html/otherimg/F.png +0 -0
  365. package/core/resources/html/otherimg/FC.png +0 -0
  366. package/core/resources/html/otherimg/NEW.png +0 -0
  367. package/core/resources/html/otherimg/Phigros_Icon_3.0.0.png +0 -0
  368. package/core/resources/html/otherimg/S.png +0 -0
  369. package/core/resources/html/otherimg/Star1.png +0 -0
  370. package/core/resources/html/otherimg/Star2.png +0 -0
  371. package/core/resources/html/otherimg/V.png +0 -0
  372. package/core/resources/html/otherimg/callback.png +0 -0
  373. package/core/resources/html/otherimg/chapHelp.png +0 -0
  374. package/core/resources/html/otherimg/dan_code.png +0 -0
  375. package/core/resources/html/otherimg/data.png +0 -0
  376. package/core/resources/html/otherimg/helpDoc.png +0 -0
  377. package/core/resources/html/otherimg/nickSuggest.png +0 -0
  378. package/core/resources/html/otherimg/phi.png +0 -0
  379. package/core/resources/html/otherimg/phigros.png +0 -0
  380. package/core/resources/html/otherimg/snow1.png +0 -0
  381. package/core/resources/html/otherimg/snow2.png +0 -0
  382. package/core/resources/html/otherimg/snow3.png +0 -0
  383. package/core/resources/html/otherimg/snow4.png +0 -0
  384. package/core/resources/html/otherimg/title.png +0 -0
  385. package/core/resources/html/rand/rand-orz.html +62 -0
  386. package/core/resources/html/rand/rand.art +59 -0
  387. package/core/resources/html/rand/rand.css +257 -0
  388. package/core/resources/html/rankingList/rankingList-orz.html +456 -0
  389. package/core/resources/html/rankingList/rankingList.art +171 -0
  390. package/core/resources/html/rankingList/rankingList.css +570 -0
  391. package/core/resources/html/rankingList-old/rankingList-orz.html +300 -0
  392. package/core/resources/html/rankingList-old/rankingList.art +89 -0
  393. package/core/resources/html/rankingList-old/rankingList.css +265 -0
  394. package/core/resources/html/score/score-orz.html +625 -0
  395. package/core/resources/html/score/score.art +249 -0
  396. package/core/resources/html/score/score.css +435 -0
  397. package/core/resources/html/score/scoreOld-orz.html +128 -0
  398. package/core/resources/html/score/scoreOld.art +230 -0
  399. package/core/resources/html/score/scoreOld.css +475 -0
  400. package/core/resources/html/score/scoreRankList-orz.html +613 -0
  401. package/core/resources/html/score/scoreRankList.art +231 -0
  402. package/core/resources/html/score/scoreRankList.css +197 -0
  403. package/core/resources/html/setting/setting-orz.html +123 -0
  404. package/core/resources/html/setting/setting.art +72 -0
  405. package/core/resources/html/setting/setting.css +170 -0
  406. package/core/resources/html/setting/userSetting-orz.html +132 -0
  407. package/core/resources/html/setting/userSetting.art +48 -0
  408. package/core/resources/html/setting/userSetting.css +160 -0
  409. package/core/resources/html/sign/sign-orz.html +262 -0
  410. package/core/resources/html/sign/sign.art +163 -0
  411. package/core/resources/html/sign/sign.css +706 -0
  412. package/core/resources/html/suggest/suggest.art +271 -0
  413. package/core/resources/html/suggest/suggest.css +300 -0
  414. package/core/resources/html/table/table-orz.html +228 -0
  415. package/core/resources/html/table/table.art +163 -0
  416. package/core/resources/html/table/table.css +401 -0
  417. package/core/resources/html/tasks/tasks-orz.html +83 -0
  418. package/core/resources/html/tasks/tasks.art +118 -0
  419. package/core/resources/html/tasks/tasks.css +334 -0
  420. package/core/resources/html/update/update-orz.html +735 -0
  421. package/core/resources/html/update/update.art +259 -0
  422. package/core/resources/html/update/update.css +676 -0
  423. package/core/resources/html/userinfo/userinfo-old-orz.html +408 -0
  424. package/core/resources/html/userinfo/userinfo-old.art +233 -0
  425. package/core/resources/html/userinfo/userinfo-old.css +466 -0
  426. package/core/resources/html/userinfo/userinfo-orz.html +543 -0
  427. package/core/resources/html/userinfo/userinfo.art +288 -0
  428. package/core/resources/html/userinfo/userinfo.css +757 -0
  429. package/core/resources/info/DLC/arc.json +412 -0
  430. package/core/resources/info/DLC/chunithm.json +1172 -0
  431. package/core/resources/info/DLC/dynamix.json +455 -0
  432. package/core/resources/info/DLC/fanshi.json +132 -0
  433. package/core/resources/info/DLC/lanota.json +412 -0
  434. package/core/resources/info/DLC/maimai.json +1110 -0
  435. package/core/resources/info/DLC/orz.json +158 -0
  436. package/core/resources/info/DLC/rizline.json +40 -0
  437. package/core/resources/info/DLC/rotaeno.json +159 -0
  438. package/core/resources/info/DLC/ryceam.json +53 -0
  439. package/core/resources/info/avatar.txt +109 -0
  440. package/core/resources/info/chaplist.yaml +102 -0
  441. package/core/resources/info/help/api.json +83 -0
  442. package/core/resources/info/help.json +436 -0
  443. package/core/resources/info/info.csv +315 -0
  444. package/core/resources/info/infolist.json +1758 -0
  445. package/core/resources/info/jrrp.json +92 -0
  446. package/core/resources/info/nicklist.yaml +2373 -0
  447. package/core/resources/info/notesInfo.json +1 -0
  448. package/core/resources/info/notice.json +11 -0
  449. package/core/resources/info/oldInfo/100/change.csv +218 -0
  450. package/core/resources/info/oldInfo/100/info.json +7 -0
  451. package/core/resources/info/oldInfo/101/change.csv +218 -0
  452. package/core/resources/info/oldInfo/101/info.json +7 -0
  453. package/core/resources/info/oldInfo/102/change.csv +223 -0
  454. package/core/resources/info/oldInfo/102/info.json +7 -0
  455. package/core/resources/info/oldInfo/103/change.csv +224 -0
  456. package/core/resources/info/oldInfo/103/info.json +7 -0
  457. package/core/resources/info/oldInfo/104/change.csv +225 -0
  458. package/core/resources/info/oldInfo/104/info.json +7 -0
  459. package/core/resources/info/oldInfo/105/change.csv +228 -0
  460. package/core/resources/info/oldInfo/105/info.json +7 -0
  461. package/core/resources/info/oldInfo/106/change.csv +229 -0
  462. package/core/resources/info/oldInfo/106/info.json +7 -0
  463. package/core/resources/info/oldInfo/108/change.csv +233 -0
  464. package/core/resources/info/oldInfo/108/info.json +7 -0
  465. package/core/resources/info/oldInfo/109/change.csv +235 -0
  466. package/core/resources/info/oldInfo/109/info.json +7 -0
  467. package/core/resources/info/oldInfo/110/change.csv +235 -0
  468. package/core/resources/info/oldInfo/110/info.json +7 -0
  469. package/core/resources/info/oldInfo/111/change.csv +242 -0
  470. package/core/resources/info/oldInfo/111/info.json +7 -0
  471. package/core/resources/info/oldInfo/112/change.csv +242 -0
  472. package/core/resources/info/oldInfo/112/info.json +7 -0
  473. package/core/resources/info/oldInfo/113/change.csv +244 -0
  474. package/core/resources/info/oldInfo/113/info.json +7 -0
  475. package/core/resources/info/oldInfo/115/change.csv +247 -0
  476. package/core/resources/info/oldInfo/115/info.json +7 -0
  477. package/core/resources/info/oldInfo/116/change.csv +249 -0
  478. package/core/resources/info/oldInfo/116/info.json +7 -0
  479. package/core/resources/info/oldInfo/118/change.csv +252 -0
  480. package/core/resources/info/oldInfo/118/info.json +7 -0
  481. package/core/resources/info/oldInfo/119/change.csv +255 -0
  482. package/core/resources/info/oldInfo/119/info.json +7 -0
  483. package/core/resources/info/oldInfo/120/change.csv +255 -0
  484. package/core/resources/info/oldInfo/120/info.json +7 -0
  485. package/core/resources/info/oldInfo/121/change.csv +257 -0
  486. package/core/resources/info/oldInfo/121/info.json +7 -0
  487. package/core/resources/info/oldInfo/122/change.csv +260 -0
  488. package/core/resources/info/oldInfo/122/info.json +7 -0
  489. package/core/resources/info/oldInfo/123/change.csv +260 -0
  490. package/core/resources/info/oldInfo/123/info.json +7 -0
  491. package/core/resources/info/oldInfo/124/change.csv +262 -0
  492. package/core/resources/info/oldInfo/124/info.json +7 -0
  493. package/core/resources/info/oldInfo/125/change.csv +266 -0
  494. package/core/resources/info/oldInfo/125/info.json +7 -0
  495. package/core/resources/info/oldInfo/126/change.csv +269 -0
  496. package/core/resources/info/oldInfo/126/info.json +7 -0
  497. package/core/resources/info/oldInfo/127/change.csv +270 -0
  498. package/core/resources/info/oldInfo/127/info.json +7 -0
  499. package/core/resources/info/oldInfo/128/change.csv +273 -0
  500. package/core/resources/info/oldInfo/128/info.json +7 -0
  501. package/core/resources/info/oldInfo/129/change.csv +274 -0
  502. package/core/resources/info/oldInfo/129/info.json +7 -0
  503. package/core/resources/info/oldInfo/130/change.csv +275 -0
  504. package/core/resources/info/oldInfo/130/info.json +7 -0
  505. package/core/resources/info/oldInfo/131/change.csv +278 -0
  506. package/core/resources/info/oldInfo/131/info.json +7 -0
  507. package/core/resources/info/oldInfo/132/change.csv +280 -0
  508. package/core/resources/info/oldInfo/132/info.json +7 -0
  509. package/core/resources/info/oldInfo/133/change.csv +284 -0
  510. package/core/resources/info/oldInfo/133/info.json +7 -0
  511. package/core/resources/info/oldInfo/134/change.csv +286 -0
  512. package/core/resources/info/oldInfo/134/info.json +7 -0
  513. package/core/resources/info/oldInfo/135/change.csv +290 -0
  514. package/core/resources/info/oldInfo/135/info.json +7 -0
  515. package/core/resources/info/oldInfo/136/change.csv +290 -0
  516. package/core/resources/info/oldInfo/136/info.json +7 -0
  517. package/core/resources/info/oldInfo/137/change.csv +293 -0
  518. package/core/resources/info/oldInfo/137/info.json +7 -0
  519. package/core/resources/info/oldInfo/138/change.csv +295 -0
  520. package/core/resources/info/oldInfo/138/info.json +7 -0
  521. package/core/resources/info/oldInfo/139/change.csv +295 -0
  522. package/core/resources/info/oldInfo/139/info.json +7 -0
  523. package/core/resources/info/oldInfo/140/change.csv +298 -0
  524. package/core/resources/info/oldInfo/140/info.json +7 -0
  525. package/core/resources/info/oldInfo/141/change.csv +300 -0
  526. package/core/resources/info/oldInfo/141/info.json +7 -0
  527. package/core/resources/info/oldInfo/142/change.csv +302 -0
  528. package/core/resources/info/oldInfo/142/info.json +7 -0
  529. package/core/resources/info/oldInfo/143/change.csv +303 -0
  530. package/core/resources/info/oldInfo/143/info.json +7 -0
  531. package/core/resources/info/oldInfo/144/change.csv +303 -0
  532. package/core/resources/info/oldInfo/144/info.json +7 -0
  533. package/core/resources/info/oldInfo/145/change.csv +305 -0
  534. package/core/resources/info/oldInfo/145/info.json +7 -0
  535. package/core/resources/info/oldInfo/146/change.csv +305 -0
  536. package/core/resources/info/oldInfo/146/info.json +7 -0
  537. package/core/resources/info/oldInfo/148/change.csv +307 -0
  538. package/core/resources/info/oldInfo/148/info.json +7 -0
  539. package/core/resources/info/oldInfo/149/change.csv +307 -0
  540. package/core/resources/info/oldInfo/149/info.json +7 -0
  541. package/core/resources/info/oldInfo/151/change.csv +309 -0
  542. package/core/resources/info/oldInfo/151/info.json +7 -0
  543. package/core/resources/info/oldInfo/152/change.csv +312 -0
  544. package/core/resources/info/oldInfo/152/info.json +7 -0
  545. package/core/resources/info/oldInfo/153/change.csv +313 -0
  546. package/core/resources/info/oldInfo/153/info.json +7 -0
  547. package/core/resources/info/oldInfo/154/change.csv +315 -0
  548. package/core/resources/info/oldInfo/154/info.json +7 -0
  549. package/core/resources/info/oldInfo/92/change.csv +204 -0
  550. package/core/resources/info/oldInfo/92/info.json +7 -0
  551. package/core/resources/info/oldInfo/93/change.csv +206 -0
  552. package/core/resources/info/oldInfo/93/info.json +7 -0
  553. package/core/resources/info/oldInfo/94/change.csv +209 -0
  554. package/core/resources/info/oldInfo/94/info.json +7 -0
  555. package/core/resources/info/oldInfo/95/change.csv +211 -0
  556. package/core/resources/info/oldInfo/95/info.json +7 -0
  557. package/core/resources/info/oldInfo/96/change.csv +216 -0
  558. package/core/resources/info/oldInfo/96/info.json +7 -0
  559. package/core/resources/info/oldInfo/98/change.csv +216 -0
  560. package/core/resources/info/oldInfo/98/info.json +7 -0
  561. package/core/resources/info/oldInfo/99/change.csv +217 -0
  562. package/core/resources/info/oldInfo/99/info.json +7 -0
  563. package/core/resources/info/oldNotesInfo.json +1 -0
  564. package/core/resources/info/sentences.json +1 -0
  565. package/core/resources/info/spinfo.json +362 -0
  566. package/core/resources/info/tips.txt +278 -0
  567. package/core/resources/otherill//351/233/252/351/231/215SP.png +0 -0
  568. package/core/resources/song-select.html +535 -0
  569. package/core/resources/themes/README.md +24 -0
  570. package/core/resources/themes/milthm/A.png +0 -0
  571. package/core/resources/themes/milthm/B.png +0 -0
  572. package/core/resources/themes/milthm/C.png +0 -0
  573. package/core/resources/themes/milthm/F.png +0 -0
  574. package/core/resources/themes/milthm/FC.png +0 -0
  575. package/core/resources/themes/milthm/S.png +0 -0
  576. package/core/resources/themes/milthm/V.png +0 -0
  577. package/core/resources/themes/milthm/arcgrosB19.css +36 -0
  578. package/core/resources/themes/milthm/b19.art +464 -0
  579. package/core/resources/themes/milthm/b19.css +1314 -0
  580. package/core/resources/themes/milthm/bg.png +0 -0
  581. package/core/resources/themes/milthm/clg.css +37 -0
  582. package/core/resources/themes/milthm/difficultyHistory.css +42 -0
  583. package/core/resources/themes/milthm/font.ttf +0 -0
  584. package/core/resources/themes/milthm/help.css +36 -0
  585. package/core/resources/themes/milthm/historyB30.css +48 -0
  586. package/core/resources/themes/milthm/info.yaml +45 -0
  587. package/core/resources/themes/milthm/list.css +39 -0
  588. package/core/resources/themes/milthm/phi.png +0 -0
  589. package/core/resources/themes/milthm/setting.css +37 -0
  590. package/core/resources/themes/milthm/sign.css +81 -0
  591. package/core/resources/themes/milthm/suggest.css +40 -0
  592. package/core/resources/themes/milthm/table.css +54 -0
  593. package/core/resources/themes/milthm/update.css +68 -0
  594. package/core/tests/aliasProposalService.test.js +82 -0
  595. package/core/tests/apiAccountDeletion.test.js +125 -0
  596. package/core/tests/apiPermission.test.js +44 -0
  597. package/core/tests/apiVersion.test.js +55 -0
  598. package/core/tests/b30Analysis.test.js +38 -0
  599. package/core/tests/backgroundIllustration.test.js +21 -0
  600. package/core/tests/botApiAuthRecovery.test.js +148 -0
  601. package/core/tests/botSyncService.test.js +42 -0
  602. package/core/tests/fileWatcherRegistry.test.js +44 -0
  603. package/core/tests/gameRecordPayload.test.js +34 -0
  604. package/core/tests/guessLetter.test.js +79 -0
  605. package/core/tests/makeRequestHandling.test.js +118 -0
  606. package/core/tests/marketOverflow.test.js +31 -0
  607. package/core/tests/platformTemplateCache.test.js +92 -0
  608. package/core/tests/pluginDataThemePreference.test.js +36 -0
  609. package/core/tests/processCleanup.test.js +58 -0
  610. package/core/tests/puppeteerShutdown.test.js +17 -0
  611. package/core/tests/renderPressureHistory.test.js +59 -0
  612. package/core/tests/saveStats.test.js +26 -0
  613. package/core/tests/themeInstallGuard.test.js +289 -0
  614. package/core/tests/themeManager.test.js +625 -0
  615. package/core/tests/themeManagerInit.test.js +11 -0
  616. package/core/tests/themeMarket.test.js +855 -0
  617. package/core/tests/themePageRouting.test.js +431 -0
  618. package/core/tests/themePolicy.test.js +28 -0
  619. package/core/tests/themeStorageRecovery.test.js +192 -0
  620. package/core/tests/userCredentialModuleBoundaries.test.js +41 -0
  621. package/core/tests/userCredentials.test.js +285 -0
  622. package/core/tsconfig.json +32 -0
  623. package/core/types/vendor.d.ts +14 -0
  624. package/lib/index.cjs +358 -0
  625. package/lib/index.d.cts +182 -0
  626. package/lib/index.d.ts +182 -0
  627. package/lib/index.js +320 -0
  628. package/package.json +74 -0
  629. package/readme.md +3 -0
@@ -0,0 +1,860 @@
1
+ import logger from '../../components/Logger.js'
2
+ import { Config } from '../../components/index.js'
3
+ import platform from '../../components/platform/index.js'
4
+ import PhigrosUser from '../../lib/PhigrosUser.js'
5
+ import { canUseApi } from './apiPermission.js'
6
+ import { PhiApiError } from '../api/phiApiErrors.js'
7
+ import getSave from '../save/getSave.js'
8
+ import getSaveFromApi from '../save/getSaveFromApi.js'
9
+ import makeRequest from '../api/makeRequest.js'
10
+ import makeRequestFnc from '../api/makeRequestFnc.js'
11
+ import userCredentialStore from './userCredentialStore.js'
12
+ import Save from '../save/Save.js'
13
+ import saveHistory from '../save/saveHistory.js'
14
+ import getNotes from './getNotes.js'
15
+ import fCompute from '../game/fCompute.js'
16
+ import { LevelNum } from '../game/constNum.js'
17
+ import getInfo from '../game/getInfo.js'
18
+
19
+ /** @typedef {{platform_name: string, platform_id: string, create_at: string, update_at: string, authentication: number, authentication_label?: string, binding_type?: 'bot'|'legacy', bot_display_name?: string, migration_notice?: string|null}} PlatformBindingInfo */
20
+ /** @typedef {{data: any, user_id: number, phigros_token: string, api_token: string, create_at: string, update_at: string, platform_data: PlatformBindingInfo[]}} PlatformBindingsResponse */
21
+ /** @typedef {{ignoreUnboundError?: boolean}} UserCredentialApiOptions */
22
+ /** @import {botEvent} from "../../components/baseClass.js" */
23
+ /**
24
+ * 单个平台事件用户的凭证与存档操作入口。
25
+ * 业务处理器应通过 `UserCredentials.fromEvent(e)` 创建实例,实例方法不得操作其他用户。
26
+ */
27
+ export class UserCredentials {
28
+ /**
29
+ * 创建绑定到单个用户 ID 的凭证实例。
30
+ * @param {string | number} userId 当前事件的用户 ID
31
+ * @param {{event?: botEvent, store?: typeof userCredentialStore}} [dependencies] 测试或内部注入的依赖
32
+ */
33
+ constructor(userId, { event = undefined, store = userCredentialStore } = {}) {
34
+ const normalizedUserId = String(userId ?? '').trim()
35
+ if (!normalizedUserId) throw new TypeError('UserCredentials requires e.userId')
36
+ this.userId = normalizedUserId
37
+ this.event = event
38
+ ? { ...event, user_id: normalizedUserId, userId: normalizedUserId }
39
+ : undefined
40
+ this.store = store
41
+ }
42
+
43
+ /**
44
+ * 根据平台事件创建用户凭证实例,优先读取标准字段 `e.userId`,并兼容旧字段 `e.user_id`。
45
+ * @param {any} e 当前平台事件
46
+ * @param {{store?: typeof userCredentialStore}} [dependencies] 测试或内部注入的依赖
47
+ * @returns {UserCredentials} 仅能操作当前事件用户的凭证实例
48
+ */
49
+ static fromEvent(e, dependencies = {}) {
50
+ const userId = e?.userId ?? e?.user_id
51
+ return new UserCredentials(userId, { ...dependencies, event: e })
52
+ }
53
+
54
+ /**
55
+ * 获取实例关联的平台事件;需要平台上下文的方法在事件缺失时直接拒绝执行。
56
+ * @returns 当前平台事件
57
+ */
58
+ requireEvent() {
59
+ if (!this.event) throw new TypeError('This UserCredentials operation requires an event')
60
+ return this.event
61
+ }
62
+
63
+ /**
64
+ * 生成当前 Bot 平台用户的 API 参数,可选附加本地 SSTK或 API ID。
65
+ * @param {boolean} [withAuthentication=false] 是否附加当前用户认证信息
66
+ * @returns {Promise<import('../api/makeRequest.js').platformAuth & {token?: phigrosToken, api_user_id?: apiUserId}>} 平台及可选认证参数
67
+ */
68
+ async platformParams(withAuthentication = false) {
69
+ const params = makeRequestFnc.makePlatform(this.requireEvent())
70
+ if (!withAuthentication) return params
71
+ const { sessionToken, apiId } = await this.getLocalCredentials()
72
+ if (sessionToken) return { ...params, token: sessionToken }
73
+ if (apiId) return { ...params, api_user_id: apiId }
74
+ throw new PhiApiError('当前 Bot 本地尚未绑定用户', 404, 'binding_not_found')
75
+ }
76
+
77
+ /**
78
+ * 生成具体 API 接口的执行上下文,日志标签和级别由 makeRequest 按接口固定。
79
+ * @param {UserCredentialApiOptions} [options] 用户接口允许的错误选项
80
+ * @returns {import('../api/makeRequest.js').ApiRequestExecutionOptions} 当前用户接口的错误处理上下文
81
+ */
82
+ endpointOptions(options = {}) {
83
+ return {
84
+ event: this.requireEvent(),
85
+ ignoreUnboundError: options.ignoreUnboundError ? true : false,
86
+ }
87
+ }
88
+
89
+ // 当前平台用户的本地凭证状态。
90
+
91
+ /** @returns {Promise<phigrosToken>} 当前用户本地保存的 sessionToken */
92
+ async getSessionToken() {
93
+ return this.store.getSessionToken(this.userId)
94
+ }
95
+
96
+ /**
97
+ * 保存当前用户的 sessionToken。
98
+ * @param {phigrosToken} sessionToken 待保存的 Phigros sessionToken
99
+ */
100
+ async setSessionToken(sessionToken) {
101
+ return this.store.setSessionToken(this.userId, sessionToken)
102
+ }
103
+
104
+ /**
105
+ * 删除当前用户的 sessionToken。
106
+ */
107
+ async deleteSessionToken() {
108
+ return this.store.deleteSessionToken(this.userId)
109
+ }
110
+
111
+ /** @returns {Promise<apiUserId>} 当前用户本地保存的 API ID */
112
+ async getApiId() {
113
+ return this.store.getApiId(this.userId)
114
+ }
115
+
116
+ /**
117
+ * 保存当前用户的 API ID。
118
+ * @param {apiUserId | string | number} apiId API 用户 ID
119
+ */
120
+ async setApiId(apiId) {
121
+ return this.store.setApiId(this.userId, /** @type {apiUserId} */(String(apiId)))
122
+ }
123
+
124
+ /** 删除当前用户本地保存的 API ID。 */
125
+ async deleteApiId() {
126
+ return this.store.deleteApiId(this.userId)
127
+ }
128
+
129
+ /**
130
+ * 一次读取当前用户的本地 sessionToken 与 API ID。
131
+ * @returns {Promise<{sessionToken: phigrosToken, apiId: apiUserId}>} 本地凭证快照
132
+ */
133
+ async getLocalCredentials() {
134
+ const [sessionToken, apiId] = await Promise.all([
135
+ this.getSessionToken(),
136
+ this.getApiId(),
137
+ ])
138
+ return { sessionToken, apiId }
139
+ }
140
+
141
+ /** 删除当前用户的 sessionToken 和 API ID。 */
142
+ async clearLocalCredentials() {
143
+ return this.store.clearLocalCredentials(this.userId)
144
+ }
145
+
146
+ /**
147
+ * 切换为本地 sessionToken 身份,并清除可能冲突的 API ID。
148
+ * @param {phigrosToken} sessionToken 新的 sessionToken
149
+ */
150
+ async useSessionToken(sessionToken) {
151
+ await this.setSessionToken(sessionToken)
152
+ await this.deleteApiId()
153
+ }
154
+
155
+ /**
156
+ * 将当前用户切换为本地 API ID身份,并清除可能冲突的 sessionToken。
157
+ * @param {apiUserId | string | number} apiId 新的 API ID
158
+ */
159
+ async useApiId(apiId) {
160
+ await this.setApiId(apiId)
161
+ await this.deleteSessionToken()
162
+ }
163
+
164
+ // 平台绑定与 API 账号操作。
165
+
166
+ /**
167
+ * 使用 API ID为当前 Bot平台账号建立首次绑定,并在方法内部完成错误处理。
168
+ * @param {apiUserId | string | number} apiId 目标 API ID
169
+ * @param {UserCredentialApiOptions} [options] 错误处理策略,可忽略未绑定错误
170
+ * @returns 绑定结果;失败返回 `null`
171
+ */
172
+ async bindWithApiId(apiId, options = {}) {
173
+ return this.bindPlatform(
174
+ { api_user_id: apiId },
175
+ { ...options, ignoreUnboundError: true },
176
+ )
177
+ }
178
+
179
+ /**
180
+ * 使用 sessionToken 为当前 Bot平台账号建立或迁移绑定,并在方法内部完成错误处理。
181
+ * @param {phigrosToken} sessionToken Phigros sessionToken
182
+ * @param {boolean} [isGlobal] 是否使用国际服
183
+ * @param {UserCredentialApiOptions} [options] 错误处理策略,可忽略未绑定错误
184
+ * @returns 绑定结果;失败返回 `null`
185
+ */
186
+ async bindWithSessionToken(sessionToken, isGlobal = undefined, options = {}) {
187
+ return this.bindPlatform({ token: sessionToken, isGlobal }, options)
188
+ }
189
+
190
+ /**
191
+ * 执行绑定原子操作。API 提交成功后才写入本地凭证,避免失败时破坏旧绑定。
192
+ * @param {({token: phigrosToken, api_user_id?: never} | {token?: never, api_user_id: apiUserId | string | number}) & {isGlobal?: boolean}} binding 互斥的绑定凭证与服务器类型
193
+ * @param {UserCredentialApiOptions} [options] 错误处理策略,可忽略未绑定错误
194
+ * @returns API 绑定结果及本地写入警告;失败返回 `null`
195
+ */
196
+ async bindPlatform(binding, options = {}) {
197
+ const { platform, platform_id: platformId } = await this.platformParams()
198
+ const data = await makeRequest.bindBotPlatform(
199
+ {
200
+ platform,
201
+ platformId,
202
+ ...(binding.token ? { token: binding.token } : { apiUserId: binding.api_user_id }),
203
+ ...(binding.isGlobal === true ? { isGlobal: true } : {}),
204
+ },
205
+ {
206
+ event: this.requireEvent(),
207
+ ignoreUnboundError: options.ignoreUnboundError === true,
208
+ errorPrefix: binding.token
209
+ ? 'API绑定暂时不可用,将仅更新当前 Bot 的本地绑定状态。'
210
+ : undefined,
211
+ notifyUser: Boolean(binding.token),
212
+ },
213
+ )
214
+ if (!data) return null
215
+ let localCredentialWarning = false
216
+ try {
217
+ if (binding.token) {
218
+ await this.useSessionToken(binding.token)
219
+ } else {
220
+ await this.useApiId(data.apiUserId)
221
+ }
222
+ } catch {
223
+ localCredentialWarning = true
224
+ logger.warn('[phi-plugin] API绑定已成功,但本地用户凭证写入失败')
225
+ }
226
+ return { ...data, localCredentialWarning }
227
+ }
228
+
229
+ /**
230
+ * 仅解绑当前 Bot本地用户,不请求 API,也不删除 API 共享存档缓存。
231
+ * @returns {Promise<{hadBinding: boolean}>} 本地执行前是否存在凭证
232
+ */
233
+ async unbindLocal() {
234
+ const { sessionToken, apiId } = await this.getLocalCredentials()
235
+ if (sessionToken) await this.deleteLocalSave()
236
+ await this.clearLocalCredentials()
237
+ return { hadBinding: Boolean(sessionToken || apiId) }
238
+ }
239
+
240
+ /**
241
+ * 以当前 Bot、平台用户和 sessionToken 创建别名提案,使审核消息只能返回来源 Bot。
242
+ * @param {import('../type/aliasProposal.js').AliasProposalCreateInput} input 别名提案内容
243
+ * @param {UserCredentialApiOptions} [options] 错误处理策略
244
+ * @returns {Promise<import('../type/aliasProposal.js').AliasProposalRecord | null>} 新建的提案
245
+ */
246
+ async createAliasProposal(input, options = {}) {
247
+ const token = await this.getSessionToken()
248
+ if (!token) throw new PhiApiError('请先绑定 sessionToken。', 404, 'binding_not_found')
249
+ return makeRequest.createAliasProposal({
250
+ ...(await this.platformParams()),
251
+ token,
252
+ alias: input.alias,
253
+ songId: input.songId,
254
+ note: input.note || undefined,
255
+ source: 'bot',
256
+ }, this.endpointOptions(options))
257
+ }
258
+
259
+ /**
260
+ * 为当前 API 账号设置新的 API Token。
261
+ * @param {string} apiToken 新 API Token
262
+ * @param {UserCredentialApiOptions} [options] 错误处理策略,可忽略未绑定错误
263
+ * @returns 设置结果;失败返回 `null`
264
+ */
265
+ async setApiToken(apiToken, options = {}) {
266
+ const sessionToken = await this.getSessionToken()
267
+ return makeRequest.setApiToken(
268
+ { ...(await this.platformParams()), token: sessionToken, token_new: apiToken },
269
+ { ...this.endpointOptions(options), errorPrefix: '设置 API Token 失败', notifyUser: true },
270
+ )
271
+ }
272
+
273
+ /**
274
+ * 使用 API Token进行强认证,取得 SSTK 后为当前平台建立 Bot绑定。
275
+ * @param {string} apiToken 待验证的 API Token
276
+ * @param {UserCredentialApiOptions} [options] 错误处理策略,可忽略未绑定错误
277
+ * @returns 认证结果;失败返回 `null`
278
+ */
279
+ async authenticateApiToken(apiToken, options = {}) {
280
+ const data = await makeRequest.getPgrToken(
281
+ { api_token: apiToken },
282
+ {
283
+ ...this.endpointOptions(options),
284
+ errorPrefix: 'API Token 验证失败',
285
+ notifyUser: true,
286
+ },
287
+ )
288
+ if (data?.token) await this.bindPlatform({ token: data.token })
289
+ return data
290
+ }
291
+
292
+ /**
293
+ * 获取当前 API 账号已绑定的平台列表。
294
+ * @param {UserCredentialApiOptions} [options] 错误处理策略,可忽略未绑定错误
295
+ * @returns {Promise<PlatformBindingsResponse | null>} 平台绑定列表;失败返回 `null`
296
+ */
297
+ async listPlatformBindings(options = {}) {
298
+ const sessionToken = await this.getSessionToken()
299
+ return makeRequest.tokenList(
300
+ { ...(await this.platformParams()), token: sessionToken },
301
+ { ...this.endpointOptions(options), errorPrefix: '获取 Token 列表失败', notifyUser: true },
302
+ )
303
+ }
304
+
305
+ /**
306
+ * 永久注销当前 API 账号;调用方应在成功后再清理本地 API 缓存。
307
+ * @param {UserCredentialApiOptions} [options] 错误处理策略,可忽略未绑定错误
308
+ * @returns 注销结果;失败返回 `null`
309
+ */
310
+ async deleteApiAccount(options = {}) {
311
+ const sessionToken = await this.getSessionToken()
312
+ return makeRequest.clear(
313
+ { ...(await this.platformParams()), token: sessionToken },
314
+ { ...this.endpointOptions(options), errorPrefix: '注销 phi-api 账号失败', notifyUser: true },
315
+ )
316
+ }
317
+
318
+ /**
319
+ * 获取当前 API 用户的禁用状态。
320
+ * @param {UserCredentialApiOptions} [options] 错误处理策略,可忽略未绑定错误
321
+ * @returns {Promise<boolean | null>} 禁用状态;请求失败或被忽略时返回 `null`
322
+ */
323
+ async getUserAPIBanStatus(options = {}) {
324
+ const endpointOptions = this.endpointOptions(options)
325
+ try {
326
+ return await makeRequest.getUserBan(
327
+ await this.platformParams(true),
328
+ endpointOptions,
329
+ )
330
+ } catch (error) {
331
+ if (makeRequest.shouldIgnoreError(error, endpointOptions)) return null
332
+ throw error
333
+ }
334
+ }
335
+
336
+ /**
337
+ * 获取当前用户的本地禁用状态。
338
+ * @returns {Promise<boolean | null>} 禁用状态;请求失败或被忽略时返回 `null`
339
+ */
340
+ async getUserLoaclBanStatus() {
341
+ return await userCredentialStore.isSessionTokenBanned(await this.getSessionToken())
342
+ }
343
+
344
+ /**
345
+ * 获取当前用户的谱面标签投票记录。
346
+ * @param {any[]} data 谱面与难度查询列表
347
+ * @param {UserCredentialApiOptions} [options] 错误处理策略,可忽略未绑定错误
348
+ * @returns {Promise<any[] | null>} 用户投票记录;请求失败或被忽略时返回 `null`
349
+ */
350
+ async getChartsUsersVote(data, options = {}) {
351
+ return makeRequest.getChartsUsersVote(
352
+ { ...(await this.platformParams(true)), data },
353
+ this.endpointOptions(options),
354
+ )
355
+ }
356
+
357
+ /**
358
+ * 获取当前用户附近的排行榜数据。
359
+ * @param {UserCredentialApiOptions} [options] 错误处理策略,可忽略未绑定错误
360
+ * @returns 排行榜数据;请求失败或被忽略时返回 `null`
361
+ */
362
+ async getRanklistUser(options = {}) {
363
+ return makeRequest.getRanklistUser(
364
+ await this.platformParams(true),
365
+ this.endpointOptions(options),
366
+ )
367
+ }
368
+
369
+ /**
370
+ * 获取当前用户发布的评论。
371
+ * @param {UserCredentialApiOptions} [options] 错误处理策略,可忽略未绑定错误
372
+ * @returns {Promise<any[] | null>} 评论列表;请求失败或被忽略时返回 `null`
373
+ */
374
+ async getCommentsByUserId(options = {}) {
375
+ return makeRequest.getCommentsByUserId(
376
+ await this.platformParams(true),
377
+ this.endpointOptions(options),
378
+ )
379
+ }
380
+
381
+ /**
382
+ * 获取当前用户在指定谱面的成绩排行。
383
+ * @param {{songId: idString, rank: levelKind, orderBy: 'acc'|'score'|'fc'|'update_at'}} query 查询条件
384
+ * @param {UserCredentialApiOptions} [options] 错误处理策略,可忽略未绑定错误
385
+ * @returns 成绩排行数据;请求失败或被忽略时返回 `null`
386
+ */
387
+ async getScoreRanklistByUser(query, options = {}) {
388
+ return makeRequest.getScoreRanklistByUser(
389
+ { ...(await this.platformParams(true)), ...query },
390
+ this.endpointOptions(options),
391
+ )
392
+ }
393
+
394
+ /**
395
+ * 获取当前用户的 B30 谱面标签分析。
396
+ * @param {UserCredentialApiOptions} [options] 错误处理策略,可忽略未绑定错误
397
+ * @returns 标签分析;请求失败或被忽略时返回 `null`
398
+ */
399
+ async getB30TagAnalysis(options = {}) {
400
+ return makeRequest.getB30TagAnalysis(
401
+ await this.platformParams(true),
402
+ this.endpointOptions(options),
403
+ )
404
+ }
405
+
406
+ /**
407
+ * 将当前用户的本地历史记录上传到 API。
408
+ * @param {import('../save/saveHistory.js').default | saveHistoryObject} history 待上传的历史记录
409
+ * @param {UserCredentialApiOptions} [options] 错误处理策略,可忽略未绑定错误
410
+ * @returns {Promise<{message: string} | null>} 上传结果;失败返回 `null`
411
+ */
412
+ async uploadHistory(history, options = {}) {
413
+ const sessionToken = await this.getSessionToken()
414
+ return makeRequest.setHistory(
415
+ { ...(await this.platformParams()), token: sessionToken, data: /** @type {any} */ (history) },
416
+ this.endpointOptions(options),
417
+ )
418
+ }
419
+
420
+ /**
421
+ * 获取当前用户的 API 隐私与数据设置。
422
+ * @param {UserCredentialApiOptions} [options] 错误处理策略,可忽略未绑定错误
423
+ * @returns {Promise<import('../api/makeRequest.js').userSetting | null>} 用户设置;失败返回 `null`
424
+ */
425
+ async getUserSetting(options = {}) {
426
+ const sessionToken = await this.getSessionToken()
427
+ return makeRequest.getUserSetting(
428
+ { ...(await this.platformParams()), token: sessionToken },
429
+ { ...this.endpointOptions(options), errorPrefix: '获取用户设置失败', notifyUser: true },
430
+ )
431
+ }
432
+
433
+ /**
434
+ * 更新当前用户的 API 隐私与数据设置。
435
+ * @param {import('../api/makeRequest.js').userSetting} setting 需要更新的设置字段
436
+ * @param {UserCredentialApiOptions} [options] 错误处理策略,可忽略未绑定错误
437
+ * @returns {Promise<import('../api/makeRequest.js').userSetting | null>} 更新后的设置;失败返回 `null`
438
+ */
439
+ async setUserSetting(setting, options = {}) {
440
+ const sessionToken = await this.getSessionToken()
441
+ return makeRequest.setUserSetting(
442
+ { ...(await this.platformParams()), token: sessionToken, setting },
443
+ { ...this.endpointOptions(options), errorPrefix: '设置失败', notifyUser: true },
444
+ )
445
+ }
446
+
447
+ /**
448
+ * 提交当前用户对指定谱面的标签投票。
449
+ * @param {idString} songId 曲目 ID
450
+ * @param {levelKind} rank 难度
451
+ * @param {chartsTagString[]} tags 标签列表
452
+ * @param {UserCredentialApiOptions} [options] 错误处理策略,可忽略未绑定错误
453
+ * @returns {Promise<{message: string} | null>} 投票结果;失败返回 `null`
454
+ */
455
+ async setChartsTag(songId, rank, tags, options = {}) {
456
+ const sessionToken = await this.getSessionToken()
457
+ return makeRequest.setChartsTag(
458
+ { ...(await this.platformParams()), token: sessionToken, song_id: songId, rank, content: tags },
459
+ { ...this.endpointOptions(options), errorPrefix: '投票失败QAQ!ERROR', notifyUser: true },
460
+ )
461
+ }
462
+
463
+ /**
464
+ * 添加当前用户的在线谱面评论。
465
+ * @param {import('../api/makeRequest.js').APIUpdateCommentObject} comment 评论内容
466
+ * @param {UserCredentialApiOptions} [options] 错误处理策略,可忽略未绑定错误
467
+ * @returns {Promise<{message: string} | null>} 添加结果;失败返回 `null`
468
+ */
469
+ async addComment(comment, options = {}) {
470
+ const sessionToken = await this.getSessionToken()
471
+ return makeRequest.addComment(
472
+ { ...(await this.platformParams()), token: sessionToken, data: { comment } },
473
+ this.endpointOptions(options),
474
+ )
475
+ }
476
+
477
+ /**
478
+ * 删除当前用户指定 ID 的在线评论。
479
+ * @param {string} commentId 评论 ID
480
+ * @param {UserCredentialApiOptions} [options] 错误处理策略,可忽略未绑定错误
481
+ * @returns {Promise<{message: string} | null>} 删除结果;失败返回 `null`
482
+ */
483
+ async deleteComment(commentId, options = {}) {
484
+ const sessionToken = await this.getSessionToken()
485
+ return makeRequest.delComment(
486
+ { ...(await this.platformParams()), token: sessionToken, comment_id: commentId },
487
+ this.endpointOptions(options),
488
+ )
489
+ }
490
+
491
+ // 以 sessionToken 为键的本地存档仓库操作。
492
+
493
+ /**
494
+ * 读取当前用户按 sessionToken 缓存的本地存档。
495
+ * @returns {Promise<import('../save/Save.js').default | undefined>} 本地存档;不存在时返回 `undefined`
496
+ */
497
+ async getLocalSave() {
498
+ return getSave.getSaveBySessionToken(await this.getSessionToken())
499
+ }
500
+
501
+ /**
502
+ * 保存当前用户的本地存档,并同步用户与 sessionToken 的映射。
503
+ * @param {import('../save/Save.js').default | import('../../lib/PhigrosUser.js').default} data 存档数据
504
+ */
505
+ async putLocalSave(data) {
506
+ await getSave.putSaveBySessionToken(data)
507
+ return this.setSessionToken(data.session)
508
+ }
509
+
510
+ /**
511
+ * 读取当前用户按 sessionToken 缓存的本地历史记录。
512
+ * @returns {Promise<import('../save/saveHistory.js').default>} 本地历史记录
513
+ */
514
+ async getLocalHistory() {
515
+ return getSave.getHistoryBySessionToken(await this.getSessionToken())
516
+ }
517
+
518
+ /**
519
+ * 保存当前用户的本地历史记录。
520
+ * @param {import('../save/saveHistory.js').default | saveHistoryObject | object} data 历史记录数据
521
+ */
522
+ async putLocalHistory(data) {
523
+ const sessionToken = await this.getSessionToken()
524
+ if (!sessionToken) throw new Error('sessionToken is undefined')
525
+ return getSave.putHistoryBySessionToken(sessionToken, data)
526
+ }
527
+
528
+ /**
529
+ * 删除当前用户的本地存档、历史记录及 sessionToken 映射。
530
+ * @returns {Promise<boolean>} 是否找到并删除了本地存档
531
+ */
532
+ async deleteLocalSave() {
533
+ const sessionToken = await this.getSessionToken()
534
+ const deleted = await getSave.deleteSaveBySessionToken(sessionToken)
535
+ await this.deleteSessionToken()
536
+ return deleted
537
+ }
538
+
539
+ // 以 API ID 为键的只读存档缓存操作。
540
+
541
+ /**
542
+ * 读取当前用户按 API ID缓存的 API 存档。
543
+ * @returns {Promise<import('../save/Save.js').default | undefined>} API 存档缓存
544
+ */
545
+ async getApiCachedSave() {
546
+ return getSaveFromApi.getSaveByApiId(await this.getApiId())
547
+ }
548
+
549
+ /**
550
+ * 保存当前用户的 API 存档缓存,并同步本地 API ID。
551
+ * @param {Partial<oriSave | import('../save/Save.js').default>} data 包含 API ID的存档数据
552
+ */
553
+ async putApiCachedSave(data) {
554
+ const apiId = data?.apiId
555
+ if (!apiId) throw new Error('apiId is undefined')
556
+ await getSaveFromApi.putSaveByApiId(apiId, data)
557
+ return this.setApiId(apiId)
558
+ }
559
+
560
+ /**
561
+ * 删除当前用户按 API ID保存的本地缓存,不影响 API 云端账号。
562
+ * @returns {Promise<boolean>} 是否找到并删除了缓存
563
+ */
564
+ async deleteApiCachedSave() {
565
+ const apiId = await this.getApiId()
566
+ const deleted = getSaveFromApi.deleteSaveByApiId(apiId)
567
+ await this.deleteApiId()
568
+ return deleted
569
+ }
570
+
571
+ // API 云端存档与历史读取。
572
+
573
+ /**
574
+ * 从 API 获取当前用户完整云存档。
575
+ * @param {UserCredentialApiOptions} [options] 错误处理策略,可忽略未绑定错误
576
+ * @returns {Promise<Save | null>} 初始化后的云存档;失败返回 `null`
577
+ */
578
+ async getCloudSave(options = {}) {
579
+ const cloudSave = await makeRequest.getCloudSaves(
580
+ await this.platformParams(true),
581
+ this.endpointOptions(options),
582
+ )
583
+ if (!cloudSave) return null
584
+ let result = new Save(cloudSave)
585
+ await result.init()
586
+ return result
587
+ }
588
+
589
+ /**
590
+ * 使用公开 API ID读取其他用户的完整云存档。
591
+ * @param {apiUserId} apiId 公开 API ID
592
+ * @param {UserCredentialApiOptions} [options] 错误处理策略
593
+ * @returns {Promise<Save | null>} 已初始化的云存档
594
+ */
595
+ async getCloudSaveByApiId(apiId, options = {}) {
596
+ const cloudSave = await makeRequest.getCloudSaves(
597
+ { api_user_id: apiId },
598
+ this.endpointOptions(options),
599
+ )
600
+ if (!cloudSave) return null
601
+ const result = new Save(cloudSave)
602
+ await result.init()
603
+ return result
604
+ }
605
+
606
+ /**hu
607
+ * 从 API 获取当前用户指定字段的历史记录。
608
+ * @template {keyof saveHistoryObject} K
609
+ * @param {K[]} [fields] 需要返回的历史字段
610
+ * @param {UserCredentialApiOptions} [options] 错误处理策略,可忽略未绑定错误
611
+ * @returns {Promise<saveHistory | null>} 云端历史记录;失败返回 `null`
612
+ */
613
+ async getCloudHistory(fields = [], options = {}) {
614
+ const result = await makeRequest.getHistory(
615
+ { ...(await this.platformParams(true)), request: fields },
616
+ this.endpointOptions(options),
617
+ )
618
+ if (!result) return null
619
+ return new saveHistory(result)
620
+ }
621
+
622
+ /**
623
+ * 从 API 获取当前用户的成绩历史,支持全部成绩、单曲或单难度三种粒度。
624
+ * @overload
625
+ * @param {idString} songId
626
+ * @param {levelKind} difficulty
627
+ * @returns {Promise<ScoreDetail[]>}
628
+ */
629
+ /**
630
+ * @overload
631
+ * @param {idString} songId
632
+ * @param {undefined} [difficulty]
633
+ * @returns {Promise<songRecordHistory>}
634
+ */
635
+ /**
636
+ * @overload
637
+ * @param {undefined} [songId]
638
+ * @param {undefined} [difficulty]
639
+ * @returns {Promise<scoreHistoryObject>}
640
+ */
641
+ /**
642
+ * 从 API 获取当前用户的成绩历史。
643
+ * @param {idString} [songId] 曲目 ID
644
+ * @param {levelKind} [difficulty] 难度
645
+ * @param {UserCredentialApiOptions} [options] 错误处理策略,可忽略未绑定错误
646
+ * @returns {Promise<ScoreDetail[] | songRecordHistory | scoreHistoryObject | null>} 成绩历史;失败返回 `null`
647
+ */
648
+ async getCloudSongHistory(songId, difficulty = undefined, options = {}) {
649
+ return makeRequest.getHistoryRecord(
650
+ {
651
+ ...(await this.platformParams(true)),
652
+ ...(songId ? { song_id: songId } : {}),
653
+ ...(difficulty ? { rank: difficulty } : {}),
654
+ },
655
+ this.endpointOptions(options),
656
+ )
657
+ }
658
+
659
+ // 云端存档同步与本地增量计算。
660
+
661
+ /**
662
+ * 从 API 拉取当前用户最新存档并更新 API 本地缓存。
663
+ * @param {UserCredentialApiOptions} [options] 错误处理策略,可忽略未绑定错误
664
+ * @returns 更新后的存档及增量统计;失败返回 `null`
665
+ */
666
+ async getUpdatedSaveFromApi(options = {}) {
667
+ const event = this.requireEvent()
668
+ const old = await this.getApiCachedSave()
669
+ const newSaveInfo = await makeRequest.getCloudSaveInfo(
670
+ {
671
+ ...(await this.platformParams(true)),
672
+ },
673
+ this.endpointOptions(options),
674
+ )
675
+ if (!newSaveInfo) return null
676
+ if (new Date(newSaveInfo.modifiedAt.iso).getTime() != old?.saveInfo?.modifiedAt?.iso?.getTime()) {
677
+ const newSave = await makeRequest.getCloudSaves(
678
+ {
679
+ ...(await this.platformParams(true))
680
+ },
681
+ this.endpointOptions(options),
682
+ )
683
+ if (!newSave) return null
684
+ await this.putApiCachedSave(newSave)
685
+ const result = new Save(newSave)
686
+ await result.init()
687
+ await this.putApiCachedSave(result)
688
+ const added_rks_notes = await buildingRecord(old, result, event)
689
+ return { save: result, added_rks_notes }
690
+ } else {
691
+ return { save: old, added_rks_notes: [0, 0] }
692
+ }
693
+ }
694
+
695
+ /**
696
+ * 直接从 Phigros 云存档更新当前用户的本地存档。
697
+ * @param {phigrosToken} [sessionToken] Phigros sessionToken;省略时使用当前本地凭证
698
+ * @param {boolean} [isGlobal] 是否使用国际服
699
+ * @returns {Promise<any>} 更新后的存档及增量统计
700
+ */
701
+ async getUpdatedSaveFromLocal(sessionToken = undefined, isGlobal = undefined) {
702
+ const event = this.requireEvent()
703
+ const old = await this.getLocalSave()
704
+ sessionToken = sessionToken ?? old?.session
705
+ if (!sessionToken) throw new Error('缺少 sessionToken 参数')
706
+
707
+ const user = new PhigrosUser(sessionToken, isGlobal ?? old?.global)
708
+ try {
709
+ await user.getSaveInfo()
710
+ await user.buildRecord()
711
+ if (getInfo.badSave && new Save(user).equalRecord(getInfo.badSave)) {
712
+ notifyUser(event, '请注意,TapTap目前的云存档API疑似存在问题,已阻止本次更新,请在确保游戏内数据正常的情况下,覆盖云存档并尝试更新,如若依旧失败请耐心等待恢复。')
713
+ return old ? { save: old, added_rks_notes: [0, 0] } : undefined
714
+ }
715
+ } catch (error) {
716
+ const message = platform.getAdapterName(event) === 'QQBot'
717
+ ? '更新失败!QAQ\n请稍后重试'
718
+ : `更新失败!QAQ\n${error}`
719
+ notifyUser(event, message)
720
+ logger.error(error)
721
+ return undefined
722
+ }
723
+
724
+ try {
725
+ await this.putLocalSave(user)
726
+ } catch (error) {
727
+ notifyUser(event, `保存存档失败!${error}`)
728
+ logger.error(error)
729
+ return undefined
730
+ }
731
+
732
+ if (old?.session && old.session !== user.session) {
733
+ notifyUser(event, `检测到新的sessionToken,将自动更换本地绑定。如果需要删除当前 Bot 本地保存的存档和历史,请使用 ⌈/${Config.getUserCfg('config', 'cmdhead')} unbind⌋。`)
734
+ }
735
+
736
+ const now = new Save(user)
737
+ const history = await this.getLocalHistory()
738
+ history.update(now)
739
+ await this.putLocalHistory(history)
740
+ const added_rks_notes = await buildingRecord(old, now, event)
741
+ return { save: now, added_rks_notes }
742
+ }
743
+
744
+ /**
745
+ * 使用新的 sessionToken 完成本地绑定。
746
+ * 只有成功读取并保存 Phigros 云存档后才替换本地凭证,API 绑定失败时可安全调用。
747
+ * @param {phigrosToken} sessionToken 新的 Phigros sessionToken
748
+ * @param {boolean} [isGlobal] 是否使用国际服
749
+ * @returns 本地更新结果;token 校验或存档更新失败时返回 `null`
750
+ */
751
+ async bindLocallyWithSessionToken(sessionToken, isGlobal = undefined) {
752
+ const updateData = await this.getUpdatedSaveFromLocal(sessionToken, isGlobal)
753
+ if (!updateData) return null
754
+ await this.useSessionToken(sessionToken)
755
+ return updateData
756
+ }
757
+
758
+ /**
759
+ * 获取当前用户历史记录:有 SSTK 时先同步本地历史,否则直接读取 API。
760
+ * @template {keyof saveHistoryObject} K
761
+ * @param {K[]} [fields] 需要返回的历史字段
762
+ * @param {UserCredentialApiOptions} [options] 错误处理策略,可忽略未绑定错误
763
+ * @returns {Promise<import('../save/saveHistory.js').default | null>} 历史记录;无法获取时返回 `null`
764
+ */
765
+ async getHistoryFromApi(fields = [], options = {}) {
766
+ const event = this.requireEvent()
767
+ const sessionToken = await this.getSessionToken()
768
+ if (!sessionToken) {
769
+ if (!await canUseApi(event)) {
770
+ notifyUser(event, '请先绑定sessionToken哦!')
771
+ return null
772
+ }
773
+ const remoteHistory = await this.getCloudHistory(fields, options)
774
+ if (!remoteHistory && !options.ignoreUnboundError) {
775
+ notifyUser(event, '从API获取历史记录失败,请稍后重试或绑定sessionToken后重试哦')
776
+ }
777
+ return remoteHistory
778
+ }
779
+
780
+ const localHistory = await this.getLocalHistory()
781
+ if (localHistory) await this.uploadHistory(localHistory)
782
+ const remoteHistory = await this.getCloudHistory(fields, options)
783
+ if (remoteHistory) return remoteHistory
784
+ if (!options.ignoreUnboundError) {
785
+ logger.warn('[phi-plugin]获取历史记录失败,将使用本地历史记录')
786
+ notifyUser(event, '从API获取历史记录失败,将使用本地存档的历史记录哦')
787
+ }
788
+ return localHistory
789
+ }
790
+ }
791
+
792
+ /**
793
+ * 更新存档
794
+ * @param {botEvent} e
795
+ * @param {Save | oriSave | undefined} old
796
+ * @param {Save | oriSave} now
797
+ * @returns {Promise<[number,number]>} [rks变化值,note变化值],失败返回 false
798
+ */
799
+ async function buildingRecord(old = undefined, now, e) {
800
+
801
+
802
+ let notesData = await getNotes.getNotesData(e.user_id)
803
+ /**删除旧字段 */
804
+ // @ts-ignore
805
+ if (notesData.update || notesData.task_update) {
806
+ // @ts-ignore
807
+ delete notesData.update
808
+ // @ts-ignore
809
+ delete notesData.task_update
810
+ }
811
+
812
+ /**note数量变化 */
813
+ let add_money = 0
814
+
815
+ let task = notesData?.task
816
+ if (task) {
817
+ const idList = fCompute.objectKeys(now.gameRecord)
818
+ for (let id of idList) {
819
+ for (let i in task) {
820
+ if (!task[i]) continue
821
+ if (!task[i].finished && id == task[i].song) {
822
+ let level = LevelNum[task[i].request.rank]
823
+ if (!now.gameRecord[id][level]) continue
824
+ switch (task[i].request.type) {
825
+ case 'acc': {
826
+ if (now.gameRecord[id][level].acc >= task[i].request.value) {
827
+ notesData.task[i].finished = true
828
+ notesData.money += task[i].reward
829
+ add_money += task[i].reward
830
+ }
831
+ break
832
+ }
833
+ case 'score': {
834
+ if (now.gameRecord[id][level].score >= task[i].request.value) {
835
+ notesData.task[i].finished = true
836
+ notesData.money += task[i].reward
837
+ add_money += task[i].reward
838
+ }
839
+ break
840
+ }
841
+ }
842
+ }
843
+ }
844
+ }
845
+ }
846
+ getNotes.putNotesData(e.user_id, notesData)
847
+
848
+ /**rks变化 */
849
+ let add_rks = old ? now.saveInfo.summary.rankingScore - old.saveInfo.summary.rankingScore : 0
850
+ return [add_rks, add_money]
851
+ }
852
+
853
+ /**
854
+ * 通过平台适配器发送 UserCredentials 内部流程提示,避免反向依赖 send 模块。
855
+ * @param {botEvent} event 当前平台事件
856
+ * @param {string} message 提示内容
857
+ */
858
+ function notifyUser(event, message) {
859
+ return platform.sendWithAt(platform.wrapEvent(event), message, false, {})
860
+ }