Twisted, framework réseau EchoBot XMPP
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
.