import xml.etree.ElementTree as ET from yuxi.channel.extensions.wechat_mp.types import InboundWeChatMessage def parse_xml_to_message(xml_text: str) -> InboundWeChatMessage: root = ET.fromstring(xml_text) def _text(tag: str) -> str: el = root.find(tag) return el.text or "" if el is not None else "" def _int(tag: str) -> int: try: return int(_text(tag)) except (ValueError, TypeError): return 0 def _float(tag: str) -> float: try: return float(_text(tag)) except (ValueError, TypeError): return 0.0 msg_type = _text("MsgType") event = _text("Event") if msg_type == "event" else "" msg_id = _text("MsgId") if not msg_id: msg_id = f"{_text('FromUserName')}_{_text('CreateTime')}_{msg_type}_{event}" return InboundWeChatMessage( msg_id=msg_id, msg_type=msg_type, from_user=_text("FromUserName"), to_user=_text("ToUserName"), create_time=_int("CreateTime"), content=_text("Content"), pic_url=_text("PicUrl"), media_id=_text("MediaId"), media_id_16k=_text("MediaId16K"), thumb_media_id=_text("ThumbMediaId"), media_format=_text("Format"), recognition=_text("Recognition"), location_x=_float("Location_X"), location_y=_float("Location_Y"), label=_text("Label"), title=_text("Title"), description=_text("Description"), url=_text("Url"), event=event, event_key=_text("EventKey"), app_id=_text("AppId"), page_path=_text("PagePath"), thumb_url=_text("ThumbUrl"), raw_xml=_dict_from_element(root), ) def resolve_voice(msg: InboundWeChatMessage) -> dict: if msg.recognition and msg.recognition.strip(): return {"mode": "recognition", "text": msg.recognition, "ctype": "text"} if msg.media_id: return {"mode": "download", "media_id": msg.media_id, "format": msg.media_format, "ctype": "voice"} return {"mode": "unknown", "ctype": "text"} def extract_content(msg: InboundWeChatMessage) -> str: match msg.msg_type: case "text": return msg.content case "image": if msg.pic_url: return f"[图片: {msg.pic_url}]" if msg.media_id: return f"[图片: {msg.media_id}]" return "[图片]" case "voice": voice_info = resolve_voice(msg) if voice_info["mode"] == "recognition": return voice_info["text"] if voice_info["mode"] == "download": return f"[语音: {voice_info['media_id']}]" return "[语音]" case "video" | "shortvideo": if msg.media_id: return f"[视频: {msg.media_id}]" return "[视频]" case "location": return f"{msg.label}\n({msg.location_y}, {msg.location_x})" case "link": return f"{msg.title}\n{msg.description}\n{msg.url}" case "miniprogrampage": parts = [] if msg.title: parts.append(f"小程序卡片: {msg.title}") if msg.app_id: parts.append(f"AppId: {msg.app_id}") if msg.page_path: parts.append(f"页面路径: {msg.page_path}") return "\n".join(parts) if parts else "[小程序卡片]" case _: return msg.content or "" async def download_media(media_id: str, gateway) -> bytes | None: from yuxi.channel.extensions.wechat_mp.media import WeChatMedia token = gateway.access_token if gateway else None if not token: return None wm = WeChatMedia(lambda: token) result = await wm.download(media_id) if result.get("success"): return result["data"] return None async def download_image_content(msg: InboundWeChatMessage, gateway) -> str: if not msg.media_id and not msg.pic_url: return "" if msg.pic_url: return msg.pic_url data = await download_media(msg.media_id, gateway) if data: import base64 return f"data:image/jpeg;base64,{base64.b64encode(data).decode()}" return "" def _dict_from_element(element: ET.Element) -> dict: result = {} for child in element: if len(child) > 0: result[child.tag] = _dict_from_element(child) else: result[child.tag] = child.text or "" return result