@zhin.js/plugin-rps 1.0.0 → 1.0.2
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 +31 -0
- package/commands/rps/[action:string=].ts +15 -0
- package/lib/engine.d.ts +0 -1
- package/lib/engine.js +2 -1
- package/lib/engine.js.map +1 -1
- package/lib/game-flow.d.ts +6 -7
- package/lib/game-flow.js +18 -16
- package/lib/game-flow.js.map +1 -1
- package/lib/index.d.ts +8 -2
- package/lib/index.js +8 -25
- package/lib/index.js.map +1 -1
- package/lib/memory-db.d.ts +6 -0
- package/lib/memory-db.js +18 -0
- package/lib/memory-db.js.map +1 -0
- package/lib/models.d.ts +5 -4
- package/lib/models.js +2 -2
- package/lib/models.js.map +1 -1
- package/lib/rps-command.d.ts +4 -3
- package/lib/rps-command.js +5 -1
- package/lib/rps-command.js.map +1 -1
- package/lib/runtime-store.d.ts +6 -0
- package/lib/runtime-store.js +7 -0
- package/lib/runtime-store.js.map +1 -0
- package/lib/session-service.d.ts +1 -3
- package/lib/session-service.js +3 -12
- package/lib/session-service.js.map +1 -1
- package/lib/view.d.ts +2 -3
- package/lib/view.js +5 -3
- package/lib/view.js.map +1 -1
- package/middlewares/rps-alias.ts +18 -0
- package/middlewares/rps-choice.ts +66 -0
- package/package.json +45 -10
- package/plugin.ts +63 -0
- package/schema.json +6 -0
- package/src/engine.ts +3 -1
- package/src/game-flow.ts +21 -22
- package/src/index.ts +9 -36
- package/src/memory-db.ts +22 -0
- package/src/models.ts +7 -4
- package/src/rps-command.ts +12 -3
- package/src/runtime-store.ts +9 -0
- package/src/session-service.ts +4 -11
- package/src/view.ts +6 -3
- package/lib/commands.d.ts +0 -6
- package/lib/commands.d.ts.map +0 -1
- package/lib/commands.js +0 -86
- package/lib/commands.js.map +0 -1
- package/lib/engine.d.ts.map +0 -1
- package/lib/game-flow.d.ts.map +0 -1
- package/lib/hub-register.d.ts +0 -5
- package/lib/hub-register.d.ts.map +0 -1
- package/lib/hub-register.js +0 -26
- package/lib/hub-register.js.map +0 -1
- package/lib/index.d.ts.map +0 -1
- package/lib/models.d.ts.map +0 -1
- package/lib/rps-command.d.ts.map +0 -1
- package/lib/session-service.d.ts.map +0 -1
- package/lib/view.d.ts.map +0 -1
- package/plugin.yml +0 -2
- package/src/commands.ts +0 -102
- package/src/hub-register.ts +0 -30
package/README.md
ADDED
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
# @zhin.js/plugin-rps
|
|
2
|
+
|
|
3
|
+
石头剪刀布猜拳插件,三局两胜;支持游戏大厅按钮与文本命令。
|
|
4
|
+
|
|
5
|
+
## 安装
|
|
6
|
+
|
|
7
|
+
```yaml
|
|
8
|
+
plugins:
|
|
9
|
+
- "@zhin.js/plugin-rps"
|
|
10
|
+
database:
|
|
11
|
+
dialect: sqlite
|
|
12
|
+
storage: ./data/zhin.db
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
## 命令
|
|
16
|
+
|
|
17
|
+
| 命令 | 说明 |
|
|
18
|
+
|------|------|
|
|
19
|
+
| `/猜拳` | 帮助与频道状态 |
|
|
20
|
+
| `/猜拳 开始` | 开始对局 |
|
|
21
|
+
| `/猜拳 继续` | 继续当前局 |
|
|
22
|
+
| `/rps` | 英文别名 |
|
|
23
|
+
|
|
24
|
+
也可通过 `/游戏` 大厅按钮进入。
|
|
25
|
+
|
|
26
|
+
## 开发
|
|
27
|
+
|
|
28
|
+
```bash
|
|
29
|
+
pnpm --filter @zhin.js/plugin-rps build
|
|
30
|
+
pnpm vitest run plugins/games/rps/tests
|
|
31
|
+
```
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { defineCommand } from '@zhin.js/command';
|
|
2
|
+
import { messageFromCommandInput, normalizeRpsAction } from '@zhin.js/game-kit';
|
|
3
|
+
import { RPS_HELP, runRpsCommandText } from '../../src/rps-command.js';
|
|
4
|
+
import { resolveGameServices } from '../../src/runtime-store.js';
|
|
5
|
+
|
|
6
|
+
export default defineCommand({
|
|
7
|
+
description: 'RPS',
|
|
8
|
+
async execute({ params, input, use, owner }) {
|
|
9
|
+
const action = normalizeRpsAction(String(params.action ?? ''));
|
|
10
|
+
if (!action || action === 'help') return RPS_HELP;
|
|
11
|
+
const services = resolveGameServices({ use, owner });
|
|
12
|
+
const message = messageFromCommandInput(input);
|
|
13
|
+
return runRpsCommandText(services, message, action);
|
|
14
|
+
},
|
|
15
|
+
});
|
package/lib/engine.d.ts
CHANGED
package/lib/engine.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { secureRandomItem } from '@zhin.js/game-kit';
|
|
1
2
|
export const RPS_PREFIX = 'rps';
|
|
2
3
|
export const MOVE_LABELS = {
|
|
3
4
|
rock: '✊ 石头',
|
|
@@ -11,7 +12,7 @@ const BEATS = {
|
|
|
11
12
|
};
|
|
12
13
|
export function randomBotMove() {
|
|
13
14
|
const moves = ['rock', 'paper', 'scissors'];
|
|
14
|
-
return
|
|
15
|
+
return secureRandomItem(moves);
|
|
15
16
|
}
|
|
16
17
|
/** 1=玩家胜 2=机器人胜 0=平局 */
|
|
17
18
|
export function resolveRound(player, bot) {
|
package/lib/engine.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"engine.js","sourceRoot":"","sources":["../src/engine.ts"],"names":[],"mappings":"AAAA,MAAM,CAAC,MAAM,UAAU,GAAG,KAAK,CAAC;AAIhC,MAAM,CAAC,MAAM,WAAW,GAA4B;IAClD,IAAI,EAAE,MAAM;IACZ,KAAK,EAAE,KAAK;IACZ,QAAQ,EAAE,OAAO;CAClB,CAAC;AAEF,MAAM,KAAK,GAA6B;IACtC,IAAI,EAAE,UAAU;IAChB,KAAK,EAAE,MAAM;IACb,QAAQ,EAAE,OAAO;CAClB,CAAC;AAEF,MAAM,UAAU,aAAa;IAC3B,MAAM,KAAK,GAAc,CAAC,MAAM,EAAE,OAAO,EAAE,UAAU,CAAC,CAAC;IACvD,OAAO,
|
|
1
|
+
{"version":3,"file":"engine.js","sourceRoot":"","sources":["../src/engine.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,gBAAgB,EAAE,MAAM,mBAAmB,CAAC;AAErD,MAAM,CAAC,MAAM,UAAU,GAAG,KAAK,CAAC;AAIhC,MAAM,CAAC,MAAM,WAAW,GAA4B;IAClD,IAAI,EAAE,MAAM;IACZ,KAAK,EAAE,KAAK;IACZ,QAAQ,EAAE,OAAO;CAClB,CAAC;AAEF,MAAM,KAAK,GAA6B;IACtC,IAAI,EAAE,UAAU;IAChB,KAAK,EAAE,MAAM;IACb,QAAQ,EAAE,OAAO;CAClB,CAAC;AAEF,MAAM,UAAU,aAAa;IAC3B,MAAM,KAAK,GAAc,CAAC,MAAM,EAAE,OAAO,EAAE,UAAU,CAAC,CAAC;IACvD,OAAO,gBAAgB,CAAC,KAAK,CAAC,CAAC;AACjC,CAAC;AAED,wBAAwB;AACxB,MAAM,UAAU,YAAY,CAAC,MAAe,EAAE,GAAY;IACxD,IAAI,MAAM,KAAK,GAAG;QAAE,OAAO,CAAC,CAAC;IAC7B,OAAO,KAAK,CAAC,MAAM,CAAC,KAAK,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;AACvC,CAAC;AAED,MAAM,UAAU,SAAS,CAAC,GAAW;IACnC,MAAM,CAAC,GAAG,GAAG,CAAC,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC;IACnC,MAAM,GAAG,GAA4B;QACnC,IAAI,EAAE,MAAM;QACZ,KAAK,EAAE,OAAO;QACd,QAAQ,EAAE,UAAU;QACpB,EAAE,EAAE,MAAM;QACV,CAAC,EAAE,OAAO;QACV,EAAE,EAAE,UAAU;QACd,CAAC,EAAE,MAAM;QACT,CAAC,EAAE,OAAO;QACV,CAAC,EAAE,UAAU;KACd,CAAC;IACF,OAAO,GAAG,CAAC,CAAC,CAAC,IAAI,IAAI,CAAC;AACxB,CAAC;AAED,MAAM,CAAC,MAAM,UAAU,GAAG,CAAC,CAAC"}
|
package/lib/game-flow.d.ts
CHANGED
|
@@ -1,14 +1,13 @@
|
|
|
1
|
-
import type { Message, Plugin } from 'zhin.js';
|
|
1
|
+
import type { Message, Plugin } from '@zhin.js/core';
|
|
2
2
|
import type { RpsSessionRow } from './models.js';
|
|
3
3
|
import { RPS_PREFIX, type RpsMove } from './engine.js';
|
|
4
4
|
import type { SessionService } from './session-service.js';
|
|
5
|
-
export declare function sendOrEditView(plugin: Plugin, services: SessionService, message: Message<any>, session: RpsSessionRow, lastRound?: {
|
|
5
|
+
export declare function sendOrEditView(plugin: Plugin | null, services: SessionService, message: Message<any>, session: RpsSessionRow, lastRound?: {
|
|
6
6
|
player: RpsMove;
|
|
7
7
|
bot: RpsMove;
|
|
8
8
|
result: 0 | 1 | 2;
|
|
9
|
-
}): Promise<void>;
|
|
10
|
-
export declare function startGame(plugin: Plugin, services: SessionService, message: Message<any>): Promise<string | undefined>;
|
|
11
|
-
export declare function handleChoice(plugin: Plugin, services: SessionService, message: Message<any>, sessionId: string, choiceId: string): Promise<string | null>;
|
|
12
|
-
export declare function continueGame(plugin: Plugin, services: SessionService, message: Message<any>): Promise<string>;
|
|
9
|
+
}): Promise<string | void>;
|
|
10
|
+
export declare function startGame(plugin: Plugin | null, services: SessionService, message: Message<any>): Promise<string | undefined>;
|
|
11
|
+
export declare function handleChoice(plugin: Plugin | null, services: SessionService, message: Message<any>, sessionId: string, choiceId: string): Promise<string | null>;
|
|
12
|
+
export declare function continueGame(plugin: Plugin | null, services: SessionService, message: Message<any>): Promise<string>;
|
|
13
13
|
export { RPS_PREFIX };
|
|
14
|
-
//# sourceMappingURL=game-flow.d.ts.map
|
package/lib/game-flow.js
CHANGED
|
@@ -1,7 +1,10 @@
|
|
|
1
|
+
import { plainTextFromSendContent, recordGameOutcome } from '@zhin.js/game-kit';
|
|
1
2
|
import { RPS_PREFIX, WIN_TARGET, randomBotMove, resolveRound, } from './engine.js';
|
|
2
3
|
import { buildRpsView } from './view.js';
|
|
3
4
|
export async function sendOrEditView(plugin, services, message, session, lastRound) {
|
|
4
|
-
const content = buildRpsView(session, lastRound);
|
|
5
|
+
const content = buildRpsView(session, lastRound, message.$channel.type);
|
|
6
|
+
if (!plugin)
|
|
7
|
+
return plainTextFromSendContent(content);
|
|
5
8
|
const adapter = plugin.root.inject(message.$adapter);
|
|
6
9
|
if (session.board_message_id) {
|
|
7
10
|
const msgId = await adapter.editMessage({
|
|
@@ -31,8 +34,8 @@ export async function startGame(plugin, services, message) {
|
|
|
31
34
|
return `本频道 ${active.player_name} 正在猜拳对决中。`;
|
|
32
35
|
}
|
|
33
36
|
const session = await services.createSession(message);
|
|
34
|
-
await sendOrEditView(plugin, services, message, session);
|
|
35
|
-
return undefined;
|
|
37
|
+
const text = await sendOrEditView(plugin, services, message, session);
|
|
38
|
+
return typeof text === 'string' ? text : undefined;
|
|
36
39
|
}
|
|
37
40
|
export async function handleChoice(plugin, services, message, sessionId, choiceId) {
|
|
38
41
|
const session = await services.getById(sessionId);
|
|
@@ -44,16 +47,9 @@ export async function handleChoice(plugin, services, message, sessionId, choiceI
|
|
|
44
47
|
if (session.player_id !== message.$sender.id)
|
|
45
48
|
return '这是别人的对局。';
|
|
46
49
|
if (choiceId === 'restart') {
|
|
47
|
-
await services.updateSession(session.id, {
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
round: 0,
|
|
51
|
-
status: 'active',
|
|
52
|
-
board_message_id: '',
|
|
53
|
-
});
|
|
54
|
-
const updated = (await services.getById(session.id));
|
|
55
|
-
await sendOrEditView(plugin, services, message, updated);
|
|
56
|
-
return null;
|
|
50
|
+
await services.updateSession(session.id, { status: 'aborted' });
|
|
51
|
+
// text-only 模式(plugin===null)下 startGame 的唯一输出就是返回文本
|
|
52
|
+
return (await startGame(plugin, services, message)) ?? null;
|
|
57
53
|
}
|
|
58
54
|
const player = choiceId;
|
|
59
55
|
if (!['rock', 'paper', 'scissors'].includes(player))
|
|
@@ -78,14 +74,20 @@ export async function handleChoice(plugin, services, message, sessionId, choiceI
|
|
|
78
74
|
status,
|
|
79
75
|
});
|
|
80
76
|
const updated = (await services.getById(session.id));
|
|
81
|
-
|
|
82
|
-
|
|
77
|
+
if (status === 'won')
|
|
78
|
+
void recordGameOutcome(message, 'rps', 'won', playerWins * 10);
|
|
79
|
+
else if (status === 'lost')
|
|
80
|
+
void recordGameOutcome(message, 'rps', 'lost');
|
|
81
|
+
const text = await sendOrEditView(plugin, services, message, updated, { player, bot, result });
|
|
82
|
+
return typeof text === 'string' ? text : null;
|
|
83
83
|
}
|
|
84
84
|
export async function continueGame(plugin, services, message) {
|
|
85
85
|
const session = await services.getActiveForUser(`${message.$adapter}-${message.$endpoint}-${message.$channel.type}:${message.$channel.id}`, message.$sender.id);
|
|
86
86
|
if (!session)
|
|
87
87
|
return '你没有进行中的猜拳,发送「猜拳 开始」。';
|
|
88
|
-
await sendOrEditView(plugin, services, message, session);
|
|
88
|
+
const text = await sendOrEditView(plugin, services, message, session);
|
|
89
|
+
if (typeof text === 'string')
|
|
90
|
+
return text;
|
|
89
91
|
return '已刷新猜拳界面。';
|
|
90
92
|
}
|
|
91
93
|
export { RPS_PREFIX };
|
package/lib/game-flow.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"game-flow.js","sourceRoot":"","sources":["../src/game-flow.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"game-flow.js","sourceRoot":"","sources":["../src/game-flow.ts"],"names":[],"mappings":"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"}
|
package/lib/index.d.ts
CHANGED
|
@@ -1,2 +1,8 @@
|
|
|
1
|
-
export {};
|
|
2
|
-
|
|
1
|
+
export { RPS_HELP } from './rps-command.js';
|
|
2
|
+
export { gameServicesToken, resolveGameServices } from './runtime-store.js';
|
|
3
|
+
export { createInMemoryRpsDb, mountRpsMemoryServices } from './memory-db.js';
|
|
4
|
+
/**
|
|
5
|
+
* 已由 plugin.ts 接线:commands/ 命令、middlewares/ 文本与选项中间件、
|
|
6
|
+
* 游戏大厅注册(registerRuntimeGame)、过期会话 cron(scheduleHostToken)、
|
|
7
|
+
* host DatabaseHost(databaseHostToken)。仍未接:interactive 按钮回调。
|
|
8
|
+
*/
|
package/lib/index.js
CHANGED
|
@@ -1,26 +1,9 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
let services = null;
|
|
10
|
-
useContext('database', (dbFeature) => {
|
|
11
|
-
services = createServices(resolveGameDatabase(dbFeature));
|
|
12
|
-
logger.info(formatCompact({ 模块: '猜拳', 数据模型: '已就绪' }));
|
|
13
|
-
});
|
|
14
|
-
registerRpsHub(() => services);
|
|
15
|
-
registerCommands(plugin, () => services);
|
|
16
|
-
registerInteractive(plugin, () => services);
|
|
17
|
-
registerTextFallback(plugin, () => services);
|
|
18
|
-
addCron(new Cron('0 */15 * * * *', async () => {
|
|
19
|
-
if (!services)
|
|
20
|
-
return;
|
|
21
|
-
const n = await services.abortStale(30 * 60 * 1000);
|
|
22
|
-
if (n > 0)
|
|
23
|
-
logger.debug(formatCompact({ 猜拳: '清理超时局', count: n }));
|
|
24
|
-
}));
|
|
25
|
-
logger.info(formatCompact({ 模块: '猜拳', 状态: '已加载' }));
|
|
1
|
+
export { RPS_HELP } from './rps-command.js';
|
|
2
|
+
export { gameServicesToken, resolveGameServices } from './runtime-store.js';
|
|
3
|
+
export { createInMemoryRpsDb, mountRpsMemoryServices } from './memory-db.js';
|
|
4
|
+
/**
|
|
5
|
+
* 已由 plugin.ts 接线:commands/ 命令、middlewares/ 文本与选项中间件、
|
|
6
|
+
* 游戏大厅注册(registerRuntimeGame)、过期会话 cron(scheduleHostToken)、
|
|
7
|
+
* host DatabaseHost(databaseHostToken)。仍未接:interactive 按钮回调。
|
|
8
|
+
*/
|
|
26
9
|
//# sourceMappingURL=index.js.map
|
package/lib/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AAC5C,OAAO,EAAE,iBAAiB,EAAE,mBAAmB,EAAE,MAAM,oBAAoB,CAAC;AAC5E,OAAO,EAAE,mBAAmB,EAAE,sBAAsB,EAAE,MAAM,gBAAgB,CAAC;AAE7E;;;;GAIG"}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import type { DatabaseHost } from '@zhin.js/plugin-runtime';
|
|
2
|
+
import { type RpsDatabase, type SessionService } from './session-service.js';
|
|
3
|
+
/** In-memory rps_sessions for Plugin Runtime slice-2 (no DatabaseFeature). */
|
|
4
|
+
export declare function createInMemoryRpsDb(): RpsDatabase;
|
|
5
|
+
export declare function mountRpsMemoryServices(): SessionService;
|
|
6
|
+
export declare function mountRpsHostServices(host: DatabaseHost): SessionService;
|
package/lib/memory-db.js
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { createHostGameDb, createInMemoryGameDb } from '@zhin.js/game-kit';
|
|
2
|
+
import { defineHostTables } from './models.js';
|
|
3
|
+
import { createServices } from './session-service.js';
|
|
4
|
+
const RPS_TABLES = ['rps_sessions'];
|
|
5
|
+
/** In-memory rps_sessions for Plugin Runtime slice-2 (no DatabaseFeature). */
|
|
6
|
+
export function createInMemoryRpsDb() {
|
|
7
|
+
return createInMemoryGameDb(RPS_TABLES);
|
|
8
|
+
}
|
|
9
|
+
export function mountRpsMemoryServices() {
|
|
10
|
+
const services = createServices(createInMemoryRpsDb());
|
|
11
|
+
return services;
|
|
12
|
+
}
|
|
13
|
+
export function mountRpsHostServices(host) {
|
|
14
|
+
defineHostTables(host);
|
|
15
|
+
const services = createServices(createHostGameDb(host, RPS_TABLES));
|
|
16
|
+
return services;
|
|
17
|
+
}
|
|
18
|
+
//# sourceMappingURL=memory-db.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"memory-db.js","sourceRoot":"","sources":["../src/memory-db.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,gBAAgB,EAAE,oBAAoB,EAAE,MAAM,mBAAmB,CAAC;AAE3E,OAAO,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAC;AAC/C,OAAO,EAAE,cAAc,EAAyC,MAAM,sBAAsB,CAAC;AAE7F,MAAM,UAAU,GAAG,CAAC,cAAc,CAAU,CAAC;AAE7C,8EAA8E;AAC9E,MAAM,UAAU,mBAAmB;IACjC,OAAO,oBAAoB,CAAC,UAAU,CAA2B,CAAC;AACpE,CAAC;AAED,MAAM,UAAU,sBAAsB;IACpC,MAAM,QAAQ,GAAG,cAAc,CAAC,mBAAmB,EAAE,CAAC,CAAC;IACvD,OAAO,QAAQ,CAAC;AAClB,CAAC;AAED,MAAM,UAAU,oBAAoB,CAAC,IAAkB;IACrD,gBAAgB,CAAC,IAAI,CAAC,CAAC;IACvB,MAAM,QAAQ,GAAG,cAAc,CAAC,gBAAgB,CAAC,IAAI,EAAE,UAAU,CAA2B,CAAC,CAAC;IAC9F,OAAO,QAAQ,CAAC;AAClB,CAAC"}
|
package/lib/models.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import type { Models
|
|
1
|
+
import type { Models } from '@zhin.js/core';
|
|
2
2
|
export type RpsSessionStatus = 'active' | 'won' | 'lost' | 'aborted';
|
|
3
|
-
declare module 'zhin.js' {
|
|
3
|
+
declare module '@zhin.js/core' {
|
|
4
4
|
interface Models {
|
|
5
5
|
rps_sessions: {
|
|
6
6
|
id: string;
|
|
@@ -22,5 +22,6 @@ declare module 'zhin.js' {
|
|
|
22
22
|
}
|
|
23
23
|
}
|
|
24
24
|
export type RpsSessionRow = Models['rps_sessions'];
|
|
25
|
-
export declare function
|
|
26
|
-
|
|
25
|
+
export declare function defineHostTables(db: {
|
|
26
|
+
define: (name: string, definition: Record<string, unknown>) => void;
|
|
27
|
+
}): void;
|
package/lib/models.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
export function
|
|
2
|
-
|
|
1
|
+
export function defineHostTables(db) {
|
|
2
|
+
db.define('rps_sessions', {
|
|
3
3
|
id: { type: 'text', primary: true },
|
|
4
4
|
adapter: { type: 'text', nullable: false },
|
|
5
5
|
endpoint: { type: 'text', nullable: false },
|
package/lib/models.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"models.js","sourceRoot":"","sources":["../src/models.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"models.js","sourceRoot":"","sources":["../src/models.ts"],"names":[],"mappings":"AA6BA,MAAM,UAAU,gBAAgB,CAC9B,EAA2E;IAE3E,EAAE,CAAC,MAAM,CAAC,cAAc,EAAE;QACxB,EAAE,EAAE,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,IAAI,EAAE;QACnC,OAAO,EAAE,EAAE,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,KAAK,EAAE;QAC1C,QAAQ,EAAE,EAAE,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,KAAK,EAAE;QAC3C,YAAY,EAAE,EAAE,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,KAAK,EAAE;QAC/C,UAAU,EAAE,EAAE,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,KAAK,EAAE;QAC7C,WAAW,EAAE,EAAE,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,KAAK,EAAE;QAC9C,SAAS,EAAE,EAAE,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,KAAK,EAAE;QAC5C,WAAW,EAAE,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,EAAE,EAAE;QAC1C,WAAW,EAAE,EAAE,IAAI,EAAE,SAAS,EAAE,OAAO,EAAE,CAAC,EAAE;QAC5C,QAAQ,EAAE,EAAE,IAAI,EAAE,SAAS,EAAE,OAAO,EAAE,CAAC,EAAE;QACzC,KAAK,EAAE,EAAE,IAAI,EAAE,SAAS,EAAE,OAAO,EAAE,CAAC,EAAE;QACtC,MAAM,EAAE,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,QAAQ,EAAE;QAC3C,gBAAgB,EAAE,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,EAAE,EAAE;QAC/C,UAAU,EAAE,EAAE,IAAI,EAAE,SAAS,EAAE,OAAO,EAAE,CAAC,EAAE;QAC3C,UAAU,EAAE,EAAE,IAAI,EAAE,SAAS,EAAE,OAAO,EAAE,CAAC,EAAE;KAC5C,CAAC,CAAC;AACL,CAAC"}
|
package/lib/rps-command.d.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
|
-
import type { Message, Plugin } from 'zhin.js';
|
|
1
|
+
import type { Message, Plugin } from '@zhin.js/core';
|
|
2
2
|
import type { SessionService } from './session-service.js';
|
|
3
3
|
export declare const RPS_HELP: string;
|
|
4
|
-
export declare function runRpsCommand(plugin: Plugin, services: SessionService, message: Message<any>, action: string): Promise<string | undefined>;
|
|
5
|
-
|
|
4
|
+
export declare function runRpsCommand(plugin: Plugin | null, services: SessionService, message: Message<any>, action: string): Promise<string | undefined>;
|
|
5
|
+
/** Plugin Runtime / smoke: text-only, no Adapter.editMessage. */
|
|
6
|
+
export declare function runRpsCommandText(services: SessionService, message: Message<any>, action: string): Promise<string>;
|
package/lib/rps-command.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { channelKey } from '@zhin.js/game-
|
|
1
|
+
import { channelKey } from '@zhin.js/game-kit';
|
|
2
2
|
import { continueGame, startGame } from './game-flow.js';
|
|
3
3
|
export const RPS_HELP = [
|
|
4
4
|
'✊✋✌️ 猜拳对决(三局两胜)',
|
|
@@ -34,4 +34,8 @@ export async function runRpsCommand(plugin, services, message, action) {
|
|
|
34
34
|
}
|
|
35
35
|
return `未知子命令:${action}\n\n${RPS_HELP}`;
|
|
36
36
|
}
|
|
37
|
+
/** Plugin Runtime / smoke: text-only, no Adapter.editMessage. */
|
|
38
|
+
export async function runRpsCommandText(services, message, action) {
|
|
39
|
+
return (await runRpsCommand(null, services, message, action)) ?? '';
|
|
40
|
+
}
|
|
37
41
|
//# sourceMappingURL=rps-command.js.map
|
package/lib/rps-command.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"rps-command.js","sourceRoot":"","sources":["../src/rps-command.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,UAAU,EAAE,MAAM,
|
|
1
|
+
{"version":3,"file":"rps-command.js","sourceRoot":"","sources":["../src/rps-command.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAC;AAC/C,OAAO,EAAE,YAAY,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAC;AAGzD,MAAM,CAAC,MAAM,QAAQ,GAAG;IACtB,iBAAiB;IACjB,eAAe;IACf,aAAa;IACb,cAAc;IACd,cAAc;CACf,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AAEb,MAAM,CAAC,KAAK,UAAU,aAAa,CACjC,MAAqB,EACrB,QAAwB,EACxB,OAAqB,EACrB,MAAc;IAEd,MAAM,EAAE,GAAG,UAAU,CAAC,OAAO,CAAC,CAAC;IAC/B,MAAM,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,EAAE,CAAC;IAElC,IAAI,CAAC,MAAM,IAAI,MAAM,KAAK,MAAM,EAAE,CAAC;QACjC,MAAM,MAAM,GAAG,MAAM,QAAQ,CAAC,kBAAkB,CAAC,EAAE,CAAC,CAAC;QACrD,MAAM,KAAK,GAAG,CAAC,QAAQ,EAAE,EAAE,CAAC,CAAC;QAC7B,IAAI,MAAM,EAAE,CAAC;YACX,KAAK,CAAC,IAAI,CAAC,OAAO,MAAM,CAAC,WAAW,MAAM,MAAM,CAAC,WAAW,IAAI,MAAM,CAAC,QAAQ,EAAE,CAAC,CAAC;QACrF,CAAC;aAAM,CAAC;YACN,KAAK,CAAC,IAAI,CAAC,yBAAyB,CAAC,CAAC;QACxC,CAAC;QACD,OAAO,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IAC1B,CAAC;IAED,IAAI,MAAM,KAAK,OAAO;QAAE,OAAO,SAAS,CAAC,MAAM,EAAE,QAAQ,EAAE,OAAO,CAAC,CAAC;IACpE,IAAI,MAAM,KAAK,UAAU;QAAE,OAAO,YAAY,CAAC,MAAM,EAAE,QAAQ,EAAE,OAAO,CAAC,CAAC;IAE1E,IAAI,MAAM,KAAK,MAAM,EAAE,CAAC;QACtB,MAAM,GAAG,GAAG,MAAM,QAAQ,CAAC,gBAAgB,CAAC,EAAE,EAAE,MAAM,CAAC,CAAC;QACxD,IAAI,CAAC,GAAG;YAAE,OAAO,YAAY,CAAC;QAC9B,MAAM,QAAQ,CAAC,aAAa,CAAC,GAAG,CAAC,EAAE,EAAE,EAAE,MAAM,EAAE,SAAS,EAAE,CAAC,CAAC;QAC5D,OAAO,UAAU,CAAC;IACpB,CAAC;IAED,OAAO,SAAS,MAAM,OAAO,QAAQ,EAAE,CAAC;AAC1C,CAAC;AAED,iEAAiE;AACjE,MAAM,CAAC,KAAK,UAAU,iBAAiB,CACrC,QAAwB,EACxB,OAAqB,EACrB,MAAc;IAEd,OAAO,CAAC,MAAM,aAAa,CAAC,IAAI,EAAE,QAAQ,EAAE,OAAO,EAAE,MAAM,CAAC,CAAC,IAAI,EAAE,CAAC;AACtE,CAAC"}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import type { SessionService } from './session-service.js';
|
|
2
|
+
/** Owner-scoped service consumed by discovered commands and middleware. */
|
|
3
|
+
export declare const gameServicesToken: import("@zhin.js/plugin-runtime").Token<SessionService>;
|
|
4
|
+
export declare function resolveGameServices(context: {
|
|
5
|
+
use<T>(token: typeof gameServicesToken): T;
|
|
6
|
+
}): SessionService;
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { createToken } from '@zhin.js/plugin-runtime';
|
|
2
|
+
/** Owner-scoped service consumed by discovered commands and middleware. */
|
|
3
|
+
export const gameServicesToken = createToken('zhin.game.rps.services');
|
|
4
|
+
export function resolveGameServices(context) {
|
|
5
|
+
return context.use(gameServicesToken);
|
|
6
|
+
}
|
|
7
|
+
//# sourceMappingURL=runtime-store.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"runtime-store.js","sourceRoot":"","sources":["../src/runtime-store.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,yBAAyB,CAAC;AAGtD,2EAA2E;AAC3E,MAAM,CAAC,MAAM,iBAAiB,GAAG,WAAW,CAAiB,wBAAwB,CAAC,CAAC;AAEvF,MAAM,UAAU,mBAAmB,CAAC,OAAuD;IACzF,OAAO,OAAO,CAAC,GAAG,CAAC,iBAAiB,CAAmB,CAAC;AAC1D,CAAC"}
|
package/lib/session-service.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { Database,
|
|
1
|
+
import type { Database, Message, Models } from '@zhin.js/core';
|
|
2
2
|
import type { RpsSessionRow } from './models.js';
|
|
3
3
|
export type RpsDatabase = Database<unknown, Models, string>;
|
|
4
4
|
export declare class SessionService {
|
|
@@ -13,5 +13,3 @@ export declare class SessionService {
|
|
|
13
13
|
abortStale(idleMs: number): Promise<number>;
|
|
14
14
|
}
|
|
15
15
|
export declare function createServices(db: RpsDatabase): SessionService;
|
|
16
|
-
export declare function resolveGameDatabase(feature: DatabaseFeature): RpsDatabase;
|
|
17
|
-
//# sourceMappingURL=session-service.d.ts.map
|
package/lib/session-service.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { channelKey, generateSessionId } from '@zhin.js/game-
|
|
1
|
+
import { channelKey, generateSessionId, boardMessageMatches } from '@zhin.js/game-kit';
|
|
2
2
|
function getModel(db) {
|
|
3
3
|
const model = db.models.get('rps_sessions');
|
|
4
4
|
if (!model)
|
|
@@ -26,15 +26,9 @@ export class SessionService {
|
|
|
26
26
|
async getActiveByBoardMessageId(messageId) {
|
|
27
27
|
if (!messageId)
|
|
28
28
|
return null;
|
|
29
|
-
const rows = await getModel(this.db).findAll({
|
|
29
|
+
const rows = await getModel(this.db).findAll({});
|
|
30
30
|
for (const row of rows) {
|
|
31
|
-
|
|
32
|
-
if (!stored)
|
|
33
|
-
continue;
|
|
34
|
-
if (stored === messageId || stored.endsWith(`:${messageId}`))
|
|
35
|
-
return row;
|
|
36
|
-
const tail = stored.split(':').pop();
|
|
37
|
-
if (tail && messageId.endsWith(tail))
|
|
31
|
+
if (boardMessageMatches(row.board_message_id ?? '', messageId))
|
|
38
32
|
return row;
|
|
39
33
|
}
|
|
40
34
|
return null;
|
|
@@ -81,7 +75,4 @@ export class SessionService {
|
|
|
81
75
|
export function createServices(db) {
|
|
82
76
|
return new SessionService(db);
|
|
83
77
|
}
|
|
84
|
-
export function resolveGameDatabase(feature) {
|
|
85
|
-
return feature.db;
|
|
86
|
-
}
|
|
87
78
|
//# sourceMappingURL=session-service.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"session-service.js","sourceRoot":"","sources":["../src/session-service.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,UAAU,EAAE,iBAAiB,EAAE,MAAM,
|
|
1
|
+
{"version":3,"file":"session-service.js","sourceRoot":"","sources":["../src/session-service.ts"],"names":[],"mappings":"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"}
|
package/lib/view.d.ts
CHANGED
|
@@ -1,9 +1,8 @@
|
|
|
1
|
-
import type { SendContent } from 'zhin.js';
|
|
1
|
+
import type { SendContent } from '@zhin.js/core';
|
|
2
2
|
import type { RpsSessionRow } from './models.js';
|
|
3
3
|
import { type RpsMove } from './engine.js';
|
|
4
4
|
export declare function buildRpsView(session: RpsSessionRow, lastRound?: {
|
|
5
5
|
player: RpsMove;
|
|
6
6
|
bot: RpsMove;
|
|
7
7
|
result: 0 | 1 | 2;
|
|
8
|
-
}): SendContent;
|
|
9
|
-
//# sourceMappingURL=view.d.ts.map
|
|
8
|
+
}, channelType?: string): SendContent;
|
package/lib/view.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { buildChoiceKeyboard } from '@zhin.js/game-
|
|
1
|
+
import { buildChoiceKeyboard } from '@zhin.js/game-kit';
|
|
2
2
|
import { MOVE_LABELS, RPS_PREFIX, WIN_TARGET } from './engine.js';
|
|
3
|
-
export function buildRpsView(session, lastRound) {
|
|
3
|
+
export function buildRpsView(session, lastRound, channelType) {
|
|
4
4
|
const terminal = session.status !== 'active';
|
|
5
5
|
const lines = [
|
|
6
6
|
'✊✋✌️ **猜拳对决**',
|
|
@@ -30,7 +30,7 @@ export function buildRpsView(session, lastRound) {
|
|
|
30
30
|
lines.push('', '继续出拳:');
|
|
31
31
|
}
|
|
32
32
|
const choices = terminal
|
|
33
|
-
? [{ id: 'restart', label: '🔄 再来一局', style: 'primary' }]
|
|
33
|
+
? [{ id: 'restart', label: '🔄 再来一局', style: 'primary', keepEnabledWhenTerminal: true }]
|
|
34
34
|
: ['rock', 'paper', 'scissors'].map((m) => ({
|
|
35
35
|
id: m,
|
|
36
36
|
label: MOVE_LABELS[m],
|
|
@@ -44,6 +44,8 @@ export function buildRpsView(session, lastRound) {
|
|
|
44
44
|
terminal,
|
|
45
45
|
buttonsPerRow: 3,
|
|
46
46
|
fallbackHint: '回复数字出拳(1石头 2布 3剪刀)',
|
|
47
|
+
interactionProfile: terminal ? 'terminal' : 'gameplay',
|
|
48
|
+
channelType,
|
|
47
49
|
});
|
|
48
50
|
}
|
|
49
51
|
//# sourceMappingURL=view.js.map
|
package/lib/view.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"view.js","sourceRoot":"","sources":["../src/view.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,mBAAmB,EAAE,MAAM,
|
|
1
|
+
{"version":3,"file":"view.js","sourceRoot":"","sources":["../src/view.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,mBAAmB,EAAE,MAAM,mBAAmB,CAAC;AAGxD,OAAO,EAAE,WAAW,EAAE,UAAU,EAAE,UAAU,EAAgB,MAAM,aAAa,CAAC;AAEhF,MAAM,UAAU,YAAY,CAC1B,OAAsB,EACtB,SAAgE,EAChE,WAAoB;IAEpB,MAAM,QAAQ,GAAG,OAAO,CAAC,MAAM,KAAK,QAAQ,CAAC;IAC7C,MAAM,KAAK,GAAG;QACZ,eAAe;QACf,EAAE;QACF,YAAY,OAAO,CAAC,WAAW,UAAU,OAAO,CAAC,QAAQ,YAAY,UAAU,KAAK;KACrF,CAAC;IAEF,IAAI,SAAS,EAAE,CAAC;QACd,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;QACf,KAAK,CAAC,IAAI,CAAC,MAAM,WAAW,CAAC,SAAS,CAAC,MAAM,CAAC,SAAS,WAAW,CAAC,SAAS,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;QACrF,KAAK,CAAC,IAAI,CACR,SAAS,CAAC,MAAM,KAAK,CAAC;YACpB,CAAC,CAAC,QAAQ;YACV,CAAC,CAAC,SAAS,CAAC,MAAM,KAAK,CAAC;gBACtB,CAAC,CAAC,YAAY;gBACd,CAAC,CAAC,cAAc,CACrB,CAAC;IACJ,CAAC;IAED,IAAI,QAAQ,EAAE,CAAC;QACb,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;QACf,IAAI,OAAO,CAAC,MAAM,KAAK,KAAK;YAAE,KAAK,CAAC,IAAI,CAAC,eAAe,CAAC,CAAC;aACrD,IAAI,OAAO,CAAC,MAAM,KAAK,MAAM;YAAE,KAAK,CAAC,IAAI,CAAC,iBAAiB,CAAC,CAAC;IACpE,CAAC;SAAM,IAAI,CAAC,SAAS,EAAE,CAAC;QACtB,KAAK,CAAC,IAAI,CAAC,EAAE,EAAE,MAAM,CAAC,CAAC;IACzB,CAAC;SAAM,CAAC;QACN,KAAK,CAAC,IAAI,CAAC,EAAE,EAAE,OAAO,CAAC,CAAC;IAC1B,CAAC;IAED,MAAM,OAAO,GAAG,QAAQ;QACtB,CAAC,CAAC,CAAC,EAAE,EAAE,EAAE,SAAS,EAAE,KAAK,EAAE,SAAS,EAAE,KAAK,EAAE,SAAkB,EAAE,uBAAuB,EAAE,IAAI,EAAE,CAAC;QACjG,CAAC,CAAE,CAAC,MAAM,EAAE,OAAO,EAAE,UAAU,CAAe,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;YACvD,EAAE,EAAE,CAAC;YACL,KAAK,EAAE,WAAW,CAAC,CAAC,CAAC;YACrB,KAAK,EAAE,WAAoB;SAC5B,CAAC,CAAC,CAAC;IAER,OAAO,mBAAmB,CAAC;QACzB,UAAU,EAAE,UAAU;QACtB,SAAS,EAAE,OAAO,CAAC,EAAE;QACrB,SAAS,EAAE,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC;QAC3B,OAAO;QACP,QAAQ;QACR,aAAa,EAAE,CAAC;QAChB,YAAY,EAAE,oBAAoB;QAClC,kBAAkB,EAAE,QAAQ,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC,UAAU;QACtD,WAAW;KACZ,CAAC,CAAC;AACL,CAAC"}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import {
|
|
2
|
+
defineGameCommandAliasMiddleware,
|
|
3
|
+
messageFromCommandInput,
|
|
4
|
+
normalizeRpsAction,
|
|
5
|
+
} from '@zhin.js/game-kit';
|
|
6
|
+
import { RPS_HELP, runRpsCommandText } from '../src/rps-command.js';
|
|
7
|
+
import { resolveGameServices } from '../src/runtime-store.js';
|
|
8
|
+
|
|
9
|
+
export default defineGameCommandAliasMiddleware({
|
|
10
|
+
aliases: ['猜拳', 'rps'],
|
|
11
|
+
async run(action, input, context) {
|
|
12
|
+
const normalized = normalizeRpsAction(String(action ?? ''));
|
|
13
|
+
if (!normalized || normalized === 'help') return RPS_HELP;
|
|
14
|
+
const services = resolveGameServices(context);
|
|
15
|
+
const message = messageFromCommandInput(input);
|
|
16
|
+
return runRpsCommandText(services, message, normalized);
|
|
17
|
+
},
|
|
18
|
+
});
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import { defineMiddleware } from '@zhin.js/middleware';
|
|
2
|
+
import type { Message } from '@zhin.js/core/runtime';
|
|
3
|
+
import {
|
|
4
|
+
buildChoiceFallbackMap,
|
|
5
|
+
channelKey,
|
|
6
|
+
messageFromCommandInput,
|
|
7
|
+
parseChoicePayload,
|
|
8
|
+
resolveGameTextPayload,
|
|
9
|
+
} from '@zhin.js/game-kit';
|
|
10
|
+
import { resolveGameServices } from '../src/runtime-store.js';
|
|
11
|
+
import { handleChoice, RPS_PREFIX } from '../src/game-flow.js';
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* 文本入口:按钮 payload(`rps:session:rock|paper|scissors|restart`)与数字 fallback
|
|
15
|
+
* (『1石头 2布 3剪刀』,对应 view 的 fallbackHint)。
|
|
16
|
+
*/
|
|
17
|
+
export default defineMiddleware<Message>({
|
|
18
|
+
target: 'inbound',
|
|
19
|
+
async handle(context, next) {
|
|
20
|
+
const raw = context.input.content?.trim() ?? '';
|
|
21
|
+
if (!raw) {
|
|
22
|
+
await next();
|
|
23
|
+
return;
|
|
24
|
+
}
|
|
25
|
+
const services = resolveGameServices(context);
|
|
26
|
+
const message = messageFromCommandInput(context.input);
|
|
27
|
+
const ch = channelKey(message);
|
|
28
|
+
|
|
29
|
+
// 直接 payload(QQ 指令预填 / Sandbox action→text)
|
|
30
|
+
const payloadFromText = resolveGameTextPayload(raw);
|
|
31
|
+
if (payloadFromText?.startsWith(`${RPS_PREFIX}:`)) {
|
|
32
|
+
const parsed = parseChoicePayload(payloadFromText, RPS_PREFIX);
|
|
33
|
+
if (parsed) {
|
|
34
|
+
const session = await services.getById(parsed.sessionId);
|
|
35
|
+
if (session?.channel_key === ch) {
|
|
36
|
+
const reply = await handleChoice(null, services, message, parsed.sessionId, parsed.choiceId);
|
|
37
|
+
if (reply) await context.input.$reply(reply);
|
|
38
|
+
return;
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
await next();
|
|
42
|
+
return;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
const session = await services.getActiveForUser(ch, message.$sender.id);
|
|
46
|
+
if (!session || session.status !== 'active') {
|
|
47
|
+
await next();
|
|
48
|
+
return;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
const map = buildChoiceFallbackMap(RPS_PREFIX, session.id, [
|
|
52
|
+
{ id: 'rock', label: '石头' },
|
|
53
|
+
{ id: 'paper', label: '布' },
|
|
54
|
+
{ id: 'scissors', label: '剪刀' },
|
|
55
|
+
]);
|
|
56
|
+
const payload = resolveGameTextPayload(raw, map);
|
|
57
|
+
const parsed = payload ? parseChoicePayload(payload, RPS_PREFIX) : null;
|
|
58
|
+
if (!parsed || parsed.sessionId !== session.id) {
|
|
59
|
+
await next();
|
|
60
|
+
return;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
const reply = await handleChoice(null, services, message, session.id, parsed.choiceId);
|
|
64
|
+
if (reply) await context.input.$reply(reply);
|
|
65
|
+
},
|
|
66
|
+
});
|