Slackのモーダルでインタラクティブなフローを実現する

Slackのモーダルでインタラクティブなフローを実現するにはこれ！って記事に出会えなかったのと、英語読み解くのに時間がかかったので、メモ用にも記事に残しておきます！

リファレンス多めですが、ざっくりの方向性がわかるだけでも時間の短縮になるかと思いますので、よければ参考にしてください。

# Slackアプリを作成し、Interactive & ShortcutをONにする
サイドバーに表示されているInteractive & ShortcutをONにして、トグルをONにします。
![スクリーンショット 2022-06-28 1.09.27.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/70770/f1413d2e-a4ba-93ca-9420-f2080a9c3ab0.png)


その際に、API Gatewayの向き先のURLを指定します。基本的にここのエンドポイント1つでリクエストによって条件分岐をして処理を実装していくイメージです。

# アプリをインストール
Shortcutの機能からモーダルでインタラクティブな機能を利用したいワークスペースにこのアプリをインストールします。

# ShortCutでアプリを立ち上げた時の処理を実装
Slackアプリの左下のプラスボタンを押すと、フォームにフォーカスされるようになり、インストール済みのアプリ名が表示されるかと思います。
アプリを選択後、下記の記事に記載されているpayloadが入ってリクエストが飛んできます。


https://api.slack.com/reference/interaction-payloads/shortcuts


正確にいうと、bodyの中で、エンコードされたpayloadが入っていますので、上記のデータはJavascriptで言うと下記の対応で取得できます。

```javascript
const requestMessageBody = queryString.parse(event.body)
const payload = JSON.parse(requestMessageBody.payload)
```

上記のリクエストから下記を実行します。

```javascript
import { App } from '@slack/bolt';

const app = new App({
  token: process.env.SLACK_BOT_TOKEN,
  signingSecret: process.env.SLACK_SIGNING_SECRET
});

app.client.views.open({
  trigger_id: payload.trigger_id
  view: // blockが入ります
})
```

ここのドキュメントを参考にしながら、各種パラメータを設定してモーダルを立ち上げます。

https://api.slack.com/methods/views.open

blockに関しては、[Slack Block Builder](https://app.slack.com/block-kit-builder/)でよしなに要素を追加することによって、Blockを作り、画面横にあるpayloadをそのままコピペして、views.openをcallすればうまく立ち上がります。

# ModalでSubmitが押された時の処理の実装
Submitボタンが押されたときは、`view_submission` がtypeに入った状態でリクエストがきます。
リファレンスはここにあるので参考にしてください。

https://api.slack.com/reference/interaction-payloads/views

# ちょっと手を加えたことをしたい時のメモ
## モーダルでsubmit以外のボタンを置いて、選択肢によってモーダルを変えたい時
ボタンを押したときは、`block_actions` がtypeに入ってリクエストが飛んできます。
その際のpayloadはこちらです。

https://api.slack.com/reference/interaction-payloads/block-actions

`view.state.values` でボタン押下時に選択されていたvalueは取得できます。

block_actionsのイベントを受け取って、モーダルの表示を変えたい場合は、下記のドキュメントを参考にすると良いかと思います。

https://api.slack.com/methods/views.update

block_actionsの際にpayloadに入ってきた、`view.id` を `views.update`のリクエストのpayloadに指定してあげると良いかと思います。

## フォームでバリデーションエラーがあり、フォームでエラーを出して再入力を促したい
APIで素直に下記のフォーマットでレスポンスを送ると、Slackがよしなに表示してくれます。
ドキュメントはこちらです。

https://api.slack.com/surfaces/modals/using#displaying_errors

これを実装する際の注意点としては、action_idを明示的に指定しておくことです。
入力時に出していたフォームの中で一意のaction_idを指定し、errorのレスポンスを返す際にそのaction_idを指定すると、当該の入力欄が赤くなる仕組みです。

response_actionでerrorを指定して、レスポンスを返すことにより、エラーの表現ができますが、response_actionに関しては、さまざまな指定方法があるので、ドキュメントを読んでみると表現の幅が広がるかと思います。


# QA
## Slash Commandでも実装できた？
slash commandでも問題ないです。
slash commandを使う際は、shortcutとリクエストメッセージボディが違うの注意してお使いください。

https://api.slack.com/interactivity/slash-commands#app_command_handling



# おまけ
## 使用した技術
serverless framework(template: aws-nodejs-typescript)

```shell
Framework Core: 2.72.3
Plugin: 5.5.4
SDK: 4.3.2
Components: 3.18.2
```

最新のバージョンは3のようですが、今回は2を使って実装しました。

serverless frameworkはここの記事を参考にしながら、セットアップするとわかりやすいです。

https://serverless.co.jp/blog/25/

serverless frameworkを使うことにより、ローカル環境で実装したものが、コマンド一つでAPI Gateway + Lambdaの構築をワンステップでやってくれます。
Lambdaの実行時に必要になるIAMなども設定ファイルに記載すれば、よしなにIAMをLambdaにアタッチしてくれてとても便利です。


記事との相違点としましては、設定ファイルがserverless.ymlではなく、serverless.tsになっていて、記述方法が若干違います。（執筆時点 20220629ではなかったはず）


指定できる内容はここのymlの定義とあまり差異がないかと思います。

https://www.serverless.com/framework/docs/providers/aws/guide/serverless.yml


今回はserverless frameworkについて深く解説しませんが、お奨めツールなので是非使ってみてください！