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']