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
|
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``:
.. code-block:: python
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 on_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:
.. 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 legacy session
******************
Setup
~~~~~
.. code-block:: python
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 :py:attr:`slidge.Session.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 :py:attr:`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
~~~~~
.. code-block:: python
async def login(self):
await self.legacy.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 legacy to XMPP
~~~~~~~~~~~~~~~~~~~
.. code-block:: python
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
:class:`slidge.LegacyContact` instance. Just by calling :meth:`slidge.LegacyContact.send_text`,
we effectively transported the message's text to the gateway user. Ain't that great?
From XMPP to legacy
~~~~~~~~~~~~~~~~~~~
.. code-block:: python
async def on_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 :py:mod:`slidge` for the API
reference, to :py:mod:`superduper` for a mock legacy module that serves as a
minimal working example, or have a look at the existing
`legacy modules <https://codeberg.org/slidge>`_
|