diff --git a/slixmpp/plugins/xep_0045/__init__.py b/slixmpp/plugins/xep_0045/__init__.py new file mode 100644 index 00000000..eb13b018 --- /dev/null +++ b/slixmpp/plugins/xep_0045/__init__.py @@ -0,0 +1,14 @@ +""" + Slixmpp: The Slick XMPP Library + Copyright (C) 2020 "Maxime “pep” Buquet " + This file is part of Slixmpp. + + See the file LICENSE for copying permission. +""" + +from slixmpp.plugins import register_plugin +from slixmpp.plugins.xep_0045 import stanza +from slixmpp.plugins.xep_0045.muc import XEP_0045 +from slixmpp.plugins.xep_0045.stanza import MUCPresence, MUCMessage + +register_plugin(XEP_0045) diff --git a/slixmpp/plugins/xep_0045.py b/slixmpp/plugins/xep_0045/muc.py similarity index 81% rename from slixmpp/plugins/xep_0045.py rename to slixmpp/plugins/xep_0045/muc.py index dfbb3b58..364c47fd 100644 --- a/slixmpp/plugins/xep_0045.py +++ b/slixmpp/plugins/xep_0045/muc.py @@ -1,6 +1,7 @@ """ Slixmpp: The Slick XMPP Library Copyright (C) 2010 Nathanael C. Fritz + Copyright (C) 2020 "Maxime “pep” Buquet " This file is part of Slixmpp. See the file LICENSE for copying permission. @@ -10,105 +11,20 @@ from __future__ import with_statement import logging from slixmpp import Presence, Message -from slixmpp.plugins import BasePlugin, register_plugin -from slixmpp.xmlstream import register_stanza_plugin, ElementBase, JID, ET +from slixmpp.plugins import BasePlugin +from slixmpp.xmlstream import register_stanza_plugin, ET from slixmpp.xmlstream.handler.callback import Callback from slixmpp.xmlstream.matcher.xpath import MatchXPath from slixmpp.xmlstream.matcher.xmlmask import MatchXMLMask from slixmpp.exceptions import IqError, IqTimeout +from slixmpp.plugins.xep_0045 import stanza +from slixmpp.plugins.xep_0045.stanza import MUCPresence, MUCMessage + log = logging.getLogger(__name__) -class MUCPresence(ElementBase): - name = 'x' - namespace = 'http://jabber.org/protocol/muc#user' - plugin_attrib = 'muc' - interfaces = {'affiliation', 'role', 'jid', 'nick', 'room'} - affiliations = {'', } - roles = {'', } - - def get_item_attr(self, attr, default): - item = self.xml.find('{http://jabber.org/protocol/muc#user}item') - if item is None: - return default - return item.get(attr) - - def set_item_attr(self, attr, value): - item = self.xml.find('{http://jabber.org/protocol/muc#user}item') - if item is None: - item = ET.Element('{http://jabber.org/protocol/muc#user}item') - self.xml.append(item) - item.attrib[attr] = value - return item - - def del_item_attr(self, attr): - item = self.xml.find('{http://jabber.org/protocol/muc#user}item') - if item is not None and attr in item.attrib: - del item.attrib[attr] - - def get_affiliation(self): - return self.get_item_attr('affiliation', '') - - def set_affiliation(self, value): - self.set_item_attr('affiliation', value) - return self - - def del_affiliation(self): - # TODO: set default affiliation - self.del_item_attr('affiliation') - return self - - def get_jid(self): - return JID(self.get_item_attr('jid', '')) - - def set_jid(self, value): - if not isinstance(value, str): - value = str(value) - self.set_item_attr('jid', value) - return self - - def del_jid(self): - self.del_item_attr('jid') - return self - - def get_role(self): - return self.get_item_attr('role', '') - - def set_role(self, value): - # TODO: check for valid role - self.set_item_attr('role', value) - return self - - def del_role(self): - # TODO: set default role - self.del_item_attr('role') - return self - - def get_nick(self): - return self.parent()['from'].resource - - def get_room(self): - return self.parent()['from'].bare - - def set_nick(self, value): - log.warning("Cannot set nick through mucpresence plugin.") - return self - - def set_room(self, value): - log.warning("Cannot set room through mucpresence plugin.") - return self - - def del_nick(self): - log.warning("Cannot delete nick through mucpresence plugin.") - return self - - def del_room(self): - log.warning("Cannot delete room through mucpresence plugin.") - return self - - class XEP_0045(BasePlugin): """ @@ -118,6 +34,7 @@ class XEP_0045(BasePlugin): name = 'xep_0045' description = 'XEP-0045: Multi-User Chat' dependencies = {'xep_0030', 'xep_0004'} + stanza = stanza def plugin_init(self): self.rooms = {} @@ -125,6 +42,7 @@ class XEP_0045(BasePlugin): self.xep = '0045' # load MUC support in presence stanzas register_stanza_plugin(Presence, MUCPresence) + register_stanza_plugin(Message, MUCMessage) self.xmpp.register_handler(Callback('MUCPresence', MatchXMLMask("" % self.xmpp.default_ns), self.handle_groupchat_presence)) self.xmpp.register_handler(Callback('MUCError', MatchXMLMask("" % self.xmpp.default_ns), self.handle_groupchat_error_message)) self.xmpp.register_handler(Callback('MUCMessage', MatchXMLMask("" % self.xmpp.default_ns), self.handle_groupchat_message)) @@ -132,14 +50,14 @@ class XEP_0045(BasePlugin): self.xmpp.register_handler(Callback('MUCConfig', MatchXMLMask("" % self.xmpp.default_ns), self.handle_config_change)) self.xmpp.register_handler(Callback('MUCInvite', MatchXPath("{%s}message/{%s}x/{%s}invite" % ( self.xmpp.default_ns, - 'http://jabber.org/protocol/muc#user', - 'http://jabber.org/protocol/muc#user')), self.handle_groupchat_invite)) + stanza.NS_USER, + stanza.NS_USER)), self.handle_groupchat_invite)) def plugin_end(self): - self.xmpp.plugin['xep_0030'].del_feature(feature='http://jabber.org/protocol/muc') + self.xmpp.plugin['xep_0030'].del_feature(feature=stanza.NS) def session_bind(self, jid): - self.xmpp.plugin['xep_0030'].add_feature('http://jabber.org/protocol/muc') + self.xmpp.plugin['xep_0030'].add_feature(stanza.NS) def handle_groupchat_invite(self, inv): """ Handle an invite into a muc. @@ -417,6 +335,3 @@ class XEP_0045(BasePlugin): iq = cls.xmpp.Iq(sto=room, sfrom=ifrom, stype='get') iq.append(query) return iq.send() - - -register_plugin(XEP_0045) diff --git a/slixmpp/plugins/xep_0045/stanza.py b/slixmpp/plugins/xep_0045/stanza.py new file mode 100644 index 00000000..1550853f --- /dev/null +++ b/slixmpp/plugins/xep_0045/stanza.py @@ -0,0 +1,149 @@ +""" + Slixmpp: The Slick XMPP Library + Copyright (C) 2010 Nathanael C. Fritz + Copyright (C) 2020 "Maxime “pep” Buquet " + This file is part of Slixmpp. + + See the file LICENSE for copying permission. +""" + +import logging +from slixmpp.xmlstream import ElementBase, ET, JID + + +log = logging.getLogger(__name__) + +NS = 'http://jabber.org/protocol/muc' +NS_USER = 'http://jabber.org/protocol/muc#user' +NS_ADMIN = 'http://jabber.org/protocol/muc#admin' +NS_OWNER = 'http://jabber.org/protocol/muc#owner' + + +class MUCBase(ElementBase): + name = 'x' + namespace = NS_USER + plugin_attrib = 'muc' + interfaces = {'affiliation', 'role', 'jid', 'nick', 'room'} + + def get_item_attr(self, attr, default: str): + item = self.xml.find('{{{NS_USER}}}item') + if item is None: + return default + return item.get(attr) + + def set_item_attr(self, attr, value: str): + item = self.xml.find(f'{{{NS_USER}}}item') + if item is None: + item = ET.Element(f'{{{NS_USER}}}item') + self.xml.append(item) + item.attrib[attr] = value + return item + + def del_item_attr(self, attr): + item = self.xml.find('{{{NS_USER}}}item') + if item is not None and attr in item.attrib: + del item.attrib[attr] + + def get_affiliation(self): + return self.get_item_attr('affiliation', '') + + def set_affiliation(self, value): + self.set_item_attr('affiliation', value) + return self + + def del_affiliation(self): + # TODO: set default affiliation + self.del_item_attr('affiliation') + return self + + def get_jid(self): + return JID(self.get_item_attr('jid', '')) + + def set_jid(self, value): + if not isinstance(value, str): + value = str(value) + self.set_item_attr('jid', value) + return self + + def del_jid(self): + self.del_item_attr('jid') + return self + + def get_role(self): + return self.get_item_attr('role', '') + + def set_role(self, value): + # TODO: check for valid role + self.set_item_attr('role', value) + return self + + def del_role(self): + # TODO: set default role + self.del_item_attr('role') + return self + + def get_nick(self): + return self.parent()['from'].resource + + def get_room(self): + return self.parent()['from'].bare + + def set_nick(self, value): + log.warning( + "Cannot set nick through the %s plugin.", + self.__class__.__name__, + ) + return self + + def set_room(self, value): + log.warning( + "Cannot set room through the %s plugin.", + self.__class__.__name__, + ) + return self + + def del_nick(self): + log.warning( + "Cannot delete nick through the %s plugin.", + self.__class__.__name__, + ) + return self + + def del_room(self): + log.warning( + "Cannot delete room through the %s plugin.", + self.__class__.__name__, + ) + return self + + +class MUCPresence(MUCBase): + ''' + A MUC Presence + + + + + + + + ''' + + +class MUCMessage(MUCBase): + ''' + A MUC Message + + + Foo + + + + + '''