あなたのwebベース/デスクトップ/モバイルのインスタントメッセージアプリケーションにFacebook Chatを実装する事が可能です。クライアントはJabber/XMPPサービスを通じてFacebook Chatと繋がることになります。このドキュメントは、Facebook Chatクライアントを実装しようとする開発者に対して、Facebook ChatのXMPPプロトコルの機能と制限について解説しています。
このドキュメントを読む前に、XMPPチャットクライアント/サーバのコンセプトや用語について理解してください。Jabberクライアントが100,000セッション以上をホストすると予測している場合は、Developer Help Contact Form へ行ってJabberを選択して我々に知らせてください。

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値と同じもの
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('&')])
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()