@zhin.js/plugin-rps 1.0.1 → 1.0.3

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/rps/[action:string=].ts +15 -0
  2. package/lib/engine.d.ts +0 -1
  3. package/lib/engine.js +1 -1
  4. package/lib/engine.js.map +1 -1
  5. package/lib/game-flow.d.ts +6 -7
  6. package/lib/game-flow.js +12 -8
  7. package/lib/game-flow.js.map +1 -1
  8. package/lib/index.d.ts +8 -2
  9. package/lib/index.js +8 -25
  10. package/lib/index.js.map +1 -1
  11. package/lib/memory-db.d.ts +6 -0
  12. package/lib/memory-db.js +18 -0
  13. package/lib/memory-db.js.map +1 -0
  14. package/lib/models.d.ts +5 -4
  15. package/lib/models.js +2 -2
  16. package/lib/models.js.map +1 -1
  17. package/lib/rps-command.d.ts +4 -3
  18. package/lib/rps-command.js +5 -1
  19. package/lib/rps-command.js.map +1 -1
  20. package/lib/runtime-store.d.ts +6 -0
  21. package/lib/runtime-store.js +7 -0
  22. package/lib/runtime-store.js.map +1 -0
  23. package/lib/session-service.d.ts +1 -3
  24. package/lib/session-service.js +1 -4
  25. package/lib/session-service.js.map +1 -1
  26. package/lib/view.d.ts +1 -2
  27. package/lib/view.js +1 -1
  28. package/lib/view.js.map +1 -1
  29. package/middlewares/rps-alias.ts +18 -0
  30. package/middlewares/rps-choice.ts +66 -0
  31. package/package.json +36 -10
  32. package/plugin.ts +63 -0
  33. package/schema.json +6 -0
  34. package/src/engine.ts +1 -1
  35. package/src/game-flow.ts +17 -14
  36. package/src/index.ts +9 -34
  37. package/src/memory-db.ts +22 -0
  38. package/src/models.ts +7 -4
  39. package/src/rps-command.ts +12 -3
  40. package/src/runtime-store.ts +9 -0
  41. package/src/session-service.ts +2 -5
  42. package/src/view.ts +2 -2
  43. package/lib/commands.d.ts +0 -6
  44. package/lib/commands.d.ts.map +0 -1
  45. package/lib/commands.js +0 -85
  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 -29
  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/rps-command.d.ts.map +0 -1
  56. package/lib/session-service.d.ts.map +0 -1
  57. package/lib/view.d.ts.map +0 -1
  58. package/plugin.yml +0 -2
  59. package/src/commands.ts +0 -105
  60. package/src/hub-register.ts +0 -30
package/src/engine.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { secureRandomItem } from '@zhin.js/game-shared';
1
+ import { secureRandomItem } from '@zhin.js/game-kit';
2
2
 
3
3
  export const RPS_PREFIX = 'rps';
4
4
 
package/src/game-flow.ts CHANGED
@@ -1,5 +1,5 @@
1
- import type { Adapter, Message, Plugin } from 'zhin.js';
2
- import { recordGameOutcome } from '@zhin.js/game-shared';
1
+ import type { Adapter, Message, Plugin } from '@zhin.js/core';
2
+ import { plainTextFromSendContent, recordGameOutcome } from '@zhin.js/game-kit';
3
3
  import type { RpsSessionRow } from './models.js';
4
4
  import {
5
5
  RPS_PREFIX,
@@ -12,13 +12,15 @@ import type { SessionService } from './session-service.js';
12
12
  import { buildRpsView } from './view.js';
13
13
 
14
14
  export async function sendOrEditView(
15
- plugin: Plugin,
15
+ plugin: Plugin | null,
16
16
  services: SessionService,
17
17
  message: Message<any>,
18
18
  session: RpsSessionRow,
19
19
  lastRound?: { player: RpsMove; bot: RpsMove; result: 0 | 1 | 2 },
20
- ): Promise<void> {
20
+ ): Promise<string | void> {
21
21
  const content = buildRpsView(session, lastRound, message.$channel.type);
22
+ if (!plugin) return plainTextFromSendContent(content);
23
+
22
24
  const adapter = plugin.root.inject(message.$adapter) as Adapter;
23
25
 
24
26
  if (session.board_message_id) {
@@ -41,7 +43,7 @@ export async function sendOrEditView(
41
43
  }
42
44
 
43
45
  export async function startGame(
44
- plugin: Plugin,
46
+ plugin: Plugin | null,
45
47
  services: SessionService,
46
48
  message: Message<any>,
47
49
  ): Promise<string | undefined> {
@@ -54,12 +56,12 @@ export async function startGame(
54
56
  return `本频道 ${active.player_name} 正在猜拳对决中。`;
55
57
  }
56
58
  const session = await services.createSession(message);
57
- await sendOrEditView(plugin, services, message, session);
58
- return undefined;
59
+ const text = await sendOrEditView(plugin, services, message, session);
60
+ return typeof text === 'string' ? text : undefined;
59
61
  }
60
62
 
61
63
  export async function handleChoice(
62
- plugin: Plugin,
64
+ plugin: Plugin | null,
63
65
  services: SessionService,
64
66
  message: Message<any>,
65
67
  sessionId: string,
@@ -74,8 +76,8 @@ export async function handleChoice(
74
76
 
75
77
  if (choiceId === 'restart') {
76
78
  await services.updateSession(session.id, { status: 'aborted' });
77
- await startGame(plugin, services, message);
78
- return null;
79
+ // text-only 模式(plugin===null)下 startGame 的唯一输出就是返回文本
80
+ return (await startGame(plugin, services, message)) ?? null;
79
81
  }
80
82
 
81
83
  const player = choiceId as RpsMove;
@@ -102,12 +104,12 @@ export async function handleChoice(
102
104
  const updated = (await services.getById(session.id))!;
103
105
  if (status === 'won') void recordGameOutcome(message, 'rps', 'won', playerWins * 10);
104
106
  else if (status === 'lost') void recordGameOutcome(message, 'rps', 'lost');
105
- await sendOrEditView(plugin, services, message, updated, { player, bot, result });
106
- return null;
107
+ const text = await sendOrEditView(plugin, services, message, updated, { player, bot, result });
108
+ return typeof text === 'string' ? text : null;
107
109
  }
108
110
 
109
111
  export async function continueGame(
110
- plugin: Plugin,
112
+ plugin: Plugin | null,
111
113
  services: SessionService,
112
114
  message: Message<any>,
113
115
  ): Promise<string> {
@@ -116,7 +118,8 @@ export async function continueGame(
116
118
  message.$sender.id,
117
119
  );
118
120
  if (!session) return '你没有进行中的猜拳,发送「猜拳 开始」。';
119
- await sendOrEditView(plugin, services, message, session);
121
+ const text = await sendOrEditView(plugin, services, message, session);
122
+ if (typeof text === 'string') return text;
120
123
  return '已刷新猜拳界面。';
121
124
  }
122
125
 
package/src/index.ts CHANGED
@@ -1,34 +1,9 @@
1
- import { formatCompact, usePlugin, type DatabaseFeature } from 'zhin.js';
2
- import { registerModels } from './models.js';
3
- import {
4
- createServices,
5
- resolveGameDatabase,
6
- type SessionService,
7
- } from './session-service.js';
8
- import { registerCommands, registerInteractive, registerTextFallback } from './commands.js';
9
- import { registerRpsHub } from './hub-register.js';
10
-
11
- const plugin = usePlugin();
12
- const { logger, useContext, addSchedule } = plugin;
13
-
14
- registerModels(plugin);
15
-
16
- let services: SessionService | null = null;
17
-
18
- useContext('database', (dbFeature: DatabaseFeature) => {
19
- services = createServices(resolveGameDatabase(dbFeature));
20
- logger.debug(formatCompact({ 模块: '猜拳', 数据模型: '已就绪' }));
21
- });
22
-
23
- registerRpsHub(() => services);
24
- registerCommands(plugin, () => services);
25
- registerInteractive(plugin, () => services);
26
- registerTextFallback(plugin, () => services);
27
-
28
- addSchedule({ kind: 'solar', cron: '0 */15 * * * *' }, async () => {
29
- if (!services) return;
30
- const n = await services.abortStale(30 * 60 * 1000);
31
- if (n > 0) logger.debug(formatCompact({ 猜拳: '清理超时局', count: n }));
32
- });
33
-
34
- logger.debug(formatCompact({ 模块: '猜拳', 状态: '已加载' }));
1
+ export { RPS_HELP } from './rps-command.js';
2
+ export { gameServicesToken, resolveGameServices } from './runtime-store.js';
3
+ export { createInMemoryRpsDb, mountRpsMemoryServices } from './memory-db.js';
4
+
5
+ /**
6
+ * 已由 plugin.ts 接线:commands/ 命令、middlewares/ 文本与选项中间件、
7
+ * 游戏大厅注册(registerRuntimeGame)、过期会话 cron(scheduleHostToken)、
8
+ * host DatabaseHost(databaseHostToken)。仍未接:interactive 按钮回调。
9
+ */
@@ -0,0 +1,22 @@
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 RpsDatabase, type SessionService } from './session-service.js';
5
+
6
+ const RPS_TABLES = ['rps_sessions'] as const;
7
+
8
+ /** In-memory rps_sessions for Plugin Runtime slice-2 (no DatabaseFeature). */
9
+ export function createInMemoryRpsDb(): RpsDatabase {
10
+ return createInMemoryGameDb(RPS_TABLES) as unknown as RpsDatabase;
11
+ }
12
+
13
+ export function mountRpsMemoryServices(): SessionService {
14
+ const services = createServices(createInMemoryRpsDb());
15
+ return services;
16
+ }
17
+
18
+ export function mountRpsHostServices(host: DatabaseHost): SessionService {
19
+ defineHostTables(host);
20
+ const services = createServices(createHostGameDb(host, RPS_TABLES) as unknown as RpsDatabase);
21
+ return services;
22
+ }
package/src/models.ts CHANGED
@@ -1,8 +1,8 @@
1
- import type { Models, Plugin } from 'zhin.js';
1
+ import type { Models } from '@zhin.js/core';
2
2
 
3
3
  export type RpsSessionStatus = 'active' | 'won' | 'lost' | 'aborted';
4
4
 
5
- declare module 'zhin.js' {
5
+ declare module '@zhin.js/core' {
6
6
  interface Models {
7
7
  rps_sessions: {
8
8
  id: string;
@@ -26,8 +26,11 @@ declare module 'zhin.js' {
26
26
 
27
27
  export type RpsSessionRow = Models['rps_sessions'];
28
28
 
29
- export function registerModels(plugin: Plugin): void {
30
- plugin.defineModel('rps_sessions', {
29
+
30
+ export function defineHostTables(
31
+ db: { define: (name: string, definition: Record<string, unknown>) => void },
32
+ ): void {
33
+ db.define('rps_sessions', {
31
34
  id: { type: 'text', primary: true },
32
35
  adapter: { type: 'text', nullable: false },
33
36
  endpoint: { type: 'text', nullable: false },
@@ -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 { continueGame, startGame } from './game-flow.js';
4
4
  import type { SessionService } from './session-service.js';
5
5
 
@@ -12,7 +12,7 @@ export const RPS_HELP = [
12
12
  ].join('\n');
13
13
 
14
14
  export async function runRpsCommand(
15
- plugin: Plugin,
15
+ plugin: Plugin | null,
16
16
  services: SessionService,
17
17
  message: Message<any>,
18
18
  action: string,
@@ -43,3 +43,12 @@ export async function runRpsCommand(
43
43
 
44
44
  return `未知子命令:${action}\n\n${RPS_HELP}`;
45
45
  }
46
+
47
+ /** Plugin Runtime / smoke: text-only, no Adapter.editMessage. */
48
+ export async function runRpsCommandText(
49
+ services: SessionService,
50
+ message: Message<any>,
51
+ action: string,
52
+ ): Promise<string> {
53
+ return (await runRpsCommand(null, services, message, action)) ?? '';
54
+ }
@@ -0,0 +1,9 @@
1
+ import { createToken } from '@zhin.js/plugin-runtime';
2
+ import type { SessionService } from './session-service.js';
3
+
4
+ /** Owner-scoped service consumed by discovered commands and middleware. */
5
+ export const gameServicesToken = createToken<SessionService>('zhin.game.rps.services');
6
+
7
+ export function resolveGameServices(context: { use<T>(token: typeof gameServicesToken): T }): SessionService {
8
+ return context.use(gameServicesToken) as SessionService;
9
+ }
@@ -1,5 +1,5 @@
1
- import type { Database, DatabaseFeature, Message, Models, RelatedModel } from 'zhin.js';
2
- import { channelKey, generateSessionId, boardMessageMatches } from '@zhin.js/game-shared';
1
+ import type { Database, Message, Models, RelatedModel } from '@zhin.js/core';
2
+ import { channelKey, generateSessionId, boardMessageMatches } from '@zhin.js/game-kit';
3
3
  import type { RpsSessionRow } from './models.js';
4
4
 
5
5
  export type RpsDatabase = Database<unknown, Models, string>;
@@ -83,6 +83,3 @@ export function createServices(db: RpsDatabase): SessionService {
83
83
  return new SessionService(db);
84
84
  }
85
85
 
86
- export function resolveGameDatabase(feature: DatabaseFeature): RpsDatabase {
87
- return feature.db;
88
- }
package/src/view.ts CHANGED
@@ -1,5 +1,5 @@
1
- import { buildChoiceKeyboard } from '@zhin.js/game-shared';
2
- import type { SendContent } from 'zhin.js';
1
+ import { buildChoiceKeyboard } from '@zhin.js/game-kit';
2
+ import type { SendContent } from '@zhin.js/core';
3
3
  import type { RpsSessionRow } from './models.js';
4
4
  import { MOVE_LABELS, RPS_PREFIX, WIN_TARGET, type RpsMove } from './engine.js';
5
5
 
package/lib/commands.d.ts DELETED
@@ -1,6 +0,0 @@
1
- import { type Plugin } from 'zhin.js';
2
- import type { SessionService } from './session-service.js';
3
- export declare function registerCommands(plugin: Plugin, getServices: () => SessionService | null): void;
4
- export declare function registerInteractive(plugin: Plugin, getServices: () => SessionService | null): void;
5
- export declare function registerTextFallback(plugin: Plugin, getServices: () => SessionService | 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;AAYrF,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AAsB3D,wBAAgB,gBAAgB,CAAC,MAAM,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,cAAc,GAAG,IAAI,GAAG,IAAI,CAG/F;AAgBD,wBAAgB,mBAAmB,CAAC,MAAM,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,cAAc,GAAG,IAAI,GAAG,IAAI,CAUlG;AAED,wBAAgB,oBAAoB,CAAC,MAAM,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,cAAc,GAAG,IAAI,GAAG,IAAI,CAuCnG"}
package/lib/commands.js DELETED
@@ -1,85 +0,0 @@
1
- import { MessageCommand, getActionFromMessage } from 'zhin.js';
2
- import { buildChoiceFallbackMap, channelKey, normalizeRpsAction, parseChoicePayload, registerGameTextMiddleware, resolveGameChoice, resolveGameTextPayload, } from '@zhin.js/game-shared';
3
- import { handleChoice, RPS_PREFIX } from './game-flow.js';
4
- import { runRpsCommand } from './rps-command.js';
5
- const VALID_CHOICES = ['rock', 'paper', 'scissors', 'restart'];
6
- function registerPattern(plugin, pattern, desc, getServices) {
7
- plugin.addCommand(new MessageCommand(pattern)
8
- .desc(desc)
9
- .action(async (message, result) => {
10
- const services = getServices();
11
- if (!services)
12
- return '猜拳需要启用 database 配置。';
13
- const raw = result.params.action ?? '';
14
- return runRpsCommand(plugin, services, message, normalizeRpsAction(raw));
15
- }));
16
- }
17
- export function registerCommands(plugin, getServices) {
18
- registerPattern(plugin, '/rps [action:word]', '猜拳(rps)', getServices);
19
- registerPattern(plugin, '/猜拳 [action:word]', '猜拳(中文)', getServices);
20
- }
21
- async function resolveChoice(message, services) {
22
- return resolveGameChoice({
23
- message,
24
- gamePrefix: RPS_PREFIX,
25
- validChoiceIds: VALID_CHOICES,
26
- getById: (id) => services.getById(id),
27
- getActiveForUser: (ch, uid) => services.getActiveForUser(ch, uid),
28
- getByBoardMessageId: (mid) => services.getActiveByBoardMessageId(mid),
29
- });
30
- }
31
- export function registerInteractive(plugin, getServices) {
32
- plugin.registerInteractiveHandler(`${RPS_PREFIX}:`, async (message) => {
33
- const services = getServices();
34
- if (!services)
35
- return false;
36
- const choice = await resolveChoice(message, services);
37
- if (!choice)
38
- return false;
39
- const err = await handleChoice(plugin, services, message, choice.sessionId, choice.choiceId);
40
- if (err)
41
- await message.$reply?.(err);
42
- return true;
43
- });
44
- }
45
- export function registerTextFallback(plugin, getServices) {
46
- registerGameTextMiddleware(plugin, async (message, next) => {
47
- const services = getServices();
48
- if (!services)
49
- return next();
50
- const action = getActionFromMessage(message);
51
- if (action?.payload.startsWith(`${RPS_PREFIX}:`))
52
- return next();
53
- const raw = message.$raw?.trim() ?? '';
54
- const ch = channelKey(message);
55
- const payloadFromText = resolveGameTextPayload(raw);
56
- if (payloadFromText?.startsWith(`${RPS_PREFIX}:`)) {
57
- const parsed = parseChoicePayload(payloadFromText, RPS_PREFIX);
58
- if (parsed) {
59
- const session = await services.getById(parsed.sessionId);
60
- if (session?.channel_key === ch) {
61
- const err = await handleChoice(plugin, services, message, parsed.sessionId, parsed.choiceId);
62
- if (err)
63
- await message.$reply?.(err);
64
- return;
65
- }
66
- }
67
- }
68
- const session = await services.getActiveForUser(ch, message.$sender.id);
69
- if (!session || session.status !== 'active')
70
- return next();
71
- const map = buildChoiceFallbackMap(RPS_PREFIX, session.id, [
72
- { id: 'rock', label: '石头' },
73
- { id: 'paper', label: '布' },
74
- { id: 'scissors', label: '剪刀' },
75
- ]);
76
- const payload = resolveGameTextPayload(raw, map);
77
- const parsed = payload ? parseChoicePayload(payload, RPS_PREFIX) : null;
78
- if (!parsed || parsed.sessionId !== session.id)
79
- return next();
80
- const err = await handleChoice(plugin, services, message, session.id, parsed.choiceId);
81
- if (err)
82
- await message.$reply?.(err);
83
- }, 'rps:text');
84
- }
85
- //# 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":"AAEA,eAAO,MAAM,UAAU,QAAQ,CAAC;AAEhC,MAAM,MAAM,OAAO,GAAG,MAAM,GAAG,OAAO,GAAG,UAAU,CAAC;AAEpD,eAAO,MAAM,WAAW,EAAE,MAAM,CAAC,OAAO,EAAE,MAAM,CAI/C,CAAC;AAQF,wBAAgB,aAAa,IAAI,OAAO,CAGvC;AAED,wBAAwB;AACxB,wBAAgB,YAAY,CAAC,MAAM,EAAE,OAAO,EAAE,GAAG,EAAE,OAAO,GAAG,CAAC,GAAG,CAAC,GAAG,CAAC,CAGrE;AAED,wBAAgB,SAAS,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,GAAG,IAAI,CAcrD;AAED,eAAO,MAAM,UAAU,IAAI,CAAC"}
@@ -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;AAExD,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AACjD,OAAO,EACL,UAAU,EAIV,KAAK,OAAO,EACb,MAAM,aAAa,CAAC;AACrB,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AAG3D,wBAAsB,cAAc,CAClC,MAAM,EAAE,MAAM,EACd,QAAQ,EAAE,cAAc,EACxB,OAAO,EAAE,OAAO,CAAC,GAAG,CAAC,EACrB,OAAO,EAAE,aAAa,EACtB,SAAS,CAAC,EAAE;IAAE,MAAM,EAAE,OAAO,CAAC;IAAC,GAAG,EAAE,OAAO,CAAC;IAAC,MAAM,EAAE,CAAC,GAAG,CAAC,GAAG,CAAC,CAAA;CAAE,GAC/D,OAAO,CAAC,IAAI,CAAC,CAqBf;AAED,wBAAsB,SAAS,CAC7B,MAAM,EAAE,MAAM,EACd,QAAQ,EAAE,cAAc,EACxB,OAAO,EAAE,OAAO,CAAC,GAAG,CAAC,GACpB,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC,CAY7B;AAED,wBAAsB,YAAY,CAChC,MAAM,EAAE,MAAM,EACd,QAAQ,EAAE,cAAc,EACxB,OAAO,EAAE,OAAO,CAAC,GAAG,CAAC,EACrB,SAAS,EAAE,MAAM,EACjB,QAAQ,EAAE,MAAM,GACf,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAwCxB;AAED,wBAAsB,YAAY,CAChC,MAAM,EAAE,MAAM,EACd,QAAQ,EAAE,cAAc,EACxB,OAAO,EAAE,OAAO,CAAC,GAAG,CAAC,GACpB,OAAO,CAAC,MAAM,CAAC,CAQjB;AAED,OAAO,EAAE,UAAU,EAAE,CAAC"}
@@ -1,5 +0,0 @@
1
- import { RPS_HELP } from './rps-command.js';
2
- import type { SessionService } from './session-service.js';
3
- export declare function registerRpsHub(getServices: () => SessionService | null): () => void;
4
- export { RPS_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,cAAc,EAAE,MAAM,sBAAsB,CAAC;AAE3D,wBAAgB,cAAc,CAAC,WAAW,EAAE,MAAM,cAAc,GAAG,IAAI,GAAG,MAAM,IAAI,CAsBnF;AAED,OAAO,EAAE,QAAQ,EAAE,CAAC"}
@@ -1,29 +0,0 @@
1
- import { getPlugin } from 'zhin.js';
2
- import { ensureGameHubService } from '@zhin.js/game-shared';
3
- import { runRpsCommand, RPS_HELP } from './rps-command.js';
4
- export function registerRpsHub(getServices) {
5
- const plugin = getPlugin();
6
- ensureGameHubService(plugin);
7
- return plugin.registerGame({
8
- id: 'rps',
9
- title: '猜拳对决',
10
- icon: '✊',
11
- description: '石头剪刀布,三局两胜',
12
- commandPrefix: '/猜拳',
13
- quickStart: '开始',
14
- aliases: ['rps'],
15
- menus: [
16
- { id: 'start', label: '🎮 开始对局', style: 'primary' },
17
- { id: 'continue', label: '🔄 继续' },
18
- { id: 'help', label: '📖 玩法说明' },
19
- ],
20
- runAction: async (actionId, ctx) => {
21
- const services = getServices();
22
- if (!services)
23
- return '猜拳需要启用 database 配置。';
24
- return runRpsCommand(ctx.plugin, services, ctx.message, actionId);
25
- },
26
- });
27
- }
28
- export { RPS_HELP };
29
- //# 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,WAAwC;IACrE,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,MAAM;QACb,IAAI,EAAE,GAAG;QACT,WAAW,EAAE,YAAY;QACzB,aAAa,EAAE,KAAK;QACpB,UAAU,EAAE,IAAI;QAChB,OAAO,EAAE,CAAC,KAAK,CAAC;QAChB,KAAK,EAAE;YACL,EAAE,EAAE,EAAE,OAAO,EAAE,KAAK,EAAE,SAAS,EAAE,KAAK,EAAE,SAAS,EAAE;YACnD,EAAE,EAAE,EAAE,UAAU,EAAE,KAAK,EAAE,OAAO,EAAE;YAClC,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,qBAAqB,CAAC;YAC5C,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,SAAS,EAAE,MAAM,CAAC;YAClB,WAAW,EAAE,MAAM,CAAC;YACpB,WAAW,EAAE,MAAM,CAAC;YACpB,QAAQ,EAAE,MAAM,CAAC;YACjB,KAAK,EAAE,MAAM,CAAC;YACd,MAAM,EAAE,gBAAgB,CAAC;YACzB,gBAAgB,EAAE,MAAM,CAAC;YACzB,UAAU,EAAE,MAAM,CAAC;YACnB,UAAU,EAAE,MAAM,CAAC;SACpB,CAAC;KACH;CACF;AAED,MAAM,MAAM,aAAa,GAAG,MAAM,CAAC,cAAc,CAAC,CAAC;AAEnD,wBAAgB,cAAc,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI,CAkBnD"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"rps-command.d.ts","sourceRoot":"","sources":["../src/rps-command.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,EAAE,MAAM,SAAS,CAAC;AAG/C,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AAE3D,eAAO,MAAM,QAAQ,QAMT,CAAC;AAEb,wBAAsB,aAAa,CACjC,MAAM,EAAE,MAAM,EACd,QAAQ,EAAE,cAAc,EACxB,OAAO,EAAE,OAAO,CAAC,GAAG,CAAC,EACrB,MAAM,EAAE,MAAM,GACb,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC,CA0B7B"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"session-service.d.ts","sourceRoot":"","sources":["../src/session-service.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,eAAe,EAAE,OAAO,EAAE,MAAM,EAAgB,MAAM,SAAS,CAAC;AAExF,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAEjD,MAAM,MAAM,WAAW,GAAG,QAAQ,CAAC,OAAO,EAAE,MAAM,EAAE,MAAM,CAAC,CAAC;AAQ5D,qBAAa,cAAc;IACb,OAAO,CAAC,QAAQ,CAAC,EAAE;gBAAF,EAAE,EAAE,WAAW;IAEtC,kBAAkB,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,aAAa,GAAG,IAAI,CAAC;IAKlE,gBAAgB,CAAC,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,aAAa,GAAG,IAAI,CAAC;IAMhF,OAAO,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,aAAa,GAAG,IAAI,CAAC;IAIlD,yBAAyB,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,aAAa,GAAG,IAAI,CAAC;IAS3E,aAAa,CAAC,OAAO,EAAE,OAAO,CAAC,GAAG,CAAC,GAAG,OAAO,CAAC,aAAa,CAAC;IAuB5D,aAAa,CAAC,EAAE,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO,CAAC,aAAa,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC;IAIvE,UAAU,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;CAalD;AAED,wBAAgB,cAAc,CAAC,EAAE,EAAE,WAAW,GAAG,cAAc,CAE9D;AAED,wBAAgB,mBAAmB,CAAC,OAAO,EAAE,eAAe,GAAG,WAAW,CAEzE"}
package/lib/view.d.ts.map DELETED
@@ -1 +0,0 @@
1
- {"version":3,"file":"view.d.ts","sourceRoot":"","sources":["../src/view.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,SAAS,CAAC;AAC3C,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AACjD,OAAO,EAAuC,KAAK,OAAO,EAAE,MAAM,aAAa,CAAC;AAEhF,wBAAgB,YAAY,CAC1B,OAAO,EAAE,aAAa,EACtB,SAAS,CAAC,EAAE;IAAE,MAAM,EAAE,OAAO,CAAC;IAAC,GAAG,EAAE,OAAO,CAAC;IAAC,MAAM,EAAE,CAAC,GAAG,CAAC,GAAG,CAAC,CAAA;CAAE,EAChE,WAAW,CAAC,EAAE,MAAM,GACnB,WAAW,CAiDb"}
package/plugin.yml DELETED
@@ -1,2 +0,0 @@
1
- name: "@zhin.js/plugin-rps"
2
- description: "猜拳对决"
package/src/commands.ts DELETED
@@ -1,105 +0,0 @@
1
- import { Message, MessageCommand, getActionFromMessage, type Plugin } from 'zhin.js';
2
- import {
3
- buildChoiceFallbackMap,
4
- channelKey,
5
- normalizeRpsAction,
6
- parseChoicePayload,
7
- registerGameTextMiddleware,
8
- resolveGameChoice,
9
- resolveGameTextPayload,
10
- } from '@zhin.js/game-shared';
11
- import { handleChoice, RPS_PREFIX } from './game-flow.js';
12
- import { runRpsCommand } from './rps-command.js';
13
- import type { SessionService } from './session-service.js';
14
-
15
- const VALID_CHOICES = ['rock', 'paper', 'scissors', 'restart'] as const;
16
-
17
- function registerPattern(
18
- plugin: Plugin,
19
- pattern: string,
20
- desc: string,
21
- getServices: () => SessionService | null,
22
- ): void {
23
- plugin.addCommand(
24
- new MessageCommand(pattern)
25
- .desc(desc)
26
- .action(async (message, result) => {
27
- const services = getServices();
28
- if (!services) return '猜拳需要启用 database 配置。';
29
- const raw = (result.params.action as string | undefined) ?? '';
30
- return runRpsCommand(plugin, services, message, normalizeRpsAction(raw));
31
- }),
32
- );
33
- }
34
-
35
- export function registerCommands(plugin: Plugin, getServices: () => SessionService | null): void {
36
- registerPattern(plugin, '/rps [action:word]', '猜拳(rps)', getServices);
37
- registerPattern(plugin, '/猜拳 [action:word]', '猜拳(中文)', getServices);
38
- }
39
-
40
- async function resolveChoice(
41
- message: Message<any>,
42
- services: SessionService,
43
- ): Promise<{ sessionId: string; choiceId: string } | null> {
44
- return resolveGameChoice({
45
- message,
46
- gamePrefix: RPS_PREFIX,
47
- validChoiceIds: VALID_CHOICES,
48
- getById: (id) => services.getById(id),
49
- getActiveForUser: (ch, uid) => services.getActiveForUser(ch, uid),
50
- getByBoardMessageId: (mid) => services.getActiveByBoardMessageId(mid),
51
- });
52
- }
53
-
54
- export function registerInteractive(plugin: Plugin, getServices: () => SessionService | null): void {
55
- plugin.registerInteractiveHandler(`${RPS_PREFIX}:`, async (message) => {
56
- const services = getServices();
57
- if (!services) return false;
58
- const choice = await resolveChoice(message, services);
59
- if (!choice) return false;
60
- const err = await handleChoice(plugin, services, message, choice.sessionId, choice.choiceId);
61
- if (err) await message.$reply?.(err);
62
- return true;
63
- });
64
- }
65
-
66
- export function registerTextFallback(plugin: Plugin, getServices: () => SessionService | null): void {
67
- registerGameTextMiddleware(plugin, async (message, next) => {
68
- const services = getServices();
69
- if (!services) return next();
70
-
71
- const action = getActionFromMessage(message);
72
- if (action?.payload.startsWith(`${RPS_PREFIX}:`)) return next();
73
-
74
- const raw = message.$raw?.trim() ?? '';
75
- const ch = channelKey(message);
76
-
77
- const payloadFromText = resolveGameTextPayload(raw);
78
- if (payloadFromText?.startsWith(`${RPS_PREFIX}:`)) {
79
- const parsed = parseChoicePayload(payloadFromText, RPS_PREFIX);
80
- if (parsed) {
81
- const session = await services.getById(parsed.sessionId);
82
- if (session?.channel_key === ch) {
83
- const err = await handleChoice(plugin, services, message, parsed.sessionId, parsed.choiceId);
84
- if (err) await message.$reply?.(err);
85
- return;
86
- }
87
- }
88
- }
89
-
90
- const session = await services.getActiveForUser(ch, message.$sender.id);
91
- if (!session || session.status !== 'active') return next();
92
-
93
- const map = buildChoiceFallbackMap(RPS_PREFIX, session.id, [
94
- { id: 'rock', label: '石头' },
95
- { id: 'paper', label: '布' },
96
- { id: 'scissors', label: '剪刀' },
97
- ]);
98
- const payload = resolveGameTextPayload(raw, map);
99
- const parsed = payload ? parseChoicePayload(payload, RPS_PREFIX) : null;
100
- if (!parsed || parsed.sessionId !== session.id) return next();
101
-
102
- const err = await handleChoice(plugin, services, message, session.id, parsed.choiceId);
103
- if (err) await message.$reply?.(err);
104
- }, 'rps:text');
105
- }
@@ -1,30 +0,0 @@
1
- import { getPlugin } from 'zhin.js';
2
- import { ensureGameHubService } from '@zhin.js/game-shared';
3
- import { runRpsCommand, RPS_HELP } from './rps-command.js';
4
- import type { SessionService } from './session-service.js';
5
-
6
- export function registerRpsHub(getServices: () => SessionService | null): () => void {
7
- const plugin = getPlugin();
8
- ensureGameHubService(plugin);
9
- return plugin.registerGame({
10
- id: 'rps',
11
- title: '猜拳对决',
12
- icon: '✊',
13
- description: '石头剪刀布,三局两胜',
14
- commandPrefix: '/猜拳',
15
- quickStart: '开始',
16
- aliases: ['rps'],
17
- menus: [
18
- { id: 'start', label: '🎮 开始对局', style: 'primary' },
19
- { id: 'continue', label: '🔄 继续' },
20
- { id: 'help', label: '📖 玩法说明' },
21
- ],
22
- runAction: async (actionId, ctx) => {
23
- const services = getServices();
24
- if (!services) return '猜拳需要启用 database 配置。';
25
- return runRpsCommand(ctx.plugin, services, ctx.message, actionId);
26
- },
27
- });
28
- }
29
-
30
- export { RPS_HELP };