[170] | 1 | XMPP Clients |
---|
| 2 | ============ |
---|
| 3 | |
---|
| 4 | Wokkel supports two different approaches to create XMPP client applications, |
---|
| 5 | one for persistent connections and one for one-off purposes. This builds |
---|
| 6 | further on the XMPP client functionality that is provided by Twisted Words, |
---|
| 7 | while providing support for so-called :term:`subprotocols`. |
---|
| 8 | |
---|
| 9 | Persistent Clients |
---|
| 10 | ------------------ |
---|
| 11 | |
---|
| 12 | Persistent clients are meant to be used for extended periods, where the |
---|
| 13 | application wants to exchange communications with other entities. Instances of |
---|
| 14 | :api:`wokkel.client.XMPPClient <XMPPClient>` are Twisted services that connect |
---|
| 15 | to an XMPP server and act as a stream manager that can be assigned as the |
---|
| 16 | parent of subprotocol implementations. |
---|
| 17 | |
---|
| 18 | Basic XMPP client |
---|
| 19 | ^^^^^^^^^^^^^^^^^ |
---|
| 20 | |
---|
| 21 | The following example creates the most basic XMPP client as a `Twisted |
---|
| 22 | Application |
---|
| 23 | <http://twistedmatrix.com/projects/core/documentation/howto/application.html>`_. |
---|
| 24 | |
---|
| 25 | .. literalinclude:: listings/client/client1.tac |
---|
| 26 | :language: python |
---|
| 27 | :linenos: |
---|
| 28 | |
---|
| 29 | First we create the application object, to later add services to. The XMPP |
---|
| 30 | client service is passed a ``JID`` instance and the associated password as |
---|
| 31 | login credentials to the server. This assumes that the server is set up |
---|
| 32 | properly, so that the client can connect to the server by extracting the domain |
---|
| 33 | name from the JID and retrieving the server's address by resolving it through |
---|
| 34 | DNS (optionally by using SRV records). To see what is exchanged between the |
---|
| 35 | client and server, we enable traffic logging. Finally, we set the application |
---|
| 36 | object as the parent of the XMPP client service. This ensures that the service |
---|
| 37 | gets started properly. |
---|
| 38 | |
---|
| 39 | The client can be started with the command ``twistd -noy client.tac``. The |
---|
| 40 | application will start while logging to the terminal it was started from, |
---|
| 41 | including the traffic log we enabled. The final lines should look similar to |
---|
| 42 | this for a regular XMPP server:: |
---|
| 43 | |
---|
| 44 | ... |
---|
| 45 | 2008-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>" |
---|
| 46 | 2008-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>' |
---|
| 47 | |
---|
| 48 | This client does not do much beyond logging into the server, and we can shut it |
---|
| 49 | down by pressing ``CTRL-C``. |
---|
| 50 | |
---|
| 51 | Adding a subprotocol handler |
---|
| 52 | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ |
---|
| 53 | |
---|
| 54 | The next step is to add a subprotocol handler to the client, the presence |
---|
| 55 | handler: |
---|
| 56 | |
---|
| 57 | .. literalinclude:: listings/client/client2.tac |
---|
| 58 | :language: python |
---|
| 59 | :linenos: |
---|
| 60 | |
---|
| 61 | The :api:`wokkel.xmppim.PresenceProtocol <PresenceProtocol>` instance has a |
---|
| 62 | number of methods for sending presence, but can also be subclassed to process |
---|
| 63 | incoming presence from contacts. For now we just add the handler to our client |
---|
| 64 | by setting the handler's parent to the client service. Then we ask it to send |
---|
| 65 | available presence. Although we are not connected at this point yet, the |
---|
| 66 | corresponding message will be stored by the client service and sent as soon as |
---|
| 67 | we have successfully connected and authenticated. |
---|
| 68 | |
---|
| 69 | One-off clients |
---|
| 70 | --------------- |
---|
| 71 | |
---|
| 72 | Sometimes, you just want a client that logs in, do a short task, log out again |
---|
| 73 | and stop the application. For this, wokkel has the |
---|
| 74 | :api:`wokkel.client.DeferredClientFactory <DeferredClientFactory>`. As the name |
---|
| 75 | suggests, it is based on working with deferreds. In the following example we |
---|
| 76 | create a subprotocol handler for inquiring a server for what software and the |
---|
| 77 | version it is running. |
---|
| 78 | |
---|
| 79 | .. literalinclude:: listings/client/one_off_client.tac |
---|
| 80 | :language: python |
---|
| 81 | :linenos: |
---|
| 82 | |
---|
| 83 | In this example we dive a little deeper in the XMPP protocol handling. Instead |
---|
| 84 | of using the more polished :api:`wokkel.client.XMPPClient <XMPPClient>`, we |
---|
| 85 | create a protocol factory that is responsible for handling the protocol once a |
---|
| 86 | connection has been made, including logging in and setting up a stream manager. |
---|
| 87 | The :api:`wokkel.client.clientFactory <clientFactory>` however is responsible |
---|
| 88 | for establishing the connection. When it does, and the connection has been |
---|
| 89 | initialized (which includes authentication), the returned deferred will be |
---|
| 90 | fired. In case of a connection or initialization error, the deferred will have |
---|
| 91 | its errback called instead. |
---|
| 92 | |
---|
| 93 | We can now use this deferred to add callbacks for our one-time tasks. The first |
---|
| 94 | callback we add is ``getVersion``, while using a lambda construct to ignore the |
---|
| 95 | result of the callback. We pass the object that represents the XML stream, as |
---|
| 96 | stored in the factory's stream manager. This is needed for tracking the |
---|
| 97 | response to the version query. The second parameter is the JID that we want to |
---|
| 98 | send the version request to, in this case, the server that holds the account we |
---|
| 99 | login with. |
---|
| 100 | |
---|
| 101 | The second callback uses the result from the version request, a dictionary with |
---|
| 102 | the keys ``name`` and ``version`` to hold the software name and version strings |
---|
| 103 | as reported by our server. Having been passed this dictionary, ``printVersion`` |
---|
| 104 | prints the information to the terminal. The third callback neatly closes the |
---|
| 105 | stream. In case of any error, the added errback handler just logs it and |
---|
| 106 | finally we add a callback that is always called, shutting down the application |
---|
| 107 | after one second. |
---|