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 = (
''