Home Top Projects Tutorials Articles Submit Project
 
  • .NET Frameworks
  • Java Frameworks
  • PHP Frameworks
  • Ruby Frameworks
  • Other Frameworks
  • Cool AJAX sites
  • Ajax Resources
  • Ajax Tools
  • JavaScript frameworks
 
  • MessengerFX
  • ebuddy
  • MSN Web Messenger
  • e-messenger
  • ILoveIM
  • AJAX file upload
  • You Tube
  • KoolIM.com
  • Meebo
  • Ajax.NET Professional
 
  • Handling array of HTML Form Elements in JavaScript and PHP
  • jQuery UI DatePicker instead of AJAX Control Toolkit CalendarExtender in ASP.NET
  • javascript calender in Ajax response
  • Cancelling an Asynchronous PostBack in ASP.NET Ajax
  • Create Your Own Ajax Effects
  • Simplify Ajax development with jQuery
  • AIM Express
  • Better File Uploads with Ajax and JavaServer Faces
  • RESTTest HTTP Tester
  • Using JavaServer Faces Technology with AJAX
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 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

Copyrights Reserved AjaxProjects.com 2006-2010, Developed by IRange -Privacy Policy