Skip to content

Tutorial: message sending client

kylef edited this page Mar 8, 2012 · 5 revisions

This little tutorial will show how to create a very simple client.

PyXMPP2 provides a XMPP client implementation via the pyxmpp2.client.Client

The constructor requires two arguments, a local user XMPP-id (JID), and a list of handlers. Let's ignore the handlers for now.

Let's define the JID:

from pyxmpp2.jid import JID

my_jid = JID("[email protected]")

…and create a Client object:

from pyxmpp2.client import Client

client = Client(JID("[email protected]"), [])

OK. We have the client. Let's connect:

client.connect()

Has anything happened? Doesn't seem so… PyXMPP API is asynchronous. A new stream and connection (transport) objects have been created and asked to connect, but the actual connection will be initiated from the main event loop. We don't have to create it ourselves, the Client object provides one by default. Let's run it:

client.run()

Oops… an exception:

 Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "pyxmpp2/client.py", line 150, in run
  self.mainloop.loop(timeout)
[...]
File "pyxmpp2/settings.py", line 135, in get
  raise KeyError(key)
KeyError: 'password'

Yes, looks like some password is missing. (The error will hopefully be more elegant in future PyXMPP2 versions). How do we pass a password? Maybe the settings argument to the Client constructor?

Yes. Many parameters for PyXMPP components are passed via a settings object, which is an instance of the pyxmpp2.settings.XMPPSettings class. Let's create some settings and use it with our client:

from pyxmpp2.settings import XMPPSettings

settings = XMPPSettings({ "password": "some-very-secret-password" })
client = Client(my_jid, [], settings)
client.connect()
client.run()

Wow. It didn't crash this time (if it crashes then you may need to check your JID/password or provide some more settings like "starttls": True, "tls_verify_peer": False required for GMail accounts). But it doesn't to do anything either. Just hangs. We didn't ask for anything more. Stop the run() method with Ctrl-C.

We can see that something is indeed happening by enabling logging via the standard Python logging module:

import logging

logging.basicConfig(level = logging.DEBUG)
client.connect()
client.run()

As we can see while the run() command is running a connection has been established, but then it just idly loops. It won't stop until we ask it, but how can we do it while the loop is running? That is one of the purposes of the handlers argument to the Client constructor. Methods of the handlers will be called while the loop is running and can affect the Client behavior.

Let's create a simple handler, that will just quit after client logs-in. We need to handle the pyxmpp.streamevents.AuthorizedEvent, so we need to create a pyxmpp.mainloop.interfaces.MainLoop subclass with a method that can handle the AuthorizedEvent:

from pyxmpp2.interfaces import EventHandler, event_handler, QUIT
from pyxmpp2.streamevents import AuthorizedEvent

class MyHandler(EventHandler):
    @event_handler(AuthorizedEvent)
    def handle_authorized(self, event):
        return QUIT

client = Client(my_jid, [MyHandler()], settings)
client.connect()
client.run()

Ok, the run() method exits now, but the excessive debug messages make it hard to see what is really happening. Maybe we could just show all the important events? PyXMPP2 doesn't do any non-DEBUG logging but itself, but the events carry some meaningful messages we could print. Let's add a catch-all method to our event handler and let it log the events received,

class MyHandler(EventHandler):
    @event_handler(AuthorizedEvent)
    def handle_authorized(self, event):
        return QUIT
    @event_handler()
    def handle_any_event(self, event):
        logging.info(u"-- {0}".format(event))

logging.getLogger().setLevel(logging.INFO)
client = Client(my_jid, [MyHandler()], settings)
client.connect()
client.run()

We should get some nice log now:

INFO:root:-- Resolving SRV record of 'xmpp-client' for 'example.org'...
INFO:root:-- Resolving address of 'jabber.example.org.'...
INFO:root:-- Connecting to 192.168.0.1:5222...
INFO:root:-- Connected to 192.168.0.1:5222
INFO:root:-- Connected to example.org
INFO:root:-- Got stream features
INFO:root:-- Authenticated: [email protected]
INFO:root:-- Connected to example.org
INFO:root:-- Got stream features
INFO:root:-- Requesting server-generated resource
INFO:root:-- Authorized: [email protected]/21090062261309179238612431

Please note there is no disconnection here. We left our connection open (until the Python GC takes care of the client object). That is because we quit the main loop as soon as the client is authorized to use the stream. A better solution would be to disconnect the client when we don't need it any more and quit the loop after it is disconnected.

Ok, time to do some real stuff. Like sending a message. Instead of terminating the main loop just after logging-in, lets send the traditional 'Hello world!' message to someone and disconnect cleanly:

from pyxmpp2.streamevents import DisconnectedEvent
from pyxmpp2.message import Message 

class MyHandler(EventHandler):
    @event_handler(AuthorizedEvent)
    def handle_authorized(self, event):
        message = Message(to_jid = "[email protected]", body = "Hello World!")
        event.stream.send(message)
        event.stream.disconnect()
    @event_handler()
    def handle_any_event(self, event):
        logging.info(u"-- {0}".format(event))
    @event_handler(DisconnectedEvent)
    def handle_disconnected(self, event):
        return QUIT

client = Client(my_jid, [MyHandler()], settings)
client.connect()
client.run()

Seems working… But, did it really send the message? We can trace the data exchanged with the server by tweaking the logging settings again:

logging.getLogger("pyxmpp.tcp.out").setLevel(logging.DEBUG)
logging.getLogger("pyxmpp.tcp.in").setLevel(logging.DEBUG)

client = Client(my_jid, [MyHandler()], settings)
client.connect()
client.run()

A working example showing what was described above is included in the PyXMPP2 sources: examples/send_message_client.py

Clone this wiki locally