http://developers.facebook.com/docs/chat/

あなたのwebベース/デスクトップ/モバイルのインスタントメッセージアプリケーションにFacebook Chatを実装する事が可能です。クライアントはJabber/XMPPサービスを通じてFacebook Chatと繋がることになります。このドキュメントは、Facebook Chatクライアントを実装しようとする開発者に対して、Facebook ChatのXMPPプロトコルの機能と制限について解説しています。

このドキュメントを読む前に、XMPPチャットクライアント/サーバのコンセプトや用語について理解してください。Jabberクライアントが100,000セッション以上をホストすると予測している場合は、Developer Help Contact Form へ行ってJabberを選択して我々に知らせてください。



34156_142606822423061_109700069047070_407590_1912553_n


Features and Limitations

Facebook Chatは下記の機能をサポートしています。
  • Facebook Platform authentication using the X-FACEBOOK-PLATFORM SASL authentication mechanism X-FACEBOOK_PLATFORM SASL認証を用います
  • Username/password authentication DIGEST-MD5 による認証を用います
  • プレーンなテキストの送受信(HTMLではありません)
  • XEP-0085 プロトコル拡張を用いて、タイピング通知を送受信します(XEP-0022ではありません)
  • presence stanzasのshowエレメントを用いてのユーザの退席(idle)設定(ユーザが実際にidle状態になるまでには遅延があります)
  • XEP-0054 を用いたvCard受信
  • 友達の写真を受信する(vCardもしくはXMPP)

Limitations

Facebook ChatはどのXMPPクライアントとも互換性を持つべきではありますが、完全なXMPPサーバではありません。考え方としては、www.facebook.com上のFacebook Chatのプロキシとして捉えてください。結果として、他のXMPPサービスと同じと期待するものとは少し異なる部分があります。

  • クライアントはHTMLメッセージを送受信できません
  • 登録者リストと状態表示はユーザのFacebook友達を基に行われるので、標準的なXMPPの仕組みを用いて作成もしくは削除できません。
  • 新しい相手に対して更新情報を送る場合、ネゴシエーションはXMPPの外部で行われるので簡単です。
  • ユーザ自身のJabber ID(JID)は、彼のコンタクト相手が見るJabber IDとは異なります。内部的に転換が行われる為です。
  • クライアント間で任意のIQ stanzasを交わす事はできません。
  • 状態チェックは今現在機能しません。
  • XEP-0078で紹介されているjabber:iq:authネームスペースを用いたNon-SASL認証は現在サポートされていません。
  • XMLパーサは、完全にXMLネームスペースを扱えてるわけではありません。XMLネームスペースを使う場合には、XMPP RFCs 3920 と3921の例と同じスタイルを維持してください。

Configuring Chat Authentication

X-FACEBOOK-PLATFORM(Facebook Platform)とDIGEST-MD5(ユーザ名/パスワード)の二通りの仕組みを用いてユーザ認証を行う事ができますが、できる限りX-FACEBOOK-PLATFORMを用いてFacebook Chatに接続する事をお薦めします。その方がシンプルな認証手順でより良いユーザ体験を提供できる為です。sample Python script でX-FACEBOOK-PLATFORMを習得してください。

Authenticating with Facebook Platform

X-FACEBOOK-PLATFORMと呼ばれるカスタマイズされたSASL機構をサポートしているので、クライアントはこれを用いてFacebook認証を行い、チャットに接続する事ができます。この機構は、一般的なソーシャルメディア、もしくは、特にacebook Platformを用いているアプリケーションでの利用を意図しています。

この機構を用いて接続するには、まずユーザがアプリケーションに対してxmpp_loginパーミッションを与えなくてはなりません。

それが済んだらX-FACEBOOK-PLATFORM機構を用いてFacebook Chatにログインすることが可能になります。ユーザのJabber IDはXMPPのリソースバインディングの段階で与えられます。X-FACEBOOK-PLATFORM機構によって定義されているメッセージは全てUTF-8文字列である事を忘れないでください。XMPPの仕様では、それらのメッセージはBase64エンコードされるべきとされています。

この機構は一般的なHTTPクエリストリングを用いたサーバの問い合わせから始まります。つまり、アンパサントで区切られ、等号で連結されたキー/バリューのペアです。これらキー/バリューはUTF-8エンコードとURLエンコードされたものです。クエリストリングには、methodとnonceという二つのパラメータが含まれています。

クライアントからのレスポンスは同様にエンコードされたクエリで、Facebook APIを呼ぶ時と同じようになります。レスポンス時には以下のパラメータを含みます。

  • string method: サーバ側に指定されたmethod値と同じもの
  • string api_key: 呼び出し側アプリケーションのAPIキー
  • string session_key: ログイン中ユーザのセッションキー
  • float call_id: リクエストシークエンスの値
  • string sig: 直近のリクエストとシークレットキーのMD5ハッシュ値
  • string v: このバージョンのAPIを利用する場合は1.0を指定
  • string format: オプション(無視されます)
  • string cnonce: オプション(クライアントが選択するnonceですが、無視されます)
  • string nonce: サーバ側に指定されたnonce値と同じもの
そしてサーバは、success/failureメッセージを返します。

Authenticating with Username/Password

DIGEST-MD5 SASL機構を利用して、典型的なXMPP、もしくはFacebook用にカスタマイズされていないマルチプロトコルのIMクライアントに対応できます。この機構は、ユーザによるパスワード入力を必須としますので、必要な場合にのみ使われるべきです。たとえば、以下のようなクライアントには使われてはなりません。
  • XMPP接続をプロキシする(ユーザのPCからFacebookへ直接繋がるべきです)
  • ユーザのアクティビティもしくはメッセージをサードパーティ(クライアントの開発者を含む)へ提供する
  • Facebookとやりとりする、もしくはFacebook アプリケーションIDを持っている

上記の一つでも合致する項目があるのなら、Facebook Platform authentication を代わりに用いてください。ユーザのJabber IDは単純で、Facebookユーザ名に@chat.facebook.comが付いたものです。ユーザはFacebookプロフィールのusernameを設定していないとDIGEST-MD5を用いる事ができません。ユーザはusernameを指定したらFacebookを一度ログアウト/ログインして、パスワードの特殊なハッシュを生成する必要があります。クライアントはXEP-0054に則ってサーバからユーザのvCardを得て、より自然な形式のユーザ名を表示します。例えば以下のようにです。

<iq id='1' type='get'><vCard xmlns='vcard-temp'/></iq>

Best Practices

ベストなユーザ体験を提供するため、以下のように実装することをお勧めします。

  • ユーザ同士のリアルタイムな対話を行うアプリケーションにのみFacebook Chatを実装する。ユーザが自分で入力しない限り、リンクや広告はチャットを通じて送信しない
  • 長時間続くと期待されるセッションを相手にする場合にのみFacebook Chatを用いる。クライアントは即座にログイン/アウトすべきではない。
  • Facebook Chatから得るvCardには、可能であればプロフィール画像が含まれています。クライアント側は、ユーザIDではなくそれらの画像のハッシュ値をキーとしてキャッシュすべきです。画像のキャッシュが無い場合にのみvCardを取得してください。
  • typeコンフリクトのストリームエラーを受け取った場合は、自動的に再接続しないでください。
  • 複数のグループ要素との単一のコンタクトを扱えるようにするべきです。
  • JIDのchat.facebook.comもしくはfacebook.comから得るメッセージは管理者権限からのメッセージとして表示されるべきです。

Platform Authentication Sample Code

Pythonで書かれている以下のサンプルコードは、X-FACEBOOOK-PLATFORM認証を用いてFacebook Chatを実装する例を示しています。このサンプルを実行するには、pyxmppとpyfacebookが必要です。pyxmppは複数のweb上リポジトリから取得できますし、pyfacebookはGitHubから取得できます。ただし、pyfacebookはFacebookによって開発/メンテナンスされているものではありません。

#!/usr/bin/env python

# This is a demonstration script for Facebook Chat
# using the X-FACEBOOK-PLATFORM SASL mechanism.
# It requires pyfacebook and pyxmpp to be installed.

# This client only works for desktop applications (configured in the
# developer app), and uses the old-style auth.getSession mechanism to get a
# Facebook session.  For newer-style or web apps, only the
# `get_facebook_client` function should have to change.

import sys
import os


def get_facebook_client():
    import facebook
    # Replace these with your app's credentials
    api_key = 'YOUR_API_KEY'
    secret_key = 'YOUR_API_SECRET'

    client = facebook.Facebook(api_key, secret_key)

    try:
        # Try to read cached credentials from the session-key file.
        # If authorization fails, you should delete this file and start of.
        handle = open('session-key', 'r')
        client.uid, client.session_key, client.secret = [ line.strip() for line in handle ]
        handle.close()
    except IOError:
        client.auth.createToken()
        client.login()
        print 'Log in to the app in your browser, then press enter.'
        raw_input()
        client.auth.getSession()
        handle = open('session-key', 'w')
        print >> handle, client.uid
        print >> handle, client.session_key
        print >> handle, client.secret
        handle.close()

    if not int(client.users.hasAppPermission('xmpp_login')):
        import webbrowser
        webbrowser.open(client.get_url('authorize',
                ext_perm = 'xmpp_login',
                api_key = client.api_key,
                v = '1.0'))
        print 'Grant the extended permission to the app in your browser, then press enter.'
       raw_input()

    return client


from pyxmpp.sasl.core import ClientAuthenticator
from pyxmpp.sasl.core import Response, Failure, Success

class XFacebookPlatformClientAuthenticator(ClientAuthenticator):
    def __init__(self, password_manager, fb_client=None):
        ClientAuthenticator.__init__(self, password_manager)
        if fb_client is None:
            global global_fb_client
            fb_client = global_fb_client
        self._fb_client = fb_client

    def start(self, ignored_username, ignored_authzid):
        return Response()

    def challenge(self, challenge):
        in_params = dict([part.split('=') for part in challenge.split('&amp;')])
        out_params = {'nonce': in_params['nonce']}
        out_params = self._fb_client._add_session_args(out_params)
        out_params = self._fb_client._build_post_args(in_params['method'], out_params)
        import urllib
        return Response(urllib.urlencode(out_params))

    def finish(self,data):
        return Success(None)


from pyxmpp.all import JID, Presence, Message
from pyxmpp.client import Client

class FacebookChatClient(Client):
    def __init__(self, to_uid, message, **kwargs):
        Client.__init__(self, **kwargs)
        self.to_uid = to_uid
        self.message = message
        self.sent = False

    def session_started(self):
        self.get_stream().set_message_handler(`chat`, self.got_message)
        self.get_stream().send(Presence())

    def idle(self):
        print 'Idle...'
        Client.idle(self)
        if self.session_established and not self.sent:
            self.sent = True
            target = JID('-' + self.to_uid, self.jid.domain)
            self.get_stream().send(Message(to_jid=target, body=unicode(self.message)))

    def got_message(self, stanza):
        print stanza.get_from().node, `:`, stanza.get_body()



if __name__ == '__main__':
    # Uncomment these lines to get more verbose logging.
    #import logging
    #logger = logging.getLogger()
    #logger.addHandler(logging.StreamHandler())
    #logger.setLevel(logging.DEBUG)

    # Sneak our authenticator into the map.
    import pyxmpp.sasl
    pyxmpp.sasl.all_mechanisms_dict['X-FACEBOOK-PLATFORM'] = \
    (XFacebookPlatformClientAuthenticator, None)

    print 'Preparing Facebook client...'
    global_fb_client = get_facebook_client()

    try:
        my_uid = str(global_fb_client.uid)
        to_uid = sys.argv[1]
        message = unicode(sys.argv[2])
        my_jid = '-' + my_uid + '@chat.facebook.com/TestClient'
    except IndexError:
        sys.exit('usage: %s {to_uid} {message}' % sys.argv[0])

    print 'Creating stream...'
    xmpp_client = FacebookChatClient(
            to_uid = to_uid,
            message = message,
            jid = JID(my_jid),
            password = u'ignored',
            auth_methods = ['sasl:X-FACEBOOK-PLATFORM'],
            #server = 'localhost'
            )

    print 'Connecting...'
    xmpp_client.connect()

    print 'Processing...'
    try:
        xmpp_client.loop(1)
    finally:
        xmpp_client.disconnect()