Tutorial
========

Wanna write a new :term:`legacy <Legacy>` :term:`module <Legacy Module>` for slidge to chat
with prisoners of walled gardens through your favorite XMPP client?
You've come to the right place.

Minimal example
---------------

Let's say we want to create a gateway to the famous *super duper chat network*.
Put this in a file called ``superduper.py``:

.. code-block:: python

    import super_duper.api  # great python lib!
    from super_duper.client import SuperDuperClient

    from slidge import BaseGateway, BaseSession, GatewayUser
    from slidge.contact import LegacyContact
    from slidge.util.types import XMPPMessage


    class Gateway(BaseGateway):
        COMPONENT_NAME = "Gateway to the super duper chat network"


    class Session(BaseSession):
        async def login(self):
            self.super_client = SuperDuperClient(
                login=self.user.registration_form["username"],
                password=self.user.registration_form["password"],
            )
            self.super_client.add_event_handler(
                callback=self.on_super_message,
                event=super_duper.api.IncomingMessageEvent
            )

            await self.legacy.login()

        async def on_super_message(self, msg: super_duper.api.Message):
            contact = await self.contacts.by_legacy_id(msg.sender_id)
            contact.send_text(msg.text, msg.msg_id)


        class Contact(LegacyContact):
            async def on_message(self, message: XMPPMessage) -> str:
                sent = self.session.super_client.send_message(
                    text=message.body, destination=self.legacy_id)
                return sent.msg_id


This can now be launched using ``slidge --legacy-network=superduper --server=...``

The gateway component
*********************

Let's dissect this a bit:

.. code-block:: python

    class Gateway(BaseGateway):
        COMPONENT_NAME = "Gateway to the super duper chat network"

By subclassing :class:`slidge.BaseGateway` we can customize our gateway component in
various ways. Here we just changed its name (something we **have** to do), but
we could also change the registration form fields by overriding
:py:attr:`slidge.BaseGateway.REGISTRATION_FIELDS`, among other things.

The user's session
******************

Setup
~~~~~

.. code-block:: python

    class Session(BaseSession):
        async def login():
            self.super_client = SuperDuperClient(
                login=self.user.registration_form["username"],
                password=self.user.registration_form["password"],
            )
            self.super_client.add_event_handler(
                callback=self.on_super_message,
                event=super_duper.api.IncomingMessageEvent
            )

The session represents the gateway user's session on the legacy network.
The :py:attr:`slidge.BaseSession.user` attribute is a
:class:`slidge.GatewayUser` instance and can be used to access the fields that
the user filled when subscribing to the gateway, via the
:py:attr:`slidge.GatewayUser.registration_form` dict.

Here, we added a ``super_client`` attribute to the session instance, because
our fake superduper lib is coded this way.
YMMV depending on the library you use.
Good python libs provide an event handler mechanism similar to what you see
here.

Login
~~~~~

.. code-block:: python

        async def login(self):
            await self.super_client.login()

When the gateway user is logged, this method is called on its
:py:attr:`slidge.Session.user` instance.
With the superduper library, starting to receive incoming messages is
very convenient, as you can see.

From "super duper" to XMPP
~~~~~~~~~~~~~~~~~~~~~~~~~~

.. code-block:: python

        async def on_super_message(self, msg: super_duper.api.Message):
            contact = await self.contacts.by_legacy_id(msg.sender)
            contact.send_text(msg.body, legacy_msg_id=msg.id)

We are really lucky, superduper user IDs can directly be mapped to the user part
of a JID. We can just use our session's virtual legacy roster to retrieve a
:class:`slidge.contact.LegacyContact` instance. Just by calling
:meth:`slidge.contact.LegacyContact.send_text`,
we effectively transported the message's text to the gateway user. Ain't that great?

From XMPP to "super duper"
~~~~~~~~~~~~~~~~~~~~~~~~~~

.. code-block:: python

    class Contact(LegacyContact):
        async def on_message(self, message: XMPPMessage) -> str:
            sent = self.session.super_client.send_message(
                text=message.body, destination=self.legacy_id)
            # return an identifier for this message, for further references
            # (reactions, replies, etc.).
            return sent.msg_id

When the :term:`user <User>` sends a message to
``something@superduper.example.org``, it triggers methods in
concrete implementations of :term:`Recipients`, ie, subclasses of
:class:`LegacyContact <slidge.contact.LegacyContact>` and
:class:`LegacyMUC <slidge.group.LegacyMUC>`.

Going further
-------------

Until we actually finish writing the docs, you can refer to :py:mod:`slidge`
for the API reference and existing
`legacy modules <https://codeberg.org/slidge>`_ for concrete example gateways
to existing chat network.
