Files
autoreg-skills-max/skills/github-autoreg/references/camoufox-registration-patterns.md

162 lines
7.4 KiB
Markdown

# 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.