Slack Connectでアプリを利用する時にハマったこと

Slackアプリの開発が楽しい最近。
こんなアプリを開発してみました。

[TASUKARU-TaskAll- Task Manager for Slack](https://lne.st/ulik)
>Slack用のタスクマネージャーアプリです。
>私自身がスレッドやメンション画面で依頼されたタスクを探すことが多いのですが、既存の画面では処理が終わったスレッドをアーカイブすることは出来ません。
>加えて、処理したあとにコメントをスレッドに追加すると、スレッドタブの上位にソートされるので、未処理タスクが下へ下へと埋もれていってしまいます。
>このアプリでは、やり取りのあったスレッドをリストするに加えて、アーカイブ機能を付けました。アプリの画面だけみていれば必要なやり取り全てが把握できるという状態を目指して作られたアプリです。


![TASUKARU_-TaskAll-_Task_manager_for_Slack_-_Google_スライド.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/22294/81f4886d-4e7e-957d-ee19-dc0baf307172.png)

細かい仕様については上記のリンク先に譲るのですが、こちらのアプリの開発中に困った出来事がありました。
TASUKARUでは、発言者とメンション先の人との間の情報を蓄積するアプリです。
一つのワークスペースで使っている段階では気付かなかった難点が、Slack Connect時に発生しました。
具体的な内容の前に、処理方法についての簡単な説明をします。

## TASUKARUでのデータ処理方法について
TASUKARUでは、上述の通り、メンション付きpost及び、それに付随したスレッド内容をトラッキングするアプリになっています。
history系SCOPEを取得することで、message eventを取得するようにしています。

``` Python:bolt.py
@bolt_app.event("message")
def handle_message_events(client, event, body, logger, view, payload):
    print('---------------------------')
    print('event message--------------')
    print('---------------------------')
```

SCOPEが正しく設定されていれば、こんな風に書いておくことでメッセージイベントを取得して処理することができるようになります。

問題になった挙動は、Slack Connect時の挙動です。
SlackConnectで共有されたチャンネルの双方のワークスペースでTASUKARUをインストールしていると、こんな状態になります。

![Slack___コラボチャンネルコネクトtest___Leave_a_Nest_Co__ltd_.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/22294/01f88e40-661b-2284-1609-d83a53673f92.png)

TASUKARUが２つはいっていることが分かります。
bot_id としては別物ですので当然そのようになります。

次に、課題がなんだったのかについて。

### ２つアプリが入っていた場合に想定する挙動
開発者として想定していた挙動は、botが2つはいっているので何かしらのpostがあった場合にはmessageイベントが2回発火するという挙動です。
しかし、どうやらSlackの仕様で、あとに入れたアプリ側でしか反応してくれないようなのです。
そうなると、情報の取得という意味では、片方のワークスペースにしか対応しないという状態になってしまいます。
この仕様に気付くまでにかなり時間を要しました。そうだったのか！

## 解決策
TASUKARUで必要な情報は、そのチャンネルにコネクトしているワークスペースのIDです。これさえあれば、各ワークスペース用のレコードとして保存が可能になります。
そこで利用できるAPI methodが[apps.event.authorizations.list](https://api.slack.com/methods/apps.event.authorizations.list)です。

今まで知らなかったのですが、App-level tokenというものを使う必要があるAPIです。
SCOPEもauthorizations:readというものを使います。
設定画面はアプリ画面のBasic Information画面の中です。
![Slack___Slack_API__Applications___George_Dev.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/22294/8af214f2-cecd-1cb1-e1d4-98eab5804f4f.png)

```python
from slack_sdk import WebClient
response = client.apps_event_authorizations_list(
    token="xapp-1-A111-111-xxx",
    event_context="4-eyJldCI6Im1lc3NhZ2UiLCJ0aWQiOiJUSktSOEg5TDIiLCJhaWQiOiJBMDJQMlNRSzRLQiIsImNpZCI6IkMwMTYwSFI4TTZGIn0",
)
```

こんな形で呼び出すと、authorizationsが返ってきます。詳細については12/2の @seratch さんの記事を参照ください。
[該当部分はこちらです](https://qiita.com/seratch/items/214866e9b6858fc4bd76#events-api-%E3%81%AE%E3%83%9A%E3%82%A4%E3%83%AD%E3%83%BC%E3%83%89%E3%81%AB%E7%B4%90%E3%81%A5%E3%81%8F-oauth-%E8%AA%8D%E5%8F%AF%E4%B8%80%E8%A6%A7%E3%81%AE%E5%8F%96%E5%BE%97%E6%96%B9%E6%B3%95%E3%81%8C%E5%A4%89%E6%9B%B4%E3%81%AB)
event_contextはmessage eventのどこにあるかと言うとbody["event_context"]で取得できます。
こんな感じの処理で、Slackコネクトで接続しているワークスペースIDをリストで取得できます。
環境変数にTOKENを入れておいてください
export APP_LEVEL_TOKEN=xxxxxxxxxxxxx

``` Python:bolt.py
from slack_sdk import WebClient
@bolt_app.event("message")
def handle_message_events(client, event, body, logger, view, payload):
    print('---------------------------')
    print('event message--------------')
    print('---------------------------')
    slack_ws_id_list = get_authorization_ws(body['event_context'])

def get_authorization_ws(event_context):
    APP_LEVEL_TOKEN = os.environ["APP_LEVEL_TOKEN"]
    app_client = WebClient(token=APP_LEVEL_TOKEN)
    authorizations_list = []
    try:
        authorizations_list = app_client.apps_event_authorizations_list(
            event_context=event_context
        )
    except SlackApiError as e:
        print("Error fetching authorizations_list: {}".format(e))
    authorizations = authorizations_list.get("authorizations")
    slack_ws_id_list = []
    for auth in authorizations:
        slack_ws_id_list.append(auth.get("team_id"))
    return slack_ws_id_list

```

これを利用することで、課題を解決することができました🎉🎉🎉🎉

苦心作のTASUKARU。是非これを期にお試しください。
[TASUKARU-TaskAll- Task Manager for Slack](https://lne.st/ulik)