修复微信自身消息过滤+病句加强+QQ共同好友黑名单
1. 微信颜色检测重写(_has_user_colors): - 绿色=自身消息(右对齐),白色=对方消息(左对齐) - 自身消息是最顶层门禁:右绿+左无白→跳过,左有白→继续OCR - 修复:之前绿色被当作bot色,导致自身消息未过滤触发查库 2. 病句过滤加强: - 短句阈值从≤5字提升到≤8字,必须含疑问词才放行 - 新增疑问词:是不是/会不会/能不能/怎么办/好不好/行不行 - 新增病句4:OCR拼接系统提示碎片(含#本/请自行/源码修改) - 新增病句5:修字碎片(自行修?帮我修) 3. QQ群UI黑名单: - _UI_SUBSTR_BLOCKLIST增加'共同好友'和'点击添加好友' - 防止QQ群'你和xxx有x个共同好友'触发查库
This commit is contained in:
+69
-31
@@ -124,6 +124,8 @@ _UI_EXACT_BLOCKLIST = frozenset({
|
||||
_UI_SUBSTR_BLOCKLIST = frozenset({
|
||||
'启用监控', '停止监控', '数据看板', '应用设置',
|
||||
'债券总和', '注册用户总数', 'php管理员', '消息漫游',
|
||||
# QQ 群"共同好友"推荐(非聊天内容,OCR误读触发查库)
|
||||
'共同好友', '点击添加好友',
|
||||
})
|
||||
|
||||
|
||||
@@ -1256,7 +1258,7 @@ class MessageMonitor:
|
||||
# ⭐ 颜色像素检测:如果截图中只有 bot 蓝色气泡,无白色用户消息,跳过
|
||||
if not self._has_user_colors(im, "qq"):
|
||||
self._ocr_unchanged["qq"] = True
|
||||
logger.debug(f"[qq] 像素检测: 无用户气泡, 跳过OCR (截图: {os.path.basename(_dbg_path)})")
|
||||
logger.debug(f"[qq] 像素检测: 无对方消息, 跳过OCR (截图: {os.path.basename(_dbg_path)})")
|
||||
return []
|
||||
# ⭐ 截图 hash:像素级指纹——内容没变就直接跳过整轮(easyocr 同一截图每次输出不同文本)
|
||||
# dHash + 5秒强制刷新:即使hash相同,每5秒也强制做一次OCR,防止永久卡死
|
||||
@@ -1356,10 +1358,10 @@ class MessageMonitor:
|
||||
except Exception as e:
|
||||
logger.warning(f"[wechat] 调试截图保存失败: {e} (路径: {os.path.join(script_dir, 'ocr_debug_wx_*.png')})")
|
||||
|
||||
# ⭐ 颜色像素检测:如果截图中只有 bot 绿色气泡,无白色用户消息,跳过
|
||||
# ⭐ 颜色像素检测:如果截图中只有自身绿色气泡,无对方白色消息,跳过
|
||||
if not self._has_user_colors(im, "wechat"):
|
||||
self._ocr_unchanged["wechat"] = True
|
||||
logger.debug(f"[wechat] 像素检测: 无用户气泡, 跳过OCR")
|
||||
logger.debug(f"[wechat] 像素检测: 无对方消息(自身消息已过滤), 跳过OCR")
|
||||
return []
|
||||
|
||||
# ⭐ 截图 hash:像素级指纹——内容没变就直接跳过整轮
|
||||
@@ -1817,13 +1819,15 @@ class MessageMonitor:
|
||||
|
||||
# —— OCR 短句/病句检测:OCR 把消息"嚼碎"生成碎片 ——
|
||||
# 通用疑问词(有这些词说明有查询意向,放行)
|
||||
_q_words = {"什么", "怎么", "如何", "为什么", "吗", "呢", "吧", "能", "可以", "有没有", "哪些", "哪", "哪几个"}
|
||||
_q_words = {"什么", "怎么", "如何", "为什么", "吗", "呢", "吧", "能", "可以", "有没有", "哪些", "哪", "哪几个",
|
||||
"是不是", "会不会", "能不能", "怎么办", "好不好", "行不行"}
|
||||
# "自行修?" → 3字无查询意向碎片;"将其修正后重三" → 病句
|
||||
if len(line) <= 5:
|
||||
# ≤5字短句:必须包含疑问词才放行,否则是OCR碎片
|
||||
if len(line) <= 8:
|
||||
# ≤8字短句:必须包含疑问词才放行,否则是OCR碎片
|
||||
# 有问号但无疑问词 → 病句(如"自行修?"无查询意向)
|
||||
has_q = any(w in line for w in _q_words)
|
||||
if not has_q:
|
||||
logger.debug(f"[{platform}] OCR 过滤: 短句碎片(≤5字) line={line!r}")
|
||||
logger.debug(f"[{platform}] OCR 过滤: 短句碎片(≤8字无问词) line={line!r}")
|
||||
continue
|
||||
if 5 < len(line) < 50:
|
||||
# 病句1:句子以动词/助词结尾但缺少宾语且无标点
|
||||
@@ -1838,6 +1842,18 @@ class MessageMonitor:
|
||||
if re.match(r'^[其将把被]', line) and not re.search(r'[。!?,,;;::?]$', line):
|
||||
logger.debug(f"[{platform}] OCR 过滤: 病句(缺主语) line={line!r}")
|
||||
continue
|
||||
# 病句4:OCR 把用户消息和系统提示拼接的复合碎片
|
||||
# 如"自行修?# 本插件" "请自行下载源码修改...使用类json"
|
||||
if '#' in line or '源码' in line and '修改' in line:
|
||||
# 含文档标记或系统提示特征
|
||||
if re.search(r'#\s*本|请自行|源码.*修改|修改.*源码', line):
|
||||
logger.debug(f"[{platform}] OCR 过滤: 病句(系统提示拼接) line={line!r}")
|
||||
continue
|
||||
# 病句5:句子含"修"但不是完整查询(如"自行修?" "帮我修" "能不能修")
|
||||
# 除非有明确疑问词,否则"修"碎片不应触发查库
|
||||
if re.search(r'修[??。]?\s*$', line) and not any(w in line for w in _q_words):
|
||||
logger.debug(f"[{platform}] OCR 过滤: 病句(修字碎片) line={line!r}")
|
||||
continue
|
||||
|
||||
texts.append(line)
|
||||
logger.debug(f"[{platform}] OCR -> {len(ocr_lines)} raw, {len(texts)} filtered, "
|
||||
@@ -2160,18 +2176,13 @@ class MessageMonitor:
|
||||
|
||||
@staticmethod
|
||||
def _has_user_colors(im, platform: str) -> bool:
|
||||
"""区域颜色检测:左/右半区独立分析,判断截图中有无用户消息。
|
||||
|
||||
QQ: bot 蓝色气泡右对齐,用户白色气泡左对齐
|
||||
WeChat: bot 绿色气泡右对齐,用户白色气泡左对齐
|
||||
|
||||
策略:
|
||||
- 跳过顶部 20%(聊天头部名称/标题干扰)
|
||||
- 左右半区分别采样(~15×15 网格/半区)
|
||||
- 右半区有 bot 色 + 左半区几乎无"其它"像素 → 仅 bot 回复 → 跳过
|
||||
- 左半区有大量"其它"像素 → 用户内容(头像/文字/时间戳)→ 继续
|
||||
|
||||
返回 True=有用户内容/拿不准,False=仅 bot 回复
|
||||
"""区域颜色检测:判断截图中是否有**对方发来的消息**,决定是否需要OCR。
|
||||
|
||||
⭐ 核心原则:自身消息 = 最顶层门禁,不过则全部取消。
|
||||
- QQ: 用户白色气泡左对齐,bot/对方蓝色气泡右对齐
|
||||
- WeChat: 对方白色气泡左对齐,自己绿色气泡右对齐
|
||||
|
||||
返回 True=有对方消息需要OCR,False=仅自身消息/无消息 → 跳过
|
||||
"""
|
||||
w, h = im.size
|
||||
if w < 10 or h < 10:
|
||||
@@ -2191,11 +2202,14 @@ class MessageMonitor:
|
||||
step_y = max(2, ch // 15)
|
||||
|
||||
if platform == "qq":
|
||||
# QQ: 蓝色 = bot/对方气泡(右对齐)
|
||||
def is_bot(r, g, b):
|
||||
return 110 <= r <= 190 and 170 <= g <= 235 and 220 <= b <= 255
|
||||
else:
|
||||
def is_bot(r, g, b):
|
||||
# WeChat: 绿色 = 自己发的消息(右对齐)— 不是 bot 色
|
||||
def is_self(r, g, b):
|
||||
return 135 <= r <= 180 and 205 <= g <= 250 and 75 <= b <= 125
|
||||
is_bot = is_self
|
||||
|
||||
def analyze(img):
|
||||
p = img.load()
|
||||
@@ -2219,27 +2233,51 @@ class MessageMonitor:
|
||||
right_bot, right_other, right_white, right_total = analyze(right_half)
|
||||
|
||||
rlogger = logging.getLogger("msgbot.monitor")
|
||||
rlogger.debug(f"[{platform}] 像素: 左其它={left_other}/{left_total} bot={left_bot} 白={left_white} | "
|
||||
f"右其它={right_other}/{right_total} bot={right_bot} 白={right_white}")
|
||||
rlogger.debug(f"[{platform}] 像素: 左其它={left_other}/{left_total} "
|
||||
f"{'自身' if platform=='wechat' else 'bot'}={left_bot} 白={left_white} | "
|
||||
f"右其它={right_other}/{right_total} "
|
||||
f"{'自身' if platform=='wechat' else 'bot'}={right_bot} 白={right_white}")
|
||||
|
||||
# 右半区有 bot 气泡 + 左半区几乎无"其它" → 仅 bot 回复
|
||||
# "其它"阈值:QQ 5 / WeChat 5(bot 气泡内白字不会产生其它像素)
|
||||
# ====== 微信专用逻辑 ======
|
||||
# 颜色语义:
|
||||
# 白色(>225,>225,>225) = 对方消息(左对齐)
|
||||
# 绿色(135-180,205-250,75-125) = 自己消息(右对齐)
|
||||
# 其它 = 背景/UI元素
|
||||
if platform == "wechat":
|
||||
# ⭐ 自身消息门禁:右大量绿色(自身) + 左无白色(无对方) → 仅自身,跳过
|
||||
if right_bot >= 3 and left_white < 3:
|
||||
rlogger.debug(f"[wechat] 像素检测: 仅自身消息(右绿={right_bot},左白={left_white}), 跳过")
|
||||
return False
|
||||
|
||||
# 左半区有白色气泡 → 有对方消息,必须OCR
|
||||
if left_white >= 3:
|
||||
rlogger.debug(f"[wechat] 像素检测: 有对方消息(左白={left_white}), 继续")
|
||||
return True
|
||||
|
||||
# 左半区无白色气泡 → 无对方消息,跳过
|
||||
# (包括:仅自身消息、空聊天、非聊天界面)
|
||||
rlogger.debug(f"[wechat] 像素检测: 无对方白色气泡(左白={left_white}), 跳过")
|
||||
return False
|
||||
|
||||
# ====== QQ/TIM 专用逻辑 ======
|
||||
# 颜色语义:
|
||||
# 白色(>225,>225,>225) = 用户消息(左对齐)
|
||||
# 蓝色(110-190,170-235,220-255) = bot/对方消息(右对齐)
|
||||
# 其它 = 背景/UI元素
|
||||
|
||||
# 右半区有 bot 蓝色 + 左半区几乎无"其它" → 仅 bot 回复
|
||||
if right_bot >= 3 and left_other < 5 and left_bot == 0:
|
||||
rlogger.debug(f"[{platform}] 像素检测: 仅bot回复(右bot={right_bot}), 跳过")
|
||||
rlogger.debug(f"[qq] 像素检测: 仅bot回复(右蓝={right_bot}), 跳过")
|
||||
return False
|
||||
|
||||
# 左右半区合计白色像素极少 → 无可见消息气泡
|
||||
# QQ 深色主题聊天区背景暗,用户白色气泡是主要白色来源
|
||||
# 微信浅色主题虽然背景较亮,但无气泡时"其它"像素占主导
|
||||
total_white = left_white + right_white
|
||||
total_bot = left_bot + right_bot
|
||||
if total_white < 20 and total_bot < 3:
|
||||
rlogger.debug(f"[{platform}] 像素检测: 无可见消息气泡(白={total_white}), 跳过")
|
||||
rlogger.debug(f"[qq] 像素检测: 无可见消息气泡(白={total_white}), 跳过")
|
||||
return False
|
||||
|
||||
# 白色气泡柱检测:左 35% 是否有白色垂直柱
|
||||
# CMD 窗口/非聊天截图即使有零星白像素也不会形成柱状
|
||||
# 用户气泡在左侧形成多行多列白色密集区,CMD 窗口白色文字分散在各行
|
||||
left_35 = check.crop((0, 0, int(cw * 0.35), ch))
|
||||
if left_35.width >= 15 and left_35.height >= 15:
|
||||
l35_p = left_35.load()
|
||||
@@ -2258,7 +2296,7 @@ class MessageMonitor:
|
||||
row_whites.append(rw)
|
||||
max_row_white = max(row_whites) if row_whites else 0
|
||||
if max_row_white < 3:
|
||||
rlogger.debug(f"[{platform}] 像素检测: 无白色气泡柱(r={row_whites}), 跳过(非聊天截图)")
|
||||
rlogger.debug(f"[qq] 像素检测: 无白色气泡柱(r={row_whites}), 跳过(非聊天截图)")
|
||||
return False
|
||||
|
||||
# 拿不准保守处理
|
||||
|
||||
Reference in New Issue
Block a user