"""消息路由:skills 快查 / 网页操作 / 截图预览 / Cursor 通用任务。""" from __future__ import annotations import re import env_config from browser_parser import is_browser_intent, parse_browser_request from browser_service import format_browser_caption, run_browser_automation from cursor_runner import run_cursor_task, strip_mention from image_extract import find_image_paths, strip_fake_image_markdown from preview_service import capture_preview, is_preview_intent, resolve_preview_request from skills_service import handle_command, parse_command from bot_types import RouteResult def routing_mode() -> str: return (env_config.env("ROUTING_MODE", "hybrid") or "hybrid").lower() def _normalize(text: str) -> str: return re.sub(r"@\S+\s*", "", text).strip().lower() def is_skills_fast_command(text: str) -> bool: raw = _normalize(text) if not raw: return True if raw in {"help", "帮助", "?", "h"}: return True cmd = parse_command(text) if cmd.kind in {"help", "list", "detail"}: return True if cmd.kind == "search" and re.match(r"^(search|搜索|find|查)\s+", raw): return True return False async def _run_browser(text: str, on_progress=None) -> RouteResult: if parse_browser_request(text) is None: raise RuntimeError("无法解析网页操作步骤") if on_progress: await on_progress("正在按步骤执行网页操作…") result = await run_browser_automation(text) return RouteResult( source="browser", text=format_browser_caption(result), image_path=str(result.screenshot_path), ) async def _run_preview(text: str, on_progress=None) -> RouteResult: preview_req = resolve_preview_request(text) if preview_req is None: raise RuntimeError("无法解析截图请求") if on_progress: await on_progress(f"正在访问并截图:{preview_req.url or '默认地址'}…") result = await capture_preview(preview_req.url, preview_req.port) caption = ( f"**页面预览**\n" f"> URL:`{result.final_url or result.url}`\n" f"> 项目:`{env_config.env('CURSOR_CWD', '')}`\n" f"> dev server:{'已自动启动' if result.started_dev_server else '使用已有服务'}" ) return RouteResult( source="preview", text=caption, image_path=str(result.screenshot_path), ) async def route_message(text: str, on_progress=None) -> RouteResult: task = strip_mention(text) if not task: return RouteResult("skills", handle_command("help")) if is_browser_intent(text): return await _run_browser(text, on_progress=on_progress) if resolve_preview_request(text) is not None: return await _run_preview(text, on_progress=on_progress) mode = routing_mode() if mode == "skills": return RouteResult("skills", handle_command(text)) if mode == "cursor" or not is_skills_fast_command(text): reply = await run_cursor_task(task, on_progress=on_progress) reply = strip_fake_image_markdown(reply) image_path: str | None = None paths = find_image_paths(reply) if paths: image_path = str(paths[0]) elif is_preview_intent(text) or is_browser_intent(text): if on_progress: await on_progress("未找到截图文件,改用 Playwright 自动执行…") if is_browser_intent(text): return await _run_browser(text, on_progress=on_progress) return await _run_preview(text, on_progress=on_progress) return RouteResult("cursor", reply, image_path=image_path) return RouteResult("skills", handle_command(text))