@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.
- package/LICENSE +21 -0
- package/README.md +93 -0
- package/lib/board-sender.d.ts +14 -0
- package/lib/board-sender.d.ts.map +1 -0
- package/lib/board-sender.js +7 -0
- package/lib/board-sender.js.map +1 -0
- package/lib/choice-keyboard.d.ts +44 -0
- package/lib/choice-keyboard.d.ts.map +1 -0
- package/lib/choice-keyboard.js +67 -0
- package/lib/choice-keyboard.js.map +1 -0
- package/lib/command-alias.d.ts +21 -0
- package/lib/command-alias.d.ts.map +1 -0
- package/lib/command-alias.js +22 -0
- package/lib/command-alias.js.map +1 -0
- package/lib/command-help.d.ts +19 -0
- package/lib/command-help.d.ts.map +1 -0
- package/lib/command-help.js +91 -0
- package/lib/command-help.js.map +1 -0
- package/lib/command-message.d.ts +25 -0
- package/lib/command-message.d.ts.map +1 -0
- package/lib/command-message.js +58 -0
- package/lib/command-message.js.map +1 -0
- package/lib/game-action-alias.d.ts +8 -0
- package/lib/game-action-alias.d.ts.map +1 -0
- package/lib/game-action-alias.js +117 -0
- package/lib/game-action-alias.js.map +1 -0
- package/lib/game-commands.d.ts +3 -0
- package/lib/game-commands.d.ts.map +1 -0
- package/lib/game-commands.js +5 -0
- package/lib/game-commands.js.map +1 -0
- package/lib/game-hub-feature.d.ts +58 -0
- package/lib/game-hub-feature.d.ts.map +1 -0
- package/lib/game-hub-feature.js +100 -0
- package/lib/game-hub-feature.js.map +1 -0
- package/lib/game-hub-flow.d.ts +9 -0
- package/lib/game-hub-flow.d.ts.map +1 -0
- package/lib/game-hub-flow.js +90 -0
- package/lib/game-hub-flow.js.map +1 -0
- package/lib/game-hub-menu-context.d.ts +25 -0
- package/lib/game-hub-menu-context.d.ts.map +1 -0
- package/lib/game-hub-menu-context.js +64 -0
- package/lib/game-hub-menu-context.js.map +1 -0
- package/lib/game-hub-menu.d.ts +37 -0
- package/lib/game-hub-menu.d.ts.map +1 -0
- package/lib/game-hub-menu.js +162 -0
- package/lib/game-hub-menu.js.map +1 -0
- package/lib/game-hub-mount.d.ts +9 -0
- package/lib/game-hub-mount.d.ts.map +1 -0
- package/lib/game-hub-mount.js +108 -0
- package/lib/game-hub-mount.js.map +1 -0
- package/lib/game-interactive.d.ts +25 -0
- package/lib/game-interactive.d.ts.map +1 -0
- package/lib/game-interactive.js +62 -0
- package/lib/game-interactive.js.map +1 -0
- package/lib/game-middleware.d.ts +6 -0
- package/lib/game-middleware.d.ts.map +1 -0
- package/lib/game-middleware.js +9 -0
- package/lib/game-middleware.js.map +1 -0
- package/lib/game-onboarding.d.ts +8 -0
- package/lib/game-onboarding.d.ts.map +1 -0
- package/lib/game-onboarding.js +66 -0
- package/lib/game-onboarding.js.map +1 -0
- package/lib/game-records.d.ts +52 -0
- package/lib/game-records.d.ts.map +1 -0
- package/lib/game-records.js +206 -0
- package/lib/game-records.js.map +1 -0
- package/lib/game-session.d.ts +64 -0
- package/lib/game-session.d.ts.map +1 -0
- package/lib/game-session.js +44 -0
- package/lib/game-session.js.map +1 -0
- package/lib/grid-keyboard.d.ts +67 -0
- package/lib/grid-keyboard.d.ts.map +1 -0
- package/lib/grid-keyboard.js +104 -0
- package/lib/grid-keyboard.js.map +1 -0
- package/lib/help-card.d.ts +12 -0
- package/lib/help-card.d.ts.map +1 -0
- package/lib/help-card.js +129 -0
- package/lib/help-card.js.map +1 -0
- package/lib/index.d.ts +29 -0
- package/lib/index.d.ts.map +1 -0
- package/lib/index.js +29 -0
- package/lib/index.js.map +1 -0
- package/lib/interaction-profiles.d.ts +16 -0
- package/lib/interaction-profiles.d.ts.map +1 -0
- package/lib/interaction-profiles.js +35 -0
- package/lib/interaction-profiles.js.map +1 -0
- package/lib/memory-db.d.ts +41 -0
- package/lib/memory-db.d.ts.map +1 -0
- package/lib/memory-db.js +92 -0
- package/lib/memory-db.js.map +1 -0
- package/lib/random.d.ts +6 -0
- package/lib/random.d.ts.map +1 -0
- package/lib/random.js +29 -0
- package/lib/random.js.map +1 -0
- package/lib/runtime-hub.d.ts +28 -0
- package/lib/runtime-hub.d.ts.map +1 -0
- package/lib/runtime-hub.js +66 -0
- package/lib/runtime-hub.js.map +1 -0
- package/lib/send-content-text.d.ts +5 -0
- package/lib/send-content-text.d.ts.map +1 -0
- package/lib/send-content-text.js +24 -0
- package/lib/send-content-text.js.map +1 -0
- package/package.json +49 -0
- package/src/board-sender.ts +16 -0
- package/src/choice-keyboard.ts +117 -0
- package/src/command-alias.ts +45 -0
- package/src/command-help.ts +117 -0
- package/src/command-message.ts +76 -0
- package/src/game-action-alias.ts +129 -0
- package/src/game-commands.ts +4 -0
- package/src/game-hub-feature.ts +151 -0
- package/src/game-hub-flow.ts +114 -0
- package/src/game-hub-menu-context.ts +94 -0
- package/src/game-hub-menu.ts +223 -0
- package/src/game-hub-mount.ts +172 -0
- package/src/game-interactive.ts +88 -0
- package/src/game-middleware.ts +14 -0
- package/src/game-onboarding.ts +67 -0
- package/src/game-records.ts +287 -0
- package/src/game-session.ts +97 -0
- package/src/grid-keyboard.ts +180 -0
- package/src/help-card.ts +188 -0
- package/src/index.ts +29 -0
- package/src/interaction-profiles.ts +56 -0
- package/src/memory-db.ts +128 -0
- package/src/random.ts +32 -0
- package/src/runtime-hub.ts +86 -0
- package/src/send-content-text.ts +21 -0
package/src/random.ts
ADDED
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { randomInt, randomUUID } from 'node:crypto';
|
|
2
|
+
|
|
3
|
+
export function secureRandomInt(maxExclusive: number): number {
|
|
4
|
+
if (!Number.isSafeInteger(maxExclusive) || maxExclusive <= 0) {
|
|
5
|
+
throw new RangeError('maxExclusive must be a positive safe integer');
|
|
6
|
+
}
|
|
7
|
+
return randomInt(maxExclusive);
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export function secureRandomIntInclusive(min: number, max: number): number {
|
|
11
|
+
if (!Number.isSafeInteger(min) || !Number.isSafeInteger(max) || max < min) {
|
|
12
|
+
throw new RangeError('invalid inclusive random range');
|
|
13
|
+
}
|
|
14
|
+
return min + randomInt(max - min + 1);
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export function secureRandomItem<T>(items: readonly T[]): T {
|
|
18
|
+
if (items.length === 0) throw new RangeError('cannot choose from an empty array');
|
|
19
|
+
return items[secureRandomInt(items.length)]!;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export function secureShuffleInPlace<T>(items: T[]): T[] {
|
|
23
|
+
for (let i = items.length - 1; i > 0; i--) {
|
|
24
|
+
const j = secureRandomInt(i + 1);
|
|
25
|
+
[items[i], items[j]] = [items[j]!, items[i]!];
|
|
26
|
+
}
|
|
27
|
+
return items;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export function generateCompactId(prefix: string): string {
|
|
31
|
+
return `${prefix}${Date.now().toString(36)}${randomUUID().replaceAll('-', '').slice(0, 8)}`;
|
|
32
|
+
}
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Plugin Runtime game hub — module-level registry (no legacy Feature / getPlugin).
|
|
3
|
+
* Games call `registerRuntimeGame` from `setup()`; hub plugin lists them.
|
|
4
|
+
*/
|
|
5
|
+
export interface RuntimeGameMenuAction {
|
|
6
|
+
readonly id: string;
|
|
7
|
+
readonly label: string;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export interface RuntimeRegisteredGame {
|
|
11
|
+
readonly id: string;
|
|
12
|
+
readonly title: string;
|
|
13
|
+
readonly icon: string;
|
|
14
|
+
readonly description: string;
|
|
15
|
+
readonly commandPrefix: string;
|
|
16
|
+
readonly quickStart?: string;
|
|
17
|
+
readonly aliases?: readonly string[];
|
|
18
|
+
readonly menus?: readonly RuntimeGameMenuAction[];
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
const games = new Map<string, RuntimeRegisteredGame[]>();
|
|
22
|
+
|
|
23
|
+
export function registerRuntimeGame(game: RuntimeRegisteredGame): () => void {
|
|
24
|
+
const registered = Object.freeze({ ...game });
|
|
25
|
+
const registrations = games.get(game.id) ?? [];
|
|
26
|
+
registrations.push(registered);
|
|
27
|
+
games.set(game.id, registrations);
|
|
28
|
+
return () => {
|
|
29
|
+
const current = games.get(game.id);
|
|
30
|
+
if (!current) return;
|
|
31
|
+
const index = current.lastIndexOf(registered);
|
|
32
|
+
if (index >= 0) current.splice(index, 1);
|
|
33
|
+
if (current.length === 0) games.delete(game.id);
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
export function getRuntimeGames(): readonly RuntimeRegisteredGame[] {
|
|
38
|
+
const active = [...games.values()]
|
|
39
|
+
.map((registrations) => registrations[registrations.length - 1])
|
|
40
|
+
.filter((game): game is RuntimeRegisteredGame => Boolean(game));
|
|
41
|
+
return Object.freeze(active.sort((a, b) => a.id.localeCompare(b.id)));
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
export function getRuntimeGame(id: string): RuntimeRegisteredGame | undefined {
|
|
45
|
+
const directRegistrations = games.get(id);
|
|
46
|
+
const direct = directRegistrations?.[directRegistrations.length - 1];
|
|
47
|
+
if (direct) return direct;
|
|
48
|
+
// 别名解析(如 bj、ttt、21点)
|
|
49
|
+
for (const registrations of games.values()) {
|
|
50
|
+
const game = registrations[registrations.length - 1];
|
|
51
|
+
if (!game) continue;
|
|
52
|
+
if (game.aliases?.includes(id)) return game;
|
|
53
|
+
}
|
|
54
|
+
return undefined;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
export function resetRuntimeGamesForTests(): void {
|
|
58
|
+
games.clear();
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
export function formatRuntimeGamesHelp(): string {
|
|
62
|
+
const list = getRuntimeGames();
|
|
63
|
+
if (list.length === 0) {
|
|
64
|
+
return [
|
|
65
|
+
'🎮 游戏大厅',
|
|
66
|
+
'',
|
|
67
|
+
'暂无已加载的游戏插件。',
|
|
68
|
+
'安装并启用 `@zhin.js/plugin-guess-number` 等游戏包后重试。',
|
|
69
|
+
].join('\n');
|
|
70
|
+
}
|
|
71
|
+
const lines = ['🎮 游戏大厅', '', '已加载:'];
|
|
72
|
+
for (const g of list) {
|
|
73
|
+
const start = g.quickStart ? ` ${g.quickStart}` : '';
|
|
74
|
+
lines.push(`${g.icon} **${g.title}** — \`${g.commandPrefix}${start}\``);
|
|
75
|
+
lines.push(` ${g.description}`);
|
|
76
|
+
}
|
|
77
|
+
lines.push('');
|
|
78
|
+
lines.push('发送对应命令开始;进行中可按各游戏说明直接回复。');
|
|
79
|
+
return lines.join('\n');
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
/** Default idle timeout for stale-session cron (30 minutes). */
|
|
83
|
+
export const DEFAULT_GAME_STALE_IDLE_MS = 30 * 60 * 1000;
|
|
84
|
+
|
|
85
|
+
/** Solar cron: every 10 minutes. */
|
|
86
|
+
export const DEFAULT_GAME_STALE_CRON = '0 */10 * * * *';
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Extract plain text from SendContent (skip buttons / keyboard) for text-only game paths.
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
export function plainTextFromSendContent(content: unknown): string {
|
|
6
|
+
if (content == null) return '';
|
|
7
|
+
if (typeof content === 'string') return content;
|
|
8
|
+
if (Array.isArray(content)) {
|
|
9
|
+
return content
|
|
10
|
+
.map(plainTextFromSendContent)
|
|
11
|
+
.filter((s) => s.length > 0)
|
|
12
|
+
.join('\n');
|
|
13
|
+
}
|
|
14
|
+
if (typeof content === 'object' && content !== null && 'type' in content) {
|
|
15
|
+
const seg = content as { type: unknown; data?: { text?: unknown; label?: unknown } };
|
|
16
|
+
if (seg.type === 'text') return String(seg.data?.text ?? '');
|
|
17
|
+
// buttons / keyboard / other segments omitted in text-only mode
|
|
18
|
+
return '';
|
|
19
|
+
}
|
|
20
|
+
return '';
|
|
21
|
+
}
|