From 030680eb8564a03133c36360cc3672d7ee4f58e8 Mon Sep 17 00:00:00 2001 From: ylytdeng Date: Mon, 9 Mar 2026 19:52:46 +0800 Subject: [PATCH] =?UTF-8?q?fix:=20=E4=BF=AE=E5=A4=8D=E7=9F=AD=E6=97=B6?= =?UTF-8?q?=E9=97=B4=E5=A4=A7=E9=87=8F=E6=B6=88=E6=81=AF=E4=B8=A2=E5=A4=B1?= =?UTF-8?q?=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 旧逻辑用 `if ts == prev_ts: continue` 粗暴跳过上轮时间戳的所有消息, 但同一秒内可能有多条不同消息(如连续转发公众号文章),导致只显示 最后一条,其余丢失。 改为用 (username, timestamp, msg_type) 精确去重: - 主消息和 hidden 消息显示后都记录到 _shown_keys - 过滤时精确匹配已显示的消息,不再按时间戳整体跳过 - _shown_keys 每轮清理过期条目(保留 5 分钟),防止内存泄漏 Fixes #20 Co-Authored-By: Claude Opus 4.6 --- monitor_web.py | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/monitor_web.py b/monitor_web.py index 78e2957..76cdfc9 100644 --- a/monitor_web.py +++ b/monitor_web.py @@ -540,6 +540,8 @@ class SessionMonitor: self.prev_state = {} self.decrypt_ms = 0 self.patched_pages = 0 + # 已显示消息去重: {(username, timestamp, base_msg_type), ...} + self._shown_keys = set() def resolve_image(self, username, timestamp): """解密图片: username+timestamp → 解密后的图片文件名,失败返回 None""" @@ -860,11 +862,8 @@ class SessionMonitor: hidden_msgs = [] for ts, lt, mc, ct in all_rows: base = lt % 4294967296 if lt > 4294967296 else lt - # 跳过与 session 当前消息相同时间戳+类型的(已显示) - if ts == curr_ts and base == curr_msg_type: - continue - # 跳过 prev_ts 的消息(上一轮已显示) - if ts == prev_ts: + # 跳过已显示的消息(精确匹配 username+timestamp+type) + if (username, ts, base) in self._shown_keys: continue # 解压 zstd if isinstance(mc, bytes) and ct == 4: @@ -883,6 +882,7 @@ class SessionMonitor: global messages_log for ts, base, mc in hidden_msgs: + self._shown_keys.add((username, ts, base)) msg_data = { 'time': datetime.fromtimestamp(ts).strftime('%H:%M:%S'), 'timestamp': ts, @@ -1303,6 +1303,7 @@ class SessionMonitor: } new_msgs.append(msg_data) + self._shown_keys.add((username, curr['timestamp'], curr['msg_type'])) # 图片消息: 后台异步解密(不阻塞轮询) if curr['msg_type'] == 3: @@ -1353,6 +1354,10 @@ class SessionMonitor: self.prev_state = curr_state + # 清理过期的去重 key(保留最近 5 分钟) + cutoff = int(time.time()) - 300 + self._shown_keys = {k for k in self._shown_keys if k[1] > cutoff} + def monitor_thread(enc_key, session_db, contact_names, db_cache=None, username_db_map=None): mon = SessionMonitor(enc_key, session_db, contact_names, db_cache, username_db_map) wal_path = mon.wal_path