@zhin.js/adapter-lark 4.0.2 → 5.0.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.
- package/CHANGELOG.md +54 -0
- package/README.md +60 -88
- package/adapters/lark.ts +26 -0
- package/agent/tools/add_members.ts +2 -2
- package/agent/tools/create_chat.ts +2 -2
- package/agent/tools/dissolve_chat.ts +2 -2
- package/agent/tools/get_user.ts +2 -2
- package/agent/tools/remove_managers.ts +2 -2
- package/agent/tools/set_managers.ts +2 -2
- package/agent/tools/update_chat.ts +2 -2
- package/agent/tools/upload_file.ts +2 -2
- package/lib/endpoint.d.ts +53 -0
- package/lib/endpoint.js +333 -0
- package/lib/index.d.ts +5 -0
- package/lib/index.js +5 -0
- package/lib/lark-agent-deps.d.ts +27 -0
- package/lib/lark-agent-deps.js +30 -0
- package/lib/platform-permit.d.ts +15 -0
- package/lib/{src/platform-permit.js → platform-permit.js} +1 -2
- package/lib/protocol.d.ts +111 -0
- package/lib/protocol.js +204 -0
- package/lib/webhook.d.ts +13 -0
- package/lib/webhook.js +67 -0
- package/package.json +43 -24
- package/plugin.ts +12 -0
- package/schema.json +61 -0
- package/src/endpoint.ts +354 -705
- package/src/index.ts +45 -59
- package/src/lark-agent-deps.ts +53 -9
- package/src/platform-permit.ts +1 -1
- package/src/protocol.ts +332 -0
- package/src/webhook.ts +101 -0
- package/lib/agent/tools/add_members.js +0 -21
- package/lib/agent/tools/add_members.js.map +0 -1
- package/lib/agent/tools/create_chat.js +0 -20
- package/lib/agent/tools/create_chat.js.map +0 -1
- package/lib/agent/tools/dissolve_chat.js +0 -18
- package/lib/agent/tools/dissolve_chat.js.map +0 -1
- package/lib/agent/tools/get_user.js +0 -17
- package/lib/agent/tools/get_user.js.map +0 -1
- package/lib/agent/tools/remove_managers.js +0 -21
- package/lib/agent/tools/remove_managers.js.map +0 -1
- package/lib/agent/tools/set_managers.js +0 -21
- package/lib/agent/tools/set_managers.js.map +0 -1
- package/lib/agent/tools/update_chat.js +0 -22
- package/lib/agent/tools/update_chat.js.map +0 -1
- package/lib/agent/tools/upload_file.js +0 -19
- package/lib/agent/tools/upload_file.js.map +0 -1
- package/lib/src/adapter.js +0 -48
- package/lib/src/adapter.js.map +0 -1
- package/lib/src/endpoint.js +0 -677
- package/lib/src/endpoint.js.map +0 -1
- package/lib/src/index.js +0 -43
- package/lib/src/index.js.map +0 -1
- package/lib/src/lark-agent-deps.js +0 -10
- package/lib/src/lark-agent-deps.js.map +0 -1
- package/lib/src/platform-permit.js.map +0 -1
- package/lib/src/segment-mapper.js +0 -2
- package/lib/src/segment-mapper.js.map +0 -1
- package/lib/src/types.js +0 -2
- package/lib/src/types.js.map +0 -1
- package/plugin.yml +0 -3
- package/src/adapter.ts +0 -57
- package/src/segment-mapper.ts +0 -1
- package/src/types.ts +0 -58
package/lib/protocol.js
ADDED
|
@@ -0,0 +1,204 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Lark/Feishu protocol helpers — no legacy Adapter/Endpoint / segment-mapper.
|
|
3
|
+
* Canonicalization is owned by gateway/core before endpoint.send.
|
|
4
|
+
*/
|
|
5
|
+
import { createHash, timingSafeEqual } from 'node:crypto';
|
|
6
|
+
export function resolveLarkConfig(config = {}) {
|
|
7
|
+
const entry = config.endpoints?.find((item) => item.context === 'lark');
|
|
8
|
+
const appId = config.appId ?? entry?.appId ?? process.env.LARK_APP_ID;
|
|
9
|
+
const appSecret = config.appSecret ?? entry?.appSecret ?? process.env.LARK_APP_SECRET;
|
|
10
|
+
if (!appId || !appSecret) {
|
|
11
|
+
throw new TypeError('Lark adapter requires appId + appSecret (plugins.<key> or endpoints with context: lark)');
|
|
12
|
+
}
|
|
13
|
+
const isFeishu = config.isFeishu ?? entry?.isFeishu ?? true;
|
|
14
|
+
const name = (typeof config.name === 'string' && config.name)
|
|
15
|
+
|| (typeof entry?.name === 'string' && entry.name)
|
|
16
|
+
|| process.env.LARK_BOT_NAME
|
|
17
|
+
|| 'lark-bot';
|
|
18
|
+
const webhookPath = normalizeWebhookPath(config.webhookPath ?? entry?.webhookPath ?? '/lark/webhook');
|
|
19
|
+
const defaultBase = isFeishu
|
|
20
|
+
? 'https://open.feishu.cn/open-apis'
|
|
21
|
+
: 'https://open.larksuite.com/open-apis';
|
|
22
|
+
const apiBaseUrl = (config.apiBaseUrl ?? entry?.apiBaseUrl ?? defaultBase).replace(/\/$/, '');
|
|
23
|
+
const encryptKey = config.encryptKey ?? entry?.encryptKey;
|
|
24
|
+
const verificationToken = config.verificationToken ?? entry?.verificationToken;
|
|
25
|
+
return {
|
|
26
|
+
context: 'lark',
|
|
27
|
+
name,
|
|
28
|
+
appId,
|
|
29
|
+
appSecret,
|
|
30
|
+
...(encryptKey ? { encryptKey } : {}),
|
|
31
|
+
...(verificationToken ? { verificationToken } : {}),
|
|
32
|
+
webhookPath,
|
|
33
|
+
apiBaseUrl,
|
|
34
|
+
isFeishu,
|
|
35
|
+
};
|
|
36
|
+
}
|
|
37
|
+
export function normalizeWebhookPath(path) {
|
|
38
|
+
const trimmed = path.trim() || '/lark/webhook';
|
|
39
|
+
return trimmed.startsWith('/') ? trimmed : `/${trimmed}`;
|
|
40
|
+
}
|
|
41
|
+
export function resolveChatType(chatId) {
|
|
42
|
+
return chatId?.startsWith('oc_') ? 'group' : 'private';
|
|
43
|
+
}
|
|
44
|
+
export function resolveTarget(msg) {
|
|
45
|
+
return msg.chat_id || 'unknown';
|
|
46
|
+
}
|
|
47
|
+
export function resolveSender(msg) {
|
|
48
|
+
return msg.sender?.sender_id?.open_id
|
|
49
|
+
|| msg.sender?.sender_id?.user_id
|
|
50
|
+
|| 'unknown';
|
|
51
|
+
}
|
|
52
|
+
export function generateMessageId(msg) {
|
|
53
|
+
return msg.message_id || `${msg.create_time ?? Date.now()}`;
|
|
54
|
+
}
|
|
55
|
+
/** Build inbound text for MessageGateway.receive. */
|
|
56
|
+
export function formatInboundContent(msg) {
|
|
57
|
+
if (!msg.content || !msg.message_type)
|
|
58
|
+
return '';
|
|
59
|
+
try {
|
|
60
|
+
const parsed = JSON.parse(msg.content);
|
|
61
|
+
switch (msg.message_type) {
|
|
62
|
+
case 'text':
|
|
63
|
+
return typeof parsed.text === 'string' ? parsed.text : '';
|
|
64
|
+
case 'image':
|
|
65
|
+
return '[image]';
|
|
66
|
+
case 'file': {
|
|
67
|
+
const name = typeof parsed.file_name === 'string' ? parsed.file_name : '';
|
|
68
|
+
return name ? `[file: ${name}]` : '[file]';
|
|
69
|
+
}
|
|
70
|
+
case 'audio':
|
|
71
|
+
return '[audio]';
|
|
72
|
+
case 'video':
|
|
73
|
+
return '[video]';
|
|
74
|
+
case 'sticker':
|
|
75
|
+
return '[sticker]';
|
|
76
|
+
case 'post':
|
|
77
|
+
case 'interactive':
|
|
78
|
+
return `[${msg.message_type}]`;
|
|
79
|
+
case 'rich_text':
|
|
80
|
+
return '[rich_text]';
|
|
81
|
+
default:
|
|
82
|
+
return `[${msg.message_type}]`;
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
catch {
|
|
86
|
+
return '[消息解析失败]';
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
export function verifySignature(encryptKey, timestamp, nonce, body, signature) {
|
|
90
|
+
try {
|
|
91
|
+
const stringToSign = `${timestamp}${nonce}${encryptKey}${body}`;
|
|
92
|
+
const calculated = createHash('sha256').update(stringToSign).digest('hex');
|
|
93
|
+
const a = Buffer.from(calculated);
|
|
94
|
+
const b = Buffer.from(signature);
|
|
95
|
+
if (a.length !== b.length)
|
|
96
|
+
return false;
|
|
97
|
+
return timingSafeEqual(a, b);
|
|
98
|
+
}
|
|
99
|
+
catch {
|
|
100
|
+
return false;
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
/**
|
|
104
|
+
* Wire-encode an already-rendered outbound payload into Lark im/v1 message body.
|
|
105
|
+
* Segment canonicalization is intentionally not done here.
|
|
106
|
+
*/
|
|
107
|
+
export function formatOutboundBody(payload) {
|
|
108
|
+
if (typeof payload === 'string') {
|
|
109
|
+
return {
|
|
110
|
+
msg_type: 'text',
|
|
111
|
+
content: JSON.stringify({ text: payload }),
|
|
112
|
+
};
|
|
113
|
+
}
|
|
114
|
+
const items = Array.isArray(payload)
|
|
115
|
+
? payload
|
|
116
|
+
: payload && typeof payload === 'object' && 'type' in payload
|
|
117
|
+
? [payload]
|
|
118
|
+
: [];
|
|
119
|
+
if (items.length === 0) {
|
|
120
|
+
const text = payload == null
|
|
121
|
+
? ''
|
|
122
|
+
: typeof payload === 'object'
|
|
123
|
+
? JSON.stringify(payload)
|
|
124
|
+
: String(payload);
|
|
125
|
+
return {
|
|
126
|
+
msg_type: 'text',
|
|
127
|
+
content: JSON.stringify({ text }),
|
|
128
|
+
};
|
|
129
|
+
}
|
|
130
|
+
const textParts = [];
|
|
131
|
+
let media = null;
|
|
132
|
+
for (const item of items) {
|
|
133
|
+
if (typeof item === 'string') {
|
|
134
|
+
textParts.push(item);
|
|
135
|
+
continue;
|
|
136
|
+
}
|
|
137
|
+
const data = item.data ?? {};
|
|
138
|
+
switch (item.type) {
|
|
139
|
+
case 'text':
|
|
140
|
+
textParts.push(String(data.content ?? data.text ?? ''));
|
|
141
|
+
break;
|
|
142
|
+
case 'at':
|
|
143
|
+
textParts.push(`<at user_id="${String(data.id ?? '')}">${String(data.name || data.id || '')}</at>`);
|
|
144
|
+
break;
|
|
145
|
+
case 'image':
|
|
146
|
+
if (!media) {
|
|
147
|
+
media = {
|
|
148
|
+
msg_type: 'image',
|
|
149
|
+
content: JSON.stringify({
|
|
150
|
+
image_key: data.file_key ?? data.key ?? data.url,
|
|
151
|
+
}),
|
|
152
|
+
};
|
|
153
|
+
}
|
|
154
|
+
break;
|
|
155
|
+
case 'file':
|
|
156
|
+
if (!media) {
|
|
157
|
+
media = {
|
|
158
|
+
msg_type: 'file',
|
|
159
|
+
content: JSON.stringify({
|
|
160
|
+
file_key: data.file_key ?? data.key,
|
|
161
|
+
}),
|
|
162
|
+
};
|
|
163
|
+
}
|
|
164
|
+
break;
|
|
165
|
+
case 'card':
|
|
166
|
+
if (!media) {
|
|
167
|
+
media = {
|
|
168
|
+
msg_type: 'interactive',
|
|
169
|
+
content: JSON.stringify(data),
|
|
170
|
+
};
|
|
171
|
+
}
|
|
172
|
+
break;
|
|
173
|
+
default:
|
|
174
|
+
textParts.push(`[${item.type}]`);
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
if (media)
|
|
178
|
+
return media;
|
|
179
|
+
return {
|
|
180
|
+
msg_type: 'text',
|
|
181
|
+
content: JSON.stringify({ text: textParts.join('') }),
|
|
182
|
+
};
|
|
183
|
+
}
|
|
184
|
+
export function headerValue(headers, name) {
|
|
185
|
+
const value = headers[name] ?? headers[name.toLowerCase()];
|
|
186
|
+
if (Array.isArray(value))
|
|
187
|
+
return value[0] ?? '';
|
|
188
|
+
return value ?? '';
|
|
189
|
+
}
|
|
190
|
+
export async function readTextBody(request, options = {}) {
|
|
191
|
+
const limit = options.limit ?? 1_048_576;
|
|
192
|
+
const chunks = [];
|
|
193
|
+
let size = 0;
|
|
194
|
+
for await (const chunk of request) {
|
|
195
|
+
const buffer = Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk);
|
|
196
|
+
size += buffer.length;
|
|
197
|
+
if (size > limit) {
|
|
198
|
+
request.destroy();
|
|
199
|
+
throw new Error(`Request body exceeds ${limit} bytes`);
|
|
200
|
+
}
|
|
201
|
+
chunks.push(buffer);
|
|
202
|
+
}
|
|
203
|
+
return Buffer.concat(chunks).toString('utf8');
|
|
204
|
+
}
|
package/lib/webhook.d.ts
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Lark/Feishu webhook HTTP: verification → signature → challenge/event → admit.
|
|
3
|
+
*/
|
|
4
|
+
import type { IncomingMessage, ServerResponse } from 'node:http';
|
|
5
|
+
import type { HttpHost, HttpRouteRegistration } from '@zhin.js/host-http';
|
|
6
|
+
import { type LarkMessage, type ResolvedLarkConfig } from './protocol.js';
|
|
7
|
+
export interface LarkWebhookHandler {
|
|
8
|
+
readonly config: ResolvedLarkConfig;
|
|
9
|
+
readonly isOpen: boolean;
|
|
10
|
+
admit(msg: LarkMessage): void;
|
|
11
|
+
}
|
|
12
|
+
export declare function registerLarkWebhookRoutes(http: HttpHost, handler: LarkWebhookHandler): HttpRouteRegistration[];
|
|
13
|
+
export declare function handleLarkWebhookRequest(request: IncomingMessage, response: ServerResponse, handler: LarkWebhookHandler): Promise<void>;
|
package/lib/webhook.js
ADDED
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import { formatCompact, getLogger } from '@zhin.js/logger';
|
|
2
|
+
import { headerValue, readTextBody, verifySignature, } from './protocol.js';
|
|
3
|
+
const logger = getLogger('lark');
|
|
4
|
+
export function registerLarkWebhookRoutes(http, handler) {
|
|
5
|
+
const path = handler.config.webhookPath;
|
|
6
|
+
return [
|
|
7
|
+
http.route('POST', path, async (request, response) => {
|
|
8
|
+
await handleLarkWebhookRequest(request, response, handler);
|
|
9
|
+
}, { summary: 'Lark/Feishu event webhook', tags: ['lark'] }),
|
|
10
|
+
];
|
|
11
|
+
}
|
|
12
|
+
export async function handleLarkWebhookRequest(request, response, handler) {
|
|
13
|
+
try {
|
|
14
|
+
const rawBody = await readTextBody(request);
|
|
15
|
+
if (handler.config.verificationToken) {
|
|
16
|
+
const token = headerValue(request.headers, 'x-lark-request-token');
|
|
17
|
+
if (token !== handler.config.verificationToken) {
|
|
18
|
+
logger.warn(formatCompact({ op: 'webhook', ok: false, error: 'invalid verification token' }));
|
|
19
|
+
response.writeHead(403, { 'Content-Type': 'text/plain' });
|
|
20
|
+
response.end('Forbidden');
|
|
21
|
+
return;
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
if (handler.config.encryptKey) {
|
|
25
|
+
const timestamp = headerValue(request.headers, 'x-lark-request-timestamp');
|
|
26
|
+
const nonce = headerValue(request.headers, 'x-lark-request-nonce');
|
|
27
|
+
const signature = headerValue(request.headers, 'x-lark-signature');
|
|
28
|
+
if (!timestamp
|
|
29
|
+
|| !nonce
|
|
30
|
+
|| !signature
|
|
31
|
+
|| !verifySignature(handler.config.encryptKey, timestamp, nonce, rawBody, signature)) {
|
|
32
|
+
logger.warn(formatCompact({ op: 'webhook', ok: false, error: 'invalid signature' }));
|
|
33
|
+
response.writeHead(403, { 'Content-Type': 'text/plain' });
|
|
34
|
+
response.end('Forbidden');
|
|
35
|
+
return;
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
let event;
|
|
39
|
+
try {
|
|
40
|
+
event = JSON.parse(rawBody);
|
|
41
|
+
}
|
|
42
|
+
catch {
|
|
43
|
+
response.writeHead(200, { 'Content-Type': 'application/json' });
|
|
44
|
+
response.end(JSON.stringify({ code: 0, msg: 'success' }));
|
|
45
|
+
return;
|
|
46
|
+
}
|
|
47
|
+
if (event.type === 'url_verification') {
|
|
48
|
+
response.writeHead(200, { 'Content-Type': 'application/json' });
|
|
49
|
+
response.end(JSON.stringify({ challenge: event.challenge }));
|
|
50
|
+
return;
|
|
51
|
+
}
|
|
52
|
+
if (event.type === 'event_callback' && event.event?.message && handler.isOpen) {
|
|
53
|
+
const message = {
|
|
54
|
+
...event.event.message,
|
|
55
|
+
sender: event.event.message.sender ?? event.event.sender,
|
|
56
|
+
};
|
|
57
|
+
handler.admit(message);
|
|
58
|
+
}
|
|
59
|
+
response.writeHead(200, { 'Content-Type': 'application/json' });
|
|
60
|
+
response.end(JSON.stringify({ code: 0, msg: 'success' }));
|
|
61
|
+
}
|
|
62
|
+
catch (error) {
|
|
63
|
+
logger.error('Webhook error:', error);
|
|
64
|
+
response.writeHead(500, { 'Content-Type': 'application/json' });
|
|
65
|
+
response.end(JSON.stringify({ code: -1, msg: 'Internal Server Error' }));
|
|
66
|
+
}
|
|
67
|
+
}
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@zhin.js/adapter-lark",
|
|
3
|
-
"version": "
|
|
4
|
-
"description": "Zhin.js
|
|
3
|
+
"version": "5.0.0",
|
|
4
|
+
"description": "Zhin.js Lark/Feishu (飞书) adapter for Plugin Runtime (HTTP webhook)",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./lib/index.js",
|
|
7
7
|
"types": "./lib/index.d.ts",
|
|
@@ -34,43 +34,48 @@
|
|
|
34
34
|
"directory": "plugins/adapters/lark"
|
|
35
35
|
},
|
|
36
36
|
"dependencies": {
|
|
37
|
-
"
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
"@
|
|
41
|
-
"@
|
|
42
|
-
"typescript": "^6.0.3",
|
|
43
|
-
"zod": "^4.4.3",
|
|
44
|
-
"@zhin.js/host-router": "2.0.3",
|
|
45
|
-
"zhin.js": "4.1.2",
|
|
46
|
-
"@zhin.js/agent": "1.0.3"
|
|
37
|
+
"@zhin.js/adapter": "1.1.0",
|
|
38
|
+
"@zhin.js/core": "1.4.0",
|
|
39
|
+
"@zhin.js/host-http": "1.0.2",
|
|
40
|
+
"@zhin.js/logger": "1.0.75",
|
|
41
|
+
"@zhin.js/plugin-runtime": "1.1.0"
|
|
47
42
|
},
|
|
48
43
|
"peerDependencies": {
|
|
49
44
|
"zod": "^4.0.0",
|
|
50
|
-
"@zhin.js/
|
|
51
|
-
"@zhin.js/
|
|
52
|
-
"zhin.js": "4.
|
|
45
|
+
"@zhin.js/adapter": "1.1.0",
|
|
46
|
+
"@zhin.js/agent": "1.0.5",
|
|
47
|
+
"@zhin.js/core": "1.4.0",
|
|
48
|
+
"@zhin.js/host-http": "1.0.2",
|
|
49
|
+
"@zhin.js/plugin-runtime": "1.1.0",
|
|
50
|
+
"zhin.js": "5.0.0"
|
|
53
51
|
},
|
|
54
52
|
"peerDependenciesMeta": {
|
|
55
|
-
"
|
|
53
|
+
"zhin.js": {
|
|
56
54
|
"optional": true
|
|
57
55
|
},
|
|
58
|
-
"@zhin.js/
|
|
59
|
-
"optional":
|
|
56
|
+
"@zhin.js/agent": {
|
|
57
|
+
"optional": true
|
|
60
58
|
},
|
|
61
59
|
"zod": {
|
|
62
60
|
"optional": true
|
|
63
61
|
}
|
|
64
62
|
},
|
|
63
|
+
"devDependencies": {
|
|
64
|
+
"@types/node": "^26.1.0",
|
|
65
|
+
"typescript": "^6.0.3",
|
|
66
|
+
"vitest": "^4.1.10",
|
|
67
|
+
"zod": "^4.4.3",
|
|
68
|
+
"@zhin.js/agent": "1.0.5",
|
|
69
|
+
"zhin.js": "5.0.0"
|
|
70
|
+
},
|
|
65
71
|
"files": [
|
|
72
|
+
"adapters",
|
|
73
|
+
"plugin.ts",
|
|
74
|
+
"schema.json",
|
|
66
75
|
"src",
|
|
67
76
|
"lib",
|
|
68
77
|
"agent",
|
|
69
|
-
"client",
|
|
70
|
-
"dist",
|
|
71
|
-
"plugin.yml",
|
|
72
78
|
"README.md",
|
|
73
|
-
"node",
|
|
74
79
|
"CHANGELOG.md"
|
|
75
80
|
],
|
|
76
81
|
"publishConfig": {
|
|
@@ -80,9 +85,23 @@
|
|
|
80
85
|
"engines": {
|
|
81
86
|
"node": "^20.19.0 || >=22.12.0"
|
|
82
87
|
},
|
|
88
|
+
"zhin": {
|
|
89
|
+
"protocol": 1,
|
|
90
|
+
"type": "plugin",
|
|
91
|
+
"entry": "./plugin.ts",
|
|
92
|
+
"engine": "^1.0.0",
|
|
93
|
+
"runtime": "trusted",
|
|
94
|
+
"features": [
|
|
95
|
+
{
|
|
96
|
+
"package": "@zhin.js/adapter",
|
|
97
|
+
"api": "^1.0.0"
|
|
98
|
+
}
|
|
99
|
+
],
|
|
100
|
+
"plugins": []
|
|
101
|
+
},
|
|
83
102
|
"scripts": {
|
|
84
|
-
"build": "
|
|
103
|
+
"build": "tsc",
|
|
85
104
|
"clean": "rimraf lib",
|
|
86
|
-
"
|
|
105
|
+
"test": "NODE_OPTIONS=--experimental-strip-types vitest run --root ../../.. plugins/adapters/lark/tests"
|
|
87
106
|
}
|
|
88
107
|
}
|
package/plugin.ts
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { definePlugin } from '@zhin.js/plugin-runtime';
|
|
2
|
+
import { registerLarkPlatformPermitChecker } from './src/platform-permit.js';
|
|
3
|
+
|
|
4
|
+
export default definePlugin({
|
|
5
|
+
name: 'lark',
|
|
6
|
+
metadata: {
|
|
7
|
+
displayName: 'Lark/Feishu (飞书) Adapter',
|
|
8
|
+
},
|
|
9
|
+
setup() {
|
|
10
|
+
return registerLarkPlatformPermitChecker();
|
|
11
|
+
},
|
|
12
|
+
});
|
package/schema.json
ADDED
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
3
|
+
"type": "object",
|
|
4
|
+
"additionalProperties": false,
|
|
5
|
+
"properties": {
|
|
6
|
+
"webhookPath": {
|
|
7
|
+
"type": "string",
|
|
8
|
+
"default": "/lark/webhook"
|
|
9
|
+
},
|
|
10
|
+
"apiBaseUrl": {
|
|
11
|
+
"type": "string"
|
|
12
|
+
},
|
|
13
|
+
"isFeishu": {
|
|
14
|
+
"type": "boolean",
|
|
15
|
+
"default": true
|
|
16
|
+
},
|
|
17
|
+
"endpoints": {
|
|
18
|
+
"type": "array",
|
|
19
|
+
"description": "多账号:一个插件实例挂多个 endpoint。每项与顶层字段同构(name 必填,其余覆盖顶层)",
|
|
20
|
+
"items": {
|
|
21
|
+
"type": "object",
|
|
22
|
+
"additionalProperties": true,
|
|
23
|
+
"properties": {
|
|
24
|
+
"name": {
|
|
25
|
+
"type": "string",
|
|
26
|
+
"description": "Lark bot name"
|
|
27
|
+
},
|
|
28
|
+
"appId": {
|
|
29
|
+
"type": "string",
|
|
30
|
+
"description": "Lark app ID"
|
|
31
|
+
},
|
|
32
|
+
"appSecret": {
|
|
33
|
+
"type": "string",
|
|
34
|
+
"description": "Lark app secret"
|
|
35
|
+
},
|
|
36
|
+
"encryptKey": {
|
|
37
|
+
"type": "string",
|
|
38
|
+
"description": "Lark encrypt key"
|
|
39
|
+
},
|
|
40
|
+
"verificationToken": {
|
|
41
|
+
"type": "string",
|
|
42
|
+
"description": "Lark verification token"
|
|
43
|
+
}
|
|
44
|
+
},
|
|
45
|
+
"required": [
|
|
46
|
+
"name",
|
|
47
|
+
"appId",
|
|
48
|
+
"appSecret"
|
|
49
|
+
]
|
|
50
|
+
}
|
|
51
|
+
},
|
|
52
|
+
"commandPrefix": {
|
|
53
|
+
"type": "string",
|
|
54
|
+
"default": "",
|
|
55
|
+
"description": "命令前缀(默认 '' 无前缀,任意文本按命令匹配;如 '/' 要求 / 开头)。endpoints[i] 可逐项覆盖"
|
|
56
|
+
}
|
|
57
|
+
},
|
|
58
|
+
"required": [
|
|
59
|
+
"endpoints"
|
|
60
|
+
]
|
|
61
|
+
}
|