KINTO Tech Blog
Slack

【Slack CLI】block_idが衝突してSlackにメッセージを送れない

Cover Image for 【Slack CLI】block_idが衝突してSlackにメッセージを送れない

この記事は KINTOテクノロジーズアドベントカレンダー2024 の5日目の記事です🎅🎄


KINTOテクノロジーズで my route(iOS) を開発しているRyomm a.k.a 幻のbot職人 です。
まなびぃから小ネタです。

ここではSlack CLIの話として書いていますが、内部的にはSlack CLIもSlack APIを使用していますので、Slack APIを直接使用しているケースでも当てはまるかもしれません。

背景

Slack CLIで以下のような処理を行おうとしたとき、なぜかBlock Kitのメッセージが送れないことがありました。

  1. フォームの入力でrich_text型として値を受け取る
  2. 値をrich_text型としてDataStoreに保存
  3. DataStoreから保存したいくつかのrich_text型のデータを取り出して結合し、整形する
  4. postMessageを使って作成したblockを送信する

処理の流れ

しかし、parameter_validation_failed というエラーで落ちてしまいました。

エラー

原因

送信部分でパラメータ不正のエラーが出ており、送信しようとしていたblockを調べてみると、以下のように block_id が重複していることがわかりました。

[
  {
    "type": "rich_text",
    "block_id": "xdrwH", // <- this
    "elements": [
      /* ... */
    ]
  }, {
    "type": "rich_text",
    "block_id": "xdrwH", // <- this
    "elements": [
      /* ... */
    ]
  }
]

送ろうとしているメッセージの block_id が衝突しているため、Slackにメッセージを送れないようです。

block_id

block_id とは、ブロックの一意の識別子です。
公式ドキュメントには以下のように説明されています。

A unique identifier for a block. If not specified, a block_id will be generated. You can use this block_id when you receive an interaction payload to identify the source of the action. Maximum length for this field is 255 characters. block_id should be unique for each message and each iteration of a message. If a message is updated, use a new block_id.

https://api.slack.com/reference/block-kit/blocks

block_id を指定せずにブロックを作成した場合は、自動的に block_id が生成されます。
主にインタラクティブなやり取りをしたい場合に使用します。例えばボタンが押された際にどのブロックのボタンを押されたのか?などの特定に役立ちます。

1回のメッセージ、もしくはメッセージの反復(=一連の双方向なやり取り)の中で一意である必要があります。
また、メッセージが更新された際には新しい block_id を使用します。

今回のようにフォームでの入力の場合、受け取ったrich_textに含まれる block_id は自動生成されたものになります。

さらに、冒頭の処理では入力で受け取るrich_textは別々のメッセージとして受け取っているため、 block_id も重複している可能性があります。
実際に今回の問題は block_id が衝突していることで起こりました。

ここで一句

なぜなのか 自動生成 信じてた 衝突するよ block_id

自動生成されたblock_idはUUID的な簡単には衝突しないものだと信じていたのに、衝突したなぁ...という唖然とした気持ちが表れていますね。

これは私の推測ですが、Slack側が自動生成する block_id はブロックの内容をもとに作られていると思われます。
試しに全く同じ入力を行うと、全く同じ block_id が取得できます。

エラー
rich_textにhogeと入力する

→ 毎回 RlmLN というblock_idが生成される

{
  "type": "rich_text",
  "block_id": "RlmLN",
  "elements": [
    {
      "type": "rich_text_section",
      "elements": [
        {
          "text": "hoge",
          "type": "text"
        }
      ]
    }
  ]
}

このため、全く同じ入力がある可能性がある場合、同じくらい block_id が衝突する可能性もあると考えると良いでしょう。

解決策

さて、いくつかのブロックを結合して1つのメッセージとしてSlackに送りたいとき、メッセージ内のそれぞれのblock_idは一意にする必要があります。

インタラクティブな動作を行わない場合、 block_id を保持する必要性はあまりないので削除してしまうのが一番シンプルな解決策です。
block_id が指定されていない場合はSlack側が自動で生成してくれるため、 block_id を削除したまま送信します。

これは整形を行なっているメソッドの一部です。delete演算子でオブジェクトからblock_idプロパティを削除しています。

// client.apps.datastore.query で取得した結果のitemsが引数eventに入る
// 参考: https://api.slack.com/methods/apps.datastore.query#examples
function eventMessage(event) {
  // ...
  
  event.description.forEach((description) => {
    if (description.block_id) {
      delete description.block_id // 🐈❗️
    }
    message.push(description)
  })
  
  // ...
}

これで block_id の衝突を気にせずメッセージを送ることができるようになりました!

ただし、インタラクティブなやり取りをしたいときには、オブジェクトから block_id を削除するのは良い方法ではないです。
その場合は、送信時にアプリ側で block_id を作成して割り当てると良いと思います。

おわりに

block_id が衝突してメッセージが送れないはなしでした!

Facebook

関連記事 | Related Posts

We are hiring!

【iOSエンジニア】モバイルアプリ開発G/東京

モバイルアプリ開発GについてKINTOテクノロジーズにおける、モバイルアプリ開発のスペシャリストが集まっているグループです。KINTOやmy routeなどのサービスを開発・運用しているグループと協調しながら品質の高いモバイルアプリを開発し、サービスの発展に貢献する事を目標としています。

【iOSエンジニア】モバイルアプリ開発G/大阪

モバイルアプリ開発GについてKINTOテクノロジーズにおける、モバイルアプリ開発のスペシャリストが集まっているグループです。KINTOやmy routeなどのサービスを開発・運用しているグループと協調しながら品質の高いモバイルアプリを開発し、サービスの発展に貢献する事を目標としています。