読者です 読者をやめる 読者になる 読者になる

Facebook Messanger Platform での BOT の作り方

Facebook Messanger Platform が公開されて Facebook Messanger でも BOT を作っていたので手順などをまとめました。

BOT を作るまでなら公式ドキュメントを見ればだいたい分かると思います。

作成したものは golang + goji を heroku で動かしています。 LINE BOT 作成時よりも半分ほどの時間で簡単に作れました。

github.com

f:id:yone098:20160414041036p:plain:w300

手順

基本的には公式ドキュメントの通りですが公式ドキュメントは、英語版ダッシュボードなので実際に試してみると少し戸惑うかもしれませんが手順としては以下の通りです。

  1. Facebook ページを作成
  2. コールバック用のAPIを作成する
  3. facebook for developer の管理画面の左下の Messagenger を選択し Webhooks (コールバック)の設定
  4. facebook for developer の管理画面の左下の Messagenger を選択し 作成したFacebookページをプルダウンから選択しアクセストークンを生成
  5. メッセージ送信 BOT を作成する
コールバック作成

Webhooks の設定でコールバック URL を設定します。 Setup Webhooks ボタンを押すとダイアログが表示されるのでコールバックURLを入力し、verify tokenを自身で設定してください。
例)MY_VERIFY_TOKEN
コールバック URL の設定画面で、コールバック URL が正しいものが確認するために Facebook platform とやりとりする必要があります。
簡単に言うと、コールバック URL に特定のパラメータを飛ばすから、お前の作ったシステムからこっちが送ったものを正常に返せよ。それが正しかったら Webhook の設定認めるよというものになります。
特定の GET パラメータでリクエストされてくるので、対応したレスポンスを返すように作ってください。

golang だと以下のようになります。(goji 使ってます)

func handleGetCallback(c web.C, w http.ResponseWriter, r *http.Request) {
    log.Println("called callback GET")

    if err := r.ParseForm(); err != nil {
        http.Error(w, err.Error(), http.StatusInternalServerError)
        return
    }

    // get parameter
    token := r.Form.Get("hub.verify_token")
    log.Println("hub.verify_token:", token)

    challenge := r.Form.Get("hub.challenge")
    log.Println("hub.challenge:", challenge)

    if token == facebookVerifyToken {
        fmt.Fprintf(w, challenge)
        return
    }
    fmt.Fprintf(w, "OK")
}

正常にコールバックを返せていれば設定画面で正常に保存されて管理画面の方で完了アイコンが表示されるので、まずはコールバック連携として完了アイコンが表示されるまでは頑張ってみましょう。

f:id:yone098:20160414035023p:plain:w600

bot でメッセージを受信する

メッセージを受信するには、コールバックに設定したURLにPOSTされてくるのでメッセージをパースして受け取ります。
LINE BOT と同じで結局メッセージの構造を実際のデータを見ながら読み解くところが一番時間かかるところだと思います。

POST されてくる JSON は以下になります。

{
    "entry": [
        {
            "id": 1583495931963063, 
            "messaging": [
                {
                    "message": {
                        "mid": "mid.1460572581000:cc8dc67dbaa3b39316", 
                        "seq": 40, 
                        "text": "こんにちは"
                    }, 
                    "recipient": {
                        "id": 1583495931963063
                    }, 
                    "sender": {
                        "id": 1036935553049492
                    }, 
                    "timestamp": 1460572581014
                }
            ], 
            "time": 1460572581050
        }
    ], 
    "object": "page"
}

これをパースすればメッセージを取得出来ます。

func handlePostCallback(c web.C, w http.ResponseWriter, r *http.Request) {
    log.Println("recieve message from facebook messagenger")
    var msg facebookMsg
    b, err := ioutil.ReadAll(r.Body)
    if err != nil {
        http.Error(w, err.Error(), http.StatusInternalServerError)
        return
    }
    log.Println("msg:", string(b))
    err = json.Unmarshal(b, &msg)
    if err != nil {
        http.Error(w, err.Error(), http.StatusInternalServerError)
        return
    }

    for _, event := range msg.Entry[0].Messaging {
        if event.Message != nil {
            log.Println("Recieved msg:", event.Message.Text)
        }
    }

    fmt.Fprintf(w, "OK")
}

メッセージにImage や Video or Audio Attachment がある場合のメッセージフォーマットは以下を確認してください。

Webhook Reference - Messenger Platform - 参考資料 - 開発者向けFacebook

bot でメッセージ送信

送信時にはページのトークンを設定するだけで、メッセージのフォーマットさえ合わせればすんなり送信出来ます。

           m := sendMessage{
                Recipient: &id{ID: senderID},
                Message:   &text{Text: "ハゲ"},
            }
            b, err := json.Marshal(m)
            if err != nil {
                http.Error(w, err.Error(), http.StatusInternalServerError)
                return
            }
            req, err := http.NewRequest("POST", facebookPostURL+facebookToken, bytes.NewBuffer(b))
            if err != nil {
                http.Error(w, err.Error(), http.StatusInternalServerError)
                return
            }

            req.Header.Add("Content-Type", "application/json; charset=UTF-8")
            client := &http.Client{}
            res, err := client.Do(req)
            if err != nil {
                http.Error(w, err.Error(), http.StatusInternalServerError)
                return
            }
            defer res.Body.Close()

今回は、簡単なテキストメッセージを送信する BOT でしたが画像なども送れるので企業用 BOT を作るには良さそうです。