mirror of
https://github.com/ylytdeng/wechat-decrypt.git
synced 2026-11-06 02:56:53 +08:00
feat: 渲染聊天里独立的视频号分享 (appmsg type=51)
独立的视频号分享(appmsg type=51)在 get_chat_history 里走 generic 分支,渲染成
无信息的 [链接/文件];而同一条视频号被引用回复(type=57 → inner 51)嵌套时反而有
[视频号] 标签(_INNER_APPMSG_TYPE_LABEL['51']),两条路径体验不一致。
新增 _format_finder_message_text(appmsg, title),把合并转发记录
_format_record_dataitem 的 datatype == '22' 已用的 finderFeed 读法接到独立消息
dispatch:
- [视频号] <昵称>: <简介>(简介截断 80 字符,与 datatype==22 行为一致)
- 缺 nickname → [视频号] {title} 兜底
在 _format_app_message_text 的 app_type == 5 之后接 app_type == 51 分支。只用上游
原生 _collapse_text + findtext,不引入新依赖。
新增 tests/test_finder_message.py(6 个用例):昵称+简介、仅昵称、简介截断、缺昵称
fallback、bare label、dispatch 端到端。fixture 全部合成。
只碰聊天侧 mcp_server.py 渲染分支。不碰 sns / 朋友圈代码路径。
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
1 parent
f80bdada3f
commit
467f1cdbe7
2 files changed
+87
No files matched your search
@@ -909,6 +909,8 @@ def _format_app_message_text(content, local_type, is_group, chat_username, chat_
|
||||
return f"[文件] {title}" if title else "[文件]"
|
||||
if app_type == 5:
|
||||
return f"[链接] {title}" if title else "[链接]"
|
||||
if app_type == 51:
|
||||
return _format_finder_message_text(appmsg, title)
|
||||
if app_type in (33, 36, 44):
|
||||
return f"[小程序] {title}" if title else "[小程序]"
|
||||
if title:
|
||||
@@ -1261,6 +1263,23 @@ def _format_redpacket_message_text(appmsg, title):
|
||||
return ' '.join(parts)
|
||||
|
||||
|
||||
def _format_finder_message_text(appmsg, title):
|
||||
"""渲染视频号分享(appmsg type=51)一行展示文本。
|
||||
|
||||
现状:独立视频号分享走 _format_app_message_text 的 generic 分支,渲染成无信息的
|
||||
[链接/文件];而同一条视频号被引用回复嵌套时反而有 [视频号] 标签
|
||||
(见 _INNER_APPMSG_TYPE_LABEL['51']),两条路径体验不一致。本 helper 把合并转发
|
||||
记录 _format_record_dataitem 的 datatype==22 已用的 finderFeed 读法接到独立消息。
|
||||
|
||||
fallback:缺 nickname → [视频号] {title};title 也缺 → [视频号]。
|
||||
"""
|
||||
nickname = _collapse_text(appmsg.findtext('finderFeed/nickname') or '')
|
||||
desc = _collapse_text(appmsg.findtext('finderFeed/desc') or '')
|
||||
if nickname:
|
||||
return f"[视频号] {nickname}: {desc[:80]}" if desc else f"[视频号] {nickname}"
|
||||
return f"[视频号] {title}" if title else "[视频号]"
|
||||
|
||||
|
||||
def _format_voip_message_text(content):
|
||||
if not content or '<voip' not in content:
|
||||
return None
|
||||
|
||||
@@ -0,0 +1,68 @@
|
||||
"""测试 appmsg type=51(视频号分享)解析与渲染。
|
||||
|
||||
此前独立视频号分享走 _format_app_message_text 的 generic 分支,渲染成无信息的
|
||||
`[链接/文件]`;而同一条视频号被引用回复嵌套时反而有 [视频号] 标签,两条路径
|
||||
体验不一致。本组用例钉住新行为:`[视频号] <昵称>: <简介>` 一行。
|
||||
|
||||
fixture 全部合成,无任何真实视频号昵称 / 简介 / id。
|
||||
"""
|
||||
import unittest
|
||||
import xml.etree.ElementTree as ET
|
||||
|
||||
import mcp_server
|
||||
|
||||
|
||||
def _appmsg(inner):
|
||||
return ET.fromstring(f'<appmsg>{inner}</appmsg>')
|
||||
|
||||
|
||||
class FormatFinderTextTests(unittest.TestCase):
|
||||
def test_nickname_and_desc(self):
|
||||
appmsg = _appmsg(
|
||||
'<title>示例标题</title>'
|
||||
'<finderFeed><nickname>示例视频号</nickname>'
|
||||
'<desc>这是一条示例视频简介</desc></finderFeed>'
|
||||
)
|
||||
out = mcp_server._format_finder_message_text(appmsg, '示例标题')
|
||||
self.assertEqual(out, '[视频号] 示例视频号: 这是一条示例视频简介')
|
||||
|
||||
def test_nickname_only(self):
|
||||
appmsg = _appmsg('<finderFeed><nickname>示例视频号</nickname></finderFeed>')
|
||||
out = mcp_server._format_finder_message_text(appmsg, 'ignored')
|
||||
self.assertEqual(out, '[视频号] 示例视频号')
|
||||
|
||||
def test_desc_truncated_to_80_chars(self):
|
||||
long_desc = '甲' * 100
|
||||
appmsg = _appmsg(
|
||||
f'<finderFeed><nickname>N</nickname><desc>{long_desc}</desc></finderFeed>'
|
||||
)
|
||||
out = mcp_server._format_finder_message_text(appmsg, 't')
|
||||
self.assertEqual(out, '[视频号] N: ' + '甲' * 80)
|
||||
|
||||
def test_missing_nickname_falls_back_to_title(self):
|
||||
appmsg = _appmsg('<finderFeed><desc>无昵称</desc></finderFeed>')
|
||||
out = mcp_server._format_finder_message_text(appmsg, '示例标题')
|
||||
self.assertEqual(out, '[视频号] 示例标题')
|
||||
|
||||
def test_missing_finderfeed_and_title_bare_label(self):
|
||||
appmsg = _appmsg('<des>x</des>')
|
||||
self.assertEqual(mcp_server._format_finder_message_text(appmsg, ''), '[视频号]')
|
||||
|
||||
def test_type51_dispatches_to_helper(self):
|
||||
# _format_app_message_text 的 type=51 分支必须走 _format_finder_message_text,
|
||||
# 不再掉进 generic [链接/文件] 兜底。
|
||||
content = (
|
||||
'<msg><appmsg><type>51</type><title>示例标题</title>'
|
||||
'<finderFeed><nickname>示例视频号</nickname>'
|
||||
'<desc>示例简介</desc></finderFeed></appmsg></msg>'
|
||||
)
|
||||
out = mcp_server._format_app_message_text(
|
||||
content, local_type=49, is_group=False,
|
||||
chat_username='wxid_synth_a', chat_display_name='Sender A', names={},
|
||||
)
|
||||
self.assertEqual(out, '[视频号] 示例视频号: 示例简介')
|
||||
self.assertNotIn('链接/文件', out)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main()
|
||||
Reference in new issue
Block a user