"""微信消息监听器 — 顶层编排""" from __future__ import annotations import logging import threading from collections.abc import Callable from ..config import Config from ..ui.wechat import WeChatWindow from ..ui.conversation import ConversationList from ..ui.message import MessageList from .event import MessageEvent from .polling import PollingEngine logger = logging.getLogger(__name__) class WeChatWatcher: """微信消息监听器:整合窗口定位、会话切换、消息轮询""" def __init__(self, config: Config | None = None) -> None: self.config = config or Config() self._wechat = WeChatWindow(self.config) self._conv_list: ConversationList | None = None self._msg_list: MessageList | None = None self._poller: PollingEngine | None = None self._thread: threading.Thread | None = None self._stop_event = threading.Event() self._callbacks: list[Callable[[MessageEvent], None]] = [] # ------------------------------------------------------------------ # 回调 # ------------------------------------------------------------------ def on_message(self, callback: Callable[[MessageEvent], None]) -> None: """注册消息回调(可在 start 前多次调用)""" self._callbacks.append(callback) # ------------------------------------------------------------------ # 初始化 # ------------------------------------------------------------------ def connect(self) -> bool: """查找并连接微信窗口""" win = self._wechat.find_window() if win is None: logger.error("无法连接微信窗口,请确保微信已登录并保持窗口可见") return False self._conv_list = ConversationList(win, self.config) self._msg_list = MessageList(win) self._poller = PollingEngine(self._msg_list, self.config) for cb in self._callbacks: self._poller.on_message(cb) logger.info("微信监听器初始化完成") return True # ------------------------------------------------------------------ # 启动 / 停止 # ------------------------------------------------------------------ def start(self, conversation: str = "", daemon: bool = True) -> bool: """在后台线程中启动消息轮询""" if self._poller is None: if not self.connect(): return False self._stop_event.clear() self._thread = threading.Thread( target=self._poller.start_loop, args=(conversation, self._stop_event), daemon=daemon, name="wechat-watcher", ) self._thread.start() logger.info("监听线程已启动") return True def stop(self) -> None: """停止监听""" self._stop_event.set() if self._poller: self._poller.stop() if self._thread and self._thread.is_alive(): self._thread.join(timeout=5) logger.info("监听已停止") # ------------------------------------------------------------------ # 会话切换 # ------------------------------------------------------------------ def switch_conversation(self, name: str) -> bool: """切换到指定会话""" if self._conv_list is None: logger.error("监听器未初始化,请先调用 connect()") return False return self._conv_list.click_conversation(name) def search_and_switch(self, keyword: str) -> bool: """搜索并切换到指定会话""" if self._conv_list is None: logger.error("监听器未初始化,请先调用 connect()") return False return self._conv_list.search_conversation(keyword)