@zhin.js/plugin-answers 0.0.3 → 0.0.5
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//345/245/275/346/227/240/350/201/212/index.js +7 -5
- package/commands//345/245/275/346/227/240/350/201/212/index.ts +7 -5
- package/commands//347/247/221/347/233/256/344/270/200/index.js +9 -6
- package/commands//347/247/221/347/233/256/344/270/200/index.ts +9 -6
- package/commands//347/255/224/346/241/210/344/271/213/344/271/246/index.js +7 -6
- package/commands//347/255/224/346/241/210/344/271/213/344/271/246/index.ts +7 -6
- package/lib/book.d.ts +3 -1
- package/lib/book.d.ts.map +1 -1
- package/lib/book.js +7 -8
- package/lib/book.js.map +1 -1
- package/lib/config.js +4 -4
- package/lib/config.js.map +1 -1
- package/lib/messages.d.ts +2 -0
- package/lib/messages.d.ts.map +1 -0
- package/lib/messages.js +16 -0
- package/lib/messages.js.map +1 -0
- package/lib/quiz.d.ts +4 -2
- package/lib/quiz.d.ts.map +1 -1
- package/lib/quiz.js +10 -7
- package/lib/quiz.js.map +1 -1
- package/package.json +6 -6
- package/schema.json +20 -0
- package/src/book.ts +8 -8
- package/src/config.ts +4 -4
- package/src/messages.ts +16 -0
- package/src/quiz.ts +11 -7
|
@@ -1,18 +1,20 @@
|
|
|
1
1
|
// Generated by build-plugin-runtime-entries.mjs. Do not edit.
|
|
2
|
+
import { messages } from "../../lib/messages.js";
|
|
2
3
|
import { defineCommand } from 'zhin.js/command';
|
|
3
4
|
import { resolveAnswersConfig } from "../../lib/config.js";
|
|
4
5
|
import { FUN_QUESTIONS } from "../../lib/fun-quiz-data.js";
|
|
5
6
|
import { formatQuestion, quizKey } from "../../lib/quiz.js";
|
|
6
7
|
import { quizServiceToken } from "../../lib/runtime.js";
|
|
7
8
|
export default defineCommand({
|
|
8
|
-
|
|
9
|
+
preferredAliasI18n: { en: 'boring' },
|
|
10
|
+
description: '好无聊?来一道原创趣味选择题,直接回复字母作答', descriptionI18n: { en: "Try an original trivia question; reply with an option letter" },
|
|
9
11
|
alias: ['boring'],
|
|
10
|
-
execute({ input, use, config }) {
|
|
12
|
+
execute({ input, use, config, locale }) {
|
|
11
13
|
const key = quizKey(input);
|
|
12
14
|
if (!key)
|
|
13
|
-
return '
|
|
15
|
+
return messages(locale, 'conversation');
|
|
14
16
|
const settings = resolveAnswersConfig(config);
|
|
15
|
-
const question = use(quizServiceToken).ask(key, 'fun', FUN_QUESTIONS, settings.quizTimeoutSeconds);
|
|
16
|
-
return formatQuestion('fun', question, settings.quizTimeoutSeconds);
|
|
17
|
+
const question = use(quizServiceToken).ask(key, 'fun', FUN_QUESTIONS, settings.quizTimeoutSeconds, Date.now(), locale);
|
|
18
|
+
return formatQuestion('fun', question, settings.quizTimeoutSeconds, locale);
|
|
17
19
|
},
|
|
18
20
|
});
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { messages } from '../../src/messages.js';
|
|
1
2
|
import { defineCommand } from 'zhin.js/command';
|
|
2
3
|
import { resolveAnswersConfig, type AnswersConfig } from '../../src/config.js';
|
|
3
4
|
import { FUN_QUESTIONS } from '../../src/fun-quiz-data.js';
|
|
@@ -5,13 +6,14 @@ import { formatQuestion, quizKey } from '../../src/quiz.js';
|
|
|
5
6
|
import { quizServiceToken } from '../../src/runtime.js';
|
|
6
7
|
|
|
7
8
|
export default defineCommand<AnswersConfig>({
|
|
8
|
-
|
|
9
|
+
preferredAliasI18n: { en: 'boring' },
|
|
10
|
+
description: '好无聊?来一道原创趣味选择题,直接回复字母作答', descriptionI18n: { en: "Try an original trivia question; reply with an option letter" },
|
|
9
11
|
alias: ['boring'],
|
|
10
|
-
execute({ input, use, config }) {
|
|
12
|
+
execute({ input, use, config, locale }) {
|
|
11
13
|
const key = quizKey(input);
|
|
12
|
-
if (!key) return '
|
|
14
|
+
if (!key) return messages(locale, 'conversation');
|
|
13
15
|
const settings = resolveAnswersConfig(config);
|
|
14
|
-
const question = use(quizServiceToken).ask(key, 'fun', FUN_QUESTIONS, settings.quizTimeoutSeconds);
|
|
15
|
-
return formatQuestion('fun', question, settings.quizTimeoutSeconds);
|
|
16
|
+
const question = use(quizServiceToken).ask(key, 'fun', FUN_QUESTIONS, settings.quizTimeoutSeconds, Date.now(), locale);
|
|
17
|
+
return formatQuestion('fun', question, settings.quizTimeoutSeconds, locale);
|
|
16
18
|
},
|
|
17
19
|
});
|
|
@@ -1,19 +1,22 @@
|
|
|
1
1
|
// Generated by build-plugin-runtime-entries.mjs. Do not edit.
|
|
2
|
+
import { messages } from "../../lib/messages.js";
|
|
2
3
|
import { defineCommand } from 'zhin.js/command';
|
|
3
4
|
import { resolveAnswersConfig } from "../../lib/config.js";
|
|
4
5
|
import { DRIVING_QUESTIONS } from "../../lib/driving-quiz-data.js";
|
|
5
6
|
import { formatQuestion, quizKey } from "../../lib/quiz.js";
|
|
6
7
|
import { quizServiceToken } from "../../lib/runtime.js";
|
|
7
8
|
export default defineCommand({
|
|
8
|
-
|
|
9
|
-
|
|
9
|
+
alias: ['driving-quiz'],
|
|
10
|
+
preferredAliasI18n: { en: 'driving-quiz' },
|
|
11
|
+
description: '科目一交规小测(原创题面,依据公开法规整理,仅供娱乐参考)', descriptionI18n: { en: "Chinese driving theory quiz with original questions; for entertainment and reference only" },
|
|
12
|
+
execute({ input, use, config, locale }) {
|
|
10
13
|
const settings = resolveAnswersConfig(config);
|
|
11
14
|
if (!settings.enableDrivingQuiz)
|
|
12
|
-
return
|
|
15
|
+
return messages(locale, 'disabled');
|
|
13
16
|
const key = quizKey(input);
|
|
14
17
|
if (!key)
|
|
15
|
-
return '
|
|
16
|
-
const question = use(quizServiceToken).ask(key, 'driving', DRIVING_QUESTIONS, settings.quizTimeoutSeconds);
|
|
17
|
-
return formatQuestion('driving', question, settings.quizTimeoutSeconds);
|
|
18
|
+
return messages(locale, 'conversation');
|
|
19
|
+
const question = use(quizServiceToken).ask(key, 'driving', DRIVING_QUESTIONS, settings.quizTimeoutSeconds, Date.now(), locale);
|
|
20
|
+
return formatQuestion('driving', question, settings.quizTimeoutSeconds, locale);
|
|
18
21
|
},
|
|
19
22
|
});
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { messages } from '../../src/messages.js';
|
|
1
2
|
import { defineCommand } from 'zhin.js/command';
|
|
2
3
|
import { resolveAnswersConfig, type AnswersConfig } from '../../src/config.js';
|
|
3
4
|
import { DRIVING_QUESTIONS } from '../../src/driving-quiz-data.js';
|
|
@@ -5,13 +6,15 @@ import { formatQuestion, quizKey } from '../../src/quiz.js';
|
|
|
5
6
|
import { quizServiceToken } from '../../src/runtime.js';
|
|
6
7
|
|
|
7
8
|
export default defineCommand<AnswersConfig>({
|
|
8
|
-
|
|
9
|
-
|
|
9
|
+
alias: ['driving-quiz'],
|
|
10
|
+
preferredAliasI18n: { en: 'driving-quiz' },
|
|
11
|
+
description: '科目一交规小测(原创题面,依据公开法规整理,仅供娱乐参考)', descriptionI18n: { en: "Chinese driving theory quiz with original questions; for entertainment and reference only" },
|
|
12
|
+
execute({ input, use, config, locale }) {
|
|
10
13
|
const settings = resolveAnswersConfig(config);
|
|
11
|
-
if (!settings.enableDrivingQuiz) return
|
|
14
|
+
if (!settings.enableDrivingQuiz) return messages(locale, 'disabled');
|
|
12
15
|
const key = quizKey(input);
|
|
13
|
-
if (!key) return '
|
|
14
|
-
const question = use(quizServiceToken).ask(key, 'driving', DRIVING_QUESTIONS, settings.quizTimeoutSeconds);
|
|
15
|
-
return formatQuestion('driving', question, settings.quizTimeoutSeconds);
|
|
16
|
+
if (!key) return messages(locale, 'conversation');
|
|
17
|
+
const question = use(quizServiceToken).ask(key, 'driving', DRIVING_QUESTIONS, settings.quizTimeoutSeconds, Date.now(), locale);
|
|
18
|
+
return formatQuestion('driving', question, settings.quizTimeoutSeconds, locale);
|
|
16
19
|
},
|
|
17
20
|
});
|
|
@@ -1,15 +1,16 @@
|
|
|
1
1
|
// Generated by build-plugin-runtime-entries.mjs. Do not edit.
|
|
2
2
|
import { defineCommand } from 'zhin.js/command';
|
|
3
|
-
import {
|
|
3
|
+
import { bookUsage, MAX_QUESTION_LENGTH, pickBookAnswer, replyBook } from "../../lib/book.js";
|
|
4
4
|
import { resolveAnswersConfig } from "../../lib/config.js";
|
|
5
5
|
export default defineCommand({
|
|
6
|
-
|
|
6
|
+
preferredAliasI18n: { en: 'answerbook' },
|
|
7
|
+
description: '答案之书:心里默念问题,翻开一页得到答案', descriptionI18n: { en: "Book of Answers: think of a question and turn a page" },
|
|
7
8
|
alias: ['answerbook'],
|
|
8
|
-
params: { question: { type: 'text', optional: true, max: MAX_QUESTION_LENGTH, description: '你想问的问题' } },
|
|
9
|
-
execute({ params, input, config }) {
|
|
9
|
+
params: { question: { type: 'text', optional: true, max: MAX_QUESTION_LENGTH, description: '你想问的问题', descriptionI18n: { en: "Your question" } } },
|
|
10
|
+
execute({ params, input, config, locale, formatCommand }) {
|
|
10
11
|
const question = typeof params.question === 'string' ? params.question.trim() : '';
|
|
11
12
|
if (!question)
|
|
12
|
-
return
|
|
13
|
+
return bookUsage(locale, formatCommand('答案之书'));
|
|
13
14
|
const settings = resolveAnswersConfig(config);
|
|
14
15
|
const answer = pickBookAnswer({
|
|
15
16
|
question,
|
|
@@ -17,6 +18,6 @@ export default defineCommand({
|
|
|
17
18
|
dailyStable: settings.dailyStable,
|
|
18
19
|
timezone: settings.timezone,
|
|
19
20
|
});
|
|
20
|
-
return replyBook(question, answer);
|
|
21
|
+
return replyBook(question, answer, locale);
|
|
21
22
|
},
|
|
22
23
|
});
|
|
@@ -1,14 +1,15 @@
|
|
|
1
1
|
import { defineCommand } from 'zhin.js/command';
|
|
2
|
-
import {
|
|
2
|
+
import { bookUsage, MAX_QUESTION_LENGTH, pickBookAnswer, replyBook } from '../../src/book.js';
|
|
3
3
|
import { resolveAnswersConfig, type AnswersConfig } from '../../src/config.js';
|
|
4
4
|
|
|
5
5
|
export default defineCommand<AnswersConfig>({
|
|
6
|
-
|
|
6
|
+
preferredAliasI18n: { en: 'answerbook' },
|
|
7
|
+
description: '答案之书:心里默念问题,翻开一页得到答案', descriptionI18n: { en: "Book of Answers: think of a question and turn a page" },
|
|
7
8
|
alias: ['answerbook'],
|
|
8
|
-
params: { question: { type: 'text', optional: true, max: MAX_QUESTION_LENGTH, description: '你想问的问题' } },
|
|
9
|
-
execute({ params, input, config }) {
|
|
9
|
+
params: { question: { type: 'text', optional: true, max: MAX_QUESTION_LENGTH, description: '你想问的问题', descriptionI18n: { en: "Your question" } } },
|
|
10
|
+
execute({ params, input, config, locale, formatCommand }) {
|
|
10
11
|
const question = typeof params.question === 'string' ? params.question.trim() : '';
|
|
11
|
-
if (!question) return
|
|
12
|
+
if (!question) return bookUsage(locale, formatCommand('答案之书'));
|
|
12
13
|
const settings = resolveAnswersConfig(config);
|
|
13
14
|
const answer = pickBookAnswer({
|
|
14
15
|
question,
|
|
@@ -16,6 +17,6 @@ export default defineCommand<AnswersConfig>({
|
|
|
16
17
|
dailyStable: settings.dailyStable,
|
|
17
18
|
timezone: settings.timezone,
|
|
18
19
|
});
|
|
19
|
-
return replyBook(question, answer);
|
|
20
|
+
return replyBook(question, answer, locale);
|
|
20
21
|
},
|
|
21
22
|
});
|
package/lib/book.d.ts
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { type Locale } from 'zhin.js/i18n';
|
|
2
|
+
export declare function bookUsage(locale?: Locale, command?: string): string;
|
|
1
3
|
export declare const BOOK_USAGE: string;
|
|
2
4
|
export declare const MAX_QUESTION_LENGTH = 200;
|
|
3
5
|
export interface BookPickInput {
|
|
@@ -13,5 +15,5 @@ export declare function normalizeQuestion(question: string): string;
|
|
|
13
15
|
/** 给定时区下的日历日期(YYYY-MM-DD);时区无效时退回 UTC。 */
|
|
14
16
|
export declare function calendarDay(now: number, timezone: string): string;
|
|
15
17
|
export declare function pickBookAnswer(input: BookPickInput, answers?: readonly string[]): string;
|
|
16
|
-
export declare function replyBook(question: string, answer: string): string;
|
|
18
|
+
export declare function replyBook(question: string, answer: string, locale?: Locale): string;
|
|
17
19
|
//# sourceMappingURL=book.d.ts.map
|
package/lib/book.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"book.d.ts","sourceRoot":"","sources":["../src/book.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"book.d.ts","sourceRoot":"","sources":["../src/book.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,MAAM,EAAE,MAAM,cAAc,CAAC;AAK3C,wBAAgB,SAAS,CAAC,MAAM,GAAE,MAAgB,EAAE,OAAO,CAAC,EAAE,MAAM,GAAG,MAAM,CAE5E;AACD,eAAO,MAAM,UAAU,QAAc,CAAC;AAEtC,eAAO,MAAM,mBAAmB,MAAM,CAAC;AAEvC,MAAM,WAAW,aAAa;IAC5B,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,mCAAmC;IACnC,QAAQ,CAAC,WAAW,EAAE,OAAO,CAAC;IAC9B,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,GAAG,CAAC,EAAE,MAAM,CAAC;CACvB;AAED,uCAAuC;AACvC,wBAAgB,iBAAiB,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CAE1D;AAED,0CAA0C;AAC1C,wBAAgB,WAAW,CAAC,GAAG,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,MAAM,CAMjE;AAED,wBAAgB,cAAc,CAAC,KAAK,EAAE,aAAa,EAAE,OAAO,GAAE,SAAS,MAAM,EAAiB,GAAG,MAAM,CAQtG;AAED,wBAAgB,SAAS,CAAC,QAAQ,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAE,MAAgB,GAAG,MAAM,CAE5F"}
|
package/lib/book.js
CHANGED
|
@@ -1,11 +1,10 @@
|
|
|
1
|
+
import { messages } from './messages.js';
|
|
1
2
|
import { createHash, randomInt } from 'node:crypto';
|
|
2
3
|
import { BOOK_ANSWERS } from './book-data.js';
|
|
3
|
-
export
|
|
4
|
-
'
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
'心里默念问题后发送,书会翻开一页给你答案。',
|
|
8
|
-
].join('\n');
|
|
4
|
+
export function bookUsage(locale = 'zh-CN', command) {
|
|
5
|
+
return [messages(locale, 'bookTitle'), ...(command === undefined ? [] : [messages(locale, 'bookUsage', { command })]), messages(locale, 'bookIntro')].join('\n');
|
|
6
|
+
}
|
|
7
|
+
export const BOOK_USAGE = bookUsage();
|
|
9
8
|
export const MAX_QUESTION_LENGTH = 200;
|
|
10
9
|
/** 规范化问题:去掉空白与常见标点,避免「?」和「?」得到不同答案。 */
|
|
11
10
|
export function normalizeQuestion(question) {
|
|
@@ -31,7 +30,7 @@ export function pickBookAnswer(input, answers = BOOK_ANSWERS) {
|
|
|
31
30
|
.digest();
|
|
32
31
|
return answers[digest.readUInt32BE(0) % answers.length];
|
|
33
32
|
}
|
|
34
|
-
export function replyBook(question, answer) {
|
|
35
|
-
return
|
|
33
|
+
export function replyBook(question, answer, locale = 'zh-CN') {
|
|
34
|
+
return messages(locale, 'bookReply', { question, answer });
|
|
36
35
|
}
|
|
37
36
|
//# sourceMappingURL=book.js.map
|
package/lib/book.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"book.js","sourceRoot":"","sources":["../src/book.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"book.js","sourceRoot":"","sources":["../src/book.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAC;AACzC,OAAO,EAAE,UAAU,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AACpD,OAAO,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAE9C,MAAM,UAAU,SAAS,CAAC,SAAiB,OAAO,EAAE,OAAgB;IAClE,OAAO,CAAC,QAAQ,CAAC,MAAM,EAAE,WAAW,CAAC,EAAE,GAAG,CAAC,OAAO,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,MAAM,EAAE,WAAW,EAAE,EAAE,OAAO,EAAE,CAAC,CAAC,CAAC,EAAE,QAAQ,CAAC,MAAM,EAAE,WAAW,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AACnK,CAAC;AACD,MAAM,CAAC,MAAM,UAAU,GAAG,SAAS,EAAE,CAAC;AAEtC,MAAM,CAAC,MAAM,mBAAmB,GAAG,GAAG,CAAC;AAWvC,uCAAuC;AACvC,MAAM,UAAU,iBAAiB,CAAC,QAAgB;IAChD,OAAO,QAAQ,CAAC,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC,OAAO,CAAC,cAAc,EAAE,EAAE,CAAC,CAAC;AACnE,CAAC;AAED,0CAA0C;AAC1C,MAAM,UAAU,WAAW,CAAC,GAAW,EAAE,QAAgB;IACvD,IAAI,CAAC;QACH,OAAO,IAAI,IAAI,CAAC,cAAc,CAAC,OAAO,EAAE,EAAE,QAAQ,EAAE,QAAQ,EAAE,IAAI,EAAE,SAAS,EAAE,KAAK,EAAE,SAAS,EAAE,GAAG,EAAE,SAAS,EAAE,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;IACjI,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,IAAI,IAAI,CAAC,GAAG,CAAC,CAAC,WAAW,EAAE,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;IAClD,CAAC;AACH,CAAC;AAED,MAAM,UAAU,cAAc,CAAC,KAAoB,EAAE,UAA6B,YAAY;IAC5F,IAAI,OAAO,CAAC,MAAM,KAAK,CAAC;QAAE,MAAM,IAAI,KAAK,CAAC,QAAQ,CAAC,CAAC;IACpD,IAAI,CAAC,KAAK,CAAC,WAAW;QAAE,OAAO,OAAO,CAAC,SAAS,CAAC,OAAO,CAAC,MAAM,CAAC,CAAE,CAAC;IACnE,MAAM,GAAG,GAAG,WAAW,CAAC,KAAK,CAAC,GAAG,IAAI,IAAI,CAAC,GAAG,EAAE,EAAE,KAAK,CAAC,QAAQ,CAAC,CAAC;IACjE,MAAM,MAAM,GAAG,UAAU,CAAC,QAAQ,CAAC;SAChC,MAAM,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC,iBAAiB,EAAE,GAAG,EAAE,KAAK,CAAC,QAAQ,EAAE,iBAAiB,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC;SACnG,MAAM,EAAE,CAAC;IACZ,OAAO,OAAO,CAAC,MAAM,CAAC,YAAY,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC,MAAM,CAAE,CAAC;AAC3D,CAAC;AAED,MAAM,UAAU,SAAS,CAAC,QAAgB,EAAE,MAAc,EAAE,SAAiB,OAAO;IAClF,OAAO,QAAQ,CAAC,MAAM,EAAE,WAAW,EAAE,EAAE,QAAQ,EAAE,MAAM,EAAE,CAAC,CAAC;AAC7D,CAAC"}
|
package/lib/config.js
CHANGED
|
@@ -7,10 +7,10 @@ export const DEFAULT_ANSWERS_CONFIG = Object.freeze({
|
|
|
7
7
|
});
|
|
8
8
|
/** Authoring declaration; scripts/sync-plugin-config-schemas.mjs generates the packaged schema.json for Runtime and Console. */
|
|
9
9
|
export const answersConfig = Schema.object({
|
|
10
|
-
dailyStable: Schema.boolean().default(DEFAULT_ANSWERS_CONFIG.dailyStable).description('同一用户同一天问同一个问题时,答案之书给出相同答案').effect('reload'),
|
|
11
|
-
timezone: Schema.string().default(DEFAULT_ANSWERS_CONFIG.timezone).description('dailyStable 判断「同一天」所用的 IANA 时区').effect('reload'),
|
|
12
|
-
quizTimeoutSeconds: Schema.number().min(15).max(600).step(1).default(DEFAULT_ANSWERS_CONFIG.quizTimeoutSeconds).description('出题后等待作答的秒数,超时题目作废').effect('reload'),
|
|
13
|
-
enableDrivingQuiz: Schema.boolean().default(DEFAULT_ANSWERS_CONFIG.enableDrivingQuiz).description('启用「科目一」交规小测(原创题面,仅供娱乐参考)').effect('reload'),
|
|
10
|
+
dailyStable: Schema.boolean().default(DEFAULT_ANSWERS_CONFIG.dailyStable).description('同一用户同一天问同一个问题时,答案之书给出相同答案').i18n({ "en": { "description": "Return the same Book of Answers response when the same user asks the same question on the same day." } }).effect('reload'),
|
|
11
|
+
timezone: Schema.string().default(DEFAULT_ANSWERS_CONFIG.timezone).description('dailyStable 判断「同一天」所用的 IANA 时区').i18n({ "en": { "description": "IANA timezone used by dailyStable to determine the day." } }).effect('reload'),
|
|
12
|
+
quizTimeoutSeconds: Schema.number().min(15).max(600).step(1).default(DEFAULT_ANSWERS_CONFIG.quizTimeoutSeconds).description('出题后等待作答的秒数,超时题目作废').i18n({ "en": { "description": "Seconds to wait for a quiz answer; the question expires afterward." } }).effect('reload'),
|
|
13
|
+
enableDrivingQuiz: Schema.boolean().default(DEFAULT_ANSWERS_CONFIG.enableDrivingQuiz).description('启用「科目一」交规小测(原创题面,仅供娱乐参考)').i18n({ "en": { "description": "Enable the original driving-theory quiz, for entertainment and reference only." } }).effect('reload'),
|
|
14
14
|
});
|
|
15
15
|
/** Missing fields fall back to defaults so commands stay usable when Runtime passes a partial view. */
|
|
16
16
|
export function resolveAnswersConfig(config) {
|
package/lib/config.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"config.js","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,SAAS,CAAC;AAEjC,MAAM,CAAC,MAAM,sBAAsB,GAAG,MAAM,CAAC,MAAM,CAAC;IAClD,WAAW,EAAE,KAAK;IAClB,QAAQ,EAAE,eAAe;IACzB,kBAAkB,EAAE,GAAG;IACvB,iBAAiB,EAAE,IAAI;CACxB,CAAC,CAAC;AAEH,gIAAgI;AAChI,MAAM,CAAC,MAAM,aAAa,GAAG,MAAM,CAAC,MAAM,CAAC;IACzC,WAAW,EAAE,MAAM,CAAC,OAAO,EAAE,CAAC,OAAO,CAAC,sBAAsB,CAAC,WAAW,CAAC,CAAC,WAAW,CAAC,2BAA2B,CAAC,CAAC,MAAM,CAAC,QAAQ,CAAC;
|
|
1
|
+
{"version":3,"file":"config.js","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,SAAS,CAAC;AAEjC,MAAM,CAAC,MAAM,sBAAsB,GAAG,MAAM,CAAC,MAAM,CAAC;IAClD,WAAW,EAAE,KAAK;IAClB,QAAQ,EAAE,eAAe;IACzB,kBAAkB,EAAE,GAAG;IACvB,iBAAiB,EAAE,IAAI;CACxB,CAAC,CAAC;AAEH,gIAAgI;AAChI,MAAM,CAAC,MAAM,aAAa,GAAG,MAAM,CAAC,MAAM,CAAC;IACzC,WAAW,EAAE,MAAM,CAAC,OAAO,EAAE,CAAC,OAAO,CAAC,sBAAsB,CAAC,WAAW,CAAC,CAAC,WAAW,CAAC,2BAA2B,CAAC,CAAC,IAAI,CAAC,EAAC,IAAI,EAAE,EAAC,aAAa,EAAE,qGAAqG,EAAC,EAAC,CAAC,CAAC,MAAM,CAAC,QAAQ,CAAC;IACxQ,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,CAAC,OAAO,CAAC,sBAAsB,CAAC,QAAQ,CAAC,CAAC,WAAW,CAAC,gCAAgC,CAAC,CAAC,IAAI,CAAC,EAAC,IAAI,EAAE,EAAC,aAAa,EAAE,yDAAyD,EAAC,EAAC,CAAC,CAAC,MAAM,CAAC,QAAQ,CAAC;IAC1N,kBAAkB,EAAE,MAAM,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,sBAAsB,CAAC,kBAAkB,CAAC,CAAC,WAAW,CAAC,mBAAmB,CAAC,CAAC,IAAI,CAAC,EAAC,IAAI,EAAE,EAAC,aAAa,EAAE,oEAAoE,EAAC,EAAC,CAAC,CAAC,MAAM,CAAC,QAAQ,CAAC;IACrQ,iBAAiB,EAAE,MAAM,CAAC,OAAO,EAAE,CAAC,OAAO,CAAC,sBAAsB,CAAC,iBAAiB,CAAC,CAAC,WAAW,CAAC,0BAA0B,CAAC,CAAC,IAAI,CAAC,EAAC,IAAI,EAAE,EAAC,aAAa,EAAE,gFAAgF,EAAC,EAAC,CAAC,CAAC,MAAM,CAAC,QAAQ,CAAC;CAC/P,CAAC,CAAC;AAIH,uGAAuG;AACvG,MAAM,UAAU,oBAAoB,CAAC,MAA0C;IAC7E,OAAO,EAAE,GAAG,sBAAsB,EAAE,GAAG,CAAC,MAAM,IAAI,EAAE,CAAC,EAAmB,CAAC;AAC3E,CAAC"}
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
export declare const messages: (locale: import("zhin.js").Locale | undefined, key: "conversation" | "disabled" | "fun" | "driving" | "answer" | "correct" | "incorrect" | "drivingFooter" | "bookTitle" | "bookUsage" | "bookIntro" | "bookReply", values?: import("zhin.js").MessageValues) => string;
|
|
2
|
+
//# sourceMappingURL=messages.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"messages.d.ts","sourceRoot":"","sources":["../src/messages.ts"],"names":[],"mappings":"AAEA,eAAO,MAAM,QAAQ,yQAanB,CAAC"}
|
package/lib/messages.js
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { createMessages } from 'zhin.js/i18n';
|
|
2
|
+
export const messages = createMessages({
|
|
3
|
+
conversation: { 'zh-CN': '这个指令需要在真实会话里使用。', en: 'Use this command in a chat conversation.' },
|
|
4
|
+
disabled: { 'zh-CN': '当前未启用科目一小测(enableDrivingQuiz: false)。', en: 'The driving theory quiz is disabled (enableDrivingQuiz: false).' },
|
|
5
|
+
fun: { 'zh-CN': '😴 好无聊?请听题!', en: '😴 Time for a trivia question! (Chinese question bank)' },
|
|
6
|
+
driving: { 'zh-CN': '🚗 科目一小测,请听题!', en: '🚗 Chinese driving theory quiz (Chinese question bank)' },
|
|
7
|
+
answer: { 'zh-CN': '直接回复 {letters} 作答,{seconds} 秒内有效。', en: 'Reply with {letters} within {seconds} seconds.' },
|
|
8
|
+
correct: { 'zh-CN': '✅ 答对了!', en: '✅ Correct!' },
|
|
9
|
+
incorrect: { 'zh-CN': '❌ 答错了,正确答案是 {answer}', en: '❌ Incorrect. The answer is {answer}' },
|
|
10
|
+
drivingFooter: { 'zh-CN': '\n(原创题面,依据公开法规整理,仅供娱乐参考,以现行规定为准)', en: '\n(Original questions based on public Chinese traffic regulations; for entertainment and reference. Current regulations take precedence.)' },
|
|
11
|
+
bookTitle: { 'zh-CN': '📖 答案之书', en: '📖 Book of Answers (Chinese answer collection)' },
|
|
12
|
+
bookUsage: { 'zh-CN': '用法:{command} <问题>\n例如:{command} 今天要不要出去走走', en: 'Usage: {command} <question>\nExample: {command} Should I go for a walk today?' },
|
|
13
|
+
bookIntro: { 'zh-CN': '心里默念问题后发送,书会翻开一页给你答案。', en: 'Think of your question and send it. The book will reveal an answer in Chinese.' },
|
|
14
|
+
bookReply: { 'zh-CN': '📖 你问:{question}\n✨ 答案之书:{answer}', en: '📖 Your question: {question}\n✨ Book of Answers (Chinese): {answer}' },
|
|
15
|
+
});
|
|
16
|
+
//# sourceMappingURL=messages.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"messages.js","sourceRoot":"","sources":["../src/messages.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC;AAE9C,MAAM,CAAC,MAAM,QAAQ,GAAG,cAAc,CAAC;IACrC,YAAY,EAAE,EAAE,OAAO,EAAE,iBAAiB,EAAE,EAAE,EAAE,0CAA0C,EAAE;IAC5F,QAAQ,EAAE,EAAE,OAAO,EAAE,uCAAuC,EAAE,EAAE,EAAE,iEAAiE,EAAE;IACrI,GAAG,EAAE,EAAE,OAAO,EAAE,aAAa,EAAE,EAAE,EAAE,wDAAwD,EAAE;IAC7F,OAAO,EAAE,EAAE,OAAO,EAAE,eAAe,EAAE,EAAE,EAAE,wDAAwD,EAAE;IACnG,MAAM,EAAE,EAAE,OAAO,EAAE,mCAAmC,EAAE,EAAE,EAAE,gDAAgD,EAAE;IAC9G,OAAO,EAAE,EAAE,OAAO,EAAE,QAAQ,EAAE,EAAE,EAAE,YAAY,EAAE;IAChD,SAAS,EAAE,EAAE,OAAO,EAAE,sBAAsB,EAAE,EAAE,EAAE,qCAAqC,EAAE;IACzF,aAAa,EAAE,EAAE,OAAO,EAAE,kCAAkC,EAAE,EAAE,EAAE,2IAA2I,EAAE;IAC/M,SAAS,EAAE,EAAE,OAAO,EAAE,SAAS,EAAE,EAAE,EAAE,gDAAgD,EAAE;IACvF,SAAS,EAAE,EAAE,OAAO,EAAE,2CAA2C,EAAE,EAAE,EAAE,+EAA+E,EAAE;IACxJ,SAAS,EAAE,EAAE,OAAO,EAAE,uBAAuB,EAAE,EAAE,EAAE,gFAAgF,EAAE;IACrI,SAAS,EAAE,EAAE,OAAO,EAAE,mCAAmC,EAAE,EAAE,EAAE,qEAAqE,EAAE;CACvI,CAAC,CAAC"}
|
package/lib/quiz.d.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { type Locale } from 'zhin.js/i18n';
|
|
1
2
|
import { type QuizLetter, type QuizQuestion } from './quiz-types.js';
|
|
2
3
|
export type QuizKind = 'fun' | 'driving';
|
|
3
4
|
/** Structural subset of the Runtime / Command message used to scope pending questions. */
|
|
@@ -16,6 +17,7 @@ export interface QuizConversationInput {
|
|
|
16
17
|
} | string;
|
|
17
18
|
}
|
|
18
19
|
export interface PendingQuiz {
|
|
20
|
+
readonly locale?: Locale;
|
|
19
21
|
readonly kind: QuizKind;
|
|
20
22
|
readonly question: QuizQuestion;
|
|
21
23
|
readonly expiresAt: number;
|
|
@@ -35,12 +37,12 @@ export declare function quizKey(input: QuizConversationInput | undefined): strin
|
|
|
35
37
|
/** 只接受单个选项字母(大小写、全角均可),其余文本一律放行给后续中间件。 */
|
|
36
38
|
export declare function parseQuizLetter(raw: string | undefined): QuizLetter | undefined;
|
|
37
39
|
export declare function pickQuestion(bank: readonly QuizQuestion[], exceptId?: string): QuizQuestion;
|
|
38
|
-
export declare function formatQuestion(kind: QuizKind, question: QuizQuestion, timeoutSeconds: number): string;
|
|
40
|
+
export declare function formatQuestion(kind: QuizKind, question: QuizQuestion, timeoutSeconds: number, locale?: Locale): string;
|
|
39
41
|
export declare function formatJudgement(judgement: QuizJudgement): string;
|
|
40
42
|
/** Generation-owned, in-memory only: pending questions vanish on reload, which is acceptable for a party game. */
|
|
41
43
|
export declare class QuizService {
|
|
42
44
|
#private;
|
|
43
|
-
ask(key: string, kind: QuizKind, bank: readonly QuizQuestion[], timeoutSeconds: number, now?: number): QuizQuestion;
|
|
45
|
+
ask(key: string, kind: QuizKind, bank: readonly QuizQuestion[], timeoutSeconds: number, now?: number, locale?: Locale): QuizQuestion;
|
|
44
46
|
peek(key: string, now?: number): PendingQuiz | undefined;
|
|
45
47
|
/** Judge and consume the pending question; letters beyond the option count are ignored (undefined). */
|
|
46
48
|
answer(key: string, letter: QuizLetter, now?: number): QuizJudgement | undefined;
|
package/lib/quiz.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"quiz.d.ts","sourceRoot":"","sources":["../src/quiz.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"quiz.d.ts","sourceRoot":"","sources":["../src/quiz.ts"],"names":[],"mappings":"AAAA,OAAO,EAAiB,KAAK,MAAM,EAAE,MAAM,cAAc,CAAC;AAG1D,OAAO,EAAgB,KAAK,UAAU,EAAE,KAAK,YAAY,EAAE,MAAM,iBAAiB,CAAC;AAEnF,MAAM,MAAM,QAAQ,GAAG,KAAK,GAAG,SAAS,CAAC;AAEzC,0FAA0F;AAC1F,MAAM,WAAW,qBAAqB;IACpC,QAAQ,CAAC,YAAY,CAAC,EAAE;QACtB,QAAQ,CAAC,QAAQ,CAAC,EAAE;YAAE,QAAQ,CAAC,EAAE,CAAC,EAAE,MAAM,CAAC;YAAC,QAAQ,CAAC,OAAO,CAAC,EAAE,MAAM,CAAA;SAAE,CAAC;QACxE,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,CAAC;QACvB,QAAQ,CAAC,EAAE,CAAC,EAAE,MAAM,CAAC;QACrB,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;KAC5B,CAAC;IACF,QAAQ,CAAC,MAAM,CAAC,EAAE;QAAE,QAAQ,CAAC,EAAE,CAAC,EAAE,MAAM,CAAA;KAAE,GAAG,MAAM,CAAC;CACrD;AAED,MAAM,WAAW,WAAW;IAC1B,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,IAAI,EAAE,QAAQ,CAAC;IACxB,QAAQ,CAAC,QAAQ,EAAE,YAAY,CAAC;IAChC,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;CAC5B;AAED,MAAM,WAAW,aAAa;IAC5B,QAAQ,CAAC,OAAO,EAAE,OAAO,CAAC;IAC1B,QAAQ,CAAC,MAAM,EAAE,UAAU,CAAC;IAC5B,QAAQ,CAAC,OAAO,EAAE,WAAW,CAAC;CAC/B;AAED,wFAAwF;AACxF,eAAO,MAAM,mBAAmB,OAAO,CAAC;AAExC;;;GAGG;AACH,wBAAgB,OAAO,CAAC,KAAK,EAAE,qBAAqB,GAAG,SAAS,GAAG,MAAM,GAAG,SAAS,CAMpF;AAED,0CAA0C;AAC1C,wBAAgB,eAAe,CAAC,GAAG,EAAE,MAAM,GAAG,SAAS,GAAG,UAAU,GAAG,SAAS,CAI/E;AAED,wBAAgB,YAAY,CAAC,IAAI,EAAE,SAAS,YAAY,EAAE,EAAE,QAAQ,CAAC,EAAE,MAAM,GAAG,YAAY,CAO3F;AAED,wBAAgB,cAAc,CAAC,IAAI,EAAE,QAAQ,EAAE,QAAQ,EAAE,YAAY,EAAE,cAAc,EAAE,MAAM,EAAE,MAAM,GAAE,MAAgB,GAAG,MAAM,CAM/H;AAED,wBAAgB,eAAe,CAAC,SAAS,EAAE,aAAa,GAAG,MAAM,CAOhE;AAED,kHAAkH;AAClH,qBAAa,WAAW;;IAKtB,GAAG,CAAC,GAAG,EAAE,MAAM,EAAE,IAAI,EAAE,QAAQ,EAAE,IAAI,EAAE,SAAS,YAAY,EAAE,EAAE,cAAc,EAAE,MAAM,EAAE,GAAG,SAAa,EAAE,MAAM,GAAE,MAAgB,GAAG,YAAY;IAajJ,IAAI,CAAC,GAAG,EAAE,MAAM,EAAE,GAAG,SAAa,GAAG,WAAW,GAAG,SAAS;IAU5D,uGAAuG;IACvG,MAAM,CAAC,GAAG,EAAE,MAAM,EAAE,MAAM,EAAE,UAAU,EAAE,GAAG,SAAa,GAAG,aAAa,GAAG,SAAS;IASpF,MAAM,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO;IAI5B,KAAK,CAAC,GAAG,SAAa,GAAG,IAAI;IAI7B,IAAI,IAAI,IAAI,MAAM,CAEjB;IAED,OAAO,IAAI,IAAI;CAShB"}
|
package/lib/quiz.js
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { resolveLocale } from 'zhin.js/i18n';
|
|
2
|
+
import { messages } from './messages.js';
|
|
1
3
|
import { randomInt } from 'node:crypto';
|
|
2
4
|
import { QUIZ_LETTERS } from './quiz-types.js';
|
|
3
5
|
/** Upper bound on simultaneously pending questions; the oldest one is dropped first. */
|
|
@@ -32,18 +34,19 @@ export function pickQuestion(bank, exceptId) {
|
|
|
32
34
|
question = bank[(bank.indexOf(question) + 1 + randomInt(bank.length - 1)) % bank.length];
|
|
33
35
|
return question;
|
|
34
36
|
}
|
|
35
|
-
export function formatQuestion(kind, question, timeoutSeconds) {
|
|
36
|
-
const head = kind
|
|
37
|
+
export function formatQuestion(kind, question, timeoutSeconds, locale = 'zh-CN') {
|
|
38
|
+
const head = messages(locale, kind);
|
|
37
39
|
const options = question.options.map((option, index) => `${QUIZ_LETTERS[index]}. ${option}`);
|
|
38
40
|
const letters = QUIZ_LETTERS.slice(0, question.options.length).join('/');
|
|
39
41
|
return [`${head}【${question.category}】`, '', question.question, '', ...options, '',
|
|
40
|
-
|
|
42
|
+
messages(locale, 'answer', { letters, seconds: timeoutSeconds })].join('\n');
|
|
41
43
|
}
|
|
42
44
|
export function formatJudgement(judgement) {
|
|
43
45
|
const { question, kind } = judgement.pending;
|
|
46
|
+
const locale = resolveLocale(judgement.pending.locale);
|
|
44
47
|
const answerText = `${question.answer}. ${question.options[QUIZ_LETTERS.indexOf(question.answer)]}`;
|
|
45
|
-
const verdict = judgement.correct ?
|
|
46
|
-
const footer = kind === 'driving' ? '
|
|
48
|
+
const verdict = judgement.correct ? messages(locale, 'correct') : messages(locale, 'incorrect', { answer: answerText });
|
|
49
|
+
const footer = kind === 'driving' ? messages(locale, 'drivingFooter') : '';
|
|
47
50
|
return `${verdict}\n💡 ${question.explanation}${footer}`;
|
|
48
51
|
}
|
|
49
52
|
/** Generation-owned, in-memory only: pending questions vanish on reload, which is acceptable for a party game. */
|
|
@@ -51,12 +54,12 @@ export class QuizService {
|
|
|
51
54
|
#pending = new Map();
|
|
52
55
|
#lastQuestion = new Map();
|
|
53
56
|
#disposed = false;
|
|
54
|
-
ask(key, kind, bank, timeoutSeconds, now = Date.now()) {
|
|
57
|
+
ask(key, kind, bank, timeoutSeconds, now = Date.now(), locale = 'zh-CN') {
|
|
55
58
|
this.#assertLive();
|
|
56
59
|
this.sweep(now);
|
|
57
60
|
const question = pickQuestion(bank, this.#lastQuestion.get(key));
|
|
58
61
|
this.#pending.delete(key);
|
|
59
|
-
this.#pending.set(key, { kind, question, expiresAt: now + timeoutSeconds * 1000 });
|
|
62
|
+
this.#pending.set(key, { kind, question, locale, expiresAt: now + timeoutSeconds * 1000 });
|
|
60
63
|
this.#lastQuestion.delete(key);
|
|
61
64
|
this.#lastQuestion.set(key, question.id);
|
|
62
65
|
while (this.#pending.size > MAX_PENDING_QUIZZES)
|
package/lib/quiz.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"quiz.js","sourceRoot":"","sources":["../src/quiz.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AACxC,OAAO,EAAE,YAAY,EAAsC,MAAM,iBAAiB,CAAC;
|
|
1
|
+
{"version":3,"file":"quiz.js","sourceRoot":"","sources":["../src/quiz.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@zhin.js/plugin-answers",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.5",
|
|
4
4
|
"description": "Zhin.js 答案之书、趣味选择题与交规小测(原创内容,Plugin Runtime)",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./lib/index.js",
|
|
@@ -37,12 +37,12 @@
|
|
|
37
37
|
},
|
|
38
38
|
"license": "MIT",
|
|
39
39
|
"dependencies": {
|
|
40
|
-
"@zhin.js/core": "1.1.
|
|
40
|
+
"@zhin.js/core": "1.1.51"
|
|
41
41
|
},
|
|
42
42
|
"devDependencies": {
|
|
43
43
|
"typescript": "^6.0.3",
|
|
44
44
|
"vitest": "^4.1.10",
|
|
45
|
-
"zhin.js": "1.1.
|
|
45
|
+
"zhin.js": "1.1.16"
|
|
46
46
|
},
|
|
47
47
|
"repository": {
|
|
48
48
|
"type": "git",
|
|
@@ -75,9 +75,9 @@
|
|
|
75
75
|
"plugins": []
|
|
76
76
|
},
|
|
77
77
|
"peerDependencies": {
|
|
78
|
-
"@zhin.js/command": "^1.1.
|
|
79
|
-
"@zhin.js/middleware": "^1.1.
|
|
80
|
-
"zhin.js": "^1.1.
|
|
78
|
+
"@zhin.js/command": "^1.1.11",
|
|
79
|
+
"@zhin.js/middleware": "^1.1.10",
|
|
80
|
+
"zhin.js": "^1.1.16"
|
|
81
81
|
},
|
|
82
82
|
"peerDependenciesMeta": {
|
|
83
83
|
"@zhin.js/command": {
|
package/schema.json
CHANGED
|
@@ -5,6 +5,11 @@
|
|
|
5
5
|
"dailyStable": {
|
|
6
6
|
"type": "boolean",
|
|
7
7
|
"description": "同一用户同一天问同一个问题时,答案之书给出相同答案",
|
|
8
|
+
"x-i18n": {
|
|
9
|
+
"en": {
|
|
10
|
+
"description": "Return the same Book of Answers response when the same user asks the same question on the same day."
|
|
11
|
+
}
|
|
12
|
+
},
|
|
8
13
|
"default": false,
|
|
9
14
|
"x-zhin-config": {
|
|
10
15
|
"effect": "reload"
|
|
@@ -13,6 +18,11 @@
|
|
|
13
18
|
"timezone": {
|
|
14
19
|
"type": "string",
|
|
15
20
|
"description": "dailyStable 判断「同一天」所用的 IANA 时区",
|
|
21
|
+
"x-i18n": {
|
|
22
|
+
"en": {
|
|
23
|
+
"description": "IANA timezone used by dailyStable to determine the day."
|
|
24
|
+
}
|
|
25
|
+
},
|
|
16
26
|
"default": "Asia/Shanghai",
|
|
17
27
|
"x-zhin-config": {
|
|
18
28
|
"effect": "reload"
|
|
@@ -21,6 +31,11 @@
|
|
|
21
31
|
"quizTimeoutSeconds": {
|
|
22
32
|
"type": "number",
|
|
23
33
|
"description": "出题后等待作答的秒数,超时题目作废",
|
|
34
|
+
"x-i18n": {
|
|
35
|
+
"en": {
|
|
36
|
+
"description": "Seconds to wait for a quiz answer; the question expires afterward."
|
|
37
|
+
}
|
|
38
|
+
},
|
|
24
39
|
"default": 120,
|
|
25
40
|
"minimum": 15,
|
|
26
41
|
"maximum": 600,
|
|
@@ -32,6 +47,11 @@
|
|
|
32
47
|
"enableDrivingQuiz": {
|
|
33
48
|
"type": "boolean",
|
|
34
49
|
"description": "启用「科目一」交规小测(原创题面,仅供娱乐参考)",
|
|
50
|
+
"x-i18n": {
|
|
51
|
+
"en": {
|
|
52
|
+
"description": "Enable the original driving-theory quiz, for entertainment and reference only."
|
|
53
|
+
}
|
|
54
|
+
},
|
|
35
55
|
"default": true,
|
|
36
56
|
"x-zhin-config": {
|
|
37
57
|
"effect": "reload"
|
package/src/book.ts
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
|
+
import { type Locale } from 'zhin.js/i18n';
|
|
2
|
+
import { messages } from './messages.js';
|
|
1
3
|
import { createHash, randomInt } from 'node:crypto';
|
|
2
4
|
import { BOOK_ANSWERS } from './book-data.js';
|
|
3
5
|
|
|
4
|
-
export
|
|
5
|
-
'
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
'心里默念问题后发送,书会翻开一页给你答案。',
|
|
9
|
-
].join('\n');
|
|
6
|
+
export function bookUsage(locale: Locale = 'zh-CN', command?: string): string {
|
|
7
|
+
return [messages(locale, 'bookTitle'), ...(command === undefined ? [] : [messages(locale, 'bookUsage', { command })]), messages(locale, 'bookIntro')].join('\n');
|
|
8
|
+
}
|
|
9
|
+
export const BOOK_USAGE = bookUsage();
|
|
10
10
|
|
|
11
11
|
export const MAX_QUESTION_LENGTH = 200;
|
|
12
12
|
|
|
@@ -43,6 +43,6 @@ export function pickBookAnswer(input: BookPickInput, answers: readonly string[]
|
|
|
43
43
|
return answers[digest.readUInt32BE(0) % answers.length]!;
|
|
44
44
|
}
|
|
45
45
|
|
|
46
|
-
export function replyBook(question: string, answer: string): string {
|
|
47
|
-
return
|
|
46
|
+
export function replyBook(question: string, answer: string, locale: Locale = 'zh-CN'): string {
|
|
47
|
+
return messages(locale, 'bookReply', { question, answer });
|
|
48
48
|
}
|
package/src/config.ts
CHANGED
|
@@ -9,10 +9,10 @@ export const DEFAULT_ANSWERS_CONFIG = Object.freeze({
|
|
|
9
9
|
|
|
10
10
|
/** Authoring declaration; scripts/sync-plugin-config-schemas.mjs generates the packaged schema.json for Runtime and Console. */
|
|
11
11
|
export const answersConfig = Schema.object({
|
|
12
|
-
dailyStable: Schema.boolean().default(DEFAULT_ANSWERS_CONFIG.dailyStable).description('同一用户同一天问同一个问题时,答案之书给出相同答案').effect('reload'),
|
|
13
|
-
timezone: Schema.string().default(DEFAULT_ANSWERS_CONFIG.timezone).description('dailyStable 判断「同一天」所用的 IANA 时区').effect('reload'),
|
|
14
|
-
quizTimeoutSeconds: Schema.number().min(15).max(600).step(1).default(DEFAULT_ANSWERS_CONFIG.quizTimeoutSeconds).description('出题后等待作答的秒数,超时题目作废').effect('reload'),
|
|
15
|
-
enableDrivingQuiz: Schema.boolean().default(DEFAULT_ANSWERS_CONFIG.enableDrivingQuiz).description('启用「科目一」交规小测(原创题面,仅供娱乐参考)').effect('reload'),
|
|
12
|
+
dailyStable: Schema.boolean().default(DEFAULT_ANSWERS_CONFIG.dailyStable).description('同一用户同一天问同一个问题时,答案之书给出相同答案').i18n({"en": {"description": "Return the same Book of Answers response when the same user asks the same question on the same day."}}).effect('reload'),
|
|
13
|
+
timezone: Schema.string().default(DEFAULT_ANSWERS_CONFIG.timezone).description('dailyStable 判断「同一天」所用的 IANA 时区').i18n({"en": {"description": "IANA timezone used by dailyStable to determine the day."}}).effect('reload'),
|
|
14
|
+
quizTimeoutSeconds: Schema.number().min(15).max(600).step(1).default(DEFAULT_ANSWERS_CONFIG.quizTimeoutSeconds).description('出题后等待作答的秒数,超时题目作废').i18n({"en": {"description": "Seconds to wait for a quiz answer; the question expires afterward."}}).effect('reload'),
|
|
15
|
+
enableDrivingQuiz: Schema.boolean().default(DEFAULT_ANSWERS_CONFIG.enableDrivingQuiz).description('启用「科目一」交规小测(原创题面,仅供娱乐参考)').i18n({"en": {"description": "Enable the original driving-theory quiz, for entertainment and reference only."}}).effect('reload'),
|
|
16
16
|
});
|
|
17
17
|
|
|
18
18
|
export type AnswersConfig = Schema.Output<typeof answersConfig>;
|
package/src/messages.ts
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { createMessages } from 'zhin.js/i18n';
|
|
2
|
+
|
|
3
|
+
export const messages = createMessages({
|
|
4
|
+
conversation: { 'zh-CN': '这个指令需要在真实会话里使用。', en: 'Use this command in a chat conversation.' },
|
|
5
|
+
disabled: { 'zh-CN': '当前未启用科目一小测(enableDrivingQuiz: false)。', en: 'The driving theory quiz is disabled (enableDrivingQuiz: false).' },
|
|
6
|
+
fun: { 'zh-CN': '😴 好无聊?请听题!', en: '😴 Time for a trivia question! (Chinese question bank)' },
|
|
7
|
+
driving: { 'zh-CN': '🚗 科目一小测,请听题!', en: '🚗 Chinese driving theory quiz (Chinese question bank)' },
|
|
8
|
+
answer: { 'zh-CN': '直接回复 {letters} 作答,{seconds} 秒内有效。', en: 'Reply with {letters} within {seconds} seconds.' },
|
|
9
|
+
correct: { 'zh-CN': '✅ 答对了!', en: '✅ Correct!' },
|
|
10
|
+
incorrect: { 'zh-CN': '❌ 答错了,正确答案是 {answer}', en: '❌ Incorrect. The answer is {answer}' },
|
|
11
|
+
drivingFooter: { 'zh-CN': '\n(原创题面,依据公开法规整理,仅供娱乐参考,以现行规定为准)', en: '\n(Original questions based on public Chinese traffic regulations; for entertainment and reference. Current regulations take precedence.)' },
|
|
12
|
+
bookTitle: { 'zh-CN': '📖 答案之书', en: '📖 Book of Answers (Chinese answer collection)' },
|
|
13
|
+
bookUsage: { 'zh-CN': '用法:{command} <问题>\n例如:{command} 今天要不要出去走走', en: 'Usage: {command} <question>\nExample: {command} Should I go for a walk today?' },
|
|
14
|
+
bookIntro: { 'zh-CN': '心里默念问题后发送,书会翻开一页给你答案。', en: 'Think of your question and send it. The book will reveal an answer in Chinese.' },
|
|
15
|
+
bookReply: { 'zh-CN': '📖 你问:{question}\n✨ 答案之书:{answer}', en: '📖 Your question: {question}\n✨ Book of Answers (Chinese): {answer}' },
|
|
16
|
+
});
|
package/src/quiz.ts
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { resolveLocale, type Locale } from 'zhin.js/i18n';
|
|
2
|
+
import { messages } from './messages.js';
|
|
1
3
|
import { randomInt } from 'node:crypto';
|
|
2
4
|
import { QUIZ_LETTERS, type QuizLetter, type QuizQuestion } from './quiz-types.js';
|
|
3
5
|
|
|
@@ -15,6 +17,7 @@ export interface QuizConversationInput {
|
|
|
15
17
|
}
|
|
16
18
|
|
|
17
19
|
export interface PendingQuiz {
|
|
20
|
+
readonly locale?: Locale;
|
|
18
21
|
readonly kind: QuizKind;
|
|
19
22
|
readonly question: QuizQuestion;
|
|
20
23
|
readonly expiresAt: number;
|
|
@@ -57,19 +60,20 @@ export function pickQuestion(bank: readonly QuizQuestion[], exceptId?: string):
|
|
|
57
60
|
return question;
|
|
58
61
|
}
|
|
59
62
|
|
|
60
|
-
export function formatQuestion(kind: QuizKind, question: QuizQuestion, timeoutSeconds: number): string {
|
|
61
|
-
const head = kind
|
|
63
|
+
export function formatQuestion(kind: QuizKind, question: QuizQuestion, timeoutSeconds: number, locale: Locale = 'zh-CN'): string {
|
|
64
|
+
const head = messages(locale, kind);
|
|
62
65
|
const options = question.options.map((option, index) => `${QUIZ_LETTERS[index]}. ${option}`);
|
|
63
66
|
const letters = QUIZ_LETTERS.slice(0, question.options.length).join('/');
|
|
64
67
|
return [`${head}【${question.category}】`, '', question.question, '', ...options, '',
|
|
65
|
-
|
|
68
|
+
messages(locale, 'answer', { letters, seconds: timeoutSeconds })].join('\n');
|
|
66
69
|
}
|
|
67
70
|
|
|
68
71
|
export function formatJudgement(judgement: QuizJudgement): string {
|
|
69
72
|
const { question, kind } = judgement.pending;
|
|
73
|
+
const locale = resolveLocale(judgement.pending.locale);
|
|
70
74
|
const answerText = `${question.answer}. ${question.options[QUIZ_LETTERS.indexOf(question.answer)]}`;
|
|
71
|
-
const verdict = judgement.correct ?
|
|
72
|
-
const footer = kind === 'driving' ? '
|
|
75
|
+
const verdict = judgement.correct ? messages(locale, 'correct') : messages(locale, 'incorrect', { answer: answerText });
|
|
76
|
+
const footer = kind === 'driving' ? messages(locale, 'drivingFooter') : '';
|
|
73
77
|
return `${verdict}\n💡 ${question.explanation}${footer}`;
|
|
74
78
|
}
|
|
75
79
|
|
|
@@ -79,12 +83,12 @@ export class QuizService {
|
|
|
79
83
|
readonly #lastQuestion = new Map<string, string>();
|
|
80
84
|
#disposed = false;
|
|
81
85
|
|
|
82
|
-
ask(key: string, kind: QuizKind, bank: readonly QuizQuestion[], timeoutSeconds: number, now = Date.now()): QuizQuestion {
|
|
86
|
+
ask(key: string, kind: QuizKind, bank: readonly QuizQuestion[], timeoutSeconds: number, now = Date.now(), locale: Locale = 'zh-CN'): QuizQuestion {
|
|
83
87
|
this.#assertLive();
|
|
84
88
|
this.sweep(now);
|
|
85
89
|
const question = pickQuestion(bank, this.#lastQuestion.get(key));
|
|
86
90
|
this.#pending.delete(key);
|
|
87
|
-
this.#pending.set(key, { kind, question, expiresAt: now + timeoutSeconds * 1000 });
|
|
91
|
+
this.#pending.set(key, { kind, question, locale, expiresAt: now + timeoutSeconds * 1000 });
|
|
88
92
|
this.#lastQuestion.delete(key);
|
|
89
93
|
this.#lastQuestion.set(key, question.id);
|
|
90
94
|
while (this.#pending.size > MAX_PENDING_QUIZZES) this.#pending.delete(this.#pending.keys().next().value!);
|