明白かつ現在のSlack：Slackの各種イベントを監視するbotを気軽に作れるようにした

この記事は**[『Slack Advent Calendar 2019』](https://qiita.com/advent-calendar/2019/slack)**24日目の記事です。

## :rocket: 3行でおｋ
- Slack上でのチャンネル作成などのイベントを検知するbotが欲しい
- 手軽にRubyでbotが作れる mobb というライブラリを使いたい
- [mobb上で簡単に作れるreppハンドラ](https://github.com/rutan/repp-heartful_slack)をつくったよ

## :muscle: モチベーション

※このセクションはポエムなので、興味がない方とQiitaの運営の方は読み飛ばしてください

### Slackで何が起こっているか、知ってますか？

Slackはとても便利な ~~ゲームプラットフォーム~~ チャットツールです。
複数拠点をまたいだやり取りや、リモートワークなどを支える
重要なコミュニケーションツールとしての地位を確立しつつあります。

一方、会社や学校、大規模コミュニティなど多くの人が所属するチームで使うようになると
全体を見渡す、どこで何が起きているのかを認識することは難しくなります。

ですが、Slackは多くのAPIを提供しています。
特に [Real Time Messaging API](https://api.slack.com/rtm) や [Events API](https://api.slack.com/events-api) を利用することで、
Slack上で起きたイベント（メンバーの追加、チャンネルの追加など）を認識することができます。
![bot.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/25109/f1fb5d2a-ed01-8579-1fd1-8afec24371f2.jpeg)
<small>※botがReal Time Messaging APIを受け取り、その内容を元に人間にわかりやすく発言している図<br>新しいメンバー・チャンネル・絵文字の追加や削除などが通知されます</small>

### ただし作るのは微妙に面倒くさい

SlackのAPIは程よく難解です<sup>[要出典]</sup>
Events APIを利用するためにはHTTPSのエンドポイントを用意してメッセージを受け取る必要があります。
ちょっと10分くらいいじってみよ～という気持ちで触るにはハードルが高いですよね……。

最も手軽なのは [Bot integration](https://app.slack.com/apps/A0F7YS25R) での Real Time Messaging API の利用ですが、
Slack初心者が挑むとAPIレスポンスの反撃を受けることがあります。

### sinatraライクなmobbを利用したい

Rubyでしっかりとしたbotを作るのであれば [Ruboty](https://github.com/r7kamura/ruboty) という選択肢があります。
すでに様々なアダプタが提供されているため、多くのチャットツールでも利用できます。

一方、僕は別にお仕事でbotを作るわけではなく、
サクっと書いてサクっと動かしたい気持ちが強いです。

そこで [Mobb](https://github.com/kinoppyd/mobb) を使います。
Mobb は [Sinatra](http://sinatrarb.com) にインスパイアされて作られたbotフレームワークで、ほぼ Sinatra のような書き味でbotを作ることができます。

```ruby
require 'mobb'
set :service, 'slack'

on 'ping' do
  'pong !'
end
```
5秒で書けそう！

## :robot: MobbでSlack監視botをつくる

### Reppハンドラ？

Mobb で作られたbotの通信処理は [Repp](https://github.com/kinoppyd/repp) の中に定義されています。
Repp はいわゆる Rack のbotフレームワーク版のようなものとのことです。

- 秒でBotを作れるMobb、その裏にあるReppって何者？
    - http://tolarian-academy.net/what-is-repp/

Reppは標準でSlackハンドラを提供しているのですが、
あくまでチャット用途のものであり、通常のメッセージしか対象ではありません。

ところで、実は Mobb/Repp の作者の方が既にSlack監視botを作成して公開しています。

- slack-watcher
    - https://github.com/kinoppyd/slack-watcher

この中身を見てみると、このbot専用にイベントだけを通知するSlackハンドラを定義して、
それを元にメッセージを送信しているようです。

これをそのままパクるのもありですが、どうせなら普通の会話もイベント監視もできる
便利でカワイイbotを作れたら嬉しいので、今回はイベントにも対応したSlackハンドラを作ってみました！

### repp-heartful_slack をつくりました

今回つくった、ReppのSlackハンドラです。
Repp標準のSlackハンドラとの違いは、通常メッセージだけでなくイベント通知も取得できるようになっています。

- https://rubygems.org/gems/repp-heartful_slack
- https://github.com/rutan/repp-heartful_slack

このハンドラを利用すると、以下のような形で書くことができます。

```ruby
require 'mobb'
require 'repp/heartful_slack'

set :service, 'heartful_slack'

# 通常メッセージだけを絞り込むフィルタ
set :on_message do |_|
  condition { @env.is_a?(::Repp::HeartfulSlack::MessageReceive) }
end

# イベントメッセージだけを絞り込むフィルタ
set :on_event do |_|
  condition { @env.is_a?(::Repp::HeartfulSlack::EventReceive) }
end

# 通知先の設定
# イベントは発言元のチャンネルがないので、これでチャンネル名を設定します
set :to_notify do |_|
  dest_condition do |res|
    res.last[:channel] = ENV['NOTIFY_CHANNEL'] # ex. Cxxxxxxx
  end
end

# 通常メッセージ
on 'ping', on_message: true do
  'pong'
end

# イベント通知（チャンネルの追加）
# 参考: https://api.slack.com/events/channel_created
on 'channel_created', on_event: true, to_notify: true do
  "created -> <##{@env.raw.channel.id}>" # 「created -> #hoge」のように投稿される
end
```

これで普通のbotとしての振る舞いもできるし、
イベント通知もできるbotが簡単に書けるようになります！[^1]ハートフルですね！

[^1]: メッセージとイベントが混ざって飛んでくるせいで、フィルタを毎回書かないといけなくなってることから目を背けながら

なお、残念なことにまだ全イベントには対応できていません。
イベントの種類が膨大なのと、そのイベントをどうやって使うかが想像しきれなかったものは省いています。

もし、このイベントにこういう形で対応したら便利！というのがあれば
PullRequestもらえると嬉しいです。

現時点でできることは以下のサンプルコードに書いてあります。

- https://github.com/rutan/repp-heartful_slack/blob/master/sample/app.rb

## :pencil2: 最後に

こういったイベント監視botは便利で愛されますが、
一部の人からは思いっきり嫌われます :dark_sunglasses: 
大きい組織に導入する場合は、強い心を持って導入するか、
自分だけが見られるチャンネルにこっそり導入しましょう。

用法、容量を守って楽しくハッピーSlacking！

## :book: 参考文献
- 今そこにあるSlack
    - http://tolarian-academy.net/clear-and-present-slack/
- 【要注意！英語？表現】「ハートフル」はうまく伝わらない和製英語？表現 | Weblio英会話コラム(英語での言い方・英語表現) 
    - https://eikaiwa.weblio.jp/column/knowledge/unnatural_english/heartful-is-japlish