diff --git a/admin/dashboard/blueprints/messages.py b/admin/dashboard/blueprints/messages.py index cd8b768..1b0d298 100644 --- a/admin/dashboard/blueprints/messages.py +++ b/admin/dashboard/blueprints/messages.py @@ -123,18 +123,24 @@ def get_messages(): msg['quoted_preview_image'] = quote_data.get('preview_image', '') msg['quoted_preview_video_thumb'] = quote_data.get('preview_video_thumb', '') msg['quoted_reference_svrid'] = quote_data.get('reference_svrid', '') + msg['quoted_reference_md5'] = quote_data.get('reference_md5', '') # 优先使用原始被引用消息自己已落库的图片/缩略图地址 reference_svrid = quote_data.get('reference_svrid') + reference_md5 = quote_data.get('reference_md5') + referenced_msg = None if reference_svrid: referenced_msg = server.message_storage.get_message_by_message_id(reference_svrid) - if referenced_msg: - if msg['quoted_type'] == 'image': - image_path = referenced_msg.get('image_path') or '' - msg['quoted_preview_image'] = image_path if _is_usable_local_media_path(image_path) else '' - elif msg['quoted_type'] == 'video': - video_thumb = referenced_msg.get('message_thumb') or referenced_msg.get('image_path') or '' - msg['quoted_preview_video_thumb'] = video_thumb if _is_usable_local_media_path(video_thumb) else '' + if not referenced_msg and reference_md5 and msg['quoted_type'] == 'image': + referenced_msg = server.message_storage.get_image_message_by_md5(reference_md5) + + if referenced_msg: + if msg['quoted_type'] == 'image': + image_path = referenced_msg.get('image_path') or '' + msg['quoted_preview_image'] = image_path if _is_usable_local_media_path(image_path) else '' + elif msg['quoted_type'] == 'video': + video_thumb = referenced_msg.get('message_thumb') or referenced_msg.get('image_path') or '' + msg['quoted_preview_video_thumb'] = video_thumb if _is_usable_local_media_path(video_thumb) else '' else: # 其他类型的应用消息,解析 XML 提取标题 root = ET.fromstring(msg['content']) diff --git a/admin/dashboard/templates/message_list.html b/admin/dashboard/templates/message_list.html index e3b0e30..94948319 100644 --- a/admin/dashboard/templates/message_list.html +++ b/admin/dashboard/templates/message_list.html @@ -94,7 +94,7 @@
{% raw %}{{ scope.row.content || `【消息类型: ${scope.row.message_type}】` }}{% endraw %} -
+
【引用图片】
@@ -323,6 +323,7 @@ }, showQuotedImage(url) { const resolvedUrl = this.getQuotedPreviewUrl(url); + if (!resolvedUrl) return; this.selectedMessage = { image_path: '', message_thumb: resolvedUrl }; this.imageDialogVisible = true; }, @@ -373,8 +374,27 @@ } return `/api/messages/media_proxy?url=${encodeURIComponent(url)}`; }, + isUsableQuotedPreview(url) { + if (!url) return false; + if (url.startsWith('http://') || url.startsWith('https://')) { + return true; + } + if (url.includes('static/images') || url.includes('static\\images')) { + return true; + } + if (url.includes('/') || url.includes('\\')) { + return true; + } + return false; + }, + hasQuotedPreview(url) { + return !!this.getQuotedPreviewUrl(url); + }, getQuotedPreviewUrl(url) { if (!url) return ''; + if (!this.isUsableQuotedPreview(url)) { + return ''; + } if (url.startsWith('http://') || url.startsWith('https://')) { return this.getMediaProxyUrl(url); } diff --git a/db/message_storage.py b/db/message_storage.py index 9024b6c..662ab1d 100644 --- a/db/message_storage.py +++ b/db/message_storage.py @@ -73,6 +73,21 @@ class MessageStorageDB(BaseDBOperator): """ return self.execute_query(sql, (message_id,), fetch_one=True) + def get_image_message_by_md5(self, md5: str) -> Optional[Dict]: + """根据图片消息 attachment_url 中的 md5 反查原图消息""" + sql = """ + SELECT id, group_id, timestamp, sender, content, message_type, + attachment_url, message_id, message_xml, message_thumb, image_path + FROM messages + WHERE message_type = 3 + AND attachment_url IS NOT NULL + AND attachment_url <> '' + AND attachment_url LIKE %s + ORDER BY id DESC + LIMIT 1 + """ + return self.execute_query(sql, (f'%md5="{md5}"%',), fetch_one=True) + def get_member_recent_messages(self, group_id: str, wxid: str, days: int = 30, limit: int = 200, include_today: bool = True) -> List[Dict]: """获取指定群成员近期消息""" diff --git a/utils/message_formatter.py b/utils/message_formatter.py index 5258fa5..008fff9 100644 --- a/utils/message_formatter.py +++ b/utils/message_formatter.py @@ -77,6 +77,10 @@ def parse_quote_message(xml_content: str) -> dict: quoted_content = _extract_first(r'.*?(.*?)', xml_content) ref_type = _extract_first(r'.*?(.*?)', xml_content) reference_svrid = _extract_first(r'.*?(.*?)', xml_content) + reference_md5 = ( + _extract_first(r'md5="(.*?)"', html.unescape(quoted_content or "")) + or _extract_first(r"(.*?)", html.unescape(quoted_content or "")) + ) pretty_reference = _format_referenced_content(ref_type, quoted_content, xml_content) media_preview = _extract_media_preview(ref_type, quoted_content) @@ -85,6 +89,7 @@ def parse_quote_message(xml_content: str) -> dict: "display_name": display_name, "quoted_content": pretty_reference, "reference_svrid": reference_svrid, + "reference_md5": reference_md5, "reference_type": media_preview.get("reference_type", "text"), "preview_image": media_preview.get("preview_image", ""), "preview_video_thumb": media_preview.get("preview_video_thumb", ""),