【GAS】GASでGoogleカレンダーの特定の文字列が含まれる予定をSlackに通知する

## 何ができるか
GAS(Google Apps Script)を使って、Googleカレンダーの予定の中で特定の文字列を含む予定をコメント付きでSlackに通知します。
例えば、「バイト」という予定があるとして、忘れたくない持ち物を予定のタイトルとともに通知を前日したい場合に有効です。予定そのものに通知を設定することや、説明欄にメモを書くこともできますが、バイトのように不定期に繰り返される予定の場合は手動で設定するのが面倒です。でも忘れ物はしたくないですよね。自動で通知してくれたら幸せだと思ったので作ることにしました。
GAS初心者による記事ですので悪しからずご了承ください。


## 完成イメージ
Googleカレンダーから予定を取得して忘れ物防止メモをSlackに通知することができます🙌

![スクリーンショット 2024-05-03 0.30.36.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/333791/31f29c9f-2210-ce60-78f4-90d411c34ce8.png)



## Slackで通知を受け取る
### 1. Slack側の準備
SlackApp・Webhook URLを作成します。最新の手順は[公式ドキュメント](https://api.slack.com/start/quickstart)を確認してください。
まず、[Slack APIのYour Appsページ](https://api.slack.com/apps)から、`Create New App`を選択します。
以下のような選択肢が表示されるので、上の`From scratch`を選択します。

![スクリーンショット 2024-05-02 22.44.18.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/333791/36251725-5488-4fd5-9296-0180c4ff2f03.png)

App NameにSlack Appの名前を入力します。後からでも変更できます。作成するワークスペースは後から変更できません。それぞれ選択したら、`Create App`を押します。

![スクリーンショット 2024-05-02 22.46.16.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/333791/d6fa3d2e-99e9-c330-fa4d-c4f023c45e1c.png)

作成できたら、`App Home`から`Display Name`を編集します。

![スクリーンショット 2024-05-02 23.23.53.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/333791/0a818c24-bb72-3595-4cf3-19b45f881417.png)

次に、`OAuth & Permissions`から、`Install to Workspace`を押します。

![スクリーンショット 2024-05-02 23.25.49.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/333791/424af681-c400-f842-7735-6a4606e7c0ea.png)

下のような画面が表示されるので、任意のチャンネルを選択して`許可する`を押します。

![スクリーンショット 2024-05-02 23.26.39.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/333791/0213429b-880f-8adb-c291-3281a4491750.png)

`Incoming Webhooks`を開きます。`Activate Incoming Webhooks`が`On`になっていることを確認します。`Off`になっている場合は、`On`に変更し、`Add New Webhook to Workspace`を押して設定します。

![スクリーンショット 2024-05-02 23.00.07.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/333791/45e68509-0dbc-8dd0-a6d2-9cb4e6449f55.png)

スクロール下部のWebhook URLは、GASのスクリプトを書く際に使用します。これでSlack Appの用意は万端です👏


### 2. GASを作成
ご自身のGoogle Driveを開き、新規 > その他 > Google Apps Scriptを選択します。

![スクリーンショット 2024-05-02 22.33.37.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/333791/f6a5e56c-5431-ec2a-3bc1-ba0898173d2a.png)

以下のような画面が表示されます。スクリプトを書く準備が整いました🎉

![スクリーンショット 2024-05-02 22.36.33.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/333791/d9b5c8df-04fd-1f52-104f-0f094cefcb45.png)


### 3. コードを書く

以下のコードのコピペで動きます。Chat GPTに生成してもらったものをベースに作成しています^_^

```js:main.gs
function sendReminders() {
  // calendarIdには取得したいGoogleカレンダーのカレンダーIDを代入します
  // メインアカウントを使用する場合primary、他のカレンダーの場合はGoogleカレンダー>マイカレンダー>設定と共有からカレンダーIDを確認して代入します
  const calendarId = 'hogehoge@gmail.com';
  const date = new Date();
  const tomorrow = new Date(date.getTime() + (24 * 60 * 60 * 1000));
  // getEventsForDayメソッドは指定された日の0:00から23:59までの予定を取得します
  const events = CalendarApp.getCalendarById(calendarId).getEventsForDay(tomorrow);

  for (const event of events) {
    // バイトの部分が検索文字列。メッセージは自由に編集できるので忘れたくないものを書いてみました
    if (event.getTitle().includes("バイト")) {
      const message = `リマインダー: 明日（${formatDate(tomorrow)}）${event.getTitle()}で働くよ。入館証を忘れない！`;
      sendToSlack(message);
    }
  }
}

function sendToSlack(message) {
  // 上で作成したSlack AppのWebhook URLを代入
  const webhookUrl = 'https://hooks.slack.com/services/XXX/YYY/ZZZ'; 
  const payload = JSON.stringify({text: message});
  const options = {
    method: 'post',
    contentType: 'application/json',
    payload: payload
  };
  UrlFetchApp.fetch(webhookUrl, options);
}

function formatDate(date) {
  return Utilities.formatDate(date, "JST", "yyyy/MM/dd");
}
```

~~ちなみに、明日の予定なのに`dayAfterTomorrow`も取得している理由をChat GPTに聞いてみたところ以下のような回答を得ました。~~
（追記）
提案通り`dayAfterTomorrow`を入れてみましたが普通に明後日の予定が通知されてきたので(それはそう)明日の予定のみを取得するようなコードに変更しました。
<details><summary>Chat GPTの解説</summary>

> スクリプトで`dayAfterTomorrow`を計算し、`getEvents(tomorrow, dayAfterTomorrow)`関数で使用しているのは、指定された期間内のイベントを取得するためです。具体的には、`tomorrow`から`dayAfterTomorrow`の範囲でイベントを検索しています。
ここで、Google Apps Script の`CalendarApp.getEvents(startDate, endDate)`メソッドは、`startDate`から`endDate`までの期間にスケジュールされているイベントを取得しますが、`endDate`はその日の0時0分を指していると解釈されます。したがって、`tomorrow`の日の0時0分から`dayAfterTomorrow`の日の0時0分までのイベントを取得しているわけです。
このように設定する理由は、次の日の全てのイベント（つまり、真夜中から24時間分）をしっかりと取得するためです。もし`dayAfterTomorrow`を使用せずに`tomorrow`のみを使った場合、`tomorrow`の日の0時0分からその日の0時0分まで（実質的には何も取得されない）という指定になり、予期した範囲のイベントが取得できません。
そのため、`tomorrow`の日の0時0分から次の日の0時0分（つまり24時間分）までのイベントをしっかりと取得し、確実に「明日」の予定をすべてチェックできるようにするために`dayAfterTomorrow`を利用しています。これにより、スクリプトは正確に明日1日間のすべてのイベントを対象に動作することが保証されます。

（追記）補足
> Google Apps Script の getEvents(startDate, endDate) は endDate がその日の0時0分で終わるというわけではなく、endDate の前の瞬間までのイベントを取得します。つまり、endDate が 5月2日だとすれば、5月1日の23時59分59秒までのイベントが取得対象となります。

</details>

### 4. 試してみる
エディタ画面の上部にあるドロップダウンから`sendReminders`を選択し、`実行`を押します。
関数を初めて実行する場合、スクリプトがGoogleアカウントのデータへのアクセス許可を求めるダイアログが表示されます。指示に従ってアクセスを許可し、以下のような画面になったら詳細を表示 > ~~~（安全ではないページ）に移動 を押します。

![スクリーンショット 2024-05-03 0.10.52.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/333791/ffa164da-88bc-2e56-3f01-75b469d8f8d5.png)

正常に実行されていれば、指定されたSlackのチャンネルへ通知が送信されているはずです。
ここまでできていたら、以下で実際に自動で通知するトリガーを設定しましょう✨

### 5. トリガーを設定
毎日決まった時間に通知が送信されるように設定します。
左側の時計のアイコンからトリガータブを開き、トリガーを追加を選択します。
画像のように
- 時間ベースのトリガーを`日付ベースのタイマー`
- 時刻は任意の時間

と設定すると、毎日任意の時間に明日特定の文字列を含む予定があった場合に通知されます。

![スクリーンショット 2024-05-03 0.50.25.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/333791/a81f02f0-21a5-76dd-9270-82455b10c2f1.png)

GASを使ってGoogleカレンダーの特定の文字列が含まれる予定をコメント付きでSlackへ通知できるようになりました！

## おわりに
初めてGASを使いましたがとても便利！いちいちメモや通知を予定に追加するのは面倒なので、一旦GASで自動化してしまえば後は楽ですね！
スプレッドシートに検索文字列と対応する持ち物をリスト化して簡単に設定できるようにしたり、LINEへ通知できるようにしたり、進化させられそうです！忘れ物に特化する必要もないので、いろいろ活用できそうですね:)
最後までお読みいただき、ありがとうございました。


## 参考記事

https://engineer-blog.ajike.co.jp/slack-bot-google-calendar/

https://tektektech.com/slack-gas-auto-notice-bot/

https://dev.classmethod.jp/articles/post-messages-to-slack-channel/

https://the-simple.jp/slack-nobotuser