XEP-0231: API changes

- ``get_bob`` and ``set_bob`` are now coroutines.
- ``del_bob`` returns a Future.
This commit is contained in:
mathieui 2021-02-14 12:02:13 +01:00
parent ab87b25030
commit 9947d3db85
3 changed files with 64 additions and 22 deletions

View file

@ -9,6 +9,40 @@ XEP-0231: Bits of Binary
:exclude-members: session_bind, plugin_init, plugin_end :exclude-members: session_bind, plugin_init, plugin_end
Internal API methods
--------------------
The default API handlers for this plugin manage an in-memory cache of
bits of binary by content-id.
.. glossary::
set_bob
- **jid**: :class:`~.JID` sending the bob
- **node**: unused
- **ifrom**: :class:`~JID` receiving the bob
- **args**: :class:`~.BitsOfBinary` element.
Set a BoB in the cache.
get_bob
- **jid**: :class:`~.JID` receiving the bob
- **node**: unused
- **ifrom**: :class:`~JID` sending the bob
- **args**: ``str`` content-id of the bob
- **returns**: :class:`~.BitsOfBinary` element.
Get a BoB from the cache.
del_bob
- **jid**: unused
- **node**: unused
- **ifrom**: :class:`~JID` sending the bob
- **args**: ``str`` content-id of the bob
Delete a BoB from the cache.
Stanza elements Stanza elements
--------------- ---------------

View file

@ -20,7 +20,7 @@ class TestBOB(SlixIntegration):
async def test_bob(self): async def test_bob(self):
"""Check we can send and receive a BOB.""" """Check we can send and receive a BOB."""
cid = self.clients[0]['xep_0231'].set_bob( cid = await self.clients[0]['xep_0231'].set_bob(
self.data, self.data,
'image/jpeg', 'image/jpeg',
) )

View file

@ -12,7 +12,7 @@ from typing import Optional
from slixmpp import future_wrapper, JID from slixmpp import future_wrapper, JID
from slixmpp.stanza import Iq, Message, Presence from slixmpp.stanza import Iq, Message, Presence
from slixmpp.exceptions import XMPPError from slixmpp.exceptions import XMPPError
from slixmpp.xmlstream.handler import Callback from slixmpp.xmlstream.handler import CoroutineCallback
from slixmpp.xmlstream.matcher import StanzaPath from slixmpp.xmlstream.matcher import StanzaPath
from slixmpp.xmlstream import register_stanza_plugin from slixmpp.xmlstream import register_stanza_plugin
from slixmpp.plugins.base import BasePlugin from slixmpp.plugins.base import BasePlugin
@ -40,17 +40,17 @@ class XEP_0231(BasePlugin):
register_stanza_plugin(Presence, BitsOfBinary) register_stanza_plugin(Presence, BitsOfBinary)
self.xmpp.register_handler( self.xmpp.register_handler(
Callback('Bits of Binary - Iq', CoroutineCallback('Bits of Binary - Iq',
StanzaPath('iq/bob'), StanzaPath('iq/bob'),
self._handle_bob_iq)) self._handle_bob_iq))
self.xmpp.register_handler( self.xmpp.register_handler(
Callback('Bits of Binary - Message', CoroutineCallback('Bits of Binary - Message',
StanzaPath('message/bob'), StanzaPath('message/bob'),
self._handle_bob)) self._handle_bob))
self.xmpp.register_handler( self.xmpp.register_handler(
Callback('Bits of Binary - Presence', CoroutineCallback('Bits of Binary - Presence',
StanzaPath('presence/bob'), StanzaPath('presence/bob'),
self._handle_bob)) self._handle_bob))
@ -67,13 +67,14 @@ class XEP_0231(BasePlugin):
def session_bind(self, jid): def session_bind(self, jid):
self.xmpp['xep_0030'].add_feature('urn:xmpp:bob') self.xmpp['xep_0030'].add_feature('urn:xmpp:bob')
def set_bob(self, data: bytes, mtype: str, cid: Optional[str] = None, async def set_bob(self, data: bytes, mtype: str, cid: Optional[str] = None,
max_age: Optional[int] = None) -> str: max_age: Optional[int] = None) -> str:
"""Register a blob of binary data as a BOB. """Register a blob of binary data as a BOB.
.. versionchanged:: 1.8.0 .. versionchanged:: 1.8.0
If ``max_age`` is specified, the registered data will be destroyed If ``max_age`` is specified, the registered data will be destroyed
after that time. after that time.
This function is now a coroutine.
:param data: Data to register. :param data: Data to register.
:param mtype: Mime Type of the data (e.g. ``image/jpeg``). :param mtype: Mime Type of the data (e.g. ``image/jpeg``).
@ -90,27 +91,27 @@ class XEP_0231(BasePlugin):
bob['cid'] = cid bob['cid'] = cid
bob['max_age'] = max_age bob['max_age'] = max_age
self.api['set_bob'](args=bob) await self.api['set_bob'](args=bob)
# Schedule destruction of the data # Schedule destruction of the data
if max_age is not None and max_age > 0: if max_age is not None and max_age > 0:
self.xmpp.loop.call_later(max_age, self.del_bob, cid) self.xmpp.loop.call_later(max_age, self.del_bob, cid)
return cid return cid
@future_wrapper async def get_bob(self, jid: Optional[JID] = None, cid: Optional[str] = None,
def get_bob(self, jid: Optional[JID] = None, cid: Optional[str] = None, cached: bool = True, ifrom: Optional[JID] = None,
cached: bool = True, ifrom: Optional[JID] = None, **iqkwargs) -> Iq:
**iqkwargs) -> Future:
"""Get a BOB. """Get a BOB.
.. versionchanged:: 1.8.0 .. versionchanged:: 1.8.0
Results not in cache do not raise an error when ``cached`` is True. Results not in cache do not raise an error when ``cached`` is True.
This function is now a coroutine.
:param jid: JID to fetch the BOB from. :param jid: JID to fetch the BOB from.
:param cid: Content ID (actually required). :param cid: Content ID (actually required).
:param cached: To fetch the BOB from the local cache first (from CID only) :param cached: To fetch the BOB from the local cache first (from CID only)
""" """
if cached: if cached:
data = self.api['get_bob'](None, None, ifrom, args=cid) data = await self.api['get_bob'](None, None, ifrom, args=cid)
if data is not None: if data is not None:
if not isinstance(data, Iq): if not isinstance(data, Iq):
iq = self.xmpp.Iq() iq = self.xmpp.Iq()
@ -120,19 +121,24 @@ class XEP_0231(BasePlugin):
iq = self.xmpp.make_iq_get(ito=jid, ifrom=ifrom) iq = self.xmpp.make_iq_get(ito=jid, ifrom=ifrom)
iq['bob']['cid'] = cid iq['bob']['cid'] = cid
return iq.send(**iqkwargs) return await iq.send(**iqkwargs)
def del_bob(self, cid: str): def del_bob(self, cid: str) -> Future:
self.api['del_bob'](args=cid) """Delete a stored BoB.
def _handle_bob_iq(self, iq: Iq): .. versionchanged:: 1.8.0
This function now returns a Future.
"""
return self.api['del_bob'](args=cid)
async def _handle_bob_iq(self, iq: Iq):
cid = iq['bob']['cid'] cid = iq['bob']['cid']
if iq['type'] == 'result': if iq['type'] == 'result':
self.api['set_bob'](iq['from'], None, iq['to'], args=iq['bob']) await self.api['set_bob'](iq['from'], None, iq['to'], args=iq['bob'])
self.xmpp.event('bob', iq) self.xmpp.event('bob', iq)
elif iq['type'] == 'get': elif iq['type'] == 'get':
data = self.api['get_bob'](iq['to'], None, iq['from'], args=cid) data = await self.api['get_bob'](iq['to'], None, iq['from'], args=cid)
if isinstance(data, Iq): if isinstance(data, Iq):
data['id'] = iq['id'] data['id'] = iq['id']
data.send() data.send()
@ -142,9 +148,11 @@ class XEP_0231(BasePlugin):
iq.append(data) iq.append(data)
iq.send() iq.send()
def _handle_bob(self, stanza): async def _handle_bob(self, stanza):
self.api['set_bob'](stanza['from'], None, await self.api['set_bob'](
stanza['to'], args=stanza['bob']) stanza['from'], None,
stanza['to'], args=stanza['bob']
)
self.xmpp.event('bob', stanza) self.xmpp.event('bob', stanza)
# ================================================================= # =================================================================