@zhin.js/adapter-kook 5.0.3 → 6.0.1

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,53 @@
1
1
  # @zhin.js/adapter-kook
2
2
 
3
+ ## 6.0.1
4
+
5
+ ### Patch Changes
6
+
7
+ - 74b035c: endpoint 管理命令扩展至 18/20 适配器:kook / discord / github(private_key 支持内联文件路径)/ icqq(bindFlow 登记式 add + `icqq login` 引导)/ dingtalk / lark / line / satori / wechat-mp / wecom / weixin-ilink 接入 `endpoint list/add/remove`(字段对齐各自 schema,凭据写 `.env`)。email(smtp/imap 嵌套对象)与 sandbox(无凭据)暂不接。
8
+ - 09d4f25: Console 社交读取面(management 语义端口)多平台落地:napcat/onebot11/onebot12/milky(好友+群+群成员,OneBot 标准动作);discord/kook/satori(guild+频道+成员,分页聚合,id 保精度留字符串);slack(workspace 成员+public channels+conversations.members);line(群/room 成员分页+profile 回退);wechat-mp(followers openid);weixin-ilink(context_token 对端推导);lark(chats+members 全分页)。`EndpointFriend.user_id`/`EndpointGroup.group_id` 放宽为 `number | string`(雪花 id 不丢精度)。telegram/wecom/dingtalk/github/email/sandbox 注明平台无列表面暂不接。
9
+ - Updated dependencies [cdf64e7]
10
+ - Updated dependencies [2d0a159]
11
+ - Updated dependencies [5691aba]
12
+ - Updated dependencies [078e3f7]
13
+ - Updated dependencies [50497a5]
14
+ - Updated dependencies [9c997b2]
15
+ - Updated dependencies [09d4f25]
16
+ - Updated dependencies [43485a9]
17
+ - Updated dependencies [f0ec5ab]
18
+ - Updated dependencies [3e925d0]
19
+ - Updated dependencies [fa66c4c]
20
+ - Updated dependencies [fa66c4c]
21
+ - Updated dependencies [6cb6152]
22
+ - @zhin.js/command@1.0.3
23
+ - @zhin.js/agent@1.0.6
24
+ - @zhin.js/plugin-runtime@1.1.1
25
+ - @zhin.js/host-http@1.0.3
26
+ - zhin.js@5.0.1
27
+ - @zhin.js/adapter@1.1.1
28
+ - @zhin.js/core@1.4.1
29
+
30
+ ## 6.0.0
31
+
32
+ ### Patch Changes
33
+
34
+ - 7db69c1: 命令前缀改为适配器配置项:`MessageDispatcher` 不再硬编码 `/`,默认按消息所属适配器实例 config 的 `commandPrefix` 解析(默认 `''` 无前缀,任意文本按命令匹配),`endpoints[i].commandPrefix` 逐项覆盖;`ImRuntime({ commandPrefix })` 仍可设全局静态前缀。全部 20 个平台适配器 schema 新增 `commandPrefix` 属性。
35
+
36
+ BREAKING(行为变化):未配置时命令不再需要 `/` 前缀——原 `/zt` 写法不再命中,直接发 `zt` 即可;需要斜杠风格的适配器请在配置里显式设 `commandPrefix: '/'`。
37
+
38
+ - 713445c: 适配器配置格式定稿(不兼容旧格式):`plugins.<adapter>` 顶层仅共享字段 + `commandPrefix`,`endpoints[i]` 携带 endpoint 级字段(`name` + 凭据,各 schema 已类型化),`endpoints` 为必填(icqq 另需顶层 `master`);icqq 新增 `trusted` 列表(顶层/逐项均可)。scaffold-wizard 全部字段式与自定义 configure() 产出改为新格式,examples(full-bot / qq-games-bot)与 20 个适配器 README 同步迁移。
39
+ - Updated dependencies [7db69c1]
40
+ - Updated dependencies [e5c84ed]
41
+ - Updated dependencies [3ea84a0]
42
+ - Updated dependencies [1ddcd70]
43
+ - Updated dependencies [ac9da66]
44
+ - @zhin.js/core@1.4.0
45
+ - @zhin.js/adapter@1.1.0
46
+ - @zhin.js/plugin-runtime@1.1.0
47
+ - @zhin.js/agent@1.0.5
48
+ - @zhin.js/host-http@1.0.2
49
+ - zhin.js@5.0.0
50
+
3
51
  ## 5.0.3
4
52
 
5
53
  ### Patch Changes
package/README.md CHANGED
@@ -38,7 +38,7 @@ pnpm add @zhin.js/adapter-kook
38
38
  | **Webhook** | 需公网 HTTPS + Host `httpHostToken`;与 WebSocket 互斥 |
39
39
  | **host-http** | 仅 Webhook 模式需要 |
40
40
 
41
- 必填字段:`token`。
41
+ 必填字段(`endpoints[i]`):`name`、`token`。
42
42
 
43
43
  ## 最小配置
44
44
 
@@ -46,9 +46,10 @@ pnpm add @zhin.js/adapter-kook
46
46
  # zhin.config.yml(Plugin Runtime)
47
47
  plugins:
48
48
  kook:
49
- name: my-kook-bot
50
- token: ${KOOK_TOKEN}
51
49
  # connection: websocket # 默认
50
+ endpoints:
51
+ - name: my-kook-bot
52
+ token: ${KOOK_TOKEN}
52
53
  ```
53
54
 
54
55
  根插件 `zhin.plugins`(或项目图)需引用 `@zhin.js/adapter-kook`(`instanceKey: kook`)。
@@ -70,12 +71,13 @@ plugins:
70
71
  ```yaml
71
72
  plugins:
72
73
  kook:
73
- name: my-kook-bot
74
- token: ${KOOK_TOKEN}
75
74
  connection: webhook
76
- verify_token: ${KOOK_VERIFY_TOKEN}
77
75
  webhookPath: /kook/webhook
78
- # encrypt_key: ${KOOK_ENCRYPT_KEY} # 启用消息加密时必填
76
+ endpoints:
77
+ - name: my-kook-bot
78
+ token: ${KOOK_TOKEN}
79
+ verify_token: ${KOOK_VERIFY_TOKEN}
80
+ # encrypt_key: ${KOOK_ENCRYPT_KEY} # 启用消息加密时必填
79
81
  ```
80
82
 
81
83
  Host 需注入 `httpHostToken`。Challenge(`type: 255`)会校验 `verify_token` 并回显 `challenge`;普通事件经 `gateway.receive` 入站,出站仍走 KOOK HTTP API。
package/adapters/kook.ts CHANGED
@@ -13,6 +13,7 @@ import {
13
13
  resolveKookConfig,
14
14
  type KookAdapterConfig,
15
15
  } from '../src/protocol.js';
16
+ import { kookRuntimeStateToken } from '../src/kook-runtime-state.js';
16
17
 
17
18
  export {
18
19
  KookWebhookEndpoint,
@@ -26,9 +27,19 @@ export type { CreateKookClient, KookClientTransport } from '../src/ws.js';
26
27
 
27
28
  export default defineAdapter<KookAdapterConfig>({
28
29
  capabilities: ['inbound', 'outbound'],
30
+ // KOOK 图片消息消费远程 URL;无按钮交互面,交互段降级纯文本。
31
+ segments: {
32
+ outboundMedia: ['url'],
33
+ interactive: 'text',
34
+ },
29
35
  create(context) {
30
36
  const config = resolveKookConfig(context.config);
31
37
  const gateway = context.use(messageGatewayToken);
38
+ // 注册到插件运行时状态(kook endpoint list 的"运行中"数据源)
39
+ context.use(kookRuntimeStateToken).endpoints.set(config.name, {
40
+ name: config.name,
41
+ mode: config.connection,
42
+ });
32
43
  if (config.connection === 'webhook') {
33
44
  return new KookWebhookEndpoint({
34
45
  id: context.id,
@@ -0,0 +1,3 @@
1
+ import { kookEndpointCommands } from '../../../src/kook-endpoint-commands.js';
2
+
3
+ export default kookEndpointCommands.add;
@@ -0,0 +1,3 @@
1
+ import { kookEndpointCommands } from '../../src/kook-endpoint-commands.js';
2
+
3
+ export default kookEndpointCommands.list;
@@ -0,0 +1,3 @@
1
+ import { kookEndpointCommands } from '../../../src/kook-endpoint-commands.js';
2
+
3
+ export default kookEndpointCommands.remove;
package/lib/endpoint.d.ts CHANGED
@@ -1,9 +1,9 @@
1
- import type { EndpointInstance } from '@zhin.js/adapter';
1
+ import type { EndpointInstance, EndpointManagement } from '@zhin.js/adapter';
2
2
  import type { MessageGateway } from '@zhin.js/core/runtime';
3
3
  import type { HttpHost } from '@zhin.js/host-http';
4
4
  import type { CapabilityId } from '@zhin.js/plugin-runtime';
5
5
  import { type KookInboundMessage, type ResolvedKookWebhookConfig, type ResolvedKookWebsocketConfig } from './protocol.js';
6
- import { type CreateKookClient } from './ws.js';
6
+ import { type CreateKookClient, type KookClientTransport } from './ws.js';
7
7
  export interface KookEndpointOptions {
8
8
  readonly id: CapabilityId;
9
9
  readonly gateway: MessageGateway;
@@ -12,6 +12,7 @@ export interface KookEndpointOptions {
12
12
  }
13
13
  export declare class KookWebsocketEndpoint implements EndpointInstance {
14
14
  #private;
15
+ readonly management: EndpointManagement;
15
16
  constructor(options: KookEndpointOptions);
16
17
  start(): Promise<void>;
17
18
  open(): void;
@@ -49,6 +50,7 @@ export interface KookWebhookEndpointOptions {
49
50
  }
50
51
  export declare class KookWebhookEndpoint implements EndpointInstance {
51
52
  #private;
53
+ readonly management: EndpointManagement;
52
54
  constructor(options: KookWebhookEndpointOptions);
53
55
  /** Used by webhook handler. */
54
56
  get isOpen(): boolean;
@@ -82,3 +84,9 @@ export declare class KookWebhookEndpoint implements EndpointInstance {
82
84
  addToBlacklist(guildId: string, userId: string, remark?: string): Promise<boolean>;
83
85
  removeFromBlacklist(guildId: string, userId: string): Promise<boolean>;
84
86
  }
87
+ /**
88
+ * KOOK endpoint 的 EndpointManagement 语义端口(websocket / webhook 共用)。
89
+ * 数据走 kook-client 的 HTTP API 封装:/api/v3/guild/list、
90
+ * /api/v3/channel/list、/api/v3/guild/user-list(SDK 内部已做分页聚合)。
91
+ */
92
+ export declare function createKookEndpointManagement(requireClient: () => KookClientTransport): EndpointManagement;
package/lib/endpoint.js CHANGED
@@ -11,6 +11,7 @@ export class KookWebsocketEndpoint {
11
11
  #open = false;
12
12
  #started = false;
13
13
  #unregisterAgent;
14
+ management = createKookEndpointManagement(() => this.#requireClient());
14
15
  constructor(options) {
15
16
  this.#options = options;
16
17
  this.#createClient = options.createClient ?? defaultCreateClient;
@@ -150,6 +151,7 @@ export class KookWebhookEndpoint {
150
151
  #open = false;
151
152
  #started = false;
152
153
  #unregisterAgent;
154
+ management = createKookEndpointManagement(() => this.#requireClient());
153
155
  constructor(options) {
154
156
  this.#options = options;
155
157
  this.#createClient = options.createClient ?? defaultCreateWebhookClient;
@@ -297,3 +299,66 @@ export class KookWebhookEndpoint {
297
299
  return this.#client;
298
300
  }
299
301
  }
302
+ /**
303
+ * KOOK guild/channel/user id 是字符串形式的雪花号,超出
304
+ * Number.MAX_SAFE_INTEGER,Number() 会丢精度。Console 社交面只把 group_id
305
+ * 当 JSON 值透传、并以字符串回传给 listGroupMembers,因此保留原始字符串
306
+ * (仅按契约类型声明强转)是全链路最不丢信息的方案。
307
+ */
308
+ function toGroupId(id) {
309
+ return id;
310
+ }
311
+ /** KOOK 文字频道标记:HTTP API type=1(2=语音),kook-client 消息侧用 'GROUP'。 */
312
+ function isKookTextChannelType(type) {
313
+ return type === 1 || type === '1' || type === 'GROUP';
314
+ }
315
+ /**
316
+ * KOOK endpoint 的 EndpointManagement 语义端口(websocket / webhook 共用)。
317
+ * 数据走 kook-client 的 HTTP API 封装:/api/v3/guild/list、
318
+ * /api/v3/channel/list、/api/v3/guild/user-list(SDK 内部已做分页聚合)。
319
+ */
320
+ export function createKookEndpointManagement(requireClient) {
321
+ return Object.freeze({
322
+ async listGroups() {
323
+ const guilds = await requireClient().getGuildList();
324
+ const groups = [];
325
+ for (const guild of guilds) {
326
+ if (guild?.id == null)
327
+ continue;
328
+ groups.push({
329
+ group_id: toGroupId(String(guild.id)),
330
+ name: String(guild.name ?? guild.id),
331
+ });
332
+ }
333
+ return groups;
334
+ },
335
+ async listChannels() {
336
+ const client = requireClient();
337
+ const channels = [];
338
+ for (const guild of await client.getGuildList()) {
339
+ if (guild?.id == null)
340
+ continue;
341
+ const guildId = String(guild.id);
342
+ const guildName = String(guild.name ?? guildId);
343
+ for (const channel of await client.getChannelList(guildId)) {
344
+ if (channel?.id == null)
345
+ continue;
346
+ if (channel.is_category)
347
+ continue;
348
+ if (!isKookTextChannelType(channel.type))
349
+ continue;
350
+ channels.push({
351
+ id: String(channel.id),
352
+ name: channel.name ? String(channel.name) : undefined,
353
+ parent: { type: 'guild', id: guildId, name: guildName },
354
+ });
355
+ }
356
+ }
357
+ return channels;
358
+ },
359
+ async listGroupMembers(groupId) {
360
+ // 平台形状(User.Info[])原样返回
361
+ return requireClient().getGuildUserList(groupId);
362
+ },
363
+ });
364
+ }
@@ -0,0 +1 @@
1
+ export declare const kookEndpointCommands: import("@zhin.js/adapter").EndpointCommands<Readonly<import("@zhin.js/command").CommandDefinition<unknown, unknown, import("@zhin.js/command").CommandMessage>>>;
@@ -0,0 +1,18 @@
1
+ /**
2
+ * `kook endpoint` 命令族:由 @zhin.js/adapter 的 createEndpointCommands 套件生成。
3
+ * commands/endpoint/ 下的 list / add / remove 直接默认导出这三项。
4
+ */
5
+ import { createEndpointCommands } from '@zhin.js/adapter';
6
+ import { defineCommand } from '@zhin.js/command';
7
+ import { kookRuntimeStateToken } from './kook-runtime-state.js';
8
+ export const kookEndpointCommands = createEndpointCommands({
9
+ adapterKey: 'kook',
10
+ adapterDisplayName: 'KOOK',
11
+ fields: [
12
+ { key: 'token', required: true, env: true, description: 'KOOK bot token' },
13
+ { key: 'encrypt_key', env: true, description: 'webhook 加密密钥(connection: webhook)' },
14
+ { key: 'verify_token', env: true, description: 'webhook 验证 token(connection: webhook)' },
15
+ ],
16
+ running: (use) => use(kookRuntimeStateToken).endpoints.values(),
17
+ describeEntry: (entry) => `token: ${String(entry.token)}`,
18
+ }, defineCommand);
@@ -0,0 +1 @@
1
+ export declare const kookRuntimeStateToken: import("@zhin.js/plugin-runtime").Token<import("@zhin.js/adapter").EndpointRuntimeState>;
@@ -0,0 +1,6 @@
1
+ /**
2
+ * KOOK 插件实例的运行时状态:adapter create() 注册的 endpoint 列表。
3
+ * 由 plugin.ts setup() provide,adapter create 与 `kook endpoint` 命令共享(同一 owner generation)。
4
+ */
5
+ import { defineEndpointRuntimeStateToken } from '@zhin.js/adapter';
6
+ export const kookRuntimeStateToken = defineEndpointRuntimeStateToken('kook');
package/lib/ws.d.ts CHANGED
@@ -33,7 +33,20 @@ export interface KookClientTransport {
33
33
  revoke(roleId: string): Promise<boolean>;
34
34
  setNickname(nickname: string): Promise<boolean>;
35
35
  };
36
- getGuildUserList?(guildId: string, channelId?: string): Promise<unknown[]>;
36
+ /** GET /api/v3/guild/list(kook-client 内部分页聚合)。 */
37
+ getGuildList(): Promise<Array<{
38
+ id: string | number;
39
+ name?: string;
40
+ }>>;
41
+ /** GET /api/v3/channel/list(type: 1=文字 2=语音;is_category 为分组)。 */
42
+ getChannelList(guildId: string): Promise<Array<{
43
+ id: string | number;
44
+ name?: string;
45
+ type?: string | number;
46
+ is_category?: boolean;
47
+ }>>;
48
+ /** GET /api/v3/guild/user-list。 */
49
+ getGuildUserList(guildId: string, channelId?: string): Promise<unknown[]>;
37
50
  self_id?: string | number;
38
51
  }
39
52
  export type CreateKookClient = (config: ResolvedKookConfig) => KookClientTransport;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@zhin.js/adapter-kook",
3
- "version": "5.0.3",
3
+ "version": "6.0.1",
4
4
  "description": "Zhin.js KOOK adapter for Plugin Runtime (WebSocket gateway or Webhook)",
5
5
  "type": "module",
6
6
  "main": "./lib/index.js",
@@ -34,19 +34,20 @@
34
34
  },
35
35
  "dependencies": {
36
36
  "kook-client": "^1.0.4",
37
- "@zhin.js/adapter": "1.0.1",
38
- "@zhin.js/core": "1.3.5",
39
- "@zhin.js/host-http": "1.0.1",
37
+ "@zhin.js/adapter": "1.1.1",
38
+ "@zhin.js/command": "1.0.3",
39
+ "@zhin.js/core": "1.4.1",
40
+ "@zhin.js/host-http": "1.0.3",
40
41
  "@zhin.js/logger": "1.0.75",
41
- "@zhin.js/plugin-runtime": "1.0.1"
42
+ "@zhin.js/plugin-runtime": "1.1.1"
42
43
  },
43
44
  "peerDependencies": {
44
45
  "zod": "^4.0.0",
45
- "@zhin.js/adapter": "1.0.1",
46
- "@zhin.js/agent": "1.0.4",
47
- "@zhin.js/core": "1.3.5",
48
- "@zhin.js/plugin-runtime": "1.0.1",
49
- "zhin.js": "4.1.3"
46
+ "@zhin.js/adapter": "1.1.1",
47
+ "@zhin.js/agent": "1.0.6",
48
+ "@zhin.js/core": "1.4.1",
49
+ "@zhin.js/plugin-runtime": "1.1.1",
50
+ "zhin.js": "5.0.1"
50
51
  },
51
52
  "peerDependenciesMeta": {
52
53
  "zhin.js": {
@@ -64,12 +65,13 @@
64
65
  "typescript": "^6.0.3",
65
66
  "vitest": "^4.1.10",
66
67
  "zod": "^4.4.3",
67
- "@zhin.js/agent": "1.0.4",
68
- "@zhin.js/host-http": "1.0.1",
69
- "zhin.js": "4.1.3"
68
+ "@zhin.js/agent": "1.0.6",
69
+ "@zhin.js/host-http": "1.0.3",
70
+ "zhin.js": "5.0.1"
70
71
  },
71
72
  "files": [
72
73
  "adapters",
74
+ "commands",
73
75
  "plugin.ts",
74
76
  "schema.json",
75
77
  "src",
@@ -95,6 +97,10 @@
95
97
  {
96
98
  "package": "@zhin.js/adapter",
97
99
  "api": "^1.0.0"
100
+ },
101
+ {
102
+ "package": "@zhin.js/command",
103
+ "api": "^1.0.0"
98
104
  }
99
105
  ],
100
106
  "plugins": []
package/plugin.ts CHANGED
@@ -1,12 +1,16 @@
1
+ import { createEndpointRuntimeState } from '@zhin.js/adapter';
1
2
  import { definePlugin } from '@zhin.js/plugin-runtime';
2
3
  import { registerKookPlatformPermitChecker } from './src/platform-permit.js';
4
+ import { kookRuntimeStateToken } from './src/kook-runtime-state.js';
3
5
 
4
6
  export default definePlugin({
5
7
  name: 'kook',
6
8
  metadata: {
7
9
  displayName: 'KOOK WebSocket Adapter',
8
10
  },
9
- setup() {
11
+ setup(context) {
12
+ // 运行中 endpoint 注册表(kook endpoint list 的"运行中"数据源)
13
+ context.resources.provide(kookRuntimeStateToken, createEndpointRuntimeState());
10
14
  // 平台权限门禁:guild_owner / guild_admin / channel_admin 等(agent 工具 platformPermit)
11
15
  return registerKookPlatformPermitChecker();
12
16
  },
package/schema.json CHANGED
@@ -3,16 +3,12 @@
3
3
  "type": "object",
4
4
  "additionalProperties": false,
5
5
  "properties": {
6
- "name": {
7
- "type": "string",
8
- "default": "kook-bot"
9
- },
10
- "token": {
11
- "type": "string"
12
- },
13
6
  "connection": {
14
7
  "type": "string",
15
- "enum": ["websocket", "webhook"],
8
+ "enum": [
9
+ "websocket",
10
+ "webhook"
11
+ ],
16
12
  "default": "websocket",
17
13
  "description": "WebSocket gateway (default). webhook requires httpHostToken, verify_token, and a public HTTPS callback URL."
18
14
  },
@@ -21,14 +17,6 @@
21
17
  "default": "/kook/webhook",
22
18
  "description": "POST path registered via httpHostToken when connection is webhook."
23
19
  },
24
- "verify_token": {
25
- "type": "string",
26
- "description": "KOOK developer console verify token (required for webhook mode)."
27
- },
28
- "encrypt_key": {
29
- "type": "string",
30
- "description": "Optional Encrypt Key when message encryption is enabled in KOOK console."
31
- },
32
20
  "data_dir": {
33
21
  "type": "string"
34
22
  },
@@ -42,14 +30,63 @@
42
30
  },
43
31
  "ignore": {
44
32
  "type": "string",
45
- "enum": ["bot", "self"],
33
+ "enum": [
34
+ "bot",
35
+ "self"
36
+ ],
46
37
  "default": "bot"
47
38
  },
48
39
  "logLevel": {
49
40
  "type": "string",
50
- "enum": ["trace", "debug", "info", "warn", "error", "fatal", "mark", "off"],
41
+ "enum": [
42
+ "trace",
43
+ "debug",
44
+ "info",
45
+ "warn",
46
+ "error",
47
+ "fatal",
48
+ "mark",
49
+ "off"
50
+ ],
51
51
  "default": "info"
52
+ },
53
+ "endpoints": {
54
+ "type": "array",
55
+ "description": "多账号:一个插件实例挂多个 endpoint。每项与顶层字段同构(name 必填,其余覆盖顶层)",
56
+ "items": {
57
+ "type": "object",
58
+ "additionalProperties": true,
59
+ "properties": {
60
+ "name": {
61
+ "type": "string",
62
+ "description": "KOOK bot name"
63
+ },
64
+ "token": {
65
+ "type": "string",
66
+ "description": "KOOK bot token"
67
+ },
68
+ "verify_token": {
69
+ "type": "string",
70
+ "description": "KOOK developer console verify token (required for webhook mode)."
71
+ },
72
+ "encrypt_key": {
73
+ "type": "string",
74
+ "description": "Optional Encrypt Key when message encryption is enabled in KOOK console."
75
+ }
76
+ },
77
+ "required": [
78
+ "name",
79
+ "token"
80
+ ]
81
+ }
82
+ },
83
+ "commandPrefix": {
84
+ "type": "string",
85
+ "default": "",
86
+ "description": "命令前缀(默认 '' 无前缀,任意文本按命令匹配;如 '/' 要求 / 开头)。endpoints[i] 可逐项覆盖"
52
87
  }
53
88
  },
54
- "required": ["token"]
89
+ "required": [
90
+ "endpoints"
91
+ ]
55
92
  }
package/src/endpoint.ts CHANGED
@@ -2,7 +2,12 @@
2
2
  * KookEndpoint — lifecycle, outbound, admit, OpenAPI helpers for agent tools.
3
3
  */
4
4
  import { Client } from 'kook-client';
5
- import type { EndpointInstance } from '@zhin.js/adapter';
5
+ import type {
6
+ EndpointChannel,
7
+ EndpointGroup,
8
+ EndpointInstance,
9
+ EndpointManagement,
10
+ } from '@zhin.js/adapter';
6
11
  import type { MessageGateway } from '@zhin.js/core/runtime';
7
12
  import type { HttpHost, HttpRouteRegistration } from '@zhin.js/host-http';
8
13
  import { formatCompact, getLogger } from '@zhin.js/logger';
@@ -44,6 +49,7 @@ export class KookWebsocketEndpoint implements EndpointInstance {
44
49
  #open = false;
45
50
  #started = false;
46
51
  #unregisterAgent?: () => void;
52
+ readonly management: EndpointManagement = createKookEndpointManagement(() => this.#requireClient());
47
53
 
48
54
  constructor(options: KookEndpointOptions) {
49
55
  this.#options = options;
@@ -202,6 +208,7 @@ export class KookWebhookEndpoint implements EndpointInstance {
202
208
  #open = false;
203
209
  #started = false;
204
210
  #unregisterAgent?: () => void;
211
+ readonly management: EndpointManagement = createKookEndpointManagement(() => this.#requireClient());
205
212
 
206
213
  constructor(options: KookWebhookEndpointOptions) {
207
214
  this.#options = options;
@@ -359,3 +366,66 @@ export class KookWebhookEndpoint implements EndpointInstance {
359
366
  return this.#client;
360
367
  }
361
368
  }
369
+
370
+ /**
371
+ * KOOK guild/channel/user id 是字符串形式的雪花号,超出
372
+ * Number.MAX_SAFE_INTEGER,Number() 会丢精度。Console 社交面只把 group_id
373
+ * 当 JSON 值透传、并以字符串回传给 listGroupMembers,因此保留原始字符串
374
+ * (仅按契约类型声明强转)是全链路最不丢信息的方案。
375
+ */
376
+ function toGroupId(id: string): number {
377
+ return id as unknown as number;
378
+ }
379
+
380
+ /** KOOK 文字频道标记:HTTP API type=1(2=语音),kook-client 消息侧用 'GROUP'。 */
381
+ function isKookTextChannelType(type: string | number | undefined): boolean {
382
+ return type === 1 || type === '1' || type === 'GROUP';
383
+ }
384
+
385
+ /**
386
+ * KOOK endpoint 的 EndpointManagement 语义端口(websocket / webhook 共用)。
387
+ * 数据走 kook-client 的 HTTP API 封装:/api/v3/guild/list、
388
+ * /api/v3/channel/list、/api/v3/guild/user-list(SDK 内部已做分页聚合)。
389
+ */
390
+ export function createKookEndpointManagement(
391
+ requireClient: () => KookClientTransport,
392
+ ): EndpointManagement {
393
+ return Object.freeze<EndpointManagement>({
394
+ async listGroups(): Promise<readonly EndpointGroup[]> {
395
+ const guilds = await requireClient().getGuildList();
396
+ const groups: EndpointGroup[] = [];
397
+ for (const guild of guilds) {
398
+ if (guild?.id == null) continue;
399
+ groups.push({
400
+ group_id: toGroupId(String(guild.id)),
401
+ name: String(guild.name ?? guild.id),
402
+ });
403
+ }
404
+ return groups;
405
+ },
406
+ async listChannels(): Promise<readonly EndpointChannel[]> {
407
+ const client = requireClient();
408
+ const channels: EndpointChannel[] = [];
409
+ for (const guild of await client.getGuildList()) {
410
+ if (guild?.id == null) continue;
411
+ const guildId = String(guild.id);
412
+ const guildName = String(guild.name ?? guildId);
413
+ for (const channel of await client.getChannelList(guildId)) {
414
+ if (channel?.id == null) continue;
415
+ if (channel.is_category) continue;
416
+ if (!isKookTextChannelType(channel.type)) continue;
417
+ channels.push({
418
+ id: String(channel.id),
419
+ name: channel.name ? String(channel.name) : undefined,
420
+ parent: { type: 'guild', id: guildId, name: guildName },
421
+ });
422
+ }
423
+ }
424
+ return channels;
425
+ },
426
+ async listGroupMembers(groupId: string): Promise<readonly unknown[]> {
427
+ // 平台形状(User.Info[])原样返回
428
+ return requireClient().getGuildUserList(groupId);
429
+ },
430
+ });
431
+ }
@@ -0,0 +1,19 @@
1
+ /**
2
+ * `kook endpoint` 命令族:由 @zhin.js/adapter 的 createEndpointCommands 套件生成。
3
+ * commands/endpoint/ 下的 list / add / remove 直接默认导出这三项。
4
+ */
5
+ import { createEndpointCommands } from '@zhin.js/adapter';
6
+ import { defineCommand } from '@zhin.js/command';
7
+ import { kookRuntimeStateToken } from './kook-runtime-state.js';
8
+
9
+ export const kookEndpointCommands = createEndpointCommands({
10
+ adapterKey: 'kook',
11
+ adapterDisplayName: 'KOOK',
12
+ fields: [
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)' },
16
+ ],
17
+ running: (use) => use(kookRuntimeStateToken).endpoints.values(),
18
+ describeEntry: (entry) => `token: ${String(entry.token)}`,
19
+ }, defineCommand);
@@ -0,0 +1,7 @@
1
+ /**
2
+ * KOOK 插件实例的运行时状态:adapter create() 注册的 endpoint 列表。
3
+ * 由 plugin.ts setup() provide,adapter create 与 `kook endpoint` 命令共享(同一 owner generation)。
4
+ */
5
+ import { defineEndpointRuntimeStateToken } from '@zhin.js/adapter';
6
+
7
+ export const kookRuntimeStateToken = defineEndpointRuntimeStateToken('kook');
package/src/ws.ts CHANGED
@@ -43,7 +43,17 @@ export interface KookClientTransport {
43
43
  revoke(roleId: string): Promise<boolean>;
44
44
  setNickname(nickname: string): Promise<boolean>;
45
45
  };
46
- getGuildUserList?(guildId: string, channelId?: string): Promise<unknown[]>;
46
+ /** GET /api/v3/guild/list(kook-client 内部分页聚合)。 */
47
+ getGuildList(): Promise<Array<{ id: string | number; name?: string }>>;
48
+ /** GET /api/v3/channel/list(type: 1=文字 2=语音;is_category 为分组)。 */
49
+ getChannelList(guildId: string): Promise<Array<{
50
+ id: string | number;
51
+ name?: string;
52
+ type?: string | number;
53
+ is_category?: boolean;
54
+ }>>;
55
+ /** GET /api/v3/guild/user-list。 */
56
+ getGuildUserList(guildId: string, channelId?: string): Promise<unknown[]>;
47
57
  self_id?: string | number;
48
58
  }
49
59