outlook2api / register /captcha.py
ohmyapi's picture
feat: init outlook2api β€” mail API + batch registration with cloud captcha
c74db65
"""FunCaptcha cloud solver service (YesCaptcha / CapSolver compatible).
Environment variables:
CAPTCHA_CLIENT_KEY β€” Cloud solver API key (required)
CAPTCHA_CLOUD_URL β€” Cloud solver base URL (default: https://api.yescaptcha.com)
"""
from __future__ import annotations
import os
import time
from typing import Optional
import requests
DEFAULT_CLOUD_URL = "https://api.yescaptcha.com"
class FunCaptchaService:
"""Cloud-based FunCaptcha (Arkose Labs) solver."""
def __init__(
self,
client_key: str = "",
cloud_url: str = "",
):
self.client_key = client_key or os.environ.get("CAPTCHA_CLIENT_KEY", "")
self.cloud_url = (
cloud_url or os.environ.get("CAPTCHA_CLOUD_URL", "") or DEFAULT_CLOUD_URL
).rstrip("/")
def solve(
self,
website_url: str,
public_key: str,
subdomain: Optional[str] = None,
blob_data: Optional[str] = None,
) -> Optional[str]:
"""Submit FunCaptcha task and poll for token.
Args:
website_url: The page URL where FunCaptcha is loaded.
public_key: Arkose Labs public key (pk= parameter from iframe src).
subdomain: Optional custom subdomain (e.g. "client-api.arkoselabs.com").
blob_data: Optional blob data from the challenge.
Returns:
Solved token string, or None on failure.
"""
if not self.client_key:
print("[Captcha] CAPTCHA_CLIENT_KEY not set")
return None
task_id = self._create_task(website_url, public_key, subdomain, blob_data)
if not task_id:
return None
return self._poll_result(task_id)
def _create_task(
self,
website_url: str,
public_key: str,
subdomain: Optional[str],
blob_data: Optional[str],
) -> Optional[str]:
task: dict = {
"type": "FunCaptchaTaskProxyless",
"websiteURL": website_url,
"websitePublicKey": public_key,
}
if subdomain:
task["funcaptchaApiJSSubdomain"] = subdomain
if blob_data:
task["data"] = blob_data
try:
r = requests.post(
f"{self.cloud_url}/createTask",
json={"clientKey": self.client_key, "task": task},
timeout=15,
)
data = r.json()
if data.get("errorId") != 0:
print(f"[Captcha] Create error: {data.get('errorDescription')}")
return None
return data.get("taskId")
except Exception as exc:
print(f"[Captcha] Create failed: {exc}")
return None
def _poll_result(self, task_id: str, max_retries: int = 60) -> Optional[str]:
time.sleep(5)
for _ in range(max_retries):
try:
r = requests.post(
f"{self.cloud_url}/getTaskResult",
json={"clientKey": self.client_key, "taskId": task_id},
timeout=15,
)
data = r.json()
if data.get("errorId") != 0:
print(f"[Captcha] Poll error: {data.get('errorDescription')}")
return None
if data.get("status") == "ready":
return (data.get("solution") or {}).get("token")
if data.get("status") != "processing":
return None
except Exception:
pass
time.sleep(3)
print("[Captcha] Solver timeout")
return None