🎉

Cargo Lambda で Slack にカスタムダイアログを出す

に公開

内容

Slack でダイアログに入力をさせたい場合、基本的にはワークフロービルダーを使ってノーコードで作成することができる。
しかし任意のスクリプトを実行したり、複雑な内容を表示したいときには外部のエンドポイントを作成して、叩く必要がある。

ここでは Cargo Lambda (Rust の Lambda) を使ってダイアログを出すサンプルを紹介する。

https://github.com/Creanciel/ZennCargoLambdaSlackDialog

大まかなフローとしては下図のように Slash Command で Lambda にリクエストを行い、ダイアログを表示するよう Slack に Request を行う。そして最初の Slash Command にレスポンスを返す。

Slash Command の Slack のリクエストには 3秒以内 に応えなければタイムアウトになってしまう。
しかしレスポンスを返すと Lambda が終了してしまうため、その前にダイアログ表示のリクエストを送信する構成になっている。
ダイアログを返すだけであればそこまで時間はかからないので問題はない。

もしすぐにレスポンスを返せないロジックであればリクエストを受けたら非同期に別の Lambda を Invoke してレスポンスは返すという対策はある。

Slack 側の設定はエンドポイントの URL が必要なので Lambda のデプロイ後に設定する。
Lambda は slash command を押したときの POST リクエストが来ているものとする。

実装

Request

Slack から slash command での POST リクエストが飛んでくる。

飛んできた値が正規のリクエストなのか検証するべきであるがそれは別の解説記事で述べる。

ダイアログを出すためには trigger_id が欲しい。リクエストボディに入っているはずである。
リクエストボディのフォーマットは

token=XXXXXXXXXXXXXXXXXXXXXXXX&team_id=T0000000000&team_domain=XXXXXXXXXXXXXX&channel_id=C0000000000&channel_name=XXXXXXXXXXXXXX&user_id=U0000000000&user_name=XXXXXXXXXXXXXX&command=%2FXXXXXXXXXXXXXX&text=&api_app_id=A0000000000&is_enterprise_install=false&response_url=https%3A%2F%2Fhooks.slack.com%2Fcommands%2FT0000000000%2F0000000000000%2FXXXXXXXXXXXXXXXXXXXXXXXX&trigger_id=XXXXXXXXXXXXX.XXXXXXXXXXXXX.XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX

見やすくすると

token=XXXXXXXXXXXXXXXXXXXXXXXX
&team_id=T0000000000
&team_domain=XXXXXXXXXXXXXX
&channel_id=C0000000000
&channel_name=XXXXXXXXXXXXXX
&user_id=U0000000000
&user_name=XXXXXXXXXXXXXX
&command=%2FXXXXXXXXXXXXXX
&text=
&api_app_id=A0000000000
&is_enterprise_install=false
&response_url=https%3A%2F%2Fhooks.slack.com%2Fcommands%2FT0000000000%2F0000000000000%2FXXXXXXXXXXXXXXXXXXXXXXXX
&trigger_id=XXXXXXXXXXXXX.XXXXXXXXXXXXX.XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX

というようにURLエンコードされて入っている。
JSON で来てくれたほうがパースは楽であるがこうなっている。

https://github.com/Creanciel/ZennCargoLambdaSlackDialog/blob/5ad9c3683a187802f2e099d296d4ff9de15b20ff/app/src/response_dialog.rs#L13-L27

パースして trigger_id を取り出す。

Dialog

あとは JSON でダイアログを出すように Slack の https://slack.com/api/views.open に POST するだけである。

サンプルではシンプルな構成にしてあるが blocks には Block API で定義されたものが使える。

ちなみに Rust で Slack を扱うクレート slack-morphism を使ってもよいが、地味にダイアログで使うような input な型が serde_json::Value のままで定義されていないことが多いのでここでは使っていない。

{
  "trigger_id": <TRIGGER_ID>,
  "view": {
    "type": "modal",
    "title": {
      "type": "plain_text",
      "text": "Dialog"
    },
    "blocks": [
      {
        "type": "section",
        "text": {
          "type": "plain_text",
          "text": "Hello Slack"
        }
      }
    ]
  }
}

https://github.com/Creanciel/ZennCargoLambdaSlackDialog/blob/5ad9c3683a187802f2e099d296d4ff9de15b20ff/app/src/response_dialog.rs#L29-L56

CDK

CDK では Lambda に Function URL を発行してアクセスできるようにしている。

https://github.com/Creanciel/ZennCargoLambdaSlackDialog/blob/5ad9c3683a187802f2e099d296d4ff9de15b20ff/cdk/lib/cdk-stack.ts#L77-L84

サンプルを動かす場合は Lambda の環境変数に

  • SLACK_TOKEN
  • SIGNING_SECRET

が必要となる。実運用であればこれは環境変数でなく ParameterStore などから参照できるようにしておくとよい。

https://zenn.dev/creanciel/articles/cargo-lambda-parameter-store

Slack App

Slack の App では

Features > Slash Commands からエンドポイントを設定しておく。

また Lambda の環境変数として必要な値を

  • SLACK_TOKEN

    Features > OAuth & Permissions > Bot User OAuth Token

  • SIGNING_SECRET

    Settings > Basic Information > App Credentials > Signing Secret

から取得して設定する。

動作確認

まとめ

Slack に任意のダイアログを出すサンプルを提示した。
シンプルなメッセージやテキストの転送であれば ワークフロービルダー などを利用すればそれなりのものを作ることはできる。
しかし、より複雑な要件には上記のような外部連携を用いたリッチなダイアログが有効である。
ファイル(最大 10 MB)の外部転送など様々な使い方もできる。
また GAS などを使うことも可能なので正確性が求められないものならば Lambda 以外の選択肢もある。
ぜひ試していただきたい。

Discussion