#!/usr/local/bin/ruby

# MySpace friend adder script.
#
# - Searches Google for a term you give it.
# - Rips friend ID's to a SQLite database in memory.
# - Adds those friends to your MySpace.
#
# !!! THIS SCRIPT IS FREE TO DISTRIBUTE, JUST KEEP THESE COMMENTS INTACT !!!
#
# Work by Subimage.com
# http://sublog.subimage.com/articles/2007/07/11/ruby-myspace-friend-adder-script
#
# REQUIRED GEMS:
#
# - activerecord
# - hpricot
# - mechanize
# - sqlite3-ruby
#

# GLOBALS =====================================================================

# STUFF YOU SHOULD CHANGE
RECORDS_PER_PAGE = 100
PAGES = 10
# Should enter these here, otherwise it'll prompt you for it...
MYSPACE_USERNAME = ''
MYSPACE_PASSWORD = ''

# mac
BROWSER_EXE = 'open -a firefox'
# windoze
# BROWSER_EXE = 'c:/progra~1/mozill~1/firefox.exe'

# Maximum sleep time between page reloads in seconds
MAX_SLEEP_TIME = 10

# DON'T CHANGE THIS STUFF

SCRIPT_VERSION = "1.2"
DBFILE = "myspace_adder_db.sqlite"
SEPARATOR = "--------------------------------------------------------------------------------"
# User agents
UA = [
   'Windows IE 6' ,
   'Windows Mozilla',
   'Mac Safari' ,
   'Mac Mozilla' ,
   'Linux Mozilla', 
   'Linux Konqueror' ]

# SETUP CONFIGURATION =========================================================
begin
  require 'rubygems'
  require 'active_record'
  require 'open-uri'
  require 'mechanize'
  require 'fileutils'
  require 'cgi'
rescue
  puts "!!! SETUP ERROR !!!"
  puts "Please make sure you have followed the setup directions at: "
  puts "http://sublog.subimage.com/articles/2007/07/11/ruby-myspace-friend-adder-script"
end

class Friend < ActiveRecord::Base
  
end


class MySpaceAdder

  # Initializes...
  def initialize
    # Initialize mechanize agent
    @agent = WWW::Mechanize.new
    # Set random user agent
    @agent.user_agent_alias =  UA[rand(UA.size)]
    @myspace_username = MYSPACE_USERNAME
    if @myspace_username.blank?
      printf "MySpace email address / username: "
      @myspace_username = gets.chomp
    end
    @myspace_password = MYSPACE_PASSWORD
    if @myspace_password.blank?
      printf "MySpace password: "
      @myspace_password = gets.chomp
    end
  end

  # GOOGLE SEARCH
  #
  # Looks at google for phrase given and snags friend IDs
  #
  def do_google_search

  
    puts SEPARATOR
    puts ""
    printf "Should we search Google or Yahoo?: (G/y)"
    @goog_yahoo = gets.chomp
    printf "What should we search for: "

    search_string = gets

    # Open url, get up to 10 pages of links since that's all google gives
    # Each page contains 100 search results...(or whatever you've set the variable to)
    puts "\n\nGrabbing page results..."
    for page_index in 0..PAGES do
      puts "...getting page #{page_index}"
      
      if @goog_yahoo != 'y'
        # Search google
        query  = "http://www.google.com/search?q=#{CGI.escape(search_string.chomp)}"
        query << "+site%3Awww.myspace.com&"
        query << "&hl=en&safe=off&client=firefox-a&rls=org.mozilla:en-US:official&"
        query << "&num=#{RECORDS_PER_PAGE}"
        query << "&start=#{(page_index * RECORDS_PER_PAGE)+1}" if page_index > 0
      else 
        # Search yahoo
        query  = "http://search.yahoo.com/search?p=#{CGI.escape(search_string.chomp)}"
        query << "+site%3Awww.myspace.com&"
        query << "&ei=utf-8&va_vt=any&vo_vt=any&ve_vt=any&vp_vt=any&vd=all&vf=all&vm=p&fl=0&xargs=0"
        query << "&n=#{RECORDS_PER_PAGE}"
        query << "&pstart=1&b=#{(page_index * RECORDS_PER_PAGE)+1}" if page_index > 0
      end
      
      puts "Getting..."
      puts query
      begin
        page = @agent.get(query)
      rescue
        next
      end
      #puts page
      # Rip out all the friend names
      page.links.each do |link|
        str_to_look_for = "www.myspace.com/"
        start_pos = link.href.index(str_to_look_for)
        next if !start_pos
        # Offset start slice pos
        start_pos = start_pos+str_to_look_for.size
        next if link.href.index("+")
        next if link.href.index("&")
  
        username = link.href[start_pos, link.href.length-1]
  
        if !Friend.find(:first, :conditions => ["username = ?", username])
          Friend.create(
           :username => username,
           :link => link.href
          ) 
        end
      end
      # Sleep between page grabs so sites doesn't freak out...
      sleep_time = rand(MAX_SLEEP_TIME)
      puts "...sleeping #{sleep_time} seconds so the search engine doesn't freak out..."
      next if page_index == PAGES
      sleep(sleep_time)
    end

    puts SEPARATOR

    puts "Total friend count: #{Friend.count()}"
    puts "Done searching...time to add these suckers"
  end

  # Tries to login to myspace...
  #
  def myspace_login
    # Login to myspace...
    puts "...Trying to login to MySpace..."
    page = @agent.get("http://www.myspace.com/?Mytoken=90f1eeb2-f110-401b-a150-d9053692048f")
    # Fill in the form
    #login_form = page.form("theForm")
    login_form = page.form("aspnetForm")
    #login_form['email'] = @myspace_username
    login_form['ctl00$Main$SplashDisplay$ctl00$Email_Textbox'] = @myspace_username
    #login_form['password'] = @myspace_password
    login_form['ctl00$Main$SplashDisplay$ctl00$Password_Textbox'] = @myspace_password
    # Click the login button
    page = @agent.submit(login_form)
    if !page.form("aspnetForm")
      puts "...We're logged in!"
      # Try to print out number of pending add requests
      page = @agent.get("http://messaging.myspace.com/index.cfm?fuseaction=mail.pendingFriendRequests")
      #pending_str = (page/"div.pagingLeft").first.inner_html
      # Chop out bullshit
      #pending_str = pending_str[pending_str.index('of')+2, pending_str.size]
      #puts "You have #{pending_str.strip} pending friend requests."
      # pagingLeft
    else
      puts "...I think we're already logged in..."
      #exit
    end
  end

  # ADD FRIENDS
  #
  # Adds friends stored in the SQLite database.
  #
  def add_friends
  
    puts SEPARATOR
    
    self.myspace_login()

    # Loop through all friends
    Friend.find(:all).each do |f|
      # Navigate to person's page, find the add link, go there...
      puts "\n\nTrying to add: #{f.username}"
      # Remove friend from the db...just because we don't want to
      # do any odd condition checking later.
      f.destroy
      #
      @agent.read_timeout = 120
      begin
        page = @agent.get(f.link)
      rescue Timeout::Error
        "Getting the page is taking too long...skipping."
        next
      rescue
        "General error trying to load page...skipping"
        next
      end
    
      add_link = nil
      # Mechanize's find method on urls is shitty, so lets do this
      # oldschool
      while add_link == nil do
        page.links.each do |link|
          add_link = link.href if link.href && link.href.index('addfriend')
        end
        # Can't find the add link, move on...
        puts "Add link: #{add_link}"
        break if !add_link
      end
      # Get contents so we can parse...
      begin
        page_contents = @agent.get_file(add_link)
        page = @agent.get(add_link)
      rescue
        puts $!
        next
      end
  
      # Look for the "confirm add friend button"
    	if (page_contents =~ /person.is.already.your.Friend/)
    		puts "#{f.username} is already your friend."
    		next
    	elsif (page_contents =~ /pending.friend.request/)
    		puts "Pending add request."
    		next
    	elsif (page_contents =~ /only.accepts.add.requests.from/)
    		puts "Private profile. Moving on."
    		next
    	elsif (page_contents =~ /from.bands/)
    	  puts "#{f.username} doesn't accept requests from bands..."
    	  next
    	elsif (page_contents =~ /Invalid.FriendID/i)
    	  puts "Invalid friend ID, moving on..."
    	  next
    	elsif (page_contents =~ /CAPTCHA/)
        puts "********************"
        puts "* CAPTCHA DETECTED *"
        puts "********************"
    		puts add_link
  		
    		# I put escaped quotes around the '&' in the next line so that my bash 
    		# wouldn't interpret it as a line break and foul up the system command
    		system("#{BROWSER_EXE} \"#{add_link}\"")
    	  puts "Press enter when you're typing it in..."
    	  gets
    	  next
    	end

    	add_form = page.form('addFriend')
    	if add_form && add_form.class != String
    	  @agent.submit(add_form)
    	  puts "...Added!"
    	else
    	  puts "Couldn't find the add form...moving on"
    	end
	
      # Sleep between friend adds grabs so myspace doesn't freak out...
      sleep_time = rand(MAX_SLEEP_TIME)
      puts "...sleeping #{sleep_time} seconds so MySpace doesn't freak out..."
      sleep(sleep_time)
  	
    end
  end
end # / END CLASS

# DO THIS!@
puts "********************************************************************************"
puts "        S U B I M A G E     M Y S P A C E     F R I E N D     A D D E R"
puts "********************************************************************************"

#ActiveRecord::Base.logger = Logger.new(STDERR)
ActiveRecord::Base.colorize_logging = false

ActiveRecord::Base.establish_connection(
  :adapter => "sqlite3",
  :dbfile  => DBFILE
)

adder = MySpaceAdder.new()

if File.size(DBFILE) > 0
  puts "An existing database filled with (#{Friend.count()}) friends has been found."
  printf "Would you like to use it to finish your last adding session? (Y/n): "
  init_response = gets.chomp
end

if (File.size(DBFILE) == 0 || (init_response == "n" || init_response == "N"))
  puts "\nInitializing the database..."
  ActiveRecord::Schema.define do
    begin
      drop_table :friends
    rescue
      # Who cares if we can't drop it...
    end
    create_table :friends do |table|
      table.column :link, :string
      table.column :username, :string
    end
  end
  puts "Done...\n\n"
  adder.do_google_search()
  adder.add_friends()
else
  adder.add_friends()
end

puts "No more friends to add...start a new search."
