修复QQ读不出/读旧数据/auto-reply错发平台
问题1: QQ读不出消息 — daemon线程SetForegroundWindow频繁失败
-> _run_loop添加PeekMessage创建Windows消息队列
-> _attach_set_foreground内部异常全部捕获,不抛给调用者
问题2: QQ读旧剪贴板数据 — PostMessage键盘 Electron不响应
-> 移除前台跳过检查(降频已足够,20s一次不频繁)
-> _click_message_area PostMessage->SendMessage(Electron响应同步消息)
-> 移除_force_scroll_bottom(PostMessage对Electron无效)
问题3: WeChat消息错发给QQ — auto-reply daemon线程SetForegroundWindow失败
-> auto_reply.on_kb_match不再直接调用_paste_and_send
-> 改为get_reply_data返回{platform,answer,sender}给主线程
-> gui.py添加_reply_queue + _flush_reply_queue(主线程after轮询)
-> _execute_reply在主线程执行_search_contact和_paste_and_send
(SetForegroundWindow从主线程调用永远可靠)
This commit is contained in:
+23
-25
@@ -64,7 +64,8 @@ class MessageMonitor:
|
||||
return
|
||||
self._running = True
|
||||
self._thread = threading.Thread(target=self._run_loop, daemon=True, name="monitor")
|
||||
self._thread.start()
|
||||
# 创建 Windows 消息队列(使 AttachThreadInput / SetForegroundWindow 在 daemon 线程生效)
|
||||
ctypes.windll.user32.PeekMessage(None, 0, 0, 0, 1) # PM_NOREMOVE
|
||||
logger.info("消息监控已启动")
|
||||
|
||||
def stop(self):
|
||||
@@ -93,9 +94,9 @@ class MessageMonitor:
|
||||
if self._wechat_enabled:
|
||||
self._poll("wechat")
|
||||
if self._qq_enabled:
|
||||
# QQ 降频:每 3 周期 poll 一次,减少抢窗口次数
|
||||
# QQ 降频:每 10 周期 poll 一次(~20s),减少抢窗口
|
||||
self._qq_skip_counter += 1
|
||||
if self._qq_skip_counter >= 3:
|
||||
if self._qq_skip_counter >= 10:
|
||||
self._qq_skip_counter = 0
|
||||
self._poll("qq")
|
||||
except Exception as e:
|
||||
@@ -221,10 +222,10 @@ class MessageMonitor:
|
||||
logger.debug(f"[{platform}] 滚动到底失败: {e}")
|
||||
|
||||
def _click_message_area(self, platform: str):
|
||||
"""PostMessage 点击消息区(靠右)聚焦消息列表,不移动物理鼠标。
|
||||
"""SendMessage 点击消息区(靠右)聚焦消息列表,不移动物理鼠标。
|
||||
|
||||
TIM 三栏布局: 联系人|会话列表|消息区 → 消息区在最右 ~12%。
|
||||
用 PostMessage 发送点击消息,不跳光标。
|
||||
用 SendMessage 同步发送(Electron 响应同步消息),PostMessage 异步 Electron 不处理。
|
||||
"""
|
||||
hwnd = self._cached_hwnd.get(platform)
|
||||
if not hwnd:
|
||||
@@ -242,10 +243,11 @@ class MessageMonitor:
|
||||
client_x = int(width * 0.88)
|
||||
client_y = int(height * 0.50)
|
||||
lparam = win32api.MAKELONG(client_x, client_y)
|
||||
win32gui.PostMessage(hwnd, win32con.WM_LBUTTONDOWN, win32con.MK_LBUTTON, lparam)
|
||||
win32gui.PostMessage(hwnd, win32con.WM_LBUTTONUP, 0, lparam)
|
||||
# SendMessage 同步 — Electron 能正确处理
|
||||
win32gui.SendMessage(hwnd, win32con.WM_LBUTTONDOWN, win32con.MK_LBUTTON, lparam)
|
||||
win32gui.SendMessage(hwnd, win32con.WM_LBUTTONUP, 0, lparam)
|
||||
time.sleep(0.3)
|
||||
logger.debug(f"[{platform}] PostMessage 点击 ({client_x},{client_y})")
|
||||
logger.debug(f"[{platform}] SendMessage 点击 ({client_x},{client_y})")
|
||||
except Exception as e:
|
||||
logger.debug(f"[{platform}] 聚焦消息区失败: {e}")
|
||||
|
||||
@@ -257,12 +259,21 @@ class MessageMonitor:
|
||||
if ctypes.windll.user32.AttachThreadInput(current_tid, target_tid, True):
|
||||
try:
|
||||
win32gui.SetForegroundWindow(hwnd)
|
||||
except Exception:
|
||||
pass
|
||||
finally:
|
||||
ctypes.windll.user32.AttachThreadInput(current_tid, target_tid, False)
|
||||
else:
|
||||
return
|
||||
# AttachThreadInput 失败 → 裸 SetForegroundWindow
|
||||
try:
|
||||
win32gui.SetForegroundWindow(hwnd)
|
||||
except Exception:
|
||||
pass
|
||||
except Exception:
|
||||
win32gui.SetForegroundWindow(hwnd)
|
||||
try:
|
||||
win32gui.SetForegroundWindow(hwnd)
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
def _read_win32_texts(self, platform: str) -> list:
|
||||
"""Win32 API 直接读窗口文本(绕过 UIA 缓存)。"""
|
||||
@@ -497,21 +508,8 @@ class MessageMonitor:
|
||||
def _read_messages(self, window, platform: str = "") -> list:
|
||||
"""每轮重新定位 ListControl。"""
|
||||
if platform == "qq":
|
||||
# QQ NT/TIM Electron: PostMessage 键盘不识别组合键 → 字面 "a"/"c"
|
||||
# 唯一可靠是 SetForegroundWindow + SendKeys(Ctrl+A/C)
|
||||
self._force_scroll_bottom(platform)
|
||||
|
||||
# QQ 在前台 → 用户正在用,跳过读取(不抢焦点)
|
||||
qq_hwnd = self._cached_hwnd.get("qq")
|
||||
if qq_hwnd:
|
||||
try:
|
||||
fg = win32gui.GetForegroundWindow()
|
||||
if fg == qq_hwnd:
|
||||
logger.debug("[QQ] 前台窗口,跳过读")
|
||||
return []
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
# QQ NT/TIM Electron: UIA 不可遍历,唯一可靠是 SetForegroundWindow + SendKeys(Ctrl+A/C)
|
||||
# 降频到 20s 一次,减少焦点切换次数;切回原前台由 sendkeys 后自动恢复
|
||||
texts = self._read_via_clipboard(platform)
|
||||
if texts:
|
||||
return texts
|
||||
|
||||
Reference in New Issue
Block a user