【第一回】超簡単!RubyでSlack Botを作る方法
この記事はCAMPHOR- Advent Calendar 2016の13日目の記事です
ごあいさつ
今年もAdvent Calendarの季節がやってきましたね。 せっかくなのでこれを機にTech関連の記事を残すため、はてなブログはじめました。 相変わらず見習いエンジニアの域を出ない@andoshin11 です。 今回の記事は2本立ての構成になっています。(2本目の記事は12/17に公開予定)
またまた長文になりますがお付き合いよろしくお願いします。
昨年度の記事はこちら↓↓
概要
- プログラミング初心者でも記事を読めばBotが作れるよう意識して書きました*1
- 昨年は
Googla Apps Script
を使用しましたが、今年はRuby
でSlack Botを実装します - Slack公式が提供する
Real Time Messaging API
を利用します - Botを作成し、外部サーバー(Heroku)で動かすところまでを目標にします
- rubotyやlitaのようなライブラリは利用せず、勉強も兼ねて自前での実装を目指します
- 質問あればコメントください!
目次
環境
- macOS Sierra 10.12.2 Beta
- MacBook Air (13-inch, Early 2014)*2
- ruby 2.3.1p112 (2016-04-26 revision 54768) [x86_64-darwin15]
参考
やっていくぞ!!
1. Slack API 入門
幸いなことにSlack自身がすでに豊富なAPIを整えてくれているので、その使い方さえ理解すれば比較的容易に手元のマシンからでもメッセージの送受信ができます。
1.1. APIのテスト
何はともあれ Ruby
で Slack API
を叩いて遊んでみましょう!
まずは作業ディレクトリ内に Gemfile
を作成します。
# Gemfile source 'https://rubygems.org' gem 'http' gem 'json'
次に本体である test.rb
の作成です
# test.rb require 'http' require 'json' response = HTTP.post("https://slack.com/api/api.test") puts JSON.pretty_generate(JSON.parse(response.body))
2つのファイルの用意ができたら $ bundle exec ruby test.rb
でスクリプトを実行します。
POSTしたリクエストへのレスポンスとして "ok":true
が返ってきましたね。Slack本体のサーバーと対話ができた証拠です。
1.2. Authentication
次はもう一歩踏み込んでチーム固有の情報を取得できるよう、 Authentication
を行なっていきたいと思います。
先ほどはhttps://slack.com/api/api.testというURLにリクエストを送りましたが今度はhttps://slack.com/api/auth.testというアドレスにリクエストを送ってみます。
test.rb
を編集
# test.rb require 'http' require 'json' response = HTTP.post("https://slack.com/api/auth.test") puts JSON.pretty_generate(JSON.parse(response.body))
実行
"ok":false, "error": "not_authed"
と怒られてしまいました。これはリクエスト時に Token
が設定されていないことが原因です。
Token
は、管理画面から Bots Integration
を追加することで取得できます。
発行された Token
をコピーして環境変数として保存しましょう。
POSTリクエストに params[:token]
を追加。
require 'http' require 'json' response = HTTP.post("https://slack.com/api/auth.test", params: { token: ENV['SLACK_API_TOKEN'] }) puts JSON.pretty_generate(JSON.parse(response.body))
実行
成功しました!今度は認証が認められ、加えて Token
に紐づいたチームの情報やユーザーの情報も返ってきています。
余談ですがSlack内で割り振られている team_id
は先頭が T
から始まり、 user_id
は U
から始まるという仕様のようです。
1.3. Post Message
認証が通るようになったので、実際にSlackへメッセージを投稿してみましょう。
メッセージ投稿用APIのURLはhttps://slack.com/api/chat.postMessageです。パラメータに投稿先のチャンネルや、投稿したいテキストを指定してリクエストを送ることでメッセージの投稿が可能になります。
test.rb
を編集
require 'http' require 'json' response = HTTP.post("https://slack.com/api/chat.postMessage", params: { token: ENV['SLACK_API_TOKEN'], channel: "#general", text: "こんにちは!", as_user: true, }) puts JSON.pretty_generate(JSON.parse(response.body))
as_user: true
としているのはデフォルトの Bots Integration
としてではなく、自分で Configure
したBotユーザーに投稿させたいからです。
スクリプトを実行
メッセージの投稿に成功しました!このときレスポンス内に投稿されたメッセージの関する情報が含まれているのもわかりますね(タイムスタンプ等)
ここまでで基本的な Slack API
の使い方を把握できました!
Cron
等で定期的にこのメッセージ投稿APIを叩くだけでも簡易Botが作れそうですね。
2. Real Time Messaging API
前節ではあくまで一方通行のメッセージの投稿方法を確認しました。
ですが実際の運用を想定すると常時双方向通信を監視し、ユーザーからの投稿に合わせて適切な出力を返してあげる必要があるでしょう。
ここからはSlackの備える Real Time Messaging API
と低コストで双方向通信を可能にする Web Socket
の仕組みを利用して、より実用的なBotを作っていきます。
2.1. Real Time Messaging APIとは
Slackには既に Outgoing Webhook
というユーザーの投稿内容を外部のサイトやアプリに出力してくれる仕組みがありますが、 Outgoing Webhook
の扱える情報は特定のキーワードから始まるメッセージのみです。
対して Real Time Messaging API
はその名の通り24時間チームを監視しユーザーの全ての投稿内容だけでなく、「チャンネルの新規作成」「チャンネルへのユーザーの参加/脱退」などといった1つ1つのイベントの情報すら出力してくれる仕組みです。
この仕組みを用いることでより複雑なBotの実装が可能になります。
2.2. Real Time Messaging APIでデータを取得する
まずは Gemfile
を編集して2つの新たなGemを追加します
# Gemfile source 'https://rubygems.org' gem 'http' gem 'json' gem 'faye-websocket' gem 'eventmachine'
faye-websocket
によってRubyでWeb Socketサーバーを立てることが容易になり、 eventmachine
は並列処理等を可能にしてくれるものです
早速、https://slack.com/api/rtm.startにリクエストを送り Web Socket
のURLを確認しましょう。
# test.rb require 'http' require 'json' require 'eventmachine' require 'faye/websocket' response = HTTP.post("https://slack.com/api/rtm.start", params: { token: ENV['SLACK_API_TOKEN'] }) rc = JSON.parse(response.body) puts rc['url']
実行
レスポンスの JSON
の中の['url']
にアドレスが含まれているのが分かりますね。このアドレスに対して双方向通信を確立するよう、 Web Socket
を動かしていきます。 Real Time Messaging API
からは常に情報が降ってくるので、並列処理のための Event Machine
もお忘れなく。
# test.rb require 'http' require 'json' require 'eventmachine' require 'faye/websocket' response = HTTP.post("https://slack.com/api/rtm.start", params: { token: ENV['SLACK_API_TOKEN'] }) rc = JSON.parse(response.body) url = rc['url'] EM.run do # Web Socketインスタンスの立ち上げ ws = Faye::WebSocket::Client.new(url) # 接続が確立した時の処理 ws.on :open do p [:open] end # RTM APIから情報を受け取った時の処理 ws.on :message do |event| p [:message, JSON.parse(event.data)] end # 接続が切断した時の処理 ws.on :close do p [:close, event.code] ws = nil EM.stop end end
実行!
一番上に接続が確立したことを示す [:open]
が表示されていますね。
二行目の [:message]
に含まれる {"type"=>"hello"}
というのはSlack側が接続確立時に送信してくるものです。三行目は無視して構いません。
四行目の内容ですが {"type"=>"presence_change"...}
というメッセージからも分かる通り、ユーザーのステータスが変わったことを示すイベントをキャッチしてReal Timeでこちらの Web Socket Server
に送信してくれています。
このプログラムを走らせたまま、もう少しSlack側でいろいろいじってみましょう。
みなさんお気付きのように Real Time Messaging APi
ではユーザーが投稿する時だけでなく、「入力中」であることや「リアクション」を取った事すらも細かく取得することがわかりました。これで様々な種類のBotが実装できますね!
2.3 ユーザーの投稿に合わせてメッセージを返す
ここまでで常時ユーザーの投稿内容が取得できるようになりました。
次は投稿に合わせたレスポンスを返せるよう test.rb
を修正していきます。
# test.rb ... ws.on :open do p [:open] end ws.on :message do |event| data = JSON.parse(event.data) p [:message, data] if data['text'] == 'こんにちは' ws.send({ type: 'message', text: "こんにちは <@#{data['user']}> さん", channel: data['channel'] }.to_json) end end ws.on :close do p [:close, event.code] ws = nil EM.stop end ...
ユーザーが「こんにちは」と投稿したら、「こんにちは (ユーザー名)さん」と同じチャンネルで返すようなシンプルなスクリプトです。
実行!
意図した通りに動きましたね。
1つ注意しなくてはいけないのが、Botが投稿した内容もまた RTM API
によって返ってくるということです。Bot同士の会話が一生終わらないという事態が無いように気をつけてください。
RTM API
で受信したJSON内に含まれる情報を上手く利用することで双方向対話ができるようになりました。おめでとうございます!
3. 作ったBotを公開する
ここまででLoaclのマシン上でBotが動くようになりましたが、せっかくなので外部サーバーでホスティングして常時稼働させてあげたいものです。
今回は Heroku
にデプロイすることでこの課題を解決していきます。
事前準備として heroku toolbelt
を導入するところまでは進めておいてください。
参考リンク:Heroku登録〜Macで環境整備〜お試しWebアプリを作るまで
3.1. Gitの設定
Heroku上で動かすにあたって Gemfile
に Ruby
のバージョンに関する情報を追記します。
# Gemfile source 'https://rubygems.org' ruby "2.3.1" gem 'http' gem 'json' gem 'faye-websocket' gem 'eventmachine'
また test.rb
の名前をカッコ悪いので bot.rb
に変更し、「こんばんは」というコマンドに反応するよう修正しました。
# bot.rb ... ws.on :message do |event| data = JSON.parse(event.data) p [:message, data] if data['text'] == 'こんばんは' ws.send({ type: 'message', text: "こんばんは <@#{data['user']}> さん", channel: data['channel'] }.to_json) end end ...
お決まりの $ git init
$ git add .
$ git commit -m "Initial commit"
までの流れで Git
の初期化を行います
3.2. Heroku側の設定
Heroku
のアカウントを取得し Heroku Toolbelt
の設定も済んでいる方はコマンドラインからログインができます。
$ heroku login
実行後ログイン情報を入力
初期状態では Heroku
上に Ruby
の実行環境が存在しないため、公式の提供する Heroku Buildpack for Ruby
を利用してアプリケーションの初期化を行います。
$ heroku create --buildpack https://github.com/heroku/heroku-buildpack-ruby.git
を実行
$ git push heroku master
でファイルをデプロイ!
Token
を Heroku
の環境変数として登録します
さぁ、ここまでで準備完了です。実際に Heroku
上で Ruby
のスクリプトを動かしてみましょう!
$ heroku run bundle exec ruby bot.rb
を実行
動いたー!!
おわりに(...そして予告)
お疲れ様でした。Slackの強力なAPIと Web Socket
の力でシンプルなスクリプトでもBotを作れるようになりましたね。やっている事は本当に単純なのでどんな言語でも応用が効くことでしょう。
冒頭でもお伝えしましたが今回は2本立てです。 次回の内容は、今回作成したプログラムにアップデートを加えてより実用的(????)なBotを作る手段を記事にします。 お楽しみに!
明日はsiriusjackの記事です