sprite-catalog
The sprite catalog that drives trade matching and giveaways in the Sprites Trading HQ Discord server. One JSON file per season, validated against a JSON Schema.
This repo is data, not code. It is public so anyone can read it, build against it, or run their own bot from it.
Why JSON
The catalog is a public data source other people consume. JSON is readable in every language and in the browser with zero dependencies; YAML needs a parser everywhere.
JSON's one real weakness for a hand-maintained file is that it has no comments and is
unforgiving about syntax. Both are covered: the schema in schema/ gives
editors autocomplete and inline validation as you type, CI rejects malformed files
before merge, and every object accepts a notes field for anything you would otherwise
have written as a comment.
Layout
schema/sprite-catalog.schema.json the contract — CI and editors both use it
seasons/c7s3.json one file per season, named for the season
seasons/c7s4.json
Season files are append-mostly. Entries are never deleted — a sprite that leaves
circulation gets "status": "hidden", which drops it out of matching while keeping it
in the inventories of everyone who already holds one.
Format
Variants nest under their base sprite so base_name and row_name aren't repeated for
every skin. The bot flattens this to one row per variant on load.
{
"season": "c7s3",
"game_mode": "br",
"cross_mode_trading": false,
"sprites": [
{
"base_name": "Example Sprite",
"row_name": "Example Row",
"variants": [
{
"variant": "Default",
"status": "released",
"released_at": "2026-03-14",
"max_stack": 5,
"confirmed": true
},
{
"variant": "Gold",
"status": "unreleased",
"notes": "Datamined; not yet obtainable in game."
}
]
}
]
}
The three statuses
| Status | Meaning |
|---|---|
unreleased |
Known but not yet obtainable. Excluded from matching. |
released |
Obtainable and tradeable. |
hidden |
Dropped out of matching, retained in inventories. Reversible. |
hidden deliberately replaces a retired concept that appeared in earlier drafts.
Sprites usually come back, and if one never does, nothing is lost by having called it
hidden — whereas retired implies a permanence the data can't promise.
Fields worth explaining
confirmed— variant lists are assumed-complete guesses until someone is actually recorded owning one. Unconfirmed variants are deprioritised in autocomplete and left out of the coverage metric, so a speculative entry can't quietly distort either.rarity_weight— display and analytics only. The matcher does not read it. Rarity matching was removed on purpose: value-matching is how selfish players win and the pool starves. Trades are N-for-N by count, never by value.max_stack— anything above 1 means spare copies can enter the give pool.cross_mode_trading— defaults tofalse. Leave it there unless the policy is deliberately changed.
Contributing
- Fork or branch, edit the season file.
- Open a PR. CI validates every file in
seasons/against the schema and fails on malformed JSON, unknown fields, bad status values, or aseasonvalue that doesn't match the filename. - A mod merges. The bot picks it up on the next
/mod catalog reload, which shows a diff preview ("adds 6 sprites, 14 variants, no removals — apply?") before anything is written.
The bot's exporter opens a draft PR automatically when it sees a sprite ID it doesn't recognise, so most additions start here rather than being typed by hand.
Editor setup — point your editor at schema/sprite-catalog.schema.json and you get
completion and error squiggles inline. In VS Code, add to .vscode/settings.json:
{
"json.schemas": [
{ "fileMatch": ["seasons/*.json"], "url": "./schema/sprite-catalog.schema.json" }
]
}
Consuming it
The bot fetches raw files over HTTP and caches them in memory — autocomplete has a hard 3-second budget with no defer available, so it never queries a database per keystroke.
The raw URL is configurable via CATALOG_REPO_RAW, so you can point a development bot
at your own fork without touching code:
CATALOG_REPO_RAW=https://git.thewichersfamily.com/thethreemagi/sprite-catalog/raw/branch/main
Nothing about the format is bot-specific. Fetch the JSON and use it however you like.
Note on history
Commits here carry no AI attribution — see .githooks/commit-msg,
enabled with git config core.hooksPath .githooks after cloning. The hook ships
executable; Git just never turns a repo's hooks on by itself. Please keep it that
way in PRs.