tweetcast/server-nodejs/tweetcast.js
changeset 404 89968844eb7d
parent 380 b3f66379fed7
child 410 bf5cf5a9e737
equal deleted inserted replaced
403:dd1686ae5506 404:89968844eb7d
       
     1 READ_OLD_TWEETS = true;
       
     2 RECORD_NEW_TWEETS = true;
       
     3 
       
     4 tweet_file = flagOption('-f', 'tweets.txt');
       
     5 app_port = flagOption('-p', 8000);
       
     6 tracking_keyword = flagOption('-t', null);
       
     7 user_pass = flagOption('-u', null);
       
     8 
       
     9 if (!user_pass) {
       
    10     console.log("You must provide user credentials on the command-line, e.g. node tweetcast.js -u user:pass")
       
    11     process.exit();
       
    12 }
       
    13 if (!tracking_keyword) {
       
    14     console.log("You must provide keyword(s) on the command-line, e.g. node tweetcast.js -t Bieber")
       
    15     process.exit();
       
    16 }
       
    17 
       
    18 console.log("Tracking keyword "+tracking_keyword);
       
    19 
       
    20 var fs = require('fs'),
       
    21     http = require('http'),
       
    22     https = require('https'),
       
    23     socketio = require('socket.io'),
       
    24     app = http.createServer(httpHandler),
       
    25     io = socketio.listen(app)
       
    26     tweets = [],
       
    27     tweet_ids = [],
       
    28     keys_to_delete = [
       
    29         'in_reply_to_screen_name',
       
    30         'in_reply_to_user_id',
       
    31         'retweeted',
       
    32         'place',
       
    33         'geo',
       
    34         'source',
       
    35         'contributors',
       
    36         'coordinates',
       
    37         'retweet_count',
       
    38         'favorited',
       
    39         'truncated',
       
    40         'possibly_sensitive'
       
    41     ],
       
    42     user_keys_to_delete = [
       
    43         'default_profile_image',
       
    44         'show_all_inline_media',
       
    45         'contributors_enabled',
       
    46         'profile_sidebar_fill_color',
       
    47         'created_at',
       
    48         'lang',
       
    49         'time_zone',
       
    50         'profile_sidebar_border_color',
       
    51         'follow_request_sent',
       
    52         'profile_background_image_url',
       
    53         'profile_background_image_url_https',
       
    54         'followers_count',
       
    55         'description',
       
    56         'url',
       
    57         'geo_enabled',
       
    58         'profile_use_background_image',
       
    59         'default_profile',
       
    60         'following',
       
    61         'profile_text_color',
       
    62         'is_translator',
       
    63         'favourites_count',
       
    64         'listed_count',
       
    65         'friends_count',
       
    66         'profile_link_color',
       
    67         'protected',
       
    68         'location',
       
    69         'notifications',
       
    70         'profile_image_url_https',
       
    71         'statuses_count',
       
    72         'verified',
       
    73         'profile_background_color',
       
    74         'profile_background_tile',
       
    75         'utc_offset'
       
    76     ],
       
    77     content_types = {
       
    78         css : "text/css; charset=utf-8",
       
    79         html : "text/html; charset=utf-8",
       
    80         js : "text/javascript; charset=utf-8",
       
    81         png : "image/png"
       
    82     };
       
    83 
       
    84 function wrapContentType(ext) {
       
    85     return {
       
    86         "Content-Type" : ( content_types[ext] ? content_types[ext] : 'text/plain' )
       
    87     }
       
    88 }
       
    89 
       
    90 function httpHandler(req, res) {
       
    91     console.log("HTTP Request for URL "+req.url);
       
    92     var url = req.url.split('?')[0];
       
    93     url = __dirname + "/client" + url + ( url[url.length - 1] == "/" ? "index.html" : "" );
       
    94     var ext = url.split('.').slice(-1)[0].toLowerCase();
       
    95     fs.readFile( url, function(err, data) {
       
    96         if (err) {
       
    97             console.log("Error 404");
       
    98             res.writeHead(404);
       
    99             return res.end('File not found');
       
   100         }
       
   101         res.writeHead(200, wrapContentType(ext));
       
   102         res.end(data);
       
   103     });
       
   104 }
       
   105 
       
   106 function flagOption(flag, defaultValue) {
       
   107     var flagPos = process.argv.indexOf(flag);
       
   108     return ( flagPos != -1 && flagPos < process.argv.length - 1) ? process.argv[flagPos + 1] : defaultValue;
       
   109 }
       
   110 
       
   111 function addToList(tweet) {
       
   112     if (tweet_ids.indexOf(tweet.id) != -1) {
       
   113         console.log("Error: Tweet already in list");
       
   114         return false;
       
   115     }
       
   116     tweets.push(tweet);
       
   117     tweet_ids.push(tweet.id);
       
   118     return true;
       
   119 }
       
   120 
       
   121 function textids(object) {
       
   122     for (var key in object) {
       
   123         // Workaround for Unicode bug in socket.io.
       
   124         
       
   125         if (typeof object[key] == "string") {
       
   126             var tmp = '';
       
   127             for (var i = 0; i < object[key].length; i++) {
       
   128                 tmp += ( object[key].charCodeAt(i) < 128 ? object[key].charAt(i) : "&#" + object[key].charCodeAt(i) + ";" );
       
   129             }
       
   130             object[key] = tmp;
       
   131         }
       
   132         if (key.substr(-2) == 'id') {
       
   133             object[key] = object[key + '_str'];
       
   134             delete object[key + '_str'];
       
   135         }
       
   136     }
       
   137 }
       
   138 
       
   139 function readTweetsFromFile(file_name) {
       
   140     console.log("Trying to read tweets from " + file_name);
       
   141     try {
       
   142         var oldtweets = fs.readFileSync(file_name, 'utf8').split('\n');
       
   143         var tweetscopied = 0;
       
   144         for (var i in oldtweets) {
       
   145             if (oldtweets[i].length > 0) {
       
   146                 addToList(JSON.parse(oldtweets[i]));
       
   147                 tweetscopied++;
       
   148             }
       
   149         }
       
   150         console.log(tweetscopied + " tweets copied");
       
   151     }
       
   152     catch (err) {
       
   153         console.log("Error opening "+file_name);
       
   154     }
       
   155 }
       
   156 
       
   157 function requestTweets() {
       
   158     console.log("Fetching tweets from https://stream.twitter.com/1/statuses/filter.json")
       
   159     var writestream = null;
       
   160     var req = https.request({
       
   161         host: "stream.twitter.com",
       
   162         path: "/1/statuses/filter.json",
       
   163         method: "POST",
       
   164         headers: {
       
   165             'Authorization': 'Basic ' + new Buffer( user_pass ).toString('base64'),
       
   166             'Content-Type': 'application/x-www-form-urlencoded'
       
   167         }
       
   168     }, function(res) {
       
   169         writestream = fs.createWriteStream( tweet_file, { flags: 'a+', encoding: 'utf-8' } );
       
   170         console.log('Response received, status : ' + res.statusCode);
       
   171         res.setEncoding('utf8');
       
   172         res.on('data', function(chunk) {
       
   173             var newdata = chunk.split('\r\n'),
       
   174                 tweetpos = tweets.length;
       
   175             try {
       
   176                 for (var i in newdata) {
       
   177                     if (newdata[i].length > 0) {
       
   178                         var tweet = JSON.parse(newdata[i]);
       
   179                         textids(tweet);
       
   180                         for (var j in keys_to_delete) {
       
   181                             delete tweet[keys_to_delete[j]];
       
   182                         }
       
   183                         textids(tweet.user);
       
   184                         for (var j in user_keys_to_delete) {
       
   185                             delete tweet.user[user_keys_to_delete[j]];
       
   186                         }
       
   187                         if (tweet.retweeted_status) {
       
   188                             textids(tweet.retweeted_status);
       
   189                             for (var j in keys_to_delete) {
       
   190                                 delete tweet.retweeted_status[keys_to_delete[j]];
       
   191                             }
       
   192                         }
       
   193                     /*    tweet.date_value = Date.parse(tweet.created_at); */
       
   194                         if (addToList(tweet)) {
       
   195                             writestream.write(JSON.stringify(tweet)+'\n');
       
   196                         }
       
   197                     }
       
   198                 }
       
   199                 io.sockets.emit('update', {
       
   200                         "new_tweets" : tweets.slice(tweetpos)
       
   201                     });
       
   202                 console.log("New tweets received. We now have", tweets.length, "tweets in memory");
       
   203             }
       
   204             catch(err) {
       
   205                 console.log(err.message);
       
   206             }
       
   207         });
       
   208     });
       
   209     
       
   210     req.write('track=' + encodeURIComponent( ( tracking_keyword ) ) );
       
   211     req.end();
       
   212 }
       
   213 
       
   214 app.listen(app_port);
       
   215 console.log("Listening on port: "+app_port);
       
   216 
       
   217 if (READ_OLD_TWEETS) {
       
   218     readTweetsFromFile(tweet_file);
       
   219 } else {
       
   220     console.log("Reading old tweets disabled !");
       
   221 }
       
   222 
       
   223 if (RECORD_NEW_TWEETS) {
       
   224     requestTweets();
       
   225 } else {
       
   226     console.log("Recording new tweets disabled !");
       
   227 }
       
   228 
       
   229 io.set('log level', 0);
       
   230 io.sockets.on('connection', function(socket) {
       
   231     console.log("New connection from "+socket.handshake.address.address);
       
   232     socket.emit('initial_data', {
       
   233         "tweets" : tweets
       
   234     });
       
   235 });