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
- Create Script: Open
script.google.com, create a New Project, and paste the code below. - Add API: Click + next to Services (left sidebar), select Google Calendar API, and click Add.
- Personalize: Update
MY_EMAILat the top of the script. - Automate: Click Triggers (Clock icon) -> + Add Trigger. Set
addDecompressTimeto run on a Time-driven "Hour timer" every hour.
How it Works
- Refreshes Hourly: Every hour, it clears and recreates buffers for the next 14 days based on your latest schedule.
- Conflicts: It won't add a buffer if another meeting starts immediately after.
- Exclusions: It ignores weekends, all-day events, Focus Time, OOO blocks, and events marked "Free" or titled "Office", "Home", or "Busy".
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'));
}
}