@zhin.js/adapter-lark 1.1.25 → 1.1.27
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 +146 -0
- package/LICENSE +21 -0
- package/PERMITS.md +11 -11
- package/README.md +7 -3
- package/adapters/lark/index.js +3 -0
- package/adapters/lark/index.ts +3 -0
- package/agents/lark/conversation.js +15 -0
- package/agents/lark/conversation.ts +17 -0
- package/agents/lark/messages.js +4 -0
- package/agents/lark/messages.ts +4 -0
- package/agents/lark/skills/lark/tools/upload_file/index.js +3 -1
- package/agents/lark/skills/lark/tools/upload_file/index.ts +3 -1
- package/agents/lark/skills/lark-group-admin/tools/add_members/index.js +5 -6
- package/agents/lark/skills/lark-group-admin/tools/add_members/index.ts +6 -7
- package/agents/lark/skills/lark-group-admin/tools/dissolve_chat/index.js +6 -6
- package/agents/lark/skills/lark-group-admin/tools/dissolve_chat/index.ts +7 -7
- package/agents/lark/skills/lark-group-admin/tools/remove_managers/index.js +5 -6
- package/agents/lark/skills/lark-group-admin/tools/remove_managers/index.ts +6 -7
- package/agents/lark/skills/lark-group-admin/tools/set_managers/index.js +5 -6
- package/agents/lark/skills/lark-group-admin/tools/set_managers/index.ts +6 -7
- package/agents/lark/skills/lark-group-admin/tools/update_chat/index.js +5 -6
- package/agents/lark/skills/lark-group-admin/tools/update_chat/index.ts +6 -7
- package/lib/client.js +49 -16
- package/lib/console-spec.d.ts +1 -1
- package/lib/console-spec.js +71 -7
- package/lib/index.d.ts +2 -2
- package/lib/index.js +2 -2
- package/lib/platform-permit.d.ts +4 -11
- package/lib/platform-permit.js +7 -33
- package/lib/protocol.d.ts +10 -5
- package/lib/protocol.js +17 -20
- package/lib/webhook.d.ts +2 -3
- package/lib/webhook.js +35 -28
- package/package.json +25 -26
- package/plugin.js +0 -6
- package/schema.json +0 -56
- package/src/client.ts +44 -19
- package/src/console-spec.ts +71 -7
- package/src/index.ts +2 -7
- package/src/platform-permit.ts +5 -45
- package/src/protocol.ts +21 -24
- package/src/webhook.ts +38 -33
package/src/webhook.ts
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Lark/Feishu webhook HTTP:
|
|
2
|
+
* Lark/Feishu webhook HTTP: signature → decrypt → verification token → challenge/event → admit.
|
|
3
3
|
*/
|
|
4
4
|
import type { LarkCardAction } from './cards.js';
|
|
5
|
-
import type { IncomingMessage, ServerResponse } from 'node:http';
|
|
6
5
|
import type { HttpHost, HttpRouteRegistration } from '@zhin.js/host-http';
|
|
6
|
+
import { readTextBody } from '@zhin.js/host-http-contract';
|
|
7
7
|
import { formatCompact, getLogger } from '@zhin.js/logger';
|
|
8
8
|
import {
|
|
9
|
+
decryptLarkEvent,
|
|
9
10
|
headerValue,
|
|
10
|
-
readTextBody,
|
|
11
11
|
verifySignature,
|
|
12
12
|
verifyToken,
|
|
13
13
|
type LarkEventBody,
|
|
@@ -31,17 +31,14 @@ export function registerLarkWebhookRoutes(
|
|
|
31
31
|
): HttpRouteRegistration[] {
|
|
32
32
|
const path = handler.config.webhookPath;
|
|
33
33
|
return [
|
|
34
|
-
http.route('POST', path,
|
|
35
|
-
await handleLarkWebhookRequest(request, response, handler);
|
|
36
|
-
}, { summary: 'Lark/Feishu event webhook', tags: ['lark'] }),
|
|
34
|
+
http.route('POST', path, (request) => handleLarkWebhookRequest(request, handler), { summary: 'Lark/Feishu event webhook', tags: ['lark'] }),
|
|
37
35
|
];
|
|
38
36
|
}
|
|
39
37
|
|
|
40
38
|
export async function handleLarkWebhookRequest(
|
|
41
|
-
request:
|
|
42
|
-
response: ServerResponse,
|
|
39
|
+
request: Request,
|
|
43
40
|
handler: LarkWebhookHandler,
|
|
44
|
-
): Promise<
|
|
41
|
+
): Promise<Response> {
|
|
45
42
|
try {
|
|
46
43
|
const rawBody = await readTextBody(request);
|
|
47
44
|
|
|
@@ -49,22 +46,10 @@ export async function handleLarkWebhookRequest(
|
|
|
49
46
|
try {
|
|
50
47
|
event = JSON.parse(rawBody) as LarkEventBody;
|
|
51
48
|
} catch {
|
|
52
|
-
|
|
53
|
-
response.end(JSON.stringify({ code: 0, msg: 'success' }));
|
|
54
|
-
return;
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
if (handler.config.verificationToken) {
|
|
58
|
-
const bodyToken = (event as LarkEventBody & { header?: { token?: unknown } }).header?.token ?? event.token;
|
|
59
|
-
const token = typeof bodyToken === 'string' ? bodyToken : headerValue(request.headers, 'x-lark-request-token');
|
|
60
|
-
if (!verifyToken(token, handler.config.verificationToken)) {
|
|
61
|
-
logger.warn(formatCompact({ op: 'webhook', ok: false, error: 'invalid verification token' }));
|
|
62
|
-
response.writeHead(403, { 'Content-Type': 'text/plain' });
|
|
63
|
-
response.end('Forbidden');
|
|
64
|
-
return;
|
|
65
|
-
}
|
|
49
|
+
return new Response(JSON.stringify({ code: 0, msg: 'success' }), { status: 200, headers: { 'Content-Type': 'application/json' } });
|
|
66
50
|
}
|
|
67
51
|
|
|
52
|
+
// Signature first: it covers the raw (possibly encrypted) body.
|
|
68
53
|
if (handler.config.encryptKey) {
|
|
69
54
|
const timestamp = headerValue(request.headers, 'x-lark-request-timestamp');
|
|
70
55
|
const nonce = headerValue(request.headers, 'x-lark-request-nonce');
|
|
@@ -76,12 +61,36 @@ export async function handleLarkWebhookRequest(
|
|
|
76
61
|
|| !verifySignature(handler.config.encryptKey, timestamp, nonce, rawBody, signature)
|
|
77
62
|
) {
|
|
78
63
|
logger.warn(formatCompact({ op: 'webhook', ok: false, error: 'invalid signature' }));
|
|
79
|
-
|
|
80
|
-
response.end('Forbidden');
|
|
81
|
-
return;
|
|
64
|
+
return new Response('Forbidden', { status: 403, headers: { 'Content-Type': 'text/plain' } });
|
|
82
65
|
}
|
|
83
66
|
}
|
|
84
67
|
|
|
68
|
+
if (typeof event?.encrypt === 'string') {
|
|
69
|
+
if (!handler.config.encryptKey) {
|
|
70
|
+
logger.warn(formatCompact({ op: 'webhook', ok: false, error: 'encrypted event received but encryptKey is not configured' }));
|
|
71
|
+
return new Response(JSON.stringify({ code: -1, msg: 'Encrypt key is not configured' }), { status: 400, headers: { 'Content-Type': 'application/json' } });
|
|
72
|
+
}
|
|
73
|
+
try {
|
|
74
|
+
event = JSON.parse(decryptLarkEvent(handler.config.encryptKey, event.encrypt)) as LarkEventBody;
|
|
75
|
+
} catch {
|
|
76
|
+
logger.warn(formatCompact({ op: 'webhook', ok: false, error: 'event decryption failed' }));
|
|
77
|
+
return new Response(JSON.stringify({ code: -1, msg: 'Decryption failed' }), { status: 400, headers: { 'Content-Type': 'application/json' } });
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
if (!event || typeof event !== 'object') {
|
|
82
|
+
return new Response(JSON.stringify({ code: 0, msg: 'success' }), { status: 200, headers: { 'Content-Type': 'application/json' } });
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
// Token and challenge live inside the decrypted event.
|
|
86
|
+
if (handler.config.verificationToken) {
|
|
87
|
+
const bodyToken = (event as LarkEventBody & { header?: { token?: unknown } }).header?.token ?? event.token;
|
|
88
|
+
const token = typeof bodyToken === 'string' ? bodyToken : headerValue(request.headers, 'x-lark-request-token');
|
|
89
|
+
if (!verifyToken(token, handler.config.verificationToken)) {
|
|
90
|
+
logger.warn(formatCompact({ op: 'webhook', ok: false, error: 'invalid verification token' }));
|
|
91
|
+
return new Response('Forbidden', { status: 403, headers: { 'Content-Type': 'text/plain' } });
|
|
92
|
+
}
|
|
93
|
+
}
|
|
85
94
|
|
|
86
95
|
if (handler.isOpen) {
|
|
87
96
|
const eventName = event.type === 'event_callback'
|
|
@@ -91,9 +100,7 @@ export async function handleLarkWebhookRequest(
|
|
|
91
100
|
}
|
|
92
101
|
|
|
93
102
|
if (event.type === 'url_verification') {
|
|
94
|
-
|
|
95
|
-
response.end(JSON.stringify({ challenge: event.challenge }));
|
|
96
|
-
return;
|
|
103
|
+
return new Response(JSON.stringify({ challenge: event.challenge }), { status: 200, headers: { 'Content-Type': 'application/json' } });
|
|
97
104
|
}
|
|
98
105
|
|
|
99
106
|
const headerEventType = (event as LarkEventBody & { header?: { event_type?: string } }).header?.event_type;
|
|
@@ -107,11 +114,9 @@ export async function handleLarkWebhookRequest(
|
|
|
107
114
|
handler.admit(message, event);
|
|
108
115
|
}
|
|
109
116
|
|
|
110
|
-
|
|
111
|
-
response.end(JSON.stringify({ code: 0, msg: 'success' }));
|
|
117
|
+
return new Response(JSON.stringify({ code: 0, msg: 'success' }), { status: 200, headers: { 'Content-Type': 'application/json' } });
|
|
112
118
|
} catch (error) {
|
|
113
119
|
logger.error('Webhook error:', error);
|
|
114
|
-
|
|
115
|
-
response.end(JSON.stringify({ code: -1, msg: 'Internal Server Error' }));
|
|
120
|
+
return new Response(JSON.stringify({ code: -1, msg: 'Internal Server Error' }), { status: 500, headers: { 'Content-Type': 'application/json' } });
|
|
116
121
|
}
|
|
117
122
|
}
|