タスクマネージャーをアップデートした！

# 前回の記事
https://qiita.com/kenjirohayashi/items/76403ac70e0733ed6438


### 作成したタスクマネージャーbot

<table>
<tr>
<td><img width="150px" alt="chatbotのQRコード" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1973408/82ec170b-303c-3da3-763e-42db27efbf2f.png"></td>
<td><img width="300px" alt="chatbotのQRコード" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1973408/067f0f38-4e8d-a515-1091-068129744538.gif"></td>
</tr>
</table>





# 追加した機能
・WebViewでタスクの登録
・通知機能
・リッチメニュー


# 使用した技術

**フロントエンド**
・LINE

**バックエンド**
・Node.js（Express)
・firebase(cloud functions, firestore)
・Messaging API
・dayJs

**WebView**
・LIFF（LINE SDK)
・ReactHooks
・Material-UI
・firebase(Hosting, firestore)


# システム構成
<img alt="システム構成図" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1973408/6fcb2b79-182f-46d8-0d5f-ef5d740da3c2.png">



### Cloud Functions + LIFF
1.main関数 : チャットボットとのやりとりを行う
2.register関数　：　タスクが登録できたことを知らせる
3.reminder関数　：　指定された時間に通知をする
＋
4.LIFF　：　タスクの登録を行う

## それぞれの関数について

## main関数
main関数の主な役割は登録したタスクの表示です。
下記シーケンス図はタスク一覧表示の処理の流れです。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1973408/8e4d219f-c0b7-7b83-fb10-156c5cf1cf37.png)
main関数はwebhookURL[^1]として設定しています。
つまりユーザーがメッセージを送信すると、main関数にpost（webhookイベントオブジェクト）が送られます。
webhookイベントオブジェクトの中身は[こちら]
(https://developers.line.biz/ja/reference/messaging-api/#webhook-event-objects)

1.webhookイベントオブジェクトを受け取ったmain関数は、メッセージ内容を確認します。
　　　メッセージ内容が"@show"であれば2へ
2.ユーザIDとfirebaseに登録してあるIDと照合し一致するタスク取り出します。
3.取り出したタスクをボタンテンプレート形式でユーザに送信するよう処理を行なっています。

ざっくり説明するとmain関数は1~3の処理を行なっています。


[^1]:[^1]webhookとはユーザーがメッセージを送信したり、友達追加したりなどイベントが発生すると、指定したURLにpostを投げる仕組み・技術のことです

## register関数
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1973408/80ab50bd-302b-0529-6b58-349c4cb5540a.png)
register関数はタスクが登録された時に登録できたことを通知するプログラムです。
トリガーをonCreateにすることでfirestoreにタスクが登録された時に、処理が走るようになっています。

```Node.js
exports.register = functions.region("asia-northeast1")
    .firestore
    .document('todos/{todoId}')
    .onCreate((snap, context) => {
      const userId = snap.data().userId;
      const todoName = snap.data().name;
      return Handle.registerMessage(userId, todoName);
});
```
firestoreにタスクが登録されるとそのタスクからユーザIDとタスク名を取得し、ユーザID宛にpushメッセージを送る処理をHandle.registerMessage()で書いています。

## remider関数
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1973408/1d7143d9-54cd-7e4d-5a48-cea2972cf26e.png)

reminder関数は通知設定されたタスクを時間通りに通知するプログラムです。
トリガーは１分毎に実行されるように書いていて、現在時刻と通知時間が一致するタスクがあるかどうかを毎分照合しています。
一致するタスクがあればそのタスクデータを取り出し、登録したユーザーにpushメッセージを送るコードをHandle.reminder()で書いています。

```Node.js
exports.reminder = functions.region('asia-northeast1')
    .pubsub.schedule('every 1 minutes')
    .timeZone('Asia/Tokyo')
    .onRun((context) => {
        return Handle.reminder();
})
```

## LIFF
タスクの登録はwebアプリケーションで行なっています。
LIFFとはwebアプリケーションをLINEの中で起動させる仕組みのことで、LINEから離れずに操作することができます。
こんな感じ↓↓↓↓
<img width="200px" alt="chatbotのQRコード" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1973408/329ffb8b-7a56-3589-ad8a-3ccc875543f2.png">

```React.js
  useEffect(() => {
    liff.init({liffId: LIFF_ID})
    .then(() => {
      getProfile();
    })
    .catch((err) => {
      console.log(err.code, err.message);
    })
  }, [])

  const getProfile = () => {
    liff.ready.then( async() => {
        const context = await liff.getContext()
        setUid(context.userId);
    })
  }
```
webアプリケーションの中でLINESDKを用いて、liff.init()からのliff.getContext()することで
ユーザー情報を取得できます。[公式ドキュメント](https://developers.line.biz/ja/docs/liff/developing-liff-apps/)
本アプリではタスクにユーザIDをタグ付けすることで、誰のタスクかを管理しています。


# まとめ
ざっくりと、本アプリの機能を解説してみました。
こちらのLINEBotはまだまだ改良していくつもりなので、追加機能の案やバグがあればドシドシコメントお願いします！！
また、今回の記事で分かりにくいことがあればコメント頂ければ幸いです！

# 実装予定機能
毎週リマインド機能
スヌーズ機能

# 参考文献
https://developers.line.biz/ja/docs/messaging-api/
https://dev.classmethod.jp/articles/try-cloud-functions-scheduler-pubsub/
https://qiita.com/hirothings/items/37430b2408a5a7a85972
https://ginpen.com/2019/06/01/firestore-indexes-json/
https://github.com/iamkun/dayjs/blob/dev/docs/en/API-reference.md#list-of-all-available-formats