universal-autoreg: initial public release (restored, redacted, hardened)
This commit is contained in:
104
lib/captcha.py
Normal file
104
lib/captcha.py
Normal file
@@ -0,0 +1,104 @@
|
||||
"""captcha.py — клиент captcha-инфраструктуры.
|
||||
|
||||
Два бэкенда:
|
||||
1. Локальный sidecar (waguriagentic/captcha-solver, http://127.0.0.1:8877) — БЕСПЛАТНО:
|
||||
turnstile / recaptcha v2-v3 / hcaptcha / arkose(funcaptcha) / aliyun / geetest и др. (11 типов).
|
||||
Запуск sidecar: см. docs/SETUP.md. Для новых версий нужен SOLVER_ALLOW_UNAUTHENTICATED=1.
|
||||
2. 2captcha fallback (платный) — когда sidecar не справляется (нативный Turnstile под
|
||||
siteverify-проверкой, DataDome, press&hold PerimeterX).
|
||||
|
||||
Все ключи — из config.json (НЕ хардкодить).
|
||||
"""
|
||||
import json, os, time, urllib.request, urllib.parse
|
||||
|
||||
CFG_FILE = os.environ.get("UA_CONFIG", os.path.join(os.path.dirname(__file__), "..", "config.json"))
|
||||
SIDECAR = os.environ.get("UA_SIDECAR", "http://127.0.0.1:8877")
|
||||
|
||||
|
||||
def _cfg():
|
||||
if os.path.exists(CFG_FILE):
|
||||
return json.load(open(CFG_FILE, encoding="utf-8"))
|
||||
return {}
|
||||
|
||||
|
||||
def _post_json(url, payload, timeout=300):
|
||||
req = urllib.request.Request(url, data=json.dumps(payload).encode(),
|
||||
headers={"Content-Type": "application/json"})
|
||||
with urllib.request.urlopen(req, timeout=timeout) as r:
|
||||
return json.loads(r.read().decode())
|
||||
|
||||
|
||||
def sidecar_health():
|
||||
try:
|
||||
with urllib.request.urlopen(SIDECAR + "/health", timeout=5) as r:
|
||||
return json.loads(r.read().decode())
|
||||
except Exception as e:
|
||||
return {"ok": False, "error": str(e)}
|
||||
|
||||
|
||||
def solve_sidecar(captcha_type: str, sitekey: str, pageurl: str, **kw):
|
||||
"""Решить капчу локальным sidecar'ом (бесплатно).
|
||||
|
||||
captcha_type: turnstile | recaptcha_v2 | recaptcha_v3 | hcaptcha | funcaptcha | ...
|
||||
Для recaptcha v3 обязательны: page_action (action сайта, напр. 'signup').
|
||||
Для turnstile рекомендуется real_page режим у sidecar'а (REAL_PAGE=1).
|
||||
"""
|
||||
payload = {"type": captcha_type, "sitekey": sitekey, "pageurl": pageurl}
|
||||
payload.update(kw)
|
||||
return _post_json(SIDECAR + "/solve", payload)
|
||||
|
||||
|
||||
def solve_2captcha(captcha_type: str, sitekey: str, pageurl: str, **kw):
|
||||
"""2captcha fallback. ВАЖНО: submit СТРОГО на in.php (res.php = ERROR_EMPTY_METHOD),
|
||||
баланс СТРОГО POST api.2captcha.com/res.php (GET = ложный ERROR_WRONG_USER_KEY)."""
|
||||
key = _cfg().get("twocaptcha_key")
|
||||
if not key:
|
||||
raise RuntimeError("twocaptcha_key not set in config.json")
|
||||
method = {"turnstile": "turnstile", "recaptcha_v2": "userrecaptcha",
|
||||
"recaptcha_v3": "recaptcha", "hcaptcha": "hcaptcha",
|
||||
"funcaptcha": "funcaptcha"}.get(captcha_type, captcha_type)
|
||||
params = {"key": key, "method": method, "googlekey" if method == "userrecaptcha" else "sitekey": sitekey,
|
||||
"pageurl": pageurl, "json": 1}
|
||||
params.update(kw)
|
||||
data = urllib.parse.urlencode(params).encode()
|
||||
with urllib.request.urlopen("https://api.2captcha.com/in.php", data=data, timeout=30) as r:
|
||||
resp = json.loads(r.read().decode())
|
||||
if resp.get("status") != 1:
|
||||
raise RuntimeError(f"2captcha submit failed: {resp}")
|
||||
cid = resp["request"]
|
||||
for _ in range(60):
|
||||
time.sleep(5)
|
||||
q = urllib.parse.urlencode({"key": key, "action": "get", "id": cid, "json": 1}).encode()
|
||||
with urllib.request.urlopen("https://api.2captcha.com/res.php", data=q, timeout=30) as r:
|
||||
resp = json.loads(r.read().decode())
|
||||
if resp.get("status") == 1:
|
||||
return {"token": resp["request"]}
|
||||
if resp.get("request") != "CAPCHA_NOT_READY":
|
||||
raise RuntimeError(f"2captcha: {resp}")
|
||||
raise TimeoutError("2captcha timeout 300s")
|
||||
|
||||
|
||||
def solve(captcha_type: str, sitekey: str, pageurl: str, prefer="sidecar", **kw):
|
||||
"""Умный решатель: sidecar → при ошибке 2captcha (если включён fallback)."""
|
||||
if prefer == "sidecar":
|
||||
try:
|
||||
return solve_sidecar(captcha_type, sitekey, pageurl, **kw)
|
||||
except Exception:
|
||||
if _cfg().get("twocaptcha_fallback", True):
|
||||
return solve_2captcha(captcha_type, sitekey, pageurl, **kw)
|
||||
raise
|
||||
return solve_2captcha(captcha_type, sitekey, pageurl, **kw)
|
||||
|
||||
|
||||
def balance_2captcha():
|
||||
key = _cfg().get("twocaptcha_key")
|
||||
if not key:
|
||||
return None
|
||||
data = urllib.parse.urlencode({"key": key, "action": "getbalance"}).encode()
|
||||
with urllib.request.urlopen("https://api.2captcha.com/res.php", data=data, timeout=15) as r:
|
||||
return json.loads(r.read().decode())
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
print("sidecar health:", sidecar_health())
|
||||
print("2captcha balance:", balance_2captcha())
|
||||
Reference in New Issue
Block a user