Tutorial: minimal legacy module from scratch¶
Wanna write a new “legacy chat network” slidge plugin? 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 *
class Gateway(BaseGateway):
COMPONENT_NAME = "Gateway to the super duper chat network"
class Session(BaseSession):
def __init__(self, user: GatewayUser):
super().__init__(user)
self.legacy = SuperDuperClient(
login=self.user.registration_form["username"],
password=self.user.registration_form["password"],
)
self.legacy.add_event_handler(
callback=self.incoming_legacy_message,
event=super_duper.api.IncomingMessageEvent
)
async def login():
await self.legacy.login()
async def incoming_legacy_message(self, msg: super_duper.api.Message):
contact = await self.contacts.by_legacy_id(msg.sender)
contact.send_text(msg.text)
async def send_text(self, chat: Recipient, text: str, *kwargs):
self.legacy.send_message(text=text, destination=chat.legacy_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 legacy session¶
Setup¶
class Session(BaseSession):
def __init__(self, user: GatewayUser):
super().__init__(user)
self.legacy = SuperDuperClient(
login=self.user.registration_form["username"],
password=self.user.registration_form["password"],
)
self.legacy.add_event_handler(
callback=self.incoming_legacy_message,
event=super_duper.api.IncomingMessageEvent
)
The session represents the gateway user’s session on the legacy network.
To add custom attributes to it, override the __init__
without changing its
signature and do not forget to call the base class __init__
.
The slidge.Session.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 slidge.GatewayUser.registration_form
dict.
Here, we added a legacy
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.legacy.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 legacy to XMPP¶
async def incoming_legacy_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.LegacyContact
instance. Just by calling slidge.LegacyContact.send_text()
,
we effectively transported the message’s text to the gateway user. Ain’t that great?
From XMPP to legacy¶
async def send_text(self, chat: Recipient, text: str, **kwargs):
self.legacy.send_message(text=text, destination=chat.legacy_id)
When our user sends a message to something@superduper.example.org
,
this method is automagically called, allowing us to transmit the message to the legacy network.
Going further¶
Until we actually write this section, you can refer to slidge
for the API
reference, to superduper
for a mock legacy module that serves as a
minimal working example, or have a look at the existing
legacy module