From c6c8cc0571e7cd608902c69ee4905fa32b5c204c Mon Sep 17 00:00:00 2001 From: youpaishidifu Date: Mon, 3 Aug 2026 16:42:21 +0800 Subject: [PATCH] =?UTF-8?q?@=20fix:=20=E5=BE=AE=E4=BF=A1=E9=A2=9C=E8=89=B2?= =?UTF-8?q?=E6=A3=80=E6=B5=8B=E6=94=BE=E5=AE=BD+KB=E7=B2=BE=E7=A1=AE?= =?UTF-8?q?=E5=8C=B9=E9=85=8D+=E8=87=AA=E5=8A=A8=E5=9B=9E=E5=A4=8D?= =?UTF-8?q?=E9=87=8D=E8=AF=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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目录下的证书/构建产物 @ --- .gitignore | 3 +++ gui.py | 25 ++++++++++++++++++------- knowledge_base.py | 17 +++++++++++++++++ monitor.py | 21 ++++++++++++++------- 4 files changed, 52 insertions(+), 14 deletions(-) diff --git a/.gitignore b/.gitignore index e525598..3020004 100644 --- a/.gitignore +++ b/.gitignore @@ -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/ diff --git a/gui.py b/gui.py index 8e03f09..913f7af 100644 --- a/gui.py +++ b/gui.py @@ -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 可靠,且不受监控干扰) - ok = self.monitor._paste_and_send(platform, answer) - if ok: - self._log(f"[{platform}] 🤖 已自动回复: {answer[:60]}...") - else: - self._log(f"[{platform}] ⚠ 自动回复发送失败(目标窗口无法带到前台)") + + # 带重试的发送逻辑 + for attempt in range(max_retries): + ok = self.monitor._paste_and_send(platform, answer) + if ok: + self._log(f"[{platform}] 🤖 已自动回复: {answer[:60]}...") + return # 成功直接退出 + else: + 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() diff --git a/knowledge_base.py b/knowledge_base.py index 19cd3db..092f882 100644 --- a/knowledge_base.py +++ b/knowledge_base.py @@ -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 词(如"下载")获得超过真实用户消息的分数 diff --git a/monitor.py b/monitor.py index 249bb84..f72e76f 100644 --- a/monitor.py +++ b/monitor.py @@ -3248,12 +3248,13 @@ class MessageMonitor: bits.append('1' if left > right else '0') return ''.join(bits) - @staticmethod + @staticmethod def _is_white_bubble_pixel(px, x, y, w, h) -> bool: """判定 (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}), 跳过")