Automated Qoder.com account registration + PAT creation via Google OAuth.
- Signup: Google OAuth (GSuite accounts) via CloakBrowser + proxy rotation
- PAT Creation: Direct API call (
POST /api/v1/me/personal-access-tokens) with custom expiry - No CAPTCHA: Google OAuth path bypasses Aliyun CAPTCHA entirely
- Proxy Rotation: Round-robin from
proxies.txt, fallback to direct if empty - Portable: No external paths — everything stays inside the repo
git clone <repo-url> qoder-farm
cd qoder-farm
pip install cloakbrowserCreate accounts.txt (GSuite accounts, email|password per line):
your-email@your-domain.com|your-password
another@your-domain.com|your-password
Create proxies.txt (one proxy per line, supports multiple formats, or leave empty for direct):
# Full URLs
socks5://127.0.0.1:1081
socks5://user:pass@203.0.113.1:1080
http://user:pass@proxy.example.com:8080
# Colon-delimited (auto-converted to socks5://)
31.56.127.193:7684:qyduzmmu:zxcbbqirra5h
# host:port only (no auth)
203.0.113.1:1080
Empty
proxies.txt= direct connection (no proxy).
./run.sh # Run with defaults (2 concurrent workers)
./run.sh --verify # Verify PATs after creation
./run.sh --workers 4 # 4 concurrent workers
./run.sh --workers 1 # Sequential mode (old behavior)
./run.sh --expiry-days 730 # Custom expiryOr directly with Python:
python3 main.py
python3 main.py --verify
python3 main.py --accounts-file custom.txt --proxies-file custom_proxies.txtOutput goes to results/:
results/
├── pats.txt # email|pat format (one per line)
├── results.json # Full JSON with timestamps, proxies, verification status
└── screenshots/ # Failure screenshots for debugging
qoder-farm/
├── run.sh # One-command runner (reads accounts.txt + proxies.txt)
├── main.py # CLI entrypoint (argparse)
├── accounts.txt # GSuite accounts (gitignored)
├── proxies.txt # Proxy list (gitignored)
├── accounts.txt.example # Template
├── proxies.txt.example # Template
├── requirements.txt
├── .gitignore
├── README.md
├── src/
│ └── qoder_farm/
│ ├── __init__.py
│ ├── config.py # Paths, defaults, API endpoints (all relative to repo)
│ ├── browser.py # CloakBrowser lifecycle (launch with proxy, close)
│ ├── proxy_pool.py # ProxyRotator (round-robin from proxies.txt)
│ ├── google_oauth.py # Google OAuth flow (email → password → consent → Qoder)
│ ├── qoder_api.py # PAT create/list/verify via Qoder API
│ ├── pipeline.py # Orchestrator (proxy rotation + signup + PAT + results)
│ └── utils.py # Timestamp calc, PAT file I/O, logging, validation
├── scripts/
│ └── register_single.sh # Quick wrapper for single account
├── results/ # Output (gitignored except .gitkeep)
│ ├── pats.txt
│ ├── results.json
│ └── screenshots/
└── tests/
└── test_utils.py # Unit tests
--accounts-file PATH Accounts file (default: accounts.txt)
--proxies-file PATH Proxies file (default: proxies.txt, empty=direct)
--pat-name NAME PAT display name (default: hermes-cli)
--expiry-days N PAT expiry in days (default: 365)
--pat-file PATH PAT output file (default: results/pats.txt)
--results-file PATH JSON results file (default: results/results.json)
--workers N Number of concurrent workers (default: 2, 1=sequential)
--verify Verify PAT via openapi.qoder.sh after creation
POST https://qoder.com/api/v1/me/personal-access-tokens
Content-Type: application/json
x-csrf-token: _echo_csrf_using_sec_fetch_site_
x-requested-with: XMLHttpRequest
bx-v: 2.5.35
Cookie: qoder_session_cookie=... (set after Google OAuth login)
Body: {"name": "hermes-cli", "expires_at": <epoch_ms>}
Response: 201 → {"token": "pt-...", "expires_at": <ts>, ...}
Key insight: expires_at is epoch milliseconds. Set to now + 365 days for 1-year PAT.
CSRF uses Sec-Fetch-Site header validation (no separate token needed).
curl -s -X POST https://openapi.qoder.sh/api/v1/jobToken/exchange \
-H "Content-Type: application/json" \
-d '{"personal_token": "pt-..."}'Returns {"token": "jt-...", "refresh_token": "jrt-..."} — PAT is valid.
| Variable | Default | Description |
|---|---|---|
QODER_PAT_NAME |
hermes-cli |
Default PAT name |
QODER_PAT_EXPIRY_DAYS |
365 |
PAT expiry in days |
QODER_HEADLESS |
true |
Run browser headless |
QODER_WORKERS |
2 |
Number of concurrent workers |
python3 -m pytest tests/ -vMIT