fix(feishu): extract content from interactive card variants when quoting#38776
fix(feishu): extract content from interactive card variants when quoting#38776lishuaigit wants to merge 2 commits intoopenclaw:mainfrom
Conversation
Greptile SummaryThis PR fixes quoted Feishu interactive card messages by expanding Changes:
Potential concerns:
Confidence Score: 4/5
Last reviewed commit: 664acbc |
| if (card.i18n_elements && typeof card.i18n_elements === "object") { | ||
| elements = | ||
| card.i18n_elements.zh_cn ?? | ||
| (Object.values(card.i18n_elements).find((v) => Array.isArray(v)) as unknown[] | undefined); | ||
| } |
There was a problem hiding this comment.
Empty zh_cn array silently skips other locales
?? only short-circuits on null/undefined, so if card.i18n_elements.zh_cn is an empty array [] (a valid, non-nullish value), elements is set to []. extractTextsFromElements([]) returns nothing, and any content present in other locales (e.g. en_us) is silently dropped, causing the function to fall through to "[Interactive Card]".
Consider guarding against an empty array as well:
| if (card.i18n_elements && typeof card.i18n_elements === "object") { | |
| elements = | |
| card.i18n_elements.zh_cn ?? | |
| (Object.values(card.i18n_elements).find((v) => Array.isArray(v)) as unknown[] | undefined); | |
| } | |
| elements = | |
| (Array.isArray(card.i18n_elements.zh_cn) && card.i18n_elements.zh_cn.length > 0 | |
| ? card.i18n_elements.zh_cn | |
| : undefined) ?? | |
| (Object.values(card.i18n_elements).find((v) => Array.isArray(v) && v.length > 0) as unknown[] | undefined); |
Prompt To Fix With AI
This is a comment left during a code review.
Path: extensions/feishu/src/send.ts
Line: 174-178
Comment:
**Empty `zh_cn` array silently skips other locales**
`??` only short-circuits on `null`/`undefined`, so if `card.i18n_elements.zh_cn` is an empty array `[]` (a valid, non-nullish value), `elements` is set to `[]`. `extractTextsFromElements([])` returns nothing, and any content present in other locales (e.g. `en_us`) is silently dropped, causing the function to fall through to `"[Interactive Card]"`.
Consider guarding against an empty array as well:
```suggestion
elements =
(Array.isArray(card.i18n_elements.zh_cn) && card.i18n_elements.zh_cn.length > 0
? card.i18n_elements.zh_cn
: undefined) ??
(Object.values(card.i18n_elements).find((v) => Array.isArray(v) && v.length > 0) as unknown[] | undefined);
```
How can I resolve this? If you propose a fix, please make it concise.| if (texts.length === 0 && card.type === "template" && card.data?.template_variable) { | ||
| const vars = card.data.template_variable; | ||
| for (const val of Object.values(vars)) { | ||
| if (typeof val === "string" && val.trim()) { | ||
| texts.push(val); | ||
| } | ||
| } | ||
| } |
There was a problem hiding this comment.
Non-string template variables are silently skipped
Template variables like { "error_code": 500, "retries": 3 } are silently dropped because only typeof val === "string" values are considered. Numeric (or boolean) values can carry meaningful context in alert/notification cards and would currently fall through to "[Interactive Card]" if no string variable exists.
Consider coercing primitive values to strings:
| if (texts.length === 0 && card.type === "template" && card.data?.template_variable) { | |
| const vars = card.data.template_variable; | |
| for (const val of Object.values(vars)) { | |
| if (typeof val === "string" && val.trim()) { | |
| texts.push(val); | |
| } | |
| } | |
| } | |
| for (const val of Object.values(vars)) { | |
| if ((typeof val === "string" || typeof val === "number" || typeof val === "boolean") && String(val).trim()) { | |
| texts.push(String(val)); | |
| } | |
| } |
Prompt To Fix With AI
This is a comment left during a code review.
Path: extensions/feishu/src/send.ts
Line: 186-193
Comment:
**Non-string template variables are silently skipped**
Template variables like `{ "error_code": 500, "retries": 3 }` are silently dropped because only `typeof val === "string"` values are considered. Numeric (or boolean) values can carry meaningful context in alert/notification cards and would currently fall through to `"[Interactive Card]"` if no string variable exists.
Consider coercing primitive values to strings:
```suggestion
for (const val of Object.values(vars)) {
if ((typeof val === "string" || typeof val === "number" || typeof val === "boolean") && String(val).trim()) {
texts.push(String(val));
}
}
```
How can I resolve this? If you propose a fix, please make it concise.664acbc to
2a623d6
Compare
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 2a623d6dca
ℹ️ 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".
| card.i18n_elements.zh_cn ?? | ||
| (Object.values(card.i18n_elements).find((v) => Array.isArray(v)) as unknown[] | undefined); |
There was a problem hiding this comment.
Fall back from empty zh_cn i18n elements to another locale
The new i18n locale selection uses card.i18n_elements.zh_cn ?? ..., which treats an empty zh_cn array as a valid hit and prevents fallback to other locales. In cards where zh_cn is present but empty (while en_us or another locale has actual elements), elements becomes [] and the parser returns [Interactive Card] instead of real quoted content. This makes quoting fail for multilingual cards unless zh_cn is populated.
Useful? React with 👍 / 👎.
2a623d6 to
9171649
Compare
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 91716491db
ℹ️ 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".
| let elements: unknown[] | undefined = card.elements; | ||
| if (!Array.isArray(elements)) { | ||
| // Try body.elements (card kit v2) | ||
| elements = card.body?.elements; |
There was a problem hiding this comment.
Continue fallback when top-level elements is empty
The resolver treats card.elements as final as soon as it is an array, so an empty elements: [] prevents checking body.elements and i18n_elements. In payloads that include an empty legacy elements field plus real content under body.elements (or locale-specific elements), this still returns [Interactive Card] instead of quoted text, which defeats the variant fallback this change is trying to add.
Useful? React with 👍 / 👎.
|
@steipete Fixes content extraction from Feishu interactive cards when quoting — handles header, body.elements, and i18n_elements variants. Includes comprehensive test coverage. CI green 🙏 |
|
Friendly follow-up @steipete 👋 This PR has been open for a while now. CI failures are all in the shared bun/windows-shard flaky tests — not related to this change. Happy to rebase or address any feedback. Would appreciate a review when you have time 🙏 |
Previously, parseInteractiveCardContent only handled flat v1 cards with top-level `elements`. When a user quoted a card message that used a header, i18n_elements, template, column_set, or body wrapper, the bot could only extract "[Interactive Card]" instead of the actual content. Now the parser handles: - Card header title extraction - body.elements (card kit v2) - i18n_elements with locale fallback (zh_cn preferred) - Template cards (template_variable values) - Nested structures: column_set columns, form/collapsible elements Closes openclaw#32712
Address Greptile review feedback: the body.elements extraction path used by the bot's own buildMarkdownCard output now has a dedicated test.
9171649 to
40557ca
Compare
Summary
Fix quoted interactive card messages showing only
[Interactive Card]instead of actual content.Closes #32712
Problem
When a user quotes a Feishu interactive card and @mentions the bot, the bot only receives:
[Replying to: "[Interactive Card]"]This is because
parseInteractiveCardContentonly handled flat v1 cards with top-levelelements. Feishu cards come in many shapes (with headers, i18n, templates, column layouts, body wrappers) and those all fell through to the fallback.Changes
extensions/feishu/src/send.ts:body.elements(card kit v2 wrapper)i18n_elementswith locale fallback (zh_cnpreferred)template_variableextraction)column_setcolumns and nestedelements(form, collapsible)extractTextsFromElementsfor recursive traversalextensions/feishu/src/send.test.ts:body.elements(card kit v2) pathTesting
All 346 feishu extension tests pass (345 existing + 1 new for body.elements path, plus 4 new card variant tests).