AWS LambdaとServerless Frameworkを利用してLINE BOTを作ってみた

# 概要
リッチメニューのボタンやメッセージに反応し、特定のデータや画像メッセージ等を返してくれるLINE BOTをAWS LambdaやServerless Frameworkを利用して作ってみたので、その手順を書き記しておきます。

# 利用する主なサービスやツールたち
### Serverless Framework
https://www.serverless.com/

Lambdaを使ったサーバーレスアプリケーションを簡単にデプロイ/管理出来るフレームワーク。LambdaやAPI Gateway周りの設定などをServerless.ymlに記載することで、ある程度柔軟な設定が出来、serverless deployコマンドひとつで関連する全ての環境をデプロイすることができるので便利です。

### AWS Lambda
Serverless Frameworkの中にLambdaも含まれているのですが一応記載。BOTを構築する手段は他にも色々ありますが、今回はAWSの他サービスと組み合わせたかった事、GUIだけでの構築ではなくプログラムを組んで柔軟なBOT構築をしたかった事、LINE公式のline-bot-sdk-nodejsライブラリが使いやすそうだった事などから今回はLambdaを選択しました。

### LINE Messaging API
https://developers.line.biz/ja/services/messaging-api/

LINE BOTの制御はこのLINE Messaging APIで行います。メッセージの送信、プロフィールの取得、リッチメニューの制御など様々な事ができます。

# 用意する環境
### Node.js
https://nodejs.org/ja/
こちらからダウンロードできます。

### Serverless Framework
```
npm install -g serverless
```
npmが使える環境ならこちらを叩いてインストールしておきます。

### AWSアカウント
Serverless Frameworkでデプロイをする為のIAMユーザーを予め作成しておく必要があります。
ドキュメントや他の記事等を見てみると、前まではそのIAMユーザーにAdministratorAccessのポリシーを設定する必要があったみたいですが、S3, CloudFormation, CloudWatch, Lambda, API Gateway辺りのフルアクセス権限があれば問題はなさそうでした。

上記でIAMユーザーを作成した後、serverless deployを行うマシンにAWS Credentialを登録しておく必要があります。
```
serverless config credentials --provider aws --key {IAM_ACCESS_KEY} --secret {IAM_SECRET_KEY} --profile {HOGEHOGE}
```

### LINE公式アカウント
LINEアカウントを作成した後、LINE Developers Consoleにログインし、Messaging APIチャネルの作成を行っておきます。
https://developers.line.biz/console/

※LIFFアプリを利用する場合は、Loginチャネルも作成しておく必要があります。

# 実装準備
### Serverless Frameworkでのデプロイ
最低限必要な以下二つのファイルを作成し、デプロイを行います。
* app.js　：BOTシステムが書かれたJSファイル
* serverless.yml　：Serverless FrameworkのAWSまわりの設定が書かれたymlファイル

serverless.ymlの書き方解説はここでは省きますが、ひとまずservice, provider, functionsの要素を記述していれば最低限は動くはずです。

ここまで用意したら、
```
serverless deploy
```

コマンドでAWS環境にデプロイすることができるようになります。
成功するとAPI GatewayのAPI URLがログに出ていると思うので、これはメモっておきます。

### LINE Messaging APIの設定
先程作成したMessaging APIチャネルの設定内で、Webhook URLを設定する必要があります。
ここは先ほどのServerless Frameworkデプロイ時に作成されたAPI GatewayのAPI URLを設定しておきましょう。

ここまで用意すれば環境構築は終了です！
後はNode.jsでBOTをひたすら実装していきます。

# LINE BOTの実装
line-bot-sdk-nodejsライブラリ
https://line.github.io/line-bot-sdk-nodejs/

今回は上記のライブラリを利用してNode.jsとexpressでBOTを構築していきます。
導入や細かい部分は置いておいて、ざっくりと主要機能の説明をしていきます。

### Messaging APIとの接続、ヘッダー署名の検証
```
const config = {
  channelAccessToken: 'YOUR_CHANNEL_ACCESS_TOKEN',
  channelSecret: 'YOUR_CHANNEL_SECRET'
};

new line.Client(config);
line.middleware(config);
```

まずはじめに、Messaging APIのアクセストークンとシークレットキーをconfigとして設定します。これはLINE Developers Consoleに書いてあります。
用意したLINE公式アカウントのMessaging APIを利用するためのconfigと、LINEサーバーとやり取りする際のx-line-signatureヘッダー署名を検証してくれるmiddlewareを利用するための設定がこちらになります。

ひとまずここまで実装してもろもろの設定が上手く行っていれば、Messaging APIチャネル設定ページのWebhook URL欄にある検証ボタンを押したら正常なレスポンスが帰ってくることが確認できるはずです。

### Messaging APIでの実装の流れ
Messaging APIの公式ドキュメントがこちら。
https://developers.line.biz/ja/reference/messaging-api/

line-bot-sdk-nodejsライブラリを利用していきますが、基本的にはこの公式ドキュメントに書かれた仕様に沿ってJSONオブジェクトでパラメータを組み立ててAPIを叩いていくことになります。

BOTに対してメッセージの送信やフォローなど何かしらのアクションが行われた際に、WebhookイベントオブジェクトとしてJSONが送られてきます。
https://developers.line.biz/ja/reference/messaging-api/#message-event
```
{
  "destination": "xxxxxxxxxx",
  "events": [
    {
      "replyToken": "nHuyWiB7yP5Zw52FIkcQobQuGDXCTA",
      "type": "message",
      "mode": "active",
      "timestamp": 1462629479859,
      "source": {
        "type": "group",
        "groupId": "Ca56f94637c...",
        "userId": "U4af4980629..."
      },
      "webhookEventId": "01FZ74A0TDDPYRVKNK77XKC3ZR",
      "deliveryContext": {
        "isRedelivery": false
      },
      "message": {
        "id": "444573844083572737",
        "type": "text",
        "text": "@All @example Good Morning!! (love)",
        "emojis": [
          {
            "index": 29,
            "length": 6,
            "productId": "5ac1bfd5040ab15980c9b435",
            "emojiId": "001"
          }
        ],
        "mention": {
          "mentionees": [
            {
              "index": 0,
              "length": 4,
              "type": "all"
            },
            {
              "index": 5,
              "length": 8,
              "userId": "U49585cd0d5...",
              "type": "user"
            }
          ]
        }
      }
    }
  ]
}
```

例えばメッセージだった場合は`events.type`が`message`になっているWebhookイベントオブジェクトとして送られてきたりするので、基本的にはこの`events.type`を見てBOTがどう応答していくかを実装していく流れとなります。

よく利用する`type`としては`message`の他に`postback`というものがあります。
https://developers.line.biz/ja/reference/messaging-api/#postback-event

これはメッセージと同時、もしくはメッセージを送らずに裏で何かしらのdataを渡すことができるもので、例えばリッチメニューのボタンを押した時などにポストバックメッセージを送信するなどの使い方があります。
トークにテキストとして打ち込まれてもBOTに反応されたくない場合や、何かしらのパラメータを含めてBOTに応答をお願いしたい時などに役立ちます。
```
{
    "destination": "xxxxxxxxxx",
    "events": [
        {
            "replyToken": "b60d432864f44d079f6d8efe86cf404b",
            "type": "postback",
            "mode": "active",
            "source": {
                "userId": "U91eeaf62d...",
                "type": "user"
            },
            "timestamp": 1513669370317,
            "webhookEventId": "01FZ74A0TDDPYRVKNK77XKC3ZR",
            "deliveryContext": {
                "isRedelivery": false
            },
            "postback": {
                "data": "storeId=12345",
                "params": {
                    "datetime": "2017-12-25T01:00"
                }
            }
        }
    ]
}
```

この様にWebhookイベントを受け取った後BOTはそのイベントに対して何かしらのアクションを行う訳ですが、ひとまず代表例としてメッセージを返すreplyMesage関数だけ紹介しておきます。

__`replyMessage(replyToken: string, messages: Message | Message[], notificationDisabled: boolean = false): Promise<MessageAPIResponseBase>`__
https://developers.line.biz/ja/reference/messaging-api/#send-reply-message

「メッセージを返す」という単純な関数ですが出来る事は多く、単純なテキストメッセージを返すだけでなく、画像やスタンプ、カルーセル（横スクロール選択メニューみたいなもの）や自分で自由にデザインが出来るFlex Messageなど様々なものを返すことができます。
メッセージの詳しい仕様はこちらから。
https://developers.line.biz/ja/reference/messaging-api/#message-objects

例えば単純にテキストメッセージを返したい場合は、以下のような形となります。
```
replyMessage(
	"replyToken",
	{
		"type" : "text",
    	"text" : "送信したいテキスト"
	}
)
```

パラメータの`message[]`に複数メッセージを含めることで最大5連投の返信ができますが、ひとつの`replyToken`につき1回しか`replyMessage()`が使えないのでそこだけ注意が必要です。

# LINE BOT実装例の紹介
こちらの記事にて、実際に今回解説した構成でのLINE BOTを利用して作ってみたシステムを紹介しているので是非！
[車のナンバープレート黒塗りBOTを作ってみた](https://qiita.com/MKen_bu/items/919ba4aff9a8a026d0a0)

![Screenshot.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2727975/8d8d3c62-9f48-04ed-ff93-9667e4de6634.png)