Twisted, framework réseau EchoBot XMPP

, par MiKaël Navarro

Présentation par l’exemple du framework réseau Twisted

Twisted (prononcer twist-dee) est un framework réseau écrit en Python sous licence MIT

L’exemple que j’ai choisi pour présenté ce framework est un Bot XMPP qui se connecte à un serveur et répond simplement à ses correspondants.

Twisted

Pour commencer Twisted utilise un mécanisme de callbacks (programmation événementielle), c’est à dire que les utilisateurs écrivent de courtes fonctions de rappel qui sont appelées par le framework en temps voulu.

Twisted, et plus spécifiquement Twisted Words (partie consacré aux Instants Messaging et IRC), permet de concevoir un programme client ou serveur XMPP.

Cependant il est très mal documenté et offre, dans l’état, un support très relatif des XEP.

Alors, d’autres frameworks plus faciles d’usage et mieux documentés n’existent-ils pas ?

En faisant le tour des solutions qui s’offrent à nous on a :
 jabber.py mais le projet semble mort depuis 2003 ;
 PyXMPP une implémentation basée sur libxml2, elle est pas mal, légère et permet de faire simplement (au sens facilement) de l’envoi / réception de messages ;
 xmpppy, sans doute la plus complète, c’est celle utilisé par GaJim ;
 A noter aussi l’existence de la librairie sleekxmpp utilisé dans l’ouvrage O’Reilly’s XMPP : The Definitive Guide par Peter Saint-Andre, Kevin Smith et Remko Tronçon.

Alors ? Pourquoi j’ai choisi Twisted Words au lieu de ces librairies ?
Eh, ben parce que ! J’avais envie d’apprendre ce framework réseau et quoi de mieux que de l’user sur un exemple...

Rq. : afin de nous faciliter la vie et avoir le support de quelques XEP, on utilisera Wokkel, une surcouche à Twisted Words.

EchoBot Sub-protocol

Notre Bot parlera un sous-langage personnalisé d’XMPP, il lira les messages qu’on lui envoi et leurs répondra :

# echobot.py

from twisted.words.xish import domish
from wokkel.xmppim import MessageProtocol, AvailablePresence

class EchoBotProtocol(MessageProtocol):
   def connectionMade(self):
       print "Connected!"

       # send initial presence
       self.send(AvailablePresence())

   def connectionLost(self, reason):
       print "Disconnected!"

   def onMessage(self, msg):
       print str(msg)

       if msg["type"] == 'chat' and hasattr(msg, "body"):
           reply = domish.Element((None, "message"))
           reply["to"] = msg["from"]
           reply["from"] = msg["to"]
           reply["type"] = 'chat'
           reply.addElement("body", content="echo: " + str(msg.body))

           self.send(reply)

La classe MessageProtocol attend simplement des messages et appel la méthode onMessage quand il en reçoit un.
Cette classe hérite de XMPPHandler qui est la classe de base pour tout les sous-protocoles.
XMPPHandler appelera connectionMade et connectionLost lors des connections et déconnections, comme le fait les autres classes de protocole du framework Twisted.

Application

Twisted inclue un lanceur d’application appelé twistd et prend un fichier .tac en entrée.
Ce lanceur positionne / paramètre les services de l’application et gère leurs cycle de vie.
Il transforme l’application en daemon, gère les PIDs et toutes les autres tâches communes aux applications.

# echobot.tac

from twisted.application import service
from twisted.words.protocols.jabber import jid
from wokkel.client import XMPPClient

from echobot import EchoBotProtocol

application = service.Application("echobot")

xmppclient = XMPPClient(jid.internJID("user@jabber.fr/echobot"), "pass")
xmppclient.logTraffic = False
echobot = EchoBotProtocol()
echobot.setHandlerParent(xmppclient)
xmppclient.setServiceParent(application)

Remplacer ’user@jabber.fr’ et ’pass’ par votre identifiant JID et votre mot de passe.
L’application EchoBot peut alors être lancé par : twistd -ny echobot.tac ou twistd -y echobot.tac si vous voulez un démon.
Une foi lancé, connectez-vous via votre client Jabber et envoyez-lui un message :)

Chaque fichier tac doit définir les services de l’application et ensuite appeler setServiceParent pour les rattacher à l’application.
Pour les services XMPPClient, nous faisons exactement la même chose en activant notre sous-protocole puis en le rattachant à xmppclient via setHandlerParent.

P.-S.

La suite serait que ce Bot informatique soit à l’écoute des messages de certains utilisateurs "autorisés" et exécute des tâches locales en fonction des commandes envoyées par ces utilisateurs.
L’idée est de pouvoir utiliser un client XMPP sur mon mobile ou autre pour contrôler à distance mon PC sans avoir à établir une connection SSH.