Integrate roster with BaseXMPP.

Last sent stanzas are saved regardless of if the roster is used
directly or self.send_presence
This commit is contained in:
Lance Stout 2011-06-16 16:03:31 -07:00
parent 251a47db8c
commit 29d775e675
5 changed files with 177 additions and 35 deletions

View file

@ -426,7 +426,7 @@ class BaseXMPP(XMLStream):
msubject -- Optional subject for the message. msubject -- Optional subject for the message.
mtype -- The message's type, such as 'chat' or 'groupchat'. mtype -- The message's type, such as 'chat' or 'groupchat'.
mhtml -- Optional HTML body content. 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 be aware that some servers require that the full JID
of the sender be used. of the sender be used.
mnick -- Optional nickname of the sender. mnick -- Optional nickname of the sender.
@ -441,7 +441,7 @@ class BaseXMPP(XMLStream):
return message return message
def make_presence(self, pshow=None, pstatus=None, ppriority=None, 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. Create and initialize a new Presence stanza.
@ -452,14 +452,16 @@ class BaseXMPP(XMLStream):
pto -- The recipient of a directed presence. pto -- The recipient of a directed presence.
ptype -- The type of presence, such as 'subscribe'. ptype -- The type of presence, such as 'subscribe'.
pfrom -- The sender of the presence. pfrom -- The sender of the presence.
pnick -- Optional nickname of the presence's sender.
""" """
presence = self.Presence(stype=ptype, sfrom=pfrom, sto=pto) presence = self.Presence(stype=ptype, sfrom=pfrom, sto=pto)
if pshow is not None: if pshow is not None:
presence['type'] = pshow presence['type'] = pshow
if pfrom is None: if pfrom is None and self.is_component:
presence['from'] = self.boundjid.full presence['from'] = self.boundjid.full
presence['priority'] = ppriority presence['priority'] = ppriority
presence['status'] = pstatus presence['status'] = pstatus
presence['nick'] = pnick
return presence return presence
def send_message(self, mto, mbody, msubject=None, mtype=None, def send_message(self, mto, mbody, msubject=None, mtype=None,
@ -467,13 +469,22 @@ class BaseXMPP(XMLStream):
""" """
Create, initialize, and send a Message stanza. 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, self.make_message(mto, mbody, msubject, mtype,
mhtml, mfrom, mnick).send() mhtml, mfrom, mnick).send()
def send_presence(self, pshow=None, pstatus=None, ppriority=None, 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. Create, initialize, and send a Presence stanza.
@ -484,13 +495,20 @@ class BaseXMPP(XMLStream):
pto -- The recipient of a directed presence. pto -- The recipient of a directed presence.
ptype -- The type of presence, such as 'subscribe'. ptype -- The type of presence, such as 'subscribe'.
pfrom -- The sender of the presence. pfrom -- The sender of the presence.
pnick -- Optional nickname of the presence's sender.
""" """
self.makePresence(pshow, pstatus, ppriority, pto, # Python2.6 chokes on Unicode strings for dict keys.
ptype=ptype, pfrom=pfrom).send() args = {str('pto'): pto,
# Unexpected errors may occur if str('ptype'): ptype,
if not self.sentpresence: str('pshow'): pshow,
self.event('sent_presence') str('pstatus'): pstatus,
self.sentpresence = True 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, def send_presence_subscription(self, pto, pfrom=None,
ptype='subscribe', pnick=None): ptype='subscribe', pnick=None):

View file

@ -105,23 +105,25 @@ class RosterItem(object):
""" """
def __init__(self, xmpp, jid, owner=None, def __init__(self, xmpp, jid, owner=None,
state=None, db=None): state=None, db=None, roster=None):
""" """
Create a new roster item. Create a new roster item.
Arguments: Arguments:
xmpp -- The main SleekXMPP instance. xmpp -- The main SleekXMPP instance.
jid -- The item's JID. jid -- The item's JID.
owner -- The roster owner's JID. Defaults owner -- The roster owner's JID. Defaults
so self.xmpp.boundjid.bare. so self.xmpp.boundjid.bare.
state -- A dictionary of initial state values. state -- A dictionary of initial state values.
db -- An optional interface to an external datastore. db -- An optional interface to an external datastore.
roster -- The roster object containing this entry.
""" """
self.xmpp = xmpp self.xmpp = xmpp
self.jid = jid self.jid = jid
self.owner = owner or self.xmpp.boundjid.bare self.owner = owner or self.xmpp.boundjid.bare
self.last_status = None self.last_status = None
self.resources = {} self.resources = {}
self.roster = roster
self.db = db self.db = db
self._state = state or { self._state = state or {
'from': False, 'from': False,
@ -290,19 +292,46 @@ class RosterItem(object):
p['from'] = self.owner p['from'] = self.owner
p.send() p.send()
def send_presence(self, ptype='available', status=None): def send_presence(self, ptype=None, pshow=None, pstatus=None,
p = self.xmpp.Presence() ppriority=None, pnick=None):
p['to'] = self.jid """
p['type'] = ptype Create, initialize, and send a Presence stanza.
p['status'] = status
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: if self.xmpp.is_component:
p['from'] = self.owner p['from'] = self.owner
self.last_status = p if p['type'] in p.showtypes or p['type'] == 'available':
self.last_status = p
p.send() p.send()
if not self.xmpp.sentpresence:
self.xmpp.event('sent_presence')
self.xmpp.sentpresence = True
def send_last_presence(self): def send_last_presence(self):
if self.last_status is None: 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: else:
self.last_status.send() self.last_status.send()

View file

@ -34,7 +34,8 @@ class Roster(object):
Defaults to True. Defaults to True.
Methods: 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): def __init__(self, xmpp, db=None):
@ -113,3 +114,27 @@ class Roster(object):
""" """
for node in self: for node in self:
self[node].reset() 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)

View file

@ -29,14 +29,17 @@ class RosterNode(object):
are created after automatically authrorizing are created after automatically authrorizing
a subscription request. a subscription request.
Defaults to True Defaults to True
last_status -- The last sent presence status that was broadcast
to all contact JIDs.
Methods: Methods:
add -- Add a JID to the roster. add -- Add a JID to the roster.
update -- Update a JID's subscription information. update -- Update a JID's subscription information.
subscribe -- Subscribe to a JID. subscribe -- Subscribe to a JID.
unsubscribe -- Unsubscribe from a JID. unsubscribe -- Unsubscribe from a JID.
remove -- Remove a JID from the roster. remove -- Remove a JID from the roster.
presence -- Return presence information for a JID's resources. presence -- Return presence information for a JID's resources.
send_presence -- Shortcut for sending a presence stanza.
""" """
def __init__(self, xmpp, jid, db=None): def __init__(self, xmpp, jid, db=None):
@ -53,6 +56,7 @@ class RosterNode(object):
self.db = db self.db = db
self.auto_authorize = True self.auto_authorize = True
self.auto_subscribe = True self.auto_subscribe = True
self.last_status = None
self._jids = {} self._jids = {}
if self.db: if self.db:
@ -135,7 +139,8 @@ class RosterNode(object):
'whitelisted': whitelisted, 'whitelisted': whitelisted,
'subscription': 'none'} 'subscription': 'none'}
self._jids[jid] = RosterItem(self.xmpp, jid, self.jid, self._jids[jid] = RosterItem(self.xmpp, jid, self.jid,
state=state, db=self.db) state=state, db=self.db,
roster=self)
if save: if save:
self._jids[jid].save() self._jids[jid].save()
@ -220,3 +225,38 @@ class RosterNode(object):
""" """
for jid in self: for jid in self:
self[jid].reset() 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

View file

@ -203,5 +203,35 @@ class TestStreamRoster(SleekTest):
self.failUnless(result == expected, self.failUnless(result == expected,
"Unexpected roster values: %s" % result) "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("""
<presence>
<show>dnd</show>
</presence>
""")
self.recv("""
<presence from="user@localhost"
to="tester@localhost"
type="subscribe" />
""")
self.send("""
<presence to="user@localhost"
type="subscribed" />
""")
self.send("""
<presence to="user@localhost">
<show>dnd</show>
</presence>
""")
suite = unittest.TestLoader().loadTestsFromTestCase(TestStreamRoster) suite = unittest.TestLoader().loadTestsFromTestCase(TestStreamRoster)