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,395 @@
---
name: github-autoreg
description: Mass GitHub account registration without captcha. Persistent Chrome profile bypasses Arkose FunCaptcha. Two backends — mail.tm (no email list) and .de emails (IMAP). 2captcha/AntiCaptcha/CapSolver ALL fail on GitHub Arkose.
category: automation
---
# GitHub AutoReg — No Captcha
Создание GitHub аккаунтов **без капчи**. Прогретый Chrome-профиль обходит Arkose Labs FunCaptcha полностью.
## Почему это работает
GitHub использует Arkose Labs FunCaptcha (public key: `0651BCE4-8F66-4F8B-9C6D-5F5411DD9158`). **Ни один captcha-сервис её не решает:**
| Сервис | Результат | Пруф |
|--------|-----------|------|
| 2captcha (old API) | `ERROR_CAPTCHA_UNSOLVABLE` | 2026-07-09 |
| AntiCaptcha | Таймаут 3+ мин → таск истекает | 2026-07-27 (taskId: 464294112) |
| CapSolver | `ERROR_INVALID_TASK_DATA` | 2026-07-09 |
| YesCaptcha | `ERROR_ZERO_BALANCE` (мин. $10) | 2026-07-27 |
**Решение**: прогретый Chrome-профиль (с куками/историей GitHub) → GitHub видит «своего» и **не показывает капчу**.
## Быстрый старт
**Основной способ (с 2026-08-11): дашборд ENI AUTOREG v3.0** — см. раздел ниже.
**Standalone (legacy):**
```bash
git clone https://github.com/nikita4a/gh-autoreg
cd gh-autoreg
pip install -r requirements.txt
playwright install chromium
python reg.py --count 10
```
## Полный флоу
```
1. Загружаем .de почты из файла (email:password)
2. Открываем прогретый Chrome-профиль
3. JS form fill (НЕ Playwright fill — палится)
4. Жмём Create account → редирект на /account_verifications
5. Читаем код подтверждения через IMAP
6. Вводим код → аккаунт создан
7. Ждём 2-5 мин (secondary rate limit)
8. Повторяем
```
## Три критических правила
### 1. JS form fill, НЕ `page.fill()`
```python
# ❌ Playwright fill — детектится
await page.fill('input[type="email"]', email)
# ✅ JS value + dispatchEvent — не детектится
await page.evaluate("""
(data) => {
const el = document.querySelector('input[type="email"]');
el.value = data.email;
el.dispatchEvent(new Event('input', {bubbles: true}));
el.dispatchEvent(new Event('change', {bubbles: true}));
}
""", {"email": email})
```
### 2. Dismiss flash alerts перед формой
```python
await page.evaluate("""
document.querySelectorAll('.flash-close, [aria-label="Dismiss"]')
.forEach(el => el.click());
document.querySelectorAll('.flash').forEach(el => el.remove());
""")
```
### 3. Задержка 2-5 минут
GitHub банит IP после 2-3 быстрых регистраций. `DELAY = (120, 300)` секунд.
## Провайдеры IMAP для .de почт
| Домен | IMAP сервер | Порт |
|-------|-------------|------|
| t-online.de | imap.t-online.de | 993 |
| web.de | imap.web.de | 993 |
| gmx.de | imap.gmx.net | 993 |
## Формат выходных данных
```
[REDACTED-EMAIL]|email_password|github_username|github_password
```
## Два backend'а для email
### Backend 1: mail.tm (НЕ рекомендуется — домены блокируются с 2026-08-10)
Временные почты через mail.tm API. **Работало до 2026-08-10**, но теперь домены (`emalupe.com`) блокируются GitHub — аккаунт не создаётся. Использовать Backend 2.
```python
from src.mailtm_utils import create_email, wait_for_code
mail = create_email() # → {email, password, token}
code = wait_for_code(mail["token"], timeout=120)
```
Скрипт: `reg_mailtm.py` (шаблон: `templates/reg_mailtm.py`). Утилиты: `references/mailtm-utils.md`.
### Backend 2: .de почты через IMAP (РЕКОМЕНДУЕМЫЙ)
t-online.de / web.de / gmx.de через IMAP. Пул: 17,872 почт в `data/working_mails_live.txt` (формат `email:password`). Скрипт: `reg.py` (шаблон: `templates/reg.py`).
## Прокси: SOCKS5 обязателен
DataDome (первый слой защиты) **блокирует HTTP-прокси** (403), но пропускает SOCKS5. Proxyshard.com: SOCKS5 порт 1080 работает, HTTP порт 80 — нет.
### Встроенный SOCKS5-мост (in-process)
Внешние процессы моста нестабильны на Windows. Встраивать asyncio SOCKS5 forwarder прямо в скрипт:
```python
class SocksBridge:
def __init__(self, sid):
self.user = f"plan-premium-country-de-sid-{sid}-ttl-8000000"
self.lp = random.randint(12000, 20000)
async def start(self):
self.srv = await asyncio.start_server(self._handle, '127.0.0.1', self.lp)
```
Полный шаблон: `templates/socks5_bridge.py`.
### Не-headless Chrome
DataDome JS-челлендж не решается в headless (включая `--headless=new`). **Обязательно `headless=False`**.
## OctoCaptcha — второй слой (DataDome в iframe)
После заполнения email + Continue, GitHub загружает **OctoCaptcha** — DataDome в скрытом iframe:
- URL: `https://octocaptcha.com/datadome?origin_page=github_signup_redesign`
- JS-ключ: `window.ddjskey = "2CF892E038EE5D8217D27787467ED2"`
- Скрипт: `https://js.datadome.co/tags.js`
- Токен: `<input name="octocaptcha-token">`
**Прогретый Chrome-профиль обходит оба слоя** — DataDome на странице + OctoCaptcha в iframe. Без профиля iframe НЕ завершает challenge (60+ секунд ожидания — поле `octocaptcha-token` пустое).
### Что НЕ работает для OctoCaptcha
| Подход | Результат |
|--------|-----------|
| `fetch()` POST в обход формы | Возвращает `/signup` обратно, аккаунт НЕ создаётся |
| Мобильный user-agent | DataDome блокирует страницу полностью |
| YesCaptcha `FunCaptchaTaskProxyless` | Несовместимый тип капчи |
| YesCaptcha `DataDomeLabsTask` | `ERROR_TASK_NOT_SUPPORTED` |
| Ручное `iframe.style` → visible | DataDome игнорирует, iframe остаётся `visible=false` |
## Google OAuth блокировка
GitHub редиректит на Google OAuth для email'ов, ассоциированных с Google (включая t-online.de). Блокировать через Playwright route:
```python
async def block_google(route):
await route.fulfill(status=204)
await page.route("**/sessions/social/google/**", block_google)
await page.route("**/accounts.google.com/**", block_google)
```
Но **блокировка не помогает с t-online.de** — GitHub не показывает форму пароля после блокировки, редиректит обратно на `/signup`. Использовать НЕ-Google email'ы: `proton.me`, `mail.ru`, `yandex.com`, `outlook.com`.
## JS form fill — безопасная передача значений
Пароли со спецсимволами (`$`, `\`, `"`) ломают f-строки в `page.evaluate()`. Использовать передачу через аргументы:
```python
await page.evaluate("""
([email, pwd]) => {
document.querySelector('input[name="user[email]"]').value = email;
document.querySelector('input[name="user[password]"]').value = pwd;
}
""", [email_addr, email_pwd])
```
## GitHub авто-определение страны
GitHub ставит `<input type="hidden" name="user_signup[country]" value="DE">` по IP. Поле `email_address` (не `user[email]`) на некоторых формах.
## Важные питфолы
1. **`:has-text()` не работает в `page.evaluate()`** — это Playwright-селектор, не CSS. В JS использовать стандартные селекторы.
2. **`mail.noop()` перед каждым IMAP search** — без этого Gmail/t-online кеширует состояние и не видит новые письма.
3. **`wait_for_selector` таймаутит на странице `/account_verifications`** — DOM меняется после отправки, поля появляются после анимации. **Добавить `page.wait_for_timeout(2000)` перед code fill.** Использовать 3 стратегии: S1 `launch_code[]` боксы (8 шт.) → S2 `input[inputmode="numeric"]` (текущий GitHub, 2026-08-10) → S3 brute force (любой input + любой button). `launch_code[]` боксы больше не используются GitHub (2026-08-10), но сохранены для обратной совместимости. Логировать каждую стратегию (S1/S2/S3). Ждать до 10с появления любого input'а перед заполнением.
4. **`is_success_url` должен проверять `account_verifications`** — `"verify" in "verifications"` НЕ срабатывает (нет буквы 'y'). Проверять обе строки: `"account_verifications" in url or "verify" in url.lower()`.
5. **mail.tm домены блокируются GitHub (2026-08-10)** — регистрация через `emalupe.com` и другие mail.tm домены: OTP принимается, но аккаунт НЕ создаётся (404 на API). GitHub молча отклоняет после OTP. Использовать t-online.de IMAP pool (17,872 почты в `data/working_mails_live.txt`).
6. **Несколько попыток с одним email** — GitHub может уже иметь аккаунт. Не повторять с тем же email.
7. **GitHub fine-grained PAT без `repo` scope не может push** — для `gh repo create` и `git push` нужен PAT с разрешением `Repository creation` (или classic PAT с `repo` scope). `gh auth refresh -h github.com -s repo` через device flow: открыть https://github.com/login/device и ввести код. Без `repo` — только ручное создание репы через web.
8. **Playwright `execution context destroyed`** — страница редиректится после `goto /signup`. Ждать стабилизации URL перед `query_selector`. Оборачивать в try/except с retry.
9. **Registrations без профиля = 0** — все попытки 2026-07-29 без прогретого профиля провалились из-за OctoCaptcha. Только persistent Chrome profile даёт результат.
10. **`page.fill()` палится** — всегда использовать `page.evaluate()` с `el.value = ` + `dispatchEvent`.
11. **Playwright Chromium крашится на Windows** — `launch_persistent_context` с bundled Chromium-1208 падает с exit code 2147483651 (STATUS_BREAKPOINT). **Решение: системный Chrome через CDP** (`--remote-debugging-port=9222`). См. `references/cdp-approach.md`.
12. **OTP ввод: `launch_code[]` БОЛЬШЕ НЕ ИСПОЛЬЗУЕТСЯ (2026-08-10)** — GitHub перешёл на single `input[inputmode="numeric"]`. Использовать 3 стратегии: S1 `launch_code[]` (legacy) → S2 `input[inputmode="numeric"]` (текущий) → S3 brute force. Ждать до 10с появления input'ов. См. `references/otp-and-provisioning.md`.
13. **Post-OTP URL detection** — проверять **путь** (`urlparse(url).path`), а не query params. `return_to=dashboard` в query params — это `/login`, а не `/dashboard`. См. `references/otp-and-provisioning.md`.
14. **Python 3.14 isolated mode** — hermes venv загрязняет `sys.path`. Использовать `python -I` для изоляции. Плюс `sys.stdout = io.TextIOWrapper(sys.stdout.buffer, encoding='utf-8')` для Windows.
15. **`page.url` — live property, не кешируется** — Playwright/Camoufox `page.url` переоценивается при каждом обращении. Между `log(page.url)` и `if "verif" in page.url` страница может уйти на другой URL. **Fix:** читать один раз в локальную переменную.
16. **Navigation race condition** — после клика на submit страница может оставаться на `/signup` 30+ сек через медленный прокси. `while "signup" in page.url` с таймаутом — ненадёжен (URL живой, может измениться между проверками). **Fix:** `page.wait_for_selector('input[name="launch_code[]"]', timeout=90000)` вместо URL-поллинга.
17. **Post-OTP "accepted" без сессии** — GitHub может редиректить на `/` или `/dashboard` после OTP, но аккаунт требует пост-регистрационный логин. **Fix:** после "accepted" → перейти на `/settings/profile`. Если редирект на `/login` → сделать логин (email + password).
18. **Rate limit держится часами** — secondary rate limit на GitHub может длиться 1+ час. 15-минутное ожидание недостаточно. Поля формы не заполняются (все `querySelector` возвращают null), страница `/signup` возвращается без редиректа. Ротировать IP агрессивно, не ждать на одном IP.
19. **bpproxy residential pool — ВСЕ прокси мертвы (2026-08-10)** — 10/10 прокси в `bpproxy_hardsession_pool.txt` не отвечают (curl: пустой ответ, Camoufox: NS_ERROR_PROXY_CONNECTION_REFUSED). Прокси не залимичены — они физически недоступны. Нужны свежие прокси от другого провайдера.
20. **Camoufox `geoip=True`** — при использовании прокси Camoufox выдаёт `LeakWarning`. Добавлять `geoip=True` в launch_opts.
21. **PYTHONPATH изоляция Python 3.11** — hermes-venv добавляет свои site-packages в `PYTHONPATH`, из-за чего `py -3.11` пытается импортировать playwright из заблокированного herm-венва (PermissionError). **Fix:** `PYTHONPATH= py -3.11 main.py` — очищает PYTHONPATH перед запуском.
22. **Path-based URL matching needs BOTH path AND full URL** — `urlparse(url).path` alone misses the verification page URL `/login?return_to=...account_verifications` (path = `/login`). Check BOTH `path` AND `url_lower` for `account_verif`/`launch`. The verification page is at `/login?return_to=...account_verifications`, NOT at `/account_verifications` directly. See `references/otp-and-provisioning.md`.
23. **`still_on_verify` time-limit** — in post-OTP loop, the `still_on_verify` check catches the same URL `/login?return_to=...account_verifications` indefinitely. Limit to first 10s of the 30s post-OTP window: `if still_on_verify and time.time() < deadline - 20`. After 10s, evaluate other signals (has_session, login page, code error).
24. **HTTP provisioning shares rate limit** — `provision.py` (requests) hits the SAME rate limit as the browser on the local IP. Login to fresh accounts via HTTP returns "login failed: rate". Use the same proxy as registration, or extract browser cookies and inject into `requests.Session`, or do 2FA+PAT through the browser (CDP) instead of HTTP.
25. **Проверка alive прокси перед использованием** — `_active_proxy_from_db()` выбирает прокси с максимальным `success_count`, но не проверяет актуальную доступность. Все прокси в пуле могут быть мертвы (bpproxy 2026-08-10). **Fix:** `curl -sI --max-time 10 -x <proxy> https://github.com` перед каждым использованием.
26. **Accounts created without octocaptcha are silently locked** — CDP form submission without `octocaptcha-token` creates the account (OTP email arrives), but GitHub flags and locks it immediately. Login fails with "Incorrect username or password" even through real Chrome browser. The `octocaptcha-token` field is `required` on the signup form — without it, the account is born-dead. Honeypot fields (`required_field_*`) must be left empty. **Fix:** solve octocaptcha before form submission, or use a pre-warmed profile that bypasses the captcha entirely. Verified 2026-08-10: manual browser login with just-created credentials also fails.
27. **CDP `requestSubmit()` vs `submit()`** — `form.submit()` bypasses JavaScript event handlers (including octocaptcha validation). Use `form.requestSubmit ? form.requestSubmit() : form.submit()` to trigger proper HTML5 form validation and event handlers.
28. **CDP debug pattern** — Dump page fields and body text via `Runtime.evaluate` to diagnose form issues. JS snippet: `JSON.stringify({title: document.title, url: window.location.href, fields: Array.from(document.querySelectorAll('input')).map(el => el.id + '|' + (el.name||'') + '|' + (el.type||'') + '|' + (el.getAttribute('inputmode')||'')), body: document.body ? document.body.innerText.substring(0, 300) : ''})`. Log before and after each form submission.
29. **CDP `Runtime.evaluate` with `awaitPromise: true`** — Without `awaitPromise: true`, `Runtime.evaluate` returns immediately without waiting for the expression to complete. Always use `awaitPromise: true` for async operations and form submissions. The CDP message format: `{"id": N, "method": "Runtime.evaluate", "params": {"expression": code, "returnByValue": true, "awaitPromise": true}}`.
## Результаты
- **2026-07-27**: `nova9417` через mail.tm + persistent profile ✅
- **2026-07-27**: `0402294270-0001-2943` через t-online.de + persistent profile ✅
- **2026-07-27**: 3-strategy code fill (digit boxes / single input / keyboard) ✅
- **2026-07-09**: v6 скрипт работал, но требовал IMAP email (не mail.tm)
- **2026-08-10**: CDP подход через системный Chrome 151 работает, Playwright Chromium крашится
- **2026-08-10**: OTP через `launch_code[]` боксы (8 шт.) — больше не используются GitHub. Текущий подход: `input[inputmode="numeric"]` (стратегия S2)
- **2026-08-10**: Post-OTP `still_on_verify` с time-limit 10s — необходим, иначе проверка ловит `/login?return_to=...account_verifications` бесконечно
- **2026-08-10**: Path + full URL matching — path-only (pitfall #22) не видит `account_verif` в query string. Нужна проверка обоих
- **2026-08-10**: bpproxy.at residential pool — все 10 прокси физически мертвы (curl: пустой ответ). Нужны новые прокси
- **2026-08-10**: `PYTHONPATH=` фикс для Python 3.11 — без него hermes-venv вызывает PermissionError на playwright
- **2026-08-10**: mail.tm домены (`emalupe.com`) блокируются GitHub — OTP accepted, но аккаунт не создаётся (404). Нужен t-online.de IMAP
- **2026-08-12**: ZTE proxy + Camoufox + .de IMAP — 4 аккаунта зарегистрировано (2FA+токен+прогрев) за один запуск. DataDome `t=bv` на 5-м — IP забанен. 2Captcha zero balance. ZTE DISCONNECT/CONNECT возвращает failure.
- **2026-08-12**: Task API формат: все параметры в `config` dict. `executor_type` default = `protocol` (не `headed`). `use_proxy: "0"` в `extra` отключает bpproxy-пул. `proxy` на верхнем уровне config задаёт прокси браузера.
- **2026-08-12**: IMAP pool `allow_reuse=false` — 17,872 email'ов, 89 использовано, 17,597 доступно. ~30% email'ов уже заняты на GitHub (из предыдущих сессий). Авто-ротация находит свежий за 1-2 попытки.
## CDP подход (альтернатива Playwright)
Когда Playwright Chromium крашится на Windows, использовать системный Chrome через CDP:
```bash
chrome.exe --remote-debugging-port=9222 --user-data-dir=.../chrome_profile
```
Управление через websocket + `Runtime.evaluate`. См. `references/cdp-approach.md`.
## Post-регистрационное provisioning
После регистрации — 2FA и PAT через `provision.py` (из `авторег проект/backend/platforms/github/provision.py`):
- `enable_2fa(email, password)` → `{totp_secret, recovery_codes}` — чистый HTTP, без браузера
- `generate_token(email, password, totp_secret, scopes)` → `ghp_...` — classic PAT
См. `references/otp-and-provisioning.md`.
## Camoufox (альтернативный backend)
`авторег проект/backend/platforms/github/browser_register.py` — полноценный Camoufox-based регистратор с:
- DataDome обходом (два слоя)
- SOCKS5 прокси
- Авто-логином после регистрации
- Извлечением сессии
Установлен: `camoufox==0.4.11`, `cloverlabs-camoufox==0.6.0`.
30. **IMAP pool exhausted — #1 failure mode в ENI AUTOREG v3.0 (2026-08-11)** — Дашборд показывает `FAIL: IMAP pool exhausted — add accounts or enable reuse` для каждой попытки регистрации. Пул `data/working_mails_live.txt` пуст или все почты израсходованы. **Fix:** (a) пополнить файл свежими t-online.de почтами, (b) переключить дефолтный mailbox на NotLetters API или mail.tm API (не требуют IMAP-пула), (c) включить `reuse` режим в настройках IMAP-провайдера. Проверить состояние пула: `wc -l backend/data/working_mails_live.txt`. Логи дашборда (Tasks → клик по задаче → LIVE LOGS) показывают ошибку мгновенно — задача не тратит прокси, fail fast.
31. **ZTE proxy IP бан после 3-4 регистраций (2026-08-12)** — DataDome детектит `t=bv` (banned). Без работающего DataDome-солвера аккаунт фейлится. **Fix:** ротировать IP ZTE-модема каждые 3 регистрации. DISCONNECT/CONNECT через goform ненадёжен — физическая перезагрузка модема надёжнее.
32. **`allow_reuse=true` отдаёт уже занятые GitHub email'ы (2026-08-12)** — IMAP-пул не знает, какие email'ы уже зарегистрированы на GitHub. При reuse pool выдаёт те же адреса → GitHub `email TAKEN` → задача тратит время на ротацию. **Fix:** `allow_reuse=false` для чистых регистраций. `allow_reuse=true` только для тестов.
33. **2Captcha zero balance — DataDome unsolvable (2026-08-12)** — 2Captcha — единственный DataDome-солвер для Firefox/Camoufox. При нулевом балансе капча не решается → задача фейлится. **Fix:** пополнить 2Captcha ($3-5 минимум). CapSolver не подходит для Camoufox (требует Chrome UA).
34. **Task API: `executor_type` default = `protocol`, не `headed` (2026-08-12)** — `task_runtime.py` line 203: `executor_type = payload.get("executor_type", "protocol")`. Без явного указания `executor_type: "headed"` в config задача запускается в HTTP-режиме, не в браузере. Лог: `Task started: github x5 [protocol]` вместо `[headed]`.
35. **Recovery codes не сохраняются в `github_accounts_2fa.txt` (2026-08-12)** — ENI AUTOREG v3.0: `provision.py` → `enable_2fa()` вызывает `persist_pending` только для сохранения в БД (таблица `account_credentials`), но **не дописывает строку в `data/github_accounts_2fa.txt`**. Файл содержит только старые аккаунты (16 записей на 2026-08-12). **Fix:** добавить дописывание строки в файл после успешного `enable_2fa` в формате `email:password:[REDACTED] Recovery codes = 12 слов/16 hex-кодов, лежат в поле `recovery_codes` ответа от `enable_2fa`.
36. **DataDome wall taxonomy — classify before acting (2026-10-12)** — четыре разные стены: slider (`div.sliderContainer`, решается drag), robot wall (`captcha__robot` в iframe — нерешаема, fail fast + disable сессии), JS-challenge wall (пустая страница «Please enable JS and disable any ad blocker» — переживает reload'ы, disable сессии), SSL-broken proxy (Firefox рендерит «SSL received a record that exceeded…» КАК СТРАНИЦУ — прокси мёртв, не reload). Маркеры проверять и внутри iframe, не только main page. Полная таблица + селекторы: `references/datadome-slider-stall-debug-bank-audit.md`.
37. **Residential sticky-пул: disable ТОЛЬКО по exact URL (2026-10-12)** — все sticky-сессии делят один host:port (различается только `sess<N>` в username); disable по host:port комментирует ВЕСЬ пул → runner падает на direct IP → secondary rate limit. Точный URL + ambiguity guard в ротаторе (отказ при >1 совпадении). Residential-латентность ломает тайминги: verify-редирект 10-30с, auto-login cookie ждать 75с (не 30с — иначе акки сохраняются verified-only без repo/2FA/PAT).
38. **Patient verify-wait после launch-кода (2026-10-12)** — проверка «verify stable» за ~4с ЛОЖНО объявляет хороший код rejected на медленном residential. Ждать до 90с; «rejected» только при явном error-баннере (incorrect/invalid code/wrong code/try again) или полном таймауте; на середине один раз перепечатать код (auto-submit может пропустить событие 8-го бокса).
39. **Параллельные батчи = дизъюнктные пулы (2026-10-12)** — раннер читает config ОДИН раз на старте и поддерживает `--config <file>`; второй батч = `config_b.json` + `proxies_b.txt` с непересекающимся диапазоном сессий → разные exit IP, ноль коллизий. Два батча с одного пула могут взять одну сессию одновременно → кросс-флаг аккаунтов.
40. **Цена рестарта батча (2026-10-12)** — kill в mid-flow теряет аккаунт безвозвратно: у упавших акков пароль НЕ сохраняется → backfill невозможен. Группировать фиксы и рестартовать один раз; перед kill сверять живой pid + CreationDate через CIM — heartbeat'ы уже убитых батчей продолжают приходить как stale-буферы (смотреть на таймстемпы в логе, не на номер процесса).
## ENI AUTOREG v3.0 Task API (прямой запуск без дашборда)
Запуск через `POST /api/tasks` — **все параметры внутри `config`**:
```json
{
"platform": "github",
"action": "register",
"count": 5,
"config": {
"executor_type": "headed",
"captcha_solver": "yescaptcha_api",
"proxy": "http://127.0.0.1:8899",
"extra": {
"use_proxy": "0",
"identity_provider": "mailbox"
}
}
}
```
**Критично:** `config` — dict с ВСЕМИ runtime-параметрами. `executor_type`, `captcha_solver`, `proxy` — на верхнем уровне config. `use_proxy` — внутри `extra`. `proxy` задаёт прокси для браузера; `use_proxy: "0"` в extra отключает bpproxy-пул.
Значения по умолчанию в `task_runtime.py`:
- `executor_type`: `"protocol"` (НЕ `"headed"`!) — всегда указывать явно
- `captcha_solver`: `"auto"`
- `_use_proxies`: `str(extra.get("use_proxy", "1"))` — `"1"` по умолчанию (bpproxy-пул активен)
## ZTE Proxy для GitHub
ZTE-модем (192.168.0.1) → mobile proxy на `127.0.0.1:8899`. **Работает чисто** — DataDome не появляется на первых 2-3 регистрациях. После 3-4 рег IP банится (`t=bv`), нужна ротация.
### Ротация IP ZTE-модема
Логин: `SHA256(SHA256(password).upper() + LD).upper()` → POST `goformId=LOGIN` (result=0).
Ротация: `DISCONNECT_NETWORK` → ждать 10с → `CONNECT_NETWORK`.
**Важно:** DISCONNECT/CONNECT часто возвращают `{"result":"failure"}` даже после успешного логина. Физическая перезагрузка модема надёжнее.
### DataDome solver chain (Camoufox)
Camoufox = Firefox UA → **CapSolver отклоняет** (требует Chrome UA, `NotImplementedError`). **2Captcha** — единственный DataDome-солвер для Firefox UA. При `t=bv` солверы не нужны — нужна ротация IP.
Порядок в `solve_datadome_with_fallback`: настроенные candidates → CapSolver → 2Captcha. YesCaptcha НЕ поддерживает DataDome.
### IMAP пул: reuse и занятые email'ы
`allow_reuse: false` — предотвращает повторную выдачу email'ов, уже использованных этим экземпляром пула. **НО:** email'ы, зарегистрированные на GitHub в предыдущих сессиях, пул не знает. GitHub отвечает `email TAKEN` → авто-ротация до 5 раз. При 17,872 почт в пуле большинство свежих находится за 1-2 ротации.
Управление: `PUT /api/providers/mailbox/imap_pool` → `config.imap_pool_allow_reuse: "true"/"false"`.
## ENI AUTOREG v3.0 Dashboard (основной интерфейс с 2026-08-11)
Основной способ запуска GitHub-регистраций — дашборд ENI AUTOREG v3.0, а не standalone-скрипты из `gh-autoreg`.
### Пути
- **Проект**: `C:\Users\User\Desktop\авторег проект\`
- **Бэкенд**: `backend/main.py` — FastAPI на `http://127.0.0.1:8002`
- **Фронтенд**: `frontend/` — Vite dev-server на `http://127.0.0.1:5173`
- **База**: `backend/account_manager.db` — SQLite (299+ аккаунтов, 1038+ задач)
- **IMAP-пул**: `backend/data/working_mails_live.txt` — t-online.de почты
- **Платформы**: 32 платформы (GitHub: 215 аккаунтов, 728 задач)
### Запуск
```bash
# Бэкенд (Python 3.11, чистая среда)
cd "/c/Users/User/Desktop/авторег проект/backend"
PYTHONPATH= py -3.11 main.py
# Фронтенд
cd "/c/Users/User/Desktop/авторег проект/frontend"
npm run dev
```
Проверка: `curl http://127.0.0.1:8002/api/health` → `{"ok":true}`.
### Запуск регистрации через дашборд
1. Открыть `http://127.0.0.1:5173`
2. Platforms → GitHub → **Register Account**
3. Модалка: выбрать количество (1-100), executor (Headed/Headless), captcha solver (auto / yescaptcha_api), прокси (опционально)
4. Нажать **Register account**
5. Смотреть логи: Tasks → клик по задаче → LIVE LOGS (SSE-стрим)
### Провайдеры mailbox в дашборде
| Провайдер | Тип | Статус |
|-----------|-----|--------|
| t-online.de IMAP Pool | IMAP (файл) | DEFAULT, enabled |
| NotLetters API | REST API | Enabled |
| NotLetters IMAP | IMAP (файл) | Enabled |
| mail.tm | REST API | Enabled |
| Temp-Mail Web | Web | Enabled |
| reforrm.me catch-all | IMAP (Gmail) | Disabled |
## Связанные референсы
- `references/datadome-slider-stall-debug-bank-audit.md` — DataDome wall taxonomy (slider/robot/JS-wall/SSL/mid-wizard interstitial) + живые DOM-селекторы слайдера, residential sticky-пул ops (exact-URL disable), patient verify-wait, параллельные батчи через --config, bank-аудит через PAT `/user`, backfill PAT через password-reset (IMAP TO-search + BODY.PEEK), debug_stall-артефакты
- `references/camoufox-registration-patterns.md` — Camoufox регистрация: `_wait_form_valid` (octocaptcha), правильный клик (не social buttons), navigation wait, mail.tm API, post-OTP pitfalls
- `references/zte-proxy-task-api.md` — ZTE прокси, Task API формат, DataDome solver chain, mailbox provider management
- `references/octocaptcha-datadome.md` — анализ OctoCaptcha (второй слой DataDome), все failed подходы
- `references/mailtm-utils.md` — утилиты mail.tm
- `references/cdp-approach.md` — CDP подход (системный Chrome) вместо Playwright
- `references/otp-and-provisioning.md` — OTP ввод через launch_code[], пост-регистрация (2FA + PAT)
- `templates/socks5_bridge.py` — встроенный SOCKS5-мост для Playwright
- `templates/reg.py` — шаблон регистрации через .de IMAP
- `templates/reg_mailtm.py` — шаблон регистрации через mail.tm
- Проект: `C:\\Users\\User\\gh-autoreg\\` (готовый к заливке на GitHub)

View File

@@ -0,0 +1,162 @@
# Camoufox Registration Patterns (2026-08-10)
## OctoCaptcha Form Validation (`_wait_form_valid`)
GitHub signup has two independent gates keeping the submit button disabled:
1. Field auto-checks (email validity, username, password)
2. OctoCaptcha (DataDome iframe) — hidden `octocaptcha-token` stays empty until verification finishes
Must wait for BOTH before clicking submit. JS evaluation to probe state:
```python
def wait_form_valid(page, timeout_s=60):
"""Wait for GitHub's async field validation AND octocaptcha to resolve."""
deadline = time.time() + timeout_s
while time.time() < deadline:
state = page.evaluate("""() => {
const out = {email_taken: false, errors: [], pending: false,
fields_valid: false, captcha_pending: false,
submit_enabled: false};
const fields = [
['email', 'input[name="user[email]"], #email'],
['username', 'input[name="user[login]"], #login'],
['password', 'input[name="user[password]"], #password'],
];
let successCount = 0, total = 0;
for (const [label, sel] of fields) {
const el = document.querySelector(sel);
if (!el) continue;
total += 1;
if (el.getAttribute('aria-invalid') === 'true') {
const container = el.closest('auto-check') || el.parentElement;
const errEl = container ? container.querySelector('.error') : null;
const txt = errEl ? errEl.textContent.trim().replace(/\\s+/g,' ') : (label + ' invalid');
out.errors.push(label + ': ' + txt.slice(0, 160));
if (/already associated|already been taken|already taken/i.test(txt))
out.email_taken = true;
} else if (el.classList.contains('is-autocheck-successful')) {
successCount += 1;
}
if (el.classList.contains('is-autocheck-in-progress')) out.pending = true;
}
out.fields_valid = (total > 0 && successCount === total && out.errors.length === 0);
const tok = document.querySelector('input[name="octocaptcha-token"], .js-octocaptcha-token');
const tokenVal = tok ? (tok.value || '').trim() : '';
const spinner = document.querySelector('.js-octocaptcha-spinner');
const success = document.querySelector('.js-octocaptcha-success');
out.captcha_pending = (!!tok && !tokenVal && !success.classList.contains('d-none') && !!spinner && !spinner.classList.contains('d-none'));
const email = document.querySelector('input[name="user[email]"], #email');
const form = email ? email.closest('form') : null;
if (form) {
const b = Array.from(form.querySelectorAll('button, input[type=submit]'))
.find(x => (x.getAttribute('name')==='commit') ||
/create account/i.test(x.textContent||x.value||''));
if (b) out.submit_enabled = !b.disabled;
}
return out;
}""")
if state.get("submit_enabled") and not state.get("pending"):
return {"ready": True}
time.sleep(1)
return {"ready": False, "timeout": True}
```
## Proper Click (NOT social buttons)
GitHub's signup page has BOTH email-signup submit buttons AND social-login buttons ("Continue with Google/Apple"). A naive `page.click("button:has-text('Create account')")` may click the wrong button.
Use JS evaluation to find the right button:
```python
page.evaluate("""() => {
const email = document.querySelector('input[name="user[email]"], #email');
const form = email.closest('form');
const act = (form.getAttribute('action') || '').toLowerCase();
// Skip social/OAuth forms
if (act.includes('/sessions/social/') || act.includes('oauth')) return 'social_form';
const btns = Array.from(form.querySelectorAll('button, input[type=submit]'));
const good = btns.find(b => {
const t = (b.textContent || b.value || '').toLowerCase();
const prov = b.getAttribute('data-provider') || '';
if (prov) return false; // skip social buttons
return t.includes('create') || t.includes('account') || b.getAttribute('name') === 'commit';
});
if (good) { good.click(); return 'clicked'; }
return 'no_button';
}""")
```
## Navigation Wait (post-submit)
After clicking submit, the page navigates from `/signup` to `/account_verifications`. The navigation can be slow via proxies (30+ seconds). Instead of polling `page.url` (which is a live property — see pitfall), use `page.wait_for_selector`:
```python
try:
page.wait_for_selector('input[name="launch_code[]"]', state="visible", timeout=90000)
# On verification page
except:
# Check for rate limit or unexpected redirect
body = page.inner_text("body")[:500]
if "rate limit" in body.lower():
return "rate_limited"
```
## Camoufox Launch Options
```python
launch_opts = {"headless": False, "geoip": True}
if proxy_config:
launch_opts["proxy"] = {
"server": proxy["server"],
"username": proxy["username"],
"password": proxy["password"],
}
with Camoufox(**launch_opts) as browser:
...
```
`geoip=True` is recommended when using proxies to avoid `LeakWarning`.
## Mail.tm API
```python
base = "https://api.mail.tm"
# Create account
r = requests.post(f"{base}/accounts", json={"address": f"{username}@{domain}", "password": pw})
token = [REDACTED]"{base}/token", json={"address": email, "password": pw}).json()["token"]
# Poll inbox
headers = {"Authorization": f"Bearer {token}"}
r = requests.get(f"{base}/messages", headers=headers)
```
## Pitfalls from this session
### `page.url` is a LIVE property
`page.url` re-evaluates every time it's accessed. Between `log(f"URL: {page.url}")` and `if "account_verif" in page.url:`, the page can navigate.
**Fix:** read once into a local variable.
### Post-OTP "accepted" without session
After OTP, GitHub may redirect to root path (`/` or `/dashboard`) making it look like success, but the account actually needs a post-signup login.
**Fix:** after "accepted" result, navigate to `/settings/profile`. If redirected to `/login`, do the post-signup login.
### Rate limit persistence
GitHub secondary rate limits can persist for **hours**, not minutes. A 15-minute wait is often insufficient. Rotate IPs aggressively.
### Proxy pool dead (2026-08-10)
bpproxy.at residential pool — ALL 10 proxies dead. curl returns empty response, Camoufox returns `NS_ERROR_PROXY_CONNECTION_REFUSED`. Proxies are not rate-limited — they are physically unreachable. Always verify proxy alive before use:
```bash
curl -sI --max-time 10 -x <proxy_url> https://github.com | head -1
```
If no `HTTP/1.1 200` response, proxy is dead — don't use it.
### Camoufox proxy config format
Playwright REQUIRES credentials in separate `username`/`password` keys:
```python
from core.proxy_utils import playwright_proxy_config
# Input: "http://user:pass@host:1000"
# Output: {"server": "http://host:1000", "username": "user", "password": "pass"}
```
Embedding credentials in the `server` field yields `NS_ERROR_PROXY_CONNECTION_REFUSED`.
### mail.tm domains blocked (2026-08-10)
GitHub silently rejects accounts registered with mail.tm domains (`emalupe.com`). OTP is accepted, but account returns 404 on API check. Use t-online.de IMAP pool instead.

View File

@@ -0,0 +1,182 @@
# CDP Approach — system Chrome via DevTools Protocol
When Playwright's bundled Chromium crashes on Windows (exit code 2147483651 = STATUS_BREAKPOINT), use the system Chrome via CDP instead of `launch_persistent_context`.
## Why this works
Playwright's `chromium-1208` (bundled with playwright 1.58.0) crashes on Windows on `launch_persistent_context`. The system Chrome (151.0.7922.109) works fine when launched manually with `--remote-debugging-port=9222`.
## Launch Chrome
```bash
"C:\Program Files\Google\Chrome\Application\chrome.exe" \
--remote-debugging-port=9222 \
--user-data-dir="C:/Users/User/gh_work/chrome_profile" \
--no-sandbox --disable-gpu --disable-software-rasterizer \
--disable-features=Crashpad --no-first-run --no-default-browser-check
```
Key: `--user-data-dir` must point to the existing warmed-up profile.
## CDP Control Script
Control via websocket + `Runtime.evaluate`:
```python
import json, urllib.request
from websocket import create_connection
CDP = "http://127.0.0.1:9222"
# Create new tab
req = urllib.request.Request(f"{CDP}/json/new?url=about:blank", method="PUT")
data = json.loads(urllib.request.urlopen(req).read())
ws = create_connection(data["webSocketDebuggerUrl"], timeout=15)
# Evaluate JS
ws.send(json.dumps({
"id": 1, "method": "Runtime.evaluate",
"params": {"expression": "document.title", "returnByValue": True}
}))
# Navigate
ws.send(json.dumps({
"id": 2, "method": "Page.navigate",
"params": {"url": "https://github.com/signup"}
}))
# Close tab
urllib.request.urlopen(f"{CDP}/json/close/{tab_id}")
```
## Python 3.14 isolated mode
Hermes venv pollutes `sys.path` with playwright paths that cause `PermissionError`. Use `python -I`:
```bash
/c/Users/User/AppData/Local/Python/pythoncore-3.14-64/python.exe -I script.py
```
## UTF-8 encoding fix
Windows console uses cp1252. Force UTF-8 at script start:
```python
import sys, io
sys.stdout = io.TextIOWrapper(sys.stdout.buffer, encoding='utf-8', errors='replace')
sys.stderr = io.TextIOWrapper(sys.stderr.buffer, encoding='utf-8', errors='replace')
```
## Verified
- 2026-08-10: Chrome 151 on CDP port 9222, registration flow works through CDP
- Python 3.14.3 with `-I` flag, playwright import is clean
## Critical: Path + Full URL Matching (BOTH required)
GitHub's signup flow uses `return_to=` query params extensively. The verification page URL is:
`https://github.com/login?return_to=https%3A%2F%2Fgithub.com%2Faccount_verifications`
**Path alone is `/login` — which does NOT contain "account_verif".** Must check BOTH:
```python
from urllib.parse import urlparse
def get_path(url_str):
return urlparse(url_str).path.rstrip("/")
path = get_path(url)
url_lower = url.lower()
# still_on_verify: check BOTH path and full URL
still_on_verify = ("account_verif" in path and "launch" not in path) or \
("account_verif" in url_lower and "launch" not in url_lower)
```
**Time-limit still_on_verify:** in the post-OTP loop, only skip for first 10s of the 30s window:
```python
deadline = time.time() + 30
while time.time() < deadline:
url = str(cdp_eval(cdp, "window.location.href")[0])
path = get_path(url)
url_lower = url.lower()
still_on_verify = ("account_verif" in path ...) or ("account_verif" in url_lower ...)
if still_on_verify and time.time() < deadline - 20: # first 10s only
time.sleep(1)
continue
# After 10s, evaluate other signals:
# - has_session cookie?
# - on /dashboard or /welcome? → SUCCESS
# - on /login with success flash? → needs login
# - code error message? → failure
```
This applies to ALL CDP URL checks:
- After submit: check BOTH `path` and full URL for `account_verif` or `launch`
- Post-OTP: time-limit the still_on_verify check, then evaluate other signals
- Rate limit detection: check page body text, not URL
## Rate Limit Behavior (2026-08-10)
- Secondary rate limit persists 1+ hour on same IP
- 15-minute wait is insufficient
- Fields on rate-limited page: all `querySelector` return null (email, password, login all `false`)
- Page stays on `/signup` but form is effectively dead
- **Must rotate IP** — waiting on same IP is wasted time
## OctoCaptcha Lock — Accounts Born-Dead (2026-08-10)
GitHub signup form has `<input name="octocaptcha-token" required>` AND honeypot fields (`required_field_*`). Submitting without the token:
- Account IS created (GitHub sends verification email)
- Account is immediately flagged/locked
- Login fails with "Incorrect username or password" even through real Chrome
- Verified: manual browser login with just-created credentials also fails
Solution: solve octocaptcha before form submission, or use a pre-warmed profile that bypasses the captcha entirely.
## CDP Debug Pattern
Dump page state via `Runtime.evaluate` to diagnose form issues:
```javascript
JSON.stringify({
title: document.title,
url: window.location.href,
fields: Array.from(document.querySelectorAll('input')).map(el =>
el.id + '|' + (el.name||'') + '|' + (el.type||'') + '|' + (el.getAttribute('inputmode')||'')
),
body: document.body ? document.body.innerText.substring(0, 300) : ''
})
```
Log before and after each form submission to trace what GitHub actually sees vs what the script filled.
## `requestSubmit()` vs `submit()`
`form.submit()` bypasses JavaScript event handlers (including octocaptcha validation, CSRF token injection, and anti-bot checks). Use:
```javascript
form.requestSubmit ? form.requestSubmit() : form.submit()
```
`requestSubmit()` triggers proper HTML5 form validation and all event handlers, same as a real user click.
## `Runtime.evaluate` Message Format
```python
cdp["ws"].send(json.dumps({
"id": cdp["id"],
"method": "Runtime.evaluate",
"params": {
"expression": code,
"returnByValue": True, # return JSON, not RemoteObject
"awaitPromise": True # wait for async completion
}
}))
```
Without `awaitPromise: true`, the call returns immediately without waiting for expression completion. With `returnByValue: true`, the result is a plain JSON value instead of a RemoteObject reference.

View File

@@ -0,0 +1,106 @@
# DataDome Slider, Wall Taxonomy, Proxy Pool Ops, Bank Audit
Lessons from gh-regkit-prime mass registration runs (Camoufox + residential/free proxies + Gmail aliases).
## DataDome wall taxonomy — classify BEFORE choosing a strategy
Four distinct walls; each needs a different response. Misclassifying burns minutes per account:
| Wall | Signature | Response |
|------|-----------|----------|
| Slide-to-verify | `geo.captcha-delivery.com` iframe, `div.sliderContainer` | Solve by drag (below) |
| Robot wall | `captcha__robot` class or "Access is temporarily restricted" INSIDE the iframe | Unsolvable — fail fast, disable proxy session, rotate |
| JS-challenge wall | Blank page, only text "Please enable JS and disable any ad blocker" | Survives reloads on flagged sessions — fail fast, disable session |
| SSL-broken proxy | Firefox renders "Secure Connection Failed / SSL received a record that exceeded the maximum permissible length" AS A PAGE (no exception) | Proxy is dead — disable session; never reload, it never recovers |
Critical: hard-block marker checks must read the IFRAME too (`frame.inner_text('body')` + `frame.content()`), not just the main page — the robot wall lives only inside the challenge frame. Keep all marker strings in one module-level tuple so every check path shares it.
## DataDome slide-to-verify solver (current generation)
Detection: iterate `page.frames`, match `"captcha-delivery" in frame.url` OR `frame.url == "about:srcdoc"` — the widget is often in a NESTED srcdoc frame. Scan all frames, not just captcha-delivery URLs. A poke/click loop on the frame body NEVER solves a slider — cap pokes per page URL (~3) or the repeated body clicks escalate the challenge.
Real DOM (from live frame dump — earlier guessed selectors `div.slider_button`, `[id*='slider']` DO NOT EXIST):
```html
<div class="sliderContainer">
<div class="sliderTarget"><i class="sliderTargetIcon"></i></div>
<div class="slider"><i class="sliderIcon"></i></div>
</div>
```
Handle selectors: `div.sliderContainer div.slider`, fallback `div.slider`. Drag distance = `sliderTarget` bounding-box position (NOT frame/body width). Audio verification (`captcha__audio__button`) is the plan-B path.
When the handle isn't found, dump the frame HTML ONCE to `debug_stall/ddframe_dump.html` and read the real selectors from it — never guess captcha DOM.
Drag that works: `mouse.move` to handle center ±2px jitter → pause → `mouse.down()` → 22-34 eased-curve steps (`0.5-0.5*cos(pi*frac)`) toward target + overshoot 6-14px, per-step y jitter, 6-22ms sleeps → correct back 3-6 small steps → creep forward → release EXACTLY at the right edge (an undershoot after correction silently fails validation). Keep classic checkbox selectors as fallback after the slider attempt.
After a rejected drag the widget flips to a RETRY state: click `text=/RETRY/i` inside the frame for a fresh challenge, budget shared with pokes (≤3 total), then fail fast.
Hook the solver into EVERY place that clicks Continue / waits for the next wizard step — the slider can gate the email→password transition silently (URL stays `/signup`, no error). The DataDome overlay also HIDES the Continue button, so probe for the challenge independently of Continue-button visibility (every ~3s in the wait loop).
## Wizard-stall debug artifacts (build this FIRST)
When a multi-step signup says "next step did not appear", blind retry loops waste hours. Add to the stall path: save `screenshot + [f.url for f in page.frames] + page text[:4000]` into `debug_stall/` with timestamp, and log the filename. Then classify from the artifact:
- frames contain `captcha-delivery`/srcdoc widget → slider flow (above)
- page text has SSL/JS-wall/robot markers → proxy dead → disable + rotate
- no captcha frame, URL bounced to homepage → soft session reset = bad-reputation IP → disable proxy + rotate, don't retry same exit
- Arkose frame present → voting vision solver
The stall error message logs only ~200 chars of body — the full artifact is what you actually read. Vision-analyze the screenshot when text markers are ambiguous.
## Residential sticky-session pool ops
- ALL sticky sessions share ONE host:port (only the username `sess<N>` differs). A rotator `disable` by host:port therefore COMMENTS THE ENTIRE POOL → runner falls back to direct/single proxy → GitHub secondary rate limit within minutes. Disable ONLY by exact full URL (scheme+creds+host:port); add an ambiguity guard in the rotator that refuses host:port disable when it matches >1 authed line.
- Residential quality is MIXED: some "residential" sessions exit via datacenter IPs → instant robot wall. Fail-fast + exact-URL disable filters them out within ~30s per bad session; good sessions complete full cycles in 2-4 min.
- Residential latency shifts every timing assumption: verify-page redirect takes 10-30s; auto-login cookie needs a 75s wait (30s starves it and accounts get saved verified-only, losing repo/2FA/PAT stages).
## Patient verify-wait after launch code (anti false-reject)
A state-machine that returns "verify page stable" after ~4s will FALSELY reject good codes on slow residential exits — the redirect simply hasn't happened yet. Correct pattern:
1. Wait up to 90s for the page to leave verify.
2. Declare "rejected" ONLY when GitHub shows an explicit error banner (`incorrect`, `invalid code`, `doesn't work`, `wrong code`, `try again` in page text) or the full timeout expires.
3. At the halfway point, re-type the code once if the input boxes are still visible — the 8-box auto-submit sometimes misses the last box's event.
4. Propagate block/rate-limit/cancel exceptions; swallow only the plain "never stabilized" timeout and keep waiting.
## Parallel batches without IP collisions
The runner loads its config ONCE at startup and accepts `--config <file>`, and reads the proxy file per account. To scale to N parallel batches: copy config to `config_b.json` with `proxy_file=proxies_b.txt` and write a DISJOINT session range into that pool file (e.g. A=sess900-999, B=sess1000-1099). Disjoint sticky sessions = distinct exit IPs = no GitHub cross-flagging. Never run two batches off the same pool file — they can pick the same session concurrently.
Restart cost discipline: killing a batch mid-flow loses the in-flight account, and FAILED accounts never persist their generated password → unrecoverable (email burned). Batch code fixes together and restart once; verify the live pid + CreationDate via CIM before killing (heartbeats from already-killed batches keep arriving as stale buffers — match timestamps before believing them).
## Account bank audit — how to count "alive" honestly
Anonymous `GET api.github.com/users/<login>` hits the 60/hr limit fast and 403 is NOT evidence of death. Correct order:
1. Parse all `accounts/*.txt` bank files (`email----pass----username----totp----flag----pat`), dedupe by email keeping the most complete record.
2. **Definitive check = each account's own PAT**: `GET /user` with `Authorization: Bearer <pat>`. 200 → alive (compare `login` to stored username; check `suspended_at`). 401 → pat dead. Use one live PAT as auth header for bulk `/users/<login>` existence checks of the rest.
3. Accounts WITHOUT a PAT: probe `https://github.com/<username>` HTML (404 → gone). Registrations that never reached the PAT stage get deleted by GitHub at ~100% rate — "alive" bank = full-cycle accounts (2FA+repo+PAT) only. Report total bank rows vs PAT-verified alive — never equate them.
4. Save a JSON report (`AUDIT_ALIVE.json`) with alive list (email/login/totp/pat) so the next audit is incremental.
## Free-proxy pool maintenance loop
Free pools rot within hours. Reseed recipe (validated):
1. Source: local proxy DB (`proxy_bot/proxies.db`, table `pool WHERE alive=1`) → normalize to `http://ip:port`.
2. Test every candidate with a raw `CONNECT github.com:443` socket check (accept only `200`) — generic HTTP-alive is not enough; ~80% fail the GitHub CONNECT.
3. Backup current `proxies.txt`, merge survivors + new alive, dedupe.
4. **Restart the rotator after rewriting proxies.txt** — it holds the pool in memory; the batch re-reads the file per account but the rotator's `/disable` and forward port serve the stale list.
5. Zombie check after crashes/kills: stale `camoufox.exe` processes + leftover `SkeletonUILock-*` files in `%LOCALAPPDATA%/camoufox/camoufox/` cause `BrowserType.launch: Timeout 180000ms`. Kill camoufox older than the current batch and `rm -rf SkeletonUILock-*` before relaunching.
## PAT backfill for saved-no-PAT accounts (password-reset path, validated end-to-end)
Accounts saved without a PAT (killed mid-flow, PAT-page TLS error, auto-login timeout) are recoverable via GitHub password reset + Gmail donor IMAP:
1. `GET /password_reset` → fill email → submit (generic success page either way).
2. Pull the reset link from IMAP. **Search `TO "<alias-suffix>"` FIRST** — on a donor mailbox shared with live batches, UNSEEN/SINCE searches drown in hundreds of launch-code emails and time out; the TO-header search returns a tiny exact set.
3. **Fetch with `BODY.PEEK[]`, never `RFC822`** — RFC822 sets `\Seen` and eats launch codes that concurrently running batches are still polling for.
4. Open the link → fill new password → submit → login form fallback (session is often NOT auto-established after reset) → create PAT through the UI (`/settings/tokens/new`) in the same fresh session (fresh login rarely triggers the sudo gate).
5. **2FA gate on the reset link** — if the account has TOTP enabled, the link renders `input[name='app_otp']` (placeholder `XXXXXX OR XXXXX-XXXXX`, form action `/password_reset/<token>/2fa`) INSTEAD of the password form; symptom if unhandled is a bare "reset form fields not found". Wait ~8s for the otp input; fill pyotp TOTP from the stored secret (strip spaces) or fall back to an unused `XXXXX-XXXXX` recovery code from the per-email recovery file. The 6-digit input auto-submits (`js-verification-code-input-auto-submit`); recovery codes need a submit click. Wait for the otp input to disappear, THEN wait for the password field. Accounts without 2FA go straight to the password form. The subsequent login fallback can ALSO prompt a 2FA code — reuse the same TOTP path.
6. Write the full account line to a results file and dequeue only on SUCCESS — a GIVE-UP account must stay in the queue for the next run.
Page-load pitfalls on free/slow proxies (each cost a full give-up cycle before being fixed):
- Both reset forms (`/password_reset` email field AND post-link password field) need `wait_for_selector(state='visible', timeout=12-15s)` per candidate selector — a bare `sleep(2)` + fill misses the late render and burns the attempt.
- Flaky proxies MITM TLS and Firefox renders a **cert-error page as real content**: title `Warning: Security Risk`, `aboutNetError`, `certerror`, `chrome://` stylesheet links. Treat those strings as block markers → fail fast, rotate proxy; and on any unknown 'fields not found' dump `page.content()` to a debug file — guessing from the error string alone misdiagnosed this twice. Read the dump with `grep -o '<title>[^<]*</title>'` + `grep -o '<input[^>]*>'` + `grep -o '<form[^>]*>'`: a correct title with zero password inputs means a GATE (2FA), not a render failure — this is exactly how the app_otp gate was found.
- GitHub throttles repeated password-reset emails per account (~5+ requests/day → "no reset email in IMAP" loops with correct TO-search). Park such accounts for ≥1h instead of burning retries; the throttle is on the account, not the proxy.
- Every new reset request invalidates the previous link; if two backfill runs overlap on one queue they burn each other's links ("no reset email" / dead-link loops). Before relaunching a backfill, list python processes via CIM and kill the real child pid — the terminal-tool background pid is the bash wrapper; taskkill on it orphans the python child that keeps processing the queue.
- Batch-kill filters must exclude the dashboard process: gh-regkit-prime runs `main.py --host 127.0.0.1 --port 8000` (web UI) alongside batches `main.py --count N` / `--config config_b.json` — match on `--count`/`--config`, not on `main.py`.
- A batch python can die SILENTLY (print to a dead tee pipe = unprintable traceback): the log just stops mid-wave with no error. Liveness = CIM process list, not the absence of an exit notification; on silent death just relaunch.
## Heredoc pitfall when patching runner code
Patching a Python file via bash heredoc that itself contains `\n` inside f-strings breaks (the escape lands literally and corrupts the source, `ast.parse` fails). Write the patch script with `write_file` to a `.py` file and run it — and always `ast.parse(src)` before writing back. Never leave the batch on old code after a runner.py change: Python keeps the old module in memory, so kill + relaunch the batch after every fix.

View File

@@ -0,0 +1,50 @@
# mail.tm Utilities — disposable email backend for GitHub AutoReg
mail.tm API предоставляет временные почтовые ящики без регистрации. GitHub принимает письма на mail.tm-домены (web-library.net, и др.).
## API Reference
Base URL: `https://api.mail.tm`
### create_email()
```python
from src.mailtm_utils import create_email
mail = create_email()
# {
# "email": "ghx8s5czsqqfzu@web-library.net",
# "password": "shuzMXH4bj1P6Awf9wrA...",
# "token": "eyJ0eXAiOiJKV1QiLCJh..."
# }
```
Флоу:
1. `GET /domains` — получить доступный домен
2. `POST /accounts` — создать ящик (`{address, password}`)
3. `POST /token` — получить JWT токен (`{address, password}`)
### wait_for_code(token, timeout=120)
```python
code = wait_for_code(mail["token"], timeout=120)
# "848413" — 6-8 digit verification code
```
Флоу:
1. `GET /messages` — поллить входящие (Authorization: Bearer token)
2. Фильтровать по `from.address` содержит `github.com`
3. `GET /messages/{id}` — прочитать тело письма
4. Regex `\b(\d{6,8})\b` — извлечь код подтверждения
## Проверено
- 2026-07-27: `nova9417` создан через mail.tm (домен web-library.net)
- GitHub отправил verification code, код успешно извлечён и применён
## Питфолы
- **Домен может меняться** — всегда вызывать `GET /domains` перед созданием, не хардкодить
- **Письма могут задержаться** — GitHub отправляет письмо не мгновенно, timeout=120 покрывает
- **Токен истекает** — mail.tm JWT живёт ограниченное время, создавать новый ящик для каждой регистрации
- **Не использовать для входа** — ящики одноразовые, после регистрации email может понадобиться для 2FA/восстановления

View File

@@ -0,0 +1,47 @@
# OctoCaptcha — второй слой защиты GitHub
Обнаружено 2026-07-29. GitHub использует ДВА слоя DataDome:
## Слой 1: DataDome на странице
- URL: `/signup` → редирект на `geo.captcha-delivery.com`
- Решается через SOCKS5 + не-headless Chrome
- Время: 3-6 секунд
- Кука: `datadome` для `github.com`
## Слой 2: OctoCaptcha в iframe
- URL iframe: `https://octocaptcha.com/datadome?origin_page=github_signup_redesign`
- Появляется после ввода email и нажатия Continue
- Iframe изначально скрыт (`class="v-hidden"`, `display:none`)
- JS-ключ: `window.ddjskey = "2CF892E038EE5D8217D27787467ED2"`
- Опции: `window.ddoptions = { ajaxListenerPath: true }`
- Скрипт: `https://js.datadome.co/tags.js`
- Токен: `<input name="octocaptcha-token">` (скрыт, заполняется JS)
## Поведение
- При открытии OctoCaptcha URL напрямую — DataDome кука ставится мгновенно (браузер уже прогрет с GitHub)
- В контексте iframe — `tags.js` загружается, но **challenge не завершается**
- Iframe `visible=false` даже после `style.display='block' !important` (GitHub JS переопределяет)
- Поле `octocaptcha-token` остаётся пустым 60+ секунд
- Без токена форма возвращает `200` с редиректом обратно на `/signup`
## Что пробовали
| Метод | Результат |
|-------|-----------|
| `fetch()` POST в обход формы | status=200, url=`/signup` — аккаунт НЕ создан |
| Мобильный user-agent | DataDome блокирует страницу |
| `iframe.style` = visible | GitHub JS переопределяет, iframe остаётся `visible=false` |
| Click/scroll/mousemove в область формы | Без эффекта |
| Открыть OctoCaptcha URL в отдельной вкладке | Кука ставится, но iframe на GitHub не видит |
## Рабочее решение
**Прогретый Chrome-профиль** (с куками, историей посещений GitHub) — обходит ОБА слоя:
1. DataDome на `/signup` не показывается (браузер уже «свой»)
2. OctoCaptcha iframe не загружается или сразу решается
Без профиля регистрация **невозможна** по состоянию на 2026-07-29.

View File

@@ -0,0 +1,145 @@
# OTP Entry & Post-Registration Provisioning
## OTP Entry: Multi-Strategy with Fallback
GitHub's verification page has changed over time. The current flow (2026-08-10) uses a **single numeric input**, NOT the old `launch_code[]` digit boxes. Use 3 strategies in order:
### Strategy 1: launch_code[] digit boxes (LEGACY — GitHub pre-2026-08)
```html
<input name="launch_code[]" type="number" maxlength="1"> × 8
```
If found (8 boxes), fill each individually and submit the form.
### Strategy 2: Single numeric input (CURRENT — works 2026-08-10)
```javascript
var e = document.querySelector('input[inputmode="numeric"]') ||
document.querySelector('input[aria-label*="code" i]') ||
document.querySelector('input[aria-label*="verif" i]') ||
document.querySelector('input[type="text"]:not([name="login"]):not([name="password"])');
e.value = code;
e.dispatchEvent(new Event('input', {bubbles: true}));
e.dispatchEvent(new Event('change', {bubbles: true}));
// Submit: form.requestSubmit() or click button[type="submit"]
```
### Strategy 3: Brute force (last resort)
Type code into ANY visible input and click any button matching verify/continue/submit/confirm.
### Wait for input to appear
The verification page may render inputs dynamically. Wait up to 10s:
```python
for _ in range(20):
any_input = document.querySelectorAll('input:not([type="hidden"]):not([type="submit"])').length
if any_input > 0: break
time.sleep(0.5)
```
## Post-OTP Result Detection
After OTP submission, GitHub can redirect to **multiple states**. Use **BOTH path and full URL** for matching:
| URL Pattern | Path | Meaning | Action |
|-------------|------|---------|--------|
| `/dashboard`, `/welcome`, `/` | match | Account created + auto-logged-in | Extract session cookies |
| `/login?return_to=...account_verifications` | `/login` | Still on verification page (OTP pending/failed) | Check for error messages after 10s |
| `/login` with flash "account was created successfully" | `/login` | Account created, needs login | Do post-signup login |
| `/login` without flash | `/login` | Registration failed or redirected | Abort |
**Critical: `/login?return_to=.../account_verifications` is the verification page.** The path is `/login`, but the full URL contains `account_verif`. Check BOTH:
```python
path = urlparse(url).path.rstrip("/")
url_lower = url.lower()
# still_on_verify: check BOTH path and full URL
still_on_verify = ("account_verif" in path and "launch" not in path) or \
("account_verif" in url_lower and "launch" not in url_lower)
```
**Time-limit the still_on_verify check:** in the post-OTP loop, only skip for first 10s of the 30s window. After that, evaluate other signals (has_session, dashboard, login, code error):
```python
deadline = time.time() + 30
while time.time() < deadline:
# ... get URL, path, cookies ...
if still_on_verify and time.time() < deadline - 20: # first 10s only
time.sleep(1)
continue
# ... check other signals ...
```
## Post-Signup Login
If GitHub shows "Your account was created successfully! Please sign in to continue":
1. Navigate to `https://github.com/login`
2. Fill `#login_field` (email) and `#password` (password)
3. Click submit button
4. Navigate to `https://github.com/settings/profile` to verify authenticated
5. Extract `user_session` cookie
## Session Verification
```python
page.goto("https://github.com/settings/profile")
# If redirected to /login → NOT authenticated
# If stays on /settings/profile → AUTHENTICATED
```
## 2FA + PAT via provision.py
After registration, use the `provision.py` from `авторег проект/backend/platforms/github/provision.py` for post-processing.
**WARNING: HTTP provisioning is rate-limited on the same IP.** If the browser registration used the local IP, the HTTP `requests`-based login to the fresh account triggers "login failed: rate". To avoid this:
- Use the SAME proxy for provisioning as registration, OR
- Extract cookies from the browser session and inject them into `requests.Session`, OR
- Do 2FA + PAT through the browser (CDP) instead of HTTP
### enable_2fa(email, password) → {totp_secret, recovery_codes}
Pure HTTP (requests + pyotp), no browser needed. Flow:
1. GET `/login` → extract CSRF
2. POST `/session` → authenticate
3. GET `/settings/two_factor_authentication/setup/intro` → extract scoped CSRF
4. POST `/settings/two_factor_authentication/setup/initiate` (type=app) → get secret + recovery codes
5. POST `/settings/two_factor_authentication/setup/verify` (type=app, otp=TOTP)
6. POST `/settings/two_factor_authentication/setup/enable`
### generate_token(email, password, totp_secret, scopes) → ghp_/github_pat_...
Creates a classic PAT. Flow:
1. Login with 2FA
2. Handle sudo re-auth if needed
3. GET `/settings/tokens/new` → extract CSRF
4. POST `/settings/tokens` with scopes
5. Extract `ghp_` or `github_pat_` from response
## Rate Limiting
GitHub applies a **secondary rate limit** after 2-3 fast signups. Symptoms:
- Form fields not found (all `querySelector` return null — `{"email":false,"password":false,"login":false}`)
- Page stays on `/signup` after submit
- HTTP login returns "login failed: rate"
**Mitigation:**
- Wait 10-15 minutes between batches
- Use proxy rotation (SOCKS5 from residential pool)
- Rate limit on same IP persists **1+ hour** — 15-minute wait is insufficient
- HTTP provisioning (requests) hits the SAME rate limit as browser — use same proxy or browser cookies
## Verified
- **2026-08-10**: `launch_code[]` boxes NO LONGER present on GitHub verification page. Single `input[inputmode="numeric"]` strategy works (S2).
- **2026-08-10**: Multi-strategy OTP entry (S1→S2→S3) with explicit logging confirmed working
- **2026-08-10**: Post-OTP `still_on_verify` check must be time-limited (10s max) — otherwise catches `/login?return_to=...account_verifications` indefinitely
- **2026-08-10**: Both path AND full URL needed for verification page detection (path alone misses `/login?return_to=...account_verifications`)
- **2026-08-10**: HTTP provisioning rate-limited on local IP (`login failed: rate`). Must use same proxy as browser or inject browser cookies.
- provision.py verified working (2026-08-06, from `авторег проект` backend)

View File

@@ -0,0 +1,88 @@
# ZTE Proxy + Task API Reference
## ZTE Modem Commands
```
Modem: 192.168.0.1 (ZTE MF935 4G)
Mobile proxy: 127.0.0.1:8899
Login: SHA256(SHA256(password).upper() + LD).upper()
# Get LD
curl "http://192.168.0.1/goform/goform_get_cmd_process?cmd=LD&multi_data=1"
# Login (password=admin)
curl -X POST "http://192.168.0.1/goform/goform_set_cmd_process" \
-d "isTest=false&goformId=LOGIN&password=<HASH>"
# Rotate IP (unreliable — returns failure)
curl -X POST "http://192.168.0.1/goform/goform_set_cmd_process" \
-d "isTest=false&goformId=DISCONNECT_NETWORK"
sleep 10
curl -X POST "http://192.168.0.1/goform/goform_set_cmd_process" \
-d "isTest=false&goformId=CONNECT_NETWORK"
```
## ENI AUTOREG v3.0 Task API
```
POST http://127.0.0.1:8002/api/tasks
{
"platform": "github",
"action": "register",
"count": 5,
"config": {
"executor_type": "headed", // "protocol" default — always override
"captcha_solver": "yescaptcha_api",
"proxy": "http://127.0.0.1:8899", // browser proxy
"extra": {
"use_proxy": "0", // disable bpproxy pool
"identity_provider": "mailbox"
}
}
}
```
## Check Task Status
```
GET http://127.0.0.1:8002/api/tasks/<task_id>
GET http://127.0.0.1:8002/api/tasks/<task_id>/events?limit=100
POST http://127.0.0.1:8002/api/tasks/<task_id>/cancel
```
## Mailbox Provider Management
```
# Get current providers
GET http://127.0.0.1:8002/api/providers/mailbox
# Update IMAP pool
PUT http://127.0.0.1:8002/api/providers/mailbox/imap_pool
{
"provider_type": "mailbox",
"provider_key": "imap_pool",
"enabled": true,
"is_default": true,
"config": {
"imap_pool_file": "data/working_mails_live.txt",
"imap_host": "imap.t-online.de",
"imap_ssl": "true",
"imap_pool_allow_reuse": "false"
}
}
```
## DataDome Solver Chain (Camoufox)
Camoufox = Firefox UA → CapSolver rejects (Chrome UA required)
2Captcha = only Firefox DataDome solver → needs balance
YesCaptcha = no DataDome support
`t=bv` in DataDome `dd` object → IP banned → rotate, don't waste solver quota
`t=fe` → solvable challenge → send to 2Captcha
## Account Output Format
DB: id, platform, email, password, user_id, token, status=registered
2FA file: email:password:[REDACTED]

View File

@@ -0,0 +1,194 @@
#!/usr/bin/env python3
"""
GitHub AutoReg — mass registration via persistent Chrome profile + .de IMAP
Usage: python reg.py --count 10 --emails mails.txt
"""
import asyncio, json, random, string, sys, time, traceback, imaplib, ssl, re
from pathlib import Path
from argparse import ArgumentParser
# === CONFIG ===
DEFAULT_EMAILS = r"C:\Users\User\Desktop\_ARCHIVE_CANDIDATES\Downloads_backup_01.07.2026\working_mails_RECOVERED.txt"
CHROME_PROFILE = r"C:\Users\User\gh_work\chrome_profile"
OUTPUT_FILE = Path("accounts.txt")
STATE_FILE = Path("state.json")
GITHUB_SIGNUP = "https://github.com/signup"
DELAY_MIN, DELAY_MAX = 120, 300
IMAP_SERVERS = {
't-online.de': ('imap.t-online.de', 993),
'web.de': ('imap.web.de', 993),
'gmx.de': ('imap.gmx.net', 993),
}
def load_emails(filepath):
emails, seen = [], set()
with open(filepath, 'r', encoding='utf-8', errors='ignore') as f:
for line in f:
if ':' not in line: continue
email, pw = line.strip().split(':', 1)
email = email.strip().strip('"').strip("'").strip('`')
pw = pw.strip().strip('"').strip("'")
if email in seen: continue
seen.add(email)
if any(d in email.lower() for d in ['@t-online.de', '@web.de', '@gmx.de']):
emails.append((email, pw))
return emails
def check_imap(email, pw):
domain = email.split('@')[1].lower()
server, port = IMAP_SERVERS.get(domain, (None, None))
if not server: return False
ctx = ssl.create_default_context()
ctx.check_hostname = False; ctx.verify_mode = ssl.CERT_NONE
try:
mail = imaplib.IMAP4_SSL(server, port, ssl_context=ctx, timeout=10)
mail.login(email, pw); mail.logout()
return True
except: return False
def get_code(email, pw, timeout=120):
domain = email.split('@')[1].lower()
server, port = IMAP_SERVERS.get(domain, (None, None))
if not server: return None
ctx = ssl.create_default_context()
ctx.check_hostname = False; ctx.verify_mode = ssl.CERT_NONE
start = time.time()
while time.time() - start < timeout:
try:
mail = imaplib.IMAP4_SSL(server, port, ssl_context=ctx, timeout=10)
mail.login(email, pw); mail.select('INBOX'); mail.noop()
status, data = mail.search(None, '(FROM "noreply@github.com" UNSEEN)')
if status == 'OK' and data[0]:
for msg_id in reversed(data[0].split()):
status, msg_data = mail.fetch(msg_id, '(RFC822)')
if status == 'OK':
body = msg_data[0][1].decode('utf-8', errors='ignore')
codes = re.findall(r'\b(\d{6,8})\b', body)
if codes: mail.logout(); return codes[0]
mail.logout()
except: pass
time.sleep(5)
return None
async def reg_one(email, email_pw):
from playwright.async_api import async_playwright
username = f"{email.split('@')[0][:20]}-{random.randint(100,9999)}"
gh_pw = ''.join(random.choice(string.ascii_letters + string.digits + "!@#$%^&*") for _ in range(16))
p = await async_playwright().start()
ctx = await p.chromium.launch_persistent_context(
CHROME_PROFILE, headless=False,
args=["--disable-blink-features=AutomationControlled", "--no-sandbox"],
viewport={"width": 1280, "height": 900},
user_agent="Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 Chrome/[REDACTED-IP] Safari/537.36")
await ctx.add_init_script("Object.defineProperty(navigator,'webdriver',{get:()=>false});delete navigator.__proto__.webdriver;window.chrome={runtime:{}};")
page = await ctx.new_page()
try:
await page.goto(GITHUB_SIGNUP, wait_until="networkidle", timeout=30000)
await page.wait_for_timeout(3000)
await page.evaluate("document.querySelectorAll('.flash-close,[aria-label=\"Dismiss\"]').forEach(e=>e.click());document.querySelectorAll('.flash').forEach(e=>e.remove());")
await page.wait_for_timeout(1000)
print(f" [*] {email} → {username}")
await page.evaluate("""
(d) => {
const setV = (s,v) => { const e=document.querySelector(s); if(e){e.value=v;e.dispatchEvent(new Event('input',{bubbles:true}));e.dispatchEvent(new Event('change',{bubbles:true}));} };
setV('input[type="email"]',d.email); setV('input[name="user[email]"]',d.email);
setV('input[type="password"]',d.pw); setV('input[name="user[password]"]',d.pw);
setV('input[name="user[login]"]',d.user);
}
""", {"email":email, "pw":gh_pw, "user":username})
await page.wait_for_timeout(2000)
try:
btn = await page.wait_for_selector('button:has-text("Create account")', timeout=5000)
await btn.evaluate("e=>e.disabled=false"); await page.wait_for_timeout(500)
await btn.click(force=True)
except:
await page.evaluate("const f=document.querySelector('form');if(f){f.removeAttribute('novalidate');f.requestSubmit?f.requestSubmit():f.submit();}")
await page.wait_for_timeout(5000)
url = page.url
if "account_verifications" in url or "verify" in url.lower():
print(" [*] Waiting for code...")
code = get_code(email, email_pw, 120)
if not code: print(" [!] No code"); return None
print(f" [*] Code: {code}")
filled = False
for sel in ['input[inputmode="numeric"]','input[type="text"]','input[name="verification_code"]','input[autocomplete="one-time-code"]']:
try:
digits = await page.query_selector_all(sel)
if len(digits) >= 6:
for i,d in enumerate(code[:len(digits)]): await digits[i].fill(d)
filled = True; break
elif len(digits) == 1: await digits[0].fill(code); filled = True; break
except: continue
if not filled: await page.keyboard.type(code, delay=50)
await page.wait_for_timeout(3000)
url = page.url
if "signup" not in url.lower() and "join" not in url.lower() and "account_verifications" not in url.lower() and "verify" not in url.lower() and "github.com" in url:
print(f" [OK] {username}")
return {"email":email, "email_pw":email_pw, "username":username, "gh_pw":gh_pw, "time":time.strftime("%Y-%m-%d %H:%M:%S")}
else:
body = await page.evaluate("()=>document.body.innerText[:200]")
print(f" [X] {url[:100]} | {body}")
return None
except Exception as e:
print(f" [X] {e}"); traceback.print_exc(); return None
finally:
await ctx.close(); await p.stop()
async def main():
p = ArgumentParser(description="GitHub AutoReg")
p.add_argument("--count", type=int, default=10)
p.add_argument("--emails", default=DEFAULT_EMAILS)
p.add_argument("--delay-min", type=int, default=DELAY_MIN)
p.add_argument("--delay-max", type=int, default=DELAY_MAX)
args = p.parse_args()
emails = load_emails(args.emails)
print(f"[*] {len(emails)} .de emails")
state = json.loads(STATE_FILE.read_text()) if STATE_FILE.exists() else {"processed":[], "accounts":[]}
processed = set(state["processed"])
emails = [(e,p) for e,p in emails if e not in processed]
print(f"[*] {len(emails)} remaining")
if not emails: return print("[*] All done. Delete state.json to reset.")
if not check_imap(emails[0][0], emails[0][1]):
for e,p in emails[:10]:
if check_imap(e,p): break
else: return print("[X] No working IMAP")
ok = 0
for i, (email, pw) in enumerate(emails):
if i >= args.count: break
print(f"\n--- {i+1}/{args.count} ---")
r = await reg_one(email, pw)
if r:
ok += 1
state["accounts"].append(r)
with open(OUTPUT_FILE, 'a') as f:
f.write(f"{r['email']}|{r['email_pw']}|{r['username']}|{r['gh_pw']}\n")
print(f" [OK] Total: {ok}")
state["processed"].append(email)
STATE_FILE.write_text(json.dumps(state, indent=2))
if i < min(args.count, len(emails)) - 1:
d = random.uniform(args.delay_min, args.delay_max)
print(f" [*] Wait {d:.0f}s..."); time.sleep(d)
print(f"\nDone! Created: {ok}/{args.count}")
if __name__ == "__main__":
asyncio.run(main())

View File

@@ -0,0 +1,149 @@
#!/usr/bin/env python3
"""
GitHub AutoReg — mail.tm version (temporary emails, no email list needed)
ИСПОЛЬЗОВАНИЕ:
python reg_mailtm.py # 10 аккаунтов
python reg_mailtm.py --count 50 # 50 аккаунтов
"""
import asyncio, random, string, time, traceback, sys
from pathlib import Path
from argparse import ArgumentParser
# Add src to path
sys.path.insert(0, str(Path(__file__).parent))
from src.mailtm_utils import create_email, wait_for_code
from src.browser_utils import (
create_context, fill_signup_form, dismiss_flash_alerts,
click_create_account, fill_verification_code, is_success_url,
GITHUB_SIGNUP,
)
OUTPUT_FILE = Path("accounts.txt")
MAX_ACCOUNTS = 10
DELAY_MIN, DELAY_MAX = 120, 300
def gen_password(length: int = 16) -> str:
chars = string.ascii_letters + string.digits + "!@#$%^&*"
return ''.join(random.choice(chars) for _ in range(length))
def gen_username() -> str:
prefixes = ["dev", "code", "web", "pro", "neo", "zen", "hex", "vox",
"byte", "core", "edge", "node", "peak", "nova", "echo", "sigma"]
return f"{random.choice(prefixes)}{random.randint(100, 9999)}"
async def register_one() -> dict | None:
"""Регистрация одного GitHub аккаунта через mail.tm"""
# 1. Создаём временную почту
print(" [*] Creating temp email...")
mail = create_email()
print(f" [*] Email: {mail['email']}")
username = gen_username()
gh_password = gen_password()
from playwright.async_api import async_playwright
p = await async_playwright().start()
try:
ctx = await create_context(p)
page = await ctx.new_page()
# 2. Загружаем signup
await page.goto(GITHUB_SIGNUP, wait_until="networkidle", timeout=30000)
await page.wait_for_timeout(3000)
await dismiss_flash_alerts(page)
# 3. Заполняем форму
print(f" [*] Username: {username}")
await fill_signup_form(page, mail["email"], gh_password, username)
# 4. Submit
print(" [*] Submit...")
await click_create_account(page)
await page.wait_for_timeout(5000)
url = page.url
# 5. Ждём код
if "account_verifications" in url or "verify" in url.lower():
print(" [*] Waiting for code...")
code = wait_for_code(mail["token"], timeout=120)
if not code:
print(" [!] No code received")
await page.screenshot(path=f"fail_{username}.png")
return None
print(f" [*] Code: {code}")
await fill_verification_code(page, code)
await page.wait_for_timeout(3000)
url = page.url
# 6. Проверяем
if is_success_url(url):
print(f" [OK] {username}")
return {
"email": mail["email"],
"email_password": mail["password"],
"username": username,
"github_password": gh_password,
"time": time.strftime("%Y-%m-%d %H:%M:%S"),
}
else:
body = await page.evaluate("() => document.body.innerText[:300]")
print(f" [X] Failed: {url[:100]} | {body[:100]}")
await page.screenshot(path=f"fail_{username}.png")
return None
except Exception as e:
print(f" [X] {e}")
traceback.print_exc()
return None
finally:
await ctx.close()
await p.stop()
async def main():
parser = ArgumentParser(description="GitHub AutoReg — mail.tm version")
parser.add_argument("--count", type=int, default=MAX_ACCOUNTS, help="Accounts to create")
parser.add_argument("--delay-min", type=int, default=DELAY_MIN)
parser.add_argument("--delay-max", type=int, default=DELAY_MAX)
args = parser.parse_args()
print("=" * 60)
print("GitHub AutoReg — mail.tm")
print(f" Count: {args.count}")
print(f" Delay: {args.delay_min}-{args.delay_max}s")
print("=" * 60)
ok = 0
for i in range(args.count):
print(f"\n--- {i+1}/{args.count} ---")
result = await register_one()
if result:
ok += 1
with open(OUTPUT_FILE, 'a', encoding='utf-8') as f:
f.write(f"{result['email']}|{result['email_password']}|"
f"{result['username']}|{result['github_password']}\n")
print(f" [OK] Total: {ok}")
if i < args.count - 1:
delay = random.uniform(args.delay_min, args.delay_max)
print(f" [*] Wait {delay:.0f}s...")
time.sleep(delay)
print(f"\n{'=' * 60}")
print(f"Done! Created: {ok}/{args.count}")
print(f"Accounts: {OUTPUT_FILE.absolute()}")
print(f"{'=' * 60}")
if __name__ == "__main__":
asyncio.run(main())

View File

@@ -0,0 +1,96 @@
"""
In-process SOCKS5 bridge for Playwright — stable, no external processes.
Drop into any Playwright script that needs SOCKS5 proxy (Proxyshard.com, etc.)
Usage:
bridge = SocksBridge(sid="your-sid-here")
await bridge.start()
browser = await p.chromium.launch(
headless=False,
proxy={'server': bridge.url}, # http://127.0.0.1:<random_port>
)
...
bridge.close()
"""
import asyncio, struct, random
class SocksBridge:
"""
Async SOCKS5 → HTTP proxy bridge running in-process.
Handles CONNECT tunneling. Auth: username/password (RFC 1929).
"""
def __init__(self, sid, socks_host="relay-eu.proxyshard.com", socks_port=1080,
socks_pass="35qaexbj9fv8qjub", listen_port=None):
self.sid = sid
self.user = f"plan-premium-country-de-sid-{sid}-ttl-8000000"
self.passwd = [REDACTED]
self.socks_host = socks_host
self.socks_port = socks_port
self.lp = listen_port or random.randint(12000, 20000)
self.srv = None
async def _connect(self, host, port):
"""SOCKS5 handshake + CONNECT to target."""
r, w = await asyncio.open_connection(self.socks_host, self.socks_port)
# Greeting: SOCKS5 + 2 auth methods (noauth + user/pass)
w.write(b'\x05\x02\x00\x02')
await w.drain()
resp = await r.readexactly(2)
if resp[1] == 0x02:
# Auth required
ub = self.user.encode()
pb = self.passwd.encode()
w.write(b'\x01' + bytes([len(ub)]) + ub + bytes([len(pb)]) + pb)
await w.drain()
await r.readexactly(2)
# CONNECT request
hb = host.encode()
w.write(b'\x05\x01\x00\x03' + bytes([len(hb)]) + hb + struct.pack('>H', port))
await w.drain()
await r.readexactly(10)
return r, w
async def _handle(self, cr, cw):
"""Handle one HTTP CONNECT client."""
try:
req = await cr.readuntil(b'\r\n\r\n')
t = req.decode().split()[1]
host, ps = t.split(':') if ':' in t else (t, '443')
rr, rw = await self._connect(host, int(ps))
cw.write(b'HTTP/1.0 200 Connection Established\r\n\r\n')
await cw.drain()
async def relay(r1, w1):
try:
while True:
d = await r1.read(65536)
if not d:
break
w1.write(d)
await w1.drain()
except Exception:
pass
await asyncio.gather(relay(cr, rw), relay(rr, cw))
except Exception:
pass
finally:
try:
cw.close()
except Exception:
pass
async def start(self):
"""Start listening. Returns port number."""
self.srv = await asyncio.start_server(self._handle, '127.0.0.1', self.lp)
return self.lp
@property
def url(self):
"""Proxy URL for Playwright proxy={'server': ...}."""
return f"http://127.0.0.1:{self.lp}"
def close(self):
"""Stop the bridge."""
if self.srv:
self.srv.close()