Facebook Messenger API + Flask 備忘録 [実装編 1]

[導入編](https://qiita.com/nozomiyamada/items/5243a9b390bf36922680) からの続き、とりあえず簡単なオウム返しから。

[pymessenger](https://pypi.org/project/pymessenger/) というサードパーティー製のライブラリもあるようだが、2016年から更新されていないので、勉強も兼ねて今回は自分で実装する。

# POSTリクエストの中身

詳しくはこちら: [Webhook Events](https://developers.facebook.com/docs/messenger-platform/reference/webhook-events), [Send API](https://developers.facebook.com/docs/messenger-platform/reference/send-api) 

全部読むのは面倒なので、必要な部分だけ使いながら実装していく。

API の webhook の本体はこんな感じ。まずはユーザーがアプリに送信した場合

~~~python
{
  'object': 'page',
  'entry': [
    {
      'id': '<FBページのID>',
      'time': 1607767826503,
      'messaging': [
        {
          'sender': {
            'id': '<FBユーザーのID>'
          },
          'recipient': {
            'id': '<FBページのID>'
          },
          'timestamp': 1607767826302,
          'message': {
            'mid': 'xxxxxxxxxxxxxxxxxxxxx',
            'text': 'hello'
          }
        }
      ]
    }
  ]
}
~~~

自分のページ側のIDは既に知っているので取得する必要はない。つまり `['entry']['messaging']` の中の `['sender']['id']` および `['message']['text']` を取り出せば良い。ここで `['entry']` はイベントの配列、 `['entry']['messaging']` はメッセージの配列になっているので for 処理などが必要。とは言っても、試した限りでは未受信のメッセージが溜まっていても同時にまとめて送られることはなく、一件のみだった。

また、メッセージの直後にほぼ同じ形式でウォーターマークが送られる。こちらには `['message']` の代わりに `['delivery']` がある。

~~~python
{
  'object': 'page',
  'entry': [
    {
      'id': '<FBページのID>',
      'time': 1607767829878,
      'messaging': [
        {
          'sender': {
            'id': '<FBユーザーのID>'
          },
          'recipient': {
            'id': '<FBページのID>'
          }, 
          'timestamp': 1607767829912,
          'delivery': {
            'mids': [
               'xxxxxxxxxxxxxxx'
             ],
             'watermark': 1607767827879
          }
        }
      ]
    }
  ]
}
~~~

一方返信用の中身はこれだけで十分。これに発行したトークンを `access_token` としてパラメータで与え、ヘッダーに `Content-Type: application/json` を指定すればOK。

~~~python
{
  "recipient": {"id": "<FBユーザーのID>"},
  "message": {"text": "hello"}
}
~~~

# おうむ返し実装

前回のコードに、POST 部分を追加する。後々のことを考えて、`send_message()` という関数を分けて作っておく。

ちなみにここで取得した `userID` というものは、FacebookアカウントのIDとは全く別物で、Page-scoped ID と呼ばれるらしい。自分のIDを知るためにも、返信文の中に埋め込む。


~~~python3
import os, json
from flask import Flask, request
from dotenv import load_dotenv
load_dotenv() # 環境変数ファイル .env の読み込み
ACCESS_TOKEN = os.getenv('ACCESS_TOKEN') # .env に記載
VERIFY_TOKEN = os.getenv('VERIFY_TOKEN') # .env に記載

@app.route('/callback', methods=['GET', 'POST'])
def receive_message():
    if request.method == 'GET': # webhook テスト用 GET
        if request.args.get("hub.verify_token") == VERIFY_TOKEN:
            return request.args.get("hub.challenge")
        else:
            return 'ちがうよ'
    elif request.method == 'POST': # POSTリクエスト -> 返信処理
        body = request.get_json() # 中身取得 
        for entry in body['entry']: # iterate 'entry'
            for messaging in entry['messaging']: # iterate 'messaging'
                userID = messaging['sender']['id'] # ユーザーのID
                if messaging.get('message'): # メッセージ以外を除外
                    received_message = messaging['message']['text'] # テキスト
                    response = f'{userID}さん, {received_message}' # おうむ返し文
                    send_message(userID, response) # 返信実行
        return "finished" # なんでも良い。返信内容とは無関係だが、無いとエラーになり返信を繰り返す

# 返信リクエスト用関数
# 失敗したらログに出力, Heroku なら heroku logs --tail で確認可
def send_message(userID, message): 
    result = requests.post("https://graph.facebook.com/v9.0/me/messages",
        params={"access_token": ACCESS_TOKEN},
        headers={"Content-Type": "application/json"},
        data=json.dumps({
            "recipient": {"id": userID},
            "message": {"text": message}
        })
    )
    if result.status_code != 200:
        print('送信失敗だよ:', data)
~~~

とりあえずこれで、このようなおうむ返しが可能になる。

![Screen Shot 2020-12-12 at 7.17.53 PM.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/727634/4dc3c1d3-890d-195e-64de-c5befda54079.png)



# POST テスト

メッセージの送信テストをする際に、いちいち Python のプログラムをデプロイするのは非常に面倒なので、専用のツールを使って試したい。

Facebook はブラウザ上で API の動作を試せる [Graph API Explorer](https://developers.facebook.com/tools/explorer/) なるものをわざわざ作ってくれておりこれだけでもいろいろできるのだが、やはり保存したり再利用したりすることを考えて、ド定番のリクエストツール [Postman](https://www.postman.com/) を利用する。

POST を選んで URL に `https://graph.facebook.com/v9.0/me/messages`
Params の KEY に `access_token`, VALUE に発行した長いアクセストークンを入力
![Screen Shot 2020-12-12 at 6.14.26 PM.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/727634/66280bbc-ca37-ffe4-1c36-2f9d4faee43f.png)
Headersは `Content-Type` と `application/json`
![Screen Shot 2020-12-12 at 6.16.17 PM.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/727634/1e9c38c7-e0a9-97d3-a530-e39d8fbf1b83.png)
最後に Body は raw を選んで、送りたいメッセージを json 形式で記入する。先ほどのプログラムで取得した自分のIDを入力する。
![Screen Shot 2020-12-12 at 6.18.06 PM.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/727634/7bcb0d3c-59c9-48a7-911e-a412043afa41.png)

このように、簡単にメッセージを送信できる。これ自体は上で実装した関数 `send_message()` を用いてももちろん可能。 

![Screen Shot 2020-12-12 at 7.29.58 PM.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/727634/409be79e-e168-ef60-d932-d7dab4e6855f.png)