```html

Automating Boat Cleaning Dispatch and Calendar Synchronization Across Multiple Platforms

This session focused on consolidating fragmented cleaning service operations into a unified dispatch system, while simultaneously building out calendar synchronization infrastructure to keep Google Calendar authoritative across multiple platforms. The work involved Python automation, Google Apps Script deployment, and Lambda API integration—all designed to reduce manual coordination overhead for a multi-venue operation.

The Problem: Manual Coordination Across Disconnected Systems

Prior to this session, boat cleaning requests were scattered across multiple platforms (GetMyBoat, Boatsetter, direct email) with no centralized dispatch mechanism. Calendar holds for recurring events (like Sea Scout Wednesday sessions) were being added manually, and calendar synchronization between Google Calendar and external booking platforms was non-existent. This created both operational risk and significant manual work.

Solution Architecture

Dispatch System

Created a new dispatch automation layer in /Users/cb/Documents/repos/tools/dispatch_boat_cleaner.py. This Python script serves as the dispatch orchestrator:

  • Input sources: Monitors email inboxes (via Gmail API) for cleaning requests from GetMyBoat, Boatsetter, and direct channels
  • Processing: Parses booking details, extracts dates/times, determines vessel and location, maps to cleaner availability
  • Output: Creates structured dispatch records and triggers outbound SMS/email notifications to assigned cleaners

The script uses environment-loaded credentials for both Gmail API and platform authentication. Credentials are stored in repos.env and loaded via standard Python os.getenv() calls—never hardcoded.

Supporting infrastructure was added in /Users/cb/Documents/repos/tools/platform_inbox_scraper.py, which performs the raw inbox monitoring, and /Users/cb/Documents/repos/tools/deploy_inbox_scraper.sh, which handles deployment and restart logic for the scraper as a long-running process.

Calendar Synchronization

The core calendar sync logic lives in /Users/cb/Documents/repos/sites/queenofsandiego.com/rady-shell-events/apps-script-replacement/CalendarSync.gs. This Google Apps Script executes in Google's managed environment and handles bidirectional calendar sync:

  • Polling interval: Configurable via constants in the GAS file (default 15-minute check)
  • Source of truth: Google Calendar is authoritative; changes propagate outbound to GetMyBoat and Boatsetter iCal feeds
  • Conflict resolution: Calendar-sourced events take precedence; platform-sourced events are merged only if they don't conflict
  • Email notifications: Uses GmailApp.sendEmail() for error reporting and status updates sent to operations email

The script was deployed to the Google Apps Script project identified by its .clasp.json configuration. The deployment process involved:

clasp login
clasp push --force

This overwrites the remote GAS file with the updated CalendarSync.gs, making changes immediately effective within the Google Apps Script runtime.

Infrastructure Integration

Lambda-Backed Calendar API

Existing calendar operations are exposed via a Lambda function (exact name found by querying AWS Lambda API) connected to API Gateway. The Lambda function accepts JSON payloads with action names like add-calendar-event, list-events, and remove-event.

During this session, seven recurring Sea Scout Wednesday holds were added to Google Calendar via direct Lambda invocation:

aws lambda invoke \
  --function-name [CALENDAR_LAMBDA_NAME] \
  --payload '{"action":"add-calendar-event","title":"Sea Scout Wednesday Hold","start":"2025-05-07T18:00:00Z",...}' \
  response.json

This approach bypasses the need for manual calendar.google.com access and creates an auditable API layer. The Lambda function maintains its own Google Calendar API credentials (service account key, stored securely in AWS Secrets Manager, not in code).

Dashboard Integration

Updated the dashboard at /tmp/dashboard_index.html to reflect dispatch and calendar status. Changes include:

  • New section tracking active boat cleaning assignments with status badges (pending, en-route, completed)
  • Real-time calendar hold validation showing which recurring events are properly synced across platforms
  • Photo upload auto-send fix: modified the JavaScript file upload handler to automatically deliver uploaded images to relevant staff via email, reducing manual forwarding work

The photo upload mechanism uses the existing SES email integration, so images are base64-encoded in the email body and delivered within seconds of upload.

Key Technical Decisions

Why Google Calendar as Authoritative Source?

Google Calendar was chosen as the source of truth because:

  • It's already in use by all team members and has a proven mobile experience
  • Google Calendar API is mature, well-documented, and has built-in sharing/permission models
  • CalendarSync.gs runs in Google's infrastructure, reducing deployment complexity
  • iCal feed export is native, making it trivial to push to third-party booking platforms

Why Python for Dispatch?

The dispatch system is Python rather than Node.js or Go because:

  • Gmail API libraries are more mature in Python (google-auth-oauthlib is the reference implementation)
  • The team has existing Python infrastructure for similar tasks
  • Email parsing complexity benefits from Python's built-in email module and better string handling
  • Natural deployment target to Lambda or EC2 without additional tooling

Why Lambda + API Gateway for Calendar?

Rather than exposing the GAS directly, a Lambda layer was added because:

  • API Gateway provides request validation, rate limiting, and authentication via API keys/tokens
  • Lambda adds a logging/monitoring boundary—all calendar operations now appear in CloudWatch Logs with request/response payloads
  • Easier to add authorization logic (check if requester is staff before allowing calendar modifications)
  • Provides a migration path toward a custom calendar service if needed

Operational Workflow

The integrated system now works as follows:

  1. Cleaner schedules hold in Google Calendar (or manually creates event in calendar.google.com)
  2. CalendarSync.gs detects the new event on its 15-minute poll cycle
  3. Event is published to GetMyBoat and Boatsetter iCal feeds as a "blocked" or "hold" entry
  4. Incoming cleaning request hits inbox via platform or email
  5. platform_inbox_scraper.py detects the request and triggers dispatch_boat_cleaner.py
  6. Dispatch script checks calendar availability, assigns cleaner, sends SMS notification
  7. Cleaner confirms/declines; status updates flow back into dashboard
  8. After completion, CalendarSync checks for updates from platforms and syncs back to Google Calendar

What's Next

  • Deployment to production: The dispatch scripts are currently in