Extensions for XEPs

Service Discovery (0030)

Events

query_items the server returned a list of items
query_info the server returned a list of identities and features

API

class xmpp.extensions.xep0030.ServiceDiscovery(stream)[source]

extension for discovering information about other XMPP entities. Two kinds of information can be discovered: (1) the identity and capabilities of an entity, including the protocols and features it supports; and (2) the items associated with an entity, such as the list of rooms hosted at a multi-user chat service.

Example

from xmpp import XMLStream
from xmpp import XMPPConnection
from xmpp import JID
from xmpp.auth import SASLAuthenticationHandler

DEBUG = True

DOMAIN = 'falcao.it'
jid = JID('presence1@falcao.it/xmpp-test')
password = 'presence1'
SASL_MECHANISM = 'SCRAM-SHA-1'


connection = XMPPConnection(DOMAIN, 5222, debug=DEBUG)

# create a XML stream
stream = XMLStream(connection, debug=DEBUG)

# prepare the SASL mechanism
sasl = SASLAuthenticationHandler(SASL_MECHANISM, jid, password)
sasl.bind(stream)

@stream.on.closed
def stream_closed(event, node):
    connection.disconnect()
    connection.connect()
    stream.reset()

@stream.on.presence
def handle_presence(event, presence):
    logging.debug("presence from: %s %s(%s)", presence.attr['from'], presence.status.strip(), presence.show.strip())

@connection.on.tcp_established
def step1_open_stream(event, host_ip):
    "sends a <stream:stream> to the XMPP server"
    logging.info("connected to %s", host_ip)
    stream.open_client(jid.domain)

@stream.on.sasl_support
def step2_send_sasl_auth(event, node):
    "sends a <auth /> to the XMPP server"
    sasl.authenticate()

@sasl.on.success
def step3_handle_success(event, result):
    "the SASL authentication succeeded, it's our time to reopen the stream"
    stream.open_client(jid.domain)

@stream.on.bind_support
def step4_bind_to_a_resource_name(event, node):
    "the server said it supports binding"
    stream.bind_to_resource(jid.resource)

@stream.on.bound_jid
def step5_send_presence(event, jid):
    stream.send_presence()
    logging.info("echobot jid: %s", jid.text)

@stream.on.presence
def step6_ensure_connectivity(event, presence):
    if presence.delay:
        stream.send_presence()

@connection.on.ready_to_write
def keep_alive(event, connection):
    if stream.has_gone_through_sasl() and (time.time() % 60 == 0):
        print 'keepalive'
        connection.send_whitespace_keepalive()

@stream.on.message
def auto_reply(event, message):
    stream.send_presence()

    from_jid = JID(message.attr['from'])
    if message.is_composing():
        logging.warning("%s is composing", from_jid.nick)

    if message.is_active():
        logging.warning("%s is active", from_jid.nick)

    body = message.get_body()
    if body:
        logging.critical("%s says: %s", from_jid.nick, body)
        stream.send_message(body, to=from_jid.text)
        stream.send_presence(to=from_jid.text)

connection.connect()

try:
    while connection.is_alive():
        connection.loop_once()

except KeyboardInterrupt as e:
    print "\r{0}".format(traceback.format_exc(e))

    raise SystemExit(1)

Component (0114)

Events

success the server sent a <handshake />
error the server returned a <stream:error>

API

class xmpp.extensions.xep0114.Component(stream)[source]

Provides an external component API while keeping minimal state based on a single boolean flag.

authenticate(secret)[source]

sends a <handshake> to the server with the encoded version of the given secret :param secret: the secret string to authenticate the component

create_node(to, tls=False)[source]

creates a ComponentStream with an optional <starttls /> in it.

is_authenticated()[source]
Returns:True if a success handshake was received by the bound

XMLStream

open(domain, tls=False)[source]

sends an <stream:stream xmlns="jabber:component:accept">

Create your own

You can easily have your own implementation of a XEP by extending the class xmpp.extensions.Extension.

As long as your implementation is being imported by your application, the XMPP toolkit will automatically recognize your subclass and make it available whenever a XMPPStream is instantiated.

XEP 9999

Let’s come up with our own XEP

1. Introduction

This document defines a protocol for communicating dummy from one user to another. Such information MUST be appended to a received_dummy_list in the receiving entity. The entity MAY also send a dummy which SHALL be appended to a sent_dummy_list in the sending entity.

2. Protocol

Sending a dummy

<iq id="23713d" type="set" from="tybalt@shakespeare.org" to="rosaline@shakespeare.org">
  <dummy xmlns="xmpp:xep:example">Romeo</dummy>
</iq>

Receiving a dummy

<iq id="23713d" type="result" from="tybalt@shakespeare.org" to="rosaline@shakespeare.org">
  <dummy xmlns="xmpp:xep:example">Juliet</dummy>
</iq>

Here is the implementation, notice its statelessness

from speakers import Speaker as Events
from xmpp.models import Node, IQ, JID
from xmpp.extensions import Extension


class Dummy(Node):
    __tag__ = 'dummy'
    __etag__ = '{xmpp:xep:example}dummy'
    __namespaces__ = [
        ('', 'xmpp:xep:example')
    ]
    __children_of__ = IQ


class Fake(Extension):
    __xep__ = '9999'

    def initialize(self):
        self.on = Events('fake', [
            'dummy',  # the server sent a dummy inside of an IQ
        ])
        self.stream.on.node(self.route_nodes)

    def route_nodes(self, _, node):
        if isinstance(node, Dummy):
            self.on.dummy.shout(node)

    def send_dummy(self, to, value):
        params = {
            'to': to,
            'type': 'set',
        }
        node = IQ.with_child_and_attributes(
            Dummy.create(value),
            **params
        )
        self.stream.send(node)

Usage of your newly created extension

from xmpp import XMLStream
from xmpp import XMPPConnection
from xmpp import JID
from xmpp.auth import SASLAuthenticationHandler

DEBUG = True

DOMAIN = 'shakespeare.oreg'
jid = JID('tybalt@shakespeare.oef/cahoots')
password = 'sk3tchy'

SASL_MECHANISM = 'SCRAM-SHA-1'


RECEIVED_DUMMY_LIST = []
SENT_DUMMY_LIST = []

connection = XMPPConnection(DOMAIN, 5222, debug=DEBUG)
stream = XMLStream(connection, debug=DEBUG)

sasl = SASLAuthenticationHandler(SASL_MECHANISM, jid, password)
sasl.bind(stream)

@connection.on.tcp_established
def step1_open_stream(event, host_ip):
    stream.open_client(jid.domain)

@stream.on.sasl_support
def step2_send_sasl_auth(event, node):
    sasl.authenticate()

@sasl.on.success
def step3_handle_success(event, result):
    stream.open_client(jid.domain)

@stream.on.bind_support
def step4_bind_to_a_resource_name(event, node):
    stream.bind_to_resource(jid.resource)

@stream.on.bound_jid
def step5_send_presence(event, jid):
    dummies.send_dummy(to='rosaline@shakespeare.org', value='Romeo')
    SENT_DUMMY_LIST.append('Romeo')

@dummies.on.dummy
def step6_store_dummy(event, dummy):
    RECEIVED_DUMMY_LIST.append(dummy.value)


connection.connect()

try:
    while connection.is_alive():
        connection.loop_once()

except KeyboardInterrupt as e:
    print "\r{0}".format(traceback.format_exc(e))

    raise SystemExit(1)