[GoogleAppsScript] ReminderをLINEに通知する

# はじめに
LINE Notifyを使用してReminderを通知するBOTを作成しました。
![2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/50563/ea17824b-c07d-a528-1de4-8a771f6daf36.png)

スプレッドシートにReminderの実行時刻とメッセージを指定します。
（※A列は、`毎日`または`毎週月`のように曜日を指定します。）
![1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/50563/d06168a7-13f4-7ece-5d8f-b1d6987802ae.png)

GoogleAppsScript（GAS）にて1日1回、スプレッドシートから当日分のリマインダーをトリガーに登録します。

```plantuml
@startuml
GoogleAppsScript -> GoogleAppsScript: 1日1回起動
activate GoogleAppsScript
  GoogleAppsScript -> GoogleAppsScript: 当日のリマインダーを登録
deactivate GoogleAppsScript
@enduml
```

登録されたトリガーから、指定された時刻にプログラムを実行します。そのプログラムでは、スプレットシートから取得したメッセージを[LINE Notify](https://notify-bot.line.me/ja/)に通知します。

```plantuml
@startuml
GoogleAppsScript -> GoogleAppsScript: 当日の指定時刻にリマインダー起動
activate GoogleAppsScript
  GoogleAppsScript -> "LINE Notify": ...POST /notify
deactivate GoogleAppsScript
activate "LINE Notify"
  "LINE Notify" -> "LINE Notify": 対象のユーザーに通知
deactivate "LINE Notify"
@enduml
```

# LINE Notify
- [詳しくはこちら](https://qiita.com/yun_bow/items/cc1f3c45c2d86e39c6f7)

# GoogleAppsScript
1. GoogleAppsScriptを開いて、以下のスクリプトを実装します。`LINE_NOTIFY_TOKEN`にLINE Notifyで発行したトークンを指定する。
2. `SSID_REMAINDER`にスプレッドシートのIDと`SSN_REMAINDER`にシート名を指定する。

```JavaScript:main.gs
/**
 * リマインダーBOT
 */
const LINE_NOTIFY_TOKEN = '*****'; // LINE NOTIFYのアクセストークン
const SSID_REMAINDER = '*****'; // リマインダーのスプレッドシートのID
const SSN_REMAINDER = 'line_trigger'; // リマインダーのスプレッドシートのシート名
const WEEKDAY = ["日", "月", "火", "水", "木", "金", "土"];

let spreadsheet = SpreadsheetApp.openById(SSID_REMAINDER);
let sheet = spreadsheet.getSheetByName(SSN_REMAINDER);

/**
 * メイン処理
 */
function main() {
    try {
        deleteTrigger();
        let nowDt = new Date();

        let itemList = getItemList();
        for (let i in itemList) {
            let item = itemList[i];

            if (item.type == `毎週${WEEKDAY[nowDt.getDay()]}` ||
                item.type == '毎日') {

                let trigger = setTrigger(item.time);
                sheet.getRange(parseInt(i) + 1, 4).setValue(trigger.getUniqueId());
            }
        }
    } catch (e) {
        console.error(e.stack);
    }
}

/**
 * メッセージを通知する
 * @param {Object} e
 */
function notify(e) {
    try {
        console.log(`notify: triggerUid=${e.triggerUid}`);

        let itemList = getItemList();
        for (let i in itemList) {
            let item = itemList[i];
            if (item.triggerId == e.triggerUid) {
                sendLineNotify(`\n${item.message}`);
            }
        }
    } catch (e) {
        console.error(e.stack);
    }
}

/**
 * トリガーを設定する
 * @param {String} time 
 * @return trigger
 */
function setTrigger(time) {
    let arr = time.split(':');
    var date = new Date();
    date.setHours(arr[0]);
    date.setMinutes(arr[1]);
    return ScriptApp.newTrigger('notify')
        .timeBased()
        .at(date)
        .create();
}

/**
 * トリガーを削除する
 */
function deleteTrigger() {
    let triggerList = ScriptApp.getProjectTriggers();
    for (let i in triggerList) {
        let trigger = triggerList[i];
        if (trigger.getHandlerFunction() == 'notify') {
            ScriptApp.deleteTrigger(trigger);
        }
    }
}

/**
 * スプレッドシートのデータを取得する
 * @return itemList
 */
function getItemList() {
    let itemList = [];
    let lastRow = sheet.getLastRow();
    if (0 < lastRow) {
        itemList = sheet.getRange(1, 1, lastRow, 4).getValues();
        itemList = itemList.map((row) => {
            return {
                type: row[0],
                time: row[1],
                message: row[2],
                triggerId: row[3],
            }
        });
    }
    return itemList;
}

/**
 * LINEにメッセージを送信する
 * @param {String} message メッセージ 
 */
function sendLineNotify(message) {
    let url = 'https://notify-api.line.me/api/notify';
    let options = {
        'method': 'post',
        'headers': {
            'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8',
            'Authorization': `Bearer ${LINE_NOTIFY_TOKEN}`
        },
        'payload': `message=${message}`
    };
    let response = UrlFetchApp.fetch(url, options);
    return JSON.parse(response.getContentText('UTF-8'));
}
```
- `main`関数を1日1回実行します。日付が変わった後の時間帯が望ましいです。トリガー登録時にトリガーIDをスプレットシートに保存します。
- 登録されたトリガーから、指定された時刻に`notify`関数が実行されます。実行時のトリガーIDから、通知対象のメッセージを取得します。

# 参考リンク
- [ScriptApp.newTriggerでatHourの前にevery***は必須？らしい](https://qiita.com/yooo_gooo/items/56762a07da97e67c3445)
- [[GAS]スクリプトからトリガーをセットする話](https://qiita.com/s_maeda_fukui/items/2fcbd34d7db5e8e7a2b8)
- [月末営業日にタスクをリマインドする ~ 使うのはSlackとGoogleアカウントだけで10分くらいで実装できるよ ~](https://qiita.com/ykhirao/items/782e20ab0465533c48f6)

# さいごに
ソースコードをGitHubに公開しています。

- [ソースファイルはこちら](https://github.com/yunbow/GoogleAppsScript-LINE-Reminder)

以上です。