koishi-plugin-prism 0.1.15 → 0.1.19
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/README.md +19 -3
- package/lib/index.d.ts +3 -0
- package/lib/index.js +76 -35
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
|
|
9
9
|
* 🎮 **玩家入场与结算**:通过 `/login` 和 `/logout` 指令开启或结算计费场次,支持 `/入场` 别名;命令回复会引用触发消息,结账账单可私聊通知管理员与指定用户。
|
|
10
10
|
* 💳 **账户钱包与资产管理**:支持查询钱包余额(`/wallet`)和持有的道具资产(`/items`)。
|
|
11
|
-
* 🀄 **麻将桌位集成**:包含 `/mahjong <tableId
|
|
11
|
+
* 🀄 **麻将桌位集成**:包含 `/mahjong <tableId>`、`/上桌 <桌号>`、自动识别当前桌位的 `/下桌`,以及用于查看桌名、别名和状态的 `/麻将列表`。`上桌` 仅允许已通过 `login`/`入场` 开启默认入场会话的玩家使用。`list` 按 session 标签分组:有非音乐游戏 session 的玩家归入最新的非音乐标签,纯音乐玩家归入音乐标签;麻将桌额外显示当前人数和容量。已开局桌位会从后端活跃 session 自动恢复,未满桌候座仍由机器人进程暂存,重启后不会保留。
|
|
12
12
|
* 🔌 **硬件设备状态与电源管理**:可直接在聊天中查看设备状态(`/show`)、远程开启/关闭电源(`/on`、`/off`)、远程投币(`/coin`)和模拟刷卡(`/scan`)。
|
|
13
13
|
* 🎟️ **礼物兑换码**:使用 `/redeem <code>` 兑换系统发放的福利礼包。
|
|
14
14
|
* 🛠️ **管理员快捷指令**:允许管理员为指定平台用户增加或扣除余额,并覆盖结账金额后立即结账。
|
|
@@ -32,6 +32,20 @@
|
|
|
32
32
|
| `enableStaffCommands` | `boolean` | `false` | 是否开启管理员快捷指令。 |
|
|
33
33
|
| `staffUserIds` | `string[]` | `[]` | 允许执行管理员快捷指令的平台用户 ID(如 QQ 号)白名单。空列表不授予目标用户操作权限。 |
|
|
34
34
|
| `logoutNotifyUserIds` | `string[]` | `[]` | 结账成功后额外私聊完整账单的平台用户 ID。通知收件人为该列表与 `staffUserIds` 的去重并集。 |
|
|
35
|
+
| `mahjongTableConfigs` | `object[]` | `[]` | 推荐的结构化麻将桌列表。每项填写显示名称、命令别名列表与计费方案 ID 列表。显示名称同时作为内部桌位锚点与 session 标签。 |
|
|
36
|
+
| `mahjongTables` | `string` | - | 旧版文本配置,仅在 `mahjongTableConfigs` 为空时读取;建议迁移到结构化列表。 |
|
|
37
|
+
|
|
38
|
+
### 麻将桌配置
|
|
39
|
+
|
|
40
|
+
在 Koishi 配置页的 `mahjongTableConfigs` 中新增桌位,每一项填写:
|
|
41
|
+
|
|
42
|
+
```yaml
|
|
43
|
+
displayName: "🀄️ M.LEAGUE联名比赛专用机"
|
|
44
|
+
aliases: [a, 四麻A, 比赛机]
|
|
45
|
+
pricingConfigIds: [pricing-mahjong-a]
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
`displayName` 是该桌的稳定锚点和 session 标签;玩家输入的桌号、简称等均写入 `aliases`(至少一个)。结构化配置有内容时优先于旧 `mahjongTables` 文本;已有旧配置不会失效,可逐桌迁移。
|
|
35
49
|
|
|
36
50
|
管理员快捷指令必须同时配置 `enableStaffCommands: true` 与 `staffUserIds`。它们使用现有 `integrationToken` 调用受限的余额调整和立即结账接口;目标用户参数使用 Koishi 的 `user` 选择器,只有白名单内的管理员可以操作其他用户。
|
|
37
51
|
|
|
@@ -43,6 +57,7 @@
|
|
|
43
57
|
* `logout` - 结算当前玩家的计费场次。
|
|
44
58
|
玩家在未产生任何费用时退场,机器人会简洁显示“本次未产生费用”和当前余额;存在收费或优惠明细时仍显示完整结算账单。
|
|
45
59
|
结账成功后,机器人会向 `staffUserIds` 与 `logoutNotifyUserIds` 中的用户私聊同一份账单,账单会明确显示结账玩家身份。
|
|
60
|
+
账单和管理员代操作回执均使用“平台昵称(QQ:号码)”称呼玩家;平台暂时无法提供昵称时显示“未知昵称(QQ:号码)”,不会显示内部玩家 ID。
|
|
46
61
|
* `billing` - 预览当前玩家本场计费的消费费用。
|
|
47
62
|
* `wallet` - 查看当前玩家的钱包余额。
|
|
48
63
|
* `items` - 查看当前玩家持有的道具或资产。
|
|
@@ -55,8 +70,9 @@
|
|
|
55
70
|
* `coin <deviceId> [count]` - 请求向指定设备投币指定枚数。
|
|
56
71
|
* `scan <deviceId> <subject>` - 请求向设备发送模拟刷卡。
|
|
57
72
|
* `redeem <code>` - 兑换礼物码。
|
|
58
|
-
* `mahjong <tableId>` / `上桌
|
|
59
|
-
*
|
|
73
|
+
* `mahjong <tableId>` / `上桌 [tableId]` - 加入指定麻将桌;`/上桌` 未提供桌号时会引导查看 `/麻将列表`。仅允许已通过 `login`/`入场` 开启默认入场会话的玩家使用。
|
|
74
|
+
* `下桌` - 自动离开当前所在麻将桌。
|
|
75
|
+
* `麻将列表` - 查看已配置机器的桌名、命令别名,以及空闲、等位或游玩中状态。
|
|
60
76
|
|
|
61
77
|
### 管理员快捷指令
|
|
62
78
|
启用 `enableStaffCommands`、配置 `staffUserIds` 白名单与 `staffSessionToken` 后可使用:
|
package/lib/index.d.ts
CHANGED
|
@@ -14,6 +14,7 @@ export type MahjongTableConfig = {
|
|
|
14
14
|
aliases: string[];
|
|
15
15
|
pricingConfigIds: string[];
|
|
16
16
|
};
|
|
17
|
+
export type MahjongTableConfigInput = Omit<MahjongTableConfig, "tableId">;
|
|
17
18
|
export type PrismKoishiPluginConfig = {
|
|
18
19
|
provider: string;
|
|
19
20
|
autoRegister: boolean;
|
|
@@ -25,6 +26,7 @@ export type PrismKoishiPluginConfig = {
|
|
|
25
26
|
enableStaffCommands?: boolean;
|
|
26
27
|
staffUserIds?: string[];
|
|
27
28
|
logoutNotifyUserIds?: string[];
|
|
29
|
+
mahjongTableConfigs?: MahjongTableConfigInput[];
|
|
28
30
|
mahjongTables?: string;
|
|
29
31
|
mahjongTableSize?: number;
|
|
30
32
|
mahjongLabelPrefix?: string;
|
|
@@ -75,6 +77,7 @@ export type Sender = {
|
|
|
75
77
|
};
|
|
76
78
|
export declare function humanReadableBotError(error: PrismBotClientError): string;
|
|
77
79
|
export declare function parseMahjongTables(value: string, labelPrefix: string): Map<string, MahjongTableConfig>;
|
|
80
|
+
export declare function resolveMahjongTableConfigs(structured: readonly MahjongTableConfigInput[], legacyValue: string, labelPrefix: string): Map<string, MahjongTableConfig>;
|
|
78
81
|
declare const _default: {
|
|
79
82
|
name: string;
|
|
80
83
|
Config: Schema<PrismKoishiPluginConfig>;
|
package/lib/index.js
CHANGED
|
@@ -5,6 +5,7 @@ exports.apply = apply;
|
|
|
5
5
|
exports.applyPrismKoishiPlugin = applyPrismKoishiPlugin;
|
|
6
6
|
exports.humanReadableBotError = humanReadableBotError;
|
|
7
7
|
exports.parseMahjongTables = parseMahjongTables;
|
|
8
|
+
exports.resolveMahjongTableConfigs = resolveMahjongTableConfigs;
|
|
8
9
|
const koishi_1 = require("koishi");
|
|
9
10
|
exports.name = "prism";
|
|
10
11
|
exports.Config = koishi_1.Schema.object({
|
|
@@ -21,7 +22,12 @@ exports.Config = koishi_1.Schema.object({
|
|
|
21
22
|
staffUserIds: koishi_1.Schema.array(koishi_1.Schema.string()).default([]).description("允许执行管理员指令的平台用户ID列表"),
|
|
22
23
|
logoutNotifyUserIds: koishi_1.Schema.array(koishi_1.Schema.string()).default([]).description("结账账单私聊通知的平台用户ID列表"),
|
|
23
24
|
powerOffInterval: koishi_1.Schema.number().default(0).description("无人自动关机等待秒数 (0为禁用)"),
|
|
24
|
-
|
|
25
|
+
mahjongTableConfigs: koishi_1.Schema.array(koishi_1.Schema.object({
|
|
26
|
+
displayName: koishi_1.Schema.string().required().description("桌位显示名称和 session 标签"),
|
|
27
|
+
aliases: koishi_1.Schema.array(koishi_1.Schema.string()).default([]).description("命令别名(至少一个),例如 a、四麻A"),
|
|
28
|
+
pricingConfigIds: koishi_1.Schema.array(koishi_1.Schema.string()).default([]).description("开局时绑定的计费方案 ID"),
|
|
29
|
+
})).default([]).description("麻将桌配置"),
|
|
30
|
+
mahjongTables: koishi_1.Schema.string().description("旧版麻将桌文本配置(已废弃;仅在结构化配置为空时使用)"),
|
|
25
31
|
mahjongTableSize: koishi_1.Schema.number().default(4).description("麻将桌人数限制"),
|
|
26
32
|
mahjongLabelPrefix: koishi_1.Schema.string().default("麻将桌").description("麻将账单前缀"),
|
|
27
33
|
});
|
|
@@ -44,7 +50,8 @@ exports.PrismBotClientError = PrismBotClientError;
|
|
|
44
50
|
const LOCAL_TZ_OFFSET_MINUTES = 8 * 60;
|
|
45
51
|
const USAGE = {
|
|
46
52
|
mahjong_join: "/上桌 <桌号>",
|
|
47
|
-
mahjong_leave: "/下桌
|
|
53
|
+
mahjong_leave: "/下桌",
|
|
54
|
+
mahjong_list: "/麻将列表",
|
|
48
55
|
prism_on: "/prism on <设备ID>",
|
|
49
56
|
prism_off: "/prism off <设备ID|all>",
|
|
50
57
|
prism_coin: "/prism coin <设备ID> [数量]",
|
|
@@ -69,8 +76,9 @@ function applyPrismKoishiPlugin(ctx, config) {
|
|
|
69
76
|
ctx.command("login [target:user]", "开启玩家计费场次").action(wrap(async (context, target) => service.loginForTarget(await service.sender(context), target, context.session?.bot)));
|
|
70
77
|
ctx.command("入场 [target:user]", "入场").action(wrap(async (context, target) => service.loginForTarget(await service.sender(context), target, context.session?.bot)));
|
|
71
78
|
ctx.command("mahjong <tableId>", "加入指定麻将桌").action(wrap(async (context, tableId) => service.mahjongJoin(await service.sender(context), tableId)));
|
|
72
|
-
ctx.command("上桌
|
|
73
|
-
ctx.command("下桌
|
|
79
|
+
ctx.command("上桌 [tableId]", "加入指定麻将桌").action(wrap(async (context, tableId) => service.mahjongJoin(await service.sender(context), tableId)));
|
|
80
|
+
ctx.command("下桌", "离开当前所在麻将桌").action(wrap(async (context) => service.mahjongLeave(await service.sender(context))));
|
|
81
|
+
ctx.command("麻将列表", "查看麻将机状态与别名").action(wrap(async () => service.listMahjongTables()));
|
|
74
82
|
ctx.command("logout [target:user]", "结算玩家计费场次").action(wrap(async (context, target) => service.withTarget(await service.sender(context), target, (sender) => service.logout(sender, context.session?.bot), context.session?.bot)));
|
|
75
83
|
ctx.command("billing [target:user]", "预览玩家结账费用").action(wrap(async (context, target) => service.withTarget(await service.sender(context), target, (sender) => service.billing(sender), context.session?.bot)));
|
|
76
84
|
ctx.command("wallet [target:user]", "查看玩家钱包").action(wrap(async (context, target) => service.withTarget(await service.sender(context), target, (sender) => service.wallet(sender), context.session?.bot)));
|
|
@@ -324,7 +332,7 @@ class PrismKoishiService {
|
|
|
324
332
|
}
|
|
325
333
|
async sender(context) {
|
|
326
334
|
const id = context.session?.senderId || context.session?.userId || "";
|
|
327
|
-
let name = id;
|
|
335
|
+
let name = context.session?.username || context.session?.senderName || id;
|
|
328
336
|
try {
|
|
329
337
|
if (context.session?.bot?.getUser) {
|
|
330
338
|
const user = await context.session.bot.getUser(id);
|
|
@@ -333,9 +341,7 @@ class PrismKoishiService {
|
|
|
333
341
|
}
|
|
334
342
|
}
|
|
335
343
|
}
|
|
336
|
-
catch {
|
|
337
|
-
name = context.session?.username || context.session?.senderName || id;
|
|
338
|
-
}
|
|
344
|
+
catch { }
|
|
339
345
|
return { id, name };
|
|
340
346
|
}
|
|
341
347
|
async register(sender) {
|
|
@@ -345,7 +351,7 @@ class PrismKoishiService {
|
|
|
345
351
|
async loginForTarget(actor, targetSubject, bot) {
|
|
346
352
|
return this.withTarget(actor, targetSubject, async (sender, isTargeted) => {
|
|
347
353
|
await this.client.startSessionByIdentity(this.identity(sender), this.loginSessionBody());
|
|
348
|
-
return isTargeted ? `✅ 已为用户 ${formatPlayerReference(sender)} 入场成功` : "✅ 入场成功";
|
|
354
|
+
return isTargeted ? `✅ 已为用户 ${formatPlayerReference(sender, this.config.provider)} 入场成功` : "✅ 入场成功";
|
|
349
355
|
}, bot);
|
|
350
356
|
}
|
|
351
357
|
async login(sender) {
|
|
@@ -370,7 +376,7 @@ class PrismKoishiService {
|
|
|
370
376
|
quantityDelta: amount * direction,
|
|
371
377
|
reason: isAddition ? "Koishi 管理员增加余额" : "Koishi 管理员扣除余额",
|
|
372
378
|
}]);
|
|
373
|
-
return `✅ 已为用户 ${sender.
|
|
379
|
+
return `✅ 已为用户 ${formatPlayerReference(sender, this.config.provider)}${isAddition ? "增加" : "扣除"} ${formatNumber(amount)} ${this.config.currencyName}`;
|
|
374
380
|
}, bot);
|
|
375
381
|
}
|
|
376
382
|
async overwriteTargetCheckout(actor, targetSubject, rawAmount, rawReason, bot) {
|
|
@@ -380,16 +386,16 @@ class PrismKoishiService {
|
|
|
380
386
|
return "金额必须为非负数";
|
|
381
387
|
const reason = cleanText(rawReason) || "Koishi 管理员手动调价";
|
|
382
388
|
await this.client.checkoutWithOverrideByIdentity(this.identity(sender), total, reason);
|
|
383
|
-
return `✅ 已为用户 ${formatPlayerReference(sender)} 覆盖结账为 ${formatNumber(total)} ${this.config.currencyName}`;
|
|
389
|
+
return `✅ 已为用户 ${formatPlayerReference(sender, this.config.provider)} 覆盖结账为 ${formatNumber(total)} ${this.config.currencyName}`;
|
|
384
390
|
}, bot);
|
|
385
391
|
}
|
|
386
392
|
async mahjongJoin(sender, rawTableId) {
|
|
387
393
|
const tableId = cleanText(rawTableId);
|
|
388
394
|
if (!tableId)
|
|
389
|
-
return
|
|
395
|
+
return "请指定麻将桌,例如 /上桌 a;可先使用 /麻将列表 查看可用桌位。";
|
|
390
396
|
const tableConfig = this.mahjongTableConfigs().get(tableId);
|
|
391
397
|
if (!tableConfig)
|
|
392
|
-
return
|
|
398
|
+
return `未找到麻将桌「${tableId}」。可先使用 /麻将列表 查看可用桌位。`;
|
|
393
399
|
const tableKey = tableConfig.tableId;
|
|
394
400
|
const tableSubject = tableConfig.displayName || `${tableKey} 桌`;
|
|
395
401
|
const player = await this.resolvePlayer(sender);
|
|
@@ -402,11 +408,11 @@ class PrismKoishiService {
|
|
|
402
408
|
}
|
|
403
409
|
const existing = this.mahjongTableForPlayer(playerId);
|
|
404
410
|
if (existing)
|
|
405
|
-
return `你已经在 ${existing}
|
|
411
|
+
return `你已经在 ${existing},无需重复上桌。`;
|
|
406
412
|
const state = this.mahjongTables.get(tableKey) ?? { waiting: [], activeSessions: {} };
|
|
407
413
|
this.mahjongTables.set(tableKey, state);
|
|
408
414
|
if (Object.keys(state.activeSessions).length > 0) {
|
|
409
|
-
return
|
|
415
|
+
return `「${tableSubject}」正在游玩中(${Object.keys(state.activeSessions).length}/${this.config.mahjongTableSize ?? 4}),请等待本局结束后再上桌。`;
|
|
410
416
|
}
|
|
411
417
|
state.waiting.push({
|
|
412
418
|
playerId,
|
|
@@ -433,20 +439,19 @@ class PrismKoishiService {
|
|
|
433
439
|
}
|
|
434
440
|
return `${tableSubject}已满,麻将计费已开始。`;
|
|
435
441
|
}
|
|
436
|
-
async mahjongLeave(sender
|
|
437
|
-
const tableId = cleanText(rawTableId);
|
|
438
|
-
if (!tableId)
|
|
439
|
-
return commandUsage("mahjong_leave");
|
|
440
|
-
const tableConfig = this.mahjongTableConfigs().get(tableId);
|
|
441
|
-
const tableKey = tableConfig?.tableId ?? tableId;
|
|
442
|
-
const tableSubject = tableConfig?.displayName || `${tableId} 桌`;
|
|
442
|
+
async mahjongLeave(sender) {
|
|
443
443
|
const activeResult = (await this.client.listActiveSessions());
|
|
444
444
|
this.syncMahjongTableStates((activeResult.sessions ?? []));
|
|
445
|
-
const state = this.mahjongTables.get(tableKey);
|
|
446
|
-
if (!state)
|
|
447
|
-
return `你不在 ${tableSubject}。`;
|
|
448
445
|
const player = await this.resolvePlayer(sender);
|
|
449
446
|
const playerId = String(player.id ?? "");
|
|
447
|
+
const tableKey = this.mahjongTableForPlayer(playerId);
|
|
448
|
+
if (!tableKey)
|
|
449
|
+
return "你当前未在任何麻将桌上。";
|
|
450
|
+
const tableConfig = uniqueMahjongConfigs(this.mahjongTableConfigs()).find((table) => table.tableId === tableKey);
|
|
451
|
+
const tableSubject = tableConfig?.displayName || tableKey;
|
|
452
|
+
const state = this.mahjongTables.get(tableKey);
|
|
453
|
+
if (!state)
|
|
454
|
+
return "你当前未在任何麻将桌上。";
|
|
450
455
|
const waitingBefore = state.waiting.length;
|
|
451
456
|
state.waiting = state.waiting.filter((seat) => seat.playerId !== playerId);
|
|
452
457
|
if (state.waiting.length !== waitingBefore) {
|
|
@@ -454,11 +459,31 @@ class PrismKoishiService {
|
|
|
454
459
|
}
|
|
455
460
|
const sessionId = state.activeSessions[playerId];
|
|
456
461
|
if (!sessionId)
|
|
457
|
-
return
|
|
462
|
+
return "你当前未在任何麻将桌上。";
|
|
458
463
|
delete state.activeSessions[playerId];
|
|
459
464
|
await this.client.stopSessionByIdentity(this.identity(sender), sessionId);
|
|
460
465
|
return `已离开 ${tableSubject},麻将计费已停止。`;
|
|
461
466
|
}
|
|
467
|
+
async listMahjongTables() {
|
|
468
|
+
const activeResult = (await this.client.listActiveSessions());
|
|
469
|
+
this.syncMahjongTableStates((activeResult.sessions ?? []));
|
|
470
|
+
const tableSize = this.config.mahjongTableSize ?? 4;
|
|
471
|
+
const tables = uniqueMahjongConfigs(this.mahjongTableConfigs());
|
|
472
|
+
if (tables.length === 0)
|
|
473
|
+
return "当前没有配置任何麻将机。";
|
|
474
|
+
const lines = [`🀄️ 麻将机列表(${tables.length} 台)`];
|
|
475
|
+
for (const table of tables) {
|
|
476
|
+
const state = this.mahjongTables.get(table.tableId) ?? { waiting: [], activeSessions: {} };
|
|
477
|
+
const activeCount = Object.keys(state.activeSessions).length;
|
|
478
|
+
const waitingCount = state.waiting.length;
|
|
479
|
+
const statuses = [
|
|
480
|
+
...(activeCount > 0 ? [`游玩中 ${activeCount}/${tableSize}`] : []),
|
|
481
|
+
...(waitingCount > 0 ? [`等位 ${waitingCount}/${tableSize}`] : []),
|
|
482
|
+
];
|
|
483
|
+
lines.push(`- ${table.displayName}|别名:${table.aliases.length > 0 ? table.aliases.join("、") : "无"}|${statuses.join(";") || "空闲"}`);
|
|
484
|
+
}
|
|
485
|
+
return lines.join("\n");
|
|
486
|
+
}
|
|
462
487
|
async billing(sender) {
|
|
463
488
|
const result = (await this.client.previewCheckoutByIdentity(this.identity(sender)));
|
|
464
489
|
return this.formatCheckoutPreview(result, sender);
|
|
@@ -675,7 +700,7 @@ class PrismKoishiService {
|
|
|
675
700
|
}
|
|
676
701
|
mergeWaitingSeats(groups) {
|
|
677
702
|
for (const [tableId, state] of this.mahjongTables) {
|
|
678
|
-
const table = this.mahjongTableConfigs().
|
|
703
|
+
const table = uniqueMahjongConfigs(this.mahjongTableConfigs()).find((candidate) => candidate.tableId === tableId);
|
|
679
704
|
if (!table)
|
|
680
705
|
continue;
|
|
681
706
|
const label = mahjongSessionLabel(table, this.config.mahjongLabelPrefix ?? "麻将桌");
|
|
@@ -702,8 +727,8 @@ class PrismKoishiService {
|
|
|
702
727
|
return playerId || "未知玩家";
|
|
703
728
|
const platformName = await this.resolvePlatformName(sender.id);
|
|
704
729
|
const name = platformName
|
|
705
|
-
|| (sender.name && sender.name !== sender.id ? sender.name :
|
|
706
|
-
||
|
|
730
|
+
|| (sender.name && sender.name !== sender.id ? sender.name : "")
|
|
731
|
+
|| "未知昵称";
|
|
707
732
|
return `玩家:${name}(${this.config.provider.toUpperCase()}:${sender.id})`;
|
|
708
733
|
}
|
|
709
734
|
mahjongTableForPlayer(playerId) {
|
|
@@ -716,7 +741,7 @@ class PrismKoishiService {
|
|
|
716
741
|
return null;
|
|
717
742
|
}
|
|
718
743
|
mahjongTableConfigs() {
|
|
719
|
-
return
|
|
744
|
+
return resolveMahjongTableConfigs(this.config.mahjongTableConfigs ?? [], this.config.mahjongTables ?? "", this.config.mahjongLabelPrefix ?? "麻将桌");
|
|
720
745
|
}
|
|
721
746
|
syncMahjongTableStates(sessions) {
|
|
722
747
|
const tables = uniqueMahjongConfigs(this.mahjongTableConfigs());
|
|
@@ -969,6 +994,23 @@ function parseMahjongTables(value, labelPrefix) {
|
|
|
969
994
|
}
|
|
970
995
|
return tables;
|
|
971
996
|
}
|
|
997
|
+
function resolveMahjongTableConfigs(structured, legacyValue, labelPrefix) {
|
|
998
|
+
if (structured.length === 0)
|
|
999
|
+
return parseMahjongTables(legacyValue, labelPrefix);
|
|
1000
|
+
const tables = new Map();
|
|
1001
|
+
for (const input of structured) {
|
|
1002
|
+
const displayName = cleanText(input.displayName);
|
|
1003
|
+
const tableId = displayName;
|
|
1004
|
+
const aliases = [...new Set((input.aliases ?? []).map(cleanText).filter(Boolean))];
|
|
1005
|
+
const pricingConfigIds = (input.pricingConfigIds ?? []).map(cleanText).filter(Boolean);
|
|
1006
|
+
if (!tableId || !displayName || pricingConfigIds.length === 0)
|
|
1007
|
+
continue;
|
|
1008
|
+
const table = { tableId, displayName, aliases, pricingConfigIds };
|
|
1009
|
+
for (const alias of aliases)
|
|
1010
|
+
tables.set(alias, table);
|
|
1011
|
+
}
|
|
1012
|
+
return tables;
|
|
1013
|
+
}
|
|
972
1014
|
function uniqueMahjongConfigs(tables) {
|
|
973
1015
|
return [...new Map([...tables.values()].map((table) => [table.tableId, table])).values()];
|
|
974
1016
|
}
|
|
@@ -1001,7 +1043,7 @@ function formatPlayerGroups(groups, tableSize, mahjongLabelPrefix) {
|
|
|
1001
1043
|
const heading = group.table
|
|
1002
1044
|
? `${formatMahjongTableLabel(group.table, mahjongLabelPrefix)} ( ${group.players.length}/${tableSize} )`
|
|
1003
1045
|
: `${group.label} ( ${group.players.length}人 )`;
|
|
1004
|
-
lines.push(
|
|
1046
|
+
lines.push(`\n${heading}:\n${formatPlayerNames(group.players)}`);
|
|
1005
1047
|
}
|
|
1006
1048
|
return lines.join("\n");
|
|
1007
1049
|
}
|
|
@@ -1042,10 +1084,9 @@ function normalizeTargetSubject(value) {
|
|
|
1042
1084
|
const separator = subject.indexOf(":");
|
|
1043
1085
|
return separator > 0 ? subject.slice(separator + 1) : subject;
|
|
1044
1086
|
}
|
|
1045
|
-
function formatPlayerReference(sender) {
|
|
1046
|
-
|
|
1047
|
-
|
|
1048
|
-
: sender.id;
|
|
1087
|
+
function formatPlayerReference(sender, provider = "qq") {
|
|
1088
|
+
const name = sender.name && sender.name !== sender.id ? sender.name : "未知昵称";
|
|
1089
|
+
return `${name}(${provider.toUpperCase()}:${sender.id})`;
|
|
1049
1090
|
}
|
|
1050
1091
|
function toNumber(value) {
|
|
1051
1092
|
if (value == null)
|