All checks were successful
技能自动化发布 / release (push) Successful in 4s
Co-authored-by: Cursor <cursoragent@cursor.com>
110 lines
4.5 KiB
Python
110 lines
4.5 KiB
Python
"""仿真 RPA 档位:演示 RpaVideoSession + launch_persistent_browser(需浏览器,默认开发档)。"""
|
||
|
||
from __future__ import annotations
|
||
|
||
import asyncio
|
||
import os
|
||
from datetime import datetime
|
||
|
||
from jiangchang_skill_core import config
|
||
from jiangchang_skill_core.rpa import anti_detect, artifacts, errors, launch_persistent_browser
|
||
from jiangchang_skill_core.rpa.human_login import wait_for_captcha_pass
|
||
from jiangchang_skill_core.rpa.video_session import RpaVideoSession
|
||
|
||
from util.constants import SKILL_SLUG
|
||
from util.runtime_paths import get_skill_data_dir, get_skill_root
|
||
|
||
from .base import AdapterBase, ExampleItem, ExampleResult
|
||
|
||
|
||
class SimRpaAdapter(AdapterBase):
|
||
name = "sim_rpa"
|
||
|
||
def do_batch(self, target: str, items: list[ExampleItem]) -> ExampleResult:
|
||
return asyncio.run(self._do_batch_async(target, items))
|
||
|
||
async def _do_batch_async(self, target: str, items: list[ExampleItem]) -> ExampleResult:
|
||
example_path = os.path.join(get_skill_root(), ".env.example")
|
||
config.ensure_env_file(SKILL_SLUG, example_path)
|
||
|
||
url = config.get("TARGET_BASE_URL") or target or "https://example.com"
|
||
batch_id = f"sim_{datetime.now().strftime('%Y%m%d_%H%M%S')}"
|
||
data_dir = get_skill_data_dir()
|
||
profile_dir = os.path.join(data_dir, "browser-profile")
|
||
os.makedirs(profile_dir, exist_ok=True)
|
||
|
||
try:
|
||
from playwright.async_api import async_playwright
|
||
except ImportError:
|
||
return ExampleResult(
|
||
ok=False,
|
||
serial_no=None,
|
||
error_msg="playwright not installed",
|
||
artifacts={},
|
||
)
|
||
|
||
artifact_paths: dict = {}
|
||
async with RpaVideoSession(
|
||
skill_slug=SKILL_SLUG,
|
||
skill_data_dir=data_dir,
|
||
batch_id=batch_id,
|
||
title="技能仿真演示",
|
||
) as video:
|
||
video.step("正在打开目标页面")
|
||
try:
|
||
async with async_playwright() as pw:
|
||
context = await launch_persistent_browser(
|
||
pw,
|
||
profile_dir=profile_dir,
|
||
headless=config.get_bool("OPENCLAW_BROWSER_HEADLESS"),
|
||
record_video_dir=video.record_video_dir,
|
||
)
|
||
page = context.pages[0] if context.pages else await context.new_page()
|
||
await page.goto(url, wait_until="domcontentloaded", timeout=60_000)
|
||
await anti_detect.human_mouse_wiggle(page)
|
||
|
||
search = page.locator('input[type="search"], input[name="q"]').first
|
||
if await search.count() > 0:
|
||
demo_text = items[0].label if items else "openclaw-demo"
|
||
video.step("正在填写演示内容")
|
||
await anti_detect.human_type(page, search, demo_text)
|
||
await anti_detect.human_delay_short()
|
||
|
||
video.step("正在等待人工验证(如有)")
|
||
if not await wait_for_captcha_pass(
|
||
page,
|
||
timeout_sec=config.get_int("HUMAN_WAIT_TIMEOUT", 180),
|
||
):
|
||
shot = await artifacts.capture_failure(
|
||
page, data_dir, batch_id, "captcha"
|
||
)
|
||
if shot:
|
||
artifact_paths["captcha"] = shot
|
||
return ExampleResult(
|
||
ok=False,
|
||
serial_no=None,
|
||
error_msg=errors.ERR_CAPTCHA_NEED_HUMAN,
|
||
artifacts={**artifact_paths, **video.to_artifact_dict()},
|
||
)
|
||
|
||
video.step("演示任务完成")
|
||
await context.close()
|
||
except Exception as exc:
|
||
video.step("演示异常结束")
|
||
return ExampleResult(
|
||
ok=False,
|
||
serial_no=None,
|
||
error_msg=str(exc),
|
||
artifacts={**artifact_paths, **video.to_artifact_dict()},
|
||
)
|
||
|
||
arts = {"mode": "sim_rpa", "url": url, **artifact_paths, **video.to_artifact_dict()}
|
||
if video.output_video_path:
|
||
arts["video_path"] = video.output_video_path
|
||
return ExampleResult(
|
||
ok=True,
|
||
serial_no=f"SIM-{len(items)}",
|
||
error_msg=None,
|
||
artifacts=arts,
|
||
)
|