287 lines
10 KiB
Python
287 lines
10 KiB
Python
"""斗鱼任务执行器:送礼(由 douyu_runner.py 按功能域拆分)。"""
|
||
|
||
from __future__ import annotations
|
||
|
||
from datetime import UTC, datetime
|
||
from typing import TYPE_CHECKING, Any
|
||
|
||
from sqlalchemy.orm import Session
|
||
|
||
from core.douyu import DouyuActivityClient
|
||
|
||
from ..models import Account, DouyuTask
|
||
|
||
DOUYU_GIFT_POINTS_REFRESH_TIMES = 3
|
||
DOUYU_GIFT_POINTS_REFRESH_INTERVAL = 2
|
||
|
||
|
||
class DonateMixin:
|
||
"""送礼域:精英令/电竞任务礼物赠送与积分确认。"""
|
||
|
||
if TYPE_CHECKING:
|
||
_stop: Any
|
||
|
||
def _client(self, cookie: str) -> DouyuActivityClient: ...
|
||
def _task_payload(self, task: DouyuTask) -> dict: ...
|
||
def _push_log(self, level: str, message: str) -> None: ...
|
||
def _mark_task(self, *args: Any, **kwargs: Any) -> None: ...
|
||
def _update_task_progress(self, *args: Any, **kwargs: Any) -> None: ...
|
||
def _refresh_account_points(
|
||
self,
|
||
client: DouyuActivityClient,
|
||
account: Account,
|
||
cookie: str,
|
||
*,
|
||
ctn: str | None = None,
|
||
) -> dict: ...
|
||
def _refresh_account_gold_balance(
|
||
self, client: DouyuActivityClient, account: Account
|
||
) -> dict: ...
|
||
def _refresh_esports_handbook(
|
||
self, client: DouyuActivityClient, account: Account, *, manual_id: str
|
||
) -> dict: ...
|
||
|
||
def _refresh_points_after_elite_gift(
|
||
self,
|
||
db: Session,
|
||
task: DouyuTask,
|
||
account: Account,
|
||
client: DouyuActivityClient,
|
||
cookie: str,
|
||
ctn: str | None,
|
||
result: dict,
|
||
baseline_points: int | None,
|
||
gift_count: int,
|
||
) -> dict:
|
||
"""赠送精英令后短轮询积分;1 个精英令约等于 10 积分。"""
|
||
expected_gain = max(0, gift_count) * 10
|
||
target_points = (
|
||
baseline_points + expected_gain if baseline_points is not None else None
|
||
)
|
||
result["gift_points_baseline"] = baseline_points
|
||
result["gift_points_expected_gain"] = expected_gain
|
||
result["gift_points_target"] = target_points
|
||
|
||
last_points = None
|
||
refresh_result: dict = {}
|
||
for index in range(1, DOUYU_GIFT_POINTS_REFRESH_TIMES + 1):
|
||
refresh_result = self._refresh_account_points(
|
||
client, account, cookie, ctn=ctn
|
||
)
|
||
db.commit()
|
||
last_points = refresh_result["points"]
|
||
result.update(refresh_result)
|
||
result["gift_points_refresh_count"] = index
|
||
if target_points is None or (
|
||
last_points is not None and last_points >= target_points
|
||
):
|
||
result["gift_points_confirmed"] = (
|
||
target_points is None or last_points is not None
|
||
)
|
||
return refresh_result
|
||
if index < DOUYU_GIFT_POINTS_REFRESH_TIMES:
|
||
self._update_task_progress(
|
||
db,
|
||
task,
|
||
"running",
|
||
f"赠送精英令成功,等待积分同步(当前 {last_points if last_points is not None else '-'},预期 {target_points})",
|
||
result,
|
||
)
|
||
if self._stop.wait(DOUYU_GIFT_POINTS_REFRESH_INTERVAL):
|
||
break
|
||
|
||
result["gift_points_confirmed"] = False
|
||
result["points"] = last_points
|
||
return refresh_result
|
||
|
||
def _execute_donate_esports_gift(
|
||
self,
|
||
db: Session,
|
||
task: DouyuTask,
|
||
account: Account,
|
||
cookie: str,
|
||
config: dict,
|
||
*,
|
||
gift_name: str,
|
||
config_gift_id_key: str,
|
||
config_skin_id_key: str,
|
||
):
|
||
"""赠送电竞手册任务礼物并刷新独立积分。"""
|
||
payload = self._task_payload(task)
|
||
try:
|
||
gift_count = max(
|
||
1, int(payload.get("gift_count") or payload.get("count") or 1)
|
||
)
|
||
except (TypeError, ValueError):
|
||
self._mark_task(db, task, "failed", "赠送数量必须是正整数")
|
||
return
|
||
|
||
manual_id = str(config.get("esports_manual_id") or "").strip()
|
||
gift_id = str(
|
||
payload.get("gift_id") or config.get(config_gift_id_key) or ""
|
||
).strip()
|
||
skin_id = str(
|
||
payload.get("skin_id") or config.get(config_skin_id_key) or ""
|
||
).strip()
|
||
room_id = str(payload.get("room_id") or config.get("room_id") or "").strip()
|
||
if not manual_id or not gift_id or not skin_id or not room_id:
|
||
self._mark_task(db, task, "failed", "请先完整配置电竞手册、房间和礼物参数")
|
||
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"赠送{gift_name}前刷新电竞积分失败: {exc}")
|
||
|
||
result = client.donate_esports_gift(
|
||
gift_name=gift_name,
|
||
gift_count=gift_count,
|
||
room_id=room_id,
|
||
gift_id=gift_id,
|
||
skin_id=skin_id,
|
||
)
|
||
result.update(
|
||
{
|
||
"gift_name": gift_name,
|
||
"gift_id": gift_id,
|
||
"skin_id": skin_id,
|
||
"gift_count": gift_count,
|
||
"esports_points_baseline": baseline_points,
|
||
}
|
||
)
|
||
refresh_errors = []
|
||
try:
|
||
result.update(self._refresh_account_gold_balance(client, account))
|
||
except Exception as exc:
|
||
refresh_errors.append(f"鱼翅余额: {exc}")
|
||
try:
|
||
points_result = self._refresh_esports_handbook(
|
||
client, account, manual_id=manual_id
|
||
)
|
||
result.update(points_result)
|
||
result["esports_points_after_gift"] = points_result["esports_points"]
|
||
result["esports_points_changed"] = (
|
||
baseline_points is not None
|
||
and points_result["esports_points"] is not None
|
||
and points_result["esports_points"] != baseline_points
|
||
)
|
||
except Exception as exc:
|
||
refresh_errors.append(f"电竞积分: {exc}")
|
||
if refresh_errors:
|
||
result["refresh_errors"] = refresh_errors
|
||
|
||
account.esports_bind_status = "esports_gift_donated"
|
||
account.updated_at = datetime.now(UTC)
|
||
message = f"赠送{gift_name}成功: {gift_count}"
|
||
if account.gold_balance is not None:
|
||
message += f",鱼翅余额: {account.gold_balance}"
|
||
if account.esports_points is not None:
|
||
message += f",电竞积分: {account.esports_points}"
|
||
self._mark_task(db, task, "success", message, result)
|
||
|
||
def _execute_donate_esports_chicken_gift(
|
||
self,
|
||
db: Session,
|
||
task: DouyuTask,
|
||
account: Account,
|
||
cookie: str,
|
||
config: dict,
|
||
):
|
||
"""赠送冠军鸡腿。"""
|
||
self._execute_donate_esports_gift(
|
||
db,
|
||
task,
|
||
account,
|
||
cookie,
|
||
config,
|
||
gift_name="冠军鸡腿",
|
||
config_gift_id_key="esports_chicken_gift_id",
|
||
config_skin_id_key="esports_chicken_skin_id",
|
||
)
|
||
|
||
def _execute_donate_esports_firework_gift(
|
||
self,
|
||
db: Session,
|
||
task: DouyuTask,
|
||
account: Account,
|
||
cookie: str,
|
||
config: dict,
|
||
):
|
||
"""赠送冠军烟花。"""
|
||
self._execute_donate_esports_gift(
|
||
db,
|
||
task,
|
||
account,
|
||
cookie,
|
||
config,
|
||
gift_name="冠军烟花",
|
||
config_gift_id_key="esports_firework_gift_id",
|
||
config_skin_id_key="esports_firework_skin_id",
|
||
)
|
||
|
||
def _execute_donate_elite_gift(
|
||
self, db: Session, task: DouyuTask, account: Account, cookie: str, config: dict
|
||
):
|
||
payload = self._task_payload(task)
|
||
gift_count = int(payload.get("gift_count") or payload.get("count") or 1)
|
||
client = self._client(cookie)
|
||
ctn = None
|
||
baseline_points = account.points
|
||
try:
|
||
ctn = client.acf_ccn(refresh_subscribe=False)
|
||
baseline_result = self._refresh_account_points(
|
||
client, account, cookie, ctn=ctn
|
||
)
|
||
db.commit()
|
||
baseline_points = baseline_result["points"]
|
||
except Exception as exc:
|
||
self._push_log("warning", f"赠送精英令前刷新积分失败: {exc}")
|
||
result = client.donate_elite_gift(
|
||
gift_count=gift_count,
|
||
room_id=str(payload.get("room_id") or config["room_id"]),
|
||
gift_id=str(payload.get("gift_id") or config["gift_id"]),
|
||
skin_id=str(payload.get("skin_id") or config["skin_id"]),
|
||
)
|
||
result["gift_points_baseline"] = baseline_points
|
||
refresh_errors = []
|
||
try:
|
||
result.update(self._refresh_account_gold_balance(client, account))
|
||
except Exception as exc:
|
||
refresh_errors.append(f"鱼翅余额: {exc}")
|
||
try:
|
||
result.update(
|
||
self._refresh_points_after_elite_gift(
|
||
db,
|
||
task,
|
||
account,
|
||
client,
|
||
cookie,
|
||
ctn,
|
||
result,
|
||
baseline_points,
|
||
gift_count,
|
||
)
|
||
)
|
||
except Exception as exc:
|
||
refresh_errors.append(f"积分: {exc}")
|
||
if refresh_errors:
|
||
result["refresh_errors"] = refresh_errors
|
||
account.bind_status = "gift_donated"
|
||
account.updated_at = datetime.now(UTC)
|
||
message = f"赠送精英令成功: {gift_count}"
|
||
if account.gold_balance is not None:
|
||
message += f",鱼翅余额: {account.gold_balance}"
|
||
if account.points is not None:
|
||
message += f",积分: {account.points}"
|
||
if result.get("gift_points_target") is not None and not result.get(
|
||
"gift_points_confirmed"
|
||
):
|
||
message += f"(未确认涨到 {result['gift_points_target']})"
|
||
self._mark_task(db, task, "success", message, result)
|