修复三个问题:消息搅和打包、QQ系统消息过滤、OCR噪声残片过滤

1. 消息搅和打包修复(#1414):
   - 将'同poll多条取最后'逻辑改为遍历处理所有消息
   - 每条消息独立处理,不再丢弃旧消息
   - 基线轮只处理一次,后续消息正常处理

2. QQ系统消息残片过滤(#1415):
   - 添加'你撤回了, 条消息'模式匹配
   - 添加'撤回了一条消息'模式匹配
   - 在_is_system_message函数中实现

3. OCR噪声残片过滤:
   - 添加'留, C'等逗号+字母组合噪声模式
   - 添加纯逗号+字母噪声模式
   - 在_is_noise_text函数中实现
This commit is contained in:
youpaishidifu
2026-08-06 16:56:28 +08:00
parent 4c044c6e7c
commit 1291a402de
+37 -28
View File
@@ -4281,6 +4281,11 @@ class MessageMonitor:
for p in system_patterns:
if text.startswith(p):
return True
# QQ系统消息残片:撤回了一条消息、你撤回了, 条消息等
if re.match(r'^(?:你|对方|他|她|管理员)\s*撤回了\s*,?\s*\d*\s*条?\s*消息', text):
return True
if re.match(r'^撤回了一条消息', text):
return True
# 纯时间格式(不含消息内容)
if re.match(r'^\d{1,2}:\d{2}', text):
return True
@@ -4560,6 +4565,14 @@ class MessageMonitor:
if re.search(r'[§¶©®™]', text):
logger.debug(f"[ocr] 过滤器2结果: 检测到OCR乱码字符,返回True")
return True
# OCR短噪声残片:如"留, C"、"留, Q"等逗号+单字母组合(OCR截断残留)
if re.match(r'^[\u4e00-\u9fff]{1,2}\s*[,,]\s*[A-Za-z]{1,2}$', text):
logger.debug(f"[ocr] 过滤器2结果: 检测到OCR短噪声残片,返回True")
return True
# 纯逗号+字母噪声(如", C"、", Q")
if re.match(r'^[,,]\s*[A-Za-z]{1,2}$', text):
logger.debug(f"[ocr] 过滤器2结果: 检测到纯逗号字母噪声,返回True")
return True
# Windows系统进程名/窗口标题:任务管理器、控制面板、文件资源管理器等
if re.search(r'^(?:任务管理器|控制面板|文件资源管理器|回收站|计算器|画图|记事本|命令提示符|'
r'Windows安全中心|设置|网络和共享中心|设备管理器|磁盘管理|注册表编辑器|'
@@ -5682,35 +5695,31 @@ class MessageMonitor:
if now - v < 60.0
}
# ⭐ 取最后一条(屏幕最下方 = 最新消息)
# OCR 从上到下读屏,旧消息排前面,新消息排后面。
# 原"只处理第一条"逻辑把旧历史消息当新消息处理,
# 真正的用户新消息被"同poll第条跳过"挡掉。
item = new_items[-1]
if len(new_items) > 1:
logger.debug(f"[{platform}] 同poll多条({len(new_items)}条),取最后(最新): {item[:40]}")
# 其余加入 seen 防止下次重复
for skipped in new_items[:-1]:
seen.add(skipped)
# ⭐ 首次成功轮询(上次无消息→本次有消息):拦截历史基线
is_baseline = self._last_new_msg_time.get(platform, 0) == 0
if is_baseline:
# 基线轮:日志标记但不触发回复。
# ⚠ 四项关键处置,防止"基线吞消息后永不回复":
# 1. 从 seen 移除 item(4383 循环已 seen.add)——否则用户重发
# 相同内容 → ts in seen 永久拦截
# 2. 清空 _last_sig ——否则下一轮 sig 相同 → 4131 静默跳过,
# 用户刚发的新消息被基线误吞后永远等不到回复
# 3. 不 seen.add ——防重复由 sig 静默跳过承担
# 4. ⭐ 清除截图 hash——否则下次轮询时 hash 相同直接跳过 OCR,
# 消息永远等不到回复(截图内容没变但消息需要被处理)
logger.info(f"[{platform}] 基线↺(不回复): {item[:60]}")
seen.discard(item)
self._last_sig[platform] = ""
self._last_screenshot_hash.pop(platform, None)
self._last_new_msg_time[platform] = time.time()
else:
baseline_handled = False # 基线轮只处理一次(标记第一条消息)
# ⭐ 遍历处理所有新消息(不再只取最后一条)
for item in new_items:
# ⭐ 基线轮:只标记第一条消息,不触发回复
if is_baseline and not baseline_handled:
# 基线轮:日志标记但不触发回复。
# ⚠ 四项关键处置,防止"基线吞消息后永不回复":
# 1. 从 seen 移除 item(4383 循环已 seen.add)——否则用户重发
# 相同内容 → ts in seen 永久拦截
# 2. 清空 _last_sig ——否则下一轮 sig 相同 → 4131 静默跳过,
# 用户刚发的新消息被基线误吞后永远等不到回复
# 3. 不 seen.add ——防重复由 sig 静默跳过承担
# 4. ⭐ 清除截图 hash——否则下次轮询时 hash 相同直接跳过 OCR,
# 消息永远等不到回复(截图内容没变但消息需要被处理)
logger.info(f"[{platform}] 基线↺(不回复): {item[:60]}")
seen.discard(item)
self._last_sig[platform] = ""
self._last_screenshot_hash.pop(platform, None)
self._last_new_msg_time[platform] = time.time()
baseline_handled = True
continue # 基线轮不处理,跳到下一条
# ⭐ 清理OCR噪声前缀(如"必选,")后再搜索知识库
cleaned_item = self._clean_ocr_prefix(item)
# ⭐ 检查是否与最近bot回复内容相同(OCR噪声干扰,把bot回复当作用户消息)
@@ -5721,7 +5730,7 @@ class MessageMonitor:
logger.info(f"[{platform}] OCR噪声检测: 与最近bot回复相似度{similarity:.2%},跳过: {cleaned_item[:60]}")
seen.add(cleaned_item)
self._last_new_msg_time[platform] = time.time()
return # 跳过本条消息处理
continue # 跳过本条消息,处理下一条
self._last_new_msg_time[platform] = time.time()
self._last_msg[platform] = cleaned_item
engine_tag = self._last_chosen_engine.get(platform, "")