LINE MessagingAPIとAWS Lambdaで雑談BOTを作ってみた

· 6 min read
LINE MessagingAPIとAWS Lambdaで雑談BOTを作ってみた


巷ではロボットとお話ができるチャットボットが流行っているそうで。
最近は受け答えの質も上がっているもので、技術は一歩ずつ進歩しているんだなぁと感じました。

※そもそもチャットボットってなんやねん?って方はこちら。

有名なところだと、マイクロソフトのりんな
docomoのiコンシェル、iOSに搭載されているSiriもそうですかね。


ChatBotを自作してみる

という事で流行りに乗って、チャットボットを作ってみます。
今回はLINE Messaging APIとAWSのLambdaを連携させた、Line botを作ります。

構成図


構成はこのようになっています。
Docomo 雑談対話APIを Lambda から呼び出し、得られたレスポンスを
DynamoDB に保存、かつ MessagingAPI へのリクエストに使用します。

雑談対話APIのリファレンスは以下です。
https://dev.smt.docomo.ne.jp/?p=docs.api.page&api_name=dialogue&p_name=api_1

1. LINE Bot 用のアカウントを作成する

Messaging API は登録すれば誰でも無料で使用できます。
有料プランもありますが、個人的な開発なら無料で十分だと思います。

まず、以下の画面から「Developer Trail」を選択します。

LINE ビジネスアカウントを所持していない場合はまずその作成を促されます。
ビジネスアカウントが作成できたら LINE@Manager の「Bot設定」にて
「APIを利用する」を選択します。

API利用を有効化すると以下のような画面になりますが、
ここで「Webhook送信」→「利用する」、「自動応答メッセージ」→「利用しない」に設定します。

ちなみに英語版ですが、公式から手順も出てます。
https://developers.line.me/messaging-api/getting-started


2. Docomoの雑談対話APIの申請を行う

Docomo の API ですが、docomo Developer support に登録後、
マイページの「新規API利用申請へ」から登録処理を行えば API key などが発行され、
雑談対話API を利用できるようになります。

API申請後、管理画面上でアプリケーション情報が出てきますが
ここにある「API key」は後で必要になってくるので、予め控えておきます。


3.  Lambda Function を登録する

次に、Messaging API の Webhook 先として URL を用意する必要があるため、
Lambda Function を作成します。

AWS のコンパネで Lambda を選択して…

「Blank Function」をクリックします。

冒頭の構成図に記載したように、トリガーとして API Gateway を選択します。

API名は適当に命名してもらい
「デプロイされるステージ」→「prod」、「セキュリティ」→「オープン」とし、次へ。

名前と説明は適当に入力し、
今回は Node.js でコードを用意するため「ランタイム」→「Node.js 4.3」を選択します。

下の方にスクロールし、「既存のロール」は「lambda_basic_execution」とします。それ以外はデフォルトで次へ。

Function の作成が完了すると以下のように API Gateway の URL が表示されます。

この URL を Messaging API の LINE Developers 画面にて、Webhook URL として登録します。

4.DynamoDB のテーブルを作成する

次に DynamoDB のテーブルを作成します。
雑談対話API のレスポンスとして得られる context と mode を保存するためです。
この 2つの値を保持することで対話が続くようになります。

DynamoDB の「テーブルの作成」をクリックし、
適当なテーブル名とプライマリキー(mid = LINEのメッセージから得られる
userId / groupId を登録)を入力して「作成」します。
これでテーブルの準備は完了です。

 

5. Lambda Function の中身を実装する

さて、いよいよ中身の処理ですが、ざっくりと次の流れで処理を行います。

① LINEからのメッセージイベントオブジェクトを取得する
② DynamoDB内に過去の対話の context と mode があるか検索する
③ context と mode が有っても無くても Docomo API へPOSTする
④ Docomo API からのレスポンスデータを DynamoDB へ保存する
⑤ ④で得られたレスポンスのメッセージを含めて LINE Messaging API へPOSTする

コードは以下のようになります。

// load modules
var request = require('request');
var aws = require('aws-sdk');
 
exports.handler = function(event, context) {
    console.log('EVENT:', JSON.stringify(event, null, 2));
 
    // Event Object取得(LINE MessagingAPI)
    var event_data = JSON.parse(event.body);
    var reply_token = event_data.events[0].replyToken;
    var receive_message_type = event_data.events[0].message.type;
    var receive_id = '';
 
    if (!event_data.events[0].source.groupId){
        receive_id = event_data.events[0].source.userId;
    } else {
        receive_id = event_data.events[0].source.groupId;
    }
 
    // Docomo雑談API
    var APIKEY = "APIKEY";
    var docomo_options = {
        url: 'https://api.apigw.smt.docomo.ne.jp/dialogue/v1/dialogue?APIKEY=' + APIKEY,
        headers: {
            "Content-Type": "application/json"
        },
        body: '',
        json: true
    };
 
    // LINE MessagingAPI
    var line_options = {
        url: 'https://api.line.me/v2/bot/message/reply',
        headers: {
            "Content-type": "application/json; charset=UTF-8",
            "Authorization": " Bearer " + "{Channel Access Token}"
        },
        body: '',
        json: true
    };
 
    // LINEへの送信データ
    var line_body = {
      replyToken: reply_token,
      messages:[
                {
                    "type":"text",
                    "text":""
                }
            ]
    };
 
    // DynamoDB Object
    var dynamo = new aws.DynamoDB.DocumentClient();
 
    var dbparams = {};
    dbparams.TableName = "DynamoDB TableName";
 
    //会話の場合はcontextとmodeを引き継ぐ
    if (receive_message_type == 'text') {
        var receive_message = event_data.events[0].message.text;
 
        // Docomo雑談API への送信データ
        var docomo_body = {
           "utt": receive_message,
           "t": "20" // 関西弁やで
         };
 
        // 検索キー
        dbparams.Key = {
                mid: receive_id
            };
 
        // DynamoDB から Contextとmodeがあるか検索
        dynamo.get(dbparams, function(err, data) {
                if (err) {
                    console.log(err, err.stack);
                } else {
                    console.log('get item from DynamoDB.');
                    if (Object.keys(data).length > 0 && data.Item.context){
                        // Contextとmodeがあれば含めてDocomo雑談APIへPOST
                        docomo_body.context = data.Item.context;
                        docomo_body.mode = data.Item.mode;
                    }
                }
 
                // Docomo API へリクエスト
                docomo_options.body = docomo_body;
                request.post(docomo_options, function (error, response, ret) {
                    if (!error) {
                        console.log(ret);
 
                        // DynamoDB に登録するデータ
                        var UpdateDBparams = {
                            TableName: dbparams.TableName,
                            Item: {
                                "mid": receive_id,
                                "context": ret.context,
                                "mode": ret.mode
                            }
                        };
                        
                        console.log('put to DynamoDB.');
                        // DynamoDB へ登録
                        dynamo.put(UpdateDBparams, function(err, data) {
                            if (err) {
                                console.log(err, err.stack);
                            } else {
                                line_body.messages[0].text = ret.utt;
                                line_options.body = line_body;
 
                                // LINE Messaging API へリクエスト
                                request.post(line_options, function(error, response, body){
                                    if (!error) {
                                        console.log(JSON.stringify(response));
                                        console.log(JSON.stringify(body));
                                        console.log('send to LINE.');
 
                                        context.succeed('done.');
 
                                    } else {
                                        console.log('error: ' + JSON.stringify(error));
                                    }
                                });
                            }
                        });
                    }
                });
        });
    }
};

・8行目でBotに向けて送信したメッセージに含まれる replyToken を取得(これが無いと返信できません)
・21行目 の APIKEY には 雑談対話API利用申請後に発行される API key を記述
・27行目の Channel Access Token には
Messaging API の LINE developers 画面にて発行できる Channel Access Token を記述
・57行目 の DynamoDB TableName は作成したテーブル名を記述

注意!

上記のコードは Lambda のエディタに記述するのではなく、
zip ファイルとしてアップロードする必要があります。
これは、処理の最初で読み込んでいる request モジュールが Lambda の標準環境に無いためです。

以下の手順で Lambda アップロード用の zipファイルを作成します。
※ここからは適当なLinuxの検証機にて作業してください。

# nvm をインストール
$ curl -o- https://raw.githubusercontent.com/creationix/nvm/v0.29.0/install.sh | bash
 
# Node.js 4.3 をインストール
$ nvm install v4.3
 
# Lambda コード用のディレクトリを作成
$ mkdir LineBot
$ cd LineBot
 
# request モジュールをインストール
$ npm install request
 
# Lambda コードを index.js に記述
$ vi index.js
 
# zip ファイルを作成
$ zip -r LineBot.zip index.js node_modules

作成したzipファイルはSCPやらftpやらで自端末にコピーして
zipファイルをアップロードして「保存」すれば完成です!


完成図

面白い受け答えもあれば、会話が成立しないこともあります。

しりとりもできます。あまり強くないですが…。


Lambda を使えばサーバメンテを気にすることなく Bot が作れますね!

※参考サイト
http://www.kazuweb.asia/aws/lambda/chatbot