tasks/douyin/(task_type=douyin_nurture)整体删除,唯一任务类型是 generic_steps。 所有默认值/文档/接口示例同步改成 generic_steps: - tasks/:删 douyin 包;__init__ 只注册 generic;base.py/generic 注释改为照 generic 抄 - 默认值:core/models.py(列默认 + 旧 JSON 迁移默认)、core/task_manager.py TaskJob、 web/tasks_api.py 建任务默认、static/admin/tasks.js 新建任务默认 - core/task_manager.py:去掉 douyin 专属的"清理废弃 comment 参数"迁移块,改为**启动时告警** 仍残留已删类型的任务(只告警不改数据);run_job_now 对已删类型直接返回明确错误, 不再"报已触发、线程里静默失败" - 清理残留:douyin_running 状态位(无任何读取方)、core/__init__、core/actions/*、 core/logger.py 注释里的抖音示例 - 文档:README(特性/目录树/类型表/参数表/示例)、TASK_DEV(目录树/注册说明/模板引用)、 ARCHITECTURE(注册示例/action 注册表示例)、API.md(task_types 与任务 JSON 示例)、 AI_TASK_GEN(P1 去掉 douyin 预设)、DEVELOPMENT - 注:示例里"抖音"作为**App 名**(MCP 列应用、AI 建任务的需求举例)保留,与任务类型无关 自测(全部通过):类型列表只剩 generic_steps;建任务不传类型默认 generic_steps;传 douyin_nurture 被 400 拒;库里塞残留旧类型任务 → 启动日志告警 + 执行返回明确错误 + 不自动删用户数据;前端新建任务下拉 1 项且默认选中、界面建任务成功;监控页卡片两个按钮 + 覆盖设备正常。临时任务/数据验完已清理,任务集合复原。
121 lines
3.9 KiB
Python
121 lines
3.9 KiB
Python
"""Action 基类与注册机制(全局通用,所有 app 任务共享接口)。
|
||
|
||
设计要点:
|
||
1. BaseAction 定义统一接口:execute(d, params, worker) -> bool
|
||
2. 每个 app 任务有自己的注册表(create_action_registry),
|
||
互不污染——A 应用的 like 和 B 应用的 like 各注册各的。
|
||
3. 概率触发、状态上报等通用逻辑放这里,子类不重复写。
|
||
4. ActionContext 封装执行上下文,方便扩展(传 d/worker/进度/计数器)。
|
||
|
||
新增 app 专属操作步骤(在 tasks/<app>/actions/ 下新建,参考该包已有实现):
|
||
|
||
1. 在 tasks/<app>/actions/ 下新建 my_action.py
|
||
2. 写一个 BaseAction 子类,用 @register_action 装饰,实现 execute:
|
||
|
||
from core.actions import BaseAction, register_action, should_trigger
|
||
from . import ACTIONS # 本 app 的注册表
|
||
|
||
@register_action(ACTIONS)
|
||
class MyAction(BaseAction):
|
||
action_type = "my_action"
|
||
name = "我的操作"
|
||
description = "做什么"
|
||
default_params = {"rate": 0.5}
|
||
|
||
def execute(self, d, params, worker):
|
||
if not should_trigger(params["rate"]):
|
||
return False
|
||
# ... 用 d(xpath=...) / d(text=...) 操作
|
||
return True
|
||
|
||
3. 在 tasks/<app>/actions/__init__.py 加:from . import my_action
|
||
|
||
做完前端自动出现该操作的可勾选项。
|
||
"""
|
||
import random
|
||
|
||
from core.logger import get_logger
|
||
|
||
_log = get_logger("action")
|
||
|
||
|
||
class ActionContext:
|
||
"""操作执行上下文,封装给 Action 用的所有依赖。
|
||
|
||
现在只是简单封装,后续可扩展(如传任务参数、设备信息、进度回调)。
|
||
Action 子类通过 worker 间接访问,或直接用 execute 的 d 参数。
|
||
"""
|
||
|
||
def __init__(self, worker=None, params=None):
|
||
self.worker = worker
|
||
self.params = params or {}
|
||
|
||
|
||
class BaseAction:
|
||
"""所有操作的基类。子类实现 execute。
|
||
|
||
约定:
|
||
execute 返回 True = 执行成功(会计入 action_counts)
|
||
execute 返回 False = 跳过/失败(不影响主流程,不抛异常)
|
||
execute 内部异常由调用方捕获,不影响其他操作和主循环
|
||
"""
|
||
action_type = "base"
|
||
name = "基础操作"
|
||
description = ""
|
||
default_params = {}
|
||
|
||
def execute(self, d, params, worker):
|
||
"""执行操作。
|
||
|
||
d: u2.Device — 已连好的设备
|
||
params: 本操作的参数 dict(已合并默认值)
|
||
worker: Worker 实例,可调 worker.set_action() 上报状态
|
||
返回 True=执行成功,False=跳过/失败。
|
||
"""
|
||
raise NotImplementedError
|
||
|
||
def __repr__(self):
|
||
return f"<{self.__class__.__name__}({self.action_type})>"
|
||
|
||
|
||
def should_trigger(rate):
|
||
"""按概率决定是否触发(rate 是 0~1 的概率)。"""
|
||
return random.random() < rate
|
||
|
||
|
||
def register_action(registry):
|
||
"""装饰器:把 Action 类注册到指定注册表。
|
||
|
||
用法:
|
||
ACTIONS = create_action_registry()
|
||
@register_action(ACTIONS)
|
||
class MyAction(BaseAction): ...
|
||
|
||
每个 app 任务有自己的 ACTIONS 注册表,互不污染。
|
||
"""
|
||
def _decorator(action_cls):
|
||
registry[action_cls.action_type] = action_cls
|
||
_log.debug(f"注册操作: {action_cls.action_type} -> {action_cls.__name__}")
|
||
return action_cls
|
||
return _decorator
|
||
|
||
|
||
def create_action_registry():
|
||
"""创建一个独立的操作注册表(每个 app 任务一个)。
|
||
|
||
返回 dict: action_type -> ActionClass
|
||
"""
|
||
return {}
|
||
|
||
|
||
def list_actions(registry):
|
||
"""返回注册表中所有操作的元信息(供前端展示)。"""
|
||
return [{"action_type": a.action_type, "name": a.name,
|
||
"description": a.description,
|
||
"default_params": a.default_params} for a in registry.values()]
|
||
|
||
|
||
def get_action(registry, action_type):
|
||
"""按 action_type 取操作类。"""
|
||
return registry.get(action_type)
|