From 09ab80e062c47b4167591d1748ed8c46494cb814 Mon Sep 17 00:00:00 2001 From: yml2213 Date: Mon, 31 Aug 2026 10:55:44 +0800 Subject: [PATCH] =?UTF-8?q?=E5=AE=8C=E6=88=90=20Ruff=20=E5=85=A8=E9=87=8F?= =?UTF-8?q?=E6=B8=85=E7=90=86?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- TODO.md | 21 ++--- core/douyu/cookie_enricher.py | 2 +- core/douyu/email_verifier.py | 30 ++++---- core/douyu/login.py | 8 +- core/douyu/proxy_platforms/base.py | 8 +- core/douyu/proxy_platforms/xiequ.py | 8 +- core/douyu/proxy_platforms/xkdaili.py | 6 +- core/douyu/proxy_resolver.py | 2 +- core/douyu/proxy_verifier.py | 8 +- core/douyu/proxy_whitelist.py | 1 - core/geetest/common/network.py | 14 ++-- core/geetest/common/trajectory.py | 2 +- core/geetest/v3_slide/solver.py | 4 +- core/huya/activity_structs.py | 6 +- core/huya/app_login.py | 12 +-- core/huya/auto_register.py | 8 +- core/huya/change_password.py | 4 +- core/huya/device_profile.py | 14 ++-- core/huya/envelope_forge.py | 6 +- core/huya/frame_decoder.py | 21 ++--- core/huya/http_client.py | 14 ++-- core/huya/login.py | 7 +- core/huya/shop_structs.py | 5 +- core/huya/taf_protocol.py | 6 +- core/huya/verification/ocr.py | 2 +- core/huya/verification/solver.py | 6 +- core/huya/wss_client.py | 26 ++++--- core/huya/wup_protocol.py | 6 +- core/sms_provider.py | 4 +- pyproject.toml | 4 + scripts/migrate_sqlite_to_mysql.py | 2 +- services/yyb-worker/runtime/main.py | 20 +++-- services/yyb-worker/runtime/pyvm/algorithm.py | 38 +++++----- services/yyb-worker/runtime/pyvm/mall.py | 10 ++- .../yyb-worker/runtime/pyvm/order_status.py | 4 +- .../yyb-worker/runtime/pyvm/pagedoo_vm.py | 76 +++++++++---------- services/yyb-worker/runtime/pyvm/protocol.py | 4 +- .../yyb-worker/runtime/scripts/jsdom-pay.py | 0 .../yyb-worker/runtime/scripts/login-qq.py | 0 .../runtime/scripts/login-wechat.py | 0 .../runtime/scripts/select-peace-elite.py | 0 .../yyb-worker/runtime/scripts/yyb-worker.py | 6 +- tests/test_huya_app_login.py | 15 ++-- tests/test_logger.py | 7 +- utils/logger.py | 20 ++--- web/backend/crypto_storage.py | 4 +- web/backend/deps.py | 1 - web/backend/routers/cookies.py | 4 +- web/backend/routers/huya.py | 8 +- web/backend/routers/login.py | 8 +- web/backend/security.py | 2 +- web/backend/services/account_check_service.py | 6 +- web/backend/services/cookie_check_service.py | 4 +- web/backend/services/douyu_runner.py | 4 +- web/backend/services/douyu_runner_bind.py | 4 +- web/backend/services/douyu_runner_core.py | 4 +- web/backend/services/douyu_runner_donate.py | 12 +-- web/backend/services/douyu_runner_gold.py | 4 +- web/backend/services/douyu_runner_goods.py | 8 +- web/backend/services/douyu_runner_manual.py | 6 +- web/backend/services/douyu_runner_xpd.py | 10 +-- web/backend/services/huya_register_runner.py | 4 +- web/backend/services/huya_runner.py | 6 +- web/backend/services/huya_runner_core.py | 2 +- web/backend/services/huya_runner_goods.py | 11 ++- web/backend/services/huya_service.py | 5 +- web/backend/services/login_service.py | 24 +++--- web/backend/services/proxy_service.py | 6 +- 68 files changed, 323 insertions(+), 291 deletions(-) mode change 100644 => 100755 services/yyb-worker/runtime/main.py mode change 100644 => 100755 services/yyb-worker/runtime/scripts/jsdom-pay.py mode change 100644 => 100755 services/yyb-worker/runtime/scripts/login-qq.py mode change 100644 => 100755 services/yyb-worker/runtime/scripts/login-wechat.py mode change 100644 => 100755 services/yyb-worker/runtime/scripts/select-peace-elite.py mode change 100644 => 100755 services/yyb-worker/runtime/scripts/yyb-worker.py diff --git a/TODO.md b/TODO.md index 7102d54..7ec70fe 100644 --- a/TODO.md +++ b/TODO.md @@ -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 lint:1263 条 -- Ruff format:207 个文件全部通过 -- pytest:104 passed +- Ruff lint:0 条 +- Ruff format:208 个文件全部通过 +- pytest:105 passed - Pyright:0 errors / 0 warnings +- Python compileall:通过 diff --git a/core/douyu/cookie_enricher.py b/core/douyu/cookie_enricher.py index efaeb2e..80006bf 100644 --- a/core/douyu/cookie_enricher.py +++ b/core/douyu/cookie_enricher.py @@ -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: diff --git a/core/douyu/email_verifier.py b/core/douyu/email_verifier.py index b28882a..e112a0c 100644 --- a/core/douyu/email_verifier.py +++ b/core/douyu/email_verifier.py @@ -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}") diff --git a/core/douyu/login.py b/core/douyu/login.py index 605f993..3848f61 100644 --- a/core/douyu/login.py +++ b/core/douyu/login.py @@ -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}" diff --git a/core/douyu/proxy_platforms/base.py b/core/douyu/proxy_platforms/base.py index 30d3f96..68fa21a 100644 --- a/core/douyu/proxy_platforms/base.py +++ b/core/douyu/proxy_platforms/base.py @@ -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 diff --git a/core/douyu/proxy_platforms/xiequ.py b/core/douyu/proxy_platforms/xiequ.py index 6d09be0..db6cc6b 100644 --- a/core/douyu/proxy_platforms/xiequ.py +++ b/core/douyu/proxy_platforms/xiequ.py @@ -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 diff --git a/core/douyu/proxy_platforms/xkdaili.py b/core/douyu/proxy_platforms/xkdaili.py index c5017c4..b53addd 100644 --- a/core/douyu/proxy_platforms/xkdaili.py +++ b/core/douyu/proxy_platforms/xkdaili.py @@ -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}" diff --git a/core/douyu/proxy_resolver.py b/core/douyu/proxy_resolver.py index ade9eff..28b6295 100644 --- a/core/douyu/proxy_resolver.py +++ b/core/douyu/proxy_resolver.py @@ -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}") diff --git a/core/douyu/proxy_verifier.py b/core/douyu/proxy_verifier.py index 8f50ffe..9f2affc 100644 --- a/core/douyu/proxy_verifier.py +++ b/core/douyu/proxy_verifier.py @@ -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)} 个代理均不可用" diff --git a/core/douyu/proxy_whitelist.py b/core/douyu/proxy_whitelist.py index 3a2d5f8..1e2ed07 100644 --- a/core/douyu/proxy_whitelist.py +++ b/core/douyu/proxy_whitelist.py @@ -1,6 +1,5 @@ """代理模块使用的白名单适配器。""" - from .proxy_platforms import create_adapter from .proxy_platforms.base import BaseWhitelistAdapter, _get_local_exit_ip diff --git a/core/geetest/common/network.py b/core/geetest/common/network.py index a1dc848..04d8eae 100644 --- a/core/geetest/common/network.py +++ b/core/geetest/common/network.py @@ -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, ) diff --git a/core/geetest/common/trajectory.py b/core/geetest/common/trajectory.py index 6fde972..6a7d757 100644 --- a/core/geetest/common/trajectory.py +++ b/core/geetest/common/trajectory.py @@ -592,7 +592,7 @@ def H(t: int, e: str) -> str: n = 36 * r[0] + r[1] # 计算目标值 - a = round(t) + n + a = (t) + n # 构建字符池 _ = [[], [], [], [], []] diff --git a/core/geetest/v3_slide/solver.py b/core/geetest/v3_slide/solver.py index 0a0da31..dc9f1ef 100644 --- a/core/geetest/v3_slide/solver.py +++ b/core/geetest/v3_slide/solver.py @@ -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) diff --git a/core/huya/activity_structs.py b/core/huya/activity_structs.py index 6b5afee..6d3033a 100644 --- a/core/huya/activity_structs.py +++ b/core/huya/activity_structs.py @@ -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 diff --git a/core/huya/app_login.py b/core/huya/app_login.py index 967a3d3..bbaed5d 100644 --- a/core/huya/app_login.py +++ b/core/huya/app_login.py @@ -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}", diff --git a/core/huya/auto_register.py b/core/huya/auto_register.py index e8eff76..1b31af1 100644 --- a/core/huya/auto_register.py +++ b/core/huya/auto_register.py @@ -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, diff --git a/core/huya/change_password.py b/core/huya/change_password.py index 54ed701..1481c15 100644 --- a/core/huya/change_password.py +++ b/core/huya/change_password.py @@ -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( diff --git a/core/huya/device_profile.py b/core/huya/device_profile.py index 8b026e2..41d4b0f 100644 --- a/core/huya/device_profile.py +++ b/core/huya/device_profile.py @@ -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]: diff --git a/core/huya/envelope_forge.py b/core/huya/envelope_forge.py index 856a82f..f103a7e 100644 --- a/core/huya/envelope_forge.py +++ b/core/huya/envelope_forge.py @@ -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 diff --git a/core/huya/frame_decoder.py b/core/huya/frame_decoder.py index 3b7e5f6..3b6292c 100644 --- a/core/huya/frame_decoder.py +++ b/core/huya/frame_decoder.py @@ -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"" 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)" diff --git a/core/huya/http_client.py b/core/huya/http_client.py index daae9d4..da54621 100644 --- a/core/huya/http_client.py +++ b/core/huya/http_client.py @@ -92,7 +92,7 @@ class WSConnectParaInfo(TafStruct): def _gen_trace_id() -> str: """生成 sTraceId (格式 hex8:hex8:0:0,HAR 实证)""" - 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 diff --git a/core/huya/login.py b/core/huya/login.py index 8449acb..7edb24e 100644 --- a/core/huya/login.py +++ b/core/huya/login.py @@ -380,10 +380,9 @@ class HuyaPasswordLogin: last_exc: Exception | None = None for attempt in range(max_ip_retries + 1): - if attempt > 0: - if not self._swap_proxy(): - logger.warning("无可用代理可切换,停止换 IP 重试") - break + if attempt > 0 and not self._swap_proxy(): + logger.warning("无可用代理可切换,停止换 IP 重试") + break try: return self._login_once() except (HuyaQrAuthRequiredError, HuyaSmsAuthRequiredError) as exc: diff --git a/core/huya/shop_structs.py b/core/huya/shop_structs.py index 2f3405e..7ddf8e3 100644 --- a/core/huya/shop_structs.py +++ b/core/huya/shop_structs.py @@ -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]: diff --git a/core/huya/taf_protocol.py b/core/huya/taf_protocol.py index 410d3e7..e50d73c 100644 --- a/core/huya/taf_protocol.py +++ b/core/huya/taf_protocol.py @@ -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 diff --git a/core/huya/verification/ocr.py b/core/huya/verification/ocr.py index d909136..6f13f97 100644 --- a/core/huya/verification/ocr.py +++ b/core/huya/verification/ocr.py @@ -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) diff --git a/core/huya/verification/solver.py b/core/huya/verification/solver.py index cd74bf7..c8a75f5 100644 --- a/core/huya/verification/solver.py +++ b/core/huya/verification/solver.py @@ -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 才是扫码, diff --git a/core/huya/wss_client.py b/core/huya/wss_client.py index ec2f443..43f5964 100644 --- a/core/huya/wss_client.py +++ b/core/huya/wss_client.py @@ -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) diff --git a/core/huya/wup_protocol.py b/core/huya/wup_protocol.py index 002b481..e7586a3 100644 --- a/core/huya/wup_protocol.py +++ b/core/huya/wup_protocol.py @@ -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 diff --git a/core/sms_provider.py b/core/sms_provider.py index 8d8d583..95fa7c0 100644 --- a/core/sms_provider.py +++ b/core/sms_provider.py @@ -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 diff --git a/pyproject.toml b/pyproject.toml index 304507d..3cb21e2 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -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", diff --git a/scripts/migrate_sqlite_to_mysql.py b/scripts/migrate_sqlite_to_mysql.py index 44fb8b5..183dcc8 100644 --- a/scripts/migrate_sqlite_to_mysql.py +++ b/scripts/migrate_sqlite_to_mysql.py @@ -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) diff --git a/services/yyb-worker/runtime/main.py b/services/yyb-worker/runtime/main.py old mode 100644 new mode 100755 index 7b77106..79213b1 --- a/services/yyb-worker/runtime/main.py +++ b/services/yyb-worker/runtime/main.py @@ -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 diff --git a/services/yyb-worker/runtime/pyvm/algorithm.py b/services/yyb-worker/runtime/pyvm/algorithm.py index 23e8409..87fe0ca 100644 --- a/services/yyb-worker/runtime/pyvm/algorithm.py +++ b/services/yyb-worker/runtime/pyvm/algorithm.py @@ -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 diff --git a/services/yyb-worker/runtime/pyvm/mall.py b/services/yyb-worker/runtime/pyvm/mall.py index 2009f6b..e657f25 100644 --- a/services/yyb-worker/runtime/pyvm/mall.py +++ b/services/yyb-worker/runtime/pyvm/mall.py @@ -72,9 +72,13 @@ 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: - raise ValueError(f"624B 中间态长度 != 624: {len(mid[0])}") + 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 def from_capture_file(cls, frames_jsonl: str | Path) -> MallSession: diff --git a/services/yyb-worker/runtime/pyvm/order_status.py b/services/yyb-worker/runtime/pyvm/order_status.py index 8bd5691..1152ee7 100644 --- a/services/yyb-worker/runtime/pyvm/order_status.py +++ b/services/yyb-worker/runtime/pyvm/order_status.py @@ -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 diff --git a/services/yyb-worker/runtime/pyvm/pagedoo_vm.py b/services/yyb-worker/runtime/pyvm/pagedoo_vm.py index a832c0e..7bcd262 100644 --- a/services/yyb-worker/runtime/pyvm/pagedoo_vm.py +++ b/services/yyb-worker/runtime/pyvm/pagedoo_vm.py @@ -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,30 +308,29 @@ 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 key == "length": - return len(obj) - if key == "charCodeAt": - return HostFunction(h_char_code_at, "charCodeAt") - if key == "charAt": - return HostFunction(h_char_at, "charAt") - if key == "indexOf": - return HostFunction(h_str_indexof, "indexOf") - if key == "slice": - return HostFunction(h_str_slice, "slice") - if key == "split": - return HostFunction(h_str_split, "split") - if key == "toLowerCase": - return HostFunction(h_str_tolower, "toLowerCase") - if key == "toUpperCase": - return HostFunction(h_str_toupper, "toUpperCase") - if key == "toString": - return HostFunction(h_str_tostring, "toString") - if key == "substr": - return HostFunction(h_str_substr, "substr") - if key == "substring": - return HostFunction(h_str_substring, "substring") + if isinstance(obj, str) and isinstance(key, str): + if key == "length": + return len(obj) + if key == "charCodeAt": + return HostFunction(h_char_code_at, "charCodeAt") + if key == "charAt": + return HostFunction(h_char_at, "charAt") + if key == "indexOf": + return HostFunction(h_str_indexof, "indexOf") + if key == "slice": + return HostFunction(h_str_slice, "slice") + if key == "split": + return HostFunction(h_str_split, "split") + if key == "toLowerCase": + return HostFunction(h_str_tolower, "toLowerCase") + if key == "toUpperCase": + return HostFunction(h_str_toupper, "toUpperCase") + if key == "toString": + return HostFunction(h_str_tostring, "toString") + if key == "substr": + return HostFunction(h_str_substr, "substr") + if key == "substring": + return HostFunction(h_str_substring, "substring") return js_index(obj, key) @@ -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 diff --git a/services/yyb-worker/runtime/pyvm/protocol.py b/services/yyb-worker/runtime/pyvm/protocol.py index 814c888..9620d1b 100644 --- a/services/yyb-worker/runtime/pyvm/protocol.py +++ b/services/yyb-worker/runtime/pyvm/protocol.py @@ -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( diff --git a/services/yyb-worker/runtime/scripts/jsdom-pay.py b/services/yyb-worker/runtime/scripts/jsdom-pay.py old mode 100644 new mode 100755 diff --git a/services/yyb-worker/runtime/scripts/login-qq.py b/services/yyb-worker/runtime/scripts/login-qq.py old mode 100644 new mode 100755 diff --git a/services/yyb-worker/runtime/scripts/login-wechat.py b/services/yyb-worker/runtime/scripts/login-wechat.py old mode 100644 new mode 100755 diff --git a/services/yyb-worker/runtime/scripts/select-peace-elite.py b/services/yyb-worker/runtime/scripts/select-peace-elite.py old mode 100644 new mode 100755 diff --git a/services/yyb-worker/runtime/scripts/yyb-worker.py b/services/yyb-worker/runtime/scripts/yyb-worker.py old mode 100644 new mode 100755 index 08c6e57..b40d54f --- a/services/yyb-worker/runtime/scripts/yyb-worker.py +++ b/services/yyb-worker/runtime/scripts/yyb-worker.py @@ -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( diff --git a/tests/test_huya_app_login.py b/tests/test_huya_app_login.py index e2ac958..9bcae06 100644 --- a/tests/test_huya_app_login.py +++ b/tests/test_huya_app_login.py @@ -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") + 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( - "core.huya.app_login.register_device", - side_effect=DfpRegistrationError("注册链 HTTP 500"), - ), pytest.raises(HuyaAppLoginError, match="注册失败"): + with ( + patch( + "core.huya.app_login.register_device", + side_effect=DfpRegistrationError("注册链 HTTP 500"), + ), + pytest.raises(HuyaAppLoginError, match="注册失败"), + ): login_cred_with_flow("300023887", "pw") def test_router_functions(self): diff --git a/tests/test_logger.py b/tests/test_logger.py index 2aea49f..46f92f5 100644 --- a/tests/test_logger.py +++ b/tests/test_logger.py @@ -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" ) diff --git a/utils/logger.py b/utils/logger.py index c863e9a..8577a78 100644 --- a/utils/logger.py +++ b/utils/logger.py @@ -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) diff --git a/web/backend/crypto_storage.py b/web/backend/crypto_storage.py index 0711883..eed80f1 100644 --- a/web/backend/crypto_storage.py +++ b/web/backend/crypto_storage.py @@ -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 是否正确" diff --git a/web/backend/deps.py b/web/backend/deps.py index 20d5faa..f976025 100644 --- a/web/backend/deps.py +++ b/web/backend/deps.py @@ -1,6 +1,5 @@ """FastAPI 依赖注入""" - from fastapi import Depends, HTTPException, Request, WebSocket, status from fastapi.security import OAuth2PasswordBearer from jose import JWTError diff --git a/web/backend/routers/cookies.py b/web/backend/routers/cookies.py index 9eccc7f..70d3a86 100644 --- a/web/backend/routers/cookies.py +++ b/web/backend/routers/cookies.py @@ -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: diff --git a/web/backend/routers/huya.py b/web/backend/routers/huya.py index 3bfb7e5..377d078 100644 --- a/web/backend/routers/huya.py +++ b/web/backend/routers/huya.py @@ -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() diff --git a/web/backend/routers/login.py b/web/backend/routers/login.py index b45ff1e..74c0530 100644 --- a/web/backend/routers/login.py +++ b/web/backend/routers/login.py @@ -58,9 +58,11 @@ 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: - continue + if ( + not user_has_permission(current, "login:view_all") + and acc.assigned_to != current.id + ): + continue valid_ids.append(aid) if not valid_ids: diff --git a/web/backend/security.py b/web/backend/security.py index 05927a3..af17e15 100644 --- a/web/backend/security.py +++ b/web/backend/security.py @@ -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 diff --git a/web/backend/services/account_check_service.py b/web/backend/services/account_check_service.py index 1a8e78b..0e50bac 100644 --- a/web/backend/services/account_check_service.py +++ b/web/backend/services/account_check_service.py @@ -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() ) diff --git a/web/backend/services/cookie_check_service.py b/web/backend/services/cookie_check_service.py index d2be2e5..6f7b8b8 100644 --- a/web/backend/services/cookie_check_service.py +++ b/web/backend/services/cookie_check_service.py @@ -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 diff --git a/web/backend/services/douyu_runner.py b/web/backend/services/douyu_runner.py index c811e81..0b73a63 100644 --- a/web/backend/services/douyu_runner.py +++ b/web/backend/services/douyu_runner.py @@ -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(): diff --git a/web/backend/services/douyu_runner_bind.py b/web/backend/services/douyu_runner_bind.py index 6fe7a5f..1923c3f 100644 --- a/web/backend/services/douyu_runner_bind.py +++ b/web/backend/services/douyu_runner_bind.py @@ -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, diff --git a/web/backend/services/douyu_runner_core.py b/web/backend/services/douyu_runner_core.py index da9714e..4019b29 100644 --- a/web/backend/services/douyu_runner_core.py +++ b/web/backend/services/douyu_runner_core.py @@ -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 = { diff --git a/web/backend/services/douyu_runner_donate.py b/web/backend/services/douyu_runner_donate.py index 10a9b2d..26ba8d2 100644 --- a/web/backend/services/douyu_runner_donate.py +++ b/web/backend/services/douyu_runner_donate.py @@ -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 diff --git a/web/backend/services/douyu_runner_gold.py b/web/backend/services/douyu_runner_gold.py index 9434ca7..c3b5850 100644 --- a/web/backend/services/douyu_runner_gold.py +++ b/web/backend/services/douyu_runner_gold.py @@ -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"]) diff --git a/web/backend/services/douyu_runner_goods.py b/web/backend/services/douyu_runner_goods.py index cac37c7..0a3f477 100644 --- a/web/backend/services/douyu_runner_goods.py +++ b/web/backend/services/douyu_runner_goods.py @@ -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" diff --git a/web/backend/services/douyu_runner_manual.py b/web/backend/services/douyu_runner_manual.py index 95205db..41338fc 100644 --- a/web/backend/services/douyu_runner_manual.py +++ b/web/backend/services/douyu_runner_manual.py @@ -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: diff --git a/web/backend/services/douyu_runner_xpd.py b/web/backend/services/douyu_runner_xpd.py index ea853cd..68a90d7 100644 --- a/web/backend/services/douyu_runner_xpd.py +++ b/web/backend/services/douyu_runner_xpd.py @@ -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", "未获取到小店绑定角色,请先生成二维码扫码绑定" diff --git a/web/backend/services/huya_register_runner.py b/web/backend/services/huya_register_runner.py index a43ab5c..db63e91 100644 --- a/web/backend/services/huya_register_runner.py +++ b/web/backend/services/huya_register_runner.py @@ -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}" diff --git a/web/backend/services/huya_runner.py b/web/backend/services/huya_runner.py index 46ffec6..85c9636 100644 --- a/web/backend/services/huya_runner.py +++ b/web/backend/services/huya_runner.py @@ -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: diff --git a/web/backend/services/huya_runner_core.py b/web/backend/services/huya_runner_core.py index 138a373..5ed541c 100644 --- a/web/backend/services/huya_runner_core.py +++ b/web/backend/services/huya_runner_core.py @@ -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: diff --git a/web/backend/services/huya_runner_goods.py b/web/backend/services/huya_runner_goods.py index 9a3abb3..3070122 100644 --- a/web/backend/services/huya_runner_goods.py +++ b/web/backend/services/huya_runner_goods.py @@ -267,10 +267,13 @@ 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): - self._mark_task(worker_db, task, "stopped", "兑换任务已停止") - return + 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 client: Any = HuyaHttpClient( logger=lambda msg: self._push_log("info", f"[{uid}] {msg}") diff --git a/web/backend/services/huya_service.py b/web/backend/services/huya_service.py index 66fc163..f442c85 100644 --- a/web/backend/services/huya_service.py +++ b/web/backend/services/huya_service.py @@ -348,9 +348,8 @@ 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: - query = query.filter(~HuyaTask.batch_id.in_(list(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:清理全部匹配状态 tasks = query.all() diff --git a/web/backend/services/login_service.py b/web/backend/services/login_service.py index 207eb7a..e516aec 100644 --- a/web/backend/services/login_service.py +++ b/web/backend/services/login_service.py @@ -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,10 +462,12 @@ 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: - self._push_log("warning", f"跳过无权账号: {acc.username}") - continue + 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) else: seen_account_ids = set() @@ -482,10 +484,12 @@ class LoginBatchRunner: if not acc: continue # 权限检查:客服只能跑分配给自己的 - if "login:view_all" not in self.creator_permissions: - if acc.assigned_to != self.created_by: - self._push_log("warning", f"跳过无权账号: {acc.username}") - continue + if ( + "login:view_all" not in self.creator_permissions + and acc.assigned_to != self.created_by + ): + self._push_log("warning", f"跳过无权账号: {acc.username}") + continue # 一个斗鱼账号只保留一条成功 CK:再次普通登录时更新最新成功记录。 latest_success_task = ( @@ -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} 完成") diff --git a/web/backend/services/proxy_service.py b/web/backend/services/proxy_service.py index 5a576fe..7afb1ea 100644 --- a/web/backend/services/proxy_service.py +++ b/web/backend/services/proxy_service.py @@ -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", "")