@zhin.js/plugin-dice-duel 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/dice/[[action]]/index.js +5 -4
- package/commands/dice/[[action]]/index.ts +5 -4
- package/lib/dice-command.d.ts +3 -3
- package/lib/dice-command.js +27 -25
- package/lib/dice-command.js.map +1 -1
- package/lib/game-flow.d.ts +4 -4
- package/lib/game-flow.js +67 -60
- package/lib/game-flow.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/dice-alias/index.js +12 -8
- package/middlewares/dice-alias/index.ts +10 -12
- package/middlewares/dice-choice/index.js +11 -9
- package/middlewares/dice-choice/index.ts +11 -9
- package/package.json +7 -7
- package/src/dice-command.ts +26 -25
- package/src/game-flow.ts +65 -59
- 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, normalizeDiceAction } from '@zhin.js/game-kit';
|
|
2
3
|
import { defineCommand } from 'zhin.js/command';
|
|
3
|
-
import { messageFromCommandInput, normalizeDiceAction } from '@zhin.js/game-kit';
|
|
4
4
|
import { getDiceHelp, runDiceCommand } from "../../../lib/dice-command.js";
|
|
5
5
|
import { resolveGameServices } from "../../../lib/runtime-store.js";
|
|
6
6
|
export default defineCommand({
|
|
7
7
|
description: 'Dice Duel',
|
|
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 = normalizeDiceAction(String(params.action ?? ''));
|
|
11
11
|
if (!action || action === 'help')
|
|
12
12
|
return getDiceHelp({ 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: () => runDiceCommand(services, message, action) });
|
|
16
17
|
},
|
|
17
18
|
});
|
|
@@ -1,16 +1,17 @@
|
|
|
1
|
+
import { replyGameOperation, gamePlayerFromInput, normalizeDiceAction } from '@zhin.js/game-kit';
|
|
1
2
|
import { defineCommand } from 'zhin.js/command';
|
|
2
|
-
import { messageFromCommandInput, normalizeDiceAction } from '@zhin.js/game-kit';
|
|
3
3
|
import { getDiceHelp, runDiceCommand } from '../../../src/dice-command.js';
|
|
4
4
|
import { resolveGameServices } from '../../../src/runtime-store.js';
|
|
5
5
|
|
|
6
6
|
export default defineCommand({
|
|
7
7
|
description: 'Dice Duel',
|
|
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 = normalizeDiceAction(String(params.action ?? ''));
|
|
11
11
|
if (!action || action === 'help') return getDiceHelp({ 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: () => runDiceCommand(services, message, action) });
|
|
15
16
|
},
|
|
16
17
|
});
|
package/lib/dice-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 getDiceHelp(message?: Pick<
|
|
3
|
+
export declare function getDiceHelp(message?: Pick<GamePlayer, 'locale' | 'formatCommand'>): string;
|
|
4
4
|
export declare const DICE_HELP: string;
|
|
5
|
-
export declare function runDiceCommand(services: SessionService, message:
|
|
5
|
+
export declare function runDiceCommand(services: SessionService, message: GamePlayer, action: string): Promise<GameReply>;
|
package/lib/dice-command.js
CHANGED
|
@@ -2,34 +2,36 @@ import { formatGameHelp, gameCommandFeedback, gameCommandHint, channelKey } from
|
|
|
2
2
|
import { createMessages } from 'zhin.js/i18n';
|
|
3
3
|
import { continueGame, handleChoice, startGame } from './game-flow.js';
|
|
4
4
|
export function getDiceHelp(message = {}) {
|
|
5
|
-
return formatGameHelp(message, { 'zh-CN': "🎲 骰子对决(三局两胜)", en: "🎲 Dice Duel (best of three)" }, 'dice', ['start', 'continue', 'quit'], { 'zh-CN': "点击按钮或回复选项数字掷骰。", en: "Choose a button or reply with its option number." });
|
|
5
|
+
return formatGameHelp({ presentation: message, title: { 'zh-CN': "🎲 骰子对决(三局两胜)", en: "🎲 Dice Duel (best of three)" }, commandPath: 'dice', actions: ['start', 'continue', 'quit'], footer: { 'zh-CN': "点击按钮或回复选项数字掷骰。", en: "Choose a button or reply with its option number." } });
|
|
6
6
|
}
|
|
7
7
|
export const DICE_HELP = getDiceHelp();
|
|
8
8
|
export async function runDiceCommand(services, message, action) {
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
9
|
+
return services.runInScope({ scope: channelKey(message), run: async () => {
|
|
10
|
+
const ch = channelKey(message);
|
|
11
|
+
const userId = message.sender.id;
|
|
12
|
+
if (!action || action === 'help') {
|
|
13
|
+
const active = await services.getActiveByChannel(ch);
|
|
14
|
+
const lines = [getDiceHelp(message), ''];
|
|
15
|
+
if (active) {
|
|
16
|
+
lines.push(messages(active.locale ?? undefined, 'text1', { p0: active.player_name, p1: active.player_wins, p2: active.bot_wins }));
|
|
17
|
+
}
|
|
18
|
+
else {
|
|
19
|
+
lines.push(gameCommandFeedback(message, 'noGame'), gameCommandHint(message, 'dice'));
|
|
20
|
+
}
|
|
21
|
+
return lines.join('\n');
|
|
22
|
+
}
|
|
23
|
+
if (action === 'start')
|
|
24
|
+
return (await startGame(services, message)) ?? '';
|
|
25
|
+
if (action === 'continue')
|
|
26
|
+
return continueGame(services, message);
|
|
27
|
+
if (action === 'quit') {
|
|
28
|
+
const row = await services.getActiveForUser(ch, userId);
|
|
29
|
+
if (!row)
|
|
30
|
+
return gameCommandFeedback(message, 'noGame');
|
|
31
|
+
return (await handleChoice(services, message, row.id, 'quit')) ?? '';
|
|
32
|
+
}
|
|
33
|
+
return gameCommandFeedback(message, 'unknown', action) + '\n\n' + getDiceHelp(message);
|
|
34
|
+
} });
|
|
33
35
|
}
|
|
34
36
|
const messages = createMessages({
|
|
35
37
|
text1: { 'zh-CN': "进行中:{p0} · {p1}:{p2}", en: "Playing: {p0} · {p1}:{p2}" },
|
package/lib/dice-command.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"dice-command.js","sourceRoot":"","sources":["../src/dice-command.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,mBAAmB,EAAE,eAAe,EAAE,UAAU,
|
|
1
|
+
{"version":3,"file":"dice-command.js","sourceRoot":"","sources":["../src/dice-command.ts"],"names":[],"mappings":"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"}
|
package/lib/game-flow.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import type
|
|
1
|
+
import { type GamePlayer, type GameReply } from '@zhin.js/game-kit';
|
|
2
2
|
import { DICE_PREFIX } from './engine.js';
|
|
3
3
|
import type { SessionService } from './session-service.js';
|
|
4
|
-
export declare function startGame(services: SessionService, message:
|
|
5
|
-
export declare function continueGame(services: SessionService, message:
|
|
6
|
-
export declare function handleChoice(services: SessionService, message:
|
|
4
|
+
export declare function startGame(services: SessionService, message: GamePlayer): Promise<GameReply | undefined>;
|
|
5
|
+
export declare function continueGame(services: SessionService, message: GamePlayer): Promise<GameReply>;
|
|
6
|
+
export declare function handleChoice(services: SessionService, message: GamePlayer, sessionId: string, choiceId: string): Promise<GameReply | null>;
|
|
7
7
|
export { DICE_PREFIX };
|
package/lib/game-flow.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { createMessages, resolveLocale } from 'zhin.js/i18n';
|
|
2
|
+
import { channelKey } from '@zhin.js/game-kit';
|
|
2
3
|
import { compareRolls, DICE_PREFIX, rollD6, WIN_TARGET } from './engine.js';
|
|
3
4
|
import { buildDiceView } from './view.js';
|
|
4
5
|
/**
|
|
@@ -7,72 +8,78 @@ import { buildDiceView } from './view.js';
|
|
|
7
8
|
* and the choice middleware return fresh text each turn.
|
|
8
9
|
*/
|
|
9
10
|
function renderView(session, message, lastRound) {
|
|
10
|
-
return buildDiceView(session, lastRound, message
|
|
11
|
+
return buildDiceView(session, lastRound, message.target.kind);
|
|
11
12
|
}
|
|
12
13
|
export async function startGame(services, message) {
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
14
|
+
return services.runInScope({ scope: channelKey(message), run: async () => {
|
|
15
|
+
const ch = channelKey(message);
|
|
16
|
+
const active = await services.getActiveByChannel(ch);
|
|
17
|
+
const locale = resolveLocale(active ? active.locale : message.locale);
|
|
18
|
+
if (active) {
|
|
19
|
+
if (active.player_id === message.sender.id) {
|
|
20
|
+
return messages(locale, 'text1');
|
|
21
|
+
}
|
|
22
|
+
return messages(locale, 'text2', { p0: active.player_name });
|
|
23
|
+
}
|
|
24
|
+
const session = await services.createSession(message);
|
|
25
|
+
return renderView(session, message);
|
|
26
|
+
} });
|
|
24
27
|
}
|
|
25
28
|
export async function continueGame(services, message) {
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
29
|
+
return services.runInScope({ scope: channelKey(message), run: async () => {
|
|
30
|
+
const session = await services.getActiveForUser(channelKey(message), message.sender.id);
|
|
31
|
+
const locale = resolveLocale(session ? session.locale : message.locale);
|
|
32
|
+
if (!session)
|
|
33
|
+
return messages(locale, 'text3');
|
|
34
|
+
return renderView(session, message);
|
|
35
|
+
} });
|
|
31
36
|
}
|
|
32
37
|
export async function handleChoice(services, message, sessionId, choiceId) {
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
38
|
+
return services.runInScope({ scope: channelKey(message), run: async () => {
|
|
39
|
+
const session = await services.getById(sessionId);
|
|
40
|
+
const locale = resolveLocale(session ? session.locale : message.locale);
|
|
41
|
+
if (!session)
|
|
42
|
+
return messages(locale, 'text4');
|
|
43
|
+
if (session.player_id !== message.sender.id)
|
|
44
|
+
return messages(locale, 'text5');
|
|
45
|
+
if (session.status !== 'active' && choiceId !== 'restart') {
|
|
46
|
+
return messages(locale, 'text6');
|
|
47
|
+
}
|
|
48
|
+
if (choiceId === 'restart') {
|
|
49
|
+
await services.updateSession(session.id, { status: 'aborted' });
|
|
50
|
+
return (await startGame(services, message)) ?? null;
|
|
51
|
+
}
|
|
52
|
+
if (choiceId === 'quit') {
|
|
53
|
+
await services.updateSession(session.id, { status: 'aborted' });
|
|
54
|
+
return renderView((await services.getById(session.id)), message);
|
|
55
|
+
}
|
|
56
|
+
if (choiceId !== 'roll')
|
|
57
|
+
return messages(locale, 'text7');
|
|
58
|
+
const player = rollD6();
|
|
59
|
+
const bot = rollD6();
|
|
60
|
+
const result = compareRolls(player, bot);
|
|
61
|
+
let playerWins = session.player_wins;
|
|
62
|
+
let botWins = session.bot_wins;
|
|
63
|
+
if (result === 1)
|
|
64
|
+
playerWins++;
|
|
65
|
+
if (result === 2)
|
|
66
|
+
botWins++;
|
|
67
|
+
let status = 'active';
|
|
68
|
+
if (playerWins >= WIN_TARGET)
|
|
69
|
+
status = 'won';
|
|
70
|
+
else if (botWins >= WIN_TARGET)
|
|
71
|
+
status = 'lost';
|
|
72
|
+
await services.updateSession(session.id, {
|
|
73
|
+
player_wins: playerWins,
|
|
74
|
+
bot_wins: botWins,
|
|
75
|
+
round: session.round + 1,
|
|
76
|
+
last_player_roll: player,
|
|
77
|
+
last_bot_roll: bot,
|
|
78
|
+
status,
|
|
79
|
+
});
|
|
80
|
+
const updated = (await services.getById(session.id));
|
|
81
|
+
return renderView(updated, message, { player, bot, result });
|
|
82
|
+
} });
|
|
76
83
|
}
|
|
77
84
|
export { DICE_PREFIX };
|
|
78
85
|
const messages = createMessages({
|
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/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 { DiceSessionRow } from './models.js';
|
|
4
4
|
export type DiceDatabase = Database<unknown, Models, string>;
|
|
5
5
|
export declare class SessionService extends BaseSessionService<DiceSessionRow> {
|
|
6
6
|
constructor(db: DiceDatabase);
|
|
7
|
-
createSession(message:
|
|
7
|
+
createSession(message: GamePlayer): Promise<DiceSessionRow>;
|
|
8
8
|
}
|
|
9
9
|
export declare function createServices(db: DiceDatabase): 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
|
export class SessionService extends BaseSessionService {
|
|
3
3
|
constructor(db) {
|
|
4
4
|
super(db, {
|
|
@@ -26,13 +26,8 @@ export class SessionService extends BaseSessionService {
|
|
|
26
26
|
const row = {
|
|
27
27
|
id: generateSessionId(),
|
|
28
28
|
locale: message.locale ?? 'zh-CN',
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
channel_type: message.$channel.type,
|
|
32
|
-
channel_id: message.$channel.id,
|
|
33
|
-
channel_key: channelKey(message),
|
|
34
|
-
player_id: message.$sender.id,
|
|
35
|
-
player_name: message.$sender.name?.trim() || message.$sender.id,
|
|
29
|
+
player_id: message.sender.id,
|
|
30
|
+
player_name: message.sender.name?.trim() || message.sender.id,
|
|
36
31
|
player_wins: 0,
|
|
37
32
|
bot_wins: 0,
|
|
38
33
|
round: 0,
|
|
@@ -42,7 +37,7 @@ export class SessionService extends BaseSessionService {
|
|
|
42
37
|
updated_at: now,
|
|
43
38
|
created_at: now,
|
|
44
39
|
};
|
|
45
|
-
return this.createRow(row);
|
|
40
|
+
return this.createRow({ target: message.target, fields: row });
|
|
46
41
|
}
|
|
47
42
|
}
|
|
48
43
|
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;AAK3B,MAAM,OAAO,cAAe,SAAQ,kBAAkC;IACpE,YAAY,EAAgB;QAC1B,KAAK,CAAC,EAAoD,EAAE;YAC1D,MAAM,EAAE,MAAM;YACd,KAAK,EAAE,eAAe;YACtB,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,OAAO,CAAC,WAAW,GAAG,EAAE;qBAChC,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,GAAiG;YACxG,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,WAAW,EAAE,CAAC;YACd,QAAQ,EAAE,CAAC;YACX,KAAK,EAAE,CAAC;YACR,gBAAgB,EAAE,CAAC;YACnB,aAAa,EAAE,CAAC;YAChB,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,EAAgB;IAC7C,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, isDiceAction,
|
|
2
|
+
import { replyGameOperation, defineGameCommandAliasMiddleware, isDiceAction, gamePlayerFromInput, normalizeDiceAction } from '@zhin.js/game-kit';
|
|
3
3
|
import { getDiceHelp, runDiceCommand } from "../../lib/dice-command.js";
|
|
4
4
|
import { resolveGameServices } from "../../lib/runtime-store.js";
|
|
5
5
|
export default defineGameCommandAliasMiddleware({
|
|
6
6
|
aliases: ['骰子', 'dice'],
|
|
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 = normalizeDiceAction(String(action ?? ''));
|
|
12
|
-
if (normalized === 'help')
|
|
13
|
-
|
|
11
|
+
if (normalized === 'help') {
|
|
12
|
+
await context.input.$reply(getDiceHelp(presentation));
|
|
13
|
+
return true;
|
|
14
|
+
}
|
|
14
15
|
// action 无法识别:放行给后续中间件,避免劫持普通聊天
|
|
15
16
|
if (!isDiceAction(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: () => runDiceCommand(services, message, normalized) });
|
|
22
|
+
return true;
|
|
19
23
|
},
|
|
20
24
|
});
|
|
@@ -1,23 +1,21 @@
|
|
|
1
|
-
import {
|
|
2
|
-
defineGameCommandAliasMiddleware,
|
|
3
|
-
isDiceAction,
|
|
4
|
-
messageFromCommandInput,
|
|
5
|
-
normalizeDiceAction,
|
|
6
|
-
} from '@zhin.js/game-kit';
|
|
1
|
+
import { replyGameOperation, defineGameCommandAliasMiddleware, isDiceAction, gamePlayerFromInput, normalizeDiceAction } from '@zhin.js/game-kit';
|
|
7
2
|
import { getDiceHelp, runDiceCommand } from '../../src/dice-command.js';
|
|
8
3
|
import { resolveGameServices } from '../../src/runtime-store.js';
|
|
9
4
|
|
|
10
5
|
export default defineGameCommandAliasMiddleware({
|
|
11
6
|
aliases: ['骰子', 'dice'],
|
|
12
7
|
async run(action, input, context) {
|
|
13
|
-
const
|
|
14
|
-
formatCommand: (path) => context.input.formatCommand?.(path, context.owner.id)
|
|
15
|
-
|
|
8
|
+
const presentation = { locale: context.input.locale,
|
|
9
|
+
formatCommand: (path: string) => context.input.formatCommand?.(path, context.owner.id) };
|
|
10
|
+
|
|
16
11
|
const normalized = normalizeDiceAction(String(action ?? ''));
|
|
17
|
-
if (normalized === 'help')
|
|
12
|
+
if (normalized === 'help') { await context.input.$reply(getDiceHelp(presentation)); return true; }
|
|
18
13
|
// action 无法识别:放行给后续中间件,避免劫持普通聊天
|
|
19
|
-
if (!isDiceAction(normalized)) return
|
|
14
|
+
if (!isDiceAction(normalized)) return false;
|
|
15
|
+
const message = gamePlayerFromInput(input, presentation);
|
|
20
16
|
const services = resolveGameServices(context);
|
|
21
|
-
|
|
17
|
+
await replyGameOperation({ session: services, player: message, reply: content => context.input.$reply(content),
|
|
18
|
+
run: () => runDiceCommand(services, message, normalized) });
|
|
19
|
+
return true;
|
|
22
20
|
},
|
|
23
21
|
});
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
// Generated by build-plugin-runtime-entries.mjs. Do not edit.
|
|
2
|
+
import { replyGameOperation, buildChoiceFallbackMap, channelKey, gamePlayerFromInput, parseChoicePayload, resolveGameTextPayload, } from '@zhin.js/game-kit';
|
|
2
3
|
import { defineMiddleware } from 'zhin.js/middleware';
|
|
3
|
-
import { buildChoiceFallbackMap, channelKey, messageFromCommandInput, parseChoicePayload, resolveGameTextPayload, } from '@zhin.js/game-kit';
|
|
4
4
|
import { resolveGameServices } from "../../lib/runtime-store.js";
|
|
5
5
|
import { DICE_PREFIX, handleChoice } from "../../lib/game-flow.js";
|
|
6
6
|
/**
|
|
@@ -10,13 +10,17 @@ import { DICE_PREFIX, handleChoice } from "../../lib/game-flow.js";
|
|
|
10
10
|
export default defineMiddleware({
|
|
11
11
|
target: 'inbound',
|
|
12
12
|
async handle(context, next) {
|
|
13
|
+
if (!context.input.sender?.id) {
|
|
14
|
+
await next();
|
|
15
|
+
return;
|
|
16
|
+
}
|
|
13
17
|
const raw = context.input.content?.trim() ?? '';
|
|
14
18
|
if (!raw) {
|
|
15
19
|
await next();
|
|
16
20
|
return;
|
|
17
21
|
}
|
|
18
22
|
const services = resolveGameServices(context);
|
|
19
|
-
const message =
|
|
23
|
+
const message = gamePlayerFromInput(context.input);
|
|
20
24
|
const ch = channelKey(message);
|
|
21
25
|
// 直接 payload(QQ 指令预填 / Sandbox action→text)
|
|
22
26
|
const payloadFromText = resolveGameTextPayload(raw);
|
|
@@ -25,16 +29,15 @@ export default defineMiddleware({
|
|
|
25
29
|
if (parsed) {
|
|
26
30
|
const session = await services.getById(parsed.sessionId);
|
|
27
31
|
if (session?.channel_key === ch) {
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
await context.input.$reply(reply);
|
|
32
|
+
await replyGameOperation({ session: services, player: message, reply: content => context.input.$reply(content),
|
|
33
|
+
run: () => handleChoice(services, message, parsed.sessionId, parsed.choiceId) });
|
|
31
34
|
return;
|
|
32
35
|
}
|
|
33
36
|
}
|
|
34
37
|
await next();
|
|
35
38
|
return;
|
|
36
39
|
}
|
|
37
|
-
const session = await services.getActiveForUser(ch, message
|
|
40
|
+
const session = await services.getActiveForUser(ch, message.sender.id);
|
|
38
41
|
if (!session || session.status !== 'active') {
|
|
39
42
|
await next();
|
|
40
43
|
return;
|
|
@@ -49,8 +52,7 @@ export default defineMiddleware({
|
|
|
49
52
|
await next();
|
|
50
53
|
return;
|
|
51
54
|
}
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
await context.input.$reply(reply);
|
|
55
|
+
await replyGameOperation({ session: services, player: message, reply: content => context.input.$reply(content),
|
|
56
|
+
run: () => handleChoice(services, message, session.id, 'roll') });
|
|
55
57
|
},
|
|
56
58
|
});
|
|
@@ -1,12 +1,13 @@
|
|
|
1
|
-
import { defineMiddleware } from 'zhin.js/middleware';
|
|
2
|
-
import type { Message } from '@zhin.js/core/runtime';
|
|
3
1
|
import {
|
|
2
|
+
replyGameOperation,
|
|
4
3
|
buildChoiceFallbackMap,
|
|
5
4
|
channelKey,
|
|
6
|
-
|
|
5
|
+
gamePlayerFromInput,
|
|
7
6
|
parseChoicePayload,
|
|
8
7
|
resolveGameTextPayload,
|
|
9
8
|
} from '@zhin.js/game-kit';
|
|
9
|
+
import { defineMiddleware } from 'zhin.js/middleware';
|
|
10
|
+
import type { Message } from '@zhin.js/core/runtime';
|
|
10
11
|
import { resolveGameServices } from '../../src/runtime-store.js';
|
|
11
12
|
import { DICE_PREFIX, handleChoice } from '../../src/game-flow.js';
|
|
12
13
|
|
|
@@ -17,13 +18,14 @@ import { DICE_PREFIX, handleChoice } from '../../src/game-flow.js';
|
|
|
17
18
|
export default defineMiddleware<Message>({
|
|
18
19
|
target: 'inbound',
|
|
19
20
|
async handle(context, next) {
|
|
21
|
+
if (!context.input.sender?.id) { await next(); return; }
|
|
20
22
|
const raw = context.input.content?.trim() ?? '';
|
|
21
23
|
if (!raw) {
|
|
22
24
|
await next();
|
|
23
25
|
return;
|
|
24
26
|
}
|
|
25
27
|
const services = resolveGameServices(context);
|
|
26
|
-
const message =
|
|
28
|
+
const message = gamePlayerFromInput(context.input);
|
|
27
29
|
const ch = channelKey(message);
|
|
28
30
|
|
|
29
31
|
// 直接 payload(QQ 指令预填 / Sandbox action→text)
|
|
@@ -33,8 +35,8 @@ export default defineMiddleware<Message>({
|
|
|
33
35
|
if (parsed) {
|
|
34
36
|
const session = await services.getById(parsed.sessionId);
|
|
35
37
|
if (session?.channel_key === ch) {
|
|
36
|
-
|
|
37
|
-
|
|
38
|
+
await replyGameOperation({ session: services, player: message, reply: content => context.input.$reply(content),
|
|
39
|
+
run: () => handleChoice(services, message, parsed.sessionId, parsed.choiceId) });
|
|
38
40
|
return;
|
|
39
41
|
}
|
|
40
42
|
}
|
|
@@ -42,7 +44,7 @@ export default defineMiddleware<Message>({
|
|
|
42
44
|
return;
|
|
43
45
|
}
|
|
44
46
|
|
|
45
|
-
const session = await services.getActiveForUser(ch, message
|
|
47
|
+
const session = await services.getActiveForUser(ch, message.sender.id);
|
|
46
48
|
if (!session || session.status !== 'active') {
|
|
47
49
|
await next();
|
|
48
50
|
return;
|
|
@@ -59,7 +61,7 @@ export default defineMiddleware<Message>({
|
|
|
59
61
|
return;
|
|
60
62
|
}
|
|
61
63
|
|
|
62
|
-
|
|
63
|
-
|
|
64
|
+
await replyGameOperation({ session: services, player: message, reply: content => context.input.$reply(content),
|
|
65
|
+
run: () => handleChoice(services, message, session.id, 'roll') });
|
|
64
66
|
},
|
|
65
67
|
});
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@zhin.js/plugin-dice-duel",
|
|
3
|
-
"version": "1.1.
|
|
3
|
+
"version": "1.1.20",
|
|
4
4
|
"description": "Zhin.js 骰子对决 — 三局两胜比大小 (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/dice-command.ts
CHANGED
|
@@ -1,44 +1,45 @@
|
|
|
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 { continueGame, handleChoice, startGame } from './game-flow.js';
|
|
4
4
|
import type { SessionService } from './session-service.js';
|
|
5
5
|
|
|
6
|
-
export function getDiceHelp(message: Pick<
|
|
7
|
-
return formatGameHelp(message, { 'zh-CN': "🎲 骰子对决(三局两胜)", en: "🎲 Dice Duel (best of three)" }, 'dice', ['start', 'continue', 'quit'],
|
|
8
|
-
{ 'zh-CN': "点击按钮或回复选项数字掷骰。", en: "Choose a button or reply with its option number." });
|
|
6
|
+
export function getDiceHelp(message: Pick<GamePlayer, 'locale' | 'formatCommand'> = {}): string {
|
|
7
|
+
return formatGameHelp({ presentation: message, title: { 'zh-CN': "🎲 骰子对决(三局两胜)", en: "🎲 Dice Duel (best of three)" }, commandPath: 'dice', actions: ['start', 'continue', 'quit'], footer: { 'zh-CN': "点击按钮或回复选项数字掷骰。", en: "Choose a button or reply with its option number." } });
|
|
9
8
|
}
|
|
10
9
|
|
|
11
10
|
export const DICE_HELP = getDiceHelp();
|
|
12
11
|
|
|
13
12
|
export async function runDiceCommand(
|
|
14
13
|
services: SessionService,
|
|
15
|
-
message:
|
|
14
|
+
message: GamePlayer,
|
|
16
15
|
action: string,
|
|
17
16
|
): Promise<GameReply> {
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
17
|
+
return services.runInScope({ scope: channelKey(message), run: async () => {
|
|
18
|
+
const ch = channelKey(message);
|
|
19
|
+
const userId = message.sender.id;
|
|
20
|
+
|
|
21
|
+
if (!action || action === 'help') {
|
|
22
|
+
const active = await services.getActiveByChannel(ch);
|
|
23
|
+
const lines = [getDiceHelp(message), ''];
|
|
24
|
+
if (active) {
|
|
25
|
+
lines.push(messages(active.locale ?? undefined, 'text1', { p0: active.player_name, p1: active.player_wins, p2: active.bot_wins }));
|
|
26
|
+
} else {
|
|
27
|
+
lines.push(gameCommandFeedback(message, 'noGame'), gameCommandHint(message, 'dice'));
|
|
28
|
+
}
|
|
29
|
+
return lines.join('\n');
|
|
28
30
|
}
|
|
29
|
-
return lines.join('\n');
|
|
30
|
-
}
|
|
31
31
|
|
|
32
|
-
|
|
33
|
-
|
|
32
|
+
if (action === 'start') return (await startGame(services, message)) ?? '';
|
|
33
|
+
if (action === 'continue') return continueGame(services, message);
|
|
34
34
|
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
35
|
+
if (action === 'quit') {
|
|
36
|
+
const row = await services.getActiveForUser(ch, userId);
|
|
37
|
+
if (!row) return gameCommandFeedback(message, 'noGame');
|
|
38
|
+
return (await handleChoice(services, message, row.id, 'quit')) ?? '';
|
|
39
|
+
}
|
|
40
40
|
|
|
41
|
-
|
|
41
|
+
return gameCommandFeedback(message, 'unknown', action) + '\n\n' + getDiceHelp(message);
|
|
42
|
+
} });
|
|
42
43
|
}
|
|
43
44
|
|
|
44
45
|
const messages = createMessages({
|
package/src/game-flow.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
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 type { DiceSessionRow } from './models.js';
|
|
4
4
|
import { compareRolls, DICE_PREFIX, rollD6, WIN_TARGET } from './engine.js';
|
|
5
5
|
import type { SessionService } from './session-service.js';
|
|
@@ -12,92 +12,98 @@ import { buildDiceView } from './view.js';
|
|
|
12
12
|
*/
|
|
13
13
|
function renderView(
|
|
14
14
|
session: DiceSessionRow,
|
|
15
|
-
message:
|
|
15
|
+
message: GamePlayer,
|
|
16
16
|
lastRound?: { player: number; bot: number; result: 0 | 1 | 2 },
|
|
17
17
|
): GameReply {
|
|
18
|
-
return buildDiceView(session, lastRound, message
|
|
18
|
+
return buildDiceView(session, lastRound, message.target.kind);
|
|
19
19
|
}
|
|
20
20
|
|
|
21
21
|
export async function startGame(
|
|
22
22
|
services: SessionService,
|
|
23
|
-
message:
|
|
23
|
+
message: GamePlayer,
|
|
24
24
|
): Promise<GameReply | undefined> {
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
if (active
|
|
30
|
-
|
|
25
|
+
return services.runInScope({ scope: channelKey(message), run: async () => {
|
|
26
|
+
const ch = channelKey(message);
|
|
27
|
+
const active = await services.getActiveByChannel(ch);
|
|
28
|
+
const locale = resolveLocale(active ? active.locale : message.locale);
|
|
29
|
+
if (active) {
|
|
30
|
+
if (active.player_id === message.sender.id) {
|
|
31
|
+
return messages(locale, 'text1');
|
|
32
|
+
}
|
|
33
|
+
return messages(locale, 'text2', { p0: active.player_name });
|
|
31
34
|
}
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
return renderView(session, message);
|
|
35
|
+
const session = await services.createSession(message);
|
|
36
|
+
return renderView(session, message);
|
|
37
|
+
} });
|
|
36
38
|
}
|
|
37
39
|
|
|
38
40
|
export async function continueGame(
|
|
39
41
|
services: SessionService,
|
|
40
|
-
message:
|
|
42
|
+
message: GamePlayer,
|
|
41
43
|
): Promise<GameReply> {
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
44
|
+
return services.runInScope({ scope: channelKey(message), run: async () => {
|
|
45
|
+
const session = await services.getActiveForUser(
|
|
46
|
+
channelKey(message),
|
|
47
|
+
message.sender.id,
|
|
48
|
+
);
|
|
49
|
+
const locale = resolveLocale(session ? session.locale : message.locale);
|
|
50
|
+
if (!session) return messages(locale, 'text3');
|
|
51
|
+
return renderView(session, message);
|
|
52
|
+
} });
|
|
49
53
|
}
|
|
50
54
|
|
|
51
55
|
export async function handleChoice(
|
|
52
56
|
services: SessionService,
|
|
53
|
-
message:
|
|
57
|
+
message: GamePlayer,
|
|
54
58
|
sessionId: string,
|
|
55
59
|
choiceId: string,
|
|
56
60
|
): Promise<GameReply | null> {
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
+
return services.runInScope({ scope: channelKey(message), run: async () => {
|
|
62
|
+
const session = await services.getById(sessionId);
|
|
63
|
+
const locale = resolveLocale(session ? session.locale : message.locale);
|
|
64
|
+
if (!session) return messages(locale, 'text4');
|
|
65
|
+
if (session.player_id !== message.sender.id) return messages(locale, 'text5');
|
|
61
66
|
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
67
|
+
if (session.status !== 'active' && choiceId !== 'restart') {
|
|
68
|
+
return messages(locale, 'text6');
|
|
69
|
+
}
|
|
65
70
|
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
71
|
+
if (choiceId === 'restart') {
|
|
72
|
+
await services.updateSession(session.id, { status: 'aborted' });
|
|
73
|
+
return (await startGame(services, message)) ?? null;
|
|
74
|
+
}
|
|
75
|
+
if (choiceId === 'quit') {
|
|
76
|
+
await services.updateSession(session.id, { status: 'aborted' });
|
|
77
|
+
return renderView((await services.getById(session.id))!, message);
|
|
78
|
+
}
|
|
74
79
|
|
|
75
|
-
|
|
80
|
+
if (choiceId !== 'roll') return messages(locale, 'text7');
|
|
76
81
|
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
82
|
+
const player = rollD6();
|
|
83
|
+
const bot = rollD6();
|
|
84
|
+
const result = compareRolls(player, bot);
|
|
80
85
|
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
86
|
+
let playerWins = session.player_wins;
|
|
87
|
+
let botWins = session.bot_wins;
|
|
88
|
+
if (result === 1) playerWins++;
|
|
89
|
+
if (result === 2) botWins++;
|
|
85
90
|
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
91
|
+
let status: DiceSessionRow['status'] = 'active';
|
|
92
|
+
if (playerWins >= WIN_TARGET) status = 'won';
|
|
93
|
+
else if (botWins >= WIN_TARGET) status = 'lost';
|
|
89
94
|
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
95
|
+
await services.updateSession(session.id, {
|
|
96
|
+
player_wins: playerWins,
|
|
97
|
+
bot_wins: botWins,
|
|
98
|
+
round: session.round + 1,
|
|
99
|
+
last_player_roll: player,
|
|
100
|
+
last_bot_roll: bot,
|
|
101
|
+
status,
|
|
102
|
+
});
|
|
98
103
|
|
|
99
|
-
|
|
100
|
-
|
|
104
|
+
const updated = (await services.getById(session.id))!;
|
|
105
|
+
return renderView(updated, message, { player, bot, result });
|
|
106
|
+
} });
|
|
101
107
|
}
|
|
102
108
|
|
|
103
109
|
export { DICE_PREFIX };
|
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 type { DiceSessionRow } from './models.js';
|
|
@@ -33,18 +32,13 @@ export class SessionService extends BaseSessionService<DiceSessionRow> {
|
|
|
33
32
|
});
|
|
34
33
|
}
|
|
35
34
|
|
|
36
|
-
async createSession(message:
|
|
35
|
+
async createSession(message: GamePlayer): Promise<DiceSessionRow> {
|
|
37
36
|
const now = Date.now();
|
|
38
|
-
const row: DiceSessionRow = {
|
|
37
|
+
const row: Omit<DiceSessionRow, 'channel_key' | 'adapter' | 'endpoint' | 'channel_type' | 'channel_id'> = {
|
|
39
38
|
id: generateSessionId(),
|
|
40
39
|
locale: message.locale ?? 'zh-CN',
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
channel_type: message.$channel.type,
|
|
44
|
-
channel_id: message.$channel.id,
|
|
45
|
-
channel_key: channelKey(message),
|
|
46
|
-
player_id: message.$sender.id,
|
|
47
|
-
player_name: message.$sender.name?.trim() || message.$sender.id,
|
|
40
|
+
player_id: message.sender.id,
|
|
41
|
+
player_name: message.sender.name?.trim() || message.sender.id,
|
|
48
42
|
player_wins: 0,
|
|
49
43
|
bot_wins: 0,
|
|
50
44
|
round: 0,
|
|
@@ -54,7 +48,7 @@ export class SessionService extends BaseSessionService<DiceSessionRow> {
|
|
|
54
48
|
updated_at: now,
|
|
55
49
|
created_at: now,
|
|
56
50
|
};
|
|
57
|
-
return this.createRow(row);
|
|
51
|
+
return this.createRow({ target: message.target, fields: row });
|
|
58
52
|
}
|
|
59
53
|
}
|
|
60
54
|
|