Update the MAM plugin for asyncio & new namespace
And add an example
This commit is contained in:
parent
b38e229359
commit
27e23672c1
3 changed files with 175 additions and 33 deletions
96
examples/mam.py
Executable file
96
examples/mam.py
Executable 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)
|
|
@ -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,
|
||||
|
|
|
@ -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))
|
||||
|
|
Loading…
Reference in a new issue