diff options
Diffstat (limited to 'slixmpp')
-rw-r--r-- | slixmpp/plugins/__init__.py | 2 | ||||
-rw-r--r-- | slixmpp/plugins/protoxep_occupantid/__init__.py | 12 | ||||
-rw-r--r-- | slixmpp/plugins/protoxep_occupantid/occupantid.py | 23 | ||||
-rw-r--r-- | slixmpp/plugins/protoxep_occupantid/stanza.py | 16 | ||||
-rw-r--r-- | slixmpp/plugins/protoxep_reactions/__init__.py | 11 | ||||
-rw-r--r-- | slixmpp/plugins/protoxep_reactions/reactions.py | 54 | ||||
-rw-r--r-- | slixmpp/plugins/protoxep_reactions/stanza.py | 31 | ||||
-rw-r--r-- | slixmpp/test/slixtest.py | 7 | ||||
-rw-r--r-- | slixmpp/xmlstream/stanzabase.py | 8 | ||||
-rw-r--r-- | slixmpp/xmlstream/xmlstream.py | 19 |
10 files changed, 164 insertions, 19 deletions
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: |