@zhin.js/plugin-idiom-chain 1.0.3 → 1.0.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/commands/chain/[action:string=].js +16 -0
- package/commands/chain/[action:string=].ts +2 -2
- package/lib/chain-command.d.ts +2 -4
- package/lib/chain-command.js +5 -9
- package/lib/chain-command.js.map +1 -1
- package/lib/game-flow.d.ts +6 -6
- package/lib/game-flow.js +23 -42
- package/lib/game-flow.js.map +1 -1
- package/lib/index.d.ts +0 -1
- package/lib/index.js +0 -1
- package/lib/index.js.map +1 -1
- package/lib/models.d.ts +0 -1
- package/lib/models.js +0 -1
- package/lib/models.js.map +1 -1
- package/lib/session-service.d.ts +4 -10
- package/lib/session-service.js +8 -52
- package/lib/session-service.js.map +1 -1
- package/middlewares/chain-alias.js +18 -0
- package/middlewares/chain-alias.ts +6 -3
- package/middlewares/chain-text.js +66 -0
- package/middlewares/chain-text.ts +3 -3
- package/package.json +8 -8
- package/plugin.js +37 -0
- package/src/chain-command.ts +10 -17
- package/src/game-flow.ts +29 -53
- package/src/index.ts +0 -1
- package/src/models.ts +0 -2
- package/src/session-service.ts +17 -56
- package/lib/memory-db.d.ts +0 -6
- package/lib/memory-db.js +0 -18
- package/lib/memory-db.js.map +0 -1
- package/plugin.ts +0 -64
- package/src/memory-db.ts +0 -22
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
// Generated by build-plugin-runtime-entries.mjs. Do not edit.
|
|
2
|
+
import { defineCommand } from '@zhin.js/command';
|
|
3
|
+
import { messageFromCommandInput, normalizeChainAction } from '@zhin.js/game-kit';
|
|
4
|
+
import { CHAIN_HELP, runChainCommand } from "../../lib/chain-command.js";
|
|
5
|
+
import { resolveGameServices } from "../../lib/runtime-store.js";
|
|
6
|
+
export default defineCommand({
|
|
7
|
+
description: 'Idiom Chain',
|
|
8
|
+
async execute({ params, input, use, owner }) {
|
|
9
|
+
const action = normalizeChainAction(String(params.action ?? ''));
|
|
10
|
+
if (!action || action === 'help')
|
|
11
|
+
return CHAIN_HELP;
|
|
12
|
+
const services = resolveGameServices({ use, owner });
|
|
13
|
+
const message = messageFromCommandInput(input);
|
|
14
|
+
return runChainCommand(services, message, action);
|
|
15
|
+
},
|
|
16
|
+
});
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { defineCommand } from '@zhin.js/command';
|
|
2
2
|
import { messageFromCommandInput, normalizeChainAction } from '@zhin.js/game-kit';
|
|
3
|
-
import { CHAIN_HELP,
|
|
3
|
+
import { CHAIN_HELP, runChainCommand } from '../../src/chain-command.js';
|
|
4
4
|
import { resolveGameServices } from '../../src/runtime-store.js';
|
|
5
5
|
|
|
6
6
|
export default defineCommand({
|
|
@@ -10,6 +10,6 @@ export default defineCommand({
|
|
|
10
10
|
if (!action || action === 'help') return CHAIN_HELP;
|
|
11
11
|
const services = resolveGameServices({ use, owner });
|
|
12
12
|
const message = messageFromCommandInput(input);
|
|
13
|
-
return
|
|
13
|
+
return runChainCommand(services, message, action);
|
|
14
14
|
},
|
|
15
15
|
});
|
package/lib/chain-command.d.ts
CHANGED
|
@@ -1,6 +1,4 @@
|
|
|
1
|
-
import type
|
|
1
|
+
import { type GameMessageLike, type GameReply } from '@zhin.js/game-kit';
|
|
2
2
|
import type { SessionService } from './session-service.js';
|
|
3
3
|
export declare const CHAIN_HELP: string;
|
|
4
|
-
export declare function runChainCommand(
|
|
5
|
-
/** Plugin Runtime / smoke: text-only, no Adapter.editMessage. */
|
|
6
|
-
export declare function runChainCommandText(services: SessionService, message: Message<any>, action: string): Promise<string>;
|
|
4
|
+
export declare function runChainCommand(services: SessionService, message: GameMessageLike, action: string): Promise<GameReply>;
|
package/lib/chain-command.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { channelKey } from '@zhin.js/game-kit';
|
|
1
|
+
import { channelKey, } from '@zhin.js/game-kit';
|
|
2
2
|
import { continueGame, startGame } from './game-flow.js';
|
|
3
3
|
import { idiomCount, modeLabel, promptLine } from './engine.js';
|
|
4
4
|
export const CHAIN_HELP = [
|
|
@@ -13,7 +13,7 @@ export const CHAIN_HELP = [
|
|
|
13
13
|
'进行中:直接回复四字成语',
|
|
14
14
|
'按钮:提示 · 跳过 · 认输(失误 3 次判负)',
|
|
15
15
|
].join('\n');
|
|
16
|
-
export async function runChainCommand(
|
|
16
|
+
export async function runChainCommand(services, message, action) {
|
|
17
17
|
const ch = channelKey(message);
|
|
18
18
|
const userId = message.$sender.id;
|
|
19
19
|
if (!action || action === 'help') {
|
|
@@ -31,11 +31,11 @@ export async function runChainCommand(plugin, services, message, action) {
|
|
|
31
31
|
return lines.join('\n');
|
|
32
32
|
}
|
|
33
33
|
if (action === 'start_pinyin')
|
|
34
|
-
return startGame(
|
|
34
|
+
return (await startGame(services, message, 'pinyin')) ?? '';
|
|
35
35
|
if (action === 'start_char')
|
|
36
|
-
return startGame(
|
|
36
|
+
return (await startGame(services, message, 'char')) ?? '';
|
|
37
37
|
if (action === 'continue')
|
|
38
|
-
return continueGame(
|
|
38
|
+
return continueGame(services, message);
|
|
39
39
|
if (action === 'quit') {
|
|
40
40
|
const row = await services.getActiveForUser(ch, userId);
|
|
41
41
|
if (!row)
|
|
@@ -45,8 +45,4 @@ export async function runChainCommand(plugin, services, message, action) {
|
|
|
45
45
|
}
|
|
46
46
|
return `未知子命令:${action}\n\n${CHAIN_HELP}`;
|
|
47
47
|
}
|
|
48
|
-
/** Plugin Runtime / smoke: text-only, no Adapter.editMessage. */
|
|
49
|
-
export async function runChainCommandText(services, message, action) {
|
|
50
|
-
return (await runChainCommand(null, services, message, action)) ?? '';
|
|
51
|
-
}
|
|
52
48
|
//# sourceMappingURL=chain-command.js.map
|
package/lib/chain-command.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"chain-command.js","sourceRoot":"","sources":["../src/chain-command.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"chain-command.js","sourceRoot":"","sources":["../src/chain-command.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,UAAU,GAGX,MAAM,mBAAmB,CAAC;AAC3B,OAAO,EAAE,YAAY,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAC;AACzD,OAAO,EAAE,UAAU,EAAE,SAAS,EAAE,UAAU,EAAkB,MAAM,aAAa,CAAC;AAIhF,MAAM,CAAC,MAAM,UAAU,GAAG;IACxB,sBAAsB;IACtB,QAAQ,UAAU,EAAE,eAAe;IACnC,iBAAiB;IACjB,8BAA8B;IAC9B,gBAAgB;IAChB,cAAc;IACd,cAAc;IACd,EAAE;IACF,cAAc;IACd,2BAA2B;CAC5B,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AAEb,MAAM,CAAC,KAAK,UAAU,eAAe,CACnC,QAAwB,EACxB,OAAwB,EACxB,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,UAAU,EAAE,EAAE,CAAC,CAAC;QAC/B,IAAI,MAAM,EAAE,CAAC;YACX,MAAM,IAAI,GAAc,MAAM,CAAC,UAAU,KAAK,MAAM,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,QAAQ,CAAC;YACzE,KAAK,CAAC,IAAI,CACR,OAAO,MAAM,CAAC,WAAW,MAAM,SAAS,CAAC,IAAI,CAAC,SAAS,MAAM,CAAC,YAAY,IAAI,MAAM,CAAC,SAAS,SAAS,MAAM,CAAC,MAAM,EAAE,CACvH,CAAC;YACF,IAAI,MAAM,CAAC,UAAU;gBAAE,KAAK,CAAC,IAAI,CAAC,UAAU,CAAC,MAAM,CAAC,UAAU,EAAE,IAAI,CAAC,CAAC,CAAC;QACzE,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,cAAc;QAAE,OAAO,CAAC,MAAM,SAAS,CAAC,QAAQ,EAAE,OAAO,EAAE,QAAQ,CAAC,CAAC,IAAI,EAAE,CAAC;IAC3F,IAAI,MAAM,KAAK,YAAY;QAAE,OAAO,CAAC,MAAM,SAAS,CAAC,QAAQ,EAAE,OAAO,EAAE,MAAM,CAAC,CAAC,IAAI,EAAE,CAAC;IACvF,IAAI,MAAM,KAAK,UAAU;QAAE,OAAO,YAAY,CAAC,QAAQ,EAAE,OAAO,CAAC,CAAC;IAElE,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,UAAU,EAAE,CAAC;AAC5C,CAAC"}
|
package/lib/game-flow.d.ts
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { GameMessageLike, GameReply } from '@zhin.js/game-kit';
|
|
2
2
|
import { CHAIN_PREFIX, type MatchMode } from './engine.js';
|
|
3
3
|
import type { ChainSessionRow } from './models.js';
|
|
4
4
|
import { type SessionService } from './session-service.js';
|
|
5
|
-
export declare function sendOrEditView(
|
|
6
|
-
export declare function startGame(
|
|
7
|
-
export declare function continueGame(
|
|
8
|
-
export declare function processIdiomText(
|
|
9
|
-
export declare function handleChoice(
|
|
5
|
+
export declare function sendOrEditView(services: SessionService, message: GameMessageLike, session: ChainSessionRow, eventLines?: string[]): Promise<GameReply>;
|
|
6
|
+
export declare function startGame(services: SessionService, message: GameMessageLike, matchMode?: MatchMode): Promise<GameReply | undefined>;
|
|
7
|
+
export declare function continueGame(services: SessionService, message: GameMessageLike): Promise<GameReply>;
|
|
8
|
+
export declare function processIdiomText(services: SessionService, message: GameMessageLike, raw: string): Promise<GameReply | null>;
|
|
9
|
+
export declare function handleChoice(services: SessionService, message: GameMessageLike, sessionId: string, choiceId: string): Promise<GameReply | null>;
|
|
10
10
|
export { CHAIN_PREFIX };
|
package/lib/game-flow.js
CHANGED
|
@@ -1,32 +1,12 @@
|
|
|
1
|
-
import { plainTextFromSendContent } from '@zhin.js/game-kit';
|
|
2
1
|
import { CHAIN_PREFIX, getGloss, lastChar, modeLabel, normalizeInput, pickBotIdiom, pickHintIdiom, pickStarterIdiom, promptLine, validatePlayerIdiom, } from './engine.js';
|
|
3
2
|
import { parseUsed } from './session-service.js';
|
|
4
3
|
import { buildChainView, MAX_WRONG } from './view.js';
|
|
5
4
|
function sessionMode(session) {
|
|
6
5
|
return session.match_mode === 'char' ? 'char' : 'pinyin';
|
|
7
6
|
}
|
|
8
|
-
export async function sendOrEditView(
|
|
7
|
+
export async function sendOrEditView(services, message, session, eventLines = []) {
|
|
9
8
|
const content = buildChainView(session, eventLines, message.$channel.type);
|
|
10
|
-
|
|
11
|
-
return plainTextFromSendContent(content);
|
|
12
|
-
const adapter = plugin.root.inject(message.$adapter);
|
|
13
|
-
if (session.board_message_id) {
|
|
14
|
-
const msgId = await adapter.editMessage({
|
|
15
|
-
messageId: session.board_message_id,
|
|
16
|
-
context: String(message.$adapter),
|
|
17
|
-
endpoint: message.$endpoint,
|
|
18
|
-
id: message.$channel.id,
|
|
19
|
-
type: message.$channel.type,
|
|
20
|
-
content,
|
|
21
|
-
});
|
|
22
|
-
if (msgId !== session.board_message_id) {
|
|
23
|
-
await services.updateSession(session.id, { board_message_id: msgId });
|
|
24
|
-
}
|
|
25
|
-
return;
|
|
26
|
-
}
|
|
27
|
-
const msgId = await message.$reply?.(content);
|
|
28
|
-
if (msgId)
|
|
29
|
-
await services.updateSession(session.id, { board_message_id: msgId });
|
|
9
|
+
return content;
|
|
30
10
|
}
|
|
31
11
|
async function botTurn(services, session, used) {
|
|
32
12
|
const mode = sessionMode(session);
|
|
@@ -55,7 +35,7 @@ async function botTurn(services, session, used) {
|
|
|
55
35
|
];
|
|
56
36
|
return { session: updated, lines, playerWon: false };
|
|
57
37
|
}
|
|
58
|
-
export async function startGame(
|
|
38
|
+
export async function startGame(services, message, matchMode = 'pinyin') {
|
|
59
39
|
const ch = `${message.$adapter}-${message.$endpoint}-${message.$channel.type}:${message.$channel.id}`;
|
|
60
40
|
const active = await services.getActiveByChannel(ch);
|
|
61
41
|
if (active) {
|
|
@@ -74,22 +54,18 @@ export async function startGame(plugin, services, message, matchMode = 'pinyin')
|
|
|
74
54
|
matchMode,
|
|
75
55
|
});
|
|
76
56
|
const gloss = starter.gloss ?? getGloss(starter.text);
|
|
77
|
-
|
|
57
|
+
return sendOrEditView(services, message, session, [
|
|
78
58
|
`🎬 ${modeLabel(matchMode)}开局!我先出:**${starter.text}**${gloss ? `(${gloss})` : ''}`,
|
|
79
59
|
promptLine(starter.text, matchMode),
|
|
80
60
|
]);
|
|
81
|
-
return typeof text === 'string' ? text : undefined;
|
|
82
61
|
}
|
|
83
|
-
export async function continueGame(
|
|
62
|
+
export async function continueGame(services, message) {
|
|
84
63
|
const session = await services.getActiveForUser(`${message.$adapter}-${message.$endpoint}-${message.$channel.type}:${message.$channel.id}`, message.$sender.id);
|
|
85
64
|
if (!session)
|
|
86
65
|
return '你没有进行中的接龙,发送「接龙 开始」。';
|
|
87
|
-
|
|
88
|
-
if (typeof text === 'string')
|
|
89
|
-
return text;
|
|
90
|
-
return '已刷新接龙界面。';
|
|
66
|
+
return sendOrEditView(services, message, session);
|
|
91
67
|
}
|
|
92
|
-
export async function processIdiomText(
|
|
68
|
+
export async function processIdiomText(services, message, raw) {
|
|
93
69
|
const ch = `${message.$adapter}-${message.$endpoint}-${message.$channel.type}:${message.$channel.id}`;
|
|
94
70
|
const session = await services.getActiveForUser(ch, message.$sender.id);
|
|
95
71
|
if (!session || session.status !== 'active')
|
|
@@ -97,6 +73,10 @@ export async function processIdiomText(plugin, services, message, raw) {
|
|
|
97
73
|
const mode = sessionMode(session);
|
|
98
74
|
const used = parseUsed(session.used_idioms);
|
|
99
75
|
const idiom = normalizeInput(raw);
|
|
76
|
+
// 格式不合规(非四字)只提示、不扣失误:避免闲聊(「好的」「谢谢」)被累计判负
|
|
77
|
+
if (!/^[\u4e00-\u9fff]{4}$/.test(idiom)) {
|
|
78
|
+
return '接龙请输入**四字成语**(这条不算失误)。';
|
|
79
|
+
}
|
|
100
80
|
const check = validatePlayerIdiom(idiom, session.last_idiom, mode, used);
|
|
101
81
|
if (!check.ok) {
|
|
102
82
|
const wrong = session.wrong_count + 1;
|
|
@@ -108,14 +88,14 @@ export async function processIdiomText(plugin, services, message, raw) {
|
|
|
108
88
|
streak: 0,
|
|
109
89
|
});
|
|
110
90
|
const updated = (await services.getById(session.id));
|
|
111
|
-
return (await sendOrEditView(
|
|
91
|
+
return (await sendOrEditView(services, message, updated, [
|
|
112
92
|
check.reason,
|
|
113
93
|
'失误过多,本局结束。',
|
|
114
94
|
])) ?? null;
|
|
115
95
|
}
|
|
116
96
|
await services.updateSession(session.id, { wrong_count: wrong, streak: 0 });
|
|
117
97
|
const updated = (await services.getById(session.id));
|
|
118
|
-
return (await sendOrEditView(
|
|
98
|
+
return (await sendOrEditView(services, message, updated, [check.reason])) ?? null;
|
|
119
99
|
}
|
|
120
100
|
used.add(idiom);
|
|
121
101
|
const streak = session.streak + 1;
|
|
@@ -133,12 +113,12 @@ export async function processIdiomText(plugin, services, message, raw) {
|
|
|
133
113
|
const current = (await services.getById(session.id));
|
|
134
114
|
const userLine = `✅ 你:${idiom}${gloss ? `(${gloss})` : ''}`;
|
|
135
115
|
const botResult = await botTurn(services, current, used);
|
|
136
|
-
return (await sendOrEditView(
|
|
116
|
+
return (await sendOrEditView(services, message, botResult.session, [
|
|
137
117
|
userLine,
|
|
138
118
|
...botResult.lines,
|
|
139
119
|
])) ?? null;
|
|
140
120
|
}
|
|
141
|
-
export async function handleChoice(
|
|
121
|
+
export async function handleChoice(services, message, sessionId, choiceId) {
|
|
142
122
|
const session = await services.getById(sessionId);
|
|
143
123
|
if (!session)
|
|
144
124
|
return '对局不存在。';
|
|
@@ -157,11 +137,10 @@ export async function handleChoice(plugin, services, message, sessionId, choiceI
|
|
|
157
137
|
wrong_count: 0,
|
|
158
138
|
turn: 'player',
|
|
159
139
|
status: 'active',
|
|
160
|
-
board_message_id: '',
|
|
161
140
|
});
|
|
162
141
|
const updated = (await services.getById(session.id));
|
|
163
142
|
const gloss = starter.gloss ?? getGloss(starter.text);
|
|
164
|
-
return (await sendOrEditView(
|
|
143
|
+
return (await sendOrEditView(services, message, updated, [
|
|
165
144
|
`🎬 新一局!我先出:**${starter.text}**${gloss ? `(${gloss})` : ''}`,
|
|
166
145
|
promptLine(starter.text, mode),
|
|
167
146
|
])) ?? null;
|
|
@@ -173,15 +152,17 @@ export async function handleChoice(plugin, services, message, sessionId, choiceI
|
|
|
173
152
|
if (choiceId === 'hint') {
|
|
174
153
|
const hint = pickHintIdiom(session.last_idiom, mode, used);
|
|
175
154
|
if (!hint) {
|
|
176
|
-
|
|
155
|
+
// 先更新状态再单次发送终局视图,避免旧 session「你赢了」与终局视图双发
|
|
177
156
|
await services.updateSession(session.id, { status: 'won', player_score: session.player_score + 1 });
|
|
178
157
|
const updated = (await services.getById(session.id));
|
|
179
|
-
return (await sendOrEditView(
|
|
158
|
+
return (await sendOrEditView(services, message, updated, [
|
|
159
|
+
'💡 词库中暂无可用提示,你赢了!',
|
|
160
|
+
])) ?? null;
|
|
180
161
|
}
|
|
181
162
|
const gloss = getGloss(hint);
|
|
182
163
|
await services.updateSession(session.id, { hints_used: session.hints_used + 1, streak: 0 });
|
|
183
164
|
const updated = (await services.getById(session.id));
|
|
184
|
-
return (await sendOrEditView(
|
|
165
|
+
return (await sendOrEditView(services, message, updated, [
|
|
185
166
|
`💡 提示:可试 **${hint}**${gloss ? `(${gloss})` : ''}`,
|
|
186
167
|
'(使用提示会清零连击)',
|
|
187
168
|
])) ?? null;
|
|
@@ -193,14 +174,14 @@ export async function handleChoice(plugin, services, message, sessionId, choiceI
|
|
|
193
174
|
streak: 0,
|
|
194
175
|
});
|
|
195
176
|
const updated = (await services.getById(session.id));
|
|
196
|
-
return (await sendOrEditView(
|
|
177
|
+
return (await sendOrEditView(services, message, updated, [
|
|
197
178
|
'⏭️ 你选择跳过,机器人得一分。',
|
|
198
179
|
])) ?? null;
|
|
199
180
|
}
|
|
200
181
|
if (choiceId === 'quit') {
|
|
201
182
|
await services.updateSession(session.id, { status: 'lost', bot_score: session.bot_score + 1 });
|
|
202
183
|
const updated = (await services.getById(session.id));
|
|
203
|
-
return (await sendOrEditView(
|
|
184
|
+
return (await sendOrEditView(services, message, updated, ['🏳️ 你认输了。'])) ?? null;
|
|
204
185
|
}
|
|
205
186
|
return '未知操作。';
|
|
206
187
|
}
|
package/lib/game-flow.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"game-flow.js","sourceRoot":"","sources":["../src/game-flow.ts"],"names":[],"mappings":"AACA,OAAO,
|
|
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,6 +1,5 @@
|
|
|
1
1
|
export { CHAIN_HELP } from './chain-command.js';
|
|
2
2
|
export { gameServicesToken, resolveGameServices } from './runtime-store.js';
|
|
3
|
-
export { createInMemoryChainDb, mountChainMemoryServices } from './memory-db.js';
|
|
4
3
|
/**
|
|
5
4
|
* 已由 plugin.ts 接线:commands/ 命令、middlewares/ 文本与选项中间件、
|
|
6
5
|
* 游戏大厅注册(registerRuntimeGame)、过期会话 cron(scheduleHostToken)、
|
package/lib/index.js
CHANGED
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
export { CHAIN_HELP } from './chain-command.js';
|
|
2
2
|
export { gameServicesToken, resolveGameServices } from './runtime-store.js';
|
|
3
|
-
export { createInMemoryChainDb, mountChainMemoryServices } from './memory-db.js';
|
|
4
3
|
/**
|
|
5
4
|
* 已由 plugin.ts 接线:commands/ 命令、middlewares/ 文本与选项中间件、
|
|
6
5
|
* 游戏大厅注册(registerRuntimeGame)、过期会话 cron(scheduleHostToken)、
|
package/lib/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,oBAAoB,CAAC;AAChD,OAAO,EAAE,iBAAiB,EAAE,mBAAmB,EAAE,MAAM,oBAAoB,CAAC;
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,oBAAoB,CAAC;AAChD,OAAO,EAAE,iBAAiB,EAAE,mBAAmB,EAAE,MAAM,oBAAoB,CAAC;AAE5E;;;;GAIG"}
|
package/lib/models.d.ts
CHANGED
package/lib/models.js
CHANGED
|
@@ -20,7 +20,6 @@ export function defineHostTables(db) {
|
|
|
20
20
|
hints_used: { type: 'integer', default: 0 },
|
|
21
21
|
turn: { type: 'text', default: 'player' },
|
|
22
22
|
status: { type: 'text', default: 'active' },
|
|
23
|
-
board_message_id: { type: 'text', default: '' },
|
|
24
23
|
updated_at: { type: 'integer', default: 0 },
|
|
25
24
|
created_at: { type: 'integer', default: 0 },
|
|
26
25
|
});
|
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":"AAqCA,MAAM,UAAU,gBAAgB,CAC9B,EAA2E;IAE3E,EAAE,CAAC,MAAM,CAAC,sBAAsB,EAAE;QAChC,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,UAAU,EAAE,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,EAAE,EAAE;QACzC,SAAS,EAAE,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,EAAE,EAAE;QACxC,UAAU,EAAE,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,QAAQ,EAAE;QAC/C,WAAW,EAAE,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,IAAI,EAAE;QAC5C,YAAY,EAAE,EAAE,IAAI,EAAE,SAAS,EAAE,OAAO,EAAE,CAAC,EAAE;QAC7C,SAAS,EAAE,EAAE,IAAI,EAAE,SAAS,EAAE,OAAO,EAAE,CAAC,EAAE;QAC1C,MAAM,EAAE,EAAE,IAAI,EAAE,SAAS,EAAE,OAAO,EAAE,CAAC,EAAE;QACvC,WAAW,EAAE,EAAE,IAAI,EAAE,SAAS,EAAE,OAAO,EAAE,CAAC,EAAE;QAC5C,WAAW,EAAE,EAAE,IAAI,EAAE,SAAS,EAAE,OAAO,EAAE,CAAC,EAAE;QAC5C,UAAU,EAAE,EAAE,IAAI,EAAE,SAAS,EAAE,OAAO,EAAE,CAAC,EAAE;QAC3C,IAAI,EAAE,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,QAAQ,EAAE;QACzC,MAAM,EAAE,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,QAAQ,EAAE;QAC3C,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/session-service.d.ts
CHANGED
|
@@ -1,23 +1,17 @@
|
|
|
1
|
-
import type { Database,
|
|
1
|
+
import type { Database, Models } from '@zhin.js/core';
|
|
2
|
+
import { BaseSessionService, type GameMessageLike } from '@zhin.js/game-kit';
|
|
2
3
|
import type { MatchMode } from './engine.js';
|
|
3
4
|
import type { ChainSessionRow } from './models.js';
|
|
4
5
|
export type ChainDatabase = Database<unknown, Models, string>;
|
|
5
6
|
export declare function parseUsed(value: string | string[] | unknown): Set<string>;
|
|
6
7
|
export declare function serializeUsed(set: Set<string>): string;
|
|
7
|
-
export declare class SessionService {
|
|
8
|
-
private readonly db;
|
|
8
|
+
export declare class SessionService extends BaseSessionService<ChainSessionRow> {
|
|
9
9
|
constructor(db: ChainDatabase);
|
|
10
|
-
|
|
11
|
-
getActiveForUser(channel: string, userId: string): Promise<ChainSessionRow | null>;
|
|
12
|
-
getById(id: string): Promise<ChainSessionRow | null>;
|
|
13
|
-
getActiveByBoardMessageId(messageId: string): Promise<ChainSessionRow | null>;
|
|
14
|
-
createSession(message: Message<any>, starter: {
|
|
10
|
+
createSession(message: GameMessageLike, starter: {
|
|
15
11
|
text: string;
|
|
16
12
|
nextChar: string;
|
|
17
13
|
used: string[];
|
|
18
14
|
matchMode: MatchMode;
|
|
19
15
|
}): Promise<ChainSessionRow>;
|
|
20
|
-
updateSession(id: string, patch: Partial<ChainSessionRow>): Promise<void>;
|
|
21
|
-
abortStale(idleMs: number): Promise<number>;
|
|
22
16
|
}
|
|
23
17
|
export declare function createServices(db: ChainDatabase): SessionService;
|
package/lib/session-service.js
CHANGED
|
@@ -1,10 +1,4 @@
|
|
|
1
|
-
import { channelKey, generateSessionId,
|
|
2
|
-
function getModel(db) {
|
|
3
|
-
const model = db.models.get('idiom_chain_sessions');
|
|
4
|
-
if (!model)
|
|
5
|
-
throw new Error('idiom_chain_sessions not registered');
|
|
6
|
-
return model;
|
|
7
|
-
}
|
|
1
|
+
import { BaseSessionService, channelKey, generateSessionId, } from '@zhin.js/game-kit';
|
|
8
2
|
export function parseUsed(value) {
|
|
9
3
|
if (Array.isArray(value))
|
|
10
4
|
return new Set(value.filter((x) => typeof x === 'string'));
|
|
@@ -21,33 +15,13 @@ export function parseUsed(value) {
|
|
|
21
15
|
export function serializeUsed(set) {
|
|
22
16
|
return JSON.stringify([...set]);
|
|
23
17
|
}
|
|
24
|
-
export class SessionService {
|
|
25
|
-
db;
|
|
18
|
+
export class SessionService extends BaseSessionService {
|
|
26
19
|
constructor(db) {
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
}
|
|
33
|
-
async getActiveForUser(channel, userId) {
|
|
34
|
-
const row = await this.getActiveByChannel(channel);
|
|
35
|
-
if (!row || row.player_id !== userId)
|
|
36
|
-
return null;
|
|
37
|
-
return row;
|
|
38
|
-
}
|
|
39
|
-
async getById(id) {
|
|
40
|
-
return getModel(this.db).findOne({ id });
|
|
41
|
-
}
|
|
42
|
-
async getActiveByBoardMessageId(messageId) {
|
|
43
|
-
if (!messageId)
|
|
44
|
-
return null;
|
|
45
|
-
const rows = await getModel(this.db).findAll({});
|
|
46
|
-
for (const row of rows) {
|
|
47
|
-
if (boardMessageMatches(row.board_message_id ?? '', messageId))
|
|
48
|
-
return row;
|
|
49
|
-
}
|
|
50
|
-
return null;
|
|
20
|
+
super(db, {
|
|
21
|
+
gameId: 'chain',
|
|
22
|
+
table: 'idiom_chain_sessions',
|
|
23
|
+
userFields: ['player_id'],
|
|
24
|
+
});
|
|
51
25
|
}
|
|
52
26
|
async createSession(message, starter) {
|
|
53
27
|
const now = Date.now();
|
|
@@ -72,28 +46,10 @@ export class SessionService {
|
|
|
72
46
|
hints_used: 0,
|
|
73
47
|
turn: 'player',
|
|
74
48
|
status: 'active',
|
|
75
|
-
board_message_id: '',
|
|
76
49
|
updated_at: now,
|
|
77
50
|
created_at: now,
|
|
78
51
|
};
|
|
79
|
-
|
|
80
|
-
return row;
|
|
81
|
-
}
|
|
82
|
-
async updateSession(id, patch) {
|
|
83
|
-
await getModel(this.db).updateWhere({ id }, { ...patch, updated_at: Date.now() });
|
|
84
|
-
}
|
|
85
|
-
async abortStale(idleMs) {
|
|
86
|
-
const cutoff = Date.now() - idleMs;
|
|
87
|
-
const model = getModel(this.db);
|
|
88
|
-
const rows = await model.findAll({ status: 'active' });
|
|
89
|
-
let n = 0;
|
|
90
|
-
for (const row of rows) {
|
|
91
|
-
if (row.updated_at < cutoff) {
|
|
92
|
-
await model.updateWhere({ id: row.id }, { status: 'aborted', updated_at: Date.now() });
|
|
93
|
-
n++;
|
|
94
|
-
}
|
|
95
|
-
}
|
|
96
|
-
return n;
|
|
52
|
+
return this.createRow(row);
|
|
97
53
|
}
|
|
98
54
|
}
|
|
99
55
|
export function createServices(db) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"session-service.js","sourceRoot":"","sources":["../src/session-service.ts"],"names":[],"mappings":"AACA,OAAO,
|
|
1
|
+
{"version":3,"file":"session-service.js","sourceRoot":"","sources":["../src/session-service.ts"],"names":[],"mappings":"AACA,OAAO,EACL,kBAAkB,EAClB,UAAU,EACV,iBAAiB,GAGlB,MAAM,mBAAmB,CAAC;AAM3B,MAAM,UAAU,SAAS,CAAC,KAAkC;IAC1D,IAAI,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC;QAAE,OAAO,IAAI,GAAG,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,EAAe,EAAE,CAAC,OAAO,CAAC,KAAK,QAAQ,CAAC,CAAC,CAAC;IAClG,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,CAAC,KAAK;QAAE,OAAO,IAAI,GAAG,EAAE,CAAC;IAC1D,IAAI,CAAC;QACH,MAAM,CAAC,GAAG,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC;QAC5B,OAAO,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,GAAG,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,EAAe,EAAE,CAAC,OAAO,CAAC,KAAK,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,GAAG,EAAE,CAAC;IACrG,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,IAAI,GAAG,EAAE,CAAC;IACnB,CAAC;AACH,CAAC;AAED,MAAM,UAAU,aAAa,CAAC,GAAgB;IAC5C,OAAO,IAAI,CAAC,SAAS,CAAC,CAAC,GAAG,GAAG,CAAC,CAAC,CAAC;AAClC,CAAC;AAED,MAAM,OAAO,cAAe,SAAQ,kBAAmC;IACrE,YAAY,EAAiB;QAC3B,KAAK,CAAC,EAAqD,EAAE;YAC3D,MAAM,EAAE,OAAO;YACf,KAAK,EAAE,sBAAsB;YAC7B,UAAU,EAAE,CAAC,WAAW,CAAC;SAC1B,CAAC,CAAC;IACL,CAAC;IAED,KAAK,CAAC,aAAa,CACjB,OAAwB,EACxB,OAAiF;QAEjF,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;QACvB,MAAM,GAAG,GAAoB;YAC3B,EAAE,EAAE,iBAAiB,EAAE;YACvB,OAAO,EAAE,MAAM,CAAC,OAAO,CAAC,QAAQ,CAAC;YACjC,QAAQ,EAAE,OAAO,CAAC,SAAS;YAC3B,YAAY,EAAE,OAAO,CAAC,QAAQ,CAAC,IAAI;YACnC,UAAU,EAAE,OAAO,CAAC,QAAQ,CAAC,EAAE;YAC/B,WAAW,EAAE,UAAU,CAAC,OAAO,CAAC;YAChC,SAAS,EAAE,OAAO,CAAC,OAAO,CAAC,EAAE;YAC7B,WAAW,EAAE,OAAO,CAAC,OAAO,CAAC,IAAI,EAAE,IAAI,EAAE,IAAI,OAAO,CAAC,OAAO,CAAC,EAAE;YAC/D,UAAU,EAAE,OAAO,CAAC,IAAI;YACxB,SAAS,EAAE,OAAO,CAAC,QAAQ;YAC3B,UAAU,EAAE,OAAO,CAAC,SAAS;YAC7B,WAAW,EAAE,aAAa,CAAC,IAAI,GAAG,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC;YACjD,YAAY,EAAE,CAAC;YACf,SAAS,EAAE,CAAC;YACZ,MAAM,EAAE,CAAC;YACT,WAAW,EAAE,CAAC;YACd,WAAW,EAAE,CAAC;YACd,UAAU,EAAE,CAAC;YACb,IAAI,EAAE,QAAQ;YACd,MAAM,EAAE,QAAQ;YAChB,UAAU,EAAE,GAAG;YACf,UAAU,EAAE,GAAG;SAChB,CAAC;QACF,OAAO,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,CAAC;IAC7B,CAAC;CACF;AAED,MAAM,UAAU,cAAc,CAAC,EAAiB;IAC9C,OAAO,IAAI,cAAc,CAAC,EAAE,CAAC,CAAC;AAChC,CAAC"}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
// Generated by build-plugin-runtime-entries.mjs. Do not edit.
|
|
2
|
+
import { defineGameCommandAliasMiddleware, isChainAction, messageFromCommandInput, normalizeChainAction, } from '@zhin.js/game-kit';
|
|
3
|
+
import { CHAIN_HELP, runChainCommand } from "../lib/chain-command.js";
|
|
4
|
+
import { resolveGameServices } from "../lib/runtime-store.js";
|
|
5
|
+
export default defineGameCommandAliasMiddleware({
|
|
6
|
+
aliases: ['接龙', 'chain'],
|
|
7
|
+
async run(action, input, context) {
|
|
8
|
+
const normalized = normalizeChainAction(String(action ?? ''));
|
|
9
|
+
if (normalized === 'help')
|
|
10
|
+
return CHAIN_HELP;
|
|
11
|
+
// action 无法识别:放行给后续中间件,避免劫持普通聊天
|
|
12
|
+
if (!isChainAction(normalized))
|
|
13
|
+
return null;
|
|
14
|
+
const services = resolveGameServices(context);
|
|
15
|
+
const message = messageFromCommandInput(input);
|
|
16
|
+
return runChainCommand(services, message, normalized);
|
|
17
|
+
},
|
|
18
|
+
});
|
|
@@ -1,18 +1,21 @@
|
|
|
1
1
|
import {
|
|
2
2
|
defineGameCommandAliasMiddleware,
|
|
3
|
+
isChainAction,
|
|
3
4
|
messageFromCommandInput,
|
|
4
5
|
normalizeChainAction,
|
|
5
6
|
} from '@zhin.js/game-kit';
|
|
6
|
-
import { CHAIN_HELP,
|
|
7
|
+
import { CHAIN_HELP, runChainCommand } from '../src/chain-command.js';
|
|
7
8
|
import { resolveGameServices } from '../src/runtime-store.js';
|
|
8
9
|
|
|
9
10
|
export default defineGameCommandAliasMiddleware({
|
|
10
11
|
aliases: ['接龙', 'chain'],
|
|
11
12
|
async run(action, input, context) {
|
|
12
13
|
const normalized = normalizeChainAction(String(action ?? ''));
|
|
13
|
-
if (
|
|
14
|
+
if (normalized === 'help') return CHAIN_HELP;
|
|
15
|
+
// action 无法识别:放行给后续中间件,避免劫持普通聊天
|
|
16
|
+
if (!isChainAction(normalized)) return null;
|
|
14
17
|
const services = resolveGameServices(context);
|
|
15
18
|
const message = messageFromCommandInput(input);
|
|
16
|
-
return
|
|
19
|
+
return runChainCommand(services, message, normalized);
|
|
17
20
|
},
|
|
18
21
|
});
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
// Generated by build-plugin-runtime-entries.mjs. Do not edit.
|
|
2
|
+
import { defineMiddleware } from '@zhin.js/middleware';
|
|
3
|
+
import { buildChoiceFallbackMap, channelKey, messageFromCommandInput, parseChoicePayload, resolveGameTextPayload, } from '@zhin.js/game-kit';
|
|
4
|
+
import { resolveGameServices } from "../lib/runtime-store.js";
|
|
5
|
+
import { CHAIN_PREFIX, handleChoice, processIdiomText } from "../lib/game-flow.js";
|
|
6
|
+
/**
|
|
7
|
+
* 文本入口:按钮 payload(`chain:session:choiceId`)、数字 fallback
|
|
8
|
+
* (『1提示 2跳过 3认输』,对应 view 的 fallbackHint),其余按成语接龙作答。
|
|
9
|
+
*/
|
|
10
|
+
export default defineMiddleware({
|
|
11
|
+
target: 'inbound',
|
|
12
|
+
async handle(context, next) {
|
|
13
|
+
const raw = context.input.content?.trim() ?? '';
|
|
14
|
+
if (!raw) {
|
|
15
|
+
await next();
|
|
16
|
+
return;
|
|
17
|
+
}
|
|
18
|
+
const services = resolveGameServices(context);
|
|
19
|
+
const message = messageFromCommandInput(context.input);
|
|
20
|
+
const ch = channelKey(message);
|
|
21
|
+
// 直接 payload(QQ 指令预填 / Sandbox action→text)
|
|
22
|
+
const payloadFromText = resolveGameTextPayload(raw);
|
|
23
|
+
if (payloadFromText?.startsWith(`${CHAIN_PREFIX}:`)) {
|
|
24
|
+
const parsed = parseChoicePayload(payloadFromText, CHAIN_PREFIX);
|
|
25
|
+
if (parsed) {
|
|
26
|
+
const session = await services.getById(parsed.sessionId);
|
|
27
|
+
if (session?.channel_key === ch) {
|
|
28
|
+
const reply = await handleChoice(services, message, parsed.sessionId, parsed.choiceId);
|
|
29
|
+
if (reply)
|
|
30
|
+
await context.input.$reply(reply);
|
|
31
|
+
return;
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
await next();
|
|
35
|
+
return;
|
|
36
|
+
}
|
|
37
|
+
const session = await services.getActiveForUser(ch, message.$sender.id);
|
|
38
|
+
if (!session) {
|
|
39
|
+
await next();
|
|
40
|
+
return;
|
|
41
|
+
}
|
|
42
|
+
if (session.status === 'active') {
|
|
43
|
+
const map = buildChoiceFallbackMap(CHAIN_PREFIX, session.id, [
|
|
44
|
+
{ id: 'hint', label: '提示' },
|
|
45
|
+
{ id: 'skip', label: '跳过' },
|
|
46
|
+
{ id: 'quit', label: '认输' },
|
|
47
|
+
]);
|
|
48
|
+
const payload = resolveGameTextPayload(raw, map);
|
|
49
|
+
const parsed = payload ? parseChoicePayload(payload, CHAIN_PREFIX) : null;
|
|
50
|
+
if (parsed?.sessionId === session.id) {
|
|
51
|
+
const reply = await handleChoice(services, message, session.id, parsed.choiceId);
|
|
52
|
+
if (reply)
|
|
53
|
+
await context.input.$reply(reply);
|
|
54
|
+
return;
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
// 仅把 2-8 个汉字的纯文本当成语作答,其余放行
|
|
58
|
+
if (/^[\u4e00-\u9fff]{2,8}$/.test(raw)) {
|
|
59
|
+
const reply = await processIdiomText(services, message, raw);
|
|
60
|
+
if (reply)
|
|
61
|
+
await context.input.$reply(reply);
|
|
62
|
+
return;
|
|
63
|
+
}
|
|
64
|
+
await next();
|
|
65
|
+
},
|
|
66
|
+
});
|
|
@@ -33,7 +33,7 @@ export default defineMiddleware<Message>({
|
|
|
33
33
|
if (parsed) {
|
|
34
34
|
const session = await services.getById(parsed.sessionId);
|
|
35
35
|
if (session?.channel_key === ch) {
|
|
36
|
-
const reply = await handleChoice(
|
|
36
|
+
const reply = await handleChoice(services, message, parsed.sessionId, parsed.choiceId);
|
|
37
37
|
if (reply) await context.input.$reply(reply);
|
|
38
38
|
return;
|
|
39
39
|
}
|
|
@@ -57,7 +57,7 @@ export default defineMiddleware<Message>({
|
|
|
57
57
|
const payload = resolveGameTextPayload(raw, map);
|
|
58
58
|
const parsed = payload ? parseChoicePayload(payload, CHAIN_PREFIX) : null;
|
|
59
59
|
if (parsed?.sessionId === session.id) {
|
|
60
|
-
const reply = await handleChoice(
|
|
60
|
+
const reply = await handleChoice(services, message, session.id, parsed.choiceId);
|
|
61
61
|
if (reply) await context.input.$reply(reply);
|
|
62
62
|
return;
|
|
63
63
|
}
|
|
@@ -65,7 +65,7 @@ export default defineMiddleware<Message>({
|
|
|
65
65
|
|
|
66
66
|
// 仅把 2-8 个汉字的纯文本当成语作答,其余放行
|
|
67
67
|
if (/^[\u4e00-\u9fff]{2,8}$/.test(raw)) {
|
|
68
|
-
const reply = await processIdiomText(
|
|
68
|
+
const reply = await processIdiomText(services, message, raw);
|
|
69
69
|
if (reply) await context.input.$reply(reply);
|
|
70
70
|
return;
|
|
71
71
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@zhin.js/plugin-idiom-chain",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.5",
|
|
4
4
|
"description": "Zhin.js 成语接龙 — 四字成语文字接龙 (Plugin Runtime)",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./lib/index.js",
|
|
@@ -14,7 +14,7 @@
|
|
|
14
14
|
"./package.json": "./package.json"
|
|
15
15
|
},
|
|
16
16
|
"files": [
|
|
17
|
-
"plugin.
|
|
17
|
+
"plugin.js",
|
|
18
18
|
"schema.json",
|
|
19
19
|
"commands",
|
|
20
20
|
"middlewares",
|
|
@@ -25,11 +25,11 @@
|
|
|
25
25
|
"license": "MIT",
|
|
26
26
|
"dependencies": {
|
|
27
27
|
"chinese-idiom-chengyu": "^1.1.0",
|
|
28
|
-
"@zhin.js/command": "1.0.
|
|
29
|
-
"@zhin.js/core": "1.4.
|
|
30
|
-
"@zhin.js/game-kit": "2.0.
|
|
31
|
-
"@zhin.js/middleware": "1.0.
|
|
32
|
-
"@zhin.js/plugin-runtime": "1.1.
|
|
28
|
+
"@zhin.js/command": "1.0.4",
|
|
29
|
+
"@zhin.js/core": "1.4.2",
|
|
30
|
+
"@zhin.js/game-kit": "2.0.2",
|
|
31
|
+
"@zhin.js/middleware": "1.0.4",
|
|
32
|
+
"@zhin.js/plugin-runtime": "1.1.1"
|
|
33
33
|
},
|
|
34
34
|
"devDependencies": {
|
|
35
35
|
"typescript": "^6.0.3",
|
|
@@ -50,7 +50,7 @@
|
|
|
50
50
|
"zhin": {
|
|
51
51
|
"protocol": 1,
|
|
52
52
|
"type": "plugin",
|
|
53
|
-
"entry": "./plugin.
|
|
53
|
+
"entry": "./plugin.js",
|
|
54
54
|
"engine": "^1.0.0",
|
|
55
55
|
"runtime": "trusted",
|
|
56
56
|
"features": [
|
package/plugin.js
ADDED
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
// Generated by build-plugin-runtime-entries.mjs. Do not edit.
|
|
2
|
+
import { defineGamePlugin } from '@zhin.js/game-kit';
|
|
3
|
+
import { defineHostTables } from "./lib/models.js";
|
|
4
|
+
import { gameServicesToken } from "./lib/runtime-store.js";
|
|
5
|
+
import { createServices, } from "./lib/session-service.js";
|
|
6
|
+
/**
|
|
7
|
+
* Plugin Runtime:
|
|
8
|
+
* - Commands under `commands/` are authoritative (help + start/continue/quit via in-memory DB).
|
|
9
|
+
* - DB: prefer databaseHostToken; else in-memory SessionService.
|
|
10
|
+
* - Text middleware under `middlewares/` handles idiom answer payloads.
|
|
11
|
+
*/
|
|
12
|
+
export default defineGamePlugin({
|
|
13
|
+
name: 'idiom-chain',
|
|
14
|
+
metadata: {
|
|
15
|
+
displayName: 'Idiom Chain',
|
|
16
|
+
},
|
|
17
|
+
game: {
|
|
18
|
+
id: 'chain',
|
|
19
|
+
title: '成语接龙',
|
|
20
|
+
icon: '📜',
|
|
21
|
+
description: '四字成语接龙(同音/同字)',
|
|
22
|
+
commandPrefix: '/接龙',
|
|
23
|
+
quickStart: 'start_pinyin',
|
|
24
|
+
aliases: ['chain'],
|
|
25
|
+
menus: [
|
|
26
|
+
{ id: 'start_pinyin', label: '🎮 同音接龙' },
|
|
27
|
+
{ id: 'start_char', label: '📝 同字接龙' },
|
|
28
|
+
{ id: 'continue', label: '🔄 继续' },
|
|
29
|
+
{ id: 'help', label: '📖 玩法说明' },
|
|
30
|
+
],
|
|
31
|
+
},
|
|
32
|
+
tables: ['idiom_chain_sessions'],
|
|
33
|
+
servicesToken: gameServicesToken,
|
|
34
|
+
defineHostTables,
|
|
35
|
+
createServices: (database) => createServices(database),
|
|
36
|
+
session: (services) => services,
|
|
37
|
+
});
|
package/src/chain-command.ts
CHANGED
|
@@ -1,5 +1,8 @@
|
|
|
1
|
-
import
|
|
2
|
-
|
|
1
|
+
import {
|
|
2
|
+
channelKey,
|
|
3
|
+
type GameMessageLike,
|
|
4
|
+
type GameReply,
|
|
5
|
+
} from '@zhin.js/game-kit';
|
|
3
6
|
import { continueGame, startGame } from './game-flow.js';
|
|
4
7
|
import { idiomCount, modeLabel, promptLine, type MatchMode } from './engine.js';
|
|
5
8
|
|
|
@@ -19,11 +22,10 @@ export const CHAIN_HELP = [
|
|
|
19
22
|
].join('\n');
|
|
20
23
|
|
|
21
24
|
export async function runChainCommand(
|
|
22
|
-
plugin: Plugin | null,
|
|
23
25
|
services: SessionService,
|
|
24
|
-
message:
|
|
26
|
+
message: GameMessageLike,
|
|
25
27
|
action: string,
|
|
26
|
-
): Promise<
|
|
28
|
+
): Promise<GameReply> {
|
|
27
29
|
const ch = channelKey(message);
|
|
28
30
|
const userId = message.$sender.id;
|
|
29
31
|
|
|
@@ -42,9 +44,9 @@ export async function runChainCommand(
|
|
|
42
44
|
return lines.join('\n');
|
|
43
45
|
}
|
|
44
46
|
|
|
45
|
-
if (action === 'start_pinyin') return startGame(
|
|
46
|
-
if (action === 'start_char') return startGame(
|
|
47
|
-
if (action === 'continue') return continueGame(
|
|
47
|
+
if (action === 'start_pinyin') return (await startGame(services, message, 'pinyin')) ?? '';
|
|
48
|
+
if (action === 'start_char') return (await startGame(services, message, 'char')) ?? '';
|
|
49
|
+
if (action === 'continue') return continueGame(services, message);
|
|
48
50
|
|
|
49
51
|
if (action === 'quit') {
|
|
50
52
|
const row = await services.getActiveForUser(ch, userId);
|
|
@@ -55,12 +57,3 @@ export async function runChainCommand(
|
|
|
55
57
|
|
|
56
58
|
return `未知子命令:${action}\n\n${CHAIN_HELP}`;
|
|
57
59
|
}
|
|
58
|
-
|
|
59
|
-
/** Plugin Runtime / smoke: text-only, no Adapter.editMessage. */
|
|
60
|
-
export async function runChainCommandText(
|
|
61
|
-
services: SessionService,
|
|
62
|
-
message: Message<any>,
|
|
63
|
-
action: string,
|
|
64
|
-
): Promise<string> {
|
|
65
|
-
return (await runChainCommand(null, services, message, action)) ?? '';
|
|
66
|
-
}
|
package/src/game-flow.ts
CHANGED
|
@@ -1,5 +1,4 @@
|
|
|
1
|
-
import type {
|
|
2
|
-
import { plainTextFromSendContent } from '@zhin.js/game-kit';
|
|
1
|
+
import type { GameMessageLike, GameReply } from '@zhin.js/game-kit';
|
|
3
2
|
import {
|
|
4
3
|
CHAIN_PREFIX,
|
|
5
4
|
getGloss,
|
|
@@ -22,34 +21,13 @@ function sessionMode(session: ChainSessionRow): MatchMode {
|
|
|
22
21
|
}
|
|
23
22
|
|
|
24
23
|
export async function sendOrEditView(
|
|
25
|
-
plugin: Plugin | null,
|
|
26
24
|
services: SessionService,
|
|
27
|
-
message:
|
|
25
|
+
message: GameMessageLike,
|
|
28
26
|
session: ChainSessionRow,
|
|
29
27
|
eventLines: string[] = [],
|
|
30
|
-
): Promise<
|
|
28
|
+
): Promise<GameReply> {
|
|
31
29
|
const content = buildChainView(session, eventLines, message.$channel.type);
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
const adapter = plugin.root.inject(message.$adapter) as Adapter;
|
|
35
|
-
|
|
36
|
-
if (session.board_message_id) {
|
|
37
|
-
const msgId = await adapter.editMessage({
|
|
38
|
-
messageId: session.board_message_id,
|
|
39
|
-
context: String(message.$adapter),
|
|
40
|
-
endpoint: message.$endpoint,
|
|
41
|
-
id: message.$channel.id,
|
|
42
|
-
type: message.$channel.type,
|
|
43
|
-
content,
|
|
44
|
-
});
|
|
45
|
-
if (msgId !== session.board_message_id) {
|
|
46
|
-
await services.updateSession(session.id, { board_message_id: msgId });
|
|
47
|
-
}
|
|
48
|
-
return;
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
const msgId = await message.$reply?.(content);
|
|
52
|
-
if (msgId) await services.updateSession(session.id, { board_message_id: msgId });
|
|
30
|
+
return content;
|
|
53
31
|
}
|
|
54
32
|
|
|
55
33
|
async function botTurn(
|
|
@@ -86,11 +64,10 @@ async function botTurn(
|
|
|
86
64
|
}
|
|
87
65
|
|
|
88
66
|
export async function startGame(
|
|
89
|
-
plugin: Plugin | null,
|
|
90
67
|
services: SessionService,
|
|
91
|
-
message:
|
|
68
|
+
message: GameMessageLike,
|
|
92
69
|
matchMode: MatchMode = 'pinyin',
|
|
93
|
-
): Promise<
|
|
70
|
+
): Promise<GameReply | undefined> {
|
|
94
71
|
const ch = `${message.$adapter}-${message.$endpoint}-${message.$channel.type}:${message.$channel.id}`;
|
|
95
72
|
const active = await services.getActiveByChannel(ch);
|
|
96
73
|
if (active) {
|
|
@@ -111,34 +88,29 @@ export async function startGame(
|
|
|
111
88
|
});
|
|
112
89
|
|
|
113
90
|
const gloss = starter.gloss ?? getGloss(starter.text);
|
|
114
|
-
|
|
91
|
+
return sendOrEditView(services, message, session, [
|
|
115
92
|
`🎬 ${modeLabel(matchMode)}开局!我先出:**${starter.text}**${gloss ? `(${gloss})` : ''}`,
|
|
116
93
|
promptLine(starter.text, matchMode),
|
|
117
94
|
]);
|
|
118
|
-
return typeof text === 'string' ? text : undefined;
|
|
119
95
|
}
|
|
120
96
|
|
|
121
97
|
export async function continueGame(
|
|
122
|
-
plugin: Plugin | null,
|
|
123
98
|
services: SessionService,
|
|
124
|
-
message:
|
|
125
|
-
): Promise<
|
|
99
|
+
message: GameMessageLike,
|
|
100
|
+
): Promise<GameReply> {
|
|
126
101
|
const session = await services.getActiveForUser(
|
|
127
102
|
`${message.$adapter}-${message.$endpoint}-${message.$channel.type}:${message.$channel.id}`,
|
|
128
103
|
message.$sender.id,
|
|
129
104
|
);
|
|
130
105
|
if (!session) return '你没有进行中的接龙,发送「接龙 开始」。';
|
|
131
|
-
|
|
132
|
-
if (typeof text === 'string') return text;
|
|
133
|
-
return '已刷新接龙界面。';
|
|
106
|
+
return sendOrEditView(services, message, session);
|
|
134
107
|
}
|
|
135
108
|
|
|
136
109
|
export async function processIdiomText(
|
|
137
|
-
plugin: Plugin | null,
|
|
138
110
|
services: SessionService,
|
|
139
|
-
message:
|
|
111
|
+
message: GameMessageLike,
|
|
140
112
|
raw: string,
|
|
141
|
-
): Promise<
|
|
113
|
+
): Promise<GameReply | null> {
|
|
142
114
|
const ch = `${message.$adapter}-${message.$endpoint}-${message.$channel.type}:${message.$channel.id}`;
|
|
143
115
|
const session = await services.getActiveForUser(ch, message.$sender.id);
|
|
144
116
|
if (!session || session.status !== 'active') return null;
|
|
@@ -146,6 +118,10 @@ export async function processIdiomText(
|
|
|
146
118
|
const mode = sessionMode(session);
|
|
147
119
|
const used = parseUsed(session.used_idioms);
|
|
148
120
|
const idiom = normalizeInput(raw);
|
|
121
|
+
// 格式不合规(非四字)只提示、不扣失误:避免闲聊(「好的」「谢谢」)被累计判负
|
|
122
|
+
if (!/^[\u4e00-\u9fff]{4}$/.test(idiom)) {
|
|
123
|
+
return '接龙请输入**四字成语**(这条不算失误)。';
|
|
124
|
+
}
|
|
149
125
|
const check = validatePlayerIdiom(idiom, session.last_idiom, mode, used);
|
|
150
126
|
if (!check.ok) {
|
|
151
127
|
const wrong = session.wrong_count + 1;
|
|
@@ -157,14 +133,14 @@ export async function processIdiomText(
|
|
|
157
133
|
streak: 0,
|
|
158
134
|
});
|
|
159
135
|
const updated = (await services.getById(session.id))!;
|
|
160
|
-
return (await sendOrEditView(
|
|
136
|
+
return (await sendOrEditView(services, message, updated, [
|
|
161
137
|
check.reason!,
|
|
162
138
|
'失误过多,本局结束。',
|
|
163
139
|
])) ?? null;
|
|
164
140
|
}
|
|
165
141
|
await services.updateSession(session.id, { wrong_count: wrong, streak: 0 });
|
|
166
142
|
const updated = (await services.getById(session.id))!;
|
|
167
|
-
return (await sendOrEditView(
|
|
143
|
+
return (await sendOrEditView(services, message, updated, [check.reason!])) ?? null;
|
|
168
144
|
}
|
|
169
145
|
|
|
170
146
|
used.add(idiom);
|
|
@@ -186,19 +162,18 @@ export async function processIdiomText(
|
|
|
186
162
|
const userLine = `✅ 你:${idiom}${gloss ? `(${gloss})` : ''}`;
|
|
187
163
|
|
|
188
164
|
const botResult = await botTurn(services, current, used);
|
|
189
|
-
return (await sendOrEditView(
|
|
165
|
+
return (await sendOrEditView(services, message, botResult.session, [
|
|
190
166
|
userLine,
|
|
191
167
|
...botResult.lines,
|
|
192
168
|
])) ?? null;
|
|
193
169
|
}
|
|
194
170
|
|
|
195
171
|
export async function handleChoice(
|
|
196
|
-
plugin: Plugin | null,
|
|
197
172
|
services: SessionService,
|
|
198
|
-
message:
|
|
173
|
+
message: GameMessageLike,
|
|
199
174
|
sessionId: string,
|
|
200
175
|
choiceId: string,
|
|
201
|
-
): Promise<
|
|
176
|
+
): Promise<GameReply | null> {
|
|
202
177
|
const session = await services.getById(sessionId);
|
|
203
178
|
if (!session) return '对局不存在。';
|
|
204
179
|
if (session.player_id !== message.$sender.id) return '这是别人的接龙。';
|
|
@@ -217,11 +192,10 @@ export async function handleChoice(
|
|
|
217
192
|
wrong_count: 0,
|
|
218
193
|
turn: 'player',
|
|
219
194
|
status: 'active',
|
|
220
|
-
board_message_id: '',
|
|
221
195
|
});
|
|
222
196
|
const updated = (await services.getById(session.id))!;
|
|
223
197
|
const gloss = starter.gloss ?? getGloss(starter.text);
|
|
224
|
-
return (await sendOrEditView(
|
|
198
|
+
return (await sendOrEditView(services, message, updated, [
|
|
225
199
|
`🎬 新一局!我先出:**${starter.text}**${gloss ? `(${gloss})` : ''}`,
|
|
226
200
|
promptLine(starter.text, mode),
|
|
227
201
|
])) ?? null;
|
|
@@ -236,15 +210,17 @@ export async function handleChoice(
|
|
|
236
210
|
if (choiceId === 'hint') {
|
|
237
211
|
const hint = pickHintIdiom(session.last_idiom, mode, used);
|
|
238
212
|
if (!hint) {
|
|
239
|
-
|
|
213
|
+
// 先更新状态再单次发送终局视图,避免旧 session「你赢了」与终局视图双发
|
|
240
214
|
await services.updateSession(session.id, { status: 'won', player_score: session.player_score + 1 });
|
|
241
215
|
const updated = (await services.getById(session.id))!;
|
|
242
|
-
return (await sendOrEditView(
|
|
216
|
+
return (await sendOrEditView(services, message, updated, [
|
|
217
|
+
'💡 词库中暂无可用提示,你赢了!',
|
|
218
|
+
])) ?? null;
|
|
243
219
|
}
|
|
244
220
|
const gloss = getGloss(hint);
|
|
245
221
|
await services.updateSession(session.id, { hints_used: session.hints_used + 1, streak: 0 });
|
|
246
222
|
const updated = (await services.getById(session.id))!;
|
|
247
|
-
return (await sendOrEditView(
|
|
223
|
+
return (await sendOrEditView(services, message, updated, [
|
|
248
224
|
`💡 提示:可试 **${hint}**${gloss ? `(${gloss})` : ''}`,
|
|
249
225
|
'(使用提示会清零连击)',
|
|
250
226
|
])) ?? null;
|
|
@@ -257,7 +233,7 @@ export async function handleChoice(
|
|
|
257
233
|
streak: 0,
|
|
258
234
|
});
|
|
259
235
|
const updated = (await services.getById(session.id))!;
|
|
260
|
-
return (await sendOrEditView(
|
|
236
|
+
return (await sendOrEditView(services, message, updated, [
|
|
261
237
|
'⏭️ 你选择跳过,机器人得一分。',
|
|
262
238
|
])) ?? null;
|
|
263
239
|
}
|
|
@@ -265,7 +241,7 @@ export async function handleChoice(
|
|
|
265
241
|
if (choiceId === 'quit') {
|
|
266
242
|
await services.updateSession(session.id, { status: 'lost', bot_score: session.bot_score + 1 });
|
|
267
243
|
const updated = (await services.getById(session.id))!;
|
|
268
|
-
return (await sendOrEditView(
|
|
244
|
+
return (await sendOrEditView(services, message, updated, ['🏳️ 你认输了。'])) ?? null;
|
|
269
245
|
}
|
|
270
246
|
|
|
271
247
|
return '未知操作。';
|
package/src/index.ts
CHANGED
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
export { CHAIN_HELP } from './chain-command.js';
|
|
2
2
|
export { gameServicesToken, resolveGameServices } from './runtime-store.js';
|
|
3
|
-
export { createInMemoryChainDb, mountChainMemoryServices } from './memory-db.js';
|
|
4
3
|
|
|
5
4
|
/**
|
|
6
5
|
* 已由 plugin.ts 接线:commands/ 命令、middlewares/ 文本与选项中间件、
|
package/src/models.ts
CHANGED
|
@@ -26,7 +26,6 @@ declare module '@zhin.js/core' {
|
|
|
26
26
|
hints_used: number;
|
|
27
27
|
turn: string;
|
|
28
28
|
status: ChainSessionStatus;
|
|
29
|
-
board_message_id: string;
|
|
30
29
|
updated_at: number;
|
|
31
30
|
created_at: number;
|
|
32
31
|
};
|
|
@@ -60,7 +59,6 @@ export function defineHostTables(
|
|
|
60
59
|
hints_used: { type: 'integer', default: 0 },
|
|
61
60
|
turn: { type: 'text', default: 'player' },
|
|
62
61
|
status: { type: 'text', default: 'active' },
|
|
63
|
-
board_message_id: { type: 'text', default: '' },
|
|
64
62
|
updated_at: { type: 'integer', default: 0 },
|
|
65
63
|
created_at: { type: 'integer', default: 0 },
|
|
66
64
|
});
|
package/src/session-service.ts
CHANGED
|
@@ -1,16 +1,16 @@
|
|
|
1
|
-
import type { Database,
|
|
2
|
-
import {
|
|
1
|
+
import type { Database, Models } from '@zhin.js/core';
|
|
2
|
+
import {
|
|
3
|
+
BaseSessionService,
|
|
4
|
+
channelKey,
|
|
5
|
+
generateSessionId,
|
|
6
|
+
type GameMessageLike,
|
|
7
|
+
type GameSessionDatabase,
|
|
8
|
+
} from '@zhin.js/game-kit';
|
|
3
9
|
import type { MatchMode } from './engine.js';
|
|
4
10
|
import type { ChainSessionRow } from './models.js';
|
|
5
11
|
|
|
6
12
|
export type ChainDatabase = Database<unknown, Models, string>;
|
|
7
13
|
|
|
8
|
-
function getModel(db: ChainDatabase) {
|
|
9
|
-
const model = db.models.get('idiom_chain_sessions');
|
|
10
|
-
if (!model) throw new Error('idiom_chain_sessions not registered');
|
|
11
|
-
return model as RelatedModel<unknown, Models, 'idiom_chain_sessions'>;
|
|
12
|
-
}
|
|
13
|
-
|
|
14
14
|
export function parseUsed(value: string | string[] | unknown): Set<string> {
|
|
15
15
|
if (Array.isArray(value)) return new Set(value.filter((x): x is string => typeof x === 'string'));
|
|
16
16
|
if (typeof value !== 'string' || !value) return new Set();
|
|
@@ -26,35 +26,17 @@ export function serializeUsed(set: Set<string>): string {
|
|
|
26
26
|
return JSON.stringify([...set]);
|
|
27
27
|
}
|
|
28
28
|
|
|
29
|
-
export class SessionService {
|
|
30
|
-
constructor(
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
async getActiveForUser(channel: string, userId: string): Promise<ChainSessionRow | null> {
|
|
38
|
-
const row = await this.getActiveByChannel(channel);
|
|
39
|
-
if (!row || row.player_id !== userId) return null;
|
|
40
|
-
return row;
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
async getById(id: string): Promise<ChainSessionRow | null> {
|
|
44
|
-
return getModel(this.db).findOne({ id });
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
async getActiveByBoardMessageId(messageId: string): Promise<ChainSessionRow | null> {
|
|
48
|
-
if (!messageId) return null;
|
|
49
|
-
const rows = await getModel(this.db).findAll({});
|
|
50
|
-
for (const row of rows) {
|
|
51
|
-
if (boardMessageMatches(row.board_message_id ?? '', messageId)) return row;
|
|
52
|
-
}
|
|
53
|
-
return null;
|
|
29
|
+
export class SessionService extends BaseSessionService<ChainSessionRow> {
|
|
30
|
+
constructor(db: ChainDatabase) {
|
|
31
|
+
super(db as unknown as GameSessionDatabase<ChainSessionRow>, {
|
|
32
|
+
gameId: 'chain',
|
|
33
|
+
table: 'idiom_chain_sessions',
|
|
34
|
+
userFields: ['player_id'],
|
|
35
|
+
});
|
|
54
36
|
}
|
|
55
37
|
|
|
56
38
|
async createSession(
|
|
57
|
-
message:
|
|
39
|
+
message: GameMessageLike,
|
|
58
40
|
starter: { text: string; nextChar: string; used: string[]; matchMode: MatchMode },
|
|
59
41
|
): Promise<ChainSessionRow> {
|
|
60
42
|
const now = Date.now();
|
|
@@ -79,34 +61,13 @@ export class SessionService {
|
|
|
79
61
|
hints_used: 0,
|
|
80
62
|
turn: 'player',
|
|
81
63
|
status: 'active',
|
|
82
|
-
board_message_id: '',
|
|
83
64
|
updated_at: now,
|
|
84
65
|
created_at: now,
|
|
85
66
|
};
|
|
86
|
-
|
|
87
|
-
return row;
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
async updateSession(id: string, patch: Partial<ChainSessionRow>): Promise<void> {
|
|
91
|
-
await getModel(this.db).updateWhere({ id }, { ...patch, updated_at: Date.now() });
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
async abortStale(idleMs: number): Promise<number> {
|
|
95
|
-
const cutoff = Date.now() - idleMs;
|
|
96
|
-
const model = getModel(this.db);
|
|
97
|
-
const rows = await model.findAll({ status: 'active' });
|
|
98
|
-
let n = 0;
|
|
99
|
-
for (const row of rows) {
|
|
100
|
-
if (row.updated_at < cutoff) {
|
|
101
|
-
await model.updateWhere({ id: row.id }, { status: 'aborted', updated_at: Date.now() });
|
|
102
|
-
n++;
|
|
103
|
-
}
|
|
104
|
-
}
|
|
105
|
-
return n;
|
|
67
|
+
return this.createRow(row);
|
|
106
68
|
}
|
|
107
69
|
}
|
|
108
70
|
|
|
109
71
|
export function createServices(db: ChainDatabase): SessionService {
|
|
110
72
|
return new SessionService(db);
|
|
111
73
|
}
|
|
112
|
-
|
package/lib/memory-db.d.ts
DELETED
|
@@ -1,6 +0,0 @@
|
|
|
1
|
-
import type { DatabaseHost } from '@zhin.js/plugin-runtime';
|
|
2
|
-
import { type ChainDatabase, type SessionService } from './session-service.js';
|
|
3
|
-
/** In-memory idiom_chain_sessions for Plugin Runtime slice-2 (no DatabaseFeature). */
|
|
4
|
-
export declare function createInMemoryChainDb(): ChainDatabase;
|
|
5
|
-
export declare function mountChainMemoryServices(): SessionService;
|
|
6
|
-
export declare function mountChainHostServices(host: DatabaseHost): SessionService;
|
package/lib/memory-db.js
DELETED
|
@@ -1,18 +0,0 @@
|
|
|
1
|
-
import { createHostGameDb, createInMemoryGameDb } from '@zhin.js/game-kit';
|
|
2
|
-
import { defineHostTables } from './models.js';
|
|
3
|
-
import { createServices } from './session-service.js';
|
|
4
|
-
const CHAIN_TABLES = ['idiom_chain_sessions'];
|
|
5
|
-
/** In-memory idiom_chain_sessions for Plugin Runtime slice-2 (no DatabaseFeature). */
|
|
6
|
-
export function createInMemoryChainDb() {
|
|
7
|
-
return createInMemoryGameDb(CHAIN_TABLES);
|
|
8
|
-
}
|
|
9
|
-
export function mountChainMemoryServices() {
|
|
10
|
-
const services = createServices(createInMemoryChainDb());
|
|
11
|
-
return services;
|
|
12
|
-
}
|
|
13
|
-
export function mountChainHostServices(host) {
|
|
14
|
-
defineHostTables(host);
|
|
15
|
-
const services = createServices(createHostGameDb(host, CHAIN_TABLES));
|
|
16
|
-
return services;
|
|
17
|
-
}
|
|
18
|
-
//# sourceMappingURL=memory-db.js.map
|
package/lib/memory-db.js.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
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,EAA2C,MAAM,sBAAsB,CAAC;AAE/F,MAAM,YAAY,GAAG,CAAC,sBAAsB,CAAU,CAAC;AAEvD,sFAAsF;AACtF,MAAM,UAAU,qBAAqB;IACnC,OAAO,oBAAoB,CAAC,YAAY,CAA6B,CAAC;AACxE,CAAC;AAED,MAAM,UAAU,wBAAwB;IACtC,MAAM,QAAQ,GAAG,cAAc,CAAC,qBAAqB,EAAE,CAAC,CAAC;IACzD,OAAO,QAAQ,CAAC;AAClB,CAAC;AAED,MAAM,UAAU,sBAAsB,CAAC,IAAkB;IACvD,gBAAgB,CAAC,IAAI,CAAC,CAAC;IACvB,MAAM,QAAQ,GAAG,cAAc,CAAC,gBAAgB,CAAC,IAAI,EAAE,YAAY,CAA6B,CAAC,CAAC;IAClG,OAAO,QAAQ,CAAC;AAClB,CAAC"}
|
package/plugin.ts
DELETED
|
@@ -1,64 +0,0 @@
|
|
|
1
|
-
import { definePlugin, databaseHostToken, scheduleHostToken } from '@zhin.js/plugin-runtime';
|
|
2
|
-
import {
|
|
3
|
-
registerRuntimeGame,
|
|
4
|
-
initGameRecordHost,
|
|
5
|
-
DEFAULT_GAME_STALE_CRON,
|
|
6
|
-
DEFAULT_GAME_STALE_IDLE_MS,
|
|
7
|
-
} from '@zhin.js/game-kit';
|
|
8
|
-
import { mountChainHostServices, mountChainMemoryServices } from './src/memory-db.js';
|
|
9
|
-
import { gameServicesToken } from './src/runtime-store.js';
|
|
10
|
-
import type { SessionService } from './src/session-service.js';
|
|
11
|
-
|
|
12
|
-
/**
|
|
13
|
-
* Plugin Runtime:
|
|
14
|
-
* - Commands under `commands/` are authoritative (help + start/continue/quit via in-memory DB).
|
|
15
|
-
* - DB: prefer databaseHostToken; else in-memory SessionService.
|
|
16
|
-
* - Text middleware under `middlewares/` handles idiom answer payloads.
|
|
17
|
-
*/
|
|
18
|
-
export default definePlugin({
|
|
19
|
-
name: 'idiom-chain',
|
|
20
|
-
metadata: {
|
|
21
|
-
displayName: 'Idiom Chain',
|
|
22
|
-
},
|
|
23
|
-
setup(context) {
|
|
24
|
-
let services: SessionService;
|
|
25
|
-
if (context.resources.has(databaseHostToken)) {
|
|
26
|
-
const host = context.resources.use(databaseHostToken);
|
|
27
|
-
services = mountChainHostServices(host);
|
|
28
|
-
context.lifecycle.add(initGameRecordHost(host));
|
|
29
|
-
} else {
|
|
30
|
-
services = mountChainMemoryServices();
|
|
31
|
-
}
|
|
32
|
-
context.resources.provide(gameServicesToken, services);
|
|
33
|
-
const disposeHub = registerRuntimeGame({
|
|
34
|
-
id: 'chain',
|
|
35
|
-
title: '成语接龙',
|
|
36
|
-
icon: '📜',
|
|
37
|
-
description: '四字成语接龙(同音/同字)',
|
|
38
|
-
commandPrefix: '/接龙',
|
|
39
|
-
quickStart: 'start_pinyin',
|
|
40
|
-
aliases: ['chain'],
|
|
41
|
-
menus: [
|
|
42
|
-
{ id: 'start_pinyin', label: '🎮 同音接龙' },
|
|
43
|
-
{ id: 'start_char', label: '📝 同字接龙' },
|
|
44
|
-
{ id: 'continue', label: '🔄 继续' },
|
|
45
|
-
{ id: 'help', label: '📖 玩法说明' },
|
|
46
|
-
],
|
|
47
|
-
});
|
|
48
|
-
context.lifecycle.add(disposeHub);
|
|
49
|
-
|
|
50
|
-
if (context.resources.has(scheduleHostToken)) {
|
|
51
|
-
const schedule = context.resources.use(scheduleHostToken);
|
|
52
|
-
const disposeCron = schedule.register({
|
|
53
|
-
id: 'chain/abort-stale',
|
|
54
|
-
cron: DEFAULT_GAME_STALE_CRON,
|
|
55
|
-
description: 'Abort stale idiom-chain sessions',
|
|
56
|
-
async execute() {
|
|
57
|
-
if (!services.abortStale) return;
|
|
58
|
-
await services.abortStale(DEFAULT_GAME_STALE_IDLE_MS);
|
|
59
|
-
},
|
|
60
|
-
});
|
|
61
|
-
context.lifecycle.add(disposeCron);
|
|
62
|
-
}
|
|
63
|
-
},
|
|
64
|
-
});
|
package/src/memory-db.ts
DELETED
|
@@ -1,22 +0,0 @@
|
|
|
1
|
-
import { createHostGameDb, createInMemoryGameDb } from '@zhin.js/game-kit';
|
|
2
|
-
import type { DatabaseHost } from '@zhin.js/plugin-runtime';
|
|
3
|
-
import { defineHostTables } from './models.js';
|
|
4
|
-
import { createServices, type ChainDatabase, type SessionService } from './session-service.js';
|
|
5
|
-
|
|
6
|
-
const CHAIN_TABLES = ['idiom_chain_sessions'] as const;
|
|
7
|
-
|
|
8
|
-
/** In-memory idiom_chain_sessions for Plugin Runtime slice-2 (no DatabaseFeature). */
|
|
9
|
-
export function createInMemoryChainDb(): ChainDatabase {
|
|
10
|
-
return createInMemoryGameDb(CHAIN_TABLES) as unknown as ChainDatabase;
|
|
11
|
-
}
|
|
12
|
-
|
|
13
|
-
export function mountChainMemoryServices(): SessionService {
|
|
14
|
-
const services = createServices(createInMemoryChainDb());
|
|
15
|
-
return services;
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
export function mountChainHostServices(host: DatabaseHost): SessionService {
|
|
19
|
-
defineHostTables(host);
|
|
20
|
-
const services = createServices(createHostGameDb(host, CHAIN_TABLES) as unknown as ChainDatabase);
|
|
21
|
-
return services;
|
|
22
|
-
}
|