1a9e5e2c18
- Rules abstraction (5e + pf2e) behind one interface, fully tested - Pure combat engine: turn-order safe across add/remove/reorder/init-change - Dexie storage with real cascade deletes + Zod validation on write - Seedable dice engine with notation parser - Lazy SRD compendium (code-split), bestiary -> combat - Strict TS, 54 unit tests, Playwright e2e smoke (all green) Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
71 lines
2.4 KiB
Python
71 lines
2.4 KiB
Python
#!/usr/bin/env python3
|
|
"""Fetch comprehensive D&D 5e data from Open5e v1 API and PF2e from GitHub"""
|
|
import json, time, os, sys, requests
|
|
from urllib.parse import urljoin
|
|
|
|
OUT_DIR = os.path.join(os.path.dirname(__file__), "src", "assets", "srd")
|
|
BASE_V1 = "https://api.open5e.com/v1/"
|
|
|
|
def fetch_paginated(endpoint, max_pages=None):
|
|
"""Fetch all pages from an Open5e v1 endpoint."""
|
|
results = []
|
|
url = urljoin(BASE_V1, f"{endpoint}/?format=json&limit=500")
|
|
page = 0
|
|
while url:
|
|
page += 1
|
|
print(f" Fetching {endpoint} page {page}... ({len(results)} items so far)")
|
|
resp = requests.get(url, headers={"User-Agent": "TTRPG-Manager/1.0"}, timeout=30)
|
|
if resp.status_code != 200:
|
|
print(f" Error: {resp.status_code}")
|
|
break
|
|
data = resp.json()
|
|
results.extend(data.get("results", []))
|
|
url = data.get("next")
|
|
if max_pages and page >= max_pages:
|
|
break
|
|
time.sleep(0.3)
|
|
return results
|
|
|
|
def fetch_all():
|
|
os.makedirs(OUT_DIR, exist_ok=True)
|
|
|
|
# Only fetch SRD/OGL content (document__slug = 'wotc-srd' or 'srd')
|
|
endpoints = {
|
|
"monsters": "monsters",
|
|
"spells": "spells",
|
|
"magicitems": "magicitems",
|
|
"weapons": "weapons",
|
|
}
|
|
|
|
for name, endpoint in endpoints.items():
|
|
print(f"\n=== Fetching {name} ===")
|
|
items = fetch_paginated(endpoint)
|
|
if not items:
|
|
print(f" No items found for {name}, trying alternate...")
|
|
continue
|
|
|
|
# Filter to SRD content only
|
|
srd_items = []
|
|
for item in items:
|
|
doc = item.get("document__slug", "")
|
|
if doc in ("wotc-srd", "srd", "wotc", "5e-srd", "o5e"):
|
|
srd_items.append(item)
|
|
|
|
print(f" Total: {len(items)}, SRD: {len(srd_items)}")
|
|
|
|
# Save full file
|
|
outpath = os.path.join(OUT_DIR, f"{name}-full.json")
|
|
with open(outpath, "w") as f:
|
|
json.dump(items, f, indent=2)
|
|
print(f" Saved {len(items)} to {outpath}")
|
|
|
|
# Save SRD-only file
|
|
if srd_items and len(srd_items) < len(items):
|
|
outpath_srd = os.path.join(OUT_DIR, f"{name}-srd.json")
|
|
with open(outpath_srd, "w") as f:
|
|
json.dump(srd_items, f, indent=2)
|
|
print(f" Saved {len(srd_items)} SRD to {outpath_srd}")
|
|
|
|
if __name__ == "__main__":
|
|
fetch_all()
|