koishi-plugin-noah 2.2.3 → 2.3.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/drawer/sdvx/index.d.ts +1 -1
- package/lib/games/sdvx/adapters/asphyxia.d.ts +18 -0
- package/lib/games/sdvx/adapters/index.d.ts +6 -0
- package/lib/games/sdvx/adapters/mao.d.ts +19 -0
- package/lib/games/sdvx/adapters/official.d.ts +14 -0
- package/lib/games/sdvx/command.d.ts +1 -1
- package/lib/games/sdvx/commands/calculate.d.ts +1 -1
- package/lib/games/sdvx/commands/chart.d.ts +1 -1
- package/lib/games/sdvx/commands/info.d.ts +1 -1
- package/lib/games/sdvx/commands/radar.d.ts +1 -1
- package/lib/games/sdvx/commands/recent.d.ts +1 -1
- package/lib/games/sdvx/commands/sync.d.ts +1 -1
- package/lib/games/sdvx/commands/vf.d.ts +1 -1
- package/lib/games/sdvx/config.d.ts +1 -2
- package/lib/games/sdvx/services/music-service.d.ts +1 -1
- package/lib/games/sdvx/types/config.d.ts +10 -0
- package/lib/games/sdvx/types/index.d.ts +16 -0
- package/lib/index.cjs +1310 -1370
- package/lib/servers/Asphyxia/index.d.ts +0 -4
- package/lib/servers/Asphyxia/utils/xml.d.ts +2 -0
- package/lib/servers/Mao/index.d.ts +0 -4
- package/lib/servers/Official/index.d.ts +0 -4
- package/lib/servers/index.d.ts +4 -17
- package/lib/types/config.d.ts +2 -12
- package/lib/types/index.d.ts +0 -67
- package/package.json +1 -1
- package/lib/servers/Asphyxia/services/sdvx-service.d.ts +0 -26
- package/lib/servers/Mao/services/sdvx-service.d.ts +0 -39
- package/lib/servers/Official/services/sdvx-service.d.ts +0 -25
- package/lib/servers/utils/grade.d.ts +0 -8
- package/lib/servers/utils/xml.d.ts +0 -13
- /package/lib/{servers → games/sdvx}/utils/difficulty.d.ts +0 -0
package/lib/index.cjs
CHANGED
|
@@ -3265,7 +3265,8 @@ var SDVXDrawer = class extends BaseDrawer {
|
|
|
3265
3265
|
3: "GRV",
|
|
3266
3266
|
4: "HVN",
|
|
3267
3267
|
5: "VVD",
|
|
3268
|
-
6: "XCD"
|
|
3268
|
+
6: "XCD",
|
|
3269
|
+
7: "NBL"
|
|
3269
3270
|
};
|
|
3270
3271
|
diffKey = infVerNum != null && infMap[infVerNum] ? infMap[infVerNum] : "INF";
|
|
3271
3272
|
}
|
|
@@ -4140,333 +4141,308 @@ __export(sdvx_exports, {
|
|
|
4140
4141
|
});
|
|
4141
4142
|
var import_koishi18 = require("koishi");
|
|
4142
4143
|
|
|
4143
|
-
// src/
|
|
4144
|
-
var
|
|
4145
|
-
__export(
|
|
4146
|
-
|
|
4147
|
-
|
|
4144
|
+
// src/servers/index.ts
|
|
4145
|
+
var servers_exports = {};
|
|
4146
|
+
__export(servers_exports, {
|
|
4147
|
+
ServerManager: () => ServerManager,
|
|
4148
|
+
apply: () => apply9,
|
|
4149
|
+
inject: () => inject2,
|
|
4150
|
+
name: () => name9
|
|
4148
4151
|
});
|
|
4149
4152
|
|
|
4150
|
-
// src/
|
|
4153
|
+
// src/servers/Asphyxia/index.ts
|
|
4151
4154
|
var import_koishi11 = require("koishi");
|
|
4152
|
-
|
|
4153
|
-
|
|
4154
|
-
|
|
4155
|
-
const num = parseInt(str, 10);
|
|
4156
|
-
if (str.endsWith("w")) {
|
|
4157
|
-
return num * 1e4;
|
|
4158
|
-
} else if (str.endsWith("k")) {
|
|
4159
|
-
return num * 1e3;
|
|
4160
|
-
}
|
|
4161
|
-
return num;
|
|
4162
|
-
}
|
|
4163
|
-
__name(parseNumberWithSuffix, "parseNumberWithSuffix");
|
|
4164
|
-
function isValidLevel(level) {
|
|
4165
|
-
if (level < 1 || level > 20.9) {
|
|
4166
|
-
return false;
|
|
4155
|
+
var Asphyxia = class {
|
|
4156
|
+
static {
|
|
4157
|
+
__name(this, "Asphyxia");
|
|
4167
4158
|
}
|
|
4168
|
-
|
|
4169
|
-
|
|
4170
|
-
|
|
4171
|
-
|
|
4159
|
+
name = "asphyxia";
|
|
4160
|
+
supportedGames = ["sdvx", "iidx"];
|
|
4161
|
+
gameServices = {};
|
|
4162
|
+
logger = new import_koishi11.Logger("Noah-Asphyxia");
|
|
4163
|
+
};
|
|
4164
|
+
|
|
4165
|
+
// src/servers/Mao/index.ts
|
|
4166
|
+
var import_koishi12 = require("koishi");
|
|
4167
|
+
var Mao = class {
|
|
4168
|
+
static {
|
|
4169
|
+
__name(this, "Mao");
|
|
4172
4170
|
}
|
|
4173
|
-
|
|
4174
|
-
|
|
4171
|
+
name = "mao";
|
|
4172
|
+
supportedGames = ["sdvx"];
|
|
4173
|
+
gameServices = {};
|
|
4174
|
+
logger = new import_koishi12.Logger("Noah-Mao");
|
|
4175
|
+
};
|
|
4176
|
+
|
|
4177
|
+
// src/servers/Official/index.ts
|
|
4178
|
+
var import_koishi13 = require("koishi");
|
|
4179
|
+
var Official = class {
|
|
4180
|
+
static {
|
|
4181
|
+
__name(this, "Official");
|
|
4175
4182
|
}
|
|
4176
|
-
|
|
4177
|
-
|
|
4178
|
-
|
|
4183
|
+
name = "official";
|
|
4184
|
+
supportedGames = ["sdvx"];
|
|
4185
|
+
gameServices = {};
|
|
4186
|
+
logger = new import_koishi13.Logger("Noah-Official");
|
|
4187
|
+
};
|
|
4188
|
+
|
|
4189
|
+
// src/servers/ServerFactory.ts
|
|
4190
|
+
var ServerFactory = class {
|
|
4191
|
+
static {
|
|
4192
|
+
__name(this, "ServerFactory");
|
|
4179
4193
|
}
|
|
4180
|
-
|
|
4181
|
-
|
|
4182
|
-
|
|
4183
|
-
|
|
4184
|
-
|
|
4185
|
-
|
|
4186
|
-
|
|
4187
|
-
|
|
4188
|
-
|
|
4189
|
-
|
|
4190
|
-
|
|
4191
|
-
|
|
4192
|
-
|
|
4193
|
-
|
|
4194
|
-
|
|
4195
|
-
|
|
4196
|
-
|
|
4197
|
-
|
|
4198
|
-
|
|
4199
|
-
|
|
4200
|
-
|
|
4201
|
-
|
|
4202
|
-
levels.push(17.5);
|
|
4203
|
-
}
|
|
4204
|
-
} else if (intPart >= 18 && intPart <= 20) {
|
|
4205
|
-
const currentStartTenth = intPart === startInt ? startTenth : 0;
|
|
4206
|
-
const currentEndTenth = intPart === endInt ? endTenth : 9;
|
|
4207
|
-
for (let tenth = currentStartTenth; tenth <= currentEndTenth && tenth <= 9; tenth++) {
|
|
4208
|
-
levels.push(intPart + tenth / 10);
|
|
4194
|
+
serverInstances = /* @__PURE__ */ new Map();
|
|
4195
|
+
/**
|
|
4196
|
+
* 根据服务器类型获取服务器实例,如果不存在则创建新实例
|
|
4197
|
+
* @param serverType - 服务器类型
|
|
4198
|
+
* @returns 对应的服务器实例
|
|
4199
|
+
* @throws 当请求不支持的服务器类型时抛出异常
|
|
4200
|
+
*/
|
|
4201
|
+
getServer(serverType) {
|
|
4202
|
+
if (!this.serverInstances.has(serverType)) {
|
|
4203
|
+
let serverInstance;
|
|
4204
|
+
switch (serverType) {
|
|
4205
|
+
case "asphyxia":
|
|
4206
|
+
serverInstance = new Asphyxia();
|
|
4207
|
+
break;
|
|
4208
|
+
case "mao":
|
|
4209
|
+
serverInstance = new Mao();
|
|
4210
|
+
break;
|
|
4211
|
+
case "official":
|
|
4212
|
+
serverInstance = new Official();
|
|
4213
|
+
break;
|
|
4214
|
+
default:
|
|
4215
|
+
throw new Error(`Unsupported server type: ${serverType}`);
|
|
4209
4216
|
}
|
|
4217
|
+
this.serverInstances.set(serverType, serverInstance);
|
|
4210
4218
|
}
|
|
4219
|
+
return this.serverInstances.get(serverType);
|
|
4211
4220
|
}
|
|
4212
|
-
|
|
4213
|
-
|
|
4214
|
-
|
|
4215
|
-
|
|
4216
|
-
|
|
4217
|
-
|
|
4218
|
-
|
|
4219
|
-
|
|
4220
|
-
if (start >= 1 && end <= 20.9 && start <= end) {
|
|
4221
|
-
if (!isValidLevel(start) || !isValidLevel(end)) {
|
|
4222
|
-
return null;
|
|
4223
|
-
}
|
|
4224
|
-
return generateValidLevelRange(start, end);
|
|
4225
|
-
}
|
|
4221
|
+
};
|
|
4222
|
+
|
|
4223
|
+
// src/servers/index.ts
|
|
4224
|
+
var name9 = "Noah-Server";
|
|
4225
|
+
var inject2 = ["globalConfig"];
|
|
4226
|
+
var ServerManager = class _ServerManager {
|
|
4227
|
+
static {
|
|
4228
|
+
__name(this, "ServerManager");
|
|
4226
4229
|
}
|
|
4227
|
-
|
|
4228
|
-
|
|
4229
|
-
|
|
4230
|
-
|
|
4231
|
-
|
|
4230
|
+
static instance;
|
|
4231
|
+
factory;
|
|
4232
|
+
externalServices = /* @__PURE__ */ new Map();
|
|
4233
|
+
constructor() {
|
|
4234
|
+
this.factory = new ServerFactory();
|
|
4232
4235
|
}
|
|
4233
|
-
|
|
4234
|
-
|
|
4235
|
-
|
|
4236
|
-
function parseScoreRange(item) {
|
|
4237
|
-
if (/^\d+[wk]?-\d+[wk]?$/.test(item)) {
|
|
4238
|
-
const [startRaw, endRaw] = item.split("-");
|
|
4239
|
-
const start = parseNumberWithSuffix(startRaw);
|
|
4240
|
-
const end = parseNumberWithSuffix(endRaw);
|
|
4241
|
-
if (start >= 0 && end <= 1e7 && start <= end) {
|
|
4242
|
-
return [start, end];
|
|
4236
|
+
static getInstance() {
|
|
4237
|
+
if (!_ServerManager.instance) {
|
|
4238
|
+
_ServerManager.instance = new _ServerManager();
|
|
4243
4239
|
}
|
|
4240
|
+
return _ServerManager.instance;
|
|
4244
4241
|
}
|
|
4245
|
-
|
|
4246
|
-
|
|
4247
|
-
const hasSuffix = /[wk]$/.test(item);
|
|
4248
|
-
if (score >= 0 && score <= 1e7 && (score >= 100 || hasSuffix)) {
|
|
4249
|
-
return [score];
|
|
4250
|
-
}
|
|
4242
|
+
getServer(serverType) {
|
|
4243
|
+
return this.factory.getServer(serverType);
|
|
4251
4244
|
}
|
|
4252
|
-
|
|
4253
|
-
}
|
|
4254
|
-
|
|
4255
|
-
function parseVfValue(item) {
|
|
4256
|
-
if (/^\d{1,2}\.\d{2}-\d{1,2}\.\d{2}$/.test(item)) {
|
|
4257
|
-
const [start, end] = item.split("-").map(parseFloat);
|
|
4258
|
-
if (start >= 0 && end <= 99 && start <= end) {
|
|
4259
|
-
return [start, end];
|
|
4260
|
-
}
|
|
4245
|
+
registerGameService(serverType, gameType, service) {
|
|
4246
|
+
const key = `${serverType}:${gameType}`;
|
|
4247
|
+
this.externalServices.set(key, service);
|
|
4261
4248
|
}
|
|
4262
|
-
|
|
4263
|
-
const
|
|
4264
|
-
|
|
4265
|
-
return vf2;
|
|
4266
|
-
}
|
|
4249
|
+
getGameService(serverType, gameType) {
|
|
4250
|
+
const key = `${serverType}:${gameType}`;
|
|
4251
|
+
return this.externalServices.get(key);
|
|
4267
4252
|
}
|
|
4268
|
-
|
|
4253
|
+
};
|
|
4254
|
+
function apply9(ctx, config) {
|
|
4269
4255
|
}
|
|
4270
|
-
__name(
|
|
4271
|
-
|
|
4272
|
-
|
|
4273
|
-
|
|
4274
|
-
|
|
4275
|
-
|
|
4276
|
-
|
|
4277
|
-
if (
|
|
4278
|
-
|
|
4279
|
-
|
|
4280
|
-
|
|
4281
|
-
for (let i = from; i <= to; i++) {
|
|
4282
|
-
clearTypes.push(ALL_CLEAR_TYPES[i]);
|
|
4283
|
-
}
|
|
4284
|
-
if (clearTypes.includes("PUC") && !clearTypes.includes("S-PUC")) {
|
|
4285
|
-
clearTypes.push("S-PUC");
|
|
4286
|
-
}
|
|
4287
|
-
return clearTypes;
|
|
4256
|
+
__name(apply9, "apply");
|
|
4257
|
+
|
|
4258
|
+
// src/servers/Asphyxia/utils/xml.ts
|
|
4259
|
+
var xml2js = __toESM(require("xml2js"), 1);
|
|
4260
|
+
var xmlToJson = /* @__PURE__ */ __name((xml) => {
|
|
4261
|
+
return new Promise((resolve3, reject) => {
|
|
4262
|
+
xml2js.parseString(xml, (err, result) => {
|
|
4263
|
+
if (err) {
|
|
4264
|
+
reject(new Error("Error parsing XML: " + err));
|
|
4265
|
+
} else {
|
|
4266
|
+
resolve3(result);
|
|
4288
4267
|
}
|
|
4289
|
-
}
|
|
4268
|
+
});
|
|
4269
|
+
});
|
|
4270
|
+
}, "xmlToJson");
|
|
4271
|
+
|
|
4272
|
+
// src/games/sdvx/services/music-service.ts
|
|
4273
|
+
var MusicService = class _MusicService {
|
|
4274
|
+
static {
|
|
4275
|
+
__name(this, "MusicService");
|
|
4290
4276
|
}
|
|
4291
|
-
|
|
4292
|
-
|
|
4293
|
-
|
|
4294
|
-
|
|
4295
|
-
|
|
4296
|
-
|
|
4297
|
-
|
|
4298
|
-
|
|
4277
|
+
static instance;
|
|
4278
|
+
sdvx_data_url;
|
|
4279
|
+
sdvx_search_url;
|
|
4280
|
+
constructor(config) {
|
|
4281
|
+
this.sdvx_data_url = config.sdvx_data_url;
|
|
4282
|
+
this.sdvx_search_url = config.sdvx_search_url;
|
|
4283
|
+
}
|
|
4284
|
+
parseDifnum(value) {
|
|
4285
|
+
if (typeof value === "number") {
|
|
4286
|
+
return Number.isFinite(value) ? value : 0;
|
|
4299
4287
|
}
|
|
4300
|
-
|
|
4288
|
+
if (typeof value === "string") {
|
|
4289
|
+
const parsed = Number.parseFloat(value.trim());
|
|
4290
|
+
return Number.isFinite(parsed) ? parsed : 0;
|
|
4291
|
+
}
|
|
4292
|
+
return 0;
|
|
4301
4293
|
}
|
|
4302
|
-
|
|
4303
|
-
|
|
4304
|
-
|
|
4305
|
-
|
|
4306
|
-
|
|
4307
|
-
|
|
4308
|
-
|
|
4309
|
-
|
|
4310
|
-
|
|
4311
|
-
const from = Math.min(start, end);
|
|
4312
|
-
const to = Math.max(start, end);
|
|
4313
|
-
const grades = [];
|
|
4314
|
-
for (let i = from; i <= to; i++) {
|
|
4315
|
-
grades.push(ALL_GRADES[i]);
|
|
4316
|
-
}
|
|
4317
|
-
return grades;
|
|
4318
|
-
}
|
|
4319
|
-
}
|
|
4320
|
-
return null;
|
|
4321
|
-
}
|
|
4322
|
-
__name(parseGradeRange, "parseGradeRange");
|
|
4323
|
-
function parseSingleGrade(item) {
|
|
4324
|
-
const grade = ALL_GRADES.find((g) => g.toLowerCase() === item.toLowerCase());
|
|
4325
|
-
if (grade) {
|
|
4326
|
-
return [grade];
|
|
4327
|
-
}
|
|
4328
|
-
return null;
|
|
4329
|
-
}
|
|
4330
|
-
__name(parseSingleGrade, "parseSingleGrade");
|
|
4331
|
-
function parseRadarFeature(item) {
|
|
4332
|
-
const radarList = ["notes", "peak", "tsumami", "tricky", "hand_trip", "one_hand"];
|
|
4333
|
-
if (radarList.includes(item)) {
|
|
4334
|
-
return item;
|
|
4294
|
+
normalizeMusicDifnum(music) {
|
|
4295
|
+
if (!music?.difficulty?.length) return music;
|
|
4296
|
+
return {
|
|
4297
|
+
...music,
|
|
4298
|
+
difficulty: music.difficulty.map((d) => ({
|
|
4299
|
+
...d,
|
|
4300
|
+
difnum: this.parseDifnum(d.difnum)
|
|
4301
|
+
}))
|
|
4302
|
+
};
|
|
4335
4303
|
}
|
|
4336
|
-
|
|
4337
|
-
|
|
4338
|
-
|
|
4339
|
-
|
|
4340
|
-
|
|
4341
|
-
|
|
4342
|
-
|
|
4304
|
+
/**
|
|
4305
|
+
* 获取 MusicService 实例
|
|
4306
|
+
* @param config - SDVX 配置对象
|
|
4307
|
+
* @returns MusicService 实例
|
|
4308
|
+
*/
|
|
4309
|
+
static getInstance(config) {
|
|
4310
|
+
if (!_MusicService.instance) {
|
|
4311
|
+
_MusicService.instance = new _MusicService(config);
|
|
4312
|
+
}
|
|
4313
|
+
return _MusicService.instance;
|
|
4343
4314
|
}
|
|
4344
|
-
|
|
4345
|
-
|
|
4346
|
-
|
|
4347
|
-
|
|
4348
|
-
|
|
4349
|
-
|
|
4350
|
-
|
|
4351
|
-
|
|
4352
|
-
|
|
4353
|
-
|
|
4315
|
+
/**
|
|
4316
|
+
* 获取音乐信息(支持数字和字符串 ID)
|
|
4317
|
+
* @param ctx - Koishi 上下文对象
|
|
4318
|
+
* @param musicIds - 音乐 ID 数组(数字或字符串)
|
|
4319
|
+
* @returns 音乐信息数组
|
|
4320
|
+
*/
|
|
4321
|
+
async getMusic(ctx, musicIds) {
|
|
4322
|
+
const numericIds = musicIds.filter((id) => typeof id === "number");
|
|
4323
|
+
const stringTitles = musicIds.filter((id) => typeof id === "string");
|
|
4324
|
+
const numericMap = /* @__PURE__ */ new Map();
|
|
4325
|
+
const stringMap = /* @__PURE__ */ new Map();
|
|
4326
|
+
if (numericIds.length > 0) {
|
|
4327
|
+
try {
|
|
4328
|
+
const response = await ctx.http.post(
|
|
4329
|
+
`/music`,
|
|
4330
|
+
{ ids: numericIds },
|
|
4331
|
+
{ baseURL: this.sdvx_data_url }
|
|
4332
|
+
);
|
|
4333
|
+
if (Array.isArray(response)) {
|
|
4334
|
+
for (const m of response) {
|
|
4335
|
+
const normalized = this.normalizeMusicDifnum(m);
|
|
4336
|
+
numericMap.set(Number(normalized.id), normalized);
|
|
4337
|
+
}
|
|
4338
|
+
}
|
|
4339
|
+
} catch (error) {
|
|
4340
|
+
console.warn("Failed to fetch music data for numeric IDs:", error);
|
|
4354
4341
|
}
|
|
4355
|
-
continue;
|
|
4356
4342
|
}
|
|
4357
|
-
|
|
4358
|
-
|
|
4359
|
-
|
|
4360
|
-
|
|
4361
|
-
|
|
4362
|
-
|
|
4363
|
-
|
|
4343
|
+
if (stringTitles.length > 0) {
|
|
4344
|
+
try {
|
|
4345
|
+
const response = await ctx.http.post(
|
|
4346
|
+
`/external/music`,
|
|
4347
|
+
{ titles: stringTitles },
|
|
4348
|
+
{ baseURL: this.sdvx_data_url }
|
|
4349
|
+
);
|
|
4350
|
+
const converted = this.convertExternalToStandard(response);
|
|
4351
|
+
for (const m of converted) {
|
|
4352
|
+
const normalized = this.normalizeMusicDifnum(m);
|
|
4353
|
+
stringMap.set(String(normalized.id), normalized);
|
|
4354
|
+
stringMap.set(normalized.title_name, normalized);
|
|
4355
|
+
}
|
|
4356
|
+
} catch (error) {
|
|
4357
|
+
console.warn("Failed to fetch music data for string IDs:", error);
|
|
4364
4358
|
}
|
|
4365
|
-
continue;
|
|
4366
4359
|
}
|
|
4367
|
-
const
|
|
4368
|
-
|
|
4369
|
-
|
|
4370
|
-
|
|
4371
|
-
params.score = [.../* @__PURE__ */ new Set([...existing, ...scores])];
|
|
4372
|
-
} else {
|
|
4373
|
-
params.score = scores.length === 1 ? scores[0] : scores;
|
|
4374
|
-
}
|
|
4375
|
-
continue;
|
|
4360
|
+
const results = [];
|
|
4361
|
+
for (const id of musicIds) {
|
|
4362
|
+
const music = typeof id === "number" ? numericMap.get(id) : stringMap.get(id);
|
|
4363
|
+
if (music) results.push(music);
|
|
4376
4364
|
}
|
|
4377
|
-
|
|
4378
|
-
|
|
4379
|
-
|
|
4380
|
-
|
|
4381
|
-
|
|
4382
|
-
|
|
4383
|
-
|
|
4365
|
+
return results;
|
|
4366
|
+
}
|
|
4367
|
+
/**
|
|
4368
|
+
* 转换外部音乐数据到标准格式
|
|
4369
|
+
* @param externalData - 外部音乐数据数组
|
|
4370
|
+
* @returns 标准格式音乐数据数组
|
|
4371
|
+
*/
|
|
4372
|
+
convertExternalToStandard(externalData) {
|
|
4373
|
+
return externalData.map((data) => ({
|
|
4374
|
+
id: data.slug,
|
|
4375
|
+
title_name: data.ext_title_name,
|
|
4376
|
+
artist_name: data.ext_artist_name,
|
|
4377
|
+
genre: data.genre,
|
|
4378
|
+
genre_name: data.genre_name,
|
|
4379
|
+
inf_ver: data.difficulty[0]?.inf_ver || 0,
|
|
4380
|
+
difficulty: data.difficulty.map((diff) => ({
|
|
4381
|
+
difstr: diff.difstr,
|
|
4382
|
+
difnum: this.parseDifnum(diff.difnum),
|
|
4383
|
+
illustrator: diff.ext_illustrator,
|
|
4384
|
+
effected_by: diff.ext_effected_by,
|
|
4385
|
+
cover_url: diff.cover_url,
|
|
4386
|
+
inf_ver: diff.inf_ver
|
|
4387
|
+
}))
|
|
4388
|
+
}));
|
|
4389
|
+
}
|
|
4390
|
+
/**
|
|
4391
|
+
* 根据曲名查询歌曲
|
|
4392
|
+
* @param ctx - Koishi 上下文对象
|
|
4393
|
+
* @param query - 查询字符串
|
|
4394
|
+
* @returns 音乐信息数组
|
|
4395
|
+
*/
|
|
4396
|
+
async searchMusic(ctx, query) {
|
|
4397
|
+
try {
|
|
4398
|
+
const response = await ctx.http.get(
|
|
4399
|
+
`/search?query=${encodeURIComponent(query)}&size=50`,
|
|
4400
|
+
{
|
|
4401
|
+
baseURL: this.sdvx_search_url
|
|
4402
|
+
}
|
|
4403
|
+
);
|
|
4404
|
+
const results = response?.results;
|
|
4405
|
+
if (!Array.isArray(results) || results.length === 0) {
|
|
4406
|
+
return null;
|
|
4384
4407
|
}
|
|
4385
|
-
|
|
4386
|
-
|
|
4387
|
-
|
|
4388
|
-
if (singleClearTypes !== null) {
|
|
4389
|
-
if (params.clearType) {
|
|
4390
|
-
const existing = Array.isArray(params.clearType) ? params.clearType : [params.clearType];
|
|
4391
|
-
params.clearType = [.../* @__PURE__ */ new Set([...existing, ...singleClearTypes])];
|
|
4392
|
-
} else {
|
|
4393
|
-
params.clearType = singleClearTypes;
|
|
4408
|
+
const musicIds = results.map((item) => Number.isInteger(item.id) ? item.id : item.external_key).filter((v) => v != null);
|
|
4409
|
+
if (musicIds.length === 0) {
|
|
4410
|
+
return null;
|
|
4394
4411
|
}
|
|
4395
|
-
|
|
4412
|
+
return await this.getMusic(ctx, musicIds);
|
|
4413
|
+
} catch {
|
|
4414
|
+
return null;
|
|
4396
4415
|
}
|
|
4397
|
-
|
|
4398
|
-
|
|
4399
|
-
|
|
4400
|
-
|
|
4401
|
-
|
|
4402
|
-
|
|
4403
|
-
|
|
4416
|
+
}
|
|
4417
|
+
async extTitleToMid(ctx, titles) {
|
|
4418
|
+
try {
|
|
4419
|
+
const response = await ctx.http.post(
|
|
4420
|
+
`/external/title-mapping`,
|
|
4421
|
+
{ titles },
|
|
4422
|
+
{ baseURL: this.sdvx_data_url }
|
|
4423
|
+
);
|
|
4424
|
+
if (!Array.isArray(response)) {
|
|
4425
|
+
return [];
|
|
4404
4426
|
}
|
|
4405
|
-
|
|
4406
|
-
|
|
4407
|
-
|
|
4408
|
-
|
|
4409
|
-
|
|
4410
|
-
|
|
4411
|
-
|
|
4412
|
-
|
|
4413
|
-
|
|
4427
|
+
const mapping = new Map(
|
|
4428
|
+
response.map((item) => [
|
|
4429
|
+
item?.title?.toLowerCase(),
|
|
4430
|
+
Number.isInteger(item?.music_id) ? String(item.music_id) : null
|
|
4431
|
+
])
|
|
4432
|
+
);
|
|
4433
|
+
const mids = [];
|
|
4434
|
+
for (const title of titles) {
|
|
4435
|
+
const mid = mapping.get(title.toLowerCase()) ?? null;
|
|
4436
|
+
mids.push({ title, mid });
|
|
4414
4437
|
}
|
|
4415
|
-
|
|
4438
|
+
return mids;
|
|
4439
|
+
} catch {
|
|
4440
|
+
return [];
|
|
4416
4441
|
}
|
|
4417
4442
|
}
|
|
4418
|
-
|
|
4419
|
-
}
|
|
4420
|
-
__name(parseQueryInput, "parseQueryInput");
|
|
4443
|
+
};
|
|
4421
4444
|
|
|
4422
|
-
// src/games/sdvx/
|
|
4423
|
-
function calculate(ctx, config, logger5) {
|
|
4424
|
-
ctx.command("sdvx.calculate <query:text>").alias("sdvx.cal").action(async ({ session, options }, query) => {
|
|
4425
|
-
try {
|
|
4426
|
-
let queryInput = query;
|
|
4427
|
-
if (!queryInput) {
|
|
4428
|
-
await session.send(session.text(".prompt"));
|
|
4429
|
-
queryInput = await session.prompt();
|
|
4430
|
-
if (!queryInput) return session.text("commands.timeout");
|
|
4431
|
-
if (queryInput === "q") return session.text(".quit");
|
|
4432
|
-
}
|
|
4433
|
-
const params = parseQueryInput(queryInput);
|
|
4434
|
-
if (Object.keys(params).length === 0) {
|
|
4435
|
-
return session.text(".invalid-query");
|
|
4436
|
-
}
|
|
4437
|
-
const results = generateQueryResults(params);
|
|
4438
|
-
if (results.length === 0) {
|
|
4439
|
-
return session.text(".no-results");
|
|
4440
|
-
}
|
|
4441
|
-
const maxResults = 500;
|
|
4442
|
-
if (results.length > maxResults) {
|
|
4443
|
-
return session.text(".too-many-results", [results.length]);
|
|
4444
|
-
}
|
|
4445
|
-
session.send(session.text(".drawing"));
|
|
4446
|
-
const drawerManager = DrawerManager.getInstance(ctx);
|
|
4447
|
-
const sdvxDrawer = drawerManager.getDrawer("sdvx");
|
|
4448
|
-
const imageBuffer = await sdvxDrawer.generateVFTableImage(
|
|
4449
|
-
{
|
|
4450
|
-
results,
|
|
4451
|
-
config
|
|
4452
|
-
},
|
|
4453
|
-
{
|
|
4454
|
-
lossless: true
|
|
4455
|
-
}
|
|
4456
|
-
);
|
|
4457
|
-
return import_koishi11.h.image(imageBuffer, "image/png");
|
|
4458
|
-
} catch (error) {
|
|
4459
|
-
ctx.logger("SDVX-Calculate").warn(error);
|
|
4460
|
-
return session.text(".error");
|
|
4461
|
-
}
|
|
4462
|
-
});
|
|
4463
|
-
}
|
|
4464
|
-
__name(calculate, "calculate");
|
|
4465
|
-
|
|
4466
|
-
// src/games/sdvx/commands/chart.ts
|
|
4467
|
-
var import_koishi12 = require("koishi");
|
|
4468
|
-
|
|
4469
|
-
// src/servers/utils/difficulty.ts
|
|
4445
|
+
// src/games/sdvx/utils/difficulty.ts
|
|
4470
4446
|
function getDiffName(diffStr, infVer) {
|
|
4471
4447
|
switch (diffStr) {
|
|
4472
4448
|
case "novice":
|
|
@@ -4489,6 +4465,8 @@ function getDiffName(diffStr, infVer) {
|
|
|
4489
4465
|
return "VVD";
|
|
4490
4466
|
case "6":
|
|
4491
4467
|
return "XCD";
|
|
4468
|
+
case "7":
|
|
4469
|
+
return "NBL";
|
|
4492
4470
|
}
|
|
4493
4471
|
}
|
|
4494
4472
|
break;
|
|
@@ -4521,6 +4499,8 @@ function getDiffFullName(diffStr, infVer) {
|
|
|
4521
4499
|
return "VIVID";
|
|
4522
4500
|
case "6":
|
|
4523
4501
|
return "EXCEED";
|
|
4502
|
+
case "7":
|
|
4503
|
+
return "NABLA";
|
|
4524
4504
|
}
|
|
4525
4505
|
}
|
|
4526
4506
|
break;
|
|
@@ -4550,6 +4530,8 @@ function getDiffStringFromAbbr(diffAbbr) {
|
|
|
4550
4530
|
return { diffStr: "infinite", infVer: 5 };
|
|
4551
4531
|
case "XCD":
|
|
4552
4532
|
return { diffStr: "infinite", infVer: 6 };
|
|
4533
|
+
case "NBL":
|
|
4534
|
+
return { diffStr: "infinite", infVer: 7 };
|
|
4553
4535
|
case "MXM":
|
|
4554
4536
|
return { diffStr: "maximum", infVer: null };
|
|
4555
4537
|
case "ULT":
|
|
@@ -4560,531 +4542,449 @@ function getDiffStringFromAbbr(diffAbbr) {
|
|
|
4560
4542
|
}
|
|
4561
4543
|
__name(getDiffStringFromAbbr, "getDiffStringFromAbbr");
|
|
4562
4544
|
|
|
4563
|
-
// src/games/sdvx/
|
|
4564
|
-
var
|
|
4545
|
+
// src/games/sdvx/adapters/asphyxia.ts
|
|
4546
|
+
var AsphyxiaSDVXService = class _AsphyxiaSDVXService {
|
|
4565
4547
|
static {
|
|
4566
|
-
__name(this, "
|
|
4548
|
+
__name(this, "AsphyxiaSDVXService");
|
|
4567
4549
|
}
|
|
4568
4550
|
static instance;
|
|
4569
|
-
|
|
4570
|
-
|
|
4571
|
-
|
|
4572
|
-
|
|
4573
|
-
this.
|
|
4551
|
+
logger;
|
|
4552
|
+
config;
|
|
4553
|
+
cachedModel = null;
|
|
4554
|
+
constructor(logger5, config) {
|
|
4555
|
+
this.logger = logger5;
|
|
4556
|
+
this.config = config;
|
|
4574
4557
|
}
|
|
4575
|
-
|
|
4576
|
-
if (
|
|
4577
|
-
|
|
4558
|
+
static getInstance(logger5, config) {
|
|
4559
|
+
if (!_AsphyxiaSDVXService.instance) {
|
|
4560
|
+
_AsphyxiaSDVXService.instance = new _AsphyxiaSDVXService(logger5, config);
|
|
4578
4561
|
}
|
|
4579
|
-
|
|
4580
|
-
|
|
4581
|
-
|
|
4562
|
+
return _AsphyxiaSDVXService.instance;
|
|
4563
|
+
}
|
|
4564
|
+
async getModel(ctx) {
|
|
4565
|
+
if (this.cachedModel) return this.cachedModel;
|
|
4566
|
+
const resp = await ctx.http.get(`${this.config.sdvx_data_url}/model`);
|
|
4567
|
+
this.cachedModel = String(resp.model);
|
|
4568
|
+
return this.cachedModel;
|
|
4569
|
+
}
|
|
4570
|
+
async getRefid(ctx, url, cardId) {
|
|
4571
|
+
const model = await this.getModel(ctx);
|
|
4572
|
+
const requestUrl = `?model=KFC:J:G:A:${model}&f=message.get`;
|
|
4573
|
+
const xmlRequestBody = `<?xml version="1.0" encoding="UTF-8"?>
|
|
4574
|
+
<call model="KFC:J:G:A:${model}" srcid="00010203040506070809">
|
|
4575
|
+
<cardmng cardid="${cardId}" cardtype="2" method="inquire" update="0" />
|
|
4576
|
+
</call>`;
|
|
4577
|
+
const response = await ctx.http.post(requestUrl, xmlRequestBody, { baseURL: url });
|
|
4578
|
+
const decodedResponse = typeof response === "string" ? response : new TextDecoder("utf-8").decode(response);
|
|
4579
|
+
const jsonResponse = await xmlToJson(decodedResponse);
|
|
4580
|
+
const cardmng = jsonResponse.response.cardmng?.[0]?.$;
|
|
4581
|
+
if (!cardmng || !cardmng.refid) throw new Error("refid not found");
|
|
4582
|
+
return cardmng.refid;
|
|
4583
|
+
}
|
|
4584
|
+
async getUserName(ctx, url, cardId) {
|
|
4585
|
+
const model = await this.getModel(ctx);
|
|
4586
|
+
const refid = await this.getRefid(ctx, url, cardId);
|
|
4587
|
+
const requestUrl = `?model=KFC:J:G:A:${model}&f=message.get`;
|
|
4588
|
+
const xmlRequestBody = `<call model="KFC:J:G:A:${model}" srcid="00010203040506070809">
|
|
4589
|
+
<game method="sv7_load" ver="0">
|
|
4590
|
+
<dataid __type="str">${refid}</dataid>
|
|
4591
|
+
<cardid __type="str">${cardId}</cardid>
|
|
4592
|
+
<refid __type="str">${refid}</refid>
|
|
4593
|
+
<locid __type="str">ea</locid>
|
|
4594
|
+
</game>
|
|
4595
|
+
</call>`;
|
|
4596
|
+
const response = await ctx.http.post(requestUrl, xmlRequestBody, { baseURL: url });
|
|
4597
|
+
const decodedResponse = typeof response === "string" ? response : new TextDecoder("utf-8").decode(response);
|
|
4598
|
+
const jsonResponse = await xmlToJson(decodedResponse);
|
|
4599
|
+
const name15 = jsonResponse.response.game?.[0]?.name?.[0]._;
|
|
4600
|
+
if (!name15) throw new Error("User name not found");
|
|
4601
|
+
return name15;
|
|
4602
|
+
}
|
|
4603
|
+
async getAllScore(ctx, url, cardId, config) {
|
|
4604
|
+
const model = await this.getModel(ctx);
|
|
4605
|
+
const refid = await this.getRefid(ctx, url, cardId);
|
|
4606
|
+
const requestUrl = `?model=KFC:J:G:A:${model}&f=message.get`;
|
|
4607
|
+
const xmlRequestBody = `<call model="KFC:J:G:A:${model}" srcid="00010203040506070809">
|
|
4608
|
+
<game method="sv7_load_m" ver="0">
|
|
4609
|
+
<dataid __type="str">${refid}</dataid>
|
|
4610
|
+
<cardid __type="str">${cardId}</cardid>
|
|
4611
|
+
<refid __type="str">${refid}</refid>
|
|
4612
|
+
<locid __type="str">ea</locid>
|
|
4613
|
+
</game>
|
|
4614
|
+
</call>`;
|
|
4615
|
+
const response = await ctx.http.post(requestUrl, xmlRequestBody, { baseURL: url });
|
|
4616
|
+
const decodedResponse = typeof response === "string" ? response : new TextDecoder("utf-8").decode(response);
|
|
4617
|
+
const jsonResponse = await xmlToJson(decodedResponse);
|
|
4618
|
+
const infoList = jsonResponse.response.game?.[0]?.music?.[0]?.info || [];
|
|
4619
|
+
if (!Array.isArray(infoList)) return [];
|
|
4620
|
+
const scoresRaw = infoList.map((item) => {
|
|
4621
|
+
const paramStr = item.param?.[0]?._ || item.param?.[0];
|
|
4622
|
+
return paramStr ? paramStr.split(" ").map(Number) : [];
|
|
4623
|
+
}).filter((arr) => arr.length >= 11);
|
|
4624
|
+
const musicIds = scoresRaw.map((arr) => arr[0]);
|
|
4625
|
+
const musicService = MusicService.getInstance(config);
|
|
4626
|
+
const musicDataList = await musicService.getMusic(ctx, musicIds);
|
|
4627
|
+
const musicDataMap = new Map(musicDataList.map((m) => [m.id, m]));
|
|
4628
|
+
return scoresRaw.map((arr) => {
|
|
4629
|
+
const [
|
|
4630
|
+
music_id,
|
|
4631
|
+
music_diff,
|
|
4632
|
+
score,
|
|
4633
|
+
exscore,
|
|
4634
|
+
clear_type,
|
|
4635
|
+
score_grade,
|
|
4636
|
+
,
|
|
4637
|
+
,
|
|
4638
|
+
btn_rate,
|
|
4639
|
+
long_rate,
|
|
4640
|
+
vol_rate
|
|
4641
|
+
] = arr;
|
|
4642
|
+
const music = musicDataMap.get(music_id) || null;
|
|
4643
|
+
const diffStr = this.getDifficultyString(music_diff);
|
|
4644
|
+
let difficultyData = null;
|
|
4645
|
+
if (music && music.difficulty && music.difficulty.length > 0) {
|
|
4646
|
+
difficultyData = music.difficulty.find((diff) => diff.difstr.toLowerCase() === diffStr) || null;
|
|
4647
|
+
}
|
|
4648
|
+
const infVer = music ? music.inf_ver : 2;
|
|
4649
|
+
const scoreObj = {
|
|
4650
|
+
music: {
|
|
4651
|
+
music_id,
|
|
4652
|
+
music_diff: difficultyData ? difficultyData.difnum : 0,
|
|
4653
|
+
music_diff_name: getDiffName(diffStr, infVer),
|
|
4654
|
+
music_diff_full_name: getDiffFullName(diffStr, infVer),
|
|
4655
|
+
score,
|
|
4656
|
+
exscore,
|
|
4657
|
+
clear_type: this.getClearType(clear_type),
|
|
4658
|
+
score_grade: getGradeByScore(score),
|
|
4659
|
+
btn_rate: String(btn_rate),
|
|
4660
|
+
long_rate: String(long_rate),
|
|
4661
|
+
vol_rate: String(vol_rate)
|
|
4662
|
+
},
|
|
4663
|
+
extra: {
|
|
4664
|
+
play_count: 0,
|
|
4665
|
+
update_at: "",
|
|
4666
|
+
volforce: 0
|
|
4667
|
+
},
|
|
4668
|
+
music_data: music,
|
|
4669
|
+
difficulty_data: difficultyData
|
|
4670
|
+
};
|
|
4671
|
+
scoreObj.extra.volforce = calculateVolforce(scoreObj);
|
|
4672
|
+
return scoreObj;
|
|
4673
|
+
});
|
|
4674
|
+
}
|
|
4675
|
+
getClearType(clearType) {
|
|
4676
|
+
const map = {
|
|
4677
|
+
0: "NO PLAY",
|
|
4678
|
+
1: "PLAYED",
|
|
4679
|
+
2: "NC",
|
|
4680
|
+
3: "HC",
|
|
4681
|
+
4: "MC",
|
|
4682
|
+
5: "UC",
|
|
4683
|
+
6: "PUC"
|
|
4684
|
+
};
|
|
4685
|
+
return map[clearType] || "NO PLAY";
|
|
4686
|
+
}
|
|
4687
|
+
getDifficultyString(diffType) {
|
|
4688
|
+
const diffStrMap = {
|
|
4689
|
+
0: "novice",
|
|
4690
|
+
1: "advanced",
|
|
4691
|
+
2: "exhaust",
|
|
4692
|
+
3: "infinite",
|
|
4693
|
+
4: "maximum",
|
|
4694
|
+
5: "ultimate"
|
|
4695
|
+
};
|
|
4696
|
+
return diffStrMap[diffType] || "novice";
|
|
4697
|
+
}
|
|
4698
|
+
};
|
|
4699
|
+
|
|
4700
|
+
// src/games/sdvx/adapters/mao.ts
|
|
4701
|
+
var MaoSDVXService = class _MaoSDVXService {
|
|
4702
|
+
static {
|
|
4703
|
+
__name(this, "MaoSDVXService");
|
|
4704
|
+
}
|
|
4705
|
+
static instance;
|
|
4706
|
+
logger;
|
|
4707
|
+
constructor(logger5) {
|
|
4708
|
+
this.logger = logger5;
|
|
4709
|
+
}
|
|
4710
|
+
static getInstance(logger5) {
|
|
4711
|
+
if (!_MaoSDVXService.instance) {
|
|
4712
|
+
_MaoSDVXService.instance = new _MaoSDVXService(logger5);
|
|
4582
4713
|
}
|
|
4583
|
-
return
|
|
4714
|
+
return _MaoSDVXService.instance;
|
|
4584
4715
|
}
|
|
4585
|
-
|
|
4586
|
-
|
|
4587
|
-
|
|
4588
|
-
|
|
4589
|
-
|
|
4590
|
-
|
|
4591
|
-
|
|
4592
|
-
|
|
4716
|
+
getDifficultyString(diffType) {
|
|
4717
|
+
const diffStrMap = {
|
|
4718
|
+
0: "novice",
|
|
4719
|
+
1: "advanced",
|
|
4720
|
+
2: "exhaust",
|
|
4721
|
+
3: "infinite",
|
|
4722
|
+
4: "maximum",
|
|
4723
|
+
5: "ultimate"
|
|
4593
4724
|
};
|
|
4725
|
+
return diffStrMap[diffType] || "novice";
|
|
4594
4726
|
}
|
|
4595
|
-
|
|
4596
|
-
|
|
4597
|
-
|
|
4598
|
-
|
|
4599
|
-
|
|
4600
|
-
|
|
4601
|
-
|
|
4602
|
-
|
|
4727
|
+
getClearType(clearType) {
|
|
4728
|
+
return clearType === 6 ? "MC" : clearType === 5 ? "PUC" : clearType === 4 ? "UC" : clearType === 3 ? "HC" : clearType === 2 ? "NC" : clearType === 1 ? "PLAYED" : "NO PLAY";
|
|
4729
|
+
}
|
|
4730
|
+
async getUserName(ctx, url, cardId) {
|
|
4731
|
+
const response = await ctx.http.get(`/my?card=${cardId}`, {
|
|
4732
|
+
baseURL: ctx.globalConfig.maoServerUrl,
|
|
4733
|
+
responseType: "text"
|
|
4734
|
+
});
|
|
4735
|
+
const match = response.match(/猫网玩家\[([^\]]+)\]/);
|
|
4736
|
+
if (!match || !match[1]) {
|
|
4737
|
+
throw new Error("Failed to extract player ID from response");
|
|
4603
4738
|
}
|
|
4604
|
-
return
|
|
4739
|
+
return match[1];
|
|
4605
4740
|
}
|
|
4606
|
-
|
|
4607
|
-
|
|
4608
|
-
|
|
4609
|
-
|
|
4610
|
-
|
|
4611
|
-
*/
|
|
4612
|
-
async getMusic(ctx, musicIds) {
|
|
4613
|
-
const numericIds = musicIds.filter((id) => typeof id === "number");
|
|
4614
|
-
const stringTitles = musicIds.filter((id) => typeof id === "string");
|
|
4615
|
-
const numericMap = /* @__PURE__ */ new Map();
|
|
4616
|
-
const stringMap = /* @__PURE__ */ new Map();
|
|
4617
|
-
if (numericIds.length > 0) {
|
|
4618
|
-
try {
|
|
4619
|
-
const response = await ctx.http.post(
|
|
4620
|
-
`/music`,
|
|
4621
|
-
{ ids: numericIds },
|
|
4622
|
-
{ baseURL: this.sdvx_data_url }
|
|
4623
|
-
);
|
|
4624
|
-
if (Array.isArray(response)) {
|
|
4625
|
-
for (const m of response) {
|
|
4626
|
-
const normalized = this.normalizeMusicDifnum(m);
|
|
4627
|
-
numericMap.set(Number(normalized.id), normalized);
|
|
4628
|
-
}
|
|
4629
|
-
}
|
|
4630
|
-
} catch (error) {
|
|
4631
|
-
console.warn("Failed to fetch music data for numeric IDs:", error);
|
|
4632
|
-
}
|
|
4741
|
+
async verifyPin(ctx, url, cardId, pin) {
|
|
4742
|
+
const apiKey = ctx.globalConfig.maoApiKey;
|
|
4743
|
+
if (!apiKey) {
|
|
4744
|
+
this.logger.warn("maoApiKey not configured");
|
|
4745
|
+
return false;
|
|
4633
4746
|
}
|
|
4634
|
-
|
|
4635
|
-
|
|
4636
|
-
|
|
4637
|
-
|
|
4638
|
-
|
|
4639
|
-
|
|
4640
|
-
|
|
4641
|
-
|
|
4642
|
-
|
|
4643
|
-
|
|
4644
|
-
|
|
4645
|
-
|
|
4646
|
-
|
|
4647
|
-
|
|
4648
|
-
|
|
4649
|
-
|
|
4747
|
+
const resp = await ctx.http.get(
|
|
4748
|
+
`/bot/v2/player/card?card=${cardId}`,
|
|
4749
|
+
{
|
|
4750
|
+
baseURL: ctx.globalConfig.maoServerUrl,
|
|
4751
|
+
headers: { "X-API-Key": apiKey }
|
|
4752
|
+
}
|
|
4753
|
+
);
|
|
4754
|
+
return resp?.code === 0 && resp?.data?.password === pin;
|
|
4755
|
+
}
|
|
4756
|
+
clearTypeToNum(clearType) {
|
|
4757
|
+
switch (clearType) {
|
|
4758
|
+
case "MC":
|
|
4759
|
+
return 6;
|
|
4760
|
+
case "PUC":
|
|
4761
|
+
case "S-PUC":
|
|
4762
|
+
return 5;
|
|
4763
|
+
case "UC":
|
|
4764
|
+
return 4;
|
|
4765
|
+
case "HC":
|
|
4766
|
+
return 3;
|
|
4767
|
+
case "NC":
|
|
4768
|
+
return 2;
|
|
4769
|
+
case "PLAYED":
|
|
4770
|
+
return 1;
|
|
4771
|
+
default:
|
|
4772
|
+
return 0;
|
|
4650
4773
|
}
|
|
4651
|
-
|
|
4652
|
-
|
|
4653
|
-
|
|
4654
|
-
|
|
4774
|
+
}
|
|
4775
|
+
diffNameToMusicType(diffName) {
|
|
4776
|
+
switch (diffName.toUpperCase()) {
|
|
4777
|
+
case "NOV":
|
|
4778
|
+
return 0;
|
|
4779
|
+
case "ADV":
|
|
4780
|
+
return 1;
|
|
4781
|
+
case "EXH":
|
|
4782
|
+
return 2;
|
|
4783
|
+
case "INF":
|
|
4784
|
+
case "GRV":
|
|
4785
|
+
case "HVN":
|
|
4786
|
+
case "VVD":
|
|
4787
|
+
case "XCD":
|
|
4788
|
+
return 3;
|
|
4789
|
+
case "MXM":
|
|
4790
|
+
return 4;
|
|
4791
|
+
case "ULT":
|
|
4792
|
+
return 5;
|
|
4793
|
+
default:
|
|
4794
|
+
return 0;
|
|
4655
4795
|
}
|
|
4656
|
-
return results;
|
|
4657
4796
|
}
|
|
4658
|
-
|
|
4659
|
-
|
|
4660
|
-
|
|
4661
|
-
|
|
4662
|
-
|
|
4663
|
-
|
|
4664
|
-
|
|
4665
|
-
|
|
4666
|
-
|
|
4667
|
-
|
|
4668
|
-
|
|
4669
|
-
|
|
4670
|
-
|
|
4671
|
-
|
|
4672
|
-
|
|
4673
|
-
difnum: this.parseDifnum(diff.difnum),
|
|
4674
|
-
illustrator: diff.ext_illustrator,
|
|
4675
|
-
effected_by: diff.ext_effected_by,
|
|
4676
|
-
cover_url: diff.cover_url,
|
|
4677
|
-
inf_ver: diff.inf_ver
|
|
4678
|
-
}))
|
|
4797
|
+
async uploadScore(ctx, url, cardId, scores) {
|
|
4798
|
+
const apiKey = ctx.globalConfig.maoApiKey;
|
|
4799
|
+
if (!apiKey) {
|
|
4800
|
+
this.logger.warn("maoApiKey not configured");
|
|
4801
|
+
return false;
|
|
4802
|
+
}
|
|
4803
|
+
const payload = scores.filter((s) => {
|
|
4804
|
+
const musicId = Number(s.music.music_id);
|
|
4805
|
+
return musicId > 0 && s.music.music_diff_name;
|
|
4806
|
+
}).map((s) => ({
|
|
4807
|
+
music_id: Number(s.music.music_id),
|
|
4808
|
+
music_type: this.diffNameToMusicType(s.music.music_diff_name),
|
|
4809
|
+
score: s.music.score,
|
|
4810
|
+
exscore: s.music.exscore,
|
|
4811
|
+
clear_type: this.clearTypeToNum(s.music.clear_type)
|
|
4679
4812
|
}));
|
|
4680
|
-
|
|
4681
|
-
|
|
4682
|
-
|
|
4683
|
-
|
|
4684
|
-
|
|
4685
|
-
|
|
4686
|
-
|
|
4687
|
-
async searchMusic(ctx, query) {
|
|
4688
|
-
try {
|
|
4689
|
-
const response = await ctx.http.get(
|
|
4690
|
-
`/search?query=${encodeURIComponent(query)}&size=50`,
|
|
4691
|
-
{
|
|
4692
|
-
baseURL: this.sdvx_search_url
|
|
4693
|
-
}
|
|
4694
|
-
);
|
|
4695
|
-
const results = response?.results;
|
|
4696
|
-
if (!Array.isArray(results) || results.length === 0) {
|
|
4697
|
-
return null;
|
|
4698
|
-
}
|
|
4699
|
-
const musicIds = results.map((item) => Number.isInteger(item.id) ? item.id : item.external_key).filter((v) => v != null);
|
|
4700
|
-
if (musicIds.length === 0) {
|
|
4701
|
-
return null;
|
|
4813
|
+
if (payload.length === 0) return false;
|
|
4814
|
+
const resp = await ctx.http.post(
|
|
4815
|
+
"/bot/v2/sdvx/upload",
|
|
4816
|
+
{ card: cardId, scores: payload },
|
|
4817
|
+
{
|
|
4818
|
+
baseURL: ctx.globalConfig.maoServerUrl,
|
|
4819
|
+
headers: { "X-API-Key": apiKey }
|
|
4702
4820
|
}
|
|
4703
|
-
|
|
4704
|
-
|
|
4705
|
-
return null;
|
|
4706
|
-
}
|
|
4821
|
+
);
|
|
4822
|
+
return resp?.code === 0;
|
|
4707
4823
|
}
|
|
4708
|
-
async
|
|
4824
|
+
async getAllScore(ctx, url, cardId, config) {
|
|
4709
4825
|
try {
|
|
4710
|
-
const
|
|
4711
|
-
|
|
4712
|
-
|
|
4713
|
-
{ baseURL: this.sdvx_data_url }
|
|
4714
|
-
);
|
|
4715
|
-
if (!Array.isArray(response)) {
|
|
4826
|
+
const apiKey = ctx.globalConfig.maoApiKey;
|
|
4827
|
+
if (!apiKey) {
|
|
4828
|
+
this.logger.warn("maoApiKey not configured");
|
|
4716
4829
|
return [];
|
|
4717
4830
|
}
|
|
4718
|
-
const
|
|
4719
|
-
|
|
4720
|
-
|
|
4721
|
-
|
|
4722
|
-
|
|
4831
|
+
const resp = await ctx.http.get(`/bot/v2/sdvx/scores?card=${cardId}`, {
|
|
4832
|
+
baseURL: ctx.globalConfig.maoServerUrl,
|
|
4833
|
+
headers: { "X-API-Key": apiKey }
|
|
4834
|
+
});
|
|
4835
|
+
if (resp?.code !== 0 || !resp?.data) {
|
|
4836
|
+
return [];
|
|
4837
|
+
}
|
|
4838
|
+
const filteredData = resp.data.filter(
|
|
4839
|
+
(score) => score.mid !== 244 && score.mid !== 1759 && score.mid !== 1761 && score.isPlus === 0
|
|
4723
4840
|
);
|
|
4724
|
-
const
|
|
4725
|
-
|
|
4726
|
-
|
|
4727
|
-
|
|
4841
|
+
const musicIds = filteredData.map((score) => score.mid);
|
|
4842
|
+
const diffTypes = filteredData.map((score) => score.musicType);
|
|
4843
|
+
const musicService = MusicService.getInstance(config);
|
|
4844
|
+
const musicData = await musicService.getMusic(ctx, musicIds);
|
|
4845
|
+
const musicDataMap = new Map(musicData.map((music) => [music.id, music]));
|
|
4846
|
+
const scores = filteredData.map((score, index) => {
|
|
4847
|
+
const musicId = score.mid;
|
|
4848
|
+
const music = musicDataMap.get(musicId);
|
|
4849
|
+
const musicDiffType = score.musicType;
|
|
4850
|
+
const diffStr = this.getDifficultyString(musicDiffType);
|
|
4851
|
+
let difficultyData = null;
|
|
4852
|
+
if (music && music.difficulty && music.difficulty.length > 0) {
|
|
4853
|
+
difficultyData = music.difficulty.find((diff) => diff.difstr.toLowerCase() === diffStr) || null;
|
|
4854
|
+
}
|
|
4855
|
+
const infVer = music ? music.inf_ver : 2;
|
|
4856
|
+
const scoreObj = {
|
|
4857
|
+
music: {
|
|
4858
|
+
music_id: musicId,
|
|
4859
|
+
music_diff: difficultyData ? difficultyData.difnum : 0,
|
|
4860
|
+
music_diff_name: getDiffName(diffStr, infVer),
|
|
4861
|
+
music_diff_full_name: getDiffFullName(diffStr, infVer),
|
|
4862
|
+
score: score.score,
|
|
4863
|
+
exscore: score.exscore,
|
|
4864
|
+
clear_type: this.getClearType(score.clearType),
|
|
4865
|
+
score_grade: getGradeByScore(score.score),
|
|
4866
|
+
btn_rate: score.btnRate.toString(),
|
|
4867
|
+
long_rate: score.longRate.toString(),
|
|
4868
|
+
vol_rate: score.volRate.toString()
|
|
4869
|
+
},
|
|
4870
|
+
extra: {
|
|
4871
|
+
play_count: score.playCount,
|
|
4872
|
+
update_at: score.updateAt,
|
|
4873
|
+
volforce: 0
|
|
4874
|
+
},
|
|
4875
|
+
music_data: music || null,
|
|
4876
|
+
difficulty_data: difficultyData || null
|
|
4877
|
+
};
|
|
4878
|
+
scoreObj.extra.volforce = calculateVolforce(scoreObj);
|
|
4879
|
+
return scoreObj;
|
|
4880
|
+
});
|
|
4881
|
+
return scores;
|
|
4882
|
+
} catch (error) {
|
|
4883
|
+
if (error.response?.status === 404) {
|
|
4884
|
+
return [];
|
|
4728
4885
|
}
|
|
4729
|
-
|
|
4730
|
-
} catch {
|
|
4731
|
-
return [];
|
|
4886
|
+
throw error;
|
|
4732
4887
|
}
|
|
4733
4888
|
}
|
|
4734
|
-
|
|
4735
|
-
|
|
4736
|
-
|
|
4737
|
-
|
|
4738
|
-
|
|
4739
|
-
if (t === "ran" || t === "random") return "random";
|
|
4740
|
-
if (t === "m" || t === "mirror" || t === "mir") return "mirror";
|
|
4741
|
-
if (t === "sran" || t === "srandom") return "s-random";
|
|
4742
|
-
if (t === "fran" || t === "wtf") return "f-random";
|
|
4743
|
-
return null;
|
|
4744
|
-
}
|
|
4745
|
-
__name(mapArrangementToken, "mapArrangementToken");
|
|
4746
|
-
function mapLeftColorToken(tok) {
|
|
4747
|
-
const m = /^l(blue|red|yellow|green)$/i.exec(tok);
|
|
4748
|
-
return m ? m[1].toUpperCase() : null;
|
|
4749
|
-
}
|
|
4750
|
-
__name(mapLeftColorToken, "mapLeftColorToken");
|
|
4751
|
-
function mapRightColorToken(tok) {
|
|
4752
|
-
const m = /^r(blue|red|yellow|green)$/i.exec(tok);
|
|
4753
|
-
return m ? m[1].toUpperCase() : null;
|
|
4754
|
-
}
|
|
4755
|
-
__name(mapRightColorToken, "mapRightColorToken");
|
|
4756
|
-
function isBarcode(s) {
|
|
4757
|
-
return /^\d+\.[A-Za-z]+$/.test(s);
|
|
4758
|
-
}
|
|
4759
|
-
__name(isBarcode, "isBarcode");
|
|
4760
|
-
function parseDiffAndTitle(rawTokens) {
|
|
4761
|
-
let diffStr = null;
|
|
4762
|
-
let diffIndex = null;
|
|
4763
|
-
const titleParts = [];
|
|
4764
|
-
const fullMap = {
|
|
4765
|
-
novice: "novice",
|
|
4766
|
-
nov: "novice",
|
|
4767
|
-
advanced: "advanced",
|
|
4768
|
-
adv: "advanced",
|
|
4769
|
-
exhaust: "exhaust",
|
|
4770
|
-
exh: "exhaust",
|
|
4771
|
-
infinite: "infinite",
|
|
4772
|
-
inf: "infinite",
|
|
4773
|
-
gravity: "infinite",
|
|
4774
|
-
grv: "infinite",
|
|
4775
|
-
heavenly: "infinite",
|
|
4776
|
-
hvn: "infinite",
|
|
4777
|
-
vivid: "infinite",
|
|
4778
|
-
vvd: "infinite",
|
|
4779
|
-
exceed: "infinite",
|
|
4780
|
-
xcd: "infinite",
|
|
4781
|
-
maximum: "maximum",
|
|
4782
|
-
mxm: "maximum"
|
|
4783
|
-
};
|
|
4784
|
-
for (const tok of rawTokens) {
|
|
4785
|
-
const trimmed = tok.trim();
|
|
4786
|
-
if (!trimmed) continue;
|
|
4787
|
-
if (diffIndex === null && /^[1-5]$/.test(trimmed)) {
|
|
4788
|
-
diffIndex = Number(trimmed) - 1;
|
|
4789
|
-
continue;
|
|
4790
|
-
}
|
|
4791
|
-
const lettersOnly = trimmed.replace(/[^A-Za-z]/g, "");
|
|
4792
|
-
if (lettersOnly) {
|
|
4793
|
-
const byAbbr = getDiffStringFromAbbr(lettersOnly);
|
|
4794
|
-
if (byAbbr) {
|
|
4795
|
-
diffStr = byAbbr.diffStr;
|
|
4796
|
-
continue;
|
|
4797
|
-
}
|
|
4798
|
-
const lower = lettersOnly.toLowerCase();
|
|
4799
|
-
if (fullMap[lower]) {
|
|
4800
|
-
diffStr = fullMap[lower];
|
|
4801
|
-
continue;
|
|
4802
|
-
}
|
|
4889
|
+
async getScore(ctx, url, cardId, musicId, config) {
|
|
4890
|
+
const apiKey = ctx.globalConfig.maoApiKey;
|
|
4891
|
+
if (!apiKey) {
|
|
4892
|
+
this.logger.warn("maoApiKey not configured");
|
|
4893
|
+
throw new Error("maoApiKey not configured");
|
|
4803
4894
|
}
|
|
4804
|
-
|
|
4805
|
-
|
|
4806
|
-
|
|
4807
|
-
|
|
4808
|
-
|
|
4809
|
-
|
|
4810
|
-
|
|
4811
|
-
|
|
4812
|
-
|
|
4813
|
-
|
|
4814
|
-
|
|
4815
|
-
|
|
4816
|
-
|
|
4817
|
-
|
|
4818
|
-
|
|
4819
|
-
|
|
4820
|
-
|
|
4821
|
-
|
|
4822
|
-
|
|
4895
|
+
const resp = await ctx.http.get(`/bot/v2/sdvx/find?card=${cardId}&mid=${musicId}`, {
|
|
4896
|
+
baseURL: ctx.globalConfig.maoServerUrl,
|
|
4897
|
+
headers: { "X-API-Key": apiKey }
|
|
4898
|
+
});
|
|
4899
|
+
if (resp?.code !== 0 || !resp?.data) {
|
|
4900
|
+
const musicService2 = MusicService.getInstance(config);
|
|
4901
|
+
const musicData2 = await musicService2.getMusic(ctx, [musicId]);
|
|
4902
|
+
const music2 = musicData2.length > 0 ? musicData2[0] : null;
|
|
4903
|
+
const diffStr2 = "novice";
|
|
4904
|
+
const infVer2 = music2 ? music2.inf_ver : 2;
|
|
4905
|
+
let difficultyData2 = null;
|
|
4906
|
+
if (music2 && music2.difficulty && music2.difficulty.length > 0) {
|
|
4907
|
+
difficultyData2 = music2.difficulty.find((diff) => diff.difstr.toLowerCase() === diffStr2) || music2.difficulty[0];
|
|
4908
|
+
}
|
|
4909
|
+
return {
|
|
4910
|
+
music: {
|
|
4911
|
+
music_id: musicId,
|
|
4912
|
+
music_diff: difficultyData2 ? difficultyData2.difnum : 0,
|
|
4913
|
+
music_diff_name: getDiffName(diffStr2, infVer2),
|
|
4914
|
+
music_diff_full_name: getDiffFullName(diffStr2, infVer2),
|
|
4915
|
+
score: 0,
|
|
4916
|
+
exscore: 0,
|
|
4917
|
+
clear_type: this.getClearType(0),
|
|
4918
|
+
score_grade: getGradeByScore(0),
|
|
4919
|
+
btn_rate: "0",
|
|
4920
|
+
long_rate: "0",
|
|
4921
|
+
vol_rate: "0"
|
|
4922
|
+
},
|
|
4923
|
+
extra: {
|
|
4924
|
+
play_count: 0,
|
|
4925
|
+
update_at: "0",
|
|
4926
|
+
volforce: 0
|
|
4927
|
+
},
|
|
4928
|
+
music_data: music2,
|
|
4929
|
+
difficulty_data: difficultyData2
|
|
4930
|
+
};
|
|
4823
4931
|
}
|
|
4932
|
+
const data = resp.data;
|
|
4933
|
+
const musicTypeNum = data.musicType;
|
|
4934
|
+
const diffStr = this.getDifficultyString(musicTypeNum);
|
|
4824
4935
|
const musicService = MusicService.getInstance(config);
|
|
4825
|
-
|
|
4826
|
-
|
|
4827
|
-
|
|
4828
|
-
|
|
4829
|
-
|
|
4830
|
-
const remaining = [];
|
|
4831
|
-
for (const tok of tokens) {
|
|
4832
|
-
const arr = mapArrangementToken(tok);
|
|
4833
|
-
if (arr) {
|
|
4834
|
-
arrangement_mode = arr;
|
|
4835
|
-
continue;
|
|
4836
|
-
}
|
|
4837
|
-
const lc = mapLeftColorToken(tok);
|
|
4838
|
-
if (lc) {
|
|
4839
|
-
laser_l_color = lc;
|
|
4840
|
-
continue;
|
|
4841
|
-
}
|
|
4842
|
-
const rc = mapRightColorToken(tok);
|
|
4843
|
-
if (rc) {
|
|
4844
|
-
laser_r_color = rc;
|
|
4845
|
-
continue;
|
|
4846
|
-
}
|
|
4847
|
-
remaining.push(tok);
|
|
4848
|
-
}
|
|
4849
|
-
const rest = remaining.join(" ").trim();
|
|
4850
|
-
if (!rest) {
|
|
4851
|
-
return;
|
|
4852
|
-
}
|
|
4853
|
-
if (isBarcode(rest)) {
|
|
4854
|
-
const [midRaw, diffAbbrRaw] = rest.split(".");
|
|
4855
|
-
const music_id = Number(midRaw);
|
|
4856
|
-
const { diffStr } = getDiffStringFromAbbr(diffAbbrRaw);
|
|
4857
|
-
const difstr = diffStr;
|
|
4858
|
-
const output_format = "PNG";
|
|
4859
|
-
const payload = {
|
|
4860
|
-
music_id,
|
|
4861
|
-
difstr,
|
|
4862
|
-
arrangement_mode,
|
|
4863
|
-
rng_seed: null,
|
|
4864
|
-
output_format,
|
|
4865
|
-
laser_l_color,
|
|
4866
|
-
laser_r_color,
|
|
4867
|
-
px_per_second: 600,
|
|
4868
|
-
column_height: 2800
|
|
4869
|
-
};
|
|
4870
|
-
const musicList = await musicService.getMusic(ctx, [music_id]);
|
|
4871
|
-
session.send(
|
|
4872
|
-
session.text(".drawing", {
|
|
4873
|
-
name: musicList[0].title_name,
|
|
4874
|
-
difstr
|
|
4875
|
-
})
|
|
4876
|
-
);
|
|
4877
|
-
const res = await ctx.http.post(`${config.sdvx_data_url}/chart`, payload, {
|
|
4878
|
-
headers: { "Content-Type": "application/json" }
|
|
4879
|
-
});
|
|
4880
|
-
return import_koishi12.h.image(res, "image/png");
|
|
4881
|
-
} else {
|
|
4882
|
-
const {
|
|
4883
|
-
diffStr: parsedDiff,
|
|
4884
|
-
diffIndex,
|
|
4885
|
-
titleQuery
|
|
4886
|
-
} = parseDiffAndTitle(remaining);
|
|
4887
|
-
if (!titleQuery) return session.text(".no-result");
|
|
4888
|
-
const musicInfo = await musicService.searchMusic(
|
|
4889
|
-
ctx,
|
|
4890
|
-
titleQuery
|
|
4891
|
-
);
|
|
4892
|
-
if (!musicInfo || musicInfo.length === 0) return session.text(".no-result");
|
|
4893
|
-
const picked = musicInfo[0];
|
|
4894
|
-
const music_id = Number(picked?.id);
|
|
4895
|
-
if (!Number.isFinite(music_id)) {
|
|
4896
|
-
logger5.warn("search result missing id", picked);
|
|
4897
|
-
return session.text(".error");
|
|
4898
|
-
}
|
|
4899
|
-
let difstr = null;
|
|
4900
|
-
if (parsedDiff) {
|
|
4901
|
-
difstr = parsedDiff;
|
|
4902
|
-
} else if (diffIndex !== null && Array.isArray(picked?.difficulty) && diffIndex >= 0 && diffIndex < picked.difficulty.length) {
|
|
4903
|
-
const d = picked.difficulty[diffIndex];
|
|
4904
|
-
difstr = d?.difstr ?? null;
|
|
4905
|
-
}
|
|
4906
|
-
if (!difstr) {
|
|
4907
|
-
difstr = getHighestDifstr(picked?.difficulty);
|
|
4908
|
-
}
|
|
4909
|
-
const output_format = "PNG";
|
|
4910
|
-
const payload = {
|
|
4911
|
-
music_id,
|
|
4912
|
-
difstr,
|
|
4913
|
-
arrangement_mode,
|
|
4914
|
-
rng_seed: null,
|
|
4915
|
-
output_format,
|
|
4916
|
-
laser_l_color,
|
|
4917
|
-
laser_r_color,
|
|
4918
|
-
px_per_second: 600,
|
|
4919
|
-
column_height: 2800
|
|
4920
|
-
};
|
|
4921
|
-
session.send(
|
|
4922
|
-
session.text(".drawing", {
|
|
4923
|
-
name: picked.title_name,
|
|
4924
|
-
difstr
|
|
4925
|
-
})
|
|
4926
|
-
);
|
|
4927
|
-
const res = await ctx.http.post(`${config.sdvx_data_url}/chart`, payload, {
|
|
4928
|
-
headers: { "Content-Type": "application/json" }
|
|
4929
|
-
});
|
|
4930
|
-
return import_koishi12.h.image(res, "image/png");
|
|
4931
|
-
}
|
|
4932
|
-
} catch (err) {
|
|
4933
|
-
logger5.warn(err);
|
|
4934
|
-
return session.text(".error");
|
|
4936
|
+
const musicData = await musicService.getMusic(ctx, [musicId]);
|
|
4937
|
+
const music = musicData.length > 0 ? musicData[0] : null;
|
|
4938
|
+
let difficultyData = null;
|
|
4939
|
+
if (music && music.difficulty && music.difficulty.length > 0) {
|
|
4940
|
+
difficultyData = music.difficulty.find((diff) => diff.difstr.toLowerCase() === diffStr) || null;
|
|
4935
4941
|
}
|
|
4936
|
-
|
|
4937
|
-
|
|
4938
|
-
|
|
4939
|
-
|
|
4940
|
-
|
|
4941
|
-
|
|
4942
|
-
|
|
4943
|
-
|
|
4944
|
-
|
|
4945
|
-
|
|
4946
|
-
|
|
4947
|
-
|
|
4948
|
-
|
|
4949
|
-
|
|
4950
|
-
|
|
4951
|
-
|
|
4952
|
-
|
|
4953
|
-
|
|
4954
|
-
|
|
4955
|
-
|
|
4956
|
-
|
|
4957
|
-
|
|
4958
|
-
|
|
4959
|
-
|
|
4960
|
-
|
|
4942
|
+
const infVer = music ? music.inf_ver : 2;
|
|
4943
|
+
const scoreObj = {
|
|
4944
|
+
music: {
|
|
4945
|
+
music_id: musicId,
|
|
4946
|
+
music_diff: difficultyData ? difficultyData.difnum : 0,
|
|
4947
|
+
music_diff_name: getDiffName(diffStr, infVer),
|
|
4948
|
+
music_diff_full_name: getDiffFullName(diffStr, infVer),
|
|
4949
|
+
score: data.score,
|
|
4950
|
+
exscore: data.exscore,
|
|
4951
|
+
clear_type: this.getClearType(data.clearType),
|
|
4952
|
+
score_grade: getGradeByScore(data.score),
|
|
4953
|
+
btn_rate: "0",
|
|
4954
|
+
long_rate: "0",
|
|
4955
|
+
vol_rate: "0"
|
|
4956
|
+
},
|
|
4957
|
+
extra: {
|
|
4958
|
+
play_count: data.playCount,
|
|
4959
|
+
update_at: "0",
|
|
4960
|
+
volforce: 0
|
|
4961
|
+
},
|
|
4962
|
+
music_data: music,
|
|
4963
|
+
difficulty_data: difficultyData
|
|
4964
|
+
};
|
|
4965
|
+
scoreObj.extra.volforce = calculateVolforce(scoreObj);
|
|
4966
|
+
return scoreObj;
|
|
4961
4967
|
}
|
|
4962
|
-
|
|
4963
|
-
|
|
4964
|
-
|
|
4968
|
+
async getRecentScores(ctx, url, cardId, config, count = 1) {
|
|
4969
|
+
const apiKey = ctx.globalConfig.maoApiKey;
|
|
4970
|
+
if (!apiKey) {
|
|
4971
|
+
this.logger.warn("maoApiKey not configured");
|
|
4972
|
+
return [];
|
|
4965
4973
|
}
|
|
4966
|
-
|
|
4967
|
-
|
|
4968
|
-
|
|
4969
|
-
}
|
|
4970
|
-
};
|
|
4971
|
-
|
|
4972
|
-
// src/servers/utils/grade.ts
|
|
4973
|
-
function getSDVXClearType(clearType) {
|
|
4974
|
-
return clearType === 6 ? "MC" : clearType === 5 ? "PUC" : clearType === 4 ? "UC" : clearType === 3 ? "HC" : clearType === 2 ? "NC" : clearType === 1 ? "PLAYED" : "NO PLAY";
|
|
4975
|
-
}
|
|
4976
|
-
__name(getSDVXClearType, "getSDVXClearType");
|
|
4977
|
-
|
|
4978
|
-
// src/servers/utils/xml.ts
|
|
4979
|
-
var xml2js = __toESM(require("xml2js"), 1);
|
|
4980
|
-
var xmlToJson = /* @__PURE__ */ __name((xml) => {
|
|
4981
|
-
return new Promise((resolve3, reject) => {
|
|
4982
|
-
xml2js.parseString(xml, (err, result) => {
|
|
4983
|
-
if (err) {
|
|
4984
|
-
reject(new Error("Error parsing XML: " + err));
|
|
4985
|
-
} else {
|
|
4986
|
-
resolve3(result);
|
|
4987
|
-
}
|
|
4974
|
+
const resp = await ctx.http.get(`/bot/v2/sdvx/recent?card=${cardId}&count=${count}`, {
|
|
4975
|
+
baseURL: ctx.globalConfig.maoServerUrl,
|
|
4976
|
+
headers: { "X-API-Key": apiKey }
|
|
4988
4977
|
});
|
|
4989
|
-
|
|
4990
|
-
|
|
4991
|
-
|
|
4992
|
-
// src/servers/Asphyxia/services/sdvx-service.ts
|
|
4993
|
-
var SDVXService = class _SDVXService {
|
|
4994
|
-
static {
|
|
4995
|
-
__name(this, "SDVXService");
|
|
4996
|
-
}
|
|
4997
|
-
static instance;
|
|
4998
|
-
logger;
|
|
4999
|
-
constructor(logger5) {
|
|
5000
|
-
this.logger = logger5;
|
|
5001
|
-
}
|
|
5002
|
-
static getInstance(logger5) {
|
|
5003
|
-
if (!_SDVXService.instance) {
|
|
5004
|
-
_SDVXService.instance = new _SDVXService(logger5);
|
|
4978
|
+
if (resp?.code !== 0 || !resp?.data || resp.data.length === 0) {
|
|
4979
|
+
return [];
|
|
5005
4980
|
}
|
|
5006
|
-
|
|
5007
|
-
}
|
|
5008
|
-
/**
|
|
5009
|
-
* 获取 refid
|
|
5010
|
-
*/
|
|
5011
|
-
async getRefid(ctx, url, model, cardId) {
|
|
5012
|
-
const requestUrl = `?model=KFC:J:G:A:${model}&f=message.get`;
|
|
5013
|
-
const xmlRequestBody = `<?xml version="1.0" encoding="UTF-8"?>
|
|
5014
|
-
<call model="KFC:J:G:A:${model}" srcid="00010203040506070809">
|
|
5015
|
-
<cardmng cardid="${cardId}" cardtype="2" method="inquire" update="0" />
|
|
5016
|
-
</call>`;
|
|
5017
|
-
const response = await ctx.http.post(requestUrl, xmlRequestBody, { baseURL: url });
|
|
5018
|
-
const decodedResponse = typeof response === "string" ? response : new TextDecoder("utf-8").decode(response);
|
|
5019
|
-
const jsonResponse = await xmlToJson(decodedResponse);
|
|
5020
|
-
const cardmng = jsonResponse.response.cardmng?.[0]?.$;
|
|
5021
|
-
if (!cardmng || !cardmng.refid) throw new Error("refid not found");
|
|
5022
|
-
return cardmng.refid;
|
|
5023
|
-
}
|
|
5024
|
-
/**
|
|
5025
|
-
* 获取用户名
|
|
5026
|
-
*/
|
|
5027
|
-
async getUserName(ctx, url, cardId, model = "model") {
|
|
5028
|
-
const refid = await this.getRefid(ctx, url, model, cardId);
|
|
5029
|
-
const requestUrl = `?model=KFC:J:G:A:${model}&f=message.get`;
|
|
5030
|
-
const xmlRequestBody = `<call model="KFC:J:G:A:${model}" srcid="00010203040506070809">
|
|
5031
|
-
<game method="sv7_load" ver="0">
|
|
5032
|
-
<dataid __type="str">${refid}</dataid>
|
|
5033
|
-
<cardid __type="str">${cardId}</cardid>
|
|
5034
|
-
<refid __type="str">${refid}</refid>
|
|
5035
|
-
<locid __type="str">ea</locid>
|
|
5036
|
-
</game>
|
|
5037
|
-
</call>`;
|
|
5038
|
-
const response = await ctx.http.post(requestUrl, xmlRequestBody, { baseURL: url });
|
|
5039
|
-
const decodedResponse = typeof response === "string" ? response : new TextDecoder("utf-8").decode(response);
|
|
5040
|
-
const jsonResponse = await xmlToJson(decodedResponse);
|
|
5041
|
-
const name15 = jsonResponse.response.game?.[0]?.name?.[0]._;
|
|
5042
|
-
if (!name15) throw new Error("User name not found");
|
|
5043
|
-
return name15;
|
|
5044
|
-
}
|
|
5045
|
-
/**
|
|
5046
|
-
* 获取所有分数
|
|
5047
|
-
*/
|
|
5048
|
-
async getAllScore(ctx, url, cardId, config, model = "model") {
|
|
5049
|
-
const refid = await this.getRefid(ctx, url, model, cardId);
|
|
5050
|
-
const requestUrl = `?model=KFC:J:G:A:${model}&f=message.get`;
|
|
5051
|
-
const xmlRequestBody = `<call model="KFC:J:G:A:${model}" srcid="00010203040506070809">
|
|
5052
|
-
<game method="sv7_load_m" ver="0">
|
|
5053
|
-
<dataid __type="str">${refid}</dataid>
|
|
5054
|
-
<cardid __type="str">${cardId}</cardid>
|
|
5055
|
-
<refid __type="str">${refid}</refid>
|
|
5056
|
-
<locid __type="str">ea</locid>
|
|
5057
|
-
</game>
|
|
5058
|
-
</call>`;
|
|
5059
|
-
const response = await ctx.http.post(requestUrl, xmlRequestBody, { baseURL: url });
|
|
5060
|
-
const decodedResponse = typeof response === "string" ? response : new TextDecoder("utf-8").decode(response);
|
|
5061
|
-
const jsonResponse = await xmlToJson(decodedResponse);
|
|
5062
|
-
const infoList = jsonResponse.response.game?.[0]?.music?.[0]?.info || [];
|
|
5063
|
-
if (!Array.isArray(infoList)) return [];
|
|
5064
|
-
const scoresRaw = infoList.map((item) => {
|
|
5065
|
-
const paramStr = item.param?.[0]?._ || item.param?.[0];
|
|
5066
|
-
return paramStr ? paramStr.split(" ").map(Number) : [];
|
|
5067
|
-
}).filter((arr) => arr.length >= 11);
|
|
5068
|
-
const musicIds = scoresRaw.map((arr) => arr[0]);
|
|
4981
|
+
const musicIds = resp.data.map((item) => item.mid);
|
|
5069
4982
|
const musicService = MusicService.getInstance(config);
|
|
5070
4983
|
const musicDataList = await musicService.getMusic(ctx, musicIds);
|
|
5071
|
-
const musicDataMap = new Map(musicDataList.map((
|
|
5072
|
-
|
|
5073
|
-
const
|
|
5074
|
-
|
|
5075
|
-
music_diff,
|
|
5076
|
-
score,
|
|
5077
|
-
exscore,
|
|
5078
|
-
clear_type,
|
|
5079
|
-
score_grade,
|
|
5080
|
-
,
|
|
5081
|
-
,
|
|
5082
|
-
btn_rate,
|
|
5083
|
-
long_rate,
|
|
5084
|
-
vol_rate
|
|
5085
|
-
] = arr;
|
|
5086
|
-
const music = musicDataMap.get(music_id) || null;
|
|
5087
|
-
const diffStr = this.getDifficultyString(music_diff);
|
|
4984
|
+
const musicDataMap = new Map(musicDataList.map((music) => [music.id, music]));
|
|
4985
|
+
const scores = resp.data.map((item) => {
|
|
4986
|
+
const music = musicDataMap.get(item.mid);
|
|
4987
|
+
const diffStr = this.getDifficultyString(item.musicType);
|
|
5088
4988
|
let difficultyData = null;
|
|
5089
4989
|
if (music && music.difficulty && music.difficulty.length > 0) {
|
|
5090
4990
|
difficultyData = music.difficulty.find((diff) => diff.difstr.toLowerCase() === diffStr) || null;
|
|
@@ -5092,649 +4992,689 @@ var SDVXService = class _SDVXService {
|
|
|
5092
4992
|
const infVer = music ? music.inf_ver : 2;
|
|
5093
4993
|
const scoreObj = {
|
|
5094
4994
|
music: {
|
|
5095
|
-
music_id,
|
|
4995
|
+
music_id: item.mid,
|
|
5096
4996
|
music_diff: difficultyData ? difficultyData.difnum : 0,
|
|
5097
4997
|
music_diff_name: getDiffName(diffStr, infVer),
|
|
5098
4998
|
music_diff_full_name: getDiffFullName(diffStr, infVer),
|
|
5099
|
-
score,
|
|
5100
|
-
exscore,
|
|
5101
|
-
clear_type:
|
|
5102
|
-
score_grade: getGradeByScore(score),
|
|
5103
|
-
btn_rate:
|
|
5104
|
-
long_rate:
|
|
5105
|
-
vol_rate:
|
|
4999
|
+
score: item.score,
|
|
5000
|
+
exscore: item.exscore,
|
|
5001
|
+
clear_type: this.getClearType(item.clearType),
|
|
5002
|
+
score_grade: getGradeByScore(item.score),
|
|
5003
|
+
btn_rate: "0",
|
|
5004
|
+
long_rate: "0",
|
|
5005
|
+
vol_rate: "0"
|
|
5106
5006
|
},
|
|
5107
5007
|
extra: {
|
|
5008
|
+
volforce: 0,
|
|
5108
5009
|
play_count: 0,
|
|
5109
|
-
update_at:
|
|
5110
|
-
volforce: 0
|
|
5010
|
+
update_at: item.time
|
|
5111
5011
|
},
|
|
5112
|
-
music_data: music,
|
|
5113
|
-
difficulty_data: difficultyData
|
|
5012
|
+
music_data: music || null,
|
|
5013
|
+
difficulty_data: difficultyData || null
|
|
5114
5014
|
};
|
|
5115
5015
|
scoreObj.extra.volforce = calculateVolforce(scoreObj);
|
|
5116
5016
|
return scoreObj;
|
|
5117
5017
|
});
|
|
5018
|
+
return scores;
|
|
5118
5019
|
}
|
|
5119
|
-
|
|
5120
|
-
|
|
5121
|
-
|
|
5122
|
-
|
|
5123
|
-
|
|
5124
|
-
|
|
5125
|
-
|
|
5126
|
-
|
|
5127
|
-
|
|
5128
|
-
|
|
5129
|
-
|
|
5020
|
+
};
|
|
5021
|
+
|
|
5022
|
+
// src/games/sdvx/adapters/official.ts
|
|
5023
|
+
var OfficialSDVXService = class _OfficialSDVXService {
|
|
5024
|
+
static {
|
|
5025
|
+
__name(this, "OfficialSDVXService");
|
|
5026
|
+
}
|
|
5027
|
+
static instance;
|
|
5028
|
+
logger;
|
|
5029
|
+
constructor(logger5) {
|
|
5030
|
+
this.logger = logger5;
|
|
5031
|
+
}
|
|
5032
|
+
static getInstance(logger5) {
|
|
5033
|
+
if (!_OfficialSDVXService.instance) {
|
|
5034
|
+
_OfficialSDVXService.instance = new _OfficialSDVXService(logger5);
|
|
5035
|
+
}
|
|
5036
|
+
return _OfficialSDVXService.instance;
|
|
5037
|
+
}
|
|
5038
|
+
mapClearType(mark) {
|
|
5039
|
+
const markMap = {
|
|
5040
|
+
"s-puc": "S-PUC",
|
|
5041
|
+
spuc: "S-PUC",
|
|
5042
|
+
puc: "PUC",
|
|
5043
|
+
uc: "UC",
|
|
5044
|
+
mc: "MC",
|
|
5045
|
+
hc: "HC",
|
|
5046
|
+
nc: "NC",
|
|
5047
|
+
none: "PLAYED"
|
|
5130
5048
|
};
|
|
5131
|
-
return
|
|
5049
|
+
return markMap[mark.toLowerCase()] || "PLAYED";
|
|
5050
|
+
}
|
|
5051
|
+
async getUserName(ctx, url, player_id) {
|
|
5052
|
+
const response = await ctx.http.get(`/api/sdvx/profile?id=${player_id}`, {
|
|
5053
|
+
baseURL: url,
|
|
5054
|
+
responseType: "json"
|
|
5055
|
+
});
|
|
5056
|
+
return response.player_name || player_id;
|
|
5057
|
+
}
|
|
5058
|
+
async getAllScore(ctx, url, player_id, config) {
|
|
5059
|
+
try {
|
|
5060
|
+
const response = await ctx.http.get(`/api/sdvx/scores?id=${player_id}`, {
|
|
5061
|
+
baseURL: url,
|
|
5062
|
+
responseType: "json"
|
|
5063
|
+
});
|
|
5064
|
+
const scoresData = response;
|
|
5065
|
+
const stringMusicTitles = [...new Set(scoresData.map((score) => score.title))];
|
|
5066
|
+
const musicService = MusicService.getInstance(config);
|
|
5067
|
+
const [musicDataList, titleMidPairs] = await Promise.all([
|
|
5068
|
+
musicService.getMusic(ctx, stringMusicTitles),
|
|
5069
|
+
musicService.extTitleToMid(ctx, stringMusicTitles)
|
|
5070
|
+
]);
|
|
5071
|
+
const titleMidMap = new Map(
|
|
5072
|
+
titleMidPairs.filter(({ mid }) => !!mid).map(({ title, mid }) => [title.toLowerCase(), mid])
|
|
5073
|
+
);
|
|
5074
|
+
const musicDataMap = new Map(musicDataList.map((music) => [music.title_name, music]));
|
|
5075
|
+
const sdvxScores = [];
|
|
5076
|
+
for (const scoreData of scoresData) {
|
|
5077
|
+
const musicData = musicDataMap.get(scoreData.title);
|
|
5078
|
+
const mappedMid = titleMidMap.get(scoreData.title.toLowerCase());
|
|
5079
|
+
const musicId = mappedMid && /^\d+$/.test(mappedMid) ? Number(mappedMid) : scoreData.music_id;
|
|
5080
|
+
for (const [diffKey, diffScore] of Object.entries(scoreData.difficulties)) {
|
|
5081
|
+
const diffAbbr = diffKey.toUpperCase();
|
|
5082
|
+
const diffInfo = getDiffStringFromAbbr(diffAbbr);
|
|
5083
|
+
const diffStr = diffInfo?.diffStr || diffKey.toLowerCase();
|
|
5084
|
+
const infVer = diffInfo?.infVer ?? musicData?.inf_ver ?? 0;
|
|
5085
|
+
let difficultyData = null;
|
|
5086
|
+
if (musicData && musicData.difficulty) {
|
|
5087
|
+
difficultyData = musicData.difficulty.find(
|
|
5088
|
+
(d) => d.difstr.toLowerCase() === diffStr.toLowerCase()
|
|
5089
|
+
) || null;
|
|
5090
|
+
}
|
|
5091
|
+
const scoreObj = {
|
|
5092
|
+
music: {
|
|
5093
|
+
music_id: musicId,
|
|
5094
|
+
music_diff: difficultyData?.difnum || 0,
|
|
5095
|
+
music_diff_name: getDiffName(diffStr, infVer),
|
|
5096
|
+
music_diff_full_name: getDiffFullName(diffStr, infVer),
|
|
5097
|
+
score: diffScore.score,
|
|
5098
|
+
exscore: 0,
|
|
5099
|
+
clear_type: this.mapClearType(diffScore.mark),
|
|
5100
|
+
score_grade: diffScore.grade,
|
|
5101
|
+
btn_rate: "0",
|
|
5102
|
+
long_rate: "0",
|
|
5103
|
+
vol_rate: "0"
|
|
5104
|
+
},
|
|
5105
|
+
extra: {
|
|
5106
|
+
volforce: 0,
|
|
5107
|
+
play_count: 0,
|
|
5108
|
+
update_at: ""
|
|
5109
|
+
},
|
|
5110
|
+
music_data: musicData || null,
|
|
5111
|
+
difficulty_data: difficultyData || null
|
|
5112
|
+
};
|
|
5113
|
+
scoreObj.extra.volforce = calculateVolforce(scoreObj);
|
|
5114
|
+
sdvxScores.push(scoreObj);
|
|
5115
|
+
}
|
|
5116
|
+
}
|
|
5117
|
+
return sdvxScores;
|
|
5118
|
+
} catch (error) {
|
|
5119
|
+
if (error.response?.status === 404) {
|
|
5120
|
+
return [];
|
|
5121
|
+
}
|
|
5122
|
+
throw error;
|
|
5123
|
+
}
|
|
5132
5124
|
}
|
|
5133
|
-
|
|
5134
|
-
|
|
5135
|
-
|
|
5136
|
-
|
|
5137
|
-
|
|
5138
|
-
|
|
5125
|
+
async getScore(ctx, url, player_id, musicId, config) {
|
|
5126
|
+
const allScores = await this.getAllScore(ctx, url, player_id, config);
|
|
5127
|
+
const score = allScores.find((s) => s.music.music_id === musicId);
|
|
5128
|
+
if (!score) {
|
|
5129
|
+
throw new Error(`Score not found for music ID: ${musicId}`);
|
|
5130
|
+
}
|
|
5131
|
+
return score;
|
|
5139
5132
|
}
|
|
5140
|
-
|
|
5141
|
-
|
|
5142
|
-
gameServices = {};
|
|
5143
|
-
logger = new import_koishi13.Logger("Noah-Asphyxia");
|
|
5144
|
-
/**
|
|
5145
|
-
* 初始化各个游戏服务实例
|
|
5146
|
-
*/
|
|
5147
|
-
constructor() {
|
|
5148
|
-
this.gameServices["sdvx"] = SDVXService.getInstance(this.logger);
|
|
5149
|
-
this.gameServices["iidx"] = IIDXService.getInstance(this.logger);
|
|
5133
|
+
async getRecentScores(ctx, url, player_id, config, count = 1) {
|
|
5134
|
+
return [];
|
|
5150
5135
|
}
|
|
5151
5136
|
};
|
|
5152
5137
|
|
|
5153
|
-
// src/
|
|
5138
|
+
// src/games/sdvx/adapters/index.ts
|
|
5139
|
+
function registerSDVXAdapters(logger5, config) {
|
|
5140
|
+
const serverManager = ServerManager.getInstance();
|
|
5141
|
+
serverManager.registerGameService(
|
|
5142
|
+
"asphyxia",
|
|
5143
|
+
"sdvx",
|
|
5144
|
+
AsphyxiaSDVXService.getInstance(logger5, config)
|
|
5145
|
+
);
|
|
5146
|
+
serverManager.registerGameService("mao", "sdvx", MaoSDVXService.getInstance(logger5));
|
|
5147
|
+
serverManager.registerGameService("official", "sdvx", OfficialSDVXService.getInstance(logger5));
|
|
5148
|
+
}
|
|
5149
|
+
__name(registerSDVXAdapters, "registerSDVXAdapters");
|
|
5150
|
+
|
|
5151
|
+
// src/games/sdvx/command.ts
|
|
5152
|
+
var command_exports2 = {};
|
|
5153
|
+
__export(command_exports2, {
|
|
5154
|
+
apply: () => apply10,
|
|
5155
|
+
name: () => name10
|
|
5156
|
+
});
|
|
5157
|
+
|
|
5158
|
+
// src/games/sdvx/commands/calculate.ts
|
|
5154
5159
|
var import_koishi14 = require("koishi");
|
|
5155
5160
|
|
|
5156
|
-
// src/
|
|
5157
|
-
|
|
5158
|
-
|
|
5159
|
-
|
|
5161
|
+
// src/games/sdvx/utils/param-parser.ts
|
|
5162
|
+
function parseNumberWithSuffix(str) {
|
|
5163
|
+
const num = parseInt(str, 10);
|
|
5164
|
+
if (str.endsWith("w")) {
|
|
5165
|
+
return num * 1e4;
|
|
5166
|
+
} else if (str.endsWith("k")) {
|
|
5167
|
+
return num * 1e3;
|
|
5160
5168
|
}
|
|
5161
|
-
|
|
5162
|
-
|
|
5163
|
-
|
|
5164
|
-
|
|
5169
|
+
return num;
|
|
5170
|
+
}
|
|
5171
|
+
__name(parseNumberWithSuffix, "parseNumberWithSuffix");
|
|
5172
|
+
function isValidLevel(level) {
|
|
5173
|
+
if (level < 1 || level > 20.9) {
|
|
5174
|
+
return false;
|
|
5165
5175
|
}
|
|
5166
|
-
|
|
5167
|
-
|
|
5168
|
-
|
|
5176
|
+
const integerPart = Math.floor(level);
|
|
5177
|
+
const decimalPart = level - integerPart;
|
|
5178
|
+
if (integerPart >= 1 && integerPart <= 16) {
|
|
5179
|
+
return decimalPart === 0;
|
|
5180
|
+
}
|
|
5181
|
+
if (integerPart === 17) {
|
|
5182
|
+
return decimalPart === 0 || decimalPart === 0.5;
|
|
5183
|
+
}
|
|
5184
|
+
if (integerPart >= 18 && integerPart <= 20) {
|
|
5185
|
+
const tenths = Math.round(decimalPart * 10);
|
|
5186
|
+
return Math.abs(decimalPart * 10 - tenths) < 1e-4 && tenths >= 0 && tenths <= 9;
|
|
5187
|
+
}
|
|
5188
|
+
return false;
|
|
5189
|
+
}
|
|
5190
|
+
__name(isValidLevel, "isValidLevel");
|
|
5191
|
+
function generateValidLevelRange(start, end) {
|
|
5192
|
+
const levels = [];
|
|
5193
|
+
const startInt = Math.floor(start);
|
|
5194
|
+
const endInt = Math.floor(end);
|
|
5195
|
+
const startTenth = Math.round((start - startInt) * 10);
|
|
5196
|
+
const endTenth = Math.round((end - endInt) * 10);
|
|
5197
|
+
for (let intPart = startInt; intPart <= endInt; intPart++) {
|
|
5198
|
+
if (intPart >= 1 && intPart <= 16) {
|
|
5199
|
+
if (intPart === startInt && startTenth > 0 || intPart === endInt && endTenth > 0) {
|
|
5200
|
+
continue;
|
|
5201
|
+
}
|
|
5202
|
+
levels.push(intPart);
|
|
5203
|
+
} else if (intPart === 17) {
|
|
5204
|
+
const currentStartTenth = intPart === startInt ? startTenth : 0;
|
|
5205
|
+
const currentEndTenth = intPart === endInt ? endTenth : 5;
|
|
5206
|
+
if (currentStartTenth <= 0 && currentEndTenth >= 0) {
|
|
5207
|
+
levels.push(17);
|
|
5208
|
+
}
|
|
5209
|
+
if (currentStartTenth <= 5 && currentEndTenth >= 5) {
|
|
5210
|
+
levels.push(17.5);
|
|
5211
|
+
}
|
|
5212
|
+
} else if (intPart >= 18 && intPart <= 20) {
|
|
5213
|
+
const currentStartTenth = intPart === startInt ? startTenth : 0;
|
|
5214
|
+
const currentEndTenth = intPart === endInt ? endTenth : 9;
|
|
5215
|
+
for (let tenth = currentStartTenth; tenth <= currentEndTenth && tenth <= 9; tenth++) {
|
|
5216
|
+
levels.push(intPart + tenth / 10);
|
|
5217
|
+
}
|
|
5169
5218
|
}
|
|
5170
|
-
return _SDVXService.instance;
|
|
5171
5219
|
}
|
|
5172
|
-
|
|
5173
|
-
|
|
5174
|
-
|
|
5175
|
-
|
|
5176
|
-
|
|
5177
|
-
|
|
5178
|
-
const
|
|
5179
|
-
|
|
5180
|
-
|
|
5181
|
-
|
|
5182
|
-
|
|
5183
|
-
|
|
5184
|
-
|
|
5185
|
-
}
|
|
5186
|
-
return diffStrMap[diffType] || "novice";
|
|
5220
|
+
return levels;
|
|
5221
|
+
}
|
|
5222
|
+
__name(generateValidLevelRange, "generateValidLevelRange");
|
|
5223
|
+
function parseLevelRange(item) {
|
|
5224
|
+
if (/^\d{1,2}(\.\d)?-\d{1,2}(\.\d)?$/.test(item)) {
|
|
5225
|
+
const [startRaw, endRaw] = item.split("-");
|
|
5226
|
+
const start = parseFloat(startRaw.includes(".") ? startRaw : startRaw + ".0");
|
|
5227
|
+
const end = parseFloat(endRaw.includes(".") ? endRaw : endRaw + ".0");
|
|
5228
|
+
if (start >= 1 && end <= 20.9 && start <= end) {
|
|
5229
|
+
if (!isValidLevel(start) || !isValidLevel(end)) {
|
|
5230
|
+
return null;
|
|
5231
|
+
}
|
|
5232
|
+
return generateValidLevelRange(start, end);
|
|
5233
|
+
}
|
|
5187
5234
|
}
|
|
5188
|
-
|
|
5189
|
-
|
|
5190
|
-
|
|
5191
|
-
|
|
5192
|
-
* @param cardId Card ID of the player
|
|
5193
|
-
* @returns The player ID (e.g., "ED*")
|
|
5194
|
-
*/
|
|
5195
|
-
async getUserName(ctx, url, cardId) {
|
|
5196
|
-
const response = await ctx.http.get(`/my?card=${cardId}`, {
|
|
5197
|
-
baseURL: ctx.globalConfig.maoServerUrl,
|
|
5198
|
-
responseType: "text"
|
|
5199
|
-
});
|
|
5200
|
-
const match = response.match(/猫网玩家\[([^\]]+)\]/);
|
|
5201
|
-
if (!match || !match[1]) {
|
|
5202
|
-
throw new Error("Failed to extract player ID from response");
|
|
5235
|
+
if (/^\d{1,2}(\.\d)?$/.test(item)) {
|
|
5236
|
+
const level = parseFloat(item.includes(".") ? item : item + ".0");
|
|
5237
|
+
if (isValidLevel(level)) {
|
|
5238
|
+
return [level];
|
|
5203
5239
|
}
|
|
5204
|
-
return match[1];
|
|
5205
5240
|
}
|
|
5206
|
-
|
|
5207
|
-
|
|
5208
|
-
|
|
5209
|
-
|
|
5210
|
-
|
|
5211
|
-
|
|
5212
|
-
|
|
5213
|
-
|
|
5214
|
-
|
|
5215
|
-
|
|
5216
|
-
if (!apiKey) {
|
|
5217
|
-
this.logger.warn("maoApiKey not configured");
|
|
5218
|
-
return false;
|
|
5241
|
+
return null;
|
|
5242
|
+
}
|
|
5243
|
+
__name(parseLevelRange, "parseLevelRange");
|
|
5244
|
+
function parseScoreRange(item) {
|
|
5245
|
+
if (/^\d+[wk]?-\d+[wk]?$/.test(item)) {
|
|
5246
|
+
const [startRaw, endRaw] = item.split("-");
|
|
5247
|
+
const start = parseNumberWithSuffix(startRaw);
|
|
5248
|
+
const end = parseNumberWithSuffix(endRaw);
|
|
5249
|
+
if (start >= 0 && end <= 1e7 && start <= end) {
|
|
5250
|
+
return [start, end];
|
|
5219
5251
|
}
|
|
5220
|
-
const resp = await ctx.http.get(
|
|
5221
|
-
`/bot/v2/player/card?card=${cardId}`,
|
|
5222
|
-
{
|
|
5223
|
-
baseURL: ctx.globalConfig.maoServerUrl,
|
|
5224
|
-
headers: { "X-API-Key": apiKey }
|
|
5225
|
-
}
|
|
5226
|
-
);
|
|
5227
|
-
return resp?.code === 0 && resp?.data?.password === pin;
|
|
5228
5252
|
}
|
|
5229
|
-
|
|
5230
|
-
|
|
5231
|
-
|
|
5232
|
-
|
|
5233
|
-
|
|
5234
|
-
case "S-PUC":
|
|
5235
|
-
return 5;
|
|
5236
|
-
case "UC":
|
|
5237
|
-
return 4;
|
|
5238
|
-
case "HC":
|
|
5239
|
-
return 3;
|
|
5240
|
-
case "NC":
|
|
5241
|
-
return 2;
|
|
5242
|
-
case "PLAYED":
|
|
5243
|
-
return 1;
|
|
5244
|
-
default:
|
|
5245
|
-
return 0;
|
|
5253
|
+
if (/^\d+[wk]?$/.test(item)) {
|
|
5254
|
+
const score = parseNumberWithSuffix(item);
|
|
5255
|
+
const hasSuffix = /[wk]$/.test(item);
|
|
5256
|
+
if (score >= 0 && score <= 1e7 && (score >= 100 || hasSuffix)) {
|
|
5257
|
+
return [score];
|
|
5246
5258
|
}
|
|
5247
5259
|
}
|
|
5248
|
-
|
|
5249
|
-
|
|
5250
|
-
|
|
5251
|
-
|
|
5252
|
-
|
|
5253
|
-
|
|
5254
|
-
|
|
5255
|
-
|
|
5256
|
-
|
|
5257
|
-
|
|
5258
|
-
|
|
5259
|
-
|
|
5260
|
-
|
|
5261
|
-
|
|
5262
|
-
|
|
5263
|
-
|
|
5264
|
-
|
|
5265
|
-
|
|
5266
|
-
|
|
5267
|
-
|
|
5260
|
+
return null;
|
|
5261
|
+
}
|
|
5262
|
+
__name(parseScoreRange, "parseScoreRange");
|
|
5263
|
+
function parseVfValue(item) {
|
|
5264
|
+
if (/^\d{1,2}\.\d{2}-\d{1,2}\.\d{2}$/.test(item)) {
|
|
5265
|
+
const [start, end] = item.split("-").map(parseFloat);
|
|
5266
|
+
if (start >= 0 && end <= 99 && start <= end) {
|
|
5267
|
+
return [start, end];
|
|
5268
|
+
}
|
|
5269
|
+
}
|
|
5270
|
+
if (/^\d{1,2}\.\d{2}$/.test(item)) {
|
|
5271
|
+
const vf2 = parseFloat(item);
|
|
5272
|
+
if (vf2 >= 0 && vf2 <= 99) {
|
|
5273
|
+
return vf2;
|
|
5274
|
+
}
|
|
5275
|
+
}
|
|
5276
|
+
return null;
|
|
5277
|
+
}
|
|
5278
|
+
__name(parseVfValue, "parseVfValue");
|
|
5279
|
+
function parseClearTypeRange(item) {
|
|
5280
|
+
if (item.includes("-")) {
|
|
5281
|
+
const [startRaw, endRaw] = item.split("-").filter(Boolean);
|
|
5282
|
+
if (startRaw && endRaw && CLEAR_TYPE_ABBR_MAP[startRaw] && CLEAR_TYPE_ABBR_MAP[endRaw]) {
|
|
5283
|
+
const start = ALL_CLEAR_TYPES.findIndex((t) => t === CLEAR_TYPE_ABBR_MAP[startRaw]);
|
|
5284
|
+
const end = ALL_CLEAR_TYPES.findIndex((t) => t === CLEAR_TYPE_ABBR_MAP[endRaw]);
|
|
5285
|
+
if (start !== -1 && end !== -1) {
|
|
5286
|
+
const from = Math.min(start, end);
|
|
5287
|
+
const to = Math.max(start, end);
|
|
5288
|
+
const clearTypes = [];
|
|
5289
|
+
for (let i = from; i <= to; i++) {
|
|
5290
|
+
clearTypes.push(ALL_CLEAR_TYPES[i]);
|
|
5291
|
+
}
|
|
5292
|
+
if (clearTypes.includes("PUC") && !clearTypes.includes("S-PUC")) {
|
|
5293
|
+
clearTypes.push("S-PUC");
|
|
5294
|
+
}
|
|
5295
|
+
return clearTypes;
|
|
5296
|
+
}
|
|
5268
5297
|
}
|
|
5269
5298
|
}
|
|
5270
|
-
|
|
5271
|
-
|
|
5272
|
-
|
|
5273
|
-
|
|
5274
|
-
|
|
5299
|
+
return null;
|
|
5300
|
+
}
|
|
5301
|
+
__name(parseClearTypeRange, "parseClearTypeRange");
|
|
5302
|
+
function parseSingleClearType(item) {
|
|
5303
|
+
if (CLEAR_TYPE_ABBR_MAP[item.toLowerCase()]) {
|
|
5304
|
+
const clearType = CLEAR_TYPE_ABBR_MAP[item.toLowerCase()];
|
|
5305
|
+
if (clearType === "PUC") {
|
|
5306
|
+
return ["PUC", "S-PUC"];
|
|
5275
5307
|
}
|
|
5276
|
-
|
|
5277
|
-
const musicId = Number(s.music.music_id);
|
|
5278
|
-
return musicId > 0 && s.music.music_diff_name;
|
|
5279
|
-
}).map((s) => ({
|
|
5280
|
-
music_id: Number(s.music.music_id),
|
|
5281
|
-
music_type: this.diffNameToMusicType(s.music.music_diff_name),
|
|
5282
|
-
score: s.music.score,
|
|
5283
|
-
exscore: s.music.exscore,
|
|
5284
|
-
clear_type: this.clearTypeToNum(s.music.clear_type)
|
|
5285
|
-
}));
|
|
5286
|
-
if (payload.length === 0) return false;
|
|
5287
|
-
const resp = await ctx.http.post(
|
|
5288
|
-
"/bot/v2/sdvx/upload",
|
|
5289
|
-
{ card: cardId, scores: payload },
|
|
5290
|
-
{
|
|
5291
|
-
baseURL: ctx.globalConfig.maoServerUrl,
|
|
5292
|
-
headers: { "X-API-Key": apiKey }
|
|
5293
|
-
}
|
|
5294
|
-
);
|
|
5295
|
-
return resp?.code === 0;
|
|
5308
|
+
return [clearType];
|
|
5296
5309
|
}
|
|
5297
|
-
|
|
5298
|
-
|
|
5299
|
-
|
|
5300
|
-
|
|
5301
|
-
|
|
5302
|
-
|
|
5303
|
-
|
|
5304
|
-
|
|
5305
|
-
|
|
5306
|
-
|
|
5307
|
-
|
|
5308
|
-
|
|
5309
|
-
|
|
5310
|
-
|
|
5311
|
-
const filteredData = resp.data.filter(
|
|
5312
|
-
(score) => score.mid !== 244 && score.mid !== 1759 && score.mid !== 1761 && score.isPlus === 0
|
|
5313
|
-
);
|
|
5314
|
-
const musicIds = filteredData.map((score) => score.mid);
|
|
5315
|
-
const diffTypes = filteredData.map((score) => score.musicType);
|
|
5316
|
-
const musicService = MusicService.getInstance(config);
|
|
5317
|
-
const musicData = await musicService.getMusic(ctx, musicIds);
|
|
5318
|
-
const musicDataMap = new Map(musicData.map((music) => [music.id, music]));
|
|
5319
|
-
const scores = filteredData.map((score, index) => {
|
|
5320
|
-
const musicId = score.mid;
|
|
5321
|
-
const music = musicDataMap.get(musicId);
|
|
5322
|
-
const musicDiffType = score.musicType;
|
|
5323
|
-
const diffStr = this.getDifficultyString(musicDiffType);
|
|
5324
|
-
let difficultyData = null;
|
|
5325
|
-
if (music && music.difficulty && music.difficulty.length > 0) {
|
|
5326
|
-
difficultyData = music.difficulty.find((diff) => diff.difstr.toLowerCase() === diffStr) || null;
|
|
5327
|
-
}
|
|
5328
|
-
const infVer = music ? music.inf_ver : 2;
|
|
5329
|
-
const scoreObj = {
|
|
5330
|
-
music: {
|
|
5331
|
-
music_id: musicId,
|
|
5332
|
-
music_diff: difficultyData ? difficultyData.difnum : 0,
|
|
5333
|
-
music_diff_name: getDiffName(diffStr, infVer),
|
|
5334
|
-
music_diff_full_name: getDiffFullName(diffStr, infVer),
|
|
5335
|
-
score: score.score,
|
|
5336
|
-
exscore: score.exscore,
|
|
5337
|
-
clear_type: getSDVXClearType(score.clearType),
|
|
5338
|
-
score_grade: getGradeByScore(score.score),
|
|
5339
|
-
btn_rate: score.btnRate.toString(),
|
|
5340
|
-
long_rate: score.longRate.toString(),
|
|
5341
|
-
vol_rate: score.volRate.toString()
|
|
5342
|
-
},
|
|
5343
|
-
extra: {
|
|
5344
|
-
play_count: score.playCount,
|
|
5345
|
-
update_at: score.updateAt,
|
|
5346
|
-
volforce: 0
|
|
5347
|
-
},
|
|
5348
|
-
music_data: music || null,
|
|
5349
|
-
difficulty_data: difficultyData || null
|
|
5350
|
-
};
|
|
5351
|
-
scoreObj.extra.volforce = calculateVolforce(scoreObj);
|
|
5352
|
-
return scoreObj;
|
|
5353
|
-
});
|
|
5354
|
-
return scores;
|
|
5355
|
-
} catch (error) {
|
|
5356
|
-
if (error.response?.status === 404) {
|
|
5357
|
-
return [];
|
|
5310
|
+
return null;
|
|
5311
|
+
}
|
|
5312
|
+
__name(parseSingleClearType, "parseSingleClearType");
|
|
5313
|
+
function parseGradeRange(item) {
|
|
5314
|
+
if (/^[a-ds][a-z+]*-[a-ds][a-z+]*$/.test(item)) {
|
|
5315
|
+
const [startRaw, endRaw] = item.split("-");
|
|
5316
|
+
const start = ALL_GRADES.findIndex((g) => g.toLowerCase() === startRaw);
|
|
5317
|
+
const end = ALL_GRADES.findIndex((g) => g.toLowerCase() === endRaw);
|
|
5318
|
+
if (start !== -1 && end !== -1) {
|
|
5319
|
+
const from = Math.min(start, end);
|
|
5320
|
+
const to = Math.max(start, end);
|
|
5321
|
+
const grades = [];
|
|
5322
|
+
for (let i = from; i <= to; i++) {
|
|
5323
|
+
grades.push(ALL_GRADES[i]);
|
|
5358
5324
|
}
|
|
5359
|
-
|
|
5325
|
+
return grades;
|
|
5360
5326
|
}
|
|
5361
5327
|
}
|
|
5362
|
-
|
|
5363
|
-
|
|
5364
|
-
|
|
5365
|
-
|
|
5366
|
-
|
|
5328
|
+
return null;
|
|
5329
|
+
}
|
|
5330
|
+
__name(parseGradeRange, "parseGradeRange");
|
|
5331
|
+
function parseSingleGrade(item) {
|
|
5332
|
+
const grade = ALL_GRADES.find((g) => g.toLowerCase() === item.toLowerCase());
|
|
5333
|
+
if (grade) {
|
|
5334
|
+
return [grade];
|
|
5335
|
+
}
|
|
5336
|
+
return null;
|
|
5337
|
+
}
|
|
5338
|
+
__name(parseSingleGrade, "parseSingleGrade");
|
|
5339
|
+
function parseRadarFeature(item) {
|
|
5340
|
+
const radarList = ["notes", "peak", "tsumami", "tricky", "hand_trip", "one_hand"];
|
|
5341
|
+
if (radarList.includes(item)) {
|
|
5342
|
+
return item;
|
|
5343
|
+
}
|
|
5344
|
+
return null;
|
|
5345
|
+
}
|
|
5346
|
+
__name(parseRadarFeature, "parseRadarFeature");
|
|
5347
|
+
function parseQueryInput(input) {
|
|
5348
|
+
const params = {};
|
|
5349
|
+
if (!input || input.trim() === "") {
|
|
5350
|
+
return params;
|
|
5351
|
+
}
|
|
5352
|
+
const items = input.split(/\s+/).map((s) => s.trim()).filter(Boolean);
|
|
5353
|
+
for (const item of items) {
|
|
5354
|
+
const vf2 = parseVfValue(item);
|
|
5355
|
+
if (vf2 !== null) {
|
|
5356
|
+
if (params.vf !== void 0) {
|
|
5357
|
+
const existing = Array.isArray(params.vf) ? params.vf : [params.vf];
|
|
5358
|
+
const newVf = Array.isArray(vf2) ? vf2 : [vf2];
|
|
5359
|
+
params.vf = [...existing, ...newVf];
|
|
5360
|
+
} else {
|
|
5361
|
+
params.vf = vf2;
|
|
5362
|
+
}
|
|
5363
|
+
continue;
|
|
5367
5364
|
}
|
|
5368
|
-
const
|
|
5369
|
-
|
|
5370
|
-
|
|
5371
|
-
|
|
5372
|
-
|
|
5373
|
-
|
|
5374
|
-
|
|
5375
|
-
const music2 = musicData2.length > 0 ? musicData2[0] : null;
|
|
5376
|
-
const diffStr2 = "novice";
|
|
5377
|
-
const infVer2 = music2 ? music2.inf_ver : 2;
|
|
5378
|
-
let difficultyData2 = null;
|
|
5379
|
-
if (music2 && music2.difficulty && music2.difficulty.length > 0) {
|
|
5380
|
-
difficultyData2 = music2.difficulty.find((diff) => diff.difstr.toLowerCase() === diffStr2) || music2.difficulty[0];
|
|
5365
|
+
const levels = parseLevelRange(item);
|
|
5366
|
+
if (levels !== null) {
|
|
5367
|
+
if (params.level !== void 0) {
|
|
5368
|
+
const existing = Array.isArray(params.level) ? params.level : [params.level];
|
|
5369
|
+
params.level = [.../* @__PURE__ */ new Set([...existing, ...levels])];
|
|
5370
|
+
} else {
|
|
5371
|
+
params.level = levels.length === 1 ? levels[0] : levels;
|
|
5381
5372
|
}
|
|
5382
|
-
|
|
5383
|
-
music: {
|
|
5384
|
-
music_id: musicId,
|
|
5385
|
-
music_diff: difficultyData2 ? difficultyData2.difnum : 0,
|
|
5386
|
-
music_diff_name: getDiffName(diffStr2, infVer2),
|
|
5387
|
-
music_diff_full_name: getDiffFullName(diffStr2, infVer2),
|
|
5388
|
-
score: 0,
|
|
5389
|
-
exscore: 0,
|
|
5390
|
-
clear_type: getSDVXClearType(0),
|
|
5391
|
-
// NO PLAY
|
|
5392
|
-
score_grade: getGradeByScore(0),
|
|
5393
|
-
btn_rate: "0",
|
|
5394
|
-
long_rate: "0",
|
|
5395
|
-
vol_rate: "0"
|
|
5396
|
-
},
|
|
5397
|
-
extra: {
|
|
5398
|
-
play_count: 0,
|
|
5399
|
-
update_at: "0",
|
|
5400
|
-
volforce: 0
|
|
5401
|
-
},
|
|
5402
|
-
music_data: music2,
|
|
5403
|
-
difficulty_data: difficultyData2
|
|
5404
|
-
};
|
|
5373
|
+
continue;
|
|
5405
5374
|
}
|
|
5406
|
-
const
|
|
5407
|
-
|
|
5408
|
-
|
|
5409
|
-
|
|
5410
|
-
|
|
5411
|
-
|
|
5412
|
-
|
|
5413
|
-
|
|
5414
|
-
|
|
5375
|
+
const scores = parseScoreRange(item);
|
|
5376
|
+
if (scores !== null) {
|
|
5377
|
+
if (params.score !== void 0) {
|
|
5378
|
+
const existing = Array.isArray(params.score) ? params.score : [params.score];
|
|
5379
|
+
params.score = [.../* @__PURE__ */ new Set([...existing, ...scores])];
|
|
5380
|
+
} else {
|
|
5381
|
+
params.score = scores.length === 1 ? scores[0] : scores;
|
|
5382
|
+
}
|
|
5383
|
+
continue;
|
|
5415
5384
|
}
|
|
5416
|
-
const
|
|
5417
|
-
|
|
5418
|
-
|
|
5419
|
-
|
|
5420
|
-
|
|
5421
|
-
|
|
5422
|
-
|
|
5423
|
-
|
|
5424
|
-
|
|
5425
|
-
|
|
5426
|
-
|
|
5427
|
-
|
|
5428
|
-
|
|
5429
|
-
|
|
5430
|
-
|
|
5431
|
-
|
|
5432
|
-
|
|
5433
|
-
|
|
5434
|
-
|
|
5435
|
-
},
|
|
5436
|
-
music_data: music,
|
|
5437
|
-
difficulty_data: difficultyData
|
|
5438
|
-
};
|
|
5439
|
-
scoreObj.extra.volforce = calculateVolforce(scoreObj);
|
|
5440
|
-
return scoreObj;
|
|
5441
|
-
}
|
|
5442
|
-
async getRecentScores(ctx, url, cardId, config, count = 1) {
|
|
5443
|
-
const apiKey = ctx.globalConfig.maoApiKey;
|
|
5444
|
-
if (!apiKey) {
|
|
5445
|
-
this.logger.warn("maoApiKey not configured");
|
|
5446
|
-
return [];
|
|
5385
|
+
const clearTypes = parseClearTypeRange(item);
|
|
5386
|
+
if (clearTypes !== null) {
|
|
5387
|
+
if (params.clearType) {
|
|
5388
|
+
const existing = Array.isArray(params.clearType) ? params.clearType : [params.clearType];
|
|
5389
|
+
params.clearType = [.../* @__PURE__ */ new Set([...existing, ...clearTypes])];
|
|
5390
|
+
} else {
|
|
5391
|
+
params.clearType = clearTypes;
|
|
5392
|
+
}
|
|
5393
|
+
continue;
|
|
5394
|
+
}
|
|
5395
|
+
const singleClearTypes = parseSingleClearType(item);
|
|
5396
|
+
if (singleClearTypes !== null) {
|
|
5397
|
+
if (params.clearType) {
|
|
5398
|
+
const existing = Array.isArray(params.clearType) ? params.clearType : [params.clearType];
|
|
5399
|
+
params.clearType = [.../* @__PURE__ */ new Set([...existing, ...singleClearTypes])];
|
|
5400
|
+
} else {
|
|
5401
|
+
params.clearType = singleClearTypes;
|
|
5402
|
+
}
|
|
5403
|
+
continue;
|
|
5447
5404
|
}
|
|
5448
|
-
const
|
|
5449
|
-
|
|
5450
|
-
|
|
5451
|
-
|
|
5452
|
-
|
|
5453
|
-
|
|
5405
|
+
const grades = parseGradeRange(item);
|
|
5406
|
+
if (grades !== null) {
|
|
5407
|
+
if (params.grade) {
|
|
5408
|
+
const existing = Array.isArray(params.grade) ? params.grade : [params.grade];
|
|
5409
|
+
params.grade = [.../* @__PURE__ */ new Set([...existing, ...grades])];
|
|
5410
|
+
} else {
|
|
5411
|
+
params.grade = grades;
|
|
5412
|
+
}
|
|
5413
|
+
continue;
|
|
5454
5414
|
}
|
|
5455
|
-
const
|
|
5456
|
-
|
|
5457
|
-
|
|
5458
|
-
|
|
5459
|
-
|
|
5460
|
-
|
|
5461
|
-
|
|
5462
|
-
let difficultyData = null;
|
|
5463
|
-
if (music && music.difficulty && music.difficulty.length > 0) {
|
|
5464
|
-
difficultyData = music.difficulty.find((diff) => diff.difstr.toLowerCase() === diffStr) || null;
|
|
5415
|
+
const singleGrades = parseSingleGrade(item);
|
|
5416
|
+
if (singleGrades !== null) {
|
|
5417
|
+
if (params.grade) {
|
|
5418
|
+
const existing = Array.isArray(params.grade) ? params.grade : [params.grade];
|
|
5419
|
+
params.grade = [.../* @__PURE__ */ new Set([...existing, ...singleGrades])];
|
|
5420
|
+
} else {
|
|
5421
|
+
params.grade = singleGrades[0];
|
|
5465
5422
|
}
|
|
5466
|
-
|
|
5467
|
-
const scoreObj = {
|
|
5468
|
-
music: {
|
|
5469
|
-
music_id: item.mid,
|
|
5470
|
-
music_diff: difficultyData ? difficultyData.difnum : 0,
|
|
5471
|
-
music_diff_name: getDiffName(diffStr, infVer),
|
|
5472
|
-
music_diff_full_name: getDiffFullName(diffStr, infVer),
|
|
5473
|
-
score: item.score,
|
|
5474
|
-
exscore: item.exscore,
|
|
5475
|
-
clear_type: getSDVXClearType(item.clearType),
|
|
5476
|
-
score_grade: getGradeByScore(item.score),
|
|
5477
|
-
btn_rate: "0",
|
|
5478
|
-
long_rate: "0",
|
|
5479
|
-
vol_rate: "0"
|
|
5480
|
-
},
|
|
5481
|
-
extra: {
|
|
5482
|
-
volforce: 0,
|
|
5483
|
-
play_count: 0,
|
|
5484
|
-
update_at: item.time
|
|
5485
|
-
},
|
|
5486
|
-
music_data: music || null,
|
|
5487
|
-
difficulty_data: difficultyData || null
|
|
5488
|
-
};
|
|
5489
|
-
scoreObj.extra.volforce = calculateVolforce(scoreObj);
|
|
5490
|
-
return scoreObj;
|
|
5491
|
-
});
|
|
5492
|
-
return scores;
|
|
5493
|
-
}
|
|
5494
|
-
};
|
|
5495
|
-
|
|
5496
|
-
// src/servers/Mao/index.ts
|
|
5497
|
-
var Mao = class {
|
|
5498
|
-
static {
|
|
5499
|
-
__name(this, "Mao");
|
|
5500
|
-
}
|
|
5501
|
-
name = "mao";
|
|
5502
|
-
supportedGames = ["sdvx"];
|
|
5503
|
-
gameServices = {};
|
|
5504
|
-
logger = new import_koishi14.Logger("Noah-Mao");
|
|
5505
|
-
/**
|
|
5506
|
-
* 初始化SDVX游戏服务实例
|
|
5507
|
-
*/
|
|
5508
|
-
constructor() {
|
|
5509
|
-
this.gameServices["sdvx"] = SDVXService2.getInstance(this.logger);
|
|
5510
|
-
}
|
|
5511
|
-
};
|
|
5512
|
-
|
|
5513
|
-
// src/servers/Official/index.ts
|
|
5514
|
-
var import_koishi15 = require("koishi");
|
|
5515
|
-
|
|
5516
|
-
// src/servers/Official/services/sdvx-service.ts
|
|
5517
|
-
var SDVXService3 = class _SDVXService {
|
|
5518
|
-
static {
|
|
5519
|
-
__name(this, "SDVXService");
|
|
5520
|
-
}
|
|
5521
|
-
static instance;
|
|
5522
|
-
logger;
|
|
5523
|
-
constructor(logger5) {
|
|
5524
|
-
this.logger = logger5;
|
|
5525
|
-
}
|
|
5526
|
-
static getInstance(logger5) {
|
|
5527
|
-
if (!_SDVXService.instance) {
|
|
5528
|
-
_SDVXService.instance = new _SDVXService(logger5);
|
|
5423
|
+
continue;
|
|
5529
5424
|
}
|
|
5530
|
-
return _SDVXService.instance;
|
|
5531
|
-
}
|
|
5532
|
-
/**
|
|
5533
|
-
* 映射 mark 到 SDVXClearType
|
|
5534
|
-
*/
|
|
5535
|
-
mapClearType(mark) {
|
|
5536
|
-
const markMap = {
|
|
5537
|
-
"s-puc": "S-PUC",
|
|
5538
|
-
spuc: "S-PUC",
|
|
5539
|
-
puc: "PUC",
|
|
5540
|
-
uc: "UC",
|
|
5541
|
-
mc: "MC",
|
|
5542
|
-
hc: "HC",
|
|
5543
|
-
nc: "NC",
|
|
5544
|
-
none: "PLAYED"
|
|
5545
|
-
};
|
|
5546
|
-
return markMap[mark.toLowerCase()] || "PLAYED";
|
|
5547
|
-
}
|
|
5548
|
-
/**
|
|
5549
|
-
* Get the user name/ID from the server response
|
|
5550
|
-
* @param ctx Context object
|
|
5551
|
-
* @param url Base Official Support URL for the API
|
|
5552
|
-
* @param player_id Player ID of the player (SV-xxxx-xxxx format)
|
|
5553
|
-
* @returns The player name
|
|
5554
|
-
*/
|
|
5555
|
-
async getUserName(ctx, url, player_id) {
|
|
5556
|
-
const response = await ctx.http.get(`/api/sdvx/profile?id=${player_id}`, {
|
|
5557
|
-
baseURL: url,
|
|
5558
|
-
responseType: "json"
|
|
5559
|
-
});
|
|
5560
|
-
return response.player_name || player_id;
|
|
5561
5425
|
}
|
|
5562
|
-
|
|
5426
|
+
return params;
|
|
5427
|
+
}
|
|
5428
|
+
__name(parseQueryInput, "parseQueryInput");
|
|
5429
|
+
|
|
5430
|
+
// src/games/sdvx/commands/calculate.ts
|
|
5431
|
+
function calculate(ctx, config, logger5) {
|
|
5432
|
+
ctx.command("sdvx.calculate <query:text>").alias("sdvx.cal").action(async ({ session, options }, query) => {
|
|
5563
5433
|
try {
|
|
5564
|
-
|
|
5565
|
-
|
|
5566
|
-
|
|
5567
|
-
|
|
5568
|
-
|
|
5569
|
-
|
|
5570
|
-
|
|
5571
|
-
const
|
|
5572
|
-
|
|
5573
|
-
|
|
5574
|
-
|
|
5575
|
-
const
|
|
5576
|
-
|
|
5577
|
-
|
|
5578
|
-
|
|
5579
|
-
const
|
|
5580
|
-
|
|
5581
|
-
|
|
5582
|
-
|
|
5583
|
-
|
|
5584
|
-
|
|
5585
|
-
|
|
5586
|
-
|
|
5587
|
-
|
|
5588
|
-
|
|
5589
|
-
|
|
5590
|
-
|
|
5591
|
-
|
|
5592
|
-
|
|
5593
|
-
) || null;
|
|
5594
|
-
}
|
|
5595
|
-
const scoreObj = {
|
|
5596
|
-
music: {
|
|
5597
|
-
music_id: musicId,
|
|
5598
|
-
music_diff: difficultyData?.difnum || 0,
|
|
5599
|
-
music_diff_name: getDiffName(diffStr, infVer),
|
|
5600
|
-
music_diff_full_name: getDiffFullName(diffStr, infVer),
|
|
5601
|
-
score: diffScore.score,
|
|
5602
|
-
exscore: 0,
|
|
5603
|
-
// Official 服务器不提供
|
|
5604
|
-
clear_type: this.mapClearType(diffScore.mark),
|
|
5605
|
-
score_grade: diffScore.grade,
|
|
5606
|
-
btn_rate: "0",
|
|
5607
|
-
long_rate: "0",
|
|
5608
|
-
vol_rate: "0"
|
|
5609
|
-
},
|
|
5610
|
-
extra: {
|
|
5611
|
-
volforce: 0,
|
|
5612
|
-
play_count: 0,
|
|
5613
|
-
update_at: ""
|
|
5614
|
-
},
|
|
5615
|
-
music_data: musicData || null,
|
|
5616
|
-
difficulty_data: difficultyData || null
|
|
5617
|
-
};
|
|
5618
|
-
scoreObj.extra.volforce = calculateVolforce(scoreObj);
|
|
5619
|
-
sdvxScores.push(scoreObj);
|
|
5434
|
+
let queryInput = query;
|
|
5435
|
+
if (!queryInput) {
|
|
5436
|
+
await session.send(session.text(".prompt"));
|
|
5437
|
+
queryInput = await session.prompt();
|
|
5438
|
+
if (!queryInput) return session.text("commands.timeout");
|
|
5439
|
+
if (queryInput === "q") return session.text(".quit");
|
|
5440
|
+
}
|
|
5441
|
+
const params = parseQueryInput(queryInput);
|
|
5442
|
+
if (Object.keys(params).length === 0) {
|
|
5443
|
+
return session.text(".invalid-query");
|
|
5444
|
+
}
|
|
5445
|
+
const results = generateQueryResults(params);
|
|
5446
|
+
if (results.length === 0) {
|
|
5447
|
+
return session.text(".no-results");
|
|
5448
|
+
}
|
|
5449
|
+
const maxResults = 500;
|
|
5450
|
+
if (results.length > maxResults) {
|
|
5451
|
+
return session.text(".too-many-results", [results.length]);
|
|
5452
|
+
}
|
|
5453
|
+
session.send(session.text(".drawing"));
|
|
5454
|
+
const drawerManager = DrawerManager.getInstance(ctx);
|
|
5455
|
+
const sdvxDrawer = drawerManager.getDrawer("sdvx");
|
|
5456
|
+
const imageBuffer = await sdvxDrawer.generateVFTableImage(
|
|
5457
|
+
{
|
|
5458
|
+
results,
|
|
5459
|
+
config
|
|
5460
|
+
},
|
|
5461
|
+
{
|
|
5462
|
+
lossless: true
|
|
5620
5463
|
}
|
|
5464
|
+
);
|
|
5465
|
+
return import_koishi14.h.image(imageBuffer, "image/png");
|
|
5466
|
+
} catch (error) {
|
|
5467
|
+
ctx.logger("SDVX-Calculate").warn(error);
|
|
5468
|
+
return session.text(".error");
|
|
5469
|
+
}
|
|
5470
|
+
});
|
|
5471
|
+
}
|
|
5472
|
+
__name(calculate, "calculate");
|
|
5473
|
+
|
|
5474
|
+
// src/games/sdvx/commands/chart.ts
|
|
5475
|
+
var import_koishi15 = require("koishi");
|
|
5476
|
+
function mapArrangementToken(tok) {
|
|
5477
|
+
const t = tok.toLowerCase();
|
|
5478
|
+
if (t === "ran" || t === "random") return "random";
|
|
5479
|
+
if (t === "m" || t === "mirror" || t === "mir") return "mirror";
|
|
5480
|
+
if (t === "sran" || t === "srandom") return "s-random";
|
|
5481
|
+
if (t === "fran" || t === "wtf") return "f-random";
|
|
5482
|
+
return null;
|
|
5483
|
+
}
|
|
5484
|
+
__name(mapArrangementToken, "mapArrangementToken");
|
|
5485
|
+
function mapLeftColorToken(tok) {
|
|
5486
|
+
const m = /^l(blue|red|yellow|green)$/i.exec(tok);
|
|
5487
|
+
return m ? m[1].toUpperCase() : null;
|
|
5488
|
+
}
|
|
5489
|
+
__name(mapLeftColorToken, "mapLeftColorToken");
|
|
5490
|
+
function mapRightColorToken(tok) {
|
|
5491
|
+
const m = /^r(blue|red|yellow|green)$/i.exec(tok);
|
|
5492
|
+
return m ? m[1].toUpperCase() : null;
|
|
5493
|
+
}
|
|
5494
|
+
__name(mapRightColorToken, "mapRightColorToken");
|
|
5495
|
+
function isBarcode(s) {
|
|
5496
|
+
return /^\d+\.[A-Za-z]+$/.test(s);
|
|
5497
|
+
}
|
|
5498
|
+
__name(isBarcode, "isBarcode");
|
|
5499
|
+
function parseDiffAndTitle(rawTokens) {
|
|
5500
|
+
let diffStr = null;
|
|
5501
|
+
let diffIndex = null;
|
|
5502
|
+
const titleParts = [];
|
|
5503
|
+
const fullMap = {
|
|
5504
|
+
novice: "novice",
|
|
5505
|
+
nov: "novice",
|
|
5506
|
+
advanced: "advanced",
|
|
5507
|
+
adv: "advanced",
|
|
5508
|
+
exhaust: "exhaust",
|
|
5509
|
+
exh: "exhaust",
|
|
5510
|
+
infinite: "infinite",
|
|
5511
|
+
inf: "infinite",
|
|
5512
|
+
gravity: "infinite",
|
|
5513
|
+
grv: "infinite",
|
|
5514
|
+
heavenly: "infinite",
|
|
5515
|
+
hvn: "infinite",
|
|
5516
|
+
vivid: "infinite",
|
|
5517
|
+
vvd: "infinite",
|
|
5518
|
+
exceed: "infinite",
|
|
5519
|
+
xcd: "infinite",
|
|
5520
|
+
maximum: "maximum",
|
|
5521
|
+
mxm: "maximum"
|
|
5522
|
+
};
|
|
5523
|
+
for (const tok of rawTokens) {
|
|
5524
|
+
const trimmed = tok.trim();
|
|
5525
|
+
if (!trimmed) continue;
|
|
5526
|
+
if (diffIndex === null && /^[1-5]$/.test(trimmed)) {
|
|
5527
|
+
diffIndex = Number(trimmed) - 1;
|
|
5528
|
+
continue;
|
|
5529
|
+
}
|
|
5530
|
+
const lettersOnly = trimmed.replace(/[^A-Za-z]/g, "");
|
|
5531
|
+
if (lettersOnly) {
|
|
5532
|
+
const byAbbr = getDiffStringFromAbbr(lettersOnly);
|
|
5533
|
+
if (byAbbr) {
|
|
5534
|
+
diffStr = byAbbr.diffStr;
|
|
5535
|
+
continue;
|
|
5621
5536
|
}
|
|
5622
|
-
|
|
5623
|
-
|
|
5624
|
-
|
|
5625
|
-
|
|
5537
|
+
const lower = lettersOnly.toLowerCase();
|
|
5538
|
+
if (fullMap[lower]) {
|
|
5539
|
+
diffStr = fullMap[lower];
|
|
5540
|
+
continue;
|
|
5626
5541
|
}
|
|
5627
|
-
throw error;
|
|
5628
5542
|
}
|
|
5543
|
+
titleParts.push(trimmed);
|
|
5629
5544
|
}
|
|
5630
|
-
|
|
5631
|
-
|
|
5632
|
-
|
|
5633
|
-
|
|
5634
|
-
|
|
5545
|
+
return {
|
|
5546
|
+
diffStr,
|
|
5547
|
+
diffIndex,
|
|
5548
|
+
titleQuery: titleParts.join(" ").trim()
|
|
5549
|
+
};
|
|
5550
|
+
}
|
|
5551
|
+
__name(parseDiffAndTitle, "parseDiffAndTitle");
|
|
5552
|
+
function getHighestDifstr(diffs) {
|
|
5553
|
+
return diffs[diffs.length - 1].difstr;
|
|
5554
|
+
}
|
|
5555
|
+
__name(getHighestDifstr, "getHighestDifstr");
|
|
5556
|
+
function chart(ctx, config, logger5) {
|
|
5557
|
+
ctx.command("sdvx.chart [query:text]").alias("sdvx.c").action(async ({ session }, query) => {
|
|
5558
|
+
if (!query) {
|
|
5559
|
+
await session.send(session.text(".prompt"));
|
|
5560
|
+
query = await session.prompt();
|
|
5561
|
+
if (!query) return session.text("commands.timeout");
|
|
5635
5562
|
}
|
|
5636
|
-
|
|
5637
|
-
|
|
5638
|
-
|
|
5639
|
-
|
|
5640
|
-
|
|
5641
|
-
|
|
5642
|
-
|
|
5643
|
-
|
|
5644
|
-
|
|
5645
|
-
|
|
5646
|
-
|
|
5647
|
-
|
|
5648
|
-
|
|
5649
|
-
|
|
5650
|
-
|
|
5651
|
-
|
|
5652
|
-
|
|
5653
|
-
|
|
5654
|
-
|
|
5655
|
-
|
|
5656
|
-
|
|
5657
|
-
|
|
5658
|
-
}
|
|
5659
|
-
|
|
5660
|
-
// src/servers/ServerFactory.ts
|
|
5661
|
-
var ServerFactory = class {
|
|
5662
|
-
static {
|
|
5663
|
-
__name(this, "ServerFactory");
|
|
5664
|
-
}
|
|
5665
|
-
serverInstances = /* @__PURE__ */ new Map();
|
|
5666
|
-
/**
|
|
5667
|
-
* 根据服务器类型获取服务器实例,如果不存在则创建新实例
|
|
5668
|
-
* @param serverType - 服务器类型
|
|
5669
|
-
* @returns 对应的服务器实例
|
|
5670
|
-
* @throws 当请求不支持的服务器类型时抛出异常
|
|
5671
|
-
*/
|
|
5672
|
-
getServer(serverType) {
|
|
5673
|
-
if (!this.serverInstances.has(serverType)) {
|
|
5674
|
-
let serverInstance;
|
|
5675
|
-
switch (serverType) {
|
|
5676
|
-
case "asphyxia":
|
|
5677
|
-
serverInstance = new Asphyxia();
|
|
5678
|
-
break;
|
|
5679
|
-
case "mao":
|
|
5680
|
-
serverInstance = new Mao();
|
|
5681
|
-
break;
|
|
5682
|
-
case "official":
|
|
5683
|
-
serverInstance = new Official();
|
|
5684
|
-
break;
|
|
5685
|
-
default:
|
|
5686
|
-
throw new Error(`Unsupported server type: ${serverType}`);
|
|
5563
|
+
const musicService = MusicService.getInstance(config);
|
|
5564
|
+
try {
|
|
5565
|
+
let arrangement_mode = "normal";
|
|
5566
|
+
let laser_l_color = "BLUE";
|
|
5567
|
+
let laser_r_color = "RED";
|
|
5568
|
+
const tokens = query.split(/\s+/).filter(Boolean);
|
|
5569
|
+
const remaining = [];
|
|
5570
|
+
for (const tok of tokens) {
|
|
5571
|
+
const arr = mapArrangementToken(tok);
|
|
5572
|
+
if (arr) {
|
|
5573
|
+
arrangement_mode = arr;
|
|
5574
|
+
continue;
|
|
5575
|
+
}
|
|
5576
|
+
const lc = mapLeftColorToken(tok);
|
|
5577
|
+
if (lc) {
|
|
5578
|
+
laser_l_color = lc;
|
|
5579
|
+
continue;
|
|
5580
|
+
}
|
|
5581
|
+
const rc = mapRightColorToken(tok);
|
|
5582
|
+
if (rc) {
|
|
5583
|
+
laser_r_color = rc;
|
|
5584
|
+
continue;
|
|
5585
|
+
}
|
|
5586
|
+
remaining.push(tok);
|
|
5687
5587
|
}
|
|
5688
|
-
|
|
5689
|
-
|
|
5690
|
-
|
|
5691
|
-
|
|
5692
|
-
|
|
5693
|
-
|
|
5694
|
-
|
|
5695
|
-
|
|
5696
|
-
|
|
5697
|
-
|
|
5698
|
-
|
|
5699
|
-
|
|
5700
|
-
|
|
5701
|
-
|
|
5702
|
-
|
|
5703
|
-
|
|
5704
|
-
|
|
5705
|
-
|
|
5706
|
-
|
|
5707
|
-
|
|
5708
|
-
|
|
5709
|
-
|
|
5710
|
-
|
|
5711
|
-
|
|
5712
|
-
|
|
5588
|
+
const rest = remaining.join(" ").trim();
|
|
5589
|
+
if (!rest) {
|
|
5590
|
+
return;
|
|
5591
|
+
}
|
|
5592
|
+
if (isBarcode(rest)) {
|
|
5593
|
+
const [midRaw, diffAbbrRaw] = rest.split(".");
|
|
5594
|
+
const music_id = Number(midRaw);
|
|
5595
|
+
const { diffStr } = getDiffStringFromAbbr(diffAbbrRaw);
|
|
5596
|
+
const difstr = diffStr;
|
|
5597
|
+
const output_format = "PNG";
|
|
5598
|
+
const payload = {
|
|
5599
|
+
music_id,
|
|
5600
|
+
difstr,
|
|
5601
|
+
arrangement_mode,
|
|
5602
|
+
rng_seed: null,
|
|
5603
|
+
output_format,
|
|
5604
|
+
laser_l_color,
|
|
5605
|
+
laser_r_color,
|
|
5606
|
+
px_per_second: 600,
|
|
5607
|
+
column_height: 2800
|
|
5608
|
+
};
|
|
5609
|
+
const musicList = await musicService.getMusic(ctx, [music_id]);
|
|
5610
|
+
session.send(
|
|
5611
|
+
session.text(".drawing", {
|
|
5612
|
+
name: musicList[0].title_name,
|
|
5613
|
+
difstr
|
|
5614
|
+
})
|
|
5615
|
+
);
|
|
5616
|
+
const res = await ctx.http.post(`${config.sdvx_data_url}/chart`, payload, {
|
|
5617
|
+
headers: { "Content-Type": "application/json" }
|
|
5618
|
+
});
|
|
5619
|
+
return import_koishi15.h.image(res, "image/png");
|
|
5620
|
+
} else {
|
|
5621
|
+
const {
|
|
5622
|
+
diffStr: parsedDiff,
|
|
5623
|
+
diffIndex,
|
|
5624
|
+
titleQuery
|
|
5625
|
+
} = parseDiffAndTitle(remaining);
|
|
5626
|
+
if (!titleQuery) return session.text(".no-result");
|
|
5627
|
+
const musicInfo = await musicService.searchMusic(
|
|
5628
|
+
ctx,
|
|
5629
|
+
titleQuery
|
|
5630
|
+
);
|
|
5631
|
+
if (!musicInfo || musicInfo.length === 0) return session.text(".no-result");
|
|
5632
|
+
const picked = musicInfo[0];
|
|
5633
|
+
const music_id = Number(picked?.id);
|
|
5634
|
+
if (!Number.isFinite(music_id)) {
|
|
5635
|
+
logger5.warn("search result missing id", picked);
|
|
5636
|
+
return session.text(".error");
|
|
5637
|
+
}
|
|
5638
|
+
let difstr = null;
|
|
5639
|
+
if (parsedDiff) {
|
|
5640
|
+
difstr = parsedDiff;
|
|
5641
|
+
} else if (diffIndex !== null && Array.isArray(picked?.difficulty) && diffIndex >= 0 && diffIndex < picked.difficulty.length) {
|
|
5642
|
+
const d = picked.difficulty[diffIndex];
|
|
5643
|
+
difstr = d?.difstr ?? null;
|
|
5644
|
+
}
|
|
5645
|
+
if (!difstr) {
|
|
5646
|
+
difstr = getHighestDifstr(picked?.difficulty);
|
|
5647
|
+
}
|
|
5648
|
+
const output_format = "PNG";
|
|
5649
|
+
const payload = {
|
|
5650
|
+
music_id,
|
|
5651
|
+
difstr,
|
|
5652
|
+
arrangement_mode,
|
|
5653
|
+
rng_seed: null,
|
|
5654
|
+
output_format,
|
|
5655
|
+
laser_l_color,
|
|
5656
|
+
laser_r_color,
|
|
5657
|
+
px_per_second: 600,
|
|
5658
|
+
column_height: 2800
|
|
5659
|
+
};
|
|
5660
|
+
session.send(
|
|
5661
|
+
session.text(".drawing", {
|
|
5662
|
+
name: picked.title_name,
|
|
5663
|
+
difstr
|
|
5664
|
+
})
|
|
5665
|
+
);
|
|
5666
|
+
const res = await ctx.http.post(`${config.sdvx_data_url}/chart`, payload, {
|
|
5667
|
+
headers: { "Content-Type": "application/json" }
|
|
5668
|
+
});
|
|
5669
|
+
return import_koishi15.h.image(res, "image/png");
|
|
5670
|
+
}
|
|
5671
|
+
} catch (err) {
|
|
5672
|
+
logger5.warn(err);
|
|
5673
|
+
return session.text(".error");
|
|
5713
5674
|
}
|
|
5714
|
-
|
|
5715
|
-
}
|
|
5716
|
-
/**
|
|
5717
|
-
* 根据服务器类型获取对应的服务器实例
|
|
5718
|
-
* @param serverType - 服务器类型
|
|
5719
|
-
* @returns 对应的服务器实例
|
|
5720
|
-
*/
|
|
5721
|
-
getServer(serverType) {
|
|
5722
|
-
return this.factory.getServer(serverType);
|
|
5723
|
-
}
|
|
5724
|
-
/**
|
|
5725
|
-
* 根据服务器类型和游戏类型获取对应的游戏服务实例
|
|
5726
|
-
* @param serverType - 服务器类型
|
|
5727
|
-
* @param gameType - 游戏类型
|
|
5728
|
-
* @returns 对应的游戏服务实例
|
|
5729
|
-
*/
|
|
5730
|
-
getGameService(serverType, gameType) {
|
|
5731
|
-
const server2 = this.factory.getServer(serverType);
|
|
5732
|
-
return server2.gameServices[gameType];
|
|
5733
|
-
}
|
|
5734
|
-
};
|
|
5735
|
-
function apply9(ctx, config) {
|
|
5675
|
+
});
|
|
5736
5676
|
}
|
|
5737
|
-
__name(
|
|
5677
|
+
__name(chart, "chart");
|
|
5738
5678
|
|
|
5739
5679
|
// src/games/sdvx/services/score-service.ts
|
|
5740
5680
|
var ScoreService = class _ScoreService {
|
|
@@ -6459,10 +6399,10 @@ function apply12(ctx) {
|
|
|
6459
6399
|
__name(apply12, "apply");
|
|
6460
6400
|
|
|
6461
6401
|
// src/games/sdvx/locales/en-US.yml
|
|
6462
|
-
var en_US_default4 = { _config: { $desc: "SDVX Module Settings",
|
|
6402
|
+
var en_US_default4 = { _config: { $desc: "SDVX Module Settings", 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: { "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>", "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>" } }, radar: { description: "Show player radar stats", 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>", result: "<p>{name}'s Radar</p>\n<p>NOTES: {notes}</p>\n<p>PEAK: {peak}</p>\n<p>TSUMAMI: {tsumami}</p>\n<p>TRICKY: {tricky}</p>\n<p>HAND-TRIP: {hand_trip}</p>\n<p>ONE-HAND: {one_hand}</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>" } } } } };
|
|
6463
6403
|
|
|
6464
6404
|
// src/games/sdvx/locales/zh-CN.yml
|
|
6465
|
-
var zh_CN_default4 = { _config: { $desc: "SDVX 模块设置",
|
|
6405
|
+
var zh_CN_default4 = { _config: { $desc: "SDVX 模块设置", 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: { "menu-select": "<p>请选择你要使用的卡片:</p>\n{card_list}\n<p>q. 退出</p>", "invalid-select": "<p>没有该选项!</p>", "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>" } }, radar: { description: "查询玩家六维雷达", messages: { "card-not-found": "<p>你还没绑卡,去绑个卡再来吧~</p>", "server-not-found": "<p>没有可用的服务器哦,自己添加一个吧~</p>", "no-scores-found": "<p>没有找到你的分数数据哦~</p>", error: "<p>Noah 遇到了错误(っ °Д °;)っ</p>", result: "<p>{name} 的雷达</p>\n<p>NOTES: {notes}</p>\n<p>PEAK: {peak}</p>\n<p>TSUMAMI: {tsumami}</p>\n<p>TRICKY: {tricky}</p>\n<p>HAND-TRIP: {hand_trip}</p>\n<p>ONE-HAND: {one_hand}</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>" } } } } };
|
|
6466
6406
|
|
|
6467
6407
|
// src/games/sdvx/index.ts
|
|
6468
6408
|
var name13 = "Noah-SDVX";
|
|
@@ -6474,6 +6414,7 @@ async function apply13(ctx, config) {
|
|
|
6474
6414
|
["en-US", en_US_default4],
|
|
6475
6415
|
["zh-CN", zh_CN_default4]
|
|
6476
6416
|
].forEach(([lang, file]) => ctx.i18n.define(lang, file));
|
|
6417
|
+
registerSDVXAdapters(logger4, config.sdvx);
|
|
6477
6418
|
ctx.plugin(database_exports2, config.sdvx);
|
|
6478
6419
|
ctx.plugin(command_exports2, config.sdvx);
|
|
6479
6420
|
ctx.plugin(event_exports2, config.sdvx);
|
|
@@ -6549,7 +6490,6 @@ var generalConfig = import_koishi22.Schema.object({
|
|
|
6549
6490
|
// src/games/sdvx/config.ts
|
|
6550
6491
|
var import_koishi23 = require("koishi");
|
|
6551
6492
|
var sdvxConfig = import_koishi23.Schema.object({
|
|
6552
|
-
default_model: import_koishi23.Schema.string().default("2025100700"),
|
|
6553
6493
|
sdvx_data_url: import_koishi23.Schema.string().required(),
|
|
6554
6494
|
sdvx_search_url: import_koishi23.Schema.string().required()
|
|
6555
6495
|
}).i18n({
|