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:
Lance Stout 2012-03-16 10:51:25 -07:00
parent 96ff2d43c0
commit 58e0f1e6c3
5 changed files with 173 additions and 65 deletions

View file

@ -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)

View 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

View file

@ -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()

View file

@ -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)

View file

@ -9,21 +9,30 @@ class TestReciept(SleekTest):
register_stanza_plugin(Message, xep_0184.Received)
def testCreateRequest(self):
request = """<message><request xmlns="urn:xmpp:receipts" /></message>"""
request = """
<message>
<request xmlns="urn:xmpp:receipts" />
</message>
"""
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 = """<message><received xmlns="urn:xmpp:receipts" id="1"/></message>"""
received = """
<message>
<received xmlns="urn:xmpp:receipts" id="1" />
</message>
"""
msg = self.Message()
msg['reciept_received']['id'] = '1'
msg['receipt'] = '1'
self.check(msg, received)
suite = unittest.TestLoader().loadTestsFromTestCase(TestReciept)