universal-autoreg: initial public release (restored, redacted, hardened)

This commit is contained in:
baradok609a1a863cbgp
2026-10-20 02:16:17 +03:00
commit 888c07b949
32 changed files with 6363 additions and 0 deletions

275
providers/conol_captcha.py Normal file
View File

@@ -0,0 +1,275 @@
"""conol_captcha.py — reCAPTCHA v3 solver chain for conol.ai registration.
Providers, tried in order:
1. chrome_cdp — FREE. Local Chrome with a warm profile renders conol.ai and
executes window.grecaptcha.execute(siteKey, {action}) over CDP. A real
browser + aged profile typically scores >= 0.3, which is all conol's
minScore gate requires. No third-party service, no per-solve cost.
2. anticaptcha — PAID fallback (existing behaviour), keys from config.json.
Public API (matches what conol_register.py needs):
solve(action) -> Optional[str] # token or None
warmup() -> None # optional: pre-launch Chrome + load page
Chrome path/profile/port come from config.json -> captcha section:
{"chrome_path": "...", "cdp_port": 9228, "profile_dir": "...", "providers": ["chrome_cdp","anticaptcha"]}
Defaults are sane; env overrides: CONOL_CAPTCHA_PROVIDERS (comma list), CONOL_CDP_PORT.
Stdlib + playwright only. The browser is launched ONCE and reused across solves
(page kept on conol.ai); the token is minted per solve with the right action.
"""
from __future__ import annotations
import json
import os
import re
import subprocess
import sys
import time
import urllib.request
from pathlib import Path
from typing import Optional
_ROOT = Path(__file__).resolve().parent
def _cfg() -> dict:
p = _ROOT / "config.json"
try:
return json.loads(p.read_text(encoding="utf-8")) if p.exists() else {}
except Exception:
return {}
_C = _cfg().get("captcha", {}) or {}
CONOL = _cfg().get("conol", {}) or {}
SITE_KEY = os.environ.get("CONOL_SITE_KEY") or CONOL.get("site_key", "")
BASE_URL = os.environ.get("CONOL_BASE_URL") or CONOL.get("base_url", "https://conol.ai")
DEFAULT_CHROME = r"C:\Program Files\Google\Chrome\Application\chrome.exe"
CHROME_PATH = os.environ.get("CONOL_CHROME_PATH") or _C.get("chrome_path") or DEFAULT_CHROME
CDP_PORT = int(os.environ.get("CONOL_CDP_PORT") or _C.get("cdp_port") or 9228)
PROFILE_DIR = _C.get("profile_dir") or str(_ROOT / ".chrome_cdp_profile")
_env_providers = os.environ.get("CONOL_CAPTCHA_PROVIDERS")
if _env_providers:
PROVIDERS = [p.strip() for p in _env_providers.split(",") if p.strip()]
else:
PROVIDERS = list(_C.get("providers") or ["chrome_cdp", "anticaptcha"])
ANTICAPTCHA_KEYS = list(_C.get("anticaptcha_keys") or [])
# ---------------------------------------------------------------- chrome_cdp
_pw = None # playwright instance
_browser = None # CDP browser handle
_page = None # tab parked on conol.ai
LAST_PROVIDER = None # which solver minted the last successful token
def _log(msg: str) -> None:
print("[captcha] %s" % msg, flush=True)
def _cdp_alive() -> bool:
try:
with urllib.request.urlopen("http://127.0.0.1:%d/json/version" % CDP_PORT, timeout=3) as r:
return r.status == 200
except Exception:
return False
def _launch_chrome() -> bool:
"""Launch Chrome with remote debugging on CDP_PORT. Reuses a warm profile dir."""
if _cdp_alive():
return True
Path(PROFILE_DIR).mkdir(parents=True, exist_ok=True)
cmd = [
CHROME_PATH,
"--remote-debugging-port=%d" % CDP_PORT,
"--user-data-dir=%s" % PROFILE_DIR,
"--no-first-run",
"--no-default-browser-check",
"--disable-blink-features=AutomationControlled",
"about:blank",
]
_cproxy = os.environ.get("CONOL_CHROME_PROXY")
if _cproxy:
cmd.insert(-1, "--proxy-server=%s" % _cproxy)
try:
subprocess.Popen(cmd, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL,
creationflags=getattr(subprocess, "DETACHED_PROCESS", 0) |
getattr(subprocess, "CREATE_NEW_PROCESS_GROUP", 0))
except FileNotFoundError:
_log("chrome not found at %s" % CHROME_PATH)
return False
for _ in range(30):
time.sleep(1)
if _cdp_alive():
_log("chrome CDP up on :%d" % CDP_PORT)
return True
return False
def _connect():
"""Connect playwright over CDP and park a page on BASE_URL. Lazy + reused."""
global _pw, _browser, _page
if _page is not None:
try:
_ = _page.url # cheap liveness probe
return _page
except Exception:
_page = None
if not _launch_chrome():
return None
try:
from playwright.sync_api import sync_playwright
except ImportError:
_log("playwright not installed — chrome_cdp provider unavailable")
return None
try:
if _pw is None:
_pw = sync_playwright().start()
if _browser is None:
# timeout is mandatory here: connect_over_cdp without it can hang
# forever when Chrome's WS endpoint accepts but never completes the
# handshake (observed 12-min wedge in an unattended run).
_browser = _pw.chromium.connect_over_cdp(
"http://127.0.0.1:%d" % CDP_PORT, timeout=30000)
ctx = _browser.contexts[0] if _browser.contexts else _browser.new_context()
page = ctx.pages[0] if ctx.pages else ctx.new_page()
page.set_default_timeout(30000)
_log("tab url: %s" % (page.url or "?")[:80])
if "conol.ai" not in (page.url or ""):
page.goto(BASE_URL, wait_until="domcontentloaded", timeout=45000)
# make sure grecaptcha is present (conol loads it lazily on auth pages)
page.evaluate("""(siteKey) => new Promise((resolve, reject) => {
if (window.grecaptcha) return resolve('already');
const old = document.getElementById('eni-recaptcha');
if (old) old.remove();
const s = document.createElement('script');
s.id = 'eni-recaptcha';
s.src = 'https://www.recaptcha.net/recaptcha/api.js?render=' + encodeURIComponent(siteKey);
s.onload = () => resolve('loaded');
s.onerror = () => reject(new Error('recaptcha load failed'));
document.head.appendChild(s);
})""", SITE_KEY)
page.wait_for_function("typeof window.grecaptcha !== 'undefined'", timeout=20000)
_log("grecaptcha ready on %s" % (page.url or "?")[:60])
_page = page
return page
except Exception as e:
_log("connect failed: %s" % str(e)[:160])
_page = None
return None
def solve_chrome_cdp(action: str) -> Optional[str]:
"""Mint one reCAPTCHA v3 token in the live Chrome tab. Returns token or None."""
page = _connect()
if page is None:
return None
try:
token = page.evaluate("""([siteKey, action]) => new Promise((resolve, reject) => {
window.grecaptcha.ready(() => {
window.grecaptcha.execute(siteKey, {action: action}).then(resolve).catch(reject);
});
})""", [SITE_KEY, action])
if isinstance(token, str) and len(token) > 50:
return token
_log("chrome_cdp returned a short/odd token (%r)" % str(token)[:40])
except Exception as e:
_log("chrome_cdp solve error: %s" % str(e)[:160])
return None
def warmup() -> None:
"""Pre-launch Chrome + load conol.ai so the first solve is fast."""
_connect()
# -------------------------------------------------------------- anticaptcha
def solve_anticaptcha(action: str) -> Optional[str]:
"""Existing paid path: RecaptchaV3TaskProxyless via api.anti-captcha.com."""
import requests
for key in ANTICAPTCHA_KEYS:
try:
resp = requests.post("https://api.anti-captcha.com/createTask", json={
"clientKey": key,
"task": {
"type": "RecaptchaV3TaskProxyless",
"websiteURL": BASE_URL,
"websiteKey": SITE_KEY,
"pageAction": action,
"minScore": 0.3,
}
}, timeout=30)
data = resp.json()
if data.get("errorId") != 0:
_log("createTask fail (%s...): %s" % (key[:8], data.get("errorDescription", "?")))
continue
task_id = data["taskId"]
deadline = time.time() + 90
while time.time() < deadline:
r = requests.post("https://api.anti-captcha.com/getTaskResult", json={
"clientKey": key, "taskId": task_id
}, timeout=15).json()
if r.get("status") == "ready":
token = r["solution"].get("gRecaptchaResponse", "")
if token:
return token
elif r.get("errorId", 0) != 0:
_log("getTaskResult error: %s" % r.get("errorDescription", ""))
break
time.sleep(3)
except Exception as e:
_log("anticaptcha exception (%s...): %s" % (key[:8], str(e)[:120]))
return None
# ------------------------------------------------------------------- chain
_SOLVERS = {"chrome_cdp": solve_chrome_cdp, "anticaptcha": solve_anticaptcha}
def solve(action: str, skip: "set|None" = None) -> Optional[str]:
"""Try each configured provider in order; first non-empty token wins.
`skip` names providers to bypass this round — used to ESCALATE when the
server rejects a token (CAPTCHA_VERIFICATION_FAILED): a freshly-created
Chrome profile mints a low reCAPTCHA-v3 score, so we skip chrome_cdp and
fall through to the paid AntiCaptcha path instead of burning attempts on
tokens the server keeps rejecting. The provider that produced the last
token is exposed via LAST_PROVIDER so the caller knows whom to skip.
"""
skip = skip or set()
for name in PROVIDERS:
if name in skip:
continue
fn = _SOLVERS.get(name)
if fn is None:
_log("unknown captcha provider %r — skipped" % name)
continue
tok = fn(action)
if tok:
global LAST_PROVIDER
LAST_PROVIDER = name
return tok
_log("provider %s returned nothing for action=%s" % (name, action))
# every non-skipped provider failed — retry ignoring the skip list once
if skip:
_log("all non-skipped providers exhausted; retrying full chain")
return solve(action, skip=None)
return None
if __name__ == "__main__":
# self-test: python conol_captcha.py [action]
act = sys.argv[1] if len(sys.argv) > 1 else "sign_up"
t0 = time.time()
tok = solve(act)
if tok:
print("OK action=%s len=%d time=%.1fs providers=%s token=%s...%s"
% (act, len(tok), time.time() - t0, PROVIDERS, tok[:24], tok[-12:]))
else:
print("FAIL action=%s time=%.1fs providers=%s" % (act, time.time() - t0, PROVIDERS))