@brushes/lark 0.1.3 → 0.1.6
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/package.json +5 -13
- package/{dist/index.d.ts → src/index.ts} +0 -1
- package/src/lark.ts +473 -0
- package/src/message-parser.ts +61 -0
- package/tsconfig.json +18 -0
- package/dist/index.d.ts.map +0 -1
- package/dist/index.js +0 -3
- package/dist/index.js.map +0 -1
- package/dist/lark.d.ts +0 -81
- package/dist/lark.d.ts.map +0 -1
- package/dist/lark.js +0 -306
- package/dist/lark.js.map +0 -1
- package/dist/message-parser.d.ts +0 -25
- package/dist/message-parser.d.ts.map +0 -1
- package/dist/message-parser.js +0 -48
- package/dist/message-parser.js.map +0 -1
package/package.json
CHANGED
|
@@ -1,22 +1,14 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@brushes/lark",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.6",
|
|
4
4
|
"type": "module",
|
|
5
|
-
"main": "./
|
|
6
|
-
"types": "./
|
|
5
|
+
"main": "./src/index.ts",
|
|
6
|
+
"types": "./src/index.ts",
|
|
7
7
|
"exports": {
|
|
8
|
-
".":
|
|
9
|
-
"types": "./dist/index.d.ts",
|
|
10
|
-
"development": "./src/index.ts",
|
|
11
|
-
"default": "./dist/index.js"
|
|
12
|
-
}
|
|
8
|
+
".": "./src/index.ts"
|
|
13
9
|
},
|
|
14
|
-
"files": [
|
|
15
|
-
"dist"
|
|
16
|
-
],
|
|
17
10
|
"scripts": {
|
|
18
|
-
"build": "
|
|
19
|
-
"prepublishOnly": "pnpm build"
|
|
11
|
+
"build": "tsc"
|
|
20
12
|
},
|
|
21
13
|
"dependencies": {
|
|
22
14
|
"@larksuiteoapi/node-sdk": "^1.71.1"
|
package/src/lark.ts
ADDED
|
@@ -0,0 +1,473 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 飞书接入:WS 长连接收消息 + REST 回消息。
|
|
3
|
+
*/
|
|
4
|
+
import * as Lark from '@larksuiteoapi/node-sdk';
|
|
5
|
+
import { mkdir } from 'node:fs/promises';
|
|
6
|
+
import { extname, join } from 'node:path';
|
|
7
|
+
import { parseMentions, type Mention } from './message-parser.ts';
|
|
8
|
+
|
|
9
|
+
export type CardJson = Record<string, unknown>;
|
|
10
|
+
|
|
11
|
+
export interface IncomingMessage {
|
|
12
|
+
messageId: string;
|
|
13
|
+
chatId: string;
|
|
14
|
+
chatType: string;
|
|
15
|
+
messageType: string;
|
|
16
|
+
text: string;
|
|
17
|
+
rootId: string;
|
|
18
|
+
threadId: string;
|
|
19
|
+
senderType: string;
|
|
20
|
+
senderOpenId: string;
|
|
21
|
+
senderUnionId: string;
|
|
22
|
+
mentions: Mention[];
|
|
23
|
+
rawContent: string;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export interface IncomingDocumentComment {
|
|
27
|
+
eventId: string;
|
|
28
|
+
fileToken: string;
|
|
29
|
+
fileType: string;
|
|
30
|
+
commentId: string;
|
|
31
|
+
replyId: string;
|
|
32
|
+
senderOpenId: string;
|
|
33
|
+
senderUnionId: string;
|
|
34
|
+
mentionedBot: boolean;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
export interface BotOptions {
|
|
38
|
+
appId: string;
|
|
39
|
+
appSecret: string;
|
|
40
|
+
onMessage: (msg: IncomingMessage, bot: Bot) => Promise<void>;
|
|
41
|
+
onCardAction?: (
|
|
42
|
+
action: CardAction,
|
|
43
|
+
) => Promise<CardActionResponse | undefined>;
|
|
44
|
+
onDocumentComment?: (
|
|
45
|
+
comment: IncomingDocumentComment,
|
|
46
|
+
bot: Bot,
|
|
47
|
+
) => Promise<void>;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
export interface BotIdentity {
|
|
51
|
+
openId: string;
|
|
52
|
+
name: string;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
export const FEISHU_TEXT_LIMIT = 3_000;
|
|
56
|
+
export const FEISHU_MENTION_LIMIT = 1_200;
|
|
57
|
+
export const FEISHU_COMMENT_LIMIT = 1_000;
|
|
58
|
+
|
|
59
|
+
export function fitFeishuText(text: string, maxLength: number): string {
|
|
60
|
+
const characters = Array.from(text);
|
|
61
|
+
if (characters.length <= maxLength) return text;
|
|
62
|
+
const suffix = '\n\n(内容过长,已截断。详细内容请写入文档或工作区文件。)';
|
|
63
|
+
const suffixLength = Array.from(suffix).length;
|
|
64
|
+
return `${characters.slice(0, Math.max(0, maxLength - suffixLength)).join('')}${suffix}`;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
export interface CardAction {
|
|
68
|
+
operatorOpenId: string;
|
|
69
|
+
operatorUnionId: string;
|
|
70
|
+
messageId: string;
|
|
71
|
+
value: Record<string, unknown>;
|
|
72
|
+
formValue: Record<string, unknown>;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
export interface CardActionResponse {
|
|
76
|
+
toast?: { type: 'success' | 'info' | 'warning' | 'error'; content: string };
|
|
77
|
+
card?: { type: 'raw'; data: CardJson };
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
export function parseCardAction(data: any): CardAction {
|
|
81
|
+
const value = data?.action?.value;
|
|
82
|
+
const formValue = data?.action?.form_value;
|
|
83
|
+
return {
|
|
84
|
+
operatorOpenId: data?.operator?.open_id ?? data?.operator_id?.open_id ?? '',
|
|
85
|
+
operatorUnionId: data?.operator?.union_id ?? data?.operator_id?.union_id ?? '',
|
|
86
|
+
messageId: data?.context?.open_message_id ?? data?.open_message_id ?? '',
|
|
87
|
+
value: isRecord(value) ? value : {},
|
|
88
|
+
formValue: isRecord(formValue) ? formValue : {},
|
|
89
|
+
};
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
export interface Bot {
|
|
93
|
+
client: Lark.Client;
|
|
94
|
+
getIdentity: () => Promise<BotIdentity>;
|
|
95
|
+
reply: (
|
|
96
|
+
messageId: string,
|
|
97
|
+
text: string,
|
|
98
|
+
replyInThread?: boolean,
|
|
99
|
+
) => Promise<string | undefined>;
|
|
100
|
+
replyCard: (
|
|
101
|
+
messageId: string,
|
|
102
|
+
card: CardJson,
|
|
103
|
+
replyInThread?: boolean,
|
|
104
|
+
) => Promise<string | undefined>;
|
|
105
|
+
replyMention: (
|
|
106
|
+
messageId: string,
|
|
107
|
+
target: BotIdentity,
|
|
108
|
+
text: string,
|
|
109
|
+
replyInThread?: boolean,
|
|
110
|
+
) => Promise<string | undefined>;
|
|
111
|
+
sendCardToChat: (
|
|
112
|
+
chatId: string,
|
|
113
|
+
card: CardJson,
|
|
114
|
+
) => Promise<string | undefined>;
|
|
115
|
+
sendMentionToChat: (
|
|
116
|
+
chatId: string,
|
|
117
|
+
target: BotIdentity,
|
|
118
|
+
text: string,
|
|
119
|
+
) => Promise<string | undefined>;
|
|
120
|
+
updateCard: (messageId: string, card: CardJson) => Promise<void>;
|
|
121
|
+
subscribeToDocumentComments: () => Promise<void>;
|
|
122
|
+
replyToDocumentComment: (
|
|
123
|
+
comment: IncomingDocumentComment,
|
|
124
|
+
text: string,
|
|
125
|
+
) => Promise<void>;
|
|
126
|
+
setDocumentCommentWorking: (
|
|
127
|
+
comment: IncomingDocumentComment,
|
|
128
|
+
active: boolean,
|
|
129
|
+
) => Promise<void>;
|
|
130
|
+
downloadResource: (
|
|
131
|
+
messageId: string,
|
|
132
|
+
fileKey: string,
|
|
133
|
+
type: 'image' | 'file',
|
|
134
|
+
saveDir: string,
|
|
135
|
+
fileName?: string,
|
|
136
|
+
) => Promise<string>;
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
export function buildMentionPostContent(
|
|
140
|
+
target: BotIdentity,
|
|
141
|
+
text: string,
|
|
142
|
+
): Record<string, unknown> {
|
|
143
|
+
const boundedText = fitFeishuText(text, FEISHU_MENTION_LIMIT);
|
|
144
|
+
return {
|
|
145
|
+
zh_cn: {
|
|
146
|
+
title: '',
|
|
147
|
+
content: [
|
|
148
|
+
[
|
|
149
|
+
{
|
|
150
|
+
tag: 'at',
|
|
151
|
+
user_id: target.openId,
|
|
152
|
+
...(target.name ? { user_name: target.name } : {}),
|
|
153
|
+
},
|
|
154
|
+
{ tag: 'text', text: ` ${boundedText}` },
|
|
155
|
+
],
|
|
156
|
+
],
|
|
157
|
+
},
|
|
158
|
+
};
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
async function fetchBotIdentity(client: Lark.Client): Promise<BotIdentity> {
|
|
162
|
+
const response = await client.request({
|
|
163
|
+
url: '/open-apis/bot/v3/info',
|
|
164
|
+
method: 'GET',
|
|
165
|
+
});
|
|
166
|
+
const bot = (response as { bot?: { open_id?: string; app_name?: string } }).bot;
|
|
167
|
+
if (!bot?.open_id) throw new Error('飞书没有返回 bot open_id');
|
|
168
|
+
return { openId: bot.open_id, name: bot.app_name?.trim() || 'Bot' };
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
const CONTENT_TYPE_EXTENSIONS: Record<string, string> = {
|
|
172
|
+
'image/jpeg': 'jpg',
|
|
173
|
+
'image/png': 'png',
|
|
174
|
+
'image/gif': 'gif',
|
|
175
|
+
'image/webp': 'webp',
|
|
176
|
+
'image/bmp': 'bmp',
|
|
177
|
+
'image/x-icon': 'ico',
|
|
178
|
+
};
|
|
179
|
+
|
|
180
|
+
function getHeader(headers: any, name: string): string {
|
|
181
|
+
const value =
|
|
182
|
+
typeof headers?.get === 'function'
|
|
183
|
+
? headers.get(name)
|
|
184
|
+
: (headers?.[name] ?? headers?.[name.toLowerCase()]);
|
|
185
|
+
return Array.isArray(value) ? (value[0] ?? '') : (value ?? '');
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
function resourceExtension(
|
|
189
|
+
type: 'image' | 'file',
|
|
190
|
+
fileName: string | undefined,
|
|
191
|
+
contentType: string,
|
|
192
|
+
): string {
|
|
193
|
+
const original = fileName ? extname(fileName).slice(1).toLowerCase() : '';
|
|
194
|
+
if (/^[a-z0-9]{1,10}$/.test(original)) return original;
|
|
195
|
+
|
|
196
|
+
const mime = contentType.split(';', 1)[0].trim().toLowerCase();
|
|
197
|
+
return CONTENT_TYPE_EXTENSIONS[mime] ?? (type === 'image' ? 'img' : 'bin');
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
interface PostElement {
|
|
201
|
+
tag?: string;
|
|
202
|
+
text?: string;
|
|
203
|
+
user_id?: string;
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
function renderPostElement(element: PostElement): string {
|
|
207
|
+
if (element.tag === 'at') return element.user_id ?? '';
|
|
208
|
+
if (element.tag === 'br') return '\n';
|
|
209
|
+
if (['text', 'a', 'code', 'code_block', 'md'].includes(element.tag ?? '')) {
|
|
210
|
+
return element.text ?? '';
|
|
211
|
+
}
|
|
212
|
+
return '';
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
export function extractMessageText(
|
|
216
|
+
messageType: string,
|
|
217
|
+
content: string,
|
|
218
|
+
): string {
|
|
219
|
+
const parsed = JSON.parse(content);
|
|
220
|
+
if (messageType === 'text') {
|
|
221
|
+
return parsed.text ?? '';
|
|
222
|
+
}
|
|
223
|
+
if (messageType === 'post') {
|
|
224
|
+
const paragraphs: PostElement[][] = parsed.content ?? [];
|
|
225
|
+
return paragraphs
|
|
226
|
+
.map((paragraph) => paragraph.map(renderPostElement).join(''))
|
|
227
|
+
.filter(Boolean)
|
|
228
|
+
.join('\n')
|
|
229
|
+
.trim();
|
|
230
|
+
}
|
|
231
|
+
return '';
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
export function startBot(opts: BotOptions): Bot {
|
|
235
|
+
const {
|
|
236
|
+
appId,
|
|
237
|
+
appSecret,
|
|
238
|
+
onMessage,
|
|
239
|
+
onCardAction,
|
|
240
|
+
onDocumentComment,
|
|
241
|
+
} = opts;
|
|
242
|
+
|
|
243
|
+
const client = new Lark.Client({ appId, appSecret });
|
|
244
|
+
|
|
245
|
+
const bot: Bot = {
|
|
246
|
+
client,
|
|
247
|
+
|
|
248
|
+
getIdentity() {
|
|
249
|
+
return fetchBotIdentity(client);
|
|
250
|
+
},
|
|
251
|
+
|
|
252
|
+
async reply(messageId, text, replyInThread = false) {
|
|
253
|
+
const res = await client.im.v1.message.reply({
|
|
254
|
+
path: { message_id: messageId },
|
|
255
|
+
data: {
|
|
256
|
+
msg_type: 'text',
|
|
257
|
+
content: JSON.stringify({
|
|
258
|
+
text: fitFeishuText(text, FEISHU_TEXT_LIMIT),
|
|
259
|
+
}),
|
|
260
|
+
...(replyInThread ? { reply_in_thread: true } : {}),
|
|
261
|
+
},
|
|
262
|
+
});
|
|
263
|
+
return res.data?.message_id;
|
|
264
|
+
},
|
|
265
|
+
|
|
266
|
+
async replyCard(messageId, card, replyInThread = false) {
|
|
267
|
+
const res = await client.im.v1.message.reply({
|
|
268
|
+
path: { message_id: messageId },
|
|
269
|
+
data: {
|
|
270
|
+
msg_type: 'interactive',
|
|
271
|
+
content: JSON.stringify(card),
|
|
272
|
+
...(replyInThread ? { reply_in_thread: true } : {}),
|
|
273
|
+
},
|
|
274
|
+
});
|
|
275
|
+
return res.data?.message_id;
|
|
276
|
+
},
|
|
277
|
+
|
|
278
|
+
async replyMention(messageId, target, text, replyInThread = false) {
|
|
279
|
+
const res = await client.im.v1.message.reply({
|
|
280
|
+
path: { message_id: messageId },
|
|
281
|
+
data: {
|
|
282
|
+
msg_type: 'post',
|
|
283
|
+
content: JSON.stringify(buildMentionPostContent(target, text)),
|
|
284
|
+
...(replyInThread ? { reply_in_thread: true } : {}),
|
|
285
|
+
},
|
|
286
|
+
});
|
|
287
|
+
return res.data?.message_id;
|
|
288
|
+
},
|
|
289
|
+
|
|
290
|
+
async sendCardToChat(chatId, card) {
|
|
291
|
+
const res = await client.im.v1.message.create({
|
|
292
|
+
params: { receive_id_type: 'chat_id' },
|
|
293
|
+
data: {
|
|
294
|
+
receive_id: chatId,
|
|
295
|
+
msg_type: 'interactive',
|
|
296
|
+
content: JSON.stringify(card),
|
|
297
|
+
},
|
|
298
|
+
});
|
|
299
|
+
return res.data?.message_id;
|
|
300
|
+
},
|
|
301
|
+
|
|
302
|
+
async sendMentionToChat(chatId, target, text) {
|
|
303
|
+
const res = await client.im.v1.message.create({
|
|
304
|
+
params: { receive_id_type: 'chat_id' },
|
|
305
|
+
data: {
|
|
306
|
+
receive_id: chatId,
|
|
307
|
+
msg_type: 'post',
|
|
308
|
+
content: JSON.stringify(buildMentionPostContent(target, text)),
|
|
309
|
+
},
|
|
310
|
+
});
|
|
311
|
+
return res.data?.message_id;
|
|
312
|
+
},
|
|
313
|
+
|
|
314
|
+
async updateCard(messageId, card) {
|
|
315
|
+
await client.im.v1.message.patch({
|
|
316
|
+
path: { message_id: messageId },
|
|
317
|
+
data: { content: JSON.stringify(card) },
|
|
318
|
+
});
|
|
319
|
+
},
|
|
320
|
+
|
|
321
|
+
async subscribeToDocumentComments() {
|
|
322
|
+
const response = await client.drive.v1.user.subscription({
|
|
323
|
+
data: { event_type: 'drive.notice.comment_add_v1' },
|
|
324
|
+
});
|
|
325
|
+
if (response.code && response.code !== 0) {
|
|
326
|
+
throw new Error(response.msg || '订阅飞书文档评论事件失败');
|
|
327
|
+
}
|
|
328
|
+
},
|
|
329
|
+
|
|
330
|
+
async replyToDocumentComment(comment, text) {
|
|
331
|
+
const response = await client.drive.v1.fileCommentReply.create({
|
|
332
|
+
path: {
|
|
333
|
+
file_token: comment.fileToken,
|
|
334
|
+
comment_id: comment.commentId,
|
|
335
|
+
},
|
|
336
|
+
params: {
|
|
337
|
+
file_type: comment.fileType as
|
|
338
|
+
| 'doc'
|
|
339
|
+
| 'docx'
|
|
340
|
+
| 'sheet'
|
|
341
|
+
| 'file'
|
|
342
|
+
| 'slides'
|
|
343
|
+
| 'bitable',
|
|
344
|
+
user_id_type: 'open_id',
|
|
345
|
+
},
|
|
346
|
+
data: {
|
|
347
|
+
content: {
|
|
348
|
+
elements: [{
|
|
349
|
+
type: 'text_run',
|
|
350
|
+
text_run: {
|
|
351
|
+
text: fitFeishuText(text, FEISHU_COMMENT_LIMIT),
|
|
352
|
+
},
|
|
353
|
+
}],
|
|
354
|
+
},
|
|
355
|
+
},
|
|
356
|
+
});
|
|
357
|
+
if (response.code && response.code !== 0) {
|
|
358
|
+
throw new Error(response.msg || '回复飞书文档评论失败');
|
|
359
|
+
}
|
|
360
|
+
},
|
|
361
|
+
|
|
362
|
+
async setDocumentCommentWorking(comment, active) {
|
|
363
|
+
const replyId = comment.replyId
|
|
364
|
+
|| await findRootCommentReplyId(client, comment);
|
|
365
|
+
const response = await client.drive.v2.commentReaction.updateReaction({
|
|
366
|
+
path: { file_token: comment.fileToken },
|
|
367
|
+
params: { file_type: comment.fileType },
|
|
368
|
+
data: {
|
|
369
|
+
action: active ? 'add' : 'delete',
|
|
370
|
+
reply_id: replyId,
|
|
371
|
+
reaction_type: 'Typing',
|
|
372
|
+
},
|
|
373
|
+
});
|
|
374
|
+
if (response.code && response.code !== 0) {
|
|
375
|
+
throw new Error(response.msg || '更新飞书文档评论状态失败');
|
|
376
|
+
}
|
|
377
|
+
},
|
|
378
|
+
|
|
379
|
+
async downloadResource(messageId, fileKey, type, saveDir, fileName) {
|
|
380
|
+
const res = await client.im.v1.messageResource.get({
|
|
381
|
+
path: { message_id: messageId, file_key: fileKey },
|
|
382
|
+
params: { type },
|
|
383
|
+
});
|
|
384
|
+
const contentType = getHeader(res.headers, 'content-type');
|
|
385
|
+
const extension = resourceExtension(type, fileName, contentType);
|
|
386
|
+
const savePath = join(saveDir, `${fileKey}.${extension}`);
|
|
387
|
+
await mkdir(saveDir, { recursive: true });
|
|
388
|
+
await res.writeFile(savePath);
|
|
389
|
+
return savePath;
|
|
390
|
+
},
|
|
391
|
+
};
|
|
392
|
+
|
|
393
|
+
const dispatcher = new Lark.EventDispatcher({}).register({
|
|
394
|
+
'card.action.trigger': async (data: any) => {
|
|
395
|
+
if (!onCardAction) return undefined;
|
|
396
|
+
return onCardAction(parseCardAction(data));
|
|
397
|
+
},
|
|
398
|
+
'im.message.receive_v1': async (data) => {
|
|
399
|
+
const m = data.message;
|
|
400
|
+
const msg: IncomingMessage = {
|
|
401
|
+
messageId: m.message_id,
|
|
402
|
+
chatId: m.chat_id,
|
|
403
|
+
chatType: m.chat_type,
|
|
404
|
+
messageType: m.message_type,
|
|
405
|
+
text: extractMessageText(m.message_type, m.content),
|
|
406
|
+
rootId: m.root_id ?? '',
|
|
407
|
+
threadId: m.thread_id ?? '',
|
|
408
|
+
senderType: data.sender.sender_type ?? '',
|
|
409
|
+
senderOpenId: data.sender.sender_id?.open_id ?? '',
|
|
410
|
+
senderUnionId: data.sender.sender_id?.union_id ?? '',
|
|
411
|
+
mentions: parseMentions(m.mentions),
|
|
412
|
+
rawContent: m.content,
|
|
413
|
+
};
|
|
414
|
+
await onMessage(msg, bot);
|
|
415
|
+
},
|
|
416
|
+
'drive.notice.comment_add_v1': async (data) => {
|
|
417
|
+
if (!onDocumentComment) return;
|
|
418
|
+
const meta = data.notice_meta;
|
|
419
|
+
if (!meta?.file_token || !meta.file_type || !data.comment_id) return;
|
|
420
|
+
await onDocumentComment(
|
|
421
|
+
{
|
|
422
|
+
eventId: data.event_id ?? '',
|
|
423
|
+
fileToken: meta.file_token,
|
|
424
|
+
fileType: meta.file_type,
|
|
425
|
+
commentId: data.comment_id,
|
|
426
|
+
replyId: data.reply_id ?? '',
|
|
427
|
+
senderOpenId: meta.from_user_id?.open_id ?? '',
|
|
428
|
+
senderUnionId: meta.from_user_id?.union_id ?? '',
|
|
429
|
+
mentionedBot: data.is_mentioned ?? false,
|
|
430
|
+
},
|
|
431
|
+
bot,
|
|
432
|
+
);
|
|
433
|
+
},
|
|
434
|
+
});
|
|
435
|
+
|
|
436
|
+
const wsClient = new Lark.WSClient({ appId, appSecret });
|
|
437
|
+
wsClient.start({ eventDispatcher: dispatcher });
|
|
438
|
+
|
|
439
|
+
return bot;
|
|
440
|
+
}
|
|
441
|
+
|
|
442
|
+
async function findRootCommentReplyId(
|
|
443
|
+
client: Lark.Client,
|
|
444
|
+
comment: IncomingDocumentComment,
|
|
445
|
+
): Promise<string> {
|
|
446
|
+
const response = await client.drive.v1.fileCommentReply.list({
|
|
447
|
+
path: {
|
|
448
|
+
file_token: comment.fileToken,
|
|
449
|
+
comment_id: comment.commentId,
|
|
450
|
+
},
|
|
451
|
+
params: {
|
|
452
|
+
file_type: comment.fileType as
|
|
453
|
+
| 'doc'
|
|
454
|
+
| 'docx'
|
|
455
|
+
| 'sheet'
|
|
456
|
+
| 'file'
|
|
457
|
+
| 'slides'
|
|
458
|
+
| 'bitable',
|
|
459
|
+
page_size: 1,
|
|
460
|
+
user_id_type: 'open_id',
|
|
461
|
+
},
|
|
462
|
+
});
|
|
463
|
+
if (response.code && response.code !== 0) {
|
|
464
|
+
throw new Error(response.msg || '读取飞书文档评论回复失败');
|
|
465
|
+
}
|
|
466
|
+
const replyId = response.data?.items?.[0]?.reply_id;
|
|
467
|
+
if (!replyId) throw new Error('飞书文档评论缺少可添加表情的回复 ID');
|
|
468
|
+
return replyId;
|
|
469
|
+
}
|
|
470
|
+
|
|
471
|
+
function isRecord(value: unknown): value is Record<string, unknown> {
|
|
472
|
+
return typeof value === 'object' && value !== null;
|
|
473
|
+
}
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 消息解析:@提及还原、图片/文件资源提取。
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
export interface Mention {
|
|
6
|
+
key: string; // '@_user_1'
|
|
7
|
+
name: string; // 显示名,如 'MyBot'
|
|
8
|
+
openId: string; // 'ou_xxx'
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* 从事件的 mentions 数组中提取结构化提及信息。
|
|
13
|
+
*/
|
|
14
|
+
export function parseMentions(raw: any[] | undefined): Mention[] {
|
|
15
|
+
if (!raw?.length) return [];
|
|
16
|
+
return raw.map((m) => ({
|
|
17
|
+
key: m.key,
|
|
18
|
+
name: m.name ?? '',
|
|
19
|
+
openId: m.id?.open_id ?? '',
|
|
20
|
+
}));
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* 把消息文本里的 @_user_N 占位符替换成 @显示名。
|
|
25
|
+
*/
|
|
26
|
+
export function resolveMentions(text: string, mentions: Mention[]): string {
|
|
27
|
+
let resolved = text;
|
|
28
|
+
for (const m of mentions) {
|
|
29
|
+
resolved = resolved.replaceAll(m.key, `@${m.name}`);
|
|
30
|
+
}
|
|
31
|
+
return resolved.trim();
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* 从消息 content 中提取资源 key(image_key / file_key)。
|
|
36
|
+
*/
|
|
37
|
+
export function extractResourceKeys(
|
|
38
|
+
messageType: string,
|
|
39
|
+
content: string,
|
|
40
|
+
): { type: 'image' | 'file'; key: string; fileName?: string }[] {
|
|
41
|
+
const parsed = JSON.parse(content);
|
|
42
|
+
const resources: { type: 'image' | 'file'; key: string; fileName?: string }[] = [];
|
|
43
|
+
|
|
44
|
+
if (messageType === 'image' && parsed.image_key) {
|
|
45
|
+
resources.push({ type: 'image', key: parsed.image_key });
|
|
46
|
+
}
|
|
47
|
+
if (messageType === 'file' && parsed.file_key) {
|
|
48
|
+
resources.push({ type: 'file', key: parsed.file_key, fileName: parsed.file_name });
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
if (messageType === 'post') {
|
|
52
|
+
const paragraphs: any[][] = parsed.content ?? [];
|
|
53
|
+
for (const el of paragraphs.flat()) {
|
|
54
|
+
if (el.tag === 'img' && el.image_key) {
|
|
55
|
+
resources.push({ type: 'image', key: el.image_key });
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
return resources;
|
|
61
|
+
}
|
package/tsconfig.json
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
{
|
|
2
|
+
"compilerOptions": {
|
|
3
|
+
"target": "ES2022",
|
|
4
|
+
"module": "ES2022",
|
|
5
|
+
"moduleResolution": "bundler",
|
|
6
|
+
"rewriteRelativeImportExtensions": true,
|
|
7
|
+
"types": ["node"],
|
|
8
|
+
"strict": true,
|
|
9
|
+
"esModuleInterop": true,
|
|
10
|
+
"skipLibCheck": true,
|
|
11
|
+
"declaration": true,
|
|
12
|
+
"declarationMap": true,
|
|
13
|
+
"sourceMap": true,
|
|
14
|
+
"outDir": "dist",
|
|
15
|
+
"rootDir": "src"
|
|
16
|
+
},
|
|
17
|
+
"include": ["src"]
|
|
18
|
+
}
|
package/dist/index.d.ts.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,WAAW,CAAC;AAC1B,cAAc,qBAAqB,CAAC"}
|
package/dist/index.js
DELETED
package/dist/index.js.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,WAAW,CAAC;AAC1B,cAAc,qBAAqB,CAAC"}
|
package/dist/lark.d.ts
DELETED
|
@@ -1,81 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* 飞书接入:WS 长连接收消息 + REST 回消息。
|
|
3
|
-
*/
|
|
4
|
-
import * as Lark from '@larksuiteoapi/node-sdk';
|
|
5
|
-
import { type Mention } from './message-parser.ts';
|
|
6
|
-
export type CardJson = Record<string, unknown>;
|
|
7
|
-
export interface IncomingMessage {
|
|
8
|
-
messageId: string;
|
|
9
|
-
chatId: string;
|
|
10
|
-
chatType: string;
|
|
11
|
-
messageType: string;
|
|
12
|
-
text: string;
|
|
13
|
-
rootId: string;
|
|
14
|
-
threadId: string;
|
|
15
|
-
senderType: string;
|
|
16
|
-
senderOpenId: string;
|
|
17
|
-
senderUnionId: string;
|
|
18
|
-
mentions: Mention[];
|
|
19
|
-
rawContent: string;
|
|
20
|
-
}
|
|
21
|
-
export interface IncomingDocumentComment {
|
|
22
|
-
eventId: string;
|
|
23
|
-
fileToken: string;
|
|
24
|
-
fileType: string;
|
|
25
|
-
commentId: string;
|
|
26
|
-
replyId: string;
|
|
27
|
-
senderOpenId: string;
|
|
28
|
-
senderUnionId: string;
|
|
29
|
-
mentionedBot: boolean;
|
|
30
|
-
}
|
|
31
|
-
export interface BotOptions {
|
|
32
|
-
appId: string;
|
|
33
|
-
appSecret: string;
|
|
34
|
-
onMessage: (msg: IncomingMessage, bot: Bot) => Promise<void>;
|
|
35
|
-
onCardAction?: (action: CardAction) => Promise<CardActionResponse | undefined>;
|
|
36
|
-
onDocumentComment?: (comment: IncomingDocumentComment, bot: Bot) => Promise<void>;
|
|
37
|
-
}
|
|
38
|
-
export interface BotIdentity {
|
|
39
|
-
openId: string;
|
|
40
|
-
name: string;
|
|
41
|
-
}
|
|
42
|
-
export declare const FEISHU_TEXT_LIMIT = 3000;
|
|
43
|
-
export declare const FEISHU_MENTION_LIMIT = 1200;
|
|
44
|
-
export declare const FEISHU_COMMENT_LIMIT = 1000;
|
|
45
|
-
export declare function fitFeishuText(text: string, maxLength: number): string;
|
|
46
|
-
export interface CardAction {
|
|
47
|
-
operatorOpenId: string;
|
|
48
|
-
operatorUnionId: string;
|
|
49
|
-
messageId: string;
|
|
50
|
-
value: Record<string, unknown>;
|
|
51
|
-
formValue: Record<string, unknown>;
|
|
52
|
-
}
|
|
53
|
-
export interface CardActionResponse {
|
|
54
|
-
toast?: {
|
|
55
|
-
type: 'success' | 'info' | 'warning' | 'error';
|
|
56
|
-
content: string;
|
|
57
|
-
};
|
|
58
|
-
card?: {
|
|
59
|
-
type: 'raw';
|
|
60
|
-
data: CardJson;
|
|
61
|
-
};
|
|
62
|
-
}
|
|
63
|
-
export declare function parseCardAction(data: any): CardAction;
|
|
64
|
-
export interface Bot {
|
|
65
|
-
client: Lark.Client;
|
|
66
|
-
getIdentity: () => Promise<BotIdentity>;
|
|
67
|
-
reply: (messageId: string, text: string, replyInThread?: boolean) => Promise<string | undefined>;
|
|
68
|
-
replyCard: (messageId: string, card: CardJson, replyInThread?: boolean) => Promise<string | undefined>;
|
|
69
|
-
replyMention: (messageId: string, target: BotIdentity, text: string, replyInThread?: boolean) => Promise<string | undefined>;
|
|
70
|
-
sendCardToChat: (chatId: string, card: CardJson) => Promise<string | undefined>;
|
|
71
|
-
sendMentionToChat: (chatId: string, target: BotIdentity, text: string) => Promise<string | undefined>;
|
|
72
|
-
updateCard: (messageId: string, card: CardJson) => Promise<void>;
|
|
73
|
-
subscribeToDocumentComments: () => Promise<void>;
|
|
74
|
-
replyToDocumentComment: (comment: IncomingDocumentComment, text: string) => Promise<void>;
|
|
75
|
-
setDocumentCommentWorking: (comment: IncomingDocumentComment, active: boolean) => Promise<void>;
|
|
76
|
-
downloadResource: (messageId: string, fileKey: string, type: 'image' | 'file', saveDir: string, fileName?: string) => Promise<string>;
|
|
77
|
-
}
|
|
78
|
-
export declare function buildMentionPostContent(target: BotIdentity, text: string): Record<string, unknown>;
|
|
79
|
-
export declare function extractMessageText(messageType: string, content: string): string;
|
|
80
|
-
export declare function startBot(opts: BotOptions): Bot;
|
|
81
|
-
//# sourceMappingURL=lark.d.ts.map
|
package/dist/lark.d.ts.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"lark.d.ts","sourceRoot":"","sources":["../src/lark.ts"],"names":[],"mappings":"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"}
|
package/dist/lark.js
DELETED
|
@@ -1,306 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* 飞书接入:WS 长连接收消息 + REST 回消息。
|
|
3
|
-
*/
|
|
4
|
-
import * as Lark from '@larksuiteoapi/node-sdk';
|
|
5
|
-
import { mkdir } from 'node:fs/promises';
|
|
6
|
-
import { extname, join } from 'node:path';
|
|
7
|
-
import { parseMentions } from './message-parser.js';
|
|
8
|
-
export const FEISHU_TEXT_LIMIT = 3_000;
|
|
9
|
-
export const FEISHU_MENTION_LIMIT = 1_200;
|
|
10
|
-
export const FEISHU_COMMENT_LIMIT = 1_000;
|
|
11
|
-
export function fitFeishuText(text, maxLength) {
|
|
12
|
-
const characters = Array.from(text);
|
|
13
|
-
if (characters.length <= maxLength)
|
|
14
|
-
return text;
|
|
15
|
-
const suffix = '\n\n(内容过长,已截断。详细内容请写入文档或工作区文件。)';
|
|
16
|
-
const suffixLength = Array.from(suffix).length;
|
|
17
|
-
return `${characters.slice(0, Math.max(0, maxLength - suffixLength)).join('')}${suffix}`;
|
|
18
|
-
}
|
|
19
|
-
export function parseCardAction(data) {
|
|
20
|
-
const value = data?.action?.value;
|
|
21
|
-
const formValue = data?.action?.form_value;
|
|
22
|
-
return {
|
|
23
|
-
operatorOpenId: data?.operator?.open_id ?? data?.operator_id?.open_id ?? '',
|
|
24
|
-
operatorUnionId: data?.operator?.union_id ?? data?.operator_id?.union_id ?? '',
|
|
25
|
-
messageId: data?.context?.open_message_id ?? data?.open_message_id ?? '',
|
|
26
|
-
value: isRecord(value) ? value : {},
|
|
27
|
-
formValue: isRecord(formValue) ? formValue : {},
|
|
28
|
-
};
|
|
29
|
-
}
|
|
30
|
-
export function buildMentionPostContent(target, text) {
|
|
31
|
-
const boundedText = fitFeishuText(text, FEISHU_MENTION_LIMIT);
|
|
32
|
-
return {
|
|
33
|
-
zh_cn: {
|
|
34
|
-
title: '',
|
|
35
|
-
content: [
|
|
36
|
-
[
|
|
37
|
-
{
|
|
38
|
-
tag: 'at',
|
|
39
|
-
user_id: target.openId,
|
|
40
|
-
...(target.name ? { user_name: target.name } : {}),
|
|
41
|
-
},
|
|
42
|
-
{ tag: 'text', text: ` ${boundedText}` },
|
|
43
|
-
],
|
|
44
|
-
],
|
|
45
|
-
},
|
|
46
|
-
};
|
|
47
|
-
}
|
|
48
|
-
async function fetchBotIdentity(client) {
|
|
49
|
-
const response = await client.request({
|
|
50
|
-
url: '/open-apis/bot/v3/info',
|
|
51
|
-
method: 'GET',
|
|
52
|
-
});
|
|
53
|
-
const bot = response.bot;
|
|
54
|
-
if (!bot?.open_id)
|
|
55
|
-
throw new Error('飞书没有返回 bot open_id');
|
|
56
|
-
return { openId: bot.open_id, name: bot.app_name?.trim() || 'Bot' };
|
|
57
|
-
}
|
|
58
|
-
const CONTENT_TYPE_EXTENSIONS = {
|
|
59
|
-
'image/jpeg': 'jpg',
|
|
60
|
-
'image/png': 'png',
|
|
61
|
-
'image/gif': 'gif',
|
|
62
|
-
'image/webp': 'webp',
|
|
63
|
-
'image/bmp': 'bmp',
|
|
64
|
-
'image/x-icon': 'ico',
|
|
65
|
-
};
|
|
66
|
-
function getHeader(headers, name) {
|
|
67
|
-
const value = typeof headers?.get === 'function'
|
|
68
|
-
? headers.get(name)
|
|
69
|
-
: (headers?.[name] ?? headers?.[name.toLowerCase()]);
|
|
70
|
-
return Array.isArray(value) ? (value[0] ?? '') : (value ?? '');
|
|
71
|
-
}
|
|
72
|
-
function resourceExtension(type, fileName, contentType) {
|
|
73
|
-
const original = fileName ? extname(fileName).slice(1).toLowerCase() : '';
|
|
74
|
-
if (/^[a-z0-9]{1,10}$/.test(original))
|
|
75
|
-
return original;
|
|
76
|
-
const mime = contentType.split(';', 1)[0].trim().toLowerCase();
|
|
77
|
-
return CONTENT_TYPE_EXTENSIONS[mime] ?? (type === 'image' ? 'img' : 'bin');
|
|
78
|
-
}
|
|
79
|
-
function renderPostElement(element) {
|
|
80
|
-
if (element.tag === 'at')
|
|
81
|
-
return element.user_id ?? '';
|
|
82
|
-
if (element.tag === 'br')
|
|
83
|
-
return '\n';
|
|
84
|
-
if (['text', 'a', 'code', 'code_block', 'md'].includes(element.tag ?? '')) {
|
|
85
|
-
return element.text ?? '';
|
|
86
|
-
}
|
|
87
|
-
return '';
|
|
88
|
-
}
|
|
89
|
-
export function extractMessageText(messageType, content) {
|
|
90
|
-
const parsed = JSON.parse(content);
|
|
91
|
-
if (messageType === 'text') {
|
|
92
|
-
return parsed.text ?? '';
|
|
93
|
-
}
|
|
94
|
-
if (messageType === 'post') {
|
|
95
|
-
const paragraphs = parsed.content ?? [];
|
|
96
|
-
return paragraphs
|
|
97
|
-
.map((paragraph) => paragraph.map(renderPostElement).join(''))
|
|
98
|
-
.filter(Boolean)
|
|
99
|
-
.join('\n')
|
|
100
|
-
.trim();
|
|
101
|
-
}
|
|
102
|
-
return '';
|
|
103
|
-
}
|
|
104
|
-
export function startBot(opts) {
|
|
105
|
-
const { appId, appSecret, onMessage, onCardAction, onDocumentComment, } = opts;
|
|
106
|
-
const client = new Lark.Client({ appId, appSecret });
|
|
107
|
-
const bot = {
|
|
108
|
-
client,
|
|
109
|
-
getIdentity() {
|
|
110
|
-
return fetchBotIdentity(client);
|
|
111
|
-
},
|
|
112
|
-
async reply(messageId, text, replyInThread = false) {
|
|
113
|
-
const res = await client.im.v1.message.reply({
|
|
114
|
-
path: { message_id: messageId },
|
|
115
|
-
data: {
|
|
116
|
-
msg_type: 'text',
|
|
117
|
-
content: JSON.stringify({
|
|
118
|
-
text: fitFeishuText(text, FEISHU_TEXT_LIMIT),
|
|
119
|
-
}),
|
|
120
|
-
...(replyInThread ? { reply_in_thread: true } : {}),
|
|
121
|
-
},
|
|
122
|
-
});
|
|
123
|
-
return res.data?.message_id;
|
|
124
|
-
},
|
|
125
|
-
async replyCard(messageId, card, replyInThread = false) {
|
|
126
|
-
const res = await client.im.v1.message.reply({
|
|
127
|
-
path: { message_id: messageId },
|
|
128
|
-
data: {
|
|
129
|
-
msg_type: 'interactive',
|
|
130
|
-
content: JSON.stringify(card),
|
|
131
|
-
...(replyInThread ? { reply_in_thread: true } : {}),
|
|
132
|
-
},
|
|
133
|
-
});
|
|
134
|
-
return res.data?.message_id;
|
|
135
|
-
},
|
|
136
|
-
async replyMention(messageId, target, text, replyInThread = false) {
|
|
137
|
-
const res = await client.im.v1.message.reply({
|
|
138
|
-
path: { message_id: messageId },
|
|
139
|
-
data: {
|
|
140
|
-
msg_type: 'post',
|
|
141
|
-
content: JSON.stringify(buildMentionPostContent(target, text)),
|
|
142
|
-
...(replyInThread ? { reply_in_thread: true } : {}),
|
|
143
|
-
},
|
|
144
|
-
});
|
|
145
|
-
return res.data?.message_id;
|
|
146
|
-
},
|
|
147
|
-
async sendCardToChat(chatId, card) {
|
|
148
|
-
const res = await client.im.v1.message.create({
|
|
149
|
-
params: { receive_id_type: 'chat_id' },
|
|
150
|
-
data: {
|
|
151
|
-
receive_id: chatId,
|
|
152
|
-
msg_type: 'interactive',
|
|
153
|
-
content: JSON.stringify(card),
|
|
154
|
-
},
|
|
155
|
-
});
|
|
156
|
-
return res.data?.message_id;
|
|
157
|
-
},
|
|
158
|
-
async sendMentionToChat(chatId, target, text) {
|
|
159
|
-
const res = await client.im.v1.message.create({
|
|
160
|
-
params: { receive_id_type: 'chat_id' },
|
|
161
|
-
data: {
|
|
162
|
-
receive_id: chatId,
|
|
163
|
-
msg_type: 'post',
|
|
164
|
-
content: JSON.stringify(buildMentionPostContent(target, text)),
|
|
165
|
-
},
|
|
166
|
-
});
|
|
167
|
-
return res.data?.message_id;
|
|
168
|
-
},
|
|
169
|
-
async updateCard(messageId, card) {
|
|
170
|
-
await client.im.v1.message.patch({
|
|
171
|
-
path: { message_id: messageId },
|
|
172
|
-
data: { content: JSON.stringify(card) },
|
|
173
|
-
});
|
|
174
|
-
},
|
|
175
|
-
async subscribeToDocumentComments() {
|
|
176
|
-
const response = await client.drive.v1.user.subscription({
|
|
177
|
-
data: { event_type: 'drive.notice.comment_add_v1' },
|
|
178
|
-
});
|
|
179
|
-
if (response.code && response.code !== 0) {
|
|
180
|
-
throw new Error(response.msg || '订阅飞书文档评论事件失败');
|
|
181
|
-
}
|
|
182
|
-
},
|
|
183
|
-
async replyToDocumentComment(comment, text) {
|
|
184
|
-
const response = await client.drive.v1.fileCommentReply.create({
|
|
185
|
-
path: {
|
|
186
|
-
file_token: comment.fileToken,
|
|
187
|
-
comment_id: comment.commentId,
|
|
188
|
-
},
|
|
189
|
-
params: {
|
|
190
|
-
file_type: comment.fileType,
|
|
191
|
-
user_id_type: 'open_id',
|
|
192
|
-
},
|
|
193
|
-
data: {
|
|
194
|
-
content: {
|
|
195
|
-
elements: [{
|
|
196
|
-
type: 'text_run',
|
|
197
|
-
text_run: {
|
|
198
|
-
text: fitFeishuText(text, FEISHU_COMMENT_LIMIT),
|
|
199
|
-
},
|
|
200
|
-
}],
|
|
201
|
-
},
|
|
202
|
-
},
|
|
203
|
-
});
|
|
204
|
-
if (response.code && response.code !== 0) {
|
|
205
|
-
throw new Error(response.msg || '回复飞书文档评论失败');
|
|
206
|
-
}
|
|
207
|
-
},
|
|
208
|
-
async setDocumentCommentWorking(comment, active) {
|
|
209
|
-
const replyId = comment.replyId
|
|
210
|
-
|| await findRootCommentReplyId(client, comment);
|
|
211
|
-
const response = await client.drive.v2.commentReaction.updateReaction({
|
|
212
|
-
path: { file_token: comment.fileToken },
|
|
213
|
-
params: { file_type: comment.fileType },
|
|
214
|
-
data: {
|
|
215
|
-
action: active ? 'add' : 'delete',
|
|
216
|
-
reply_id: replyId,
|
|
217
|
-
reaction_type: 'Typing',
|
|
218
|
-
},
|
|
219
|
-
});
|
|
220
|
-
if (response.code && response.code !== 0) {
|
|
221
|
-
throw new Error(response.msg || '更新飞书文档评论状态失败');
|
|
222
|
-
}
|
|
223
|
-
},
|
|
224
|
-
async downloadResource(messageId, fileKey, type, saveDir, fileName) {
|
|
225
|
-
const res = await client.im.v1.messageResource.get({
|
|
226
|
-
path: { message_id: messageId, file_key: fileKey },
|
|
227
|
-
params: { type },
|
|
228
|
-
});
|
|
229
|
-
const contentType = getHeader(res.headers, 'content-type');
|
|
230
|
-
const extension = resourceExtension(type, fileName, contentType);
|
|
231
|
-
const savePath = join(saveDir, `${fileKey}.${extension}`);
|
|
232
|
-
await mkdir(saveDir, { recursive: true });
|
|
233
|
-
await res.writeFile(savePath);
|
|
234
|
-
return savePath;
|
|
235
|
-
},
|
|
236
|
-
};
|
|
237
|
-
const dispatcher = new Lark.EventDispatcher({}).register({
|
|
238
|
-
'card.action.trigger': async (data) => {
|
|
239
|
-
if (!onCardAction)
|
|
240
|
-
return undefined;
|
|
241
|
-
return onCardAction(parseCardAction(data));
|
|
242
|
-
},
|
|
243
|
-
'im.message.receive_v1': async (data) => {
|
|
244
|
-
const m = data.message;
|
|
245
|
-
const msg = {
|
|
246
|
-
messageId: m.message_id,
|
|
247
|
-
chatId: m.chat_id,
|
|
248
|
-
chatType: m.chat_type,
|
|
249
|
-
messageType: m.message_type,
|
|
250
|
-
text: extractMessageText(m.message_type, m.content),
|
|
251
|
-
rootId: m.root_id ?? '',
|
|
252
|
-
threadId: m.thread_id ?? '',
|
|
253
|
-
senderType: data.sender.sender_type ?? '',
|
|
254
|
-
senderOpenId: data.sender.sender_id?.open_id ?? '',
|
|
255
|
-
senderUnionId: data.sender.sender_id?.union_id ?? '',
|
|
256
|
-
mentions: parseMentions(m.mentions),
|
|
257
|
-
rawContent: m.content,
|
|
258
|
-
};
|
|
259
|
-
await onMessage(msg, bot);
|
|
260
|
-
},
|
|
261
|
-
'drive.notice.comment_add_v1': async (data) => {
|
|
262
|
-
if (!onDocumentComment)
|
|
263
|
-
return;
|
|
264
|
-
const meta = data.notice_meta;
|
|
265
|
-
if (!meta?.file_token || !meta.file_type || !data.comment_id)
|
|
266
|
-
return;
|
|
267
|
-
await onDocumentComment({
|
|
268
|
-
eventId: data.event_id ?? '',
|
|
269
|
-
fileToken: meta.file_token,
|
|
270
|
-
fileType: meta.file_type,
|
|
271
|
-
commentId: data.comment_id,
|
|
272
|
-
replyId: data.reply_id ?? '',
|
|
273
|
-
senderOpenId: meta.from_user_id?.open_id ?? '',
|
|
274
|
-
senderUnionId: meta.from_user_id?.union_id ?? '',
|
|
275
|
-
mentionedBot: data.is_mentioned ?? false,
|
|
276
|
-
}, bot);
|
|
277
|
-
},
|
|
278
|
-
});
|
|
279
|
-
const wsClient = new Lark.WSClient({ appId, appSecret });
|
|
280
|
-
wsClient.start({ eventDispatcher: dispatcher });
|
|
281
|
-
return bot;
|
|
282
|
-
}
|
|
283
|
-
async function findRootCommentReplyId(client, comment) {
|
|
284
|
-
const response = await client.drive.v1.fileCommentReply.list({
|
|
285
|
-
path: {
|
|
286
|
-
file_token: comment.fileToken,
|
|
287
|
-
comment_id: comment.commentId,
|
|
288
|
-
},
|
|
289
|
-
params: {
|
|
290
|
-
file_type: comment.fileType,
|
|
291
|
-
page_size: 1,
|
|
292
|
-
user_id_type: 'open_id',
|
|
293
|
-
},
|
|
294
|
-
});
|
|
295
|
-
if (response.code && response.code !== 0) {
|
|
296
|
-
throw new Error(response.msg || '读取飞书文档评论回复失败');
|
|
297
|
-
}
|
|
298
|
-
const replyId = response.data?.items?.[0]?.reply_id;
|
|
299
|
-
if (!replyId)
|
|
300
|
-
throw new Error('飞书文档评论缺少可添加表情的回复 ID');
|
|
301
|
-
return replyId;
|
|
302
|
-
}
|
|
303
|
-
function isRecord(value) {
|
|
304
|
-
return typeof value === 'object' && value !== null;
|
|
305
|
-
}
|
|
306
|
-
//# sourceMappingURL=lark.js.map
|
package/dist/lark.js.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"lark.js","sourceRoot":"","sources":["../src/lark.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,OAAO,KAAK,IAAI,MAAM,yBAAyB,CAAC;AAChD,OAAO,EAAE,KAAK,EAAE,MAAM,kBAAkB,CAAC;AACzC,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAC1C,OAAO,EAAE,aAAa,EAAgB,MAAM,qBAAqB,CAAC;AAgDlE,MAAM,CAAC,MAAM,iBAAiB,GAAG,KAAK,CAAC;AACvC,MAAM,CAAC,MAAM,oBAAoB,GAAG,KAAK,CAAC;AAC1C,MAAM,CAAC,MAAM,oBAAoB,GAAG,KAAK,CAAC;AAE1C,MAAM,UAAU,aAAa,CAAC,IAAY,EAAE,SAAiB;IAC3D,MAAM,UAAU,GAAG,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IACpC,IAAI,UAAU,CAAC,MAAM,IAAI,SAAS;QAAE,OAAO,IAAI,CAAC;IAChD,MAAM,MAAM,GAAG,iCAAiC,CAAC;IACjD,MAAM,YAAY,GAAG,KAAK,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,MAAM,CAAC;IAC/C,OAAO,GAAG,UAAU,CAAC,KAAK,CAAC,CAAC,EAAE,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,SAAS,GAAG,YAAY,CAAC,CAAC,CAAC,IAAI,CAAC,EAAE,CAAC,GAAG,MAAM,EAAE,CAAC;AAC3F,CAAC;AAeD,MAAM,UAAU,eAAe,CAAC,IAAS;IACvC,MAAM,KAAK,GAAG,IAAI,EAAE,MAAM,EAAE,KAAK,CAAC;IAClC,MAAM,SAAS,GAAG,IAAI,EAAE,MAAM,EAAE,UAAU,CAAC;IAC3C,OAAO;QACL,cAAc,EAAE,IAAI,EAAE,QAAQ,EAAE,OAAO,IAAI,IAAI,EAAE,WAAW,EAAE,OAAO,IAAI,EAAE;QAC3E,eAAe,EAAE,IAAI,EAAE,QAAQ,EAAE,QAAQ,IAAI,IAAI,EAAE,WAAW,EAAE,QAAQ,IAAI,EAAE;QAC9E,SAAS,EAAE,IAAI,EAAE,OAAO,EAAE,eAAe,IAAI,IAAI,EAAE,eAAe,IAAI,EAAE;QACxE,KAAK,EAAE,QAAQ,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE;QACnC,SAAS,EAAE,QAAQ,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,EAAE;KAChD,CAAC;AACJ,CAAC;AAiDD,MAAM,UAAU,uBAAuB,CACrC,MAAmB,EACnB,IAAY;IAEZ,MAAM,WAAW,GAAG,aAAa,CAAC,IAAI,EAAE,oBAAoB,CAAC,CAAC;IAC9D,OAAO;QACL,KAAK,EAAE;YACL,KAAK,EAAE,EAAE;YACT,OAAO,EAAE;gBACP;oBACE;wBACE,GAAG,EAAE,IAAI;wBACT,OAAO,EAAE,MAAM,CAAC,MAAM;wBACtB,GAAG,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,SAAS,EAAE,MAAM,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;qBACnD;oBACD,EAAE,GAAG,EAAE,MAAM,EAAE,IAAI,EAAE,IAAI,WAAW,EAAE,EAAE;iBACzC;aACF;SACF;KACF,CAAC;AACJ,CAAC;AAED,KAAK,UAAU,gBAAgB,CAAC,MAAmB;IACjD,MAAM,QAAQ,GAAG,MAAM,MAAM,CAAC,OAAO,CAAC;QACpC,GAAG,EAAE,wBAAwB;QAC7B,MAAM,EAAE,KAAK;KACd,CAAC,CAAC;IACH,MAAM,GAAG,GAAI,QAA8D,CAAC,GAAG,CAAC;IAChF,IAAI,CAAC,GAAG,EAAE,OAAO;QAAE,MAAM,IAAI,KAAK,CAAC,oBAAoB,CAAC,CAAC;IACzD,OAAO,EAAE,MAAM,EAAE,GAAG,CAAC,OAAO,EAAE,IAAI,EAAE,GAAG,CAAC,QAAQ,EAAE,IAAI,EAAE,IAAI,KAAK,EAAE,CAAC;AACtE,CAAC;AAED,MAAM,uBAAuB,GAA2B;IACtD,YAAY,EAAE,KAAK;IACnB,WAAW,EAAE,KAAK;IAClB,WAAW,EAAE,KAAK;IAClB,YAAY,EAAE,MAAM;IACpB,WAAW,EAAE,KAAK;IAClB,cAAc,EAAE,KAAK;CACtB,CAAC;AAEF,SAAS,SAAS,CAAC,OAAY,EAAE,IAAY;IAC3C,MAAM,KAAK,GACT,OAAO,OAAO,EAAE,GAAG,KAAK,UAAU;QAChC,CAAC,CAAC,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC;QACnB,CAAC,CAAC,CAAC,OAAO,EAAE,CAAC,IAAI,CAAC,IAAI,OAAO,EAAE,CAAC,IAAI,CAAC,WAAW,EAAE,CAAC,CAAC,CAAC;IACzD,OAAO,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,KAAK,IAAI,EAAE,CAAC,CAAC;AACjE,CAAC;AAED,SAAS,iBAAiB,CACxB,IAAsB,EACtB,QAA4B,EAC5B,WAAmB;IAEnB,MAAM,QAAQ,GAAG,QAAQ,CAAC,CAAC,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,WAAW,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;IAC1E,IAAI,kBAAkB,CAAC,IAAI,CAAC,QAAQ,CAAC;QAAE,OAAO,QAAQ,CAAC;IAEvD,MAAM,IAAI,GAAG,WAAW,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC;IAC/D,OAAO,uBAAuB,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,KAAK,OAAO,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC;AAC7E,CAAC;AAQD,SAAS,iBAAiB,CAAC,OAAoB;IAC7C,IAAI,OAAO,CAAC,GAAG,KAAK,IAAI;QAAE,OAAO,OAAO,CAAC,OAAO,IAAI,EAAE,CAAC;IACvD,IAAI,OAAO,CAAC,GAAG,KAAK,IAAI;QAAE,OAAO,IAAI,CAAC;IACtC,IAAI,CAAC,MAAM,EAAE,GAAG,EAAE,MAAM,EAAE,YAAY,EAAE,IAAI,CAAC,CAAC,QAAQ,CAAC,OAAO,CAAC,GAAG,IAAI,EAAE,CAAC,EAAE,CAAC;QAC1E,OAAO,OAAO,CAAC,IAAI,IAAI,EAAE,CAAC;IAC5B,CAAC;IACD,OAAO,EAAE,CAAC;AACZ,CAAC;AAED,MAAM,UAAU,kBAAkB,CAChC,WAAmB,EACnB,OAAe;IAEf,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;IACnC,IAAI,WAAW,KAAK,MAAM,EAAE,CAAC;QAC3B,OAAO,MAAM,CAAC,IAAI,IAAI,EAAE,CAAC;IAC3B,CAAC;IACD,IAAI,WAAW,KAAK,MAAM,EAAE,CAAC;QAC3B,MAAM,UAAU,GAAoB,MAAM,CAAC,OAAO,IAAI,EAAE,CAAC;QACzD,OAAO,UAAU;aACd,GAAG,CAAC,CAAC,SAAS,EAAE,EAAE,CAAC,SAAS,CAAC,GAAG,CAAC,iBAAiB,CAAC,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;aAC7D,MAAM,CAAC,OAAO,CAAC;aACf,IAAI,CAAC,IAAI,CAAC;aACV,IAAI,EAAE,CAAC;IACZ,CAAC;IACD,OAAO,EAAE,CAAC;AACZ,CAAC;AAED,MAAM,UAAU,QAAQ,CAAC,IAAgB;IACvC,MAAM,EACJ,KAAK,EACL,SAAS,EACT,SAAS,EACT,YAAY,EACZ,iBAAiB,GAClB,GAAG,IAAI,CAAC;IAET,MAAM,MAAM,GAAG,IAAI,IAAI,CAAC,MAAM,CAAC,EAAE,KAAK,EAAE,SAAS,EAAE,CAAC,CAAC;IAErD,MAAM,GAAG,GAAQ;QACf,MAAM;QAEN,WAAW;YACT,OAAO,gBAAgB,CAAC,MAAM,CAAC,CAAC;QAClC,CAAC;QAED,KAAK,CAAC,KAAK,CAAC,SAAS,EAAE,IAAI,EAAE,aAAa,GAAG,KAAK;YAChD,MAAM,GAAG,GAAG,MAAM,MAAM,CAAC,EAAE,CAAC,EAAE,CAAC,OAAO,CAAC,KAAK,CAAC;gBAC3C,IAAI,EAAE,EAAE,UAAU,EAAE,SAAS,EAAE;gBAC/B,IAAI,EAAE;oBACJ,QAAQ,EAAE,MAAM;oBAChB,OAAO,EAAE,IAAI,CAAC,SAAS,CAAC;wBACtB,IAAI,EAAE,aAAa,CAAC,IAAI,EAAE,iBAAiB,CAAC;qBAC7C,CAAC;oBACF,GAAG,CAAC,aAAa,CAAC,CAAC,CAAC,EAAE,eAAe,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;iBACpD;aACF,CAAC,CAAC;YACH,OAAO,GAAG,CAAC,IAAI,EAAE,UAAU,CAAC;QAC9B,CAAC;QAED,KAAK,CAAC,SAAS,CAAC,SAAS,EAAE,IAAI,EAAE,aAAa,GAAG,KAAK;YACpD,MAAM,GAAG,GAAG,MAAM,MAAM,CAAC,EAAE,CAAC,EAAE,CAAC,OAAO,CAAC,KAAK,CAAC;gBAC3C,IAAI,EAAE,EAAE,UAAU,EAAE,SAAS,EAAE;gBAC/B,IAAI,EAAE;oBACJ,QAAQ,EAAE,aAAa;oBACvB,OAAO,EAAE,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC;oBAC7B,GAAG,CAAC,aAAa,CAAC,CAAC,CAAC,EAAE,eAAe,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;iBACpD;aACF,CAAC,CAAC;YACH,OAAO,GAAG,CAAC,IAAI,EAAE,UAAU,CAAC;QAC9B,CAAC;QAED,KAAK,CAAC,YAAY,CAAC,SAAS,EAAE,MAAM,EAAE,IAAI,EAAE,aAAa,GAAG,KAAK;YAC/D,MAAM,GAAG,GAAG,MAAM,MAAM,CAAC,EAAE,CAAC,EAAE,CAAC,OAAO,CAAC,KAAK,CAAC;gBAC3C,IAAI,EAAE,EAAE,UAAU,EAAE,SAAS,EAAE;gBAC/B,IAAI,EAAE;oBACJ,QAAQ,EAAE,MAAM;oBAChB,OAAO,EAAE,IAAI,CAAC,SAAS,CAAC,uBAAuB,CAAC,MAAM,EAAE,IAAI,CAAC,CAAC;oBAC9D,GAAG,CAAC,aAAa,CAAC,CAAC,CAAC,EAAE,eAAe,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;iBACpD;aACF,CAAC,CAAC;YACH,OAAO,GAAG,CAAC,IAAI,EAAE,UAAU,CAAC;QAC9B,CAAC;QAED,KAAK,CAAC,cAAc,CAAC,MAAM,EAAE,IAAI;YAC/B,MAAM,GAAG,GAAG,MAAM,MAAM,CAAC,EAAE,CAAC,EAAE,CAAC,OAAO,CAAC,MAAM,CAAC;gBAC5C,MAAM,EAAE,EAAE,eAAe,EAAE,SAAS,EAAE;gBACtC,IAAI,EAAE;oBACJ,UAAU,EAAE,MAAM;oBAClB,QAAQ,EAAE,aAAa;oBACvB,OAAO,EAAE,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC;iBAC9B;aACF,CAAC,CAAC;YACH,OAAO,GAAG,CAAC,IAAI,EAAE,UAAU,CAAC;QAC9B,CAAC;QAED,KAAK,CAAC,iBAAiB,CAAC,MAAM,EAAE,MAAM,EAAE,IAAI;YAC1C,MAAM,GAAG,GAAG,MAAM,MAAM,CAAC,EAAE,CAAC,EAAE,CAAC,OAAO,CAAC,MAAM,CAAC;gBAC5C,MAAM,EAAE,EAAE,eAAe,EAAE,SAAS,EAAE;gBACtC,IAAI,EAAE;oBACJ,UAAU,EAAE,MAAM;oBAClB,QAAQ,EAAE,MAAM;oBAChB,OAAO,EAAE,IAAI,CAAC,SAAS,CAAC,uBAAuB,CAAC,MAAM,EAAE,IAAI,CAAC,CAAC;iBAC/D;aACF,CAAC,CAAC;YACH,OAAO,GAAG,CAAC,IAAI,EAAE,UAAU,CAAC;QAC9B,CAAC;QAED,KAAK,CAAC,UAAU,CAAC,SAAS,EAAE,IAAI;YAC9B,MAAM,MAAM,CAAC,EAAE,CAAC,EAAE,CAAC,OAAO,CAAC,KAAK,CAAC;gBAC/B,IAAI,EAAE,EAAE,UAAU,EAAE,SAAS,EAAE;gBAC/B,IAAI,EAAE,EAAE,OAAO,EAAE,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,EAAE;aACxC,CAAC,CAAC;QACL,CAAC;QAED,KAAK,CAAC,2BAA2B;YAC/B,MAAM,QAAQ,GAAG,MAAM,MAAM,CAAC,KAAK,CAAC,EAAE,CAAC,IAAI,CAAC,YAAY,CAAC;gBACvD,IAAI,EAAE,EAAE,UAAU,EAAE,6BAA6B,EAAE;aACpD,CAAC,CAAC;YACH,IAAI,QAAQ,CAAC,IAAI,IAAI,QAAQ,CAAC,IAAI,KAAK,CAAC,EAAE,CAAC;gBACzC,MAAM,IAAI,KAAK,CAAC,QAAQ,CAAC,GAAG,IAAI,cAAc,CAAC,CAAC;YAClD,CAAC;QACH,CAAC;QAED,KAAK,CAAC,sBAAsB,CAAC,OAAO,EAAE,IAAI;YACxC,MAAM,QAAQ,GAAG,MAAM,MAAM,CAAC,KAAK,CAAC,EAAE,CAAC,gBAAgB,CAAC,MAAM,CAAC;gBAC7D,IAAI,EAAE;oBACJ,UAAU,EAAE,OAAO,CAAC,SAAS;oBAC7B,UAAU,EAAE,OAAO,CAAC,SAAS;iBAC9B;gBACD,MAAM,EAAE;oBACN,SAAS,EAAE,OAAO,CAAC,QAMN;oBACb,YAAY,EAAE,SAAS;iBACxB;gBACD,IAAI,EAAE;oBACJ,OAAO,EAAE;wBACP,QAAQ,EAAE,CAAC;gCACT,IAAI,EAAE,UAAU;gCAChB,QAAQ,EAAE;oCACR,IAAI,EAAE,aAAa,CAAC,IAAI,EAAE,oBAAoB,CAAC;iCAChD;6BACF,CAAC;qBACH;iBACF;aACF,CAAC,CAAC;YACH,IAAI,QAAQ,CAAC,IAAI,IAAI,QAAQ,CAAC,IAAI,KAAK,CAAC,EAAE,CAAC;gBACzC,MAAM,IAAI,KAAK,CAAC,QAAQ,CAAC,GAAG,IAAI,YAAY,CAAC,CAAC;YAChD,CAAC;QACH,CAAC;QAED,KAAK,CAAC,yBAAyB,CAAC,OAAO,EAAE,MAAM;YAC7C,MAAM,OAAO,GAAG,OAAO,CAAC,OAAO;mBAC1B,MAAM,sBAAsB,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;YACnD,MAAM,QAAQ,GAAG,MAAM,MAAM,CAAC,KAAK,CAAC,EAAE,CAAC,eAAe,CAAC,cAAc,CAAC;gBACpE,IAAI,EAAE,EAAE,UAAU,EAAE,OAAO,CAAC,SAAS,EAAE;gBACvC,MAAM,EAAE,EAAE,SAAS,EAAE,OAAO,CAAC,QAAQ,EAAE;gBACvC,IAAI,EAAE;oBACJ,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,QAAQ;oBACjC,QAAQ,EAAE,OAAO;oBACjB,aAAa,EAAE,QAAQ;iBACxB;aACF,CAAC,CAAC;YACH,IAAI,QAAQ,CAAC,IAAI,IAAI,QAAQ,CAAC,IAAI,KAAK,CAAC,EAAE,CAAC;gBACzC,MAAM,IAAI,KAAK,CAAC,QAAQ,CAAC,GAAG,IAAI,cAAc,CAAC,CAAC;YAClD,CAAC;QACH,CAAC;QAED,KAAK,CAAC,gBAAgB,CAAC,SAAS,EAAE,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,QAAQ;YAChE,MAAM,GAAG,GAAG,MAAM,MAAM,CAAC,EAAE,CAAC,EAAE,CAAC,eAAe,CAAC,GAAG,CAAC;gBACjD,IAAI,EAAE,EAAE,UAAU,EAAE,SAAS,EAAE,QAAQ,EAAE,OAAO,EAAE;gBAClD,MAAM,EAAE,EAAE,IAAI,EAAE;aACjB,CAAC,CAAC;YACH,MAAM,WAAW,GAAG,SAAS,CAAC,GAAG,CAAC,OAAO,EAAE,cAAc,CAAC,CAAC;YAC3D,MAAM,SAAS,GAAG,iBAAiB,CAAC,IAAI,EAAE,QAAQ,EAAE,WAAW,CAAC,CAAC;YACjE,MAAM,QAAQ,GAAG,IAAI,CAAC,OAAO,EAAE,GAAG,OAAO,IAAI,SAAS,EAAE,CAAC,CAAC;YAC1D,MAAM,KAAK,CAAC,OAAO,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;YAC1C,MAAM,GAAG,CAAC,SAAS,CAAC,QAAQ,CAAC,CAAC;YAC9B,OAAO,QAAQ,CAAC;QAClB,CAAC;KACF,CAAC;IAEF,MAAM,UAAU,GAAG,IAAI,IAAI,CAAC,eAAe,CAAC,EAAE,CAAC,CAAC,QAAQ,CAAC;QACvD,qBAAqB,EAAE,KAAK,EAAE,IAAS,EAAE,EAAE;YACzC,IAAI,CAAC,YAAY;gBAAE,OAAO,SAAS,CAAC;YACpC,OAAO,YAAY,CAAC,eAAe,CAAC,IAAI,CAAC,CAAC,CAAC;QAC7C,CAAC;QACD,uBAAuB,EAAE,KAAK,EAAE,IAAI,EAAE,EAAE;YACtC,MAAM,CAAC,GAAG,IAAI,CAAC,OAAO,CAAC;YACvB,MAAM,GAAG,GAAoB;gBAC3B,SAAS,EAAE,CAAC,CAAC,UAAU;gBACvB,MAAM,EAAE,CAAC,CAAC,OAAO;gBACjB,QAAQ,EAAE,CAAC,CAAC,SAAS;gBACrB,WAAW,EAAE,CAAC,CAAC,YAAY;gBAC3B,IAAI,EAAE,kBAAkB,CAAC,CAAC,CAAC,YAAY,EAAE,CAAC,CAAC,OAAO,CAAC;gBACnD,MAAM,EAAE,CAAC,CAAC,OAAO,IAAI,EAAE;gBACvB,QAAQ,EAAE,CAAC,CAAC,SAAS,IAAI,EAAE;gBAC3B,UAAU,EAAE,IAAI,CAAC,MAAM,CAAC,WAAW,IAAI,EAAE;gBACzC,YAAY,EAAE,IAAI,CAAC,MAAM,CAAC,SAAS,EAAE,OAAO,IAAI,EAAE;gBAClD,aAAa,EAAE,IAAI,CAAC,MAAM,CAAC,SAAS,EAAE,QAAQ,IAAI,EAAE;gBACpD,QAAQ,EAAE,aAAa,CAAC,CAAC,CAAC,QAAQ,CAAC;gBACnC,UAAU,EAAE,CAAC,CAAC,OAAO;aACtB,CAAC;YACF,MAAM,SAAS,CAAC,GAAG,EAAE,GAAG,CAAC,CAAC;QAC5B,CAAC;QACD,6BAA6B,EAAE,KAAK,EAAE,IAAI,EAAE,EAAE;YAC5C,IAAI,CAAC,iBAAiB;gBAAE,OAAO;YAC/B,MAAM,IAAI,GAAG,IAAI,CAAC,WAAW,CAAC;YAC9B,IAAI,CAAC,IAAI,EAAE,UAAU,IAAI,CAAC,IAAI,CAAC,SAAS,IAAI,CAAC,IAAI,CAAC,UAAU;gBAAE,OAAO;YACrE,MAAM,iBAAiB,CACrB;gBACE,OAAO,EAAE,IAAI,CAAC,QAAQ,IAAI,EAAE;gBAC5B,SAAS,EAAE,IAAI,CAAC,UAAU;gBAC1B,QAAQ,EAAE,IAAI,CAAC,SAAS;gBACxB,SAAS,EAAE,IAAI,CAAC,UAAU;gBAC1B,OAAO,EAAE,IAAI,CAAC,QAAQ,IAAI,EAAE;gBAC5B,YAAY,EAAE,IAAI,CAAC,YAAY,EAAE,OAAO,IAAI,EAAE;gBAC9C,aAAa,EAAE,IAAI,CAAC,YAAY,EAAE,QAAQ,IAAI,EAAE;gBAChD,YAAY,EAAE,IAAI,CAAC,YAAY,IAAI,KAAK;aACzC,EACD,GAAG,CACJ,CAAC;QACJ,CAAC;KACF,CAAC,CAAC;IAEH,MAAM,QAAQ,GAAG,IAAI,IAAI,CAAC,QAAQ,CAAC,EAAE,KAAK,EAAE,SAAS,EAAE,CAAC,CAAC;IACzD,QAAQ,CAAC,KAAK,CAAC,EAAE,eAAe,EAAE,UAAU,EAAE,CAAC,CAAC;IAEhD,OAAO,GAAG,CAAC;AACb,CAAC;AAED,KAAK,UAAU,sBAAsB,CACnC,MAAmB,EACnB,OAAgC;IAEhC,MAAM,QAAQ,GAAG,MAAM,MAAM,CAAC,KAAK,CAAC,EAAE,CAAC,gBAAgB,CAAC,IAAI,CAAC;QAC3D,IAAI,EAAE;YACJ,UAAU,EAAE,OAAO,CAAC,SAAS;YAC7B,UAAU,EAAE,OAAO,CAAC,SAAS;SAC9B;QACD,MAAM,EAAE;YACN,SAAS,EAAE,OAAO,CAAC,QAMN;YACb,SAAS,EAAE,CAAC;YACZ,YAAY,EAAE,SAAS;SACxB;KACF,CAAC,CAAC;IACH,IAAI,QAAQ,CAAC,IAAI,IAAI,QAAQ,CAAC,IAAI,KAAK,CAAC,EAAE,CAAC;QACzC,MAAM,IAAI,KAAK,CAAC,QAAQ,CAAC,GAAG,IAAI,cAAc,CAAC,CAAC;IAClD,CAAC;IACD,MAAM,OAAO,GAAG,QAAQ,CAAC,IAAI,EAAE,KAAK,EAAE,CAAC,CAAC,CAAC,EAAE,QAAQ,CAAC;IACpD,IAAI,CAAC,OAAO;QAAE,MAAM,IAAI,KAAK,CAAC,qBAAqB,CAAC,CAAC;IACrD,OAAO,OAAO,CAAC;AACjB,CAAC;AAED,SAAS,QAAQ,CAAC,KAAc;IAC9B,OAAO,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,KAAK,IAAI,CAAC;AACrD,CAAC"}
|
package/dist/message-parser.d.ts
DELETED
|
@@ -1,25 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* 消息解析:@提及还原、图片/文件资源提取。
|
|
3
|
-
*/
|
|
4
|
-
export interface Mention {
|
|
5
|
-
key: string;
|
|
6
|
-
name: string;
|
|
7
|
-
openId: string;
|
|
8
|
-
}
|
|
9
|
-
/**
|
|
10
|
-
* 从事件的 mentions 数组中提取结构化提及信息。
|
|
11
|
-
*/
|
|
12
|
-
export declare function parseMentions(raw: any[] | undefined): Mention[];
|
|
13
|
-
/**
|
|
14
|
-
* 把消息文本里的 @_user_N 占位符替换成 @显示名。
|
|
15
|
-
*/
|
|
16
|
-
export declare function resolveMentions(text: string, mentions: Mention[]): string;
|
|
17
|
-
/**
|
|
18
|
-
* 从消息 content 中提取资源 key(image_key / file_key)。
|
|
19
|
-
*/
|
|
20
|
-
export declare function extractResourceKeys(messageType: string, content: string): {
|
|
21
|
-
type: 'image' | 'file';
|
|
22
|
-
key: string;
|
|
23
|
-
fileName?: string;
|
|
24
|
-
}[];
|
|
25
|
-
//# sourceMappingURL=message-parser.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"message-parser.d.ts","sourceRoot":"","sources":["../src/message-parser.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,MAAM,WAAW,OAAO;IACtB,GAAG,EAAE,MAAM,CAAC;IACZ,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,CAAC;CAChB;AAED;;GAEG;AACH,wBAAgB,aAAa,CAAC,GAAG,EAAE,GAAG,EAAE,GAAG,SAAS,GAAG,OAAO,EAAE,CAO/D;AAED;;GAEG;AACH,wBAAgB,eAAe,CAAC,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,OAAO,EAAE,GAAG,MAAM,CAMzE;AAED;;GAEG;AACH,wBAAgB,mBAAmB,CACjC,WAAW,EAAE,MAAM,EACnB,OAAO,EAAE,MAAM,GACd;IAAE,IAAI,EAAE,OAAO,GAAG,MAAM,CAAC;IAAC,GAAG,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,EAAE,MAAM,CAAA;CAAE,EAAE,CAqB9D"}
|
package/dist/message-parser.js
DELETED
|
@@ -1,48 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* 消息解析:@提及还原、图片/文件资源提取。
|
|
3
|
-
*/
|
|
4
|
-
/**
|
|
5
|
-
* 从事件的 mentions 数组中提取结构化提及信息。
|
|
6
|
-
*/
|
|
7
|
-
export function parseMentions(raw) {
|
|
8
|
-
if (!raw?.length)
|
|
9
|
-
return [];
|
|
10
|
-
return raw.map((m) => ({
|
|
11
|
-
key: m.key,
|
|
12
|
-
name: m.name ?? '',
|
|
13
|
-
openId: m.id?.open_id ?? '',
|
|
14
|
-
}));
|
|
15
|
-
}
|
|
16
|
-
/**
|
|
17
|
-
* 把消息文本里的 @_user_N 占位符替换成 @显示名。
|
|
18
|
-
*/
|
|
19
|
-
export function resolveMentions(text, mentions) {
|
|
20
|
-
let resolved = text;
|
|
21
|
-
for (const m of mentions) {
|
|
22
|
-
resolved = resolved.replaceAll(m.key, `@${m.name}`);
|
|
23
|
-
}
|
|
24
|
-
return resolved.trim();
|
|
25
|
-
}
|
|
26
|
-
/**
|
|
27
|
-
* 从消息 content 中提取资源 key(image_key / file_key)。
|
|
28
|
-
*/
|
|
29
|
-
export function extractResourceKeys(messageType, content) {
|
|
30
|
-
const parsed = JSON.parse(content);
|
|
31
|
-
const resources = [];
|
|
32
|
-
if (messageType === 'image' && parsed.image_key) {
|
|
33
|
-
resources.push({ type: 'image', key: parsed.image_key });
|
|
34
|
-
}
|
|
35
|
-
if (messageType === 'file' && parsed.file_key) {
|
|
36
|
-
resources.push({ type: 'file', key: parsed.file_key, fileName: parsed.file_name });
|
|
37
|
-
}
|
|
38
|
-
if (messageType === 'post') {
|
|
39
|
-
const paragraphs = parsed.content ?? [];
|
|
40
|
-
for (const el of paragraphs.flat()) {
|
|
41
|
-
if (el.tag === 'img' && el.image_key) {
|
|
42
|
-
resources.push({ type: 'image', key: el.image_key });
|
|
43
|
-
}
|
|
44
|
-
}
|
|
45
|
-
}
|
|
46
|
-
return resources;
|
|
47
|
-
}
|
|
48
|
-
//# sourceMappingURL=message-parser.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"message-parser.js","sourceRoot":"","sources":["../src/message-parser.ts"],"names":[],"mappings":"AAAA;;GAEG;AAQH;;GAEG;AACH,MAAM,UAAU,aAAa,CAAC,GAAsB;IAClD,IAAI,CAAC,GAAG,EAAE,MAAM;QAAE,OAAO,EAAE,CAAC;IAC5B,OAAO,GAAG,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;QACrB,GAAG,EAAE,CAAC,CAAC,GAAG;QACV,IAAI,EAAE,CAAC,CAAC,IAAI,IAAI,EAAE;QAClB,MAAM,EAAE,CAAC,CAAC,EAAE,EAAE,OAAO,IAAI,EAAE;KAC5B,CAAC,CAAC,CAAC;AACN,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,eAAe,CAAC,IAAY,EAAE,QAAmB;IAC/D,IAAI,QAAQ,GAAG,IAAI,CAAC;IACpB,KAAK,MAAM,CAAC,IAAI,QAAQ,EAAE,CAAC;QACzB,QAAQ,GAAG,QAAQ,CAAC,UAAU,CAAC,CAAC,CAAC,GAAG,EAAE,IAAI,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC;IACtD,CAAC;IACD,OAAO,QAAQ,CAAC,IAAI,EAAE,CAAC;AACzB,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,mBAAmB,CACjC,WAAmB,EACnB,OAAe;IAEf,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;IACnC,MAAM,SAAS,GAAiE,EAAE,CAAC;IAEnF,IAAI,WAAW,KAAK,OAAO,IAAI,MAAM,CAAC,SAAS,EAAE,CAAC;QAChD,SAAS,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,OAAO,EAAE,GAAG,EAAE,MAAM,CAAC,SAAS,EAAE,CAAC,CAAC;IAC3D,CAAC;IACD,IAAI,WAAW,KAAK,MAAM,IAAI,MAAM,CAAC,QAAQ,EAAE,CAAC;QAC9C,SAAS,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,CAAC,QAAQ,EAAE,QAAQ,EAAE,MAAM,CAAC,SAAS,EAAE,CAAC,CAAC;IACrF,CAAC;IAED,IAAI,WAAW,KAAK,MAAM,EAAE,CAAC;QAC3B,MAAM,UAAU,GAAY,MAAM,CAAC,OAAO,IAAI,EAAE,CAAC;QACjD,KAAK,MAAM,EAAE,IAAI,UAAU,CAAC,IAAI,EAAE,EAAE,CAAC;YACnC,IAAI,EAAE,CAAC,GAAG,KAAK,KAAK,IAAI,EAAE,CAAC,SAAS,EAAE,CAAC;gBACrC,SAAS,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,OAAO,EAAE,GAAG,EAAE,EAAE,CAAC,SAAS,EAAE,CAAC,CAAC;YACvD,CAAC;QACH,CAAC;IACH,CAAC;IAED,OAAO,SAAS,CAAC;AACnB,CAAC"}
|