116 lines
4.0 KiB
Python
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}
|