feat: support XEP-0492 (Chat Notification Settings)
This commit is contained in:
parent
8d984cd8a1
commit
2e736bc715
8
doap.xml
8
doap.xml
@ -917,6 +917,14 @@
|
|||||||
<xmpp:since>1.8.6</xmpp:since>
|
<xmpp:since>1.8.6</xmpp:since>
|
||||||
</xmpp:SupportedXep>
|
</xmpp:SupportedXep>
|
||||||
</implements>
|
</implements>
|
||||||
|
<implements>
|
||||||
|
<xmpp:SupportedXep>
|
||||||
|
<xmpp:xep rdf:resource="https://xmpp.org/extensions/xep-0492.html"/>
|
||||||
|
<xmpp:status>complete</xmpp:status>
|
||||||
|
<xmpp:version>0.1.0</xmpp:version>
|
||||||
|
<xmpp:since>1.8.7</xmpp:since>
|
||||||
|
</xmpp:SupportedXep>
|
||||||
|
</implements>
|
||||||
|
|
||||||
<release>
|
<release>
|
||||||
<Version>
|
<Version>
|
||||||
|
@ -94,3 +94,4 @@ Plugin index
|
|||||||
xep_0439
|
xep_0439
|
||||||
xep_0441
|
xep_0441
|
||||||
xep_0444
|
xep_0444
|
||||||
|
xep_0492
|
||||||
|
18
docs/api/plugins/xep_0492.rst
Normal file
18
docs/api/plugins/xep_0492.rst
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
|
||||||
|
XEP-0492: Chat Notification Settings
|
||||||
|
===========================
|
||||||
|
|
||||||
|
.. module:: slixmpp.plugins.xep_0492
|
||||||
|
|
||||||
|
.. autoclass:: XEP_0492
|
||||||
|
:members:
|
||||||
|
:exclude-members: session_bind, plugin_init, plugin_end
|
||||||
|
|
||||||
|
|
||||||
|
Stanza elements
|
||||||
|
---------------
|
||||||
|
|
||||||
|
.. automodule:: slixmpp.plugins.xep_0492.stanza
|
||||||
|
:members:
|
||||||
|
:undoc-members:
|
||||||
|
|
@ -122,6 +122,7 @@ PLUGINS = [
|
|||||||
'xep_0461', # Message Replies
|
'xep_0461', # Message Replies
|
||||||
'xep_0469', # Bookmarks Pinning
|
'xep_0469', # Bookmarks Pinning
|
||||||
'xep_0490', # Message Displayed Synchronization
|
'xep_0490', # Message Displayed Synchronization
|
||||||
|
'xep_0492', # Chat Notification Settings
|
||||||
# Meant to be imported by plugins
|
# Meant to be imported by plugins
|
||||||
]
|
]
|
||||||
|
|
||||||
|
13
slixmpp/plugins/xep_0492/__init__.py
Normal file
13
slixmpp/plugins/xep_0492/__init__.py
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
# Slixmpp: The Slick XMPP Library
|
||||||
|
# Copyright (C) 2025 nicoco
|
||||||
|
# This file is part of Slixmpp.
|
||||||
|
# See the file LICENSE for copying permission.
|
||||||
|
|
||||||
|
from slixmpp.plugins.base import register_plugin
|
||||||
|
|
||||||
|
from . import stanza
|
||||||
|
from .notify import XEP_0492
|
||||||
|
|
||||||
|
register_plugin(XEP_0492)
|
||||||
|
|
||||||
|
__all__ = ["stanza", "XEP_0492"]
|
21
slixmpp/plugins/xep_0492/notify.py
Normal file
21
slixmpp/plugins/xep_0492/notify.py
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
# Slixmpp: The Slick XMPP Library
|
||||||
|
# Copyright (C) 2025 nicoco
|
||||||
|
# This file is part of Slixmpp.
|
||||||
|
# See the file LICENSE for copying permission.
|
||||||
|
|
||||||
|
from slixmpp.plugins import BasePlugin
|
||||||
|
from . import stanza
|
||||||
|
|
||||||
|
|
||||||
|
class XEP_0492(BasePlugin):
|
||||||
|
"""
|
||||||
|
XEP-0492: Chat notification settings
|
||||||
|
"""
|
||||||
|
|
||||||
|
name = "xep_0492"
|
||||||
|
description = "XEP-0492: Chat notification settings"
|
||||||
|
dependencies = {"xep_0402"}
|
||||||
|
stanza = stanza
|
||||||
|
|
||||||
|
def plugin_init(self):
|
||||||
|
stanza.register_plugin()
|
106
slixmpp/plugins/xep_0492/stanza.py
Normal file
106
slixmpp/plugins/xep_0492/stanza.py
Normal file
@ -0,0 +1,106 @@
|
|||||||
|
# Slixmpp: The Slick XMPP Library
|
||||||
|
# Copyright (C) 2025 nicoco
|
||||||
|
# This file is part of Slixmpp.
|
||||||
|
# See the file LICENSE for copying permission.
|
||||||
|
|
||||||
|
from typing import Literal, Optional, cast
|
||||||
|
|
||||||
|
from slixmpp import register_stanza_plugin
|
||||||
|
from slixmpp.plugins.xep_0402.stanza import Extensions
|
||||||
|
from slixmpp.types import ClientTypes
|
||||||
|
from slixmpp.xmlstream import ElementBase
|
||||||
|
|
||||||
|
NS = "urn:xmpp:notification-settings:0"
|
||||||
|
|
||||||
|
WhenLiteral = Literal["never", "always", "on-mention"]
|
||||||
|
|
||||||
|
|
||||||
|
class Notify(ElementBase):
|
||||||
|
"""
|
||||||
|
Chat notification settings element
|
||||||
|
|
||||||
|
|
||||||
|
To enable it on a Conference element, use configure() like this:
|
||||||
|
|
||||||
|
.. code-block::python
|
||||||
|
|
||||||
|
# C being a Conference element
|
||||||
|
C['extensions']["notify"].configure("always", client_type="pc")
|
||||||
|
|
||||||
|
Which will add the <notify> element to the <extensions> element.
|
||||||
|
"""
|
||||||
|
|
||||||
|
namespace = NS
|
||||||
|
name = "notify"
|
||||||
|
plugin_attrib = "notify"
|
||||||
|
interfaces = {"notify"}
|
||||||
|
|
||||||
|
def configure(self, when: WhenLiteral, client_type: Optional[ClientTypes] = None) -> None:
|
||||||
|
"""
|
||||||
|
Configure the chat notification settings for this bookmark.
|
||||||
|
|
||||||
|
This method ensures that there are no conflicting settings, e.g.,
|
||||||
|
both a <never /> and a <always /> element.
|
||||||
|
"""
|
||||||
|
cls = _CLASS_MAP[when]
|
||||||
|
element = cls()
|
||||||
|
if client_type is not None:
|
||||||
|
element["client-type"] = client_type
|
||||||
|
|
||||||
|
match = client_type if client_type is not None else ""
|
||||||
|
for child in self:
|
||||||
|
if isinstance(child, _Base) and child["client-type"] == match:
|
||||||
|
self.xml.remove(child.xml)
|
||||||
|
|
||||||
|
self.append(element)
|
||||||
|
|
||||||
|
def get_config(
|
||||||
|
self, client_type: Optional[ClientTypes] = None
|
||||||
|
) -> Optional[WhenLiteral]:
|
||||||
|
"""
|
||||||
|
Get the chat notification settings for this bookmark.
|
||||||
|
|
||||||
|
:param client_type: Optionally, get the notification for a specific client type.
|
||||||
|
If unset, returns the global notification setting.
|
||||||
|
|
||||||
|
:return: The chat notification setting as a string, or None if unset.
|
||||||
|
"""
|
||||||
|
match = client_type if client_type is not None else ""
|
||||||
|
for child in self:
|
||||||
|
if isinstance(child, _Base) and child["client-type"] == match:
|
||||||
|
return cast(WhenLiteral, child.name)
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
||||||
|
class _Base(ElementBase):
|
||||||
|
namespace = NS
|
||||||
|
interfaces = {"client-type"}
|
||||||
|
|
||||||
|
|
||||||
|
class Never(_Base):
|
||||||
|
name = "never"
|
||||||
|
|
||||||
|
|
||||||
|
class Always(_Base):
|
||||||
|
name = "always"
|
||||||
|
|
||||||
|
|
||||||
|
class OnMention(_Base):
|
||||||
|
name = "on-mention"
|
||||||
|
|
||||||
|
|
||||||
|
class Advanced(ElementBase):
|
||||||
|
namespace = NS
|
||||||
|
name = plugin_attrib = "advanced"
|
||||||
|
|
||||||
|
|
||||||
|
_CLASS_MAP = {
|
||||||
|
"never": Never,
|
||||||
|
"always": Always,
|
||||||
|
"on-mention": OnMention,
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
def register_plugin():
|
||||||
|
register_stanza_plugin(Extensions, Notify)
|
||||||
|
register_stanza_plugin(Notify, Advanced)
|
@ -109,8 +109,21 @@ ErrorConditions = Literal[
|
|||||||
"unexpected-request",
|
"unexpected-request",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
# https://xmpp.org/registrar/disco-categories.html#client
|
||||||
|
ClientTypes = Literal[
|
||||||
|
"bot",
|
||||||
|
"console",
|
||||||
|
"game",
|
||||||
|
"handheld",
|
||||||
|
"pc",
|
||||||
|
"phone",
|
||||||
|
"sms",
|
||||||
|
"tablet",
|
||||||
|
"web",
|
||||||
|
]
|
||||||
|
|
||||||
__all__ = [
|
__all__ = [
|
||||||
'Protocol', 'TypedDict', 'Literal', 'OptJid', 'OptJidStr', 'JidStr', 'MAMDefault',
|
'Protocol', 'TypedDict', 'Literal', 'OptJid', 'OptJidStr', 'JidStr', 'MAMDefault',
|
||||||
'PresenceTypes', 'PresenceShows', 'MessageTypes', 'IqTypes', 'MucRole',
|
'PresenceTypes', 'PresenceShows', 'MessageTypes', 'IqTypes', 'MucRole',
|
||||||
'MucAffiliation', 'FilterString', 'ErrorConditions', 'ErrorTypes'
|
'MucAffiliation', 'FilterString', 'ErrorConditions', 'ErrorTypes', 'ClientTypes'
|
||||||
]
|
]
|
||||||
|
178
tests/test_stanza_xep_0492.py
Normal file
178
tests/test_stanza_xep_0492.py
Normal file
@ -0,0 +1,178 @@
|
|||||||
|
# Slixmpp: The Slick XMPP Library
|
||||||
|
# Copyright (C) 2025 nicoco
|
||||||
|
# This file is part of Slixmpp.
|
||||||
|
# See the file LICENSE for copying permission.
|
||||||
|
|
||||||
|
import unittest
|
||||||
|
|
||||||
|
from slixmpp import register_stanza_plugin, ElementBase
|
||||||
|
from slixmpp.test import SlixTest
|
||||||
|
from slixmpp.plugins.xep_0492 import stanza
|
||||||
|
from slixmpp.plugins.xep_0402 import stanza as b_stanza
|
||||||
|
|
||||||
|
|
||||||
|
class TestNotificationSetting(SlixTest):
|
||||||
|
def setUp(self):
|
||||||
|
b_stanza.register_plugin()
|
||||||
|
stanza.register_plugin()
|
||||||
|
|
||||||
|
def test_never(self):
|
||||||
|
bookmark = b_stanza.Conference()
|
||||||
|
bookmark["extensions"]["notify"].configure("never")
|
||||||
|
self.check(
|
||||||
|
bookmark,
|
||||||
|
"""
|
||||||
|
<conference xmlns='urn:xmpp:bookmarks:1'>
|
||||||
|
<extensions>
|
||||||
|
<notify xmlns='urn:xmpp:notification-settings:0'>
|
||||||
|
<never />
|
||||||
|
</notify>
|
||||||
|
</extensions>
|
||||||
|
</conference>
|
||||||
|
""",
|
||||||
|
use_values=False,
|
||||||
|
)
|
||||||
|
|
||||||
|
def test_always(self):
|
||||||
|
bookmark = b_stanza.Conference()
|
||||||
|
bookmark["extensions"]["notify"].configure("always")
|
||||||
|
self.check(
|
||||||
|
bookmark,
|
||||||
|
"""
|
||||||
|
<conference xmlns='urn:xmpp:bookmarks:1'>
|
||||||
|
<extensions>
|
||||||
|
<notify xmlns='urn:xmpp:notification-settings:0'>
|
||||||
|
<always />
|
||||||
|
</notify>
|
||||||
|
</extensions>
|
||||||
|
</conference>
|
||||||
|
""",
|
||||||
|
use_values=False,
|
||||||
|
)
|
||||||
|
|
||||||
|
def test_on_mention(self):
|
||||||
|
bookmark = b_stanza.Conference()
|
||||||
|
bookmark["extensions"]["notify"].configure("on-mention")
|
||||||
|
self.check(
|
||||||
|
bookmark,
|
||||||
|
"""
|
||||||
|
<conference xmlns='urn:xmpp:bookmarks:1'>
|
||||||
|
<extensions>
|
||||||
|
<notify xmlns='urn:xmpp:notification-settings:0'>
|
||||||
|
<on-mention />
|
||||||
|
</notify>
|
||||||
|
</extensions>
|
||||||
|
</conference>
|
||||||
|
""",
|
||||||
|
use_values=False,
|
||||||
|
)
|
||||||
|
|
||||||
|
def test_advanced(self):
|
||||||
|
bookmark = b_stanza.Conference()
|
||||||
|
bookmark["extensions"]["notify"].configure("never", client_type="pc")
|
||||||
|
bookmark["extensions"]["notify"].configure("on-mention", client_type="mobile")
|
||||||
|
|
||||||
|
register_stanza_plugin(stanza.Advanced, AdvancedExtension)
|
||||||
|
bookmark["extensions"]["notify"]["advanced"].enable("cool")
|
||||||
|
bookmark["extensions"]["notify"]["advanced"]["cool"]["attrib"] = "cool-attrib"
|
||||||
|
bookmark["extensions"]["notify"]["advanced"]["cool"]["content"] = "cool-content"
|
||||||
|
self.check(
|
||||||
|
bookmark,
|
||||||
|
"""
|
||||||
|
<conference xmlns='urn:xmpp:bookmarks:1'>
|
||||||
|
<extensions>
|
||||||
|
<notify xmlns='urn:xmpp:notification-settings:0'>
|
||||||
|
<never client-type="pc" />
|
||||||
|
<on-mention client-type="mobile" />
|
||||||
|
<advanced>
|
||||||
|
<cool xmlns="cool-ns" attrib="cool-attrib">cool-content</cool>
|
||||||
|
</advanced>
|
||||||
|
</notify>
|
||||||
|
</extensions>
|
||||||
|
</conference>
|
||||||
|
""",
|
||||||
|
use_values=False,
|
||||||
|
)
|
||||||
|
|
||||||
|
def test_change_config(self):
|
||||||
|
bookmark = b_stanza.Conference()
|
||||||
|
bookmark["extensions"]["notify"].configure("never")
|
||||||
|
bookmark["extensions"]["notify"].configure("never", client_type="pc")
|
||||||
|
bookmark["extensions"]["notify"].configure("on-mention", client_type="mobile")
|
||||||
|
|
||||||
|
self.check(
|
||||||
|
bookmark,
|
||||||
|
"""
|
||||||
|
<conference xmlns='urn:xmpp:bookmarks:1'>
|
||||||
|
<extensions>
|
||||||
|
<notify xmlns='urn:xmpp:notification-settings:0'>
|
||||||
|
<never />
|
||||||
|
<never client-type="pc" />
|
||||||
|
<on-mention client-type="mobile" />
|
||||||
|
</notify>
|
||||||
|
</extensions>
|
||||||
|
</conference>
|
||||||
|
""",
|
||||||
|
use_values=False,
|
||||||
|
)
|
||||||
|
|
||||||
|
bookmark["extensions"]["notify"].configure("always")
|
||||||
|
|
||||||
|
self.check(
|
||||||
|
bookmark,
|
||||||
|
"""
|
||||||
|
<conference xmlns='urn:xmpp:bookmarks:1'>
|
||||||
|
<extensions>
|
||||||
|
<notify xmlns='urn:xmpp:notification-settings:0'>
|
||||||
|
<always />
|
||||||
|
<never client-type="pc" />
|
||||||
|
<on-mention client-type="mobile" />
|
||||||
|
</notify>
|
||||||
|
</extensions>
|
||||||
|
</conference>
|
||||||
|
""",
|
||||||
|
use_values=False,
|
||||||
|
)
|
||||||
|
|
||||||
|
bookmark["extensions"]["notify"].configure("always", "mobile")
|
||||||
|
|
||||||
|
self.check(
|
||||||
|
bookmark,
|
||||||
|
"""
|
||||||
|
<conference xmlns='urn:xmpp:bookmarks:1'>
|
||||||
|
<extensions>
|
||||||
|
<notify xmlns='urn:xmpp:notification-settings:0'>
|
||||||
|
<always />
|
||||||
|
<never client-type="pc" />
|
||||||
|
<always client-type="mobile" />
|
||||||
|
</notify>
|
||||||
|
</extensions>
|
||||||
|
</conference>
|
||||||
|
""",
|
||||||
|
use_values=False,
|
||||||
|
)
|
||||||
|
|
||||||
|
def test_get_config(self):
|
||||||
|
bookmark = b_stanza.Conference()
|
||||||
|
bookmark["extensions"]["notify"].configure("never")
|
||||||
|
bookmark["extensions"]["notify"].configure("never", client_type="pc")
|
||||||
|
bookmark["extensions"]["notify"].configure("on-mention", client_type="mobile")
|
||||||
|
|
||||||
|
self.assertEqual(bookmark["extensions"]["notify"].get_config(), "never")
|
||||||
|
self.assertEqual(bookmark["extensions"]["notify"].get_config("pc"), "never")
|
||||||
|
self.assertEqual(
|
||||||
|
bookmark["extensions"]["notify"].get_config("mobile"), "on-mention"
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class AdvancedExtension(ElementBase):
|
||||||
|
namespace = "cool-ns"
|
||||||
|
name = "cool"
|
||||||
|
plugin_attrib = name
|
||||||
|
interfaces = {"attrib", "content"}
|
||||||
|
|
||||||
|
def set_content(self, content: str):
|
||||||
|
self.xml.text = content
|
||||||
|
|
||||||
|
|
||||||
|
suite = unittest.TestLoader().loadTestsFromTestCase(TestNotificationSetting)
|
Loading…
Reference in New Issue
Block a user