Skip to content

Instantly share code, notes, and snippets.

@cr0ybot
Created November 16, 2022 23:59
Show Gist options
  • Save cr0ybot/c4d21271b740bc098e79c135ba4ed766 to your computer and use it in GitHub Desktop.
Save cr0ybot/c4d21271b740bc098e79c135ba4ed766 to your computer and use it in GitHub Desktop.
SyncPersonalCalendar
{
"timeZone": "America/New_York",
"dependencies": {
"enabledAdvancedServices": [
{
"userSymbol": "Calendar",
"version": "v3",
"serviceId": "calendar"
}
]
},
"exceptionLogging": "STACKDRIVER",
"runtimeVersion": "V8",
"oauthScopes": [
"https://www.googleapis.com/auth/calendar"
]
}
/**
* Sync personal events with your work calendar.
*
* Setup:
* 1. From your personal calendar, add your work account in Calendar Settings > Share with specific people, you need at least "See all event details" access
* 2. Create an Apps Script project in your work Google account: https://script.google.com/u/0/home
* 3. Add this file and the appsscript.json file and customize the email addresses and other variables.
* 4. Add a trigger for the `sync` function:
* - Event source: From calendar
* - Event calendar details: Calendar updated
* - Calendar owner email: [your personal email address]
*
* @ref https://medium.com/@willroman/auto-block-time-on-your-work-google-calendar-for-your-personal-events-2a752ae91dab
*
* @todo create Out Of Office events when the API is available: https://issuetracker.google.com/issues/112063903
* @todo Use sync token: https://developers.google.com/apps-script/advanced/calendar#synchronizing_events
*/
const { GuestStatus, Visibility } = CalendarApp;
const fromEmail = "[email protected]"; // email of the personal calendar to pull events from
const toEmail = "[email protected]"; // work email
const daysOut = 30; // how many days in advance to monitor and block off time
const skipWeekends = true; // syncing should ignore events on weekends
const officeHours = {
start: 8, // start hour (8am)
end: 16, // end hour (4pm)
};
const label = 'Out of Office'; // label to prepend to copied events
const acceptCalendars = [fromEmail, '[email protected]']; // Only process events from these calendars (prevents spam)
function sync() {
const toCal = CalendarApp.getDefaultCalendar();
const fromCal = CalendarApp.getCalendarById(fromEmail);
const today = new Date();
const enddate = new Date(); enddate.setDate(today.getDate()+daysOut);
const fromEvents = fromCal.getEvents(today,enddate);
for (let i = 0; i < fromEvents.length; i++) {
const ev = fromEvents[i];
const startTime = ev.getStartTime();
const endTime = ev.getEndTime();
// Ignore events that don't come from the accepted calendars
const fromEventCal = ev.getOriginalCalendarId();
if (!acceptCalendars.includes(fromEventCal)) continue;
// Ignore events that I have not responded YES to or am not the owner of
// NOTE: Can't use getMyStatus() because that would only work for the main calendar (toEmail)
// NOTE: If guestList is empty I guess it's my own event?
const guestList = ev.getGuestList(true);
if (guestList.length) {
const myEvent = guestList.filter((guest) => {
return guest.getEmail() === fromEmail || guest.getEmail() === toEmail;
}).reduce((mine, guest) => {
return mine || (guest.getGuestStatus() == GuestStatus.OWNER || guest.getGuestStatus() == GuestStatus.YES);
}, false);
if (!myEvent) continue;
}
// Ignore all-day events (for now)
if (ev.isAllDayEvent()) continue;
// Check if event is on weekend
const dow = startTime.getDay(); // day of week
if (skipWeekends && (dow < 1 || dow > 5)) continue;
// Check if event is during work day
if (startTime.getHours() >= officeHours.end || endTime.getHours() <= officeHours.start) continue;
// If the secondary event has already been blocked in the primary calendar, ignore it
let alreadyBooked = false;
const existing = toCal.getEvents(startTime, endTime);
for (let j = 0; j < existing.length; j++) {
const ex = existing[j];
if (ex.getStartTime().getTime() == startTime.getTime() && ex.getEndTime().getTime() == endTime.getTime()) {
console.log(`Skipping event from ${fromEventCal} scheduled for booked timeslot on ${toCal.getId()}: ${ex.getStartTime().toISOString()}–${ex.getEndTime().toISOString()}`);
alreadyBooked = true;
break;
}
}
if (alreadyBooked) continue;
// Check if the timeslot is marked "busy" on my personal calendar
const checkBusy = {
items: [{id: fromEmail, busy: 'Active'}],
timeMin: ev.getStartTime().toISOString(),
timeMax: ev.getEndTime().toISOString(),
};
const busyResponse = Calendar.Freebusy.query(checkBusy);
// If there are no entries in the busy array, don't add to work calendar
if (busyResponse.calendars[fromEmail].busy.length === 0) {
console.log(`Skipping event marked "free" from ${fromEventCal}: ${ev.getTitle()}`);
continue;
}
const visibility = ev.getVisibility();
const public = visibility == Visibility.PUBLIC;
console.log(`Syncing ${public ? 'public' : 'private'} event from ${fromEventCal}: ${ev.getTitle()}`);
const newEvent = toCal.createEvent(public ? ev.getTitle() : label, startTime, endTime);
// alternative version below that copies the exact secondary event information into the primary calendar event
// var newEvent = toCal.createEvent(ev.getTitle(),ev.getStartTime(),ev.getEndTime(), {location: ev.getLocation(), description: ev.getDescription()});
// Remove all notifications from new event
newEvent.removeAllReminders();
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment