WechatOnCloud/bridge/woc_bridge/messaging/friend_parser.py

85 lines
2.3 KiB
Python
Raw Normal View History

"""fmessage 系统消息 XML 解析器。
解析微信 4.x Linux fmessage sysmsg XML提取好友申请信息
无状态函数便于单测
XML 格式推断需环境验证
<sysmsg type="verifyUser" ...>
<Link ...>
<UserName>xxx@stranger</UserName>
<NickName>申请人昵称</NickName>
<Content>验证消息内容</Content>
<Scene>...</Scene>
</Link>
</sysmsg>
"""
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 ""