この広告は、90日以上更新していないブログに表示しています。
RubyでSlackのボットを書くには、slack-ruby-client gemやruboty gemなどを使うのが一般的だと思います。しかし個人的には、Slackボット程度でgemを使うのは好みでないので、なるべく素のRubyだけで書くようにしています。その方法をまとめておきます。
まず、https://api.slack.com/appsで"Create New App"して、適当に設定をします。
次のYAMLを"App Manifest"に貼ってSave Changesすると一気に設定できます。
display_information: name: Sample Slack Appfeatures: bot_user: display_name: Sample Slack App always_online: trueoauth_config: scopes: bot: - app_mentions:read - chat:writesettings: event_subscriptions: request_url: https://example.com/ bot_events: - app_mention org_deploy_enabled: false socket_mode_enabled: false token_rotation_enabled: false
かんたんに説明すると、メンションされたときに通知を受け取る(app_mentions:read Scopeとapp_mention Event Subscription)、チャンネルで発言する(chat:write Scope)、という設定です。
なお、メンションではない発言も全部受け取りたかったらchannels:history Scopeとmessage.channels Event Subscriptionを足すとよいです。
chat.postMessageAPIを叩くだけです。標準ライブラリだけで簡単にできます。
require"net/http"require"json"TOKEN ="xoxb-..."# Bot User OAuth Token を埋めるCHANNEL ="CXXXXXXXX"# Channel ID を埋めるresp =Net::HTTP.post_form(URI.parse("https://slack.com/api/chat.postMessage"), {token:TOKEN,channel:CHANNEL,text:"Hello", })json =JSON.parse(resp.body,symbolize_names:true)pp json[:ok]#=> true on success, false on failure
必要な設定は2つです。
TOKENに入れるCHANNELにいれるそうしてコードを実行すれば、発言できるはずです。

ここでは単純に"Hello"というテキストを送っていますが、text: "Hello"の代わりに次のようなものを書けば、Slackのmarkdown風のマークアップができます。
{ blocks: [ { type: "section", text: { type: "mrkdwn", text: "*Hello* `world`" } } ]}このあたりについて詳しくはReference: blocksをご参照ください。Block Kit Builderでインタラクティブに構築することもできるようです。
Slackから「メンションされた」や「誰かが発言した」などのイベントの通知を受け取るには、Events APIを使います。
2024年現在、EventsAPIには2種類の通信方法があります。
両方かんたんに説明します。
sinatra gemでHTTPサーバを書きます。gemですが、sinatraは自分の心の許容リストに入っているのでOKとしています。
require"sinatra"require"json"require"openssl"SIGNING_SECRET ="..."# Signing Secret で埋めるdefverify_signature(timestamp, body, sig_actual) msg = ["v0", timestamp, body].join(":") sig_expected ="v0=" +OpenSSL::HMAC::hexdigest(OpenSSL::Digest::SHA256.new,SIGNING_SECRET, msg)OpenSSL.secure_compare(sig_actual, sig_expected)endpost"/"do body = request.body.read# Slack からの POST であることを検証する halt401,"{}"unless verify_signature( request.env["HTTP_X_SLACK_REQUEST_TIMESTAMP"], body, request.env["HTTP_X_SLACK_SIGNATURE"] ) json =JSON.parse(body,symbolize_names:true)case json[:type]when"url_verification"# Slack に URL を登録するときのイベント、challenge をそのまま返せば良い json[:challenge]when"event_callback" event = json[:event]case event[:type]when"app_mention"# メンションされた p event[:text]end""else""endend
必要な設定は1つだけです。
SIGNING_SECRETにいれるこのサーバをインターネットからアクセスできるところで実行します。実験ではngrokなど使うとよいかもしれません。
$ ruby ~/bot-server.rb...== Sinatra (v4.0.0) has taken the stage on 4567 for development with backup from WEBrick[20XX-XX-XX XX:XX:XX] INFO WEBrick::HTTPServer#start: pid=XXXXXX port=4567
そして、"Event Subscriptions"のRequest URLで、立てたサーバのURLを入力します。うまく行けば"Verified"となります。

Slackでボットに対して@Sample Slack Bot Helloなどとメンションしてみましょう。うまく行っていれば、HTTPサーバの方に発言内容が出ているはずです。
"<@XXXXXXXXXXX> Hello"XXX.XXX.XXX.XXX - - [XX/XXX/20XX:XX:XX:XX +0900] "POST / HTTP/1.1" 200 - 0.0064
Socket Modeは、RubyからWebSocketでSlackに接続してプッシュ通知を受け取る方法です。publicなHTTPサーバを用意しなくてよいので、運用は手軽かもしれません。
残念ながら、RubyでgemなしでWebSocketクライアントを使うのは大変です。いろいろ調べましたが、満足できる方法はみつけられませんでした。
調べたこと(クリックしたら詳細表示)
WebSocketプロトコルを扱うwebsocket gemはよくできていて、依存もゼロなので好みです。
ただ、実際に通信するgemとなると、eventmachineだったりfaradayだったり、巨大なgemに依存しがちです。
その点websocket-client-simple gemは、その手のものに依存しないクライアントというコンセプトは好きなのですが、細かいところで気になることが多かったです*1 。これを好みに合わせて直すくらいなら、Slackボットに特化したかんたんなものを自作するかって気分になりました。
net/httpがWebSocketをサポートしてくれたらいいのになあ。
ということで、あきらめてwebsocket gemのみに依存するslack_socket_mode_botというgemを作りました。それを使う例だけ示しておきます。
require"slack_socket_mode_bot"SLACK_BOT_TOKEN ="xoxb-..."SLACK_APP_TOKEN ="xapp-..."bot =SlackSocketModeBot.new(token:SLACK_BOT_TOKEN,app_token:SLACK_APP_TOKEN)do |data|if data[:type] =="events_api" && data[:payload][:event][:type] =="app_mention" event = data[:payload][:event] p event[:text]endendbot.run
必要な設定は3つです。
SLACK_BOT_TOKENに入れる(`xoxb-で始まるもの)SLACK_APP_TOKENにいれる(xapp-で始まるもの)あとは、このコードを実行するとSlackと通信し始めます。
$ ruby bot.rb
Slackでボットに対して@Sample Slack Bot Helloなどとメンションしてみましょう。うまく行っていれば、発言内容が通知され、pで出力されるはずです。
"<@XXXXXXXXXXX> Hello"
ちなみに、Socket ModeのWebSocketはあくまでイベント通知を受け取るためだけのものであり、これを経由してchat.postMessageなどのAPIを呼ぶことはできません。slack_socket_mode_botでは、SlackSocketModeBot#call(method, data)というAPIを呼ぶ方法もおまけで付けておきました。
なるべくgemに頼らずにRubyでSlackのボットを書く方法を説明しました。ここで述べた方法は、Ruby開発者のSlack workspaceで動くボットたちで長期間運用しています。
もちろんslack-ruby-client gemなどを使うのがかんたんだと思うし、抵抗がないならそれが賢いと思います。ただ、slack-ruby-client gemはSlack社謹製ではないので、たとえばまだSocket Modeに対応していないようです。
なぜgemに頼らないかというと、依存を減らしたいという個人的な好み(いわゆるNot Invented Here症候群かも)が最大の理由ですが、Slackはわりと頻繁にAPIを仕様変更するので、多少面倒でもSlack公式のAPIを直接叩いておくほうが長期的にはメンテナンス性が高いのでは? と思ったり思わなかったり。
OpenSSL.secure_compare やhalt 401, "{}" を使うようにした(thanks@sora_h)引用をストックしました
引用するにはまずログインしてください
引用をストックできませんでした。再度お試しください
限定公開記事のため引用できません。