Christine Phu

Google AppScript: Automated Decompress Time

Instructions and script written by Al.
Reviewed, edited, and tested by Christine.


This script automatically inserts a 10-minute buffer after your meetings to help you reorganize and take a break.

Quick Setup

  1. Create Script: Open script.google.com, create a New Project, and paste the code below.
  2. Add API: Click + next to Services (left sidebar), select Google Calendar API, and click Add.
  3. Personalize: Update MY_EMAIL at the top of the script.
  4. Automate: Click Triggers (Clock icon) -> + Add Trigger. Set addDecompressTime to run on a Time-driven "Hour timer" every hour.

How it Works

Note: It adds buffers regardless of official working hours.

Tip: Because the script automatically deletes and recreates any event titled Decompress, do not use this exact name for manual blocks.

If you want to schedule a longer personal break, use a name like "Extended Break" or "Personal Time" so the script doesn't overwrite it.


The Script

(Remember to test it!)


/**
* Senior Developer Note:
* Added Error Handling (Try-Catch) to prevent script failure emails
* during Google service timeouts or empty API responses.
*/

const EVENT_TITLE = 'Decompress';
const DURATION_MIN = 10;
const MY_EMAIL = 'YOUR_EMAIL_HERE';
const SUMMARY = 'I need a break to decompress and organize thoughts from the previous meeting. Please ping me before scheduling a meeting over this event.';

function addDecompressTime() {
  const calendar = CalendarApp.getCalendarById(MY_EMAIL);
  const now = new Date();
  const futureLimit = new Date();
  futureLimit.setDate(now.getDate() + 14);
  const changesLog = [];

  // 1. CLEANUP (Wrapped in Try-Catch)
  try {
    const existing = calendar.getEvents(now, futureLimit);
    existing.forEach(function(e) {
      if (e.getTitle() === EVENT_TITLE) {
        e.deleteEvent();
      }
    });
  } catch (e) {
    console.warn("Cleanup failed: " + e.message);
    // If cleanup fails, we stop to avoid creating duplicate events
    return;
  }

  // 2. FETCH (Advanced Service with Error Handling)
  let items = [];
  try {
    const response = Calendar.Events.list(MY_EMAIL, {
      timeMin: now.toISOString(),
      timeMax: futureLimit.toISOString(),
      singleEvents: true,
      orderBy: 'startTime'
    });
    items = response.items || [];
  } catch (e) {
    console.error("Calendar API Fetch failed: " + e.message);
    return; // Exit gracefully; the next hourly trigger will try again
  }

  // 3. PROCESS
  items.forEach(function(item) {
    const rawTitle = item.summary || "";
    const title = rawTitle.trim();
    const startTime = new Date(item.start.dateTime || item.start.date);
    const endTime = new Date(item.end.dateTime || item.end.date);

    const isFocusTime = (item.eventType === 'focusTime');
    const isOutOfOffice = (item.eventType === 'outOfOffice');
    const isSelf = (title === EVENT_TITLE);
    const isWeekend = (startTime.getDay() === 0 || startTime.getDay() === 6);
    const isAllDay = !item.start.dateTime;
    const isBusyShield = (title.toLowerCase() === 'busy');
    const isEmptyTitle = (title === "");

    const shouldSkip = isSelf || isWeekend || isAllDay || isFocusTime || isOutOfOffice || isBusyShield || isEmptyTitle;

    if (!shouldSkip) {
      const gapStart = new Date(endTime.getTime() + 1000);
      const gapEnd = new Date(endTime.getTime() + (DURATION_MIN * 60 * 1000));

      try {
        const potentialConflicts = calendar.getEvents(gapStart, gapEnd);
        const realConflicts = potentialConflicts.filter(function(c) {
          const cTitle = c.getTitle();
          const isFree = (c.isFreeEvent ? c.isFreeEvent() : false);
          const isWorkingLoc = cTitle === "Office" || cTitle === "Home";
          return cTitle !== EVENT_TITLE && !isFree && !isWorkingLoc;
        });

        if (realConflicts.length === 0) {
          calendar.createEvent(EVENT_TITLE, endTime, gapEnd, {
            description: SUMMARY
          }).setColor(CalendarApp.EventColor.GRAY);
          changesLog.push("[SUCCESS] Added after: " + title);
        } else {
          changesLog.push("[SKIPPED] " + title + " Conflict with: " + realConflicts[0].getTitle());
        }
      } catch (err) {
        console.warn("Could not process event " + title + ": " + err.message);
      }
    }
  });

  if (changesLog.length > 0) {
    console.log("--- Execution Summary ---");
    console.log(changesLog.join('\n'));
  }
}

↑ Back to top

#work