#!/usr/bin/env ruby # # This will scan all your facebook contacts and use the profile image # found there for any address book entries that doesn't have an image # # Example usage: # # % ./facebookcontacts # # Facebook require 'rubygems' require 'facebooker' require 'open-uri' require 'net/http' require 'uri' require 'RMagick' require 'ftools' # For AddressBook require 'sqlite3' # Facebook constants APP_ID = '86252418116' APIKEY = 'c33f0b8e7b53412893d289bba336dd3a' SECRET = 'f2c3b8ae29b4f9dd198a3f1b360cf803' # Simple logging def log(msg) STDERR.puts '*** ' + msg STDERR.flush end # ---------------------------------------------------------------------- # Abstractions over a mac address book. Specific for the contacts, # facebook app in that we only care about the images. The model is # that a first name/last name combination maps to a possible list of # images, since there could be multiple entries for a single person. # ---------------------------------------------------------------------- class AddressBook def initialize # # Create a map from first/last names to lists of images # @names2imagess = {} base = File.join ENV['HOME'], 'Library/Application Support/AddressBook' addressbook = Dir[File.join base, 'AddressBook*.abcddb'][0] db = SQLite3::Database.new addressbook records = db.execute 'select ZFIRSTNAME, ZLASTNAME, ZUNIQUEID from ZABCDRECORD' images = File.join base, 'Images' records.each do |rec| first_name = rec[0] last_name = rec[1] id = rec[2].gsub /\:ABPerson/, '' img = File.join images, id full_name = lowercase(first_name) + ' ' + lowercase(last_name) imgs = @names2imagess[full_name] imgs = [] if !imgs imgs.push img @names2imagess[full_name] = imgs end end # Returns # 1. the paths of the existing image # 2. nil if we don't have this contact in here def get_images(full_name) return @names2imagess[lowercase(full_name)] end private # Safe version of downcase def lowercase(s) return s == '' ? nil : s.to_s.downcase end end # ---------------------------------------------------------------------- # A simple person with an id, name, and image file # ---------------------------------------------------------------------- class Person attr_reader :id, :name, :img def initialize(id,name,img) @id = id @name = name @img = img end def to_s return @name + ':' + @img + ':' + @id.to_s end end # ---------------------------------------------------------------------- # Main class # ---------------------------------------------------------------------- class GetIcons def get_icons # # Create the address book # ab = AddressBook.new # # Log in # ENV['FACEBOOK_API_KEY'] = APIKEY ENV['FACEBOOK_SECRET_KEY'] = SECRET session = Facebooker::Session::Desktop.create APIKEY,SECRET system "open", session.login_url puts 'Hit RETURN to continue. . .' gets # # Search every user to see if they are in the addressbook If so # see if there's an image for them and take it from there # user = session.user friends = user.friends!( :name, :email ) needed_persons = [] friends.each do |friend| # # Look up this friend in contacts # full_name = friend.name imgs = ab.get_images full_name next if !imgs imgs.each do |img| if img and !File.exists? img needed_persons.push Person.new(friend.id,friend.name,img) end end end # # Now, download all the images for people for which we don't have # images # needed_persons.each do |p| download_image p end end private def download_image(person) url = 'http://www.facebook.com/profile.php?id=' + person.id.to_s log 'Downloading ' + person.name + ' @ ' + url + '...' open(url,'User-Agent' => user_agent) do |f| content = f.read # # Parse out the profile image, which is a php file # content.scan /src=\"(\/profile\/pic.php\?uid=[^\"]+)\"/ do |res| # # Then download the image # img_to_download = res[0] url_to_download = 'http://facebook.com' + img_to_download log ' image: ' + url_to_download url = URI.parse url_to_download headers = { 'User-Agent' => user_agent } res = Net::HTTP.start(url.host, url.port) do |http| http.get img_to_download, headers end # # Write the image to a temporary file # outfile = '.image.jpg' open(outfile, "wb") do |file| file.write res.body end # # Convert that file to a 320x320 gif and write it to the destination # file for this person # convert_image outfile, person.img end end end # Converts the jpeg image, jpg, to the destination file, dest_gif def convert_image(jpg,dest_gif) log ' convert ' + jpg + ' -> ' + dest_gif image = Magick::Image.read(jpg).first new_image = image.resize_to_fill 320, 320 cropped = '.cropped.gif' new_image.write cropped File.copy cropped, dest_gif end private # TODO: May have to randomize this? def user_agent return 'Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 6.0; SLCC1; .NET CLR 2.0.50727; Media Center PC 5.0; .NET CLR 3.0.04506)' end end def main(argv) GetIcons.new.get_icons end main ARGV