diff --git a/sleekxmpp/basexmpp.py b/sleekxmpp/basexmpp.py
index 91b419ba..ef6906a6 100644
--- a/sleekxmpp/basexmpp.py
+++ b/sleekxmpp/basexmpp.py
@@ -426,7 +426,7 @@ class BaseXMPP(XMLStream):
msubject -- Optional subject for the message.
mtype -- The message's type, such as 'chat' or 'groupchat'.
mhtml -- Optional HTML body content.
- mfrom -- The sender of the message. If sending from a client,
+ mfrom -- The sender of the message. if sending from a client,
be aware that some servers require that the full JID
of the sender be used.
mnick -- Optional nickname of the sender.
@@ -441,7 +441,7 @@ class BaseXMPP(XMLStream):
return message
def make_presence(self, pshow=None, pstatus=None, ppriority=None,
- pto=None, ptype=None, pfrom=None):
+ pto=None, ptype=None, pfrom=None, pnick=None):
"""
Create and initialize a new Presence stanza.
@@ -452,14 +452,16 @@ class BaseXMPP(XMLStream):
pto -- The recipient of a directed presence.
ptype -- The type of presence, such as 'subscribe'.
pfrom -- The sender of the presence.
+ pnick -- Optional nickname of the presence's sender.
"""
presence = self.Presence(stype=ptype, sfrom=pfrom, sto=pto)
if pshow is not None:
presence['type'] = pshow
- if pfrom is None:
+ if pfrom is None and self.is_component:
presence['from'] = self.boundjid.full
presence['priority'] = ppriority
presence['status'] = pstatus
+ presence['nick'] = pnick
return presence
def send_message(self, mto, mbody, msubject=None, mtype=None,
@@ -467,13 +469,22 @@ class BaseXMPP(XMLStream):
"""
Create, initialize, and send a Message stanza.
-
+ Arguments:
+ mto -- The recipient of the message.
+ mbody -- The main contents of the message.
+ msubject -- Optional subject for the message.
+ mtype -- The message's type, such as 'chat' or 'groupchat'.
+ mhtml -- Optional HTML body content.
+ mfrom -- The sender of the message. if sending from a client,
+ be aware that some servers require that the full JID
+ of the sender be used.
+ mnick -- Optional nickname of the sender.
"""
- self.makeMessage(mto, mbody, msubject, mtype,
- mhtml, mfrom, mnick).send()
+ self.make_message(mto, mbody, msubject, mtype,
+ mhtml, mfrom, mnick).send()
def send_presence(self, pshow=None, pstatus=None, ppriority=None,
- pto=None, pfrom=None, ptype=None):
+ pto=None, pfrom=None, ptype=None, pnick=None):
"""
Create, initialize, and send a Presence stanza.
@@ -484,13 +495,20 @@ class BaseXMPP(XMLStream):
pto -- The recipient of a directed presence.
ptype -- The type of presence, such as 'subscribe'.
pfrom -- The sender of the presence.
+ pnick -- Optional nickname of the presence's sender.
"""
- self.makePresence(pshow, pstatus, ppriority, pto,
- ptype=ptype, pfrom=pfrom).send()
- # Unexpected errors may occur if
- if not self.sentpresence:
- self.event('sent_presence')
- self.sentpresence = True
+ # Python2.6 chokes on Unicode strings for dict keys.
+ args = {str('pto'): pto,
+ str('ptype'): ptype,
+ str('pshow'): pshow,
+ str('pstatus'): pstatus,
+ str('ppriority'): ppriority,
+ str('pnick'): pnick}
+
+ if self.is_component:
+ self.roster[pfrom].send_presence(**args)
+ else:
+ self.client_roster.send_presence(**args)
def send_presence_subscription(self, pto, pfrom=None,
ptype='subscribe', pnick=None):
diff --git a/sleekxmpp/roster/item.py b/sleekxmpp/roster/item.py
index eb115a4a..72baf694 100644
--- a/sleekxmpp/roster/item.py
+++ b/sleekxmpp/roster/item.py
@@ -105,23 +105,25 @@ class RosterItem(object):
"""
def __init__(self, xmpp, jid, owner=None,
- state=None, db=None):
+ state=None, db=None, roster=None):
"""
Create a new roster item.
Arguments:
- xmpp -- The main SleekXMPP instance.
- jid -- The item's JID.
- owner -- The roster owner's JID. Defaults
- so self.xmpp.boundjid.bare.
- state -- A dictionary of initial state values.
- db -- An optional interface to an external datastore.
+ xmpp -- The main SleekXMPP instance.
+ jid -- The item's JID.
+ owner -- The roster owner's JID. Defaults
+ so self.xmpp.boundjid.bare.
+ state -- A dictionary of initial state values.
+ db -- An optional interface to an external datastore.
+ roster -- The roster object containing this entry.
"""
self.xmpp = xmpp
self.jid = jid
self.owner = owner or self.xmpp.boundjid.bare
self.last_status = None
self.resources = {}
+ self.roster = roster
self.db = db
self._state = state or {
'from': False,
@@ -290,19 +292,46 @@ class RosterItem(object):
p['from'] = self.owner
p.send()
- def send_presence(self, ptype='available', status=None):
- p = self.xmpp.Presence()
- p['to'] = self.jid
- p['type'] = ptype
- p['status'] = status
+ def send_presence(self, ptype=None, pshow=None, pstatus=None,
+ ppriority=None, pnick=None):
+ """
+ Create, initialize, and send a Presence stanza.
+
+ Arguments:
+ pshow -- The presence's show value.
+ pstatus -- The presence's status message.
+ ppriority -- This connections' priority.
+ ptype -- The type of presence, such as 'subscribe'.
+ pnick -- Optional nickname of the presence's sender.
+ """
+ p = self.xmpp.make_presence(pshow=pshow,
+ pstatus=pstatus,
+ ppriority=ppriority,
+ ptype=ptype,
+ pnick=pnick,
+ pto=self.jid)
if self.xmpp.is_component:
p['from'] = self.owner
- self.last_status = p
+ if p['type'] in p.showtypes or p['type'] == 'available':
+ self.last_status = p
p.send()
+ if not self.xmpp.sentpresence:
+ self.xmpp.event('sent_presence')
+ self.xmpp.sentpresence = True
+
def send_last_presence(self):
if self.last_status is None:
- self.send_presence()
+ pres = self.roster.last_status
+ if pres is None:
+ self.send_presence()
+ else:
+ pres['to'] = self.jid
+ if self.xmpp.is_component:
+ pres['from'] = self.owner
+ else:
+ del pres['from']
+ pres.send()
else:
self.last_status.send()
diff --git a/sleekxmpp/roster/multi.py b/sleekxmpp/roster/multi.py
index cd4a5195..e9f3389e 100644
--- a/sleekxmpp/roster/multi.py
+++ b/sleekxmpp/roster/multi.py
@@ -34,7 +34,8 @@ class Roster(object):
Defaults to True.
Methods:
- add -- Create a new roster node for a JID.
+ add -- Create a new roster node for a JID.
+ send_presence -- Shortcut for sending a presence stanza.
"""
def __init__(self, xmpp, db=None):
@@ -113,3 +114,27 @@ class Roster(object):
"""
for node in self:
self[node].reset()
+
+ def send_presence(self, pshow=None, pstatus=None, ppriority=None,
+ pto=None, pfrom=None, ptype=None, pnick=None):
+ """
+ Create, initialize, and send a Presence stanza.
+
+ Forwards the send request to the appropriate roster to
+ perform the actual sending.
+
+ Arguments:
+ pshow -- The presence's show value.
+ pstatus -- The presence's status message.
+ ppriority -- This connections' priority.
+ pto -- The recipient of a directed presence.
+ ptype -- The type of presence, such as 'subscribe'.
+ pfrom -- The sender of the presence.
+ pnick -- Optional nickname of the presence's sender.
+ """
+ self[pfrom].send_presence(ptype=ptype,
+ pshow=pshow,
+ pstatus=pstatus,
+ ppriority=ppriority,
+ pnick=pnick,
+ pto=pto)
diff --git a/sleekxmpp/roster/single.py b/sleekxmpp/roster/single.py
index 6ce44e8f..6833563f 100644
--- a/sleekxmpp/roster/single.py
+++ b/sleekxmpp/roster/single.py
@@ -29,14 +29,17 @@ class RosterNode(object):
are created after automatically authrorizing
a subscription request.
Defaults to True
+ last_status -- The last sent presence status that was broadcast
+ to all contact JIDs.
Methods:
- add -- Add a JID to the roster.
- update -- Update a JID's subscription information.
- subscribe -- Subscribe to a JID.
- unsubscribe -- Unsubscribe from a JID.
- remove -- Remove a JID from the roster.
- presence -- Return presence information for a JID's resources.
+ add -- Add a JID to the roster.
+ update -- Update a JID's subscription information.
+ subscribe -- Subscribe to a JID.
+ unsubscribe -- Unsubscribe from a JID.
+ remove -- Remove a JID from the roster.
+ presence -- Return presence information for a JID's resources.
+ send_presence -- Shortcut for sending a presence stanza.
"""
def __init__(self, xmpp, jid, db=None):
@@ -53,6 +56,7 @@ class RosterNode(object):
self.db = db
self.auto_authorize = True
self.auto_subscribe = True
+ self.last_status = None
self._jids = {}
if self.db:
@@ -135,7 +139,8 @@ class RosterNode(object):
'whitelisted': whitelisted,
'subscription': 'none'}
self._jids[jid] = RosterItem(self.xmpp, jid, self.jid,
- state=state, db=self.db)
+ state=state, db=self.db,
+ roster=self)
if save:
self._jids[jid].save()
@@ -220,3 +225,38 @@ class RosterNode(object):
"""
for jid in self:
self[jid].reset()
+
+ def send_presence(self, ptype=None, pshow=None, pstatus=None,
+ ppriority=None, pnick=None, pto=None):
+ """
+ Create, initialize, and send a Presence stanza.
+
+ If no recipient is specified, send the presence immediately.
+ Otherwise, forward the send request to the recipient's roster
+ entry for processing.
+
+ Arguments:
+ pshow -- The presence's show value.
+ pstatus -- The presence's status message.
+ ppriority -- This connections' priority.
+ pto -- The recipient of a directed presence.
+ ptype -- The type of presence, such as 'subscribe'.
+ """
+ if pto:
+ self[pto].send_presence(ptype, pshow, pstatus,
+ ppriority, pnick)
+ else:
+ p = self.xmpp.make_presence(pshow=pshow,
+ pstatus=pstatus,
+ ppriority=ppriority,
+ ptype=ptype,
+ pnick=pnick)
+ if self.xmpp.is_component:
+ p['from'] = self.jid
+ if p['type'] in p.showtypes or p['type'] == 'available':
+ self.last_status = p
+ p.send()
+
+ if not self.xmpp.sentpresence:
+ self.xmpp.event('sent_presence')
+ self.xmpp.sentpresence = True
diff --git a/tests/test_stream_roster.py b/tests/test_stream_roster.py
index 69e5ca13..aa8fc26f 100644
--- a/tests/test_stream_roster.py
+++ b/tests/test_stream_roster.py
@@ -203,5 +203,35 @@ class TestStreamRoster(SleekTest):
self.failUnless(result == expected,
"Unexpected roster values: %s" % result)
+ def testSendLastPresence(self):
+ """Test that sending the last presence works."""
+ self.stream_start()
+ self.xmpp.send_presence(pshow='dnd')
+ self.xmpp.auto_authorize = True
+ self.xmpp.auto_subscribe = True
+
+ self.send("""
+
+ dnd
+
+ """)
+
+ self.recv("""
+
+ """)
+
+ self.send("""
+
+ """)
+
+ self.send("""
+
+ dnd
+
+ """)
+
suite = unittest.TestLoader().loadTestsFromTestCase(TestStreamRoster)