Files
biliTickerBuy/interface/auth.py
2026-06-18 16:08:54 +08:00

163 lines
5.2 KiB
Python

from __future__ import annotations
import tempfile
import time
from pathlib import Path
import requests
from .common import (
_cookie_store_path,
_fetch_username_silently,
_make_request,
_resolve_cookie_list,
)
def get_login_state(
*,
cookies: list[dict[str, object]] | dict[str, object] | None = None,
cookies_path: str | Path | None = None,
) -> dict[str, object]:
cookie_list = _resolve_cookie_list(cookies, cookies_path=cookies_path)
has_cookies = bool(cookie_list)
username = _fetch_username_silently(cookie_list)
logged_in = has_cookies and username != "Not login"
return {
"ok": True,
"logged_in": logged_in,
"username": username,
"has_cookies": has_cookies,
"cookies_path": _cookie_store_path(cookies_path),
"next_action": "continue" if logged_in else "prompt_qr_login",
}
def start_qr_login(
*,
headers: dict[str, str] | None = None,
max_retry: int = 10,
retry_interval: float = 1.0,
qr_image_path: str | Path | None = None,
) -> dict[str, object]:
import qrcode
request_headers = headers or {
"user-agent": (
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) "
"AppleWebKit/537.36 (KHTML, like Gecko) "
"Chrome/138.0.0.0 Safari/537.36 Edg/138.0.0.0"
),
}
last_error = "二维码生成失败"
for _ in range(max_retry):
response = requests.get(
"https://passport.bilibili.com/x/passport-login/web/qrcode/generate",
headers=request_headers,
timeout=10,
)
payload = response.json()
if payload.get("code") == 0:
data = payload["data"]
image_path_value: str | None = None
if qr_image_path is not False:
target = (
Path(qr_image_path)
if qr_image_path is not None
else Path(tempfile.gettempdir()) / "biliTickerBuy-login-qrcode.png"
)
target.parent.mkdir(parents=True, exist_ok=True)
qr = qrcode.QRCode(box_size=10, border=4)
qr.add_data(data["url"])
qr.make(fit=True)
qr.make_image(fill_color="black", back_color="white").save(target)
image_path_value = str(target)
return {
"ok": True,
"login_url": data["url"],
"qrcode_key": data["qrcode_key"],
"qr_image_path": image_path_value,
"next_action": "show_qr_and_confirm_scan",
}
last_error = payload.get("message", last_error)
time.sleep(retry_interval)
return {"ok": False, "error": last_error}
def poll_qr_login(
qrcode_key: str,
*,
cookies_path: str | Path | None = None,
timeout_seconds: float = 60.0,
poll_interval: float = 0.5,
headers: dict[str, str] | None = None,
) -> dict[str, object]:
from util.request.CookieManager import parse_cookie_list
if not qrcode_key:
raise ValueError("qrcode_key is required")
request_headers = headers or {"User-Agent": "Mozilla/5.0"}
deadline = time.time() + timeout_seconds
last_message = "等待扫码"
while time.time() < deadline:
response = requests.get(
"https://passport.bilibili.com/x/passport-login/web/qrcode/poll",
params={"qrcode_key": qrcode_key},
headers=request_headers,
timeout=10,
)
payload = response.json()
if payload.get("code") != 0:
last_message = payload.get("message", "轮询登录失败")
time.sleep(poll_interval)
continue
data = payload.get("data", {})
state_code = data.get("code")
last_message = data.get("message", last_message)
if state_code == 0:
cookies = parse_cookie_list(response.headers.get("set-cookie", ""))
cookie_path_value = _cookie_store_path(cookies_path)
request = _make_request(cookies=cookies, cookies_path=cookie_path_value)
username = request.get_request_name()
return {
"ok": True,
"status": "confirmed",
"message": "登录成功",
"cookies": cookies,
"cookies_path": cookie_path_value,
"username": username,
}
if state_code in (86101, 86090):
time.sleep(poll_interval)
continue
return {
"ok": False,
"status": "failed",
"message": last_message,
"code": state_code,
}
return {"ok": False, "status": "timeout", "message": last_message or "登录超时"}
def login_with_cookies(
cookies: list[dict[str, object]] | dict[str, object],
*,
cookies_path: str | Path | None = None,
) -> dict[str, object]:
cookie_list = _resolve_cookie_list(cookies, cookies_path=cookies_path)
username = _fetch_username_silently(cookie_list)
return {
"ok": True,
"logged_in": username != "Not login",
"username": username,
"cookies": cookie_list,
"cookies_path": _cookie_store_path(cookies_path),
}