koishi-plugin-noah 2.0.6 → 2.2.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/core/commands/link.d.ts +3 -0
- package/lib/drawer/sdvx/index.d.ts +8 -0
- package/lib/games/sdvx/commands/radar.d.ts +3 -0
- package/lib/games/sdvx/constants.d.ts +15 -0
- package/lib/games/sdvx/services/score-service.d.ts +17 -1
- package/lib/games/sdvx/utils/calculator.d.ts +3 -15
- package/lib/global/config.d.ts +16 -0
- package/lib/index.cjs +1110 -1854
- package/lib/servers/index.d.ts +1 -0
- package/lib/servers/utils/grade.d.ts +2 -7
- package/lib/slash/discord/adapter.d.ts +10 -15
- package/lib/slash/discord/index.d.ts +7 -1
- package/lib/slash/service.d.ts +1 -0
- package/lib/types/config.d.ts +14 -10
- package/package.json +1 -1
package/lib/index.cjs
CHANGED
|
@@ -6,8 +6,8 @@ var __getProtoOf = Object.getPrototypeOf;
|
|
|
6
6
|
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
7
7
|
var __name = (target, value) => __defProp(target, "name", { value, configurable: true });
|
|
8
8
|
var __export = (target, all) => {
|
|
9
|
-
for (var
|
|
10
|
-
__defProp(target,
|
|
9
|
+
for (var name15 in all)
|
|
10
|
+
__defProp(target, name15, { get: all[name15], enumerable: true });
|
|
11
11
|
};
|
|
12
12
|
var __copyProps = (to, from, except, desc) => {
|
|
13
13
|
if (from && typeof from === "object" || typeof from === "function") {
|
|
@@ -28,14 +28,14 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
|
|
|
28
28
|
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
29
29
|
|
|
30
30
|
// src/index.ts
|
|
31
|
-
var
|
|
32
|
-
__export(
|
|
31
|
+
var index_exports = {};
|
|
32
|
+
__export(index_exports, {
|
|
33
33
|
Config: () => Config,
|
|
34
|
-
apply: () =>
|
|
34
|
+
apply: () => apply14,
|
|
35
35
|
inject: () => inject4,
|
|
36
|
-
name: () =>
|
|
36
|
+
name: () => name14
|
|
37
37
|
});
|
|
38
|
-
module.exports = __toCommonJS(
|
|
38
|
+
module.exports = __toCommonJS(index_exports);
|
|
39
39
|
|
|
40
40
|
// src/asset/index.ts
|
|
41
41
|
var asset_exports = {};
|
|
@@ -64,6 +64,7 @@ var AssetService = class {
|
|
|
64
64
|
this.config = config;
|
|
65
65
|
this.basePath = import_path.default.resolve(this.ctx.baseDir, this.config.data_path);
|
|
66
66
|
}
|
|
67
|
+
ctx;
|
|
67
68
|
static {
|
|
68
69
|
__name(this, "AssetService");
|
|
69
70
|
}
|
|
@@ -329,18 +330,18 @@ __name(getAssetPath, "getAssetPath");
|
|
|
329
330
|
// src/core/index.ts
|
|
330
331
|
var core_exports = {};
|
|
331
332
|
__export(core_exports, {
|
|
332
|
-
apply: () =>
|
|
333
|
-
inject: () =>
|
|
334
|
-
logger: () =>
|
|
335
|
-
name: () =>
|
|
333
|
+
apply: () => apply5,
|
|
334
|
+
inject: () => inject,
|
|
335
|
+
logger: () => logger2,
|
|
336
|
+
name: () => name5
|
|
336
337
|
});
|
|
337
|
-
var
|
|
338
|
+
var import_koishi6 = require("koishi");
|
|
338
339
|
|
|
339
340
|
// src/core/command.ts
|
|
340
341
|
var command_exports = {};
|
|
341
342
|
__export(command_exports, {
|
|
342
|
-
apply: () =>
|
|
343
|
-
name: () =>
|
|
343
|
+
apply: () => apply2,
|
|
344
|
+
name: () => name2
|
|
344
345
|
});
|
|
345
346
|
|
|
346
347
|
// src/core/services/card-service.ts
|
|
@@ -348,6 +349,7 @@ var CardService = class {
|
|
|
348
349
|
constructor(ctx) {
|
|
349
350
|
this.ctx = ctx;
|
|
350
351
|
}
|
|
352
|
+
ctx;
|
|
351
353
|
static {
|
|
352
354
|
__name(this, "CardService");
|
|
353
355
|
}
|
|
@@ -410,8 +412,8 @@ var CardService = class {
|
|
|
410
412
|
* @param name - 卡片名称
|
|
411
413
|
* @returns 卡片信息,不存在则返回null
|
|
412
414
|
*/
|
|
413
|
-
async getCardByName(
|
|
414
|
-
const rows = await this.ctx.database.get(this.tableName, { name:
|
|
415
|
+
async getCardByName(name15) {
|
|
416
|
+
const rows = await this.ctx.database.get(this.tableName, { name: name15 });
|
|
415
417
|
if (!rows[0]) return null;
|
|
416
418
|
return {
|
|
417
419
|
...rows[0],
|
|
@@ -426,10 +428,10 @@ var CardService = class {
|
|
|
426
428
|
* @param defaultServerId - 默认服务器ID(若没有默认服务器则为0)
|
|
427
429
|
* @returns 创建成功后包含自动填充ID等字段的完整对象
|
|
428
430
|
*/
|
|
429
|
-
async createCard(uid, code,
|
|
431
|
+
async createCard(uid, code, name15, defaultServerId = 0) {
|
|
430
432
|
const data = {
|
|
431
433
|
code,
|
|
432
|
-
name:
|
|
434
|
+
name: name15,
|
|
433
435
|
defaultServerId
|
|
434
436
|
};
|
|
435
437
|
const res = await this.ctx.database.create(this.tableName, data);
|
|
@@ -492,6 +494,7 @@ var ServerService = class {
|
|
|
492
494
|
constructor(ctx) {
|
|
493
495
|
this.ctx = ctx;
|
|
494
496
|
}
|
|
497
|
+
ctx;
|
|
495
498
|
static {
|
|
496
499
|
__name(this, "ServerService");
|
|
497
500
|
}
|
|
@@ -567,8 +570,8 @@ var ServerService = class {
|
|
|
567
570
|
* @param name - 服务器名称
|
|
568
571
|
* @returns 服务器信息,不存在则返回null
|
|
569
572
|
*/
|
|
570
|
-
async getServerByName(
|
|
571
|
-
const rows = await this.ctx.database.get(this.tableName, { name:
|
|
573
|
+
async getServerByName(name15) {
|
|
574
|
+
const rows = await this.ctx.database.get(this.tableName, { name: name15 });
|
|
572
575
|
return rows[0] ?? null;
|
|
573
576
|
}
|
|
574
577
|
/**
|
|
@@ -685,6 +688,7 @@ var UserService = class {
|
|
|
685
688
|
constructor(ctx) {
|
|
686
689
|
this.ctx = ctx;
|
|
687
690
|
}
|
|
691
|
+
ctx;
|
|
688
692
|
static {
|
|
689
693
|
__name(this, "UserService");
|
|
690
694
|
}
|
|
@@ -997,7 +1001,7 @@ async function ensureOfficialServerForUser(ctx, serverService, userService, uid,
|
|
|
997
1001
|
const userServers = await serverService.getServersByUid(uid);
|
|
998
1002
|
const existingOfficial = userServers.find((server3) => server3.type === "official");
|
|
999
1003
|
if (existingOfficial) return existingOfficial.id;
|
|
1000
|
-
const baseUrl = officialSupportUrl || ctx.
|
|
1004
|
+
const baseUrl = officialSupportUrl || ctx.globalConfig.official_support_url;
|
|
1001
1005
|
const server2 = await serverService.createServerForUser(
|
|
1002
1006
|
{
|
|
1003
1007
|
type: "official",
|
|
@@ -1017,7 +1021,7 @@ __name(ensureOfficialServerForUser, "ensureOfficialServerForUser");
|
|
|
1017
1021
|
|
|
1018
1022
|
// src/core/commands/bind.ts
|
|
1019
1023
|
function bind(ctx, config) {
|
|
1020
|
-
ctx.command("bind [cardCode:text]").alias("绑卡").userFields(["defaultCardId", "id"]).action(async ({ session }, cardCode) => {
|
|
1024
|
+
ctx.command("bind [cardCode:text]", { slash: true }).alias("绑卡").userFields(["defaultCardId", "id"]).action(async ({ session }, cardCode) => {
|
|
1021
1025
|
const cardService = new CardService(ctx);
|
|
1022
1026
|
const serverService = new ServerService(ctx);
|
|
1023
1027
|
const userService = new UserService(ctx);
|
|
@@ -1055,7 +1059,7 @@ function bind(ctx, config) {
|
|
|
1055
1059
|
serverService,
|
|
1056
1060
|
userService,
|
|
1057
1061
|
session.user.id,
|
|
1058
|
-
|
|
1062
|
+
ctx.globalConfig.official_support_url
|
|
1059
1063
|
);
|
|
1060
1064
|
}
|
|
1061
1065
|
const res = await cardService.createCard(
|
|
@@ -1076,7 +1080,7 @@ __name(bind, "bind");
|
|
|
1076
1080
|
// src/core/commands/card.ts
|
|
1077
1081
|
var import_koishi2 = require("koishi");
|
|
1078
1082
|
function card(ctx, config) {
|
|
1079
|
-
ctx.command("card").alias("卡片管理").userFields(["id"]).channelFields(["id"]).option("detail", "-d <cardCode:text>").action(async ({ session, options }) => {
|
|
1083
|
+
ctx.command("card", { slash: true }).alias("卡片管理").userFields(["id"]).channelFields(["id"]).option("detail", "-d <cardCode:text>").action(async ({ session, options }) => {
|
|
1080
1084
|
const cardService = new CardService(ctx);
|
|
1081
1085
|
const atGuild = session.guildId != null;
|
|
1082
1086
|
if (options.detail) {
|
|
@@ -1198,23 +1202,43 @@ async function showCardSelectMenu(ctx, session, cardService, serverService, user
|
|
|
1198
1202
|
}
|
|
1199
1203
|
__name(showCardSelectMenu, "showCardSelectMenu");
|
|
1200
1204
|
async function showCardMenu(ctx, session, card2, cardService, serverService, userService, uid, userDefaultCardId, userDefaultServerId, cid, channelDefaultServerId) {
|
|
1201
|
-
|
|
1202
|
-
|
|
1203
|
-
|
|
1204
|
-
|
|
1205
|
-
|
|
1206
|
-
|
|
1207
|
-
})
|
|
1208
|
-
|
|
1205
|
+
let selectNum;
|
|
1206
|
+
if (session.platform === "discord") {
|
|
1207
|
+
const serverInfo = card2.defaultServerId != 0 ? `
|
|
1208
|
+
> 🔗 ${(await serverService.getServerById(card2.defaultServerId)).name}` : "";
|
|
1209
|
+
const buttons = [
|
|
1210
|
+
(0, import_koishi2.h)("button", { id: "card.action/1", type: "primary" }, "⭐ 设为默认"),
|
|
1211
|
+
(0, import_koishi2.h)("button", { id: "card.action/2", type: "secondary" }, "✏️ 修改卡号"),
|
|
1212
|
+
(0, import_koishi2.h)("button", { id: "card.action/3", type: "danger" }, "🗑️ 删除"),
|
|
1213
|
+
(0, import_koishi2.h)("button", { id: "card.action/4", type: "secondary" }, "📝 重命名"),
|
|
1214
|
+
(0, import_koishi2.h)("button", { id: "card.action/5", type: "secondary" }, "🖥️ 绑定服务器"),
|
|
1215
|
+
(0, import_koishi2.h)("button", { id: "card.action/0", type: "secondary" }, "↩️ 返回")
|
|
1216
|
+
];
|
|
1217
|
+
await session.send((0, import_koishi2.h)("message", [`**[${card2.name}]**${serverInfo}`, ...buttons]));
|
|
1218
|
+
const click = await session.prompt();
|
|
1219
|
+
if (!click) return session.text("commands.timeout");
|
|
1220
|
+
const match = click.match(/^card\.action\/(\d+)$/);
|
|
1221
|
+
if (!match) return session.text(".invalid-select");
|
|
1222
|
+
selectNum = Number(match[1]);
|
|
1209
1223
|
} else {
|
|
1210
|
-
|
|
1211
|
-
|
|
1212
|
-
|
|
1213
|
-
|
|
1214
|
-
|
|
1215
|
-
|
|
1216
|
-
|
|
1217
|
-
|
|
1224
|
+
if (card2.defaultServerId != 0) {
|
|
1225
|
+
const server2 = await serverService.getServerById(card2.defaultServerId);
|
|
1226
|
+
await session.send(
|
|
1227
|
+
session.text(".menu-has-bound-server", {
|
|
1228
|
+
name: card2.name,
|
|
1229
|
+
defaultServerName: server2.name
|
|
1230
|
+
})
|
|
1231
|
+
);
|
|
1232
|
+
} else {
|
|
1233
|
+
await session.send(session.text(".menu", card2));
|
|
1234
|
+
}
|
|
1235
|
+
const select = await session.prompt();
|
|
1236
|
+
if (!select) return session.text("commands.timeout");
|
|
1237
|
+
if (select === "q") return session.text(".quit");
|
|
1238
|
+
selectNum = parseInt(select, 10);
|
|
1239
|
+
if (isNaN(selectNum) || selectNum < 0 || selectNum > 5) {
|
|
1240
|
+
return await session.text(".invalid-select");
|
|
1241
|
+
}
|
|
1218
1242
|
}
|
|
1219
1243
|
if (selectNum === 0) {
|
|
1220
1244
|
return await showCardSelectMenu(
|
|
@@ -1277,7 +1301,7 @@ async function showCardMenu(ctx, session, card2, cardService, serverService, use
|
|
|
1277
1301
|
serverService,
|
|
1278
1302
|
userService,
|
|
1279
1303
|
uid,
|
|
1280
|
-
ctx.
|
|
1304
|
+
ctx.globalConfig.official_support_url
|
|
1281
1305
|
);
|
|
1282
1306
|
}
|
|
1283
1307
|
await cardService.updateCard(card2.id, { code: cardCode, defaultServerId });
|
|
@@ -1309,6 +1333,33 @@ async function showCardMenu(ctx, session, card2, cardService, serverService, use
|
|
|
1309
1333
|
}
|
|
1310
1334
|
if (selectNum === 5) {
|
|
1311
1335
|
const res = await serverService.getSelectableServers(uid, cid);
|
|
1336
|
+
if (session.platform === "discord") {
|
|
1337
|
+
const buttons = res.map((srv, index) => {
|
|
1338
|
+
let label = srv.name;
|
|
1339
|
+
if (srv.id === userDefaultServerId) label += " ⭐";
|
|
1340
|
+
else if (srv.id === channelDefaultServerId) label += " 📌";
|
|
1341
|
+
return (0, import_koishi2.h)(
|
|
1342
|
+
"button",
|
|
1343
|
+
{
|
|
1344
|
+
id: `card.srv/${index + 1}`,
|
|
1345
|
+
type: srv.id === card2.defaultServerId ? "primary" : "secondary"
|
|
1346
|
+
},
|
|
1347
|
+
label
|
|
1348
|
+
);
|
|
1349
|
+
});
|
|
1350
|
+
await session.send((0, import_koishi2.h)("message", ["请选择一个服务器:", ...buttons]));
|
|
1351
|
+
const click = await session.prompt();
|
|
1352
|
+
if (!click) return session.text("commands.timeout");
|
|
1353
|
+
const match = click.match(/^card\.srv\/(\d+)$/);
|
|
1354
|
+
if (!match) return session.text(".invalid-select");
|
|
1355
|
+
const selected = Number(match[1]);
|
|
1356
|
+
if (isNaN(selected) || selected < 1 || selected > res.length)
|
|
1357
|
+
return session.text(".invalid-select");
|
|
1358
|
+
if (card2.defaultServerId === res[selected - 1].id)
|
|
1359
|
+
return session.text(".menu-5-error-duplicate");
|
|
1360
|
+
await cardService.updateCard(card2.id, { defaultServerId: res[selected - 1].id });
|
|
1361
|
+
return session.text(".menu-5-success");
|
|
1362
|
+
}
|
|
1312
1363
|
let serverListMsg = "";
|
|
1313
1364
|
for (let i = 0; i < res.length; i++) {
|
|
1314
1365
|
if (res[i].id === userDefaultServerId) {
|
|
@@ -1321,10 +1372,10 @@ async function showCardMenu(ctx, session, card2, cardService, serverService, use
|
|
|
1321
1372
|
}
|
|
1322
1373
|
const msg = import_koishi2.h.unescape(session.text(".menu-5", { server_list: serverListMsg })).replace("< 你的默认服务器", "< 你的默认服务器").replace("< 群组默认服务器", "< 群组默认服务器");
|
|
1323
1374
|
await session.send(msg);
|
|
1324
|
-
const
|
|
1325
|
-
if (!
|
|
1326
|
-
if (
|
|
1327
|
-
const selectNum2 = parseInt(
|
|
1375
|
+
const select = await session.prompt();
|
|
1376
|
+
if (!select) return session.text("commands.timeout");
|
|
1377
|
+
if (select === "q") return session.text(".quit");
|
|
1378
|
+
const selectNum2 = parseInt(select, 10);
|
|
1328
1379
|
if (isNaN(selectNum2) || selectNum2 < 1 || selectNum2 > res.length) {
|
|
1329
1380
|
return session.text(".invalid-select");
|
|
1330
1381
|
}
|
|
@@ -1336,996 +1387,198 @@ async function showCardMenu(ctx, session, card2, cardService, serverService, use
|
|
|
1336
1387
|
}
|
|
1337
1388
|
__name(showCardMenu, "showCardMenu");
|
|
1338
1389
|
|
|
1339
|
-
// src/
|
|
1340
|
-
|
|
1341
|
-
|
|
1342
|
-
|
|
1343
|
-
|
|
1344
|
-
|
|
1345
|
-
registerSlashCommand: () => registerSlashCommand
|
|
1346
|
-
});
|
|
1347
|
-
var import_koishi3 = require("koishi");
|
|
1348
|
-
|
|
1349
|
-
// src/slash/database.ts
|
|
1350
|
-
var database_exports = {};
|
|
1351
|
-
__export(database_exports, {
|
|
1352
|
-
apply: () => apply2,
|
|
1353
|
-
name: () => name2
|
|
1354
|
-
});
|
|
1355
|
-
var name2 = "database";
|
|
1356
|
-
function apply2(ctx) {
|
|
1357
|
-
ctx.model.extend(
|
|
1358
|
-
"slash_command",
|
|
1359
|
-
{
|
|
1360
|
-
key: "string",
|
|
1361
|
-
json: "text",
|
|
1362
|
-
version: "string",
|
|
1363
|
-
lastDeclaredAt: "timestamp"
|
|
1364
|
-
},
|
|
1365
|
-
{
|
|
1366
|
-
primary: "key"
|
|
1367
|
-
}
|
|
1368
|
-
);
|
|
1369
|
-
ctx.model.extend(
|
|
1370
|
-
"slash_registered_command",
|
|
1371
|
-
{
|
|
1372
|
-
id: "unsigned",
|
|
1373
|
-
platform: "string",
|
|
1374
|
-
scope: "string",
|
|
1375
|
-
key: "string",
|
|
1376
|
-
platformCommandId: "string",
|
|
1377
|
-
hash: "string",
|
|
1378
|
-
rawPayload: "text",
|
|
1379
|
-
lastSyncedAt: "timestamp"
|
|
1380
|
-
},
|
|
1381
|
-
{
|
|
1382
|
-
autoInc: true
|
|
1383
|
-
}
|
|
1384
|
-
);
|
|
1385
|
-
ctx.model.extend(
|
|
1386
|
-
"slash_sync_log",
|
|
1387
|
-
{
|
|
1388
|
-
id: "unsigned",
|
|
1389
|
-
platform: "string",
|
|
1390
|
-
scope: "string",
|
|
1391
|
-
action: "string",
|
|
1392
|
-
key: "string",
|
|
1393
|
-
details: "text",
|
|
1394
|
-
createdAt: "timestamp"
|
|
1395
|
-
},
|
|
1396
|
-
{
|
|
1397
|
-
autoInc: true
|
|
1398
|
-
}
|
|
1399
|
-
);
|
|
1400
|
-
ctx.model.extend(
|
|
1401
|
-
"slash_lock",
|
|
1402
|
-
{
|
|
1403
|
-
name: "string",
|
|
1404
|
-
owner: "string",
|
|
1405
|
-
acquiredAt: "timestamp"
|
|
1406
|
-
},
|
|
1407
|
-
{
|
|
1408
|
-
primary: "name"
|
|
1409
|
-
}
|
|
1410
|
-
);
|
|
1390
|
+
// src/core/commands/help.ts
|
|
1391
|
+
function help(ctx, config) {
|
|
1392
|
+
ctx.command("help", { slash: true }).alias("帮助").action(({ session }) => {
|
|
1393
|
+
console.log(session);
|
|
1394
|
+
return session.text(".content");
|
|
1395
|
+
});
|
|
1411
1396
|
}
|
|
1412
|
-
__name(
|
|
1397
|
+
__name(help, "help");
|
|
1413
1398
|
|
|
1414
|
-
// src/
|
|
1415
|
-
var
|
|
1416
|
-
|
|
1417
|
-
|
|
1418
|
-
|
|
1419
|
-
|
|
1420
|
-
|
|
1421
|
-
|
|
1422
|
-
|
|
1423
|
-
|
|
1424
|
-
|
|
1425
|
-
|
|
1426
|
-
|
|
1427
|
-
|
|
1428
|
-
|
|
1429
|
-
|
|
1399
|
+
// src/core/commands/link.ts
|
|
1400
|
+
var import_koishi3 = require("koishi");
|
|
1401
|
+
function link(ctx, _config) {
|
|
1402
|
+
const tokens = /* @__PURE__ */ Object.create(null);
|
|
1403
|
+
function generate(platform, userId, phase) {
|
|
1404
|
+
const token = "noah/" + import_koishi3.Random.id(6, 10);
|
|
1405
|
+
tokens[token] = [platform, userId, phase];
|
|
1406
|
+
ctx.setTimeout(() => delete tokens[token], 5 * import_koishi3.Time.minute);
|
|
1407
|
+
return token;
|
|
1408
|
+
}
|
|
1409
|
+
__name(generate, "generate");
|
|
1410
|
+
async function bindAccount(aid, platform, pid) {
|
|
1411
|
+
await ctx.database.set("binding", { platform, pid }, { aid });
|
|
1412
|
+
}
|
|
1413
|
+
__name(bindAccount, "bindAccount");
|
|
1414
|
+
ctx.command("link", { slash: true }).alias("关联").userFields(["id"]).option("remove", "-r").action(async ({ session, options }) => {
|
|
1415
|
+
if (options.remove) {
|
|
1416
|
+
const { platform, userId: pid } = session;
|
|
1417
|
+
const bindings = await ctx.database.get("binding", { aid: session.user.id });
|
|
1418
|
+
const binding = bindings.find((b) => b.platform === platform && b.pid === pid);
|
|
1419
|
+
if (binding.aid !== binding.bid) {
|
|
1420
|
+
await bindAccount(binding.bid, platform, pid);
|
|
1421
|
+
return session.text(".remove-success");
|
|
1422
|
+
} else if (bindings.filter((b) => b.aid === b.bid).length === 1) {
|
|
1423
|
+
return session.text(".remove-original");
|
|
1424
|
+
} else {
|
|
1425
|
+
const authority = await session.resolve(ctx.root.config.autoAuthorize);
|
|
1426
|
+
const user = await ctx.database.create("user", { authority });
|
|
1427
|
+
await bindAccount(user.id, platform, pid);
|
|
1428
|
+
return session.text(".remove-success");
|
|
1430
1429
|
}
|
|
1431
|
-
} catch (err) {
|
|
1432
|
-
console.error("[slash] registry listener failed", err);
|
|
1433
1430
|
}
|
|
1434
|
-
|
|
1431
|
+
const token = generate(session.platform, session.userId, +!session.isDirect);
|
|
1432
|
+
return session.text(".generated-1", [token]);
|
|
1433
|
+
});
|
|
1434
|
+
ctx.middleware(async (session, next) => {
|
|
1435
|
+
const token = session.stripped.content;
|
|
1436
|
+
const data = tokens[token];
|
|
1437
|
+
if (!data) return next();
|
|
1438
|
+
if (data[0] === session.platform && data[1] === session.userId) {
|
|
1439
|
+
return session.text("commands.link.messages.self-" + (data[2] < 0 ? "2" : "1"));
|
|
1440
|
+
}
|
|
1441
|
+
delete tokens[token];
|
|
1442
|
+
if (data[2] < 0) {
|
|
1443
|
+
const [binding] = await ctx.database.get(
|
|
1444
|
+
"binding",
|
|
1445
|
+
{ platform: data[0], pid: data[1] },
|
|
1446
|
+
["aid"]
|
|
1447
|
+
);
|
|
1448
|
+
await bindAccount(binding.aid, session.platform, session.userId);
|
|
1449
|
+
return session.text("commands.link.messages.success");
|
|
1450
|
+
} else {
|
|
1451
|
+
const user = await ctx.database.getUser(session.platform, session.userId, [
|
|
1452
|
+
"id",
|
|
1453
|
+
"authority"
|
|
1454
|
+
]);
|
|
1455
|
+
if (!user.authority) return session.text("internal.low-authority");
|
|
1456
|
+
if (data[2]) {
|
|
1457
|
+
const token2 = generate(session.platform, session.userId, -1);
|
|
1458
|
+
return session.text("commands.link.messages.generated-2", [token2]);
|
|
1459
|
+
} else {
|
|
1460
|
+
await bindAccount(user.id, data[0], data[1]);
|
|
1461
|
+
return session.text("commands.link.messages.success");
|
|
1462
|
+
}
|
|
1463
|
+
}
|
|
1464
|
+
}, true);
|
|
1435
1465
|
}
|
|
1436
|
-
__name(
|
|
1437
|
-
|
|
1438
|
-
|
|
1439
|
-
|
|
1440
|
-
|
|
1466
|
+
__name(link, "link");
|
|
1467
|
+
|
|
1468
|
+
// src/core/commands/locale.ts
|
|
1469
|
+
var import_koishi4 = require("koishi");
|
|
1470
|
+
|
|
1471
|
+
// src/core/utils/role.ts
|
|
1472
|
+
function hasPermission(...perms) {
|
|
1473
|
+
return perms.some((perm) => perm === true);
|
|
1441
1474
|
}
|
|
1442
|
-
__name(
|
|
1443
|
-
function
|
|
1444
|
-
return
|
|
1475
|
+
__name(hasPermission, "hasPermission");
|
|
1476
|
+
function isPluginAdmin(session, config) {
|
|
1477
|
+
return config.adminUsers?.includes(session.userId) ?? false;
|
|
1445
1478
|
}
|
|
1446
|
-
__name(
|
|
1447
|
-
function
|
|
1448
|
-
|
|
1449
|
-
|
|
1479
|
+
__name(isPluginAdmin, "isPluginAdmin");
|
|
1480
|
+
function isGuildAdmin(session) {
|
|
1481
|
+
const platform = session.event.platform;
|
|
1482
|
+
if (platform === "onebot") {
|
|
1483
|
+
if (session.guildId == null) return true;
|
|
1484
|
+
const role = session.event.member.roles[0];
|
|
1485
|
+
const guildMember = typeof role === "string" ? role : role?.name || "";
|
|
1486
|
+
return guildMember === "owner" || guildMember === "admin" || guildMember === "SUBCHANNEL_ADMIN" || guildMember === "OWNER" || guildMember === "ADMIN";
|
|
1487
|
+
} else if (platform === "qq") {
|
|
1488
|
+
return true;
|
|
1489
|
+
} else {
|
|
1490
|
+
return true;
|
|
1491
|
+
}
|
|
1450
1492
|
}
|
|
1451
|
-
__name(
|
|
1493
|
+
__name(isGuildAdmin, "isGuildAdmin");
|
|
1452
1494
|
|
|
1453
|
-
// src/
|
|
1454
|
-
|
|
1455
|
-
|
|
1456
|
-
|
|
1457
|
-
|
|
1458
|
-
STRING: 3,
|
|
1459
|
-
INTEGER: 4,
|
|
1460
|
-
BOOLEAN: 5,
|
|
1461
|
-
USER: 6,
|
|
1462
|
-
CHANNEL: 7,
|
|
1463
|
-
ROLE: 8,
|
|
1464
|
-
MENTIONABLE: 9,
|
|
1465
|
-
NUMBER: 10,
|
|
1466
|
-
ATTACHMENT: 11
|
|
1467
|
-
};
|
|
1468
|
-
var sleep = /* @__PURE__ */ __name((ms) => new Promise((resolve3) => setTimeout(resolve3, ms)), "sleep");
|
|
1469
|
-
function hashDefinition(def) {
|
|
1470
|
-
const serialized = JSON.stringify(def) + "::" + (def.version ?? "");
|
|
1471
|
-
return import_crypto.default.createHash("sha256").update(serialized).digest("hex");
|
|
1472
|
-
}
|
|
1473
|
-
__name(hashDefinition, "hashDefinition");
|
|
1474
|
-
var PersistentDiscordSlashCommandService = class {
|
|
1475
|
-
constructor(ctx, adapter, logger6) {
|
|
1476
|
-
this.ctx = ctx;
|
|
1477
|
-
this.adapter = adapter;
|
|
1478
|
-
this.logger = logger6;
|
|
1479
|
-
}
|
|
1480
|
-
static {
|
|
1481
|
-
__name(this, "PersistentDiscordSlashCommandService");
|
|
1482
|
-
}
|
|
1483
|
-
platform = "discord";
|
|
1484
|
-
lockOwner = `noah-slash-${process.pid}-${Math.random().toString(36).slice(2)}`;
|
|
1485
|
-
async registerDeclaration(def) {
|
|
1486
|
-
const payload = {
|
|
1487
|
-
key: def.key,
|
|
1488
|
-
json: JSON.stringify(def),
|
|
1489
|
-
version: def.version ?? null,
|
|
1490
|
-
lastDeclaredAt: /* @__PURE__ */ new Date()
|
|
1491
|
-
};
|
|
1492
|
-
const existing = await this.ctx.database.get("slash_command", { key: def.key });
|
|
1493
|
-
if (existing.length) {
|
|
1494
|
-
await this.ctx.database.set(
|
|
1495
|
-
"slash_command",
|
|
1496
|
-
{ key: def.key },
|
|
1497
|
-
{
|
|
1498
|
-
json: payload.json,
|
|
1499
|
-
version: payload.version,
|
|
1500
|
-
lastDeclaredAt: payload.lastDeclaredAt
|
|
1501
|
-
}
|
|
1502
|
-
);
|
|
1503
|
-
} else {
|
|
1504
|
-
await this.ctx.database.create("slash_command", payload);
|
|
1505
|
-
}
|
|
1506
|
-
}
|
|
1507
|
-
async listDeclarations() {
|
|
1508
|
-
const rows = await this.ctx.database.get("slash_command", {});
|
|
1509
|
-
return rows.map((row) => JSON.parse(row.json));
|
|
1510
|
-
}
|
|
1511
|
-
async previewGuild(guildId) {
|
|
1512
|
-
return this.previewScope({ type: "guild", id: guildId });
|
|
1513
|
-
}
|
|
1514
|
-
async previewScope(target) {
|
|
1515
|
-
const plan = await this.buildPlan(target);
|
|
1516
|
-
return {
|
|
1517
|
-
create: plan.toCreate.map((item) => item.def.key),
|
|
1518
|
-
update: plan.toUpdate.map((item) => item.def.key),
|
|
1519
|
-
delete: plan.toDelete.map((item) => item.persisted.key)
|
|
1520
|
-
};
|
|
1521
|
-
}
|
|
1522
|
-
async syncGuild(guildId, opts) {
|
|
1523
|
-
return this.syncScope({ type: "guild", id: guildId }, opts);
|
|
1524
|
-
}
|
|
1525
|
-
async syncScope(target, opts) {
|
|
1526
|
-
const lockKey = target.type === "global" ? "sync:global" : `sync:guild:${target.id}`;
|
|
1527
|
-
if (!await this.acquireLock(lockKey, opts?.force)) {
|
|
1528
|
-
throw new Error(
|
|
1529
|
-
`Another sync is currently running for ${target.type === "global" ? "global" : target.id}`
|
|
1530
|
-
);
|
|
1495
|
+
// src/core/commands/locale.ts
|
|
1496
|
+
function locale(ctx, config) {
|
|
1497
|
+
ctx.command("locale", { slash: true }).alias("language").option("channel", "-c").option("reset", "-r").userFields(["locales"]).channelFields(["locales"]).action(async ({ session, options }) => {
|
|
1498
|
+
if (options.channel && !hasPermission(isPluginAdmin(session, config), isGuildAdmin(session))) {
|
|
1499
|
+
return session.text(".noAuth");
|
|
1531
1500
|
}
|
|
1532
|
-
|
|
1533
|
-
|
|
1534
|
-
|
|
1535
|
-
|
|
1536
|
-
|
|
1537
|
-
|
|
1538
|
-
|
|
1539
|
-
|
|
1540
|
-
|
|
1541
|
-
await this.logSync("create", target, entry.def.key, { response: result });
|
|
1542
|
-
summary.created++;
|
|
1543
|
-
}
|
|
1544
|
-
for (const entry of plan.toUpdate) {
|
|
1545
|
-
const commandId = entry.persisted.platformCommandId;
|
|
1546
|
-
if (!commandId) {
|
|
1547
|
-
const created = await this.retryWrapper(
|
|
1548
|
-
() => this.createOnScope(target, entry.payload)
|
|
1549
|
-
);
|
|
1550
|
-
const commandId2 = this.resolveCommandId(created);
|
|
1551
|
-
await this.persistRegisteredCommand(target, entry.def, commandId2, entry.payload);
|
|
1552
|
-
await this.logSync("update-create", target, entry.def.key, {
|
|
1553
|
-
response: created
|
|
1554
|
-
});
|
|
1555
|
-
summary.updated++;
|
|
1556
|
-
continue;
|
|
1557
|
-
}
|
|
1558
|
-
try {
|
|
1559
|
-
await this.retryWrapper(
|
|
1560
|
-
() => this.updateOnScope(target, commandId, entry.payload)
|
|
1561
|
-
);
|
|
1562
|
-
await this.persistRegisteredCommand(target, entry.def, commandId, entry.payload);
|
|
1563
|
-
await this.logSync("update", target, entry.def.key, { ok: true });
|
|
1564
|
-
summary.updated++;
|
|
1565
|
-
} catch (err) {
|
|
1566
|
-
await this.logSync("update-error", target, entry.def.key, {
|
|
1567
|
-
error: err instanceof Error ? err.message : String(err)
|
|
1568
|
-
});
|
|
1569
|
-
await this.retryWrapper(() => this.deleteOnScope(target, commandId2)).catch().catch(() => null);
|
|
1570
|
-
const recreated = await this.retryWrapper(
|
|
1571
|
-
() => this.createOnScope(target, entry.payload)
|
|
1572
|
-
);
|
|
1573
|
-
const commandId2 = this.resolveCommandId(recreated);
|
|
1574
|
-
await this.persistRegisteredCommand(target, entry.def, commandId2, entry.payload);
|
|
1575
|
-
await this.logSync("update-recreate", target, entry.def.key, {
|
|
1576
|
-
response: recreated
|
|
1577
|
-
});
|
|
1578
|
-
summary.updated++;
|
|
1579
|
-
}
|
|
1580
|
-
}
|
|
1581
|
-
for (const entry of plan.toDelete) {
|
|
1582
|
-
if (entry.persisted.platformCommandId) {
|
|
1583
|
-
await this.retryWrapper(
|
|
1584
|
-
() => this.deleteOnScope(target, entry.persisted.platformCommandId)
|
|
1585
|
-
).catch((err) => {
|
|
1586
|
-
this.logger.warn(
|
|
1587
|
-
`[slash] failed to delete command ${entry.persisted.key}: ${err}`
|
|
1588
|
-
);
|
|
1589
|
-
if (err?.status !== 404) throw err;
|
|
1590
|
-
});
|
|
1591
|
-
}
|
|
1592
|
-
await this.removeRegisteredCommand(entry.persisted.id);
|
|
1593
|
-
await this.logSync("delete", target, entry.persisted.key, { ok: true });
|
|
1594
|
-
summary.deleted++;
|
|
1595
|
-
}
|
|
1596
|
-
return summary;
|
|
1597
|
-
} finally {
|
|
1598
|
-
await this.releaseLock(lockKey);
|
|
1599
|
-
}
|
|
1600
|
-
}
|
|
1601
|
-
async buildPlan(target) {
|
|
1602
|
-
const allDecls = await this.listDeclarations();
|
|
1603
|
-
const scopeLabel = target.type === "global" ? "global" : target.id;
|
|
1604
|
-
const localDefs = allDecls.filter((def) => this.shouldInclude(def, target));
|
|
1605
|
-
const remote = target.type === "global" ? await this.fetchGlobalCommands() : await this.fetchRemoteCommands(target.id);
|
|
1606
|
-
const scopeKey = target.type === "global" ? "global" : `guild:${target.id}`;
|
|
1607
|
-
const persistedRows = await this.ctx.database.get("slash_registered_command", {
|
|
1608
|
-
platform: this.platform,
|
|
1609
|
-
scope: scopeKey
|
|
1610
|
-
});
|
|
1611
|
-
const persistedByKey = /* @__PURE__ */ new Map();
|
|
1612
|
-
for (const row of persistedRows) persistedByKey.set(row.key, row);
|
|
1613
|
-
const remoteByName = new Map(remote.map((cmd) => [cmd.name, cmd]));
|
|
1614
|
-
const toCreate = [];
|
|
1615
|
-
const toUpdate = [];
|
|
1616
|
-
const toDelete = [];
|
|
1617
|
-
for (const def of localDefs) {
|
|
1618
|
-
const payload = this.mapToDiscordPayload(def);
|
|
1619
|
-
const hash = hashDefinition(def);
|
|
1620
|
-
const persisted = persistedByKey.get(def.key);
|
|
1621
|
-
if (!persisted) {
|
|
1622
|
-
const remoteSameName = remoteByName.get(def.name);
|
|
1623
|
-
if (!remoteSameName) {
|
|
1624
|
-
toCreate.push({ def, payload });
|
|
1625
|
-
} else {
|
|
1626
|
-
toUpdate.push({
|
|
1627
|
-
def,
|
|
1628
|
-
payload,
|
|
1629
|
-
persisted: {
|
|
1630
|
-
platform: this.platform,
|
|
1631
|
-
scope: scopeKey,
|
|
1632
|
-
key: def.key,
|
|
1633
|
-
platformCommandId: remoteSameName.id,
|
|
1634
|
-
hash: null,
|
|
1635
|
-
rawPayload: null,
|
|
1636
|
-
lastSyncedAt: /* @__PURE__ */ new Date()
|
|
1637
|
-
}
|
|
1638
|
-
});
|
|
1639
|
-
}
|
|
1640
|
-
} else if (persisted.hash !== hash) {
|
|
1641
|
-
toUpdate.push({ def, payload, persisted });
|
|
1642
|
-
}
|
|
1643
|
-
}
|
|
1644
|
-
for (const persisted of persistedRows) {
|
|
1645
|
-
const exists = localDefs.some((def) => def.key === persisted.key);
|
|
1646
|
-
if (!exists) {
|
|
1647
|
-
toDelete.push({ persisted });
|
|
1648
|
-
}
|
|
1649
|
-
}
|
|
1650
|
-
for (const remoteCmd of remote) {
|
|
1651
|
-
const name17 = remoteCmd?.name;
|
|
1652
|
-
const id = remoteCmd?.id;
|
|
1653
|
-
const matchedLocal = localDefs.some((def) => def.name === name17);
|
|
1654
|
-
const matchedPersisted = persistedRows.some((row) => row.platformCommandId === id);
|
|
1655
|
-
if (!matchedLocal && !matchedPersisted && id) {
|
|
1656
|
-
toDelete.push({
|
|
1657
|
-
persisted: {
|
|
1658
|
-
platform: this.platform,
|
|
1659
|
-
scope: scopeKey,
|
|
1660
|
-
key: name17 ?? `remote:${id}`,
|
|
1661
|
-
platformCommandId: id,
|
|
1662
|
-
hash: null,
|
|
1663
|
-
rawPayload: JSON.stringify(remoteCmd),
|
|
1664
|
-
lastSyncedAt: /* @__PURE__ */ new Date()
|
|
1665
|
-
}
|
|
1666
|
-
});
|
|
1501
|
+
if (options.reset) {
|
|
1502
|
+
if (options.channel) {
|
|
1503
|
+
session.channel.locales = [];
|
|
1504
|
+
await session.channel.$update();
|
|
1505
|
+
return session.text(".reset-channel");
|
|
1506
|
+
} else {
|
|
1507
|
+
session.user.locales = [];
|
|
1508
|
+
await session.user.$update();
|
|
1509
|
+
return session.text(".reset-user");
|
|
1667
1510
|
}
|
|
1668
1511
|
}
|
|
1669
|
-
|
|
1670
|
-
|
|
1671
|
-
)
|
|
1672
|
-
|
|
1673
|
-
|
|
1674
|
-
|
|
1675
|
-
|
|
1676
|
-
|
|
1677
|
-
|
|
1678
|
-
|
|
1679
|
-
|
|
1680
|
-
|
|
1681
|
-
|
|
1682
|
-
|
|
1683
|
-
scope: target.type === "global" ? "global" : `guild:${target.id}`,
|
|
1684
|
-
key: def.key,
|
|
1685
|
-
platformCommandId: commandId,
|
|
1686
|
-
hash: hashDefinition(def),
|
|
1687
|
-
rawPayload: JSON.stringify(payload),
|
|
1688
|
-
lastSyncedAt: now
|
|
1689
|
-
};
|
|
1690
|
-
if (existing.length) {
|
|
1691
|
-
await this.ctx.database.set("slash_registered_command", { id: existing[0].id }, record);
|
|
1512
|
+
const target = options.channel ? "channel" : "user";
|
|
1513
|
+
let lang;
|
|
1514
|
+
if (session.platform === "discord") {
|
|
1515
|
+
const buttons = [
|
|
1516
|
+
(0, import_koishi4.h)("button", { id: "locale/zh-CN", type: "primary" }, "简体中文"),
|
|
1517
|
+
(0, import_koishi4.h)("button", { id: "locale/en-US", type: "secondary" }, "English")
|
|
1518
|
+
];
|
|
1519
|
+
const label = target === "channel" ? "群聊" : "你";
|
|
1520
|
+
await session.send((0, import_koishi4.h)("message", [`选择${label}使用的语言:`, ...buttons]));
|
|
1521
|
+
const click = await session.prompt();
|
|
1522
|
+
if (!click) return session.text("commands.timeout");
|
|
1523
|
+
const match = click.match(/^locale\/(zh-CN|en-US)$/);
|
|
1524
|
+
if (!match) return session.text(".invalid-select");
|
|
1525
|
+
lang = match[1];
|
|
1692
1526
|
} else {
|
|
1693
|
-
await
|
|
1694
|
-
|
|
1695
|
-
|
|
1696
|
-
|
|
1697
|
-
|
|
1698
|
-
|
|
1699
|
-
|
|
1700
|
-
|
|
1701
|
-
|
|
1702
|
-
return target.type === "global";
|
|
1527
|
+
await session.send(session.text(`.set-${target}`));
|
|
1528
|
+
const select = await session.prompt();
|
|
1529
|
+
if (!select) return session.text("commands.timeout");
|
|
1530
|
+
if (select === "q") return session.text(".quit");
|
|
1531
|
+
const selectNum = parseInt(select, 10);
|
|
1532
|
+
if (isNaN(selectNum) || selectNum < 1 || selectNum > 2) {
|
|
1533
|
+
return session.text(".invalid-select");
|
|
1534
|
+
}
|
|
1535
|
+
lang = selectNum === 1 ? "zh-CN" : "en-US";
|
|
1703
1536
|
}
|
|
1704
|
-
if (
|
|
1705
|
-
|
|
1537
|
+
if (options.channel) {
|
|
1538
|
+
updateLocales(session.channel, lang);
|
|
1539
|
+
await session.channel.$update();
|
|
1540
|
+
} else {
|
|
1541
|
+
updateLocales(session.user, lang);
|
|
1542
|
+
await session.user.$update();
|
|
1706
1543
|
}
|
|
1707
|
-
return
|
|
1708
|
-
}
|
|
1709
|
-
|
|
1710
|
-
|
|
1711
|
-
|
|
1712
|
-
|
|
1713
|
-
|
|
1714
|
-
|
|
1715
|
-
description_localizations: description.locales,
|
|
1716
|
-
options: def.options?.map((opt) => this.mapOption(opt)),
|
|
1717
|
-
default_member_permissions: def.default_member_permissions === void 0 ? null : def.default_member_permissions,
|
|
1718
|
-
dm_permission: def.dm_permission,
|
|
1719
|
-
integration_types: def.integration_types,
|
|
1720
|
-
contexts: def.contexts,
|
|
1721
|
-
nsfw: def.nsfw
|
|
1722
|
-
};
|
|
1723
|
-
}
|
|
1724
|
-
mapOption(option) {
|
|
1725
|
-
const description = this.normalizeLocalized(
|
|
1726
|
-
option.description,
|
|
1727
|
-
option.description_localizations,
|
|
1728
|
-
"Configure option"
|
|
1729
|
-
);
|
|
1730
|
-
return {
|
|
1731
|
-
type: OPTION_TYPE_MAP[option.type] ?? OPTION_TYPE_MAP.STRING,
|
|
1732
|
-
name: option.name,
|
|
1733
|
-
name_localizations: option.name_localizations,
|
|
1734
|
-
description: description.text,
|
|
1735
|
-
description_localizations: description.locales,
|
|
1736
|
-
required: option.required,
|
|
1737
|
-
options: option.options?.map((sub) => this.mapOption(sub)),
|
|
1738
|
-
choices: option.choices?.map((choice) => {
|
|
1739
|
-
const localized = this.normalizeLocalized(choice.name, void 0, "choice");
|
|
1740
|
-
return {
|
|
1741
|
-
name: localized.text,
|
|
1742
|
-
name_localizations: localized.locales,
|
|
1743
|
-
value: choice.value
|
|
1744
|
-
};
|
|
1745
|
-
}),
|
|
1746
|
-
channel_types: option.channel_types,
|
|
1747
|
-
min_value: option.min_value,
|
|
1748
|
-
max_value: option.max_value,
|
|
1749
|
-
min_length: option.min_length,
|
|
1750
|
-
max_length: option.max_length,
|
|
1751
|
-
autocomplete: option.autocomplete
|
|
1752
|
-
};
|
|
1544
|
+
return session.text(".success");
|
|
1545
|
+
});
|
|
1546
|
+
}
|
|
1547
|
+
__name(locale, "locale");
|
|
1548
|
+
function updateLocales(target, lang) {
|
|
1549
|
+
const index = target.locales.indexOf(lang);
|
|
1550
|
+
if (index !== -1) {
|
|
1551
|
+
target.locales.splice(index, 1);
|
|
1753
1552
|
}
|
|
1754
|
-
|
|
1755
|
-
|
|
1756
|
-
|
|
1757
|
-
|
|
1758
|
-
|
|
1759
|
-
|
|
1760
|
-
|
|
1761
|
-
if (
|
|
1762
|
-
|
|
1763
|
-
return { text, locales: { ...value, ...explicit ?? {} } };
|
|
1553
|
+
target.locales.unshift(lang);
|
|
1554
|
+
}
|
|
1555
|
+
__name(updateLocales, "updateLocales");
|
|
1556
|
+
|
|
1557
|
+
// src/core/commands/maintain.ts
|
|
1558
|
+
function maintain(ctx, config) {
|
|
1559
|
+
ctx.command("maintain", { slash: true }).alias("维护").option("stop", "-s").action(async ({ session, options }) => {
|
|
1560
|
+
if (!hasPermission(isPluginAdmin(session, config))) {
|
|
1561
|
+
return session.text(".no-auth");
|
|
1764
1562
|
}
|
|
1765
|
-
|
|
1766
|
-
|
|
1767
|
-
|
|
1768
|
-
|
|
1769
|
-
|
|
1770
|
-
|
|
1771
|
-
|
|
1772
|
-
|
|
1563
|
+
let card2 = "";
|
|
1564
|
+
let text = "";
|
|
1565
|
+
if (options.stop) {
|
|
1566
|
+
session.send(session.text(".stop"));
|
|
1567
|
+
card2 = config.guildNameCards[0];
|
|
1568
|
+
text = ".success-stop";
|
|
1569
|
+
} else {
|
|
1570
|
+
session.send(session.text(".start"));
|
|
1571
|
+
card2 = "Noah | 🚧维护中";
|
|
1572
|
+
text = ".success-start";
|
|
1773
1573
|
}
|
|
1774
|
-
|
|
1775
|
-
|
|
1776
|
-
|
|
1777
|
-
|
|
1778
|
-
|
|
1779
|
-
|
|
1780
|
-
|
|
1781
|
-
|
|
1782
|
-
this.logger.debug(`[slash] failed to acquire lock ${name17}: ${err}`);
|
|
1783
|
-
return false;
|
|
1784
|
-
}
|
|
1785
|
-
}
|
|
1786
|
-
async releaseLock(name17) {
|
|
1787
|
-
await this.ctx.database.remove("slash_lock", { name: name17, owner: this.lockOwner });
|
|
1788
|
-
}
|
|
1789
|
-
async releaseAllLocks() {
|
|
1790
|
-
await this.ctx.database.remove("slash_lock", { owner: this.lockOwner });
|
|
1791
|
-
}
|
|
1792
|
-
async logSync(action, target, key, details) {
|
|
1793
|
-
await this.ctx.database.create("slash_sync_log", {
|
|
1794
|
-
platform: this.platform,
|
|
1795
|
-
scope: target.type === "global" ? "global" : `guild:${target.id}`,
|
|
1796
|
-
action,
|
|
1797
|
-
key,
|
|
1798
|
-
details: JSON.stringify(details ?? {}),
|
|
1799
|
-
createdAt: /* @__PURE__ */ new Date()
|
|
1800
|
-
});
|
|
1801
|
-
}
|
|
1802
|
-
async retryWrapper(fn, retries = 5) {
|
|
1803
|
-
let attempt = 0;
|
|
1804
|
-
while (true) {
|
|
1805
|
-
try {
|
|
1806
|
-
return await fn();
|
|
1807
|
-
} catch (err) {
|
|
1808
|
-
attempt++;
|
|
1809
|
-
const status = err?.status;
|
|
1810
|
-
if (status === 429 && attempt <= retries) {
|
|
1811
|
-
const retryAfter = this.parseRetryAfter(err);
|
|
1812
|
-
await sleep(retryAfter ?? Math.min(2e3 * attempt, 1e4));
|
|
1813
|
-
continue;
|
|
1814
|
-
}
|
|
1815
|
-
if (status && status >= 500 && attempt < retries) {
|
|
1816
|
-
await sleep(500 * attempt);
|
|
1817
|
-
continue;
|
|
1818
|
-
}
|
|
1819
|
-
throw err;
|
|
1820
|
-
}
|
|
1821
|
-
}
|
|
1822
|
-
}
|
|
1823
|
-
parseRetryAfter(err) {
|
|
1824
|
-
if (typeof err?.retryAfter === "number") {
|
|
1825
|
-
return err.retryAfter;
|
|
1826
|
-
}
|
|
1827
|
-
return null;
|
|
1828
|
-
}
|
|
1829
|
-
async fetchRemoteCommands(guildId) {
|
|
1830
|
-
const raw = await this.adapter.listGuild(guildId);
|
|
1831
|
-
return this.normalizeRemoteCommands(raw, `guild:${guildId}`);
|
|
1832
|
-
}
|
|
1833
|
-
async fetchGlobalCommands() {
|
|
1834
|
-
const raw = await this.adapter.listGlobal();
|
|
1835
|
-
return this.normalizeRemoteCommands(raw, "global");
|
|
1836
|
-
}
|
|
1837
|
-
async createOnScope(target, payload) {
|
|
1838
|
-
return target.type === "global" ? this.adapter.createGlobal(payload) : this.adapter.createGuild(target.id, payload);
|
|
1839
|
-
}
|
|
1840
|
-
async updateOnScope(target, commandId, payload) {
|
|
1841
|
-
return target.type === "global" ? this.adapter.updateGlobal(commandId, payload) : this.adapter.updateGuild(target.id, commandId, payload);
|
|
1842
|
-
}
|
|
1843
|
-
async deleteOnScope(target, commandId) {
|
|
1844
|
-
return target.type === "global" ? this.adapter.deleteGlobal(commandId) : this.adapter.deleteGuild(target.id, commandId);
|
|
1845
|
-
}
|
|
1846
|
-
async normalizeRemoteCommands(raw, scopeLabel) {
|
|
1847
|
-
if (Array.isArray(raw)) return raw;
|
|
1848
|
-
const rawData = raw?.data;
|
|
1849
|
-
if (Array.isArray(rawData)) return rawData;
|
|
1850
|
-
const jsonFn = raw && typeof raw.json === "function" ? raw.json : null;
|
|
1851
|
-
if (jsonFn) {
|
|
1852
|
-
const data = await jsonFn.call(raw);
|
|
1853
|
-
if (Array.isArray(data)) return data;
|
|
1854
|
-
}
|
|
1855
|
-
this.logger.warn(`[slash] unexpected command payload for ${scopeLabel}, defaulting to []`);
|
|
1856
|
-
return [];
|
|
1857
|
-
}
|
|
1858
|
-
resolveCommandId(result) {
|
|
1859
|
-
if (result && typeof result === "object") {
|
|
1860
|
-
if (typeof result.id === "string") return result.id;
|
|
1861
|
-
if (result.data && typeof result.data.id === "string") return result.data.id;
|
|
1862
|
-
}
|
|
1863
|
-
throw new Error("Unable to resolve command id from Discord response.");
|
|
1864
|
-
}
|
|
1865
|
-
async importRemoteCommands(target) {
|
|
1866
|
-
const remote = target.type === "global" ? await this.fetchGlobalCommands() : await this.fetchRemoteCommands(target.id);
|
|
1867
|
-
console.log(
|
|
1868
|
-
`[slash] fetched ${remote.length} commands from ${target.type === "global" ? "global" : target.id}`,
|
|
1869
|
-
remote.map((c) => ({ id: c?.id, name: c?.name }))
|
|
1870
|
-
);
|
|
1871
|
-
const scope = target.type === "global" ? "global" : `guild:${target.id}`;
|
|
1872
|
-
const inserted = [];
|
|
1873
|
-
for (const command of remote) {
|
|
1874
|
-
const platformCommandId = typeof command?.id === "string" ? command.id : typeof command?.data?.id === "string" ? command.data.id : void 0;
|
|
1875
|
-
const key = platformCommandId ? `remote:${platformCommandId}` : `remote:${scope}:${command?.name ?? Math.random().toString(36).slice(2)}`;
|
|
1876
|
-
const exists = await this.ctx.database.get("slash_registered_command", {
|
|
1877
|
-
platform: this.platform,
|
|
1878
|
-
scope,
|
|
1879
|
-
key
|
|
1880
|
-
});
|
|
1881
|
-
if (exists.length) continue;
|
|
1882
|
-
await this.ctx.database.create("slash_registered_command", {
|
|
1883
|
-
platform: this.platform,
|
|
1884
|
-
scope,
|
|
1885
|
-
key,
|
|
1886
|
-
platformCommandId,
|
|
1887
|
-
hash: null,
|
|
1888
|
-
rawPayload: JSON.stringify(command),
|
|
1889
|
-
lastSyncedAt: /* @__PURE__ */ new Date()
|
|
1890
|
-
});
|
|
1891
|
-
inserted.push(key);
|
|
1892
|
-
}
|
|
1893
|
-
return inserted;
|
|
1894
|
-
}
|
|
1895
|
-
};
|
|
1896
|
-
|
|
1897
|
-
// src/slash/discord/adapter.ts
|
|
1898
|
-
var DiscordAdapter = class {
|
|
1899
|
-
constructor(ctx, appId, token) {
|
|
1900
|
-
this.ctx = ctx;
|
|
1901
|
-
this.appId = appId;
|
|
1902
|
-
this.token = token;
|
|
1903
|
-
if (!appId || !token) {
|
|
1904
|
-
throw new Error("Discord application id and bot token are required for slash sync");
|
|
1905
|
-
}
|
|
1906
|
-
}
|
|
1907
|
-
static {
|
|
1908
|
-
__name(this, "DiscordAdapter");
|
|
1909
|
-
}
|
|
1910
|
-
baseURL = "https://discord.com/api/v10";
|
|
1911
|
-
headers() {
|
|
1912
|
-
return {
|
|
1913
|
-
Authorization: `Bot ${this.token}`,
|
|
1914
|
-
"Content-Type": "application/json"
|
|
1915
|
-
};
|
|
1916
|
-
}
|
|
1917
|
-
async listGuild(guildId) {
|
|
1918
|
-
return this.request(`/applications/${this.appId}/guilds/${guildId}/commands`, "GET");
|
|
1919
|
-
}
|
|
1920
|
-
async listGlobal() {
|
|
1921
|
-
return this.request(`/applications/${this.appId}/commands`, "GET");
|
|
1922
|
-
}
|
|
1923
|
-
async createGlobal(payload) {
|
|
1924
|
-
return this.request(`/applications/${this.appId}/commands`, "POST", payload);
|
|
1925
|
-
}
|
|
1926
|
-
async updateGlobal(commandId, payload) {
|
|
1927
|
-
return this.request(`/applications/${this.appId}/commands/${commandId}`, "PATCH", payload);
|
|
1928
|
-
}
|
|
1929
|
-
async deleteGlobal(commandId) {
|
|
1930
|
-
await this.request(
|
|
1931
|
-
`/applications/${this.appId}/commands/${commandId}`,
|
|
1932
|
-
"DELETE",
|
|
1933
|
-
void 0,
|
|
1934
|
-
false
|
|
1935
|
-
);
|
|
1936
|
-
return null;
|
|
1937
|
-
}
|
|
1938
|
-
async createGuild(guildId, payload) {
|
|
1939
|
-
return this.request(
|
|
1940
|
-
`/applications/${this.appId}/guilds/${guildId}/commands`,
|
|
1941
|
-
"POST",
|
|
1942
|
-
payload
|
|
1943
|
-
);
|
|
1944
|
-
}
|
|
1945
|
-
async updateGuild(guildId, commandId, payload) {
|
|
1946
|
-
return this.request(
|
|
1947
|
-
`/applications/${this.appId}/guilds/${guildId}/commands/${commandId}`,
|
|
1948
|
-
"PATCH",
|
|
1949
|
-
payload
|
|
1950
|
-
);
|
|
1951
|
-
}
|
|
1952
|
-
async deleteGuild(guildId, commandId) {
|
|
1953
|
-
await this.request(
|
|
1954
|
-
`/applications/${this.appId}/guilds/${guildId}/commands/${commandId}`,
|
|
1955
|
-
"DELETE",
|
|
1956
|
-
void 0,
|
|
1957
|
-
false
|
|
1958
|
-
);
|
|
1959
|
-
return null;
|
|
1960
|
-
}
|
|
1961
|
-
async bulkOverwriteGuild(guildId, payloads) {
|
|
1962
|
-
return this.request(
|
|
1963
|
-
`/applications/${this.appId}/guilds/${guildId}/commands`,
|
|
1964
|
-
"PUT",
|
|
1965
|
-
payloads
|
|
1966
|
-
);
|
|
1967
|
-
}
|
|
1968
|
-
async request(path3, method, data, expectJson = true) {
|
|
1969
|
-
try {
|
|
1970
|
-
return await this.ctx.http(method, `${this.baseURL}${path3}`, {
|
|
1971
|
-
headers: this.headers(),
|
|
1972
|
-
data,
|
|
1973
|
-
...expectJson ? { responseType: "json" } : {}
|
|
1974
|
-
});
|
|
1975
|
-
} catch (error) {
|
|
1976
|
-
throw this.makeError(error);
|
|
1977
|
-
}
|
|
1978
|
-
}
|
|
1979
|
-
makeError(err) {
|
|
1980
|
-
const status = err?.response?.status ?? err?.status;
|
|
1981
|
-
const statusText = err?.response?.statusText ?? "";
|
|
1982
|
-
const data = err?.response?.data;
|
|
1983
|
-
const body = typeof data === "string" ? data : data ? JSON.stringify(data) : err?.message ?? "";
|
|
1984
|
-
const wrapped = new Error(`Discord API ${status ?? "ERR"} ${statusText}: ${body}`);
|
|
1985
|
-
const retryAfterHeader = err?.response?.headers?.["retry-after"] ?? err?.response?.headers?.["Retry-After"];
|
|
1986
|
-
if (status) wrapped.status = status;
|
|
1987
|
-
if (body) wrapped.body = body;
|
|
1988
|
-
if (retryAfterHeader) {
|
|
1989
|
-
const retryAfterSeconds = Number(retryAfterHeader);
|
|
1990
|
-
if (Number.isFinite(retryAfterSeconds)) {
|
|
1991
|
-
;
|
|
1992
|
-
wrapped.retryAfter = retryAfterSeconds * 1e3;
|
|
1993
|
-
}
|
|
1994
|
-
}
|
|
1995
|
-
return wrapped;
|
|
1996
|
-
}
|
|
1997
|
-
};
|
|
1998
|
-
|
|
1999
|
-
// src/slash/discord/index.ts
|
|
2000
|
-
var DiscordSlashService = class {
|
|
2001
|
-
constructor(ctx, config, logger6) {
|
|
2002
|
-
this.ctx = ctx;
|
|
2003
|
-
this.config = config;
|
|
2004
|
-
this.logger = logger6;
|
|
2005
|
-
const adapter = new DiscordAdapter(ctx, config.discord_application_id, config.discord_token);
|
|
2006
|
-
this.slashService = new PersistentDiscordSlashCommandService(ctx, adapter, this.logger);
|
|
2007
|
-
this.ready = this.seedDeclarations().catch((err) => {
|
|
2008
|
-
this.logger.error(
|
|
2009
|
-
`[slash] failed to initialize declarations: ${err instanceof Error ? err.message : String(err)}`
|
|
2010
|
-
);
|
|
2011
|
-
throw err;
|
|
2012
|
-
});
|
|
2013
|
-
this.stopRegistryListener = onSlashCommandRegistered(
|
|
2014
|
-
(def) => this.handleNewDeclaration(def)
|
|
2015
|
-
);
|
|
2016
|
-
this.registerCli();
|
|
2017
|
-
this.setupAutoSync();
|
|
2018
|
-
this.ctx.on("dispose", () => this.slashService.releaseAllLocks());
|
|
2019
|
-
this.ctx.on("dispose", () => this.dispose());
|
|
2020
|
-
}
|
|
2021
|
-
static {
|
|
2022
|
-
__name(this, "DiscordSlashService");
|
|
2023
|
-
}
|
|
2024
|
-
slashService;
|
|
2025
|
-
stopRegistryListener;
|
|
2026
|
-
ready;
|
|
2027
|
-
async seedDeclarations() {
|
|
2028
|
-
for (const def of getRegisteredSlashCommands()) {
|
|
2029
|
-
await this.slashService.registerDeclaration(def);
|
|
2030
|
-
}
|
|
2031
|
-
}
|
|
2032
|
-
async handleNewDeclaration(def) {
|
|
2033
|
-
try {
|
|
2034
|
-
await this.slashService.registerDeclaration(def);
|
|
2035
|
-
} catch (err) {
|
|
2036
|
-
this.logger.error(
|
|
2037
|
-
`[slash] failed to persist declaration ${def.key}: ${err instanceof Error ? err.message : String(err)}`
|
|
2038
|
-
);
|
|
2039
|
-
}
|
|
2040
|
-
}
|
|
2041
|
-
setupAutoSync() {
|
|
2042
|
-
const enableAutoSync = this.config.auto_sync_on_start ?? true;
|
|
2043
|
-
if (!enableAutoSync) return;
|
|
2044
|
-
this.ctx.on("ready", async () => {
|
|
2045
|
-
await this.ready;
|
|
2046
|
-
const guilds = this.resolveGuilds();
|
|
2047
|
-
if (!guilds.length) {
|
|
2048
|
-
this.logger.warn("[slash] no guilds configured for Discord sync; skip auto sync");
|
|
2049
|
-
return;
|
|
2050
|
-
}
|
|
2051
|
-
for (const guild of guilds) {
|
|
2052
|
-
try {
|
|
2053
|
-
const summary = await this.slashService.syncScope({
|
|
2054
|
-
type: "guild",
|
|
2055
|
-
id: guild
|
|
2056
|
-
});
|
|
2057
|
-
this.logger.info(this.formatSummary(guild, summary));
|
|
2058
|
-
} catch (err) {
|
|
2059
|
-
this.logger.error(
|
|
2060
|
-
`[slash] failed to sync guild ${guild}: ${err instanceof Error ? err.message : String(err)}`
|
|
2061
|
-
);
|
|
2062
|
-
}
|
|
2063
|
-
}
|
|
2064
|
-
});
|
|
2065
|
-
}
|
|
2066
|
-
registerCli() {
|
|
2067
|
-
this.ctx.command("slash.sync [targets:text]").option("preview", "-p, --preview").option("force", "-f, --force").action(async ({ options }, targetsArg) => {
|
|
2068
|
-
await this.ready;
|
|
2069
|
-
const targets = this.resolveTargets(targetsArg);
|
|
2070
|
-
if (!targets.length) return "No targets configured for slash sync.";
|
|
2071
|
-
const lines = [];
|
|
2072
|
-
for (const target of targets) {
|
|
2073
|
-
const label = target.type === "global" ? "global" : target.id;
|
|
2074
|
-
if (options?.preview) {
|
|
2075
|
-
const diff = await this.slashService.previewScope(target);
|
|
2076
|
-
lines.push(this.formatPreview(label, diff));
|
|
2077
|
-
} else {
|
|
2078
|
-
const summary = await this.slashService.syncScope(target, {
|
|
2079
|
-
force: options?.force
|
|
2080
|
-
});
|
|
2081
|
-
lines.push(this.formatSummary(label, summary));
|
|
2082
|
-
}
|
|
2083
|
-
}
|
|
2084
|
-
return lines.join("\n");
|
|
2085
|
-
});
|
|
2086
|
-
this.ctx.command("slash.preview [targets:text]").action(async (_, targetsArg) => {
|
|
2087
|
-
await this.ready;
|
|
2088
|
-
const targets = this.resolveTargets(targetsArg);
|
|
2089
|
-
if (!targets.length) return "No targets configured for slash sync.";
|
|
2090
|
-
const lines = [];
|
|
2091
|
-
for (const target of targets) {
|
|
2092
|
-
const label = target.type === "global" ? "global" : target.id;
|
|
2093
|
-
const diff = await this.slashService.previewScope(target);
|
|
2094
|
-
lines.push(this.formatPreview(label, diff));
|
|
2095
|
-
}
|
|
2096
|
-
return lines.join("\n");
|
|
2097
|
-
});
|
|
2098
|
-
this.ctx.command("slash.import-remote [targets:text]").action(async (_, targetsArg) => {
|
|
2099
|
-
await this.ready;
|
|
2100
|
-
const targets = this.resolveImportTargets(targetsArg);
|
|
2101
|
-
if (!targets.length) return "No targets configured for slash import.";
|
|
2102
|
-
const lines = [];
|
|
2103
|
-
for (const target of targets) {
|
|
2104
|
-
const inserted = await this.slashService.importRemoteCommands(target);
|
|
2105
|
-
const label = target.type === "global" ? "global" : target.id;
|
|
2106
|
-
lines.push(`[${label}] imported ${inserted.length} commands`);
|
|
2107
|
-
}
|
|
2108
|
-
return lines.join("\n");
|
|
2109
|
-
});
|
|
2110
|
-
}
|
|
2111
|
-
resolveTargets(overrides) {
|
|
2112
|
-
const tokens = overrides ? overrides.split(/[,;\s]+/).map((t) => t.trim()).filter(Boolean) : [];
|
|
2113
|
-
const targets = [];
|
|
2114
|
-
const seenGuilds = /* @__PURE__ */ new Set();
|
|
2115
|
-
let globalAdded = false;
|
|
2116
|
-
const baseGuilds = tokens.length > 0 ? [] : Array.from(
|
|
2117
|
-
/* @__PURE__ */ new Set([
|
|
2118
|
-
...this.config.test_guilds ?? [],
|
|
2119
|
-
...process.env.DEV_GUILD_ID ? [process.env.DEV_GUILD_ID] : []
|
|
2120
|
-
])
|
|
2121
|
-
).filter(Boolean);
|
|
2122
|
-
const source = tokens.length > 0 ? tokens : ["global", ...baseGuilds];
|
|
2123
|
-
for (const token of source) {
|
|
2124
|
-
if (token.toLowerCase() === "global") {
|
|
2125
|
-
if (!globalAdded) {
|
|
2126
|
-
targets.push({ type: "global" });
|
|
2127
|
-
globalAdded = true;
|
|
2128
|
-
}
|
|
2129
|
-
continue;
|
|
2130
|
-
}
|
|
2131
|
-
if (seenGuilds.has(token)) continue;
|
|
2132
|
-
seenGuilds.add(token);
|
|
2133
|
-
targets.push({ type: "guild", id: token });
|
|
2134
|
-
}
|
|
2135
|
-
return targets;
|
|
2136
|
-
}
|
|
2137
|
-
resolveImportTargets(overrides) {
|
|
2138
|
-
const tokens = overrides ? overrides.split(/[,;\s]+/).map((token) => token.trim()).filter(Boolean) : [];
|
|
2139
|
-
const targets = [];
|
|
2140
|
-
const guilds = tokens.length > 0 ? [] : Array.from(
|
|
2141
|
-
/* @__PURE__ */ new Set([
|
|
2142
|
-
...this.config.test_guilds ?? [],
|
|
2143
|
-
...process.env.DEV_GUILD_ID ? [process.env.DEV_GUILD_ID] : []
|
|
2144
|
-
])
|
|
2145
|
-
).filter(Boolean);
|
|
2146
|
-
const seenGuilds = /* @__PURE__ */ new Set();
|
|
2147
|
-
let globalAdded = false;
|
|
2148
|
-
const processToken = /* @__PURE__ */ __name((token) => {
|
|
2149
|
-
if (token.toLowerCase() === "global") {
|
|
2150
|
-
if (!globalAdded) {
|
|
2151
|
-
targets.push({ type: "global" });
|
|
2152
|
-
globalAdded = true;
|
|
2153
|
-
}
|
|
2154
|
-
return;
|
|
2155
|
-
}
|
|
2156
|
-
if (!seenGuilds.has(token)) {
|
|
2157
|
-
seenGuilds.add(token);
|
|
2158
|
-
targets.push({ type: "guild", id: token });
|
|
2159
|
-
}
|
|
2160
|
-
}, "processToken");
|
|
2161
|
-
if (tokens.length) {
|
|
2162
|
-
tokens.forEach(processToken);
|
|
2163
|
-
} else {
|
|
2164
|
-
processToken("global");
|
|
2165
|
-
guilds.forEach(processToken);
|
|
2166
|
-
}
|
|
2167
|
-
return targets;
|
|
2168
|
-
}
|
|
2169
|
-
formatPreview(guildId, diff) {
|
|
2170
|
-
const parts = [];
|
|
2171
|
-
if (diff.create.length) parts.push(`create(${diff.create.join(", ")})`);
|
|
2172
|
-
if (diff.update.length) parts.push(`update(${diff.update.join(", ")})`);
|
|
2173
|
-
if (diff.delete.length) parts.push(`delete(${diff.delete.join(", ")})`);
|
|
2174
|
-
const detail = parts.length ? parts.join(" | ") : "up-to-date";
|
|
2175
|
-
return `[${guildId}] ${detail}`;
|
|
2176
|
-
}
|
|
2177
|
-
formatSummary(guildId, summary) {
|
|
2178
|
-
return `[${guildId}] +${summary.created} / ~${summary.updated} / -${summary.deleted}`;
|
|
2179
|
-
}
|
|
2180
|
-
dispose() {
|
|
2181
|
-
this.stopRegistryListener();
|
|
2182
|
-
}
|
|
2183
|
-
resolveGuilds() {
|
|
2184
|
-
const configured = this.config.test_guilds ?? [];
|
|
2185
|
-
const envGuild = process.env.DEV_GUILD_ID ? [process.env.DEV_GUILD_ID] : [];
|
|
2186
|
-
return Array.from(/* @__PURE__ */ new Set([...configured, ...envGuild])).filter(Boolean);
|
|
2187
|
-
}
|
|
2188
|
-
};
|
|
2189
|
-
|
|
2190
|
-
// src/slash/locales/en-US.yml
|
|
2191
|
-
var en_US_default2 = { _config: { $desc: "Slash Module Settings", discord_token: "**Discord Token**", discord_application_id: "**Discord Application ID**", test_guilds: "**Guilds To Sync**", auto_sync_on_start: "**Auto Sync on Connect**" } };
|
|
2192
|
-
|
|
2193
|
-
// src/slash/locales/zh-CN.yml
|
|
2194
|
-
var zh_CN_default2 = { _config: { $desc: "Slash 模块设置", discord_token: "**Discord Token**", discord_application_id: "**Discord Application ID**", test_guilds: "**默认同步 Guild 列表**", auto_sync_on_start: "**连接后自动同步**" } };
|
|
2195
|
-
|
|
2196
|
-
// src/slash/index.ts
|
|
2197
|
-
var name3 = "Noah-Slash";
|
|
2198
|
-
var inject = ["database"];
|
|
2199
|
-
var logger2 = new import_koishi3.Logger("Noah-Slash");
|
|
2200
|
-
async function apply3(ctx, config) {
|
|
2201
|
-
;
|
|
2202
|
-
[
|
|
2203
|
-
["en-US", en_US_default2],
|
|
2204
|
-
["zh-CN", zh_CN_default2]
|
|
2205
|
-
].forEach(([lang, file]) => ctx.i18n.define(lang, file));
|
|
2206
|
-
ctx.plugin(database_exports, config.core);
|
|
2207
|
-
if (!config.slash.discord_token || !config.slash.discord_application_id) {
|
|
2208
|
-
logger2.warn("Discord token or application id missing, slash sync disabled.");
|
|
2209
|
-
return;
|
|
2210
|
-
}
|
|
2211
|
-
new DiscordSlashService(ctx, config.slash, logger2);
|
|
2212
|
-
}
|
|
2213
|
-
__name(apply3, "apply");
|
|
2214
|
-
|
|
2215
|
-
// src/core/commands/help.ts
|
|
2216
|
-
var helpSlashCommand = {
|
|
2217
|
-
key: "core.help",
|
|
2218
|
-
name: "help",
|
|
2219
|
-
description: "Display Noah help menu.",
|
|
2220
|
-
description_localizations: {
|
|
2221
|
-
"zh-CN": "显示 Noah 帮助菜单"
|
|
2222
|
-
},
|
|
2223
|
-
version: "1.0.0"
|
|
2224
|
-
};
|
|
2225
|
-
registerSlashCommand(helpSlashCommand);
|
|
2226
|
-
function help(ctx, config) {
|
|
2227
|
-
ctx.command("noah.help").alias("帮助").action(({ session }) => {
|
|
2228
|
-
return session.text(".content");
|
|
2229
|
-
});
|
|
2230
|
-
}
|
|
2231
|
-
__name(help, "help");
|
|
2232
|
-
|
|
2233
|
-
// src/core/utils/role.ts
|
|
2234
|
-
function hasPermission(...perms) {
|
|
2235
|
-
return perms.some((perm) => perm === true);
|
|
2236
|
-
}
|
|
2237
|
-
__name(hasPermission, "hasPermission");
|
|
2238
|
-
function isPluginAdmin(session, config) {
|
|
2239
|
-
return config.adminUsers?.includes(session.userId) ?? false;
|
|
2240
|
-
}
|
|
2241
|
-
__name(isPluginAdmin, "isPluginAdmin");
|
|
2242
|
-
function isGuildAdmin(session) {
|
|
2243
|
-
const platform = session.event.platform;
|
|
2244
|
-
let guildMember;
|
|
2245
|
-
if (platform === "onebot") {
|
|
2246
|
-
if (session.guildId == null) return true;
|
|
2247
|
-
guildMember = session.event.member.roles[0];
|
|
2248
|
-
return guildMember === "owner" || guildMember === "admin" || guildMember === "SUBCHANNEL_ADMIN" || guildMember === "OWNER" || guildMember === "ADMIN";
|
|
2249
|
-
} else if (platform === "qq") {
|
|
2250
|
-
return true;
|
|
2251
|
-
} else {
|
|
2252
|
-
return true;
|
|
2253
|
-
}
|
|
2254
|
-
}
|
|
2255
|
-
__name(isGuildAdmin, "isGuildAdmin");
|
|
2256
|
-
|
|
2257
|
-
// src/core/commands/locale.ts
|
|
2258
|
-
function locale(ctx, config) {
|
|
2259
|
-
ctx.command("locale").alias("language").option("channel", "-c").option("reset", "-r").userFields(["locales"]).channelFields(["locales"]).action(async ({ session, options }) => {
|
|
2260
|
-
if (options.channel && !hasPermission(isPluginAdmin(session, config), isGuildAdmin(session))) {
|
|
2261
|
-
return session.text(".noAuth");
|
|
2262
|
-
}
|
|
2263
|
-
if (options.reset) {
|
|
2264
|
-
if (options.channel) {
|
|
2265
|
-
session.channel.locales = [];
|
|
2266
|
-
await session.channel.$update();
|
|
2267
|
-
return session.text(".reset-channel");
|
|
2268
|
-
} else {
|
|
2269
|
-
session.user.locales = [];
|
|
2270
|
-
await session.user.$update();
|
|
2271
|
-
return session.text(".reset-user");
|
|
2272
|
-
}
|
|
2273
|
-
}
|
|
2274
|
-
const target = options.channel ? "channel" : "user";
|
|
2275
|
-
await session.send(session.text(`.set-${target}`));
|
|
2276
|
-
const select = await session.prompt();
|
|
2277
|
-
if (!select) return session.text("commands.timeout");
|
|
2278
|
-
if (select === "q") return session.text(".quit");
|
|
2279
|
-
const selectNum = parseInt(select, 10);
|
|
2280
|
-
if (isNaN(selectNum) || selectNum < 1 || selectNum > 2) {
|
|
2281
|
-
return session.text(".invalid-select");
|
|
2282
|
-
}
|
|
2283
|
-
const lang = selectNum === 1 ? "zh-CN" : "en-US";
|
|
2284
|
-
if (options.channel) {
|
|
2285
|
-
updateLocales(session.channel, lang);
|
|
2286
|
-
await session.channel.$update();
|
|
2287
|
-
} else {
|
|
2288
|
-
updateLocales(session.user, lang);
|
|
2289
|
-
await session.user.$update();
|
|
2290
|
-
}
|
|
2291
|
-
return session.text(".success");
|
|
2292
|
-
});
|
|
2293
|
-
}
|
|
2294
|
-
__name(locale, "locale");
|
|
2295
|
-
function updateLocales(target, lang) {
|
|
2296
|
-
const index = target.locales.indexOf(lang);
|
|
2297
|
-
if (index !== -1) {
|
|
2298
|
-
target.locales.splice(index, 1);
|
|
2299
|
-
}
|
|
2300
|
-
target.locales.unshift(lang);
|
|
2301
|
-
}
|
|
2302
|
-
__name(updateLocales, "updateLocales");
|
|
2303
|
-
|
|
2304
|
-
// src/core/commands/maintain.ts
|
|
2305
|
-
function maintain(ctx, config) {
|
|
2306
|
-
ctx.command("maintain").alias("维护").option("stop", "-s").action(async ({ session, options }) => {
|
|
2307
|
-
if (!hasPermission(isPluginAdmin(session, config))) {
|
|
2308
|
-
return session.text(".no-auth");
|
|
2309
|
-
}
|
|
2310
|
-
let card2 = "";
|
|
2311
|
-
let text = "";
|
|
2312
|
-
if (options.stop) {
|
|
2313
|
-
session.send(session.text(".stop"));
|
|
2314
|
-
card2 = config.guildNameCards[0];
|
|
2315
|
-
text = ".success-stop";
|
|
2316
|
-
} else {
|
|
2317
|
-
session.send(session.text(".start"));
|
|
2318
|
-
card2 = "Noah | 🚧维护中";
|
|
2319
|
-
text = ".success-start";
|
|
2320
|
-
}
|
|
2321
|
-
const bots = ctx.bots;
|
|
2322
|
-
for (const bot of bots) {
|
|
2323
|
-
if (bot.platform !== "onebot") continue;
|
|
2324
|
-
const guilds = bot.getGuildIter();
|
|
2325
|
-
for await (const guild of guilds) {
|
|
2326
|
-
await bot.internal.setGroupCard(guild.id, bot.user.id, card2);
|
|
2327
|
-
}
|
|
2328
|
-
await new Promise((resolve3) => setTimeout(resolve3, 3e3));
|
|
1574
|
+
const bots = ctx.bots;
|
|
1575
|
+
for (const bot of bots) {
|
|
1576
|
+
if (bot.platform !== "onebot") continue;
|
|
1577
|
+
const guilds = bot.getGuildIter();
|
|
1578
|
+
for await (const guild of guilds) {
|
|
1579
|
+
await bot.internal.setGroupCard(guild.id, bot.user.id, card2);
|
|
1580
|
+
}
|
|
1581
|
+
await new Promise((resolve3) => setTimeout(resolve3, 3e3));
|
|
2329
1582
|
}
|
|
2330
1583
|
return session.text(text);
|
|
2331
1584
|
});
|
|
@@ -2333,7 +1586,7 @@ function maintain(ctx, config) {
|
|
|
2333
1586
|
__name(maintain, "maintain");
|
|
2334
1587
|
|
|
2335
1588
|
// src/core/commands/server.ts
|
|
2336
|
-
var
|
|
1589
|
+
var import_koishi5 = require("koishi");
|
|
2337
1590
|
|
|
2338
1591
|
// src/types/index.ts
|
|
2339
1592
|
var serverValues = ["asphyxia", "mao", "official"];
|
|
@@ -2354,8 +1607,50 @@ function normalizeUrl(url) {
|
|
|
2354
1607
|
}
|
|
2355
1608
|
}
|
|
2356
1609
|
__name(normalizeUrl, "normalizeUrl");
|
|
1610
|
+
async function promptServerSelect(session, servers, defaultId, prefix) {
|
|
1611
|
+
if (session.platform === "discord") {
|
|
1612
|
+
const buttons = [
|
|
1613
|
+
(0, import_koishi5.h)("button", { id: `${prefix}/0`, type: "secondary" }, "➕ 添加新服务器"),
|
|
1614
|
+
...servers.map(
|
|
1615
|
+
(srv, i) => (0, import_koishi5.h)(
|
|
1616
|
+
"button",
|
|
1617
|
+
{
|
|
1618
|
+
id: `${prefix}/${i + 1}`,
|
|
1619
|
+
type: srv.id === defaultId ? "primary" : "secondary"
|
|
1620
|
+
},
|
|
1621
|
+
srv.name
|
|
1622
|
+
)
|
|
1623
|
+
)
|
|
1624
|
+
];
|
|
1625
|
+
await session.send((0, import_koishi5.h)("message", ["请选择一个服务器:", ...buttons]));
|
|
1626
|
+
const click = await session.prompt();
|
|
1627
|
+
if (!click) return null;
|
|
1628
|
+
const match = click.match(new RegExp(`^${prefix.replace(".", "\\.")}\\/(\\d+)$`));
|
|
1629
|
+
if (!match) return -1;
|
|
1630
|
+
const n = Number(match[1]);
|
|
1631
|
+
if (n > servers.length) return -1;
|
|
1632
|
+
return n;
|
|
1633
|
+
}
|
|
1634
|
+
let serverListMsg = "";
|
|
1635
|
+
for (let i = 0; i < servers.length; i++) {
|
|
1636
|
+
if (servers[i].id === defaultId) {
|
|
1637
|
+
serverListMsg += `<p>${i + 1}. ${servers[i].name} < 默认服务器</p>`;
|
|
1638
|
+
} else {
|
|
1639
|
+
serverListMsg += `<p>${i + 1}. ${servers[i].name}</p>`;
|
|
1640
|
+
}
|
|
1641
|
+
}
|
|
1642
|
+
const msg = import_koishi5.h.unescape(session.text(".menu-select", { server_list: serverListMsg })).replace("< 默认服务器", "< 默认服务器");
|
|
1643
|
+
await session.send(msg);
|
|
1644
|
+
const select = await session.prompt();
|
|
1645
|
+
if (!select) return null;
|
|
1646
|
+
if (select === "q") return -2;
|
|
1647
|
+
const selectNum = parseInt(select, 10);
|
|
1648
|
+
if (isNaN(selectNum) || selectNum < 0 || selectNum > servers.length) return -1;
|
|
1649
|
+
return selectNum;
|
|
1650
|
+
}
|
|
1651
|
+
__name(promptServerSelect, "promptServerSelect");
|
|
2357
1652
|
function server(ctx, config) {
|
|
2358
|
-
ctx.command("server").alias("服务器管理").option("channel", "-c").userFields(["id"]).channelFields(["id"]).action(async ({ session, options }) => {
|
|
1653
|
+
ctx.command("server", { slash: true }).alias("服务器管理").option("channel", "-c").userFields(["id"]).channelFields(["id"]).action(async ({ session, options }) => {
|
|
2359
1654
|
const serverService = new ServerService(ctx);
|
|
2360
1655
|
const userService = new UserService(ctx);
|
|
2361
1656
|
const atGuild = session.guildId != null;
|
|
@@ -2401,24 +1696,11 @@ function server(ctx, config) {
|
|
|
2401
1696
|
__name(server, "server");
|
|
2402
1697
|
async function showChannelServerSelectMenu(ctx, session, serverService, userService, uid, userDefaultServerId, cid, channelDefaultServerId) {
|
|
2403
1698
|
const res = await serverService.getServersByCid(cid);
|
|
2404
|
-
|
|
2405
|
-
|
|
2406
|
-
|
|
2407
|
-
|
|
2408
|
-
|
|
2409
|
-
serverListMsg += `<p>${i + 1}. ${res[i].name}</p>`;
|
|
2410
|
-
}
|
|
2411
|
-
}
|
|
2412
|
-
const msg = import_koishi4.h.unescape(session.text(".menu-select", { server_list: serverListMsg })).replace("< 群聊默认服务器", "< 群聊默认服务器");
|
|
2413
|
-
await session.send(msg);
|
|
2414
|
-
const select = await session.prompt();
|
|
2415
|
-
if (!select) return session.text("commands.timeout");
|
|
2416
|
-
if (select === "q") return session.text(".quit");
|
|
2417
|
-
const selectNum = parseInt(select, 10);
|
|
2418
|
-
if (isNaN(selectNum) || selectNum < 0 || selectNum > res.length) {
|
|
2419
|
-
return session.text(".invalid-select");
|
|
2420
|
-
}
|
|
2421
|
-
if (selectNum === 0) {
|
|
1699
|
+
const selected = await promptServerSelect(session, res, channelDefaultServerId, "srv.ch");
|
|
1700
|
+
if (selected === null) return session.text("commands.timeout");
|
|
1701
|
+
if (selected === -1) return session.text(".invalid-select");
|
|
1702
|
+
if (selected === -2) return session.text(".quit");
|
|
1703
|
+
if (selected === 0) {
|
|
2422
1704
|
return addServer(
|
|
2423
1705
|
ctx,
|
|
2424
1706
|
session,
|
|
@@ -2430,42 +1712,28 @@ async function showChannelServerSelectMenu(ctx, session, serverService, userServ
|
|
|
2430
1712
|
cid,
|
|
2431
1713
|
channelDefaultServerId
|
|
2432
1714
|
);
|
|
2433
|
-
} else {
|
|
2434
|
-
return showServerMenu(
|
|
2435
|
-
ctx,
|
|
2436
|
-
session,
|
|
2437
|
-
serverService,
|
|
2438
|
-
userService,
|
|
2439
|
-
res[selectNum - 1],
|
|
2440
|
-
"channel",
|
|
2441
|
-
uid,
|
|
2442
|
-
userDefaultServerId,
|
|
2443
|
-
cid,
|
|
2444
|
-
channelDefaultServerId
|
|
2445
|
-
);
|
|
2446
1715
|
}
|
|
1716
|
+
return showServerMenu(
|
|
1717
|
+
ctx,
|
|
1718
|
+
session,
|
|
1719
|
+
serverService,
|
|
1720
|
+
userService,
|
|
1721
|
+
res[selected - 1],
|
|
1722
|
+
"channel",
|
|
1723
|
+
uid,
|
|
1724
|
+
userDefaultServerId,
|
|
1725
|
+
cid,
|
|
1726
|
+
channelDefaultServerId
|
|
1727
|
+
);
|
|
2447
1728
|
}
|
|
2448
1729
|
__name(showChannelServerSelectMenu, "showChannelServerSelectMenu");
|
|
2449
1730
|
async function showUserServerSelectMenu(ctx, session, serverService, userService, uid, userDefaultServerId, cid, channelDefaultServerId) {
|
|
2450
1731
|
const res = await serverService.getServersByUid(uid);
|
|
2451
|
-
|
|
2452
|
-
|
|
2453
|
-
|
|
2454
|
-
|
|
2455
|
-
|
|
2456
|
-
serverListMsg += `<p>${i + 1}. ${res[i].name}</p>`;
|
|
2457
|
-
}
|
|
2458
|
-
}
|
|
2459
|
-
const msg = import_koishi4.h.unescape(session.text(".menu-select", { server_list: serverListMsg })).replace("< 默认服务器", "< 默认服务器");
|
|
2460
|
-
await session.send(msg);
|
|
2461
|
-
const select = await session.prompt();
|
|
2462
|
-
if (!select) return session.text("commands.timeout");
|
|
2463
|
-
if (select === "q") return session.text(".quit");
|
|
2464
|
-
const selectNum = parseInt(select, 10);
|
|
2465
|
-
if (isNaN(selectNum) || selectNum < 0 || selectNum > res.length) {
|
|
2466
|
-
return session.text(".invalid-select");
|
|
2467
|
-
}
|
|
2468
|
-
if (selectNum === 0) {
|
|
1732
|
+
const selected = await promptServerSelect(session, res, userDefaultServerId, "srv.usr");
|
|
1733
|
+
if (selected === null) return session.text("commands.timeout");
|
|
1734
|
+
if (selected === -1) return session.text(".invalid-select");
|
|
1735
|
+
if (selected === -2) return session.text(".quit");
|
|
1736
|
+
if (selected === 0) {
|
|
2469
1737
|
return addServer(
|
|
2470
1738
|
ctx,
|
|
2471
1739
|
session,
|
|
@@ -2477,30 +1745,49 @@ async function showUserServerSelectMenu(ctx, session, serverService, userService
|
|
|
2477
1745
|
cid,
|
|
2478
1746
|
channelDefaultServerId
|
|
2479
1747
|
);
|
|
2480
|
-
} else {
|
|
2481
|
-
return showServerMenu(
|
|
2482
|
-
ctx,
|
|
2483
|
-
session,
|
|
2484
|
-
serverService,
|
|
2485
|
-
userService,
|
|
2486
|
-
res[selectNum - 1],
|
|
2487
|
-
"user",
|
|
2488
|
-
uid,
|
|
2489
|
-
userDefaultServerId,
|
|
2490
|
-
cid,
|
|
2491
|
-
channelDefaultServerId
|
|
2492
|
-
);
|
|
2493
1748
|
}
|
|
1749
|
+
return showServerMenu(
|
|
1750
|
+
ctx,
|
|
1751
|
+
session,
|
|
1752
|
+
serverService,
|
|
1753
|
+
userService,
|
|
1754
|
+
res[selected - 1],
|
|
1755
|
+
"user",
|
|
1756
|
+
uid,
|
|
1757
|
+
userDefaultServerId,
|
|
1758
|
+
cid,
|
|
1759
|
+
channelDefaultServerId
|
|
1760
|
+
);
|
|
2494
1761
|
}
|
|
2495
1762
|
__name(showUserServerSelectMenu, "showUserServerSelectMenu");
|
|
2496
1763
|
async function showServerMenu(ctx, session, serverService, userService, server2, from, uid, userDefaultServerId, cid, channelDefaultServerId) {
|
|
2497
|
-
|
|
2498
|
-
|
|
2499
|
-
|
|
2500
|
-
|
|
2501
|
-
|
|
2502
|
-
|
|
2503
|
-
|
|
1764
|
+
let selectNum;
|
|
1765
|
+
if (session.platform === "discord") {
|
|
1766
|
+
const buttons = [
|
|
1767
|
+
(0, import_koishi5.h)("button", { id: "srv.act/1", type: "primary" }, "⭐ 设为默认"),
|
|
1768
|
+
(0, import_koishi5.h)("button", { id: "srv.act/2", type: "secondary" }, "🔗 修改URL"),
|
|
1769
|
+
(0, import_koishi5.h)("button", { id: "srv.act/3", type: "danger" }, "🗑️ 删除"),
|
|
1770
|
+
(0, import_koishi5.h)("button", { id: "srv.act/4", type: "secondary" }, "📝 重命名"),
|
|
1771
|
+
(0, import_koishi5.h)("button", { id: "srv.act/0", type: "secondary" }, "↩️ 返回")
|
|
1772
|
+
];
|
|
1773
|
+
await session.send(
|
|
1774
|
+
(0, import_koishi5.h)("message", [`**[${server2.name}]**
|
|
1775
|
+
> 类型: ${server2.type}`, ...buttons])
|
|
1776
|
+
);
|
|
1777
|
+
const click = await session.prompt();
|
|
1778
|
+
if (!click) return session.text("commands.timeout");
|
|
1779
|
+
const match = click.match(/^srv\.act\/(\d+)$/);
|
|
1780
|
+
if (!match) return session.text(".invalid-select");
|
|
1781
|
+
selectNum = Number(match[1]);
|
|
1782
|
+
} else {
|
|
1783
|
+
await session.send(session.text(".menu", server2));
|
|
1784
|
+
const select = await session.prompt();
|
|
1785
|
+
if (!select) return session.text("commands.timeout");
|
|
1786
|
+
if (select === "q") return session.text(".quit");
|
|
1787
|
+
selectNum = parseInt(select, 10);
|
|
1788
|
+
if (isNaN(selectNum) || selectNum < 0 || selectNum > 4) {
|
|
1789
|
+
return session.text(".invalid-select");
|
|
1790
|
+
}
|
|
2504
1791
|
}
|
|
2505
1792
|
if (selectNum === 0) {
|
|
2506
1793
|
if (from === "user")
|
|
@@ -2618,27 +1905,41 @@ async function showServerMenu(ctx, session, serverService, userService, server2,
|
|
|
2618
1905
|
}
|
|
2619
1906
|
__name(showServerMenu, "showServerMenu");
|
|
2620
1907
|
async function addServer(ctx, session, serverService, userService, from, uid, userDefaultServerId, cid, channelDefaultServerId) {
|
|
2621
|
-
let
|
|
2622
|
-
|
|
2623
|
-
|
|
2624
|
-
|
|
2625
|
-
|
|
2626
|
-
|
|
2627
|
-
|
|
2628
|
-
|
|
2629
|
-
|
|
2630
|
-
|
|
2631
|
-
|
|
2632
|
-
|
|
1908
|
+
let serverType;
|
|
1909
|
+
if (session.platform === "discord") {
|
|
1910
|
+
const buttons = serverValues.map(
|
|
1911
|
+
(type) => (0, import_koishi5.h)("button", { id: `srv.type/${type}`, type: "secondary" }, type)
|
|
1912
|
+
);
|
|
1913
|
+
await session.send((0, import_koishi5.h)("message", ["请选择服务器类型:", ...buttons]));
|
|
1914
|
+
const click = await session.prompt();
|
|
1915
|
+
if (!click) return session.text("commands.timeout");
|
|
1916
|
+
const match = click.match(/^srv\.type\/(.+)$/);
|
|
1917
|
+
if (!match || !serverValues.includes(match[1]))
|
|
1918
|
+
return session.text(".invalid-select");
|
|
1919
|
+
serverType = match[1];
|
|
1920
|
+
} else {
|
|
1921
|
+
let serverTypeListMsg = "";
|
|
1922
|
+
for (let i = 0; i < serverValues.length; i++) {
|
|
1923
|
+
serverTypeListMsg += `<p>${i + 1}. ${serverValues[i]}</p>`;
|
|
1924
|
+
}
|
|
1925
|
+
const msg = import_koishi5.h.unescape(session.text(".add-type", { server_type_list: serverTypeListMsg }));
|
|
1926
|
+
await session.send(msg);
|
|
1927
|
+
const select = await session.prompt();
|
|
1928
|
+
if (!select) return session.text("commands.timeout");
|
|
1929
|
+
if (select === "q") return session.text(".quit");
|
|
1930
|
+
const selectNum = parseInt(select, 10);
|
|
1931
|
+
if (isNaN(selectNum) || selectNum < 1 || selectNum > serverValues.length) {
|
|
1932
|
+
return session.text(".invalid-select");
|
|
1933
|
+
}
|
|
1934
|
+
serverType = serverValues[selectNum - 1];
|
|
2633
1935
|
}
|
|
2634
|
-
const serverType = serverValues[selectNum - 1];
|
|
2635
1936
|
let url;
|
|
2636
1937
|
let serverName;
|
|
2637
1938
|
if (serverType === "mao") {
|
|
2638
|
-
url = ctx.
|
|
1939
|
+
url = ctx.globalConfig.maoServerUrl;
|
|
2639
1940
|
serverName = "MaoMaNi";
|
|
2640
1941
|
} else if (serverType === "official") {
|
|
2641
|
-
url = ctx.
|
|
1942
|
+
url = ctx.globalConfig.official_support_url;
|
|
2642
1943
|
serverName = "Official";
|
|
2643
1944
|
} else {
|
|
2644
1945
|
let attempts = 0;
|
|
@@ -2698,25 +1999,26 @@ async function addServer(ctx, session, serverService, userService, from, uid, us
|
|
|
2698
1999
|
__name(addServer, "addServer");
|
|
2699
2000
|
|
|
2700
2001
|
// src/core/command.ts
|
|
2701
|
-
var
|
|
2702
|
-
function
|
|
2002
|
+
var name2 = "command";
|
|
2003
|
+
function apply2(ctx, config) {
|
|
2703
2004
|
help(ctx, config);
|
|
2704
2005
|
bind(ctx, config);
|
|
2705
2006
|
card(ctx, config);
|
|
2706
2007
|
server(ctx, config);
|
|
2707
2008
|
locale(ctx, config);
|
|
2009
|
+
link(ctx, config);
|
|
2708
2010
|
maintain(ctx, config);
|
|
2709
2011
|
}
|
|
2710
|
-
__name(
|
|
2012
|
+
__name(apply2, "apply");
|
|
2711
2013
|
|
|
2712
2014
|
// src/core/database.ts
|
|
2713
|
-
var
|
|
2714
|
-
__export(
|
|
2715
|
-
apply: () =>
|
|
2716
|
-
name: () =>
|
|
2015
|
+
var database_exports = {};
|
|
2016
|
+
__export(database_exports, {
|
|
2017
|
+
apply: () => apply3,
|
|
2018
|
+
name: () => name3
|
|
2717
2019
|
});
|
|
2718
|
-
var
|
|
2719
|
-
function
|
|
2020
|
+
var name3 = "database";
|
|
2021
|
+
function apply3(ctx) {
|
|
2720
2022
|
ctx.model.extend("user", {
|
|
2721
2023
|
defaultCardId: "unsigned",
|
|
2722
2024
|
defaultServerId: "unsigned"
|
|
@@ -2784,13 +2086,13 @@ function apply5(ctx) {
|
|
|
2784
2086
|
}
|
|
2785
2087
|
);
|
|
2786
2088
|
}
|
|
2787
|
-
__name(
|
|
2089
|
+
__name(apply3, "apply");
|
|
2788
2090
|
|
|
2789
2091
|
// src/core/event.ts
|
|
2790
2092
|
var event_exports = {};
|
|
2791
2093
|
__export(event_exports, {
|
|
2792
|
-
apply: () =>
|
|
2793
|
-
name: () =>
|
|
2094
|
+
apply: () => apply4,
|
|
2095
|
+
name: () => name4
|
|
2794
2096
|
});
|
|
2795
2097
|
|
|
2796
2098
|
// src/core/events/friend.ts
|
|
@@ -2941,44 +2243,47 @@ function guildNameCard(ctx, config) {
|
|
|
2941
2243
|
__name(guildNameCard, "guildNameCard");
|
|
2942
2244
|
|
|
2943
2245
|
// src/core/event.ts
|
|
2944
|
-
var
|
|
2945
|
-
function
|
|
2246
|
+
var name4 = "event";
|
|
2247
|
+
function apply4(ctx, config) {
|
|
2946
2248
|
friendRequest(ctx);
|
|
2947
2249
|
guildRequest(ctx, config);
|
|
2948
2250
|
guildNameCard(ctx, config);
|
|
2949
2251
|
}
|
|
2950
|
-
__name(
|
|
2252
|
+
__name(apply4, "apply");
|
|
2951
2253
|
|
|
2952
2254
|
// src/core/locales/en-US.yml
|
|
2953
|
-
var
|
|
2255
|
+
var en_US_default2 = { _config: { $desc: "Core Module Settings", adminUsers: "**Plugin Admin** User ID (use inspect command to get)", guildNameCards: "**Group Card** Name List", maoServerUrl: "**Mao Server** URL address", official_support_url: "**Official Support** URL address" }, commands: { maintain: { description: "Switch to maintenance mode", messages: { "no-auth": "<p>You don't have permission to use this feature~</p>", start: "<p>Entering maintenance mode</p>", "success-start": "<p>Successfully switched to maintenance mode</p>", stop: "<p>Exiting maintenance mode</p>", "success-stop": "<p>Successfully exited maintenance mode</p>" } }, timeout: "Noah didn't wait for your reply, please try again!", noah: { help: { description: "Show Noah help information", messages: { content: "<p>Guide:</p>\nhttps://docs.logthm.cn/noah" } } }, locale: { description: "Set language", messages: { "no-auth": "<p>Only group admins can use this feature~</p>", "invalid-select": "<p>No such option!</p>", quit: "<p>Quit!</p>", "reset-channel": "<p>Reset successfully!</p>", "reset-user": "<p>Reset successfully!</p>", success: "<p>Set successfully!</p>", "set-channel": "<p>Select the default language for group chats:</p>\n<p>1. 简体中文</p>\n<p>2. English</p>\n<p>q. Quit</p>", "set-user": "<p>Select the language you use:</p>\n<p>1. 中文</p>\n<p>2. English</p>\n<p>q. Quit</p>" } }, link: { description: "Link account to another platform", options: { remove: "Unlink" }, messages: { "generated-1": `<p>The Link command can be used to link user data across multiple platforms. During the linking process, the original platform's user data is fully preserved, while the target platform's user data is overwritten.</p>
|
|
2256
|
+
<p>Please make sure the current platform is your target platform and send the following text to the bot on the original platform within 5 minutes:</p>
|
|
2257
|
+
<p>{0}</p>
|
|
2258
|
+
<p>Once linked, you can use "link -r" to unlink at any time.</p>`, "generated-2": "<p>Token verified! Now proceeding to the second step.</p>\n<p>Please send the following text to the bot on the target platform within 5 minutes:</p>\n<p>{0}</p>\n<p>Note: The current platform is your original platform. User data here will overwrite the target platform's data.</p>", "self-1": "<p>Please enter this on the original platform.</p>", "self-2": "<p>Please enter this on the target platform.</p>", success: "<p>Account linked successfully!</p>", "remove-success": "<p>Account unlinked successfully!</p>", "remove-original": "<p>Cannot unlink: this is your original account.</p>" } }, bind: { description: "Bind card", messages: { prompt: "<p>Please enter your card number:</p>", "convert-access-failed": "<p>Failed to convert Access Code, please use 16-digit card code instead.</p>", "invalid-code": "<p>The card number is incorrect, if you don't remember it, go to the arcade to check it~</p>", name: "<p>Received! Please give this card a name, no spaces allowed:</p>", "invalid-name": "<p>No spaces allowed! Please try again o(一︿一+)o</p>", success: "<p>Bound successfully, your card information is as follows:</p>\n<p>[{cardName}]</p>\n<p>{cardCode}</p>" } }, card: { description: "Manage cards", options: { detail: "Show detailed card information" }, messages: { "invalid-code": "<p>The card number is incorrect, if you don't remember it, go to the arcade to check it~</p>", "invalid-select": "<p>No such option!</p>", "lookup-error": "Lookup failed: {message}", "lookup-error-unknown": "Lookup failed: unknown error", "menu-select": "<p>[Card Management]</p>\n{card_list}\n<p>0. Add new card</p>\n<p>Please enter the serial number:</p>", menu: "<p>[{name}]</p>\n<p>1. Set as default card</p>\n<p>2. View or modify card number</p>\n<p>3. Delete the card</p>\n<p>4. Rename the card</p>\n<p>5. Bind the card to a specified server</p>\n<p>0. Return to card selection</p>\n<p>Please enter the serial number:</p>", "menu-has-bound-server": "<p>[{name}]</p>\n<p>Bound server: {defaultServerName}</p>\n<p>1. Set as default card</p>\n<p>2. View or modify card number</p>\n<p>3. Delete the card</p>\n<p>4. Rename the card</p>\n<p>5. Bind the card to a specified server</p>\n<p>0. Return to card selection</p>\n<p>Please enter the serial number:</p>", "menu-1-success": "<p>Set successfully!</p>", "menu-1-error-duplicate": "<p>The selected card is the same as the old default card!</p>", "menu-2-prompt": "<p>Card number: {code}</p>\n<p>Please enter the new card number:</p>\n<p>Enter 0 to return</p>", "menu-2-success": "<p>Modified successfully!</p>", "menu-2-error-invalid-code": "<p>The card number is incorrect, if you don't remember it, go to the arcade to check it~</p>", "menu-3-success": "<p>Deleted successfully!</p>", "menu-4-prompt": "<p>Enter a new name, no spaces allowed:</p>", "menu-4-error-invalid-name": "<p>No spaces allowed! Please try again o(一︿一+)o</p>", "menu-4-success": "<p>Modified successfully!</p>", "menu-5": "{server_list}\n<p>Please enter the serial number:</p>", "menu-5-success": "<p>Set successfully!</p>", "menu-5-error-duplicate": "<p>The selected server is the same as the old default server!</p>" } }, server: { description: "Manage servers", messages: { "no-channel": "<p>Please use this feature in group chats~</p>", "invalid-select": "<p>No such option!</p>", "no-auth": "<p>Only group admins can use this feature~</p>", "admin-menu-select": "<p>[Group server management]</p>\n{server_list}\n<p>0. Add new server</p>\n<p>Please enter the serial number:</p>", "menu-select": "<p>[Server management]</p>\n{server_list}\n<p>0. Add new server</p>\n<p>Please enter the serial number:</p>", menu: "<p>[{name}]</p>\n<p>Type: {type}</p>\n<p>1. Set as default server</p>\n<p>2. View or modify url</p>\n<p>3. Delete the server</p>\n<p>4. Rename the server</p>\n<p>0. Return to server selection</p>\n<p>Please enter the serial number:</p>", "menu-1-success": "<p>Set successfully!</p>", "menu-1-error-duplicate": "<p>The selected server is the same as the old default server!</p>", "menu-2-prompt": "<p>The server's url: {baseUrl}</p>\n<p>Please enter the new server url:</p>\n<p>Enter 0 to return</p>", "menu-2-success": "<p>Modified successfully!</p>", "menu-2-invalid-url": "<p>Invalid URL format! Please enter a valid url address.</p>", "menu-2-too-many-attempts": "<p>Too many attempts. Operation cancelled.</p>", "menu-3-success": "<p>Deleted successfully!</p>", "menu-4-prompt": "<p>Enter a new name, no spaces allowed:</p>", "menu-4-error-invalid-name": "<p>No spaces allowed! Please try again o(一︿一+)o</p>", "menu-4-success": "<p>Modified successfully!</p>", "add-type": "<p>Please select the server type:</p>\n{server_type_list}", "add-url": "<p>Please enter the server url:</p>", "add-invalid-url": "<p>Invalid URL format! Please enter a valid url address.</p>", "add-too-many-attempts": "<p>Too many attempts. Operation cancelled.</p>", "add-name": "<p>Received! Please give the server a name, no spaces allowed:</p>", "add-invalid-name": "<p>No spaces allowed! Please try again o(一︿一+)o</p>", "add-success": "<p>Added successfully, the server information is as follows:</p>\n<p>[{serverName}]</p>\n<p>Type: {serverType}</p>" } } } };
|
|
2954
2259
|
|
|
2955
2260
|
// src/core/locales/zh-CN.yml
|
|
2956
|
-
var
|
|
2261
|
+
var zh_CN_default2 = { _config: { $desc: "Core 模块设置", adminUsers: "**插件管理员** 的用户id (使用 inspect 指令获取)", guildNameCards: "**群聊卡片** 的名称列表", maoServerUrl: "**猫网服务器** 的 URL 地址", official_support_url: "**官方支持** 的 URL 地址" }, commands: { maintain: { description: "切换到维护模式", messages: { "no-auth": "<p>你没有权限使用本功能哦~</p>", start: "<p>正在进入维护模式</p>", "success-start": "<p>成功切换到维护模式</p>", stop: "<p>正在退出维护模式</p>", "success-stop": "<p>成功退出维护模式</p>" } }, timeout: "Noah 没等到你的回复,请重试!", noah: { help: { description: "显示 Noah 帮助信息", messages: { content: "<p>使用文档:</p>\nhttps://docs.logthm.cn/noah" } } }, locale: { description: "设置语言", messages: { "no-auth": "<p>只有群管理员能使用本功能哦~</p>", "invalid-select": "<p>没有该选项!</p>", quit: "<p>已退出~</p>", "reset-channel": "<p>重置成功!</p>", "reset-user": "<p>重置成功!</p>", success: "<p>设置成功!</p>", "set-channel": "<p>选择群聊默认使用的语言:</p>\n<p>1. 简体中文</p>\n<p>2. English</p>\n<p>q. 退出</p>", "set-user": "<p>选择你使用的语言:</p>\n<p>1. 简体中文</p>\n<p>2. English</p>\n<p>q. 退出</p>" } }, link: { description: "关联账号到其他平台", options: { remove: "解除关联" }, messages: { "generated-1": "<p>Link 指令可用于在多个平台间关联用户数据。关联过程中,原始平台的用户数据将完全保留,而目标平台的用户数据将被原始平台的数据所覆盖。</p>\n<p>请确认当前平台是你的目标平台,并在 5 分钟内使用你的账号在原始平台内向机器人发送以下文本:</p>\n<p>{0}</p>\n<p>关联完成后,你可以随时使用「link -r」来解除关联。</p>", "generated-2": "<p>令牌核验成功!下面将进行第二步操作。</p>\n<p>请在 5 分钟内使用你的账号在目标平台内向机器人发送以下文本:</p>\n<p>{0}</p>\n<p>注意:当前平台是你的原始平台,这里的用户数据将覆盖目标平台的数据。</p>", "self-1": "<p>请前往原始平台输入。</p>", "self-2": "<p>请前往目标平台输入。</p>", success: "<p>账号关联成功!</p>", "remove-success": "<p>账号解除关联成功!</p>", "remove-original": "<p>无法解除关联:这是你的原始账号。</p>" } }, bind: { description: "绑定卡片", messages: { prompt: "<p>请输入你的卡号:</p>", "convert-access-failed": "<p>转换 Access Code 失败,请使用 16 位卡号进行绑定。</p>", "invalid-code": "<p>卡号不对哟,不记得的话去机台刷一下吧~</p>", name: "<p>收到!为这张卡取一个名字吧,不要带空格哦:</p>", "invalid-name": "<p>名字不要带空格!重来重来 o(一︿一+)o</p>", success: "<p>绑好啦,你的卡片信息如下:</p>\n<p>[{cardName}]</p>\n<p>{cardCode}</p>" } }, card: { description: "管理卡片", options: { detail: "显示卡片详细信息" }, messages: { "invalid-code": "<p>卡号不对哟,不记得的话去机台刷一下吧~</p>", "invalid-select": "<p>没有该选项!</p>", quit: "<p>已退出~</p>", "lookup-error": "查询失败: {message}", "lookup-error-unknown": "查询失败: 未知错误", "menu-select": "<p>[卡片管理]</p>\n{card_list}\n<p>0. 添加新卡片</p>\n<p>q. 退出菜单</p>\n<p>请输入序号:</p>", menu: "<p>[{name}]</p>\n<p>1. 设为默认卡片</p>\n<p>2. 查看或修改卡号</p>\n<p>3. 删除该卡</p>\n<p>4. 重命名该卡片</p>\n<p>5. 将卡片与指定服务器绑定</p>\n<p>0. 返回卡片选择</p>\n<p>q. 退出菜单</p>\n<p>请输入序号:</p>", "menu-has-bound-server": "<p>[{name}]</p>\n<p>已绑定服务器:{defaultServerName}</p>\n<p>1. 设为默认卡片</p>\n<p>2. 查看或修改卡号</p>\n<p>3. 删除该卡</p>\n<p>4. 重命名该卡片</p>\n<p>5. 将卡片与指定服务器绑定</p>\n<p>0. 返回卡片选择</p>\n<p>q. 退出菜单</p>\n<p>请输入序号:</p>", "menu-1-success": "<p>设置成功!</p>", "menu-1-error-duplicate": "<p>选择的卡片与旧的默认卡片相同!</p>", "menu-2-prompt": "<p>卡号:{code}</p>\n<p>请输入新的卡号:</p>\n<p>0. 返回</p>\n<p>q. 退出</p>", "menu-2-success": "<p>修改成功!</p>", "menu-2-error-invalid-code": "<p>卡号不对哟,不记得的话去机台刷一下吧~</p>", "menu-3-success": "<p>已删除!</p>", "menu-4-prompt": "<p>输入一个新名字,不要带空格哦:</p>\n<p>q. 退出</p>", "menu-4-error-invalid-name": "<p>名字不要带空格!重来重来 o(一︿一+)o</p>", "menu-4-success": "<p>修改成功!</p>", "menu-5": "<p>请选择一个服务器:</p>\n{server_list}\n<p>q. 退出</p>", "menu-5-success": "<p>设置成功!</p>", "menu-5-error-duplicate": "<p>选择的服务器与旧的默认服务器相同!</p>" } }, server: { description: "管理服务器", messages: { "no-channel": "<p>请在群聊中使用本功能哦~</p>", "invalid-select": "<p>没有该选项!</p>", "no-auth": "<p>只有群管理员能使用本功能哦~</p>", quit: "<p>已退出~</p>", "admin-menu-select": "<p>[群聊服务器管理]</p>\n{server_list}\n<p>0. 添加新服务器</p>\n<p>q. 退出菜单</p>\n<p>请输入序号:</p>", "menu-select": "<p>[服务器管理]</p>\n{server_list}\n<p>0. 添加新服务器</p>\n<p>q. 退出菜单</p>\n<p>请输入序号:</p>", menu: "<p>[{name}]</p>\n<p>类型:{type}</p>\n<p>1. 设为默认服务器</p>\n<p>2. 查看或修改 url</p>\n<p>3. 删除该服务器</p>\n<p>4. 重命名该服务器</p>\n<p>0. 返回服务器选择</p>\n<p>q. 退出菜单</p>\n<p>请输入序号:</p>", "menu-1-success": "<p>设置成功!</p>", "menu-1-error-duplicate": "<p>选择的服务器与旧的默认服务器相同!</p>", "menu-2-prompt": "<p>该服务器的 url:{baseUrl}</p>\n<p>请输入新的服务器 url:</p>\n<p>0. 返回</p>\n<p>q. 退出</p>", "menu-2-success": "<p>修改成功!</p>", "menu-2-invalid-url": "<p>URL 格式不正确!请输入有效的 url 地址。</p>", "menu-2-too-many-attempts": "<p>尝试次数过多,操作已取消。</p>", "menu-3-success": "<p>已删除!</p>", "menu-4-prompt": "<p>输入一个新名字,不要带空格哦:</p>\n<p>0. 返回</p>\n<p>q. 退出</p>", "menu-4-error-invalid-name": "<p>名字不要带空格!重来重来 o(一︿一+)o</p>", "menu-4-success": "<p>修改成功!</p>", "add-type": "<p>请选择服务器的类型:</p>\n{server_type_list}\n<p>q. 退出</p>", "add-url": "<p>请输入服务器的 url:</p>\n<p>q. 退出</p>", "add-invalid-url": "<p>URL 格式不正确!请输入有效的 url 地址。</p>", "add-too-many-attempts": "<p>尝试次数过多,操作已取消。</p>", "add-name": "<p>收到!为服务器取一个名字吧,不要带空格哦:</p>\n<p>q. 退出</p>", "add-invalid-name": "<p>名字不要带空格!重来重来 o(一︿一+)o</p>", "add-success": "<p>添加成功啦,服务器信息如下:</p>\n<p>[{serverName}]</p>\n<p>类型:{serverType}</p>" } } } };
|
|
2957
2262
|
|
|
2958
2263
|
// src/core/index.ts
|
|
2959
|
-
var
|
|
2960
|
-
var
|
|
2961
|
-
var
|
|
2962
|
-
function
|
|
2264
|
+
var name5 = "Noah-Core";
|
|
2265
|
+
var inject = ["database", "globalConfig"];
|
|
2266
|
+
var logger2 = new import_koishi6.Logger("Noah-Core");
|
|
2267
|
+
function apply5(ctx, config) {
|
|
2963
2268
|
;
|
|
2964
2269
|
[
|
|
2965
|
-
["en-US",
|
|
2966
|
-
["zh-CN",
|
|
2270
|
+
["en-US", en_US_default2],
|
|
2271
|
+
["zh-CN", zh_CN_default2]
|
|
2967
2272
|
].forEach(([lang, file]) => ctx.i18n.define(lang, file));
|
|
2968
|
-
ctx.plugin(
|
|
2273
|
+
ctx.plugin(database_exports, config.core);
|
|
2969
2274
|
ctx.plugin(command_exports, config.core);
|
|
2970
2275
|
ctx.plugin(event_exports, config.core);
|
|
2971
2276
|
}
|
|
2972
|
-
__name(
|
|
2277
|
+
__name(apply5, "apply");
|
|
2973
2278
|
|
|
2974
2279
|
// src/drawer/index.ts
|
|
2975
2280
|
var drawer_exports = {};
|
|
2976
2281
|
__export(drawer_exports, {
|
|
2977
2282
|
DrawerManager: () => DrawerManager,
|
|
2978
|
-
apply: () =>
|
|
2979
|
-
name: () =>
|
|
2283
|
+
apply: () => apply6,
|
|
2284
|
+
name: () => name6
|
|
2980
2285
|
});
|
|
2981
|
-
var
|
|
2286
|
+
var import_koishi7 = require("koishi");
|
|
2982
2287
|
|
|
2983
2288
|
// src/drawer/BaseDrawer.ts
|
|
2984
2289
|
var import_sharp = __toESM(require("sharp"), 1);
|
|
@@ -2986,6 +2291,7 @@ var BaseDrawer = class {
|
|
|
2986
2291
|
constructor(ctx) {
|
|
2987
2292
|
this.ctx = ctx;
|
|
2988
2293
|
}
|
|
2294
|
+
ctx;
|
|
2989
2295
|
static {
|
|
2990
2296
|
__name(this, "BaseDrawer");
|
|
2991
2297
|
}
|
|
@@ -3025,6 +2331,7 @@ var CoreDrawer = class extends BaseDrawer {
|
|
|
3025
2331
|
super(ctx);
|
|
3026
2332
|
this.ctx = ctx;
|
|
3027
2333
|
}
|
|
2334
|
+
ctx;
|
|
3028
2335
|
static {
|
|
3029
2336
|
__name(this, "CoreDrawer");
|
|
3030
2337
|
}
|
|
@@ -3060,6 +2367,7 @@ var IIDXDrawer = class extends BaseDrawer {
|
|
|
3060
2367
|
super(ctx);
|
|
3061
2368
|
this.ctx = ctx;
|
|
3062
2369
|
}
|
|
2370
|
+
ctx;
|
|
3063
2371
|
static {
|
|
3064
2372
|
__name(this, "IIDXDrawer");
|
|
3065
2373
|
}
|
|
@@ -3125,6 +2433,50 @@ var IIDXDrawer = class extends BaseDrawer {
|
|
|
3125
2433
|
var fs2 = __toESM(require("fs"), 1);
|
|
3126
2434
|
var import_bwip_js = __toESM(require("bwip-js"), 1);
|
|
3127
2435
|
|
|
2436
|
+
// src/games/sdvx/constants.ts
|
|
2437
|
+
var ALL_GRADES = ["S", "AAA+", "AAA", "AA+", "AA", "A+", "A", "B", "C", "D"];
|
|
2438
|
+
var ALL_CLEAR_TYPES = [
|
|
2439
|
+
"S-PUC",
|
|
2440
|
+
"PUC",
|
|
2441
|
+
"UC",
|
|
2442
|
+
"MC",
|
|
2443
|
+
"HC",
|
|
2444
|
+
"NC",
|
|
2445
|
+
"PLAYED",
|
|
2446
|
+
"NO PLAY"
|
|
2447
|
+
];
|
|
2448
|
+
var SCORE_GRADE_MAP = [
|
|
2449
|
+
{ min: 99e5, max: 1e7, grade: "S" },
|
|
2450
|
+
{ min: 98e5, max: 9899999, grade: "AAA+" },
|
|
2451
|
+
{ min: 97e5, max: 9799999, grade: "AAA" },
|
|
2452
|
+
{ min: 95e5, max: 9699999, grade: "AA+" },
|
|
2453
|
+
{ min: 93e5, max: 9499999, grade: "AA" },
|
|
2454
|
+
{ min: 9e6, max: 9299999, grade: "A+" },
|
|
2455
|
+
{ min: 87e5, max: 8999999, grade: "A" },
|
|
2456
|
+
{ min: 75e5, max: 8699999, grade: "B" },
|
|
2457
|
+
{ min: 65e5, max: 7499999, grade: "C" },
|
|
2458
|
+
{ min: 0, max: 6499999, grade: "D" }
|
|
2459
|
+
];
|
|
2460
|
+
var CLEAR_TYPE_ABBR_MAP = {
|
|
2461
|
+
spuc: "S-PUC",
|
|
2462
|
+
puc: "PUC",
|
|
2463
|
+
uc: "UC",
|
|
2464
|
+
mc: "MC",
|
|
2465
|
+
hc: "HC",
|
|
2466
|
+
nc: "NC",
|
|
2467
|
+
played: "PLAYED",
|
|
2468
|
+
noplay: "NO PLAY"
|
|
2469
|
+
};
|
|
2470
|
+
function getGradeByScore(score) {
|
|
2471
|
+
for (const mapping of SCORE_GRADE_MAP) {
|
|
2472
|
+
if (score >= mapping.min && score <= mapping.max) {
|
|
2473
|
+
return mapping.grade;
|
|
2474
|
+
}
|
|
2475
|
+
}
|
|
2476
|
+
return "D";
|
|
2477
|
+
}
|
|
2478
|
+
__name(getGradeByScore, "getGradeByScore");
|
|
2479
|
+
|
|
3128
2480
|
// src/games/sdvx/utils/vf.ts
|
|
3129
2481
|
var FACTOR_SCALE = 100;
|
|
3130
2482
|
var GRADE_FACTOR_SCALES = {
|
|
@@ -3180,18 +2532,6 @@ __name(calculateVolforceInt, "calculateVolforceInt");
|
|
|
3180
2532
|
|
|
3181
2533
|
// src/games/sdvx/utils/calculator.ts
|
|
3182
2534
|
var cachedLookupTable = null;
|
|
3183
|
-
var SCORE_GRADE_MAP = [
|
|
3184
|
-
{ min: 99e5, max: 1e7, grade: "S" },
|
|
3185
|
-
{ min: 98e5, max: 9899999, grade: "AAA+" },
|
|
3186
|
-
{ min: 97e5, max: 9799999, grade: "AAA" },
|
|
3187
|
-
{ min: 95e5, max: 9699999, grade: "AA+" },
|
|
3188
|
-
{ min: 93e5, max: 9499999, grade: "AA" },
|
|
3189
|
-
{ min: 9e6, max: 9299999, grade: "A+" },
|
|
3190
|
-
{ min: 87e5, max: 8999999, grade: "A" },
|
|
3191
|
-
{ min: 75e5, max: 8699999, grade: "B" },
|
|
3192
|
-
{ min: 65e5, max: 7499999, grade: "C" },
|
|
3193
|
-
{ min: 0, max: 6499999, grade: "D" }
|
|
3194
|
-
];
|
|
3195
2535
|
var VF_INT_MULTIPLIER = 20;
|
|
3196
2536
|
var VF_INPUT_PRECISION = 6;
|
|
3197
2537
|
var LEVEL_SCALE = 10;
|
|
@@ -3245,16 +2585,7 @@ function getMaxScoreForVfInt(multiplier, vfInt) {
|
|
|
3245
2585
|
const numerator = BigInt(vfInt + 1) * VOLFORCE_BASE_DENOMINATOR - 1n;
|
|
3246
2586
|
return Number(numerator / multiplier);
|
|
3247
2587
|
}
|
|
3248
|
-
__name(getMaxScoreForVfInt, "getMaxScoreForVfInt");
|
|
3249
|
-
function getGradeByScore(score) {
|
|
3250
|
-
for (const mapping of SCORE_GRADE_MAP) {
|
|
3251
|
-
if (score >= mapping.min && score <= mapping.max) {
|
|
3252
|
-
return mapping.grade;
|
|
3253
|
-
}
|
|
3254
|
-
}
|
|
3255
|
-
return "D";
|
|
3256
|
-
}
|
|
3257
|
-
__name(getGradeByScore, "getGradeByScore");
|
|
2588
|
+
__name(getMaxScoreForVfInt, "getMaxScoreForVfInt");
|
|
3258
2589
|
function getPossibleClearTypes(score) {
|
|
3259
2590
|
if (score === 1e7) {
|
|
3260
2591
|
return ["PUC", "S-PUC"];
|
|
@@ -3321,23 +2652,12 @@ function getNextVFIncrease(level, currentScore, clearType) {
|
|
|
3321
2652
|
}
|
|
3322
2653
|
__name(getNextVFIncrease, "getNextVFIncrease");
|
|
3323
2654
|
function getGradeRange(grade) {
|
|
3324
|
-
|
|
3325
|
-
if (!grade) return allGrades;
|
|
2655
|
+
if (!grade) return ALL_GRADES;
|
|
3326
2656
|
return Array.isArray(grade) ? grade : [grade];
|
|
3327
2657
|
}
|
|
3328
2658
|
__name(getGradeRange, "getGradeRange");
|
|
3329
2659
|
function getClearTypeRange(clearType) {
|
|
3330
|
-
|
|
3331
|
-
"S-PUC",
|
|
3332
|
-
"PUC",
|
|
3333
|
-
"UC",
|
|
3334
|
-
"MC",
|
|
3335
|
-
"HC",
|
|
3336
|
-
"NC",
|
|
3337
|
-
"PLAYED",
|
|
3338
|
-
"NO PLAY"
|
|
3339
|
-
];
|
|
3340
|
-
if (!clearType) return allClearTypes;
|
|
2660
|
+
if (!clearType) return ALL_CLEAR_TYPES;
|
|
3341
2661
|
return Array.isArray(clearType) ? clearType : [clearType];
|
|
3342
2662
|
}
|
|
3343
2663
|
__name(getClearTypeRange, "getClearTypeRange");
|
|
@@ -3349,27 +2669,16 @@ function getLevelRange(level) {
|
|
|
3349
2669
|
__name(getLevelRange, "getLevelRange");
|
|
3350
2670
|
function generateVFLookupTable() {
|
|
3351
2671
|
const lookupTable = /* @__PURE__ */ new Map();
|
|
3352
|
-
const allGrades = ["S", "AAA+", "AAA", "AA+", "AA", "A+", "A", "B", "C", "D"];
|
|
3353
|
-
const allClearTypes = [
|
|
3354
|
-
"S-PUC",
|
|
3355
|
-
"PUC",
|
|
3356
|
-
"UC",
|
|
3357
|
-
"MC",
|
|
3358
|
-
"HC",
|
|
3359
|
-
"NC",
|
|
3360
|
-
"PLAYED",
|
|
3361
|
-
"NO PLAY"
|
|
3362
|
-
];
|
|
3363
2672
|
for (const level of getAllValidLevels()) {
|
|
3364
2673
|
const levelScaled = scaleLevel(level);
|
|
3365
|
-
for (const grade of
|
|
2674
|
+
for (const grade of ALL_GRADES) {
|
|
3366
2675
|
const gradeMapping = SCORE_GRADE_MAP.find((m) => m.grade === grade);
|
|
3367
2676
|
if (!gradeMapping) {
|
|
3368
2677
|
continue;
|
|
3369
2678
|
}
|
|
3370
2679
|
const gradeMin = gradeMapping.min;
|
|
3371
2680
|
const gradeMax = gradeMapping.max;
|
|
3372
|
-
for (const clearType of
|
|
2681
|
+
for (const clearType of ALL_CLEAR_TYPES) {
|
|
3373
2682
|
const scoresToCheck = /* @__PURE__ */ new Set();
|
|
3374
2683
|
scoresToCheck.add(gradeMin);
|
|
3375
2684
|
scoresToCheck.add(gradeMax);
|
|
@@ -3575,9 +2884,85 @@ var SDVXDrawer = class extends BaseDrawer {
|
|
|
3575
2884
|
super(ctx);
|
|
3576
2885
|
this.ctx = ctx;
|
|
3577
2886
|
}
|
|
2887
|
+
ctx;
|
|
3578
2888
|
static {
|
|
3579
2889
|
__name(this, "SDVXDrawer");
|
|
3580
2890
|
}
|
|
2891
|
+
fontsLoaded = false;
|
|
2892
|
+
recentAssets = null;
|
|
2893
|
+
vfAssets = null;
|
|
2894
|
+
ensureFontsLoaded() {
|
|
2895
|
+
if (this.fontsLoaded) return;
|
|
2896
|
+
const { FontLibrary } = this.ctx.skia;
|
|
2897
|
+
const fonts = [
|
|
2898
|
+
["Noto Sans", "fonts/NotoSans-VariableFont_wdth,wght.ttf"],
|
|
2899
|
+
["Noto Sans JP", "fonts/NotoSansJP-VariableFont_wght.ttf"],
|
|
2900
|
+
["Slant", "fonts/Slant.ttf"],
|
|
2901
|
+
["Fredoka One", "fonts/FredokaOne.ttf"]
|
|
2902
|
+
];
|
|
2903
|
+
for (const [name15, file] of fonts) {
|
|
2904
|
+
const path3 = getAssetPath(this.ctx, file);
|
|
2905
|
+
if (fs2.existsSync(path3)) FontLibrary.use(name15, path3);
|
|
2906
|
+
}
|
|
2907
|
+
this.fontsLoaded = true;
|
|
2908
|
+
}
|
|
2909
|
+
async ensureRecentAssets() {
|
|
2910
|
+
if (this.recentAssets) return this.recentAssets;
|
|
2911
|
+
const { loadImage } = this.ctx.skia;
|
|
2912
|
+
const gradeList = ["C", "D", "S", "B", "AAA", "AAA+", "AA+", "A", "A+", "AA"];
|
|
2913
|
+
const playTypeList = ["UC", "PLAYED", "SPUC", "PUC", "NOPLAY", "NC", "MC", "HC"];
|
|
2914
|
+
const [gradeImages, playTypeImages] = await Promise.all([
|
|
2915
|
+
this.loadImageMap(loadImage, gradeList, (g) => `sdvx/recent/grade/Type=${g}.png`),
|
|
2916
|
+
this.loadImageMap(
|
|
2917
|
+
loadImage,
|
|
2918
|
+
playTypeList,
|
|
2919
|
+
(t) => `sdvx/recent/play_type/Type=${t}.png`
|
|
2920
|
+
)
|
|
2921
|
+
]);
|
|
2922
|
+
this.recentAssets = { gradeImages, playTypeImages };
|
|
2923
|
+
return this.recentAssets;
|
|
2924
|
+
}
|
|
2925
|
+
async ensureVFAssets() {
|
|
2926
|
+
if (this.vfAssets) return this.vfAssets;
|
|
2927
|
+
const { loadImage } = this.ctx.skia;
|
|
2928
|
+
const grades = ["D", "C", "B", "A", "A+", "AA", "AA+", "AAA", "AAA+", "S"];
|
|
2929
|
+
const circles = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
|
|
2930
|
+
const diffs = ["NOV", "ADV", "EXH", "INF", "MXM", "GRV", "HVN", "VVD", "XCD", "ULT"];
|
|
2931
|
+
const clears = ["PUC", "SPUC", "UC", "MC", "HC", "NC", "PLAYED", "NOPLAY"];
|
|
2932
|
+
const [gradeImages, circleImages, difficultyBadges, clearTypeBadges] = await Promise.all([
|
|
2933
|
+
this.loadImageMap(loadImage, grades, (g) => `sdvx/vf/grade/${g}.png`),
|
|
2934
|
+
this.loadImageMap(loadImage, circles, (i) => `sdvx/vf/circle/Class=${i}.png`),
|
|
2935
|
+
this.loadImageMap(loadImage, diffs, (d) => `sdvx/vf/difficulty/${d}.png`),
|
|
2936
|
+
this.loadImageMap(loadImage, clears, (c) => `sdvx/vf/clear_type/${c}.png`)
|
|
2937
|
+
]);
|
|
2938
|
+
this.vfAssets = { gradeImages, circleImages, difficultyBadges, clearTypeBadges };
|
|
2939
|
+
return this.vfAssets;
|
|
2940
|
+
}
|
|
2941
|
+
async loadImageMap(loadImage, keys, pathFn) {
|
|
2942
|
+
const entries = await Promise.all(
|
|
2943
|
+
keys.map(async (key) => {
|
|
2944
|
+
try {
|
|
2945
|
+
return [
|
|
2946
|
+
String(key),
|
|
2947
|
+
await loadImage(getAssetPath(this.ctx, pathFn(key)))
|
|
2948
|
+
];
|
|
2949
|
+
} catch {
|
|
2950
|
+
return [String(key), null];
|
|
2951
|
+
}
|
|
2952
|
+
})
|
|
2953
|
+
);
|
|
2954
|
+
return Object.fromEntries(entries.filter(([, v]) => v !== null));
|
|
2955
|
+
}
|
|
2956
|
+
fitText(ctx, text, maxWidth) {
|
|
2957
|
+
if (ctx.measureText(text).width <= maxWidth) return text;
|
|
2958
|
+
let lo = 0, hi = text.length;
|
|
2959
|
+
while (lo < hi) {
|
|
2960
|
+
const mid = lo + hi + 1 >> 1;
|
|
2961
|
+
if (ctx.measureText(text.substring(0, mid)).width <= maxWidth) lo = mid;
|
|
2962
|
+
else hi = mid - 1;
|
|
2963
|
+
}
|
|
2964
|
+
return text.substring(0, lo);
|
|
2965
|
+
}
|
|
3581
2966
|
/**
|
|
3582
2967
|
* Format difficulty level text.
|
|
3583
2968
|
* If it's an integer like 17 / 20, keep trailing ".0" (e.g. "17.0").
|
|
@@ -3593,46 +2978,16 @@ var SDVXDrawer = class extends BaseDrawer {
|
|
|
3593
2978
|
* @returns 包含图像数据的Buffer
|
|
3594
2979
|
*/
|
|
3595
2980
|
async generateRecentImage(options, compression) {
|
|
3596
|
-
const { Canvas, loadImage
|
|
2981
|
+
const { Canvas, loadImage } = this.ctx.skia;
|
|
3597
2982
|
if (!compression) {
|
|
3598
2983
|
compression = { lossless: true };
|
|
3599
2984
|
}
|
|
3600
|
-
|
|
3601
|
-
const
|
|
3602
|
-
|
|
3603
|
-
|
|
3604
|
-
this.ctx
|
|
3605
|
-
|
|
3606
|
-
this.ctx.logger("SDVX-Drawer").warn(`Noto Sans font file not found at: ${notoSansPath}`);
|
|
3607
|
-
}
|
|
3608
|
-
if (fs2.existsSync(notoSansJPPath)) {
|
|
3609
|
-
FontLibrary.use("Noto Sans JP", notoSansJPPath);
|
|
3610
|
-
this.ctx.logger("SDVX-Drawer").debug("Loaded Noto Sans JP font successfully");
|
|
3611
|
-
} else {
|
|
3612
|
-
this.ctx.logger("SDVX-Drawer").warn(`Noto Sans JP font file not found at: ${notoSansJPPath}`);
|
|
3613
|
-
}
|
|
3614
|
-
const bgImage = await loadImage(getAssetPath(this.ctx, "sdvx/recent/main_bg.png"));
|
|
3615
|
-
const ticketBgImage = await loadImage(getAssetPath(this.ctx, "sdvx/recent/ticket_bg.png"));
|
|
3616
|
-
const gradeImages = {};
|
|
3617
|
-
const gradeList = ["C", "D", "S", "B", "AAA", "AAA+", "AA+", "A", "A+", "AA"];
|
|
3618
|
-
for (const grade of gradeList) {
|
|
3619
|
-
try {
|
|
3620
|
-
const gradePath = getAssetPath(this.ctx, `sdvx/recent/grade/Type=${grade}.png`);
|
|
3621
|
-
gradeImages[grade] = await loadImage(gradePath);
|
|
3622
|
-
} catch (error) {
|
|
3623
|
-
this.ctx.logger("SDVX-Drawer").warn(`Failed to load grade image for ${grade}: ${error.message}`);
|
|
3624
|
-
}
|
|
3625
|
-
}
|
|
3626
|
-
const playTypeImages = {};
|
|
3627
|
-
const playTypeList = ["UC", "PLAYED", "SPUC", "PUC", "NOPLAY", "NC", "MC", "HC"];
|
|
3628
|
-
for (const type of playTypeList) {
|
|
3629
|
-
try {
|
|
3630
|
-
const typePath = getAssetPath(this.ctx, `sdvx/recent/play_type/Type=${type}.png`);
|
|
3631
|
-
playTypeImages[type] = await loadImage(typePath);
|
|
3632
|
-
} catch (error) {
|
|
3633
|
-
this.ctx.logger("SDVX-Drawer").warn(`Failed to load play_type image for ${type}: ${error.message}`);
|
|
3634
|
-
}
|
|
3635
|
-
}
|
|
2985
|
+
this.ensureFontsLoaded();
|
|
2986
|
+
const { gradeImages, playTypeImages } = await this.ensureRecentAssets();
|
|
2987
|
+
const [bgImage, ticketBgImage] = await Promise.all([
|
|
2988
|
+
loadImage(getAssetPath(this.ctx, "sdvx/recent/main_bg.png")),
|
|
2989
|
+
loadImage(getAssetPath(this.ctx, "sdvx/recent/ticket_bg.png"))
|
|
2990
|
+
]);
|
|
3636
2991
|
let jacketImage;
|
|
3637
2992
|
try {
|
|
3638
2993
|
if (options.score.difficulty_data?.cover_url) {
|
|
@@ -3656,20 +3011,20 @@ var SDVXDrawer = class extends BaseDrawer {
|
|
|
3656
3011
|
ctx.save();
|
|
3657
3012
|
ctx.translate(556, 17);
|
|
3658
3013
|
ctx.beginPath();
|
|
3659
|
-
const r = 16, w = 100,
|
|
3014
|
+
const r = 16, w = 100, h10 = 200, x0 = 0, y0 = 0;
|
|
3660
3015
|
ctx.moveTo(x0 + r, y0);
|
|
3661
3016
|
ctx.lineTo(x0 + w - r, y0);
|
|
3662
3017
|
ctx.arcTo(x0 + w, y0, x0 + w, y0 + r, r);
|
|
3663
|
-
ctx.lineTo(x0 + w, y0 +
|
|
3664
|
-
ctx.arcTo(x0 + w, y0 +
|
|
3665
|
-
ctx.lineTo(x0 + r, y0 +
|
|
3666
|
-
ctx.arcTo(x0, y0 +
|
|
3018
|
+
ctx.lineTo(x0 + w, y0 + h10 - r);
|
|
3019
|
+
ctx.arcTo(x0 + w, y0 + h10, x0 + w - r, y0 + h10, r);
|
|
3020
|
+
ctx.lineTo(x0 + r, y0 + h10);
|
|
3021
|
+
ctx.arcTo(x0, y0 + h10, x0, y0 + h10 - r, r);
|
|
3667
3022
|
ctx.lineTo(x0, y0 + r);
|
|
3668
3023
|
ctx.arcTo(x0, y0, x0 + r, y0, r);
|
|
3669
3024
|
ctx.closePath();
|
|
3670
3025
|
const circleRadius = 14;
|
|
3671
3026
|
const circleX = x0 + w;
|
|
3672
|
-
const circleY = y0 +
|
|
3027
|
+
const circleY = y0 + h10 / 2;
|
|
3673
3028
|
ctx.moveTo(circleX + circleRadius, circleY);
|
|
3674
3029
|
ctx.arc(circleX, circleY, circleRadius, 0, Math.PI * 2, true);
|
|
3675
3030
|
ctx.clip();
|
|
@@ -3694,42 +3049,23 @@ var SDVXDrawer = class extends BaseDrawer {
|
|
|
3694
3049
|
ctx.textAlign = "left";
|
|
3695
3050
|
ctx.fillStyle = "#0230A5";
|
|
3696
3051
|
const maxWidth = 360;
|
|
3052
|
+
const gap = 8;
|
|
3053
|
+
let mainTitleWidth = ctx.measureText(mainTitle).width;
|
|
3697
3054
|
let displayMainTitle = mainTitle;
|
|
3698
3055
|
let displaySubTitle = subTitle;
|
|
3699
|
-
|
|
3700
|
-
|
|
3701
|
-
|
|
3702
|
-
if (mainTitleWidth + (displaySubTitle ? gap + subTitleWidth : 0) > maxWidth) {
|
|
3703
|
-
if (displaySubTitle) {
|
|
3704
|
-
let fitLength = 0;
|
|
3705
|
-
for (let i = 1; i <= displaySubTitle.length; i++) {
|
|
3706
|
-
if (ctx.measureText(displaySubTitle.substring(0, i)).width + mainTitleWidth + gap > maxWidth) {
|
|
3707
|
-
fitLength = i - 1;
|
|
3708
|
-
break;
|
|
3709
|
-
}
|
|
3710
|
-
}
|
|
3711
|
-
if (fitLength > 0) {
|
|
3712
|
-
displaySubTitle = displaySubTitle.substring(0, fitLength);
|
|
3713
|
-
} else {
|
|
3714
|
-
displaySubTitle = "";
|
|
3715
|
-
}
|
|
3716
|
-
subTitleWidth = ctx.measureText(displaySubTitle).width;
|
|
3056
|
+
if (mainTitleWidth + (subTitle ? gap + ctx.measureText(subTitle).width : 0) > maxWidth) {
|
|
3057
|
+
if (subTitle) {
|
|
3058
|
+
displaySubTitle = this.fitText(ctx, subTitle, maxWidth - mainTitleWidth - gap);
|
|
3717
3059
|
}
|
|
3060
|
+
const subTitleWidth = displaySubTitle ? ctx.measureText(displaySubTitle).width : 0;
|
|
3718
3061
|
if (mainTitleWidth + (displaySubTitle ? gap + subTitleWidth : 0) > maxWidth) {
|
|
3719
|
-
|
|
3720
|
-
|
|
3721
|
-
|
|
3722
|
-
|
|
3723
|
-
|
|
3724
|
-
}
|
|
3725
|
-
}
|
|
3726
|
-
if (fitLength > 0) {
|
|
3727
|
-
displayMainTitle = displayMainTitle.substring(0, fitLength);
|
|
3728
|
-
} else {
|
|
3729
|
-
displayMainTitle = "";
|
|
3730
|
-
}
|
|
3731
|
-
mainTitleWidth = ctx.measureText(displayMainTitle).width;
|
|
3062
|
+
displayMainTitle = this.fitText(
|
|
3063
|
+
ctx,
|
|
3064
|
+
mainTitle,
|
|
3065
|
+
maxWidth - (displaySubTitle ? gap + subTitleWidth : 0)
|
|
3066
|
+
);
|
|
3732
3067
|
}
|
|
3068
|
+
mainTitleWidth = ctx.measureText(displayMainTitle).width;
|
|
3733
3069
|
}
|
|
3734
3070
|
ctx.fillStyle = "#0230A5";
|
|
3735
3071
|
ctx.globalAlpha = 1;
|
|
@@ -3836,42 +3172,11 @@ var SDVXDrawer = class extends BaseDrawer {
|
|
|
3836
3172
|
* @returns 包含图像数据的Buffer
|
|
3837
3173
|
*/
|
|
3838
3174
|
async generateVFImage(options, compression) {
|
|
3839
|
-
const { Canvas, loadImage
|
|
3175
|
+
const { Canvas, loadImage } = this.ctx.skia;
|
|
3840
3176
|
if (!compression) {
|
|
3841
3177
|
compression = { lossless: true };
|
|
3842
3178
|
}
|
|
3843
|
-
|
|
3844
|
-
const notoSansPath = getAssetPath(this.ctx, "fonts/NotoSans-VariableFont_wdth,wght.ttf");
|
|
3845
|
-
const slantPath = getAssetPath(this.ctx, "fonts/Slant.ttf");
|
|
3846
|
-
const fredokaPath = getAssetPath(this.ctx, "fonts/FredokaOne.ttf");
|
|
3847
|
-
const notoSansJPPath = getAssetPath(this.ctx, "fonts/NotoSansJP-VariableFont_wght.ttf");
|
|
3848
|
-
if (fs2.existsSync(notoSansPath)) {
|
|
3849
|
-
FontLibrary.use("Noto Sans", notoSansPath);
|
|
3850
|
-
this.ctx.logger("SDVX-Drawer").debug("Loaded Noto Sans font successfully");
|
|
3851
|
-
} else {
|
|
3852
|
-
this.ctx.logger("SDVX-Drawer").warn(`Noto Sans font file not found at: ${notoSansPath}`);
|
|
3853
|
-
}
|
|
3854
|
-
if (fs2.existsSync(slantPath)) {
|
|
3855
|
-
FontLibrary.use("Slant", slantPath);
|
|
3856
|
-
this.ctx.logger("SDVX-Drawer").debug("Loaded Slant font successfully");
|
|
3857
|
-
} else {
|
|
3858
|
-
this.ctx.logger("SDVX-Drawer").warn(`Slant font file not found at: ${slantPath}`);
|
|
3859
|
-
}
|
|
3860
|
-
if (fs2.existsSync(fredokaPath)) {
|
|
3861
|
-
FontLibrary.use("Fredoka One", fredokaPath);
|
|
3862
|
-
this.ctx.logger("SDVX-Drawer").debug("Loaded Fredoka One font successfully");
|
|
3863
|
-
} else {
|
|
3864
|
-
this.ctx.logger("SDVX-Drawer").warn(`Fredoka One font file not found at: ${fredokaPath}`);
|
|
3865
|
-
}
|
|
3866
|
-
if (fs2.existsSync(notoSansJPPath)) {
|
|
3867
|
-
FontLibrary.use("Noto Sans JP", notoSansJPPath);
|
|
3868
|
-
this.ctx.logger("SDVX-Drawer").debug("Loaded Noto Sans JP font successfully");
|
|
3869
|
-
} else {
|
|
3870
|
-
this.ctx.logger("SDVX-Drawer").warn(`Noto Sans JP font file not found at: ${notoSansJPPath}`);
|
|
3871
|
-
}
|
|
3872
|
-
} catch (error) {
|
|
3873
|
-
this.ctx.logger("SDVX-Drawer").warn(`Error loading fonts: ${error.message}`);
|
|
3874
|
-
}
|
|
3179
|
+
this.ensureFontsLoaded();
|
|
3875
3180
|
const bgImage = await loadImage(getAssetPath(this.ctx, "sdvx/vf/main_bg.png"));
|
|
3876
3181
|
const canvas = new Canvas(bgImage.width, bgImage.height);
|
|
3877
3182
|
const ctx = canvas.getContext("2d");
|
|
@@ -3909,85 +3214,29 @@ var SDVXDrawer = class extends BaseDrawer {
|
|
|
3909
3214
|
const cardWidth = cardBgImage.width;
|
|
3910
3215
|
const cardHeight = cardBgImage.height;
|
|
3911
3216
|
const effectiveCardWidth = cardWidth + cardHorizontalSpacing;
|
|
3912
|
-
const gradeImages =
|
|
3913
|
-
const circleImages = {};
|
|
3217
|
+
const { gradeImages, circleImages, difficultyBadges, clearTypeBadges } = await this.ensureVFAssets();
|
|
3914
3218
|
const jacketImages = {};
|
|
3915
|
-
const
|
|
3916
|
-
|
|
3917
|
-
|
|
3918
|
-
|
|
3919
|
-
|
|
3920
|
-
|
|
3921
|
-
|
|
3922
|
-
|
|
3923
|
-
|
|
3924
|
-
|
|
3925
|
-
|
|
3926
|
-
|
|
3927
|
-
for (let i = 1; i <= 10; i++) {
|
|
3928
|
-
try {
|
|
3929
|
-
const circlePath = getAssetPath(this.ctx, `sdvx/vf/circle/Class=${i}.png`);
|
|
3930
|
-
circleImages[i] = await loadImage(circlePath);
|
|
3931
|
-
} catch (error) {
|
|
3932
|
-
this.ctx.logger("SDVX-Drawer").warn(`Failed to load circle image for class ${i}: ${error.message}`);
|
|
3933
|
-
}
|
|
3934
|
-
}
|
|
3935
|
-
const difficultyBadgeKeys = [
|
|
3936
|
-
"NOV",
|
|
3937
|
-
"ADV",
|
|
3938
|
-
"EXH",
|
|
3939
|
-
"INF",
|
|
3940
|
-
"MXM",
|
|
3941
|
-
"GRV",
|
|
3942
|
-
"HVN",
|
|
3943
|
-
"VVD",
|
|
3944
|
-
"XCD",
|
|
3945
|
-
"ULT"
|
|
3946
|
-
];
|
|
3947
|
-
for (const difficulty of difficultyBadgeKeys) {
|
|
3948
|
-
try {
|
|
3949
|
-
const difficultyBadgePath = getAssetPath(
|
|
3950
|
-
this.ctx,
|
|
3951
|
-
`sdvx/vf/difficulty/${difficulty}.png`
|
|
3952
|
-
);
|
|
3953
|
-
difficultyBadges[difficulty] = await loadImage(difficultyBadgePath);
|
|
3954
|
-
} catch (error) {
|
|
3955
|
-
this.ctx.logger("SDVX-Drawer").warn(
|
|
3956
|
-
`Failed to load difficulty badge image for ${difficulty}: ${error.message}`
|
|
3957
|
-
);
|
|
3958
|
-
}
|
|
3959
|
-
}
|
|
3960
|
-
const clearTypeBadgeKeys = ["PUC", "SPUC", "UC", "MC", "HC", "NC", "PLAYED", "NOPLAY"];
|
|
3961
|
-
for (const clearType of clearTypeBadgeKeys) {
|
|
3962
|
-
try {
|
|
3963
|
-
const clearTypeBadgePath = getAssetPath(
|
|
3964
|
-
this.ctx,
|
|
3965
|
-
`sdvx/vf/clear_type/${clearType}.png`
|
|
3966
|
-
);
|
|
3967
|
-
clearTypeBadges[clearType] = await loadImage(clearTypeBadgePath);
|
|
3968
|
-
} catch (error) {
|
|
3969
|
-
this.ctx.logger("SDVX-Drawer").warn(
|
|
3970
|
-
`Failed to load clear type badge image for ${clearType}: ${error.message}`
|
|
3971
|
-
);
|
|
3972
|
-
}
|
|
3973
|
-
}
|
|
3974
|
-
for (const score of scores) {
|
|
3219
|
+
const uniqueUrls = [
|
|
3220
|
+
...new Set(
|
|
3221
|
+
scores.filter((s) => s.difficulty_data?.cover_url).map(
|
|
3222
|
+
(s) => `${config.sdvx_data_url}${s.difficulty_data.cover_url}`.replace(
|
|
3223
|
+
".webp",
|
|
3224
|
+
".png"
|
|
3225
|
+
)
|
|
3226
|
+
)
|
|
3227
|
+
)
|
|
3228
|
+
];
|
|
3229
|
+
const jacketEntries = await Promise.all(
|
|
3230
|
+
uniqueUrls.map(async (url) => {
|
|
3975
3231
|
try {
|
|
3976
|
-
|
|
3977
|
-
|
|
3978
|
-
|
|
3979
|
-
".png"
|
|
3980
|
-
);
|
|
3981
|
-
if (!jacketImages[coverUrl]) {
|
|
3982
|
-
jacketImages[coverUrl] = await loadImage(coverUrl);
|
|
3983
|
-
}
|
|
3984
|
-
}
|
|
3985
|
-
} catch (error) {
|
|
3986
|
-
this.ctx.logger("SDVX-Drawer").warn(`Failed to load jacket image: ${error.message}`);
|
|
3232
|
+
return [url, await loadImage(url)];
|
|
3233
|
+
} catch {
|
|
3234
|
+
return [url, null];
|
|
3987
3235
|
}
|
|
3988
|
-
}
|
|
3989
|
-
|
|
3990
|
-
|
|
3236
|
+
})
|
|
3237
|
+
);
|
|
3238
|
+
for (const [url, img] of jacketEntries) {
|
|
3239
|
+
if (img) jacketImages[url] = img;
|
|
3991
3240
|
}
|
|
3992
3241
|
for (let i = 0; i < scores.length; i++) {
|
|
3993
3242
|
const score = scores[i];
|
|
@@ -3996,17 +3245,7 @@ var SDVXDrawer = class extends BaseDrawer {
|
|
|
3996
3245
|
const x = startX + col * effectiveCardWidth;
|
|
3997
3246
|
const y = startY + row * (cardHeight + cardVerticalSpacing);
|
|
3998
3247
|
const vf2 = score.extra.volforce;
|
|
3999
|
-
|
|
4000
|
-
if (vf2 >= 20) vfClass = 10;
|
|
4001
|
-
else if (vf2 >= 19) vfClass = 9;
|
|
4002
|
-
else if (vf2 >= 18) vfClass = 8;
|
|
4003
|
-
else if (vf2 >= 17) vfClass = 7;
|
|
4004
|
-
else if (vf2 >= 16) vfClass = 6;
|
|
4005
|
-
else if (vf2 >= 15) vfClass = 5;
|
|
4006
|
-
else if (vf2 >= 14) vfClass = 4;
|
|
4007
|
-
else if (vf2 >= 12) vfClass = 3;
|
|
4008
|
-
else if (vf2 >= 10) vfClass = 2;
|
|
4009
|
-
else vfClass = 1;
|
|
3248
|
+
const { class: vfClass } = this.getVFClassColor(vf2);
|
|
4010
3249
|
if (circleImages[vfClass]) {
|
|
4011
3250
|
ctx.drawImage(circleImages[vfClass], x + 628, y - 42);
|
|
4012
3251
|
}
|
|
@@ -4199,48 +3438,19 @@ var SDVXDrawer = class extends BaseDrawer {
|
|
|
4199
3438
|
}
|
|
4200
3439
|
const totalAvailableWidth = 413;
|
|
4201
3440
|
ctx.font = '400 24px "Noto Sans JP"';
|
|
4202
|
-
const mainTitleWidth = ctx.measureText(mainTitle).width;
|
|
4203
3441
|
let displayMainTitle = mainTitle;
|
|
4204
3442
|
if (subTitle) {
|
|
4205
3443
|
const mainTitleMaxWidth = totalAvailableWidth * 0.7;
|
|
4206
|
-
|
|
4207
|
-
let fitLength = 0;
|
|
4208
|
-
for (let i2 = 1; i2 <= mainTitle.length; i2++) {
|
|
4209
|
-
if (ctx.measureText(mainTitle.substring(0, i2)).width > mainTitleMaxWidth) {
|
|
4210
|
-
fitLength = i2 - 1;
|
|
4211
|
-
break;
|
|
4212
|
-
}
|
|
4213
|
-
}
|
|
4214
|
-
displayMainTitle = mainTitle.substring(0, fitLength);
|
|
4215
|
-
}
|
|
3444
|
+
displayMainTitle = this.fitText(ctx, mainTitle, mainTitleMaxWidth);
|
|
4216
3445
|
ctx.fillStyle = "#FFFFFF";
|
|
4217
3446
|
ctx.fillText(displayMainTitle, x + 388, y + 115);
|
|
4218
3447
|
ctx.fillStyle = "rgba(255, 255, 255, 0.7)";
|
|
4219
3448
|
const actualMainTitleWidth = ctx.measureText(displayMainTitle).width;
|
|
4220
3449
|
const remainingWidth = totalAvailableWidth - actualMainTitleWidth - 10;
|
|
4221
|
-
|
|
4222
|
-
if (ctx.measureText(subTitle).width > remainingWidth) {
|
|
4223
|
-
let fitLength = 0;
|
|
4224
|
-
for (let i2 = 1; i2 <= subTitle.length; i2++) {
|
|
4225
|
-
if (ctx.measureText(subTitle.substring(0, i2)).width > remainingWidth) {
|
|
4226
|
-
fitLength = i2 - 1;
|
|
4227
|
-
break;
|
|
4228
|
-
}
|
|
4229
|
-
}
|
|
4230
|
-
displaySubTitle = subTitle.substring(0, fitLength);
|
|
4231
|
-
}
|
|
3450
|
+
const displaySubTitle = this.fitText(ctx, subTitle, remainingWidth);
|
|
4232
3451
|
ctx.fillText(displaySubTitle, x + 388 + actualMainTitleWidth + 10, y + 115);
|
|
4233
3452
|
} else {
|
|
4234
|
-
|
|
4235
|
-
let fitLength = 0;
|
|
4236
|
-
for (let i2 = 1; i2 <= mainTitle.length; i2++) {
|
|
4237
|
-
if (ctx.measureText(mainTitle.substring(0, i2)).width > totalAvailableWidth) {
|
|
4238
|
-
fitLength = i2 - 1;
|
|
4239
|
-
break;
|
|
4240
|
-
}
|
|
4241
|
-
}
|
|
4242
|
-
displayMainTitle = mainTitle.substring(0, fitLength);
|
|
4243
|
-
}
|
|
3453
|
+
displayMainTitle = this.fitText(ctx, mainTitle, totalAvailableWidth);
|
|
4244
3454
|
ctx.fillStyle = "#FFFFFF";
|
|
4245
3455
|
ctx.fillText(displayMainTitle, x + 388, y + 115);
|
|
4246
3456
|
}
|
|
@@ -4365,18 +3575,11 @@ var SDVXDrawer = class extends BaseDrawer {
|
|
|
4365
3575
|
return { class: vfClass, color };
|
|
4366
3576
|
}
|
|
4367
3577
|
async generateVFTableImage(options, compression) {
|
|
4368
|
-
const { Canvas
|
|
3578
|
+
const { Canvas } = this.ctx.skia;
|
|
4369
3579
|
if (!compression) {
|
|
4370
3580
|
compression = { lossless: true };
|
|
4371
3581
|
}
|
|
4372
|
-
|
|
4373
|
-
const notoSansJPPath = getAssetPath(this.ctx, "fonts/NotoSansJP-VariableFont_wght.ttf");
|
|
4374
|
-
if (fs2.existsSync(notoSansPath)) {
|
|
4375
|
-
FontLibrary.use("Noto Sans", notoSansPath);
|
|
4376
|
-
}
|
|
4377
|
-
if (fs2.existsSync(notoSansJPPath)) {
|
|
4378
|
-
FontLibrary.use("Noto Sans JP", notoSansJPPath);
|
|
4379
|
-
}
|
|
3582
|
+
this.ensureFontsLoaded();
|
|
4380
3583
|
const baseCellWidth = 160;
|
|
4381
3584
|
const baseCellHeight = 60;
|
|
4382
3585
|
const headerHeight = 80;
|
|
@@ -4568,6 +3771,7 @@ var DrawerFactory = class {
|
|
|
4568
3771
|
constructor(ctx) {
|
|
4569
3772
|
this.ctx = ctx;
|
|
4570
3773
|
}
|
|
3774
|
+
ctx;
|
|
4571
3775
|
static {
|
|
4572
3776
|
__name(this, "DrawerFactory");
|
|
4573
3777
|
}
|
|
@@ -4601,7 +3805,7 @@ var DrawerFactory = class {
|
|
|
4601
3805
|
};
|
|
4602
3806
|
|
|
4603
3807
|
// src/drawer/index.ts
|
|
4604
|
-
var
|
|
3808
|
+
var name6 = "Noah-Drawer";
|
|
4605
3809
|
var DrawerManager = class _DrawerManager {
|
|
4606
3810
|
static {
|
|
4607
3811
|
__name(this, "DrawerManager");
|
|
@@ -4611,7 +3815,7 @@ var DrawerManager = class _DrawerManager {
|
|
|
4611
3815
|
logger;
|
|
4612
3816
|
constructor(ctx) {
|
|
4613
3817
|
this.factory = new DrawerFactory(ctx);
|
|
4614
|
-
this.logger = new
|
|
3818
|
+
this.logger = new import_koishi7.Logger("Noah-Drawer");
|
|
4615
3819
|
}
|
|
4616
3820
|
/**
|
|
4617
3821
|
* 获取绘图管理器实例
|
|
@@ -4634,35 +3838,35 @@ var DrawerManager = class _DrawerManager {
|
|
|
4634
3838
|
return this.factory.getDrawer(pluginType);
|
|
4635
3839
|
}
|
|
4636
3840
|
};
|
|
4637
|
-
function
|
|
3841
|
+
function apply6(ctx, config) {
|
|
4638
3842
|
ctx.logger("Noah-Drawer").info("Initializing drawer manager");
|
|
4639
3843
|
const drawerManager = DrawerManager.getInstance(ctx);
|
|
4640
3844
|
}
|
|
4641
|
-
__name(
|
|
3845
|
+
__name(apply6, "apply");
|
|
4642
3846
|
|
|
4643
3847
|
// src/fun/poke/index.ts
|
|
4644
3848
|
var poke_exports = {};
|
|
4645
3849
|
__export(poke_exports, {
|
|
4646
|
-
apply: () =>
|
|
4647
|
-
name: () =>
|
|
3850
|
+
apply: () => apply7,
|
|
3851
|
+
name: () => name7
|
|
4648
3852
|
});
|
|
4649
3853
|
var fs3 = __toESM(require("fs"), 1);
|
|
4650
3854
|
var path2 = __toESM(require("path"), 1);
|
|
4651
|
-
var
|
|
3855
|
+
var import_koishi8 = require("koishi");
|
|
4652
3856
|
|
|
4653
3857
|
// src/fun/poke/locales/en-US.yml
|
|
4654
|
-
var
|
|
3858
|
+
var en_US_default3 = { _config: { $desc: "Poke Module Settings", interval: "最小触发间隔(毫秒)", warning: "频繁触发是否发送警告", prompt: "警告内容", messages: { $desc: "消息内容", content: "消息内容", weight: "权重" } }, commands: { poke: { description: "poke" } } };
|
|
4655
3859
|
|
|
4656
3860
|
// src/fun/poke/locales/zh-CN.yml
|
|
4657
|
-
var
|
|
3861
|
+
var zh_CN_default3 = { _config: { $desc: "Poke 模块设置", interval: "最小触发间隔(毫秒)", warning: "频繁触发是否发送警告", prompt: { $desc: "警告内容", content: "消息", weight: "权重" }, messages: { $desc: "消息内容", content: "消息", weight: "权重" } }, commands: { poke: { description: "戳一戳" } } };
|
|
4658
3862
|
|
|
4659
3863
|
// src/fun/poke/index.ts
|
|
4660
|
-
var
|
|
4661
|
-
function
|
|
3864
|
+
var name7 = "Noah-Poke";
|
|
3865
|
+
function apply7(ctx, config) {
|
|
4662
3866
|
;
|
|
4663
3867
|
[
|
|
4664
|
-
["en-US",
|
|
4665
|
-
["zh-CN",
|
|
3868
|
+
["en-US", en_US_default3],
|
|
3869
|
+
["zh-CN", zh_CN_default3]
|
|
4666
3870
|
].forEach(([lang, file]) => ctx.i18n.define(lang, file));
|
|
4667
3871
|
const cache = /* @__PURE__ */ new Map();
|
|
4668
3872
|
const pokeConfig2 = config.poke;
|
|
@@ -4751,11 +3955,11 @@ function apply9(ctx, config) {
|
|
|
4751
3955
|
}
|
|
4752
3956
|
const imageBuffer = fs3.readFileSync(randomStamp.path);
|
|
4753
3957
|
const audioPath = findMatchingAudio(randomStamp.path);
|
|
4754
|
-
await session.sendQueued(
|
|
3958
|
+
await session.sendQueued(import_koishi8.h.image(imageBuffer, getMimeType(randomStamp.path)));
|
|
4755
3959
|
if (audioPath && fs3.existsSync(audioPath)) {
|
|
4756
3960
|
ctx2.logger("Noah-Poke").debug(`Found matching audio: ${path2.basename(audioPath)}`);
|
|
4757
3961
|
const audioBuffer = fs3.readFileSync(audioPath);
|
|
4758
|
-
await session.sendQueued(
|
|
3962
|
+
await session.sendQueued(import_koishi8.h.audio(audioBuffer, "audio/mpeg"));
|
|
4759
3963
|
}
|
|
4760
3964
|
} catch (error) {
|
|
4761
3965
|
ctx2.logger("Noah-Poke").error(`Error sending noah stamp: ${error.message}`);
|
|
@@ -4776,7 +3980,7 @@ function apply9(ctx, config) {
|
|
|
4776
3980
|
return "Selected stamp not found!";
|
|
4777
3981
|
}
|
|
4778
3982
|
const imageBuffer = fs3.readFileSync(stampPath);
|
|
4779
|
-
return
|
|
3983
|
+
return import_koishi8.h.image(imageBuffer, getMimeType(stampPath));
|
|
4780
3984
|
} catch (error) {
|
|
4781
3985
|
ctx2.logger("Noah-Poke").error(`Error sending chat stamp: ${error.message}`);
|
|
4782
3986
|
return "Failed to send chat stamp.";
|
|
@@ -4823,7 +4027,7 @@ function apply9(ctx, config) {
|
|
|
4823
4027
|
if (session.timestamp - ts < pokeConfig2.interval) {
|
|
4824
4028
|
if (pokeConfig2.warning) {
|
|
4825
4029
|
const msg = randomMessage(pokeConfig2.prompt);
|
|
4826
|
-
const content =
|
|
4030
|
+
const content = import_koishi8.h.parse(msg, session);
|
|
4827
4031
|
session.sendQueued(content);
|
|
4828
4032
|
}
|
|
4829
4033
|
return;
|
|
@@ -4832,12 +4036,12 @@ function apply9(ctx, config) {
|
|
|
4832
4036
|
cache.set(session.userId, session.timestamp);
|
|
4833
4037
|
if (pokeConfig2.messages.length > 0) {
|
|
4834
4038
|
const msg = randomMessage(pokeConfig2.messages);
|
|
4835
|
-
const content =
|
|
4039
|
+
const content = import_koishi8.h.parse(msg, session);
|
|
4836
4040
|
await session.sendQueued(content);
|
|
4837
4041
|
}
|
|
4838
4042
|
});
|
|
4839
4043
|
}
|
|
4840
|
-
__name(
|
|
4044
|
+
__name(apply7, "apply");
|
|
4841
4045
|
function randomMessage(messages) {
|
|
4842
4046
|
const totalWeight = messages.reduce((sum2, cur) => sum2 + cur.weight, 0);
|
|
4843
4047
|
const random = Math.random() * totalWeight;
|
|
@@ -4859,14 +4063,14 @@ __name(parsePlatform, "parsePlatform");
|
|
|
4859
4063
|
// src/games/general/index.ts
|
|
4860
4064
|
var general_exports = {};
|
|
4861
4065
|
__export(general_exports, {
|
|
4862
|
-
apply: () =>
|
|
4863
|
-
logger: () =>
|
|
4864
|
-
name: () =>
|
|
4066
|
+
apply: () => apply8,
|
|
4067
|
+
logger: () => logger3,
|
|
4068
|
+
name: () => name8
|
|
4865
4069
|
});
|
|
4866
|
-
var
|
|
4070
|
+
var import_koishi10 = require("koishi");
|
|
4867
4071
|
|
|
4868
4072
|
// src/games/general/events/quote.ts
|
|
4869
|
-
var
|
|
4073
|
+
var import_koishi9 = require("koishi");
|
|
4870
4074
|
|
|
4871
4075
|
// src/games/general/utils/codeReader.ts
|
|
4872
4076
|
async function readCode128(ctx, barcodeApiUrl, imageUrl) {
|
|
@@ -4892,8 +4096,8 @@ __name(readCode128, "readCode128");
|
|
|
4892
4096
|
function quote(ctx, config) {
|
|
4893
4097
|
ctx.on("message", async (session) => {
|
|
4894
4098
|
if (session.quote && session.quote.user.id === session.selfId) {
|
|
4895
|
-
const images = await
|
|
4896
|
-
const textElements = await
|
|
4099
|
+
const images = await import_koishi9.h.select(session.quote.elements, "img");
|
|
4100
|
+
const textElements = await import_koishi9.h.select(session.elements, "text");
|
|
4897
4101
|
const allText = textElements.map((el) => el.attrs?.content || "").join(" ");
|
|
4898
4102
|
if (images.length === 1) {
|
|
4899
4103
|
const imageUrl = images[0].attrs.src;
|
|
@@ -4918,32 +4122,32 @@ function quote(ctx, config) {
|
|
|
4918
4122
|
__name(quote, "quote");
|
|
4919
4123
|
|
|
4920
4124
|
// src/games/general/index.ts
|
|
4921
|
-
var
|
|
4922
|
-
var
|
|
4923
|
-
async function
|
|
4125
|
+
var name8 = "Noah-General";
|
|
4126
|
+
var logger3 = new import_koishi10.Logger("Noah-General");
|
|
4127
|
+
async function apply8(ctx, config) {
|
|
4924
4128
|
quote(ctx, config.general);
|
|
4925
4129
|
}
|
|
4926
|
-
__name(
|
|
4130
|
+
__name(apply8, "apply");
|
|
4927
4131
|
|
|
4928
4132
|
// src/games/sdvx/index.ts
|
|
4929
4133
|
var sdvx_exports = {};
|
|
4930
4134
|
__export(sdvx_exports, {
|
|
4931
|
-
apply: () =>
|
|
4135
|
+
apply: () => apply13,
|
|
4932
4136
|
inject: () => inject3,
|
|
4933
|
-
logger: () =>
|
|
4934
|
-
name: () =>
|
|
4137
|
+
logger: () => logger4,
|
|
4138
|
+
name: () => name13
|
|
4935
4139
|
});
|
|
4936
|
-
var
|
|
4140
|
+
var import_koishi18 = require("koishi");
|
|
4937
4141
|
|
|
4938
4142
|
// src/games/sdvx/command.ts
|
|
4939
4143
|
var command_exports2 = {};
|
|
4940
4144
|
__export(command_exports2, {
|
|
4941
|
-
apply: () =>
|
|
4942
|
-
name: () =>
|
|
4145
|
+
apply: () => apply10,
|
|
4146
|
+
name: () => name10
|
|
4943
4147
|
});
|
|
4944
4148
|
|
|
4945
4149
|
// src/games/sdvx/commands/calculate.ts
|
|
4946
|
-
var
|
|
4150
|
+
var import_koishi11 = require("koishi");
|
|
4947
4151
|
|
|
4948
4152
|
// src/games/sdvx/utils/param-parser.ts
|
|
4949
4153
|
function parseNumberWithSuffix(str) {
|
|
@@ -5064,37 +4268,17 @@ function parseVfValue(item) {
|
|
|
5064
4268
|
}
|
|
5065
4269
|
__name(parseVfValue, "parseVfValue");
|
|
5066
4270
|
function parseClearTypeRange(item) {
|
|
5067
|
-
const clearTypeMap = {
|
|
5068
|
-
spuc: "S-PUC",
|
|
5069
|
-
puc: "PUC",
|
|
5070
|
-
uc: "UC",
|
|
5071
|
-
mc: "MC",
|
|
5072
|
-
hc: "HC",
|
|
5073
|
-
nc: "NC",
|
|
5074
|
-
played: "PLAYED",
|
|
5075
|
-
noplay: "NO PLAY"
|
|
5076
|
-
};
|
|
5077
|
-
const clearTypeList = [
|
|
5078
|
-
"S-PUC",
|
|
5079
|
-
"PUC",
|
|
5080
|
-
"UC",
|
|
5081
|
-
"MC",
|
|
5082
|
-
"HC",
|
|
5083
|
-
"NC",
|
|
5084
|
-
"PLAYED",
|
|
5085
|
-
"NO PLAY"
|
|
5086
|
-
];
|
|
5087
4271
|
if (item.includes("-")) {
|
|
5088
4272
|
const [startRaw, endRaw] = item.split("-").filter(Boolean);
|
|
5089
|
-
if (startRaw && endRaw &&
|
|
5090
|
-
const start =
|
|
5091
|
-
const end =
|
|
4273
|
+
if (startRaw && endRaw && CLEAR_TYPE_ABBR_MAP[startRaw] && CLEAR_TYPE_ABBR_MAP[endRaw]) {
|
|
4274
|
+
const start = ALL_CLEAR_TYPES.findIndex((t) => t === CLEAR_TYPE_ABBR_MAP[startRaw]);
|
|
4275
|
+
const end = ALL_CLEAR_TYPES.findIndex((t) => t === CLEAR_TYPE_ABBR_MAP[endRaw]);
|
|
5092
4276
|
if (start !== -1 && end !== -1) {
|
|
5093
4277
|
const from = Math.min(start, end);
|
|
5094
4278
|
const to = Math.max(start, end);
|
|
5095
4279
|
const clearTypes = [];
|
|
5096
4280
|
for (let i = from; i <= to; i++) {
|
|
5097
|
-
clearTypes.push(
|
|
4281
|
+
clearTypes.push(ALL_CLEAR_TYPES[i]);
|
|
5098
4282
|
}
|
|
5099
4283
|
if (clearTypes.includes("PUC") && !clearTypes.includes("S-PUC")) {
|
|
5100
4284
|
clearTypes.push("S-PUC");
|
|
@@ -5107,18 +4291,8 @@ function parseClearTypeRange(item) {
|
|
|
5107
4291
|
}
|
|
5108
4292
|
__name(parseClearTypeRange, "parseClearTypeRange");
|
|
5109
4293
|
function parseSingleClearType(item) {
|
|
5110
|
-
|
|
5111
|
-
|
|
5112
|
-
puc: "PUC",
|
|
5113
|
-
uc: "UC",
|
|
5114
|
-
mc: "MC",
|
|
5115
|
-
hc: "HC",
|
|
5116
|
-
nc: "NC",
|
|
5117
|
-
played: "PLAYED",
|
|
5118
|
-
noplay: "NO PLAY"
|
|
5119
|
-
};
|
|
5120
|
-
if (clearTypeMap[item.toLowerCase()]) {
|
|
5121
|
-
const clearType = clearTypeMap[item.toLowerCase()];
|
|
4294
|
+
if (CLEAR_TYPE_ABBR_MAP[item.toLowerCase()]) {
|
|
4295
|
+
const clearType = CLEAR_TYPE_ABBR_MAP[item.toLowerCase()];
|
|
5122
4296
|
if (clearType === "PUC") {
|
|
5123
4297
|
return ["PUC", "S-PUC"];
|
|
5124
4298
|
}
|
|
@@ -5128,17 +4302,16 @@ function parseSingleClearType(item) {
|
|
|
5128
4302
|
}
|
|
5129
4303
|
__name(parseSingleClearType, "parseSingleClearType");
|
|
5130
4304
|
function parseGradeRange(item) {
|
|
5131
|
-
const gradeList = ["S", "AAA+", "AAA", "AA+", "AA", "A+", "A", "B", "C", "D"];
|
|
5132
4305
|
if (/^[a-ds][a-z+]*-[a-ds][a-z+]*$/.test(item)) {
|
|
5133
4306
|
const [startRaw, endRaw] = item.split("-");
|
|
5134
|
-
const start =
|
|
5135
|
-
const end =
|
|
4307
|
+
const start = ALL_GRADES.findIndex((g) => g.toLowerCase() === startRaw);
|
|
4308
|
+
const end = ALL_GRADES.findIndex((g) => g.toLowerCase() === endRaw);
|
|
5136
4309
|
if (start !== -1 && end !== -1) {
|
|
5137
4310
|
const from = Math.min(start, end);
|
|
5138
4311
|
const to = Math.max(start, end);
|
|
5139
4312
|
const grades = [];
|
|
5140
4313
|
for (let i = from; i <= to; i++) {
|
|
5141
|
-
grades.push(
|
|
4314
|
+
grades.push(ALL_GRADES[i]);
|
|
5142
4315
|
}
|
|
5143
4316
|
return grades;
|
|
5144
4317
|
}
|
|
@@ -5147,8 +4320,7 @@ function parseGradeRange(item) {
|
|
|
5147
4320
|
}
|
|
5148
4321
|
__name(parseGradeRange, "parseGradeRange");
|
|
5149
4322
|
function parseSingleGrade(item) {
|
|
5150
|
-
const
|
|
5151
|
-
const grade = gradeList.find((g) => g.toLowerCase() === item.toLowerCase());
|
|
4323
|
+
const grade = ALL_GRADES.find((g) => g.toLowerCase() === item.toLowerCase());
|
|
5152
4324
|
if (grade) {
|
|
5153
4325
|
return [grade];
|
|
5154
4326
|
}
|
|
@@ -5247,7 +4419,7 @@ function parseQueryInput(input) {
|
|
|
5247
4419
|
__name(parseQueryInput, "parseQueryInput");
|
|
5248
4420
|
|
|
5249
4421
|
// src/games/sdvx/commands/calculate.ts
|
|
5250
|
-
function calculate(ctx, config,
|
|
4422
|
+
function calculate(ctx, config, logger5) {
|
|
5251
4423
|
ctx.command("sdvx.calculate <query:text>").alias("sdvx.cal").action(async ({ session, options }, query) => {
|
|
5252
4424
|
try {
|
|
5253
4425
|
let queryInput = query;
|
|
@@ -5281,7 +4453,7 @@ function calculate(ctx, config, logger6) {
|
|
|
5281
4453
|
lossless: true
|
|
5282
4454
|
}
|
|
5283
4455
|
);
|
|
5284
|
-
return
|
|
4456
|
+
return import_koishi11.h.image(imageBuffer, "image/png");
|
|
5285
4457
|
} catch (error) {
|
|
5286
4458
|
ctx.logger("SDVX-Calculate").warn(error);
|
|
5287
4459
|
return session.text(".error");
|
|
@@ -5291,7 +4463,7 @@ function calculate(ctx, config, logger6) {
|
|
|
5291
4463
|
__name(calculate, "calculate");
|
|
5292
4464
|
|
|
5293
4465
|
// src/games/sdvx/commands/chart.ts
|
|
5294
|
-
var
|
|
4466
|
+
var import_koishi12 = require("koishi");
|
|
5295
4467
|
|
|
5296
4468
|
// src/servers/utils/difficulty.ts
|
|
5297
4469
|
function getDiffName(diffStr, infVer) {
|
|
@@ -5480,7 +4652,6 @@ var MusicService = class _MusicService {
|
|
|
5480
4652
|
const music = typeof id === "number" ? numericMap.get(id) : stringMap.get(id);
|
|
5481
4653
|
if (music) results.push(music);
|
|
5482
4654
|
}
|
|
5483
|
-
console.log("results", results.slice(0, 3));
|
|
5484
4655
|
return results;
|
|
5485
4656
|
}
|
|
5486
4657
|
/**
|
|
@@ -5528,7 +4699,6 @@ var MusicService = class _MusicService {
|
|
|
5528
4699
|
if (musicIds.length === 0) {
|
|
5529
4700
|
return null;
|
|
5530
4701
|
}
|
|
5531
|
-
console.log("musicIds", musicIds);
|
|
5532
4702
|
return await this.getMusic(ctx, musicIds);
|
|
5533
4703
|
} catch {
|
|
5534
4704
|
return null;
|
|
@@ -5643,7 +4813,7 @@ function getHighestDifstr(diffs) {
|
|
|
5643
4813
|
return diffs[diffs.length - 1].difstr;
|
|
5644
4814
|
}
|
|
5645
4815
|
__name(getHighestDifstr, "getHighestDifstr");
|
|
5646
|
-
function chart(ctx, config,
|
|
4816
|
+
function chart(ctx, config, logger5) {
|
|
5647
4817
|
ctx.command("sdvx.chart [query:text]").alias("sdvx.c").action(async ({ session }, query) => {
|
|
5648
4818
|
if (!query) {
|
|
5649
4819
|
await session.send(session.text(".prompt"));
|
|
@@ -5706,7 +4876,7 @@ function chart(ctx, config, logger6) {
|
|
|
5706
4876
|
const res = await ctx.http.post(`${config.sdvx_data_url}/chart`, payload, {
|
|
5707
4877
|
headers: { "Content-Type": "application/json" }
|
|
5708
4878
|
});
|
|
5709
|
-
return
|
|
4879
|
+
return import_koishi12.h.image(res, "image/png");
|
|
5710
4880
|
} else {
|
|
5711
4881
|
const {
|
|
5712
4882
|
diffStr: parsedDiff,
|
|
@@ -5722,7 +4892,7 @@ function chart(ctx, config, logger6) {
|
|
|
5722
4892
|
const picked = musicInfo[0];
|
|
5723
4893
|
const music_id = Number(picked?.id);
|
|
5724
4894
|
if (!Number.isFinite(music_id)) {
|
|
5725
|
-
|
|
4895
|
+
logger5.warn("search result missing id", picked);
|
|
5726
4896
|
return session.text(".error");
|
|
5727
4897
|
}
|
|
5728
4898
|
let difstr = null;
|
|
@@ -5756,29 +4926,27 @@ function chart(ctx, config, logger6) {
|
|
|
5756
4926
|
const res = await ctx.http.post(`${config.sdvx_data_url}/chart`, payload, {
|
|
5757
4927
|
headers: { "Content-Type": "application/json" }
|
|
5758
4928
|
});
|
|
5759
|
-
return
|
|
4929
|
+
return import_koishi12.h.image(res, "image/png");
|
|
5760
4930
|
}
|
|
5761
4931
|
} catch (err) {
|
|
5762
|
-
|
|
4932
|
+
logger5.warn(err);
|
|
5763
4933
|
return session.text(".error");
|
|
5764
4934
|
}
|
|
5765
4935
|
});
|
|
5766
4936
|
}
|
|
5767
4937
|
__name(chart, "chart");
|
|
5768
4938
|
|
|
5769
|
-
// src/games/sdvx/commands/recent.ts
|
|
5770
|
-
var import_koishi15 = require("koishi");
|
|
5771
|
-
|
|
5772
4939
|
// src/servers/index.ts
|
|
5773
4940
|
var servers_exports = {};
|
|
5774
4941
|
__export(servers_exports, {
|
|
5775
4942
|
ServerManager: () => ServerManager,
|
|
5776
|
-
apply: () =>
|
|
5777
|
-
|
|
4943
|
+
apply: () => apply9,
|
|
4944
|
+
inject: () => inject2,
|
|
4945
|
+
name: () => name9
|
|
5778
4946
|
});
|
|
5779
4947
|
|
|
5780
4948
|
// src/servers/Asphyxia/index.ts
|
|
5781
|
-
var
|
|
4949
|
+
var import_koishi13 = require("koishi");
|
|
5782
4950
|
|
|
5783
4951
|
// src/servers/Asphyxia/services/iidx-service.ts
|
|
5784
4952
|
var IIDXService = class _IIDXService {
|
|
@@ -5787,12 +4955,12 @@ var IIDXService = class _IIDXService {
|
|
|
5787
4955
|
}
|
|
5788
4956
|
static instance;
|
|
5789
4957
|
logger;
|
|
5790
|
-
constructor(
|
|
5791
|
-
this.logger =
|
|
4958
|
+
constructor(logger5) {
|
|
4959
|
+
this.logger = logger5;
|
|
5792
4960
|
}
|
|
5793
|
-
static getInstance(
|
|
4961
|
+
static getInstance(logger5) {
|
|
5794
4962
|
if (!_IIDXService.instance) {
|
|
5795
|
-
_IIDXService.instance = new _IIDXService(
|
|
4963
|
+
_IIDXService.instance = new _IIDXService(logger5);
|
|
5796
4964
|
}
|
|
5797
4965
|
return _IIDXService.instance;
|
|
5798
4966
|
}
|
|
@@ -5801,19 +4969,6 @@ var IIDXService = class _IIDXService {
|
|
|
5801
4969
|
};
|
|
5802
4970
|
|
|
5803
4971
|
// src/servers/utils/grade.ts
|
|
5804
|
-
function getSDVXGrade(score) {
|
|
5805
|
-
if (score >= 99e5) return "S";
|
|
5806
|
-
if (score >= 98e5) return "AAA+";
|
|
5807
|
-
if (score >= 97e5) return "AAA";
|
|
5808
|
-
if (score >= 95e5) return "AA+";
|
|
5809
|
-
if (score >= 93e5) return "AA";
|
|
5810
|
-
if (score >= 9e6) return "A+";
|
|
5811
|
-
if (score >= 87e5) return "A";
|
|
5812
|
-
if (score >= 75e5) return "B";
|
|
5813
|
-
if (score >= 65e5) return "C";
|
|
5814
|
-
return "D";
|
|
5815
|
-
}
|
|
5816
|
-
__name(getSDVXGrade, "getSDVXGrade");
|
|
5817
4972
|
function getSDVXClearType(clearType) {
|
|
5818
4973
|
return clearType === 6 ? "MC" : clearType === 5 ? "PUC" : clearType === 4 ? "UC" : clearType === 3 ? "HC" : clearType === 2 ? "NC" : clearType === 1 ? "PLAYED" : "NO PLAY";
|
|
5819
4974
|
}
|
|
@@ -5840,12 +4995,12 @@ var SDVXService = class _SDVXService {
|
|
|
5840
4995
|
}
|
|
5841
4996
|
static instance;
|
|
5842
4997
|
logger;
|
|
5843
|
-
constructor(
|
|
5844
|
-
this.logger =
|
|
4998
|
+
constructor(logger5) {
|
|
4999
|
+
this.logger = logger5;
|
|
5845
5000
|
}
|
|
5846
|
-
static getInstance(
|
|
5001
|
+
static getInstance(logger5) {
|
|
5847
5002
|
if (!_SDVXService.instance) {
|
|
5848
|
-
_SDVXService.instance = new _SDVXService(
|
|
5003
|
+
_SDVXService.instance = new _SDVXService(logger5);
|
|
5849
5004
|
}
|
|
5850
5005
|
return _SDVXService.instance;
|
|
5851
5006
|
}
|
|
@@ -5882,9 +5037,9 @@ var SDVXService = class _SDVXService {
|
|
|
5882
5037
|
const response = await ctx.http.post(requestUrl, xmlRequestBody, { baseURL: url });
|
|
5883
5038
|
const decodedResponse = typeof response === "string" ? response : new TextDecoder("utf-8").decode(response);
|
|
5884
5039
|
const jsonResponse = await xmlToJson(decodedResponse);
|
|
5885
|
-
const
|
|
5886
|
-
if (!
|
|
5887
|
-
return
|
|
5040
|
+
const name15 = jsonResponse.response.game?.[0]?.name?.[0]._;
|
|
5041
|
+
if (!name15) throw new Error("User name not found");
|
|
5042
|
+
return name15;
|
|
5888
5043
|
}
|
|
5889
5044
|
/**
|
|
5890
5045
|
* 获取所有分数
|
|
@@ -5943,7 +5098,7 @@ var SDVXService = class _SDVXService {
|
|
|
5943
5098
|
score,
|
|
5944
5099
|
exscore,
|
|
5945
5100
|
clear_type: getSDVXClearType(clear_type),
|
|
5946
|
-
score_grade:
|
|
5101
|
+
score_grade: getGradeByScore(score),
|
|
5947
5102
|
btn_rate: String(btn_rate),
|
|
5948
5103
|
long_rate: String(long_rate),
|
|
5949
5104
|
vol_rate: String(vol_rate)
|
|
@@ -5984,7 +5139,7 @@ var Asphyxia = class {
|
|
|
5984
5139
|
name = "asphyxia";
|
|
5985
5140
|
supportedGames = ["sdvx", "iidx"];
|
|
5986
5141
|
gameServices = {};
|
|
5987
|
-
logger = new
|
|
5142
|
+
logger = new import_koishi13.Logger("Noah-Asphyxia");
|
|
5988
5143
|
/**
|
|
5989
5144
|
* 初始化各个游戏服务实例
|
|
5990
5145
|
*/
|
|
@@ -5995,7 +5150,7 @@ var Asphyxia = class {
|
|
|
5995
5150
|
};
|
|
5996
5151
|
|
|
5997
5152
|
// src/servers/Mao/index.ts
|
|
5998
|
-
var
|
|
5153
|
+
var import_koishi14 = require("koishi");
|
|
5999
5154
|
|
|
6000
5155
|
// src/servers/Mao/services/sdvx-service.ts
|
|
6001
5156
|
var SDVXService2 = class _SDVXService {
|
|
@@ -6004,12 +5159,12 @@ var SDVXService2 = class _SDVXService {
|
|
|
6004
5159
|
}
|
|
6005
5160
|
static instance;
|
|
6006
5161
|
logger;
|
|
6007
|
-
constructor(
|
|
6008
|
-
this.logger =
|
|
5162
|
+
constructor(logger5) {
|
|
5163
|
+
this.logger = logger5;
|
|
6009
5164
|
}
|
|
6010
|
-
static getInstance(
|
|
5165
|
+
static getInstance(logger5) {
|
|
6011
5166
|
if (!_SDVXService.instance) {
|
|
6012
|
-
_SDVXService.instance = new _SDVXService(
|
|
5167
|
+
_SDVXService.instance = new _SDVXService(logger5);
|
|
6013
5168
|
}
|
|
6014
5169
|
return _SDVXService.instance;
|
|
6015
5170
|
}
|
|
@@ -6038,7 +5193,7 @@ var SDVXService2 = class _SDVXService {
|
|
|
6038
5193
|
*/
|
|
6039
5194
|
async getUserName(ctx, url, cardId) {
|
|
6040
5195
|
const response = await ctx.http.get(`/my?card=${cardId}`, {
|
|
6041
|
-
baseURL:
|
|
5196
|
+
baseURL: ctx.globalConfig.maoServerUrl,
|
|
6042
5197
|
responseType: "text"
|
|
6043
5198
|
});
|
|
6044
5199
|
const match = response.match(/猫网玩家\[([^\]]+)\]/);
|
|
@@ -6056,16 +5211,19 @@ var SDVXService2 = class _SDVXService {
|
|
|
6056
5211
|
* @returns 验证是否通过
|
|
6057
5212
|
*/
|
|
6058
5213
|
async verifyPin(ctx, url, cardId, pin) {
|
|
6059
|
-
const
|
|
6060
|
-
|
|
6061
|
-
"
|
|
5214
|
+
const apiKey = ctx.globalConfig.maoApiKey;
|
|
5215
|
+
if (!apiKey) {
|
|
5216
|
+
this.logger.warn("maoApiKey not configured");
|
|
5217
|
+
return false;
|
|
5218
|
+
}
|
|
5219
|
+
const resp = await ctx.http.get(
|
|
5220
|
+
`/bot/v2/player/card?card=${cardId}`,
|
|
6062
5221
|
{
|
|
6063
|
-
|
|
6064
|
-
|
|
6065
|
-
}
|
|
6066
|
-
{ baseURL }
|
|
5222
|
+
baseURL: ctx.globalConfig.maoServerUrl,
|
|
5223
|
+
headers: { "X-API-Key": apiKey }
|
|
5224
|
+
}
|
|
6067
5225
|
);
|
|
6068
|
-
return resp?.
|
|
5226
|
+
return resp?.code === 0 && resp?.data?.password === pin;
|
|
6069
5227
|
}
|
|
6070
5228
|
async uploadScore(ctx, url, cardId, scorePayload) {
|
|
6071
5229
|
const baseURL = "https://maomani.cn";
|
|
@@ -6081,22 +5239,30 @@ var SDVXService2 = class _SDVXService {
|
|
|
6081
5239
|
}
|
|
6082
5240
|
async getAllScore(ctx, url, cardId, config) {
|
|
6083
5241
|
try {
|
|
6084
|
-
const
|
|
6085
|
-
|
|
6086
|
-
|
|
5242
|
+
const apiKey = ctx.globalConfig.maoApiKey;
|
|
5243
|
+
if (!apiKey) {
|
|
5244
|
+
this.logger.warn("maoApiKey not configured");
|
|
5245
|
+
return [];
|
|
5246
|
+
}
|
|
5247
|
+
const resp = await ctx.http.get(`/bot/v2/sdvx/scores?card=${cardId}`, {
|
|
5248
|
+
baseURL: ctx.globalConfig.maoServerUrl,
|
|
5249
|
+
headers: { "X-API-Key": apiKey }
|
|
6087
5250
|
});
|
|
6088
|
-
|
|
6089
|
-
|
|
5251
|
+
if (resp?.code !== 0 || !resp?.data) {
|
|
5252
|
+
return [];
|
|
5253
|
+
}
|
|
5254
|
+
const filteredData = resp.data.filter(
|
|
5255
|
+
(score) => score.mid !== 244 && score.mid !== 1759 && score.mid !== 1761 && score.isPlus === 0
|
|
6090
5256
|
);
|
|
6091
|
-
const musicIds = filteredData.map((score) => score.
|
|
6092
|
-
const diffTypes = filteredData.map((score) =>
|
|
5257
|
+
const musicIds = filteredData.map((score) => score.mid);
|
|
5258
|
+
const diffTypes = filteredData.map((score) => score.musicType);
|
|
6093
5259
|
const musicService = MusicService.getInstance(config);
|
|
6094
5260
|
const musicData = await musicService.getMusic(ctx, musicIds);
|
|
6095
5261
|
const musicDataMap = new Map(musicData.map((music) => [music.id, music]));
|
|
6096
5262
|
const scores = filteredData.map((score, index) => {
|
|
6097
|
-
const musicId = score.
|
|
5263
|
+
const musicId = score.mid;
|
|
6098
5264
|
const music = musicDataMap.get(musicId);
|
|
6099
|
-
const musicDiffType =
|
|
5265
|
+
const musicDiffType = score.musicType;
|
|
6100
5266
|
const diffStr = this.getDifficultyString(musicDiffType);
|
|
6101
5267
|
let difficultyData = null;
|
|
6102
5268
|
if (music && music.difficulty && music.difficulty.length > 0) {
|
|
@@ -6111,15 +5277,15 @@ var SDVXService2 = class _SDVXService {
|
|
|
6111
5277
|
music_diff_full_name: getDiffFullName(diffStr, infVer),
|
|
6112
5278
|
score: score.score,
|
|
6113
5279
|
exscore: score.exscore,
|
|
6114
|
-
clear_type: getSDVXClearType(score.
|
|
6115
|
-
score_grade:
|
|
6116
|
-
btn_rate: score.
|
|
6117
|
-
long_rate: score.
|
|
6118
|
-
vol_rate: score.
|
|
5280
|
+
clear_type: getSDVXClearType(score.clearType),
|
|
5281
|
+
score_grade: getGradeByScore(score.score),
|
|
5282
|
+
btn_rate: score.btnRate.toString(),
|
|
5283
|
+
long_rate: score.longRate.toString(),
|
|
5284
|
+
vol_rate: score.volRate.toString()
|
|
6119
5285
|
},
|
|
6120
5286
|
extra: {
|
|
6121
|
-
play_count: score.
|
|
6122
|
-
update_at: score.
|
|
5287
|
+
play_count: score.playCount,
|
|
5288
|
+
update_at: score.updateAt,
|
|
6123
5289
|
volforce: 0
|
|
6124
5290
|
},
|
|
6125
5291
|
music_data: music || null,
|
|
@@ -6137,12 +5303,16 @@ var SDVXService2 = class _SDVXService {
|
|
|
6137
5303
|
}
|
|
6138
5304
|
}
|
|
6139
5305
|
async getScore(ctx, url, cardId, musicId, config) {
|
|
6140
|
-
const
|
|
6141
|
-
|
|
6142
|
-
|
|
5306
|
+
const apiKey = ctx.globalConfig.maoApiKey;
|
|
5307
|
+
if (!apiKey) {
|
|
5308
|
+
this.logger.warn("maoApiKey not configured");
|
|
5309
|
+
throw new Error("maoApiKey not configured");
|
|
5310
|
+
}
|
|
5311
|
+
const resp = await ctx.http.get(`/bot/v2/sdvx/find?card=${cardId}&mid=${musicId}`, {
|
|
5312
|
+
baseURL: ctx.globalConfig.maoServerUrl,
|
|
5313
|
+
headers: { "X-API-Key": apiKey }
|
|
6143
5314
|
});
|
|
6144
|
-
|
|
6145
|
-
if (text === "没有找到相关记录") {
|
|
5315
|
+
if (resp?.code !== 0 || !resp?.data) {
|
|
6146
5316
|
const musicService2 = MusicService.getInstance(config);
|
|
6147
5317
|
const musicData2 = await musicService2.getMusic(ctx, [musicId]);
|
|
6148
5318
|
const music2 = musicData2.length > 0 ? musicData2[0] : null;
|
|
@@ -6162,7 +5332,7 @@ var SDVXService2 = class _SDVXService {
|
|
|
6162
5332
|
exscore: 0,
|
|
6163
5333
|
clear_type: getSDVXClearType(0),
|
|
6164
5334
|
// NO PLAY
|
|
6165
|
-
score_grade:
|
|
5335
|
+
score_grade: getGradeByScore(0),
|
|
6166
5336
|
btn_rate: "0",
|
|
6167
5337
|
long_rate: "0",
|
|
6168
5338
|
vol_rate: "0"
|
|
@@ -6176,14 +5346,8 @@ var SDVXService2 = class _SDVXService {
|
|
|
6176
5346
|
difficulty_data: difficultyData2
|
|
6177
5347
|
};
|
|
6178
5348
|
}
|
|
6179
|
-
const
|
|
6180
|
-
|
|
6181
|
-
);
|
|
6182
|
-
if (!matches) {
|
|
6183
|
-
throw new Error(`Invalid score format received: ${text}`);
|
|
6184
|
-
}
|
|
6185
|
-
const [, musicType, score, exscore, clearType, playCount] = matches;
|
|
6186
|
-
const musicTypeNum = parseInt(musicType);
|
|
5349
|
+
const data = resp.data;
|
|
5350
|
+
const musicTypeNum = data.musicType;
|
|
6187
5351
|
const diffStr = this.getDifficultyString(musicTypeNum);
|
|
6188
5352
|
const musicService = MusicService.getInstance(config);
|
|
6189
5353
|
const musicData = await musicService.getMusic(ctx, [musicId]);
|
|
@@ -6199,19 +5363,16 @@ var SDVXService2 = class _SDVXService {
|
|
|
6199
5363
|
music_diff: difficultyData ? difficultyData.difnum : 0,
|
|
6200
5364
|
music_diff_name: getDiffName(diffStr, infVer),
|
|
6201
5365
|
music_diff_full_name: getDiffFullName(diffStr, infVer),
|
|
6202
|
-
score:
|
|
6203
|
-
exscore:
|
|
6204
|
-
clear_type: getSDVXClearType(
|
|
6205
|
-
score_grade:
|
|
5366
|
+
score: data.score,
|
|
5367
|
+
exscore: data.exscore,
|
|
5368
|
+
clear_type: getSDVXClearType(data.clearType),
|
|
5369
|
+
score_grade: getGradeByScore(data.score),
|
|
6206
5370
|
btn_rate: "0",
|
|
6207
|
-
// Not provided in response
|
|
6208
5371
|
long_rate: "0",
|
|
6209
|
-
// Not provided in response
|
|
6210
5372
|
vol_rate: "0"
|
|
6211
|
-
// Not provided in response
|
|
6212
5373
|
},
|
|
6213
5374
|
extra: {
|
|
6214
|
-
play_count:
|
|
5375
|
+
play_count: data.playCount,
|
|
6215
5376
|
update_at: "0",
|
|
6216
5377
|
volforce: 0
|
|
6217
5378
|
},
|
|
@@ -6222,49 +5383,25 @@ var SDVXService2 = class _SDVXService {
|
|
|
6222
5383
|
return scoreObj;
|
|
6223
5384
|
}
|
|
6224
5385
|
async getRecentScores(ctx, url, cardId, config, count = 1) {
|
|
6225
|
-
const
|
|
6226
|
-
|
|
6227
|
-
|
|
6228
|
-
|
|
6229
|
-
const scores = [];
|
|
6230
|
-
const musicIds = [];
|
|
6231
|
-
const tempScores = [];
|
|
6232
|
-
const lines = text.split("\n");
|
|
6233
|
-
for (const line of lines) {
|
|
6234
|
-
const match = line.match(
|
|
6235
|
-
/lscore:(\d+)\s+lexscore:(\d+)\s+lctype:(\d+)\s+mid:(\d+)\s+mtype:(\d+)\s+ctype:(\d+)\s+score:(\d+)\s+exscore:(\d+)\s+time:([^,]+)(?:,\s+retryCnt:(\d+)(?:,\s+judge:([^,]*)(?:,\s+critical:(\d+)(?:,\s+near:(\d+)(?:,\s+error:(\d+)(?:,\s+combo:(\d+))?)?)?)?)?)?/
|
|
6236
|
-
);
|
|
6237
|
-
if (match) {
|
|
6238
|
-
const musicId = match[4];
|
|
6239
|
-
const musicType = match[5];
|
|
6240
|
-
const clearType = match[6];
|
|
6241
|
-
const score = match[7];
|
|
6242
|
-
const exscore = match[8];
|
|
6243
|
-
const time = match[9];
|
|
6244
|
-
const parsedMusicId = parseInt(musicId);
|
|
6245
|
-
tempScores.push({
|
|
6246
|
-
musicId: parsedMusicId,
|
|
6247
|
-
musicType: parseInt(musicType),
|
|
6248
|
-
clearType: parseInt(clearType),
|
|
6249
|
-
score: parseInt(score),
|
|
6250
|
-
exscore: parseInt(exscore),
|
|
6251
|
-
time: time.trim()
|
|
6252
|
-
});
|
|
6253
|
-
musicIds.push(parsedMusicId);
|
|
6254
|
-
}
|
|
5386
|
+
const apiKey = ctx.globalConfig.maoApiKey;
|
|
5387
|
+
if (!apiKey) {
|
|
5388
|
+
this.logger.warn("maoApiKey not configured");
|
|
5389
|
+
return [];
|
|
6255
5390
|
}
|
|
6256
|
-
|
|
5391
|
+
const resp = await ctx.http.get(`/bot/v2/sdvx/recent?card=${cardId}&count=${count}`, {
|
|
5392
|
+
baseURL: ctx.globalConfig.maoServerUrl,
|
|
5393
|
+
headers: { "X-API-Key": apiKey }
|
|
5394
|
+
});
|
|
5395
|
+
if (resp?.code !== 0 || !resp?.data || resp.data.length === 0) {
|
|
6257
5396
|
return [];
|
|
6258
5397
|
}
|
|
5398
|
+
const musicIds = resp.data.map((item) => item.mid);
|
|
6259
5399
|
const musicService = MusicService.getInstance(config);
|
|
6260
5400
|
const musicDataList = await musicService.getMusic(ctx, musicIds);
|
|
6261
5401
|
const musicDataMap = new Map(musicDataList.map((music) => [music.id, music]));
|
|
6262
|
-
|
|
6263
|
-
const
|
|
6264
|
-
const
|
|
6265
|
-
const musicTypeNum = tempScore.musicType;
|
|
6266
|
-
const music = musicDataMap.get(musicId);
|
|
6267
|
-
const diffStr = this.getDifficultyString(musicTypeNum);
|
|
5402
|
+
const scores = resp.data.map((item) => {
|
|
5403
|
+
const music = musicDataMap.get(item.mid);
|
|
5404
|
+
const diffStr = this.getDifficultyString(item.musicType);
|
|
6268
5405
|
let difficultyData = null;
|
|
6269
5406
|
if (music && music.difficulty && music.difficulty.length > 0) {
|
|
6270
5407
|
difficultyData = music.difficulty.find((diff) => diff.difstr.toLowerCase() === diffStr) || null;
|
|
@@ -6272,31 +5409,28 @@ var SDVXService2 = class _SDVXService {
|
|
|
6272
5409
|
const infVer = music ? music.inf_ver : 2;
|
|
6273
5410
|
const scoreObj = {
|
|
6274
5411
|
music: {
|
|
6275
|
-
music_id:
|
|
5412
|
+
music_id: item.mid,
|
|
6276
5413
|
music_diff: difficultyData ? difficultyData.difnum : 0,
|
|
6277
5414
|
music_diff_name: getDiffName(diffStr, infVer),
|
|
6278
5415
|
music_diff_full_name: getDiffFullName(diffStr, infVer),
|
|
6279
|
-
score:
|
|
6280
|
-
exscore:
|
|
6281
|
-
clear_type: getSDVXClearType(
|
|
6282
|
-
score_grade:
|
|
5416
|
+
score: item.score,
|
|
5417
|
+
exscore: item.exscore,
|
|
5418
|
+
clear_type: getSDVXClearType(item.clearType),
|
|
5419
|
+
score_grade: getGradeByScore(item.score),
|
|
6283
5420
|
btn_rate: "0",
|
|
6284
|
-
// Not provided in API response
|
|
6285
5421
|
long_rate: "0",
|
|
6286
|
-
// Not provided in API response
|
|
6287
5422
|
vol_rate: "0"
|
|
6288
|
-
// Not provided in API response
|
|
6289
5423
|
},
|
|
6290
5424
|
extra: {
|
|
6291
5425
|
volforce: 0,
|
|
6292
5426
|
play_count: 0,
|
|
6293
|
-
update_at:
|
|
5427
|
+
update_at: item.time
|
|
6294
5428
|
},
|
|
6295
5429
|
music_data: music || null,
|
|
6296
5430
|
difficulty_data: difficultyData || null
|
|
6297
5431
|
};
|
|
6298
5432
|
scoreObj.extra.volforce = calculateVolforce(scoreObj);
|
|
6299
|
-
|
|
5433
|
+
return scoreObj;
|
|
6300
5434
|
});
|
|
6301
5435
|
return scores;
|
|
6302
5436
|
}
|
|
@@ -6310,7 +5444,7 @@ var Mao = class {
|
|
|
6310
5444
|
name = "mao";
|
|
6311
5445
|
supportedGames = ["sdvx"];
|
|
6312
5446
|
gameServices = {};
|
|
6313
|
-
logger = new
|
|
5447
|
+
logger = new import_koishi14.Logger("Noah-Mao");
|
|
6314
5448
|
/**
|
|
6315
5449
|
* 初始化SDVX游戏服务实例
|
|
6316
5450
|
*/
|
|
@@ -6320,7 +5454,7 @@ var Mao = class {
|
|
|
6320
5454
|
};
|
|
6321
5455
|
|
|
6322
5456
|
// src/servers/Official/index.ts
|
|
6323
|
-
var
|
|
5457
|
+
var import_koishi15 = require("koishi");
|
|
6324
5458
|
|
|
6325
5459
|
// src/servers/Official/services/sdvx-service.ts
|
|
6326
5460
|
var SDVXService3 = class _SDVXService {
|
|
@@ -6329,12 +5463,12 @@ var SDVXService3 = class _SDVXService {
|
|
|
6329
5463
|
}
|
|
6330
5464
|
static instance;
|
|
6331
5465
|
logger;
|
|
6332
|
-
constructor(
|
|
6333
|
-
this.logger =
|
|
5466
|
+
constructor(logger5) {
|
|
5467
|
+
this.logger = logger5;
|
|
6334
5468
|
}
|
|
6335
|
-
static getInstance(
|
|
5469
|
+
static getInstance(logger5) {
|
|
6336
5470
|
if (!_SDVXService.instance) {
|
|
6337
|
-
_SDVXService.instance = new _SDVXService(
|
|
5471
|
+
_SDVXService.instance = new _SDVXService(logger5);
|
|
6338
5472
|
}
|
|
6339
5473
|
return _SDVXService.instance;
|
|
6340
5474
|
}
|
|
@@ -6457,7 +5591,7 @@ var Official = class {
|
|
|
6457
5591
|
name = "official";
|
|
6458
5592
|
supportedGames = ["sdvx"];
|
|
6459
5593
|
gameServices = {};
|
|
6460
|
-
logger = new
|
|
5594
|
+
logger = new import_koishi15.Logger("Noah-Official");
|
|
6461
5595
|
/**
|
|
6462
5596
|
* 初始化SDVX游戏服务实例
|
|
6463
5597
|
*/
|
|
@@ -6501,51 +5635,239 @@ var ServerFactory = class {
|
|
|
6501
5635
|
};
|
|
6502
5636
|
|
|
6503
5637
|
// src/servers/index.ts
|
|
6504
|
-
var
|
|
5638
|
+
var name9 = "Noah-Server";
|
|
5639
|
+
var inject2 = ["globalConfig"];
|
|
6505
5640
|
var ServerManager = class _ServerManager {
|
|
6506
5641
|
static {
|
|
6507
5642
|
__name(this, "ServerManager");
|
|
6508
5643
|
}
|
|
6509
|
-
static instance;
|
|
6510
|
-
factory;
|
|
6511
|
-
constructor() {
|
|
6512
|
-
this.factory = new ServerFactory();
|
|
5644
|
+
static instance;
|
|
5645
|
+
factory;
|
|
5646
|
+
constructor() {
|
|
5647
|
+
this.factory = new ServerFactory();
|
|
5648
|
+
}
|
|
5649
|
+
/**
|
|
5650
|
+
* 获取服务器管理器实例
|
|
5651
|
+
* @returns 服务器管理器的唯一实例
|
|
5652
|
+
*/
|
|
5653
|
+
static getInstance() {
|
|
5654
|
+
if (!_ServerManager.instance) {
|
|
5655
|
+
_ServerManager.instance = new _ServerManager();
|
|
5656
|
+
}
|
|
5657
|
+
return _ServerManager.instance;
|
|
5658
|
+
}
|
|
5659
|
+
/**
|
|
5660
|
+
* 根据服务器类型获取对应的服务器实例
|
|
5661
|
+
* @param serverType - 服务器类型
|
|
5662
|
+
* @returns 对应的服务器实例
|
|
5663
|
+
*/
|
|
5664
|
+
getServer(serverType) {
|
|
5665
|
+
return this.factory.getServer(serverType);
|
|
5666
|
+
}
|
|
5667
|
+
/**
|
|
5668
|
+
* 根据服务器类型和游戏类型获取对应的游戏服务实例
|
|
5669
|
+
* @param serverType - 服务器类型
|
|
5670
|
+
* @param gameType - 游戏类型
|
|
5671
|
+
* @returns 对应的游戏服务实例
|
|
5672
|
+
*/
|
|
5673
|
+
getGameService(serverType, gameType) {
|
|
5674
|
+
const server2 = this.factory.getServer(serverType);
|
|
5675
|
+
return server2.gameServices[gameType];
|
|
5676
|
+
}
|
|
5677
|
+
};
|
|
5678
|
+
function apply9(ctx, config) {
|
|
5679
|
+
}
|
|
5680
|
+
__name(apply9, "apply");
|
|
5681
|
+
|
|
5682
|
+
// src/games/sdvx/services/score-service.ts
|
|
5683
|
+
var ScoreService = class _ScoreService {
|
|
5684
|
+
static {
|
|
5685
|
+
__name(this, "ScoreService");
|
|
5686
|
+
}
|
|
5687
|
+
static instance;
|
|
5688
|
+
constructor() {
|
|
5689
|
+
}
|
|
5690
|
+
/**
|
|
5691
|
+
* 获取 ScoreService 实例
|
|
5692
|
+
* @returns ScoreService 实例
|
|
5693
|
+
*/
|
|
5694
|
+
static getInstance() {
|
|
5695
|
+
if (!_ScoreService.instance) {
|
|
5696
|
+
_ScoreService.instance = new _ScoreService();
|
|
5697
|
+
}
|
|
5698
|
+
return _ScoreService.instance;
|
|
5699
|
+
}
|
|
5700
|
+
/**
|
|
5701
|
+
* 按 Volforce 降序排序分数
|
|
5702
|
+
* @param scores - 待排序的 SDVX 分数数组
|
|
5703
|
+
* @returns 按 Volforce 降序排序的新数组
|
|
5704
|
+
*/
|
|
5705
|
+
sortByVolforce(scores) {
|
|
5706
|
+
return [...scores].sort((a, b) => b.extra.volforce - a.extra.volforce);
|
|
5707
|
+
}
|
|
5708
|
+
/**
|
|
5709
|
+
* 获取前 50 名分数(包括并列)
|
|
5710
|
+
* @param scores - 待处理的 SDVX 分数数组
|
|
5711
|
+
* @returns 包含最高 Volforce 值的分数数组(如果存在并列,可能超过 50 个)
|
|
5712
|
+
*/
|
|
5713
|
+
getBest50(scores) {
|
|
5714
|
+
const sortedScores = this.sortByVolforce(scores);
|
|
5715
|
+
if (sortedScores.length <= 50) {
|
|
5716
|
+
return sortedScores;
|
|
5717
|
+
}
|
|
5718
|
+
const fiftiethVF = sortedScores[49].extra.volforce;
|
|
5719
|
+
return sortedScores.filter((score) => score.extra.volforce >= fiftiethVF);
|
|
6513
5720
|
}
|
|
6514
5721
|
/**
|
|
6515
|
-
*
|
|
6516
|
-
* @
|
|
5722
|
+
* 过滤 SDVXScore 数组
|
|
5723
|
+
* @param scores - 待过滤的分数数组
|
|
5724
|
+
* @param options - 过滤选项
|
|
5725
|
+
* @returns 过滤后的分数数组
|
|
6517
5726
|
*/
|
|
6518
|
-
|
|
6519
|
-
|
|
6520
|
-
|
|
5727
|
+
filterScores(scores, options) {
|
|
5728
|
+
let filtered = scores;
|
|
5729
|
+
if (options.grade) {
|
|
5730
|
+
const grades = Array.isArray(options.grade) ? options.grade : [options.grade];
|
|
5731
|
+
filtered = filtered.filter((score) => grades.includes(score.music.score_grade));
|
|
6521
5732
|
}
|
|
6522
|
-
|
|
5733
|
+
if (options.clearType) {
|
|
5734
|
+
const clearTypes = Array.isArray(options.clearType) ? options.clearType : [options.clearType];
|
|
5735
|
+
filtered = filtered.filter((score) => clearTypes.includes(score.music.clear_type));
|
|
5736
|
+
}
|
|
5737
|
+
if (options.musicDiff !== void 0) {
|
|
5738
|
+
const diffs = Array.isArray(options.musicDiff) ? options.musicDiff : [options.musicDiff];
|
|
5739
|
+
filtered = filtered.filter((score) => diffs.includes(score.music.music_diff));
|
|
5740
|
+
}
|
|
5741
|
+
if (options.radarFeature) {
|
|
5742
|
+
const featureKeys = Array.isArray(options.radarFeature) ? options.radarFeature : [options.radarFeature];
|
|
5743
|
+
filtered = filtered.filter((score) => {
|
|
5744
|
+
if (!score.difficulty_data) return false;
|
|
5745
|
+
const radar2 = score.difficulty_data.radar;
|
|
5746
|
+
let maxKey = "notes";
|
|
5747
|
+
let maxValue = radar2["notes"];
|
|
5748
|
+
for (const key of Object.keys(radar2)) {
|
|
5749
|
+
if (radar2[key] > maxValue) {
|
|
5750
|
+
maxKey = key;
|
|
5751
|
+
maxValue = radar2[key];
|
|
5752
|
+
}
|
|
5753
|
+
}
|
|
5754
|
+
return featureKeys.includes(maxKey);
|
|
5755
|
+
});
|
|
5756
|
+
}
|
|
5757
|
+
return filtered;
|
|
6523
5758
|
}
|
|
6524
5759
|
/**
|
|
6525
|
-
*
|
|
6526
|
-
* @param serverType - 服务器类型
|
|
6527
|
-
* @returns 对应的服务器实例
|
|
5760
|
+
* 计算单曲在某个雷达分类下的贡献值 F
|
|
6528
5761
|
*/
|
|
6529
|
-
|
|
6530
|
-
|
|
5762
|
+
calcSongRadarContribution(score, category) {
|
|
5763
|
+
const { difficulty_data } = score;
|
|
5764
|
+
if (!difficulty_data?.radar || !difficulty_data.max_exscore) return 0;
|
|
5765
|
+
const L = Math.floor(difficulty_data.difnum);
|
|
5766
|
+
const R = difficulty_data.radar[category];
|
|
5767
|
+
const S = score.music.score;
|
|
5768
|
+
const E = score.music.exscore;
|
|
5769
|
+
const Emax = difficulty_data.max_exscore;
|
|
5770
|
+
const P = Math.min(2 * (L + 31), 100);
|
|
5771
|
+
const B = Math.floor(3 * S * R / (4 * 1e7));
|
|
5772
|
+
const H = Math.floor((20 - L) / 2);
|
|
5773
|
+
const G = Math.max(1e3 + 2 * 2 ** H * (E - Emax), 0);
|
|
5774
|
+
const X = Math.floor(R * G / 4e3);
|
|
5775
|
+
return P * (B + X);
|
|
6531
5776
|
}
|
|
6532
5777
|
/**
|
|
6533
|
-
*
|
|
6534
|
-
*
|
|
6535
|
-
* @param gameType - 游戏类型
|
|
6536
|
-
* @returns 对应的游戏服务实例
|
|
5778
|
+
* 计算玩家六维雷达(显示值,如 200.00)
|
|
5779
|
+
* 算法:每个分类取每首歌最高贡献的难度,取 top50 求和,再除以 400000
|
|
6537
5780
|
*/
|
|
6538
|
-
|
|
6539
|
-
const
|
|
6540
|
-
|
|
5781
|
+
getPlayerRadarByAllScore(scores) {
|
|
5782
|
+
const categories = ["notes", "peak", "tsumami", "tricky", "hand_trip", "one_hand"];
|
|
5783
|
+
const radar2 = { notes: 0, peak: 0, tsumami: 0, tricky: 0, hand_trip: 0, one_hand: 0 };
|
|
5784
|
+
for (const category of categories) {
|
|
5785
|
+
const bestPerSong = /* @__PURE__ */ new Map();
|
|
5786
|
+
for (const score of scores) {
|
|
5787
|
+
const F = this.calcSongRadarContribution(score, category);
|
|
5788
|
+
if (F <= 0) continue;
|
|
5789
|
+
const musicId = score.music.music_id;
|
|
5790
|
+
const existing = bestPerSong.get(musicId) ?? 0;
|
|
5791
|
+
if (F > existing) {
|
|
5792
|
+
bestPerSong.set(musicId, F);
|
|
5793
|
+
}
|
|
5794
|
+
}
|
|
5795
|
+
const top50 = [...bestPerSong.values()].sort((a, b) => b - a).slice(0, 50);
|
|
5796
|
+
const T = top50.reduce((sum, v) => sum + v, 0);
|
|
5797
|
+
radar2[category] = Math.floor(T / 40) / 100;
|
|
5798
|
+
}
|
|
5799
|
+
return radar2;
|
|
6541
5800
|
}
|
|
6542
5801
|
};
|
|
6543
|
-
|
|
5802
|
+
|
|
5803
|
+
// src/games/sdvx/commands/radar.ts
|
|
5804
|
+
function radar(ctx, config, logger5) {
|
|
5805
|
+
ctx.command("sdvx.radar").userFields(["defaultCardId", "defaultServerId", "id"]).channelFields(["defaultServerId", "id"]).action(async ({ session }) => {
|
|
5806
|
+
const atGuild = session.guildId != null;
|
|
5807
|
+
const cardService = new CardService(ctx);
|
|
5808
|
+
const serverService = new ServerService(ctx);
|
|
5809
|
+
const userCards = await cardService.getCardsByUid(session.user.id);
|
|
5810
|
+
if (userCards.length === 0) return session.text(".card-not-found");
|
|
5811
|
+
const serverRes = await serverService.getSelectableServers(
|
|
5812
|
+
session.user.id,
|
|
5813
|
+
atGuild ? session.channel.id : null
|
|
5814
|
+
);
|
|
5815
|
+
if (serverRes.length === 0) return session.text(".server-not-found");
|
|
5816
|
+
const defaultCard = await cardService.getDefaultCardByUid(session.user.id);
|
|
5817
|
+
if (!defaultCard) return session.text(".card-not-found");
|
|
5818
|
+
const card2 = await cardService.getCardByCode(defaultCard.code);
|
|
5819
|
+
let server2;
|
|
5820
|
+
if (card2.defaultServerId != void 0 && card2.defaultServerId != 0)
|
|
5821
|
+
server2 = await serverService.getServerById(card2.defaultServerId);
|
|
5822
|
+
else {
|
|
5823
|
+
const channelDefaultServer = atGuild ? await serverService.getDefaultServerByCid(
|
|
5824
|
+
session.platform,
|
|
5825
|
+
session.channel.id
|
|
5826
|
+
) : null;
|
|
5827
|
+
if (channelDefaultServer) server2 = channelDefaultServer;
|
|
5828
|
+
else {
|
|
5829
|
+
const userDefaultServer = await serverService.getDefaultServerByUid(
|
|
5830
|
+
session.user.id
|
|
5831
|
+
);
|
|
5832
|
+
if (userDefaultServer) server2 = userDefaultServer;
|
|
5833
|
+
else return session.text(".server-not-found");
|
|
5834
|
+
}
|
|
5835
|
+
}
|
|
5836
|
+
const serverManager = ServerManager.getInstance();
|
|
5837
|
+
const sdvxService = serverManager.getGameService(server2.type, "sdvx");
|
|
5838
|
+
const scoreService = ScoreService.getInstance();
|
|
5839
|
+
try {
|
|
5840
|
+
const scoreList = await sdvxService.getAllScore(
|
|
5841
|
+
ctx,
|
|
5842
|
+
server2.baseUrl,
|
|
5843
|
+
card2.code,
|
|
5844
|
+
config
|
|
5845
|
+
);
|
|
5846
|
+
if (scoreList.length === 0) {
|
|
5847
|
+
return session.text(".no-scores-found");
|
|
5848
|
+
}
|
|
5849
|
+
const radarResult = scoreService.getPlayerRadarByAllScore(scoreList);
|
|
5850
|
+
const playerName = await sdvxService.getUserName(ctx, server2.baseUrl, card2.code);
|
|
5851
|
+
return session.text(".result", {
|
|
5852
|
+
name: playerName,
|
|
5853
|
+
notes: radarResult.notes.toFixed(2),
|
|
5854
|
+
peak: radarResult.peak.toFixed(2),
|
|
5855
|
+
tsumami: radarResult.tsumami.toFixed(2),
|
|
5856
|
+
tricky: radarResult.tricky.toFixed(2),
|
|
5857
|
+
hand_trip: radarResult.hand_trip.toFixed(2),
|
|
5858
|
+
one_hand: radarResult.one_hand.toFixed(2)
|
|
5859
|
+
});
|
|
5860
|
+
} catch (error) {
|
|
5861
|
+
logger5.warn(error);
|
|
5862
|
+
return session.text(".error");
|
|
5863
|
+
}
|
|
5864
|
+
});
|
|
6544
5865
|
}
|
|
6545
|
-
__name(
|
|
5866
|
+
__name(radar, "radar");
|
|
6546
5867
|
|
|
6547
5868
|
// src/games/sdvx/commands/recent.ts
|
|
6548
|
-
|
|
5869
|
+
var import_koishi16 = require("koishi");
|
|
5870
|
+
function recent(ctx, config, logger5) {
|
|
6549
5871
|
ctx.command("sdvx.recent [count:number]").alias("sdvx.r").userFields(["defaultCardId", "defaultServerId", "id"]).channelFields(["defaultServerId", "id"]).option("lossless", "-l").option("card", "-c").action(async ({ session, options }, count) => {
|
|
6550
5872
|
const atGuild = session.guildId != null;
|
|
6551
5873
|
if (!count) count = 1;
|
|
@@ -6573,7 +5895,7 @@ function recent(ctx, config, logger6) {
|
|
|
6573
5895
|
cardListMsg += `<p>${i + 1}. ${userCards[i].name}</p>`;
|
|
6574
5896
|
}
|
|
6575
5897
|
}
|
|
6576
|
-
const msg =
|
|
5898
|
+
const msg = import_koishi16.h.unescape(session.text(".menu-select", { card_list: cardListMsg })).replace("< 默认卡片", "< 默认卡片");
|
|
6577
5899
|
await session.send(msg);
|
|
6578
5900
|
const select = await session.prompt();
|
|
6579
5901
|
if (!select) return session.text("commands.timeout");
|
|
@@ -6630,11 +5952,11 @@ function recent(ctx, config, logger6) {
|
|
|
6630
5952
|
}
|
|
6631
5953
|
);
|
|
6632
5954
|
if (options.lossless) {
|
|
6633
|
-
return
|
|
5955
|
+
return import_koishi16.h.image(imageBuffer, "image/png");
|
|
6634
5956
|
}
|
|
6635
|
-
return
|
|
5957
|
+
return import_koishi16.h.image(imageBuffer, "image/jpg");
|
|
6636
5958
|
} catch (error) {
|
|
6637
|
-
|
|
5959
|
+
logger5.warn(error);
|
|
6638
5960
|
return session.text(".error");
|
|
6639
5961
|
}
|
|
6640
5962
|
});
|
|
@@ -6642,7 +5964,7 @@ function recent(ctx, config, logger6) {
|
|
|
6642
5964
|
__name(recent, "recent");
|
|
6643
5965
|
|
|
6644
5966
|
// src/games/sdvx/commands/sync.ts
|
|
6645
|
-
function sync(ctx, config,
|
|
5967
|
+
function sync(ctx, config, logger5) {
|
|
6646
5968
|
ctx.command("sdvx.sync").userFields(["id"]).channelFields(["id"]).action(async ({ session }) => {
|
|
6647
5969
|
const serverService = new ServerService(ctx);
|
|
6648
5970
|
const cardService = new CardService(ctx);
|
|
@@ -6717,7 +6039,7 @@ function sync(ctx, config, logger6) {
|
|
|
6717
6039
|
const maoSdvxService = serverManager.getGameService("mao", "sdvx");
|
|
6718
6040
|
const maoVerifyUrl = "https://maomani.cn";
|
|
6719
6041
|
if (!maoSdvxService || typeof maoSdvxService.verifyPin !== "function") {
|
|
6720
|
-
|
|
6042
|
+
logger5.warn("Mao SDVX service does not support PIN verification");
|
|
6721
6043
|
return session.text(".pin-verify-error");
|
|
6722
6044
|
}
|
|
6723
6045
|
const existingPin = await ctx.database.get("sdvx_pin_verified", {
|
|
@@ -6738,7 +6060,7 @@ function sync(ctx, config, logger6) {
|
|
|
6738
6060
|
pinVerified = true;
|
|
6739
6061
|
}
|
|
6740
6062
|
} catch (error) {
|
|
6741
|
-
|
|
6063
|
+
logger5.warn(error);
|
|
6742
6064
|
}
|
|
6743
6065
|
}
|
|
6744
6066
|
if (!pinVerified) {
|
|
@@ -6774,7 +6096,7 @@ function sync(ctx, config, logger6) {
|
|
|
6774
6096
|
})
|
|
6775
6097
|
);
|
|
6776
6098
|
} catch (error) {
|
|
6777
|
-
|
|
6099
|
+
logger5.warn(error);
|
|
6778
6100
|
await session.send(session.text(".pin-verify-error"));
|
|
6779
6101
|
}
|
|
6780
6102
|
}
|
|
@@ -6796,7 +6118,7 @@ function sync(ctx, config, logger6) {
|
|
|
6796
6118
|
config
|
|
6797
6119
|
);
|
|
6798
6120
|
} catch (error) {
|
|
6799
|
-
|
|
6121
|
+
logger5.warn(error);
|
|
6800
6122
|
return session.text(".fetch-error");
|
|
6801
6123
|
}
|
|
6802
6124
|
if (!scoreList || scoreList.length === 0) {
|
|
@@ -6819,11 +6141,11 @@ function sync(ctx, config, logger6) {
|
|
|
6819
6141
|
syncPayload
|
|
6820
6142
|
);
|
|
6821
6143
|
if (!ok) {
|
|
6822
|
-
|
|
6144
|
+
logger5.warn("Mao SDVX uploadScore returned falsy result");
|
|
6823
6145
|
return session.text(".sync-failed");
|
|
6824
6146
|
}
|
|
6825
6147
|
} catch (error) {
|
|
6826
|
-
|
|
6148
|
+
logger5.warn(error);
|
|
6827
6149
|
return session.text(".sync-error");
|
|
6828
6150
|
}
|
|
6829
6151
|
return session.text(".selected-summary", {
|
|
@@ -6889,86 +6211,7 @@ __name(buildSyncPayload, "buildSyncPayload");
|
|
|
6889
6211
|
|
|
6890
6212
|
// src/games/sdvx/commands/vf.ts
|
|
6891
6213
|
var fs4 = __toESM(require("fs"), 1);
|
|
6892
|
-
var
|
|
6893
|
-
|
|
6894
|
-
// src/games/sdvx/services/score-service.ts
|
|
6895
|
-
var ScoreService = class _ScoreService {
|
|
6896
|
-
static {
|
|
6897
|
-
__name(this, "ScoreService");
|
|
6898
|
-
}
|
|
6899
|
-
static instance;
|
|
6900
|
-
constructor() {
|
|
6901
|
-
}
|
|
6902
|
-
/**
|
|
6903
|
-
* 获取 ScoreService 实例
|
|
6904
|
-
* @returns ScoreService 实例
|
|
6905
|
-
*/
|
|
6906
|
-
static getInstance() {
|
|
6907
|
-
if (!_ScoreService.instance) {
|
|
6908
|
-
_ScoreService.instance = new _ScoreService();
|
|
6909
|
-
}
|
|
6910
|
-
return _ScoreService.instance;
|
|
6911
|
-
}
|
|
6912
|
-
/**
|
|
6913
|
-
* 按 Volforce 降序排序分数
|
|
6914
|
-
* @param scores - 待排序的 SDVX 分数数组
|
|
6915
|
-
* @returns 按 Volforce 降序排序的新数组
|
|
6916
|
-
*/
|
|
6917
|
-
sortByVolforce(scores) {
|
|
6918
|
-
return [...scores].sort((a, b) => b.extra.volforce - a.extra.volforce);
|
|
6919
|
-
}
|
|
6920
|
-
/**
|
|
6921
|
-
* 获取前 50 名分数(包括并列)
|
|
6922
|
-
* @param scores - 待处理的 SDVX 分数数组
|
|
6923
|
-
* @returns 包含最高 Volforce 值的分数数组(如果存在并列,可能超过 50 个)
|
|
6924
|
-
*/
|
|
6925
|
-
getBest50(scores) {
|
|
6926
|
-
const sortedScores = this.sortByVolforce(scores);
|
|
6927
|
-
if (sortedScores.length <= 50) {
|
|
6928
|
-
return sortedScores;
|
|
6929
|
-
}
|
|
6930
|
-
const fiftiethVF = sortedScores[49].extra.volforce;
|
|
6931
|
-
return sortedScores.filter((score) => score.extra.volforce >= fiftiethVF);
|
|
6932
|
-
}
|
|
6933
|
-
/**
|
|
6934
|
-
* 过滤 SDVXScore 数组
|
|
6935
|
-
* @param scores - 待过滤的分数数组
|
|
6936
|
-
* @param options - 过滤选项
|
|
6937
|
-
* @returns 过滤后的分数数组
|
|
6938
|
-
*/
|
|
6939
|
-
filterScores(scores, options) {
|
|
6940
|
-
let filtered = scores;
|
|
6941
|
-
if (options.grade) {
|
|
6942
|
-
const grades = Array.isArray(options.grade) ? options.grade : [options.grade];
|
|
6943
|
-
filtered = filtered.filter((score) => grades.includes(score.music.score_grade));
|
|
6944
|
-
}
|
|
6945
|
-
if (options.clearType) {
|
|
6946
|
-
const clearTypes = Array.isArray(options.clearType) ? options.clearType : [options.clearType];
|
|
6947
|
-
filtered = filtered.filter((score) => clearTypes.includes(score.music.clear_type));
|
|
6948
|
-
}
|
|
6949
|
-
if (options.musicDiff !== void 0) {
|
|
6950
|
-
const diffs = Array.isArray(options.musicDiff) ? options.musicDiff : [options.musicDiff];
|
|
6951
|
-
filtered = filtered.filter((score) => diffs.includes(score.music.music_diff));
|
|
6952
|
-
}
|
|
6953
|
-
if (options.radarFeature) {
|
|
6954
|
-
const featureKeys = Array.isArray(options.radarFeature) ? options.radarFeature : [options.radarFeature];
|
|
6955
|
-
filtered = filtered.filter((score) => {
|
|
6956
|
-
if (!score.difficulty_data) return false;
|
|
6957
|
-
const radar = score.difficulty_data.radar;
|
|
6958
|
-
let maxKey = "notes";
|
|
6959
|
-
let maxValue = radar["notes"];
|
|
6960
|
-
for (const key of Object.keys(radar)) {
|
|
6961
|
-
if (radar[key] > maxValue) {
|
|
6962
|
-
maxKey = key;
|
|
6963
|
-
maxValue = radar[key];
|
|
6964
|
-
}
|
|
6965
|
-
}
|
|
6966
|
-
return featureKeys.includes(maxKey);
|
|
6967
|
-
});
|
|
6968
|
-
}
|
|
6969
|
-
return filtered;
|
|
6970
|
-
}
|
|
6971
|
-
};
|
|
6214
|
+
var import_koishi17 = require("koishi");
|
|
6972
6215
|
|
|
6973
6216
|
// src/games/sdvx/utils/filter.ts
|
|
6974
6217
|
function parseFilterQuery(query) {
|
|
@@ -7021,7 +6264,7 @@ function parseFilterQuery(query) {
|
|
|
7021
6264
|
__name(parseFilterQuery, "parseFilterQuery");
|
|
7022
6265
|
|
|
7023
6266
|
// src/games/sdvx/commands/vf.ts
|
|
7024
|
-
function vf(ctx, config,
|
|
6267
|
+
function vf(ctx, config, logger5) {
|
|
7025
6268
|
ctx.command("vf").userFields(["defaultCardId", "defaultServerId", "id"]).channelFields(["defaultServerId", "id"]).option("lossless", "-l").option("card", "-c").option("filter", "-f <query:text>").action(async ({ session, options }) => {
|
|
7026
6269
|
const atGuild = session.guildId != null;
|
|
7027
6270
|
const cardService = new CardService(ctx);
|
|
@@ -7048,7 +6291,7 @@ function vf(ctx, config, logger6) {
|
|
|
7048
6291
|
cardListMsg += `<p>${i + 1}. ${userCards[i].name}</p>`;
|
|
7049
6292
|
}
|
|
7050
6293
|
}
|
|
7051
|
-
const msg =
|
|
6294
|
+
const msg = import_koishi17.h.unescape(session.text(".menu-select", { card_list: cardListMsg })).replace("< 默认卡片", "< 默认卡片");
|
|
7052
6295
|
await session.send(msg);
|
|
7053
6296
|
const select = await session.prompt();
|
|
7054
6297
|
if (!select) return session.text("commands.timeout");
|
|
@@ -7111,9 +6354,9 @@ function vf(ctx, config, logger6) {
|
|
|
7111
6354
|
}
|
|
7112
6355
|
);
|
|
7113
6356
|
if (options.lossless) {
|
|
7114
|
-
session.send(
|
|
6357
|
+
session.send(import_koishi17.h.file(imageBuffer, "image/png"));
|
|
7115
6358
|
} else {
|
|
7116
|
-
session.send(
|
|
6359
|
+
session.send(import_koishi17.h.image(imageBuffer, "image/jpg"));
|
|
7117
6360
|
}
|
|
7118
6361
|
const sortedScores = [...best50ScoreList].sort((a, b) => b.extra.volforce - a.extra.volforce).slice(0, 50);
|
|
7119
6362
|
const total = sortedScores.reduce((sum, score) => sum + score.extra.volforce, 0);
|
|
@@ -7142,18 +6385,18 @@ function vf(ctx, config, logger6) {
|
|
|
7142
6385
|
);
|
|
7143
6386
|
if (fs4.existsSync(audioPath)) {
|
|
7144
6387
|
const audioBuffer = fs4.readFileSync(audioPath);
|
|
7145
|
-
session.send(
|
|
6388
|
+
session.send(import_koishi17.h.audio(audioBuffer, "audio/mpeg"));
|
|
7146
6389
|
}
|
|
7147
6390
|
if (fs4.existsSync(imagePath)) {
|
|
7148
6391
|
const imageBuffer2 = fs4.readFileSync(imagePath);
|
|
7149
|
-
session.send(
|
|
6392
|
+
session.send(import_koishi17.h.image(imageBuffer2, "image/png"));
|
|
7150
6393
|
}
|
|
7151
6394
|
} catch (error) {
|
|
7152
|
-
|
|
6395
|
+
logger5.warn(`Failed to send celebration files: ${error.message}`);
|
|
7153
6396
|
}
|
|
7154
6397
|
}
|
|
7155
6398
|
} catch (error) {
|
|
7156
|
-
|
|
6399
|
+
logger5.warn(error);
|
|
7157
6400
|
return session.text(".error");
|
|
7158
6401
|
}
|
|
7159
6402
|
return;
|
|
@@ -7162,25 +6405,26 @@ function vf(ctx, config, logger6) {
|
|
|
7162
6405
|
__name(vf, "vf");
|
|
7163
6406
|
|
|
7164
6407
|
// src/games/sdvx/command.ts
|
|
7165
|
-
var
|
|
7166
|
-
function
|
|
6408
|
+
var name10 = "command";
|
|
6409
|
+
function apply10(ctx, config) {
|
|
7167
6410
|
ctx.command("sdvx").alias("s");
|
|
7168
|
-
vf(ctx, config,
|
|
7169
|
-
recent(ctx, config,
|
|
7170
|
-
chart(ctx, config,
|
|
7171
|
-
calculate(ctx, config,
|
|
7172
|
-
sync(ctx, config,
|
|
6411
|
+
vf(ctx, config, logger4);
|
|
6412
|
+
recent(ctx, config, logger4);
|
|
6413
|
+
chart(ctx, config, logger4);
|
|
6414
|
+
calculate(ctx, config, logger4);
|
|
6415
|
+
sync(ctx, config, logger4);
|
|
6416
|
+
radar(ctx, config, logger4);
|
|
7173
6417
|
}
|
|
7174
|
-
__name(
|
|
6418
|
+
__name(apply10, "apply");
|
|
7175
6419
|
|
|
7176
6420
|
// src/games/sdvx/database.ts
|
|
7177
|
-
var
|
|
7178
|
-
__export(
|
|
7179
|
-
apply: () =>
|
|
7180
|
-
name: () =>
|
|
6421
|
+
var database_exports2 = {};
|
|
6422
|
+
__export(database_exports2, {
|
|
6423
|
+
apply: () => apply11,
|
|
6424
|
+
name: () => name11
|
|
7181
6425
|
});
|
|
7182
|
-
var
|
|
7183
|
-
function
|
|
6426
|
+
var name11 = "database";
|
|
6427
|
+
function apply11(ctx) {
|
|
7184
6428
|
ctx.model.extend(
|
|
7185
6429
|
"sdvx_pin_verified",
|
|
7186
6430
|
{
|
|
@@ -7195,135 +6439,147 @@ function apply13(ctx) {
|
|
|
7195
6439
|
}
|
|
7196
6440
|
);
|
|
7197
6441
|
}
|
|
7198
|
-
__name(
|
|
6442
|
+
__name(apply11, "apply");
|
|
7199
6443
|
|
|
7200
6444
|
// src/games/sdvx/event.ts
|
|
7201
6445
|
var event_exports2 = {};
|
|
7202
6446
|
__export(event_exports2, {
|
|
7203
|
-
apply: () =>
|
|
7204
|
-
name: () =>
|
|
6447
|
+
apply: () => apply12,
|
|
6448
|
+
name: () => name12
|
|
7205
6449
|
});
|
|
7206
|
-
var
|
|
7207
|
-
function
|
|
6450
|
+
var name12 = "event";
|
|
6451
|
+
function apply12(ctx) {
|
|
7208
6452
|
}
|
|
7209
|
-
__name(
|
|
6453
|
+
__name(apply12, "apply");
|
|
7210
6454
|
|
|
7211
6455
|
// src/games/sdvx/locales/en-US.yml
|
|
7212
|
-
var
|
|
6456
|
+
var en_US_default4 = { _config: { $desc: "SDVX Module Settings", default_model: "<p>Default model value (e.g. `2024110700`)</p>", sdvx_data_url: "<p>The URL of the SDVX data service</p>", sdvx_search_url: "<p>The URL of the SDVX search service</p>", official_support_url: "<p>The URL of the SDVX official support service</p>" }, commands: { vf: { description: "Show Noah help information", messages: { "card-not-found": "<p>You haven't bound a card yet, go bind a card first~</p>", "server-not-found": "<p>No available servers, add one yourself~</p>", "no-scores-found": "<p>No scores found, please check your data.</p>", error: "<p>An error occurred(っ °Д °;)っ</p>", drawing: "<p>Noah is drawing {name} [{difstr}], please wait patiently~</p>", "menu-select": "<p>Please select the card you want to use:</p>\n{card_list}\n<p>q. Exit</p>", "invalid-select": "<p>No such option!</p>", quit: "<p>Exited~</p>" } }, sdvx: { recent: { description: "Show recent scores", messages: { "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>" } } } } };
|
|
7213
6457
|
|
|
7214
6458
|
// src/games/sdvx/locales/zh-CN.yml
|
|
7215
|
-
var
|
|
6459
|
+
var zh_CN_default4 = { _config: { $desc: "SDVX 模块设置", default_model: "<p>默认的 model 值(如 `2024110700`)</p>", sdvx_data_url: "<p>SDVX 数据服务的 URL</p>", sdvx_search_url: "<p>SDVX 搜索服务的 URL</p>", official_support_url: "<p>SDVX 官方支持服务的 URL</p>" }, commands: { vf: { description: "查询 SDVX VOLFORCE", messages: { "card-not-found": "<p>你还没绑卡,去绑个卡再来吧~</p>", "server-not-found": "<p>没有可用的服务器哦,自己添加一个吧~</p>", "no-scores-found": "<p>没有找到你的分数数据哦~</p>", error: "<p>Noah 遇到了错误(っ °Д °;)っ</p>", drawing: "<p>Noah 正在画啦,请耐心等待~</p>", "menu-select": "<p>请选择你要使用的卡片:</p>\n{card_list}\n<p>q. 退出</p>", "invalid-select": "<p>没有该选项!</p>", quit: "<p>已退出~</p>" } }, sdvx: { recent: { description: "查询最近分数", messages: { "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>" } } } } };
|
|
7216
6460
|
|
|
7217
6461
|
// src/games/sdvx/index.ts
|
|
7218
|
-
var
|
|
7219
|
-
var inject3 = ["database"];
|
|
7220
|
-
var
|
|
7221
|
-
async function
|
|
6462
|
+
var name13 = "Noah-SDVX";
|
|
6463
|
+
var inject3 = ["database", "globalConfig"];
|
|
6464
|
+
var logger4 = new import_koishi18.Logger("Noah-SDVX");
|
|
6465
|
+
async function apply13(ctx, config) {
|
|
7222
6466
|
;
|
|
7223
6467
|
[
|
|
7224
|
-
["en-US",
|
|
7225
|
-
["zh-CN",
|
|
6468
|
+
["en-US", en_US_default4],
|
|
6469
|
+
["zh-CN", zh_CN_default4]
|
|
7226
6470
|
].forEach(([lang, file]) => ctx.i18n.define(lang, file));
|
|
7227
|
-
ctx.plugin(
|
|
6471
|
+
ctx.plugin(database_exports2, config.sdvx);
|
|
7228
6472
|
ctx.plugin(command_exports2, config.sdvx);
|
|
7229
6473
|
ctx.plugin(event_exports2, config.sdvx);
|
|
7230
6474
|
}
|
|
7231
|
-
__name(
|
|
6475
|
+
__name(apply13, "apply");
|
|
7232
6476
|
|
|
7233
6477
|
// src/config.ts
|
|
7234
|
-
var
|
|
6478
|
+
var import_koishi26 = require("koishi");
|
|
7235
6479
|
|
|
7236
6480
|
// src/asset/config.ts
|
|
7237
|
-
var
|
|
7238
|
-
var assetConfig =
|
|
7239
|
-
data_path:
|
|
7240
|
-
auto_update:
|
|
7241
|
-
update_url:
|
|
7242
|
-
update_interval:
|
|
6481
|
+
var import_koishi19 = require("koishi");
|
|
6482
|
+
var assetConfig = import_koishi19.Schema.object({
|
|
6483
|
+
data_path: import_koishi19.Schema.string().default("noah_assets"),
|
|
6484
|
+
auto_update: import_koishi19.Schema.boolean().default(true),
|
|
6485
|
+
update_url: import_koishi19.Schema.string().default("https://github.com/logthm/noah/releases/latest/download/"),
|
|
6486
|
+
update_interval: import_koishi19.Schema.number().default(24 * 60 * 60 * 1e3),
|
|
7243
6487
|
// 24 hours in milliseconds
|
|
7244
|
-
github_token:
|
|
6488
|
+
github_token: import_koishi19.Schema.string().description("GitHub token for accessing private repositories").role("secret")
|
|
7245
6489
|
}).i18n({
|
|
7246
6490
|
"en-US": en_US_default._config,
|
|
7247
6491
|
"zh-CN": zh_CN_default._config
|
|
7248
6492
|
});
|
|
7249
6493
|
|
|
7250
6494
|
// src/core/config.ts
|
|
7251
|
-
var
|
|
7252
|
-
var coreConfig =
|
|
7253
|
-
adminUsers:
|
|
7254
|
-
guildNameCards:
|
|
7255
|
-
maoServerUrl: import_koishi19.Schema.string().default("http://maomani.cn:573"),
|
|
7256
|
-
official_support_url: import_koishi19.Schema.string().default("https://noah.logthm.com")
|
|
6495
|
+
var import_koishi20 = require("koishi");
|
|
6496
|
+
var coreConfig = import_koishi20.Schema.object({
|
|
6497
|
+
adminUsers: import_koishi20.Schema.array(String).role("table"),
|
|
6498
|
+
guildNameCards: import_koishi20.Schema.array(String).role("table").default(["Noah | /help 获取食用指南"])
|
|
7257
6499
|
}).i18n({
|
|
7258
|
-
"en-US":
|
|
7259
|
-
"zh-CN":
|
|
6500
|
+
"en-US": en_US_default2._config,
|
|
6501
|
+
"zh-CN": zh_CN_default2._config
|
|
7260
6502
|
});
|
|
7261
6503
|
|
|
7262
6504
|
// src/fun/poke/config.ts
|
|
7263
|
-
var
|
|
7264
|
-
var pokeConfig =
|
|
7265
|
-
interval:
|
|
7266
|
-
warning:
|
|
7267
|
-
prompt:
|
|
7268
|
-
|
|
7269
|
-
content:
|
|
7270
|
-
weight:
|
|
6505
|
+
var import_koishi21 = require("koishi");
|
|
6506
|
+
var pokeConfig = import_koishi21.Schema.object({
|
|
6507
|
+
interval: import_koishi21.Schema.number().default(1e3).step(100),
|
|
6508
|
+
warning: import_koishi21.Schema.boolean().default(false),
|
|
6509
|
+
prompt: import_koishi21.Schema.array(
|
|
6510
|
+
import_koishi21.Schema.object({
|
|
6511
|
+
content: import_koishi21.Schema.string().required(),
|
|
6512
|
+
weight: import_koishi21.Schema.number().min(0).max(100).default(50)
|
|
7271
6513
|
})
|
|
7272
6514
|
).role("table"),
|
|
7273
|
-
messages:
|
|
7274
|
-
|
|
7275
|
-
content:
|
|
7276
|
-
weight:
|
|
6515
|
+
messages: import_koishi21.Schema.array(
|
|
6516
|
+
import_koishi21.Schema.object({
|
|
6517
|
+
content: import_koishi21.Schema.string().required(),
|
|
6518
|
+
weight: import_koishi21.Schema.number().min(0).max(100).default(50)
|
|
7277
6519
|
})
|
|
7278
6520
|
).role("table")
|
|
7279
6521
|
}).i18n({
|
|
7280
|
-
"en-US":
|
|
7281
|
-
"zh-CN":
|
|
6522
|
+
"en-US": en_US_default3._config,
|
|
6523
|
+
"zh-CN": zh_CN_default3._config
|
|
7282
6524
|
});
|
|
7283
6525
|
|
|
7284
6526
|
// src/games/general/config.ts
|
|
7285
|
-
var
|
|
6527
|
+
var import_koishi22 = require("koishi");
|
|
7286
6528
|
|
|
7287
6529
|
// src/games/general/locales/en-US.yml
|
|
7288
|
-
var
|
|
6530
|
+
var en_US_default5 = { _config: { $desc: "General Module Settings", barcode_api_url: "<p>The URL of the barcode API</p>" } };
|
|
7289
6531
|
|
|
7290
6532
|
// src/games/general/locales/zh-CN.yml
|
|
7291
|
-
var
|
|
6533
|
+
var zh_CN_default5 = { _config: { $desc: "通用工具模块设置", barcode_api_url: "<p>条形码 API 地址</p>" } };
|
|
7292
6534
|
|
|
7293
6535
|
// src/games/general/config.ts
|
|
7294
|
-
var generalConfig =
|
|
7295
|
-
barcode_api_url:
|
|
6536
|
+
var generalConfig = import_koishi22.Schema.object({
|
|
6537
|
+
barcode_api_url: import_koishi22.Schema.string().required()
|
|
7296
6538
|
}).i18n({
|
|
7297
|
-
"en-US":
|
|
7298
|
-
"zh-CN":
|
|
6539
|
+
"en-US": en_US_default5._config,
|
|
6540
|
+
"zh-CN": zh_CN_default5._config
|
|
7299
6541
|
});
|
|
7300
6542
|
|
|
7301
6543
|
// src/games/sdvx/config.ts
|
|
7302
|
-
var
|
|
7303
|
-
var sdvxConfig =
|
|
7304
|
-
default_model:
|
|
7305
|
-
sdvx_data_url:
|
|
7306
|
-
sdvx_search_url:
|
|
7307
|
-
official_support_url: import_koishi22.Schema.string().required()
|
|
6544
|
+
var import_koishi23 = require("koishi");
|
|
6545
|
+
var sdvxConfig = import_koishi23.Schema.object({
|
|
6546
|
+
default_model: import_koishi23.Schema.string().default("2025100700"),
|
|
6547
|
+
sdvx_data_url: import_koishi23.Schema.string().required(),
|
|
6548
|
+
sdvx_search_url: import_koishi23.Schema.string().required()
|
|
7308
6549
|
}).i18n({
|
|
7309
|
-
"en-US":
|
|
7310
|
-
"zh-CN":
|
|
6550
|
+
"en-US": en_US_default4._config,
|
|
6551
|
+
"zh-CN": zh_CN_default4._config
|
|
6552
|
+
});
|
|
6553
|
+
|
|
6554
|
+
// src/global/config.ts
|
|
6555
|
+
var import_koishi24 = require("koishi");
|
|
6556
|
+
var globalConfig = import_koishi24.Schema.object({
|
|
6557
|
+
official_support_url: import_koishi24.Schema.string().default("https://noah.logthm.com"),
|
|
6558
|
+
maoServerUrl: import_koishi24.Schema.string().default("http://maomani.cn:577"),
|
|
6559
|
+
maoApiKey: import_koishi24.Schema.string().role("secret").default("")
|
|
7311
6560
|
});
|
|
7312
6561
|
|
|
7313
6562
|
// src/slash/config.ts
|
|
7314
|
-
var
|
|
7315
|
-
|
|
7316
|
-
|
|
7317
|
-
|
|
7318
|
-
|
|
7319
|
-
|
|
6563
|
+
var import_koishi25 = require("koishi");
|
|
6564
|
+
|
|
6565
|
+
// src/slash/locales/en-US.yml
|
|
6566
|
+
var en_US_default6 = { _config: { $desc: "Slash Module Settings", test_guilds: "**Guilds To Sync**", auto_sync_on_start: "**Auto Sync on Connect**" } };
|
|
6567
|
+
|
|
6568
|
+
// src/slash/locales/zh-CN.yml
|
|
6569
|
+
var zh_CN_default6 = { _config: { $desc: "Slash 模块设置", test_guilds: "**默认同步 Guild 列表**", auto_sync_on_start: "**连接后自动同步**" } };
|
|
6570
|
+
|
|
6571
|
+
// src/slash/config.ts
|
|
6572
|
+
var slashConfig = import_koishi25.Schema.object({
|
|
6573
|
+
test_guilds: import_koishi25.Schema.array(String).default([]),
|
|
6574
|
+
auto_sync_on_start: import_koishi25.Schema.boolean().default(true)
|
|
7320
6575
|
}).i18n({
|
|
7321
|
-
"en-US":
|
|
7322
|
-
"zh-CN":
|
|
6576
|
+
"en-US": en_US_default6._config,
|
|
6577
|
+
"zh-CN": zh_CN_default6._config
|
|
7323
6578
|
});
|
|
7324
6579
|
|
|
7325
6580
|
// src/config.ts
|
|
7326
|
-
var Config =
|
|
6581
|
+
var Config = import_koishi26.Schema.object({
|
|
6582
|
+
global: globalConfig,
|
|
7327
6583
|
core: coreConfig,
|
|
7328
6584
|
sdvx: sdvxConfig,
|
|
7329
6585
|
poke: pokeConfig,
|
|
@@ -7333,10 +6589,11 @@ var Config = import_koishi24.Schema.object({
|
|
|
7333
6589
|
});
|
|
7334
6590
|
|
|
7335
6591
|
// src/index.ts
|
|
7336
|
-
var
|
|
6592
|
+
var name14 = "noah";
|
|
7337
6593
|
var inject4 = ["database", "skia"];
|
|
7338
|
-
async function
|
|
6594
|
+
async function apply14(ctx, config) {
|
|
7339
6595
|
initConstants(ctx);
|
|
6596
|
+
ctx.set("globalConfig", config.global);
|
|
7340
6597
|
ctx.plugin(core_exports, config);
|
|
7341
6598
|
ctx.plugin(general_exports, config);
|
|
7342
6599
|
ctx.plugin(sdvx_exports, config);
|
|
@@ -7344,9 +6601,8 @@ async function apply16(ctx, config) {
|
|
|
7344
6601
|
ctx.plugin(drawer_exports, config);
|
|
7345
6602
|
ctx.plugin(poke_exports, config);
|
|
7346
6603
|
ctx.plugin(asset_exports, config);
|
|
7347
|
-
ctx.plugin(slash_exports, config);
|
|
7348
6604
|
}
|
|
7349
|
-
__name(
|
|
6605
|
+
__name(apply14, "apply");
|
|
7350
6606
|
// Annotate the CommonJS export names for ESM import in node:
|
|
7351
6607
|
0 && (module.exports = {
|
|
7352
6608
|
Config,
|