LINEBot のバックエンドを AWS Lambda で作る

LINEBot を作ってたら、Webhook URL を指定してとあったので、慣れ親しんだ AWS Lambda で LINEBotの バックエンドを作ってみました！
その作り方や、作成中に引っかかったことを忘れないように書いておこうと思います。


# 実装概要

LINE Messaging API を使って、LINEBot を作成します。
LINEBot に対して投稿された内容を AWS Lambda に送信し、送信された内容によって、LINEBot が任意の返答を行うという処理を実装します。

また、汎用的に使うことを想定して、どの LINEBot から投稿が行われたかわかるようにしています。

AWS Lambda の コードは Node.js で記載し、バージョンは 8.10 を使っています。


# 手順

1. チャネル作成
2. チャネル設定
    1. AWS Lambda作成
    2. API Gateway設定
3. LINE送信


## チャネル作成

公式ドキュメント [Messaging APIを利用するには](https://developers.line.biz/ja/docs/messaging-api/getting-started/) を参考にして、チャネルを作成します。


## チャネル設定

次にチャネルを設定していきます。

項目はBotの内容に応じて入力します。
※"基本情報"の"利用可能な機能"で"REPLY_MESSAGE"が含まれているかは確認しておいてください。

一通り入力が完了したら、"メッセージ送受信設定"の "Webhook URL" を指定するために AWS Lambda を作成していきます。

<img width="1338" alt="Screen Shot 2019-03-26 at 15.33.47.png" src="https://qiita-image-store.s3.amazonaws.com/0/92506/4566ac4d-7c72-ee9c-c8d0-fabbe9b6c8ec.png">

上図のように "Webhook URL" は、"https" の SSL のみの対応なので、適当な localhost サーバーなどは指定できません。
AWS の API Gateway と AWS Lambda を使うと、比較的簡単に "https" の API ができるので、十分に対応できます。


### AWS Lambda 作成

LINE からのメッセージ投稿を処理する Lambda を作成します。

```javascript:index.js

// LINEアクセストークン
const LINE_TOKEN = 'XXXXXXXXXXXXXXXXXXXXXX';

// 成功時のレスポンス
const createResponse = (statusCode, body) => {
    return {
        statusCode: statusCode,
        headers: {
            "Access-Control-Allow-Origin": "*" // Required for CORS support to work
        },
        body: JSON.stringify(body)
    }
};

// メイン処理
exports.handler = (event, context) => {

    console.log(event);

    const jsonbody = JSON.parse(event.body)
    const botid = jsonbody.destination;
    const reqtext = jsonbody.events[0].message.text;
    const reptoken = jsonbody.events[0].replyToken;

    let resStr = '';

    if (reptoken == '00000000000000000000000000000000') {
        context.succeed(createResponse(200, 'Completed successfully !!'));
        console.log("Success: Response completed successfully !!");
    } else {

        if (botid == 'XXXXXXXXXX') {
            if (reqtext == 'おはよう') {
                resStr = 'いい朝ですね';
            } else if (reqtext == 'こんにちは') {
                resStr = 'お腹が減りました';
            } else if (reqtext == 'こんばんは') {
                resStr = 'お疲れ様です';
            } else {
                resStr = '^ ^';
            }
            return replyLine(reptoken, resStr).then(() => {
                context.succeed(createResponse(200, 'Completed successfully !!'));
            });
        } else {
            context.succeed(createResponse(500, 'There is no corresponding process ...'));
        }
    }
}

// LINEへのReply
function replyLine(reptoken, resStr) {
    return new Promise((resolve, reject) => {
        const request = require('request');
        const url = 'https://api.line.me/v2/bot/message/reply';

        let options = {
            uri: url,
            headers: {
                "Content-Type": "application/json",
                "Authorization": `Bearer ${LINE_TOKEN}`,
            },
            json: {
                "replyToken": reptoken,
                "messages": [{
                    "type": "text",
                    "text": resStr
                }]
            }
        };
        request.post(options, function (error, response, body) {
            if (!error) {
                console.log('Success: Communication successful completion !!');
                console.log(body);
                resolve();
            } else {
                console.log(`Failed: ${error}`);
                resolve();
            }
        });
    });
}
```

ハンドラの "event" に "LINE" からのリクエスト情報が含まれています。body 部に"送信されたメッセージ"、"Bot を一意に示すID"が含まれているので取得しておきます。

Lambda を作成する上での注意として、Webhook URL 接続確認の際、レスポンスを 2, 3 秒内で返す必要があるということです。

Webhook URL の接続確認をする際、設定画面から対象の AWS Lambda にリクエストを送信します。リクエストに対して、statusCode 200 のレスポンスを返せばいいのですが、レスポンスを返すのに時間がかかると接続失敗と判定され、設定が行えません。すぐ完了する処理ならそのまま接続確認を行ってもいいのですが、AWS Lambda から更に色々なところにリクエストを送信していたりすると、処理に時間がかかり、接続確認が失敗してしまいます。

<img width="1286" alt="Screen Shot 2019-03-27 at 11.36.08.png" src="https://qiita-image-store.s3.amazonaws.com/0/92506/124adb01-ca2c-38a0-fca1-67c7bee70210.png">

なので、あらかじめ接続確認用の分岐を作っておきます。
LINE からのリクエストには、"replyToken" という値が含まれています。
接続確認のリクエストの "replytoken" は "00000000000000000000000000000000" の値で送られてくるので、分岐を用意しておき、すぐにレスポンスを返せるように備えておきましょう。

また、LINE へのリプライにはアクセストークンが必要です。"メッセージ送受信設定"の"アクセストークン"で発行したアクセストークンを使って、リプライを行います。

### API Gateway設定

API Gatewayは、上記で作った AWS Lambda の処理を呼び出すために設定します。
今回の POST メソッドは、「Lambda プロキシ統合の使用」をチェックして作成しています。（リソースから作成する場合は、「API Gateway CORS を有効にする」をチェックするのを忘れないようにしてください。）
リソース、メソッドが作成できたら、API デプロイを行います。

デプロイをして生成された URL を LINE チャネルのメッセージ送受信設定の Webhook URL に入力します。
「接続確認」ボタンをクリックして成功したら、Webhook の設定が完了です。


## LINE送信

他の LINE チャネル設定項目の確認点としては、"LINE@機能の利用"の"自動応答メッセージ"は「利用しない」としておきます。そのように設定しておかないと、LINEBot が自動で任意の定型文を返答してくることになります。

チャネル設定画面の下部に、LINEBot を友達登録するための QR コードが表示されているので、使ってもらいたい人（ここでは自分で）に LINE アプリで QR 読み取りをしてもらい、友達になってもらいます。

友達になり、LINEBot に対し、メッセージを投稿すると AWS Lambda の処理に応じたメッセージが返ってきます。
![Screenshot_20190329-134035.png](https://qiita-image-store.s3.amazonaws.com/0/92506/b5ce6ace-4cc7-b877-19ce-8079a61a4e39.png)


# まとめ

AWS Lambda で バックエンドの API を簡単に作ることができました。色々な人が使うフロントエンドの LINEと、簡単に作れてスケーリングもかなり効く AWS Lambda の組み合わせ、是非とも試してみてください。