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

217 lines
6.1 KiB
Python

from __future__ import annotations
import copy
import json
from pathlib import Path
from typing import Any
from urllib.parse import parse_qs, urlparse
import requests
REQUIRED_FIELDS = (
"detail",
"count",
"screen_id",
"project_id",
"sku_id",
"pay_money",
"buyer_info",
"buyer",
"tel",
"deliver_info",
"cookies",
)
BUYER_REQUIRED_FIELDS = ("name", "personal_id")
DELIVER_REQUIRED_FIELDS = ("name", "tel", "addr_id", "addr")
COOKIE_REQUIRED_FIELDS = ("name", "value")
SALES_FLAG_NUMBER_MAP = {
1: "不可售",
2: "预售",
3: "停售",
4: "售罄",
5: "不可用",
6: "库存紧张",
8: "暂时售罄",
9: "不在白名单",
101: "未开始",
102: "已结束",
103: "未完成",
105: "下架",
106: "已取消",
}
def _load_json_file(path: str | Path) -> Any:
with open(path, "r", encoding="utf-8") as handle:
return json.load(handle)
def _read_tinydb_value(path: str | Path, key: str) -> Any:
target = Path(path)
if not target.exists():
return None
try:
raw = _load_json_file(target)
except Exception:
return None
if isinstance(raw, dict):
default_group = raw.get("_default")
if isinstance(default_group, dict):
for item in default_group.values():
if isinstance(item, dict) and item.get("key") == key:
return item.get("value")
if raw.get("key") == key:
return raw.get("value")
if isinstance(raw, list):
for item in raw:
if isinstance(item, dict) and item.get("key") == key:
return item.get("value")
return None
def _cookie_store_path(cookies_path: str | Path | None) -> str | None:
if cookies_path is not None:
return str(Path(cookies_path))
package_root = Path(__file__).resolve().parents[1]
config_path = package_root / "config.json"
configured = _read_tinydb_value(config_path, "cookies_path")
if configured:
return str(Path(configured))
return str(package_root / "cookies.json")
def _coerce_cookie_store(raw: Any) -> list[dict[str, Any]] | None:
if raw is None:
return None
if isinstance(raw, list):
return copy.deepcopy(raw)
if isinstance(raw, dict):
if isinstance(raw.get("cookie"), list):
return copy.deepcopy(raw["cookie"])
default_group = raw.get("_default")
if isinstance(default_group, dict):
for item in default_group.values():
if isinstance(item, dict) and item.get("key") == "cookie":
value = item.get("value")
if isinstance(value, list):
return copy.deepcopy(value)
return None
def _resolve_cookie_list(
cookies: list[dict[str, Any]] | dict[str, Any] | None = None,
*,
cookies_path: str | Path | None = None,
) -> list[dict[str, Any]] | None:
if cookies is not None:
return _coerce_cookie_store(cookies)
store_path = _cookie_store_path(cookies_path)
if not store_path:
return None
try:
return _coerce_cookie_store(_load_json_file(store_path))
except Exception:
return None
def _cookies_to_header(cookies: list[dict[str, Any]] | None) -> str:
if not cookies:
return ""
parts: list[str] = []
for cookie in cookies:
name = cookie.get("name")
value = cookie.get("value")
if name and value is not None:
parts.append("{0}={1}".format(name, value))
return "; ".join(parts)
def _fetch_username_silently(
cookies: list[dict[str, Any]] | None,
*,
timeout: float = 10.0,
) -> str:
if not cookies:
return "Not login"
headers = {
"accept": "*/*",
"accept-language": "zh-CN,zh;q=0.9",
"referer": "https://show.bilibili.com/",
"user-agent": (
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) "
"AppleWebKit/537.36 (KHTML, like Gecko) "
"Chrome/126.0.0.0 Safari/537.36"
),
"cookie": _cookies_to_header(cookies),
}
try:
response = requests.get(
"https://api.bilibili.com/x/web-interface/nav",
headers=headers,
timeout=timeout,
)
response.raise_for_status()
payload = response.json()
data = payload.get("data") or {}
username = data.get("uname")
if isinstance(username, str) and username.strip():
return username.strip()
except Exception:
return "Not login"
return "Not login"
def _deepcopy_dict(data: Any) -> dict[str, Any]:
if isinstance(data, dict):
return copy.deepcopy(data)
raise TypeError("config must be a dict or a json file path")
def _load_config(config_or_path: str | Path | dict[str, Any]) -> dict[str, Any]:
if isinstance(config_or_path, (str, Path)):
return _deepcopy_dict(_load_json_file(config_or_path))
return _deepcopy_dict(config_or_path)
def _extract_project_id(project_input: str | int) -> int:
if isinstance(project_input, int):
return project_input
text = str(project_input).strip()
if not text:
raise ValueError("project_input is empty")
if text.isdigit():
return int(text)
parsed = urlparse(text)
query = parse_qs(parsed.query)
project_ids = query.get("id", [])
if project_ids and project_ids[0].isdigit():
return int(project_ids[0])
raise ValueError("could not extract project id from input")
def _format_sale_status(ticket: dict[str, Any]) -> str:
sale_flag_number = ticket.get("sale_flag_number")
if sale_flag_number in SALES_FLAG_NUMBER_MAP:
return SALES_FLAG_NUMBER_MAP[sale_flag_number]
if "clickable" in ticket:
return "可购买" if ticket.get("clickable") else "不可购买"
return "未知状态"
def _make_request(
*,
cookies: list[dict[str, Any]] | dict[str, Any] | None = None,
cookies_path: str | Path | None = None,
) -> Any:
from util.request.BiliRequest import BiliRequest
return BiliRequest(
cookies=cookies,
cookies_config_path=_cookie_store_path(cookies_path),
)