```html

Building a Multi-Tenant Event Synchronization System: CalendarSync.gs and Boat Booking Integration

This session focused on consolidating event management across multiple platforms: Google Calendar, boat booking services (GetMyBoat, Boatsetter), and internal scheduling systems. The core challenge was synchronizing calendar data bidirectionally while maintaining separation between business domains (venue events, boat cleanings, scout activities).

What Was Done

We completed three major infrastructure components:

  • Enhanced CalendarSync.gs — the Google Apps Script file managing calendar synchronization logic
  • Created dispatch_boat_cleaner.py — a Python tool for scheduling boat maintenance tasks
  • Built platform_inbox_scraper.py and campaign_scheduler.py — email and event automation tools

The underlying architecture moved from manual task scheduling to an automated event pipeline that ingests booking data, synchronizes calendar state, and triggers downstream workflows.

CalendarSync.gs: The Synchronization Engine

CalendarSync.gs lives in the Google Apps Script project replacement directory at:

/Users/cb/Documents/repos/sites/queenofsandiego.com/rady-shell-events/apps-script-replacement/CalendarSync.gs

This file handles bidirectional synchronization between Google Calendar and external booking platforms. The architecture uses:

  • Polling mechanism — Retrieves calendar events at configurable intervals (currently checking for changes every N minutes)
  • Event state tracking — Maintains a record of synced events to detect additions, modifications, and deletions
  • Platform-specific adapters — Separate logic branches for GetMyBoat iCal feeds, internal calendar holds, and automated boat cleaning schedules
  • Email notifications — Triggers sendEmail() calls to stakeholders when events are added or modified

Key function names we worked with:

onOpen()              // Initialize script UI triggers
syncCalendarEvents()  // Main polling loop
parseICalFeed()       // Parse external iCal from boat platforms
addEventToCalendar()  // Insert events into Google Calendar
notifyStakeholders()  // Send email notifications

Why this approach: Google Calendar is the single source of truth. By syncing external bookings into it, we avoid maintaining separate event databases. The polling model (rather than webhooks) was chosen because GetMyBoat and Boatsetter don't offer reliable push notifications for iCal changes, and Google Apps Script's time-based triggers are sufficient for the business velocity we're operating at.

Boat Cleaner Dispatch System

Created at:

/Users/cb/Documents/repos/tools/dispatch_boat_cleaner.py

This Python utility reads boat maintenance events from a calendar source and dispatches cleaning tasks. The workflow:

  1. Reads Google Calendar for boat-related events (filtered by label or description)
  2. Extracts location, time, and boat ID metadata
  3. Creates dispatch records with contractor assignments
  4. Logs task status to campaign_schedule.json for auditing

The script uses environment variables (loaded from repos.env) to authenticate with:

  • Google Calendar API (service account credentials)
  • Internal task tracking system
  • Email system (SES) for contractor notifications

Why Python over GAS: This logic needed to live outside the Apps Script environment because it coordinates multiple external systems. Python's ecosystem (requests, boto3, google-auth) made cross-platform integration cleaner. It runs on a Lambda function or scheduled EC2 instance, separate from the synchronization layer.

Campaign and Event Scheduling Infrastructure

Built two complementary systems:

Campaign Scheduler (campaign_scheduler.py):

/Users/cb/Documents/repos/tools/campaign_scheduler.py

Manages email blast campaigns with template composition. Reads from:

/Users/cb/Documents/repos/tools/campaign_schedule.json

Which contains scheduling metadata (send times, recipient segments, template references). The system:

  • Hydrates HTML templates from /tools/templates/ with dynamic content
  • Batches sends via AWS SES
  • Tracks delivery status and bounces
  • Supports A/B variant scheduling (rady_shell_blast1.html vs. blast2.html)

Platform Inbox Scraper (platform_inbox_scraper.py):

/Users/cb/Documents/repos/tools/platform_inbox_scraper.py

Listens to incoming messages from booking platforms (GetMyBoat, Boatsetter, et al.) via Gmail API. When new inquiries arrive, it:

  1. Extracts booking metadata (dates, vessel, party size)
  2. Validates against calendar hold conflicts
  3. Routes responses through appropriate business logic
  4. Logs interactions to platform-specific dashboards

Why separate systems: Campaign scheduling and inbox scraping have different trigger models — campaigns are time-driven (cron), inbox scraping is event-driven (email arrival). Decoupling them prevents resource contention and allows independent scaling.

Infrastructure and Deployment

Google Apps Script:

CalendarSync.gs is deployed via .clasp.json configuration. Located at:

/Users/cb/Documents/repos/sites/queenofsandiego.com/rady-shell-events/apps-script-replacement/.clasp.json

The scriptId maps to the GAS project. Updates are pushed via:

clasp push

This compiles the TypeScript/JavaScript and deploys to Google's infrastructure, making functions immediately available to calendar triggers.

Python deployment:

Both Python scripts have corresponding deployment shells:

/Users/cb/Documents/repos/tools/deploy_campaign_scheduler.sh
/Users/cb/Documents/repos/tools/deploy_inbox_scraper.sh

These handle:

  • Environment variable injection (secrets from repos.env)
  • Dependency installation (pip requirements)
  • Lambda function updates or EC2 task scheduling
  • CloudWatch log group configuration

Why Lambda over EC2: For event-driven work (inbox scraping), Lambda's pay-per-invocation model is more cost-efficient. Time-based campaigns use CloudWatch Events to trigger Lambda on schedule — simpler than managing cron jobs on persistent compute.

Key Architectural Decisions

1. Calendar as source of truth — Rather than maintaining event state in multiple databases, Google Calendar becomes the system of record. This simplifies consistency guarantees.