【LINE Messaging API＆Google Sheets API】プッシュ通知で週末に家計簿を通知

# はじめに
[前回](https://qiita.com/tacchan5424/items/eae56319e9b84692edfe)は応答通知を作成したので、今回はプッシュ通知に挑戦してみました。
正しい実装かどうかは不明ですが参考程度になりましたら幸いです。

# 背景
今年の6月からGoogleのspreadsheetを使って家計簿をつけ始めました。
最初はいくら使ったのか気になって、お金を使ってもいないのに見ていました。
7月は興味が薄れて最終的に何にどれくらい使ったか、把握できていませんでした。

そこで、決まったタイミングで支出がどのくらいになっているのか通知しれくれたら便利だなと思い作成しました。

# アウトプット
このようになります。
![2196.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1465078/0f7f2699-688e-2a9d-83c1-633e3a1bd2dc.jpeg)

# 目次
・[LINE Developersの登録とチャンネル作成](#line-developersの登録とチャンネル作成)
・[Google APIの登録とGoogle Sheets APIの登録](#google-apiの登録とgoogle-sheets-apiの登録)
・[Google Sheets API実装](#google-sheets-api実装)
・[プッシュ通知実装](#プッシュ通知実装)
・[スケジューリング](#スケジューリング)
・[結果](#結果)

# 開発環境
Node.js：v14.16.1
yarn：1.15.2

# LINE Developersの登録とチャンネル作成
[前回記事]()で作成しているため割愛します。

# Google APIの登録とGoogle Sheets APIの登録
[Google Sheets APIの準備](https://www.1ft-seabass.jp/memo/2020/05/08/npm-googleapis-google-sheet-await-async/)はこちらをご参照ください。
※一部現時点での画面表示と異なる部分があります。

# Google Sheets API実装
実装も[こちら]()の記事を参考にさせていただきました。

```Javascript
module.exports = async function fetchSpreadSheet(){
  // JSON Web Token(JWT) の認証
  await jwtClient.authorize();
 
  // シートを読み込む
  const sheet = await sheets.spreadsheets.values.get({
    auth: jwtClient,
    spreadsheetId: sheet,
    range: cells
  })
  .then(response => {
    return response.data.values;
  })
  .catch(error => {
    console.log('The API returned an error: ' + error);
    return;
  });
  return sheet;
}
```

[こちら](https://developers.google.com/sheets/api/reference/rest/v4/spreadsheets/get)からパラメータを確認することができます。

取得したデータは以下のような形式で取得されます。

```
// シートのA1:C3の範囲を取得した場合
["A1", "B1", "C1"]
["A2", "B2", "C2"]
["A3", "B3", "C3"]
```
これを表示方法に従って整える必要があります。

# プッシュ通知実装
チュートリアルで作成したサンプルBOTをベースにして以下のように実装しました。

```Javascript
async function pushMessage() {
    const dataString = JSON.stringify({
      // ユーザID
      to: "U33c6dc~~~",
      messages: [
        {
          "type": "text",
          "text": `${commonFunction.format(await fetchSpreadSheet())}`
        }
      ]
    })

    const headers = {
      "Content-Type": "application/json",
      "Authorization": "Bearer " + TOKEN
    }

    const webhookOptions = {
      "hostname": "api.line.me",
      "path": "/v2/bot/message/push",
      "method": "POST",
      "headers": headers,
      "body": dataString
    }

    const request = https.request(webhookOptions, (res) => {
      res.on("data", (d) => {
        process.stdout.write(d)
      })
    })

    request.on("error", (err) => {
      console.error(err)
    })

    request.write(dataString)
    request.end()
}

app.listen(PORT, () => {
  console.log(`Example app listening at http://localhost:${PORT}`)
  pushMessage();
})
```
ユーザIDは一般的にどのように取得するのか把握できていませんが、
私はLINE Developersの`トップ > プロバイダー > チャンネル`配下の
`あなたのユーザID`という項目から取得しました。

その後`npm start`コマンドでLINEに通知が来ることを確認します。


# スケジューリング
Heroku Schedulerを使用します。
設定手順は[こちら](https://qiita.com/pythonista/items/2eab3e3acad88c5b759e)をご参照ください。
週末の昼頃に通知が欲しいのですが、日次までしかなかったのでひとまず日次で設定します。
また時差は9時間あるので、3時を設定しておきます。
実行するコマンドは`npm start`です。
**※無料で使えるのですがクレジットカードの登録が必要です。**
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1465078/e103fefb-b0f9-184e-114b-eb9e2dae9837.png)


このままでは毎日通知が来てしまいます。
Heroku Schedulerでは選択肢がなかったため実装でカバーしました。
(もっと方法があるとは思うのですが妥協してしまいました。)

```Javascript
app.listen(PORT, () => {
  console.log(`Example app listening at http://localhost:${PORT}`)
  const today = new Date();
  // 日曜日のみ動作するように設定
  if (today.getDay() === 0) {
    pushMessage();
  }
})
```

# 結果
日曜日の午後0時に予定通り通知が来ました！
![2196.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1465078/256b1b1c-fc74-c91a-1201-319c082dc8af.jpeg)
※罪は酒代です。また、いろいろマイナスになっていますが問題ないです。


# 最後に
私個人用かつとりあえず動かすという目的のため、きれいではない実装をしてしまっていますので
今後は少しでも見ていただきやすい記事になるように気を付けて実装できればと思いました。

どなたかの参考になりましたら幸いです。

# 参考
[Node.js googleapis npmパッケージで Google スプレッドシートを await/async で読み取るメモ](https://www.1ft-seabass.jp/memo/2020/05/08/npm-googleapis-google-sheet-await-async/)
[Heroku Schedulerの設定方法](https://qiita.com/pythonista/items/2eab3e3acad88c5b759e)