Merge branch 'master' into develop

This commit is contained in:
Lance Stout 2012-09-28 11:02:57 -07:00
commit a2c60a4911
28 changed files with 779 additions and 35 deletions

View File

@ -60,6 +60,7 @@ packages = [ 'sleekxmpp',
'sleekxmpp/plugins/xep_0009',
'sleekxmpp/plugins/xep_0009/stanza',
'sleekxmpp/plugins/xep_0012',
'sleekxmpp/plugins/xep_0013',
'sleekxmpp/plugins/xep_0016',
'sleekxmpp/plugins/xep_0027',
'sleekxmpp/plugins/xep_0030',
@ -80,6 +81,7 @@ packages = [ 'sleekxmpp',
'sleekxmpp/plugins/xep_0084',
'sleekxmpp/plugins/xep_0085',
'sleekxmpp/plugins/xep_0086',
'sleekxmpp/plugins/xep_0091',
'sleekxmpp/plugins/xep_0092',
'sleekxmpp/plugins/xep_0107',
'sleekxmpp/plugins/xep_0108',
@ -105,6 +107,8 @@ packages = [ 'sleekxmpp',
'sleekxmpp/plugins/xep_0279',
'sleekxmpp/plugins/xep_0280',
'sleekxmpp/plugins/xep_0297',
'sleekxmpp/plugins/xep_0308',
'sleekxmpp/plugins/xep_0313',
'sleekxmpp/features',
'sleekxmpp/features/feature_mechanisms',
'sleekxmpp/features/feature_mechanisms/stanza',

View File

@ -114,6 +114,17 @@ class BaseXMPP(XMLStream):
#: ``'to'`` and ``'from'`` JIDs of stanzas.
self.is_component = False
#: Messages may optionally be tagged with ID values. Setting
#: :attr:`use_message_ids` to `True` will assign all outgoing
#: messages an ID. Some plugin features require enabling
#: this option.
self.use_message_ids = False
#: Presence updates may optionally be tagged with ID values.
#: Setting :attr:`use_message_ids` to `True` will assign all
#: outgoing messages an ID.
self.use_presence_ids = False
#: The API registry is a way to process callbacks based on
#: JID+node combinations. Each callback in the registry is
#: marked with:

View File

@ -18,6 +18,7 @@ __all__ = [
'xep_0004', # Data Forms
'xep_0009', # Jabber-RPC
'xep_0012', # Last Activity
'xep_0013', # Flexible Offline Message Retrieval
'xep_0016', # Privacy Lists
'xep_0027', # Current Jabber OpenPGP Usage
'xep_0030', # Service Discovery
@ -38,6 +39,7 @@ __all__ = [
'xep_0084', # User Avatar
'xep_0085', # Chat State Notifications
'xep_0086', # Legacy Error Codes
'xep_0091', # Legacy Delayed Delivery
'xep_0092', # Software Version
'xep_0106', # JID Escaping
'xep_0107', # User Mood
@ -72,4 +74,6 @@ __all__ = [
'xep_0280', # Message Carbons
'xep_0297', # Stanza Forwarding
'xep_0302', # XMPP Compliance Suites 2012
'xep_0308', # Last Message Correction
'xep_0313', # Message Archive Management
]

View File

@ -0,0 +1,15 @@
"""
SleekXMPP: The Sleek XMPP Library
Copyright (C) 2012 Nathanael C. Fritz, Lance J.T. Stout
This file is part of SleekXMPP.
See the file LICENSE for copying permissio
"""
from sleekxmpp.plugins.base import register_plugin
from sleekxmpp.plugins.xep_0013.stanza import Offline
from sleekxmpp.plugins.xep_0013.offline import XEP_0013
register_plugin(XEP_0013)

View File

@ -0,0 +1,134 @@
"""
SleekXMPP: The Sleek XMPP Library
Copyright (C) 2012 Nathanael C. Fritz, Lance J.T. Stout
This file is part of SleekXMPP.
See the file LICENSE for copying permissio
"""
import logging
import sleekxmpp
from sleekxmpp.stanza import Message, Iq
from sleekxmpp.exceptions import XMPPError
from sleekxmpp.xmlstream.handler import Collector
from sleekxmpp.xmlstream.matcher import StanzaPath
from sleekxmpp.xmlstream import register_stanza_plugin
from sleekxmpp.plugins import BasePlugin
from sleekxmpp.plugins.xep_0013 import stanza
log = logging.getLogger(__name__)
class XEP_0013(BasePlugin):
"""
XEP-0013 Flexible Offline Message Retrieval
"""
name = 'xep_0013'
description = 'XEP-0013: Flexible Offline Message Retrieval'
dependencies = set(['xep_0030'])
stanza = stanza
def plugin_init(self):
register_stanza_plugin(Iq, stanza.Offline)
register_stanza_plugin(Message, stanza.Offline)
def get_count(self, **kwargs):
return self.xmpp['xep_0030'].get_info(
node='http://jabber.org/protocol/offline',
local=False,
**kwargs)
def get_headers(self, **kwargs):
return self.xmpp['xep_0030'].get_items(
node='http://jabber.org/protocol/offline',
local=False,
**kwargs)
def view(self, nodes, ifrom=None, block=True, timeout=None, callback=None):
if not isinstance(nodes, (list, set)):
nodes = [nodes]
iq = self.xmpp.Iq()
iq['type'] = 'get'
iq['from'] = ifrom
offline = iq['offline']
for node in nodes:
item = stanza.Item()
item['node'] = node
item['action'] = 'view'
offline.append(item)
collector = Collector(
'Offline_Results_%s' % iq['id'],
StanzaPath('message/offline'))
self.xmpp.register_handler(collector)
if not block and callback is not None:
def wrapped_cb(iq):
results = collector.stop()
if iq['type'] == 'result':
iq['offline']['results'] = results
callback(iq)
return iq.send(block=block, timeout=timeout, callback=wrapped_cb)
else:
try:
resp = iq.send(block=block, timeout=timeout, callback=callback)
resp['offline']['results'] = collector.stop()
return resp
except XMPPError as e:
collector.stop()
raise e
def remove(self, nodes, ifrom=None, block=True, timeout=None, callback=None):
if not isinstance(nodes, (list, set)):
nodes = [nodes]
iq = self.xmpp.Iq()
iq['type'] = 'set'
iq['from'] = ifrom
offline = iq['offline']
for node in nodes:
item = stanza.Item()
item['node'] = node
item['action'] = 'remove'
offline.append(item)
return iq.send(block=block, timeout=timeout, callback=callback)
def fetch(self, ifrom=None, block=True, timeout=None, callback=None):
iq = self.xmpp.Iq()
iq['type'] = 'set'
iq['from'] = ifrom
iq['offline']['fetch'] = True
collector = Collector(
'Offline_Results_%s' % iq['id'],
StanzaPath('message/offline'))
self.xmpp.register_handler(collector)
if not block and callback is not None:
def wrapped_cb(iq):
results = collector.stop()
if iq['type'] == 'result':
iq['offline']['results'] = results
callback(iq)
return iq.send(block=block, timeout=timeout, callback=wrapped_cb)
else:
try:
resp = iq.send(block=block, timeout=timeout, callback=callback)
resp['offline']['results'] = collector.stop()
return resp
except XMPPError as e:
collector.stop()
raise e
def purge(self, ifrom=None, block=True, timeout=None, callback=None):
iq = self.xmpp.Iq()
iq['type'] = 'set'
iq['from'] = ifrom
iq['offline']['purge'] = True
return iq.send(block=block, timeout=timeout, callback=callback)

View File

@ -0,0 +1,53 @@
"""
SleekXMPP: The Sleek XMPP Library
Copyright (C) 2012 Nathanael C. Fritz, Lance J.T. Stout
This file is part of SleekXMPP.
See the file LICENSE for copying permissio
"""
from sleekxmpp.jid import JID
from sleekxmpp.xmlstream import ElementBase, register_stanza_plugin
class Offline(ElementBase):
name = 'offline'
namespace = 'http://jabber.org/protocol/offline'
plugin_attrib = 'offline'
interfaces = set(['fetch', 'purge', 'results'])
bool_interfaces = interfaces
def setup(self, xml=None):
ElementBase.setup(self, xml)
self._results = []
# The results interface is meant only as an easy
# way to access the set of collected message responses
# from the query.
def get_results(self):
return self._results
def set_results(self, values):
self._results = values
def del_results(self):
self._results = []
class Item(ElementBase):
name = 'item'
namespace = 'http://jabber.org/protocol/offline'
plugin_attrib = 'item'
interfaces = set(['action', 'node', 'jid'])
actions = set(['view', 'remove'])
def get_jid(self):
return JID(self._get_attr('jid'))
def set_jid(self, value):
self._set_attr('jid', str(value))
register_stanza_plugin(Offline, Item, iterable=True)

View File

@ -288,7 +288,7 @@ class XEP_0030(BasePlugin):
'cached': cached}
return self.api['has_identity'](jid, node, ifrom, data)
def get_info(self, jid=None, node=None, local=False,
def get_info(self, jid=None, node=None, local=None,
cached=None, **kwargs):
"""
Retrieve the disco#info results from a given JID/node combination.
@ -325,6 +325,7 @@ class XEP_0030(BasePlugin):
received instead of blocking and waiting for
the reply.
"""
if local is None:
if jid is not None and not isinstance(jid, JID):
jid = JID(jid)
if self.xmpp.is_component:
@ -405,7 +406,7 @@ class XEP_0030(BasePlugin):
the XEP-0059 plugin, if the plugin is loaded.
Otherwise the parameter is ignored.
"""
if local or jid is None:
if local or local is None and jid is None:
items = self.api['get_items'](jid, node,
kwargs.get('ifrom', None),
kwargs)

View File

@ -25,11 +25,14 @@ class ResultIterator():
An iterator for Result Set Managment
"""
def __init__(self, query, interface, amount=10, start=None, reverse=False):
def __init__(self, query, interface, results='substanzas', amount=10,
start=None, reverse=False):
"""
Arguments:
query -- The template query
interface -- The substanza of the query, for example disco_items
results -- The query stanza's interface which provides a
countable list of query results.
amount -- The max amounts of items to request per iteration
start -- From which item id to start
reverse -- If True, page backwards through the results
@ -46,6 +49,7 @@ class ResultIterator():
self.amount = amount
self.start = start
self.interface = interface
self.results = results
self.reverse = reverse
self._stop = False
@ -85,7 +89,7 @@ class ResultIterator():
r[self.interface]['rsm']['first_index']:
count = int(r[self.interface]['rsm']['count'])
first = int(r[self.interface]['rsm']['first_index'])
num_items = len(r[self.interface]['substanzas'])
num_items = len(r[self.interface][self.results])
if first + num_items == count:
self._stop = True
@ -123,7 +127,7 @@ class XEP_0059(BasePlugin):
def session_bind(self, jid):
self.xmpp['xep_0030'].add_feature(Set.namespace)
def iterate(self, stanza, interface):
def iterate(self, stanza, interface, results='substanzas'):
"""
Create a new result set iterator for a given stanza query.
@ -135,5 +139,7 @@ class XEP_0059(BasePlugin):
result set management stanza should be
appended. For example, for disco#items queries
the interface 'disco_items' should be used.
results -- The name of the interface containing the
query results (typically just 'substanzas').
"""
return ResultIterator(stanza, interface)
return ResultIterator(stanza, interface, results)

View File

@ -0,0 +1,16 @@
"""
SleekXMPP: The Sleek XMPP Library
Copyright (C) 2012 Nathanael C. Fritz, Lance J.T. Stout
This file is part of SleekXMPP.
See the file LICENSE for copying permission.
"""
from sleekxmpp.plugins.base import register_plugin
from sleekxmpp.plugins.xep_0091 import stanza
from sleekxmpp.plugins.xep_0091.stanza import LegacyDelay
from sleekxmpp.plugins.xep_0091.legacy_delay import XEP_0091
register_plugin(XEP_0091)

View File

@ -0,0 +1,29 @@
"""
SleekXMPP: The Sleek XMPP Library
Copyright (C) 2012 Nathanael C. Fritz, Lance J.T. Stout
This file is part of SleekXMPP.
See the file LICENSE for copying permission.
"""
from sleekxmpp.stanza import Message, Presence
from sleekxmpp.xmlstream import register_stanza_plugin
from sleekxmpp.plugins import BasePlugin
from sleekxmpp.plugins.xep_0091 import stanza
class XEP_0091(BasePlugin):
"""
XEP-0091: Legacy Delayed Delivery
"""
name = 'xep_0091'
description = 'XEP-0091: Legacy Delayed Delivery'
dependencies = set()
stanza = stanza
def plugin_init(self):
register_stanza_plugin(Message, stanza.LegacyDelay)
register_stanza_plugin(Presence, stanza.LegacyDelay)

View File

@ -0,0 +1,46 @@
"""
SleekXMPP: The Sleek XMPP Library
Copyright (C) 2012 Nathanael C. Fritz, Lance J.T. Stout
This file is part of SleekXMPP.
See the file LICENSE for copying permission.
"""
import datetime as dt
from sleekxmpp.jid import JID
from sleekxmpp.xmlstream import ElementBase
from sleekxmpp.plugins import xep_0082
class LegacyDelay(ElementBase):
name = 'x'
namespace = 'jabber:x:delay'
plugin_attrib = 'legacy_delay'
interfaces = set(('from', 'stamp', 'text'))
def get_from(self):
return JID(self._get_attr('from'))
def set_from(self, value):
self._set_attr('from', str(value))
def get_stamp(self):
timestamp = self._get_attr('stamp')
return xep_0082.parse('%sZ' % timestamp)
def set_stamp(self, value):
if isinstance(value, dt.datetime):
value = value.astimezone(xep_0082.tzutc)
value = xep_0082.format_datetime(value)
self._set_attr('stamp', value[0:19].replace('-', ''))
def get_text(self):
return self.xml.text
def set_text(self, value):
self.xml.text = value
def del_text(self):
self.xml.text = ''

View File

@ -14,14 +14,17 @@ from sleekxmpp.plugins import xep_0082
class Delay(ElementBase):
"""
"""
name = 'delay'
namespace = 'urn:xmpp:delay'
plugin_attrib = 'delay'
interfaces = set(('from', 'stamp', 'text'))
def get_from(self):
return JID(self._get_attr('from'))
def set_from(self, value):
self._set_attr('from', str(value))
def get_stamp(self):
timestamp = self._get_attr('stamp')
return xep_0082.parse(timestamp)

View File

@ -1,6 +1,6 @@
"""
SleekXMPP: The Sleek XMPP Library
Copyright (C) 2011 Nathanael C. Fritz, Lance J.T. Stout
Copyright (C) 2012 Nathanael C. Fritz, Lance J.T. Stout
This file is part of SleekXMPP.
See the file LICENSE for copying permissio

View File

@ -1,6 +1,6 @@
"""
SleekXMPP: The Sleek XMPP Library
Copyright (C) 2011 Nathanael C. Fritz, Lance J.T. Stout
Copyright (C) 2012 Nathanael C. Fritz, Lance J.T. Stout
This file is part of SleekXMPP.
See the file LICENSE for copying permissio

View File

@ -26,9 +26,14 @@ class XEP_0297(BasePlugin):
def plugin_init(self):
register_stanza_plugin(Message, Forwarded)
register_stanza_plugin(Forwarded, Message)
register_stanza_plugin(Forwarded, Presence)
register_stanza_plugin(Forwarded, Iq)
# While these are marked as iterable, that is just for
# making it easier to extract the forwarded stanza. There
# still can be only a single forwarded stanza.
register_stanza_plugin(Forwarded, Message, iterable=True)
register_stanza_plugin(Forwarded, Presence, iterable=True)
register_stanza_plugin(Forwarded, Iq, iterable=True)
register_stanza_plugin(Forwarded, self.xmpp['xep_0203'].stanza.Delay)
self.xmpp.register_handler(

View File

@ -6,6 +6,7 @@
See the file LICENSE for copying permission.
"""
from sleekxmpp.stanza import Message, Presence, Iq
from sleekxmpp.xmlstream import ElementBase
@ -16,12 +17,9 @@ class Forwarded(ElementBase):
interfaces = set(['stanza'])
def get_stanza(self):
if self.xml.find('{jabber:client}message') is not None:
return self['message']
elif self.xml.find('{jabber:client}presence') is not None:
return self['presence']
elif self.xml.find('{jabber:client}iq') is not None:
return self['iq']
for stanza in self:
if isinstance(stanza, (Message, Presence, Iq)):
return stanza
return ''
def set_stanza(self, value):
@ -29,6 +27,10 @@ class Forwarded(ElementBase):
self.append(value)
def del_stanza(self):
del self['message']
del self['presence']
del self['iq']
found_stanzas = []
for stanza in self:
if isinstance(stanza, (Message, Presence, Iq)):
found_stanzas.append(stanza)
for stanza in found_stanzas:
self.iterables.remove(stanza)
self.xml.remove(stanza.xml)

View File

@ -0,0 +1,15 @@
"""
SleekXMPP: The Sleek XMPP Library
Copyright (C) 2012 Nathanael C. Fritz, Lance J.T. Stout
This file is part of SleekXMPP.
See the file LICENSE for copying permissio
"""
from sleekxmpp.plugins.base import register_plugin
from sleekxmpp.plugins.xep_0308.stanza import Replace
from sleekxmpp.plugins.xep_0308.correction import XEP_0308
register_plugin(XEP_0308)

View File

@ -0,0 +1,52 @@
"""
SleekXMPP: The Sleek XMPP Library
Copyright (C) 2012 Nathanael C. Fritz, Lance J.T. Stout
This file is part of SleekXMPP.
See the file LICENSE for copying permissio
"""
import logging
import sleekxmpp
from sleekxmpp.stanza import Message
from sleekxmpp.xmlstream.handler import Callback
from sleekxmpp.xmlstream.matcher import StanzaPath
from sleekxmpp.xmlstream import register_stanza_plugin
from sleekxmpp.plugins import BasePlugin
from sleekxmpp.plugins.xep_0308 import stanza, Replace
log = logging.getLogger(__name__)
class XEP_0308(BasePlugin):
"""
XEP-0308 Last Message Correction
"""
name = 'xep_0308'
description = 'XEP-0308: Last Message Correction'
dependencies = set(['xep_0030'])
stanza = stanza
def plugin_init(self):
self.xmpp.register_handler(
Callback('Message Correction',
StanzaPath('message/replace'),
self._handle_correction))
register_stanza_plugin(Message, Replace)
self.xmpp.use_message_ids = True
def plugin_end(self):
self.xmpp.remove_handler('Message Correction')
self.xmpp.plugin['xep_0030'].del_feature(feature=Replace.namespace)
def session_bind(self, jid):
self.xmpp.plugin['xep_0030'].add_feature(Replace.namespace)
def _handle_correction(self, msg):
self.xmpp.event('message_correction', msg)

View File

@ -0,0 +1,16 @@
"""
SleekXMPP: The Sleek XMPP Library
Copyright (C) 2012 Nathanael C. Fritz, Lance J.T. Stout
This file is part of SleekXMPP.
See the file LICENSE for copying permissio
"""
from sleekxmpp.xmlstream import ElementBase
class Replace(ElementBase):
name = 'replace'
namespace = 'urn:xmpp:message-correct:0'
plugin_attrib = 'replace'
interfaces = set(['id'])

View File

@ -0,0 +1,15 @@
"""
SleekXMPP: The Sleek XMPP Library
Copyright (C) 2012 Nathanael C. Fritz, Lance J.T. Stout
This file is part of SleekXMPP.
See the file LICENSE for copying permissio
"""
from sleekxmpp.plugins.base import register_plugin
from sleekxmpp.plugins.xep_0313.stanza import Result, MAM, Preferences
from sleekxmpp.plugins.xep_0313.mam import XEP_0313
register_plugin(XEP_0313)

View File

@ -0,0 +1,92 @@
"""
SleekXMPP: The Sleek XMPP Library
Copyright (C) 2012 Nathanael C. Fritz, Lance J.T. Stout
This file is part of SleekXMPP.
See the file LICENSE for copying permissio
"""
import logging
import sleekxmpp
from sleekxmpp.stanza import Message, Iq
from sleekxmpp.exceptions import XMPPError
from sleekxmpp.xmlstream.handler import Collector
from sleekxmpp.xmlstream.matcher import StanzaPath
from sleekxmpp.xmlstream import register_stanza_plugin
from sleekxmpp.plugins import BasePlugin
from sleekxmpp.plugins.xep_0313 import stanza
log = logging.getLogger(__name__)
class XEP_0313(BasePlugin):
"""
XEP-0313 Message Archive Management
"""
name = 'xep_0313'
description = 'XEP-0313: Message Archive Management'
dependencies = set(['xep_0030', 'xep_0050', 'xep_0059', 'xep_0297'])
stanza = stanza
def plugin_init(self):
register_stanza_plugin(Iq, stanza.MAM)
register_stanza_plugin(Iq, stanza.Preferences)
register_stanza_plugin(Message, stanza.Result)
register_stanza_plugin(stanza.MAM, self.xmpp['xep_0059'].stanza.Set)
def retrieve(self, jid=None, start=None, end=None, with_jid=None, ifrom=None,
block=True, timeout=None, callback=None, iterator=False):
iq = self.xmpp.Iq()
query_id = iq['id']
iq['to'] = jid
iq['from'] = ifrom
iq['type'] = 'get'
iq['mam']['queryid'] = query_id
iq['mam']['start'] = start
iq['mam']['end'] = end
iq['mam']['with'] = with_jid
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')
elif not block and callback is not None:
def wrapped_cb(iq):
results = collector.stop()
if iq['type'] == 'result':
iq['mam']['results'] = results
callback(iq)
return iq.send(block=block, timeout=timeout, callback=wrapped_cb)
else:
try:
resp = iq.send(block=block, timeout=timeout, callback=callback)
resp['mam']['results'] = collector.stop()
return resp
except XMPPError as e:
collector.stop()
raise e
def set_preferences(self, jid=None, default=None, always=None, never=None,
ifrom=None, block=True, timeout=None, callback=None):
iq = self.xmpp.Iq()
iq['type'] = 'set'
iq['to'] = jid
iq['from'] = ifrom
iq['mam_prefs']['default'] = default
iq['mam_prefs']['always'] = always
iq['mam_prefs']['never'] = never
return iq.send(block=block, timeout=timeout, callback=callback)
def get_configuration_commands(self, jid, **kwargs):
return self.xmpp['xep_0030'].get_items(
jid=jid,
node='urn:xmpp:mam#configure',
**kwargs)

View File

@ -0,0 +1,131 @@
"""
SleekXMPP: The Sleek XMPP Library
Copyright (C) 2012 Nathanael C. Fritz, Lance J.T. Stout
This file is part of SleekXMPP.
See the file LICENSE for copying permissio
"""
import datetime as dt
from sleekxmpp.jid import JID
from sleekxmpp.xmlstream import ElementBase, ET
from sleekxmpp.plugins import xep_0082
class MAM(ElementBase):
name = 'query'
namespace = 'urn:xmpp:mam:tmp'
plugin_attrib = 'mam'
interfaces = set(['queryid', 'start', 'end', 'with', 'results'])
sub_interfaces = set(['start', 'end', 'with'])
def setup(self, xml=None):
ElementBase.setup(self, xml)
self._results = []
def get_start(self):
timestamp = self._get_attr('start')
return xep_0082.parse(timestamp)
def set_start(self, value):
if isinstance(value, dt.datetime):
value = xep_0082.format_datetime(value)
self._set_attr('start', value)
def get_end(self):
timestamp = self._get_sub_text('end')
return xep_0082.parse(timestamp)
def set_end(self, value):
if isinstance(value, dt.datetime):
value = xep_0082.format_datetime(value)
self._set_sub_text('end', value)
def get_with(self):
return JID(self._get_sub_text('with'))
def set_with(self, value):
self._set_sub_text('with', str(value))
# The results interface is meant only as an easy
# way to access the set of collected message responses
# from the query.
def get_results(self):
return self._results
def set_results(self, values):
self._results = values
def del_results(self):
self._results = []
class Preferences(ElementBase):
name = 'prefs'
namespace = 'urn:xmpp:mam:tmp'
plugin_attrib = 'mam_prefs'
interfaces = set(['default', 'always', 'never'])
sub_interfaces = set(['always', 'never'])
def get_always(self):
results = set()
jids = self.xml.findall('{%s}always/{%s}jid' % (
self.namespace, self.namespace))
for jid in jids:
results.add(JID(jid.text))
return results
def set_always(self, value):
self._set_sub_text('always', '', keep=True)
always = self.xml.find('{%s}always' % self.namespace)
always.clear()
if not isinstance(value, (list, set)):
value = [value]
for jid in value:
jid_xml = ET.Element('{%s}jid' % self.namespace)
jid_xml.text = str(jid)
always.append(jid_xml)
def get_never(self):
results = set()
jids = self.xml.findall('{%s}never/{%s}jid' % (
self.namespace, self.namespace))
for jid in jids:
results.add(JID(jid.text))
return results
def set_never(self, value):
self._set_sub_text('never', '', keep=True)
never = self.xml.find('{%s}never' % self.namespace)
never.clear()
if not isinstance(value, (list, set)):
value = [value]
for jid in value:
jid_xml = ET.Element('{%s}jid' % self.namespace)
jid_xml.text = str(jid)
never.append(jid_xml)
class Result(ElementBase):
name = 'result'
namespace = 'urn:xmpp:mam:tmp'
plugin_attrib = 'mam_result'
interfaces = set(['forwarded', 'queryid', 'id'])
def get_forwarded(self):
return self.parent()['forwarded']
def del_forwarded(self):
del self.parent()['forwarded']

View File

@ -63,6 +63,17 @@ class Message(RootStanza):
lang_interfaces = sub_interfaces
types = set(['normal', 'chat', 'headline', 'error', 'groupchat'])
def __init__(self, *args, **kwargs):
"""
Initialize a new <message /> stanza with an optional 'id' value.
Overrides StanzaBase.__init__.
"""
StanzaBase.__init__(self, *args, **kwargs)
if self['id'] == '':
if self.stream is not None and self.stream.use_message_ids:
self['id'] = self.stream.new_id()
def get_type(self):
"""
Return the message type.

View File

@ -72,6 +72,17 @@ class Presence(RootStanza):
'subscribed', 'unsubscribe', 'unsubscribed'])
showtypes = set(['dnd', 'chat', 'xa', 'away'])
def __init__(self, *args, **kwargs):
"""
Initialize a new <presence /> stanza with an optional 'id' value.
Overrides StanzaBase.__init__.
"""
StanzaBase.__init__(self, *args, **kwargs)
if self['id'] == '':
if self.stream is not None and self.stream.use_presence_ids:
self['id'] = self.stream.new_id()
def exception(self, e):
"""
Override exception passback for presence.

View File

@ -368,6 +368,11 @@ class SleekTest(unittest.TestCase):
else:
for plugin in plugins:
self.xmpp.register_plugin(plugin)
# Some plugins require messages to have ID values. Set
# this to True in tests related to those plugins.
self.xmpp.use_message_ids = False
self.xmpp.process(threaded=True)
if skip:
if socket != 'live':

View File

@ -7,6 +7,7 @@
"""
from sleekxmpp.xmlstream.handler.callback import Callback
from sleekxmpp.xmlstream.handler.collector import Collector
from sleekxmpp.xmlstream.handler.waiter import Waiter
from sleekxmpp.xmlstream.handler.xmlcallback import XMLCallback
from sleekxmpp.xmlstream.handler.xmlwaiter import XMLWaiter

View File

@ -0,0 +1,66 @@
# -*- coding: utf-8 -*-
"""
sleekxmpp.xmlstream.handler.collector
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Part of SleekXMPP: The Sleek XMPP Library
:copyright: (c) 2012 Nathanael C. Fritz, Lance J.T. Stout
:license: MIT, see LICENSE for more details
"""
import logging
from sleekxmpp.util import Queue, QueueEmpty
from sleekxmpp.xmlstream.handler.base import BaseHandler
log = logging.getLogger(__name__)
class Collector(BaseHandler):
"""
The Collector handler allows for collecting a set of stanzas
that match a given pattern. Unlike the Waiter handler, a
Collector does not block execution, and will continue to
accumulate matching stanzas until told to stop.
:param string name: The name of the handler.
:param matcher: A :class:`~sleekxmpp.xmlstream.matcher.base.MatcherBase`
derived object for matching stanza objects.
:param stream: The :class:`~sleekxmpp.xmlstream.xmlstream.XMLStream`
instance this handler should monitor.
"""
def __init__(self, name, matcher, stream=None):
BaseHandler.__init__(self, name, matcher, stream=stream)
self._payload = Queue()
def prerun(self, payload):
"""Store the matched stanza when received during processing.
:param payload: The matched
:class:`~sleekxmpp.xmlstream.stanzabase.ElementBase` object.
"""
self._payload.put(payload)
def run(self, payload):
"""Do not process this handler during the main event loop."""
pass
def stop(self):
"""
Stop collection of matching stanzas, and return the ones that
have been stored so far.
"""
self._destroy = True
results = []
try:
while True:
results.append(self._payload.get(False))
except QueueEmpty:
pass
self.stream().remove_handler(self.name)
return results

View File

@ -17,7 +17,7 @@ class TestStreamSet(SleekTest):
def iter(self, rev=False):
q = self.xmpp.Iq()
q['type'] = 'get'
it = ResultIterator(q, 'disco_items', '1', reverse=rev)
it = ResultIterator(q, 'disco_items', amount='1', reverse=rev)
for i in it:
for j in i['disco_items']['items']:
self.items.append(j[0])