-
Notifications
You must be signed in to change notification settings - Fork 42
Tutorial: message sending client
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