Tag Archives: html5

Monitorer un flux twitter en temps réel avec Node.js

Cet article présente rapidement comment, grâce aux APIs de Twitter, Node.Js et un peu d’html5 (websocket) on peut facilement en quelques lignes de codes monitorer une partie du flux twitter.

Le code présente une application que j’ai mise en ligne sur http://t.mkz.me/. Le code ne marche que si le navigateur supporte les WebSocket (Chrome 5 et +, Firefox 4, IE 9 et Safari 5 je crois, pas sûr ;-) )

Comment ça marche ?

Grace à twitter-node, une bibliothèque node.js, on va se connecter à un flux en streaming de Twitter, en particulier maintenant au flux ‘filter’ auquel on va demander tous les tweets inclus dans une zone géographie donnée. Ensuite, pour chaque websocket ouverte sur le node.js (via la bibliothèque node-websocket-server, que j’ai déjà utilisé dans un précédent article, par des clients (notre navigateur par l’intermédiaire d’un script javascript).

Comment le mettre en place:

On choppe la dernière version de node.js:

$ wget http://nodejs.org/dist/node-v0.2.5.tar.gz
$ tar xfz node-v0.2.5.tar.gz
$ cd node-v0.2.5
$ ./configure ; make ; cd ..
[...]

Mais aussi de node-websocket-server et de twitter-node.

$ git clone https://github.com/miksago/node-websocket-server.git
[...]
$ git clone https://github.com/technoweenie/twitter-node.git
[...]

$ export NODE_PATH=$(pwd)/node-websocket-server/lib:$(pwd)/twitter-node/lib
$

A ce moment, on a besoin du code du serveur. On remprend rapidement l’exemple donné par twitter-node et on l’adapte pour utiliser node-websocket-server. Ca nous donne grosso-modo (Notez qu’il faut modifier le login/password…). Dans l’exemple suivant, on va tracker les tweets postés avec des informations de geoloc aux USA, mais on pourrait aussi utiliser des mots (Le faire sur justin bieber est tout simplement royal à suivre):

// server.js
var TwitterNode = require('twitter-node').TwitterNode;
var ws          = require('ws/server');
var http        = require('http');
var sys         = require('sys');

var twit = new TwitterNode({
  user: 'twitter login',
  password: 'twitter password',
  // http://www.findlatitudeandlongitude.com/
  // SW,NE (long, lat, long, lat)
  // France:
  // locations: [ 1.40, 48.2, 3.56, 49.3 ]
  // Europe:
  //locations: [ -12, 36.1, 27.3, 62.44 ]
  // USA:
  locations: [ -170, 24.3, -44, 71 ]
  // on peut aussi tracker des mots:
  //  track: ['bieber', 'justin', 'justinbieber']
});

// L'action est 'filter'. On peut aussi mettre 'sample', etc.
twit.action = 'filter';

twit.addListener('error', function(error) {
  console.log(error.message);
  sys.puts(error.message);
});

// server http pour la websocket
var httpServer = http.createServer(function(req, res){});

// websocket
var server = ws.createServer({
  debug: false
}, httpServer);

server.addListener("connection", function(socket){

    // Fonction qui va nous servir pour envoyer chaque tweet:
    var func = function(tweet) {
        try {
            socket.write(JSON.stringify(tweet));
        }
        catch (e) {
            sys.puts("Socket write error");
        }
    };

    // Evenement qui va avoir lieu a chaque reception d'un tweet.
    twit.addListener('tweet', func);

    socket.addListener("end", function () {
        sys.puts("socket end");
        twit.removeListener('tweet', func);
        socket.end();
    });
});

// On lance le serveur. On notera qu'on le lance sur le port 8001.
server.listen(8001);

// On lance le flux
twit.stream();

sys.puts("Hi world.");

On n’a qu’à le lancer dans le shell:

$ ./node-v0.2.5/node server.js
Hi world.

A ce moment, il nous manque juste le code du client. Un peu d’HTML, on choppe jquery au passage et ça nous donne:

<!-- index.php -->
<html>
<head>
<script src="jquery-1.4.4.min.js" type="text/javascript"></script>
<script type="text/javascript">

$(document).ready(function(){
    // La websocket que l'on stocke "globalement"
    var ws;
    var num_tweet;

    if ("WebSocket" in window) {
        // Ou connecter la websocket (serveur node.js)
        ws = new WebSocket("ws://t.mkz.me:8001/service");

        ws.onopen = function() {};

        ws.onmessage = function (evt) {
            // Quand on recoit un message du serveur...:
            var received_msg = evt.data;
            var tweet = JSON.parse(evt.data);
            var content = tweet.text;
            num_tweet = num_tweet + 1;

            // A chaque tweet on l'affiche:
            if( ! tweet.retweeted_status )
            {
                $('#flux ul').prepend('<li><b>' + tweet.user.screen_name + '</b>: ' + content + '</li>');
                $('#flux ul li').slice(30).remove();
            }
            else
            {
                $('#rt ul').prepend('<li><b>' + tweet.user.screen_name + '</b>: ' + content + '</li>');
                $('#rt ul li').slice(10).remove();
            }

            $('#counter').text( num_tweet + ' tweets' );
        };

        ws.onclose = function() {};
    } else {
        // Pas de support WebSocket.
        $('#flux').append('You need a browser with websocket');
    }
});
</script>
</head>
<body>

<h2>Last tweet</h2>
<span id='counter'></span>
<div id='flux'>
<ul></ul>
</div>

<h2>Last RT</h2>
<div id='rt'>
<ul></ul>
</div>

</body>
</html>

Et voilà le résultat: http://t.mkz.me/.

Synchronisez vos évènements avec node.js

J’ai passé mon après midi à tester node.js (framework basé sur V8 pour développer des serveurs d’I/O de façon évènementielle), node-websocket-server (composant websocket pour node.js) et l’élément <video> html5. Au final, ça m’a donné de synchroniser le lancement de plusieurs vidéos lancées dans plusieurs navigateurs à partir d’un point unique (j’ai comme idée de créer un cinéma virtuel sur le net où tout le monde pourra voir la même vidéo en même temps ;) ).

Au final, même si j’ai pris un peu de temps, c’est assez simple et il commence à y avoir pas mal d’exemples pour faire tout ça sur le net.

On commence par installer et compiler Node.js:

$ git clone http://github.com/ry/node.git
[...]
$ cd node
$ ./configure
[...]
$ make
[...]
$ file build/default/node
ELF 32-bit LSB executable, Intel 80386, version 1 (GNU/Linux), dynamically linked (uses shared libs), for GNU/Linux 2.6.18, not stripped

Une fois compilé, on pourra valider avec le petit test « hello world » présent sur la home page du site.

Ensuite, pour communiquer entre mon navigateur et le serveur, j’ai décidé d’utiliser les websockets. Pour cela, il faut avoir la lib spéciale pour node.js, et il s’agit de node-websocket-server. Il ne faut pas oublier de setter la variable NODE_PATH pour donner l’emplacement de la bibliothèque, car cela est indispensable au lancement de node.js

$ git clone http://github.com/miksago/node-websocket-server.git
[...]
$ export NODE_PATH=$(pwd)/node-websocket-server/lib

Il faut maintenant se fabriquer un tout petit serveur simple qui relaie juste les évènements.

C’est parti pour serveur.js:

// Modules nécessaires
var http = require("http");
var ws = require("ws");
var sys = require("sys");

function dummy(req, res){
};

var httpServer = http.createServer(dummy);

var server = ws.createServer({
  debug: true
}, httpServer);

server.addListener("listening", function(){
    sys.puts("Listening...");
});

server.addListener("connection", function(conn){
    server.send(conn.id, "Connected as: "+conn.id);
    conn.addListener("message", function(message){
        sys.puts("Message: " + message);
        conn.broadcast("<"+conn.id+"> "+message);
    });
});

server.addListener("close", function(conn){
    sys.puts("Closing...");
    conn.broadcast("<"+conn.id+"> disconnected");
});

// lancement du serveur sur le port 8000
server.listen(8000);

Il faut lancer le serveur. Rien de plus facile:

$ ./node/node serveur.js
Listening…

Pour la partie cliente, pour gagner du temps, on va utiliser jquery. Ca donnera ça pour le html:

<html>
<head>
<script src="jquery-1.4.2.min.js" type="text/javascript"></script>
<script src="client.js" type="text/javascript"></script>
</head>
<body>

<input id="start" type="button" value="start video" />
<input id="stop" type="button" value="stop video" />
<input id="reset" type="button" value="reset video" />

<video id="video" src="lavide.ogv" style="width: 100%; height: 100%;" tabindex="0"></video>

</body>
</html>

Et pour le javascript (client.js):

$(document).ready(function(){
    // La websocket que l'on stocke "globalement"
    var ws;

    if ("WebSocket" in window) {
    // Ou connecter la socket:
        ws = new WebSocket("ws://localhost:8000/service");
        ws.onopen = function() {};
        ws.onmessage = function (evt) {
            // Quand on reçoit un message du serveur...:
            var received_msg = evt.data;

            if(received_msg.match(/start/gi)) { $('#video').get(0).play(); };
            if(received_msg.match(/stop/gi)) { $('#video').get(0).pause(); };
            if(received_msg.match(/reset/gi)) { $('#video').get(0).currentTime = 0; };
        };
        ws.onclose = function() {};
    } else {
        // Pas de support WebSocket.
    }

    // Et les évènements locaux (les boutons)...:
    $('#start').click(function() {
        ws.send("start");
        $('#video').get(0).play();
    });

    $('#stop').click(function() {
        ws.send("stop");
        $('#video').get(0).pause();
    });

    $('#reset').click(function() {
        ws.send("reset");
        $('#video').get(0).currentTime = 0;
    });
});

Et voilà. C’est tout. Chargez la page dans plusieurs sessions du navigateur, et tout devrait rouler. ;)