Keith Smiley - Who?

Automated Google Reader Backups

I spend a lot of time in my RSS Reeder (see what I did there?). I still find Google Reader to be the best and easiest way to manage my subscriptions, although I've been wanting to switch to Fever for a while.

One thing I wanted to do when I launched my new site (the one you're reading) was to have a downloadable up to date export of my Google Reader OPML file (which of course I never did). I looked around for good ways to automate this and I found a simple Python script to do it with (sorry I couldn't find it again for this post). I decided to rewrite it in Ruby and set it up on my server as an automated cron job.

To run the script I came up with use something like:

ruby path/to/googleReaderOPML.rb username@gmail.com SekretPassword

To add it to your crontab (to run every Sunday at 1:01am) use something like:

1 1 * * 7 ruby path/to/googleReaderOPML.rb username@gmail.com SekretPassword
#!/usr/bin/env ruby

#
# => This script will authorize your Google credentials and download your Google Reader subscriptions
# => Usage: ./googleReaderOPML.rb GOOGLEUSERNAME PASSWORD
#

# The required networking shenanigans
require 'uri'
require 'net/http'
require 'open-uri'

require 'rubygems'
# This requires the 'colorize' gem. Install with '[sudo] gem install colorize'
require 'colorize'


# The base Google URLs for callback, authentication, and subscription export
$GOOGLE_URL = "http://www.google.com"
$LOGIN_URL = "https://www.google.com/accounts/ClientLogin"
$READER_URL = "http://www.google.com/reader/subscriptions/export"

# The user agent string, for some reason this is required, feel free to change it
$SOURCE = "keith.so"

# The default output filename, it is automatically overwritten if one already exists
$FILE_NAME = "googlereadersubscriptions.opml"


# Make sure there is the correct number of arguments
if ARGV.count != 2
    # Print the instruction
    puts "Usage: ./#{ File.basename(__FILE__) } USERNAME PASSWORD".red
    exit
end

# Build the request URL
uri = URI.parse($LOGIN_URL)

# Setup the Parameters
params = { Email: ARGV.first, Passwd: ARGV.last, service: "reader", source: $SOURCE, continue: $GOOGLE_URL }

# Add the user-agent string, my website (feel free to replace it) to the headers
headers = { "User-agent" => $SOURCE }

# Encode the parameters into the url
uri.query = URI.encode_www_form(params)

# Create a new NET:HTTP object with the request URL
http = Net::HTTP.new(uri.host, uri.port)

# Require HTTPS without this net/http will not be happy with you
http.use_ssl = true

# Execute the request
request = Net::HTTP::Get.new(uri.request_uri, headers)

# Get the data from the request
response = http.request(request)

# Check for valid response code, should ONLY be 200
if response.code != '200'
    puts "Google returned #{ response.code }, check your username and password".red
    exit
end

# split each token into a different item then load them each into a hash with the key as the token key
auth_hash = Hash.new
response.body.split(/\n/).each do |token|
    split_array = token.split('=')
    auth_hash[split_array.first.downcase] = split_array.last
end

# Create a header hash for the request of the XML file
headers = { "user-agent" => $SOURCE, "cookie" => "Name=SID;SID=#{ auth_hash['sid'] };Domain=.google.com;Path=/;Expires=160000000000", "authorization" => "GoogleLogin auth=#{ auth_hash['auth'] }" }

# Open the URL for the Google Reader export with the setup headers
request = open($READER_URL, headers)

# Open the received XML feeds file
google_reader_file = File.open(request, 'r')

# Read the entire feeds file into 'subscriptions'
subscriptions = google_reader_file.read

# Close the downloaded file
google_reader_file.close()

# Open a new file with the global filename to write to, overwrite it if it exists
subscriptions_file = File.open($FILE_NAME, 'w')

# Verify the file was created
if File.exists?(subscriptions_file)
    # Write the subscriptions to the file and close it
    subscriptions_file.write(subscriptions)
    subscriptions_file.close()

    # Display a success message
    puts "Wrote Google Reader subscriptions to #{ $FILE_NAME }".green
else
    # If the file wasn't created print an error
    puts "Couldn't write to #{ $FILE_NAME } (the process running this script may not have sufficient privileges".red
end