koishi-plugin-prism 0.1.13 → 0.1.18
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 +17 -2
- package/lib/index.d.ts +3 -0
- package/lib/index.js +100 -43
- 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>` 以及便捷的 `/上桌` / `/下桌` 控制。`上桌` 仅允许已通过 `login`/`入场` 开启默认入场会话的玩家使用。`list`
|
|
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,10 +57,11 @@
|
|
|
43
57
|
* `logout` - 结算当前玩家的计费场次。
|
|
44
58
|
玩家在未产生任何费用时退场,机器人会简洁显示“本次未产生费用”和当前余额;存在收费或优惠明细时仍显示完整结算账单。
|
|
45
59
|
结账成功后,机器人会向 `staffUserIds` 与 `logoutNotifyUserIds` 中的用户私聊同一份账单,账单会明确显示结账玩家身份。
|
|
60
|
+
账单和管理员代操作回执均使用“平台昵称(QQ:号码)”称呼玩家;平台暂时无法提供昵称时显示“未知昵称(QQ:号码)”,不会显示内部玩家 ID。
|
|
46
61
|
* `billing` - 预览当前玩家本场计费的消费费用。
|
|
47
62
|
* `wallet` - 查看当前玩家的钱包余额。
|
|
48
63
|
* `items` - 查看当前玩家持有的道具或资产。
|
|
49
|
-
* `list` -
|
|
64
|
+
* `list` - 查看当前在线/在店游玩玩家的列表,按 session 标签分组并对同一玩家去重;存在非音乐游戏 session 时取最新的非音乐标签,麻将桌显示当前人数和容量。已开局桌位由后端 session 恢复;未满桌候座由机器人进程暂存,机器人重启后不会保留。
|
|
50
65
|
* `show [deviceId]` - 查看设备电源与连接状态。
|
|
51
66
|
* `history` - 查看自己的历史游玩记录。
|
|
52
67
|
* `lock` - 发送开门指令。
|
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
|
});
|
|
@@ -324,7 +330,7 @@ class PrismKoishiService {
|
|
|
324
330
|
}
|
|
325
331
|
async sender(context) {
|
|
326
332
|
const id = context.session?.senderId || context.session?.userId || "";
|
|
327
|
-
let name = id;
|
|
333
|
+
let name = context.session?.username || context.session?.senderName || id;
|
|
328
334
|
try {
|
|
329
335
|
if (context.session?.bot?.getUser) {
|
|
330
336
|
const user = await context.session.bot.getUser(id);
|
|
@@ -333,9 +339,7 @@ class PrismKoishiService {
|
|
|
333
339
|
}
|
|
334
340
|
}
|
|
335
341
|
}
|
|
336
|
-
catch {
|
|
337
|
-
name = context.session?.username || context.session?.senderName || id;
|
|
338
|
-
}
|
|
342
|
+
catch { }
|
|
339
343
|
return { id, name };
|
|
340
344
|
}
|
|
341
345
|
async register(sender) {
|
|
@@ -345,7 +349,7 @@ class PrismKoishiService {
|
|
|
345
349
|
async loginForTarget(actor, targetSubject, bot) {
|
|
346
350
|
return this.withTarget(actor, targetSubject, async (sender, isTargeted) => {
|
|
347
351
|
await this.client.startSessionByIdentity(this.identity(sender), this.loginSessionBody());
|
|
348
|
-
return isTargeted ? `✅ 已为用户 ${formatPlayerReference(sender)} 入场成功` : "✅ 入场成功";
|
|
352
|
+
return isTargeted ? `✅ 已为用户 ${formatPlayerReference(sender, this.config.provider)} 入场成功` : "✅ 入场成功";
|
|
349
353
|
}, bot);
|
|
350
354
|
}
|
|
351
355
|
async login(sender) {
|
|
@@ -370,7 +374,7 @@ class PrismKoishiService {
|
|
|
370
374
|
quantityDelta: amount * direction,
|
|
371
375
|
reason: isAddition ? "Koishi 管理员增加余额" : "Koishi 管理员扣除余额",
|
|
372
376
|
}]);
|
|
373
|
-
return `✅ 已为用户 ${sender.
|
|
377
|
+
return `✅ 已为用户 ${formatPlayerReference(sender, this.config.provider)}${isAddition ? "增加" : "扣除"} ${formatNumber(amount)} ${this.config.currencyName}`;
|
|
374
378
|
}, bot);
|
|
375
379
|
}
|
|
376
380
|
async overwriteTargetCheckout(actor, targetSubject, rawAmount, rawReason, bot) {
|
|
@@ -380,7 +384,7 @@ class PrismKoishiService {
|
|
|
380
384
|
return "金额必须为非负数";
|
|
381
385
|
const reason = cleanText(rawReason) || "Koishi 管理员手动调价";
|
|
382
386
|
await this.client.checkoutWithOverrideByIdentity(this.identity(sender), total, reason);
|
|
383
|
-
return `✅ 已为用户 ${formatPlayerReference(sender)} 覆盖结账为 ${formatNumber(total)} ${this.config.currencyName}`;
|
|
387
|
+
return `✅ 已为用户 ${formatPlayerReference(sender, this.config.provider)} 覆盖结账为 ${formatNumber(total)} ${this.config.currencyName}`;
|
|
384
388
|
}, bot);
|
|
385
389
|
}
|
|
386
390
|
async mahjongJoin(sender, rawTableId) {
|
|
@@ -396,6 +400,7 @@ class PrismKoishiService {
|
|
|
396
400
|
const playerId = String(player.id ?? "");
|
|
397
401
|
const activeResult = (await this.client.listActiveSessions());
|
|
398
402
|
const activeSessions = (activeResult.sessions ?? []);
|
|
403
|
+
this.syncMahjongTableStates(activeSessions);
|
|
399
404
|
if (!this.hasEntrySession(playerId, activeSessions)) {
|
|
400
405
|
return "请先入场后再上桌。";
|
|
401
406
|
}
|
|
@@ -439,6 +444,8 @@ class PrismKoishiService {
|
|
|
439
444
|
const tableConfig = this.mahjongTableConfigs().get(tableId);
|
|
440
445
|
const tableKey = tableConfig?.tableId ?? tableId;
|
|
441
446
|
const tableSubject = tableConfig?.displayName || `${tableId} 桌`;
|
|
447
|
+
const activeResult = (await this.client.listActiveSessions());
|
|
448
|
+
this.syncMahjongTableStates((activeResult.sessions ?? []));
|
|
442
449
|
const state = this.mahjongTables.get(tableKey);
|
|
443
450
|
if (!state)
|
|
444
451
|
return `你不在 ${tableSubject}。`;
|
|
@@ -567,6 +574,7 @@ class PrismKoishiService {
|
|
|
567
574
|
async listActiveSessions(sender) {
|
|
568
575
|
const result = (await this.client.listActiveSessions());
|
|
569
576
|
const sessions = (result?.sessions ?? []);
|
|
577
|
+
this.syncMahjongTableStates(sessions);
|
|
570
578
|
const tableByLabel = new Map(uniqueMahjongConfigs(this.mahjongTableConfigs()).map((table) => [
|
|
571
579
|
mahjongSessionLabel(table, this.config.mahjongLabelPrefix ?? "麻将桌"),
|
|
572
580
|
table,
|
|
@@ -650,41 +658,44 @@ class PrismKoishiService {
|
|
|
650
658
|
|| "未知玩家";
|
|
651
659
|
}
|
|
652
660
|
async buildPlayerGroups(players, tableByLabel) {
|
|
653
|
-
const groups = {
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
};
|
|
657
|
-
const groupForTable = new Map(groups.mahjong.map((group) => [group.table.tableId, group]));
|
|
661
|
+
const groups = { groups: [] };
|
|
662
|
+
const groupByLabel = new Map();
|
|
663
|
+
const musicLabel = this.config.loginSessionLabel?.trim() || "音游区间";
|
|
658
664
|
for (const player of players.values()) {
|
|
659
665
|
player.displayName = await this.displayNameForPlayer(player);
|
|
660
|
-
const
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
666
|
+
const nonMusic = player.sessions.filter((session) => Boolean(session.label) && session.label !== musicLabel);
|
|
667
|
+
const source = nonMusic.length > 0 ? nonMusic : player.sessions;
|
|
668
|
+
const selected = source.reduce((latest, session) => !latest || sessionStartedAt(session) > sessionStartedAt(latest) ? session : latest, undefined);
|
|
669
|
+
const label = selected?.label || musicLabel;
|
|
670
|
+
let group = groupByLabel.get(label);
|
|
671
|
+
if (!group) {
|
|
672
|
+
group = { label, table: tableByLabel.get(label), players: [] };
|
|
673
|
+
groupByLabel.set(label, group);
|
|
674
|
+
groups.groups.push(group);
|
|
668
675
|
}
|
|
676
|
+
group.players.push(player);
|
|
669
677
|
}
|
|
670
678
|
return groups;
|
|
671
679
|
}
|
|
672
680
|
mergeWaitingSeats(groups) {
|
|
673
681
|
for (const [tableId, state] of this.mahjongTables) {
|
|
674
|
-
const
|
|
675
|
-
if (!
|
|
682
|
+
const table = uniqueMahjongConfigs(this.mahjongTableConfigs()).find((candidate) => candidate.tableId === tableId);
|
|
683
|
+
if (!table)
|
|
676
684
|
continue;
|
|
685
|
+
const label = mahjongSessionLabel(table, this.config.mahjongLabelPrefix ?? "麻将桌");
|
|
686
|
+
let group = groups.groups.find((candidate) => candidate.label === label);
|
|
687
|
+
if (!group) {
|
|
688
|
+
group = { label, table, players: [] };
|
|
689
|
+
groups.groups.push(group);
|
|
690
|
+
}
|
|
677
691
|
for (const seat of state.waiting) {
|
|
678
692
|
if (group.players.some((player) => player.playerId === seat.playerId))
|
|
679
693
|
continue;
|
|
680
|
-
const existing =
|
|
681
|
-
.flat()
|
|
694
|
+
const existing = groups.groups.flatMap((candidate) => candidate.players)
|
|
682
695
|
.find((player) => player.playerId === seat.playerId);
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
if (candidate !== group) {
|
|
696
|
+
for (const candidate of groups.groups) {
|
|
697
|
+
if (candidate !== group)
|
|
686
698
|
candidate.players = candidate.players.filter((player) => player.playerId !== seat.playerId);
|
|
687
|
-
}
|
|
688
699
|
}
|
|
689
700
|
group.players.push(existing ?? { playerId: seat.playerId, sessions: [], displayName: seat.displayName });
|
|
690
701
|
}
|
|
@@ -695,8 +706,8 @@ class PrismKoishiService {
|
|
|
695
706
|
return playerId || "未知玩家";
|
|
696
707
|
const platformName = await this.resolvePlatformName(sender.id);
|
|
697
708
|
const name = platformName
|
|
698
|
-
|| (sender.name && sender.name !== sender.id ? sender.name :
|
|
699
|
-
||
|
|
709
|
+
|| (sender.name && sender.name !== sender.id ? sender.name : "")
|
|
710
|
+
|| "未知昵称";
|
|
700
711
|
return `玩家:${name}(${this.config.provider.toUpperCase()}:${sender.id})`;
|
|
701
712
|
}
|
|
702
713
|
mahjongTableForPlayer(playerId) {
|
|
@@ -709,7 +720,33 @@ class PrismKoishiService {
|
|
|
709
720
|
return null;
|
|
710
721
|
}
|
|
711
722
|
mahjongTableConfigs() {
|
|
712
|
-
return
|
|
723
|
+
return resolveMahjongTableConfigs(this.config.mahjongTableConfigs ?? [], this.config.mahjongTables ?? "", this.config.mahjongLabelPrefix ?? "麻将桌");
|
|
724
|
+
}
|
|
725
|
+
syncMahjongTableStates(sessions) {
|
|
726
|
+
const tables = uniqueMahjongConfigs(this.mahjongTableConfigs());
|
|
727
|
+
const tableByLabel = new Map(tables.map((table) => [
|
|
728
|
+
mahjongSessionLabel(table, this.config.mahjongLabelPrefix ?? "麻将桌"),
|
|
729
|
+
table,
|
|
730
|
+
]));
|
|
731
|
+
const activeByTable = new Map();
|
|
732
|
+
for (const session of sessions) {
|
|
733
|
+
const table = tableByLabel.get(session.label ?? "");
|
|
734
|
+
const playerId = String(session.playerId ?? "");
|
|
735
|
+
const sessionId = String(session.id ?? "");
|
|
736
|
+
if (!table || !playerId || !sessionId)
|
|
737
|
+
continue;
|
|
738
|
+
const active = activeByTable.get(table.tableId) ?? {};
|
|
739
|
+
active[playerId] = sessionId;
|
|
740
|
+
activeByTable.set(table.tableId, active);
|
|
741
|
+
}
|
|
742
|
+
for (const table of tables) {
|
|
743
|
+
const state = this.mahjongTables.get(table.tableId) ?? { waiting: [], activeSessions: {} };
|
|
744
|
+
state.activeSessions = activeByTable.get(table.tableId) ?? {};
|
|
745
|
+
state.waiting = state.waiting.filter((seat) => !state.activeSessions[seat.playerId]);
|
|
746
|
+
if (state.waiting.length > 0 || Object.keys(state.activeSessions).length > 0 || this.mahjongTables.has(table.tableId)) {
|
|
747
|
+
this.mahjongTables.set(table.tableId, state);
|
|
748
|
+
}
|
|
749
|
+
}
|
|
713
750
|
}
|
|
714
751
|
async resolvePlayer(sender) {
|
|
715
752
|
return (await this.client.resolveOrRegisterIdentity(this.identity(sender)));
|
|
@@ -936,6 +973,23 @@ function parseMahjongTables(value, labelPrefix) {
|
|
|
936
973
|
}
|
|
937
974
|
return tables;
|
|
938
975
|
}
|
|
976
|
+
function resolveMahjongTableConfigs(structured, legacyValue, labelPrefix) {
|
|
977
|
+
if (structured.length === 0)
|
|
978
|
+
return parseMahjongTables(legacyValue, labelPrefix);
|
|
979
|
+
const tables = new Map();
|
|
980
|
+
for (const input of structured) {
|
|
981
|
+
const displayName = cleanText(input.displayName);
|
|
982
|
+
const tableId = displayName;
|
|
983
|
+
const aliases = [...new Set((input.aliases ?? []).map(cleanText).filter(Boolean))];
|
|
984
|
+
const pricingConfigIds = (input.pricingConfigIds ?? []).map(cleanText).filter(Boolean);
|
|
985
|
+
if (!tableId || !displayName || pricingConfigIds.length === 0)
|
|
986
|
+
continue;
|
|
987
|
+
const table = { tableId, displayName, aliases, pricingConfigIds };
|
|
988
|
+
for (const alias of aliases)
|
|
989
|
+
tables.set(alias, table);
|
|
990
|
+
}
|
|
991
|
+
return tables;
|
|
992
|
+
}
|
|
939
993
|
function uniqueMahjongConfigs(tables) {
|
|
940
994
|
return [...new Map([...tables.values()].map((table) => [table.tableId, table])).values()];
|
|
941
995
|
}
|
|
@@ -954,17 +1008,21 @@ function groupSessionsByPlayer(sessions) {
|
|
|
954
1008
|
}
|
|
955
1009
|
return players;
|
|
956
1010
|
}
|
|
1011
|
+
function sessionStartedAt(session) {
|
|
1012
|
+
const value = Date.parse(session.startedAt ?? "");
|
|
1013
|
+
return Number.isFinite(value) ? value : 0;
|
|
1014
|
+
}
|
|
957
1015
|
function formatPlayerGroups(groups, tableSize, mahjongLabelPrefix) {
|
|
958
|
-
const
|
|
959
|
-
const total =
|
|
1016
|
+
const populatedGroups = groups.groups.filter((group) => group.players.length > 0);
|
|
1017
|
+
const total = new Set(populatedGroups.flatMap((group) => group.players.map((player) => player.playerId))).size;
|
|
960
1018
|
if (total === 0)
|
|
961
1019
|
return "🫥 窝里目前没有玩家呢";
|
|
962
1020
|
const lines = [`[总计 ${total} 人]`];
|
|
963
|
-
|
|
964
|
-
|
|
965
|
-
|
|
966
|
-
|
|
967
|
-
lines.push(
|
|
1021
|
+
for (const group of populatedGroups) {
|
|
1022
|
+
const heading = group.table
|
|
1023
|
+
? `${formatMahjongTableLabel(group.table, mahjongLabelPrefix)} ( ${group.players.length}/${tableSize} )`
|
|
1024
|
+
: `${group.label} ( ${group.players.length}人 )`;
|
|
1025
|
+
lines.push(`\n${heading}:\n${formatPlayerNames(group.players)}`);
|
|
968
1026
|
}
|
|
969
1027
|
return lines.join("\n");
|
|
970
1028
|
}
|
|
@@ -1005,10 +1063,9 @@ function normalizeTargetSubject(value) {
|
|
|
1005
1063
|
const separator = subject.indexOf(":");
|
|
1006
1064
|
return separator > 0 ? subject.slice(separator + 1) : subject;
|
|
1007
1065
|
}
|
|
1008
|
-
function formatPlayerReference(sender) {
|
|
1009
|
-
|
|
1010
|
-
|
|
1011
|
-
: sender.id;
|
|
1066
|
+
function formatPlayerReference(sender, provider = "qq") {
|
|
1067
|
+
const name = sender.name && sender.name !== sender.id ? sender.name : "未知昵称";
|
|
1068
|
+
return `${name}(${provider.toUpperCase()}:${sender.id})`;
|
|
1012
1069
|
}
|
|
1013
1070
|
function toNumber(value) {
|
|
1014
1071
|
if (value == null)
|