diff options
Diffstat (limited to 'docs/howto/xmpp_tdg.rst')
-rw-r--r-- | docs/howto/xmpp_tdg.rst | 249 |
1 files changed, 249 insertions, 0 deletions
diff --git a/docs/howto/xmpp_tdg.rst b/docs/howto/xmpp_tdg.rst new file mode 100644 index 00000000..53194e13 --- /dev/null +++ b/docs/howto/xmpp_tdg.rst @@ -0,0 +1,249 @@ +Following *XMPP: The Definitive Guide* +====================================== + +Slixmpp was featured in the first edition of the O'Reilly book +`XMPP: The Definitive Guide <http://oreilly.com/catalog/9780596521271/>`_ +by Peter Saint-Andre, Kevin Smith, and Remko Tronçon. The original source code +for the book's examples can be found at http://github.com/remko/xmpp-tdg. An +updated version of the source code, maintained to stay current with the latest +Slixmpp release, is available at http://github.com/legastero/xmpp-tdg. + +However, since publication, Slixmpp has advanced from version 0.2.1 to version +1.0 and there have been several major API changes. The most notable is the +introduction of :term:`stanza objects <stanza object>` which have simplified and +standardized interactions with the XMPP XML stream. + +What follows is a walk-through of *The Definitive Guide* highlighting the +changes needed to make the code examples work with version 1.0 of Slixmpp. +These changes have been kept to a minimum to preserve the correlation with +the book's explanations, so be aware that some code may not use current best +practices. + +Example 2-2. (Page 26) +---------------------- + +**Implementation of a basic bot that echoes all incoming messages back to its sender.** + +The echo bot example requires a change to the ``handleIncomingMessage`` method +to reflect the use of the ``Message`` :term:`stanza object`. The +``"jid"`` field of the message object should now be ``"from"`` to match the +``from`` attribute of the actual XML message stanza. Likewise, ``"message"`` +changes to ``"body"`` to match the ``body`` element of the message stanza. + +Updated Code +~~~~~~~~~~~~ + +.. code-block:: python + + def handleIncomingMessage(self, message): + self.xmpp.send_message(message["from"], message["body"]) + +`View full source (1) <http://github.com/legastero/xmpp-tdg/blob/master/code/EchoBot/EchoBot.py>`_ | +`View original code (1) <http://github.com/remko/xmpp-tdg/blob/master/code/EchoBot/EchoBot.py>`_ + +Example 14-1. (Page 215) +------------------------ + +**CheshiR IM bot implementation.** + +The main event handling method in the Bot class is meant to process both message +events and presence update events. With the new changes in Slixmpp 1.0, +extracting a CheshiR status "message" from both types of stanzas +requires accessing different attributes. In the case of a message stanza, the +``"body"`` attribute would contain the CheshiR message. For a presence event, +the information is stored in the ``"status"`` attribute. To handle both cases, +we can test the type of the given event object and look up the proper attribute +based on the type. + +Like in the EchoBot example, the expression ``event["jid"]`` needs to change +to ``event["from"]`` in order to get a JID object for the stanza's sender. +Because other functions in CheshiR assume that the JID is a string, the ``jid`` +attribute is used to access the string version of the JID. A check is also added +in case ``user`` is ``None``, but the check could (and probably should) be +placed in ``addMessageFromUser``. + +Another change is needed in ``handleMessageAddedToBackend`` where +an HTML-IM response is created. The HTML content should be enclosed in a single +element, such as a ``<p>`` tag. + +Updated Code +~~~~~~~~~~~~ + +.. code-block:: python + + def handleIncomingXMPPEvent(self, event): + msgLocations = {slixmpp.stanza.presence.Presence: "status", + slixmpp.stanza.message.Message: "body"} + + message = event[msgLocations[type(event)]] + user = self.backend.getUserFromJID(event["from"].jid) + if user is not None: + self.backend.addMessageFromUser(message, user) + + def handleMessageAddedToBackend(self, message) : + body = message.user + ": " + message.text + htmlBody = "<p><a href='%(uri)s'>%(user)s</a>: %(message)s</p>" % { + "uri": self.url + "/" + message.user, + "user" : message.user, "message" : message.text } + for subscriberJID in self.backend.getSubscriberJIDs(message.user) : + self.xmpp.send_message(subscriberJID, body, mhtml=htmlBody) + +`View full source (2) <http://github.com/legastero/xmpp-tdg/blob/master/code/CheshiR/Bot.py>`_ | +`View original code (2) <http://github.com/remko/xmpp-tdg/blob/master/code/CheshiR/Bot.py>`_ + + +Example 14-3. (Page 217) +------------------------ +**Configurable CheshiR IM bot implementation.** + +.. note:: + Since the CheshiR examples build on each other, see previous sections for + corrections to code that is not marked as new in the book example. + +The main difference for the configurable IM bot is the handling for the +data form in ``handleConfigurationCommand``. The test for equality +with the string ``"1"`` is no longer required; Slixmpp converts +boolean data form fields to the values ``True`` and ``False`` +automatically. + +For the method ``handleIncomingXMPPPresence``, the attribute +``"jid"`` is again converted to ``"from"`` to get a JID +object for the presence stanza's sender, and the ``jid`` attribute is +used to access the string version of that JID object. A check is also added in +case ``user`` is ``None``, but the check could (and probably +should) be placed in ``getShouldMonitorPresenceFromUser``. + +Updated Code +~~~~~~~~~~~~ + +.. code-block:: python + + def handleConfigurationCommand(self, form, sessionId): + values = form.getValues() + monitorPresence =values["monitorPresence"] + jid = self.xmpp.plugin["xep_0050"].sessions[sessionId]["jid"] + user = self.backend.getUserFromJID(jid) + self.backend.setShouldMonitorPresenceFromUser(user, monitorPresence) + + def handleIncomingXMPPPresence(self, event): + user = self.backend.getUserFromJID(event["from"].jid) + if user is not None: + if self.backend.getShouldMonitorPresenceFromUser(user): + self.handleIncomingXMPPEvent(event) + +`View full source (3) <http://github.com/legastero/xmpp-tdg/blob/master/code/CheshiR/ConfigurableBot.py>`_ | +`View original code (3) <http://github.com/remko/xmpp-tdg/blob/master/code/CheshiR/ConfigurableBot.py>`_ + + +Example 14-4. (Page 220) +------------------------ +**CheshiR IM server component implementation.** + +.. note:: + Since the CheshiR examples build on each other, see previous sections for + corrections to code that is not marked as new in the book example. + +Like several previous examples, a needed change is to replace +``subscription["from"]`` with ``subscription["from"].jid`` because the +``BaseXMPP`` method ``make_presence`` requires the JID to be a string. + +A correction needs to be made in ``handleXMPPPresenceProbe`` because a line was +left out of the original implementation; the variable ``user`` is undefined. The +JID of the user can be extracted from the presence stanza's ``from`` attribute. + +Since this implementation of CheshiR uses an XMPP component, it must +include a ``from`` attribute in all messages that it sends. Adding the +``from`` attribute is done by including ``mfrom=self.xmpp.jid`` in calls to +``self.xmpp.send_message``. + +Updated Code +~~~~~~~~~~~~ + +.. code-block:: python + + def handleXMPPPresenceProbe(self, event) : + self.xmpp.send_presence(pto = event["from"]) + + def handleXMPPPresenceSubscription(self, subscription) : + if subscription["type"] == "subscribe" : + userJID = subscription["from"].jid + self.xmpp.send_presence_subscription(pto=userJID, ptype="subscribed") + self.xmpp.send_presence(pto = userJID) + self.xmpp.send_presence_subscription(pto=userJID, ptype="subscribe") + + def handleMessageAddedToBackend(self, message) : + body = message.user + ": " + message.text + for subscriberJID in self.backend.getSubscriberJIDs(message.user) : + self.xmpp.send_message(subscriberJID, body, mfrom=self.xmpp.jid) + +`View full source (4) <http://github.com/legastero/xmpp-tdg/blob/master/code/CheshiR/SimpleComponent.py>`_ | +`View original code (4) <http://github.com/remko/xmpp-tdg/blob/master/code/CheshiR/SimpleComponent.py>`_ + + +Example 14-6. (Page 223) +------------------------ +**CheshiR IM server component with in-band registration support.** + +.. note:: + Since the CheshiR examples build on each other, see previous sections for + corrections to code that is not marked as new in the book example. + +After applying the changes from Example 14-4 above, the registrable component +implementation should work correctly. + +.. tip:: + To see how to implement in-band registration as a Slixmpp plugin, + see the tutorial :ref:`tutorial-create-plugin`. + +`View full source (5) <http://github.com/legastero/xmpp-tdg/blob/master/code/CheshiR/RegistrableComponent.py>`_ | +`View original code (5) <http://github.com/remko/xmpp-tdg/blob/master/code/CheshiR/RegistrableComponent.py>`_ + +Example 14-7. (Page 225) +------------------------ +**Extended CheshiR IM server component implementation.** + +.. note:: + Since the CheshiR examples build on each other, see previous + sections for corrections to code that is not marked as new in the book + example. + +While the final code example can look daunting with all of the changes +made, it requires very few modifications to work with the latest version of +Slixmpp. Most differences are the result of CheshiR's backend functions +expecting JIDs to be strings so that they can be stripped to bare JIDs. To +resolve these, use the ``jid`` attribute of the JID objects. Also, +references to ``"message"`` and ``"jid"`` attributes need to +be changed to either ``"body"`` or ``"status"``, and either +``"from"`` or ``"to"`` depending on if the object is a message +or presence stanza and which of the JIDs from the stanza is needed. + +Updated Code +~~~~~~~~~~~~ + +.. code-block:: python + + def handleIncomingXMPPMessage(self, event) : + message = self.addRecipientToMessage(event["body"], event["to"].jid) + user = self.backend.getUserFromJID(event["from"].jid) + self.backend.addMessageFromUser(message, user) + + def handleIncomingXMPPPresence(self, event) : + if event["to"].jid == self.componentDomain : + user = self.backend.getUserFromJID(event["from"].jid) + self.backend.addMessageFromUser(event["status"], user) + + ... + + def handleXMPPPresenceSubscription(self, subscription) : + if subscription["type"] == "subscribe" : + userJID = subscription["from"].jid + user = self.backend.getUserFromJID(userJID) + contactJID = subscription["to"] + self.xmpp.send_presence_subscription( + pfrom=contactJID, pto=userJID, ptype="subscribed", pnick=user) + self.sendPresenceOfContactToUser(contactJID=contactJID, userJID=userJID) + if contactJID == self.componentDomain : + self.sendAllContactSubscriptionRequestsToUser(userJID) + +`View full source (6) <http://github.com/legastero/xmpp-tdg/blob/master/code/CheshiR/Component.py>`_ | +`View original code (6) <http://github.com/remko/xmpp-tdg/blob/master/code/CheshiR/Component.py>`_ |