@max1874/feishu 0.2.12 → 0.2.14
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +1 -1
- package/src/bot.ts +22 -9
- package/src/docx.ts +262 -86
- package/src/send.ts +5 -0
package/package.json
CHANGED
package/src/bot.ts
CHANGED
|
@@ -227,7 +227,7 @@ function parsePostContent(content: string): {
|
|
|
227
227
|
* Format merge_forward child messages into readable text.
|
|
228
228
|
*/
|
|
229
229
|
function formatMergeForwardContent(
|
|
230
|
-
messages: Array<{ content: string; contentType: string }>,
|
|
230
|
+
messages: Array<{ content: string; contentType: string; senderName?: string; senderId?: string }>,
|
|
231
231
|
): string {
|
|
232
232
|
if (messages.length === 0) {
|
|
233
233
|
return "[转发的聊天记录 - 无内容]";
|
|
@@ -236,20 +236,22 @@ function formatMergeForwardContent(
|
|
|
236
236
|
const lines: string[] = ["[转发的聊天记录]", "---"];
|
|
237
237
|
|
|
238
238
|
for (const msg of messages) {
|
|
239
|
+
const speaker = msg.senderName || msg.senderId || "未知";
|
|
240
|
+
|
|
239
241
|
if (msg.contentType === "text" || msg.contentType === "post") {
|
|
240
|
-
lines.push(msg.content);
|
|
242
|
+
lines.push(`${speaker}: ${msg.content}`);
|
|
241
243
|
} else if (msg.contentType === "image") {
|
|
242
|
-
lines.push(
|
|
244
|
+
lines.push(`${speaker}: [图片]`);
|
|
243
245
|
} else if (msg.contentType === "file") {
|
|
244
|
-
lines.push(
|
|
246
|
+
lines.push(`${speaker}: [文件]`);
|
|
245
247
|
} else if (msg.contentType === "audio") {
|
|
246
|
-
lines.push(
|
|
248
|
+
lines.push(`${speaker}: [语音]`);
|
|
247
249
|
} else if (msg.contentType === "video") {
|
|
248
|
-
lines.push(
|
|
250
|
+
lines.push(`${speaker}: [视频]`);
|
|
249
251
|
} else if (msg.contentType === "sticker") {
|
|
250
|
-
lines.push(
|
|
252
|
+
lines.push(`${speaker}: [表情]`);
|
|
251
253
|
} else {
|
|
252
|
-
lines.push(
|
|
254
|
+
lines.push(`${speaker}: [${msg.contentType}]`);
|
|
253
255
|
}
|
|
254
256
|
}
|
|
255
257
|
|
|
@@ -488,7 +490,18 @@ export async function handleFeishuMessage(params: {
|
|
|
488
490
|
cfg,
|
|
489
491
|
messageId: ctx.messageId,
|
|
490
492
|
});
|
|
491
|
-
|
|
493
|
+
|
|
494
|
+
// Resolve sender names for child messages (only when ID type is open_id)
|
|
495
|
+
const messagesWithNames = await Promise.all(
|
|
496
|
+
childMessages.map(async (msg) => ({
|
|
497
|
+
...msg,
|
|
498
|
+
senderName: (msg.senderId && msg.senderIdType === "open_id")
|
|
499
|
+
? await resolveFeishuSenderName({ feishuCfg, senderOpenId: msg.senderId, log })
|
|
500
|
+
: undefined,
|
|
501
|
+
}))
|
|
502
|
+
);
|
|
503
|
+
|
|
504
|
+
const formattedContent = formatMergeForwardContent(messagesWithNames);
|
|
492
505
|
ctx = { ...ctx, content: formattedContent };
|
|
493
506
|
log(`feishu: resolved merge_forward with ${childMessages.length} child message(s)`);
|
|
494
507
|
} catch (err) {
|
package/src/docx.ts
CHANGED
|
@@ -263,6 +263,231 @@ function cleanBlocksForInsert(blocks: any[]): { cleaned: any[]; skipped: string[
|
|
|
263
263
|
return { cleaned, skipped };
|
|
264
264
|
}
|
|
265
265
|
|
|
266
|
+
// ============ Helpers for Recursive Insert ============
|
|
267
|
+
|
|
268
|
+
/** Delay helper for rate limiting */
|
|
269
|
+
const delay = (ms: number) => new Promise(resolve => setTimeout(resolve, ms));
|
|
270
|
+
|
|
271
|
+
/** API call with retry for rate limiting */
|
|
272
|
+
async function createChildrenWithRetry(
|
|
273
|
+
client: Lark.Client,
|
|
274
|
+
docToken: string,
|
|
275
|
+
parentBlockId: string,
|
|
276
|
+
children: any[],
|
|
277
|
+
index?: number,
|
|
278
|
+
retries = 3
|
|
279
|
+
): Promise<any> {
|
|
280
|
+
for (let i = 0; i < retries; i++) {
|
|
281
|
+
try {
|
|
282
|
+
const res = await client.docx.documentBlockChildren.create({
|
|
283
|
+
path: { document_id: docToken, block_id: parentBlockId },
|
|
284
|
+
data: {
|
|
285
|
+
children,
|
|
286
|
+
...(index !== undefined && { index })
|
|
287
|
+
},
|
|
288
|
+
});
|
|
289
|
+
if (res.code === 0) return res;
|
|
290
|
+
// Rate limit error
|
|
291
|
+
if (res.code === 99991400 || res.msg?.includes('rate')) {
|
|
292
|
+
await delay((i + 1) * 2000);
|
|
293
|
+
continue;
|
|
294
|
+
}
|
|
295
|
+
return res;
|
|
296
|
+
} catch (e: any) {
|
|
297
|
+
if (e.message?.includes('429') || e.response?.status === 429) {
|
|
298
|
+
await delay((i + 1) * 2000);
|
|
299
|
+
continue;
|
|
300
|
+
}
|
|
301
|
+
throw e;
|
|
302
|
+
}
|
|
303
|
+
}
|
|
304
|
+
return { code: -1, msg: 'Max retries exceeded' };
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
/** Context for recursive block insertion */
|
|
308
|
+
interface InsertContext {
|
|
309
|
+
client: Lark.Client;
|
|
310
|
+
docToken: string;
|
|
311
|
+
blockMap: Map<string, any>;
|
|
312
|
+
tableRelatedIds: Set<string>;
|
|
313
|
+
tableDataMap: Map<string, TableData>;
|
|
314
|
+
insertedCount: number;
|
|
315
|
+
tablesCreated: number;
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
/** Recursively insert a block and its children */
|
|
319
|
+
async function insertBlockWithChildren(
|
|
320
|
+
ctx: InsertContext,
|
|
321
|
+
block: any,
|
|
322
|
+
parentBlockId: string,
|
|
323
|
+
index?: number
|
|
324
|
+
): Promise<string | null> {
|
|
325
|
+
// Skip table-related blocks (handled separately)
|
|
326
|
+
if (ctx.tableRelatedIds.has(block.block_id)) {
|
|
327
|
+
if (block.block_type === TABLE_BLOCK_TYPE) {
|
|
328
|
+
const tableData = ctx.tableDataMap.get(block.block_id);
|
|
329
|
+
if (tableData) {
|
|
330
|
+
return await createTableWithRetry(ctx, parentBlockId, tableData, index);
|
|
331
|
+
}
|
|
332
|
+
}
|
|
333
|
+
return null;
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
// Clean block (remove read-only fields but preserve children reference)
|
|
337
|
+
const { block_id, parent_id, children, ...cleanBlock } = block;
|
|
338
|
+
|
|
339
|
+
// Add delay to avoid rate limiting
|
|
340
|
+
await delay(100);
|
|
341
|
+
|
|
342
|
+
// Insert current block
|
|
343
|
+
const insertRes = await createChildrenWithRetry(
|
|
344
|
+
ctx.client,
|
|
345
|
+
ctx.docToken,
|
|
346
|
+
parentBlockId,
|
|
347
|
+
[cleanBlock],
|
|
348
|
+
index
|
|
349
|
+
);
|
|
350
|
+
|
|
351
|
+
if (insertRes.code !== 0) {
|
|
352
|
+
console.error(`Failed to insert block: ${insertRes.msg}`);
|
|
353
|
+
return null;
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
const newBlockId = insertRes.data?.children?.[0]?.block_id;
|
|
357
|
+
if (!newBlockId) return null;
|
|
358
|
+
|
|
359
|
+
ctx.insertedCount++;
|
|
360
|
+
|
|
361
|
+
// Recursively insert children
|
|
362
|
+
if (children && children.length > 0) {
|
|
363
|
+
for (let i = 0; i < children.length; i++) {
|
|
364
|
+
const childId = children[i];
|
|
365
|
+
const childBlock = ctx.blockMap.get(childId);
|
|
366
|
+
if (childBlock) {
|
|
367
|
+
await insertBlockWithChildren(ctx, childBlock, newBlockId, i);
|
|
368
|
+
}
|
|
369
|
+
}
|
|
370
|
+
}
|
|
371
|
+
|
|
372
|
+
return newBlockId;
|
|
373
|
+
}
|
|
374
|
+
|
|
375
|
+
/** Create and fill a table with retry */
|
|
376
|
+
async function createTableWithRetry(
|
|
377
|
+
ctx: InsertContext,
|
|
378
|
+
parentBlockId: string,
|
|
379
|
+
tableData: TableData,
|
|
380
|
+
index?: number
|
|
381
|
+
): Promise<string | null> {
|
|
382
|
+
await delay(100);
|
|
383
|
+
|
|
384
|
+
const createRes = await createChildrenWithRetry(
|
|
385
|
+
ctx.client,
|
|
386
|
+
ctx.docToken,
|
|
387
|
+
parentBlockId,
|
|
388
|
+
[{
|
|
389
|
+
block_type: TABLE_BLOCK_TYPE,
|
|
390
|
+
table: {
|
|
391
|
+
property: { row_size: tableData.rowSize, column_size: tableData.colSize, header_row: true },
|
|
392
|
+
cells: []
|
|
393
|
+
}
|
|
394
|
+
}],
|
|
395
|
+
index
|
|
396
|
+
);
|
|
397
|
+
|
|
398
|
+
if (createRes.code !== 0) {
|
|
399
|
+
console.error(`Failed to create table: ${createRes.msg}`);
|
|
400
|
+
return null;
|
|
401
|
+
}
|
|
402
|
+
|
|
403
|
+
const tableBlockId = createRes.data?.children?.[0]?.block_id;
|
|
404
|
+
if (!tableBlockId) return null;
|
|
405
|
+
|
|
406
|
+
// Fill table content
|
|
407
|
+
const cellBlocksRes = await ctx.client.docx.documentBlockChildren.get({
|
|
408
|
+
path: { document_id: ctx.docToken, block_id: tableBlockId },
|
|
409
|
+
});
|
|
410
|
+
const cellBlocks = cellBlocksRes.data?.items ?? [];
|
|
411
|
+
|
|
412
|
+
let cellIndex = 0;
|
|
413
|
+
for (let row = 0; row < tableData.rowSize; row++) {
|
|
414
|
+
for (let col = 0; col < tableData.colSize; col++) {
|
|
415
|
+
if (cellIndex >= cellBlocks.length) break;
|
|
416
|
+
const cellBlockId = cellBlocks[cellIndex]?.block_id;
|
|
417
|
+
const cellText = tableData.cells[row]?.[col] ?? "";
|
|
418
|
+
if (cellBlockId && cellText) {
|
|
419
|
+
await delay(50);
|
|
420
|
+
const cellChildrenRes = await ctx.client.docx.documentBlockChildren.get({
|
|
421
|
+
path: { document_id: ctx.docToken, block_id: cellBlockId },
|
|
422
|
+
});
|
|
423
|
+
const textBlockId = cellChildrenRes.data?.items?.[0]?.block_id;
|
|
424
|
+
if (textBlockId) {
|
|
425
|
+
await ctx.client.docx.documentBlock.patch({
|
|
426
|
+
path: { document_id: ctx.docToken, block_id: textBlockId },
|
|
427
|
+
data: { update_text_elements: { elements: [{ text_run: { content: cellText } }] } },
|
|
428
|
+
});
|
|
429
|
+
}
|
|
430
|
+
}
|
|
431
|
+
cellIndex++;
|
|
432
|
+
}
|
|
433
|
+
}
|
|
434
|
+
|
|
435
|
+
ctx.tablesCreated++;
|
|
436
|
+
return tableBlockId;
|
|
437
|
+
}
|
|
438
|
+
|
|
439
|
+
/** Insert blocks recursively preserving nested structure */
|
|
440
|
+
async function insertBlocksRecursively(
|
|
441
|
+
client: Lark.Client,
|
|
442
|
+
docToken: string,
|
|
443
|
+
blocks: any[],
|
|
444
|
+
firstLevelBlockIds: string[],
|
|
445
|
+
startIndex: number = 0
|
|
446
|
+
): Promise<{ blocksInserted: number; tablesCreated: number }> {
|
|
447
|
+
// Build block map
|
|
448
|
+
const blockMap = new Map<string, any>();
|
|
449
|
+
for (const block of blocks) {
|
|
450
|
+
if (block.block_id) {
|
|
451
|
+
blockMap.set(block.block_id, block);
|
|
452
|
+
}
|
|
453
|
+
}
|
|
454
|
+
|
|
455
|
+
// Extract table data and related IDs
|
|
456
|
+
const tableRelatedIds = new Set<string>();
|
|
457
|
+
const tableDataMap = new Map<string, TableData>();
|
|
458
|
+
|
|
459
|
+
for (const block of blocks) {
|
|
460
|
+
if (block.block_type === TABLE_BLOCK_TYPE) {
|
|
461
|
+
const { tableData, relatedIds } = extractTableData(block, blockMap);
|
|
462
|
+
tableDataMap.set(block.block_id, tableData);
|
|
463
|
+
for (const id of relatedIds) {
|
|
464
|
+
tableRelatedIds.add(id);
|
|
465
|
+
}
|
|
466
|
+
}
|
|
467
|
+
}
|
|
468
|
+
|
|
469
|
+
const ctx: InsertContext = {
|
|
470
|
+
client,
|
|
471
|
+
docToken,
|
|
472
|
+
blockMap,
|
|
473
|
+
tableRelatedIds,
|
|
474
|
+
tableDataMap,
|
|
475
|
+
insertedCount: 0,
|
|
476
|
+
tablesCreated: 0,
|
|
477
|
+
};
|
|
478
|
+
|
|
479
|
+
// Insert blocks in order of first_level_block_ids
|
|
480
|
+
for (let i = 0; i < firstLevelBlockIds.length; i++) {
|
|
481
|
+
const blockId = firstLevelBlockIds[i];
|
|
482
|
+
const block = blockMap.get(blockId);
|
|
483
|
+
if (block) {
|
|
484
|
+
await insertBlockWithChildren(ctx, block, docToken, startIndex + i);
|
|
485
|
+
}
|
|
486
|
+
}
|
|
487
|
+
|
|
488
|
+
return { blocksInserted: ctx.insertedCount, tablesCreated: ctx.tablesCreated };
|
|
489
|
+
}
|
|
490
|
+
|
|
266
491
|
// ============ Core Functions ============
|
|
267
492
|
|
|
268
493
|
/** Convert markdown to Feishu blocks using the Convert API */
|
|
@@ -811,124 +1036,75 @@ async function writeDoc(client: Lark.Client, docToken: string, markdown: string)
|
|
|
811
1036
|
const deleted = await clearDocumentContent(client, docToken);
|
|
812
1037
|
|
|
813
1038
|
// 2. Convert markdown to blocks
|
|
814
|
-
const { blocks } = await convertMarkdown(client, markdown);
|
|
1039
|
+
const { blocks, firstLevelBlockIds } = await convertMarkdown(client, markdown);
|
|
815
1040
|
if (blocks.length === 0) {
|
|
816
1041
|
return { success: true, blocks_deleted: deleted, blocks_added: 0, images_processed: 0, tables_created: 0 };
|
|
817
1042
|
}
|
|
818
1043
|
|
|
819
|
-
// 3.
|
|
820
|
-
const
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
let pendingBlocks: any[] = [];
|
|
828
|
-
|
|
829
|
-
const flushPendingBlocks = async () => {
|
|
830
|
-
if (pendingBlocks.length === 0) return;
|
|
831
|
-
const { children, skipped } = await insertBlocksAtIndex(client, docToken, pendingBlocks, insertIndex);
|
|
832
|
-
allInserted.push(...children);
|
|
833
|
-
allSkipped.push(...skipped);
|
|
834
|
-
insertIndex += children.length;
|
|
835
|
-
pendingBlocks = [];
|
|
836
|
-
};
|
|
837
|
-
|
|
838
|
-
for (const item of contentItems) {
|
|
839
|
-
if (item.type === "block") {
|
|
840
|
-
pendingBlocks.push(item.block);
|
|
841
|
-
} else if (item.type === "table") {
|
|
842
|
-
await flushPendingBlocks();
|
|
843
|
-
const success = await createAndFillTableAtIndex(client, docToken, docToken, item.table, insertIndex);
|
|
844
|
-
if (success) {
|
|
845
|
-
tablesCreated++;
|
|
846
|
-
insertIndex++;
|
|
847
|
-
}
|
|
848
|
-
}
|
|
849
|
-
}
|
|
850
|
-
|
|
851
|
-
await flushPendingBlocks();
|
|
1044
|
+
// 3. Insert blocks recursively (preserves nested structure)
|
|
1045
|
+
const { blocksInserted, tablesCreated } = await insertBlocksRecursively(
|
|
1046
|
+
client,
|
|
1047
|
+
docToken,
|
|
1048
|
+
blocks,
|
|
1049
|
+
firstLevelBlockIds,
|
|
1050
|
+
0
|
|
1051
|
+
);
|
|
852
1052
|
|
|
853
|
-
//
|
|
854
|
-
const
|
|
1053
|
+
// 4. Process images (get inserted blocks for image processing)
|
|
1054
|
+
const insertedRes = await client.docx.documentBlock.list({
|
|
1055
|
+
path: { document_id: docToken },
|
|
1056
|
+
params: { page_size: 500 },
|
|
1057
|
+
});
|
|
1058
|
+
const insertedBlocks = insertedRes.data?.items ?? [];
|
|
1059
|
+
const imagesProcessed = await processImages(client, docToken, markdown, insertedBlocks);
|
|
855
1060
|
|
|
856
1061
|
return {
|
|
857
1062
|
success: true,
|
|
858
1063
|
blocks_deleted: deleted,
|
|
859
|
-
blocks_added:
|
|
1064
|
+
blocks_added: blocksInserted,
|
|
860
1065
|
tables_created: tablesCreated,
|
|
861
1066
|
images_processed: imagesProcessed,
|
|
862
|
-
...(allSkipped.length > 0 && {
|
|
863
|
-
warning: `Skipped unsupported block types: ${allSkipped.join(", ")}.`,
|
|
864
|
-
}),
|
|
865
1067
|
};
|
|
866
1068
|
}
|
|
867
1069
|
|
|
868
1070
|
async function appendDoc(client: Lark.Client, docToken: string, markdown: string) {
|
|
869
1071
|
// 1. Convert markdown to blocks
|
|
870
|
-
const { blocks } = await convertMarkdown(client, markdown);
|
|
1072
|
+
const { blocks, firstLevelBlockIds } = await convertMarkdown(client, markdown);
|
|
871
1073
|
if (blocks.length === 0) {
|
|
872
1074
|
throw new Error("Content is empty");
|
|
873
1075
|
}
|
|
874
1076
|
|
|
875
|
-
// 2.
|
|
876
|
-
|
|
877
|
-
|
|
878
|
-
// 3. Get current insert index
|
|
879
|
-
let insertIndex = 0;
|
|
1077
|
+
// 2. Get current insert index
|
|
1078
|
+
let startIndex = 0;
|
|
880
1079
|
const existing = await client.docx.documentBlockChildren.get({
|
|
881
1080
|
path: { document_id: docToken, block_id: docToken },
|
|
882
1081
|
});
|
|
883
1082
|
if (existing.code === 0) {
|
|
884
|
-
|
|
885
|
-
}
|
|
886
|
-
|
|
887
|
-
// 4. Insert content items in order, batching consecutive blocks
|
|
888
|
-
const allInserted: any[] = [];
|
|
889
|
-
const allSkipped: string[] = [];
|
|
890
|
-
let tablesCreated = 0;
|
|
891
|
-
let pendingBlocks: any[] = [];
|
|
892
|
-
|
|
893
|
-
const flushPendingBlocks = async () => {
|
|
894
|
-
if (pendingBlocks.length === 0) return;
|
|
895
|
-
const { children, skipped } = await insertBlocksAtIndex(client, docToken, pendingBlocks, insertIndex);
|
|
896
|
-
allInserted.push(...children);
|
|
897
|
-
allSkipped.push(...skipped);
|
|
898
|
-
insertIndex += children.length;
|
|
899
|
-
pendingBlocks = [];
|
|
900
|
-
};
|
|
901
|
-
|
|
902
|
-
for (const item of contentItems) {
|
|
903
|
-
if (item.type === "block") {
|
|
904
|
-
pendingBlocks.push(item.block);
|
|
905
|
-
} else if (item.type === "table") {
|
|
906
|
-
// Flush pending blocks before creating table
|
|
907
|
-
await flushPendingBlocks();
|
|
908
|
-
// Create table at current position
|
|
909
|
-
const success = await createAndFillTableAtIndex(client, docToken, docToken, item.table, insertIndex);
|
|
910
|
-
if (success) {
|
|
911
|
-
tablesCreated++;
|
|
912
|
-
insertIndex++;
|
|
913
|
-
}
|
|
914
|
-
}
|
|
1083
|
+
startIndex = existing.data?.items?.length ?? 0;
|
|
915
1084
|
}
|
|
916
1085
|
|
|
917
|
-
//
|
|
918
|
-
await
|
|
1086
|
+
// 3. Insert blocks recursively (preserves nested structure)
|
|
1087
|
+
const { blocksInserted, tablesCreated } = await insertBlocksRecursively(
|
|
1088
|
+
client,
|
|
1089
|
+
docToken,
|
|
1090
|
+
blocks,
|
|
1091
|
+
firstLevelBlockIds,
|
|
1092
|
+
startIndex
|
|
1093
|
+
);
|
|
919
1094
|
|
|
920
|
-
//
|
|
921
|
-
const
|
|
1095
|
+
// 4. Process images (get inserted blocks for image processing)
|
|
1096
|
+
const insertedRes = await client.docx.documentBlock.list({
|
|
1097
|
+
path: { document_id: docToken },
|
|
1098
|
+
params: { page_size: 500 },
|
|
1099
|
+
});
|
|
1100
|
+
const insertedBlocks = insertedRes.data?.items ?? [];
|
|
1101
|
+
const imagesProcessed = await processImages(client, docToken, markdown, insertedBlocks);
|
|
922
1102
|
|
|
923
1103
|
return {
|
|
924
1104
|
success: true,
|
|
925
|
-
blocks_added:
|
|
1105
|
+
blocks_added: blocksInserted,
|
|
926
1106
|
tables_created: tablesCreated,
|
|
927
1107
|
images_processed: imagesProcessed,
|
|
928
|
-
block_ids: allInserted.map((b: any) => b.block_id),
|
|
929
|
-
...(allSkipped.length > 0 && {
|
|
930
|
-
warning: `Skipped unsupported block types: ${allSkipped.join(", ")}.`,
|
|
931
|
-
}),
|
|
932
1108
|
};
|
|
933
1109
|
}
|
|
934
1110
|
|
package/src/send.ts
CHANGED
|
@@ -22,6 +22,8 @@ export type FeishuMergeForwardMessage = {
|
|
|
22
22
|
content: string;
|
|
23
23
|
contentType: string;
|
|
24
24
|
upperMessageId?: string;
|
|
25
|
+
senderId?: string;
|
|
26
|
+
senderIdType?: string;
|
|
25
27
|
};
|
|
26
28
|
|
|
27
29
|
/**
|
|
@@ -124,6 +126,7 @@ export async function getMergeForwardMessagesFeishu(params: {
|
|
|
124
126
|
msg_type?: string;
|
|
125
127
|
body?: { content?: string };
|
|
126
128
|
upper_message_id?: string;
|
|
129
|
+
sender?: { id?: string; id_type?: string };
|
|
127
130
|
}>;
|
|
128
131
|
};
|
|
129
132
|
};
|
|
@@ -161,6 +164,8 @@ export async function getMergeForwardMessagesFeishu(params: {
|
|
|
161
164
|
content,
|
|
162
165
|
contentType: item.msg_type ?? "text",
|
|
163
166
|
upperMessageId: item.upper_message_id,
|
|
167
|
+
senderId: item.sender?.id,
|
|
168
|
+
senderIdType: item.sender?.id_type,
|
|
164
169
|
});
|
|
165
170
|
}
|
|
166
171
|
|