Initial commit: spec, Claude Code setup, and project docs
Design spec for the bggpipe shelf-to-BGG pipeline, CLAUDE.md and bgg-api skill capturing BGG API constraints, ruff format-on-edit hook, README, LICENSE, and .gitignore. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,17 @@
|
||||
{
|
||||
"hooks": {
|
||||
"PostToolUse": [
|
||||
{
|
||||
"matcher": "Write|Edit",
|
||||
"hooks": [
|
||||
{
|
||||
"type": "command",
|
||||
"command": "jq -r '.tool_input.file_path // .tool_response.filePath // empty' | { read -r f; case \"$f\" in *.py) uvx ruff format \"$f\" && uvx ruff check --fix \"$f\";; esac; } 2>/dev/null || true",
|
||||
"timeout": 60,
|
||||
"statusMessage": "ruff format + check"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
---
|
||||
name: bgg-api
|
||||
description: Reference for BoardGameGeek's XML API2 and website automation — endpoints, 202 queueing, rate limits, collection quirks, upload etiquette. Use when writing or debugging any code that talks to boardgamegeek.com (resolve, diff, or upload stages).
|
||||
---
|
||||
|
||||
# BoardGameGeek API & site automation reference
|
||||
|
||||
## Endpoints (XML API2 — the only sanctioned read API)
|
||||
|
||||
- Search: `https://boardgamegeek.com/xmlapi2/search?query=<title>&type=boardgame,boardgameexpansion`
|
||||
- Thing (details/stats): `https://boardgamegeek.com/xmlapi2/thing?id=<id1,id2,...>&stats=1` — accepts comma-separated IDs; batch (~20) to reduce request count.
|
||||
- Thing versions: `https://boardgamegeek.com/xmlapi2/thing?id=<id>&versions=1` — lists every published edition/version of a game, each with its own version id, name, publisher, year, and language. Used to match photo edition cues to a concrete version.
|
||||
- Collection: `https://boardgamegeek.com/xmlapi2/collection?username=<user>&own=1` — add `&version=1` to include version info on collection items.
|
||||
|
||||
All responses are XML. There is no JSON API and **no write API** — writes automate the website via Playwright with a logged-in session.
|
||||
|
||||
## HTTP 202 queueing (collection endpoint)
|
||||
|
||||
The first `/collection` call typically returns **HTTP 202** with a "please retry" message: BGG queues the export and serves it on a later request. Retry schedule: **2s, 5s, 10s, 30s, give up after ~5 tries** with a clear error message. Treat 202 as normal flow, not an error.
|
||||
|
||||
## Collection endpoint quirks
|
||||
|
||||
- The default subtype **excludes expansions**. Make a second call with `&subtype=boardgameexpansion` and merge, or owned expansions will be invisible to `diff` and re-uploaded.
|
||||
- `own=1` filters to owned items; other statuses (wishlist, previously owned) exist and must not be counted as owned.
|
||||
- Each collection item has a **`collid`** (unique per copy) alongside `objectid` (the game). Owning two editions of one game = two items, same `objectid`, different `collid`s. Diff on (objectid, version) pairs, not bare objectid, when versions are known.
|
||||
- With `&version=1`, items that have a version set include it; items without one simply don't — version-less entries are legal and common.
|
||||
|
||||
## Rate limiting
|
||||
|
||||
- **≤1 request every 2 seconds** to any BGG endpoint (API or website). Jittered exponential backoff on 429/503.
|
||||
- Cache every API response on disk under `data/bgg_cache/` keyed by query/ID; check cache before hitting the network so re-runs are free.
|
||||
- Upload stage is deliberately slower: **2–4 s randomized delay** between games — this is a real account on a community site.
|
||||
|
||||
## Search & matching heuristics
|
||||
|
||||
1. Exact normalized-name match → strong candidate.
|
||||
2. Fuzzy match: `rapidfuzz` `token_sort_ratio ≥ 90` → good candidate.
|
||||
3. Ties: fetch `/thing?stats=1` for top ~5 candidates; prefer higher owned-count / better BGG rank (well-known game beats obscure duplicate of the same name).
|
||||
- Search results include `boardgameexpansion` as a distinct `type` — keep expansions but tag them so review catches base/expansion confusion.
|
||||
- Classify every result: `auto` (single confident match) / `ambiguous` (store all candidates) / `unmatched`. When in doubt between editions or base-vs-expansion, choose `ambiguous`.
|
||||
|
||||
## Website automation (upload stage)
|
||||
|
||||
- Login with `BGG_USERNAME` / `BGG_PASSWORD` env vars; persist Playwright storage state locally (gitignored) so repeat runs skip login. Never write credentials to disk, logs, or error messages.
|
||||
- Per game: navigate to the game page → "Add to Collection" flow → status **Owned** → if a version_id is known, set it in the collection item's version picker → save. Never guess a version — omit it when unknown. The version picker is the most fragile part of the UI: walk it manually once and document the selectors before automating.
|
||||
- A second copy of an owned game must be a NEW collection entry (new collid), not an edit of the existing item.
|
||||
- Expect UI fragility: wrap each game in its own try/except, log the failure to `upload_log.csv`, and continue. `--retry-failed` re-attempts failures; `--dry-run` logs without touching the site.
|
||||
- Idempotency: skip IDs already logged `added`; `--verify` re-fetches the collection to confirm.
|
||||
|
||||
## Policy note
|
||||
|
||||
BGG changed API access policies in 2025 and broke older community tools. Do not use undocumented endpoints, scraped JSON blobs, or third-party mirrors — only XML API2 and the public website.
|
||||
Reference in New Issue
Block a user