Socket.IOでTwitterのpublicタイムラインをリアルタイムに表示

```
$ npm install -g express
```


```
$ express xml2jsonp
$ cd xml2jsonp
$ npm install
```

```
$ npm install oauth socket.io --save
```


```js:app.js

/**
 * Module dependencies.
 */

var express = require('express');
var socketio = require('socket.io');
var routes = require('./routes');
var user = require('./routes/user');
var http = require('http');
var path = require('path');
var Twitter = require('./libs/twitter');
var settings = require('./libs/oauth_settings');

var app = express();

// all environments
app.set('port', process.env.PORT || 3000);
app.set('views', path.join(__dirname, 'views'));
app.set('view engine', 'jade');
app.use(express.favicon());
app.use(express.logger('dev'));
app.use(express.json());
app.use(express.urlencoded());
app.use(express.methodOverride());
app.use(app.router);
app.use(require('stylus').middleware(path.join(__dirname, 'public')));
app.use(express.static(path.join(__dirname, 'public')));

// development only
if ('development' == app.get('env')) {
  app.use(express.errorHandler());
}

app.get('/', routes.index);
app.get('/users', user.list);

var server = http.createServer(app).listen(app.get('port'), function(){
  console.log('Express server listening on port ' + app.get('port'));
});

var io = socketio.listen(server);
var tw = new Twitter(settings);
tw.streaming('sample', function(stream){
    stream.on('data',function(data){
        console.log(data.text);
        if(data.text){
            io.sockets.emit('tweet', data.text);
        }
    });
    
});
```

```js:routes/index.js

/*
 * GET home page.
 */

exports.index = function(req, res){
    res.render('index', { title: 'Twitter Public Timeline' });
};
```

```js:libs/twitter.js
var OAuth = require('oauth').OAuth
  , querystring = require('querystring')
  , events = require('events');

module.exports = Twitter;

function Twitter(oauthInfo) {
  if (!oauthInfo) throw new Error('Oauth情報を指定してください');

  this.options = {
    consumer_key       : oauthInfo.consumer_key,
    consumer_secret    : oauthInfo.consumer_secret,
    access_token       : oauthInfo.access_token,
    access_token_secret: oauthInfo.access_token_secret,

    headers: {
      'Accept'    : '*/*',
      'User-Agent': 'node.js book sample'
    },

    request_token_url: 'https://api.twitter.com/oauth/request_token',
    access_token_url : 'https://api.twitter.com/oauth/access_token',
    authorize_url    : 'https://api.twitter.com/oauth/authorize',
    callback_url     : oauthInfo.callback_url,

    restapi_base_url: 'http://api.twitter.com/1/',
    stream_base_url : 'https://stream.twitter.com/1/statuses/'
  };

  this.oauth = new OAuth(
    this.options.request_token_url,
    this.options.access_token_url,
    this.options.consumer_key,
    this.options.consumer_secret,
    '1.0a',
    this.options.callback_url,
    'HMAC-SHA1',
    null,
    this.options.headers
  );
}

Twitter.prototype.streaming = function(method, params, callback) {
  if (typeof params === 'function') {
    callback = params;
    params = null;
  }

  var url = this.options.stream_base_url + method + '.json?' + querystring.stringify(params);
  var stream = new events.EventEmitter();
  var CRLF = '\r\n';
  var CRLF_LENGTH = 2;

  var request = this.oauth.get(
    url,
    this.options.access_token,
    this.options.access_token_secret
  );

  request.on('response', function(response) {
    response.setEncoding('utf8');
    var buffer = '';

    response.on('data', function(chunk) {
      buffer += chunk;
      var index, json;

      while((index = buffer.indexOf(CRLF)) > -1) {
        json = buffer.slice(0, index);
        buffer = buffer.slice(index + CRLF_LENGTH);

        if (json.length > 0) {
            console.log(json);
            
        try {
            stream.emit('data', JSON.parse(json));
          } catch (error) {
            stream.emit('error', new Error('Parse Error: ' + error.message));
          }
          
        }
      }
    });
    response.on('end', function() {
      stream.emit('end');
    });
  });
  request.end();

  callback(stream);
  return this;
}
```

```js:libs/oauth_settings.js
module.exports = {
  'consumer_key'       : 'YOUR CONSUMER KEY',
  'consumer_secret'    : 'YOUR CONSUMER SECRET',
  'access_token'       : 'YOUR ACESS TOKEN',
  'access_token_secret': 'YOUR ACCESS TOKEN SECRET'
}
```

```jade:views/layout.jade
doctype html
html
    head
        title= title
        link(rel='stylesheet', href='/stylesheets/style.css')
        script(src='http://code.jquery.com/jquery-1.11.0.min.js')
        script(src='/socket.io/socket.io.js')
        script
            :js
                var socket = io.connect();
                socket.on('tweet', function(tweet){
                    if($('.tweet').size() > 20){
                        $('.tweet:last').remove();
                    }
                    $('#timeline').prepend('<p class="tweet">' + tweet + '</p>')
                });
    body
        block content
```

```jade:views/index.jade
extends layout

block content
    h1= title
    p Welcome to #{title}
    #timeline
```