refactor(douyu): runner 按功能域拆分 Mixin + 精英手册兑换对齐真机流程 (HAR 审核通过)
拆分: douyu_runner.py (-3186行) → core/bind/manual/gold/donate/goods/xpd 七个 Mixin + 入口聚合类, douyu_batch_registry 供 routers 重导出 精英手册兑换对齐 HAR 抓包 (activity_client +379 行): - csrf 复用 Cookie 值不再每次 generateCsrf (HAR 实测全程零 csrf 请求), 仅服务端报 csrf 错误时 force_refresh 重试一次 - pay Referer 补 roomId 对齐浏览器 - 新增手册链路接口: elite_user_info/storedetail/pre_exchange check+confirm/ subscribe/batch_limit/赠品兑换/自动转换 - resolve_exchange_plan: 浏览器兑换按钮状态机路由 (normal/subscribe/ pre_exchange/wait/blocked) - 兑换执行: 人类节奏抖动 → 锁单(火爆重试1次) → 支付分级退避 (频控 30/60/90s 4次上限, 普错 2/5/10/20s 5次上限, 300s 锁单期 15s 余量, 可中断睡眠) 测试: 新增 17 项 (csrf 复用语义/状态机路由/Referer), 全量 94 通过; 未夹带代理改动
This commit is contained in:
@@ -0,0 +1,485 @@
|
||||
"""斗鱼任务执行器:商城兑换(由 douyu_runner.py 按功能域拆分)。"""
|
||||
|
||||
from __future__ import annotations
|
||||
import random
|
||||
import time
|
||||
from datetime import datetime, timezone
|
||||
from sqlalchemy.orm import Session
|
||||
|
||||
from core.douyu import DouyuActivityClient, DouyuActivityError
|
||||
from ..models import Account, DouyuEsportsGoodsSnapshot, DouyuGoodsSnapshot, DouyuTask
|
||||
|
||||
# 兑换节奏与重试(对齐 8.30 浏览器抓包:锁单->支付间隔约 2.2~4.2s;火爆类错误要长退避而不是秒级连打)
|
||||
DOUYU_EXCHANGE_PRE_CREATE_JITTER = (0.3, 1.2)
|
||||
DOUYU_EXCHANGE_LOCK_PAY_DELAY_RANGE = (2.0, 4.0)
|
||||
DOUYU_EXCHANGE_LOCK_TTL_SECONDS = 300
|
||||
DOUYU_EXCHANGE_RATE_LIMIT_HINTS = ("火爆", "频繁", "稍后", "太热", "限流", "繁忙", "人多", "排队", "手慢")
|
||||
DOUYU_EXCHANGE_RATE_LIMIT_BACKOFFS = (30, 60, 90)
|
||||
DOUYU_EXCHANGE_GENERIC_BACKOFFS = (2, 5, 10, 20)
|
||||
|
||||
class GoodsMixin:
|
||||
"""商城兑换域:商品刷新、锁单/支付/兑换、兑换节奏与退避重试。"""
|
||||
def _execute_refresh_goods(self, db: Session, task: DouyuTask, account: Account, cookie: str, config: dict):
|
||||
client = self._client(cookie)
|
||||
result = client.list_goods(manual_id=config["manual_id"], rid=config["rid"])
|
||||
goods = result["goods"]
|
||||
self._upsert_goods(db, goods)
|
||||
account.bind_status = account.bind_status or "active"
|
||||
account.updated_at = datetime.now(timezone.utc)
|
||||
self._mark_task(db, task, "success", f"已刷新商品 {len(goods)} 个", {"goods_count": len(goods), "goods": goods})
|
||||
|
||||
def _execute_refresh_esports_goods(
|
||||
self,
|
||||
db: Session,
|
||||
task: DouyuTask,
|
||||
account: Account,
|
||||
cookie: str,
|
||||
config: dict,
|
||||
):
|
||||
"""刷新电竞手册皮肤商城快照。"""
|
||||
client = self._client(cookie)
|
||||
result = client.list_esports_goods(
|
||||
manual_id=str(config["esports_manual_id"]),
|
||||
rid=str(config["room_id"]),
|
||||
)
|
||||
goods = result["goods"]
|
||||
self._upsert_esports_goods(db, goods)
|
||||
account.esports_bind_status = "esports_goods_refreshed"
|
||||
account.updated_at = datetime.now(timezone.utc)
|
||||
self._mark_task(
|
||||
db,
|
||||
task,
|
||||
"success",
|
||||
f"已刷新电竞皮肤 {len(goods)} 个",
|
||||
{"goods_count": len(goods), "esports_store_score": result["score"], "goods": goods},
|
||||
)
|
||||
|
||||
def _execute_lock_goods(self, db: Session, task: DouyuTask, account: Account, cookie: str, config: dict):
|
||||
payload = self._task_payload(task)
|
||||
commodity_id = str(payload.get("commodity_id") or payload.get("commodityId") or "").strip()
|
||||
if not commodity_id:
|
||||
self._mark_task(db, task, "failed", "请选择锁定商品")
|
||||
return
|
||||
try:
|
||||
num = int(payload.get("num") or 1)
|
||||
except (TypeError, ValueError):
|
||||
num = 1
|
||||
client = self._client(cookie)
|
||||
result = client.create_exchange_order(
|
||||
manual_id=str(config["manual_id"]),
|
||||
rid=str(config["rid"]),
|
||||
commodity_id=commodity_id,
|
||||
num=max(1, num),
|
||||
)
|
||||
goods = (
|
||||
db.query(DouyuGoodsSnapshot)
|
||||
.filter(DouyuGoodsSnapshot.commodity_id == commodity_id)
|
||||
.first()
|
||||
)
|
||||
account.bind_status = "goods_locked"
|
||||
account.updated_at = datetime.now(timezone.utc)
|
||||
expire_seconds = result.get("expire_seconds")
|
||||
expire_text = f",{expire_seconds} 秒内有效" if expire_seconds else ""
|
||||
self._mark_task(
|
||||
db,
|
||||
task,
|
||||
"success",
|
||||
f"锁单成功: {(goods.name if goods else '') or commodity_id}(订单 {result['order_id']}{expire_text})",
|
||||
{
|
||||
"goods": goods.raw if goods else None,
|
||||
"game_name": account.game_name or "",
|
||||
"game_channel": account.game_channel or "",
|
||||
"account_name": account.nickname or account.username or account.uid or f"#{account.id}",
|
||||
**result,
|
||||
},
|
||||
)
|
||||
|
||||
def _execute_pay_locked_order(
|
||||
self,
|
||||
db: Session,
|
||||
task: DouyuTask,
|
||||
account: Account,
|
||||
cookie: str,
|
||||
config: dict,
|
||||
):
|
||||
payload = self._task_payload(task)
|
||||
order_id = str(payload.get("order_id") or payload.get("orderId") or "").strip()
|
||||
commodity_id = str(payload.get("commodity_id") or payload.get("commodityId") or "").strip()
|
||||
if not order_id:
|
||||
self._mark_task(db, task, "failed", "锁单订单号不能为空")
|
||||
return
|
||||
client = self._client(cookie)
|
||||
payment = client.pay_exchange_order(
|
||||
manual_id=str(config["manual_id"]),
|
||||
order_id=order_id,
|
||||
rid=str(config.get("rid") or ""),
|
||||
)
|
||||
goods = None
|
||||
if commodity_id:
|
||||
goods = (
|
||||
db.query(DouyuGoodsSnapshot)
|
||||
.filter(DouyuGoodsSnapshot.commodity_id == commodity_id)
|
||||
.first()
|
||||
)
|
||||
account.bind_status = "goods_exchanged"
|
||||
account.updated_at = datetime.now(timezone.utc)
|
||||
result = {
|
||||
"commodity_id": commodity_id,
|
||||
"order_id": order_id,
|
||||
"exchange_id": payment["exchange_id"],
|
||||
"commodity_image": payment["commodity_image"],
|
||||
"exchange_num": payment["exchange_num"],
|
||||
"payment": payment,
|
||||
"goods": goods.raw if goods else None,
|
||||
"game_name": account.game_name or "",
|
||||
"game_channel": account.game_channel or "",
|
||||
"account_name": account.nickname or account.username or account.uid or f"#{account.id}",
|
||||
}
|
||||
try:
|
||||
ctn = client.acf_ccn(refresh_subscribe=False)
|
||||
result.update(self._refresh_account_points(client, account, cookie, ctn=ctn))
|
||||
except Exception as exc:
|
||||
self._push_log("warning", f"支付锁单后刷新积分失败: {exc}")
|
||||
self._mark_task(
|
||||
db,
|
||||
task,
|
||||
"success",
|
||||
f"锁单支付成功: {(goods.name if goods else '') or commodity_id or order_id}",
|
||||
result,
|
||||
)
|
||||
|
||||
@staticmethod
|
||||
def _is_rate_limited_error(message: str) -> bool:
|
||||
"""识别频控/火爆类错误文案,命中时按长退避重试。"""
|
||||
return any(hint in message for hint in DOUYU_EXCHANGE_RATE_LIMIT_HINTS)
|
||||
|
||||
def _pay_exchange_with_backoff(
|
||||
self,
|
||||
client: DouyuActivityClient,
|
||||
*,
|
||||
manual_id: str,
|
||||
order_id: str,
|
||||
rid: str,
|
||||
locked_at: float,
|
||||
) -> dict | None:
|
||||
"""支付锁单,按错误类型退避重试(不再 0.3s 无脑连打)。
|
||||
|
||||
频控/火爆类错误:30/60/90 秒退避,最多 4 次尝试且不超出锁单有效期;
|
||||
其他错误:2/5/10/20 秒退避,最多 5 次尝试。
|
||||
"""
|
||||
for attempt in range(6):
|
||||
if self._stop.is_set():
|
||||
return None
|
||||
try:
|
||||
return client.pay_exchange_order(
|
||||
manual_id=manual_id,
|
||||
order_id=order_id,
|
||||
rid=rid,
|
||||
)
|
||||
except DouyuActivityError as exc:
|
||||
last_error = str(exc)
|
||||
if self._is_rate_limited_error(last_error):
|
||||
if attempt >= len(DOUYU_EXCHANGE_RATE_LIMIT_BACKOFFS):
|
||||
raise DouyuActivityError(
|
||||
f"锁单 {order_id} 支付被频控拦截(已退避重试{attempt}次): {last_error}"
|
||||
) from exc
|
||||
backoff = DOUYU_EXCHANGE_RATE_LIMIT_BACKOFFS[attempt]
|
||||
else:
|
||||
if attempt >= len(DOUYU_EXCHANGE_GENERIC_BACKOFFS):
|
||||
raise DouyuActivityError(
|
||||
f"锁单 {order_id} 支付失败(已重试{attempt}次): {last_error}"
|
||||
) from exc
|
||||
backoff = DOUYU_EXCHANGE_GENERIC_BACKOFFS[attempt]
|
||||
# 超出锁单有效期(服务端 300s)前留 15s 余量,放弃继续重试
|
||||
remaining = locked_at + DOUYU_EXCHANGE_LOCK_TTL_SECONDS - time.time()
|
||||
if remaining - backoff < 15:
|
||||
raise DouyuActivityError(
|
||||
f"锁单 {order_id} 支付失败且临近过期({int(remaining)}s): {last_error}"
|
||||
) from exc
|
||||
kind = "频控" if self._is_rate_limited_error(last_error) else "错误"
|
||||
self._push_log(
|
||||
"info",
|
||||
f" 锁单 {order_id} 支付{kind}退避 {backoff:.0f}s 后重试 {attempt + 1}/"
|
||||
f"{len(DOUYU_EXCHANGE_RATE_LIMIT_BACKOFFS) if self._is_rate_limited_error(last_error) else len(DOUYU_EXCHANGE_GENERIC_BACKOFFS) + 1}: {last_error}",
|
||||
)
|
||||
if not self._sleep_interruptible(backoff):
|
||||
return None
|
||||
return None
|
||||
|
||||
def _execute_exchange_goods(self, db: Session, task: DouyuTask, account: Account, cookie: str, config: dict):
|
||||
payload = self._task_payload(task)
|
||||
commodity_id = str(payload.get("commodity_id") or payload.get("commodityId") or "").strip()
|
||||
if not commodity_id:
|
||||
self._mark_task(db, task, "failed", "请选择兑换商品")
|
||||
return
|
||||
client = self._client(cookie)
|
||||
manual_id = str(config["manual_id"])
|
||||
rid = str(config.get("rid") or "")
|
||||
result: dict = {"commodity_id": commodity_id}
|
||||
|
||||
# ---- 对齐浏览器:先查手册状态 + 商品详情,按页面状态机选兑换链路 ----
|
||||
manual_type = None
|
||||
try:
|
||||
user_info = client.elite_user_info(manual_id=manual_id, rid=rid)
|
||||
manual_type = user_info.get("manual_type")
|
||||
except DouyuActivityError as exc:
|
||||
self._push_log("warning", f"查询手册状态失败,按经典流程继续: {exc}")
|
||||
detail = None
|
||||
try:
|
||||
detail = client.query_goods_detail(manual_id=manual_id, commodity_id=commodity_id, rid=rid)["detail"]
|
||||
except DouyuActivityError as exc:
|
||||
self._push_log("warning", f"查询商品详情失败,按经典流程继续: {exc}")
|
||||
|
||||
batch_num_limit = None
|
||||
if detail and int(detail.get("batchExchange") or 0) > 0:
|
||||
try:
|
||||
batch_num_limit = client.batch_exchange_limit(
|
||||
manual_id=manual_id, commodity_id=commodity_id, rid=rid
|
||||
)["limit"]
|
||||
except DouyuActivityError as exc:
|
||||
self._push_log("warning", f"查询批量兑换上限失败: {exc}")
|
||||
|
||||
plan = DouyuActivityClient.resolve_exchange_plan(
|
||||
detail or {}, manual_type=manual_type, batch_num_limit=batch_num_limit
|
||||
)
|
||||
result["plan"] = plan
|
||||
if detail:
|
||||
result["detail"] = detail
|
||||
action = plan["action"]
|
||||
|
||||
def finish_failed(message: str) -> None:
|
||||
self._mark_task(
|
||||
db, task, "failed", message,
|
||||
{"commodity_id": commodity_id, "plan": plan, "detail": detail},
|
||||
)
|
||||
|
||||
if action in ("blocked", "wait"):
|
||||
finish_failed(f"兑换失败: {plan['text']}")
|
||||
return
|
||||
|
||||
goods = (
|
||||
db.query(DouyuGoodsSnapshot)
|
||||
.filter(DouyuGoodsSnapshot.commodity_id == commodity_id)
|
||||
.first()
|
||||
)
|
||||
name = (goods.name if goods else "") or commodity_id
|
||||
|
||||
if action == "subscribe":
|
||||
try:
|
||||
client.subscribe_commodity(manual_id=manual_id, commodity_id=commodity_id)
|
||||
except DouyuActivityError as exc:
|
||||
finish_failed(f"预约到货失败: {exc}")
|
||||
return
|
||||
account.bind_status = "goods_subscribed"
|
||||
account.updated_at = datetime.now(timezone.utc)
|
||||
self._mark_task(
|
||||
db, task, "success", f"已预约到货: {name} ({plan['text']})",
|
||||
{"commodity_id": commodity_id, "plan": plan, "detail": detail},
|
||||
)
|
||||
return
|
||||
|
||||
if action == "pre_exchange":
|
||||
try:
|
||||
check = client.pre_exchange_check(manual_id=manual_id, commodity_id=commodity_id)
|
||||
user_status = int((check.get("data") or {}).get("userStatus") or 0)
|
||||
if user_status == 1 and not plan["pre_exchange"]:
|
||||
self._mark_task(
|
||||
db, task, "success", f"已预兑: {name},等待开放后继续兑换",
|
||||
{"commodity_id": commodity_id, "plan": plan, "detail": detail, "check": check},
|
||||
)
|
||||
return
|
||||
confirm = client.confirm_pre_exchange(manual_id=manual_id, commodity_id=commodity_id, rid=rid)
|
||||
except DouyuActivityError as exc:
|
||||
finish_failed(f"预兑失败: {exc}")
|
||||
return
|
||||
account.bind_status = "goods_pre_exchanged"
|
||||
account.updated_at = datetime.now(timezone.utc)
|
||||
self._mark_task(
|
||||
db, task, "success", f"预兑成功: {name},开放后可继续兑换",
|
||||
{"commodity_id": commodity_id, "plan": plan, "detail": detail, "check": check, "confirm": confirm},
|
||||
)
|
||||
return
|
||||
|
||||
# ---- 经典链路:锁单 -> 人类节奏等待 -> 支付(错误分类退避重试)----
|
||||
try:
|
||||
num = max(1, int(payload.get("num") or 1))
|
||||
except (TypeError, ValueError):
|
||||
num = 1
|
||||
num = min(num, int(plan.get("max_num") or 1))
|
||||
|
||||
# 少量随机抖动(浏览器点击商品到锁单之间有人工间隔)
|
||||
self._push_log("info", f" 兑换 {name}: {plan['text']}")
|
||||
if not self._sleep_interruptible(random.uniform(*DOUYU_EXCHANGE_PRE_CREATE_JITTER)):
|
||||
self._mark_task(db, task, "stopped", "任务已停止", result)
|
||||
return
|
||||
try:
|
||||
locked = client.create_exchange_order(
|
||||
manual_id=manual_id,
|
||||
rid=rid,
|
||||
commodity_id=commodity_id,
|
||||
num=num,
|
||||
)
|
||||
except DouyuActivityError as exc:
|
||||
# 锁单本身被频控时做一次长退避重试,避免无脑连打
|
||||
backoff = DOUYU_EXCHANGE_RATE_LIMIT_BACKOFFS[0] if self._is_rate_limited_error(str(exc)) else 3
|
||||
self._push_log("info", f" 锁定兑换商品失败({exc}),{backoff:.0f}s 后重试一次")
|
||||
if not self._sleep_interruptible(backoff):
|
||||
self._mark_task(db, task, "stopped", "任务已停止", result)
|
||||
return
|
||||
try:
|
||||
locked = client.create_exchange_order(
|
||||
manual_id=manual_id,
|
||||
rid=rid,
|
||||
commodity_id=commodity_id,
|
||||
num=num,
|
||||
)
|
||||
except DouyuActivityError as exc2:
|
||||
finish_failed(f"锁定兑换商品失败: {exc2}")
|
||||
return
|
||||
result.update({**locked, "lock_order": locked, "goods": goods.raw if goods else None})
|
||||
|
||||
# 锁单成功后按浏览器节奏(抓包实测 2.24s~4.2s)等待再支付
|
||||
delay = random.uniform(*DOUYU_EXCHANGE_LOCK_PAY_DELAY_RANGE)
|
||||
self._push_log("info", f" 锁单成功(订单 {locked['order_id']}),{delay:.1f}s 后支付")
|
||||
if not self._sleep_interruptible(delay):
|
||||
self._mark_task(db, task, "stopped", "任务已停止,商品锁单仍可能有效", locked)
|
||||
return
|
||||
try:
|
||||
payment = self._pay_exchange_with_backoff(
|
||||
client,
|
||||
manual_id=manual_id,
|
||||
order_id=locked["order_id"],
|
||||
rid=rid,
|
||||
locked_at=locked["locked_at"],
|
||||
)
|
||||
except DouyuActivityError as exc:
|
||||
finish_failed(str(exc))
|
||||
return
|
||||
if payment is None:
|
||||
self._mark_task(db, task, "stopped", "任务已停止,商品锁单仍可能有效", locked)
|
||||
return
|
||||
|
||||
result.update({
|
||||
"order_id": locked["order_id"],
|
||||
"exchange_id": payment["exchange_id"],
|
||||
"commodity_image": payment["commodity_image"] or locked["commodity_image"],
|
||||
"exchange_num": payment["exchange_num"],
|
||||
"payment": payment,
|
||||
})
|
||||
account.bind_status = "goods_exchanged"
|
||||
account.updated_at = datetime.now(timezone.utc)
|
||||
# 兑换成功后自动刷新积分,更新账号最新积分信息(失败不阻断兑换成功)
|
||||
points_refresh = None
|
||||
try:
|
||||
ctn = client.acf_ccn(refresh_subscribe=False)
|
||||
points_refresh = self._refresh_account_points(client, account, cookie, ctn=ctn)
|
||||
except Exception as exc:
|
||||
self._push_log("warning", f"兑换后刷新积分失败: {exc}")
|
||||
self._mark_task(
|
||||
db,
|
||||
task,
|
||||
"success",
|
||||
f"兑换成功: {name}",
|
||||
{
|
||||
"goods": goods.raw if goods else None,
|
||||
"game_name": account.game_name or "",
|
||||
"game_channel": account.game_channel or "",
|
||||
"account_name": account.nickname or account.username or account.uid or f"#{account.id}",
|
||||
**result,
|
||||
**(points_refresh or {}),
|
||||
},
|
||||
)
|
||||
|
||||
def _execute_exchange_esports_goods(
|
||||
self,
|
||||
db: Session,
|
||||
task: DouyuTask,
|
||||
account: Account,
|
||||
cookie: str,
|
||||
config: dict,
|
||||
):
|
||||
"""兑换电竞手册皮肤,并同步电竞积分。"""
|
||||
payload = self._task_payload(task)
|
||||
commodity_id = str(payload.get("commodity_id") or payload.get("commodityId") or "").strip()
|
||||
if not commodity_id:
|
||||
self._mark_task(db, task, "failed", "请选择电竞皮肤")
|
||||
return
|
||||
try:
|
||||
quantity = max(1, int(payload.get("quantity") or payload.get("num") or 1))
|
||||
except (TypeError, ValueError):
|
||||
self._mark_task(db, task, "failed", "兑换数量必须是正整数")
|
||||
return
|
||||
|
||||
manual_id = str(config.get("esports_manual_id") or "").strip()
|
||||
room_id = str(config.get("room_id") or "").strip()
|
||||
if not manual_id or not room_id:
|
||||
self._mark_task(db, task, "failed", "请先配置电竞手册 manualID 和房间 ID")
|
||||
return
|
||||
|
||||
client = self._client(cookie)
|
||||
baseline_points = account.esports_points
|
||||
try:
|
||||
baseline = self._refresh_esports_handbook(client, account, manual_id=manual_id)
|
||||
baseline_points = baseline["esports_points"]
|
||||
db.commit()
|
||||
except Exception as exc:
|
||||
self._push_log("warning", f"兑换电竞皮肤前刷新积分失败: {exc}")
|
||||
|
||||
result = client.exchange_esports_goods(
|
||||
manual_id=manual_id,
|
||||
rid=room_id,
|
||||
commodity_id=commodity_id,
|
||||
quantity=quantity,
|
||||
)
|
||||
goods = (
|
||||
db.query(DouyuEsportsGoodsSnapshot)
|
||||
.filter(DouyuEsportsGoodsSnapshot.commodity_id == commodity_id)
|
||||
.first()
|
||||
)
|
||||
result["goods"] = goods.raw if goods else None
|
||||
result["esports_points_baseline"] = baseline_points
|
||||
try:
|
||||
points_result = self._refresh_esports_handbook(client, account, manual_id=manual_id)
|
||||
result.update(points_result)
|
||||
result["esports_points_after_exchange"] = points_result["esports_points"]
|
||||
except Exception as exc:
|
||||
result["esports_points_refresh_error"] = str(exc)
|
||||
|
||||
account.esports_bind_status = "esports_goods_exchanged"
|
||||
account.updated_at = datetime.now(timezone.utc)
|
||||
name = (goods.name if goods else "") or commodity_id
|
||||
message = f"兑换电竞皮肤成功: {name}"
|
||||
if quantity > 1:
|
||||
message += f" x{quantity}"
|
||||
if account.esports_points is not None:
|
||||
message += f",电竞积分: {account.esports_points}"
|
||||
result["game_name"] = account.esports_game_name or ""
|
||||
result["game_channel"] = account.esports_game_channel or ""
|
||||
result["account_name"] = account.nickname or account.username or account.uid or f"#{account.id}"
|
||||
self._mark_task(db, task, "success", message, result)
|
||||
|
||||
def _execute_query_limited_goods(self, db: Session, task: DouyuTask, account: Account, cookie: str, config: dict):
|
||||
client = self._client(cookie)
|
||||
result = client.query_limited_goods(manual_id=str(config["manual_id"]), rid=str(config["rid"]))
|
||||
limited = result["limited_goods"]
|
||||
names = [str(item.get("commodityName") or "") for item in limited if item.get("commodityName")]
|
||||
message = "无限制商品" if not names else f"限兑 {len(names)} 个: {', '.join(names[:5])}"
|
||||
account.bind_status = "limited_goods_queried"
|
||||
account.updated_at = datetime.now(timezone.utc)
|
||||
self._mark_task(db, task, "success", message, {"limited_count": len(limited), "limited_goods": limited})
|
||||
|
||||
def _execute_query_exchange_records(self, db: Session, task: DouyuTask, account: Account, cookie: str, config: dict):
|
||||
client = self._client(cookie)
|
||||
result = client.exchange_records(manual_id=str(config["manual_id"]))
|
||||
records = result["records"]
|
||||
account.bind_status = "exchange_records_queried"
|
||||
account.updated_at = datetime.now(timezone.utc)
|
||||
self._mark_task(db, task, "success", f"兑换记录 {len(records)} 条" if records else "暂无兑换记录", result)
|
||||
|
||||
def _execute_prefetch_csrf_token(self, db: Session, task: DouyuTask, account: Account, cookie: str, config: dict):
|
||||
client = self._client(cookie)
|
||||
token = client.csrf_token()
|
||||
account.bind_status = "csrf_token_ready"
|
||||
account.updated_at = datetime.now(timezone.utc)
|
||||
self._mark_task(db, task, "success", "获取 csrf_token 成功", {"csrf_token": token, "cookie": client.cookie})
|
||||
|
||||
Reference in New Issue
Block a user