diff --git a/monitor.py b/monitor.py index dde9375..bb50a00 100644 --- a/monitor.py +++ b/monitor.py @@ -8341,6 +8341,7 @@ class MessageMonitor: - 段内出现 ≥2 个 bot 配置键词 + 冒号(值可空,OCR 常把值拆到下一行) - 或短段(≤30字)以单个 bot 配置键词 + 空冒号结尾(被截断的标签) 排除含疑问词或 @提及 的真实用户问句。 + ⭐ Task #1685: 排除表格内容,避免误判表格中的"键:值"格式。 """ if not text: return False @@ -8354,6 +8355,17 @@ class MessageMonitor: if _has_question or _has_at: return False + # ── Task #1685: 排除表格内容 ── + # 表格内容中的"|"分隔符是特征,不应被误判为配置碎片 + _has_table_structure = '|' in text and text.count('|') >= 2 + if _has_table_structure: + _lines = text.split('\n') + _table_lines = [line for line in _lines if '|' in line] + if len(_table_lines) >= 2: + # 包含多行表格结构,不是配置碎片 + logger.debug(f"[ocr] 预分割: 检测到表格结构({len(_table_lines)}行),不是配置碎片: {_s[:60]!r}") + return False + # ── bot 配置键词表(bot 渲染插件配置/帮助面板使用的标签)── _config_keys = ( '图标', '标题', '指令', '命令', '权限类型', '权限', '格子', '玩家', '商人', @@ -8554,51 +8566,73 @@ class MessageMonitor: logger.debug(f"[ocr] 过滤器2结果: 空文本,返回False") return False + # ⭐ Task #1685: 表格内容特征检查(在配置文件检测之前) + # OCR可能把多个消息拼接成一个整体,包含表格内容 + # 表格内容中的"键:值"格式不应被误判为配置文件 + # 特征:包含"|"分隔符(Markdown表格格式) + _has_table_structure = '|' in text and text.count('|') >= 2 + if _has_table_structure: + # 检查是否为表格结构:包含多行"|"分隔的内容 + _lines = text.split('\n') + _table_lines = [line for line in _lines if '|' in line] + if len(_table_lines) >= 2: + # 包含多行表格结构,跳过配置文件检测 + logger.debug(f"[ocr] 过滤器2结果: 检测到表格结构({len(_table_lines)}行),跳过配置文件检测: {text[:80]!r}") + # 继续后续其他噪声检测,但不进行配置文件格式检测 + # ⭐ Task #1592: 配置文件格式检测(最粗暴方法) # 检测所有配置文件格式:key:value 对、JSON 对象等 # 特征:无问号/疑问词,有明显的配置结构 # 直接丢弃,不进入后续KB搜索 # 1. 检测JSON格式:包含大括号且是键值对 - _json_match = re.search(r'\{[^}]+\}', text) - if _json_match: - # 检查是否包含键值对(key: value 或 "key": "value") - _json_content = _json_match.group(0) - _kv_pattern = re.search(r'["\']?\w+["\']?\s*[::]\s*["\']?[^"\']+["\']?', _json_content) - if _kv_pattern: - # ⭐ Task #1595: 检查用户消息特征 - _has_user_msg_feature = '@' in text and bool(re.search(r'@\w+', text)) - _has_user_msg_feature |= any(q in text for q in ['?', '?', '吗', '呢', '多少', '怎么', '如何', '哪', '什么', '几']) - if _has_user_msg_feature: - logger.debug(f"[ocr] 过滤器2结果: 检测到JSON但含用户消息特征,继续检查: {text[:80]!r}") - else: - logger.debug(f"[ocr] 过滤器2结果: 检测到JSON配置文件格式,返回True: {text!r}") - return True + # ⭐ Task #1685: 跳过表格结构内容 + if not _has_table_structure: + _json_match = re.search(r'\{[^}]+\}', text) + if _json_match: + # 检查是否包含键值对(key: value 或 "key": "value") + _json_content = _json_match.group(0) + _kv_pattern = re.search(r'["\']?\w+["\']?\s*[::]\s*["\']?[^"\']+["\']?', _json_content) + if _kv_pattern: + # ⭐ Task #1595: 检查用户消息特征 + _has_user_msg_feature = '@' in text and bool(re.search(r'@\w+', text)) + _has_user_msg_feature |= any(q in text for q in ['?', '?', '吗', '呢', '多少', '怎么', '如何', '哪', '什么', '几']) + if _has_user_msg_feature: + logger.debug(f"[ocr] 过滤器2结果: 检测到JSON但含用户消息特征,继续检查: {text[:80]!r}") + else: + logger.debug(f"[ocr] 过滤器2结果: 检测到JSON配置文件格式,返回True: {text!r}") + return True + else: + logger.debug(f"[ocr] 过滤器2结果: 检测到表格结构,跳过JSON配置文件检测: {text[:80]!r}") # 2. 检测key:value对格式:至少两个冒号分隔的键值对 - _colon_pattern = r'[\u4e00-\u9fff]{2,10}\s*[::]\s*\S+' - _colon_matches = re.findall(_colon_pattern, text) - if len(_colon_matches) >= 2: - # ⭐ Task #1595 修复:配置面板+用户消息混排场景 - # OCR可能把"配置面板 + 用户消息 + UI时间戳"拼成一行 - # 例:"图标:potato标题:自杀...近期公告游戏专用<>有人@利17:36:25" - # 如果直接丢弃整段,会误杀其中的用户消息 - # 检查是否有用户消息特征(@提及、疑问词、明确提问) - _has_user_msg_feature = False - # 特征1: @提及(用户@他人或@机器人) - if '@' in text and re.search(r'@\w+', text): - _has_user_msg_feature = True - # 特征2: 疑问词(吗呢多少怎么如何哪什么几) - if any(q in text for q in ['?', '?', '吗', '呢', '多少', '怎么', '如何', '哪', '什么', '几']): - _has_user_msg_feature = True - # 特征3: 明确的查询意图词(查/问/看/有/在哪/如何/怎么) - if re.search(r'[查问看].*(?:插件|功能|命令|模式|系统|公告|信息)', text): - _has_user_msg_feature = True - if _has_user_msg_feature: - # 包含用户消息特征,不丢弃,继续后续检查 - logger.debug(f"[ocr] 过滤器2结果: 检测到多条key:value但含用户消息特征(@提及/疑问词),继续检查: {text[:80]!r}") - else: - # 纯配置文本,丢弃 - logger.debug(f"[ocr] 过滤器2结果: 检测到多条配置文件格式({len(_colon_matches)}条key:value),返回True: {text!r}") - return True + # ⭐ Task #1685: 跳过表格结构内容,避免误判表格中的"键:值"格式 + if not _has_table_structure: + _colon_pattern = r'[\u4e00-\u9fff]{2,10}\s*[::]\s*\S+' + _colon_matches = re.findall(_colon_pattern, text) + if len(_colon_matches) >= 2: + # ⭐ Task #1595 修复:配置面板+用户消息混排场景 + # OCR可能把"配置面板 + 用户消息 + UI时间戳"拼成一行 + # 例:"图标:potato标题:自杀...近期公告游戏专用<>有人@利17:36:25" + # 如果直接丢弃整段,会误杀其中的用户消息 + # 检查是否有用户消息特征(@提及、疑问词、明确提问) + _has_user_msg_feature = False + # 特征1: @提及(用户@他人或@机器人) + if '@' in text and re.search(r'@\w+', text): + _has_user_msg_feature = True + # 特征2: 疑问词(吗呢多少怎么如何哪什么几) + if any(q in text for q in ['?', '?', '吗', '呢', '多少', '怎么', '如何', '哪', '什么', '几']): + _has_user_msg_feature = True + # 特征3: 明确的查询意图词(查/问/看/有/在哪/如何/怎么) + if re.search(r'[查问看].*(?:插件|功能|命令|模式|系统|公告|信息)', text): + _has_user_msg_feature = True + if _has_user_msg_feature: + # 包含用户消息特征,不丢弃,继续后续检查 + logger.debug(f"[ocr] 过滤器2结果: 检测到多条key:value但含用户消息特征(@提及/疑问词),继续检查: {text[:80]!r}") + else: + # 纯配置文本,丢弃 + logger.debug(f"[ocr] 过滤器2结果: 检测到多条配置文件格式({len(_colon_matches)}条key:value),返回True: {text!r}") + return True + else: + logger.debug(f"[ocr] 过滤器2结果: 检测到表格结构,跳过key:value配置文件检测: {text[:80]!r}") elif len(_colon_matches) == 1: # 单条key:value,检查是否为完整的一条(无多余字符) _colon_match = re.match(r'^[\u4e00-\u9fff]{2,10}\s*[::]\s*\S+$', text) @@ -8625,29 +8659,37 @@ class MessageMonitor: logger.debug(f"[ocr] 过滤器2结果: 检测到单条配置文件格式(key: value),返回True: {text!r}") return True # 3. 检测其他配置文件格式:等号分隔的键值对(如key=value) - _eq_pattern = r'[\u4e00-\u9fff]{2,10}\s*=\s*\S+' - _eq_matches = re.findall(_eq_pattern, text) - if len(_eq_matches) >= 2: - # ⭐ Task #1595: 检查用户消息特征 - _has_user_msg_feature = '@' in text and bool(re.search(r'@\w+', text)) - _has_user_msg_feature |= any(q in text for q in ['?', '?', '吗', '呢', '多少', '怎么', '如何', '哪', '什么', '几']) - if _has_user_msg_feature: - logger.debug(f"[ocr] 过滤器2结果: 检测到等号配置但含用户消息特征,继续检查: {text[:80]!r}") - else: - logger.debug(f"[ocr] 过滤器2结果: 检测到等号分隔的配置文件格式({len(_eq_matches)}条key=value),返回True: {text!r}") - return True + # ⭐ Task #1685: 跳过表格结构内容 + if not _has_table_structure: + _eq_pattern = r'[\u4e00-\u9fff]{2,10}\s*=\s*\S+' + _eq_matches = re.findall(_eq_pattern, text) + if len(_eq_matches) >= 2: + # ⭐ Task #1595: 检查用户消息特征 + _has_user_msg_feature = '@' in text and bool(re.search(r'@\w+', text)) + _has_user_msg_feature |= any(q in text for q in ['?', '?', '吗', '呢', '多少', '怎么', '如何', '哪', '什么', '几']) + if _has_user_msg_feature: + logger.debug(f"[ocr] 过滤器2结果: 检测到等号配置但含用户消息特征,继续检查: {text[:80]!r}") + else: + logger.debug(f"[ocr] 过滤器2结果: 检测到等号分隔的配置文件格式({len(_eq_matches)}条key=value),返回True: {text!r}") + return True + else: + logger.debug(f"[ocr] 过滤器2结果: 检测到表格结构,跳过等号配置文件检测: {text[:80]!r}") # 4. 检测YAML/INI格式:行首为key,后跟冒号或等号 - _yaml_ini_pattern = r'^[\u4e00-\u9fff]{2,10}\s*[:=]\s*\S+' - _yaml_ini_matches = re.findall(_yaml_ini_pattern, text, re.MULTILINE) - if len(_yaml_ini_matches) >= 2: - # ⭐ Task #1595: 检查用户消息特征 - _has_user_msg_feature = '@' in text and bool(re.search(r'@\w+', text)) - _has_user_msg_feature |= any(q in text for q in ['?', '?', '吗', '呢', '多少', '怎么', '如何', '哪', '什么', '几']) - if _has_user_msg_feature: - logger.debug(f"[ocr] 过滤器2结果: 检测到YAML/INI但含用户消息特征,继续检查: {text[:80]!r}") - else: - logger.debug(f"[ocr] 过滤器2结果: 检测到YAML/INI配置文件格式({len(_yaml_ini_matches)}行),返回True: {text!r}") - return True + # ⭐ Task #1685: 跳过表格结构内容 + if not _has_table_structure: + _yaml_ini_pattern = r'^[\u4e00-\u9fff]{2,10}\s*[:=]\s*\S+' + _yaml_ini_matches = re.findall(_yaml_ini_pattern, text, re.MULTILINE) + if len(_yaml_ini_matches) >= 2: + # ⭐ Task #1595: 检查用户消息特征 + _has_user_msg_feature = '@' in text and bool(re.search(r'@\w+', text)) + _has_user_msg_feature |= any(q in text for q in ['?', '?', '吗', '呢', '多少', '怎么', '如何', '哪', '什么', '几']) + if _has_user_msg_feature: + logger.debug(f"[ocr] 过滤器2结果: 检测到YAML/INI但含用户消息特征,继续检查: {text[:80]!r}") + else: + logger.debug(f"[ocr] 过滤器2结果: 检测到YAML/INI配置文件格式({len(_yaml_ini_matches)}行),返回True: {text!r}") + return True + else: + logger.debug(f"[ocr] 过滤器2结果: 检测到表格结构,跳过YAML/INI配置文件检测: {text[:80]!r}") # ⭐ 闲聊消息检测(2026-08-22 新增) # 特征:短文本 + 疑问词 + 无具体查询主题