Node.js
AWS
lambda
linebot
LINEmessagingAPI

ゼロから始めるLINEBot(AWS×node.js) ②オウム返しする

前回の記事の続きです。
ゼロから始めるLINEBot(AWS×node.js) ①とりあえず動かす https://qiita.com/tenn25/items/498f768a58ba6a6de156

この記事で分かること

  • 投稿したメッセージをそのままオウム返しする
  • 自分がハマったポイント

この記事の対象者

  • いろいろと初心者(自分がそうなので)
  • MessagingAPIのような外部APIを使ったことがない
  • AWSもあんま詳しくない(アカウントは持ってるくらい)

流れ

第1回 とりあえず動かす
1.LINE Developersの登録/設定
2.実装(Lambda/node.js)
3.定期実行の設定(CloudWatch)

第2回 オウム返しの実装 (この記事)
4.ドメイン取得,DNS設定,SSL証明書の設定(Route53/ACM/SNS/S3)
5.イベントの受け取り(APIGateWay)
6.ユーザへの返信(Lambda/node.js)

第3回 ユーザ情報や投稿画像の保存 (予定)
7.ストレージ(S3やDB)との連携

4. ドメイン取得,DNS設定(※不要です)

LINE上でのメッセージ投稿や、botの友達追加などのイベントは、
WebHookに指定したURLにHTTPSのPOSTリクエストが送られます。

独自ドメインとSSL証明書を取って・・・などと考えましたが、不要でしたね。。
ユーザーが直接アクセスするAPIの場合は独自ドメインを割り当てた方がユーザーに優しいかもですが、
LINE側から叩かれるだけなので、APIのURLはAPIGateWay作成時のままで良いでしょう。

SSL証明書はAWSのACMで無料で取得できるのですが、
ドメインの認証の為にメールを受け取る必要があり、SESを使う必要があり少々面倒です。

流れは以下の通り
①ドメインを取得
②Route53にDNSレコードの設定
③SESでドメインの認証を行い、Route53にMX,TXTレコードを設定
④ACMのドメイン認証メールを受け取るための受信設定
⑤ACMの証明書リクエスト
⑥届いたメール内の認証用リンクにアクセスして認証

クラスメソッドさんの以下の記事を参考になります。
「[ACM] SSL証明書発行時のドメイン認証メールをSESで受け取ってみた」
https://dev.classmethod.jp/cloud/aws/acm-verifydomain-ses/

5. イベントの受け取り

LINEbotがイベントを受け取ると、
WebHookに指定したAPIにHTTPSのPOSTリクエストを送ります。
APIGateWayは指定のLambdaにリクエストを渡し、
LambdaからReplyAPIに対してHTTPSリクエストを投げることでbotの返信を実現しています。

(APIを使ったり作るのが初めてだったので、この流れを理解しておらず、
てっきりリクエストに対してそのままレスポンスを返すのかと思ってしまいました。。)

ではまず、APIGatewayの設定をする前に、
空でも良いのでLambdaの関数を作っておきましょう。
ここでは[LineBotReplyTest]を作りました。

その後に、APIGateWay上にAPIを作成して、
リソースからPOSTメソッドを以下のように定義しましょう。
(私はなぜかここをPUTで作ってしまい、WebHookが動かず謎に時間を無駄にしました。。)

APIGateWayでは、リクエストの中身をある程度加工してからバックエンドのサービス(今回はLambda)に渡すことができます。

スクリーンショット 2017-11-01 22.31.24.png

APIGateWayを初めて使うとおそらく必ずつまづくポイントですが、
リソースに作成しただけでは使えません。
必ず[APIのデプロイ]するのを忘れないようにしてください。
スクリーンショット 2017-11-01 21.13.02.png

作成したAPIのURLを確認して、LINE側の WebhookURLに設定します。
接続確認が成功すればOKです。

スクリーンショット 2017-11-01 23.07.43.png

6. ユーザへの返信

それでは先ほど作成したLambdaにコードを書きましょう。
node.jsのバージョンは6.10で動作確認済みです。

index.js
var https = require('https');

exports.handler = (event, context, callback) => {
    data = event.events[0];
    var replyToken = data.replyToken;
    var message = data.message;
    var txt = message.text;
    var data = JSON.stringify({
       replyToken: replyToken,
       messages: [
           {
               type: "text", 
               text: txt
           }
        ]
    });
    opts = {
        hostname: 'api.line.me',
        path: '/v2/bot/message/reply',
        headers: {
            "Content-type": "application/json; charset=UTF-8",
            "Content-Length": Buffer.byteLength(data),
            "Authorization": "Bearer " + process.env.CHANNEL_ACCESS_TOKEN
        },
        method: 'POST',
    };

    var req = https.request(opts, function(res) {
        res.on('data', function(res) {
            console.log(res.toString());
        }).on('error', function(e) {
            console.log('ERROR: ' + e.stack);
        });
    });
    callback(null, data);
    req.write(data);
    req.end();

};

APIGateWayから受け取ったリクエストの中身をそのままReplyAPIへ渡すことでオウム返しさせます。

①の記事でも書いてますが、
リクエストヘッダにContent-Lengthを忘れないようにしましょう。

また、CHANNEL_ACCESS_TOKEN は環境変数から取得するようにしています。

スクリーンショット 2017-11-01 23.26.02.png

これで完成なので、LINEからメッセージを送ってみましょう

スクリーンショット 2017-11-02 00.23.51.png

成功です!

次回はユーザの情報や投稿内容をDBに格納したり、投稿画像をS3に保存したりしましょう。

所感

結構細かいところにつまずいてしまったのですが、
まとめてみれば、なんてことは無い内容でした。