SlackのOutgoing WebhooksとAPI Gateway、Lambdaを組み合わせてBot作成

2018年12月4日

こんにちは、おてらです。

SlackのWebhooksとLambdaなどの組み合わせでチャットBotを作ることは結構流行りですね。

私もIncoming Webhooksを利用したSlackのBotなら既にいくつか作って運用してるんですが、Outgoing Webhooksを利用したものはまだ作ったことがなかったので練習がてら作ってみました。

スポンサーリンク

概要

特定のワードを含めてSlackに投稿すると、投稿内容に応じてスプラトゥーン2のステージ情報を返してくれるBotです。

ソースコードはGitHubに公開していますが、AWS側の設定については触れていないのでこっちで解説します。

Botが出来るまで

とりあえずコードを書く

コードを動かす環境はAWS Lambdaと決めていたのでとりあえずコードを用意します。PyCharmで仮想環境をサクサクっと作ります。

スプラトゥーン2のステージ・ルールの情報をAPIとして提供してくれているサイトがありますのでこちらのサイトをありがたく使わせていただきました。

PyCharmの仮想環境に rewuests モジュールをインストールしてあとはGithubに公開しているコードを書くだけです。

AWSの環境を構築する

Lambda の構築

適当な名前を付けたLambda Function を作成し、作成したコード群をデプロイします。

外部のモジュール(requests)を利用しているため、 lambda_function.py とまとめて Zip 圧縮してアップロードする方法を採ります。

venv 環境では外部のモジュールは以下のディレクトリにインストールされています。

Project_Dir/venv/Lib/site-packages

ここにあるファイル、ディレクトリ群と lambda_function.py をまとめて ZIP ファイル化します。

ZIP ファイル化したらマネジメントコンソールからアップロードします。

環境変数の項目にキーとバリューを設定すると 

os.environ['キー名']
でアクセス出来ます。

コード内に直接書きたくないシークレットキーなどはここに書くとよいでしょう。

API Gateway の作成

続いてAPI Gateway を作成します。

適当な名前を付けてAPIを作成します。

Slack の Outgoing Webhooks からは POST リクエストが送られてくるため、API Gateway で作成するメソッドは POST を指定します。

作成したら次のような画面になるので統合リクエストを選択します。

Slack から飛んでくるデータはJSON形式ではなく、 form-urlencoded のようなので、これをJSON形式に変換してあげる必要があります。

マッピングテンプレートを開き、「マッピングテンプレートの追加」ボタンを押し、リクエスト本文のパススルーの Content-Type に「application/x-www-formurlencoded」と入力し、レ点をクリックします。

テキストエディタが開くので、以下のコードを入力して保存します。

## convert HTML POST data or HTTP GET query string to JSON

## get the raw post data from the AWS built-in variable and give it a nicer name
#if ($context.httpMethod == "POST")
#set($rawAPIData = $input.path('$'))
#elseif ($context.httpMethod == "GET")
#set($rawAPIData = $input.params().querystring)
#set($rawAPIData = $rawAPIData.toString())
#set($rawAPIDataLength = $rawAPIData.length() - 1)
#set($rawAPIData = $rawAPIData.substring(1, $rawAPIDataLength))
#set($rawAPIData = $rawAPIData.replace(", ", "&"))
#else
#set($rawAPIData = "")
#end

## first we get the number of "&" in the string, this tells us if there is more than one key value pair
#set($countAmpersands = $rawAPIData.length() - $rawAPIData.replace("&", "").length())

## if there are no "&" at all then we have only one key value pair.
## we append an ampersand to the string so that we can tokenise it the same way as multiple kv pairs.
## the "empty" kv pair to the right of the ampersand will be ignored anyway.
#if ($countAmpersands == 0)
#set($rawPostData = $rawAPIData + "&")
#end

## now we tokenise using the ampersand(s)
#set($tokenisedAmpersand = $rawAPIData.split("&"))

## we set up a variable to hold the valid key value pairs
#set($tokenisedEquals = [])

## now we set up a loop to find the valid key value pairs, which must contain only one "="
#foreach( $kvPair in $tokenisedAmpersand )
#set($countEquals = $kvPair.length() - $kvPair.replace("=", "").length())
#if ($countEquals == 1)
#set($kvTokenised = $kvPair.split("="))
#if ($kvTokenised[0].length() > 0)
## we found a valid key value pair. add it to the list.
#set($devNull = $tokenisedEquals.add($kvPair))
#end
#end
#end

## next we set up our loop inside the output structure "{" and "}"
{
#foreach( $kvPair in $tokenisedEquals )
## finally we output the JSON for this pair and append a comma if this isn't the last pair
#set($kvTokenised = $kvPair.split("="))
"$util.urlDecode($kvTokenised[0])" : #if($kvTokenised[1].length() > 0)"$util.urlDecode($kvTokenised[1])"#{else}""#end#if( $foreach.hasNext ),#end
#end
}

保存したらAPIをデプロイします。ステージ名は任意のものを付けます。(私は適当に callback と付けました。)

ステージが作成されそこにデプロイされます。

確認するとAPIを呼び出すURLが発行されているので、このURLに対してPOSTリクエストを発行するとLambdaが実行されるようになります。

Slackの設定

最後にSlackの設定です。

Slackの管理画面より「App管理」⇒「カスタムインテグレーション」と進み、「Outgoing Webhook」をインストールします。設定項目はこんな感じです。

Webhookを発行できるチャンネル、トリガーとなるキーワード、POST先のURLを入力します。
トークンは、このSlackチームから発行されたものかどうかをチェックするために利用するとよいでしょう。

同じページの上部にSlackからPOSTされる際のペイロードのサンプルが記載されているのでそれを確認してみましょう。

この内容がSlackからAPI Gatewayを経由してLambdaに送られます。

 text  にトリガーワードと共に送信されたメッセージが入ってくるため、Lambdaではこれを拾う必要があります。

これをコピーしておいて、Lambdaでテストしてみましょう。

Lambdaでテスト

マネジメントコンソールにログインし、Lambdaのコンソールを開き、右上のメニューからテストイベントを作成します。

先ほどコピーしたSlackからのペイロードのサンプルをペーストしてJSON形式に加工します。

 text  に「ガチ」や「ナワバリ」と入れて保存し、テストを実行すればレスポンスが返ってきます。

テストが成功すると以下のように「成功」の表示がなされます。

これでSlackから同じようなJSONが送られてきても問題なさそうなことが確認できました。

完成

実際にSlackからトリガーワードと共に実行してみましょう。

見事にレスポンスがありました!

 

というわけで Slack と API Gateway と Lambda を組み合わせたサーバレスな簡易Botが完成しました。

これを読んだ誰かの参考になれば幸いです~。

フォローする

スポンサーリンク