API vs RSI

World's-most-famous-twitterer Stephen Fry has a system for handling follow requests: you tweet using the #followmestephen hashtag, and he wades diligently through them, manually following people.

This seems an odd sort of thing to do – most people choose whom to follow based on whether they know them or like what they say, rather than on request – but I suppose when you have over a quarter of a million followers things work a little differently. It also creates lots of work , and looks like an ideal candidate for automation.

I thought I'd have a quick play with the Twitter API this morning (no doubt I'm not the only one), and cobbled together the script below, which you can also download as follow_me_stephen.rb (although if you're not Mr Fry I'm not sure why you would want to). Save the file, and run using ruby follow_me_stephen.rb.

I wanted to avoid having too many dependencies, so I didn't use the twitter gem, or the excellent httparty, but I was too lazy to figure out all the XPaths to handle the Atom version of the API. This means you need to have the JSON gem installed, which is as simple as sudo gem install json (omit the sudo on Windows).

The script's pretty dumb, in that it grabs the whole set of search results every time, and blindly requests to follow everyone, regardless of whether you're already following them.

RUBY:
  1. #!/usr/bin/env ruby
  2.  
  3. require 'net/http'
  4.  
  5. begin
  6.   require 'json'
  7. rescue LoadError
  8.   STDERR.puts <<EOF
  9.  
  10. No JSON parser found. Please run the following command to install:
  11.  
  12.   sudo gem install json
  13.  
  14. EOF
  15.   raise
  16. end
  17.  
  18. module FollowMeStephen
  19.   def run
  20.     auth_user, password = get_user_details
  21.     requestors = fetch_requestors
  22.     requestors.each do |user|
  23.       follow user, auth_user, password
  24.     end
  25.   end
  26.  
  27.   private
  28.  
  29.   def get_user_details
  30.     print 'Please enter your Twitter username: '
  31.     auth_user = gets.chomp
  32.     print 'Please enter your Twitter password: '
  33.     password = gets.chomp
  34.     return auth_user, password
  35.   end
  36.  
  37.   def fetch_requestors
  38.     requestors = []
  39.     puts 'Searching for hashtag "followmestephen"...'
  40.     query = '?q=%23followmestephen&rpp=150'
  41.     while query do
  42.       search = JSON.parse(get("/search.json#{query}"))
  43.       puts "Received page #{search['page']}"
  44.       requestors += search['results'].map {|r| r['from_user']}
  45.       query = search['next_page']
  46.     end
  47.     requestors.uniq
  48.   end
  49.  
  50.   def follow user, auth_user, password
  51.     print "Following #{user}... "
  52.     result = post "/friendships/create/#{user}.json", auth_user, password
  53.     puts result
  54.   end
  55.  
  56.   def get path
  57.     Net::HTTP.get 'search.twitter.com', path
  58.   end
  59.  
  60.   def post path, auth_user, password
  61.     request = Net::HTTP::Post.new path
  62.     request.basic_auth auth_user, password
  63.     response = Net::HTTP.new('twitter.com').start {|http| http.request(request)}
  64.     (response.kind_of? Net::HTTPSuccess) ? 'OK' : 'Failed'
  65.   end
  66. end
  67.  
  68. include FollowMeStephen
  69. run