Implement P0 of the reaction dashboard: config loading with hot reload, SQLite migrations with backups, single-writer queue, read-only Bilibili fav client, folder import/diff sync, invalid-resource handling, blacklist seed, scheduler, and JSON redacted logging. Includes offline unit and transport-level contract tests; all existing and new tests pass.
95 lines
3.4 KiB
Python
95 lines
3.4 KiB
Python
# -*- coding: utf-8 -*-
|
|
"""只读接口的身份校验探测:对比 完整 cookie / 无 cookie / 去掉身份字段 三种情况。
|
|
|
|
用法: python tools/probe_anon.py
|
|
"""
|
|
import json
|
|
import os
|
|
import sys
|
|
import time
|
|
import urllib.parse
|
|
import urllib.request
|
|
|
|
ROOT = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
|
|
sys.path.insert(0, ROOT)
|
|
sys.stdout.reconfigure(encoding="utf-8")
|
|
|
|
from favsort import strip_identity # noqa: E402 与主程序保持同一套剥离规则
|
|
|
|
BASE = "https://api.bilibili.com"
|
|
UA = ("Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 "
|
|
"(KHTML, like Gecko) Chrome/155.0.0.0 Safari/537.36")
|
|
|
|
BASE_HEADERS = {
|
|
"accept": "*/*",
|
|
"accept-language": "zh-CN,zh;q=0.9,en-US;q=0.8,en;q=0.7",
|
|
"dnt": "1",
|
|
"origin": "https://www.bilibili.com",
|
|
"referer": "https://www.bilibili.com/",
|
|
"sec-ch-ua": '"Google Chrome";v="155", "Chromium";v="155", "Not(A:Brand";v="24"',
|
|
"sec-ch-ua-mobile": "?0",
|
|
"sec-ch-ua-platform": '"Windows"',
|
|
"sec-fetch-dest": "empty",
|
|
"sec-fetch-mode": "cors",
|
|
"sec-fetch-site": "same-site",
|
|
"user-agent": UA,
|
|
}
|
|
|
|
|
|
def get(url, cookie):
|
|
headers = dict(BASE_HEADERS)
|
|
if cookie is not None:
|
|
headers["cookie"] = cookie
|
|
req = urllib.request.Request(url, headers=headers)
|
|
try:
|
|
with urllib.request.urlopen(req, timeout=30) as r:
|
|
return json.loads(r.read().decode("utf-8", "replace"))
|
|
except Exception as e: # noqa: BLE001
|
|
return {"code": "EXC", "message": str(e)[:60]}
|
|
|
|
|
|
def main():
|
|
cfg = json.load(open(os.path.join(ROOT, "config.json"), encoding="utf-8"))
|
|
full = cfg["cookie"]
|
|
anon = strip_identity(full)
|
|
|
|
variants = [("完整 cookie", full), ("去掉身份字段", anon), ("完全无 cookie", None)]
|
|
folders = [(3700023351, "咻咻满 reaction 合集 (658)"),
|
|
(4451053851, "咻咻满-reaction-2025 (167)"),
|
|
(85420451, "默认收藏夹 (29)")]
|
|
|
|
print("=== ids 接口 ===")
|
|
for name, ck in variants:
|
|
for mid, label in folders:
|
|
url = f"{BASE}/x/v3/fav/resource/ids?media_id={mid}&platform=web"
|
|
r = get(url, ck)
|
|
n = len(r.get("data") or []) if isinstance(r.get("data"), list) else "-"
|
|
print(f" [{name:<14}] media={mid:<11} code={r.get('code')!s:<5} "
|
|
f"条数={n!s:<5} msg={r.get('message')}")
|
|
time.sleep(1.2)
|
|
print()
|
|
|
|
print("=== infos 接口(各取前 3 条)===")
|
|
for mid, label in folders:
|
|
ids = get(f"{BASE}/x/v3/fav/resource/ids?media_id={mid}&platform=web", full)
|
|
arr = ids.get("data") or []
|
|
res = ",".join(f"{x['id']}:{x['type']}" for x in arr[:3])
|
|
if not res:
|
|
print(f" media={mid}: 没有可测资源")
|
|
continue
|
|
for name, ck in variants:
|
|
url = (f"{BASE}/x/v3/fav/resource/infos?resources={urllib.parse.quote(res)}"
|
|
f"&platform=web&folder_mid={cfg['mid']}&folder_id={mid}")
|
|
r = get(url, ck)
|
|
d = r.get("data")
|
|
n = len(d) if isinstance(d, list) else "-"
|
|
first = (d[0].get("title", "")[:18] if isinstance(d, list) and d else "")
|
|
print(f" [{name:<14}] media={mid:<11} code={r.get('code')!s:<5} "
|
|
f"返回={n!s:<3} msg={r.get('message')} {first}")
|
|
time.sleep(1.2)
|
|
print()
|
|
|
|
|
|
if __name__ == "__main__":
|
|
main()
|