TL;DR
- SlackのInteracive Messageにおいて複数回メッセージのやりとりをしようと思うとステートフルにならざるをえない
- JSONなどの構造化データを使ってやりとりすることでステートレスにワークフローを実現する
Interactive Message
SlackのMessage送信ではInteractive Messageという仕組みを用いて、ただのテキストのやりとりだけではない、リッチなインタラクションを実現する手法を提供している。
具体的には以下のようなメッセージを送信してユーザからのインタラクションに使える。
Yes, interactive messages work on Enterprise Grid and on internal integrations built for your team. A Slack app is required.
ちなみにInteractive Messageを使うためにはSlack Appである必要がある。
ワークフローの実現とステートフル
これらのUIを用いることで例えば何かを選択させて確認メッセージを表示してから、Approve、Cancelを押させてワークフローを確定させる、といったことを実現できる。
この場合、以下のような3つのメッセージを送信することになる。
- 選択メニュー表示
- 選択された情報を元に確認メッセージを表示しつつ、最終確認のため、Approve, Cancelボタンを表示する
- 押されたボタンの処理を実行し、完了メッセージを表示する
このとき最終的にApproveされた場合には、当然2.
で取得した選択項目を使用して処理を実行してあげる必要がある。
またそもそもの最初のメニューを表示させるトリガーの実行時(replayとかslash message)になんらか引数を渡して処理を実行させたい場合もある。
そのような情報は2->3へのボタンクリック時のリクエストに含まれてこないので、そのようなデータをどこかに保存してワークフロー単位でステートフルに処理を実行させる必要がある。
Nodeで作られた公式サンプルをみてみると、メッセージを送信したユーザIDをkeyにして、プロセス内のObjectにデータを突っ込んでいっていることがわかる。
↓の辺。
https://github.com/slackapi/sample-message-menus-node/blob/master/lib/bot.js#L66-L77
ユーザIDだとあんまりだけど、つまりはなんらかメッセージ毎にユニークなIDを決めてそれをkeyにセッションデータをどこかに保存してあげる必要がある。
オンメモリだと再起動したらデータ消えるんでちゃんとやろうと思ったら、Redisとか使う必要がある。
JSONをValueにして常にやりとりする
いまのところSlackのMessage Object内にはPayload的なものをいれておけるfieldは存在しない。
Interactionが発生した場合は、JSONで元のメッセージと実行されたActionの情報がやってくる。
この中には、callback_idやvalueが送られてくる。
このvalueにJSONを文字列として過去のインタラクションで送られてきた情報を常につけ足していくことで、サーバサイドに保存することなしにやりとりができる。
menu1でhoge、menu2でfugaと選択した場合は、確定ボタンのvalueに{"menu1":"hoge","menu2":fuga"}
を渡してサーバサイドでパースしてあげればよい。
{
"text": "これでよいですか?",
"attachments": [
{
"callback_id": "confirmBtn",
"attachment_type": "default",
"actions": [
{
"name": "ok",
"text": "OK",
"type": "button",
"value": "{\"menu1\":\"hoge\",\"menu2\":\"fuga\"}",
"style": "primary"
},
{
"name": "cancel",
"text": "Cancel",
"type": "button",
"value": "cancel",
"style": "danger"
}
]
}
]
}
常にクライアントサイドから送信されてくるので改竄の危険性があるが、verificationTokenをチェックするのでそんなには気にする必要ないかと。
やりとりがセンシティブなら、JWTとかにして署名つきで送信すればいいんじゃないかと。