如何通过Google Calendar API程序化接收事件通知?解决实时广播需求
Solving Google Calendar Real-Time Notifications (Beyond Polling & Event Changes)
Hey there! Let's break down how to build a real-time notification system for Google Calendar that avoids quota-heavy polling, handles both event changes and upcoming reminders for existing events. Here's a step-by-step approach tailored to your Express + Angular Service Worker setup:
1. Use Google Calendar Push Notifications (Webhooks) for Event Changes
First, replace polling for event updates with Google's official webhook system—this lets Google notify your server directly when events are created, updated, or deleted.
Setup Steps:
- Verify your domain: Google requires HTTPS and domain verification (via Google Cloud Console) to send webhooks. Ensure your Express server is hosted on a verified HTTPS domain.
- Create a webhook endpoint in Express:
const express = require('express'); const app = express(); app.use(express.json()); // Webhook endpoint to receive Google Calendar updates app.post('/google-calendar-webhook', (req, res) => { // Validate request comes from Google (use your custom token) if (req.headers['x-goog-token'] !== 'your-custom-verification-token') { return res.sendStatus(403); } // Extract change metadata const resourceId = req.headers['x-goog-resource-id']; const channelId = req.headers['x-goog-channel-id']; // Trigger a refresh of your local event/reminder queue refreshReminderQueue(auth); // auth = your Google API auth object res.sendStatus(200); }); - Subscribe to push notifications: Use the Google Calendar API to create a watch subscription. This tells Google to send updates to your webhook:
const { google } = require('googleapis'); const calendar = google.calendar('v3'); async function setupCalendarWatch(auth) { const response = await calendar.events.watch({ auth: auth, calendarId: 'primary', requestBody: { id: 'unique-watch-id-123', // Unique ID for your subscription type: 'web_hook', address: 'https://your-domain.com/google-calendar-webhook', // Your endpoint token: 'your-custom-verification-token', // Match the token in your endpoint expiration: Date.now() + 7 * 24 * 60 * 60 * 1000, // 7-day expiration (refresh periodically) }, }); console.log('Push subscription active:', response.data); } - Refresh subscriptions: Since subscriptions expire, set up a cron job to re-run
setupCalendarWatchevery 6 days to avoid gaps.
2. Maintain a Local Reminder Queue for Existing Events
To handle upcoming reminders for existing events (not just changes), you'll need to:
- Load upcoming events once (or low-frequency refresh): Pull events from the Calendar API once on server start, and refresh daily (not hourly—this keeps quota usage low).
- Schedule reminders locally: Use a job scheduler to trigger notifications when reminders are due, then push them to your Socket.io server.
Example with node-schedule:
const schedule = require('node-schedule'); let reminderJobs = new Map(); // Track active reminder jobs (event ID + reminder time as key) async function refreshReminderQueue(auth) { // Cancel all existing jobs to avoid duplicates reminderJobs.forEach(job => job.cancel()); reminderJobs.clear(); // Fetch upcoming events (next 7 days) const eventsResponse = await calendar.events.list({ auth: auth, calendarId: 'primary', timeMin: new Date().toISOString(), timeMax: new Date(Date.now() + 7 * 24 * 60 * 60 * 1000).toISOString(), singleEvents: true, orderBy: 'startTime', fields: 'items(id,summary,start,reminders)', // Only fetch needed fields to save quota }); const events = eventsResponse.data.items; events.forEach(event => { // Check for custom reminders (ignore default reminders if needed) if (event.reminders?.overrides) { event.reminders.overrides.forEach(reminder => { if (reminder.method === 'popup') { // Calculate reminder trigger time (event start - reminder minutes) const eventStart = new Date(event.start.dateTime || event.start.date); const reminderTime = new Date(eventStart.getTime() - reminder.minutes * 60 * 1000); // Only schedule future reminders if (reminderTime > new Date()) { const job = schedule.scheduleJob(reminderTime, () => { // Send reminder to frontend via Socket.io io.emit('calendar-reminder', { title: event.summary, eventTime: event.start.dateTime || event.start.date, message: `Upcoming event: ${event.summary}` }); }); reminderJobs.set(`${event.id}-${reminder.minutes}`, job); } } }); } }); }
3. Connect to Your Angular Service Worker
Once your Socket.io server sends a reminder or event change:
- In your Angular Service Worker, listen for Socket.io events.
- Use the browser's Notification API to display push notifications (ensure users have granted notification permissions).
4. Quota-Saving Tips
- Fetch only necessary fields: Use the
fieldsparameter in Calendar API calls to limit response size (as shown in the code examples). - Avoid redundant API calls: Only refresh the reminder queue when a webhook triggers an event change, or once daily.
- Cache event data: Store event details locally (e.g., in Redis) to avoid re-fetching unchanged data.
内容的提问来源于stack exchange,提问作者rassa45




