refactor: md checkbox 一刀切,不再枚举符号
用户从不手打 markdown,短方括号结构才是稳定特征: - [√] [x] [ ] [⚠] [✔] [V] 及 OCR 变体 [ √ ]、全角【√】全部覆盖 - 不再枚举具体勾选符号——OCR 偏差永远撞不全符号 - 多字词方括号([钻石] [VIP])绝不命中,保护用户正文 - \s*.?\s* = 空或单字符,md checkbox 的本质特征 改动点: 1. monitor.py _MD_TAIL_PATTERNS: checkbox 模式改一刀切 + 补全角【】 2. monitor.py _is_markdown_document: checkbox 检测同步放宽 3. monitor.py OCR 过滤链: checkbox残片/多checkbox/计数/行首菜单 4处同步 4. auto_reply.py _clean_markdown: checkbox 移除改一刀切,(?!\() 保护链接 策略(用户定):md在头→整段跳过;在中间→看语义(C#负向前瞻已挡);在尾→一刀切
This commit is contained in:
+6
-5
@@ -622,11 +622,12 @@ class AutoReply:
|
||||
# 移除列表语法:- item → item
|
||||
text = re.sub(r'^[\-\*]\s+', '', text, flags=re.MULTILINE)
|
||||
|
||||
# ⭐ Task #1549: 移除 checkbox 标记:[√] GitHub(稳定版) → GitHub(稳定版)
|
||||
# KB 中的下载选项列表使用 checkbox 格式,如 "- [ √ ] GitHub(稳定版)"
|
||||
# 清理后应返回 "GitHub(稳定版)" 而非 "[ √ ] GitHub(稳定版)"
|
||||
# 支持: [√] [✗] [X] [x] [V] [v] [ ] [⚠] [!]
|
||||
text = re.sub(r'\[\s*[√✗XxVv⚠! ]?\s*\]\s*', '', text)
|
||||
# ⭐ Task #1549+ 一刀切:md checkbox 内容=单符号或空白([√] [x] [ ] [⚠]),
|
||||
# 直接剥掉。不再枚举具体符号——OCR 偏差永远撞不全符号,
|
||||
# 单字符方括号结构才是稳定特征([ √ ] 带空格变体也覆盖)。
|
||||
# \s*.?\s* = 空 或 单字符;多字词 [钻石] [VIP] 不剥,保护正文内容。
|
||||
# (?!\() 保护 md 链接 [text](url):keep_links 模式下不误伤链接体。
|
||||
text = re.sub(r'\[\s*.?\s*\](?!\()\s*', '', text)
|
||||
|
||||
# 移除加粗语法:**text** → text
|
||||
text = re.sub(r'\*\*(.+?)\*\*', r'\1', text)
|
||||
|
||||
+28
-17
@@ -3589,12 +3589,13 @@ class MessageMonitor:
|
||||
if special_chars / len(line) > 0.15 and alpha_count >= 2 and digit_count >= 2:
|
||||
logger.debug(f"[{platform}] OCR 过滤: 随机字符垃圾 line={line!r}")
|
||||
continue
|
||||
# checkbox 残片:`[V]` `[X]` `[√]` 开头,明显不是用户提问
|
||||
if re.match(r'^\[[VX√✗]\s*\](?:\s*[A-Za-z0-9_\-./]+)?\s*$', line):
|
||||
# ⭐ 一刀切 checkbox 残片:单字符方括号 = md checkbox([V]/[X]/[√]/[ ]),
|
||||
# 不是用户提问(用户方括号是多字词 [钻石] [VIP],此处不命中)
|
||||
if re.match(r'^\[\s*.?\s*\](?!\()(?:\s*[A-Za-z0-9_\-./]+)?\s*$', line):
|
||||
logger.debug(f"[{platform}] OCR 过滤: checkbox残片 line={line!r}")
|
||||
continue
|
||||
# 多 checkbox 碎片:一行含 2+ 个 [V] 标记,md 列表 OCR 残留
|
||||
if len(re.findall(r'\[\s*[VXvx√✗]\s*\]', line)) >= 2:
|
||||
# 多 checkbox 碎片:一行含 2+ 个单字符方括号,md 列表 OCR 残留
|
||||
if len(re.findall(r'\[\s*.?\s*\](?!\()', line)) >= 2:
|
||||
logger.debug(f"[{platform}] OCR 过滤: 多checkbox碎片 line={line!r}")
|
||||
continue
|
||||
# 全角括号 md 链接碎片:`[text](url` 或 `](` — md 链接 OCR 全角退化
|
||||
@@ -3610,8 +3611,8 @@ class MessageMonitor:
|
||||
# 提取不重复的中文词(避免"稳定版稳定版稳定版"重复计数)
|
||||
_content_words = list(set(re.findall(r'[\u4e00-\u9fff]{2,}', line)))
|
||||
_question_marks = '?' in line or '?' in line
|
||||
# 统计checkbox模式数量(如[ ]、[√])
|
||||
_checkbox_count = len(re.findall(r'\[\s*[√✗XvV ]?\s*\]', line))
|
||||
# 统计checkbox模式数量(单字符方括号,如[ ]、[√],一刀切不枚举符号)
|
||||
_checkbox_count = len(re.findall(r'\[\s*.?\s*\](?!\()', line))
|
||||
# 统计平台名出现次数
|
||||
_platform_count = len(re.findall(r'\b(?:github|gitee|gitcode)\b', line, re.I))
|
||||
# 判定规则:
|
||||
@@ -4001,8 +4002,11 @@ class MessageMonitor:
|
||||
if _BOT_MENU_LABELS.search(line):
|
||||
logger.debug(f"[{platform}] OCR 过滤: bot回复菜单(版本标签) line={line!r}")
|
||||
continue
|
||||
# checkbox 标记:[ √ ] [ ⚠ ] [ ✗ ] [ X ] [ √] 等(OCR可能漏空格/括号)
|
||||
if re.search(r'[\[【]\s*[√⚠✗×XvV✓✔]\s*[\]】]', line, re.IGNORECASE):
|
||||
# ⭐ 一刀切 checkbox 标记(行首):md checkbox 在头部 → 整段跳过。
|
||||
# 单字符方括号(含全角【】变体)= md checkbox,[ √ ] [ ⚠ ] [ ✗ ] [ X ] 等
|
||||
# [钻石] [VIP] 等多字词绝不命中,保护用户正文;
|
||||
# 中间位置 → 交给 _MD_TAIL_PATTERNS 切尾巴,不整行丢(用户策略:md在头整段跳,在尾一刀切)
|
||||
if re.match(r'^[\[【]\s*.?\s*[\]】](?!\()', line, re.IGNORECASE):
|
||||
logger.debug(f"[{platform}] OCR 过滤: bot回复菜单(checkbox标记) line={line!r}")
|
||||
continue
|
||||
# 纯 checkbox 行:OCR 把 "[ √ ]" 读成 "√ " 或 "√"(无括号)
|
||||
@@ -6275,11 +6279,11 @@ class MessageMonitor:
|
||||
|
||||
return '\n'.join(cleaned)
|
||||
|
||||
# —— md 尾巴剥离模式 ——
|
||||
# ⭐ 2026-08-11:QQ 群截图 OCR 会把 bot 的 md 回复残片拼在用户消息尾部,
|
||||
# 如 "群管理员其实都是傲娇小南...(头衔UI)...# #来测试# # @C418minecraf"。
|
||||
# 历史做法整条判 md 丢弃 → 用户原始提问陪葬;若放行 → md 尾巴入库,
|
||||
# 可能撞知识库关键词把 bot 拽去回复残片内容,脱离用户原始提问。
|
||||
# —— md 尾巴剥离模式(一刀切)——
|
||||
# ⭐ 2026-08-17 原则:用户聊天从不手打 markdown(- [ ] 列表、# 标题、**加粗**、
|
||||
# [x](url) 链接、| 表格、` 代码),任何 md 特有结构都可安全一刀切。
|
||||
# OCR 偏差(如 [ √ ] 带空格、[✔]、【√】、[x、[ ])会让"逐个符号匹配"漏网,
|
||||
# 所以 checkbox 用"任意短方括号"结构匹配,而非枚举具体勾选符号。
|
||||
# 规则:md 在末尾 → 切掉小尾巴保留前段;md 大头/混中间 → 整段切掉。
|
||||
_MD_TAIL_PATTERNS = [
|
||||
# # 标题(## 文档 / ##直接跟汉字 / # # 分割 / ###1.)
|
||||
@@ -6290,8 +6294,14 @@ class MessageMonitor:
|
||||
# [text](url) 及 OCR 退化 [text](、](
|
||||
re.compile(r'\[.+?\]\([^)]*\)?'),
|
||||
re.compile(r'\]('),
|
||||
# OCR 退化 checkbox [V]/[x]/[√]
|
||||
re.compile(r'\[\s*[VXvx√✗]\s*\]'),
|
||||
# ⭐ 一刀切:md checkbox 内容=单符号或空白([√] [x] [ ] [⚠]),
|
||||
# 用户游戏名词方括号是多字词([钻石] [VIP])绝不命中。
|
||||
# \s*.?\s* = 空 或 单字符(√✗xXVv⚠!✔ 等任意符号,OCR 变体如 [ √ ] 也覆盖)
|
||||
# 不再枚举具体符号——OCR 偏差永远撞不全符号,单字符结构才是稳定特征。
|
||||
# 负向前瞻 (?!\() 保护 md 链接 [text](url) 不被算作 checkbox。
|
||||
re.compile(r'\[\s*.?\s*\](?!\()\s*(?=\(|[\u4e00-\u9fffA-Za-z]|$)'),
|
||||
# 全角 checkbox:OCR 把 [√] 读成 【√】(内容仍为单符号或空白)
|
||||
re.compile(r'【\s*.?\s*】\s*(?=\(|[\u4e00-\u9fffA-Za-z]|$)'),
|
||||
# - 列表 / 编号列表 / > 引用(行内出现,前有空白或行首)
|
||||
re.compile(r'(?:^|\s)[-*+]\s+(?=\S)'),
|
||||
re.compile(r'(?:^|\s)\d{1,3}[\.\)、]\s+(?=\S)'),
|
||||
@@ -6362,8 +6372,9 @@ class MessageMonitor:
|
||||
if re.search(r'\[.+?\](', text):
|
||||
logger.debug(f"[ocr] 过滤器3结果: 检测到全角括号退化链接模式2,返回True")
|
||||
return True
|
||||
# OCR 退化 md checkbox:短文本也检测([V] / [x])
|
||||
if re.search(r'\[\s*[VXvx√✗]\s*\]', text):
|
||||
# OCR 退化 md checkbox:短文本也检测([V] / [x] / [ √ ] / [ ])
|
||||
# ⭐ 一刀切:单字符方括号即 md checkbox 特征(用户聊天方括号是多字词 [钻石])
|
||||
if re.search(r'\[\s*.?\s*\](?!\()', text):
|
||||
logger.debug(f"[ocr] 过滤器3结果: 检测到OCR退化checkbox,返回True")
|
||||
return True
|
||||
# OCR 退化 md 链接缺括号:短文本也检测
|
||||
|
||||
Reference in New Issue
Block a user