Python で Slack API や Webhook を扱うなら公式 SDK（slack-sdk/slack-bolt）を使おう

こんにちは、Slack で公式 SDK の開発に従事している @seratch と申します。

これから Slack の Python SDK について日本語のチュートリアルを書いていく予定で、この記事はそれらのエントリーページにしようと思っています。ご興味あれば、ぜひこの記事をストックしておいてください :wave: (良いと思ったら LGTM もぜひ）

## え！ Slack に Python 公式 SDK なんてあったの？

・・と思った方も多いかもしれません。Python で Slack の Incoming Webhooks や Web API を使うとき、`requests` のような HTTP クライアントやコミュニティで開発された PyPI パッケージを使っている例をよく見かけます。Slack API は簡単に扱えることを重視して提供されていますので、もちろんそのようなやり方でも全く問題ありません！

しかし、公式 SDK を使えば、新しい API や機能への対応が容易になったり、パラメーターが非推奨になったときにいち早くワーニングログが出たり、細かい落とし穴にハマらずに済むかもしれません。

公式 SDK の開発者としては、ぜひ日本の皆さんにももっと使っていただけたらと思い、この記事を書くことにしました。

## どのようなパッケージがあるのか

2021 年現在、Slack は以下の PyPI パッケージを提供しています。前半の二つが現在アクティブに開発されているものです。この記事では slack-sdk と slack-bolt について解説する記事を順次リンクしていきます。

|PyPI パッケージ/ウェブサイト|説明|
|-|-|
|・[slack-sdk](https://pypi.org/project/slack-sdk/)<br>・[slack.dev/python-slack-sdk/](https://slack.dev/python-slack-sdk/)<br>・[GitHub リポジトリ](https://github.com/slackapi/python-slack-sdk)|最新の Python SDK で全ての機能（Web API、Incoming Webhooks、リクエスト署名、ソケットモード、Audit Logs API、SCIM API、OAuth フロー、RTM）をサポートしている。|
|・[slack-bolt](https://pypi.org/project/slack-bolt/)<br>・[slack.dev/bolt-python/](https://slack.dev/bolt-python/)<br>・[GitHub リポジトリ](https://github.com/slackapi/bolt-python)|ユーザーとのインタラクションやイベント API を扱うためのフルスタックフレームワーク。ソケットモード、Django、Flask、FastAPI などのメジャーなフレームワークとのアダプターが提供されており、Slack アプリ開発の本質的な実装だけに集中することができる。Web フレームワークへのアダプターなしで動作する場合 `slack-sdk` のみに依存し、より抽象化されたレイヤーを提供する。|

以下の二つは旧来のパッケージです。引き続きメンテナンスされていますが、少なくとも新しいアプリでは上の二つを使うようにしてください :bow: 

|PyPI パッケージ/ウェブサイト/GitHub リポジトリ|説明|
|-|-|
|・[slackclient](https://pypi.org/project/slackclient/)<br>・[slack.dev/python-slackclient/](https://slack.dev/python-slackclient/)<br>・[GitHub リポジトリ](https://github.com/slackapi/python-slack-sdk)|slack-sdk の旧バージョンで [slack-sdk への移行](https://slack.dev/python-slack-sdk/v3-migration/index.html#from-slackclient-2-x)を推奨している。現在もメンテナンスされているが、今後新機能の追加はされない。Web API、Incoming Webhooks、RTM API、リクエスト署名にのみ対応している。|
|・[slackeventsapi](https://pypi.org/project/slackeventsapi/)<br>・[github.com/slackapi/python-slack-events-api](https://github.com/slackapi/python-slack-events-api)|[イベント API](https://api.slack.com/apis/connections/events-api) を Flask アプリで受信するためのライブラリ。Bolt の `ack()` のような 3 秒タイムアウトへの考慮はない。現在もメンテナンスされているが、[slack-bolt への移行を推奨](https://github.com/slackapi/python-slack-events-api#%EF%B8%8F--important-notice)している。|

## とにかくはじめよう

pip install でパッケージを入手しましょう。どちらのパッケージも Python 3.6 以上で動作しますので `python3 --version` でバージョンを確認してください。

この記事では [Poetry](https://python-poetry.org/) を使って説明していきますが、pip でインストールするなら

```bash
pip3 install slack-sdk
```

のようになります。Bolt を使う場合は `pip3 install slack-bolt` だけで OK です。`slack-bolt` は `slack-sdk` に依存しているので `slack-bolt` をインストールすれば `slack-sdk` も自動的にインストールされます。

では、ここからは Poetry を使っていきましょう。インストールしていない人は[こちら](https://python-poetry.org/docs/)を参考にしてください。

```bash
# 新しいプロジェクトを作成
poetry new my-awesome-slack-app
cd my-awesome-slack-app/

# 必要に応じて poetry env use 3.9 など

# virtualenv を有効化
poetry shell

# slack-sdk を追加
poetry add slack-sdk

# とりあえずインストールしたパッケージが使えているか確認
echo 'from slack_sdk.web import WebClient
client = WebClient()
response = client.api_test()
print(response)' > api_test.py

python api_test.py

# 以下のステップで Slack の API 呼び出しに使えるトークンを設定してください
#
# 1) https://api.slack.com/apps であらかじめ Slack アプリを作成
# 2) OAuth & Permissions のページで chat:write scope を追加
# 3) Install to Workspace ボタンからワークスペースへのインストールを実行
# 4) 発行された xoxb- か xoxp- で始まるトークンを環境変数に設定
#
export SLACK_API_TOKEN='xoxb-{your own token}'

# 以下のコードを実行するとトークンのメタ情報が取得できるはず
echo 'import os
from slack_sdk.web import WebClient
client = WebClient(token=os.environ["SLACK_API_TOKEN"])
response = client.auth_test()
print(response)' > auth_test.py

python auth_test.py

# #random チャンネルにメッセージを投稿してみる
echo 'import os
from slack_sdk.web import WebClient
client = WebClient(token=os.environ["SLACK_API_TOKEN"])
response = client.chat_postMessage(text=":wave: こんにちは！", channel="#random")
print(response)' > say_hi.py

python say_hi.py
```

という感じになります。

Incoming Webhooks の例に興味があれば、代わりに以下のようにしてみてください。

```bash
export SLACK_WEBHOOK_URL='https://hooks.slack.com/services/T11111/B11111/xxxxxxx'

echo 'import os
from slack_sdk.webhook import WebhookClient
webhook = WebhookClient(os.environ["SLACK_WEBHOOK_URL"])
response = webhook.send(text=":wave: こんにちは！")
print(f"status: {response.status_code} body: {response.body}")' > webhook.py

python webhook.py
```

これらを何度か実行すると Slack のチャンネルには「:wave: こんにちは！」が溢れ（私がテストで何回も実行しただけの話なんですが・・）
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/19163/8c7bcfaa-9a81-5afc-be8f-a0f7e5754a9a.png" width=400 />
ターミナル上の出力は以下のようになるはずです。

```bash
$ poetry new my-awesome-slack-app
Created package my_awesome_slack_app in my-awesome-slack-app

$ cd my-awesome-slack-app/
$ ls
README.rst		my_awesome_slack_app	pyproject.toml		tests

$ poetry shell
Creating virtualenv my-awesome-slack-app in /path-to-app/my-awesome-slack-app/.venv
Spawning shell within /path-to-app/my-awesome-slack-app/.venv
. /path-to-app/my-awesome-slack-app/.venv/bin/activate
bash-3.2$ . /path-to-app/my-awesome-slack-app/.venv/bin/activate

(.venv) bash-3.2$ poetry add slack-sdk
Using version ^3.5.1 for slack-sdk

Updating dependencies
Resolving dependencies... (0.7s)

Writing lock file

Package operations: 9 installs, 0 updates, 0 removals

  • Installing pyparsing (2.4.7)
  • Installing attrs (21.2.0)
  • Installing more-itertools (8.7.0)
  • Installing packaging (20.9)
  • Installing pluggy (0.13.1)
  • Installing py (1.10.0)
  • Installing wcwidth (0.2.5)
  • Installing pytest (5.4.3)
  • Installing slack-sdk (3.5.1)

(.venv) bash-3.2$ echo 'from slack_sdk.web import WebClient
> client = WebClient()
> response = client.api_test()
> print(response)' > api_test.py

(.venv) bash-3.2$ python api_test.py
{'ok': True, 'args': {}}

(.venv) bash-3.2$ export SLACK_BOT_TOKEN='xoxb-11111111111-xxxxxxxxxxxxxxx'

(.venv) bash-3.2$ echo 'import os
> from slack_sdk.web import WebClient
> client = WebClient(token=os.environ["SLACK_API_TOKEN"])
> response = client.auth_test()
> print(response)' > auth_test.py

(.venv) bash-3.2$ python auth_test.py
{'ok': True, 'url': 'https://xxx.slack.com/', 'team': 'xxx', 'user': 'my-awesome-bot', 'team_id': 'T111', 'user_id': 'U111', 'bot_id': 'B111', 'is_enterprise_install': False}

(.venv) bash-3.2$ echo 'import os
> from slack_sdk.web import WebClient
> client = WebClient(token=os.environ["SLACK_API_TOKEN"])
> response = client.chat_postMessage(text=":wave: こんにちは！", channel="#random")
> print(response)' > say_hi.py

(.venv) bash-3.2$ python say_hi.py
{'ok': True, 'channel': 'C111', 'ts': '1621562691.008000', 'message': {'bot_id': 'B111', 'type': 'message', 'text': ':wave: こんにちは！', 'user': 'U111', 'ts': '1621562691.008000', 'team': 'T111', 'bot_profile': {'id': 'B111', 'deleted': False, 'name': 'My App', 'updated': 1584342729, 'app_id': 'A111', 'icons': {'image_36': 'https://avatars.slack-edge.com/xxx_36.jpg', 'image_48': 'https://avatars.slack-edge.com/xxx_48.jpg', 'image_72': 'https://avatars.slack-edge.com/xxx_72.jpg'}, 'team_id': 'T111'}}}

(.venv) bash-3.2$ export SLACK_WEBHOOK_URL='https://hooks.slack.com/services/T11111/B11111/xxxxxxx'

(.venv) bash-3.2$ echo 'import os
> from slack_sdk.webhook import WebhookClient
> webhook = WebhookClient(os.environ["SLACK_WEBHOOK_URL"])
> response = webhook.send(text=":wave: こんにちは！")
> print(f"status: {response.status_code} body: {response.body}")' > webhook.py

(.venv) bash-3.2$ python webhook.py
status: 200 body: ok
```

## 今後公開予定の記事

以下の記事をこれから一つずつ書いていく予定です。これら以外に知りたい情報がありましたら、この記事のコメントで教えてください！

* [Slack Python SDK で Incoming Webhooks を使おう](https://qiita.com/seratch/items/03e224cc092e1a7dd764)（[英語ドキュメント](https://slack.dev/python-slack-sdk/webhook/index.html)）
* [Slack Python SDK でチャンネルにメッセージを投稿しよう](https://qiita.com/seratch/items/93714b5cf3974c2f5327)（[英語ドキュメント](https://slack.dev/python-slack-sdk/web/index.html#messaging)）
* [Slack Python SDK でチャンネルにファイルをアップロードしよう](https://qiita.com/seratch/items/2dd8e5676b09e8863618)（[英語ドキュメント](https://slack.dev/python-slack-sdk/web/index.html#files)）
* [Bolt for Python でソケットモードのアプリをつくろう](https://qiita.com/seratch/items/1a460c08c3e245b56441#python-%E3%81%A7%E5%A7%8B%E3%82%81%E3%82%8B)（[日本語ドキュメント](https://slack.dev/bolt-python/ja-jp/concepts#socket-mode)）
* [Bolt for Python でモーダルを使った申請フォームを作ろう](https://slack.dev/bolt-python/ja-jp/concepts#opening-modals)
* [Bolt for Python で複数のワークスペースで使える Slack アプリをつくろう](https://slack.dev/bolt-python/ja-jp/concepts#authenticating-oauth)
* [asyncio ベースのアプリで Slack Web API を利用しよう](https://qiita.com/seratch/items/c03e9dcef2b425b72222)（[英語ドキュメント](https://slack.dev/python-slack-sdk/web/index.html#asyncwebclient)）

英語でもよろしければ、全ての内容は https://slack.dev/python-slack-sdk/ と https://slack.dev/bolt-python/ にあります。今すぐ情報を知りたい方は、英語のドキュメントを参考にしてください。

また、Bolt for Python の GitHub リポジトリには様々なサンプルコードがありますので、チェックしてみてください。
https://github.com/slackapi/bolt-python/tree/main/examples

ぜひ公式 SDK を使って Slack をより便利にする創意工夫を楽しんでください！それではまた :wave: