koishi-plugin-noah 2.4.4 → 2.4.5

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/index.cjs CHANGED
@@ -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 src_exports = {};
32
- __export(src_exports, {
31
+ var index_exports = {};
32
+ __export(index_exports, {
33
33
  Config: () => Config,
34
34
  apply: () => apply14,
35
35
  inject: () => inject4,
36
36
  name: () => name14
37
37
  });
38
- module.exports = __toCommonJS(src_exports);
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
  }
@@ -336,18 +337,61 @@ __export(core_exports, {
336
337
  });
337
338
  var import_koishi6 = require("koishi");
338
339
 
339
- // src/core/command.ts
340
- var command_exports = {};
341
- __export(command_exports, {
342
- apply: () => apply2,
343
- name: () => name2
344
- });
340
+ // src/core/api/auth.ts
341
+ var crypto = __toESM(require("crypto"), 1);
342
+ function resolveApiContext(config) {
343
+ return {
344
+ secret: crypto.randomBytes(32).toString("hex"),
345
+ tokenTtl: config.tokenTtl ?? 1800,
346
+ frontendUrl: config.frontendUrl ?? "",
347
+ corsOrigin: config.corsOrigin ?? "*"
348
+ };
349
+ }
350
+ __name(resolveApiContext, "resolveApiContext");
351
+ var ALG = "sha256";
352
+ function base64url(data) {
353
+ const buf = typeof data === "string" ? Buffer.from(data) : data;
354
+ return buf.toString("base64url");
355
+ }
356
+ __name(base64url, "base64url");
357
+ function sign(payload, secret) {
358
+ const header = base64url(JSON.stringify({ alg: "HS256", typ: "JWT" }));
359
+ const body = base64url(payload);
360
+ const signature = crypto.createHmac(ALG, secret).update(`${header}.${body}`).digest("base64url");
361
+ return `${header}.${body}.${signature}`;
362
+ }
363
+ __name(sign, "sign");
364
+ function createToken(uid, secret, ttlSeconds = 1800) {
365
+ const now = Math.floor(Date.now() / 1e3);
366
+ const payload = { uid, iat: now, exp: now + ttlSeconds };
367
+ return sign(JSON.stringify(payload), secret);
368
+ }
369
+ __name(createToken, "createToken");
370
+ function verifyToken(token, secret) {
371
+ const parts = token.split(".");
372
+ if (parts.length !== 3) return null;
373
+ const [header, body, signature] = parts;
374
+ const expected = crypto.createHmac(ALG, secret).update(`${header}.${body}`).digest("base64url");
375
+ if (!crypto.timingSafeEqual(Buffer.from(signature), Buffer.from(expected))) return null;
376
+ try {
377
+ const payload = JSON.parse(Buffer.from(body, "base64url").toString());
378
+ if (payload.exp < Math.floor(Date.now() / 1e3)) return null;
379
+ return payload;
380
+ } catch {
381
+ return null;
382
+ }
383
+ }
384
+ __name(verifyToken, "verifyToken");
385
+
386
+ // src/types/index.ts
387
+ var serverValues = ["asphyxia", "mao", "official"];
345
388
 
346
389
  // src/core/services/card-service.ts
347
390
  var CardService = class {
348
391
  constructor(ctx) {
349
392
  this.ctx = ctx;
350
393
  }
394
+ ctx;
351
395
  static {
352
396
  __name(this, "CardService");
353
397
  }
@@ -447,9 +491,6 @@ var CardService = class {
447
491
  * @returns 无返回值的Promise
448
492
  */
449
493
  async updateCard(id, partial) {
450
- if (partial.defaultServerId === void 0) {
451
- partial.defaultServerId = 0;
452
- }
453
494
  await this.ctx.database.set(this.tableName, id, partial);
454
495
  }
455
496
  /**
@@ -492,6 +533,7 @@ var ServerService = class {
492
533
  constructor(ctx) {
493
534
  this.ctx = ctx;
494
535
  }
536
+ ctx;
495
537
  static {
496
538
  __name(this, "ServerService");
497
539
  }
@@ -641,18 +683,20 @@ var ServerService = class {
641
683
  if (channelServerRes.length > 0) {
642
684
  await this.ctx.database.set(
643
685
  "channel",
644
- { id: channel.id },
686
+ { id: channel.id, platform: channel.platform },
645
687
  { defaultServerId: channelServerRes[0].sid }
646
688
  );
647
689
  } else {
648
- await this.ctx.database.set("channel", { id: channel.id }, { defaultServerId: 0 });
690
+ await this.ctx.database.set(
691
+ "channel",
692
+ { id: channel.id, platform: channel.platform },
693
+ { defaultServerId: 0 }
694
+ );
649
695
  }
650
696
  }
651
697
  const cardRes = await this.ctx.database.get("card", { defaultServerId: id });
652
698
  for (const card2 of cardRes) {
653
- if (card2.defaultServerId !== void 0 && card2.defaultServerId !== 0) {
654
- await this.ctx.database.set("card", { id: card2.id }, { defaultServerId: 0 });
655
- }
699
+ await this.ctx.database.set("card", { id: card2.id }, { defaultServerId: 0 });
656
700
  }
657
701
  }
658
702
  /**
@@ -685,6 +729,7 @@ var UserService = class {
685
729
  constructor(ctx) {
686
730
  this.ctx = ctx;
687
731
  }
732
+ ctx;
688
733
  static {
689
734
  __name(this, "UserService");
690
735
  }
@@ -719,7 +764,7 @@ var UserService = class {
719
764
  };
720
765
 
721
766
  // src/core/utils/card.ts
722
- var crypto = __toESM(require("crypto"), 1);
767
+ var crypto2 = __toESM(require("crypto"), 1);
723
768
  var KEY_STRING = "?I'llB2c.YouXXXeMeHaYpy!";
724
769
  var KEY_BUFFER = Buffer.from(KEY_STRING, "ascii");
725
770
  var DES3_KEY = Buffer.from(KEY_BUFFER.map((b) => b * 2 & 255));
@@ -729,7 +774,7 @@ function encDes(data) {
729
774
  if (data.length !== 8) {
730
775
  throw new Error("encDes: data must be 8 bytes");
731
776
  }
732
- const cipher = crypto.createCipheriv("des-ede3-cbc", DES3_KEY, IV);
777
+ const cipher = crypto2.createCipheriv("des-ede3-cbc", DES3_KEY, IV);
733
778
  cipher.setAutoPadding(false);
734
779
  const encrypted = Buffer.concat([cipher.update(data), cipher.final()]);
735
780
  return encrypted;
@@ -739,7 +784,7 @@ function decDes(data) {
739
784
  if (data.length !== 8) {
740
785
  throw new Error("decDes: data must be 8 bytes");
741
786
  }
742
- const decipher = crypto.createDecipheriv("des-ede3-cbc", DES3_KEY, IV);
787
+ const decipher = crypto2.createDecipheriv("des-ede3-cbc", DES3_KEY, IV);
743
788
  decipher.setAutoPadding(false);
744
789
  const decrypted = Buffer.concat([decipher.update(data), decipher.final()]);
745
790
  return decrypted;
@@ -993,6 +1038,19 @@ function formatCardInfo(data) {
993
1038
  __name(formatCardInfo, "formatCardInfo");
994
1039
 
995
1040
  // src/core/utils/server.ts
1041
+ async function resolveServerForCard(card2, cardService, serverService, uid, platform, channelId) {
1042
+ if (card2.defaultServerId != void 0 && card2.defaultServerId != 0) {
1043
+ const cardServer = await serverService.getServerById(card2.defaultServerId);
1044
+ if (cardServer) return cardServer;
1045
+ await cardService.updateCard(card2.id, { defaultServerId: 0 });
1046
+ }
1047
+ if (channelId) {
1048
+ const channelDefault = await serverService.getDefaultServerByCid(platform, channelId);
1049
+ if (channelDefault) return channelDefault;
1050
+ }
1051
+ return await serverService.getDefaultServerByUid(uid);
1052
+ }
1053
+ __name(resolveServerForCard, "resolveServerForCard");
996
1054
  async function ensureOfficialServerForUser(ctx, serverService, userService, uid, officialSupportUrl) {
997
1055
  const userServers = await serverService.getServersByUid(uid);
998
1056
  const existingOfficial = userServers.find((server3) => server3.type === "official");
@@ -1015,12 +1073,355 @@ async function ensureOfficialServerForUser(ctx, serverService, userService, uid,
1015
1073
  }
1016
1074
  __name(ensureOfficialServerForUser, "ensureOfficialServerForUser");
1017
1075
 
1018
- // src/core/commands/bind.ts
1019
- function bind(ctx, config) {
1020
- ctx.command("bind [cardCode:text]", { slash: true }).alias("绑卡").userFields(["defaultCardId", "id"]).action(async ({ session }, cardCode) => {
1076
+ // src/core/api/index.ts
1077
+ function registerApiRoutes(ctx, apiCtx) {
1078
+ const { secret, corsOrigin } = apiCtx;
1079
+ function setCors(kctx) {
1080
+ kctx.set("Access-Control-Allow-Origin", corsOrigin);
1081
+ kctx.set("Access-Control-Allow-Methods", "GET, POST, PUT, PATCH, DELETE, OPTIONS");
1082
+ kctx.set("Access-Control-Allow-Headers", "Authorization, Content-Type");
1083
+ }
1084
+ __name(setCors, "setCors");
1085
+ function authenticate(kctx) {
1086
+ const auth = kctx.get("Authorization");
1087
+ if (!auth?.startsWith("Bearer ")) return null;
1088
+ return verifyToken(auth.slice(7), secret);
1089
+ }
1090
+ __name(authenticate, "authenticate");
1091
+ function unauthorized(kctx) {
1092
+ kctx.status = 401;
1093
+ kctx.body = { error: "Unauthorized" };
1094
+ }
1095
+ __name(unauthorized, "unauthorized");
1096
+ function badRequest(kctx, message) {
1097
+ kctx.status = 400;
1098
+ kctx.body = { error: message };
1099
+ }
1100
+ __name(badRequest, "badRequest");
1101
+ ctx.server.get("/noah/api/health", async (kctx) => {
1102
+ setCors(kctx);
1103
+ kctx.body = { status: "ok" };
1104
+ });
1105
+ const apiPaths = [
1106
+ "/noah/api/me",
1107
+ "/noah/api/cards",
1108
+ "/noah/api/cards/:id",
1109
+ "/noah/api/servers",
1110
+ "/noah/api/servers/:id",
1111
+ "/noah/api/defaults",
1112
+ "/noah/api/resolve",
1113
+ "/noah/api/health"
1114
+ ];
1115
+ for (const path4 of apiPaths) {
1116
+ ctx.server.all(path4, async (kctx, next) => {
1117
+ setCors(kctx);
1118
+ if (kctx.method === "OPTIONS") {
1119
+ kctx.status = 204;
1120
+ return;
1121
+ }
1122
+ await next();
1123
+ });
1124
+ }
1125
+ ctx.server.get("/noah/api/me", async (kctx) => {
1126
+ setCors(kctx);
1127
+ const payload = authenticate(kctx);
1128
+ if (!payload) return unauthorized(kctx);
1129
+ const cardService = new CardService(ctx);
1130
+ const serverService = new ServerService(ctx);
1131
+ const defaultCard = await cardService.getDefaultCardByUid(payload.uid);
1132
+ const defaultServer = await serverService.getDefaultServerByUid(payload.uid);
1133
+ kctx.body = {
1134
+ uid: payload.uid,
1135
+ defaultCardId: defaultCard?.id ?? null,
1136
+ defaultServerId: defaultServer?.id ?? null
1137
+ };
1138
+ });
1139
+ ctx.server.get("/noah/api/cards", async (kctx) => {
1140
+ setCors(kctx);
1141
+ const payload = authenticate(kctx);
1142
+ if (!payload) return unauthorized(kctx);
1143
+ const cardService = new CardService(ctx);
1144
+ const defaultCard = await cardService.getDefaultCardByUid(payload.uid);
1145
+ const cards = await cardService.getCardsByUid(payload.uid);
1146
+ kctx.body = cards.map((card2) => ({
1147
+ id: card2.id,
1148
+ code: card2.code,
1149
+ name: card2.name,
1150
+ defaultServerId: card2.defaultServerId || null,
1151
+ isDefault: defaultCard?.id === card2.id
1152
+ }));
1153
+ });
1154
+ ctx.server.post("/noah/api/cards", async (kctx) => {
1155
+ setCors(kctx);
1156
+ const payload = authenticate(kctx);
1157
+ if (!payload) return unauthorized(kctx);
1158
+ const body = kctx.request.body;
1159
+ if (!body?.code || !body?.name) return badRequest(kctx, "code and name are required");
1021
1160
  const cardService = new CardService(ctx);
1022
1161
  const serverService = new ServerService(ctx);
1023
1162
  const userService = new UserService(ctx);
1163
+ const processed = processInputCardCode(body.code);
1164
+ const cardType = classifyCardId(ctx, processed);
1165
+ if (cardType === "invalid") return badRequest(kctx, "Invalid card code");
1166
+ let finalCode = processed;
1167
+ if (cardType === "access") {
1168
+ try {
1169
+ finalCode = await accessToUid(ctx, processed);
1170
+ } catch (e) {
1171
+ return badRequest(kctx, `Failed to convert access code: ${e.message}`);
1172
+ }
1173
+ } else if (cardType === "konamiid") {
1174
+ try {
1175
+ finalCode = konamiIdToUid(processed);
1176
+ } catch (e) {
1177
+ return badRequest(kctx, `Failed to convert Konami ID: ${e.message}`);
1178
+ }
1179
+ }
1180
+ const existing = await cardService.getCardByCode(finalCode);
1181
+ if (existing) return badRequest(kctx, "Card already exists");
1182
+ const card2 = await cardService.createCard(payload.uid, finalCode, body.name);
1183
+ if (cardType === "official") {
1184
+ await ensureOfficialServerForUser(ctx, serverService, userService, payload.uid);
1185
+ }
1186
+ const defaultCard = await cardService.getDefaultCardByUid(payload.uid);
1187
+ if (!defaultCard) {
1188
+ await userService.updateUserDefaultCard(payload.uid, card2.id);
1189
+ }
1190
+ kctx.status = 201;
1191
+ kctx.body = { id: card2.id, code: card2.code, name: card2.name, defaultServerId: null };
1192
+ });
1193
+ ctx.server.patch("/noah/api/cards/:id", async (kctx) => {
1194
+ setCors(kctx);
1195
+ const payload = authenticate(kctx);
1196
+ if (!payload) return unauthorized(kctx);
1197
+ const cardId = Number(kctx.params.id);
1198
+ if (isNaN(cardId)) return badRequest(kctx, "Invalid card id");
1199
+ const cardService = new CardService(ctx);
1200
+ const cards = await cardService.getCardsByUid(payload.uid);
1201
+ if (!cards.find((c) => c.id === cardId)) {
1202
+ kctx.status = 404;
1203
+ kctx.body = { error: "Card not found" };
1204
+ return;
1205
+ }
1206
+ const body = kctx.request.body ?? {};
1207
+ const update = {};
1208
+ if (body.name !== void 0) update.name = body.name;
1209
+ if (body.defaultServerId !== void 0) update.defaultServerId = body.defaultServerId ?? 0;
1210
+ if (Object.keys(update).length > 0) {
1211
+ await cardService.updateCard(cardId, update);
1212
+ }
1213
+ const updated = await cardService.getCardById(cardId);
1214
+ kctx.body = updated;
1215
+ });
1216
+ ctx.server.delete("/noah/api/cards/:id", async (kctx) => {
1217
+ setCors(kctx);
1218
+ const payload = authenticate(kctx);
1219
+ if (!payload) return unauthorized(kctx);
1220
+ const cardId = Number(kctx.params.id);
1221
+ if (isNaN(cardId)) return badRequest(kctx, "Invalid card id");
1222
+ const cardService = new CardService(ctx);
1223
+ const cards = await cardService.getCardsByUid(payload.uid);
1224
+ if (!cards.find((c) => c.id === cardId)) {
1225
+ kctx.status = 404;
1226
+ kctx.body = { error: "Card not found" };
1227
+ return;
1228
+ }
1229
+ await cardService.removeCard(cardId);
1230
+ kctx.status = 204;
1231
+ });
1232
+ ctx.server.get("/noah/api/servers", async (kctx) => {
1233
+ setCors(kctx);
1234
+ const payload = authenticate(kctx);
1235
+ if (!payload) return unauthorized(kctx);
1236
+ const serverService = new ServerService(ctx);
1237
+ const defaultServer = await serverService.getDefaultServerByUid(payload.uid);
1238
+ const servers = await serverService.getServersByUid(payload.uid);
1239
+ kctx.body = servers.map((s) => ({
1240
+ id: s.id,
1241
+ type: s.type,
1242
+ name: s.name,
1243
+ baseUrl: s.baseUrl,
1244
+ pcbid: s.pcbid ?? null,
1245
+ isDefault: defaultServer?.id === s.id
1246
+ }));
1247
+ });
1248
+ ctx.server.post("/noah/api/servers", async (kctx) => {
1249
+ setCors(kctx);
1250
+ const payload = authenticate(kctx);
1251
+ if (!payload) return unauthorized(kctx);
1252
+ const body = kctx.request.body;
1253
+ if (!body?.type) return badRequest(kctx, "type is required");
1254
+ if (!serverValues.includes(body.type)) {
1255
+ return badRequest(
1256
+ kctx,
1257
+ `Invalid server type. Must be one of: ${serverValues.join(", ")}`
1258
+ );
1259
+ }
1260
+ const serverType = body.type;
1261
+ let baseUrl;
1262
+ let serverName;
1263
+ if (serverType === "mao") {
1264
+ baseUrl = ctx.globalConfig.maoServerUrl;
1265
+ serverName = body.name || "MaoMaNi";
1266
+ } else if (serverType === "official") {
1267
+ baseUrl = ctx.globalConfig.official_support_url;
1268
+ serverName = body.name || "Official";
1269
+ } else {
1270
+ if (!body.baseUrl) return badRequest(kctx, "baseUrl is required for asphyxia servers");
1271
+ if (!body.name) return badRequest(kctx, "name is required for asphyxia servers");
1272
+ baseUrl = body.baseUrl;
1273
+ serverName = body.name;
1274
+ }
1275
+ const serverService = new ServerService(ctx);
1276
+ const userService = new UserService(ctx);
1277
+ const server2 = await serverService.createServerForUser(
1278
+ {
1279
+ type: serverType,
1280
+ name: serverName,
1281
+ baseUrl,
1282
+ pcbid: body.pcbid,
1283
+ supportedGames: []
1284
+ },
1285
+ payload.uid
1286
+ );
1287
+ const defaultServer = await serverService.getDefaultServerByUid(payload.uid);
1288
+ if (!defaultServer) {
1289
+ await userService.updateUserDefaultServer(payload.uid, server2.id);
1290
+ }
1291
+ kctx.status = 201;
1292
+ kctx.body = {
1293
+ id: server2.id,
1294
+ type: server2.type,
1295
+ name: server2.name,
1296
+ baseUrl: server2.baseUrl,
1297
+ pcbid: server2.pcbid ?? null
1298
+ };
1299
+ });
1300
+ ctx.server.patch("/noah/api/servers/:id", async (kctx) => {
1301
+ setCors(kctx);
1302
+ const payload = authenticate(kctx);
1303
+ if (!payload) return unauthorized(kctx);
1304
+ const serverId = Number(kctx.params.id);
1305
+ if (isNaN(serverId)) return badRequest(kctx, "Invalid server id");
1306
+ const serverService = new ServerService(ctx);
1307
+ const servers = await serverService.getServersByUid(payload.uid);
1308
+ if (!servers.find((s) => s.id === serverId)) {
1309
+ kctx.status = 404;
1310
+ kctx.body = { error: "Server not found" };
1311
+ return;
1312
+ }
1313
+ const body = kctx.request.body ?? {};
1314
+ const update = {};
1315
+ if (body.name !== void 0) update.name = body.name;
1316
+ if (body.baseUrl !== void 0) update.baseUrl = body.baseUrl;
1317
+ if (body.pcbid !== void 0) update.pcbid = body.pcbid;
1318
+ if (Object.keys(update).length > 0) {
1319
+ await serverService.updateServer(serverId, update);
1320
+ }
1321
+ const updated = await serverService.getServerById(serverId);
1322
+ kctx.body = {
1323
+ id: updated.id,
1324
+ type: updated.type,
1325
+ name: updated.name,
1326
+ baseUrl: updated.baseUrl,
1327
+ pcbid: updated.pcbid ?? null
1328
+ };
1329
+ });
1330
+ ctx.server.delete("/noah/api/servers/:id", async (kctx) => {
1331
+ setCors(kctx);
1332
+ const payload = authenticate(kctx);
1333
+ if (!payload) return unauthorized(kctx);
1334
+ const serverId = Number(kctx.params.id);
1335
+ if (isNaN(serverId)) return badRequest(kctx, "Invalid server id");
1336
+ const serverService = new ServerService(ctx);
1337
+ const servers = await serverService.getServersByUid(payload.uid);
1338
+ if (!servers.find((s) => s.id === serverId)) {
1339
+ kctx.status = 404;
1340
+ kctx.body = { error: "Server not found" };
1341
+ return;
1342
+ }
1343
+ await serverService.removeServer(serverId);
1344
+ kctx.status = 204;
1345
+ });
1346
+ ctx.server.put("/noah/api/defaults", async (kctx) => {
1347
+ setCors(kctx);
1348
+ const payload = authenticate(kctx);
1349
+ if (!payload) return unauthorized(kctx);
1350
+ const body = kctx.request.body ?? {};
1351
+ const userService = new UserService(ctx);
1352
+ const cardService = new CardService(ctx);
1353
+ const serverService = new ServerService(ctx);
1354
+ if (body.defaultCardId !== void 0) {
1355
+ if (body.defaultCardId === null || body.defaultCardId === 0) {
1356
+ await userService.updateUserDefaultCard(payload.uid, 0);
1357
+ } else {
1358
+ const cards = await cardService.getCardsByUid(payload.uid);
1359
+ if (!cards.find((c) => c.id === body.defaultCardId)) {
1360
+ return badRequest(kctx, "Card not found");
1361
+ }
1362
+ await userService.updateUserDefaultCard(payload.uid, body.defaultCardId);
1363
+ }
1364
+ }
1365
+ if (body.defaultServerId !== void 0) {
1366
+ if (body.defaultServerId === null || body.defaultServerId === 0) {
1367
+ await userService.updateUserDefaultServer(payload.uid, 0);
1368
+ } else {
1369
+ const servers = await serverService.getServersByUid(payload.uid);
1370
+ if (!servers.find((s) => s.id === body.defaultServerId)) {
1371
+ return badRequest(kctx, "Server not found");
1372
+ }
1373
+ await userService.updateUserDefaultServer(payload.uid, body.defaultServerId);
1374
+ }
1375
+ }
1376
+ const defaultCard = await cardService.getDefaultCardByUid(payload.uid);
1377
+ const defaultServer = await serverService.getDefaultServerByUid(payload.uid);
1378
+ kctx.body = {
1379
+ defaultCardId: defaultCard?.id ?? null,
1380
+ defaultServerId: defaultServer?.id ?? null
1381
+ };
1382
+ });
1383
+ ctx.server.get("/noah/api/resolve", async (kctx) => {
1384
+ setCors(kctx);
1385
+ const payload = authenticate(kctx);
1386
+ if (!payload) return unauthorized(kctx);
1387
+ const cardService = new CardService(ctx);
1388
+ const serverService = new ServerService(ctx);
1389
+ const cards = await cardService.getCardsByUid(payload.uid);
1390
+ const defaultCard = await cardService.getDefaultCardByUid(payload.uid);
1391
+ const result = await Promise.all(
1392
+ cards.map(async (card2) => {
1393
+ let resolvedServer = null;
1394
+ if (card2.defaultServerId && card2.defaultServerId !== 0) {
1395
+ resolvedServer = await serverService.getServerById(card2.defaultServerId);
1396
+ }
1397
+ if (!resolvedServer) {
1398
+ resolvedServer = await serverService.getDefaultServerByUid(payload.uid);
1399
+ }
1400
+ return {
1401
+ cardId: card2.id,
1402
+ cardName: card2.name,
1403
+ isDefaultCard: defaultCard?.id === card2.id,
1404
+ resolvedServerId: resolvedServer?.id ?? null,
1405
+ resolvedServerName: resolvedServer?.name ?? null,
1406
+ source: card2.defaultServerId && card2.defaultServerId !== 0 ? "card" : "user-default"
1407
+ };
1408
+ })
1409
+ );
1410
+ kctx.body = result;
1411
+ });
1412
+ }
1413
+ __name(registerApiRoutes, "registerApiRoutes");
1414
+
1415
+ // src/core/command.ts
1416
+ var command_exports = {};
1417
+ __export(command_exports, {
1418
+ apply: () => apply2,
1419
+ name: () => name2
1420
+ });
1421
+
1422
+ // src/core/commands/bind.ts
1423
+ function bind(ctx, config, cardService, serverService, userService) {
1424
+ ctx.command("bind [cardCode:text]", { slash: true }).alias("绑卡").userFields(["id"]).action(async ({ session }, cardCode) => {
1024
1425
  if (!cardCode) {
1025
1426
  await session.send(session.text(".prompt"));
1026
1427
  cardCode = await session.prompt();
@@ -1075,9 +1476,8 @@ __name(bind, "bind");
1075
1476
 
1076
1477
  // src/core/commands/card.ts
1077
1478
  var import_koishi2 = require("koishi");
1078
- function card(ctx, config) {
1479
+ function card(ctx, config, cardService, serverService, userService) {
1079
1480
  ctx.command("card", { slash: true }).alias("卡片管理").userFields(["id"]).channelFields(["id"]).option("detail", "-d <cardCode:text>").action(async ({ session, options }) => {
1080
- const cardService = new CardService(ctx);
1081
1481
  const atGuild = session.guildId != null;
1082
1482
  if (options.detail) {
1083
1483
  const cardCode = processInputCardCode(options.detail);
@@ -1094,8 +1494,6 @@ function card(ctx, config) {
1094
1494
  return session.text(".lookup-error-unknown");
1095
1495
  }
1096
1496
  }
1097
- const serverService = new ServerService(ctx);
1098
- const userService = new UserService(ctx);
1099
1497
  const defaultCard = await cardService.getDefaultCardByUid(session.user.id);
1100
1498
  const defaultCardId = defaultCard ? defaultCard.id : 0;
1101
1499
  const userDefaultServer = await serverService.getDefaultServerByUid(session.user.id);
@@ -1305,14 +1703,6 @@ async function showCardMenu(ctx, session, card2, cardService, serverService, use
1305
1703
  }
1306
1704
  if (selectNum === 3) {
1307
1705
  await cardService.removeCard(card2.id);
1308
- if (userDefaultCardId === card2.id) {
1309
- const res = await cardService.getCardsByUid(uid);
1310
- if (res.length === 0) {
1311
- await userService.updateUserDefaultCard(uid, 0);
1312
- } else {
1313
- await userService.updateUserDefaultCard(uid, res[0].id);
1314
- }
1315
- }
1316
1706
  return session.text(".menu-3-success");
1317
1707
  }
1318
1708
  if (selectNum === 4) {
@@ -1321,10 +1711,7 @@ async function showCardMenu(ctx, session, card2, cardService, serverService, use
1321
1711
  if (!cardName) return session.text("commands.timeout");
1322
1712
  if (cardName === "q") return session.text(".quit");
1323
1713
  if (cardName.includes(" ")) return session.text(".menu-4-error-invalid-name");
1324
- await cardService.updateCard(card2.id, {
1325
- name: cardName,
1326
- defaultServerId: card2.defaultServerId
1327
- });
1714
+ await cardService.updateCard(card2.id, { name: cardName });
1328
1715
  return session.text(".menu-4-success");
1329
1716
  }
1330
1717
  if (selectNum === 5) {
@@ -1386,8 +1773,9 @@ __name(showCardMenu, "showCardMenu");
1386
1773
  // src/core/commands/help.ts
1387
1774
  function help(ctx, config) {
1388
1775
  ctx.command("help", { slash: true }).alias("帮助").action(({ session }) => {
1389
- console.log(session);
1390
- return session.text(".content");
1776
+ let msg = session.text(".content");
1777
+ if (session.platform == "onebot") msg = msg.concat(session.text(".qq-extra"));
1778
+ return msg;
1391
1779
  });
1392
1780
  }
1393
1781
  __name(help, "help");
@@ -1583,11 +1971,6 @@ __name(maintain, "maintain");
1583
1971
 
1584
1972
  // src/core/commands/server.ts
1585
1973
  var import_koishi5 = require("koishi");
1586
-
1587
- // src/types/index.ts
1588
- var serverValues = ["asphyxia", "mao", "official"];
1589
-
1590
- // src/core/commands/server.ts
1591
1974
  function normalizeUrl(url) {
1592
1975
  try {
1593
1976
  if (!url.match(/^https?:\/\//i)) {
@@ -1645,10 +2028,8 @@ async function promptServerSelect(session, servers, defaultId, prefix) {
1645
2028
  return selectNum;
1646
2029
  }
1647
2030
  __name(promptServerSelect, "promptServerSelect");
1648
- function server(ctx, config) {
2031
+ function server(ctx, config, serverService, userService) {
1649
2032
  ctx.command("server", { slash: true }).alias("服务器管理").option("channel", "-c").userFields(["id"]).channelFields(["id"]).action(async ({ session, options }) => {
1650
- const serverService = new ServerService(ctx);
1651
- const userService = new UserService(ctx);
1652
2033
  const atGuild = session.guildId != null;
1653
2034
  if (options.channel) {
1654
2035
  if (!atGuild) return session.text(".no-channel");
@@ -1855,25 +2236,6 @@ async function showServerMenu(ctx, session, serverService, userService, server2,
1855
2236
  }
1856
2237
  if (selectNum === 3) {
1857
2238
  await serverService.removeServer(server2.id);
1858
- if (from === "user") {
1859
- if (userDefaultServerId === server2.id) {
1860
- const res = await serverService.getServersByUid(uid);
1861
- if (res.length === 0) {
1862
- await userService.updateUserDefaultServer(uid, 0);
1863
- } else {
1864
- await userService.updateUserDefaultServer(uid, res[0].id);
1865
- }
1866
- }
1867
- } else {
1868
- if (channelDefaultServerId === server2.id) {
1869
- const res = await serverService.getServersByCid(cid);
1870
- if (res.length === 0) {
1871
- await userService.updateChannelDefaultServer(session.platform, cid, 0);
1872
- } else {
1873
- await userService.updateChannelDefaultServer(session.platform, cid, res[0].id);
1874
- }
1875
- }
1876
- }
1877
2239
  return session.text(".menu-3-success");
1878
2240
  }
1879
2241
  if (selectNum === 4) {
@@ -1994,16 +2356,35 @@ async function addServer(ctx, session, serverService, userService, from, uid, us
1994
2356
  }
1995
2357
  __name(addServer, "addServer");
1996
2358
 
2359
+ // src/core/commands/settings.ts
2360
+ function settings(ctx, apiCtx) {
2361
+ ctx.command("settings", { slash: true }).alias("设置", "setting").userFields(["id"]).channelFields(["id"]).action(async ({ session }) => {
2362
+ const atGuild = session.guildId != null;
2363
+ if (atGuild) return session.text(".dm-only");
2364
+ const { secret, tokenTtl, frontendUrl } = apiCtx;
2365
+ const token = createToken(session.user.id, secret, tokenTtl);
2366
+ const url = frontendUrl ? `${frontendUrl}?token=${token}` : `Token: ${token}`;
2367
+ return session.text(".success", { url, minutes: Math.floor(tokenTtl / 60) });
2368
+ });
2369
+ }
2370
+ __name(settings, "settings");
2371
+
1997
2372
  // src/core/command.ts
1998
2373
  var name2 = "command";
1999
2374
  function apply2(ctx, config) {
2375
+ const cardService = new CardService(ctx);
2376
+ const serverService = new ServerService(ctx);
2377
+ const userService = new UserService(ctx);
2000
2378
  help(ctx, config);
2001
- bind(ctx, config);
2002
- card(ctx, config);
2003
- server(ctx, config);
2379
+ bind(ctx, config, cardService, serverService, userService);
2380
+ card(ctx, config, cardService, serverService, userService);
2381
+ server(ctx, config, serverService, userService);
2004
2382
  locale(ctx, config);
2005
2383
  link(ctx, config);
2006
2384
  maintain(ctx, config);
2385
+ if (config._apiCtx) {
2386
+ settings(ctx, config._apiCtx);
2387
+ }
2007
2388
  }
2008
2389
  __name(apply2, "apply");
2009
2390
 
@@ -2178,7 +2559,7 @@ function guildNameCard(ctx, config) {
2178
2559
  bot.selfId,
2179
2560
  nextNameCard
2180
2561
  );
2181
- ctx.logger("guild-namecard").info(
2562
+ ctx.logger("guild-namecard").debug(
2182
2563
  `更新 ${bot.selfId} 在 ${guild.name} 的群名片: ${currentNameCard} -> ${nextNameCard}`
2183
2564
  );
2184
2565
  try {
@@ -2248,17 +2629,17 @@ function apply4(ctx, config) {
2248
2629
  __name(apply4, "apply");
2249
2630
 
2250
2631
  // src/core/locales/en-US.yml
2251
- 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!", 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>
2632
+ 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", tokenTtl: "**Settings Panel** Link expiry time (seconds)", frontendUrl: "**Settings Panel** Frontend URL (/settings will generate a full link when set)", corsOrigin: "**Settings Panel** CORS allowed origin (set to frontend domain in production)" }, selector: { "card-not-found": "<p>You haven't bound a card yet, go bind one first~</p>", "server-not-found": "<p>No available servers, add one yourself~</p>", "menu-select": "<p>Please select the card you want to use:</p>\n{card_list}\n<p>q. Quit</p>", "server-menu-select": "<p>Please select the server you want to use:</p>\n{server_list}\n<p>q. Quit</p>", "invalid-select": "<p>No such option!</p>", quit: "<p>Quit!</p>", "default-card": "Default", "default-server": "Default" }, 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!", help: { description: "Show Noah help information", messages: { content: "<p>Guide:</p>\n<p>https://docs.logthm.cn/noah</p>", "qq-extra": "<p>Having issues? Join group 723977027 to give feedback~</p>" } }, 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>
2252
2633
  <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>
2253
2634
  <p>{0}</p>
2254
- <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>" } } } };
2635
+ <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>" } }, settings: { description: "Get settings panel link", messages: { "dm-only": "Please use this command in a direct message.", success: "<p>Open the settings panel via the link below:</p>\n<p>{url}</p>\n<p>This link expires in {minutes} minutes. Do not share it.</p>" } } } };
2255
2636
 
2256
2637
  // src/core/locales/zh-CN.yml
2257
- 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 没等到你的回复,请重试!", 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>" } } } };
2638
+ var zh_CN_default2 = { _config: { $desc: "Core 模块设置", adminUsers: "**插件管理员** 的用户id (使用 inspect 指令获取)", guildNameCards: "**群聊卡片** 的名称列表", tokenTtl: "**设置面板** 链接有效期(秒)", frontendUrl: "**设置面板** 前端地址(配置后 /settings 会生成完整链接)", corsOrigin: "**设置面板** CORS 允许来源(生产环境请设为前端域名)", maoServerUrl: "**猫网服务器** 的 URL 地址", official_support_url: "**官方支持** 的 URL 地址" }, selector: { "card-not-found": "<p>你还没绑卡,去绑个卡再来吧~</p>", "server-not-found": "<p>没有可用的服务器哦,自己添加一个吧~</p>", "menu-select": "<p>请选择你要使用的卡片:</p>\n{card_list}\n<p>q. 退出</p>", "server-menu-select": "<p>请选择你要使用的服务器:</p>\n{server_list}\n<p>q. 退出</p>", "invalid-select": "<p>没有该选项!</p>", quit: "<p>已退出~</p>", "default-card": "默认卡片", "default-server": "默认服务器" }, commands: { maintain: { description: "切换到维护模式", messages: { "no-auth": "<p>你没有权限使用本功能哦~</p>", start: "<p>正在进入维护模式</p>", "success-start": "<p>成功切换到维护模式</p>", stop: "<p>正在退出维护模式</p>", "success-stop": "<p>成功退出维护模式</p>" } }, timeout: "Noah 没等到你的回复,请重试!", help: { description: "显示 Noah 帮助信息", messages: { content: "<p>使用文档:</p>\n<p>https://docs.logthm.cn/noah</p>", "qq-extra": "<p>遇到问题了?加群 723977027 反馈~</p>" } }, 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>" } }, settings: { description: "获取设置面板链接", messages: { "dm-only": "请在私聊中使用此命令。", success: "<p>点击下方链接打开设置面板:</p>\n<p>{url}</p>\n<p>链接有效期 {minutes} 分钟,请勿分享给他人。</p>" } } } };
2258
2639
 
2259
2640
  // src/core/index.ts
2260
2641
  var name5 = "Noah-Core";
2261
- var inject = ["database", "globalConfig"];
2642
+ var inject = ["database", "globalConfig", "server"];
2262
2643
  var logger2 = new import_koishi6.Logger("Noah-Core");
2263
2644
  function apply5(ctx, config) {
2264
2645
  ;
@@ -2266,9 +2647,11 @@ function apply5(ctx, config) {
2266
2647
  ["en-US", en_US_default2],
2267
2648
  ["zh-CN", zh_CN_default2]
2268
2649
  ].forEach(([lang, file]) => ctx.i18n.define(lang, file));
2650
+ const apiCtx = resolveApiContext(config.core);
2269
2651
  ctx.plugin(database_exports, config.core);
2270
- ctx.plugin(command_exports, config.core);
2652
+ ctx.plugin(command_exports, { ...config.core, _apiCtx: apiCtx });
2271
2653
  ctx.plugin(event_exports, config.core);
2654
+ registerApiRoutes(ctx, apiCtx);
2272
2655
  }
2273
2656
  __name(apply5, "apply");
2274
2657
 
@@ -2287,6 +2670,7 @@ var BaseDrawer = class {
2287
2670
  constructor(ctx) {
2288
2671
  this.ctx = ctx;
2289
2672
  }
2673
+ ctx;
2290
2674
  static {
2291
2675
  __name(this, "BaseDrawer");
2292
2676
  }
@@ -2326,6 +2710,7 @@ var CoreDrawer = class extends BaseDrawer {
2326
2710
  super(ctx);
2327
2711
  this.ctx = ctx;
2328
2712
  }
2713
+ ctx;
2329
2714
  static {
2330
2715
  __name(this, "CoreDrawer");
2331
2716
  }
@@ -2361,6 +2746,7 @@ var IIDXDrawer = class extends BaseDrawer {
2361
2746
  super(ctx);
2362
2747
  this.ctx = ctx;
2363
2748
  }
2749
+ ctx;
2364
2750
  static {
2365
2751
  __name(this, "IIDXDrawer");
2366
2752
  }
@@ -2645,12 +3031,12 @@ function getNextVFIncrease(level, currentScore, clearType) {
2645
3031
  }
2646
3032
  __name(getNextVFIncrease, "getNextVFIncrease");
2647
3033
  function getGradeRange(grade) {
2648
- if (!grade) return ALL_GRADES;
3034
+ if (!grade) return ALL_GRADES.slice(0, ALL_GRADES.indexOf("A") + 1);
2649
3035
  return Array.isArray(grade) ? grade : [grade];
2650
3036
  }
2651
3037
  __name(getGradeRange, "getGradeRange");
2652
3038
  function getClearTypeRange(clearType) {
2653
- if (!clearType) return ALL_CLEAR_TYPES;
3039
+ if (!clearType) return ALL_CLEAR_TYPES.slice(0, ALL_CLEAR_TYPES.indexOf("NC") + 1);
2654
3040
  return Array.isArray(clearType) ? clearType : [clearType];
2655
3041
  }
2656
3042
  __name(getClearTypeRange, "getClearTypeRange");
@@ -2866,7 +3252,20 @@ function generateQueryResults(params) {
2866
3252
  uniqueResults.push(result);
2867
3253
  }
2868
3254
  }
2869
- const filteredResults = uniqueResults.filter((result) => result.clearType !== "S-PUC");
3255
+ const excludeLevelSet = params.excludeLevel ? new Set(params.excludeLevel.map((l) => Math.round(l * 10))) : null;
3256
+ const excludeScoreSet = params.excludeScore ? new Set(params.excludeScore) : null;
3257
+ const excludeGradeSet = params.excludeGrade ? new Set(params.excludeGrade) : null;
3258
+ const excludeClearTypeSet = params.excludeClearType ? new Set(params.excludeClearType) : null;
3259
+ const excludeVfSet = params.excludeVf ? new Set(params.excludeVf.map((v) => Math.round(v * 20))) : null;
3260
+ const afterExclusion = uniqueResults.filter((result) => {
3261
+ if (excludeLevelSet && excludeLevelSet.has(Math.round(result.level * 10))) return false;
3262
+ if (excludeScoreSet && excludeScoreSet.has(result.score)) return false;
3263
+ if (excludeGradeSet && excludeGradeSet.has(result.grade)) return false;
3264
+ if (excludeClearTypeSet && excludeClearTypeSet.has(result.clearType)) return false;
3265
+ if (excludeVfSet && excludeVfSet.has(Math.round(result.vf * 20))) return false;
3266
+ return true;
3267
+ });
3268
+ const filteredResults = afterExclusion.filter((result) => result.clearType !== "S-PUC");
2870
3269
  return filteredResults.sort((a, b) => b.vf - a.vf);
2871
3270
  }
2872
3271
  __name(generateQueryResults, "generateQueryResults");
@@ -2877,6 +3276,7 @@ var SDVXDrawer = class extends BaseDrawer {
2877
3276
  super(ctx);
2878
3277
  this.ctx = ctx;
2879
3278
  }
3279
+ ctx;
2880
3280
  static {
2881
3281
  __name(this, "SDVXDrawer");
2882
3282
  }
@@ -2893,8 +3293,8 @@ var SDVXDrawer = class extends BaseDrawer {
2893
3293
  ["Fredoka One", "fonts/FredokaOne.ttf"]
2894
3294
  ];
2895
3295
  for (const [name15, file] of fonts) {
2896
- const path3 = getAssetPath(this.ctx, file);
2897
- if (fs2.existsSync(path3)) FontLibrary.use(name15, path3);
3296
+ const path4 = getAssetPath(this.ctx, file);
3297
+ if (fs2.existsSync(path4)) FontLibrary.use(name15, path4);
2898
3298
  }
2899
3299
  this.fontsLoaded = true;
2900
3300
  }
@@ -3000,20 +3400,20 @@ var SDVXDrawer = class extends BaseDrawer {
3000
3400
  ctx.save();
3001
3401
  ctx.translate(556, 17);
3002
3402
  ctx.beginPath();
3003
- const r = 16, w = 100, h10 = 200, x0 = 0, y0 = 0;
3403
+ const r = 16, w = 100, h12 = 200, x0 = 0, y0 = 0;
3004
3404
  ctx.moveTo(x0 + r, y0);
3005
3405
  ctx.lineTo(x0 + w - r, y0);
3006
3406
  ctx.arcTo(x0 + w, y0, x0 + w, y0 + r, r);
3007
- ctx.lineTo(x0 + w, y0 + h10 - r);
3008
- ctx.arcTo(x0 + w, y0 + h10, x0 + w - r, y0 + h10, r);
3009
- ctx.lineTo(x0 + r, y0 + h10);
3010
- ctx.arcTo(x0, y0 + h10, x0, y0 + h10 - r, r);
3407
+ ctx.lineTo(x0 + w, y0 + h12 - r);
3408
+ ctx.arcTo(x0 + w, y0 + h12, x0 + w - r, y0 + h12, r);
3409
+ ctx.lineTo(x0 + r, y0 + h12);
3410
+ ctx.arcTo(x0, y0 + h12, x0, y0 + h12 - r, r);
3011
3411
  ctx.lineTo(x0, y0 + r);
3012
3412
  ctx.arcTo(x0, y0, x0 + r, y0, r);
3013
3413
  ctx.closePath();
3014
3414
  const circleRadius = 14;
3015
3415
  const circleX = x0 + w;
3016
- const circleY = y0 + h10 / 2;
3416
+ const circleY = y0 + h12 / 2;
3017
3417
  ctx.moveTo(circleX + circleRadius, circleY);
3018
3418
  ctx.arc(circleX, circleY, circleRadius, 0, Math.PI * 2, true);
3019
3419
  ctx.clip();
@@ -3765,6 +4165,7 @@ var DrawerFactory = class {
3765
4165
  constructor(ctx) {
3766
4166
  this.ctx = ctx;
3767
4167
  }
4168
+ ctx;
3768
4169
  static {
3769
4170
  __name(this, "DrawerFactory");
3770
4171
  }
@@ -3841,154 +4242,20 @@ __name(apply6, "apply");
3841
4242
  var poke_exports = {};
3842
4243
  __export(poke_exports, {
3843
4244
  apply: () => apply7,
4245
+ logger: () => logger3,
3844
4246
  name: () => name7
3845
4247
  });
3846
- var fs3 = __toESM(require("fs"), 1);
3847
- var path2 = __toESM(require("path"), 1);
3848
- var import_koishi8 = require("koishi");
3849
-
3850
- // src/fun/poke/locales/en-US.yml
3851
- var en_US_default3 = { _config: { $desc: "Poke Module Settings", interval: "最小触发间隔(毫秒)", warning: "频繁触发是否发送警告", prompt: "警告内容", messages: { $desc: "消息内容", content: "消息内容", weight: "权重" } }, commands: { poke: { description: "poke" } } };
3852
-
3853
- // src/fun/poke/locales/zh-CN.yml
3854
- var zh_CN_default3 = { _config: { $desc: "Poke 模块设置", interval: "最小触发间隔(毫秒)", warning: "频繁触发是否发送警告", prompt: { $desc: "警告内容", content: "消息", weight: "权重" }, messages: { $desc: "消息内容", content: "消息", weight: "权重" } }, commands: { poke: { description: "戳一戳" } } };
4248
+ var import_koishi10 = require("koishi");
3855
4249
 
3856
- // src/fun/poke/index.ts
3857
- var name7 = "Noah-Poke";
3858
- function apply7(ctx, config) {
3859
- ;
3860
- [
3861
- ["en-US", en_US_default3],
3862
- ["zh-CN", zh_CN_default3]
3863
- ].forEach(([lang, file]) => ctx.i18n.define(lang, file));
3864
- const cache = /* @__PURE__ */ new Map();
3865
- const pokeConfig2 = config.poke;
3866
- const IMAGE_EXTENSIONS = [".png", ".jpg", ".jpeg", ".gif", ".webp"];
3867
- function isImageFile(filename) {
3868
- const ext = path2.extname(filename).toLowerCase();
3869
- return IMAGE_EXTENSIONS.includes(ext);
3870
- }
3871
- __name(isImageFile, "isImageFile");
3872
- function getMimeType(filename) {
3873
- const ext = path2.extname(filename).toLowerCase();
3874
- switch (ext) {
3875
- case ".png":
3876
- return "image/png";
3877
- case ".jpg":
3878
- case ".jpeg":
3879
- return "image/jpeg";
3880
- case ".gif":
3881
- return "image/gif";
3882
- case ".webp":
3883
- return "image/webp";
3884
- default:
3885
- return "image/png";
3886
- }
3887
- }
3888
- __name(getMimeType, "getMimeType");
3889
- function findMatchingAudio(imagePath) {
3890
- try {
3891
- const dir = path2.dirname(imagePath);
3892
- const filename = path2.basename(imagePath);
3893
- const match = filename.match(/_(\d+)\.(png|jpg|jpeg|gif|webp)$/i);
3894
- if (!match) {
3895
- return null;
3896
- }
3897
- const imageNumber = match[1];
3898
- const audioNumber = parseInt(imageNumber, 10).toString();
3899
- const files = fs3.readdirSync(dir);
3900
- const audioFile = files.find((file) => {
3901
- return file.endsWith(`-${audioNumber}.mp3`);
3902
- });
3903
- return audioFile ? path2.join(dir, audioFile) : null;
3904
- } catch {
3905
- return null;
3906
- }
3907
- }
3908
- __name(findMatchingAudio, "findMatchingAudio");
3909
- async function sendRandomNoahStamp(ctx2, session, voiceOnly = false) {
3910
- try {
3911
- const stampsPath = getAssetPath(ctx2, "stamps/noah_stamp");
3912
- const stampDirs = fs3.readdirSync(stampsPath).filter((dir) => dir.startsWith("stamp_"));
3913
- if (stampDirs.length === 0) {
3914
- await session.sendQueued("No stamps found!");
3915
- return;
3916
- }
3917
- const allStamps = [];
3918
- for (const dir of stampDirs) {
3919
- const stampDirPath = path2.join(stampsPath, dir);
3920
- const stampFiles = fs3.readdirSync(stampDirPath).filter((file) => file.startsWith(dir) && isImageFile(file));
3921
- stampFiles.forEach((file) => {
3922
- allStamps.push({
3923
- path: path2.join(stampDirPath, file),
3924
- dirName: dir
3925
- });
3926
- });
3927
- }
3928
- if (allStamps.length === 0) {
3929
- await session.sendQueued("No stamps found in any directory!");
3930
- return;
3931
- }
3932
- let availableStamps = allStamps;
3933
- if (voiceOnly) {
3934
- availableStamps = allStamps.filter((stamp) => {
3935
- const audioPath2 = findMatchingAudio(stamp.path);
3936
- return audioPath2 && fs3.existsSync(audioPath2);
3937
- });
3938
- if (availableStamps.length === 0) {
3939
- await session.sendQueued("No stamps with voice found!");
3940
- return;
3941
- }
3942
- }
3943
- const randomStamp = availableStamps[Math.floor(Math.random() * availableStamps.length)];
3944
- ctx2.logger("Noah-Poke").debug(`Selected stamp from ${randomStamp.dirName}`);
3945
- if (!fs3.existsSync(randomStamp.path)) {
3946
- await session.sendQueued("Selected stamp not found!");
3947
- return;
3948
- }
3949
- const imageBuffer = fs3.readFileSync(randomStamp.path);
3950
- const audioPath = findMatchingAudio(randomStamp.path);
3951
- await session.sendQueued(import_koishi8.h.image(imageBuffer, getMimeType(randomStamp.path)));
3952
- if (audioPath && fs3.existsSync(audioPath)) {
3953
- ctx2.logger("Noah-Poke").debug(`Found matching audio: ${path2.basename(audioPath)}`);
3954
- const audioBuffer = fs3.readFileSync(audioPath);
3955
- await session.sendQueued(import_koishi8.h.audio(audioBuffer, "audio/mpeg"));
3956
- }
3957
- } catch (error) {
3958
- ctx2.logger("Noah-Poke").error(`Error sending noah stamp: ${error.message}`);
3959
- await session.sendQueued("Failed to send noah stamp.");
3960
- }
3961
- }
3962
- __name(sendRandomNoahStamp, "sendRandomNoahStamp");
3963
- async function sendRandomChatStamp(ctx2) {
3964
- try {
3965
- const stampsPath = getAssetPath(ctx2, "stamps/chat_stamp");
3966
- const stampFiles = fs3.readdirSync(stampsPath).filter((file) => isImageFile(file));
3967
- if (stampFiles.length === 0) {
3968
- return "No chat stamps found!";
3969
- }
3970
- const randomStamp = stampFiles[Math.floor(Math.random() * stampFiles.length)];
3971
- const stampPath = path2.join(stampsPath, randomStamp);
3972
- if (!fs3.existsSync(stampPath)) {
3973
- return "Selected stamp not found!";
3974
- }
3975
- const imageBuffer = fs3.readFileSync(stampPath);
3976
- return import_koishi8.h.image(imageBuffer, getMimeType(stampPath));
3977
- } catch (error) {
3978
- ctx2.logger("Noah-Poke").error(`Error sending chat stamp: ${error.message}`);
3979
- return "Failed to send chat stamp.";
3980
- }
3981
- }
3982
- __name(sendRandomChatStamp, "sendRandomChatStamp");
3983
- ctx.command("stamp").option("type", "-t [type]").option("voice", "-v", { fallback: false }).action(async ({ session, options }) => {
3984
- if (options.type == "noah") {
3985
- await sendRandomNoahStamp(ctx, session, options.voice);
3986
- } else if (options.type == "chat") {
3987
- return sendRandomChatStamp(ctx);
3988
- } else {
3989
- return sendRandomChatStamp(ctx);
3990
- }
3991
- });
4250
+ // src/fun/poke/commands/poke.ts
4251
+ function parsePlatform(target) {
4252
+ const index = target.indexOf(":");
4253
+ const platform = target.slice(0, index);
4254
+ const id = target.slice(index + 1);
4255
+ return [platform, id];
4256
+ }
4257
+ __name(parsePlatform, "parsePlatform");
4258
+ function registerPokeCommand(ctx) {
3992
4259
  ctx.platform("onebot").command("poke [target:user]").action(async ({ session }, target) => {
3993
4260
  if (!session.onebot) {
3994
4261
  return;
@@ -3996,7 +4263,7 @@ function apply7(ctx, config) {
3996
4263
  const params = { user_id: session.userId };
3997
4264
  if (target) {
3998
4265
  const [platform, id] = parsePlatform(target);
3999
- if (platform != "onebot") {
4266
+ if (platform !== "onebot") {
4000
4267
  return;
4001
4268
  }
4002
4269
  params.user_id = id;
@@ -4004,66 +4271,292 @@ function apply7(ctx, config) {
4004
4271
  if (session.isDirect) {
4005
4272
  await session.onebot._request("friend_poke", params);
4006
4273
  } else {
4007
- params["group_id"] = session.guildId;
4274
+ params.group_id = session.guildId;
4008
4275
  await session.onebot._request("group_poke", params);
4009
4276
  }
4010
4277
  });
4011
- ctx.platform("onebot").on("notice", async (session) => {
4012
- if (session.subtype != "poke") {
4278
+ }
4279
+ __name(registerPokeCommand, "registerPokeCommand");
4280
+
4281
+ // src/fun/poke/utils/stamp.ts
4282
+ var fs4 = __toESM(require("fs"), 1);
4283
+ var path3 = __toESM(require("path"), 1);
4284
+ var import_koishi8 = require("koishi");
4285
+
4286
+ // src/fun/poke/utils/file.ts
4287
+ var fs3 = __toESM(require("fs"), 1);
4288
+ var path2 = __toESM(require("path"), 1);
4289
+
4290
+ // src/fun/poke/constants.ts
4291
+ var IMAGE_EXTENSIONS = [".png", ".jpg", ".jpeg", ".gif", ".webp"];
4292
+ var MIME_MAP = {
4293
+ ".png": "image/png",
4294
+ ".jpg": "image/jpeg",
4295
+ ".jpeg": "image/jpeg",
4296
+ ".gif": "image/gif",
4297
+ ".webp": "image/webp"
4298
+ };
4299
+ var DEFAULT_MIME = "image/png";
4300
+ var DEFAULT_LOCALE = "zh-CN";
4301
+ var TIP_POOL = {
4302
+ "zh-CN": [
4303
+ "你知道吗: 《FIN4LE ~終止線の彼方へ~》在发布 337 天后才出现首个 PUC。",
4304
+ "你知道吗: Near 是双子中的姐姐哟~",
4305
+ "你知道吗: Noah 是双子中的妹妹哟~",
4306
+ "你知道吗: 可以看刘海方向来区分 Near 和 Noah。",
4307
+ "你知道吗: Near 和 Noah 就读于ボルテ学園初等部。",
4308
+ "你知道吗: 元气满满的是姐姐 Near,文静的是妹妹 Noah。",
4309
+ "你知道吗: Near 和 Noah 的兔子鞋是烈风刀送给她们的。",
4310
+ "你知道吗: Near 和 Noah 都会飞哟~",
4311
+ "你知道吗: Near 和 Noah 初登场于 BOOTH 代歌曲《freaky freak》。",
4312
+ "你知道吗: Near 和 Noah 的 CV 为 日高里菜。",
4313
+ "你知道吗: Near 和 Noah 的身高是 140 cm。",
4314
+ "你知道吗: Near 和 Noah 的生日是 6 月 10 日。"
4315
+ ],
4316
+ "en-US": [
4317
+ "Do you know: FIN4LE ~終止線の彼方へ~ took 337 days to get its first PUC.",
4318
+ "Do you know: Near is the older sister of the twin.",
4319
+ "Do you know: Noah is the younger sister of the twin.",
4320
+ "Do you know: You can tell Near and Noah apart by their hair direction.",
4321
+ "Do you know: Near and Noah attend ボルテ学園初等部.",
4322
+ "Do you know: The energetic Near is the older sister, while the quiet Noah is the younger sister.",
4323
+ "Do you know: Near and Noah received their rabbit shoes from 烈风刀.",
4324
+ "Do you know: Near and Noah can fly.",
4325
+ 'Do you know: Near and Noah made their debut in the BOOTH song "freaky freak".',
4326
+ "Do you know: Near and Noah's CV is 日高里菜.",
4327
+ "Do you know: Near and Noah's height is 140 cm.",
4328
+ "Do you know: Near and Noah's birthday is June 10th."
4329
+ ]
4330
+ };
4331
+
4332
+ // src/fun/poke/utils/file.ts
4333
+ function isImageFile(filename) {
4334
+ const ext = path2.extname(filename).toLowerCase();
4335
+ return IMAGE_EXTENSIONS.includes(ext);
4336
+ }
4337
+ __name(isImageFile, "isImageFile");
4338
+ function getMimeType(filename) {
4339
+ const ext = path2.extname(filename).toLowerCase();
4340
+ return MIME_MAP[ext] ?? DEFAULT_MIME;
4341
+ }
4342
+ __name(getMimeType, "getMimeType");
4343
+ function findMatchingAudio(imagePath) {
4344
+ try {
4345
+ const dir = path2.dirname(imagePath);
4346
+ const filename = path2.basename(imagePath);
4347
+ const match = filename.match(/_(\d+)\.(png|jpg|jpeg|gif|webp)$/i);
4348
+ if (!match) {
4349
+ return null;
4350
+ }
4351
+ const audioNumber = parseInt(match[1], 10).toString();
4352
+ const files = fs3.readdirSync(dir);
4353
+ const audioFile = files.find((file) => file.endsWith(`-${audioNumber}.mp3`));
4354
+ return audioFile ? path2.join(dir, audioFile) : null;
4355
+ } catch {
4356
+ return null;
4357
+ }
4358
+ }
4359
+ __name(findMatchingAudio, "findMatchingAudio");
4360
+
4361
+ // src/fun/poke/utils/random.ts
4362
+ function randomMessage(items) {
4363
+ if (items.length === 0) {
4364
+ return void 0;
4365
+ }
4366
+ const totalWeight = items.reduce((sum2, cur) => sum2 + cur.weight, 0);
4367
+ const random = Math.random() * totalWeight;
4368
+ let sum = 0;
4369
+ for (const item of items) {
4370
+ sum += item.weight;
4371
+ if (random < sum) return item;
4372
+ }
4373
+ return items[items.length - 1];
4374
+ }
4375
+ __name(randomMessage, "randomMessage");
4376
+ function pickRandom(items) {
4377
+ if (items.length === 0) {
4378
+ return void 0;
4379
+ }
4380
+ return items[Math.floor(Math.random() * items.length)];
4381
+ }
4382
+ __name(pickRandom, "pickRandom");
4383
+
4384
+ // src/fun/poke/utils/stamp.ts
4385
+ var LOGGER_NAME = "Noah-Poke";
4386
+ function collectNoahStamps(stampsPath) {
4387
+ const stampDirs = fs4.readdirSync(stampsPath).filter((dir) => dir.startsWith("stamp_"));
4388
+ const allStamps = [];
4389
+ for (const dir of stampDirs) {
4390
+ const stampDirPath = path3.join(stampsPath, dir);
4391
+ const stampFiles = fs4.readdirSync(stampDirPath).filter((file) => file.startsWith(dir) && isImageFile(file));
4392
+ stampFiles.forEach((file) => {
4393
+ allStamps.push({ path: path3.join(stampDirPath, file), dirName: dir });
4394
+ });
4395
+ }
4396
+ return allStamps;
4397
+ }
4398
+ __name(collectNoahStamps, "collectNoahStamps");
4399
+ async function sendRandomNoahStamp(ctx, session, voiceOnly = false) {
4400
+ const logger6 = ctx.logger(LOGGER_NAME);
4401
+ try {
4402
+ const stampsPath = getAssetPath(ctx, "stamps/noah_stamp");
4403
+ const allStamps = collectNoahStamps(stampsPath);
4404
+ if (allStamps.length === 0) {
4405
+ await session.sendQueued("No stamps found!");
4013
4406
  return;
4014
4407
  }
4015
- if (session.targetId != session.selfId) {
4408
+ const availableStamps = voiceOnly ? allStamps.filter((stamp) => {
4409
+ const audioPath2 = findMatchingAudio(stamp.path);
4410
+ return audioPath2 && fs4.existsSync(audioPath2);
4411
+ }) : allStamps;
4412
+ if (availableStamps.length === 0) {
4413
+ await session.sendQueued("No stamps with voice found!");
4016
4414
  return;
4017
4415
  }
4018
- if (pokeConfig2.interval > 0 && cache.has(session.userId)) {
4416
+ const randomStamp = pickRandom(availableStamps);
4417
+ logger6.debug(`Selected stamp from ${randomStamp.dirName}`);
4418
+ if (!fs4.existsSync(randomStamp.path)) {
4419
+ await session.sendQueued("Selected stamp not found!");
4420
+ return;
4421
+ }
4422
+ const imageBuffer = fs4.readFileSync(randomStamp.path);
4423
+ await session.sendQueued(import_koishi8.h.image(imageBuffer, getMimeType(randomStamp.path)));
4424
+ const audioPath = findMatchingAudio(randomStamp.path);
4425
+ if (audioPath && fs4.existsSync(audioPath)) {
4426
+ logger6.debug(`Found matching audio: ${path3.basename(audioPath)}`);
4427
+ const audioBuffer = fs4.readFileSync(audioPath);
4428
+ await session.sendQueued(import_koishi8.h.audio(audioBuffer, "audio/mpeg"));
4429
+ }
4430
+ } catch (error) {
4431
+ logger6.error(`Error sending noah stamp: ${error.message}`);
4432
+ await session.sendQueued("Failed to send noah stamp.");
4433
+ }
4434
+ }
4435
+ __name(sendRandomNoahStamp, "sendRandomNoahStamp");
4436
+ function getRandomChatStamp(ctx) {
4437
+ const logger6 = ctx.logger(LOGGER_NAME);
4438
+ try {
4439
+ const stampsPath = getAssetPath(ctx, "stamps/chat_stamp");
4440
+ const stampFiles = fs4.readdirSync(stampsPath).filter((file) => isImageFile(file));
4441
+ if (stampFiles.length === 0) {
4442
+ return "No chat stamps found!";
4443
+ }
4444
+ const randomStamp = pickRandom(stampFiles);
4445
+ const stampPath = path3.join(stampsPath, randomStamp);
4446
+ if (!fs4.existsSync(stampPath)) {
4447
+ return "Selected stamp not found!";
4448
+ }
4449
+ const imageBuffer = fs4.readFileSync(stampPath);
4450
+ return import_koishi8.h.image(imageBuffer, getMimeType(stampPath));
4451
+ } catch (error) {
4452
+ logger6.error(`Error sending chat stamp: ${error.message}`);
4453
+ return "Failed to send chat stamp.";
4454
+ }
4455
+ }
4456
+ __name(getRandomChatStamp, "getRandomChatStamp");
4457
+
4458
+ // src/fun/poke/commands/stamp.ts
4459
+ function registerStampCommand(ctx) {
4460
+ ctx.command("stamp").option("type", "-t [type]").option("voice", "-v", { fallback: false }).action(async ({ session, options }) => {
4461
+ if (options.type === "noah") {
4462
+ await sendRandomNoahStamp(ctx, session, options.voice);
4463
+ return;
4464
+ }
4465
+ return getRandomChatStamp(ctx);
4466
+ });
4467
+ }
4468
+ __name(registerStampCommand, "registerStampCommand");
4469
+
4470
+ // src/fun/poke/events/notice.ts
4471
+ var import_koishi9 = require("koishi");
4472
+
4473
+ // src/fun/poke/utils/tip.ts
4474
+ async function pickTip(session) {
4475
+ const user = await session.observeUser(["locales"]);
4476
+ const locales = user.locales ?? [];
4477
+ for (const locale2 of locales) {
4478
+ const tips = TIP_POOL[locale2];
4479
+ if (tips && tips.length > 0) {
4480
+ return pickRandom(tips);
4481
+ }
4482
+ }
4483
+ return pickRandom(TIP_POOL[DEFAULT_LOCALE] ?? []);
4484
+ }
4485
+ __name(pickTip, "pickTip");
4486
+
4487
+ // src/fun/poke/events/notice.ts
4488
+ function registerPokeNotice(ctx, config, cache) {
4489
+ ctx.platform("onebot").on("notice", async (session) => {
4490
+ if (session.subtype !== "poke" || session.targetId !== session.selfId) {
4491
+ return;
4492
+ }
4493
+ if (config.interval > 0 && cache.has(session.userId)) {
4019
4494
  const ts = cache.get(session.userId);
4020
- if (session.timestamp - ts < pokeConfig2.interval) {
4021
- if (pokeConfig2.warning) {
4022
- const msg = randomMessage(pokeConfig2.prompt);
4023
- const content = import_koishi8.h.parse(msg, session);
4024
- session.sendQueued(content);
4495
+ if (session.timestamp - ts < config.interval) {
4496
+ if (config.warning) {
4497
+ const msg2 = randomMessage(config.prompt);
4498
+ if (msg2) {
4499
+ await session.sendQueued(import_koishi9.h.parse(msg2.content, session));
4500
+ }
4025
4501
  }
4026
4502
  return;
4027
4503
  }
4028
4504
  }
4029
4505
  cache.set(session.userId, session.timestamp);
4030
- if (pokeConfig2.messages.length > 0) {
4031
- const msg = randomMessage(pokeConfig2.messages);
4032
- const content = import_koishi8.h.parse(msg, session);
4033
- await session.sendQueued(content);
4506
+ if (config.messages.length === 0) {
4507
+ return;
4508
+ }
4509
+ const msg = randomMessage(config.messages);
4510
+ if (!msg) {
4511
+ return;
4512
+ }
4513
+ if (msg.type === "noah") {
4514
+ await sendRandomNoahStamp(ctx, session);
4515
+ } else if (msg.type === "chat") {
4516
+ await session.sendQueued(getRandomChatStamp(ctx));
4517
+ } else {
4518
+ const tip = await pickTip(session);
4519
+ if (tip) {
4520
+ await session.sendQueued(import_koishi9.h.parse(tip, session));
4521
+ }
4034
4522
  }
4035
4523
  });
4036
4524
  }
4037
- __name(apply7, "apply");
4038
- function randomMessage(messages) {
4039
- const totalWeight = messages.reduce((sum2, cur) => sum2 + cur.weight, 0);
4040
- const random = Math.random() * totalWeight;
4041
- let sum = 0;
4042
- for (const message of messages) {
4043
- sum += message.weight;
4044
- if (random < sum) return message.content;
4045
- }
4046
- }
4047
- __name(randomMessage, "randomMessage");
4048
- function parsePlatform(target) {
4049
- const index = target.indexOf(":");
4050
- const platform = target.slice(0, index);
4051
- const id = target.slice(index + 1);
4052
- return [platform, id];
4525
+ __name(registerPokeNotice, "registerPokeNotice");
4526
+
4527
+ // src/fun/poke/locales/en-US.yml
4528
+ var en_US_default3 = { _config: { $desc: "Poke Module Settings", interval: "最小触发间隔(毫秒)", warning: "频繁触发是否发送警告", prompt: "警告内容", messages: { $desc: "消息内容", type: "Type (text=random tip / noah=voice stamp / chat=chat stamp)", weight: "权重" } }, commands: { poke: { description: "poke" } } };
4529
+
4530
+ // src/fun/poke/locales/zh-CN.yml
4531
+ var zh_CN_default3 = { _config: { $desc: "Poke 模块设置", interval: "最小触发间隔(毫秒)", warning: "频繁触发是否发送警告", prompt: { $desc: "警告内容", content: "消息", weight: "权重" }, messages: { $desc: "消息内容", type: "类型(text=随机提示 / noah=语音表情 / chat=聊天表情)", weight: "权重" } }, commands: { poke: { description: "戳一戳" } } };
4532
+
4533
+ // src/fun/poke/index.ts
4534
+ var name7 = "Noah-Poke";
4535
+ var logger3 = new import_koishi10.Logger("Noah-Poke");
4536
+ function apply7(ctx, config) {
4537
+ ;
4538
+ [
4539
+ ["en-US", en_US_default3],
4540
+ ["zh-CN", zh_CN_default3]
4541
+ ].forEach(([lang, file]) => ctx.i18n.define(lang, file));
4542
+ const cache = /* @__PURE__ */ new Map();
4543
+ registerStampCommand(ctx);
4544
+ registerPokeCommand(ctx);
4545
+ registerPokeNotice(ctx, config.poke, cache);
4053
4546
  }
4054
- __name(parsePlatform, "parsePlatform");
4547
+ __name(apply7, "apply");
4055
4548
 
4056
4549
  // src/games/general/index.ts
4057
4550
  var general_exports = {};
4058
4551
  __export(general_exports, {
4059
4552
  apply: () => apply8,
4060
- logger: () => logger3,
4553
+ logger: () => logger4,
4061
4554
  name: () => name8
4062
4555
  });
4063
- var import_koishi10 = require("koishi");
4556
+ var import_koishi12 = require("koishi");
4064
4557
 
4065
4558
  // src/games/general/events/quote.ts
4066
- var import_koishi9 = require("koishi");
4559
+ var import_koishi11 = require("koishi");
4067
4560
 
4068
4561
  // src/games/general/utils/codeReader.ts
4069
4562
  async function readCode128(ctx, barcodeApiUrl, imageUrl) {
@@ -4089,8 +4582,8 @@ __name(readCode128, "readCode128");
4089
4582
  function quote(ctx, config) {
4090
4583
  ctx.on("message", async (session) => {
4091
4584
  if (session.quote && session.quote.user.id === session.selfId) {
4092
- const images = await import_koishi9.h.select(session.quote.elements, "img");
4093
- const textElements = await import_koishi9.h.select(session.elements, "text");
4585
+ const images = await import_koishi11.h.select(session.quote.elements, "img");
4586
+ const textElements = await import_koishi11.h.select(session.elements, "text");
4094
4587
  const allText = textElements.map((el) => el.attrs?.content || "").join(" ");
4095
4588
  if (images.length === 1) {
4096
4589
  const imageUrl = images[0].attrs.src;
@@ -4116,7 +4609,7 @@ __name(quote, "quote");
4116
4609
 
4117
4610
  // src/games/general/index.ts
4118
4611
  var name8 = "Noah-General";
4119
- var logger3 = new import_koishi10.Logger("Noah-General");
4612
+ var logger4 = new import_koishi12.Logger("Noah-General");
4120
4613
  async function apply8(ctx, config) {
4121
4614
  quote(ctx, config.general);
4122
4615
  }
@@ -4127,10 +4620,10 @@ var sdvx_exports = {};
4127
4620
  __export(sdvx_exports, {
4128
4621
  apply: () => apply13,
4129
4622
  inject: () => inject3,
4130
- logger: () => logger4,
4623
+ logger: () => logger5,
4131
4624
  name: () => name13
4132
4625
  });
4133
- var import_koishi18 = require("koishi");
4626
+ var import_koishi21 = require("koishi");
4134
4627
 
4135
4628
  // src/servers/index.ts
4136
4629
  var servers_exports = {};
@@ -4142,7 +4635,7 @@ __export(servers_exports, {
4142
4635
  });
4143
4636
 
4144
4637
  // src/servers/Asphyxia/index.ts
4145
- var import_koishi11 = require("koishi");
4638
+ var import_koishi13 = require("koishi");
4146
4639
  var Asphyxia = class {
4147
4640
  static {
4148
4641
  __name(this, "Asphyxia");
@@ -4150,11 +4643,11 @@ var Asphyxia = class {
4150
4643
  name = "asphyxia";
4151
4644
  supportedGames = ["sdvx", "iidx"];
4152
4645
  gameServices = {};
4153
- logger = new import_koishi11.Logger("Noah-Asphyxia");
4646
+ logger = new import_koishi13.Logger("Noah-Asphyxia");
4154
4647
  };
4155
4648
 
4156
4649
  // src/servers/Mao/index.ts
4157
- var import_koishi12 = require("koishi");
4650
+ var import_koishi14 = require("koishi");
4158
4651
  var Mao = class {
4159
4652
  static {
4160
4653
  __name(this, "Mao");
@@ -4162,11 +4655,11 @@ var Mao = class {
4162
4655
  name = "mao";
4163
4656
  supportedGames = ["sdvx"];
4164
4657
  gameServices = {};
4165
- logger = new import_koishi12.Logger("Noah-Mao");
4658
+ logger = new import_koishi14.Logger("Noah-Mao");
4166
4659
  };
4167
4660
 
4168
4661
  // src/servers/Official/index.ts
4169
- var import_koishi13 = require("koishi");
4662
+ var import_koishi15 = require("koishi");
4170
4663
  var Official = class {
4171
4664
  static {
4172
4665
  __name(this, "Official");
@@ -4174,7 +4667,7 @@ var Official = class {
4174
4667
  name = "official";
4175
4668
  supportedGames = ["sdvx"];
4176
4669
  gameServices = {};
4177
- logger = new import_koishi13.Logger("Noah-Official");
4670
+ logger = new import_koishi15.Logger("Noah-Official");
4178
4671
  };
4179
4672
 
4180
4673
  // src/servers/ServerFactory.ts
@@ -4542,13 +5035,13 @@ var AsphyxiaSDVXService = class _AsphyxiaSDVXService {
4542
5035
  logger;
4543
5036
  config;
4544
5037
  cachedModel = null;
4545
- constructor(logger5, config) {
4546
- this.logger = logger5;
5038
+ constructor(logger6, config) {
5039
+ this.logger = logger6;
4547
5040
  this.config = config;
4548
5041
  }
4549
- static getInstance(logger5, config) {
5042
+ static getInstance(logger6, config) {
4550
5043
  if (!_AsphyxiaSDVXService.instance) {
4551
- _AsphyxiaSDVXService.instance = new _AsphyxiaSDVXService(logger5, config);
5044
+ _AsphyxiaSDVXService.instance = new _AsphyxiaSDVXService(logger6, config);
4552
5045
  }
4553
5046
  return _AsphyxiaSDVXService.instance;
4554
5047
  }
@@ -4695,12 +5188,12 @@ var MaoSDVXService = class _MaoSDVXService {
4695
5188
  }
4696
5189
  static instance;
4697
5190
  logger;
4698
- constructor(logger5) {
4699
- this.logger = logger5;
5191
+ constructor(logger6) {
5192
+ this.logger = logger6;
4700
5193
  }
4701
- static getInstance(logger5) {
5194
+ static getInstance(logger6) {
4702
5195
  if (!_MaoSDVXService.instance) {
4703
- _MaoSDVXService.instance = new _MaoSDVXService(logger5);
5196
+ _MaoSDVXService.instance = new _MaoSDVXService(logger6);
4704
5197
  }
4705
5198
  return _MaoSDVXService.instance;
4706
5199
  }
@@ -4994,7 +5487,7 @@ var MaoSDVXService = class _MaoSDVXService {
4994
5487
  baseURL: ctx.globalConfig.maoServerUrl,
4995
5488
  headers: { "X-API-Key": apiKey }
4996
5489
  });
4997
- if (resp?.code !== 0 || !resp?.data || resp.data.length === 0) {
5490
+ if (resp?.code !== 0 || !Array.isArray(resp?.data) || resp.data.length === 0) {
4998
5491
  return [];
4999
5492
  }
5000
5493
  const musicIds = resp.data.map((item) => item.mid);
@@ -5045,12 +5538,12 @@ var OfficialSDVXService = class _OfficialSDVXService {
5045
5538
  }
5046
5539
  static instance;
5047
5540
  logger;
5048
- constructor(logger5) {
5049
- this.logger = logger5;
5541
+ constructor(logger6) {
5542
+ this.logger = logger6;
5050
5543
  }
5051
- static getInstance(logger5) {
5544
+ static getInstance(logger6) {
5052
5545
  if (!_OfficialSDVXService.instance) {
5053
- _OfficialSDVXService.instance = new _OfficialSDVXService(logger5);
5546
+ _OfficialSDVXService.instance = new _OfficialSDVXService(logger6);
5054
5547
  }
5055
5548
  return _OfficialSDVXService.instance;
5056
5549
  }
@@ -5155,15 +5648,15 @@ var OfficialSDVXService = class _OfficialSDVXService {
5155
5648
  };
5156
5649
 
5157
5650
  // src/games/sdvx/adapters/index.ts
5158
- function registerSDVXAdapters(logger5, config) {
5651
+ function registerSDVXAdapters(logger6, config) {
5159
5652
  const serverManager = ServerManager.getInstance();
5160
5653
  serverManager.registerGameService(
5161
5654
  "asphyxia",
5162
5655
  "sdvx",
5163
- AsphyxiaSDVXService.getInstance(logger5, config)
5656
+ AsphyxiaSDVXService.getInstance(logger6, config)
5164
5657
  );
5165
- serverManager.registerGameService("mao", "sdvx", MaoSDVXService.getInstance(logger5));
5166
- serverManager.registerGameService("official", "sdvx", OfficialSDVXService.getInstance(logger5));
5658
+ serverManager.registerGameService("mao", "sdvx", MaoSDVXService.getInstance(logger6));
5659
+ serverManager.registerGameService("official", "sdvx", OfficialSDVXService.getInstance(logger6));
5167
5660
  }
5168
5661
  __name(registerSDVXAdapters, "registerSDVXAdapters");
5169
5662
 
@@ -5175,7 +5668,7 @@ __export(command_exports2, {
5175
5668
  });
5176
5669
 
5177
5670
  // src/games/sdvx/commands/calculate.ts
5178
- var import_koishi14 = require("koishi");
5671
+ var import_koishi16 = require("koishi");
5179
5672
 
5180
5673
  // src/games/sdvx/utils/param-parser.ts
5181
5674
  function parseNumberWithSuffix(str) {
@@ -5363,6 +5856,249 @@ function parseRadarFeature(item) {
5363
5856
  return null;
5364
5857
  }
5365
5858
  __name(parseRadarFeature, "parseRadarFeature");
5859
+ function extractOperator(item) {
5860
+ if (item.startsWith(">=")) return { op: ">=", value: item.slice(2) };
5861
+ if (item.startsWith("<=")) return { op: "<=", value: item.slice(2) };
5862
+ if (item.startsWith("!=")) return { op: "!=", value: item.slice(2) };
5863
+ if (item.startsWith(">")) return { op: ">", value: item.slice(1) };
5864
+ if (item.startsWith("<")) return { op: "<", value: item.slice(1) };
5865
+ if (item.startsWith("=")) return { op: "=", value: item.slice(1) };
5866
+ if (item.startsWith("~")) return { op: "~", value: item.slice(1) };
5867
+ return { op: null, value: item };
5868
+ }
5869
+ __name(extractOperator, "extractOperator");
5870
+ function getAllValidLevels2() {
5871
+ const levels = [];
5872
+ for (let i = 1; i <= 16; i++) levels.push(i);
5873
+ levels.push(17, 17.5);
5874
+ for (let scaled = 180; scaled <= 209; scaled++) levels.push(scaled / 10);
5875
+ return levels;
5876
+ }
5877
+ __name(getAllValidLevels2, "getAllValidLevels");
5878
+ function applyLevelOperator(op, levels) {
5879
+ const allLevels = getAllValidLevels2();
5880
+ const value = levels[0];
5881
+ if (op === "=") return { include: levels, exclude: [] };
5882
+ if (op === "!=") return { include: [], exclude: levels };
5883
+ if (op === "~") {
5884
+ const result = /* @__PURE__ */ new Set();
5885
+ for (const lv of levels) {
5886
+ const idx2 = allLevels.indexOf(lv);
5887
+ if (idx2 === -1) continue;
5888
+ if (idx2 > 0) result.add(allLevels[idx2 - 1]);
5889
+ result.add(allLevels[idx2]);
5890
+ if (idx2 < allLevels.length - 1) result.add(allLevels[idx2 + 1]);
5891
+ }
5892
+ return { include: [...result], exclude: [] };
5893
+ }
5894
+ const idx = allLevels.indexOf(value);
5895
+ if (idx === -1) return { include: levels, exclude: [] };
5896
+ switch (op) {
5897
+ case ">":
5898
+ return { include: allLevels.slice(idx + 1), exclude: [] };
5899
+ case ">=":
5900
+ return { include: allLevels.slice(idx), exclude: [] };
5901
+ case "<":
5902
+ return { include: allLevels.slice(0, idx), exclude: [] };
5903
+ case "<=":
5904
+ return { include: allLevels.slice(0, idx + 1), exclude: [] };
5905
+ }
5906
+ }
5907
+ __name(applyLevelOperator, "applyLevelOperator");
5908
+ function applyScoreOperator(op, scores) {
5909
+ const value = scores[0];
5910
+ if (op === "=") return { include: [value], exclude: [] };
5911
+ if (op === "!=") return { include: [], exclude: [value] };
5912
+ if (op === "~") {
5913
+ return { include: [value - 5e4, value + 5e4], exclude: [] };
5914
+ }
5915
+ switch (op) {
5916
+ case ">":
5917
+ return { include: [value + 1, 1e7], exclude: [] };
5918
+ case ">=":
5919
+ return { include: [value, 1e7], exclude: [] };
5920
+ case "<":
5921
+ return { include: [0, value - 1], exclude: [] };
5922
+ case "<=":
5923
+ return { include: [0, value], exclude: [] };
5924
+ }
5925
+ }
5926
+ __name(applyScoreOperator, "applyScoreOperator");
5927
+ function applyVfOperator(op, vfValue) {
5928
+ if (op === "=") return { include: vfValue, exclude: [] };
5929
+ if (op === "!=") return { include: [], exclude: [vfValue] };
5930
+ if (op === "~") {
5931
+ return { include: [vfValue - 0.25, vfValue + 0.25], exclude: [] };
5932
+ }
5933
+ switch (op) {
5934
+ case ">":
5935
+ return { include: [vfValue + 0.05, 99], exclude: [] };
5936
+ case ">=":
5937
+ return { include: [vfValue, 99], exclude: [] };
5938
+ case "<":
5939
+ return { include: [0, vfValue - 0.05], exclude: [] };
5940
+ case "<=":
5941
+ return { include: [0, vfValue], exclude: [] };
5942
+ }
5943
+ }
5944
+ __name(applyVfOperator, "applyVfOperator");
5945
+ function applyGradeOperator(op, grade) {
5946
+ const idx = ALL_GRADES.indexOf(grade);
5947
+ if (idx === -1) return { include: [grade], exclude: [] };
5948
+ if (op === "=") return { include: [grade], exclude: [] };
5949
+ if (op === "!=") return { include: [], exclude: [grade] };
5950
+ if (op === "~") {
5951
+ const result = [];
5952
+ if (idx > 0) result.push(ALL_GRADES[idx - 1]);
5953
+ result.push(ALL_GRADES[idx]);
5954
+ if (idx < ALL_GRADES.length - 1) result.push(ALL_GRADES[idx + 1]);
5955
+ return { include: result, exclude: [] };
5956
+ }
5957
+ switch (op) {
5958
+ case ">":
5959
+ return { include: ALL_GRADES.slice(0, idx), exclude: [] };
5960
+ case ">=":
5961
+ return { include: ALL_GRADES.slice(0, idx + 1), exclude: [] };
5962
+ case "<":
5963
+ return { include: ALL_GRADES.slice(idx + 1), exclude: [] };
5964
+ case "<=":
5965
+ return { include: ALL_GRADES.slice(idx), exclude: [] };
5966
+ }
5967
+ }
5968
+ __name(applyGradeOperator, "applyGradeOperator");
5969
+ function applyClearTypeOperator(op, clearType) {
5970
+ const idx = ALL_CLEAR_TYPES.indexOf(clearType);
5971
+ if (idx === -1) return { include: [clearType], exclude: [] };
5972
+ if (op === "=") return { include: [clearType], exclude: [] };
5973
+ if (op === "!=") return { include: [], exclude: [clearType] };
5974
+ if (op === "~") {
5975
+ const result = [];
5976
+ if (idx > 0) result.push(ALL_CLEAR_TYPES[idx - 1]);
5977
+ result.push(ALL_CLEAR_TYPES[idx]);
5978
+ if (idx < ALL_CLEAR_TYPES.length - 1) result.push(ALL_CLEAR_TYPES[idx + 1]);
5979
+ return { include: result, exclude: [] };
5980
+ }
5981
+ switch (op) {
5982
+ case ">":
5983
+ return { include: ALL_CLEAR_TYPES.slice(0, idx), exclude: [] };
5984
+ case ">=":
5985
+ return { include: ALL_CLEAR_TYPES.slice(0, idx + 1), exclude: [] };
5986
+ case "<":
5987
+ return { include: ALL_CLEAR_TYPES.slice(idx + 1), exclude: [] };
5988
+ case "<=":
5989
+ return { include: ALL_CLEAR_TYPES.slice(idx), exclude: [] };
5990
+ }
5991
+ }
5992
+ __name(applyClearTypeOperator, "applyClearTypeOperator");
5993
+ function mergeLevels(params, levels) {
5994
+ if (params.level !== void 0) {
5995
+ const existing = Array.isArray(params.level) ? params.level : [params.level];
5996
+ params.level = [.../* @__PURE__ */ new Set([...existing, ...levels])];
5997
+ } else {
5998
+ params.level = levels.length === 1 ? levels[0] : levels;
5999
+ }
6000
+ }
6001
+ __name(mergeLevels, "mergeLevels");
6002
+ function mergeScores(params, scores) {
6003
+ if (params.score !== void 0) {
6004
+ const existing = Array.isArray(params.score) ? params.score : [params.score];
6005
+ params.score = [.../* @__PURE__ */ new Set([...existing, ...scores])];
6006
+ } else {
6007
+ params.score = scores.length === 1 ? scores[0] : scores;
6008
+ }
6009
+ }
6010
+ __name(mergeScores, "mergeScores");
6011
+ function mergeVf(params, vf2) {
6012
+ if (params.vf !== void 0) {
6013
+ const existing = Array.isArray(params.vf) ? params.vf : [params.vf];
6014
+ const newVf = Array.isArray(vf2) ? vf2 : [vf2];
6015
+ params.vf = [...existing, ...newVf];
6016
+ } else {
6017
+ params.vf = vf2;
6018
+ }
6019
+ }
6020
+ __name(mergeVf, "mergeVf");
6021
+ function mergeGrades(params, grades) {
6022
+ if (params.grade) {
6023
+ const existing = Array.isArray(params.grade) ? params.grade : [params.grade];
6024
+ params.grade = [.../* @__PURE__ */ new Set([...existing, ...grades])];
6025
+ } else {
6026
+ params.grade = grades.length === 1 ? grades[0] : grades;
6027
+ }
6028
+ }
6029
+ __name(mergeGrades, "mergeGrades");
6030
+ function mergeClearTypes(params, clearTypes) {
6031
+ if (params.clearType) {
6032
+ const existing = Array.isArray(params.clearType) ? params.clearType : [params.clearType];
6033
+ params.clearType = [.../* @__PURE__ */ new Set([...existing, ...clearTypes])];
6034
+ } else {
6035
+ params.clearType = clearTypes;
6036
+ }
6037
+ }
6038
+ __name(mergeClearTypes, "mergeClearTypes");
6039
+ function parseWithOperator(params, op, value) {
6040
+ const vf2 = parseVfValue(value);
6041
+ if (vf2 !== null) {
6042
+ const vfNum = Array.isArray(vf2) ? vf2[0] : vf2;
6043
+ const result = applyVfOperator(op, vfNum);
6044
+ if (result.exclude.length > 0) {
6045
+ params.excludeVf = [...params.excludeVf || [], ...result.exclude];
6046
+ }
6047
+ if (Array.isArray(result.include) && result.include.length > 0) {
6048
+ mergeVf(params, result.include);
6049
+ } else if (typeof result.include === "number") {
6050
+ mergeVf(params, result.include);
6051
+ }
6052
+ return true;
6053
+ }
6054
+ const levels = parseLevelRange(value);
6055
+ if (levels !== null && levels.length === 1) {
6056
+ const result = applyLevelOperator(op, levels);
6057
+ if (result.exclude.length > 0) {
6058
+ params.excludeLevel = [...params.excludeLevel || [], ...result.exclude];
6059
+ }
6060
+ if (result.include.length > 0) {
6061
+ mergeLevels(params, result.include);
6062
+ }
6063
+ return true;
6064
+ }
6065
+ const scores = parseScoreRange(value);
6066
+ if (scores !== null && scores.length === 1) {
6067
+ const result = applyScoreOperator(op, scores);
6068
+ if (result.exclude.length > 0) {
6069
+ params.excludeScore = [...params.excludeScore || [], ...result.exclude];
6070
+ }
6071
+ if (result.include.length > 0) {
6072
+ mergeScores(params, result.include);
6073
+ }
6074
+ return true;
6075
+ }
6076
+ const singleClearTypes = parseSingleClearType(value);
6077
+ if (singleClearTypes !== null) {
6078
+ const ct = singleClearTypes[0];
6079
+ const result = applyClearTypeOperator(op, ct);
6080
+ if (result.exclude.length > 0) {
6081
+ params.excludeClearType = [...params.excludeClearType || [], ...result.exclude];
6082
+ }
6083
+ if (result.include.length > 0) {
6084
+ mergeClearTypes(params, result.include);
6085
+ }
6086
+ return true;
6087
+ }
6088
+ const singleGrades = parseSingleGrade(value);
6089
+ if (singleGrades !== null) {
6090
+ const result = applyGradeOperator(op, singleGrades[0]);
6091
+ if (result.exclude.length > 0) {
6092
+ params.excludeGrade = [...params.excludeGrade || [], ...result.exclude];
6093
+ }
6094
+ if (result.include.length > 0) {
6095
+ mergeGrades(params, result.include);
6096
+ }
6097
+ return true;
6098
+ }
6099
+ return false;
6100
+ }
6101
+ __name(parseWithOperator, "parseWithOperator");
5366
6102
  function parseQueryInput(input) {
5367
6103
  const params = {};
5368
6104
  if (!input || input.trim() === "") {
@@ -5370,6 +6106,10 @@ function parseQueryInput(input) {
5370
6106
  }
5371
6107
  const items = input.split(/\s+/).map((s) => s.trim()).filter(Boolean);
5372
6108
  for (const item of items) {
6109
+ const { op, value } = extractOperator(item);
6110
+ if (op) {
6111
+ if (parseWithOperator(params, op, value)) continue;
6112
+ }
5373
6113
  const vf2 = parseVfValue(item);
5374
6114
  if (vf2 !== null) {
5375
6115
  if (params.vf !== void 0) {
@@ -5447,7 +6187,7 @@ function parseQueryInput(input) {
5447
6187
  __name(parseQueryInput, "parseQueryInput");
5448
6188
 
5449
6189
  // src/games/sdvx/commands/calculate.ts
5450
- function calculate(ctx, config, logger5) {
6190
+ function calculate(ctx, config, logger6) {
5451
6191
  ctx.command("sdvx.calculate <query:text>").alias("sdvx.cal").action(async ({ session, options }, query) => {
5452
6192
  try {
5453
6193
  let queryInput = query;
@@ -5481,7 +6221,7 @@ function calculate(ctx, config, logger5) {
5481
6221
  lossless: true
5482
6222
  }
5483
6223
  );
5484
- return import_koishi14.h.image(imageBuffer, "image/png");
6224
+ return import_koishi16.h.image(imageBuffer, "image/png");
5485
6225
  } catch (error) {
5486
6226
  ctx.logger("SDVX-Calculate").warn(error);
5487
6227
  return session.text(".error");
@@ -5491,7 +6231,7 @@ function calculate(ctx, config, logger5) {
5491
6231
  __name(calculate, "calculate");
5492
6232
 
5493
6233
  // src/games/sdvx/commands/chart.ts
5494
- var import_koishi15 = require("koishi");
6234
+ var import_koishi17 = require("koishi");
5495
6235
  function mapArrangementToken(tok) {
5496
6236
  const t = tok.toLowerCase();
5497
6237
  if (t === "ran" || t === "random") return "random";
@@ -5574,7 +6314,7 @@ function getHighestDifstr(diffs) {
5574
6314
  return diffs[diffs.length - 1].difstr;
5575
6315
  }
5576
6316
  __name(getHighestDifstr, "getHighestDifstr");
5577
- function chart(ctx, config, logger5) {
6317
+ function chart(ctx, config, logger6) {
5578
6318
  ctx.command("sdvx.chart [query:text]").alias("sdvx.c").action(async ({ session }, query) => {
5579
6319
  if (!query) {
5580
6320
  await session.send(session.text(".prompt"));
@@ -5637,7 +6377,7 @@ function chart(ctx, config, logger5) {
5637
6377
  const res = await ctx.http.post(`${config.sdvx_data_url}/chart`, payload, {
5638
6378
  headers: { "Content-Type": "application/json" }
5639
6379
  });
5640
- return import_koishi15.h.image(res, "image/png");
6380
+ return import_koishi17.h.image(res, "image/png");
5641
6381
  } else {
5642
6382
  const {
5643
6383
  diffStr: parsedDiff,
@@ -5653,7 +6393,7 @@ function chart(ctx, config, logger5) {
5653
6393
  const picked = musicInfo[0];
5654
6394
  const music_id = Number(picked?.id);
5655
6395
  if (!Number.isFinite(music_id)) {
5656
- logger5.warn("search result missing id", picked);
6396
+ logger6.warn("search result missing id", picked);
5657
6397
  return session.text(".error");
5658
6398
  }
5659
6399
  let difstr = null;
@@ -5687,16 +6427,76 @@ function chart(ctx, config, logger5) {
5687
6427
  const res = await ctx.http.post(`${config.sdvx_data_url}/chart`, payload, {
5688
6428
  headers: { "Content-Type": "application/json" }
5689
6429
  });
5690
- return import_koishi15.h.image(res, "image/png");
6430
+ return import_koishi17.h.image(res, "image/png");
5691
6431
  }
5692
6432
  } catch (err) {
5693
- logger5.warn(err);
6433
+ logger6.warn(err);
5694
6434
  return session.text(".error");
5695
6435
  }
5696
6436
  });
5697
6437
  }
5698
6438
  __name(chart, "chart");
5699
6439
 
6440
+ // src/core/utils/selector.ts
6441
+ var import_koishi18 = require("koishi");
6442
+ async function selectCard(session, cardService, uid) {
6443
+ const userCards = await cardService.getCardsByUid(uid);
6444
+ if (userCards.length === 0) {
6445
+ return { ok: false, message: session.text("selector.card-not-found") };
6446
+ }
6447
+ const defaultCard = await cardService.getDefaultCardByUid(uid);
6448
+ const defaultLabel = session.text("selector.default-card");
6449
+ let cardListMsg = "";
6450
+ for (let i = 0; i < userCards.length; i++) {
6451
+ if (defaultCard && userCards[i].id === defaultCard.id) {
6452
+ cardListMsg += `<p>${i + 1}. ${userCards[i].name} &lt; ${defaultLabel}</p>`;
6453
+ } else {
6454
+ cardListMsg += `<p>${i + 1}. ${userCards[i].name}</p>`;
6455
+ }
6456
+ }
6457
+ const msg = import_koishi18.h.unescape(session.text("selector.menu-select", { card_list: cardListMsg }));
6458
+ await session.send(msg);
6459
+ const select = await session.prompt();
6460
+ if (!select) return { ok: false, message: session.text("commands.timeout") };
6461
+ if (select === "q") return { ok: false, message: session.text("selector.quit") };
6462
+ const selectNum = parseInt(select, 10);
6463
+ if (isNaN(selectNum) || selectNum < 1 || selectNum > userCards.length) {
6464
+ return { ok: false, message: session.text("selector.invalid-select") };
6465
+ }
6466
+ const card2 = await cardService.getCardByCode(userCards[selectNum - 1].code);
6467
+ return { ok: true, value: card2 };
6468
+ }
6469
+ __name(selectCard, "selectCard");
6470
+ async function selectServer(session, serverService, uid, channelId) {
6471
+ const servers = await serverService.getSelectableServers(uid, channelId);
6472
+ if (servers.length === 0) {
6473
+ return { ok: false, message: session.text("selector.server-not-found") };
6474
+ }
6475
+ const defaultServer = await serverService.getDefaultServerByUid(uid);
6476
+ const defaultLabel = session.text("selector.default-server");
6477
+ let serverListMsg = "";
6478
+ for (let i = 0; i < servers.length; i++) {
6479
+ if (defaultServer && servers[i].id === defaultServer.id) {
6480
+ serverListMsg += `<p>${i + 1}. ${servers[i].name} (${servers[i].type}) &lt; ${defaultLabel}</p>`;
6481
+ } else {
6482
+ serverListMsg += `<p>${i + 1}. ${servers[i].name} (${servers[i].type})</p>`;
6483
+ }
6484
+ }
6485
+ const msg = import_koishi18.h.unescape(
6486
+ session.text("selector.server-menu-select", { server_list: serverListMsg })
6487
+ );
6488
+ await session.send(msg);
6489
+ const select = await session.prompt();
6490
+ if (!select) return { ok: false, message: session.text("commands.timeout") };
6491
+ if (select === "q") return { ok: false, message: session.text("selector.quit") };
6492
+ const selectNum = parseInt(select, 10);
6493
+ if (isNaN(selectNum) || selectNum < 1 || selectNum > servers.length) {
6494
+ return { ok: false, message: session.text("selector.invalid-select") };
6495
+ }
6496
+ return { ok: true, value: servers[selectNum - 1] };
6497
+ }
6498
+ __name(selectServer, "selectServer");
6499
+
5700
6500
  // src/games/sdvx/services/score-service.ts
5701
6501
  var ScoreService = class _ScoreService {
5702
6502
  static {
@@ -5780,17 +6580,18 @@ var ScoreService = class _ScoreService {
5780
6580
  calcSongRadarContribution(score, category) {
5781
6581
  const { difficulty_data } = score;
5782
6582
  if (!difficulty_data?.radar || !difficulty_data.max_exscore) return 0;
5783
- const L = Math.floor(difficulty_data.difnum);
6583
+ const L = Math.max(1, Math.min(20, Math.floor(difficulty_data.difnum)));
5784
6584
  const R = difficulty_data.radar[category];
5785
6585
  const S = score.music.score;
5786
6586
  const E = score.music.exscore;
5787
6587
  const Emax = difficulty_data.max_exscore;
5788
- const P = Math.min(2 * (L + 31), 100);
6588
+ const P = Math.min(100, Math.max(0, 60 + 2 * L));
5789
6589
  const B = Math.floor(3 * S * R / (4 * 1e7));
5790
- const H = Math.floor((20 - L) / 2);
5791
- const G = Math.max(1e3 + 2 * 2 ** H * (E - Emax), 0);
6590
+ const H = Math.floor(Math.max(1, 21 - L) / 2);
6591
+ const G = Emax > 0 && E <= Emax ? Math.max(0, 1e3 + 2 * (E - Emax) * (1 << H)) : 0;
5792
6592
  const X = Math.floor(R * G / 4e3);
5793
- return P * (B + X);
6593
+ const completedAxis = Math.min(R, B + X);
6594
+ return P * completedAxis;
5794
6595
  }
5795
6596
  /**
5796
6597
  * 计算玩家六维雷达(显示值,如 200.00)
@@ -5819,37 +6620,46 @@ var ScoreService = class _ScoreService {
5819
6620
  };
5820
6621
 
5821
6622
  // src/games/sdvx/commands/radar.ts
5822
- function radar(ctx, config, logger5) {
5823
- ctx.command("sdvx.radar").userFields(["defaultCardId", "defaultServerId", "id"]).channelFields(["defaultServerId", "id"]).action(async ({ session }) => {
6623
+ function radar(ctx, config, logger6) {
6624
+ ctx.command("sdvx.radar").userFields(["id"]).channelFields(["id"]).option("card", "-c").option("server", "-s").action(async ({ session, options }) => {
5824
6625
  const atGuild = session.guildId != null;
5825
6626
  const cardService = new CardService(ctx);
5826
6627
  const serverService = new ServerService(ctx);
6628
+ const channelId = atGuild ? session.channel.id : null;
5827
6629
  const userCards = await cardService.getCardsByUid(session.user.id);
5828
6630
  if (userCards.length === 0) return session.text(".card-not-found");
5829
- const serverRes = await serverService.getSelectableServers(
5830
- session.user.id,
5831
- atGuild ? session.channel.id : null
5832
- );
6631
+ const serverRes = await serverService.getSelectableServers(session.user.id, channelId);
5833
6632
  if (serverRes.length === 0) return session.text(".server-not-found");
5834
- const defaultCard = await cardService.getDefaultCardByUid(session.user.id);
5835
- if (!defaultCard) return session.text(".card-not-found");
5836
- const card2 = await cardService.getCardByCode(defaultCard.code);
6633
+ let card2;
6634
+ if (options.card) {
6635
+ const result = await selectCard(session, cardService, session.user.id);
6636
+ if (result.ok === false) return result.message;
6637
+ card2 = result.value;
6638
+ } else {
6639
+ const defaultCard = await cardService.getDefaultCardByUid(session.user.id);
6640
+ if (!defaultCard) return session.text(".card-not-found");
6641
+ card2 = await cardService.getCardByCode(defaultCard.code);
6642
+ }
5837
6643
  let server2;
5838
- if (card2.defaultServerId != void 0 && card2.defaultServerId != 0)
5839
- server2 = await serverService.getServerById(card2.defaultServerId);
5840
- else {
5841
- const channelDefaultServer = atGuild ? await serverService.getDefaultServerByCid(
6644
+ if (options.server) {
6645
+ const result = await selectServer(
6646
+ session,
6647
+ serverService,
6648
+ session.user.id,
6649
+ channelId
6650
+ );
6651
+ if (result.ok === false) return result.message;
6652
+ server2 = result.value;
6653
+ } else {
6654
+ server2 = await resolveServerForCard(
6655
+ card2,
6656
+ cardService,
6657
+ serverService,
6658
+ session.user.id,
5842
6659
  session.platform,
5843
- session.channel.id
5844
- ) : null;
5845
- if (channelDefaultServer) server2 = channelDefaultServer;
5846
- else {
5847
- const userDefaultServer = await serverService.getDefaultServerByUid(
5848
- session.user.id
5849
- );
5850
- if (userDefaultServer) server2 = userDefaultServer;
5851
- else return session.text(".server-not-found");
5852
- }
6660
+ channelId
6661
+ );
6662
+ if (!server2) return session.text(".server-not-found");
5853
6663
  }
5854
6664
  const serverManager = ServerManager.getInstance();
5855
6665
  const sdvxService = serverManager.getGameService(server2.type, "sdvx");
@@ -5876,71 +6686,56 @@ function radar(ctx, config, logger5) {
5876
6686
  one_hand: radarResult.one_hand.toFixed(2)
5877
6687
  });
5878
6688
  } catch (error) {
5879
- logger5.warn(error);
5880
- return session.text(".error");
6689
+ logger6.warn(error);
6690
+ return session.text(".error", { card: card2.name, server: server2.name });
5881
6691
  }
5882
6692
  });
5883
6693
  }
5884
6694
  __name(radar, "radar");
5885
6695
 
5886
6696
  // src/games/sdvx/commands/recent.ts
5887
- var import_koishi16 = require("koishi");
5888
- function recent(ctx, config, logger5) {
5889
- 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) => {
6697
+ var import_koishi19 = require("koishi");
6698
+ function recent(ctx, config, logger6) {
6699
+ ctx.command("sdvx.recent [count:number]").alias("sdvx.r").userFields(["id"]).channelFields(["id"]).option("lossless", "-l").option("card", "-c").option("server", "-s").action(async ({ session, options }, count) => {
5890
6700
  const atGuild = session.guildId != null;
5891
6701
  if (!count) count = 1;
5892
6702
  const cardService = new CardService(ctx);
5893
6703
  const serverService = new ServerService(ctx);
6704
+ const channelId = atGuild ? session.channel.id : null;
5894
6705
  const userCards = await cardService.getCardsByUid(session.user.id);
5895
6706
  if (userCards.length === 0) return session.text(".card-not-found");
5896
- const serverRes = await serverService.getSelectableServers(
5897
- session.user.id,
5898
- atGuild ? session.channel.id : null
5899
- );
6707
+ const serverRes = await serverService.getSelectableServers(session.user.id, channelId);
5900
6708
  if (serverRes.length === 0) return session.text(".server-not-found");
5901
- let cardCode = "";
5902
- if (!options.card) {
6709
+ let card2;
6710
+ if (options.card) {
6711
+ const result = await selectCard(session, cardService, session.user.id);
6712
+ if (result.ok === false) return result.message;
6713
+ card2 = result.value;
6714
+ } else {
5903
6715
  const defaultCard = await cardService.getDefaultCardByUid(session.user.id);
5904
6716
  if (!defaultCard) return session.text(".card-not-found");
5905
- cardCode = defaultCard.code;
5906
- } else {
5907
- let cardListMsg = "";
5908
- for (let i = 0; i < userCards.length; i++) {
5909
- const defaultCard = await cardService.getDefaultCardByUid(session.user.id);
5910
- if (defaultCard && userCards[i].id === defaultCard.id) {
5911
- cardListMsg += `<p>${i + 1}. ${userCards[i].name} < 默认卡片</p>`;
5912
- } else {
5913
- cardListMsg += `<p>${i + 1}. ${userCards[i].name}</p>`;
5914
- }
5915
- }
5916
- const msg = import_koishi16.h.unescape(session.text(".menu-select", { card_list: cardListMsg })).replace("< 默认卡片", "&lt; 默认卡片");
5917
- await session.send(msg);
5918
- const select = await session.prompt();
5919
- if (!select) return session.text("commands.timeout");
5920
- if (select === "q") return session.text(".quit");
5921
- const selectNum = parseInt(select, 10);
5922
- if (isNaN(selectNum) || selectNum < 1 || selectNum > userCards.length) {
5923
- return session.text(".invalid-select");
5924
- }
5925
- cardCode = userCards[selectNum - 1].code;
6717
+ card2 = await cardService.getCardByCode(defaultCard.code);
5926
6718
  }
5927
- const card2 = await cardService.getCardByCode(cardCode);
5928
6719
  let server2;
5929
- if (card2.defaultServerId != void 0 && card2.defaultServerId != 0)
5930
- server2 = await serverService.getServerById(card2.defaultServerId);
5931
- else {
5932
- const channelDefaultServer = atGuild ? await serverService.getDefaultServerByCid(
6720
+ if (options.server) {
6721
+ const result = await selectServer(
6722
+ session,
6723
+ serverService,
6724
+ session.user.id,
6725
+ channelId
6726
+ );
6727
+ if (result.ok === false) return result.message;
6728
+ server2 = result.value;
6729
+ } else {
6730
+ server2 = await resolveServerForCard(
6731
+ card2,
6732
+ cardService,
6733
+ serverService,
6734
+ session.user.id,
5933
6735
  session.platform,
5934
- session.channel.id
5935
- ) : null;
5936
- if (channelDefaultServer) server2 = channelDefaultServer;
5937
- else {
5938
- const userDefaultServer = await serverService.getDefaultServerByUid(
5939
- session.user.id
5940
- );
5941
- if (userDefaultServer) server2 = userDefaultServer;
5942
- else return session.text(".server-not-found");
5943
- }
6736
+ channelId
6737
+ );
6738
+ if (!server2) return session.text(".server-not-found");
5944
6739
  }
5945
6740
  const serverManager = ServerManager.getInstance();
5946
6741
  const sdvxService = serverManager.getGameService(server2.type, "sdvx");
@@ -5970,19 +6765,19 @@ function recent(ctx, config, logger5) {
5970
6765
  }
5971
6766
  );
5972
6767
  if (options.lossless) {
5973
- return import_koishi16.h.image(imageBuffer, "image/png");
6768
+ return import_koishi19.h.image(imageBuffer, "image/png");
5974
6769
  }
5975
- return import_koishi16.h.image(imageBuffer, "image/jpg");
6770
+ return import_koishi19.h.image(imageBuffer, "image/jpg");
5976
6771
  } catch (error) {
5977
- logger5.warn(error);
5978
- return session.text(".error");
6772
+ logger6.warn(error);
6773
+ return session.text(".error", { card: card2.name, server: server2.name });
5979
6774
  }
5980
6775
  });
5981
6776
  }
5982
6777
  __name(recent, "recent");
5983
6778
 
5984
6779
  // src/games/sdvx/commands/sync.ts
5985
- function sync(ctx, config, logger5) {
6780
+ function sync(ctx, config, logger6) {
5986
6781
  ctx.command("sdvx.sync").userFields(["id"]).channelFields(["id"]).action(async ({ session }) => {
5987
6782
  const serverService = new ServerService(ctx);
5988
6783
  const cardService = new CardService(ctx);
@@ -6057,7 +6852,7 @@ function sync(ctx, config, logger5) {
6057
6852
  const maoSdvxService = serverManager.getGameService("mao", "sdvx");
6058
6853
  const maoVerifyUrl = "https://maomani.cn";
6059
6854
  if (!maoSdvxService || typeof maoSdvxService.verifyPin !== "function") {
6060
- logger5.warn("Mao SDVX service does not support PIN verification");
6855
+ logger6.warn("Mao SDVX service does not support PIN verification");
6061
6856
  return session.text(".pin-verify-error");
6062
6857
  }
6063
6858
  const existingPin = await ctx.database.get("sdvx_pin_verified", {
@@ -6078,7 +6873,7 @@ function sync(ctx, config, logger5) {
6078
6873
  pinVerified = true;
6079
6874
  }
6080
6875
  } catch (error) {
6081
- logger5.warn(error);
6876
+ logger6.warn(error);
6082
6877
  }
6083
6878
  }
6084
6879
  if (!pinVerified) {
@@ -6114,7 +6909,7 @@ function sync(ctx, config, logger5) {
6114
6909
  })
6115
6910
  );
6116
6911
  } catch (error) {
6117
- logger5.warn(error);
6912
+ logger6.warn(error);
6118
6913
  await session.send(session.text(".pin-verify-error"));
6119
6914
  }
6120
6915
  }
@@ -6136,8 +6931,11 @@ function sync(ctx, config, logger5) {
6136
6931
  config
6137
6932
  );
6138
6933
  } catch (error) {
6139
- logger5.warn(error);
6140
- return session.text(".fetch-error");
6934
+ logger6.warn(error);
6935
+ return session.text(".fetch-error", {
6936
+ card: sourceCard.name,
6937
+ server: sourceServer.name
6938
+ });
6141
6939
  }
6142
6940
  if (!scoreList || scoreList.length === 0) {
6143
6941
  return session.text(".no-scores");
@@ -6158,12 +6956,15 @@ function sync(ctx, config, logger5) {
6158
6956
  scoreList
6159
6957
  );
6160
6958
  if (!ok) {
6161
- logger5.warn("Mao SDVX uploadScore returned falsy result");
6959
+ logger6.warn("Mao SDVX uploadScore returned falsy result");
6162
6960
  return session.text(".sync-failed");
6163
6961
  }
6164
6962
  } catch (error) {
6165
- logger5.warn(error);
6166
- return session.text(".sync-error");
6963
+ logger6.warn(error);
6964
+ return session.text(".sync-error", {
6965
+ card: targetCard.name,
6966
+ server: maoServer.name
6967
+ });
6167
6968
  }
6168
6969
  return session.text(".selected-summary", {
6169
6970
  source_server_name: sourceServer.name,
@@ -6177,8 +6978,8 @@ function sync(ctx, config, logger5) {
6177
6978
  __name(sync, "sync");
6178
6979
 
6179
6980
  // src/games/sdvx/commands/vf.ts
6180
- var fs4 = __toESM(require("fs"), 1);
6181
- var import_koishi17 = require("koishi");
6981
+ var fs5 = __toESM(require("fs"), 1);
6982
+ var import_koishi20 = require("koishi");
6182
6983
 
6183
6984
  // src/games/sdvx/utils/filter.ts
6184
6985
  function parseFilterQuery(query) {
@@ -6231,61 +7032,46 @@ function parseFilterQuery(query) {
6231
7032
  __name(parseFilterQuery, "parseFilterQuery");
6232
7033
 
6233
7034
  // src/games/sdvx/commands/vf.ts
6234
- function vf(ctx, config, logger5) {
6235
- 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 }) => {
7035
+ function vf(ctx, config, logger6) {
7036
+ ctx.command("vf").userFields(["id"]).channelFields(["id"]).option("lossless", "-l").option("card", "-c").option("server", "-s").option("filter", "-f <query:text>").action(async ({ session, options }) => {
6236
7037
  const atGuild = session.guildId != null;
6237
7038
  const cardService = new CardService(ctx);
6238
7039
  const serverService = new ServerService(ctx);
7040
+ const channelId = atGuild ? session.channel.id : null;
6239
7041
  const userCards = await cardService.getCardsByUid(session.user.id);
6240
7042
  if (userCards.length === 0) return session.text(".card-not-found");
6241
- const serverRes = await serverService.getSelectableServers(
6242
- session.user.id,
6243
- atGuild ? session.channel.id : null
6244
- );
7043
+ const serverRes = await serverService.getSelectableServers(session.user.id, channelId);
6245
7044
  if (serverRes.length === 0) return session.text(".server-not-found");
6246
- let cardCode = "";
6247
- if (!options.card) {
7045
+ let card2;
7046
+ if (options.card) {
7047
+ const result = await selectCard(session, cardService, session.user.id);
7048
+ if (result.ok === false) return result.message;
7049
+ card2 = result.value;
7050
+ } else {
6248
7051
  const defaultCard = await cardService.getDefaultCardByUid(session.user.id);
6249
7052
  if (!defaultCard) return session.text(".card-not-found");
6250
- cardCode = defaultCard.code;
6251
- } else {
6252
- let cardListMsg = "";
6253
- for (let i = 0; i < userCards.length; i++) {
6254
- const defaultCard = await cardService.getDefaultCardByUid(session.user.id);
6255
- if (defaultCard && userCards[i].id === defaultCard.id) {
6256
- cardListMsg += `<p>${i + 1}. ${userCards[i].name} < 默认卡片</p>`;
6257
- } else {
6258
- cardListMsg += `<p>${i + 1}. ${userCards[i].name}</p>`;
6259
- }
6260
- }
6261
- const msg = import_koishi17.h.unescape(session.text(".menu-select", { card_list: cardListMsg })).replace("< 默认卡片", "&lt; 默认卡片");
6262
- await session.send(msg);
6263
- const select = await session.prompt();
6264
- if (!select) return session.text("commands.timeout");
6265
- if (select === "q") return session.text(".quit");
6266
- const selectNum = parseInt(select, 10);
6267
- if (isNaN(selectNum) || selectNum < 1 || selectNum > userCards.length) {
6268
- return session.text(".invalid-select");
6269
- }
6270
- cardCode = userCards[selectNum - 1].code;
7053
+ card2 = await cardService.getCardByCode(defaultCard.code);
6271
7054
  }
6272
- const card2 = await cardService.getCardByCode(cardCode);
6273
7055
  let server2;
6274
- if (card2.defaultServerId != void 0 && card2.defaultServerId != 0)
6275
- server2 = await serverService.getServerById(card2.defaultServerId);
6276
- else {
6277
- const channelDefaultServer = atGuild ? await serverService.getDefaultServerByCid(
7056
+ if (options.server) {
7057
+ const result = await selectServer(
7058
+ session,
7059
+ serverService,
7060
+ session.user.id,
7061
+ channelId
7062
+ );
7063
+ if (result.ok === false) return result.message;
7064
+ server2 = result.value;
7065
+ } else {
7066
+ server2 = await resolveServerForCard(
7067
+ card2,
7068
+ cardService,
7069
+ serverService,
7070
+ session.user.id,
6278
7071
  session.platform,
6279
- session.channel.id
6280
- ) : null;
6281
- if (channelDefaultServer) server2 = channelDefaultServer;
6282
- else {
6283
- const userDefaultServer = await serverService.getDefaultServerByUid(
6284
- session.user.id
6285
- );
6286
- if (userDefaultServer) server2 = userDefaultServer;
6287
- else return session.text(".server-not-found");
6288
- }
7072
+ channelId
7073
+ );
7074
+ if (!server2) return session.text(".server-not-found");
6289
7075
  }
6290
7076
  const serverManager = ServerManager.getInstance();
6291
7077
  const sdvxService = serverManager.getGameService(server2.type, "sdvx");
@@ -6321,9 +7107,9 @@ function vf(ctx, config, logger5) {
6321
7107
  }
6322
7108
  );
6323
7109
  if (options.lossless) {
6324
- session.send(import_koishi17.h.file(imageBuffer, "image/png"));
7110
+ session.send(import_koishi20.h.file(imageBuffer, "image/png"));
6325
7111
  } else {
6326
- session.send(import_koishi17.h.image(imageBuffer, "image/jpg"));
7112
+ session.send(import_koishi20.h.image(imageBuffer, "image/jpg"));
6327
7113
  }
6328
7114
  const sortedScores = [...best50ScoreList].sort((a, b) => b.extra.volforce - a.extra.volforce).slice(0, 50);
6329
7115
  const total = sortedScores.reduce((sum, score) => sum + score.extra.volforce, 0);
@@ -6350,21 +7136,21 @@ function vf(ctx, config, logger5) {
6350
7136
  ctx,
6351
7137
  "stamps/noah_stamp/stamp_0385/stamp_0385_02.png"
6352
7138
  );
6353
- if (fs4.existsSync(audioPath)) {
6354
- const audioBuffer = fs4.readFileSync(audioPath);
6355
- session.send(import_koishi17.h.audio(audioBuffer, "audio/mpeg"));
7139
+ if (fs5.existsSync(audioPath)) {
7140
+ const audioBuffer = fs5.readFileSync(audioPath);
7141
+ session.send(import_koishi20.h.audio(audioBuffer, "audio/mpeg"));
6356
7142
  }
6357
- if (fs4.existsSync(imagePath)) {
6358
- const imageBuffer2 = fs4.readFileSync(imagePath);
6359
- session.send(import_koishi17.h.image(imageBuffer2, "image/png"));
7143
+ if (fs5.existsSync(imagePath)) {
7144
+ const imageBuffer2 = fs5.readFileSync(imagePath);
7145
+ session.send(import_koishi20.h.image(imageBuffer2, "image/png"));
6360
7146
  }
6361
7147
  } catch (error) {
6362
- logger5.warn(`Failed to send celebration files: ${error.message}`);
7148
+ logger6.warn(`Failed to send celebration files: ${error.message}`);
6363
7149
  }
6364
7150
  }
6365
7151
  } catch (error) {
6366
- logger5.warn(error);
6367
- return session.text(".error");
7152
+ logger6.warn(error);
7153
+ return session.text(".error", { card: card2.name, server: server2.name });
6368
7154
  }
6369
7155
  return;
6370
7156
  });
@@ -6375,12 +7161,12 @@ __name(vf, "vf");
6375
7161
  var name10 = "command";
6376
7162
  function apply10(ctx, config) {
6377
7163
  ctx.command("sdvx").alias("s");
6378
- vf(ctx, config, logger4);
6379
- recent(ctx, config, logger4);
6380
- chart(ctx, config, logger4);
6381
- calculate(ctx, config, logger4);
6382
- sync(ctx, config, logger4);
6383
- radar(ctx, config, logger4);
7164
+ vf(ctx, config, logger5);
7165
+ recent(ctx, config, logger5);
7166
+ chart(ctx, config, logger5);
7167
+ calculate(ctx, config, logger5);
7168
+ sync(ctx, config, logger5);
7169
+ radar(ctx, config, logger5);
6384
7170
  }
6385
7171
  __name(apply10, "apply");
6386
7172
 
@@ -6420,22 +7206,22 @@ function apply12(ctx) {
6420
7206
  __name(apply12, "apply");
6421
7207
 
6422
7208
  // src/games/sdvx/locales/en-US.yml
6423
- var en_US_default4 = { _config: { $desc: "SDVX Module Settings", sdvx_data_url: "<p>The URL of the SDVX data service</p>", sdvx_search_url: "<p>The URL of the SDVX search service</p>", official_support_url: "<p>The URL of the SDVX official support service</p>" }, commands: { vf: { description: "Show Noah help information", messages: { "card-not-found": "<p>You haven't bound a card yet, go bind a card first~</p>", "server-not-found": "<p>No available servers, add one yourself~</p>", "no-scores-found": "<p>No scores found, please check your data.</p>", error: "<p>An error occurred(っ °Д °;)っ</p>", drawing: "<p>Noah is drawing {name} [{difstr}], please wait patiently~</p>", "menu-select": "<p>Please select the card you want to use:</p>\n{card_list}\n<p>q. Exit</p>", "invalid-select": "<p>No such option!</p>", quit: "<p>Exited~</p>" } }, sdvx: { recent: { description: "Show recent scores", messages: { "menu-select": "<p>Please select the card you want to use:</p>\n{card_list}\n<p>q. Exit</p>", "invalid-select": "<p>No such option!</p>", "card-not-found": "<p>You haven't bound a card yet, go bind a card first~</p>", "server-not-found": "<p>No available servers, add one yourself~</p>", "no-scores-found": "<p>No scores found, please check your data.</p>", error: "<p>An error occurred(っ °Д °;)っ</p>", drawing: "<p>Noah is drawing, please wait patiently~</p>" } }, chart: { description: "Show SDVX chart", messages: { prompt: "<p>Which song's chart would you like to view?</p>", error: "<p>An error occurred(っ °Д °;)っ</p>", drawing: "<p>Noah is drawing, please wait patiently~</p>", "no-result": "<p>Aww, Noah couldn’t find that song~ try another keyword, okay?</p>" } }, calculate: { description: "Calculate volforce value or score", messages: { "invalid-query": "<p>Invalid query parameters, please check your input~</p>", "no-results": "<p>No results found~</p>", "too-many-results": "<p>Too many results! Found {0} results, please narrow down your query (current limit: 500 results)</p>", drawing: "<p>Noah is drawing, please wait patiently~</p>", error: "<p>An error occurred(っ °Д °;)っ</p>" } }, radar: { description: "Show player radar stats", messages: { "card-not-found": "<p>You haven't bound a card yet, go bind a card first~</p>", "server-not-found": "<p>No available servers, add one yourself~</p>", "no-scores-found": "<p>No scores found, please check your data.</p>", error: "<p>An error occurred(っ °Д °;)っ</p>", result: "<p>{name}'s Radar</p>\n<p>NOTES: {notes}</p>\n<p>PEAK: {peak}</p>\n<p>TSUMAMI: {tsumami}</p>\n<p>TRICKY: {tricky}</p>\n<p>HAND-TRIP: {hand_trip}</p>\n<p>ONE-HAND: {one_hand}</p>" } }, sync: { description: "Sync scores to Mao", messages: { "mao-not-found": "<p>Mao server not found. Please add a Mao server first, then try syncing again.</p>", "source-server-not-found": "<p>No available source servers. Please add a server first.</p>", "source-server-select": "<p>Please choose the source server:</p>\n{server_list}\n<p>q. Exit</p>", "card-not-found": "<p>You haven't bound any card yet. Please bind a card first.</p>", "source-card-select": "<p>Please choose the source card:</p>\n{card_list}\n<p>q. Exit</p>", "target-card-select": "<p>Please choose the target card (sync to Mao):</p>\n{card_list}\n<p>q. Exit</p>", "invalid-select": "<p>No such option!</p>", quit: "<p>Sync cancelled.</p>", "same-card-error": "<p>When the source server is Mao, the source card and target card cannot be the same. Please choose again.</p>", "dm-only": "<p>For security, please use this command in a private chat.</p>", "pin-prompt": "<p>Please enter the Mao PIN (4 digits):</p>\n<p>q. Exit</p>", "pin-invalid": "<p>Invalid PIN format. Please enter 4 digits.</p>", "pin-verify-failed": "<p>PIN verification failed: {message}</p>", "pin-verify-error": "<p>PIN verification error. Please try again later.</p>", "pin-too-many": "<p>Too many PIN attempts. Please try again later.</p>", "fetch-error": "<p>Failed to fetch scores from the source server. Please try again later.</p>", "no-scores": "<p>No scores available for sync.</p>", "confirm-sync": "<p>Found {score_count} scores. Start sync?</p>\n<p>Type y to confirm, any other key to cancel.</p>", "sync-error": "<p>Error occurred during sync(っ °Д °;)っ</p>", "sync-failed": "<p>Sync failed(っ °Д °;)っ</p>", "selected-summary": "<p>Sync completedヾ(≧▽≦*)o</p>\n<p>Source server: {source_server_name} ({source_server_type})</p>\n<p>Source card: {source_card_name}</p>\n<p>Target card: {target_card_name}</p>\n<p>Tracks synced: {score_count}</p>" } } } } };
7209
+ var en_US_default4 = { _config: { $desc: "SDVX Module Settings", sdvx_data_url: "<p>The URL of the SDVX data service</p>", sdvx_search_url: "<p>The URL of the SDVX search service</p>", official_support_url: "<p>The URL of the SDVX official support service</p>" }, commands: { vf: { description: "Show Noah help information", messages: { "card-not-found": "<p>You haven't bound a card yet, go bind a card first~</p>", "server-not-found": "<p>No available servers, add one yourself~</p>", "no-scores-found": "<p>No scores found, please check your data.</p>", error: "<p>An error occurred while using card: {card}, server: {server} (っ °Д °;)っ</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>", "server-menu-select": "<p>Please select the server you want to use:</p>\n{server_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>", "server-menu-select": "<p>Please select the server you want to use:</p>\n{server_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 while using card: {card}, server: {server} (っ °Д °;)っ</p>", drawing: "<p>Noah is drawing, please wait patiently~</p>", quit: "<p>Exited~</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 while using card: {card}, server: {server} (っ °Д °;)っ</p>", "menu-select": "<p>Please select the card you want to use:</p>\n{card_list}\n<p>q. Exit</p>", "server-menu-select": "<p>Please select the server you want to use:</p>\n{server_list}\n<p>q. Exit</p>", "invalid-select": "<p>No such option!</p>", quit: "<p>Exited~</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>An error occurred while using card: {card}, server: {server} (っ °Д °;)っ</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>An error occurred while using card: {card}, server: {server} (っ °Д °;)っ</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>" } } } } };
6424
7210
 
6425
7211
  // src/games/sdvx/locales/zh-CN.yml
6426
- var zh_CN_default4 = { _config: { $desc: "SDVX 模块设置", sdvx_data_url: "<p>SDVX 数据服务的 URL</p>", sdvx_search_url: "<p>SDVX 搜索服务的 URL</p>", official_support_url: "<p>SDVX 官方支持服务的 URL</p>" }, commands: { vf: { description: "查询 SDVX VOLFORCE", messages: { "card-not-found": "<p>你还没绑卡,去绑个卡再来吧~</p>", "server-not-found": "<p>没有可用的服务器哦,自己添加一个吧~</p>", "no-scores-found": "<p>没有找到你的分数数据哦~</p>", error: "<p>Noah 遇到了错误(っ °Д °;)っ</p>", drawing: "<p>Noah 正在画啦,请耐心等待~</p>", "menu-select": "<p>请选择你要使用的卡片:</p>\n{card_list}\n<p>q. 退出</p>", "invalid-select": "<p>没有该选项!</p>", quit: "<p>已退出~</p>" } }, sdvx: { recent: { description: "查询最近分数", messages: { "menu-select": "<p>请选择你要使用的卡片:</p>\n{card_list}\n<p>q. 退出</p>", "invalid-select": "<p>没有该选项!</p>", "card-not-found": "<p>你还没绑卡,去绑个卡再来吧~</p>", "server-not-found": "<p>没有可用的服务器哦,自己添加一个吧~</p>", "no-scores-found": "<p>没有找到你的分数数据哦~</p>", error: "<p>Noah 遇到了错误(っ °Д °;)っ</p>", drawing: "<p>Noah 正在画啦,请耐心等待~</p>" } }, chart: { description: "查询 SDVX 谱面", messages: { prompt: "<p>要查哪首歌的铺面呢?</p>", error: "<p>Noah 遇到了错误(っ °Д °;)っ</p>", drawing: "<p>Noah 正在绘制 {name} [{difstr}],请耐心等待~</p>", "no-result": "<p>Noah 没有找到这首歌,换个关键词试试吧~</p>" } }, calculate: { description: "计算 volforce 值或分数", messages: { "invalid-query": "<p>查询参数无效,请检查你的输入~</p>", "no-results": "<p>没有找到符合条件的结果~</p>", "too-many-results": "<p>结果太多啦!共找到 {0} 条结果,请缩小查询范围(当前限制:500 条)</p>", drawing: "<p>Noah 正在画啦,请耐心等待~</p>", error: "<p>Noah 遇到了错误(っ °Д °;)っ</p>" } }, radar: { description: "查询玩家六维雷达", messages: { "card-not-found": "<p>你还没绑卡,去绑个卡再来吧~</p>", "server-not-found": "<p>没有可用的服务器哦,自己添加一个吧~</p>", "no-scores-found": "<p>没有找到你的分数数据哦~</p>", error: "<p>Noah 遇到了错误(っ °Д °;)っ</p>", result: "<p>{name} 的雷达</p>\n<p>NOTES: {notes}</p>\n<p>PEAK: {peak}</p>\n<p>TSUMAMI: {tsumami}</p>\n<p>TRICKY: {tricky}</p>\n<p>HAND-TRIP: {hand_trip}</p>\n<p>ONE-HAND: {one_hand}</p>" } }, sync: { description: "同步成绩到猫网", messages: { "mao-not-found": "<p>没有找到猫网服务器,请先添加猫网服务器后再尝试同步。</p>", "source-server-not-found": "<p>没有可作为来源的服务器,请先添加服务器。</p>", "source-server-select": "<p>请选择来源服务器:</p>\n{server_list}\n<p>q. 退出</p>", "card-not-found": "<p>你还没有绑定任何卡片哦~</p>", "source-card-select": "<p>请选择来源卡片:</p>\n{card_list}\n<p>q. 退出</p>", "target-card-select": "<p>请选择目标卡片(同步到猫网):</p>\n{card_list}\n<p>q. 退出</p>", "invalid-select": "<p>没有该选项!</p>", quit: "<p>已退出同步。</p>", "same-card-error": "<p>来源服务器为猫网时,来源卡片和目标卡片不能相同,请重新选择。</p>", "dm-only": "<p>出于安全考虑,请在私聊中使用该指令。</p>", "pin-prompt": "<p>请输入猫网 PIN 码(4 位数字):</p>\n<p>q. 退出</p>", "pin-invalid": "<p>PIN 码格式不正确,请输入 4 位数字。</p>", "pin-verify-failed": "<p>PIN 验证失败:{message}</p>", "pin-verify-error": "<p>PIN 验证时出现错误,请稍后重试。</p>", "pin-too-many": "<p>PIN 验证次数过多,请稍后再试。</p>", "fetch-error": "<p>获取来源服务器成绩失败,请稍后再试。</p>", "no-scores": "<p>没有可同步的成绩。</p>", "confirm-sync": "<p>共找到 {score_count} 条成绩,是否开始同步?</p>\n<p>输入 y 确认,其他任意键取消。</p>", "sync-error": "<p>同步过程中出现了错误(っ °Д °;)っ</p>", "sync-failed": "<p>同步失败(っ °Д °;)っ</p>", "selected-summary": "<p>同步完成ヾ(≧▽≦*)o</p>\n<p>来源服务器:{source_server_name} ({source_server_type})</p>\n<p>来源卡片:{source_card_name}</p>\n<p>目标卡片:{target_card_name}</p>\n<p>同步曲目数量:{score_count}</p>" } } } } };
7212
+ var zh_CN_default4 = { _config: { $desc: "SDVX 模块设置", sdvx_data_url: "<p>SDVX 数据服务的 URL</p>", sdvx_search_url: "<p>SDVX 搜索服务的 URL</p>", official_support_url: "<p>SDVX 官方支持服务的 URL</p>" }, commands: { vf: { description: "查询 SDVX VOLFORCE", messages: { "card-not-found": "<p>你还没绑卡,去绑个卡再来吧~</p>", "server-not-found": "<p>没有可用的服务器哦,自己添加一个吧~</p>", "no-scores-found": "<p>没有找到你的分数数据哦~</p>", error: "<p>Noah 在使用卡片:{card},服务器:{server} 时遇到了错误(っ °Д °;)っ</p>", drawing: "<p>Noah 正在画啦,请耐心等待~</p>", "menu-select": "<p>请选择你要使用的卡片:</p>\n{card_list}\n<p>q. 退出</p>", "server-menu-select": "<p>请选择你要使用的服务器:</p>\n{server_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>", "server-menu-select": "<p>请选择你要使用的服务器:</p>\n{server_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 在使用卡片:{card},服务器:{server} 时遇到了错误(っ °Д °;)っ</p>", drawing: "<p>Noah 正在画啦,请耐心等待~</p>", quit: "<p>已退出~</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 在使用卡片:{card},服务器:{server} 时遇到了错误(っ °Д °;)っ</p>", "menu-select": "<p>请选择你要使用的卡片:</p>\n{card_list}\n<p>q. 退出</p>", "server-menu-select": "<p>请选择你要使用的服务器:</p>\n{server_list}\n<p>q. 退出</p>", "invalid-select": "<p>没有该选项!</p>", quit: "<p>已退出~</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>Noah 在使用卡片:{card},服务器:{server} 时遇到了错误(っ °Д °;)っ</p>", "no-scores": "<p>没有可同步的成绩。</p>", "confirm-sync": "<p>共找到 {score_count} 条成绩,是否开始同步?</p>\n<p>输入 y 确认,其他任意键取消。</p>", "sync-error": "<p>Noah 在使用卡片:{card},服务器:{server} 时遇到了错误(っ °Д °;)っ</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>" } } } } };
6427
7213
 
6428
7214
  // src/games/sdvx/index.ts
6429
7215
  var name13 = "Noah-SDVX";
6430
7216
  var inject3 = ["database", "globalConfig"];
6431
- var logger4 = new import_koishi18.Logger("Noah-SDVX");
7217
+ var logger5 = new import_koishi21.Logger("Noah-SDVX");
6432
7218
  async function apply13(ctx, config) {
6433
7219
  ;
6434
7220
  [
6435
7221
  ["en-US", en_US_default4],
6436
7222
  ["zh-CN", zh_CN_default4]
6437
7223
  ].forEach(([lang, file]) => ctx.i18n.define(lang, file));
6438
- registerSDVXAdapters(logger4, config.sdvx);
7224
+ registerSDVXAdapters(logger5, config.sdvx);
6439
7225
  ctx.plugin(database_exports2, config.sdvx);
6440
7226
  ctx.plugin(command_exports2, config.sdvx);
6441
7227
  ctx.plugin(event_exports2, config.sdvx);
@@ -6443,47 +7229,50 @@ async function apply13(ctx, config) {
6443
7229
  __name(apply13, "apply");
6444
7230
 
6445
7231
  // src/config.ts
6446
- var import_koishi26 = require("koishi");
7232
+ var import_koishi29 = require("koishi");
6447
7233
 
6448
7234
  // src/asset/config.ts
6449
- var import_koishi19 = require("koishi");
6450
- var assetConfig = import_koishi19.Schema.object({
6451
- data_path: import_koishi19.Schema.string().default("noah_assets"),
6452
- auto_update: import_koishi19.Schema.boolean().default(true),
6453
- update_url: import_koishi19.Schema.string().default("https://github.com/logthm/noah/releases/latest/download/"),
6454
- update_interval: import_koishi19.Schema.number().default(24 * 60 * 60 * 1e3),
7235
+ var import_koishi22 = require("koishi");
7236
+ var assetConfig = import_koishi22.Schema.object({
7237
+ data_path: import_koishi22.Schema.string().default("noah_assets"),
7238
+ auto_update: import_koishi22.Schema.boolean().default(true),
7239
+ update_url: import_koishi22.Schema.string().default("https://github.com/logthm/noah/releases/latest/download/"),
7240
+ update_interval: import_koishi22.Schema.number().default(24 * 60 * 60 * 1e3),
6455
7241
  // 24 hours in milliseconds
6456
- github_token: import_koishi19.Schema.string().description("GitHub token for accessing private repositories").role("secret")
7242
+ github_token: import_koishi22.Schema.string().description("GitHub token for accessing private repositories").role("secret")
6457
7243
  }).i18n({
6458
7244
  "en-US": en_US_default._config,
6459
7245
  "zh-CN": zh_CN_default._config
6460
7246
  });
6461
7247
 
6462
7248
  // src/core/config.ts
6463
- var import_koishi20 = require("koishi");
6464
- var coreConfig = import_koishi20.Schema.object({
6465
- adminUsers: import_koishi20.Schema.array(String).role("table"),
6466
- guildNameCards: import_koishi20.Schema.array(String).role("table").default(["Noah | /help 获取食用指南"])
7249
+ var import_koishi23 = require("koishi");
7250
+ var coreConfig = import_koishi23.Schema.object({
7251
+ adminUsers: import_koishi23.Schema.array(String).role("table"),
7252
+ guildNameCards: import_koishi23.Schema.array(String).role("table").default(["Noah | /help 获取食用指南"]),
7253
+ tokenTtl: import_koishi23.Schema.number().default(1800),
7254
+ frontendUrl: import_koishi23.Schema.string().default(""),
7255
+ corsOrigin: import_koishi23.Schema.string().default("*")
6467
7256
  }).i18n({
6468
7257
  "en-US": en_US_default2._config,
6469
7258
  "zh-CN": zh_CN_default2._config
6470
7259
  });
6471
7260
 
6472
7261
  // src/fun/poke/config.ts
6473
- var import_koishi21 = require("koishi");
6474
- var pokeConfig = import_koishi21.Schema.object({
6475
- interval: import_koishi21.Schema.number().default(1e3).step(100),
6476
- warning: import_koishi21.Schema.boolean().default(false),
6477
- prompt: import_koishi21.Schema.array(
6478
- import_koishi21.Schema.object({
6479
- content: import_koishi21.Schema.string().required(),
6480
- weight: import_koishi21.Schema.number().min(0).max(100).default(50)
7262
+ var import_koishi24 = require("koishi");
7263
+ var pokeConfig = import_koishi24.Schema.object({
7264
+ interval: import_koishi24.Schema.number().default(1e3).step(100),
7265
+ warning: import_koishi24.Schema.boolean().default(false),
7266
+ prompt: import_koishi24.Schema.array(
7267
+ import_koishi24.Schema.object({
7268
+ content: import_koishi24.Schema.string().required(),
7269
+ weight: import_koishi24.Schema.number().min(0).max(100).default(50)
6481
7270
  })
6482
7271
  ).role("table"),
6483
- messages: import_koishi21.Schema.array(
6484
- import_koishi21.Schema.object({
6485
- content: import_koishi21.Schema.string().required(),
6486
- weight: import_koishi21.Schema.number().min(0).max(100).default(50)
7272
+ messages: import_koishi24.Schema.array(
7273
+ import_koishi24.Schema.object({
7274
+ type: import_koishi24.Schema.union(["text", "noah", "chat"]).default("text"),
7275
+ weight: import_koishi24.Schema.number().min(0).max(100).default(50)
6487
7276
  })
6488
7277
  ).role("table")
6489
7278
  }).i18n({
@@ -6492,7 +7281,7 @@ var pokeConfig = import_koishi21.Schema.object({
6492
7281
  });
6493
7282
 
6494
7283
  // src/games/general/config.ts
6495
- var import_koishi22 = require("koishi");
7284
+ var import_koishi25 = require("koishi");
6496
7285
 
6497
7286
  // src/games/general/locales/en-US.yml
6498
7287
  var en_US_default5 = { _config: { $desc: "General Module Settings", barcode_api_url: "<p>The URL of the barcode API</p>" } };
@@ -6501,33 +7290,33 @@ var en_US_default5 = { _config: { $desc: "General Module Settings", barcode_api_
6501
7290
  var zh_CN_default5 = { _config: { $desc: "通用工具模块设置", barcode_api_url: "<p>条形码 API 地址</p>" } };
6502
7291
 
6503
7292
  // src/games/general/config.ts
6504
- var generalConfig = import_koishi22.Schema.object({
6505
- barcode_api_url: import_koishi22.Schema.string().required()
7293
+ var generalConfig = import_koishi25.Schema.object({
7294
+ barcode_api_url: import_koishi25.Schema.string().required()
6506
7295
  }).i18n({
6507
7296
  "en-US": en_US_default5._config,
6508
7297
  "zh-CN": zh_CN_default5._config
6509
7298
  });
6510
7299
 
6511
7300
  // src/games/sdvx/config.ts
6512
- var import_koishi23 = require("koishi");
6513
- var sdvxConfig = import_koishi23.Schema.object({
6514
- sdvx_data_url: import_koishi23.Schema.string().required(),
6515
- sdvx_search_url: import_koishi23.Schema.string().required()
7301
+ var import_koishi26 = require("koishi");
7302
+ var sdvxConfig = import_koishi26.Schema.object({
7303
+ sdvx_data_url: import_koishi26.Schema.string().required(),
7304
+ sdvx_search_url: import_koishi26.Schema.string().required()
6516
7305
  }).i18n({
6517
7306
  "en-US": en_US_default4._config,
6518
7307
  "zh-CN": zh_CN_default4._config
6519
7308
  });
6520
7309
 
6521
7310
  // src/global/config.ts
6522
- var import_koishi24 = require("koishi");
6523
- var globalConfig = import_koishi24.Schema.object({
6524
- official_support_url: import_koishi24.Schema.string().default("https://noah.logthm.com"),
6525
- maoServerUrl: import_koishi24.Schema.string().default("http://maomani.cn:577"),
6526
- maoApiKey: import_koishi24.Schema.string().role("secret").default("")
7311
+ var import_koishi27 = require("koishi");
7312
+ var globalConfig = import_koishi27.Schema.object({
7313
+ official_support_url: import_koishi27.Schema.string().default("https://noah.logthm.com"),
7314
+ maoServerUrl: import_koishi27.Schema.string().default("http://maomani.cn:577"),
7315
+ maoApiKey: import_koishi27.Schema.string().role("secret").default("")
6527
7316
  });
6528
7317
 
6529
7318
  // src/slash/config.ts
6530
- var import_koishi25 = require("koishi");
7319
+ var import_koishi28 = require("koishi");
6531
7320
 
6532
7321
  // src/slash/locales/en-US.yml
6533
7322
  var en_US_default6 = { _config: { $desc: "Slash Module Settings", test_guilds: "**Guilds To Sync**", auto_sync_on_start: "**Auto Sync on Connect**" } };
@@ -6536,16 +7325,16 @@ var en_US_default6 = { _config: { $desc: "Slash Module Settings", test_guilds: "
6536
7325
  var zh_CN_default6 = { _config: { $desc: "Slash 模块设置", test_guilds: "**默认同步 Guild 列表**", auto_sync_on_start: "**连接后自动同步**" } };
6537
7326
 
6538
7327
  // src/slash/config.ts
6539
- var slashConfig = import_koishi25.Schema.object({
6540
- test_guilds: import_koishi25.Schema.array(String).default([]),
6541
- auto_sync_on_start: import_koishi25.Schema.boolean().default(true)
7328
+ var slashConfig = import_koishi28.Schema.object({
7329
+ test_guilds: import_koishi28.Schema.array(String).default([]),
7330
+ auto_sync_on_start: import_koishi28.Schema.boolean().default(true)
6542
7331
  }).i18n({
6543
7332
  "en-US": en_US_default6._config,
6544
7333
  "zh-CN": zh_CN_default6._config
6545
7334
  });
6546
7335
 
6547
7336
  // src/config.ts
6548
- var Config = import_koishi26.Schema.object({
7337
+ var Config = import_koishi29.Schema.object({
6549
7338
  global: globalConfig,
6550
7339
  core: coreConfig,
6551
7340
  sdvx: sdvxConfig,
@@ -6557,7 +7346,7 @@ var Config = import_koishi26.Schema.object({
6557
7346
 
6558
7347
  // src/index.ts
6559
7348
  var name14 = "noah";
6560
- var inject4 = ["database", "skia"];
7349
+ var inject4 = ["database", "skia", "server"];
6561
7350
  async function apply14(ctx, config) {
6562
7351
  initConstants(ctx);
6563
7352
  ctx.set("globalConfig", config.global);