@zhin.js/plugin-tic-tac-toe 1.0.0 → 1.0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (60) hide show
  1. package/commands/ttt/[action:string=].ts +15 -0
  2. package/lib/board-view.d.ts +3 -3
  3. package/lib/board-view.js +6 -2
  4. package/lib/board-view.js.map +1 -1
  5. package/lib/engine.d.ts +0 -1
  6. package/lib/game-flow.d.ts +6 -6
  7. package/lib/game-flow.js +61 -12
  8. package/lib/game-flow.js.map +1 -1
  9. package/lib/index.d.ts +8 -2
  10. package/lib/index.js +6 -34
  11. package/lib/index.js.map +1 -1
  12. package/lib/memory-db.d.ts +6 -0
  13. package/lib/memory-db.js +18 -0
  14. package/lib/memory-db.js.map +1 -0
  15. package/lib/models.d.ts +5 -4
  16. package/lib/models.js +5 -5
  17. package/lib/models.js.map +1 -1
  18. package/lib/player-label.d.ts +1 -2
  19. package/lib/runtime-store.d.ts +6 -0
  20. package/lib/runtime-store.js +7 -0
  21. package/lib/runtime-store.js.map +1 -0
  22. package/lib/session-service.d.ts +1 -3
  23. package/lib/session-service.js +4 -15
  24. package/lib/session-service.js.map +1 -1
  25. package/lib/ttt-command.d.ts +4 -3
  26. package/lib/ttt-command.js +8 -3
  27. package/lib/ttt-command.js.map +1 -1
  28. package/middlewares/ttt-alias.ts +18 -0
  29. package/middlewares/ttt-choice.ts +71 -0
  30. package/package.json +36 -18
  31. package/plugin.ts +64 -0
  32. package/schema.json +6 -0
  33. package/src/board-view.ts +8 -3
  34. package/src/game-flow.ts +71 -21
  35. package/src/index.ts +7 -41
  36. package/src/memory-db.ts +22 -0
  37. package/src/models.ts +10 -10
  38. package/src/player-label.ts +1 -1
  39. package/src/runtime-store.ts +9 -0
  40. package/src/session-service.ts +5 -13
  41. package/src/ttt-command.ts +15 -5
  42. package/lib/board-view.d.ts.map +0 -1
  43. package/lib/commands.d.ts +0 -6
  44. package/lib/commands.d.ts.map +0 -1
  45. package/lib/commands.js +0 -111
  46. package/lib/commands.js.map +0 -1
  47. package/lib/engine.d.ts.map +0 -1
  48. package/lib/game-flow.d.ts.map +0 -1
  49. package/lib/hub-register.d.ts +0 -5
  50. package/lib/hub-register.d.ts.map +0 -1
  51. package/lib/hub-register.js +0 -30
  52. package/lib/hub-register.js.map +0 -1
  53. package/lib/index.d.ts.map +0 -1
  54. package/lib/models.d.ts.map +0 -1
  55. package/lib/player-label.d.ts.map +0 -1
  56. package/lib/session-service.d.ts.map +0 -1
  57. package/lib/ttt-command.d.ts.map +0 -1
  58. package/plugin.yml +0 -2
  59. package/src/commands.ts +0 -129
  60. package/src/hub-register.ts +0 -31
@@ -1,5 +1,5 @@
1
- import type { Message, Plugin } from 'zhin.js';
2
- import { channelKey } from '@zhin.js/game-shared';
1
+ import type { Message, Plugin } from '@zhin.js/core';
2
+ import { channelKey } from '@zhin.js/game-kit';
3
3
  import { formatPlayerWithMark, formatRosterLine } from './player-label.js';
4
4
  import { startBotGame, startPvpGame } from './game-flow.js';
5
5
  import type { SessionServices } from './session-service.js';
@@ -15,7 +15,7 @@ export const TTT_HELP = [
15
15
  ].join('\n');
16
16
 
17
17
  export async function runTttCommand(
18
- plugin: Plugin,
18
+ plugin: Plugin | null,
19
19
  services: SessionServices,
20
20
  message: Message<any>,
21
21
  action: string,
@@ -43,8 +43,9 @@ export async function runTttCommand(
43
43
  const pair = await services.queue.tryMatch(ch);
44
44
  if (pair) {
45
45
  const [px, po] = pair;
46
- await startPvpGame(plugin, services, message, px, po);
47
- return `匹配成功!${formatPlayerWithMark(px.id, px.displayName, '✕')} vs ${formatPlayerWithMark(po.id, po.displayName, '○')}`;
46
+ const board = await startPvpGame(plugin, services, message, px, po);
47
+ const matchLine = `匹配成功!${formatPlayerWithMark(px.id, px.displayName, '✕')} vs ${formatPlayerWithMark(po.id, po.displayName, '○')}`;
48
+ return board ? `${matchLine}\n\n${board}` : matchLine;
48
49
  }
49
50
  return `已加入排队(第 ${position} 位),凑满 2 人自动开局。`;
50
51
  }
@@ -74,3 +75,12 @@ export async function runTttCommand(
74
75
 
75
76
  return `未知子命令:${action}\n\n${TTT_HELP}`;
76
77
  }
78
+
79
+ /** Plugin Runtime / smoke: text-only, no Adapter.editMessage. */
80
+ export async function runTttCommandText(
81
+ services: SessionServices,
82
+ message: Message<any>,
83
+ action: string,
84
+ ): Promise<string> {
85
+ return (await runTttCommand(null, services, message, action)) ?? '';
86
+ }
@@ -1 +0,0 @@
1
- {"version":3,"file":"board-view.d.ts","sourceRoot":"","sources":["../src/board-view.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAW,WAAW,EAAE,MAAM,SAAS,CAAC;AACpD,OAAO,EAKL,UAAU,EAEX,MAAM,sBAAsB,CAAC;AAC9B,OAAO,EAGL,KAAK,KAAK,EACV,KAAK,IAAI,EAIV,MAAM,aAAa,CAAC;AAErB,uBAAuB;AACvB,eAAO,MAAM,UAAU,QAAQ,CAAC;AAEhC,OAAO,EAAE,UAAU,EAAE,CAAC;AAwBtB,wBAAgB,qBAAqB,CAAC,OAAO,EAAE;IAC7C,SAAS,EAAE,MAAM,CAAC;IAClB,KAAK,EAAE,KAAK,CAAC;IACb,UAAU,EAAE,MAAM,CAAC;IACnB,QAAQ,EAAE,IAAI,CAAC;IACf,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,cAAc,CAAC,EAAE,OAAO,CAAC;IACzB,SAAS,CAAC,EAAE,MAAM,EAAE,CAAC;CACtB,GAAG,WAAW,CAgBd;AAED,wBAAgB,gBAAgB,CAAC,SAAS,EAAE,MAAM,EAAE,KAAK,EAAE,KAAK,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAExF;AAED,wBAAgB,eAAe,CAAC,OAAO,EAAE,MAAM,GAAG;IAAE,SAAS,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,GAAG,IAAI,CAK3F;AAED,sDAAsD;AACtD,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAI9D;AAED,wBAAgB,QAAQ,CAAC,IAAI,EAAE,IAAI,GAAG,MAAM,CAE3C"}
package/lib/commands.d.ts DELETED
@@ -1,6 +0,0 @@
1
- import { type Plugin } from 'zhin.js';
2
- import type { SessionServices } from './session-service.js';
3
- export declare function registerCommands(plugin: Plugin, getServices: () => SessionServices | null): void;
4
- export declare function registerInteractive(plugin: Plugin, getServices: () => SessionServices | null): void;
5
- export declare function registerTextFallback(plugin: Plugin, getServices: () => SessionServices | null): void;
6
- //# sourceMappingURL=commands.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"commands.d.ts","sourceRoot":"","sources":["../src/commands.ts"],"names":[],"mappings":"AAAA,OAAO,EAAiD,KAAK,MAAM,EAAE,MAAM,SAAS,CAAC;AAMrF,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,sBAAsB,CAAC;AAgE5D,wBAAgB,gBAAgB,CAC9B,MAAM,EAAE,MAAM,EACd,WAAW,EAAE,MAAM,eAAe,GAAG,IAAI,GACxC,IAAI,CAGN;AAgBD,wBAAgB,mBAAmB,CAAC,MAAM,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,eAAe,GAAG,IAAI,GAAG,IAAI,CAGnG;AAED,wBAAgB,oBAAoB,CAAC,MAAM,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,eAAe,GAAG,IAAI,GAAG,IAAI,CA+BpG"}
package/lib/commands.js DELETED
@@ -1,111 +0,0 @@
1
- import { MessageCommand, getActionFromMessage } from 'zhin.js';
2
- import { channelKey, normalizeTttAction } from '@zhin.js/game-shared';
3
- import { buildFallbackMap } from './board-view.js';
4
- import { parseBoard } from './engine.js';
5
- import { handleMove } from './game-flow.js';
6
- import { runTttCommand } from './ttt-command.js';
7
- function actionPayload(message) {
8
- return getActionFromMessage(message)?.payload;
9
- }
10
- /** @deprecated 使用 game-shared parseChoicePayload;保留 ttt 专用解析 */
11
- function parseTttPayload(payload) {
12
- const m = /^ttt:([^:]+):(\d)$/.exec(payload);
13
- if (!m)
14
- return null;
15
- return { sessionId: m[1], cell: Number(m[2]) };
16
- }
17
- function parseCellButtonId(id) {
18
- const m = /^c(\d)$/.exec(id);
19
- if (!m)
20
- return null;
21
- const cell = Number(m[1]);
22
- return cell >= 0 && cell <= 8 ? cell : null;
23
- }
24
- async function resolveTttMove(message, services) {
25
- const action = getActionFromMessage(message);
26
- if (!action)
27
- return null;
28
- const fromPayload = parseTttPayload(action.payload);
29
- if (fromPayload)
30
- return fromPayload;
31
- const cell = parseCellButtonId(action.payload)
32
- ?? parseCellButtonId(action.id ?? '');
33
- if (cell == null)
34
- return null;
35
- const ch = channelKey(message);
36
- const session = await services.session.getActiveForUser(ch, message.$sender.id)
37
- ?? (action.sourceMessageId
38
- ? await services.session.getActiveByBoardMessageId(action.sourceMessageId)
39
- : null);
40
- if (!session || session.channel_key !== ch)
41
- return null;
42
- return { sessionId: session.id, cell };
43
- }
44
- function registerTttPattern(plugin, pattern, desc, getServices) {
45
- plugin.addCommand(new MessageCommand(pattern)
46
- .desc(desc)
47
- .action(async (message, result) => {
48
- const services = getServices();
49
- if (!services)
50
- return '井字棋需要启用 database 配置。';
51
- const raw = result.params.action ?? '';
52
- const action = normalizeTttAction(raw);
53
- return runTttCommand(plugin, services, message, action);
54
- }));
55
- }
56
- export function registerCommands(plugin, getServices) {
57
- registerTttPattern(plugin, 'ttt [action:word]', '井字棋(ttt)', getServices);
58
- registerTttPattern(plugin, '井字棋 [action:word]', '井字棋(中文)', getServices);
59
- }
60
- async function handleTttAction(plugin, getServices, message) {
61
- const services = getServices();
62
- if (!services)
63
- return false;
64
- const move = await resolveTttMove(message, services);
65
- if (!move)
66
- return false;
67
- const err = await handleMove(plugin, services, message, move.sessionId, move.cell);
68
- if (err)
69
- await message.$reply?.(err);
70
- return true;
71
- }
72
- export function registerInteractive(plugin, getServices) {
73
- plugin.registerInteractiveHandler('ttt:', (message) => handleTttAction(plugin, getServices, message));
74
- plugin.registerInteractiveHandler('c', (message) => handleTttAction(plugin, getServices, message));
75
- }
76
- export function registerTextFallback(plugin, getServices) {
77
- plugin.addMiddleware(async (message, next) => {
78
- const services = getServices();
79
- if (!services)
80
- return next();
81
- const inboundAction = actionPayload(message);
82
- if (inboundAction?.startsWith('ttt:'))
83
- return next();
84
- const ch = channelKey(message);
85
- const session = await services.session.getActiveForUser(ch, message.$sender.id);
86
- if (!session)
87
- return next();
88
- const raw = message.$raw?.trim() ?? '';
89
- let cell = null;
90
- const direct = parseTttPayload(raw.startsWith('ttt:') ? raw : '');
91
- if (direct && direct.sessionId === session.id) {
92
- cell = direct.cell;
93
- }
94
- else {
95
- const n = /^(\d)$/.exec(raw);
96
- if (n) {
97
- const map = buildFallbackMap(session.id, parseBoard(session.board));
98
- const payload = map[n[1]];
99
- const p = payload ? parseTttPayload(payload) : null;
100
- if (p?.sessionId === session.id)
101
- cell = p.cell;
102
- }
103
- }
104
- if (cell == null)
105
- return next();
106
- const err = await handleMove(plugin, services, message, session.id, cell);
107
- if (err)
108
- await message.$reply?.(err);
109
- });
110
- }
111
- //# sourceMappingURL=commands.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"commands.js","sourceRoot":"","sources":["../src/commands.ts"],"names":[],"mappings":"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"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"engine.d.ts","sourceRoot":"","sources":["../src/engine.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,KAAK,IAAI,CAAC;AACvB,eAAO,MAAM,CAAC,IAAI,CAAC;AACnB,eAAO,MAAM,CAAC,IAAI,CAAC;AAEnB,MAAM,MAAM,IAAI,GAAG,OAAO,KAAK,GAAG,OAAO,CAAC,GAAG,OAAO,CAAC,CAAC;AACtD,MAAM,MAAM,KAAK,GAAG,IAAI,EAAE,CAAC;AAE3B,MAAM,MAAM,UAAU,GAAG,QAAQ,GAAG,KAAK,GAAG,MAAM,GAAG,SAAS,CAAC;AAQ/D,wBAAgB,UAAU,IAAI,KAAK,CAElC;AAED,wBAAgB,UAAU,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,EAAE,GAAG,IAAI,GAAG,SAAS,GAAG,KAAK,CAa3E;AAED,wBAAgB,SAAS,CAAC,IAAI,EAAE,IAAI,GAAG,MAAM,CAI5C;AAED,wBAAgB,UAAU,CAAC,KAAK,EAAE,KAAK,EAAE,SAAS,CAAC,EAAE,MAAM,EAAE,GAAG,MAAM,CAYrE;AAED,wBAAgB,WAAW,CAAC,KAAK,EAAE,KAAK,GAAG;IAAE,MAAM,EAAE,IAAI,CAAC;IAAC,IAAI,EAAE,MAAM,EAAE,CAAA;CAAE,GAAG,IAAI,CASjF;AAED,wBAAgB,MAAM,CAAC,KAAK,EAAE,KAAK,GAAG,OAAO,CAE5C;AAED,wBAAgB,SAAS,CAAC,KAAK,EAAE,KAAK,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAE7D;AAED,wBAAgB,SAAS,CAAC,KAAK,EAAE,KAAK,EAAE,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,IAAI,GAAG,KAAK,CAKzE;AAED,wBAAgB,QAAQ,CAAC,MAAM,EAAE,IAAI,GAAG,IAAI,CAE3C;AAED,iEAAiE;AACjE,wBAAgB,QAAQ,CAAC,KAAK,EAAE,KAAK,EAAE,EAAE,GAAE,IAAQ,GAAG,MAAM,CAe3D;AA0BD,wBAAgB,UAAU,CAAC,QAAQ,EAAE,MAAM,EAAE,OAAO,EAAE;IAAE,OAAO,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE,GAAG,IAAI,GAAG,IAAI,CAIvG"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"game-flow.d.ts","sourceRoot":"","sources":["../src/game-flow.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAW,OAAO,EAAE,MAAM,EAAE,MAAM,SAAS,CAAC;AACxD,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAejD,OAAO,EACL,MAAM,EAOP,MAAM,mBAAmB,CAAC;AAC3B,OAAO,KAAK,EAAE,eAAe,EAAE,YAAY,EAAE,MAAM,sBAAsB,CAAC;AAE1E,OAAO,EAAE,MAAM,EAAE,CAAC;AAElB,wBAAgB,YAAY,CAAC,OAAO,EAAE,aAAa,GAAG,OAAO,CAE5D;AAED,wBAAsB,eAAe,CACnC,MAAM,EAAE,MAAM,EACd,QAAQ,EAAE,eAAe,EACzB,OAAO,EAAE,OAAO,CAAC,GAAG,CAAC,EACrB,OAAO,EAAE,aAAa,EACtB,UAAU,EAAE,MAAM,EAClB,QAAQ,UAAQ,EAChB,SAAS,CAAC,EAAE,MAAM,EAAE,GACnB,OAAO,CAAC,MAAM,CAAC,CAkCjB;AAUD,wBAAsB,UAAU,CAC9B,MAAM,EAAE,MAAM,EACd,QAAQ,EAAE,eAAe,EACzB,OAAO,EAAE,OAAO,CAAC,GAAG,CAAC,EACrB,SAAS,EAAE,MAAM,EACjB,IAAI,EAAE,MAAM,GACX,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CA+DxB;AAqBD,wBAAsB,YAAY,CAChC,MAAM,EAAE,MAAM,EACd,QAAQ,EAAE,eAAe,EACzB,OAAO,EAAE,OAAO,CAAC,GAAG,CAAC,GACpB,OAAO,CAAC,MAAM,CAAC,CAiBjB;AAED,wBAAsB,YAAY,CAChC,MAAM,EAAE,MAAM,EACd,QAAQ,EAAE,eAAe,EACzB,OAAO,EAAE,OAAO,CAAC,GAAG,CAAC,EACrB,OAAO,EAAE,YAAY,EACrB,OAAO,EAAE,YAAY,GACpB,OAAO,CAAC,IAAI,CAAC,CAYf"}
@@ -1,5 +0,0 @@
1
- import { TTT_HELP } from './ttt-command.js';
2
- import type { SessionServices } from './session-service.js';
3
- export declare function registerTttHub(getServices: () => SessionServices | null): () => void;
4
- export { TTT_HELP };
5
- //# sourceMappingURL=hub-register.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"hub-register.d.ts","sourceRoot":"","sources":["../src/hub-register.ts"],"names":[],"mappings":"AAEA,OAAO,EAAiB,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AAC3D,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,sBAAsB,CAAC;AAE5D,wBAAgB,cAAc,CAAC,WAAW,EAAE,MAAM,eAAe,GAAG,IAAI,GAAG,MAAM,IAAI,CAuBpF;AAED,OAAO,EAAE,QAAQ,EAAE,CAAC"}
@@ -1,30 +0,0 @@
1
- import { getPlugin } from 'zhin.js';
2
- import { ensureGameHubService } from '@zhin.js/game-shared';
3
- import { runTttCommand, TTT_HELP } from './ttt-command.js';
4
- export function registerTttHub(getServices) {
5
- const plugin = getPlugin();
6
- ensureGameHubService(plugin);
7
- return plugin.registerGame({
8
- id: 'ttt',
9
- title: '井字棋',
10
- icon: '♟️',
11
- description: '三子连珠,群聊排队或人机对战',
12
- commandPrefix: '井字棋',
13
- quickStart: '人机',
14
- aliases: ['ttt'],
15
- menus: [
16
- { id: 'bot', label: '🤖 人机对战', style: 'primary' },
17
- { id: 'join', label: '👥 加入排队', groupOnly: true },
18
- { id: 'spectate', label: '👀 观战', groupOnly: true },
19
- { id: 'help', label: '📖 玩法说明' },
20
- ],
21
- runAction: async (actionId, ctx) => {
22
- const services = getServices();
23
- if (!services)
24
- return '井字棋需要启用 database 配置。';
25
- return runTttCommand(ctx.plugin, services, ctx.message, actionId);
26
- },
27
- });
28
- }
29
- export { TTT_HELP };
30
- //# sourceMappingURL=hub-register.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"hub-register.js","sourceRoot":"","sources":["../src/hub-register.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,SAAS,CAAC;AACpC,OAAO,EAAE,oBAAoB,EAAE,MAAM,sBAAsB,CAAC;AAC5D,OAAO,EAAE,aAAa,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AAG3D,MAAM,UAAU,cAAc,CAAC,WAAyC;IACtE,MAAM,MAAM,GAAG,SAAS,EAAE,CAAC;IAC3B,oBAAoB,CAAC,MAAM,CAAC,CAAC;IAC7B,OAAO,MAAM,CAAC,YAAY,CAAC;QACzB,EAAE,EAAE,KAAK;QACT,KAAK,EAAE,KAAK;QACZ,IAAI,EAAE,IAAI;QACV,WAAW,EAAE,gBAAgB;QAC7B,aAAa,EAAE,KAAK;QACpB,UAAU,EAAE,IAAI;QAChB,OAAO,EAAE,CAAC,KAAK,CAAC;QAChB,KAAK,EAAE;YACL,EAAE,EAAE,EAAE,KAAK,EAAE,KAAK,EAAE,SAAS,EAAE,KAAK,EAAE,SAAS,EAAE;YACjD,EAAE,EAAE,EAAE,MAAM,EAAE,KAAK,EAAE,SAAS,EAAE,SAAS,EAAE,IAAI,EAAE;YACjD,EAAE,EAAE,EAAE,UAAU,EAAE,KAAK,EAAE,OAAO,EAAE,SAAS,EAAE,IAAI,EAAE;YACnD,EAAE,EAAE,EAAE,MAAM,EAAE,KAAK,EAAE,SAAS,EAAE;SACjC;QACD,SAAS,EAAE,KAAK,EAAE,QAAQ,EAAE,GAAG,EAAE,EAAE;YACjC,MAAM,QAAQ,GAAG,WAAW,EAAE,CAAC;YAC/B,IAAI,CAAC,QAAQ;gBAAE,OAAO,sBAAsB,CAAC;YAC7C,OAAO,aAAa,CAAC,GAAG,CAAC,MAAM,EAAE,QAAQ,EAAE,GAAG,CAAC,OAAO,EAAE,QAAQ,CAAC,CAAC;QACpE,CAAC;KACF,CAAC,CAAC;AACL,CAAC;AAED,OAAO,EAAE,QAAQ,EAAE,CAAC"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":""}
@@ -1 +0,0 @@
1
- {"version":3,"file":"models.d.ts","sourceRoot":"","sources":["../src/models.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,SAAS,CAAC;AAE9C,MAAM,MAAM,gBAAgB,GAAG,QAAQ,GAAG,KAAK,GAAG,MAAM,GAAG,SAAS,CAAC;AAErE,OAAO,QAAQ,SAAS,CAAC;IACvB,UAAU,MAAM;QACd,YAAY,EAAE;YACZ,EAAE,EAAE,MAAM,CAAC;YACX,OAAO,EAAE,MAAM,CAAC;YAChB,QAAQ,EAAE,MAAM,CAAC;YACjB,YAAY,EAAE,MAAM,CAAC;YACrB,UAAU,EAAE,MAAM,CAAC;YACnB,WAAW,EAAE,MAAM,CAAC;YACpB,QAAQ,EAAE,MAAM,CAAC;YACjB,QAAQ,EAAE,MAAM,CAAC;YACjB,aAAa,EAAE,MAAM,CAAC;YACtB,aAAa,EAAE,MAAM,CAAC;YACtB,KAAK,EAAE,MAAM,CAAC;YACd,IAAI,EAAE,MAAM,CAAC;YACb,MAAM,EAAE,gBAAgB,CAAC;YACzB,MAAM,EAAE,MAAM,CAAC;YACf,gBAAgB,EAAE,MAAM,CAAC;YACzB,UAAU,EAAE,MAAM,CAAC;YACnB,UAAU,EAAE,MAAM,CAAC;YACnB,UAAU,EAAE,MAAM,CAAC;SACpB,CAAC;QACF,SAAS,EAAE;YACT,EAAE,EAAE,MAAM,CAAC;YACX,WAAW,EAAE,MAAM,CAAC;YACpB,OAAO,EAAE,MAAM,CAAC;YAChB,SAAS,EAAE,MAAM,CAAC;YAClB,SAAS,EAAE,MAAM,CAAC;SACnB,CAAC;QACF,SAAS,EAAE;YACT,EAAE,EAAE,MAAM,CAAC;YACX,UAAU,EAAE,MAAM,CAAC;YACnB,SAAS,EAAE,MAAM,CAAC;YAClB,IAAI,EAAE,MAAM,CAAC;YACb,UAAU,EAAE,MAAM,CAAC;YACnB,UAAU,EAAE,MAAM,CAAC;SACpB,CAAC;QACF,cAAc,EAAE;YACd,EAAE,EAAE,MAAM,CAAC;YACX,UAAU,EAAE,MAAM,CAAC;YACnB,OAAO,EAAE,MAAM,CAAC;YAChB,SAAS,EAAE,MAAM,CAAC;SACnB,CAAC;KACH;CACF;AAED,MAAM,MAAM,aAAa,GAAG,MAAM,CAAC,cAAc,CAAC,CAAC;AACnD,MAAM,MAAM,WAAW,GAAG,MAAM,CAAC,WAAW,CAAC,CAAC;AAC9C,MAAM,MAAM,UAAU,GAAG,MAAM,CAAC,WAAW,CAAC,CAAC;AAC7C,MAAM,MAAM,eAAe,GAAG,MAAM,CAAC,gBAAgB,CAAC,CAAC;AAEvD,iBAAiB;AACjB,MAAM,MAAM,YAAY,GAAG,cAAc,GAAG,WAAW,GAAG,WAAW,GAAG,gBAAgB,CAAC;AAEzF,wBAAgB,cAAc,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI,CA6CnD"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"player-label.d.ts","sourceRoot":"","sources":["../src/player-label.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,SAAS,CAAC;AAC7C,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AACjD,OAAO,EAAQ,KAAK,IAAI,EAAa,MAAM,aAAa,CAAC;AAEzD,eAAO,MAAM,MAAM,gBAAgB,CAAC;AAEpC,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,IAAI,CAAC,aAAa,EAAE,IAAI,GAAG,MAAM,CAAC,GAAG,MAAM,CAGpF;AAED,wBAAgB,iBAAiB,CAAC,QAAQ,EAAE,MAAM,EAAE,UAAU,CAAC,EAAE,MAAM,GAAG,MAAM,CAI/E;AAED,wBAAgB,oBAAoB,CAClC,QAAQ,EAAE,MAAM,EAChB,UAAU,EAAE,MAAM,GAAG,SAAS,EAC9B,IAAI,EAAE,GAAG,GAAG,GAAG,GACd,MAAM,CAER;AAED,wBAAgB,gBAAgB,CAAC,OAAO,EAAE,aAAa,GAAG,MAAM,CAE/D;AAED,wBAAgB,gBAAgB,CAAC,OAAO,EAAE,aAAa,EAAE,IAAI,EAAE,IAAI,GAAG;IAAE,EAAE,EAAE,MAAM,CAAC;IAAC,IAAI,CAAC,EAAE,MAAM,CAAA;CAAE,CAGlG;AAED,wBAAgB,gBAAgB,CAAC,OAAO,EAAE,aAAa,EAAE,SAAS,EAAE,MAAM,GAAG,MAAM,CAIlF;AAED,wBAAgB,iBAAiB,CAAC,OAAO,EAAE,aAAa,EAAE,MAAM,EAAE,IAAI,GAAG,MAAM,CAG9E;AAED,wBAAgB,eAAe,CAAC,OAAO,EAAE,aAAa,EAAE,MAAM,EAAE,IAAI,EAAE,UAAU,EAAE,MAAM,GAAG,MAAM,CAEhG"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"session-service.d.ts","sourceRoot":"","sources":["../src/session-service.ts"],"names":[],"mappings":"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"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"ttt-command.d.ts","sourceRoot":"","sources":["../src/ttt-command.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,EAAE,MAAM,SAAS,CAAC;AAI/C,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,sBAAsB,CAAC;AAE5D,eAAO,MAAM,QAAQ,QAQT,CAAC;AAEb,wBAAsB,aAAa,CACjC,MAAM,EAAE,MAAM,EACd,QAAQ,EAAE,eAAe,EACzB,OAAO,EAAE,OAAO,CAAC,GAAG,CAAC,EACrB,MAAM,EAAE,MAAM,GACb,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC,CAsD7B"}
package/plugin.yml DELETED
@@ -1,2 +0,0 @@
1
- name: "@zhin.js/plugin-tic-tac-toe"
2
- description: "跨平台井字棋游戏"
package/src/commands.ts DELETED
@@ -1,129 +0,0 @@
1
- import { Message, MessageCommand, getActionFromMessage, type Plugin } from 'zhin.js';
2
- import { channelKey, normalizeTttAction, registerGameTextMiddleware } from '@zhin.js/game-shared';
3
- import { buildFallbackMap } from './board-view.js';
4
- import { parseBoard } from './engine.js';
5
- import { handleMove } from './game-flow.js';
6
- import { runTttCommand } from './ttt-command.js';
7
- import type { SessionServices } from './session-service.js';
8
-
9
- function actionPayload(message: Message<any>): string | undefined {
10
- return getActionFromMessage(message)?.payload;
11
- }
12
-
13
- /** @deprecated 使用 game-shared parseChoicePayload;保留 ttt 专用解析 */
14
- function parseTttPayload(payload: string): { sessionId: string; cell: number } | null {
15
- const m = /^ttt:([^:]+):(\d)$/.exec(payload);
16
- if (!m) return null;
17
- return { sessionId: m[1]!, cell: Number(m[2]) };
18
- }
19
-
20
- function parseCellButtonId(id: string): number | null {
21
- const m = /^c(\d)$/.exec(id);
22
- if (!m) return null;
23
- const cell = Number(m[1]);
24
- return cell >= 0 && cell <= 8 ? cell : null;
25
- }
26
-
27
- async function resolveTttMove(
28
- message: Message<any>,
29
- services: SessionServices,
30
- ): Promise<{ sessionId: string; cell: number } | null> {
31
- const action = getActionFromMessage(message);
32
- if (!action) return null;
33
-
34
- const fromPayload = parseTttPayload(action.payload);
35
- if (fromPayload) return fromPayload;
36
-
37
- const cell =
38
- parseCellButtonId(action.payload)
39
- ?? parseCellButtonId(action.id ?? '');
40
- if (cell == null) return null;
41
-
42
- const ch = channelKey(message);
43
- const session =
44
- await services.session.getActiveForUser(ch, message.$sender.id)
45
- ?? (action.sourceMessageId
46
- ? await services.session.getActiveByBoardMessageId(action.sourceMessageId)
47
- : null);
48
- if (!session || session.channel_key !== ch) return null;
49
- return { sessionId: session.id, cell };
50
- }
51
-
52
- function registerTttPattern(
53
- plugin: Plugin,
54
- pattern: string,
55
- desc: string,
56
- getServices: () => SessionServices | null,
57
- ): void {
58
- plugin.addCommand(
59
- new MessageCommand(pattern)
60
- .desc(desc)
61
- .action(async (message, result) => {
62
- const services = getServices();
63
- if (!services) return '井字棋需要启用 database 配置。';
64
- const raw = (result.params.action as string | undefined) ?? '';
65
- const action = normalizeTttAction(raw);
66
- return runTttCommand(plugin, services, message, action);
67
- }),
68
- );
69
- }
70
-
71
- export function registerCommands(
72
- plugin: Plugin,
73
- getServices: () => SessionServices | null,
74
- ): void {
75
- registerTttPattern(plugin, 'ttt [action:word]', '井字棋(ttt)', getServices);
76
- registerTttPattern(plugin, '井字棋 [action:word]', '井字棋(中文)', getServices);
77
- }
78
-
79
- async function handleTttAction(
80
- plugin: Plugin,
81
- getServices: () => SessionServices | null,
82
- message: Message<any>,
83
- ): Promise<boolean> {
84
- const services = getServices();
85
- if (!services) return false;
86
- const move = await resolveTttMove(message, services);
87
- if (!move) return false;
88
- const err = await handleMove(plugin, services, message, move.sessionId, move.cell);
89
- if (err) await message.$reply?.(err);
90
- return true;
91
- }
92
-
93
- export function registerInteractive(plugin: Plugin, getServices: () => SessionServices | null): void {
94
- plugin.registerInteractiveHandler('ttt:', (message) => handleTttAction(plugin, getServices, message));
95
- plugin.registerInteractiveHandler('c', (message) => handleTttAction(plugin, getServices, message));
96
- }
97
-
98
- export function registerTextFallback(plugin: Plugin, getServices: () => SessionServices | null): void {
99
- registerGameTextMiddleware(plugin, async (message, next) => {
100
- const services = getServices();
101
- if (!services) return next();
102
-
103
- const inboundAction = actionPayload(message);
104
- if (inboundAction?.startsWith('ttt:')) return next();
105
-
106
- const ch = channelKey(message);
107
- const session = await services.session.getActiveForUser(ch, message.$sender.id);
108
- if (!session) return next();
109
-
110
- const raw = message.$raw?.trim() ?? '';
111
- let cell: number | null = null;
112
- const direct = parseTttPayload(raw.startsWith('ttt:') ? raw : '');
113
- if (direct && direct.sessionId === session.id) {
114
- cell = direct.cell;
115
- } else {
116
- const n = /^(\d)$/.exec(raw);
117
- if (n) {
118
- const map = buildFallbackMap(session.id, parseBoard(session.board));
119
- const payload = map[n[1]!];
120
- const p = payload ? parseTttPayload(payload) : null;
121
- if (p?.sessionId === session.id) cell = p.cell;
122
- }
123
- }
124
- if (cell == null) return next();
125
-
126
- const err = await handleMove(plugin, services, message, session.id, cell);
127
- if (err) await message.$reply?.(err);
128
- }, 'ttt:text');
129
- }
@@ -1,31 +0,0 @@
1
- import { getPlugin } from 'zhin.js';
2
- import { ensureGameHubService } from '@zhin.js/game-shared';
3
- import { runTttCommand, TTT_HELP } from './ttt-command.js';
4
- import type { SessionServices } from './session-service.js';
5
-
6
- export function registerTttHub(getServices: () => SessionServices | null): () => void {
7
- const plugin = getPlugin();
8
- ensureGameHubService(plugin);
9
- return plugin.registerGame({
10
- id: 'ttt',
11
- title: '井字棋',
12
- icon: '♟️',
13
- description: '三子连珠,群聊排队或人机对战',
14
- commandPrefix: '井字棋',
15
- quickStart: '人机',
16
- aliases: ['ttt'],
17
- menus: [
18
- { id: 'bot', label: '🤖 人机对战', style: 'primary' },
19
- { id: 'join', label: '👥 加入排队', groupOnly: true },
20
- { id: 'spectate', label: '👀 观战', groupOnly: true },
21
- { id: 'help', label: '📖 玩法说明' },
22
- ],
23
- runAction: async (actionId, ctx) => {
24
- const services = getServices();
25
- if (!services) return '井字棋需要启用 database 配置。';
26
- return runTttCommand(ctx.plugin, services, ctx.message, actionId);
27
- },
28
- });
29
- }
30
-
31
- export { TTT_HELP };