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
end
Again 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
end
Ultimately, 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...:)