koishi-plugin-noah 1.8.2 → 2.0.1
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.
- package/lib/core/services/server-service.d.ts +8 -0
- package/lib/drawer/sdvx/index.d.ts +5 -0
- package/lib/games/sdvx/commands/sync.d.ts +3 -0
- package/lib/games/sdvx/database.d.ts +12 -0
- package/lib/games/sdvx/services/music-service.d.ts +6 -0
- package/lib/games/sdvx/utils/calculator.d.ts +1 -1
- package/lib/games/sdvx/utils/param-parser.d.ts +4 -0
- package/lib/games/sdvx/utils/vf.d.ts +1 -0
- package/lib/index.cjs +526 -48
- package/lib/servers/Mao/services/sdvx-service.d.ts +18 -1
- package/lib/types/index.d.ts +25 -0
- package/package.json +2 -2
|
@@ -22,6 +22,14 @@ export declare class ServerService {
|
|
|
22
22
|
* @returns 该频道绑定的服务器完整信息列表
|
|
23
23
|
*/
|
|
24
24
|
getServersByCid(cid: string): Promise<Server[]>;
|
|
25
|
+
/**
|
|
26
|
+
* 获取可供选择的服务器列表(合并用户与频道)
|
|
27
|
+
* 对于 mao 与 official 类型,由于其 URL 固定,仅保留其中一个实例
|
|
28
|
+
* @param uid - 用户ID
|
|
29
|
+
* @param cid - 频道ID,可为空
|
|
30
|
+
* @returns 去重后的服务器列表
|
|
31
|
+
*/
|
|
32
|
+
getSelectableServers(uid: number, cid?: string | null): Promise<Server[]>;
|
|
25
33
|
/**
|
|
26
34
|
* 根据服务器ID获取服务器信息
|
|
27
35
|
* @param id - 服务器ID
|
|
@@ -8,6 +8,11 @@ import { BaseDrawer } from '../BaseDrawer';
|
|
|
8
8
|
export declare class SDVXDrawer extends BaseDrawer {
|
|
9
9
|
ctx: Context;
|
|
10
10
|
constructor(ctx: Context);
|
|
11
|
+
/**
|
|
12
|
+
* Format difficulty level text.
|
|
13
|
+
* If it's an integer like 17 / 20, keep trailing ".0" (e.g. "17.0").
|
|
14
|
+
*/
|
|
15
|
+
private formatDifficultyLevel;
|
|
11
16
|
/**
|
|
12
17
|
* 生成SDVX最近成绩图像
|
|
13
18
|
* @param options - 图像生成配置选项
|
|
@@ -1,3 +1,15 @@
|
|
|
1
1
|
import { Context } from 'koishi';
|
|
2
|
+
declare module 'koishi' {
|
|
3
|
+
interface Tables {
|
|
4
|
+
sdvx_pin_verified: SdvxPinVerified;
|
|
5
|
+
}
|
|
6
|
+
}
|
|
7
|
+
export interface SdvxPinVerified {
|
|
8
|
+
id: number;
|
|
9
|
+
uid: number;
|
|
10
|
+
cardId: number;
|
|
11
|
+
sid: number;
|
|
12
|
+
pin: string;
|
|
13
|
+
}
|
|
2
14
|
export declare const name = "database";
|
|
3
15
|
export declare function apply(ctx: Context): void;
|
|
@@ -6,6 +6,8 @@ export declare class MusicService {
|
|
|
6
6
|
private sdvx_data_url;
|
|
7
7
|
private sdvx_search_url;
|
|
8
8
|
private constructor();
|
|
9
|
+
private parseDifnum;
|
|
10
|
+
private normalizeMusicDifnum;
|
|
9
11
|
/**
|
|
10
12
|
* 获取 MusicService 实例
|
|
11
13
|
* @param config - SDVX 配置对象
|
|
@@ -32,4 +34,8 @@ export declare class MusicService {
|
|
|
32
34
|
* @returns 音乐信息数组
|
|
33
35
|
*/
|
|
34
36
|
searchMusic(ctx: Context, query: string): Promise<SDVXMusic[]>;
|
|
37
|
+
extTitleToMid(ctx: Context, titles: string[]): Promise<{
|
|
38
|
+
title: string;
|
|
39
|
+
mid: string | null;
|
|
40
|
+
}[]>;
|
|
35
41
|
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { SDVXGrade, SDVXClearType } from '../types';
|
|
2
2
|
/**
|
|
3
3
|
* VF 查找表类型
|
|
4
|
-
* Key: `${
|
|
4
|
+
* Key: `${levelScaled}-${grade}-${clearType}-${vfInt}`
|
|
5
5
|
* Value: QueryResult(每个组合只保留最小分数)
|
|
6
6
|
*/
|
|
7
7
|
type VFLookupTable = Map<string, QueryResult>;
|
|
@@ -15,6 +15,10 @@ export interface QueryParams {
|
|
|
15
15
|
export declare function parseNumberWithSuffix(str: string): number;
|
|
16
16
|
/**
|
|
17
17
|
* 解析等级范围
|
|
18
|
+
* 规则:
|
|
19
|
+
* - 1-16: 只能是整数(用户输入整数时看作 xx.0)
|
|
20
|
+
* - 17: 可以是 17.0 或 17.5
|
|
21
|
+
* - 18-20.9: 0.1 步进(18.0, 18.1, ..., 20.9)
|
|
18
22
|
*/
|
|
19
23
|
export declare function parseLevelRange(item: string): number[] | null;
|
|
20
24
|
/**
|
package/lib/index.cjs
CHANGED
|
@@ -221,7 +221,6 @@ var AssetService = class {
|
|
|
221
221
|
*/
|
|
222
222
|
async extractAssets(archivePath) {
|
|
223
223
|
const extractPath = (0, import_path.resolve)(this.basePath, "assets");
|
|
224
|
-
await (0, import_promises.mkdir)(extractPath, { recursive: true });
|
|
225
224
|
this.logger.info(`Extracting assets to ${extractPath}`);
|
|
226
225
|
try {
|
|
227
226
|
if (!import_fs.default.existsSync(archivePath)) {
|
|
@@ -232,6 +231,11 @@ var AssetService = class {
|
|
|
232
231
|
} catch (err) {
|
|
233
232
|
throw new Error(`Archive file is not readable: ${archivePath}`);
|
|
234
233
|
}
|
|
234
|
+
if (import_fs.default.existsSync(extractPath)) {
|
|
235
|
+
this.logger.info(`Removing existing assets: ${extractPath}`);
|
|
236
|
+
import_fs.default.rmSync(extractPath, { recursive: true, force: true });
|
|
237
|
+
}
|
|
238
|
+
await (0, import_promises.mkdir)(extractPath, { recursive: true });
|
|
235
239
|
const zip = new import_adm_zip.default(archivePath);
|
|
236
240
|
const zipEntries = zip.getEntries();
|
|
237
241
|
this.logger.info(`Found ${zipEntries.length} files in archive`);
|
|
@@ -523,6 +527,32 @@ var ServerService = class {
|
|
|
523
527
|
rows.map((obj) => obj.sid)
|
|
524
528
|
);
|
|
525
529
|
}
|
|
530
|
+
/**
|
|
531
|
+
* 获取可供选择的服务器列表(合并用户与频道)
|
|
532
|
+
* 对于 mao 与 official 类型,由于其 URL 固定,仅保留其中一个实例
|
|
533
|
+
* @param uid - 用户ID
|
|
534
|
+
* @param cid - 频道ID,可为空
|
|
535
|
+
* @returns 去重后的服务器列表
|
|
536
|
+
*/
|
|
537
|
+
async getSelectableServers(uid, cid) {
|
|
538
|
+
const channelServers = cid ? await this.getServersByCid(cid) : [];
|
|
539
|
+
const userServers = await this.getServersByUid(uid);
|
|
540
|
+
const merged = [];
|
|
541
|
+
const seenIds = /* @__PURE__ */ new Set();
|
|
542
|
+
const seenFixedTypes = /* @__PURE__ */ new Set();
|
|
543
|
+
for (const server2 of [...channelServers, ...userServers]) {
|
|
544
|
+
if (seenIds.has(server2.id)) continue;
|
|
545
|
+
if ((server2.type === "mao" || server2.type === "official") && seenFixedTypes.has(server2.type)) {
|
|
546
|
+
continue;
|
|
547
|
+
}
|
|
548
|
+
if (server2.type === "mao" || server2.type === "official") {
|
|
549
|
+
seenFixedTypes.add(server2.type);
|
|
550
|
+
}
|
|
551
|
+
seenIds.add(server2.id);
|
|
552
|
+
merged.push(server2);
|
|
553
|
+
}
|
|
554
|
+
return merged;
|
|
555
|
+
}
|
|
526
556
|
/**
|
|
527
557
|
* 根据服务器ID获取服务器信息
|
|
528
558
|
* @param id - 服务器ID
|
|
@@ -1002,8 +1032,12 @@ function bind(ctx, config) {
|
|
|
1002
1032
|
case "invalid":
|
|
1003
1033
|
return session.text(".invalid-code");
|
|
1004
1034
|
case "access":
|
|
1005
|
-
|
|
1006
|
-
|
|
1035
|
+
try {
|
|
1036
|
+
cardCode = await accessToUid(ctx, cardCode);
|
|
1037
|
+
break;
|
|
1038
|
+
} catch {
|
|
1039
|
+
return session.text(".convert-access-failed");
|
|
1040
|
+
}
|
|
1007
1041
|
case "konamiid":
|
|
1008
1042
|
cardCode = konamiIdToUid(cardCode);
|
|
1009
1043
|
break;
|
|
@@ -1234,7 +1268,8 @@ async function showCardMenu(ctx, session, card2, cardService, serverService, use
|
|
|
1234
1268
|
case "official":
|
|
1235
1269
|
break;
|
|
1236
1270
|
}
|
|
1237
|
-
if (await cardService.getCardByCode(cardCode) != null)
|
|
1271
|
+
if (await cardService.getCardByCode(cardCode) != null)
|
|
1272
|
+
return session.text(".menu-1-error-duplicate");
|
|
1238
1273
|
let defaultServerId = card2.defaultServerId;
|
|
1239
1274
|
if (cardType === "official") {
|
|
1240
1275
|
defaultServerId = await ensureOfficialServerForUser(
|
|
@@ -1273,9 +1308,7 @@ async function showCardMenu(ctx, session, card2, cardService, serverService, use
|
|
|
1273
1308
|
return session.text(".menu-4-success");
|
|
1274
1309
|
}
|
|
1275
1310
|
if (selectNum === 5) {
|
|
1276
|
-
const
|
|
1277
|
-
const channelRes = await serverService.getServersByCid(cid);
|
|
1278
|
-
const res = channelRes.concat(userRes);
|
|
1311
|
+
const res = await serverService.getSelectableServers(uid, cid);
|
|
1279
1312
|
let serverListMsg = "";
|
|
1280
1313
|
for (let i = 0; i < res.length; i++) {
|
|
1281
1314
|
if (res[i].id === userDefaultServerId) {
|
|
@@ -2917,10 +2950,10 @@ function apply6(ctx, config) {
|
|
|
2917
2950
|
__name(apply6, "apply");
|
|
2918
2951
|
|
|
2919
2952
|
// src/core/locales/en-US.yml
|
|
2920
|
-
var en_US_default3 = { _config: { $desc: "Core Module Settings", adminUsers: "**Plugin Admin** User ID (use inspect command to get)", guildNameCards: "**Group Card** Name List", maoServerUrl: "**Mao Server** URL address", official_support_url: "**Official Support** URL address" }, commands: { maintain: { description: "Switch to maintenance mode", messages: { "no-auth": "<p>You don't have permission to use this feature~</p>", start: "<p>Entering maintenance mode</p>", "success-start": "<p>Successfully switched to maintenance mode</p>", stop: "<p>Exiting maintenance mode</p>", "success-stop": "<p>Successfully exited maintenance mode</p>" } }, timeout: "Noah didn't wait for your reply, please try again!", noah: { help: { description: "Show Noah help information", messages: { content: "<p>Guide:</p>\nhttps://docs.logthm.cn/noah" } } }, locale: { description: "Set language", messages: { "no-auth": "<p>Only group admins can use this feature~</p>", "invalid-select": "<p>No such option!</p>", quit: "<p>Quit!</p>", "reset-channel": "<p>Reset successfully!</p>", "reset-user": "<p>Reset successfully!</p>", success: "<p>Set successfully!</p>", "set-channel": "<p>Select the default language for group chats:</p>\n<p>1. 简体中文</p>\n<p>2. English</p>\n<p>q. Quit</p>", "set-user": "<p>Select the language you use:</p>\n<p>1. 中文</p>\n<p>2. English</p>\n<p>q. Quit</p>" } }, bind: { description: "Bind card", messages: { prompt: "<p>Please enter your card number:</p>", "invalid-code": "<p>The card number is incorrect, if you don't remember it, go to the arcade to check it~</p>", name: "<p>Received! Please give this card a name, no spaces allowed:</p>", invalid_name: "<p>No spaces allowed! Please try again o(一︿一+)o</p>", success: "<p>Bound successfully, your card information is as follows:</p>\n<p>[{cardName}]</p>\n<p>{cardCode}</p>" } }, card: { description: "Manage cards", options: { detail: "Show detailed card information" }, messages: { "invalid-code": "<p>The card number is incorrect, if you don't remember it, go to the arcade to check it~</p>", "invalid-select": "<p>No such option!</p>", "lookup-error": "Lookup failed: {message}", "lookup-error-unknown": "Lookup failed: unknown error", "menu-select": "<p>[Card Management]</p>\n{card_list}\n<p>0. Add new card</p>\n<p>Please enter the serial number:</p>", menu: "<p>[{name}]</p>\n<p>1. Set as default card</p>\n<p>2. View or modify card number</p>\n<p>3. Delete the card</p>\n<p>4. Rename the card</p>\n<p>5. Bind the card to a specified server</p>\n<p>0. Return to card selection</p>\n<p>Please enter the serial number:</p>", "menu-has-bound-server": "<p>[{name}]</p>\n<p>Bound server: {defaultServerName}</p>\n<p>1. Set as default card</p>\n<p>2. View or modify card number</p>\n<p>3. Delete the card</p>\n<p>4. Rename the card</p>\n<p>5. Bind the card to a specified server</p>\n<p>0. Return to card selection</p>\n<p>Please enter the serial number:</p>", "menu-1-success": "<p>Set successfully!</p>", "menu-1-error-duplicate": "<p>The selected card is the same as the old default card!</p>", "menu-2-prompt": "<p>Card number: {code}</p>\n<p>Please enter the new card number:</p>\n<p>Enter 0 to return</p>", "menu-2-success": "<p>Modified successfully!</p>", "menu-2-error-invalid-code": "<p>The card number is incorrect, if you don't remember it, go to the arcade to check it~</p>", "menu-3-success": "<p>Deleted successfully!</p>", "menu-4-prompt": "<p>Enter a new name, no spaces allowed:</p>", "menu-4-error-invalid-name": "<p>No spaces allowed! Please try again o(一︿一+)o</p>", "menu-4-success": "<p>Modified successfully!</p>", "menu-5": "{server_list}\n<p>Please enter the serial number:</p>", "menu-5-success": "<p>Set successfully!</p>", "menu-5-error-duplicate": "<p>The selected server is the same as the old default server!</p>" } }, server: { description: "Manage servers", messages: { "no-channel": "<p>Please use this feature in group chats~</p>", "invalid-select": "<p>No such option!</p>", "no-auth": "<p>Only group admins can use this feature~</p>", "admin-menu-select": "<p>[Group server management]</p>\n{server_list}\n<p>0. Add new server</p>\n<p>Please enter the serial number:</p>", "menu-select": "<p>[Server management]</p>\n{server_list}\n<p>0. Add new server</p>\n<p>Please enter the serial number:</p>", menu: "<p>[{name}]</p>\n<p>Type: {type}</p>\n<p>1. Set as default server</p>\n<p>2. View or modify url</p>\n<p>3. Delete the server</p>\n<p>4. Rename the server</p>\n<p>0. Return to server selection</p>\n<p>Please enter the serial number:</p>", "menu-1-success": "<p>Set successfully!</p>", "menu-1-error-duplicate": "<p>The selected server is the same as the old default server!</p>", "menu-2-prompt": "<p>The server's url: {baseUrl}</p>\n<p>Please enter the new server url:</p>\n<p>Enter 0 to return</p>", "menu-2-success": "<p>Modified successfully!</p>", "menu-2-invalid-url": "<p>Invalid URL format! Please enter a valid url address.</p>", "menu-2-too-many-attempts": "<p>Too many attempts. Operation cancelled.</p>", "menu-3-success": "<p>Deleted successfully!</p>", "menu-4-prompt": "<p>Enter a new name, no spaces allowed:</p>", "menu-4-error-invalid-name": "<p>No spaces allowed! Please try again o(一︿一+)o</p>", "menu-4-success": "<p>Modified successfully!</p>", "add-type": "<p>Please select the server type:</p>\n{server_type_list}", "add-url": "<p>Please enter the server url:</p>", "add-invalid-url": "<p>Invalid URL format! Please enter a valid url address.</p>", "add-too-many-attempts": "<p>Too many attempts. Operation cancelled.</p>", "add-name": "<p>Received! Please give the server a name, no spaces allowed:</p>", "add-invalid_name": "<p>No spaces allowed! Please try again o(一︿一+)o</p>", "add-success": "<p>Added successfully, the server information is as follows:</p>\n<p>[{serverName}]</p>\n<p>Type: {serverType}</p>" } } } };
|
|
2953
|
+
var en_US_default3 = { _config: { $desc: "Core Module Settings", adminUsers: "**Plugin Admin** User ID (use inspect command to get)", guildNameCards: "**Group Card** Name List", maoServerUrl: "**Mao Server** URL address", official_support_url: "**Official Support** URL address" }, commands: { maintain: { description: "Switch to maintenance mode", messages: { "no-auth": "<p>You don't have permission to use this feature~</p>", start: "<p>Entering maintenance mode</p>", "success-start": "<p>Successfully switched to maintenance mode</p>", stop: "<p>Exiting maintenance mode</p>", "success-stop": "<p>Successfully exited maintenance mode</p>" } }, timeout: "Noah didn't wait for your reply, please try again!", noah: { help: { description: "Show Noah help information", messages: { content: "<p>Guide:</p>\nhttps://docs.logthm.cn/noah" } } }, locale: { description: "Set language", messages: { "no-auth": "<p>Only group admins can use this feature~</p>", "invalid-select": "<p>No such option!</p>", quit: "<p>Quit!</p>", "reset-channel": "<p>Reset successfully!</p>", "reset-user": "<p>Reset successfully!</p>", success: "<p>Set successfully!</p>", "set-channel": "<p>Select the default language for group chats:</p>\n<p>1. 简体中文</p>\n<p>2. English</p>\n<p>q. Quit</p>", "set-user": "<p>Select the language you use:</p>\n<p>1. 中文</p>\n<p>2. English</p>\n<p>q. Quit</p>" } }, bind: { description: "Bind card", messages: { prompt: "<p>Please enter your card number:</p>", "convert-access-failed": "<p>Failed to convert Access Code, please use 16-digit card code instead.</p>", "invalid-code": "<p>The card number is incorrect, if you don't remember it, go to the arcade to check it~</p>", name: "<p>Received! Please give this card a name, no spaces allowed:</p>", invalid_name: "<p>No spaces allowed! Please try again o(一︿一+)o</p>", success: "<p>Bound successfully, your card information is as follows:</p>\n<p>[{cardName}]</p>\n<p>{cardCode}</p>" } }, card: { description: "Manage cards", options: { detail: "Show detailed card information" }, messages: { "invalid-code": "<p>The card number is incorrect, if you don't remember it, go to the arcade to check it~</p>", "invalid-select": "<p>No such option!</p>", "lookup-error": "Lookup failed: {message}", "lookup-error-unknown": "Lookup failed: unknown error", "menu-select": "<p>[Card Management]</p>\n{card_list}\n<p>0. Add new card</p>\n<p>Please enter the serial number:</p>", menu: "<p>[{name}]</p>\n<p>1. Set as default card</p>\n<p>2. View or modify card number</p>\n<p>3. Delete the card</p>\n<p>4. Rename the card</p>\n<p>5. Bind the card to a specified server</p>\n<p>0. Return to card selection</p>\n<p>Please enter the serial number:</p>", "menu-has-bound-server": "<p>[{name}]</p>\n<p>Bound server: {defaultServerName}</p>\n<p>1. Set as default card</p>\n<p>2. View or modify card number</p>\n<p>3. Delete the card</p>\n<p>4. Rename the card</p>\n<p>5. Bind the card to a specified server</p>\n<p>0. Return to card selection</p>\n<p>Please enter the serial number:</p>", "menu-1-success": "<p>Set successfully!</p>", "menu-1-error-duplicate": "<p>The selected card is the same as the old default card!</p>", "menu-2-prompt": "<p>Card number: {code}</p>\n<p>Please enter the new card number:</p>\n<p>Enter 0 to return</p>", "menu-2-success": "<p>Modified successfully!</p>", "menu-2-error-invalid-code": "<p>The card number is incorrect, if you don't remember it, go to the arcade to check it~</p>", "menu-3-success": "<p>Deleted successfully!</p>", "menu-4-prompt": "<p>Enter a new name, no spaces allowed:</p>", "menu-4-error-invalid-name": "<p>No spaces allowed! Please try again o(一︿一+)o</p>", "menu-4-success": "<p>Modified successfully!</p>", "menu-5": "{server_list}\n<p>Please enter the serial number:</p>", "menu-5-success": "<p>Set successfully!</p>", "menu-5-error-duplicate": "<p>The selected server is the same as the old default server!</p>" } }, server: { description: "Manage servers", messages: { "no-channel": "<p>Please use this feature in group chats~</p>", "invalid-select": "<p>No such option!</p>", "no-auth": "<p>Only group admins can use this feature~</p>", "admin-menu-select": "<p>[Group server management]</p>\n{server_list}\n<p>0. Add new server</p>\n<p>Please enter the serial number:</p>", "menu-select": "<p>[Server management]</p>\n{server_list}\n<p>0. Add new server</p>\n<p>Please enter the serial number:</p>", menu: "<p>[{name}]</p>\n<p>Type: {type}</p>\n<p>1. Set as default server</p>\n<p>2. View or modify url</p>\n<p>3. Delete the server</p>\n<p>4. Rename the server</p>\n<p>0. Return to server selection</p>\n<p>Please enter the serial number:</p>", "menu-1-success": "<p>Set successfully!</p>", "menu-1-error-duplicate": "<p>The selected server is the same as the old default server!</p>", "menu-2-prompt": "<p>The server's url: {baseUrl}</p>\n<p>Please enter the new server url:</p>\n<p>Enter 0 to return</p>", "menu-2-success": "<p>Modified successfully!</p>", "menu-2-invalid-url": "<p>Invalid URL format! Please enter a valid url address.</p>", "menu-2-too-many-attempts": "<p>Too many attempts. Operation cancelled.</p>", "menu-3-success": "<p>Deleted successfully!</p>", "menu-4-prompt": "<p>Enter a new name, no spaces allowed:</p>", "menu-4-error-invalid-name": "<p>No spaces allowed! Please try again o(一︿一+)o</p>", "menu-4-success": "<p>Modified successfully!</p>", "add-type": "<p>Please select the server type:</p>\n{server_type_list}", "add-url": "<p>Please enter the server url:</p>", "add-invalid-url": "<p>Invalid URL format! Please enter a valid url address.</p>", "add-too-many-attempts": "<p>Too many attempts. Operation cancelled.</p>", "add-name": "<p>Received! Please give the server a name, no spaces allowed:</p>", "add-invalid_name": "<p>No spaces allowed! Please try again o(一︿一+)o</p>", "add-success": "<p>Added successfully, the server information is as follows:</p>\n<p>[{serverName}]</p>\n<p>Type: {serverType}</p>" } } } };
|
|
2921
2954
|
|
|
2922
2955
|
// src/core/locales/zh-CN.yml
|
|
2923
|
-
var zh_CN_default3 = { _config: { $desc: "Core 模块设置", adminUsers: "**插件管理员** 的用户id (使用 inspect 指令获取)", guildNameCards: "**群聊卡片** 的名称列表", maoServerUrl: "**猫网服务器** 的 URL 地址", official_support_url: "**官方支持** 的 URL 地址" }, commands: { maintain: { description: "切换到维护模式", messages: { "no-auth": "<p>你没有权限使用本功能哦~</p>", start: "<p>正在进入维护模式</p>", "success-start": "<p>成功切换到维护模式</p>", stop: "<p>正在退出维护模式</p>", "success-stop": "<p>成功退出维护模式</p>" } }, timeout: "Noah 没等到你的回复,请重试!", noah: { help: { description: "显示 Noah 帮助信息", messages: { content: "<p>使用文档:</p>\nhttps://docs.logthm.cn/noah" } } }, locale: { description: "设置语言", messages: { "no-auth": "<p>只有群管理员能使用本功能哦~</p>", "invalid-select": "<p>没有该选项!</p>", quit: "<p>已退出~</p>", "reset-channel": "<p>重置成功!</p>", "reset-user": "<p>重置成功!</p>", success: "<p>设置成功!</p>", "set-channel": "<p>选择群聊默认使用的语言:</p>\n<p>1. 简体中文</p>\n<p>2. English</p>\n<p>q. 退出</p>", "set-user": "<p>选择你使用的语言:</p>\n<p>1. 简体中文</p>\n<p>2. English</p>\n<p>q. 退出</p>" } }, bind: { description: "绑定卡片", messages: { prompt: "<p>请输入你的卡号:</p>", "invalid-code": "<p>卡号不对哟,不记得的话去机台刷一下吧~</p>", name: "<p>收到!为这张卡取一个名字吧,不要带空格哦:</p>", invalid_name: "<p>名字不要带空格!重来重来 o(一︿一+)o</p>", success: "<p>绑好啦,你的卡片信息如下:</p>\n<p>[{cardName}]</p>\n<p>{cardCode}</p>" } }, card: { description: "管理卡片", options: { detail: "显示卡片详细信息" }, messages: { "invalid-code": "<p>卡号不对哟,不记得的话去机台刷一下吧~</p>", "invalid-select": "<p>没有该选项!</p>", quit: "<p>已退出~</p>", "lookup-error": "查询失败: {message}", "lookup-error-unknown": "查询失败: 未知错误", "menu-select": "<p>[卡片管理]</p>\n{card_list}\n<p>0. 添加新卡片</p>\n<p>q. 退出菜单</p>\n<p>请输入序号:</p>", menu: "<p>[{name}]</p>\n<p>1. 设为默认卡片</p>\n<p>2. 查看或修改卡号</p>\n<p>3. 删除该卡</p>\n<p>4. 重命名该卡片</p>\n<p>5. 将卡片与指定服务器绑定</p>\n<p>0. 返回卡片选择</p>\n<p>q. 退出菜单</p>\n<p>请输入序号:</p>", "menu-has-bound-server": "<p>[{name}]</p>\n<p>已绑定服务器:{defaultServerName}</p>\n<p>1. 设为默认卡片</p>\n<p>2. 查看或修改卡号</p>\n<p>3. 删除该卡</p>\n<p>4. 重命名该卡片</p>\n<p>5. 将卡片与指定服务器绑定</p>\n<p>0. 返回卡片选择</p>\n<p>q. 退出菜单</p>\n<p>请输入序号:</p>", "menu-1-success": "<p>设置成功!</p>", "menu-1-error-duplicate": "<p>选择的卡片与旧的默认卡片相同!</p>", "menu-2-prompt": "<p>卡号:{code}</p>\n<p>请输入新的卡号:</p>\n<p>0. 返回</p>\n<p>q. 退出</p>", "menu-2-success": "<p>修改成功!</p>", "menu-2-error-invalid-code": "<p>卡号不对哟,不记得的话去机台刷一下吧~</p>", "menu-3-success": "<p>已删除!</p>", "menu-4-prompt": "<p>输入一个新名字,不要带空格哦:</p>\n<p>q. 退出</p>", "menu-4-error-invalid-name": "<p>名字不要带空格!重来重来 o(一︿一+)o</p>", "menu-4-success": "<p>修改成功!</p>", "menu-5": "<p>请选择一个服务器:</p>\n{server_list}\n<p>q. 退出</p>", "menu-5-success": "<p>设置成功!</p>", "menu-5-error-duplicate": "<p>选择的服务器与旧的默认服务器相同!</p>" } }, server: { description: "管理服务器", messages: { "no-channel": "<p>请在群聊中使用本功能哦~</p>", "invalid-select": "<p>没有该选项!</p>", "no-auth": "<p>只有群管理员能使用本功能哦~</p>", quit: "<p>已退出~</p>", "admin-menu-select": "<p>[群聊服务器管理]</p>\n{server_list}\n<p>0. 添加新服务器</p>\n<p>q. 退出菜单</p>\n<p>请输入序号:</p>", "menu-select": "<p>[服务器管理]</p>\n{server_list}\n<p>0. 添加新服务器</p>\n<p>q. 退出菜单</p>\n<p>请输入序号:</p>", menu: "<p>[{name}]</p>\n<p>类型:{type}</p>\n<p>1. 设为默认服务器</p>\n<p>2. 查看或修改 url</p>\n<p>3. 删除该服务器</p>\n<p>4. 重命名该服务器</p>\n<p>0. 返回服务器选择</p>\n<p>q. 退出菜单</p>\n<p>请输入序号:</p>", "menu-1-success": "<p>设置成功!</p>", "menu-1-error-duplicate": "<p>选择的服务器与旧的默认服务器相同!</p>", "menu-2-prompt": "<p>该服务器的 url:{baseUrl}</p>\n<p>请输入新的服务器 url:</p>\n<p>0. 返回</p>\n<p>q. 退出</p>", "menu-2-success": "<p>修改成功!</p>", "menu-2-invalid-url": "<p>URL 格式不正确!请输入有效的 url 地址。</p>", "menu-2-too-many-attempts": "<p>尝试次数过多,操作已取消。</p>", "menu-3-success": "<p>已删除!</p>", "menu-4-prompt": "<p>输入一个新名字,不要带空格哦:</p>\n<p>0. 返回</p>\n<p>q. 退出</p>", "menu-4-error-invalid-name": "<p>名字不要带空格!重来重来 o(一︿一+)o</p>", "menu-4-success": "<p>修改成功!</p>", "add-type": "<p>请选择服务器的类型:</p>\n{server_type_list}\n<p>q. 退出</p>", "add-url": "<p>请输入服务器的 url:</p>\n<p>q. 退出</p>", "add-invalid-url": "<p>URL 格式不正确!请输入有效的 url 地址。</p>", "add-too-many-attempts": "<p>尝试次数过多,操作已取消。</p>", "add-name": "<p>收到!为服务器取一个名字吧,不要带空格哦:</p>\n<p>q. 退出</p>", "add-invalid_name": "<p>名字不要带空格!重来重来 o(一︿一+)o</p>", "add-success": "<p>添加成功啦,服务器信息如下:</p>\n<p>[{serverName}]</p>\n<p>类型:{serverType}</p>" } } } };
|
|
2956
|
+
var zh_CN_default3 = { _config: { $desc: "Core 模块设置", adminUsers: "**插件管理员** 的用户id (使用 inspect 指令获取)", guildNameCards: "**群聊卡片** 的名称列表", maoServerUrl: "**猫网服务器** 的 URL 地址", official_support_url: "**官方支持** 的 URL 地址" }, commands: { maintain: { description: "切换到维护模式", messages: { "no-auth": "<p>你没有权限使用本功能哦~</p>", start: "<p>正在进入维护模式</p>", "success-start": "<p>成功切换到维护模式</p>", stop: "<p>正在退出维护模式</p>", "success-stop": "<p>成功退出维护模式</p>" } }, timeout: "Noah 没等到你的回复,请重试!", noah: { help: { description: "显示 Noah 帮助信息", messages: { content: "<p>使用文档:</p>\nhttps://docs.logthm.cn/noah" } } }, locale: { description: "设置语言", messages: { "no-auth": "<p>只有群管理员能使用本功能哦~</p>", "invalid-select": "<p>没有该选项!</p>", quit: "<p>已退出~</p>", "reset-channel": "<p>重置成功!</p>", "reset-user": "<p>重置成功!</p>", success: "<p>设置成功!</p>", "set-channel": "<p>选择群聊默认使用的语言:</p>\n<p>1. 简体中文</p>\n<p>2. English</p>\n<p>q. 退出</p>", "set-user": "<p>选择你使用的语言:</p>\n<p>1. 简体中文</p>\n<p>2. English</p>\n<p>q. 退出</p>" } }, bind: { description: "绑定卡片", messages: { prompt: "<p>请输入你的卡号:</p>", "convert-access-failed": "<p>转换 Access Code 失败,请使用 16 位卡号进行绑定。</p>", "invalid-code": "<p>卡号不对哟,不记得的话去机台刷一下吧~</p>", name: "<p>收到!为这张卡取一个名字吧,不要带空格哦:</p>", invalid_name: "<p>名字不要带空格!重来重来 o(一︿一+)o</p>", success: "<p>绑好啦,你的卡片信息如下:</p>\n<p>[{cardName}]</p>\n<p>{cardCode}</p>" } }, card: { description: "管理卡片", options: { detail: "显示卡片详细信息" }, messages: { "invalid-code": "<p>卡号不对哟,不记得的话去机台刷一下吧~</p>", "invalid-select": "<p>没有该选项!</p>", quit: "<p>已退出~</p>", "lookup-error": "查询失败: {message}", "lookup-error-unknown": "查询失败: 未知错误", "menu-select": "<p>[卡片管理]</p>\n{card_list}\n<p>0. 添加新卡片</p>\n<p>q. 退出菜单</p>\n<p>请输入序号:</p>", menu: "<p>[{name}]</p>\n<p>1. 设为默认卡片</p>\n<p>2. 查看或修改卡号</p>\n<p>3. 删除该卡</p>\n<p>4. 重命名该卡片</p>\n<p>5. 将卡片与指定服务器绑定</p>\n<p>0. 返回卡片选择</p>\n<p>q. 退出菜单</p>\n<p>请输入序号:</p>", "menu-has-bound-server": "<p>[{name}]</p>\n<p>已绑定服务器:{defaultServerName}</p>\n<p>1. 设为默认卡片</p>\n<p>2. 查看或修改卡号</p>\n<p>3. 删除该卡</p>\n<p>4. 重命名该卡片</p>\n<p>5. 将卡片与指定服务器绑定</p>\n<p>0. 返回卡片选择</p>\n<p>q. 退出菜单</p>\n<p>请输入序号:</p>", "menu-1-success": "<p>设置成功!</p>", "menu-1-error-duplicate": "<p>选择的卡片与旧的默认卡片相同!</p>", "menu-2-prompt": "<p>卡号:{code}</p>\n<p>请输入新的卡号:</p>\n<p>0. 返回</p>\n<p>q. 退出</p>", "menu-2-success": "<p>修改成功!</p>", "menu-2-error-invalid-code": "<p>卡号不对哟,不记得的话去机台刷一下吧~</p>", "menu-3-success": "<p>已删除!</p>", "menu-4-prompt": "<p>输入一个新名字,不要带空格哦:</p>\n<p>q. 退出</p>", "menu-4-error-invalid-name": "<p>名字不要带空格!重来重来 o(一︿一+)o</p>", "menu-4-success": "<p>修改成功!</p>", "menu-5": "<p>请选择一个服务器:</p>\n{server_list}\n<p>q. 退出</p>", "menu-5-success": "<p>设置成功!</p>", "menu-5-error-duplicate": "<p>选择的服务器与旧的默认服务器相同!</p>" } }, server: { description: "管理服务器", messages: { "no-channel": "<p>请在群聊中使用本功能哦~</p>", "invalid-select": "<p>没有该选项!</p>", "no-auth": "<p>只有群管理员能使用本功能哦~</p>", quit: "<p>已退出~</p>", "admin-menu-select": "<p>[群聊服务器管理]</p>\n{server_list}\n<p>0. 添加新服务器</p>\n<p>q. 退出菜单</p>\n<p>请输入序号:</p>", "menu-select": "<p>[服务器管理]</p>\n{server_list}\n<p>0. 添加新服务器</p>\n<p>q. 退出菜单</p>\n<p>请输入序号:</p>", menu: "<p>[{name}]</p>\n<p>类型:{type}</p>\n<p>1. 设为默认服务器</p>\n<p>2. 查看或修改 url</p>\n<p>3. 删除该服务器</p>\n<p>4. 重命名该服务器</p>\n<p>0. 返回服务器选择</p>\n<p>q. 退出菜单</p>\n<p>请输入序号:</p>", "menu-1-success": "<p>设置成功!</p>", "menu-1-error-duplicate": "<p>选择的服务器与旧的默认服务器相同!</p>", "menu-2-prompt": "<p>该服务器的 url:{baseUrl}</p>\n<p>请输入新的服务器 url:</p>\n<p>0. 返回</p>\n<p>q. 退出</p>", "menu-2-success": "<p>修改成功!</p>", "menu-2-invalid-url": "<p>URL 格式不正确!请输入有效的 url 地址。</p>", "menu-2-too-many-attempts": "<p>尝试次数过多,操作已取消。</p>", "menu-3-success": "<p>已删除!</p>", "menu-4-prompt": "<p>输入一个新名字,不要带空格哦:</p>\n<p>0. 返回</p>\n<p>q. 退出</p>", "menu-4-error-invalid-name": "<p>名字不要带空格!重来重来 o(一︿一+)o</p>", "menu-4-success": "<p>修改成功!</p>", "add-type": "<p>请选择服务器的类型:</p>\n{server_type_list}\n<p>q. 退出</p>", "add-url": "<p>请输入服务器的 url:</p>\n<p>q. 退出</p>", "add-invalid-url": "<p>URL 格式不正确!请输入有效的 url 地址。</p>", "add-too-many-attempts": "<p>尝试次数过多,操作已取消。</p>", "add-name": "<p>收到!为服务器取一个名字吧,不要带空格哦:</p>\n<p>q. 退出</p>", "add-invalid_name": "<p>名字不要带空格!重来重来 o(一︿一+)o</p>", "add-success": "<p>添加成功啦,服务器信息如下:</p>\n<p>[{serverName}]</p>\n<p>类型:{serverType}</p>" } } } };
|
|
2924
2957
|
|
|
2925
2958
|
// src/core/index.ts
|
|
2926
2959
|
var name7 = "Noah-Core";
|
|
@@ -3109,14 +3142,14 @@ var GRADE_FACTOR_SCALES = {
|
|
|
3109
3142
|
var CLEAR_FACTOR_SCALES = {
|
|
3110
3143
|
"S-PUC": 110,
|
|
3111
3144
|
PUC: 110,
|
|
3112
|
-
UC:
|
|
3145
|
+
UC: 106,
|
|
3113
3146
|
MC: 104,
|
|
3114
3147
|
HC: 102,
|
|
3115
3148
|
NC: 100,
|
|
3116
3149
|
PLAYED: 50,
|
|
3117
3150
|
"NO PLAY": 50
|
|
3118
3151
|
};
|
|
3119
|
-
var VOLFORCE_BASE_DENOMINATOR = BigInt(5e5) * BigInt(FACTOR_SCALE) * BigInt(FACTOR_SCALE);
|
|
3152
|
+
var VOLFORCE_BASE_DENOMINATOR = BigInt(5e5) * BigInt(FACTOR_SCALE) * BigInt(FACTOR_SCALE) * BigInt(10);
|
|
3120
3153
|
function calculateVolforce(raw_score) {
|
|
3121
3154
|
const score = raw_score.music.score;
|
|
3122
3155
|
const level = raw_score.music.music_diff;
|
|
@@ -3139,7 +3172,8 @@ function calculateVolforceIntValue(level, score, grade, clearType) {
|
|
|
3139
3172
|
}
|
|
3140
3173
|
__name(calculateVolforceIntValue, "calculateVolforceIntValue");
|
|
3141
3174
|
function calculateVolforceInt(level, score, grade, clearType) {
|
|
3142
|
-
const
|
|
3175
|
+
const levelScaled = Math.round(level * 10);
|
|
3176
|
+
const numerator = BigInt(levelScaled) * BigInt(score) * BigInt(GRADE_FACTOR_SCALES[grade]) * BigInt(CLEAR_FACTOR_SCALES[clearType]);
|
|
3143
3177
|
return numerator / VOLFORCE_BASE_DENOMINATOR;
|
|
3144
3178
|
}
|
|
3145
3179
|
__name(calculateVolforceInt, "calculateVolforceInt");
|
|
@@ -3160,6 +3194,27 @@ var SCORE_GRADE_MAP = [
|
|
|
3160
3194
|
];
|
|
3161
3195
|
var VF_INT_MULTIPLIER = 20;
|
|
3162
3196
|
var VF_INPUT_PRECISION = 6;
|
|
3197
|
+
var LEVEL_SCALE = 10;
|
|
3198
|
+
function scaleLevel(level) {
|
|
3199
|
+
return Math.round(level * LEVEL_SCALE);
|
|
3200
|
+
}
|
|
3201
|
+
__name(scaleLevel, "scaleLevel");
|
|
3202
|
+
function unscaleLevel(levelScaled) {
|
|
3203
|
+
return levelScaled / LEVEL_SCALE;
|
|
3204
|
+
}
|
|
3205
|
+
__name(unscaleLevel, "unscaleLevel");
|
|
3206
|
+
function getAllValidLevels() {
|
|
3207
|
+
const levels = [];
|
|
3208
|
+
for (let i = 1; i <= 16; i++) {
|
|
3209
|
+
levels.push(i);
|
|
3210
|
+
}
|
|
3211
|
+
levels.push(17, 17.5);
|
|
3212
|
+
for (let levelScaled = 180; levelScaled <= 209; levelScaled++) {
|
|
3213
|
+
levels.push(unscaleLevel(levelScaled));
|
|
3214
|
+
}
|
|
3215
|
+
return levels;
|
|
3216
|
+
}
|
|
3217
|
+
__name(getAllValidLevels, "getAllValidLevels");
|
|
3163
3218
|
function normalizeVfInput(value) {
|
|
3164
3219
|
return Number(value.toFixed(VF_INPUT_PRECISION));
|
|
3165
3220
|
}
|
|
@@ -3177,7 +3232,8 @@ function floorVfInt(value) {
|
|
|
3177
3232
|
}
|
|
3178
3233
|
__name(floorVfInt, "floorVfInt");
|
|
3179
3234
|
function getVolforceMultiplier(level, grade, clearType) {
|
|
3180
|
-
|
|
3235
|
+
const levelScaled = Math.round(level * 10);
|
|
3236
|
+
return BigInt(levelScaled) * BigInt(getGradeFactorScale(grade)) * BigInt(getClearFactorScale(clearType));
|
|
3181
3237
|
}
|
|
3182
3238
|
__name(getVolforceMultiplier, "getVolforceMultiplier");
|
|
3183
3239
|
function getMinScoreForVfInt(multiplier, vfInt) {
|
|
@@ -3286,8 +3342,9 @@ function getClearTypeRange(clearType) {
|
|
|
3286
3342
|
}
|
|
3287
3343
|
__name(getClearTypeRange, "getClearTypeRange");
|
|
3288
3344
|
function getLevelRange(level) {
|
|
3289
|
-
if (!level) return
|
|
3290
|
-
|
|
3345
|
+
if (!level) return getAllValidLevels();
|
|
3346
|
+
const raw = Array.isArray(level) ? level : [level];
|
|
3347
|
+
return raw.map((l) => unscaleLevel(scaleLevel(l)));
|
|
3291
3348
|
}
|
|
3292
3349
|
__name(getLevelRange, "getLevelRange");
|
|
3293
3350
|
function generateVFLookupTable() {
|
|
@@ -3303,7 +3360,8 @@ function generateVFLookupTable() {
|
|
|
3303
3360
|
"PLAYED",
|
|
3304
3361
|
"NO PLAY"
|
|
3305
3362
|
];
|
|
3306
|
-
for (
|
|
3363
|
+
for (const level of getAllValidLevels()) {
|
|
3364
|
+
const levelScaled = scaleLevel(level);
|
|
3307
3365
|
for (const grade of allGrades) {
|
|
3308
3366
|
const gradeMapping = SCORE_GRADE_MAP.find((m) => m.grade === grade);
|
|
3309
3367
|
if (!gradeMapping) {
|
|
@@ -3337,12 +3395,13 @@ function generateVFLookupTable() {
|
|
|
3337
3395
|
if (!possibleClearTypes.includes(clearType)) {
|
|
3338
3396
|
continue;
|
|
3339
3397
|
}
|
|
3340
|
-
const
|
|
3341
|
-
const
|
|
3398
|
+
const vfInt = calculateVolforceIntValue(level, score, grade, clearType);
|
|
3399
|
+
const vf2 = vfInt / VF_INT_MULTIPLIER;
|
|
3400
|
+
const key = `${levelScaled}-${grade}-${clearType}-${vfInt}`;
|
|
3342
3401
|
const existing = lookupTable.get(key);
|
|
3343
3402
|
if (!existing || score < existing.score) {
|
|
3344
3403
|
lookupTable.set(key, {
|
|
3345
|
-
level,
|
|
3404
|
+
level: unscaleLevel(levelScaled),
|
|
3346
3405
|
score,
|
|
3347
3406
|
grade,
|
|
3348
3407
|
clearType,
|
|
@@ -3386,13 +3445,14 @@ function generateQueryResults(params) {
|
|
|
3386
3445
|
const lookupTable = getVFLookupTable();
|
|
3387
3446
|
const results = [];
|
|
3388
3447
|
const levels = getLevelRange(params.level);
|
|
3448
|
+
const levelScaleSet = new Set(levels.map(scaleLevel));
|
|
3389
3449
|
const clearTypes = getClearTypeRange(params.clearType);
|
|
3390
3450
|
const grades = getGradeRange(params.grade);
|
|
3391
3451
|
if (params.vf) {
|
|
3392
3452
|
const vfValues = getVfRange(params.vf);
|
|
3393
3453
|
const vfSet = new Set(vfValues);
|
|
3394
3454
|
for (const result of lookupTable.values()) {
|
|
3395
|
-
if (
|
|
3455
|
+
if (levelScaleSet.has(scaleLevel(result.level)) && clearTypes.includes(result.clearType) && grades.includes(result.grade) && vfSet.has(result.vf)) {
|
|
3396
3456
|
results.push(result);
|
|
3397
3457
|
}
|
|
3398
3458
|
}
|
|
@@ -3490,7 +3550,7 @@ function generateQueryResults(params) {
|
|
|
3490
3550
|
}
|
|
3491
3551
|
} else {
|
|
3492
3552
|
for (const result of lookupTable.values()) {
|
|
3493
|
-
if (
|
|
3553
|
+
if (levelScaleSet.has(scaleLevel(result.level)) && clearTypes.includes(result.clearType) && grades.includes(result.grade)) {
|
|
3494
3554
|
results.push(result);
|
|
3495
3555
|
}
|
|
3496
3556
|
}
|
|
@@ -3518,6 +3578,14 @@ var SDVXDrawer = class extends BaseDrawer {
|
|
|
3518
3578
|
static {
|
|
3519
3579
|
__name(this, "SDVXDrawer");
|
|
3520
3580
|
}
|
|
3581
|
+
/**
|
|
3582
|
+
* Format difficulty level text.
|
|
3583
|
+
* If it's an integer like 17 / 20, keep trailing ".0" (e.g. "17.0").
|
|
3584
|
+
*/
|
|
3585
|
+
formatDifficultyLevel(level) {
|
|
3586
|
+
if (!Number.isFinite(level)) return String(level);
|
|
3587
|
+
return Number.isInteger(level) ? level.toFixed(1) : String(level);
|
|
3588
|
+
}
|
|
3521
3589
|
/**
|
|
3522
3590
|
* 生成SDVX最近成绩图像
|
|
3523
3591
|
* @param options - 图像生成配置选项
|
|
@@ -3709,7 +3777,7 @@ var SDVXDrawer = class extends BaseDrawer {
|
|
|
3709
3777
|
ctx.drawImage(gradeImage, 139, 151);
|
|
3710
3778
|
const playTypeImage = playTypeImages[options.score.music.clear_type];
|
|
3711
3779
|
ctx.drawImage(playTypeImage, 139, 178);
|
|
3712
|
-
const levelStr = options.score.music.music_diff
|
|
3780
|
+
const levelStr = this.formatDifficultyLevel(options.score.music.music_diff);
|
|
3713
3781
|
ctx.fillStyle = "#0230A5";
|
|
3714
3782
|
ctx.textBaseline = "alphabetic";
|
|
3715
3783
|
ctx.textAlign = "left";
|
|
@@ -3978,7 +4046,7 @@ var SDVXDrawer = class extends BaseDrawer {
|
|
|
3978
4046
|
ctx.font = '24px "Fredoka One"';
|
|
3979
4047
|
ctx.textAlign = "right";
|
|
3980
4048
|
ctx.textBaseline = "alphabetic";
|
|
3981
|
-
ctx.fillText(score.music.music_diff
|
|
4049
|
+
ctx.fillText(this.formatDifficultyLevel(score.music.music_diff), x + 248, y + 67);
|
|
3982
4050
|
ctx.restore();
|
|
3983
4051
|
const scoreText = score.music.score.toString();
|
|
3984
4052
|
const scoreStartX = x + 381;
|
|
@@ -4414,7 +4482,7 @@ var SDVXDrawer = class extends BaseDrawer {
|
|
|
4414
4482
|
const rowHeight = rowHeights.get(level) || baseCellHeight;
|
|
4415
4483
|
const x = padding;
|
|
4416
4484
|
const y = levelY + rowHeight / 2;
|
|
4417
|
-
ctx.fillText(
|
|
4485
|
+
ctx.fillText(this.formatDifficultyLevel(level), x + levelColumnWidth / 2, y);
|
|
4418
4486
|
levelY += rowHeight;
|
|
4419
4487
|
}
|
|
4420
4488
|
ctx.strokeStyle = "#444444";
|
|
@@ -4886,20 +4954,72 @@ function parseNumberWithSuffix(str) {
|
|
|
4886
4954
|
return num;
|
|
4887
4955
|
}
|
|
4888
4956
|
__name(parseNumberWithSuffix, "parseNumberWithSuffix");
|
|
4957
|
+
function isValidLevel(level) {
|
|
4958
|
+
if (level < 1 || level > 20.9) {
|
|
4959
|
+
return false;
|
|
4960
|
+
}
|
|
4961
|
+
const integerPart = Math.floor(level);
|
|
4962
|
+
const decimalPart = level - integerPart;
|
|
4963
|
+
if (integerPart >= 1 && integerPart <= 16) {
|
|
4964
|
+
return decimalPart === 0;
|
|
4965
|
+
}
|
|
4966
|
+
if (integerPart === 17) {
|
|
4967
|
+
return decimalPart === 0 || decimalPart === 0.5;
|
|
4968
|
+
}
|
|
4969
|
+
if (integerPart >= 18 && integerPart <= 20) {
|
|
4970
|
+
const tenths = Math.round(decimalPart * 10);
|
|
4971
|
+
return Math.abs(decimalPart * 10 - tenths) < 1e-4 && tenths >= 0 && tenths <= 9;
|
|
4972
|
+
}
|
|
4973
|
+
return false;
|
|
4974
|
+
}
|
|
4975
|
+
__name(isValidLevel, "isValidLevel");
|
|
4976
|
+
function generateValidLevelRange(start, end) {
|
|
4977
|
+
const levels = [];
|
|
4978
|
+
const startInt = Math.floor(start);
|
|
4979
|
+
const endInt = Math.floor(end);
|
|
4980
|
+
const startTenth = Math.round((start - startInt) * 10);
|
|
4981
|
+
const endTenth = Math.round((end - endInt) * 10);
|
|
4982
|
+
for (let intPart = startInt; intPart <= endInt; intPart++) {
|
|
4983
|
+
if (intPart >= 1 && intPart <= 16) {
|
|
4984
|
+
if (intPart === startInt && startTenth > 0 || intPart === endInt && endTenth > 0) {
|
|
4985
|
+
continue;
|
|
4986
|
+
}
|
|
4987
|
+
levels.push(intPart);
|
|
4988
|
+
} else if (intPart === 17) {
|
|
4989
|
+
const currentStartTenth = intPart === startInt ? startTenth : 0;
|
|
4990
|
+
const currentEndTenth = intPart === endInt ? endTenth : 5;
|
|
4991
|
+
if (currentStartTenth <= 0 && currentEndTenth >= 0) {
|
|
4992
|
+
levels.push(17);
|
|
4993
|
+
}
|
|
4994
|
+
if (currentStartTenth <= 5 && currentEndTenth >= 5) {
|
|
4995
|
+
levels.push(17.5);
|
|
4996
|
+
}
|
|
4997
|
+
} else if (intPart >= 18 && intPart <= 20) {
|
|
4998
|
+
const currentStartTenth = intPart === startInt ? startTenth : 0;
|
|
4999
|
+
const currentEndTenth = intPart === endInt ? endTenth : 9;
|
|
5000
|
+
for (let tenth = currentStartTenth; tenth <= currentEndTenth && tenth <= 9; tenth++) {
|
|
5001
|
+
levels.push(intPart + tenth / 10);
|
|
5002
|
+
}
|
|
5003
|
+
}
|
|
5004
|
+
}
|
|
5005
|
+
return levels;
|
|
5006
|
+
}
|
|
5007
|
+
__name(generateValidLevelRange, "generateValidLevelRange");
|
|
4889
5008
|
function parseLevelRange(item) {
|
|
4890
|
-
if (/^\d{1,2}
|
|
4891
|
-
const [
|
|
4892
|
-
|
|
4893
|
-
|
|
4894
|
-
|
|
4895
|
-
|
|
5009
|
+
if (/^\d{1,2}(\.\d)?-\d{1,2}(\.\d)?$/.test(item)) {
|
|
5010
|
+
const [startRaw, endRaw] = item.split("-");
|
|
5011
|
+
const start = parseFloat(startRaw.includes(".") ? startRaw : startRaw + ".0");
|
|
5012
|
+
const end = parseFloat(endRaw.includes(".") ? endRaw : endRaw + ".0");
|
|
5013
|
+
if (start >= 1 && end <= 20.9 && start <= end) {
|
|
5014
|
+
if (!isValidLevel(start) || !isValidLevel(end)) {
|
|
5015
|
+
return null;
|
|
4896
5016
|
}
|
|
4897
|
-
return
|
|
5017
|
+
return generateValidLevelRange(start, end);
|
|
4898
5018
|
}
|
|
4899
5019
|
}
|
|
4900
|
-
if (/^\d{1,2}
|
|
4901
|
-
const level =
|
|
4902
|
-
if (level
|
|
5020
|
+
if (/^\d{1,2}(\.\d)?$/.test(item)) {
|
|
5021
|
+
const level = parseFloat(item.includes(".") ? item : item + ".0");
|
|
5022
|
+
if (isValidLevel(level)) {
|
|
4903
5023
|
return [level];
|
|
4904
5024
|
}
|
|
4905
5025
|
}
|
|
@@ -5199,6 +5319,8 @@ function getDiffName(diffStr, infVer) {
|
|
|
5199
5319
|
break;
|
|
5200
5320
|
case "maximum":
|
|
5201
5321
|
return "MXM";
|
|
5322
|
+
case "ultimate":
|
|
5323
|
+
return "ULT";
|
|
5202
5324
|
}
|
|
5203
5325
|
}
|
|
5204
5326
|
__name(getDiffName, "getDiffName");
|
|
@@ -5229,6 +5351,8 @@ function getDiffFullName(diffStr, infVer) {
|
|
|
5229
5351
|
break;
|
|
5230
5352
|
case "maximum":
|
|
5231
5353
|
return "MAXIMUM";
|
|
5354
|
+
case "ultimate":
|
|
5355
|
+
return "ULTIMATE";
|
|
5232
5356
|
}
|
|
5233
5357
|
}
|
|
5234
5358
|
__name(getDiffFullName, "getDiffFullName");
|
|
@@ -5253,6 +5377,8 @@ function getDiffStringFromAbbr(diffAbbr) {
|
|
|
5253
5377
|
return { diffStr: "infinite", infVer: 6 };
|
|
5254
5378
|
case "MXM":
|
|
5255
5379
|
return { diffStr: "maximum", infVer: null };
|
|
5380
|
+
case "ULT":
|
|
5381
|
+
return { diffStr: "ultimate", infVer: null };
|
|
5256
5382
|
default:
|
|
5257
5383
|
return null;
|
|
5258
5384
|
}
|
|
@@ -5271,6 +5397,26 @@ var MusicService = class _MusicService {
|
|
|
5271
5397
|
this.sdvx_data_url = config.sdvx_data_url;
|
|
5272
5398
|
this.sdvx_search_url = config.sdvx_search_url;
|
|
5273
5399
|
}
|
|
5400
|
+
parseDifnum(value) {
|
|
5401
|
+
if (typeof value === "number") {
|
|
5402
|
+
return Number.isFinite(value) ? value : 0;
|
|
5403
|
+
}
|
|
5404
|
+
if (typeof value === "string") {
|
|
5405
|
+
const parsed = Number.parseFloat(value.trim());
|
|
5406
|
+
return Number.isFinite(parsed) ? parsed : 0;
|
|
5407
|
+
}
|
|
5408
|
+
return 0;
|
|
5409
|
+
}
|
|
5410
|
+
normalizeMusicDifnum(music) {
|
|
5411
|
+
if (!music?.difficulty?.length) return music;
|
|
5412
|
+
return {
|
|
5413
|
+
...music,
|
|
5414
|
+
difficulty: music.difficulty.map((d) => ({
|
|
5415
|
+
...d,
|
|
5416
|
+
difnum: this.parseDifnum(d.difnum)
|
|
5417
|
+
}))
|
|
5418
|
+
};
|
|
5419
|
+
}
|
|
5274
5420
|
/**
|
|
5275
5421
|
* 获取 MusicService 实例
|
|
5276
5422
|
* @param config - SDVX 配置对象
|
|
@@ -5299,7 +5445,11 @@ var MusicService = class _MusicService {
|
|
|
5299
5445
|
{ ids: numericIds },
|
|
5300
5446
|
{ baseURL: this.sdvx_data_url }
|
|
5301
5447
|
);
|
|
5302
|
-
|
|
5448
|
+
if (Array.isArray(response)) {
|
|
5449
|
+
results.push(
|
|
5450
|
+
...response.map((m) => this.normalizeMusicDifnum(m))
|
|
5451
|
+
);
|
|
5452
|
+
}
|
|
5303
5453
|
} catch (error) {
|
|
5304
5454
|
console.warn("Failed to fetch music data for numeric IDs:", error);
|
|
5305
5455
|
}
|
|
@@ -5312,7 +5462,7 @@ var MusicService = class _MusicService {
|
|
|
5312
5462
|
{ baseURL: this.sdvx_data_url }
|
|
5313
5463
|
);
|
|
5314
5464
|
const converted = this.convertExternalToStandard(response);
|
|
5315
|
-
results.push(...converted);
|
|
5465
|
+
results.push(...converted.map((m) => this.normalizeMusicDifnum(m)));
|
|
5316
5466
|
} catch (error) {
|
|
5317
5467
|
console.warn("Failed to fetch music data for string IDs:", error);
|
|
5318
5468
|
}
|
|
@@ -5334,7 +5484,7 @@ var MusicService = class _MusicService {
|
|
|
5334
5484
|
inf_ver: data.difficulty[0]?.inf_ver || 0,
|
|
5335
5485
|
difficulty: data.difficulty.map((diff) => ({
|
|
5336
5486
|
difstr: diff.difstr,
|
|
5337
|
-
difnum: diff.difnum,
|
|
5487
|
+
difnum: this.parseDifnum(diff.difnum),
|
|
5338
5488
|
illustrator: diff.ext_illustrator,
|
|
5339
5489
|
effected_by: diff.ext_effected_by,
|
|
5340
5490
|
cover_url: diff.cover_url,
|
|
@@ -5365,6 +5515,32 @@ var MusicService = class _MusicService {
|
|
|
5365
5515
|
return null;
|
|
5366
5516
|
}
|
|
5367
5517
|
}
|
|
5518
|
+
async extTitleToMid(ctx, titles) {
|
|
5519
|
+
try {
|
|
5520
|
+
const response = await ctx.http.post(
|
|
5521
|
+
`/external/title-mapping`,
|
|
5522
|
+
{ titles },
|
|
5523
|
+
{ baseURL: this.sdvx_data_url }
|
|
5524
|
+
);
|
|
5525
|
+
if (!Array.isArray(response)) {
|
|
5526
|
+
return [];
|
|
5527
|
+
}
|
|
5528
|
+
const mapping = new Map(
|
|
5529
|
+
response.map((item) => [
|
|
5530
|
+
item?.title?.toLowerCase(),
|
|
5531
|
+
Number.isInteger(item?.music_id) ? String(item.music_id) : null
|
|
5532
|
+
])
|
|
5533
|
+
);
|
|
5534
|
+
const mids = [];
|
|
5535
|
+
for (const title of titles) {
|
|
5536
|
+
const mid = mapping.get(title.toLowerCase()) ?? null;
|
|
5537
|
+
mids.push({ title, mid });
|
|
5538
|
+
}
|
|
5539
|
+
return mids;
|
|
5540
|
+
} catch {
|
|
5541
|
+
return [];
|
|
5542
|
+
}
|
|
5543
|
+
}
|
|
5368
5544
|
};
|
|
5369
5545
|
|
|
5370
5546
|
// src/games/sdvx/commands/chart.ts
|
|
@@ -5819,7 +5995,7 @@ var SDVXService2 = class _SDVXService {
|
|
|
5819
5995
|
}
|
|
5820
5996
|
/**
|
|
5821
5997
|
* Get the difficulty string based on the difficulty type number
|
|
5822
|
-
* @param diffType Difficulty type
|
|
5998
|
+
* @param diffType Difficulty type
|
|
5823
5999
|
* @returns The difficulty string (novice, advanced, exhaust, infinite, maximum)
|
|
5824
6000
|
*/
|
|
5825
6001
|
getDifficultyString(diffType) {
|
|
@@ -5851,6 +6027,38 @@ var SDVXService2 = class _SDVXService {
|
|
|
5851
6027
|
}
|
|
5852
6028
|
return match[1];
|
|
5853
6029
|
}
|
|
6030
|
+
/**
|
|
6031
|
+
* 验证猫网 PIN 码
|
|
6032
|
+
* @param ctx - Koishi 上下文对象
|
|
6033
|
+
* @param url - 猫网基础 URL
|
|
6034
|
+
* @param cardId - 卡号
|
|
6035
|
+
* @param pin - 四位数字 PIN
|
|
6036
|
+
* @returns 验证是否通过
|
|
6037
|
+
*/
|
|
6038
|
+
async verifyPin(ctx, url, cardId, pin) {
|
|
6039
|
+
const baseURL = "https://maomani.cn";
|
|
6040
|
+
const resp = await ctx.http.post(
|
|
6041
|
+
"/api/login/account",
|
|
6042
|
+
{
|
|
6043
|
+
username: cardId,
|
|
6044
|
+
password: pin
|
|
6045
|
+
},
|
|
6046
|
+
{ baseURL }
|
|
6047
|
+
);
|
|
6048
|
+
return resp?.errorCode === 200;
|
|
6049
|
+
}
|
|
6050
|
+
async uploadScore(ctx, url, cardId, scorePayload) {
|
|
6051
|
+
const baseURL = "https://maomani.cn";
|
|
6052
|
+
const userName = await this.getUserName(ctx, url, cardId);
|
|
6053
|
+
const resp = await ctx.http.post(
|
|
6054
|
+
`/api/logs/upload?name=${encodeURIComponent(`${userName}`)}&pw=logs`,
|
|
6055
|
+
scorePayload,
|
|
6056
|
+
{
|
|
6057
|
+
baseURL
|
|
6058
|
+
}
|
|
6059
|
+
);
|
|
6060
|
+
return resp === "Logs-573sdvx-json-导入成功";
|
|
6061
|
+
}
|
|
5854
6062
|
async getAllScore(ctx, url, cardId, config) {
|
|
5855
6063
|
try {
|
|
5856
6064
|
const data = await ctx.http.get(`/sdvx/scores?card=${cardId}`, {
|
|
@@ -6149,11 +6357,19 @@ var SDVXService3 = class _SDVXService {
|
|
|
6149
6357
|
const scoresData = response;
|
|
6150
6358
|
const stringMusicTitles = [...new Set(scoresData.map((score) => score.title))];
|
|
6151
6359
|
const musicService = MusicService.getInstance(config);
|
|
6152
|
-
const musicDataList = await
|
|
6360
|
+
const [musicDataList, titleMidPairs] = await Promise.all([
|
|
6361
|
+
musicService.getMusic(ctx, stringMusicTitles),
|
|
6362
|
+
musicService.extTitleToMid(ctx, stringMusicTitles)
|
|
6363
|
+
]);
|
|
6364
|
+
const titleMidMap = new Map(
|
|
6365
|
+
titleMidPairs.filter(({ mid }) => !!mid).map(({ title, mid }) => [title.toLowerCase(), mid])
|
|
6366
|
+
);
|
|
6153
6367
|
const musicDataMap = new Map(musicDataList.map((music) => [music.title_name, music]));
|
|
6154
6368
|
const sdvxScores = [];
|
|
6155
6369
|
for (const scoreData of scoresData) {
|
|
6156
6370
|
const musicData = musicDataMap.get(scoreData.title);
|
|
6371
|
+
const mappedMid = titleMidMap.get(scoreData.title.toLowerCase());
|
|
6372
|
+
const musicId = mappedMid && /^\d+$/.test(mappedMid) ? Number(mappedMid) : scoreData.music_id;
|
|
6157
6373
|
for (const [diffKey, diffScore] of Object.entries(scoreData.difficulties)) {
|
|
6158
6374
|
const diffAbbr = diffKey.toUpperCase();
|
|
6159
6375
|
const diffInfo = getDiffStringFromAbbr(diffAbbr);
|
|
@@ -6167,7 +6383,7 @@ var SDVXService3 = class _SDVXService {
|
|
|
6167
6383
|
}
|
|
6168
6384
|
const scoreObj = {
|
|
6169
6385
|
music: {
|
|
6170
|
-
music_id:
|
|
6386
|
+
music_id: musicId,
|
|
6171
6387
|
music_diff: difficultyData?.difnum || 0,
|
|
6172
6388
|
music_diff_name: getDiffName(diffStr, infVer),
|
|
6173
6389
|
music_diff_full_name: getDiffFullName(diffStr, infVer),
|
|
@@ -6317,9 +6533,10 @@ function recent(ctx, config, logger6) {
|
|
|
6317
6533
|
const serverService = new ServerService(ctx);
|
|
6318
6534
|
const userCards = await cardService.getCardsByUid(session.user.id);
|
|
6319
6535
|
if (userCards.length === 0) return session.text(".card-not-found");
|
|
6320
|
-
const
|
|
6321
|
-
|
|
6322
|
-
|
|
6536
|
+
const serverRes = await serverService.getSelectableServers(
|
|
6537
|
+
session.user.id,
|
|
6538
|
+
atGuild ? session.channel.id : null
|
|
6539
|
+
);
|
|
6323
6540
|
if (serverRes.length === 0) return session.text(".server-not-found");
|
|
6324
6541
|
let cardCode = "";
|
|
6325
6542
|
if (!options.card) {
|
|
@@ -6404,6 +6621,252 @@ function recent(ctx, config, logger6) {
|
|
|
6404
6621
|
}
|
|
6405
6622
|
__name(recent, "recent");
|
|
6406
6623
|
|
|
6624
|
+
// src/games/sdvx/commands/sync.ts
|
|
6625
|
+
function sync(ctx, config, logger6) {
|
|
6626
|
+
ctx.command("sdvx.sync").userFields(["id"]).channelFields(["id"]).action(async ({ session }) => {
|
|
6627
|
+
const serverService = new ServerService(ctx);
|
|
6628
|
+
const cardService = new CardService(ctx);
|
|
6629
|
+
const atGuild = session.guildId != null;
|
|
6630
|
+
if (atGuild) return session.text(".dm-only");
|
|
6631
|
+
const allServers = await serverService.getSelectableServers(
|
|
6632
|
+
session.user.id,
|
|
6633
|
+
atGuild ? session.channel.id : null
|
|
6634
|
+
);
|
|
6635
|
+
const maoServer = allServers.find((server2) => server2.type === "mao");
|
|
6636
|
+
if (!maoServer) {
|
|
6637
|
+
return session.text(".mao-not-found");
|
|
6638
|
+
}
|
|
6639
|
+
const sourceServers = allServers;
|
|
6640
|
+
if (sourceServers.length === 0) {
|
|
6641
|
+
return session.text(".source-server-not-found");
|
|
6642
|
+
}
|
|
6643
|
+
let serverListMsg = "";
|
|
6644
|
+
for (let i = 0; i < sourceServers.length; i++) {
|
|
6645
|
+
serverListMsg += `${i + 1}. ${sourceServers[i].name} (${sourceServers[i].type})
|
|
6646
|
+
`;
|
|
6647
|
+
}
|
|
6648
|
+
await session.send(
|
|
6649
|
+
session.text(".source-server-select", { server_list: serverListMsg })
|
|
6650
|
+
);
|
|
6651
|
+
const serverSelect = await session.prompt();
|
|
6652
|
+
if (!serverSelect) return session.text("commands.timeout");
|
|
6653
|
+
if (serverSelect === "q") return session.text(".quit");
|
|
6654
|
+
const serverIndex = Number(serverSelect);
|
|
6655
|
+
if (!Number.isInteger(serverIndex) || serverIndex < 1 || serverIndex > sourceServers.length) {
|
|
6656
|
+
return session.text(".invalid-select");
|
|
6657
|
+
}
|
|
6658
|
+
const sourceServer = sourceServers[serverIndex - 1];
|
|
6659
|
+
const cards = await cardService.getCardsByUid(session.user.id);
|
|
6660
|
+
if (cards.length === 0) return session.text(".card-not-found");
|
|
6661
|
+
const defaultCard = await cardService.getDefaultCardByUid(session.user.id);
|
|
6662
|
+
const defaultCardId = defaultCard ? defaultCard.id : 0;
|
|
6663
|
+
let cardListMsg = "";
|
|
6664
|
+
for (let i = 0; i < cards.length; i++) {
|
|
6665
|
+
if (cards[i].id === defaultCardId) {
|
|
6666
|
+
cardListMsg += `${i + 1}. ${cards[i].name} < 默认卡片
|
|
6667
|
+
`;
|
|
6668
|
+
} else {
|
|
6669
|
+
cardListMsg += `${i + 1}. ${cards[i].name}
|
|
6670
|
+
`;
|
|
6671
|
+
}
|
|
6672
|
+
}
|
|
6673
|
+
await session.send(session.text(".source-card-select", { card_list: cardListMsg }));
|
|
6674
|
+
const sourceCardSelect = await session.prompt();
|
|
6675
|
+
if (!sourceCardSelect) return session.text("commands.timeout");
|
|
6676
|
+
if (sourceCardSelect === "q") return session.text(".quit");
|
|
6677
|
+
const sourceCardIndex = Number(sourceCardSelect);
|
|
6678
|
+
if (!Number.isInteger(sourceCardIndex) || sourceCardIndex < 1 || sourceCardIndex > cards.length) {
|
|
6679
|
+
return session.text(".invalid-select");
|
|
6680
|
+
}
|
|
6681
|
+
const sourceCard = cards[sourceCardIndex - 1];
|
|
6682
|
+
await session.send(session.text(".target-card-select", { card_list: cardListMsg }));
|
|
6683
|
+
const targetCardSelect = await session.prompt();
|
|
6684
|
+
if (!targetCardSelect) return session.text("commands.timeout");
|
|
6685
|
+
if (targetCardSelect === "q") return session.text(".quit");
|
|
6686
|
+
const targetCardIndex = Number(targetCardSelect);
|
|
6687
|
+
if (!Number.isInteger(targetCardIndex) || targetCardIndex < 1 || targetCardIndex > cards.length) {
|
|
6688
|
+
return session.text(".invalid-select");
|
|
6689
|
+
}
|
|
6690
|
+
const targetCard = cards[targetCardIndex - 1];
|
|
6691
|
+
if (sourceServer.type === "mao" && sourceCard.id === targetCard.id) {
|
|
6692
|
+
return session.text(".same-card-error");
|
|
6693
|
+
}
|
|
6694
|
+
const maxPinAttempts = 3;
|
|
6695
|
+
let pinVerified = false;
|
|
6696
|
+
const serverManager = ServerManager.getInstance();
|
|
6697
|
+
const maoSdvxService = serverManager.getGameService("mao", "sdvx");
|
|
6698
|
+
const maoVerifyUrl = "https://maomani.cn";
|
|
6699
|
+
if (!maoSdvxService || typeof maoSdvxService.verifyPin !== "function") {
|
|
6700
|
+
logger6.warn("Mao SDVX service does not support PIN verification");
|
|
6701
|
+
return session.text(".pin-verify-error");
|
|
6702
|
+
}
|
|
6703
|
+
const existingPin = await ctx.database.get("sdvx_pin_verified", {
|
|
6704
|
+
uid: session.user.id,
|
|
6705
|
+
cardId: targetCard.id,
|
|
6706
|
+
sid: maoServer.id
|
|
6707
|
+
});
|
|
6708
|
+
if (existingPin.length > 0) {
|
|
6709
|
+
const cachedPin = existingPin[0].pin;
|
|
6710
|
+
try {
|
|
6711
|
+
const ok = await maoSdvxService.verifyPin(
|
|
6712
|
+
ctx,
|
|
6713
|
+
maoVerifyUrl,
|
|
6714
|
+
targetCard.code,
|
|
6715
|
+
cachedPin
|
|
6716
|
+
);
|
|
6717
|
+
if (ok) {
|
|
6718
|
+
pinVerified = true;
|
|
6719
|
+
}
|
|
6720
|
+
} catch (error) {
|
|
6721
|
+
logger6.warn(error);
|
|
6722
|
+
}
|
|
6723
|
+
}
|
|
6724
|
+
if (!pinVerified) {
|
|
6725
|
+
for (let attempt = 0; attempt < maxPinAttempts; attempt++) {
|
|
6726
|
+
await session.send(session.text(".pin-prompt"));
|
|
6727
|
+
const pinInput = await session.prompt();
|
|
6728
|
+
if (!pinInput) return session.text("commands.timeout");
|
|
6729
|
+
if (pinInput === "q") return session.text(".quit");
|
|
6730
|
+
if (!/^\d{4}$/.test(pinInput)) {
|
|
6731
|
+
await session.send(session.text(".pin-invalid"));
|
|
6732
|
+
continue;
|
|
6733
|
+
}
|
|
6734
|
+
try {
|
|
6735
|
+
const ok = await maoSdvxService.verifyPin(
|
|
6736
|
+
ctx,
|
|
6737
|
+
maoVerifyUrl,
|
|
6738
|
+
targetCard.code,
|
|
6739
|
+
pinInput
|
|
6740
|
+
);
|
|
6741
|
+
if (ok) {
|
|
6742
|
+
pinVerified = true;
|
|
6743
|
+
await ctx.database.create("sdvx_pin_verified", {
|
|
6744
|
+
uid: session.user.id,
|
|
6745
|
+
cardId: targetCard.id,
|
|
6746
|
+
sid: maoServer.id,
|
|
6747
|
+
pin: pinInput
|
|
6748
|
+
});
|
|
6749
|
+
break;
|
|
6750
|
+
}
|
|
6751
|
+
await session.send(
|
|
6752
|
+
session.text(".pin-verify-failed", {
|
|
6753
|
+
message: ""
|
|
6754
|
+
})
|
|
6755
|
+
);
|
|
6756
|
+
} catch (error) {
|
|
6757
|
+
logger6.warn(error);
|
|
6758
|
+
await session.send(session.text(".pin-verify-error"));
|
|
6759
|
+
}
|
|
6760
|
+
}
|
|
6761
|
+
if (!pinVerified) return session.text(".pin-too-many");
|
|
6762
|
+
}
|
|
6763
|
+
const sourceSdvxService = serverManager.getGameService(
|
|
6764
|
+
sourceServer.type,
|
|
6765
|
+
"sdvx"
|
|
6766
|
+
);
|
|
6767
|
+
if (!sourceSdvxService || typeof sourceSdvxService.getAllScore !== "function") {
|
|
6768
|
+
return session.text(".fetch-error");
|
|
6769
|
+
}
|
|
6770
|
+
let scoreList = [];
|
|
6771
|
+
try {
|
|
6772
|
+
scoreList = await sourceSdvxService.getAllScore(
|
|
6773
|
+
ctx,
|
|
6774
|
+
sourceServer.baseUrl,
|
|
6775
|
+
sourceCard.code,
|
|
6776
|
+
config
|
|
6777
|
+
);
|
|
6778
|
+
} catch (error) {
|
|
6779
|
+
logger6.warn(error);
|
|
6780
|
+
return session.text(".fetch-error");
|
|
6781
|
+
}
|
|
6782
|
+
if (!scoreList || scoreList.length === 0) {
|
|
6783
|
+
return session.text(".no-scores");
|
|
6784
|
+
}
|
|
6785
|
+
const syncPayload = buildSyncPayload(scoreList);
|
|
6786
|
+
await session.send(
|
|
6787
|
+
session.text(".confirm-sync", {
|
|
6788
|
+
score_count: syncPayload.length
|
|
6789
|
+
})
|
|
6790
|
+
);
|
|
6791
|
+
const confirm = await session.prompt();
|
|
6792
|
+
if (!confirm) return session.text("commands.timeout");
|
|
6793
|
+
if (confirm.toLowerCase() !== "y") return session.text(".quit");
|
|
6794
|
+
try {
|
|
6795
|
+
const ok = await maoSdvxService.uploadScore(
|
|
6796
|
+
ctx,
|
|
6797
|
+
maoServer.baseUrl,
|
|
6798
|
+
targetCard.code,
|
|
6799
|
+
syncPayload
|
|
6800
|
+
);
|
|
6801
|
+
if (!ok) {
|
|
6802
|
+
logger6.warn("Mao SDVX uploadScore returned falsy result");
|
|
6803
|
+
return session.text(".sync-failed");
|
|
6804
|
+
}
|
|
6805
|
+
} catch (error) {
|
|
6806
|
+
logger6.warn(error);
|
|
6807
|
+
return session.text(".sync-error");
|
|
6808
|
+
}
|
|
6809
|
+
return session.text(".selected-summary", {
|
|
6810
|
+
source_server_name: sourceServer.name,
|
|
6811
|
+
source_server_type: sourceServer.type,
|
|
6812
|
+
source_card_name: sourceCard.name,
|
|
6813
|
+
target_card_name: targetCard.name,
|
|
6814
|
+
score_count: syncPayload.length
|
|
6815
|
+
});
|
|
6816
|
+
});
|
|
6817
|
+
}
|
|
6818
|
+
__name(sync, "sync");
|
|
6819
|
+
function clearTypeToMark(clearType) {
|
|
6820
|
+
switch (clearType) {
|
|
6821
|
+
case "S-PUC":
|
|
6822
|
+
return "spuc";
|
|
6823
|
+
case "PUC":
|
|
6824
|
+
return "puc";
|
|
6825
|
+
case "UC":
|
|
6826
|
+
return "uc";
|
|
6827
|
+
case "MC":
|
|
6828
|
+
return "mc";
|
|
6829
|
+
case "HC":
|
|
6830
|
+
return "hc";
|
|
6831
|
+
case "NC":
|
|
6832
|
+
return "nc";
|
|
6833
|
+
case "PLAYED":
|
|
6834
|
+
return "played";
|
|
6835
|
+
case "NO PLAY":
|
|
6836
|
+
return "noplay";
|
|
6837
|
+
default:
|
|
6838
|
+
return "noplay";
|
|
6839
|
+
}
|
|
6840
|
+
}
|
|
6841
|
+
__name(clearTypeToMark, "clearTypeToMark");
|
|
6842
|
+
function buildSyncPayload(scoreList) {
|
|
6843
|
+
const payloadMap = /* @__PURE__ */ new Map();
|
|
6844
|
+
for (const score of scoreList) {
|
|
6845
|
+
const musicIdRaw = String(score.music.music_id);
|
|
6846
|
+
if (!/^\d+$/.test(musicIdRaw)) continue;
|
|
6847
|
+
const musicId = musicIdRaw;
|
|
6848
|
+
let diffName = score.music.music_diff_name;
|
|
6849
|
+
if (!diffName) continue;
|
|
6850
|
+
const upperDiff = diffName.toUpperCase();
|
|
6851
|
+
if (["GRV", "HVN", "VVD", "XCD"].includes(upperDiff)) {
|
|
6852
|
+
diffName = "INF";
|
|
6853
|
+
} else {
|
|
6854
|
+
diffName = upperDiff;
|
|
6855
|
+
}
|
|
6856
|
+
if (!payloadMap.has(musicId)) {
|
|
6857
|
+
payloadMap.set(musicId, { music_id: musicId, difficulties: {} });
|
|
6858
|
+
}
|
|
6859
|
+
const entry = payloadMap.get(musicId);
|
|
6860
|
+
entry.difficulties[diffName] = {
|
|
6861
|
+
score: score.music.score,
|
|
6862
|
+
mark: clearTypeToMark(score.music.clear_type),
|
|
6863
|
+
grade: score.music.score_grade
|
|
6864
|
+
};
|
|
6865
|
+
}
|
|
6866
|
+
return Array.from(payloadMap.values());
|
|
6867
|
+
}
|
|
6868
|
+
__name(buildSyncPayload, "buildSyncPayload");
|
|
6869
|
+
|
|
6407
6870
|
// src/games/sdvx/commands/vf.ts
|
|
6408
6871
|
var fs4 = __toESM(require("fs"), 1);
|
|
6409
6872
|
var import_koishi16 = require("koishi");
|
|
@@ -6545,9 +7008,10 @@ function vf(ctx, config, logger6) {
|
|
|
6545
7008
|
const serverService = new ServerService(ctx);
|
|
6546
7009
|
const userCards = await cardService.getCardsByUid(session.user.id);
|
|
6547
7010
|
if (userCards.length === 0) return session.text(".card-not-found");
|
|
6548
|
-
const
|
|
6549
|
-
|
|
6550
|
-
|
|
7011
|
+
const serverRes = await serverService.getSelectableServers(
|
|
7012
|
+
session.user.id,
|
|
7013
|
+
atGuild ? session.channel.id : null
|
|
7014
|
+
);
|
|
6551
7015
|
if (serverRes.length === 0) return session.text(".server-not-found");
|
|
6552
7016
|
let cardCode = "";
|
|
6553
7017
|
if (!options.card) {
|
|
@@ -6685,6 +7149,7 @@ function apply12(ctx, config) {
|
|
|
6685
7149
|
recent(ctx, config, logger5);
|
|
6686
7150
|
chart(ctx, config, logger5);
|
|
6687
7151
|
calculate(ctx, config, logger5);
|
|
7152
|
+
sync(ctx, config, logger5);
|
|
6688
7153
|
}
|
|
6689
7154
|
__name(apply12, "apply");
|
|
6690
7155
|
|
|
@@ -6696,6 +7161,19 @@ __export(database_exports3, {
|
|
|
6696
7161
|
});
|
|
6697
7162
|
var name13 = "database";
|
|
6698
7163
|
function apply13(ctx) {
|
|
7164
|
+
ctx.model.extend(
|
|
7165
|
+
"sdvx_pin_verified",
|
|
7166
|
+
{
|
|
7167
|
+
id: "unsigned",
|
|
7168
|
+
uid: "unsigned",
|
|
7169
|
+
cardId: "unsigned",
|
|
7170
|
+
sid: "unsigned",
|
|
7171
|
+
pin: "string"
|
|
7172
|
+
},
|
|
7173
|
+
{
|
|
7174
|
+
autoInc: true
|
|
7175
|
+
}
|
|
7176
|
+
);
|
|
6699
7177
|
}
|
|
6700
7178
|
__name(apply13, "apply");
|
|
6701
7179
|
|
|
@@ -6711,10 +7189,10 @@ function apply14(ctx) {
|
|
|
6711
7189
|
__name(apply14, "apply");
|
|
6712
7190
|
|
|
6713
7191
|
// src/games/sdvx/locales/en-US.yml
|
|
6714
|
-
var en_US_default5 = { _config: { $desc: "SDVX Module Settings", default_model: "<p>Default model value (e.g. `2024110700`)</p>", sdvx_data_url: "<p>The URL of the SDVX data service</p>", sdvx_search_url: "<p>The URL of the SDVX search service</p>", official_support_url: "<p>The URL of the SDVX official support service</p>" }, commands: { vf: { description: "Show Noah help information", messages: { "card-not-found": "<p>You haven't bound a card yet, go bind a card first~</p>", "server-not-found": "<p>No available servers, add one yourself~</p>", "no-scores-found": "<p>No scores found, please check your data.</p>", error: "<p>An error occurred(っ °Д °;)っ</p>", drawing: "<p>Noah is drawing {name} [{difstr}], please wait patiently~</p>", "menu-select": "<p>Please select the card you want to use:</p>\n{card_list}\n<p>q. Exit</p>", "invalid-select": "<p>No such option!</p>", quit: "<p>Exited~</p>" } }, sdvx: { recent: { description: "Show recent scores", messages: { "card-not-found": "<p>You haven't bound a card yet, go bind a card first~</p>", "server-not-found": "<p>No available servers, add one yourself~</p>", "no-scores-found": "<p>No scores found, please check your data.</p>", error: "<p>An error occurred(っ °Д °;)っ</p>", drawing: "<p>Noah is drawing, please wait patiently~</p>" } }, chart: { description: "Show SDVX chart", messages: { prompt: "<p>Which song's chart would you like to view?</p>", error: "<p>An error occurred(っ °Д °;)っ</p>", drawing: "<p>Noah is drawing, please wait patiently~</p>", "no-result": "<p>Aww, Noah couldn’t find that song~ try another keyword, okay?</p>" } }, calculate: { description: "Calculate volforce value or score", messages: { "invalid-query": "<p>Invalid query parameters, please check your input~</p>", "no-results": "<p>No results found~</p>", "too-many-results": "<p>Too many results! Found {0} results, please narrow down your query (current limit: 500 results)</p>", drawing: "<p>Noah is drawing, please wait patiently~</p>", error: "<p>An error occurred(っ °Д °;)っ</p>" } } } } };
|
|
7192
|
+
var en_US_default5 = { _config: { $desc: "SDVX Module Settings", default_model: "<p>Default model value (e.g. `2024110700`)</p>", sdvx_data_url: "<p>The URL of the SDVX data service</p>", sdvx_search_url: "<p>The URL of the SDVX search service</p>", official_support_url: "<p>The URL of the SDVX official support service</p>" }, commands: { vf: { description: "Show Noah help information", messages: { "card-not-found": "<p>You haven't bound a card yet, go bind a card first~</p>", "server-not-found": "<p>No available servers, add one yourself~</p>", "no-scores-found": "<p>No scores found, please check your data.</p>", error: "<p>An error occurred(っ °Д °;)っ</p>", drawing: "<p>Noah is drawing {name} [{difstr}], please wait patiently~</p>", "menu-select": "<p>Please select the card you want to use:</p>\n{card_list}\n<p>q. Exit</p>", "invalid-select": "<p>No such option!</p>", quit: "<p>Exited~</p>" } }, sdvx: { recent: { description: "Show recent scores", messages: { "card-not-found": "<p>You haven't bound a card yet, go bind a card first~</p>", "server-not-found": "<p>No available servers, add one yourself~</p>", "no-scores-found": "<p>No scores found, please check your data.</p>", error: "<p>An error occurred(っ °Д °;)っ</p>", drawing: "<p>Noah is drawing, please wait patiently~</p>" } }, chart: { description: "Show SDVX chart", messages: { prompt: "<p>Which song's chart would you like to view?</p>", error: "<p>An error occurred(っ °Д °;)っ</p>", drawing: "<p>Noah is drawing, please wait patiently~</p>", "no-result": "<p>Aww, Noah couldn’t find that song~ try another keyword, okay?</p>" } }, calculate: { description: "Calculate volforce value or score", messages: { "invalid-query": "<p>Invalid query parameters, please check your input~</p>", "no-results": "<p>No results found~</p>", "too-many-results": "<p>Too many results! Found {0} results, please narrow down your query (current limit: 500 results)</p>", drawing: "<p>Noah is drawing, please wait patiently~</p>", error: "<p>An error occurred(っ °Д °;)っ</p>" } }, sync: { description: "Sync scores to Mao", messages: { "mao-not-found": "<p>Mao server not found. Please add a Mao server first, then try syncing again.</p>", "source-server-not-found": "<p>No available source servers. Please add a server first.</p>", "source-server-select": "<p>Please choose the source server:</p>\n{server_list}\n<p>q. Exit</p>", "card-not-found": "<p>You haven't bound any card yet. Please bind a card first.</p>", "source-card-select": "<p>Please choose the source card:</p>\n{card_list}\n<p>q. Exit</p>", "target-card-select": "<p>Please choose the target card (sync to Mao):</p>\n{card_list}\n<p>q. Exit</p>", "invalid-select": "<p>No such option!</p>", quit: "<p>Sync cancelled.</p>", "same-card-error": "<p>When the source server is Mao, the source card and target card cannot be the same. Please choose again.</p>", "dm-only": "<p>For security, please use this command in a private chat.</p>", "pin-prompt": "<p>Please enter the Mao PIN (4 digits):</p>\n<p>q. Exit</p>", "pin-invalid": "<p>Invalid PIN format. Please enter 4 digits.</p>", "pin-verify-failed": "<p>PIN verification failed: {message}</p>", "pin-verify-error": "<p>PIN verification error. Please try again later.</p>", "pin-too-many": "<p>Too many PIN attempts. Please try again later.</p>", "fetch-error": "<p>Failed to fetch scores from the source server. Please try again later.</p>", "no-scores": "<p>No scores available for sync.</p>", "confirm-sync": "<p>Found {score_count} scores. Start sync?</p>\n<p>Type y to confirm, any other key to cancel.</p>", "sync-error": "<p>Error occurred during sync(っ °Д °;)っ</p>", "sync-failed": "<p>Sync failed(っ °Д °;)っ</p>", "selected-summary": "<p>Sync completedヾ(≧▽≦*)o</p>\n<p>Source server: {source_server_name} ({source_server_type})</p>\n<p>Source card: {source_card_name}</p>\n<p>Target card: {target_card_name}</p>\n<p>Tracks synced: {score_count}</p>" } } } } };
|
|
6715
7193
|
|
|
6716
7194
|
// src/games/sdvx/locales/zh-CN.yml
|
|
6717
|
-
var zh_CN_default5 = { _config: { $desc: "SDVX 模块设置", default_model: "<p>默认的 model 值(如 `2024110700`)</p>", sdvx_data_url: "<p>SDVX 数据服务的 URL</p>", sdvx_search_url: "<p>SDVX 搜索服务的 URL</p>", official_support_url: "<p>SDVX 官方支持服务的 URL</p>" }, commands: { vf: { description: "查询 SDVX VOLFORCE", messages: { "card-not-found": "<p>你还没绑卡,去绑个卡再来吧~</p>", "server-not-found": "<p>没有可用的服务器哦,自己添加一个吧~</p>", "no-scores-found": "<p>没有找到你的分数数据哦~</p>", error: "<p>Noah 遇到了错误(っ °Д °;)っ</p>", drawing: "<p>Noah 正在画啦,请耐心等待~</p>", "menu-select": "<p>请选择你要使用的卡片:</p>\n{card_list}\n<p>q. 退出</p>", "invalid-select": "<p>没有该选项!</p>", quit: "<p>已退出~</p>" } }, sdvx: { recent: { description: "查询最近分数", messages: { "card-not-found": "<p>你还没绑卡,去绑个卡再来吧~</p>", "server-not-found": "<p>没有可用的服务器哦,自己添加一个吧~</p>", "no-scores-found": "<p>没有找到你的分数数据哦~</p>", error: "<p>Noah 遇到了错误(っ °Д °;)っ</p>", drawing: "<p>Noah 正在画啦,请耐心等待~</p>" } }, chart: { description: "查询 SDVX 谱面", messages: { prompt: "<p>要查哪首歌的铺面呢?</p>", error: "<p>Noah 遇到了错误(っ °Д °;)っ</p>", drawing: "<p>Noah 正在绘制 {name} [{difstr}],请耐心等待~</p>", "no-result": "<p>Noah 没有找到这首歌,换个关键词试试吧~</p>" } }, calculate: { description: "计算 volforce 值或分数", messages: { "invalid-query": "<p>查询参数无效,请检查你的输入~</p>", "no-results": "<p>没有找到符合条件的结果~</p>", "too-many-results": "<p>结果太多啦!共找到 {0} 条结果,请缩小查询范围(当前限制:500 条)</p>", drawing: "<p>Noah 正在画啦,请耐心等待~</p>", error: "<p>Noah 遇到了错误(っ °Д °;)っ</p>" } } } } };
|
|
7195
|
+
var zh_CN_default5 = { _config: { $desc: "SDVX 模块设置", default_model: "<p>默认的 model 值(如 `2024110700`)</p>", sdvx_data_url: "<p>SDVX 数据服务的 URL</p>", sdvx_search_url: "<p>SDVX 搜索服务的 URL</p>", official_support_url: "<p>SDVX 官方支持服务的 URL</p>" }, commands: { vf: { description: "查询 SDVX VOLFORCE", messages: { "card-not-found": "<p>你还没绑卡,去绑个卡再来吧~</p>", "server-not-found": "<p>没有可用的服务器哦,自己添加一个吧~</p>", "no-scores-found": "<p>没有找到你的分数数据哦~</p>", error: "<p>Noah 遇到了错误(っ °Д °;)っ</p>", drawing: "<p>Noah 正在画啦,请耐心等待~</p>", "menu-select": "<p>请选择你要使用的卡片:</p>\n{card_list}\n<p>q. 退出</p>", "invalid-select": "<p>没有该选项!</p>", quit: "<p>已退出~</p>" } }, sdvx: { recent: { description: "查询最近分数", messages: { "card-not-found": "<p>你还没绑卡,去绑个卡再来吧~</p>", "server-not-found": "<p>没有可用的服务器哦,自己添加一个吧~</p>", "no-scores-found": "<p>没有找到你的分数数据哦~</p>", error: "<p>Noah 遇到了错误(っ °Д °;)っ</p>", drawing: "<p>Noah 正在画啦,请耐心等待~</p>" } }, chart: { description: "查询 SDVX 谱面", messages: { prompt: "<p>要查哪首歌的铺面呢?</p>", error: "<p>Noah 遇到了错误(っ °Д °;)っ</p>", drawing: "<p>Noah 正在绘制 {name} [{difstr}],请耐心等待~</p>", "no-result": "<p>Noah 没有找到这首歌,换个关键词试试吧~</p>" } }, calculate: { description: "计算 volforce 值或分数", messages: { "invalid-query": "<p>查询参数无效,请检查你的输入~</p>", "no-results": "<p>没有找到符合条件的结果~</p>", "too-many-results": "<p>结果太多啦!共找到 {0} 条结果,请缩小查询范围(当前限制:500 条)</p>", drawing: "<p>Noah 正在画啦,请耐心等待~</p>", error: "<p>Noah 遇到了错误(っ °Д °;)っ</p>" } }, sync: { description: "同步成绩到猫网", messages: { "mao-not-found": "<p>没有找到猫网服务器,请先添加猫网服务器后再尝试同步。</p>", "source-server-not-found": "<p>没有可作为来源的服务器,请先添加服务器。</p>", "source-server-select": "<p>请选择来源服务器:</p>\n{server_list}\n<p>q. 退出</p>", "card-not-found": "<p>你还没有绑定任何卡片哦~</p>", "source-card-select": "<p>请选择来源卡片:</p>\n{card_list}\n<p>q. 退出</p>", "target-card-select": "<p>请选择目标卡片(同步到猫网):</p>\n{card_list}\n<p>q. 退出</p>", "invalid-select": "<p>没有该选项!</p>", quit: "<p>已退出同步。</p>", "same-card-error": "<p>来源服务器为猫网时,来源卡片和目标卡片不能相同,请重新选择。</p>", "dm-only": "<p>出于安全考虑,请在私聊中使用该指令。</p>", "pin-prompt": "<p>请输入猫网 PIN 码(4 位数字):</p>\n<p>q. 退出</p>", "pin-invalid": "<p>PIN 码格式不正确,请输入 4 位数字。</p>", "pin-verify-failed": "<p>PIN 验证失败:{message}</p>", "pin-verify-error": "<p>PIN 验证时出现错误,请稍后重试。</p>", "pin-too-many": "<p>PIN 验证次数过多,请稍后再试。</p>", "fetch-error": "<p>获取来源服务器成绩失败,请稍后再试。</p>", "no-scores": "<p>没有可同步的成绩。</p>", "confirm-sync": "<p>共找到 {score_count} 条成绩,是否开始同步?</p>\n<p>输入 y 确认,其他任意键取消。</p>", "sync-error": "<p>同步过程中出现了错误(っ °Д °;)っ</p>", "sync-failed": "<p>同步失败(っ °Д °;)っ</p>", "selected-summary": "<p>同步完成ヾ(≧▽≦*)o</p>\n<p>来源服务器:{source_server_name} ({source_server_type})</p>\n<p>来源卡片:{source_card_name}</p>\n<p>目标卡片:{target_card_name}</p>\n<p>同步曲目数量:{score_count}</p>" } } } } };
|
|
6718
7196
|
|
|
6719
7197
|
// src/games/sdvx/index.ts
|
|
6720
7198
|
var name15 = "Noah-SDVX";
|
|
@@ -9,7 +9,7 @@ export declare class SDVXService implements ISDVXService {
|
|
|
9
9
|
static getInstance(logger: Logger): SDVXService;
|
|
10
10
|
/**
|
|
11
11
|
* Get the difficulty string based on the difficulty type number
|
|
12
|
-
* @param diffType Difficulty type
|
|
12
|
+
* @param diffType Difficulty type
|
|
13
13
|
* @returns The difficulty string (novice, advanced, exhaust, infinite, maximum)
|
|
14
14
|
*/
|
|
15
15
|
private getDifficultyString;
|
|
@@ -21,6 +21,23 @@ export declare class SDVXService implements ISDVXService {
|
|
|
21
21
|
* @returns The player ID (e.g., "ED*")
|
|
22
22
|
*/
|
|
23
23
|
getUserName(ctx: Context, url: string, cardId: string): Promise<string>;
|
|
24
|
+
/**
|
|
25
|
+
* 验证猫网 PIN 码
|
|
26
|
+
* @param ctx - Koishi 上下文对象
|
|
27
|
+
* @param url - 猫网基础 URL
|
|
28
|
+
* @param cardId - 卡号
|
|
29
|
+
* @param pin - 四位数字 PIN
|
|
30
|
+
* @returns 验证是否通过
|
|
31
|
+
*/
|
|
32
|
+
verifyPin(ctx: Context, url: string, cardId: string, pin: string): Promise<boolean>;
|
|
33
|
+
uploadScore(ctx: Context, url: string, cardId: string, scorePayload: {
|
|
34
|
+
music_id: string;
|
|
35
|
+
difficulties: Record<string, {
|
|
36
|
+
score: number;
|
|
37
|
+
mark: string;
|
|
38
|
+
grade: string;
|
|
39
|
+
}>;
|
|
40
|
+
}[]): Promise<boolean>;
|
|
24
41
|
getAllScore(ctx: Context, url: string, cardId: string, config: SDVXConfig): Promise<SDVXScore[]>;
|
|
25
42
|
getScore(ctx: Context, url: string, cardId: string, musicId: number, config: SDVXConfig): Promise<SDVXScore>;
|
|
26
43
|
getRecentScores(ctx: Context, url: string, cardId: string, config: SDVXConfig, count?: number): Promise<SDVXScore[]>;
|
package/lib/types/index.d.ts
CHANGED
|
@@ -62,6 +62,31 @@ export interface SDVXService extends GameService {
|
|
|
62
62
|
* @returns 玩家 ID
|
|
63
63
|
*/
|
|
64
64
|
getUserName(ctx: Context, url: string, cardId: string): Promise<string>;
|
|
65
|
+
/**
|
|
66
|
+
* 校验 PIN 码
|
|
67
|
+
* @param ctx - Koishi 上下文对象
|
|
68
|
+
* @param url - API 基础 URL
|
|
69
|
+
* @param cardId - 玩家卡片 ID
|
|
70
|
+
* @param pin - 四位数字 PIN
|
|
71
|
+
* @returns 是否验证通过
|
|
72
|
+
*/
|
|
73
|
+
verifyPin?(ctx: Context, url: string, cardId: string, pin: string): Promise<boolean>;
|
|
74
|
+
/**
|
|
75
|
+
* 上传分数数据
|
|
76
|
+
* @param ctx - Koishi 上下文对象
|
|
77
|
+
* @param url - API 基础 URL
|
|
78
|
+
* @param cardId - 玩家卡片 ID
|
|
79
|
+
* @param scorePayload - 待上传的分数数据
|
|
80
|
+
* @returns 上传是否成功
|
|
81
|
+
*/
|
|
82
|
+
uploadScore?(ctx: Context, url: string, cardId: string, scorePayload: {
|
|
83
|
+
music_id: string;
|
|
84
|
+
difficulties: Record<string, {
|
|
85
|
+
score: number;
|
|
86
|
+
mark: string;
|
|
87
|
+
grade: string;
|
|
88
|
+
}>;
|
|
89
|
+
}[]): Promise<boolean>;
|
|
65
90
|
}
|
|
66
91
|
/**
|
|
67
92
|
* 服务器接口
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "koishi-plugin-noah",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "2.0.1",
|
|
4
4
|
"contributors": [
|
|
5
5
|
"Logthm <logthm@outlook.com>"
|
|
6
6
|
],
|
|
@@ -51,7 +51,7 @@
|
|
|
51
51
|
"@ltxhhz/koishi-plugin-skia-canvas": "^0.0.8",
|
|
52
52
|
"@types/adm-zip": "^0",
|
|
53
53
|
"@types/xml2js": "^0",
|
|
54
|
-
"eslint": "^9.39.
|
|
54
|
+
"eslint": "^9.39.2",
|
|
55
55
|
"eslint-config-prettier": "^10.1.8",
|
|
56
56
|
"eslint-import-resolver-typescript": "^3.10.1",
|
|
57
57
|
"eslint-plugin-prettier": "^5.5.4",
|