Automatically synchronize local Luma events to Google Calendar based on geographic location and event category.
- Overview
- Prerequisites
- Installation
- Configuration
- Deployment
- Usage
- Scheduled Sync
- Troubleshooting
- API Reference
- Security
- Project Structure
EventSync enables users to:
- Select a geographic area using an interactive map
- Choose an event category (AI, Tech, Food, Arts, etc.)
- Automatically sync matching Luma events to a dedicated Google Calendar
- Receive updates via scheduled background synchronization
Stack: Static HTML (GitHub Pages) + Google Apps Script + Google Sheets + OAuth 2.0
- Google account
- Modern web browser
- Node.js 18+
- Google Cloud project
- Google Apps Script project
- Google Sheet for user database
- GitHub account (for Pages hosting)
git clone https://github.com/diaorui/event-sync.git
cd event-syncnpm installnpm install -g @google/claspVisit Google Cloud Console and create a new project or select an existing one.
- Navigate to APIs & Services → Library
- Search for "Google Calendar API"
- Click Enable
- Go to APIs & Services → Credentials
- Click Create Credentials → OAuth client ID
- Application type: Web application
- Authorized redirect URIs:
https://yourusername.github.io - Save the Client ID and Client Secret
Visit script.google.com and create a new project.
Copy the contents of:
apps-script/Code.gsapps-script/Config.gsapps-script/appsscript.json
Into your Apps Script project.
In Apps Script Editor:
- Click Project Settings (gear icon)
- Scroll to Script Properties
- Add the following properties:
| Property Name | Value |
|---|---|
CLIENT_ID |
Your OAuth Client ID |
CLIENT_SECRET |
Your OAuth Client Secret |
DB_SHEET_ID |
Your Google Sheet ID (see below) |
- Create a new Google Sheet
- Rename the first tab to "Users"
- Add header row:
Timestamp | Email | RefreshToken | Config | CalendarID - Copy the Sheet ID from the URL:
https://docs.google.com/spreadsheets/d/{SHEET_ID}/edit - Add this ID to Script Properties as
DB_SHEET_ID
- In Apps Script Editor, click Project Settings
- Copy the Script ID (needed for deployment)
Edit docs/index.html and update:
const CLIENT_ID = 'your-client-id.apps.googleusercontent.com';
const GAS_URL = 'https://script.google.com/macros/s/.../exec';
const REDIRECT_URI = 'postmessage'; // Works on any domainNote: GAS_URL will be auto-updated if using CI/CD deployment.
Automatically deploy to Google Apps Script on every push to apps-script/ folder.
1. Login to clasp
clasp loginThis creates ~/.clasprc.json with your Google OAuth credentials.
2. Create .clasp.json
Create .clasp.json in project root with your Script ID:
{
"scriptId": "YOUR_SCRIPT_ID",
"rootDir": "./apps-script"
}Replace YOUR_SCRIPT_ID with your actual Script ID from Apps Script Project Settings.
3. Enable Apps Script API
Visit https://script.google.com/home/usersettings and enable Google Apps Script API.
4. Test Local Deployment
clasp pushYou should see:
└─ apps-script/Code.gs
└─ apps-script/Config.gs
└─ apps-script/appsscript.json
Pushed 3 files.
5. Configure GitHub Secrets
Go to your GitHub repo: Settings → Secrets and variables → Actions
Add two repository secrets:
Secret 1: CLASPRC_JSON
cat ~/.clasprc.json
# Copy entire output and paste as secret valueSecret 2: CLASP_JSON
cat .clasp.json
# Copy entire output and paste as secret valueFormat should be:
{"scriptId":"YOUR_SCRIPT_ID","rootDir":"./apps-script"}6. Deploy Frontend
Push to GitHub and enable Pages:
- Go to Settings → Pages
- Source: main branch / docs folder
- Save
Your site will be live at: https://yourusername.github.io/event-sync
Push changes to apps-script/ to trigger auto-deployment:
git add apps-script/
git commit -m "Update backend logic"
git pushWorkflow automatically:
- Pushes code to Google Apps Script
- Creates versioned deployment (e.g., @10, @11)
- Updates
GAS_URLindocs/index.html - Commits updated HTML back to repo
- Deletes old deployments (keeps last 5)
Monitor: GitHub repo → Actions tab
If not using CI/CD:
Option 1: Using clasp
npm run push # Push to Apps Script
npm run deploy # Create new deploymentOption 2: Via UI
- Copy contents of
apps-script/*.gs - Paste into Apps Script editor
- Click Deploy → New deployment
- Type: Web app
- Execute as: Me
- Who has access: Anyone
- Click Deploy
- Copy the deployment URL
- Update
GAS_URLindocs/index.htmlwith deployment URL - Commit and push to GitHub
- Visit the deployed site
- Select event category from dropdown
- Pan and zoom the map to your desired area
- Click "🔐 Authorize & Sync"
- Authorize Google Calendar access in popup
- A new calendar will be created: "Luma Events (category)"
- Events sync automatically on a regular schedule
# Make changes to backend
vim apps-script/Code.gs
# Commit and push
git add apps-script/
git commit -m "Add new feature"
git push
# GitHub Actions handles deployment automatically# Push changes to Apps Script
npm run push
# View execution logs
npm run logs
# Open script in browser
npm run open| Command | Description |
|---|---|
npm run push |
Push local files to Apps Script |
npm run deploy |
Create new deployment |
npm run logs |
View Apps Script execution logs |
npm run open |
Open script in browser |
Enable automatic background sync:
- Apps Script editor → Triggers (clock icon) → Add Trigger
- Function:
syncAllUsers| Event: Time-driven | Interval: Every 4 hours - Save
"Server configuration error"
- Verify all Script Properties are set:
CLIENT_ID,CLIENT_SECRET,DB_SHEET_ID - Check values are correct (no extra spaces or quotes)
"Invalid Category" error
- Selected category must match one in
ALLOWED_SLUGS(Code.gs:2) - Current allowed values: tech, food, ai, arts, climate, fitness, wellness, crypto
Calendar not syncing
- Check Apps Script Executions log for errors
- Verify Google Calendar API is enabled
- Ensure trigger is set up for
syncAllUsers - Check user has not revoked OAuth permissions
OAuth errors
- For Google OAuth verification, ensure Privacy Policy and Terms of Service are published at
/privacy.htmland/terms.html - Check Client ID/Secret are correct in Script Properties
- Ensure OAuth consent screen is configured with required URLs
Workflow fails
- Verify secrets:
CLASPRC_JSON,CLASP_JSON(GitHub Settings → Secrets) - Enable Apps Script API: https://script.google.com/home/usersettings
- Ensure secrets are valid JSON (no extra quotes)
"Could not find script"
- Check Script ID in
CLASP_JSONmatches your project - Test locally:
clasp open
Version mismatch / access_token error
- Match clasp versions:
clasp --versionshould be 3.x - Regenerate:
clasp logout && clasp login, updateCLASPRC_JSONsecret
Workflow not triggering
- Only triggers on
apps-script/**changes - Manual: Actions → Run workflow
Map not loading
- Check browser console for errors
- Verify internet connection (uses OpenStreetMap tiles)
Authorization popup blocked
- Allow popups for your domain
- Try in different browser
URL: Deployed web app URL from Apps Script
Method: POST
Request Body:
{
"auth_code": "4/0AbC123...",
"redirect_uri": "postmessage",
"config": {
"slug": "ai",
"north": 37.9,
"south": 37.6,
"east": -122.1,
"west": -122.5
}
}Parameters:
| Field | Type | Description |
|---|---|---|
auth_code |
string | OAuth authorization code |
redirect_uri |
string | OAuth redirect URI (use "postmessage") |
config.slug |
string | Event category (see ALLOWED_SLUGS) |
config.north |
number | Northern boundary (latitude) |
config.south |
number | Southern boundary (latitude) |
config.east |
number | Eastern boundary (longitude) |
config.west |
number | Western boundary (longitude) |
Success Response:
{
"status": "success",
"action": "created",
"email": "user@example.com",
"calendarName": "Luma Events (ai)"
}| Field | Description |
|---|---|
action |
Either "created" or "updated" |
email |
User's email address |
calendarName |
Name of the synced calendar |
Error Response:
{
"status": "error",
"msg": "Error description"
}Defined in Code.gs line 2:
tech- Technology eventsfood- Food & Drinkai- AI & Machine Learningarts- Arts & Cultureclimate- Climate & Environmentfitness- Fitness & Sportswellness- Health & Wellnesscrypto- Cryptocurrency & Web3
- Never commit secrets: Config.gs uses Script Properties, not hardcoded values
- OAuth scope: Limited to
calendar.app.created(only calendars created by this app) - Input validation: Backend validates event category against whitelist
- Concurrent access: Uses LockService to prevent database race conditions
- Credentials storage:
.clasp.jsonand.clasprc.jsonare gitignored
Never commit these files:
.clasp.json- Contains Script ID~/.clasprc.json- Contains OAuth tokens- Both are in
.gitignore
If credentials are compromised:
- Revoke access: https://myaccount.google.com/permissions
- Regenerate:
clasp logout clasp login - Update GitHub Secrets with new
CLASPRC_JSON - Rotate OAuth Client Secret in Google Cloud Console
- Update Script Properties with new
CLIENT_SECRET
The app requests:
https://www.googleapis.com/auth/calendar.app.created- Manage calendars created by this appopenid- User identificationemail- User email address
Users can revoke access anytime at: https://myaccount.google.com/permissions
event-sync/
├── .github/
│ └── workflows/
│ └── deploy.yml # GitHub Actions CI/CD workflow
├── apps-script/
│ ├── Code.gs # Backend logic (OAuth, sync, API)
│ ├── Config.gs # Configuration (Script Properties)
│ └── appsscript.json # Apps Script manifest
├── docs/
│ ├── index.html # Frontend (map, OAuth, UI)
│ ├── privacy.html # Privacy Policy (for OAuth verification)
│ ├── terms.html # Terms of Service (for OAuth verification)
│ └── eventsync-logo.png # App logo
├── scripts/
│ └── update-gas-url.js # Auto-update deployment URL
├── .gitignore # Git ignore rules
├── package.json # Node.js dependencies & scripts
└── README.md # This file
MIT