You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

如何通过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 setupCalendarWatch every 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 fields parameter 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

火山引擎 最新活动