Use a list comprehension instead of filter() to work with Python3.
[slixmpp.git] / docs / xmpp_tdg.rst
blob3d12b1b65771119c4fe1d6329236929fb5abff8d
1 Following *XMPP: The Definitive Guide*
2 ======================================
4 SleekXMPP was featured in the first edition of the O'Reilly book 
5 `XMPP: The Definitive Guide <http://oreilly.com/catalog/9780596521271/>`_
6 by Peter Saint-Andre, Kevin Smith, and Remko Tronçon. The original source code
7 for the book's examples can be found at http://github.com/remko/xmpp-tdg. An
8 updated version of the source code, maintained to stay current with the latest
9 SleekXMPP release, is available at http://github.com/legastero/xmpp-tdg.
11 However, since publication, SleekXMPP has advanced from version 0.2.1 to version
12 1.0 and there have been several major API changes. The most notable is the
13 introduction of :term:`stanza objects <stanza object>` which have simplified and
14 standardized interactions with the XMPP XML stream.
16 What follows is a walk-through of *The Definitive Guide* highlighting the
17 changes needed to make the code examples work with version 1.0 of SleekXMPP.
18 These changes have been kept to a minimum to preserve the correlation with
19 the book's explanations, so be aware that some code may not use current best
20 practices.
22 Example 2-2. (Page 26)
23 ----------------------
25 **Implementation of a basic bot that echoes all incoming messages back to its sender.**
27 The echo bot example requires a change to the ``handleIncomingMessage`` method
28 to reflect the use of the ``Message`` :term:`stanza object`. The
29 ``"jid"`` field of the message object should now be ``"from"`` to match the
30 ``from`` attribute of the actual XML message stanza. Likewise, ``"message"``
31 changes to ``"body"`` to match the ``body`` element of the message stanza.
33 Updated Code
34 ~~~~~~~~~~~~
36 .. code-block:: python
38     def handleIncomingMessage(self, message):
39         self.xmpp.sendMessage(message["from"], message["body"])
41 `View full source <http://github.com/legastero/xmpp-tdg/blob/master/code/EchoBot/EchoBot.py>`_ |
42 `View original code <http://github.com/remko/xmpp-tdg/blob/master/code/EchoBot/EchoBot.py>`_
44 Example 14-1. (Page 215)
45 ------------------------
47 **CheshiR IM bot implementation.**
49 The main event handling method in the Bot class is meant to process both message
50 events and presence update events. With the new changes in SleekXMPP 1.0,
51 extracting a CheshiR status "message" from both types of stanzas
52 requires accessing different attributes. In the case of a message stanza, the
53 ``"body"`` attribute would contain the CheshiR message. For a presence event,
54 the information is stored in the ``"status"`` attribute. To handle both cases,
55 we can test the type of the given event object and look up the proper attribute
56 based on the type.
58 Like in the EchoBot example, the expression ``event["jid"]`` needs to change
59 to ``event["from"]`` in order to get a JID object for the stanza's sender.
60 Because other functions in CheshiR assume that the JID is a string, the ``jid``
61 attribute is used to access the string version of the JID. A check is also added
62 in case ``user`` is ``None``, but the check could (and probably should) be
63 placed in ``addMessageFromUser``.
65 Another change is needed in ``handleMessageAddedToBackend`` where
66 an HTML-IM response is created. The HTML content should be enclosed in a single
67 element, such as a ``<p>`` tag.
69 Updated Code
70 ~~~~~~~~~~~~
72 .. code-block:: python
74   def handleIncomingXMPPEvent(self, event):
75     msgLocations = {sleekxmpp.stanza.presence.Presence: "status",
76                     sleekxmpp.stanza.message.Message: "body"}
78     message = event[msgLocations[type(event)]]
79     user = self.backend.getUserFromJID(event["from"].jid)
80     if user is not None:
81       self.backend.addMessageFromUser(message, user)
82   
83   def handleMessageAddedToBackend(self, message) :
84     body = message.user + ": " + message.text
85     htmlBody = "<p><a href='%(uri)s'>%(user)s</a>: %(message)s</p>" % {
86       "uri": self.url + "/" + message.user,
87       "user" : message.user, "message" : message.text }
88     for subscriberJID in self.backend.getSubscriberJIDs(message.user) :
89       self.xmpp.sendMessage(subscriberJID, body, mhtml=htmlBody)
91 `View full source <http://github.com/legastero/xmpp-tdg/blob/master/code/CheshiR/Bot.py>`_ |
92 `View original code <http://github.com/remko/xmpp-tdg/blob/master/code/CheshiR/Bot.py>`_
95 Example 14-3. (Page 217)
96 ------------------------
97 **Configurable CheshiR IM bot implementation.**
99 .. note::
100     Since the CheshiR examples build on each other, see previous sections for
101     corrections to code that is not marked as new in the book example.
103 The main difference for the configurable IM bot is the handling for the
104 data form in ``handleConfigurationCommand``. The test for equality
105 with the string ``"1"`` is no longer required; SleekXMPP converts
106 boolean data form fields to the values ``True`` and ``False``
107 automatically.
109 For the method ``handleIncomingXMPPPresence``, the attribute
110 ``"jid"`` is again converted to ``"from"`` to get a JID
111 object for the presence stanza's sender, and the ``jid`` attribute is
112 used to access the string version of that JID object. A check is also added in
113 case ``user`` is ``None``, but the check could (and probably
114 should) be placed in ``getShouldMonitorPresenceFromUser``.
116 Updated Code
117 ~~~~~~~~~~~~
119 .. code-block:: python
121   def handleConfigurationCommand(self, form, sessionId):
122     values = form.getValues()
123     monitorPresence =values["monitorPresence"]
124     jid = self.xmpp.plugin["xep_0050"].sessions[sessionId]["jid"]
125     user = self.backend.getUserFromJID(jid)
126     self.backend.setShouldMonitorPresenceFromUser(user, monitorPresence)
128   def handleIncomingXMPPPresence(self, event):
129     user = self.backend.getUserFromJID(event["from"].jid)
130     if user is not None:
131       if self.backend.getShouldMonitorPresenceFromUser(user):
132         self.handleIncomingXMPPEvent(event)
134 `View full source <http://github.com/legastero/xmpp-tdg/blob/master/code/CheshiR/ConfigurableBot.py>`_ |
135 `View original code <http://github.com/remko/xmpp-tdg/blob/master/code/CheshiR/ConfigurableBot.py>`_
138 Example 14-4. (Page 220)
139 ------------------------
140 **CheshiR IM server component implementation.**
142 .. note::
143     Since the CheshiR examples build on each other, see previous sections for
144     corrections to code that is not marked as new in the book example.
146 Like several previous examples, a needed change is to replace
147 ``subscription["from"]`` with ``subscription["from"].jid`` because the
148 ``BaseXMPP`` method ``makePresence`` requires the JID to be a string.
150 A correction needs to be made in ``handleXMPPPresenceProbe`` because a line was
151 left out of the original implementation; the variable ``user`` is undefined. The
152 JID of the user can be extracted from the presence stanza's ``from`` attribute.
154 Since this implementation of CheshiR uses an XMPP component, it must
155 include a ``from`` attribute in all messages that it sends. Adding the
156 ``from`` attribute is done by including ``mfrom=self.xmpp.jid`` in calls to
157 ``self.xmpp.sendMessage``.
159 Updated Code
160 ~~~~~~~~~~~~
162 .. code-block:: python
164   def handleXMPPPresenceProbe(self, event) :
165     self.xmpp.sendPresence(pto = event["from"])
167   def handleXMPPPresenceSubscription(self, subscription) :
168     if subscription["type"] == "subscribe" :
169       userJID = subscription["from"].jid
170       self.xmpp.sendPresenceSubscription(pto=userJID, ptype="subscribed")
171       self.xmpp.sendPresence(pto = userJID)
172       self.xmpp.sendPresenceSubscription(pto=userJID, ptype="subscribe")
174   def handleMessageAddedToBackend(self, message) :
175     body = message.user + ": " + message.text
176     for subscriberJID in self.backend.getSubscriberJIDs(message.user) :
177       self.xmpp.sendMessage(subscriberJID, body, mfrom=self.xmpp.jid)
179 `View full source <http://github.com/legastero/xmpp-tdg/blob/master/code/CheshiR/SimpleComponent.py>`_ |
180 `View original code <http://github.com/remko/xmpp-tdg/blob/master/code/CheshiR/SimpleComponent.py>`_
183 Example 14-6. (Page 223)
184 ------------------------
185 **CheshiR IM server component with in-band registration support.**
187 .. note::
188     Since the CheshiR examples build on each other, see previous sections for
189     corrections to code that is not marked as new in the book example.
191 After applying the changes from Example 14-4 above, the registrable component
192 implementation should work correctly.
194 .. tip::
195     To see how to implement in-band registration as a SleekXMPP plugin,
196     see the tutorial :ref:`tutorial-create-plugin`.
198 `View full source <http://github.com/legastero/xmpp-tdg/blob/master/code/CheshiR/RegistrableComponent.py>`_ |
199 `View original code <http://github.com/remko/xmpp-tdg/blob/master/code/CheshiR/RegistrableComponent.py>`_
201 Example 14-7. (Page 225)
202 ------------------------
203 **Extended CheshiR IM server component implementation.**
205 .. note::
206     Since the CheshiR examples build on each other, see previous 
207     sections for corrections to code that is not marked as new in the book
208     example.
210 While the final code example can look daunting with all of the changes
211 made, it requires very few modifications to work with the latest version of
212 SleekXMPP. Most differences are the result of CheshiR's backend functions
213 expecting JIDs to be strings so that they can be stripped to bare JIDs. To
214 resolve these, use the ``jid`` attribute of the JID objects. Also,
215 references to ``"message"`` and ``"jid"`` attributes need to
216 be changed to either ``"body"`` or ``"status"``, and either
217 ``"from"`` or ``"to"`` depending on if the object is a message
218 or presence stanza and which of the JIDs from the stanza is needed.
220 Updated Code
221 ~~~~~~~~~~~~
223 .. code-block:: python
225   def handleIncomingXMPPMessage(self, event) :
226     message = self.addRecipientToMessage(event["body"], event["to"].jid)
227     user = self.backend.getUserFromJID(event["from"].jid)
228     self.backend.addMessageFromUser(message, user)
230   def handleIncomingXMPPPresence(self, event) :
231     if event["to"].jid == self.componentDomain :
232       user = self.backend.getUserFromJID(event["from"].jid)
233       self.backend.addMessageFromUser(event["status"], user)
235   ...
237   def handleXMPPPresenceSubscription(self, subscription) :
238     if subscription["type"] == "subscribe" :
239       userJID = subscription["from"].jid
240       user = self.backend.getUserFromJID(userJID)
241       contactJID = subscription["to"]
242       self.xmpp.sendPresenceSubscription(
243           pfrom=contactJID, pto=userJID, ptype="subscribed", pnick=user)
244       self.sendPresenceOfContactToUser(contactJID=contactJID, userJID=userJID)
245       if contactJID == self.componentDomain :
246         self.sendAllContactSubscriptionRequestsToUser(userJID)
248 `View full source <http://github.com/legastero/xmpp-tdg/blob/master/code/CheshiR/Component.py>`_ |
249 `View original code <http://github.com/remko/xmpp-tdg/blob/master/code/CheshiR/Component.py>`_