199 lines
6.7 KiB
ReStructuredText
199 lines
6.7 KiB
ReStructuredText
.. _mucbot:
|
|
|
|
=========================
|
|
Multi-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 <https://codeberg.org/poezio/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
|
|
|
|
async def start(self, event):
|
|
await self.get_roster()
|
|
self.send_presence()
|
|
self.plugin['xep_0045'].join_muc(self.room,
|
|
self.nick)
|
|
|
|
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:
|