diff options
-rw-r--r-- | sleekxmpp/stanza/presence.py | 183 | ||||
-rw-r--r-- | tests/test_presencestanzas.py | 96 |
2 files changed, 185 insertions, 94 deletions
diff --git a/sleekxmpp/stanza/presence.py b/sleekxmpp/stanza/presence.py index ec681763..651bf34d 100644 --- a/sleekxmpp/stanza/presence.py +++ b/sleekxmpp/stanza/presence.py @@ -6,54 +6,141 @@ See the file LICENSE for copying permission. """ -from . error import Error -from . rootstanza import RootStanza -from .. xmlstream.stanzabase import StanzaBase, ET +from sleekxmpp.stanza import Error +from sleekxmpp.stanza.rootstanza import RootStanza +from sleekxmpp.xmlstream.stanzabase import StanzaBase, ET class Presence(RootStanza): - interfaces = set(('type', 'to', 'from', 'id', 'show', 'status', 'priority')) - types = set(('available', 'unavailable', 'error', 'probe', 'subscribe', 'subscribed', 'unsubscribe', 'unsubscribed')) - showtypes = set(('dnd', 'chat', 'xa', 'away')) - sub_interfaces = set(('show', 'status', 'priority')) - name = 'presence' - plugin_attrib = name - namespace = 'jabber:client' - - def setShow(self, show): - if show in self.showtypes: - self._setSubText('show', text=show) - return self - - def setType(self, value): - if value in self.types: - self['show'] = None - if value == 'available': - value = '' - self._setAttr('type', value) - elif value in self.showtypes: - self['show'] = value - return self - - def setPriority(self, value): - self._setSubText('priority', text = str(value)) - - def getPriority(self): - p = self._getSubText('priority') - if not p: p = 0 - return int(p) - - def getType(self): - out = self._getAttr('type') - if not out: - out = self['show'] - if not out or out is None: - out = 'available' - return out - - def reply(self): - if self['type'] == 'unsubscribe': - self['type'] = 'unsubscribed' - elif self['type'] == 'subscribe': - self['type'] = 'subscribed' - return StanzaBase.reply(self) + + """ + XMPP's <presence> stanza allows entities to know the status of other + clients and components. Since it is currently the only multi-cast + stanza in XMPP, many extensions add more information to <presence> + stanzas to broadcast to every entry in the roster, such as + capabilities, music choices, or locations (XEP-0115: Entity Capabilities + and XEP-0163: Personal Eventing Protocol). + + Since <presence> stanzas are broadcast when an XMPP entity changes + its status, the bulk of the traffic in an XMPP network will be from + <presence> stanzas. Therefore, do not include more information than + necessary in a status message or within a <presence> stanza in order + to help keep the network running smoothly. + + Example <presence> stanzas: + <presence /> + + <presence from="user@example.com"> + <show>away</show> + <status>Getting lunch.</status> + <priority>5</priority> + </presence> + + <presence type="unavailable" /> + + <presence to="user@otherhost.com" type="subscribe" /> + + Stanza Interface: + priority -- A value used by servers to determine message routing. + show -- The type of status, such as away or available for chat. + status -- Custom, human readable status message. + + Attributes: + types -- One of: available, unavailable, error, probe, + subscribe, subscribed, unsubscribe, + and unsubscribed. + showtypes -- One of: away, chat, dnd, and xa. + + Methods: + reply -- Overrides StanzaBase.reply + setShow -- Set the value of the <show> element. + getType -- Get the value of the type attribute or <show> element. + setType -- Set the value of the type attribute or <show> element. + getPriority -- Get the value of the <priority> element. + setPriority -- Set the value of the <priority> element. + """ + + namespace = 'jabber:client' + name = 'presence' + interfaces = set(('type', 'to', 'from', 'id', 'show', + 'status', 'priority')) + sub_interfaces = set(('show', 'status', 'priority')) + plugin_attrib = name + + types = set(('available', 'unavailable', 'error', 'probe', 'subscribe', + 'subscribed', 'unsubscribe', 'unsubscribed')) + showtypes = set(('dnd', 'chat', 'xa', 'away')) + + def setShow(self, show): + """ + Set the value of the <show> element. + + Arguments: + show -- Must be one of: away, chat, dnd, or xa. + """ + if show in self.showtypes: + self._setSubText('show', text=show) + return self + + def setType(self, value): + """ + Set the type attribute's value, and the <show> element + if applicable. + + Arguments: + value -- Must be in either self.types or self.showtypes. + """ + if value in self.types: + self['show'] = None + if value == 'available': + value = '' + self._setAttr('type', value) + elif value in self.showtypes: + self['show'] = value + return self + + def setPriority(self, value): + """ + Set the entity's priority value. Some server use priority to + determine message routing behavior. + + Bot clients should typically use a priority of 0 if the same + JID is used elsewhere by a human-interacting client. + + Arguments: + value -- An integer value greater than or equal to 0. + """ + self._setSubText('priority', text=str(value)) + + def getPriority(self): + """ + Return the value of the <presence> element as an integer. + """ + p = self._getSubText('priority') + if not p: + p = 0 + return int(p) + + def getType(self): + """ + Return the value of the <presence> stanza's type attribute, or + the value of the <show> element. + """ + out = self._getAttr('type') + if not out: + out = self['show'] + if not out or out is None: + out = 'available' + return out + + def reply(self): + """ + Set the appropriate presence reply type. + + Overrides StanzaBase.reply. + """ + if self['type'] == 'unsubscribe': + self['type'] = 'unsubscribed' + elif self['type'] == 'subscribe': + self['type'] = 'subscribed' + return StanzaBase.reply(self) diff --git a/tests/test_presencestanzas.py b/tests/test_presencestanzas.py index 02799c8f..2cab3af7 100644 --- a/tests/test_presencestanzas.py +++ b/tests/test_presencestanzas.py @@ -2,52 +2,56 @@ import sleekxmpp from sleektest import * from sleekxmpp.stanza.presence import Presence + class TestPresenceStanzas(SleekTest): - - def testPresenceShowRegression(self): - """Regression check presence['type'] = 'dnd' show value working""" - p = self.Presence() - p['type'] = 'dnd' - self.checkPresence(p, """ - <presence><show>dnd</show></presence> - """) - - def testPresenceType(self): - """Test manipulating presence['type']""" - p = self.Presence() - p['type'] = 'available' - self.checkPresence(p, """ - <presence /> - """) - self.failUnless(p['type'] == 'available', "Incorrect presence['type'] for type 'available'") - - for showtype in ['away', 'chat', 'dnd', 'xa']: - p['type'] = showtype - self.checkPresence(p, """ - <presence><show>%s</show></presence> - """ % showtype) - self.failUnless(p['type'] == showtype, "Incorrect presence['type'] for type '%s'" % showtype) - - p['type'] = None - self.checkPresence(p, """ - <presence /> - """) - - def testPresenceUnsolicitedOffline(self): - """Unsolicted offline presence does not spawn changed_status or update roster""" - p = self.Presence() - p['type'] = 'unavailable' - p['from'] = 'bill@chadmore.com/gmail15af' - - c = sleekxmpp.ClientXMPP('crap@wherever', 'password') - happened = [] - def handlechangedpresence(event): - happened.append(True) - c.add_event_handler("changed_status", handlechangedpresence) - c._handlePresence(p) - - self.failUnless(happened == [], "changed_status event triggered for superfulous unavailable presence") - self.failUnless(c.roster == {}, "Roster updated for superfulous unavailable presence") - + + def testPresenceShowRegression(self): + """Regression check presence['type'] = 'dnd' show value working""" + p = self.Presence() + p['type'] = 'dnd' + self.checkPresence(p, "<presence><show>dnd</show></presence>") + + def testPresenceType(self): + """Test manipulating presence['type']""" + p = self.Presence() + p['type'] = 'available' + self.checkPresence(p, "<presence />") + self.failUnless(p['type'] == 'available', + "Incorrect presence['type'] for type 'available'") + + for showtype in ['away', 'chat', 'dnd', 'xa']: + p['type'] = showtype + self.checkPresence(p, """ + <presence><show>%s</show></presence> + """ % showtype) + self.failUnless(p['type'] == showtype, + "Incorrect presence['type'] for type '%s'" % showtype) + + p['type'] = None + self.checkPresence(p, "<presence />") + + def testPresenceUnsolicitedOffline(self): + """ + Unsolicted offline presence does not spawn changed_status + or update the roster. + """ + p = self.Presence() + p['type'] = 'unavailable' + p['from'] = 'bill@chadmore.com/gmail15af' + + c = sleekxmpp.ClientXMPP('crap@wherever', 'password') + happened = [] + + def handlechangedpresence(event): + happened.append(True) + + c.add_event_handler("changed_status", handlechangedpresence) + c._handlePresence(p) + + self.failUnless(happened == [], + "changed_status event triggered for extra unavailable presence") + self.failUnless(c.roster == {}, + "Roster updated for superfulous unavailable presence") + suite = unittest.TestLoader().loadTestsFromTestCase(TestPresenceStanzas) |