LINE Messaging API 〜 API Gateway 〜 SQS 〜 Lambda を連携する

## 前書き
- LINEのchatbot基盤を作ります
- LINE Messaging APIとAWSの各種マネージドサービスとの連携方法についてまとめます
- セキュリティ設定については別記事にまとめます（一旦ゆるゆるに設定）
- Lambdaで使用する言語はGoを採用します

## 構成
<img width="1015" alt="architecture-3.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/25691/846b5de3-5684-a786-70c9-36629cd478c9.png">

作成するchatbotの流れは以下の通りです。最終的には送信したメッセージをDynamoDBに保存してLINE Messaging APIを呼び出しますが、本記事では対象外とします。

1. LINE公式アカウントを友達登録する
2. 公式アカウントにメッセージを送る
3. LINE ServerからAPI Gatewayにリクエストが送信される（webhook URL）
4. API GatewayはSQSにメッセージを保存して、LINE Serverにレスポンスを返す
5. SQSにメッセージが保存されたタイミングでLambdaが起動する

## 環境
| Name | Version |
| --- | --- |
| Go | 1.12.1 |

## LINE chatbotの設定
- 必要な登録を行い、[LINE developer console](https://developers.line.biz/console/) にアクセスする。
- `Provider`を作成する（`Provider`とはアプリケーションを管理する組織のこと）。
- `Channel` を作成する。Channel TypeはMessaging APIを選択する。

## SQSの作成
- 新しいキューを作成する。
  - 「キュー名」は`line-message`を入力
  - 「キューの種類」は`標準キュー`を選ぶ
- 「アクセス許可」タブを選択する。
  - `アクセス許可の追加`ボタンを押す
  - 「効果」は`許可`を選ぶ
  - 「プリンシパル」は`全員(*)`にチェックを付ける
  - 「アクション」は`すべてのSQSアクション(SQS:*)`にチェックを付ける

## API Gateway用のIAMロールの作成
- 新しいIAMロールを作成する。
  - 「エンティティの種類」は`AWSサービス`を選ぶ
  - 「使用するサービス」は`API Gateway`を選ぶ
  - 「ポリシー」と「タグ」設定は飛ばす
  - 「ロール名」は`APIGatewayTestRole`と入力
- `ポリシーをアタッチします`ボタンを押す
  - 「ポリシーのフィルタ」で`AmazonSQSFullAccess`を検索して、アタッチする

## API Gatewayの作成
- 新しいAPI Gatewayを作成する。
  - `REST API`を選ぶ
  - 「API名」は任意の名前を入力（今回は`TestAPI`とする）
  - 「エンドポイントタイプ」は`リージョン`を選ぶ
- `linemessage`という名前の新しいリソースを作成する。
- 作成した`linemessage`リソースにPOSTメソッドを追加する。
  - 「統合タイプ」は`AWSサービス`を選ぶ
  - 「AWSリージョン」は使用中のエリアを選ぶ
  - 「AWSサービス」は`Simple Queue Service(SQS)`を選ぶ
  - 「HTTPメソッド」は`POST`を選ぶ
  - 「アクションの種類」は`パス上書きの使用`を選ぶ
  - 「パス上書き (省略可能)」は作成したSQSのURLのうち`{account_id}/line-message`を入力
  - 「実行ロール」は作成したIAMロールのARNを入力
- 作成したPOSTメソッドの「統合リクエスト」をクリックする
  - 「HTTP ヘッダー」から`ヘッダーの追加`ボタンを押す
  - 「名前」は`Content-Type`を入力
  - 「マッピング元」は`'application/x-www-form-urlencoded'`を入力
- 作成したPOSTメソッドの「マッピングテンプレート」をクリックする
  - 「リクエスト本文のパススルー」は`テンプレートが定義されていない場合 (推奨)`を選ぶ
  - `マッピングテンプレートの追加`リンクを押して`application/json`を入力
  - 下のコード入力欄に`Action=SendMessage&MessageBody=$util.urlEncode($input.body)`を入力して保存
- 作成したPOSTメソッドのアクションから`APIのデプロイ`を押す
  - 「デプロイされるステージ」は`[新しいステージ]`を選ぶ
  - 「ステージ名*」は`prod`を入力して、`デプロイ`ボタンを押す

## LINE Messaging API の webhook設定
- API GatewayのURLを確認する
  - 先ほど作成したAPI Gatewayの「ステージ」を押す
  - 「prod」の左側にある矢印をクリックして展開して、「POST」を押す
  - 右側の「URL の呼び出し:」にURLが表示されるのでコピーしておく（形式： `https://{api_id}.execute-api.{region_name}.amazonaws.com/prod/linemessage`）
- 最初に作成したLINE chatbotのchannelの設定画面を開く
- `Messaging API`タブを開く
- 「Webhook URL」にコピーしたURLを入力して、`Verify`ボタンを押す

以上でLINEからメッセージを送るとSQSにキューイングされるところまでが完成したので、一度動作チェックしてみるとよいと思います。次はLambdaを作成してSQSにメッセージが入ってきたら自動的に動くように連携します。

## Lambda実行ファイルの用意
- Lambdaの公式ドキュメントにあるgoのサンプルコードを`main.go`として保存する

```go:main.go

package main

import (
	"context"
	"fmt"

	"github.com/aws/aws-lambda-go/events"
	"github.com/aws/aws-lambda-go/lambda"
)

func handler(ctx context.Context, sqsEvent events.SQSEvent) error {
	for _, message := range sqsEvent.Records {
		fmt.Printf("The message %s for event source %s = %s \n", message.MessageId, message.EventSource, message.Body)
	}

	return nil
}

func main() {
	lambda.Start(handler)
}

```

- `GOOS=linux GOARCH=amd64 go build -o main`コマンドでビルドする
- ビルドした実行ファイル`main`をzip圧縮する

## Lambda用のIAMロールの作成
- 新しいIAMロールを作成する。
  - 「エンティティの種類」は`AWSサービス`を選ぶ
  - 「使用するサービス」は`Lambda`を選ぶ
  - 「ポリシー」は`AdministratorAccess`を選ぶ
  - 「タグ」設定は飛ばす
  - 「ロール名」は`LambdaLineMessageTestRole`と入力

## Lambdaの作成
- 新しいLambdaを作成する。
  - `一から作成`を選ぶ
  - 「関数名」は`testLineMessage`を入力
  - 「ランタイム」は`Go 1.x`を選ぶ
  - 「実行ロールの選択または作成」の矢印を押して展開する
  - 「実行ロール」は`既存のロールを使用する`を選ぶ
  - 「既存のロール」は先ほど作成した`LambdaLineMessageTestRole`を選ぶ
- SQSと連携する。
  - 「Designer」の`トリガーを追加`ボタンを押す
  - トリガーの種類は`SQS`を選ぶ
  - 「SQSキュー」は作成した`line-message`を選び、`追加`ボタンを押す
- サンプルコードをアップロードする。
  - 「関数コード」の「関数パッケージ」の「アップロード」ボタンを押して、作成した`main.zip`を選び、右上の「保存」ボタンを押す

以上でSQSにメッセージが入ったタイミングでLambdaが動くようになりました。試しにLINEでメッセージを送ってみるとSQSにキューイング、即座にLambdaが起動したことがCloud Watchで確認できると思います。（Lambdaのモニタリングタブを開いて、一番下の`CloudWatch Logs Insights`をみると簡単に確認できます）

## あとがき
本記事の内容は以上になりますが、もちろん本番運用させるには他にも考慮しなくてはいけない点は沢山あると思います。ざっと記載すると、

- セキュリティの考慮
  - API GatewayのAKIキー設定（LINE Messaging APIで生成）
  - API Gatewayのリソースポリシー設定
  - API GatewayのprodステージのWAF設定
  - API Gatewayの適切なロール設定
  - Lambdaの適切なロール設定
- SQSキューの種類選定（標準キューでよいのか？）
- SQSエラーキュー発生時の考慮（再処理ポリシー含む）
- Lambdaの同時実行数の設定
- Lambda実装時の冪等性の考慮（標準キュー使用時）

といったところでしょうか。しかし、LambdaでSQSから取得したメッセージをパースしてデータベース保存、LINE Messaging APIをコールしてユーザーに対してメッセージを送り返すなど、実際のアプリケーション要件に沿って実装するためのベースは一応構築できたのかなと思います。

## 参考
[LINE Messaging API 公式ドキュメント](https://developers.line.biz/ja/docs/messaging-api/getting-started/)
[AWS公式ドキュメント - Lambdaのgoサンプルコード](https://docs.aws.amazon.com/ja_jp/lambda/latest/dg/with-sqs-create-package.html#with-sqs-example-deployment-pkg-go)