@zhin.js/game-kit 1.0.1

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 (128) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +93 -0
  3. package/lib/board-sender.d.ts +14 -0
  4. package/lib/board-sender.d.ts.map +1 -0
  5. package/lib/board-sender.js +7 -0
  6. package/lib/board-sender.js.map +1 -0
  7. package/lib/choice-keyboard.d.ts +44 -0
  8. package/lib/choice-keyboard.d.ts.map +1 -0
  9. package/lib/choice-keyboard.js +67 -0
  10. package/lib/choice-keyboard.js.map +1 -0
  11. package/lib/command-alias.d.ts +21 -0
  12. package/lib/command-alias.d.ts.map +1 -0
  13. package/lib/command-alias.js +22 -0
  14. package/lib/command-alias.js.map +1 -0
  15. package/lib/command-help.d.ts +19 -0
  16. package/lib/command-help.d.ts.map +1 -0
  17. package/lib/command-help.js +91 -0
  18. package/lib/command-help.js.map +1 -0
  19. package/lib/command-message.d.ts +25 -0
  20. package/lib/command-message.d.ts.map +1 -0
  21. package/lib/command-message.js +58 -0
  22. package/lib/command-message.js.map +1 -0
  23. package/lib/game-action-alias.d.ts +8 -0
  24. package/lib/game-action-alias.d.ts.map +1 -0
  25. package/lib/game-action-alias.js +117 -0
  26. package/lib/game-action-alias.js.map +1 -0
  27. package/lib/game-commands.d.ts +3 -0
  28. package/lib/game-commands.d.ts.map +1 -0
  29. package/lib/game-commands.js +5 -0
  30. package/lib/game-commands.js.map +1 -0
  31. package/lib/game-hub-feature.d.ts +58 -0
  32. package/lib/game-hub-feature.d.ts.map +1 -0
  33. package/lib/game-hub-feature.js +100 -0
  34. package/lib/game-hub-feature.js.map +1 -0
  35. package/lib/game-hub-flow.d.ts +9 -0
  36. package/lib/game-hub-flow.d.ts.map +1 -0
  37. package/lib/game-hub-flow.js +90 -0
  38. package/lib/game-hub-flow.js.map +1 -0
  39. package/lib/game-hub-menu-context.d.ts +25 -0
  40. package/lib/game-hub-menu-context.d.ts.map +1 -0
  41. package/lib/game-hub-menu-context.js +64 -0
  42. package/lib/game-hub-menu-context.js.map +1 -0
  43. package/lib/game-hub-menu.d.ts +37 -0
  44. package/lib/game-hub-menu.d.ts.map +1 -0
  45. package/lib/game-hub-menu.js +162 -0
  46. package/lib/game-hub-menu.js.map +1 -0
  47. package/lib/game-hub-mount.d.ts +9 -0
  48. package/lib/game-hub-mount.d.ts.map +1 -0
  49. package/lib/game-hub-mount.js +108 -0
  50. package/lib/game-hub-mount.js.map +1 -0
  51. package/lib/game-interactive.d.ts +25 -0
  52. package/lib/game-interactive.d.ts.map +1 -0
  53. package/lib/game-interactive.js +62 -0
  54. package/lib/game-interactive.js.map +1 -0
  55. package/lib/game-middleware.d.ts +6 -0
  56. package/lib/game-middleware.d.ts.map +1 -0
  57. package/lib/game-middleware.js +9 -0
  58. package/lib/game-middleware.js.map +1 -0
  59. package/lib/game-onboarding.d.ts +8 -0
  60. package/lib/game-onboarding.d.ts.map +1 -0
  61. package/lib/game-onboarding.js +66 -0
  62. package/lib/game-onboarding.js.map +1 -0
  63. package/lib/game-records.d.ts +52 -0
  64. package/lib/game-records.d.ts.map +1 -0
  65. package/lib/game-records.js +206 -0
  66. package/lib/game-records.js.map +1 -0
  67. package/lib/game-session.d.ts +64 -0
  68. package/lib/game-session.d.ts.map +1 -0
  69. package/lib/game-session.js +44 -0
  70. package/lib/game-session.js.map +1 -0
  71. package/lib/grid-keyboard.d.ts +67 -0
  72. package/lib/grid-keyboard.d.ts.map +1 -0
  73. package/lib/grid-keyboard.js +104 -0
  74. package/lib/grid-keyboard.js.map +1 -0
  75. package/lib/help-card.d.ts +12 -0
  76. package/lib/help-card.d.ts.map +1 -0
  77. package/lib/help-card.js +129 -0
  78. package/lib/help-card.js.map +1 -0
  79. package/lib/index.d.ts +29 -0
  80. package/lib/index.d.ts.map +1 -0
  81. package/lib/index.js +29 -0
  82. package/lib/index.js.map +1 -0
  83. package/lib/interaction-profiles.d.ts +16 -0
  84. package/lib/interaction-profiles.d.ts.map +1 -0
  85. package/lib/interaction-profiles.js +35 -0
  86. package/lib/interaction-profiles.js.map +1 -0
  87. package/lib/memory-db.d.ts +41 -0
  88. package/lib/memory-db.d.ts.map +1 -0
  89. package/lib/memory-db.js +92 -0
  90. package/lib/memory-db.js.map +1 -0
  91. package/lib/random.d.ts +6 -0
  92. package/lib/random.d.ts.map +1 -0
  93. package/lib/random.js +29 -0
  94. package/lib/random.js.map +1 -0
  95. package/lib/runtime-hub.d.ts +28 -0
  96. package/lib/runtime-hub.d.ts.map +1 -0
  97. package/lib/runtime-hub.js +66 -0
  98. package/lib/runtime-hub.js.map +1 -0
  99. package/lib/send-content-text.d.ts +5 -0
  100. package/lib/send-content-text.d.ts.map +1 -0
  101. package/lib/send-content-text.js +24 -0
  102. package/lib/send-content-text.js.map +1 -0
  103. package/package.json +49 -0
  104. package/src/board-sender.ts +16 -0
  105. package/src/choice-keyboard.ts +117 -0
  106. package/src/command-alias.ts +45 -0
  107. package/src/command-help.ts +117 -0
  108. package/src/command-message.ts +76 -0
  109. package/src/game-action-alias.ts +129 -0
  110. package/src/game-commands.ts +4 -0
  111. package/src/game-hub-feature.ts +151 -0
  112. package/src/game-hub-flow.ts +114 -0
  113. package/src/game-hub-menu-context.ts +94 -0
  114. package/src/game-hub-menu.ts +223 -0
  115. package/src/game-hub-mount.ts +172 -0
  116. package/src/game-interactive.ts +88 -0
  117. package/src/game-middleware.ts +14 -0
  118. package/src/game-onboarding.ts +67 -0
  119. package/src/game-records.ts +287 -0
  120. package/src/game-session.ts +97 -0
  121. package/src/grid-keyboard.ts +180 -0
  122. package/src/help-card.ts +188 -0
  123. package/src/index.ts +29 -0
  124. package/src/interaction-profiles.ts +56 -0
  125. package/src/memory-db.ts +128 -0
  126. package/src/random.ts +32 -0
  127. package/src/runtime-hub.ts +86 -0
  128. package/src/send-content-text.ts +21 -0
@@ -0,0 +1,117 @@
1
+ const TTT_ACTIONS = {
2
+ bot: 'bot',
3
+ join: 'join',
4
+ leave: 'leave',
5
+ quit: 'quit',
6
+ spectate: 'spectate',
7
+ help: 'help',
8
+ 人机: 'bot',
9
+ 排队: 'join',
10
+ 加入: 'join',
11
+ 离开: 'leave',
12
+ 认输: 'quit',
13
+ 观战: 'spectate',
14
+ 帮助: 'help',
15
+ };
16
+ const ADV_ACTIONS = {
17
+ start: 'start',
18
+ continue: 'continue',
19
+ map: 'map',
20
+ achievements: 'achievements',
21
+ achievement: 'achievements',
22
+ quit: 'quit',
23
+ help: 'help',
24
+ 开始: 'start',
25
+ 继续: 'continue',
26
+ 地图: 'map',
27
+ 成就: 'achievements',
28
+ 放弃: 'quit',
29
+ 退出: 'quit',
30
+ 帮助: 'help',
31
+ };
32
+ const RPS_ACTIONS = {
33
+ start: 'start',
34
+ continue: 'continue',
35
+ quit: 'quit',
36
+ help: 'help',
37
+ 开始: 'start',
38
+ 继续: 'continue',
39
+ 放弃: 'quit',
40
+ 帮助: 'help',
41
+ };
42
+ const GUESS_ACTIONS = {
43
+ start: 'start',
44
+ quit: 'quit',
45
+ help: 'help',
46
+ 开始: 'start',
47
+ 放弃: 'quit',
48
+ 帮助: 'help',
49
+ };
50
+ const DICE_ACTIONS = {
51
+ start: 'start',
52
+ continue: 'continue',
53
+ quit: 'quit',
54
+ help: 'help',
55
+ 开始: 'start',
56
+ 继续: 'continue',
57
+ 放弃: 'quit',
58
+ 帮助: 'help',
59
+ };
60
+ function lookup(map, raw) {
61
+ const t = raw.trim();
62
+ if (!t)
63
+ return 'help';
64
+ return map[t] ?? map[t.toLowerCase()] ?? t.toLowerCase();
65
+ }
66
+ export function normalizeTttAction(raw) {
67
+ return lookup(TTT_ACTIONS, raw);
68
+ }
69
+ export function normalizeAdvAction(raw) {
70
+ return lookup(ADV_ACTIONS, raw);
71
+ }
72
+ export function normalizeRpsAction(raw) {
73
+ return lookup(RPS_ACTIONS, raw);
74
+ }
75
+ export function normalizeGuessAction(raw) {
76
+ return lookup(GUESS_ACTIONS, raw);
77
+ }
78
+ export function normalizeDiceAction(raw) {
79
+ return lookup(DICE_ACTIONS, raw);
80
+ }
81
+ const CHAIN_ACTIONS = {
82
+ start: 'start_pinyin',
83
+ start_pinyin: 'start_pinyin',
84
+ start_char: 'start_char',
85
+ continue: 'continue',
86
+ quit: 'quit',
87
+ help: 'help',
88
+ pinyin: 'start_pinyin',
89
+ char: 'start_char',
90
+ 开始: 'start_pinyin',
91
+ 同音: 'start_pinyin',
92
+ 同字: 'start_char',
93
+ 继续: 'continue',
94
+ 放弃: 'quit',
95
+ 帮助: 'help',
96
+ };
97
+ const RIDDLE_ACTIONS = {
98
+ start: 'start',
99
+ continue: 'continue',
100
+ quit: 'quit',
101
+ help: 'help',
102
+ char: 'char',
103
+ idiom: 'idiom',
104
+ 开始: 'start',
105
+ 继续: 'continue',
106
+ 放弃: 'quit',
107
+ 帮助: 'help',
108
+ 字谜: 'char',
109
+ 成语: 'idiom',
110
+ };
111
+ export function normalizeChainAction(raw) {
112
+ return lookup(CHAIN_ACTIONS, raw);
113
+ }
114
+ export function normalizeRiddleAction(raw) {
115
+ return lookup(RIDDLE_ACTIONS, raw);
116
+ }
117
+ //# sourceMappingURL=game-action-alias.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"game-action-alias.js","sourceRoot":"","sources":["../src/game-action-alias.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,3 @@
1
+ /** 展示用:`/猜拳` */
2
+ export declare function slashCommandPrefix(name: string): string;
3
+ //# sourceMappingURL=game-commands.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"game-commands.d.ts","sourceRoot":"","sources":["../src/game-commands.ts"],"names":[],"mappings":"AAAA,gBAAgB;AAChB,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAEvD"}
@@ -0,0 +1,5 @@
1
+ /** 展示用:`/猜拳` */
2
+ export function slashCommandPrefix(name) {
3
+ return name.startsWith('/') ? name : `/${name}`;
4
+ }
5
+ //# sourceMappingURL=game-commands.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"game-commands.js","sourceRoot":"","sources":["../src/game-commands.ts"],"names":[],"mappings":"AAAA,gBAAgB;AAChB,MAAM,UAAU,kBAAkB,CAAC,IAAY;IAC7C,OAAO,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,IAAI,EAAE,CAAC;AAClD,CAAC"}
@@ -0,0 +1,58 @@
1
+ import { Feature, type FeatureJSON, type Plugin, type PluginLike } from '@zhin.js/core';
2
+ export interface GameHubContext {
3
+ plugin: Plugin;
4
+ message: import('zhin.js').Message<any>;
5
+ }
6
+ export interface GameMenuAction {
7
+ id: string;
8
+ label: string;
9
+ style?: 'primary' | 'danger' | 'secondary';
10
+ groupOnly?: boolean;
11
+ privateOnly?: boolean;
12
+ }
13
+ export interface RegisteredGame {
14
+ id: string;
15
+ title: string;
16
+ icon: string;
17
+ description: string;
18
+ commandPrefix: string;
19
+ quickStart?: string;
20
+ aliases?: string[];
21
+ menus: GameMenuAction[];
22
+ runAction: (actionId: string, ctx: GameHubContext) => Promise<string | undefined | void>;
23
+ }
24
+ export interface GameHubContextExtensions {
25
+ registerGame(game: RegisteredGame): () => void;
26
+ }
27
+ declare module '@zhin.js/core' {
28
+ namespace Plugin {
29
+ interface Extensions extends GameHubContextExtensions {
30
+ }
31
+ interface Contexts {
32
+ game: GameHubFeature;
33
+ }
34
+ }
35
+ }
36
+ export declare class GameHubFeature extends Feature<RegisteredGame> {
37
+ readonly name: "game";
38
+ readonly icon = "Gamepad2";
39
+ readonly desc = "\u6E38\u620F";
40
+ private readonly byId;
41
+ private hubDisposers;
42
+ register(game: RegisteredGame, pluginName: string): () => void;
43
+ getGame(id: string): RegisteredGame | undefined;
44
+ remove(item: RegisteredGame, pluginName?: string): boolean;
45
+ toJSON(pluginName?: string): FeatureJSON;
46
+ get extensions(): {
47
+ registerGame(game: RegisteredGame): () => void;
48
+ };
49
+ mounted(plugin: PluginLike): void;
50
+ dispose(): void;
51
+ }
52
+ /**
53
+ * 在 root 上注册 `game` 服务(幂等)。游戏插件初始化时调用一次即可。
54
+ */
55
+ export declare function ensureGameHubService(plugin: Plugin): GameHubFeature;
56
+ export declare function getRegisteredGames(): readonly RegisteredGame[];
57
+ export declare function getRegisteredGame(id: string): RegisteredGame | undefined;
58
+ //# sourceMappingURL=game-hub-feature.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"game-hub-feature.d.ts","sourceRoot":"","sources":["../src/game-hub-feature.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAa,KAAK,WAAW,EAAE,KAAK,MAAM,EAAE,KAAK,UAAU,EAAwB,MAAM,eAAe,CAAC;AAIzH,MAAM,WAAW,cAAc;IAC7B,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,OAAO,SAAS,EAAE,OAAO,CAAC,GAAG,CAAC,CAAC;CACzC;AAED,MAAM,WAAW,cAAc;IAC7B,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,CAAC,EAAE,SAAS,GAAG,QAAQ,GAAG,WAAW,CAAC;IAC3C,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,WAAW,CAAC,EAAE,OAAO,CAAC;CACvB;AAED,MAAM,WAAW,cAAc;IAC7B,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,MAAM,CAAC;IACpB,aAAa,EAAE,MAAM,CAAC;IACtB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,OAAO,CAAC,EAAE,MAAM,EAAE,CAAC;IACnB,KAAK,EAAE,cAAc,EAAE,CAAC;IACxB,SAAS,EAAE,CAAC,QAAQ,EAAE,MAAM,EAAE,GAAG,EAAE,cAAc,KAAK,OAAO,CAAC,MAAM,GAAG,SAAS,GAAG,IAAI,CAAC,CAAC;CAC1F;AAED,MAAM,WAAW,wBAAwB;IACvC,YAAY,CAAC,IAAI,EAAE,cAAc,GAAG,MAAM,IAAI,CAAC;CAChD;AAED,OAAO,QAAQ,eAAe,CAAC;IAC7B,UAAU,MAAM,CAAC;QACf,UAAU,UAAW,SAAQ,wBAAwB;SAAG;QACxD,UAAU,QAAQ;YAChB,IAAI,EAAE,cAAc,CAAC;SACtB;KACF;CACF;AAID,qBAAa,cAAe,SAAQ,OAAO,CAAC,cAAc,CAAC;IACzD,QAAQ,CAAC,IAAI,EAAG,MAAM,CAAU;IAChC,QAAQ,CAAC,IAAI,cAAc;IAC3B,QAAQ,CAAC,IAAI,kBAAQ;IAErB,OAAO,CAAC,QAAQ,CAAC,IAAI,CAAqC;IAC1D,OAAO,CAAC,YAAY,CAAsB;IAE1C,QAAQ,CAAC,IAAI,EAAE,cAAc,EAAE,UAAU,EAAE,MAAM,GAAG,MAAM,IAAI;IAS9D,OAAO,CAAC,EAAE,EAAE,MAAM,GAAG,cAAc,GAAG,SAAS;IAItC,MAAM,CAAC,IAAI,EAAE,cAAc,EAAE,UAAU,CAAC,EAAE,MAAM,GAAG,OAAO;IAKnE,MAAM,CAAC,UAAU,CAAC,EAAE,MAAM,GAAG,WAAW;IAexC,IAAI,UAAU;2BAGS,cAAc,SApCqB,IAAI;MA6C7D;IAED,OAAO,CAAC,MAAM,EAAE,UAAU,GAAG,IAAI;IAgBjC,OAAO,IAAI,IAAI;CAOhB;AAED;;GAEG;AACH,wBAAgB,oBAAoB,CAAC,MAAM,EAAE,MAAM,GAAG,cAAc,CAenE;AAED,wBAAgB,kBAAkB,IAAI,SAAS,cAAc,EAAE,CAE9D;AAED,wBAAgB,iBAAiB,CAAC,EAAE,EAAE,MAAM,GAAG,cAAc,GAAG,SAAS,CAExE"}
@@ -0,0 +1,100 @@
1
+ import { Feature, getPlugin } from '@zhin.js/core';
2
+ import { mountGameHubUi, markGameHubUiMounted } from './game-hub-mount.js';
3
+ import { registerGameRecordModels, initGameRecordDatabase } from './game-records.js';
4
+ let activeFeature = null;
5
+ export class GameHubFeature extends Feature {
6
+ name = 'game';
7
+ icon = 'Gamepad2';
8
+ desc = '游戏';
9
+ byId = new Map();
10
+ hubDisposers = [];
11
+ register(game, pluginName) {
12
+ const prev = this.byId.get(game.id);
13
+ if (prev) {
14
+ this.remove(prev);
15
+ }
16
+ this.byId.set(game.id, game);
17
+ return this.add(game, pluginName);
18
+ }
19
+ getGame(id) {
20
+ return this.byId.get(id);
21
+ }
22
+ remove(item, pluginName) {
23
+ this.byId.delete(item.id);
24
+ return super.remove(item, pluginName);
25
+ }
26
+ toJSON(pluginName) {
27
+ const list = pluginName ? this.getByPlugin(pluginName) : [...this.items];
28
+ return {
29
+ name: this.name,
30
+ icon: this.icon,
31
+ desc: this.desc,
32
+ count: list.length,
33
+ items: list.map((g) => ({
34
+ name: g.id,
35
+ desc: g.title,
36
+ usage: `${g.commandPrefix} ${g.quickStart ?? '开始'}`,
37
+ })),
38
+ };
39
+ }
40
+ get extensions() {
41
+ const feature = this;
42
+ return {
43
+ registerGame(game) {
44
+ const plugin = getPlugin();
45
+ ensureGameHubService(plugin);
46
+ const dispose = feature.register(game, plugin.name);
47
+ plugin.recordFeatureContribution(feature.name, game.id);
48
+ plugin.onDispose(dispose);
49
+ return dispose;
50
+ },
51
+ };
52
+ }
53
+ mounted(plugin) {
54
+ activeFeature = this;
55
+ const root = plugin;
56
+ const host = root.root ?? root;
57
+ registerGameRecordModels(host);
58
+ if (host.contextIsReady('database')) {
59
+ initGameRecordDatabase(host.inject('database'));
60
+ }
61
+ else {
62
+ host.useContext('database', (dbFeature) => {
63
+ initGameRecordDatabase(dbFeature);
64
+ });
65
+ }
66
+ this.hubDisposers = mountGameHubUi(host);
67
+ markGameHubUiMounted();
68
+ }
69
+ dispose() {
70
+ for (const d of this.hubDisposers)
71
+ d();
72
+ this.hubDisposers = [];
73
+ if (activeFeature === this) {
74
+ activeFeature = null;
75
+ }
76
+ }
77
+ }
78
+ /**
79
+ * 在 root 上注册 `game` 服务(幂等)。游戏插件初始化时调用一次即可。
80
+ */
81
+ export function ensureGameHubService(plugin) {
82
+ const root = plugin.root;
83
+ if (root.contextIsReady('game')) {
84
+ return root.inject('game');
85
+ }
86
+ const feature = new GameHubFeature();
87
+ root.provide(feature);
88
+ activeFeature = feature;
89
+ if (root.started && feature.mounted) {
90
+ feature.mounted(root);
91
+ }
92
+ return root.inject('game');
93
+ }
94
+ export function getRegisteredGames() {
95
+ return activeFeature?.items ?? [];
96
+ }
97
+ export function getRegisteredGame(id) {
98
+ return activeFeature?.getGame(id);
99
+ }
100
+ //# sourceMappingURL=game-hub-feature.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"game-hub-feature.js","sourceRoot":"","sources":["../src/game-hub-feature.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,9 @@
1
+ import type { Message, Plugin } from '@zhin.js/core';
2
+ import { buildMainHubMenu } from './game-hub-menu.js';
3
+ export declare function openMainMenu(message: Message<any>): ReturnType<typeof buildMainHubMenu> | string;
4
+ export declare function handleHubChoice(plugin: Plugin, message: Message<any>, scopeId: string, choiceId: string): Promise<boolean>;
5
+ export declare function parseHubPayload(payload: string): {
6
+ scopeId: string;
7
+ choiceId: string;
8
+ } | null;
9
+ //# sourceMappingURL=game-hub-flow.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"game-hub-flow.d.ts","sourceRoot":"","sources":["../src/game-hub-flow.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,EAAE,MAAM,eAAe,CAAC;AAErD,OAAO,EAEL,gBAAgB,EAMjB,MAAM,oBAAoB,CAAC;AAkC5B,wBAAgB,YAAY,CAAC,OAAO,EAAE,OAAO,CAAC,GAAG,CAAC,GAAG,UAAU,CAAC,OAAO,gBAAgB,CAAC,GAAG,MAAM,CAShG;AAED,wBAAsB,eAAe,CACnC,MAAM,EAAE,MAAM,EACd,OAAO,EAAE,OAAO,CAAC,GAAG,CAAC,EACrB,OAAO,EAAE,MAAM,EACf,QAAQ,EAAE,MAAM,GACf,OAAO,CAAC,OAAO,CAAC,CA+ClB;AAED,wBAAgB,eAAe,CAAC,OAAO,EAAE,MAAM,GAAG;IAAE,OAAO,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAA;CAAE,GAAG,IAAI,CAI7F"}
@@ -0,0 +1,90 @@
1
+ import { parseChoicePayload } from './choice-keyboard.js';
2
+ import { buildGameHubMenu, buildMainHubMenu, formatHubEmptyMessage, HUB_PREFIX, hubActionChoiceId, hubGameChoiceId, parseHubChoiceId, } from './game-hub-menu.js';
3
+ import { getRegisteredGame, getRegisteredGames } from './game-hub-feature.js';
4
+ import { createHubScope, getHubContext, rememberHubMenu, } from './game-hub-menu-context.js';
5
+ function mainMenuChoices() {
6
+ return getRegisteredGames().map((g) => ({
7
+ id: hubGameChoiceId(g.id),
8
+ label: `${g.icon} ${g.title}`,
9
+ }));
10
+ }
11
+ function gameMenuChoices(gameId, channelType) {
12
+ const game = getRegisteredGame(gameId);
13
+ if (!game)
14
+ return [];
15
+ const isGroup = channelType !== 'private';
16
+ const menus = game.menus.filter((m) => {
17
+ if (m.groupOnly && !isGroup)
18
+ return false;
19
+ if (m.privateOnly && isGroup)
20
+ return false;
21
+ return true;
22
+ });
23
+ return [
24
+ ...menus.map((m) => ({
25
+ id: hubActionChoiceId(game.id, m.id),
26
+ label: m.label,
27
+ })),
28
+ { id: 'back', label: '↩️ 返回大厅' },
29
+ ];
30
+ }
31
+ export function openMainMenu(message) {
32
+ const games = getRegisteredGames();
33
+ if (!games.length) {
34
+ return formatHubEmptyMessage();
35
+ }
36
+ const scopeId = createHubScope(message);
37
+ const choices = mainMenuChoices();
38
+ rememberHubMenu(message, scopeId, choices);
39
+ return buildMainHubMenu(scopeId, games, message.$channel.type);
40
+ }
41
+ export async function handleHubChoice(plugin, message, scopeId, choiceId) {
42
+ const ctx = getHubContext(scopeId);
43
+ if (!ctx) {
44
+ await message.$reply?.('菜单已过期,请重新发送「游戏」。');
45
+ return true;
46
+ }
47
+ const ch = `${message.$adapter}-${message.$endpoint}-${message.$channel.type}:${message.$channel.id}`;
48
+ if (ctx.channelKey !== ch) {
49
+ await message.$reply?.('请在本频道使用游戏大厅。');
50
+ return true;
51
+ }
52
+ const parsed = parseHubChoiceId(choiceId);
53
+ if (!parsed)
54
+ return false;
55
+ // 大厅导航(选游戏 / 返回):同频道任何人都可点;具体开局由 runAction 用当前点击者 message
56
+ if (parsed.kind === 'back') {
57
+ const games = getRegisteredGames();
58
+ const choices = mainMenuChoices();
59
+ rememberHubMenu(message, scopeId, choices);
60
+ await message.$reply?.(buildMainHubMenu(scopeId, games, message.$channel.type));
61
+ return true;
62
+ }
63
+ if (parsed.kind === 'game') {
64
+ const game = getRegisteredGame(parsed.gameId);
65
+ if (!game) {
66
+ await message.$reply?.('该游戏未安装。');
67
+ return true;
68
+ }
69
+ const choices = gameMenuChoices(parsed.gameId, message.$channel.type);
70
+ rememberHubMenu(message, scopeId, choices);
71
+ await message.$reply?.(buildGameHubMenu(scopeId, game, message.$channel.type));
72
+ return true;
73
+ }
74
+ const game = getRegisteredGame(parsed.gameId);
75
+ if (!game) {
76
+ await message.$reply?.('该游戏未安装。');
77
+ return true;
78
+ }
79
+ const result = await game.runAction(parsed.actionId, { plugin, message });
80
+ if (typeof result === 'string')
81
+ await message.$reply?.(result);
82
+ return true;
83
+ }
84
+ export function parseHubPayload(payload) {
85
+ const parsed = parseChoicePayload(payload, HUB_PREFIX);
86
+ if (!parsed)
87
+ return null;
88
+ return { scopeId: parsed.sessionId, choiceId: parsed.choiceId };
89
+ }
90
+ //# sourceMappingURL=game-hub-flow.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"game-hub-flow.js","sourceRoot":"","sources":["../src/game-hub-flow.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,25 @@
1
+ import type { Message } from '@zhin.js/core';
2
+ export interface HubMenuContext {
3
+ channelKey: string;
4
+ /** 打开菜单的用户(仅记录,大厅导航不限制点击者) */
5
+ openerId: string;
6
+ message: Message<any>;
7
+ expiresAt: number;
8
+ }
9
+ export interface HubMenuChoice {
10
+ id: string;
11
+ label: string;
12
+ disabled?: boolean;
13
+ }
14
+ export interface LastHubMenu {
15
+ scopeId: string;
16
+ choices: HubMenuChoice[];
17
+ expiresAt: number;
18
+ }
19
+ export declare function createHubScope(message: Message<any>): string;
20
+ export declare function rememberHubMenu(message: Message<any>, scopeId: string, choices: HubMenuChoice[]): void;
21
+ export declare function getLastHubMenu(message: Message<any>): LastHubMenu | null;
22
+ export declare function getHubContext(scopeId: string): HubMenuContext | null;
23
+ /** 测试专用:清空菜单上下文 */
24
+ export declare function resetHubMenuContextForTests(): void;
25
+ //# sourceMappingURL=game-hub-menu-context.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"game-hub-menu-context.d.ts","sourceRoot":"","sources":["../src/game-hub-menu-context.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,eAAe,CAAC;AAI7C,MAAM,WAAW,cAAc;IAC7B,UAAU,EAAE,MAAM,CAAC;IACnB,8BAA8B;IAC9B,QAAQ,EAAE,MAAM,CAAC;IACjB,OAAO,EAAE,OAAO,CAAC,GAAG,CAAC,CAAC;IACtB,SAAS,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,aAAa;IAC5B,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,CAAC,EAAE,OAAO,CAAC;CACpB;AAED,MAAM,WAAW,WAAW;IAC1B,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,aAAa,EAAE,CAAC;IACzB,SAAS,EAAE,MAAM,CAAC;CACnB;AAWD,wBAAgB,cAAc,CAAC,OAAO,EAAE,OAAO,CAAC,GAAG,CAAC,GAAG,MAAM,CAU5D;AAED,wBAAgB,eAAe,CAC7B,OAAO,EAAE,OAAO,CAAC,GAAG,CAAC,EACrB,OAAO,EAAE,MAAM,EACf,OAAO,EAAE,aAAa,EAAE,GACvB,IAAI,CAON;AAED,wBAAgB,cAAc,CAAC,OAAO,EAAE,OAAO,CAAC,GAAG,CAAC,GAAG,WAAW,GAAG,IAAI,CASxE;AAED,wBAAgB,aAAa,CAAC,OAAO,EAAE,MAAM,GAAG,cAAc,GAAG,IAAI,CAQpE;AAYD,mBAAmB;AACnB,wBAAgB,2BAA2B,IAAI,IAAI,CAGlD"}
@@ -0,0 +1,64 @@
1
+ import { channelKey } from './board-sender.js';
2
+ import { generateCompactId } from './random.js';
3
+ const TTL_MS = 60 * 60 * 1000;
4
+ const contexts = new Map();
5
+ const lastMenus = new Map();
6
+ /** 大厅 fallback 按频道共享,任意成员可回复数字或点按钮 */
7
+ function hubMenuKey(message) {
8
+ return channelKey(message);
9
+ }
10
+ export function createHubScope(message) {
11
+ const scopeId = generateCompactId('h');
12
+ contexts.set(scopeId, {
13
+ channelKey: channelKey(message),
14
+ openerId: message.$sender.id,
15
+ message,
16
+ expiresAt: Date.now() + TTL_MS,
17
+ });
18
+ pruneExpired();
19
+ return scopeId;
20
+ }
21
+ export function rememberHubMenu(message, scopeId, choices) {
22
+ lastMenus.set(hubMenuKey(message), {
23
+ scopeId,
24
+ choices,
25
+ expiresAt: Date.now() + TTL_MS,
26
+ });
27
+ pruneExpired();
28
+ }
29
+ export function getLastHubMenu(message) {
30
+ pruneExpired();
31
+ const key = hubMenuKey(message);
32
+ const last = lastMenus.get(key);
33
+ if (!last || last.expiresAt < Date.now()) {
34
+ lastMenus.delete(key);
35
+ return null;
36
+ }
37
+ return last;
38
+ }
39
+ export function getHubContext(scopeId) {
40
+ pruneExpired();
41
+ const ctx = contexts.get(scopeId);
42
+ if (!ctx || ctx.expiresAt < Date.now()) {
43
+ contexts.delete(scopeId);
44
+ return null;
45
+ }
46
+ return ctx;
47
+ }
48
+ function pruneExpired() {
49
+ const now = Date.now();
50
+ for (const [id, ctx] of contexts) {
51
+ if (ctx.expiresAt < now)
52
+ contexts.delete(id);
53
+ }
54
+ for (const [key, menu] of lastMenus) {
55
+ if (menu.expiresAt < now)
56
+ lastMenus.delete(key);
57
+ }
58
+ }
59
+ /** 测试专用:清空菜单上下文 */
60
+ export function resetHubMenuContextForTests() {
61
+ contexts.clear();
62
+ lastMenus.clear();
63
+ }
64
+ //# sourceMappingURL=game-hub-menu-context.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"game-hub-menu-context.js","sourceRoot":"","sources":["../src/game-hub-menu-context.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAC;AAC/C,OAAO,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAC;AAsBhD,MAAM,MAAM,GAAG,EAAE,GAAG,EAAE,GAAG,IAAI,CAAC;AAC9B,MAAM,QAAQ,GAAG,IAAI,GAAG,EAA0B,CAAC;AACnD,MAAM,SAAS,GAAG,IAAI,GAAG,EAAuB,CAAC;AAEjD,sCAAsC;AACtC,SAAS,UAAU,CAAC,OAAqB;IACvC,OAAO,UAAU,CAAC,OAAO,CAAC,CAAC;AAC7B,CAAC;AAED,MAAM,UAAU,cAAc,CAAC,OAAqB;IAClD,MAAM,OAAO,GAAG,iBAAiB,CAAC,GAAG,CAAC,CAAC;IACvC,QAAQ,CAAC,GAAG,CAAC,OAAO,EAAE;QACpB,UAAU,EAAE,UAAU,CAAC,OAAO,CAAC;QAC/B,QAAQ,EAAE,OAAO,CAAC,OAAO,CAAC,EAAE;QAC5B,OAAO;QACP,SAAS,EAAE,IAAI,CAAC,GAAG,EAAE,GAAG,MAAM;KAC/B,CAAC,CAAC;IACH,YAAY,EAAE,CAAC;IACf,OAAO,OAAO,CAAC;AACjB,CAAC;AAED,MAAM,UAAU,eAAe,CAC7B,OAAqB,EACrB,OAAe,EACf,OAAwB;IAExB,SAAS,CAAC,GAAG,CAAC,UAAU,CAAC,OAAO,CAAC,EAAE;QACjC,OAAO;QACP,OAAO;QACP,SAAS,EAAE,IAAI,CAAC,GAAG,EAAE,GAAG,MAAM;KAC/B,CAAC,CAAC;IACH,YAAY,EAAE,CAAC;AACjB,CAAC;AAED,MAAM,UAAU,cAAc,CAAC,OAAqB;IAClD,YAAY,EAAE,CAAC;IACf,MAAM,GAAG,GAAG,UAAU,CAAC,OAAO,CAAC,CAAC;IAChC,MAAM,IAAI,GAAG,SAAS,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;IAChC,IAAI,CAAC,IAAI,IAAI,IAAI,CAAC,SAAS,GAAG,IAAI,CAAC,GAAG,EAAE,EAAE,CAAC;QACzC,SAAS,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;QACtB,OAAO,IAAI,CAAC;IACd,CAAC;IACD,OAAO,IAAI,CAAC;AACd,CAAC;AAED,MAAM,UAAU,aAAa,CAAC,OAAe;IAC3C,YAAY,EAAE,CAAC;IACf,MAAM,GAAG,GAAG,QAAQ,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC;IAClC,IAAI,CAAC,GAAG,IAAI,GAAG,CAAC,SAAS,GAAG,IAAI,CAAC,GAAG,EAAE,EAAE,CAAC;QACvC,QAAQ,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC;QACzB,OAAO,IAAI,CAAC;IACd,CAAC;IACD,OAAO,GAAG,CAAC;AACb,CAAC;AAED,SAAS,YAAY;IACnB,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;IACvB,KAAK,MAAM,CAAC,EAAE,EAAE,GAAG,CAAC,IAAI,QAAQ,EAAE,CAAC;QACjC,IAAI,GAAG,CAAC,SAAS,GAAG,GAAG;YAAE,QAAQ,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC;IAC/C,CAAC;IACD,KAAK,MAAM,CAAC,GAAG,EAAE,IAAI,CAAC,IAAI,SAAS,EAAE,CAAC;QACpC,IAAI,IAAI,CAAC,SAAS,GAAG,GAAG;YAAE,SAAS,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;IAClD,CAAC;AACH,CAAC;AAED,mBAAmB;AACnB,MAAM,UAAU,2BAA2B;IACzC,QAAQ,CAAC,KAAK,EAAE,CAAC;IACjB,SAAS,CAAC,KAAK,EAAE,CAAC;AACpB,CAAC"}
@@ -0,0 +1,37 @@
1
+ import { type SendContent } from '@zhin.js/core';
2
+ import type { RegisteredGame } from './game-hub-feature.js';
3
+ import { type CommandHelpSource } from './command-help.js';
4
+ export declare const HUB_PREFIX = "hub";
5
+ export declare function hubGameChoiceId(gameId: string): string;
6
+ export declare function hubActionChoiceId(gameId: string, actionId: string): string;
7
+ export declare function parseHubChoiceId(choiceId: string): {
8
+ kind: 'game';
9
+ gameId: string;
10
+ } | {
11
+ kind: 'action';
12
+ gameId: string;
13
+ actionId: string;
14
+ } | {
15
+ kind: 'back';
16
+ } | null;
17
+ export declare function buildMainHubMenu(scopeId: string, games: readonly RegisteredGame[], channelType: string): SendContent;
18
+ export declare function buildGameHubMenu(scopeId: string, game: RegisteredGame, channelType: string): SendContent;
19
+ /** 大厅主菜单:命令前缀提示 */
20
+ export declare function formatCommandPrefixHint(games: readonly RegisteredGame[]): string;
21
+ /** 游戏大厅帮助文案(随 registerGame 动态生成) */
22
+ export declare function formatHubHelp(games: readonly RegisteredGame[], commands?: readonly CommandHelpSource[]): string;
23
+ /** 顶层 /帮助:游戏大厅 + 已注册命令自动汇总 */
24
+ export declare function formatBotHelp(games: readonly RegisteredGame[], options?: {
25
+ channelType?: string;
26
+ commands?: readonly CommandHelpSource[];
27
+ }): string;
28
+ /** 顶层 /帮助:HTML 卡片(QQ 等出站 policy 为 image 时渲染为 PNG;无 renderer 时回退文本) */
29
+ export declare function buildBotHelpReply(games: readonly RegisteredGame[], options?: {
30
+ channelType?: string;
31
+ commands?: readonly CommandHelpSource[];
32
+ }): SendContent;
33
+ /** /游戏 帮助:大厅专用卡片 */
34
+ export declare function buildHubHelpReply(games: readonly RegisteredGame[], commands?: readonly CommandHelpSource[]): SendContent;
35
+ /** 无游戏时的提示 */
36
+ export declare function formatHubEmptyMessage(): string;
37
+ //# sourceMappingURL=game-hub-menu.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"game-hub-menu.d.ts","sourceRoot":"","sources":["../src/game-hub-menu.ts"],"names":[],"mappings":"AAAA,OAAO,EAAW,KAAK,WAAW,EAAE,MAAM,eAAe,CAAC;AAI1D,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC;AAC5D,OAAO,EAGL,KAAK,iBAAiB,EACvB,MAAM,mBAAmB,CAAC;AAQ3B,eAAO,MAAM,UAAU,QAAQ,CAAC;AAEhC,wBAAgB,eAAe,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAEtD;AAED,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,MAAM,CAE1E;AAED,wBAAgB,gBAAgB,CAC9B,QAAQ,EAAE,MAAM,GAEd;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,GAChC;IAAE,IAAI,EAAE,QAAQ,CAAC;IAAC,MAAM,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAA;CAAE,GACpD;IAAE,IAAI,EAAE,MAAM,CAAA;CAAE,GAChB,IAAI,CAcP;AAED,wBAAgB,gBAAgB,CAC9B,OAAO,EAAE,MAAM,EACf,KAAK,EAAE,SAAS,cAAc,EAAE,EAChC,WAAW,EAAE,MAAM,GAClB,WAAW,CA4Bb;AAED,wBAAgB,gBAAgB,CAC9B,OAAO,EAAE,MAAM,EACf,IAAI,EAAE,cAAc,EACpB,WAAW,EAAE,MAAM,GAClB,WAAW,CAyBb;AAED,mBAAmB;AACnB,wBAAgB,uBAAuB,CAAC,KAAK,EAAE,SAAS,cAAc,EAAE,GAAG,MAAM,CAKhF;AAED,oCAAoC;AACpC,wBAAgB,aAAa,CAC3B,KAAK,EAAE,SAAS,cAAc,EAAE,EAChC,QAAQ,CAAC,EAAE,SAAS,iBAAiB,EAAE,GACtC,MAAM,CAsBR;AAED,8BAA8B;AAC9B,wBAAgB,aAAa,CAC3B,KAAK,EAAE,SAAS,cAAc,EAAE,EAChC,OAAO,CAAC,EAAE;IACR,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,EAAE,SAAS,iBAAiB,EAAE,CAAC;CACzC,GACA,MAAM,CAoBR;AAED,sEAAsE;AACtE,wBAAgB,iBAAiB,CAC/B,KAAK,EAAE,SAAS,cAAc,EAAE,EAChC,OAAO,CAAC,EAAE;IACR,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,EAAE,SAAS,iBAAiB,EAAE,CAAC;CACzC,GACA,WAAW,CAab;AAED,oBAAoB;AACpB,wBAAgB,iBAAiB,CAC/B,KAAK,EAAE,SAAS,cAAc,EAAE,EAChC,QAAQ,CAAC,EAAE,SAAS,iBAAiB,EAAE,GACtC,WAAW,CASb;AAED,cAAc;AACd,wBAAgB,qBAAqB,IAAI,MAAM,CAK9C"}