Slack上で動くChatbotを実現するためのアーキテクチャを考える

Slack上でメッセージを送り、それをもとにBotが何らかの処理をするということを実現するアーキテクチャには何種類か存在します。
どういうときにどのアーキテクチャを採用すればよいか、そのメリットとデメリットや使い所について紹介します。


# [Events API](https://api.slack.com/events-api)

![events_api.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/16458/76c4a184-3db8-c0fd-1f27-67e9ed83021b.png)

Slackで(あらかじめ指定した)特定のイベントが発生すると、(Bot側の)URLにwebhookが発生します。「チャンネル内での発言」や「Botへのmention」のように、拾いたいイベントは選ぶことができます。したがって、Slack上のイベントのあらゆるものがwebhookされるわけではなく、欲しいものに(ある程度)絞って通信されるため、Bot側にとってはある程度優しい仕組みになっています。

その性質上、Slackからwebhookを受けるURLを登録する必要があります。そのため、AWSでいうAPI Gateway + Lambdaのような小さい構成で使うと良さそうかなと思います。(結果的にBotを常駐するより大抵の場合はコストが抑えられそう)

## この仕組みのメリット

### Bot側のリソース消費を抑えられる

必要なイベントに絞ってSlackから通信が行われるため、Bot側のリソース消費を抑えられます。
とはいえ、SubscribeするのはSlack側でのイベントの単位というざっくりしたレベルなので、Bot側でなんの制御も行わなくて良いわけではなく、ちゃんと発言者や発言の内容、チャンネル名などをパラメータとして受けて制御を行うのは避けられません。(これはどの仕組みでも同じですが)

## この仕組みのデメリット

### インターネットに向けてURLを公開する必要がある

仕組み上、Slackからの通信を受けることになります。
普段は気にすることはないでしょうが、例えばオンプレミスの環境などで簡単にインターネットに公開するURLを持てない場合などはこの仕組みを採用しづらくなるでしょう。

### 規模が大きくなるとhookが遅い

(これを書きたくてこの記事を書いたのですが)規模が大きくなると、如実に遅くなります。

そもそもそんな規模でSlackを使っているところはそんなにないのかもしれませんが、2500人規模のSlackだと、 発言してから `app_mention` イベント(BOTへのmention)がBOT側に到達するまでに5秒くらいかかりました。(正確には、遅さが何に依存するかをはっきりとは調べていません。workspace全体での瞬間的なイベントの量かもしれません)

BOTに到達してからのチューニングであればある程度できそうですが、そもそも到達するのが遅いとなると手の打ちようがありません…。ですので、全体的にはとてもお手軽に作ることができる一方、規模が多いとBotに負担させたいことをまとめて次に説明するRTMで処理するほうがいいように思います。

遅くなる要因がはっきりしないので、一応その時の環境を残しておきます。

* Workspace全体で2500人程度
* とはいえ、そのBOTが所属するchannelは10人前後、発言もほとんどない(多くて数秒に1回程度)
* BOT自体は2チャンネルくらいしかjoinしていない

## 注意すべき仕様

* 3秒以内に応答を返さないと、Exponential Backoffによるリトライを最大3回行います。
* Subscribeするイベントは3万回/1時間までとなっています。
* イベントの種類によるsubscribe制限は可能ですが、細かい制御はできないので自前で作る必要があります。

# [Real Time Messaging API(RTM)](https://api.slack.com/rtm)

![rtm.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/16458/b4d11142-f38b-0d6a-f252-6bdd9a74b84c.png)

BOT側からWebSocket通信をSlack側で行い、あとはSlackとの間でイベントのやり取りを行います。

## この仕組みのメリット

### 早い

Real Timeというだけあって、Slack上で必要なイベント(例えばBOTへのmention)が発生すると、ほぼ即時でBOT側に到達します。
したがってユーザとインタラクティブな反応が必要な処理を行う場合の即応性を求める場合に適しています。

### URLを公開する必要がない

Events APIの逆ですが、BOT側からSlackへWebSocketを張りにいくので、URLを持たなくてもやりたいことを実現することができます。

## この仕組みのデメリット

### 基本的にBOTを常駐(常時起動)させる必要がある

これもEvents APIの逆ですが、BOTとSlackの間でWebSocketを張っておく必要があるので、基本的にBOT側は常時起動となると思います。
となると、そのインスタンスのランニングコストがそれなりにかかることになります。

# おまけ: [Slash Commands](https://api.slack.com/interactivity/slash-commands)

## 説明

アーキテクチャでいうならEvents APIとほとんど同じ(EndpointとPayloadが別というだけ)です。(3秒以内に反応を返さなければならないというのも同じ)

機能の違いなのでおまけとして説明します。

## この仕組みのメリット

そのbotの存在関係なく、あらゆるチャンネルで発動させることができます。
なので、自然と汎用的な用途、チャンネルに無関係な用途のためにこの方法を用いることになると思われます。例えばすでにあるコマンドであるToDoリストの作成や、remind、channelへのinviteなどがそれに当たります。

## この仕組みのデメリット

メリットの裏返しですが、どこのチャンネルでも発動させることができるので、用途が違います。
特定のチャンネルで特定の用途で仕事をさせるChatbotと、あらゆるチャンネルで汎用的な仕事をさせるSlash Commandでは、目的の時点で選択が分かれるはずです。
(ですので、別にデメリットというわけではないのですが、自ずといずれを選択するかは決まるかと思います)

# おまけ: [Outgoing webhook](https://api.slack.com/custom-integrations/outgoing-webhooks)

Slack Integrationの1つとしての機能です。単にSlackからhookを受けて返答したいだけならこれでもよいと思います。
ただし、Slackとしてはlegacyの位置づけであり、非推奨となっています。

# どのアーキテクチャを選択すべきか

手軽に済ませたいならEvents API、今後の拡張性を考えて、そのBotが様々なことを行う可能性があるならRTMでしょうか。
URL公開が負担になるなら選択のしようがありませんが、Events APIのみならずSlack Commandや [Interactive Components](https://api.slack.com/reference/block-kit/interactive-components)などでも必要になってくるので、実現できるのであればURL endpointの1つでも持っていくと今後便利だろうなと思います。