From e823c1049f3b7d7e603a8906a9660923e6bf3089 Mon Sep 17 00:00:00 2001 From: liuwei Date: Tue, 7 Apr 2026 17:40:10 +0800 Subject: [PATCH] fix: proxy quoted media in dashboard messages --- admin/dashboard/blueprints/messages.py | 63 +++++++++++++++++++-- admin/dashboard/templates/message_list.html | 9 ++- 2 files changed, 65 insertions(+), 7 deletions(-) diff --git a/admin/dashboard/blueprints/messages.py b/admin/dashboard/blueprints/messages.py index ec023cf..4fd6a4f 100644 --- a/admin/dashboard/blueprints/messages.py +++ b/admin/dashboard/blueprints/messages.py @@ -1,4 +1,5 @@ -from flask import Blueprint, render_template, jsonify, request, current_app +import requests +from flask import Blueprint, render_template, jsonify, request, current_app, Response, stream_with_context from .auth import login_required from loguru import logger import xml.etree.ElementTree as ET @@ -17,6 +18,41 @@ def _is_emoji_message(msg: dict) -> bool: return message_type in {'47', '1048625', '1090519089'} or any(marker in content for marker in xml_markers) +def _proxy_remote_media(target_url: str) -> Response: + if not target_url: + return Response("missing url", status=400) + + headers = { + "User-Agent": ( + "Mozilla/5.0 (Windows NT 10.0; Win64; x64) " + "AppleWebKit/537.36 (KHTML, like Gecko) " + "Chrome/123.0.0.0 Safari/537.36" + ), + "Referer": "http://weixin.qq.com/" + } + range_header = request.headers.get("Range") + if range_header: + headers["Range"] = range_header + + upstream = requests.get(target_url, headers=headers, stream=True, timeout=30) + + response_headers = {} + for key in ("Content-Type", "Content-Length", "Content-Range", "Accept-Ranges", "Cache-Control", "ETag", "Last-Modified"): + value = upstream.headers.get(key) + if value: + response_headers[key] = value + + if "Accept-Ranges" not in response_headers: + response_headers["Accept-Ranges"] = "bytes" + + return Response( + stream_with_context(upstream.iter_content(chunk_size=64 * 1024)), + status=upstream.status_code, + headers=response_headers, + direct_passthrough=True + ) + + # 消息列表页面 @messages_bp.route('/messages') @login_required @@ -63,16 +99,16 @@ def get_messages(): continue # 处理消息内容,格式化引用消息 - if msg['message_type'] == "49" and msg['content']: # 应用消息类型 + if str(msg.get('message_type')) == "49" and msg.get('content'): # 应用消息类型 try: # 检查是否为引用消息 if '' in msg['content']: # 使用格式化工具处理引用消息 quote_data = parse_quote_message(msg['content']) - msg['content'] = quote_data['formatted_message'] - msg['quoted_type'] = quote_data['reference_type'] - msg['quoted_preview_image'] = quote_data['preview_image'] - msg['quoted_preview_video_thumb'] = quote_data['preview_video_thumb'] + msg['content'] = quote_data.get('formatted_message') or format_quote_message(msg['content']) + msg['quoted_type'] = quote_data.get('reference_type', '') + msg['quoted_preview_image'] = quote_data.get('preview_image', '') + msg['quoted_preview_video_thumb'] = quote_data.get('preview_video_thumb', '') else: # 其他类型的应用消息,解析 XML 提取标题 root = ET.fromstring(msg['content']) @@ -81,6 +117,10 @@ def get_messages(): msg['content'] = title_elem.text except Exception as e: logger.error(f"解析消息类型49出错: {e}") + try: + msg['content'] = format_quote_message(msg['content']) + except Exception: + pass return jsonify(result) except Exception as e: @@ -143,3 +183,14 @@ def get_hourly_message_trend(): except Exception as e: logger.error(f"获取按小时聊天趋势数据失败: {e}") return jsonify({'success': False, 'error': str(e)}), 500 + + +@messages_bp.route('/api/messages/media_proxy', methods=['GET']) +@login_required +def message_media_proxy(): + target_url = request.args.get('url', '').strip() + try: + return _proxy_remote_media(target_url) + except Exception as e: + logger.error(f"消息媒体代理失败: {e}") + return Response(f"proxy failed: {e}", status=502) diff --git a/admin/dashboard/templates/message_list.html b/admin/dashboard/templates/message_list.html index 915678e..98e6c6c 100644 --- a/admin/dashboard/templates/message_list.html +++ b/admin/dashboard/templates/message_list.html @@ -96,7 +96,7 @@ {% raw %}{{ scope.row.content || `【消息类型: ${scope.row.message_type}】` }}{% endraw %}
【引用图片】
- +
【引用视频】
@@ -361,6 +361,13 @@ } return `/static/images/${fileName}`; + }, + getMediaProxyUrl(url) { + if (!url) return ''; + if (url.startsWith('/api/messages/media_proxy')) { + return url; + } + return `/api/messages/media_proxy?url=${encodeURIComponent(url)}`; } }, watch: {