@zhin.js/adapter-kook 4.0.2 → 5.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 +27 -0
- package/dist/index.js +2 -2
- package/lib/adapter.d.ts +4 -1
- package/lib/adapter.d.ts.map +1 -1
- package/lib/adapter.js +8 -2
- package/lib/adapter.js.map +1 -1
- package/lib/endpoint.d.ts +1 -1
- package/lib/endpoint.d.ts.map +1 -1
- package/lib/endpoint.js +17 -4
- package/lib/endpoint.js.map +1 -1
- package/lib/index.js +3 -3
- package/lib/kook-side-events.d.ts +4 -1
- package/lib/kook-side-events.d.ts.map +1 -1
- package/lib/kook-side-events.js +43 -1
- package/lib/kook-side-events.js.map +1 -1
- package/lib/outbound-keyboard.d.ts +13 -0
- package/lib/outbound-keyboard.d.ts.map +1 -0
- package/lib/outbound-keyboard.js +63 -0
- package/lib/outbound-keyboard.js.map +1 -0
- package/lib/outbound-media.d.ts +1 -1
- package/lib/outbound-media.d.ts.map +1 -1
- package/lib/outbound-media.js +4 -48
- package/lib/outbound-media.js.map +1 -1
- package/lib/outbound-sendable.d.ts +2 -1
- package/lib/outbound-sendable.d.ts.map +1 -1
- package/lib/outbound-sendable.js +9 -0
- package/lib/outbound-sendable.js.map +1 -1
- package/lib/platform-permit.js +2 -2
- package/lib/segment-mapper.d.ts +2 -0
- package/lib/segment-mapper.d.ts.map +1 -0
- package/lib/segment-mapper.js +2 -0
- package/lib/segment-mapper.js.map +1 -0
- package/package.json +15 -15
- package/skills/kook/PERMITS.md +1 -1
- package/src/adapter.ts +9 -2
- package/src/endpoint.ts +21 -4
- package/src/index.ts +4 -4
- package/src/kook-side-events.ts +49 -1
- package/src/outbound-keyboard.ts +78 -0
- package/src/outbound-media.ts +11 -55
- package/src/outbound-sendable.ts +13 -1
- package/src/platform-permit.ts +2 -2
- package/src/segment-mapper.ts +1 -0
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"outbound-media.d.ts","sourceRoot":"","sources":["../src/outbound-media.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,OAAO,KAAK,EAAE,cAAc,EAAkB,WAAW,EAAE,MAAM,SAAS,CAAC;
|
|
1
|
+
{"version":3,"file":"outbound-media.d.ts","sourceRoot":"","sources":["../src/outbound-media.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,OAAO,KAAK,EAAE,cAAc,EAAkB,WAAW,EAAE,MAAM,SAAS,CAAC;AAS3E,MAAM,WAAW,iBAAiB;IAChC,WAAW,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC;CACrD;AA0CD,sCAAsC;AACtC,wBAAsB,wBAAwB,CAC5C,QAAQ,EAAE,iBAAiB,EAC3B,OAAO,EAAE,WAAW,GACnB,OAAO,CAAC,cAAc,EAAE,CAAC,CAM3B"}
|
package/lib/outbound-media.js
CHANGED
|
@@ -1,51 +1,7 @@
|
|
|
1
|
-
|
|
1
|
+
import { asMessageElements, extractBase64Buffer, isMediaSegmentType, isRemoteUrl, resolveLocalMediaPath, } from "zhin.js";
|
|
2
2
|
function isMessageSegment(seg) {
|
|
3
3
|
return typeof seg.type === "string";
|
|
4
4
|
}
|
|
5
|
-
function isRemoteUrl(value) {
|
|
6
|
-
return /^https?:\/\//i.test(value);
|
|
7
|
-
}
|
|
8
|
-
function asSegments(content) {
|
|
9
|
-
if (typeof content === "string") {
|
|
10
|
-
return [{ type: "text", data: { text: content } }];
|
|
11
|
-
}
|
|
12
|
-
if (!Array.isArray(content)) {
|
|
13
|
-
return [content];
|
|
14
|
-
}
|
|
15
|
-
return content.map((item) => typeof item === "string" ? { type: "text", data: { text: item } } : item);
|
|
16
|
-
}
|
|
17
|
-
function extractBase64Buffer(data) {
|
|
18
|
-
const url = typeof data.url === "string" ? data.url : undefined;
|
|
19
|
-
const file = typeof data.file === "string" ? data.file : undefined;
|
|
20
|
-
const base64 = typeof data.base64 === "string" ? data.base64 : undefined;
|
|
21
|
-
if (url?.startsWith("base64://")) {
|
|
22
|
-
return Buffer.from(url.slice(9), "base64");
|
|
23
|
-
}
|
|
24
|
-
if (url && /^data:[^/]+\/[^;]+;base64,/i.test(url)) {
|
|
25
|
-
return Buffer.from(url.replace(/^data:[^/]+\/[^;]+;base64,/, ""), "base64");
|
|
26
|
-
}
|
|
27
|
-
if (file?.startsWith("base64://")) {
|
|
28
|
-
return Buffer.from(file.slice(9), "base64");
|
|
29
|
-
}
|
|
30
|
-
if (base64) {
|
|
31
|
-
const raw = base64.startsWith("base64://") ? base64.slice(9) : base64;
|
|
32
|
-
return Buffer.from(raw, "base64");
|
|
33
|
-
}
|
|
34
|
-
return undefined;
|
|
35
|
-
}
|
|
36
|
-
function resolveLocalFile(data) {
|
|
37
|
-
const url = typeof data.url === "string" ? data.url : undefined;
|
|
38
|
-
const file = typeof data.file === "string" ? data.file : undefined;
|
|
39
|
-
const candidate = file ?? url;
|
|
40
|
-
if (!candidate || isRemoteUrl(candidate))
|
|
41
|
-
return undefined;
|
|
42
|
-
if (candidate.startsWith("base64://") || /^data:/.test(candidate))
|
|
43
|
-
return undefined;
|
|
44
|
-
if (candidate.startsWith("file://") || candidate.startsWith("/") || !candidate.includes("://")) {
|
|
45
|
-
return candidate;
|
|
46
|
-
}
|
|
47
|
-
return undefined;
|
|
48
|
-
}
|
|
49
5
|
function withUploadedUrl(seg, uploadedUrl) {
|
|
50
6
|
const data = { ...seg.data };
|
|
51
7
|
data.url = uploadedUrl;
|
|
@@ -54,7 +10,7 @@ function withUploadedUrl(seg, uploadedUrl) {
|
|
|
54
10
|
return { type: seg.type, data };
|
|
55
11
|
}
|
|
56
12
|
async function resolveMediaSegment(uploader, seg) {
|
|
57
|
-
if (!
|
|
13
|
+
if (!isMediaSegmentType(seg.type))
|
|
58
14
|
return seg;
|
|
59
15
|
const data = seg.data;
|
|
60
16
|
const url = typeof data.url === "string" ? data.url : undefined;
|
|
@@ -67,7 +23,7 @@ async function resolveMediaSegment(uploader, seg) {
|
|
|
67
23
|
const uploaded = await uploader.uploadMedia(buffer);
|
|
68
24
|
return withUploadedUrl(seg, uploaded);
|
|
69
25
|
}
|
|
70
|
-
const local =
|
|
26
|
+
const local = resolveLocalMediaPath(data);
|
|
71
27
|
if (local) {
|
|
72
28
|
const uploaded = await uploader.uploadMedia(local);
|
|
73
29
|
return withUploadedUrl(seg, uploaded);
|
|
@@ -76,7 +32,7 @@ async function resolveMediaSegment(uploader, seg) {
|
|
|
76
32
|
}
|
|
77
33
|
/** 将 base64 / 本地媒体上传为 KOOK 可访问 URL */
|
|
78
34
|
export async function materializeOutboundMedia(uploader, content) {
|
|
79
|
-
const segments =
|
|
35
|
+
const segments = asMessageElements(content);
|
|
80
36
|
return Promise.all(segments.map(async (item) => {
|
|
81
37
|
if (!isMessageSegment(item))
|
|
82
38
|
return item;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"outbound-media.js","sourceRoot":"","sources":["../src/outbound-media.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"outbound-media.js","sourceRoot":"","sources":["../src/outbound-media.ts"],"names":[],"mappings":"AAIA,OAAO,EACL,iBAAiB,EACjB,mBAAmB,EACnB,kBAAkB,EAClB,WAAW,EACX,qBAAqB,GACtB,MAAM,SAAS,CAAC;AAMjB,SAAS,gBAAgB,CAAC,GAAmB;IAC3C,OAAO,OAAO,GAAG,CAAC,IAAI,KAAK,QAAQ,CAAC;AACtC,CAAC;AAED,SAAS,eAAe,CAAC,GAAmB,EAAE,WAAmB;IAC/D,MAAM,IAAI,GAAG,EAAE,GAAG,GAAG,CAAC,IAAI,EAA6B,CAAC;IACxD,IAAI,CAAC,GAAG,GAAG,WAAW,CAAC;IACvB,IAAI,CAAC,IAAI,GAAG,WAAW,CAAC;IACxB,OAAO,IAAI,CAAC,MAAM,CAAC;IACnB,OAAO,EAAE,IAAI,EAAE,GAAG,CAAC,IAAI,EAAE,IAAI,EAAoB,CAAC;AACpD,CAAC;AAED,KAAK,UAAU,mBAAmB,CAChC,QAA2B,EAC3B,GAAmB;IAEnB,IAAI,CAAC,kBAAkB,CAAC,GAAG,CAAC,IAAI,CAAC;QAAE,OAAO,GAAG,CAAC;IAE9C,MAAM,IAAI,GAAG,GAAG,CAAC,IAA+B,CAAC;IACjD,MAAM,GAAG,GAAG,OAAO,IAAI,CAAC,GAAG,KAAK,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,SAAS,CAAC;IAChE,MAAM,IAAI,GAAG,OAAO,IAAI,CAAC,IAAI,KAAK,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,SAAS,CAAC;IACnE,IAAI,CAAC,GAAG,IAAI,WAAW,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,IAAI,IAAI,WAAW,CAAC,IAAI,CAAC,CAAC,EAAE,CAAC;QAC7D,OAAO,GAAG,CAAC;IACb,CAAC;IAED,MAAM,MAAM,GAAG,mBAAmB,CAAC,IAAI,CAAC,CAAC;IACzC,IAAI,MAAM,EAAE,CAAC;QACX,MAAM,QAAQ,GAAG,MAAM,QAAQ,CAAC,WAAW,CAAC,MAAM,CAAC,CAAC;QACpD,OAAO,eAAe,CAAC,GAAG,EAAE,QAAQ,CAAC,CAAC;IACxC,CAAC;IAED,MAAM,KAAK,GAAG,qBAAqB,CAAC,IAAI,CAAC,CAAC;IAC1C,IAAI,KAAK,EAAE,CAAC;QACV,MAAM,QAAQ,GAAG,MAAM,QAAQ,CAAC,WAAW,CAAC,KAAK,CAAC,CAAC;QACnD,OAAO,eAAe,CAAC,GAAG,EAAE,QAAQ,CAAC,CAAC;IACxC,CAAC;IAED,OAAO,GAAG,CAAC;AACb,CAAC;AAED,sCAAsC;AACtC,MAAM,CAAC,KAAK,UAAU,wBAAwB,CAC5C,QAA2B,EAC3B,OAAoB;IAEpB,MAAM,QAAQ,GAAG,iBAAiB,CAAC,OAAO,CAAC,CAAC;IAC5C,OAAO,OAAO,CAAC,GAAG,CAAC,QAAQ,CAAC,GAAG,CAAC,KAAK,EAAE,IAAI,EAAE,EAAE;QAC7C,IAAI,CAAC,gBAAgB,CAAC,IAAI,CAAC;YAAE,OAAO,IAAI,CAAC;QACzC,OAAO,mBAAmB,CAAC,QAAQ,EAAE,IAAI,CAAC,CAAC;IAC7C,CAAC,CAAC,CAAC,CAAC;AACN,CAAC"}
|
|
@@ -4,8 +4,9 @@
|
|
|
4
4
|
*/
|
|
5
5
|
import { type MessageSegment as KookMessageSegment } from "kook-client";
|
|
6
6
|
import type { MessageElement } from "zhin.js";
|
|
7
|
+
export type KookSendable = string | KookMessageSegment[] | Array<Record<string, unknown>>;
|
|
7
8
|
/**
|
|
8
9
|
* 单张图片(可带 reply)→ kook image 消息段(type=2)
|
|
9
10
|
*/
|
|
10
|
-
export declare function convertToKookSendable(elements: MessageElement[], formatText: (content: MessageElement[]) => string):
|
|
11
|
+
export declare function convertToKookSendable(elements: MessageElement[], formatText: (content: MessageElement[]) => string): KookSendable;
|
|
11
12
|
//# sourceMappingURL=outbound-sendable.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"outbound-sendable.d.ts","sourceRoot":"","sources":["../src/outbound-sendable.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,OAAO,EAAW,KAAK,cAAc,IAAI,kBAAkB,EAAE,MAAM,aAAa,CAAC;AACjF,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,SAAS,CAAC;
|
|
1
|
+
{"version":3,"file":"outbound-sendable.d.ts","sourceRoot":"","sources":["../src/outbound-sendable.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,OAAO,EAAW,KAAK,cAAc,IAAI,kBAAkB,EAAE,MAAM,aAAa,CAAC;AACjF,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,SAAS,CAAC;AAG9C,MAAM,MAAM,YAAY,GAAG,MAAM,GAAG,kBAAkB,EAAE,GAAG,KAAK,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC;AAM1F;;GAEG;AACH,wBAAgB,qBAAqB,CACnC,QAAQ,EAAE,cAAc,EAAE,EAC1B,UAAU,EAAE,CAAC,OAAO,EAAE,cAAc,EAAE,KAAK,MAAM,GAChD,YAAY,CAoDd"}
|
package/lib/outbound-sendable.js
CHANGED
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
* 图片/音视频须用 segment 发送对应 msg_type,不能写成 Markdown 。
|
|
4
4
|
*/
|
|
5
5
|
import { segment } from "kook-client";
|
|
6
|
+
import { coreKeyboardToKookCard, findKeyboardSegment } from "./outbound-keyboard.js";
|
|
6
7
|
function mediaUrl(data) {
|
|
7
8
|
return String(data.url ?? data.file ?? "");
|
|
8
9
|
}
|
|
@@ -10,6 +11,14 @@ function mediaUrl(data) {
|
|
|
10
11
|
* 单张图片(可带 reply)→ kook image 消息段(type=2)
|
|
11
12
|
*/
|
|
12
13
|
export function convertToKookSendable(elements, formatText) {
|
|
14
|
+
const keyboardBundle = findKeyboardSegment(elements);
|
|
15
|
+
if (keyboardBundle) {
|
|
16
|
+
const { keyboard, rest } = keyboardBundle;
|
|
17
|
+
const nonReplyRest = rest.filter((el) => el.type !== "reply");
|
|
18
|
+
if (nonReplyRest.every((el) => el.type === "text" || el.type === "reply")) {
|
|
19
|
+
return coreKeyboardToKookCard(keyboard.data, nonReplyRest.filter((el) => el.type === "text"));
|
|
20
|
+
}
|
|
21
|
+
}
|
|
13
22
|
const replies = [];
|
|
14
23
|
const images = [];
|
|
15
24
|
const videos = [];
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"outbound-sendable.js","sourceRoot":"","sources":["../src/outbound-sendable.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,OAAO,EAAE,OAAO,EAA6C,MAAM,aAAa,CAAC;
|
|
1
|
+
{"version":3,"file":"outbound-sendable.js","sourceRoot":"","sources":["../src/outbound-sendable.ts"],"names":[],"mappings":"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"}
|
package/lib/platform-permit.js
CHANGED
|
@@ -5,8 +5,8 @@ export function platformPermit(perm) {
|
|
|
5
5
|
return `platform(${ADAPTER},${perm})`;
|
|
6
6
|
}
|
|
7
7
|
const FACTORY_PERM_MAP = {
|
|
8
|
-
|
|
9
|
-
|
|
8
|
+
scene_admin: 'guild_admin',
|
|
9
|
+
scene_owner: 'guild_owner',
|
|
10
10
|
};
|
|
11
11
|
export function kookGroupPermitResolver(logicalPerm) {
|
|
12
12
|
return platformPermit(FACTORY_PERM_MAP[logicalPerm] ?? logicalPerm);
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"segment-mapper.d.ts","sourceRoot":"","sources":["../src/segment-mapper.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,mBAAmB,EAAE,qBAAqB,EAAE,MAAM,SAAS,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"segment-mapper.js","sourceRoot":"","sources":["../src/segment-mapper.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,mBAAmB,EAAE,qBAAqB,EAAE,MAAM,SAAS,CAAC"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@zhin.js/adapter-kook",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "5.0.1",
|
|
4
4
|
"description": "Zhin.js adapter for KOOK (开黑啦)",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./lib/index.js",
|
|
@@ -38,23 +38,23 @@
|
|
|
38
38
|
"devDependencies": {
|
|
39
39
|
"@types/react": "^19.2.17",
|
|
40
40
|
"@types/react-dom": "^19.2.3",
|
|
41
|
-
"lucide-react": "^1.
|
|
41
|
+
"lucide-react": "^1.22.0",
|
|
42
42
|
"typescript": "^6.0.3",
|
|
43
|
-
"@zhin.js/cli": "1.0.
|
|
44
|
-
"@zhin.js/client": "2.0.
|
|
45
|
-
"@zhin.js/contract": "1.0.
|
|
46
|
-
"@zhin.js/host-api": "2.0.
|
|
47
|
-
"@zhin.js/host-router": "2.0.
|
|
48
|
-
"
|
|
49
|
-
"zhin.js": "
|
|
43
|
+
"@zhin.js/cli": "1.0.92",
|
|
44
|
+
"@zhin.js/client": "2.0.4",
|
|
45
|
+
"@zhin.js/contract": "1.0.2",
|
|
46
|
+
"@zhin.js/host-api": "2.0.4",
|
|
47
|
+
"@zhin.js/host-router": "2.0.2",
|
|
48
|
+
"zhin.js": "4.1.1",
|
|
49
|
+
"@zhin.js/logger": "1.0.73"
|
|
50
50
|
},
|
|
51
51
|
"peerDependencies": {
|
|
52
|
-
"@zhin.js/client": "2.0.
|
|
53
|
-
"@zhin.js/host-api": "2.0.
|
|
54
|
-
"@zhin.js/contract": "1.0.
|
|
55
|
-
"@zhin.js/
|
|
56
|
-
"@zhin.js/
|
|
57
|
-
"zhin.js": "4.
|
|
52
|
+
"@zhin.js/client": "2.0.4",
|
|
53
|
+
"@zhin.js/host-api": "2.0.4",
|
|
54
|
+
"@zhin.js/contract": "1.0.2",
|
|
55
|
+
"@zhin.js/logger": "1.0.73",
|
|
56
|
+
"@zhin.js/host-router": "2.0.2",
|
|
57
|
+
"zhin.js": "4.1.1"
|
|
58
58
|
},
|
|
59
59
|
"peerDependenciesMeta": {
|
|
60
60
|
"@zhin.js/client": {
|
package/skills/kook/PERMITS.md
CHANGED
package/src/adapter.ts
CHANGED
|
@@ -4,9 +4,16 @@
|
|
|
4
4
|
import { formatCompact, Adapter, Plugin } from 'zhin.js';
|
|
5
5
|
import { KookEndpoint } from "./endpoint.js";
|
|
6
6
|
import type { KookEndpointConfig } from "./types.js";
|
|
7
|
+
import type { OutboundRichSegmentPolicy } from "zhin.js";
|
|
7
8
|
|
|
8
9
|
export class KookAdapter extends Adapter<KookEndpoint> {
|
|
9
10
|
static override readonly capabilities = ['inbound', 'outbound'] as const;
|
|
11
|
+
static override outboundRichSegmentPolicy: OutboundRichSegmentPolicy = {
|
|
12
|
+
qrcode: 'image',
|
|
13
|
+
html: 'image',
|
|
14
|
+
markdown: 'origin',
|
|
15
|
+
};
|
|
16
|
+
static override interactivePolicy = 'native' as const;
|
|
10
17
|
|
|
11
18
|
constructor(plugin: Plugin) {
|
|
12
19
|
super(plugin, "kook", []);
|
|
@@ -16,9 +23,9 @@ export class KookAdapter extends Adapter<KookEndpoint> {
|
|
|
16
23
|
return new KookEndpoint(this, config);
|
|
17
24
|
}
|
|
18
25
|
|
|
19
|
-
// ──
|
|
26
|
+
// ── ISceneManagement 标准群管方法 ──────────────────────────────────
|
|
20
27
|
|
|
21
|
-
async
|
|
28
|
+
async removeMember(endpointId: string, sceneId: string, userId: string) {
|
|
22
29
|
const endpoint = this.endpoints.get(endpointId);
|
|
23
30
|
if (!endpoint) throw new Error(`Endpoint ${endpointId} 不存在`);
|
|
24
31
|
return endpoint.kickUser(sceneId, userId);
|
package/src/endpoint.ts
CHANGED
|
@@ -20,8 +20,8 @@ import {
|
|
|
20
20
|
SendContent,
|
|
21
21
|
MessageElement,
|
|
22
22
|
segment,
|
|
23
|
-
|
|
24
|
-
} from
|
|
23
|
+
expandInteractiveSegmentsInContent,
|
|
24
|
+
MessageType,} from 'zhin.js';
|
|
25
25
|
import type { KookEndpointConfig, KookSenderInfo, KookRawMessage } from "./types.js";
|
|
26
26
|
import { KookPermission } from "./types.js";
|
|
27
27
|
import type { KookAdapter } from "./adapter.js";
|
|
@@ -31,6 +31,8 @@ import {
|
|
|
31
31
|
enrichKookGatewayForPlugins,
|
|
32
32
|
formatKookNotice,
|
|
33
33
|
formatKookNoticeLog,
|
|
34
|
+
formatKookButtonClickMessage,
|
|
35
|
+
isKookButtonClickEvent,
|
|
34
36
|
isKookNoticeGatewayEvent,
|
|
35
37
|
resolveKookSideEventDedupeKey,
|
|
36
38
|
type KookGatewayEvent,
|
|
@@ -55,6 +57,7 @@ import {
|
|
|
55
57
|
import { materializeOutboundMedia } from "./outbound-media.js";
|
|
56
58
|
import { uploadKookAsset } from "./kook-asset-upload.js";
|
|
57
59
|
import { convertToKookSendable } from "./outbound-sendable.js";
|
|
60
|
+
import { fromCanonicalSegments, toCanonicalSegments } from './segment-mapper.js';
|
|
58
61
|
|
|
59
62
|
export class KookEndpoint extends Client implements Endpoint<KookEndpointConfig, KookRawMessage> {
|
|
60
63
|
$connected: boolean = false;
|
|
@@ -111,6 +114,17 @@ export class KookEndpoint extends Client implements Endpoint<KookEndpointConfig,
|
|
|
111
114
|
const enriched = enrichKookGatewayForPlugins(raw);
|
|
112
115
|
EventEmitter.prototype.emit.call(this.adapter, "kook.gateway", enriched);
|
|
113
116
|
|
|
117
|
+
if (isKookButtonClickEvent(raw)) {
|
|
118
|
+
const dedupeKey = resolveKookSideEventDedupeKey(raw, "gateway");
|
|
119
|
+
if (!this.inboundDeduper.shouldProcess(dedupeKey)) return;
|
|
120
|
+
const message = formatKookButtonClickMessage(raw, this.$id);
|
|
121
|
+
this.adapter.emit("message.receive", message);
|
|
122
|
+
this.pluginLogger.debug(
|
|
123
|
+
`KOOK 按钮点击 ${message.$channel.type}(${message.$channel.id}): ${message.$raw}`,
|
|
124
|
+
);
|
|
125
|
+
return;
|
|
126
|
+
}
|
|
127
|
+
|
|
114
128
|
if (!isKookNoticeGatewayEvent(raw)) return;
|
|
115
129
|
|
|
116
130
|
const dedupeKey = resolveKookSideEventDedupeKey(raw, "notice");
|
|
@@ -222,7 +236,7 @@ export class KookEndpoint extends Client implements Endpoint<KookEndpointConfig,
|
|
|
222
236
|
...(guildId ? { guild_id: guildId } : {}),
|
|
223
237
|
},
|
|
224
238
|
|
|
225
|
-
$content: this.parseMessageContent(msg.message),
|
|
239
|
+
$content: toCanonicalSegments(this.parseMessageContent(msg.message)),
|
|
226
240
|
$raw: msg.raw_message,
|
|
227
241
|
$timestamp: msg.timestamp,
|
|
228
242
|
|
|
@@ -708,7 +722,10 @@ export class KookEndpoint extends Client implements Endpoint<KookEndpointConfig,
|
|
|
708
722
|
try {
|
|
709
723
|
const { id, type, content } = options;
|
|
710
724
|
|
|
711
|
-
const
|
|
725
|
+
const expanded = expandInteractiveSegmentsInContent(content);
|
|
726
|
+
const canonical = toCanonicalSegments(Array.isArray(expanded) ? expanded : [expanded]);
|
|
727
|
+
const wire = fromCanonicalSegments(canonical);
|
|
728
|
+
const elements = await materializeOutboundMedia(this, wire);
|
|
712
729
|
const kookContent = convertToKookSendable(
|
|
713
730
|
elements,
|
|
714
731
|
(els) => this.convertToKookFormat(els),
|
package/src/index.ts
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
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
7
|
import { kookGroupPermitResolver, platformPermit, registerKookPlatformPermitChecker } from "./platform-permit.js";
|
|
8
8
|
import { PageManager } from "@zhin.js/host-api";
|
|
@@ -44,12 +44,12 @@ provide({
|
|
|
44
44
|
useContext('tool', 'kook', (toolService: ToolFeature, kook: KookAdapter) => {
|
|
45
45
|
const disposers: (() => void)[] = [];
|
|
46
46
|
disposers.push(registerKookPlatformPermitChecker());
|
|
47
|
-
const
|
|
48
|
-
kook as unknown as
|
|
47
|
+
const sceneTools = createSceneManagementTools(
|
|
48
|
+
kook as unknown as ISceneManagement,
|
|
49
49
|
'kook',
|
|
50
50
|
{ permitResolver: kookGroupPermitResolver, registerChecker: false },
|
|
51
51
|
);
|
|
52
|
-
disposers.push(...
|
|
52
|
+
disposers.push(...sceneTools.map(t => toolService.addTool(t, plugin.name)));
|
|
53
53
|
|
|
54
54
|
disposers.push(toolService.addTool({
|
|
55
55
|
name: 'kook_grant_role',
|
package/src/kook-side-events.ts
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
* KOOK Gateway 非聊天入站:系统消息 (type=255) / BROADCAST → Notice
|
|
3
3
|
* @see https://developer.kookapp.cn/doc/event/event-introduction
|
|
4
4
|
*/
|
|
5
|
-
import { Notice, formatCompact, type NoticeType } from "zhin.js";
|
|
5
|
+
import { Notice, formatCompact, Message, type NoticeType } from "zhin.js";
|
|
6
6
|
|
|
7
7
|
/** KOOK WebSocket s=0 时 d 字段(kook-client Receiver 入参) */
|
|
8
8
|
export interface KookGatewayEvent {
|
|
@@ -200,3 +200,51 @@ export function formatKookNoticeLog(notice: ReturnType<typeof formatKookNotice>)
|
|
|
200
200
|
endpoint: notice.$endpoint,
|
|
201
201
|
});
|
|
202
202
|
}
|
|
203
|
+
|
|
204
|
+
/** KOOK 卡片按钮点击(type=255, extra.type=message_btn_click) */
|
|
205
|
+
export function isKookButtonClickEvent(event: KookGatewayEvent): boolean {
|
|
206
|
+
return event.type === SYSTEM_MESSAGE_TYPE && event.extra?.type === 'message_btn_click';
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
export function formatKookButtonClickMessage(
|
|
210
|
+
event: KookGatewayEvent,
|
|
211
|
+
endpointName: string,
|
|
212
|
+
): ReturnType<typeof Message.from<KookGatewayEvent>> {
|
|
213
|
+
const extra = event.extra ?? {};
|
|
214
|
+
const body = extra.body ?? {};
|
|
215
|
+
const payload = String(body.value ?? '');
|
|
216
|
+
const userInfo = body.user_info as { id?: string; username?: string; nickname?: string } | undefined;
|
|
217
|
+
const userId = String(body.user_id ?? userInfo?.id ?? event.author_id ?? '');
|
|
218
|
+
const userName = userInfo?.nickname ?? userInfo?.username ?? userId;
|
|
219
|
+
const sourceMessageId = String(body.msg_id ?? '');
|
|
220
|
+
|
|
221
|
+
let channelType: 'private' | 'channel' = 'private';
|
|
222
|
+
let channelId = String(body.target_id ?? event.target_id ?? userId);
|
|
223
|
+
if (body.channel_type === 'GROUP' || event.channel_type === 'GROUP') {
|
|
224
|
+
channelType = 'channel';
|
|
225
|
+
channelId = String(body.target_id ?? event.target_id ?? channelId);
|
|
226
|
+
} else if (body.channel_type === 'PERSON' || event.channel_type === 'PERSON') {
|
|
227
|
+
channelType = 'private';
|
|
228
|
+
channelId = String(body.target_id ?? event.target_id ?? userId);
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
return Message.from(event, {
|
|
232
|
+
$id: event.msg_id ?? `btn:${Date.now()}`,
|
|
233
|
+
$adapter: 'kook',
|
|
234
|
+
$endpoint: endpointName,
|
|
235
|
+
$sender: { id: userId, name: userName },
|
|
236
|
+
$channel: { id: channelId, type: channelType },
|
|
237
|
+
$content: [{
|
|
238
|
+
type: 'action',
|
|
239
|
+
data: {
|
|
240
|
+
id: payload,
|
|
241
|
+
payload,
|
|
242
|
+
sourceMessageId,
|
|
243
|
+
},
|
|
244
|
+
}],
|
|
245
|
+
$raw: payload,
|
|
246
|
+
$timestamp: event.msg_timestamp ?? Date.now(),
|
|
247
|
+
$recall: async () => {},
|
|
248
|
+
$reply: async () => '',
|
|
249
|
+
});
|
|
250
|
+
}
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* core keyboard 段 → KOOK 卡片消息(action-group + button)
|
|
3
|
+
*/
|
|
4
|
+
import type { KeyboardSegmentData, MessageElement } from "zhin.js";
|
|
5
|
+
import { KeyboardSegment, isKeyboardSegment } from "zhin.js";
|
|
6
|
+
|
|
7
|
+
function asKeyboardElement(
|
|
8
|
+
el: MessageElement,
|
|
9
|
+
): (MessageElement & { data: KeyboardSegmentData }) | null {
|
|
10
|
+
if (el instanceof KeyboardSegment) {
|
|
11
|
+
return { type: "keyboard", data: el.data };
|
|
12
|
+
}
|
|
13
|
+
if (isKeyboardSegment(el)) return el;
|
|
14
|
+
return null;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
function textFromElements(elements: MessageElement[]): string {
|
|
18
|
+
return elements
|
|
19
|
+
.filter((el) => el.type === "text" && typeof el.data.text === "string")
|
|
20
|
+
.map((el) => el.data.text as string)
|
|
21
|
+
.join("\n")
|
|
22
|
+
.trim();
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/** core keyboard 布局 → KOOK card[](可直接传给 sendChannelMsg / sendPrivateMsg) */
|
|
26
|
+
export function coreKeyboardToKookCard(
|
|
27
|
+
data: KeyboardSegmentData,
|
|
28
|
+
prefixElements: MessageElement[] = [],
|
|
29
|
+
): Array<Record<string, unknown>> {
|
|
30
|
+
const modules: Array<Record<string, unknown>> = [];
|
|
31
|
+
const prefixText = textFromElements(prefixElements);
|
|
32
|
+
if (prefixText) {
|
|
33
|
+
modules.push({
|
|
34
|
+
type: "section",
|
|
35
|
+
text: { type: "plain-text", content: prefixText },
|
|
36
|
+
});
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
for (const row of data.rows ?? []) {
|
|
40
|
+
modules.push({
|
|
41
|
+
type: "action-group",
|
|
42
|
+
elements: row.map((btn) => ({
|
|
43
|
+
type: "button",
|
|
44
|
+
theme: btn.disabled ? "secondary" : "primary",
|
|
45
|
+
value: btn.payload,
|
|
46
|
+
click: "return-val",
|
|
47
|
+
text: { type: "plain-text", content: btn.label },
|
|
48
|
+
})),
|
|
49
|
+
});
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
return [{
|
|
53
|
+
type: "card",
|
|
54
|
+
theme: "secondary",
|
|
55
|
+
size: "sm",
|
|
56
|
+
modules,
|
|
57
|
+
}];
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
export function findKeyboardSegment(
|
|
61
|
+
elements: MessageElement[],
|
|
62
|
+
): { keyboard: MessageElement & { data: KeyboardSegmentData }; rest: MessageElement[] } | null {
|
|
63
|
+
let keyboard: (MessageElement & { data: KeyboardSegmentData }) | undefined;
|
|
64
|
+
const rest: MessageElement[] = [];
|
|
65
|
+
|
|
66
|
+
for (const el of elements) {
|
|
67
|
+
const kb = asKeyboardElement(el);
|
|
68
|
+
if (kb) {
|
|
69
|
+
if (keyboard) return null;
|
|
70
|
+
keyboard = kb;
|
|
71
|
+
continue;
|
|
72
|
+
}
|
|
73
|
+
rest.push(el);
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
if (!keyboard) return null;
|
|
77
|
+
return { keyboard, rest };
|
|
78
|
+
}
|
package/src/outbound-media.ts
CHANGED
|
@@ -1,9 +1,14 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* KOOK
|
|
2
|
+
* KOOK 出站 media:base64 / 本地文件须先走 /v3/asset/create 上传,再嵌入 KMarkdown。
|
|
3
3
|
*/
|
|
4
4
|
import type { MessageElement, MessageSegment, SendContent } from "zhin.js";
|
|
5
|
-
|
|
6
|
-
|
|
5
|
+
import {
|
|
6
|
+
asMessageElements,
|
|
7
|
+
extractBase64Buffer,
|
|
8
|
+
isMediaSegmentType,
|
|
9
|
+
isRemoteUrl,
|
|
10
|
+
resolveLocalMediaPath,
|
|
11
|
+
} from "zhin.js";
|
|
7
12
|
|
|
8
13
|
export interface KookMediaUploader {
|
|
9
14
|
uploadMedia(data: string | Buffer): Promise<string>;
|
|
@@ -13,55 +18,6 @@ function isMessageSegment(seg: MessageElement): seg is MessageSegment {
|
|
|
13
18
|
return typeof seg.type === "string";
|
|
14
19
|
}
|
|
15
20
|
|
|
16
|
-
function isRemoteUrl(value: string): boolean {
|
|
17
|
-
return /^https?:\/\//i.test(value);
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
function asSegments(content: SendContent): MessageElement[] {
|
|
21
|
-
if (typeof content === "string") {
|
|
22
|
-
return [{ type: "text", data: { text: content } }];
|
|
23
|
-
}
|
|
24
|
-
if (!Array.isArray(content)) {
|
|
25
|
-
return [content];
|
|
26
|
-
}
|
|
27
|
-
return content.map((item) =>
|
|
28
|
-
typeof item === "string" ? { type: "text", data: { text: item } } : item,
|
|
29
|
-
);
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
function extractBase64Buffer(data: Record<string, unknown>): Buffer | undefined {
|
|
33
|
-
const url = typeof data.url === "string" ? data.url : undefined;
|
|
34
|
-
const file = typeof data.file === "string" ? data.file : undefined;
|
|
35
|
-
const base64 = typeof data.base64 === "string" ? data.base64 : undefined;
|
|
36
|
-
|
|
37
|
-
if (url?.startsWith("base64://")) {
|
|
38
|
-
return Buffer.from(url.slice(9), "base64");
|
|
39
|
-
}
|
|
40
|
-
if (url && /^data:[^/]+\/[^;]+;base64,/i.test(url)) {
|
|
41
|
-
return Buffer.from(url.replace(/^data:[^/]+\/[^;]+;base64,/, ""), "base64");
|
|
42
|
-
}
|
|
43
|
-
if (file?.startsWith("base64://")) {
|
|
44
|
-
return Buffer.from(file.slice(9), "base64");
|
|
45
|
-
}
|
|
46
|
-
if (base64) {
|
|
47
|
-
const raw = base64.startsWith("base64://") ? base64.slice(9) : base64;
|
|
48
|
-
return Buffer.from(raw, "base64");
|
|
49
|
-
}
|
|
50
|
-
return undefined;
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
function resolveLocalFile(data: Record<string, unknown>): string | undefined {
|
|
54
|
-
const url = typeof data.url === "string" ? data.url : undefined;
|
|
55
|
-
const file = typeof data.file === "string" ? data.file : undefined;
|
|
56
|
-
const candidate = file ?? url;
|
|
57
|
-
if (!candidate || isRemoteUrl(candidate)) return undefined;
|
|
58
|
-
if (candidate.startsWith("base64://") || /^data:/.test(candidate)) return undefined;
|
|
59
|
-
if (candidate.startsWith("file://") || candidate.startsWith("/") || !candidate.includes("://")) {
|
|
60
|
-
return candidate;
|
|
61
|
-
}
|
|
62
|
-
return undefined;
|
|
63
|
-
}
|
|
64
|
-
|
|
65
21
|
function withUploadedUrl(seg: MessageSegment, uploadedUrl: string): MessageSegment {
|
|
66
22
|
const data = { ...seg.data } as Record<string, unknown>;
|
|
67
23
|
data.url = uploadedUrl;
|
|
@@ -74,7 +30,7 @@ async function resolveMediaSegment(
|
|
|
74
30
|
uploader: KookMediaUploader,
|
|
75
31
|
seg: MessageSegment,
|
|
76
32
|
): Promise<MessageSegment> {
|
|
77
|
-
if (!
|
|
33
|
+
if (!isMediaSegmentType(seg.type)) return seg;
|
|
78
34
|
|
|
79
35
|
const data = seg.data as Record<string, unknown>;
|
|
80
36
|
const url = typeof data.url === "string" ? data.url : undefined;
|
|
@@ -89,7 +45,7 @@ async function resolveMediaSegment(
|
|
|
89
45
|
return withUploadedUrl(seg, uploaded);
|
|
90
46
|
}
|
|
91
47
|
|
|
92
|
-
const local =
|
|
48
|
+
const local = resolveLocalMediaPath(data);
|
|
93
49
|
if (local) {
|
|
94
50
|
const uploaded = await uploader.uploadMedia(local);
|
|
95
51
|
return withUploadedUrl(seg, uploaded);
|
|
@@ -103,7 +59,7 @@ export async function materializeOutboundMedia(
|
|
|
103
59
|
uploader: KookMediaUploader,
|
|
104
60
|
content: SendContent,
|
|
105
61
|
): Promise<MessageElement[]> {
|
|
106
|
-
const segments =
|
|
62
|
+
const segments = asMessageElements(content);
|
|
107
63
|
return Promise.all(segments.map(async (item) => {
|
|
108
64
|
if (!isMessageSegment(item)) return item;
|
|
109
65
|
return resolveMediaSegment(uploader, item);
|
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
|
@@ -13,8 +13,8 @@ export function platformPermit(perm: string): string {
|
|
|
13
13
|
}
|
|
14
14
|
|
|
15
15
|
const FACTORY_PERM_MAP: Record<string, string> = {
|
|
16
|
-
|
|
17
|
-
|
|
16
|
+
scene_admin: 'guild_admin',
|
|
17
|
+
scene_owner: 'guild_owner',
|
|
18
18
|
};
|
|
19
19
|
|
|
20
20
|
export function kookGroupPermitResolver(logicalPerm: string): string {
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { toCanonicalSegments, fromCanonicalSegments } from 'zhin.js';
|