feat: 新增通用步骤任务+元素抓取,完善全部项目文档
- 新增 generic_steps 通用步骤任务(可视化步骤编辑器编排流程,支持 open_app/click/swipe/input_text/wait/loop/group) - 新增 core/uiauto_helper.py 封装 uiautodev 元素抓取客户端 - web_server 新增元素抓取/截图/设备列表等 API - monitor.html 新增步骤编辑器、元素抓取模态框、独立关闭逻辑 - 新增 README.md 项目总览(快速上手/架构/配置/FAQ) - 新增 doc/ARCHITECTURE.md 架构详解、doc/DEPLOY.md 部署指南、doc/API.md 接口文档 - 修复 doc/TASK_DEV.md:移除已删除的 comment 引用,补充 generic 包,更新注册示例 - .gitignore 忽略 .claude/ 工具产物
This commit is contained in:
+33
-61
@@ -42,16 +42,18 @@ platform-tools/
|
||||
│ ├── __init__.py # create_action_registry / register_action / should_trigger
|
||||
│ └── base.py # BaseAction 全局基类
|
||||
├── tasks/ # 任务定义层
|
||||
│ ├── __init__.py # 全局 _TASK_TYPES 注册表
|
||||
│ ├── __init__.py # 全局 _TASK_TYPES 注册表(import 各任务包触发注册)
|
||||
│ ├── base.py # BaseTask 基类
|
||||
│ └── douyin/ # 抖音养号(示例)
|
||||
│ ├── douyin/ # 抖音养号(示例)
|
||||
│ │ ├── __init__.py
|
||||
│ │ ├── task.py # DEFAULT_PARAMS + Worker + Task + @register_task
|
||||
│ │ └── actions/
|
||||
│ │ ├── __init__.py # 先 from .base import ACTIONS,再 from . import like
|
||||
│ │ ├── base.py # ACTIONS = create_action_registry()
|
||||
│ │ └── like.py # @register_action(ACTIONS) LikeAction
|
||||
│ └── generic/ # 通用步骤任务(可视化步骤编辑器编排)
|
||||
│ ├── __init__.py
|
||||
│ ├── task.py # DEFAULT_PARAMS + Worker + Task + @register_task
|
||||
│ └── actions/
|
||||
│ ├── __init__.py # 先 from .base import ACTIONS,再 from . import like, comment
|
||||
│ ├── base.py # ACTIONS = create_action_registry()
|
||||
│ ├── like.py # @register_action(ACTIONS) LikeAction
|
||||
│ └── comment.py # @register_action(ACTIONS) CommentAction
|
||||
│ └── task.py # STEP_TYPES + Worker + Task(按 steps 顺序执行)
|
||||
├── templates/admin/
|
||||
│ ├── monitor.html # 单页应用(5 Tab,纯前端渲染)
|
||||
│ └── login.html # 登录页
|
||||
@@ -135,7 +137,7 @@ def get_task_class(task_type):
|
||||
|
||||
### 2.5 Action — 操作
|
||||
|
||||
`Action` 是任务循环里执行的"原子操作"(点赞 / 评论 / 滑动 / 关注)。每个 app 有**独立的 Action 注册表**(通过 `create_action_registry()` 创建),互不污染。全局基类 `core/actions/base.py::BaseAction` 提供通用能力。
|
||||
`Action` 是任务循环里执行的"原子操作"(点赞 / 关注 / 滑动等)。每个 app 有**独立的 Action 注册表**(通过 `create_action_registry()` 创建),互不污染。全局基类 `core/actions/base.py::BaseAction` 提供通用能力。
|
||||
|
||||
### 2.6 进度上报(通用,适配任意 app)
|
||||
|
||||
@@ -326,7 +328,7 @@ from .base import (
|
||||
|
||||
# 再导入各操作模块,触发 @register_action(ACTIONS) 注册
|
||||
from . import like # noqa: F401
|
||||
# from . import comment # 新增 action 在此 import
|
||||
# 新增 action 在此 import,例如:from . import follow
|
||||
|
||||
__all__ = [
|
||||
"BaseAction", "register_action", "create_action_registry",
|
||||
@@ -342,7 +344,7 @@ __all__ = [
|
||||
"""快手养号任务定义。
|
||||
|
||||
本文件自包含所有快手养号参数,不依赖 core 的业务配置。
|
||||
快手专属操作(点赞/评论)在 actions/ 子包里,xpath 只适用于快手。
|
||||
快手专属操作(点赞/关注等)在 actions/ 子包里,xpath 只适用于快手。
|
||||
"""
|
||||
import time
|
||||
import random
|
||||
@@ -509,8 +511,9 @@ from . import task # noqa: F401 触发 @register_task 注册
|
||||
```python
|
||||
# tasks/__init__.py
|
||||
from .base import BaseTask, register_task, list_task_types, get_task_class
|
||||
from . import douyin # noqa: F401
|
||||
from . import kuaishou # noqa: F401 ← 新增这一行
|
||||
from .douyin import task # noqa: F401
|
||||
from .generic import task # noqa: F401
|
||||
from .kuaishou import task # noqa: F401 ← 新增这一行
|
||||
```
|
||||
|
||||
完成。重启 web 后,前端任务类型下拉自动出现 `kuaishou_nurture`。
|
||||
@@ -672,8 +675,10 @@ def run_task(self, d):
|
||||
|
||||
### 5.3 完整 Action 模板
|
||||
|
||||
下面以"关注"操作为例,展示一个完整 Action 的写法(多策略定位 + 概率触发 + 异常兜底):
|
||||
|
||||
```python
|
||||
# tasks/xxx/actions/comment.py
|
||||
# tasks/xxx/actions/follow.py
|
||||
import time
|
||||
import random
|
||||
|
||||
@@ -681,17 +686,16 @@ from core.actions import BaseAction, register_action, should_trigger
|
||||
from core.logger import get_logger
|
||||
from . import ACTIONS # 从 __init__ 导入本 app 注册表
|
||||
|
||||
_log = get_logger("action.xxx.comment")
|
||||
_log = get_logger("action.xxx.follow")
|
||||
|
||||
|
||||
@register_action(ACTIONS)
|
||||
class CommentAction(BaseAction):
|
||||
action_type = "comment"
|
||||
name = "评论"
|
||||
description = "看完视频后随机发一条评论"
|
||||
class FollowAction(BaseAction):
|
||||
action_type = "follow"
|
||||
name = "关注"
|
||||
description = "看完视频后随机关注作者"
|
||||
default_params = {
|
||||
"rate": 0.1,
|
||||
"texts": ["不错", "666", "学到了"],
|
||||
}
|
||||
|
||||
def execute(self, d, params, worker):
|
||||
@@ -699,51 +703,19 @@ class CommentAction(BaseAction):
|
||||
if not should_trigger(rate):
|
||||
return False
|
||||
|
||||
texts = params.get("texts") or ["不错"]
|
||||
text = random.choice(texts)
|
||||
|
||||
# 定位评论按钮(多策略组合,失败回退)
|
||||
for desc in ("评论", "未评论", "comment"):
|
||||
# 定位关注按钮(多策略组合,失败回退)
|
||||
for desc in ("关注", "未关注", "follow"):
|
||||
el = d(description=desc)
|
||||
if el.exists:
|
||||
el.click()
|
||||
break
|
||||
else:
|
||||
_log.info("未找到评论按钮")
|
||||
_log.info("未找到关注按钮")
|
||||
return False
|
||||
|
||||
time.sleep(1.5)
|
||||
|
||||
# 找输入框
|
||||
for rid in ("com.xxx:id/comment_input",):
|
||||
el = d(resourceId=rid)
|
||||
if el.exists:
|
||||
el.click()
|
||||
break
|
||||
else:
|
||||
d.press("back")
|
||||
return False
|
||||
|
||||
time.sleep(0.8)
|
||||
|
||||
# 输入文本(中文需切输入法)
|
||||
try:
|
||||
d.set_fastinput_ime(True)
|
||||
except Exception:
|
||||
pass
|
||||
d.send_keys(text)
|
||||
time.sleep(0.6)
|
||||
|
||||
# 发送
|
||||
el = d(text="发送")
|
||||
if el.exists:
|
||||
el.click()
|
||||
_log.info(f"评论成功: {text}")
|
||||
time.sleep(1.0)
|
||||
d.press("back")
|
||||
return True
|
||||
d.press("back")
|
||||
return False
|
||||
time.sleep(1.0)
|
||||
_log.info("关注成功")
|
||||
return True
|
||||
```
|
||||
|
||||
> **返回值约定**:`True`=成功执行;`False`=主动跳过(概率未中、元素不存在等);抛异常=执行失败,由 Worker 捕获并记录。
|
||||
@@ -931,10 +903,10 @@ d.xpath('//FrameLayout[2]/LinearLayout[1]/TextView[3]').click()
|
||||
```python
|
||||
# tasks/xxx/actions/__init__.py
|
||||
from .base import ACTIONS, ... # 1. 先建注册表
|
||||
from . import like, comment # 2. 再导入各 action,触发 @register_action
|
||||
from . import like # 2. 再导入各 action,触发 @register_action
|
||||
```
|
||||
|
||||
`tasks/__init__.py` 同理:先 `from .base import BaseTask`,再 `from . import douyin`。
|
||||
`tasks/__init__.py` 同理:先 `from .base import BaseTask`,再 `from . import douyin`、`from . import generic`。
|
||||
|
||||
### 10.2 中文输入
|
||||
|
||||
@@ -1036,7 +1008,7 @@ self.set_progress(videos_watched=5, round_idx=3)
|
||||
- [ ] `tasks/<app>/actions/__init__.py` 先 `from .base import ACTIONS` 再导入各 action
|
||||
- [ ] 每个 Action 有 `action_type` / `name` / `default_params` / `execute`,返回 `True/False`
|
||||
- [ ] `Task.create_worker` 做 actions 参数级深合并(照抄抖音模板)
|
||||
- [ ] `tasks/__init__.py` 已 `from . import <app>` 注册
|
||||
- [ ] `tasks/__init__.py` 已 `from .<app> import task` 注册
|
||||
- [ ] 日志用 `get_logger("task.<app>")` / `get_logger("action.<app>.<name>")`,无 `print`
|
||||
- [ ] 定位元素优先 `description` / `descriptionContains`,resourceId 多候选,xpath 用相对定位
|
||||
- [ ] 中文输入用 `set_fastinput_ime`,加 try/except
|
||||
|
||||
Reference in New Issue
Block a user