---
title: One Calendar Drives My Twitch Schedule and Discord Events
description: How one Google Calendar drives my Twitch schedule and Discord events, and why a finite series has to become one-off Discord events.
image: https://willcurran.com/hubfs/will-curran-brand/one-calendar-twitch-discord-events-cover-nolane.png
---

[Skip to content](https://willcurran.com/blog/one-calendar-twitch-discord-events#main)

[Will Curran](https://willcurran.com/)

[Home](https://willcurran.com/) [Portfolio](https://willcurran.com/portfolio) [Press](https://willcurran.com/media) [Blog](https://willcurran.com/blog) [Favorites](https://willcurran.com/favorites) [Speaking](https://willcurran.com/speaking) [Consulting](https://willcurran.com/consult) 

[Hire Will](https://willcurran.com/hire)

[Writing](https://willcurran.com/blog) Music — One Calendar Drives My Twitch Schedule and Discord Events [Subscribe](https://willcurran.com/blog/subscribe)

Music

# One Calendar Drives My Twitch Schedule and Discord Events

![](https://willcurran.com/hs-fs/hubfs/will-curran-brand/will-curran-avatar.jpg?width=44&height=44&name=will-curran-avatar.jpg)

Will Curran Sep 10, 2026 · 7 min read

<https://www.linkedin.com/sharing/share-offsite/?url=https%3A%2F%2Fwillcurran.com%2Fblog%2Fone-calendar-twitch-discord-events>

On this page

![An abstract dark graphic: a grid of calendar squares with a few lit in orange, linked by dotted orange lines to a schedule strip and a wide event card, next to the title One calendar, two schedules.](https://willcurran.com/hs-fs/hubfs/will-curran-brand/one-calendar-twitch-discord-events-cover-nolane.png?width=1600&height=900&name=one-calendar-twitch-discord-events-cover-nolane.png)

In September 2026 I built a small service that copied my stream schedule from one Google Calendar onto my Twitch schedule and into native events on my Discord server, with generated cover art on every Discord event. Google became the only place I edited. A change made directly on Twitch or Discord got overwritten on the next pass.

The twist was on the Discord side. Discord scheduled events do support recurrence, but an app can't set when a series ends, so any recurring event an app creates repeats forever. A Google series that stops on a date, or after a set number of times, can't become a Discord recurrence at all. It had to be expanded into one-off events.

## Three systems, three answers

Before this, I kept the calendar, the Twitch schedule and the Discord events in step by hand, and they drifted. When I sat down to fix it, Twitch still listed events that were already over and was missing new ones from the calendar.

So the rule became one source and one direction. The service read the calendar every five minutes and pushed changes outward.

## What a Discord recurrence can say

Discord describes its recurrence rule as a subset of iCalendar, the standard Google Calendar uses. The [guild scheduled event docs](https://discord.com/developers/docs/resources/guild-scheduled-event) list the limits in a "System limitations" section, under the heading "The following fields cannot be set by the client / application". The fields listed are `count`, `end` and `by_year_day`.

Without `count` or `end`, a recurring event made by an app has no last date. There are more limits: a weekly rule can name only one weekday, and an interval other than 1 is allowed only for weekly rules. There is also no field for skipping a single date.

That set the mapping rule. A Google series became one recurring Discord event only when Discord can express it and it has no end. Everything else became individual one-off events inside a rolling 90-day window: a series with an end date or a count, a monthly pattern like the first and third week.

Twitch needed less translation. A weekly series mapped to one recurring segment.

![Decision diagram for mapping one Google Calendar series to Discord. If the series has an end date or count, Discord cannot express its rule, or a date in it is cancelled, it becomes one-off Discord events inside a rolling 90-day window. Otherwise it becomes one recurring Discord event. A side note says Twitch takes a weekly series as one recurring segment.](https://willcurran.com/hs-fs/hubfs/will-curran-brand/one-calendar-twitch-discord-events-series-mapping.png?width=1440&height=1172&name=one-calendar-twitch-discord-events-series-mapping.png)

## Cancelling one date was the hard case

Discord's rule can't express a weekly series with one week missing, so the only faithful version is a set of one-off events with that date left out. When a series had a cancelled date, the service flipped it to one-offs.

The flip had to be sticky. Once the cancelled date scrolled out of the window, the exception disappeared from what the calendar returned and the series looked plain again. Without a record that it had been expanded, the next pass would turn it back into a recurrence, then delete and recreate every event. The flip also had a cost Discord's API leaves no way around: RSVPs on the old recurring event don't carry over to the new one-offs.

## Only touch what it created

The Discord server also held events that had nothing to do with my calendar. A sync that matched everything in the server against everything on the calendar would have deleted them. So the service kept its own record of every event it created, and anything outside that record was invisible to it.

- **Dry run by default.** A pass wrote nothing unless it was told to.
- **Bounded deletes.** A pass that would delete more than a few events stopped instead.
- **Empty means broken.** A calendar that came back empty was an error, never a reason to delete everything.
- **Edit in place.** A moved or renamed event was modified, not recreated.

Early on, the tag marking an event as the service's own sat in the event description. I had it moved out. The description was something my community read, so from then on it led with the link to watch, and ownership lived only in the service's database.

## Cover art at 2.5:1

The banners were rendered at 1280 by 512.

A recurring event is one Discord object with one cover, so its art was keyed on the series title. One-offs were keyed on title and start time. An unchanged event cost no new generation, so frequent polling added nothing. The design estimated about ten generations a month, for under a dollar. Art was best-effort: if generation failed, the event was still created, just without a cover.

![Diagram of a 2.5:1 event cover, 1280 by 512 pixels, made of abstract generated art. Below it, two cache rules: a recurring series gets one cover keyed on its title, and a one-off event is keyed on its title and start time.](https://willcurran.com/hs-fs/hubfs/will-curran-brand/one-calendar-twitch-discord-events-banner-frame.png?width=1440&height=1066&name=one-calendar-twitch-discord-events-banner-frame.png)

## What only live data caught

I built it in one day, design to last fix, with an AI coding assistant. The tests passed against hand-written fixtures, and the real calendar and APIs disagreed with them.

- **A weekly rule with no weekday.** The calendar returned the weekly series as `FREQ=WEEKLY` with no day named; the standard takes the weekday from the start date. The mapper expected a day, fell through to expansion, and would have created about 13 separate Discord events for one weekly series.
- **Timestamps compared as text.** Discord returned times in UTC and the calendar in local time. As strings they never matched, so every pass saw drift and re-patched every event. Comparing instants fixed it.
- **An ID that changes every week.** Twitch's ID for an occurrence of a recurring segment included the week. Keyed on it, the series kept re-adopting itself, and deleting it would have quietly stopped working. Keying on the stable part fixed it.
- **Duplicates from a failed save.** The service once couldn't save its records. The Discord events were created before the save failed, so the next pass made them again: two duplicates. After the fix, every pass proved it could write its records before touching either platform, and a matching existing event was always claimed rather than created twice.

By the end of that day the service had 98 tests.

## If you are about to try this

- **Pick one source and sync one way.** Two-way sync across three systems is a much harder project.
- **Read the recurrence limits before you design.** A series with an end, mapped to a recurrence that can't end, keeps going after the real series stops.
- **Keep your own record of what you created.** Treat everything else in the server as someone else's.
- **Run against your real calendar early, in dry run.** Fixtures are tidier than real data.

## Build it yourself

What it does: one calendar drives a Twitch schedule and a Discord server's events, with a cover image on each Discord event. The parts:

- **Google Calendar API** for both series and expanded occurrences.
- **A Discord bot** allowed to manage the server's scheduled events.
- **The Twitch API's schedule endpoints** for your channel.
- **A small database**, such as SQLite, holding what the service created on each side.
- **Optional: an image generator** plus your own overlay.
- **Something always on** to run a polling loop.

How the pieces fit: a loop reads the calendar, works out what each platform should hold, compares that with its own record, and applies only the differences, each platform on its own so a failure on one doesn't block the other. Cost: the art was estimated at under a dollar a month. Time: one day for me.

Give this to your AIHelp me build a one-way sync from one Google Calendar to my Twitch schedule and my Discord server's scheduled events. Google is the only source; edits on Twitch or Discord get overwritten. Before mapping recurrences, check Discord's current docs for which recurrence fields an app can't set (count and end were on that list when I built mine), and only turn a Google series into one recurring Discord event if Discord can express it and it never ends. Expand everything else into one-off events inside a rolling window, including any series with a cancelled occurrence, and remember that a series was expanded so it doesn't flip back. Keep a database of every event the sync created and never touch anything outside it. Default to dry run, stop if a pass would delete more than a few events, treat an empty calendar response as an error, and edit events in place. Optionally generate a cover image per event, cached so unchanged events cost nothing.

If you have mapped one calendar onto a platform with stricter rules than iCalendar, I would like to hear how you handled it.

Filed under [Music](https://willcurran.com/blog/tag/music)

![](https://willcurran.com/hs-fs/hubfs/will-curran-brand/will-curran-avatar.jpg?width=64&height=64&name=will-curran-avatar.jpg)

Will Curran

I spent sixteen years building one company, and I have been in the event industry since 2007. These days I write about the technology I actually use — trucks, radios, home infrastructure, and the tools that survive past the first invoice.

[All posts by Will →](https://willcurran.com/blog/author/will-curran)

Get the next one by email

One email per post. No digest, no sponsors.

[← Previous Three Answers I Found in the Home Assistant iOS App's Source](https://willcurran.com/blog/three-answers-in-the-ios-app-source) [Next → One Set of Cameras, Two Uses: NDI in My DJ Studio](https://willcurran.com/blog/one-set-of-cameras-two-uses)

## Related reading

[Music My Chat Bot Called a House Set Drum and Bass 6 min read](https://willcurran.com/blog/chat-bot-called-a-house-set-drum-and-bass) [Music When the Stream Got Stuck on Song 1: Seeing Layered Mixes 7 min read](https://willcurran.com/blog/layered-mixes) [Music How I Pick the Best Live Sets for a Playlist 6 min read](https://willcurran.com/blog/best-live-sets-playlist) [Music Getting Viewers onto My Drop List Without Leaving Twitch 5 min read](https://willcurran.com/blog/drop-list-without-leaving-twitch)

[Will Curran](https://willcurran.com/)

[LinkedIn — 29,339+ followers](https://www.linkedin.com/in/willcurran) [GitHub](https://github.com/rancur) [YouTube](https://www.youtube.com/channel/UCJgscc27l4fpkWx1SeK05xg/) [Spotify](https://open.spotify.com/user/rancur) [SoundCloud DJ mixes](https://www.soundcloud.com/thewillsee) [Twitch](https://www.twitch.tv/thewillsee) [Last.fm](https://www.last.fm/user/default) [Trakt — movies & TV](https://trakt.tv/users/willcurran) [Goodreads](https://www.goodreads.com/user/show/55302838-will-curran) [Will See Music](https://www.thewillsee.com) [Arizona Meshtastic](https://azmsh.net) [Electric Forest Meshtastic](https://efmesh.com)

© 2026 Will Curran — All rights reserved. [RSS](https://willcurran.com/blog/rss.xml) [Back to top](https://willcurran.com/blog/one-calendar-twitch-discord-events#main)

```json
{
  "@context" : "https://schema.org",
  "@type" : "BlogPosting",
  "author" : {
    "@type" : "Person",
    "name" : "Will Curran",
    "url" : "https://willcurran.com/blog/author/will-curran"
  },
  "dateModified" : "2026-10-22T18:33:55.379Z",
  "datePublished" : "2026-09-10T16:00:00.000Z",
  "headline" : "One Calendar Drives My Twitch Schedule and Discord Events",
  "image" : [ "https://willcurran.com/hubfs/will-curran-brand/one-calendar-twitch-discord-events-cover-nolane.png" ],
  "mainEntityOfPage" : {
    "@id" : "https://willcurran.com/blog/one-calendar-twitch-discord-events",
    "@type" : "WebPage"
  },
  "publisher" : {
    "@type" : "Organization",
    "logo" : {
      "@type" : "ImageObject",
      "url" : "https://willcurran.com/hubfs/image-1.png"
    },
    "name" : "Will Curran LLC"
  }
}
```