Tutorial

Wanna write a new 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:

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:

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

By subclassing 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 slidge.BaseGateway.REGISTRATION_FIELDS, among other things.

The user’s session

Setup

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 slidge.BaseSession.user attribute is a slidge.GatewayUser instance and can be used to access the fields that the user filled when subscribing to the gateway, via the 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

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

When the gateway user is logged, this method is called on its 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

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 slidge.contact.LegacyContact instance. Just by calling 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”

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 user sends a message to something@superduper.example.org, it triggers methods in concrete implementations of Recipients, ie, subclasses of LegacyContact and LegacyMUC.

Going further

Until we actually finish writing the docs, you can refer to slidge for the API reference and existing legacy modules for concrete example gateways to existing chat network.