Slackで自分の投稿にリアクションされたら通知する

# はじめに
Slackで自分が投稿したメッセージに対してリアクションされた場合に通知されて欲しいなと思い調べてみましたが見つかりません…。
未読やメンションは通知され、かつバッヂで残るので見逃しは基本ありませんが、リアクションは自発的に確認する必要があるので確認が遅れたり忘れたりすることがあります。
返信が必ず欲しい内容ではメンションしてもらうよう促せば良いですがリアクションでの返事の手軽さは中々手放せません。
なのでリアクションが通知されるようにSlackアプリを作りました。

※今回作成したアプリにスラッシュコマンドを追加した記事も書いてます。
[Slackのスラッシュコマンドを作成する](https://qiita.com/ytamu2/items/f81f9bc0023ef8758377)

完成したアプリ通知はこんな感じです。
![reaction_alert.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/604991/ec8d6ddb-f8e8-db52-5390-a4252676ceb9.gif)

漠然と以下をイメージしながら進めました。

- 自分にだけ表示
- 自分のリアクションは無視
- 特定のメッセージのみ通知

# 環境
- Python3.8
- Azure App Service

# Slackアプリ作成
以下URLにアクセスしてアプリ作成開始。
[https://api.slack.com/apps](https://api.slack.com/apps)
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/604991/8b8af3c8-012e-1a65-9106-fc3fe91d2298.png" width=70%>

アプリ名とワークスペースを選択して作成。
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/604991/21dead22-19d6-959c-6f74-b6f7855d8b1b.png" width=50%>

アプリ作成後はアプリのスコープを設定します。
設定するのは以下です。

- channels:history
- channels:read
- chat:write
- groups:history
- reactions:read
- users:read

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/604991/e14eeb2d-84aa-4736-cfd4-e47a573edd40.png" width=55%>

botユーザを作成します。

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/604991/c98f03c4-7eba-719c-e696-2038a20bb210.png" width=60%>

ワークスペースにアプリをインストールします。
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/604991/74cb5270-0f7a-ae02-98b1-147176fb9f9b.png" width=50%>


次にイベントAPIの設定をしていきますが、先にChallengeパラメータを返す処理を用意する必要があります。
Slackの設定はまた後で行います。

# Pythonアプリの作成
WebフレームワークはFlaskを利用します。
Slackとの接続やAPI処理は`slack-bolt`、`slack-sdk`を使用します。
必要なパッケージは `requirements.txt`に追加します。
slack-boltの指定でslack-sdkも一緒にインストールされます。

```plaintext:requirements.txt
Flask>=1.0
slack-bolt
```

BOTのトークン等はApp Serviceの`アプリケーション設定`で設定して処理の中で取得するようにします。
`os.environ`で取得している箇所です。

```python:app.py
import logging
import os
from slack_bolt import App
from slack_bolt.adapter.flask import SlackRequestHandler
from slack_sdk import WebClient

logging.basicConfig(level=logging.INFO)

client = WebClient(os.environ["SLACK_BOT_TOKEN"])

app = App()

@app.event("reaction_added")
def reaction_add(event, say):
    emoji = event["reaction"]
    user = event["user"]
    item_user = event["item_user"]
    channel = event["item"]["channel"]
    ts = event["item"]["ts"]

    if user == item_user:
        return

    # タイムスタンプでメッセージを特定
    conversations_history = client.conversations_history(
        channel=channel, oldest=ts, latest=ts, inclusive=1
    )

    messages = conversations_history.data["messages"]

    # メッセージが取得出来ない場合、スレッドからメッセージを特定
    if not messages:
        group_history = client.conversations_replies(channel=channel, ts=ts)
        messages = group_history.data["messages"]

    reactions = messages[0]["reactions"]

    target_reaction = os.environ["SLACK_REACTION_KEY"]
    start_postmessage = False

    reactions_text = ""

    for reaction in reactions:
        reactions_text += ":{}:{} ".format(reaction["name"], reaction["count"])
        if reaction["name"] == target_reaction:
            start_postmessage = True

    if not start_postmessage:
        return

    userslist = client.users_list()
    members = userslist["members"]

    for member in members:
        if member["id"] == user:
            reaction_user = member["real_name"]
            break

    response = client.chat_postEphemeral(
        channel=channel,
        user=item_user,
        text=f"{reaction_user} さんが以下のメッセージにリアクション :{emoji}: を追加しました\n\n"
        + messages[0]["text"]
        + f"\n{reactions_text}",
    )

from flask import Flask, request

flask_app = Flask(__name__)
handler = SlackRequestHandler(app)

@flask_app.route("/slack/events", methods=["POST"])
def slack_events():
    return handler.handle(request)

if __name__ == "__main__":
    flask_app.run(host="0.0.0.0", port=3000)
```

# Azure App Serviceの作成
WebサーバはAzureのApp Serviceを使用します。
ローカルGitリポジトリでデプロイするため、`コード`を選択します。
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/604991/b2005339-a483-88b6-ae43-f11190d58c26.png" width=70%>

ローカルGitリポジトリを選択します。
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/604991/fd728221-a313-281c-d3a0-e0393a16ef9e.png" width=80%>

資格情報を登録します。
Gitクローン作製時の認証に使用します。

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/604991/0678c05f-3b6a-5f81-4794-825581c673a7.png" width=80%>

## アプリケーション設定
環境変数を定義します。
`SLACK_REACTION_KEY`はリアクション通知対象のフラグを立てる絵文字名を入れます。
今回は:star:`star`を入れてます。 
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/604991/f1a8038a-afa4-6610-d009-a06f30927db2.png" width=85%>

`SLACK_BOT_TOKEN`と`SLACK_SIGNING_SECRET`はSlackのアプリ画面から取得します。

**SLACK_SIGNING_SECRET**
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/604991/81170783-ffc5-f33c-ecdc-c7c5feb14482.png" width=50%>

**SLACK_BOT_TOKEN**
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/604991/3595d5e4-7b3f-76be-3ee8-ed3e306395bc.png" width=55%>

次に`カスタムスタートアップコマンド`を設定します。
デフォルトは`app`というWSGIサーバが指定されています。
Pythonソースコード上は`flask_app`としているのでこちらで起動するように設定します。

```python:カスタムスタートアップコマンド
gunicorn --bind=0.0.0.0 --timeout 600 app:flask_app
```

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/604991/9a3e9e0b-1989-70a2-1a55-9a74bf92f954.png" width=60%>

App Serviceの設定が完了したらPythonアプリをデプロイします。
MSドキュメントの手順に従ってデプロイ設定をします。
[Azure App Service へのローカル Git デプロイ](https://docs.microsoft.com/ja-jp/azure/app-service/deploy-local-git?tabs=portal)

# Slackアプリ設定
Event Subscriptions画面でChallengeパラメータを返すURLを入力し、`Verified`になれば成功です。
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/604991/67395a24-0465-0458-33e5-4185d99a28e7.png" width=65%>

今回のRequest URLは以下の赤枠 + `/slack/events`です。
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/604991/7e934a96-45af-3538-4d3c-5e4f9e173e01.png" width=95%>

続いてリアクションされた時のイベントを連携するための設定をします。
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/604991/d0050dc7-eb4e-0cd7-e648-0151d650c1cf.png" width=65%>

設定追加後は忘れずに保存してください。
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/604991/555c93bf-d4d0-3e06-cb76-b61dbfe42d3d.png" width=70%>

# Slackのチャンネルにアプリ追加
アプリを利用したいチャンネルの詳細画面を開きアプリを追加します。
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/604991/7f1ad4f2-ab84-0e05-05a4-326dc9a13734.png" width=60%>
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/604991/ce2753b9-2a72-4252-5af5-6e1e75c758f3.png" width=60%>

これで冒頭のようにアプリ設定したチャンネルにおいてリアクションが通知されるようになります。
今回の例ですと自身の投稿に:star:のリアクションがついているものは他の人が追加でリアクションした場合に通知されます。
投稿のリアクションが即座に確認したい場合に利用してみてください。

# 参考にさせていただいたサイトや記事
[slackapi/bolt-python](https://github.com/slackapi/bolt-python)（サンプルプログラムもあります）
[Slack Bolt for Python](https://slack.dev/bolt-python/ja-jp/concepts)
[Python で Slack API や Webhook を扱うなら公式 SDK（slack-sdk/slack-bolt）を使おう](https://qiita.com/seratch/items/8f93fd0bf815b0b1d557)
[Slack Python SDK でチャンネルにメッセージを投稿しよう](https://qiita.com/seratch/items/93714b5cf3974c2f5327)
[Azure App Service 向けの Linux Python アプリを構成する](https://docs.microsoft.com/ja-jp/azure/app-service/configure-language-python)