リモートワークに役立つ！？Slackbotを自作してみよう！

#今回作成するもの
今回作成するBotは、**マイナスの言葉がチャットに書き込まれると、削除し、逆にプラスの言葉が書き込まれるとリアクションで感謝を伝えてくれるBot**です:robot:
実際にSlackでどのように動くのか見てみましょう！

![slackbot.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1912865/5c6714ee-45ef-dce4-2d50-0114f522fb6e.gif)

**このSlackBotを作成して、不快なチャットからはおさらば！快適なチャットには感謝を！そして、平和なSlackを手に入れましょう:dove:**

1から丁寧に解説していくので初心者の方も安心してください！
それでは、楽しい楽しいBot制作に取り組んでいきましょう:muscle_tone2::muscle_tone2::fire:

#主に使用するもの
今回は「Web API methods」を利用して、SlackBotの機能を拡張していきます。

https://api.slack.com/methods

数多くある「Web API methods」の中から、
`chat_delete`　...　Slack上のチャットを消すことができるメソッド。
`reactions_add` ... Slack上のチャットにリアクション（絵文字スタンプを追加）することができるメソッド。
を利用します。

https://api.slack.com/methods/chat.delete

https://api.slack.com/methods/reactions.add


#Bot用のアプリを作成する。

https://api.slack.com/apps?new_app=1

上のURLからアプリを新規作成し、アプリ名を入力し、導入するワークスペースを選択する。

![slack01.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1912865/51e99832-d165-0b08-e16a-5ac56c683307.png)

#Bot用のアプリの初期設定を行い、Slackにインストールする。
左サイドメニューバーの「OAuth & Permissions」をクリックし、「Scopes」の「Bot Token Scopes」に以下のように`chat:write`と`reactions:write`と`channels:history`を追加する（「channels:history」に関しては後で自動追加されるので、ここで追加しなくてもOK）。

同じく「Scopes」の「User Token Scopes」に以下のように`chat:write`を追加する。

![slack08.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1912865/3f297c40-2249-0905-be26-127f2b27f0bc.png)


左サイドメニューバーの「Basic Information」の「App-Level Tokens」に以下のように`connections:write`を追加する。その後に表示される**APP_TOKEN(xapp-で始まるもの)**は今後使用するのでコピーしておく。

![slack03.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1912865/777fc47a-8b8c-092e-0116-28bbce12e6b9.png)

左サイドメニューバーの「Socket Mode」をクリックし、「Enable Socket Mode」をONにする。

![slack04.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1912865/2bcff300-8c5b-275c-c4a1-d3a2741be670.png)

左サイドメニューバーの「Event Subscriptions」の「Enable Events」をONにし、「Subscribe to bot events」の「Add Bot User Event」をクリックし、`message.channels`を追加する。追加したら「**Save Changes**」をクリックし、変更を保存する。

![slack05.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1912865/7c44cacc-fc97-e581-71ac-0c7a1c0374a4.png)

左サイドメニューバーの「App Home」をクリックし、「App Display Name」の「Edit」をクリックする。「Display Name(Bot Name)」にBotの名前を入力し、「Default username」に英数字のみでユーザーネームを入力する（ユーザーネームが何に使われているかは不明…）。

![slack06.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1912865/af07302f-d6df-94d0-aab5-ae1b525a1b4c.png)

左サイドメニューバーの「Install App」をクリックし、「Install to Workspace」をクリックし、「許可する」をクリックする。その後に表示される**Bot User OAuth Token（xoxb-から始まるもの）**と**User OAuth Token（xoxp-から始まるもの）**は今後使用するのでコピーしておく。

#使用するディレクトリ・ファイルの用意
ターミナルで、作業ディレクトリを作成し、移動する。

```
$ mkdir slack-bot
$ cd slack-bot
```

作業ディレクトリの中に、「app.py」ファイルと「.env」ファイルを作成する(VSC：Visual Studio Codeを使用)。

![file_name.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1912865/bbab5091-bbfd-6740-1478-f116f8587e82.png)

#「.env」ファイルにコードを書いていく
```:.env
SLACK_BOT_TOKEN = 'xoxb-*****'（「準備」で保存した「Bot User OAuth Token」）
SLACK_USER_TOKEN = 'xoxp-*****'（「準備」で保存した「User OAuth Token」）
SLACK_APP_TOKEN = 'xapp-*****'（「準備」で保存した「App-Level Tokens」）
```
※（）のところはコードに書く必要なし

<details><summary>**:warning:後にBotを実行したときにSSL認証エラーが出る場合はこの処理を行う。**</summary><div>

```
$ pip install certifi
$ python3 -m certifi
```

`〜 site-packages/certifi/cacert.pem` ←こんな感じのものが出力されるのでコピーする。

SSL_CERT_FILEを「.env」ファイルに書き加える。

```:.env
SSL_CERT_FILE = '〜 site-packages/certifi/cacert.pem'（「python3 -m certifi」の出力結果）

SLACK_BOT_TOKEN = 'xoxb-*****'
SLACK_USER_TOKEN = 'xoxp-*****'
SLACK_APP_TOKEN = 'xapp-*****'
```
※（）のところはコードに書く必要なし
</div></details>

#「app.py」ファイルにコードを書いていく
##1. 必要なパッケージをインストールする

```
$ pip install python-dotenv
$ pip install slack_bolt
```

##2. ライブラリの呼び出し
```python:app.py
#環境変数を扱うためのライブラリ
import os
from dotenv import load_dotenv
#Botを動かすためのライブラリ
from slack_bolt import App
from slack_bolt.adapter.socket_mode import SocketModeHandler
#Web API methodsを使用するためのライブラリ
from slack_sdk import WebClient
```

##3. Botを起動する

```python:app.py
#「.env」ファイルに書いた環境変数を扱えるようにする。
load_dotenv()
#「Web API methods」を使うための準備
client = WebClient(token=os.environ['SLACK_BOT_TOKEN'])
user_client = WebClient(token=os.environ['SLACK_USER_TOKEN'])
#アプリ（Bot）の初期化
app = App(token=os.environ['SLACK_BOT_TOKEN'])
#後でここにBotの機能を書く

#アプリ（Bot）の起動
if __name__ == '__main__':
    SocketModeHandler(app, os.environ['SLACK_APP_TOKEN']).start()
```

**CHECK**
`load_dotenv()` ... 「.env」ファイルと「app.py」を結びつける
`os.environ['環境変数']`...環境変数(「.env」で定義した変数)を扱う

**POINT**
`WebClient()`...「Web API methods」を使用するクライアントを指定する
「SLACK_BOT_TOKEN（xoxb-）」と「SLACK_USER_TOKEN（xoxp-）」には**権限に差があります**。例えば、チャットを消すというメソッドを使用するときに、前者では、自分自身のチャットしか消すことができませんが、後者では、すべてのチャットを消すことができます。
※詳しいことはわからないので、[ここ](https://api.slack.com/authentication/token-types#user)を参照してください。

:::note info
今回は、reactions_addは、xoxb-の方を使用し、chat_deleteは、xoxp-の方を使用します。
:::
`App()`...アプリ（Bot）を使用するための準備
`SocketModeHandler`...ソケットモードでアプリを起動する

**ここまで書けたら、エラーが起きないか確認するためにBotを起動してみましょう:robot:**

<details><summary>**Botの起動方法**</summary><div>

```
$ cd slack-bot
$ python3 app.py
```

正常にBotが起動すると、次のメッセージが出力されます。
> :zap:Bolt app is running!

</div></details>

##4. 不快なメッセージを削除する機能を追加する
「残業」という言葉を含むメッセージが送られたら削除する機能です（働きたくないでござる:smirk_cat:）。
`#後でここにBotの機能を書く`と書いたところに、Botの機能を表すコードを書き加えていきましょう！

```python:app.py
#「残業」という言葉を含むメッセージがきたら消す機能
@app.message('残業')
def delete_zangyo(message, say):
    channel = message['channel']
    timestamp = message['ts']
    user_client.chat_delete(channel=channel, ts=timestamp)
    say('ん？なんか言ってた？聞こえなかったなぁ。')
```

**POINT 基本的なBotのコード**
`@app.message('キーワード')` ...キーワードを含むメッセージがチャットに送信されたら、アプリ（Bot）が反応します。今回は、「残業」をキーワードに設定するので、 `@app.message('残業')` です。

`def 関数名(message, say):` ... アプリ（Bot）が反応したら、何を実行するのか、を関数で定義します。関数名はどんな名前でもOKです。今回は、残業というキーワードを含むチャットを削除するので、`def delete_zangyo(message, say):`とします。引数には、**message**と**say**を指定します。この関数に「残業」を含むチャットを削除する機能を後で加えます。

`message`=> Botが反応したメッセージに関する情報を持っています。以下のように辞書型で情報を持っているので、必要な情報を`message['辞書のkey']`の形で取り出すことができます。

:::note info
{'client_msg_id': '********-****-****-****-************', 'type': 'message', 'text': '残業しろ！', 'user': 'U**********', 'ts': '********** . ******', 'team': 'T**********', 'blocks': [{'type': 'rich_text', 'block_id': 'dm3', 'elements': [{'type': 'rich_text_section', 'elements': [{'type': 'text', 'text': '残業しろ！'}]}]}], 'channel': 'C**********', 'event_ts': '********** . ******', 'channel_type': 'channel'}
:::

例えば、送られてきた「残業しろ！」というメッセージを取り出したい場合は、 `message['text']`で取り出すことができます。

`say`=> Botが反応したメッセージに対する返信を定義する関数です。say('返信したい言葉')で返信できます。

**POINT Slack API methodsの使い方**

まずは、[chat_delete](https://api.slack.com/methods/chat.delete)を見ます。

![chat_del.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1912865/f2f7ed1f-550c-813d-120b-da0dd169ca2b.png)

`・Method access`には、コードの書き方が書かれています。

`・Required scopes`には、このAPIメソッドを使用するのに必要なBotの権限の範囲が書かれています（今回は**User tokens**の方を使用。）。このScopeは「準備」の時にすでに追加してあります。

<details><summary>**Scopeを新たに追加したい時**</summary><div>
[ここ](https://api.slack.com/apps?new_app=1)に飛び、Scopeを追加したいアプリ（Bot）を選択し、左サイドメニューの「OAuth & Permissions」の「Scopes」の「Add an OAuth Scope」で追加し、左サイドメニューの「Install App」からアプリを再インストールします。
</div></details>

`・Arguments`には、このメソッドの引数が書かれています。tokenは、「.env」ファイルで`SLACK_BOT_TOKEN`として定義したもので、すでに`client`に定義されているので、chat_deleteの引数として書く必要はないです。残りの引数は、chat_deleteの引数として使用することができます（「Required」と書かれているものは必須なので、引数として必ず使用します）。


**channel引数**...（削除する）対象のチャットが打ち込まれたチャンネルIDを指定します（Cから始まる11文字の英数字で表される）。
**ts引数**...（削除する）対象のチャットの場所（タイムスタンプ）を指定します（10文字の数字.6文字の数字で表される）。

色々と言ってきましたが今回は、API methodのchat_deleteを使用するためには下のように書きます。ここを押さえてくれればOKです！

```python:
user_client.chat_delete(channel='削除したいチャットのチャンネルID', ts='削除したいチャットのタイムスタンプ')
```

削除したいチャットのチャンネルIDは、`message['channel']`、削除したいチャットのタイムスタンプは、`message['ts']`にあります（上の「POINT 基本的なBotのコード」の`message`の説明参照。）。

<details><summary>**ここまでの全体のコードの確認**</summary><div>

```python:app.py
#環境変数を扱うためのライブラリ
import os
from dotenv import load_dotenv
#Botを動かすためのライブラリ
from slack_bolt import App
from slack_bolt.adapter.socket_mode import SocketModeHandler
#Web API methodsを使用するためのライブラリ
from slack_sdk import WebClient

#「.env」ファイルに書いた環境変数を扱えるようにする。
load_dotenv()
#「Web API methods」を使うための準備
client = WebClient(token=os.environ['SLACK_BOT_TOKEN'])
user_client = WebClient(token=os.environ['SLACK_USER_TOKEN'])
#アプリ（Bot）の初期化
app = App(token=os.environ['SLACK_BOT_TOKEN'])
#「残業」という言葉を含むメッセージがきたら消す機能
@app.message('残業')
def delete_zangyo(message, say):
    channel_id = message['channel']
    timestamp = message['ts']
    user_client.chat_delete(channel=channel_id, ts=timestamp)
    say('ん？なんか言ってた？聞こえなかったなぁ。')
#アプリ（Bot）の起動
if __name__ == '__main__':
    SocketModeHandler(app, os.environ['SLACK_APP_TOKEN']).start()
```
</div></details>

ここまでかけたら、Botを起動させ、Botを招待したSlackのチャンネル上で、「残業」というキーワードを送信してみてください！Botがメッセージを削除し、喧嘩を売ってくれます:thumbsup_tone1:

![残業しろ.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1912865/a6c9c474-2567-2b16-97ee-dba07cfe65f7.gif)


##5. プラスな言葉にリアクションをつける機能を追加する
先ほどと同じ要領で、さらに機能を追加していきましょう！

```python:app.py
#「ありがとう」という言葉を含むメッセージがきたらリアクションを追加する機能
@app.message('ありがとう')
def react_thank(message, say):
    channel_id = message['channel']
    timestamp = message['ts']
    client.reactions_add(channel=channel_id, timestamp=timestamp, name='+1')
    say('Peaceful Chat!!')
```

このように先程のコードを少し変えただけで違う機能を作ることができます！
説明をしなくても、さっきと文字が変わったくらいか！とおおよそどんなコードか理解できると思います。

さて、まだ説明をしていない部分をこれから説明していきます。

[reactions_add](https://api.slack.com/methods/reactions.add)を見ます。

![react_add.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1912865/ee3eb615-b897-842a-c374-67868ee17522.png)

`Required Scope`は、今回は**Bot tokens**を使用。このScopeは「準備」の時にすでに追加してあります。

reactions_addはこのように引数を指定すればOKです！

```python:
client.reactions_add(channel='リアクションをつけるチャンネルID', timestamp='リアクションをつけるタイムスタンプ', name='絵文字の名前')
```

<details><summary>**ここまでの全体のコードの確認**</summary><div>

```python:app.py
#環境変数を扱うためのライブラリ
import os
from dotenv import load_dotenv
#Botを動かすためのライブラリ
from slack_bolt import App
from slack_bolt.adapter.socket_mode import SocketModeHandler
#Web API methodsを使用するためのライブラリ
from slack_sdk import WebClient

#「.env」ファイルに書いた環境変数を扱えるようにする。
load_dotenv()
#「Web API methods」を使うための準備
client = WebClient(token=os.environ['SLACK_BOT_TOKEN'])
user_client = WebClient(token=os.environ['SLACK_USER_TOKEN'])
#アプリ（Bot）の初期化
app = App(token=os.environ['SLACK_BOT_TOKEN'])
#「残業」という言葉を含むメッセージがきたら消す機能
@app.message('残業')
def delete_zangyo(message, say):
    channel_id = message['channel']
    timestamp = message['ts']
    user_client.chat_delete(channel=channel_id, ts=timestamp)
    say('ん？なんか言ってた？聞こえなかったなぁ。')
    
#「ありがとう」という言葉を含むメッセージがきたらリアクションを追加する機能
@app.message('ありがとう')
def react_thank(message, say):
    channel_id = message['channel']
    timestamp = message['ts']
    client.reactions_add(channel=channel_id, timestamp=timestamp, name='+1')
    say('Peaceful Chat!!')
#アプリ（Bot）の起動
if __name__ == '__main__':
    SocketModeHandler(app, os.environ['SLACK_APP_TOKEN']).start()
```
</div></details>

ここまでかけたら、Botを起動させ、Botを招待したSlackのチャンネル上で、「ありがとう」というキーワードを送信してみてください！Botがメッセージを削除し、喧嘩を売ってくれます:thumbsup_tone1:

![ありがとう.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1912865/4518942a-a975-2892-10fe-e2ca306219a6.gif)

##6. キーワードを増やそう！

Botが反応するキーワードが「残業」と「ありがとう」だけでは物足らないですよね…。ということで、キーワードを増やしましょう:v_tone1:

キーワードの増やし方は、`@app.message('キーワード')`を増やすだけOKです！
機能の部分を次のように変更してみましょう！好きな言葉を追加してもらって大丈夫です！
ついでに、関数の名前も変えておきましょう（変えなくても問題なく動きます）。

```python:
#マイナスの言葉を含むメッセージがきたら消す機能
@app.message('残業')
@app.message('ハゲ')
@app.message('クサイ')
def delete_minus(message, say):
    channel_id = message['channel']
    timestamp = message['ts']
    user_client.chat_delete(channel=channel_id, ts=timestamp)
    say('ん？なんか言ってた？聞こえなかったなぁ。')

#プラスの言葉を含むメッセージがきたらリアクションを追加する機能
@app.message('ありがとう')
@app.message('お疲れ様')
@app.message('ラーメン')
def react_plus(message, say):
    channel_id = message['channel']
    timestamp = message['ts']
    client.reactions_add(channel=channel_id, timestamp=timestamp, name='+1')
    say('Peaceful Chat!!')
```
<details><summary>**最終的な全体のコードの確認**</summary><div>

```python:app.py
#環境変数を扱うためのライブラリ
import os
from dotenv import load_dotenv
#Botを動かすためのライブラリ
from slack_bolt import App
from slack_bolt.adapter.socket_mode import SocketModeHandler
#Web API methodsを使用するためのライブラリ
from slack_sdk import WebClient

#「.env」ファイルに書いた環境変数を扱えるようにする。
load_dotenv()
#「Web API methods」を使うための準備
client = WebClient(token=os.environ['SLACK_BOT_TOKEN'])
user_client = WebClient(token=os.environ['SLACK_USER_TOKEN'])
#アプリ（Bot）の初期化
app = App(token=os.environ['SLACK_BOT_TOKEN'])
#マイナスの言葉を含むメッセージがきたら消す機能
@app.message('残業')
@app.message('ハゲ')
@app.message('クサイ')
def delete_minus(message, say):
    channel_id = message['channel']
    timestamp = message['ts']
    user_client.chat_delete(channel=channel_id, ts=timestamp)
    say('ん？なんか言ってた？聞こえなかったなぁ。')

#プラスの言葉を含むメッセージがきたらリアクションを追加する機能
@app.message('ありがとう')
@app.message('お疲れ様')
@app.message('ラーメン')
def react_plus(message, say):
    channel_id = message['channel']
    timestamp = message['ts']
    client.reactions_add(channel=channel_id, timestamp=timestamp, name='+1')
    say('Peaceful Chat!!')

#アプリ（Bot）の起動
if __name__ == '__main__':
    SocketModeHandler(app, os.environ['SLACK_APP_TOKEN']).start()
```
</div></details>

お疲れ様でした:clap_tone1: Botの完成です！
Botを起動させて、ちゃんと動くか確かめてみましょう！
平和なSlackのチャットをお楽しみください:dancer_tone1:

#最後に

ここまで読んでくださり、ありがとうございます:relaxed:
今回のBotの作り方を応用すれば、勤怠管理やお知らせ、予定管理、ノルマ管理、トレーニング管理など、様々な機能を作ることができます。
今回は紹介できていませんが、Googleスプレッドシートと連携すると作れる機能がもっともっと増えそうです。是非挑戦してみてください:muscle_tone1:

僕はPythonを学び始めて5ヶ月の初心者なので、説明不足なところや、コードが未熟な部分があるかもしれません。そんな記事ですが、少しでもみなさんのBot制作の助けになり、多くの人にBot制作の楽しさを実感してもらえたら、嬉しいです。



#参考

**slack boltの基本的な使い方**

https://slack.dev/bolt-python/ja-jp/tutorial/getting-started

**slack Web API methodの種類**

https://api.slack.com/methods

**slack appの管理ページ**

https://api.slack.com/apps?new_app=1

#関連
その他の僕の書いた記事です。Bot制作の基本をより丁寧に説明してあります（今回は記事の分量の関係ですこし大雑把に説明しました）。

**slackの初期設定に関して（ワークスペースの作成から）**

https://qiita.com/hayahaya2/items/0787225d9fa66dbcdf4f

**Botのインストールから起動まで**

https://qiita.com/hayahaya2/items/d952455ff5fb20808262

**Botの簡単な機能の実装**

https://qiita.com/hayahaya2/items/9e2692ffe7695377aafe