83 lines
3.3 KiB
Markdown
83 lines
3.3 KiB
Markdown
# Native reCAPTCHA v3 via Chrome CDP
|
|
|
|
Conol.ai uses reCAPTCHA v3 (site key: `6Lc3wmAtAAAAAB9YBPXQtT9uGGsH3ul6LQBc5AUu`).
|
|
The captcha is **domain-locked** — external solvers (2Captcha, AntiCaptcha, CapSolver) do NOT work.
|
|
|
|
## Solution: Chrome CDP Native Execution
|
|
|
|
The browser already has the reCAPTCHA script loaded. Execute `grecaptcha.execute()` directly in the page context.
|
|
|
|
### Generic reCAPTCHA executor (playwright)
|
|
|
|
```python
|
|
async def recaptcha(page, action: str) -> str:
|
|
"""Execute reCAPTCHA v3 and return token."""
|
|
return await page.evaluate(
|
|
"""async ({siteKey, action}) => {
|
|
// Ensure grecaptcha is loaded
|
|
if (!window.grecaptcha) {
|
|
await new Promise((resolve, reject) => {
|
|
const old = document.getElementById('eni-recaptcha');
|
|
if (old) old.remove();
|
|
const script = document.createElement('script');
|
|
script.id = 'eni-recaptcha';
|
|
script.src = `https://www.recaptcha.net/recaptcha/api.js?render=${encodeURIComponent(siteKey)}`;
|
|
script.onload = resolve;
|
|
script.onerror = () => reject(new Error('recaptcha load failed'));
|
|
document.head.appendChild(script);
|
|
});
|
|
}
|
|
await new Promise((resolve) => window.grecaptcha.ready(resolve));
|
|
return await window.grecaptcha.execute(siteKey, {action});
|
|
}""",
|
|
{"siteKey": SITE_KEY, "action": action},
|
|
)
|
|
```
|
|
|
|
### Usage in registration flow
|
|
|
|
```python
|
|
# Step 1: Sign Up
|
|
signup_token = await recaptcha(page, "sign_up")
|
|
r = await json_fetch(page, "/api/invites/register", method="POST",
|
|
captcha=signup_token, body={...})
|
|
|
|
# Step 2: Send Verification Email
|
|
verify_token = await recaptcha(page, "send_verification_email")
|
|
r = await json_fetch(page, "/api/auth/send-verification-email", method="POST",
|
|
captcha=verify_token, body={...})
|
|
|
|
# Step 3: Sign In
|
|
signin_token = await recaptcha(page, "sign_in")
|
|
r = await json_fetch(page, "/api/auth/sign-in/email", method="POST",
|
|
captcha=signin_token, body={...})
|
|
```
|
|
|
|
### JSON fetch helper (via page.evaluate)
|
|
|
|
```python
|
|
async def json_fetch(page, path: str, *, method="GET", body=None, captcha=None):
|
|
"""Fetch API endpoint from page context (uses browser's cookies)."""
|
|
return await page.evaluate(
|
|
"""async ({path, method, body, captcha}) => {
|
|
const headers = {Accept: 'application/json'};
|
|
if (body !== null) headers['Content-Type'] = 'application/json';
|
|
if (captcha) headers['x-captcha-response'] = captcha;
|
|
const r = await fetch(path, {method, headers, credentials: 'include',
|
|
body: body === null ? undefined : JSON.stringify(body)});
|
|
const text = await r.text();
|
|
let data = null;
|
|
try { data = JSON.parse(text); } catch (_) {}
|
|
return {status: r.status, url: r.url, text: text.slice(0, 2000), data};
|
|
}""",
|
|
{"path": path, "method": method, "body": body, "captcha": captcha},
|
|
)
|
|
```
|
|
|
|
## Key Points
|
|
|
|
- Captcha token is sent via `x-captcha-response` header (NOT in body)
|
|
- Use `page.evaluate` to call browser APIs within the origin context
|
|
- Always clear cookies/storage between registrations to avoid session conflicts
|
|
- Increasing delays between accounts (8s + 3s per index) reduces HTTP 429 errors
|