from __future__ import annotations from typing import Any TEXT_CHUNK_LIMIT = 30000 CARD_TEMPLATE_COLORS = { "blue", "green", "red", "orange", "purple", "indigo", "wathet", "turquoise", "yellow", "grey", "carmine", "violet", "lime", } PRESENTATION_TONE_MAP = { "default": "blue", "danger": "red", "warning": "orange", "success": "green", } BUTTON_TYPE_MAP = { "primary": "primary", "danger": "danger", "default": "default", "": "default", } SELECTOR_TAGS = { "select_static", "select_person", "date_picker", "time_picker", "datetime_picker", "overflow", } def resolve_template_color(template: str | None = None, tone: str | None = None) -> str: if template and template in CARD_TEMPLATE_COLORS: return template if tone and tone in PRESENTATION_TONE_MAP: return PRESENTATION_TONE_MAP[tone] return "blue" def resolve_button_type(style: str | None = None) -> str: if not style: return "default" return BUTTON_TYPE_MAP.get(style, "default") def resolve_selector_tag(tag: str) -> str: return tag if tag in SELECTOR_TAGS else "select_static" def make_select_static( placeholder: str = "", options: list[dict[str, str]] | None = None, value: str = "", initial_option: str = "", ) -> dict[str, Any]: sel: dict[str, Any] = {"tag": "select_static"} if placeholder: sel["placeholder"] = {"tag": "plain_text", "content": placeholder} if options: sel["options"] = [ {"text": {"tag": "plain_text", "content": opt.get("text", "")}, "value": opt.get("value", "")} for opt in options ] if value: sel["value"] = value if initial_option: sel["initial_option"] = initial_option return sel def make_select_person( placeholder: str = "", value: str = "", ) -> dict[str, Any]: sel: dict[str, Any] = {"tag": "select_person"} if placeholder: sel["placeholder"] = {"tag": "plain_text", "content": placeholder} if value: sel["value"] = value return sel def make_date_picker( placeholder: str = "", value: str = "", initial_date: str = "", ) -> dict[str, Any]: sel: dict[str, Any] = {"tag": "date_picker"} if placeholder: sel["placeholder"] = {"tag": "plain_text", "content": placeholder} if value: sel["value"] = value if initial_date: sel["initial_date"] = initial_date return sel def make_time_picker( placeholder: str = "", value: str = "", initial_time: str = "", ) -> dict[str, Any]: sel: dict[str, Any] = {"tag": "time_picker"} if placeholder: sel["placeholder"] = {"tag": "plain_text", "content": placeholder} if value: sel["value"] = value if initial_time: sel["initial_time"] = initial_time return sel def make_datetime_picker( placeholder: str = "", value: str = "", initial_datetime: str = "", ) -> dict[str, Any]: sel: dict[str, Any] = {"tag": "datetime_picker"} if placeholder: sel["placeholder"] = {"tag": "plain_text", "content": placeholder} if value: sel["value"] = value if initial_datetime: sel["initial_datetime"] = initial_datetime return sel def make_overflow_menu( options: list[dict[str, str]] | None = None, value: str = "", ) -> dict[str, Any]: sel: dict[str, Any] = {"tag": "overflow"} if options: sel["options"] = [ {"text": {"tag": "plain_text", "content": opt.get("text", "")}, "value": opt.get("value", "")} for opt in options ] if value: sel["value"] = value return sel def build_feishu_text_content(content: str) -> dict[str, Any]: return {"text": content} def build_feishu_post_content(content: str) -> dict[str, Any]: md_lines = content.split("\n") paragraphs: list[list[dict[str, Any]]] = [] for line in md_lines: if not line.strip(): paragraphs.append([{"tag": "text", "text": ""}]) else: paragraphs.append([{"tag": "md", "text": line}]) return {"zh_cn": {"content": paragraphs}} def is_post_format_requested(metadata: dict | None = None) -> bool: if not metadata: return False return metadata.get("use_post_format", False) or metadata.get("usePostFormat", False) def build_feishu_card( content: str, *, title: str = "AI εŠ©ζ‰‹", buttons: list[dict[str, str]] | None = None, streaming: bool = False, url_unfurl: list[str] | None = None, images: list[str] | None = None, files: list[dict[str, str]] | None = None, note: str | None = None, template: str | None = None, tone: str | None = None, context_text: str | None = None, dividers: int = 0, selectors: list[dict[str, Any]] | None = None, ) -> dict[str, Any]: elements: list[dict[str, Any]] = [] if context_text: elements.append( { "tag": "note", "elements": [{"tag": "plain_text", "content": context_text}], } ) if dividers > 0: for _ in range(dividers): elements.append({"tag": "hr"}) if images: for img_key in images: elements.append( { "tag": "img", "img_key": img_key, "alt": {"tag": "plain_text", "content": ""}, } ) elements.append({"tag": "markdown", "content": content}) if files: for f in files: name = f.get("name", "ζ–‡δ»Ά") url = f.get("url", "") link = f"[πŸ“Ž {name}]({url})" if url else f"πŸ“Ž {name}" elements.append({"tag": "markdown", "content": link}) if selectors: for sel in selectors: selector_tag = resolve_selector_tag(sel.get("tag", "select_static")) selector_element: dict[str, Any] = {"tag": selector_tag} if "placeholder" in sel: if isinstance(sel["placeholder"], dict): selector_element["placeholder"] = sel["placeholder"] else: selector_element["placeholder"] = {"tag": "plain_text", "content": str(sel["placeholder"])} if "options" in sel: selector_element["options"] = [ {"text": {"tag": "plain_text", "content": opt.get("text", "")}, "value": opt.get("value", "")} for opt in sel["options"] ] if "value" in sel: selector_element["value"] = sel["value"] if "initial_option" in sel: selector_element["initial_option"] = sel["initial_option"] if "initial_date" in sel: selector_element["initial_date"] = sel["initial_date"] if "initial_time" in sel: selector_element["initial_time"] = sel["initial_time"] if "initial_datetime" in sel: selector_element["initial_datetime"] = sel["initial_datetime"] elements.append(selector_element) if buttons: actions: list[dict[str, Any]] = [] for btn in buttons: btn_type = resolve_button_type(btn.get("style")) actions.append( { "tag": "button", "text": {"tag": "plain_text", "content": btn.get("text", "")}, "type": btn_type, "value": {"action": btn.get("action", "")}, } ) elements.append({"tag": "action", "actions": actions}) if url_unfurl: for url in url_unfurl: elements.append( { "tag": "markdown", "content": f"[πŸ”— {url}]({url})", } ) if note: elements.append( { "tag": "note", "elements": [{"tag": "plain_text", "content": note}], } ) header_title = f"{title} (ε›žε€δΈ­...)" if streaming else title color = resolve_template_color(template, tone) return { "header": { "title": {"tag": "plain_text", "content": header_title}, "template": color, }, "elements": elements, }