fix(export): strip group prefix before parsing appmsg in chat export (#101)

Issue #88: 群聊里的引用回复(appmsg type=57)/ 卡片 / 视频在 export_chat
和 export_all_chats 渲染成 type=link_or_file 且 content 为空。

根因:`_extract_content` 把数据库里带 `wxid_xxx:\n` 群前缀的原始 content
直接喂给 `_format_app_message_text`,XML 解析器在前缀文本上 ParseError,
返回 None。

修复:
- 用 `chat_username.endswith('@chatroom')` 判定群聊
- 在 dispatch 前调 `mcp_server._parse_message_content(..., is_group=True)`
  剥前缀;逻辑也对群里的 base=1 text 生效(之前同样带前缀)
- 把 `is_group=True` 透传给 `_format_app_message_text`,让引用回复走 group
  分支的发送者标签解析
- 用 `mcp_server.get_contact_names()` 代替之前硬编码的 `{}`,让 wxid 能
  正确解出昵称

测试:新增 5 个测试覆盖群引用回复带前缀 / 1-on-1 不受影响 / 群 text
前缀剥离 / 1-on-1 text 不变 / names dict 正确解析。126/126 通过。

Belugary 在 #100 修了 `_format_app_message_text` 内部的 type=57 schema
渲染(对 get_chat_history 生效),本 PR 是补 export 这条路径上的群前缀
bug。两者互补。

Co-authored-by: ylytdeng <ylytdeng@users.noreply.github.com>
This commit is contained in:
joshua-deng
2026-05-13 13:40:17 +08:00
committed by GitHub
parent 8645fe4210
commit 70d44ef61f
2 changed files with 116 additions and 1 deletions

View File

@@ -167,6 +167,17 @@ def _extract_content(local_id, local_type, content, ct, chat_username, chat_disp
if content is None:
return None, None
# 群消息的 content 形如 'wxid_xxx:\n<xml...>'。Issue #88: 之前直接把
# 带前缀的字符串喂给 XML 解析器,群里的引用回复 / 卡片 / 视频等都因
# 解析失败导致 type 渲染成 link_or_file 且 content 为空。
is_group = bool(chat_username) and chat_username.endswith('@chatroom')
if is_group:
_, content = mcp_server._parse_message_content(content, local_type, True)
# names 用于群引用回复的发送者名解析_resolve_quote_sender_label
# 1-on-1 场景也能用到(按 wxid 查显示名)。
names = mcp_server.get_contact_names()
base, _ = mcp_server._split_msg_type(local_type)
if base == 1:
return (content or ""), None
@@ -176,7 +187,7 @@ def _extract_content(local_id, local_type, content, ct, chat_username, chat_disp
return _format_sticker_message(content), None
if base == 49:
rendered = mcp_server._format_app_message_text(
content, local_type, False, chat_username, chat_display_name, {}
content, local_type, is_group, chat_username, chat_display_name, names
)
transfer = _extract_transfer_extras(content)
extras = {'type': 'transfer', 'transfer': transfer} if transfer else None