From bc59fe8719a442c2fcf8fa49f5e3544038bc0b34 Mon Sep 17 00:00:00 2001 From: Jiajun Xie Date: Sun, 14 Jun 2026 22:00:03 +0800 Subject: [PATCH] fix(feishu): support reading WebSocket rendered card content Feishu cards arriving via WebSocket have a different structure than expected: elements are nested lists with tag:text/text fields instead of flat lists with tag:markdown/content. Also extract user_dsl for richer card data and support body.elements (schema 2.0). --- nanobot/channels/feishu.py | 43 +++++++++++++++++++++++++++++++++----- 1 file changed, 38 insertions(+), 5 deletions(-) diff --git a/nanobot/channels/feishu.py b/nanobot/channels/feishu.py index a709521d..d1d0e562 100644 --- a/nanobot/channels/feishu.py +++ b/nanobot/channels/feishu.py @@ -108,6 +108,18 @@ def _extract_interactive_content(content: dict) -> list[str]: if not isinstance(content, dict): return parts + # user_dsl: original card definition (richest source for rendered cards) + user_dsl = content.get("user_dsl") + if isinstance(user_dsl, str) and user_dsl.strip(): + try: + dsl = json.loads(user_dsl) + if isinstance(dsl, dict): + parts.extend(_extract_interactive_content(dsl)) + if parts: + return parts + except (json.JSONDecodeError, TypeError): + pass + if "title" in content: title = content["title"] if isinstance(title, dict): @@ -117,11 +129,27 @@ def _extract_interactive_content(content: dict) -> list[str]: elif isinstance(title, str): parts.append(f"title: {title}") - for elements in ( - content.get("elements", []) if isinstance(content.get("elements"), list) else [] - ): - for element in elements: - parts.extend(_extract_element_content(element)) + # Top-level elements: flat list or nested list format + elements = content.get("elements") + if isinstance(elements, list): + if elements and isinstance(elements[0], list): + # Nested list: [[{tag:"text",text:"..."}], ...] + for row in elements: + if isinstance(row, list): + for element in row: + parts.extend(_extract_element_content(element)) + else: + # Flat list: [{tag:"markdown",content:"..."}, ...] + for element in elements: + parts.extend(_extract_element_content(element)) + + # Body elements (schema 2.0) + body = content.get("body", {}) + if isinstance(body, dict): + body_elements = body.get("elements") + if isinstance(body_elements, list): + for element in body_elements: + parts.extend(_extract_element_content(element)) card = content.get("card", {}) if card: @@ -152,6 +180,11 @@ def _extract_element_content(element: dict) -> list[str]: if content: parts.append(content) + elif tag == "text": + text = element.get("text", "") + if isinstance(text, str) and text.strip(): + parts.append(text) + elif tag == "div": text = element.get("text", {}) if isinstance(text, dict):