LINE Messaging API と AWS Lambda で LINE BOT を作ってみた
手段「これってもしかして」
目的「私たち」
「「入れ替わってる〜〜〜〜?」」— みすま (@msmt9) 2016年9月22日
これ、ほんとよく入れ替わりますよね。入れ替わらなくなる気配がありません。今回も(手段と目的が)バッチリ入れ替わってます。
AWS の Lambda を使ってみたいがために LINE の Bot を作ってみたんですね。といってもまずは、はろーわーるどです。後半では対話可能な Bot も作ってみたので、その具体的な方法を記載しておきます。
(※ちなみに上記Twitterは私のアカウントではありません。)
クラウド界隈では近頃、サーバレスアーキテクチャ という話題がちょくちょくありまして、乱暴に言うならば「馬鹿みたいにEC2を立てるのはやめて、お安く賢くビジネスに集中しようぜ」という話のようです。とはいえ、本当の意味での「サーバレス」ではなくて、要するに自前で保守しないといけないサーバはできるだけ無くして、AWSでいうところのマネージドサービスを活用していきましょうよ、ということだと今は理解してます。
で、よくそのサーバレスアーキテクチャ で例に挙がるのが Lambda とよばれるサービスを組み合わせたもの。Lambda というのはイベントドリブンな云々…(詳細はこちら) つまり、何かしらのイベントを検知して処理をさせたいときに Lambda ならソースコードを配置するだけでいいよ、という PaaS です。
いちいち EC2 立てて環境をごにょごにょしなくても済むし、コストは EC2 1台分より安く済みそうだし、なによりスマートでかっこよさげだったので使ってみたくなったのです。そんなときにちょうど先日 LINE が Chatbot用の新しい API を公開したとの記事を見て、LINE の Bot を作ってみることにしました。
とりあえず Messaging API を使って返答させてみようということで、以下の構成で動かします。Messaging API からのイベント通知を Webhook させて API Gateway で受け取って Lambda が動くイメージです。
1. LINE Bot 用のアカウントを作成する
以前、LINE BOT API Trial という APIサービス が開始されていましたが、こちらの利用は先着登録順で誰もが利用できるものではなかったようです。先日公開された Messaging API は登録すれば誰でも無料で使用できます。有料プランもありますが、個人的な開発なら無料で十分だと思います。
まず、以下の画面から「Developer Trail」を選択します。
https://business.line.me/ja/services/bot
LINE ビジネスアカウントを所持していない場合はまずその作成を促されます。ビジネスアカウントが作成できたら LINE@Manager の「Bot設定」にて「APIを利用する」を選択します。
API利用を有効化すると以下のような画面になりますが、ここで「Webhook送信」→「利用する」、「自動応答メッセージ」→「利用しない」に設定します。
ちなみに登録方法は以下に説明があります(英語ですが)。
https://developers.line.me/messaging-api/getting-started
2. Lambda Function を登録する
次に、Messaging API の Webhook 先として URL を用意する必要があるため、Lambda Function を作成します。
AWS のコンパネで Lambda を選択して「Create a Lambda Function」をクリックします。BluePrint の雛形一覧の画面になりますが「Skip」します。
冒頭の構成図に記載したように、Trigger として API Gateway を選択します。「Deployment stage」→「prod」、「Security」→「Open」とします。
Function名は適当に入力し、今回は Node.js でコードを用意するため 「Runtime」→「Node.js 4.3」を選択します。「Existing role」は既存の「lambda_basic_execution」とします。それ以外はデフォルトで。
Function の作成が完了すると以下のように API Gateway の URL が表示されます。
この URL を Messaging API の LINE Developers 画面にて、Webhook URL として登録します。
以上で事前準備は完了です。
3. Lambda Function の中身を実装する
最後に、登録した Lambda Function の中身(Node.js)を書いていきます。
Messaging API では、メッセージを相手に送りつける Push Message と相手のメッセージに返答する Reply Message 以外に、ボタンや画像を送信することもできるようですが、今回は Reply のみを扱います(Lambdaを使ってみることが主眼なので)。
Messaging API のリファレンスは以下になります。
https://devdocs.line.me/ja/
では、以下のコードを Lambda の Code に記述して「Save」します。… ハイ!Bot 完成しました!終わりです。何でもいいのでメッセージを Bot に送信すると Bot が決まったセリフを返答する、というものです。それだけです…。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 |
// load modules var https = require('https'); exports.handler = function(event, context) { console.log('EVENT:', JSON.stringify(event, null, 2)); var event_data = JSON.parse(event.body); var reply_token = event_data.events[0].replyToken; // Request Body var request_body = JSON.stringify({ replyToken: reply_token, messages:[ { "type":"text", "text":"はろーわ~るど" } ] }); // Request Headers var send_options = { host: 'api.line.me', path: '/v2/bot/message/reply', headers: { "Content-type": "application/json; charset=UTF-8", "Authorization": " Bearer " + "{Channel Access Token}" }, method: 'POST' }; // APIリクエスト var req = https.request(send_options, function(res){ res.on('request_body', function (chunk) { console.log(res.statusCode + chunk.toString()); }); req.on('error', function(err) { console.log('ERROR: ' + err.message); }); }); req.write(request_body); req.end(); }; |
・8行目でBotに向けて送信したメッセージに含まれる replyToken を取得(これが無いと返信できません)
・27行目の Channel Access Token には Messaging API の LINE developers 画面にて発行できる Channel Access Token を記述
こんな感じになります↓ 白い吹き出しが Bot です。
(事前に Bot を友達登録しておきましょう)
こんな具合に Lambda で Bot を動かすことができました。Lambda は AWSのマネージドサービスなので、ユーザが管理するのはコードとアクセス権限くらいです。スケーリングや対障害性はAWSがよしなにやってくれるようです。Cloud Watch でのモニタリング や log の取得も可能です。今回は Lambda の Trigger を API Gateway としましたが、対応する他のAWSサービスを指定すれば AWS内のイベントに対しても Lambda で処理できますね。たぶん。
Ex. せっかくなので Bot をもう少し賢くしてみる
Lambda上でもう少しだけ複雑なことをやってみたいので、Docomo 雑談対話API を Bot に組み込んで会話できるようにしてみました。それにあたってはこちらのクラスメソッドさんの記事を参考にさせていただきました。(が、そのまま書いてもピクリともしなかったので所々修正してます ^^; )
雑談対話APIのリファレンスは以下です。
https://dev.smt.docomo.ne.jp/?p=docs.api.page&api_name=dialogue&p_name=api_1
構成はこのようになっています。Docomo 雑談対話APIを Lambda から呼び出し、得られたレスポンスを DynamoDB に保存、かつ MessagingAPI へのリクエストに使用します。
まず Docomo の API ですが、docomo Developer support に登録後、マイページの「新規API利用申請へ」から登録処理を行えば API key などが発行され、雑談対話API を利用できるようになります。
次に DynamoDB のテーブルを作成します。雑談対話API のレスポンスとして得られる context と mode を保存するためです。この 2つの値を保持することで対話が続くようになります。
DynamoDB の「テーブルの作成」をクリックし、適当なテーブル名とプライマリキー(mid = LINEのメッセージから得られる userId / groupId を登録)を入力して「作成」します。これでテーブルの準備は完了です。
さて、いよいよ中身の処理ですが、ざっくりと次の流れで処理を行います。
① LINEからのメッセージイベントオブジェクトを取得する
② DynamoDB内に過去の対話の context と mode があるか検索する
③ context と mode が有っても無くても Docomo API へPOSTする
④ Docomo API からのレスポンスデータを DynamoDB へ保存する
⑤ ④で得られたレスポンスのメッセージを含めて LINE Messaging API へPOSTする
コードは以下のようになりました。(ちょっとコールバックが…)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 |
// 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)); } }); } }); } }); }); } }; |
・21行目 の APIKEY には 雑談対話API利用申請後に発行される API key を記述
・57行目 の DynamoDB TableName は作成したテーブル名を記述
ただここで、上記のコードを Lambda に配置する際に少し注意点があります。
Lambda の IAMロールに DynamoDB へのアクセスを許可するポリシーをアタッチする必要があります。また、上記のコードは Lambda のエディタに記述するのではなく、zip ファイルとしてアップロードする必要があります。これは、処理の最初で読み込んでいる request モジュールが Lambda の標準環境に無いためです。
以下の手順で Lambda アップロード用の zipファイルを作成します。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
# 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ファイルをアップロードして「Save」すれば完成です!
うまく会話が弾めばいい具合にムカつく返しをしてきます。(会話が成立しないこともしばしばありますが…)
Lambda を使えばサーバメンテを気にすることなく Bot が作れますね!社内で ChatOps のひとつとして導入してみたくなってきました。
こちらのらくがきもどうぞ。
関連記事
-
Oracle Cloud で仮想サーバを立ててみた
クラウドのインフラサービス(IaaS)というと、普段はやはり Amazon Web Services
-
AWS認定ソリューションアーキテクトになりました(アソシエイト)
久しぶりに資格試験を受けてきました。たぶん6年振りくらい。無事に合格しました。(内心ホッとし
-
Serverless Framework を使えばサーバレス開発がもっと楽しくなりそう
前回、AWS Lambda を使って LINE の Bot を作ってみましたが、実は正直、L