Skip to content

Instantly share code, notes, and snippets.

@alranel
Forked from ttrahan/block_personal_appts
Last active January 23, 2023 15:10
Show Gist options
  • Star 2 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save alranel/f1c9b7abb9013f4a39aa4fb12f6de4ef to your computer and use it in GitHub Desktop.
Save alranel/f1c9b7abb9013f4a39aa4fb12f6de4ef to your computer and use it in GitHub Desktop.
Google Apps Script to automatically create, edit and delete events on work calendar for personal calendar events. Inspired by @willroman's work but heavily changed. Instructions on how to set up can be found in the original post: https://medium.com/@willroman/auto-block-time-on-your-work-google-calendar-for-your-personal-events-2a752ae91dab
var source_calendars = [
'xxx',
];
function sync() {
var today = new Date();
var enddate = new Date();
enddate.setDate(today.getDate()+90); // how many days in advance to monitor and block off time
var sourceEvents = [];
source_calendars.forEach(function(id) {
var secondaryCal = CalendarApp.getCalendarById(id);
sourceEvents = sourceEvents.concat(secondaryCal.getEvents(today,enddate));
});
var thisCal = CalendarApp.getDefaultCalendar();
var offEventTitle = "off"; // update this to the text you'd like to appear in the new events created in primary calendar
var thisCalEventsUpdated = []; // to contain primary calendar events that were updated from secondary calendar
var thisCalEventsCreated = []; // to contain primary calendar events that were created from secondary calendar
var thisCalEventsDeleted = []; // to contain primary calendar events previously created that have been deleted from secondary calendar
// create filtered list of events from this calendar that were previously created from the source calendar
var thisCalOffEvents = [];
for (var pEvent of thisCal.getEvents(today,enddate)) {
if (pEvent.getTitle() == offEventTitle)
thisCalOffEvents.push(pEvent);
}
// process all events in source calendar
for (var sev of sourceEvents) {
// skip events in secondary calendar that were already created by a script similar to this one
if (sev.getTitle() == offEventTitle)
continue;
// ignore declined events
if (sev.getMyStatus() == CalendarApp.GuestStatus.NO || sev.getMyStatus() == CalendarApp.GuestStatus.MAYBE)
continue;
// ignore all-day events
if (sev.isAllDayEvent())
continue;
/*
// Unfortunately, the GSuite API does not provide access to the Transparency property
if (sev.getTransparency() == 'transparent')
continue; // Ignore events whose transparency is set to transparent
*/
// Ignore events in the weekend
if (![1,2,3,4,5].includes(sev.getStartTime().getDay()))
continue;
Logger.log("Processing: " + sev.getTitle());
// Does an "off" event already exist in this calendar?
var offEvent = null;
for (var pEvent of thisCalOffEvents) {
if (pEvent.getStartTime().getTime() == sev.getStartTime().getTime()
&& pEvent.getEndTime().getTime() == sev.getEndTime().getTime()
&& pEvent.getTitle() == offEventTitle)
{
offEvent = pEvent;
break;
}
}
if (!offEvent) {
offEvent = thisCal.createEvent(offEventTitle, sev.getStartTime(), sev.getEndTime());
thisCalEventsCreated.push(offEvent.getId());
Logger.log(' off event created');
} else {
thisCalEventsUpdated.push(offEvent.getId());
Logger.log(' off event updated');
}
//offEvent.setDescription(sev.getTitle() + '\n\n' + sev.getDescription());
offEvent.setVisibility(CalendarApp.Visibility.DEFAULT);
offEvent.removeAllReminders();
}
// if an off event previously created no longer exists in the secondary calendar, delete it
for (var pev of thisCalOffEvents) {
if (!thisCalEventsUpdated.includes(pev.getId())) {
thisCalEventsDeleted.push(pev.getId());
Logger.log('OFF EVENT DELETED: ' + pev.getId());
pev.deleteEvent();
}
}
Logger.log('Off events previously created: ' + thisCalOffEvents.length);
Logger.log('Off events updated: ' + thisCalEventsUpdated.length);
Logger.log('Off events deleted: ' + thisCalEventsDeleted.length);
Logger.log('Off events created: ' + thisCalEventsCreated.length);
// Delete past events in primary calendar
var olddate = new Date();
olddate.setDate(today.getDate()-10);
var pastthisCalEvents = thisCal.getEvents(olddate, today);
var deletedPastEvents = 0;
for (var pEvent of pastthisCalEvents) {
if (pEvent.getEndTime() > today)
continue;
if (pEvent.getTitle() == offEventTitle) {
pEvent.deleteEvent();
deletedPastEvents++;
}
}
Logger.log('Past off events deleted: ' + deletedPastEvents);
}
@stregg
Copy link

stregg commented Nov 2, 2020

What was the modification from the original you forked from? Did it solve the issue of duplicate events in the primary calendar or the error message when manually running the script?

@alranel
Copy link
Author

alranel commented Dec 22, 2020

@stregg, sorry for the late answer. It's been a while since I did this so I don't remember exactly what modifications I made. I'm happily using this. I don't have duplicate events nor errors while running this manually

@JohnLockeNJ
Copy link

I added this to line 46 which kept me from getting an error from creating/deleting too many events in too short a period of time which happened about 9% of the time:
Utilities.sleep(1000); // added to prevent "too many updates" error on a busy calendar

Does anyone have suggested modifications that could enable 2-way syncing? ie the script running on GCal1 copies GCal2 events to GCal1 with the subject "X". A second copy of the script on GCal2 copies events the other direction but would need to be set to ignore GCal1 entries with the subject "X".

@JohnLockeNJ
Copy link

@stregg, sorry for the late answer. It's been a while since I did this so I don't remember exactly what modifications I made. I'm happily using this. I don't have duplicate events nor errors while running this manually

@alranel
It looks to me like the main change is that you added code to skip events in the secondary calendar that are marked as "free" (ie transparent) but the code doesn't work as you noted because the Gsuite API doesn't support the expected call. Would you consider making a script tweak to truly enable that functionality using the workaround suggested here? https://stackoverflow.com/questions/61191652/how-to-return-if-google-calendar-time-or-calendar-event-is-busy?rq=1

As for other changes:

  • It looks like you added an extra check in line 37 to not only filter events marked by the script as "off" (as set in line 21) but also "Booked". That seems unnecessary and perhaps its function was to clean out entries created by a past version of the script gone awry.
  • I think your commenting out of lines 57 and 88 keeps some secondary calendar info from being added to the primary for better privacy
  • I don't understand the changes in lines 2-17 but they seem equivalents to the original

@alranel
Copy link
Author

alranel commented Jun 30, 2021

Thank you for the hint @JohnLockeNJ. For now I don't need that feature so I don't plan to add the free/busy query, but if anyone wants to contribute that change I'll be happy to merge that.

I just uploaded a new version with many cleanups and some minor changes to the logic. I removed the "Booked" event title since we're only using the (configurable) "off" string. I also made a change that ignores the events in the source calendars with a "maybe" answer. (I try to use "maybe" as I would use the "free" option). I also changed the privacy of placeholder events which are public now (not private anymore), since they don't contain details about the original event; this change prevents synchronization bugs. The script will now consider also slots blocked by private events, which were previously not taken into account.

@JohnLockeNJ
Copy link

The new version has been working great and I've been using it since you uploaded!

For anyone interested, I've found a way to use the script to sync an Outlook Exchange calendar to a Google Calendar.

If you publish your Outlook Exchange calendar and set it to "Can view all details", you then get an ICS link that you can add as an Other Calendar in one of your Google Calendar accounts. Instructions: https://www.alphr.com/sync-outlook-calendar-google-calendar/

Those instructions say it sets up syncing, but it really just lets you view the calendar in Google and doesn't include it in Google Calendar free/busy without more steps.

Once it's added as an Other Calendar, you can get the Google Calendar ID for that new calendar per Will Roman's original Medium article and put it into the script. Pick different text for the "off" blocked off time than your other Google Cal to Google Cal scripts. You now have Outlook Exchange meetings blocked off in your Google Calendar and it will replicate to other Google Calendars for which you've set up sync scripts and everyone who looks at the Google Calendar free/busy times.

Rather than find a way to sync the other direction (Google Calendar to Outlook Exchange) using client software or setting up a personal server, I just added one of my Google Calendars (that already syncs with all my other work Google Calendars) as a Personal Calendar in Outlook for the Web which lets you include those entries when people look at your free/busy time.
https://office365itpros.com/2020/02/27/adding-your-personal-calendar-owa/

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