Skip to content

FoolMeOnce/discord-calendar-sync

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

11 Commits
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Discord → Google Calendar Sync

One-way sync of Discord scheduled events to a Google Calendar. Runs entirely within Google Apps Script on an hourly trigger — there is no hosted bot process. A Discord bot token is used solely as a read-only API credential.

Architecture

Google Apps Script (hourly trigger)
        │
        │  POST (proxied request)
        ▼
Cloudflare Worker (discord API proxy)
        │
        │  GET /guilds/{id}/scheduled-events
        ▼
    Discord API

Google Apps Script's UrlFetchApp sends requests from Google's shared IP pool with a user agent that Discord's Cloudflare configuration blocks (error 40333). A lightweight Cloudflare Worker acts as a relay, forwarding requests to the Discord API with a standard bot user agent. The worker is authenticated with a shared secret and only proxies to discord.com/api/v10.

Features

  • One-way sync: Discord → Google Calendar only. Never writes to Discord.
  • Create, update, delete: New Discord events are created on the calendar, changes are synced, and cancelled events are handled based on your preference.
  • Removal behavior: Configurable — delete the calendar event, keep it untouched, or strike it (prepend ❌ to the title).
  • Past event protection: If a Discord event disappears after its start time has passed, it's assumed to have ended naturally and is left on the calendar.
  • Default duration: Discord events without an end time (voice/stage events) are created as 1-hour events.
  • Location mapping: Discord "External" event locations are mapped to the Google Calendar location field.
  • Input sanitisation: Fields are truncated before being passed to the Calendar API (name: 500 chars, location: 1000 chars, description: 8000 chars).

Prerequisites

  • A Discord server you own or have Manage Events permission on
  • A Google account
  • A Cloudflare account (free tier is sufficient)

Setup

1. Create the Discord bot

  1. Go to the Discord Developer Portal and create a new application.
  2. Go to the Installation tab and set the default install link to None (required to make the bot private).
  3. Go to Bot tab and copy the bot token.
  4. Under OAuth2 → URL Generator, select the bot scope. Under Bot Permissions, check View Channels and Manage Events.
  5. Open the generated URL in your browser and add the bot to your server.

2. Get your IDs

  • Discord Server ID: In Discord, enable Developer Mode (User Settings → Advanced → Developer Mode). Right-click your server name → Copy Server ID.
  • Google Calendar ID: Google Calendar → Settings for your target calendar → "Calendar ID" under Integrate calendar. For your primary calendar, this is your Gmail address.

3. Deploy the Cloudflare Worker

  1. Go to the Cloudflare dashboard → Workers & Pages → Create.
  2. Select the "Hello World" script template and name your worker (e.g. discord-events-sync).
  3. Deploy, then edit the code and paste the contents of cloudflare-worker/worker.js. Save & Deploy.
  4. Go to the worker's Settings → Variables and Secrets and add a secret:
    • PROXY_SECRET — a strong random string (e.g. a UUID)
  5. Note your worker URL (e.g. https://discord-events-sync.your-subdomain.workers.dev).

4. Set up the Apps Script

  1. Go to script.google.com and create a new project.

  2. Delete the placeholder code in Code.gs and paste the contents of apps-script/Code.gs.

  3. Go to Project Settings (gear icon) → Script Properties and add:

    Property Value
    DISCORD_BOT_TOKEN Your bot token from step 1
    DISCORD_GUILD_ID Your server ID from step 2
    GOOGLE_CALENDAR_ID Your calendar ID from step 2
    PROXY_URL Your Cloudflare Worker URL from step 3
    PROXY_SECRET The same secret you set in the worker
    DELETE_REMOVED_EVENTS Optional: delete (default), keep, or strike
  4. Select syncDiscordEvents from the function dropdown and click Run. Authorize Calendar access when prompted. Check the execution log to confirm events synced.

  5. Select installTrigger and run it once to set up the hourly auto-sync.

How it works

Each sync cycle:

  1. Fetches all scheduled events from Discord via the Cloudflare Worker proxy.
  2. Fetches all managed calendar events (identified by a [discord-event-id:XXXXXXX] tag in the description).
  3. Creates calendar events for new Discord events.
  4. Updates calendar events where the Discord event has changed. Unchanged events are skipped.
  5. Handles calendar events whose Discord event no longer exists, according to the configured removal behavior. Past events are always left untouched.
┌─────────────────────────────────────────────────────────┐
│                    Hourly Trigger                       │
│                  syncDiscordEvents()                    │
└────────────────────────┬────────────────────────────────┘
                         │
                         ▼
              ┌─────────────────────┐
              │  Load script props  │
              │  Validate calendar  │
              └──────────┬──────────┘
                         │
          ┌──────────────┴──────────────┐
          ▼                             ▼
┌───────────────────┐        ┌────────────────────┐
│   Apps Script     │        │  Google Calendar   │
│   UrlFetchApp     │        │  CalendarApp       │
│   (POST request)  │        │  (1wk back–2yr fwd)│
└─────────┬─────────┘        └──────────┬─────────┘
          │                             │
          ▼                             │
┌───────────────────┐                   │
│ Cloudflare Worker │                   │
│ - Auth secret     │                   │
│ - Validate path   │                   │
│ - Whitelist method│                   │
└─────────┬─────────┘                   │
          │                             │
          ▼                             │
┌───────────────────┐                   │
│    Discord API    │                   │
│    GET /guilds/   │                   │
│    {id}/scheduled │                   │
│    -events        │                   │
└─────────┬─────────┘                   │
          │                             │
          ▼                             ▼
┌──────────────────────────────────────────────────────┐
│              Compare Discord ↔ Calendar              │
│        (matched by [discord-event-id:XXXX] tag)      │
└───┬──────────────────┬───────────────────┬───────────┘
    │                  │                   │
    ▼                  ▼                   ▼
┌──────────┐   ┌──────────────┐   ┌──────────────────┐
│   NEW    │   │   EXISTING   │   │ GONE FROM DISCORD│
│          │   │              │   │                  │
│ Create   │   │ Compare all  │   │ Past? → Skip     │
│ calendar │   │ fields:      │   │                  │
│ event    │   │ - title      │   │ Otherwise:       │
│          │   │ - start/end  │   │ delete: remove   │
│ Default  │   │ - description│   │ strike: add ❌   │
│ 1hr if   │   │ - location   │   │ keep:   leave it │
│ no end   │   │              │   │                  │
│ time     │   │ Changed? →   │   │ Clear sync tag   │
│          │   │   Update     │   │ (strike/keep)    │
│          │   │ Same? →      │   │                  │
│          │   │   Skip       │   │                  │
└──────────┘   └──────────────┘   └──────────────────┘
    │                  │                   │
    └──────────────────┴───────────────────┘
                       │
                       ▼
              ┌─────────────────┐
              │  Log summary    │
              │  Created: N     │
              │  Updated: N     │
              │  Unchanged: N   │
              │  Removed: N     │
              │  Skipped: N     │
              │  Errors: N      │
              └─────────────────┘

Quotas and limits

  • Cloudflare Workers free tier: 100,000 requests/day. An hourly sync uses 24.
  • Google Calendar API: Daily quota is generous for personal use. Each create/update/delete is one call.
  • Apps Script UrlFetchApp: 20,000 calls/day for consumer accounts.
  • CalendarApp.getEvents(): Returns up to ~2,500 events per call.

License

MIT

About

Discord scheduled events → Google Calendar sync. Runs hourly via Google Apps Script and a Cloudflare Worker proxy.

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors