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
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.
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
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
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
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 ¬ifications=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
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
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.