@zhin.js/plugin-blackjack 0.0.2 → 0.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.
@@ -1,6 +1,6 @@
1
1
  import { defineCommand } from '@zhin.js/command';
2
2
  import { messageFromCommandInput } from '@zhin.js/game-kit';
3
- import { BJ_HELP, runBjCommandText } from '../../src/bj-command.js';
3
+ import { BJ_HELP, runBjCommand } from '../../src/bj-command.js';
4
4
  import { resolveGameServices } from '../../src/runtime-store.js';
5
5
 
6
6
  function normalizeBjAction(raw: string): string {
@@ -18,6 +18,6 @@ export default defineCommand({
18
18
  if (!action || action === 'help') return BJ_HELP;
19
19
  const services = resolveGameServices({ use, owner });
20
20
  const message = messageFromCommandInput(input);
21
- return runBjCommandText(services, message, action);
21
+ return runBjCommand(services, message, action);
22
22
  },
23
23
  });
@@ -1,6 +1,4 @@
1
- import type { Message, Plugin } from '@zhin.js/core';
1
+ import type { GameMessageLike } from '@zhin.js/game-kit';
2
2
  import type { SessionService } from './session-service.js';
3
3
  export declare const BJ_HELP: string;
4
- export declare function runBjCommand(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 runBjCommandText(services: SessionService, message: Message<any>, action: string): Promise<string>;
4
+ export declare function runBjCommand(services: SessionService, message: GameMessageLike, action: string): Promise<string>;
package/lib/bj-command.js CHANGED
@@ -8,18 +8,14 @@ export const BJ_HELP = [
8
8
  '• `/21点 继续` — 刷新界面',
9
9
  '• 对局中点击 **要牌** / **停牌**',
10
10
  ].join('\n');
11
- export async function runBjCommand(plugin, services, message, action) {
11
+ export async function runBjCommand(services, message, action) {
12
12
  const a = action.trim().toLowerCase() || 'help';
13
13
  if (a === 'help' || a === '帮助')
14
14
  return BJ_HELP;
15
15
  if (a === 'start' || a === '开始')
16
- return startGame(plugin, services, message);
16
+ return (await startGame(services, message)) ?? '';
17
17
  if (a === 'continue' || a === '继续')
18
- return continueGame(plugin, services, message);
18
+ return continueGame(services, message);
19
19
  return `未知操作「${action}」。发送 \`/21点 帮助\`。`;
20
20
  }
21
- /** Plugin Runtime / smoke: text-only, no Adapter.editMessage. */
22
- export async function runBjCommandText(services, message, action) {
23
- return (await runBjCommand(null, services, message, action)) ?? '';
24
- }
25
21
  //# sourceMappingURL=bj-command.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"bj-command.js","sourceRoot":"","sources":["../src/bj-command.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,YAAY,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAC;AAGzD,MAAM,CAAC,MAAM,OAAO,GAAG;IACrB,aAAa;IACb,EAAE;IACF,4BAA4B;IAC5B,EAAE;IACF,kBAAkB;IAClB,oBAAoB;IACpB,yBAAyB;CAC1B,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AAEb,MAAM,CAAC,KAAK,UAAU,YAAY,CAChC,MAAqB,EACrB,QAAwB,EACxB,OAAqB,EACrB,MAAc;IAEd,MAAM,CAAC,GAAG,MAAM,CAAC,IAAI,EAAE,CAAC,WAAW,EAAE,IAAI,MAAM,CAAC;IAChD,IAAI,CAAC,KAAK,MAAM,IAAI,CAAC,KAAK,IAAI;QAAE,OAAO,OAAO,CAAC;IAC/C,IAAI,CAAC,KAAK,OAAO,IAAI,CAAC,KAAK,IAAI;QAAE,OAAO,SAAS,CAAC,MAAM,EAAE,QAAQ,EAAE,OAAO,CAAC,CAAC;IAC7E,IAAI,CAAC,KAAK,UAAU,IAAI,CAAC,KAAK,IAAI;QAAE,OAAO,YAAY,CAAC,MAAM,EAAE,QAAQ,EAAE,OAAO,CAAC,CAAC;IACnF,OAAO,QAAQ,MAAM,mBAAmB,CAAC;AAC3C,CAAC;AAED,iEAAiE;AACjE,MAAM,CAAC,KAAK,UAAU,gBAAgB,CACpC,QAAwB,EACxB,OAAqB,EACrB,MAAc;IAEd,OAAO,CAAC,MAAM,YAAY,CAAC,IAAI,EAAE,QAAQ,EAAE,OAAO,EAAE,MAAM,CAAC,CAAC,IAAI,EAAE,CAAC;AACrE,CAAC"}
1
+ {"version":3,"file":"bj-command.js","sourceRoot":"","sources":["../src/bj-command.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,YAAY,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAC;AAGzD,MAAM,CAAC,MAAM,OAAO,GAAG;IACrB,aAAa;IACb,EAAE;IACF,4BAA4B;IAC5B,EAAE;IACF,kBAAkB;IAClB,oBAAoB;IACpB,yBAAyB;CAC1B,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AAEb,MAAM,CAAC,KAAK,UAAU,YAAY,CAChC,QAAwB,EACxB,OAAwB,EACxB,MAAc;IAEd,MAAM,CAAC,GAAG,MAAM,CAAC,IAAI,EAAE,CAAC,WAAW,EAAE,IAAI,MAAM,CAAC;IAChD,IAAI,CAAC,KAAK,MAAM,IAAI,CAAC,KAAK,IAAI;QAAE,OAAO,OAAO,CAAC;IAC/C,IAAI,CAAC,KAAK,OAAO,IAAI,CAAC,KAAK,IAAI;QAAE,OAAO,CAAC,MAAM,SAAS,CAAC,QAAQ,EAAE,OAAO,CAAC,CAAC,IAAI,EAAE,CAAC;IACnF,IAAI,CAAC,KAAK,UAAU,IAAI,CAAC,KAAK,IAAI;QAAE,OAAO,YAAY,CAAC,QAAQ,EAAE,OAAO,CAAC,CAAC;IAC3E,OAAO,QAAQ,MAAM,mBAAmB,CAAC;AAC3C,CAAC"}
@@ -1,7 +1,7 @@
1
- import type { Message, Plugin } from '@zhin.js/core';
1
+ import { type GameMessageLike } from '@zhin.js/game-kit';
2
2
  import { BJ_PREFIX, handValue, TARGET } from './engine.js';
3
3
  import { type SessionService } from './session-service.js';
4
- export declare function startGame(plugin: Plugin | null, services: SessionService, message: Message<any>): Promise<string | undefined>;
5
- export declare function continueGame(plugin: Plugin | null, services: SessionService, message: Message<any>): Promise<string>;
6
- 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>;
7
7
  export { BJ_PREFIX, handValue, TARGET };
package/lib/game-flow.js CHANGED
@@ -2,30 +2,16 @@ import { plainTextFromSendContent, recordGameOutcome } from '@zhin.js/game-kit';
2
2
  import { BJ_PREFIX, compareHands, dealerShouldHit, handValue, TARGET, } from './engine.js';
3
3
  import { parseCards, parseDeck } from './session-service.js';
4
4
  import { buildBjView, naturalOutcome, playerBust } from './view.js';
5
- async function sendOrEditView(plugin, services, message, session, terminal = false, revealDealer = false) {
5
+ /**
6
+ * Plugin Runtime: render the board as text. Interactive in-place board editing
7
+ * (the old Adapter.editMessage path) is not part of the runtime flow; commands
8
+ * and the choice middleware return fresh text each turn.
9
+ */
10
+ function renderView(session, message, terminal = false, revealDealer = false) {
6
11
  const content = buildBjView(session, terminal, revealDealer, message.$channel.type);
7
- if (!plugin)
8
- return plainTextFromSendContent(content);
9
- const adapter = plugin.root.inject(message.$adapter);
10
- if (session.board_message_id) {
11
- const msgId = await adapter.editMessage({
12
- messageId: session.board_message_id,
13
- context: String(message.$adapter),
14
- endpoint: message.$endpoint,
15
- id: message.$channel.id,
16
- type: message.$channel.type,
17
- content,
18
- });
19
- if (msgId !== session.board_message_id) {
20
- await services.updateSession(session.id, { board_message_id: msgId });
21
- }
22
- return;
23
- }
24
- const msgId = await message.$reply?.(content);
25
- if (msgId)
26
- await services.updateSession(session.id, { board_message_id: msgId });
12
+ return plainTextFromSendContent(content);
27
13
  }
28
- async function finishRound(plugin, services, message, session, status) {
14
+ async function finishRound(services, message, session, status) {
29
15
  await services.updateSession(session.id, { status });
30
16
  const updated = (await services.getById(session.id));
31
17
  if (status === 'won')
@@ -34,7 +20,7 @@ async function finishRound(plugin, services, message, session, status) {
34
20
  void recordGameOutcome(message, 'blackjack', 'lost');
35
21
  else if (status === 'draw')
36
22
  void recordGameOutcome(message, 'blackjack', 'draw');
37
- return sendOrEditView(plugin, services, message, updated, true, true);
23
+ return renderView(updated, message, true, true);
38
24
  }
39
25
  async function dealerPlay(deck, dealer) {
40
26
  const hand = [...dealer];
@@ -46,7 +32,7 @@ async function dealerPlay(deck, dealer) {
46
32
  }
47
33
  return hand;
48
34
  }
49
- export async function startGame(plugin, services, message) {
35
+ export async function startGame(services, message) {
50
36
  const ch = `${message.$adapter}-${message.$endpoint}-${message.$channel.type}:${message.$channel.id}`;
51
37
  const active = await services.getActiveByChannel(ch);
52
38
  if (active) {
@@ -58,22 +44,17 @@ export async function startGame(plugin, services, message) {
58
44
  const session = await services.createSession(message);
59
45
  const natural = naturalOutcome(session);
60
46
  if (natural) {
61
- const text = await finishRound(plugin, services, message, session, natural);
62
- return typeof text === 'string' ? text : undefined;
47
+ return finishRound(services, message, session, natural);
63
48
  }
64
- const text = await sendOrEditView(plugin, services, message, session);
65
- return typeof text === 'string' ? text : undefined;
49
+ return renderView(session, message);
66
50
  }
67
- export async function continueGame(plugin, services, message) {
51
+ export async function continueGame(services, message) {
68
52
  const session = await services.getActiveForUser(`${message.$adapter}-${message.$endpoint}-${message.$channel.type}:${message.$channel.id}`, message.$sender.id);
69
53
  if (!session)
70
54
  return '你没有进行中的 21 点,发送「21点 开始」。';
71
- const text = await sendOrEditView(plugin, services, message, session);
72
- if (typeof text === 'string')
73
- return text;
74
- return '已刷新 21 点界面。';
55
+ return renderView(session, message);
75
56
  }
76
- export async function handleChoice(plugin, services, message, sessionId, choiceId) {
57
+ export async function handleChoice(services, message, sessionId, choiceId) {
77
58
  const session = await services.getById(sessionId);
78
59
  if (!session)
79
60
  return '对局不存在。';
@@ -81,8 +62,7 @@ export async function handleChoice(plugin, services, message, sessionId, choiceI
81
62
  return '这是别人的对局。';
82
63
  if (session.status !== 'active' && choiceId === 'restart') {
83
64
  await services.updateSession(session.id, { status: 'aborted' });
84
- // text-only 模式(plugin===null)下 startGame 的唯一输出就是返回文本
85
- return (await startGame(plugin, services, message)) ?? null;
65
+ return (await startGame(services, message)) ?? null;
86
66
  }
87
67
  if (session.status !== 'active')
88
68
  return '对局已结束,请点击再来一局。';
@@ -99,12 +79,10 @@ export async function handleChoice(plugin, services, message, sessionId, choiceI
99
79
  player_cards_json: JSON.stringify(player),
100
80
  });
101
81
  if (playerBust({ ...session, player_cards_json: JSON.stringify(player) })) {
102
- const text = await finishRound(plugin, services, message, session, 'lost');
103
- return typeof text === 'string' ? text : null;
82
+ return finishRound(services, message, session, 'lost');
104
83
  }
105
84
  const updated = (await services.getById(session.id));
106
- const text = await sendOrEditView(plugin, services, message, updated);
107
- return typeof text === 'string' ? text : null;
85
+ return renderView(updated, message);
108
86
  }
109
87
  if (choiceId !== 'stand')
110
88
  return '无效操作。';
@@ -115,8 +93,7 @@ export async function handleChoice(plugin, services, message, sessionId, choiceI
115
93
  });
116
94
  const outcome = compareHands(player, dealer);
117
95
  const status = outcome === 'won' ? 'won' : outcome === 'lost' ? 'lost' : 'draw';
118
- const text = await finishRound(plugin, services, message, session, status);
119
- return typeof text === 'string' ? text : null;
96
+ return finishRound(services, message, session, status);
120
97
  }
121
98
  export { BJ_PREFIX, handValue, TARGET };
122
99
  //# sourceMappingURL=game-flow.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"game-flow.js","sourceRoot":"","sources":["../src/game-flow.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"game-flow.js","sourceRoot":"","sources":["../src/game-flow.ts"],"names":[],"mappings":"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"}
@@ -1,12 +1,15 @@
1
- import type { Database, Message, Models } from '@zhin.js/core';
1
+ import type { Database, Models } from '@zhin.js/core';
2
+ import { type GameMessageLike } from '@zhin.js/game-kit';
2
3
  import type { BjSessionRow } from './models.js';
3
4
  export type BjDatabase = Database<unknown, Models, string>;
4
5
  export declare class SessionService {
5
6
  private readonly db;
6
7
  constructor(db: BjDatabase);
7
- createSession(message: Message<any>): Promise<BjSessionRow>;
8
+ createSession(message: GameMessageLike): Promise<BjSessionRow>;
8
9
  getById(id: string): Promise<BjSessionRow | null>;
9
10
  getActiveForUser(channelKeyValue: string, userId: string): Promise<BjSessionRow | null>;
11
+ /** 本频道本人最近一局(不按 status 过滤),供终局「回复 1 再来一局」restart 用。 */
12
+ getLatestForUser(channelKeyValue: string, userId: string): Promise<BjSessionRow | null>;
10
13
  getActiveByChannel(channelKeyValue: string): Promise<BjSessionRow | null>;
11
14
  getActiveByBoardMessageId(messageId: string): Promise<BjSessionRow | null>;
12
15
  updateSession(id: string, patch: Partial<BjSessionRow>): Promise<void>;
@@ -47,6 +47,17 @@ export class SessionService {
47
47
  });
48
48
  return rows[0] ?? null;
49
49
  }
50
+ /** 本频道本人最近一局(不按 status 过滤),供终局「回复 1 再来一局」restart 用。 */
51
+ async getLatestForUser(channelKeyValue, userId) {
52
+ const rows = await getModel(this.db).findAll({
53
+ channel_key: channelKeyValue,
54
+ player_id: userId,
55
+ });
56
+ if (rows.length === 0)
57
+ return null;
58
+ // updated_at 可能同毫秒平局,此时取靠后的行(更接近最近创建的一局)
59
+ return rows.reduce((latest, row) => (row.updated_at >= latest.updated_at ? row : latest));
60
+ }
50
61
  async getActiveByChannel(channelKeyValue) {
51
62
  const rows = await getModel(this.db).findAll({
52
63
  channel_key: channelKeyValue,
@@ -1 +1 @@
1
- {"version":3,"file":"session-service.js","sourceRoot":"","sources":["../src/session-service.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"session-service.js","sourceRoot":"","sources":["../src/session-service.ts"],"names":[],"mappings":"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"}
@@ -1,5 +1,5 @@
1
1
  import { defineGameCommandAliasMiddleware, messageFromCommandInput } from '@zhin.js/game-kit';
2
- import { BJ_HELP, runBjCommandText } from '../src/bj-command.js';
2
+ import { BJ_HELP, runBjCommand } from '../src/bj-command.js';
3
3
  import { resolveGameServices } from '../src/runtime-store.js';
4
4
 
5
5
  function normalizeBjAction(raw: string): string {
@@ -17,6 +17,6 @@ export default defineGameCommandAliasMiddleware({
17
17
  if (!normalized || normalized === 'help') return BJ_HELP;
18
18
  const services = resolveGameServices(context);
19
19
  const message = messageFromCommandInput(input);
20
- return runBjCommandText(services, message, normalized);
20
+ return runBjCommand(services, message, normalized);
21
21
  },
22
22
  });
@@ -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(null, services, message, parsed.sessionId, parsed.choiceId);
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
  }
@@ -42,7 +42,8 @@ export default defineMiddleware<Message>({
42
42
  return;
43
43
  }
44
44
 
45
- const session = await services.getActiveForUser(ch, message.$sender.id);
45
+ // 不按 status 过滤取本频道本人最近一局:终局 session 也要能命中 restart 分支
46
+ const session = await services.getLatestForUser(ch, message.$sender.id);
46
47
  if (!session) {
47
48
  await next();
48
49
  return;
@@ -55,7 +56,7 @@ export default defineMiddleware<Message>({
55
56
  const payload = resolveGameTextPayload(raw, map);
56
57
  const parsed = payload ? parseChoicePayload(payload, BJ_PREFIX) : null;
57
58
  if (parsed?.choiceId === 'restart') {
58
- const reply = await handleChoice(null, services, message, parsed.sessionId, 'restart');
59
+ const reply = await handleChoice(services, message, parsed.sessionId, 'restart');
59
60
  if (reply) await context.input.$reply(reply);
60
61
  return;
61
62
  }
@@ -74,7 +75,7 @@ export default defineMiddleware<Message>({
74
75
  return;
75
76
  }
76
77
 
77
- const reply = await handleChoice(null, services, message, session.id, parsed.choiceId);
78
+ const reply = await handleChoice(services, message, session.id, parsed.choiceId);
78
79
  if (reply) await context.input.$reply(reply);
79
80
  },
80
81
  });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@zhin.js/plugin-blackjack",
3
- "version": "0.0.2",
3
+ "version": "0.0.4",
4
4
  "description": "Zhin.js 21 点(Blackjack)(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.1",
28
- "@zhin.js/core": "1.3.5",
29
- "@zhin.js/game-kit": "1.0.2",
30
- "@zhin.js/middleware": "1.0.1",
31
- "@zhin.js/plugin-runtime": "1.0.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/bj-command.ts CHANGED
@@ -1,4 +1,4 @@
1
- import type { Message, Plugin } from '@zhin.js/core';
1
+ import type { GameMessageLike } from '@zhin.js/game-kit';
2
2
  import { continueGame, startGame } from './game-flow.js';
3
3
  import type { SessionService } from './session-service.js';
4
4
 
@@ -13,23 +13,13 @@ export const BJ_HELP = [
13
13
  ].join('\n');
14
14
 
15
15
  export async function runBjCommand(
16
- plugin: Plugin | null,
17
16
  services: SessionService,
18
- message: Message<any>,
17
+ message: GameMessageLike,
19
18
  action: string,
20
- ): Promise<string | undefined> {
19
+ ): Promise<string> {
21
20
  const a = action.trim().toLowerCase() || 'help';
22
21
  if (a === 'help' || a === '帮助') return BJ_HELP;
23
- if (a === 'start' || a === '开始') return startGame(plugin, services, message);
24
- if (a === 'continue' || a === '继续') return continueGame(plugin, services, message);
22
+ if (a === 'start' || a === '开始') return (await startGame(services, message)) ?? '';
23
+ if (a === 'continue' || a === '继续') return continueGame(services, message);
25
24
  return `未知操作「${action}」。发送 \`/21点 帮助\`。`;
26
25
  }
27
-
28
- /** Plugin Runtime / smoke: text-only, no Adapter.editMessage. */
29
- export async function runBjCommandText(
30
- services: SessionService,
31
- message: Message<any>,
32
- action: string,
33
- ): Promise<string> {
34
- return (await runBjCommand(null, services, message, action)) ?? '';
35
- }
package/src/game-flow.ts CHANGED
@@ -1,5 +1,4 @@
1
- import type { Adapter, Message, Plugin } from '@zhin.js/core';
2
- import { plainTextFromSendContent, recordGameOutcome } from '@zhin.js/game-kit';
1
+ import { plainTextFromSendContent, recordGameOutcome, type GameMessageLike } from '@zhin.js/game-kit';
3
2
  import {
4
3
  BJ_PREFIX,
5
4
  compareHands,
@@ -12,51 +11,33 @@ import { parseCards, parseDeck, type SessionService } from './session-service.js
12
11
 
13
12
  import { buildBjView, naturalOutcome, playerBust } from './view.js';
14
13
 
15
- async function sendOrEditView(
16
- plugin: Plugin | null,
17
- services: SessionService,
18
- message: Message<any>,
14
+ /**
15
+ * Plugin Runtime: render the board as text. Interactive in-place board editing
16
+ * (the old Adapter.editMessage path) is not part of the runtime flow; commands
17
+ * and the choice middleware return fresh text each turn.
18
+ */
19
+ function renderView(
19
20
  session: BjSessionRow,
21
+ message: GameMessageLike,
20
22
  terminal = false,
21
23
  revealDealer = false,
22
- ): Promise<string | void> {
24
+ ): string {
23
25
  const content = buildBjView(session, terminal, revealDealer, message.$channel.type);
24
- if (!plugin) return plainTextFromSendContent(content);
25
-
26
- const adapter = plugin.root.inject(message.$adapter) as Adapter;
27
-
28
- if (session.board_message_id) {
29
- const msgId = await adapter.editMessage({
30
- messageId: session.board_message_id,
31
- context: String(message.$adapter),
32
- endpoint: message.$endpoint,
33
- id: message.$channel.id,
34
- type: message.$channel.type,
35
- content,
36
- });
37
- if (msgId !== session.board_message_id) {
38
- await services.updateSession(session.id, { board_message_id: msgId });
39
- }
40
- return;
41
- }
42
-
43
- const msgId = await message.$reply?.(content);
44
- if (msgId) await services.updateSession(session.id, { board_message_id: msgId });
26
+ return plainTextFromSendContent(content);
45
27
  }
46
28
 
47
29
  async function finishRound(
48
- plugin: Plugin | null,
49
30
  services: SessionService,
50
- message: Message<any>,
31
+ message: GameMessageLike,
51
32
  session: BjSessionRow,
52
33
  status: BjSessionRow['status'],
53
- ): Promise<string | void> {
34
+ ): Promise<string> {
54
35
  await services.updateSession(session.id, { status });
55
36
  const updated = (await services.getById(session.id))!;
56
37
  if (status === 'won') void recordGameOutcome(message, 'blackjack', 'won', 30);
57
38
  else if (status === 'lost') void recordGameOutcome(message, 'blackjack', 'lost');
58
39
  else if (status === 'draw') void recordGameOutcome(message, 'blackjack', 'draw');
59
- return sendOrEditView(plugin, services, message, updated, true, true);
40
+ return renderView(updated, message, true, true);
60
41
  }
61
42
 
62
43
  async function dealerPlay(
@@ -73,9 +54,8 @@ async function dealerPlay(
73
54
  }
74
55
 
75
56
  export async function startGame(
76
- plugin: Plugin | null,
77
57
  services: SessionService,
78
- message: Message<any>,
58
+ message: GameMessageLike,
79
59
  ): Promise<string | undefined> {
80
60
  const ch = `${message.$adapter}-${message.$endpoint}-${message.$channel.type}:${message.$channel.id}`;
81
61
  const active = await services.getActiveByChannel(ch);
@@ -88,32 +68,26 @@ export async function startGame(
88
68
  const session = await services.createSession(message);
89
69
  const natural = naturalOutcome(session);
90
70
  if (natural) {
91
- const text = await finishRound(plugin, services, message, session, natural);
92
- return typeof text === 'string' ? text : undefined;
71
+ return finishRound(services, message, session, natural);
93
72
  }
94
- const text = await sendOrEditView(plugin, services, message, session);
95
- return typeof text === 'string' ? text : undefined;
73
+ return renderView(session, message);
96
74
  }
97
75
 
98
76
  export async function continueGame(
99
- plugin: Plugin | null,
100
77
  services: SessionService,
101
- message: Message<any>,
78
+ message: GameMessageLike,
102
79
  ): Promise<string> {
103
80
  const session = await services.getActiveForUser(
104
81
  `${message.$adapter}-${message.$endpoint}-${message.$channel.type}:${message.$channel.id}`,
105
82
  message.$sender.id,
106
83
  );
107
84
  if (!session) return '你没有进行中的 21 点,发送「21点 开始」。';
108
- const text = await sendOrEditView(plugin, services, message, session);
109
- if (typeof text === 'string') return text;
110
- return '已刷新 21 点界面。';
85
+ return renderView(session, message);
111
86
  }
112
87
 
113
88
  export async function handleChoice(
114
- plugin: Plugin | null,
115
89
  services: SessionService,
116
- message: Message<any>,
90
+ message: GameMessageLike,
117
91
  sessionId: string,
118
92
  choiceId: string,
119
93
  ): Promise<string | null> {
@@ -123,8 +97,7 @@ export async function handleChoice(
123
97
 
124
98
  if (session.status !== 'active' && choiceId === 'restart') {
125
99
  await services.updateSession(session.id, { status: 'aborted' });
126
- // text-only 模式(plugin===null)下 startGame 的唯一输出就是返回文本
127
- return (await startGame(plugin, services, message)) ?? null;
100
+ return (await startGame(services, message)) ?? null;
128
101
  }
129
102
 
130
103
  if (session.status !== 'active') return '对局已结束,请点击再来一局。';
@@ -142,12 +115,10 @@ export async function handleChoice(
142
115
  player_cards_json: JSON.stringify(player),
143
116
  });
144
117
  if (playerBust({ ...session, player_cards_json: JSON.stringify(player) })) {
145
- const text = await finishRound(plugin, services, message, session, 'lost');
146
- return typeof text === 'string' ? text : null;
118
+ return finishRound(services, message, session, 'lost');
147
119
  }
148
120
  const updated = (await services.getById(session.id))!;
149
- const text = await sendOrEditView(plugin, services, message, updated);
150
- return typeof text === 'string' ? text : null;
121
+ return renderView(updated, message);
151
122
  }
152
123
 
153
124
  if (choiceId !== 'stand') return '无效操作。';
@@ -160,8 +131,7 @@ export async function handleChoice(
160
131
  const outcome = compareHands(player, dealer);
161
132
  const status: BjSessionRow['status'] =
162
133
  outcome === 'won' ? 'won' : outcome === 'lost' ? 'lost' : 'draw';
163
- const text = await finishRound(plugin, services, message, session, status);
164
- return typeof text === 'string' ? text : null;
134
+ return finishRound(services, message, session, status);
165
135
  }
166
136
 
167
137
  export { BJ_PREFIX, handValue, TARGET };
@@ -1,5 +1,5 @@
1
- import type { Database, Message, Models, RelatedModel } from '@zhin.js/core';
2
- import { channelKey, generateSessionId } from '@zhin.js/game-kit';
1
+ import type { Database, Models, RelatedModel } from '@zhin.js/core';
2
+ import { channelKey, generateSessionId, type GameMessageLike } from '@zhin.js/game-kit';
3
3
  import type { BjSessionRow } from './models.js';
4
4
  import { freshDeck } from './engine.js';
5
5
 
@@ -15,7 +15,7 @@ function getModel(db: BjDatabase): RelatedModel<unknown, Models, 'bj_sessions'>
15
15
  export class SessionService {
16
16
  constructor(private readonly db: BjDatabase) {}
17
17
 
18
- async createSession(message: Message<any>): Promise<BjSessionRow> {
18
+ async createSession(message: GameMessageLike): Promise<BjSessionRow> {
19
19
  const deck = freshDeck();
20
20
  const playerCards = [deck.pop()!, deck.pop()!];
21
21
  const dealerCards = [deck.pop()!, deck.pop()!];
@@ -54,6 +54,17 @@ export class SessionService {
54
54
  return rows[0] ?? null;
55
55
  }
56
56
 
57
+ /** 本频道本人最近一局(不按 status 过滤),供终局「回复 1 再来一局」restart 用。 */
58
+ async getLatestForUser(channelKeyValue: string, userId: string): Promise<BjSessionRow | null> {
59
+ const rows = await getModel(this.db).findAll({
60
+ channel_key: channelKeyValue,
61
+ player_id: userId,
62
+ });
63
+ if (rows.length === 0) return null;
64
+ // updated_at 可能同毫秒平局,此时取靠后的行(更接近最近创建的一局)
65
+ return rows.reduce((latest, row) => (row.updated_at >= latest.updated_at ? row : latest));
66
+ }
67
+
57
68
  async getActiveByChannel(channelKeyValue: string): Promise<BjSessionRow | null> {
58
69
  const rows = await getModel(this.db).findAll({
59
70
  channel_key: channelKeyValue,