From 6d3c59bd79e52c759a1ecafcd56475a9aef373b0 Mon Sep 17 00:00:00 2001 From: wangweiming Date: Wed, 1 Apr 2026 13:14:30 +0800 Subject: [PATCH] Feishu: route comment targets through comment replies --- extensions/feishu/src/monitor.comment.test.ts | 4 +- extensions/feishu/src/outbound.test.ts | 44 +++++++++++++++++++ extensions/feishu/src/outbound.ts | 10 +++++ 3 files changed, 56 insertions(+), 2 deletions(-) diff --git a/extensions/feishu/src/monitor.comment.test.ts b/extensions/feishu/src/monitor.comment.test.ts index 198919d5575..fee1184e5af 100644 --- a/extensions/feishu/src/monitor.comment.test.ts +++ b/extensions/feishu/src/monitor.comment.test.ts @@ -310,7 +310,7 @@ describe("resolveDriveCommentEventTurn", () => { }); it("skips comment notices when bot open_id is unavailable", async () => { - const synthetic = await resolveDriveCommentSyntheticEvent({ + const turn = await resolveDriveCommentEventTurn({ cfg: buildMonitorConfig(), accountId: "default", event: makeDriveCommentEvent(), @@ -318,7 +318,7 @@ describe("resolveDriveCommentEventTurn", () => { createClient: () => makeOpenApiClient({}) as never, }); - expect(synthetic).toBeNull(); + expect(turn).toBeNull(); }); }); diff --git a/extensions/feishu/src/outbound.test.ts b/extensions/feishu/src/outbound.test.ts index 5a3bb01ddb7..4eb5924fc07 100644 --- a/extensions/feishu/src/outbound.test.ts +++ b/extensions/feishu/src/outbound.test.ts @@ -235,6 +235,50 @@ describe("feishuOutbound comment-thread routing", () => { expect(result).toEqual(expect.objectContaining({ channel: "feishu", messageId: "reply_msg" })); }); + it("routes comment-thread code-block replies through replyComment instead of IM cards", async () => { + const result = await sendText({ + cfg: emptyConfig, + to: "comment:docx:doxcn123:7623358762119646411", + text: "```ts\nconst x = 1\n```", + accountId: "main", + }); + + expect(replyCommentMock).toHaveBeenCalledWith( + expect.anything(), + expect.objectContaining({ + file_token: "doxcn123", + file_type: "docx", + comment_id: "7623358762119646411", + content: "```ts\nconst x = 1\n```", + }), + ); + expect(sendStructuredCardFeishuMock).not.toHaveBeenCalled(); + expect(sendMarkdownCardFeishuMock).not.toHaveBeenCalled(); + expect(result).toEqual(expect.objectContaining({ channel: "feishu", messageId: "reply_msg" })); + }); + + it("routes comment-thread replies through replyComment even when renderMode=card", async () => { + const result = await sendText({ + cfg: cardRenderConfig, + to: "comment:docx:doxcn123:7623358762119646411", + text: "handled in thread", + accountId: "main", + }); + + expect(replyCommentMock).toHaveBeenCalledWith( + expect.anything(), + expect.objectContaining({ + file_token: "doxcn123", + file_type: "docx", + comment_id: "7623358762119646411", + content: "handled in thread", + }), + ); + expect(sendStructuredCardFeishuMock).not.toHaveBeenCalled(); + expect(sendMarkdownCardFeishuMock).not.toHaveBeenCalled(); + expect(result).toEqual(expect.objectContaining({ channel: "feishu", messageId: "reply_msg" })); + }); + it("falls back to a text-only comment reply for media payloads", async () => { const result = await feishuOutbound.sendMedia?.({ cfg: emptyConfig, diff --git a/extensions/feishu/src/outbound.ts b/extensions/feishu/src/outbound.ts index 97b38002f47..b12a5e7357a 100644 --- a/extensions/feishu/src/outbound.ts +++ b/extensions/feishu/src/outbound.ts @@ -153,6 +153,16 @@ export const feishuOutbound: ChannelOutboundAdapter = { } } + if (parseFeishuCommentTarget(to)) { + return await sendOutboundText({ + cfg, + to, + text, + accountId: accountId ?? undefined, + replyToMessageId, + }); + } + const account = resolveFeishuAccount({ cfg, accountId: accountId ?? undefined }); const renderMode = account.config?.renderMode ?? "auto"; const useCard = renderMode === "card" || (renderMode === "auto" && shouldUseCard(text));