完成 Ruff 全量清理

This commit is contained in:
yml2213
2026-08-31 10:55:44 +08:00
parent 840af3108e
commit 09ab80e062
68 changed files with 323 additions and 291 deletions
+8 -13
View File
@@ -2,21 +2,16 @@
## Ruff lint 清理
记录日期:2026-08-30
状态:已完成
`uv run ruff check .` 当前发现 1263 条 lint 诊断。该事项暂不在本次处理,后续按批次清理并逐批运行 pytest、Pyright 和 Ruff 检查。
完成日期:2026-08-31
建议顺序:
本轮完成了 Ruff lint 全量清理,包括未使用导入与变量、导入排序、类型注解现代化、FastAPI 依赖声明、异常处理标注、时区处理及其他规则。FastAPI 路由参数中的依赖调用通过 `web/backend/**/*.py` 的 B008 配置例外保留框架惯用写法;非 FastAPI 的可变默认对象已改为函数内惰性初始化。
1. `F401``F841`:未使用导入和变量。
2. `BLE001``S110``S112`:异常处理质量。
3. `B008`:FastAPI 依赖声明模式,区分真实问题与框架惯用写法。
4. `I001`:导入排序。
5. `UP045``UP007``UP017``UP035``UP006`:类型注解和 Python 版本语法现代化。
最终基线:
当前基线:
- Ruff lint1263 条
- Ruff format207 个文件全部通过
- pytest104 passed
- Ruff lint0 条
- Ruff format208 个文件全部通过
- pytest105 passed
- Pyright0 errors / 0 warnings
- Python compileall:通过
+1 -1
View File
@@ -44,7 +44,7 @@ class CookieEnricher:
self.generate_acf_ccn_cookie()
logger.info("补CK完成")
return
except Exception as e:
except Exception as e: # noqa: BLE001 外部接口与任务边界需要保留宽泛异常兜底
last_error = e
logger.warning(f"补CK失败 {attempt}/{max_attempts}: {e}")
if attempt < max_attempts:
+17 -13
View File
@@ -5,7 +5,7 @@ import os
import re
import threading
import time
from datetime import datetime, timedelta
from datetime import UTC, datetime, timedelta
import requests
from loguru import logger
@@ -68,8 +68,8 @@ class EmailVerifier:
f"{self.roundcube_url}?_task=logout",
timeout=5,
)
except Exception:
pass
except Exception as exc: # noqa: BLE001
logger.debug(f"Roundcube logout failed: {exc}")
self._rc_session = None
self._rc_logged_in = False
@@ -176,7 +176,7 @@ class EmailVerifier:
logger.debug(f"Roundcube: {password_name}登录成功 ({self.username})")
return True, False
except Exception as e:
except Exception as e: # noqa: BLE001 外部接口与任务边界需要保留宽泛异常兜底
logger.warning(f"Roundcube: 登录异常: {e}")
return False, False
@@ -230,7 +230,7 @@ class EmailVerifier:
return messages
except Exception as e:
except Exception as e: # noqa: BLE001 外部接口与任务边界需要保留宽泛异常兜底
logger.warning(f"Roundcube: 获取邮件列表失败: {e}")
return []
@@ -252,7 +252,7 @@ class EmailVerifier:
)
return resp.text
except Exception as e:
except Exception as e: # noqa: BLE001 外部接口与任务边界需要保留宽泛异常兜底
logger.warning(f"Roundcube: 读取邮件 {uid} 失败: {e}")
return None
@@ -270,7 +270,7 @@ class EmailVerifier:
- "2026-06-20" → 直接解析
- "06-20" → 今年的该日期
"""
now = datetime.now()
now = datetime.now(UTC)
# 今天
if date_str.startswith("今天"):
@@ -322,32 +322,36 @@ class EmailVerifier:
# 日期格式 2026-06-20
try:
return datetime.strptime(date_str.strip(), "%Y-%m-%d")
return datetime.strptime(date_str.strip(), "%Y-%m-%d").replace(tzinfo=UTC)
except ValueError:
pass
# 日期格式 2026-06-20 14:30
try:
return datetime.strptime(date_str.strip(), "%Y-%m-%d %H:%M")
return datetime.strptime(date_str.strip(), "%Y-%m-%d %H:%M").replace(
tzinfo=UTC
)
except ValueError:
pass
# 日期格式 2026-06-20 14:30:00
try:
return datetime.strptime(date_str.strip(), "%Y-%m-%d %H:%M:%S")
return datetime.strptime(date_str.strip(), "%Y-%m-%d %H:%M:%S").replace(
tzinfo=UTC
)
except ValueError:
pass
# 日期格式 06-20
try:
dt = datetime.strptime(date_str.strip(), "%m-%d")
dt = datetime.strptime(date_str.strip(), "%m-%d").replace(tzinfo=UTC)
return dt.replace(year=now.year)
except ValueError:
pass
# 日期格式 06-20 14:30
try:
dt = datetime.strptime(date_str.strip(), "%m-%d %H:%M")
dt = datetime.strptime(date_str.strip(), "%m-%d %H:%M").replace(tzinfo=UTC)
return dt.replace(year=now.year)
except ValueError:
pass
@@ -395,7 +399,7 @@ class EmailVerifier:
return code
except EmailLoginError:
raise
except Exception as e:
except Exception as e: # noqa: BLE001 外部接口与任务边界需要保留宽泛异常兜底
last_error = str(e)
logger.warning(f"Roundcube 读邮件异常: {e}")
+4 -4
View File
@@ -398,7 +398,7 @@ class DouyuLogin:
return LoginResult(
success=False, message=str(e), code="email_login_failed"
)
except Exception as e:
except Exception as e: # noqa: BLE001 外部接口与任务边界需要保留宽泛异常兜底
elapsed = time.monotonic() - start_time
if self.max_login_retries > 0:
logger.error(
@@ -475,7 +475,7 @@ class DouyuLogin:
return LoginResult(
success=False, message=str(e), code="email_login_failed"
)
except Exception as e:
except Exception as e: # noqa: BLE001 外部接口与任务边界需要保留宽泛异常兜底
elapsed = time.monotonic() - start_time
if self.max_login_retries > 0:
logger.error(
@@ -901,7 +901,7 @@ class DouyuLogin:
if response2.status_code == 200:
logger.info("WebLogin成功")
except Exception as e:
except Exception as e: # noqa: BLE001 外部接口与任务边界需要保留宽泛异常兜底
logger.warning(f"WebLogin请求失败(不影响登录): {e}")
# 登录成功后补齐 Web 侧 Cookie。补 CK 失败只影响完整度,不回滚已成功的登录态。
@@ -915,7 +915,7 @@ class DouyuLogin:
).enrich_with_retry()
except InterruptedError:
raise
except Exception as e:
except Exception as e: # noqa: BLE001 外部接口与任务边界需要保留宽泛异常兜底
self._cookie_enrich_error = self._truncate_error(str(e), 160)
logger.warning(
f"补CK最终失败,本次仍按登录成功保存基础CK: {self._cookie_enrich_error}"
+5 -3
View File
@@ -137,7 +137,7 @@ class BaseWhitelistAdapter(ABC):
return False, f"白名单添加失败,API响应: {resp}"
except Exception as e:
except Exception as e: # noqa: BLE001 外部接口与任务边界需要保留宽泛异常兜底
msg = f"白名单同步失败: {e}"
logger.error(msg)
return False, msg
@@ -165,7 +165,8 @@ def _get_local_exit_ip() -> str | None:
match = re.search(r"(\d{1,3}(?:\.\d{1,3}){3})", text)
if match:
return match.group(1)
except Exception:
except Exception as exc: # noqa: BLE001
logger.debug(f"本机出口 IP 查询失败: {url}: {exc}")
continue
return None
@@ -197,6 +198,7 @@ def get_exit_ip_via_proxy(proxy: str) -> str | None:
match = re.search(r"(\d{1,3}(?:\.\d{1,3}){3})", text)
if match:
return match.group(1)
except Exception:
except Exception as exc: # noqa: BLE001
logger.debug(f"代理出口 IP 查询失败: {url}: {exc}")
continue
return None
+4 -4
View File
@@ -75,7 +75,7 @@ class XiequAdapter(BaseWhitelistAdapter):
if r.get("IP") or r.get("ip")
]
except Exception as e:
except Exception as e: # noqa: BLE001 外部接口与任务边界需要保留宽泛异常兜底
logger.error(f"获取白名单失败: {e}")
return []
@@ -120,7 +120,7 @@ class XiequAdapter(BaseWhitelistAdapter):
logger.warning(f"白名单添加结果: {text}")
return False, text
except Exception as e:
except Exception as e: # noqa: BLE001 外部接口与任务边界需要保留宽泛异常兜底
logger.error(f"添加白名单失败: {e}")
return False, str(e)
@@ -141,7 +141,7 @@ class XiequAdapter(BaseWhitelistAdapter):
logger.warning(f"白名单删除结果: {text}")
return False, text
except Exception as e:
except Exception as e: # noqa: BLE001 外部接口与任务边界需要保留宽泛异常兜底
logger.error(f"删除白名单失败: {e}")
return False, str(e)
@@ -160,7 +160,7 @@ class XiequAdapter(BaseWhitelistAdapter):
logger.info(msg)
return True, msg
except Exception as e:
except Exception as e: # noqa: BLE001 外部接口与任务边界需要保留宽泛异常兜底
msg = f"连接失败: {e}"
logger.error(msg)
return False, msg
+3 -3
View File
@@ -115,7 +115,7 @@ class XkdailiAdapter(BaseWhitelistAdapter):
logger.warning(f"星空白名单添加失败: {msg}")
return ok, msg
except Exception as e:
except Exception as e: # noqa: BLE001 外部接口与任务边界需要保留宽泛异常兜底
logger.error(f"星空添加白名单失败: {e}")
return False, str(e)
@@ -136,7 +136,7 @@ class XkdailiAdapter(BaseWhitelistAdapter):
logger.warning(f"星空白名单删除失败: {msg}")
return ok, msg
except Exception as e:
except Exception as e: # noqa: BLE001 外部接口与任务边界需要保留宽泛异常兜底
logger.error(f"星空删除白名单失败: {e}")
return False, str(e)
@@ -159,5 +159,5 @@ class XkdailiAdapter(BaseWhitelistAdapter):
# 其他错误(如无效IP),说明认证通过了
return True, f"连接成功,API可访问: {msg}"
except Exception as e:
except Exception as e: # noqa: BLE001 外部接口与任务边界需要保留宽泛异常兜底
return False, f"连接失败: {e}"
+1 -1
View File
@@ -167,7 +167,7 @@ class ProxyResolver:
f"代理预检 {attempt}/{max_attempts}: {last_error}: {text[:80]}",
)
except Exception as exc:
except Exception as exc: # noqa: BLE001 外部接口与任务边界需要保留宽泛异常兜底
last_error = f"代理API请求失败: {exc}"
self._log("warning", f"代理预检 {attempt}/{max_attempts}: {last_error}")
+5 -3
View File
@@ -27,7 +27,7 @@ def verify_proxy_url(proxy_url: str, timeout: tuple = (3, 5)) -> tuple[bool, str
)
response.raise_for_status()
return True, "代理可用 → 斗鱼可达"
except Exception as exc:
except Exception as exc: # noqa: BLE001 外部接口与任务边界需要保留宽泛异常兜底
err_msg = str(exc)
if "Tunnel connection failed" in err_msg or "503" in err_msg:
detail = "代理拒绝连接(白名单可能未生效)"
@@ -75,7 +75,8 @@ def verify_proxies_concurrent(
ok, _ = future.result()
if ok:
available.append(future_map[future])
except Exception:
except Exception as exc: # noqa: BLE001
logger.debug(f"代理并发验证失败: {future_map[future]}: {exc}")
continue
if available:
logger.success(
@@ -99,7 +100,8 @@ def verify_proxies_concurrent(
if item != future:
item.cancel()
return proxy_url, msg
except Exception:
except Exception as exc: # noqa: BLE001
logger.debug(f"代理并发验证失败: {proxy_url}: {exc}")
continue
return None, f"{len(proxy_urls)} 个代理均不可用"
-1
View File
@@ -1,6 +1,5 @@
"""代理模块使用的白名单适配器。"""
from .proxy_platforms import create_adapter
from .proxy_platforms.base import BaseWhitelistAdapter, _get_local_exit_ip
+7 -7
View File
@@ -73,7 +73,7 @@ def get_challenge_gt_bak() -> tuple[str, str]:
}
params = {
"t": str(int(round(time.time() * 1000))),
"t": str(round(time.time() * 1000)),
}
response = requests.get(
@@ -133,7 +133,7 @@ def get_js_address(gt: str, proxies: Mapping[str, str] | None = None) -> dict:
params = {
"gt": gt,
"callback": "geetest_" + str(int(round(time.time() * 1000))),
"callback": "geetest_" + str(round(time.time() * 1000)),
}
response = _get(
@@ -173,7 +173,7 @@ def get_c_s(
+ "&lang=zh-cn&pt=0&client_type=web&w="
+ w
+ "&callback=geetest_"
+ str(int(round(time.time() * 1000))),
+ str(round(time.time() * 1000)),
headers=headers,
proxies=proxies,
)
@@ -210,7 +210,7 @@ def req_fullpage_validate(
+ "&lang=zh-cn&pt=0&client_type=web&w="
+ w
+ "&callback=geetest_"
+ str(int(round(time.time() * 1000))),
+ str(round(time.time() * 1000)),
headers=headers,
proxies=proxies,
)
@@ -241,7 +241,7 @@ def req_slide(gt: str, challenge: str, w2: str) -> None:
+ "&lang=zh-cn&pt=0&client_type=web&w="
+ w2
+ "&callback=geetest_"
+ str(int(round(time.time() * 1000))),
+ str(round(time.time() * 1000)),
headers=headers,
timeout=REQUEST_TIMEOUT,
)
@@ -277,7 +277,7 @@ def get_picture(gt: str, challenge: str) -> tuple[str, str, list[int], str, str,
"isPC": "true",
"autoReset": "true",
"width": "100%",
"callback": "geetest_" + str(int(round(time.time() * 1000))),
"callback": "geetest_" + str(round(time.time() * 1000)),
}
response = requests.get(
@@ -343,7 +343,7 @@ def req_end(gt: str, challenge: str, w: str) -> dict:
+ "&lang=zh-cn&%24_BCm=0&client_type=web&w="
+ w
+ "&callback=geetest_"
+ str(int(round(time.time() * 1000))),
+ str(round(time.time() * 1000)),
headers=headers,
timeout=REQUEST_TIMEOUT,
)
+1 -1
View File
@@ -592,7 +592,7 @@ def H(t: int, e: str) -> str:
n = 36 * r[0] + r[1]
# 计算目标值
a = round(t) + n
a = (t) + n
# 构建字符池
_ = [[], [], [], [], []]
+2 -2
View File
@@ -85,7 +85,7 @@ def get_w2(gt: str, challenge: str, c: list[int], s: str, str_16: str) -> str:
"u": fake_timing["loadEventEnd"],
}
first_time = int(round(time.time() * 1000)) # 伪造脚本开始运行时间
first_time = round(time.time() * 1000) # 伪造脚本开始运行时间
guiji_yuanshu_shuzu = generate_realistic_trajectory(
start_x=random.randint(400, 600), # 起始位置随机
@@ -99,7 +99,7 @@ def get_w2(gt: str, challenge: str, c: list[int], s: str, str_16: str) -> str:
compressed = compress_trajectory(trajectory)
tt = encrypt_string(compressed, c, s)
passtime = str(int(round(time.time() * 1000)) - first_time)
passtime = str(round(time.time() * 1000) - first_time)
rp = simple_md5(gt + challenge + passtime)
+3 -3
View File
@@ -306,7 +306,7 @@ class UserPrizeRecordItem(TafStruct):
self.exchangeDate = ins.read_int64(21, default=self.exchangeDate)
while True:
pos = ins.buf.tell()
tag, dtype = ins.read_head()
_tag, dtype = ins.read_head()
if dtype == TafType.STRUCT_END:
ins.buf.seek(pos)
return
@@ -406,7 +406,7 @@ class ActTaskPrizeInfo(TafStruct):
self.extra = ins.read_map(12)
while True:
pos = ins.buf.tell()
tag, dtype = ins.read_head()
_tag, dtype = ins.read_head()
if dtype == TafType.STRUCT_END:
ins.buf.seek(pos)
return
@@ -494,7 +494,7 @@ class ActTaskDetailItem(TafStruct):
self.endTime = ins.read_string(26, default=self.endTime)
while True:
pos = ins.buf.tell()
tag, dtype = ins.read_head()
_tag, dtype = ins.read_head()
if dtype == TafType.STRUCT_END:
ins.buf.seek(pos)
return
+6 -6
View File
@@ -232,7 +232,7 @@ def solve_safe_auth(
result = solver.solve(risk_url)
except HuyaQrAuthRequiredError:
raise
except Exception as exc:
except Exception as exc: # noqa: BLE001 外部接口与任务边界需要保留宽泛异常兜底
last_err = exc
logger.warning(f"[safe_auth] 第 {attempt + 1} 次过验异常: {exc}")
time.sleep(1.0)
@@ -425,8 +425,8 @@ class HuyaAppPasswordLogin:
from .device_profile import record_login
record_login(self.username, result.success, result.message)
except Exception: # 元数据记录失败不影响登录结果
pass
except Exception as exc: # noqa: BLE001 # 元数据记录失败不影响登录结果
logger.debug(f"登录元数据记录失败: {exc}")
return result
def _login_impl(self) -> HuyaLoginResult:
@@ -450,7 +450,7 @@ class HuyaAppPasswordLogin:
message=str(exc),
code="QR_AUTH_REQUIRED",
)
except Exception as exc:
except Exception as exc: # noqa: BLE001 外部接口与任务边界需要保留宽泛异常兜底
return HuyaLoginResult(
success=False,
message=f"App 登录凭证获取失败: {exc}",
@@ -478,7 +478,7 @@ class HuyaAppPasswordLogin:
if env.uid != uid:
struct.pack_into(">Q", raw, env.uid_off, uid)
wup = base64.b64encode(bytes(raw)).decode("ascii")
except Exception as exc:
except Exception as exc: # noqa: BLE001 外部接口与任务边界需要保留宽泛异常兜底
return HuyaLoginResult(
success=False,
message=f"证书铸造/信封补丁失败: {exc}",
@@ -603,7 +603,7 @@ class HuyaAppPasswordLogin:
sdid=sdid,
context=pc.context,
)
except Exception as exc:
except Exception as exc: # noqa: BLE001 外部接口与任务边界需要保留宽泛异常兜底
return HuyaLoginResult(
success=False,
message=f"扫码绑定兑换 Cookie 失败: {exc}",
+4 -4
View File
@@ -6,7 +6,7 @@ import threading
import time
from collections.abc import Mapping
from dataclasses import dataclass
from datetime import datetime
from datetime import UTC, datetime
from core.sms_provider import SmsLine, SmsProviderClient
@@ -76,7 +76,7 @@ def register_huya_with_sms_line(
sms_url=item.url,
)
sent_at = datetime.now()
sent_at = datetime.now(UTC)
try:
code_result = send_huya_sms_code(phone=phone, proxies=proxies)
except HuyaLoginError as exc:
@@ -89,7 +89,7 @@ def register_huya_with_sms_line(
normalized_phone=normalized_phone,
sms_url=item.url,
)
except Exception as exc:
except Exception as exc: # noqa: BLE001 外部接口与任务边界需要保留宽泛异常兜底
return HuyaAutoRegisterResult(
phone=phone,
provider=item.provider,
@@ -155,7 +155,7 @@ def register_huya_with_sms_line(
sms_url=item.url,
attempts=attempts,
)
except Exception as exc:
except Exception as exc: # noqa: BLE001 外部接口与任务边界需要保留宽泛异常兜底
return HuyaAutoRegisterResult(
phone=phone,
provider=item.provider,
+2 -2
View File
@@ -10,7 +10,7 @@ import time
import uuid
from collections.abc import Mapping
from dataclasses import dataclass
from datetime import datetime
from datetime import UTC, datetime
from urllib.parse import quote, urlsplit, urlunsplit
import requests
@@ -496,7 +496,7 @@ def change_huya_password_with_sms_line(
) -> HuyaChangePasswordResult:
"""使用同一手机号接码链接完成改密短信验证。"""
changer = HuyaPasswordChanger(uid=uid, cookie=cookie, proxies=proxies)
sent_at = datetime.now()
sent_at = datetime.now(UTC)
code_result = changer.send_code()
if not code_result.success or not code_result.session_data:
return HuyaChangePasswordResult(
+8 -6
View File
@@ -22,6 +22,8 @@ import random
from collections.abc import Mapping
from pathlib import Path
from loguru import logger
ROOT = Path(__file__).resolve().parent.parent.parent
DATA_DIR = ROOT / "data"
PRIMARY_PROFILE_DB = DATA_DIR / "huya_device_profiles.json"
@@ -119,13 +121,13 @@ def _load_db() -> dict:
if PRIMARY_PROFILE_DB.exists():
try:
return json.loads(PRIMARY_PROFILE_DB.read_text("utf-8"))
except Exception:
pass
except Exception as exc: # noqa: BLE001
logger.debug(f"读取主设备画像库失败: {exc}")
if FALLBACK_PROFILE_DB.exists():
try:
return json.loads(FALLBACK_PROFILE_DB.read_text("utf-8"))
except Exception:
pass
except Exception as exc: # noqa: BLE001
logger.debug(f"读取备用设备画像库失败: {exc}")
return {}
@@ -135,8 +137,8 @@ def _save_db(db: dict) -> None:
PRIMARY_PROFILE_DB.write_text(
json.dumps(db, indent=2, ensure_ascii=False), encoding="utf-8"
)
except Exception:
pass
except Exception as exc: # noqa: BLE001
logger.debug(f"保存设备画像库失败: {exc}")
def _enrich_profile(profile: dict) -> tuple[dict, bool]:
+4 -2
View File
@@ -10,6 +10,8 @@ import json
import struct
from pathlib import Path
from loguru import logger
INT8, INT16, INT32, INT64 = 0x00, 0x01, 0x02, 0x03
STRING1, STRING4 = 0x06, 0x07
MAP, LIST = 0x08, 0x09
@@ -131,8 +133,8 @@ class Envelope:
if candidate.exists():
try:
return cls._load_from_path(candidate)
except Exception:
pass
except Exception as exc: # noqa: BLE001
logger.debug(f"加载证书信封候选文件失败: {candidate}: {exc}")
return cls(base64.b64decode(DEFAULT_QURL_B64))
@classmethod
+12 -9
View File
@@ -4,6 +4,8 @@ TAF/WUP 帧解码器 — 将二进制帧转为可读摘要,用于日志输出
from typing import Any, cast
from loguru import logger
from .taf_protocol import TafInputStream, TafType
from .wup_protocol import normalize_wup_payload
@@ -101,13 +103,13 @@ def _decode_taf_struct(ins: TafInputStream, depth: int = 0) -> dict:
if depth < 5:
try:
val = _decode_taf_value(ins, dtype, depth)
except Exception:
except Exception: # noqa: BLE001 外部接口与任务边界需要保留宽泛异常兜底
val = f"<decode_err:0x{dtype:02x}>"
else:
val = "<...>"
try:
ins.skip_field(dtype)
except Exception:
except Exception: # noqa: BLE001 外部接口与任务边界需要保留宽泛异常兜底
break
key = f"tag{tag}"
if key in fields:
@@ -195,19 +197,20 @@ def _decode_wup_body(body: bytes) -> dict:
if v:
try:
tins = TafInputStream(v)
ttag, tdt = tins.peek_head()
_ttag, tdt = tins.peek_head()
if tdt == TafType.STRUCT_BEGIN:
tins.read_head()
result[k] = _decode_taf_struct(tins)
else:
result[k] = f"<{len(v)}B>"
except Exception:
except Exception as exc: # noqa: BLE001
logger.debug(f"TAF 嵌套结构解码失败: {exc}")
result[k] = f"<{len(v)}B>"
else:
result[k] = _decode_taf_value(sins, vt)
except Exception:
pass
except Exception as e:
except Exception as exc: # noqa: BLE001
logger.debug(f"TAF 字段解码失败: {exc}")
except Exception as e: # noqa: BLE001 外部接口与任务边界需要保留宽泛异常兜底
result["err"] = str(e)
return result
@@ -286,7 +289,7 @@ def format_wss_log(body: bytes, cmd: int, seq: int, direction: str) -> str:
try:
ins = TafInputStream(clean)
# 看第一个 head
tag, dtype = ins.peek_head()
_tag, dtype = ins.peek_head()
if dtype == TafType.STRUCT_BEGIN:
ins.read_head()
fields = _decode_taf_struct(ins)
@@ -314,6 +317,6 @@ def format_wss_log(body: bytes, cmd: int, seq: int, direction: str) -> str:
if fields:
return f"{prefix} {cmd_name} {_fmt_fields(cast(dict[str, Any], _truncate(fields)))}"
return f"{prefix} {cmd_name}"
except Exception:
except Exception: # noqa: BLE001 外部接口与任务边界需要保留宽泛异常兜底
cmd_name = CMD_NAMES.get(cmd, f"0x{cmd:02x}")
return f"{prefix} {cmd_name} ({len(body)}B)"
+7 -7
View File
@@ -92,7 +92,7 @@ class WSConnectParaInfo(TafStruct):
def _gen_trace_id() -> str:
"""生成 sTraceId (格式 hex8:hex8:0:0HAR 实证)"""
h = "%016x" % random.getrandbits(64)
h = f"{random.getrandbits(64):016x}"
return f"{h}:{h}:0:0"
@@ -294,7 +294,7 @@ class HuyaHttpClient:
import gzip
resp_data = gzip.decompress(resp_data)
except Exception as e:
except Exception as e: # noqa: BLE001 外部接口与任务边界需要保留宽泛异常兜底
self.logger(f"[HTTP] ❌ 请求失败: {type(e).__name__}: {e}")
return None
@@ -611,13 +611,13 @@ class HuyaHttpClient:
or resp.headers.get("Content-Encoding") == "gzip"
):
resp_data = gzip.decompress(resp_data)
except Exception as e:
except Exception as e: # noqa: BLE001 外部接口与任务边界需要保留宽泛异常兜底
self.logger(f"[LIVELINK] ❌ 小程序码请求失败: {type(e).__name__}: {e}")
return None
try:
data = json.loads(resp_data.decode("utf-8", "replace"))
except Exception as e:
except Exception as e: # noqa: BLE001 外部接口与任务边界需要保留宽泛异常兜底
self.logger(f"[LIVELINK] ❌ 小程序码响应解析失败: {type(e).__name__}: {e}")
return None
@@ -679,13 +679,13 @@ class HuyaHttpClient:
or resp.headers.get("Content-Encoding") == "gzip"
):
resp_data = gzip.decompress(resp_data)
except Exception as e:
except Exception as e: # noqa: BLE001 外部接口与任务边界需要保留宽泛异常兜底
self.logger(f"[LIVELINK] ❌ 二维码状态请求失败: {type(e).__name__}: {e}")
return None
try:
data = json.loads(resp_data.decode("utf-8", "replace"))
except Exception as e:
except Exception as e: # noqa: BLE001 外部接口与任务边界需要保留宽泛异常兜底
self.logger(
f"[LIVELINK] ❌ 二维码状态响应解析失败: {type(e).__name__}: {e}"
)
@@ -972,7 +972,7 @@ class HuyaHttpClient:
import gzip
resp_data = gzip.decompress(resp_data)
except Exception as e:
except Exception as e: # noqa: BLE001 外部接口与任务边界需要保留宽泛异常兜底
self.logger(f"[HTTP] ❌ payOrderSubmitV5 失败: {e}")
return None
+1 -2
View File
@@ -380,8 +380,7 @@ class HuyaPasswordLogin:
last_exc: Exception | None = None
for attempt in range(max_ip_retries + 1):
if attempt > 0:
if not self._swap_proxy():
if attempt > 0 and not self._swap_proxy():
logger.warning("无可用代理可切换,停止换 IP 重试")
break
try:
+2 -3
View File
@@ -5,7 +5,6 @@
来源:m-shop.yaoguo.com 的 jce/ShopFacade.js、api/orderui.ts、api/mall/PlayMallNewHome.ts
"""
from .taf_protocol import TafInputStream, TafOutputStream, TafStruct, TafType
@@ -68,7 +67,7 @@ def _skip_to_struct_end(ins: TafInputStream):
"""跳过当前结构里未解析的尾部字段,停在 STRUCT_END 前。"""
while True:
pos = ins.buf.tell()
tag, dtype = ins.read_head()
_tag, dtype = ins.read_head()
if dtype == TafType.STRUCT_END:
ins.buf.seek(pos)
return
@@ -427,7 +426,7 @@ class GoodsPriceInfo(TafStruct):
def first_sku_id(self) -> int:
if not self.skuMap:
return 0
return sorted(self.skuMap.keys())[0]
return min(self.skuMap.keys())
@property
def sku_list(self) -> list[dict]:
+3 -3
View File
@@ -255,7 +255,7 @@ class TafInputStream:
def _read_int_len(self) -> int:
"""读 map/list 长度(int32 带优化)"""
tag, dtype = self.read_head()
_tag, dtype = self.read_head()
return self._read_int_value(dtype)
def _read_int_value(self, dtype: int) -> int:
@@ -273,7 +273,7 @@ class TafInputStream:
def _skip_struct(self):
while True:
tag, dtype = self.read_head()
_tag, dtype = self.read_head()
if dtype == TafType.STRUCT_END:
break
self.skip_field(dtype)
@@ -441,7 +441,7 @@ class TafInputStream:
obj = struct_class()
obj.read_from(self)
# 消费 STRUCT_END
t, dt = self.read_head()
_t, dt = self.read_head()
if dt != TafType.STRUCT_END:
raise ValueError(f"期望 STRUCT_END, 实际 0x{dt:02x}")
return obj
+1 -1
View File
@@ -261,7 +261,7 @@ class HuyaCaptchaOcr:
target["cropped_image"],
char["cropped_image"],
)
except Exception:
except Exception: # noqa: BLE001 外部接口与任务边界需要保留宽泛异常兜底
score_matrix[target_index][char_index] = 1e6
row_ind, col_ind = linear_sum_assignment(score_matrix)
+3 -3
View File
@@ -137,7 +137,7 @@ class HuyaVerificationSolver:
max_width = max(bg.shape[1], tip.shape[1])
def pad_right(img, target_width):
height, width = img.shape[:2]
_height, width = img.shape[:2]
if width >= target_width:
return img
return cv2.copyMakeBorder(
@@ -299,8 +299,8 @@ class HuyaVerificationSolver:
"虎牙登录风控strategys完整结构: {}",
json.dumps(strategies, ensure_ascii=False)[:1200],
)
except Exception: # noqa: BLE001
pass
except Exception as exc: # noqa: BLE001
logger.debug(f"风控策略日志序列化失败: {exc}")
strategy_url_lower = strategy_url.lower()
# 判定依据是 URL 路径,不是 strategy 数值。
# 实测(2026-08-25): strategy=64 时 pt_auth.html 是滑块、qr_auth.html 才是扫码,
+14 -12
View File
@@ -76,7 +76,7 @@ class WssMessage:
def decode(cls, data: bytes) -> "WssMessage":
if len(data) < 6:
raise ValueError(f"消息太短: {len(data)} bytes")
version, command = struct.unpack(">BB", data[0:2])
_version, command = struct.unpack(">BB", data[0:2])
sequence = struct.unpack(">I", data[2:6])[0]
body = data[6:]
return cls(command=command, sequence=sequence, body=body)
@@ -256,7 +256,7 @@ class HuyaWssClient:
format_wss_log(msg.body, msg.command, msg.sequence, "")
)
await self._handle_message(msg)
except Exception as e:
except Exception as e: # noqa: BLE001 外部接口与任务边界需要保留宽泛异常兜底
self.logger(
f"[WSS] 解析消息失败: {e} raw_hex={raw_bytes[:50].hex()}"
)
@@ -264,7 +264,7 @@ class HuyaWssClient:
pass
except websockets.exceptions.ConnectionClosed as e:
self.logger(f"[WSS] 连接关闭: code={e.code} reason={e.reason}")
except Exception as e:
except Exception as e: # noqa: BLE001 外部接口与任务边界需要保留宽泛异常兜底
self.logger(f"[WSS] 接收循环异常: {type(e).__name__}: {e}")
async def _handle_message(self, msg: WssMessage):
@@ -289,7 +289,7 @@ class HuyaWssClient:
await self.send_heartbeat()
except asyncio.CancelledError:
pass
except Exception as e:
except Exception as e: # noqa: BLE001 外部接口与任务边界需要保留宽泛异常兜底
self.logger(f"[WSS] 心跳循环异常: {e}")
async def send_heartbeat(self):
@@ -399,7 +399,7 @@ class HuyaWssClient:
)
return
ins = TafInputStream(treq)
tag, dtype = ins.peek_head()
_tag, dtype = ins.peek_head()
if dtype != 0x0A: # STRUCT_BEGIN
self.logger(f"[RPC] wsLaunch tRsp 非结构体 dtype=0x{dtype:02x}")
return
@@ -432,7 +432,7 @@ class HuyaWssClient:
self.logger(
f"[RPC] wsLaunch 解析: guid={self._launch_guid} ip={self._launch_ip}"
)
except Exception as e:
except Exception as e: # noqa: BLE001 外部接口与任务边界需要保留宽泛异常兜底
self.logger(f"[RPC] wsLaunch 响应解析失败: {e}")
@staticmethod
@@ -610,15 +610,17 @@ class HuyaWssClient:
if data and isinstance(data, bytes) and len(data) > 0:
try:
ins = TafInputStream(data)
tag, dtype = ins.peek_head()
_tag, dtype = ins.peek_head()
if dtype == TafType.STRUCT_BEGIN:
ins.read_head()
decoded = _decode_taf_struct(ins)
self.logger(
f"[←] {service}.{method} {key}: {_truncate(decoded)}"
)
except Exception:
pass
except Exception as exc: # noqa: BLE001
self.logger(
f"[debug] WUP 响应字段解码失败: {service}.{method}.{key}: {exc}"
)
if rsp_class is None:
return body
@@ -794,13 +796,13 @@ class HuyaWssClient:
if data and isinstance(data, bytes) and len(data) > 0:
try:
ins = TafInputStream(data)
tag, dtype = ins.peek_head()
_tag, dtype = ins.peek_head()
if dtype == TafType.STRUCT_BEGIN:
ins.read_head()
decoded = _decode_taf_struct(ins)
self.logger(f"[←] payOrderSubmitV5 {key}: {_truncate(decoded)}")
except Exception:
pass
except Exception as exc: # noqa: BLE001
self.logger(f"[debug] WUP 支付响应字段解码失败: {key}: {exc}")
result = wup_resp.readStruct("tRsp", PayOrderRes)
if result is None:
result = wup_resp.readStruct("tResp", PayOrderRes)
+3 -3
View File
@@ -211,7 +211,7 @@ class WupResponse:
_, vt = ins.read_head()
val = _read_bytes_value(ins, vt)
self.newdata[key] = val
except Exception as e:
except Exception as e: # noqa: BLE001 外部接口与任务边界需要保留宽泛异常兜底
print(f"[WupResponse] 解析 newdata 失败: {e}")
def readStruct(self, key: str, struct_class=None):
@@ -236,13 +236,13 @@ class WupResponse:
ins = TafInputStream(data)
# newdata 里的结构体以 STRUCT_BEGIN 开头
try:
tag, dtype = ins.peek_head()
_tag, dtype = ins.peek_head()
if dtype == TafType.STRUCT_BEGIN:
ins.read_head() # 消费 STRUCT_BEGIN
obj = struct_class()
obj.read_from(ins)
return obj
except Exception as e:
except Exception as e: # noqa: BLE001 外部接口与任务边界需要保留宽泛异常兜底
print(f"[WupResponse] 解析 {struct_class.__name__} 失败: {e}")
return None
+2 -2
View File
@@ -5,7 +5,7 @@ from __future__ import annotations
import json
import re
from dataclasses import dataclass
from datetime import datetime
from datetime import UTC, datetime
from urllib.parse import urlparse
import requests
@@ -98,7 +98,7 @@ def _parse_sms8_time(value: str) -> datetime | None:
return None
for fmt in ("%Y-%m-%d %H:%M:%S", "%Y/%m/%d %H:%M:%S"):
try:
return datetime.strptime(text, fmt)
return datetime.strptime(text, fmt).replace(tzinfo=UTC)
except ValueError:
continue
return None
+4
View File
@@ -38,6 +38,10 @@ packages = ["core", "utils", "web"]
[tool.pytest.ini_options]
testpaths = ["tests"]
[tool.ruff.lint.per-file-ignores]
# FastAPI evaluates dependency metadata in route parameter defaults by design.
"web/backend/**/*.py" = ["B008"]
[dependency-groups]
dev = [
"pyright>=1.1.411",
+1 -1
View File
@@ -225,6 +225,6 @@ def main() -> int:
if __name__ == "__main__":
try:
sys.exit(main())
except Exception as exc:
except Exception as exc: # noqa: BLE001 外部接口与任务边界需要保留宽泛异常兜底
print(f"迁移失败:{exc}", file=sys.stderr)
sys.exit(1)
+12 -8
View File
@@ -22,6 +22,7 @@ from __future__ import annotations
import argparse
import json
import logging
import os
import re
import secrets
@@ -49,6 +50,8 @@ from pyvm.protocol import (
)
from pyvm.session import SessionState, load_session
logger = logging.getLogger(__name__)
REPLAY = ROOT / "replay"
DEFAULT_APPID = PAY_APPID
DEFAULT_SAVE_URL = f"https://api.unipay.qq.com/v1/r/{DEFAULT_APPID}/web_save"
@@ -103,7 +106,8 @@ def _load_cap(path: Path) -> dict:
d = attempt()
if isinstance(d, dict) and "C" in d:
return d
except Exception:
except Exception as exc: # noqa: BLE001
logger.debug("deepCap 候选格式解析失败: %s", exc)
continue
raise ValueError(f"无法解析 deepCap 文件: {path}(前 80 字符: {raw[:80]!r})")
@@ -122,8 +126,8 @@ def parse_plaintext(path: str | Path) -> dict:
d = json.loads(raw)
if isinstance(d, dict):
return {str(k): str(v) for k, v in d.items()}
except Exception:
pass
except Exception as exc: # noqa: BLE001
logger.debug("订单响应 JSON 解析失败: %s", exc)
fields: dict[str, str] = {}
for kv in raw.split("&"):
k, _, v = kv.partition("=")
@@ -249,7 +253,7 @@ def cmd_submit(args) -> int:
return 0
print(f"❌ web_save ret:{ret}({js.get('err_code', '')})—— 见 case 踩坑记录")
return 1
except Exception:
except Exception: # noqa: BLE001 外部接口与任务边界需要保留宽泛异常兜底
return 1
@@ -399,8 +403,8 @@ def cmd_mall_submit(args) -> int:
js = json.loads(raw)
if isinstance(js, dict):
ret = js.get("ret", js.get("result_code", js.get("code")))
except Exception:
pass
except Exception as exc: # noqa: BLE001
logger.debug("支付响应 JSON 解析失败: %s", exc)
ok = ret in (0, "0")
if ok:
_write_private_text(out, raw)
@@ -999,8 +1003,8 @@ def cmd_mall_pay(args) -> int:
print("\n ══ 微信扫码支付(终端二维码)══")
try:
qr.terminal(compact=False)
except Exception: # noqa: BLE001
pass
except Exception as exc: # noqa: BLE001
logger.debug("终端二维码输出失败: %s", exc)
return 0
+17 -21
View File
@@ -17,6 +17,7 @@ from __future__ import annotations
import itertools
import json
import math
import random
import urllib.parse
from pathlib import Path
@@ -40,7 +41,7 @@ def _ic(x):
try:
f = float(x)
return 0 if math.isnan(f) else int(f)
except Exception:
except Exception: # noqa: BLE001 外部接口与任务边界需要保留宽泛异常兜底
return 0
@@ -79,7 +80,7 @@ def js_typeof(v):
return "number"
if isinstance(v, str):
return "string"
if isinstance(v, JSFunction) or isinstance(v, HostFunction):
if isinstance(v, (JSFunction, HostFunction)):
return "function"
return "object"
@@ -99,7 +100,7 @@ def js_truthy(v):
def _nan_ok(x):
try:
return not (isinstance(x, float) and math.isnan(x))
except Exception:
except Exception: # noqa: BLE001 外部接口与任务边界需要保留宽泛异常兜底
return True
@@ -207,7 +208,7 @@ def js_num(v):
return v
try:
return float(v)
except Exception:
except Exception: # noqa: BLE001 外部接口与任务边界需要保留宽泛异常兜底
return float("nan")
@@ -226,12 +227,12 @@ def js_eq(a, b):
if isinstance(a, (int, float)) and isinstance(b, str):
try:
return a == float(b)
except Exception:
except Exception: # noqa: BLE001 外部接口与任务边界需要保留宽泛异常兜底
return False
if isinstance(b, (int, float)) and isinstance(a, str):
try:
return float(a) == b
except Exception:
except Exception: # noqa: BLE001 外部接口与任务边界需要保留宽泛异常兜底
return False
return a == b
@@ -355,9 +356,7 @@ def js_set(obj, key, val):
pass
if isinstance(obj, str):
raise TypeError("Cannot assign to read only property")
cur = getattr(
__import__("pyvm.algorithm", fromlist=["x"]).VM, "_last_u", None
)
cur = getattr(__import__("pyvm.algorithm", fromlist=["x"]).VM, "_last_u", None)
raise TypeError(
f"invalid set target obj={type(obj).__name__} key={key!r} val={type(val).__name__} u={cur}"
)
@@ -736,7 +735,7 @@ def h_parsefloat(this, s):
def h_isnan(this, x):
try:
return math.isnan(float(x))
except Exception:
except Exception: # noqa: BLE001 外部接口与任务边界需要保留宽泛异常兜底
return True
@@ -896,7 +895,7 @@ class VM:
for x in v:
try:
parts.append("%02x" % (int(x) & 255))
except Exception:
except Exception: # noqa: BLE001 外部接口与任务边界需要保留宽泛异常兜底
parts.append("??")
self.out_hex = "".join(parts)
# 仅供离线排查 webSave 输出缓冲何时变化,生产路径默认不启用。
@@ -1527,9 +1526,7 @@ class VM:
if not (
isinstance(fn, (JSFunction, HostFunction)) or callable(fn)
):
raise RuntimeError(
"op97 non-function at u=%s fn=%r" % (u, fn)
)
raise RuntimeError(f"op97 non-function at u={u} fn={fn!r}")
js_set(C, dest, js_apply(fn, thisv, f))
elif op == 98:
a = o[u + 1]
@@ -1647,10 +1644,10 @@ class VM:
u += 1
js_set(C, a, None)
else:
raise RuntimeError("unknown opcode %s at %s" % (op, u))
raise RuntimeError(f"unknown opcode {op} at {u}")
except _VMThrow as e:
if not d:
raise RuntimeError("VM uncaught throw: %s" % (e.value,))
raise RuntimeError(f"VM uncaught throw: {e.value}")
l = e.value
u = d.pop()
continue
@@ -1658,9 +1655,8 @@ class VM:
if not d:
raise
if not isinstance(d, list):
raise RuntimeError(
"d corrupted: %r (type %s) at trace %s"
% (d, type(d).__name__, self._trace[-3:])
raise TypeError(
f"d corrupted: {d!r} (type {type(d).__name__}) at trace {self._trace[-3:]}"
)
l = e
u = d.pop()
@@ -1746,14 +1742,14 @@ def decode_d(v):
vs = p[ci + 1 :]
try:
obj.set(key, decode_d(json.loads(vs)))
except Exception:
except Exception: # noqa: BLE001 外部接口与任务边界需要保留宽泛异常兜底
obj.set(key, decode_d(vs))
return obj
if v.lstrip("-").isdigit():
return int(v)
try:
return float(v)
except Exception:
except Exception: # noqa: BLE001 外部接口与任务边界需要保留宽泛异常兜底
return v
+6 -2
View File
@@ -72,8 +72,12 @@ class MallSession:
f"mall xMidasOps 应为 59620(非 goods 59640),实际 {len(self.xmidas_ops)}"
)
mid = self.transform_input[10]
if isinstance(mid, list) and mid and isinstance(mid[0], list):
if len(mid[0]) != 624:
if (
isinstance(mid, list)
and mid
and isinstance(mid[0], list)
and len(mid[0]) != 624
):
raise ValueError(f"624B 中间态长度 != 624: {len(mid[0])}")
@classmethod
@@ -38,13 +38,13 @@ def get_official_orders(cookies: dict[str, str], count: int = 20) -> dict[str, A
except ValueError as exc:
raise RuntimeError("订单状态查询返回非 JSON") from exc
if not isinstance(document, dict):
raise RuntimeError("订单状态查询响应格式异常")
raise TypeError("订单状态查询响应格式异常")
if document.get("ret_code") not in (None, 0, "0"):
raise RuntimeError(
f"订单状态查询失败: {document.get('ret_code')} {document.get('ret_msg', '')}"
)
if not isinstance(document.get("list", []), list):
raise RuntimeError("订单状态查询响应缺少 list")
raise TypeError("订单状态查询响应缺少 list")
return document
+14 -18
View File
@@ -13,6 +13,8 @@ JS 语义辅助复用 algorithm.py(_ic/i32/js_add/js_index/JSObject/JSFunction
from __future__ import annotations
import json
import math
from pathlib import Path
from .algorithm import (
UNDEF,
@@ -20,6 +22,7 @@ from .algorithm import (
JSDate,
JSFunction,
JSObject,
Window,
_ic,
h_decodeuri,
h_decodeuricomponent,
@@ -57,6 +60,8 @@ from .algorithm import (
ushr,
)
REPLAY = Path(__file__).resolve().parent.parent / "replay"
__all__ = ["REPLAY", "PagedooVM", "run_frame"]
@@ -66,28 +71,28 @@ __all__ = ["REPLAY", "PagedooVM", "run_frame"]
def _cmp_lt(a, b):
try:
return a < b
except Exception:
except Exception: # noqa: BLE001 外部接口与任务边界需要保留宽泛异常兜底
return False
def _cmp_le(a, b):
try:
return a <= b
except Exception:
except Exception: # noqa: BLE001 外部接口与任务边界需要保留宽泛异常兜底
return False
def _cmp_gt(a, b):
try:
return a > b
except Exception:
except Exception: # noqa: BLE001 外部接口与任务边界需要保留宽泛异常兜底
return False
def _cmp_ge(a, b):
try:
return a >= b
except Exception:
except Exception: # noqa: BLE001 外部接口与任务边界需要保留宽泛异常兜底
return False
@@ -122,8 +127,8 @@ def h_arr_slice(this, a=None, b=None):
a = 0
if b is None or b is UNDEF:
b = n
a = int(a) if a == a else 0
b = int(b) if b == b else n
a = int(a) if not isinstance(a, float) or not math.isnan(a) else 0
b = int(b) if not isinstance(b, float) or not math.isnan(b) else n
if a < 0:
a = max(0, n + a)
if b < 0:
@@ -303,8 +308,7 @@ def _pg_index(obj, key):
return HostFunction(
lambda this: this.t if isinstance(this, JSDate) else obj.t, "valueOf"
)
if isinstance(obj, str):
if isinstance(key, str):
if isinstance(obj, str) and isinstance(key, str):
if key == "length":
return len(obj)
if key == "charCodeAt":
@@ -486,15 +490,7 @@ class PagedooVM:
and len(self._host_log) < 2000
):
# 记录调用目标(简化)
_tgt = (
o[u + 2]
if op in (4, 11, 44, 50)
else (
o[u + 2]
if op in (0, 18, 23, 26, 43, 48, 84, 107)
else o[u + 2]
)
)
_tgt = o[u + 2]
try:
_tv = C[_tgt] if 0 <= _tgt < len(C) else UNDEF
_tr = (
@@ -505,7 +501,7 @@ class PagedooVM:
else repr(_tv)[:30]
)
self._host_log.append((op, u, _tr))
except Exception:
except Exception: # noqa: BLE001 外部接口与任务边界需要保留宽泛异常兜底
self._host_log.append((op, u, "?"))
if (
getattr(self, "_trace", None) is not None
+2 -2
View File
@@ -86,7 +86,7 @@ def validate_goods_materials(
def validate_mall_materials(transform_fixed: dict[str, Any]) -> None:
"""校验 mall 固定槽模板;动态槽仍由当前会话的 GetPayToken 填充。"""
if not isinstance(transform_fixed, dict):
raise ValueError("mall transform-fixed 必须是对象")
raise TypeError("mall transform-fixed 必须是对象")
required = {
str(index) for index in (1, 2, 3, 4, 5, 7, 8, 9, 11, 12, 13, 14, 15, 16, 17)
}
@@ -95,7 +95,7 @@ def validate_mall_materials(transform_fixed: dict[str, Any]) -> None:
raise ValueError(f"mall transform-fixed 缺少槽: {', '.join(missing)}")
for index in required:
if not isinstance(transform_fixed[index], list):
raise ValueError(f"mall transform-fixed 槽 {index} 不是数组")
raise TypeError(f"mall transform-fixed 槽 {index} 不是数组")
def goods_material_diagnostics(
View File
View File
View File
View File
+5 -1
View File
@@ -81,7 +81,10 @@ def _safe_log(job: dict, line: str) -> None:
clean = re.sub(r"HOLD_[A-Za-z0-9_-]+", "[订单已隐藏]", clean)
clean = re.sub(r"(订单\s*[:]\s*)\S+", r"\1[订单已隐藏]", clean)
clean = re.sub(
r"(?:token|openid|openkey|cookie)=\S+", "[敏感字段已隐藏]", clean, flags=re.IGNORECASE
r"(?:token|openid|openkey|cookie)=\S+",
"[敏感字段已隐藏]",
clean,
flags=re.IGNORECASE,
)
clean = re.sub(
r"(?:pay_token|web_token|anti_token|session_id|sessionid)\s*[:= ]\s*\S+",
@@ -282,6 +285,7 @@ def _check_payment_once(job_id: str) -> int:
text=True,
env=environment,
timeout=90,
check=False,
)
except subprocess.TimeoutExpired:
_safe_log(
+6 -3
View File
@@ -175,18 +175,21 @@ class TestHuyaAppLogin:
side_effect=DfpRegistrationError("注册链超时"),
) as m_reg,
patch("core.huya.app_login.requests.post") as m_post,
pytest.raises(HuyaAppLoginError),
):
with pytest.raises(HuyaAppLoginError):
wup_password_login_raw("300023887", "pw")
m_reg.assert_called_once()
m_post.assert_not_called()
def test_login_cred_flow_registration_failure_is_explicit(self):
"""login_cred_with_flow 注册失败同样包装为 HuyaAppLoginError 显式失败。"""
with patch(
with (
patch(
"core.huya.app_login.register_device",
side_effect=DfpRegistrationError("注册链 HTTP 500"),
), pytest.raises(HuyaAppLoginError, match="注册失败"):
),
pytest.raises(HuyaAppLoginError, match="注册失败"),
):
login_cred_with_flow("300023887", "pw")
def test_router_functions(self):
+4 -3
View File
@@ -3,7 +3,7 @@
import gzip
import logging
import tempfile
from datetime import datetime, timedelta
from datetime import UTC, datetime, timedelta
from pathlib import Path
from utils.logger import (
@@ -40,7 +40,8 @@ class TestLogger:
archives = list(Path(tmpdir).glob("app-2026-08-28.log.*.gz"))
assert len(archives) == 1
archived_content = gzip.open(archives[0], "rt", encoding="utf-8").read()
with gzip.open(archives[0], "rt", encoding="utf-8") as archive:
archived_content = archive.read()
current_content = log_path.read_text(encoding="utf-8")
combined = archived_content + current_content
assert "super-secret" not in combined
@@ -50,7 +51,7 @@ class TestLogger:
def test_daily_log_switches_to_a_new_dated_file(self):
with tempfile.TemporaryDirectory() as tmpdir:
today = datetime.now().date()
today = datetime.now(UTC).date()
old_path = (
Path(tmpdir) / f"app-{(today - timedelta(days=1)).isoformat()}.log"
)
+11 -9
View File
@@ -8,7 +8,7 @@ import os
import re
import shutil
import sys
from datetime import datetime, timedelta
from datetime import UTC, datetime, timedelta
from logging.handlers import BaseRotatingHandler
from pathlib import Path
@@ -35,7 +35,9 @@ def _parse_positive_int(value: str | None, default: int) -> int:
def _parse_size(value: str | None, default: int = _DEFAULT_ROTATION_SIZE) -> int:
"""解析 50M、1GiB 等易读大小;非法值保持安全默认值。"""
matched = re.fullmatch(r"\s*(\d+)\s*([kmgt]?i?b?)?\s*", str(value or ""), re.IGNORECASE)
matched = re.fullmatch(
r"\s*(\d+)\s*([kmgt]?i?b?)?\s*", str(value or ""), re.IGNORECASE
)
if not matched:
return default
amount = int(matched.group(1))
@@ -101,7 +103,7 @@ class _SizeAndDayRotatingFileHandler(BaseRotatingHandler):
super().__init__(str(filename), "a", encoding="utf-8", delay=True)
self.max_bytes = max_bytes
self.retention_days = retention_days
self._active_day = datetime.now().date()
self._active_day = datetime.now(UTC).date()
path = Path(filename)
self._log_dir = path.parent
self._suffix = path.suffix
@@ -113,7 +115,7 @@ class _SizeAndDayRotatingFileHandler(BaseRotatingHandler):
)
def shouldRollover(self, record: logging.LogRecord) -> bool:
if datetime.now().date() != self._active_day:
if datetime.now(UTC).date() != self._active_day:
return True
if self.stream is None:
self.stream = self._open()
@@ -126,7 +128,7 @@ class _SizeAndDayRotatingFileHandler(BaseRotatingHandler):
self.stream.close()
self.stream = None
source = Path(self.baseFilename)
current_day = datetime.now().date()
current_day = datetime.now(UTC).date()
if current_day != self._active_day:
# 每日文件本身已带日期,跨日时直接切换到新文件,无需再移动旧文件。
self.baseFilename = os.fspath(self._path_for_day(current_day).resolve())
@@ -134,7 +136,7 @@ class _SizeAndDayRotatingFileHandler(BaseRotatingHandler):
self._delete_expired_archives()
return
if source.exists() and source.stat().st_size:
stamp = datetime.now().strftime("%Y-%m-%d_%H-%M-%S")
stamp = datetime.now(UTC).strftime("%Y-%m-%d_%H-%M-%S")
archive = source.with_name(f"{source.name}.{stamp}.{os.getpid()}.gz")
sequence = 1
while archive.exists():
@@ -148,10 +150,10 @@ class _SizeAndDayRotatingFileHandler(BaseRotatingHandler):
self._delete_expired_archives()
def _delete_expired_archives(self) -> None:
cutoff = datetime.now() - timedelta(days=self.retention_days)
cutoff = datetime.now(UTC) - timedelta(days=self.retention_days)
for archive in self._log_dir.glob(f"{self._filename_prefix}-*.log*.gz"):
try:
if datetime.fromtimestamp(archive.stat().st_mtime) < cutoff:
if datetime.fromtimestamp(archive.stat().st_mtime, UTC) < cutoff:
archive.unlink()
except OSError:
continue
@@ -226,7 +228,7 @@ def setup_logger(
if log_file:
file_path = Path(log_file).expanduser()
elif log_dir:
file_path = Path(log_dir).expanduser() / f"app-{datetime.now():%Y-%m-%d}.log"
file_path = Path(log_dir).expanduser() / f"app-{datetime.now(UTC):%Y-%m-%d}.log"
else:
return
file_path.parent.mkdir(parents=True, exist_ok=True)
+2 -2
View File
@@ -109,7 +109,7 @@ def decrypt_value(value: str | None) -> str | None:
for candidate in _key_candidates():
try:
return _decrypt_with_candidate(value, candidate)
except Exception as exc:
except Exception as exc: # noqa: BLE001 外部接口与任务边界需要保留宽泛异常兜底
last_error = exc
raise ValueError(
"敏感字段解密失败,请确认 APP_ENCRYPTION_KEY 是否正确"
@@ -124,7 +124,7 @@ def decrypt_value_with_key_name(value: str) -> tuple[str, str]:
for candidate in _key_candidates():
try:
return _decrypt_with_candidate(value, candidate), candidate.name
except Exception as exc:
except Exception as exc: # noqa: BLE001 外部接口与任务边界需要保留宽泛异常兜底
last_error = exc
raise ValueError(
"敏感字段解密失败,请确认 APP_ENCRYPTION_KEY 是否正确"
-1
View File
@@ -1,6 +1,5 @@
"""FastAPI 依赖注入"""
from fastapi import Depends, HTTPException, Request, WebSocket, status
from fastapi.security import OAuth2PasswordBearer
from jose import JWTError
+2 -2
View File
@@ -483,7 +483,7 @@ def _check_cookies(ids: str, db: Session, current: User, *, detailed: bool) -> d
for future in as_completed(futures):
try:
results.append(future.result())
except Exception as exc:
except Exception as exc: # noqa: BLE001 外部接口与任务边界需要保留宽泛异常兜底
task = futures[future]
results.append(
{
@@ -623,7 +623,7 @@ def _start_relogin_tasks(
def run_relogin_batch():
try:
runner.run()
except Exception as exc:
except Exception as exc: # noqa: BLE001 外部接口与任务边界需要保留宽泛异常兜底
runner._push_log("error", f"批量重新登录异常: {exc}")
message = f"重新登录异常: {exc}(旧 Cookie 已保留)"
for task_id in task_ids:
+5 -3
View File
@@ -852,12 +852,14 @@ def stop_auto_register_batch(
)
def retry_auto_register_batch(
batch_id: str,
req: HuyaAutoRegisterRetryRequest = HuyaAutoRegisterRetryRequest.model_construct(),
req: HuyaAutoRegisterRetryRequest | None = None,
db: Session = Depends(get_db),
current: User = Depends(get_current_user),
):
"""继续批次:默认从停止处往下跑(跳过成功与已失败);可传 mode 改行为。"""
_require_huya_perm(current, "huya:import")
if req is None:
req = HuyaAutoRegisterRetryRequest.model_construct()
use_proxy = req.use_proxy
# 先看历史批次是否用过代理
snapshot = huya_register_registry.get_snapshot(batch_id)
@@ -1075,7 +1077,7 @@ def password_login_selected_accounts(
"account": _account_out(account, include_cookie=include_cookie),
}
)
except Exception as exc:
except Exception as exc: # noqa: BLE001 外部接口与任务边界需要保留宽泛异常兜底
account.status = "login_failed"
account.updated_at = datetime.now(UTC)
db.commit()
@@ -1239,7 +1241,7 @@ def app_password_login_selected_accounts(
"account": _account_out(account, include_cookie=include_cookie),
}
)
except Exception as exc:
except Exception as exc: # noqa: BLE001 外部接口与任务边界需要保留宽泛异常兜底
account.status = "login_failed"
account.updated_at = datetime.now(UTC)
db.commit()
+4 -2
View File
@@ -58,8 +58,10 @@ async def create_batch(
acc = db.query(Account).filter(Account.id == aid).first()
if not acc:
continue
if not user_has_permission(current, "login:view_all"):
if acc.assigned_to != current.id:
if (
not user_has_permission(current, "login:view_all")
and acc.assigned_to != current.id
):
continue
valid_ids.append(aid)
+1 -1
View File
@@ -33,7 +33,7 @@ def hash_password(password: str) -> str:
def verify_password(plain: str, hashed: str) -> bool:
try:
return bcrypt.checkpw(plain.encode("utf-8")[:72], hashed.encode("utf-8"))
except Exception:
except Exception: # noqa: BLE001 外部接口与任务边界需要保留宽泛异常兜底
return False
@@ -258,7 +258,7 @@ class AccountCheckRunner:
stop_event=self._stop,
api_strategy=WgapiLoginAPI(),
).check_account()
except Exception as exc:
except Exception as exc: # noqa: BLE001 外部接口与任务边界需要保留宽泛异常兜底
self._set_item(
index, status="error", message=f"检测异常: {exc}", finished_at=_now()
)
@@ -319,7 +319,7 @@ class AccountCheckRunner:
with self._lock:
items = list(self.batch.items)
timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
timestamp = datetime.now(UTC).strftime("%Y%m%d_%H%M%S")
buffer = io.BytesIO()
with zipfile.ZipFile(buffer, "w", compression=zipfile.ZIP_DEFLATED) as zf:
for status in EXPORT_STATUS_ORDER:
@@ -367,7 +367,7 @@ class AccountCheckRunner:
for future in as_completed(futures):
future.result()
except Exception as exc:
except Exception as exc: # noqa: BLE001 外部接口与任务边界需要保留宽泛异常兜底
self._set_batch(
status="error", message=f"批次执行异常: {exc}", finished_at=_now()
)
+2 -2
View File
@@ -52,7 +52,7 @@ def check_douyu_cookie(cookie: str) -> dict:
if isinstance(fish_data, dict)
else "响应异常"
)
except Exception as exc:
except Exception as exc: # noqa: BLE001 外部接口与任务边界需要保留宽泛异常兜底
fish_msg = f"请求失败: {exc}"
level_ok = False
@@ -83,7 +83,7 @@ def check_douyu_cookie(cookie: str) -> dict:
if isinstance(level_data, dict)
else "响应异常"
)
except Exception as exc:
except Exception as exc: # noqa: BLE001 外部接口与任务边界需要保留宽泛异常兜底
level_msg = f"请求失败: {exc}"
valid = fish_ok and level_ok
+2 -2
View File
@@ -133,7 +133,7 @@ class DouyuBatchRunner(
if "task" in locals() and task:
self._mark_task(worker_db, task, "failed", str(exc))
self._push_log("warning", f"斗鱼任务失败: {exc}")
except Exception as exc:
except Exception as exc: # noqa: BLE001 外部接口与任务边界需要保留宽泛异常兜底
if "task" in locals() and task:
self._mark_task(worker_db, task, "error", str(exc))
self._push_log("error", f"斗鱼任务异常: {exc}")
@@ -177,7 +177,7 @@ class DouyuBatchRunner(
for future in as_completed(futures):
try:
future.result()
except Exception as exc:
except Exception as exc: # noqa: BLE001 外部接口与任务边界需要保留宽泛异常兜底
self._push_log("error", f"Worker 异常: {exc}")
if self._stop.is_set():
+2 -2
View File
@@ -386,9 +386,9 @@ class BindMixin:
]
if not bound:
return None
return sorted(
return min(
bound, key=lambda info: prefer.index(str(info.get("act_alias") or ""))
)[0]
)
def _pick_baseline_bind_info(
self,
+2 -2
View File
@@ -138,7 +138,7 @@ class DouyuBatchRunnerCore:
if proxy_url:
return {"http": proxy_url, "https": proxy_url}
self._push_log("warning", "代理 API 未返回可用代理, 本任务降级直连")
except Exception as exc:
except Exception as exc: # noqa: BLE001 外部接口与任务边界需要保留宽泛异常兜底
self._push_log("warning", f"取代理失败, 本任务降级直连: {exc}")
return None
@@ -193,7 +193,7 @@ class DouyuBatchRunnerCore:
return
try:
payload = douyu_task_payload(task)
except Exception:
except Exception: # noqa: BLE001 外部接口与任务边界需要保留宽泛异常兜底
logger.exception("[douyu] 推送任务状态失败: task_id={}", task.id)
return
event = {
+6 -6
View File
@@ -136,7 +136,7 @@ class DonateMixin:
)
baseline_points = baseline["esports_points"]
db.commit()
except Exception as exc:
except Exception as exc: # noqa: BLE001 外部接口与任务边界需要保留宽泛异常兜底
self._push_log("warning", f"赠送{gift_name}前刷新电竞积分失败: {exc}")
result = client.donate_esports_gift(
@@ -158,7 +158,7 @@ class DonateMixin:
refresh_errors = []
try:
result.update(self._refresh_account_gold_balance(client, account))
except Exception as exc:
except Exception as exc: # noqa: BLE001 外部接口与任务边界需要保留宽泛异常兜底
refresh_errors.append(f"鱼翅余额: {exc}")
try:
points_result = self._refresh_esports_handbook(
@@ -171,7 +171,7 @@ class DonateMixin:
and points_result["esports_points"] is not None
and points_result["esports_points"] != baseline_points
)
except Exception as exc:
except Exception as exc: # noqa: BLE001 外部接口与任务边界需要保留宽泛异常兜底
refresh_errors.append(f"电竞积分: {exc}")
if refresh_errors:
result["refresh_errors"] = refresh_errors
@@ -240,7 +240,7 @@ class DonateMixin:
)
db.commit()
baseline_points = baseline_result["points"]
except Exception as exc:
except Exception as exc: # noqa: BLE001 外部接口与任务边界需要保留宽泛异常兜底
self._push_log("warning", f"赠送精英令前刷新积分失败: {exc}")
result = client.donate_elite_gift(
gift_count=gift_count,
@@ -252,7 +252,7 @@ class DonateMixin:
refresh_errors = []
try:
result.update(self._refresh_account_gold_balance(client, account))
except Exception as exc:
except Exception as exc: # noqa: BLE001 外部接口与任务边界需要保留宽泛异常兜底
refresh_errors.append(f"鱼翅余额: {exc}")
try:
result.update(
@@ -268,7 +268,7 @@ class DonateMixin:
gift_count,
)
)
except Exception as exc:
except Exception as exc: # noqa: BLE001 外部接口与任务边界需要保留宽泛异常兜底
refresh_errors.append(f"积分: {exc}")
if refresh_errors:
result["refresh_errors"] = refresh_errors
+2 -2
View File
@@ -106,7 +106,7 @@ class GoldMixin:
f"鱼翅支付码已生成,等待到账(当前鱼翅 {last_gold if last_gold is not None else '-'}",
result,
)
except Exception as exc:
except Exception as exc: # noqa: BLE001 外部接口与任务边界需要保留宽泛异常兜底
poll_count += 1
result["payment_poll_count"] = poll_count
result["payment_poll_error"] = str(exc)
@@ -260,7 +260,7 @@ class GoldMixin:
baseline = self._refresh_account_gold_balance(client, account)
baseline_gold = baseline["gold_balance"]
db.commit()
except Exception as exc:
except Exception as exc: # noqa: BLE001 外部接口与任务边界需要保留宽泛异常兜底
self._push_log("warning", f"生成鱼翅码前刷新余额失败: {exc}")
result = client.create_gold_qr(
amount=amount, pay_type=int(config["gold_pay_type"])
+4 -4
View File
@@ -202,7 +202,7 @@ class GoodsMixin:
result.update(
self._refresh_account_points(client, account, cookie, ctn=ctn)
)
except Exception as exc:
except Exception as exc: # noqa: BLE001 外部接口与任务边界需要保留宽泛异常兜底
self._push_log("warning", f"支付锁单后刷新积分失败: {exc}")
self._mark_task(
db,
@@ -493,7 +493,7 @@ class GoodsMixin:
points_refresh = self._refresh_account_points(
client, account, cookie, ctn=ctn
)
except Exception as exc:
except Exception as exc: # noqa: BLE001 外部接口与任务边界需要保留宽泛异常兜底
self._push_log("warning", f"兑换后刷新积分失败: {exc}")
self._mark_task(
db,
@@ -549,7 +549,7 @@ class GoodsMixin:
)
baseline_points = baseline["esports_points"]
db.commit()
except Exception as exc:
except Exception as exc: # noqa: BLE001 外部接口与任务边界需要保留宽泛异常兜底
self._push_log("warning", f"兑换电竞皮肤前刷新积分失败: {exc}")
result = client.exchange_esports_goods(
@@ -571,7 +571,7 @@ class GoodsMixin:
)
result.update(points_result)
result["esports_points_after_exchange"] = points_result["esports_points"]
except Exception as exc:
except Exception as exc: # noqa: BLE001 外部接口与任务边界需要保留宽泛异常兜底
result["esports_points_refresh_error"] = str(exc)
account.esports_bind_status = "esports_goods_exchanged"
+3 -3
View File
@@ -95,7 +95,7 @@ class ManualMixin:
f"精英宝典支付码已生成,等待开通到账(当前积分 {last_points if last_points is not None else '-'}",
result,
)
except Exception as exc:
except Exception as exc: # noqa: BLE001 外部接口与任务边界需要保留宽泛异常兜底
poll_count += 1
result["payment_poll_count"] = poll_count
result["payment_poll_error"] = str(exc)
@@ -184,7 +184,7 @@ class ManualMixin:
f"积分 {last_manual_score if last_manual_score is not None else '-'}",
result,
)
except Exception as exc:
except Exception as exc: # noqa: BLE001 外部接口与任务边界需要保留宽泛异常兜底
poll_count += 1
result["payment_poll_count"] = poll_count
result["payment_poll_error"] = str(exc)
@@ -276,7 +276,7 @@ class ManualMixin:
baseline_manual_type = baseline_result["esports_manual_type"]
baseline_manual_score = baseline_result["esports_manual_score"]
db.commit()
except Exception as exc:
except Exception as exc: # noqa: BLE001 外部接口与任务边界需要保留宽泛异常兜底
self._push_log("warning", f"生成电竞手册支付码前查询活动状态失败: {exc}")
if baseline_manual_type is not None and baseline_manual_type >= 1:
+5 -5
View File
@@ -135,7 +135,7 @@ class XpdMixin:
result["before_role_name"] = str(before.get("role_name") or "")
result["before_area_name"] = str(before.get("area_name") or "")
result["before_plat_name"] = str(before.get("plat_name") or "")
except Exception:
except Exception: # noqa: BLE001 外部接口与任务边界需要保留宽泛异常兜底
result["before_bound"] = False
result["before_role_name"] = ""
result["bind_polling"] = True
@@ -208,7 +208,7 @@ class XpdMixin:
f"等待扫码绑定(第 {poll_count} 次)",
result,
)
except Exception as exc:
except Exception as exc: # noqa: BLE001 外部接口与任务边界需要保留宽泛异常兜底
poll_count += 1
result["bind_poll_count"] = poll_count
result["bind_poll_error"] = str(exc)
@@ -259,7 +259,7 @@ class XpdMixin:
else:
account.xpd_game_name = role_name
account.updated_at = datetime.now(UTC)
except Exception:
except Exception: # noqa: BLE001 外部接口与任务边界需要保留宽泛异常兜底
account.xpd_game_name = role_name
account.updated_at = datetime.now(UTC)
account.xpd_bind_status = "xpd_bound"
@@ -409,8 +409,8 @@ class XpdMixin:
plat = str(role_plat) if role_plat not in (None, "") else plat
areaid = str(role_area) or areaid
self._apply_xpd_role_to_account(account, role, role_area)
except Exception:
pass
except Exception as exc: # noqa: BLE001
self._push_log("debug", f"小店角色信息补全失败: {exc}")
if not openid or not roleid:
self._mark_task(
db, task, "failed", "未获取到小店绑定角色,请先生成二维码扫码绑定"
+2 -2
View File
@@ -690,7 +690,7 @@ class HuyaRegisterRunner:
)
try:
account_id, username, uid = self._save_success(index, result)
except Exception as exc:
except Exception as exc: # noqa: BLE001 外部接口与任务边界需要保留宽泛异常兜底
status = "error"
cookie = ""
full_cookie = ""
@@ -779,7 +779,7 @@ class HuyaRegisterRunner:
for future in as_completed(futures):
future.result()
except Exception as exc:
except Exception as exc: # noqa: BLE001 外部接口与任务边界需要保留宽泛异常兜底
with self._lock:
self.batch.status = "error"
self.batch.message = f"批次执行异常: {exc}"
+3 -3
View File
@@ -118,7 +118,7 @@ class HuyaBatchRunner(
self._push_log("success", f"[{current}] {name} {task.message}")
else:
self._push_log("error", f"[{current}] {name} {task.message}")
except Exception as exc:
except Exception as exc: # noqa: BLE001 外部接口与任务边界需要保留宽泛异常兜底
self._mark_task(worker_db, task, "error", f"执行异常: {exc}")
self._push_log("error", f"[{current}] {name} 执行异常: {exc}")
finally:
@@ -196,12 +196,12 @@ class HuyaBatchRunner(
for future in as_completed(futures):
try:
future.result()
except Exception as exc:
except Exception as exc: # noqa: BLE001 外部接口与任务边界需要保留宽泛异常兜底
self._push_log("error", f"虎牙 Worker 异常: {exc}")
self._push_log("info", f"虎牙批次 {self.batch_id} 完成")
self._push_log("result", "")
except Exception as exc:
except Exception as exc: # noqa: BLE001 外部接口与任务边界需要保留宽泛异常兜底
self._push_log("error", f"虎牙批次执行异常: {exc}")
self._push_log("result", "")
finally:
+1 -1
View File
@@ -90,7 +90,7 @@ class HuyaBatchRunnerCore:
def _format_local_time(timestamp: int) -> str:
if not timestamp:
return ""
return datetime.fromtimestamp(timestamp).strftime("%Y-%m-%d %H:%M:%S")
return datetime.fromtimestamp(timestamp, UTC).strftime("%Y-%m-%d %H:%M:%S")
@staticmethod
def _parse_scheduled_time(value) -> datetime | None:
+5 -2
View File
@@ -267,8 +267,11 @@ class GoodsMixin:
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):
if (
scheduled_at
and scheduled_at.timestamp() > time.time()
and not self._wait_until(scheduled_at, uid)
):
self._mark_task(worker_db, task, "stopped", "兑换任务已停止")
return
+1 -2
View File
@@ -348,8 +348,7 @@ def cleanup_orphan_huya_tasks(
query = db.query(HuyaTask).filter(HuyaTask.status.in_(statuses))
if batch_id:
query = query.filter(HuyaTask.batch_id == batch_id)
elif active_batch_ids is not None:
if active_batch_ids:
elif active_batch_ids is not None and active_batch_ids:
query = query.filter(~HuyaTask.batch_id.in_(list(active_batch_ids)))
# active_batch_ids is None 且未指定 batch_id:清理全部匹配状态
+10 -6
View File
@@ -372,7 +372,7 @@ class LoginBatchRunner:
f"[{current}] {acc_info['username']} {action_name}失败: {result.message}",
)
except Exception as e:
except Exception as e: # noqa: BLE001 外部接口与任务边界需要保留宽泛异常兜底
if self.mode == "relogin":
task.status = "relogin_failed"
task.message = f"重新登录异常: {e}(旧 Cookie 已保留)"
@@ -462,8 +462,10 @@ class LoginBatchRunner:
if not acc:
self._push_log("warning", f"跳过无账号的任务 #{task_id}")
continue
if "login:view_all" not in self.creator_permissions:
if acc.assigned_to != self.created_by:
if (
"login:view_all" not in self.creator_permissions
and acc.assigned_to != self.created_by
):
self._push_log("warning", f"跳过无权账号: {acc.username}")
continue
_append_task_info(task, acc)
@@ -482,8 +484,10 @@ class LoginBatchRunner:
if not acc:
continue
# 权限检查:客服只能跑分配给自己的
if "login:view_all" not in self.creator_permissions:
if acc.assigned_to != self.created_by:
if (
"login:view_all" not in self.creator_permissions
and acc.assigned_to != self.created_by
):
self._push_log("warning", f"跳过无权账号: {acc.username}")
continue
@@ -563,7 +567,7 @@ class LoginBatchRunner:
for future in as_completed(futures):
try:
future.result()
except Exception as e:
except Exception as e: # noqa: BLE001 外部接口与任务边界需要保留宽泛异常兜底
self._push_log("error", f"Worker 异常: {e}")
self._push_log("info", f"批量{action_name}任务 {batch_id} 完成")
+3 -3
View File
@@ -189,7 +189,7 @@ class ProxyService:
push("error", "未配置代理地址或API")
push("result", "")
except Exception as e:
except Exception as e: # noqa: BLE001 外部接口与任务边界需要保留宽泛异常兜底
push("error", f"测试异常: {e}")
push("result", "")
@@ -254,7 +254,7 @@ class ProxyService:
if whitelist_ip:
local_ip = whitelist_ip
push("info", f"从代理API获取到本机IP: {local_ip}")
except Exception as e:
except Exception as e: # noqa: BLE001 外部接口与任务边界需要保留宽泛异常兜底
push("warning", f"代理API请求失败: {e}")
if not local_ip:
@@ -293,7 +293,7 @@ class ProxyService:
push("success" if sync_ok else "error", f"白名单同步: {sync_msg}")
push("result", "")
except Exception as e:
except Exception as e: # noqa: BLE001 外部接口与任务边界需要保留宽泛异常兜底
push("error", f"测试异常: {e}")
push("result", "")