@zhin.js/adapter-kook 1.0.84 → 1.1.0

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 (74) hide show
  1. package/CHANGELOG.md +804 -17
  2. package/README.md +75 -374
  3. package/adapters/kook.js +41 -0
  4. package/adapters/kook.ts +56 -0
  5. package/agent/PERMITS.md +23 -0
  6. package/agent/tools/blacklist.ts +31 -0
  7. package/agent/tools/create_role.ts +23 -0
  8. package/agent/tools/delete_role.ts +19 -0
  9. package/agent/tools/grant_role.ts +20 -0
  10. package/agent/tools/list_roles.ts +25 -0
  11. package/agent/tools/revoke_role.ts +20 -0
  12. package/commands/endpoint/add/[id].js +3 -0
  13. package/commands/endpoint/add/[id].ts +3 -0
  14. package/commands/endpoint/list.js +3 -0
  15. package/commands/endpoint/list.ts +3 -0
  16. package/commands/endpoint/remove/[id].js +3 -0
  17. package/commands/endpoint/remove/[id].ts +3 -0
  18. package/lib/client.d.ts +11 -0
  19. package/lib/client.js +2 -0
  20. package/lib/endpoint.d.ts +59 -0
  21. package/lib/endpoint.js +374 -0
  22. package/lib/index.d.ts +6 -18
  23. package/lib/index.js +6 -342
  24. package/lib/kook-endpoint-commands.d.ts +1 -0
  25. package/lib/kook-endpoint-commands.js +18 -0
  26. package/lib/kook-runtime-state.d.ts +1 -0
  27. package/lib/kook-runtime-state.js +6 -0
  28. package/lib/platform-permit.d.ts +22 -0
  29. package/lib/platform-permit.js +48 -0
  30. package/lib/protocol.d.ts +144 -0
  31. package/lib/protocol.js +313 -0
  32. package/lib/side-event-dispatch.d.ts +3 -0
  33. package/lib/side-event-dispatch.js +49 -0
  34. package/lib/webhook.d.ts +16 -0
  35. package/lib/webhook.js +71 -0
  36. package/lib/ws.d.ts +56 -0
  37. package/lib/ws.js +63 -0
  38. package/package.json +60 -28
  39. package/plugin.js +19 -0
  40. package/schema.json +128 -0
  41. package/src/client.ts +15 -0
  42. package/src/endpoint.ts +433 -0
  43. package/src/index.ts +45 -322
  44. package/src/kook-endpoint-commands.ts +19 -0
  45. package/src/kook-runtime-state.ts +7 -0
  46. package/src/platform-permit.ts +75 -0
  47. package/src/protocol.ts +462 -0
  48. package/src/side-event-dispatch.ts +54 -0
  49. package/src/webhook.ts +114 -0
  50. package/src/ws.ts +131 -0
  51. package/client/Dashboard.tsx +0 -255
  52. package/client/index.tsx +0 -11
  53. package/client/tsconfig.json +0 -7
  54. package/client/utils/api.ts +0 -17
  55. package/dist/index.js +0 -31
  56. package/lib/adapter.d.ts +0 -26
  57. package/lib/adapter.d.ts.map +0 -1
  58. package/lib/adapter.js +0 -61
  59. package/lib/adapter.js.map +0 -1
  60. package/lib/bot.d.ts +0 -122
  61. package/lib/bot.d.ts.map +0 -1
  62. package/lib/bot.js +0 -608
  63. package/lib/bot.js.map +0 -1
  64. package/lib/index.d.ts.map +0 -1
  65. package/lib/index.js.map +0 -1
  66. package/lib/types.d.ts +0 -32
  67. package/lib/types.d.ts.map +0 -1
  68. package/lib/types.js +0 -8
  69. package/lib/types.js.map +0 -1
  70. package/plugin.yml +0 -3
  71. package/src/adapter.ts +0 -71
  72. package/src/bot.ts +0 -693
  73. package/src/types.ts +0 -36
  74. /package/{skills/kook/SKILL.md → agent/skills/kook.md} +0 -0
package/src/index.ts CHANGED
@@ -1,322 +1,45 @@
1
- /**
2
- * KOOK 适配器入口:类型扩展、导出、注册
3
- */
4
- import path from "node:path";
5
- import { usePlugin, type Plugin, type IGroupManagement, createGroupManagementTools, type ToolFeature } from "zhin.js";
6
- import { KookAdapter } from "./adapter.js";
7
- import { PageManager } from "@zhin.js/console";
8
-
9
- declare module "zhin.js" {
10
- namespace Plugin {
11
- interface Contexts {
12
- web: PageManager;
13
- }
14
- }
15
- }
16
-
17
- declare module "zhin.js" {
18
- interface Adapters {
19
- kook: KookAdapter;
20
- }
21
- }
22
-
23
- export * from "./types.js";
24
- export { KookBot } from "./bot.js";
25
- export { KookAdapter } from "./adapter.js";
26
-
27
- const plugin = usePlugin();
28
- const { provide, useContext } = plugin;
29
-
30
- provide({
31
- name: "kook",
32
- description: "KOOK Adapter",
33
- mounted: async (p: Plugin) => {
34
- const adapter = new KookAdapter(p);
35
- await adapter.start();
36
- return adapter;
37
- },
38
- dispose: async (adapter: KookAdapter) => {
39
- await adapter.stop();
40
- },
41
- });
42
-
43
- useContext('tool', 'kook', (toolService: ToolFeature, kook: KookAdapter) => {
44
- const groupTools = createGroupManagementTools(
45
- kook as unknown as IGroupManagement,
46
- 'kook',
47
- );
48
- const disposers: (() => void)[] = groupTools.map(t => toolService.addTool(t, plugin.name));
49
-
50
- disposers.push(toolService.addTool({
51
- name: 'kook_grant_role',
52
- description: '给用户授予 KOOK 服务器角色',
53
- parameters: {
54
- type: 'object',
55
- properties: {
56
- bot: { type: 'string', description: 'Bot 名称' },
57
- guild_id: { type: 'string', description: '服务器 ID' },
58
- user_id: { type: 'string', description: '用户 ID' },
59
- role_id: { type: 'string', description: '角色 ID' },
60
- },
61
- required: ['bot', 'guild_id', 'user_id', 'role_id'],
62
- },
63
- platforms: ['kook'],
64
- tags: ['kook'],
65
- execute: async (args: Record<string, any>) => {
66
- const bot = kook.bots.get(args.bot);
67
- if (!bot) throw new Error(`Bot ${args.bot} 不存在`);
68
- const success = await bot.grantRole(args.guild_id, args.user_id, args.role_id);
69
- return { success, message: success ? `已授予用户 ${args.user_id} 角色 ${args.role_id}` : '授予角色失败' };
70
- },
71
- }, plugin.name));
72
-
73
- disposers.push(toolService.addTool({
74
- name: 'kook_revoke_role',
75
- description: '撤销用户的 KOOK 服务器角色',
76
- parameters: {
77
- type: 'object',
78
- properties: {
79
- bot: { type: 'string', description: 'Bot 名称' },
80
- guild_id: { type: 'string', description: '服务器 ID' },
81
- user_id: { type: 'string', description: '用户 ID' },
82
- role_id: { type: 'string', description: '角色 ID' },
83
- },
84
- required: ['bot', 'guild_id', 'user_id', 'role_id'],
85
- },
86
- platforms: ['kook'],
87
- tags: ['kook'],
88
- execute: async (args: Record<string, any>) => {
89
- const bot = kook.bots.get(args.bot);
90
- if (!bot) throw new Error(`Bot ${args.bot} 不存在`);
91
- const success = await bot.revokeRole(args.guild_id, args.user_id, args.role_id);
92
- return { success, message: success ? `已撤销用户 ${args.user_id} 的角色 ${args.role_id}` : '撤销角色失败' };
93
- },
94
- }, plugin.name));
95
-
96
- disposers.push(toolService.addTool({
97
- name: 'kook_list_roles',
98
- description: '获取 KOOK 服务器的角色列表',
99
- parameters: {
100
- type: 'object',
101
- properties: {
102
- bot: { type: 'string', description: 'Bot 名称' },
103
- guild_id: { type: 'string', description: '服务器 ID' },
104
- },
105
- required: ['bot', 'guild_id'],
106
- },
107
- platforms: ['kook'],
108
- tags: ['kook'],
109
- execute: async (args: Record<string, any>) => {
110
- const bot = kook.bots.get(args.bot);
111
- if (!bot) throw new Error(`Bot ${args.bot} 不存在`);
112
- const roles = await bot.getRoleList(args.guild_id);
113
- return {
114
- roles: roles.map((r: any) => ({
115
- id: r.role_id,
116
- name: r.name,
117
- color: r.color,
118
- position: r.position,
119
- permissions: r.permissions,
120
- })),
121
- count: roles.length,
122
- };
123
- },
124
- }, plugin.name));
125
-
126
- disposers.push(toolService.addTool({
127
- name: 'kook_create_role',
128
- description: '在 KOOK 服务器中创建新角色',
129
- parameters: {
130
- type: 'object',
131
- properties: {
132
- bot: { type: 'string', description: 'Bot 名称' },
133
- guild_id: { type: 'string', description: '服务器 ID' },
134
- name: { type: 'string', description: '角色名称' },
135
- },
136
- required: ['bot', 'guild_id', 'name'],
137
- },
138
- platforms: ['kook'],
139
- tags: ['kook'],
140
- execute: async (args: Record<string, any>) => {
141
- const bot = kook.bots.get(args.bot);
142
- if (!bot) throw new Error(`Bot ${args.bot} 不存在`);
143
- const role = await bot.createRole(args.guild_id, args.name);
144
- return {
145
- success: true,
146
- message: `已创建角色 "${args.name}"`,
147
- role: { id: role.role_id, name: role.name },
148
- };
149
- },
150
- }, plugin.name));
151
-
152
- disposers.push(toolService.addTool({
153
- name: 'kook_delete_role',
154
- description: '删除 KOOK 服务器中的角色',
155
- parameters: {
156
- type: 'object',
157
- properties: {
158
- bot: { type: 'string', description: 'Bot 名称' },
159
- guild_id: { type: 'string', description: '服务器 ID' },
160
- role_id: { type: 'string', description: '角色 ID' },
161
- },
162
- required: ['bot', 'guild_id', 'role_id'],
163
- },
164
- platforms: ['kook'],
165
- tags: ['kook'],
166
- execute: async (args: Record<string, any>) => {
167
- const bot = kook.bots.get(args.bot);
168
- if (!bot) throw new Error(`Bot ${args.bot} 不存在`);
169
- const success = await bot.deleteRole(args.guild_id, args.role_id);
170
- return { success, message: success ? `已删除角色 ${args.role_id}` : '删除角色失败' };
171
- },
172
- }, plugin.name));
173
-
174
- disposers.push(toolService.addTool({
175
- name: 'kook_blacklist',
176
- description: 'KOOK 服务器黑名单管理:添加/移除',
177
- parameters: {
178
- type: 'object',
179
- properties: {
180
- bot: { type: 'string', description: 'Bot 名称' },
181
- guild_id: { type: 'string', description: '服务器 ID' },
182
- action: { type: 'string', description: 'add|remove', enum: ['add', 'remove'] },
183
- user_id: { type: 'string', description: '用户 ID' },
184
- remark: { type: 'string', description: '备注(add 可选)' },
185
- },
186
- required: ['bot', 'guild_id', 'action', 'user_id'],
187
- },
188
- platforms: ['kook'],
189
- tags: ['kook'],
190
- execute: async (args: Record<string, any>) => {
191
- const bot = kook.bots.get(args.bot);
192
- if (!bot) throw new Error(`Bot ${args.bot} 不存在`);
193
- switch (args.action) {
194
- case 'add': {
195
- const success = await bot.addToBlacklist(args.guild_id, args.user_id, args.remark);
196
- return { success, message: success ? `已将 ${args.user_id} 加入黑名单` : '操作失败' };
197
- }
198
- case 'remove': {
199
- const success = await bot.removeFromBlacklist(args.guild_id, args.user_id);
200
- return { success, message: success ? `已将 ${args.user_id} 从黑名单移除` : '操作失败' };
201
- }
202
- default:
203
- return { success: false, message: `未知操作: ${args.action}` };
204
- }
205
- },
206
- }, plugin.name));
207
-
208
- return () => disposers.forEach(d => d());
209
- });
210
-
211
- // ── Web 控制台 ─────────────────────────────────────────────────────────
212
- useContext("web", (pageManager) => {
213
- pageManager.addEntry({
214
- id: "kook",
215
- development: path.resolve(import.meta.dirname, "../client/index.tsx"),
216
- production: path.resolve(import.meta.dirname, "../dist/index.js"),
217
- meta: { name: "KOOK" },
218
- });
219
- });
220
-
221
- useContext("router", "kook", (router: any, kook: KookAdapter) => {
222
- router.get("/api/kook/bots", async (ctx: any) => {
223
- try {
224
- const bots = Array.from(kook.bots.values());
225
- const result = bots.map((bot: any) => {
226
- try {
227
- return {
228
- name: bot.$config.name,
229
- connected: bot.$connected || false,
230
- guildCount: bot.guilds?.size || 0,
231
- status: bot.$connected ? "online" : "offline",
232
- };
233
- } catch {
234
- return { name: bot.$config.name, connected: false, guildCount: 0, status: "error" };
235
- }
236
- });
237
- ctx.body = { success: true, data: result };
238
- } catch {
239
- ctx.status = 500;
240
- ctx.body = { success: false, error: "获取机器人数据失败" };
241
- }
242
- });
243
-
244
- // Bot 连接/断开
245
- router.post("/api/kook/bots/:name/connect", async (ctx: any) => {
246
- try {
247
- const bot = kook.bots.get(ctx.params.name);
248
- if (!bot) { ctx.status = 404; ctx.body = { success: false, error: "Bot 不存在" }; return; }
249
- if (bot.$connected) { ctx.body = { success: true, message: "已经在线" }; return; }
250
- await bot.$connect();
251
- ctx.body = { success: true, message: "连接成功" };
252
- } catch (e: any) {
253
- ctx.status = 500;
254
- ctx.body = { success: false, error: e?.message || "连接失败" };
255
- }
256
- });
257
-
258
- router.post("/api/kook/bots/:name/disconnect", async (ctx: any) => {
259
- try {
260
- const bot = kook.bots.get(ctx.params.name);
261
- if (!bot) { ctx.status = 404; ctx.body = { success: false, error: "Bot 不存在" }; return; }
262
- if (!bot.$connected) { ctx.body = { success: true, message: "已经离线" }; return; }
263
- await bot.$disconnect();
264
- ctx.body = { success: true, message: "已断开" };
265
- } catch (e: any) {
266
- ctx.status = 500;
267
- ctx.body = { success: false, error: e?.message || "断开失败" };
268
- }
269
- });
270
-
271
- // 角色列表
272
- router.get("/api/kook/bots/:name/guilds/:guildId/roles", async (ctx: any) => {
273
- try {
274
- const bot: any = kook.bots.get(ctx.params.name);
275
- if (!bot) { ctx.status = 404; ctx.body = { success: false, error: "Bot 不存在" }; return; }
276
- if (!bot.$connected) { ctx.status = 400; ctx.body = { success: false, error: "Bot 未连接" }; return; }
277
- const roles = await bot.getRoleList(ctx.params.guildId);
278
- ctx.body = {
279
- success: true,
280
- data: roles.map((r: any) => ({
281
- id: r.role_id,
282
- name: r.name,
283
- color: r.color,
284
- position: r.position,
285
- })),
286
- };
287
- } catch (e: any) {
288
- ctx.status = 500;
289
- ctx.body = { success: false, error: e?.message || "获取角色列表失败" };
290
- }
291
- });
292
-
293
- // 创建角色
294
- router.post("/api/kook/bots/:name/guilds/:guildId/roles", async (ctx: any) => {
295
- try {
296
- const bot: any = kook.bots.get(ctx.params.name);
297
- if (!bot) { ctx.status = 404; ctx.body = { success: false, error: "Bot 不存在" }; return; }
298
- if (!bot.$connected) { ctx.status = 400; ctx.body = { success: false, error: "Bot 未连接" }; return; }
299
- const { name } = ctx.request.body || {};
300
- if (!name) { ctx.status = 400; ctx.body = { success: false, error: "缺少角色名称" }; return; }
301
- const role = await bot.createRole(ctx.params.guildId, name);
302
- ctx.body = { success: true, data: { id: role.role_id, name: role.name } };
303
- } catch (e: any) {
304
- ctx.status = 500;
305
- ctx.body = { success: false, error: e?.message || "创建角色失败" };
306
- }
307
- });
308
-
309
- // 删除角色
310
- router.delete("/api/kook/bots/:name/guilds/:guildId/roles/:roleId", async (ctx: any) => {
311
- try {
312
- const bot: any = kook.bots.get(ctx.params.name);
313
- if (!bot) { ctx.status = 404; ctx.body = { success: false, error: "Bot 不存在" }; return; }
314
- if (!bot.$connected) { ctx.status = 400; ctx.body = { success: false, error: "Bot 未连接" }; return; }
315
- const success = await bot.deleteRole(ctx.params.guildId, ctx.params.roleId);
316
- ctx.body = { success, message: success ? "角色已删除" : "删除失败" };
317
- } catch (e: any) {
318
- ctx.status = 500;
319
- ctx.body = { success: false, error: e?.message || "删除角色失败" };
320
- }
321
- });
322
- });
1
+ export {
2
+ formatInboundContent,
3
+ formatOutboundKmarkdown,
4
+ resolveKookConfig,
5
+ senderDisplayName,
6
+ KookPermission,
7
+ type KookAdapterConfig,
8
+ type KookInboundMessage,
9
+ type KookWireSegment,
10
+ type LogLevel,
11
+ type ResolvedKookConfig,
12
+ type ResolvedKookWebhookConfig,
13
+ type ResolvedKookWebsocketConfig,
14
+ } from './protocol.js';
15
+
16
+ export {
17
+ KookWebhookEndpoint,
18
+ KookWebsocketEndpoint,
19
+ type KookEndpointOptions,
20
+ type KookWebhookEndpointOptions,
21
+ } from './endpoint.js';
22
+
23
+ export {
24
+ registerKookWebhookRoutes,
25
+ handleKookWebhookRequest,
26
+ type KookWebhookHandler,
27
+ } from './webhook.js';
28
+
29
+ export {
30
+ defaultCreateClient,
31
+ defaultCreateWebhookClient,
32
+ normalizeKookMessage,
33
+ type CreateKookClient,
34
+ type KookClientTransport,
35
+ } from './ws.js';
36
+
37
+ export { kookClient, type KookClientEventMap } from './client.js';
38
+
39
+ export {
40
+ checkKookPlatformPermit,
41
+ kookGroupPermitResolver,
42
+ normalizeKookSenderForPermit,
43
+ platformPermit,
44
+ type KookSenderInfo,
45
+ } from './platform-permit.js';
@@ -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');
@@ -0,0 +1,75 @@
1
+ /**
2
+ * KOOK platform permit — guild / channel 差异化门禁
3
+ */
4
+ import type { PermissionSubject } from '@zhin.js/permission';
5
+ import { KookPermission } from './protocol.js';
6
+
7
+ const ADAPTER = 'kook';
8
+
9
+ export function platformPermit(perm: string): string {
10
+ return `platform(${ADAPTER},${perm})`;
11
+ }
12
+
13
+ const FACTORY_PERM_MAP: Record<string, string> = {
14
+ scene_admin: 'guild_admin',
15
+ scene_owner: 'guild_owner',
16
+ };
17
+
18
+ export function kookGroupPermitResolver(logicalPerm: string): string {
19
+ return platformPermit(FACTORY_PERM_MAP[logicalPerm] ?? logicalPerm);
20
+ }
21
+
22
+ export interface KookSenderInfo {
23
+ id: string;
24
+ name: string;
25
+ permission?: KookPermission;
26
+ roles?: number[];
27
+ isGuildOwner?: boolean;
28
+ isAdmin?: boolean;
29
+ role?: string;
30
+ permissions?: string[];
31
+ }
32
+
33
+ export function normalizeKookSenderForPermit(
34
+ info: KookSenderInfo,
35
+ isChannel: boolean,
36
+ ): { role?: string; permissions?: string[] } {
37
+ const permissions: string[] = [];
38
+ if (info.roles?.length) {
39
+ permissions.push(...info.roles.map((r) => `role:${r}`));
40
+ }
41
+
42
+ if (info.isGuildOwner || info.permission === KookPermission.Owner) {
43
+ permissions.push('guild_owner', 'guild_admin');
44
+ return { role: 'owner', permissions };
45
+ }
46
+ if (info.permission === KookPermission.ChannelAdmin && isChannel) {
47
+ permissions.push('channel_admin', 'guild_admin');
48
+ return { role: 'channel_admin', permissions };
49
+ }
50
+ if (info.isAdmin || info.permission === KookPermission.Admin) {
51
+ permissions.push('guild_admin');
52
+ return { role: 'admin', permissions };
53
+ }
54
+ return { role: 'member', permissions };
55
+ }
56
+
57
+ export function checkKookPlatformPermit(perm: string, subject: PermissionSubject): boolean {
58
+ const role = subject.sender?.role?.[0];
59
+ const permissions = subject.sender?.permissions ?? [];
60
+ const has = (t: string) => permissions.includes(t);
61
+
62
+ switch (perm) {
63
+ case 'guild_owner':
64
+ return role === 'owner' || has('guild_owner');
65
+ case 'guild_admin':
66
+ return role === 'owner' || role === 'admin' || role === 'channel_admin' || has('guild_admin') || has('channel_admin');
67
+ case 'channel_admin':
68
+ return role === 'owner' || role === 'admin' || role === 'channel_admin' || has('channel_admin') || has('guild_admin');
69
+ case 'manage_roles':
70
+ return role === 'owner' || role === 'admin' || has('guild_owner') || has('guild_admin');
71
+ default:
72
+ return false;
73
+ }
74
+ }
75
+