0
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

GASを使ってGoogleカレンダーに特定の予定があったときに前営業日にSlackに通知を送る仕組みを作ってみる

Last updated at Posted at 2024-12-03

概要

GASを使い、Googleカレンダーに特定の予定があった時にSlackに通知を送る仕組みを作ってみたので方法を簡単にまとめる。

注意

すべての予定をSlack通知したい場合、GASなんか使わずにSlackのGoogleカレンダーのアプリを使ったほうがはるかに楽

前提

  • 下記の内容を実施しSlack通知用のSlackアプリの作成が完了していること。

  • GAS作成時に下記の内容を実施し、タイムゾーンが日本に設定されていること。

  • 下記の方法でチェック対象のGoogleカレンダーのカレンダーIDが確認できること。

方法

  1. 下記のようにそれぞれのファイルでコードを記載する。

    • main.gs

      function noticeSpecificEvent() {
        const config = getConfig();
        const today = new Date();
      
        // 日本の祝日カレンダーのID(固定)
        const holidayCalendarId = "ja.japanese#holiday@group.v.calendar.google.com";
        const holidayCalendar = CalendarApp.getCalendarById(holidayCalendarId);
      
        const targetCalender = CalendarApp.getCalendarById(config.checkTargetCalendarId);
      
        if (!getIsNoticeDay(today, holidayCalendar, config)) {
          const message = "本日は通知対象日ではないため、通知は行いません。";
          console.log(message);
          return false;
        }
      
        if (!getIsLastBusinessDayBeforeSpecificEvent(today, holidayCalendar, targetCalender, config)) {
          let message = `本日は「${config.specificEventTitle}」の予定前の最終営業日ではないため通知は行いません。`;
          console.log(message);
          return false;
        }
      
        const memberInfo = getMemberInfo();
        const slackUserIds = memberInfo.slackUserIds;
      
        let text = "";
        slackUserIds.forEach(function (slackUserId) {
          text += "<@" + slackUserId + "> ";
        });
        text +=
          "\n" +
          `本日は「${config.specificEventTitle}」の予定前の最終営業日です!\n予定を忘れないようにしましょう!`;
      
        let payload = JSON.stringify({ text: text });
      
        sendSlackMessage(payload, config);
      }
      
      function getStartOfDay(date) {
          return new Date(date.getFullYear(), date.getMonth(), date.getDate());
      }
      
      /**
       * 通知対象日かどうかのboolを返却
       *
       * @param {Date} today
       * @param {Calendar} holidayCalendar
       * @param {Object} config
       * @returns {Boolean}
       */
      function getIsNoticeDay(today, holidayCalendar, config) {
        return !getIsHolidayOrWeekend(today, holidayCalendar, config);
      }
      
      /**
       * 指定された日付が祝日か休日かどうかのboolを返却
       *
       * @param {Date} day
       * @param {Calendar} holidayCalendar
       * @param {Object} config
       * @returns {Boolean}
       */
      function getIsHolidayOrWeekend(day, holidayCalendar, config) {
        const isHoliday = holidayCalendar.getEventsForDay(day).length > 0;
        const isWeekend =
          day.getDay() === config.dayOfWeeks.saturday.id ||
          day.getDay() === config.dayOfWeeks.sunday.id;
        return isHoliday || isWeekend;
      }
      
      /**
       * 指定予定前の最終営業日かどうかのboolを返却
       *
       * @param {Date} today
       * @param {Calendar} holidayCalendar
       * @param {Calendar} targetCalender
       * @param {Object} config
       * @returns {Boolean}
       */
      function getIsLastBusinessDayBeforeSpecificEvent(today, holidayCalendar, targetCalender, config) {
        let specificEventDates = getSpecificEventDates(today, targetCalender, config);
        let isLastBusinessDayBeforeSpecificEvent = false;
      
        if (specificEventDates.length === 0) {
          const message = "指定された予定が見つかりませんでした。";
          console.log(message);
          return isLastBusinessDayBeforeSpecificEvent;
        }
      
        const subDayNumber = 1;
        let lastBusinessDayBeforeSpecificEventDates = [];
      
        specificEventDates.forEach(function (specificEventDate) {
          while (true) {
            const specificEventBeforeDate = new Date(specificEventDate);
            // 指定された予定の前日を算出
            const specificEventBeforeDay = specificEventDate.getDate() - subDayNumber;
            specificEventBeforeDate.setDate(specificEventBeforeDay);
            if (!getIsHolidayOrWeekend(specificEventBeforeDate, holidayCalendar, config)) {
              lastBusinessDayBeforeSpecificEventDates.push(specificEventBeforeDate);
              break;
            }
            // falseだった場合、特定の予定の前日を特定の予定の日としてセットして本ループを再度実行
            specificEventDate = specificEventBeforeDate;
          }
        });
      
        // 年、月、日が完全に一致するかどうかを確認
        lastBusinessDayBeforeSpecificEventDates.forEach((lastBusinessDayBeforeSpecificEventDate) => {
          if (getIsSameDate(today, lastBusinessDayBeforeSpecificEventDate)) {
            isLastBusinessDayBeforeSpecificEvent = true;
          }
        });
        return isLastBusinessDayBeforeSpecificEvent;
      }
      
      /**
       * 2つの日付が完全に一致するかどうかのboolを返却
       *
       * @param {Date} dateOne
       * @param {Date} dateTwo
       * @returns {Boolean}
       */
      function getIsSameDate(dateOne, dateTwo) {
        return dateOne.getFullYear() === dateTwo.getFullYear() &&
          dateOne.getMonth() === dateTwo.getMonth() &&
          dateOne.getDate() === dateTwo.getDate();
      }
      
      /**
       * 指定された予定の日付を取得
       *
       * @param {Date} today
       * @param {Calendar} targetCalender
       * @param {Object} config
       * @returns {Array}
       */
      function getSpecificEventDates(today, targetCalender, config) {
          const oneMonthLater = getOneMonthLater(today);
          const targetCalenderEvents = targetCalender.getEvents(today, oneMonthLater);
      
          let specificEventDates = [];
          targetCalenderEvents.forEach(function (targetCalenderEvent) {
              if (targetCalenderEvent.getTitle() === config.specificEventTitle) {
                  specificEventDates.push(targetCalenderEvent.getStartTime());
              }
          });
          return specificEventDates;
      }
      
      /**
       * 指定された日付の1ヶ月後の日付を取得
       *
       * @param {Date} date
       * @returns {Date}
       */
      function getOneMonthLater(date) {
          const newDate = new Date(date);
          newDate.setMonth(newDate.getMonth() + 1);
          return newDate;
      }
      
      /**
       * Slackにメッセージを送信
       *
       * @param {String} payload
       * @param {Object} config
       */
      function sendSlackMessage(payload, config) {
        try {
          const webhookUrl = config.webhookUrl;
        
          let options = {
            method: "post",
            contentType: "application/json",
            payload: payload,
          };
        
          UrlFetchApp.fetch(webhookUrl, options);
        } catch (e) {
          console.log(e);
          throw e;
        }
      }
      
    • config.gs

      function getConfig() {
        return {
          checkTargetCalendarId: "参照するGoogleカレンダーのカレンダー ID",
          specificEventTitle: "特定の予定のタイトル",
          webhookUrl:
            "用意したSlackアプリのWebhook URL",
          dayOfWeeks: {
            sunday: {
              name: "sunday",
              id: 0,
            },
            monday: {
              name: "monday",
              id: 1,
            },
            tuesday: {
              name: "tuesday",
              id: 2,
            },
            wednesday: {
              name: "wednesday",
              id: 3,
            },
            thursday: {
              name: "thursday",
              id: 4,
            },
            friday: {
              name: "friday",
              id: 5,
            },
            saturday: {
              name: "saturday",
              id: 6,
            },
          },
        };
      }
      
    • memberInfo.gs

      function getMemberInfo() {
        return {
          slackUserIds: [
            "メンションをつけたいユーザーのslackID",
          ],
        };
      }
      
  2. main.gsのnoticeSpecificEvent()を毎日1回通知を行いたい時間に実行する設定をする

これでおそらく休日・祝日も考慮し、特定の予定の前営業日に通知が行われるはずである。

参考文献

0
1
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
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?