Building a Twitter mashup

by Blake Schwendiman on May 22, 2009

This week at Squidoo we released a new Twitter tool that provides a way to aggregate, archive and evangelize the best tweets by topic. We call it TwttrList. Having been a part of its development I wanted to write a brief post about the experience.

TwttrList is deeply integrated with the Twitter API, taking advantage of the search, timeline, status, and favorites interfaces. Because of the timing of our release relative to the release, subsequent unrelease and re-release of OAuth for Twitter, we built almost all of the API requests to support both OAuth and username/password. The code for any Twitter request looks like this (slightly simplified):

/**
 * Return favorite tweets list for user specified by OAuth or user/pass.
 *   doc: http://apiwiki.twitter.com/Twitter-REST-API-Method%3A-favorites
 *
 * @param MemberTwitterData object
 * @return list of favorites or null
 */
public static function getUserFavorites($client_twitter_data, $page = 1) {
  $cache_key = internal_hash($client_twitter_data, ...);
  if ($res = ApplicationHelper::memcacheGet($cache_key)) {
    if ($res == 'none') {
      return null;
    } else {
      return $res;
    }
  }
   if (!self::$use_oauth) {
    $curl_options = array(CURLOPT_POST => 0,
                          CURLOPT_HTTPHEADER => array('Expect:'),
                          CURLOPT_USERPWD => $client_twitter_data->screen_name . ':' . $client_twitter_data->twitter_password);
 
    $results = ApplicationHelper::rest_connect('https://twitter.com/favorites.json', ...,  $curl_options);
    if ($results === false) {
      $results = null;
    }
  } else {
    $oauth = new TwitterOAuth(self::$app_token, self::$app_secret, $client_twitter_data->oauth_token, $client_twitter_data->oauth_secret);
    $results = $oauth->OAuthRequest('https://twitter.com/favorites.json', array('page' => $page), 'GET');
    if ($oauth->lastStatusCode() != 200) {
      $results = null;
    }
  }
 
  if ($results) {
    $results = json_decode($results);
    ApplicationHelper::memcacheSet($cache_key, $results, ...); 
  } else {
    ApplicationHelper::memcacheSet($cache_key, 'none', ...); 
  }
 
  return $results;
}

The Twitter OAuth library I chose is the one provided by Abraham Williams and despite the warnings in the comments about not using the library in production code, I found the library to be very stable and usable.

With this structure in place, we were able to switch between using OAuth and Twitter usernames/passwords by simply setting a single boolean value in the Twitter helper class. With the boolean use_oauth set to false, the authentication popup screen we developed displays a username/password form. When true, the popup screen goes through the actual OAuth process. Switching mid-stream is possible as well because each time authentication is required we can simply re-request the new user credentials.

TwttrList obviously uses a lot of request caching. We cache everything we possibly can using timeouts for each type of data based on our best judgment. Search requests are cached for a shorter time than user favorites, for example.

The final large coding piece was to develop a reusable Javascript class to provide consistent display and AJAX functionality on all the pages that are involved in the TwttrList lens display. This class is mostly agnostic to any framework (jQuery, Prototype) because Squidoo is transitioning between frameworks at the moment. The JS for TwttrList is lightweight and very reusable.

The real magic of TwttrList is in its usability, concept and design which is a group effort by the whole Squidoo team. It takes a village to create a great software application and I’m part of one of the best villages around. The creative elements of the design and the constant attention to usability details throughout the application make TwttrList something more than just the nuts and bolts of the code.

Take a look at TwttrList today and let me know what you think. I’d love to answer questions about the implementation (within reason), so ask away!

Comments:

Comments on this entry are closed.