diff --git a/CLAUDE.md b/CLAUDE.md index 0dd7b1f..55f87b7 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -41,7 +41,7 @@ wechat_clicker/ - "..."按钮搜索限制在预览区域(独立窗口或主窗口 x>200),排除侧边栏 - 可见性检查基于元素**中心点**是否在消息列表可见区域内(30px margin) - 状态检测基于窗口计数 + **AXMinimized 属性**:区分窗口存在但最小化 vs 真正可见 -- **窗口恢复策略**:NSRunningApplication.unhide(隐藏)→ AX API 设置 AXMinimized=False(最小化)→ activate(前台);AppleScript 不可靠(微信不支持 miniaturized 属性) +- **窗口恢复策略(4 层)**:NSRunningApplication.unhide(隐藏)→ 检测 0 窗口时 `open -b` 重新打开(窗口被关闭)→ AX API 设置 AXMinimized=False(最小化)→ activate(前台) - **UNKNOWN 状态恢复**:先 Escape + Cmd+W 关闭可能的弹窗/多余窗口,再 activate + 点击侧边栏;外层循环连续恢复失败时指数退避(10s → 20s → 40s → ... → 120s 封顶),避免无效高频重试 ## 使用方法 @@ -68,12 +68,12 @@ python main.py --debug # 详细日志 - `config.yaml` 中可设置扫描间隔、延迟范围、白/黑名单、工作时间、媒体类型开关 - `max_chats_per_scan: 0` 表示不限制,处理全部未读聊天(适合大量群聊场景) - 默认只点击图片(`media.click_files: false`, `media.click_videos: false`) -- 默认黑名单包含微信系统账号 +- 默认黑名单包含微信系统账号和非聊天界面(腾讯新闻、微信支付、微信团队、服务号、订阅号、文件传输助手) - 处理完有未读的聊天后立即重扫,不等待 scan interval;只有无未读时才 sleep - 忙时(未读 > 5)自动跳过随机休息 ## 注意事项 -- 微信窗口最小化或隐藏时工具会自动恢复(通过 AX API 设置 AXMinimized=False + NSRunningApplication activate) +- 微信窗口最小化、隐藏或被关闭时工具均会自动恢复(unhide → open -b 重开窗口 → AXMinimized=False → activate) - 运行时会占用微信前台操作 - 建议在专用电脑上运行 diff --git a/config.example.yaml b/config.example.yaml index 1ed0329..19a34df 100644 --- a/config.example.yaml +++ b/config.example.yaml @@ -36,6 +36,9 @@ filter: - "腾讯新闻" - "微信支付" - "微信团队" + - "服务号" + - "订阅号" + - "文件传输助手" # 媒体处理 media: diff --git a/project.md b/project.md index c15525a..892f053 100644 --- a/project.md +++ b/project.md @@ -102,6 +102,11 @@ - [x] **修复: UNKNOWN 状态长时间无法恢复** — 原恢复逻辑只做 activate + 点侧边栏,遇到弹窗/多余窗口无法清理。改为先 Escape + Cmd+W 关闭可能的弹窗,再 activate + 点侧边栏 - [x] **改进: 恢复失败指数退避** — 外层循环连续恢复失败时退避等待:10s → 20s → 40s → 80s → 120s(封顶),避免每 13 秒重复无效尝试。恢复成功后计数器归零 +### v0.9.3 窗口关闭恢复 (2026/04/24) + +- [x] **修复: 窗口被关闭(非最小化)时无法恢复** — 微信进程在运行但 AXWindows=0(窗口被关闭而非最小化),原 unhide/AXMinimized=False/activate 均无效。新增策略:检测到 0 窗口时通过 `subprocess.run(["open", "-b", bundle_id])` 重新打开微信窗口 +- [x] **窗口恢复策略升级为 4 层** — unhide(隐藏)→ 检测 0 窗口时 open -b 重开(关闭)→ AXMinimized=False(最小化)→ activate(前台) + ### 待验证 - [x] ~~验证最小化窗口自动恢复功能(需手动最小化微信窗口测试)~~ diff --git a/wechat_clicker/ax_bridge.py b/wechat_clicker/ax_bridge.py index f2fdcc1..4bb5448 100644 --- a/wechat_clicker/ax_bridge.py +++ b/wechat_clicker/ax_bridge.py @@ -5,6 +5,7 @@ import logging import re +import subprocess import time from ApplicationServices import ( @@ -129,11 +130,23 @@ class AXBridge: app_ref = self.get_app_ref(bundle_id) if app_ref: windows = self.get_windows(app_ref) - for win in windows: - if self.is_window_minimized(win): - title = self.get_title(win) or "?" - logger.info(f"通过 AX API 取消窗口最小化: {title}") - self.set_window_minimized(win, False) + if not windows: + # 窗口被关闭(不是最小化),通过 open 命令重新打开 + logger.info(f"{app_name} 没有窗口(已被关闭),通过 open 命令重新打开...") + try: + subprocess.run( + ["open", "-b", bundle_id], + capture_output=True, timeout=5, + ) + time.sleep(1.5) + except Exception as e: + logger.error(f"open 命令失败: {e}") + else: + for win in windows: + if self.is_window_minimized(win): + title = self.get_title(win) or "?" + logger.info(f"通过 AX API 取消窗口最小化: {title}") + self.set_window_minimized(win, False) time.sleep(0.5) # 策略3: activate 带到前台 diff --git a/wechat_clicker/config.py b/wechat_clicker/config.py index ce0448e..63d42ce 100644 --- a/wechat_clicker/config.py +++ b/wechat_clicker/config.py @@ -35,7 +35,7 @@ DEFAULTS = { "filter": { "mode": "all", "whitelist": [], - "blacklist": ["腾讯新闻", "微信支付", "微信团队"], + "blacklist": ["腾讯新闻", "微信支付", "微信团队", "服务号", "订阅号", "文件传输助手"], }, "media": { "click_images": True,