diff options
-rwxr-xr-x | examples/custom_stanzas/custom_stanza_provider.py | 40 | ||||
-rwxr-xr-x | examples/custom_stanzas/custom_stanza_user.py | 32 | ||||
-rw-r--r-- | examples/custom_stanzas/stanza.py | 48 |
3 files changed, 99 insertions, 21 deletions
diff --git a/examples/custom_stanzas/custom_stanza_provider.py b/examples/custom_stanzas/custom_stanza_provider.py index 16fc6c30..40a77204 100755 --- a/examples/custom_stanzas/custom_stanza_provider.py +++ b/examples/custom_stanzas/custom_stanza_provider.py @@ -37,8 +37,8 @@ else: class ActionBot(sleekxmpp.ClientXMPP): """ - A simple SleekXMPP bot that provides a basic - adhoc command. + A simple SleekXMPP bot that receives a custom stanza + from another client. """ def __init__(self, jid, password): @@ -54,7 +54,12 @@ class ActionBot(sleekxmpp.ClientXMPP): self.registerHandler( Callback('Some custom iq', StanzaPath('iq@type=set/action'), - self._handleAction)) + self._handle_action)) + + self.add_event_handler('custom_action', + self._handle_action_event, + threaded=True) + register_stanza_plugin(Iq, Action) def start(self, event): @@ -73,17 +78,34 @@ class ActionBot(sleekxmpp.ClientXMPP): self.send_presence() self.get_roster() - def _handleAction(self, iq): - if iq['action']['method'] == 'is_prime' and iq['action']['param'] == '2': - print("got message: " + str(iq)) + def _handle_action(self, iq): + """ + Raise an event for the stanza so that it can be processed in its + own thread without blocking the main stanza processing loop. + """ + self.event('custom_action', iq) + + def _handle_action_event(self, iq): + """ + Respond to the custom action event. + + Since one of the actions is to disconnect, this + event handler needs to be run in threaded mode, by + using `threaded=True` in the `add_event_handler` call. + """ + method = iq['action']['method'] + param = iq['action']['param'] + + if method == 'is_prime' and param == '2': + print("got message: %s" % iq) iq.reply() iq['action']['status'] = 'done' iq.send() - elif iq['action']['method'] == 'bye': - print("got message: " + str(iq)) + elif method == 'bye': + print("got message: %s" % iq) self.disconnect() else: - print("got message: " + str(iq)) + print("got message: %s" % iq) iq.reply() iq['action']['status'] = 'error' iq.send() diff --git a/examples/custom_stanzas/custom_stanza_user.py b/examples/custom_stanzas/custom_stanza_user.py index 0741b3a4..eabb45fe 100755 --- a/examples/custom_stanzas/custom_stanza_user.py +++ b/examples/custom_stanzas/custom_stanza_user.py @@ -15,8 +15,10 @@ import getpass from optparse import OptionParser import sleekxmpp - +from sleekxmpp import Iq +from sleekxmpp.exceptions import XMPPError from sleekxmpp.xmlstream import register_stanza_plugin + from stanza import Action # Python versions before 3.0 do not use UTF-8 encoding @@ -33,8 +35,8 @@ else: class ActionUserBot(sleekxmpp.ClientXMPP): """ - A simple SleekXMPP bot that uses the adhoc command - provided by the adhoc_provider.py example. + A simple SleekXMPP bot that sends a custom action stanza + to another client. """ def __init__(self, jid, password, other): @@ -47,10 +49,10 @@ class ActionUserBot(sleekxmpp.ClientXMPP): # and the XML streams are ready for use. We want to # listen for this event so that we we can initialize # our roster. - self.add_event_handler("session_start", self.start) + self.add_event_handler("session_start", self.start, threaded=True) self.add_event_handler("message", self.message) - register_stanza_plugin(sleekxmpp.Iq, Action) + register_stanza_plugin(Iq, Action) def start(self, event): """ @@ -71,12 +73,16 @@ class ActionUserBot(sleekxmpp.ClientXMPP): self.send_custom_iq() def send_custom_iq(self): + """Create and send two custom actions. + + If the first action was successful, then send + a shutdown command and then disconnect. + """ iq = self.Iq() iq['to'] = self.action_provider iq['type'] = 'set' - iq.enable('action') iq['action']['method'] = 'is_prime' - iq['action']['param'] = str(2) + iq['action']['param'] = '2' try: resp = iq.send() @@ -85,12 +91,14 @@ class ActionUserBot(sleekxmpp.ClientXMPP): iq2 = self.Iq() iq2['to'] = self.action_provider iq2['type'] = 'set' - iq2.enable('action') iq2['action']['method'] = 'bye' - iq2.send(block = False) - self.disconnect() - except sleekxmpp.exceptions.XMPPError: - pass + iq2.send(block=False) + + # The wait=True delays the disconnect until the queue + # of stanzas to be sent becomes empty. + self.disconnect(wait=True) + except XMPPError: + print('There was an error sending the custom action.') def message(self, msg): """ diff --git a/examples/custom_stanzas/stanza.py b/examples/custom_stanzas/stanza.py index ca85a0f0..50d0f9f2 100644 --- a/examples/custom_stanzas/stanza.py +++ b/examples/custom_stanzas/stanza.py @@ -1,8 +1,56 @@ from sleekxmpp.xmlstream import ElementBase class Action(ElementBase): + + """ + A stanza class for XML content of the form: + + <action xmlns="sleekxmpp:custom:actions"> + <method>X</method> + <param>X</param> + <status>X</status> + </action> + """ + + #: The `name` field refers to the basic XML tag name of the + #: stanza. Here, the tag name will be 'action'. name = 'action' + + #: The namespace of the main XML tag. namespace = 'sleekxmpp:custom:actions' + + #: The `plugin_attrib` value is the name that can be used + #: with a parent stanza to access this stanza. For example + #: from an Iq stanza object, accessing: + #: + #: iq['action'] + #: + #: would reference an Action object, and will even create + #: an Action object and append it to the Iq stanza if + #: one doesn't already exist. plugin_attrib = 'action' + + #: Stanza objects expose dictionary-like interfaces for + #: accessing and manipulating substanzas and other values. + #: The set of interfaces defined here are the names of + #: these dictionary-like interfaces provided by this stanza + #: type. For example, an Action stanza object can use: + #: + #: action['method'] = 'foo' + #: print(action['param']) + #: del action['status'] + #: + #: to set, get, or remove its values. interfaces = set(('method', 'param', 'status')) + + #: By default, values in the `interfaces` set are mapped to + #: attribute values. This can be changed such that an interface + #: maps to a subelement's text value by adding interfaces to + #: the sub_interfaces set. For example, here all interfaces + #: are marked as sub_interfaces, and so the XML produced will + #: look like: + #: + #: <action xmlns="sleekxmpp:custom:actions"> + #: <method>foo</method> + #: </action> sub_interfaces = interfaces |