【GoogleAppsScript】GASとMessageAPIで作ったLINEbotにゴミ出しのリマインドをしてもらおう！【#2 ゴミ出しリマインダーを実装する】

# 前回のあらすじ
前回の記事ではシステムの構成と仕様の解説、環境構築を済ませました。
今回の記事ではシステムの根幹となる、ゴミ出しリマインダー機能の部分を実装します。
![スクリーンショット 2023-01-21 15.36.24.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3115841/768347a8-19e2-6f05-0700-1e15f367ecd6.png)
※図の曜日は一例です。

# ディレクトリ構成
### ファイルの作成方法
1つのGoogleAppsScriptファイルの中に、複数のgsファイルを作成していきます。
「ファイル」右側の「＋」ボタンから「スクリプト」を選択することで、ファイルを新規追加することができます。
![スクリーンショット 2023-01-18 23.50.36.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3115841/0324a400-b087-3f1a-acee-b88acb247c41.png)

### ディレクトリ構成
```String:ゴミ出しLINEbot
・
├── information.gs
├── globalConstant.gs
├── trashNotification.gs
├── getFlag.gs
├── getNowHours.gs
├── defineMessageAboutTrash.gs
├── getDayOfWeekStringToday.gs
├── getDayOfWeekStringTomorrow.gs
├── getWeekNumberToday.gs
├── getWeekNumberTomorrow.gs
├── defineMessageToday.gs
├── defineMessageTomorrow.gs
├── defineParams.gs
└── deliverMessage.gs
```
# 必要情報を入力する
[XXX]と記述されている箇所をご自身の情報に書き換えてください。
※「"」は消さないようにご注意ください！

今回書き換える場所はこれだけです。

※スプレッドシートのIDとは、URLの以下の部分です。

```JS:information.gs
//スプレッドシートのID(#1で作成したスプレッドシートのIDを記入してください)
//IDとは、URLのこの部分です。https://docs.google.com/spreadsheets/d/[XXXXXXXXX]
const spreadsheet = SpreadsheetApp.openById("[XXXXXXXXXXXXXXXXXXXXXXXXXXXX]"); 

//LINEbotの情報(#1で取得したチャンネルアクセストークンとユーザIDを記入してください)
const token = "[XXXXXXXXXXXXXXXXXXXXXXXXXXXXXX]"; //チャネルアクセストークン
const userId = "[XXXXXXXXXXXXXXXXXX]"; //ユーザID

//ゴミの日を指定(お住まいの地域に合わせて記入してください)　例：月
const burnableGarbageDay1 = "[XX]"; //燃えるゴミの曜日1
const burnableGarbageDay2 = "[XX]"; //燃えるゴミの曜日2
const incombustibleGarbageDay = "[XX]"; //燃えないゴミの曜日
const binGarbageDay = "[XX]"; //ビン・カン・ペットボトルゴミの曜日
const cardboardGarbageDay = "[XX]"; //段ボールゴミの曜日 ※隔週想定
```

# グローバル定数を宣言する
その他、書き換える必要のない定数を宣言していきます。
```JS:globalConstant.gs
//スプレッドシートを指定
const sheet = spreadsheet.getSheetByName("シート1"); //シート名を指定
const range = sheet.getRange(1,1); //A1セルを指定

//LINEbotの情報を指定
const url = "https://api.line.me/v2/bot/message/push";

//本日の日付を取得
const date = new Date();

//通知の一時停止・再開で使用する文字列を宣言
const restartString = "再開";
const stopString = "一時停止";

//ゴミ出しメッセージを宣言
const burnableGarbageString = "燃えるゴミの日です！";
const incombustibleGarbageString = "燃えないゴミの日です！";
const binGarbageString = "ビン・カン・ペットの日です！";
const cardboardGarbageString = "段ボールゴミの日です！";

//曜日の配列を作る（曜日番号7=8つ目の場合は日曜）
const dayOfWeeks = ['日', '月', '火', '水', '木', '金', '土','日'];  
```

# 関数を定義する

### メイン部分
ゴミ出しリマインダーの根幹となる関数です。

通知の可否を判定し、
通知可能の場合はメッセージの内容を決めたのちに送信
通知不可の場合は何もせず処理を終了します。

```JS:trashNotification.gs
//メイン部分　※トリガー必須
function trashNotification() {
  //スプレッドシートの値を取得し、配信を実行していいかどうかを判断
  let flagNumber = getFlag();
  if(flagNumber == 0){ 
    console.log("ゴミ出し通知処理を開始します。"); //配信処理開始の旨をログに出力
    let message = defineMessageAboutTrash() //メッセージの内容を決める
    deliverMessage(message); //メッセージを送信
  }else if (flagNumber == 1){ 
    console.log("通知が一時停止されているので、何もせず処理を終了します。"); //処理を修了する旨をログに出力
    return; //処理を終了
  }
}
```

### 通知可否のフラグを取得する
通知可否を判定するために、スプレッドシートに記述された値を取得します。
メイン部分や、次回の記事で解説する「通知の一時停止」機能で使用します。
```JS:getFlag.gs
function getFlag(){
  let flag = range.getValue(); //A1セルの値を取得して「flag」変数に格納
  console.log("現在のflagは[" + flag + "]です。"); //結果をログに出力
  return flag;
}
```

### 当日か前日かを判定
メイン部分で通知可能だと判定された場合、この関数が実行されます。
実行時の日時が当日朝の場合は、当日用の処理を実行、
前日夜の場合は、前日用の処理を実行します。
```JS:defineMessageAboutTrash.gs
function defineMessageAboutTrash() {
  const hours = getNowHours();  //時刻を取得し、変数に格納
  if (hours < 9){
    return defineMessageToday(); //当日に送るメッセージを定義
  }else{
    return defineMessageTomorrow(); //前日に送るメッセージを定義
  }
}
```

### 現在の時刻を取得する
"defineMessageAboutTrash.gs"を実行した際に呼び出される関数です。
現在の時刻を取得します。
```JS:getNowHours.gs
function getNowHours(){
  const nowHour = date.getHours(); //現在の時間を取得
  console.log("今は" + nowHour + "時です。"); //結果をログに出力
  return nowHour;
}
```

### 当日に送るメッセージを定義
前述した「当日用の処理」です。
曜日を取得して、送信するメッセージを判定します。
ゴミの日でない場合、メッセージは空白になります。

```JS:defineMessageToday.gs
function defineMessageToday(){
  console.log("当日メッセージを取得します。");
  const dayOfWeekStringToday = getDayOfWeekStringToday(); //曜日を取得
  if (dayOfWeekStringToday === burnableGarbageDay1 || dayOfWeekStringToday === burnableGarbageDay2) {
    return "本日は" + burnableGarbageString; //燃えるゴミ
  }else if (dayOfWeekStringToday === incombustibleGarbageDay) {
    return "本日は" + incombustibleGarbageString; //燃えないゴミ
  }else if (dayOfWeekStringToday === binGarbageDay) {
    return "本日は" + binGarbageString; //ビン・カン・ペットゴミ
  }else if (dayOfWeekStringToday === cardboardGarbageDay && getWeekNumberToday() % 2 === 0) {
    return "本日は" + cardboardGarbageString; //段ボールゴミ
  }else{
    console.log("本日のゴミ出しはお休みです。"); //その他
  }
}
```

### 当日の曜日を取得
当日用の処理で実行されます。
実行日から見て当日の曜日を取得します。
```JS:getDayOfWeekStringToday.gs
function getDayOfWeekStringToday() {
  console.log("今日の曜日を取得します。");
  const dayOfWeekNumber = date.getDay();  //曜日番号を取得
  const dayOfWeek = dayOfWeeks[dayOfWeekNumber]; //配列を使って曜日番号を曜日（文字列）に変換
  console.log("今日は" + dayOfWeek + "曜日です。"); //結果をログに出力
  return dayOfWeek;
}
```

### 当日が何週目かを取得
隔週でゴミの回収が実施される、段ボールゴミ等のメッセージ判定で使用します。
```JS:getWeekNumberToday.gs
function getWeekNumberToday() {
  let weekNumberToday = Math.floor((date.getDate() - 1) / 7) + 1; //日付から1を引いた値を7で割って1を足す＝今日の週番号がわかる
  console.log("今日は第"+ weekNumberToday +"週目です。"); //結果をログに出力
  return weekNumberToday;
}
```

### 前日に送るメッセージを定義
前述した「前日用の処理」です。
曜日を取得して、送信するメッセージを判定します。
ゴミの日でない場合、メッセージは空白になります。
```JS:defineMessageToday.gs
function defineMessageTomorrow(){
  console.log("前日メッセージを取得します。");
  const dayOfWeekStringTomorrow = getDayOfWeekStringTomorrow();  //曜日を取得
  if (dayOfWeekStringTomorrow === burnableGarbageDay1 || dayOfWeekStringTomorrow === burnableGarbageDay2) {
    return "明日は" + burnableGarbageString; //燃えるゴミ
  }else if (dayOfWeekStringTomorrow === incombustibleGarbageDay) {
    return "明日は" + incombustibleGarbageString; //燃えないゴミ
  }else if (dayOfWeekStringTomorrow === binGarbageDay) {
    return "明日は" + binGarbageString; //ビン・カン・ペットゴミ
  }else if (dayOfWeekStringTomorrow === cardboardGarbageDay && getWeekNumberTomorrow() % 2 === 0) {
    return "明日は" + cardboardGarbageString; //段ボールゴミ
  }else{
    console.log("明日のゴミ出しはお休みです。"); //その他
  }
}
```

### 翌日の曜日を取得
前日用の処理で実行されます。
実行日から見て翌日の曜日を取得します。
```JS:getDayOfWeekStringTomorrow.gs
function getDayOfWeekStringTomorrow() {
  console.log("明日の曜日を取得します。");
  const dayOfWeekNumber = date.getDay() + 1;  //曜日番号を取得
  const dayOfWeek = dayOfWeeks[dayOfWeekNumber]; //配列を使って曜日番号を曜日（文字列）に変換
  console.log("明日は" + dayOfWeek + "曜日です。"); //結果をログに出力
  return dayOfWeek;
}
```

### 翌日が何週目かを取得
隔週でゴミの回収が実施される、段ボールゴミ等のメッセージ判定で使用します。
```JS:getWeekNumberTomorrow.gs
function getWeekNumberTomorrow() {
  let weekNumberTomorrow = Math.floor((date.getDate() - 0) / 7) + 1; //日付を7で割って1を足す＝明日の週番号がわかる
    console.log("明日は第"+ weekNumberTomorrow +"週目です。"); //結果をログに出力
  return weekNumberTomorrow;
}
```

### メッセージを送信
様々な情報を取りまとめて、最終的にメッセージを送信する処理です。
メッセージが空白=ゴミの日ではない場合は、メッセージを送信せず、処理を終了します。
```JS:deliverMessage.gs
function deliverMessage(message){
  if(!message){
    console.log("メッセージが空白です。配信は行われませんでした。"); //メッセージを送信しない場合
  }else{
    let params = defineParams(message); //Paramsを定義
    UrlFetchApp.fetch(url, params); //メッセージを配信
    console.log("下記のメッセージが配信されました。\n\n"+ message); // //メッセージの内容をログに出力
  }
}
```

### MessageAPIで必要なParams配列を定義
メッセージ送信で必要となる”Params”配列に、メッセージの内容とLINEbotの情報を格納します。
```JS:defineParams.gs
function defineParams(message){
  let payload = {
  to: userId, //ユーザーID
  messages: [
    { type: 'text', text: message }
  ]
  };
  
  let params = {
  method: 'post',
  contentType: 'application/json',
  headers: {
    Authorization: 'Bearer ' + token
  },
  payload: JSON.stringify(payload)
  };

  return params;
}
```
# 動作確認
関数「trashNotification.gs」を開き、「実行」ボタンをクリックします。
![スクリーンショット 2023-01-20 14.04.55.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3115841/1b243e4a-71d3-ba8c-1ef1-acfabc38ecc7.png)

初回はこのようなメッセージが出ます。「権限を確認」をクリックします。
![スクリーンショット 2023-01-21 14.31.40.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3115841/736068fe-fc99-ed95-0396-2f39ae400ba5.png)

Googleアカウントを選択します。
![スクリーンショット 2023-01-21 14.34.40.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3115841/05e78ef8-9358-c08c-251d-5009675fa293.png)

このような画面が出ますので、「詳細」をクリックします。
![スクリーンショット 2023-01-21 14.36.08.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3115841/83610223-609b-6783-26b0-76c10ffe32ea.png)

「ゴミ出しLINEbot(ご自身で設定したファイル名)に移動」をクリックします。
![スクリーンショット 2023-01-21 14.37.03.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3115841/ecc6fc14-6177-8616-d700-3153e18bd9a7.png)

「許可」をクリックします。
![スクリーンショット 2023-01-21 14.39.56.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3115841/982a1cb5-f302-d2f9-a077-d2312bb86684.png)

もう一度、「実行」ボタンをクリックします。
![スクリーンショット 2023-01-20 14.04.55.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3115841/1b243e4a-71d3-ba8c-1ef1-acfabc38ecc7.png)

画面下に実行ログが表示されます。
![スクリーンショット 2023-01-20 14.05.48.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3115841/a44faa65-e919-c489-6b9f-ce6a21d4c454.png)
正常に実行完了したら、成功です！

# トリガーを設定する
画面左の「トリガー」ボタンをクリックします。
![スクリーンショット 2023-01-21 15.39.17.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3115841/50038b1d-c23a-1bc0-ea93-612438ccdd85.png)

画面右下の「トリガーを追加」をクリックします。
![スクリーンショット 2023-01-21 15.39.24.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3115841/f2ecfdf4-4f4e-3643-028f-cc0989933262.png)

実行する関数を選択：trashNotification
実行するデプロイを選択：Head
イベントのソースを選択：時間主導型
時間ベースのトリガーのタイプを選択：日付ベースのタイマー
時刻を選択：お好きな時間を選択してください
「保存」をクリック
![スクリーンショット 2023-01-21 15.39.58.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3115841/452c5f0f-69c8-c404-14b5-7aef02c1fb95.png)

好きなだけトリガーを設定したら完了です。
私は6~7時、20~21時、20~23時の3つを設定しました。
![スクリーンショット 2023-01-21 15.41.04.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3115841/87f1afa1-1408-011f-f38b-59504c2e4b05.png)

この時点で、リマインダーとして利用可能です。

次回は、「通知の一時停止」機能を実装していきます！