Files
hello_js_reverse_skill/tcaptcha/rainyun_adapter.py
T

116 lines
4.0 KiB
Python

"""
雨云 (Rainyun) 业务接口适配器 (Rainyun Captcha Adapter)
将腾讯天御点选验证码纯协议客户端无缝注入雨云核心业务流程:
1. 任务签到与积分领取: POST /user/reward/tasks (带 vticket 与 vrandstr)
2. 账号密码登录: POST /user/login (带 vticket 与 vrandstr)
"""
import json
import logging
from typing import Dict, Optional, Tuple
import httpx
from tcaptcha.client import TencentCaptchaClient
logging.basicConfig(level=logging.INFO, format="%(asctime)s [%(levelname)s] %(message)s")
logger = logging.getLogger("rainyun_adapter")
class RainyunAdapter:
API_BASE = "https://api.v2.rainyun.com"
RAINYUN_AID = "2039519451"
def __init__(
self,
api_key: Optional[str] = None,
cookie: Optional[str] = None,
timeout: float = 15.0,
):
self.api_key = api_key
self.cookie = cookie
self.timeout = timeout
self.captcha_client = TencentCaptchaClient(aid=self.RAINYUN_AID)
headers = {
"User-Agent": (
"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) "
"AppleWebKit/537.36 (KHTML, like Gecko) Chrome/152.0.0.0 Safari/537.36"
),
"Referer": "https://app.rainyun.com/",
"Origin": "https://app.rainyun.com",
"Accept": "application/json, text/plain, */*",
}
if self.api_key:
headers["X-Api-Key"] = self.api_key
if self.cookie:
headers["Cookie"] = self.cookie
self.http_client = httpx.Client(
base_url=self.API_BASE,
trust_env=False,
headers=headers,
timeout=self.timeout,
)
def get_captcha_ticket(self) -> Tuple[Optional[str], Optional[str]]:
"""调用纯协议验证码求解管线,获取有效 ticket 与 randstr"""
logger.info("正在调用腾讯天御验证码求解客户端...")
res = self.captcha_client.solve()
if res["success"]:
logger.info(f"验证码求解成功 (耗时: {res['elapsed_ms']}ms, randstr: {res['randstr']})")
return res["ticket"], res["randstr"]
else:
logger.error(f"验证码求解失败: {res['error']}")
return None, None
def claim_reward_task(self, task_name: str = "加入用户群") -> Dict:
"""
带验证码提交雨云积分任务
接口: POST https://api.v2.rainyun.com/user/reward/tasks
"""
ticket, randstr = self.get_captcha_ticket()
if not ticket or not randstr:
return {"code": -1, "message": "验证码获取失败"}
payload = {
"task_name": task_name,
"verifyCode": "",
"vticket": ticket,
"vrandstr": randstr,
}
logger.info(f"正在提交雨云任务: {task_name}...")
resp = self.http_client.post("/user/reward/tasks", json=payload)
try:
res_json = resp.json()
logger.info(f"雨云任务接口返回: {res_json}")
return res_json
except Exception as e:
logger.error(f"雨云任务接口解析失败: {e}")
return {"code": resp.status_code, "text": resp.text}
def login(self, field: str, password: str) -> Dict:
"""
雨云用户登录 (自动注入验证码凭证)
接口: POST https://api.v2.rainyun.com/user/login
"""
ticket, randstr = self.get_captcha_ticket()
if not ticket or not randstr:
return {"code": -1, "message": "验证码获取失败"}
payload = {
"field": field,
"password": password,
"verifyCode": "",
"vticket": ticket,
"vrandstr": randstr,
}
logger.info(f"正在发起雨云登录: {field}...")
resp = self.http_client.post("/user/login", json=payload)
try:
return resp.json()
except Exception as e:
return {"code": resp.status_code, "text": resp.text}