feat(python): 完成 Python 全量迁移——剩余平台、登录通道与公共层修复
- 新增 5sing/apple/xm/me/qt/lr 平台包与取流实现(含 xm-sign、MissEvan 签名、蜻蜓 HMAC、Apple developer token) - 新增 wy/kg/tx/lr 登录:扫码、手机号、账密(kg 20028 风控指纹)、cookie 绑定、kg/tx 续期 - request 层:保留多值 Set-Cookie;JSON body 紧凑化(修复 kg 网关验签);直连不经系统代理(对齐 Node) - 登录路由统一 Body(embed=True),修复扫码轮询 422;lifespan 接入登录续期调度 - ruff/format/pyright 全绿;14 平台真实联网与 HTTP 路由验证通过
This commit is contained in:
1 parent
800871ef6e
commit
dfc06bf4de
182 files changed
+26620
No files matched your search
@@ -0,0 +1,77 @@
|
||||
"""登录态自动续期 —— 启动后延时刷一轮 + 每 24h 一次(对应原 src/refresh.ts)。
|
||||
|
||||
各平台凭据独立处理:未登录跳过,单个平台失败不影响另一个。
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import asyncio
|
||||
import contextlib
|
||||
from datetime import datetime
|
||||
from typing import Any
|
||||
|
||||
from app.sdk.auth import kg as kg_auth
|
||||
from app.sdk.auth import tx as tx_auth
|
||||
|
||||
INTERVAL_SECONDS = 24 * 60 * 60
|
||||
# 启动后延迟触发,避开启动期与冒烟测试的快速起停
|
||||
STARTUP_DELAY_SECONDS = 10
|
||||
|
||||
_running = False
|
||||
_tasks: list[asyncio.Task[None]] = []
|
||||
|
||||
|
||||
def _log(msg: str) -> None:
|
||||
print(f"[{datetime.now().isoformat(timespec='milliseconds')}] [登录续期] {msg}")
|
||||
|
||||
|
||||
async def refresh_all() -> dict[str, Any]:
|
||||
"""手动/定时共用的续期入口,返回各平台结果(skipped / refreshed / error)。"""
|
||||
global _running
|
||||
if _running:
|
||||
return {"skipped": True}
|
||||
_running = True
|
||||
try:
|
||||
result: dict[str, Any] = {}
|
||||
for name, fn in (("kg", kg_auth.refresh_login), ("tx", tx_auth.refresh_login)):
|
||||
try:
|
||||
res = await fn()
|
||||
result[name] = res
|
||||
if isinstance(res, dict) and res.get("skipped"):
|
||||
_log(f"{name}: 未登录,跳过")
|
||||
else:
|
||||
_log(f"{name}: 登录态已刷新")
|
||||
except NotImplementedError:
|
||||
result[name] = {"skipped": True, "msg": "尚未移植"}
|
||||
except Exception as err: # noqa: BLE001 - 单平台失败不影响其它平台
|
||||
result[name] = {"error": str(err)}
|
||||
_log(f"{name}: {err}")
|
||||
return result
|
||||
finally:
|
||||
_running = False
|
||||
|
||||
|
||||
async def _loop() -> None:
|
||||
await asyncio.sleep(STARTUP_DELAY_SECONDS)
|
||||
while True:
|
||||
await refresh_all()
|
||||
await asyncio.sleep(INTERVAL_SECONDS)
|
||||
|
||||
|
||||
def start_refresh_scheduler() -> None:
|
||||
"""注册定时任务(在应用 startup 时调用)。"""
|
||||
try:
|
||||
loop = asyncio.get_running_loop()
|
||||
except RuntimeError: # 无事件循环(如导入期)时忽略
|
||||
return
|
||||
task = loop.create_task(_loop())
|
||||
_tasks.append(task)
|
||||
_log(f"已启动自动续期(启动后 {STARTUP_DELAY_SECONDS}s 首刷,此后每 {INTERVAL_SECONDS // 3600}h 一次)")
|
||||
|
||||
|
||||
async def stop_refresh_scheduler() -> None:
|
||||
while _tasks:
|
||||
task = _tasks.pop()
|
||||
task.cancel()
|
||||
with contextlib.suppress(asyncio.CancelledError):
|
||||
await task
|
||||
Reference in new issue
Block a user