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