Skip to content

Instantly share code, notes, and snippets.

@cmbuckley
Last active July 29, 2022 16:33
Show Gist options
  • Save cmbuckley/587f913627c843f4becc8c5db9d4a82e to your computer and use it in GitHub Desktop.
Save cmbuckley/587f913627c843f4becc8c5db9d4a82e to your computer and use it in GitHub Desktop.
const config = {
'[email protected]': {
prefix: 'Source',
color: CalendarApp.EventColor.PALE_RED,
visibility: 'private'
},
};
function onCalendarChanged(trigger) {
copyEvents(trigger.calendarId);
}
function copyAll() {
Object.keys(config).forEach(copyEvents);
}
function copyEvents(sourceId) {
if (!config[sourceId]) {
throw new Error('Missing config for ' + sourceId);
}
// avoid multiple scripts running at the same time
const lock = LockService.getScriptLock();
lock.tryLock(1000);
if (!lock.hasLock()) {
console.log('Process already running');
return;
}
console.log('Copying events from ' + sourceId);
const summaryPrefix = (config[sourceId].prefix ? `[${config[sourceId].prefix}] ` : '');
const syncDays = config[sourceId].syncDays || 14;
const targetId = Session.getActiveUser().getEmail();
// start and end dates
let startDate = new Date();
let endDate = new Date();
endDate.setDate(startDate.getDate() + syncDays);
// delete and recreate in case things move
// @todo if there's no prefix, it'll delete everything in your calendar!
if (summaryPrefix) { cleanup(syncDays, summaryPrefix); }
// get all events
const listResponse = Calendar.Events.list(sourceId, {
timeMin: startDate.toISOString(),
timeMax: endDate.toISOString(),
singleEvents: true,
});
// loop over and copy
listResponse.items.forEach(function (event) {
const summary = (event.summary || '(No title)');
console.log('Copying event: ' + summary + ' (' + (event.start.dateTime || event.start.date) + ')');
Calendar.Events.insert({
start: event.start,
end: event.end,
summary: summaryPrefix + summary,
description: event.description,
conferenceData: event.conferenceData,
colorId: config[sourceId].color || 0,
visibility: config[sourceId].visibility || event.visibility,
attendees: event.attendees ? [{
email: targetId,
responseStatus: event.attendees.find(a => a.self).responseStatus
}] : []
}, targetId, {
conferenceDataVersion: 1
});
Utilities.sleep(500);
});
lock.releaseLock();
console.log('All events copied');
}
function cleanup(daysAhead, titlePrefix) {
let calendar = CalendarApp.getCalendarById(Session.getActiveUser().getEmail());
let startDate = new Date();
let endDate = new Date();
endDate.setDate(startDate.getDate() + daysAhead);
calendar.getEvents(startDate, endDate).forEach(function (event) {
if (event.getTitle().startsWith(titlePrefix)) {
console.log('Deleting event: ' + event.getTitle() + ' (' + event.getStartTime().toLocaleString('en-GB') + ')');
try {
event.deleteEvent();
} catch (e) {
console.log('Failed to delete: ' + e);
}
Utilities.sleep(500);
}
});
}
@cmbuckley
Copy link
Author

cmbuckley commented Jul 20, 2022

Copy Calendar Events

This Apps Script copies calendar events from any number of source calendars. This can be useful
when use multiple work calendars and need colleagues to see your true availability.

Note

This script currently deletes events from your target calendar and recreates them. Not only is this a naïve approach, it is risky that it might delete events not part of the sync that just happen to have a similar title.

Installation

  1. In your source calendar, click the actions button next to the calendar, and click Settings and sharing.
  2. Scroll to "Share with specific people", and add the email address of your target calendar.
  3. Choose "See all event details". (This is needed to replicate the status / conference info to your target calendar.)
  4. You will receive an email to your target account sharing this calendar. Click Add this calendar.
  5. Open Google Apps Script and log in as your target account.
  6. Click New project to create the project for the script.
  7. Click "Untitled project" at the top to give the project a name, such as "Copy calendar events", and click Rename.
  8. Replace the pre-filled code in Code.gs with the contents of copyCalendarEvents.gs. Update the configuration appropriately (see below).
  9. Click the Save icon to save the project.
  10. Next to "Services" click + to add a service.
  11. Select "Google Calendar API" and click Add.
  12. Click Deploy, then New deployment.
  13. Click the cog next to "Select type" and choose "Web app".
  14. For "Execute as", select "User accessing the web app".
  15. Click Deploy.
  16. In the left navigation, click "Triggers", then click Add Trigger.
  17. Set the following options:
    • Function to run: copyAll
    • Event source: from calendar
    • Calendar owner email: your source calendar email
  18. Click Save. Sign in as your target email address to allow the script. (You may need to allow popups)
  19. Click Save again.
  20. Back in the Editor, you can select to run the function "copyAll" from the menu to test the sync.

Configuration

The script begins with a config object taking the following options:

Parameter Value Description
<source_calendar> string Email address of the source calendar. The target calendar must be permitted to view this calendar.
<source_calendar>.prefix string A prefix for the event titles. If the prefix is Prefix, the title is prefixed with [Prefix] . Defaults to empty string, which adds no prefix to the title.
<source_calendar>.color integer One of the EventColor enum values. Defaults to 0, which does not change the event's colour.
<source_calendar>.visibility string One of "default", "public" or "private". Overrides the source calendar event's visibility, for instance to hide sensitive information. Defaults to the event's existing visibility.
<source_calendar>.syncDays integer How many days ahead to sync calendar events. Defaults to 14.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment