diff --git a/sleekxmpp/plugins/xep_0184/__init__.py b/sleekxmpp/plugins/xep_0184/__init__.py index 01de14aa..4b129b6b 100644 --- a/sleekxmpp/plugins/xep_0184/__init__.py +++ b/sleekxmpp/plugins/xep_0184/__init__.py @@ -9,7 +9,7 @@ from sleekxmpp.plugins.base import register_plugin from sleekxmpp.plugins.xep_0184.stanza import Request, Received -from sleekxmpp.plugins.xep_0184.reciept import XEP_0184 +from sleekxmpp.plugins.xep_0184.receipt import XEP_0184 register_plugin(XEP_0184) diff --git a/sleekxmpp/plugins/xep_0184/receipt.py b/sleekxmpp/plugins/xep_0184/receipt.py new file mode 100644 index 00000000..c0086b03 --- /dev/null +++ b/sleekxmpp/plugins/xep_0184/receipt.py @@ -0,0 +1,120 @@ +""" + SleekXMPP: The Sleek XMPP Library + Copyright (C) 2012 Erik Reuterborg Larsson, Nathanael C. Fritz + This file is part of SleekXMPP. + + See the file LICENSE for copying permission. +""" + +import logging + +from sleekxmpp.stanza import Message +from sleekxmpp.xmlstream import register_stanza_plugin +from sleekxmpp.xmlstream.handler import Callback +from sleekxmpp.xmlstream.matcher import StanzaPath +from sleekxmpp.plugins import BasePlugin +from sleekxmpp.plugins.xep_0184 import stanza, Request, Received + + +class XEP_0184(BasePlugin): + + """ + XEP-0184: Message Delivery Receipts + """ + + name = 'xep_0184' + description = 'XEP-0184: Message Delivery Receipts' + dependencies = set(['xep_0030']) + stanza = stanza + + ack_types = ('normal', 'chat', 'headline') + + def plugin_init(self): + self.auto_ack = self.config.get('auto_ack', True) + self.auto_request = self.config.get('auto_request', False) + + register_stanza_plugin(Message, Request) + register_stanza_plugin(Message, Received) + + self.xmpp.add_filter('out', self._filter_add_receipt_request) + + self.xmpp.register_handler( + Callback('Message Receipt', + StanzaPath('message/receipt'), + self._handle_receipt_received)) + + self.xmpp.register_handler( + Callback('Message Receipt Request', + StanzaPath('message/request_receipt'), + self._handle_receipt_request)) + + self.xmpp['xep_0030'].add_feature('urn:xmpp:receipts') + + def ack(self, msg): + """ + Acknowledge a message by sending a receipt. + + Arguments: + msg -- The message to acknowledge. + """ + ack = self.xmpp.Message() + ack['to'] = msg['from'] + ack['from'] = msg['to'] + ack['receipt'] = msg['id'] + ack['id'] = self.xmpp.new_id() + ack.send() + + def _handle_receipt_received(self, msg): + self.xmpp.event('receipt_received', msg) + + def _handle_receipt_request(self, msg): + """ + Auto-ack message receipt requests if ``self.auto_ack`` is ``True``. + + Arguments: + msg -- The incoming message requesting a receipt. + """ + if self.auto_ack: + if msg['type'] in self.ack_types: + if not msg['receipt']: + self.ack(msg) + + def _filter_add_receipt_request(self, stanza): + """ + Auto add receipt requests to outgoing messages, if: + + - ``self.auto_request`` is set to ``True`` + - The message is not for groupchat + - The message does not contain a receipt acknowledgment + - The recipient is a bare JID or, if a full JID, one + that has the ``urn:xmpp:receipts`` feature enabled + + The disco cache is checked if a full JID is specified in + the outgoing message, which may mean a round-trip disco#info + delay for the first message sent to the JID if entity caps + are not used. + """ + + if not self.auto_request: + return stanza + + if not isinstance(stanza, Message): + return stanza + + if stanza['request_receipt']: + return stanza + + if not stanza['type'] in self.ack_types: + return stanza + + if stanza['receipt']: + return stanza + + if stanza['to'].resource: + if not self.xmpp['xep_0030'].supports(stanza['to'], + feature='urn:xmpp:receipts', + cached=True): + return stanza + + stanza['request_receipt'] = True + return stanza diff --git a/sleekxmpp/plugins/xep_0184/reciept.py b/sleekxmpp/plugins/xep_0184/reciept.py deleted file mode 100644 index fd3f24e7..00000000 --- a/sleekxmpp/plugins/xep_0184/reciept.py +++ /dev/null @@ -1,45 +0,0 @@ -""" - SleekXMPP: The Sleek XMPP Library - Copyright (C) 2012 Erik Reuterborg Larsson, Nathanael C. Fritz - This file is part of SleekXMPP. - - See the file LICENSE for copying permission. -""" - -from sleekxmpp.stanza import Message -from sleekxmpp.xmlstream import register_stanza_plugin -from sleekxmpp.plugins import BasePlugin -from sleekxmpp.plugins.xep_0184 import stanza, Request, Received - - -class XEP_0184(BasePlugin): - - """ - XEP-0184: Message Delivery Receipts - """ - - name = 'xep_0184' - description = 'XEP-0184: Message Delivery Receipts' - dependencies = set(['xep_0030']) - stanza = stanza - - def plugin_init(self): - register_stanza_plugin(Message, Request) - register_stanza_plugin(Message, Received) - - self.xmpp.plugin['xep_0030'].add_feature('urn:xmpp:receipts') - - def ack(self, message): - """ - Acknowledges a message - - Arguments: - message -- The message to acknowledge. - """ - mto = message['to'] - mfrom = message['from'] - mid = message['id'] - msg = self.xmpp.make_message(mto=mfrom, mfrom=mto) - msg['reciept_received']['id'] = mid - msg['id'] = self.xmpp.new_id() - msg.send() diff --git a/sleekxmpp/plugins/xep_0184/stanza.py b/sleekxmpp/plugins/xep_0184/stanza.py index 42ba8c3f..d1654ef8 100644 --- a/sleekxmpp/plugins/xep_0184/stanza.py +++ b/sleekxmpp/plugins/xep_0184/stanza.py @@ -12,34 +12,58 @@ from sleekxmpp.xmlstream.stanzabase import ElementBase, ET class Request(ElementBase): namespace = 'urn:xmpp:receipts' name = 'request' - plugin_attrib = 'request_reciept' - interfaces = set(('request_reciept',)) + plugin_attrib = 'request_receipt' + interfaces = set(('request_receipt',)) + sub_interfaces = interfaces is_extension = True def setup(self, xml=None): self.xml = ET.Element('') return True - def set_request_reciept(self, val): - self.del_request_reciept() - parent = self.parent() + def set_request_receipt(self, val): + self.del_request_receipt() if val: - self.xml = ET.Element("{%s}%s" % (self.namespace, self.name)) - parent.append(self.xml) + parent = self.parent() + parent._set_sub_text("{%s}request" % self.namespace, keep=True) - def get_request_reciept(self): + def get_request_receipt(self): parent = self.parent() - if parent.find("{%s}%s" % (self.namespace, self.name)) is not None: + if parent.find("{%s}request" % self.namespace) is not None: return True else: return False - def del_request_reciept(self): - self.xml = ET.Element('') + def del_request_receipt(self): + self.parent()._del_sub("{%s}request" % self.namespace) class Received(ElementBase): namespace = 'urn:xmpp:receipts' name = 'received' - plugin_attrib = 'reciept_received' - interfaces = set(('id',)) + plugin_attrib = 'receipt' + interfaces = set(['receipt']) + sub_interfaces = interfaces + is_extension = True + + def setup(self, xml=None): + self.xml = ET.Element('') + return True + + def set_receipt(self, value): + self.del_receipt() + if value: + parent = self.parent() + xml = ET.Element("{%s}received" % self.namespace) + xml.attrib['id'] = value + parent.append(xml) + + def get_receipt(self): + parent = self.parent() + xml = parent.find("{%s}received" % self.namespace) + if xml is not None: + return xml.attrib.get('id', '') + return '' + + def del_receipt(self): + self.parent()._del_sub('{%s}received' % self.namespace) diff --git a/tests/test_stanza_xep_0184.py b/tests/test_stanza_xep_0184.py index e3668d3a..13472373 100644 --- a/tests/test_stanza_xep_0184.py +++ b/tests/test_stanza_xep_0184.py @@ -9,21 +9,30 @@ class TestReciept(SleekTest): register_stanza_plugin(Message, xep_0184.Received) def testCreateRequest(self): - request = """""" + request = """ + + + + """ msg = self.Message() - self.assertEqual(msg['request_reciept'], False) + self.assertEqual(msg['request_receipt'], False) - msg['request_reciept'] = True - self.check(msg, request, use_values=False) + msg['request_receipt'] = True + self.check(msg, request) def testCreateReceived(self): - received = """""" + received = """ + + + + """ msg = self.Message() - msg['reciept_received']['id'] = '1' + msg['receipt'] = '1' self.check(msg, received) + suite = unittest.TestLoader().loadTestsFromTestCase(TestReciept)