Yoan Blanc’s weblog

Another swiss guy lost in Paris

November 2007

Ajax is everywhere, everyone loves it. You can play with it easily with any popular JavaScript libraries, in the Firebug console or even from Spidermonkey (a JavaScript shell). Ajax the giant has a little brother called Comet, unlike Ajax Comet works but pushing the data from the server to the client and not by polling the server. It’s used in (i.e. financial) websites to get the page updated every couple of seconds.

There are many tools to do this like: cometd, mod_pubsub, Nevow Athena (built upon Twisted)… but most of them are productions tools and, for a proof of concept or a little try, sound overkill. The idea of Comet is to have an hidden iframe in which the server will write every n seconds. It’s the same file, so the same connection, so for example PHP will kill it after the 30 seconds limit. The iframe contains a JavaScript call to the parent page to give it the data. Finally the parent page display it.

I’ll build a little financial-like page to display every seconds the load average or my box. NB: it’s completely useless.

Web.py is a very simple Python web framework created for Reddit when they rewrote their code from LISP to Python. Be sure to do the tutorial (install web.py from easy_install).

First of all, the url handling, we’ll have an index page and a js file:

urls = (
 '/(.*).js$', 'js',
 '/(.*)', 'index' )

js is the class that will handle JavaScript files, and index the HTML one. Next, the data for the load average ((1.111, 2.222, 3.333)): load of the last minute, load of the past 5 minutes and the load of the 15 last minutes.

def getloadavg():
 a,b,c = os.getloadavg()
 format = "%.3f"
 return (format % a, format % b, format % c)

The index handler is quite trivial. Give the load average data to the template and display it.

class index(object):
 def GET(self, name):
  web.header('Content-type', 'text/html')

  load = getloadavg()
  print render.index(*load)

Then the js handler that will show every couple of seconds a piece of JavaScript, here the cheetah template:

$def with (a, b, c)
<script type="text/javascript">
 window.parent.update($a, $b, $c);
</script>

The trick is how the server (CherryPy in this case) will handle the connection by keeping it open and flushing it.

class js(object):
 def GET(self, name):
  web.header('Content-type', 'text/html')
  yield web.flush()
  print "<html><body>"
  for i in xrange(1000):
   time.sleep(1)
   load = getloadavg()
   print render.js(*load)
  print "</html></body>"

Everything is done by the flush call. Then the system will loop for a thousand operations and yielding every second the above piece of JavaScript. (see code.py)

In the main template the job is to care the iframe, and handle the data send via the update call. Download the full source tarball and run it: python code.py.

Application of this code are pretty nothing because the main issue of Comet is the server load, the JavaScript side is quite easy (but interesting still). The application of Comet are with financial quotes, chats like GTalk inside GMail (aka Google Mail). The Flash player offers XMLsocket which can be an alternative to the iframe (i.e. Juggernaut for Ruby on Rails). And I’m sure that Twisted can do a better web server for Comet things.

Si tous le monde joue ou a déjà joué avec Ajax (via XMLHttpRequest) via les nombreuses bibliothèques JavaScript, depuis la console de FireBug ou même avec SpiderMonkey (un shell JavaScript tout droit sorti de Gecko). Comet, le petit frère d’Ajax (le titan), partage la même idée de mettre à jour un petit bout de contenu dans une page, sans avoir à la recharger. Ajax fonctionne sur le principe du client demandant l’information au serveur et Comet fait exactement l’inverse, le serveur envoyant l’information au client dès que possible (ou de manière temporisée dans l’idée que ce temps est relativement cours).

Comet with Web.py

Il y a presque autant d’outils pour faire du Comet côté serveur que de célèbres bibliothèques JavaScript pour Ajax comme: cometd, mod_pubsub, Nevow Athena (utilisant Twisted)… De mon point de vue, ils sont essentiellement orientés production et cela ne facilite pas leur approche, juste pour tester.

Donc, comme j’aime faire des choses juste pour tester, à partir de web.py, j’ai tenté de contrefaire le système utilisé sur les sites financiers en mettant à jouer les valeurs concernant la charge de ma machine (load average). Oui, c’est absolument inutile, je confirme.

Comme d’hab. la partie intéressante est en anglais (on ne se refait pas), je ne peux que vous conseiller d’attraper les sources et de tester tout ça. Faire le tutoriel de web.py avant peu servir, c'est rapide et simple.

In fine, pour parler un peu de Comet qui a tout de même un usage très marginal, il est intéressant de connaître ces usages. GMail s’en sert pour gérer les chats au sein du client mail. Il est également possible d’éviter l’usage de l’iframe en se servant d’une animation Flash. Flash permet de se servir d’XMLSocket maintenant un canal de communication ouvert entre le serveur et client. Ce dernier à le grand avantage d’être bi-directionnel (voir Juggernaut pour Ruby on Rails).

Pour ne pas frustrer mon lectorat francophone aussi puissant soit-il, voici une petite astuce JavaScript concernant les expressions régulières qui n’est que trop peu documentée. Le but étant d’avoir le preg_match_all si apprécié en PHP. Échauffement, (servez-vous de Firebug pour tester):

>>> "ab cd ef cb".search(/\b(c.)\b/)
3

En effet, la première occurence d’un c suivi d’une lettre est à la position 3. Avoir une expression régulier comme motif de recherche est intéressant. Maintenant, comment trouver toutes le occurence d’un coup? Étape intermédiaire:

>>> "ab cd ef cb".replace(/\bc(.)\b/, "Z$1")
"ab Zd ef Zb"

Donc il est possible de faire un rechercher remplacer en réutilisant l’expression trouvée. La dernière étape est d'extraire l'expression trouvée.

>>> var matches = [];
>>> "ab cd ef cb".replace(/\bc(.)\b/g,
 function(str, match){
  matches.push([str, match]);
  return str;
 })
"ab cd ef cb"
>>> // then display matches
>>> matches
[["cd", "d"], ["cb", "b"]]

La fonction replace ne fait rien sur la chaîne elle-même (retournant l’entrée sans la modifier, return str) mais a comme effet de bord de remplir un tableau matches des éléments trouvés. Le but initial étant de faire un traitement plus avant qu’un simple rechercher remplacer, mais qu’on peut détourner à d’autre fins.

En espèrant que ça puisse vous servir.

A IM bot as an user interface (kind of)

Yoan BlancSun 11 November 2007, , , , , ,

A while ago, I’ve already played with this kind of tool that looks to be magical. A remote bot that will do some action for you, to communicate with him, you just have to fire up your favorite Instant Messaging software talk with him. How many times a day you do so with your work mates asking little things? I’ll present a bot for Tumblr (yes it already exists, but it’s closed source and for AOL only), it enables you to post easily 4 of the 7 items you can post on Tumblr (mainly due to my laziness). The result is on my temporary Tumblr.

First of all, I’ve chosen Twisted instead of XMPPPy (like Thomas Perl’s Jabberbot) because Twisted contains support for many other protocols like IRC, OSCAR (ICQ) or MSN which could be useful if you need them as well.

Let’s do a very simple bot (the same I did before):

from jabberbot import JabberBot

class DumbBot(JabberBot):
 def gotMessage(self, jid, msg, resource):
  words = msg.split(" ")
  words.reverse()
  self.sendMessage(jid, " ".join(words))

print "DumbBot running, hit Ctrl+C to quit"
DumbBot("greutly@swissjabber.ch", PASSWORD).run()
		

This bot only handle message type of data, feel free to extend it.

To Post on Tumblr, it was required to create a Tumblr client that will take advantage of their REST API which is very simple.

A simple usage of it:

from tumblr import Tumblr, Regular

hello_world = Regular("Hello World!")
blog = Tumblr("greut", EMAIL, PASSWORD)
blog.post(hello_world)

You can also post Quotes, Conversations and Links at the moment.

Now mix the Jabber and the Tumblr things together:

from tumblr import Tumblr, Regular
from jabberbot import JabberBot

class TumblrBot(JabberBot):
 def __init__(self, jid, password, tumblr):
  super(TumblrBot, self).__init__(jid, password)
  self.tumblr = tumblr

 def gotMessage(self, jid, msg, resource):
  post = Regular(msg)
  self.tumblr.post(post)
  self.sendMessage(jid, "posted %s" % \
   self.tumblr.get_url(post))

blog = Tumblr("greut", EMAIL, PASSWORD)

print "TumblrBot running, hit Ctrl+C to quit"
TumblrBot("greutly@swissjabber.ch", PASSWORD, blog).run()

Et voilà, you’ve just posted on Tumblr via Jabber. The above code is the minimal code to achieve this kind of sweetness. The bot (tumblr.tgz) supports more actions and format types.

Going further: you can have a Jabber client on your Blackberry, your iPhone, simple and lightweight that is already heavily used by applications like Remember the Milk or Twitter. The SwissJabber guy made some bots for: the meteo, for asking a phone number, a whois, etc. Most of the time, web applications offer a IM interface, Tumblr does, but internal tools don’t, internal entities to your company for example or relative to your datas you may not want external companies look at. Their is a field of exploration here. Unwanted IM are more disruptive than unwanted emails, but the cool thing with a IM bot is that it knows if you’re either online, away or not and can acting accordingly to this.

A Doodle like that is IM powered for simple and very important things like: “where are we eating today?” One of the most recurrent question inside a company before lunch time. Many things are doable in this area and it’s easy.

J’ai ressorti un vieux bout de code servant à faire un bot Jabber très simple pour tenter d’aller plus loin et en faire une interface à Tumblr, un système de blogging minimaliste (le résultat). Un (ro)bot se greffant sur un système de messagerie instantanée est là pour ces petites actions qui doivent prendre 10 secondes, Remember the Milk et Twitter en font déjà un précieux usage.

Parti sur Twisted Matrix plutôt que XMPPPy (à l’instar de Thomas Perl et son très élégant Jabberbot) car Twisted intègre le support d’autres protocoles comme OSCAR (AOL et ICQ), IRC ou MSN qui peuvent s’avérer utile le besoin venant.

Pour commencer, j’ai réécrit un bot Jabber vide permettant de refaire le bot précédant encore plus simplement, le code source se trouvant dans la partie en anglais (mes excuses).

Puis une couche d’abstraction à l’API REST de Tumblr. Qui est plutôt triviale, toute bonne API se devant de l’être. À l’heure actuelle, seul 4 des 7 types de posts sont supportés (libre à vous).

En enfin, en combinant les deux on obtient un bot permettant qui va faire le pont entre les messages reçu et ceux qu’il va créer sur votre Tumblr personnel (télécharger tumblr.tgz). Différentes syntax suivant le type de message désiré sont comprises, ça n’est pas forcément toujours simple de trouver la syntaxe adéquate aux besoins qu’on va en faire (le grand avantage d’avoir un contrôle sur lui est qu’on peut choisir ce genre de détails là).

Aller plus loin: Monsieur Swissjabber a créé différents bots pour différents usages comme la météo, un whois, la recherche de prix sur toppreise, etc. pas forcément très sexy, mais tout aussi inutile que l’application ci-dessus. Si vous pouvez questionner un bot, il peut également en plus de vous répondre vous parler directement, vous tenir informé d’informations pouvant vous être intéressantes. Son plus grand atout est de savoir si vous êtes connecté ou pas. Si à l’heure actuelle bon nombre de services Web (2.0 pour la plupart) offrent l’option messagerie instantanée ou sont supportés par un autre service Web 2.0 imified (qui en a fait son business model), toutes les applications qui concernent vos données, celles de votre entreprise doivent rester entre vos mains d’où la nécessité de gérer ce service-là vous même.

Des services comme Doodle ou PollDaddy fonctionnant uniquement via messagerie instantanée seraient assez fun, la question universelle et récurrante étant : « où va-t-on manger ce midi? » Il faudra toujours des moyens inventifs pour y répondre.

About

meYoan Blanc is a web developer that lives in Paris (19, rue Bichat75010France) works for Yahoo! and comes from Switzerland ( La Chaux-de-Fonds). This web site is for this weblog, a playground for random stuffs and can help keepingconnected with some friends out there.

Get my vCard or contact me by phone (+33 1 74 30 12 92) or email ().

Misc

RSS, list.blogug.ch

This site reflects only my opinion and is not affiliated with my employer.

copyright 2006-2007 — doSimple.ch