Files
universal-autoreg/lib/captcha.py

105 lines
4.7 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

"""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())