Skip to content

fix(feishu): parse interactive card post-format fallback content#60383

Open
lskun wants to merge 1 commit intoopenclaw:mainfrom
lskun:fix/feishu-interactive-card-fallback-parsing
Open

fix(feishu): parse interactive card post-format fallback content#60383
lskun wants to merge 1 commit intoopenclaw:mainfrom
lskun:fix/feishu-interactive-card-fallback-parsing

Conversation

@lskun
Copy link
Copy Markdown

@lskun lskun commented Apr 3, 2026

Summary

Fix parseInteractiveCardContent() to handle Feishu API post-format fallback content for interactive card messages.

Fixes #60380
Related: #41609 #48281

Problem

When reading card messages via GET /im/v1/messages/{message_id}, Feishu returns body.content in a post-style fallback format — nested arrays with tag:"text" elements — instead of the original card template JSON. The existing parser only handled card template format (div/markdown tags), causing all fallback content to silently return "[Interactive Card]".

Before: Any message(action=read) on a card → "[Interactive Card]"
After: Extracts title + text content from both card template and post-format fallback

Example

API returns this for a card message:

{
  "title": "🤖 clawdbot",
  "elements": [[
    {"tag": "img", "image_key": "img_v3_02ad_..."},
    {"tag": "text", "text": "Daily report summary"},
    {"tag": "text", "text": "13 people missing reports"}
  ]]
}
  • Before: "[Interactive Card]"
  • After: "🤖 clawdbot\nDaily report summary\n13 people missing reports"

Changes

extensions/feishu/src/send.ts

  • Handle nested arrays in elements (post-format fallback [[...]])
  • Extract tag:"text" and tag:"a" elements from fallback content
  • Extract card title from header.title.content (template) or top-level title (fallback)
  • Support tag:"plain_text" elements
  • Return title even when elements array is missing

extensions/feishu/src/send.test.ts

  • Add test: post-format fallback with nested arrays and title
  • Add test: card template with header.title.content
  • Add test: link extraction from tag:"a" elements with href

Testing

  • Verified with real Feishu API responses from production bot
  • Existing card template format tests still pass (div/markdown)
  • 3 new test cases covering fallback scenarios

@openclaw-barnacle openclaw-barnacle Bot added channel: feishu Channel integration: feishu size: S labels Apr 3, 2026
@greptile-apps
Copy link
Copy Markdown
Contributor

greptile-apps Bot commented Apr 3, 2026

Greptile Summary

This PR extends parseInteractiveCardContent() in the Feishu extension to handle the post-format fallback that the Feishu API returns when reading interactive card messages via GET /im/v1/messages/{message_id}. The logic correctly detects nested arrays ([[...]]), extracts tag:"text" / tag:"a" elements, picks up the top-level title string for fallback cards, and falls through gracefully to "[Interactive Card]" when nothing is extractable.

  • Trailing-whitespace test/code mismatch: The anchor-tag test provides { tag: "text", text: "See: " } (trailing space) but asserts the output contains "See:" (no space). The production code pushes node.text without trimming, so the joined string will be "See: ..." not "See: ...". This test will fail at runtime; the fix is to call .trim() before pushing each tag:"text" node text.
  • The rest of the logic — nested-array flattening, header/title extraction, and tag:"a" href formatting — looks correct and the other two new tests (post-format fallback and header.title.content) are consistent with the implementation.

Confidence Score: 3/5

Safe to merge after fixing the trailing-whitespace bug in the anchor-tag test; the core parsing logic is sound.

The PR logic is correct for all described scenarios, but there is a concrete test failure in the anchor-tag test: input text "See: " (trailing space) will not match the asserted "See:" because individual texts are pushed untrimmed. The PR's stated test coverage is not actually green.

extensions/feishu/src/send.ts line 220-221 — tag:"text" node text should be trimmed before pushing. extensions/feishu/src/send.test.ts line 257/275 — input and expected value are inconsistent.

Prompt To Fix All With AI
This is a comment left during a code review.
Path: extensions/feishu/src/send.test.ts
Line: 257-275

Comment:
**Test/code mismatch — trailing whitespace in text causes assertion failure**

The test input at line 257 has `{ tag: "text", text: "See: " }` — note the trailing space. The production code in `send.ts` pushes `node.text` directly (not trimmed):

```typescript
if (node.tag === "text" && typeof node.text === "string" && node.text.trim()) {
  texts.push(node.text);  // pushes "See: " with the trailing space intact
}
```

After `texts.join("\n")`, the result is `"Report\nSee: \nWeekly Report (...)"` — but the assertion expects `"Report\nSee:\nWeekly Report (...)"` (no trailing space). The `.trim()` call at the end only strips the leading/trailing edges of the whole string, not interior segments.

This test will fail as written. The fix is to trim the individual text value before pushing in `send.ts`:
```suggestion
                if (node.tag === "text" && typeof node.text === "string" && node.text.trim()) {
                  texts.push(node.text.trim());
                }
```
This is also better UX — stray trailing spaces in card text are noise and shouldn't appear in the extracted content.

How can I resolve this? If you propose a fix, please make it concise.

Reviews (1): Last reviewed commit: "fix(feishu): parse interactive card post..." | Re-trigger Greptile

Comment on lines +257 to +275
{ tag: "text", text: "See: " },
{ tag: "a", text: "Weekly Report", href: "https://example.com/report" },
],
],
}),
},
},
],
},
});

const result = await getMessageFeishu({
cfg: {} as ClawdbotConfig,
messageId: "om_card_links",
});

expect(result).toEqual(
expect.objectContaining({
content: "Report\nSee:\nWeekly Report (https://example.com/report)",
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Test/code mismatch — trailing whitespace in text causes assertion failure

The test input at line 257 has { tag: "text", text: "See: " } — note the trailing space. The production code in send.ts pushes node.text directly (not trimmed):

if (node.tag === "text" && typeof node.text === "string" && node.text.trim()) {
  texts.push(node.text);  // pushes "See: " with the trailing space intact
}

After texts.join("\n"), the result is "Report\nSee: \nWeekly Report (...)" — but the assertion expects "Report\nSee:\nWeekly Report (...)" (no trailing space). The .trim() call at the end only strips the leading/trailing edges of the whole string, not interior segments.

This test will fail as written. The fix is to trim the individual text value before pushing in send.ts:

Suggested change
{ tag: "text", text: "See: " },
{ tag: "a", text: "Weekly Report", href: "https://example.com/report" },
],
],
}),
},
},
],
},
});
const result = await getMessageFeishu({
cfg: {} as ClawdbotConfig,
messageId: "om_card_links",
});
expect(result).toEqual(
expect.objectContaining({
content: "Report\nSee:\nWeekly Report (https://example.com/report)",
if (node.tag === "text" && typeof node.text === "string" && node.text.trim()) {
texts.push(node.text.trim());
}

This is also better UX — stray trailing spaces in card text are noise and shouldn't appear in the extracted content.

Prompt To Fix With AI
This is a comment left during a code review.
Path: extensions/feishu/src/send.test.ts
Line: 257-275

Comment:
**Test/code mismatch — trailing whitespace in text causes assertion failure**

The test input at line 257 has `{ tag: "text", text: "See: " }` — note the trailing space. The production code in `send.ts` pushes `node.text` directly (not trimmed):

```typescript
if (node.tag === "text" && typeof node.text === "string" && node.text.trim()) {
  texts.push(node.text);  // pushes "See: " with the trailing space intact
}
```

After `texts.join("\n")`, the result is `"Report\nSee: \nWeekly Report (...)"` — but the assertion expects `"Report\nSee:\nWeekly Report (...)"` (no trailing space). The `.trim()` call at the end only strips the leading/trailing edges of the whole string, not interior segments.

This test will fail as written. The fix is to trim the individual text value before pushing in `send.ts`:
```suggestion
                if (node.tag === "text" && typeof node.text === "string" && node.text.trim()) {
                  texts.push(node.text.trim());
                }
```
This is also better UX — stray trailing spaces in card text are noise and shouldn't appear in the extracted content.

How can I resolve this? If you propose a fix, please make it concise.

@clawsweeper
Copy link
Copy Markdown
Contributor

clawsweeper Bot commented May 1, 2026

Codex review: needs changes before merge.

Summary
The PR extends Feishu interactive-card read parsing and tests to extract title, text, plain-text, and anchor content from top-level nested post-format fallback card payloads.

Reproducibility: yes. A mocked getMessageFeishu() response with msg_type "interactive" and body.content containing {"title":"...","elements":[[{"tag":"text","text":"..."}]]} follows current main to the placeholder because nested arrays are ignored and the post fallback requires a post/content payload.

Next step before merge
Automation can safely repair this PR with a small parser-control-flow change, one regression test, and the required changelog entry.

Security
Cleared: The diff only changes Feishu parser logic and colocated tests, with no dependency, workflow, secret, install, package, or code-execution surface changes.

Review findings

  • [P2] Preserve later element-array fallthrough — extensions/feishu/src/send.ts:335-337
  • [P3] Add a changelog entry for this user-facing fix — extensions/feishu/src/send.ts:246-256
Review details

Best possible solution:

Land a narrow Feishu parser/test fix that preserves #72397 empty-array fallback semantics while adding top-level nested fallback title/text/link extraction and an Unreleased changelog entry.

Do we have a high-confidence way to reproduce the issue?

Yes. A mocked getMessageFeishu() response with msg_type "interactive" and body.content containing {"title":"...","elements":[[{"tag":"text","text":"..."}]]} follows current main to the placeholder because nested arrays are ignored and the post fallback requires a post/content payload.

Is this the best way to solve the issue?

No, not as currently patched. The narrow extraction approach is right, but the implementation should first find nonempty element text across candidate arrays and only prepend or return title text after preserving the existing empty-array fallthrough behavior.

Full review comments:

  • [P2] Preserve later element-array fallthrough — extensions/feishu/src/send.ts:335-337
    readInteractiveElementArrays() yields parsed.elements before body.elements and i18n_elements. With this change, any card that has a header/top-level title plus an empty first elements array returns the title at this line and never checks later nonempty arrays, regressing the empty-array fallback behavior added in fix(feishu): repair interactive card content extraction #72397; only combine title text after an element array actually produced content, or collect all candidate text before returning.
    Confidence: 0.87
  • [P3] Add a changelog entry for this user-facing fix — extensions/feishu/src/send.ts:246-256
    This branch changes user-visible Feishu message content for top-level fallback cards, but the diff only touches parser code and tests. The existing 2026.4.26 note credited this source work for fix(feishu): repair interactive card content extraction #72397, whose shipped code still misses this exact top-level nested elements shape, so add an Unreleased Feishu Fixes entry for the behavior actually landing here.
    Confidence: 0.72

Overall correctness: patch is incorrect
Overall confidence: 0.87

Acceptance criteria:

  • git diff --check
  • pnpm exec oxfmt --check --threads=1 extensions/feishu/src/send.ts extensions/feishu/src/send.test.ts CHANGELOG.md
  • pnpm test extensions/feishu/src/send.test.ts
  • pnpm check:changed

What I checked:

  • Current main misses nested fallback elements: Current main only extracts records in extractInteractiveElementText(); a top-level elements value shaped as [[{ tag: "text" }]] is passed through as an array and ignored, so the reported payload can still fall through to the interactive-card placeholder. (extensions/feishu/src/send.ts:230, cc8a8f1df1cd)
  • Post fallback does not cover this top-level shape: parseInteractivePostFallback delegates to parsePostContent(), whose payload recognizer requires a content array or wrapped post payload; the reported title plus elements shape is not accepted there. (extensions/feishu/src/post.ts:188, cc8a8f1df1cd)
  • PR head adds the requested extraction paths: The PR head recurses nested arrays, trims text nodes, renders anchor labels with hrefs, and reads header/top-level titles for interactive card content. (extensions/feishu/src/send.ts:228, a380eb11d56f)
  • PR head has a title-only early return regression: Because titleTexts is included inside each per-array loop iteration, an empty first candidate such as parsed.elements with a nonempty title returns the title immediately and skips later body.elements or i18n_elements content. (extensions/feishu/src/send.ts:335, a380eb11d56f)
  • Related merged replacement context: fix(feishu): repair interactive card content extraction #72397 introduced the current-main parser refactor and a shipped changelog entry crediting this PR, but current main still lacks the exact top-level nested elements extraction this PR tests. (CHANGELOG.md:1340, c6cf37068cae)

Likely related people:

Remaining risk / open question:

  • Validation was not executed in this read-only sweep; the merge path should run the targeted Feishu test and changed gate after the parser fix.

Codex review notes: model gpt-5.5, reasoning high; reviewed against cc8a8f1df1cd.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

channel: feishu Channel integration: feishu size: S

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Bug] Feishu: parseInteractiveCardContent fails on fallback post-format content

1 participant