fix: md格式直接砍掉 — 删除用户文本提取,统一过滤逻辑

问题:OCR截屏截到bot旧回复(md格式文档内容),时间戳分割后
调用 _extract_user_text_from_md_fragment 尝试从混合文本中提取
'用户文本',导致md内容中的非md部分绕过过滤→触发查库→错误回复。

修复:
1. 时间戳分割逻辑(两处):md格式直接跳过,不再提取用户文本
2. 删除 _extract_user_text_from_md_fragment 方法(约100行)
3. 统一过滤逻辑:只要匹配 _is_markdown_document → 直接continue

原理:正常用户聊天不用 #标题、[链接](url)、-列表等md语法,
只要出现就是bot旧回复被OCR读回。不需要尝试提取'用户部分'。
This commit is contained in:
youpaishidifu
2026-07-31 00:32:22 +08:00
parent 567924a2e4
commit bd5ec8cbd8
+2 -125
View File
@@ -1576,16 +1576,7 @@ class MessageMonitor:
logger.debug(f"[ocr] stage[{i}] 时间戳{last_ts}过旧({300}s),跳过: {buf.strip()[:60]!r}")
else:
if self._is_markdown_document(buf.strip()):
extracted = self._extract_user_text_from_md_fragment(buf.strip())
for e in extracted:
if e:
# 再检:提取后的文本不应是纯 md 语法
if self._is_markdown_document(e):
logger.debug(f"[ocr] stage[{i}] md-only after extraction: {e[:60]!r}")
continue
expanded.append(e)
if not extracted:
logger.debug(f"[ocr] stage[{i}] markdown fragment skipped: {buf.strip()[:80]!r}")
logger.debug(f"[ocr] stage[{i}] md文档直接跳过: {buf.strip()[:80]!r}")
else:
msg = re.sub(r'\s*(?:[A-Za-z0-9]~)+\s*$', '', buf.strip())
if msg:
@@ -1605,18 +1596,7 @@ class MessageMonitor:
if last_ts and self._is_timestamp_old(last_ts, 300):
logger.debug(f"[ocr] stage[{i}] 尾部时间戳{last_ts}已过期(≥{300}s),跳过尾部: {buf.strip()[:60]!r}")
elif self._is_markdown_document(buf.strip()):
extracted = self._extract_user_text_from_md_fragment(buf.strip())
for e in extracted:
if e:
if self._is_markdown_document(e):
logger.debug(f"[ocr] stage[{i}] md-only after extraction: {e[:60]!r}")
continue
if self._is_reply_fragment(e):
logger.debug(f"[ocr] stage[{i}] md提取回复残片跳过: {e[:60]!r}")
continue
expanded.append(e)
if not extracted:
logger.debug(f"[ocr] stage[{i}] markdown fragment skipped: {buf.strip()[:80]!r}")
logger.debug(f"[ocr] stage[{i}] md文档直接跳过(尾部): {buf.strip()[:80]!r}")
else:
msg = re.sub(r'\s*(?:[A-Za-z0-9]~)+\s*$', '', buf.strip())
if msg:
@@ -2834,109 +2814,6 @@ class MessageMonitor:
return True
return False
@staticmethod
def _extract_user_text_from_md_fragment(text: str) -> list:
"""从混合了 md 文档和用户消息的 fragment 中提取用户文本。
OCR 经常把 bot md 回复和用户新消息合并为一行无换行文本,
如 "用户问题 ##bot标题|表格|数据|...另一个用户问题"。
本方法找到第一个 md 标记位置,提取其前面的用户文本;
再找到末尾连续非 md 尾巴,提取为第二条用户文本。
"""
# ── 整段以 ## 开头 → 无用户文本(无空格 ##中文/###数字 也捕获)──
if re.match(r'^#{1,6}(?:\s|\[|[\u4e00-\u9fff]|\d)', text):
return []
# 整段以 ** 开头 → 纯加粗语法,无用户文本
if text.startswith('**'):
return []
# 整段以 - / * / 数字编号 开头 → 列表项,无用户文本
if re.match(r'^[\-\*\d]\s', text):
return []
# 整段以 > 开头 → 引用块,无用户文本
if re.match(r'^>\s', text):
return []
# 整段以 [text]( 开头 → 全角括号 md 链接碎片,无用户文本
if re.match(r'^\[.+?\](', text):
return []
# ── 找第一个 markdown 起始标记的位置 ──
# 标记:## 标题、[text](url)、.md)、| 表格分隔、``` 代码块
md_start = len(text) # 默认整段都是 md
# ## 标题(放宽:允许 ##后直接跟汉字/[,不限于空格)
m = re.search(r'(?<=\S)\s*#{1,6}(?:\s|\[|[\u4e00-\u9fff])', text)
if m and m.start() < md_start:
md_start = m.start()
# [text](url) 链接(含全角括号退化 [text](url))
m = re.search(r'(?<=\S)\s*\[.+?\]\(.+?\)', text)
if m and m.start() < md_start:
md_start = m.start()
m = re.search(r'(?<=\S)\s*\[.+?\](.+?)', text)
if m and m.start() < md_start:
md_start = m.start()
# .md) 文件引用
m = re.search(r'(?<=\S)\s*\S+\.md\)', text)
if m and m.start() < md_start:
md_start = m.start()
# | 表格
m = re.search(r'(?<=\S)\s*\|', text)
if m and m.start() < md_start:
md_start = m.start()
# ``` 代码块
m = re.search(r'`{3}', text)
if m and m.start() < md_start:
md_start = m.start()
results = []
# 提取 md 标记前的用户文本
if md_start > 2:
# 如果 md 标记是 | 且前一个字符非空白 → head 是表格细胞值,不是用户提问
skip_head = (
md_start < len(text)
and text[md_start] == '|'
and md_start > 0
and not text[md_start-1].isspace()
)
if not skip_head:
head = text[:md_start].strip()
# 去掉尾部状态标记
head = re.sub(r'\s*(?:[A-Za-z0-9]~)+\s*$', '', head).strip()
# 过滤纯标点/符号
if head and not re.match(r'^[\d:\s|]+$', head):
results.append(head)
# 提取末尾可能的用户文本(md 块之后的尾巴)
# 找最后一个句号/感叹号/问号后的内容(可能是新消息的开头)
tail_pattern = re.search(r'[。!?\?!.]\s*(.{2,50})$', text)
if tail_pattern:
tail = tail_pattern.group(1).strip()
tail = re.sub(r'\s*(?:[A-Za-z0-9]~)+\s*$', '', tail).strip()
if tail and not re.match(r'^[\d:\s|]+$', tail):
# 不是纯标点、不是表格碎片
if not re.match(r'^[|/\-─━━═]+$', tail):
results.append(tail)
# 末尾没有句号但有中文问号结尾的(如 "PHP用户端有哪些登录模式?")
if not tail_pattern:
tail2 = re.search(r'([\u4e00-\u9fff].{2,50}\?)$', text)
if tail2:
tail = tail2.group(1).strip()
tail = re.sub(r'\s*(?:[A-Za-z0-9]~)+\s*$', '', tail).strip()
if tail and len(tail) >= 3:
# 避免重复
if tail not in results:
results.append(tail)
# ── 终极过滤:提取结果含 ** 加粗语法或 # 标题 → 不是用户消息 ──
# 正常聊天用户从不打 md 语法标记
results = [r for r in results if '**' not in r and not r.startswith('#')]
return results
def _paste_and_send_safe(self, text: str, hwnd: int, msg_hash: int, platform: str) -> bool:
"""Electron 应用安全发送(TIM/微信)。无 AttachThreadInput、无 SendMessage 同步点击。