Fixing the Twitter Timeline in Octopress

On June 11th 2013, Twitter shut down the old 1.0 API and the Twitter sidebar widget of Octopress, the software that powers this blog, suddenly stopped working.

In the 1.1 version of the API, every single call is authenticated by OAuth. The intended goal for Twitter was to shutdown third party Twitter clients, in order to monetize content by shoving ads into the face of their users. This controversial move was announced about a year ago and has already made some victims.

Jason McIntosh published on his blog a technique to replace the built-in Octopress widget by an official Twitter timeline widget. It works, but I think the widget looks very gross, and since it hurts my feelings to use any kind of official twitter “client”, I decided to fix the original Octopress widget instead.

The most obvious way to fix the problem, is to update the twitter.js file to use the new API. However, it is probably not a good idea to put your OAuth tokens in JavaScript, where anybody could grab them and abuse them. These OAuth tokens need to be kept on the server side. The solution I describe below does this in a very straightforward manner.

1. Register as a Twitter developer and get OAuth credentials

  • Log into https://dev.twitter.com/apps with your Twitter credentials (eg: bartheph).
  • Create a new application. (eg: blog.barthe.ph).
  • Accept the controversial rules of the road.
  • Click on “Create my access token”.
  • Write down the following values:
    • Consumer key
    • Consumer secret
    • Access token
    • Access token secret

2. Set up a cron job to generate a static timeline.json file

  • This file will contain the timeline, and be loaded by Octopress’ slightly modified JavaScript code

  • Install ruby with the oauth gem. On Ubuntu, you do something like that:

    sudo apt-get install ruby rubygems
    sudo gem install oauth
    
  • Create a ruby file on the server. You need to put your Twitter OAuth token and choose an output path.

    #!/usr/bin/env ruby
    require 'rubygems'
    require 'oauth'
    
    # Edit config to suit your needs
    config = {
        :consumer_key => 'xxxxx',
        :consumer_secret => 'xxxxxx',
        :oauth_token => 'xxxx-xxxxxx',
        :oauth_token_secret => 'xxxxx',
        :output_path => '/srv/blog.barthe.ph/www/timeline.json'
    }
    
    # Create OAuth context
    oauth = OAuth::Consumer.new(
        config[:consumer_key], 
        config[:consumer_secret],
        { :site => "https://twitter.com", :scheme => :header }
    )
    access_token = OAuth::AccessToken.from_hash(
        oauth, 
        { :oauth_token => config[:oauth_token], :oauth_token_secret => config[:oauth_token_secret] }
    )
    
    # Get timeline
    url = 'https://api.twitter.com/1.1/statuses/user_timeline.json?screen_name=bartheph&trim_user=true&count=22&include_entities=1&exclude_replies=1'
    response = access_token.request(:get, url)
    
    # Write response into output file with JSONP wrapper
    File.open(config[:output_path], 'w') { |file| 
        file.write('processTweeter(')
        file.write(response.body)
        file.write(');')
    }
    
  • Make sure the file is executable and not readable by anyone but the web server, since it contains OAuth tokens.

    chmod 700 /srv/blog.barthe.ph/cron/update_twitter.rb
    
  • Add the ruby script to crontab to run it periodically. Type crontab -e and add a line similar to what follows.

    # Update every 10 minutes
    */10 * * * * /srv/blog.barthe.ph/cron/update_twitter.rb
    

3. Modify Octopress to use timeline.json instead of the Twitter API

  • Edit your local version of source/javascripts/twitter.js.
  • Edit the getTwitterFeed() function. Add a jasonp attribute and change the value of url.

function getTwitterFeed(user, count, replies) {
  count = parseInt(count, 10);
  $.ajax({

//  REMOVED     url: "http://api.twitter.com/1/statuses/user_timeline/" + user + ".json?trim_user=true&count=" + (count + 20) + "&include_entities=1&exclude_replies=" + (replies ? "0" : "1") + "&callback=?"
/* ADDED */ url: 'http://blog.barthe.ph/timeline.json?callback=processTweeter'
    , type: 'jsonp'
/* ADDED */, jsonp: 'processTweeter'
    , error: function (err) { $('#tweets li.loading').addClass('error').text("Twitter's busted"); }
    , success: function(data) { showTwitterFeed(data.slice(0, count), user); }
  })
}

That’s all. As you can see, on my blog, it looks just like before the old API was shut down. Using dynamic server side code goes a bit against the spirit of Octopress, but it’s a very tiny self contained change.