修复OCR md碎片漏网:短列表项/- 玩家义务/漏过、尾部#草残留

- _is_markdown_document: 行首列表项(- /*)、checkbox([V])、
  md链接缺括号([text]( )检查移到len<10之前,短碎片不漏
- 时间戳分割else分支: 新增尾部#标题清理(\s+#[\u4e00-\u9fff]...$)
- 提取后重检: extraction出的文本再走_is_markdown_document,
  纯md碎片丢弃
- 过滤循环兜底: line进入texts.append前再扫一次尾部#标题+
This commit is contained in:
youpaishidifu
2026-07-28 16:48:56 +08:00
parent 42db2ad5b8
commit ba139113b1
+40 -11
View File
@@ -1149,13 +1149,20 @@ class MessageMonitor:
extracted = self._extract_user_text_from_md_fragment(buf.strip()) extracted = self._extract_user_text_from_md_fragment(buf.strip())
for e in extracted: for e in extracted:
if e: 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) expanded.append(e)
if not extracted: if not extracted:
logger.debug(f"[ocr] stage[{i}] markdown fragment skipped: {buf.strip()[:80]!r}") logger.debug(f"[ocr] stage[{i}] markdown fragment skipped: {buf.strip()[:80]!r}")
else: else:
msg = re.sub(r'\s*(?:[A-Za-z0-9]~)+\s*$', '', buf.strip()) msg = re.sub(r'\s*(?:[A-Za-z0-9]~)+\s*$', '', buf.strip())
if msg: if msg:
expanded.append(msg) # 清理末尾 #标题 碎片(如 "玩家义务? #草")
msg = re.sub(r'\s+#[\u4e00-\u9fff][^\s#\n]{0,30}$', '', msg).strip()
if msg:
expanded.append(msg)
buf = '' buf = ''
else: else:
buf += part buf += part
@@ -1164,13 +1171,18 @@ class MessageMonitor:
extracted = self._extract_user_text_from_md_fragment(buf.strip()) extracted = self._extract_user_text_from_md_fragment(buf.strip())
for e in extracted: for e in extracted:
if e: if e:
if self._is_markdown_document(e):
logger.debug(f"[ocr] stage[{i}] md-only after extraction: {e[:60]!r}")
continue
expanded.append(e) expanded.append(e)
if not extracted: if not extracted:
logger.debug(f"[ocr] stage[{i}] markdown fragment skipped: {buf.strip()[:80]!r}") logger.debug(f"[ocr] stage[{i}] markdown fragment skipped: {buf.strip()[:80]!r}")
else: else:
msg = re.sub(r'\s*(?:[A-Za-z0-9]~)+\s*$', '', buf.strip()) msg = re.sub(r'\s*(?:[A-Za-z0-9]~)+\s*$', '', buf.strip())
if msg: if msg:
expanded.append(msg) msg = re.sub(r'\s+#[\u4e00-\u9fff][^\s#\n]{0,30}$', '', msg).strip()
if msg:
expanded.append(msg)
# —— 逐行过滤 —— # —— 逐行过滤 ——
ocr_lines = expanded ocr_lines = expanded
@@ -1287,6 +1299,22 @@ class MessageMonitor:
logger.debug(f"[{platform}] OCR 过滤: URL-only碎片 line={line!r}") logger.debug(f"[{platform}] OCR 过滤: URL-only碎片 line={line!r}")
continue continue
# —— 末尾 #标题 碎片兜底 ——
# OCR 时间戳分割可能遗漏尾部 " #测试" " #草" 等 md 标题碎片
# 注:\s+ 要求空格在前,避免误杀 "C#语言" "中文#标签"
if re.search(r'#[\u4e00-\u9fff]', line):
cleaned = re.sub(r'\s+#[\u4e00-\u9fff][^\s#\n]{0,30}$', '', line).strip()
if cleaned != line:
if not cleaned or MessageMonitor._is_markdown_document(cleaned):
logger.debug(f"[{platform}] OCR 过滤: 末尾#标题碎片 line={line!r}")
continue
line = cleaned
# 如果 line 本身以 # 开头(纯 md),前面的 _is_markdown_document 应该已捕获
# 但此处再补一刀
if MessageMonitor._is_markdown_document(line):
logger.debug(f"[{platform}] OCR 过滤: md纯标题碎片 line={line!r}")
continue
texts.append(line) texts.append(line)
logger.debug(f"[{platform}] OCR -> {len(ocr_lines)} raw, {len(texts)} filtered, " logger.debug(f"[{platform}] OCR -> {len(ocr_lines)} raw, {len(texts)} filtered, "
f"last=\"{texts[-1][:50] if texts else ''}\"") f"last=\"{texts[-1][:50] if texts else ''}\"")
@@ -1641,11 +1669,21 @@ class MessageMonitor:
# # 行首标题:短文本也检测(OCR 把 ##协议修 切成 5 字符片段) # # 行首标题:短文本也检测(OCR 把 ##协议修 切成 5 字符片段)
if re.match(r'^#{1,6}(?:\s|\[|[\u4e00-\u9fff])', text): if re.match(r'^#{1,6}(?:\s|\[|[\u4e00-\u9fff])', text):
return True return True
# 行首列表项:短文本也检测(- 玩家义务 / * 注意)
# 必须放 len<10 之前,否则 "- 玩家应尊重他"(8c) 漏网
if re.match(r'^[\-\*]\s', text):
return True
# 全角括号退化 md 链接:短文本也检测 # 全角括号退化 md 链接:短文本也检测
if re.search(r'\](', text): if re.search(r'\](', text):
return True return True
if re.search(r'\[.+?\](', text): if re.search(r'\[.+?\](', text):
return True return True
# OCR 退化 md checkbox:短文本也检测([V] / [x])
if re.search(r'\[\s*[VXvx√✗]\s*\]', text):
return True
# OCR 退化 md 链接缺括号:短文本也检测
if re.search(r'\[.+?\]\(', text):
return True
if len(text) < 10: if len(text) < 10:
return False return False
# ##直接跟汉字(OCR 丢失 ##后空格时常见:##文档导航) # ##直接跟汉字(OCR 丢失 ##后空格时常见:##文档导航)
@@ -1657,9 +1695,6 @@ class MessageMonitor:
# 包含 .md 文件引用(如 "gin.md)" 分割后断开的链接) # 包含 .md 文件引用(如 "gin.md)" 分割后断开的链接)
if re.search(r'\S+\.md\)', text): if re.search(r'\S+\.md\)', text):
return True return True
# 行首是 - 或 * 列表项(标准 md 列表)
if re.match(r'^[\-\*]\s', text):
return True
# 包含代码块标记 ``` 或 `code` # 包含代码块标记 ``` 或 `code`
if re.search(r'`{1,3}', text): if re.search(r'`{1,3}', text):
return True return True
@@ -1670,12 +1705,6 @@ class MessageMonitor:
# 内联 # # 标题标记(如 "# # 必装依赖" → 分割后 `# #` 或 `# #`) # 内联 # # 标题标记(如 "# # 必装依赖" → 分割后 `# #` 或 `# #`)
if re.search(r'#\s*#', text): if re.search(r'#\s*#', text):
return True return True
# OCR 退化 md checkbox:`[V]`、`[v]`、`[x]` 等(含空格变体 [V ])
if re.search(r'\[\s*[VXvx√✗]\s*\]', text):
return True
# OCR 退化 md 链接:`[text](...` 缺闭合括号,QQ 群聊几乎不用此语法
if re.search(r'\[.+?\]\(', text):
return True
return False return False
@staticmethod @staticmethod