Changes between Initial Version and Version 1 of XMPPClients


Ignore:
Timestamp:
Feb 29, 2008, 4:48:01 PM (14 years ago)
Author:
ralphm
Comment:

--

Legend:

Unmodified
Added
Removed
Modified
  • XMPPClients

    v1 v1  
     1= XMPP Clients =
     2
     3Wokkel supports two different approaches to create XMPP client applications, one for persistent connections and one for one-off purposes. This builds further on the XMPP client functionality that is provided by Twisted Words, while providing support for so-called [SubProtocols subprotocols].
     4
     5== Persistent Clients ==
     6
     7Persistent clients are meant to be used for extended periods, where the application wants to exchange communications with other entities. Instances of `wokkel.client.XMPPClient` are Twisted services that connect to an XMPP server and act as a stream manager that can be assigned as the parent of subprotocol implementations.
     8
     9=== Basic XMPP client ==
     10The following example creates the most basic XMPP client as a [http://twistedmatrix.com/projects/core/documentation/howto/application.html Twisted Application].
     11
     12{{{
     13#!python
     14#
     15# client1.tac - A basic XMPP client
     16#
     17
     18from twisted.application import service
     19from twisted.words.protocols.jabber.jid import JID
     20
     21from wokkel import client
     22
     23jid = JID("user@example.org")
     24password = 'secret'
     25
     26application = service.Application('XMPP client')
     27xmppClient = client.XMPPClient(jid, password)
     28xmppClient.logTraffic = True
     29xmppClient.setServiceParent(application)
     30}}}
     31
     32First we create the application object, to later add services to. The XMPP client service is passed a `JID` instance and the associated password as login credentials to the server. This assumes that the server is set up properly, so that the client can connect to the server by extracting the domain name from the JID and retrieving the server's address by resolving it through DNS (optionally by using SRV records). To see what is exchanged between the client and server, we enable traffic logging. Finally, we set the application object as the parent of the XMPP client service. This ensures that the service gets started properly.
     33
     34The client can be started with the command {{{twistd -noy client.tac}}}. The application will start while logging to the terminal it was started from, including the traffic log we enabled. The final lines should look similar to this for a regular XMPP server:
     35
     36{{{
     37...
     382008-02-29 14:21:08+0100 [XmlStream,client] SEND: "<iq type='set' id='H_1'><session xmlns='urn:ietf:params:xml:ns:xmpp-session'/></iq>"
     392008-02-29 14:21:08+0100 [XmlStream,client] RECV: '<iq type="result" id="H_1" to="test@example.org/ca05ba89"><session xmlns="urn:ietf:params:xml:ns:xmpp-session"/></iq>'
     40}}}
     41
     42This client does not do much beyond logging into the server, and we can shut it down by pressing `CTRL-C`.
     43
     44=== Adding a subprotocol handler ===
     45
     46The next step is to add a subprotocol handler to the client, the presence handler:
     47
     48{{{
     49#!python
     50#
     51# client2.tac - A basic XMPP client sending presence
     52#
     53
     54from twisted.application import service
     55from twisted.words.protocols.jabber.jid import JID
     56
     57from wokkel import client, xmppim
     58
     59jid = JID("user@example.org")
     60password = 'secret'
     61
     62application = service.Application('XMPP client')
     63xmppClient = client.XMPPClient(jid, password)
     64xmppClient.logTraffic = True
     65xmppClient.setServiceParent(application)
     66
     67presence = xmppim.PresenceClientProtocol()
     68presence.setHandlerParent(xmppClient)
     69presence.available()
     70}}}
     71
     72The `PresenceClientProtocol` instance has a number of methods for sending presence, but can also be subclassed to process incoming presence
     73from contacts. For now we just add the handler to our client by setting the handler's parent to the client service. Then we ask it to send
     74available presence. Although we are not connected at this point yet, the corresponding message will be stored by the client service and sent as soon as we have successfully connected and authenticated.
     75
     76== One-off clients ==
     77
     78Sometimes, you just want a client that logs in, do a short task, log out again and stop the application. For this, wokkel has the `DeferredClientFactory`. As the name suggests, it is based on working with deferreds. In the following example we create a subprotocol handler for inquiring a server for what software and the version it is running.
     79
     80{{{
     81#!python
     82#
     83# one-off-client.tac - A one-off XMPP client
     84#
     85from twisted.application import service
     86from twisted.internet import reactor
     87from twisted.python import log
     88from twisted.words.protocols.jabber.jid import JID
     89from twisted.words.protocols.jabber.xmlstream import IQ
     90
     91from wokkel import client, subprotocols
     92
     93NS_VERSION = 'jabber:iq:version'
     94
     95def getVersion(xmlstream, target):
     96    def cb(result):
     97        version = {}
     98        for element in result.query.elements():
     99            if (element.uri == NS_VERSION and
     100                element.name in ('name', 'version')):
     101                version[element.name] = unicode(element)
     102        return version
     103
     104    iq = IQ(xmlstream, 'get')
     105    iq.addElement((NS_VERSION, 'query'))
     106    d = iq.send(target.full())
     107    d.addCallback(cb)
     108    return d
     109
     110def printVersion(version):
     111    print "Name: %s" % version['name'].encode('utf-8')
     112    print "Version: %s" % version['version'].encode('utf-8')
     113
     114jid = JID("user@example.org")
     115password = 'secret'
     116
     117application = service.Application('XMPP client')
     118
     119factory = client.DeferredClientFactory(jid, password)
     120factory.streamManager.logTraffic = True
     121
     122d = client.clientCreator(factory)
     123d.addCallback(lambda _: getVersion(factory.streamManager.xmlstream,
     124                                   JID(jid.host)))
     125d.addCallback(printVersion)
     126d.addCallback(lambda _: factory.streamManager.xmlstream.sendFooter())
     127d.addErrback(log.err)
     128d.addBoth(lambda _: reactor.callLater(1, reactor.stop))
     129}}}
     130
     131In this example we dive a little deeper in the XMPP protocol handling. Instead of using the more polished `XMPPClient`, we create a protocol factory that is responsible for handling the protocol once a connection has been made, including logging in and setting up a stream manager. The `clientFactory` however is responsible for establishing the connection. When it does, and the connection has been initialized (which includes authentication), the returned deferred will be fired. In case of a connection or initialization error, the deferred will have its errback called instead.
     132
     133We can now use this deferred to add callbacks for our one-time tasks. The first callback we add is `getVersion`, while using a lambda construct to ignore the result of the callback. We pass the object that represents the XML stream, as stored in the factory's stream manager. This is needed for tracking the response to the version query. The second parameter is the JID that we want to send the version request to, in this case, the server that holds the account we login with.
     134
     135The second callback uses the result from the version request, a dictionary with the keys `name` and `version` to hold the software name and version strings as reported by our server.
     136
     137The third callback neatly closes the stream.
     138
     139In case of any error, the added errback handler just logs it.
     140
     141Finally we add a callback that is always called, shutting down the application after one second.