Compare commits
14
Commits
8eccfa1833
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
0b74efb3e6 | ||
|
|
7256e215bb | ||
|
|
4e75beaeca | ||
|
|
87b8bbb075 | ||
|
|
6959670729 | ||
|
|
d436d6aaca | ||
|
|
e8143e2627 | ||
|
|
a3c3d65c02 | ||
|
|
84ccb7bac4 | ||
|
|
b3d31cb2d2 | ||
|
|
80760b9dd8 | ||
|
|
729ada2a6f | ||
|
|
7625b3a9ad | ||
|
|
c706116de3 |
No files matched your search
+1
-1
@@ -1,7 +1,7 @@
|
||||
# ===========================================
|
||||
# Browser Use MCP Server Configuration
|
||||
# ===========================================
|
||||
|
||||
#
|
||||
# OpenRouter API Key (обязательно)
|
||||
# Получить на https://openrouter.ai/keys
|
||||
OPENROUTER_API_KEY=your_openrouter_api_key_here
|
||||
|
||||
+3
-1
@@ -1,10 +1,12 @@
|
||||
FROM mcr.microsoft.com/playwright/python:v1.49.0-noble
|
||||
|
||||
# Установка системных зависимостей
|
||||
# socat — проброс портов для CDP
|
||||
# socat — проброс портов для CDP_
|
||||
# Видеокодеки и мультимедийные библиотеки для воспроизведения видео в Chrome
|
||||
RUN apt-get update && apt-get install -y \
|
||||
socat \
|
||||
wget \
|
||||
unzip \
|
||||
libgstreamer1.0-0 \
|
||||
gstreamer1.0-plugins-base \
|
||||
gstreamer1.0-plugins-good \
|
||||
|
||||
@@ -40,7 +40,7 @@ CAPSOLVER_API_KEY = os.getenv("CAPSOLVER_API_KEY", "")
|
||||
AUTO_SOLVE_CAPTCHA = os.getenv("AUTO_SOLVE_CAPTCHA", "false").lower() == "true"
|
||||
|
||||
# ===========================================
|
||||
# Настройки обхода reCAPTCHA
|
||||
# Настройки обхода reCAPTCHA_
|
||||
# ===========================================
|
||||
RECAPTCHA_BYPASS = os.getenv("RECAPTCHA_BYPASS", "false").lower() == "true"
|
||||
|
||||
@@ -59,8 +59,28 @@ logger.info(f"USE_XVFB: {USE_XVFB}")
|
||||
logger.info(f"REMOTE_DEBUGGING_ENABLED: {REMOTE_DEBUGGING_ENABLED}")
|
||||
logger.info(f"RECAPTCHA_BYPASS: {RECAPTCHA_BYPASS}")
|
||||
logger.info(f"AUTO_SOLVE_CAPTCHA: {AUTO_SOLVE_CAPTCHA}")
|
||||
logger.info(f"CAPSOLVER_API_KEY: {'set' if CAPSOLVER_API_KEY else 'not set'}")
|
||||
logger.info(f"PROXY_URL: {PROXY_URL if PROXY_URL else 'not set'}")
|
||||
logger.info(f"===========================")
|
||||
|
||||
# Настраиваем CapSolver расширение с API ключом
|
||||
if CAPSOLVER_API_KEY:
|
||||
capsolver_config_path = "/app/capsolver-extension/assets/config.js"
|
||||
if os.path.exists(capsolver_config_path):
|
||||
try:
|
||||
with open(capsolver_config_path, 'r') as f:
|
||||
config_content = f.read()
|
||||
# Заменяем apiKey на реальный
|
||||
import re
|
||||
config_content = re.sub(r'apiKey:\s*"[^"]*"', f'apiKey: "{CAPSOLVER_API_KEY}"', config_content)
|
||||
with open(capsolver_config_path, 'w') as f:
|
||||
f.write(config_content)
|
||||
logger.info("CapSolver API key configured in extension")
|
||||
except Exception as e:
|
||||
logger.warning(f"Failed to configure CapSolver API key: {e}")
|
||||
else:
|
||||
logger.warning(f"CapSolver config not found at {capsolver_config_path}")
|
||||
|
||||
# Создаём Tools с кастомными действиями
|
||||
try:
|
||||
from browser_use import Tools, BrowserSession
|
||||
@@ -198,7 +218,7 @@ try:
|
||||
sitekey = await turnstile_element.get_attribute('data-sitekey')
|
||||
turnstile_frame = await page.query_selector('iframe[src*="turnstile"]')
|
||||
if turnstile_frame and sitekey:
|
||||
captcha_type = "TurnstileTask"
|
||||
captcha_type = "AntiTurnstileTaskProxyLess"
|
||||
|
||||
if not sitekey or not captcha_type:
|
||||
return ActionResult(
|
||||
@@ -299,7 +319,7 @@ try:
|
||||
include_in_memory=True
|
||||
)
|
||||
|
||||
elif captcha_type == "TurnstileTask":
|
||||
elif captcha_type == "AntiTurnstileTaskProxyLess":
|
||||
token = solution.get("token")
|
||||
if token:
|
||||
await page.evaluate(f'''(token) => {{
|
||||
@@ -418,7 +438,7 @@ async def solve_captcha_with_capsolver(page, capsolver_api_key: str) -> bool:
|
||||
if sitekey:
|
||||
turnstile_frame = await page.query_selector('iframe[src*="turnstile"]')
|
||||
if turnstile_frame:
|
||||
captcha_type = "TurnstileTask"
|
||||
captcha_type = "AntiTurnstileTaskProxyLess"
|
||||
|
||||
if not sitekey or not captcha_type:
|
||||
logger.info("No captcha found on page")
|
||||
@@ -487,7 +507,7 @@ async def solve_captcha_with_capsolver(page, capsolver_api_key: str) -> bool:
|
||||
await textarea.fill(h_captcha_response)
|
||||
logger.info("hCaptcha solved successfully!")
|
||||
return True
|
||||
elif captcha_type == "TurnstileTask":
|
||||
elif captcha_type == "AntiTurnstileTaskProxyLess":
|
||||
token = solution.get("token")
|
||||
if token:
|
||||
# Вставляем токен
|
||||
@@ -791,6 +811,7 @@ async def run_browser_task(task_id: str, task: str, max_steps: int, model: str):
|
||||
f"--server-args=-screen 0 {width}x{height}x24",
|
||||
])
|
||||
|
||||
# Всегда добавляем --no-sandbox
|
||||
chrome_cmd.extend([
|
||||
chrome_exe,
|
||||
f"--remote-debugging-port={CHROME_INTERNAL_PORT}",
|
||||
@@ -823,18 +844,23 @@ 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",
|
||||
# WebGPU флаги для Cloudflare Turnstile
|
||||
"--enable-webgpu",
|
||||
"--enable-webgpu-developer-features",
|
||||
"--use-angle=swiftshader",
|
||||
# Отключено: stealth-расширение вызывает ошибки и ломает Cloudflare Turnstile
|
||||
# "--load-extension=/app/stealth-extension",
|
||||
# "--disable-extensions-except=/app/stealth-extension",
|
||||
"--use-gl=desktop",
|
||||
"--ignore-gpu-blocklist",
|
||||
"--enable-accelerated-2d-canvas",
|
||||
"--enable-gpu-rasterization",
|
||||
"--enable-zero-copy",
|
||||
"--ignore-webgl-blacklist",
|
||||
# Добавляем экспериментальные флаги для WebGL
|
||||
"--enable-experimental-webgl",
|
||||
"--enable-webgl2-compute-context",
|
||||
])
|
||||
|
||||
# Убираем загрузку расширения CapSolver - используем API вместо него
|
||||
# Расширение конфликтует с Cloudflare Turnstile
|
||||
# Решение капчи происходит через функцию solve_captcha_with_capsolver
|
||||
if AUTO_SOLVE_CAPTCHA and CAPSOLVER_API_KEY:
|
||||
logger.info(f"[{task_id}] CapSolver will solve CAPTCHA via API (not extension)")
|
||||
|
||||
# Добавляем специальные флаги для обхода reCAPTCHA если включено
|
||||
if RECAPTCHA_BYPASS:
|
||||
chrome_cmd.extend([
|
||||
@@ -848,6 +874,14 @@ async def run_browser_task(task_id: str, task: str, max_steps: int, model: str):
|
||||
"--disable-ipc-flooding-protection",
|
||||
])
|
||||
logger.info(f"[{task_id}] Added reCAPTCHA bypass flags")
|
||||
else:
|
||||
# Всегда добавляем --no-sandbox для работы в контейнере
|
||||
# и --disable-blink-features=AutomationControlled для обхода Cloudflare Turnstile
|
||||
chrome_cmd.extend([
|
||||
"--no-sandbox",
|
||||
"--disable-blink-features=AutomationControlled",
|
||||
])
|
||||
logger.info(f"[{task_id}] Added minimal stealth flags for Cloudflare Turnstile")
|
||||
|
||||
# Добавляем прокси если есть
|
||||
chrome_cmd.extend(proxy_args)
|
||||
@@ -913,39 +947,70 @@ async def run_browser_task(task_id: str, task: str, max_steps: int, model: str):
|
||||
logger.info(f"[{task_id}] Using new Chrome on internal port {CHROME_INTERNAL_PORT}")
|
||||
|
||||
# Инжектируем stealth-скрипты через CDP для дополнительной защиты
|
||||
# Отключено: stealth-скрипты вызывают ошибки и ломают Cloudflare Turnstile
|
||||
# try:
|
||||
# import json
|
||||
# stealth_script_path = "/app/stealth-extension/stealth.js"
|
||||
# if os.path.exists(stealth_script_path):
|
||||
# with open(stealth_script_path, 'r') as f:
|
||||
# stealth_js = f.read()
|
||||
#
|
||||
# # Получаем список целей (вкладок) через CDP
|
||||
# response = urllib.request.urlopen(f"{cdp_url}/json/list", timeout=5)
|
||||
# targets = json.loads(response.read().decode())
|
||||
#
|
||||
# for target in targets:
|
||||
# if target.get('type') == 'page':
|
||||
# ws_url = target.get('webSocketDebuggerUrl')
|
||||
# if ws_url:
|
||||
# logger.info(f"[{task_id}] Injecting stealth scripts via CDP into {target.get('url', 'unknown')}")
|
||||
#
|
||||
# logger.info(f"[{task_id}] Stealth extension loaded + CDP stealth injection ready")
|
||||
# else:
|
||||
# logger.warning(f"[{task_id}] Stealth script not found at {stealth_script_path}")
|
||||
# except Exception as e:
|
||||
# logger.warning(f"[{task_id}] CDP stealth injection failed (extension should still work): {e}")
|
||||
try:
|
||||
import json
|
||||
stealth_script_path = "/app/stealth-extension/stealth.js"
|
||||
if os.path.exists(stealth_script_path):
|
||||
with open(stealth_script_path, 'r') as f:
|
||||
stealth_js = f.read()
|
||||
|
||||
# Получаем список целей (вкладок) через CDP
|
||||
response = urllib.request.urlopen(f"{cdp_url}/json/list", timeout=5)
|
||||
targets = json.loads(response.read().decode())
|
||||
|
||||
for target in targets:
|
||||
if target.get('type') == 'page':
|
||||
ws_url = target.get('webSocketDebuggerUrl')
|
||||
if ws_url:
|
||||
logger.info(f"[{task_id}] Injecting stealth scripts via CDP into {target.get('url', 'unknown')}")
|
||||
|
||||
logger.info(f"[{task_id}] Stealth extension loaded + CDP stealth injection ready")
|
||||
else:
|
||||
logger.warning(f"[{task_id}] Stealth script not found at {stealth_script_path}")
|
||||
except Exception as e:
|
||||
logger.warning(f"[{task_id}] CDP stealth injection failed (extension should still work): {e}")
|
||||
|
||||
# Подключаемся через CDP
|
||||
browser = Browser(cdp_url=cdp_url)
|
||||
else:
|
||||
# Обычный запуск
|
||||
# Добавляем флаги для работы в контейнере
|
||||
extra_chromium_args = [
|
||||
"--no-sandbox",
|
||||
"--disable-dev-shm-usage",
|
||||
"--disable-gpu",
|
||||
"--enable-webgl",
|
||||
"--use-gl=desktop",
|
||||
"--ignore-gpu-blocklist",
|
||||
"--enable-accelerated-2d-canvas",
|
||||
"--enable-gpu-rasterization",
|
||||
"--enable-zero-copy",
|
||||
"--ignore-webgl-blacklist",
|
||||
"--enable-experimental-webgl",
|
||||
]
|
||||
|
||||
# Используем Google Chrome если доступен
|
||||
chrome_path = os.getenv("CHROME_PATH", "/usr/bin/google-chrome")
|
||||
if os.path.exists(chrome_path):
|
||||
logger.info(f"Using Google Chrome at {chrome_path}")
|
||||
else:
|
||||
logger.info("Google Chrome not found, using default Chromium")
|
||||
chrome_path = None
|
||||
|
||||
# Убираем загрузку расширения CapSolver - используем API
|
||||
# Расширение конфликтует с Cloudflare Turnstile
|
||||
extensions = []
|
||||
if AUTO_SOLVE_CAPTCHA and CAPSOLVER_API_KEY:
|
||||
logger.info("CapSolver will solve CAPTCHA via API (not extension)")
|
||||
|
||||
browser_profile = BrowserProfile(
|
||||
user_data_dir="/app/browser-data/main_profile",
|
||||
profile_directory="Default",
|
||||
headless=False, # Всегда false для поддержки reCAPTCHA
|
||||
keep_alive=True,
|
||||
extra_chromium_args=extra_chromium_args,
|
||||
executable_path=chrome_path,
|
||||
extensions=extensions,
|
||||
)
|
||||
browser = Browser(browser_profile=browser_profile)
|
||||
|
||||
@@ -963,29 +1028,67 @@ async def run_browser_task(task_id: str, task: str, max_steps: int, model: str):
|
||||
if custom_tools is not None:
|
||||
agent_kwargs["tools"] = custom_tools
|
||||
|
||||
# Добавляем информацию о капче в промпт, если включено авто-решение
|
||||
if AUTO_SOLVE_CAPTCHA and CAPSOLVER_API_KEY:
|
||||
captcha_hint = "\n\nIMPORTANT: If you encounter a CAPTCHA (reCAPTCHA, hCaptcha, or Cloudflare Turnstile), use the solve_captcha tool to automatically solve it. The tool requires CAPSOLVER_API_KEY to be configured."
|
||||
agent_kwargs["task"] = task + captcha_hint
|
||||
|
||||
agent = Agent(**agent_kwargs)
|
||||
|
||||
# Автоматическое решение капчи перед запуском, если включено
|
||||
if AUTO_SOLVE_CAPTCHA and CAPSOLVER_API_KEY:
|
||||
logger.info(f"[{task_id}] AUTO_SOLVE_CAPTCHA enabled, checking for captcha...")
|
||||
try:
|
||||
# Получаем страницу через browser_use
|
||||
from browser_use import BrowserSession
|
||||
browser_session = BrowserSession(browser=browser)
|
||||
page = await browser_session.get_current_page()
|
||||
# Получаем страницу через browser
|
||||
page = None
|
||||
try:
|
||||
# Пробуем получить страницу через CDP
|
||||
import urllib.request
|
||||
import json
|
||||
response = urllib.request.urlopen(f"{cdp_url}/json/list", timeout=5)
|
||||
targets = json.loads(response.read().decode())
|
||||
for target in targets:
|
||||
if target.get('type') == 'page' and target.get('url', '') not in ['about:blank', 'chrome://newtab/']:
|
||||
# Найдена страница с URL
|
||||
logger.info(f"[{task_id}] Found page: {target.get('url')}")
|
||||
# Подключаемся к странице через Playwright
|
||||
from playwright.async_api import async_playwright
|
||||
async with async_playwright() as p:
|
||||
browser = await p.chromium.connect_over_cdp(cdp_url)
|
||||
context = browser.contexts[0] if browser.contexts else None
|
||||
if context:
|
||||
pages = context.pages
|
||||
if pages:
|
||||
page = pages[0]
|
||||
break
|
||||
except Exception as e_page:
|
||||
logger.warning(f"[{task_id}] Could not get page: {e_page}")
|
||||
|
||||
if page:
|
||||
logger.info(f"[{task_id}] Page obtained, checking for captcha elements...")
|
||||
# Проверяем наличие капчи
|
||||
captcha_found = await page.query_selector('[data-sitekey]')
|
||||
turnstile_found = await page.query_selector('iframe[src*="turnstile"]')
|
||||
if captcha_found or turnstile_found:
|
||||
logger.info(f"[{task_id}] CAPTCHA detected, attempting to solve automatically...")
|
||||
# Вызываем функцию решения капчи
|
||||
solved = await solve_captcha_with_capsolver(page, CAPSOLVER_API_KEY)
|
||||
if solved:
|
||||
logger.info(f"[{task_id}] CAPTCHA solved successfully!")
|
||||
try:
|
||||
captcha_found = await page.query_selector('[data-sitekey]')
|
||||
turnstile_found = await page.query_selector('iframe[src*="turnstile"]')
|
||||
logger.info(f"[{task_id}] Captcha found: {captcha_found}, Turnstile found: {turnstile_found}")
|
||||
if captcha_found or turnstile_found:
|
||||
logger.info(f"[{task_id}] CAPTCHA detected, attempting to solve automatically...")
|
||||
# Вызываем функцию решения капчи
|
||||
solved = await solve_captcha_with_capsolver(page, CAPSOLVER_API_KEY)
|
||||
if solved:
|
||||
logger.info(f"[{task_id}] CAPTCHA solved successfully!")
|
||||
else:
|
||||
logger.warning(f"[{task_id}] Failed to solve CAPTCHA automatically")
|
||||
else:
|
||||
logger.warning(f"[{task_id}] Failed to solve CAPTCHA automatically")
|
||||
logger.info(f"[{task_id}] No captcha detected on page")
|
||||
except Exception as e_check:
|
||||
logger.warning(f"[{task_id}] Error checking captcha: {e_check}")
|
||||
else:
|
||||
logger.warning(f"[{task_id}] Could not get page from browser")
|
||||
except Exception as e:
|
||||
logger.warning(f"[{task_id}] Auto-captcha solve failed: {e}")
|
||||
logger.error(f"[{task_id}] Auto-captcha solve failed: {e}")
|
||||
else:
|
||||
logger.info(f"[{task_id}] AUTO_SOLVE_CAPTCHA={AUTO_SOLVE_CAPTCHA}, CAPSOLVER_API_KEY={'set' if CAPSOLVER_API_KEY else 'not set'}")
|
||||
|
||||
# Выполнение задачи
|
||||
history: AgentHistoryList = await agent.run(max_steps=max_steps)
|
||||
|
||||
+6
-5
@@ -6,23 +6,24 @@ services:
|
||||
context: .
|
||||
dockerfile: Dockerfile
|
||||
image: browser-use-mcp:latest
|
||||
container_name: browser-use
|
||||
security_opt:
|
||||
- seccomp:unconfined
|
||||
- apparmor:unconfined
|
||||
ports:
|
||||
- "8383:8383"
|
||||
- "9222:9222"
|
||||
- "${APP_PORT_8383}:${CONTAINER_PORT_8383}"
|
||||
- "${APP_PORT_9222}:${CONTAINER_PORT_9222}"
|
||||
environment:
|
||||
- OPENROUTER_API_KEY=${OPENROUTER_API_KEY}
|
||||
- LLM_MODEL_NAME=${LLM_MODEL_NAME:-arcee-ai/trinity-large-preview:free}
|
||||
- REMOTE_DEBUGGING_ENABLED=true
|
||||
- REMOTE_DEBUGGING_PORT=9222
|
||||
- REMOTE_DEBUGGING_PORT=${CONTAINER_PORT_9222}
|
||||
# CapSolver для автоматического решения капчи (Turnstile, reCAPTCHA, hCaptcha)
|
||||
- CAPSOLVER_API_KEY=${CAPSOLVER_API_KEY:-}
|
||||
- AUTO_SOLVE_CAPTCHA=${AUTO_SOLVE_CAPTCHA:-false}
|
||||
# ВАЖНО! Прокси обязательна для Turnstile! Без прокси Cloudflare детектит бота
|
||||
- PROXY_URL=${PROXY_URL:-}
|
||||
volumes:
|
||||
- /home/nafer/dokploy_data/browser-use:/app/browser-data
|
||||
- "${APP_DATA_PATH}:/app/browser-data"
|
||||
shm_size: '4gb'
|
||||
ipc: host
|
||||
deploy:
|
||||
|
||||
Reference in new issue
Block a user