summaryrefslogtreecommitdiff
path: root/docs/getting_started/muc.rst
blob: 0a641b8bfce71db99bba90252259a6286bfdd92c (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
.. _mucbot:

=========================
Mulit-User Chat (MUC) Bot
=========================

.. note::

    If you have any issues working through this quickstart guide
    join the chat room at `slixmpp@muc.poez.io
    <xmpp:slixmpp@muc.poez.io?join>`_.

If you have not yet installed Slixmpp, do so now by either checking out a version
from `Git <http://git.poez.io/slixmpp>`_.

Now that you've got the basic gist of using Slixmpp by following the
echobot example (:ref:`echobot`), we can use one of the bundled plugins
to create a very popular XMPP starter project: a `Multi-User Chat`_
(MUC) bot. Our bot will login to an XMPP server, join an MUC chat room
and "lurk" indefinitely, responding with a generic message to anyone
that mentions its nickname. It will also greet members as they join the
chat room.

.. _`multi-user chat`: http://xmpp.org/extensions/xep-0045.html

Joining The Room
----------------

As usual, our code will be based on the pattern explained in :ref:`echobot`.
To start, we create an ``MUCBot`` class based on
:class:`ClientXMPP <slixmpp.clientxmpp.ClientXMPP>` and which accepts
parameters for the JID of the MUC room to join, and the nick that the
bot will use inside the chat room.  We also register an
:term:`event handler` for the :term:`session_start` event.


.. code-block:: python

    import slixmpp

    class MUCBot(slixmpp.ClientXMPP):

        def __init__(self, jid, password, room, nick):
            slixmpp.ClientXMPP.__init__(self, jid, password)

            self.room = room
            self.nick = nick

            self.add_event_handler("session_start", self.start)

After initialization, we also need to register the MUC (XEP-0045) plugin
so that we can make use of the group chat plugin's methods and events.

.. code-block:: python

    xmpp.register_plugin('xep_0045')

Finally, we can make our bot join the chat room once an XMPP session
has been established:

.. code-block:: python

    def start(self, event):
        self.get_roster()
        self.send_presence()
        self.plugin['xep_0045'].join_muc(self.room,
                                         self.nick,
                                         wait=True)

Note that as in :ref:`echobot`, we need to include send an initial presence and request
the roster. Next, we want to join the group chat, so we call the
``join_muc`` method of the MUC plugin.

.. note::

    The :attr:`plugin <slixmpp.basexmpp.BaseXMPP.plugin>` attribute is
    dictionary that maps to instances of plugins that we have previously
    registered, by their names.


Adding Functionality
--------------------

Currently, our bot just sits dormantly inside the chat room, but we
would like it to respond to two distinct events by issuing a generic
message in each case to the chat room. In particular, when a member
mentions the bot's nickname inside the chat room, and when a member
joins the chat room.

Responding to Mentions
~~~~~~~~~~~~~~~~~~~~~~

Whenever a user mentions our bot's nickname in chat, our bot will
respond with a generic message resembling *"I heard that, user."* We do
this by examining all of the messages sent inside the chat and looking
for the ones which contain the nickname string.

First, we register an event handler for the :term:`groupchat_message`
event inside the bot's ``__init__`` function.

.. note::

    We do not register a handler for the :term:`message` event in this
    bot, but if we did, the group chat message would have been sent to
    both handlers.

.. code-block:: python

    def __init__(self, jid, password, room, nick):
        slixmpp.ClientXMPP.__init__(self, jid, password)

        self.room = room
        self.nick = nick

        self.add_event_handler("session_start", self.start)
        self.add_event_handler("groupchat_message", self.muc_message)

Then, we can send our generic message whenever the bot's nickname gets
mentioned.

.. warning::

    Always check that a message is not from yourself,
    otherwise you will create an infinite loop responding
    to your own messages.

.. code-block:: python

    def muc_message(self, msg):
        if msg['mucnick'] != self.nick and self.nick in msg['body']:
            self.send_message(mto=msg['from'].bare,
                              mbody="I heard that, %s." % msg['mucnick'],
                              mtype='groupchat')


Greeting Members
~~~~~~~~~~~~~~~~

Now we want to greet member whenever they join the group chat. To
do this we will use the dynamic ``muc::room@server::got_online`` [1]_
event so it's a good idea to register an event handler for it.

.. note::

    The groupchat_presence event is triggered whenever a
    presence stanza is received from any chat room, including
    any presences you send yourself. To limit event handling
    to a single room, use the events ``muc::room@server::presence``,
    ``muc::room@server::got_online``, or ``muc::room@server::got_offline``.

.. code-block:: python

    def __init__(self, jid, password, room, nick):
        slixmpp.ClientXMPP.__init__(self, jid, password)

        self.room = room
        self.nick = nick

        self.add_event_handler("session_start", self.start)
        self.add_event_handler("groupchat_message", self.muc_message)
        self.add_event_handler("muc::%s::got_online" % self.room,
                               self.muc_online)

Now all that's left to do is to greet them:

.. code-block:: python

    def muc_online(self, presence):
        if presence['muc']['nick'] != self.nick:
            self.send_message(mto=presence['from'].bare,
                              mbody="Hello, %s %s" % (presence['muc']['role'],
                                                      presence['muc']['nick']),
                              mtype='groupchat')

.. [1] this is similar to the :term:`got_online` event and is sent by
       the xep_0045 plugin whenever a member joins the referenced
       MUC chat room.


Final Product
-------------

.. compound::

    The final step is to create a small runner script for initialising our ``MUCBot`` class and adding some
    basic configuration options. By following the basic boilerplate pattern in :ref:`echobot`, we arrive
    at the code below. To experiment with this example, you can use:

    .. code-block:: sh

            python muc.py -d -j jid@example.com -r room@muc.example.net -n lurkbot

    which will prompt for the password, log in, and join the group chat. To test, open
    your regular IM client and join the same group chat that you sent the bot to. You
    will see ``lurkbot`` as one of the members in the group chat, and that it greeted
    you upon entry. Send a message with the string "lurkbot" inside the body text, and you
    will also see that it responds with our pre-programmed customized message.

.. include:: ../../examples/muc.py
    :literal: