@zhin.js/adapter-kook 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.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,39 @@
1
1
  # @zhin.js/adapter-kook
2
2
 
3
+ ## 1.1.16
4
+
5
+ ### Patch Changes
6
+
7
+ - 53bc6a0: 适配器配置标签、端点命令和工具反馈支持中英文;复用当前操作语言,保持协议值、用户内容、身份、权限与发送链路不变。Sandbox 展示使用项目语言,QQ 绑定与交互状态保留完整会话身份。
8
+ - 9cade30: 修复国际化调用链遗漏:Agent 审批、管理命令和错误反馈采用可信语言及实际命令前缀;游戏快捷帮助、工具反馈、平台默认卡片和后台通知贯通语言。协议即时回复复用受运行代约束的个人语言查询,业务验证支持不改变审批身份的双语展示元数据。
9
+
10
+ AI 正文、进度与耗尽摘要统一优先遵从用户明确语言要求,再跟随实际输入;个人/项目语言仅作兜底,固定界面文案继续使用配置语言,不增加翻译调用。
11
+
12
+ GitHub 工作区为 Git clone/pull/fetch/ls-remote 显式分隔选项与位置参数,保留仓库和分支校验;顺序写入 Bot 名称和邮箱,避免竞争同一配置文件锁。
13
+
14
+ - Updated dependencies [334e92d]
15
+ - Updated dependencies [f3b7c9f]
16
+ - Updated dependencies [658a5fb]
17
+ - Updated dependencies [144a17b]
18
+ - Updated dependencies [18296cf]
19
+ - Updated dependencies [08e64de]
20
+ - Updated dependencies [9cade30]
21
+ - Updated dependencies [53bc6a0]
22
+ - Updated dependencies [7595136]
23
+ - Updated dependencies [66b6404]
24
+ - Updated dependencies [adb4af2]
25
+ - Updated dependencies [69f9ef6]
26
+ - Updated dependencies [17d1301]
27
+ - @zhin.js/adapter@1.1.23
28
+ - @zhin.js/host-http@1.1.10
29
+ - @zhin.js/core@1.1.51
30
+ - @zhin.js/permission@1.1.9
31
+ - @zhin.js/tool@1.1.10
32
+ - @zhin.js/skill@1.1.10
33
+ - @zhin.js/im-contract@1.1.7
34
+ - @zhin.js/agent-feature@1.1.10
35
+ - @zhin.js/feature-kit@1.1.9
36
+
3
37
  ## 1.1.15
4
38
 
5
39
  ### Patch Changes
@@ -0,0 +1,48 @@
1
+ // Generated by build-plugin-runtime-entries.mjs. Do not edit.
2
+ import { createMessages } from 'zhin.js/i18n';
3
+ export const messages = createMessages({
4
+ "unknownAction": {
5
+ "zh-CN": "未知操作: {p0}",
6
+ "en": "Unknown action: {p0}"
7
+ },
8
+ "operationFailed": {
9
+ "zh-CN": "操作失败",
10
+ "en": "Operation failed"
11
+ },
12
+ "unblacklisted": {
13
+ "zh-CN": "已将 {p0} 从黑名单移除",
14
+ "en": "Removed {p0} from the blacklist"
15
+ },
16
+ "blacklisted": {
17
+ "zh-CN": "已将 {p0} 加入黑名单",
18
+ "en": "Added {p0} to the blacklist"
19
+ },
20
+ "roleCreated": {
21
+ "zh-CN": "已创建角色 \"{p0}\"",
22
+ "en": "Role \"{p0}\" created"
23
+ },
24
+ "roleDeleteFailed": {
25
+ "zh-CN": "删除角色失败",
26
+ "en": "Could not delete the role"
27
+ },
28
+ "roleDeleted": {
29
+ "zh-CN": "已删除角色 {p0}",
30
+ "en": "Role {p0} deleted"
31
+ },
32
+ "roleGrantFailed": {
33
+ "zh-CN": "授予角色失败",
34
+ "en": "Could not grant the role"
35
+ },
36
+ "roleGranted": {
37
+ "zh-CN": "已授予用户 {p0} 角色 {p1}",
38
+ "en": "Granted role {p1} to user {p0}"
39
+ },
40
+ "roleRevokeFailed": {
41
+ "zh-CN": "撤销角色失败",
42
+ "en": "Could not revoke the role"
43
+ },
44
+ "roleRevoked": {
45
+ "zh-CN": "已撤销用户 {p0} 的角色 {p1}",
46
+ "en": "Revoked role {p1} from user {p0}"
47
+ }
48
+ });
@@ -0,0 +1,48 @@
1
+ import { createMessages } from 'zhin.js/i18n';
2
+
3
+ export const messages = createMessages({
4
+ "unknownAction": {
5
+ "zh-CN": "未知操作: {p0}",
6
+ "en": "Unknown action: {p0}"
7
+ },
8
+ "operationFailed": {
9
+ "zh-CN": "操作失败",
10
+ "en": "Operation failed"
11
+ },
12
+ "unblacklisted": {
13
+ "zh-CN": "已将 {p0} 从黑名单移除",
14
+ "en": "Removed {p0} from the blacklist"
15
+ },
16
+ "blacklisted": {
17
+ "zh-CN": "已将 {p0} 加入黑名单",
18
+ "en": "Added {p0} to the blacklist"
19
+ },
20
+ "roleCreated": {
21
+ "zh-CN": "已创建角色 \"{p0}\"",
22
+ "en": "Role \"{p0}\" created"
23
+ },
24
+ "roleDeleteFailed": {
25
+ "zh-CN": "删除角色失败",
26
+ "en": "Could not delete the role"
27
+ },
28
+ "roleDeleted": {
29
+ "zh-CN": "已删除角色 {p0}",
30
+ "en": "Role {p0} deleted"
31
+ },
32
+ "roleGrantFailed": {
33
+ "zh-CN": "授予角色失败",
34
+ "en": "Could not grant the role"
35
+ },
36
+ "roleGranted": {
37
+ "zh-CN": "已授予用户 {p0} 角色 {p1}",
38
+ "en": "Granted role {p1} to user {p0}"
39
+ },
40
+ "roleRevokeFailed": {
41
+ "zh-CN": "撤销角色失败",
42
+ "en": "Could not revoke the role"
43
+ },
44
+ "roleRevoked": {
45
+ "zh-CN": "已撤销用户 {p0} 的角色 {p1}",
46
+ "en": "Revoked role {p1} from user {p0}"
47
+ }
48
+ });
@@ -1,4 +1,5 @@
1
1
  // Generated by build-plugin-runtime-entries.mjs. Do not edit.
2
+ import { messages as t } from "../../../../messages.js";
2
3
  import { defineAgentTool } from '@zhin.js/tool';
3
4
  import { z } from 'zod';
4
5
  import { platformPermission } from '@zhin.js/permission';
@@ -19,14 +20,14 @@ export default defineAgentTool({
19
20
  switch (action) {
20
21
  case 'add': {
21
22
  const success = await member.addToBlackList(remark);
22
- return { success, message: success ? `已将 ${user_id} 加入黑名单` : '操作失败' };
23
+ return { success, message: success ? t(context.locale, 'blacklisted', { p0: user_id }) : t(context.locale, 'operationFailed') };
23
24
  }
24
25
  case 'remove': {
25
26
  const success = await member.removeFromBlackList();
26
- return { success, message: success ? `已将 ${user_id} 从黑名单移除` : '操作失败' };
27
+ return { success, message: success ? t(context.locale, 'unblacklisted', { p0: user_id }) : t(context.locale, 'operationFailed') };
27
28
  }
28
29
  default:
29
- return { success: false, message: `未知操作: ${action}` };
30
+ return { success: false, message: t(context.locale, 'unknownAction', { p0: action }) };
30
31
  }
31
32
  },
32
33
  });
@@ -1,3 +1,4 @@
1
+ import { messages as t } from "../../../../messages.js";
1
2
  import { defineAgentTool } from '@zhin.js/tool';
2
3
  import { z } from 'zod';
3
4
  import { platformPermission } from '@zhin.js/permission';
@@ -19,14 +20,14 @@ export default defineAgentTool<{ guild_id: string; action: 'add' | 'remove'; use
19
20
  switch (action) {
20
21
  case 'add': {
21
22
  const success = await member.addToBlackList(remark);
22
- return { success, message: success ? `已将 ${user_id} 加入黑名单` : '操作失败' };
23
+ return { success, message: success ? t(context.locale, 'blacklisted', { p0: user_id }) : t(context.locale, 'operationFailed') };
23
24
  }
24
25
  case 'remove': {
25
26
  const success = await member.removeFromBlackList();
26
- return { success, message: success ? `已将 ${user_id} 从黑名单移除` : '操作失败' };
27
+ return { success, message: success ? t(context.locale, 'unblacklisted', { p0: user_id }) : t(context.locale, 'operationFailed') };
27
28
  }
28
29
  default:
29
- return { success: false, message: `未知操作: ${action}` };
30
+ return { success: false, message: t(context.locale, 'unknownAction', { p0: action }) };
30
31
  }
31
32
  },
32
33
  });
@@ -1,4 +1,5 @@
1
1
  // Generated by build-plugin-runtime-entries.mjs. Do not edit.
2
+ import { messages as t } from "../../../../messages.js";
2
3
  import { defineAgentTool } from '@zhin.js/tool';
3
4
  import { z } from 'zod';
4
5
  import { platformPermission } from '@zhin.js/permission';
@@ -17,7 +18,7 @@ export default defineAgentTool({
17
18
  const role = await client.pickGuild(guild_id).createRole(name);
18
19
  return {
19
20
  success: true,
20
- message: `已创建角色 "${name}"`,
21
+ message: t(context.locale, 'roleCreated', { p0: name }),
21
22
  role: { id: role.role_id, name: role.name },
22
23
  };
23
24
  },
@@ -1,3 +1,4 @@
1
+ import { messages as t } from "../../../../messages.js";
1
2
  import { defineAgentTool } from '@zhin.js/tool';
2
3
  import { z } from 'zod';
3
4
  import { platformPermission } from '@zhin.js/permission';
@@ -17,7 +18,7 @@ export default defineAgentTool<{ guild_id: string; name: string }>({
17
18
  const role = await client.pickGuild(guild_id).createRole(name);
18
19
  return {
19
20
  success: true,
20
- message: `已创建角色 "${name}"`,
21
+ message: t(context.locale, 'roleCreated', { p0: name }),
21
22
  role: { id: role.role_id, name: role.name },
22
23
  };
23
24
  },
@@ -1,4 +1,5 @@
1
1
  // Generated by build-plugin-runtime-entries.mjs. Do not edit.
2
+ import { messages as t } from "../../../../messages.js";
2
3
  import { defineAgentTool } from '@zhin.js/tool';
3
4
  import { z } from 'zod';
4
5
  import { platformPermission } from '@zhin.js/permission';
@@ -15,6 +16,6 @@ export default defineAgentTool({
15
16
  async execute({ guild_id, role_id }, context) {
16
17
  const client = context.$client;
17
18
  const success = await client.pickGuild(guild_id).deleteRole(role_id);
18
- return { success, message: success ? `已删除角色 ${role_id}` : '删除角色失败' };
19
+ return { success, message: success ? t(context.locale, 'roleDeleted', { p0: role_id }) : t(context.locale, 'roleDeleteFailed') };
19
20
  },
20
21
  });
@@ -1,3 +1,4 @@
1
+ import { messages as t } from "../../../../messages.js";
1
2
  import { defineAgentTool } from '@zhin.js/tool';
2
3
  import { z } from 'zod';
3
4
  import { platformPermission } from '@zhin.js/permission';
@@ -15,6 +16,6 @@ export default defineAgentTool<{ guild_id: string; role_id: string }>({
15
16
  async execute({ guild_id, role_id }: { guild_id: string; role_id: string }, context) {
16
17
  const client = context.$client;
17
18
  const success = await client.pickGuild(guild_id).deleteRole(role_id);
18
- return { success, message: success ? `已删除角色 ${role_id}` : '删除角色失败' };
19
+ return { success, message: success ? t(context.locale, 'roleDeleted', { p0: role_id }) : t(context.locale, 'roleDeleteFailed') };
19
20
  },
20
21
  });
@@ -1,4 +1,5 @@
1
1
  // Generated by build-plugin-runtime-entries.mjs. Do not edit.
2
+ import { messages as t } from "../../../../messages.js";
2
3
  import { defineAgentTool } from '@zhin.js/tool';
3
4
  import { z } from 'zod';
4
5
  import { platformPermission } from '@zhin.js/permission';
@@ -16,6 +17,6 @@ export default defineAgentTool({
16
17
  async execute({ guild_id, user_id, role_id }, context) {
17
18
  const client = context.$client;
18
19
  const success = await client.pickGuildMember(guild_id, user_id).grant(role_id);
19
- return { success, message: success ? `已授予用户 ${user_id} 角色 ${role_id}` : '授予角色失败' };
20
+ return { success, message: success ? t(context.locale, 'roleGranted', { p0: user_id, p1: role_id }) : t(context.locale, 'roleGrantFailed') };
20
21
  },
21
22
  });
@@ -1,3 +1,4 @@
1
+ import { messages as t } from "../../../../messages.js";
1
2
  import { defineAgentTool } from '@zhin.js/tool';
2
3
  import { z } from 'zod';
3
4
  import { platformPermission } from '@zhin.js/permission';
@@ -16,6 +17,6 @@ export default defineAgentTool<{ guild_id: string; user_id: string; role_id: str
16
17
  async execute({ guild_id, user_id, role_id }: { guild_id: string; user_id: string; role_id: string }, context) {
17
18
  const client = context.$client;
18
19
  const success = await client.pickGuildMember(guild_id, user_id).grant(role_id);
19
- return { success, message: success ? `已授予用户 ${user_id} 角色 ${role_id}` : '授予角色失败' };
20
+ return { success, message: success ? t(context.locale, 'roleGranted', { p0: user_id, p1: role_id }) : t(context.locale, 'roleGrantFailed') };
20
21
  },
21
22
  });
@@ -1,4 +1,5 @@
1
1
  // Generated by build-plugin-runtime-entries.mjs. Do not edit.
2
+ import { messages as t } from "../../../../messages.js";
2
3
  import { defineAgentTool } from '@zhin.js/tool';
3
4
  import { z } from 'zod';
4
5
  import { platformPermission } from '@zhin.js/permission';
@@ -16,6 +17,6 @@ export default defineAgentTool({
16
17
  async execute({ guild_id, user_id, role_id }, context) {
17
18
  const client = context.$client;
18
19
  const success = await client.pickGuildMember(guild_id, user_id).revoke(role_id);
19
- return { success, message: success ? `已撤销用户 ${user_id} 的角色 ${role_id}` : '撤销角色失败' };
20
+ return { success, message: success ? t(context.locale, 'roleRevoked', { p0: user_id, p1: role_id }) : t(context.locale, 'roleRevokeFailed') };
20
21
  },
21
22
  });
@@ -1,3 +1,4 @@
1
+ import { messages as t } from "../../../../messages.js";
1
2
  import { defineAgentTool } from '@zhin.js/tool';
2
3
  import { z } from 'zod';
3
4
  import { platformPermission } from '@zhin.js/permission';
@@ -16,6 +17,6 @@ export default defineAgentTool<{ guild_id: string; user_id: string; role_id: str
16
17
  async execute({ guild_id, user_id, role_id }: { guild_id: string; user_id: string; role_id: string }, context) {
17
18
  const client = context.$client;
18
19
  const success = await client.pickGuildMember(guild_id, user_id).revoke(role_id);
19
- return { success, message: success ? `已撤销用户 ${user_id} 的角色 ${role_id}` : '撤销角色失败' };
20
+ return { success, message: success ? t(context.locale, 'roleRevoked', { p0: user_id, p1: role_id }) : t(context.locale, 'roleRevokeFailed') };
20
21
  },
21
22
  });
@@ -11,8 +11,8 @@ export const kookEndpointCommands = createEndpointCommands({
11
11
  adapterDisplayName: 'KOOK',
12
12
  fields: [
13
13
  { key: 'token', required: true, env: true, description: 'KOOK bot token' },
14
- { key: 'encrypt_key', env: true, description: 'webhook 加密密钥(connection: webhook)' },
15
- { key: 'verify_token', env: true, description: 'webhook 验证 token(connection: webhook)' },
14
+ { key: 'encrypt_key', env: true, description: 'webhook 加密密钥(connection: webhook)', descriptionI18n: { en: "Webhook encryption secret (connection: webhook)" } },
15
+ { key: 'verify_token', env: true, description: 'webhook 验证 token(connection: webhook)', descriptionI18n: { en: "Webhook verification token (connection: webhook)" } },
16
16
  ],
17
17
  running: (use) => use(kookRuntimeStateToken).endpoints.values(),
18
18
  describeEntry: (entry) => `token: ${String(entry.token)}`,
@@ -11,8 +11,8 @@ export const kookEndpointCommands = createEndpointCommands({
11
11
  adapterDisplayName: 'KOOK',
12
12
  fields: [
13
13
  { key: 'token', required: true, env: true, description: 'KOOK bot token' },
14
- { key: 'encrypt_key', env: true, description: 'webhook 加密密钥(connection: webhook)' },
15
- { key: 'verify_token', env: true, description: 'webhook 验证 token(connection: webhook)' },
14
+ { key: 'encrypt_key', env: true, description: 'webhook 加密密钥(connection: webhook)', descriptionI18n: { en: "Webhook encryption secret (connection: webhook)" } },
15
+ { key: 'verify_token', env: true, description: 'webhook 验证 token(connection: webhook)', descriptionI18n: { en: "Webhook verification token (connection: webhook)" } },
16
16
  ],
17
17
  running: (use) => use(kookRuntimeStateToken).endpoints.values(),
18
18
  describeEntry: (entry) => `token: ${String(entry.token)}`,
package/lib/endpoint.d.ts CHANGED
@@ -20,7 +20,7 @@ export declare class KookWebsocketEndpoint extends Endpoint<KookClientTransport>
20
20
  open(): void;
21
21
  close(): void;
22
22
  stop(): Promise<void>;
23
- send({ conversation, payload }: EndpointSendRequest): Promise<string>;
23
+ send(request: EndpointSendRequest): Promise<string>;
24
24
  /** Test / internal: admit a normalized message when open. */
25
25
  admit(msg: KookInboundMessage): void;
26
26
  recallMessage(messageId: string, conversation?: ConversationRef): Promise<void>;
@@ -46,7 +46,7 @@ export declare class KookWebhookEndpoint extends Endpoint<KookClientTransport> {
46
46
  open(): void;
47
47
  close(): void;
48
48
  stop(): Promise<void>;
49
- send({ conversation, payload }: EndpointSendRequest): Promise<string>;
49
+ send(request: EndpointSendRequest): Promise<string>;
50
50
  admitEvent(event: KookWebhookEventData): Promise<void>;
51
51
  /** Test / internal: admit a message when open. */
52
52
  admit(msg: KookInboundMessage): void;
package/lib/endpoint.js CHANGED
@@ -100,8 +100,9 @@ export class KookWebsocketEndpoint extends Endpoint {
100
100
  this.#started = false;
101
101
  this.#logger.debug(formatCompact({ op: 'disconnect' }));
102
102
  }
103
- async send({ conversation, payload }) {
104
- const messageId = await sendKookOutbound(this.#requireClient(), { conversation, payload });
103
+ async send(request) {
104
+ const { conversation } = request;
105
+ const messageId = await sendKookOutbound(this.#requireClient(), request);
105
106
  rememberKookConversation(this.#sentConversations, messageId, conversation);
106
107
  this.#logger.debug(formatCompact({
107
108
  op: 'kook_send',
@@ -255,8 +256,9 @@ export class KookWebhookEndpoint extends Endpoint {
255
256
  this.#started = false;
256
257
  this.#logger.debug(formatCompact({ op: 'disconnect' }));
257
258
  }
258
- async send({ conversation, payload }) {
259
- const messageId = await sendKookOutbound(this.#requireClient(), { conversation, payload });
259
+ async send(request) {
260
+ const { conversation } = request;
261
+ const messageId = await sendKookOutbound(this.#requireClient(), request);
260
262
  rememberKookConversation(this.#sentConversations, messageId, conversation);
261
263
  this.#logger.debug(formatCompact({
262
264
  op: 'kook_send',
package/lib/i18n.d.ts ADDED
@@ -0,0 +1 @@
1
+ export declare const outboundMessages: (locale: import("zhin.js").Locale | undefined, key: "openLink" | "image" | "video" | "audio" | "file" | "unnamed", values?: import("zhin.js").MessageValues) => string;
package/lib/i18n.js ADDED
@@ -0,0 +1,9 @@
1
+ import { createMessages } from 'zhin.js/i18n';
2
+ export const outboundMessages = createMessages({
3
+ openLink: { 'zh-CN': '打开链接', en: 'Open link' },
4
+ image: { 'zh-CN': '图片', en: 'Image' },
5
+ video: { 'zh-CN': '视频', en: 'Video' },
6
+ audio: { 'zh-CN': '音频', en: 'Audio' },
7
+ file: { 'zh-CN': '文件: {name}', en: 'File: {name}' },
8
+ unnamed: { 'zh-CN': '未命名', en: 'Unnamed' },
9
+ });
package/lib/outbound.d.ts CHANGED
@@ -2,6 +2,6 @@ import { type MessageRef } from '@zhin.js/im-contract';
2
2
  import type { EndpointSendRequest } from 'zhin.js/adapter';
3
3
  import type { KookClientTransport } from './ws.js';
4
4
  /** Native cards preserve mixed text/images; SDK's ordinary segments stop at first media. */
5
- export declare function sendKookOutbound(client: KookClientTransport, { conversation, payload }: EndpointSendRequest): Promise<string>;
5
+ export declare function sendKookOutbound(client: KookClientTransport, { conversation, payload, locale }: EndpointSendRequest): Promise<string>;
6
6
  /** Deletion requires the canonical conversation: KOOK has separate channel/private APIs. */
7
7
  export declare function recallKookMessage(client: KookClientTransport, message: MessageRef): Promise<void>;
package/lib/outbound.js CHANGED
@@ -1,8 +1,9 @@
1
1
  import { readFile } from 'node:fs/promises';
2
2
  import { EndpointDeliveryError, isMediaRef } from '@zhin.js/im-contract';
3
3
  import { formatOutboundKmarkdown } from './protocol.js';
4
+ import { outboundMessages as t } from './i18n.js';
4
5
  /** Native cards preserve mixed text/images; SDK's ordinary segments stop at first media. */
5
- export async function sendKookOutbound(client, { conversation, payload }) {
6
+ export async function sendKookOutbound(client, { conversation, payload, locale }) {
6
7
  try {
7
8
  const items = Array.isArray(payload) ? payload : payload && typeof payload === 'object' ? [payload] : [payload];
8
9
  let quote;
@@ -52,7 +53,7 @@ export async function sendKookOutbound(client, { conversation, payload }) {
52
53
  for (const value of [data.description, data.content])
53
54
  if (typeof value === 'string' && value)
54
55
  modules.push({ type: 'section', mode: 'left', text: { type: 'plain-text', content: value } });
55
- modules.push({ type: 'action-group', elements: [{ type: 'button', theme: 'secondary', click: 'link', value: url.href, text: { type: 'plain-text', content: '打开链接' } }] });
56
+ modules.push({ type: 'action-group', elements: [{ type: 'button', theme: 'secondary', click: 'link', value: url.href, text: { type: 'plain-text', content: t(locale, 'openLink') } }] });
56
57
  }
57
58
  else if (item && typeof item === 'object' && item.type === 'image') {
58
59
  hasImage = true;
@@ -87,12 +88,12 @@ export async function sendKookOutbound(client, { conversation, payload }) {
87
88
  && isMediaRef(item.data?.media) && !['url', 'file'].includes(item.data.media.kind)) {
88
89
  throw new EndpointDeliveryError('unsupported_media', 'KOOK binary uploads currently support images only', 'rejected');
89
90
  }
90
- const text = formatOutboundKmarkdown([item]);
91
+ const text = formatOutboundKmarkdown([item], locale);
91
92
  if (text)
92
93
  modules.push({ type: 'section', mode: 'left', text: { type: 'kmarkdown', content: text } });
93
94
  }
94
95
  }
95
- const body = hasImage || hasCard ? { __isCard: true, type: 'card', theme: 'secondary', size: 'lg', modules } : formatOutboundKmarkdown(payload);
96
+ const body = hasImage || hasCard ? { __isCard: true, type: 'card', theme: 'secondary', size: 'lg', modules } : formatOutboundKmarkdown(payload, locale);
96
97
  const result = conversation.kind === 'private'
97
98
  ? await client.sendPrivateMsg(conversation.id, body, quote)
98
99
  : await client.sendChannelMsg(conversation.id, body, quote);
package/lib/protocol.d.ts CHANGED
@@ -1,6 +1,7 @@
1
1
  /** KOOK protocol helpers. Canonicalization is owned by Core. */
2
2
  import type { IncomingMessage } from 'node:http';
3
3
  import { type ConversationRef } from '@zhin.js/im-contract';
4
+ import type { Locale } from 'zhin.js/i18n';
4
5
  export type LogLevel = 'trace' | 'debug' | 'info' | 'warn' | 'error' | 'fatal' | 'mark' | 'off';
5
6
  export declare enum KookPermission {
6
7
  Normal = 1,
@@ -126,7 +127,7 @@ export declare function isKookBotMentioned(msg: KookInboundMessage, selfId?: str
126
127
  * Wire-encode an already-rendered outbound payload into KOOK KMarkdown.
127
128
  * Segment canonicalization is intentionally not done here.
128
129
  */
129
- export declare function formatOutboundKmarkdown(payload: unknown): string;
130
+ export declare function formatOutboundKmarkdown(payload: unknown, locale?: Locale): string;
130
131
  export declare function normalizeWebhookPath(path: string): string;
131
132
  export declare function readTextBody(request: IncomingMessage, options?: {
132
133
  readonly limit?: number;
package/lib/protocol.js CHANGED
@@ -3,6 +3,7 @@ import { createDecipheriv, timingSafeEqual } from 'node:crypto';
3
3
  import { inflateSync } from 'node:zlib';
4
4
  import { EndpointDeliveryError, isMediaRef } from '@zhin.js/im-contract';
5
5
  import { formatCompact, getLogger } from '@zhin.js/logger';
6
+ import { outboundMessages as t } from './i18n.js';
6
7
  const logger = getLogger('kook');
7
8
  export var KookPermission;
8
9
  (function (KookPermission) {
@@ -118,7 +119,7 @@ function resolveOutboundMediaUrl(seg) {
118
119
  * Wire-encode an already-rendered outbound payload into KOOK KMarkdown.
119
120
  * Segment canonicalization is intentionally not done here.
120
121
  */
121
- export function formatOutboundKmarkdown(payload) {
122
+ export function formatOutboundKmarkdown(payload, locale) {
122
123
  if (typeof payload === 'string')
123
124
  return payload;
124
125
  const segments = Array.isArray(payload)
@@ -152,7 +153,7 @@ export function formatOutboundKmarkdown(payload) {
152
153
  }
153
154
  case 'image': {
154
155
  const url = resolveOutboundMediaUrl(item);
155
- return url ? `![${String(data.alt || '图片')}](${url})` : '';
156
+ return url ? `![${String(data.alt || t(locale, 'image'))}](${url})` : '';
156
157
  }
157
158
  case 'face':
158
159
  return `(emj)${String(data.name || 'emoji')}(emj)[${String(data.id ?? '')}]`;
@@ -160,15 +161,15 @@ export function formatOutboundKmarkdown(payload) {
160
161
  return `[${String(data.text || data.url || '')}](${String(data.url || '')})`;
161
162
  case 'video': {
162
163
  const url = resolveOutboundMediaUrl(item);
163
- return url ? `[视频](${url})` : '';
164
+ return url ? `[${t(locale, 'video')}](${url})` : '';
164
165
  }
165
166
  case 'audio': {
166
167
  const url = resolveOutboundMediaUrl(item);
167
- return url ? `[音频](${url})` : '';
168
+ return url ? `[${t(locale, 'audio')}](${url})` : '';
168
169
  }
169
170
  case 'file': {
170
171
  const url = resolveOutboundMediaUrl(item);
171
- return url ? `[文件: ${String(data.name || '未命名')}](${url})` : '';
172
+ return url ? `[${t(locale, 'file', { name: String(data.name || t(locale, 'unnamed')) })}](${url})` : '';
172
173
  }
173
174
  case 'bold':
174
175
  return `**${String(data.text ?? '')}**`;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@zhin.js/adapter-kook",
3
- "version": "1.1.15",
3
+ "version": "1.1.16",
4
4
  "description": "Zhin.js KOOK adapter for Plugin Runtime (WebSocket gateway or Webhook)",
5
5
  "type": "module",
6
6
  "main": "./lib/index.js",
@@ -33,23 +33,23 @@
33
33
  },
34
34
  "dependencies": {
35
35
  "kook-client": "^1.0.5",
36
- "@zhin.js/adapter": "1.1.22",
37
- "@zhin.js/agent-feature": "1.1.9",
38
- "@zhin.js/core": "1.1.50",
39
- "@zhin.js/feature-kit": "1.1.8",
40
- "@zhin.js/host-http": "1.1.9",
41
- "@zhin.js/im-contract": "1.1.6",
36
+ "@zhin.js/adapter": "1.1.23",
37
+ "@zhin.js/agent-feature": "1.1.10",
38
+ "@zhin.js/core": "1.1.51",
39
+ "@zhin.js/feature-kit": "1.1.9",
40
+ "@zhin.js/host-http": "1.1.10",
41
+ "@zhin.js/im-contract": "1.1.7",
42
42
  "@zhin.js/logger": "1.1.2",
43
- "@zhin.js/permission": "1.1.8",
44
- "@zhin.js/skill": "1.1.9",
45
- "@zhin.js/tool": "1.1.9"
43
+ "@zhin.js/permission": "1.1.9",
44
+ "@zhin.js/skill": "1.1.10",
45
+ "@zhin.js/tool": "1.1.10"
46
46
  },
47
47
  "peerDependencies": {
48
48
  "zod": "^4.0.0",
49
- "@zhin.js/adapter": "^1.1.22",
50
- "@zhin.js/command": "^1.1.10",
51
- "@zhin.js/core": "^1.1.50",
52
- "zhin.js": "^1.1.15"
49
+ "@zhin.js/adapter": "^1.1.23",
50
+ "@zhin.js/command": "^1.1.11",
51
+ "@zhin.js/core": "^1.1.51",
52
+ "zhin.js": "^1.1.16"
53
53
  },
54
54
  "peerDependenciesMeta": {
55
55
  "@zhin.js/command": {
@@ -67,8 +67,8 @@
67
67
  "typescript": "^6.0.3",
68
68
  "vitest": "^4.1.10",
69
69
  "zod": "^4.4.3",
70
- "@zhin.js/host-http": "1.1.9",
71
- "zhin.js": "1.1.15"
70
+ "@zhin.js/host-http": "1.1.10",
71
+ "zhin.js": "1.1.16"
72
72
  },
73
73
  "files": [
74
74
  "adapters",
package/schema.json CHANGED
@@ -55,7 +55,12 @@
55
55
  "string",
56
56
  "number"
57
57
  ],
58
- "description": "框架 master(KOOK user id;AI/工具权限、endpoint 管理)。endpoints[i].master 可逐项覆盖"
58
+ "description": "框架 master(KOOK user id;AI/工具权限、endpoint 管理)。endpoints[i].master 可逐项覆盖",
59
+ "x-i18n": {
60
+ "en": {
61
+ "description": "Framework masters (KOOK user id; AI/tool permissions and endpoint management). endpoints[i].master can override this value."
62
+ }
63
+ }
59
64
  },
60
65
  "trusted": {
61
66
  "type": "array",
@@ -66,7 +71,12 @@
66
71
  ],
67
72
  "description": "Trusted KOOK user id"
68
73
  },
69
- "description": "框架 trusted 用户列表(弱于 master)。endpoints[i].trusted 可逐项追加"
74
+ "description": "框架 trusted 用户列表(弱于 master)。endpoints[i].trusted 可逐项追加",
75
+ "x-i18n": {
76
+ "en": {
77
+ "description": "Framework trusted users, below master authority. endpoints[i].trusted can add users."
78
+ }
79
+ }
70
80
  },
71
81
  "endpoints": {
72
82
  "type": "array",
@@ -81,7 +91,12 @@
81
91
  "string",
82
92
  "number"
83
93
  ],
84
- "description": "本 endpoint 的框架 master(KOOK user id);覆盖顶层 master"
94
+ "description": "本 endpoint 的框架 master(KOOK user id);覆盖顶层 master",
95
+ "x-i18n": {
96
+ "en": {
97
+ "description": "Framework masters for this endpoint (KOOK user id), overriding the top-level master list."
98
+ }
99
+ }
85
100
  },
86
101
  "trusted": {
87
102
  "type": "array",
@@ -92,7 +107,12 @@
92
107
  ],
93
108
  "description": "Trusted KOOK user id"
94
109
  },
95
- "description": "本 endpoint 的 trusted 列表"
110
+ "description": "本 endpoint 的 trusted 列表",
111
+ "x-i18n": {
112
+ "en": {
113
+ "description": "Trusted users for this endpoint."
114
+ }
115
+ }
96
116
  },
97
117
  "token": {
98
118
  "type": "string",
@@ -171,7 +191,12 @@
171
191
  "port",
172
192
  "serverName"
173
193
  ],
174
- "description": "WSS故障验收固定loopback,保持真实TLS身份"
194
+ "description": "WSS故障验收固定loopback,保持真实TLS身份",
195
+ "x-i18n": {
196
+ "en": {
197
+ "description": "Pin WSS fault-injection tests to loopback while preserving the actual TLS identity."
198
+ }
199
+ }
175
200
  },
176
201
  "apiProxy": {
177
202
  "type": "object",
@@ -193,12 +218,22 @@
193
218
  "id",
194
219
  "token"
195
220
  ]
221
+ },
222
+ "x-i18n": {
223
+ "en": {
224
+ "description": "Multiple accounts: each entry defines an endpoint. id and token are required; other fields override instance defaults."
225
+ }
196
226
  }
197
227
  },
198
228
  "commandPrefix": {
199
229
  "type": "string",
200
230
  "default": "",
201
- "description": "命令前缀(默认 '' 无前缀,任意文本按命令匹配;如 '/' 要求 / 开头)。endpoints[i] 可逐项覆盖"
231
+ "description": "命令前缀(默认 '' 无前缀,任意文本按命令匹配;如 '/' 要求 / 开头)。endpoints[i] 可逐项覆盖",
232
+ "x-i18n": {
233
+ "en": {
234
+ "description": "Command prefix. The default empty string allows unprefixed commands; '/' requires a leading slash. Each endpoints[i] can override it."
235
+ }
236
+ }
202
237
  }
203
238
  },
204
239
  "required": [
package/src/endpoint.ts CHANGED
@@ -151,8 +151,9 @@ export class KookWebsocketEndpoint extends Endpoint<KookClientTransport> {
151
151
  this.#logger.debug(formatCompact({ op: 'disconnect' }));
152
152
  }
153
153
 
154
- async send({ conversation, payload }: EndpointSendRequest): Promise<string> {
155
- const messageId = await sendKookOutbound(this.#requireClient(), { conversation, payload });
154
+ async send(request: EndpointSendRequest): Promise<string> {
155
+ const { conversation } = request;
156
+ const messageId = await sendKookOutbound(this.#requireClient(), request);
156
157
  rememberKookConversation(this.#sentConversations, messageId, conversation);
157
158
  this.#logger.debug(formatCompact({
158
159
  op: 'kook_send',
@@ -321,8 +322,9 @@ export class KookWebhookEndpoint extends Endpoint<KookClientTransport> {
321
322
  this.#logger.debug(formatCompact({ op: 'disconnect' }));
322
323
  }
323
324
 
324
- async send({ conversation, payload }: EndpointSendRequest): Promise<string> {
325
- const messageId = await sendKookOutbound(this.#requireClient(), { conversation, payload });
325
+ async send(request: EndpointSendRequest): Promise<string> {
326
+ const { conversation } = request;
327
+ const messageId = await sendKookOutbound(this.#requireClient(), request);
326
328
  rememberKookConversation(this.#sentConversations, messageId, conversation);
327
329
  this.#logger.debug(formatCompact({
328
330
  op: 'kook_send',
package/src/i18n.ts ADDED
@@ -0,0 +1,10 @@
1
+ import { createMessages } from 'zhin.js/i18n';
2
+
3
+ export const outboundMessages = createMessages({
4
+ openLink: { 'zh-CN': '打开链接', en: 'Open link' },
5
+ image: { 'zh-CN': '图片', en: 'Image' },
6
+ video: { 'zh-CN': '视频', en: 'Video' },
7
+ audio: { 'zh-CN': '音频', en: 'Audio' },
8
+ file: { 'zh-CN': '文件: {name}', en: 'File: {name}' },
9
+ unnamed: { 'zh-CN': '未命名', en: 'Unnamed' },
10
+ });
package/src/outbound.ts CHANGED
@@ -3,9 +3,10 @@ import { EndpointDeliveryError, isMediaRef, type MessageRef } from '@zhin.js/im-
3
3
  import type { EndpointSendRequest } from 'zhin.js/adapter';
4
4
  import { formatOutboundKmarkdown } from './protocol.js';
5
5
  import type { KookClientTransport } from './ws.js';
6
+ import { outboundMessages as t } from './i18n.js';
6
7
 
7
8
  /** Native cards preserve mixed text/images; SDK's ordinary segments stop at first media. */
8
- export async function sendKookOutbound(client: KookClientTransport, { conversation, payload }: EndpointSendRequest): Promise<string> {
9
+ export async function sendKookOutbound(client: KookClientTransport, { conversation, payload, locale }: EndpointSendRequest): Promise<string> {
9
10
  try {
10
11
  const items = Array.isArray(payload) ? payload : payload && typeof payload === 'object' ? [payload] : [payload];
11
12
  let quote: { message_id: string } | undefined;
@@ -40,7 +41,7 @@ export async function sendKookOutbound(client: KookClientTransport, { conversati
40
41
  if (['image', 'audio', 'artist', 'duration', 'platform', 'config'].some(key => data[key] !== undefined)) throw new EndpointDeliveryError('unsupported_operation', 'KOOK share media/app metadata is not implemented', 'not_sent');
41
42
  modules.push({ type: 'section', mode: 'left', text: { type: 'plain-text', content: data.title } });
42
43
  for (const value of [data.description, data.content]) if (typeof value === 'string' && value) modules.push({ type: 'section', mode: 'left', text: { type: 'plain-text', content: value } });
43
- modules.push({ type: 'action-group', elements: [{ type: 'button', theme: 'secondary', click: 'link', value: url.href, text: { type: 'plain-text', content: '打开链接' } }] });
44
+ modules.push({ type: 'action-group', elements: [{ type: 'button', theme: 'secondary', click: 'link', value: url.href, text: { type: 'plain-text', content: t(locale, 'openLink') } }] });
44
45
  } else if (item && typeof item === 'object' && item.type === 'image') {
45
46
  hasImage = true;
46
47
  const media = item.data?.media;
@@ -66,11 +67,11 @@ export async function sendKookOutbound(client: KookClientTransport, { conversati
66
67
  && isMediaRef(item.data?.media) && !['url', 'file'].includes(item.data.media.kind)) {
67
68
  throw new EndpointDeliveryError('unsupported_media', 'KOOK binary uploads currently support images only', 'rejected');
68
69
  }
69
- const text = formatOutboundKmarkdown([item]);
70
+ const text = formatOutboundKmarkdown([item], locale);
70
71
  if (text) modules.push({ type: 'section', mode: 'left', text: { type: 'kmarkdown', content: text } });
71
72
  }
72
73
  }
73
- const body = hasImage || hasCard ? { __isCard: true, type: 'card', theme: 'secondary', size: 'lg', modules } : formatOutboundKmarkdown(payload);
74
+ const body = hasImage || hasCard ? { __isCard: true, type: 'card', theme: 'secondary', size: 'lg', modules } : formatOutboundKmarkdown(payload, locale);
74
75
  const result = conversation.kind === 'private'
75
76
  ? await client.sendPrivateMsg(conversation.id, body, quote)
76
77
  : await client.sendChannelMsg(conversation.id, body, quote);
package/src/protocol.ts CHANGED
@@ -5,6 +5,8 @@ import { inflateSync } from 'node:zlib';
5
5
  import type { IncomingMessage } from 'node:http';
6
6
  import { EndpointDeliveryError, isMediaRef, type ConversationRef } from '@zhin.js/im-contract';
7
7
  import { formatCompact, getLogger } from '@zhin.js/logger';
8
+ import type { Locale } from 'zhin.js/i18n';
9
+ import { outboundMessages as t } from './i18n.js';
8
10
 
9
11
  const logger = getLogger('kook');
10
12
 
@@ -248,7 +250,7 @@ function resolveOutboundMediaUrl(seg: KookWireSegment): string | undefined {
248
250
  * Wire-encode an already-rendered outbound payload into KOOK KMarkdown.
249
251
  * Segment canonicalization is intentionally not done here.
250
252
  */
251
- export function formatOutboundKmarkdown(payload: unknown): string {
253
+ export function formatOutboundKmarkdown(payload: unknown, locale?: Locale): string {
252
254
  if (typeof payload === 'string') return payload;
253
255
 
254
256
  const segments: Array<string | KookWireSegment> = Array.isArray(payload)
@@ -283,7 +285,7 @@ export function formatOutboundKmarkdown(payload: unknown): string {
283
285
  }
284
286
  case 'image': {
285
287
  const url = resolveOutboundMediaUrl(item);
286
- return url ? `![${String(data.alt || '图片')}](${url})` : '';
288
+ return url ? `![${String(data.alt || t(locale, 'image'))}](${url})` : '';
287
289
  }
288
290
  case 'face':
289
291
  return `(emj)${String(data.name || 'emoji')}(emj)[${String(data.id ?? '')}]`;
@@ -291,15 +293,15 @@ export function formatOutboundKmarkdown(payload: unknown): string {
291
293
  return `[${String(data.text || data.url || '')}](${String(data.url || '')})`;
292
294
  case 'video': {
293
295
  const url = resolveOutboundMediaUrl(item);
294
- return url ? `[视频](${url})` : '';
296
+ return url ? `[${t(locale, 'video')}](${url})` : '';
295
297
  }
296
298
  case 'audio': {
297
299
  const url = resolveOutboundMediaUrl(item);
298
- return url ? `[音频](${url})` : '';
300
+ return url ? `[${t(locale, 'audio')}](${url})` : '';
299
301
  }
300
302
  case 'file': {
301
303
  const url = resolveOutboundMediaUrl(item);
302
- return url ? `[文件: ${String(data.name || '未命名')}](${url})` : '';
304
+ return url ? `[${t(locale, 'file', { name: String(data.name || t(locale, 'unnamed')) })}](${url})` : '';
303
305
  }
304
306
  case 'bold':
305
307
  return `**${String(data.text ?? '')}**`;