Update the MAM plugin for asyncio & new namespace

And add an example
This commit is contained in:
mathieui 2017-09-24 17:43:06 +02:00
parent b38e229359
commit 27e23672c1
No known key found for this signature in database
GPG key ID: C59F84CEEFD616E3
3 changed files with 175 additions and 33 deletions

96
examples/mam.py Executable file
View file

@ -0,0 +1,96 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
Slixmpp: The Slick XMPP Library
Copyright (C) 2017 Mathieu Pasquet
This file is part of Slixmpp.
See the file LICENSE for copying permission.
"""
import logging
from getpass import getpass
from argparse import ArgumentParser
import slixmpp
from slixmpp.exceptions import XMPPError
from slixmpp import asyncio
log = logging.getLogger(__name__)
class MAM(slixmpp.ClientXMPP):
"""
A basic client fetching mam archive messages
"""
def __init__(self, jid, password, remote_jid, start):
slixmpp.ClientXMPP.__init__(self, jid, password)
self.remote_jid = remote_jid
self.start_date = start
self.add_event_handler("session_start", self.start)
async def start(self, *args):
"""
Fetch mam results for the specified JID.
Use RSM to paginate the results.
"""
iq = self.make_iq_get()
results = self.plugin['xep_0313'].retrieve(jid=self.remote_jid, iterator=True, rsm={'max': 10}, start=self.start_date)
page = 1
async for rsm in results:
print('Page %s' % page)
for msg in rsm['mam']['results']:
print('%s: %s' % (msg['mam_result']['forwarded']['stanza']['from'], msg['mam_result']['forwarded']['stanza']['body']))
page += 1
self.disconnect()
if __name__ == '__main__':
# Setup the command line arguments.
parser = ArgumentParser()
parser.add_argument("-q","--quiet", help="set logging to ERROR",
action="store_const",
dest="loglevel",
const=logging.ERROR,
default=logging.INFO)
parser.add_argument("-d","--debug", help="set logging to DEBUG",
action="store_const",
dest="loglevel",
const=logging.DEBUG,
default=logging.INFO)
# JID and password options.
parser.add_argument("-j", "--jid", dest="jid",
help="JID to use")
parser.add_argument("-p", "--password", dest="password",
help="password to use")
# Other options
parser.add_argument("-r", "--remote-jid", dest="remote_jid",
help="Remote JID")
parser.add_argument("--start", help="Start date", default='2017-09-20T12:00:00Z')
args = parser.parse_args()
# Setup logging.
logging.basicConfig(level=args.loglevel,
format='%(levelname)-8s %(message)s')
if args.jid is None:
args.jid = input("Username: ")
if args.password is None:
args.password = getpass("Password: ")
if args.remote_jid is None:
args.remote_jid = input("Remote JID: ")
if args.start is None:
args.start = input("Start time: ")
xmpp = MAM(args.jid, args.password, args.remote_jid, args.start)
xmpp.register_plugin('xep_0313')
# Connect to the XMPP server and start processing XMPP stanzas.
xmpp.connect()
xmpp.process(forever=False)

View file

@ -36,35 +36,58 @@ class XEP_0313(BasePlugin):
register_stanza_plugin(Iq, stanza.MAM)
register_stanza_plugin(Iq, stanza.Preferences)
register_stanza_plugin(Message, stanza.Result)
register_stanza_plugin(Message, stanza.Archived, iterable=True)
register_stanza_plugin(Iq, stanza.Fin)
register_stanza_plugin(stanza.Result, self.xmpp['xep_0297'].stanza.Forwarded)
register_stanza_plugin(stanza.MAM, self.xmpp['xep_0059'].stanza.Set)
register_stanza_plugin(stanza.Fin, self.xmpp['xep_0059'].stanza.Set)
def retrieve(self, jid=None, start=None, end=None, with_jid=None, ifrom=None,
timeout=None, callback=None, iterator=False):
timeout=None, callback=None, iterator=False, rsm=None):
iq = self.xmpp.Iq()
query_id = iq['id']
iq['to'] = jid
iq['from'] = ifrom
iq['type'] = 'get'
iq['type'] = 'set'
iq['mam']['queryid'] = query_id
iq['mam']['start'] = start
iq['mam']['end'] = end
iq['mam']['with'] = with_jid
if rsm:
for key, value in rsm.items():
iq['mam']['rsm'][key] = str(value)
cb_data = {}
def pre_cb(query):
query['mam']['queryid'] = query['id']
collector = Collector(
'MAM_Results_%s' % query_id,
StanzaPath('message/mam_result@queryid=%s' % query['id']))
self.xmpp.register_handler(collector)
cb_data['collector'] = collector
def post_cb(result):
results = cb_data['collector'].stop()
if result['type'] == 'result':
result['mam']['results'] = results
if iterator:
return self.xmpp['xep_0059'].iterate(iq, 'mam', 'results',
recv_interface='mam_fin',
pre_cb=pre_cb, post_cb=post_cb)
collector = Collector(
'MAM_Results_%s' % query_id,
StanzaPath('message/mam_result@queryid=%s' % query_id))
self.xmpp.register_handler(collector)
if iterator:
return self.xmpp['xep_0059'].iterate(iq, 'mam', 'results')
def wrapped_cb(iq):
results = collector.stop()
if iq['type'] == 'result':
iq['mam']['results'] = results
if callback:
callback(iq)
return iq.send(timeout=timeout, callback=wrapped_cb)
def set_preferences(self, jid=None, default=None, always=None, never=None,

View file

@ -10,44 +10,76 @@ import datetime as dt
from slixmpp.jid import JID
from slixmpp.xmlstream import ElementBase, ET
from slixmpp.plugins import xep_0082
from slixmpp.plugins import xep_0082, xep_0004
class MAM(ElementBase):
name = 'query'
namespace = 'urn:xmpp:mam:tmp'
namespace = 'urn:xmpp:mam:2'
plugin_attrib = 'mam'
interfaces = {'queryid', 'start', 'end', 'with', 'results'}
sub_interfaces = {'start', 'end', 'with'}
def setup(self, xml=None):
ElementBase.setup(self, xml)
self._form = xep_0004.stanza.Form()
self._form['type'] = 'submit'
field = self._form.add_field(var='FORM_TYPE', ftype='hidden',
value='urn:xmpp:mam:2')
self.append(self._form)
self._results = []
def __get_fields(self):
return self._form.get_fields()
def get_start(self):
timestamp = self._get_sub_text('start')
return xep_0082.parse(timestamp)
fields = self.__get_fields()
field = fields.get('start')
if field:
return xep_0082.parse(field['value'])
def set_start(self, value):
if isinstance(value, dt.datetime):
value = xep_0082.format_datetime(value)
self._set_sub_text('start', value)
fields = self.__get_fields()
field = fields.get('start')
if field:
field['value'] = value
else:
field = self._form.add_field(var='start')
field['value'] = value
def get_end(self):
timestamp = self._get_sub_text('end')
return xep_0082.parse(timestamp)
fields = self.__get_fields()
field = fields.get('end')
if field:
return xep_0082.parse(field['value'])
def set_end(self, value):
if isinstance(value, dt.datetime):
value = xep_0082.format_datetime(value)
self._set_sub_text('end', value)
fields = self.__get_fields()
field = fields.get('end')
if field:
field['value'] = value
else:
field = self._form.add_field(var='end')
field['value'] = value
def get_with(self):
return JID(self._get_sub_text('with'))
fields = self.__get_fields()
field = fields.get('with')
if field:
return JID(field['value'])
def set_with(self, value):
self._set_sub_text('with', str(value))
fields = self.__get_fields()
field = fields.get('with')
if field:
field['with'] = str(value)
else:
field = self._form.add_field(var='with')
field['value'] = str(value)
# The results interface is meant only as an easy
# way to access the set of collected message responses
# from the query.
@ -64,7 +96,7 @@ class MAM(ElementBase):
class Preferences(ElementBase):
name = 'prefs'
namespace = 'urn:xmpp:mam:tmp'
namespace = 'urn:xmpp:mam:2'
plugin_attrib = 'mam_prefs'
interfaces = {'default', 'always', 'never'}
sub_interfaces = {'always', 'never'}
@ -118,22 +150,13 @@ class Preferences(ElementBase):
never.append(jid_xml)
class Fin(ElementBase):
name = 'fin'
namespace = 'urn:xmpp:mam:2'
plugin_attrib = 'mam_fin'
class Result(ElementBase):
name = 'result'
namespace = 'urn:xmpp:mam:tmp'
namespace = 'urn:xmpp:mam:2'
plugin_attrib = 'mam_result'
interfaces = {'queryid', 'id'}
class Archived(ElementBase):
name = 'archived'
namespace = 'urn:xmpp:mam:tmp'
plugin_attrib = 'mam_archived'
plugin_multi_attrib = 'mam_archives'
interfaces = {'by', 'id'}
def get_by(self):
return JID(self._get_attr('by'))
def set_by(self, value):
return self._set_attr('by', str(value))