Expand support for XEP-0184.
New stanza interfaces:
    Adding a message receipt request:
        msg['request_receipt'] = True
    Adding a message receipt:
        msg['receipt'] = '123-24234'
    Retrieving the acked message ID:
        ack_id = msg['receipt']
        print(ack_id)
        '123-24234'
New configuration options:
    auto_ack:
        If True, auto reply to messages that request receipts.
        Defaults to True
    auto_request:
        If True, auto add receipt requests to appropriate outgoing
        messages.
        Defaults to False
			
			
This commit is contained in:
		| @@ -9,7 +9,7 @@ | |||||||
| from sleekxmpp.plugins.base import register_plugin | from sleekxmpp.plugins.base import register_plugin | ||||||
|  |  | ||||||
| from sleekxmpp.plugins.xep_0184.stanza import Request, Received | 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) | register_plugin(XEP_0184) | ||||||
|   | |||||||
							
								
								
									
										120
									
								
								sleekxmpp/plugins/xep_0184/receipt.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										120
									
								
								sleekxmpp/plugins/xep_0184/receipt.py
									
									
									
									
									
										Normal file
									
								
							| @@ -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 | ||||||
| @@ -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() |  | ||||||
| @@ -12,34 +12,58 @@ from sleekxmpp.xmlstream.stanzabase import ElementBase, ET | |||||||
| class Request(ElementBase): | class Request(ElementBase): | ||||||
|     namespace = 'urn:xmpp:receipts' |     namespace = 'urn:xmpp:receipts' | ||||||
|     name = 'request' |     name = 'request' | ||||||
|     plugin_attrib = 'request_reciept' |     plugin_attrib = 'request_receipt' | ||||||
|     interfaces = set(('request_reciept',)) |     interfaces = set(('request_receipt',)) | ||||||
|  |     sub_interfaces = interfaces | ||||||
|     is_extension = True |     is_extension = True | ||||||
|  |  | ||||||
|     def setup(self, xml=None): |     def setup(self, xml=None): | ||||||
|         self.xml = ET.Element('') |         self.xml = ET.Element('') | ||||||
|         return True |         return True | ||||||
|  |  | ||||||
|     def set_request_reciept(self, val): |     def set_request_receipt(self, val): | ||||||
|         self.del_request_reciept() |         self.del_request_receipt() | ||||||
|         parent = self.parent() |  | ||||||
|         if val: |         if val: | ||||||
|             self.xml = ET.Element("{%s}%s" % (self.namespace, self.name)) |  | ||||||
|             parent.append(self.xml) |  | ||||||
|  |  | ||||||
|     def get_request_reciept(self): |  | ||||||
|             parent = self.parent() |             parent = self.parent() | ||||||
|         if parent.find("{%s}%s" % (self.namespace, self.name)) is not None: |             parent._set_sub_text("{%s}request" % self.namespace, keep=True) | ||||||
|  |  | ||||||
|  |     def get_request_receipt(self): | ||||||
|  |         parent = self.parent() | ||||||
|  |         if parent.find("{%s}request" % self.namespace) is not None: | ||||||
|             return True |             return True | ||||||
|         else: |         else: | ||||||
|             return False |             return False | ||||||
|  |  | ||||||
|     def del_request_reciept(self): |     def del_request_receipt(self): | ||||||
|         self.xml = ET.Element('') |         self.parent()._del_sub("{%s}request" % self.namespace) | ||||||
|  |  | ||||||
|  |  | ||||||
| class Received(ElementBase): | class Received(ElementBase): | ||||||
|     namespace = 'urn:xmpp:receipts' |     namespace = 'urn:xmpp:receipts' | ||||||
|     name = 'received' |     name = 'received' | ||||||
|     plugin_attrib = 'reciept_received' |     plugin_attrib = 'receipt' | ||||||
|     interfaces = set(('id',)) |     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) | ||||||
|   | |||||||
| @@ -9,21 +9,30 @@ class TestReciept(SleekTest): | |||||||
|         register_stanza_plugin(Message, xep_0184.Received) |         register_stanza_plugin(Message, xep_0184.Received) | ||||||
|  |  | ||||||
|     def testCreateRequest(self): |     def testCreateRequest(self): | ||||||
|         request = """<message><request xmlns="urn:xmpp:receipts" /></message>""" |         request = """ | ||||||
|  |           <message> | ||||||
|  |             <request xmlns="urn:xmpp:receipts" /> | ||||||
|  |           </message> | ||||||
|  |         """ | ||||||
|  |  | ||||||
|         msg = self.Message() |         msg = self.Message() | ||||||
|  |  | ||||||
|         self.assertEqual(msg['request_reciept'], False) |         self.assertEqual(msg['request_receipt'], False) | ||||||
|  |  | ||||||
|         msg['request_reciept'] = True |         msg['request_receipt'] = True | ||||||
|         self.check(msg, request, use_values=False) |         self.check(msg, request) | ||||||
|  |  | ||||||
|     def testCreateReceived(self): |     def testCreateReceived(self): | ||||||
|         received = """<message><received xmlns="urn:xmpp:receipts" id="1"/></message>""" |         received = """ | ||||||
|  |           <message> | ||||||
|  |             <received xmlns="urn:xmpp:receipts" id="1" /> | ||||||
|  |           </message> | ||||||
|  |         """ | ||||||
|  |  | ||||||
|         msg = self.Message() |         msg = self.Message() | ||||||
|         msg['reciept_received']['id'] = '1' |  | ||||||
|  |  | ||||||
|  |         msg['receipt'] = '1' | ||||||
|         self.check(msg, received) |         self.check(msg, received) | ||||||
|  |  | ||||||
|  |  | ||||||
| suite = unittest.TestLoader().loadTestsFromTestCase(TestReciept) | suite = unittest.TestLoader().loadTestsFromTestCase(TestReciept) | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user
	 Lance Stout
					Lance Stout