Skip to content
All work
  • #discord
  • #bots
  • #api-integration
  • #twitch
  • #automation

BotFire

A Discord bot for Il Bonfire, my streaming community: viewers suggest games through an IGDB-powered search, and I run my Twitch schedule from Discord with automatic Google Calendar sync.

Role
Solo developer and the community’s streamer: design, code and day-to-day running.
Outcome
In daily use on Il Bonfire since 2025. /prossima-live and /suggerisci-gioco are the most-used commands, and the schedule reaches Google Calendar more reliably than Twitch’s own sync.
Year
2025
Status
in progress
Stack
  • Discord.js v14
  • Node.js
  • Twitch API
  • IGDB API
  • Google Calendar API
  • Firestore
  • Components V2

comandi-bot

  • BotFireAPP

    Ciao viandante! Sono BotFire. Prova un comando qui sotto: suggerisci un gioco, guarda la prossima live o il programma del mese.

Try a command

A recreation of BotFire with sample data. The bot speaks Italian, like its community.

  1. The start

    A community and a creator, both underserved

    I stream on Twitch, and Il Bonfire is my community's Discord server, the meeting point for my followers, the viandanti. Two chores kept eating my time: game suggestions scattered across the chat with no official way to collect them, and keeping my Twitch schedule in step with Google Calendar, which Twitch's own sync does in a half-broken way. No off-the-shelf bot did either the way I wanted, and I'd rather build a tool that fits than bend my workflow around a generic one.

  2. The goal

    One bot, two audiences

    BotFire had to be a community hub for the viandanti and a private control panel for me. For the community: an official, well-made way to suggest games, smart enough to understand sagas and whether a game runs on PC, plus an easy way to see when the next stream is. For me: create, view and remove Twitch schedule entries instantly from Discord, with Google Calendar following along reliably.

  3. The adventure

    Four services behind one Discord bot

    I started on September 16, 2025, and had the first complete version in about a month. BotFire runs on Discord.js v14 with ES modules and uses Discord's newest Components V2 for embed-free, interactive messages. Behind it sit four services: IGDB for the game database, Twitch's Helix API for the schedule, Google Calendar for the sync, and Firestore for the suggestions and votes. An in-memory cache with expiry and least-recently-used eviction keeps it fast without hammering any of them.

    VideoSuggesting a game with /suggerisci-gioco
    0:00 / 0:00
    From search to saved suggestion.
  4. The difficulties

    Sagas, calendars, limits and time zones

    Finding every official, canonical game in a series took IGDB queries far more involved than a search: filtering out remasters, ports and spin-offs, then checking PC availability game by game. Keeping two calendars consistent meant linking every event to its Twitch segment and never letting a calendar failure block Twitch. Discord caps a message at about 40 components, so a busy month had to be split across messages. And every date had to survive time zones and daylight saving time on its way to two different APIs.

  5. The end

    In daily use, and still growing

    BotFire is part of how I run my channel every day. /prossima-live and /suggerisci-gioco are the community's most-used commands, and the schedule reaches Google Calendar more reliably than Twitch's own sync. Not everyone uses its full potential, since some people are wary of Discord bots even when they're friendly, but the core features are part of the server's routine. It's an ongoing project: I keep adding tools, fixing and upgrading the existing ones, and I update this page whenever it changes significantly.

To the first full version
~1 month
To the first full version
Services integrated
4
Services integrated
Permission levels
5
Permission levels
In use on Il Bonfire
Daily
In use on Il Bonfire

What's next

  • A friendlier schedule

    I'd make the schedule even easier to use, within what Discord Components allow. Discord's forms are still limited, and I hope they get more powerful.

  • Calendar to Twitch

    The infrastructure for syncing from Google Calendar back to Twitch exists; the logic to create Twitch segments from calendar events is the next step.

  • Game suggestions that understand sagas

    IGDB-powered search with covers, saga detection, a PC check and no duplicates.

  • Twitch schedule from Discord

    Create, view and remove streams instantly, in an interface with month navigation and per-stream actions.

  • Google Calendar sync

    Every scheduled stream lands in Google Calendar, recurring ones included, more reliably than Twitch's own sync.

  • Community commands

    The next stream, the suggested games with their votes, and the server's information, for everyone.

  • Five permission levels

    Everyone, Member, Moderator, Admin and Owner, detected from roles and adjustable with a command.

  • Components V2 interface

    Embed-free messages with buttons, modals and select menus, built on Discord's newest display components.

Game suggestions

/suggerisci-gioco searches IGDB as you type and understands that a game might be part of a bigger saga.

  • Saga detection

    When a game belongs to a franchise, BotFire switches to the whole collection and lists every main entry.

  • PC check

    I stream from a PC, so standalone games are checked for a PC release, and sagas get a PC compatibility percentage.

  • No duplicates

    The same person can't suggest the same game or saga twice.

  • Search as you type

    Live IGDB search with covers and metadata, from the third character.

  • Votes

    Every suggestion counts as a vote, so the most wanted games and sagas rise to the top.

  • Smart filtering

    Remasters, ports and spin-offs are left out, so a saga shows its canonical games only.

VideoSuggesting a game with /suggerisci-gioco
0:00 / 0:00
From search to saved suggestion.
The confirmation step, before anything is saved.
A saga that's mostly off PC gets a warning.

Suggestions and their votes can be browsed by game or by saga, with a leaderboard of the most active suggesters.

Votes, as the community sees them.
The same vote, stored in Firestore.

The Twitch schedule

The schedule commands are mine: they turn Discord into the place where I plan my streams.

  • Instant changes

    Add, view and remove schedule entries straight from Discord, with no trip to Twitch's dashboard.

  • Google Calendar, automatically

    Viewers can add streams to their own calendars, with reminders and notifications.

  • Categories with box art

    Autocomplete over Twitch's game categories, with thumbnails.

  • Recurring streams

    A weekly show is one entry, mirrored as a proper recurring event in the calendar.

  • Time zones

    Full time zone support, daylight saving time included.

  • Fast navigation

    Month by month, with smart caching and the next and previous months loaded in advance.

Adding a stream: everything Twitch needs in one form.
Twitch categories, autocompleted.
The full month, in Components V2.
Each stream has its own actions.

For the viandanti, /prossima-live shows when the next stream is and what it's about. The message deletes itself after 15 seconds, with a 20-second cooldown so it can't flood the chat.

The community's favourite command.

Who can do what

Five levels, from the widest to the narrowest. The suggestion and next-stream commands are open to everyone; schedule management is owner-only by default, and /permission-set can change any of it.

  • Everyone

    All users, including those with no role.
  • Member

    Users with any role beyond @everyone.
  • Moderator

    Users with moderation roles or permissions.
  • Admin

    Users with admin roles, and the server owner.
  • Owner

    Only the bot's owner.

Architecture

BotFire sits between Discord and three external services. Twitch is the source of truth for the schedule, Google Calendar follows it, and Firestore stands on its own for the game suggestions.

  • Twitch API (Helix)

    Master

    The schedule lives here. App-level tokens read public data; a user token authorizes schedule changes.

  • Google Calendar API

    Follower

    Synchronized from Twitch, through a service account. Each event stores its Twitch segment ID.

  • Firebase Firestore

    Independent

    Game suggestions, votes and user data, through service account credentials.

  • IGDB API

    Game database

    Search, collections and platforms, authenticated with the same Twitch OAuth2 credentials.

Every schedule action follows the same path: Discord command, validation, Twitch API, Google Calendar API, reply. A calendar failure is logged and never blocks Twitch.

Under the hood: game suggestions

  1. Search

    From the third character, up to 50 IGDB matches are fetched and the 25 shortest titles shown, as the most relevant.
  2. Details

    The chosen game is fetched in full, including its collections.
  3. Saga

    If it belongs to one, a multiquery fetches the saga's games and memberships in parallel.
  4. PC check

    PC availability per game, and a percentage for the saga.
  5. Confirm and save

    A confirmation, a duplicate check, then Firestore.

The saga multiquery

One request to IGDB's multiquery endpoint runs two queries side by side. The first fetches every game in the collection with its cover, release dates, platforms and parent game. The second fetches the collection's memberships, so the bot can keep only main games (membership type.id === 1) and drop spin-offs. Games are sorted by release date, using parent_game.first_release_date when there is one. The originally suggested game gets a marker in the list, PC games a check and the others a warning.

The filters keep only:

  • Main games (game_type = 0) that aren't remakes, remasters or ports.
  • DLCs (game_type = 4) on PC only, and expansions (game_type = 8, 9) and standalone expansions (game_type = 10), filtered the same way.
  • Released games (game_status = 0 or none) that have a release year.
  • Modern platforms: PC, PlayStation 3, 4 and 5, Xbox 360, One and Series X|S, and Switch.

PC compatibility

A standalone game is checked for a release on PC (Microsoft Windows); without one, the confirmation turns gold (0xFFD700) with a warning. For a saga, each game is checked for platform 6 (PC), and below 75% compatibility the community gets a warning.

Storage and duplicates

Each suggestion is a Firestore document whose ID encodes the type, the target and the user:

text
{type}_{targetId}_{userId}

0_1942_123456789   a standalone game
1_123_123456789    a collection

So the same user can't suggest the same thing twice, all suggestions for a game are one query away, and so are all suggestions by a user. Each document also stores the target's name, the user's name, a timestamp and audit fields (createdAt, updatedAt, createdBy, updatedBy).

  • Authentication

    Twitch OAuth2

    Client credentials flow; the token is cached in memory and refreshed when it expires, after an hour.

  • Performance

    Caching

    Firestore queries cached for 5 minutes with LRU eviction, at most 1,000 entries; the multiquery saves round trips.

  • Errors

    Resilience

    IGDB, autocomplete, Firestore and timeout errors are all handled, logged with context, and explained to users in Italian.

Under the hood: the schedule

Adding a stream

  1. Form

    A modal asks for title (140 characters, Twitch's limit), date, time, duration and whether it repeats.
  2. Validation

    Formats, real dates, in the future but within a year, 30 minutes to 23 hours, category and time zone.
  3. Time zone

    Local time becomes an ISO 8601 string with the right offset for that date.
  4. Confirmation

    A summary with Confirm and Cancel, which expires after 30 seconds.
  5. Twitch

    POST /helix/schedule/segment; overlapping streams get a clear error.
  6. Calendar

    The event is created and linked to its Twitch segment.

Time zones are where schedules usually break. The input is read as local time in the chosen zone, and the offset is computed for that specific date with Intl.DateTimeFormat, so daylight saving time is always right:

text
19/09/2025 19:00, Europe/Rome  →  2025-09-19T19:00:00+02:00

The calendar event gets the title, a description, start and end times with the time zone, and a link to the channel. It stores twitchSegmentId and broadcasterId in its extended properties, and a recurring stream becomes an RRULE, such as RRULE:FREQ=WEEKLY;BYDAY=FR for every Friday.

Viewing the schedule

/schedule is built from Components V2: previous and next month, a refresh that clears the cache, and a close button. Each stream shows its game's box art, title (with a marker for recurring ones), date and relative time, duration and category, plus a menu to remove it or copy its ID.

  • Component budget

    Discord limits

    A message holds 5 action rows and about 40 components. A stream costs about 8, so the month is split: the first message keeps 34 for navigation, follow-ups get the full 40.

  • Caching

    TTL

    User info for 10 minutes, schedules for 1 minute, categories for 1 hour.

  • Prefetching

    Speed

    The months before and after the one on screen load in the background, so navigation feels instant.

Removing a stream

/schedule-remove cleans the calendar first: it looks for the event by twitchSegmentId, or, as a fallback, fetches the segment from Twitch and searches the calendar by time and title. Then it calls DELETE /helix/schedule/segment. If Twitch no longer has the segment but the calendar was cleaned, it still reports success; if the calendar fails, Twitch deletion goes ahead. Nothing blocks, everything is logged.

Keeping two calendars in step

Each calendar event stores its Twitch segment ID in extendedProperties.private.twitchSegmentId. To find an event, the bot first searches by that ID, which is fast and exact and returns the master event of a recurring series. If that fails, it searches within 60 minutes of the stream's time and matches by title. A recurring series keeps the ID on its master event, its instances inherit it, and removing the stream removes the whole series.

/schedule-sync repairs anything that drifted:

  • Twitch to Calendar creates the events that are missing, without duplicates, and reports the counts. For the first setup, after calendar problems, or for streams created outside the bot.
  • Calendar to Twitch is planned: the infrastructure exists, the logic to create Twitch segments from calendar events is still to come.
  • Full sync runs both and reports on everything.

Operations aren't atomic across the two services, by design: Twitch is the master, the calendar follows, and a manual sync can always repair an inconsistency.

The stack

  • Discord.js

    14.14.1

    Slash commands, autocomplete and Components V2.

  • Node.js

    16.9+ with ES modules

    The runtime, with modern import and export.

  • Twitch API

    Helix, OAuth2

    Schedule management, with app and user tokens.

  • IGDB API

    Twitch OAuth2

    Game search, collections and platforms.

  • Google Calendar API

    Service account

    Event CRUD and RRULE recurrence.

  • Firebase Firestore

    NoSQL

    Votes and persistence, with caching and audit fields.

Challenges

Problem

Canonical games in a saga

Fetching every official game in a series meant excluding remasters, ports and non-canonical entries, across thousands of IGDB records.

Solution

A multiquery with membership filtering (type.id === 1 for main games), game_type filters and platform checks, sorted by release date and scored for PC compatibility.

Problem

A reliable calendar sync

Twitch's own calendar sync is unreliable, so consistency between Twitch and Google Calendar was on me.

Solution

Two-way linking through extended properties, fallback matching by time and title, non-blocking sync, and manual sync commands for repairs.

Problem

Discord's component limit

A message holds about 40 components, and a busy month needs many more.

Solution

A component budget per message, automatic splitting into follow-ups, and a first message reserved for navigation.

Problem

Time zones and DST

Every stream crosses two APIs, and an hour off is a missed stream.

Solution

Intl.DateTimeFormat computes the offset for the specific date, and every API call uses ISO 8601 with the offset.

Skills I gained

  • Discord.js v14 and Components V2

    Modern Discord bots

    Embed-free messages, interactive components, modals, slash commands, autocomplete and Discord's interaction model.

  • IGDB

    Complex game queries

    Multiquery syntax, collections, platform filters and the logic to tell canonical games from the rest.

  • Twitch Helix

    Schedule management

    App and user OAuth2, schedule segment CRUD, category search, time zones and rate limits.

  • Google Calendar

    Synchronization

    Service accounts, event CRUD, RRULE recurrence, extended properties for linking, and DST-aware time zones.

  • Firestore

    NoSQL and caching

    Document CRUD, query design, custom IDs, audit fields, and in-memory caching with TTL and LRU eviction.

  • Integration architecture

    Multi-service sync

    Master and follower patterns, two-way linking, error handling across services and keeping data consistent.

Why did you create BotFire?

Several reasons. I prefer custom solutions that fit my needs; I wanted an official, well-made way for people to suggest games; I needed better Twitch schedule management than the official, half-broken sync; and I wanted the community to have easy access to stream information. It's both a community hub and a tool that makes my life as a creator easier.

What makes the game suggestions special?

They use complex IGDB queries to find every official, canonical game in a series. The bot detects sagas, filters out remasters and ports, checks PC compatibility, prevents duplicates and stores everything in Firestore with vote tracking. It even works out what share of a whole saga is playable on PC.

How does the Twitch schedule management work?

Instant create, view and remove operations from Discord. Adding a stream creates the segment on Twitch and syncs it to Google Calendar automatically. The interface uses Components V2, with month navigation, category thumbnails and an action menu for each stream, and it's more reliable than Twitch's own calendar sync.

Why Components V2 instead of embeds?

Better UX and more practical patterns. They look better, feel like modern Discord, and handle interactive pieces like navigation buttons, action menus and dynamic content much more flexibly.

What did you learn building it?

I discovered IGDB and its well-maintained API, learned Twitch's Helix API for schedules and Firebase Firestore for persistence, and went deep on Discord.js v14 with Components V2, the Google Calendar API and multi-service synchronization.

Is the code public?

No. BotFire is fitted to my community's server and my own workflow, with integrations and features built around my specific needs.

What's your favourite feature?

The one I use most is the Twitch schedule: it's directly connected to Twitch and its form is comfortable to use. Instant changes plus the Google Calendar sync make managing my streams much easier than Twitch's own tools.

How long did it take to build?

About a month for the first version. Since then, maintenance means building new tools and fixing, updating and upgrading the existing ones, and I update this page whenever a lot changes.

What would you improve with more time?

I'd make the schedule even friendlier, but Discord Components can only go so far, and Discord's forms aren't very powerful yet. I hope they will be. There's always room to improve the integration architecture and the error handling too.

Does the community use every feature?

Only some people use its full potential: many aren't comfortable with Discord bots and are afraid of messing something up, even though it's friendly and robust. The most popular commands, /prossima-live and /suggerisci-gioco, are used all the time.

Need something like this?

Tell me what it should do, where it runs and when you need it.

Get in touch