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, )
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")
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
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