0
0

Automating Calendar Event Notifications with Google Apps Script and Slack: A Step-by-Step Guide

Last updated at Posted at 2024-08-13

Google Calendar and Slack are indispensable tools for managing schedules and team communications. By integrating these two, you can automate sending notifications about calendar events to a Slack channel, ensuring everyone stays informed. This article will guide you through creating a Calendar Events Notification App using Google Apps Script and Slack.

Table of Contents

  1. Introduction
  2. Setting Up Your Environment
    • Required Tools and Accounts
    • Obtain the Group Calendar ID
    • Create a Slack Webhook
  3. Creating a Google Apps Script Project and Write Scripts
    • Writing the Script
    • Sending Notifications to Slack
    • Posting Daily Events
    • Checking for New or Updated Events
    • Bonus Functions for Debugging
  4. Setting up Apps Script Triggers
    • Trigger for Daily Event Notifications
    • Trigger for Calendar Updates
  5. Conclusion
    • Resources

Setting Up Your Environment

Required Tools and Accounts

Usage
Google Account To access Google Calendar and Google Apps Script.
Slack Workspace To send notifications to a Slack channel.

Step 1. Obtain the Group Calendar ID

Before integrating with Google Calendar, you need the ID of the calendar you want to monitor.

  1. Go to Google Calendar
  2. Go to Settings and Setings for my calendars section
  3. Select the calendar you want to monitor
  4. Find and copy the Calendar ID

Step 2. Create a Slack Webhook

To send notifications to Slack, you need to create an Incoming Webhook and obtain a webhook URL to a specific slack channel to which you intend to send notifications.

  1. Go to Slack API: https://api.slack.com/apps
  2. Create a New App from scratch
  3. Set Up Incoming Webhooks:
    1. In the "Features" section on the left, select "Incoming Webhooks".
    2. Turn on "Activate Incoming Webhooks".
  4. Add a New Webhook to Workspace:
    1. Scroll down and click "Add New Webhook to Workspace".
    2. Select the channel where you want to post the notifications.
  5. After adding, you will see a webhook URL. Copy it

Note: If you encounter the message [you app name] にはインストールするボットユーザーがありません, it indicates that the app requires a bot user. Ensure that the bot user is enabled.

Create a Google Apps Script Project and Write a script

Now, we have all the ingredients we need such as Calendar ID and Slack Webhook URL we're good to create new Apps Script project.

My app will have 3 functionalities:

  1. Sending Notifications to Slack
  2. Posting Daily Events
  3. Checking&Sending for New or Updated Events of the current day
  4. Bonus functions useful for debugging

Step 1: Create a Google Apps Script Project

Go to https://script.google.com/home and Create a New Apps Script Project.

Step 2: Writing the Script

Before writing scripts, let's first insert the obtained Google Calendar ID and Slack webhook URL

const SLACK_WEBHOOK_URL = 'https://hooks.slack.com/services/YOUR/SLACK/WEBHOOK'; // Replace with your Slack webhook URL
const CALENDAR_ID = 'your_calendar_id@group.calendar.google.com'; // Replace with your calendar ID

Sending Notifications to Slack

This sendSlackNotification function sends a message to a Slack channel using a webhook URL.

// 🎯 Sending Notifications to Slack
function sendSlackNotification(message) {
  const payload = {
    text: message,
    link_names: 1, // ✅Tips: Ensures that @mentions are recognized by Slack
  };

  const options = {
    method: 'post',
    contentType: 'application/json',
    payload: JSON.stringify(payload),
  };

  UrlFetchApp.fetch(SLACK_WEBHOOK_URL, options);
}

If you want to add @ mentions in your message (channel, here, etc.) then add link_names: 1 to your payload. Otherwise, your mentions will be recognized as text.

Posting Daily Events

This postDailyEvents function posts events that day's events to the Slack channel at a specific time every day. Read inline code comments for more details

postDailyEvents()
// 🎯 Post daily events 
function postDailyEvents() {
  const today = new Date();
  today.setHours(0, 0, 0, 0);
  const tomorrow = new Date(today);
  tomorrow.setDate(tomorrow.getDate() + 1);

  const events = CalendarApp.getCalendarById(CALENDAR_ID).getEvents(today, tomorrow);

  let messageHeader =  ':spiral_calendar_pad: *今日の予定を共有します:*\n\n'
  let message = messageHeader;
  let postedEvents = [];

  events.forEach(event => {
    const eventId = event.getId();
    const startTime = event.getStartTime().toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' });
    const endTime = event.getEndTime().toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' });

    message += `▸ *${event.getTitle()}*: \n     時間:(${startTime} ~ ${endTime})\n`;

    // Add event details to postedEvents array
    postedEvents.push({ id: eventId, startTime, endTime });
  });

  // Send a message if there are events
  if (message !== messageHeader) {
    message += `\n 本日もよろしくお願いします!`
    sendSlackNotification(message);
  } else {
    Logger.log('There are no events for today.');
  }

  // Store the posted event details to avoid duplicate posting of the same event by checkNewCalendarEvents()
  const properties = PropertiesService.getScriptProperties();
  properties.setProperty('postedEvents', JSON.stringify(postedEvents));
}

This is the result message in Slack:

Checking for New or Updated Events

This checkNewCalendarEvents function checks for new or updated events and sends notifications accordingly. More specifically, it does two things:

  1. It posts a message to a Slack channel when a new event is added to the calendar for today.
  2. It posts a message to a Slack channel when today's events start/end time gets updated.

Read inline code comments for more details. Please don't mind too many code comments they're there to explain the logic of the code for beginners.

checkNewCalendarEvents()
// 1. 🎯  Post newly created events for today 
// 2. 🎯  Post today's events when their start/end time gets updated
function checkNewCalendarEvents() {
   // Access the script properties service to store and retrieve persistent key-value pairs.
  const properties = PropertiesService.getScriptProperties(); 
  // Retrieve the posted events from the previous runs or initialize an empty array if none are found.
  const postedEvents = JSON.parse(properties.getProperty('postedEvents') || '[]');  
  // Get the last checked time or use the current time if not found.
  const lastChecked = new Date(properties.getProperty('lastChecked') || new Date().getTime());  

  const now = new Date();  
  // Create a new Date object representing the start of today.
  const startOfToday = new Date(now); 
  // Set the time of the startOfToday to midnight (00:00:00). 
  startOfToday.setHours(0, 0, 0, 0);  
  // Create a new Date object representing the start of tomorrow.
  const startOfTomorrow = new Date(startOfToday);  
  // Move the date to the next day.
  startOfTomorrow.setDate(startOfTomorrow.getDate() + 1);  

  // Retrieve events from the calendar for today.
  const events = CalendarApp.getCalendarById(CALENDAR_ID).getEvents(startOfToday, startOfTomorrow); 

  // Initialize an array to store the current events.
  let newPostedEvents = [];  
  
  events.forEach(event => {  
    const eventId = event.getId();  
    // Get the start time of the event in a readable format. Remove milliseconds and only display hours and minutes
    const startTime = event.getStartTime().toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' });
    const endTime = event.getEndTime().toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' });
    // Check if the event already exists in the posted events.
    const existingEvent = postedEvents.find(e => e.id === eventId);  

    if (existingEvent) {  // If the event exists in the posted events:
      // Check if event start or end time has changed
      if (existingEvent.startTime !== startTime || existingEvent.endTime !== endTime) { 
        // Create a message indicating the event time has changed.
        const message = `@channel \n\n :pencil: *本日の予定の時間が変更されました:* \n  ▸ *${event.getTitle()}* \n        時間:(${startTime} ~ ${endTime})`;  
        sendSlackNotification(message); 
      }
    } else {  // If the event does not exist in the posted events:
    // Create a message indicating a new event has been added.
      const message = `@channel \n\n :new_1: *本日の新しい予定が追加されました:* \n  ▸ *${event.getTitle()}* \n        時間:(${startTime} ~ ${endTime})`;  
      sendSlackNotification(message);  
    }

    // Add the current event details to the new posted events array.
    newPostedEvents.push({ id: eventId, startTime, endTime });  
  });

  // Update the posted events property with the new posted events array to avoid duplicate posting of the same event by checkNewCalendarEvents() when the calendar updates
  properties.setProperty('postedEvents', JSON.stringify(newPostedEvents));  
  // Update the last checked time with the current time.
  properties.setProperty('lastChecked', now.getTime());  
}

This is the result message in Slack:

New event:

Updated existing event:

Bonus functions useful for debugging

Functions to check and clear "posted event" properties for debugging and resetting the state.

To test and debug your script, you can manually run the functions and check the logs using Logger.log().

checkProperties()
// 🛠️ Check the state of posted events. 
// Use it manually to debug and verify the events that have been posted.
function checkProperties() {
  const properties = PropertiesService.getScriptProperties();
  const postedEvents = JSON.parse(properties.getProperty('postedEvents'));  

  Logger.log(`Posted events: ${postedEvents}`);
}
clearChannelProperties()
// 🛠️ Clears the 'postedEvents' property from script properties.
// Use it manually to reset the state if you need to start fresh, ensuring that no previous events are posted and thus avoiding potential duplication in notifications.
function clearChannelProperties() {
  const properties = PropertiesService.getScriptProperties();
  properties.deleteProperty('postedEvents');

  Logger.log('Channel properties cleared.');
}

Step 3: Setting up Apps Script Triggers

Both postDaily (to post daily events) and checkNewCalendarEvents (Checking for New or Updated Events) have to be triggered to check the calendar and send notifications to Slack. To trigger both of the functions we will need to add triggers from "Triggers" tab.

Here you can see 2 separate triggers that I have added:
image.png

1. Trigger for postDailyEvents function

We want this function to get triggered once a day, preferrable in the morning, and send all the planned events for the day. Therefore, we set-up a Time-driven trigger just like in the screenshot image.

2. Trigger for checkNewCalendarEvents function

We want this function to get trigger every time calendar updates. Therefore, we set-up a separate From calendar trigger for it just like in the screenshot image.
Don't forget to replace your Calendar owner email so that Apps Script can watch the changes in your calendar.

Conclusion

Integrating Google Calendar with Slack using Google Apps Script allows for automated, real-time event notifications. This setup can enhance team communication and efficiency. Customize and extend the script to fit your specific needs.

Useful Resources:

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0