@zhin.js/adapter-kook 5.0.0 → 5.0.2
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 +34 -0
- package/README.md +1 -0
- package/{skills/kook → agent}/PERMITS.md +1 -1
- package/agent/tools/blacklist.ts +33 -0
- package/agent/tools/create_role.ts +25 -0
- package/agent/tools/delete_role.ts +21 -0
- package/agent/tools/grant_role.ts +22 -0
- package/agent/tools/list_roles.ts +27 -0
- package/agent/tools/revoke_role.ts +22 -0
- package/dist/index.js +2 -2
- package/lib/agent/tools/blacklist.js +33 -0
- package/lib/agent/tools/blacklist.js.map +1 -0
- package/lib/agent/tools/create_role.js +25 -0
- package/lib/agent/tools/create_role.js.map +1 -0
- package/lib/agent/tools/delete_role.js +21 -0
- package/lib/agent/tools/delete_role.js.map +1 -0
- package/lib/agent/tools/grant_role.js +22 -0
- package/lib/agent/tools/grant_role.js.map +1 -0
- package/lib/agent/tools/list_roles.js +27 -0
- package/lib/agent/tools/list_roles.js.map +1 -0
- package/lib/agent/tools/revoke_role.js +22 -0
- package/lib/agent/tools/revoke_role.js.map +1 -0
- package/lib/{adapter.js → src/adapter.js} +4 -3
- package/lib/src/adapter.js.map +1 -0
- package/lib/{endpoint.js → src/endpoint.js} +93 -59
- package/lib/src/endpoint.js.map +1 -0
- package/lib/src/index.js +197 -0
- package/lib/src/index.js.map +1 -0
- package/lib/src/kook-agent-deps.js +10 -0
- package/lib/src/kook-agent-deps.js.map +1 -0
- package/lib/src/kook-asset-upload.js.map +1 -0
- package/lib/src/kook-inbound.js.map +1 -0
- package/lib/src/kook-msg-route.js.map +1 -0
- package/lib/{kook-side-events.js → src/kook-side-events.js} +52 -33
- package/lib/src/kook-side-events.js.map +1 -0
- package/lib/src/outbound-keyboard.js +66 -0
- package/lib/src/outbound-keyboard.js.map +1 -0
- package/lib/{outbound-media.js → src/outbound-media.js} +4 -1
- package/lib/src/outbound-media.js.map +1 -0
- package/lib/{outbound-sendable.js → src/outbound-sendable.js} +9 -0
- package/lib/src/outbound-sendable.js.map +1 -0
- package/lib/{platform-permit.js → src/platform-permit.js} +5 -2
- package/lib/src/platform-permit.js.map +1 -0
- package/lib/src/segment-mapper.js +2 -0
- package/lib/src/segment-mapper.js.map +1 -0
- package/lib/src/types.js.map +1 -0
- package/package.json +26 -16
- package/src/adapter.ts +5 -5
- package/src/endpoint.ts +97 -66
- package/src/index.ts +14 -168
- package/src/kook-agent-deps.ts +22 -0
- package/src/kook-side-events.ts +66 -38
- package/src/outbound-keyboard.ts +77 -0
- package/src/outbound-media.ts +1 -8
- package/src/outbound-sendable.ts +13 -1
- package/src/platform-permit.ts +5 -6
- package/src/segment-mapper.ts +1 -0
- package/src/types.ts +1 -2
- package/lib/adapter.d.ts +0 -29
- package/lib/adapter.d.ts.map +0 -1
- package/lib/adapter.js.map +0 -1
- package/lib/endpoint.d.ts +0 -155
- package/lib/endpoint.d.ts.map +0 -1
- package/lib/endpoint.js.map +0 -1
- package/lib/index.d.ts +0 -18
- package/lib/index.d.ts.map +0 -1
- package/lib/index.js +0 -350
- package/lib/index.js.map +0 -1
- package/lib/kook-asset-upload.d.ts +0 -12
- package/lib/kook-asset-upload.d.ts.map +0 -1
- package/lib/kook-asset-upload.js.map +0 -1
- package/lib/kook-inbound.d.ts +0 -6
- package/lib/kook-inbound.d.ts.map +0 -1
- package/lib/kook-inbound.js.map +0 -1
- package/lib/kook-msg-route.d.ts +0 -34
- package/lib/kook-msg-route.d.ts.map +0 -1
- package/lib/kook-msg-route.js.map +0 -1
- package/lib/kook-side-events.d.ts +0 -43
- package/lib/kook-side-events.d.ts.map +0 -1
- package/lib/kook-side-events.js.map +0 -1
- package/lib/outbound-media.d.ts +0 -10
- package/lib/outbound-media.d.ts.map +0 -1
- package/lib/outbound-media.js.map +0 -1
- package/lib/outbound-sendable.d.ts +0 -11
- package/lib/outbound-sendable.d.ts.map +0 -1
- package/lib/outbound-sendable.js.map +0 -1
- package/lib/platform-permit.d.ts +0 -14
- package/lib/platform-permit.d.ts.map +0 -1
- package/lib/platform-permit.js.map +0 -1
- package/lib/types.d.ts +0 -54
- package/lib/types.d.ts.map +0 -1
- package/lib/types.js.map +0 -1
- /package/{skills/kook/SKILL.md → agent/skills/kook.md} +0 -0
- /package/lib/{kook-asset-upload.js → src/kook-asset-upload.js} +0 -0
- /package/lib/{kook-inbound.js → src/kook-inbound.js} +0 -0
- /package/lib/{kook-msg-route.js → src/kook-msg-route.js} +0 -0
- /package/lib/{types.js → src/types.js} +0 -0
package/src/index.ts
CHANGED
|
@@ -2,9 +2,10 @@
|
|
|
2
2
|
* KOOK 适配器入口:类型扩展、导出、注册
|
|
3
3
|
*/
|
|
4
4
|
import path from "node:path";
|
|
5
|
-
import { usePlugin, type Plugin, type
|
|
5
|
+
import { usePlugin, type Plugin, type ISceneManagement, createSceneManagementTools, type ToolFeature } from "zhin.js";
|
|
6
6
|
import { KookAdapter } from "./adapter.js";
|
|
7
|
-
import { kookGroupPermitResolver,
|
|
7
|
+
import { kookGroupPermitResolver, registerKookPlatformPermitChecker } from "./platform-permit.js";
|
|
8
|
+
import { setKookAgentDeps } from "./kook-agent-deps.js";
|
|
8
9
|
import { PageManager } from "@zhin.js/host-api";
|
|
9
10
|
|
|
10
11
|
declare module "zhin.js" {
|
|
@@ -44,175 +45,20 @@ provide({
|
|
|
44
45
|
useContext('tool', 'kook', (toolService: ToolFeature, kook: KookAdapter) => {
|
|
45
46
|
const disposers: (() => void)[] = [];
|
|
46
47
|
disposers.push(registerKookPlatformPermitChecker());
|
|
47
|
-
|
|
48
|
-
|
|
48
|
+
setKookAgentDeps({
|
|
49
|
+
getEndpoint: (endpointId) => {
|
|
50
|
+
const endpoint = kook.endpoints.get(endpointId);
|
|
51
|
+
if (!endpoint) throw new Error(`Endpoint ${endpointId} 不存在`);
|
|
52
|
+
return endpoint;
|
|
53
|
+
},
|
|
54
|
+
getAdapter: () => kook,
|
|
55
|
+
});
|
|
56
|
+
const sceneTools = createSceneManagementTools(
|
|
57
|
+
kook as unknown as ISceneManagement,
|
|
49
58
|
'kook',
|
|
50
59
|
{ permitResolver: kookGroupPermitResolver, registerChecker: false },
|
|
51
60
|
);
|
|
52
|
-
disposers.push(...
|
|
53
|
-
|
|
54
|
-
disposers.push(toolService.addTool({
|
|
55
|
-
name: 'kook_grant_role',
|
|
56
|
-
description: '给用户授予 KOOK 服务器角色',
|
|
57
|
-
parameters: {
|
|
58
|
-
type: 'object',
|
|
59
|
-
properties: {
|
|
60
|
-
endpoint_id: { type: 'string', description: 'Endpoint 名称', contextKey: 'endpointId' },
|
|
61
|
-
guild_id: { type: 'string', description: '服务器 ID' },
|
|
62
|
-
user_id: { type: 'string', description: '用户 ID' },
|
|
63
|
-
role_id: { type: 'string', description: '角色 ID' },
|
|
64
|
-
},
|
|
65
|
-
required: ['endpoint_id', 'guild_id', 'user_id', 'role_id'],
|
|
66
|
-
},
|
|
67
|
-
platforms: ['kook'],
|
|
68
|
-
tags: ['kook'],
|
|
69
|
-
permissions: [platformPermit('manage_roles')],
|
|
70
|
-
execute: async (args: Record<string, any>) => {
|
|
71
|
-
const endpoint = kook.endpoints.get(args.endpoint_id);
|
|
72
|
-
if (!endpoint) throw new Error(`Endpoint ${args.endpoint_id} 不存在`);
|
|
73
|
-
const success = await endpoint.grantRole(args.guild_id, args.user_id, args.role_id);
|
|
74
|
-
return { success, message: success ? `已授予用户 ${args.user_id} 角色 ${args.role_id}` : '授予角色失败' };
|
|
75
|
-
},
|
|
76
|
-
}, plugin.name));
|
|
77
|
-
|
|
78
|
-
disposers.push(toolService.addTool({
|
|
79
|
-
name: 'kook_revoke_role',
|
|
80
|
-
description: '撤销用户的 KOOK 服务器角色',
|
|
81
|
-
parameters: {
|
|
82
|
-
type: 'object',
|
|
83
|
-
properties: {
|
|
84
|
-
endpoint_id: { type: 'string', description: 'Endpoint 名称', contextKey: 'endpointId' },
|
|
85
|
-
guild_id: { type: 'string', description: '服务器 ID' },
|
|
86
|
-
user_id: { type: 'string', description: '用户 ID' },
|
|
87
|
-
role_id: { type: 'string', description: '角色 ID' },
|
|
88
|
-
},
|
|
89
|
-
required: ['endpoint_id', 'guild_id', 'user_id', 'role_id'],
|
|
90
|
-
},
|
|
91
|
-
platforms: ['kook'],
|
|
92
|
-
tags: ['kook'],
|
|
93
|
-
permissions: [platformPermit('manage_roles')],
|
|
94
|
-
execute: async (args: Record<string, any>) => {
|
|
95
|
-
const endpoint = kook.endpoints.get(args.endpoint_id);
|
|
96
|
-
if (!endpoint) throw new Error(`Endpoint ${args.endpoint_id} 不存在`);
|
|
97
|
-
const success = await endpoint.revokeRole(args.guild_id, args.user_id, args.role_id);
|
|
98
|
-
return { success, message: success ? `已撤销用户 ${args.user_id} 的角色 ${args.role_id}` : '撤销角色失败' };
|
|
99
|
-
},
|
|
100
|
-
}, plugin.name));
|
|
101
|
-
|
|
102
|
-
disposers.push(toolService.addTool({
|
|
103
|
-
name: 'kook_list_roles',
|
|
104
|
-
description: '获取 KOOK 服务器的角色列表',
|
|
105
|
-
parameters: {
|
|
106
|
-
type: 'object',
|
|
107
|
-
properties: {
|
|
108
|
-
endpoint_id: { type: 'string', description: 'Endpoint 名称', contextKey: 'endpointId' },
|
|
109
|
-
guild_id: { type: 'string', description: '服务器 ID' },
|
|
110
|
-
},
|
|
111
|
-
required: ['endpoint_id', 'guild_id'],
|
|
112
|
-
},
|
|
113
|
-
platforms: ['kook'],
|
|
114
|
-
tags: ['kook'],
|
|
115
|
-
execute: async (args: Record<string, any>) => {
|
|
116
|
-
const endpoint = kook.endpoints.get(args.endpoint_id);
|
|
117
|
-
if (!endpoint) throw new Error(`Endpoint ${args.endpoint_id} 不存在`);
|
|
118
|
-
const roles = await endpoint.getRoleList(args.guild_id);
|
|
119
|
-
return {
|
|
120
|
-
roles: roles.map((r: any) => ({
|
|
121
|
-
id: r.role_id,
|
|
122
|
-
name: r.name,
|
|
123
|
-
color: r.color,
|
|
124
|
-
position: r.position,
|
|
125
|
-
permissions: r.permissions,
|
|
126
|
-
})),
|
|
127
|
-
count: roles.length,
|
|
128
|
-
};
|
|
129
|
-
},
|
|
130
|
-
}, plugin.name));
|
|
131
|
-
|
|
132
|
-
disposers.push(toolService.addTool({
|
|
133
|
-
name: 'kook_create_role',
|
|
134
|
-
description: '在 KOOK 服务器中创建新角色',
|
|
135
|
-
parameters: {
|
|
136
|
-
type: 'object',
|
|
137
|
-
properties: {
|
|
138
|
-
endpoint_id: { type: 'string', description: 'Endpoint 名称', contextKey: 'endpointId' },
|
|
139
|
-
guild_id: { type: 'string', description: '服务器 ID' },
|
|
140
|
-
name: { type: 'string', description: '角色名称' },
|
|
141
|
-
},
|
|
142
|
-
required: ['endpoint_id', 'guild_id', 'name'],
|
|
143
|
-
},
|
|
144
|
-
platforms: ['kook'],
|
|
145
|
-
tags: ['kook'],
|
|
146
|
-
permissions: [platformPermit('guild_owner')],
|
|
147
|
-
execute: async (args: Record<string, any>) => {
|
|
148
|
-
const endpoint = kook.endpoints.get(args.endpoint_id);
|
|
149
|
-
if (!endpoint) throw new Error(`Endpoint ${args.endpoint_id} 不存在`);
|
|
150
|
-
const role = await endpoint.createRole(args.guild_id, args.name);
|
|
151
|
-
return {
|
|
152
|
-
success: true,
|
|
153
|
-
message: `已创建角色 "${args.name}"`,
|
|
154
|
-
role: { id: role.role_id, name: role.name },
|
|
155
|
-
};
|
|
156
|
-
},
|
|
157
|
-
}, plugin.name));
|
|
158
|
-
|
|
159
|
-
disposers.push(toolService.addTool({
|
|
160
|
-
name: 'kook_delete_role',
|
|
161
|
-
description: '删除 KOOK 服务器中的角色',
|
|
162
|
-
parameters: {
|
|
163
|
-
type: 'object',
|
|
164
|
-
properties: {
|
|
165
|
-
endpoint_id: { type: 'string', description: 'Endpoint 名称', contextKey: 'endpointId' },
|
|
166
|
-
guild_id: { type: 'string', description: '服务器 ID' },
|
|
167
|
-
role_id: { type: 'string', description: '角色 ID' },
|
|
168
|
-
},
|
|
169
|
-
required: ['endpoint_id', 'guild_id', 'role_id'],
|
|
170
|
-
},
|
|
171
|
-
platforms: ['kook'],
|
|
172
|
-
tags: ['kook'],
|
|
173
|
-
permissions: [platformPermit('guild_owner')],
|
|
174
|
-
execute: async (args: Record<string, any>) => {
|
|
175
|
-
const endpoint = kook.endpoints.get(args.endpoint_id);
|
|
176
|
-
if (!endpoint) throw new Error(`Endpoint ${args.endpoint_id} 不存在`);
|
|
177
|
-
const success = await endpoint.deleteRole(args.guild_id, args.role_id);
|
|
178
|
-
return { success, message: success ? `已删除角色 ${args.role_id}` : '删除角色失败' };
|
|
179
|
-
},
|
|
180
|
-
}, plugin.name));
|
|
181
|
-
|
|
182
|
-
disposers.push(toolService.addTool({
|
|
183
|
-
name: 'kook_blacklist',
|
|
184
|
-
description: 'KOOK 服务器黑名单管理:添加/移除',
|
|
185
|
-
parameters: {
|
|
186
|
-
type: 'object',
|
|
187
|
-
properties: {
|
|
188
|
-
endpoint_id: { type: 'string', description: 'Endpoint 名称', contextKey: 'endpointId' },
|
|
189
|
-
guild_id: { type: 'string', description: '服务器 ID' },
|
|
190
|
-
action: { type: 'string', description: 'add|remove', enum: ['add', 'remove'] },
|
|
191
|
-
user_id: { type: 'string', description: '用户 ID' },
|
|
192
|
-
remark: { type: 'string', description: '备注(add 可选)' },
|
|
193
|
-
},
|
|
194
|
-
required: ['endpoint_id', 'guild_id', 'action', 'user_id'],
|
|
195
|
-
},
|
|
196
|
-
platforms: ['kook'],
|
|
197
|
-
tags: ['kook'],
|
|
198
|
-
permissions: [platformPermit('guild_admin')],
|
|
199
|
-
execute: async (args: Record<string, any>) => {
|
|
200
|
-
const endpoint = kook.endpoints.get(args.endpoint_id);
|
|
201
|
-
if (!endpoint) throw new Error(`Endpoint ${args.endpoint_id} 不存在`);
|
|
202
|
-
switch (args.action) {
|
|
203
|
-
case 'add': {
|
|
204
|
-
const success = await endpoint.addToBlacklist(args.guild_id, args.user_id, args.remark);
|
|
205
|
-
return { success, message: success ? `已将 ${args.user_id} 加入黑名单` : '操作失败' };
|
|
206
|
-
}
|
|
207
|
-
case 'remove': {
|
|
208
|
-
const success = await endpoint.removeFromBlacklist(args.guild_id, args.user_id);
|
|
209
|
-
return { success, message: success ? `已将 ${args.user_id} 从黑名单移除` : '操作失败' };
|
|
210
|
-
}
|
|
211
|
-
default:
|
|
212
|
-
return { success: false, message: `未知操作: ${args.action}` };
|
|
213
|
-
}
|
|
214
|
-
},
|
|
215
|
-
}, plugin.name));
|
|
61
|
+
disposers.push(...sceneTools.map(t => toolService.addTool(t, plugin.name)));
|
|
216
62
|
|
|
217
63
|
return () => disposers.forEach(d => d());
|
|
218
64
|
});
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared runtime deps for kook agent/ authoring tools.
|
|
3
|
+
* Set once from plugin index during init — never call getPlugin() from tool execute.
|
|
4
|
+
*/
|
|
5
|
+
import type { KookAdapter } from './adapter.js';
|
|
6
|
+
import type { KookEndpoint } from './endpoint.js';
|
|
7
|
+
|
|
8
|
+
export interface KookAgentDeps {
|
|
9
|
+
getEndpoint: (endpointId: string) => KookEndpoint;
|
|
10
|
+
getAdapter: () => KookAdapter;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
let _deps: KookAgentDeps | null = null;
|
|
14
|
+
|
|
15
|
+
export function setKookAgentDeps(deps: KookAgentDeps): void {
|
|
16
|
+
_deps = deps;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export function getKookAgentDeps(): KookAgentDeps {
|
|
20
|
+
if (!_deps) throw new Error('kook agent deps not initialized');
|
|
21
|
+
return _deps;
|
|
22
|
+
}
|
package/src/kook-side-events.ts
CHANGED
|
@@ -1,10 +1,15 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* KOOK Gateway 非聊天入站:系统消息 (type=255) / BROADCAST → Notice
|
|
3
|
-
* @see https://developer.kookapp.cn/doc/event/event-introduction
|
|
4
3
|
*/
|
|
5
|
-
import {
|
|
4
|
+
import {
|
|
5
|
+
Notice,
|
|
6
|
+
Message,
|
|
7
|
+
mapNoticeParts,
|
|
8
|
+
senderFromId,
|
|
9
|
+
buildNotice,
|
|
10
|
+
formatCompact,
|
|
11
|
+
} from "zhin.js";
|
|
6
12
|
|
|
7
|
-
/** KOOK WebSocket s=0 时 d 字段(kook-client Receiver 入参) */
|
|
8
13
|
export interface KookGatewayEvent {
|
|
9
14
|
channel_type?: string;
|
|
10
15
|
type?: number;
|
|
@@ -30,7 +35,6 @@ export interface KookGatewayExtra {
|
|
|
30
35
|
|
|
31
36
|
const SYSTEM_MESSAGE_TYPE = 255;
|
|
32
37
|
|
|
33
|
-
/** 是否应由适配器作为 notice 处理(kook-client 对 notice 的 transform 为空实现) */
|
|
34
38
|
export function isKookNoticeGatewayEvent(data: unknown): data is KookGatewayEvent {
|
|
35
39
|
if (!data || typeof data !== "object") return false;
|
|
36
40
|
const ev = data as KookGatewayEvent;
|
|
@@ -50,34 +54,12 @@ export function resolveKookSideEventDedupeKey(
|
|
|
50
54
|
return `${kind}:${ts}_${noticeType}_${scope}`;
|
|
51
55
|
}
|
|
52
56
|
|
|
53
|
-
const NOTICE_TYPE_MAP: Record<string, NoticeType> = {
|
|
54
|
-
joined_guild: "group_member_increase",
|
|
55
|
-
exited_guild: "group_member_decrease",
|
|
56
|
-
deleted_message: "group_recall",
|
|
57
|
-
deleted_private_message: "friend_recall",
|
|
58
|
-
added_reaction: "group_emoji_reaction",
|
|
59
|
-
deleted_reaction: "group_emoji_reaction",
|
|
60
|
-
private_added_reaction: "group_emoji_reaction",
|
|
61
|
-
private_deleted_reaction: "group_emoji_reaction",
|
|
62
|
-
};
|
|
63
|
-
|
|
64
|
-
function resolveNoticeZhinType(rawType: string): NoticeType {
|
|
65
|
-
return NOTICE_TYPE_MAP[rawType] ?? rawType;
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
function senderFromId(id: unknown, name?: string) {
|
|
69
|
-
if (id == null || id === "") return undefined;
|
|
70
|
-
const s = String(id);
|
|
71
|
-
return { id: s, name: name ?? s };
|
|
72
|
-
}
|
|
73
|
-
|
|
74
57
|
function readString(obj: Record<string, unknown> | undefined, key: string): string | undefined {
|
|
75
58
|
const v = obj?.[key];
|
|
76
59
|
if (v == null || v === "") return undefined;
|
|
77
60
|
return String(v);
|
|
78
61
|
}
|
|
79
62
|
|
|
80
|
-
/** 解析 notice 发生的频道 / 群 / 私聊 */
|
|
81
63
|
export function resolveKookNoticeChannel(
|
|
82
64
|
event: KookGatewayEvent,
|
|
83
65
|
): { id: string; type: "group" | "private" | "channel" } {
|
|
@@ -161,11 +143,11 @@ export function enrichKookGatewayForPlugins(event: KookGatewayEvent): KookGatewa
|
|
|
161
143
|
export function formatKookNotice(
|
|
162
144
|
event: KookGatewayEvent,
|
|
163
145
|
endpointName: string,
|
|
164
|
-
)
|
|
146
|
+
) {
|
|
165
147
|
const extra = event.extra ?? {};
|
|
166
148
|
const body = extra.body ?? {};
|
|
167
149
|
const rawType = String(extra.type ?? "unknown");
|
|
168
|
-
const
|
|
150
|
+
const { scene_type, sub_type } = mapNoticeParts('kook', rawType);
|
|
169
151
|
const channel = resolveKookNoticeChannel(event);
|
|
170
152
|
const ts = event.msg_timestamp ?? Date.now();
|
|
171
153
|
|
|
@@ -177,16 +159,15 @@ export function formatKookNotice(
|
|
|
177
159
|
readString(body, "operator_id")
|
|
178
160
|
?? (event.author_id !== "1" ? event.author_id : undefined);
|
|
179
161
|
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
return Notice.from(event, {
|
|
162
|
+
return buildNotice(event, {
|
|
183
163
|
$id: resolveKookSideEventDedupeKey(event, "notice"),
|
|
184
164
|
$adapter: "kook",
|
|
185
165
|
$endpoint: endpointName,
|
|
186
|
-
$type,
|
|
187
|
-
$
|
|
188
|
-
$
|
|
189
|
-
$
|
|
166
|
+
$type: "notice",
|
|
167
|
+
$scene_id: channel.id,
|
|
168
|
+
$scene_type: scene_type,
|
|
169
|
+
$sub_type: sub_type,
|
|
170
|
+
$actor: senderFromId(operatorId),
|
|
190
171
|
$target: senderFromId(targetId),
|
|
191
172
|
$timestamp: ts,
|
|
192
173
|
});
|
|
@@ -194,9 +175,56 @@ export function formatKookNotice(
|
|
|
194
175
|
|
|
195
176
|
export function formatKookNoticeLog(notice: ReturnType<typeof formatKookNotice>): string {
|
|
196
177
|
return formatCompact({
|
|
197
|
-
notice: notice.$type
|
|
198
|
-
kook_type: notice.$
|
|
199
|
-
|
|
178
|
+
notice: `${notice.$type}.${notice.$scene_type}.${notice.$sub_type}`,
|
|
179
|
+
kook_type: notice.$sub_type,
|
|
180
|
+
scene: `${notice.$scene_type}(${notice.$scene_id})`,
|
|
200
181
|
endpoint: notice.$endpoint,
|
|
201
182
|
});
|
|
202
183
|
}
|
|
184
|
+
|
|
185
|
+
export function isKookButtonClickEvent(event: KookGatewayEvent): boolean {
|
|
186
|
+
return event.type === SYSTEM_MESSAGE_TYPE && event.extra?.type === 'message_btn_click';
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
export function formatKookButtonClickMessage(
|
|
190
|
+
event: KookGatewayEvent,
|
|
191
|
+
endpointName: string,
|
|
192
|
+
): ReturnType<typeof Message.from<KookGatewayEvent>> {
|
|
193
|
+
const extra = event.extra ?? {};
|
|
194
|
+
const body = extra.body ?? {};
|
|
195
|
+
const payload = String(body.value ?? '');
|
|
196
|
+
const userInfo = body.user_info as { id?: string; username?: string; nickname?: string } | undefined;
|
|
197
|
+
const userId = String(body.user_id ?? userInfo?.id ?? event.author_id ?? '');
|
|
198
|
+
const userName = userInfo?.nickname ?? userInfo?.username ?? userId;
|
|
199
|
+
const sourceMessageId = String(body.msg_id ?? '');
|
|
200
|
+
|
|
201
|
+
let channelType: 'private' | 'channel' = 'private';
|
|
202
|
+
let channelId = String(body.target_id ?? event.target_id ?? userId);
|
|
203
|
+
if (body.channel_type === 'GROUP' || event.channel_type === 'GROUP') {
|
|
204
|
+
channelType = 'channel';
|
|
205
|
+
channelId = String(body.target_id ?? event.target_id ?? channelId);
|
|
206
|
+
} else if (body.channel_type === 'PERSON' || event.channel_type === 'PERSON') {
|
|
207
|
+
channelType = 'private';
|
|
208
|
+
channelId = String(body.target_id ?? event.target_id ?? userId);
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
return Message.from(event, {
|
|
212
|
+
$id: event.msg_id ?? `btn:${Date.now()}`,
|
|
213
|
+
$adapter: 'kook',
|
|
214
|
+
$endpoint: endpointName,
|
|
215
|
+
$sender: { id: userId, name: userName },
|
|
216
|
+
$channel: { id: channelId, type: channelType },
|
|
217
|
+
$content: [{
|
|
218
|
+
type: 'action',
|
|
219
|
+
data: {
|
|
220
|
+
id: payload,
|
|
221
|
+
payload,
|
|
222
|
+
sourceMessageId,
|
|
223
|
+
},
|
|
224
|
+
}],
|
|
225
|
+
$raw: payload,
|
|
226
|
+
$timestamp: event.msg_timestamp ?? Date.now(),
|
|
227
|
+
$recall: async () => {},
|
|
228
|
+
$reply: async () => '',
|
|
229
|
+
});
|
|
230
|
+
}
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* core keyboard 段 → KOOK 卡片消息(action-group + button)
|
|
3
|
+
*/
|
|
4
|
+
import { KeyboardSegment, isKeyboardSegment, type KeyboardSegmentData, type MessageElement } from 'zhin.js';
|
|
5
|
+
|
|
6
|
+
function asKeyboardElement(
|
|
7
|
+
el: MessageElement,
|
|
8
|
+
): (MessageElement & { data: KeyboardSegmentData }) | null {
|
|
9
|
+
if (el instanceof KeyboardSegment) {
|
|
10
|
+
return { type: "keyboard", data: el.data };
|
|
11
|
+
}
|
|
12
|
+
if (isKeyboardSegment(el)) return el;
|
|
13
|
+
return null;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
function textFromElements(elements: MessageElement[]): string {
|
|
17
|
+
return elements
|
|
18
|
+
.filter((el) => el.type === "text" && typeof el.data.text === "string")
|
|
19
|
+
.map((el) => el.data.text as string)
|
|
20
|
+
.join("\n")
|
|
21
|
+
.trim();
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
/** core keyboard 布局 → KOOK card[](可直接传给 sendChannelMsg / sendPrivateMsg) */
|
|
25
|
+
export function coreKeyboardToKookCard(
|
|
26
|
+
data: KeyboardSegmentData,
|
|
27
|
+
prefixElements: MessageElement[] = [],
|
|
28
|
+
): Array<Record<string, unknown>> {
|
|
29
|
+
const modules: Array<Record<string, unknown>> = [];
|
|
30
|
+
const prefixText = textFromElements(prefixElements);
|
|
31
|
+
if (prefixText) {
|
|
32
|
+
modules.push({
|
|
33
|
+
type: "section",
|
|
34
|
+
text: { type: "plain-text", content: prefixText },
|
|
35
|
+
});
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
for (const row of data.rows ?? []) {
|
|
39
|
+
modules.push({
|
|
40
|
+
type: "action-group",
|
|
41
|
+
elements: row.map((btn) => ({
|
|
42
|
+
type: "button",
|
|
43
|
+
theme: btn.disabled ? "secondary" : "primary",
|
|
44
|
+
value: btn.payload,
|
|
45
|
+
click: "return-val",
|
|
46
|
+
text: { type: "plain-text", content: btn.label },
|
|
47
|
+
})),
|
|
48
|
+
});
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
return [{
|
|
52
|
+
type: "card",
|
|
53
|
+
theme: "secondary",
|
|
54
|
+
size: "sm",
|
|
55
|
+
modules,
|
|
56
|
+
}];
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
export function findKeyboardSegment(
|
|
60
|
+
elements: MessageElement[],
|
|
61
|
+
): { keyboard: MessageElement & { data: KeyboardSegmentData }; rest: MessageElement[] } | null {
|
|
62
|
+
let keyboard: (MessageElement & { data: KeyboardSegmentData }) | undefined;
|
|
63
|
+
const rest: MessageElement[] = [];
|
|
64
|
+
|
|
65
|
+
for (const el of elements) {
|
|
66
|
+
const kb = asKeyboardElement(el);
|
|
67
|
+
if (kb) {
|
|
68
|
+
if (keyboard) return null;
|
|
69
|
+
keyboard = kb;
|
|
70
|
+
continue;
|
|
71
|
+
}
|
|
72
|
+
rest.push(el);
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
if (!keyboard) return null;
|
|
76
|
+
return { keyboard, rest };
|
|
77
|
+
}
|
package/src/outbound-media.ts
CHANGED
|
@@ -1,14 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* KOOK 出站 media:base64 / 本地文件须先走 /v3/asset/create 上传,再嵌入 KMarkdown。
|
|
3
3
|
*/
|
|
4
|
-
import type
|
|
5
|
-
import {
|
|
6
|
-
asMessageElements,
|
|
7
|
-
extractBase64Buffer,
|
|
8
|
-
isMediaSegmentType,
|
|
9
|
-
isRemoteUrl,
|
|
10
|
-
resolveLocalMediaPath,
|
|
11
|
-
} from "zhin.js";
|
|
4
|
+
import { asMessageElements, extractBase64Buffer, isMediaSegmentType, isRemoteUrl, resolveLocalMediaPath, type MessageElement, type MessageSegment, type SendContent } from 'zhin.js';
|
|
12
5
|
|
|
13
6
|
export interface KookMediaUploader {
|
|
14
7
|
uploadMedia(data: string | Buffer): Promise<string>;
|
package/src/outbound-sendable.ts
CHANGED
|
@@ -4,6 +4,9 @@
|
|
|
4
4
|
*/
|
|
5
5
|
import { segment, type MessageSegment as KookMessageSegment } from "kook-client";
|
|
6
6
|
import type { MessageElement } from "zhin.js";
|
|
7
|
+
import { coreKeyboardToKookCard, findKeyboardSegment } from "./outbound-keyboard.js";
|
|
8
|
+
|
|
9
|
+
export type KookSendable = string | KookMessageSegment[] | Array<Record<string, unknown>>;
|
|
7
10
|
|
|
8
11
|
function mediaUrl(data: Record<string, unknown>): string {
|
|
9
12
|
return String(data.url ?? data.file ?? "");
|
|
@@ -15,7 +18,16 @@ function mediaUrl(data: Record<string, unknown>): string {
|
|
|
15
18
|
export function convertToKookSendable(
|
|
16
19
|
elements: MessageElement[],
|
|
17
20
|
formatText: (content: MessageElement[]) => string,
|
|
18
|
-
):
|
|
21
|
+
): KookSendable {
|
|
22
|
+
const keyboardBundle = findKeyboardSegment(elements);
|
|
23
|
+
if (keyboardBundle) {
|
|
24
|
+
const { keyboard, rest } = keyboardBundle;
|
|
25
|
+
const nonReplyRest = rest.filter((el) => el.type !== "reply");
|
|
26
|
+
if (nonReplyRest.every((el) => el.type === "text" || el.type === "reply")) {
|
|
27
|
+
return coreKeyboardToKookCard(keyboard.data, nonReplyRest.filter((el) => el.type === "text"));
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
|
|
19
31
|
const replies: string[] = [];
|
|
20
32
|
const images: string[] = [];
|
|
21
33
|
const videos: string[] = [];
|
package/src/platform-permit.ts
CHANGED
|
@@ -1,10 +1,9 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* KOOK platform permit — guild / channel 差异化门禁
|
|
3
3
|
*/
|
|
4
|
-
import type
|
|
5
|
-
|
|
6
|
-
import { KookPermission } from './types.js';
|
|
7
|
-
import type { KookSenderInfo } from './types.js';
|
|
4
|
+
import { registerPlatformPermitChecker, type Message } from 'zhin.js';
|
|
5
|
+
|
|
6
|
+
import { KookPermission, type KookSenderInfo } from './types.js';
|
|
8
7
|
|
|
9
8
|
const ADAPTER = 'kook';
|
|
10
9
|
|
|
@@ -13,8 +12,8 @@ export function platformPermit(perm: string): string {
|
|
|
13
12
|
}
|
|
14
13
|
|
|
15
14
|
const FACTORY_PERM_MAP: Record<string, string> = {
|
|
16
|
-
|
|
17
|
-
|
|
15
|
+
scene_admin: 'guild_admin',
|
|
16
|
+
scene_owner: 'guild_owner',
|
|
18
17
|
};
|
|
19
18
|
|
|
20
19
|
export function kookGroupPermitResolver(logicalPerm: string): string {
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { toCanonicalSegments, fromCanonicalSegments } from 'zhin.js';
|
package/src/types.ts
CHANGED
|
@@ -1,8 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* KOOK 适配器类型与配置
|
|
3
3
|
*/
|
|
4
|
-
import type
|
|
5
|
-
import type { LogLevel } from "kook-client";
|
|
4
|
+
import { type PrivateMessageEvent, type ChannelMessageEvent, type LogLevel } from 'kook-client';
|
|
6
5
|
|
|
7
6
|
export type { LogLevel };
|
|
8
7
|
|
package/lib/adapter.d.ts
DELETED
|
@@ -1,29 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* KOOK 适配器
|
|
3
|
-
*/
|
|
4
|
-
import { Adapter, Plugin } from 'zhin.js';
|
|
5
|
-
import { KookEndpoint } from "./endpoint.js";
|
|
6
|
-
import type { KookEndpointConfig } from "./types.js";
|
|
7
|
-
import type { OutboundRichSegmentPolicy } from "zhin.js";
|
|
8
|
-
export declare class KookAdapter extends Adapter<KookEndpoint> {
|
|
9
|
-
static readonly capabilities: readonly ["inbound", "outbound"];
|
|
10
|
-
static outboundRichSegmentPolicy: OutboundRichSegmentPolicy;
|
|
11
|
-
constructor(plugin: Plugin);
|
|
12
|
-
createEndpoint(config: KookEndpointConfig): KookEndpoint;
|
|
13
|
-
kickMember(endpointId: string, sceneId: string, userId: string): Promise<boolean>;
|
|
14
|
-
banMember(endpointId: string, sceneId: string, userId: string, reason?: string): Promise<boolean>;
|
|
15
|
-
unbanMember(endpointId: string, sceneId: string, userId: string): Promise<boolean>;
|
|
16
|
-
setMemberNickname(endpointId: string, sceneId: string, userId: string, nickname: string): Promise<boolean>;
|
|
17
|
-
listMembers(endpointId: string, sceneId: string): Promise<{
|
|
18
|
-
members: {
|
|
19
|
-
id: string;
|
|
20
|
-
username: string;
|
|
21
|
-
nickname: string;
|
|
22
|
-
roles: number[];
|
|
23
|
-
}[];
|
|
24
|
-
count: number;
|
|
25
|
-
}>;
|
|
26
|
-
start(): Promise<void>;
|
|
27
|
-
stop(): Promise<void>;
|
|
28
|
-
}
|
|
29
|
-
//# sourceMappingURL=adapter.d.ts.map
|
package/lib/adapter.d.ts.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"adapter.d.ts","sourceRoot":"","sources":["../src/adapter.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,OAAO,EAAiB,OAAO,EAAE,MAAM,EAAE,MAAM,SAAS,CAAC;AACzD,OAAO,EAAE,YAAY,EAAE,MAAM,eAAe,CAAC;AAC7C,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,YAAY,CAAC;AACrD,OAAO,KAAK,EAAE,yBAAyB,EAAE,MAAM,SAAS,CAAC;AAEzD,qBAAa,WAAY,SAAQ,OAAO,CAAC,YAAY,CAAC;IACpD,gBAAyB,YAAY,mCAAoC;IACzE,OAAgB,yBAAyB,EAAE,yBAAyB,CAIlE;gBAEU,MAAM,EAAE,MAAM;IAI1B,cAAc,CAAC,MAAM,EAAE,kBAAkB,GAAG,YAAY;IAMlD,UAAU,CAAC,UAAU,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM;IAM9D,SAAS,CAAC,UAAU,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,MAAM;IAM9E,WAAW,CAAC,UAAU,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM;IAM/D,iBAAiB,CAAC,UAAU,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM;IAMvF,WAAW,CAAC,UAAU,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM;;;;;;;;;IAe/C,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IAItB,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC;CAI5B"}
|
package/lib/adapter.js.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"adapter.js","sourceRoot":"","sources":["../src/adapter.ts"],"names":[],"mappings":"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"}
|