やること
- slackのメッセージ投稿をフックしてweb apiで利用できるようにする
環境
- python3.6
- Flask==1.0.2
- slackeventsapi==2.1.0
対象
- slackの投稿に対して何か処理をしたい人
前提
やりたいことそのまんまの名前をしているslackのmessage Outgoing Webhookは既に廃止😭
slack api Outgoing Webhooks
代わりにevent apiを用いることでメッセージをフックする。
手順
- Flaskをインストールする
- python-slack-events-apiをインストールする
- slack apiコンソールからslackアプリを作成する
- Flaskでslack-events-apiを用いて、フックを受けるメソッドを定義する
- Event Subscriptionsにフック先のURLを登録する
Flaskをインストールする
- 今回はFlaskの説明は割愛
- 知らない方はネットに説明がたくさんあるので、そちらを参考してください
というわけで、とりあえず
pip install flask
python-slack-events-apiをインストールする
- slack event apiには便利なクライアントライブラリが用意されている
- 別にクライアントライブラリを使わなくても実装はできるが、後述する理由により、ライブラリを使う方が圧倒的に楽。
使うのはこれ→event api ライブラリ
こちらも
pip install slackeventsapi
公式でも導入方法が説明されている。英語に抵抗なければこちらも参考に
slack apiコンソールからslackアプリを作成する
- slackのapiコンソールを開く
- Create New Appからアプリを作成

- 導入先のワークスペースを選ぶ。アプリの名前は後からでも変えられるから適当に決める

- Event Subscriptionsを開いて、Enable EventsをONにする。

Flaskでslack-events-apiを用いて、フックを受けるメソッドを定義する
- 適当な名前のpythonファイルを作成して、以下を書き込む
slackHook.py
from flask import abort, Flask, jsonify, request
from slackeventsapi import SlackEventAdapter
app = Flask(__name__)
slack_events_adapter = SlackEventAdapter("SIGINING_SECRET", "/slack/events", app)
@slack_events_adapter.on("message")
def hookSlackEvents(event_data):
app.logger.debug(event_data)
- この際のSIGINING_SECRETには、先ほど作成したslackアプリのBasic Informationに書いてある、SIGINING SECRETを入れる。
※図の中ではミスによりClients Secretにオレンジ枠がかかっているが、正しくはSigning Secretなので注意

- Flaskサーバを起動する
- Flaskサーバをローカルで立ち上げた場合は、ngrok等を利用して、外部からアクセスできるようにする
Event Subscriptionsにフック先のURLを登録する
- 先ほど作成したslackアプリのEvent Subscriptionsを開いて、Request Eventsにさっき立ち上げたサーバのエンドポイントを登録する
-
baseURL + /slack/events
というように、Flaskのslack_events_adapteに登録したリソースと同じように入力する

- エンドポイントを入力してしばらくすると、Verifiedとでるので、こうなればフックが完了したこととなる
- 同じ画面の中間あたりのSubscribe to Workspace Eventsで、フックしたいイベントを選択する。今回は
message.channels
を

- 画面を離れる前に画面下部のsave changeを押すのを忘れないように
- ちなみに、クライアントライブラリを使わなかった場合、エンドポントが生きているかの認証処理を自分で実装しなければならないのでめんどくさい(自分はうまくできなかった・・・)
- 成功してる人いた→Outgoing WebHooksの代わりをSlack APPで実装する
確認
- どのチャンネルでもいいので、なにかしたメッセージを投稿すれば、コンソールにログがでるはず。
うまく行かないとき
- apiを使うときにはいくつかの権限を付与する必要があるため、もし自分のslackアカウントがアプリに権限を与える権限を持っていない場合はうまく行かないかも
ちょっと解説
わかる人か見れば明白なのだけど、一応この部分で何やってるかの解説
slackHook.py
slack_events_adapter = SlackEventAdapter("SIGINING_SECRET", "/slack/events", app)
@slack_events_adapter.on("message")
def hookSlackEvents(event_data):
app.logger.debug(event_data)
- 一行目ではただクライアントのインスタンスを作成しているだけ
- @から始まる二行目は、通常のFlaskアプリのルーティングの代わりの役目を果たす
- 三行目が、そのローティングに対して実行したいメソッド
- event_dataにイベントの内容がJsonで受け取れる
- 今回はメッセージの投稿をフックしたかったため、
"message"
を指定したが、別のイベントもフックすることができる。 -
"message"
の変わりに、使いたいイベントを@slack_events_adapter.on("event")
に書いて、Subscribe to Workspace Eventsからフックしたいイベントを追加すればOK- イベント一覧API Event Types
あとがき
- 多分slack上のほぼ全てのイベントをフックできるから、使いこなせばワークスペースの神になれる
- 新しいメンバーがジョインしたら自動で資料を送信する
- ファイルがアップロードされたらそれをGoogle driveにアップ
- 嫌いな奴が投稿するたび、その投稿を削除する
- slack web apiを使うためのpython-slackclientと、今回のpython-slack-events-apiは別ライブラリなので注意
- slack web apiでもRTM apiを使えば、同じようにイベントのフックが可能っぽい
参考↓
rtm.py
import os
import slack
@slack.RTMClient.run_on(event='message')
def say_hello(**payload):
data = payload['data']
web_client = payload['web_client']
rtm_client = payload['rtm_client']
if 'Hello' in data['text']:
channel_id = data['channel']
thread_ts = data['ts']
user = data['user']
web_client.chat_postMessage(
channel=channel_id,
text=f"Hi <@{user}>!",
thread_ts=thread_ts
)
slack_token = os.environ["SLACK_API_TOKEN"]
rtm_client = slack.RTMClient(token=slack_token)
rtm_client.start()
- 自分はFlaskをzappaをつかって、lambdaにデプロイしたが、ワークスペースの全ての投稿のたびに呼びだされるから、さすがのlambdaも無料枠突破するのでは? とちょっと心配
- zappaでデプロイするとデフォルトで512MBになるから、無料枠は800,000秒
- 一回の処理に多くみつもって1秒かかるとしても800,000投稿/月までは大丈夫。1日26,666投稿までは大丈夫と考えると全然余裕だった。
Twitterもやってるので、よければフォローお願いします。
→https://twitter.com/ObataGenta