Created
September 9, 2026 21:30
-
-
Save WhatsARanjit/e75970b0f5e5be9103d5784e4e6b66c5 to your computer and use it in GitHub Desktop.
Calendar Colorizer
This file contains hidden or 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
| /** | |
| * Lists the next X upcoming events in the user's default calendar. | |
| * @see https://developers.google.com/calendar/api/v3/reference/events/list | |
| */ | |
| function listNextEvents(e) { | |
| // Set pre-configured label IDs | |
| const CUSTOMER_LABEL_ID = 'C25E7F70-3C29-4D46-B87F-7FB449468931'; | |
| const TRAVEL_LABEL_ID = 'A1CB409D-AC99-414E-A079-1767AAAFC0D8'; | |
| const PERSONAL_LABEL_ID = 'A5E64B30-404C-4047-8F2E-25198C6A1286'; | |
| const BLOCK_LABEL_ID = 'A5E64B30-404C-4047-8F2E-25198C6A1286'; | |
| const FORUM_LABEL_ID = '7B358992-640C-458C-8B9A-76A6C25972CB'; | |
| const ONE_LABEL_ID = '9DCC45FC-5489-4036-90EE-6B8004640566'; | |
| const ONE_WAITING_LABEL_ID = '89873B19-681B-4DF2-9532-E84A64AE244D'; | |
| const FORUM_MIN = 15; | |
| const MAX_EVENTS = 25; | |
| // Search for last Monday to 7 days ahead | |
| const calendarId = 'primary'; | |
| const now = new Date(); | |
| const day = now.getDay(); | |
| const daysSinceMonday = (day + 6) % 7; | |
| const startTime = new Date(now); | |
| //startTime.setDate(now.getDate() - daysSinceMonday); | |
| startTime.setHours(9, 0, 0, 0); | |
| // Max MAX_EVENTS results | |
| const events = Calendar.Events.list(calendarId, { | |
| timeMin: startTime.toISOString(), | |
| singleEvents: true, | |
| orderBy: "startTime", | |
| maxResults: MAX_EVENTS, | |
| showHiddenInvitations: true, | |
| }); | |
| if (!events.items || events.items.length === 0) { | |
| console.log("No events found."); | |
| return; | |
| } | |
| // Domains to ignore, my domain and Google Meet rooms | |
| const internalDomains = [ | |
| Session.getActiveUser().getEmail().split("@")[1], | |
| 'whatsaranjit.com', | |
| ] | |
| // Iterate through results | |
| for (event of events.items) { | |
| if (event.start.date) { | |
| // All-day event. | |
| const start = new Date(event.start.date); | |
| //console.log("%s (%s)", event.summary, start.toLocaleDateString()); | |
| continue; | |
| } | |
| const start = new Date(event.start.dateTime); | |
| // Name the meeting | |
| console.log(event.summary); | |
| // There might be no attendees | |
| const allAttendees = event.attendees || []; | |
| const attendees = allAttendees | |
| .map(attendee => attendee) | |
| .filter(entry => entry.resource !== true) | |
| // Filter out internal domains | |
| const externalAttendees = attendees | |
| .map(attendee => attendee.email.split('@')[1].toLowerCase()) | |
| .filter(domain => !internalDomains.includes(domain)) | |
| const internalAttendees = attendees.filter( | |
| entry => !externalAttendees.some( | |
| excluded => excluded.email === entry.email | |
| ) | |
| ); | |
| // If we find more domains, they're external | |
| if (externalAttendees.length > 0) { | |
| console.log(`External meeting: ${event.id}`); | |
| // Update color | |
| updateColor(event, CUSTOMER_LABEL_ID); | |
| } | |
| // Next check for 2 internal attendees (including myself) ignoring the room | |
| else if (internalAttendees.length == 2) { | |
| console.log(`Internal meeting: ${event.id}`); | |
| const accepted_one = attendees | |
| .map(attendee => attendee.responseStatus) | |
| .filter(response => response != 'accepted') | |
| if (accepted_one.length > 0) { | |
| // Find if your 1:1 hasn't been accepted yet | |
| console.log(`Internal meeting - pending acceptance: ${event.id}`); | |
| // Update color | |
| updateColor(event, ONE_WAITING_LABEL_ID); | |
| console.log(`Awaiting response to 1:1 from ${accepted_one}`); | |
| } | |
| else { | |
| // Find if your 1:1 has been accepted yet | |
| console.log(`Internal meeting - all accepted: ${event.id}`); | |
| // Update color | |
| updateColor(event, ONE_LABEL_ID); | |
| } | |
| } | |
| // Next check for attendees greater than FORUM_MIN | |
| else if (attendees.length > FORUM_MIN) { | |
| console.log(`Forum meeting: ${event.id}`); | |
| // Update color | |
| updateColor(event, FORUM_LABEL_ID); | |
| } | |
| // Next check for Travel events | |
| else if (event.summary.match(/Travel/)) { | |
| console.log(`Travel meeting: ${event.id}`); | |
| // Update color | |
| updateColor(event, TRAVEL_LABEL_ID); | |
| } | |
| // Next check for Lunch events | |
| else if (event.summary.match(/Lunch/)) { | |
| console.log(`Lunch meeting: ${event.id}`); | |
| // Only worry about solo events that I created | |
| if ((event.attendees || []).length > 1) { | |
| continue; | |
| } | |
| // Update color | |
| updateColor(event, PERSONAL_LABEL_ID); | |
| } | |
| // Next look for personal blocks | |
| else if (event.summary.match(/Block/)) { | |
| // Only worry about solo events that I created | |
| if ((event.attendees || []).length > 1) { | |
| continue; | |
| } | |
| console.log(`Blocked meeting: ${event.id}`); | |
| // Update color | |
| updateColor(event, BLOCK_LABEL_ID); | |
| } | |
| else { | |
| console.log(`Uncategorized meeting: ${event.id}`); | |
| // Clear color | |
| updateColor(event, null); | |
| } | |
| } | |
| } | |
| function updateColor(event, color, calendarId = 'primary') { | |
| console.log(`Current label: ${event.eventLabelId}`); | |
| // Don't make update if the color is already set | |
| if (event.eventLabelId == color) { | |
| return; | |
| } | |
| console.log(`Updating ${event.id} to ${color}`); | |
| try { | |
| const eventu = { | |
| eventLabelId: color, | |
| }; | |
| Calendar.Events.patch( | |
| eventu, | |
| calendarId, | |
| event.id, { | |
| eventLabelVersion: 1 | |
| } | |
| ); | |
| console.log(`Successfully updated event: ${event.id} to ${color}`); | |
| } | |
| catch (e) { | |
| console.log(`Fetch threw an exception: ${e}`); | |
| } | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment