Last active
February 2, 2023 18:10
-
-
Save noperator/7a6a2585f27a3a838f58531b21adae53 to your computer and use it in GitHub Desktop.
Sync home calendar events with blocks on work calendar via Google Apps Script
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
function main() { | |
const props = PropertiesService.getScriptProperties().getProperties(); | |
// Look for created/updated/deleted events on scheduler calendar. | |
const events = logSyncedEvents(props['schedulerCal'], false); | |
console.log("got %d new event(s)", events.length); | |
let e = 0 | |
for (let event of events) { | |
e += 1 | |
console.log("[%d: %s] event: %s", e, event.id, event.summary); | |
// If the scheduler calendar event doesn't have home email address as attendee, then add it. | |
if (event.status != "cancelled") { | |
if (event.attendees && event.attendees.length > 0) { | |
if (!event.attendees.map(({ email }) => email).includes(props['homeEmail'])) { | |
console.log("[%d: %s] adding attendee", e, event.id); | |
event.attendees.push({ email: props['homeEmail'] }); | |
updateEvent(props['schedulerCal'], event.id, event); | |
} | |
} else { | |
console.log("[%d: %s] adding attendee", e, event.id); | |
event.attendees = [{ email: props['homeEmail'] }]; | |
updateEvent(props['schedulerCal'], event.id, event); | |
} | |
} | |
// Look for blocks on blocker calendar whose description contains the scheduler calendar event ID. | |
const blocks = Calendar.Events.list(props['blockerCal'], { | |
q: event.id, | |
}); | |
if (blocks.items && blocks.items.length > 0) { | |
console.log("[%d: %s] found matching block", e, event.id); | |
let block = blocks.items[0]; | |
// If the scheduler calendar event was deleted, then delete its matching block on the blocker calendar. | |
if (event.status == "cancelled") { | |
console.log("[%d: %s] deleting block", e, event.id); | |
deleteEvent(props['blockerCal'], block.id); | |
// If the scheduler calendar event was updated, then update the corresponding blocker calendar block if needed. | |
} else { | |
const eventRecurrence = (event.recurrence ? event.recurrence : null); | |
const blockRecurrence = (block.recurrence ? block.recurrence : null); | |
if (block.start != event.start || block.end != event.end || blockRecurrence != eventRecurrence) { | |
console.log("[%d: %s] updating block", e, event.id); | |
block.start = event.start; | |
block.end = event.end; | |
delete block.recurrence; | |
if (eventRecurrence) { | |
block.recurrence = eventRecurrence | |
} | |
updateEvent(props['blockerCal'], block.id, block); | |
} | |
} | |
// If there's no matching block on the blocker calendar, then create it. | |
} else { | |
if (event.status != "cancelled") { | |
console.log("[%d: %s] creating block", e, event.id); | |
let block = { | |
summary: "🟢 BLOCK", | |
description: event.id, | |
start: event.start, | |
end: event.end, | |
attendees: [{ email: props['workEmail'] }], | |
}; | |
if (event.recurrence) { | |
block.recurrence = event.recurrence | |
} | |
createEvent(props['blockerCal'], block); | |
} | |
} | |
} | |
} | |
function deleteEvent(calendarId, eventId) { | |
try { | |
const deletedEvent = Calendar.Events.remove(calendarId, eventId, { | |
sendUpdates: "all", | |
}); | |
console.log("successfully deleted event: %s", deletedEvent.id); | |
} catch (e) { | |
console.log("delete failed with error: %s", e.message); | |
} | |
} | |
function updateEvent(calendarId, eventId, event) { | |
try { | |
const updatedEvent = Calendar.Events.update(event, calendarId, eventId, { | |
sendUpdates: "all", | |
}); | |
console.log("successfully updated event: %s ", updatedEvent.id); | |
} catch (e) { | |
console.log("update failed with error: %s", e.message); | |
} | |
} | |
function createEvent(calendarId, event) { | |
try { | |
const createdEvent = Calendar.Events.insert(event, calendarId, { | |
sendUpdates: "all", | |
}); | |
console.log("successfully created event: %s", createdEvent.id); | |
} catch (e) { | |
console.log("create failed with error: %s", e.message); | |
} | |
} | |
function getRelativeDate(daysOffset, hour) { | |
const date = new Date(); | |
date.setDate(date.getDate() + daysOffset); | |
date.setHours(hour); | |
date.setMinutes(0); | |
date.setSeconds(0); | |
date.setMilliseconds(0); | |
return date; | |
} | |
function logSyncedEvents(calendarId, fullSync) { | |
const properties = PropertiesService.getUserProperties(); | |
const options = { | |
maxResults: 100, | |
}; | |
const syncToken = properties.getProperty("syncToken"); | |
if (syncToken && !fullSync) { | |
options.syncToken = syncToken; | |
} else { | |
// Sync events up to thirty days in the past. | |
options.timeMin = getRelativeDate(-30, 0).toISOString(); | |
} | |
// Retrieve events one page at a time. | |
let evts = []; | |
let events; | |
let pageToken; | |
do { | |
try { | |
options.pageToken = pageToken; | |
events = Calendar.Events.list(calendarId, options); | |
} catch (e) { | |
// Check to see if the sync token was invalidated by the server; if so, perform a full sync instead. | |
if ( | |
e.message.includes("Sync token is no longer valid") | |
) { | |
properties.deleteProperty("syncToken"); | |
return logSyncedEvents(calendarId, true); | |
} else { | |
throw new Error(e.message); | |
} | |
} | |
if (events.items && events.items.length > 0) { | |
} | |
evts = evts.concat(events.items); | |
pageToken = events.nextPageToken; | |
} while (pageToken); | |
properties.setProperty("syncToken", events.nextSyncToken); | |
return evts; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment