实现虎牙商品兑换并优化列表展示
This commit is contained in:
@@ -1,6 +1,7 @@
|
||||
"""虎牙任务批次执行器。"""
|
||||
|
||||
import asyncio
|
||||
import time
|
||||
import threading
|
||||
from concurrent.futures import ThreadPoolExecutor, as_completed
|
||||
from datetime import datetime, timezone
|
||||
@@ -123,6 +124,31 @@ class HuyaBatchRunner:
|
||||
return ""
|
||||
return datetime.fromtimestamp(timestamp).strftime("%Y-%m-%d %H:%M:%S")
|
||||
|
||||
@staticmethod
|
||||
def _parse_scheduled_time(value) -> datetime | None:
|
||||
text = str(value or "").strip()
|
||||
if not text:
|
||||
return None
|
||||
try:
|
||||
normalized = text.replace("Z", "+00:00")
|
||||
dt = datetime.fromisoformat(normalized)
|
||||
except ValueError:
|
||||
return None
|
||||
if dt.tzinfo is None:
|
||||
return dt.astimezone()
|
||||
return dt
|
||||
|
||||
def _wait_until(self, when: datetime, uid: int) -> bool:
|
||||
target = when.timestamp()
|
||||
local_text = self._format_local_time(int(target))
|
||||
self._push_log("info", f"[{uid}] 定时兑换等待到 {local_text}")
|
||||
while not self._stop.is_set():
|
||||
remaining = target - time.time()
|
||||
if remaining <= 0:
|
||||
return True
|
||||
time.sleep(min(0.2, max(0.02, remaining)))
|
||||
return False
|
||||
|
||||
@classmethod
|
||||
def _bind_change_state(cls, bind_status) -> dict:
|
||||
account_data = bind_status.accountData
|
||||
@@ -337,6 +363,82 @@ class HuyaBatchRunner:
|
||||
message = f"已刷新商品 {len(goods)} 个"
|
||||
self._mark_task(worker_db, task, "success", message, {**result, "goods": goods})
|
||||
|
||||
def _execute_exchange_goods(
|
||||
self,
|
||||
worker_db: Session,
|
||||
task: HuyaTask,
|
||||
account: HuyaAccount,
|
||||
account_info: dict,
|
||||
config_info: dict,
|
||||
):
|
||||
sid = str(self.payload.get("sid") or config_info.get("sid") or "").strip()
|
||||
if not sid:
|
||||
self._mark_task(worker_db, task, "failed", "请先配置虎牙活动 SID")
|
||||
return
|
||||
|
||||
sid_int = self._to_int(sid)
|
||||
if not sid_int:
|
||||
self._mark_task(worker_db, task, "failed", f"虎牙活动 SID 无效: {sid}")
|
||||
return
|
||||
|
||||
product_id = self._to_int(self.payload.get("product_id"))
|
||||
if not product_id:
|
||||
self._mark_task(worker_db, task, "failed", "请选择兑换商品")
|
||||
return
|
||||
|
||||
uid = self._resolve_uid(account_info)
|
||||
if not uid:
|
||||
self._mark_task(worker_db, task, "failed", "无法从账号或 Cookie 解析 yyuid")
|
||||
return
|
||||
|
||||
cookie = account_info.get("cookie") or ""
|
||||
if not cookie:
|
||||
self._mark_task(worker_db, task, "failed", "账号 Cookie 为空")
|
||||
return
|
||||
|
||||
snapshot = worker_db.query(HuyaGoodsSnapshot).filter(
|
||||
HuyaGoodsSnapshot.product_id == str(product_id)
|
||||
).first()
|
||||
product_name = str(self.payload.get("product_name") or (snapshot.name if snapshot else "") or product_id)
|
||||
scheduled_at = self._parse_scheduled_time(self.payload.get("scheduled_at"))
|
||||
if self.payload.get("scheduled_at") and scheduled_at is None:
|
||||
self._mark_task(worker_db, task, "failed", "定时兑换时间格式无效")
|
||||
return
|
||||
if scheduled_at and scheduled_at.timestamp() > time.time():
|
||||
if not self._wait_until(scheduled_at, uid):
|
||||
self._mark_task(worker_db, task, "failed", "兑换任务已停止")
|
||||
return
|
||||
|
||||
client = HuyaHttpClient(logger=lambda msg: self._push_log("info", f"[{uid}] {msg}"))
|
||||
response = client.score_exchange_prize(uid=uid, cookie=cookie, sid=sid_int, pid=product_id)
|
||||
if response is None:
|
||||
self._mark_task(worker_db, task, "error", "虎牙兑换接口无响应")
|
||||
return
|
||||
|
||||
result = response.to_dict()
|
||||
result.update({
|
||||
"sid": sid_int,
|
||||
"product_id": str(product_id),
|
||||
"product_name": product_name,
|
||||
"scheduled_at": scheduled_at.isoformat() if scheduled_at else "",
|
||||
"executed_at": datetime.now(timezone.utc).isoformat(),
|
||||
"goods": snapshot.raw if snapshot else None,
|
||||
})
|
||||
if response.status != 200:
|
||||
self._mark_task(
|
||||
worker_db,
|
||||
task,
|
||||
"failed",
|
||||
response.msg or f"虎牙兑换失败: {response.status}",
|
||||
result,
|
||||
)
|
||||
return
|
||||
|
||||
account.status = "goods_exchanged"
|
||||
account.updated_at = datetime.now(timezone.utc)
|
||||
message = response.msg or f"兑换成功: {product_name}"
|
||||
self._mark_task(worker_db, task, "success", message, result)
|
||||
|
||||
@staticmethod
|
||||
def _normalize_pay_channel(value) -> str:
|
||||
text = str(value or "").strip()
|
||||
@@ -1017,6 +1119,7 @@ class HuyaBatchRunner:
|
||||
"query_exchange_records",
|
||||
"refresh_goods",
|
||||
"refresh_recharge_goods",
|
||||
"exchange_goods",
|
||||
"create_recharge_order",
|
||||
}:
|
||||
self._mark_task(worker_db, task, "failed", "该虎牙任务执行器暂未实现")
|
||||
@@ -1030,6 +1133,8 @@ class HuyaBatchRunner:
|
||||
self._execute_refresh_goods(worker_db, task, account, account_info, config_info)
|
||||
elif self.task_type == "refresh_recharge_goods":
|
||||
self._execute_refresh_recharge_goods(worker_db, task, account, account_info, config_info)
|
||||
elif self.task_type == "exchange_goods":
|
||||
self._execute_exchange_goods(worker_db, task, account, account_info, config_info)
|
||||
elif self.task_type == "create_recharge_order":
|
||||
self._execute_create_recharge_order(worker_db, task, account, account_info, config_info)
|
||||
elif self.task_type == "get_bind_qr":
|
||||
|
||||
@@ -18,6 +18,7 @@ SUPPORTED_TASK_TYPES = {
|
||||
"confirm_bind": "确认绑定",
|
||||
"refresh_goods": "刷新商品列表",
|
||||
"refresh_recharge_goods": "刷新充值商品列表",
|
||||
"exchange_goods": "兑换商品",
|
||||
"create_recharge_order": "生成支付二维码",
|
||||
}
|
||||
|
||||
@@ -172,8 +173,8 @@ def create_planned_tasks(
|
||||
batch_id = uuid.uuid4().hex[:12]
|
||||
payload = payload or {}
|
||||
accounts = db.query(HuyaAccount).filter(HuyaAccount.id.in_(account_ids)).all()
|
||||
if task_type in {"refresh_goods", "refresh_recharge_goods", "create_recharge_order"} and accounts:
|
||||
# 全局快照和单笔支付二维码都使用一个选中的 CK 即可。
|
||||
if task_type in {"refresh_goods", "refresh_recharge_goods", "exchange_goods", "create_recharge_order"} and accounts:
|
||||
# 全局快照、单笔兑换和单笔支付二维码都使用一个选中的 CK 即可。
|
||||
accounts = accounts[:1]
|
||||
for account in accounts:
|
||||
db.add(HuyaTask(
|
||||
|
||||
Reference in New Issue
Block a user