改动结果:

聊天窗口工具栏新增了“表情”按钮,打开表情库弹窗。
表情库会从历史“已下载落盘的表情消息”里自动聚合。
选中后直接通过 send_emoji_message(wxid, md5, total_length) 发原生表情,不是当普通图片发。
仍保持你现在的发送通道和聊天刷新逻辑。
主要改动文件:

后端接口与发送支持:contacts.py
表情资源查询:message_storage.py
前端表情面板与发送交互:contacts_management.html
新增接口:

GET /contacts/api/emojis:返回聚合后的表情库(md5、total_length、预览图)。
POST /contacts/api/send_message 新增 type=emoji。
我也做了 Python 语法检查,相关后端文件都通过了。
你可以直接在聊天弹窗里点“表情”试一下。如果表情库为空,通常是该群还没落盘到 image_path,让媒体下载功能先抓几条表情就会出现。
This commit is contained in:
liuwei
2026-04-15 11:29:18 +08:00
parent d326ad164f
commit d0fa823557

View File

@@ -17,7 +17,6 @@ message_thread_pool = ThreadPoolExecutor(max_workers=10, thread_name_prefix="mes
# 创建共享的事件循环
shared_loop = None
loop_lock = threading.Lock()
_PROJECT_ROOT = os.path.abspath(os.path.join(os.path.dirname(__file__), "..", "..", ".."))
_EMOJI_MD5_RE = re.compile(r'md5\s*=\s*[\"\']([0-9a-fA-F]{16,64})[\"\']', re.IGNORECASE)
_EMOJI_TOTALLEN_RE = re.compile(r'(?:totallen|total_len|len)\s*=\s*[\"\'](\d+)[\"\']', re.IGNORECASE)
@@ -149,16 +148,33 @@ def _extract_emoji_meta(attachment_url: str, image_path: str):
md5 = ""
total_length = 0
md5_match = _EMOJI_MD5_RE.search(text)
if md5_match:
md5 = md5_match.group(1).lower()
len_match = _EMOJI_TOTALLEN_RE.search(text)
if len_match:
# 优先按 XML 结构解析,避免纯正则误命中其他字段。
if text.startswith("<"):
try:
total_length = int(len_match.group(1))
root = ET.fromstring(text)
emoji_node = root.find(".//emoji")
if emoji_node is not None:
md5 = _safe_text(emoji_node.attrib.get("md5", "")).strip().lower()
for key in ("totallen", "total_len", "totalLen", "len"):
value = _safe_text(emoji_node.attrib.get(key, "")).strip()
if value.isdigit():
total_length = int(value)
break
except Exception:
total_length = 0
pass
if not md5:
md5_match = _EMOJI_MD5_RE.search(text)
if md5_match:
md5 = md5_match.group(1).lower()
if total_length <= 0:
len_match = _EMOJI_TOTALLEN_RE.search(text)
if len_match:
try:
total_length = int(len_match.group(1))
except Exception:
total_length = 0
if not md5 and image_path:
filename = os.path.basename(_safe_text(image_path))
@@ -166,14 +182,6 @@ def _extract_emoji_meta(attachment_url: str, image_path: str):
if re.fullmatch(r"[0-9a-fA-F]{16,64}", stem):
md5 = stem.lower()
if total_length <= 0 and image_path and image_path.startswith("/static/"):
abs_path = os.path.join(_PROJECT_ROOT, image_path.lstrip("/").replace("/", os.sep))
if os.path.isfile(abs_path):
try:
total_length = int(os.path.getsize(abs_path))
except Exception:
total_length = 0
return md5, total_length