Files
auto_control/README.md
T

462 lines
20 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 设备自动化后台(platform-tools)
基于 **STF(Smartphone Test Farm)+ uiautomator2 + Flask** 的 Android 多设备自动化任务执行平台。
提供 Web 管理后台,支持多设备并发任务执行、定时调度、设备分组管理、APK 批量安装、UI 元素抓取等功能。内置抖音养号任务和通用步骤任务,可扩展任意 App 的自动化操作。
---
## 目录
- [快速上手](#快速上手)
- [项目结构](#项目结构)
- [核心概念](#核心概念)
- [配置说明](#配置说明)
- [Web 管理后台](#web-管理后台)
- [任务系统](#任务系统)
- [日志系统](#日志系统)
- [常用脚本](#常用脚本)
- [常见问题](#常见问题)
- [更多文档](#更多文档)
---
## 快速上手
### 环境要求
- **Python 3.10+**(推荐 3.12)
- **OpenSTF 服务**:已部署并运行,设备已接入 STF
- **Windows / Linux / macOS**均可(adb 二进制需放对应平台版本到 `bin/adb/`)
- 设备需开启 **USB 调试**或通过 **adb 网络连接**(IP:5555)接入
### 三步启动
```bash
# 1. 安装依赖
pip install -r requirements.txt
# 2. 修改配置(STF 地址、token、adb 路径等)
# 编辑 config.py 中的 STF_URL 和 STF_TOKEN
# 3. 启动 Web 后台
python web_server.py
```
启动后访问 **http://localhost:18050/**,默认账号 `admin` / `admin123`。
> Windows 用户也可双击 `start_web.bat` 启动(自动提权 + 添加防火墙规则,支持局域网访问)。
### 验证启动
控制台看到以下日志即表示启动成功:
```
[INFO] [core.worker] 心跳看门狗已启动
[INFO] [core.tm] 从数据库加载 X 个分组, X 个任务
[INFO] [web] 管理后台: http://localhost:18050/ (admin/admin123)
[INFO] [web] 启动服务: http://localhost:18050/
```
### 首次使用流程
1. **登录后台** → 用 `admin/admin123` 登录,建议立即修改密码
2. **查看设备** → 首页"监控"Tab 自动展示 STF 设备池状态
3. **创建分组**(可选)→ "分组"Tab 按批次/项目给设备分组
4. **创建任务** → "任务"Tab 新建任务,选择任务类型、目标设备、参数、调度
5. **执行任务** → 任务列表点"立即执行",或在"监控"Tab 勾选设备批量操作
6. **查看日志** → "日志"Tab 实时查看运行日志
---
## 项目结构
```
platform-tools/
├── config.py # 根配置(STF地址/token、adb路径、web端口)
├── web_server.py # Flask 入口(API + 登录 + 单页应用)
├── main.py # 命令行入口(单设备跑一次,不走 web)
├── requirements.txt # Python 依赖清单
├── start_web.bat # Windows 一键启动(提权 + 防火墙)
│
├── core/ # 核心基础设施层
│ ├── logger.py # 统一日志(分文件、10MB 滚动)
│ ├── stf_client.py # STF REST API 封装(占用/释放/远程连接)
│ ├── adb_helper.py # adb 命令封装(全局锁,绝不 kill-server)
│ ├── device_worker.py # BaseWorker 基类 + STFDevice + 心跳看门狗
│ ├── task_manager.py # TaskManager 调度器 + 前台App扫描器
│ ├── u2_helper.py # uiautomator2 通用辅助函数
│ ├── uiauto_helper.py # uiautodev 元素抓取客户端
│ ├── apk_manager.py # APK 上传/解析/批量安装
│ ├── models.py # SQLAlchemy 数据模型 + 数据库初始化
│ └── actions/
│ ├── __init__.py # 全局 Action 框架导出
│ └── base.py # BaseAction 基类 + 注册机制
│
├── tasks/ # 任务定义层(每个 App 一个子包)
│ ├── __init__.py # 全局任务注册表
│ ├── base.py # BaseTask 基类 + @register_task 装饰器
│ ├── douyin/ # 抖音养号任务
│ │ ├── __init__.py
│ │ ├── task.py # DEFAULT_PARAMS + Worker + Task
│ │ └── actions/ # 抖音专属操作(点赞等)
│ │ ├── __init__.py
│ │ ├── base.py # 抖音 ACTIONS 注册表
│ │ └── like.py # 点赞操作(找红心/双击回退)
│ └── generic/ # 通用步骤任务(可视化编辑器编排)
│ ├── __init__.py
│ └── task.py # 步骤执行引擎(open_app/click/swipe/loop...)
│
├── templates/admin/ # 前端页面
│ ├── monitor.html # 单页应用(监控/任务/分组/日志/用户 5 Tab)
│ └── login.html # 登录页
│
├── static/admin/
│ ├── custom.css # 自定义样式
│ └── monitor.js # 前端 JS(已从 monitor.html 拆分)
│
├── data/ # 运行时数据
│ ├── users.db # SQLite(用户/分组/任务/自定义动作/APK记录)
│ └── apks/ # 上传的 APK 文件存储
│
├── logs/ # 日志文件(自动生成,10MB 滚动保留 5 份)
│ ├── core.log # 核心程序日志
│ ├── task.log # 任务执行日志
│ ├── web.log # Web 请求日志
│ └── action.log # 操作执行日志
│
├── bin/adb/ # adb 可执行文件(Windows: adb.exe + dll)
├── scripts/ # 实用脚本
│ ├── cleanup.py # 清理 STF 残留设备占用
│ └── pack.py # 打包项目为 zip(排除运行时产物)
└── doc/ # 项目文档
├── TASK_DEV.md # 任务开发指南(新增 App 任务模板)
├── ARCHITECTURE.md # 架构详解
├── DEPLOY.md # 部署指南
└── API.md # API 接口文档
```
---
## 核心概念
### 设备生命周期
```
STF occupy(占用设备)
↓
STF remoteConnect(建立远程 ADB 隧道)
↓
adb connect(连接远程设备)
↓
u2.connect(连接 uiautomator2,自动推送 atx-agent)
↓
Worker.run_task(执行业务逻辑)
↓
adb disconnect + STF release(释放设备)
```
> 直连模式:当设备 serial 为 `IP:5555` 格式时,直接 adb connect,不走 STF 桥接,更稳定。
### Worker — 单设备执行线程
每台设备对应一个 `Worker` 线程,继承 `BaseWorker`(`core/device_worker.py`)。基类已封装:
- STF 设备占用/释放(try/finally 保证释放)
- adb 连接 + u2.connect(带 30 秒超时保护)
- 状态上报(实时推送到前端监控大屏)
- 异常捕获(设备离线不重试,其他异常按策略重试)
- stop 停止信号(循环里检查 `self.stopped()`)
- 心跳看门狗(120 秒无心跳自动标记卡死)
子类只需实现 `run_task(d)` 方法专注业务逻辑。
### TaskType — 任务类型
| 任务类型 | task_type | 说明 |
|---------|-----------|------|
| 抖音养号 | `douyin_nurture` | 自动看视频 + 随机点赞,被退出自动重连 |
| 通用步骤 | `generic_steps` | 可视化步骤编辑器编排流程,支持循环/点击/滑动等 |
### TaskJob — 任务计划
一个 TaskJob 描述"什么时候、在哪些设备上、用什么参数执行什么任务":
| 字段 | 说明 | 示例 |
|------|------|------|
| `task_type` | 任务类型 | `"douyin_nurture"` |
| `target` | 目标设备 | `{"mode": "all"}` 或 `{"mode": "group", "group_name": "A组"}` 或 `{"mode": "serial", "serial": "192.168.1.100:5555"}` |
| `params` | 任务参数(与默认值深合并) | `{"watch_count": 50, "actions": {"like": {"params": {"rate": 0.5}}}}` |
| `schedule` | 调度策略 | `{"mode": "once"}` 或 `{"mode": "cron", "cron": "0 9 * * *"}` 或 `{"mode": "cron_stop", "cron": "0 9 * * *", "stop_cron": "0 18 * * *"}` |
| `retry` | 重试策略 | `{"max_attempts": 3, "delay": 60}` |
| `enabled` | 是否启用 | `true` |
### 调度模式
| 模式 | 行为 |
|------|------|
| `once` | 手动执行(前端点"立即执行") |
| `cron` | 定时启动:到 cron 时间点自动启动所有目标设备 |
| `cron_stop` | 定时启停:启动 cron 到点启动,停止 cron 到点停止本任务 worker |
**简单设置**:编辑任务时选"定时启动/定时启动+停止"后,频率用下拉选择——每天(选时间)、每小时(整点)、每隔 N 小时、每周(选星期+时间)——cron 表达式自动生成,不需要懂 cron 语法。老手可在"自定义 cron(高级)"里直接填。
cron 表达式为标准 5 段格式:`分 时 日 月 周`(如 `0 9 * * *` = 每天 9:00,`0 */2 * * *` = 每 2 小时整点,周 `0`/`7` 均为周日)
**运行窗口**:任务可勾选"启用运行窗口",设置每天允许运行的时间段(如 `21:00-09:00` = 晚 9 点到次日早 9 点,支持跨午夜)。窗口外**定时触发和手动执行都不会启动**(手动执行会提示"当前不在运行窗口内")。典型用法:`每小时`定时 + 窗口 `09:00-21:00`,即只在白天每小时跑一次。
### 设备分组
设备分组存于 SQLite,便于按批次/项目分组下发任务。一个 Job 指定 `target.mode="group"` 时,调度器展开为组内全部设备。
### 进度上报
Worker 通过 `self.set_progress()` 上报通用进度字段,前端统一解析展示:
```python
self.set_progress(done=5, total=80, unit="视频",
action_counts={"like": 3})
```
前端展示:进度条 + `5/80 视频` + `点赞 3` 徽章。
---
## 配置说明
所有核心配置在 [config.py](file:///d:/platform-tools/config.py),**任务参数不放在这里**(放各自 `tasks/xxx.py` 顶部)。
| 配置项 | 默认值 | 说明 |
|-------|--------|------|
| `STF_URL` | `http://192.168.20.220:7100` | OpenSTF 服务地址 |
| `STF_TOKEN` | (需替换) | STF API Token,在 STF 个人设置里生成 |
| `ADB_PATH` | 自动识别 | adb 二进制路径,自动区分 Windows/Linux |
| `WEB_HOST` | `0.0.0.0` | Web 监听地址(0.0.0.0 支持局域网访问) |
| `WEB_PORT` | `18050` | Web 端口(避开 Windows 动态端口范围) |
| `DATA_DIR` | `data/` | 持久化数据目录 |
| `APK_DIR` | `data/apks/` | APK 文件存储目录 |
**必须修改的配置**:`STF_URL` 和 `STF_TOKEN`,改成你自己的 STF 服务地址和 Token。
---
## Web 管理后台
### 页面结构
单页应用(`templates/admin/monitor.html`),6 个 Tab:
| Tab | 功能 | 可见性 |
|-----|------|--------|
| 监控 | 设备状态大屏:在线/离线、运行任务、当前动作、进度条、前台 App、截图、停止 | 所有登录用户(设备操作按钮需"设备控制"权限) |
| 任务 | 任务计划 CRUD:新建/编辑/删除/启用停用/立即执行 | 所有登录用户可看,写操作需"任务管理"权限 |
| 应用 | APK 上传/安装/删除 | 需"应用管理"权限 |
| 分组 | 设备分组管理:创建/编辑/删除分组 | 所有登录用户可看,写操作需"任务管理"权限 |
| 日志 | 实时日志查看:按模块切换(core/task/web/action) | 需"日志查看"权限 |
| 用户 | 用户管理:创建/删除/修改密码/分配权限 | 仅管理员 |
| 维护 | STF 容器一键重启、adb 远程终端(设备快捷选择/自动 `-s`、卸载 STF agent、重连设备)、Tailscale 管理(设备列表/改名/授权/密钥不过期/生成 auth key) | 仅管理员 |
### 用户与权限
默认账号 `admin` / `admin123`(管理员,拥有全部权限)。管理员可在"用户"Tab 创建普通用户并分配权限:
| 权限位 | 说明 | 覆盖功能 |
|--------|------|---------|
| 任务管理 `tasks` | 任务/自定义动作/分组的增删改、启停、立即执行 | 任务 Tab、分组 Tab 的写操作 |
| 设备控制 `devices` | 停止设备、释放占用、清除异常、前台扫描、元素抓取 | 监控 Tab 的设备操作按钮、步骤编辑器的"抓取元素" |
| 应用管理 `apks` | APK 上传、安装、删除 | 应用 Tab |
| 日志查看 `logs` | 日志页 | 日志 Tab |
规则:
- **管理员拥有全部权限**,不受权限位限制
- 查看类接口(设备/任务/分组列表、状态、截图)所有登录用户可用
- **用户管理仅管理员可用**;不能删除/取消最后一个管理员
- 无权限的 tab 和按钮在界面上自动隐藏(后端同样拦截,返回 403)
### 前台 App 扫描
监控页"扫描前台App"按钮,获取所有设备当前前台 App。**不打扰设备**:
| 设备状态 | 处理方式 |
|---------|---------|
| worker 运行中 | 复用已有 ADB 连接查询 |
| 自己占用 | 通过 STF remoteConnect 隧道查询(不 occupy/release) |
| 完全空闲 | 返回"空闲"(不主动 adb connect,避免 STF 误判离线) |
| 他人占用 | 标记"(他人占用)" |
### 截图功能
监控页每台设备可查看实时截图。用 `adb exec-out screencap -p`,只读操作,**任务运行中也能安全调用**(不抢占 u2 的 atx-agent 通道)。
### 元素抓取
任务编辑器的"抓取元素"按钮可拉取设备当前 UI 元素树,点击元素一键回填选择器。依赖本地运行的 uiautodev 服务(端口 20242),`web_server.py` 启动时会自动拉起。
---
## 任务系统
### 抖音养号(douyin_nurture)
自动观看抖音视频,按配置随机执行点赞操作。参数(在 `tasks/douyin/task.py` 顶部):
| 参数 | 默认值 | 说明 |
|------|--------|------|
| `watch_count` | 80 | 观看视频数量(0=不限,靠时长停止) |
| `watch_min` / `watch_max` | 5.0 / 35.0 | 单视频观看时长范围(秒) |
| `max_duration` | 0 | 最大运行时长(秒),0=不限时 |
| `swipe_min` / `swipe_max` | 0.25 / 0.50 | 上滑手势时长范围(秒) |
| `gap_min` / `gap_max` | 1.0 / 3.0 | 视频间隔时长范围(秒) |
| `actions.like.enabled` | true | 是否启用点赞 |
| `actions.like.params.rate` | 0.3 | 点赞概率(0~1) |
| `actions.like.params.method` | by_element | 点赞方式:by_element(找红心) / double_click(双击) |
终止条件(哪个先到就停):`watch_count` 数量到 / `max_duration` 时长到 / 手动停止。
### 通用步骤(generic_steps)
通过可视化步骤编辑器编排任务流程,worker 按步骤顺序执行。支持的步骤类型:
| 步骤类型 | 说明 |
|---------|------|
| `open_app` | 启动 App(指定包名,可选等待首页) |
| `stop_app` | 强制结束 App(am force-stop,清后台,下次打开冷启动) |
| `screen_on` | 亮屏(息屏时唤醒并滑动解锁) |
| `screen_off` | 息屏 |
| `keep_screen` | 保持亮屏/恢复自动息屏(充电时屏幕常亮,适合长任务) |
| `key_event` | 按键:返回/Home/回车/菜单等(退出评论、返回上一页) |
| `swipe` | 滑动(上/下/左/右,可配置时长) |
| `swipe_until` | 滑动直到元素出现(最多 N 次,可选找到后点击) |
| `click_xy` | 点击坐标(屏幕百分比,无选择器时兜底) |
| `long_click` | 长按元素(选择器 + 时长) |
| `wait_el` | 等待元素出现(条件等待,替代固定时长) |
| `input_text` | 输入文字(随机候选/指定文字,可选输入前先清空) |
| `click` | 点击元素(支持 xpath/description/resourceId/text 选择器) |
| `input_text` | 输入文字(随机候选/固定文字) |
| `wait` | 等待(可配置时长范围) |
| `loop` | 循环块(含子步骤,可配置循环次数) |
| `group` | 动作组(含子步骤,按序执行一次) |
### 新增 App 任务
参照 `tasks/douyin/` 结构,6 步即可新增一个 App 任务,详见 [doc/TASK_DEV.md](doc/TASK_DEV.md)。
---
## 日志系统
日志按模块分文件,自动滚动(10MB 一份,保留 5 份历史):
| 文件 | 模块前缀 | 内容 |
|------|---------|------|
| `logs/core.log` | `core.*` | STF/adb/worker/task_manager 核心程序 |
| `logs/task.log` | `task.*` | 任务执行(worker 业务逻辑) |
| `logs/web.log` | `web.*` | Web 请求/管理 |
| `logs/action.log` | `action.*` | 操作执行(点赞/评论等) |
使用方式:
```python
from core.logger import get_logger
log = get_logger("task.douyin") # 写入 task.log
log.info(f"[{self.serial}] 开始任务")
```
Web 后台"日志"Tab 可实时查看各模块日志。
---
## 常用脚本
| 脚本 | 用途 |
|------|------|
| `python web_server.py` | 启动 Web 管理后台 |
| `python main.py` | 命令行跑一次抖音养号(单设备,不走 web) |
| `python scripts/cleanup.py` | 清理 STF 残留设备占用(强杀进程后用) |
| `python scripts/pack.py` | 打包项目为 zip(排除日志/数据库/APK) |
| `start_web.bat` | Windows 一键启动(提权 + 防火墙规则) |
---
## 常见问题
### 端口被占用(WinError 10013/10048)
`web_server.py` 会自动重试候选端口(原端口 → 127.0.0.1:原端口 → 127.0.0.1:原端口+1~+5)。如果全部失败,检查 Windows 动态端口范围:
```bash
netsh interface ipv4 show excludedportrange protocol=tcp
```
修改 `config.py` 中的 `WEB_PORT` 到一个不在排除范围内的端口。
### STF 设备显示离线但实际在线
STF 状态有缓存,`present=True` 不代表设备真在线。用"扫描前台App"按钮复测,或直接尝试执行任务。
### 设备一直被占用无法释放
运行清理脚本:
```bash
python scripts/cleanup.py
```
会释放当前账户占用的所有设备。
### u2.connect 卡死
`u2.connect()` 在 atx-agent 无响应时会永久 hang。基类已用 `ThreadPoolExecutor + 30 秒超时` 保护,超时自动放弃。如果频繁超时,检查设备 atx-agent 是否正常(重启设备或重新推送 atx-agent)。
### 任务运行中看门狗误杀
看门狗 120 秒无心跳会标记卡死。长耗时操作(如长视频等待)需在循环内周期性调用 `self.heartbeat()`。
### 中文输入失败
uiautomator2 默认 IME 不支持中文,需切到 FastInput 输入法:
```python
try:
d.set_fastinput_ime(True)
d.send_keys("中文内容")
finally:
d.set_fastinput_ime(False)
```
### 修改 core/ 后不生效
`web_server.py` 以 `debug=False` 运行,Python 不会热重载。修改 `core/` 目录下的文件后**必须重启 web_server 进程**。
### 元素抓取按钮不可用
依赖 uiautodev 服务(端口 20242)。确保已安装 `pip install uiautodev`。`web_server.py` 启动时会自动拉起该服务。
---
## 更多文档
| 文档 | 内容 |
|------|------|
| [doc/DEVELOPMENT.md](doc/DEVELOPMENT.md) | **开发手册**:开发流程、git 工作流、技术红线、环境、本地开发 |
| [doc/TASK_DEV.md](doc/TASK_DEV.md) | 任务开发指南(新增 App 任务的完整模板和规范) |
| [doc/ARCHITECTURE.md](doc/ARCHITECTURE.md) | 架构详解(分层设计、数据流、关键设计决策) |
| [doc/DEPLOY.md](doc/DEPLOY.md) | 部署指南(环境准备、STF 配置、生产部署) |
| [doc/API.md](doc/API.md) | API 接口文档(全部 HTTP 接口说明) |
> **注意**:所有 git 操作(含 push 到 dev)都需负责人确认后才能执行,详见开发手册。修改 `core/`、`tasks/`、`templates/` 后需重启服务/强刷浏览器才生效。
---
## 技术栈
| 组件 | 用途 |
|------|------|
| Flask + Flask-Login | Web 后台 + 用户认证 |
| Flask-SQLAlchemy | SQLite 数据持久化 |
| APScheduler | 定时任务调度 |
| uiautomator2 | Android UI 自动化 |
| uiautodev | UI 元素抓取(步骤编辑器"抓取元素") |
| pyaxmlparser | APK 元信息解析 |
| OpenSTF | 设备农场管理(占用/释放/远程 ADB) |