Skip to content

Commit dce6aac

Browse files
committed
refactor(feishu): tighten fallback command marker
1 parent bd44abb commit dce6aac

2 files changed

Lines changed: 35 additions & 35 deletions

File tree

extensions/feishu/src/outbound.test.ts

Lines changed: 23 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -923,13 +923,7 @@ describe("feishuOutbound.sendPayload native cards", () => {
923923
});
924924

925925
it("adds command guidance when presentation is stripped but channelData carries the rendered-command marker", async () => {
926-
// Simulates the core delivery path: renderPresentationForDelivery strips
927-
// the presentation field, but renderFeishuPresentationPayload sets
928-
// hasRenderedCommandAction in channelData.feishu so the comment-target
929-
// branch in sendPayload can still detect that a command was rendered.
930-
// The payload includes a minimal card in channelData.feishu.card so
931-
// buildFeishuPayloadCard does not return undefined and skip the
932-
// comment-target handler entirely.
926+
// Core strips presentation before sendPayload; channelData retains the fact.
933927
const result = await feishuOutbound.sendPayload?.({
934928
cfg: emptyConfig,
935929
to: "comment:docx:doxcn123:7623358762119646411",
@@ -940,7 +934,7 @@ describe("feishuOutbound.sendPayload native cards", () => {
940934
channelData: {
941935
feishu: {
942936
card: { body: { elements: [{ tag: "hr" }] } },
943-
hasRenderedCommandAction: true,
937+
fallbackHasCommand: true,
944938
},
945939
},
946940
},
@@ -952,6 +946,27 @@ describe("feishuOutbound.sendPayload native cards", () => {
952946
);
953947
expectFeishuResult(result, "reply_msg");
954948
});
949+
950+
it("ignores non-boolean fallback command markers", async () => {
951+
const result = await feishuOutbound.sendPayload?.({
952+
cfg: emptyConfig,
953+
to: "comment:docx:doxcn123:7623358762119646411",
954+
text: "Review this",
955+
accountId: "main",
956+
payload: {
957+
text: "Review this",
958+
channelData: {
959+
feishu: {
960+
card: { body: { elements: [{ tag: "hr" }] } },
961+
fallbackHasCommand: "true",
962+
},
963+
},
964+
},
965+
});
966+
967+
expect(commentThreadParams()?.content).toBe("Review this");
968+
expectFeishuResult(result, "reply_msg");
969+
});
955970
});
956971

957972
describe("feishuOutbound comment-thread routing", () => {

extensions/feishu/src/outbound.ts

Lines changed: 12 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -321,15 +321,9 @@ function buildFeishuPayloadCard(params: {
321321
});
322322
}
323323

324-
/**
325-
* Check whether any presentation block contains a visible command action
326-
* that would be rendered as copyable text in the fallback output.
327-
*
328-
* Must match the same conditions used by the shared
329-
* {@link renderMessagePresentationFallbackText} so that Feishu comment-thread
330-
* guidance is only shown when a copyable command is actually rendered.
331-
*/
332-
function hasRenderedCommandAction(
324+
// Keep this aligned with the shared fallback renderer: guidance is valid only
325+
// when the fallback text exposes a command the user can copy.
326+
function hasVisibleFallbackCommand(
333327
blocks: readonly MessagePresentationBlock[] | undefined,
334328
): boolean {
335329
return (
@@ -364,11 +358,8 @@ function renderFeishuPresentationPayload({
364358
const existingFeishuData = isRecord(payload.channelData?.feishu)
365359
? payload.channelData.feishu
366360
: undefined;
367-
// Preserve a rendered-command marker in channelData so the downstream
368-
// comment-target branch in sendPayload can detect that a command was
369-
// rendered even after core renderPresentationForDelivery strips the
370-
// presentation field before calling sendPayload.
371-
const hasCmd = hasRenderedCommandAction(presentation?.blocks);
361+
// Core consumes presentation before sendPayload; carry the fallback fact.
362+
const fallbackHasCommand = hasVisibleFallbackCommand(presentation?.blocks);
372363
return {
373364
...payload,
374365
text: renderMessagePresentationFallbackText({ text: payload.text, presentation }),
@@ -377,7 +368,7 @@ function renderFeishuPresentationPayload({
377368
feishu: {
378369
...existingFeishuData,
379370
card,
380-
...(hasCmd ? { hasRenderedCommandAction: true } : {}),
371+
...(fallbackHasCommand ? { fallbackHasCommand: true } : {}),
381372
},
382373
},
383374
};
@@ -549,18 +540,12 @@ export const feishuOutbound: ChannelOutboundAdapter = {
549540
text: ctx.payload.text,
550541
presentation: normalizedPresentation,
551542
});
552-
// Use isRecord to narrow channelData.feishu before reading the marker,
553-
// matching the same pattern used in renderFeishuPresentationPayload.
554-
const feishuMarker = isRecord(ctx.payload.channelData?.feishu)
555-
? ctx.payload.channelData.feishu.hasRenderedCommandAction
556-
: undefined;
557-
const hasCmd =
558-
hasRenderedCommandAction(normalizedPresentation?.blocks) ||
559-
// When core delivery strips presentation via renderPresentationForDelivery
560-
// before calling sendPayload, the blocks check above returns false. Fall
561-
// back to the marker set by renderFeishuPresentationPayload.
562-
Boolean(feishuMarker);
563-
const text = hasCmd
543+
// Direct delivery retains blocks; core-rendered delivery carries the fact.
544+
const fallbackHasCommand =
545+
hasVisibleFallbackCommand(normalizedPresentation?.blocks) ||
546+
(isRecord(ctx.payload.channelData?.feishu) &&
547+
ctx.payload.channelData.feishu.fallbackHasCommand === true);
548+
const text = fallbackHasCommand
564549
? `${presentationFallbackText}\n\n> Interactive buttons are unavailable in Feishu document comments. You can type the command shown above manually.`
565550
: presentationFallbackText;
566551

0 commit comments

Comments
 (0)