Rails API that tracks World of Warcraft PvP leaderboard data for US and EU regions. It fetches character equipment and talent data from the Blizzard API, aggregates it into PvP meta statistics (item/enchant/gem popularity, talent builds, class distribution), and serves the results as a JSON API.
- Ruby 4.0.1 (via rbenv or mise)
- PostgreSQL 14+
- Bundler (
gem install bundler) - Blizzard developer credentials (register an app)
git clone <repo-url>
cd bis
bundle installcp .env.example .envEdit .env and set at minimum:
# Required — Blizzard OAuth app credentials
BLIZZARD_CLIENT_ID=your_client_id
BLIZZARD_CLIENT_SECRET=your_client_secret
# Optional — second credential pair for auth pool redundancy
# BLIZZARD_CLIENT_ID_2=your_second_client_id
# BLIZZARD_CLIENT_SECRET_2=your_second_client_secretAll other values in .env.example have sane defaults for local development.
The app uses four PostgreSQL databases: primary app data, SolidQueue jobs, SolidCache, and SolidCable. Rails manages all of them.
bundle exec rails db:create db:migrateOr for a faster fresh setup (skips migrations, loads schema directly):
bundle exec rails db:schema:loadbundle exec rails serverThe API is available at http://localhost:3000.
In a separate terminal:
bundle exec rails solid_queue:startWithout the worker, the API still serves existing data — you just won't be able to trigger new syncs.
The sync pipeline is triggered manually (no cron scheduler in development). Use the Rails console or the Avo admin panel.
bundle exec rails console# You need at least one current PvpSeason record
PvpSeason.create!(name: "Season 1", blizzard_id: 37, is_current: true, display_name: "Season 1")
# Full sync: discovers all brackets for US+EU, fetches all characters, builds aggregations
Pvp::SyncCurrentSeasonLeaderboardsJob.perform_later
# Ad-hoc single bracket (no sync cycle tracking)
Pvp::SyncBracketJob.perform_later(region: "us", season: PvpSeason.current, bracket: "3v3")| Variable | Default | Description |
|---|---|---|
BLIZZARD_CLIENT_ID |
— | Required. Blizzard OAuth client ID |
BLIZZARD_CLIENT_SECRET |
— | Required. Blizzard OAuth client secret |
BLIZZARD_CLIENT_ID_2 |
— | Optional second client for auth pool |
BLIZZARD_CLIENT_SECRET_2 |
— | Optional second client secret |
DB_HOST |
localhost |
PostgreSQL host |
DB_PORT |
5432 |
PostgreSQL port |
DB_POOL |
130 |
Connection pool size (20 is fine locally) |
PVP_SYNC_BATCH_SIZE |
50 |
Characters processed per batch job |
PVP_SYNC_CONCURRENCY |
15 |
Thread concurrency within a batch job |
PVP_SYNC_THREADS |
8 |
SolidQueue threads for character sync workers |
PVP_LEADERBOARD_CONCURRENCY |
10 |
Concurrent Blizzard leaderboard fetches |
PVP_BLIZZARD_RPS |
95.0 |
Blizzard API rate limit (requests/sec) |
PVP_BLIZZARD_HOURLY_QUOTA |
36000 |
Blizzard API hourly request cap |
PVP_META_TOP_N |
1000 |
Top N entries used in meta aggregations |
RAILS_MAX_THREADS |
3 |
Puma thread count |
TELEGRAM_BOT_TOKEN |
— | Telegram Bot API token |
TELEGRAM_CHAT_ID |
— | Default chat for broadcast notifications |
TELEGRAM_ALLOWED_CHAT_IDS |
— | Comma-separated chat IDs allowed to send commands |
TELEGRAM_WEBHOOK_SECRET |
— | Secret token for webhook auth |
All endpoints return JSON. No authentication required.
GET /up # Health check
GET /api/v1/characters # Character list
GET /api/v1/pvp/meta/items # Item popularity stats
GET /api/v1/pvp/meta/enchants # Enchant popularity stats
GET /api/v1/pvp/meta/gems # Gem popularity stats
GET /api/v1/pvp/meta/specs # Spec distribution
GET /api/v1/pvp/meta/specs/:id # Single spec stats
GET /api/v1/pvp/meta/class_distribution # Class distribution
- Job monitor —
http://localhost:3000/jobs(Mission Control UI for SolidQueue) - Admin panel —
http://localhost:3000/avo(Avo resource management)
The app exposes a Telegram bot for sync visibility and control. Register the webhook once:
curl "https://api.telegram.org/bot<TOKEN>/setWebhook?url=https://api.wowinsights.xyz/telegram/webhook&secret_token=<SECRET>"Commands:
| Command | Description |
|---|---|
/cycle [id] |
Last (or specific) cycle status with inline action buttons |
/progress |
Live batch progress bar + ETA |
/history |
Last 5 completed cycles with duration |
/syncnow |
Trigger an immediate sync |
/currentsync |
Active cycle details |
/abort <id> |
Abort a running cycle |
/errors |
Job errors in the last 24h |
/jobs |
Job success rate summary |
Inline buttons on /cycle: active cycles show Abort, failed/aborted cycles show Retry failed chars.
Auto-notifications: sync milestones (25/50/75%), failed-character report if >5% fail, stale-cycle alert if stuck >2h, deploy notification on each Dokku deploy.
bundle exec rspec # full suite
bundle exec rspec spec/jobs/ # job specs only
bundle exec rspec spec/services/pvp/ # PvP service specs
bundle exec rspec spec/requests/ # API integration specsLinting and type checking:
bundle exec rubocop
bundle exec rubocop --autocorrect
bundle exec steep checkBlizzard API
│
▼
SyncCurrentSeasonLeaderboardsJob ← Phase 1: discover brackets, sync leaderboards
│ (per region, parallel HTTP)
▼
SyncCharacterBatchJob ×N ← Phase 2: fetch character equipment + talents
│ (threaded, region-isolated queues)
▼
BuildAggregationsJob ← Phase 3: compute meta stats per bracket
│ (triggered atomically when all batches complete)
▼
PostgreSQL (pvp_meta_* tables)
│
▼
JSON API (/api/v1/pvp/meta/*)