Techioz Blog

Sinatra は Slack のインタラクティブ コンテンツ ペイロードを解析しません

概要

Slack のインタラクティブ コンテンツを使用してボタンの応答を Sinatra アプリに送信しようとしていますが、本文が逆シリアル化されません。

Slack メッセージ ボタンを使用しようとすると、Sinatra サーバーで次のエラーが表示されます。

JSON::ParserError - unexpected token at 'payload=%7B%22type%22%3A%22block_actions%22%2C%22user%22%3A%7B%22id%22%3A%22UG0HVH1GX%22%2C%22username%22%3A%22asd%22%2C%22name%22%3A%22asd%22%2C%22team_id%22%3A%22T024GE59A%22%7D%2C%22api_app_id%22%3A%22A06F3NEL14Z%22%2C%22token%22%3A%22AYZC30arIx4CsV9AMQxU%22%2C%22container%22%3A%7B%22type%22%3A%22message%22%2C%22message_ts%22%3A%221705755611.478699%22%2C%22channel_id%22%3A%22C05GN6W4K5W%22%2C%22is_ephemeral%22%3Afalse%7D%2C%22trigger_id%22%3A%226503856512579.2152481316.74034a39fd2de763bfb820dc5060e6bb%22%2C%22team%22%3A%7B%22id%22%3A%22T024GE59A%22%2C%22domain%22%3A%22asd%22%7D%2C%22enterprise%22%3Anull%2C%22is_enterprise_install%22%3Afalse%2C%22channel%22%3A%7B%22id%22%3A%22C05GN6W4K5W%22%2C%22name%22%3A%22privategroup%22%7D%2C%22message%22%3A%7B%22type%22%3A%22message%22%2C%22subtype%22%3A%22bot_message%22%2C%22text%22%3A%22Click+Me+button%22%2C%22ts%22%3A%221705755611.478699%22%2C%22bot_id%22%3A%22B06EN64BP9B%22%2C%22blocks%22%3A%5B%7B%22type%22%3A%22actions%22%2C%22block_id%22%3A%22TMafK%22%2C%22elements%22%3A%5B%7B%22type%22%3A%22button%22%2C%22action_id%22%3A%22actionId-0%22%2C%22text%22%3A%7B%22type%22%3A%22plain_text%22%2C%22text%22%3A%22Click+Me%22%2C%22emoji%22%3Atrue%7D%2C%22value%22%3A%22click_me_123%22%7D%5D%7D%5D%7D%2C%22state%22%3A%7B%22values%22%3A%7B%7D%7D%2C%22response_url%22%3A%22https%3A%5C%2F%5C%2Fhooks.slack.com%5C%2Factions%5C%2FT024GE59B%5C%2F6516603344528%5C%2FUuYsbmvSAo56tu351G0Zl36w%22%2C%22actions%22%3A%5B%7B%22action_id%22%3A%22actionId-0%22%2C%22block_id%22%3A%22TMafK%22%2C%22text%22%3A%7B%22type%22%3A%22plain_text%22%2C%22text%22%3A%22Click+Me%22%2C%22emoji%22%3Atrue%7D%2C%22value%22%3A%22click_me_123%22%2C%22type%22%3A%22button%22%2C%22action_ts%22%3A%221705758257.494064%22%7D%5D%7D'

サーバーの先頭にこのデシリアライザーがあります。

before do
  unless request.body.read.empty?
    request.body.rewind
    @params = Sinatra::IndifferentHash.new
    @params.merge!(JSON.parse(request.body.read))
  end
end

このセクションを削除して @params を pp すると、次のようになります。

{"payload"=>
  "{\"type\":\"block_actions\",\"user\":{\"id\":\"UG0HVH1GX\",\"username\":\"asd\",\"name\":\"asd\",\"team_id\":\"T024GE59A\"},\"api_app_id\":\"A06F3NEL14Z\",\"token\":\"AYZC30arIx4CsVQxU\",\"container\":{\"type\":\"message\",\"message_ts\":\"1705755611.478699\",\"channel_id\":\"C05GN6W4K5W\",\"is_ephemeral\":false},\"trigger_id\":\"6503973207090.2152481316.8ad17fadb62def07bf15071b521180d7\",\"team\":{\"id\":\"T024GE59A\",\"domain\":\"asd\"},\"enterprise\":null,\"is_enterprise_install\":false,\"channel\":{\"id\":\"C05GN6W4K5W\",\"name\":\"privategroup\"},\"message\":{\"type\":\"message\",\"subtype\":\"bot_message\",\"text\":\"Click Me button\",\"ts\":\"1705755611.478699\",\"bot_id\":\"B06EN64BP9B\",\"blocks\":[{\"type\":\"actions\",\"block_id\":\"TMafK\",\"elements\":[{\"type\":\"button\",\"action_id\":\"actionId-0\",\"text\":{\"type\":\"plain_text\",\"text\":\"Click Me\",\"emoji\":true},\"value\":\"click_me_123\"}]}]},\"state\":{\"values\":{}},\"response_url\":\"https:\\/\\/hooks.slack.com\\/actions\\/T024GE59A\\/6501045563aa3\\/MlzRCYKujL9ETzxPJySY1v\",\"actions\":[{\"action_id\":\"actionId-0\",\"block_id\":\"TMafK\",\"text\":{\"type\":\"plain_text\",\"text\":\"Click Me\",\"emoji\":true},\"value\":\"click_me_123\",\"type\":\"button\",\"action_ts\":\"1705758480.661885\"}]}"}
2024-01-20 14:48:00 +0100 Rack app ("POST /reply" - (44.204.57.110)): #<Rack::Lint::LintError: Body yielded non-string value ["payload", "{\"type\":\"block_actions\",\"user\":{\"id\":\"UG0HVH1GX\",\"username\":\"asd\",\"name\":\"asd\",\"team_id\":\"T024GE59A\"},\"api_app_id\":\"A06F3NEL14Z\",\"token\":\"A30arIx4CsV9AM\",\"container\":{\"type\":\"message\",\"message_ts\":\"1705755611.478699\",\"channel_id\":\"C05GN6W4K5W\",\"is_ephemeral\":false},\"trigger_id\":\"6503973207090.2152481316.8ad17fadb62def07bf15071b521180d7\",\"team\":{\"id\":\"T024GE59A\",\"domain\":\"asd\"},\"enterprise\":null,\"is_enterprise_install\":false,\"channel\":{\"id\":\"C05GN6W4K5W\",\"name\":\"privategroup\"},\"message\":{\"type\":\"message\",\"subtype\":\"bot_message\",\"text\":\"Click Me button\",\"ts\":\"1705755611.478699\",\"bot_id\":\"B06EN64BP9B\",\"blocks\":[{\"type\":\"actions\",\"block_id\":\"TMafK\",\"elements\":[{\"type\":\"button\",\"action_id\":\"actionId-0\",\"text\":{\"type\":\"plain_text\",\"text\":\"Click Me\",\"emoji\":true},\"value\":\"click_me_123\"}]}]},\"state\":{\"values\":{}},\"response_url\":\"https:\\/\\/hooks.slack.com\\/actions\\/T4GE59A\\/6501045552673\\/MlzRCYKujL2zxPJySY1v\",\"actions\":[{\"action_id\":\"actionId-0\",\"block_id\":\"TMafK\",\"text\":{\"type\":\"plain_text\",\"text\":\"Click Me\",\"emoji\":true},\"value\":\"click_me_123\",\"type\":\"button\",\"action_ts\":\"1705758480.661885\"}]}"]>

ここでは Rack が lint エラーを出していますが、本体はとにかく利用可能です。 ここで何が起きてるの? response_url に送信される本文がこのように動作するのはなぜですか?どうすれば修正できますか?

ありがとう!

解決策

Slack によって送信されるペイロードのコンテンツ タイプは application/x-www-form-urlencoded です。この回答で、Sinatra がすでにこの種のコンテンツを解析していると読みました。

コンテンツ タイプが application/x-www-form-urlencoded の場合、最初のパーサーを除外することでこの問題を修正しました。

before do
  unless request.body.read.empty? || @env['CONTENT_TYPE'] == 'application/x-www-form-urlencoded'
    request.body.rewind
    @params = Sinatra::IndifferentHash.new
    @params.merge!(JSON.parse(request.body.read))
  end
end