diff --git a/docs/api/plugins/index.rst b/docs/api/plugins/index.rst index e6aa6913..6737d3c9 100644 --- a/docs/api/plugins/index.rst +++ b/docs/api/plugins/index.rst @@ -92,6 +92,5 @@ Plugin index xep_0428 xep_0437 xep_0439 + xep_0441 xep_0444 - - diff --git a/docs/api/plugins/xep_0441.rst b/docs/api/plugins/xep_0441.rst new file mode 100644 index 00000000..34e51ea1 --- /dev/null +++ b/docs/api/plugins/xep_0441.rst @@ -0,0 +1,18 @@ + +XEP-0441: Message Archive Management Preferences +================================================ + +.. module:: slixmpp.plugins.xep_0441 + +.. autoclass:: XEP_0441 + :members: + :exclude-members: session_bind, plugin_init, plugin_end + + +Stanza elements +--------------- + +.. automodule:: slixmpp.plugins.xep_0441.stanza + :members: + :undoc-members: + diff --git a/slixmpp/plugins/__init__.py b/slixmpp/plugins/__init__.py index d087f92b..55627113 100644 --- a/slixmpp/plugins/__init__.py +++ b/slixmpp/plugins/__init__.py @@ -110,5 +110,6 @@ __all__ = [ 'xep_0428', # Message Fallback 'xep_0437', # Room Activity Indicators 'xep_0439', # Quick Response + 'xep_0441', # Message Archive Management Preferences 'xep_0444', # Message Reactions ] diff --git a/slixmpp/plugins/xep_0313/__init__.py b/slixmpp/plugins/xep_0313/__init__.py index eace3dc7..04e65eff 100644 --- a/slixmpp/plugins/xep_0313/__init__.py +++ b/slixmpp/plugins/xep_0313/__init__.py @@ -5,8 +5,10 @@ # See the file LICENSE for copying permissio from slixmpp.plugins.base import register_plugin -from slixmpp.plugins.xep_0313.stanza import Result, MAM, Preferences +from slixmpp.plugins.xep_0313.stanza import Result, MAM from slixmpp.plugins.xep_0313.mam import XEP_0313 register_plugin(XEP_0313) + +__all__ = ['XEP_0313', 'Result', 'MAM'] diff --git a/slixmpp/plugins/xep_0313/mam.py b/slixmpp/plugins/xep_0313/mam.py index ba4c02bb..5f2c0bcc 100644 --- a/slixmpp/plugins/xep_0313/mam.py +++ b/slixmpp/plugins/xep_0313/mam.py @@ -37,7 +37,6 @@ class XEP_0313(BasePlugin): def plugin_init(self): register_stanza_plugin(stanza.MAM, Form) register_stanza_plugin(Iq, stanza.MAM) - register_stanza_plugin(Iq, stanza.Preferences) register_stanza_plugin(Message, stanza.Result) register_stanza_plugin(Iq, stanza.Fin) register_stanza_plugin( diff --git a/slixmpp/plugins/xep_0313/stanza.py b/slixmpp/plugins/xep_0313/stanza.py index 3021f1ad..55c80a35 100644 --- a/slixmpp/plugins/xep_0313/stanza.py +++ b/slixmpp/plugins/xep_0313/stanza.py @@ -169,86 +169,6 @@ class MAM(ElementBase): self._results = [] -class Preferences(ElementBase): - """MAM Preferences payload. - - .. code-block:: xml - - - - - romeo@montague.lit - - - montague@montague.lit - - - - - """ - name = 'prefs' - namespace = 'urn:xmpp:mam:2' - plugin_attrib = 'mam_prefs' - #: Available interfaces: - #: - #: - ``default``: Default MAM policy (must be one of 'roster', 'always', - #: 'never' - #: - ``always`` (``List[JID]``): list of JIDs to always store - #: conversations with. - #: - ``never`` (``List[JID]``): list of JIDs to never store - #: conversations with. - interfaces = {'default', 'always', 'never'} - sub_interfaces = {'always', 'never'} - - def get_always(self) -> Set[JID]: - results = set() - - jids = self.xml.findall('{%s}always/{%s}jid' % ( - self.namespace, self.namespace)) - - for jid in jids: - results.add(JID(jid.text)) - - return results - - def set_always(self, value: Iterable[JID]): - self._set_sub_text('always', '', keep=True) - always = self.xml.find('{%s}always' % self.namespace) - always.clear() - - if not isinstance(value, (list, set)): - value = [value] - - for jid in value: - jid_xml = ET.Element('{%s}jid' % self.namespace) - jid_xml.text = str(jid) - always.append(jid_xml) - - def get_never(self) -> Set[JID]: - results = set() - - jids = self.xml.findall('{%s}never/{%s}jid' % ( - self.namespace, self.namespace)) - - for jid in jids: - results.add(JID(jid.text)) - - return results - - def set_never(self, value: Iterable[JID]): - self._set_sub_text('never', '', keep=True) - never = self.xml.find('{%s}never' % self.namespace) - never.clear() - - if not isinstance(value, (list, set)): - value = [value] - - for jid in value: - jid_xml = ET.Element('{%s}jid' % self.namespace) - jid_xml.text = str(jid) - never.append(jid_xml) - - class Fin(ElementBase): name = 'fin' namespace = 'urn:xmpp:mam:2' diff --git a/slixmpp/plugins/xep_0441/__init__.py b/slixmpp/plugins/xep_0441/__init__.py new file mode 100644 index 00000000..0e169082 --- /dev/null +++ b/slixmpp/plugins/xep_0441/__init__.py @@ -0,0 +1,13 @@ +# Slixmpp: The Slick XMPP Library +# Copyright (C) 2021 Mathieu Pasquet +# This file is part of Slixmpp. +# See the file LICENSE for copying permissio +from slixmpp.plugins.base import register_plugin + +from slixmpp.plugins.xep_0441.stanza import Preferences +from slixmpp.plugins.xep_0441.mam_prefs import XEP_0441 + + +register_plugin(XEP_0441) + +__all__ = ['XEP_0441', 'Preferences'] diff --git a/slixmpp/plugins/xep_0441/mam_prefs.py b/slixmpp/plugins/xep_0441/mam_prefs.py new file mode 100644 index 00000000..06a830bc --- /dev/null +++ b/slixmpp/plugins/xep_0441/mam_prefs.py @@ -0,0 +1,75 @@ +# Slixmpp: The Slick XMPP Library +# Copyright (C) 2021 Mathieu Pasquet +# This file is part of Slixmpp. +# See the file LICENSE for copying permission +import logging + +from asyncio import Future +from typing import ( + List, + Optional, + Tuple, +) + +from slixmpp import JID +from slixmpp.types import MAMDefault +from slixmpp.stanza import Iq +from slixmpp.xmlstream import register_stanza_plugin +from slixmpp.plugins import BasePlugin +from slixmpp.plugins.xep_0441 import stanza + + +log = logging.getLogger(__name__) + + +class XEP_0441(BasePlugin): + + """ + XEP-0441: Message Archive Management Preferences + """ + + name = 'xep_0441' + description = 'XEP-0441: Message Archive Management Preferences' + dependencies = {'xep_0313'} + stanza = stanza + + def plugin_init(self): + register_stanza_plugin(Iq, stanza.Preferences) + + async def get_preferences(self, **iqkwargs + ) -> Tuple[MAMDefault, List[JID], List[JID]]: + """Get the current MAM preferences. + + :returns: A tuple of MAM preferences with (default, always, never) + """ + ifrom = iqkwargs.pop('ifrom', None) + ito = iqkwargs.pop('ito', None) + iq = self.xmpp.make_iq_get(ito=ito, ifrom=ifrom) + iq['type'] = 'get' + query_id = iq['id'] + iq['mam_prefs']['query_id'] = query_id + result = await iq.send(**iqkwargs) + return ( + result['mam_prefs']['default'], + result['mam_prefs']['always'], + result['mam_prefs']['never'] + ) + + def set_preferences(self, default: Optional[MAMDefault] = 'roster', + always: Optional[List[JID]] = None, + never: Optional[List[JID]] = None, *, + ito: Optional[JID] = None, ifrom: Optional[JID] = None, + **iqkwargs) -> Future: + """Set MAM Preferences. + + The server answer MAY contain different items. + + :param default: Default behavior (one of 'always', 'never', 'roster'). + :param always: List of JIDs whose messages will always be stored. + :param never: List of JIDs whose messages will never be stored. + """ + iq = self.xmpp.make_iq_set(ito=ito, ifrom=ifrom) + iq['mam_prefs']['default'] = default + iq['mam_prefs']['always'] = always + iq['mam_prefs']['never'] = never + return iq.send(**iqkwargs) diff --git a/slixmpp/plugins/xep_0441/stanza.py b/slixmpp/plugins/xep_0441/stanza.py new file mode 100644 index 00000000..99dbe802 --- /dev/null +++ b/slixmpp/plugins/xep_0441/stanza.py @@ -0,0 +1,91 @@ +# Slixmpp: The Slick XMPP Library +# Copyright (C) 2021 Mathieu Pasquet +# This file is part of Slixmpp. +# See the file LICENSE for copying permissio +from typing import ( + Iterable, + Set, +) + +from slixmpp.jid import JID +from slixmpp.xmlstream import ElementBase, ET + + +class Preferences(ElementBase): + """MAM Preferences payload. + + .. code-block:: xml + + + + + romeo@montague.lit + + + montague@montague.lit + + + + + """ + name = 'prefs' + namespace = 'urn:xmpp:mam:2' + plugin_attrib = 'mam_prefs' + #: Available interfaces: + #: + #: - ``default``: Default MAM policy (must be one of 'roster', 'always', + #: 'never' + #: - ``always`` (``List[JID]``): list of JIDs to always store + #: conversations with. + #: - ``never`` (``List[JID]``): list of JIDs to never store + #: conversations with. + interfaces = {'default', 'always', 'never'} + sub_interfaces = {'always', 'never'} + + def get_always(self) -> Set[JID]: + results = set() + + jids = self.xml.findall('{%s}always/{%s}jid' % ( + self.namespace, self.namespace)) + + for jid in jids: + results.add(JID(jid.text)) + + return results + + def set_always(self, value: Iterable[JID]): + self._set_sub_text('always', '', keep=True) + always = self.xml.find('{%s}always' % self.namespace) + always.clear() + + if not isinstance(value, (list, set)): + value = [value] + + for jid in value: + jid_xml = ET.Element('{%s}jid' % self.namespace) + jid_xml.text = str(jid) + always.append(jid_xml) + + def get_never(self) -> Set[JID]: + results = set() + + jids = self.xml.findall('{%s}never/{%s}jid' % ( + self.namespace, self.namespace)) + + for jid in jids: + results.add(JID(jid.text)) + + return results + + def set_never(self, value: Iterable[JID]): + self._set_sub_text('never', '', keep=True) + never = self.xml.find('{%s}never' % self.namespace) + never.clear() + + if not isinstance(value, (list, set)): + value = [value] + + for jid in value: + jid_xml = ET.Element('{%s}jid' % self.namespace) + jid_xml.text = str(jid) + never.append(jid_xml) diff --git a/slixmpp/types.py b/slixmpp/types.py index b9a1d319..453d25e3 100644 --- a/slixmpp/types.py +++ b/slixmpp/types.py @@ -76,3 +76,5 @@ MucRoomItemKeys = Literal[ OptJid = Optional[JID] JidStr = Union[str, JID] OptJidStr = Optional[Union[str, JID]] + +MAMDefault = Literal['always', 'never', 'roster']