fix: 微信颜色检测放宽+KB精确匹配+自动回复重试

1. monitor.py - 微信白色气泡检测阈值放宽(>=245->>=240, >=3->>=2)
   适配新版微信UI颜色变化,减少漏检
2. knowledge_base.py - 精确短语匹配(5x加分)+子串匹配(2.5x/1.5x)
   防止查询被不相关文档抢走高分
3. gui.py - 自动回复发送失败重试(3次/5秒间隔)
   连续失败退避本轮,避免反复抢焦点
4. .gitignore - 补充build目录下的证书/构建产物
@
This commit is contained in:
youpaishidifu
2026-08-03 16:42:21 +08:00
parent 708bcbf315
commit c6c8cc0571
4 changed files with 52 additions and 14 deletions
+3
View File
@@ -7,6 +7,9 @@ dist/
# PyInstaller output (在build目录内),但保留构建脚本
build/build/
build/*.log
build/cert_info.txt
build/code_signing_cert.*
build/windowmsgbot/
# Virtual environment
venv/
+14 -3
View File
@@ -383,10 +383,14 @@ class MsgBotApp(ctk.CTk):
self.after(200, self._flush_reply_queue)
def _execute_reply(self, data: dict):
"""主线程执行自动回复发送。加上发送锁防止监控轮询干扰。"""
"""主线程执行自动回复发送。加上发送锁防止监控轮询干扰。
自动重试:失败后延缓5秒重试,连续3次失败视为用户接管,退避本轮。
"""
platform = data["platform"]
answer = data["answer"]
sender = data.get("sender")
max_retries = 3
retry_delay = 5 # 秒
# 设发送锁,阻止监控轮询切走焦点
self.monitor._sending_event.set()
@@ -396,12 +400,19 @@ class MsgBotApp(ctk.CTk):
found = self.monitor._search_contact(platform, sender)
if not found:
self._log(f"[{platform}] ⚠ 找不到联系人 {sender},当前会话回复")
# 发送(主线程 SetForegroundWindow 可靠,且不受监控干扰)
# 带重试的发送逻辑
for attempt in range(max_retries):
ok = self.monitor._paste_and_send(platform, answer)
if ok:
self._log(f"[{platform}] 🤖 已自动回复: {answer[:60]}...")
return # 成功直接退出
else:
self._log(f"[{platform}] ⚠ 自动回复发送失败(目标窗口无法带到前台)")
if attempt < max_retries - 1:
self._log(f"[{platform}] ⚠ 自动回复发送失败,{retry_delay}秒后重试({attempt+1}/{max_retries})")
time.sleep(retry_delay)
else:
self._log(f"[{platform}] ⚠ 自动回复发送失败(连续{max_retries}次),用户可能在接管,本轮跳过")
finally:
self.monitor._sending_event.clear()
+17
View File
@@ -317,6 +317,23 @@ class KnowledgeBase:
combined_scores[i] *= 1.3
elif body_hits >= 1:
combined_scores[i] *= 1.1
# ⭐ 精确短语匹配:文档包含完整查询短语 → 巨幅奖励
# 防止"php端的管理员密码"匹配到不含"php"的云背包文档
full_text = heading + " " + body
if query_stripped in full_text:
combined_scores[i] *= 5.0
elif len(query_stripped) >= 4:
# 2字以上子串匹配:如"php端"、"管理员密码"
sub_hits = 0
for qlen in range(min(len(query_stripped), 6), 3, -1):
for start in range(len(query_stripped) - qlen + 1):
phrase = query_stripped[start:start+qlen]
if phrase in full_text:
sub_hits += 1
if sub_hits >= 2:
combined_scores[i] *= 2.5
elif sub_hits == 1:
combined_scores[i] *= 1.5
# 短查询惩罚:分词数 ≤2 的短查询(如图标残片/短词)得分大幅降权
# 防止 OCR 碎片中的高 IDF 词(如"下载")获得超过真实用户消息的分数
+13 -6
View File
@@ -3253,7 +3253,8 @@ class MessageMonitor:
"""判定 (x,y) 的纯白像素是否属于"白色气泡"(对方消息),而非:
- 聊天浅灰背景(本身不是 >=245 候选)
- 彩色(绿/蓝)自身气泡上的白色文字(邻域为彩色底色)
规则:四方向(±4px)邻域中 >=3 个也为 near-white(>=245) 才算气泡。
规则:四方向(±4px)邻域中 >=2 个也为 near-white(>=240) 才算气泡。
⭐ 放宽条件:>=3→>=2,>=245→>=240,适配新版微信UI颜色变化
白字笔画的邻域多为彩色气泡底色 → 不通过 → 不被误判为对方消息。
"""
d = 4
@@ -3263,11 +3264,11 @@ class MessageMonitor:
if 0 <= nx < w and 0 <= ny < h:
try:
rr, gg, bb = px[nx, ny][:3]
if rr >= 245 and gg >= 245 and bb >= 245:
if rr >= 240 and gg >= 240 and bb >= 240:
hits += 1
except Exception:
pass
return hits >= 3
return hits >= 2
@staticmethod
@@ -3347,15 +3348,21 @@ class MessageMonitor:
# 其它 = 背景/UI元素
if platform == "wechat":
# ⭐ 自身消息门禁:右大量绿色(自身) + 左无白色(无对方) → 仅自身,跳过
if right_bot >= 3 and left_white < 3:
if right_bot >= 3 and left_white < 2:
rlogger.debug(f"[wechat] 像素检测: 仅自身消息(右绿={right_bot},左白={left_white}), 跳过")
return False
# 左半区有白色气泡 → 有对方消息,必须OCR
if left_white >= 3:
# 左半区有白色气泡(>=2) → 有对方消息,必须OCR
if left_white >= 2:
rlogger.debug(f"[wechat] 像素检测: 有对方消息(左白={left_white}), 继续")
return True
# ⭐ 放宽兜底:左半区有1个白色像素也尝试OCR,防新版UI检测遗漏
# (旧逻辑:0个才跳过,可能误杀新UI浅色气泡)
if left_white == 1:
rlogger.debug(f"[wechat] 像素检测: 左白=1(边缘), 尝试OCR")
return True
# 左半区无白色气泡 → 无对方消息,跳过
# (包括:仅自身消息、空聊天、非聊天界面)
rlogger.debug(f"[wechat] 像素检测: 无对方白色气泡(左白={left_white}), 跳过")