LINE API+AWS+Node.jsでLINE Bot作成してみて

チャットボットを作成したい。そんな時ありますよね。
今回、趣味でLINE MessgingAPI + AWSを利用してチャットボットを作成してみました。
約3~5ヶ月前の話ですが、復習も兼ねて思い出しながら記事にまとめたいと思います。

# Messaging APIでできること
ボットのアプリサーバーとLINEサーバー間でデータ通信ができ、Webhookをトリガーにリクエストを送信することができます。

![image.png](https://qiita-image-store.s3.amazonaws.com/0/191919/f2042697-6879-4fd7-4039-a9318d2e1538.png)

具体的には、[応答メッセージ](https://developers.line.biz/ja/reference/messaging-api/#send-reply-message)や[プッシュメッセージ](https://developers.line.biz/ja/reference/messaging-api/#send-push-message)にはじまり、[画像・音声・動画やスタンプ](https://developers.line.biz/ja/docs/messaging-api/message-types/)を送信することができたりします。また、[ユーザーのプロフィール取得](https://developers.line.biz/ja/reference/messaging-api/#get-profile)など、**基本的に普段利用しているLINEの機能ならある程度使えます**。

詳しくは[こちら](https://developers.line.biz/ja/docs/messaging-api/)を参照すると良いと思います！


# システム設計
参考記事を習い、以下のような構成を取ります。
![kamihork.png](https://qiita-image-store.s3.amazonaws.com/0/191919/16891b99-5b39-08d1-e041-704a97b22fbf.png)

大枠としては以下の流れになります。
**1) LINEで送信した情報をAPI Gatewayを通じてLambda（Receiver）が受信**
**2) 受信した情報をDynamoに格納**
**3) Dynamoは更新を検知し、トリガーとして別のLambda（Sender）を呼び出す**
**4) Senderで情報を更新し、NATを通しLINEサーバーに送信**
**5) Botからレスポンスが返る**

今回作成するBOTでは、一度に大人数に利用してもらうことを想定しており、かつレスポンス処理に様々なロジックを組み込むことを想定していました。
そのため、単純に一つのサーバー内で完結するような構成にはせず、サーバー負荷やパフォーマンスを考慮した構成になっています。

結論から言うと、以下の戦略をとり「メッセージの受信」と「メッセージの送信」との役割を非同期にすることで、これを解決します。
> BOT Serverは、ユーザからのメッセージをLINE Serverに「できるだけ貯めさせない」ようにする。つまり、とにかくLINE ServerからBOT Serverにメッセージを迅速に移動させる。
ユーザは数秒であれば、返事を待ってくれる。返信処理を焦って行わない。

上記システムの具体的な説明については、以下の記事にまとまっており、そちらを参考にさせて頂きました。
[大量メッセージが来ても安心なLINE BOTサーバのアーキテクチャ](https://qiita.com/yoichiro6642/items/6d4c7309210af20a5c8f)
[LINE Bot を AWSを使ってシステム構築してみた。](https://qiita.com/hiyuzawa/items/10e7bf2f6ad5d1c7fc9c)

## 注意したいポイント
具体的な作成手順は上記の記事にまとまっていますので、
ここでは、ネットワーク構築や開発の際に気をつけたいポイントを**実際にハマってしまった実例**を元に紹介します。

### ルートの定義忘れ
実際に構築を行えばわかりますが、DynamoDBでは**異なるAvailability ZoneのSubnetを2セット**利用します。
これにより、ルートテーブルも追加で作成する必要があるのですが、あろうことか後で作成した**AZ-CのPrivateRouteTableのTargetにNAT Gatewayの設定をし忘れ**、一定頻度で接続ができない状態が続きました。

AZ-Aの方はしっかり定義されているため、Lambdaの中身を変えたり等、環境を更新するたびにいずれかのアベラリティーゾーンのルートで通り一定確率で疎通する、しないが起こり原因特定に時間がかかってしまった背景もあり、
特に**設定をドキュメントに起こさず構築してしまったが故**に、このミスに気づくのに実に1ヶ月ほどかかりました😂

この間、[LambdaやDynamoの設定が悪いんじゃないか](https://blog.honeybadger.io/how-to-report-node-js-errors-from-aws-lambda/)？、[もしかしてメモリやタイムアウトの設定](https://www.yamamanx.com/aws-lambda-process-exited-before-completing-request/)?、[Lambdaでの組み合わせによってそういうこともあるらしい](https://employment.en-japan.com/engineerhub/entry/2018/07/17/110000#bot5)?、[そもそも方法が違うのではないか](https://viewse.blogspot.com/2017/01/aws-lambda-process-exited-before.html)等々、ひたすら調べては実行してみて解決できなかったので、最終的にはHerokuにNode.jsのソースを移植して開発していました（笑）
原因がまさかのルートの定義忘れだとは。。灯台下暗しでした。

### 恐怖！deadbeafメッセージ
LINE上でメッセージを送り、それをDynamoDBに格納していくテストを行なっていた時です・・・
idはユニーク, fromには送信されたグループIDまたはユーザーIDが, textには送信内容が入るようになっています。
<img width="1405" alt="deadbeef.png" src="https://qiita-image-store.s3.amazonaws.com/0/191919/e9385d76-88c7-8556-3166-5958973697ec.png">

そうです。

<img width="1405" alt="deadbeef2.png" src="https://qiita-image-store.s3.amazonaws.com/0/191919/1157d3db-764b-665d-f8c6-1bcfeacf25fe.png">

明らかに、**自分たちがデバッグで送信したもの以外のメッセージが飛んできていた**のです。
この時深夜3時ぐらいに2人で開発をしていたのですが、「自分たちがデバッグしているLINEグループに知らない人が入っている？」「DBを直接いじられているのかも！」「AWSに入られている可能性があるかも、今すぐ止めよう！」とかなり焦った記憶を鮮明に覚えています。

「タチの悪いハッカーが侵入してきた、やばい、終わった。」当時はそんな気持ちでした。

ひとまず稼働しているものを全て止めて動かない状態にして、変なインスタンスが立っていないか？高額請求されていないか？を一通り確認しましたが、**結論何も問題がありませんでした**。

落ち着いて朝方5時ぐらいに調査をしていた結果、衝撃の事実が。実はこれ、[LINEの仕様](https://qiita.com/jiz/items/0cd1b39bf54184f20d10)だったのです。

*「deadbeefdeadbeefdeadbeefdeadbeef・・・」というのは、 16進数で作れるおもしろ単語ということで、一部の界隈では好まれているらしいです。*

不吉すぎて何も面白くなかったです。。。心臓に悪い。。

### はじめてのNode.js+LINE APIでのサーバーサイド開発
今回はじめてAPIを調べながら考え、一から叩くような開発を行いました。
そして普段開発で利用しないNode.jsを利用することもあり、[LINEのAPIリファレンス](https://developers.line.biz/ja/reference/messaging-api/)や参考記事をみながら開発にかなりの時間がかかりました。
APIを叩いて、結果を受け取ってというのは割とスムーズにできたのですが、Node.jsのサーバーサイド開発が一番苦労しました。

#### コールバック地獄
はい、コールバック地獄にどハマりしました。
単体の非同期処理は何度か書いたことがあるのですが、今回の開発のように複数の非同期処理と同期処理が混ざるような開発は初めてでして、とても苦戦しました。
苦戦した原因としては以下の2つです。
**1) 完全に趣味の開発だったので、ろくに設計をせず実装をしながら方針を決めて開発していた**
**2) async/awaitを知らなかった**

1つ目に関しては、設計の手間を省いたために大変なことになりました。最初のうちはよかったのですが、機能を追加するたびにコードが肥大化していってしまっため、かなり可読性が悪いコードとなってしまいました。反省。

2つめに関しては、完全に知識不足でした。非同期=Promiseという固定観念が自分の中であり、ひたすらPromiseしていました。同期処理を行うためにひたすらthenで苦しみながら連結していたのですが、**それってasync/awaitで簡単にできるよ**と言われ衝撃を受け、のちに書き換え可能な箇所は全て書き換えました。。

また、LINE BOTの開発ということで当然テスト環境のようなものはなく、**毎回「修正→（herokuの場合）デプロイ→LINEで動作確認→ログやDBを確認」ということを行なっていた**ため、ここにも時間を費やしてしまいました。

### その他
他にもLINEの認証設定や、LambdaやDynamoの設定、JSONの利用等、色々なところで詰まりましたが、ここでは割愛します。

## まとめ
LambdaやDynamoDB等はじめての技術を多く使い、LINE Botを作成することができました。
開発期間としては、込み込みで3ヶ月ぐらいでしたが、UXを考え、ここで処理を失敗した時はこうしようとか、初めてBotを入れる人の立場に立ってシナリオを考えようとか、色々な経験ができてとても楽しかったです。
作成背景やサービスについてはここでは語れないですが、ただのBotではなく、**AIを利用した感情分析を入れてみたり**、**対話AI**を追加してみたりをしていました。
**対話AIについては[こちらの記事](https://qiita.com/kamihork/items/a875cb26ee2c1444f08b)**にまとめていますので参考にして見てください。

また、今回は紹介しませんでしたが**[LIFF（LINE Front-end Framework）](https://developers.line.biz/ja/docs/liff/overview/)**というプラットーフォームを機能に
組み込んだりもしました。
LINE上でwebアプリを開けるので、Formをコーディングをするのも地味に楽しかったです。

普段頻繁に利用しているLINEだからこそ、こんなUXだったら良いよねというのが思いつき、それをすぐに近くの人に利用してもらうことでフィードバックを貰うことができたのがとても良かったです。

実は、AWSで開発する前はしっかりDB設計をしてPosgre+Node+herokuで開発をしており、そこでも山あり谷ありだったのですが、それは別の機会に。

# 参考文献等（メモしている限り）
[LINE Bot を AWSを使ってシステム構築してみた。](https://qiita.com/hiyuzawa/items/10e7bf2f6ad5d1c7fc9c)
[LINE Messaging API](https://developers.line.biz/ja/docs/messaging-api/)
[LINE Front-end Framework](https://developers.line.biz/ja/docs/liff/overview/)
[DynamoDB の基礎知識とまとめ](https://qiita.com/hshimo/items/e5ad98b21786d796f1da)
[AWS Lambda超入門](https://qiita.com/s_s_k/items/b584435120e99d63975b)
[parse・stringifyメソッドの使い方](https://blog.codecamp.jp/javascript-json)
[LINEのMessaging APIを使って投稿(push)と返信(reply)を使ってみる。](https://ameblo.jp/ponkotsuameba/entry-12212318642.html)
[LINE Messaging API と AWS Lambda で LINE BOT を作ってみた](http://www.kazuweb.asia/aws/lambda/chatbot)
[AWS LambdaとLINE BOT APIで友達になったユーザーをDynamoDBで管理する](https://dev.classmethod.jp/cloud/aws/receive-line-bot-api-on-lambda/)
[aws lambdaとapi gatewayで linebotを作成する](http://kakts-tec.hatenablog.com/entry/2017/06/23/002024)
[LINE BOTをNode.jsで作る](https://qiita.com/Hirosaji/items/4c136c13660bb1217662)
[Talk API](https://a3rt.recruit-tech.co.jp/product/talkAPI/)
[日本語極性判定を作って公開した](https://qiita.com/keigohtr/items/e85b413283b87ac02f4b)