summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--docs/getting_started/sendlogout.rst14
-rw-r--r--slixmpp/plugins/__init__.py2
-rw-r--r--slixmpp/plugins/protoxep_occupantid/__init__.py12
-rw-r--r--slixmpp/plugins/protoxep_occupantid/occupantid.py23
-rw-r--r--slixmpp/plugins/protoxep_occupantid/stanza.py16
-rw-r--r--slixmpp/plugins/protoxep_reactions/__init__.py11
-rw-r--r--slixmpp/plugins/protoxep_reactions/reactions.py54
-rw-r--r--slixmpp/plugins/protoxep_reactions/stanza.py31
-rw-r--r--slixmpp/test/slixtest.py7
-rw-r--r--slixmpp/xmlstream/stanzabase.py8
-rw-r--r--slixmpp/xmlstream/xmlstream.py19
-rw-r--r--tests/test_stanza_base.py8
12 files changed, 171 insertions, 34 deletions
diff --git a/docs/getting_started/sendlogout.rst b/docs/getting_started/sendlogout.rst
index a27976c5..3b5d6d5a 100644
--- a/docs/getting_started/sendlogout.rst
+++ b/docs/getting_started/sendlogout.rst
@@ -47,11 +47,11 @@ the roster. Next, we want to send our message, and to do that we will use :meth:
self.send_message(mto=self.recipient, mbody=self.msg)
Finally, we need to disconnect the client using :meth:`disconnect <slixmpp.xmlstream.XMLStream.disconnect>`.
-Now, sent stanzas are placed in a queue to pass them to the send thread. If we were to call
-:meth:`disconnect <slixmpp.xmlstream.XMLStream.disconnect>` without any parameters, then it is possible
-for the client to disconnect before the send queue is processed and the message is actually
-sent on the wire. To ensure that our message is processed, we use
-:meth:`disconnect(wait=True) <slixmpp.xmlstream.XMLStream.disconnect>`.
+Now, sent stanzas are placed in a queue to pass them to the send thread.
+:meth:`disconnect <slixmpp.xmlstream.XMLStream.disconnect>` by default will wait for an
+acknowledgement from the server for at least `2.0` seconds. This time is configurable with
+the `wait` parameter. If `0.0` is passed for `wait`, :meth:`disconnect
+<slixmpp.xmlstream.XMLStream.disconnect>` will not close the connection gracefully.
.. code-block:: python
@@ -61,12 +61,12 @@ sent on the wire. To ensure that our message is processed, we use
self.send_message(mto=self.recipient, mbody=self.msg)
- self.disconnect(wait=True)
+ self.disconnect()
.. warning::
If you happen to be adding stanzas to the send queue faster than the send thread
- can process them, then :meth:`disconnect(wait=True) <slixmpp.xmlstream.XMLStream.disconnect>`
+ can process them, then :meth:`disconnect() <slixmpp.xmlstream.XMLStream.disconnect>`
will block and not disconnect.
Final Product
diff --git a/slixmpp/plugins/__init__.py b/slixmpp/plugins/__init__.py
index d28cf281..716b15c3 100644
--- a/slixmpp/plugins/__init__.py
+++ b/slixmpp/plugins/__init__.py
@@ -85,4 +85,6 @@ __all__ = [
'xep_0323', # IoT Systems Sensor Data
'xep_0325', # IoT Systems Control
'xep_0332', # HTTP Over XMPP Transport
+ 'protoxep_reactions', # https://dino.im/xeps/reactions.html
+ 'protoxep_occupantid', # https://dino.im/xeps/occupant-id.html
]
diff --git a/slixmpp/plugins/protoxep_occupantid/__init__.py b/slixmpp/plugins/protoxep_occupantid/__init__.py
new file mode 100644
index 00000000..1bd374b6
--- /dev/null
+++ b/slixmpp/plugins/protoxep_occupantid/__init__.py
@@ -0,0 +1,12 @@
+"""
+ Slixmpp: The Slick XMPP Library
+ Copyright (C) 2019 Mathieu Pasquet
+ This file is part of Slixmpp.
+
+ See the file LICENSE for copying permission.
+"""
+from slixmpp.plugins.base import register_plugin
+from slixmpp.plugins.protoxep_occupantid.occupantid import XEP_OccupantID
+from slixmpp.plugins.protoxep_occupantid.stanza import OccupantID
+
+register_plugin(XEP_OccupantID)
diff --git a/slixmpp/plugins/protoxep_occupantid/occupantid.py b/slixmpp/plugins/protoxep_occupantid/occupantid.py
new file mode 100644
index 00000000..7f4a9d4a
--- /dev/null
+++ b/slixmpp/plugins/protoxep_occupantid/occupantid.py
@@ -0,0 +1,23 @@
+"""
+ Slixmpp: The Slick XMPP Library
+ Copyright (C) 2019 Mathieu Pasquet
+ This file is part of Slixmpp.
+
+ See the file LICENSE for copying permission.
+"""
+from slixmpp.plugins import BasePlugin
+from slixmpp.stanza import Message, Presence
+from slixmpp.xmlstream import register_stanza_plugin
+
+from slixmpp.plugins.protoxep_occupantid import stanza
+
+
+class XEP_OccupantID(BasePlugin):
+ name = 'protoxep_occupantid'
+ description = 'XEP-XXXX: Anonymous unique occupant identifiers for MUCs'
+ dependencies = set()
+ stanza = stanza
+
+ def plugin_init(self):
+ register_stanza_plugin(Message, stanza.OccupantID)
+ register_stanza_plugin(Presence, stanza.OccupantID)
diff --git a/slixmpp/plugins/protoxep_occupantid/stanza.py b/slixmpp/plugins/protoxep_occupantid/stanza.py
new file mode 100644
index 00000000..e5853111
--- /dev/null
+++ b/slixmpp/plugins/protoxep_occupantid/stanza.py
@@ -0,0 +1,16 @@
+"""
+ Slixmpp: The Slick XMPP Library
+ Copyright (C) 2019 Mathieu Pasquet
+ This file is part of Slixmpp.
+
+ See the file LICENSE for copying permission.
+"""
+
+from slixmpp.xmlstream import ElementBase
+
+
+class OccupantID(ElementBase):
+ name = 'occupant-id'
+ plugin_attrib = 'occupant-id'
+ namespace = 'urn:xmpp:occupant-id:0'
+ interfaces = {'id'}
diff --git a/slixmpp/plugins/protoxep_reactions/__init__.py b/slixmpp/plugins/protoxep_reactions/__init__.py
new file mode 100644
index 00000000..e107bd16
--- /dev/null
+++ b/slixmpp/plugins/protoxep_reactions/__init__.py
@@ -0,0 +1,11 @@
+"""
+ Slixmpp: The Slick XMPP Library
+ Copyright (C) 2019 Mathieu Pasquet
+ This file is part of Slixmpp.
+
+ See the file LICENSE for copying permission.
+"""
+from slixmpp.plugins.base import register_plugin
+from slixmpp.plugins.protoxep_reactions.reactions import XEP_Reactions
+
+register_plugin(XEP_Reactions)
diff --git a/slixmpp/plugins/protoxep_reactions/reactions.py b/slixmpp/plugins/protoxep_reactions/reactions.py
new file mode 100644
index 00000000..e7af8fcb
--- /dev/null
+++ b/slixmpp/plugins/protoxep_reactions/reactions.py
@@ -0,0 +1,54 @@
+"""
+ Slixmpp: The Slick XMPP Library
+ Copyright (C) 2019 Mathieu Pasquet
+ This file is part of Slixmpp.
+
+ See the file LICENSE for copying permission.
+"""
+from typing import Iterable
+
+from slixmpp.plugins import BasePlugin
+from slixmpp.stanza import Message
+from slixmpp.xmlstream import register_stanza_plugin
+from slixmpp.xmlstream.matcher import MatchXMLMask
+from slixmpp.xmlstream.handler import Callback
+
+from slixmpp.plugins.protoxep_reactions import stanza
+
+
+class XEP_Reactions(BasePlugin):
+ name = 'protoxep_reactions'
+ description = 'XEP-XXXX: Message Reactions'
+ dependencies = {'xep_0030'}
+ stanza = stanza
+
+ def plugin_init(self):
+ self.xmpp.register_handler(
+ Callback(
+ 'Reaction received',
+ MatchXMLMask('<message><reactions xmlns="urn:xmpp:reactions:0"/></message>'),
+ self._handle_reactions,
+ )
+ )
+ self.xmpp['xep_0030'].add_feature('urn:xmpp:reactions:0')
+ register_stanza_plugin(Message, stanza.Reactions)
+
+ def plugin_end(self):
+ self.xmpp.remove_handler('Reaction received')
+ self.xmpp['xep_0030'].remove_feature('urn:xmpp:reactions:0')
+
+ def _handle_reactions(self, message: Message):
+ self.xmpp.event('reactions', message)
+
+ @staticmethod
+ def set_reactions(message: Message, to_id: str, reactions: Iterable[str]):
+ """
+ Add reactions to a Message object.
+ """
+ reactions_stanza = stanza.Reactions()
+ reactions_stanza['to'] = to_id
+ for reaction in reactions:
+ reaction_stanza = stanza.Reaction()
+ reaction_stanza['value'] = reaction
+ reactions_stanza.append(reaction_stanza)
+ message.append(reactions_stanza)
diff --git a/slixmpp/plugins/protoxep_reactions/stanza.py b/slixmpp/plugins/protoxep_reactions/stanza.py
new file mode 100644
index 00000000..45414a37
--- /dev/null
+++ b/slixmpp/plugins/protoxep_reactions/stanza.py
@@ -0,0 +1,31 @@
+"""
+ Slixmpp: The Slick XMPP Library
+ Copyright (C) 2019 Mathieu Pasquet
+ This file is part of Slixmpp.
+
+ See the file LICENSE for copying permission.
+"""
+
+from slixmpp.xmlstream import ElementBase, register_stanza_plugin
+
+
+class Reactions(ElementBase):
+ name = 'reactions'
+ plugin_attrib = 'reactions'
+ namespace = 'urn:xmpp:reactions:0'
+ interfaces = {'to'}
+
+
+class Reaction(ElementBase):
+ name = 'reaction'
+ namespace = 'urn:xmpp:reactions:0'
+ interfaces = {'value'}
+
+ def get_value(self) -> str:
+ return self.xml.text
+
+ def set_value(self, value: str):
+ self.xml.text = value
+
+
+register_stanza_plugin(Reactions, Reaction, iterable=True)
diff --git a/slixmpp/test/slixtest.py b/slixmpp/test/slixtest.py
index 3953d77d..802df73c 100644
--- a/slixmpp/test/slixtest.py
+++ b/slixmpp/test/slixtest.py
@@ -340,6 +340,13 @@ class SlixTest(unittest.TestCase):
self.xmpp.default_lang = None
self.xmpp.peer_default_lang = None
+ def new_id():
+ self.xmpp._id += 1
+ return str(self.xmpp._id)
+
+ self.xmpp._id = 0
+ self.xmpp.new_id = new_id
+
# Must have the stream header ready for xmpp.process() to work.
if not header:
header = self.xmpp.stream_header
diff --git a/slixmpp/xmlstream/stanzabase.py b/slixmpp/xmlstream/stanzabase.py
index 1c000b69..3e45f613 100644
--- a/slixmpp/xmlstream/stanzabase.py
+++ b/slixmpp/xmlstream/stanzabase.py
@@ -1374,14 +1374,6 @@ class StanzaBase(ElementBase):
#: The default XMPP client namespace
namespace = 'jabber:client'
- #: There is a small set of attributes which apply to all XMPP stanzas:
- #: the stanza type, the to and from JIDs, the stanza ID, and, especially
- #: in the case of an Iq stanza, a payload.
- interfaces = {'type', 'to', 'from', 'id', 'payload'}
-
- #: A basic set of allowed values for the ``'type'`` interface.
- types = {'get', 'set', 'error', None, 'unavailable', 'normal', 'chat'}
-
def __init__(self, stream=None, xml=None, stype=None,
sto=None, sfrom=None, sid=None, parent=None):
self.stream = stream
diff --git a/slixmpp/xmlstream/xmlstream.py b/slixmpp/xmlstream/xmlstream.py
index 98b0744c..9f6f3083 100644
--- a/slixmpp/xmlstream/xmlstream.py
+++ b/slixmpp/xmlstream/xmlstream.py
@@ -201,11 +201,6 @@ class XMLStream(asyncio.BaseProtocol):
self.__event_handlers = {}
self.__filters = {'in': [], 'out': [], 'out_sync': []}
- self._id = 0
-
- #: We use an ID prefix to ensure that all ID values are unique.
- self._id_prefix = '%s-' % uuid.uuid4()
-
# Current connection attempt (Future)
self._current_connection_attempt = None
@@ -243,12 +238,7 @@ class XMLStream(asyncio.BaseProtocol):
ID values. Using this method ensures that all new ID values
are unique in this stream.
"""
- self._id += 1
- return self.get_id()
-
- def get_id(self):
- """Return the current unique stream ID in hexadecimal form."""
- return "%s%X" % (self._id_prefix, self._id)
+ return uuid.uuid4().hex
def connect(self, host='', port=0, use_ssl=False,
force_starttls=True, disable_starttls=False):
@@ -478,6 +468,13 @@ class XMLStream(asyncio.BaseProtocol):
:param wait: Time to wait for a response from the server.
"""
+ # Compat: docs/getting_started/sendlogout.rst has been promoting
+ # `disconnect(wait=True)` for ages. This doesn't mean anything to the
+ # schedule call below. It would fortunately be converted to `1` later
+ # down the call chain. Praise the implicit casts lord.
+ if wait == True:
+ wait = 2.0
+
self.disconnect_reason = reason
self.cancel_connection_attempt()
if self.transport:
diff --git a/tests/test_stanza_base.py b/tests/test_stanza_base.py
index 35fa5e99..55554aa9 100644
--- a/tests/test_stanza_base.py
+++ b/tests/test_stanza_base.py
@@ -68,13 +68,5 @@ class TestStanzaBase(SlixTest):
self.assertTrue(stanza['payload'] == [],
"Stanza reply did not empty stanza payload.")
- def testError(self):
- """Test marking a stanza as an error."""
- stanza = StanzaBase()
- stanza['type'] = 'get'
- stanza.error()
- self.assertTrue(stanza['type'] == 'error',
- "Stanza type is not 'error' after calling error()")
-
suite = unittest.TestLoader().loadTestsFromTestCase(TestStanzaBase)