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 を作るには良さそうです。

LINE BOT API でのハマりどころ

LINE BOT API

こんにちは、🌸満開で花見日和ですね。 LINE BOT API が公開されたので golangbot を一番手軽に出来そうな環境 heroku 上で作ってみました。
プログラム自体は JSON をコールバックで受け取り、メッセージを送るにはセキュリティ情報を設定し所定のフォーマットで JSON を POST するだけなので特に難しいところはないですが環境面や設定でハマりそうなポイントがあったので、まとめておきます。
ソースコードのサンプルは PHP であれば、ネット上にたくさんあるようなので Qiita などで参考にしてみてください。

github.com

手順

  1. LINE developer に登録し BOT API を利用出来る準備を整える LINE Developers

  2. BOT が LINE メッセージを受け取った際のコールバック実装
    LINE BOT には証明書および固定 IP アドレスが必要になるので、手軽なのは heroku + Fixie で試すのが良いと思います。

ハマりポイント

LINE developer 登録などに関しては特に問題はないと思います。先着で人数制限があるようなので試したい方は早めに登録をした方が良いでしょう。
コールバック実装において私がすんなりいかなかったポイントを本BLOGに記載しておきます。 コールバック以外の API 実装に関しては、とにかく LINE からのレスポンスを確認すること。これに尽きます。ステータスコードとエラー内容(レスポンスの BODY)を常にデバッグ出力して API コール時の何に不備があるか確認してください。だいたいは以下のようになるでしょう。
・セキュリティの設定が不正(CHANNEL関連のキーを正しく HTTP ヘッダに設定出来ていない)
API コール時のパラメータが不正(メッセージを送る際に宛先が配列だったのに string 単体で送っていたのでメッセージ送信は何度か失敗しました)
・IP アドレスのホワイトリストが正しくない

Callback URL

LINE develper の設定画面から Callback URL を設定するのですが、ここで指定する URL は https であることと :443 のポート番号指定をする必要があります。
例)

https://yourdomain.com:443/bot/callback

設定し保存時に Not secure URL or port number not sepcified. とエラーメッセージが出た場合はプロトコルとポート番号を確認しましょう。

設定したコールバックが呼ばれない

私は1日ほどして正常にコールバックが呼ばれるようになりましたが、作成当初は bot にLINE メッセージをしてもコールバックが呼ばれず、何が悪いのか検討もつかなかったです。 しかし翌日になると正常にコールバックが呼ばれるようになったので、LINE developer で Callback URL を設定しても即反映ではなさそうですので気をつけてください。

Server IP Whitelist

BOT がメッセージ送信する際に許可する IP アドレスを登録するのですが、これも反映まで時間がかかるようです。
BOT がメッセージを送信した際に、LINE サーバからこのIPアドレスは許可してないよというレスポンスが届くのですがこちらも Fixie の画面から得られる IP アドレスを LINE developer 設定画面から設定してしばらくするまでは正常に BOT が機能しませんでしたので気をつけてください。 しばらくはメッセージ送信時には以下のようなレスポンスが返ってきます。([X.X.X.X]は実際のIPアドレス)

{"statusCode":"427","statusMessage":"Your ip address [X.X.X.X] is not allowed to access this API."}
成功時

メッセージの送信が成功した場合は以下のような JSON がレスポンスとして返ってくるので、このレスポンスが返ってくるまで諦めずに頑張りましょう。

{"failed":[],"messageId":"1460222924676","timestamp":1460222924676,"version":1}

f:id:yone098:20160410031824p:plain:w300

LINE さんに要望

コールバックが呼ばれない間は、何が問題で呼ばれないのかさっぱり分からずここで断念する人が結構いるのでは?と思いました。
管理コンソールなどで、エラーログだけ見られるようにしてもらえると非常に良いと思いました。

2015年 振り返り

ほとんど blog を書かなくなったでお馴染みの yone098 です、こんにちは。
今年は、仕事でずっと Go を書いていました。前半は Java も書いていましたが、後半は Go のみ書いていたように思います。
ムーミンソーシャルゲームのサーバサイドを実装し、別の開発でも Go1.5 を採用したので1年以上書き続けてやっと Go の事が少し理解出来たと思います。

6月

ムーミンソーシャルゲームをリリースしました。
開発基盤に libGDX を採用し iOS, Android ともに Java で開発することによって(iOS用には RoboVM を併用)、基盤の資産やノウハウもかなり溜まりました。
日本国内だと一番 libGDX での実績があるのではないでしょうか。
一緒に開発してみたいという方は是非、連絡してください。

ムーミン 〜ようこそ! ムーミン谷へ〜 - Google Play の Android アプリ https://play.google.com/store/apps/details?id=com.poppingames.moo

Dockerエキスパート養成読本が発売されました。

Dockerエキスパート養成読本[活用の基礎と実践ノウハウ満載!] (Software Design plus)

Dockerエキスパート養成読本[活用の基礎と実践ノウハウ満載!] (Software Design plus)

9月

Seasar Conference 2015 で Javaプログラマ向けに Go のことを話してきました。
久しぶりに Seasar でのイベントで懐かしい人に出会えて楽しかったです。
Seasar 全盛期に活躍していた人たちは、まだまだ現役であり第一線で活躍されていて嬉しく思いました。DJ HIGAYSUWO には今後も注目していきたいなと思います。
seasar.connpass.com

メンバー

会社のメンバーが少しずつ増えてきて優秀な人材を今年も数名を採用することが出来たのでとても嬉しく思っています。
2人とも20台前半で、とても若いのにプログラミング能力が非常に高いのでワクワクしているところです。
これからも宜しくお願いします!

バスケ

後半は仕事が忙しくなって練習に行けなかった時もありましたが大きな怪我もなく楽しくバスケ出来ました。
観る方は、NBA が面白すぎて去年 WOWOW に加入したんですが全試合観たいので nba.com の LeaguePass を遂に購入しました。
Live での試合も見れますが、終わった試合でも観ることが可能なのでお勧めです。今年は GSW の試合は全て観ています。

Subscription - NBA

ingress

仕事とバスケ以外では ingress でたくさん歩きました。1億APまではもう少し頑張りたいです! f:id:yone098:20151225124302p:plain:w300

ハイタン

今年は突然食欲が無くなって死んじゃうんじゃないかと思う時があって初めて病院に連れて行きました。
f:id:yone098:20151225131927p:plain:w300
点滴打ったらすぐ元気になりました。

今年のベスト動画です💙

食べるのに夢中で顔から落ちるやつ😆

Masaaki Yonebayashiさん(@yone098)が投稿した動画 -

まとめ

仕事もバスケも遊びも全てが楽しい1年でした。
今年残り少しと来年も頑張ります!