You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
107 lines
3.7 KiB
107 lines
3.7 KiB
"""微信消息监听器 — 顶层编排"""
|
|
|
|
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)
|
|
|