Skip to content

Instantly share code, notes, and snippets.

@lavoiesl
Last active February 1, 2022 14:40
Show Gist options
  • Save lavoiesl/c5dde3970aae5a6cda4d53313435c8f6 to your computer and use it in GitHub Desktop.
Save lavoiesl/c5dde3970aae5a6cda4d53313435c8f6 to your computer and use it in GitHub Desktop.
// Adapted from https://developers.google.com/apps-script/articles/vacation-calendar
var TEAM_CALENDAR_ID = 'c_xxxxxxxxxxxx@group.calendar.google.com';
var MONTHS_IN_ADVANCE = 2;
var WHOLE_DAY_HOURS_THRESHOLD = 6;
var USER_EMAILS = [
];
// The maximum script run time under Apps Script Pro is 30 minutes; this setting
// will be used to report when the script is about to reach that limit.
var MAX_PRO_RUNTIME_MS = 29 * 60 * 1000;
/**
* Look through the domain users' public calendars and add any
* 'vacation' or 'out of office' events to the team calendar.
*/
function syncTeamVacationCalendar() {
// Define the calendar event date range to search.
var today = new Date();
var startDate = new Date();
startDate.setDate(startDate.getDate() - 7) // Last week
var endDate = new Date();
endDate.setMonth(endDate.getMonth() + MONTHS_IN_ADVANCE);
// For each user, find events having one or more of the keywords in the event
// summary in the specified date range. Import each of those to the team
// calendar.
var count = 0;
var timeout = false;
for (var i = 0; i < USER_EMAILS.length; i++) {
if (isTimeUp(today, new Date())) {
timeout = true;
break;
}
var user = USER_EMAILS[i];
var username = user.split('@')[0];
var lastRun = PropertiesService.getScriptProperties().getProperty('lastRun-' + user);
lastRun = lastRun ? new Date(lastRun) : null;
findEvents(user, startDate, endDate, lastRun).forEach(function(event) {
if (event.start.dateTime) {
// Convert long events to day-events to reduce clutter in shared calendars
var start = new Date(event.start.dateTime)
var end = new Date(event.end.dateTime)
var durationHours = (end - start) / 1000 / 60 / 60
if (durationHours >= WHOLE_DAY_HOURS_THRESHOLD) {
event.start.date = formatDate(start)
delete event.start.dateTime
if (end.getHours() >= 12) {
// End is exclusive, so it must be the date after
// However, if the coming back time is before 10am, don't count it.
end.setDate(end.getDate() + 1)
}
event.end.date = formatDate(end)
delete event.end.dateTime
}
}
event.summary = '[' + username + '] ' + event.summary;
event.organizer = {
id: TEAM_CALENDAR_ID
};
event.attendees = [];
Logger.log('Importing: %s', event.summary);
try {
Calendar.Events.import(event, TEAM_CALENDAR_ID);
PropertiesService.getScriptProperties().setProperty('lastRun-' + user, today);
count++;
} catch (e) {
Logger.log('Error attempting to import event: %s. Skipping.', e.toString());
}
});
}
Logger.log('Imported ' + count + ' events');
if (timeout) {
Logger.log('Execution time about to hit quota limit; execution stopped.');
}
var executionTime = ((new Date()).getTime() - today.getTime()) / 1000.0;
Logger.log('Total execution time (s) : ' + executionTime); ;
}
/**
* In a given user's calendar, look for occurrences of the given keyword
* in events within the specified date range and return any such events
* found.
* @param {string} user the user's primary email String.
* @param {string} keyword the keyword String to look for.
* @param {Date} start the starting Date of the range to examine.
* @param {Date} end the ending Date of the range to examine.
* @param {Date} opt_since a Date indicating the last time this script was run.
* @return {object[]} an array of calendar event Objects.
*/
function findEvents(user, start, end, opt_since) {
var params = {
timeMin: formatDateTime(start),
timeMax: formatDateTime(end),
maxAttendees: 1,
showDeleted: true,
};
// Logger.log('Importing user %s %s-%s', user, start, end);
if (opt_since) {
// This prevents the script from examining events that have not been
// modified since the specified date (that is, the last time the
// script was run).
params['updatedMin'] = formatDateTime(opt_since);
}
try {
var responses = Calendar.Events.list(user, params)
return responses.items.filter(function(item) {
return item.eventType == 'outOfOffice';
});
} catch (e) {
Logger.log('Error retriving events for %s: %s; skipping', user, e.toString());
return [];
}
}
function eventMatches(event, keywords) {
for (var i = 0; i < length(keywords); i++) {
if (event.summary.toLowerCase().indexOf(keywords[i]) != -1) {
return true;
}
}
return false;
}
function formatDate(date) {
var d = new Date(date),
month = '' + (d.getMonth() + 1),
day = '' + d.getDate(),
year = d.getFullYear();
if (month.length < 2)
month = '0' + month;
if (day.length < 2)
day = '0' + day;
return [year, month, day].join('-');
}
/**
* Return an RFC3339 formated date String corresponding to the given
* Date object.
* @param {Date} date a Date.
* @return {string} a formatted date string.
*/
function formatDateTime(date) {
return Utilities.formatDate(date, 'UTC', 'yyyy-MM-dd\'T\'HH:mm:ssZ');
}
/**
* Compares two Date objects and returns true if the difference
* between them is more than the maximum specified run time.
*
* @param {Date} start the first Date object.
* @param {Date} now the (later) Date object.
* @return {boolean} true if the time difference is greater than
* MAX_PROP_RUNTIME_MS (in milliseconds).
*/
function isTimeUp(start, now) {
return now.getTime() - start.getTime() > MAX_PRO_RUNTIME_MS;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment