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

Serverless Framework を利用したカスタム Slack コマンド(非同期実行版)の作り方を共有します。
2020.01.06

新年明けましておめでとうございます、本年も宜しくお願い致します! 前回の記事にて、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 コマンド作りに一度挑戦してみてはいかがでしょうか? ではでは