@zhin.js/plugin-dice-duel 1.0.2 → 1.0.4
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/dice/[action:string=].ts +2 -2
- package/lib/dice-command.d.ts +2 -4
- package/lib/dice-command.js +3 -7
- package/lib/dice-command.js.map +1 -1
- package/lib/game-flow.d.ts +4 -10
- package/lib/game-flow.js +14 -35
- package/lib/game-flow.js.map +1 -1
- package/lib/session-service.d.ts +3 -2
- package/lib/session-service.js.map +1 -1
- package/middlewares/dice-alias.ts +6 -3
- package/middlewares/dice-choice.ts +2 -2
- package/package.json +6 -6
- package/src/dice-command.ts +5 -16
- package/src/game-flow.ts +17 -44
- package/src/session-service.ts +3 -3
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { defineCommand } from '@zhin.js/command';
|
|
2
2
|
import { messageFromCommandInput, normalizeDiceAction } from '@zhin.js/game-kit';
|
|
3
|
-
import { DICE_HELP,
|
|
3
|
+
import { DICE_HELP, runDiceCommand } from '../../src/dice-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 DICE_HELP;
|
|
11
11
|
const services = resolveGameServices({ use, owner });
|
|
12
12
|
const message = messageFromCommandInput(input);
|
|
13
|
-
return
|
|
13
|
+
return runDiceCommand(services, message, action);
|
|
14
14
|
},
|
|
15
15
|
});
|
package/lib/dice-command.d.ts
CHANGED
|
@@ -1,6 +1,4 @@
|
|
|
1
|
-
import
|
|
1
|
+
import { type GameMessageLike } from '@zhin.js/game-kit';
|
|
2
2
|
import type { SessionService } from './session-service.js';
|
|
3
3
|
export declare const DICE_HELP: string;
|
|
4
|
-
export declare function runDiceCommand(
|
|
5
|
-
/** Plugin Runtime / smoke: text-only, no Adapter.editMessage. */
|
|
6
|
-
export declare function runDiceCommandText(services: SessionService, message: Message<any>, action: string): Promise<string>;
|
|
4
|
+
export declare function runDiceCommand(services: SessionService, message: GameMessageLike, action: string): Promise<string>;
|
package/lib/dice-command.js
CHANGED
|
@@ -7,7 +7,7 @@ export const DICE_HELP = [
|
|
|
7
7
|
'骰子 继续 — 刷新界面',
|
|
8
8
|
'骰子 放弃 — 结束对局',
|
|
9
9
|
].join('\n');
|
|
10
|
-
export async function runDiceCommand(
|
|
10
|
+
export async function runDiceCommand(services, message, action) {
|
|
11
11
|
const ch = channelKey(message);
|
|
12
12
|
const userId = message.$sender.id;
|
|
13
13
|
if (!action || action === 'help') {
|
|
@@ -22,9 +22,9 @@ export async function runDiceCommand(plugin, services, message, action) {
|
|
|
22
22
|
return lines.join('\n');
|
|
23
23
|
}
|
|
24
24
|
if (action === 'start')
|
|
25
|
-
return startGame(
|
|
25
|
+
return (await startGame(services, message)) ?? '';
|
|
26
26
|
if (action === 'continue')
|
|
27
|
-
return continueGame(
|
|
27
|
+
return continueGame(services, message);
|
|
28
28
|
if (action === 'quit') {
|
|
29
29
|
const row = await services.getActiveForUser(ch, userId);
|
|
30
30
|
if (!row)
|
|
@@ -34,8 +34,4 @@ export async function runDiceCommand(plugin, services, message, action) {
|
|
|
34
34
|
}
|
|
35
35
|
return `未知子命令:${action}\n\n${DICE_HELP}`;
|
|
36
36
|
}
|
|
37
|
-
/** Plugin Runtime / smoke: text-only, no Adapter.editMessage. */
|
|
38
|
-
export async function runDiceCommandText(services, message, action) {
|
|
39
|
-
return (await runDiceCommand(null, services, message, action)) ?? '';
|
|
40
|
-
}
|
|
41
37
|
//# sourceMappingURL=dice-command.js.map
|
package/lib/dice-command.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"dice-command.js","sourceRoot":"","sources":["../src/dice-command.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"dice-command.js","sourceRoot":"","sources":["../src/dice-command.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAwB,MAAM,mBAAmB,CAAC;AACrE,OAAO,EAAE,YAAY,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAC;AAGzD,MAAM,CAAC,MAAM,SAAS,GAAG;IACvB,eAAe;IACf,gBAAgB;IAChB,aAAa;IACb,cAAc;IACd,cAAc;CACf,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AAEb,MAAM,CAAC,KAAK,UAAU,cAAc,CAClC,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,SAAS,EAAE,EAAE,CAAC,CAAC;QAC9B,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,CAAC,MAAM,SAAS,CAAC,QAAQ,EAAE,OAAO,CAAC,CAAC,IAAI,EAAE,CAAC;IAC1E,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,cAAc,CAAC;QAChC,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,SAAS,EAAE,CAAC;AAC3C,CAAC"}
|
package/lib/game-flow.d.ts
CHANGED
|
@@ -1,13 +1,7 @@
|
|
|
1
|
-
import
|
|
2
|
-
import type { DiceSessionRow } from './models.js';
|
|
1
|
+
import { type GameMessageLike } from '@zhin.js/game-kit';
|
|
3
2
|
import { DICE_PREFIX } from './engine.js';
|
|
4
3
|
import type { SessionService } from './session-service.js';
|
|
5
|
-
export declare function
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
result: 0 | 1 | 2;
|
|
9
|
-
}): Promise<string | void>;
|
|
10
|
-
export declare function startGame(plugin: Plugin | null, services: SessionService, message: Message<any>): Promise<string | undefined>;
|
|
11
|
-
export declare function continueGame(plugin: Plugin | null, services: SessionService, message: Message<any>): Promise<string>;
|
|
12
|
-
export declare function handleChoice(plugin: Plugin | null, services: SessionService, message: Message<any>, sessionId: string, choiceId: string): Promise<string | null>;
|
|
4
|
+
export declare function startGame(services: SessionService, message: GameMessageLike): Promise<string | undefined>;
|
|
5
|
+
export declare function continueGame(services: SessionService, message: GameMessageLike): Promise<string>;
|
|
6
|
+
export declare function handleChoice(services: SessionService, message: GameMessageLike, sessionId: string, choiceId: string): Promise<string | null>;
|
|
13
7
|
export { DICE_PREFIX };
|
package/lib/game-flow.js
CHANGED
|
@@ -1,30 +1,15 @@
|
|
|
1
1
|
import { plainTextFromSendContent, recordGameOutcome } from '@zhin.js/game-kit';
|
|
2
2
|
import { compareRolls, DICE_PREFIX, rollD6, WIN_TARGET } from './engine.js';
|
|
3
3
|
import { buildDiceView } from './view.js';
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
messageId: session.board_message_id,
|
|
12
|
-
context: String(message.$adapter),
|
|
13
|
-
endpoint: message.$endpoint,
|
|
14
|
-
id: message.$channel.id,
|
|
15
|
-
type: message.$channel.type,
|
|
16
|
-
content,
|
|
17
|
-
});
|
|
18
|
-
if (msgId !== session.board_message_id) {
|
|
19
|
-
await services.updateSession(session.id, { board_message_id: msgId });
|
|
20
|
-
}
|
|
21
|
-
return;
|
|
22
|
-
}
|
|
23
|
-
const msgId = await message.$reply?.(content);
|
|
24
|
-
if (msgId)
|
|
25
|
-
await services.updateSession(session.id, { board_message_id: msgId });
|
|
4
|
+
/**
|
|
5
|
+
* Plugin Runtime: render the board as text. Interactive in-place board editing
|
|
6
|
+
* (the old Adapter.editMessage path) is not part of the runtime flow; commands
|
|
7
|
+
* and the choice middleware return fresh text each turn.
|
|
8
|
+
*/
|
|
9
|
+
function renderView(session, message, lastRound) {
|
|
10
|
+
return plainTextFromSendContent(buildDiceView(session, lastRound, message.$channel.type));
|
|
26
11
|
}
|
|
27
|
-
export async function startGame(
|
|
12
|
+
export async function startGame(services, message) {
|
|
28
13
|
const ch = `${message.$adapter}-${message.$endpoint}-${message.$channel.type}:${message.$channel.id}`;
|
|
29
14
|
const active = await services.getActiveByChannel(ch);
|
|
30
15
|
if (active) {
|
|
@@ -34,19 +19,15 @@ export async function startGame(plugin, services, message) {
|
|
|
34
19
|
return `本频道 ${active.player_name} 正在掷骰对决中。`;
|
|
35
20
|
}
|
|
36
21
|
const session = await services.createSession(message);
|
|
37
|
-
|
|
38
|
-
return typeof text === 'string' ? text : undefined;
|
|
22
|
+
return renderView(session, message);
|
|
39
23
|
}
|
|
40
|
-
export async function continueGame(
|
|
24
|
+
export async function continueGame(services, message) {
|
|
41
25
|
const session = await services.getActiveForUser(`${message.$adapter}-${message.$endpoint}-${message.$channel.type}:${message.$channel.id}`, message.$sender.id);
|
|
42
26
|
if (!session)
|
|
43
27
|
return '你没有进行中的骰子对决,发送「骰子 开始」。';
|
|
44
|
-
|
|
45
|
-
if (typeof text === 'string')
|
|
46
|
-
return text;
|
|
47
|
-
return '已刷新骰子界面。';
|
|
28
|
+
return renderView(session, message);
|
|
48
29
|
}
|
|
49
|
-
export async function handleChoice(
|
|
30
|
+
export async function handleChoice(services, message, sessionId, choiceId) {
|
|
50
31
|
const session = await services.getById(sessionId);
|
|
51
32
|
if (!session)
|
|
52
33
|
return '对局不存在。';
|
|
@@ -57,8 +38,7 @@ export async function handleChoice(plugin, services, message, sessionId, choiceI
|
|
|
57
38
|
}
|
|
58
39
|
if (choiceId === 'restart') {
|
|
59
40
|
await services.updateSession(session.id, { status: 'aborted' });
|
|
60
|
-
|
|
61
|
-
return (await startGame(plugin, services, message)) ?? null;
|
|
41
|
+
return (await startGame(services, message)) ?? null;
|
|
62
42
|
}
|
|
63
43
|
if (choiceId !== 'roll')
|
|
64
44
|
return '无效操作。';
|
|
@@ -89,8 +69,7 @@ export async function handleChoice(plugin, services, message, sessionId, choiceI
|
|
|
89
69
|
void recordGameOutcome(message, 'dice', 'won', playerWins * 10);
|
|
90
70
|
else if (status === 'lost')
|
|
91
71
|
void recordGameOutcome(message, 'dice', 'lost');
|
|
92
|
-
|
|
93
|
-
return typeof text === 'string' ? text : null;
|
|
72
|
+
return renderView(updated, message, { player, bot, result });
|
|
94
73
|
}
|
|
95
74
|
export { DICE_PREFIX };
|
|
96
75
|
//# sourceMappingURL=game-flow.js.map
|
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/session-service.d.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import type { Database,
|
|
1
|
+
import type { Database, Models } from '@zhin.js/core';
|
|
2
|
+
import { type GameMessageLike } from '@zhin.js/game-kit';
|
|
2
3
|
import type { DiceSessionRow } from './models.js';
|
|
3
4
|
export type DiceDatabase = Database<unknown, Models, string>;
|
|
4
5
|
export declare class SessionService {
|
|
@@ -8,7 +9,7 @@ export declare class SessionService {
|
|
|
8
9
|
getActiveForUser(channel: string, userId: string): Promise<DiceSessionRow | null>;
|
|
9
10
|
getById(id: string): Promise<DiceSessionRow | null>;
|
|
10
11
|
getActiveByBoardMessageId(messageId: string): Promise<DiceSessionRow | null>;
|
|
11
|
-
createSession(message:
|
|
12
|
+
createSession(message: GameMessageLike): Promise<DiceSessionRow>;
|
|
12
13
|
updateSession(id: string, patch: Partial<DiceSessionRow>): Promise<void>;
|
|
13
14
|
abortStale(idleMs: number): Promise<number>;
|
|
14
15
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"session-service.js","sourceRoot":"","sources":["../src/session-service.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,UAAU,EAAE,iBAAiB,EAAE,mBAAmB,
|
|
1
|
+
{"version":3,"file":"session-service.js","sourceRoot":"","sources":["../src/session-service.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,18 +1,21 @@
|
|
|
1
1
|
import {
|
|
2
2
|
defineGameCommandAliasMiddleware,
|
|
3
|
+
isDiceAction,
|
|
3
4
|
messageFromCommandInput,
|
|
4
5
|
normalizeDiceAction,
|
|
5
6
|
} from '@zhin.js/game-kit';
|
|
6
|
-
import { DICE_HELP,
|
|
7
|
+
import { DICE_HELP, runDiceCommand } from '../src/dice-command.js';
|
|
7
8
|
import { resolveGameServices } from '../src/runtime-store.js';
|
|
8
9
|
|
|
9
10
|
export default defineGameCommandAliasMiddleware({
|
|
10
11
|
aliases: ['骰子', 'dice'],
|
|
11
12
|
async run(action, input, context) {
|
|
12
13
|
const normalized = normalizeDiceAction(String(action ?? ''));
|
|
13
|
-
if (
|
|
14
|
+
if (normalized === 'help') return DICE_HELP;
|
|
15
|
+
// action 无法识别:放行给后续中间件,避免劫持普通聊天
|
|
16
|
+
if (!isDiceAction(normalized)) return null;
|
|
14
17
|
const services = resolveGameServices(context);
|
|
15
18
|
const message = messageFromCommandInput(input);
|
|
16
|
-
return
|
|
19
|
+
return runDiceCommand(services, message, normalized);
|
|
17
20
|
},
|
|
18
21
|
});
|
|
@@ -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
|
}
|
|
@@ -56,7 +56,7 @@ export default defineMiddleware<Message>({
|
|
|
56
56
|
return;
|
|
57
57
|
}
|
|
58
58
|
|
|
59
|
-
const reply = await handleChoice(
|
|
59
|
+
const reply = await handleChoice(services, message, session.id, 'roll');
|
|
60
60
|
if (reply) await context.input.$reply(reply);
|
|
61
61
|
},
|
|
62
62
|
});
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@zhin.js/plugin-dice-duel",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.4",
|
|
4
4
|
"description": "Zhin.js 骰子对决 — 三局两胜比大小 (Plugin Runtime)",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./lib/index.js",
|
|
@@ -24,11 +24,11 @@
|
|
|
24
24
|
],
|
|
25
25
|
"license": "MIT",
|
|
26
26
|
"dependencies": {
|
|
27
|
-
"@zhin.js/command": "1.0.
|
|
28
|
-
"@zhin.js/core": "1.
|
|
29
|
-
"@zhin.js/game-kit": "
|
|
30
|
-
"@zhin.js/middleware": "1.0.
|
|
31
|
-
"@zhin.js/plugin-runtime": "1.
|
|
27
|
+
"@zhin.js/command": "1.0.3",
|
|
28
|
+
"@zhin.js/core": "1.4.1",
|
|
29
|
+
"@zhin.js/game-kit": "2.0.1",
|
|
30
|
+
"@zhin.js/middleware": "1.0.3",
|
|
31
|
+
"@zhin.js/plugin-runtime": "1.1.1"
|
|
32
32
|
},
|
|
33
33
|
"devDependencies": {
|
|
34
34
|
"typescript": "^6.0.3",
|
package/src/dice-command.ts
CHANGED
|
@@ -1,5 +1,4 @@
|
|
|
1
|
-
import
|
|
2
|
-
import { channelKey } from '@zhin.js/game-kit';
|
|
1
|
+
import { channelKey, type GameMessageLike } from '@zhin.js/game-kit';
|
|
3
2
|
import { continueGame, startGame } from './game-flow.js';
|
|
4
3
|
import type { SessionService } from './session-service.js';
|
|
5
4
|
|
|
@@ -12,11 +11,10 @@ export const DICE_HELP = [
|
|
|
12
11
|
].join('\n');
|
|
13
12
|
|
|
14
13
|
export async function runDiceCommand(
|
|
15
|
-
plugin: Plugin | null,
|
|
16
14
|
services: SessionService,
|
|
17
|
-
message:
|
|
15
|
+
message: GameMessageLike,
|
|
18
16
|
action: string,
|
|
19
|
-
): Promise<string
|
|
17
|
+
): Promise<string> {
|
|
20
18
|
const ch = channelKey(message);
|
|
21
19
|
const userId = message.$sender.id;
|
|
22
20
|
|
|
@@ -31,8 +29,8 @@ export async function runDiceCommand(
|
|
|
31
29
|
return lines.join('\n');
|
|
32
30
|
}
|
|
33
31
|
|
|
34
|
-
if (action === 'start') return startGame(
|
|
35
|
-
if (action === 'continue') return continueGame(
|
|
32
|
+
if (action === 'start') return (await startGame(services, message)) ?? '';
|
|
33
|
+
if (action === 'continue') return continueGame(services, message);
|
|
36
34
|
|
|
37
35
|
if (action === 'quit') {
|
|
38
36
|
const row = await services.getActiveForUser(ch, userId);
|
|
@@ -43,12 +41,3 @@ export async function runDiceCommand(
|
|
|
43
41
|
|
|
44
42
|
return `未知子命令:${action}\n\n${DICE_HELP}`;
|
|
45
43
|
}
|
|
46
|
-
|
|
47
|
-
/** Plugin Runtime / smoke: text-only, no Adapter.editMessage. */
|
|
48
|
-
export async function runDiceCommandText(
|
|
49
|
-
services: SessionService,
|
|
50
|
-
message: Message<any>,
|
|
51
|
-
action: string,
|
|
52
|
-
): Promise<string> {
|
|
53
|
-
return (await runDiceCommand(null, services, message, action)) ?? '';
|
|
54
|
-
}
|
package/src/game-flow.ts
CHANGED
|
@@ -1,45 +1,25 @@
|
|
|
1
|
-
import
|
|
2
|
-
import { plainTextFromSendContent, recordGameOutcome } from '@zhin.js/game-kit';
|
|
1
|
+
import { plainTextFromSendContent, recordGameOutcome, type GameMessageLike } from '@zhin.js/game-kit';
|
|
3
2
|
import type { DiceSessionRow } from './models.js';
|
|
4
3
|
import { compareRolls, DICE_PREFIX, rollD6, WIN_TARGET } from './engine.js';
|
|
5
4
|
import type { SessionService } from './session-service.js';
|
|
6
5
|
import { buildDiceView } from './view.js';
|
|
7
6
|
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
7
|
+
/**
|
|
8
|
+
* Plugin Runtime: render the board as text. Interactive in-place board editing
|
|
9
|
+
* (the old Adapter.editMessage path) is not part of the runtime flow; commands
|
|
10
|
+
* and the choice middleware return fresh text each turn.
|
|
11
|
+
*/
|
|
12
|
+
function renderView(
|
|
12
13
|
session: DiceSessionRow,
|
|
14
|
+
message: GameMessageLike,
|
|
13
15
|
lastRound?: { player: number; bot: number; result: 0 | 1 | 2 },
|
|
14
|
-
):
|
|
15
|
-
|
|
16
|
-
if (!plugin) return plainTextFromSendContent(content);
|
|
17
|
-
|
|
18
|
-
const adapter = plugin.root.inject(message.$adapter) as Adapter;
|
|
19
|
-
|
|
20
|
-
if (session.board_message_id) {
|
|
21
|
-
const msgId = await adapter.editMessage({
|
|
22
|
-
messageId: session.board_message_id,
|
|
23
|
-
context: String(message.$adapter),
|
|
24
|
-
endpoint: message.$endpoint,
|
|
25
|
-
id: message.$channel.id,
|
|
26
|
-
type: message.$channel.type,
|
|
27
|
-
content,
|
|
28
|
-
});
|
|
29
|
-
if (msgId !== session.board_message_id) {
|
|
30
|
-
await services.updateSession(session.id, { board_message_id: msgId });
|
|
31
|
-
}
|
|
32
|
-
return;
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
const msgId = await message.$reply?.(content);
|
|
36
|
-
if (msgId) await services.updateSession(session.id, { board_message_id: msgId });
|
|
16
|
+
): string {
|
|
17
|
+
return plainTextFromSendContent(buildDiceView(session, lastRound, message.$channel.type));
|
|
37
18
|
}
|
|
38
19
|
|
|
39
20
|
export async function startGame(
|
|
40
|
-
plugin: Plugin | null,
|
|
41
21
|
services: SessionService,
|
|
42
|
-
message:
|
|
22
|
+
message: GameMessageLike,
|
|
43
23
|
): Promise<string | undefined> {
|
|
44
24
|
const ch = `${message.$adapter}-${message.$endpoint}-${message.$channel.type}:${message.$channel.id}`;
|
|
45
25
|
const active = await services.getActiveByChannel(ch);
|
|
@@ -50,29 +30,24 @@ export async function startGame(
|
|
|
50
30
|
return `本频道 ${active.player_name} 正在掷骰对决中。`;
|
|
51
31
|
}
|
|
52
32
|
const session = await services.createSession(message);
|
|
53
|
-
|
|
54
|
-
return typeof text === 'string' ? text : undefined;
|
|
33
|
+
return renderView(session, message);
|
|
55
34
|
}
|
|
56
35
|
|
|
57
36
|
export async function continueGame(
|
|
58
|
-
plugin: Plugin | null,
|
|
59
37
|
services: SessionService,
|
|
60
|
-
message:
|
|
38
|
+
message: GameMessageLike,
|
|
61
39
|
): Promise<string> {
|
|
62
40
|
const session = await services.getActiveForUser(
|
|
63
41
|
`${message.$adapter}-${message.$endpoint}-${message.$channel.type}:${message.$channel.id}`,
|
|
64
42
|
message.$sender.id,
|
|
65
43
|
);
|
|
66
44
|
if (!session) return '你没有进行中的骰子对决,发送「骰子 开始」。';
|
|
67
|
-
|
|
68
|
-
if (typeof text === 'string') return text;
|
|
69
|
-
return '已刷新骰子界面。';
|
|
45
|
+
return renderView(session, message);
|
|
70
46
|
}
|
|
71
47
|
|
|
72
48
|
export async function handleChoice(
|
|
73
|
-
plugin: Plugin | null,
|
|
74
49
|
services: SessionService,
|
|
75
|
-
message:
|
|
50
|
+
message: GameMessageLike,
|
|
76
51
|
sessionId: string,
|
|
77
52
|
choiceId: string,
|
|
78
53
|
): Promise<string | null> {
|
|
@@ -86,8 +61,7 @@ export async function handleChoice(
|
|
|
86
61
|
|
|
87
62
|
if (choiceId === 'restart') {
|
|
88
63
|
await services.updateSession(session.id, { status: 'aborted' });
|
|
89
|
-
|
|
90
|
-
return (await startGame(plugin, services, message)) ?? null;
|
|
64
|
+
return (await startGame(services, message)) ?? null;
|
|
91
65
|
}
|
|
92
66
|
|
|
93
67
|
if (choiceId !== 'roll') return '无效操作。';
|
|
@@ -117,8 +91,7 @@ export async function handleChoice(
|
|
|
117
91
|
const updated = (await services.getById(session.id))!;
|
|
118
92
|
if (status === 'won') void recordGameOutcome(message, 'dice', 'won', playerWins * 10);
|
|
119
93
|
else if (status === 'lost') void recordGameOutcome(message, 'dice', 'lost');
|
|
120
|
-
|
|
121
|
-
return typeof text === 'string' ? text : null;
|
|
94
|
+
return renderView(updated, message, { player, bot, result });
|
|
122
95
|
}
|
|
123
96
|
|
|
124
97
|
export { DICE_PREFIX };
|
package/src/session-service.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import type { Database,
|
|
2
|
-
import { channelKey, generateSessionId, boardMessageMatches } from '@zhin.js/game-kit';
|
|
1
|
+
import type { Database, Models, RelatedModel } from '@zhin.js/core';
|
|
2
|
+
import { channelKey, generateSessionId, boardMessageMatches, type GameMessageLike } from '@zhin.js/game-kit';
|
|
3
3
|
import type { DiceSessionRow } from './models.js';
|
|
4
4
|
|
|
5
5
|
export type DiceDatabase = Database<unknown, Models, string>;
|
|
@@ -37,7 +37,7 @@ export class SessionService {
|
|
|
37
37
|
return null;
|
|
38
38
|
}
|
|
39
39
|
|
|
40
|
-
async createSession(message:
|
|
40
|
+
async createSession(message: GameMessageLike): Promise<DiceSessionRow> {
|
|
41
41
|
const now = Date.now();
|
|
42
42
|
const row: DiceSessionRow = {
|
|
43
43
|
id: generateSessionId(),
|