Esempio n. 1
0
    def _gather_configuration(self):
        raw_jid = environ['HAL_JABBER_JID']
        jid = JID(raw_jid)

        domain = jid.getDomain()

        port = 5222

        try:
            parts = environ['HAL_JABBER_SERVER']
        except KeyError:
            host = domain
        else:
            parts = parts.split(':')
            try:
                host, port = parts
            except ValueError:
                (host,) = parts
            else:
                port = int(port)

        server = (host, int(port))

        return JabberConfiguration(
            user=jid.getNode(),
            domain=domain,
            password=environ['HAL_JABBER_PASSWORD'],
            rooms=environ['HAL_JABBER_ROOMS'].split(','),
            conference_server=environ.get('HAL_JABBER_CONFERENCE_SERVER') or 'conference.%s' % (domain,),
            server=server,
        )
Esempio n. 2
0
 def __init__(self):
     jid = JID(settings["jid"])
     conn = Client(jid.getDomain(), debug=[])
     if not conn.connect():
         statusMessage("PSTO: connection error")
     if not conn.auth(jid.getNode(), settings["password"], settings["res"]):
         statusMessage("PSTO: autentification error")
     self.conn = conn
def send_psto(jid, password, message):
    jid = JID(jid)
    conn = Client(jid.getDomain(), debug=[])

    if not conn.connect():
        logger.error("not connect to jabber")
    if not conn.auth(jid.getNode(), password, "Lastfm2Jabber"):
        logger.error("error auth to jabber")
    try:
        conn.send(Message(to="*****@*****.**",
                          body="* bot, /mu/ // %s" % message))
    except:
        logger.error("error sending message")
Esempio n. 4
0
class JabberBot(ErrBot):
    # Show types for presence
    AVAILABLE, AWAY, CHAT = None, 'away', 'chat'
    DND, XA, OFFLINE = 'dnd', 'xa', 'unavailable'

    # UI-messages (overwrite to change content)
    MSG_AUTHORIZE_ME = 'Hey there. You are not yet on my roster. '\
                       'Authorize my request and I will do the same.'
    MSG_NOT_AUTHORIZED = 'You did not authorize my subscription request. '\
                         'Access denied.'

    PING_FREQUENCY = 10 # Set to the number of seconds, e.g. 60.
    PING_TIMEOUT = 2 # Seconds to wait for a response.
    RETRY_FREQUENCY = 10 # Set to the number of seconds to attempt another connection attempt in case of connectivity loss

    return_code = 0 # code for the process exit

    def __init__(self, username, password, res=None, debug=False,
                 privatedomain=False, acceptownmsgs=False, handlers=None):
        """Initializes the jabber bot and sets up commands.

        username and password should be clear ;)

        If res provided, res will be ressourcename,
        otherwise it defaults to classname of childclass

        If debug is True log messages of xmpppy will be printed to console.
        Logging of Jabberbot itself is NOT affected.

        If privatedomain is provided, it should be either
        True to only allow subscriptions from the same domain
        as the bot or a string that describes the domain for
        which subscriptions are accepted (e.g. 'jabber.org').

        If acceptownmsgs it set to True, this bot will accept
        messages from the same JID that the bot itself has. This
        is useful when using JabberBot with a single Jabber account
        and multiple instances that want to talk to each other.

        If handlers are provided, default handlers won't be enabled.
        Usage like: [('stanzatype1', function1), ('stanzatype2', function2)]
        Signature of function should be callback_xx(self, conn, stanza),
        where conn is the connection and stanza the current stanza in process.
        First handler in list will be served first.
        Don't forget to raise exception xmpp.NodeProcessed to stop
        processing in other handlers (see callback_presence)
        """
        super(JabberBot, self).__init__()
        self.__debug = debug
        self.log = logging.getLogger(__name__)
        self.__username = username
        self.__password = password
        self.jid = JID(self.__username)
        self.res = (res or self.__class__.__name__)
        self.conn = None
        self.__finished = False
        self.__show = None
        self.__status = None
        self.__seen = {}
        self.__lastping = time.time()
        self.__privatedomain = privatedomain
        self.__acceptownmsgs = acceptownmsgs

        self.handlers = (handlers or [('message', self.callback_message),
            ('presence', self.callback_presence)])

        # Collect commands from source
        self.roster = None


    ################################

    def _send_status(self):
        """Send status to everyone"""
        pres = dispatcher.Presence(show=self.__show, status=self.__status)
        pres.setTag('c', namespace=NS_CAPS, attrs=HIPCHAT_PRESENCE_ATTRS)

        self.conn.send_message(pres)

    def __set_status(self, value):
        """Set status message.
        If value remains constant, no presence stanza will be send"""
        if self.__status != value:
            self.__status = value
            self._send_status()

    def __get_status(self):
        """Get current status message"""
        return self.__status

    status_message = property(fget=__get_status, fset=__set_status)

    def __set_show(self, value):
        """Set show (status type like AWAY, DND etc.).
        If value remains constant, no presence stanza will be send"""
        if self.__show != value:
            self.__show = value
            self._send_status()

    def __get_show(self):
        """Get current show (status type like AWAY, DND etc.)."""
        return self.__show

    status_type = property(fget=__get_show, fset=__set_show)

    ################################

    # meant to be overridden by XMPP backends variations
    def create_connection(self):
        if self.__debug:
            return JabberClient(self.jid.getDomain())
        return JabberClient(self.jid.getDomain(), debug=[])


    def connect(self):
        """Connects the bot to server or returns current connection,
        send inital presence stanza
        and registers handlers
        """
        if not self.conn:
            self.log.info('Start Connection ...........')
            conn = self.create_connection()
            conn.UnregisterDisconnectHandler(conn.DisconnectHandler)
            #connection attempt
            self.log.info('Connect attempt')
            conres = conn.connect()
            if not conres:
                self.log.error('unable to connect to server %s.' %
                               self.jid.getDomain())
                return None
            if conres != 'tls':
                self.log.warning('unable to establish secure connection - TLS failed!')
            self.log.info('Auth attempt')
            authres = conn.auth(self.jid.getNode(), self.__password, self.res)
            if not authres:
                self.log.error('unable to authorize with server.')
                return None
            if authres != 'sasl':
                self.log.warning("unable to perform SASL auth on %s. "\
                                 "Old authentication method used!" % self.jid.getDomain())
            self.log.info('Connection established')
            # Connection established - save connection
            self.conn = conn

            # Send initial presence stanza (say hello to everyone)
            self.log.info('Send Hello to everyone')
            self.conn.sendInitPresence()
            # Save roster and log Items
            self.log.info('Get Roster')
            self.roster = self.conn.Roster.getRoster()
            self.log.info('*** roster ***')
            for contact in self.roster.getItems():
                self.log.info('  %s' % contact)
            self.log.info('*** roster ***')

            # Register given handlers
            for (handler, callback) in self.handlers:
                self.conn.RegisterHandler(handler, callback)
                self.log.info('Registered handler: %s' % handler)
            self.log.info('............ Connection Done')

        return self.conn

    def join_room(self, room, username=None, password=None):
        """Join the specified multi-user chat room

        If username is NOT provided fallback to node part of JID"""
        NS_MUC = 'http://jabber.org/protocol/muc'
        if username is None:
            username = self.__username.split('@')[0]
        my_room_JID = '/'.join((room, username))
        pres = dispatcher.Presence(to=my_room_JID) #, frm=self.__username + '/bot')
        pres.setTag('c', namespace=NS_CAPS, attrs=HIPCHAT_PRESENCE_ATTRS)
        t = pres.setTag('x', namespace=NS_MUC)
        if password is not None:
            t.setTagData('password', password)
        self.log.info(pres)
        self.send_message(pres)

    def kick(self, room, nick, reason=None):
        """Kicks user from muc
        Works only with sufficient rights."""
        NS_MUCADMIN = 'http://jabber.org/protocol/muc#admin'
        item = simplexml.Node('item')
        item.setAttr('nick', nick)
        item.setAttr('role', 'none')
        iq = Iq(typ='set', queryNS=NS_MUCADMIN, xmlns=None, to=room, payload=item)
        if reason is not None:
            item.setTagData('reason', reason)
            self.connect().send(iq)

    def invite(self, room, jids, reason=None):
        """Invites user to muc.
        Works only if user has permission to invite to muc"""
        NS_MUCUSER = '******'
        mess = Message(to=room)
        for jid in jids:
            invite = simplexml.Node('invite')
            invite.setAttr('to', jid)
            if reason is not None:
                invite.setTagData('reason', reason)
            mess.setTag('x', namespace=NS_MUCUSER).addChild(node=invite)
        self.log.info(mess)
        self.connect().send(mess)

    def quit(self, return_code = -1):
        """Stop serving messages and exit.

        I find it is handy for development to run the
        jabberbot in a 'while true' loop in the shell, so
        whenever I make a code change to the bot, I send
        the 'reload' command, which I have mapped to call
        self.quit(), and my shell script relaunches the
        new version.
        """
        self.__finished = True
        self.return_code = return_code

    def send_tune(self, song, debug=False):
        """Set information about the currently played tune

        Song is a dictionary with keys: file, title, artist, album, pos, track,
        length, uri. For details see <http://xmpp.org/protocols/tune/>.
        """
        NS_TUNE = 'http://jabber.org/protocol/tune'
        iq = Iq(typ='set')
        iq.setFrom(self.jid)
        iq.pubsub = iq.addChild('pubsub', namespace=NS_PUBSUB)
        iq.pubsub.publish = iq.pubsub.addChild('publish',
            attrs={'node': NS_TUNE})
        iq.pubsub.publish.item = iq.pubsub.publish.addChild('item',
            attrs={'id': 'current'})
        tune = iq.pubsub.publish.item.addChild('tune')
        tune.setNamespace(NS_TUNE)

        title = None
        if song.has_key('title'):
            title = song['title']
        elif song.has_key('file'):
            title = os.path.splitext(os.path.basename(song['file']))[0]
        if title is not None:
            tune.addChild('title').addData(title)
        if song.has_key('artist'):
            tune.addChild('artist').addData(song['artist'])
        if song.has_key('album'):
            tune.addChild('source').addData(song['album'])
        if song.has_key('pos') and song['pos'] > 0:
            tune.addChild('track').addData(str(song['pos']))
        if song.has_key('time'):
            tune.addChild('length').addData(str(song['time']))
        if song.has_key('uri'):
            tune.addChild('uri').addData(song['uri'])

        if debug:
            self.log.info('Sending tune: %s' % iq.__str__().encode('utf8'))
        self.conn.send_message(iq)

    def build_message(self, text):
        """Builds an xhtml message without attributes.
        If input is not valid xhtml-im fallback to normal."""
        try:
            node = XML2Node(text)
            # logging.debug('This message is XML : %s' % text)
            text_plain = xhtml2txt(text)
            logging.debug('Plain Text translation from XHTML-IM:\n%s' % text_plain)
            message = Message(body=text_plain)
            message.addChild(node = node)
        except ExpatError as ee:
            if text.strip(): # avoids keep alive pollution
                logging.debug('Determined that [%s] is not XHTML-IM (%s)' % (text, ee))
            message = Message(body=text)
        return message

    def get_full_jids(self, jid):
        """Returns all full jids, which belong to a bare jid

        Example: A bare jid is [email protected], with two clients connected,
        which
        have the full jids [email protected]/home and [email protected]/work."""
        for res in self.roster.getResources(jid):
            full_jid = "%s/%s" % (jid, res)
            yield full_jid

    def status_type_changed(self, jid, new_status_type):
        """Callback for tracking status types (dnd, away, offline, ...)"""
        self.log.debug('user %s changed status to %s' % (jid, new_status_type))

    def status_message_changed(self, jid, new_status_message):
        """Callback for tracking status messages (the free-form status text)"""
        self.log.debug('user %s updated text to %s' %
                       (jid, new_status_message))

    def broadcast(self, message, only_available=False):
        """Broadcast a message to all users 'seen' by this bot.

        If the parameter 'only_available' is True, the broadcast
        will not go to users whose status is not 'Available'."""
        for jid, (show, status) in self.__seen.items():
            print str(jid) + ' - ' + str(show) + ' - ' + str(status)
            if not only_available or show is self.AVAILABLE:
                self.send(jid, message)
            else:
                print 'not available'

    def callback_presence(self, conn, presence):
        self.__lastping = time.time()
        jid, type_, show, status = presence.getFrom(),\
                                   presence.getType(), presence.getShow(),\
                                   presence.getStatus()

        if self.jid.bareMatch(jid):
            # update internal status
            if type_ != self.OFFLINE:
                self.__status = status
                self.__show = show
            else:
                self.__status = ""
                self.__show = self.OFFLINE
            if not self.__acceptownmsgs:
                # Ignore our own presence messages
                return

        if type_ is None:
            # Keep track of status message and type changes
            old_show, old_status = self.__seen.get(jid, (self.OFFLINE, None))
            if old_show != show:
                self.status_type_changed(jid, show)

            if old_status != status:
                self.status_message_changed(jid, status)

            self.__seen[jid] = (show, status)
        elif type_ == self.OFFLINE and jid in self.__seen:
            # Notify of user offline status change
            del self.__seen[jid]
            self.status_type_changed(jid, self.OFFLINE)

        try:
            subscription = self.roster.getSubscription(unicode(jid.__str__()))
        except KeyError, e:
            # User not on our roster
            subscription = None
        except AttributeError, e:
            # Recieved presence update before roster built
            return
Esempio n. 5
0
class JabberBot(ErrBot):
    # Show types for presence
    AVAILABLE, AWAY, CHAT = None, 'away', 'chat'
    DND, XA, OFFLINE = 'dnd', 'xa', 'unavailable'

    # UI-messages (overwrite to change content)
    MSG_AUTHORIZE_ME = 'Hey there. You are not yet on my roster. '\
                       'Authorize my request and I will do the same.'
    MSG_NOT_AUTHORIZED = 'You did not authorize my subscription request. '\
                         'Access denied.'

    PING_FREQUENCY = 10  # Set to the number of seconds, e.g. 60.
    PING_TIMEOUT = 2  # Seconds to wait for a response.
    RETRY_FREQUENCY = 10  # Set to the number of seconds to attempt another connection attempt in case of connectivity loss

    return_code = 0  # code for the process exit

    def __init__(self,
                 username,
                 password,
                 res=None,
                 debug=False,
                 privatedomain=False,
                 acceptownmsgs=False,
                 handlers=None):
        """Initializes the jabber bot and sets up commands.

        username and password should be clear ;)

        If res provided, res will be ressourcename,
        otherwise it defaults to classname of childclass

        If debug is True log messages of xmpppy will be printed to console.
        Logging of Jabberbot itself is NOT affected.

        If privatedomain is provided, it should be either
        True to only allow subscriptions from the same domain
        as the bot or a string that describes the domain for
        which subscriptions are accepted (e.g. 'jabber.org').

        If acceptownmsgs it set to True, this bot will accept
        messages from the same JID that the bot itself has. This
        is useful when using JabberBot with a single Jabber account
        and multiple instances that want to talk to each other.

        If handlers are provided, default handlers won't be enabled.
        Usage like: [('stanzatype1', function1), ('stanzatype2', function2)]
        Signature of function should be callback_xx(self, conn, stanza),
        where conn is the connection and stanza the current stanza in process.
        First handler in list will be served first.
        Don't forget to raise exception xmpp.NodeProcessed to stop
        processing in other handlers (see callback_presence)
        """
        super(JabberBot, self).__init__()
        self.__debug = debug
        self.log = logging.getLogger(__name__)
        self.__username = username
        self.__password = password
        self.jid = JID(self.__username)
        self.res = (res or self.__class__.__name__)
        self.conn = None
        self.__finished = False
        self.__show = None
        self.__status = None
        self.__seen = {}
        self.__lastping = time.time()
        self.__privatedomain = privatedomain
        self.__acceptownmsgs = acceptownmsgs

        self.handlers = (handlers or [('message', self.callback_message),
                                      ('presence', self.callback_presence)])

        # Collect commands from source
        self.roster = None

    ################################

    def _send_status(self):
        """Send status to everyone"""
        pres = dispatcher.Presence(show=self.__show, status=self.__status)
        pres.setTag('c', namespace=NS_CAPS, attrs=HIPCHAT_PRESENCE_ATTRS)

        self.conn.send_message(pres)

    def __set_status(self, value):
        """Set status message.
        If value remains constant, no presence stanza will be send"""
        if self.__status != value:
            self.__status = value
            self._send_status()

    def __get_status(self):
        """Get current status message"""
        return self.__status

    status_message = property(fget=__get_status, fset=__set_status)

    def __set_show(self, value):
        """Set show (status type like AWAY, DND etc.).
        If value remains constant, no presence stanza will be send"""
        if self.__show != value:
            self.__show = value
            self._send_status()

    def __get_show(self):
        """Get current show (status type like AWAY, DND etc.)."""
        return self.__show

    status_type = property(fget=__get_show, fset=__set_show)

    ################################

    # meant to be overridden by XMPP backends variations
    def create_connection(self):
        if self.__debug:
            return JabberClient(self.jid.getDomain())
        return JabberClient(self.jid.getDomain(), debug=[])

    def connect(self):
        """Connects the bot to server or returns current connection,
        send inital presence stanza
        and registers handlers
        """
        if not self.conn:
            self.log.info('Start Connection ...........')
            conn = self.create_connection()
            conn.UnregisterDisconnectHandler(conn.DisconnectHandler)
            #connection attempt
            self.log.info('Connect attempt')
            conres = conn.connect()
            if not conres:
                self.log.error('unable to connect to server %s.' %
                               self.jid.getDomain())
                return None
            if conres != 'tls':
                self.log.warning(
                    'unable to establish secure connection - TLS failed!')
            self.log.info('Auth attempt')
            authres = conn.auth(self.jid.getNode(), self.__password, self.res)
            if not authres:
                self.log.error('unable to authorize with server.')
                return None
            if authres != 'sasl':
                self.log.warning("unable to perform SASL auth on %s. "\
                                 "Old authentication method used!" % self.jid.getDomain())
            self.log.info('Connection established')
            # Connection established - save connection
            self.conn = conn

            # Send initial presence stanza (say hello to everyone)
            self.log.info('Send Hello to everyone')
            self.conn.sendInitPresence()
            # Save roster and log Items
            self.log.info('Get Roster')
            self.roster = self.conn.Roster.getRoster()
            self.log.info('*** roster ***')
            for contact in self.roster.getItems():
                self.log.info('  %s' % contact)
            self.log.info('*** roster ***')

            # Register given handlers
            for (handler, callback) in self.handlers:
                self.conn.RegisterHandler(handler, callback)
                self.log.info('Registered handler: %s' % handler)
            self.log.info('............ Connection Done')

        return self.conn

    def join_room(self, room, username=None, password=None):
        """Join the specified multi-user chat room

        If username is NOT provided fallback to node part of JID"""
        NS_MUC = 'http://jabber.org/protocol/muc'
        if username is None:
            username = self.__username.split('@')[0]
        my_room_JID = '/'.join((room, username))
        pres = dispatcher.Presence(
            to=my_room_JID)  #, frm=self.__username + '/bot')
        pres.setTag('c', namespace=NS_CAPS, attrs=HIPCHAT_PRESENCE_ATTRS)
        t = pres.setTag('x', namespace=NS_MUC)
        if password is not None:
            t.setTagData('password', password)
        self.log.info(pres)
        self.send_message(pres)

    def kick(self, room, nick, reason=None):
        """Kicks user from muc
        Works only with sufficient rights."""
        NS_MUCADMIN = 'http://jabber.org/protocol/muc#admin'
        item = simplexml.Node('item')
        item.setAttr('nick', nick)
        item.setAttr('role', 'none')
        iq = Iq(typ='set',
                queryNS=NS_MUCADMIN,
                xmlns=None,
                to=room,
                payload=item)
        if reason is not None:
            item.setTagData('reason', reason)
            self.connect().send(iq)

    def invite(self, room, jids, reason=None):
        """Invites user to muc.
        Works only if user has permission to invite to muc"""
        NS_MUCUSER = '******'
        mess = Message(to=room)
        for jid in jids:
            invite = simplexml.Node('invite')
            invite.setAttr('to', jid)
            if reason is not None:
                invite.setTagData('reason', reason)
            mess.setTag('x', namespace=NS_MUCUSER).addChild(node=invite)
        self.log.info(mess)
        self.connect().send(mess)

    def quit(self, return_code=-1):
        """Stop serving messages and exit.

        I find it is handy for development to run the
        jabberbot in a 'while true' loop in the shell, so
        whenever I make a code change to the bot, I send
        the 'reload' command, which I have mapped to call
        self.quit(), and my shell script relaunches the
        new version.
        """
        self.__finished = True
        self.return_code = return_code

    def send_tune(self, song, debug=False):
        """Set information about the currently played tune

        Song is a dictionary with keys: file, title, artist, album, pos, track,
        length, uri. For details see <http://xmpp.org/protocols/tune/>.
        """
        NS_TUNE = 'http://jabber.org/protocol/tune'
        iq = Iq(typ='set')
        iq.setFrom(self.jid)
        iq.pubsub = iq.addChild('pubsub', namespace=NS_PUBSUB)
        iq.pubsub.publish = iq.pubsub.addChild('publish',
                                               attrs={'node': NS_TUNE})
        iq.pubsub.publish.item = iq.pubsub.publish.addChild(
            'item', attrs={'id': 'current'})
        tune = iq.pubsub.publish.item.addChild('tune')
        tune.setNamespace(NS_TUNE)

        title = None
        if song.has_key('title'):
            title = song['title']
        elif song.has_key('file'):
            title = os.path.splitext(os.path.basename(song['file']))[0]
        if title is not None:
            tune.addChild('title').addData(title)
        if song.has_key('artist'):
            tune.addChild('artist').addData(song['artist'])
        if song.has_key('album'):
            tune.addChild('source').addData(song['album'])
        if song.has_key('pos') and song['pos'] > 0:
            tune.addChild('track').addData(str(song['pos']))
        if song.has_key('time'):
            tune.addChild('length').addData(str(song['time']))
        if song.has_key('uri'):
            tune.addChild('uri').addData(song['uri'])

        if debug:
            self.log.info('Sending tune: %s' % iq.__str__().encode('utf8'))
        self.conn.send_message(iq)

    def build_message(self, text):
        """Builds an xhtml message without attributes.
        If input is not valid xhtml-im fallback to normal."""
        try:
            node = XML2Node(text)
            # logging.debug('This message is XML : %s' % text)
            text_plain = xhtml2txt(text)
            logging.debug('Plain Text translation from XHTML-IM:\n%s' %
                          text_plain)
            message = Message(body=text_plain)
            message.addChild(node=node)
        except ExpatError as ee:
            if text.strip():  # avoids keep alive pollution
                logging.debug('Determined that [%s] is not XHTML-IM (%s)' %
                              (text, ee))
            message = Message(body=text)
        return message

    def get_full_jids(self, jid):
        """Returns all full jids, which belong to a bare jid

        Example: A bare jid is [email protected], with two clients connected,
        which
        have the full jids [email protected]/home and [email protected]/work."""
        for res in self.roster.getResources(jid):
            full_jid = "%s/%s" % (jid, res)
            yield full_jid

    def status_type_changed(self, jid, new_status_type):
        """Callback for tracking status types (dnd, away, offline, ...)"""
        self.log.debug('user %s changed status to %s' % (jid, new_status_type))

    def status_message_changed(self, jid, new_status_message):
        """Callback for tracking status messages (the free-form status text)"""
        self.log.debug('user %s updated text to %s' %
                       (jid, new_status_message))

    def broadcast(self, message, only_available=False):
        """Broadcast a message to all users 'seen' by this bot.

        If the parameter 'only_available' is True, the broadcast
        will not go to users whose status is not 'Available'."""
        for jid, (show, status) in self.__seen.items():
            print str(jid) + ' - ' + str(show) + ' - ' + str(status)
            if not only_available or show is self.AVAILABLE:
                self.send(jid, message)
            else:
                print 'not available'

    def callback_presence(self, conn, presence):
        self.__lastping = time.time()
        jid, type_, show, status = presence.getFrom(),\
                                   presence.getType(), presence.getShow(),\
                                   presence.getStatus()

        if self.jid.bareMatch(jid):
            # update internal status
            if type_ != self.OFFLINE:
                self.__status = status
                self.__show = show
            else:
                self.__status = ""
                self.__show = self.OFFLINE
            if not self.__acceptownmsgs:
                # Ignore our own presence messages
                return

        if type_ is None:
            # Keep track of status message and type changes
            old_show, old_status = self.__seen.get(jid, (self.OFFLINE, None))
            if old_show != show:
                self.status_type_changed(jid, show)

            if old_status != status:
                self.status_message_changed(jid, status)

            self.__seen[jid] = (show, status)
        elif type_ == self.OFFLINE and jid in self.__seen:
            # Notify of user offline status change
            del self.__seen[jid]
            self.status_type_changed(jid, self.OFFLINE)

        try:
            subscription = self.roster.getSubscription(unicode(jid.__str__()))
        except KeyError, e:
            # User not on our roster
            subscription = None
        except AttributeError, e:
            # Recieved presence update before roster built
            return