GoのFacebook Messenger Platform APIライブラリを作った

先日からLINE BOTで遊んでいたところ、今朝のfacebookのDeveloper Conferenceのf8でMessenger Platformが公開されたので、合わせてGoのライブラリを作ってみました。

github.com

いつものechobotはこんな感じです。

f:id:stanaka:20160413144318p:plain

Callback URLはhttps必須ですので、herokuなどで動かすのがお手軽でしょう。LINE BOT APIとは異なりIPアドレスホワイトリスト設定は不要なので、Fixieアドオンなどなしで普通にheroku上で動かすことができます。FB_TOKENとVerify用のFB_VERIFY_TOKEN環境変数として設定します。

また、最初にcallbackのverifyが必要ですので、その処理が入ってます。この処理はVerifyが終われば消しても問題ありません。もちろんCallback URLを変更する際は再度verifyが必要です。

facebook Messenger Platformは最初にfacebook appやpageを作ったりいろいろ手順が必要ですので、公式ドキュメントに沿って進めるのが良いでしょう。あと別の人がbotに話しかけてもcallbackが飛んでこなったりすることもあるようで、まだ細かい挙動や条件がよくわからないところもいくつかありますが、そのあたりの熟れてないのを味わうのも初物の面白さなので、いろいろ触って楽しむのがいいんじゃないかと思っています。

package main

import (
    "encoding/json"
    "fmt"
    "io/ioutil"
    "log"
    "net/http"
    "net/url"
    "os"

    fbmsg "github.com/stanaka/facebook-messenger"
)

var debug bool
var fb *fbmsg.FacebookMessenger

func callbackHandler(w http.ResponseWriter, r *http.Request) {
    b, err := ioutil.ReadAll(r.Body)
    if err != nil {
        fmt.Printf("Something wrong: %s\n", err.Error())
        return
    }
    if debug {
        log.Println("RecievedMessage Body:", string(b))
    }

    m, _ := url.ParseQuery(r.URL.RawQuery)
    fmt.Println(m["hub.verify_token"])
    if len(m["hub.verify_token"]) > 0 && m["hub.verify_token"][0] == os.Getenv("FB_VERIFY_TOKEN") && len(m["hub.challenge"]) > 0 {
        fmt.Fprintf(w, m["hub.challenge"][0])
        return
    }

    var msg fbmsg.CallbackMessage
    err = json.Unmarshal(b, &msg)
    if err != nil {
        fmt.Printf("Something wrong: %s\n", err.Error())
        return
    }

    for _, event := range msg.Entry[0].Messaging {
        sender := event.Sender.ID
        if event.Message != nil {
            fmt.Printf("Recieved Text: %s\n", event.Message.Text)
            err := fb.SendTextMessage(sender, event.Message.Text)
            if err != nil {
                fmt.Printf("Something wrong: %s\n", err.Error())
            }
        }
    }

}

func main() {
    debug = true
    fb = &fbmsg.FacebookMessenger{
        Token: os.Getenv("FB_TOKEN"),
    }

    http.HandleFunc("/fbbot/callback", callbackHandler)

    port := os.Getenv("PORT")
    addr := fmt.Sprintf(":%s", port)
    http.ListenAndServe(addr, nil)
}

GoのLINE BOT APIライブラリを作った

先日、LINE BOT API公開されたので、この波に乗らねば、というわけでいろいろ遊んでいます。最近はGoが手に馴染んでいるのでGoでbotを書いているのですが、皆さんもご存知の通り、JSONAPIをGoで扱うのはなかなか辛いものがあるので、ライブラリにしてみました。

github.com

こんな感じでecho botを書けます。まだテキスト部分しか実装してないので、これから画像などは実装していきます。 herokuで動かすことを想定していますので、お試しの際はLINE BOT をとりあえずタダで Heroku で動かす - Qiitaあたりを参考にデプロイしてみてください。

LINE BOT APIのアカウントの取得方法やcallbackが来ない!といったトラブルシュートは他にもいろいろ情報あがってますので、そちらでどうぞ。

package main

import (
    "fmt"
    "log"
    "net/http"
    "os"

    line "github.com/stanaka/line-bot-api"
)

func main() {
    http.HandleFunc("/callback", handler)
    port := os.Getenv("PORT")
    if port == "" {
        port = "8080"
    }
    addr := fmt.Sprintf(":%s", port)
    http.ListenAndServe(addr, nil)
}

func handler(w http.ResponseWriter, r *http.Request) {
    api := line.New(
        os.Getenv("LINE_CHANNEL_ID"),
        os.Getenv("LINE_CHANNEL_SECRET"),
        os.Getenv("LINE_MID"),
    )
    err := api.SetProxy(os.Getenv("PROXY_URL"))
    if err != nil {
        log.Println(err)
    }
    msg, err := api.DecodeMessage(r.Body)
    if err != nil {
        log.Println(err)
    }
    for _, result := range msg.Results {
        from := result.Content.From
        text := result.Content.Text
        err := api.SendMessage([]string{from}, text)
        if err != nil {
            log.Println(err)
        }
    }
    fmt.Fprintf(w, "OK")
}

facebookの13億ユーザーを支えるロードバランサーの話

最近、SREが話題ですね。

tech.mercari.com www.wantedly.com

ということでSREについて調べてたら、SREconなんてものが開催されていたので中を見てたら、「Building a Billion User Load Balancer」というタイトルでFacebookDNS〜LBまでの話があったので、そのメモです。

Building a Billion User Load Balancer | USENIX

tl;dr

  • tinydns + IPVS で Facebook規模はいける
  • httpsの接続確立はかなり重い(RTTの4倍 = RTT 150msとするとGETまで600ms)ので、太平洋越えとかは厳しい
  • httpsを終端させるCDNとかは活用の可能性ありそう (国内だけを考慮するなら影響は軽微かも)

メモ

  • L4 LB
    • shiv (IPVS + python-wrapper) を利用 (OSSにはなっていなさそう?)
    • IPIPカプセリングしてDSR(Direct Server Return)
  • L7 LB
  • ルータ 〜 L4 LB
    • BGPによるECMP Hash
  • L4 LB 〜 L7 LB
    • Consistent hashingで行なっている
  • DNS(tinydns)でロードバランシング
    • DCはUS 4ヶ所、EUに1ヶ所
  • POPを世界中にもっている
    • POPにはL4 LBとL7 LBを配置している
    • https接続は重いのでできるだけ近くで終端する必要がある
      • httpはtcpの3way + httpの1往復
      • httpsだとそこにtlsの2往復が加わってさらに厳しくなる
  • POP内のL7 LBから(別DC上の)アプリケーションサーバーへのhttpsは事前に張っておく
  • 5分に一度DNSマッピングを生成
# dig facebook.com
...
;; ANSWER SECTION:
facebook.com.       113 IN  A   173.252.120.68
...

SORACOM Airを使ってみた #soracom

本日発表されたSORACOM AirのSIMを事前に使わせていただけたのでトライしてみました。 サービスの詳細はTechCrunchの記事が詳しいので、そちらを参照してください。

jp.techcrunch.com

blog.soracom.jp

SORACOM Airの最大の特徴は、定額な基本料金(10円/日)と完全従量制による価格体系で、通信帯域を動的にAPIで変更できることです。 また容量は事前申請ではなく、AWSのように使った分だけの事後清算ですので、 うまい使い方をすればするほど安くなる特徴があります。

SIMはSORACOMの管理画面もしくはAmazonから買えます。 (初日から買えるのはすばらしい)

SORACOM Air SIMカード(データ通信のみ) (ナノ)

SORACOM Air SIMカード(データ通信のみ) (ナノ)

SORACOM AirDocomoのMVNOですのでSIMはDocomoのものと同様です。

f:id:stanaka:20150930122256j:plain

SIMを挿入して、さらっとAPNを設定します。

f:id:stanaka:20150930134634p:plain:w240

このSIMをアクティベートするとこんな管理画面が出てきます。

f:id:stanaka:20150930122228p:plain

ここの「Speed class」で速度をいつでも変更できます。これは新しい体験ですね!

SORACOMの価格体系

SORACOM Airの価格体系で一番安いs1.minimumは32kbpsで1MBあたり、上りが0.2円、下りが0.6円です。深夜時間帯料金も設定されていて、AM2-6は下りも0.2円とさらに安くなっています。

例えば、温度湿度センサーのデータ(1回1KBと仮定)を毎分SORACOM Air経由で送るとすると、データ量は

{1024 (Bytes) \times 43200 (回) \approx 44.19 MB}

ですので、

{44.19 (MB) \times 0.2 (円/MB) \approx 8.44 円}

となります。実際には深夜時間帯価格がありますので、さらにお安くなります。 基本料金と合わせて、月額309円(1ヶ月30日の場合)で3G通信を利用したセンサー環境を構築できます!

もうすこし早いs1.standardの512kbps(上り0.24円/MB 下り0.8円/MB)を利用したとしても従量部分は10.13円となりますので、 それほど変わらずに構築できます。 また1分間隔ではなく、10秒間隔としても従量部分が約50.63円で計351円と、従来の格安SIMと比べても十分お安いですね。

もっとも普通のスマホに差して、1GB, 2GBと使うとそれなりの価格になってきます。 たとえば、2Mbpsのs1.fastで下り1GB使うと従量部分が1024円になり、さらにそれ以上使う可能性があるのであれば、他の格安SIMのほうが安いケースが多そうです。 ちなみにスマホで使う場合、s1.minimum(32kbps)はさすがに遅いので、s1.standard(512kbps)は必要でしょう。

Raspberry Piで使いたい!

これを個人で遊ぼうとすると、Raspberry Piに3Gモジュールを差して、という話が多そうです。 こちらはまだ試せていないので、早めに試してみたいと思っています。 ただ、いまの3Gモジュールは小ロットではけっこう高く( 3GPI(Raspberry Pi 向け3G通信モジュール) だと1個購入時 29,800円)て、 これにさらにいろいろ加えて一通りの機能を持ったものを作ろうとするとそれなりの値段になってしまいます。

ASUS Zonefone2はあれだけの機能が詰まっていて 24,835円 (参考 expansys) なので、3Gモジュール部分がこれからどんどん値が下っていって例えば10,000円を切るところまでいってくれると、個人や、小ロットのビジネスでも手が出しやすくなるので、期待したいです。

SORACOMの中身

先日、中身の話を聞く機会があったのですが、TechCrunchの記事にもあるように、従来は専用ハードウェアだった内部システムは全てAWS上にソフトウェアで構築されているそうです。 従来はハードウェアが必要だったところをソフトウェアのみで実装することでイノベーションを起こす、という流れでしょう。 CTOの安川さんに聞いたところ、ソフトウェア自体は3GPPの仕様は公開されているので、作ること自体はすごく難しくはないですよ、とのことでした。このあたりを頑張ると自分でもDocomoとの接続まで含めてできそうですね!

ascii.jp

またデータストアはDynamoを駆使し、通信のコア部分はCで書いている、とのことです (安川さんの出身からErlangが期待されるのですが(笑))。 AWS出身のエンジニアが多いのでAWSを使い方についても気になるところです。 DynamoDBもたまに障害を起こすので、そのような場合に備えてAWSの裏表を知りつくしたエンジニアがどのような設計をしているか是非知りたいですね!

さいごに

今日からいろいろブログエントリもあがってきますので、いろいろな使い方が広がっていくことを楽しみにしています!

blog.soracom.jp

追記(9/30)

温度湿度センサーによるデータ通信費の試算が上りの料金を使うべきところを下りの料金を使っていましたので、正しく上りの料金を使うように修正しています。