@zhin.js/plugin-music 1.1.15 → 1.1.16

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (51) hide show
  1. package/commands/cookie/delete/[source]/index.js +8 -7
  2. package/commands/cookie/delete/[source]/index.ts +8 -7
  3. package/commands/cookie/get/[source]/index.js +10 -9
  4. package/commands/cookie/get/[source]/index.ts +10 -9
  5. package/commands/cookie/set/[source]/index.js +8 -7
  6. package/commands/cookie/set/[source]/index.ts +8 -7
  7. package/commands/cookie/status/index.js +7 -7
  8. package/commands/cookie/status/index.ts +7 -7
  9. package/commands/login/[platform]/index.js +22 -18
  10. package/commands/login/[platform]/index.ts +23 -19
  11. package/commands//347/202/271/346/255/214/[keyword]/index.js +13 -9
  12. package/commands//347/202/271/346/255/214/[keyword]/index.ts +13 -9
  13. package/lib/i18n.d.ts +6 -0
  14. package/lib/i18n.d.ts.map +1 -0
  15. package/lib/i18n.js +50 -0
  16. package/lib/i18n.js.map +1 -0
  17. package/lib/login/index.d.ts +5 -2
  18. package/lib/login/index.d.ts.map +1 -1
  19. package/lib/login/index.js +13 -10
  20. package/lib/login/index.js.map +1 -1
  21. package/lib/login/netease.d.ts +3 -2
  22. package/lib/login/netease.d.ts.map +1 -1
  23. package/lib/login/netease.js +10 -9
  24. package/lib/login/netease.js.map +1 -1
  25. package/lib/login/qq.d.ts +2 -1
  26. package/lib/login/qq.d.ts.map +1 -1
  27. package/lib/login/qq.js +12 -11
  28. package/lib/login/qq.js.map +1 -1
  29. package/lib/login/types.d.ts +3 -2
  30. package/lib/login/types.d.ts.map +1 -1
  31. package/lib/login/types.js.map +1 -1
  32. package/lib/music-lib.d.ts +3 -2
  33. package/lib/music-lib.d.ts.map +1 -1
  34. package/lib/music-lib.js +8 -7
  35. package/lib/music-lib.js.map +1 -1
  36. package/lib/session.d.ts +7 -10
  37. package/lib/session.d.ts.map +1 -1
  38. package/lib/session.js +9 -8
  39. package/lib/session.js.map +1 -1
  40. package/middlewares/music-selection/index.js +9 -7
  41. package/middlewares/music-selection/index.ts +9 -6
  42. package/package.json +11 -10
  43. package/skills/music/tools/music-share/index.js +1 -1
  44. package/skills/music/tools/music-share/index.ts +1 -1
  45. package/src/i18n.ts +50 -0
  46. package/src/login/index.ts +16 -10
  47. package/src/login/netease.ts +11 -9
  48. package/src/login/qq.ts +13 -11
  49. package/src/login/types.ts +3 -2
  50. package/src/music-lib.ts +9 -7
  51. package/src/session.ts +13 -11
@@ -1,23 +1,24 @@
1
1
  // Generated by build-plugin-runtime-entries.mjs. Do not edit.
2
+ import { messages as t, sourceName, usage } from "../../../../lib/i18n.js";
2
3
  import { defineCommand } from 'zhin.js/command';
3
4
  import { musicRuntimeToken } from "../../../../lib/runtime.js";
4
- import { SOURCE_DISPLAY_NAME } from "../../../../lib/config.js";
5
5
  const VALID_SOURCES = new Set(['qq', 'netease', 'kuwo', 'kugou']);
6
6
  export default defineCommand({
7
- description: '删除音乐平台凭证',
7
+ description: '删除音乐平台凭证', descriptionI18n: { en: "Delete music platform credentials" },
8
8
  alias: ['删除'],
9
- params: { source: { type: 'string', description: '音乐源(qq/netease/kuwo/kugou)' } },
9
+ params: { source: { type: 'string', description: '音乐源(qq/netease/kuwo/kugou)', descriptionI18n: { en: "Music source (qq / netease / kuwo / kugou)" } } },
10
10
  permit: ['role(master)'],
11
- async execute({ params, args, use }) {
11
+ async execute({ params, args, use, locale, formatCommand }) {
12
12
  const source = String(params.source ?? '').trim();
13
13
  const key = String(args[0] ?? '').trim();
14
14
  if (!VALID_SOURCES.has(source)) {
15
- return `不支持的音乐源:${source}`;
15
+ return t(locale, 'unsupported', { source });
16
16
  }
17
17
  if (!key) {
18
- return `格式:cookie delete ${source} <key>`;
18
+ const command = formatCommand('cookie delete');
19
+ return usage(locale, command === undefined ? undefined : `${command} ${source} <key>`);
19
20
  }
20
21
  await use(musicRuntimeToken).credentials.delete(source, key);
21
- return `[${SOURCE_DISPLAY_NAME[source]}] 凭证 ${key} 已删除`;
22
+ return t(locale, 'deleted', { source: sourceName(source, locale), key });
22
23
  },
23
24
  });
@@ -1,27 +1,28 @@
1
+ import { messages as t, sourceName, usage } from '../../../../src/i18n.js';
1
2
  import { defineCommand } from 'zhin.js/command';
2
3
  import { musicRuntimeToken } from '../../../../src/runtime.js';
3
- import { SOURCE_DISPLAY_NAME } from '../../../../src/config.js';
4
4
  import type { MusicSource } from '../../../../src/types.js';
5
5
 
6
6
  const VALID_SOURCES = new Set<string>(['qq', 'netease', 'kuwo', 'kugou']);
7
7
 
8
8
  export default defineCommand({
9
- description: '删除音乐平台凭证',
9
+ description: '删除音乐平台凭证', descriptionI18n: { en: "Delete music platform credentials" },
10
10
  alias: ['删除'],
11
- params: { source: { type: 'string', description: '音乐源(qq/netease/kuwo/kugou)' } },
11
+ params: { source: { type: 'string', description: '音乐源(qq/netease/kuwo/kugou)', descriptionI18n: { en: "Music source (qq / netease / kuwo / kugou)" } } },
12
12
  permit: ['role(master)'],
13
- async execute({ params, args, use }) {
13
+ async execute({ params, args, use, locale, formatCommand }) {
14
14
  const source = String(params.source ?? '').trim();
15
15
  const key = String(args[0] ?? '').trim();
16
16
 
17
17
  if (!VALID_SOURCES.has(source)) {
18
- return `不支持的音乐源:${source}`;
18
+ return t(locale, 'unsupported', { source });
19
19
  }
20
20
  if (!key) {
21
- return `格式:cookie delete ${source} <key>`;
21
+ const command = formatCommand('cookie delete');
22
+ return usage(locale, command === undefined ? undefined : `${command} ${source} <key>`);
22
23
  }
23
24
 
24
25
  await use(musicRuntimeToken).credentials.delete(source as MusicSource, key);
25
- return `[${SOURCE_DISPLAY_NAME[source as MusicSource]}] 凭证 ${key} 已删除`;
26
+ return t(locale, 'deleted', { source: sourceName(source as MusicSource, locale), key });
26
27
  },
27
28
  });
@@ -1,29 +1,30 @@
1
1
  // Generated by build-plugin-runtime-entries.mjs. Do not edit.
2
+ import { messages as t, sourceName, usage } from "../../../../lib/i18n.js";
2
3
  import { defineCommand } from 'zhin.js/command';
3
4
  import { musicRuntimeToken } from "../../../../lib/runtime.js";
4
- import { SOURCE_DISPLAY_NAME } from "../../../../lib/config.js";
5
5
  const VALID_SOURCES = new Set(['qq', 'netease', 'kuwo', 'kugou']);
6
6
  export default defineCommand({
7
- description: '查看音乐平台凭证(脱敏)',
7
+ description: '查看音乐平台凭证(脱敏)', descriptionI18n: { en: "View music platform credentials with sensitive values masked" },
8
8
  alias: ['查看'],
9
- params: { source: { type: 'string', description: '音乐源(qq/netease/kuwo/kugou)' } },
9
+ params: { source: { type: 'string', description: '音乐源(qq/netease/kuwo/kugou)', descriptionI18n: { en: "Music source (qq / netease / kuwo / kugou)" } } },
10
10
  permit: ['role(master)'],
11
- async execute({ params, args, use }) {
11
+ async execute({ params, args, use, locale, formatCommand }) {
12
12
  const source = String(params.source ?? '').trim();
13
13
  const key = String(args[0] ?? '').trim();
14
14
  if (!VALID_SOURCES.has(source)) {
15
- return `不支持的音乐源:${source}`;
15
+ return t(locale, 'unsupported', { source });
16
16
  }
17
17
  if (!key) {
18
- return `格式:cookie get ${source} <key>`;
18
+ const command = formatCommand('cookie get');
19
+ return usage(locale, command === undefined ? undefined : `${command} ${source} <key>`);
19
20
  }
20
- const sourceName = SOURCE_DISPLAY_NAME[source];
21
+ const label = sourceName(source, locale);
21
22
  const value = await use(musicRuntimeToken).credentials.get(source, key);
22
23
  if (!value)
23
- return `[${sourceName}] 凭证 ${key} 未设置`;
24
+ return t(locale, 'credentialUnset', { source: label, key });
24
25
  const masked = value.length > 10
25
26
  ? `${value.slice(0, 4)}****${value.slice(-4)}`
26
27
  : '****';
27
- return `[${sourceName}] ${key} = ${masked}`;
28
+ return `[${label}] ${key} = ${masked}`;
28
29
  },
29
30
  });
@@ -1,33 +1,34 @@
1
+ import { messages as t, sourceName, usage } from '../../../../src/i18n.js';
1
2
  import { defineCommand } from 'zhin.js/command';
2
3
  import { musicRuntimeToken } from '../../../../src/runtime.js';
3
- import { SOURCE_DISPLAY_NAME } from '../../../../src/config.js';
4
4
  import type { MusicSource } from '../../../../src/types.js';
5
5
 
6
6
  const VALID_SOURCES = new Set<string>(['qq', 'netease', 'kuwo', 'kugou']);
7
7
 
8
8
  export default defineCommand({
9
- description: '查看音乐平台凭证(脱敏)',
9
+ description: '查看音乐平台凭证(脱敏)', descriptionI18n: { en: "View music platform credentials with sensitive values masked" },
10
10
  alias: ['查看'],
11
- params: { source: { type: 'string', description: '音乐源(qq/netease/kuwo/kugou)' } },
11
+ params: { source: { type: 'string', description: '音乐源(qq/netease/kuwo/kugou)', descriptionI18n: { en: "Music source (qq / netease / kuwo / kugou)" } } },
12
12
  permit: ['role(master)'],
13
- async execute({ params, args, use }) {
13
+ async execute({ params, args, use, locale, formatCommand }) {
14
14
  const source = String(params.source ?? '').trim();
15
15
  const key = String(args[0] ?? '').trim();
16
16
 
17
17
  if (!VALID_SOURCES.has(source)) {
18
- return `不支持的音乐源:${source}`;
18
+ return t(locale, 'unsupported', { source });
19
19
  }
20
20
  if (!key) {
21
- return `格式:cookie get ${source} <key>`;
21
+ const command = formatCommand('cookie get');
22
+ return usage(locale, command === undefined ? undefined : `${command} ${source} <key>`);
22
23
  }
23
24
 
24
- const sourceName = SOURCE_DISPLAY_NAME[source as MusicSource];
25
+ const label = sourceName(source as MusicSource, locale);
25
26
  const value = await use(musicRuntimeToken).credentials.get(source as MusicSource, key);
26
- if (!value) return `[${sourceName}] 凭证 ${key} 未设置`;
27
+ if (!value) return t(locale, 'credentialUnset', { source: label, key });
27
28
 
28
29
  const masked = value.length > 10
29
30
  ? `${value.slice(0, 4)}****${value.slice(-4)}`
30
31
  : '****';
31
- return `[${sourceName}] ${key} = ${masked}`;
32
+ return `[${label}] ${key} = ${masked}`;
32
33
  },
33
34
  });
@@ -1,23 +1,24 @@
1
1
  // Generated by build-plugin-runtime-entries.mjs. Do not edit.
2
+ import { messages as t, sourceName, usage } from "../../../../lib/i18n.js";
2
3
  import { defineCommand } from 'zhin.js/command';
3
4
  import { musicRuntimeToken } from "../../../../lib/runtime.js";
4
- import { SOURCE_DISPLAY_NAME } from "../../../../lib/config.js";
5
5
  const VALID_SOURCES = new Set(['qq', 'netease', 'kuwo', 'kugou']);
6
6
  export default defineCommand({
7
- description: '保存音乐平台凭证',
7
+ description: '保存音乐平台凭证', descriptionI18n: { en: "Save music platform credentials" },
8
8
  alias: ['设置'],
9
- params: { source: { type: 'string', description: '音乐源(qq/netease/kuwo/kugou)' } },
9
+ params: { source: { type: 'string', description: '音乐源(qq/netease/kuwo/kugou)', descriptionI18n: { en: "Music source (qq / netease / kuwo / kugou)" } } },
10
10
  permit: ['role(master)'],
11
- async execute({ params, args, use }) {
11
+ async execute({ params, args, use, locale, formatCommand }) {
12
12
  const source = String(params.source ?? '').trim();
13
13
  const [key, ...valueParts] = args.map(String);
14
14
  if (!VALID_SOURCES.has(source)) {
15
- return `不支持的音乐源:${source}\n支持:${[...VALID_SOURCES].join(', ')}`;
15
+ return t(locale, 'unsupported', { source }) + '\n' + t(locale, 'sources', { sources: [...VALID_SOURCES].join(', ') });
16
16
  }
17
17
  if (!key || valueParts.length === 0) {
18
- return `格式:cookie set ${source} <key> <value>\n示例:cookie set netease cookie MUSIC_U=xxx`;
18
+ const command = formatCommand('cookie set');
19
+ return usage(locale, command === undefined ? undefined : `${command} ${source} <key> <value>`);
19
20
  }
20
21
  await use(musicRuntimeToken).credentials.set(source, key, valueParts.join(' '));
21
- return `[${SOURCE_DISPLAY_NAME[source]}] 凭证 ${key} 已保存`;
22
+ return t(locale, 'saved', { source: sourceName(source, locale), key });
22
23
  },
23
24
  });
@@ -1,27 +1,28 @@
1
+ import { messages as t, sourceName, usage } from '../../../../src/i18n.js';
1
2
  import { defineCommand } from 'zhin.js/command';
2
3
  import { musicRuntimeToken } from '../../../../src/runtime.js';
3
- import { SOURCE_DISPLAY_NAME } from '../../../../src/config.js';
4
4
  import type { MusicSource } from '../../../../src/types.js';
5
5
 
6
6
  const VALID_SOURCES = new Set<string>(['qq', 'netease', 'kuwo', 'kugou']);
7
7
 
8
8
  export default defineCommand({
9
- description: '保存音乐平台凭证',
9
+ description: '保存音乐平台凭证', descriptionI18n: { en: "Save music platform credentials" },
10
10
  alias: ['设置'],
11
- params: { source: { type: 'string', description: '音乐源(qq/netease/kuwo/kugou)' } },
11
+ params: { source: { type: 'string', description: '音乐源(qq/netease/kuwo/kugou)', descriptionI18n: { en: "Music source (qq / netease / kuwo / kugou)" } } },
12
12
  permit: ['role(master)'],
13
- async execute({ params, args, use }) {
13
+ async execute({ params, args, use, locale, formatCommand }) {
14
14
  const source = String(params.source ?? '').trim();
15
15
  const [key, ...valueParts] = args.map(String);
16
16
 
17
17
  if (!VALID_SOURCES.has(source)) {
18
- return `不支持的音乐源:${source}\n支持:${[...VALID_SOURCES].join(', ')}`;
18
+ return t(locale, 'unsupported', { source }) + '\n' + t(locale, 'sources', { sources: [...VALID_SOURCES].join(', ') });
19
19
  }
20
20
  if (!key || valueParts.length === 0) {
21
- return `格式:cookie set ${source} <key> <value>\n示例:cookie set netease cookie MUSIC_U=xxx`;
21
+ const command = formatCommand('cookie set');
22
+ return usage(locale, command === undefined ? undefined : `${command} ${source} <key> <value>`);
22
23
  }
23
24
 
24
25
  await use(musicRuntimeToken).credentials.set(source as MusicSource, key, valueParts.join(' '));
25
- return `[${SOURCE_DISPLAY_NAME[source as MusicSource]}] 凭证 ${key} 已保存`;
26
+ return t(locale, 'saved', { source: sourceName(source as MusicSource, locale), key });
26
27
  },
27
28
  });
@@ -1,23 +1,23 @@
1
1
  // Generated by build-plugin-runtime-entries.mjs. Do not edit.
2
2
  import { defineCommand } from 'zhin.js/command';
3
3
  import { musicRuntimeToken } from "../../../lib/runtime.js";
4
- import { SOURCE_DISPLAY_NAME } from "../../../lib/config.js";
4
+ import { messages as t, sourceName } from "../../../lib/i18n.js";
5
5
  export default defineCommand({
6
- description: '查看各平台凭证配置状态',
6
+ description: '查看各平台凭证配置状态', descriptionI18n: { en: "View configured music credentials by platform" },
7
7
  alias: ['状态'],
8
8
  permit: ['role(master)'],
9
- async execute({ use }) {
9
+ async execute({ use, locale }) {
10
10
  const allCreds = await use(musicRuntimeToken).credentials.list();
11
- const lines = ['--- 音乐凭证状态 ---'];
11
+ const lines = [t(locale, 'credentials')];
12
12
  for (const source of ['qq', 'netease', 'kuwo', 'kugou']) {
13
- const name = SOURCE_DISPLAY_NAME[source];
13
+ const name = sourceName(source, locale);
14
14
  const creds = allCreds.filter((c) => c.source === source);
15
15
  if (creds.length === 0) {
16
- lines.push(`${name}: 未配置`);
16
+ lines.push(`${name}: ${t(locale, 'unset')}`);
17
17
  }
18
18
  else {
19
19
  const keys = creds.map((c) => c.key).join(', ');
20
- lines.push(`${name}: ${keys} (${creds.length} 项)`);
20
+ lines.push(`${name}: ${t(locale, 'count', { keys, count: creds.length })}`);
21
21
  }
22
22
  }
23
23
  return lines.join('\n');
@@ -1,23 +1,23 @@
1
1
  import { defineCommand } from 'zhin.js/command';
2
2
  import { musicRuntimeToken } from '../../../src/runtime.js';
3
- import { SOURCE_DISPLAY_NAME } from '../../../src/config.js';
3
+ import { messages as t, sourceName } from '../../../src/i18n.js';
4
4
  import type { MusicSource } from '../../../src/types.js';
5
5
 
6
6
  export default defineCommand({
7
- description: '查看各平台凭证配置状态',
7
+ description: '查看各平台凭证配置状态', descriptionI18n: { en: "View configured music credentials by platform" },
8
8
  alias: ['状态'],
9
9
  permit: ['role(master)'],
10
- async execute({ use }) {
10
+ async execute({ use, locale }) {
11
11
  const allCreds = await use(musicRuntimeToken).credentials.list();
12
- const lines: string[] = ['--- 音乐凭证状态 ---'];
12
+ const lines: string[] = [t(locale, 'credentials')];
13
13
  for (const source of ['qq', 'netease', 'kuwo', 'kugou'] as MusicSource[]) {
14
- const name = SOURCE_DISPLAY_NAME[source];
14
+ const name = sourceName(source, locale);
15
15
  const creds = allCreds.filter((c) => c.source === source);
16
16
  if (creds.length === 0) {
17
- lines.push(`${name}: 未配置`);
17
+ lines.push(`${name}: ${t(locale, 'unset')}`);
18
18
  } else {
19
19
  const keys = creds.map((c) => c.key).join(', ');
20
- lines.push(`${name}: ${keys} (${creds.length} 项)`);
20
+ lines.push(`${name}: ${t(locale, 'count', { keys, count: creds.length })}`);
21
21
  }
22
22
  }
23
23
  return lines.join('\n');
@@ -2,58 +2,62 @@
2
2
  import { defineCommand } from 'zhin.js/command';
3
3
  import { loginSessionKey, } from "../../../lib/login/index.js";
4
4
  import { resolveMessageIds } from "../../../lib/session.js";
5
- import { SOURCE_DISPLAY_NAME } from "../../../lib/config.js";
5
+ import { messages as t, sourceName as displayName } from "../../../lib/i18n.js";
6
6
  import { musicRuntimeToken } from "../../../lib/runtime.js";
7
7
  const QR_LOGIN_SOURCES = {
8
8
  qq: 'qq',
9
9
  netease: 'netease',
10
10
  };
11
11
  export default defineCommand({
12
- description: '扫码登录音乐平台获取凭证(master 专用)',
12
+ description: '扫码登录音乐平台获取凭证(master 专用)', descriptionI18n: { en: "Sign in to a music platform with a QR code (bot master only)" },
13
13
  params: {
14
- platform: { type: 'string', description: '音乐平台(qq / netease / 取消)' },
14
+ platform: { type: 'string', description: '音乐平台(qq / netease / 取消)', descriptionI18n: { en: "Music platform (qq / netease) or cancel" } },
15
15
  },
16
16
  permit: ['role(master)'],
17
- async execute({ params, input, use }) {
17
+ async execute({ params, input, use, locale, formatCommand }) {
18
18
  const platform = String(params.platform ?? '').trim();
19
19
  const ids = resolveMessageIds(input);
20
20
  if (!ids)
21
- return '无法识别会话信息,请在群聊或私聊中使用';
22
- const key = loginSessionKey(ids.endpointId, ids.conversationId, ids.senderId);
21
+ return t(locale, 'conversation');
22
+ const key = loginSessionKey(ids.conversation, ids.senderId);
23
23
  const runtime = use(musicRuntimeToken);
24
- if (platform === '取消' || platform === 'cancel') {
25
- return runtime.logins.cancel(key) ? '已取消登录' : '没有进行中的登录会话';
24
+ const existing = runtime.logins.get(key);
25
+ const flowLocale = existing?.locale ?? locale;
26
+ const cancelInput = flowLocale === 'en' ? 'cancel' : '取消';
27
+ const command = formatCommand('login');
28
+ const cancelHint = command === undefined ? '' : t(flowLocale, 'cancelHint', { command: `${command} ${cancelInput}` });
29
+ if (platform === cancelInput) {
30
+ return t(flowLocale, runtime.logins.cancel(key) ? 'cancelLogin' : 'noLogin');
26
31
  }
27
32
  const source = QR_LOGIN_SOURCES[platform];
28
33
  if (!source) {
29
- return `不支持的平台:${platform || '(空)'}\n支持:qq, netease`;
34
+ return t(flowLocale, 'platform', { platform });
30
35
  }
31
- const existing = runtime.logins.get(key);
32
36
  if (existing) {
33
- return `正在进行 ${SOURCE_DISPLAY_NAME[existing.source]} 登录,请先完成或发送"音乐登录 取消"`;
37
+ return [t(flowLocale, 'active', { source: displayName(existing.source, flowLocale) }), cancelHint].filter(Boolean).join('\n');
34
38
  }
35
- const sourceName = SOURCE_DISPLAY_NAME[source];
39
+ const sourceName = displayName(source, locale);
36
40
  let qrResult;
37
41
  try {
38
- qrResult = await runtime.logins.start(source, key);
42
+ qrResult = await runtime.logins.start(source, key, locale);
39
43
  }
40
44
  catch (err) {
41
- return `[${sourceName}] 获取二维码失败:${err instanceof Error ? err.message : String(err)}`;
45
+ return t(locale, 'qrFailed', { source: sourceName, error: err instanceof Error ? err.message : String(err) });
42
46
  }
43
47
  const replyFn = input?.$reply;
44
48
  if (!replyFn)
45
- return '无法发送消息';
49
+ return t(locale, 'noReply');
46
50
  await replyFn([
47
- `[${sourceName}] 请使用 ${sourceName} App 扫描二维码登录:\n`,
51
+ t(locale, 'scan', { source: sourceName }) + '\n',
48
52
  qrResult.imageSegment,
49
- `\n二维码有效期 2 分钟,发送"音乐登录 取消"可中止`,
53
+ '\n' + [t(locale, 'valid'), cancelHint].filter(Boolean).join('\n'),
50
54
  ]);
51
55
  const finalResult = await runtime.logins.poll(key, async (result) => {
52
56
  if (result.status === 'scanned') {
53
57
  await replyFn(`[${sourceName}] ${result.message}`);
54
58
  }
55
59
  else if (result.status === 'confirmed') {
56
- await replyFn(`[${sourceName}] 登录成功!cookie 已自动保存,现在可以搜索和播放高品质音乐了`);
60
+ await replyFn(`[${sourceName}] ${t(locale, 'success')}`);
57
61
  }
58
62
  else if (result.status === 'expired' || result.status === 'error') {
59
63
  await replyFn(`[${sourceName}] ${result.message}`);
@@ -5,7 +5,7 @@ import {
5
5
  type QrLoginSource,
6
6
  } from '../../../src/login/index.js';
7
7
  import { resolveMessageIds } from '../../../src/session.js';
8
- import { SOURCE_DISPLAY_NAME } from '../../../src/config.js';
8
+ import { messages as t, sourceName as displayName } from '../../../src/i18n.js';
9
9
  import { musicRuntimeToken } from '../../../src/runtime.js';
10
10
 
11
11
  const QR_LOGIN_SOURCES: Record<string, QrLoginSource> = {
@@ -14,55 +14,59 @@ const QR_LOGIN_SOURCES: Record<string, QrLoginSource> = {
14
14
  };
15
15
 
16
16
  export default defineCommand<unknown, string, Message>({
17
- description: '扫码登录音乐平台获取凭证(master 专用)',
17
+ description: '扫码登录音乐平台获取凭证(master 专用)', descriptionI18n: { en: "Sign in to a music platform with a QR code (bot master only)" },
18
18
  params: {
19
- platform: { type: 'string', description: '音乐平台(qq / netease / 取消)' },
19
+ platform: { type: 'string', description: '音乐平台(qq / netease / 取消)', descriptionI18n: { en: "Music platform (qq / netease) or cancel" } },
20
20
  },
21
21
  permit: ['role(master)'],
22
- async execute({ params, input, use }) {
22
+ async execute({ params, input, use, locale, formatCommand }) {
23
23
  const platform = String(params.platform ?? '').trim();
24
24
 
25
- const ids = resolveMessageIds(input!);
26
- if (!ids) return '无法识别会话信息,请在群聊或私聊中使用';
27
- const key = loginSessionKey(ids.endpointId, ids.conversationId, ids.senderId);
25
+ const ids = resolveMessageIds(input);
26
+ if (!ids) return t(locale, 'conversation');
27
+ const key = loginSessionKey(ids.conversation, ids.senderId);
28
28
  const runtime = use(musicRuntimeToken);
29
29
 
30
- if (platform === '取消' || platform === 'cancel') {
31
- return runtime.logins.cancel(key) ? '已取消登录' : '没有进行中的登录会话';
30
+ const existing = runtime.logins.get(key);
31
+ const flowLocale = existing?.locale ?? locale;
32
+ const cancelInput = flowLocale === 'en' ? 'cancel' : '取消';
33
+ const command = formatCommand('login');
34
+ const cancelHint = command === undefined ? '' : t(flowLocale, 'cancelHint', { command: `${command} ${cancelInput}` });
35
+ if (platform === cancelInput) {
36
+ return t(flowLocale, runtime.logins.cancel(key) ? 'cancelLogin' : 'noLogin');
32
37
  }
33
38
 
34
39
  const source = QR_LOGIN_SOURCES[platform];
35
40
  if (!source) {
36
- return `不支持的平台:${platform || '(空)'}\n支持:qq, netease`;
41
+ return t(flowLocale, 'platform', { platform });
37
42
  }
38
43
 
39
- const existing = runtime.logins.get(key);
40
44
  if (existing) {
41
- return `正在进行 ${SOURCE_DISPLAY_NAME[existing.source]} 登录,请先完成或发送"音乐登录 取消"`;
45
+ return [t(flowLocale, 'active', { source: displayName(existing.source, flowLocale) }), cancelHint].filter(Boolean).join('\n');
42
46
  }
43
47
 
44
- const sourceName = SOURCE_DISPLAY_NAME[source];
48
+ const sourceName = displayName(source, locale);
45
49
  let qrResult;
46
50
  try {
47
- qrResult = await runtime.logins.start(source, key);
51
+ qrResult = await runtime.logins.start(source, key, locale);
48
52
  } catch (err) {
49
- return `[${sourceName}] 获取二维码失败:${err instanceof Error ? err.message : String(err)}`;
53
+ return t(locale, 'qrFailed', { source: sourceName, error: err instanceof Error ? err.message : String(err) });
50
54
  }
51
55
 
52
56
  const replyFn = input?.$reply;
53
- if (!replyFn) return '无法发送消息';
57
+ if (!replyFn) return t(locale, 'noReply');
54
58
 
55
59
  await replyFn([
56
- `[${sourceName}] 请使用 ${sourceName} App 扫描二维码登录:\n`,
60
+ t(locale, 'scan', { source: sourceName }) + '\n',
57
61
  qrResult.imageSegment,
58
- `\n二维码有效期 2 分钟,发送"音乐登录 取消"可中止`,
62
+ '\n' + [t(locale, 'valid'), cancelHint].filter(Boolean).join('\n'),
59
63
  ]);
60
64
 
61
65
  const finalResult = await runtime.logins.poll(key, async (result) => {
62
66
  if (result.status === 'scanned') {
63
67
  await replyFn(`[${sourceName}] ${result.message}`);
64
68
  } else if (result.status === 'confirmed') {
65
- await replyFn(`[${sourceName}] 登录成功!cookie 已自动保存,现在可以搜索和播放高品质音乐了`);
69
+ await replyFn(`[${sourceName}] ${t(locale, 'success')}`);
66
70
  } else if (result.status === 'expired' || result.status === 'error') {
67
71
  await replyFn(`[${sourceName}] ${result.message}`);
68
72
  }
@@ -1,11 +1,14 @@
1
1
  // Generated by build-plugin-runtime-entries.mjs. Do not edit.
2
+ import { messages as t, sourceName, usage } from "../../../lib/i18n.js";
2
3
  import { defineCommand } from 'zhin.js/command';
3
4
  import { searchMusic, formatSearchResults } from "../../../lib/music-lib.js";
4
- import { resolveSourceAlias, SOURCE_DISPLAY_NAME } from "../../../lib/config.js";
5
+ import { resolveSourceAlias } from "../../../lib/config.js";
5
6
  import { sessionKey, resolveMessageIds } from "../../../lib/session.js";
6
7
  import { musicRuntimeToken } from "../../../lib/runtime.js";
7
8
  export default defineCommand({
8
- description: '搜索音乐并点歌(支持QQ/网易云/酷我/酷狗)',
9
+ alias: ['music search'],
10
+ preferredAliasI18n: { en: 'music search' },
11
+ description: '搜索音乐并点歌(支持QQ/网易云/酷我/酷狗)', descriptionI18n: { en: "Search for music and play a track (QQ, NetEase, Kuwo and Kugou)" },
9
12
  params: { keyword: { type: 'string' } },
10
13
  shortcut: {
11
14
  'QQ点歌': { keyword: '' },
@@ -14,11 +17,11 @@ export default defineCommand({
14
17
  '酷我点歌': { keyword: '' },
15
18
  '酷狗点歌': { keyword: '' },
16
19
  },
17
- async execute({ params, config, input, use }) {
20
+ async execute({ params, config, input, use, locale, formatCommand }) {
18
21
  const rawKeyword = String(params.keyword ?? '').trim();
19
22
  if (!rawKeyword) {
20
- const sources = Object.values(SOURCE_DISPLAY_NAME).join('/');
21
- return `请输入歌曲名称,如:点歌 稻香\n支持音乐源:${sources}`;
23
+ const sources = ['qq', 'netease', 'kuwo', 'kugou'].map(source => sourceName(source, locale)).join('/');
24
+ return [t(locale, 'keyword'), usage(locale, formatCommand('点歌') === undefined ? undefined : `${formatCommand('点歌')} <song>`), t(locale, 'sources', { sources })].filter(Boolean).join('\n');
22
25
  }
23
26
  let source = config?.defaultSource ?? 'qq';
24
27
  let keyword = rawKeyword;
@@ -41,23 +44,24 @@ export default defineCommand({
41
44
  source = sourceFromKeyword;
42
45
  keyword = keyword.slice(keyword.indexOf(' ') + 1).trim();
43
46
  if (!keyword)
44
- return '请输入歌曲名称';
47
+ return t(locale, 'keyword');
45
48
  }
46
49
  const pageSize = config?.pageSize ?? 5;
47
50
  const result = await searchMusic(use(musicRuntimeToken), keyword, source, pageSize);
48
51
  if (result.total === 0) {
49
- return `[${SOURCE_DISPLAY_NAME[source]}] 未找到"${keyword}"相关歌曲`;
52
+ return t(locale, 'noMatch', { source: sourceName(source, locale), keyword });
50
53
  }
51
54
  const ids = input ? resolveMessageIds(input) : null;
52
55
  if (ids) {
53
- const key = sessionKey(ids.endpointId, ids.conversationId, ids.senderId);
56
+ const key = sessionKey(ids.conversation, ids.senderId);
54
57
  use(musicRuntimeToken).sessions.set(key, {
58
+ locale,
55
59
  results: result.results,
56
60
  source: result.source,
57
61
  keyword,
58
62
  timestamp: Date.now(),
59
63
  });
60
64
  }
61
- return formatSearchResults(result.results, result.source);
65
+ return formatSearchResults(result.results, result.source, locale);
62
66
  },
63
67
  });
@@ -1,7 +1,8 @@
1
+ import { messages as t, sourceName, usage } from '../../../src/i18n.js';
1
2
  import { defineCommand } from 'zhin.js/command';
2
3
  import type { Message } from '@zhin.js/core/runtime';
3
4
  import { searchMusic, formatSearchResults } from '../../../src/music-lib.js';
4
- import { resolveSourceAlias, SOURCE_DISPLAY_NAME } from '../../../src/config.js';
5
+ import { resolveSourceAlias } from '../../../src/config.js';
5
6
  import { sessionKey, resolveMessageIds } from '../../../src/session.js';
6
7
  import type { MusicSource } from '../../../src/types.js';
7
8
  import { musicRuntimeToken } from '../../../src/runtime.js';
@@ -12,7 +13,9 @@ interface MusicConfig {
12
13
  }
13
14
 
14
15
  export default defineCommand<MusicConfig, string, Message>({
15
- description: '搜索音乐并点歌(支持QQ/网易云/酷我/酷狗)',
16
+ alias: ['music search'],
17
+ preferredAliasI18n: { en: 'music search' },
18
+ description: '搜索音乐并点歌(支持QQ/网易云/酷我/酷狗)', descriptionI18n: { en: "Search for music and play a track (QQ, NetEase, Kuwo and Kugou)" },
16
19
  params: { keyword: { type: 'string' } },
17
20
  shortcut: {
18
21
  'QQ点歌': { keyword: '' },
@@ -21,11 +24,11 @@ export default defineCommand<MusicConfig, string, Message>({
21
24
  '酷我点歌': { keyword: '' },
22
25
  '酷狗点歌': { keyword: '' },
23
26
  },
24
- async execute({ params, config, input, use }) {
27
+ async execute({ params, config, input, use, locale, formatCommand }) {
25
28
  const rawKeyword = String(params.keyword ?? '').trim();
26
29
  if (!rawKeyword) {
27
- const sources = Object.values(SOURCE_DISPLAY_NAME).join('/');
28
- return `请输入歌曲名称,如:点歌 稻香\n支持音乐源:${sources}`;
30
+ const sources = (['qq', 'netease', 'kuwo', 'kugou'] as const).map(source => sourceName(source, locale)).join('/');
31
+ return [t(locale, 'keyword'), usage(locale, formatCommand('点歌') === undefined ? undefined : `${formatCommand('点歌')} <song>`), t(locale, 'sources', { sources })].filter(Boolean).join('\n');
29
32
  }
30
33
 
31
34
  let source: MusicSource = (config as MusicConfig | undefined)?.defaultSource ?? 'qq';
@@ -50,19 +53,20 @@ export default defineCommand<MusicConfig, string, Message>({
50
53
  if (sourceFromKeyword) {
51
54
  source = sourceFromKeyword;
52
55
  keyword = keyword.slice(keyword.indexOf(' ') + 1).trim();
53
- if (!keyword) return '请输入歌曲名称';
56
+ if (!keyword) return t(locale, 'keyword');
54
57
  }
55
58
 
56
59
  const pageSize = (config as MusicConfig | undefined)?.pageSize ?? 5;
57
60
  const result = await searchMusic(use(musicRuntimeToken), keyword, source, pageSize);
58
61
  if (result.total === 0) {
59
- return `[${SOURCE_DISPLAY_NAME[source]}] 未找到"${keyword}"相关歌曲`;
62
+ return t(locale, 'noMatch', { source: sourceName(source, locale), keyword });
60
63
  }
61
64
 
62
65
  const ids = input ? resolveMessageIds(input) : null;
63
66
  if (ids) {
64
- const key = sessionKey(ids.endpointId, ids.conversationId, ids.senderId);
67
+ const key = sessionKey(ids.conversation, ids.senderId);
65
68
  use(musicRuntimeToken).sessions.set(key, {
69
+ locale,
66
70
  results: result.results,
67
71
  source: result.source,
68
72
  keyword,
@@ -70,6 +74,6 @@ export default defineCommand<MusicConfig, string, Message>({
70
74
  });
71
75
  }
72
76
 
73
- return formatSearchResults(result.results, result.source);
77
+ return formatSearchResults(result.results, result.source, locale);
74
78
  },
75
79
  });
package/lib/i18n.d.ts ADDED
@@ -0,0 +1,6 @@
1
+ import { type Locale } from 'zhin.js/i18n';
2
+ import type { MusicSource } from './types.js';
3
+ export declare const messages: (locale: Locale | undefined, key: "loginAborted" | "keyword" | "usage" | "sources" | "noResults" | "noMatch" | "results" | "unsupported" | "cancelSelection" | "detailFailed" | "credentials" | "unset" | "count" | "credentialUnset" | "saved" | "deleted" | "conversation" | "cancelLogin" | "noLogin" | "platform" | "active" | "cancelHint" | "qrFailed" | "noReply" | "scan" | "valid" | "success" | "waiting" | "scanned" | "expired" | "confirmed" | "unknown" | "pollFailed" | "pollError" | "timeout" | "neteaseQr" | "emptyResponse" | "cookieFailed", values?: import("zhin.js").MessageValues) => string;
4
+ export declare function sourceName(source: MusicSource, locale?: Locale): string;
5
+ export declare function usage(locale: Locale, command: string | undefined): string;
6
+ //# sourceMappingURL=i18n.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"i18n.d.ts","sourceRoot":"","sources":["../src/i18n.ts"],"names":[],"mappings":"AAAA,OAAO,EAAkB,KAAK,MAAM,EAAE,MAAM,cAAc,CAAC;AAC3D,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAE9C,eAAO,MAAM,QAAQ,slBAuCnB,CAAC;AAEH,wBAAgB,UAAU,CAAC,MAAM,EAAE,WAAW,EAAE,MAAM,GAAE,MAAgB,GAAG,MAAM,CAEhF;AACD,wBAAgB,KAAK,CAAC,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,SAAS,GAAG,MAAM,CAEzE"}