"""fmessage 系统消息 XML 解析器。 解析微信 4.x Linux 的 fmessage sysmsg XML,提取好友申请信息。 无状态函数,便于单测。 XML 格式(推断,需环境验证): xxx@stranger 申请人昵称 验证消息内容 ... """ from __future__ import annotations import logging import xml.etree.ElementTree as ET from typing import TYPE_CHECKING, Optional if TYPE_CHECKING: # 仅类型注解用,避免与 friend_watcher.py 形成运行时循环 import from woc_bridge.messaging.friend_watcher import FriendRequestInfo logger = logging.getLogger("woc-bridge") def parse_friend_request( content: str, create_time: int, msg_local_id: int ) -> Optional[FriendRequestInfo]: """解析 fmessage 系统消息 XML,提取好友申请信息。 Args: content: message_content 原始文本(XML) create_time: 消息时间戳 msg_local_id: 消息 local_id Returns: FriendRequestInfo 或 None(解析失败 / 非 verifyUser 类型) """ if not content or not content.strip(): return None try: root = ET.fromstring(content) except ET.ParseError: # 部分消息可能不是合法 XML(如纯文本通知),跳过 return None # 检查是否为 verifyUser 类型 sysmsg msg_type = root.get("type", "") if msg_type != "verifyUser": return None # 提取 Link 节点中的申请人信息 link = root.find(".//Link") if link is None: return None stranger_wxid = _text(link, "UserName") nickname = _text(link, "NickName") verify_message = _text(link, "Content") scene = _text(link, "Scene") if not stranger_wxid: return None return FriendRequestInfo( stranger_wxid=stranger_wxid, nickname=nickname, verify_message=verify_message, scene=scene, raw_xml=content, create_time=create_time, msg_local_id=msg_local_id, ) def _text(parent: ET.Element, tag: str) -> str: """安全提取子元素文本(None 安全)。""" el = parent.find(tag) return el.text.strip() if el is not None and el.text else ""