-
-
Save vaurora/880b6edbe7d06fad76db062481170ab0 to your computer and use it in GitHub Desktop.
Sync events from a personal Google calendar to a work Google calendar
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// Sync one or more personal calendars to a work calendar - and you can sync the work calendar back to | |
// your personal calendars too! Put this into a Google Apps Script and set up an hourly trigger. | |
// | |
// IMPORTANT: To run the script, you must enable the "Calendar API" under the "Services" menu on the left | |
// of the Google Apps Script IDE. | |
// | |
// Author: Oliver J. Woodford https://gist.github.com/ojwoodford | |
// Small tweaks: Valerie Aurora | |
function SyncMyCal() { | |
var options = { | |
'targetEventTitle': "Busy (sync'd event)", // What event title do you want ported events to have | |
'useSourceTitle' : true, // If you want sync'd events to have the same title as the source | |
'daysahead': 30, // How many days ahead do you want to sync events over | |
'ignorealldayevents': true, // Do you want to ignore all day events in your "from" calendars | |
'ignorethesedays': [0,6], // Are there days of the week you don't want to sync? 0 = Sunday | |
'maxhoursbetweenruns': 1, // How many hours between scheduled runs of the calendar sync (you need to set these up) | |
'onlybusyevents': true, // Only copy events marked 'busy' | |
'busyness': 1, // Should events be marked 'busy' by default (0: free, 1: busy, 2: match source event) | |
'visibility': 0, // Should the events be visible by default (0: private, 1: public, 2: match source event) | |
'verbose': true, // Do you want to log output (useful for debugging) | |
} | |
// The first array below contains the ID strings of the "from" calendar (your personal calendars), and | |
// The second argument is the ID string of the "to" calendar (your work calendar) | |
CalendarSync(["XXXXXXXXX", "YYYYYYYYYY"], "ZZZZZZZZ", options) | |
// Want to sync the calendar the other way? Swap the order. It won't sync events created by this script. | |
// Add one CalendarSync() call per work calendar that you want to sync to a personal calendar. | |
CalendarSync(["ZZZZZZZZ",], "XXXXXXXXX", options) | |
} | |
// CalendarSync - sync from one or more google calendars to another calendar, with: | |
// - a generic event name (original name in the description) | |
// - visibility set to private | |
// - no reminders set | |
// Useful if you want to automatically block out personal commitments in your work calendar | |
// Note, you must have previously shared the "from" calendars with the "to" calendar, | |
// otherwise this script won't have access | |
function CalendarSync(fromcals, tocal, options) { | |
var today=new Date(); | |
var enddate=new Date(); | |
enddate.setDate(today.getDate()+options.daysahead); // how many days in advance to monitor and block off time | |
var lastupdate=new Date(); | |
lastupdate.setHours(today.getHours()-options.maxhoursbetweenruns); // how long ago was this script last run (at a maximum) | |
// Calendar to copy events to | |
var targetEvents=CalendarApp.getCalendarById(tocal).getEvents(today,enddate).filter(e => e.getTag("CalSyncKey") != null); // all target calendar events created by this script | |
var targetEventIds=targetEvents.map(e => e.getTag("CalSyncKey")) // Original event IDs | |
// Process source calendars | |
if (typeof fromcals === 'string' || fromcals instanceof String) { | |
ProcessSourceCalendar(tocal, fromcals, targetEventIds, today, enddate, lastupdate, options) | |
} else { | |
for (const cal of fromcals) { | |
ProcessSourceCalendar(tocal, cal, targetEventIds, today, enddate, lastupdate, options) | |
} | |
} | |
// If a target event previously created no longer exists in the source calendar, delete it | |
for (var tev in targetEvents) | |
{ | |
if (targetEventIds[tev] === "") | |
continue; | |
if (options.verbose) { Logger.log('EVENT DELETED: ' + targetEvents[tev].getStartTime() + ' ' + targetEvents[tev].getDescription()); } | |
targetEvents[tev].deleteEvent(); | |
} | |
} | |
function OwnerNotAttending(event, fromcal) { | |
var guests = event.attendees; | |
if (typeof guests == 'undefined') | |
return false; | |
for (var g of guests) | |
if (g.email == fromcal) | |
if (g.responseStatus != "accepted") | |
return true; | |
else | |
return false; | |
} | |
function ProcessSourceCalendar(tocal, fromcal, targetEventIds, today, enddate, lastupdate, options) { | |
// Get all the events in source calendar in the relevant time period | |
var events; | |
try { | |
events = Calendar.Events.list(fromcal, { | |
timeMin: today.toISOString(), | |
timeMax: enddate.toISOString(), | |
singleEvents: true | |
}); | |
} | |
catch (err) { | |
Logger.log('Error calling Calendar.Events.list for calendar ' + fromcal + ': ' + err); | |
// Failure. Ensure that the calendar ID is correct, that your account has access, and that you've enabled the Calendar API under "Services" | |
return; | |
} | |
if (!events.items || events.items.length === 0) { | |
return; | |
} | |
// Process each event | |
for (const event of events.items) | |
{ | |
if (options.ignorealldayevents && event.start.date) | |
continue; // Do nothing if the event is an all-day event. This script only syncs hour-based events | |
const startTime = event.start.getDateTime(); | |
const endTime = event.end.getDateTime(); | |
if (!PeriodOverlapsDays(startTime, endTime, options.ignorethesedays)) // Skip events outside of work days | |
continue; | |
var available = event.getTransparency(); | |
if (options.onlybusyevents && available) // Skip events marked available | |
continue; | |
if (event.extendedProperties && event.extendedProperties.shared && event.extendedProperties.shared.hasOwnProperty("CalSyncKey")) | |
continue; // Don't copy events created by this script | |
if (OwnerNotAttending(event, fromcal)) // Skip events the user owns but is not attending | |
continue; | |
// Check if the source event has already been blocked in the target calendar | |
var id = event.getId(); | |
var ind = targetEventIds.indexOf(id); | |
if (ind != -1) { | |
// Check if anything changed since the last script run | |
var lastupdated = new Date(event.getUpdated()) | |
if (lastupdated < lastupdate) { | |
targetEventIds[ind] = "" | |
if (options.verbose) { Logger.log('EVENT UNCHANGED: ' + startTime + ' ' + event.getSummary()); } | |
continue; | |
} | |
} | |
// Create a new event | |
available = options.busyness == 0 ? true : (options.busyness == 1 ? false : available) | |
var visible = options.visibility <= 1 ? options.visibility : event.getVisibility(); | |
var title = options.useSourceTitle ? event.getSummary() : options.targetEventTitle; | |
var newevent = { | |
"summary": title, | |
"description": event.getDescription(), | |
"start": {"dateTime": startTime}, | |
"end": {"dateTime": endTime}, | |
"transparency": available ? "transparent" : "opaque", | |
"visibility": visible ? "public" : "private", | |
"extendedProperties": {"shared": {"CalSyncKey": id}} | |
}; | |
// call method to insert/create new event in provided calandar | |
Calendar.Events.insert(newevent, tocal); | |
if (options.verbose) { Logger.log('EVENT CREATED: ' + startTime + ' ' + event.getSummary()); } | |
} | |
} | |
function PeriodOverlapsDays(startDate_, endDate_, ignorethesedays) { | |
if (ignorethesedays.length == 0) | |
return true; | |
var startDate = new Date(startDate_) | |
var endDate = new Date(endDate_) | |
var date = startDate.getDate() | |
var day = startDate.getDay() | |
while (startDate < endDate) { | |
if (!ignorethesedays.includes(day)) | |
return true; | |
date++; | |
startDate.setDate(date); | |
day = (day + 1) % 7; | |
} | |
return false; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment