Home | Projects | Tutorials | Articles | live chat | Submit Project | Big Vote
 
Ajax Projects
.NET Frameworks
Java Frameworks
PHP Frameworks
Ruby Frameworks
Other Frameworks
Cool AJAX sites
Ajax Resources
Ajax Tools
JavaScript frameworks
Western Digital Hard drive recovery

 Home /  Tutorials / Facebook Chat API

Facebook Chat API





Facebook uses a Comet and JSON approach to receive incoming messages. Comet is a technique where you open a long running HTTP connection to a server (usually AJAX). The connection stays open until the server has something to tell you at which point it sen

Read The Full Tutorial.
































Facebook uses a Comet and JSON approach to receive incoming messages. Comet is a technique where you open a long running HTTP connection to a server (usually AJAX). The connection stays open until the server has something to tell you at which point it sends the data through the connection and the connection can be closed (or in some cases remain open). JSON is just a way of representing data in Javascript. Let’s look at how it works.

A connection is opened to: http://[ANYTHING].channel[NUM].facebook.com/x/[ANYTHING]/false/p_[UID]=[SEQ]. First let’s look at the url and what the different placeholders represent.

ANYTHING can be anything, Facebook has setup *.channel[NUM].facebook.com as a catch-all subdomain. So no matter what you put there it will resolve to the same IP. The ANYTHING in the path can also be anything. I assume both the ANYTHING values are used to prevent the browser from caching these requests.

NUM represents the channel number you are using. It appears there are 20 channels (01-20), as that range of subdomains resolve and the rest don’t. I am assuming that a “channel” is just one of Facebook’s comet interface servers. Every time you log in you are assigned to a specific channel and it doesn’t change throughout your session. It may also be fixed per user, I’m not sure about this though.

UID is just your user id. SEQ is the sequence number you are requesting from the server. If you give an out of range seq number the server will tell you what the latest seq number is so that you can request it. If you give an old seq number the server will show you the (old) messages that correspond with that number. Requesting http://0.channel06.facebook.com/x/0/false/p_MYID=-1 will immediately respond telling me I should re-request with a seq number of 0 (or whatever my current seq number is) since the seq number -1 is never valid.
view plaincopy to clipboardprint

   1. // request http://0.channel06.facebook.com/x/0/false/p_MYID=-1 
   2. for (;;);{"t":"refresh", "seq":0} 

// request http://0.channel06.facebook.com/x/0/false/p_MYID=-1
for (;;);{"t":"refresh", "seq":0}

After we build the correct URL and make the connection, it will just “sit there”. This is how Comet connections work. The connection will remain inactive in a “requesting” state until someone sends us a message. If a message is not received in less than a minute (approx 55 seconds) Facebook will close the connection and instruct you to reopen it. I assume they do this to get around browser timeout issues. If a message is received you will get the message data through the connection which is then closed. After a message is received the seq number should be incremented for the next request. After either a message is received or the connection times out a new connection will immediately be established.
view plaincopy to clipboardprint

   1. // request http://0.channel06.facebook.com/x/0/false/p_MYID=0 times out 
   2. for (;;);{"t":"continue"} 
   3. // request http://0.channel06.facebook.com/x/0/false/p_MYID=0 receives a message! 
   4. for (;;);{"t":"msg","c":"p_MYID","ms":[{"type":"msg","msg":{"text":"yo","time":1209557234412,"clientTime":1209557232415,"msgID":"4177168544"},"from":FRIENDSID,"to":MYID,"from_name":"Myfriends Name","to_name":"My name","from_first_name":"Myfriends","to_first_name":"My"}]} 
   5. // request http://0.channel06.facebook.com/x/0/false/p_MYID=1 for the next message 

// request http://0.channel06.facebook.com/x/0/false/p_MYID=0 times out
for (;;);{"t":"continue"}
// request http://0.channel06.facebook.com/x/0/false/p_MYID=0 receives a message!
for (;;);{"t":"msg","c":"p_MYID","ms":[{"type":"msg","msg":{"text":"yo","time":1209557234412,"clientTime":1209557232415,"msgID":"4177168544"},"from":FRIENDSID,"to":MYID,"from_name":"Myfriends Name","to_name":"My name","from_first_name":"Myfriends","to_first_name":"My"}]}
// request http://0.channel06.facebook.com/x/0/false/p_MYID=1 for the next message

Sending Messages

Sending messages is a lot simpler. All you need to do is make a POST request to http://www.facebook.com/ajax/chat/send.php with a few simple values: msg_text with the text of the message, to with the uid of the recipient, msg_id with a random unique number id for the message, client_time with the current time in microseconds, and post_form_id which can be found as a hidden form value on any facebook page. Facebook will respond with some JSON indicating there was no error and the message will be sent!
view plaincopy to clipboardprint

   1. // POST request to: http://www.facebook.com/ajax/chat/send.php?msg_text=hey&msg_id34567890&to=FRIENDSID&client_time09558664256&post_form_id)7fdbad61d3b1d88f0c311cda25bbbc 
   2. for (;;);{"error":0,"errorSummary":"","errorDescription":"No error.","payload":[]} 

// POST request to: http://www.facebook.com/ajax/chat/send.php?msg_text=hey&msg_id34567890&to=FRIENDSID&client_time09558664256&post_form_id)7fdbad61d3b1d88f0c311cda25bbbc
for (;;);{"error":0,"errorSummary":"","errorDescription":"No error.","payload":[]}

Buddy List
The list of your friends who are online is polled about every 3 minutes with a POST request to http://www.facebook.com/ajax/presence/update.php. The ‘userInfos’ hash contains the actual buddy list. Some extra info is provided to let you know how the buddy list has changed since the last request.
view plaincopy to clipboardprint

   1. // POST request to http://www.facebook.com/ajax/presence/update.php?buddy_list=1 
   2. for (;;);{"error":0,"errorSummary":"","errorDescription":"No error.","payload":{"buddy_list":{"listChanged":true,"availableCount":1,"nowAvailableList":{"UID1":{"i":false}},"wasAvailableIDs":[],"userInfos":{"UID1":{"name":"Buddy 1","firstName":"Buddy","thumbSrc":"http:\/\/static.ak.fbcdn.net\/pics\/q_default.gif","status":null,"statusTime":0,"statusTimeRel":""},"UID2":{"name":"Buddi 2","firstName":"Buddi","thumbSrc":"http:\/\/static.ak.fbcdn.net\/pics\/q_default.gif","status":null,"statusTime":0,"statusTimeRel":""}},"forcedRender":true},"time":1209560380000}} 

// POST request to http://www.facebook.com/ajax/presence/update.php?buddy_list=1
for (;;);{"error":0,"errorSummary":"","errorDescription":"No error.","payload":{"buddy_list":{"listChanged":true,"availableCount":1,"nowAvailableList":{"UID1":{"i":false}},"wasAvailableIDs":[],"userInfos":{"UID1":{"name":"Buddy 1","firstName":"Buddy","thumbSrc":"http:\/\/static.ak.fbcdn.net\/pics\/q_default.gif","status":null,"statusTime":0,"statusTimeRel":""},"UID2":{"name":"Buddi 2","firstName":"Buddi","thumbSrc":"http:\/\/static.ak.fbcdn.net\/pics\/q_default.gif","status":null,"statusTime":0,"statusTimeRel":""}},"forcedRender":true},"time":1209560380000}}

Notifications

Notifications are polled through the same mechanism as the buddy list. You need to pass &notifications=1 to update.php to receive info about them. Other than that I won’t be covering them here.

The Fun Part
So here’s a very simple implementation of a Facebook Chat client written in Ruby. It will require you have the json and mechanize gems installed.
view plaincopy to clipboardprint

   1. require 'mechanize' 
   2. require 'json' 
   3. require 'ostruct' 
   4. require 'pp' 
   5.  
   6. class FacebookChat 
   7.   def initialize(email, pass); @email, @pass = email, pass; end 
   8.  
   9.   def login 
  10.     @agent = WWW::Mechanize.new 
  11.     @agent.user_agent_alias = 'Windows IE 7' 
  12.     f = @agent.get("http://facebook.com/login.php").forms.first 
  13.     f.fields.name("email").value = @email 
  14.     f.fields.name("pass").value = @pass 
  15.     f.submit 
  16.     body = @agent.get("http://www.facebook.com/home.php").body 
  17.  
  18.     # parse info out of facebook home page 
  19.     @uid = %r{<a href=".+?/profile.php\?id=(\d+)" class="profile_nav_link">Profile</a>}.match(body)[1].to_i 
  20.     @channel = %r{"channel(\d+)"}.match(body)[1] 
  21.     @post_form_id = %r{<input type="hidden" id="post_form_id" name="post_form_id" value="([^"]+)}.match(body)[1] 
  22.   end 
  23.  
  24.   def wait_for_messages 
  25.     determine_initial_seq_number  unless @seq 
  26.  
  27.     begin 
  28.       json = parse_json @agent.get(get_message_url(@seq)).body 
  29.     end  while json["t"] == "continue"   # no messages yet, keep waiting 
  30.     @seq += 1 
  31.  
  32.     json["ms"].select{|m| m['type'] == 'msg'}.map do |msg| 
  33.       info = msg.delete 'msg' 
  34.       msg['text'] = info['text'] 
  35.       msg['time'] = Time.at(info['time']/1000) 
  36.       OpenStruct.new msg 
  37.     end.reject {|msg| msg.from == @uid }  # get rid of messages from us 
  38.   end 
  39.  
  40.   def send_message(uid, text) 
  41.     r = @agent.post "http://www.facebook.com/ajax/chat/send.php", 
  42.       'msg_text' => text, 
  43.       'msg_id' => rand(999999999), 
  44.       'client_time' => (Time.now.to_f*1000).to_i, 
  45.       'to' => uid, 
  46.       'post_form_id' => @post_form_id 
  47.   end 
  48.  
  49.   def buddy_list 
  50.     json = parse_json(@agent.post("http://www.facebook.com/ajax/presence/update.php", 
  51.                         'buddy_list' => 1, 'post_form_id' => @post_form_id, 'user' => @uid).body) 
  52.     json['payload']['buddy_list']['userInfos'].inject({}) do |hash, (uid, info)| 
  53.       hash.merge uid => info['name'] 
  54.     end 
  55.   end 
  56.  
  57.   private 
  58.  
  59.   def determine_initial_seq_number 
  60.     # -1 will always be a bad seq number so fb will tell us what the correct one is 
  61.     json = parse_json @agent.get(get_message_url(-1)).body 
  62.     @seq = json["seq"].to_i 
  63.   end 
  64.  
  65.   def get_message_url(seq) 
  66.     "http://0.channel#{@channel}.facebook.com/x/0/false/p_#{@uid}=#{seq}" 
  67.   end 
  68.  
  69.   # get rid of initial js junk, like 'for(;;);' 
  70.   def parse_json(s) 
  71.     JSON.parse s.sub(/^[^{]+/, '') 
  72.   end 
  73. end 
  74.  
  75. if __FILE__ == $0 
  76.   fb = FacebookChat.new(ARGV.shift, ARGV.shift) 
  77.   fb.login 
  78.  
  79.   puts "Buddy List:" 
  80.   pp fb.buddy_list 
  81.  
  82.   Thread.abort_on_exception = true 
  83.   Thread.new do 
  84.     puts usage = "Enter message as <facebook_id> <message> (eg: 124423 hey man wassup?) or type 'buddy' for buddy list" 
  85.     loop do 
  86.       case gets.strip 
  87.       when 'buddy' then pp fb.buddy_list 
  88.       when /^(\d+) (.+)$/ 
  89.         uid, text = $1.to_i, $2 
  90.         fb.send_message(uid, text) 
  91.       else 
  92.         puts usage 
  93.       end 
  94.     end 
  95.   end 
  96.  
  97.   # message receiving loop 
  98.   loop do 
  99.     fb.wait_for_messages.each do |msg| 
 100.       puts "[#{msg.time.strftime('%H:%M')}] #{msg.from_name} (#{msg.from}): #{msg.text}" 
 101.     end 
 102.   end 
 103. end 

source: coderrr

blueflycn Says:
Thu Jun 04, 2009 3:21 am
As far as I know the notifications are sent by Comet way too, much immediately than regular polling
dewabrata Says:
Mon Jul 13, 2009 10:24 am
when i tried to running this program there`s error message like this

chat.rb:19:in `login': undefined method `[]' for nil:NilClass (NoMethodError)
from chat.rb:77

how to fix this error please, thanx?
Archana Says:
Mon Aug 17, 2009 9:35 am
Hi,

I tried this way its working fine but after 2 minutes Connection lost.
How to reconnect it?
Cman Says:
Wed Sep 23, 2009 8:05 am
Hi,
what is the "for (;;);" in each response?
neon tabela Says:
Thu Oct 29, 2009 6:06 pm
Everything is very open and very clear explanation of issues. was truly information. Your website is very useful. Thanks for sharing.AJAX...
online architecture degree Says:
Sat Jan 23, 2010 7:43 am
Everything is very open and very clear explanation of issues. was truly information. Your website is very useful. Thanks for sharing.
online aerospace engineering degree Says:
Sat Jan 23, 2010 7:43 am
which is grown by mounding earth up around the emerging spears to protect them from light.
online advertising degree Says:
Sat Jan 23, 2010 7:44 am
France and Germany, favour blanched white asparagus, with its characteristic violet shading and yellow tips
online adult education degree Says:
Sat Jan 23, 2010 7:44 am
Asparagus ranges from young, slender sprue to the thicker kitchen and jumbo grades, which have a more mature flavour. Some European countries, particularlyonline adult education degree
online administration and supervision degree Says:
Sat Jan 23, 2010 7:45 am
April onwards, depending on the weather. Harvested after the Hungry Gap of March and early April, asparagus provides a welcome splurge of fleeting green vegetable after the sturdy roots and brassicas of winter months and before the tender salads of summer.
coach handbags Says:
Thu Mar 11, 2010 8:41 am
Wow...this was a really good article. interesting...., thanks for sharing the info, keep up the good work going....

Leave Your Comment

Name (Required)
Mail (will not be published) (required)
Website
AddThis Social Bookmark Button
Top Projects
MSN Web Messenger
MessengerFX
ebuddy
e-messenger
ILoveIM
You Tube
AJAX file upload
KoolIM.com
Meebo
Ajax.NET Professional
Tutorials
A JavaScript make-like utility
YUI AutoComplete applying JSON and Ajax
Fun with Drag and Drop with RICO
Detecting an End of Session event with jQuery
Accessing a POP3 Email Account through an ASP Page
Blendy Backgrounds Explained
jQuery - PHP & Ajax with the Twitter API
Using Ajax to Simulate Server Push
CSSHttpRequest: cross-domain Ajax using CSS for transport
Using jQuery Ajax methods in ASP.NET