Openfire Logo

Twisted Words: A Minimal Working Example (in Python)

Introduction

This document provides a minimal working example of a client implementation using the Twisted Words library, making it connect to a running Openfire server.

Topics that are covered in this document:

Background

Twisted is an event-based framework for internet applications, supporting Python 3.6+. It includes modules for many different purposes, including the Words module, that adds support for the XMPP protocol.

This guide describes how to use Twisted Words to connect to Openfire. It provides nothing more than a minimal working example, intended as a stepping stone to for client developers that get started with a new project.

Preparations

In this example, a client connection will be made against a running Openfire server. For ease of configuration, the 'demoboot' setup of Openfire is used.

The 'demoboot' setup of Openfire allows one to start a fresh installation of Openfire into a certain provisioned state, without running any of the setup steps. When running in 'demoboot' mode:

To start Openfire in 'demoboot' mode, you can invoke the Openfire executable using the -demoboot argument, as shown below.

Starting Openfire in 'demoboot' mode.
$ ./bin/openfire.sh -demoboot

Code

First, create the Twisted module for Python, as shown below.

Install the Python Twisted module
$ python -m pip install twisted

Create a file named xmpp_client.py and copy the code shown below.

Twisted Words client example
#!/usr/bin/python
# Copyright (c) Twisted Matrix Laboratories.
# See LICENSE for details.

"""
A very simple twisted xmpp-client (Jabber ID)

To run the script:
$ python xmpp_client.py <jid> <secret>
"""


import sys

from twisted.internet.defer import Deferred
from twisted.internet.task import react
from twisted.internet._sslverify import (ClientTLSOptions,
                                         OpenSSLCertificateOptions)
from twisted.names.srvconnect import SRVConnector
from twisted.words.xish import domish
from twisted.words.protocols.jabber import xmlstream, client
from twisted.words.protocols.jabber.jid import JID


class Client:
    def __init__(self, reactor, jid, secret, configurationForTLS):
        self.reactor = reactor
        f = client.XMPPClientFactory(jid, secret,
                                     configurationForTLS=configurationForTLS)
        f.addBootstrap(xmlstream.STREAM_CONNECTED_EVENT, self.connected)
        f.addBootstrap(xmlstream.STREAM_END_EVENT, self.disconnected)
        f.addBootstrap(xmlstream.STREAM_AUTHD_EVENT, self.authenticated)
        f.addBootstrap(xmlstream.INIT_FAILED_EVENT, self.init_failed)
        connector = SRVConnector(reactor, "xmpp-client", jid.host, f, defaultPort=5222)
        connector.connect()
        self.finished = Deferred()

    def rawDataIn(self, buf):
        print("RECV: %r" % buf)

    def rawDataOut(self, buf):
        print("SEND: %r" % buf)

    def connected(self, xs):
        print("Connected.")

        self.xmlstream = xs

        # Log all traffic
        xs.rawDataInFn = self.rawDataIn
        xs.rawDataOutFn = self.rawDataOut

    def disconnected(self, reason):
        print("Disconnected.")
        print(reason)

        self.finished.callback(None)

    def authenticated(self, xs):
        print("Authenticated.")

        presence = domish.Element((None, "presence"))
        xs.send(presence)

        self.reactor.callLater(5, xs.sendFooter)

    def init_failed(self, failure):
        print("Initialization failed.")
        print(failure)

        self.xmlstream.sendFooter()


def main(reactor, jid, secret):
    """
    Connect to the given Jabber ID and return a L{Deferred} which will be
    called back when the connection is over.

    @param reactor: The reactor to use for the connection.
    @param jid: A L{JID} to connect to.
    @param secret: A C{str}
    """
    configurationForTLS = ClientTLSOptions(JID(jid).host,
                                           OpenSSLCertificateOptions().getContext())
    return Client(reactor, JID(jid), secret,
                  configurationForTLS=configurationForTLS).finished


if __name__ == "__main__":
    react(main, sys.argv[1:])

Execute the example client, and provide the JID of 'john' as well as it's password, as shown below:

Run the example
$ python xmpp_client.py john@example.org secret

If all goes well, this will print a short exchange of XMPP data.

Note that this example is based on Twisted's example code, but modified to disable important security features. You should not use this for anything important!

Further Reading

Please use the links below to find more information.