summaryrefslogtreecommitdiff
path: root/docs/xmpp_tdg.rst
blob: b14fd9e15f2f78ce47bfb319a10407b23892b024 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
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 <http://github.com/legastero/xmpp-tdg/blob/master/code/EchoBot/EchoBot.py>`_ |
`View original code <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 <http://github.com/legastero/xmpp-tdg/blob/master/code/CheshiR/Bot.py>`_ |
`View original code <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 <http://github.com/legastero/xmpp-tdg/blob/master/code/CheshiR/ConfigurableBot.py>`_ |
`View original code <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 <http://github.com/legastero/xmpp-tdg/blob/master/code/CheshiR/SimpleComponent.py>`_ |
`View original code <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 <http://github.com/legastero/xmpp-tdg/blob/master/code/CheshiR/RegistrableComponent.py>`_ |
`View original code <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 <http://github.com/legastero/xmpp-tdg/blob/master/code/CheshiR/Component.py>`_ |
`View original code <http://github.com/remko/xmpp-tdg/blob/master/code/CheshiR/Component.py>`_