Compare commits

..
2 Commits
Author SHA1 Message Date
nafer d379c19b8c opus_ctrl+v enable 2026-03-06 12:27:52 +03:00
nafer f6f4ba4067 opus_видит капчу и предлагает увидеть видео 2026-03-06 10:22:00 +03:00
2 changed files with 135 additions and 11 deletions

No files matched your search

+25 -2
View File
@@ -1,7 +1,30 @@
FROM mcr.microsoft.com/playwright/python:v1.49.0-noble
# Установка системных зависимостей (socat для проброса портов)
RUN apt-get update && apt-get install -y socat && rm -rf /var/lib/apt/lists/*
# Установка системных зависимостей
# socat — проброс портов для CDP
# Видеокодеки и мультимедийные библиотеки для воспроизведения видео в Chrome
RUN apt-get update && apt-get install -y \
socat \
libgstreamer1.0-0 \
gstreamer1.0-plugins-base \
gstreamer1.0-plugins-good \
gstreamer1.0-plugins-bad \
gstreamer1.0-plugins-ugly \
gstreamer1.0-libav \
libavcodec-extra \
libavformat-dev \
libavutil-dev \
libnss3 \
libatk-bridge2.0-0 \
libdrm2 \
libxkbcommon0 \
libgbm1 \
libvpx-dev \
libopus-dev \
fonts-liberation \
fonts-noto-color-emoji \
fonts-noto-cjk \
&& rm -rf /var/lib/apt/lists/*
# Установка дополнительных Python зависимостей
RUN pip install --no-cache-dir --root-user-action=ignore \
+110 -9
View File
@@ -7,7 +7,7 @@ import asyncio
from typing import Optional
from fastapi import FastAPI, HTTPException
from pydantic import BaseModel, Field
from browser_use import Agent, Browser, BrowserProfile, ChatOpenAI
from browser_use import Agent, Browser, BrowserProfile, ChatOpenAI, ActionResult
from browser_use.agent.views import AgentHistoryList
import uvicorn
from fastapi.responses import HTMLResponse
@@ -27,6 +27,99 @@ import logging
logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s')
logger = logging.getLogger(__name__)
# Создаём Tools с кастомными действиями
try:
from browser_use import Tools, BrowserSession
custom_tools = Tools()
@custom_tools.action(description='Paste text from clipboard into the currently focused element using Ctrl+V. Use this instead of regular typing when a website requires a paste event to trigger special behavior (e.g., URL preview generation, link formatting). Parameters: text - the text to paste, index - optional index of element to click/focus first.')
async def paste_text(text: str, index: int = None, browser_session: BrowserSession = None):
"""Вставляет текст через Ctrl+V (эмуляция вставки из буфера обмена)"""
import json as json_util
try:
page = await browser_session.get_current_page()
# Если указан индекс элемента — кликаем на него для фокуса
if index is not None:
dom_element = await browser_session.get_dom_element_by_index(index)
if dom_element:
await browser_session.click_element_node(dom_element)
await asyncio.sleep(0.3)
# Экранируем текст для безопасной вставки в JS
safe_text = json_util.dumps(text)
# Записываем текст в буфер обмена через JavaScript
await page.evaluate(f'''() => {{
const textArea = document.createElement('textarea');
textArea.value = {safe_text};
textArea.style.position = 'fixed';
textArea.style.left = '-9999px';
document.body.appendChild(textArea);
textArea.select();
document.execCommand('copy');
document.body.removeChild(textArea);
}}''')
# Также пробуем через Clipboard API
try:
await page.evaluate(f'''async () => {{
await navigator.clipboard.writeText({safe_text});
}}''')
except Exception:
pass
# Нажимаем Ctrl+V для вставки
await page.keyboard.down('Control')
await page.keyboard.press('v')
await page.keyboard.up('Control')
await asyncio.sleep(0.5)
return ActionResult(
extracted_content=f"Text pasted successfully: {text[:100]}...",
include_in_memory=True
)
except Exception as e:
# Fallback: эмулируем paste event через JavaScript
try:
page = await browser_session.get_current_page()
safe_text = json_util.dumps(text)
await page.evaluate(f'''() => {{
const el = document.activeElement;
if (el) {{
const clipboardData = new DataTransfer();
clipboardData.setData('text/plain', {safe_text});
const pasteEvent = new ClipboardEvent('paste', {{
bubbles: true,
cancelable: true,
clipboardData: clipboardData
}});
el.dispatchEvent(pasteEvent);
if ('value' in el) {{
el.value = {safe_text};
el.dispatchEvent(new Event('input', {{ bubbles: true }}));
el.dispatchEvent(new Event('change', {{ bubbles: true }}));
}} else if (el.isContentEditable) {{
document.execCommand('insertText', false, {safe_text});
}}
}}
}}''')
return ActionResult(
extracted_content=f"Text pasted via fallback: {text[:100]}...",
include_in_memory=True
)
except Exception as e2:
return ActionResult(error=f"Failed to paste text: {e2}")
logger.info("Custom tools (paste_text) registered successfully")
except ImportError as e:
custom_tools = None
logger.warning(f"Could not import Tools/BrowserSession from browser_use: {e}. Custom tools disabled.")
def patch_chrome_binary():
"""Патчит бинарный файл Chrome для удаления строки cdc_, которую ищут антифроды"""
possible_paths = [
@@ -224,9 +317,11 @@ async def run_browser_task(task_id: str, task: str, max_steps: int, model: str):
existing_prefs['safebrowsing'] = {}
existing_prefs['safebrowsing']['enabled'] = False
# Разрешаем cookies для всех сайтов
existing_prefs['profile']['default_content_setting_values'] = existing_prefs.get('profile', {}).get('default_content_setting_values', {})
# Разрешаем cookies и clipboard для всех сайтов
if 'default_content_setting_values' not in existing_prefs['profile']:
existing_prefs['profile']['default_content_setting_values'] = {}
existing_prefs['profile']['default_content_setting_values']['cookies'] = 1 # 1 = Allow
existing_prefs['profile']['default_content_setting_values']['clipboard'] = 1 # 1 = Allow (для paste_text action)
# Отключаем Tracking Protection
if 'tracking_protection' not in existing_prefs:
@@ -313,6 +408,8 @@ async def run_browser_task(task_id: str, task: str, max_steps: int, model: str):
"--enable-unsafe-swiftshader",
"--ignore-gpu-blocklist",
"--enable-webgl",
"--use-gl=angle",
"--use-angle=swiftshader",
"--test-type",
"--load-extension=/app/stealth-extension",
"--disable-extensions-except=/app/stealth-extension",
@@ -411,12 +508,16 @@ async def run_browser_task(task_id: str, task: str, max_steps: int, model: str):
logger.info("[startup] Chrome started successfully and ready for connections.")
return
# Создание агента
agent = Agent(
task=task,
llm=llm,
browser=browser,
)
# Создание агента с кастомными действиями (paste_text и др.)
agent_kwargs = {
"task": task,
"llm": llm,
"browser": browser,
}
if custom_tools is not None:
agent_kwargs["tools"] = custom_tools
agent = Agent(**agent_kwargs)
# Выполнение задачи
history: AgentHistoryList = await agent.run(max_steps=max_steps)