GASとLINEでゴミ出し忘れがゼロになる

## 戸建てってゴミ出しが面倒だ
最近引っ越したんですが、いろいろとゴミ捨てのスケジュールが決まっていてめんどくさいのです。燃えるゴミはまだしもカン・ビンのゴミの日は隔週で訪れたりします。つまりうっかり出し損ねると次回の収集は2週間後…せっかくの新居がこうやってゴミ屋敷になっていくわけです。とはいえ常にゴミのカレンダーを意識ながら生活するのも難儀なもので、都合よくウチのゴミ出しスケジュールをみて通知してくれるボットが欲しいなと思って作りました。

## 作ったもの
- 毎日6:30と18:30に、当日および翌日のゴミを通知してくれる
- 通知はLineに送ってくれる
- ゴミカレンダーはSpreadSheetに事前に登録しておく

## 構成
基本的にはGoogleAppsScriptでメインのプログラムを作成。Lineの通知にはLineMessagingAPIを活用。ゴミ出しカレンダーのデータベース替わりにGoogleSpreadsheetを使いました。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/646573/bb73b794-1e8a-4438-ea3a-ca95c3e993fd.png)

## ソースコード
ソースコードは以下の通りです。モジュールは大きく分けて3つあるので、それぞれについての説明は後述します。

```Javascript
//グローバル変数（主にLineAPIのヘッダ情報）
var channel_access_token = "[アクセストークン]";
var headers = {
   "Content-Type": "application/json; charset=UTF-8",
   "Authorization": "Bearer " + channel_access_token
};

//#####モジュール①：ゴミの日チェック#####
function GarbageCheck() {
  setTrigger();   //次回のトリガーをセット
  var ssMasterSheet = SpreadsheetApp.getActive().getSheetByName('Master');
  var AMorPM = ssMasterSheet.getRange(1, 2).getValue();         //AMorPM
  var TodayGarbage = ssMasterSheet.getRange(2, 2).getValue();   //今日のゴミ
  var TomrwGarbage = ssMasterSheet.getRange(3, 2).getValue();   //明日のゴミ
  var Today = ssMasterSheet.getRange(2, 4).getValue();          //今日
  var Tomrw = ssMasterSheet.getRange(3, 4).getValue();          //明日
  
  switch (AMorPM) {
  case "AM":
    message = "今日" + Today + "は" +  TodayGarbage + "の日";  //AM(6:30実行)の場合は今日のゴミ出し予定を表示
    break;
  case "PM":
    message = "明日" + Tomrw + "は" +  TomrwGarbage + "の日";  //PM(18:30実行)の場合は明日のゴミ出し予定を表示
    break;
  default:
    message = "?";
    break;
  }
  sendLineMessage(message)
}

//#####モジュール②：LineAPIをコールする#####
function sendLineMessage(message){
  var payload =
   {
    "messages":[
        {
            "type":"text",
            "text":message
        }
    ]
   };
  
  var params ={method : 'post', headers: headers, payload: JSON.stringify(payload)};
  UrlFetchApp.fetch("https://api.line.me/v2/bot/message/broadcast", params);
}

//#####モジュール③：次回のトリガーをセットする#####
function setTrigger() {

  var m = Moment.moment().add(12,"hours"); //現在日時 + 12時間後
  var NextTriggerTime = new Date(m.format('YYYY/M/D'))
  NextTriggerTime.setHours(m.format('H'));
  NextTriggerTime.setMinutes(30); 
  
  ScriptApp.newTrigger('GarbageCheck').timeBased().at(NextTriggerTime).create();  
}
```

## モジュール①：ゴミの日チェック
まずは基本となるモジュールから。ゴミのカレンダーをみて、当日および翌日の通知内容を取得します。ゴミのカレンダーをこのようにSpreadSheetに登録しておきます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/646573/734bc16b-31c6-8db9-1cfb-5a7d6395ddb3.png)

当日および翌日の通知内容は、Spreadsheetのカレンダー([ゴミの日カレンダー]シート)をもとにSpreadSheet関数で取得します。
※この辺はもちろんGASの中でもできるんですが、SS関数を使った方がはるかにメンテしやすい＆実装が早いので。（余談ですが、最近は仕事でもExcel関数でできることはわざわざプログラム書かずにExcelに任せたりしてます。）
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/646573/b9d47f6d-6da4-7a81-b30a-2560feacec27.png)
↑の実体はこんな感じ
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/646573/8a345f59-e79a-2037-f55e-038b967a5bc4.png)


これらの情報をもとに、AMに実行された場合は当日のゴミを、PMに実行された場合は翌日のゴミを通知します

```javascript
//#####モジュール①：ゴミの日チェック#####
function GarbageCheck() {
  setTrigger();   //次回のトリガーをセット
  var ssMasterSheet = SpreadsheetApp.getActive().getSheetByName('Master');
  var AMorPM = ssMasterSheet.getRange(1, 2).getValue();         //AMorPM
  var TodayGarbage = ssMasterSheet.getRange(2, 2).getValue();   //今日のゴミ
  var TomrwGarbage = ssMasterSheet.getRange(3, 2).getValue();   //明日のゴミ
  var Today = ssMasterSheet.getRange(2, 4).getValue();          //今日
  var Tomrw = ssMasterSheet.getRange(3, 4).getValue();          //明日
  
  switch (AMorPM) {
  case "AM":
    message = "今日" + Today + "は" +  TodayGarbage + "の日";  //AMの場合は今日のゴミ出し予定を表示
    break;
  case "PM":
    message = "明日" + Tomrw + "は" +  TomrwGarbage + "の日";  //PMの場合は明日のゴミ出し予定を表示
    break;
  default:
    message = "?";
    break;
  }
  sendLineMessage(message)
}
```

## モジュール②：LineAPIをコールする
これが今回の開発のミソ。GASからLineAPIをコールしてスマホに通知します。Lineの公式ドキュメントに従って開発用アカウントを取得し、以下のリファレンスを参照しながらGASからAPIをコールしていきます。
[MessagingAPI - メッセージを送る](https://developers.line.biz/ja/docs/messaging-api/sending-messages/#methods-of-sending-message)
※1,000件/月までのメッセージなら無料で利用できるそうです。今回のプログラムではせいぜい200件\月程度と思われるので無料アカウントで十分でした。

実際のソースはこちらになります。

```javascript
//グローバル変数
var channel_access_token = "[アクセストークン]";
var headers = {
   "Content-Type": "application/json; charset=UTF-8",
   "Authorization": "Bearer " + channel_access_token
};

//#####モジュール②：LineAPIをコールする#####
function sendLineMessage(message){
  var payload =
   {
    "messages":[
        {
            "type":"text",
            "text":message
        }
    ]
   };
  
  var params ={method : 'post', headers: headers, payload: JSON.stringify(payload)};
  UrlFetchApp.fetch("https://api.line.me/v2/bot/message/broadcast", params);
}
```
こちらは特に迷うことなく（というかサンプルソースコードもあったので）公式リファレンスとサンプルコードを見ながらの実装でした。

## モジュール③：次回のトリガーをセットする
GASでは、トリガー実行（ズバリの日時指定実行）は可能であるものの、スケジューラ実行（毎日X時）という実行ができません。しかし、**トリガーをセットする**という機能はあります。例えば初回実行をトリガでAM8時に実行とセットしておき、そのスクリプトの先頭で「次回のトリガを24時間後に設定」としておけば、事実上スケジューラ実行ができます。以下がサンプルのソースコードです。

```javascript
function setTrigger() {
  var m = Moment.moment().add(12,"hours"); //現在日時 + 12時間後
  var NextTriggerTime = new Date(m.format('YYYY/M/D'))
  NextTriggerTime.setHours(m.format('H'));
  NextTriggerTime.setMinutes(30); 
  ScriptApp.newTrigger('GarbageCheck').timeBased().at(NextTriggerTime).create();  
}
```
このモジュールをメインモジュールの先頭に仕込んでおけば、実行と同時に次回のトリガが設定されることになり、スケジューラのような動きを実現できます。
今回は朝6:30に当日のゴミを、夜18:30に翌日のゴミを通知するのでスケジューラは12時間おきになります。よって、現在日時の12時間後にトリガーをセットする動きにしています。

## 出来上がった我が家のアシスタント

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/646573/3a2a2511-18eb-cd0a-66fd-623a27f3e66a.png)

いい感じです。これで我が家のゴミ出し忘れがゼロになり~~私が妻に怒られることも無くなるでしょう！~~家がゴミ屋敷になることもなくなるでしょう

## 失敗談
- 最初はPowerAutomateで実装しており、データもスプレッドシートじゃなくてExcelOnline上で管理していました。が、Httpリクエストを送るアクションがプレミアムのみであったためLine連携ができず断念
- 次にZapierで実装。こちらもいいところまで実装して、Httpリクエストがプレミアムじゃないと使えないことがわかり断念（学習しない人←）

こちらについては、有料である故に(私がケチであるが故に)運用には乗せられなかったものの、いずれもプログラムとしては機能したので機会があればスピンオフ記事を書きたいと思います

## やってみたいこと
次は、「ｘ月」とかをLineボットに送ったら、カレンダーを一覧で送り返してくれる機能を実装してみたいです

## 最後に
全国の市役所の皆さん。ゴミの不法投棄の防止のためにもぜひBot活用しましょう。たった1-2時間で実装できるので…なにとぞ…