diff --git a/frontend/lib/chat/message-normalizer.js b/frontend/lib/chat/message-normalizer.js index aef313f..7b0cec4 100644 --- a/frontend/lib/chat/message-normalizer.js +++ b/frontend/lib/chat/message-normalizer.js @@ -227,6 +227,8 @@ export const createMessageNormalizer = ({ apiBase, getSelectedAccount, getSelect _quoteThumbError: false, amount: msg.amount || '', coverUrl: msg.coverUrl || '', + objectId: String(msg.objectId || '').trim(), + objectNonceId: String(msg.objectNonceId || '').trim(), fileSize: msg.fileSize || '', fileMd5: msg.fileMd5 || '', paySubType: msg.paySubType || '', diff --git a/src/wechat_decrypt_tool/chat_export_service.py b/src/wechat_decrypt_tool/chat_export_service.py index 8ef9ec6..3f721e3 100644 --- a/src/wechat_decrypt_tool/chat_export_service.py +++ b/src/wechat_decrypt_tool/chat_export_service.py @@ -3435,6 +3435,8 @@ def _parse_message_for_export( quote_voice_length = "" quote_title = "" quote_content = "" + object_id = "" + object_nonce_id = "" amount = "" cover_url = "" file_size = "" @@ -3472,6 +3474,8 @@ def _parse_message_for_export( from_username = str(parsed.get("fromUsername") or "") link_type = str(parsed.get("linkType") or "") link_style = str(parsed.get("linkStyle") or "") + object_id = str(parsed.get("objectId") or "") + object_nonce_id = str(parsed.get("objectNonceId") or "") record_item = str(parsed.get("recordItem") or "") quote_username = str(parsed.get("quoteUsername") or "") quote_server_id = str(parsed.get("quoteServerId") or "") @@ -3699,6 +3703,8 @@ def _parse_message_for_export( from_username = str(parsed.get("fromUsername") or from_username) link_type = str(parsed.get("linkType") or link_type) link_style = str(parsed.get("linkStyle") or link_style) + object_id = str(parsed.get("objectId") or object_id) + object_nonce_id = str(parsed.get("objectNonceId") or object_nonce_id) record_item = str(parsed.get("recordItem") or record_item) quote_username = str(parsed.get("quoteUsername") or quote_username) quote_server_id = str(parsed.get("quoteServerId") or quote_server_id) @@ -3765,6 +3771,8 @@ def _parse_message_for_export( "fromUsername": from_username, "linkType": link_type, "linkStyle": link_style, + "objectId": object_id, + "objectNonceId": object_nonce_id, "recordItem": record_item, "thumbUrl": thumb_url, "imageMd5": image_md5, diff --git a/src/wechat_decrypt_tool/chat_helpers.py b/src/wechat_decrypt_tool/chat_helpers.py index 90267aa..b02ae11 100644 --- a/src/wechat_decrypt_tool/chat_helpers.py +++ b/src/wechat_decrypt_tool/chat_helpers.py @@ -1238,6 +1238,14 @@ def _parse_app_message(text: str) -> dict[str, Any]: or (_extract_xml_tag_text(finder_feed, "username") if finder_feed else "") or (_extract_xml_tag_text(finder_feed, "finderusername") if finder_feed else "") ) + object_id = ( + (_extract_xml_tag_or_attr(finder_feed, "objectid") if finder_feed else "") + or _extract_xml_tag_or_attr(text, "objectid") + ) + object_nonce_id = ( + (_extract_xml_tag_or_attr(finder_feed, "objectnonceid") if finder_feed else "") + or _extract_xml_tag_or_attr(text, "objectnonceid") + ) thumb_url = _normalize_xml_url( _extract_xml_tag_or_attr(text, "thumburl") @@ -1277,6 +1285,8 @@ def _parse_app_message(text: str) -> dict[str, Any]: "fromUsername": from_u, "linkType": "finder", "linkStyle": "finder", + "objectId": str(object_id or "").strip(), + "objectNonceId": str(object_nonce_id or "").strip(), } if app_type in (33, 36): @@ -2418,6 +2428,8 @@ def _row_to_search_hit( quote_thumb_url = "" link_type = "" link_style = "" + object_id = "" + object_nonce_id = "" amount = "" pay_sub_type = "" transfer_status = "" @@ -2441,6 +2453,8 @@ def _row_to_search_hit( quote_thumb_url = str(parsed.get("quoteThumbUrl") or "") link_type = str(parsed.get("linkType") or "") link_style = str(parsed.get("linkStyle") or "") + object_id = str(parsed.get("objectId") or "") + object_nonce_id = str(parsed.get("objectNonceId") or "") quote_username = str(parsed.get("quoteUsername") or "") amount = str(parsed.get("amount") or "") pay_sub_type = str(parsed.get("paySubType") or "") @@ -2526,6 +2540,8 @@ def _row_to_search_hit( quote_thumb_url = str(parsed.get("quoteThumbUrl") or quote_thumb_url) link_type = str(parsed.get("linkType") or link_type) link_style = str(parsed.get("linkStyle") or link_style) + object_id = str(parsed.get("objectId") or object_id) + object_nonce_id = str(parsed.get("objectNonceId") or object_nonce_id) amount = str(parsed.get("amount") or amount) pay_sub_type = str(parsed.get("paySubType") or pay_sub_type) quote_username = str(parsed.get("quoteUsername") or quote_username) @@ -2567,6 +2583,8 @@ def _row_to_search_hit( "url": url, "linkType": link_type, "linkStyle": link_style, + "objectId": object_id, + "objectNonceId": object_nonce_id, "quoteUsername": quote_username, "quoteTitle": quote_title, "quoteContent": quote_content, diff --git a/src/wechat_decrypt_tool/routers/chat.py b/src/wechat_decrypt_tool/routers/chat.py index 8ab0e92..ae117af 100644 --- a/src/wechat_decrypt_tool/routers/chat.py +++ b/src/wechat_decrypt_tool/routers/chat.py @@ -3049,6 +3049,8 @@ def _append_full_messages_from_rows( quote_thumb_url = "" link_type = "" link_style = "" + object_id = "" + object_nonce_id = "" quote_server_id = "" quote_type = "" quote_voice_length = "" @@ -3082,6 +3084,8 @@ def _append_full_messages_from_rows( quote_thumb_url = str(parsed.get("quoteThumbUrl") or "") link_type = str(parsed.get("linkType") or "") link_style = str(parsed.get("linkStyle") or "") + object_id = str(parsed.get("objectId") or "") + object_nonce_id = str(parsed.get("objectNonceId") or "") quote_username = str(parsed.get("quoteUsername") or "") quote_server_id = str(parsed.get("quoteServerId") or "") quote_type = str(parsed.get("quoteType") or "") @@ -3324,6 +3328,8 @@ def _append_full_messages_from_rows( quote_thumb_url = str(parsed.get("quoteThumbUrl") or quote_thumb_url) link_type = str(parsed.get("linkType") or link_type) link_style = str(parsed.get("linkStyle") or link_style) + object_id = str(parsed.get("objectId") or object_id) + object_nonce_id = str(parsed.get("objectNonceId") or object_nonce_id) amount = str(parsed.get("amount") or amount) cover_url = str(parsed.get("coverUrl") or cover_url) thumb_url = str(parsed.get("thumbUrl") or thumb_url) @@ -3382,6 +3388,8 @@ def _append_full_messages_from_rows( "url": url, "linkType": link_type, "linkStyle": link_style, + "objectId": object_id, + "objectNonceId": object_nonce_id, "from": from_name, "fromUsername": from_username, "recordItem": record_item, @@ -4584,6 +4592,8 @@ def _collect_chat_messages( quote_thumb_url = "" link_type = "" link_style = "" + object_id = "" + object_nonce_id = "" quote_server_id = "" quote_type = "" quote_voice_length = "" @@ -4617,6 +4627,8 @@ def _collect_chat_messages( quote_thumb_url = str(parsed.get("quoteThumbUrl") or "") link_type = str(parsed.get("linkType") or "") link_style = str(parsed.get("linkStyle") or "") + object_id = str(parsed.get("objectId") or "") + object_nonce_id = str(parsed.get("objectNonceId") or "") quote_username = str(parsed.get("quoteUsername") or "") quote_server_id = str(parsed.get("quoteServerId") or "") quote_type = str(parsed.get("quoteType") or "") @@ -4838,6 +4850,8 @@ def _collect_chat_messages( quote_thumb_url = str(parsed.get("quoteThumbUrl") or quote_thumb_url) link_type = str(parsed.get("linkType") or link_type) link_style = str(parsed.get("linkStyle") or link_style) + object_id = str(parsed.get("objectId") or object_id) + object_nonce_id = str(parsed.get("objectNonceId") or object_nonce_id) amount = str(parsed.get("amount") or amount) cover_url = str(parsed.get("coverUrl") or cover_url) thumb_url = str(parsed.get("thumbUrl") or thumb_url) @@ -4901,6 +4915,8 @@ def _collect_chat_messages( "url": url, "linkType": link_type, "linkStyle": link_style, + "objectId": object_id, + "objectNonceId": object_nonce_id, "from": from_name, "fromUsername": from_username, "recordItem": record_item, @@ -5502,6 +5518,8 @@ def list_chat_messages( quote_thumb_url = "" link_type = "" link_style = "" + object_id = "" + object_nonce_id = "" quote_server_id = "" quote_type = "" quote_voice_length = "" @@ -5531,6 +5549,8 @@ def list_chat_messages( quote_thumb_url = str(parsed.get("quoteThumbUrl") or "") link_type = str(parsed.get("linkType") or "") link_style = str(parsed.get("linkStyle") or "") + object_id = str(parsed.get("objectId") or "") + object_nonce_id = str(parsed.get("objectNonceId") or "") quote_username = str(parsed.get("quoteUsername") or "") quote_server_id = str(parsed.get("quoteServerId") or "") quote_type = str(parsed.get("quoteType") or "") @@ -5736,6 +5756,8 @@ def list_chat_messages( quote_thumb_url = str(parsed.get("quoteThumbUrl") or quote_thumb_url) link_type = str(parsed.get("linkType") or link_type) link_style = str(parsed.get("linkStyle") or link_style) + object_id = str(parsed.get("objectId") or object_id) + object_nonce_id = str(parsed.get("objectNonceId") or object_nonce_id) amount = str(parsed.get("amount") or amount) cover_url = str(parsed.get("coverUrl") or cover_url) thumb_url = str(parsed.get("thumbUrl") or thumb_url) @@ -5788,6 +5810,8 @@ def list_chat_messages( "url": url, "linkType": link_type, "linkStyle": link_style, + "objectId": object_id, + "objectNonceId": object_nonce_id, "from": from_name, "fromUsername": from_username, "recordItem": record_item, @@ -7796,6 +7820,8 @@ async def resolve_app_message( "fromUsername": str(parsed.get("fromUsername") or "").strip(), "linkType": str(parsed.get("linkType") or "").strip(), "linkStyle": str(parsed.get("linkStyle") or "").strip(), + "objectId": str(parsed.get("objectId") or "").strip(), + "objectNonceId": str(parsed.get("objectNonceId") or "").strip(), "size": str(parsed.get("size") or "").strip(), "baseUrl": base_url, } diff --git a/tests/test_parse_app_message.py b/tests/test_parse_app_message.py index d0ec3ab..c7308f1 100644 --- a/tests/test_parse_app_message.py +++ b/tests/test_parse_app_message.py @@ -148,6 +148,26 @@ class TestParseAppMessage(unittest.TestCase): self.assertEqual(parsed.get("thumbUrl"), "https://finder.video.qq.com/cover.jpg") self.assertEqual(parsed.get("url"), "https://channels.weixin.qq.com/web/pages/feed?feedid=abc") + def test_finder_type_51_exposes_object_fields(self): + raw_text = ( + '' + '当前版本不支持展示该内容,请升级至最新版本。' + '' + '51' + '' + '' + '' + '' + '' + '' + ) + + parsed = _parse_app_message(raw_text) + + self.assertEqual(parsed.get("linkType"), "finder") + self.assertEqual(parsed.get("objectId"), "1234567890") + self.assertEqual(parsed.get("objectNonceId"), "nonce-abc") + def test_quote_type_5_nested_xml_refermsg_uses_inner_title(self): raw_text = ( ''