空談録

http://artfulplace.net/blogs/ からひっこしつつ

Slack APIのスレッドの扱いについて

進捗…ガハッ…

Slackにスレッド機能が追加されたらしいですね。

slackhq.com

www.itmedia.co.jp

見てたらdevcussionのSlackには来てたのでAPIではどう扱うのかのさわり的なところを書いておきます。
Slack APIを触っている人間がどれだけいるのかは知りませんが…

スレッドのリプライメッセージについて

まず、Slackのスレッドは親となる通常メッセージにリプライメッセージを子としてつけていくことで実現しています。
この親メッセージとリプライメッセージが一つのスレッドとなります。

実際のデータを見ながら追っていきましょう。
(ただし内容については架空のものです)

まずチャンネルに流れるメッセージは通常次のような形となっています

{
    "type": "message",
    "user": "Uxxxxxxxx",
    "text": "ねむい",
    "ts": "99999999.000001"
}

このメッセージにスレッド機能をつかってリプライをつけてみます。
つけたリプライは次のようなデータとなります。

{
    "type": "message",
    "user": "Uyyyyyyyy",
    "text": "ビャーーーッ",
    "thread_ts": "99999999.000001",
    "parent_user_id": "Uxxxxxxxx",
    "ts": "99999999.000333"
}

このとき、スレッドとなった親メッセージは次のような値に変化しています。

{
    "type": "message",
    "user": "Uxxxxxxxx",
    "text": "ねむい",
    "thread_ts": "99999999.000001",
    "reply_count": 1,
    "replies": [
        {
            "user": "Uyyyyyyyy",
            "ts": "99999999.000333"
        }
    ],
    "subscribed": false,
    "ts": "99999999.000001"
}

このように基本的にはスレッドを構成するメッセージにはthread_tsフィールドが存在します。
同じthread_tsを持つメッセージをスレッドとして管理できればスレッド機能への対応は可能です。

…え? スレッドメッセージがリプライかどうか見分ける方法って? フィールドがあるかないかじゃないですかね…

スレッドメッセージの取得方法は?

これは別に対応する必要がなくて、普通にx.history系で取れます。x.repliesで取ってくる必要はないです。
またRealTime Messagingでも普通に流れてくるはずです。

さらに言えばスレッドメッセージはthread_tsで基本的には実現しているので、クライアントが対応していなければありがたいことに普通にチャンネルに流れてきます。
つまるところ、スレッドしてリプライメッセージをまとめるか、そのまま表示するか程度の差でしかないです。公式SlackはUIでスレッドとして別にメッセージがあるように見せていますが、実際は他のメッセージと同じ扱いです。

クライアントを作るのが大変そうって? わかる

スレッドにポストする方法は?

これは普通にスレッドの親メッセージのtsを"thread_ts"パラメータに入れるだけです。
リプライしたいメッセージにthread_tsがついててもついてなくても関係なく、chat.postMessageでthread_tsのパラメータを指定すれば勝手にスレッドのリプライとしてやってくれます。

ところで一つ注意点があります。(1/19時点) thread_tsに不正な値を入れてもchat.postMessageは通ります。tsフォーマットさえ守ればおそらく通るはずです。
これの問題点はリプライ扱いなので公式クライアントでは見れなくなります。というかなんだこれ。

ちなみにAPIレスポンスではちゃんと拾えています。
f:id:fantasticswallow:20170119203621p:plain

im.historyによるレスポンスと公式クライアントの見え方です。少なくともuuuuというメッセージは見えません。
そして世界のどこのSlackにもなさそうな"1484824875.990999"とかいうふざけたthread_tsですが通っています。
ウーーン

reply_broadcastとかいうやつ

殺意しか湧かないですね!

Slackのスレッド機能にはもう一つ機能があります。リプライメッセージをチャンネルにも通知するという機能です。
"Also send to {チャンネル名}"とかそういうチェックがリプライの所にあると思います。それですそれ。

さっきの画像の"Also send as direct message"もそうです。
これらはAPIではreply_broadcastとして扱われます。

この機能を使う、つまりチェックを入れて投稿するにはchat.postMessageの"reply_broadcast"パラメータをtrueにするだけです。
問題はやってくるレスポンスデータですよええ…

こんなんが来ます。

{
    "channel_id": "00000000",
    "channel_type": "D",
    "timestamp": "1484826106000008",
    "is_multiteam": false,
    "attachments": [
        {
            "from_url": "https:\/\/devcussion.slack.com\/archives",
            "fallback": "[January 19th, 2017 3:21 AM] fantasticswallow: \u3042\u3042\u3042\u3042",
            "ts": "1484824875.000002",
            "author_subname": "fantasticswallow",
            "channel_id": "D1A25G6E8",
            "channel_name": "Direct Message",
            "is_msg_unfurl": true,
            "text": "\u3042\u3042\u3042\u3042",
            "author_link": "https:\/\/devcussion.slack.com\/team\/fantasticswallow",
            "author_icon": "https:\/\/avatars.slack-edge.com\/2016-01-16\/18649887269_b1c2847c8a63b9a64a8c_48.png",
            "mrkdwn_in": [
                "text"
            ],
            "id": 1,
            "footer": "2 replies"
        },
        {
            "fallback": "Foo\uff01",
            "author_subname": "fantasticswallow",
            "text": "Foo\uff01",
            "mrkdwn_in": [
                "text"
            ],
            "author_link": "https:\/\/devcussion.slack.com\/team\/fantasticswallow",
            "from_url": "https:\/\/devcussion.slack.com\/archives",
            "ts": "1484826106.000008",
            "author_icon": "https:\/\/avatars.slack-edge.com\/2016-01-16\/18649887269_b1c2847c8a63b9a64a8c_48.png",
            "id": 2
        }
    ],
    "text": "",
    "type": "message",
    "subtype": "reply_broadcast",
    "user": "U0JK7N9D5",
    "ts": "1484826106.000010"
}

ウウウーーーーン……なんだこれ…
なんかもう頑張って対応…って無理じゃね…

RealTime Messagingにおけるスレッドイベント

最後にRTMでもらえるスレッド周りのイベントを紹介しましょう。
リプライメッセージそのものは普通にメッセージと同じでやってくるので特別に用意されていません。

まずmessage_repliedっていうのがあります。
https://api.slack.com/events/message/message_replied
これは簡単で、「すでにあるメッセージがスレッドになったぜ!」って通知をしてくれます。それだけです。

次にreply_broadcastがあります
https://api.slack.com/events/message/reply_broadcast
上のreply_broadcastがポストされたら飛んできます。



というわけでSlack APIのスレッド周りのお話でした。
reply_broadcastに殺意が湧くのとUIどうしろと感が半端ないこと以外は平和でしたね!

基本的にスレッドも普通のメッセージと同じフィールドを持っているのでスレッドガン無視も一つの手かなぁとは思います。
それはそれでどうなのよという感じですが、かといって対応しようにもそう簡単に実装できるものでもない気が…

この辺で