@zhin.js/adapter-kook 7.0.12 → 7.0.14
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 +47 -0
- package/README.md +2 -1
- package/adapters/kook.js +7 -2
- package/adapters/kook.ts +7 -2
- package/lib/endpoint.d.ts +3 -1
- package/lib/endpoint.js +5 -0
- package/lib/protocol.js +2 -0
- package/lib/side-event-dispatch.d.ts +3 -0
- package/lib/side-event-dispatch.js +51 -0
- package/lib/webhook.d.ts +2 -0
- package/lib/webhook.js +4 -0
- package/package.json +14 -14
- package/src/endpoint.ts +8 -1
- package/src/protocol.ts +2 -0
- package/src/side-event-dispatch.ts +55 -0
- package/src/webhook.ts +6 -0
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,52 @@
|
|
|
1
1
|
# @zhin.js/adapter-kook
|
|
2
2
|
|
|
3
|
+
## 7.0.14
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- 974772e: Replace the user-facing `Prompt` vocabulary with the `UserInteraction` authoring surface for input, confirmation, and selection. Commands and handlers now expose `interaction`; IM Runtime exposes `createInteraction`; schema-driven endpoint collection is named `SchemaInteraction`. The old prompt-named interaction types and properties are removed rather than aliased. User interactions render through one canonical Markdown and keyboard/list presentation module shared by commands and Agent `ask_user` turns.
|
|
8
|
+
|
|
9
|
+
Extract the transport-neutral interaction contract into `@zhin.js/interaction`. A discriminated `ask()` API supports text, number, confirmation, single-select, multi-select, and typed lists with structured `title`, `description`, and `tip` content. Typed `sequence()` interactions return one result object keyed by step id, render progress, and retry invalid replies without leaking invalid values to callers.
|
|
10
|
+
|
|
11
|
+
Preserve AI Markdown and card command actions through outbound publishing. QQ delivers Markdown with native command buttons; KOOK, Discord, Telegram, DingTalk, and Lark/Feishu now declare and encode their native Markdown dialects while retaining each adapter's interaction policy. Correct QQ callback button action encoding and button style mapping.
|
|
12
|
+
|
|
13
|
+
- 5969c5b: Add SideEventGateway so adapters forward notice/request/system into HandlerIndex. HandlerContext now exposes only generation-safe capabilities and prompt ports; live Endpoint escape hatches are removed.
|
|
14
|
+
- Updated dependencies [5969c5b]
|
|
15
|
+
- Updated dependencies [d336a3f]
|
|
16
|
+
- Updated dependencies [0c82a7e]
|
|
17
|
+
- Updated dependencies [b9217e4]
|
|
18
|
+
- Updated dependencies [5969c5b]
|
|
19
|
+
- Updated dependencies [5969c5b]
|
|
20
|
+
- Updated dependencies [974772e]
|
|
21
|
+
- Updated dependencies [5969c5b]
|
|
22
|
+
- Updated dependencies [5969c5b]
|
|
23
|
+
- Updated dependencies [2f786bd]
|
|
24
|
+
- Updated dependencies [63d89f9]
|
|
25
|
+
- Updated dependencies [71c7cdd]
|
|
26
|
+
- Updated dependencies [3cca0ea]
|
|
27
|
+
- Updated dependencies [1312ca0]
|
|
28
|
+
- Updated dependencies [985fa22]
|
|
29
|
+
- Updated dependencies [04b861d]
|
|
30
|
+
- Updated dependencies [a23d544]
|
|
31
|
+
- Updated dependencies [8cddabf]
|
|
32
|
+
- Updated dependencies [dbe5081]
|
|
33
|
+
- @zhin.js/im-contract@1.0.4
|
|
34
|
+
- @zhin.js/core@1.5.12
|
|
35
|
+
- @zhin.js/adapter@1.1.11
|
|
36
|
+
- @zhin.js/agent@1.1.14
|
|
37
|
+
- @zhin.js/host-http@1.0.11
|
|
38
|
+
- @zhin.js/command@1.0.15
|
|
39
|
+
- zhin.js@6.0.12
|
|
40
|
+
- @zhin.js/permission@1.0.3
|
|
41
|
+
|
|
42
|
+
## 7.0.13
|
|
43
|
+
|
|
44
|
+
### Patch Changes
|
|
45
|
+
|
|
46
|
+
- @zhin.js/core@1.5.11
|
|
47
|
+
- @zhin.js/agent@1.1.13
|
|
48
|
+
- zhin.js@6.0.11
|
|
49
|
+
|
|
3
50
|
## 7.0.12
|
|
4
51
|
|
|
5
52
|
### Patch Changes
|
package/README.md
CHANGED
|
@@ -8,6 +8,7 @@ Zhin.js KOOK(开黑啦)适配器(Plugin Runtime),默认通过 **WebSoc
|
|
|
8
8
|
- Webhook 入站(`connection: webhook` + `httpHostToken` + `verify_token`)
|
|
9
9
|
- 解析频道与私聊文本消息
|
|
10
10
|
- 出站 `send({ conversation, payload })` → KOOK KMarkdown(`kind: 'channel' | 'private'`)
|
|
11
|
+
- canonical `markdown` 段原生保留为 [KMarkdown](https://developer.kookapp.cn/doc/kmarkdown-desc)
|
|
11
12
|
- 约定式 `defineAdapter` / `definePlugin`(无需 `usePlugin`)
|
|
12
13
|
|
|
13
14
|
## 安装
|
|
@@ -96,7 +97,7 @@ platform permit checker 由 `plugin.ts` 的 generation 生命周期注册;Capa
|
|
|
96
97
|
|
|
97
98
|
## 迁移后出站能力变化
|
|
98
99
|
|
|
99
|
-
迁移到 Plugin Runtime 后,出站统一经 `messageGatewayToken`
|
|
100
|
+
迁移到 Plugin Runtime 后,出站统一经 `messageGatewayToken` 渲染并由 endpoint 编码为 KMarkdown(`sendChannelMsg` / `sendPrivateMsg`)。canonical `markdown` 保留格式;图片、视频、音频和文件仅有远程 URL 时可表示为 KMarkdown 链接。卡片消息与附件上传仍未接入统一出站通道。
|
|
100
101
|
|
|
101
102
|
## 许可证
|
|
102
103
|
|
package/adapters/kook.js
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
* Implementation lives under `src/` (endpoint / webhook / ws / protocol).
|
|
5
5
|
*/
|
|
6
6
|
import { defineAdapter } from 'zhin.js/adapter';
|
|
7
|
-
import { messageGatewayToken } from '@zhin.js/core/runtime';
|
|
7
|
+
import { messageGatewayToken, sideEventGatewayToken } from '@zhin.js/core/runtime';
|
|
8
8
|
import { httpHostToken } from '@zhin.js/host-http';
|
|
9
9
|
import { KookWebhookEndpoint, KookWebsocketEndpoint, } from "../lib/endpoint.js";
|
|
10
10
|
import { resolveKookConfig, } from "../lib/protocol.js";
|
|
@@ -12,14 +12,17 @@ import { kookRuntimeStateToken } from "../lib/kook-runtime-state.js";
|
|
|
12
12
|
export { KookWebhookEndpoint, KookWebsocketEndpoint, } from "../lib/endpoint.js";
|
|
13
13
|
export default defineAdapter({
|
|
14
14
|
capabilities: ['inbound', 'outbound'],
|
|
15
|
-
// KOOK 图片消息消费远程 URL
|
|
15
|
+
// KOOK 图片消息消费远程 URL;KMarkdown 由 endpoint codec 原生消费;
|
|
16
|
+
// 无按钮交互面,交互段降级纯文本。
|
|
16
17
|
segments: {
|
|
17
18
|
outboundMedia: ['url'],
|
|
18
19
|
interactive: 'text',
|
|
20
|
+
markdown: 'native',
|
|
19
21
|
},
|
|
20
22
|
create(context) {
|
|
21
23
|
const config = resolveKookConfig(context.config);
|
|
22
24
|
const gateway = context.use(messageGatewayToken);
|
|
25
|
+
const sideEvents = context.use(sideEventGatewayToken);
|
|
23
26
|
// 注册到插件运行时状态(kook.endpoint list 的"运行中"数据源)
|
|
24
27
|
context.use(kookRuntimeStateToken).endpoints.set(config.id, {
|
|
25
28
|
id: config.id,
|
|
@@ -29,6 +32,7 @@ export default defineAdapter({
|
|
|
29
32
|
return new KookWebhookEndpoint({
|
|
30
33
|
id: context.id,
|
|
31
34
|
gateway,
|
|
35
|
+
sideEvents,
|
|
32
36
|
http: context.use(httpHostToken),
|
|
33
37
|
config,
|
|
34
38
|
});
|
|
@@ -36,6 +40,7 @@ export default defineAdapter({
|
|
|
36
40
|
return new KookWebsocketEndpoint({
|
|
37
41
|
id: context.id,
|
|
38
42
|
gateway,
|
|
43
|
+
sideEvents,
|
|
39
44
|
config,
|
|
40
45
|
});
|
|
41
46
|
},
|
package/adapters/kook.ts
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
* Implementation lives under `src/` (endpoint / webhook / ws / protocol).
|
|
4
4
|
*/
|
|
5
5
|
import { defineAdapter } from 'zhin.js/adapter';
|
|
6
|
-
import { messageGatewayToken } from '@zhin.js/core/runtime';
|
|
6
|
+
import { messageGatewayToken, sideEventGatewayToken } from '@zhin.js/core/runtime';
|
|
7
7
|
import { httpHostToken } from '@zhin.js/host-http';
|
|
8
8
|
import {
|
|
9
9
|
KookWebhookEndpoint,
|
|
@@ -27,14 +27,17 @@ export type { CreateKookClient, KookClientTransport } from '../src/ws.js';
|
|
|
27
27
|
|
|
28
28
|
export default defineAdapter<KookAdapterConfig>({
|
|
29
29
|
capabilities: ['inbound', 'outbound'],
|
|
30
|
-
// KOOK 图片消息消费远程 URL
|
|
30
|
+
// KOOK 图片消息消费远程 URL;KMarkdown 由 endpoint codec 原生消费;
|
|
31
|
+
// 无按钮交互面,交互段降级纯文本。
|
|
31
32
|
segments: {
|
|
32
33
|
outboundMedia: ['url'],
|
|
33
34
|
interactive: 'text',
|
|
35
|
+
markdown: 'native',
|
|
34
36
|
},
|
|
35
37
|
create(context) {
|
|
36
38
|
const config = resolveKookConfig(context.config);
|
|
37
39
|
const gateway = context.use(messageGatewayToken);
|
|
40
|
+
const sideEvents = context.use(sideEventGatewayToken);
|
|
38
41
|
// 注册到插件运行时状态(kook.endpoint list 的"运行中"数据源)
|
|
39
42
|
context.use(kookRuntimeStateToken).endpoints.set(config.id, {
|
|
40
43
|
id: config.id,
|
|
@@ -44,6 +47,7 @@ export default defineAdapter<KookAdapterConfig>({
|
|
|
44
47
|
return new KookWebhookEndpoint({
|
|
45
48
|
id: context.id,
|
|
46
49
|
gateway,
|
|
50
|
+
sideEvents,
|
|
47
51
|
http: context.use(httpHostToken),
|
|
48
52
|
config,
|
|
49
53
|
});
|
|
@@ -51,6 +55,7 @@ export default defineAdapter<KookAdapterConfig>({
|
|
|
51
55
|
return new KookWebsocketEndpoint({
|
|
52
56
|
id: context.id,
|
|
53
57
|
gateway,
|
|
58
|
+
sideEvents,
|
|
54
59
|
config,
|
|
55
60
|
});
|
|
56
61
|
},
|
package/lib/endpoint.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { EndpointInstance, EndpointManagement, EndpointSendRequest } from 'zhin.js/adapter';
|
|
2
|
-
import type { MessageGateway } from '@zhin.js/core/runtime';
|
|
2
|
+
import type { MessageGateway, SideEventGateway } from '@zhin.js/core/runtime';
|
|
3
3
|
import type { HttpHost } from '@zhin.js/host-http';
|
|
4
4
|
import type { CapabilityId } from 'zhin.js';
|
|
5
5
|
import { type KookInboundMessage, type ResolvedKookWebhookConfig, type ResolvedKookWebsocketConfig } from './protocol.js';
|
|
@@ -7,6 +7,7 @@ import { type CreateKookClient, type KookClientTransport } from './ws.js';
|
|
|
7
7
|
export interface KookEndpointOptions {
|
|
8
8
|
readonly id: CapabilityId;
|
|
9
9
|
readonly gateway: MessageGateway;
|
|
10
|
+
readonly sideEvents?: SideEventGateway;
|
|
10
11
|
readonly config: ResolvedKookWebsocketConfig;
|
|
11
12
|
readonly createClient?: CreateKookClient;
|
|
12
13
|
}
|
|
@@ -42,6 +43,7 @@ export declare class KookWebsocketEndpoint implements EndpointInstance {
|
|
|
42
43
|
export interface KookWebhookEndpointOptions {
|
|
43
44
|
readonly id: CapabilityId;
|
|
44
45
|
readonly gateway: MessageGateway;
|
|
46
|
+
readonly sideEvents?: SideEventGateway;
|
|
45
47
|
readonly http: HttpHost;
|
|
46
48
|
readonly config: ResolvedKookWebhookConfig;
|
|
47
49
|
readonly createClient?: CreateKookClient;
|
package/lib/endpoint.js
CHANGED
|
@@ -3,6 +3,7 @@ import { registerKookAgentEndpoint } from './kook-agent-deps.js';
|
|
|
3
3
|
import { formatInboundContent, formatOutboundKmarkdown, isKookBotMentioned, kookInboundConversation, senderDisplayName, } from './protocol.js';
|
|
4
4
|
import { registerKookWebhookRoutes } from './webhook.js';
|
|
5
5
|
import { defaultCreateClient, defaultCreateWebhookClient, normalizeKookMessage, } from './ws.js';
|
|
6
|
+
import { receiveKookSideEvent } from './side-event-dispatch.js';
|
|
6
7
|
export class KookWebsocketEndpoint {
|
|
7
8
|
#logger;
|
|
8
9
|
#options;
|
|
@@ -143,6 +144,10 @@ export class KookWebsocketEndpoint {
|
|
|
143
144
|
if (msg)
|
|
144
145
|
this.admit(msg);
|
|
145
146
|
});
|
|
147
|
+
const receiver = client.receiver;
|
|
148
|
+
receiver?.on('event', (raw) => {
|
|
149
|
+
receiveKookSideEvent(this.#options.sideEvents, this.#options.config.id, raw, this.#logger);
|
|
150
|
+
});
|
|
146
151
|
}
|
|
147
152
|
#requireClient() {
|
|
148
153
|
if (!this.#client)
|
package/lib/protocol.js
CHANGED
|
@@ -146,6 +146,8 @@ export function formatOutboundKmarkdown(payload) {
|
|
|
146
146
|
switch (item.type) {
|
|
147
147
|
case 'text':
|
|
148
148
|
return String(data.text ?? data.content ?? '');
|
|
149
|
+
case 'markdown':
|
|
150
|
+
return String(data.content ?? data.text ?? '');
|
|
149
151
|
case 'at': {
|
|
150
152
|
const id = String(data.user_id ?? data.qq ?? data.id ?? '');
|
|
151
153
|
return id === 'all' ? '(met)all(met)' : `(met)${id}(met)`;
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
import type { SideEventGateway } from '@zhin.js/core/runtime';
|
|
2
|
+
import { type getAdapterLogger } from '@zhin.js/logger';
|
|
3
|
+
export declare function receiveKookSideEvent(sideEvents: SideEventGateway | undefined, configId: string, raw: unknown, logger: ReturnType<typeof getAdapterLogger>): boolean;
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import { buildNotice, mapNoticeParts, senderFromId, KOOK_NOTICE_PARTS_MAP } from '@zhin.js/core';
|
|
2
|
+
import { formatCompact } from '@zhin.js/logger';
|
|
3
|
+
function extractKookNoticeType(raw) {
|
|
4
|
+
if (!raw || typeof raw !== 'object')
|
|
5
|
+
return null;
|
|
6
|
+
const event = raw;
|
|
7
|
+
const extraType = event.extra?.type;
|
|
8
|
+
if (typeof extraType === 'string' && extraType)
|
|
9
|
+
return extraType;
|
|
10
|
+
if (event.channel_type === 'NOTICE' && event.type != null) {
|
|
11
|
+
return String(event.type);
|
|
12
|
+
}
|
|
13
|
+
return null;
|
|
14
|
+
}
|
|
15
|
+
export function receiveKookSideEvent(sideEvents, configId, raw, logger) {
|
|
16
|
+
if (!sideEvents)
|
|
17
|
+
return false;
|
|
18
|
+
const noticeType = extractKookNoticeType(raw);
|
|
19
|
+
if (!noticeType)
|
|
20
|
+
return false;
|
|
21
|
+
if (!Object.prototype.hasOwnProperty.call(KOOK_NOTICE_PARTS_MAP, noticeType))
|
|
22
|
+
return false;
|
|
23
|
+
const event = raw;
|
|
24
|
+
const parts = mapNoticeParts('kook', noticeType);
|
|
25
|
+
const guildId = event.extra?.guild_id;
|
|
26
|
+
const userId = event.author_id ?? event.extra?.author?.id;
|
|
27
|
+
const sceneId = guildId != null
|
|
28
|
+
? String(guildId)
|
|
29
|
+
: userId != null
|
|
30
|
+
? String(userId)
|
|
31
|
+
: configId;
|
|
32
|
+
void sideEvents.receiveNotice(buildNotice(event, {
|
|
33
|
+
$id: `kook:${noticeType}:${event.msg_timestamp ?? Date.now()}:${sceneId}:${String(userId ?? '')}`,
|
|
34
|
+
$adapter: 'kook',
|
|
35
|
+
$endpoint: configId,
|
|
36
|
+
$type: 'notice',
|
|
37
|
+
$scene_id: sceneId,
|
|
38
|
+
$scene_type: parts.scene_type,
|
|
39
|
+
$sub_type: parts.sub_type,
|
|
40
|
+
$actor: senderFromId(userId, event.extra?.author?.username),
|
|
41
|
+
$timestamp: event.msg_timestamp ?? Date.now(),
|
|
42
|
+
})).catch((err) => {
|
|
43
|
+
logger.warn(formatCompact({
|
|
44
|
+
op: 'kook_side_event_failed',
|
|
45
|
+
endpoint: configId,
|
|
46
|
+
event: noticeType,
|
|
47
|
+
error: err instanceof Error ? err.message : String(err),
|
|
48
|
+
}));
|
|
49
|
+
});
|
|
50
|
+
return true;
|
|
51
|
+
}
|
package/lib/webhook.d.ts
CHANGED
|
@@ -3,11 +3,13 @@
|
|
|
3
3
|
*/
|
|
4
4
|
import type { IncomingMessage, ServerResponse } from 'node:http';
|
|
5
5
|
import type { HttpHost, HttpRouteRegistration } from '@zhin.js/host-http';
|
|
6
|
+
import type { SideEventGateway } from '@zhin.js/core/runtime';
|
|
6
7
|
import { type KookInboundMessage, type ResolvedKookWebhookConfig } from './protocol.js';
|
|
7
8
|
export interface KookWebhookHandler {
|
|
8
9
|
readonly config: ResolvedKookWebhookConfig;
|
|
9
10
|
readonly isOpen: boolean;
|
|
10
11
|
readonly selfId?: string;
|
|
12
|
+
readonly sideEvents?: SideEventGateway;
|
|
11
13
|
admit(msg: KookInboundMessage): void;
|
|
12
14
|
checkAndRememberSn(sn: number): boolean;
|
|
13
15
|
}
|
package/lib/webhook.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { formatCompact, getLogger } from '@zhin.js/logger';
|
|
2
2
|
import { isKookWebhookChallenge, normalizeKookWebhookEvent, parseKookWebhookBody, readRequestBody, verifyKookWebhookToken, } from './protocol.js';
|
|
3
|
+
import { receiveKookSideEvent } from './side-event-dispatch.js';
|
|
3
4
|
const logger = getLogger('kook');
|
|
4
5
|
export function registerKookWebhookRoutes(http, handler) {
|
|
5
6
|
const path = handler.config.webhookPath;
|
|
@@ -67,6 +68,9 @@ export async function handleKookWebhookRequest(request, response, handler) {
|
|
|
67
68
|
}
|
|
68
69
|
}
|
|
69
70
|
function handleKookWebhookEvent(event, handler) {
|
|
71
|
+
if (receiveKookSideEvent(handler.sideEvents, handler.config.id, event, logger)) {
|
|
72
|
+
return;
|
|
73
|
+
}
|
|
70
74
|
const msg = normalizeKookWebhookEvent(event, {
|
|
71
75
|
ignore: handler.config.ignore,
|
|
72
76
|
selfId: handler.selfId,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@zhin.js/adapter-kook",
|
|
3
|
-
"version": "7.0.
|
|
3
|
+
"version": "7.0.14",
|
|
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,20 +34,20 @@
|
|
|
34
34
|
},
|
|
35
35
|
"dependencies": {
|
|
36
36
|
"kook-client": "^1.0.4",
|
|
37
|
-
"@zhin.js/adapter": "1.1.
|
|
38
|
-
"@zhin.js/core": "1.5.
|
|
39
|
-
"@zhin.js/host-http": "1.0.
|
|
40
|
-
"@zhin.js/im-contract": "1.0.
|
|
37
|
+
"@zhin.js/adapter": "1.1.11",
|
|
38
|
+
"@zhin.js/core": "1.5.12",
|
|
39
|
+
"@zhin.js/host-http": "1.0.11",
|
|
40
|
+
"@zhin.js/im-contract": "1.0.4",
|
|
41
41
|
"@zhin.js/logger": "1.0.76",
|
|
42
|
-
"@zhin.js/permission": "1.0.
|
|
42
|
+
"@zhin.js/permission": "1.0.3"
|
|
43
43
|
},
|
|
44
44
|
"peerDependencies": {
|
|
45
45
|
"zod": "^4.0.0",
|
|
46
|
-
"@zhin.js/adapter": "1.1.
|
|
47
|
-
"@zhin.js/agent": "1.1.
|
|
48
|
-
"@zhin.js/command": "1.0.
|
|
49
|
-
"@zhin.js/core": "1.5.
|
|
50
|
-
"zhin.js": "6.0.
|
|
46
|
+
"@zhin.js/adapter": "1.1.11",
|
|
47
|
+
"@zhin.js/agent": "1.1.14",
|
|
48
|
+
"@zhin.js/command": "1.0.15",
|
|
49
|
+
"@zhin.js/core": "1.5.12",
|
|
50
|
+
"zhin.js": "6.0.12"
|
|
51
51
|
},
|
|
52
52
|
"peerDependenciesMeta": {
|
|
53
53
|
"@zhin.js/agent": {
|
|
@@ -68,9 +68,9 @@
|
|
|
68
68
|
"typescript": "^6.0.3",
|
|
69
69
|
"vitest": "^4.1.10",
|
|
70
70
|
"zod": "^4.4.3",
|
|
71
|
-
"@zhin.js/agent": "1.1.
|
|
72
|
-
"@zhin.js/host-http": "1.0.
|
|
73
|
-
"zhin.js": "6.0.
|
|
71
|
+
"@zhin.js/agent": "1.1.14",
|
|
72
|
+
"@zhin.js/host-http": "1.0.11",
|
|
73
|
+
"zhin.js": "6.0.12"
|
|
74
74
|
},
|
|
75
75
|
"files": [
|
|
76
76
|
"adapters",
|
package/src/endpoint.ts
CHANGED
|
@@ -9,7 +9,7 @@ import type {
|
|
|
9
9
|
EndpointManagement,
|
|
10
10
|
EndpointSendRequest,
|
|
11
11
|
} from 'zhin.js/adapter';
|
|
12
|
-
import type { MessageGateway } from '@zhin.js/core/runtime';
|
|
12
|
+
import type { MessageGateway, SideEventGateway } from '@zhin.js/core/runtime';
|
|
13
13
|
import type { HttpHost, HttpRouteRegistration } from '@zhin.js/host-http';
|
|
14
14
|
import { formatCompact, getAdapterLogger } from '@zhin.js/logger';
|
|
15
15
|
import type { CapabilityId } from 'zhin.js';
|
|
@@ -32,10 +32,12 @@ import {
|
|
|
32
32
|
type CreateKookClient,
|
|
33
33
|
type KookClientTransport,
|
|
34
34
|
} from './ws.js';
|
|
35
|
+
import { receiveKookSideEvent } from './side-event-dispatch.js';
|
|
35
36
|
|
|
36
37
|
export interface KookEndpointOptions {
|
|
37
38
|
readonly id: CapabilityId;
|
|
38
39
|
readonly gateway: MessageGateway;
|
|
40
|
+
readonly sideEvents?: SideEventGateway;
|
|
39
41
|
readonly config: ResolvedKookWebsocketConfig;
|
|
40
42
|
readonly createClient?: CreateKookClient;
|
|
41
43
|
}
|
|
@@ -191,6 +193,10 @@ export class KookWebsocketEndpoint implements EndpointInstance {
|
|
|
191
193
|
const msg = normalizeKookMessage(raw);
|
|
192
194
|
if (msg) this.admit(msg);
|
|
193
195
|
});
|
|
196
|
+
const receiver = (client as { receiver?: { on(event: string, listener: (...args: unknown[]) => void): void } }).receiver;
|
|
197
|
+
receiver?.on('event', (raw) => {
|
|
198
|
+
receiveKookSideEvent(this.#options.sideEvents, this.#options.config.id, raw, this.#logger);
|
|
199
|
+
});
|
|
194
200
|
}
|
|
195
201
|
|
|
196
202
|
#requireClient(): KookClientTransport {
|
|
@@ -202,6 +208,7 @@ export class KookWebsocketEndpoint implements EndpointInstance {
|
|
|
202
208
|
export interface KookWebhookEndpointOptions {
|
|
203
209
|
readonly id: CapabilityId;
|
|
204
210
|
readonly gateway: MessageGateway;
|
|
211
|
+
readonly sideEvents?: SideEventGateway;
|
|
205
212
|
readonly http: HttpHost;
|
|
206
213
|
readonly config: ResolvedKookWebhookConfig;
|
|
207
214
|
readonly createClient?: CreateKookClient;
|
package/src/protocol.ts
CHANGED
|
@@ -279,6 +279,8 @@ export function formatOutboundKmarkdown(payload: unknown): string {
|
|
|
279
279
|
switch (item.type) {
|
|
280
280
|
case 'text':
|
|
281
281
|
return String(data.text ?? data.content ?? '');
|
|
282
|
+
case 'markdown':
|
|
283
|
+
return String(data.content ?? data.text ?? '');
|
|
282
284
|
case 'at': {
|
|
283
285
|
const id = String(data.user_id ?? data.qq ?? data.id ?? '');
|
|
284
286
|
return id === 'all' ? '(met)all(met)' : `(met)${id}(met)`;
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import { buildNotice, mapNoticeParts, senderFromId, KOOK_NOTICE_PARTS_MAP } from '@zhin.js/core';
|
|
2
|
+
import type { SideEventGateway } from '@zhin.js/core/runtime';
|
|
3
|
+
import { formatCompact, type getAdapterLogger } from '@zhin.js/logger';
|
|
4
|
+
import type { KookWebhookEventData } from './protocol.js';
|
|
5
|
+
|
|
6
|
+
function extractKookNoticeType(raw: unknown): string | null {
|
|
7
|
+
if (!raw || typeof raw !== 'object') return null;
|
|
8
|
+
const event = raw as KookWebhookEventData & { extra?: { type?: string } };
|
|
9
|
+
const extraType = event.extra?.type;
|
|
10
|
+
if (typeof extraType === 'string' && extraType) return extraType;
|
|
11
|
+
if (event.channel_type === 'NOTICE' && event.type != null) {
|
|
12
|
+
return String(event.type);
|
|
13
|
+
}
|
|
14
|
+
return null;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export function receiveKookSideEvent(
|
|
18
|
+
sideEvents: SideEventGateway | undefined,
|
|
19
|
+
configId: string,
|
|
20
|
+
raw: unknown,
|
|
21
|
+
logger: ReturnType<typeof getAdapterLogger>,
|
|
22
|
+
): boolean {
|
|
23
|
+
if (!sideEvents) return false;
|
|
24
|
+
const noticeType = extractKookNoticeType(raw);
|
|
25
|
+
if (!noticeType) return false;
|
|
26
|
+
if (!Object.prototype.hasOwnProperty.call(KOOK_NOTICE_PARTS_MAP, noticeType)) return false;
|
|
27
|
+
const event = raw as KookWebhookEventData;
|
|
28
|
+
const parts = mapNoticeParts('kook', noticeType);
|
|
29
|
+
const guildId = event.extra?.guild_id;
|
|
30
|
+
const userId = event.author_id ?? event.extra?.author?.id;
|
|
31
|
+
const sceneId = guildId != null
|
|
32
|
+
? String(guildId)
|
|
33
|
+
: userId != null
|
|
34
|
+
? String(userId)
|
|
35
|
+
: configId;
|
|
36
|
+
void sideEvents.receiveNotice(buildNotice(event, {
|
|
37
|
+
$id: `kook:${noticeType}:${event.msg_timestamp ?? Date.now()}:${sceneId}:${String(userId ?? '')}`,
|
|
38
|
+
$adapter: 'kook' as never,
|
|
39
|
+
$endpoint: configId,
|
|
40
|
+
$type: 'notice',
|
|
41
|
+
$scene_id: sceneId,
|
|
42
|
+
$scene_type: parts.scene_type,
|
|
43
|
+
$sub_type: parts.sub_type,
|
|
44
|
+
$actor: senderFromId(userId, event.extra?.author?.username),
|
|
45
|
+
$timestamp: event.msg_timestamp ?? Date.now(),
|
|
46
|
+
})).catch((err) => {
|
|
47
|
+
logger.warn(formatCompact({
|
|
48
|
+
op: 'kook_side_event_failed',
|
|
49
|
+
endpoint: configId,
|
|
50
|
+
event: noticeType,
|
|
51
|
+
error: err instanceof Error ? err.message : String(err),
|
|
52
|
+
}));
|
|
53
|
+
});
|
|
54
|
+
return true;
|
|
55
|
+
}
|
package/src/webhook.ts
CHANGED
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
*/
|
|
4
4
|
import type { IncomingMessage, ServerResponse } from 'node:http';
|
|
5
5
|
import type { HttpHost, HttpRouteRegistration } from '@zhin.js/host-http';
|
|
6
|
+
import type { SideEventGateway } from '@zhin.js/core/runtime';
|
|
6
7
|
import { formatCompact, getLogger } from '@zhin.js/logger';
|
|
7
8
|
import {
|
|
8
9
|
isKookWebhookChallenge,
|
|
@@ -15,6 +16,7 @@ import {
|
|
|
15
16
|
type KookWebhookFrame,
|
|
16
17
|
type ResolvedKookWebhookConfig,
|
|
17
18
|
} from './protocol.js';
|
|
19
|
+
import { receiveKookSideEvent } from './side-event-dispatch.js';
|
|
18
20
|
|
|
19
21
|
const logger = getLogger('kook');
|
|
20
22
|
|
|
@@ -22,6 +24,7 @@ export interface KookWebhookHandler {
|
|
|
22
24
|
readonly config: ResolvedKookWebhookConfig;
|
|
23
25
|
readonly isOpen: boolean;
|
|
24
26
|
readonly selfId?: string;
|
|
27
|
+
readonly sideEvents?: SideEventGateway;
|
|
25
28
|
admit(msg: KookInboundMessage): void;
|
|
26
29
|
checkAndRememberSn(sn: number): boolean;
|
|
27
30
|
}
|
|
@@ -108,6 +111,9 @@ function handleKookWebhookEvent(
|
|
|
108
111
|
event: KookWebhookEventData,
|
|
109
112
|
handler: KookWebhookHandler,
|
|
110
113
|
): void {
|
|
114
|
+
if (receiveKookSideEvent(handler.sideEvents, handler.config.id, event, logger)) {
|
|
115
|
+
return;
|
|
116
|
+
}
|
|
111
117
|
const msg = normalizeKookWebhookEvent(event, {
|
|
112
118
|
ignore: handler.config.ignore,
|
|
113
119
|
selfId: handler.selfId,
|