Slack Botを使ってドア開閉システムの構築（の途中）その１

# はじめに
今回はSlack Botを用いて部屋の鍵の施錠や開錠を行うシステムを構築しようとしたのでその記録を残しときます
Slack botを作成して送信したメッセージに応じて返信するところまで書きます（時間足りなかった…）

# 注意
筆者はごりごりのプログラミング初心者で間違っているところや適当なところ、必要のないことをしているかもしれませんが温かい目で見ていただけただ幸いです
また、本記事を真似するのは自己責任でお願いします（一応）

# もくじ
1.開発環境
2.システム概要
3.Webhookされるサーバーの作成
4.サーバー上の確認コード作成
5.Slack Botの作成
6.実際のサーバーのコード作成
7.ESP32上のコード作成
8.動作確認
9.あとがき

# 1.開発環境
Ubuntu 20.0.4
python3.8.10
ngrok 2.3.40


# 2.システム概要
今回作成したシステム概要図を下に示します
![新規 Microsoft PowerPoint プレゼンテーション.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2406056/56d43465-2bb4-dd8a-22be-ba7089f76102.png)






# 3.Webhookされるサーバーの作成
Ubuntuのインストールや基本的なセットアップは終了してるものとして話していきます
### 3.1 ngrokのインストール
まずコマンドを使ってngrokをインストールします

```
sudo snap install ngrok
```
次のコマンドでインストールがしっかりできてngrokが仕事してくれることを確認します(80は使用するポート番号)

```
ngrok http 80
```

このコマンドを打つと次のように表示されていたらインストールは成功しています。

```
ngrok by @inconshreveable                                       (Ctrl+C to quit)
                                                                                
Session Status                online                                            
Session Expires               1 hour, 59 minutes                                
Version                       2.3.40                                            
Region                        United States (us)                                
Web Interface                 http://127.0.0.1:4040                             
Forwarding                    http://2467-2400-4150-52e2-6200-264b-feff-fe90-8af
Forwarding                    https://2467-2400-4150-52e2-6200-264b-feff-fe90-8a
                                                                                
Connections                   ttl     opn     rt1     rt5     p50     p90       
                              0       0       0.00    0.00    0.00    0.00      
                                                                             
```
ちなみにこの状態で「http://127.0.0.1:4040 」にアクセスできることも確認しておきましょう


ただ、この状態では少し問題があってどうやらログインしないと2時間で自動的に終了してしまうようなので[ここ](https://ngrok.com/)でアカウント登録します(無料版で大丈夫です)

アカウント登録後に表示される[この](https://dashboard.ngrok.com/get-started/setup)サイトの「Connect your account」という欄に書いている

```
ngrok authtoken {authToken}
```
の欄をコピペしてコマンドに打ち込んでください
その状態で先程の

```
ngrok http 80
```

を入力するとその実行結果が少し変わって時間制限がなくなります
とはいえ無料版にはIPが固定できない欠点があり、ngrokを起動するたびにURLが変わってしまいます

```
ngrok by @inconshreveable                                       (Ctrl+C to quit)
                                                                                
Session Status                online                                            
Account                       ｛ここが時間からアカウントに変化する｝(Plan: Free)               
Version                       2.3.40                                            
Region                        United States (us)                                
Web Interface                 http://127.0.0.1:4040                             
Forwarding                    http://0d77-2400-4150-52e2-6200-264b-feff-fe90-8af
Forwarding                    https://0d77-2400-4150-52e2-6200-264b-feff-fe90-8a
                                                                                
Connections                   ttl     opn     rt1     rt5     p50     p90       
                              0       0       0.00    0.00    0.00    0.00      
```
これでngrok自体のインストールと初期設定は終了です。

# 4.サーバー上の確認コード作成
次にこのwebhookされるサーバー（今作ったやつ）にアクセスされたときに実行されるコードを作っていきたいと思います。
まず最初にSlackのEvent APIにWebhookとして登録するときにサーバーの存在確認用のリクエストが来るため、それに対応するコードを書く必要があります。（詳しくは[公式ドキュメント](https://api.slack.com/apis/connections/events-api#the-events-api__subscribing-to-event-types__events-api-request-urls__request-url-configuration--verification)を見てね）
公式ドキュメントによると以下のようなPOSTリクエストが来ます。（公式ドキュメントからの引用なので実際に来る際は少し違うかもしれません）

```
{
    "token": "Jhj5dZrVaK7ZwHHjRyZWjbDl",
    "challenge": "3eZbrw1aBm2rZgRNFdxV2595E9CY3gmdALWMmHkvFXO7tYXAYM8P",
    "type": "url_verification"
}
```

ここでtokenが[Slack App](https://api.slack.com/apps/)のBasic Informationで確認できるtokenと一致しているか、challengeの内容がそのまま帰ってきているか、typeがurl_verificationと一致しているかの３つの条件を満たすコードを用意する必要があります。

まずそのコードに使用するライブラリをインポートしていきたいと思います。

```
sudo apt install python3-pip
```
```
pip install flask
```

その用意するコードのサンプルがこちら(わかりやすいように好きなところにフォルダを作ってそこに作成してください)

```py
import flask
from flask import request, Response
import os
import json

app = flask.Flask(__name__)

#動作確認用
@app.route('/')
def main():
    return "Hello world!"

#実際にSlackからの認証用に使うところ
@app.route('/slackCheck', methods=["POST"])
def index():
    data = request.data.decode('utf-8')
    data = json.loads(data)
    # for challenge of slack api
    if 'challenge' in data:
        token = str(data['challenge'])
        return Response(token, mimetype='text/plane')
    # for events which you added
    if 'event' in data:
        print("get event")
        event = data['event']
        if 'user' in event:
            print("user = ", event["user"])
        if "text" in event:
            print("text = ", event["text"])
    return Response("nothing", mimetype='text/plane')

#ここのポート番号は好きにしてね。基本は80とか8080とか使うのかな？知らんけど
port = os.getenv('VCAP_APP_PORT', '30240')


if __name__ == '__main__':
    app.run(host='0.0.0.0', port=int(port), debug=True)
```

また好きなポート番号を使っている人は

```
sudo ufw allow 30240/tcp
```

```
sudo ufw reload
```
でポートを開放しておいてください
それができたら
```
ngrok http 30240
```
で実際にpythonのプログラムを動かした状態でngrokによって作られたForwarding後のURLでアクセスして「Hello World!」が表示されることを確認してください

```
ngrok by @inconshreveable                                                                                                                                                                  (Ctrl+C to quit)
                                                                                                                                                                                                           
Session Status                online                                                                                                                                                                       
Account                       ??????(Plan: Free)                                                                                                                                          
Version                       2.3.40                                                                                                                                                                       
Region                        United States (us)                                                                                                                                                           
Web Interface                 http://127.0.0.1:4040                                                                                                                                                        
Forwarding                    http://4068-2400-4150-52e2-6200-264b-feff-fe90-8afa.ngrok.io -> http://localhost:30240                                                                                       
Forwarding                    https://4068-2400-4150-52e2-6200-264b-feff-fe90-8afa.ngrok.io -> http://localhost:30240                                                                                      
#ここの上にあるやつです→→→→→→→→→→→→→→→→→→↑
                                                                                                                                                                                                          
Connections                   ttl     opn     rt1     rt5     p50     p90                                                                                                                                  
                              0       0       0.00    0.00    0.00    0.00                                                                                                                                 
                                                                                                         
```

ここまでで一旦サーバー側の操作は終了です。（今作ったURLは後々使うので起動したままでほっといてね）
ここからはSlack botの作成に移ります

# 5.Slack botの作成
slackにブラウザでログインした状態で[ここ](https://api.slack.com/apps)にアクセスしてください
アクセスしたら画面中央の「Create an App」をクリックしてください
![Desktop Screenshot 2022.02.25 - 01.58.55.69_LI.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2406056/47b8726a-0788-7f0b-5eb5-08aa50ecca01.jpeg)
クリックしたら次の画像のように２つ候補が出てくると思うので「From scratch」を選択してください
![Desktop Screenshot 2022.02.25 - 02.01.13.99_LI.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2406056/ce04d3be-4bd5-6c69-214a-98ab556f5b45.jpeg)
選択後はそのAppの名前（①）と入れるworkspace（②）を選択します
![Desktop Screenshot 2022.02.25 - 02.01.45.41_LI.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2406056/2baea3b0-4ea3-1568-4837-cd7d1c27f8db.jpeg)
選択後はAppの設定画面に入ることができます
ここから
①Permissions
②Bots
③Event Subscriptions
の順に設定していきたいと思います。
![Desktop Screenshot 2022.02.25 - 02.02.03.60.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2406056/f169e911-f5eb-da10-d250-a85f24f1f771.png)
まず「Permissions」をクリックしてサイト中ほどまでいって「Add an OAuth Scope」をクリックして今回やりたいことに応じたScopeを追加してください
ここで追加するものは[OAuth Permission scopes](https://api.slack.com/legacy/oauth-scopes)を参考にしてください
また今回は「channels:history」「chat:write」「reactions:write」「channnels:join」の4つを追加しました
![Desktop Screenshot 2022.02.25 - 02.12.57.09_LI.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2406056/f1db526f-8ee6-484e-3e48-364b18c5aa5e.jpeg)

次に最初の画面に戻ってBotsの設定に入ります。（左のBasic Information→Add features and functionalityでさっきの画面が見れます）
ここで「Add Display Name」の「Edit」から
・Display Name(Slack上で表示される名前)
・Default username(ちょっとよくわかんないです)
の２つを入力して、「Always Show My Bot as Online」をONにしておいてください
![Desktop Screenshot 2022.02.25 - 02.26.07.06_LI.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2406056/f94f547d-dee0-eaa6-9ce8-47e62f0fe3df.jpeg)
その後「Basic Information」に戻り「Install to Workspace」をクリックしてそのままの流れでインストールしちゃってください
![Desktop Screenshot 2022.02.25 - 02.31.09.14_LI.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2406056/95d9f9dd-eecd-99e8-b836-f8c563d6d52a.jpeg)

次に、また「Basic Information」に戻り、「Display Information」というSlack上で確認できるAppの情報を編集します
ここも好きに入れちゃってください
![Desktop Screenshot 2022.02.25 - 02.34.46.83_LI.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2406056/2e944735-a166-5725-9890-376d435f0497.jpeg)
次に「Event Subscriprions」というこのslack botのサーバーを設定していきたいと思います（さっき作ったURLはここで使います）
「Basic Information」に戻り、「Event Subscriprions」をクリックしてください
ここで「Enable Events」をONにした後、「Request URL」に先ほど作ったURLを入れてください
（今回の私の場合は「http://4068-2400-4150-52e2-6200-264b-feff-fe90-8afa.ngrok.io/slackCheck」になります）
![Desktop Screenshot 2022.02.25 - 02.39.22.66_LI.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2406056/18c477fe-0733-6f59-6425-8213d48ba2cf.jpeg)
入力直後に確認が始まり、確認が取れると「Verified」と表示されサーバーとの紐づけは完了です。
一度紐づけしてしまえば、サーバーを止めてもまた起動すれば大丈夫です
![Desktop Screenshot 2022.02.25 - 02.41.26.93_LI.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2406056/84eec247-c8b8-7c31-1979-6c5a1a1f8e15.jpeg)
その後、同じページのちょっと下にある「Subsceribe to events on behalf of users」にさせたい動作を登録してください。
この際、「Required Scope」に先ほど「Permissions」の時に追加したScope以外のものが入っていないか確認し、入っていたらScopeを追加してください。

今回は「message.channels」を追加しました。

ここまでで基本的なSlack Appの準備は終了になります。(書くの忘れてたけどこまめにSave Changesしてね)


# 6.実際のサーバーのコード作成
ここまでで環境つくりは終わりです。
ここからは実際にSlackから「施錠/開錠」のメッセージが来るとESP32へ送るメッセージを作るプログラムをサーバーに作っていきたいと思います。
とはいえ、いちいち説明するのもめんどくさいので完成品を一気にばんと出しときたいと思います。
（EPS32周りのコードはまだいじってないです＋returnがなんちゃらでエラー出るし結構汚いし…でも動くからいいよね！！！）
コード内に出てくる「channnel」ってのは入れたいチャンネルのとこコピペで調べれます
![Desktop Screenshot 2022.02.25 - 07.06.01.53_LI.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2406056/0dd12508-4722-bbf6-5fe2-093c0f8b96e4.jpeg)

```py
from cgi import print_environ_usage
import flask
from flask import request, Response
import os
import json
import requests

app = flask.Flask(__name__)
previous = "施錠"
global lockState = 0  #0がしまってて、1が空いてる

#動作確認用
@app.route('/')
def main():
    return "Hello world!"

#実際にSlackからの認証用に使うところ
@app.route('/slackCheck', methods=["POST"])
def index():
    TOKEN = '????'
    CHANNEL = '???'

    url = "https://slack.com/api/conversations.history"
    headers = {"Authorization": "Bearer "+TOKEN}
    params = {
        "channel": CHANNEL,
        "limit": 1
    }
    r = requests.get(url, headers=headers, params=params)
    json_data = r.json()
    messages = json_data["messages"]
    global previous #ここらへんめんどくさかったのだ
    global fuser
    for i in messages:
        print(i["text"])
        word = i["text"]

        if word == previous:
            url1 = "https://slack.com/api/chat.postMessage"
            data = {
               "token": "????",
               "channel": "???",
               "text": "変化なしだぞ"
            }
            requests.post(url1, data=data)
        elif word != previous:
            if word == '開錠':
                lockState = 1
                url2 = "https://slack.com/api/chat.postMessage"
                data = {
                   "token": "????",
                   "channel": "???",
                   "text": "開けたよ"
                }
                requests.post(url2, data=data)
                previous = word

            elif word == '施錠':
                lockState = 0
                url3 = "https://slack.com/api/chat.postMessage"
                data = {
                   "token": "????",
                   "channel": "????",
                   "text": "閉めたよ"
                }
                requests.post(url3, data=data)
                previous = word

            else:
                fuser = i["user"]
                if fuser == '?????': #これはBOTの無限ループを防ぐためのやつ(botのuserを入れてね)
                    break
                else:
                    url4 = "https://slack.com/api/chat.postMessage"
                    data = {
                       "token": "????",
                       "channel": "???????",
                       "text": "ちゃんとしてよ"
                    }
                    requests.post(url4, data=data)                

    return word ,lockState
#ここのポート番号は好きにしてね。基本は80とか8080とか使うのかな？知らんけど
port = os.getenv('VCAP_APP_PORT', '30240')


if __name__ == '__main__':
    app.run(host='0.0.0.0', port=int(port), debug=True)

#ESP32が情報求むするとこ
@app.route('/esp32')
def esp():
    return "Hello world!"
```

# 7.ESP32上のコード作成
ここでは一定間隔ごとにサーバーに対してリクエストを送り、現在の鍵の状態をレスポンスしてもらってその状態によって鍵の開閉をするプログラムを作りました。
```
今回はパス（ちょっと待ってね）
```

# 8.動作確認
一応動作させたときのslackの写真だけはっときます
![InkedIMG_7230_LI.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2406056/03368d5e-61f2-6690-ffd2-eadc0a54fcfa.jpeg)

# 9.あとがき
最後はesp32がサーバーに定期的にhttp getして状態見てってのを作って終わりです
ちょっと話それるけどubuntuにgeforceのドライバ入れた瞬間ネットが何やってもつながらんくなったのいったいなんやったんやろ？あれさえなければ…