Skip to content

Instantly share code, notes, and snippets.

@ojwoodford
Last active March 27, 2024 16:03
Show Gist options
  • Star 4 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save ojwoodford/69b72a5bcded0f11ae6c58138a55debb to your computer and use it in GitHub Desktop.
Save ojwoodford/69b72a5bcded0f11ae6c58138a55debb to your computer and use it in GitHub Desktop.
Sync events from a personal Google calendar to a work Google calendar
function SyncMyCal() {
var options = {
'targetEventTitle': "Busy (sync'd event)", // What event title do you want ported events to have
'daysahead': 60, // 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': 2, // Should the events be visible by default (0: private, 1: public, 2: match source event)
'verbose': false, // 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)
}
// 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 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;
// 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 newevent = {
"summary": options.targetEventTitle,
"description": event.getSummary(),
"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;
}
@jedweintrob
Copy link

Thanks for the clarity, @swagluke - I really appreciate it. I have replaced the email in the script with mypersonal@gmail.com and I am still having the problem where i get the error "Error calling Calendar.Events.list for calendar mypersonal@gmail.com: ReferenceError: Calendar is not defined"
I do not get this error with the older ttrahan script using the same calendar. I have enabled google calendar API. do you have any other ideas on how to fix that error?

@ojwoodford see question above AND one more question: in the line that follows, what are the XXX, YYY and ZZZ variables supposed to be replaced with? I assume XXXX is for secondary calendar and that maybe YYY is for primary calendar, but then what is ZZZ for? CalendarSync(["XXXXXXXXX", "YYYYYYYYYY"], "ZZZZZZZZ", options)

@jedweintrob I think ojwoodford made it very clear that XXX, YYY are your personal calenders and ZZZ is the work calendar. For example, you have two personal gmails (p1@gmail.com, p2@gmail.com) and one work email (w1@gmail.com). Whenever you create an event on personals, this script will automatically block it off on your work.

So XXX will be p1@gmail.com and YYY will be p2@gmail.com and ZZZ is w1@gmail.com

Here is the comment he made in the script. // 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)

And your first Error is related to you not replacing the email in the script.

@swagluke
Copy link

Thanks for the clarity, @swagluke - I really appreciate it. I have replaced the email in the script with mypersonal@gmail.com and I am still having the problem where i get the error "Error calling Calendar.Events.list for calendar mypersonal@gmail.com: ReferenceError: Calendar is not defined" I do not get this error with the older ttrahan script using the same calendar. I have enabled google calendar API. do you have any other ideas on how to fix that error?

@ojwoodford see question above AND one more question: in the line that follows, what are the XXX, YYY and ZZZ variables supposed to be replaced with? I assume XXXX is for secondary calendar and that maybe YYY is for primary calendar, but then what is ZZZ for? CalendarSync(["XXXXXXXXX", "YYYYYYYYYY"], "ZZZZZZZZ", options)

@jedweintrob I think ojwoodford made it very clear that XXX, YYY are your personal calenders and ZZZ is the work calendar. For example, you have two personal gmails (p1@gmail.com, p2@gmail.com) and one work email (w1@gmail.com). Whenever you create an event on personals, this script will automatically block it off on your work.
So XXX will be p1@gmail.com and YYY will be p2@gmail.com and ZZZ is w1@gmail.com
Here is the comment he made in the script. // 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)
And your first Error is related to you not replacing the email in the script.

ttrahan's script is much different from this script.

  1. Did you make sure the work email and personal emails are in the right place in the script?
  2. Is Google Calendar API enabled for the script?
  3. When you run the script on App Script from your Work Email?
  4. Does your Work Email and Personal Email have access to see all event details from each other?
  5. When you run the script from your Work Email on App Script, Google will prompt log in, did you sign in with your personal email to give permission?

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