@zhin.js/plugin-guess-number 1.1.18 → 1.1.20
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/commands/guess/[[action]]/index.js +5 -4
- package/commands/guess/[[action]]/index.ts +5 -4
- package/lib/game-flow.d.ts +4 -4
- package/lib/game-flow.js +65 -58
- package/lib/game-flow.js.map +1 -1
- package/lib/guess-command.d.ts +3 -3
- package/lib/guess-command.js +27 -25
- package/lib/guess-command.js.map +1 -1
- package/lib/session-service.d.ts +2 -2
- package/lib/session-service.js +4 -9
- package/lib/session-service.js.map +1 -1
- package/middlewares/guess-alias/index.js +12 -8
- package/middlewares/guess-alias/index.ts +11 -7
- package/middlewares/guess-choice/index.js +8 -3
- package/middlewares/guess-choice/index.ts +7 -9
- package/middlewares/guess-digit/index.js +8 -7
- package/middlewares/guess-digit/index.ts +5 -7
- package/package.json +7 -7
- package/src/game-flow.ts +75 -69
- package/src/guess-command.ts +27 -26
- package/src/session-service.ts +6 -12
|
@@ -1,17 +1,18 @@
|
|
|
1
1
|
// Generated by build-plugin-runtime-entries.mjs. Do not edit.
|
|
2
|
+
import { replyGameOperation, gamePlayerFromInput, normalizeGuessAction } from '@zhin.js/game-kit';
|
|
2
3
|
import { defineCommand } from 'zhin.js/command';
|
|
3
|
-
import { messageFromCommandInput, normalizeGuessAction } from '@zhin.js/game-kit';
|
|
4
4
|
import { getGuessHelp, runGuessCommand } from "../../../lib/guess-command.js";
|
|
5
5
|
import { resolveGameServices } from "../../../lib/runtime-store.js";
|
|
6
6
|
export default defineCommand({
|
|
7
7
|
description: 'Guess Number',
|
|
8
8
|
params: { action: { type: 'string', default: '' } },
|
|
9
|
-
async execute({ params, input, use, locale, formatCommand }) {
|
|
9
|
+
async execute({ params, input, use, locale, formatCommand, session }) {
|
|
10
10
|
const action = normalizeGuessAction(String(params.action ?? ''));
|
|
11
11
|
if (!action || action === 'help')
|
|
12
12
|
return getGuessHelp({ locale, formatCommand });
|
|
13
13
|
const services = resolveGameServices({ use });
|
|
14
|
-
const message =
|
|
15
|
-
|
|
14
|
+
const message = gamePlayerFromInput(input, { locale, formatCommand });
|
|
15
|
+
await replyGameOperation({ session: services, player: message, reply: session?.reply,
|
|
16
|
+
run: () => runGuessCommand(services, message, action) });
|
|
16
17
|
},
|
|
17
18
|
});
|
|
@@ -1,16 +1,17 @@
|
|
|
1
|
+
import { replyGameOperation, gamePlayerFromInput, normalizeGuessAction } from '@zhin.js/game-kit';
|
|
1
2
|
import { defineCommand } from 'zhin.js/command';
|
|
2
|
-
import { messageFromCommandInput, normalizeGuessAction } from '@zhin.js/game-kit';
|
|
3
3
|
import { getGuessHelp, runGuessCommand } from '../../../src/guess-command.js';
|
|
4
4
|
import { resolveGameServices } from '../../../src/runtime-store.js';
|
|
5
5
|
|
|
6
6
|
export default defineCommand({
|
|
7
7
|
description: 'Guess Number',
|
|
8
8
|
params: { action: { type: 'string', default: '' } },
|
|
9
|
-
async execute({ params, input, use, locale, formatCommand }) {
|
|
9
|
+
async execute({ params, input, use, locale, formatCommand, session }) {
|
|
10
10
|
const action = normalizeGuessAction(String(params.action ?? ''));
|
|
11
11
|
if (!action || action === 'help') return getGuessHelp({ locale, formatCommand });
|
|
12
12
|
const services = resolveGameServices({ use });
|
|
13
|
-
const message =
|
|
14
|
-
|
|
13
|
+
const message = gamePlayerFromInput(input, { locale, formatCommand });
|
|
14
|
+
await replyGameOperation({ session: services, player: message, reply: session?.reply,
|
|
15
|
+
run: () => runGuessCommand(services, message, action) });
|
|
15
16
|
},
|
|
16
17
|
});
|
package/lib/game-flow.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import type
|
|
1
|
+
import { type GamePlayer, type GameReply } from '@zhin.js/game-kit';
|
|
2
2
|
import type { SessionService } from './session-service.js';
|
|
3
|
-
export declare function startGame(services: SessionService, message:
|
|
4
|
-
export declare function processGuess(services: SessionService, message:
|
|
5
|
-
export declare function handleGuessChoice(services: SessionService, message:
|
|
3
|
+
export declare function startGame(services: SessionService, message: GamePlayer): Promise<GameReply>;
|
|
4
|
+
export declare function processGuess(services: SessionService, message: GamePlayer, value: number): Promise<GameReply | null>;
|
|
5
|
+
export declare function handleGuessChoice(services: SessionService, message: GamePlayer, sessionId: string, choiceId: string): Promise<GameReply>;
|
package/lib/game-flow.js
CHANGED
|
@@ -1,69 +1,76 @@
|
|
|
1
1
|
import { createMessages, resolveLocale } from 'zhin.js/i18n';
|
|
2
|
+
import { channelKey } from '@zhin.js/game-kit';
|
|
2
3
|
import { evaluateGuess, hintText, MAX, MIN } from './engine.js';
|
|
3
4
|
import { buildGuessView } from './view.js';
|
|
4
5
|
export async function startGame(services, message) {
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
6
|
+
return services.runInScope({ scope: channelKey(message), run: async () => {
|
|
7
|
+
const ch = channelKey(message);
|
|
8
|
+
const mine = await services.getActiveForUser(ch, message.sender.id);
|
|
9
|
+
const locale = resolveLocale(mine ? mine.locale : message.locale);
|
|
10
|
+
if (mine) {
|
|
11
|
+
return buildGuessView(mine, [messages(locale, 'text1')], message.target.kind);
|
|
12
|
+
}
|
|
13
|
+
const session = await services.createSession(message);
|
|
14
|
+
return buildGuessView(session, [], message.target.kind);
|
|
15
|
+
} });
|
|
13
16
|
}
|
|
14
17
|
export async function processGuess(services, message, value) {
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
18
|
+
return services.runInScope({ scope: channelKey(message), run: async () => {
|
|
19
|
+
const ch = channelKey(message);
|
|
20
|
+
const session = await services.getActiveForUser(ch, message.sender.id);
|
|
21
|
+
const locale = resolveLocale(session ? session.locale : message.locale);
|
|
22
|
+
if (!session)
|
|
23
|
+
return null;
|
|
24
|
+
const result = evaluateGuess(session.secret, value);
|
|
25
|
+
if (result === 'invalid') {
|
|
26
|
+
return messages(locale, 'text2', { p0: MIN, p1: MAX });
|
|
27
|
+
}
|
|
28
|
+
const attempts = session.attempts + 1;
|
|
29
|
+
if (result === 'win') {
|
|
30
|
+
await services.updateSession(session.id, { attempts, status: 'won' });
|
|
31
|
+
const updated = (await services.getById(session.id));
|
|
32
|
+
return buildGuessView(updated, [messages(locale, 'text3', { p0: attempts })], message.target.kind);
|
|
33
|
+
}
|
|
34
|
+
if (attempts >= session.max_attempts) {
|
|
35
|
+
await services.updateSession(session.id, { attempts, status: 'lost' });
|
|
36
|
+
const updated = (await services.getById(session.id));
|
|
37
|
+
return buildGuessView(updated, [], message.target.kind);
|
|
38
|
+
}
|
|
39
|
+
let rangeMin = session.range_min;
|
|
40
|
+
let rangeMax = session.range_max;
|
|
41
|
+
if (result === 'low')
|
|
42
|
+
rangeMin = Math.max(rangeMin, value + 1);
|
|
43
|
+
else
|
|
44
|
+
rangeMax = Math.min(rangeMax, value - 1);
|
|
45
|
+
await services.updateSession(session.id, { attempts, range_min: rangeMin, range_max: rangeMax });
|
|
46
|
+
const updated = (await services.getById(session.id));
|
|
47
|
+
return buildGuessView(updated, [hintText(result, rangeMin, rangeMax, locale)], message.target.kind);
|
|
48
|
+
} });
|
|
44
49
|
}
|
|
45
50
|
export async function handleGuessChoice(services, message, sessionId, choiceId) {
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
51
|
+
return services.runInScope({ scope: channelKey(message), run: async () => {
|
|
52
|
+
const session = await services.getById(sessionId);
|
|
53
|
+
const locale = resolveLocale(session ? session.locale : message.locale);
|
|
54
|
+
if (!session)
|
|
55
|
+
return messages(locale, 'text4');
|
|
56
|
+
if (session.channel_key
|
|
57
|
+
!== channelKey(message)) {
|
|
58
|
+
return messages(locale, 'text5');
|
|
59
|
+
}
|
|
60
|
+
if (session.player_id !== message.sender.id)
|
|
61
|
+
return messages(locale, 'text6');
|
|
62
|
+
if (choiceId === 'restart' && session.status !== 'active') {
|
|
63
|
+
return startGame(services, message);
|
|
64
|
+
}
|
|
65
|
+
if (choiceId === 'quit' && session.status === 'active') {
|
|
66
|
+
await services.updateSession(session.id, { status: 'aborted' });
|
|
67
|
+
const updated = (await services.getById(session.id));
|
|
68
|
+
return buildGuessView(updated, [messages(locale, 'text7')], message.target.kind);
|
|
69
|
+
}
|
|
70
|
+
return session.status === 'active'
|
|
71
|
+
? messages(locale, 'text8')
|
|
72
|
+
: buildGuessView(session, [], message.target.kind);
|
|
73
|
+
} });
|
|
67
74
|
}
|
|
68
75
|
const messages = createMessages({
|
|
69
76
|
text1: { 'zh-CN': "你已有进行中的一局,请继续猜数字。", en: "You already have an active game. Keep guessing." },
|
package/lib/game-flow.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"game-flow.js","sourceRoot":"","sources":["../src/game-flow.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;
|
|
1
|
+
{"version":3,"file":"game-flow.js","sourceRoot":"","sources":["../src/game-flow.ts"],"names":[],"mappings":"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"}
|
package/lib/guess-command.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { type
|
|
1
|
+
import { type GamePlayer, type GameReply } from '@zhin.js/game-kit';
|
|
2
2
|
import type { SessionService } from './session-service.js';
|
|
3
|
-
export declare function getGuessHelp(message?: Pick<
|
|
3
|
+
export declare function getGuessHelp(message?: Pick<GamePlayer, 'locale' | 'formatCommand'>): string;
|
|
4
4
|
export declare const GUESS_HELP: string;
|
|
5
|
-
export declare function runGuessCommand(services: SessionService, message:
|
|
5
|
+
export declare function runGuessCommand(services: SessionService, message: GamePlayer, action: string): Promise<GameReply>;
|
package/lib/guess-command.js
CHANGED
|
@@ -3,34 +3,36 @@ import { createMessages } from 'zhin.js/i18n';
|
|
|
3
3
|
import { startGame } from './game-flow.js';
|
|
4
4
|
import { buildGuessView } from './view.js';
|
|
5
5
|
export function getGuessHelp(message = {}) {
|
|
6
|
-
return formatGameHelp(message, { 'zh-CN': "猜数字(1~100,7 次机会)", en: "Guess the Number (1–100, seven guesses)" }, 'guess', ['start', 'quit'], { 'zh-CN': "进行中直接回复范围内的整数。", en: "During a game, reply with an integer in the range." });
|
|
6
|
+
return formatGameHelp({ presentation: message, title: { 'zh-CN': "猜数字(1~100,7 次机会)", en: "Guess the Number (1–100, seven guesses)" }, commandPath: 'guess', actions: ['start', 'quit'], footer: { 'zh-CN': "进行中直接回复范围内的整数。", en: "During a game, reply with an integer in the range." } });
|
|
7
7
|
}
|
|
8
8
|
export const GUESS_HELP = getGuessHelp();
|
|
9
9
|
export async function runGuessCommand(services, message, action) {
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
10
|
+
return services.runInScope({ scope: channelKey(message), run: async () => {
|
|
11
|
+
const ch = channelKey(message);
|
|
12
|
+
const userId = message.sender.id;
|
|
13
|
+
if (!action || action === 'help') {
|
|
14
|
+
const active = await services.getActiveForUser(ch, userId);
|
|
15
|
+
const lines = [getGuessHelp(message), ''];
|
|
16
|
+
if (active) {
|
|
17
|
+
return buildGuessView(active, [getGuessHelp(message)], message.target.kind);
|
|
18
|
+
}
|
|
19
|
+
else {
|
|
20
|
+
lines.push(gameCommandFeedback(message, 'noGame'), gameCommandHint(message, 'guess'));
|
|
21
|
+
}
|
|
22
|
+
return lines.join('\n');
|
|
23
|
+
}
|
|
24
|
+
if (action === 'start')
|
|
25
|
+
return startGame(services, message);
|
|
26
|
+
if (action === 'quit') {
|
|
27
|
+
const row = await services.getActiveForUser(ch, userId);
|
|
28
|
+
if (!row)
|
|
29
|
+
return gameCommandFeedback(message, 'noGame');
|
|
30
|
+
await services.updateSession(row.id, { status: 'aborted' });
|
|
31
|
+
const updated = (await services.getById(row.id));
|
|
32
|
+
return buildGuessView(updated, [messages(row.locale ?? undefined, 'text1')], message.target.kind);
|
|
33
|
+
}
|
|
34
|
+
return gameCommandFeedback(message, 'unknown', action) + '\n\n' + getGuessHelp(message);
|
|
35
|
+
} });
|
|
34
36
|
}
|
|
35
37
|
const messages = createMessages({
|
|
36
38
|
text1: { 'zh-CN': "已放弃本局。", en: "You quit this game." },
|
package/lib/guess-command.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"guess-command.js","sourceRoot":"","sources":["../src/guess-command.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,mBAAmB,EAAE,eAAe,EAAE,UAAU,
|
|
1
|
+
{"version":3,"file":"guess-command.js","sourceRoot":"","sources":["../src/guess-command.ts"],"names":[],"mappings":"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"}
|
package/lib/session-service.d.ts
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import type { Database, Models } from '@zhin.js/core';
|
|
2
|
-
import { BaseSessionService, type
|
|
2
|
+
import { BaseSessionService, type GamePlayer } from '@zhin.js/game-kit';
|
|
3
3
|
import type { GuessSessionRow } from './models.js';
|
|
4
4
|
export type GuessDatabase = Database<unknown, Models, string>;
|
|
5
5
|
export declare class SessionService extends BaseSessionService<GuessSessionRow> {
|
|
6
6
|
constructor(db: GuessDatabase);
|
|
7
|
-
createSession(message:
|
|
7
|
+
createSession(message: GamePlayer): Promise<GuessSessionRow>;
|
|
8
8
|
}
|
|
9
9
|
export declare function createServices(db: GuessDatabase): SessionService;
|
package/lib/session-service.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { BaseSessionService,
|
|
1
|
+
import { BaseSessionService, generateSessionId, } from '@zhin.js/game-kit';
|
|
2
2
|
import { MAX, MAX_ATTEMPTS, MIN, newSecret } from './engine.js';
|
|
3
3
|
export class SessionService extends BaseSessionService {
|
|
4
4
|
constructor(db) {
|
|
@@ -27,13 +27,8 @@ export class SessionService extends BaseSessionService {
|
|
|
27
27
|
const row = {
|
|
28
28
|
id: generateSessionId(),
|
|
29
29
|
locale: message.locale ?? 'zh-CN',
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
channel_type: message.$channel.type,
|
|
33
|
-
channel_id: message.$channel.id,
|
|
34
|
-
channel_key: channelKey(message),
|
|
35
|
-
player_id: message.$sender.id,
|
|
36
|
-
player_name: message.$sender.name?.trim() || message.$sender.id,
|
|
30
|
+
player_id: message.sender.id,
|
|
31
|
+
player_name: message.sender.name?.trim() || message.sender.id,
|
|
37
32
|
secret: newSecret(),
|
|
38
33
|
range_min: MIN,
|
|
39
34
|
range_max: MAX,
|
|
@@ -43,7 +38,7 @@ export class SessionService extends BaseSessionService {
|
|
|
43
38
|
updated_at: now,
|
|
44
39
|
created_at: now,
|
|
45
40
|
};
|
|
46
|
-
return this.createRow(row);
|
|
41
|
+
return this.createRow({ target: message.target, fields: row });
|
|
47
42
|
}
|
|
48
43
|
}
|
|
49
44
|
export function createServices(db) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"session-service.js","sourceRoot":"","sources":["../src/session-service.ts"],"names":[],"mappings":"AACA,OAAO,EACL,kBAAkB,EAClB,
|
|
1
|
+
{"version":3,"file":"session-service.js","sourceRoot":"","sources":["../src/session-service.ts"],"names":[],"mappings":"AACA,OAAO,EACL,kBAAkB,EAClB,iBAAiB,GAGlB,MAAM,mBAAmB,CAAC;AAC3B,OAAO,EAAE,GAAG,EAAE,YAAY,EAAE,GAAG,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AAKhE,MAAM,OAAO,cAAe,SAAQ,kBAAmC;IACrE,YAAY,EAAiB;QAC3B,KAAK,CAAC,EAAqD,EAAE;YAC3D,MAAM,EAAE,OAAO;YACf,KAAK,EAAE,gBAAgB;YACvB,UAAU,EAAE,CAAC,WAAW,CAAC;YACzB,eAAe,EAAE,CAAC,OAAO,EAAE,EAAE,CAAC,OAAO,CAAC,MAAM,KAAK,KAAK;gBACpD,CAAC,CAAC,CAAC;wBACC,MAAM,EAAE,OAAO,CAAC,SAAS;wBACzB,QAAQ,EAAE,OAAO,CAAC,WAAW;wBAC7B,MAAM,EAAE,KAAK;wBACb,KAAK,EAAE,IAAI,CAAC,GAAG,CACb,CAAC,EACD,OAAO,CAAC,YAAY,GAAG,OAAO,CAAC,QAAQ,GAAG,CAAC,CAC5C,GAAG,EAAE;qBACP,CAAC;gBACJ,CAAC,CAAC,OAAO,CAAC,MAAM,KAAK,MAAM;oBACzB,CAAC,CAAC,CAAC;4BACC,MAAM,EAAE,OAAO,CAAC,SAAS;4BACzB,QAAQ,EAAE,OAAO,CAAC,WAAW;4BAC7B,MAAM,EAAE,MAAM;yBACf,CAAC;oBACJ,CAAC,CAAC,EAAE;SACT,CAAC,CAAC;IACL,CAAC;IAED,KAAK,CAAC,aAAa,CAAC,OAAmB;QACrC,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;QACvB,MAAM,GAAG,GAAkG;YACzG,EAAE,EAAE,iBAAiB,EAAE;YACvB,MAAM,EAAE,OAAO,CAAC,MAAM,IAAI,OAAO;YACjC,SAAS,EAAE,OAAO,CAAC,MAAM,CAAC,EAAE;YAC5B,WAAW,EAAE,OAAO,CAAC,MAAM,CAAC,IAAI,EAAE,IAAI,EAAE,IAAI,OAAO,CAAC,MAAM,CAAC,EAAE;YAC7D,MAAM,EAAE,SAAS,EAAE;YACnB,SAAS,EAAE,GAAG;YACd,SAAS,EAAE,GAAG;YACd,QAAQ,EAAE,CAAC;YACX,YAAY,EAAE,YAAY;YAC1B,MAAM,EAAE,QAAQ;YAChB,UAAU,EAAE,GAAG;YACf,UAAU,EAAE,GAAG;SAChB,CAAC;QACF,OAAO,IAAI,CAAC,SAAS,CAAC,EAAE,MAAM,EAAE,OAAO,CAAC,MAAM,EAAE,MAAM,EAAE,GAAG,EAAE,CAAC,CAAC;IACjE,CAAC;CACF;AAED,MAAM,UAAU,cAAc,CAAC,EAAiB;IAC9C,OAAO,IAAI,cAAc,CAAC,EAAE,CAAC,CAAC;AAChC,CAAC"}
|
|
@@ -1,20 +1,24 @@
|
|
|
1
1
|
// Generated by build-plugin-runtime-entries.mjs. Do not edit.
|
|
2
|
-
import { defineGameCommandAliasMiddleware, isGuessAction,
|
|
2
|
+
import { replyGameOperation, defineGameCommandAliasMiddleware, isGuessAction, gamePlayerFromInput, normalizeGuessAction, } from '@zhin.js/game-kit';
|
|
3
3
|
import { getGuessHelp, runGuessCommand } from "../../lib/guess-command.js";
|
|
4
4
|
import { resolveGameServices } from "../../lib/runtime-store.js";
|
|
5
5
|
export default defineGameCommandAliasMiddleware({
|
|
6
6
|
aliases: ['猜数', 'guess'],
|
|
7
7
|
async run(action, input, context) {
|
|
8
|
-
const
|
|
9
|
-
formatCommand: (path) => context.input.formatCommand?.(path, context.owner.id)
|
|
10
|
-
});
|
|
8
|
+
const presentation = { locale: context.input.locale,
|
|
9
|
+
formatCommand: (path) => context.input.formatCommand?.(path, context.owner.id) };
|
|
11
10
|
const normalized = normalizeGuessAction(String(action ?? ''));
|
|
12
|
-
if (normalized === 'help')
|
|
13
|
-
|
|
11
|
+
if (normalized === 'help') {
|
|
12
|
+
await context.input.$reply(getGuessHelp(presentation));
|
|
13
|
+
return true;
|
|
14
|
+
}
|
|
14
15
|
// action 无法识别:放行给后续中间件,避免劫持普通聊天
|
|
15
16
|
if (!isGuessAction(normalized))
|
|
16
|
-
return
|
|
17
|
+
return false;
|
|
18
|
+
const message = gamePlayerFromInput(input, presentation);
|
|
17
19
|
const services = resolveGameServices(context);
|
|
18
|
-
|
|
20
|
+
await replyGameOperation({ session: services, player: message, reply: content => context.input.$reply(content),
|
|
21
|
+
run: () => runGuessCommand(services, message, normalized) });
|
|
22
|
+
return true;
|
|
19
23
|
},
|
|
20
24
|
});
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import {
|
|
2
|
+
replyGameOperation,
|
|
2
3
|
defineGameCommandAliasMiddleware,
|
|
3
4
|
isGuessAction,
|
|
4
|
-
|
|
5
|
+
gamePlayerFromInput,
|
|
5
6
|
normalizeGuessAction,
|
|
6
7
|
} from '@zhin.js/game-kit';
|
|
7
8
|
import { getGuessHelp, runGuessCommand } from '../../src/guess-command.js';
|
|
@@ -10,14 +11,17 @@ import { resolveGameServices } from '../../src/runtime-store.js';
|
|
|
10
11
|
export default defineGameCommandAliasMiddleware({
|
|
11
12
|
aliases: ['猜数', 'guess'],
|
|
12
13
|
async run(action, input, context) {
|
|
13
|
-
const
|
|
14
|
-
formatCommand: (path) => context.input.formatCommand?.(path, context.owner.id)
|
|
15
|
-
|
|
14
|
+
const presentation = { locale: context.input.locale,
|
|
15
|
+
formatCommand: (path: string) => context.input.formatCommand?.(path, context.owner.id) };
|
|
16
|
+
|
|
16
17
|
const normalized = normalizeGuessAction(String(action ?? ''));
|
|
17
|
-
if (normalized === 'help')
|
|
18
|
+
if (normalized === 'help') { await context.input.$reply(getGuessHelp(presentation)); return true; }
|
|
18
19
|
// action 无法识别:放行给后续中间件,避免劫持普通聊天
|
|
19
|
-
if (!isGuessAction(normalized)) return
|
|
20
|
+
if (!isGuessAction(normalized)) return false;
|
|
21
|
+
const message = gamePlayerFromInput(input, presentation);
|
|
20
22
|
const services = resolveGameServices(context);
|
|
21
|
-
|
|
23
|
+
await replyGameOperation({ session: services, player: message, reply: content => context.input.$reply(content),
|
|
24
|
+
run: () => runGuessCommand(services, message, normalized) });
|
|
25
|
+
return true;
|
|
22
26
|
},
|
|
23
27
|
});
|
|
@@ -1,12 +1,16 @@
|
|
|
1
1
|
// Generated by build-plugin-runtime-entries.mjs. Do not edit.
|
|
2
|
+
import { replyGameOperation, gamePlayerFromInput, parseChoicePayload, resolveGameTextPayload } from '@zhin.js/game-kit';
|
|
2
3
|
import { defineMiddleware } from 'zhin.js/middleware';
|
|
3
|
-
import { messageFromCommandInput, parseChoicePayload, resolveGameTextPayload, } from '@zhin.js/game-kit';
|
|
4
4
|
import { handleGuessChoice } from "../../lib/game-flow.js";
|
|
5
5
|
import { resolveGameServices } from "../../lib/runtime-store.js";
|
|
6
6
|
import { GUESS_PREFIX } from "../../lib/view.js";
|
|
7
7
|
export default defineMiddleware({
|
|
8
8
|
target: 'inbound',
|
|
9
9
|
async handle(context, next) {
|
|
10
|
+
if (!context.input.sender?.id) {
|
|
11
|
+
await next();
|
|
12
|
+
return;
|
|
13
|
+
}
|
|
10
14
|
const raw = context.input.content?.trim() ?? '';
|
|
11
15
|
const payload = resolveGameTextPayload(raw);
|
|
12
16
|
if (!payload?.startsWith(`${GUESS_PREFIX}:`)) {
|
|
@@ -19,7 +23,8 @@ export default defineMiddleware({
|
|
|
19
23
|
return;
|
|
20
24
|
}
|
|
21
25
|
const services = resolveGameServices(context);
|
|
22
|
-
const
|
|
23
|
-
await context.input.$reply(
|
|
26
|
+
const message = gamePlayerFromInput(context.input);
|
|
27
|
+
await replyGameOperation({ session: services, player: message, reply: content => context.input.$reply(content),
|
|
28
|
+
run: () => handleGuessChoice(services, message, parsed.sessionId, parsed.choiceId) });
|
|
24
29
|
},
|
|
25
30
|
});
|
|
@@ -1,10 +1,6 @@
|
|
|
1
|
+
import { replyGameOperation, gamePlayerFromInput, parseChoicePayload, resolveGameTextPayload } from '@zhin.js/game-kit';
|
|
1
2
|
import type { Message } from '@zhin.js/core/runtime';
|
|
2
3
|
import { defineMiddleware } from 'zhin.js/middleware';
|
|
3
|
-
import {
|
|
4
|
-
messageFromCommandInput,
|
|
5
|
-
parseChoicePayload,
|
|
6
|
-
resolveGameTextPayload,
|
|
7
|
-
} from '@zhin.js/game-kit';
|
|
8
4
|
import { handleGuessChoice } from '../../src/game-flow.js';
|
|
9
5
|
import { resolveGameServices } from '../../src/runtime-store.js';
|
|
10
6
|
import { GUESS_PREFIX } from '../../src/view.js';
|
|
@@ -12,6 +8,7 @@ import { GUESS_PREFIX } from '../../src/view.js';
|
|
|
12
8
|
export default defineMiddleware<Message>({
|
|
13
9
|
target: 'inbound',
|
|
14
10
|
async handle(context, next) {
|
|
11
|
+
if (!context.input.sender?.id) { await next(); return; }
|
|
15
12
|
const raw = context.input.content?.trim() ?? '';
|
|
16
13
|
const payload = resolveGameTextPayload(raw);
|
|
17
14
|
if (!payload?.startsWith(`${GUESS_PREFIX}:`)) {
|
|
@@ -24,12 +21,13 @@ export default defineMiddleware<Message>({
|
|
|
24
21
|
return;
|
|
25
22
|
}
|
|
26
23
|
const services = resolveGameServices(context);
|
|
27
|
-
const
|
|
24
|
+
const message = gamePlayerFromInput(context.input);
|
|
25
|
+
await replyGameOperation({ session: services, player: message, reply: content => context.input.$reply(content),
|
|
26
|
+
run: () => handleGuessChoice(
|
|
28
27
|
services,
|
|
29
|
-
|
|
28
|
+
message,
|
|
30
29
|
parsed.sessionId,
|
|
31
30
|
parsed.choiceId,
|
|
32
|
-
);
|
|
33
|
-
await context.input.$reply(reply);
|
|
31
|
+
) });
|
|
34
32
|
},
|
|
35
33
|
});
|
|
@@ -1,23 +1,24 @@
|
|
|
1
1
|
// Generated by build-plugin-runtime-entries.mjs. Do not edit.
|
|
2
|
+
import { replyGameOperation, gamePlayerFromInput } from '@zhin.js/game-kit';
|
|
2
3
|
import { defineMiddleware } from 'zhin.js/middleware';
|
|
3
|
-
import { messageFromCommandInput } from '@zhin.js/game-kit';
|
|
4
4
|
import { resolveGameServices } from "../../lib/runtime-store.js";
|
|
5
5
|
import { processGuess } from "../../lib/game-flow.js";
|
|
6
6
|
export default defineMiddleware({
|
|
7
7
|
target: 'inbound',
|
|
8
8
|
async handle(context, next) {
|
|
9
|
+
if (!context.input.sender?.id) {
|
|
10
|
+
await next();
|
|
11
|
+
return;
|
|
12
|
+
}
|
|
9
13
|
const raw = context.input.content?.trim() ?? '';
|
|
10
14
|
if (!/^(\d+)$/.test(raw)) {
|
|
11
15
|
await next();
|
|
12
16
|
return;
|
|
13
17
|
}
|
|
14
18
|
const services = resolveGameServices(context);
|
|
15
|
-
const message =
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
await context.input.$reply(reply);
|
|
19
|
-
return;
|
|
20
|
-
}
|
|
19
|
+
const message = gamePlayerFromInput(context.input);
|
|
20
|
+
await replyGameOperation({ session: services, player: message, reply: content => context.input.$reply(content),
|
|
21
|
+
run: () => processGuess(services, message, Number(raw)) });
|
|
21
22
|
await next();
|
|
22
23
|
},
|
|
23
24
|
});
|
|
@@ -1,24 +1,22 @@
|
|
|
1
|
+
import { replyGameOperation, gamePlayerFromInput } from '@zhin.js/game-kit';
|
|
1
2
|
import { defineMiddleware } from 'zhin.js/middleware';
|
|
2
3
|
import type { Message } from '@zhin.js/core/runtime';
|
|
3
|
-
import { messageFromCommandInput } from '@zhin.js/game-kit';
|
|
4
4
|
import { resolveGameServices } from '../../src/runtime-store.js';
|
|
5
5
|
import { processGuess } from '../../src/game-flow.js';
|
|
6
6
|
|
|
7
7
|
export default defineMiddleware<Message>({
|
|
8
8
|
target: 'inbound',
|
|
9
9
|
async handle(context, next) {
|
|
10
|
+
if (!context.input.sender?.id) { await next(); return; }
|
|
10
11
|
const raw = context.input.content?.trim() ?? '';
|
|
11
12
|
if (!/^(\d+)$/.test(raw)) {
|
|
12
13
|
await next();
|
|
13
14
|
return;
|
|
14
15
|
}
|
|
15
16
|
const services = resolveGameServices(context);
|
|
16
|
-
const message =
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
await context.input.$reply(reply);
|
|
20
|
-
return;
|
|
21
|
-
}
|
|
17
|
+
const message = gamePlayerFromInput(context.input);
|
|
18
|
+
await replyGameOperation({ session: services, player: message, reply: content => context.input.$reply(content),
|
|
19
|
+
run: () => processGuess(services, message, Number(raw)) });
|
|
22
20
|
await next();
|
|
23
21
|
},
|
|
24
22
|
});
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@zhin.js/plugin-guess-number",
|
|
3
|
-
"version": "1.1.
|
|
3
|
+
"version": "1.1.20",
|
|
4
4
|
"description": "Zhin.js 猜数字 — 1~100 七步之内猜中 (Plugin Runtime)",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./lib/index.js",
|
|
@@ -23,13 +23,13 @@
|
|
|
23
23
|
],
|
|
24
24
|
"license": "MIT",
|
|
25
25
|
"dependencies": {
|
|
26
|
-
"@zhin.js/core": "1.1.
|
|
27
|
-
"@zhin.js/game-kit": "1.1.
|
|
26
|
+
"@zhin.js/core": "1.1.55",
|
|
27
|
+
"@zhin.js/game-kit": "1.1.20"
|
|
28
28
|
},
|
|
29
29
|
"devDependencies": {
|
|
30
30
|
"typescript": "^6.0.3",
|
|
31
31
|
"vitest": "^4.1.10",
|
|
32
|
-
"zhin.js": "1.1.
|
|
32
|
+
"zhin.js": "1.1.20"
|
|
33
33
|
},
|
|
34
34
|
"repository": {
|
|
35
35
|
"type": "git",
|
|
@@ -66,9 +66,9 @@
|
|
|
66
66
|
"plugins": []
|
|
67
67
|
},
|
|
68
68
|
"peerDependencies": {
|
|
69
|
-
"@zhin.js/command": "^1.1.
|
|
70
|
-
"@zhin.js/middleware": "^1.1.
|
|
71
|
-
"zhin.js": "^1.1.
|
|
69
|
+
"@zhin.js/command": "^1.1.13",
|
|
70
|
+
"@zhin.js/middleware": "^1.1.12",
|
|
71
|
+
"zhin.js": "^1.1.20"
|
|
72
72
|
},
|
|
73
73
|
"peerDependenciesMeta": {
|
|
74
74
|
"@zhin.js/command": {
|
package/src/game-flow.ts
CHANGED
|
@@ -1,101 +1,107 @@
|
|
|
1
1
|
import { createMessages, resolveLocale } from 'zhin.js/i18n';
|
|
2
|
-
import
|
|
2
|
+
import { channelKey, type GamePlayer, type GameReply } from '@zhin.js/game-kit';
|
|
3
3
|
import { evaluateGuess, hintText, MAX, MIN } from './engine.js';
|
|
4
4
|
import type { SessionService } from './session-service.js';
|
|
5
5
|
import { buildGuessView } from './view.js';
|
|
6
6
|
|
|
7
7
|
export async function startGame(
|
|
8
8
|
services: SessionService,
|
|
9
|
-
message:
|
|
9
|
+
message: GamePlayer,
|
|
10
10
|
): Promise<GameReply> {
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
11
|
+
return services.runInScope({ scope: channelKey(message), run: async () => {
|
|
12
|
+
const ch = channelKey(message);
|
|
13
|
+
const mine = await services.getActiveForUser(ch, message.sender.id);
|
|
14
|
+
const locale = resolveLocale(mine ? mine.locale : message.locale);
|
|
15
|
+
if (mine) {
|
|
16
|
+
return buildGuessView(
|
|
17
|
+
mine,
|
|
18
|
+
[messages(locale, 'text1')],
|
|
19
|
+
message.target.kind,
|
|
20
|
+
);
|
|
21
|
+
}
|
|
22
|
+
const session = await services.createSession(message);
|
|
23
|
+
return buildGuessView(session, [], message.target.kind);
|
|
24
|
+
} });
|
|
23
25
|
}
|
|
24
26
|
|
|
25
27
|
export async function processGuess(
|
|
26
28
|
services: SessionService,
|
|
27
|
-
message:
|
|
29
|
+
message: GamePlayer,
|
|
28
30
|
value: number,
|
|
29
31
|
): Promise<GameReply | null> {
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
32
|
+
return services.runInScope({ scope: channelKey(message), run: async () => {
|
|
33
|
+
const ch = channelKey(message);
|
|
34
|
+
const session = await services.getActiveForUser(ch, message.sender.id);
|
|
35
|
+
const locale = resolveLocale(session ? session.locale : message.locale);
|
|
36
|
+
if (!session) return null;
|
|
34
37
|
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
38
|
+
const result = evaluateGuess(session.secret, value);
|
|
39
|
+
if (result === 'invalid') {
|
|
40
|
+
return messages(locale, 'text2', { p0: MIN, p1: MAX });
|
|
41
|
+
}
|
|
39
42
|
|
|
40
|
-
|
|
43
|
+
const attempts = session.attempts + 1;
|
|
41
44
|
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
45
|
+
if (result === 'win') {
|
|
46
|
+
await services.updateSession(session.id, { attempts, status: 'won' });
|
|
47
|
+
const updated = (await services.getById(session.id))!;
|
|
48
|
+
return buildGuessView(
|
|
49
|
+
updated,
|
|
50
|
+
[messages(locale, 'text3', { p0: attempts })],
|
|
51
|
+
message.target.kind,
|
|
52
|
+
);
|
|
53
|
+
}
|
|
51
54
|
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
55
|
+
if (attempts >= session.max_attempts) {
|
|
56
|
+
await services.updateSession(session.id, { attempts, status: 'lost' });
|
|
57
|
+
const updated = (await services.getById(session.id))!;
|
|
58
|
+
return buildGuessView(updated, [], message.target.kind);
|
|
59
|
+
}
|
|
57
60
|
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
61
|
+
let rangeMin = session.range_min;
|
|
62
|
+
let rangeMax = session.range_max;
|
|
63
|
+
if (result === 'low') rangeMin = Math.max(rangeMin, value + 1);
|
|
64
|
+
else rangeMax = Math.min(rangeMax, value - 1);
|
|
62
65
|
|
|
63
|
-
|
|
66
|
+
await services.updateSession(session.id, { attempts, range_min: rangeMin, range_max: rangeMax });
|
|
64
67
|
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
68
|
+
const updated = (await services.getById(session.id))!;
|
|
69
|
+
return buildGuessView(
|
|
70
|
+
updated,
|
|
71
|
+
[hintText(result, rangeMin, rangeMax, locale)],
|
|
72
|
+
message.target.kind,
|
|
73
|
+
);
|
|
74
|
+
} });
|
|
71
75
|
}
|
|
72
76
|
|
|
73
77
|
export async function handleGuessChoice(
|
|
74
78
|
services: SessionService,
|
|
75
|
-
message:
|
|
79
|
+
message: GamePlayer,
|
|
76
80
|
sessionId: string,
|
|
77
81
|
choiceId: string,
|
|
78
82
|
): Promise<GameReply> {
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
83
|
+
return services.runInScope({ scope: channelKey(message), run: async () => {
|
|
84
|
+
const session = await services.getById(sessionId);
|
|
85
|
+
const locale = resolveLocale(session ? session.locale : message.locale);
|
|
86
|
+
if (!session) return messages(locale, 'text4');
|
|
87
|
+
if (session.channel_key
|
|
88
|
+
!== channelKey(message)) {
|
|
89
|
+
return messages(locale, 'text5');
|
|
90
|
+
}
|
|
91
|
+
if (session.player_id !== message.sender.id) return messages(locale, 'text6');
|
|
87
92
|
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
93
|
+
if (choiceId === 'restart' && session.status !== 'active') {
|
|
94
|
+
return startGame(services, message);
|
|
95
|
+
}
|
|
96
|
+
if (choiceId === 'quit' && session.status === 'active') {
|
|
97
|
+
await services.updateSession(session.id, { status: 'aborted' });
|
|
98
|
+
const updated = (await services.getById(session.id))!;
|
|
99
|
+
return buildGuessView(updated, [messages(locale, 'text7')], message.target.kind);
|
|
100
|
+
}
|
|
101
|
+
return session.status === 'active'
|
|
102
|
+
? messages(locale, 'text8')
|
|
103
|
+
: buildGuessView(session, [], message.target.kind);
|
|
104
|
+
} });
|
|
99
105
|
}
|
|
100
106
|
|
|
101
107
|
const messages = createMessages({
|
package/src/guess-command.ts
CHANGED
|
@@ -1,46 +1,47 @@
|
|
|
1
|
-
import { formatGameHelp, gameCommandFeedback, gameCommandHint, channelKey, type
|
|
1
|
+
import { formatGameHelp, gameCommandFeedback, gameCommandHint, channelKey, type GamePlayer, type GameReply } from '@zhin.js/game-kit';
|
|
2
2
|
import { createMessages } from 'zhin.js/i18n';
|
|
3
3
|
import { startGame } from './game-flow.js';
|
|
4
4
|
import type { SessionService } from './session-service.js';
|
|
5
5
|
import { buildGuessView } from './view.js';
|
|
6
6
|
|
|
7
|
-
export function getGuessHelp(message: Pick<
|
|
8
|
-
return formatGameHelp(message, { 'zh-CN': "猜数字(1~100,7 次机会)", en: "Guess the Number (1–100, seven guesses)" }, 'guess', ['start', 'quit'],
|
|
9
|
-
{ 'zh-CN': "进行中直接回复范围内的整数。", en: "During a game, reply with an integer in the range." });
|
|
7
|
+
export function getGuessHelp(message: Pick<GamePlayer, 'locale' | 'formatCommand'> = {}): string {
|
|
8
|
+
return formatGameHelp({ presentation: message, title: { 'zh-CN': "猜数字(1~100,7 次机会)", en: "Guess the Number (1–100, seven guesses)" }, commandPath: 'guess', actions: ['start', 'quit'], footer: { 'zh-CN': "进行中直接回复范围内的整数。", en: "During a game, reply with an integer in the range." } });
|
|
10
9
|
}
|
|
11
10
|
|
|
12
11
|
export const GUESS_HELP = getGuessHelp();
|
|
13
12
|
|
|
14
13
|
export async function runGuessCommand(
|
|
15
14
|
services: SessionService,
|
|
16
|
-
message:
|
|
15
|
+
message: GamePlayer,
|
|
17
16
|
action: string,
|
|
18
17
|
): Promise<GameReply> {
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
18
|
+
return services.runInScope({ scope: channelKey(message), run: async () => {
|
|
19
|
+
const ch = channelKey(message);
|
|
20
|
+
const userId = message.sender.id;
|
|
21
|
+
|
|
22
|
+
if (!action || action === 'help') {
|
|
23
|
+
const active = await services.getActiveForUser(ch, userId);
|
|
24
|
+
const lines = [getGuessHelp(message), ''];
|
|
25
|
+
if (active) {
|
|
26
|
+
return buildGuessView(active, [getGuessHelp(message)], message.target.kind);
|
|
27
|
+
} else {
|
|
28
|
+
lines.push(gameCommandFeedback(message, 'noGame'), gameCommandHint(message, 'guess'));
|
|
29
|
+
}
|
|
30
|
+
return lines.join('\n');
|
|
29
31
|
}
|
|
30
|
-
return lines.join('\n');
|
|
31
|
-
}
|
|
32
32
|
|
|
33
|
-
|
|
33
|
+
if (action === 'start') return startGame(services, message);
|
|
34
34
|
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
35
|
+
if (action === 'quit') {
|
|
36
|
+
const row = await services.getActiveForUser(ch, userId);
|
|
37
|
+
if (!row) return gameCommandFeedback(message, 'noGame');
|
|
38
|
+
await services.updateSession(row.id, { status: 'aborted' });
|
|
39
|
+
const updated = (await services.getById(row.id))!;
|
|
40
|
+
return buildGuessView(updated, [messages(row.locale ?? undefined, 'text1')], message.target.kind);
|
|
41
|
+
}
|
|
42
42
|
|
|
43
|
-
|
|
43
|
+
return gameCommandFeedback(message, 'unknown', action) + '\n\n' + getGuessHelp(message);
|
|
44
|
+
} });
|
|
44
45
|
}
|
|
45
46
|
|
|
46
47
|
const messages = createMessages({
|
package/src/session-service.ts
CHANGED
|
@@ -1,9 +1,8 @@
|
|
|
1
1
|
import type { Database, Models } from '@zhin.js/core';
|
|
2
2
|
import {
|
|
3
3
|
BaseSessionService,
|
|
4
|
-
channelKey,
|
|
5
4
|
generateSessionId,
|
|
6
|
-
type
|
|
5
|
+
type GamePlayer,
|
|
7
6
|
type GameSessionDatabase,
|
|
8
7
|
} from '@zhin.js/game-kit';
|
|
9
8
|
import { MAX, MAX_ATTEMPTS, MIN, newSecret } from './engine.js';
|
|
@@ -37,18 +36,13 @@ export class SessionService extends BaseSessionService<GuessSessionRow> {
|
|
|
37
36
|
});
|
|
38
37
|
}
|
|
39
38
|
|
|
40
|
-
async createSession(message:
|
|
39
|
+
async createSession(message: GamePlayer): Promise<GuessSessionRow> {
|
|
41
40
|
const now = Date.now();
|
|
42
|
-
const row: GuessSessionRow = {
|
|
41
|
+
const row: Omit<GuessSessionRow, 'channel_key' | 'adapter' | 'endpoint' | 'channel_type' | 'channel_id'> = {
|
|
43
42
|
id: generateSessionId(),
|
|
44
43
|
locale: message.locale ?? 'zh-CN',
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
channel_type: message.$channel.type,
|
|
48
|
-
channel_id: message.$channel.id,
|
|
49
|
-
channel_key: channelKey(message),
|
|
50
|
-
player_id: message.$sender.id,
|
|
51
|
-
player_name: message.$sender.name?.trim() || message.$sender.id,
|
|
44
|
+
player_id: message.sender.id,
|
|
45
|
+
player_name: message.sender.name?.trim() || message.sender.id,
|
|
52
46
|
secret: newSecret(),
|
|
53
47
|
range_min: MIN,
|
|
54
48
|
range_max: MAX,
|
|
@@ -58,7 +52,7 @@ export class SessionService extends BaseSessionService<GuessSessionRow> {
|
|
|
58
52
|
updated_at: now,
|
|
59
53
|
created_at: now,
|
|
60
54
|
};
|
|
61
|
-
return this.createRow(row);
|
|
55
|
+
return this.createRow({ target: message.target, fields: row });
|
|
62
56
|
}
|
|
63
57
|
}
|
|
64
58
|
|