Discord Bot for iRacing Race Reports

Your league's races,
posted the moment they finish

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.

1iRacing API call per driver, per poll
5m–2hAdaptive polling cadence
2Charts per card, zero extra calls

What it does

Built to stay efficient toward iRacing's API while staying fast for active drivers.

Adaptive Polling Cadence

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.

Cross-Driver Discovery

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.

Image Race Cards

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.

Position & Lap-Time Charts

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.

Podium Detection (In-Class)

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 & Endurance Races

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.

Dual Channel Routing

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.

Crash-Safe, No Duplicates

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.

Guild & Interaction Logging

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.

Raw Data Archival

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.

Embed Fallback

If image rendering ever fails, the report still posts as a Discord embed — a report is never dropped for a rendering hiccup.

Command Reference

Commands labeled Admin Only require the Manage Server permission. Parameters marked ? are optional.

How It Works

From server setup to a race card landing in Discord.

01

Admin Configures Channels

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.

02

Drivers Track Themselves

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.

03

The Poller Wakes on Schedule

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.

04

New Race Detected

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.

05

Card Rendered

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.

06

Fan-Out & Dedup

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.

Architecture

A single long-running Node process — no webhooks required, since iRacing doesn't offer any.

Discord Gateway

A persistent discord.js WebSocket connection handles slash commands and posts reports — no HTTP interactions endpoint to host.

iRacing Client

OAuth2 Password Limited Grant, with access tokens cached in Redis and refreshed automatically. All calls run through an in-process rate limiter.

MySQL

Drivers, per-guild config, subscriptions, dedup log, guild/interaction analytics, and raw API archives — all in parameterized queries, no ORM.

Puppeteer + Handlebars

The race card is a Handlebars template rendered to PNG via headless Chromium, with charts drawn as plain inline SVG.

Run it on your server

SimBot Race Reports is self-hosted — point it at your own Discord app and iRacing credentials.

1

Create a Discord application

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.

2

Configure and register

Copy .env.example to .env, fill in your Discord, MySQL, Redis, and iRacing credentials, then run npm run register to push the slash commands.

3

Start the bot

Run npm start (or npm run pm2:start for a managed process). Tables are created automatically on first boot.

4

Set up your server

In Discord, run /racereports-admin setup to pick your channels, then have drivers run /racereports track with their iRacing customer ID.