autoreg-skills-max: initial public release (restored, redacted, hardened)
This commit is contained in:
@@ -0,0 +1,216 @@
|
||||
# GitHub Autoreg — Email Domain Blocklist & Working Providers
|
||||
|
||||
Captured 2026-08-10 from extended autoreg testing with CDP + Camoufox.
|
||||
|
||||
## Blocked Disposable Domains
|
||||
|
||||
GitHub **silently rejects** accounts registered with these domains. The signup form accepts the OTP, GitHub redirects to the home page (`github.com/`), but the account **never exists** (API returns 404). No error message, no warning — the rejection is silent.
|
||||
|
||||
| Domain | Provider | Status |
|
||||
|--------|----------|--------|
|
||||
| `emalupe.com` | mail.tm | ❌ Silently blocked |
|
||||
| `*.mail.tm` | mail.tm (all domains) | ❌ Likely blocked |
|
||||
|
||||
## Confirmed Working Providers
|
||||
|
||||
| Domain | Provider | Notes |
|
||||
|--------|----------|-------|
|
||||
| `t-online.de` | Telekom Germany | 212 accounts created 6-7 Aug 2026, all alive. IMAP-based mailbox. Requires `t_online.py` provider from `авторег проект/backend/providers/mailbox/`. 17,872 emails in `data/working_mails_live.txt`. |
|
||||
|
||||
## Detection: How to Tell if Account Was Created
|
||||
|
||||
After OTP acceptance, the account may or may not exist:
|
||||
|
||||
```python
|
||||
import urllib.request, json
|
||||
url = f"https://api.github.com/users/{username}"
|
||||
req = urllib.request.Request(url, headers={"User-Agent": "Mozilla/5.0"})
|
||||
resp = urllib.request.urlopen(req, timeout=10)
|
||||
data = json.loads(resp.read())
|
||||
if "login" in data:
|
||||
print(f"LIVE: {username} (created: {data.get('created_at')})")
|
||||
else:
|
||||
print(f"DEAD: {username} — 404 Not Found")
|
||||
```
|
||||
|
||||
**Key insight:** `pending_login` status in the CDP script means "OTP was accepted but session not confirmed" — NOT that the account exists. Always verify with the GitHub API before assuming the account was created.
|
||||
|
||||
## Pattern: Silent Rejection Flow
|
||||
|
||||
1. Signup form submitted → `account_verifications` page
|
||||
2. OTP code entered → form accepted
|
||||
3. GitHub redirects to `https://github.com/` (home page)
|
||||
4. No `user_session` cookie set
|
||||
5. Navigate to `/settings/profile` → redirected to `/login`
|
||||
6. GitHub API: `404 Not Found`
|
||||
|
||||
The account was **never created**. The OTP form accepts the code but the server-side validation rejects the signup silently.
|
||||
|
||||
## Camoufox OTP Submit
|
||||
|
||||
The launch-code form uses `form[data-target="launch-code.form"]`. The correct way to submit after filling individual `launch_code[]` boxes:
|
||||
|
||||
```python
|
||||
# Fill each box
|
||||
for i, digit in enumerate(code):
|
||||
try:
|
||||
boxes[i].click(timeout=2000) # focus
|
||||
except Exception:
|
||||
pass
|
||||
boxes[i].fill(digit)
|
||||
time.sleep(0.08)
|
||||
|
||||
time.sleep(0.6)
|
||||
|
||||
# Submit via JS (Playwright's click may fail on disabled button)
|
||||
page.evaluate("""
|
||||
const f = document.querySelector('form[data-target="launch-code.form"]');
|
||||
if (f) f.requestSubmit();
|
||||
""")
|
||||
```
|
||||
|
||||
`requestSubmit()` is preferred over `form.submit()` because it triggers the form's submit event handlers and validation.
|
||||
|
||||
## Python 3.11 Isolation from Hermes Venv
|
||||
|
||||
When running `py -3.11`, the hermes venv's site-packages (`C:\Users\User\AppData\Local\hermes\hermes-agent-0.18.2-latest\venv\Lib\site-packages\`) can leak into `sys.path`, causing:
|
||||
- `PermissionError: [Errno 13] Permission denied` on playwright imports
|
||||
- Wrong versions of packages being loaded
|
||||
|
||||
**Fix:** Set `PYTHONPATH=` to empty before running, or use `-I` flag:
|
||||
```bash
|
||||
PYTHONPATH= py -3.11 main.py
|
||||
# or use -I (isolated mode):
|
||||
py -3.11 -I script.py
|
||||
```
|
||||
|
||||
## ENI AUTOREG v3.0 Backend
|
||||
|
||||
- Location: `C:\Users\User\Desktop\авторег проект\backend\`
|
||||
- Start: `cd backend && PYTHONPATH= py -3.11 main.py` (runs on port 8002)
|
||||
- **CRITICAL: `PYTHONPATH=` is mandatory** — without it, hermes-venv playwright leaks into Python 3.11's sys.path causing `PermissionError`
|
||||
- API: FastAPI with `/api/tasks` endpoint for task creation
|
||||
- Task API: `POST /api/tasks` with `{"platform":"github","action":"register","count":N,"config":{...}}`
|
||||
- GitHub platform: `browser_register` capability via Camoufox (supported executors: `headless`, `headed`)
|
||||
- Mailbox: `t-online.de` via IMAP pool (17,872 emails in `data/working_mails_live.txt`). Provider key: `imap_pool`
|
||||
- DB: `account_manager.db` (SQLite)
|
||||
- Provision: `provision.py` (2FA + PAT via HTTP, no browser needed)
|
||||
- Warmup: `_warmup_account()` in `browser_register.py` (avatars, bio, display name)
|
||||
|
||||
### Task Config Format
|
||||
|
||||
**Correct format for headed browser with ZTE proxy:**
|
||||
```json
|
||||
{
|
||||
"platform": "github",
|
||||
"action": "register",
|
||||
"count": 3,
|
||||
"config": {
|
||||
"executor_type": "headed",
|
||||
"captcha": "yescaptcha_api",
|
||||
"proxy": "http://127.0.0.1:8899",
|
||||
"extra": {
|
||||
"use_proxy": "0"
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**CRITICAL**: `use_proxy:"0"` MUST be in `extra` sub-dict, NOT top-level config. The task runtime reads `extra.get("use_proxy")` (line 252 of `services/task_runtime.py`). If omitted or placed in the wrong location, the proxy pool's round-robin selection overrides the custom `proxy` URL.
|
||||
|
||||
**Default executor**: If `executor_type` is omitted, defaults to `"protocol"` (HTTP-based), which GitHub doesn't support. Must explicitly set `"headed"` or `"headless"`.
|
||||
|
||||
### DataDome Captcha Solver Compatibility
|
||||
|
||||
GitHub triggers DataDome challenge on the signup page. Only **2Captcha** supports DataDome:
|
||||
|
||||
| Solver | DataDome Support | Status (2026-08-12) |
|
||||
|--------|-----------------|---------------------|
|
||||
| 2Captcha (`twocaptcha_api`) | ✅ Yes | Balance: ZERO (`ERROR_ZERO_BALANCE`) |
|
||||
| YesCaptcha (`yescaptcha_api`) | ❌ `不支持 DataDome` | — |
|
||||
| CapSolver (`capsolver_api`) | ❌ `不支持 DataDome` | — |
|
||||
|
||||
When DataDome pre-solve fails, the browser continues to the signup form but may encounter additional challenges. Without a working DataDome solver, the email input may not become visible within timeout.
|
||||
|
||||
### IMAP Pool Provider Config (Pitfall)
|
||||
|
||||
When updating the `imap_pool` provider via `PUT /api/providers/mailbox/imap_pool`, the `config` dict REPLACES the existing config entirely. Always include the full config:
|
||||
|
||||
```json
|
||||
{
|
||||
"provider_type": "mailbox",
|
||||
"provider_key": "imap_pool",
|
||||
"is_default": true,
|
||||
"config": {
|
||||
"imap_pool_file": "data/working_mails_live.txt",
|
||||
"imap_host": "secureimap.t-online.de",
|
||||
"imap_port": 993,
|
||||
"imap_ssl": true,
|
||||
"imap_pool_allow_reuse": "true"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Sending `"config":{}` wipes all fields and the pool fails to load emails.
|
||||
|
||||
## Proxy Pool Status
|
||||
|
||||
| Proxy | Type | Status | Date |
|
||||
|-------|------|--------|------|
|
||||
| bpproxy.at (all) | residential + datacenter | ❌ ALL DEAD — 10/10 proxies return empty response, curl exits 0 but no HTTP headers | 2026-08-10 |
|
||||
| `residential-x.bpproxy.at:1000` | residential hardsession | ❌ Dead | 2026-08-10 |
|
||||
| `datacenter-x.bpproxy.at:2000` | datacenter hardsession | ❌ Dead | 2026-08-10 |
|
||||
| **ZTE mobile proxy** (`127.0.0.1:8899`) | 4G mobile (Vodafone UA) | ✅ **WORKS** — browser reaches GitHub, DataDome pre-solve triggers | 2026-08-12 |
|
||||
|
||||
### ZTE Mobile Proxy (2026-08-12)
|
||||
|
||||
The ZTE MF935 4G modem provides a mobile IP that bypasses GitHub's proxy blocking. The `mobile_proxy.py` in `backend/platforms/android_gmail/` creates a local HTTP/CONNECT proxy on port 8899 that binds to the ZTE interface (192.168.0.4).
|
||||
|
||||
**How to use:**
|
||||
1. ZTE modem must be connected (check: `curl http://192.168.0.1` → redirect to index.html)
|
||||
2. Mobile proxy must be running on port 8899 (check: `netstat -ano | grep ":8899"`)
|
||||
3. Task config: `{"executor_type":"headed","captcha":"yescaptcha_api","proxy":"http://127.0.0.1:8899","extra":{"use_proxy":"0"}}`
|
||||
4. **CRITICAL**: `use_proxy:"0"` MUST be in `extra` sub-dict, not top-level config. Without it, the proxy pool overrides the custom proxy URL.
|
||||
|
||||
**Network topology:**
|
||||
- ZTE interface: `192.168.0.4` → gateway `192.168.0.1`
|
||||
- Default route: WiFi `192.168.50.1` (metric 55), ZTE `192.168.0.1` (metric 10000 — backup only)
|
||||
- Mobile proxy forces upstream through `192.168.0.4` via `socket.bind((src_ip, 0))`
|
||||
|
||||
**Proxy format:** `http://bpuser-XXX:[REDACTED-EMAIL]:[REDACTED]
|
||||
|
||||
**Playwright/Camoufox proxy format:** Must use `playwright_proxy_config()` from `core/proxy_utils.py` — splits URL into `{server, username, password}`. Embedding credentials in the `server` field causes `NS_ERROR_PROXY_CONNECTION_REFUSED`.
|
||||
|
||||
**Testing proxies:** Use `curl -sI --max-time 10 -x "$URL" https://github.com` — must return HTTP/1.1 200 with headers. Empty response = dead proxy.
|
||||
|
||||
## CDP Sync Client (reg_cdp.py pattern)
|
||||
|
||||
The CDP Chrome auto-registration uses a sync (non-async) client with `urllib.request` + `websocket-client`. Simpler than the async aiohttp pattern for single-threaded flows:
|
||||
|
||||
```python
|
||||
import json, urllib.request, websocket
|
||||
|
||||
def cdp_connect(ws_url):
|
||||
ws = websocket.create_connection(ws_url, timeout=15)
|
||||
ws.send(json.dumps({"id":1,"method":"Page.enable"}))
|
||||
ws.send(json.dumps({"id":2,"method":"Runtime.enable"}))
|
||||
return ws
|
||||
|
||||
def cdp_cmd(ws, method, params=None):
|
||||
mid = random.randint(10000, 99999)
|
||||
ws.send(json.dumps({"id":mid,"method":method,"params":params or {}}))
|
||||
while True:
|
||||
r = json.loads(ws.recv())
|
||||
if r.get("id") == mid:
|
||||
return r.get("result", {})
|
||||
|
||||
def cdp_eval(ws, code):
|
||||
r = cdp_cmd(ws, "Runtime.evaluate", {"expression":code,"returnByValue":True})
|
||||
return r.get("result",{}).get("value")
|
||||
|
||||
# Usage:
|
||||
ws = cdp_connect("ws://127.0.0.1:9222/devtools/page/XXX")
|
||||
cdp_eval(ws, "window.location.href")
|
||||
```
|
||||
|
||||
Chrome launch: `chrome.exe --remote-debugging-port=9222 --user-data-dir=C:\Users\User\gh_work\chrome_profile`
|
||||
Reference in New Issue
Block a user