Skip to content

fix(feishu): parse interactive card content in quoted messages and merge_forward#34736

Closed
Daily-AC wants to merge 4 commits intoopenclaw:mainfrom
Daily-AC:fix/feishu-interactive-card-parsing
Closed

fix(feishu): parse interactive card content in quoted messages and merge_forward#34736
Daily-AC wants to merge 4 commits intoopenclaw:mainfrom
Daily-AC:fix/feishu-interactive-card-parsing

Conversation

@Daily-AC
Copy link
Copy Markdown

@Daily-AC Daily-AC commented Mar 4, 2026

Summary

Two related fixes for interactive card content parsing:

1. Quoted message card parsing (fixes #32712)

parseInteractiveCardContent only checked top-level elements array, missing:

  • Card header title (header.title.content)
  • Card Kit v2 structure (body.elements instead of top-level elements)
  • column_set layout containers with nested text/markdown elements

This caused quoted interactive cards to display as [Interactive Card] instead of showing their content.

2. Merge-forward sub-message card parsing (fixes #34571)

formatSubMessageContent in bot.ts had no case for interactive message type, falling through to the default [interactive] placeholder. Added explicit handling using the same enhanced parsing logic.

Testing

  • All existing send.ts tests pass (4/4)
  • Both parseInteractiveCardContent (quoted messages) and formatSubMessageContent (merge_forward) now handle cards

@openclaw-barnacle openclaw-barnacle Bot added channel: feishu Channel integration: feishu size: S labels Mar 4, 2026
Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: e1bba92d96

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

Comment on lines +38 to +39
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- card structure varies
const card = parsed as any;
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Badge Replace no-explicit-any suppression with typed card shape

The repository guideline in AGENTS.md explicitly says not to disable no-explicit-any, but this commit introduces // eslint-disable-next-line @typescript-eslint/no-explicit-any and a cast to any in the new interactive-card parsing path; that removes type guarantees for untrusted message payloads and makes future regressions in this parser harder to catch, so this should be rewritten with a narrow typed interface for the accessed card fields instead of suppressing the rule.

Useful? React with 👍 / 👎.

@greptile-apps
Copy link
Copy Markdown
Contributor

greptile-apps Bot commented Mar 4, 2026

Greptile Summary

This PR fixes two related gaps in Feishu interactive card parsing: quoted messages (via parseInteractiveCardContent in send.ts) and merge-forward sub-messages (via a new interactive case in formatSubMessageContent in bot.ts).

The send.ts changes are solid and well-tested. However, the bot.ts implementation has a logic gap in its column_set inner-element handling: the loop does not filter by element tag before extracting text. It will attempt to read text?.content or content from any element in a column — including buttons, images, and actions — potentially surfacing unintended content from non-text elements in merge-forward messages. The send.ts version correctly guards against this with tag filters (markdown and div only).

Confidence Score: 3/5

  • Safe to merge with inline fix for missing tag filters in bot.ts column_set inner-element loop.
  • The send.ts changes are solid and well-tested (proper tag filtering, type guards, handles all card structures). The bot.ts addition provides real value by handling merge-forward interactive cards, but contains one concrete logic gap: the inner loop in column_set handler doesn't filter by element tag before extracting text, which could inadvertently surface content from non-text elements (buttons, images, actions). This is fixable but needs correction. No crash risk since the entire block is wrapped in try/catch.
  • extensions/feishu/src/bot.ts — specifically the inner loop in the column_set handler (lines 458–461) needs tag filtering to match the guards in send.ts.

Last reviewed commit: e1bba92

Comment on lines +458 to +461
for (const inner of col.elements || []) {
const t = inner.text?.content || inner.content || "";
if (t) texts.push(t);
}
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.

The inner element loop lacks tag filtering. This means it will attempt to extract text?.content or content from any element inside a column — including buttons, images, action elements, and other non-text nodes — potentially leaking unintended content into the output.

The equivalent code in send.ts correctly guards against this by checking innerItem.tag === "markdown" or innerItem.tag === "div" before extraction.

Suggested change
for (const inner of col.elements || []) {
const t = inner.text?.content || inner.content || "";
if (t) texts.push(t);
}
for (const inner of col.elements || []) {
if (!inner || typeof inner !== "object") continue;
const innerEl = inner as { tag?: string; content?: string; text?: { content?: string } };
if (innerEl.tag === "markdown" && innerEl.content) {
texts.push(innerEl.content);
} else if (innerEl.tag === "div") {
const t = innerEl.text?.content || "";
if (t) texts.push(t);
}
}
Prompt To Fix With AI
This is a comment left during a code review.
Path: extensions/feishu/src/bot.ts
Line: 458-461

Comment:
The inner element loop lacks tag filtering. This means it will attempt to extract `text?.content` or `content` from **any** element inside a column — including buttons, images, action elements, and other non-text nodes — potentially leaking unintended content into the output.

The equivalent code in `send.ts` correctly guards against this by checking `innerItem.tag === "markdown"` or `innerItem.tag === "div"` before extraction.

```suggestion
              for (const inner of col.elements || []) {
                if (!inner || typeof inner !== "object") continue;
                const innerEl = inner as { tag?: string; content?: string; text?: { content?: string } };
                if (innerEl.tag === "markdown" && innerEl.content) {
                  texts.push(innerEl.content);
                } else if (innerEl.tag === "div") {
                  const t = innerEl.text?.content || "";
                  if (t) texts.push(t);
                }
              }
```

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

YiLin added 4 commits March 5, 2026 01:44
…rge_forward

Enhance parseInteractiveCardContent to extract:
- Card header title
- body.elements (Card Kit v2 structure) in addition to top-level elements
- column_set layout containers with nested text/markdown elements

Also add interactive card handling in formatSubMessageContent for
merge_forward sub-messages, which previously fell through to the
default case and displayed as [interactive].

Fixes openclaw#32712
Fixes openclaw#34571
Only extract text from markdown/div elements inside column_set,
skipping buttons, images, and action elements to avoid surfacing
unintended content.
@Daily-AC Daily-AC force-pushed the fix/feishu-interactive-card-parsing branch from 92cfbdf to 72a9ac2 Compare March 5, 2026 01:44
Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 72a9ac22e3

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

Comment on lines +448 to +450
const elements = parsed.elements || parsed.body?.elements || [];
const texts: string[] = [];
for (const el of elements) {
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Validate card elements before iterating forwarded interactive cards

In the new interactive branch, elements is assigned with parsed.elements || parsed.body?.elements || [] and then iterated directly. If a forwarded card payload has a truthy non-array elements value (for example from malformed or schema-shifted cards), for (const el of elements) throws, the outer catch runs, and we fall back to returning raw JSON content instead of a readable placeholder/card summary. Guarding with Array.isArray here (as done in send.ts) keeps merge-forward formatting resilient.

Useful? React with 👍 / 👎.

@openclaw-barnacle
Copy link
Copy Markdown

This pull request has been automatically marked as stale due to inactivity.
Please add updates or it will be closed.

@openclaw-barnacle openclaw-barnacle Bot added the stale Marked as stale due to inactivity label Apr 14, 2026
@openclaw-barnacle
Copy link
Copy Markdown

Closing due to inactivity.
If you believe this PR should be revived, post in #pr-thunderdome-dangerzone on Discord to talk to a maintainer.
That channel is the escape hatch for high-quality PRs that get auto-closed.

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 stale Marked as stale due to inactivity

Projects

None yet

1 participant