koishi-plugin-prism 0.1.6 → 0.1.7
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 +3 -3
- package/lib/index.js +114 -22
- package/package.json +2 -2
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` 会按音乐游戏和麻将桌分组,并对同一玩家的多个计时会话去重;麻将桌显示当前人数和容量。未满桌候座由机器人进程暂存,机器人重启后不会保留。
|
|
12
12
|
* 🔌 **硬件设备状态与电源管理**:可直接在聊天中查看设备状态(`/show`)、远程开启/关闭电源(`/on`、`/off`)、远程投币(`/coin`)和模拟刷卡(`/scan`)。
|
|
13
13
|
* 🎟️ **礼物兑换码**:使用 `/redeem <code>` 兑换系统发放的福利礼包。
|
|
14
14
|
* 🛠️ **管理员高级指令**:允许管理员在聊天中查看玩家(`/admin.players`)、创建新玩家账户(`/admin.create-player`)、充值余额(`/admin.grant-balance`)、手动结账(`/admin.checkout`)以及制作礼包兑换码(`/admin.redeem-code`)。
|
|
@@ -41,7 +41,7 @@
|
|
|
41
41
|
* `billing` - 预览当前玩家本场计费的消费费用。
|
|
42
42
|
* `wallet` - 查看当前玩家的钱包余额。
|
|
43
43
|
* `items` - 查看当前玩家持有的道具或资产。
|
|
44
|
-
* `list` -
|
|
44
|
+
* `list` - 查看当前在线/在店游玩玩家的列表,按音乐游戏和麻将桌分组并对同一玩家的多个计时会话去重;麻将桌显示当前人数和容量。未满桌候座由机器人进程暂存,机器人重启后不会保留。
|
|
45
45
|
* `show [deviceId]` - 查看设备电源与连接状态。
|
|
46
46
|
* `history` - 查看自己的历史游玩记录。
|
|
47
47
|
* `lock` - 发送开门指令。
|
|
@@ -50,7 +50,7 @@
|
|
|
50
50
|
* `coin <deviceId> [count]` - 请求向指定设备投币指定枚数。
|
|
51
51
|
* `scan <deviceId> <subject>` - 请求向设备发送模拟刷卡。
|
|
52
52
|
* `redeem <code>` - 兑换礼物码。
|
|
53
|
-
* `mahjong <tableId>` / `上桌 <tableId>` -
|
|
53
|
+
* `mahjong <tableId>` / `上桌 <tableId>` - 加入指定麻将桌;仅允许已通过 `login`/`入场` 开启默认入场会话的玩家使用。
|
|
54
54
|
* `下桌 <tableId>` - 离开指定麻将桌。
|
|
55
55
|
|
|
56
56
|
### 管理员指令 (需在白名单内并开启)
|
package/lib/index.js
CHANGED
|
@@ -389,6 +389,11 @@ class PrismKoishiService {
|
|
|
389
389
|
const tableSubject = tableConfig.displayName || `${tableKey} 桌`;
|
|
390
390
|
const player = await this.resolvePlayer(sender);
|
|
391
391
|
const playerId = String(player.id ?? "");
|
|
392
|
+
const activeResult = (await this.client.listActiveSessions());
|
|
393
|
+
const activeSessions = (activeResult.sessions ?? []);
|
|
394
|
+
if (!this.hasEntrySession(playerId, activeSessions)) {
|
|
395
|
+
return "请先入场后再上桌。";
|
|
396
|
+
}
|
|
392
397
|
const existing = this.mahjongTableForPlayer(playerId);
|
|
393
398
|
if (existing)
|
|
394
399
|
return `你已经在 ${existing} 桌了。`;
|
|
@@ -552,28 +557,14 @@ class PrismKoishiService {
|
|
|
552
557
|
async listActiveSessions(sender) {
|
|
553
558
|
const result = (await this.client.listActiveSessions());
|
|
554
559
|
const sessions = (result?.sessions ?? []);
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
if (platformName) {
|
|
564
|
-
display = `${platformName} ( ${identitySubject} )`;
|
|
565
|
-
}
|
|
566
|
-
else {
|
|
567
|
-
display = `${session.playerDisplayName || identitySubject} ( ${identitySubject} )`;
|
|
568
|
-
}
|
|
569
|
-
}
|
|
570
|
-
else {
|
|
571
|
-
display = session.playerDisplayName || session.playerId || "未知玩家";
|
|
572
|
-
}
|
|
573
|
-
const timeStr = formatDateTime(session.startedAt);
|
|
574
|
-
lines.push(`玩家: ${display}\n入场时间: ${timeStr}`);
|
|
575
|
-
}
|
|
576
|
-
return lines.join("\n\n");
|
|
560
|
+
const tableByLabel = new Map(uniqueMahjongConfigs(this.mahjongTableConfigs()).map((table) => [
|
|
561
|
+
mahjongSessionLabel(table, this.config.mahjongLabelPrefix ?? "麻将桌"),
|
|
562
|
+
table,
|
|
563
|
+
]));
|
|
564
|
+
const players = groupSessionsByPlayer(sessions);
|
|
565
|
+
const groups = await this.buildPlayerGroups(players, tableByLabel);
|
|
566
|
+
this.mergeWaitingSeats(groups);
|
|
567
|
+
return formatPlayerGroups(groups, this.config.mahjongTableSize ?? 4, this.config.mahjongLabelPrefix ?? "麻将桌");
|
|
577
568
|
}
|
|
578
569
|
async listDeviceStates(rawAlias) {
|
|
579
570
|
const alias = cleanText(rawAlias);
|
|
@@ -712,6 +703,64 @@ class PrismKoishiService {
|
|
|
712
703
|
return null;
|
|
713
704
|
}
|
|
714
705
|
}
|
|
706
|
+
async displayNameForPlayer(player) {
|
|
707
|
+
let identitySubject;
|
|
708
|
+
for (const session of player.sessions) {
|
|
709
|
+
const subject = findSubjectForSession(session, this.config.provider);
|
|
710
|
+
if (!subject)
|
|
711
|
+
continue;
|
|
712
|
+
identitySubject ??= subject;
|
|
713
|
+
const platformName = await this.resolvePlatformName(subject);
|
|
714
|
+
if (platformName)
|
|
715
|
+
return platformName;
|
|
716
|
+
}
|
|
717
|
+
return player.sessions.find((session) => session.playerDisplayName)?.playerDisplayName
|
|
718
|
+
|| identitySubject
|
|
719
|
+
|| player.displayName
|
|
720
|
+
|| player.playerId
|
|
721
|
+
|| "未知玩家";
|
|
722
|
+
}
|
|
723
|
+
async buildPlayerGroups(players, tableByLabel) {
|
|
724
|
+
const groups = {
|
|
725
|
+
music: [],
|
|
726
|
+
mahjong: uniqueMahjongConfigs(this.mahjongTableConfigs()).map((table) => ({ table, players: [] })),
|
|
727
|
+
};
|
|
728
|
+
const groupForTable = new Map(groups.mahjong.map((group) => [group.table.tableId, group]));
|
|
729
|
+
for (const player of players.values()) {
|
|
730
|
+
player.displayName = await this.displayNameForPlayer(player);
|
|
731
|
+
const table = player.sessions
|
|
732
|
+
.map((session) => tableByLabel.get(session.label ?? ""))
|
|
733
|
+
.find((value) => Boolean(value));
|
|
734
|
+
if (table) {
|
|
735
|
+
groupForTable.get(table.tableId)?.players.push(player);
|
|
736
|
+
}
|
|
737
|
+
else {
|
|
738
|
+
groups.music.push(player);
|
|
739
|
+
}
|
|
740
|
+
}
|
|
741
|
+
return groups;
|
|
742
|
+
}
|
|
743
|
+
mergeWaitingSeats(groups) {
|
|
744
|
+
for (const [tableId, state] of this.mahjongTables) {
|
|
745
|
+
const group = groups.mahjong.find((candidate) => candidate.table.tableId === tableId);
|
|
746
|
+
if (!group)
|
|
747
|
+
continue;
|
|
748
|
+
for (const seat of state.waiting) {
|
|
749
|
+
if (group.players.some((player) => player.playerId === seat.playerId))
|
|
750
|
+
continue;
|
|
751
|
+
const existing = [groups.music, ...groups.mahjong.map((candidate) => candidate.players)]
|
|
752
|
+
.flat()
|
|
753
|
+
.find((player) => player.playerId === seat.playerId);
|
|
754
|
+
groups.music = groups.music.filter((player) => player.playerId !== seat.playerId);
|
|
755
|
+
for (const candidate of groups.mahjong) {
|
|
756
|
+
if (candidate !== group) {
|
|
757
|
+
candidate.players = candidate.players.filter((player) => player.playerId !== seat.playerId);
|
|
758
|
+
}
|
|
759
|
+
}
|
|
760
|
+
group.players.push(existing ?? { playerId: seat.playerId, sessions: [], displayName: seat.displayName });
|
|
761
|
+
}
|
|
762
|
+
}
|
|
763
|
+
}
|
|
715
764
|
async resolvePlayerDisplay(sender, playerId) {
|
|
716
765
|
if (!sender)
|
|
717
766
|
return playerId || "未知玩家";
|
|
@@ -736,6 +785,10 @@ class PrismKoishiService {
|
|
|
736
785
|
async resolvePlayer(sender) {
|
|
737
786
|
return (await this.client.resolveOrRegisterIdentity(this.identity(sender)));
|
|
738
787
|
}
|
|
788
|
+
hasEntrySession(playerId, sessions) {
|
|
789
|
+
const label = this.config.loginSessionLabel?.trim();
|
|
790
|
+
return sessions.some((session) => session.playerId === playerId && (label ? session.label === label : true));
|
|
791
|
+
}
|
|
739
792
|
identity(sender) {
|
|
740
793
|
return {
|
|
741
794
|
provider: this.config.provider,
|
|
@@ -924,6 +977,45 @@ function parseMahjongTables(value, labelPrefix) {
|
|
|
924
977
|
}
|
|
925
978
|
return tables;
|
|
926
979
|
}
|
|
980
|
+
function uniqueMahjongConfigs(tables) {
|
|
981
|
+
return [...new Map([...tables.values()].map((table) => [table.tableId, table])).values()];
|
|
982
|
+
}
|
|
983
|
+
function mahjongSessionLabel(table, labelPrefix) {
|
|
984
|
+
return table.displayName || `${labelPrefix} ${table.tableId}`;
|
|
985
|
+
}
|
|
986
|
+
function groupSessionsByPlayer(sessions) {
|
|
987
|
+
const players = new Map();
|
|
988
|
+
for (const session of sessions) {
|
|
989
|
+
const playerId = session.playerId || findSubjectForSession(session, "") || "";
|
|
990
|
+
if (!playerId)
|
|
991
|
+
continue;
|
|
992
|
+
const player = players.get(playerId) ?? { playerId, sessions: [] };
|
|
993
|
+
player.sessions.push(session);
|
|
994
|
+
players.set(playerId, player);
|
|
995
|
+
}
|
|
996
|
+
return players;
|
|
997
|
+
}
|
|
998
|
+
function formatPlayerGroups(groups, tableSize, mahjongLabelPrefix) {
|
|
999
|
+
const populatedMahjongGroups = groups.mahjong.filter((group) => group.players.length > 0);
|
|
1000
|
+
const total = groups.music.length + populatedMahjongGroups.reduce((sum, group) => sum + group.players.length, 0);
|
|
1001
|
+
if (total === 0)
|
|
1002
|
+
return "🫥 窝里目前没有玩家呢";
|
|
1003
|
+
const lines = [`[总计 ${total} 人]`];
|
|
1004
|
+
if (groups.music.length > 0) {
|
|
1005
|
+
lines.push(`🎵 音乐游戏 ( ${groups.music.length}人 ):\n${formatPlayerNames(groups.music)}`);
|
|
1006
|
+
}
|
|
1007
|
+
for (const group of populatedMahjongGroups) {
|
|
1008
|
+
lines.push(`${formatMahjongTableLabel(group.table, mahjongLabelPrefix)} ( ${group.players.length}/${tableSize} ):\n${formatPlayerNames(group.players)}`);
|
|
1009
|
+
}
|
|
1010
|
+
return lines.join("\n");
|
|
1011
|
+
}
|
|
1012
|
+
function formatMahjongTableLabel(table, labelPrefix) {
|
|
1013
|
+
const label = mahjongSessionLabel(table, labelPrefix);
|
|
1014
|
+
return table.displayName ? label : `🀄️ ${label}`;
|
|
1015
|
+
}
|
|
1016
|
+
function formatPlayerNames(players) {
|
|
1017
|
+
return players.map((player) => `- ${player.displayName || player.playerId || "未知玩家"}`).join(", ");
|
|
1018
|
+
}
|
|
927
1019
|
function splitOnce(text, sep) {
|
|
928
1020
|
const idx = text.indexOf(sep);
|
|
929
1021
|
if (idx === -1)
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "koishi-plugin-prism",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.7",
|
|
4
4
|
"description": "PRiSM Next 计费与设备管理系统的 Koishi 机器人集成插件",
|
|
5
5
|
"main": "./lib/index.js",
|
|
6
6
|
"exports": {
|
|
@@ -48,4 +48,4 @@
|
|
|
48
48
|
"test": "bun test test",
|
|
49
49
|
"typecheck": "tsc -p tsconfig.json"
|
|
50
50
|
}
|
|
51
|
-
}
|
|
51
|
+
}
|