Introduction:
Contacts Gem is an interface to import email contacts from various providers like Yahoo, Gmail, Hotmail, and Plaxo including AOL to avail invite functionality.Download:
sudo gem install contacts http://github.com/cardmagic/contacts git clone git://github.com/cardmagic/contacts.gitDo not forget to install Json Gem as well.
gem isntall json
Facebook Inviter
First thing first. You need to have Mechanize Gem including its dependency(Nokogiri). Very simple...sudo gem install mechanizethis will install Nokogiri as well.
Using Contacts Gem as Plugin
One more thing, I am using Contacts Gem as plugin to get things easier. Making this Gem as plugin is not a big deal! Move the Gem, either getting it from Local Gem repository C:\ruby\lib\ruby\gems\1.8\gems\ or whatever you have downloaded, to '#{RAILS_ROOT}/vendor/plugins'.Then create initializer init.rb into #{RAILS_ROOT}/vendor/plugins/contacts/.
init.rb
require 'contacts'Get to the console(restart to load the plugin) to check it out.
C:\Me\Workspace\contacts>ruby script/console Loading development environment (Rails 2.3.4) >> Contacts => ContactsSo far so good.
Now back to facebook fetcher Fix a base class(say page_scraper.rb) for all the fetchers(facebook, orkut etc...) that uses mechanize into #{RAILS_ROOT}/vendor/plugins/contacts/lib/contacts extending Contacts's base class as follow.
page_scraper.rb
require 'mechanize' class Contacts class PageScraper < Base attr_accessor :agent # creates the Mechanize agent used to do the scraping def create_agent self.agent = WWW::Mechanize.new agent.keep_alive = false agent end # Logging in def prepare; end # stub def validate(page) return false if page.nil? return false unless page.respond_to?('forms') return true end def strip_html( html ) html.gsub(/<\/?[^>]*>/, '') end end endAgain in the same location(#{RAILS_ROOT}/vendor/plugins/contacts/lib/contacts) put facebook.rb containing...
require 'page_scraper' class Contacts class Facebook < PageScraper URL = "http://www.facebook.com/" LOGIN_URL = "http://m.facebook.com/" PROTOCOL_ERROR = "Facebook has changed its protocols, please upgrade this library." def real_connect create_agent prepare end def prepare page = agent.get(LOGIN_URL) raise ConnectionError, PROTOCOL_ERROR unless validate(page) login_form = page.forms.first login_form.email = @login login_form.pass = @password login_form.charset_test = '' page = login_form.submit raise AuthenticationError, "Email and Password do not match!" if page.body =~ /Incorrect email\/password combination/ #Friends Page friends_links = page.links_with(:href => /\/friends.php/) friends_link = friends_links.collect{|flink| flink.href if flink.text == 'Friends'}.compact.first raise ConnectionError, PROTOCOL_ERROR unless friends_link friends_url = 'http://m.facebook.com' + friends_link page = agent.get(friends_url) #My Friends Page my_friends_link = page.links_with(:href => /\/friends.php/) @everyone = my_friends_link.collect{|l| l.href if l.text == 'Everyone'}.compact.first @everyone = my_friends_link.collect{|l| l.href if l.text == 'Friends'}.compact.first unless @everyone @logout_url = page.link_with(:href => /logout/) return true end def contacts contacts = [] next_page = true current_page = 2 everyone = 'http://m.facebook.com' + @everyone page = agent.get(everyone) raise AuthenticationError, "You must login first!" if page.body =~ /Enter your login and password/ while(next_page) next_page = false data = page.search('//tr[@valign="top"]') if data data.children.each do |node| pf_nodes = node.children.children if pf_nodes fr_name = msg_link = '' pf_nodes.each do |nd| fr_name = nd.text if nd.name == 'a' && nd.has_attribute?('href') if nd.name == 'span' || nd.text.include?('Message') nd.children.each do |n| msg_link = n.attributes['href'] if n.name == 'a' && n.text == 'Message' && n.has_attribute?('href') && n.attributes['href'].to_s.include?('compose') end end end contacts << [fr_name, msg_link.to_s] unless msg_link.blank? end end end # End Outer if data = page.search('//div[@class="pad"]') data.each do |node| childs = node.children next if childs.empty? childs.each do |c| if c.name == 'a' && c.text.to_i == current_page next_page = 'http://m.facebook.com' + c.attributes['href'].to_s break end end end current_page += 1 page = agent.get(next_page) unless next_page == false end #End While return contacts end def send_message(contact, message, subj=nil) success = false if contact url_inbox = "http://m.facebook.com" + contact.first page = agent.get(url_inbox) raise AuthenticationError, "You must login first!" if page.body =~ /Enter your login and password/ return false unless validate(page) page.forms.each do |f| if f.has_field?('body') f.subject = subj if subj && !subj.empty? f.body = message f.charset_test = '' page = agent.submit(f, f.buttons.first) return true if page.code == '200' end end end return success end def logout agent.click @logout_url if @logout_url return true end end TYPES[:facebook] = Facebook endUltimately, Open lib/contacts.rb and add this at the end of the line as
require 'facebook'You are done
Usage
C:\Me\Workspace\contacts>ruby script/console Loading development environment (Rails 2.3.4) >> fb=Contacts::Facebook.new('facebookid','password') ... ... >> contacts = fb.contacts => [["User1", "/inbox/?r3a2c6b2e&r39ade298&compose&ids=100000485673107&refid=5"], ["User2", "/inbox/?r46861c8d&rd5ef5fb6&compose&ids=691998819&refid=5"]] >> contacts.each{|c|fb.send_message(c, 'Your Message', 'Subject')}Done! but scopes are always there for improvement...:)