slack で bot を動かしていて 「 別の bot のメッセージに反応して何かする bot 」 を書くユースケースがあります *1
ユースケースの例
例えば こんな メッセージを出す bot がいるとします *2
上記の メッセージ から UUID ( OpenStack の Server UUID ) を取り出して、別の bot で何か処理をしたい場合を考えましょう
text
を抜き出して正規表現で UUID のマッチを試みる- attachments や blocks の Hash/Array をイテレートして UUID を取り出す
といったアプローチでメッセージを扱うかと思います。一種のスクレイピングですね
非構造化されたデータを扱う問題
非構造化されたデータ を扱うのは何かと面倒で変更にも弱いですね
元のメッセージのフォーマットが変わる ( text
の内容が変わる, attachments や blocks の構造が変わる ) と bot を再実装する必要が出そうです 。人間向けの view
に依存してるコードは実装が複雑になったり、メンテナンス性が下がります。スクレイピングのコードを書いたことのある人はよくわかるかと思います。
bot が bot のメッセージを扱う場合に、構造化されたデータ ( Hash, Array ) で扱えると楽だよなぁと.... 長らく思っていたのでした。
metadata を使おう
で、改めて Slack のドキュメントを調べてみたら、メッセージに metadata
として Hash 構造のデータを付けられるのを この度 知ったのでした。
今年の4月頃に出ていたのかな? *3
metadata の例
下記のように metadata
に event_type
と event_payload
を入れてメッセージを POST できる!
{ "channel": "C23456", "text": "New teammate @Billy just joined", "metadata": { "event_type": "new_teammate", "event_payload": { "id": "TK-2132", "summary": "New teammate has been added to the channel", "description": "@Billy is a new teammate and needs to be added to the neccesary channels", "priority": "HIGH", "resource_ type": "TASK" } } }
別の bot のメッセージに反応して何かする bot を作る際、metadata を参照すると構造化されたデータで扱いやすそう (ただし メッセージを出す bot で metadata を付与してないといけないけど ... )
metadata を使った、別の bot のメッセージに反応して何かする bot 実装例
を例に作ってみました
metadata 付きのメッセージを通知する Ruby アプリ
↑ のようなメッセージを POST する単純なコードです。metadata も付けています。
#!/usr/bin/env ruby require 'slack-ruby-client' require 'dotenv' Dotenv.load params = { channel: '#dev', username: 'live-migration-notifier', text: ":arrow_forward: live-migrationが開始されました", blocks: [ { type: :section, text: { type: :plain_text, text: ":arrow_forward: live-migrationが開始されました", emoji: true } }, { type: :divider, }, { type: :context, elements: [ { type: :mrkdwn, text: ":desktop_computer: *example.com*\n:pencil2: 7ef9111c-0000-0000-0000-1717950e45cf", verbatim: true } ] }, { type: :context, elements: [ { type: :plain_text, text: ":outbox_tray: host000", emoji: true }, { type: :plain_text, text: ":inbox_tray: host001", emoji: true } ] }, { type: :divider, }, { type: :context, elements: [ { type: :plain_text, text: "req-b23c7b63-0000-0000-0000-00000000", emoji: false }, { type: :plain_text, text: "by instance-migrator", emoji: false } ] } ] } # こんな感じで 構造化したデータを突っ込める metadata = { event_type: "live_migration_started", event_payload: { source_host: "host000", dest_host: "host001", server: { name: "example.com", uuid: "7ef9111c-0000-0000-0000-1717950e45cf", }, } } # metadata には JSON を入れる params[:metadata] = metadata.to_json client = Slack::Web::Client.new(token: ENV['SLACK_API_TOKEN']) client.chat_postMessage(params)
metadata 付きのメッセージに反応する Python Bolt アプリ
先のメッセージに反応する Python Bolt アプリです。
"""_summary_ """ import os import dotenv import slack_bolt from slack_bolt.adapter.socket_mode import SocketModeHandler dotenv.load_dotenv() app = slack_bolt.App(token=os.environ["SLACK_BOT_TOKEN"]) @app.event("message") def handle_message(event, say): metadata = event.get("metadata") if metadata is None: return if metadata["event_type"] == "live_migration_started": payload = metadata["event_payload"] print(payload) server_name = payload["server"]["name"] server_uuid = payload["server"]["uuid"] say("{0} {1} started live-migration".format(server_name, server_uuid)) if __name__ == "__main__": handler = SocketModeHandler(app, os.environ["SLACK_APP_TOKEN"]) handler.start()
動作の例
二つの bot が連携して動作している例です
Python Bolt アプリでは、以下のように metadata を受け取っています
$ poetry run python bot.py ⚡️ Bolt app is running! {'source_host': 'host000', 'dest_host': 'host001', 'server': {'name': 'example.com', 'uuid': '7ef9111c-0000-0000-0000-1717950e45cf'}}
意図した通りにデータを扱うことができています。
Proof Of Concent なコードなので、特に利用価値がある実装ではありません。 metadata
を通して依存関係を持っている、ってのがポイントですね。
感想
他のユースケース
- Emoji で Reaction 付けたら、メッセージの metadata ( event_type, event_payload ) を取り出して何か操作するってコード書けそう
metadata の 技術的な疑問点
- OSS なんかで汎用的に使う bot の場合、
event_type
の命名はどう扱うのがいいのだろうか?- あまり単純な名前をつけると、別の bot と衝突するだろう
- namespace の規約があるといいようには思う
そもそもの話
- live-migration-notifier の通知を別チャンネルにも転送したいユースケースがあった
- live-migration-notifier に機能を追加するよりも、別の bot で転送機能実装したらいいかと思って 「 別の bot のメッセージに反応して何かする bot 」を書いていた
*1:何らかの理由で 元のメッセージを出す bot の実装を変えたくない/変えられない場合
*2:同僚の id:buty4649 が書いた OpenStackのLiveMigrationの通知するくん のメッセージです github.com
*3: このエントリを書いている時点では open beta の機能です