Support XEP-0490 (Message Display Synchronization)
This commit is contained in:
parent
a18a6c4eb8
commit
23544731ef
8
doap.xml
8
doap.xml
@ -909,6 +909,14 @@
|
|||||||
<xmpp:note>no thumbnail support</xmpp:note>
|
<xmpp:note>no thumbnail support</xmpp:note>
|
||||||
</xmpp:SupportedXep>
|
</xmpp:SupportedXep>
|
||||||
</implements>
|
</implements>
|
||||||
|
<implements>
|
||||||
|
<xmpp:SupportedXep>
|
||||||
|
<xmpp:xep rdf:resource="https://xmpp.org/extensions/xep-0490.html"/>
|
||||||
|
<xmpp:status>complete</xmpp:status>
|
||||||
|
<xmpp:version>0.1.0</xmpp:version>
|
||||||
|
<xmpp:since>1.8.6</xmpp:since>
|
||||||
|
</xmpp:SupportedXep>
|
||||||
|
</implements>
|
||||||
|
|
||||||
<release>
|
<release>
|
||||||
<Version>
|
<Version>
|
||||||
|
@ -121,6 +121,7 @@ PLUGINS = [
|
|||||||
'xep_0447', # Stateless file sharing
|
'xep_0447', # Stateless file sharing
|
||||||
'xep_0461', # Message Replies
|
'xep_0461', # Message Replies
|
||||||
'xep_0469', # Bookmarks Pinning
|
'xep_0469', # Bookmarks Pinning
|
||||||
|
'xep_0490', # Message Displayed Synchronization
|
||||||
# Meant to be imported by plugins
|
# Meant to be imported by plugins
|
||||||
]
|
]
|
||||||
|
|
||||||
|
@ -20,6 +20,18 @@ class XEP_0223(BasePlugin):
|
|||||||
|
|
||||||
"""
|
"""
|
||||||
XEP-0223: Persistent Storage of Private Data via PubSub
|
XEP-0223: Persistent Storage of Private Data via PubSub
|
||||||
|
|
||||||
|
If a specific pubsub node requires additional publish options, edit the
|
||||||
|
:attr:`.node_profile` attribute of this plugin:
|
||||||
|
|
||||||
|
.. code-block:: python
|
||||||
|
|
||||||
|
self.xmpp.plugin["xep_0223"].node_profiles["urn:some:node"] = {
|
||||||
|
"pubsub#max_items" = "max"
|
||||||
|
}
|
||||||
|
|
||||||
|
This makes :meth:`.store` add these publish options whenever it is called
|
||||||
|
for the ``urn:some:node`` node.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
name = 'xep_0223'
|
name = 'xep_0223'
|
||||||
@ -28,6 +40,7 @@ class XEP_0223(BasePlugin):
|
|||||||
|
|
||||||
profile = {'pubsub#persist_items': True,
|
profile = {'pubsub#persist_items': True,
|
||||||
'pubsub#access_model': 'whitelist'}
|
'pubsub#access_model': 'whitelist'}
|
||||||
|
node_profiles = dict[str, dict[str, str]]()
|
||||||
|
|
||||||
def configure(self, node: str, **iqkwargs) -> Future:
|
def configure(self, node: str, **iqkwargs) -> Future:
|
||||||
"""
|
"""
|
||||||
@ -70,7 +83,8 @@ class XEP_0223(BasePlugin):
|
|||||||
value='http://jabber.org/protocol/pubsub#publish-options')
|
value='http://jabber.org/protocol/pubsub#publish-options')
|
||||||
|
|
||||||
fields = options['fields']
|
fields = options['fields']
|
||||||
for field, value in self.profile.items():
|
profile = self.profile | self.node_profiles.get(node, {})
|
||||||
|
for field, value in profile.items():
|
||||||
if field not in fields:
|
if field not in fields:
|
||||||
options.add_field(var=field)
|
options.add_field(var=field)
|
||||||
options.get_fields()[field]['value'] = value
|
options.get_fields()[field]['value'] = value
|
||||||
|
8
slixmpp/plugins/xep_0490/__init__.py
Normal file
8
slixmpp/plugins/xep_0490/__init__.py
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
from slixmpp.plugins.base import register_plugin
|
||||||
|
|
||||||
|
from . import stanza
|
||||||
|
from .mds import XEP_0490
|
||||||
|
|
||||||
|
register_plugin(XEP_0490)
|
||||||
|
|
||||||
|
__all__ = ['stanza', 'XEP_0490']
|
42
slixmpp/plugins/xep_0490/mds.py
Normal file
42
slixmpp/plugins/xep_0490/mds.py
Normal file
@ -0,0 +1,42 @@
|
|||||||
|
from asyncio import Future
|
||||||
|
|
||||||
|
from slixmpp import Iq
|
||||||
|
from slixmpp.plugins import BasePlugin
|
||||||
|
from slixmpp.types import JidStr
|
||||||
|
|
||||||
|
from . import stanza
|
||||||
|
from ..xep_0004 import Form
|
||||||
|
|
||||||
|
|
||||||
|
class XEP_0490(BasePlugin):
|
||||||
|
"""
|
||||||
|
XEP-0490: Message Displayed Synchronization
|
||||||
|
"""
|
||||||
|
|
||||||
|
name = "xep_0490"
|
||||||
|
description = "XEP-0490: Message Displayed Synchronization"
|
||||||
|
dependencies = {"xep_0060", "xep_0163", "xep_0223", "xep_0359"}
|
||||||
|
stanza = stanza
|
||||||
|
|
||||||
|
def plugin_init(self):
|
||||||
|
stanza.register_plugin()
|
||||||
|
self.xmpp.plugin["xep_0163"].register_pep(
|
||||||
|
"message_displayed_synchronization",
|
||||||
|
stanza.Displayed,
|
||||||
|
)
|
||||||
|
self.xmpp.plugin["xep_0223"].node_profiles[self.stanza.NS] = {
|
||||||
|
"pubsub#max_items": "max",
|
||||||
|
"pubsub#send_last_published_item": "never",
|
||||||
|
}
|
||||||
|
|
||||||
|
def flag_chat(self, chat: JidStr, stanza_id: str, **kwargs) -> Future[Iq]:
|
||||||
|
displayed = stanza.Displayed()
|
||||||
|
displayed["stanza_id"]["id"] = stanza_id
|
||||||
|
return self.xmpp.plugin["xep_0223"].store(
|
||||||
|
displayed, node=stanza.NS, id=str(chat), **kwargs
|
||||||
|
)
|
||||||
|
|
||||||
|
def catch_up(self, **kwargs):
|
||||||
|
return self.xmpp.plugin["xep_0060"].get_items(
|
||||||
|
self.xmpp.boundjid.bare, stanza.NS, **kwargs
|
||||||
|
)
|
17
slixmpp/plugins/xep_0490/stanza.py
Normal file
17
slixmpp/plugins/xep_0490/stanza.py
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
from slixmpp import register_stanza_plugin
|
||||||
|
from slixmpp.plugins.xep_0060.stanza import Item
|
||||||
|
from slixmpp.xmlstream import ElementBase
|
||||||
|
from slixmpp.plugins.xep_0359.stanza import StanzaID
|
||||||
|
|
||||||
|
NS = "urn:xmpp:mds:displayed:0"
|
||||||
|
|
||||||
|
|
||||||
|
class Displayed(ElementBase):
|
||||||
|
namespace = NS
|
||||||
|
name = "displayed"
|
||||||
|
plugin_attrib = "displayed"
|
||||||
|
|
||||||
|
|
||||||
|
def register_plugin():
|
||||||
|
register_stanza_plugin(Displayed, StanzaID)
|
||||||
|
register_stanza_plugin(Item, Displayed)
|
@ -103,6 +103,7 @@ from slixmpp.plugins.xep_0437 import XEP_0437
|
|||||||
from slixmpp.plugins.xep_0439 import XEP_0439
|
from slixmpp.plugins.xep_0439 import XEP_0439
|
||||||
from slixmpp.plugins.xep_0444 import XEP_0444
|
from slixmpp.plugins.xep_0444 import XEP_0444
|
||||||
from slixmpp.plugins.xep_0461 import XEP_0461
|
from slixmpp.plugins.xep_0461 import XEP_0461
|
||||||
|
from slixmpp.plugins.xep_0490 import XEP_0490
|
||||||
|
|
||||||
|
|
||||||
class PluginsDict(TypedDict):
|
class PluginsDict(TypedDict):
|
||||||
@ -199,3 +200,4 @@ class PluginsDict(TypedDict):
|
|||||||
xep_0439: XEP_0439
|
xep_0439: XEP_0439
|
||||||
xep_0444: XEP_0444
|
xep_0444: XEP_0444
|
||||||
xep_0461: XEP_0461
|
xep_0461: XEP_0461
|
||||||
|
xep_0490: XEP_0490
|
||||||
|
135
tests/test_stream_xep_0490.py
Normal file
135
tests/test_stream_xep_0490.py
Normal file
@ -0,0 +1,135 @@
|
|||||||
|
import unittest.mock
|
||||||
|
|
||||||
|
from slixmpp.test import SlixTest
|
||||||
|
# from slixmpp.plugins import xep_0490
|
||||||
|
|
||||||
|
|
||||||
|
class TestMessageDisplaySynchronization(SlixTest):
|
||||||
|
def setUp(self):
|
||||||
|
self.stream_start(jid="juliet@capulet.lit", plugins={"xep_0490"})
|
||||||
|
|
||||||
|
def test_catch_up(self):
|
||||||
|
future = self.xmpp.plugin["xep_0490"].catch_up()
|
||||||
|
self.send( # language=XML
|
||||||
|
"""
|
||||||
|
<iq type="get" to="juliet@capulet.lit" id="1">
|
||||||
|
<pubsub xmlns="http://jabber.org/protocol/pubsub">
|
||||||
|
<items node="urn:xmpp:mds:displayed:0" />
|
||||||
|
</pubsub>
|
||||||
|
</iq>
|
||||||
|
"""
|
||||||
|
)
|
||||||
|
self.recv( # language=XML
|
||||||
|
"""
|
||||||
|
<iq type='result'
|
||||||
|
to='juliet@capulet.lit/balcony'
|
||||||
|
id='1'>
|
||||||
|
<pubsub xmlns='http://jabber.org/protocol/pubsub'>
|
||||||
|
<items node='urn:xmpp:mds:displayed:0'>
|
||||||
|
<item id='romeo@montegue.lit'>
|
||||||
|
<displayed xmlns='urn:xmpp:mds:displayed:0'>
|
||||||
|
<stanza-id xmlns='urn:xmpp:sid:0'
|
||||||
|
id='0f710f2b-52ed-4d52-b928-784dad74a52b'
|
||||||
|
by='juliet@capulet.lit'/>
|
||||||
|
</displayed>
|
||||||
|
</item>
|
||||||
|
<item id='example@conference.shakespeare.lit'>
|
||||||
|
<displayed xmlns='urn:xmpp:mds:displayed:0'>
|
||||||
|
<stanza-id xmlns='urn:xmpp:sid:0'
|
||||||
|
id='ca21deaf-812c-48f1-8f16-339a674f2864'
|
||||||
|
by='example@conference.shakespeare.lit'/>
|
||||||
|
</displayed>
|
||||||
|
</item>
|
||||||
|
</items>
|
||||||
|
</pubsub>
|
||||||
|
</iq>
|
||||||
|
"""
|
||||||
|
)
|
||||||
|
iq = future.result()
|
||||||
|
item = list(iq["pubsub"]["items"])
|
||||||
|
self.assertEqual(item[0]["id"], "romeo@montegue.lit")
|
||||||
|
self.assertEqual(
|
||||||
|
item[0]["displayed"]["stanza_id"]["id"],
|
||||||
|
"0f710f2b-52ed-4d52-b928-784dad74a52b",
|
||||||
|
)
|
||||||
|
|
||||||
|
self.assertEqual(item[1]["id"], "example@conference.shakespeare.lit")
|
||||||
|
self.assertEqual(
|
||||||
|
item[1]["displayed"]["stanza_id"]["id"],
|
||||||
|
"ca21deaf-812c-48f1-8f16-339a674f2864",
|
||||||
|
)
|
||||||
|
|
||||||
|
def test_flag_chat(self):
|
||||||
|
self.xmpp.plugin["xep_0490"].flag_chat(
|
||||||
|
"romeo@montegue.lit", "0f710f2b-52ed-4d52-b928-784dad74a52b"
|
||||||
|
)
|
||||||
|
self.send( # language=XML
|
||||||
|
"""
|
||||||
|
<iq type='set' id='1'>
|
||||||
|
<pubsub xmlns='http://jabber.org/protocol/pubsub'>
|
||||||
|
<publish node='urn:xmpp:mds:displayed:0'>
|
||||||
|
<item id='romeo@montegue.lit'>
|
||||||
|
<displayed xmlns='urn:xmpp:mds:displayed:0'>
|
||||||
|
<stanza-id xmlns='urn:xmpp:sid:0'
|
||||||
|
id="0f710f2b-52ed-4d52-b928-784dad74a52b" />
|
||||||
|
</displayed>
|
||||||
|
</item>
|
||||||
|
</publish>
|
||||||
|
<publish-options>
|
||||||
|
<x xmlns='jabber:x:data' type='submit'>
|
||||||
|
<field var='FORM_TYPE' type='hidden'>
|
||||||
|
<value>http://jabber.org/protocol/pubsub#publish-options</value>
|
||||||
|
</field>
|
||||||
|
<field var='pubsub#persist_items'>
|
||||||
|
<value>1</value>
|
||||||
|
</field>
|
||||||
|
<field var='pubsub#max_items'>
|
||||||
|
<value>max</value>
|
||||||
|
</field>
|
||||||
|
<field var='pubsub#send_last_published_item'>
|
||||||
|
<value>never</value>
|
||||||
|
</field>
|
||||||
|
<field var='pubsub#access_model'>
|
||||||
|
<value>whitelist</value>
|
||||||
|
</field>
|
||||||
|
</x>
|
||||||
|
</publish-options>
|
||||||
|
</pubsub>
|
||||||
|
</iq>
|
||||||
|
""",
|
||||||
|
use_values=False,
|
||||||
|
)
|
||||||
|
|
||||||
|
def test_notification(self):
|
||||||
|
handler = unittest.mock.Mock()
|
||||||
|
|
||||||
|
self.xmpp.add_event_handler(
|
||||||
|
"message_displayed_synchronization_publish", handler
|
||||||
|
)
|
||||||
|
self.recv( # language=XML
|
||||||
|
"""
|
||||||
|
<message from='juliet@capulet.lit' to='juliet@capulet.lit/balcony' type='headline' id='new-displayed-pep-event'>
|
||||||
|
<event xmlns='http://jabber.org/protocol/pubsub#event'>
|
||||||
|
<items node='urn:xmpp:mds:displayed:0'>
|
||||||
|
<item id='romeo@montegue.lit'>
|
||||||
|
<displayed xmlns='urn:xmpp:mds:displayed:0'>
|
||||||
|
<stanza-id xmlns='urn:xmpp:sid:0' by='juliet@capulet.lit' id='0423e3a9-d516-493d-bb06-bee0e51ab9fb'/>
|
||||||
|
</displayed>
|
||||||
|
</item>
|
||||||
|
</items>
|
||||||
|
</event>
|
||||||
|
</message>
|
||||||
|
"""
|
||||||
|
)
|
||||||
|
handler.assert_called()
|
||||||
|
msg = handler.call_args[0][0]
|
||||||
|
self.assertEqual(
|
||||||
|
msg["pubsub_event"]["items"]["item"]["id"], "romeo@montegue.lit"
|
||||||
|
)
|
||||||
|
self.assertEqual(
|
||||||
|
msg["pubsub_event"]["items"]["item"]["displayed"]["stanza_id"]["id"],
|
||||||
|
"0423e3a9-d516-493d-bb06-bee0e51ab9fb",
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
suite = unittest.TestLoader().loadTestsFromTestCase(TestMessageDisplaySynchronization)
|
Loading…
Reference in New Issue
Block a user