Slack API - some head-scratching

So, I’m playing a bit with building a Slack app, and I’ve managed to get some stuff working, but I’m having some issues which have me perplexed.

I have defined a few methods for sending / deleting messages to a channel. The basic stuff works ok, when using form-encoding for the payload.

For example, my basic message-sending looks like this:

(defn send-msg [message]
  (let [response (http/post slack-chat-post-message-uri
                            {:form-params {:token token
                                           :channel chan-id
                                           :text message}})
        r (-> response :body (json/read-str :key-fn keyword))
        ok? (:ok r)
        ts (:ts r)
        chan (:channel r)
        error (:error r)]
    (log/info (if ok? (str "Message sent successfully ts - " ts)
                  (str "Message sending failed (" error ")")) )
    [ok? chan ts]))

Slack has a “Blocks” syntax for richer styling of the messages (it allows to use markdown, and insert interactive components, etc).

I’ve defined one such block for testing:

(def msg {:blocks [{:type "section"
                    :text {:type "mrkdwn"
                           :text "* Test!\n* List!"}}
                   {:type "divider"}
                   {:type  "section"
                    :text {:type "mrkdwn"
                           :text "Tada! :tada:"}}]})

And if I convert that to JSON, and try it out in slack’s block builder app, it works as expected.

However, when I try to send a message formatted like that, the API says the format is incorrect:

(defn send-block-msg [blocks]
  (let [response (http/post slack-chat-post-message-uri
                            {:form-params (merge {:token token
                                                  :text ""
                                                  :channel chan-id}
                                                 blocks)})
        r (-> response :body (json/read-str :key-fn keyword))
        ok? (:ok r)
        ts (:ts r)
        chan (:channel r)
        error (:error r)]
    (clojure.pprint/pprint response)
    (log/info (if ok? (str "Message sent successfully ts - " ts)
                (str "Message sending failed (" error ")")) )
    [ok? chan ts]))

The response I get (the error bit is at the bottom) looks like this:

{:cached nil,
 :request-time 368,
 :repeatable? false,
 :protocol-version {:name "HTTP", :major 1, :minor 1},
 :streaming? true,
 :http-client
 #object[org.apache.http.impl.client.InternalHttpClient 0x5a3c124 "[email protected]"],
 :chunked? false,
 :reason-phrase "OK",
 :headers
 {"access-control-expose-headers" "x-slack-req-id, retry-after",
  "referrer-policy" "no-referrer",
  "x-via" "haproxy-www-npun,haproxy-edge-fra-q105",
  "access-control-allow-headers"
  "slack-route, x-slack-version-ts, x-b3-traceid, x-b3-spanid, x-b3-parentspanid, x-b3-sampled, x-b3-flags",
  "x-accepted-oauth-scopes" "chat:write",
  "server" "Apache",
  "content-type" "application/json; charset=utf-8",
  "access-control-allow-origin" "*",
  "x-content-type-options" "nosniff",
  "content-length" "64",
  "x-slack-req-id" "937037ac948b2b222b861ff8235e7768",
  "strict-transport-security"
  "max-age=31536000; includeSubDomains; preload",
  "connection" "close",
  "pragma" "no-cache",
  "x-slack-backend" "r",
  "expires" "Mon, 26 Jul 1997 05:00:00 GMT",
  "x-oauth-scopes"
  "app_mentions:read,channels:join,chat:write,chat:write.customize,commands,emoji:read,reactions:read,reactions:write",
  "date" "Wed, 23 Sep 2020 17:22:49 GMT",
  "vary" "Accept-Encoding",
  "x-xss-protection" "0",
  "cache-control" "private, no-cache, no-store, must-revalidate"},
 :orig-content-encoding "gzip",
 :status 200,
 :length 64,
 :body "{\"ok\":false,\"error\":\"invalid_blocks_format\"}",
 :trace-redirects []}

If I send the request to a local netcat, it looks like this:

POST / HTTP/1.1
Connection: close
content-type: application/x-www-form-urlencoded
accept-encoding: gzip, deflate
Content-Length: 344
Host: localhost:6969
User-Agent: Apache-HttpClient/4.5.10 (Java/11.0.7)

token=xoxb-XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX&text=&channel=C01B0AQD1M3&blocks=%7B%3Atype+%22section%22%2C+%3Atext+%7B%3Atype+%22mrkdwn%22%2C+%3Atext+%22*+Test%21%5Cn*+List%21%22%7D%7D&blocks=%7B%3Atype+%22divider%22%7D&blocks=%7B%3Atype+%22section%22%2C+%3Atext+%7B%3Atype+%22mrkdwn%22%2C+%3Atext+%22Tada%21+%3Atada%3A%22%7D%7D^C

I have also tried to use a JSON body, but haven’t been able to get the auth to work with it…

I’m sure I’m making some n00b mistake with this, but it’s got me stumped… Any ideas would be highly appreciated!

Ok, I guess writing the whole thing out did help (sorry for using you guys for rubber-ducking!)

I tried json-encoding the value for the :blocks key, and that did the trick. So instead of defining the content like I had above, I had to define it as:

(def msg {:blocks (json/json-str [{:type "section"
                                   :text {:type "mrkdwn"
                                          :text "* Test!\n* List!"}}
                                  {:type "divider"}
                                  {:type  "section"
                                   :text {:type "mrkdwn"
                                          :text "Tada! :tada:"}}])})
1 Like