381 lines
11 KiB
Markdown
381 lines
11 KiB
Markdown
## 微信小程序活动发布自动化系统
|
||
|
||
一个基于 **FastAPI + pyautogui + Qwen-VL** 的微信小程序活动发布自动化工具,用于在桌面端微信中自动打开指定小程序、填写活动信息并提交发布。
|
||
|
||
系统整体采用「**规则脚本方案(pyautogui)优先,AI 视觉方案(Qwen)兜底」的双方案架构,配合任务调度与重试机制,提高自动化发布成功率。
|
||
|
||
---
|
||
|
||
## 功能概览
|
||
|
||
- **REST API 服务**
|
||
- `POST /api/publish`:提交一个活动发布任务
|
||
- `GET /api/task/{task_id}`:查询单个任务状态
|
||
- `GET /api/tasks`:查看所有历史任务
|
||
- `GET /api/health`:健康检查
|
||
|
||
- **双方案自动化执行**
|
||
- **方案 1:pyautogui 固定步骤**
|
||
- 通过 `xdotool` 查找并激活微信窗口
|
||
- 使用相对坐标点击小程序入口 / 目标小程序 / 文本输入框 / 提交按钮
|
||
- 自动输入活动标题和内容
|
||
- 每个关键步骤都会截图留存
|
||
- **方案 2:Qwen AI 视觉控制(备选)**
|
||
- 截图当前桌面,通过 Qwen-VL 分析界面
|
||
- 模型返回 JSON 控制指令(点击、输入、滚动、快捷键等)
|
||
- 根据模型输出逐步操作,直到标记为 `done` 或超时
|
||
|
||
- **任务调度与重试**
|
||
- 每个发布请求会生成独立 `task_id`
|
||
- 支持多次重试(指数退避),优先尝试 pyautogui
|
||
- 若 pyautogui 多次失败,会自动切换到 Qwen 方案
|
||
|
||
- **日志与截图**
|
||
- 日志输出到控制台和文件(默认 `/tmp/wechat_auto.log`)
|
||
- 截图保存到指定目录(默认 `/tmp/wechat_screenshots`)
|
||
|
||
---
|
||
|
||
## 目录结构
|
||
|
||
```text
|
||
wechat_auto/
|
||
├── main.py # FastAPI 应用入口(uvicorn 启动)
|
||
├── config.py # 配置加载(基于 pydantic-settings)
|
||
├── models/
|
||
│ └── activity.py # 活动模型 & 任务状态模型
|
||
├── api/
|
||
│ └── trigger.py # 对外 REST API 路由
|
||
├── core/
|
||
│ ├── task_scheduler.py # 任务调度与双方案执行
|
||
│ ├── window_manager.py # 微信窗口查找 / 激活 / 几何信息
|
||
│ └── executor/
|
||
│ ├── pyautogui_executor.py # 方案 1:规则脚本执行器
|
||
│ └── qwen_ai_executor.py # 方案 2:Qwen AI 执行器
|
||
├── utils/
|
||
│ ├── logger.py # 日志初始化
|
||
│ └── retry.py # 同步 / 异步重试装饰器
|
||
├── .env.example # 环境变量示例文件(不含真实密钥)
|
||
├── .env # 实际环境配置(**请勿提交到仓库**)
|
||
└── requirements.txt # Python 依赖
|
||
```
|
||
|
||
---
|
||
|
||
## 环境要求
|
||
|
||
- **操作系统**
|
||
- 建议:Linux 桌面环境(X11),当前实现依赖 `xdotool`、`scrot` 等工具
|
||
- **桌面环境**
|
||
- 已安装并登录 PC 版微信(窗口标题默认是 `WeChat`,可通过配置修改)
|
||
- **系统工具依赖**
|
||
- `xdotool`:窗口查找、激活、获取几何信息
|
||
- `scrot`:桌面截图(若无则退回 `pyautogui.screenshot`)
|
||
|
||
在 Debian/Ubuntu 上可通过下面命令安装:
|
||
|
||
```bash
|
||
sudo apt update
|
||
sudo apt install -y xdotool scrot
|
||
```
|
||
|
||
- **Python 环境**
|
||
- Python 3.10+(建议使用虚拟环境)
|
||
|
||
---
|
||
|
||
## 安装步骤
|
||
|
||
### 1. 克隆项目并进入目录
|
||
|
||
```bash
|
||
cd /home/quant/data/dev/mini_auto
|
||
cd wechat_auto
|
||
```
|
||
|
||
(如果你是在其他目录,请根据实际路径调整。)
|
||
|
||
### 2. 创建并激活虚拟环境(推荐)
|
||
|
||
```bash
|
||
python -m venv .venv
|
||
source .venv/bin/activate
|
||
```
|
||
|
||
Windows PowerShell:
|
||
|
||
```powershell
|
||
python -m venv .venv
|
||
.venv\Scripts\Activate.ps1
|
||
```
|
||
|
||
### 3. 安装 Python 依赖
|
||
|
||
```bash
|
||
pip install -r requirements.txt
|
||
```
|
||
|
||
> 如需使用 Qwen AI 方案,请确保能正常访问 DashScope 接口。
|
||
|
||
---
|
||
|
||
## 配置说明
|
||
|
||
项目通过 `pydantic-settings` 从 `.env` 文件和系统环境变量中加载配置,对应定义见 `config.py` 中 `Settings` 类。
|
||
|
||
### 1. 创建 `.env`
|
||
|
||
以 `.env.example` 为模板复制一份:
|
||
|
||
```bash
|
||
cp .env.example .env
|
||
```
|
||
|
||
然后根据实际情况修改 `.env` 中的配置项。
|
||
|
||
### 2. 关键配置项
|
||
|
||
- **FastAPI 服务**
|
||
- `HOST`:服务监听地址(默认 `0.0.0.0`)
|
||
- `PORT`:服务端口(例如 `8000` 或 `8001`)
|
||
|
||
- **微信窗口相关**
|
||
- `WECHAT_WINDOW_NAME`:微信窗口标题,默认 `WeChat`
|
||
如果你的微信窗口标题不同(例如有多语言 / 带后缀),需要改成实际名称。
|
||
|
||
- **自动化行为**
|
||
- `CLICK_PAUSE`:每次 pyautogui 操作之间的暂停秒数
|
||
- `FAILSAFE`:是否开启边角移动触发 FailSafe 保护
|
||
- `ACTION_TIMEOUT`:单步骤操作超时时间(秒)
|
||
- `MAX_RETRIES`:重试次数(用于调度和重试装饰器)
|
||
- `RETRY_BASE_DELAY`:重试基础延时(秒,配合指数退避)
|
||
|
||
- **日志与截图**
|
||
- `LOG_LEVEL`:日志级别(如 `INFO` / `DEBUG`)
|
||
- `LOG_FILE`:日志文件路径(默认 `/tmp/wechat_auto.log`)
|
||
- `SCREENSHOT_DIR`:截图保存目录(默认 `/tmp/wechat_screenshots`)
|
||
|
||
- **Qwen API(如需启用 AI 方案)**
|
||
- `DASHSCOPE_API_KEY`:DashScope 的 API Key
|
||
- 在 `.env.example` 中为占位值,请在自己的 `.env` 中改成真实密钥
|
||
- **安全提示:不要把包含真实密钥的 `.env` 提交到代码仓库**
|
||
|
||
---
|
||
|
||
## 运行服务
|
||
|
||
确保:
|
||
- 已激活虚拟环境(如有)
|
||
- `.env` 配置正确
|
||
- 微信 PC 客户端已启动并登录
|
||
- DISPLAY 环境变量可用(例如 `:0`)
|
||
|
||
### 1. 直接运行入口脚本
|
||
|
||
在 `wechat_auto` 目录下执行:
|
||
|
||
```bash
|
||
python main.py
|
||
```
|
||
|
||
或显式调用:
|
||
|
||
```bash
|
||
python -m wechat_auto.main
|
||
```
|
||
|
||
启动成功后,日志中会输出类似信息:
|
||
|
||
- 服务地址: `http://<HOST>:<PORT>`
|
||
- API 文档: `http://<HOST>:<PORT>/docs`
|
||
|
||
例如:
|
||
|
||
```text
|
||
服务地址: http://0.0.0.0:8000
|
||
API文档: http://0.0.0.0:8000/docs
|
||
```
|
||
|
||
也可以手动启动 uvicorn(等价于入口里做的事情):
|
||
|
||
```bash
|
||
uvicorn wechat_auto.main:app --host 0.0.0.0 --port 8000 --log-level info
|
||
```
|
||
|
||
---
|
||
|
||
## API 使用说明
|
||
|
||
服务启动后,可以通过 swagger 文档直接调试:
|
||
`http://<HOST>:<PORT>/docs`
|
||
|
||
### 1. 发布活动:`POST /api/publish`
|
||
|
||
- **请求体模型**:`ActivityModel`
|
||
|
||
示例 JSON:
|
||
|
||
```json
|
||
{
|
||
"title": "周末优惠活动",
|
||
"content": "全场 8 折优惠,会员额外 9 折。",
|
||
"start_time": "2026-03-10 10:00:00",
|
||
"end_time": "2026-03-15 22:00:00",
|
||
"images": ["/tmp/promotion.jpg"],
|
||
"location": "线上",
|
||
"organizer": "某某公司"
|
||
}
|
||
```
|
||
|
||
- **响应示例**:
|
||
|
||
```json
|
||
{
|
||
"code": 200,
|
||
"message": "任务已提交",
|
||
"data": {
|
||
"task_id": "xxx-uuid",
|
||
"status": "success",
|
||
"method": "pyautogui",
|
||
"error": null
|
||
}
|
||
}
|
||
```
|
||
|
||
> 说明:
|
||
> - `method` 字段指示实际使用的执行方案,可能为 `pyautogui` 或 `qwen_ai`
|
||
> - 如果任务执行失败,`status` 会是 `failed`,`error` 中包含原因
|
||
|
||
### 2. 查询任务状态:`GET /api/task/{task_id}`
|
||
|
||
路径参数:
|
||
- `task_id`:发布任务返回的 `task_id`
|
||
|
||
返回:
|
||
|
||
```json
|
||
{
|
||
"code": 200,
|
||
"data": {
|
||
"task_id": "xxx-uuid",
|
||
"status": "success",
|
||
"method": "pyautogui",
|
||
"error": null,
|
||
"created_at": "2026-03-04T12:00:00",
|
||
"updated_at": "2026-03-04T12:00:15"
|
||
}
|
||
}
|
||
```
|
||
|
||
### 3. 查询所有任务:`GET /api/tasks`
|
||
|
||
返回当前进程内维护的所有任务状态列表:
|
||
|
||
```json
|
||
{
|
||
"code": 200,
|
||
"data": [
|
||
{
|
||
"task_id": "xxx-uuid",
|
||
"status": "success",
|
||
"method": "pyautogui",
|
||
"error": null,
|
||
"created_at": "...",
|
||
"updated_at": "..."
|
||
}
|
||
]
|
||
}
|
||
```
|
||
|
||
> 注意:任务状态保存在内存中,重启进程后历史任务不会保留。
|
||
|
||
### 4. 健康检查:`GET /api/health`
|
||
|
||
简单返回服务状态:
|
||
|
||
```json
|
||
{
|
||
"status": "ok",
|
||
"service": "wechat_auto"
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
## 执行流程与架构简要说明
|
||
|
||
1. **HTTP 请求进入**
|
||
- `POST /api/publish` 接收 `ActivityModel`,调用 `TaskScheduler.publish_activity`
|
||
|
||
2. **任务调度**
|
||
- 创建 `task_id` 与 `TaskStatus`,状态置为 `running`
|
||
- 调用 `_execute_with_fallback`,执行双方案逻辑
|
||
|
||
3. **方案 1:pyautogui 执行**
|
||
- 通过 `WindowManager` 使用 `xdotool` 查找微信窗口
|
||
- 如果未找到微信窗口或无法获取几何信息,则抛出异常
|
||
- 根据预设相对坐标依次执行:
|
||
- 点击小程序入口 → 点击目标小程序 → 点击发布按钮
|
||
- 填写标题与内容 → 点击提交按钮
|
||
- 每一步执行前后会截图记录
|
||
|
||
4. **方案 2:Qwen AI 执行(备选)**
|
||
- 若 pyautogui 连续多次失败,则切换到 Qwen 方案
|
||
- 周期性地对当前屏幕截图并编码为 base64
|
||
- 将截图和任务描述一并发送给 Qwen-VL 模型
|
||
- 解析模型返回的 JSON(`action` + `params`),执行对应鼠标 / 键盘操作
|
||
- 若模型返回 `action = "done"` 则认为任务完成,否则在最大步数内继续
|
||
|
||
5. **状态更新与返回**
|
||
- 根据执行结果更新 `TaskStatus`(`success` 或 `failed`)
|
||
- 将 `status`、`method`、`error` 等字段返回给调用方
|
||
|
||
---
|
||
|
||
## 常见问题与排查建议
|
||
|
||
- **微信窗口未找到**
|
||
- 确认已登录 PC 版微信,且窗口标题与 `WECHAT_WINDOW_NAME` 配置一致
|
||
- 终端手动执行:
|
||
```bash
|
||
xdotool search --name WeChat
|
||
```
|
||
确认能返回窗口 ID。
|
||
|
||
- **截图目录 / 日志目录权限问题**
|
||
- 确保当前用户对 `SCREENSHOT_DIR` 和 `LOG_FILE` 目录有读写权限
|
||
- 如有需要,可在 `.env` 中改为当前用户有权限的路径
|
||
|
||
- **Qwen API 调用失败**
|
||
- 确认 `DASHSCOPE_API_KEY` 已正确配置且未过期
|
||
- 检查服务器是否能访问 DashScope 接口
|
||
- 查看日志中 `调用Qwen API失败` 相关报错信息
|
||
|
||
- **坐标不匹配 / 点击错位**
|
||
- 目前 pyautogui 方案使用固定相对坐标,适合「窗口大小 / DPI 固定」的场景
|
||
- 如果你使用不同分辨率或窗口布局,可能需要自行调整 `_get_activity_steps` 中的相对坐标
|
||
- 可以通过日志和截图对照,修正每一步操作的位置
|
||
|
||
---
|
||
|
||
## 开发与二次扩展建议
|
||
|
||
- 如需适配不同的小程序或表单结构:
|
||
- 可以扩展 `PyAutoGUIExecutor._get_activity_steps`,根据活动字段动态拼装步骤
|
||
- 或为不同小程序编写不同的步骤模板
|
||
|
||
- 如需增强 Qwen 方案:
|
||
- 可以在 `QwenAIExecutor._build_prompt` 中添加更详细的 UI 说明和约束
|
||
- 增加对更多 `action` 类型的支持(例如拖拽、选择框等)
|
||
|
||
- 如需持久化任务状态:
|
||
- 可以在 `TaskScheduler` 中将 `tasks` 从内存结构替换为数据库存储(如 SQLite / Redis)
|
||
|
||
---
|
||
|
||
## 免责声明
|
||
|
||
本项目涉及对桌面环境和微信客户端的自动化控制,请在遵守微信相关用户协议和所在地区法律法规的前提下使用。
|
||
如用于生产环境,请务必充分测试自动化脚本的稳定性与安全性,避免误操作造成损失。
|
||
|