Twilioを使ってシンプルなCrypto Botを作る方法

### [作るもの]
スマートフォンで決められたメッセージをTwilioで取得した番号に送ると、指定した暗号資産のシンボル（BTC、ETH等）現在価格を送ってくるシンプルなBot。

![8lk02e1h7wy4fiotnsj7.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/24723/d420391c-452b-c18c-35d1-0af1cc0f6fc1.jpeg)



### [事前準備]
- Twilioの開発者アカウント（TrialでOK）
- python3がインストール済み
- pipがインストール済み
- ngrok (https://ngrok.com/download)
- スマートフォンSMSが送れる

### [使用した開発環境]
- macOS BigSur
- VSCode
- iTerm2
- Poetry

今回はコードをデプロイしないので、python3がインストールされていて、ターミナルもしくはコマンドラインツールが使えれば、問題ないと思います。
Poetryに関しても必須ではないです。

Poetryとは
https://python-poetry.org/

### Step1 Twilioで番号を取得
Twilioにログインした状態で、コンソールページ(https://console.twilio.com/?frameUrl=/console) にアクセスすると下記のイメージのようなサイドバーが表示されます。

![sy3tgbia6jqo25oj97so.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/24723/d1351c69-78d4-c47b-fcf3-09d98b19a9e9.png)


`Phone Numbers` > `Manage` > `Active numbers`をクリックすると、番号を既に取得している場合は番号が表示されます。
番号を持っていない場合は番号を取得してください。検索すると、空いている番号が表示されます。

### Step2 プロジェクトの作成
ここでやることはプロジェクト用のフォルダを作成して、2つのPythonファイルの作成及び必要なpythonパッケージをインストールすること。

##### Poetryを使う場合
```zsh
$poetry new twilio_crypto_bot
$cd twilio_crypto_bot
$poetry add twilio flask requests cryptocompare 
```

##### Poetryを使わない場合
```zsh
$mkdir twilio_crypto_bot
$cd twilio_crypto_bot
$pip install twilio flask requests cryptocompare
```

##### ファイルの作成
もちろんエディタ経由で作成するのでも、全く問題ありません。

```zsh
$ touch app.py CryptoPrice.py
```

### Step3　コードを書く

`app.py`
packageをインポートして、Flaskで簡単なAPIを書くのと似ています。
Twilioのパッケージを利用して、スマホから送られてきたメッセージを受け取って、
そのメッセージの中に`price`という文言がある場合に暗号資産のシンボルを利用して、cryptocompareパッケージ経由で現在の価格を取得して、価格をメッセージとして返信するという非常にシンプルなものです。
`check_numeric`は暗号資産の価格の取得するプロセスの中で存在しない暗号資産について価格取得をしようとすると`CryptoPrice.py`でメッセージを返すようにしてあるので、存在する暗号資産の価格取得との処理の切り分けをするのに使っています。

```py
from flask import Flask, request, redirect
from twilio.twiml.messaging_response import MessagingResponse
import CryptoPrice
import requests
import re

app = Flask(__name__)


def check_numeric(s: str):
    pattern = r"^[+-]?[0-9]*[.]?[0-9]+$"
    return (re.match(pattern, s) is not None)


@app.route('/sms', methods=['GET', 'POST'])
def sms_reply():
    incoming_msg = request.values.get('Body', '').lower()
    resp = MessagingResponse()
    msg = resp.message()
    responded = False
    # print(incoming_msg)

    if 'price' in incoming_msg:
        # return cryptocurrency price
        crypto = incoming_msg.replace('price', '').strip().upper()
        cc = CryptoPrice.Crypto(crypto)
        crypto_price = str(cc.get_crypto_price())
        # print(crypto_price)

        if not check_numeric(crypto_price):
            response = crypto_price
        else:
            response = f"the current price of {crypto} is ${crypto_price} from cryptocompare"
        print(response)
        msg.body(response)
        responded = True

    if not responded:
        msg.body('what do you want to know about cryptocurrencies?')

    # print(str(resp))
    return str(resp)


if __name__ == '__main__':
    app.run(debug=True)
```

`CryptoPrice.py`
main()はCryptoクラスが正しく動いているか確認するためだけのものです。
この場合、存在しない暗号通貨のシンボルを送っているので、エラーメッセージが返ってきます。
ちなみに、日本円で価格を受け取りたい場合は`result[self.crypto_symbol]['USD']`はUSDを`JPY`に変えてください。

```py
import cryptocompare


class Crypto():
    def __init__(self, crypto_symbol):
        self.crypto_symbol = crypto_symbol

    def get_crypto_price(self):
        result = cryptocompare.get_price(self.crypto_symbol, currency='USD')
        try:
            return result[self.crypto_symbol]['USD']
        except TypeError as error:
            return 'There is not a coin pair for {}-USD.'.format(self.crypto_symbol)


def main():
    test = 'KOJI'
    cc = Crypto(test)

    val = cc.get_crypto_price()
    print(val)


if __name__ == '__main__':
    main()
```

### Step4 Webhookの設定
現状、app.pyを動かしてもTwilioの番号と繋がっていないので、スマホからメッセージ送っても、エラーメッセージがTwilioから届くだけです。
そのため、Webhookを設定する必要があります。

Webhookについてはこちらの記事が参考になります。
https://qiita.com/soarflat/items/ed970f6dc59b2ab76169

Webhookの設定のために、ローカル環境でapp.pyを動かして、ngrokで外部からアクセスできるようにして、そのURLをターゲットとしてTwilioのコンソールページで設定する必要があります。

```zsh
# with poetry
$ poetry run python app.py

# without poetry
$ python app.py
```

プロジェクトフォルダで上記を実行すると、ポート番号5000でサーバが立ち上がります。
次にngrokを使います。ngrokをダウンロードして、プロジェクトフォルダに移しておくと実行コマンドが短くてすみます。

新しいタブもしくはウィンドウを開いた上で、下記を実行します。

```zsh
$./ngrok http 5000
```

問題がなければ、下記のような画面が表示され、http httpsのURLが表示されますので、httpsの方をTwilioに登録します。
![icmgic3imgyv1b4xqa21.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/24723/131e5e84-1d39-a836-a22c-84125f620965.png)


番号取得の時にアクセスしたコンソールページで番号を表示して、使いたい番号をクリックします。すると、Messagingの箇所にWebhookを指定する場所があるので、`ngrokが表示しているURL/sms`をターゲットとして入力して、設定を保存します。

![ftm8h8t8xahyqzo069pq.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/24723/7905e22a-2a37-0caf-d568-67d3617f98ae.png)


### Step5 スマホからメッセージを送る
Step4でTwilioの番号とローカルのプログラムを関連づけが終わったので、スマホから取得した番号に`Price BTC`というメッセージを送ります。
うまくいくと、ビットコインの現在価格がTwilioの番号から送られてきます。
ちなみに、存在しない暗号資産のシンボルを送るとそんなのないよというメッセージが飛んできます。


![8lk02e1h7wy4fiotnsj7.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/24723/d420391c-452b-c18c-35d1-0af1cc0f6fc1.jpeg)


TwilioのAPIなりpythonコードでメッセージを解析するような処理を追加すれば、より自然なやり取りで価格の取得が可能になります。
また、特定の価格を超えるもしくは下回るというようなアラート機能も追加可能です。
ちなみに、cryptocompareパッケージだと、APIを叩く間隔は30秒以上開ける必要があります。もう少し、短いスパンで価格のチェックを行いたい場合はCoinbaseのAPI(15秒)を利用することをお勧めします。