【入門】Slack のコマンドを作ってみよう!(非同期実行版)

新年明けましておめでとうございます、本年も宜しくお願い致します! 前回の記事にて、Serverless Framework を利用した、同期実行版の Slack コマンドを作成しました。 Slack のスラッシュコマンドの仕様上、3秒 以内に処理を完了し Slack コマンドへレスポンスを返却する必要があるため、今回は非同期に処理を実行する Slack コマンドを作成してみたいと思います。 前回の記事(同期実行版)については、こちらからご参照ください。

【入門】Slack のコマンドを作ってみよう!(同期実行版)

Slack コマンド作ってみた

前回同様に GitHub にソースを Push しております。下記の手順にて async ブランチを clone してご利用ください。 同期実行版では、handler.py と serverless.yml しかありませんでしたが、今回 slack.py という Slack コマンド実行者へレスポンスを返却するための Lambda 関数が定義されたファイルを用意しています。

$ git clone -b async https://github.com/yuji-shimoda/hello-slack-command.git
Cloning into 'hello-slack-command'...
remote: Enumerating objects: 10, done.
remote: Counting objects: 100% (10/10), done.
remote: Compressing objects: 100% (9/9), done.
remote: Total 10 (delta 2), reused 9 (delta 1), pack-reused 0
Unpacking objects: 100% (10/10), done.
$ cd hello-slack-command/
$ git branch
* async
$ ls -l
total 24
-rw-r--r--  1 shimoda.yuji  staff  2165 12 26 11:37 handler.py
-rw-r--r--  1 shimoda.yuji  staff  1809 12 26 11:37 serverless.yml
-rw-r--r--  1 shimoda.yuji  staff   633 12 26 11:37 slack.py

インターネットで公開されている記事を検索すると、Slack コマンドを非同期に処理する実装は SQS/SNS を利用するパターンが散見されますが、今回は AWS Step Functions を利用するパターンで実装してみました。

まずは、下記のリンクから Slack アプリを作成します。

表示されたウィンドウ内の App Name にアプリケーション名を、Development Slack Workspace に、このアプリケーションを利用する Slack のワークスペースを選択します。

1d4456f2bd58a964d6fb1b91c9b5ee19.png

定番ネタではありますが、Hello World アプリを作成します。Create App ボタンをクリックしましょう。

e776a0cafd394042e4b34d758f68919b-640x458.png

アプリケーションが作成されたら、Basic Information 画面が表示されます。まずはじめに必要な情報として、App Credentials の Signing Secret を取得します。少し下にスクロールしてください。

4a35a8d60d8c03d7233bdd3942b2b120-640x620.png

Signing Secret の横に設置された Show ボタンをクリックして、シークレット情報をコピーしておいてください。取得したシークレットは、Lambda から利用するために AWS Secrets Manager に保存します。利用している PC のターミナルを開いて、AWS CLI でシークレットを作成します。(acbde... となってる部分を、取得したシークレット情報に置き換えてください)

$ cat <<EOF > secret.json
{
    "key": "abcdefghijklmnopqrstu123456789"
}
EOF
$ aws secretsmanager create-secret \
--region ap-northeast-1 \
--name slack/secret \
--secret-string file://secret.json
{
    "ARN": "arn:aws:secretsmanager:ap-northeast-1:012345678901:secret:slack/secret-QF4hfm",
    "Name": "slack/secret",
    "VersionId": "8612e5ec-c620-4d4c-9249-xxxxxxxxxxxx"
}
$ rm -f secret.json

次に Slack コマンドからのリクエストを受け取る API Gateway と Lambda および非同期に処理を実行するための Step Functions(ステートマシン)と Slack コマンドへレスポンスを返却するための Lambda を作成していきます。前回同様デプロイ作業は、sls deploy コマンドを実行するのみです。事前の準備として serverless-python-requirements や serverless-step-functions プラグイン、boto3 等の必要なライブラリをインストールしてから実行しましょう。

$ npm install -g serverless
$ brew install pipenv
$ sls plugin install -n serverless-python-requirements
$ sls plugin install -n serverless-step-functions
$ pipenv install boto3 requests
$ sls deploy
:
Service Information
service: async-hello
stage: dev
region: ap-northeast-1
stack: async-hello-dev
resources: 15
api keys:
  None
endpoints:
  POST - https://xxxxxxxxxx.execute-api.ap-northeast-1.amazonaws.com/dev/
functions:
  hello: async-hello-dev-hello
  slack: async-hello-dev-slack

Serverless Framework により、API Gateway と Lambda 2つおよび StepFunctions のステートマシンが作成されました。POST - の後にある URL(https://xxxxxxxxxx.execute-api.ap-northeast-1.amazonaws.com/dev/)が API Gateway のエンドポイントになります。Slack アプリ側で設定を行うためメモしておきましょう。

再度、Slack アプリ側の設定ページに戻り Slack コマンドを作成していきます。

e5e66060aa6b67a5d520fe3c127d0067-640x458.png

Slash Commands をクリックします。

6c6fa8909c2a6dadd9b47315501631f0-640x213.png

Create New Command ボタンをクリックします。

6d797d797f539ef846525c647a85c149.png

必須項目を埋めていきましょう。

項目 入力値
Command コマンド名
Request URL API Gateway のエンドポイント
Short Description コマンドの簡単な説明

パラメータ入力後は、実際の利用イメージとしてプレビュー画面が更新表示されます。

298961bc3f8387d0bdcf038ca29ebdbc.png

Save ボタンをクリックしたら Slack コマンドの準備は完了です。 最後に Slack アプリをワークスペース内で有効化することで、コマンドが使用可能になります。

abb20f58a9ad825e8340cb3835ace016-640x185.png

左袖メニューの「Install App」から、「Install App to Workspace」ボタンをクリックして、権限のリクエストを許可してあげてください。

55eef1d42792a5b2fff08e670d9d3ddd.png

Slack のメッセージ作成ボックスから /hello と入力してみましょう。

4be825723d6f255409e45066e3b156f8-640x267.png

Slack コマンドにテキストを入力してみます。

d5a6518349094eb7c87cb1d5c873345b-640x35.png

送信ボタンを押すと

4f1bf75471c7753ceb93ca1ffa26f3c7.png

"Hello" + Slack コマンドの引数として「入力した文字列」の応答が返ってきました。

非同期実行版 Slack コマンドの概要

非同期実行版では、下記の流れで処理が実行されます。

  1. Slack コマンドのリクエストを API Gateway が受け取る
  2. API Gateway から Lambda が呼び出され、Slack コマンドのリクエストに含まれる response_url および入力されたテキストを取得する
  3. 入力されたテキストに "Hello " を付与し、response_url と共に、Step Functions の入力データを生成する
  4. Step Functions のステートマシンを起動する
  5. Slack コマンドのリクエストには、ステータスコード 200 と共に「しばらくお待ち下さい」というテキストを返却する
  6. ステートマシンから、Slack コマンドへのレスポンス応答用 Lambda を呼び出す
  7. ステートマシンの入力データを取得し、Slack コマンドへのレスポンスデータを生成する
  8. Slack コマンド(response_url)へ応答を返す
  9. Slack コマンドに、"Hello" + Slack コマンドの引数として「入力した文字列」が表示される

今回作成された ResponseHello ステートマシンは、下記のようにシンプルなワークフローとなります。

b2d6f6a974edccd5581b5ec2511f5cbf-640x528.png

このソースをベースに、Slack へレスポンス応答を返す slack.py 側をカスタマイズすれば色々と遊べるはずです。 なお、その他の詳細等についてはソースコードをご参照ください。

さいごに

AWS Step Functions を利用して、Slack コマンドからのリクエストを非同期に処理することにより Slack 側の制限( 3秒 )に抵触することなく様々な処理を行うことが可能になります。皆さんのアイデア次第で可能性は無限に広がりますので、Slack コマンド作りに一度挑戦してみてはいかがでしょうか? ではでは