GASを使ってゴミの日をGoogleカレンダー経由で通知させる

## はじめに
こちらの記事は[ヤプリ Advent Calendar 2023](https://adventar.org/calendars/8946)の2日目の記事です！

https://adventar.org/calendars/8946


## 動機
ゴミの日、忘れると大変ですよね...
毎週固定の曜日だったらいいんですが、隔週だったり月一だったり変則的だと忘れることもあると思います。
そこで今回は特定のゴミの日だったらGoogleカレンダーに予定を登録し、Googleカレンダー経由で通知を送るようにやっていきます

## 構成
![trashbot-ページ1 (2).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/257100/43454314-39b2-0069-2314-6e2f51eb9e24.png)



流れとしては下記です。

前提：スプレッドシートに後述のデータ構成でデータを入れておくこと

1. 日次で後述のコード例に記載しているGoogleAppsScriptのコードが発火し、曜日などをベースにスプレッドシートからデータを取得します。
1. スプレッドシート内にゴミの日が登録されていればゴミの日を返します。
1. ゴミの日が登録されていればGoogleカレンダーに予定を登録します。
1. Googleカレンダーに登録された時間になったらモバイル端末に通知を飛ばします。

## データ構成
データ自体はスプレッドシートに登録しているのですが、RDB風にデータを入れているのでER図にして解説します。
![trashbot-ページ2 (2).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/257100/26ff372e-b2e1-caa6-d741-fa1fdf791427.png)


## コード例

```js
/**
 * 現在日時から曜日と第何週かを取得し、スプシから該当するゴミの日を取得する
 */
function getTrashDaysFromSpreadSheet() {
  // 地域ID
  const regeion_id = 1;
  const spreadSheet = SpreadsheetApp.getActive().getSheetByName(${"スクリプトに紐づくスプシのタイトル"});
  
  const now = new Date();

  // 第何週かを取得
  const weekOfMonth = getWeekOfMonth(now);

  // 何曜日かを取得
  const dayOfWeek = now.getDay();

  const query = `"select C where B = ${regeion_id} and D = ${dayOfWeek} and E = ${weekOfMonth}"`;

  const resultSheet = SpreadsheetApp.getActive().getSheetByName("result");

  // データが入っている範囲から検索し、結果を結果表示用のシートに書き込む
  resultSheet.getRange(1, 1).setValue(`=iferror(QUERY(trash_days!A2:E25, ${query}), "")`);

  const lastRow = resultSheet.getLastRow();

  if (lastRow >= 1) {
    for (var i = 1; i <= lastRow; i++) {
      const typeId = sheet.getRange(i, 1).getValue();
      if (typeId === "") {
        return true;
      }
      const query = `"select C where A = ${typeId}"`;
      sheet.getRange(1, 2).setValue(`=iferror(QUERY(waste_types!A2:C15, ${query}), "")`);
      const typeName = sheet.getRange(1, 2).getValue()
      if (typeName === "") {
        return true;
      }
      registCalenderEvent(typeName)
    }
  }
  sheet.deleteRows(1,lastRow)
}

/**
 * ゴミの日のタイトルを受け取ってgoogleカレンダーの予定を登録する
 */
function registCalenderEvent(typeName) {
  // アクセス可能なカレンダーのIDを指定して、Googleカレンダーを取得する
  const myCalendar = CalendarApp.getCalendarById('********@gmail.com');
  const date = new Date();

  // 予定登録
  myCalendar.createAllDayEvent(typeName, date);
}

/**
 * 日時を受け取って何週目かを計算して返す
 */
function getWeekOfMonth(inputDate) {  
  if (isNaN(inputDate.getTime())) {
    console.log("無効な日付");
    return -1;
  }

  const firstDateOfMonth = new Date(inputDate.getFullYear(), inputDate.getMonth(), 1);
  const dayOfWeek = firstDateOfMonth.getDay(); // 1日の曜日 (0: 日曜日, 1: 月曜日, 2: 火曜日, など)

  // 1日から指定日までの経過日数を計算
  const daysSinceFirstDay = inputDate.getDate() - 1;

  // 1週間の日数 (通常は7日) で割って切り上げることで週数を計算
  const weekNumber = Math.ceil((dayOfWeek + daysSinceFirstDay) / 7);

  return weekNumber;
}
```

## 動かし方
GASは関数実行時のトリガーを手軽に設定できるので、今回はそちらを使っていきます。
GASのコンソール画面に入り、左のメニューからトリガーを選択します。
トリガーを作成して下記の画像のように設定すればOKです。

![スクリーンショット 2023-11-26 15.50.40.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/257100/fd524034-eaf3-0f04-7e25-d39017e8c7c6.png)

## 結果
上記でトリガーを設定して関数が実行されると対象の日だとGoogleカレンダーの予定が作成されるはずです🎉

<img width="500" height="1000" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/257100/2f03d07d-b0b6-b3c5-d1f7-f3ba4e83243d.png">

## 参考
https://developers.google.com/apps-script/reference?hl=ja