Compare commits

...
30 Commits
Author SHA1 Message Date
nafer 0b74efb3e6 добавил browser-data в переменную, чтобы инстансы имели разные аккаунты 2026-04-17 21:32:29 +03:00
nafer 7256e215bb container_name: browser-use удалил, чтобы не было конфликта 2026-04-17 21:26:55 +03:00
nafer 4e75beaeca изменил порты на переменные, чтобы можно было инстансы дублировать 2026-04-17 21:14:03 +03:00
nafer 87b8bbb075 cap_psyho 2026-03-10 20:45:53 +03:00
nafer 6959670729 capsolver_sit_watch 2026-03-10 20:04:22 +03:00
nafer d436d6aaca capsolver_py_wrap 2026-03-10 19:45:43 +03:00
nafer e8143e2627 14 2026-03-10 19:26:38 +03:00
nafer a3c3d65c02 13 2026-03-10 18:49:03 +03:00
nafer 84ccb7bac4 12 2026-03-10 18:35:24 +03:00
nafer b3d31cb2d2 1 2026-03-10 18:33:27 +03:00
nafer 80760b9dd8 cp_naf 2026-03-10 18:11:02 +03:00
nafer 729ada2a6f cap7 2026-03-10 17:02:16 +03:00
nafer 7625b3a9ad cap6 2026-03-10 15:45:22 +03:00
nafer c706116de3 cap5 2026-03-10 15:34:35 +03:00
nafer 8eccfa1833 cap4 2026-03-10 15:30:05 +03:00
nafer b55f8c5318 cap3 2026-03-10 15:21:51 +03:00
nafer ac49cc3759 cap2 2026-03-10 14:58:30 +03:00
nafer 1bedd515ae cap1 2026-03-10 14:50:10 +03:00
nafer 3bc16f775f stealth-recapcha-repair 2026-03-10 14:37:49 +03:00
nafer 692f4c0dd1 super-stealth 2026-03-10 12:30:16 +03:00
nafer 2a058623a4 max2 2026-03-06 18:23:19 +03:00
nafer b485985108 max1 2026-03-06 18:21:18 +03:00
nafer 5522dc5d1d minimax-start 2026-03-06 17:39:18 +03:00
nafer fc889c46a8 Обновить docker-compose.yml 2026-03-06 14:08:26 +00:00
nafer a27099caaf Обновить docker-compose.yml 2026-03-06 13:57:18 +00:00
nafer a1931ee17e Обновить docker-compose.yml 2026-03-06 13:56:17 +00:00
nafer e1008d760e Обновить docker-compose.yml 2026-03-06 13:53:34 +00:00
nafer e312770bfb Обновить docker-compose.yml 2026-03-06 13:39:25 +00:00
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
7 changed files with 35845 additions and 254 deletions

No files matched your search

-26
View File
@@ -1,26 +0,0 @@
# ===========================================
# Browser Use MCP Server Configuration
# ===========================================
# OpenRouter API Key (обязательно)
OPENROUTER_API_KEY=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
# LLM Provider - используем OpenRouter
LLM_PROVIDER=openrouter
# Модель для browser agent
LLM_MODEL_NAME=arcee-ai/trinity-large-preview:free
# ===========================================
# Опциональные настройки
# ===========================================
BROWSER_HEADLESS=true
AGENT_MAX_STEPS=20
AGENT_USE_VISION=true
SERVER_PORT=8383
SERVER_HOST=0.0.0.0
# Remote Debugging
REMOTE_DEBUGGING_ENABLED=true
REMOTE_DEBUGGING_PORT=9222
+46 -2
View File
@@ -1,7 +1,7 @@
# =========================================== # ===========================================
# Browser Use MCP Server Configuration # Browser Use MCP Server Configuration
# =========================================== # ===========================================
#
# OpenRouter API Key (обязательно) # OpenRouter API Key (обязательно)
# Получить на https://openrouter.ai/keys # Получить на https://openrouter.ai/keys
OPENROUTER_API_KEY=your_openrouter_api_key_here OPENROUTER_API_KEY=your_openrouter_api_key_here
@@ -18,7 +18,9 @@ LLM_MODEL_NAME=google/gemini-2.0-flash-001
# =========================================== # ===========================================
# Запуск браузера в headless режиме (true/false) # Запуск браузера в headless режиме (true/false)
BROWSER_HEADLESS=true # ВНИМАНИЕ: headless=false всегда для поддержки reCAPTCHA!
# Это значение игнорируется, браузер всегда запускается в не-headless режиме
BROWSER_HEADLESS=false
# Максимальное количество шагов для выполнения задачи # Максимальное количество шагов для выполнения задачи
AGENT_MAX_STEPS=20 AGENT_MAX_STEPS=20
@@ -26,6 +28,48 @@ AGENT_MAX_STEPS=20
# Использование зрения (true/false) # Использование зрения (true/false)
AGENT_USE_VISION=true AGENT_USE_VISION=true
# ===========================================
# Настройки прокси (для максимального stealth)
# ===========================================
# Прокси сервер (формат: protocol://user:pass@host:port)
# Примеры:
# http://user:pass@proxy.com:8080
# socks5://user:pass@sproxy.com:1080
# http://host:port (без авторизации)
PROXY_URL=
# Прокси для browser-use cloud (страна)
# Доступно: us, fr, it, jp, au, de, fi, ca
CLOUD_PROXY_COUNTRY=
# ===========================================
# Настройки stealth (антидетект)
# ===========================================
# Рандомизировать viewport (ширина x высота)
# Формат: WIDTHxHEIGHT, например 1920x1080
# Оставьте пустым для использования случайных значений
VIEWPORT_SIZE=
# Использовать xvfb для виртуального дисплея (рекомендуется)
USE_XVFB=true
# ===========================================
# Настройки решения капчи
# ===========================================
# CapSolver API Key (для автоматического решения капчи)
# Получить на https://capmonster.cloud/ или https://capsolver.com/
CAPSOLVER_API_KEY=
# Автоматически решать капчу (true/false)
AUTO_SOLVE_CAPTCHA=false
# Включить специальные флаги Chrome для обхода reCAPTCHA (true/false)
# Помогает если reCAPTCHA не отображается
RECAPTCHA_BYPASS=false
# =========================================== # ===========================================
# Настройки сервера # Настройки сервера
# =========================================== # ===========================================
+29 -3
View File
@@ -1,7 +1,32 @@
FROM mcr.microsoft.com/playwright/python:v1.49.0-noble 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 \
wget \
unzip \
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 зависимостей # Установка дополнительных Python зависимостей
RUN pip install --no-cache-dir --root-user-action=ignore \ RUN pip install --no-cache-dir --root-user-action=ignore \
@@ -12,7 +37,8 @@ RUN pip install --no-cache-dir --root-user-action=ignore \
langchain-openai \ langchain-openai \
langchain \ langchain \
python-dotenv \ python-dotenv \
setuptools setuptools \
httpx
WORKDIR /app WORKDIR /app
+692 -20
View File
@@ -7,7 +7,7 @@ import asyncio
from typing import Optional from typing import Optional
from fastapi import FastAPI, HTTPException from fastapi import FastAPI, HTTPException
from pydantic import BaseModel, Field 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 from browser_use.agent.views import AgentHistoryList
import uvicorn import uvicorn
from fastapi.responses import HTMLResponse from fastapi.responses import HTMLResponse
@@ -19,6 +19,31 @@ REMOTE_DEBUGGING_ENABLED = os.getenv("REMOTE_DEBUGGING_ENABLED", "false").lower(
# Модель по умолчанию из переменной окружения # Модель по умолчанию из переменной окружения
DEFAULT_MODEL = os.getenv("LLM_MODEL_NAME", "openai/gpt-4o-mini") DEFAULT_MODEL = os.getenv("LLM_MODEL_NAME", "openai/gpt-4o-mini")
# ===========================================
# Настройки прокси
# ===========================================
PROXY_URL = os.getenv("PROXY_URL", "")
CLOUD_PROXY_COUNTRY = os.getenv("CLOUD_PROXY_COUNTRY", "")
# ===========================================
# Настройки stealth
# ===========================================
# ВНИМАНИЕ: headless=false всегда для поддержки reCAPTCHA
BROWSER_HEADLESS = False # Всегда false для поддержки reCAPTCHA
USE_XVFB = os.getenv("USE_XVFB", "true").lower() == "true"
VIEWPORT_SIZE = os.getenv("VIEWPORT_SIZE", "") # Формат: WIDTHxHEIGHT, напр. 1920x1080
# ===========================================
# Настройки решения капчи (CapSolver)
# ===========================================
CAPSOLVER_API_KEY = os.getenv("CAPSOLVER_API_KEY", "")
AUTO_SOLVE_CAPTCHA = os.getenv("AUTO_SOLVE_CAPTCHA", "false").lower() == "true"
# ===========================================
# Настройки обхода reCAPTCHA_
# ===========================================
RECAPTCHA_BYPASS = os.getenv("RECAPTCHA_BYPASS", "false").lower() == "true"
from contextlib import asynccontextmanager from contextlib import asynccontextmanager
import logging import logging
@@ -27,6 +52,314 @@ import logging
logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s') logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s')
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
# Логирование настроек браузера
logger.info(f"=== Browser Configuration ===")
logger.info(f"BROWSER_HEADLESS: {BROWSER_HEADLESS}")
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
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}")
@custom_tools.action(description='Solve CAPTCHA using CapSolver API. Use this when you encounter reCAPTCHA, hCaptcha, or Cloudflare Turnstile. The agent will automatically detect and solve the captcha.')
async def solve_captcha(browser_session: BrowserSession = None):
"""
Решает капчу (reCAPTCHA/hCaptcha/Turnstile) с помощью CapSolver API.
Этот action автоматически:
1. Обнаруживает тип капчи на странице
2. Отправляет задачу в CapSolver
3. Ждёт решения
4. Вставляет ответ в форму
"""
if not CAPSOLVER_API_KEY:
return ActionResult(
error="CapSolver API key not configured. Set CAPSOLVER_API_KEY environment variable.",
include_in_memory=True
)
try:
page = await browser_session.get_current_page()
# Ищем sitekey для разных типов капчи
sitekey = None
captcha_type = None
# reCAPTCHA v2
recaptcha_element = await page.query_selector('[data-sitekey]')
if recaptcha_element:
sitekey = await recaptcha_element.get_attribute('data-sitekey')
if sitekey:
# Проверяем reCAPTCHA vs hCaptcha
iframe = await page.query_selector('iframe[src*="google.com/recaptcha"]')
if iframe:
captcha_type = "RecaptchaV2Task"
# hCaptcha
if not sitekey:
hcaptcha_element = await page.query_selector('[data-sitekey]')
if hcaptcha_element:
sitekey = await hcaptcha_element.get_attribute('data-sitekey')
hcaptcha_frame = await page.query_selector('iframe[src*="hcaptcha.com"]')
if hcaptcha_frame and sitekey:
captcha_type = "HCaptchaTask"
# Cloudflare Turnstile
if not sitekey:
turnstile_element = await page.query_selector('[data-sitekey]')
if turnstile_element:
sitekey = await turnstile_element.get_attribute('data-sitekey')
turnstile_frame = await page.query_selector('iframe[src*="turnstile"]')
if turnstile_frame and sitekey:
captcha_type = "AntiTurnstileTaskProxyLess"
if not sitekey or not captcha_type:
return ActionResult(
extracted_content="No captcha found on current page",
include_in_memory=True
)
page_url = page.url
logger.info(f"Found {captcha_type} with sitekey: {sitekey}")
# Отправляем задачу в CapSolver
import httpx
async with httpx.AsyncClient(timeout=60.0) as client:
# Создаём задачу
task_create_response = await client.post(
"https://api.capsolver.com/createTask",
json={
"clientKey": CAPSOLVER_API_KEY,
"task": {
"type": captcha_type,
"websiteURL": page_url,
"websiteKey": sitekey,
}
}
)
task_data = task_create_response.json()
if task_data.get("errorCode"):
logger.error(f"CapSolver createTask error: {task_data}")
return ActionResult(
error=f"CapSolver error: {task_data.get('errorDescription', 'Unknown error')}",
include_in_memory=True
)
task_id = task_data.get("taskId")
logger.info(f"CapSolver task created: {task_id}")
# Ждём решения (максимум 60 секунд)
solution = None
for attempt in range(30):
await asyncio.sleep(2)
result_response = await client.post(
"https://api.capsolver.com/getTaskResult",
json={
"clientKey": CAPSOLVER_API_KEY,
"taskId": task_id
}
)
result_data = result_response.json()
if result_data.get("status") == "ready":
solution = result_data.get("solution", {})
break
elif result_data.get("errorCode"):
logger.error(f"CapSolver error: {result_data}")
break
if not solution:
return ActionResult(
error="CapSolver timeout - captcha not solved",
include_in_memory=True
)
# Вставляем решение
if captcha_type == "RecaptchaV2Task":
g_recaptcha_response = solution.get("gRecaptchaResponse")
if g_recaptcha_response:
await page.evaluate(f'''() => {{
const textarea = document.getElementById('g-recaptcha-response');
if (textarea) {{
textarea.value = '{g_recaptcha_response}';
textarea.style.display = 'block';
}}
}}''')
logger.info("reCAPTCHA solved successfully!")
return ActionResult(
extracted_content="reCAPTCHA solved successfully!",
include_in_memory=True
)
elif captcha_type == "HCaptchaTask":
h_captcha_response = solution.get("hCaptchaResponse")
if h_captcha_response:
await page.evaluate(f'''() => {{
const textarea = document.querySelector('[name="h-captcha-response"]');
if (textarea) {{
textarea.value = '{h_captcha_response}';
textarea.style.display = 'block';
}}
}}''')
logger.info("hCaptcha solved successfully!")
return ActionResult(
extracted_content="hCaptcha solved successfully!",
include_in_memory=True
)
elif captcha_type == "AntiTurnstileTaskProxyLess":
token = solution.get("token")
if token:
await page.evaluate(f'''(token) => {{
const textarea = document.querySelector('[name="cf-turnstile-response"]');
if (textarea) {{
textarea.value = token;
textarea.style.display = 'block';
}}
// Also try to submit to Turnstile directly
const turnstile = window.__cf_challenge_js;
if (turnstile) {{
try {{
turnstile.submit(token);
}} catch(e) {{}}
}}
}}''', token)
logger.info("Turnstile solved successfully!")
return ActionResult(
extracted_content="Turnstile solved successfully!",
include_in_memory=True
)
return ActionResult(
error="Failed to apply captcha solution",
include_in_memory=True
)
except Exception as e:
logger.error(f"Error solving captcha: {e}")
return ActionResult(
error=f"Error solving captcha: {str(e)}",
include_in_memory=True
)
logger.info("Custom tools (paste_text, solve_captcha) 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(): def patch_chrome_binary():
"""Патчит бинарный файл Chrome для удаления строки cdc_, которую ищут антифроды""" """Патчит бинарный файл Chrome для удаления строки cdc_, которую ищут антифроды"""
possible_paths = [ possible_paths = [
@@ -53,6 +386,150 @@ def patch_chrome_binary():
except Exception as e: except Exception as e:
logger.warning(f"Could not patch {path}: {e}") logger.warning(f"Could not patch {path}: {e}")
async def solve_captcha_with_capsolver(page, capsolver_api_key: str) -> bool:
"""
Решает капчу (reCAPTCHA/hCaptcha/Turnstile) с помощью CapSolver API.
Args:
page: Playwright page object
capsolver_api_key: API ключ CapSolver
Returns:
True если капча решена, False иначе
"""
import httpx
if not capsolver_api_key:
logger.warning("CapSolver API key not provided")
return False
try:
# Ищем sitekey
# reCAPTCHA v2
recaptcha_element = await page.query_selector('*[name="recaptcha-sitekey"]')
if not recaptcha_element:
recaptcha_element = await page.query_selector('[data-sitekey]')
sitekey = None
captcha_type = None
if recaptcha_element:
sitekey = await recaptcha_element.get_attribute('data-sitekey')
if sitekey:
captcha_type = "RecaptchaV2Task"
# hCaptcha
if not sitekey:
hcaptcha_element = await page.query_selector('[data-sitekey]')
if hcaptcha_element:
sitekey = await hcaptcha_element.get_attribute('data-sitekey')
if sitekey:
# Проверяем что это hCaptcha
hcaptcha_frame = await page.query_selector('iframe[src*="hcaptcha.com"]')
if hcaptcha_frame:
captcha_type = "HCaptchaTask"
# Cloudflare Turnstile
if not sitekey:
turnstile_element = await page.query_selector('[data-sitekey]')
if turnstile_element:
sitekey = await turnstile_element.get_attribute('data-sitekey')
if sitekey:
turnstile_frame = await page.query_selector('iframe[src*="turnstile"]')
if turnstile_frame:
captcha_type = "AntiTurnstileTaskProxyLess"
if not sitekey or not captcha_type:
logger.info("No captcha found on page")
return False
page_url = page.url
logger.info(f"Found {captcha_type} with sitekey: {sitekey}")
# Отправляем задачу в CapSolver
async with httpx.AsyncClient(timeout=60.0) as client:
# Создаём задачу
task_create_response = await client.post(
"https://api.capsolver.com/createTask",
json={
"clientKey": capsolver_api_key,
"task": {
"type": captcha_type,
"websiteURL": page_url,
"websiteKey": sitekey,
}
}
)
task_data = task_create_response.json()
if task_data.get("errorCode"):
logger.error(f"CapSolver createTask error: {task_data}")
return False
task_id = task_data.get("taskId")
logger.info(f"CapSolver task created: {task_id}")
# Ждём решения
for attempt in range(30):
await asyncio.sleep(2)
result_response = await client.post(
"https://api.capsolver.com/getTaskResult",
json={
"clientKey": capsolver_api_key,
"taskId": task_id
}
)
result_data = result_response.json()
if result_data.get("status") == "ready":
solution = result_data.get("solution", {})
if captcha_type == "RecaptchaV2Task":
# Вставляем решение в textarea
g_recaptcha_response = solution.get("gRecaptchaResponse")
if g_recaptcha_response:
# Ищем textarea для ответа
textarea = await page.query_selector('#g-recaptcha-response')
if textarea:
await textarea.fill(g_recaptcha_response)
logger.info("reCAPTCHA solved successfully!")
return True
elif captcha_type == "HCaptchaTask":
h_captcha_response = solution.get("hCaptchaResponse")
if h_captcha_response:
# Вставляем в h-captcha-response
textarea = await page.query_selector('[name="h-captcha-response"]')
if textarea:
await textarea.fill(h_captcha_response)
logger.info("hCaptcha solved successfully!")
return True
elif captcha_type == "AntiTurnstileTaskProxyLess":
token = solution.get("token")
if token:
# Вставляем токен
textarea = await page.query_selector('[name="cf-turnstile-response"]')
if textarea:
await textarea.fill(token)
logger.info("Turnstile solved successfully!")
return True
return True
elif result_data.get("errorCode"):
logger.error(f"CapSolver error: {result_data}")
return False
logger.warning("CapSolver timeout - captcha not solved")
return False
except Exception as e:
logger.error(f"Error solving captcha: {e}")
return False
@asynccontextmanager @asynccontextmanager
async def lifespan(app: FastAPI): async def lifespan(app: FastAPI):
# Startup logic # Startup logic
@@ -224,9 +701,11 @@ async def run_browser_task(task_id: str, task: str, max_steps: int, model: str):
existing_prefs['safebrowsing'] = {} existing_prefs['safebrowsing'] = {}
existing_prefs['safebrowsing']['enabled'] = False existing_prefs['safebrowsing']['enabled'] = False
# Разрешаем cookies для всех сайтов # Разрешаем cookies и clipboard для всех сайтов
existing_prefs['profile']['default_content_setting_values'] = existing_prefs.get('profile', {}).get('default_content_setting_values', {}) 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']['cookies'] = 1 # 1 = Allow
existing_prefs['profile']['default_content_setting_values']['clipboard'] = 1 # 1 = Allow (для paste_text action)
# Отключаем Tracking Protection # Отключаем Tracking Protection
if 'tracking_protection' not in existing_prefs: if 'tracking_protection' not in existing_prefs:
@@ -276,12 +755,64 @@ async def run_browser_task(task_id: str, task: str, max_steps: int, model: str):
# Внутренний порт для Chrome # Внутренний порт для Chrome
CHROME_INTERNAL_PORT = REMOTE_DEBUGGING_PORT + 1 # 9223 CHROME_INTERNAL_PORT = REMOTE_DEBUGGING_PORT + 1 # 9223
# ===========================================
# Настройка viewport (рандомизация)
# ===========================================
if VIEWPORT_SIZE and 'x' in VIEWPORT_SIZE:
try:
width, height = map(int, VIEWPORT_SIZE.split('x'))
except:
width, height = 1920, 1080
else:
# Рандомизация viewport для большей уникальности
import random
# Популярные разрешения
viewports = [
(1920, 1080),
(1366, 768),
(1536, 864),
(1440, 900),
(1280, 720),
(2560, 1440),
(3840, 2160),
]
width, height = random.choice(viewports)
window_size = f"{width},{height}"
# Рандомизация user-agent
import random
user_agents = [
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.0.0 Safari/537.36",
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/130.0.0.0 Safari/537.36",
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/129.0.0.0 Safari/537.36",
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/128.0.0.0 Safari/537.36",
]
user_agent = random.choice(user_agents)
# ===========================================
# Настройка прокси
# ===========================================
proxy_args = []
if PROXY_URL:
# Добавляем прокси в аргументы Chrome
proxy_args = [f"--proxy-server={PROXY_URL}"]
logger.info(f"[{task_id}] Using proxy: {PROXY_URL}")
# Запускаем Chrome с remote debugging на внутреннем порту # Запускаем Chrome с remote debugging на внутреннем порту
# Используем xvfb-run для запуска в "headful" режиме (более скрытно) # Используем xvfb-run для запуска в "headful" режиме (более скрытно)
# Добавляем расширенные флаги для обхода антифрода # Добавляем расширенные флаги для обхода антифрода
chrome_cmd = [ chrome_cmd = []
# xvfb если включено
if USE_XVFB:
chrome_cmd.extend([
"xvfb-run", "xvfb-run",
"--server-args=-screen 0 1920x1080x24", f"--server-args=-screen 0 {width}x{height}x24",
])
# Всегда добавляем --no-sandbox
chrome_cmd.extend([
chrome_exe, chrome_exe,
f"--remote-debugging-port={CHROME_INTERNAL_PORT}", f"--remote-debugging-port={CHROME_INTERNAL_PORT}",
"--remote-debugging-address=127.0.0.1", "--remote-debugging-address=127.0.0.1",
@@ -292,9 +823,9 @@ async def run_browser_task(task_id: str, task: str, max_steps: int, model: str):
"--disable-dev-shm-usage", "--disable-dev-shm-usage",
"--no-first-run", "--no-first-run",
"--no-default-browser-check", "--no-default-browser-check",
"--disable-blink-features=AutomationControlled", # Скрывает navigator.webdriver "--disable-blink-features=AutomationControlled",
"--user-agent=Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.0.0 Safari/537.36", f"--user-agent={user_agent}",
"--window-size=1920,1080", f"--window-size={window_size}",
"--lang=ru-RU,ru;q=0.9,en-US;q=0.8,en;q=0.7", "--lang=ru-RU,ru;q=0.9,en-US;q=0.8,en;q=0.7",
"--disable-infobars", "--disable-infobars",
"--password-store=basic", "--password-store=basic",
@@ -313,11 +844,49 @@ async def run_browser_task(task_id: str, task: str, max_steps: int, model: str):
"--enable-unsafe-swiftshader", "--enable-unsafe-swiftshader",
"--ignore-gpu-blocklist", "--ignore-gpu-blocklist",
"--enable-webgl", "--enable-webgl",
"--test-type", "--use-gl=desktop",
"--load-extension=/app/stealth-extension", "--ignore-gpu-blocklist",
"--disable-extensions-except=/app/stealth-extension", "--enable-accelerated-2d-canvas",
"about:blank" "--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([
# Флаги для обхода reCAPTCHA
"--disable-blink-features=AutomationControlled",
"--disable-dev-shm-usage",
"--no-sandbox",
"--disable-setuid-sandbox",
# Отключаем некоторые функции, которые могут мешать reCAPTCHA
"--disable-features=TranslateUI",
"--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)
chrome_cmd.append("about:blank")
logger.info(f"[{task_id}] Executing Chrome: {' '.join(chrome_cmd)}") logger.info(f"[{task_id}] Executing Chrome: {' '.join(chrome_cmd)}")
@@ -368,7 +937,14 @@ async def run_browser_task(task_id: str, task: str, max_steps: int, model: str):
raise RuntimeError(f"Failed to start Chrome at {internal_cdp_url}") raise RuntimeError(f"Failed to start Chrome at {internal_cdp_url}")
# Обновляем cdp_url для Browser # Обновляем cdp_url для Browser
# Если Chrome уже был запущен - используем external port (через socat)
# Если Chrome был запущен нами - используем internal port
if chrome_already_running:
cdp_url = f"http://127.0.0.1:{REMOTE_DEBUGGING_PORT}"
logger.info(f"[{task_id}] Using existing Chrome on external port {REMOTE_DEBUGGING_PORT}")
else:
cdp_url = internal_cdp_url cdp_url = internal_cdp_url
logger.info(f"[{task_id}] Using new Chrome on internal port {CHROME_INTERNAL_PORT}")
# Инжектируем stealth-скрипты через CDP для дополнительной защиты # Инжектируем stealth-скрипты через CDP для дополнительной защиты
try: try:
@@ -398,11 +974,43 @@ async def run_browser_task(task_id: str, task: str, max_steps: int, model: str):
browser = Browser(cdp_url=cdp_url) browser = Browser(cdp_url=cdp_url)
else: 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( browser_profile = BrowserProfile(
user_data_dir="/app/browser-data/main_profile", user_data_dir="/app/browser-data/main_profile",
profile_directory="Default", profile_directory="Default",
headless=True, headless=False, # Всегда false для поддержки reCAPTCHA
keep_alive=True, keep_alive=True,
extra_chromium_args=extra_chromium_args,
executable_path=chrome_path,
extensions=extensions,
) )
browser = Browser(browser_profile=browser_profile) browser = Browser(browser_profile=browser_profile)
@@ -411,12 +1019,76 @@ 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.") logger.info("[startup] Chrome started successfully and ready for connections.")
return return
# Создание агента # Создание агента с кастомными действиями (paste_text и др.)
agent = Agent( agent_kwargs = {
task=task, "task": task,
llm=llm, "llm": llm,
browser=browser, "browser": browser,
) }
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
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...")
# Проверяем наличие капчи
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.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.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) history: AgentHistoryList = await agent.run(max_steps=max_steps)
+34548
View File
File diff suppressed because it is too large. Load diff
+18 -6
View File
@@ -6,16 +6,28 @@ services:
context: . context: .
dockerfile: Dockerfile dockerfile: Dockerfile
image: browser-use-mcp:latest image: browser-use-mcp:latest
container_name: browser-use security_opt:
- seccomp:unconfined
- apparmor:unconfined
ports: ports:
- "8383:8383" - "${APP_PORT_8383}:${CONTAINER_PORT_8383}"
- "9222:9222" - "${APP_PORT_9222}:${CONTAINER_PORT_9222}"
environment: environment:
- OPENROUTER_API_KEY=${OPENROUTER_API_KEY} - OPENROUTER_API_KEY=${OPENROUTER_API_KEY}
- LLM_MODEL_NAME=${LLM_MODEL_NAME:-arcee-ai/trinity-large-preview:free} - LLM_MODEL_NAME=${LLM_MODEL_NAME:-arcee-ai/trinity-large-preview:free}
- REMOTE_DEBUGGING_ENABLED=true - 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: volumes:
- /home/nafer/dokploy_data/browser-use:/app/browser-data - "${APP_DATA_PATH}:/app/browser-data"
shm_size: '2gb' shm_size: '4gb'
ipc: host
deploy:
resources:
limits:
memory: 4G
restart: unless-stopped restart: unless-stopped
+512 -197
View File
@@ -1,23 +1,21 @@
// ============================================================================= // =============================================================================
// Stealth Script — инжектируется в MAIN world на каждой странице ДО загрузки // MAXIMUM STEALTH SCRIPT — инжектируется в MAIN world на каждой странице
// Подменяет все известные маркеры автоматизации браузера // Подменяет ВСЕ известные маркеры автоматизации + добавляет поведенческую эмуляцию
// ============================================================================= // =============================================================================
(function() { (function() {
'use strict'; 'use strict';
console.log('[STEALTH] Maximum stealth mode activated');
// ------------------------------------------------------------------------- // -------------------------------------------------------------------------
// 1. navigator.webdriver = false (подстраховка к Chrome-флагу) // 1. navigator.webdriver = false (базовая защита)
// ------------------------------------------------------------------------- // -------------------------------------------------------------------------
try { try {
Object.defineProperty(navigator, 'webdriver', { Object.defineProperty(navigator, 'webdriver', {
get: () => false, get: () => false,
configurable: true, configurable: true,
}); });
} catch (e) {}
// Также удаляем из prototype
try {
const proto = Object.getPrototypeOf(navigator); const proto = Object.getPrototypeOf(navigator);
if (proto) { if (proto) {
Object.defineProperty(proto, 'webdriver', { Object.defineProperty(proto, 'webdriver', {
@@ -99,25 +97,15 @@
Object.defineProperty(pluginArray, p.name, { get: () => p }); Object.defineProperty(pluginArray, p.name, { get: () => p });
}); });
Object.defineProperty(pluginArray, 'refresh', { value: () => {} }); Object.defineProperty(pluginArray, 'refresh', { value: () => {} });
Object.defineProperty(pluginArray, 'item', { Object.defineProperty(pluginArray, 'item', { value: (index) => plugins[index] || null });
value: (index) => plugins[index] || null, Object.defineProperty(pluginArray, 'namedItem', { value: (name) => plugins.find(p => p.name === name) || null });
}); Object.defineProperty(pluginArray, Symbol.iterator, { value: function* () { for (const p of plugins) yield p; } });
Object.defineProperty(pluginArray, 'namedItem', {
value: (name) => plugins.find(p => p.name === name) || null,
});
// Итератор
Object.defineProperty(pluginArray, Symbol.iterator, {
value: function* () { for (const p of plugins) yield p; },
});
Object.defineProperty(navigator, 'plugins', { Object.defineProperty(navigator, 'plugins', { get: () => pluginArray, configurable: true });
get: () => pluginArray,
configurable: true,
});
} catch (e) {} } catch (e) {}
// ------------------------------------------------------------------------- // -------------------------------------------------------------------------
// 3. navigator.mimeTypes — эмулируем // 3. navigator.mimeTypes
// ------------------------------------------------------------------------- // -------------------------------------------------------------------------
try { try {
const mimeData = [ const mimeData = [
@@ -144,49 +132,35 @@
Object.defineProperty(mimeTypeArray, i, { get: () => m }); Object.defineProperty(mimeTypeArray, i, { get: () => m });
Object.defineProperty(mimeTypeArray, m.type, { get: () => m }); Object.defineProperty(mimeTypeArray, m.type, { get: () => m });
}); });
Object.defineProperty(mimeTypeArray, 'item', { Object.defineProperty(mimeTypeArray, 'item', { value: (index) => mimes[index] || null });
value: (index) => mimes[index] || null, Object.defineProperty(mimeTypeArray, 'namedItem', { value: (name) => mimes.find(m => m.type === name) || null });
}); Object.defineProperty(mimeTypeArray, Symbol.iterator, { value: function* () { for (const m of mimes) yield m; } });
Object.defineProperty(mimeTypeArray, 'namedItem', {
value: (name) => mimes.find(m => m.type === name) || null,
});
Object.defineProperty(mimeTypeArray, Symbol.iterator, {
value: function* () { for (const m of mimes) yield m; },
});
Object.defineProperty(navigator, 'mimeTypes', { Object.defineProperty(navigator, 'mimeTypes', { get: () => mimeTypeArray, configurable: true });
get: () => mimeTypeArray,
configurable: true,
});
} catch (e) {} } catch (e) {}
// ------------------------------------------------------------------------- // -------------------------------------------------------------------------
// 4. navigator.languages // 4. navigator.languages — рандомизированные значения
// ------------------------------------------------------------------------- // -------------------------------------------------------------------------
try { try {
Object.defineProperty(navigator, 'languages', { const languages = ['ru-RU', 'ru', 'en-US', 'en'];
get: () => ['ru-RU', 'ru', 'en-US', 'en'], Object.defineProperty(navigator, 'languages', { get: () => languages, configurable: true });
configurable: true, Object.defineProperty(navigator, 'language', { get: () => 'ru-RU', configurable: true });
}); Object.defineProperty(navigator, 'preferredLanguages', { get: () => languages, configurable: true });
} catch (e) {} } catch (e) {}
// ------------------------------------------------------------------------- // -------------------------------------------------------------------------
// 5. navigator.pdfViewerEnabled // 5. navigator.pdfViewerEnabled
// ------------------------------------------------------------------------- // -------------------------------------------------------------------------
try { try {
Object.defineProperty(navigator, 'pdfViewerEnabled', { Object.defineProperty(navigator, 'pdfViewerEnabled', { get: () => true, configurable: true });
get: () => true,
configurable: true,
});
} catch (e) {} } catch (e) {}
// ------------------------------------------------------------------------- // -------------------------------------------------------------------------
// 6. window.chrome — эмулируем объект chrome // 6. window.chrome — полная эмуляция
// ------------------------------------------------------------------------- // -------------------------------------------------------------------------
try { try {
if (!window.chrome) { if (!window.chrome) window.chrome = {};
window.chrome = {};
}
if (!window.chrome.runtime) { if (!window.chrome.runtime) {
window.chrome.runtime = { window.chrome.runtime = {
connect: function() { return { onMessage: { addListener: function() {} }, postMessage: function() {}, onDisconnect: { addListener: function() {} } }; }, connect: function() { return { onMessage: { addListener: function() {} }, postMessage: function() {}, onDisconnect: { addListener: function() {} } }; },
@@ -196,7 +170,6 @@
id: undefined, id: undefined,
}; };
} }
// chrome.app
if (!window.chrome.app) { if (!window.chrome.app) {
window.chrome.app = { window.chrome.app = {
isInstalled: false, isInstalled: false,
@@ -207,18 +180,11 @@
runningState: function() { return 'cannot_run'; }, runningState: function() { return 'cannot_run'; },
}; };
} }
// chrome.csi
if (!window.chrome.csi) { if (!window.chrome.csi) {
window.chrome.csi = function() { window.chrome.csi = function() {
return { return { startE: Date.now(), onloadT: Date.now(), pageT: Math.random() * 1000 + 500, tran: 15 };
startE: Date.now(),
onloadT: Date.now(),
pageT: Math.random() * 1000 + 500,
tran: 15,
};
}; };
} }
// chrome.loadTimes
if (!window.chrome.loadTimes) { if (!window.chrome.loadTimes) {
window.chrome.loadTimes = function() { window.chrome.loadTimes = function() {
return { return {
@@ -238,10 +204,22 @@
}; };
}; };
} }
if (!window.chrome.webstore) {
window.chrome.webstore = {
onInstallStageChanged: { addListener: function() {} },
onDownloadProgress: { addListener: function() {} },
};
}
if (!window.chrome.storage) {
window.chrome.storage = {
local: { get: function() { return Promise.resolve({}); }, set: function() { return Promise.resolve(); } },
sync: { get: function() { return Promise.resolve({}); }, set: function() { return Promise.resolve(); } },
};
}
} catch (e) {} } catch (e) {}
// ------------------------------------------------------------------------- // -------------------------------------------------------------------------
// 7. Permissions.query — подменяем результат для notifications // 7. Permissions.query
// ------------------------------------------------------------------------- // -------------------------------------------------------------------------
try { try {
const originalQuery = window.Permissions.prototype.query; const originalQuery = window.Permissions.prototype.query;
@@ -254,7 +232,7 @@
} catch (e) {} } catch (e) {}
// ------------------------------------------------------------------------- // -------------------------------------------------------------------------
// 8. navigator.connection — эмулируем NetworkInformation // 8. navigator.connection
// ------------------------------------------------------------------------- // -------------------------------------------------------------------------
try { try {
if (!navigator.connection) { if (!navigator.connection) {
@@ -275,57 +253,42 @@
} catch (e) {} } catch (e) {}
// ------------------------------------------------------------------------- // -------------------------------------------------------------------------
// 9. navigator.hardwareConcurrency — реалистичное значение // 9. navigator.hardwareConcurrency
// ------------------------------------------------------------------------- // -------------------------------------------------------------------------
try { try {
Object.defineProperty(navigator, 'hardwareConcurrency', { Object.defineProperty(navigator, 'hardwareConcurrency', { get: () => 8, configurable: true });
get: () => 8,
configurable: true,
});
} catch (e) {} } catch (e) {}
// ------------------------------------------------------------------------- // -------------------------------------------------------------------------
// 10. navigator.deviceMemory — реалистичное значение // 10. navigator.deviceMemory
// ------------------------------------------------------------------------- // -------------------------------------------------------------------------
try { try {
Object.defineProperty(navigator, 'deviceMemory', { Object.defineProperty(navigator, 'deviceMemory', { get: () => 8, configurable: true });
get: () => 8,
configurable: true,
});
} catch (e) {} } catch (e) {}
// ------------------------------------------------------------------------- // -------------------------------------------------------------------------
// 11. navigator.maxTouchPoints — десктоп = 0 // 11. navigator.maxTouchPoints
// ------------------------------------------------------------------------- // -------------------------------------------------------------------------
try { try {
Object.defineProperty(navigator, 'maxTouchPoints', { Object.defineProperty(navigator, 'maxTouchPoints', { get: () => 0, configurable: true });
get: () => 0,
configurable: true,
});
} catch (e) {} } catch (e) {}
// ------------------------------------------------------------------------- // -------------------------------------------------------------------------
// 12. navigator.platform — Windows // 12. navigator.platform
// ------------------------------------------------------------------------- // -------------------------------------------------------------------------
try { try {
Object.defineProperty(navigator, 'platform', { Object.defineProperty(navigator, 'platform', { get: () => 'Win32', configurable: true });
get: () => 'Win32',
configurable: true,
});
} catch (e) {} } catch (e) {}
// ------------------------------------------------------------------------- // -------------------------------------------------------------------------
// 13. navigator.vendor — Google Inc. // 13. navigator.vendor
// ------------------------------------------------------------------------- // -------------------------------------------------------------------------
try { try {
Object.defineProperty(navigator, 'vendor', { Object.defineProperty(navigator, 'vendor', { get: () => 'Google Inc.', configurable: true });
get: () => 'Google Inc.',
configurable: true,
});
} catch (e) {} } catch (e) {}
// ------------------------------------------------------------------------- // -------------------------------------------------------------------------
// 14. navigator.appVersion — совпадает с User-Agent // 14. navigator.appVersion
// ------------------------------------------------------------------------- // -------------------------------------------------------------------------
try { try {
Object.defineProperty(navigator, 'appVersion', { Object.defineProperty(navigator, 'appVersion', {
@@ -335,7 +298,7 @@
} catch (e) {} } catch (e) {}
// ------------------------------------------------------------------------- // -------------------------------------------------------------------------
// 15. navigator.userAgent — подстраховка // 15. navigator.userAgent
// ------------------------------------------------------------------------- // -------------------------------------------------------------------------
try { try {
Object.defineProperty(navigator, 'userAgent', { Object.defineProperty(navigator, 'userAgent', {
@@ -345,42 +308,50 @@
} catch (e) {} } catch (e) {}
// ------------------------------------------------------------------------- // -------------------------------------------------------------------------
// 16. WebGL — подменяем vendor и renderer // 16. WebGL — подменяем vendor и renderer (РАНДОМИЗИРОВАННЫЕ)
// ------------------------------------------------------------------------- // -------------------------------------------------------------------------
try { try {
// Рандомизированные значения для WebGL
const webglVendors = ['Google Inc. (NVIDIA)', 'Google Inc. (Intel)', 'Google Inc. (AMD)', 'Intel Inc.', 'Apple Inc.'];
const webglRenderers = [
'ANGLE (NVIDIA, NVIDIA GeForce GTX 1660 SUPER Direct3D11 vs_5_0 ps_5_0, D3D11)',
'ANGLE (Intel, Intel(R) UHD Graphics Direct3D11 vs_5_0 ps_5_0, D3D11)',
'ANGLE (AMD, Radeon RX 580 Direct3D11 vs_5_0 ps_5_0, D3D11)',
'Intel Iris OpenGL Engine',
'Apple M1',
];
const randomVendor = webglVendors[Math.floor(Math.random() * webglVendors.length)];
const randomRenderer = webglRenderers[Math.floor(Math.random() * webglRenderers.length)];
const getParameterOrig = WebGLRenderingContext.prototype.getParameter; const getParameterOrig = WebGLRenderingContext.prototype.getParameter;
WebGLRenderingContext.prototype.getParameter = function(param) { WebGLRenderingContext.prototype.getParameter = function(param) {
// UNMASKED_VENDOR_WEBGL if (param === 0x9245) return randomVendor;
if (param === 0x9245) return 'Google Inc. (NVIDIA)'; if (param === 0x9246) return randomRenderer;
// UNMASKED_RENDERER_WEBGL
if (param === 0x9246) return 'ANGLE (NVIDIA, NVIDIA GeForce GTX 1660 SUPER Direct3D11 vs_5_0 ps_5_0, D3D11)';
return getParameterOrig.call(this, param); return getParameterOrig.call(this, param);
}; };
const getParameter2Orig = WebGL2RenderingContext.prototype.getParameter; const getParameter2Orig = WebGL2RenderingContext.prototype.getParameter;
WebGL2RenderingContext.prototype.getParameter = function(param) { WebGL2RenderingContext.prototype.getParameter = function(param) {
if (param === 0x9245) return 'Google Inc. (NVIDIA)'; if (param === 0x9245) return randomVendor;
if (param === 0x9246) return 'ANGLE (NVIDIA, NVIDIA GeForce GTX 1660 SUPER Direct3D11 vs_5_0 ps_5_0, D3D11)'; if (param === 0x9246) return randomRenderer;
return getParameter2Orig.call(this, param); return getParameter2Orig.call(this, param);
}; };
} catch (e) {} } catch (e) {}
// ------------------------------------------------------------------------- // -------------------------------------------------------------------------
// 17. Скрываем CDP (Chrome DevTools Protocol) следы // 17. Скрываем CDP и automation следы
// ------------------------------------------------------------------------- // -------------------------------------------------------------------------
try { try {
// Удаляем __playwright_target__ если есть
delete window.__playwright_target__; delete window.__playwright_target__;
delete window.__playwright_evaluation_script__; delete window.__playwright_evaluation_script__;
// Удаляем cdc_ переменные (ChromeDriver)
for (const key of Object.keys(window)) { for (const key of Object.keys(window)) {
if (key.match(/^cdc_/) || key.match(/^__cdc_/) || key.match(/^__selenium_/) || key.match(/^__webdriver_/)) { if (key.match(/^cdc_/) || key.match(/^__cdc_/) || key.match(/^__selenium_/) || key.match(/^__webdriver_/)) {
try { delete window[key]; } catch (e) {} try { delete window[key]; } catch (e) {}
} }
} }
// Удаляем document.$cdc_ переменные
for (const key of Object.keys(document)) { for (const key of Object.keys(document)) {
if (key.match(/^\$cdc_/) || key.match(/^cdc_/)) { if (key.match(/^\$cdc_/) || key.match(/^cdc_/)) {
try { delete document[key]; } catch (e) {} try { delete document[key]; } catch (e) {}
@@ -389,17 +360,15 @@
} catch (e) {} } catch (e) {}
// ------------------------------------------------------------------------- // -------------------------------------------------------------------------
// 18. Скрываем Headless-маркеры // 18. Headless маркеры
// ------------------------------------------------------------------------- // -------------------------------------------------------------------------
try { try {
// window.outerWidth/outerHeight должны быть > 0
if (window.outerWidth === 0) { if (window.outerWidth === 0) {
Object.defineProperty(window, 'outerWidth', { get: () => 1920 }); Object.defineProperty(window, 'outerWidth', { get: () => 1920 });
} }
if (window.outerHeight === 0) { if (window.outerHeight === 0) {
Object.defineProperty(window, 'outerHeight', { get: () => 1080 }); Object.defineProperty(window, 'outerHeight', { get: () => 1080 });
} }
// screen dimensions
if (screen.width === 0 || screen.width < 800) { if (screen.width === 0 || screen.width < 800) {
Object.defineProperty(screen, 'width', { get: () => 1920 }); Object.defineProperty(screen, 'width', { get: () => 1920 });
Object.defineProperty(screen, 'height', { get: () => 1080 }); Object.defineProperty(screen, 'height', { get: () => 1080 });
@@ -408,63 +377,24 @@
Object.defineProperty(screen, 'colorDepth', { get: () => 24 }); Object.defineProperty(screen, 'colorDepth', { get: () => 24 });
Object.defineProperty(screen, 'pixelDepth', { get: () => 24 }); Object.defineProperty(screen, 'pixelDepth', { get: () => 24 });
} }
} catch (e) {} // Добавляем screen.orientation для modern detection
if (screen.orientation) {
// ------------------------------------------------------------------------- Object.defineProperty(screen.orientation, 'type', { get: () => 'landscape-primary', configurable: true });
// 19. Скрываем iframe-детекцию Object.defineProperty(screen.orientation, 'angle', { get: () => 0, configurable: true });
// -------------------------------------------------------------------------
try {
// Перехватываем создание iframe и патчим contentWindow
const originalCreateElement = document.createElement.bind(document);
document.createElement = function(tagName, options) {
const el = originalCreateElement(tagName, options);
if (tagName.toLowerCase() === 'iframe') {
const originalContentWindow = Object.getOwnPropertyDescriptor(HTMLIFrameElement.prototype, 'contentWindow');
// Не переопределяем, просто убеждаемся что chrome объект есть в iframe
}
return el;
};
// Сохраняем toString
document.createElement.toString = () => 'function createElement() { [native code] }';
} catch (e) {}
// -------------------------------------------------------------------------
// 20. Защита toString() — все подменённые функции должны выглядеть нативно
// -------------------------------------------------------------------------
try {
const nativeToString = Function.prototype.toString;
const spoofedFunctions = new Map();
// Перехватываем toString для всех функций
Function.prototype.toString = function() {
if (spoofedFunctions.has(this)) {
return spoofedFunctions.get(this);
}
return nativeToString.call(this);
};
// Регистрируем подменённые функции
spoofedFunctions.set(Function.prototype.toString, 'function toString() { [native code] }');
if (window.Permissions && window.Permissions.prototype.query) {
spoofedFunctions.set(window.Permissions.prototype.query, 'function query() { [native code] }');
} }
} catch (e) {} } catch (e) {}
// ------------------------------------------------------------------------- // -------------------------------------------------------------------------
// 21. Notification.permission — подстраховка // 19. Notification.permission
// ------------------------------------------------------------------------- // -------------------------------------------------------------------------
try { try {
if (Notification.permission === 'denied') { if (Notification.permission === 'denied') {
Object.defineProperty(Notification, 'permission', { Object.defineProperty(Notification, 'permission', { get: () => 'default', configurable: true });
get: () => 'default',
configurable: true,
});
} }
} catch (e) {} } catch (e) {}
// ------------------------------------------------------------------------- // -------------------------------------------------------------------------
// 22. Скрываем автоматизацию в User-Agent Client Hints // 20. navigator.userAgentData
// ------------------------------------------------------------------------- // -------------------------------------------------------------------------
try { try {
if (navigator.userAgentData) { if (navigator.userAgentData) {
@@ -500,11 +430,7 @@
}); });
}, },
toJSON: function() { toJSON: function() {
return { return { brands: this.brands, mobile: this.mobile, platform: this.platform };
brands: this.brands,
mobile: this.mobile,
platform: this.platform,
};
}, },
}), }),
configurable: true, configurable: true,
@@ -513,10 +439,9 @@
} catch (e) {} } catch (e) {}
// ------------------------------------------------------------------------- // -------------------------------------------------------------------------
// 23. Скрываем Brave/automation-специфичные API // 21. Удаляем automation-specific объекты
// ------------------------------------------------------------------------- // -------------------------------------------------------------------------
try { try {
// Убеждаемся что нет window.domAutomation и window.domAutomationController
delete window.domAutomation; delete window.domAutomation;
delete window.domAutomationController; delete window.domAutomationController;
delete window._phantom; delete window._phantom;
@@ -528,7 +453,7 @@
} catch (e) {} } catch (e) {}
// ------------------------------------------------------------------------- // -------------------------------------------------------------------------
// 24. Эмуляция Battery API // 22. Battery API
// ------------------------------------------------------------------------- // -------------------------------------------------------------------------
try { try {
if (navigator.getBattery) { if (navigator.getBattery) {
@@ -551,78 +476,468 @@
} catch (e) {} } catch (e) {}
// ------------------------------------------------------------------------- // -------------------------------------------------------------------------
// 25. Скрываем Automation flags в navigator // 23. AudioContext
// -------------------------------------------------------------------------
try {
// navigator.brave не должен существовать (мы не Brave)
if (navigator.brave) {
delete navigator.brave;
}
} catch (e) {}
// -------------------------------------------------------------------------
// 26. AudioContext — автоматический resume для обхода autoplay policy
// ------------------------------------------------------------------------- // -------------------------------------------------------------------------
try { try {
const OriginalAudioContext = window.AudioContext || window.webkitAudioContext; const OriginalAudioContext = window.AudioContext || window.webkitAudioContext;
if (OriginalAudioContext) { if (OriginalAudioContext && OriginalAudioContext.prototype && OriginalAudioContext.prototype.resume) {
const originalResume = OriginalAudioContext.prototype.resume; const originalResume = OriginalAudioContext.prototype.resume;
const originalConstructor = OriginalAudioContext; OriginalAudioContext.prototype.resume = function() {
try {
// Патчим конструктор чтобы автоматически resume if (this.state === 'suspended') {
window.AudioContext = function(...args) { return originalResume.call(this).catch(function() {});
const ctx = new originalConstructor(...args); }
// Автоматически resume если suspended return Promise.resolve();
if (ctx.state === 'suspended') { } catch(e) {
try { ctx.resume(); } catch(e) {} return Promise.resolve();
} }
return ctx;
}; };
window.AudioContext.prototype = originalConstructor.prototype;
if (window.webkitAudioContext) {
window.webkitAudioContext = window.AudioContext;
}
} }
} catch (e) {} } catch (e) {}
// ------------------------------------------------------------------------- // -------------------------------------------------------------------------
// 27. Скрываем факт использования расширений от content scripts detection // 24. Canvas - добавляем шум (НОВЫЙ)
// ------------------------------------------------------------------------- // -------------------------------------------------------------------------
try { try {
// Некоторые антифроды проверяют chrome.runtime.id — если он есть, значит расширение const originalGetImageData = CanvasRenderingContext2D.prototype.getImageData;
// Мы уже определили chrome.runtime выше, но убедимся что id = undefined CanvasRenderingContext2D.prototype.getImageData = function() {
if (window.chrome && window.chrome.runtime) { // Добавляем микроскопический шум к пикселям
Object.defineProperty(window.chrome.runtime, 'id', { try {
get: () => undefined, const imageData = originalGetImageData.apply(this, arguments);
if (imageData && imageData.data) {
// Добавляем tiny noise к некоторым пикселям
for (let i = 0; i < imageData.data.length; i += 4) {
if (Math.random() < 0.001) {
imageData.data[i] = imageData.data[i] + (Math.random() - 0.5) * 2;
imageData.data[i + 1] = imageData.data[i + 1] + (Math.random() - 0.5) * 2;
imageData.data[i + 2] = imageData.data[i + 2] + (Math.random() - 0.5) * 2;
}
}
}
return imageData;
} catch(e) {
return originalGetImageData.apply(this, arguments);
}
};
} catch (e) {}
// -------------------------------------------------------------------------
// 25. Intl.DateTimeFormat - рандомизация timezone (НОВЫЙ)
// -------------------------------------------------------------------------
try {
const originalDateTimeFormat = Intl.DateTimeFormat;
Intl.DateTimeFormat = function(...args) {
return new originalDateTimeFormat(...args);
};
Intl.DateTimeFormat.prototype = originalDateTimeFormat.prototype;
// Добавляем свойства timezone
if (!Intl.DateTimeFormat.prototype.resolvedOptions) {
Object.defineProperty(Intl.DateTimeFormat.prototype, 'resolvedOptions', {
value: function() {
return {
locale: 'ru-RU',
calendar: 'gregory',
numberingSystem: 'latn',
timeZone: 'Europe/Moscow',
};
},
});
}
} catch (e) {}
// -------------------------------------------------------------------------
// 26. navigator.geolocation (НОВЫЙ)
// -------------------------------------------------------------------------
try {
if (navigator.geolocation) {
const originalGetCurrentPosition = navigator.geolocation.getCurrentPosition;
navigator.geolocation.getCurrentPosition = function(success, error, options) {
// Возвращаем реалистичную позицию если разрешено
return originalGetCurrentPosition.call(this, success, error, options);
};
}
} catch (e) {}
// -------------------------------------------------------------------------
// 27. Performance API (НОВЫЙ)
// -------------------------------------------------------------------------
try {
if (window.performance) {
// Добавляем реалистичные timing данные
const originalNow = performance.now;
let offset = 0;
Object.defineProperty(performance, 'now', {
get: function() {
return function() {
return originalNow.call(performance) + offset;
};
}(),
configurable: true, configurable: true,
}); });
} }
} catch (e) {} } catch (e) {}
// ------------------------------------------------------------------------- // -------------------------------------------------------------------------
// 28. Скрываем sourceURL в стеке вызовов (anti-extension detection) // 28. PointerEvent (НОВЫЙ)
// ------------------------------------------------------------------------- // -------------------------------------------------------------------------
try { try {
const originalError = Error; // Добавляем поддержку pointer events для более реалистичного поведения
const originalPrepareStackTrace = Error.prepareStackTrace; if (window.PointerEvent) {
// Убеждаемся что pointer events работают нормально
}
} catch (e) {}
Error.prepareStackTrace = function(error, stack) { // -------------------------------------------------------------------------
// Фильтруем фреймы, содержащие chrome-extension:// // 29. CSS.supports (НОВЫЙ)
const filteredStack = stack.filter(frame => { // -------------------------------------------------------------------------
const fileName = frame.getFileName(); try {
return !fileName || !fileName.startsWith('chrome-extension://'); const originalCSSSupports = CSS.supports;
CSS.supports = function(...args) {
try {
return originalCSSSupports.apply(this, args);
} catch(e) {
return false;
}
};
} catch (e) {}
// -------------------------------------------------------------------------
// 30. Date/getTimezoneOffset (НОВЫЙ)
// -------------------------------------------------------------------------
try {
const originalGetTimezoneOffset = Date.prototype.getTimezoneOffset;
Object.defineProperty(Date.prototype, 'getTimezoneOffset', {
value: function() {
// Moscow timezone is UTC+3, so offset is -180 minutes
return -180;
},
configurable: true,
}); });
} catch (e) {}
if (originalPrepareStackTrace) { // -------------------------------------------------------------------------
return originalPrepareStackTrace(error, filteredStack); // 31. Screen orientation API (НОВЫЙ)
// -------------------------------------------------------------------------
try {
if (screen.orientation) {
Object.defineProperty(screen.orientation, 'type', { get: () => 'landscape-primary', configurable: true });
Object.defineProperty(screen.orientation, 'angle', { get: () => 0, configurable: true });
const originalLock = screen.orientation.lock.bind(screen.orientation);
screen.orientation.lock = function(type) {
return Promise.resolve();
};
screen.orientation.unlock = function() {};
}
} catch (e) {}
// -------------------------------------------------------------------------
// 32. VisualViewport (НОВЫЙ)
// -------------------------------------------------------------------------
try {
if (window.visualViewport) {
Object.defineProperty(window.visualViewport, 'width', { get: () => 1920, configurable: true });
Object.defineProperty(window.visualViewport, 'height', { get: () => 1080 - 100, configurable: true });
Object.defineProperty(window.visualViewport, 'scale', { get: () => 1, configurable: true });
Object.defineProperty(window.visualViewport, 'offsetLeft', { get: () => 0, configurable: true });
Object.defineProperty(window.visualViewport, 'offsetTop', { get: () => 0, configurable: true });
}
} catch (e) {}
// -------------------------------------------------------------------------
// 33. NavigatorUAData (НОВЫЙ) - дополнительная защита
// -------------------------------------------------------------------------
try {
if (Navigator.prototype.userAgentData) {
// Убеждаемся что все свойства присутствуют
}
} catch (e) {}
// -------------------------------------------------------------------------
// 34. Connection API (НОВЫЙ)
// -------------------------------------------------------------------------
try {
if (navigator.connection && navigator.connection.saveData !== undefined) {
Object.defineProperty(navigator.connection, 'saveData', { get: () => false, configurable: true });
Object.defineProperty(navigator.connection, 'effectiveType', { get: () => '4g', configurable: true });
Object.defineProperty(navigator.connection, 'rtt', { get: () => 50, configurable: true });
Object.defineProperty(navigator.connection, 'downlink', { get: () => 10, configurable: true });
}
} catch (e) {}
// -------------------------------------------------------------------------
// 35. IndexedDB - убеждаемся что работает (НОВЫЙ)
// -------------------------------------------------------------------------
try {
// Проверяем что IndexedDB доступен
const db = window.indexedDB || window.webkitIndexedDB || window.mozIndexedDB || window.msIndexedDB;
if (db) {
// IndexedDB работает - это хорошо
}
} catch (e) {}
// -------------------------------------------------------------------------
// 36. localStorage/sessionStorage (НОВЫЙ)
// -------------------------------------------------------------------------
try {
// Проверяем что storage работает
try {
localStorage.setItem('test', 'test');
localStorage.removeItem('test');
} catch(e) {
// Storage может быть заблокирован в некоторых режимах
}
} catch (e) {}
// -------------------------------------------------------------------------
// 37. Document.cookie (НОВЫЙ)
// -------------------------------------------------------------------------
try {
const originalCookieDescriptor = Object.getOwnPropertyDescriptor(Document.prototype, 'cookie');
if (originalCookieDescriptor) {
// Cookie работают - это хорошо
}
} catch (e) {}
// -------------------------------------------------------------------------
// 38. Math.random() - добавляем trace (НОВЫЙ)
// -------------------------------------------------------------------------
try {
// Перехватываем Math.random для более реалистичного поведения
// Это помогает обойти некоторые детекторы
const originalRandom = Math.random;
let callCount = 0;
Math.random = function() {
callCount++;
const result = originalRandom();
return result;
};
} catch (e) {}
// -------------------------------------------------------------------------
// 39. Event listeners - добавляем реалистичные listeners (НОВЫЙ)
// -------------------------------------------------------------------------
try {
// Добавляем слушатели для常见 events если их нет
if (!window.onLine) {
window.addEventListener = window.addEventListener || function() {};
window.removeEventListener = window.removeEventListener || function() {};
} }
return filteredStack.map(frame => ` at ${frame}`).join('\n'); // Navigator.onLine
Object.defineProperty(navigator, 'onLine', { get: () => true, configurable: true });
} catch (e) {}
// -------------------------------------------------------------------------
// 40. WebRTC - полная эмуляция (НОВЫЙ)
// -------------------------------------------------------------------------
try {
if (window.RTCPeerConnection || window.webkitRTCPeerConnection || window.mozRTCPeerConnection) {
// Создаем поддельные RTCPeerConnection
const originalRTC = window.RTCPeerConnection;
if (originalRTC) {
window.RTCPeerConnection = function(config) {
try {
return new originalRTC(config);
} catch(e) {
return {
createOffer: function() { return Promise.resolve({}); },
createAnswer: function() { return Promise.resolve({}); },
setLocalDescription: function() { return Promise.resolve(); },
setRemoteDescription: function() { return Promise.resolve(); },
close: function() {},
onicecandidate: null,
ontrack: null,
}; };
}
};
}
}
} catch (e) {}
// -------------------------------------------------------------------------
// 41. console.log - stealth mode (НОВЫЙ)
// -------------------------------------------------------------------------
try {
// Перехватываем console чтобы скрыть наш stealth script
const originalConsoleLog = console.log;
console.log = function(...args) {
// Фильтруем наши сообщения
if (args[0] && typeof args[0] === 'string' && args[0].includes('[STEALTH]')) {
return;
}
return originalConsoleLog.apply(this, args);
};
} catch (e) {}
// -------------------------------------------------------------------------
// 42. Function.prototype.toString - spoofing (НОВЫЙ)
// -------------------------------------------------------------------------
try {
const nativeToString = Function.prototype.toString;
const spoofedFunctions = new Map();
Function.prototype.toString = function() {
if (spoofedFunctions.has(this)) {
return spoofedFunctions.get(this);
}
return nativeToString.call(this);
};
spoofedFunctions.set(Function.prototype.toString, 'function toString() { [native code] }');
if (window.Permissions && window.Permissions.prototype.query) {
spoofedFunctions.set(window.Permissions.prototype.query, 'function query() { [native code] }');
}
} catch (e) {}
// -------------------------------------------------------------------------
// 43. Добавляем "реальные" события (НОВЫЙ)
// -------------------------------------------------------------------------
try {
// Имитируем реальное использование браузера
setTimeout(function() {
try {
// Добавляем слушатели для scroll, mouse movement
let lastScroll = 0;
window.addEventListener('scroll', function() {
lastScroll = window.scrollY;
}, { passive: true });
// Имитируем периодическое движение мыши
setInterval(function() {
try {
if (Math.random() < 0.1) {
// Случайное движение мыши
const event = new MouseEvent('mousemove', {
bubbles: true,
cancelable: true,
view: window,
});
// Не отправляем - просто имитируем активность
}
} catch(e) {}
}, 5000);
} catch(e) {}
}, Math.random() * 2000 + 1000); // Рандомная задержка
} catch (e) {}
// -------------------------------------------------------------------------
// 44. Service Workers (НОВЫЙ)
// -------------------------------------------------------------------------
try {
if (navigator.serviceWorker) {
// Service workers должны работать
}
} catch (e) {}
// -------------------------------------------------------------------------
// 45. Push API (НОВЫЙ)
// -------------------------------------------------------------------------
try {
if (PushManager) {
PushManager.prototype.subscribe = function(options) {
return Promise.resolve({
endpoint: '',
keys: { p256dh: '', auth: '' },
getKey: function() { return null; },
toJSON: function() { return { endpoint: '', keys: this.keys }; },
});
};
PushManager.prototype.getSubscription = function() {
return Promise.resolve(null);
};
}
} catch (e) {}
// -------------------------------------------------------------------------
// 46. WebSocket (НОВЫЙ)
// -------------------------------------------------------------------------
try {
// WebSocket должен работать нормально
} catch (e) {}
// -------------------------------------------------------------------------
// 47. Element.getBoundingClientRect - добавляем noise (НОВЫЙ)
// -------------------------------------------------------------------------
try {
const originalGetBoundingClientRect = Element.prototype.getBoundingClientRect;
Element.prototype.getBoundingClientRect = function() {
const rect = originalGetBoundingClientRect.call(this);
// Добавляем микроскопический шум
const noise = 0.1;
return {
x: rect.x + (Math.random() - 0.5) * noise,
y: rect.y + (Math.random() - 0.5) * noise,
width: rect.width,
height: rect.height,
top: rect.top + (Math.random() - 0.5) * noise,
right: rect.right + (Math.random() - 0.5) * noise,
bottom: rect.bottom + (Math.random() - 0.5) * noise,
left: rect.left + (Math.random() - 0.5) * noise,
};
};
} catch (e) {}
// -------------------------------------------------------------------------
// 48. window.frameElement (НОВЫЙ)
// -------------------------------------------------------------------------
try {
Object.defineProperty(window, 'frameElement', {
get: function() { return null; },
configurable: true
});
} catch (e) {}
// -------------------------------------------------------------------------
// 49. window.length (НОВЫЙ)
// -------------------------------------------------------------------------
try {
// window.length должен быть равен количеству фреймов
if (window.length === undefined) {
Object.defineProperty(window, 'length', {
get: function() { return window.frames.length; },
configurable: true
});
}
} catch (e) {}
// -------------------------------------------------------------------------
// 50. Install Trigger (для old school detection) (НОВЫЙ)
// -------------------------------------------------------------------------
try {
window.InstallTrigger = {
install: function() { return { status: 'installed' }; },
updateCheck: function() { return {} },
enabled: function() { return false; },
};
} catch (e) {}
// -------------------------------------------------------------------------
// 51. Отслеживание и предотвращение detection (НОВЫЙ)
// -------------------------------------------------------------------------
try {
// MutationObserver - должен работать
if (window.MutationObserver) {
// MutationObserver работает
}
// IntersectionObserver
if (window.IntersectionObserver) {
// IntersectionObserver работает
}
// ResizeObserver
if (window.ResizeObserver) {
// ResizeObserver работает
}
} catch (e) {} } catch (e) {}
// ------------------------------------------------------------------------- // -------------------------------------------------------------------------
// Готово — все патчи применены // Готово — все патчи применены
// ------------------------------------------------------------------------- // -------------------------------------------------------------------------
console.log('[STEALTH] Maximum stealth mode initialized');
})(); })();