SimBot Race Reports watches a list of iRacing drivers and posts a rich, image-based race card to Discord every time one of them finishes an official race — podiums to their own channel, everyone else to the main feed.
Built to stay efficient toward iRacing's API while staying fast for active drivers.
Re-check frequency scales with how recently a driver raced: 5 min if active this week, up to a 2-hour ceiling if dormant. Active drivers stay near-realtime; idle ones cost almost nothing.
Every fetched race is scanned for other tracked drivers in the same field — including across different Discord servers. One API call can surface reports for several drivers at once.
Each report is a rendered dark-themed card: finish position, iRating & Safety Rating deltas, incidents, class strength of field, weather, and more — built from a single API response.
Inline SVG charts for position-over-laps (class vs. overall, dual axis) and lap-time trend with the fastest lap marked — drawn from one extra lap-chart call, no charting library.
Podiums are top-3 finish position within car class, not overall — correct for multiclass series where P8 overall can still be a class win.
Team results show the full roster sorted by iRating, with each driver's laps, iRating change, and Safety Rating change — real per-driver stats, not team placeholders.
An "all races" channel and a "podiums only" channel can both be set at once — every race goes to one, podium finishes also go to the other.
Every post is claimed in a dedup table before sending. A crash or restart mid-poll can never result in the same race being posted twice.
Every server the bot joins or leaves, and every slash command run, is logged for analytics and debugging — matching the rest of the SimBot suite.
Every race result, lap chart, and recent-races response is cached verbatim in the database — a foundation for future stats without re-hitting the iRacing API.
If image rendering ever fails, the report still posts as a Discord embed — a report is never dropped for a rendering hiccup.
Commands labeled Admin Only require the Manage Server permission. Parameters marked ? are optional.
From server setup to a race card landing in Discord.
An admin runs /racereports-admin setup to pick an all-races channel and/or a podiums-only channel. The bot verifies it can actually post there before saving.
A user runs /racereports track with their iRacing customer ID. Their watermark is seeded to their most recent existing race — no history spam, only new races going forward.
A lightweight tick checks which drivers are due, based on their adaptive cadence. Due drivers are queried once via member_recent_races through a shared rate limiter.
Any subsession newer than the stored watermark triggers a full results/get fetch — this single response carries weather, splits, standings, and per-driver stats for the card.
A lap-chart call feeds the position and lap-time charts, then the whole card is rendered to an image. If rendering fails for any reason, an embed is posted instead — nothing is dropped.
The report is posted to every server that tracks the driver — all-races channel always, podium channel if it's a top-3-in-class finish — with a dedup claim guaranteeing no double-posts.
A single long-running Node process — no webhooks required, since iRacing doesn't offer any.
A persistent discord.js WebSocket connection handles slash commands and posts reports — no HTTP interactions endpoint to host.
OAuth2 Password Limited Grant, with access tokens cached in Redis and refreshed automatically. All calls run through an in-process rate limiter.
Drivers, per-guild config, subscriptions, dedup log, guild/interaction analytics, and raw API archives — all in parameterized queries, no ORM.
The race card is a Handlebars template rendered to PNG via headless Chromium, with charts drawn as plain inline SVG.
SimBot Race Reports is self-hosted — point it at your own Discord app and iRacing credentials.
Create a bot in the Discord Developer Portal with the bot and applications.commands scopes, and the Send Messages, Embed Links, and View Channel permissions.
Copy .env.example to .env, fill in your Discord, MySQL, Redis, and iRacing credentials, then run npm run register to push the slash commands.
Run npm start (or npm run pm2:start for a managed process). Tables are created automatically on first boot.
In Discord, run /racereports-admin setup to pick your channels, then have drivers run /racereports track with their iRacing customer ID.