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:
+2
-125
@@ -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 同步点击。
|
||||
|
||||
Reference in New Issue
Block a user