Twitter だって使えるんだぜ。

もう一つ。こちらも必要だったので。

/**
 * Twitter Client for node.js
 */
var sys = require('sys'),
    url = require('url'),
    querystring = require('querystring'),
    http = require('http'),
    crypto = require('crypto'),
    EventEmitter = require('events').EventEmitter;
var OAuth = require('oauth').OAuth;

var OAUTH_CONFIG = {
   RequestTokenUrl : "https://api.twitter.com/oauth/request_token"
   , AccessTokenUrl : "https://api.twitter.com/oauth/access_token"
   , Version : "1.0"
   , Method  : "HMAC-SHA1"
};

var logger = require('./logger').DefaultLogger;
var API_URL = "http://api.twitter.com/1";


var Client = function(consumerKey, consumerSecret,
                      options){
   if(!options) options = {};
   this._oa = new OAuth(
      OAUTH_CONFIG.RequestTokenUrl
      , OAUTH_CONFIG.AccessTokenUrl
      , consumerKey
      , consumerSecret
      , OAUTH_CONFIG.Version
      , null
      , OAUTH_CONFIG.Method
   );
   this._accessKey = options.accessKey;
   this._accessSecret = options.accessSecret;
   this._logger = options.logger || logger;
};

Client.prototype.getAccessToken = function(callback){
   var self = this;
   this._oa.getOAuthRequestToken(function(err, token, token_secret, results){
      if(err){
         callback && callback(err);
      }
      else{
         var stdin = process.openStdin();
         stdin.setEncoding('utf8');
         var url = "http://twitter.com/oauth/authorize?oauth_token=" + token;
         process.stdout.write('please visit ' + url + ' to get verification code.\n');
         process.stdout.write('input verification code: ');
         stdin.on('data', function (chunk) {
            var verifier = chunk.split('\n')[0];
            self._oa.getOAuthAccessToken(
               token, token_secret, verifier,
               function(error, akey, asecret, results2){
                  if(error){
                     callback && callback(error);
                  }else{
                     self._accessKey = akey;
                     self._accessSecret = asecret;
                     callback && callback(null, akey, asecret);
                  }
               }
            );
         });
      }
   });
};

Client.prototype._doGet = function(path, callback){
   var self = this;
   var url = [API_URL, path].join('');
   self._logger.debug('[Twitter] GET {url}', {url: url});
   this._oa.get(url, this._accessKey, this._accessSecret,
                function(error, data, response){
                   self._logger.debug('[Twitter] ' + data);
                    if(callback){
                       callback(error, JSON.parse(data), response);
                    }
                });;
};

Client.prototype._doPost = function(path, body, callback){
   var self = this;
   var url = [API_URL, path].join('');
   self._logger.debug('[Twitter] POST {url}', {url: url});
   self._logger.debug('[Twitter] ' + body);
   this._oa.post(url, this._accessKey, this._accessSecret,
                 body,
                 function(error, data, response){
                    self._logger.debug('[Twitter] ' + data);
                    if(callback){
                       callback(error, JSON.parse(data), response);
                    }
                 });;
}

Client.prototype.update = function(status, params, callback){
   if( typeof params == "function" ) {
      callback= params;
      params = {};
   }
   this._doPost("/statuses/update.json", {status: status}, callback);
}


Client.prototype.follow = function(params, callback){
   if( typeof params == "string"){
      params = {
         "user_id" : params
      };
   }
   this._doPost("/friendships/create.json", params, callback);
}

Client.prototype.unfollow = function(params, callback){
   if( typeof params == "string"){
      params = {
         "user_id" : params
      };
   }
   this._doPost("/friendships/destroy.json", params, callback);
}

var allowedTL = ['public', 'home', 'friends', 'user'];
Client.prototype.timeline = function(type, params, callback){
   if( allowedTL.indexOf(type) == -1 ){
      throw "Invalid timeline type '"+ type + "'";
   }
   var qs = querystring.stringify(params);
   this._doGet('/statuses/' + type + "_timeline.json?" + qs, callback);
}

Client.prototype._handleStream = function(request, callback){
   var logger = this._logger;
   var event = new EventEmitter();
   function _invokeCallback(str){
      var obj, err;
      try{
         obj = JSON.parse(str);
         try{
            event.emit('data', obj);
         }catch(e1){
            logger.error(e1);
         }
      }catch(e2){
         logger.error('Emit error: ' + sys.inspect(e2));
         event.emit('error', e2);
      }

   };
   request.connection.on('error', function(err){
      logger.error('Connection Error: ' + sys.inspect(err));
      event.emit('error', err);
      event.emit('end', err);
   });
   request.on('response', function(response){
      response.setEncoding('utf8');
      response.on('end', function(){
         logger.debug('Connection End');
         event.emit('end');
      });
      var buff = "";
      response.on('data', function(chunk){
         if( chunk.match(/\n/) ){
            var chunks = chunk.split(/\r?\n/);
            var jsonStr = buff + chunks.shift();
            var obj = null, err = null;
            if( jsonStr.length > 0 ){
               _invokeCallback(jsonStr);
            }
            if( chunks.length ){
               buff = chunks.pop(); // last buffer.
            }
            var c = '';
            while(c = chunks.shift()){
               _invokeCallback(c);
            }
         }else{
            buff += chunk;
         }
      });
   });
   return event;
}

Client.prototype.chirpStream = function(params){
   if( !params ){
      params = {};
   }
   var self = this;
   var url = "http://chirpstream.twitter.com/2b/user.json";
   var request = this._oa.get(url, this._accessKey, this._accessSecret);
   var event = this._handleStream(request);
   request.end();
   return event;
}

exports.Client = Client;

再利用できるようにライブラリ化を進めているので、機会があったら github に公開して、npm にも放り込んでおこうかと思います。モダンなAPIにアクセスするためのクライアント実装サンプル、みたいなモノ。