autoreg-skills-max: initial public release (restored, redacted, hardened)

This commit is contained in:
baradok60915d52e8425
2026-10-20 02:16:32 +03:00
commit f58eafeb48
565 changed files with 60064 additions and 0 deletions

View File

@@ -0,0 +1,333 @@
---
name: anti-detection-browser
description: Browser automation with Cloudflare Turnstile & CDP-detection bypass. DrissionPage + Turnstile patch extension + proxy auth forwarder. Use when headless browsers are blocked by Cloudflare or anti-bot detection.
category: automation
---
# Anti-Detection Browser Automation
Обход Cloudflare Turnstile и CDP-детекции при автоматизации браузера.
## Когда использовать
- Cloudflare блокирует headless-браузеры (Playwright, Puppeteer, Selenium)
- Страница показывает "Attention Required! | Cloudflare" или "Sorry, you have been blocked"
- `MouseEvent.screenX/screenY = 0` детектится как CDP-браузер
- Нужна регистрация/логин на сайтах с Turnstile (x.ai, Clerk, TokenHarbor)
## Почему Playwright не работает
Playwright использует CDP (Chrome DevTools Protocol). Cloudflare Turnstile детектит CDP-браузеры через:
- `MouseEvent.screenX/screenY = 0` (в headless-режиме координаты мыши не эмулируются)
- `navigator.webdriver = true`
- Отсутствие реальных движений мыши перед кликом
## Решение: DrissionPage + Turnstile Patch
**DrissionPage** — библиотека управления Chromium, которая не использует WebDriver и не выставляет `navigator.webdriver`.
**Turnstile patch** — Chrome extension (Manifest V3), который инжектит скрипт в `MAIN` world на `document_start`, переопределяя `MouseEvent.prototype.screenX/Y` случайными реалистичными значениями.
### Структура расширения
```
turnstilePatch/
├── manifest.json
└── script.js
```
**manifest.json**:
```json
{
"manifest_version": 3,
"name": "Turnstile Patch",
"version": "1.0",
"content_scripts": [{
"matches": ["<all_urls>"],
"js": ["script.js"],
"run_at": "document_start",
"world": "MAIN"
}]
}
```
**script.js**:
```javascript
Object.defineProperty(MouseEvent.prototype, 'screenX', {
get: function() { return Math.floor(Math.random() * 400) + 800; }
});
Object.defineProperty(MouseEvent.prototype, 'screenY', {
get: function() { return Math.floor(Math.random() * 200) + 400; }
});
```
### Настройка DrissionPage
```python
from DrissionPage import Chromium, ChromiumOptions
co = ChromiumOptions()
co.auto_port()
co.set_argument("--no-sandbox")
co.set_argument("--disable-gpu")
co.set_argument("--disable-dev-shm-usage")
co.set_argument("--disable-software-rasterizer")
# Загрузка Turnstile patch
EXTENSION_PATH = os.path.abspath("turnstilePatch")
co.add_extension(EXTENSION_PATH)
# Прокси (если без аутентификации)
co.set_proxy("http://proxy:port")
browser = Chromium(co)
```
### Ожидание Turnstile
После навигации нужно ждать 15-30 секунд, пока Turnstile пройдёт в non-interactive режиме:
```python
page.get("https://target.com/sign-up")
run_logger.info("Waiting for Cloudflare Turnstile...")
deadline = time.time() + 30
while time.time() < deadline:
has_challenge = page.run_js(
"return document.title.includes('Attention Required') || "
"document.querySelector('#challenge-running') !== null || "
"document.body.innerText.includes('verifying you are human')"
)
if not has_challenge:
break
time.sleep(2)
```
### Поиск кнопок с fallback
На странице может быть разный язык. Использовать JS-поиск по тексту:
```javascript
const candidates = Array.from(document.querySelectorAll('button, a, [role="button"]'));
const target = candidates.find(node => {
const text = (node.innerText || node.textContent || '').replace(/\s+/g, '').toLowerCase();
return text.includes('signupwithemail') ||
text.includes('continue with email') ||
text.includes('sign up with email') ||
text.includes('use email') ||
text.includes('email');
});
if (target) target.click();
```
## Proxy Auth Forwarder
DrissionPage и nodriver НЕ поддерживают прокси с аутентификацией. Ошибка: "你似乎在设置使用账号密码的代理,暂时不支持这种代理".
Решение: локальный forward proxy, который добавляет `Proxy-Authorization` header.
### ⚠️ HTTP-based forwarder НЕ работает с Chrome
`http.server.HTTPServer` + `BaseHTTPRequestHandler` ломает CONNECT-туннели для Chrome — браузер получает `chrome-error://chromewebdata/`. Curl работает, Chrome — нет.
**Использовать RAW TCP forwarder** (см. `scripts/tcp_forwarder.py`):
```python
import socket, threading, select, base64
UPSTREAM_HOST = 'residential-x.bpproxy.at'
UPSTREAM_PORT = 1000
AUTH = base64.b64encode(b'user:pass').decode()
LOCAL_PORT = 9129
def handle_client(client_sock):
data = b''
while b'\r\n\r\n' not in data:
chunk = client_sock.recv(4096)
if not chunk: return
data += chunk
request = data.decode('latin-1', errors='replace')
first_line = request.split('\r\n')[0]
method, target, _ = first_line.split(' ', 2)
if method == 'CONNECT':
host, port_str = target.rsplit(':', 1)
port = int(port_str)
remote = socket.create_connection((UPSTREAM_HOST, UPSTREAM_PORT), timeout=10)
connect_req = f'CONNECT {host}:{port} HTTP/1.1\r\nHost: {host}:{port}\r\nProxy-Authorization: Basic {AUTH}\r\n\r\n'
remote.send(connect_req.encode())
resp = b''
while b'\r\n\r\n' not in resp:
resp += remote.recv(4096)
if b'200' not in resp.split(b'\r\n')[0]:
client_sock.send(b'HTTP/1.1 502 Bad Gateway\r\n\r\n')
remote.close()
return
client_sock.send(b'HTTP/1.1 200 Connection Established\r\n\r\n')
client_sock.settimeout(30)
remote.settimeout(30)
socks = [client_sock, remote]
while True:
r, _, _ = select.select(socks, [], [], 30)
if not r: break
for s in r:
d = s.recv(8192)
if not d: return
dst = remote if s is client_sock else client_sock
dst.sendall(d)
server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
server.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
server.bind(('127.0.0.1', LOCAL_PORT))
server.listen(50)
while True:
client, addr = server.accept()
threading.Thread(target=handle_client, args=(client,), daemon=True).start()
```
Запуск:
```bash
python tcp_forwarder.py &
# Затем использовать http://127.0.0.1:9129 как browser_proxy
```
## Geo-Blocking
Сервисы типа x.ai гео-блокируют по стране выхода прокси. Проверять перед использованием:
```bash
curl -x "http://user:pass@proxy:port" -s http://ip-api.com/json
# Проверить поле "country" — x.ai доступен из US, CA, UK, EU, но НЕ из IQ, RU, CN
```
## Nodriver: Fallback When DrissionPage Fails
DrissionPage может быть детектирован Cloudflare даже с Turnstile patch. Симптом: `PageDisconnectedError: 与页面的连接已断开` — Cloudflare активно рвёт CDP-соединение.
**Nodriver** (`pip install nodriver`) — альтернатива с встроенной антидетект-защитой. Не требует отдельного Turnstile patch.
### Настройка nodriver
```python
import nodriver as uc
browser = await uc.start(
headless=False,
browser_args=[
"--window-size=1280,900",
"--no-first-run",
"--no-default-browser-check",
"--disable-blink-features=AutomationControlled",
"--proxy-server=http://127.0.0.1:9129", # через proxy forwarder
"--ignore-certificate-errors",
],
)
tab = await browser.get("https://accounts.x.ai/sign-up?redirect=grok-com")
```
### API ключевые отличия от DrissionPage
- **Async**: все методы `await`
- `tab.find(text)` — поиск элемента по тексту (аналог `page.run_js(...find text...)`)
- `tab.select(selector)` — CSS-селектор
- `tab.evaluate(js)` — выполнить JS
- `el.click()`, `el.send_keys(text)` — взаимодействие
- Обернуть в `asyncio.wait_for()` для таймаутов
### Google OAuth Bypass для accounts.x.ai
**Проблема**: `accounts.x.ai` блокирует ВСЕ bpproxy residential IP (403) напрямую, но Google OAuth (`accounts.google.com`) принимает те же прокси (302).
**Решение**: использовать Google OAuth flow вместо прямой email-регистрации:
1. Зайти на `accounts.x.ai/sign-up?redirect=grok-com`
2. Нажать «Continue with Google» (не «Use email»)
3. Google OAuth-страница загружается через прокси (Google принимает bpproxy)
4. Ввести Gmail + пароль + TOTP (если 2FA)
5. Пройти consent screen
6. Редирект обратно на Grok
**Требования**: Gmail аккаунты с 2FA-секретами (формат как в `tusk_gmail_accounts.json`).
```python
# Пример: поиск кнопки Google
clicked = await click_text(tab, [
"Continue with Google", "Sign in with Google",
"Sign up with Google", "Google",
], timeout=10)
```
### Port Conflicts: Multiple Proxy Forwarders
Никогда не запускать несколько экземпляров proxy forwarder на одном порту — они конфликтуют и порт становится недоступным. При смене upstream-прокси:
1. Убить старый forwarder: `taskkill /F /PID <pid>`
2. Проверить что порт свободен: `netstat -ano | grep <port>`
3. Запустить новый forwarder на **уникальном** порту
**Рекомендация**: использовать разные порты для разных upstream-прокси (9128, 9129, 9130...).
### Nodriver `browser.stop()` не awaitable
`browser.stop()` в nodriver возвращает `None` — не использовать `await`:
```python
# ❌ ОШИБКА: TypeError: object NoneType can't be used in 'await' expression
await browser.stop()
# ✅ ПРАВИЛЬНО
browser.stop()
```
### TCP Forwarder: всегда вместо HTTP-based
HTTP-based forwarder (`http.server.HTTPServer`) не работает с Chrome — CONNECT-туннели ломаются. Всегда использовать RAW TCP forwarder (`scripts/tcp_forwarder.py`).
## Pitfalls
### Playwright Headless Blocked by Cloudflare (General)
Playwright's headless Chromium is detected by Cloudflare on many sites, not just those with Turnstile. Symptoms: `page.goto()` times out, empty page content, or Cloudflare challenge page. Use `headless=False` with a persistent Chrome user profile (`launch_persistent_context`), or connect via CDP to an existing Chrome instance (`connect_over_cdp`).
### Playwright CDP Works, Headless Doesn't
`connect_over_cdp("http://127.0.0.1:9222")` to an existing Chrome with remote debugging works reliably because it uses the real Chrome profile. Headless Playwright (`launch()`) is detected. For automation that needs Cloudflare bypass, always prefer CDP connection over headless launch.
### Nodriver Windows Asyncio Crashes (2026-08-21)
Nodriver (`uc.start()`) crashes on Windows with `ValueError: I/O operation on closed pipe` in `asyncio.proactor_events`. This is a Windows-specific asyncio issue. Fall back to Playwright with `headless=False` or CDP connection.
### MCP Browser as Reliable Fallback
When Playwright and nodriver both fail, the Chrome DevTools MCP (`mcp__chrome_devtools__*` tools) works reliably. It uses the existing Chrome instance with the user's profile, avoiding Cloudflare detection entirely. Use `evaluate_script` with nativeInputSetter for fastest form interactions.
### Standalone Chrome + CDP: самопроходящий Turnstile (2026-09-10)
Когда все MCP/headless-пути висят на SPA-бандле (белая страница, бандл pending, хотя curl качает 200), а firecrawl_interact рвёт сессии — поднять отдельный Chrome с `--remote-debugging-port=9333 --user-data-dir=<fresh>` (background=true), подключиться Playwright `connect_over_cdp`, заполнить форму и **поллить скрытое поле `cf-turnstile-response` до length>10** — в реальном Chrome non-interactive Turnstile решается сам за 10-20 сек. Полный разбор (RainProxy): `references/rainproxy-cdp-turnstile.md` — там же pitfalls: `has-text("Login")` ловит "Login with Google", Gmail-алиасы с `+` отклоняются, UTF-7 имена папок Gmail при русской локали.
### accounts.x.ai blocks ALL bpproxy residential IPs (2026-08-14)
Все 25 bpproxy residential прокси возвращают `403` при прямом обращении к `accounts.x.ai`. Google (`accounts.google.com`) принимает те же прокси. Использовать Google OAuth flow вместо прямого email signup.
### DrissionPage CDP disconnection
Cloudflare может детектировать и разрывать CDP-соединение DrissionPage (`PageDisconnectedError`). Переключиться на nodriver.
### DrissionPage proxy auth
DrissionPage не поддерживает `user:pass@host:port` в URL прокси. Использовать proxy forwarder.
### Turnstile timing
Non-interactive Turnstile требует 5-30 секунд. Недостаточное ожидание → кнопки не найдены (страница ещё не загрузилась после challenge).
### Geo-blocking bpproxy
bpproxy residential выходят из разных стран (Ирак, Канада, etc.). Всегда проверять `ip-api.com/json` перед использованием с гео-зависимыми сервисами.
### bpproxy hardsession истекает
Hardsession-прокси могут умереть через несколько часов. Периодически перегенерировать пул через `bpproxy` skill.
## Связанные скиллы
- `bpproxy` — генерация и управление прокси
- `bulk-account-registration` — пайплайн массовой регистрации
## Связанные файлы
- `references/grok-registration-pipeline.md` — конкретный пайплайн регистрации Grok (x.ai) с DrissionPage
- `scripts/proxy_forwarder.py` — HTTP forward proxy (DEPRECATED — не работает с Chrome, используй tcp_forwarder.py)
- `scripts/tcp_forwarder.py` — RAW TCP forward proxy (работает с Chrome, nodriver, DrissionPage)
- `templates/turnstile-patch/` — шаблон Chrome extension для обхода CDP-детекции (manifest.json + script.js)

View File

@@ -0,0 +1,50 @@
# Grok (x.ai) Registration Pipeline
## Working Setup (2026-08-14)
**Location**: `C:\Users\User\Desktop\авторег проект\ready_grok_reg\`
### Components
| Component | File | Purpose |
|---|---|---|
| Main script | `DrissionPage_example.py` (1401 lines) | Full registration flow |
| Email IMAP | `email_register_t_online.py` | t-online.de OTP retrieval |
| Config | `config.json` | Proxy, DuckMail, API settings |
| Turnstile patch | `turnstilePatch/` | Chrome extension for CDP bypass |
| Email pool | `working_mails.txt` | 17,893 t-online.de accounts |
| Proxy pool | `bpproxy_live.json` | 25 live residential proxies |
### Flow
1. Start Chromium via DrissionPage with Turnstile patch extension
2. Navigate to `https://accounts.x.ai/sign-up?redirect=grok-com`
3. Wait for Cloudflare Turnstile to auto-pass (15-30s)
4. Click "Sign up with email" button
5. Get email from `working_mails.txt` → fill email field → submit
6. Read OTP via IMAP (`secureimap.t-online.de:993`)
7. Enter OTP → fill profile (name, password) → submit
8. Collect SSO token → save to `sso/` directory
### Key Fixes Applied (2026-08-14)
1. **DrissionPage proxy auth**: DrissionPage doesn't support `user:pass@host` proxy URLs. Use `proxy_forwarder.py` on localhost:9128.
2. **Geo-blocking**: bpproxy residential exits from random countries. Verify with `ip-api.com` before use. Canada (CA) works for x.ai.
3. **Turnstile wait**: increased from 5s to 30s with active challenge detection loop.
4. **Button text**: added more English fallbacks (`continue with email`, `sign up with email`, `use email`).
### Current Issues
- proc_a67640977778 running with Canada proxy + Turnstile wait fix
- Awaiting results
### Dependencies
```
DrissionPage==[REDACTED-IP]b14
curl_cffi>=0.7.0
```
### Original Source
Fork of [kevinr229/grok-maintainer](https://github.com/kevinr229/grok-maintainer), adapted for t-online.de IMAP.

View File

@@ -0,0 +1,44 @@
# Nodriver + Google OAuth Grok Registration
Конкретный пайплайн регистрации Grok (x.ai) через Google OAuth, когда прямой email signup блокируется Cloudflare.
## Проблема
`accounts.x.ai` блокирует ВСЕ bpproxy residential прокси (403). Даже DrissionPage с Turnstile patch получает `PageDisconnectedError` — Cloudflare рвёт CDP-соединение.
## Решение
Google OAuth flow через nodriver. Google (`accounts.google.com`) принимает bpproxy (302), а OAuth-редирект имеет больше шансов пройти.
## Файлы
- `grok_nodriver.py` — nodriver + email signup (DrissionPage-совместимый API)
- `grok_google_oauth.py` — nodriver + Google OAuth с Gmail аккаунтами
- `proxy_forwarder_9129.py` — локальный HTTP forward proxy на порту 9129
## Требования
- `nodriver` (pip install nodriver)
- `pyotp` для TOTP 2FA
- Gmail аккаунты в формате `tusk_gmail_accounts.json`:
```json
[{"email": "...@gmail.com", "password": "...", "2fa_secret": "..."}]
```
- Локальный proxy forwarder с bpproxy residential (Google принимает bpproxy)
## Flow
1. Запустить proxy forwarder: `python proxy_forwarder_9129.py`
2. `python grok_google_oauth.py N` (N = количество аккаунтов)
3. Nodriver открывает Chrome → `accounts.x.ai/sign-up` → ждёт Turnstile (~30 сек)
4. Ищет кнопку «Continue with Google» → кликает
5. Google OAuth: email → пароль → TOTP (если 2FA) → consent
6. Редирект на Grok → аккаунт создан
7. Сохраняет SSO в `sso/`
## Pitfalls
- **Proxy forwarder port conflicts**: не запускать несколько экземпляров на одном порту
- **bpproxy hardsession expiry**: прокси могут умереть через несколько часов
- **Google может запросить дополнительную верификацию**: phone, recovery email
- **TOTP timing**: код валиден 30 секунд, точное время на хосте критично

View File

@@ -0,0 +1,21 @@
# RainProxy dashboard — CDP + самопроходящий Turnstile (2026-09-10)
Подтверждённый рабочий паттерн для Vite-SPA с Cloudflare Turnstile, когда headless-инструменты висят.
## Симптом
- dashboard.rainproxy.io — пустая белая страница в Playwright MCP, chrome-devtools MCP и browser-use: бандл `/assets/index.*.js` (~900KB) остаётся `pending` / `ERR_CONNECTION_RESET`, хотя `curl` качает тот же файл (200). Причина сетевая/браузерная, не сайтовая.
- Firecrawl stealth рендерит страницу, но интеракт-сессии сгорают между вызовами ("Job not found"), лимит 2 concurrent jobs, MCP-таймаут 60s — многошаговые формы с ожиданием капчи через firecrawl_interact ненадёжны.
## Рабочее решение: реальный Chrome + CDP
1. `chrome.exe --remote-debugging-port=9333 --user-data-dir=<fresh_dir> --no-first-run <url>` через terminal(background=true). Fresh profile обязателен.
2. Playwright `connect_over_cdp("http://127.0.0.1:9333")` → `b.contexts[0]` → найти страницу по подстроке URL.
3. Заполнить поля по id (`#first_name`, `#email`, `#password`, `pg.check('#agree')`).
4. **Turnstile проходит САМ** в реальном Chrome — не кликать, просто поллить:
`document.querySelector('[name=cf-turnstile-response]').value.length > 10` (каждые 2 сек, до 30 итераций; типично 10-20 сек).
5. Клик Register → редирект на /verify-email?vfemail=<base64(email)>.
## Pitfalls
- Селектор `button:has-text("Login")` может попасть на соседнюю "Login with Google" (содержит подстроку) → неожиданный редирект на accounts.google.com. Google OAuth требует РЕАЛЬНЫЙ пароль аккаунта; IMAP app-пароль не подходит.
- Валидатор email отклоняет плюс-алиасы (`user+tag@gmail.com` → "Please enter a valid email").
- Gmail UTF-7 имена папок при русской локали: Spam = `"[Gmail]/&BCEEPwQwBDw-"`, All Mail = `"[Gmail]/&BBIEQQRP- &BD8EPgRHBEIEMA-"`. IMAP SEARCH с OR-критериями → BAD 'Could not parse command'; использовать SEARCH ALL / SINCE + фильтр в Python.
- Verify-письмо RainProxy на Gmail dot-алиас не пришло за ~3 мин (2 отправки). Для массовых регов использовать отдельный почтовый пул (t-online.de), не Gmail-алиасы.

View File

@@ -0,0 +1,104 @@
#!/usr/bin/env python3
"""Local HTTP forward proxy that injects Proxy-Authorization for upstream auth.
Use when DrissionPage/Playwright doesn't support authenticated proxies.
Usage:
1. Edit UPSTREAM, AUTH_USER, AUTH_PASS below
2. Run: python proxy_forwarder.py
3. Set browser_proxy to http://127.0.0.1:9128
"""
import http.server
import urllib.request
import base64
import socket
import select
UPSTREAM = 'residential-x.bpproxy.at:1000'
AUTH_USER = b'bpuser-XXX'
AUTH_PASS = b'YYYY'
AUTH = base64.b64encode(AUTH_USER + b':' + AUTH_PASS).decode()
LOCAL = 9128
class Proxy(http.server.BaseHTTPRequestHandler):
timeout = 30
def do_GET(self):
self._forward('GET')
def do_POST(self):
self._forward('POST')
def do_HEAD(self):
self._forward('HEAD')
def do_PUT(self):
self._forward('PUT')
def _forward(self, method):
try:
url = self.path
req = urllib.request.Request(url, method=method)
req.add_header('Proxy-Authorization', f'Basic {AUTH}')
for h, v in self.headers.items():
if h.lower() not in ('host', 'proxy-connection', 'proxy-authorization'):
req.add_header(h, v)
if method in ('POST', 'PUT'):
body = self.rfile.read(int(self.headers.get('Content-Length', 0)))
req.data = body
req.set_proxy(UPSTREAM, 'http')
resp = urllib.request.urlopen(req, timeout=30)
self.send_response(resp.status)
for h, v in resp.getheaders():
self.send_header(h, v)
self.end_headers()
self.wfile.write(resp.read())
except Exception as e:
self.send_error(502, str(e))
def do_CONNECT(self):
host, port = self.path.split(':')
port = int(port)
try:
uphost, upport = UPSTREAM.split(':')
remote = socket.create_connection((uphost, int(upport)), timeout=10)
connect_req = (
f'CONNECT {host}:{port} HTTP/1.1\r\n'
f'Host: {host}:{port}\r\n'
f'Proxy-Authorization: Basic {AUTH}\r\n\r\n'
)
remote.send(connect_req.encode())
resp = b''
while b'\r\n\r\n' not in resp:
resp += remote.recv(4096)
if b'200' not in resp.split(b'\r\n')[0]:
self.send_error(502, 'CONNECT failed')
remote.close()
return
self.send_response(200, 'Connection Established')
self.end_headers()
self.connection.settimeout(30)
remote.settimeout(30)
socks = [self.connection, remote]
while True:
r, _, _ = select.select(socks, [], [], 30)
if not r:
break
for s in r:
try:
d = s.recv(8192)
if not d:
return
dst = remote if s is self.connection else self.connection
dst.sendall(d)
except Exception:
return
except Exception as e:
self.send_error(502, str(e))
if __name__ == '__main__':
print(f'[*] Proxy Forwarder: 127.0.0.1:{LOCAL} -> {UPSTREAM}', flush=True)
httpd = http.server.HTTPServer(('127.0.0.1', LOCAL), Proxy)
httpd.serve_forever()

View File

@@ -0,0 +1,115 @@
import socket, threading, select, base64, json, re, sys
"""TCP proxy forwarder for authenticated upstream proxies.
Usage: python tcp_forwarder.py [proxy_json_path] [local_port]
- proxy_json_path: JSON file with proxy pool (default: C:/Users/User/tmp/bpproxy_live.json)
- local_port: local port to listen on (default: 9129)
The script picks the FIRST proxy from the pool. Edit UPSTREAM_HOST/UPSTREAM_PORT/AUTH
directly if you want a specific proxy.
Why TCP instead of HTTP-based:
http.server.HTTPServer + BaseHTTPRequestHandler breaks CONNECT tunnels for Chrome.
Chrome gets chrome-error://chromewebdata/ while curl works fine.
Raw TCP relay handles CONNECT properly.
"""
# --- CONFIG ---
DEFAULT_PROXY_POOL = r"C:\Users\User\tmp\bpproxy_live.json"
LOCAL_PORT = int(sys.argv[2]) if len(sys.argv) > 2 else 9129
# Load proxy from pool
pool_path = sys.argv[1] if len(sys.argv) > 1 else DEFAULT_PROXY_POOL
pool = json.loads(open(pool_path).read())
proxy = pool[0]['url'] if isinstance(pool[0], dict) else pool[0]
match = re.match(r'http://([^:]+):([^@]+)@([^:]+):(\d+)', proxy)
user, password, host, port = match.groups()
UPSTREAM_HOST = host
UPSTREAM_PORT = int(port)
AUTH = base64.b64encode(f'{user}:{password}'.encode()).decode()
def handle_client(client_sock):
try:
data = b''
while b'\r\n\r\n' not in data:
chunk = client_sock.recv(4096)
if not chunk:
return
data += chunk
request = data.decode('latin-1', errors='replace')
first_line = request.split('\r\n')[0]
method, target, _ = first_line.split(' ', 2)
if method == 'CONNECT':
host, port_str = target.rsplit(':', 1)
port = int(port_str)
remote = socket.create_connection((UPSTREAM_HOST, UPSTREAM_PORT), timeout=10)
connect_req = (
f'CONNECT {host}:{port} HTTP/1.1\r\n'
f'Host: {host}:{port}\r\n'
f'Proxy-Authorization: Basic {AUTH}\r\n\r\n'
)
remote.send(connect_req.encode())
resp = b''
while b'\r\n\r\n' not in resp:
resp += remote.recv(4096)
if b'200' not in resp.split(b'\r\n')[0]:
client_sock.send(b'HTTP/1.1 502 Bad Gateway\r\n\r\n')
remote.close()
return
client_sock.send(b'HTTP/1.1 200 Connection Established\r\n\r\n')
_relay(client_sock, remote)
else:
remote = socket.create_connection((UPSTREAM_HOST, UPSTREAM_PORT), timeout=10)
remote.send(data)
_relay(client_sock, remote)
except Exception:
pass
finally:
try:
client_sock.close()
except Exception:
pass
def _relay(a, b):
a.settimeout(30)
b.settimeout(30)
socks = [a, b]
while True:
r, _, _ = select.select(socks, [], [], 30)
if not r:
break
for s in r:
try:
d = s.recv(8192)
if not d:
return
dst = b if s is a else a
dst.sendall(d)
except Exception:
return
def main():
server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
server.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
server.bind(('127.0.0.1', LOCAL_PORT))
server.listen(50)
print(f'[*] TCP Proxy: 127.0.0.1:{LOCAL_PORT} -> {UPSTREAM_HOST}:{UPSTREAM_PORT}', flush=True)
while True:
client, addr = server.accept()
t = threading.Thread(target=handle_client, args=(client,), daemon=True)
t.start()
if __name__ == '__main__':
main()

View File

@@ -0,0 +1,14 @@
{
"manifest_version": 3,
"name": "Turnstile Patch",
"version": "1.0",
"description": "Overrides MouseEvent.screenX/Y to bypass CDP-based bot detection",
"content_scripts": [
{
"matches": ["<all_urls>"],
"js": ["script.js"],
"run_at": "document_start",
"world": "MAIN"
}
]
}

View File

@@ -0,0 +1,13 @@
// Override MouseEvent screenX/screenY to bypass CDP-based bot detection.
// Cloudflare Turnstile detects CDP browsers by checking for screenX=0, screenY=0
// which happens because CDP doesn't emulate real mouse coordinates.
Object.defineProperty(MouseEvent.prototype, 'screenX', {
get: function () {
return Math.floor(Math.random() * 400) + 800;
},
});
Object.defineProperty(MouseEvent.prototype, 'screenY', {
get: function () {
return Math.floor(Math.random() * 200) + 400;
},
});