初心者なのでAPI Gateway+LambdaなSlack Botで円の面積を求めてみた
整数で値をひとつ読み込み、それを半径とする円の面積を求めて表示するプログラムを作成しなさい。 円周率は3.14とし、計算結果は、小数第2位を四捨五入して小数第一位まで表示すること。
知恵袋方面に上記のプログラムをJavaScriptで書きたい人がいるという噂を聞きました。というわけで、AWSのAPI GatewayとLambdaを利用して書いてみましょう。API GatewayもLambdaもNode.jsも初心者ですが、がんばります!
元ネタ:
Slack Bot「parvati」の作成
まずはSlack Bot本体を作成します。AWS Lambdaと連携したSlack Botを作るnpmモジュールが公開されていたのでこれを利用してみます。
まずはnpm初期化と必要なnpmモジュールのインストールをします。
$ npm init -y Wrote to /Users/d-tasaki/dev/parvati/package.json: $ npm install --save lambda_bot node-env-file bluebird npm WARN package.json parvati@1.0.0 No description npm WARN package.json parvati@1.0.0 No repository field. npm WARN package.json parvati@1.0.0 No README data lambda_bot@1.0.0 node_modules/lambda_bot node-env-file@0.1.8 node_modules/node-env-file bluebird@3.3.4 node_modules/bluebird
lambda_botのモジュールからサンプルをコピーしてきます。
$ cp node_modules/lambda_bot/example_bot.js ./index.js
コピーしてきたサンプルを元に今回の要件に合わせてロジックを修正します。
// NODE_ROOT/index.js var LambdaBot = require('lambda_bot'); var env = require('node-env-file'); env(__dirname + '/.env'); var bot = new LambdaBot({ iconEmoji: process.env['SLACK_ICON_EMOJI'], userName: process.env['SLACK_USER_NAME'], channelName: process.env['SLACK_CHANNEL_NAME'], slackIncomingWebhookURL: process.env['SLACK_INCOMING_WEBHOOK_URL'] }); var round = function(x) { return Math.round(x * 10) / 10.0; }; bot.respond(/parvati:?\s*([\d\.]+)/, function(res) { const pi = 3.14; var r = parseFloat(res.match[1]); var area = round(r * r * pi); return res.reply(area); }); exports.handler = bot.createHandler();
SlackのIncoming WebHookの登録
計算した円の面積をSlackのチャンネル上で回答を投稿するためにIncoming WebHookを登録します。
SlackのApp DirectoryからIncoming WebHookを選択します。
回答を投稿するチャンネルを指定します。
WebHook URLというモノが表示されますので、これをローカルコンソールの環境変数に登録します。
私の場合は普段direnvを利用しているので、ローカルの.envrc
に登録して環境変数に反映されるようにしました。
# NODE_ROOT/.envrc export SLACK_INCOMING_WEBHOOK_URL='https://hooks.slack.com/services/XXXXXX/XXXXXXXXXXXXXXXXXXXX' export SLACK_CHANNEL_NAME='times_tasaki' export SLACK_USER_NAME='parvati' export SLACK_ICON_EMOJI=':parvati:'
AWSアカウントの開設
AWS LambdaとAPI Gatewayを利用するためにAWSアカウントを開設します。
メールアドレスとパスワードを入力します。
連絡先情報を入力します。
お支払い情報を入力します。
電話を使って本人確認します。
サポートプランを選択します。
これでアカウント開設は完了です。
Lambda関数の登録用のアクセスキーの作成
lambda_bot npmモジュールではLambda関数のデプロイ作業もやってくれる機能があります。中身を見るとどうやらaws cliを実行しているようなので、ローカルPCからaws cliでlambda create-functionなどが利用できるようにIAMユーザーを作成してアクセスキー&シークレットキーを取得します。
IAMの画面のUsersメニューを開き、Create New User
をクリックします。
ユーザー名は適当にuploaderさんとしてCreate
をクリックします。
表示されたアクセスキーとシークレットキーをメモします。
アクセスキー&シークレットキーは環境変数に登録するか、~/.aws/credentials
にdefaultプロファイルとして登録します。
先述の通りdirenvを利用していますので、.envrcに追加登録します。
# NODE_ROOT/.envrc export SLACK_INCOMING_WEBHOOK_URL='https://hooks.slack.com/services/XXXXXX/XXXXXXXXXXXXXXXXXXXX' export SLACK_CHANNEL_NAME='times_tasaki' export SLACK_USER_NAME='parvati' export SLACK_ICON_EMOJI=':parvati:' export AWS_ACCESS_KEY_ID=XXXXXXXXXXXXXXX export AWS_SECRET_ACCESS_KEY=XXXXXXXXXXXXXXXXXXXXXXXXXXXXXX export AWS_REGION=ap-northeast-1
作成したIAMユーザーにはAWSLambdaFullAccessポリシーを適用しておきます。
Lambda実行用のIAMロールの作成
Lambda関数が実行できるように権限を付与したIAMロールを作成します。
lambda_botのデプロイコマンドの中身を見るとlambda_basic_execution
という名前のロールを探しているようなので、この名前で作成します。
ロールタイプにはAWS Lambda
を選択します。
適用するポリシーにはAWSLambdaBasicExecutionRole
を選択します。
Lambda関数の登録
さていよいよLambdaにNodeモジュールを登録します。
登録するLambda関数名とハンドラーを環境変数に登録します。例によって.envrcに追加登録です。最終的に.envrcは下記の内容となりました。
# NODE_ROOT/.envrc export SLACK_INCOMING_WEBHOOK_URL='https://hooks.slack.com/services/XXXXXX/XXXXXXXXXXXXXXXXXXXX' export SLACK_CHANNEL_NAME='times_tasaki' export SLACK_USER_NAME='parvati' export SLACK_ICON_EMOJI=':parvati:' export AWS_ACCESS_KEY_ID=XXXXXXXXXXXXXXX export AWS_SECRET_ACCESS_KEY=XXXXXXXXXXXXXXXXXXXXXXXXXXXXXX export AWS_REGION=ap-northeast-1 export LAMBDA_FUNCTION_NAME='parvati_bot' export LAMBDA_HANDLER='index.handler'
これらの環境変数を利用してlambda_botのデプロイコマンドを実行します。
$ ./node_modules/lambda_bot/bin/lambda_bot deploy Archiving a lambda function. Uploading the lambda function. The lambda function named parvati_bot does not exist. Creating. Testing the lambda function.
おぉー! なんかできたっぽい。念のためaws cliで確認してみます。
$ aws lambda list-functions --region ap-northeast-1 { "Functions": [ { "Version": "$LATEST", "CodeSha256": "MpF1uD2maCj4bGsDv9GcggbjkqC7/1/YVtbWDNa7/60=", "FunctionName": "parvati_bot", "MemorySize": 128, "CodeSize": 177250, "FunctionArn": "arn:aws:lambda:ap-northeast-1:XXXXXXXXXXX:function:parvati_bot", "Handler": "index.handler", "Role": "arn:aws:iam::XXXXXXXXXXXXX:role/lambda_basic_execution", "Timeout": 3, "LastModified": "2016-03-14T12:35:37.110+0000", "Runtime": "nodejs", "Description": "" } ] }
API Gateway
次はLambda関数を発火させるAPI Gatewayを設定します。
API名はなんか適当に。
なんかよくわからないけど/
にPOSTメソッドのAPIを作成したらいいらしい。
Lambda Functionを選択肢、先ほど作成したLambda関数名を指定します。
Lambda関数呼び出すための権限付与すんぜ!!? とかなんかきかれるので渋々OKします。
するとこんな画面が表示されます。なんかAPI GatewayとLambdaが連携できたっぽい感じになりました。
ただ、この状態だとAPI GatewayにはSlackからの通知はContent-typeがapplication/x-www-formurlencoded
として渡ってきます。一方、LambdaはJSON形式を想定しているので途中で変換する必要があるとかないとか。
それを上記画面のIntegration Request
というところで指定します。
画面下部のMapping Template
というところでContent-typeのところにapplication/x-www-formurlencoded
します。すると右側になんか入力するフォームが表示されるので、そこに以下のコードを入力します。
#set($httpPost = $input.path('$').split("&")) { #foreach( $keyValue in $httpPost ) #set($data = $keyValue.split("=")) "$util.urlDecode($data[0])" : "$util.urlDecode($data[1])"#if( $foreach.hasNext ),#end #end }
初めて見る書式ですが、VTL(Velocity Template Language)とかいう言語らしいです。
APIをデプロイします。ステージ名は適当に。
Invoke URLというものが表示されるのでこれをメモっておきます。
SlackのOutgoing WebHookの登録
最後はSlack上で入力された文字列をAPI Gatewayに通知するための設定です。
SlackのApp DirectoryからOutgoing WebHookを選択します。
監視するチャンネルを選択し、URLに先ほどメモっておいたAPI GatewayのInvoke URLを入力します。
ここでTrigger WordというのにBot名を指定しておきます。これを指定していないと全部の発言がLambdaに通知されます。今回は要件にないし、AWSの無料枠での運用なので少しでも稼働を少なくさせるために指定しました。
円の面積は?
SlackでBotに向かって発言してみます。
やった!初心者だけど出来ちゃった!