AWS Lambdaを使ってサーバレスでクイズができるLINE Botをつくった

More than 1 year has passed since last update.


今回つくったもの

LINEのMessaging APIを使ってクイズというか英熟語の問題を出すbotを作成しました。

タイトルにもあるようにLambdaを使ってますので、サーバレスでつくってます。

(LINE BotのAPIは当初アクセスできるIPアドレスをホワイトリストとして登録する必要があったのですが、それがオプション扱いになったので、Lambdaが使えてます)

line.gif

AWS Lambdaを使ったLINE BOTを作ってみた系の記事いっぱいあるのですが、今のMessaging API発表前に作成した記事が多いので、参考にする場合は注意してください。


このBotの機能


  • スタートで開始

  • templateメッセージで問題を送信

  • 正解、間違いを判定

ソースコードはこちら

https://github.com/ykyk1218/linebot_quize

(問題もソースコードべた書きだったり、結果判定処理がなかったりで作り途中感は満載)


準備


  • LINEビジネスアカウントを用意

  • Messaging API(Developer Trial)を申し込む

  • Lambda Functionの作成

LINEビジネスアカウントの作成からMessaging APIの申し込みまでは下記ブログが参考になります。

http://www.kazuweb.asia/aws/lambda/chatbot


LINEビジネスアカウントを用意

こちらのURLから作成します。

https://business.line.me/ja/


Messaging API

最初は混乱したんですが、LINE Bot APIが新しくなり、この辺の登録方法も若干変わってきてます。

Messaging APIは通常のMessaging APIと別にDeveloper Trialがあります。

開発でとりあえず準備する場合はこちらを選択したほうがよいです。

Developer Trialは登録できる友達の数に制限(50人まで)がありますが、開発する上では、そんなに問題ないでしょう。

LINE Business Centerから

「サービス」 > 「Messaging API」でDeveloper Trialを選択します。

messaging.png

Developer Trialの申し込みが完了したらLINE@Managerの管理画面にいきます。

line_account.png

LINE@MANAGERで

「アカウント設定」> 「Bot設定」からAPIを利用するを選択します。

APIの利用を選択したら、同じく「アカウント設定」> 「Bot設定」から自動応答メッセージ「利用しない」にしておきます。

これをしておかないとLINE@Managerで設定した応答メッセージが勝手に返信されてしまいます。

LINE _MANAGER.png


Lambda Functionの作成

AWSのコンソールからLambdaを新規に作成して、LambdaのtriggerとしてAPI Gatewayを選択します。

作成したAPI GatewayのURLをMessaging APIのLINE Developers管理画面でwebhook URLとして登録します。

LINE Developer管理画面へいき

line_account2.png

webhook urlをセット

line_dev.png


コードを書く

準備ができたらコードを書きます。

今回Lambdaではnode.jsを選択してます。

処理的に難しいところは特にないですが、コードのピックアップをしていきます:point_up:


コードを分割する

準備ができたところで、ここからゴリゴリコードを書いていくわけですが、今回作成するぐらいのものになると1ファイルで管理するにはつらいので、ファイルを分割したいです。

ファイルを分割したいばあいはブラウザ上でコードは書かずに、zipファイルをアップロードする形式にします。

今回は


  • index.js(最初に処理をうけるところ)

  • quize.js(出題する問題を決めたり、正解・不正解を判定したり)

  • lineBot.js(Messaging APIに関連する処理もろもろ)

の3つにファイルをわけました。


ログを見る

エラーの内容を確認するにはCloudWatchから確認できます。

CloudWatch > ログ

sample.png


メッセージタイプによって処理をわける

通常のテキストが送られた場合はイベントタイプがmessage

選択肢から選んだ場合はpostbackになります。

これにより処理をわけるようにします。

exports.handler = (event, context, callback) => {

if (event["events"][0]["type"] == "message") {
//通常メッセージ

}else if (event["events"][0]["type"] == "postback") {
//何かしらのボタンを押した
}
}


選択肢付のメッセージ(template message)を送信する

下記のようなものを送りたい場合

スクリーンショット 2016-12-05 1.37.45.png

JSONでPOSTするデータ

{

"type": "template",
"altText": "これはサンプルです",
"template": {
"type": "buttons",
"title": "with an eye to〜",
"text": "選択してください",
"actions": <アクションボタン>
}
}

アクションボタンは下記の形

[

{ type: "postback", label: "〜をよく見ながら", data: "1-0"},
{ type: "postback", label: "〜を横目に", data: "2-0"},
{ type: "postback", label: "〜と目が合う", data: "3-0"},
{ type: "postback", label: "〜の目的で", data: "4-1" },
]

dataのところがボタンを押したときにpostbackイベントで送信される値になるので、ここで正解が押されたか、不正解が押されたかの判定ができるように値をもっています。

(↑の例の場合だとハイフン区切りで2つ目の数字に1が入っていれば正解、0だと不正解)

postbackイベントのときのwebhookで取得できる値は下記の通り

{

"replyToken": "hogehoge",
"type": "postback",
"timestamp": 1462629479859,
"source": {
"type": "user",
"userId": "fugafuga"
},
"postback": {
"data": "4-1"
}
}

一応気をつけるポイントとしては、、、


  • アクションボタンは最大4つまで

  • labelは文字数が20文字まで

この辺間違うとBad RequestとなりHTTP Statusが400番で帰ってきます。

labelの最大文字数に気づかずはまった:sweat_smile:

詳しくは

https://devdocs.line.me/ja/


メッセージの投稿

今までは


  • Channel ID

  • Channel Secret

  • MID
    をヘッダーに設定してメッセージをPOST

Content-Type: application/json

X-Line-ChannelID: {$channel_id}
X-Line-ChannelSecret: {$channel_secret}
X-Line-Trusted-User-With-ACL: {$mid}

新しいMessaging APIでは

AuthorizationにChannel Access Tokenを設定してPOST

var postData = JSON.stringify(replyMessage)

var headers = {
'Content-Type' : 'application/json; charset=UTF-8',
'Authorization' : `Bearer {${process.env.CHANNEL_ACCESS_TOKEN}}`
}
var options = {
host: 'api.line.me',
path: '/v2/bot/message/reply',
headers: headers,
method: 'POST'
}

// APIリクエスト
var req = https.request(options, function(res){
console.log('statusCode:', res.statusCode);

res.on('request_body', function (chunk) {

});
req.on('error', function(err) {
console.log('ERROR: ' + err.message);
});
});

req.write(postData);
req.end();

var headers = {

'Content-Type' : 'application/json; charset=UTF-8',
'Authorization' : `Bearer {${process.env.CHANNEL_ACCESS_TOKEN}}`
}

この部分で環境変数に設定した値を使うようにしてます。

Lambdaの環境変数はawsコンソール上から設定できます。


面倒なところの解消方法


ローカルで動作確認

いちいちzipファイルをアップロードしなくても多少の動作確認ができます。

具体的には下記のようなコードを用意。


driver.js

var event = {

"events": [
{
"type": "message",
"replyToken": "<返信用のトークン>",
"source": {
"userId": "<ユーザーID",
"type": "user"
},
"timestamp": <タイプスタンプ>,
"message": {
"type": "text",
"id": "<メッセージID>",
"text": "<LINEに投稿されたテストしたいメッセージ>"
}
}
]
}

var context = {
invokeid: 'invokeid',
done: function(err,message){
return;
}
}

var lambda = require("./index.js");
lambda.handler(event,context, function(){ return "hoge"});


テストしたいメッセージだけちゃんと準備しておけば、あとは

node <ファイル名>実行してテストできます。

とはいえreplyでのLINEへの投稿はできないので、最低限のところだけになりますが。


簡単アップロード

ブラウザ上でコードを書かずzipでアップロードする形になるので、アップロードが少々面倒...

そこでもう少し簡単にしたいです。

Lambdaのデプロイ周りのツール系は結構色々ありそうな雰囲気でしたが、あんまり大規模すぎると逆に設定が面倒になるのでただzipに圧縮してアップロードだけをしてくれるのが欲しい、と思っていたところaws-node-utilという良さげなライブラリがありました。

製作者

http://takamints.hatenablog.jp/entry/2016/03/09/launcher-script-for-the-aws-lambda-function#setup

これを使うことで、コマンド1発でサクッとファイルをアップロードできます。