IAmMoonie

Auto Block Time Before and After a Meeting

Nov 12th, 2024
161
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
JavaScript 9.01 KB | Source Code | 0 0
  1. /**
  2.  * @fileoverview This script automates the scheduling of "Prep Time" and "Follow Up" events
  3.  * around events in a specified Google Calendar based on invitations from specified email addresses.
  4.  * It is designed for users needing dedicated time before and after meetings with specific attendees.
  5.  *
  6.  * This script manages fetching recent event updates, identifying relevant events,
  7.  * and automatically creating "Prep Time" and "Follow Up" events.
  8.  * It avoids duplicate scheduling by marking events as processed using extended properties.
  9.  *
  10.  * @author u/IAmMoonie
  11.  * @version 1.0
  12.  * @license Apache-2.0
  13.  *
  14.  * @requires Google Apps Script built-in services for Calendar and PropertiesService.
  15.  *
  16.  * @description This script monitors Google Calendar events originating from specific email addresses,
  17.  * adding configurable buffer times before and after events to ensure adequate preparation and
  18.  * follow-up time. When a relevant event is accepted by the user, the script automatically schedules
  19.  * "Prep Time" and "Follow Up" events based on specified time offsets.
  20.  *
  21.  * The script uses the Google Calendar API to manage events and PropertiesService
  22.  * for tracking processed events and trigger email lists. It also includes a mechanism to mark
  23.  * events as processed, preventing redundant triggers.
  24.  *
  25.  * @example
  26.  * // Expected event structure and execution flow:
  27.  * // 1. User receives an invitation from a specified email address.
  28.  * // 2. Upon accepting the invitation, "Prep Time" and "Follow Up" events are automatically added.
  29.  * // 3. If the event is rescheduled, it is marked as processed to prevent duplicate additions.
  30.  *
  31.  * @see {@link https://developers.google.com/calendar/api/v3/reference|Google Calendar API Reference}
  32.  * @see {@link https://developers.google.com/apps-script/reference/properties/properties-service|Google Apps Script PropertiesService Reference}
  33.  *
  34.  * @since 1.0
  35.  *
  36.  */
  37.  
  38. /**
  39.  * Configuration object for script settings.
  40.  * @type {Object}
  41.  * @property {string[]} defaultEmails - List of email addresses to monitor for event invites.
  42.  * @property {number} prepTimeOffsetMins - Time in minutes before the main event to schedule prep time.
  43.  * @property {number} followUpOffsetMins - Time in minutes to schedule follow-up after the main event.
  44.  * @property {string} userEmail - The user's email address, used to check acceptance status of events.
  45.  */
  46. const CONFIG = {
  47.   defaultEmails: ["[email protected]", "[email protected]"],
  48.   prepTimeOffsetMins: 30,
  49.   followUpOffsetMins: 15,
  50.   userEmail: "[email protected]"
  51. };
  52. const scriptProperties = PropertiesService.getScriptProperties();
  53.  
  54. /**
  55.  * Initializes or updates the TRIGGER_EMAILS property in PropertiesService with default emails.
  56.  * Run this whenever you make a change to the CONFIG defaultEmails (so adding or removing email addresses)
  57.  */
  58. const initializeProperties = () => {
  59.   const storedEmails = scriptProperties.getProperty("TRIGGER_EMAILS");
  60.   const configEmailsSet = new Set(CONFIG.defaultEmails);
  61.   if (!storedEmails) {
  62.     scriptProperties.setProperty(
  63.       "TRIGGER_EMAILS",
  64.       CONFIG.defaultEmails.join(",")
  65.     );
  66.   } else {
  67.     const storedEmailsSet = new Set(storedEmails.split(","));
  68.     const isUpdated =
  69.       CONFIG.defaultEmails.some((email) => !storedEmailsSet.has(email)) ||
  70.       [...storedEmailsSet].some((email) => !configEmailsSet.has(email));
  71.     if (isUpdated) {
  72.       scriptProperties.setProperty(
  73.         "TRIGGER_EMAILS",
  74.         CONFIG.defaultEmails.join(",")
  75.       );
  76.     }
  77.   }
  78. };
  79.  
  80. /**
  81.  * Main function triggered by onEventUpdated. Fetches recent events and processes each.
  82.  * @param {object} e - The event trigger object containing calendarId.
  83.  */
  84. const onEventUpdated = (e) => {
  85.   const calendarId = e.calendarId || "primary";
  86.   const events = fetchRecentEvents_(calendarId);
  87.   if (events.length === 0) return;
  88.   events.forEach((event) => processEvent_(event, calendarId));
  89. };
  90.  
  91. /**
  92.  * Fetches recent calendar events updated within the past hour.
  93.  * @param {string} calendarId - The ID of the calendar.
  94.  * @returns {object[]} Array of recent event objects.
  95.  */
  96. const fetchRecentEvents_ = (calendarId) => {
  97.   const now = new Date();
  98.   const pastHour = new Date(now.getTime() - 1000 * 60 * 60).toISOString();
  99.   const params = {
  100.     timeMin: pastHour,
  101.     showDeleted: false,
  102.     singleEvents: true,
  103.     orderBy: "updated",
  104.     updatedMin: pastHour
  105.   };
  106.   const response = Calendar.Events.list(calendarId, params);
  107.   return response.items || [];
  108. };
  109.  
  110. /**
  111.  * Processes an individual event and creates prep and follow-up events if conditions are met.
  112.  * @param {object} event - The event object.
  113.  * @param {string} calendarId - The ID of the calendar.
  114.  */
  115. const processEvent_ = (event, calendarId) => {
  116.   let _extendedProperties$p;
  117.   const {
  118.     summary: eventTitle,
  119.     attendees,
  120.     start,
  121.     end,
  122.     id: eventId,
  123.     extendedProperties
  124.   } = event;
  125.   if (eventTitle.includes("Prep Time") || eventTitle.includes("Follow Up"))
  126.     return;
  127.   const alreadyProcessed =
  128.     (extendedProperties === null ||
  129.     extendedProperties === void 0 ||
  130.     (_extendedProperties$p = extendedProperties.private) === null ||
  131.     _extendedProperties$p === void 0
  132.       ? void 0
  133.       : _extendedProperties$p.processed) === "true";
  134.   if (alreadyProcessed) return;
  135.   const startEventTime = start.dateTime ? new Date(start.dateTime) : null;
  136.   const endEventTime = end.dateTime ? new Date(end.dateTime) : null;
  137.   if (!startEventTime || !endEventTime) return;
  138.   const triggerEmails = getConfigEmails_();
  139.   const isRelevantSender =
  140.     attendees === null || attendees === void 0
  141.       ? void 0
  142.       : attendees.some(({ email }) => triggerEmails.includes(email));
  143.   if (!isRelevantSender) return;
  144.   const userAttending =
  145.     attendees === null || attendees === void 0
  146.       ? void 0
  147.       : attendees.some(
  148.           ({ email, responseStatus }) =>
  149.             email === CONFIG.userEmail &&
  150.             (responseStatus === "accepted" || responseStatus === "tentative")
  151.         );
  152.   if (!userAttending) return;
  153.   addPrepAndFollowUpEvents_(
  154.     calendarId,
  155.     eventTitle,
  156.     startEventTime,
  157.     endEventTime,
  158.     start.timeZone,
  159.     eventId
  160.   );
  161.   markEventAsProcessed_(calendarId, eventId);
  162. };
  163.  
  164. /**
  165.  * Marks an event as processed by adding an extended property.
  166.  * @param {string} calendarId - The ID of the calendar.
  167.  * @param {string} eventId - The ID of the event to mark.
  168.  */
  169. const markEventAsProcessed_ = (calendarId, eventId) => {
  170.   Calendar.Events.patch(
  171.     {
  172.       extendedProperties: {
  173.         private: {
  174.           processed: "true"
  175.         }
  176.       }
  177.     },
  178.     calendarId,
  179.     eventId
  180.   );
  181. };
  182.  
  183. /**
  184.  * Retrieves the synchronized list of emails from PropertiesService.
  185.  * @returns {string[]} Array of email addresses.
  186.  */
  187. const getConfigEmails_ = () => {
  188.   const emails = scriptProperties.getProperty("TRIGGER_EMAILS");
  189.   return emails ? emails.split(",") : [];
  190. };
  191.  
  192. /**
  193.  * Adds "Prep Time" and "Follow Up" events relative to a main event.
  194.  * @param {string} calendarId - The ID of the calendar.
  195.  * @param {string} eventTitle - The title of the main event.
  196.  * @param {Date} startEventTime - Start time of the main event.
  197.  * @param {Date} endEventTime - End time of the main event.
  198.  * @param {string} timeZone - Time zone for the event.
  199.  * @param {string} eventId - ID of the original event to associate.
  200.  */
  201. const addPrepAndFollowUpEvents_ = (
  202.   calendarId,
  203.   eventTitle,
  204.   startEventTime,
  205.   endEventTime,
  206.   timeZone,
  207.   eventId
  208. ) => {
  209.   const prepStart = new Date(
  210.     startEventTime.getTime() - CONFIG.prepTimeOffsetMins * 60000
  211.   );
  212.   createEvent_(
  213.     calendarId,
  214.     `Prep Time for ${eventTitle}`,
  215.     prepStart,
  216.     startEventTime,
  217.     timeZone,
  218.     eventId,
  219.     "Prep"
  220.   );
  221.   const followUpStart = new Date(endEventTime.getTime());
  222.   const followUpEnd = new Date(
  223.     followUpStart.getTime() + CONFIG.followUpOffsetMins * 60000
  224.   );
  225.   createEvent_(
  226.     calendarId,
  227.     `Follow Up for ${eventTitle}`,
  228.     followUpStart,
  229.     followUpEnd,
  230.     timeZone,
  231.     eventId,
  232.     "FollowUp"
  233.   );
  234. };
  235.  
  236. /**
  237.  * Creates a new event in Google Calendar without any attendees and stores its ID.
  238.  * @param {string} calendarId - The ID of the calendar.
  239.  * @param {string} summary - The event summary or title.
  240.  * @param {Date} start - The start date and time of the event.
  241.  * @param {Date} end - The end date and time of the event.
  242.  * @param {string} timeZone - Time zone for the event.
  243.  * @param {string} eventId - ID of the original event to associate.
  244.  * @param {string} type - Type of event ("Prep" or "Follow Up").
  245.  */
  246. const createEvent_ = (
  247.   calendarId,
  248.   summary,
  249.   start,
  250.   end,
  251.   timeZone,
  252.   eventId,
  253.   type
  254. ) => {
  255.   const createdEvent = Calendar.Events.insert(
  256.     {
  257.       summary,
  258.       start: {
  259.         dateTime: start.toISOString(),
  260.         timeZone
  261.       },
  262.       end: {
  263.         dateTime: end.toISOString(),
  264.         timeZone
  265.       },
  266.       attendees: []
  267.     },
  268.     calendarId
  269.   );
  270.   scriptProperties.setProperty(`${type}_${eventId}`, createdEvent.id);
  271. };
Advertisement
Add Comment
Please, Sign In to add comment