plugin XEP-0060: MAM integration:

- removed useless XEP-0059 recommendation
    - added XEP-0313 as a recommended plugin
    - parseExtra now manage mam_* keys (mam_filter_[name] to use a filter)
    - if a mam request is in 'mam' key of extra parameted in getItems, a MAM request is done, but the result is returned in the same way as for normal getItems, making like more easy for other plugins and for frontends
This commit is contained in:
Goffi 2016-01-05 23:20:22 +01:00
commit b3428838cc

View file

@ -28,15 +28,16 @@ from sat.tools import sat_defer
from twisted.words.protocols.jabber import jid, error
from twisted.internet import defer
from wokkel import disco
# XXX: tmp.pubsub is actually use instead of wokkel version
# same thing for rsm
from wokkel import pubsub
from wokkel import rsm
from wokkel import data_form
from zope.interface import implements
from collections import namedtuple
UNSPECIFIED = "unspecified error"
import datetime
from dateutil import tz
# XXX: tmp.wokkel.pubsub is actually use instead of wokkel version
# mam and rsm come from tmp.wokkel too
from wokkel import pubsub
from wokkel import rsm
from wokkel import mam
PLUGIN_INFO = {
@ -45,12 +46,15 @@ PLUGIN_INFO = {
"type": "XEP",
"protocols": ["XEP-0060"],
"dependencies": [],
"recommendations": ["XEP-0059"],
"recommendations": ["XEP-0313"],
"main": "XEP_0060",
"handler": "yes",
"description": _("""Implementation of PubSub Protocol""")
}
UNSPECIFIED = "unspecified error"
MAM_FILTER = "mam_filter_"
Extra = namedtuple('Extra', ('rsm_request', 'extra'))
# rsm_request is the rsm.RSMRequest build with rsm_ prefixed keys, or None
@ -77,6 +81,7 @@ class XEP_0060(object):
def __init__(self, host):
log.info(_(u"PubSub plugin initialization"))
self.host = host
self._mam = host.plugins.get('XEP-0313')
self._node_cb = {} # dictionnary of callbacks for node (key: node, value: list of callbacks)
self.rt_sessions = sat_defer.RTDeferredSessions()
host.bridge.addMethod("psDeleteNode", ".plugin", in_sign='sss', out_sign='', method=self._deleteNode, async=True)
@ -125,15 +130,46 @@ class XEP_0060(object):
@return(Extra): filled Extra instance
"""
if extra is not None:
rsm_dict = { key[4:]: value for key, value in extra.iteritems() if key.startswith('rsm_') }
if rsm_dict:
# rsm
rsm_args = {}
for arg in ('max', 'after', 'before', 'index'):
try:
rsm_dict['max_'] = rsm_dict.pop('max')
argname = "max_" if arg == 'max' else arg
rsm_args[argname] = extra.pop('rsm_{}'.format(arg))
except KeyError:
pass
rsm_request = rsm.RSMRequest(**rsm_dict)
continue
if rsm_args:
rsm_request = rsm.RSMRequest(**rsm_args)
else:
rsm_request = None
# mam
mam_args = {}
for arg in ('start', 'end'):
try:
mam_args[arg] = datetime.datetime.fromtimestamp(int(extra.pop('{}{}'.format(MAM_FILTER, arg))), tz.tzutc())
except (TypeError, ValueError):
log.warning(u"Bad value for {} filter".format(arg))
except KeyError:
continue
try:
mam_args['with_jid'] = jid.JID(extra.pop('{}jid'.format(MAM_FILTER)))
except (jid.InvalidFormat):
log.warning(u"Bad value for jid filter")
except KeyError:
pass
for name, value in extra:
if name.startswith(MAM_FILTER):
var = name[len[MAM_FILTER]:]
extra_fields = mam_args.setdefault('extra_fields', [])
extra_fields.append(data_form.Field(var=var, value=value))
if mam_args:
assert 'mam' not in extra
extra['mam'] = mam.MAMRequest(mam.buildForm(**mam_args))
else:
rsm_request = None
extra = {}
@ -218,6 +254,18 @@ class XEP_0060(object):
client = self.host.getClient(profile_key)
return client.pubsub_client.publish(service, nodeIdentifier, items, client.pubsub_client.parent.jid)
def _unwrapMAMMessage(self, message_elt):
try:
item_elt = (message_elt.elements(mam.NS_MAM, 'result').next()
.elements(C.NS_FORWARD, 'forwarded').next()
.elements(C.NS_CLIENT, 'message').next()
.elements('http://jabber.org/protocol/pubsub#event', 'event').next()
.elements('http://jabber.org/protocol/pubsub#event', 'items').next()
.elements('http://jabber.org/protocol/pubsub#event', 'item').next())
except StopIteration:
raise exceptions.DataError(u"Can't find Item in MAM message element")
return item_elt
def getItems(self, service, node, max_items=None, item_ids=None, sub_id=None, rsm_request=None, extra=None, profile_key=C.PROF_KEY_NONE):
"""Retrieve pubsub items from a node.
@ -238,7 +286,28 @@ class XEP_0060(object):
if extra is None:
extra = {}
client = self.host.getClient(profile_key)
d = client.pubsub_client.items(service, node, max_items, item_ids, sub_id, client.pubsub_client.parent.jid, rsm_request)
try:
mam_query = extra['mam']
except KeyError:
d = client.pubsub_client.items(service, node, max_items, item_ids, sub_id, None, rsm_request)
else:
# if mam is requested, we have to do a totally different query
if self._mam is None:
raise exceptions.NotFound(u"MAM (XEP-0313) plugin is not available")
if max_items is not None:
raise exceptions.DataError(u"max_items parameter can't be used with MAM")
if item_ids:
raise exceptions.DataError(u"items_ids parameter can't be used with MAM")
if mam_query.node is None:
mam_query.node = node
elif mam_query.node != node:
raise exceptions.DataError(u"MAM query node is incoherent with getItems's node")
if mam_query.rsm is None:
mam_query.rsm = rsm_request
else:
if mam_query.rsm != rsm_request:
raise exceptions.DataError(u"Conflict between RSM request and MAM's RSM request")
d = self._mam.getArchives(client, mam_query, service, self._unwrapMAMMessage)
try:
subscribe = C.bool(extra['subscribe'])