Metadata-Version: 2.4
Name: weixinautox4
Version: 2.3.0
Summary: WeChat automation SDK with license management.
Author-email: agentaibot <zhilongfeng66@gmail.com>
License-Expression: LicenseRef-Proprietary
Classifier: Programming Language :: Python :: 3
Classifier: Operating System :: Microsoft :: Windows
Requires-Python: >=3.10
Description-Content-Type: text/markdown
License-File: LICENSE
Requires-Dist: requests>=2.24.0
Requires-Dist: pydantic>=1.8.2
Requires-Dist: Pillow>=9.0.0
Requires-Dist: pywin32==311
Requires-Dist: uiautomation==2.0.29
Dynamic: license-file

# WeixinAuto（Windows 微信自动化）

基于 UIAutomation 的微信 PC 端自动化工具，用来：

- 打开 / 切换聊天窗口  
- 发送文本消息 / 文件消息
- 监听指定群 / 会话的新消息  
- 批量监听多个群，并统一处理消息  

> ⚠️ 仅支持 **Windows + 新版微信（`weixin.exe`）**，并且需要保持微信登录状态。本项目核心类已全面增加 **Type Hints (类型注解)**，在 PyCharm 等 IDE 中具有极佳的代码补全体验。

---

## 环境要求

- Windows 10 | 11
- Python 3.10+（建议）  
- PC 微信客户端 4.1 版本以上（`weixin.exe`）  

## 安装

外部用户直接通过 pip 安装（包名 `weixinautox4`）：

```bash
pip install weixinautox4
```

源码开发环境则安装依赖：

```bash
pip install -r requirements.txt
```

---

## 核心类概览

主要对外只需要关心一个类：`Wechat`

```python
from weixinauto.wechat import Wechat
```

核心能力：

- `send_message(target: str, text: str)`：给指定的 target 目标发送文本消息
- `send_file(target: str, file_path: str)`：给指定的 target 目标发送文件
- `add_listen_chat(title: str, on_new: Callable, ...)`：监听单个会话（回调模式）
- `add_listens(titles: Iterable[str], ...)` + `start_listen()` + `get_listen_message()`：批量监听多个群（轮询模式）
- `stop_listen(title: Optional[str] = None)`：停止监听

*注：底层方法已封装针对微信状态的安全保护，未激活时调用将自发拦截并输出结构化警告日志。*

---

## 1. 激活与初始化示例

首次运行必须要激活才能实例化对象工作：

```python
from weixinauto import Wechat
from weixinauto.logger import get_logger

logger = get_logger(__name__)
wx = Wechat()

# 填入您的激活码
ok, update_info = wx.activate("LIC-XGFD8TF3-QTKIZ8Q5")
if ok:
    logger.info("核心已就绪，微信驱动激活成功！")
else:
    logger.error("激活失败！")
```

说明：

- 一台电脑一个授权码，授权码有效期为 1 年：
  1. 联系 `wx:agentaibot` 获得授权码
  2. 每个授权码一年 300 元

---

## 2. 发送消息与发送文件示例

**向指定群 / 好友发送文本 / 文件：**

```python
from weixinauto.wechat import Wechat
import time

wx = Wechat()
wx.activate("LIC-XGFD8TF3-QTKIZ8Q5") # 确保已激活

# 1. 发送文本消息
ok_msg = wx.send_message(
    target="工作群", 
    text="大家好，我是自动化助手~"
)
print("文本发送结果：", ok_msg)

time.sleep(1)

# 2. 发送文件或图片
ok_file = wx.send_file(
    target="工作群", 
    file_path=r"E:\1.jpg"
)
print("文件发送结果：", ok_file)
```

说明：

- 内部会自动执行：
  1. 激活并前置微信主窗口。
  2. 在搜索框搜索目标并精准进入聊天窗口。
  3. 将文本或文件填入/粘贴到输入框后执行发送。
- 方法全自动检测微信是否可用，若未就绪会打印标准 `logger` 级别警告。

---

## 3. 单个窗口监听（精准对答模式）

### 3.1 回调函数标准签名

由于我们添加了严格的类型提示，你可以直接复制以下推荐签名来享受 IDE 补全：

```python
from weixinauto.infra.uia.message import ChatMessage
from typing import Callable

def on_new_msg(group: str, msg: ChatMessage, reply: Callable[[str, str], bool]):
    """
    group: 当前消息所属群 / 会话标题
    msg: 包含发送人、时间戳、类型的消息对象
    reply: 便捷的当前窗口回复函数
    """
    pass
```

`ChatMessage` 常用属性：
- `msg.text` → 文本内容（去掉尾部空白）  
- `msg.mtype` → 消息类型（纯文本/图片等）  
- `msg.is_self` → 是否为自己发的消息（用于防回声死循环）  
- `msg.sender` → 发送者昵称（若开启获取功能）

### 3.2 示例：自动回复指定口令

```python
import time
from weixinauto.wechat import Wechat
from weixinauto.infra.uia.message import ChatMessage
from typing import Callable

def on_new(group: str, msg: ChatMessage, reply: Callable[[str, str], bool]):
    print(f"[{group}] 收到来自【{msg.sender}】的新消息：{msg.text}")

    # 简单示例：别人说“1”，我回“收到1” (防回声检测：必须排除是自己发的消息)
    if msg.text == "1" and not msg.is_self:
        # 这里的 reply 直接绑定了当前窗口的发送函数
        reply("收到1")

def main():
    wx = Wechat()
    wx.activate("...")
    
    # 挂载监听，不需要获取对方昵称时建议 need_nickname=False
    ok = wx.add_listen_chat(
        title="工作群",
        on_new=on_new,
        interval_sec=0.18,
        need_nickname=False, 
        auto_save_image=False # 若需存图改为 True
    )
    
    if ok:
        print("开始监听：工作群")
        try:
            while True:
                time.sleep(1.0)
        except KeyboardInterrupt:
            wx.stop_listen("工作群")
            
if __name__ == "__main__":
    main()
```

---

## 4. 批量监听多个群（轮询模式）

如果你需要作为“监控中心”同时监听数十个群落，轮询拉取是最稳健的方式：

```python
import time
from weixinauto.wechat import Wechat

def main():
    wx = Wechat()
    wx.activate("...")

    # 需要监听的群列表
    groups = ["工作群", "测试群A", "测试群B"]

    # 1) 批量挂载监听
    wx.add_listens(
        titles=groups,
        need_nickname=True,  # 如果要收集发件人名字
        poll_sec=0.15,
        auto_save_image=True # 文件如需下载也开启对应的配置
    )

    # 2) 驱动跑起来
    wx.start_listen()
    print(f"已启动批量监听，目标池：{groups}")

    try:
        while True:
            # 3) 按需自定间隔获取最近产生的所有未读信息
            all_msgs = wx.get_listen_messages(clear=True)
            
            if all_msgs:
                for chat_wnd, msgs in all_msgs.items():
                    group_name = chat_wnd.title
                    
                    for msg in msgs:
                        print(f"[{group_name}] {msg.sender}: {msg.text}")
                        # 可以直接从 wnd_obj 回复
                        if msg.text == "查表" and not msg.is_self:
                            chat_wnd.reply("表格生成中...")
                            
            time.sleep(0.5) 

    except KeyboardInterrupt:
        wx.stop_listen()

if __name__ == "__main__":
    main()
```

---

## 5. 全局未读红点监听（无视被折叠的群）

当有群聊或人在后台给你发消息（即使被折叠在消息列表），只要微信左侧边栏产生小红点，我们就可以进行全局抽取并回复。此模式非常适合做**全局的默认接待机器人**。

```python
import time
from typing import List, Callable
from weixinauto.wechat import Wechat
from weixinauto.infra.uia.message import ChatMessage

def on_global_new(msgs: List[ChatMessage], reply: Callable[[ChatMessage, str], None]) -> None:
    print(f"\\n================ 本轮收到 {len(msgs)} 条全局未读消息 =================")
    for m in msgs:
        print(f"[{m.group}] -> {m.sender} -> {m.text}")
        
        # 遇到特定词语触发全局回复（利用传入的 reply 函数和 msg 对象本身定位）
        if m.text == "在吗":
            ok = reply(m, "您好，系统自动接待中，请留言。")
            print(f"回复 {m.group} 结果: {ok}")

def main():
    wx = Wechat()
    wx.activate("...")

    # 挂载全局监听
    ok = wx.get_new_message(
        on_new=on_global_new, 
        interval_sec=5.0,        # 每 5 秒扫描一次红点
        need_nickname=True,      # 若要获取陌生人昵称
        auto_save_image=False, 
        auto_save_file=False
    )
    
    if ok:
        print("✅ 已启动【全局未读消息监听】。")
        try:
            while True:
                time.sleep(1)
        except KeyboardInterrupt:
            wx.stop_new_message_listen()
            
if __name__ == "__main__":
    main()
```

---

## 6. 防自言自语（回声抑制机制）

为避免机器人发出消息后误将其识别为新到达指令从而死循环，内部集成了双保险：

1. **缓存令牌校验**：在 `chat_wnd.reply` 或主动发送消息时，立刻缓存文本或文件的 HASH 令牌。
2. **侦听过滤**：监听到包含自发 HASH 的新节点时，直接抛弃不触发业务 callback。

**开发者守则：**
编写业务流时，依然建议加上 `if not msg.is_self` 作为终端护城河判定以保持逻辑严谨。

---

## 7. 其他运维管理

- **停止特定监听**：
  ```python
  wx.stop_listen("业务群A")
  ```
- **全局停止**：
  ```python
  wx.stop_listen()
  ```
- **全局退出清理**：
  ```python
  wx.shutdown()
  ```

### 注意事项：
1. **必须保持微信前台可见可用**，本版本深度依赖原生 Windows UI Automation。
2. 批量监听且 `need_nickname=True` 会模拟点击头像卡片弹出进行解析，在对前台鼠标干扰极度敏感或非专用副机的场景请慎用该参数（配置为 `False` 即可无感挂机）。

---

## 8. 系统整体说明与限制（高频并发场景必读）

本自动化基于 UI 树元素的物理抓取，针对高频消息环境存在以下客观限制，请开发者在业务架构中做好容错：

1. **多媒体处理抢占**
   - 接收到图片/文件消息时，若开启了自动保存，系统会先触发 UI 菜单级操作去下载。
   - 文件下载完成后，通过回调赋予 `msg.file_path`。也可以通过自定义路径保存。
2. **高频突发消息遗漏 (丢消息风险)**
   - 在执行诸如图片下载、点击头像获取昵称等前台焦点操作时，若瞬间（20~50毫秒内）群内爆发出第二条、第三条高速刷屏消息，后续消息可能在本次轮询周期内被 UI 树直接冲掉（覆盖）。
   - **现状**：当前版本暂时**未实现高并发的深度队列缓冲处理**。在极端高速刷屏的抢答或监控场景下，大概率会发生信息遗漏，这属于 UI 自动化方案的物理极限。业务层请不要对 100% 毫无漏接的消息流做绝对强依赖。
