执行 Ruff 安全自动修复
This commit is contained in:
@@ -8,9 +8,9 @@ import uuid
|
||||
import zipfile
|
||||
from concurrent.futures import ThreadPoolExecutor, as_completed
|
||||
from dataclasses import dataclass, field
|
||||
from datetime import datetime, timezone
|
||||
from datetime import UTC, datetime
|
||||
from types import SimpleNamespace
|
||||
from typing import Optional, cast
|
||||
from typing import cast
|
||||
|
||||
from core.douyu import DouyuLogin, WgapiLoginAPI
|
||||
from core.douyu.login import AccountLike
|
||||
@@ -21,7 +21,7 @@ from ..models import ProxyConfig as ProxyConfigModel
|
||||
|
||||
def _now() -> datetime:
|
||||
"""返回时区感知 UTC 时间。"""
|
||||
return datetime.now(timezone.utc)
|
||||
return datetime.now(UTC)
|
||||
|
||||
|
||||
STATUS_LABELS = {
|
||||
@@ -139,7 +139,7 @@ class AccountCheckRunner:
|
||||
self,
|
||||
batch: AccountCheckBatch,
|
||||
accounts: list[AccountCheckInput],
|
||||
proxy_config: Optional[ProxyConfigModel] = None,
|
||||
proxy_config: ProxyConfigModel | None = None,
|
||||
):
|
||||
self.batch = batch
|
||||
self.accounts = accounts
|
||||
@@ -394,7 +394,7 @@ class AccountCheckRegistry:
|
||||
max_login_retries: int,
|
||||
max_total_time: float,
|
||||
use_proxy: bool = False,
|
||||
proxy_config: Optional[ProxyConfigModel] = None,
|
||||
proxy_config: ProxyConfigModel | None = None,
|
||||
) -> AccountCheckRunner:
|
||||
batch_id = uuid.uuid4().hex[:12]
|
||||
normalized_retries = max_login_retries if max_login_retries > 0 else 20
|
||||
|
||||
@@ -7,7 +7,6 @@ from sqlalchemy.orm import Session
|
||||
|
||||
from ..models import Account, LoginTask
|
||||
|
||||
|
||||
EMAIL_PATTERN = re.compile(r"^[^\s@|]+@[^\s@|]+\.[^\s@|]+$")
|
||||
|
||||
|
||||
|
||||
@@ -10,7 +10,6 @@ from sqlalchemy.orm import Session
|
||||
|
||||
from ..models import AuditLog, User
|
||||
|
||||
|
||||
_SENSITIVE_KEY_PARTS = (
|
||||
"cookie",
|
||||
"token",
|
||||
|
||||
@@ -1,10 +1,9 @@
|
||||
"""斗鱼 Cookie 有效性检测服务。"""
|
||||
|
||||
from datetime import datetime, timezone
|
||||
from datetime import UTC, datetime
|
||||
|
||||
import requests
|
||||
|
||||
|
||||
CHECK_UA = (
|
||||
"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 "
|
||||
"(KHTML, like Gecko) Chrome/150.0.0.0 Safari/537.36"
|
||||
@@ -15,7 +14,7 @@ USER_LEVEL_API = "https://www.douyu.com/japi/interactnc/web/userLevel/userLevelD
|
||||
|
||||
def check_douyu_cookie(cookie: str) -> dict:
|
||||
"""检测斗鱼 Cookie 有效性,鱼丸与等级接口均通过才算有效。"""
|
||||
checked_at = datetime.now(timezone.utc)
|
||||
checked_at = datetime.now(UTC)
|
||||
base = {
|
||||
"checked_at": checked_at,
|
||||
"fish_ball": None,
|
||||
|
||||
@@ -7,17 +7,21 @@ from concurrent.futures import ThreadPoolExecutor, as_completed
|
||||
from sqlalchemy.orm import joinedload
|
||||
|
||||
from core.douyu import DouyuActivityError
|
||||
|
||||
from ..database import SessionLocal
|
||||
from ..models import DouyuTask
|
||||
from .cookie_check_service import check_douyu_cookie
|
||||
from .douyu_service import latest_success_login_task, update_account_profile_from_cookie
|
||||
from .douyu_runner_core import DouyuBatchRunnerCore, douyu_batch_registry # noqa: F401 (douyu_batch_registry 供 routers 重导出)
|
||||
from .douyu_runner_bind import BindMixin
|
||||
from .douyu_runner_manual import ManualMixin
|
||||
from .douyu_runner_gold import GoldMixin
|
||||
from .douyu_runner_core import ( # noqa: F401 (douyu_batch_registry 供 routers 重导出)
|
||||
DouyuBatchRunnerCore,
|
||||
douyu_batch_registry,
|
||||
)
|
||||
from .douyu_runner_donate import DonateMixin
|
||||
from .douyu_runner_gold import GoldMixin
|
||||
from .douyu_runner_goods import GoodsMixin
|
||||
from .douyu_runner_manual import ManualMixin
|
||||
from .douyu_runner_xpd import XpdMixin
|
||||
from .douyu_service import latest_success_login_task, update_account_profile_from_cookie
|
||||
|
||||
|
||||
class DouyuBatchRunner(
|
||||
|
||||
@@ -1,16 +1,16 @@
|
||||
"""斗鱼任务执行器:角色绑定(由 douyu_runner.py 按功能域拆分)。"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import time
|
||||
from datetime import datetime, timezone
|
||||
from typing import Any, TYPE_CHECKING
|
||||
from datetime import UTC, datetime
|
||||
from typing import TYPE_CHECKING, Any
|
||||
|
||||
from sqlalchemy.orm import Session
|
||||
|
||||
from core.douyu import DouyuActivityClient, DouyuActivityError
|
||||
from ..models import Account, DouyuTask
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from .douyu_runner import DouyuBatchRunner
|
||||
from ..models import Account, DouyuTask
|
||||
|
||||
DOUYU_LEGACY_BIND_ACT_ALIAS = "20250213NQCYX"
|
||||
DOUYU_BIND_ROLE_POLL_SECONDS = 65
|
||||
@@ -126,7 +126,7 @@ class BindMixin:
|
||||
if str(bind_info.get("api_version") or "") == "esports":
|
||||
can_change_time = cls._to_int(bind_info.get("can_change_time"))
|
||||
if can_change_time is not None:
|
||||
return can_change_time <= int(datetime.now(timezone.utc).timestamp())
|
||||
return can_change_time <= int(datetime.now(UTC).timestamp())
|
||||
wait_time = cls._to_int(bind_info.get("change_role_wait_time"))
|
||||
if wait_time is not None and wait_time > 0:
|
||||
return False
|
||||
@@ -236,7 +236,7 @@ class BindMixin:
|
||||
bind_info.get("change_role_wait_time")
|
||||
)
|
||||
account.bind_status = status
|
||||
account.updated_at = datetime.now(timezone.utc)
|
||||
account.updated_at = datetime.now(UTC)
|
||||
|
||||
def _apply_esports_bind_info_to_account(
|
||||
self, account: Account, bind_info: dict, status: str
|
||||
@@ -252,7 +252,7 @@ class BindMixin:
|
||||
)
|
||||
account.esports_can_change_time = self._to_int(bind_info.get("can_change_time"))
|
||||
account.esports_bind_status = status
|
||||
account.updated_at = datetime.now(timezone.utc)
|
||||
account.updated_at = datetime.now(UTC)
|
||||
|
||||
def _esports_bind_state(
|
||||
self,
|
||||
@@ -503,7 +503,7 @@ class BindMixin:
|
||||
role_name = snapshot["role_name"]
|
||||
# 待确认角色只回传前端展示,不写入账号表,避免“未换绑成功但角色信息已变新”
|
||||
account.bind_status = "game_queried"
|
||||
account.updated_at = datetime.now(timezone.utc)
|
||||
account.updated_at = datetime.now(UTC)
|
||||
self._push_log(
|
||||
"success", f"识别到待确认角色: {role_name} (act={query_alias})"
|
||||
)
|
||||
@@ -688,7 +688,7 @@ class BindMixin:
|
||||
"bind_polling": True,
|
||||
}
|
||||
account.bind_status = "bind_qr_generated"
|
||||
account.updated_at = datetime.now(timezone.utc)
|
||||
account.updated_at = datetime.now(UTC)
|
||||
# 关键:先把二维码 progress 出去,前端 running 期间即可弹窗扫码。
|
||||
self._update_task_progress(
|
||||
db, task, "running", "已生成绑定二维码,等待扫码绑定", result
|
||||
@@ -822,7 +822,7 @@ class BindMixin:
|
||||
self._apply_bind_info_to_account(account, before_bound, "game_queried")
|
||||
else:
|
||||
account.bind_status = "game_queried"
|
||||
account.updated_at = datetime.now(timezone.utc)
|
||||
account.updated_at = datetime.now(UTC)
|
||||
self._mark_task(
|
||||
db,
|
||||
task,
|
||||
@@ -882,7 +882,7 @@ class BindMixin:
|
||||
)
|
||||
else:
|
||||
account.bind_status = "game_queried"
|
||||
account.updated_at = datetime.now(timezone.utc)
|
||||
account.updated_at = datetime.now(UTC)
|
||||
self._mark_task(
|
||||
db,
|
||||
task,
|
||||
@@ -917,7 +917,7 @@ class BindMixin:
|
||||
)
|
||||
else:
|
||||
account.bind_status = "bind_confirmed"
|
||||
account.updated_at = datetime.now(timezone.utc)
|
||||
account.updated_at = datetime.now(UTC)
|
||||
self._mark_task(
|
||||
db,
|
||||
task,
|
||||
@@ -1027,7 +1027,7 @@ class BindMixin:
|
||||
"bind_phase": "switching_role",
|
||||
}
|
||||
account.esports_bind_status = "esports_role_switching"
|
||||
account.updated_at = datetime.now(timezone.utc)
|
||||
account.updated_at = datetime.now(UTC)
|
||||
self._mark_task(
|
||||
db,
|
||||
task,
|
||||
@@ -1052,7 +1052,7 @@ class BindMixin:
|
||||
return
|
||||
|
||||
account.esports_bind_status = "esports_bind_confirming"
|
||||
account.updated_at = datetime.now(timezone.utc)
|
||||
account.updated_at = datetime.now(UTC)
|
||||
self._update_task_progress(
|
||||
db,
|
||||
task,
|
||||
@@ -1191,7 +1191,7 @@ class BindMixin:
|
||||
self._apply_bind_info_to_account(account, bind_info, "game_queried")
|
||||
else:
|
||||
account.bind_status = "game_queried" if has_pending else "game_not_bound"
|
||||
account.updated_at = datetime.now(timezone.utc)
|
||||
account.updated_at = datetime.now(UTC)
|
||||
result = {
|
||||
"act_alias": source_alias,
|
||||
"query_act_alias": source_alias,
|
||||
@@ -1281,7 +1281,7 @@ class BindMixin:
|
||||
wait_time = snapshot["change_role_wait_time"]
|
||||
account.change_role_wait_time = wait_time
|
||||
account.bind_status = "change_time_queried"
|
||||
account.updated_at = datetime.now(timezone.utc)
|
||||
account.updated_at = datetime.now(UTC)
|
||||
source_alias = str(bind_info.get("act_alias") or "")
|
||||
wait_text = self._format_wait_time(wait_time)
|
||||
can_change = snapshot["can_change_role"]
|
||||
|
||||
@@ -5,27 +5,30 @@ from __future__ import annotations
|
||||
import asyncio
|
||||
import threading
|
||||
import time
|
||||
from datetime import datetime, timezone
|
||||
from typing import Optional, TYPE_CHECKING
|
||||
from datetime import UTC, datetime
|
||||
from typing import TYPE_CHECKING
|
||||
|
||||
from loguru import logger
|
||||
from sqlalchemy.orm import Session
|
||||
|
||||
from core.douyu import DouyuActivityClient
|
||||
from core.douyu.proxy_fetcher import ProxyFetcher
|
||||
|
||||
from ..models import (
|
||||
Account,
|
||||
DouyuEsportsGoodsSnapshot,
|
||||
DouyuGoodsSnapshot,
|
||||
DouyuTask,
|
||||
DouyuXpdGoodsSnapshot,
|
||||
)
|
||||
from ..models import (
|
||||
ProxyConfig as ProxyConfigModel,
|
||||
)
|
||||
from .douyu_service import (
|
||||
DOUYU_CONFIG_FIELDS,
|
||||
douyu_config_value,
|
||||
ensure_douyu_config,
|
||||
douyu_task_payload,
|
||||
ensure_douyu_config,
|
||||
)
|
||||
|
||||
if TYPE_CHECKING:
|
||||
@@ -62,9 +65,9 @@ class DouyuBatchRunnerCore:
|
||||
db: Session,
|
||||
batch_id: str,
|
||||
task_type: str,
|
||||
payload: Optional[dict] = None,
|
||||
log_queue: Optional[asyncio.Queue] = None,
|
||||
loop: Optional[asyncio.AbstractEventLoop] = None,
|
||||
payload: dict | None = None,
|
||||
log_queue: asyncio.Queue | None = None,
|
||||
loop: asyncio.AbstractEventLoop | None = None,
|
||||
concurrency: int = 3,
|
||||
):
|
||||
self.db = db
|
||||
@@ -212,7 +215,7 @@ class DouyuBatchRunnerCore:
|
||||
task.message = message[:512]
|
||||
if result is not None:
|
||||
task.result = result
|
||||
task.finished_at = datetime.now(timezone.utc)
|
||||
task.finished_at = datetime.now(UTC)
|
||||
db.commit()
|
||||
self._push_task_event(task)
|
||||
|
||||
@@ -232,7 +235,7 @@ class DouyuBatchRunnerCore:
|
||||
self._push_task_event(task)
|
||||
|
||||
def _upsert_goods(self, db: Session, goods: list[dict]) -> None:
|
||||
now = datetime.now(timezone.utc)
|
||||
now = datetime.now(UTC)
|
||||
for raw in goods:
|
||||
commodity_id = str(raw.get("commodityId") or raw.get("commodity_id") or "")
|
||||
if not commodity_id:
|
||||
@@ -254,7 +257,7 @@ class DouyuBatchRunnerCore:
|
||||
db.commit()
|
||||
|
||||
def _upsert_esports_goods(self, db: Session, goods: list[dict]) -> None:
|
||||
now = datetime.now(timezone.utc)
|
||||
now = datetime.now(UTC)
|
||||
for raw in goods:
|
||||
commodity_id = str(raw.get("commodityId") or raw.get("commodity_id") or "")
|
||||
if not commodity_id:
|
||||
@@ -276,7 +279,7 @@ class DouyuBatchRunnerCore:
|
||||
|
||||
def _upsert_xpd_goods(self, db: Session, goods: list[dict]) -> None:
|
||||
"""同步和平小店商品快照,移除上一次热门抢购等遗留商品。"""
|
||||
now = datetime.now(timezone.utc)
|
||||
now = datetime.now(UTC)
|
||||
commodity_ids = {
|
||||
str(raw.get("commodity_id") or raw.get("iGoodsId") or "") for raw in goods
|
||||
}
|
||||
|
||||
@@ -1,15 +1,15 @@
|
||||
"""斗鱼任务执行器:送礼(由 douyu_runner.py 按功能域拆分)。"""
|
||||
|
||||
from __future__ import annotations
|
||||
from datetime import datetime, timezone
|
||||
from typing import Any, TYPE_CHECKING
|
||||
|
||||
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
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from .douyu_runner import DouyuBatchRunner
|
||||
from ..models import Account, DouyuTask
|
||||
|
||||
DOUYU_GIFT_POINTS_REFRESH_TIMES = 3
|
||||
DOUYU_GIFT_POINTS_REFRESH_INTERVAL = 2
|
||||
@@ -177,7 +177,7 @@ class DonateMixin:
|
||||
result["refresh_errors"] = refresh_errors
|
||||
|
||||
account.esports_bind_status = "esports_gift_donated"
|
||||
account.updated_at = datetime.now(timezone.utc)
|
||||
account.updated_at = datetime.now(UTC)
|
||||
message = f"赠送{gift_name}成功: {gift_count}"
|
||||
if account.gold_balance is not None:
|
||||
message += f",鱼翅余额: {account.gold_balance}"
|
||||
@@ -273,7 +273,7 @@ class DonateMixin:
|
||||
if refresh_errors:
|
||||
result["refresh_errors"] = refresh_errors
|
||||
account.bind_status = "gift_donated"
|
||||
account.updated_at = datetime.now(timezone.utc)
|
||||
account.updated_at = datetime.now(UTC)
|
||||
message = f"赠送精英令成功: {gift_count}"
|
||||
if account.gold_balance is not None:
|
||||
message += f",鱼翅余额: {account.gold_balance}"
|
||||
|
||||
@@ -1,11 +1,13 @@
|
||||
"""斗鱼任务执行器:鱼翅充值(由 douyu_runner.py 按功能域拆分)。"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import re
|
||||
import time
|
||||
from datetime import UTC, datetime
|
||||
from decimal import Decimal
|
||||
from datetime import datetime, timezone
|
||||
from typing import Any, TYPE_CHECKING, cast
|
||||
from typing import TYPE_CHECKING, Any, cast
|
||||
|
||||
from sqlalchemy.orm import Session
|
||||
|
||||
from core.douyu import (
|
||||
@@ -14,12 +16,10 @@ from core.douyu import (
|
||||
FishFinRechargeConfig,
|
||||
FishFinRechargeError,
|
||||
)
|
||||
from ..models import Account, DouyuTask
|
||||
from .douyu_service import update_account_profile_from_cookie
|
||||
from .douyu_runner_core import DOUYU_PAYMENT_POLL_INTERVAL, DOUYU_PAYMENT_POLL_SECONDS
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from .douyu_runner import DouyuBatchRunner
|
||||
from ..models import Account, DouyuTask
|
||||
from .douyu_runner_core import DOUYU_PAYMENT_POLL_INTERVAL, DOUYU_PAYMENT_POLL_SECONDS
|
||||
from .douyu_service import update_account_profile_from_cookie
|
||||
|
||||
|
||||
class GoldMixin:
|
||||
@@ -45,7 +45,7 @@ class GoldMixin:
|
||||
exchange = client.exchange_balance()
|
||||
account.gold_balance = self._to_int(gold.get("gold"))
|
||||
account.exchange_balance = self._to_int(exchange.get("count"))
|
||||
account.updated_at = datetime.now(timezone.utc)
|
||||
account.updated_at = datetime.now(UTC)
|
||||
return {
|
||||
"gold_balance": account.gold_balance,
|
||||
"exchange_balance": account.exchange_balance,
|
||||
@@ -266,7 +266,7 @@ class GoldMixin:
|
||||
amount=amount, pay_type=int(config["gold_pay_type"])
|
||||
)
|
||||
account.bind_status = "gold_qr_created"
|
||||
account.updated_at = datetime.now(timezone.utc)
|
||||
account.updated_at = datetime.now(UTC)
|
||||
self._update_task_progress(
|
||||
db, task, "running", f"鱼翅 {amount} 元支付码已生成,等待到账", result
|
||||
)
|
||||
@@ -278,7 +278,7 @@ class GoldMixin:
|
||||
return
|
||||
if recharged:
|
||||
account.bind_status = "gold_recharged"
|
||||
account.updated_at = datetime.now(timezone.utc)
|
||||
account.updated_at = datetime.now(UTC)
|
||||
self._mark_task(
|
||||
db,
|
||||
task,
|
||||
@@ -414,7 +414,7 @@ class GoldMixin:
|
||||
)
|
||||
return
|
||||
account.bind_status = "gold_api_order_created"
|
||||
account.updated_at = datetime.now(timezone.utc)
|
||||
account.updated_at = datetime.now(UTC)
|
||||
self._update_task_progress(
|
||||
db, task, "running", "供应商直充订单已创建,等待到账", result
|
||||
)
|
||||
@@ -425,7 +425,7 @@ class GoldMixin:
|
||||
return
|
||||
if status == 2:
|
||||
account.bind_status = "gold_recharged"
|
||||
account.updated_at = datetime.now(timezone.utc)
|
||||
account.updated_at = datetime.now(UTC)
|
||||
self._mark_task(db, task, "success", "供应商直充成功", result)
|
||||
return
|
||||
if status in {3, 4}:
|
||||
@@ -439,7 +439,7 @@ class GoldMixin:
|
||||
client = self._client(cookie)
|
||||
result = self._refresh_account_gold_balance(client, account)
|
||||
account.bind_status = "gold_balance_queried"
|
||||
account.updated_at = datetime.now(timezone.utc)
|
||||
account.updated_at = datetime.now(UTC)
|
||||
self._mark_task(
|
||||
db,
|
||||
task,
|
||||
|
||||
@@ -1,17 +1,17 @@
|
||||
"""斗鱼任务执行器:商城兑换(由 douyu_runner.py 按功能域拆分)。"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import random
|
||||
import time
|
||||
from datetime import datetime, timezone
|
||||
from typing import Any, TYPE_CHECKING
|
||||
from datetime import UTC, datetime
|
||||
from typing import TYPE_CHECKING, Any
|
||||
|
||||
from sqlalchemy.orm import Session
|
||||
|
||||
from core.douyu import DouyuActivityClient, DouyuActivityError
|
||||
from ..models import Account, DouyuEsportsGoodsSnapshot, DouyuGoodsSnapshot, DouyuTask
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from .douyu_runner import DouyuBatchRunner
|
||||
from ..models import Account, DouyuEsportsGoodsSnapshot, DouyuGoodsSnapshot, DouyuTask
|
||||
|
||||
# 兑换节奏与重试(对齐 8.30 浏览器抓包:锁单->支付间隔约 2.2~4.2s;火爆类错误要长退避而不是秒级连打)
|
||||
DOUYU_EXCHANGE_PRE_CREATE_JITTER = (0.3, 1.2)
|
||||
@@ -65,7 +65,7 @@ class GoodsMixin:
|
||||
goods = result["goods"]
|
||||
self._upsert_goods(db, goods)
|
||||
account.bind_status = account.bind_status or "active"
|
||||
account.updated_at = datetime.now(timezone.utc)
|
||||
account.updated_at = datetime.now(UTC)
|
||||
self._mark_task(
|
||||
db,
|
||||
task,
|
||||
@@ -91,7 +91,7 @@ class GoodsMixin:
|
||||
goods = result["goods"]
|
||||
self._upsert_esports_goods(db, goods)
|
||||
account.esports_bind_status = "esports_goods_refreshed"
|
||||
account.updated_at = datetime.now(timezone.utc)
|
||||
account.updated_at = datetime.now(UTC)
|
||||
self._mark_task(
|
||||
db,
|
||||
task,
|
||||
@@ -131,7 +131,7 @@ class GoodsMixin:
|
||||
.first()
|
||||
)
|
||||
account.bind_status = "goods_locked"
|
||||
account.updated_at = datetime.now(timezone.utc)
|
||||
account.updated_at = datetime.now(UTC)
|
||||
expire_seconds = result.get("expire_seconds")
|
||||
expire_text = f",{expire_seconds} 秒内有效" if expire_seconds else ""
|
||||
self._mark_task(
|
||||
@@ -181,7 +181,7 @@ class GoodsMixin:
|
||||
.first()
|
||||
)
|
||||
account.bind_status = "goods_exchanged"
|
||||
account.updated_at = datetime.now(timezone.utc)
|
||||
account.updated_at = datetime.now(UTC)
|
||||
result = {
|
||||
"commodity_id": commodity_id,
|
||||
"order_id": order_id,
|
||||
@@ -346,7 +346,7 @@ class GoodsMixin:
|
||||
finish_failed(f"预约到货失败: {exc}")
|
||||
return
|
||||
account.bind_status = "goods_subscribed"
|
||||
account.updated_at = datetime.now(timezone.utc)
|
||||
account.updated_at = datetime.now(UTC)
|
||||
self._mark_task(
|
||||
db,
|
||||
task,
|
||||
@@ -383,7 +383,7 @@ class GoodsMixin:
|
||||
finish_failed(f"预兑失败: {exc}")
|
||||
return
|
||||
account.bind_status = "goods_pre_exchanged"
|
||||
account.updated_at = datetime.now(timezone.utc)
|
||||
account.updated_at = datetime.now(UTC)
|
||||
self._mark_task(
|
||||
db,
|
||||
task,
|
||||
@@ -485,7 +485,7 @@ class GoodsMixin:
|
||||
}
|
||||
)
|
||||
account.bind_status = "goods_exchanged"
|
||||
account.updated_at = datetime.now(timezone.utc)
|
||||
account.updated_at = datetime.now(UTC)
|
||||
# 兑换成功后自动刷新积分,更新账号最新积分信息(失败不阻断兑换成功)
|
||||
points_refresh = None
|
||||
try:
|
||||
@@ -575,7 +575,7 @@ class GoodsMixin:
|
||||
result["esports_points_refresh_error"] = str(exc)
|
||||
|
||||
account.esports_bind_status = "esports_goods_exchanged"
|
||||
account.updated_at = datetime.now(timezone.utc)
|
||||
account.updated_at = datetime.now(UTC)
|
||||
name = (goods.name if goods else "") or commodity_id
|
||||
message = f"兑换电竞皮肤成功: {name}"
|
||||
if quantity > 1:
|
||||
@@ -608,7 +608,7 @@ class GoodsMixin:
|
||||
else f"限兑 {len(names)} 个: {', '.join(names[:5])}"
|
||||
)
|
||||
account.bind_status = "limited_goods_queried"
|
||||
account.updated_at = datetime.now(timezone.utc)
|
||||
account.updated_at = datetime.now(UTC)
|
||||
self._mark_task(
|
||||
db,
|
||||
task,
|
||||
@@ -624,7 +624,7 @@ class GoodsMixin:
|
||||
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)
|
||||
account.updated_at = datetime.now(UTC)
|
||||
self._mark_task(
|
||||
db,
|
||||
task,
|
||||
@@ -639,7 +639,7 @@ class GoodsMixin:
|
||||
client = self._client(cookie)
|
||||
token = client.csrf_token()
|
||||
account.bind_status = "csrf_token_ready"
|
||||
account.updated_at = datetime.now(timezone.utc)
|
||||
account.updated_at = datetime.now(UTC)
|
||||
self._mark_task(
|
||||
db,
|
||||
task,
|
||||
|
||||
@@ -1,18 +1,18 @@
|
||||
"""斗鱼任务执行器:手册开通与积分(由 douyu_runner.py 按功能域拆分)。"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import time
|
||||
from datetime import datetime, timezone
|
||||
from typing import Any, TYPE_CHECKING
|
||||
from datetime import UTC, datetime
|
||||
from typing import TYPE_CHECKING, Any
|
||||
|
||||
from sqlalchemy.orm import Session
|
||||
|
||||
from core.douyu import DouyuActivityClient, DouyuActivityError
|
||||
from ..models import Account, DouyuTask
|
||||
from .douyu_service import account_uid, update_account_profile_from_cookie
|
||||
from .douyu_runner_core import DOUYU_PAYMENT_POLL_INTERVAL, DOUYU_PAYMENT_POLL_SECONDS
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from .douyu_runner import DouyuBatchRunner
|
||||
from ..models import Account, DouyuTask
|
||||
from .douyu_runner_core import DOUYU_PAYMENT_POLL_INTERVAL, DOUYU_PAYMENT_POLL_SECONDS
|
||||
from .douyu_service import account_uid, update_account_profile_from_cookie
|
||||
|
||||
|
||||
class ManualMixin:
|
||||
@@ -54,7 +54,7 @@ class ManualMixin:
|
||||
account.uid = uid
|
||||
account.points = points
|
||||
update_account_profile_from_cookie(account, cookie)
|
||||
account.updated_at = datetime.now(timezone.utc)
|
||||
account.updated_at = datetime.now(UTC)
|
||||
return {"points": points, "points_query": result}
|
||||
|
||||
def _wait_points_after_payment(
|
||||
@@ -121,7 +121,7 @@ class ManualMixin:
|
||||
manual_type = self._to_int(result.get("manual_type"))
|
||||
manual_score = self._to_int(result.get("manual_score"))
|
||||
account.esports_points = manual_score
|
||||
account.updated_at = datetime.now(timezone.utc)
|
||||
account.updated_at = datetime.now(UTC)
|
||||
return {
|
||||
"esports_manual_type": manual_type,
|
||||
"esports_manual_score": manual_score,
|
||||
@@ -220,7 +220,7 @@ class ManualMixin:
|
||||
room_id=str(config["room_id"]),
|
||||
)
|
||||
account.bind_status = "elite_qr_created"
|
||||
account.updated_at = datetime.now(timezone.utc)
|
||||
account.updated_at = datetime.now(UTC)
|
||||
self._update_task_progress(
|
||||
db, task, "running", "精英宝典支付码已生成,等待开通到账", result
|
||||
)
|
||||
@@ -232,7 +232,7 @@ class ManualMixin:
|
||||
return
|
||||
if opened:
|
||||
account.bind_status = "elite_opened"
|
||||
account.updated_at = datetime.now(timezone.utc)
|
||||
account.updated_at = datetime.now(UTC)
|
||||
self._mark_task(
|
||||
db, task, "success", f"精英宝典已开通,积分: {account.points}", result
|
||||
)
|
||||
@@ -281,7 +281,7 @@ class ManualMixin:
|
||||
|
||||
if baseline_manual_type is not None and baseline_manual_type >= 1:
|
||||
account.esports_bind_status = "esports_opened"
|
||||
account.updated_at = datetime.now(timezone.utc)
|
||||
account.updated_at = datetime.now(UTC)
|
||||
self._mark_task(
|
||||
db,
|
||||
task,
|
||||
@@ -299,7 +299,7 @@ class ManualMixin:
|
||||
)
|
||||
result.update(baseline_result)
|
||||
account.esports_bind_status = "esports_qr_created"
|
||||
account.updated_at = datetime.now(timezone.utc)
|
||||
account.updated_at = datetime.now(UTC)
|
||||
self._update_task_progress(
|
||||
db, task, "running", "电竞手册支付码已生成,等待开通到账", result
|
||||
)
|
||||
@@ -318,7 +318,7 @@ class ManualMixin:
|
||||
return
|
||||
if opened:
|
||||
account.esports_bind_status = "esports_opened"
|
||||
account.updated_at = datetime.now(timezone.utc)
|
||||
account.updated_at = datetime.now(UTC)
|
||||
self._mark_task(
|
||||
db,
|
||||
task,
|
||||
@@ -353,7 +353,7 @@ class ManualMixin:
|
||||
client = self._client(cookie)
|
||||
result = self._refresh_esports_handbook(client, account, manual_id=manual_id)
|
||||
account.esports_bind_status = "esports_points_queried"
|
||||
account.updated_at = datetime.now(timezone.utc)
|
||||
account.updated_at = datetime.now(UTC)
|
||||
points = result["esports_points"]
|
||||
self._mark_task(
|
||||
db,
|
||||
@@ -370,7 +370,7 @@ class ManualMixin:
|
||||
ctn = client.acf_ccn(refresh_subscribe=False)
|
||||
result = self._refresh_account_points(client, account, cookie, ctn=ctn)
|
||||
account.bind_status = "points_queried"
|
||||
account.updated_at = datetime.now(timezone.utc)
|
||||
account.updated_at = datetime.now(UTC)
|
||||
points = result["points"]
|
||||
self._mark_task(
|
||||
db,
|
||||
|
||||
@@ -1,16 +1,16 @@
|
||||
"""斗鱼任务执行器:和平小店(由 douyu_runner.py 按功能域拆分)。"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import time
|
||||
from datetime import datetime, timezone
|
||||
from typing import Any, TYPE_CHECKING, cast
|
||||
from datetime import UTC, datetime
|
||||
from typing import TYPE_CHECKING, Any, cast
|
||||
|
||||
from sqlalchemy.orm import Session
|
||||
|
||||
from core.douyu import DouyuActivityClient, DouyuActivityError
|
||||
from ..models import Account, DouyuTask, DouyuXpdGoodsSnapshot
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from .douyu_runner import DouyuBatchRunner
|
||||
from ..models import Account, DouyuTask, DouyuXpdGoodsSnapshot
|
||||
|
||||
DOUYU_XPD_BIND_POLL_SECONDS = 300
|
||||
DOUYU_XPD_BIND_POLL_INTERVAL = 5
|
||||
@@ -72,7 +72,7 @@ class XpdMixin:
|
||||
account.xpd_role_id = str(role.get("role_id") or "") or account.xpd_role_id
|
||||
account.xpd_plat_id = self._to_int(role.get("plat_id"))
|
||||
account.xpd_area_id = area_id
|
||||
account.updated_at = datetime.now(timezone.utc)
|
||||
account.updated_at = datetime.now(UTC)
|
||||
|
||||
def _execute_query_xpd_role(
|
||||
self,
|
||||
@@ -126,7 +126,7 @@ class XpdMixin:
|
||||
return
|
||||
result = client.xpd_bind_qr(act_alias=act_alias)
|
||||
account.xpd_bind_status = "xpd_bind_qr_ready"
|
||||
account.updated_at = datetime.now(timezone.utc)
|
||||
account.updated_at = datetime.now(UTC)
|
||||
db.commit()
|
||||
# 记录绑定前状态:已绑定账号生成二维码后必须等扫码换绑,不能立即成功
|
||||
try:
|
||||
@@ -242,7 +242,7 @@ class XpdMixin:
|
||||
role_name = str(result.get("role_name") or "")
|
||||
if not result.get("bind_role") or not role_name:
|
||||
account.xpd_bind_status = "xpd_not_bound"
|
||||
account.updated_at = datetime.now(timezone.utc)
|
||||
account.updated_at = datetime.now(UTC)
|
||||
db.commit()
|
||||
self._mark_task(
|
||||
db, task, "failed", "尚未检测到小店绑定角色,请先扫码绑定", result
|
||||
@@ -258,12 +258,12 @@ class XpdMixin:
|
||||
)
|
||||
else:
|
||||
account.xpd_game_name = role_name
|
||||
account.updated_at = datetime.now(timezone.utc)
|
||||
account.updated_at = datetime.now(UTC)
|
||||
except Exception:
|
||||
account.xpd_game_name = role_name
|
||||
account.updated_at = datetime.now(timezone.utc)
|
||||
account.updated_at = datetime.now(UTC)
|
||||
account.xpd_bind_status = "xpd_bound"
|
||||
account.updated_at = datetime.now(timezone.utc)
|
||||
account.updated_at = datetime.now(UTC)
|
||||
db.commit()
|
||||
result["xpd_pending_confirm"] = False
|
||||
result["xpd_bound"] = True
|
||||
@@ -427,7 +427,7 @@ class XpdMixin:
|
||||
fragments = result.get("fragments")
|
||||
account.xpd_fragments = fragments
|
||||
account.xpd_bind_status = "xpd_fragments_queried"
|
||||
account.updated_at = datetime.now(timezone.utc)
|
||||
account.updated_at = datetime.now(UTC)
|
||||
db.commit()
|
||||
if fragments is None:
|
||||
self._mark_task(db, task, "failed", "未获取到小店扭蛋碎片数量")
|
||||
@@ -459,7 +459,7 @@ class XpdMixin:
|
||||
act_id=str(config["xpd_act_id"]),
|
||||
)
|
||||
account.xpd_bind_status = "xpd_purchase_records_queried"
|
||||
account.updated_at = datetime.now(timezone.utc)
|
||||
account.updated_at = datetime.now(UTC)
|
||||
total = result.get("total") or len(result.get("records") or [])
|
||||
self._mark_task(
|
||||
db,
|
||||
@@ -561,7 +561,7 @@ class XpdMixin:
|
||||
if pay_type == 5 and result.get("new_balance") is not None:
|
||||
account.xpd_fragments = result["new_balance"]
|
||||
account.xpd_bind_status = "xpd_goods_exchanged"
|
||||
account.updated_at = datetime.now(timezone.utc)
|
||||
account.updated_at = datetime.now(UTC)
|
||||
db.commit()
|
||||
|
||||
currency = "点券" if pay_type == 1 else "扭蛋碎片"
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import uuid
|
||||
from datetime import datetime, timezone
|
||||
from datetime import UTC, datetime
|
||||
|
||||
from sqlalchemy import select
|
||||
from sqlalchemy.orm import Session
|
||||
@@ -13,7 +13,6 @@ from core.douyu.cookie_utils import cookie_value
|
||||
|
||||
from ..models import Account, DouyuConfig, DouyuTask, LoginTask
|
||||
|
||||
|
||||
SUPPORTED_DOUYU_TASK_TYPES = {
|
||||
"get_bind_qr": "获取绑定二维码",
|
||||
"confirm_bind": "确认绑定",
|
||||
@@ -302,7 +301,7 @@ def cleanup_orphan_douyu_tasks(
|
||||
tasks = query.all()
|
||||
if not tasks:
|
||||
return 0
|
||||
now = datetime.now(timezone.utc)
|
||||
now = datetime.now(UTC)
|
||||
for task in tasks:
|
||||
task.status = "stopped"
|
||||
task.message = message
|
||||
@@ -426,7 +425,7 @@ def douyu_task_payload(task: DouyuTask, *, include_detail: bool = False) -> dict
|
||||
if value is None:
|
||||
return None
|
||||
if value.tzinfo is None:
|
||||
value = value.replace(tzinfo=timezone.utc)
|
||||
value = value.replace(tzinfo=UTC)
|
||||
return value.isoformat()
|
||||
|
||||
return {
|
||||
|
||||
@@ -6,8 +6,7 @@ import threading
|
||||
import uuid
|
||||
from concurrent.futures import ThreadPoolExecutor, as_completed
|
||||
from dataclasses import dataclass, field
|
||||
from datetime import datetime, timezone
|
||||
from typing import Optional
|
||||
from datetime import UTC, datetime
|
||||
|
||||
from core.douyu.proxy_fetcher import ProxyFetcher
|
||||
from core.huya.auto_register import HuyaAutoRegisterResult, register_huya_with_sms_line
|
||||
@@ -17,13 +16,18 @@ from core.sms_provider import SmsLine
|
||||
from ..database import SessionLocal
|
||||
from ..models import (
|
||||
HuyaRegisterBatch as HuyaRegisterBatchModel,
|
||||
)
|
||||
from ..models import (
|
||||
HuyaRegisterItem as HuyaRegisterItemModel,
|
||||
)
|
||||
from ..models import (
|
||||
HuyaRegisterSuccessLog,
|
||||
)
|
||||
from ..models import (
|
||||
ProxyConfig as ProxyConfigModel,
|
||||
)
|
||||
from .huya_service import upsert_huya_cookie
|
||||
|
||||
|
||||
# 运行中状态:服务中断后视为未完成,可继续
|
||||
RUNNING_ITEM_STATUSES = frozenset({"sending", "waiting", "changing", "logging"})
|
||||
# 续跑(continue):只跑「还没跑完」的,不重试已明确失败(error)、不重跑成功(success)
|
||||
@@ -37,7 +41,7 @@ TERMINAL_BATCH_STATUSES = frozenset({"finished", "stopped", "error", "interrupte
|
||||
|
||||
|
||||
def _now() -> datetime:
|
||||
return datetime.now(timezone.utc)
|
||||
return datetime.now(UTC)
|
||||
|
||||
|
||||
def _cookie_preview(cookie: str) -> str:
|
||||
@@ -409,7 +413,7 @@ class HuyaRegisterRunner:
|
||||
self,
|
||||
batch: HuyaRegisterBatch,
|
||||
sms_lines: list[SmsLine],
|
||||
proxy_config: Optional[ProxyConfigModel] = None,
|
||||
proxy_config: ProxyConfigModel | None = None,
|
||||
*,
|
||||
item_indices: list[int] | None = None,
|
||||
):
|
||||
@@ -812,7 +816,7 @@ class HuyaRegisterRegistry:
|
||||
password_prefix: str = "hy",
|
||||
fixed_password: str = "",
|
||||
use_proxy: bool = False,
|
||||
proxy_config: Optional[ProxyConfigModel] = None,
|
||||
proxy_config: ProxyConfigModel | None = None,
|
||||
) -> HuyaRegisterRunner:
|
||||
batch_id = uuid.uuid4().hex[:12]
|
||||
concurrency = max(1, min(int(concurrency or 1), 5))
|
||||
@@ -918,7 +922,7 @@ class HuyaRegisterRegistry:
|
||||
def retry(
|
||||
self,
|
||||
batch_id: str,
|
||||
proxy_config: Optional[ProxyConfigModel] = None,
|
||||
proxy_config: ProxyConfigModel | None = None,
|
||||
*,
|
||||
mode: str = "continue",
|
||||
concurrency: int | None = None,
|
||||
|
||||
@@ -3,19 +3,22 @@
|
||||
from __future__ import annotations
|
||||
|
||||
from concurrent.futures import ThreadPoolExecutor, as_completed
|
||||
from datetime import UTC, datetime
|
||||
|
||||
from sqlalchemy.orm import joinedload
|
||||
|
||||
from ..database import SessionLocal
|
||||
from datetime import datetime, timezone
|
||||
|
||||
from ..models import HuyaAccount, HuyaTask
|
||||
from core.huya.cookie_utils import normalize_huya_cookie
|
||||
from .huya_service import HUYA_CONFIG_FIELDS, ensure_huya_config, huya_config_value
|
||||
from .huya_runner_core import HuyaBatchRunnerCore, huya_batch_registry # noqa: F401 (huya_batch_registry 供 routers 重导出)
|
||||
|
||||
from ..database import SessionLocal
|
||||
from ..models import HuyaAccount, HuyaTask
|
||||
from .huya_runner_bind import BindMixin
|
||||
from .huya_runner_core import ( # noqa: F401 (huya_batch_registry 供 routers 重导出)
|
||||
HuyaBatchRunnerCore,
|
||||
huya_batch_registry,
|
||||
)
|
||||
from .huya_runner_goods import GoodsMixin
|
||||
from .huya_runner_recharge import RechargeMixin
|
||||
from .huya_service import HUYA_CONFIG_FIELDS, ensure_huya_config, huya_config_value
|
||||
|
||||
|
||||
class HuyaBatchRunner(
|
||||
@@ -148,7 +151,7 @@ class HuyaBatchRunner(
|
||||
if not account:
|
||||
task.status = "error"
|
||||
task.message = "账号不存在"
|
||||
task.finished_at = datetime.now(timezone.utc)
|
||||
task.finished_at = datetime.now(UTC)
|
||||
continue
|
||||
task.status = "pending"
|
||||
task.message = "等待执行"
|
||||
|
||||
@@ -1,20 +1,17 @@
|
||||
"""虎牙任务执行器:角色绑定(由 huya_runner.py 按功能域拆分)。"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import time
|
||||
from datetime import datetime, timezone
|
||||
from typing import Any
|
||||
from datetime import UTC, datetime
|
||||
from typing import TYPE_CHECKING, Any
|
||||
|
||||
from sqlalchemy.orm import Session
|
||||
|
||||
from core.huya import HuyaHttpClient
|
||||
|
||||
from ..models import HuyaAccount, HuyaTask
|
||||
|
||||
from typing import TYPE_CHECKING
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from .huya_runner import HuyaBatchRunner
|
||||
|
||||
HUYA_BIND_ROLE_POLL_SECONDS = 180
|
||||
HUYA_BIND_ROLE_POLL_INTERVAL = 3
|
||||
HUYA_BIND_ZT_UUID = "b02faae1"
|
||||
@@ -77,7 +74,7 @@ class BindMixin:
|
||||
account_data = bind_status.accountData
|
||||
is_bound = bool(account_data.isBindAcount and account_data.isBindRole)
|
||||
change_time = int(account_data.changBindTime or 0)
|
||||
now = int(datetime.now(timezone.utc).timestamp())
|
||||
now = int(datetime.now(UTC).timestamp())
|
||||
can_change = not is_bound or not change_time or change_time <= now
|
||||
return {
|
||||
"is_bound": is_bound,
|
||||
@@ -150,7 +147,7 @@ class BindMixin:
|
||||
account.status = status
|
||||
account.game_name = role_name or account.game_name
|
||||
account.game_channel = self._role_channel(bind_status) or account.game_channel
|
||||
account.updated_at = datetime.now(timezone.utc)
|
||||
account.updated_at = datetime.now(UTC)
|
||||
|
||||
@staticmethod
|
||||
def _bind_redirect_url(config_info: dict) -> str:
|
||||
@@ -439,7 +436,7 @@ class BindMixin:
|
||||
account.game_name = role_info["role_name"] or account.game_name
|
||||
account.game_channel = self._role_channel(bind_status) or account.game_channel
|
||||
account.nickname = profile_nick or account.nickname
|
||||
account.updated_at = datetime.now(timezone.utc)
|
||||
account.updated_at = datetime.now(UTC)
|
||||
|
||||
# 生成二维码后自动轮询扫码/角色,避免用户必须手动点「查询角色」。
|
||||
role_name, result = self._wait_bind_role_result(
|
||||
@@ -545,7 +542,7 @@ class BindMixin:
|
||||
return
|
||||
|
||||
account.status = "game_not_bound"
|
||||
account.updated_at = datetime.now(timezone.utc)
|
||||
account.updated_at = datetime.now(UTC)
|
||||
self._mark_task(worker_db, task, "success", "未绑定游戏角色", result)
|
||||
|
||||
def _execute_confirm_bind(
|
||||
|
||||
@@ -5,11 +5,11 @@ from __future__ import annotations
|
||||
import asyncio
|
||||
import threading
|
||||
import time
|
||||
from datetime import datetime, timezone
|
||||
from typing import Optional
|
||||
from datetime import UTC, datetime
|
||||
|
||||
from loguru import logger
|
||||
from sqlalchemy.orm import Session
|
||||
|
||||
from ..models import HuyaTask
|
||||
from .huya_service import cookie_value
|
||||
|
||||
@@ -29,9 +29,9 @@ class HuyaBatchRunnerCore:
|
||||
db: Session,
|
||||
batch_id: str,
|
||||
task_type: str,
|
||||
payload: Optional[dict] = None,
|
||||
log_queue: Optional[asyncio.Queue] = None,
|
||||
loop: Optional[asyncio.AbstractEventLoop] = None,
|
||||
payload: dict | None = None,
|
||||
log_queue: asyncio.Queue | None = None,
|
||||
loop: asyncio.AbstractEventLoop | None = None,
|
||||
concurrency: int = 3,
|
||||
):
|
||||
self.db = db
|
||||
@@ -123,12 +123,12 @@ class HuyaBatchRunnerCore:
|
||||
task: HuyaTask,
|
||||
status: str,
|
||||
message: str,
|
||||
result: Optional[dict] = None,
|
||||
result: dict | None = None,
|
||||
):
|
||||
task.status = status
|
||||
task.message = message
|
||||
task.result = result
|
||||
task.finished_at = datetime.now(timezone.utc)
|
||||
task.finished_at = datetime.now(UTC)
|
||||
worker_db.commit()
|
||||
|
||||
def _update_task_progress(
|
||||
@@ -137,7 +137,7 @@ class HuyaBatchRunnerCore:
|
||||
task: HuyaTask,
|
||||
status: str,
|
||||
message: str,
|
||||
result: Optional[dict] = None,
|
||||
result: dict | None = None,
|
||||
):
|
||||
task.status = status
|
||||
task.message = message
|
||||
|
||||
@@ -1,17 +1,16 @@
|
||||
"""虎牙任务执行器:积分与商城(由 huya_runner.py 按功能域拆分)。"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import time
|
||||
from datetime import datetime, timezone
|
||||
from typing import Any, TYPE_CHECKING
|
||||
from datetime import UTC, datetime
|
||||
from typing import TYPE_CHECKING, Any
|
||||
|
||||
from sqlalchemy.orm import Session
|
||||
|
||||
from core.huya import HuyaHttpClient
|
||||
from ..models import HuyaAccount, HuyaGoodsSnapshot, HuyaTask
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from .huya_runner import HuyaBatchRunner
|
||||
from ..models import HuyaAccount, HuyaGoodsSnapshot, HuyaTask
|
||||
|
||||
|
||||
class GoodsMixin:
|
||||
@@ -83,7 +82,7 @@ class GoodsMixin:
|
||||
points = response.available_score
|
||||
account.points = points
|
||||
account.status = "points_queried"
|
||||
account.updated_at = datetime.now(timezone.utc)
|
||||
account.updated_at = datetime.now(UTC)
|
||||
self._mark_task(worker_db, task, "success", f"积分: {points}", result)
|
||||
|
||||
def _execute_query_exchange_records(
|
||||
@@ -144,7 +143,7 @@ class GoodsMixin:
|
||||
item["score_text"] = f"{int(item.get('score') or 0)}积分"
|
||||
|
||||
account.status = "exchange_records_queried"
|
||||
account.updated_at = datetime.now(timezone.utc)
|
||||
account.updated_at = datetime.now(UTC)
|
||||
count = len(records)
|
||||
message = f"兑换记录 {count} 条" if count else "暂无兑换记录"
|
||||
self._mark_task(worker_db, task, "success", message, result)
|
||||
@@ -202,7 +201,7 @@ class GoodsMixin:
|
||||
for item in result.get("goods", [])
|
||||
if item.get("product_id") and item.get("name")
|
||||
]
|
||||
now = datetime.now(timezone.utc)
|
||||
now = datetime.now(UTC)
|
||||
worker_db.query(HuyaGoodsSnapshot).delete(synchronize_session=False)
|
||||
for item in goods:
|
||||
worker_db.add(
|
||||
@@ -290,7 +289,7 @@ class GoodsMixin:
|
||||
"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(),
|
||||
"executed_at": datetime.now(UTC).isoformat(),
|
||||
"goods": snapshot.raw if snapshot else None,
|
||||
}
|
||||
)
|
||||
@@ -305,6 +304,6 @@ class GoodsMixin:
|
||||
return
|
||||
|
||||
account.status = "goods_exchanged"
|
||||
account.updated_at = datetime.now(timezone.utc)
|
||||
account.updated_at = datetime.now(UTC)
|
||||
message = response.msg or f"兑换成功: {product_name}"
|
||||
self._mark_task(worker_db, task, "success", message, result)
|
||||
|
||||
@@ -1,13 +1,15 @@
|
||||
"""虎牙任务执行器:充值(由 huya_runner.py 按功能域拆分)。"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import time
|
||||
from datetime import datetime, timezone
|
||||
from typing import Any, TYPE_CHECKING
|
||||
from datetime import UTC, datetime
|
||||
from typing import TYPE_CHECKING, Any
|
||||
|
||||
from sqlalchemy.orm import Session
|
||||
|
||||
from core.huya import HuyaHttpClient
|
||||
|
||||
from ..models import HuyaAccount, HuyaRechargeGoodsSnapshot, HuyaTask
|
||||
|
||||
HUYA_RECHARGE_ACT_ID = 25135
|
||||
@@ -25,9 +27,6 @@ HUYA_RECHARGE_EXTRA_PRODUCTS = [
|
||||
]
|
||||
from .huya_runner_core import HUYA_RECHARGE_SOURCE_ID
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from .huya_runner import HuyaBatchRunner
|
||||
|
||||
|
||||
class RechargeMixin:
|
||||
"""充值域:充值商品、下单与到账轮询。"""
|
||||
@@ -103,7 +102,7 @@ class RechargeMixin:
|
||||
status=0,
|
||||
timeout=10.0,
|
||||
)
|
||||
checked_at = datetime.now(timezone.utc).isoformat()
|
||||
checked_at = datetime.now(UTC).isoformat()
|
||||
if resp is not None and getattr(resp, "orders", None):
|
||||
for order in resp.orders:
|
||||
if str(getattr(order, "orderId", "")) != order_id_text:
|
||||
@@ -248,7 +247,7 @@ class RechargeMixin:
|
||||
)
|
||||
return
|
||||
|
||||
now = datetime.now(timezone.utc)
|
||||
now = datetime.now(UTC)
|
||||
goods: list[dict] = []
|
||||
failed: list[dict] = []
|
||||
|
||||
@@ -501,7 +500,7 @@ class RechargeMixin:
|
||||
"order": order_result,
|
||||
}
|
||||
account.status = "recharge_order_created"
|
||||
account.updated_at = datetime.now(timezone.utc)
|
||||
account.updated_at = datetime.now(UTC)
|
||||
message = f"{product_name} x{count} {self._pay_channel_label(pay_channel)} {result['amount_text']}"
|
||||
self._update_task_progress(
|
||||
worker_db, task, "running", f"{message},等待扫码支付", result
|
||||
@@ -518,7 +517,7 @@ class RechargeMixin:
|
||||
order_id=order_resp.orderId,
|
||||
result=result,
|
||||
)
|
||||
account.updated_at = datetime.now(timezone.utc)
|
||||
account.updated_at = datetime.now(UTC)
|
||||
if payment_status == "paid":
|
||||
account.status = "recharge_paid"
|
||||
paid_message = f"支付成功: {product_name} x{count} {result['amount_text']}"
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
import csv
|
||||
import uuid
|
||||
from dataclasses import dataclass
|
||||
from datetime import datetime, timezone
|
||||
from datetime import UTC, datetime
|
||||
from urllib.parse import urlparse
|
||||
|
||||
from sqlalchemy.orm import Session
|
||||
@@ -13,7 +13,6 @@ from core.huya.cookie_utils import cookie_value, normalize_huya_cookie
|
||||
from ..huya_defaults import HUYA_CONFIG_DEFAULTS, HUYA_CONFIG_FIELDS
|
||||
from ..models import HuyaAccount, HuyaConfig, HuyaTask
|
||||
|
||||
|
||||
SUPPORTED_TASK_TYPES = {
|
||||
"get_bind_qr": "获取绑定二维码",
|
||||
"query_points": "一键查询积分",
|
||||
@@ -204,7 +203,7 @@ def import_huya_password_accounts(
|
||||
account.tag = tag
|
||||
if not account.cookie:
|
||||
account.status = "password_imported"
|
||||
account.updated_at = datetime.now(timezone.utc)
|
||||
account.updated_at = datetime.now(UTC)
|
||||
created_or_updated += 1
|
||||
|
||||
if created_or_updated:
|
||||
@@ -247,7 +246,7 @@ def _upsert_huya_account(
|
||||
if tag:
|
||||
account.tag = tag
|
||||
account.status = status or "updated"
|
||||
account.updated_at = datetime.now(timezone.utc)
|
||||
account.updated_at = datetime.now(UTC)
|
||||
return account
|
||||
|
||||
|
||||
@@ -276,7 +275,7 @@ def save_huya_login_cookie_to_account(
|
||||
if tag:
|
||||
account.tag = tag
|
||||
account.status = "login_success"
|
||||
account.updated_at = datetime.now(timezone.utc)
|
||||
account.updated_at = datetime.now(UTC)
|
||||
db.commit()
|
||||
db.refresh(account)
|
||||
return account
|
||||
@@ -358,7 +357,7 @@ def cleanup_orphan_huya_tasks(
|
||||
if not tasks:
|
||||
return 0
|
||||
|
||||
now = datetime.now(timezone.utc)
|
||||
now = datetime.now(UTC)
|
||||
for task in tasks:
|
||||
task.status = "stopped"
|
||||
task.message = message
|
||||
|
||||
@@ -5,18 +5,21 @@ import os
|
||||
import threading
|
||||
import time
|
||||
import uuid
|
||||
from types import SimpleNamespace
|
||||
from concurrent.futures import ThreadPoolExecutor, as_completed
|
||||
from datetime import datetime, timezone
|
||||
from typing import Optional, cast
|
||||
from datetime import UTC, datetime
|
||||
from types import SimpleNamespace
|
||||
from typing import cast
|
||||
|
||||
from sqlalchemy.orm import Session
|
||||
from loguru import logger
|
||||
from sqlalchemy.orm import Session
|
||||
|
||||
from core.douyu import DouyuLogin, WgapiLoginAPI, IframeLoginAPI
|
||||
from core.douyu import DouyuLogin, IframeLoginAPI, WgapiLoginAPI
|
||||
from core.douyu.login import AccountLike
|
||||
from core.douyu.proxy_fetcher import ProxyFetcher
|
||||
from ..models import Account as AccountModel, LoginTask, ProxyConfig as ProxyConfigModel
|
||||
|
||||
from ..models import Account as AccountModel
|
||||
from ..models import LoginTask
|
||||
from ..models import ProxyConfig as ProxyConfigModel
|
||||
from .cookie_check_service import check_douyu_cookie
|
||||
|
||||
|
||||
@@ -62,8 +65,8 @@ def get_relogin_limits() -> tuple[int, int]:
|
||||
|
||||
|
||||
def _snapshot_proxy_config(
|
||||
proxy_config: Optional[ProxyConfigModel],
|
||||
) -> Optional[ProxyConfigModel]:
|
||||
proxy_config: ProxyConfigModel | None,
|
||||
) -> ProxyConfigModel | None:
|
||||
"""复制代理配置,避免后台线程访问已关闭会话中的 ORM 对象。"""
|
||||
if proxy_config is None:
|
||||
return None
|
||||
@@ -97,13 +100,13 @@ class LoginBatchRunner:
|
||||
creator_permissions: list[str],
|
||||
max_login_retries: int = 0,
|
||||
max_total_time: float = 0,
|
||||
proxy_config: Optional[ProxyConfigModel] = None,
|
||||
log_queue: Optional[asyncio.Queue] = None,
|
||||
loop: Optional[asyncio.AbstractEventLoop] = None,
|
||||
proxy_config: ProxyConfigModel | None = None,
|
||||
log_queue: asyncio.Queue | None = None,
|
||||
loop: asyncio.AbstractEventLoop | None = None,
|
||||
concurrency: int = 3,
|
||||
api_strategy: str = "wgapi",
|
||||
mode: str = "login",
|
||||
relogin_task_ids: Optional[list[int]] = None,
|
||||
relogin_task_ids: list[int] | None = None,
|
||||
):
|
||||
self.db = db
|
||||
self.account_ids = account_ids
|
||||
@@ -179,7 +182,7 @@ class LoginBatchRunner:
|
||||
if task and task.status in ("relogin_pending", "relogin_running"):
|
||||
task.status = "relogin_failed"
|
||||
task.message = message
|
||||
task.finished_at = datetime.now(timezone.utc)
|
||||
task.finished_at = datetime.now(UTC)
|
||||
worker_db.commit()
|
||||
finally:
|
||||
worker_db.close()
|
||||
@@ -199,7 +202,7 @@ class LoginBatchRunner:
|
||||
self.loop,
|
||||
)
|
||||
|
||||
def _resolve_static_proxy(self) -> tuple[Optional[dict], str]:
|
||||
def _resolve_static_proxy(self) -> tuple[dict | None, str]:
|
||||
"""解析静态代理配置。"""
|
||||
if not self.proxy_config or not self.proxy_config.enabled:
|
||||
return None, ""
|
||||
@@ -270,7 +273,7 @@ class LoginBatchRunner:
|
||||
else:
|
||||
task.status = "error"
|
||||
task.message = "代理不可用: 未配置代理"
|
||||
task.finished_at = datetime.now(timezone.utc)
|
||||
task.finished_at = datetime.now(UTC)
|
||||
worker_db.commit()
|
||||
self._push_log(
|
||||
"error", f"[{current}] {acc_info['username']} 代理不可用"
|
||||
@@ -384,7 +387,7 @@ class LoginBatchRunner:
|
||||
f"[{current}] {acc_info['username']} {action_name}异常: {e}",
|
||||
)
|
||||
|
||||
task.finished_at = datetime.now(timezone.utc)
|
||||
task.finished_at = datetime.now(UTC)
|
||||
worker_db.commit()
|
||||
|
||||
finally:
|
||||
@@ -581,10 +584,10 @@ class BatchRegistry:
|
||||
def register(
|
||||
self,
|
||||
batch_id: str,
|
||||
log_queue: Optional[asyncio.Queue],
|
||||
loop: Optional[asyncio.AbstractEventLoop],
|
||||
log_queue: asyncio.Queue | None,
|
||||
loop: asyncio.AbstractEventLoop | None,
|
||||
runner: LoginBatchRunner,
|
||||
owner_id: Optional[int] = None,
|
||||
owner_id: int | None = None,
|
||||
):
|
||||
self._batches[batch_id] = {
|
||||
"log_queue": log_queue,
|
||||
@@ -630,7 +633,7 @@ def cleanup_orphan_relogin_tasks(
|
||||
if not tasks:
|
||||
return 0
|
||||
|
||||
finished_at = datetime.now(timezone.utc)
|
||||
finished_at = datetime.now(UTC)
|
||||
for task in tasks:
|
||||
task.status = "relogin_failed"
|
||||
task.message = message
|
||||
|
||||
@@ -1,21 +1,21 @@
|
||||
"""代理 & 白名单服务层:封装 core/ 代理/白名单逻辑,供路由调用。"""
|
||||
|
||||
import asyncio
|
||||
import re
|
||||
import threading
|
||||
import uuid
|
||||
from typing import Optional
|
||||
|
||||
from sqlalchemy.orm import Session
|
||||
|
||||
from core.douyu.proxy import (
|
||||
parse_proxy_response,
|
||||
resolve_working_proxy,
|
||||
verify_proxy_url,
|
||||
parse_proxy_response,
|
||||
)
|
||||
from core.douyu.proxy_platforms import create_adapter, get_platform_labels
|
||||
from core.douyu.proxy_platforms import create_adapter
|
||||
from core.douyu.proxy_platforms.base import _get_local_exit_ip
|
||||
from ..models import ProxyConfig as ProxyConfigModel, AuditLog
|
||||
|
||||
from ..models import AuditLog
|
||||
from ..models import ProxyConfig as ProxyConfigModel
|
||||
|
||||
|
||||
def _build_whitelist_params(cfg: ProxyConfigModel) -> dict:
|
||||
@@ -128,7 +128,7 @@ class ProxyService:
|
||||
self._active_tests[test_id] = {"log_queue": log_queue, "loop": loop}
|
||||
return test_id, log_queue, loop
|
||||
|
||||
def get_test(self, test_id: str) -> Optional[dict]:
|
||||
def get_test(self, test_id: str) -> dict | None:
|
||||
"""获取测试任务信息。"""
|
||||
with self._active_tests_lock:
|
||||
return self._active_tests.get(test_id)
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from datetime import datetime, timezone
|
||||
from datetime import UTC, datetime
|
||||
from typing import Any
|
||||
|
||||
from sqlalchemy.orm import Session
|
||||
@@ -12,12 +12,12 @@ from .yyb_worker_client import YybWorkerClient
|
||||
|
||||
|
||||
def _utcnow():
|
||||
return datetime.now(timezone.utc)
|
||||
return datetime.now(UTC)
|
||||
|
||||
|
||||
def _from_ts(value) -> datetime | None:
|
||||
try:
|
||||
return datetime.fromtimestamp(int(value), tz=timezone.utc) if value else None
|
||||
return datetime.fromtimestamp(int(value), tz=UTC) if value else None
|
||||
except (TypeError, ValueError, OSError):
|
||||
return None
|
||||
|
||||
@@ -27,8 +27,8 @@ def _as_utc(value: datetime | None) -> datetime | None:
|
||||
if value is None:
|
||||
return None
|
||||
if value.tzinfo is None:
|
||||
return value.replace(tzinfo=timezone.utc)
|
||||
return value.astimezone(timezone.utc)
|
||||
return value.replace(tzinfo=UTC)
|
||||
return value.astimezone(UTC)
|
||||
|
||||
|
||||
def cleanup_orphan_yyb_tasks(db: Session, message: str) -> int:
|
||||
|
||||
Reference in New Issue
Block a user