From d043da57e3aac11c89c73bb1b3af85ff8955b9e8 Mon Sep 17 00:00:00 2001 From: youpaishidifu Date: Wed, 12 Aug 2026 16:48:26 +0800 Subject: [PATCH] =?UTF-8?q?fix(ocr):=20=E4=BF=AE=E5=A4=8D=E5=A4=9A?= =?UTF-8?q?=E6=97=B6=E9=97=B4=E6=88=B3=E5=9C=BA=E6=99=AF=E4=B8=8B=E7=94=A8?= =?UTF-8?q?=E6=88=B7=E6=8F=90=E9=97=AE=E8=A2=AB=E8=AF=AF=E4=B8=A2=20(#1531?= =?UTF-8?q?=20#1532)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 1. stage splitting 时间戳规整:第二段分隔符由 \2(同分隔符) 放宽为 [.;:], 使 15;27.05 这类 EasyOCR 混合分隔读法被正确识别为 15:27:05, 修复 _inherited_ts 不更新导致下方消息继承陈旧时间戳被'尾部较老'误杀。 2. 过滤器层调整顺序:先剥 bot 旧回复片段,再剥 md 尾巴。OCR 把 '用户新消息+bot旧回复'拼行时,md 尾巴剥离会在 bot 回复的 '::|::' 残片处切断、误删用户提问,导致 filter4 整体丢弃。 3. _is_qq_ui_element 规则6:日志措辞修正(跳过->豁免/放行),并新增 含@+疑问词直接放行的纵深防御,避免用户提问被 UI 规则误杀。 --- monitor.py | 94 ++++++++++++++++++++++++++++++++++-------------------- 1 file changed, 60 insertions(+), 34 deletions(-) diff --git a/monitor.py b/monitor.py index 4f9dcdc..b4c565f 100644 --- a/monitor.py +++ b/monitor.py @@ -3233,8 +3233,12 @@ class MessageMonitor: # 相邻的真实新消息(过滤1与stage继承双重误判)。 # 后顾 ? {md_stripped!r}") - if md_stripped and self._is_markdown_document(md_stripped): - logger.info(f"[ocr] stage[{i}] [filter3/md] DROP md文档跳过: {md_stripped[:80]!r}") - elif md_stripped and self._is_noise_text(md_stripped): - logger.info(f"[ocr] stage[{i}] [filter2/噪声] DROP 噪声文本跳过: {md_stripped[:80]!r}") + # ⭐ 2026-08-12 调整顺序:必须先剥离 bot 旧回复片段,再剥 md 尾巴。 + # 原因:OCR 常把「用户新消息 + bot 旧回复」拼成一行,bot 回复若含 + # '::|::' 等 UI 残片,_strip_markdown_tail 会在第一个 '|' 处切断, + # 误删其后真正的用户提问(如 "@游戏专用 PHP管理员端的那登录密码是多少?"), + # 导致用户消息被后续 filter4 整体丢弃。先剥离 bot 回复保留用户提问, + # 再走 md/噪声/残片过滤,用户提问才能存活。 + _msg_bot = self._strip_bot_reply_fragments(buf.strip()) + if not _msg_bot: + logger.debug(f"[ocr] stage[{i}] [filter/bot] bot旧回复整体剥离后为空,跳过: {buf.strip()[:60]!r}") else: - msg = re.sub(r'\s*(?:[A-Za-z0-9]~)+\s*$', '', md_stripped or '') - if msg: - msg = re.sub(r'\s+#[\u4e00-\u9fff][^\s#\n]{0,30}$', '', msg).strip() + # ⭐ 剥 md 尾巴:md 在末尾→切尾巴留用户提问; + # md 大头/混中间→None 整段丢弃,防 md 尾巴入库撞关键词 + md_stripped = self._strip_markdown_tail(_msg_bot) + if md_stripped is None: + logger.info(f"[ocr] stage[{i}] [filter3/md] DROP md大头整段丢弃: {_msg_bot[:80]!r}") + elif md_stripped != _msg_bot: + logger.info(f"[ocr] stage[{i}] [filter3/md] 剥离md尾巴: {_msg_bot[:60]!r} -> {md_stripped!r}") + if md_stripped and self._is_markdown_document(md_stripped): + logger.info(f"[ocr] stage[{i}] [filter3/md] DROP md文档跳过: {md_stripped[:80]!r}") + elif md_stripped and self._is_noise_text(md_stripped): + logger.info(f"[ocr] stage[{i}] [filter2/噪声] DROP 噪声文本跳过: {md_stripped[:80]!r}") + else: + msg = re.sub(r'\s*(?:[A-Za-z0-9]~)+\s*$', '', md_stripped or '') if msg: - # ⭐ 先剥离 bot 旧回复片段:OCR 把「用户新消息 + bot 旧回复」 - # 拼成一行时,filter4 会误判整段为残片丢弃,导致用户新消息 - # 永久丢失(bot 回复一次后再也不回复)。剥离后保留用户提问。 - msg = self._strip_bot_reply_fragments(msg) + msg = re.sub(r'\s+#[\u4e00-\u9fff][^\s#\n]{0,30}$', '', msg).strip() if msg: # ⭐ 剥离QQ用户昵称,防止昵称拼凑进消息文本导致查库关键词被压住 msg = self._strip_qq_nickname(msg) @@ -3339,22 +3348,27 @@ class MessageMonitor: # else: 时间戳新鲜(<5min),直接保留 # —— 过滤器层(始终运行,绝不静默吞噬;每层放行/打回都记录)—— if buf.strip(): - md_stripped = self._strip_markdown_tail(buf.strip()) - if md_stripped is None: - logger.info(f"[ocr] stage[{i}] [filter3/md] DROP md大头整段丢弃(尾部): {buf.strip()[:80]!r}") - elif md_stripped != buf.strip(): - logger.info(f"[ocr] stage[{i}] [filter3/md] 剥离md尾巴(尾部): {buf.strip()[:60]!r} -> {md_stripped!r}") - if md_stripped and self._is_markdown_document(md_stripped): - logger.info(f"[ocr] stage[{i}] [filter3/md] DROP md文档跳过(尾部): {md_stripped[:80]!r}") - elif md_stripped and self._is_noise_text(md_stripped): - logger.info(f"[ocr] stage[{i}] [filter2/噪声] DROP 噪声文本跳过(尾部): {md_stripped[:80]!r}") + # ⭐ 2026-08-12 必须先剥离 bot 旧回复片段(同 for 循环块理由): + # OCR 把「用户新消息 + bot 旧回复」拼成一行时,先剥 bot 回复才能 + # 保住用户提问,否则 _strip_markdown_tail 会在 bot 回复的 '::|::' + # 残片处切断、误删用户提问,导致 filter4 整体丢弃。 + _msg_bot = self._strip_bot_reply_fragments(buf.strip()) + if not _msg_bot: + logger.debug(f"[ocr] stage[{i}] [filter/bot] 尾部bot旧回复整体剥离后为空,跳过: {buf.strip()[:60]!r}") else: - msg = re.sub(r'\s*(?:[A-Za-z0-9]~)+\s*$', '', md_stripped or '') - if msg: - msg = re.sub(r'\s+#[\u4e00-\u9fff][^\s#\n]{0,30}$', '', msg).strip() + md_stripped = self._strip_markdown_tail(_msg_bot) + if md_stripped is None: + logger.info(f"[ocr] stage[{i}] [filter3/md] DROP md大头整段丢弃(尾部): {_msg_bot[:80]!r}") + elif md_stripped != _msg_bot: + logger.info(f"[ocr] stage[{i}] [filter3/md] 剥离md尾巴(尾部): {_msg_bot[:60]!r} -> {md_stripped!r}") + if md_stripped and self._is_markdown_document(md_stripped): + logger.info(f"[ocr] stage[{i}] [filter3/md] DROP md文档跳过(尾部): {md_stripped[:80]!r}") + elif md_stripped and self._is_noise_text(md_stripped): + logger.info(f"[ocr] stage[{i}] [filter2/噪声] DROP 噪声文本跳过(尾部): {md_stripped[:80]!r}") + else: + msg = re.sub(r'\s*(?:[A-Za-z0-9]~)+\s*$', '', md_stripped or '') if msg: - # ⭐ 同上:尾部消息也要先剥离 bot 旧回复片段,避免整段误判为残片 - msg = self._strip_bot_reply_fragments(msg) + msg = re.sub(r'\s+#[\u4e00-\u9fff][^\s#\n]{0,30}$', '', msg).strip() if msg: # ⭐ 剥离QQ用户昵称,防止昵称拼凑进消息文本导致查库关键词被压住 msg = self._strip_qq_nickname(msg) @@ -5549,6 +5563,16 @@ class MessageMonitor: if not line: return False s = line.strip() + # ⭐ 2026-08-12 纵深防御:含 @ 且含疑问词的行一律视为用户提问, + # 绝不被任何 UI 规则(规则1-6)判为界面元素而丢弃。即使后续规则误匹配, + # 此处直接放行,确保用户提问永远不会因 UI 过滤而丢失。 + if '@' in s: + _qq_qwords = ('?', '?', '请问', '是不是', '有没有', '怎么', '如何', '什么', + '哪', '谁', '多少', '几', '为什么', '是否', '能否', '可以吗', + '好吗', '对吗', '是吗', '对不对', '好不好', '行不行', '可不可以') + if any(q in s for q in _qq_qwords): + logger.debug(f"[qq-ui] 含@+疑问词,直接放行(非UI元素): {s}") + return False # 1) 图片文件引用: !0(img/xxx.png) 或 !0(img/xxx.jpg) 等 # QQ 发送图片后,OCR 识别出的文件引用格式 if re.match(r'^!0?\s*\(?\s*img/', s, re.IGNORECASE): @@ -5596,7 +5620,9 @@ class MessageMonitor: '哪', '谁', '多少', '几', '为什么', '是否', '能否', '可以吗', '好吗', '对吗', '是吗', '对不对', '好不好', '行不行', '可不可以') if any(q in s for q in _question_words): - logger.debug(f"[qq-ui] 规则6跳过(含疑问词): {s}") + # ⭐ 2026-08-12 日志措辞修正:此处 return False 表示「不是UI元素、 + # 放行(当作用户消息处理)」,并非丢弃消息。"跳过"易误解为消息被跳过。 + logger.debug(f"[qq-ui] 规则6豁免(含疑问词,放行): {s}") return False # 匹配模式:可选中文前缀(群名片) + @ + 任意字符(可能乱码) + 空格 + 用户名(可多词) # 示例:"有人@拜 youpaishidifu"、"用户@张三 测试"、"@李四 123"