イベント駆動型slack botの実装

こんにちは、久保田です。インフラdivです。

インフラdivでは商用サービスのインフラ管理の他に、色々なオペレーション業務があります。
最近は、空いた時間でそんなオペレーション業務を自動化させて行っています。

その中でも、比較的小さな改善によく使うのが、botです。
弊社のシステム部門はslackを使っているので、botを作ってしまえば結構色々なことが便利になったり解決できたりします。

slackのbotといえば、色々な作り方がありますね。

一番簡単なのは、incomming webhookを使う方法だと思います。
エンドポイントにメッセージを投げるだけなので、非常にお手軽ですね。色々な言語でライブラリもあります。

しかしこれではメッセージを投げるだけの一方通行ですね。
できればchatの中のキーワードやユーザーのイベントをキャッチして何かをしたいですね。
そっちの方ができることの幅が広そうです。

このようなbotは大きく分けてRTM API(Real Time Messaging API)Events APIを使う2つの方法で作ることができます。
そしてRTM APIはTokenが必要なのですが、Tokenの取得に関して、レガシーなやり方とslack appを作成する2つの方法があります。
今回は、この合計3つの実装方法を確認していきます。

RTM API(レガシー)

まずはRTMのレガシーなやり方で実装します。
レガシーと言いますが、僕としてはこれが一番わかりやすく楽です。

流れは、
1. Custom Integrations で Bot を作成する。
2. 作成したBotのtokenを使い、websocketでslackとつなぎイベントを取得する。
3. websocketを使って、メッセージを送信する。 となります。

1. Custom Integrations で Bot を作成する。

https://{your slack domain}/apps/manage/custom-integrations こちらから作ることができます。
色々とBotの設定をしていき、発行されたTokenを後で使います。

f:id:AdwaysEngineerBlog:20170817205432j:plain

2. 作成したBotのtokenを使い、websocketでslackとつなぎイベントを取得する。

実装部分ですね。こちらはwebsocketを使って繋ぐ必要があります。
実装例は後ほど書いておきます。

3. websocketを使って、メッセージを送信する。

こちらはjsonで送ってあげれば大丈夫です。
こちらも後ほど書いておきます。

RTM API(slack app)

こちらはslack appを作成し、そのslack app上でbotを作りtokenを発行する方法です。
こちらの方が現在は推奨されているようです。

流れは、
1. slack appを作成する
2. bot userを作成する
3. tokenを発行する
4. RTM API(レガシー)の2,3と一緒

1. slack appを作成する

まずはslack appをこちらから作ります。

https://api.slack.com/slack-apps

2. bot userを作成する

次にslack app上にbot userを作ります。

f:id:AdwaysEngineerBlog:20170817205528p:plain

3. tokenを発行する

Install AppからAuthorizeしてtokenを発行します。

f:id:AdwaysEngineerBlog:20170817205455p:plain

4 実装

今回はRubyで実装しました。
レガシーでも、slack app経由でもtokenの取り方が違うだけなので、一緒です。
websocketでつないでおくのですが、処理が抽象化できそうだったので、gemにしました。

github.com

このライブラリを使うと、以下の処理だけでbotが作成できます。

require 'slack-rtm-bot-helper'

# channelはnilならば送信してきたchannelにメッセージを返す。
Slack::Rtm::Bot::Helper.run(token='xoxo-hgoehogheoajgjafhodhgao', channel=nil) do |data|
  # messageを作るロジック
end

Slack::Rtm::Bot::Helper.runを実行して、ブロックを渡してあげます。
slackのRTM経由でjsonが送られてくるので、それを処理し、文字列を返すとメッセージを返します。

RTMの実装はこれだけです。

https://github.com/slack-ruby/slack-ruby-client でも同じようなことができます。

Events API

さて、次はEvents APIを使う方法です。
これはRTMとは違い、websocketでつなぎません。
RTMはwebsocketの扱いが面倒くさいので、僕はこのAPIを使うのがベストなのでは、と思います。
このAPIでは、slack app上にエンドポイントを作成し、そこにイベントが送られてきます。

なので実装の流れは、

  1. slack appを作成する。
  2. エンドポイントを実装する
  3. Event Subscriptionから、エンドポイントとsubscribeするeventを登録する。
  4. webhookURLなどを使ってメッセージを送信する

という流れになります。
4は普通なので今回は割愛します。

1. slack appを作成する。

これはRTMの時と同じです。

2. エンドポイントを実装する

Eventが発火された時、受け取るためのエンドポイントが必要になります。
httpsでpostを受け取れるようにしておきます。

そして、登録時にverficationを突破する必要があるので、送られてくるchallengeパラメータを返すようにしておく必要があります。
https://api.slack.com/events/url_verification

僕はAPI Gatewayとlambdaでサクッと作りました。 とりあえず登録だけしたいので、lambdaは

exports.handler = (event, context, callback) => {
    callback(null, event["challenge"]);
};

こんな感じです。

APIGateway経由で動くようにしておきましょう。

3. Event Subscriptionから、エンドポイントとsubscribeするeventを登録する。

さて管理画面に戻り、設定をします。

f:id:AdwaysEngineerBlog:20170817205612p:plain

まずは右上のoffになっているところをonにし、 先ほど作ったエンドポイントを登録します。
そしてverfiyが通ればOKです。

そして下の方でsubscribeするイベントを登録します。
今回はmessage.channelsを使います。

https://api.slack.com/events/message.channels

f:id:AdwaysEngineerBlog:20170817205630p:plain

これで対象のチャンネルで何か喋ると

f:id:AdwaysEngineerBlog:20170817205645p:plain

ログに出てきます。

f:id:AdwaysEngineerBlog:20170817205842j:plain

良さそうですね。

他にも色々なイベントを扱えるので、色々試してみてください。

今回は以上です。