Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Building a Real-Time Twitter Game in Node.js

Building a Real-Time Twitter Game in Node.js

Walking through the process of building a Node.js mashup of Twitter and PubNub.

phildeschaine

July 06, 2012
Tweet

Other Decks in Technology

Transcript

  1. PubNub Building a Real-Time Twitter Game in Node.js by Phil

    Deschaine Developer Evangelist PubNub Friday, July 6, 12
  2. PubNub the Internet ½ adults online worldwide Ping anyone in

    ¼ seconds 1.2 billion mobile web users Friday, July 6, 12
  3. PubNub The Reality of Most Apps No real human interaction

    No ability to instantly react Friday, July 6, 12
  4. PubNub Real-Time is complex and expensive Polling Long Polling WebSockets

    TCP UDP C2DM APNS SPDY SSL Network Config Load Balancing Global Scaling Connection dropouts Application State Data “Fan-Out” Maintaining Open Sockets Kernel Config Messaging frameworks Security & Encryption iOS Android Blackberry Dumb Connected Phones Firefox Chrome/Safari IE 6, 7, 8, 9, 10 PHP Python Java Ruby C Erlang Appcelerator/Corona/Sencha Protocols Ops Environments Friday, July 6, 12
  5. PubNub PubNub Democratizes Real-Time Apps Connecting everyone on Earth in

    < 0.25 seconds PubNub is a blazing fast cloud-hosted real-time messaging system for web and mobile apps. Friday, July 6, 12
  6. PubNub Engineered for Simplicity Up-and-Running in 5 minutes // listen

    for events PUBNUB.subscribe({ channel : "hello_world", callback : alert }) // send events PUBNUB.publish({ channel : "hello_world", message : "data" }) Friday, July 6, 12
  7. PubNub Distributed globally in 10 data centers Replicated points-of-presence for

    global reach Blazing fast: 1,000,000/second with < 50ms latency Close to the metal (C native codebase) Friday, July 6, 12
  8. PubNub Works Everywhere Every device, every server, every real-world environment

    JavaScript iOS Android Ruby PHP Python Perl Erlang NodeJS PhoneGap Objective C Java C Corona Lua Flash Perl 5 .NET Silverlight HTML5 Appcelerator Friday, July 6, 12
  9. PubNub Our Stack - Backend •Node.js v0.6.* •Flatiron (+Union) for

    routing •Ecstatic for file routing •Plates for templating •Notejitsu for hosting / deployment •PubNub for messaging Friday, July 6, 12
  10. PubNub FlatIron var flatiron = require('flatiron'); var app = flatiron.app;

    app.use(flatiron.plugins.http); app.router.get('/', function () { console.log('/'); var flat = this; fs.readFile('index.html', function(err, buffer) { var html = buffer.toString(); flat.res.writeHead(200, { 'Content-Type': 'text/html' }); flat.res.end(html); }); }); Friday, July 6, 12
  11. PubNub Plates plates = require('plates'); app.router.get('/', function () { console.log('/');

    var flat = this; fs.readFile('index.html', function(err, buffer) { var html = buffer.toString(); var output = plates.bind(html, {'foo': 'bar'}); flat.res.writeHead(200, { 'Content-Type': 'text/html' }); flat.res.end(output); }); }); Friday, July 6, 12
  12. PubNub Plates - on the client <div id="content"> <h1>Tweet Guesser

    </h1> <p id='foo'></p> </div> Friday, July 6, 12
  13. PubNub ./settings.js and PubNub var pubnub = require('pubnub'), settings =

    require('./settings.js'), events = new (require('events').EventEmitter)(); var network = pubnub.init({ publish_key : settings.PUBNUB_PUBLISH_KEY, subscribe_key : settings.PUBNUB_SUBSCRIBE_KEY, secret_key : settings.PUBNUB_SECRET_KEY, ssl : false, origin : "pubsub.pubnub.com" }); network.subscribe({ channel : channel, callback : function(message) { if (debug) { console.log("got message: " + JSON.stringify(message)); } events.emit(message.name, message.data); }, error : function(e) { console.log(e); } }); Friday, July 6, 12
  14. PubNub Client-side templating <div id='public_channel' style="display:none"></div> <div id='user_id' style="display:none"></div> <div

    id="publish_key" style="display:none"></div> <div id="subscribe_key" style="display:none"></div> <script src=http://cdn.pubnub.com/pubnub-3.1.min.js></script> Friday, July 6, 12
  15. PubNub PubNub Client Side: main.js var publish_key = document.getElementById('publish_key').innerHTML, subscribe_key

    = document.getElementById('subscribe_key').innerHTML, public_channel = document.getElementById('public_channel').innerHTML, user_id = document.getElementById('user_id').innerHTML; var pubnub = PUBNUB.init({ publish_key : publish_key, subscribe_key : subscribe_key, ssl : false, origin : 'pubsub.pubnub.com' }); pubnub.subscribe({ channel : public_channel, callback : function(message) { console.log("got message on public channel: " + JSON.stringify(message)); pubnub.events.fire(message.name, message.data); }, error : function(e) { console.log(e); } }); Friday, July 6, 12
  16. PubNub Store a signature for every user function newUserConnected() {

    var new_uuid = uuid.v4(); var sig = uuid.v4().substring(0, 8); users[new_uuid] = { 'user_id' : new_uuid, 'connected' : new Date(), 'sig' : sig } return users[new_uuid]; } Friday, July 6, 12
  17. PubNub PubNub Server Side - Private Messages function sendPrivateMessage(name, data,

    to) { var message = { 'name' : name, 'data' : data, 'from' : 'server', 'sig': users[to].sig }; network.publish({ channel : public_channel + "::" + to, message : message, callback : function(info) { if (!info[0]) { console.log(info[1]); } if (debug) { console.log("sent message: " + JSON.stringify(message)); } } }); } Friday, July 6, 12
  18. PubNub PubNub Client Side - Private Messages pubnub.subscribe({ channel :

    private_channel, callback : function(message) { console.log("got message on private channel: " + JSON.stringify(message)); if (message.from != "server") { pubnub.events.fire("private::" + message.name, message.data); } else if (message.sig == sig) { pubnub.events.fire("private::" + message.name, message.data); } else { console.log('signature didnt match'); } }, error : function(e) { console.log(e); } Friday, July 6, 12
  19. PubNub Syntax to catch messages - client side pubnub.events.bind('public::hello_world', function(data)

    { console.log('public hello!'); }); pubnub.events.bind('private::hello_world', function(data) { console.log('private hello!'); }); Friday, July 6, 12
  20. PubNub Modularization required •We still haven’t written any app-specific code!

    •This stuff can and should be reused •It’s a good starting point Friday, July 6, 12
  21. PubNub Server-side: pubnub-node-utils.js var putils = require('./pubnub-node-utils.js'); putils.sendPublicMessage("hello_world", { 'server

    sending a' : 'public message' }); putils.sendPrivateMessage("hello_world", { 'server sending a ' : 'private message' }, new_user.user_id); Friday, July 6, 12
  22. PubNub Client-side: pubnub-browser-utils.js var putils = (function () { var

    utils = {}; // ... // the stuff from before return utils; }()); <script src="js/pubnub_browser_utils.js"></script> Friday, July 6, 12
  23. PubNub Finally: some app specific code! var http = require(‘http’);

    var options = { host: 'twitter.com', port: 80, path: '/search.json? q=the&rpp=10&page=1&include_entities=false&result_type=r ecent' }; var twitter_req = http.get(options, function(res){ ... }); Friday, July 6, 12
  24. PubNub Grab a bunch of words from twitter, pick one

    var random_tweets = JSON.parse(raw_data); var words = []; random_tweets.results.forEach( function(tweet) { console.log(tweet.text); tweet.text.split(' ').forEach( function(word) { if ((word != 'RT') && (word[0] != '@') && (word.length > 4)) { words.push(word.toLowerCase()); } }); }); var random_word = words[Math.floor(Math.random() * words.length)]; Friday, July 6, 12
  25. PubNub async var async = require('async'); function selectWordAtRandom(tweets, callback) {

    //... var random_word = words[Math.floor(Math.random() * words.length)]; callback(null, random_word); // Can put this anywhere in function } async.waterfall([ getRecentTweets, selectWordAtRandom, collectTweetsForGuess, ], function (err, result) { console.log(result); }); Friday, July 6, 12
  26. PubNub Async cont. async.waterfall([ getRecentTweets, selectWordAtRandom, collectTweetsForGuess, pickRandomTweet, dateTweet, ],

    function (err, result) { console.log(current_game.word); console.log(current_game.tweet); console.log(current_game.times_tweeted); console.log(current_game.human_date_diff); }); Friday, July 6, 12
  27. PubNub client-side game start putils.pubnub.events.bind('private::new_game', function(message) { $("#answer_results").html(""); document.getElementById('question').innerHTML =

    "<h2>How many times has the word '" + message.data.word + "' been tweeted in the last " + message.data.diff + "?</h2>"; $('#answer').show(); current_game = message.data; var now = +new Date(), time_diff = now - current_game.posed_at; $('#time_left').html("").countdown({ image: 'img/digits.png', startTime: "" + Math.floor((current_game.length - time_diff) / 1000), format: 'ss' }); }); Friday, July 6, 12
  28. PubNub Deployment { "description": "a fun game for guessing tweets",

    "version": "0.1.0", "dependencies": { "flatiron": "0.2.1", "union": "0.3.2", "ecstatic": "0.1.6", "plates": "0.4.6", "pubnub": "3.1.8", "node-uuid": "1.3.3", "async": "0.1.22" }, "scripts": { "start": "server.js" }, "name": "tweet-guesser", "subdomain": "tweet-guesser", "engines": { "node": "v0.6.x" } } Friday, July 6, 12
  29. PubNub Nodejitsu tweet-guesser $ jitsu apps create info: Welcome to

    Nodejitsu info: It worked if it ends with Nodejitsu ok info: Executing command apps create info: Analyzing your application dependencies in server.js info: Checking app availability tweet-guesser info: Creating app tweet-guesser info: Nodejitsu ok Friday, July 6, 12
  30. PubNub Nodejitsu: deploy tweet-guesser $ jitsu apps deploy warn: Creating

    new snapshot for version 0.1.0-1 info: Done creating snapshot 0.1.0-1 info: Updating application tweet-guesser info: Activating snapshot 0.1.0-1 for tweet-guesser info: Stopping app tweet-guesser info: App tweet-guesser is now stopped info: Starting app tweet-guesser info: App tweet-guesser is now started info: http://tweet-guesser.nodejitsu.com on Port 80 info: Nodejitsu ok Friday, July 6, 12