fix: 微信窗口被覆盖时无法唤醒 - 保留PID快速EnumWindows + 冷却缩短到10s
问题根因: 1. UIA (auto.WindowControl) 在窗口被其他程序覆盖时搜索不稳定 2. 缓存HWND验证失败时,PID也被清空,导致每次都要从psutil全进程扫描重新找PID 3. 搜索失败后30s冷却太长 修复: - 仅清HWND,保留PID供后续EnumWindows快速复用(毫秒级,跳过psutil扫描) - 新增PID快速路径:验证失败后先用缓存PID直接EnumWindows,再走UIA和psutil兜底 - 冷却时间从30s缩短到10s,加快恢复轮询
This commit is contained in:
+67
-6
@@ -703,8 +703,9 @@ class MessageMonitor:
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
# 仅清 HWND,保留 PID 供后续 EnumWindows 快速复用
|
||||
old_pid = self._cached_pid.get(platform)
|
||||
self._cached_hwnd.pop(platform, None)
|
||||
self._cached_pid.pop(platform, None)
|
||||
|
||||
# 非阻塞:窗口未找到冷却期内直接跳过搜索,不浪费 12s 超时
|
||||
cooldown_until = self._window_not_found_until.get(platform, 0)
|
||||
@@ -712,6 +713,35 @@ class MessageMonitor:
|
||||
return None
|
||||
|
||||
if platform == "wechat":
|
||||
# ── 快速路径:用缓存 PID 直接 EnumWindows(毫秒级,跳过 psutil 全进程扫描)──
|
||||
if old_pid:
|
||||
try:
|
||||
pid_windows = []
|
||||
def _fast_enum_cb(h, results, _pid=old_pid):
|
||||
try:
|
||||
_, found_pid = win32process.GetWindowThreadProcessId(h)
|
||||
if found_pid == _pid:
|
||||
top = win32gui.GetAncestor(h, win32con.GA_ROOT)
|
||||
if top not in results:
|
||||
results.append(top)
|
||||
except Exception:
|
||||
pass
|
||||
return True
|
||||
win32gui.EnumWindows(_fast_enum_cb, pid_windows)
|
||||
for top in pid_windows:
|
||||
if not (win32gui.IsWindow(top) and win32gui.IsWindowVisible(top)):
|
||||
continue
|
||||
rect = win32gui.GetWindowRect(top)
|
||||
if (rect[2] - rect[0]) > 300 and (rect[3] - rect[1]) > 200:
|
||||
self._cached_hwnd[platform] = top
|
||||
self._window_not_found_until.pop(platform, None)
|
||||
title = win32gui.GetWindowText(top)
|
||||
logger.info(f"[{platform}] 窗口已定位(PID 快速): pid={old_pid} title={title!r} top={top}")
|
||||
return top
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
# ── UIA 搜索 ──
|
||||
for name in ["微信", "WeChat"]:
|
||||
try:
|
||||
w = auto.WindowControl(Name=name)
|
||||
@@ -844,8 +874,8 @@ class MessageMonitor:
|
||||
continue
|
||||
except Exception:
|
||||
pass
|
||||
# 搜索完毕没找到 → 设冷却避免重复 12s 超时
|
||||
self._window_not_found_until[platform] = time.time() + 30
|
||||
# 搜索完毕没找到 → 设冷却避免重复 12s 超时(10s 比 30s 更快恢复轮询)
|
||||
self._window_not_found_until[platform] = time.time() + 10
|
||||
return None
|
||||
|
||||
def _ensure_hwnd_with_timeout(self, platform: str, timeout: float = 12.0) -> Optional[int]:
|
||||
@@ -875,7 +905,7 @@ class MessageMonitor:
|
||||
t.start()
|
||||
if not done.wait(timeout=timeout):
|
||||
logger.warning(f"[{platform}] _ensure_hwnd 超时({timeout:.0f}s),跳过本轮")
|
||||
self._window_not_found_until[platform] = time.time() + 30
|
||||
self._window_not_found_until[platform] = time.time() + 10
|
||||
return None
|
||||
return result[0]
|
||||
|
||||
@@ -4129,6 +4159,35 @@ class MessageMonitor:
|
||||
logger.debug(f"[ocr] 过滤器2结果: 未检测到噪声特征,返回False")
|
||||
return False
|
||||
|
||||
# ⭐ OCR噪声前缀清理:去除UI按钮标签等OCR误识别的前缀
|
||||
# 如"必选,运营方有哪些权利和义务?" → "运营方有哪些权利和义务?"
|
||||
_OCR_NOISE_PREFIXES = re.compile(
|
||||
r'^(?:必选|必填|可选|请输入|搜索|查找|查询|发送|取消|确认|确定|提交|保存|删除|编辑| '
|
||||
r'|添加|新建|创建|设置|修改|返回|关闭|展开|收起|更多|选项|操作|'
|
||||
r'选择|查看|下载|上传|导入|导出|刷新|重置|清空|复制|粘贴|剪切|'
|
||||
r'同意|拒绝|同意并|下一步|上一步|完成|跳过|继续|登录|注册|退出|'
|
||||
r'开启|关闭|启用|禁用|暂停|恢复|开始|停止|运行|'
|
||||
r'是|否|有|无|对|错|真|假|好|坏|新|旧|已|未)'
|
||||
r'[,,::\s]*'
|
||||
)
|
||||
|
||||
@staticmethod
|
||||
def _clean_ocr_prefix(text: str) -> str:
|
||||
"""清理OCR噪声前缀(UI按钮标签等)。
|
||||
|
||||
如"必选,运营方有哪些权利和义务?" → "运营方有哪些权利和义务?"
|
||||
只清理开头的短前缀(≤6字),避免误删正常消息内容。
|
||||
"""
|
||||
if not text or len(text) < 4:
|
||||
return text
|
||||
m = MessageMonitor._OCR_NOISE_PREFIXES.match(text)
|
||||
if m and len(m.group(0)) <= 6:
|
||||
cleaned = text[len(m.group(0)):].strip()
|
||||
if cleaned and len(cleaned) >= 2:
|
||||
logger.debug(f"[ocr] 清理噪声前缀: {text[:30]!r} → {cleaned[:30]!r}")
|
||||
return cleaned
|
||||
return text
|
||||
|
||||
def _paste_and_send_safe(self, text: str, hwnd: int, msg_hash: int, platform: str) -> bool:
|
||||
"""Electron 应用安全发送(TIM/微信)。无 AttachThreadInput、无 SendMessage 同步点击。
|
||||
|
||||
@@ -5211,14 +5270,16 @@ class MessageMonitor:
|
||||
self._last_new_msg_time[platform] = time.time()
|
||||
else:
|
||||
self._last_new_msg_time[platform] = time.time()
|
||||
self._last_msg[platform] = item
|
||||
self._last_msg[platform] = cleaned_item
|
||||
engine_tag = self._last_chosen_engine.get(platform, "")
|
||||
logger.info(f"[{platform}] 新消息[{engine_tag}]: {item[:60]}")
|
||||
# ⭐ 清理OCR噪声前缀(如"必选,")后再搜索知识库
|
||||
cleaned_item = self._clean_ocr_prefix(item)
|
||||
# ⚠️ 急停检查:回复前最后检查,防止急停后仍然发送
|
||||
if not self._running:
|
||||
return
|
||||
if self._on_message:
|
||||
self._on_message(platform, item)
|
||||
self._on_message(platform, cleaned_item)
|
||||
if platform == "qq":
|
||||
if stale == 3:
|
||||
logger.debug(f"[QQ] 冻结 #{stale},强制刷新")
|
||||
|
||||
Binary file not shown.
Reference in New Issue
Block a user