Slack Appを動作させるモードには、Socket ModeとHTTP Modeの2つがある。公式ドキュメントではこちらで比較紹介されている。Socket Modeのほうは、アプリの起動時にSlackとWebsocketで接続して、以後その接続を使って通信するので、アプリ側にInboundの通信許可が不要(初回のWebsocketのOutboundがステートフルであれば)である。対してHTTP Modeではアプリ側でSlackからのリクエストをListenするWebアプリケーションの用意が必要になる。
今までSocket Modeしか使ってこなかったのでHTTP Modeも使ってみようと思い、今回試してみた。結論からいうが、HTTP Modeは面倒くさかった。 (個人の意見です。)できることならSocket Modeで全部やりたい。以下、その辺の話をいくつか。例によってDisclaimerですが この記事に書かれている内容はすべて個人の意見・見解・実験・試行の記録であり、Heroku/Slack公式の内容ではありません。
設定個所が多い
単純にSocket Modeに比べると設定しなければならない箇所が多い。例の金田一少年の画像貼りたくなる。こちらの記事でも解説されているのだが、以下4点である:
Slash CommandやらShortcut、Interactive Componentを使わないならその辺の設定は不要である。
どのフィールドもhttps://~から指定するので、全部別のところを指定することもできるため、非常に自由度が高い一方、増やせば増やすほど複雑で保守が面倒くさくなりそうではある。ただそもそも、これらはSocket Modeだと全部指定する必要がない項目なので、HTTP Modeだと単純に設定作業の負担が増えることになる。
ペイロードの形式がバラバラ
もう一つ面倒なのが、Slackからくるリクエストのペイロードの形式が、ものによってバラバラで、指定箇所ごとに構え方を変えなければならない。例えばEvent Subscriptionはapplication/jsonで来るので、Honoだとc.req.json()で処理できるが:
app.post('/event', async (c)=>{
const body = await c.req.json(); // これでOK
return c.text('ok');
});
Slash CommandやInteractive Componentのアクションはapplication/x-www-form-urlencodedなので、同じ実装で待ち構えてるとJSONパースに失敗して落ちる。なので、例えばHTTP HeaderのContent-Typeを見て処理を分岐するとか、そもそも別のリスナーにするとか考慮する必要が出てくる。各設定個所ごとに別のURLを指定できるので、後者の方が良いかもしれない(というかそもそもそういう事情があるのでURL指定箇所が複数あるのかもしれない、というポジティブシンキングをしている)。
Socket Modeだとこんな考慮は不要で(そもそもWebサーバーで待ち構える部分が不要である)、結果ペイロードの形式違いも考慮する必要がない。この辺のことを考えなくて済むのは非常に楽である。
Event Subscriptionのみ、challengeと通常のEventの通信が一緒になっている
これはEvent Subscriptionだけだが、最初にURLを指定した際に、まずそのURLが問題なく動作するのかSlack側から実際にリクエストが送信されてチェックされる。この際、送信されるchallengeパラメータの値を、なんらかの形でレスポンスするよう求められている。レスポンスの形式はtext/plainでもapplication/jsonでもなんでもいいらしいが、とにかくリクエストパラメータのchallengeを受け取ってそのままレスポンスせよ、と求められている。これにより設定URLのVerificationを行っている。Let's EncryptのHTTP Challengeに似ている。(詳しくはここに書いてある)。
ここでチェックがOKになると、メッセージの送信などのイベントのペイロードが指定したURLに向けて送信されるようになるが、この際の送信先URLがチェック用URLと同一のものなので、受け手側の事情からすると、「challengeのレスポンス(最初のチェック通過用)」と「通常のEvent発生時のペイロードの受信・処理」を同じ処理内に同居せざるを得なくなる。後者(「通常のEvent発生時のペイロードの受信・処理」)のほうにはchallengeがリクエストパラメータに含まれないため、challengeを見てレスポンスするというチェック通過用の実装は、運用開始後は死にロジックになってしまう。個人的に作ったサンプルコードだと以下のような実装になった:
app.post('/slack', async (c)=>{
const cloneReq = c.req.raw.clone();
const body = await cloneReq.json();
const challenge = body['challenge'];
if (challenge) return c.text(challenge, 200); // for initial setup
return await runSlackApp(c);
});
例えばSlash Commandなどは、設定時点で送信先URLのチェックなんかしないので、設定前に審査が入るこの形式はEvent Subscriptionだけであり、この分だけ妙に重厚である。色々背景はありそうだがこの部分には少し気を付けておく必要があると感じた。
というわけで個人的にはHTTP ModeよりSocket Modeのほうが好きである。ただslack-edgeを使ってSocket Modeを起動すると"WARNING: The Socket Mode support provided by slack-edge is still experimental and is not designed to handle reconnections for production-grade applications. It is recommended to use this mode only for local development and testing purposes."というメッセージが出力される。少なくともslack-edgeのSocket Modeは本番環境で使わない方がよさそうである。
似たような話で、Socket Modeは、Slack Marketplaceを通じて一般配布を想定したSlackAppには適用できなくなっている(ここに書いてある。まあそりゃそうか)
なのでSocket Modeは設定が簡単ではあるもののローカルや特定の一部環境などで動作させるユースケースでしか使用できない、ということになりそうである。



