Merge branch 'fix/pyright-env-and-type-guards' into dev
This commit is contained in:
@@ -10,6 +10,7 @@ from __future__ import annotations
|
||||
|
||||
import sqlalchemy as sa
|
||||
from alembic import op
|
||||
|
||||
from web.backend.services.login_failure import classify_failure
|
||||
|
||||
revision: str = "20261008_0052"
|
||||
|
||||
@@ -57,7 +57,6 @@ from ..services.account_lifecycle import (
|
||||
set_manual_state,
|
||||
)
|
||||
from ..services.account_service import (
|
||||
cookie_account_ids_query,
|
||||
parse_and_build_accounts,
|
||||
)
|
||||
from ..services.account_value import (
|
||||
|
||||
@@ -58,10 +58,12 @@ def _audit_log_out(db: Session, row: AuditLog) -> dict:
|
||||
for task, account in tasks_and_accounts:
|
||||
task_result = task.result if isinstance(task.result, dict) else {}
|
||||
channel = str(task_result.get("recharge_channel") or "")
|
||||
# 先落到局部变量再判 isinstance:直接写
|
||||
# `x.get(k) if isinstance(x.get(k), dict) else {}` 是两次独立取值,
|
||||
# 类型检查器无法据此收窄,会报「可能为 None」。
|
||||
raw_supplier_order = task_result.get("supplier_order")
|
||||
supplier_order = (
|
||||
task_result.get("supplier_order")
|
||||
if isinstance(task_result.get("supplier_order"), dict)
|
||||
else {}
|
||||
raw_supplier_order if isinstance(raw_supplier_order, dict) else {}
|
||||
)
|
||||
supplier_order_id = str(
|
||||
supplier_order.get("order_id")
|
||||
|
||||
@@ -544,4 +544,16 @@ def _unbind_device_accounts(accounts: list[str]) -> tuple[list[str], list[str]]:
|
||||
return removed, missing
|
||||
|
||||
|
||||
__all__ = [name for name in globals() if not name.startswith("__")]
|
||||
# 运行期导出清单:显式列出「非 dunder」的全部模块级名字(**含下划线开头的内部
|
||||
# helper**),因为 python 的 `import *` 只要模块定义了 `__all__` 就照单全收。
|
||||
#
|
||||
# ⚠️ 六个 huya 路由模块(accounts/cookies/register/workbench/login/devices)原本
|
||||
# 用 `from ._common import *`,全部已改为**显式导入**。原因不是风格洁癖:pyright
|
||||
# 不会执行上面的推导式,只能按 PEP 8 保守假设「下划线名不导出」,于是那 108 处
|
||||
# helper 调用全被报成「未定义」—— 更糟的是,它让这 6 个模块的类型检查彻底失明
|
||||
# (helper 改名/删除时静态检查一声不吭,只能等运行期 500;曾因此漏掉一个
|
||||
# `ImportError` 死端点)。新增代码请一律用显式导入。
|
||||
#
|
||||
# 这里保留计算式 `__all__` 是为了不改动任何运行期行为:万一还有别处 `import *`,
|
||||
# 行为与改造前完全一致。pyright 无法静态求值它,故就地忽略该诊断。
|
||||
__all__ = [name for name in globals() if not name.startswith("__")] # pyright: ignore[reportUnsupportedDunderAll]
|
||||
|
||||
@@ -2,7 +2,53 @@
|
||||
|
||||
from fastapi import APIRouter
|
||||
|
||||
from ._common import *
|
||||
from ._common import (
|
||||
DEFAULT_EXCHANGE_TARGET,
|
||||
AccountAssign,
|
||||
AccountBulkSelection,
|
||||
AccountBulkTag,
|
||||
AccountTag,
|
||||
BatchAssign,
|
||||
Depends,
|
||||
HTTPException,
|
||||
HuyaAccount,
|
||||
HuyaBrowserExchangeRequest,
|
||||
HuyaCookieImport,
|
||||
HuyaPasswordAccountImport,
|
||||
HuyaTask,
|
||||
HuyaWorkbenchAccount,
|
||||
Query,
|
||||
Session,
|
||||
User,
|
||||
_account_out,
|
||||
_account_out_light,
|
||||
_can_view_huya_all,
|
||||
_can_view_huya_assigned,
|
||||
_can_view_huya_cookie,
|
||||
_clear_huya_account_references,
|
||||
_filter_huya_accounts_query,
|
||||
_huya_cookie_state_map,
|
||||
_huya_password_state_map,
|
||||
_require_huya_perm,
|
||||
_require_huya_tag_permission,
|
||||
_selected_huya_account_ids,
|
||||
_visible_huya_accounts_query,
|
||||
browser_login_script,
|
||||
cookie_pairs,
|
||||
defer,
|
||||
ensure_huya_config,
|
||||
exchange_target_options,
|
||||
exchange_target_url,
|
||||
func,
|
||||
get_current_user,
|
||||
get_db,
|
||||
huya_config_value,
|
||||
import_huya_cookies,
|
||||
import_huya_password_accounts,
|
||||
normalize_huya_cookie,
|
||||
or_,
|
||||
record_audit,
|
||||
)
|
||||
|
||||
router = APIRouter()
|
||||
|
||||
|
||||
@@ -2,7 +2,30 @@
|
||||
|
||||
from fastapi import APIRouter
|
||||
|
||||
from ._common import *
|
||||
from ._common import (
|
||||
Depends,
|
||||
HTTPException,
|
||||
HuyaAccount,
|
||||
Query,
|
||||
Session,
|
||||
StreamingResponse,
|
||||
User,
|
||||
_can_view_huya_cookie,
|
||||
_cookie_out,
|
||||
_cookie_out_light,
|
||||
_has_huya_perm,
|
||||
_require_huya_perm,
|
||||
_visible_huya_accounts_query,
|
||||
csv,
|
||||
defer,
|
||||
get_current_user,
|
||||
get_db,
|
||||
io,
|
||||
normalize_huya_cookie,
|
||||
or_,
|
||||
rebuild_web_cookie_from_raw,
|
||||
record_audit,
|
||||
)
|
||||
|
||||
router = APIRouter()
|
||||
|
||||
|
||||
@@ -2,7 +2,25 @@
|
||||
|
||||
from fastapi import APIRouter
|
||||
|
||||
from ._common import *
|
||||
from ._common import (
|
||||
_DEVICE_BINDINGS_PERMS_VIEW,
|
||||
Depends,
|
||||
HTTPException,
|
||||
HuyaAccount,
|
||||
HuyaDeviceBindingsBatchDeleteRequest,
|
||||
Session,
|
||||
User,
|
||||
_fp_state_dir,
|
||||
_load_profile_db,
|
||||
_mask_hex,
|
||||
_require_huya_perm,
|
||||
_save_profile_db,
|
||||
_unbind_device_accounts,
|
||||
get_current_user,
|
||||
get_db,
|
||||
shutil,
|
||||
user_has_permission,
|
||||
)
|
||||
|
||||
router = APIRouter()
|
||||
|
||||
|
||||
@@ -4,7 +4,41 @@ import time
|
||||
|
||||
from fastapi import APIRouter
|
||||
|
||||
from ._common import *
|
||||
from ._common import (
|
||||
UTC,
|
||||
AccountBulkSelection,
|
||||
Depends,
|
||||
HTTPException,
|
||||
HuyaAccount,
|
||||
HuyaAppLoginError,
|
||||
HuyaAppPasswordLoginRequest,
|
||||
HuyaAppQrAuthRequiredError,
|
||||
HuyaCredentialError,
|
||||
HuyaLoginError,
|
||||
HuyaPasswordLoginRequest,
|
||||
HuyaPasswordLoginSelectedRequest,
|
||||
HuyaSmsCodeRequest,
|
||||
HuyaSmsLoginRequest,
|
||||
HuyaTestSmsUrlRequest,
|
||||
Session,
|
||||
User,
|
||||
_account_out,
|
||||
_can_view_huya_cookie,
|
||||
_require_huya_perm,
|
||||
_selected_huya_account_ids,
|
||||
_visible_huya_accounts_query,
|
||||
cast,
|
||||
datetime,
|
||||
get_current_user,
|
||||
get_db,
|
||||
logger,
|
||||
login_huya_app_password,
|
||||
login_huya_password,
|
||||
login_huya_sms,
|
||||
save_huya_login_cookie_to_account,
|
||||
send_huya_sms_code,
|
||||
upsert_huya_cookie,
|
||||
)
|
||||
|
||||
router = APIRouter()
|
||||
|
||||
@@ -287,7 +321,12 @@ def auto_sms_login_account(
|
||||
sms_url = (account.sms_url or "").strip()
|
||||
if not phone or not sms_url:
|
||||
raise HTTPException(status_code=400, detail="该账号缺少手机号或接码链接")
|
||||
from core.huya.auto_register import auto_register_huya_phone
|
||||
# ⚠️ 这里原来是 `auto_register_huya_phone` —— 该名字在 core.huya.auto_register
|
||||
# 里**从来没存在过**(2026-09-06 起就是死的),每次调用都在 import 阶段抛
|
||||
# ImportError → 500。真实函数名是 register_huya_with_sms_line,返回的
|
||||
# HuyaAutoRegisterResult 同样有 .success / .cookie / .message。
|
||||
# 回归用例:tests/test_huya_auto_sms_login.py
|
||||
from core.huya.auto_register import register_huya_with_sms_line
|
||||
from core.sms_provider import SmsLine, detect_provider
|
||||
|
||||
item = SmsLine(
|
||||
@@ -296,7 +335,7 @@ def auto_sms_login_account(
|
||||
provider=detect_provider(sms_url),
|
||||
raw=f"{phone}----{sms_url}",
|
||||
)
|
||||
res = auto_register_huya_phone(item, wait_seconds=90, poll_interval=2)
|
||||
res = register_huya_with_sms_line(item, wait_seconds=90, poll_interval=2)
|
||||
if not res.success or not res.cookie:
|
||||
account.status = "login_failed"
|
||||
account.updated_at = datetime.now(UTC)
|
||||
|
||||
@@ -2,7 +2,27 @@
|
||||
|
||||
from fastapi import APIRouter
|
||||
|
||||
from ._common import *
|
||||
from ._common import (
|
||||
Depends,
|
||||
HTTPException,
|
||||
HuyaAutoRegisterBatchOut,
|
||||
HuyaAutoRegisterRequest,
|
||||
HuyaAutoRegisterRetryRequest,
|
||||
HuyaRegisterSuccessLogOut,
|
||||
ProxyConfig,
|
||||
Query,
|
||||
Session,
|
||||
StreamingResponse,
|
||||
User,
|
||||
_require_huya_perm,
|
||||
export_success_logs_text,
|
||||
get_current_user,
|
||||
get_db,
|
||||
huya_register_registry,
|
||||
list_success_logs,
|
||||
parse_sms_lines,
|
||||
threading,
|
||||
)
|
||||
|
||||
router = APIRouter()
|
||||
|
||||
|
||||
@@ -2,7 +2,52 @@
|
||||
|
||||
from fastapi import APIRouter
|
||||
|
||||
from ._common import *
|
||||
from ._common import (
|
||||
HUYA_CONFIG_FIELDS,
|
||||
SUPPORTED_TASK_TYPES,
|
||||
UTC,
|
||||
Depends,
|
||||
HTTPException,
|
||||
HuyaBatchRunner,
|
||||
HuyaConfigOut,
|
||||
HuyaConfigUpdate,
|
||||
HuyaGoodsOut,
|
||||
HuyaGoodsSnapshot,
|
||||
HuyaRechargeGoodsOut,
|
||||
HuyaRechargeGoodsSnapshot,
|
||||
HuyaTask,
|
||||
HuyaTaskBatchRequest,
|
||||
HuyaTaskOut,
|
||||
HuyaWorkbench,
|
||||
HuyaWorkbenchAccount,
|
||||
HuyaWorkbenchAccountsUpdate,
|
||||
Query,
|
||||
Session,
|
||||
SessionLocal,
|
||||
User,
|
||||
WebSocket,
|
||||
WebSocketDisconnect,
|
||||
_config_out,
|
||||
_has_huya_perm,
|
||||
_huya_task_summary,
|
||||
_require_huya_batch_owner,
|
||||
_require_huya_task_account_access,
|
||||
_task_out,
|
||||
_visible_huya_tasks_query,
|
||||
_workbench_account_ids,
|
||||
apply_huya_config_defaults,
|
||||
asyncio,
|
||||
authenticate_websocket,
|
||||
cleanup_orphan_huya_tasks,
|
||||
create_planned_tasks,
|
||||
datetime,
|
||||
ensure_huya_config,
|
||||
get_db,
|
||||
huya_batch_registry,
|
||||
record_audit,
|
||||
require_permission,
|
||||
threading,
|
||||
)
|
||||
|
||||
router = APIRouter()
|
||||
|
||||
|
||||
+31
-31
@@ -20,7 +20,7 @@ class YybTaskCreateRequest(BaseModel):
|
||||
|
||||
class YybLoginRequest(BaseModel):
|
||||
provider: str = Field(..., pattern="^(qq|wechat)$")
|
||||
timeout: int = Field(600, ge=60, le=1800)
|
||||
timeout: int = Field(default=600, ge=60, le=1800)
|
||||
|
||||
|
||||
class YybSelectionRequest(BaseModel):
|
||||
@@ -28,7 +28,7 @@ class YybSelectionRequest(BaseModel):
|
||||
points: int = Field(..., gt=0)
|
||||
product_id: str = Field(..., min_length=1, max_length=128)
|
||||
zone_id: str = Field(..., min_length=1, max_length=64)
|
||||
zone_name: str = Field("", max_length=128)
|
||||
zone_name: str = Field(default="", max_length=128)
|
||||
role_id: str = Field(..., min_length=1, max_length=64)
|
||||
role_name: str = Field(..., min_length=1, max_length=128)
|
||||
|
||||
@@ -91,7 +91,7 @@ class UserInfo(BaseModel):
|
||||
class UserCreate(BaseModel):
|
||||
username: str = Field(..., min_length=2, max_length=64)
|
||||
password: str = Field(..., min_length=6, max_length=128)
|
||||
role: str = Field("support", pattern="^(super_admin|operation|support)$")
|
||||
role: str = Field(default="support", pattern="^(super_admin|operation|support)$")
|
||||
remark: str = ""
|
||||
|
||||
|
||||
@@ -153,7 +153,7 @@ class AccountBulkSelection(BaseModel):
|
||||
class AccountExportRequest(AccountBulkSelection):
|
||||
"""账号导出请求:支持纯账号名单(txt)或结构化明细(csv)。"""
|
||||
|
||||
format: str = Field("txt", pattern="^(txt|csv)$")
|
||||
format: str = Field(default="txt", pattern="^(txt|csv)$")
|
||||
|
||||
|
||||
class BatchAssign(AccountBulkSelection):
|
||||
@@ -295,7 +295,7 @@ class LoginBatchRequest(BaseModel):
|
||||
concurrency: int = 3 # 并发数,1-10
|
||||
api_strategy: str = "wgapi" # 接口策略: wgapi(新版)或 iframe(旧版备选)
|
||||
mode: str = Field(
|
||||
"login", pattern="^(login|check)$"
|
||||
default="login", pattern="^(login|check)$"
|
||||
) # login=登录取CK,check=账号状态检测
|
||||
|
||||
|
||||
@@ -352,9 +352,9 @@ class AccountCheckBatchRequest(BaseModel):
|
||||
status: str = ""
|
||||
ck_status: str = ""
|
||||
bind_status: str = ""
|
||||
concurrency: int = Field(3, ge=1, le=10)
|
||||
max_login_retries: int = Field(0, ge=0, le=50)
|
||||
max_total_time: float = Field(300, ge=0, le=3600)
|
||||
concurrency: int = Field(default=3, ge=1, le=10)
|
||||
max_login_retries: int = Field(default=0, ge=0, le=50)
|
||||
max_total_time: float = Field(default=300, ge=0, le=3600)
|
||||
use_proxy: bool = False
|
||||
# 登录接口策略,与登录任务同一套取值:wgapi(默认,保持原行为)或 iframe。
|
||||
# 有些人只有其中一套能收到邮箱验证码,所以要能选。
|
||||
@@ -446,11 +446,11 @@ class HuyaAutoRegisterRequest(BaseModel):
|
||||
|
||||
text: str = Field(..., min_length=1)
|
||||
tag: str = ""
|
||||
concurrency: int = Field(1, ge=1, le=5)
|
||||
wait_seconds: float = Field(180, ge=15, le=600)
|
||||
poll_interval: float = Field(5, ge=1, le=30)
|
||||
password_prefix: str = Field("hy", max_length=8)
|
||||
fixed_password: str = Field("", max_length=64)
|
||||
concurrency: int = Field(default=1, ge=1, le=5)
|
||||
wait_seconds: float = Field(default=180, ge=15, le=600)
|
||||
poll_interval: float = Field(default=5, ge=1, le=30)
|
||||
password_prefix: str = Field(default="hy", max_length=8)
|
||||
fixed_password: str = Field(default="", max_length=64)
|
||||
use_proxy: bool = False
|
||||
|
||||
|
||||
@@ -463,12 +463,12 @@ class HuyaAutoRegisterRetryRequest(BaseModel):
|
||||
- all_unfinished: 失败+未完成都跑
|
||||
"""
|
||||
|
||||
mode: str = Field("continue", max_length=32)
|
||||
concurrency: int | None = Field(None, ge=1, le=5)
|
||||
wait_seconds: float | None = Field(None, ge=15, le=600)
|
||||
poll_interval: float | None = Field(None, ge=1, le=30)
|
||||
password_prefix: str | None = Field(None, max_length=8)
|
||||
fixed_password: str | None = Field(None, max_length=64)
|
||||
mode: str = Field(default="continue", max_length=32)
|
||||
concurrency: int | None = Field(default=None, ge=1, le=5)
|
||||
wait_seconds: float | None = Field(default=None, ge=15, le=600)
|
||||
poll_interval: float | None = Field(default=None, ge=1, le=30)
|
||||
password_prefix: str | None = Field(default=None, max_length=8)
|
||||
fixed_password: str | None = Field(default=None, max_length=64)
|
||||
use_proxy: bool | None = None
|
||||
|
||||
|
||||
@@ -550,9 +550,9 @@ class HuyaPasswordLoginSelectedRequest(BaseModel):
|
||||
assigned_only: bool = False
|
||||
has_cookie: bool = False
|
||||
force_new_device: bool = False
|
||||
max_attempts: int = Field(2, ge=1, le=3)
|
||||
interval_seconds: float = Field(8, ge=0, le=60)
|
||||
retry_delay_seconds: float = Field(3, ge=0, le=30)
|
||||
max_attempts: int = Field(default=2, ge=1, le=3)
|
||||
interval_seconds: float = Field(default=8, ge=0, le=60)
|
||||
retry_delay_seconds: float = Field(default=3, ge=0, le=30)
|
||||
|
||||
|
||||
class HuyaDeviceBindingsBatchDeleteRequest(BaseModel):
|
||||
@@ -647,7 +647,7 @@ class HuyaConfigUpdate(BaseModel):
|
||||
class HuyaTaskBatchRequest(BaseModel):
|
||||
account_ids: list[int]
|
||||
task_type: str
|
||||
handbook_scope: str = Field("legacy", pattern="^(legacy|elite)$")
|
||||
handbook_scope: str = Field(default="legacy", pattern="^(legacy|elite)$")
|
||||
concurrency: int = 3
|
||||
payload: dict[str, Any] = Field(default_factory=dict)
|
||||
|
||||
@@ -660,7 +660,7 @@ class HuyaWorkbenchAccountsUpdate(BaseModel):
|
||||
class HuyaBrowserExchangeRequest(BaseModel):
|
||||
"""浏览器兑换的目标页选择(常量见 services/huya_exchange_targets.py)。"""
|
||||
|
||||
target: str | None = Field(None, pattern="^(live_room|elite|esports)$")
|
||||
target: str | None = Field(default=None, pattern="^(live_room|elite|esports)$")
|
||||
|
||||
|
||||
class HuyaTaskOut(BaseModel):
|
||||
@@ -833,10 +833,10 @@ class DouyuConfigUpdate(BaseModel):
|
||||
confirm_act_alias: str | None = None
|
||||
legacy_act_alias: str | None = None
|
||||
room_id: str | None = None
|
||||
elite_amount: int | None = Field(None, ge=1)
|
||||
elite_amount: int | None = Field(default=None, ge=1)
|
||||
esports_manual_id: str | None = None
|
||||
esports_act_alias: str | None = None
|
||||
esports_amount: int | None = Field(None, ge=1)
|
||||
esports_amount: int | None = Field(default=None, ge=1)
|
||||
esports_chicken_gift_id: str | None = None
|
||||
esports_chicken_skin_id: str | None = None
|
||||
esports_firework_gift_id: str | None = None
|
||||
@@ -845,12 +845,12 @@ class DouyuConfigUpdate(BaseModel):
|
||||
xpd_act_alias: str | None = None
|
||||
xpd_act_id: str | None = None
|
||||
xpd_rid: str | None = None
|
||||
gold_pay_type: int | None = Field(None, ge=1, le=9)
|
||||
gold_pay_type: int | None = Field(default=None, ge=1, le=9)
|
||||
gold_recharge_channel: str | None = Field(
|
||||
None, pattern="^(wechat_qr|supplier_api)$"
|
||||
default=None, pattern="^(wechat_qr|supplier_api)$"
|
||||
)
|
||||
gold_api_product_id: str | None = Field(None, max_length=128)
|
||||
gold_api_account_template_name: str | None = Field(None, max_length=64)
|
||||
gold_api_product_id: str | None = Field(default=None, max_length=128)
|
||||
gold_api_account_template_name: str | None = Field(default=None, max_length=64)
|
||||
gift_id: str | None = None
|
||||
skin_id: str | None = None
|
||||
|
||||
@@ -859,7 +859,7 @@ class DouyuTaskBatchRequest(BaseModel):
|
||||
account_ids: list[int]
|
||||
task_type: str
|
||||
handbook_scope: str = Field(..., pattern="^(elite|esports|peace)$")
|
||||
concurrency: int = Field(3, ge=1, le=10)
|
||||
concurrency: int = Field(default=3, ge=1, le=10)
|
||||
payload: dict[str, Any] = Field(default_factory=dict)
|
||||
|
||||
|
||||
|
||||
@@ -41,10 +41,10 @@ from __future__ import annotations
|
||||
|
||||
import json
|
||||
from datetime import UTC, datetime, timedelta
|
||||
from typing import Any
|
||||
from typing import Any, cast
|
||||
|
||||
from loguru import logger
|
||||
from sqlalchemy import text
|
||||
from sqlalchemy import CursorResult, text
|
||||
from sqlalchemy.orm import Session
|
||||
|
||||
from ....models import AutomationJob
|
||||
@@ -133,7 +133,9 @@ def _delete_batch(
|
||||
),
|
||||
params,
|
||||
)
|
||||
return int(result.rowcount or 0)
|
||||
# Session.execute 的静态返回类型是 Result[Any],但 DML 语句实际拿到的是
|
||||
# CursorResult(rowcount 只定义在它上面)。运行期行为不变,只是把这一点写实。
|
||||
return int(cast(CursorResult[Any], result).rowcount or 0)
|
||||
|
||||
|
||||
def run_douyu_tasks_retention(db: Session, job: AutomationJob) -> dict[str, Any]:
|
||||
|
||||
@@ -221,9 +221,11 @@ class ManualMixin:
|
||||
account.esports_opened = manual_type is not None and manual_type >= 1
|
||||
# 到期时间也落库 —— 否则界面只能显示"已开通",看不到什么时候到期
|
||||
expire_raw = result.get("expire_time")
|
||||
expire_int = (
|
||||
int(expire_raw) if str(expire_raw or "").strip().isdigit() else None
|
||||
)
|
||||
# 先规整成字符串再判 isdigit:原来写 `int(expire_raw) if str(expire_raw
|
||||
# or "").strip().isdigit()` —— 判的是 A、转的是 B,类型检查器收窄不了,
|
||||
# 而且同一份值取了两遍。规整到一个局部变量后两个问题一起没了。
|
||||
expire_text = str(expire_raw or "").strip()
|
||||
expire_int = int(expire_text) if expire_text.isdigit() else None
|
||||
# ⚠️ 斗鱼用 0 表示「没有到期时间」。直接存 0 的话界面会显示 1970/1/1
|
||||
# (这个坑在积分到期上踩过一次,这里别再踩)。
|
||||
account.esports_expire_time = (
|
||||
|
||||
@@ -67,9 +67,12 @@ def _match_suit_item(want: str, rows: list[dict[str, Any]]) -> dict[str, Any] |
|
||||
# 优先找带「套装」前缀的
|
||||
for it in rows:
|
||||
cname = str(it.get("commodityName") or "").strip()
|
||||
if core in cname and not any(k in cname for k in SUIT_EXCLUDE_KEYWORDS):
|
||||
if "套装" in cname:
|
||||
return it
|
||||
if (
|
||||
core in cname
|
||||
and not any(k in cname for k in SUIT_EXCLUDE_KEYWORDS)
|
||||
and "套装" in cname
|
||||
):
|
||||
return it
|
||||
|
||||
# 3. 兜底匹配不带排除词的项
|
||||
for it in rows:
|
||||
|
||||
@@ -7,7 +7,7 @@ import copy
|
||||
import threading
|
||||
import time
|
||||
from datetime import UTC, datetime
|
||||
from typing import Any
|
||||
from typing import Any, Literal
|
||||
|
||||
from loguru import logger
|
||||
from sqlalchemy.orm import Session
|
||||
@@ -51,7 +51,9 @@ class HuyaBatchRunnerCore:
|
||||
def stop(self):
|
||||
self._stop.set()
|
||||
|
||||
def _open_wss_session(self, uid: int, cookie: str, kind: str):
|
||||
def _open_wss_session(
|
||||
self, uid: int, cookie: str, kind: Literal["activity", "shop"]
|
||||
):
|
||||
"""在当前任务线程创建并登记 WSS 会话,统一由 finally 回收。"""
|
||||
from core.huya.elite_session import HuyaEliteWssSession
|
||||
|
||||
|
||||
@@ -4,7 +4,7 @@ from __future__ import annotations
|
||||
|
||||
import time
|
||||
from datetime import UTC, datetime
|
||||
from typing import TYPE_CHECKING, Any
|
||||
from typing import TYPE_CHECKING, Any, Literal
|
||||
|
||||
from sqlalchemy.orm import Session
|
||||
|
||||
@@ -40,7 +40,9 @@ class RechargeMixin:
|
||||
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 _open_wss_session(self, uid: int, cookie: str, kind: str): ...
|
||||
def _open_wss_session(
|
||||
self, uid: int, cookie: str, kind: Literal["activity", "shop"]
|
||||
): ...
|
||||
def _shop_client(self, uid: int, cookie: str): ...
|
||||
|
||||
@staticmethod
|
||||
|
||||
@@ -71,9 +71,9 @@ full = gradient_rounded(S, RADIUS)
|
||||
draw_gift(full)
|
||||
base = "web/extension/huya-exchange-helper/"
|
||||
for size in (128, 48):
|
||||
full.resize((size, size), Image.LANCZOS).save(f"{base}icon{size}.png")
|
||||
full.resize((size, size), Image.Resampling.LANCZOS).save(f"{base}icon{size}.png")
|
||||
|
||||
simple = gradient_rounded(512, 100)
|
||||
draw_gift_simple(simple)
|
||||
simple.resize((16, 16), Image.LANCZOS).save(f"{base}icon16.png")
|
||||
simple.resize((16, 16), Image.Resampling.LANCZOS).save(f"{base}icon16.png")
|
||||
print("icons regenerated")
|
||||
|
||||
Reference in New Issue
Block a user