fix(web): 设备绑定页『最后登录』修复 — 登录时间记录下沉到 App 登录本体
- 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
This commit is contained in:
+11
-1
@@ -392,7 +392,17 @@ class HuyaAppPasswordLogin:
|
|||||||
self.device_info = device_info or get_profile(self.username, force_new=force_new_device)
|
self.device_info = device_info or get_profile(self.username, force_new=force_new_device)
|
||||||
|
|
||||||
def login(self) -> HuyaLoginResult:
|
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
|
acct = self.username
|
||||||
logger.info(f"[huya-app] 开始登录账号 {acct} (机型: {self.device_info.get('model')})...")
|
logger.info(f"[huya-app] 开始登录账号 {acct} (机型: {self.device_info.get('model')})...")
|
||||||
|
|
||||||
|
|||||||
+30
-12
@@ -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:
|
def _load_db() -> dict:
|
||||||
if PRIMARY_PROFILE_DB.exists():
|
if PRIMARY_PROFILE_DB.exists():
|
||||||
try:
|
try:
|
||||||
@@ -100,34 +117,35 @@ def _save_db(db: dict) -> None:
|
|||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
def _enrich_profile(profile: dict) -> dict:
|
def _enrich_profile(profile: dict) -> tuple[dict, bool]:
|
||||||
"""补齐 dfp 一致性字段 (幂等): guid32 / Hebe_D1-D5。
|
"""补齐 dfp 一致性字段 (幂等): guid32 / Hebe_D1-D5, 返回 (新画像, 是否有变更)。
|
||||||
|
|
||||||
来源: R36 解密的真实 dfp 指纹 JSON 结构 (deviceinfo.guid + deviceinfo.Hebe_D1-D5)。
|
来源: R36 解密的真实 dfp 指纹 JSON 结构 (deviceinfo.guid + deviceinfo.Hebe_D1-D5)。
|
||||||
一号一设备: 每账号独立生成后终身不变, 供后续真实载荷构建与 GUI 设备绑定页展示。
|
一号一设备: 每账号独立生成后终身不变, 供后续真实载荷构建与 GUI 设备绑定页展示。
|
||||||
|
注意: 返回新字典 (不原地改), 调用方据此判断是否需要回写。
|
||||||
"""
|
"""
|
||||||
|
out = dict(profile)
|
||||||
changed = False
|
changed = False
|
||||||
if not profile.get("guid32"):
|
if not out.get("guid32"):
|
||||||
profile["guid32"] = hashlib.sha256(os.urandom(16) + b"guid").hexdigest()
|
out["guid32"] = hashlib.sha256(os.urandom(16) + b"guid").hexdigest()
|
||||||
changed = True
|
changed = True
|
||||||
hebe = profile.get("hebe") or {}
|
if len(out.get("hebe") or {}) < 5:
|
||||||
if len(hebe) < 5:
|
out["hebe"] = {f"Hebe_D{i}": hashlib.sha256(os.urandom(16) + f"hebe{i}".encode()).hexdigest()
|
||||||
profile["hebe"] = {f"Hebe_D{i}": hashlib.sha256(os.urandom(16) + f"hebe{i}".encode()).hexdigest()
|
for i in range(1, 6)}
|
||||||
for i in range(1, 6)}
|
|
||||||
changed = True
|
changed = True
|
||||||
return profile if changed else profile
|
return out, changed
|
||||||
|
|
||||||
|
|
||||||
def get_profile(account: str, force_new: bool = False) -> dict:
|
def get_profile(account: str, force_new: bool = False) -> dict:
|
||||||
"""按账号获取或创建画像(幂等:同账号复用同一套, 自动补齐缺失一致性字段)。"""
|
"""按账号获取或创建画像(幂等:同账号复用同一套, 自动补齐缺失一致性字段)。"""
|
||||||
db = _load_db()
|
db = _load_db()
|
||||||
if not force_new and account in db:
|
if not force_new and account in db:
|
||||||
enriched = _enrich_profile(db[account])
|
enriched, changed = _enrich_profile(db[account])
|
||||||
if enriched is not db[account]:
|
if changed:
|
||||||
db[account] = enriched
|
db[account] = enriched
|
||||||
_save_db(db)
|
_save_db(db)
|
||||||
return enriched
|
return enriched
|
||||||
p = _enrich_profile(generate_profile())
|
p, _ = _enrich_profile(generate_profile())
|
||||||
db[account] = p
|
db[account] = p
|
||||||
_save_db(db)
|
_save_db(db)
|
||||||
return p
|
return p
|
||||||
|
|||||||
Reference in New Issue
Block a user