修复OCR消息处理多个问题

1. Task #1696: 管道符碎片过滤器修复
   - 添加疑问词例外逻辑,避免误杀包含表格内容的真实用户消息
   - 例如:'是否支持|,长期活跃奖是如何计算的'不再被丢弃

2. Task #1700: 用户消息被误判为bot回复片段的问题
   - 在'反向包含剥离bot回复片段'逻辑中添加问句豁免机制
   - 用户提问即使含与旧回复共享的片段,也不应被剥离
   - 例如:'领地系统有哪几种操作模式'不再被错误识别为bot回复

3. Task #1697: OCR消息入库时机修复
   - 在评分管道之前立即记录OCR消息到暂存表
   - 确保消息被首次OCR抓取时入库,避免历史消息被重复处理
   - 修改第二关逻辑,同时检查ocr_staging表和user_messages表
This commit is contained in:
youpaishidifu
2026-09-11 19:57:51 +08:00
parent 6f4b6a645e
commit d1ed67358e
+79 -32
View File
@@ -4663,9 +4663,15 @@ class MessageMonitor:
# —— 绿色气泡 OCR 乱码:| 管道符混中文 ——
# bot 的绿色气泡/蓝色气泡文字经过 OCR 后常把 :; 识别为 |,
# 正常中文聊天从不混用 | 与中文。如 "GU |进行配置" "SMTP | 设置"
# ⭐ Task #1696: 含疑问词的消息不丢弃(如"是否支持|,长期活跃奖是如何计算的")
if '|' in line and re.search(r'[\u4e00-\u9fff]', line) and len(line) > 5:
logger.debug(f"[{platform}] OCR 过滤: 管道符碎片(绿色气泡) line={line!r}")
continue
# 检查是否包含疑问词或问号(用户真实查询)
_has_question = re.search(r'[??吗呢多少怎么如何哪什么几]', line)
if _has_question:
logger.debug(f"[{platform}] OCR 过滤: 管道符碎片但含疑问词,放行: line={line!r}")
else:
logger.debug(f"[{platform}] OCR 过滤: 管道符碎片(绿色气泡) line={line!r}")
continue
# —— 游戏指令列表碎片:重复 /cmd 模式 ——
# OCR 截到命令帮助文本如 "/sdfl主指令 /sdfl reload /sdfl update"
@@ -10444,9 +10450,15 @@ class MessageMonitor:
logger.debug(f"[{platform}] Poll过滤: 游戏UI乱码(CJK+大写英文) ts={ts!r}")
continue
# === 绿色气泡 OCR 乱码:| 管道符混中文(正常聊天从不混用)===
# ⭐ Task #1696: 含疑问词的消息不丢弃(如"是否支持|,长期活跃奖是如何计算的")
if '|' in ts and re.search(r'[\u4e00-\u9fff]', ts) and len(ts) > 5:
logger.debug(f"[{platform}] Poll过滤: 管道符碎片(绿色气泡) ts={ts!r}")
continue
# 检查是否包含疑问词或问号(用户真实查询)
_has_question = re.search(r'[??吗呢多少怎么如何哪什么几]', ts)
if _has_question:
logger.debug(f"[{platform}] Poll过滤: 管道符碎片但含疑问词,放行: ts={ts!r}")
else:
logger.debug(f"[{platform}] Poll过滤: 管道符碎片(绿色气泡) ts={ts!r}")
continue
# === JSON/YAML 碎片兜底:OCR 截屏混入配置面板 ===
if re.match(r'^["\']?[a-zA-Z_][\w]*["\']?\s*:\s*["\d\[\{tfn]', ts):
logger.debug(f"[{platform}] Poll过滤: JSON行 ts={ts!r}")
@@ -10498,7 +10510,11 @@ class MessageMonitor:
# r="从未注册过草原探险服务器的玩家、账号。\n使用全新的邮箱进行验证。"
# → "从未注册过草原" in r, "使用全新的邮箱" in r → 剥离这两段
_has_comma = ',' in ts or (',' in ts and re.search(r'[\u4e00-\u9fff],[\u4e00-\u9fff]', ts))
if not stripped_bot and _has_comma:
# ⭐ Task #1700: 问句豁免——用户提问即使含与旧回复共享的片段,也不应被剥离
is_question_mark = bool(re.search(r'[??]', ts))
is_question_word = any(qw in ts for qw in ('吗', '呢', '多少', '怎么', '如何', '哪', '什么', '几'))
is_question = is_question_mark or is_question_word
if not stripped_bot and _has_comma and not is_question:
parts = re.split(r'[,,]', ts)
parts = [p.strip() for p in parts if p.strip()]
if len(parts) >= 2:
@@ -10905,6 +10921,15 @@ class MessageMonitor:
logger.info(f"[{platform}] 候选病句/噪声拦截: {cleaned_item[:50]!r}")
self._last_new_msg_time[platform] = time.time()
continue
# ⭐ Task #1697: 立即记录OCR消息到暂存表(评分管道之前)
# 原因:record_ocr_message()在评分管道之后才调用,导致消息在第一关/第二关失败时不入库
# 后果:历史消息每次OCR抓取都被当作"新消息"处理
# 修复:在评分管道之前立即记录,确保消息被首次OCR抓取时入库
if self._msg_history:
try:
self._msg_history.record_ocr_message(platform, cleaned_item)
except Exception as e:
logger.debug(f"[{platform}] OCR暂存记录失败(评分前): {e}")
# ⭐ 检查是否与最近bot回复内容相同(OCR噪声干扰,把bot回复当作用户消息)
# ⭐ Task #1574: 降低阈值 0.9→0.65,骨架长度 6→4,防止自言自语漏过
# ⭐ Task #1574 v2: 拆行比对——bot回复是多行(含表格),OCR只读首行
@@ -11021,28 +11046,58 @@ class MessageMonitor:
# 旧消息通过 get_latest_record_timestamp() 搭上新消息的新鲜DB记录,
# 导致 time_diff≤300s → 通关 → 分数从15→20/18→23,盖过真正的新消息。
# 修复:改为查询「本条消息自身的 DB 记录」时间戳,而非全局最新记录。
# ⭐ Task #1697: 同时检查 ocr_staging 表
# 原因:record_ocr_message()在评分管道之前调用,确保消息被首次OCR抓取时入库
# 修复:在检查user_messages表之前,先检查ocr_staging表
if self._msg_history:
# 查询本条消息自身的入库时间(created_at)
my_record_ts = self._msg_history.get_message_record_timestamp(platform, cleaned_item)
if my_record_ts is not None:
# 有本条消息的数据库记录:计算本地时间与记录时间戳的差值
time_diff_seconds = (datetime.now() - my_record_ts).total_seconds()
logger.debug(f"[{platform}] 第二关:有本条DB记录,时间差={time_diff_seconds:.0f}秒: {item[:50]}")
if time_diff_seconds <= 300:
# 本地时间 - 记录时间戳 ≤ 300秒:通关
# 先检查ocr_staging表(评分管道之前已记录)
has_ocr_record = self._msg_history.has_ocr_message_record(platform, cleaned_item)
if has_ocr_record:
# ocr_staging表中有记录:查询本条消息自身的入库时间(created_at)
my_record_ts = self._msg_history.get_message_record_timestamp(platform, cleaned_item)
if my_record_ts is not None:
# 有本条消息的数据库记录:计算本地时间与记录时间戳的差值
time_diff_seconds = (datetime.now() - my_record_ts).total_seconds()
logger.debug(f"[{platform}] 第二关:有本条DB记录(ocr_staging),时间差={time_diff_seconds:.0f}秒: {item[:50]}")
if time_diff_seconds <= 300:
# 本地时间 - 记录时间戳 ≤ 300秒:通关
old_score = score
score = max(score + 5.0, 5.0)
logger.info(f"[{platform}] 第二关通过:DB记录时间差{time_diff_seconds:.0f}秒≤300秒,放行: {old_score:.1f}→{score:.1f}: {item[:50]}")
else:
# 本地时间 - 记录时间戳 > 300秒:返团拦截
score = -100.0
logger.info(f"[{platform}] 第二关拦截:DB记录时间差{time_diff_seconds:.0f}秒>300秒,丢弃: {item[:50]}")
else:
# ocr_staging表有记录但user_messages表无记录:放行(首次抓取)
old_score = score
score = max(score + 5.0, 5.0)
logger.info(f"[{platform}] 第二关通过:DB记录时间差{time_diff_seconds:.0f}秒≤300秒,放行: {old_score:.1f}→{score:.1f}: {item[:50]}")
else:
# 本地时间 - 记录时间戳 > 300秒:返团拦截
score = -100.0
logger.info(f"[{platform}] 第二关拦截:DB记录时间差{time_diff_seconds:.0f}秒>300秒,丢弃: {item[:50]}")
logger.info(f"[{platform}] 第二关通过:ocr_staging有记录但user_messages无记录,放行: {old_score:.1f}→{score:.1f}: {item[:50]}")
else:
# 无本条消息的数据库记录:返团拦截
score = -100.0
logger.info(f"[{platform}] 第二关拦截:无本条消息DB记录,丢弃: {item[:50]}")
# ocr_staging表无记录:检查user_messages表
my_record_ts = self._msg_history.get_message_record_timestamp(platform, cleaned_item)
if my_record_ts is not None:
# 有本条消息的数据库记录:计算本地时间与记录时间戳的差值
time_diff_seconds = (datetime.now() - my_record_ts).total_seconds()
logger.debug(f"[{platform}] 第二关:有本条DB记录(user_messages),时间差={time_diff_seconds:.0f}秒: {item[:50]}")
if time_diff_seconds <= 300:
# 本地时间 - 记录时间戳 ≤ 300秒:通关
old_score = score
score = max(score + 5.0, 5.0)
logger.info(f"[{platform}] 第二关通过:DB记录时间差{time_diff_seconds:.0f}秒≤300秒,放行: {old_score:.1f}→{score:.1f}: {item[:50]}")
else:
# 本地时间 - 记录时间戳 > 300秒:返团拦截
score = -100.0
logger.info(f"[{platform}] 第二关拦截:DB记录时间差{time_diff_seconds:.0f}秒>300秒,丢弃: {item[:50]}")
else:
# 无本条消息的数据库记录:返团拦截
score = -100.0
logger.info(f"[{platform}] 第二关拦截:无本条消息DB记录,丢弃: {item[:50]}")
else:
# msg_history不可用:强制丢弃
score = -100.0
@@ -11146,15 +11201,7 @@ class MessageMonitor:
self._last_msg[platform] = _cleaned
engine_tag = self._last_chosen_engine.get(platform, "")
logger.info(f"[{platform}] 新消息[{engine_tag}]: {_raw[:60]}")
# ⭐ Task #1572: 记录OCR消息到暂存表(用于后续查重)
if self._msg_history:
# ⭐ 数据库查重:重复率≥50%就跳过该条消息(continue继续处理下一条)
# ⚠️ 注意:这里不再查重,因为第10106行已经查过了(且用了exclude_self=True排除自身)
# 如果在这里再查一次,刚入库的消息会被自身匹配上,导致误跳过
try:
self._msg_history.record_ocr_message(platform, _cleaned)
except Exception as e:
logger.debug(f"[{platform}] OCR暂存记录失败: {e}")
# ⭐ Task #1697: OCR消息已在评分管道之前记录到ocr_staging表,此处不再重复记录
# ⚠️ 急停检查:回复前最后检查,防止急停后仍然发送
if self._stop_event.is_set() or not self._running:
return