Stanzas: Presence

rhcarvalho edited this page Jan 9, 2012 · 2 revisions

Stanzas: Presence

Creating

Creating and sending presence stanzas can be done in four ways: * xmpp.Presence() * xmpp.makePresence() * xmpp.sendPresence() * xmpp.sendPresenceSubscription()

The first option is simply a plain presence stanza object. Any additional information such as the from attribute or status element will need to be added using the interfaces described below.

The makePresence method accepts keyword parameters that match the stanza object's interface, except that they are prefixed with a p. For example, to create a presence stanza from aclice@wonderland.lit with a status of "Curiouser and curiouser!" and a show type of 'xa' the following would be used:

# Where xmpp is a SleekXMPP object
xmpp.makePresence(pfrom='alice@wonderland.lit', 
                  pstatus='Curiouser and curiouser!', 
                  pshow='xa')

The sendPresence method accepts the same keyword arguments as makePresence and also queues the stanza to be sent to the XMPP server for broadcasting.

Unlike sendPresence, sendPresenceSubscription does not mirror the keyword parameters of makePresence. Instead, only pfrom, pto, ptype, and pnick are accepted. The pnick option allows for adding an optional nick element to the presence in accordance with XEP-0172

Using Presence Stanzas

Subscription Management

Adding or removing a subscription is typically a four-step process:

  1. Send a presence stanza with type 'subscribe' or 'unsubscribe'
  2. Receive a presence stanza of type 'subscribed' or 'unsubscribed'
  3. Receive a presence stanza of type 'subscribe' or 'unsubscribe'
  4. Send a presence stanza of type 'subscribed' or 'unsubscribed'

If your program will be accepting subscriptions instead of initiating them, reverse the send and receive steps above.

Sending and replying to a presence subscription can be done using xmpp.sendPresence or xmpp.sendPresenceSubscription. In both cases, the ptype keyword parameter must be set. If adding a nick element to the subscription request is desired, xmpp.sendPresenceSubscription must be used.

# Where xmpp is a SleekXMPP object
xmpp.sendPresence(pto='user@example.com', ptype='subscribe')
# -or-
xmpp.sendPresenceSubscription(pto='user@example.com', 
                              ptype='subscribe', 
                              pnick='Sleek')

Automatic Subscription Management

SleekXMPP already provides basic subscription management, but the automatic handling is to either accept or reject all subscription requests.

The value xmpp.auto_authorize controls how the agent responds to a 'subscribe' request. If True, then a 'subscribed' response will be sent. If False, then an 'unsubscribed' response will be sent to decline the subscription. Setting xmpp.auto_authorize to None will disable automatic management.

The value xmpp.auto_subscribe is used when xmpp.auto_authorize is set to True. If xmpp.auto_subscribe is True then a 'subscribe' request will be sent after accepting a subscription request.

  • Accept and create bidirectional subscription requests:
xmpp.auto_authorize = True
xmpp.auto_subscribe = True
  • Accept only one-directional subscription requests:
xmpp.auto_authorize = True
xmpp.auto_subscribe = False
  • Decline all subscription requests:
xmpp.auto_authorize = False
  • Use a custom subscription policy:
xmpp.auto_authorize = None

Custom Subscription Management

To create custom subscription management, the events 'presence_subscribe', 'presence_subscribed', 'presence_unsubscribe', and 'presence_unsubscribed' must be listened for by event handlers.

Note: If your application will both send and receive subscription requests, you will need to keep a roster data structure that knows the status of all pending requests (the ClientXMPP implementation has such a roster object, but ComponentXMPP does not). Otherwise, you can create an infinite loop of two bots subscribing to the other's presence. If you only test with a human-facing client, most client programs will detect that situation and stop the cycle without reporting an error to you.

As an example, here is a sample snippet for custom subscription handling. However, note that if you are using a server component, it is a good idea to also set the pfrom parameter when sending presence subscriptions. Doing so allows you to support different subscription states for different JIDs for the component, such as user1@component.example.com and user2@component.example.com.

# Where self is a SleekXMPP object, and self.backend is some arbitrary
# object that you create depending on the needs of your application.

# self.add_event_handler('presence_subscribe', self.subscribe)
# self.add_event_handler('presence_subscribed', self.subscribed)
# The unsubscribe and unsubscribed handlers will be similar.

# If a component is being used, be sure to set the pfrom parameter
# when sending presences if you are using multiple JIDs for the component, 
# such as user1@component.example.com and user2@component.example.com.

def subscribe(self, presence):
    # If the subscription request is rejected.
    if not self.backend.allow_subscription(presence['from']):
        self.sendPresence(pto=presence['from'], 
                          ptype='unsubscribed')
        return
     
    # If the subscription request is accepted.
    self.sendPresence(pto=presence['from'],
                      ptype='subscribed')

    # Save the fact that a subscription has been accepted, somehow. Here
    # we use a backend object that has a roster.
    self.backend.roster.subscribe(presence['from'])

    # If a bi-directional subscription does not exist, create one.
    if not self.backend.roster.sub_from(presence['from']):
        self.sendPresence(pto=presence['from'],
                          ptype='subscribe')

def subscribed(self, presence):
    # Store the new subscription state, somehow. Here we use a backend object.
    self.backend.roster.subscribed(presence['from'])

    # Send a new presence update to the subscriber.
    self.sendPresence(pto=presence['from'])

Presence Probes

Presence probes (presence stanzas with a type of 'probe') are used to discover the status of other XMPP agents. When a presence probe is received, a presence stanza should be sent in reply that contains the agent's current state. Some XMPP servers use presence probes to generate the initial burst of presence stanzas to populate the roster when an agent connects.

Using presence probes can be done in two ways, either as a recipient or as a sender of the probes. Receiving and handling a probe is done by listening for the 'presence_probe' event, as so:

# Where self is a SleekXMPP object:
# def __init__(self, ...):
#     ...
#     self.add_event_handler('presence_probe', self.handle_probe)

def handle_probe(self, presence):
    sender = presence['from']
    # Populate the presence reply with the agent's current status.
    self.sendPresence(pto=sender, pstatus="Busy studying XMPP", pshow="dnd")

It is a good idea for server components to respond to presence probes since the main XMPP server will usually not respond on its behalf.

Sending a presence probe can be done at any time. It can be especially useful for XMPP components that require some form of registration before interacting with other agents. If a client already has the component in its roster, then the component can receive a presence stanza before registration takes place, and then not receive a presence stanza after registration. By sending a presence probe after a client registers, the server component can be sure to get a new presence update once registration is completed. Issuing a presence probe is done like so:

# Where xmpp is a SleekXMPP object
xmpp.sendPresence(pto='recipient@example.com', ptype='probe')

Interface

  • presence['from']

    Standard stanza interface. The JID of the presence stanza's sender.

  • presence['id']

    Standard stanza interface. An 'id' value may be used to track and associate series of stanzas.

  • presence['priority']

    A number greater than or equal to zero. Most XMPP servers will route messages to connections with the highest priority. Setting the priority to '0' is usually recommended for bots that will not interact directly with humans.

  • presence['status']

    Free form, human readable text that describes the agent's status. For example, "Ready for data", or "Executing job". The status text will usually be displayed in a client's roster.

  • presence['to']

    Standard stanza interface. The JID of the presence stanza's recipient.

  • presence['type']

    Be careful, this attribute does not correspond exactly with the XML of a presence stanza since. The values that may be used with 'type' are: 'available', 'away', 'chat', 'dnd', 'error', 'probe', 'subscribe', 'subscribed', 'unavailable', 'unsubscribe', 'unsubscribed', and 'xa'.

    Note: The 'type' key maps to both the type attribute and the show element of a presence stanza.

  • presence.reply()

    A standard stanza interface method. If the presence has a type of 'subscribe' or 'unsubscribe', the reply presence will have a type of 'subscribed' or 'unsubscribed', respectively.

Plugin Extensions

XEP-0045:Multi-User Chat

The XEP-0045 plugin adds support for the <x xmlns="http://jabber.org/protocol/muc#user" /> element as part of presence stanzas. Not every possible subelement of <x xmlns="http://jabber.org/protocol/muc#user" /> is supported, but interfaces for the <item /> element are provided.

  • presence['muc']['affiliation']

    Provide the agent's affiliation with the chatroom. Possible values are: 'owner', 'admin', 'member', 'outcast', or '' for no affiliation. Affiliations are persisted across visits to the chatroom and are useful for controlling who may enter the room.

  • presence['muc']['jid']

    If the chatroom is not anonymous, or the agent has sufficient privileges, then the JID for other members in the chatroom may be found from their presence updates using presence['muc']['jid'].

  • presence['muc']['nick']

    The nickname used by a participant in the chatroom. It is equivalent to presence['from'].resource.

    Note: It is not possible to change the agent's room nick by modifying presence['muc']['nick']. The nick must be set before joining the room.

  • presence['muc']['role']

    The chatroom participant's role in the room. Possible values are: 'visitor', 'participant', 'moderator', or '' for no role. Roles are typically more temporary than affiliations and are used to control what room participants may do to manage the room's configuration and the roles of other participants.

  • presence['muc']['room']

    The name of the chatroom. It is equivalent to presence['from'].bare.

    Note: It is not possible to change the room name by modifying presence['muc']['room'].