From 239ef91c56d9d44a9e67d16db465e69800428145 Mon Sep 17 00:00:00 2001 From: yml2213 Date: Sat, 29 Aug 2026 16:59:16 +0800 Subject: [PATCH] =?UTF-8?q?fix(web):=20=E8=AE=BE=E5=A4=87=E7=BB=91?= =?UTF-8?q?=E5=AE=9A=E9=A1=B5=E3=80=8E=E6=9C=80=E5=90=8E=E7=99=BB=E5=BD=95?= =?UTF-8?q?=E3=80=8F=E4=BF=AE=E5=A4=8D=20=E2=80=94=20=E7=99=BB=E5=BD=95?= =?UTF-8?q?=E6=97=B6=E9=97=B4=E8=AE=B0=E5=BD=95=E4=B8=8B=E6=B2=89=E5=88=B0?= =?UTF-8?q?=20App=20=E7=99=BB=E5=BD=95=E6=9C=AC=E4=BD=93?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - device_profile.record_login(): 登录成功/失败均写 last_login (bound_at 首次绑定) - HuyaAppPasswordLogin.login() 包装 _login_impl, GUI 批量/行内/CLI 全覆盖 (此前只有 account_env CLI 路径写元数据, GUI 登录该列全为 '-') - 修复 _enrich_profile 原地修改导致回写判断失效 (存量 hebe/guid32 被旧代码 整体回写冲掉后无法自动补齐) — 改为返回新字典 - 存量 6 账号画像已全部补齐 hebe/guid32 --- core/huya/app_login.py | 12 ++++++++++- core/huya/device_profile.py | 42 ++++++++++++++++++++++++++----------- 2 files changed, 41 insertions(+), 13 deletions(-) diff --git a/core/huya/app_login.py b/core/huya/app_login.py index afe29d4..b638331 100644 --- a/core/huya/app_login.py +++ b/core/huya/app_login.py @@ -392,7 +392,17 @@ class HuyaAppPasswordLogin: self.device_info = device_info or get_profile(self.username, force_new=force_new_device) def login(self) -> HuyaLoginResult: - """执行完整 App 登录获取 Cookie 流程。""" + """执行完整 App 登录获取 Cookie 流程 (成功/失败均记录登录时间 → 设备绑定页)。""" + result = self._login_impl() + try: + from .device_profile import record_login + record_login(self.username, result.success, result.message) + except Exception: # 元数据记录失败不影响登录结果 + pass + return result + + def _login_impl(self) -> HuyaLoginResult: + """登录主体 (原 login)。""" acct = self.username logger.info(f"[huya-app] 开始登录账号 {acct} (机型: {self.device_info.get('model')})...") diff --git a/core/huya/device_profile.py b/core/huya/device_profile.py index 07254a5..2d502c4 100644 --- a/core/huya/device_profile.py +++ b/core/huya/device_profile.py @@ -78,6 +78,23 @@ def generate_profile(model_pick=None) -> dict: } +def record_login(account: str, ok: bool, message: str = "") -> None: + """记录该账号环境的一次登录结果 (最后登录时间/成败)。 + + 幂等元数据: bound_at 只在首次出现时写入; 失败也记录 (设备绑定页要展示)。 + 供 app_login 登录流程调用, GUI 设备绑定页读取展示。 + """ + import time as _time + db = _load_db() + rec = db.get(account) + if rec is None: + return # 尚无环境的账号 (纯 Cookie 导入) 不生成记录 + now = int(_time.time()) + rec.setdefault("bound_at", now) + rec["last_login"] = {"ok": bool(ok), "msg": str(message or "")[:120], "at": now} + _save_db(db) + + def _load_db() -> dict: if PRIMARY_PROFILE_DB.exists(): try: @@ -100,34 +117,35 @@ def _save_db(db: dict) -> None: pass -def _enrich_profile(profile: dict) -> dict: - """补齐 dfp 一致性字段 (幂等): guid32 / Hebe_D1-D5。 +def _enrich_profile(profile: dict) -> tuple[dict, bool]: + """补齐 dfp 一致性字段 (幂等): guid32 / Hebe_D1-D5, 返回 (新画像, 是否有变更)。 来源: R36 解密的真实 dfp 指纹 JSON 结构 (deviceinfo.guid + deviceinfo.Hebe_D1-D5)。 一号一设备: 每账号独立生成后终身不变, 供后续真实载荷构建与 GUI 设备绑定页展示。 + 注意: 返回新字典 (不原地改), 调用方据此判断是否需要回写。 """ + out = dict(profile) changed = False - if not profile.get("guid32"): - profile["guid32"] = hashlib.sha256(os.urandom(16) + b"guid").hexdigest() + if not out.get("guid32"): + out["guid32"] = hashlib.sha256(os.urandom(16) + b"guid").hexdigest() changed = True - hebe = profile.get("hebe") or {} - if len(hebe) < 5: - profile["hebe"] = {f"Hebe_D{i}": hashlib.sha256(os.urandom(16) + f"hebe{i}".encode()).hexdigest() - for i in range(1, 6)} + if len(out.get("hebe") or {}) < 5: + out["hebe"] = {f"Hebe_D{i}": hashlib.sha256(os.urandom(16) + f"hebe{i}".encode()).hexdigest() + for i in range(1, 6)} changed = True - return profile if changed else profile + return out, changed def get_profile(account: str, force_new: bool = False) -> dict: """按账号获取或创建画像(幂等:同账号复用同一套, 自动补齐缺失一致性字段)。""" db = _load_db() if not force_new and account in db: - enriched = _enrich_profile(db[account]) - if enriched is not db[account]: + enriched, changed = _enrich_profile(db[account]) + if changed: db[account] = enriched _save_db(db) return enriched - p = _enrich_profile(generate_profile()) + p, _ = _enrich_profile(generate_profile()) db[account] = p _save_db(db) return p