diff -Nru jabberbot-0.14/AUTHORS jabberbot-0.15/AUTHORS --- jabberbot-0.14/AUTHORS 2011-07-31 10:01:39.000000000 +0000 +++ jabberbot-0.15/AUTHORS 2011-08-29 13:09:11.000000000 +0000 @@ -13,4 +13,5 @@ René Mayrhofer Andreas Zwinkau Fabian Deutsch +Richard Marko diff -Nru jabberbot-0.14/debian/changelog jabberbot-0.15/debian/changelog --- jabberbot-0.14/debian/changelog 2011-12-07 18:56:53.000000000 +0000 +++ jabberbot-0.15/debian/changelog 2012-06-03 10:39:11.000000000 +0000 @@ -1,3 +1,15 @@ +jabberbot (0.15-1) unstable; urgency=low + + * New upstream release + * debian/control + - Bump Standards-Version to 3.9.3 + - Replaced Uploaders by chaica@debian.org + * debian/copyright + - Update the upstream copyright + - Update the Debian packaging copyright + + -- Carl Chenet Sun, 03 Jun 2012 12:38:32 +0200 + jabberbot (0.14-1) unstable; urgency=low * New upstream release (Closes: #643995) diff -Nru jabberbot-0.14/debian/control jabberbot-0.15/debian/control --- jabberbot-0.14/debian/control 2011-12-07 18:56:53.000000000 +0000 +++ jabberbot-0.15/debian/control 2012-06-03 10:31:27.000000000 +0000 @@ -3,9 +3,9 @@ Section: python X-Python-Version: >= 2.5 Maintainer: Debian Python Modules Team -Uploaders: Carl Chenet +Uploaders: Carl Chenet Build-Depends: debhelper (>= 7.0.50~), python(>= 2.6.6-3), python-xmpp -Standards-Version: 3.9.2.0 +Standards-Version: 3.9.3 Homepage: http://thpinfo.com/2007/python-jabberbot/ Vcs-Svn: svn://svn.debian.org/svn/python-modules/packages/jabberbot/trunk/ Vcs-Browser: http://svn.debian.org/wsvn/python-modules/packages/jabberbot/trunk/ diff -Nru jabberbot-0.14/debian/copyright jabberbot-0.15/debian/copyright --- jabberbot-0.14/debian/copyright 2011-02-22 13:07:02.000000000 +0000 +++ jabberbot-0.15/debian/copyright 2012-06-03 10:38:28.000000000 +0000 @@ -12,7 +12,7 @@ Copyright: - Copyright (c) 2007-2011 Thomas Perl + Copyright (c) 2007-2012 Thomas Perl except for this file: @@ -37,5 +37,5 @@ On Debian systems, the complete text of the GNU General Public License can be found in `/usr/share/common-licenses/GPL-3'. -The Debian packaging is © 2009, Chenet Carl and +The Debian packaging is © 2012, Chenet Carl and is licensed under the GPL-3 or any later version, see above. diff -Nru jabberbot-0.14/examples/uptime.py jabberbot-0.15/examples/uptime.py --- jabberbot-0.14/examples/uptime.py 1970-01-01 00:00:00.000000000 +0000 +++ jabberbot-0.15/examples/uptime.py 2012-02-09 19:05:44.000000000 +0000 @@ -0,0 +1,41 @@ +#!/usr/bin/python +# +# uptime.py: Basic JabberBot example with command prefix +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . +# +# Homepage: http://thp.io/2007/python-jabberbot/ +# + +import subprocess +import sys + +from jabberbot import * + +class UptimeBot(JabberBot): + @botcmd + def uptime(self, mess, args): + """Get current uptime information""" + return subprocess.check_output('uptime') + +if __name__ == '__main__': + if len(sys.argv) != 3: + print >>sys.stderr, """ + Usage: %s + """ % sys.argv[0] + + username, password = sys.argv[1:] + uptime_bot = UptimeBot(username, password, command_prefix='.') + uptime_bot.serve_forever() + diff -Nru jabberbot-0.14/jabberbot.py jabberbot-0.15/jabberbot.py --- jabberbot-0.14/jabberbot.py 2011-07-31 10:18:11.000000000 +0000 +++ jabberbot-0.15/jabberbot.py 2012-03-08 11:24:33.000000000 +0000 @@ -2,8 +2,8 @@ # -*- coding: utf-8 -*- # JabberBot: A simple jabber/xmpp bot framework -# Copyright (c) 2007-2011 Thomas Perl -# $Id: 40d08966900594ddefb8d3302290c2be097ab2b8 $ +# Copyright (c) 2007-2012 Thomas Perl +# $Id: 32c2cb16c60352ee527896f200a8b623147e75ff $ # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by @@ -31,7 +31,7 @@ import os import re import sys -import cgi +import thread try: import xmpp @@ -49,17 +49,19 @@ # Will be parsed by setup.py to determine package metadata __author__ = 'Thomas Perl ' -__version__ = '0.14' +__version__ = '0.15' __website__ = 'http://thp.io/2007/python-jabberbot/' __license__ = 'GNU General Public License version 3 or later' + def botcmd(*args, **kwargs): """Decorator for bot command functions""" - def decorate(func, hidden=False, name=None): + def decorate(func, hidden=False, name=None, thread=False): setattr(func, '_jabberbot_command', True) - setattr(func, '_jabberbot_hidden', hidden) + setattr(func, '_jabberbot_command_hidden', hidden) setattr(func, '_jabberbot_command_name', name or func.__name__) + setattr(func, '_jabberbot_command_thread', thread) # Experimental! return func if len(args): @@ -69,22 +71,39 @@ class JabberBot(object): - AVAILABLE, AWAY, CHAT, DND, XA, OFFLINE = None, 'away', 'chat', 'dnd', 'xa', 'unavailable' - - 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.' - MSG_UNKNOWN_COMMAND = 'Unknown command: "%(command)s". Type "help" for available commands.' - MSG_HELP_TAIL = 'Type help to get more info about that specific command.' + # 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.' + MSG_UNKNOWN_COMMAND = 'Unknown command: "%(command)s". '\ + 'Type "%(helpcommand)s" for available commands.' + MSG_HELP_TAIL = 'Type %(helpcommand)s to get more info '\ + 'about that specific command.' MSG_HELP_UNDEFINED_COMMAND = 'That command is not defined.' - MSG_ERROR_OCCURRED = 'Sorry for your inconvenience. An unexpected error occurred.' + MSG_ERROR_OCCURRED = 'Sorry for your inconvenience. '\ + 'An unexpected error occurred.' - PING_FREQUENCY = 0 # Set to the number of seconds, e.g. 60. - PING_TIMEOUT = 2 # Seconds to wait for a response. + PING_FREQUENCY = 0 # Set to the number of seconds, e.g. 60. + PING_TIMEOUT = 2 # Seconds to wait for a response. def __init__(self, username, password, res=None, debug=False, - privatedomain=False, acceptownmsgs=False): + privatedomain=False, acceptownmsgs=False, handlers=None, + command_prefix=''): """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 @@ -94,7 +113,20 @@ 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) + + If command_prefix is set to a string different from '' (the empty + string), it will require the commands to be prefixed with this text, + e.g. command_prefix = '!' means: Type "!info" for the "info" command. """ + # TODO sort this initialisation thematically self.__debug = debug self.log = logging.getLogger(__name__) self.__username = username @@ -110,39 +142,50 @@ self.__lastping = time.time() self.__privatedomain = privatedomain self.__acceptownmsgs = acceptownmsgs + self.__command_prefix = command_prefix - self.custom_message_handler = None + self.handlers = (handlers or [('message', self.callback_message), + ('presence', self.callback_presence)]) + # Collect commands from source self.commands = {} - for name, value in inspect.getmembers(self): - if inspect.ismethod(value) and getattr(value, '_jabberbot_command', False): + for name, value in inspect.getmembers(self, inspect.ismethod): + if getattr(value, '_jabberbot_command', False): name = getattr(value, '_jabberbot_command_name') self.log.info('Registered command: %s' % name) - self.commands[name] = value + self.commands[self.__command_prefix + name] = value self.roster = None ################################ def _send_status(self): - self.conn.send(xmpp.dispatcher.Presence(show=self.__show, status=self.__status)) + """Send status to everyone""" + self.conn.send(xmpp.dispatcher.Presence(show=self.__show, + status=self.__status)) 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) @@ -150,49 +193,95 @@ ################################ def connect(self): + """Connects the bot to server or returns current connection, + send inital presence stanza + and registers handlers + """ if not self.conn: + # TODO improve debug if self.__debug: conn = xmpp.Client(self.jid.getDomain()) else: conn = xmpp.Client(self.jid.getDomain(), debug=[]) + #connection attempt conres = conn.connect() if not conres: - self.log.error('unable to connect to server %s.' % self.jid.getDomain()) + 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.warning('unable to establish secure connection '\ + '- TLS failed!') 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 os %s. Old authentication method used!" % self.jid.getDomain()) + self.log.warning("unable to perform SASL auth on %s. "\ + "Old authentication method used!" % self.jid.getDomain()) - conn.sendInitPresence() + # Connection established - save connection self.conn = conn + + # Send initial presence stanza (say hello to everyone) + self.conn.sendInitPresence() + # Save roster and log Items 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 ***') - self.conn.RegisterHandler('message', self.callback_message) - self.conn.RegisterHandler('presence', self.callback_presence) + + # Register given handlers (TODO move to own function) + for (handler, callback) in self.handlers: + self.conn.RegisterHandler(handler, callback) + self.log.debug('Registered handler: %s' % handler) return self.conn def join_room(self, room, username=None, password=None): - """Join the specified multi-user chat room""" + """Join the specified multi-user chat room + + If username is NOT provided fallback to node part of JID""" + # TODO fix namespacestrings and history settings NS_MUC = 'http://jabber.org/protocol/muc' if username is None: + # TODO use xmpppy function getNode username = self.__username.split('@')[0] my_room_JID = '/'.join((room, username)) pres = xmpp.Presence(to=my_room_JID) if password is not None: - pres.setTag('x',namespace=NS_MUC).setTagData('password',password) + pres.setTag('x', namespace=NS_MUC).setTagData('password', password) self.connect().send(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 = xmpp.simplexml.Node('item') + item.setAttr('nick', nick) + item.setAttr('role', 'none') + iq = xmpp.Iq(typ='set', queryNS=NS_MUCADMIN, xmlns=None, to=room, + payload=set([item])) + if reason is not None: + item.setTagData('reason', reason) + self.connect().send(iq) + + def invite(self, room, jid, reason=None): + """Invites user to muc. + Works only if user has permission to invite to muc""" + NS_MUCUSER = 'http://jabber.org/protocol/muc#user' + invite = xmpp.simplexml.Node('invite') + invite.setAttr('to', jid) + if reason is not None: + invite.setTagData('reason', reason) + mess = xmpp.Message(to=room) + mess.setTag('x', namespace=NS_MUCUSER).addChild(node=invite) + self.log.error(mess) + self.connect().send(mess) + def quit(self): """Stop serving messages and exit. @@ -219,27 +308,29 @@ iq = xmpp.Iq(typ='set') iq.setFrom(self.jid) iq.pubsub = iq.addChild('pubsub', namespace=xmpp.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' }) + 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'): + if 'title' in song: title = song['title'] - elif song.has_key('file'): + elif 'file' in song: 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'): + if 'artist' in song: tune.addChild('artist').addData(song['artist']) - if song.has_key('album'): + if 'album' in song: tune.addChild('source').addData(song['album']) - if song.has_key('pos') and song['pos'] > 0: + if 'pos' in song and song['pos'] > 0: tune.addChild('track').addData(str(song['pos'])) - if song.has_key('time'): + if 'time' in song: tune.addChild('length').addData(str(song['time'])) - if song.has_key('uri'): + if 'uri' in song: tune.addChild('uri').addData(song['uri']) if debug: @@ -265,7 +356,8 @@ self.send_message(self.build_reply(mess, text, private)) def build_reply(self, mess, text=None, private=False): - """Build a message for responding to another message. Message is NOT sent""" + """Build a message for responding to another message. + Message is NOT sent""" response = self.build_message(text) if private: response.setTo(mess.getFrom()) @@ -277,21 +369,32 @@ return response def build_message(self, text): - """Builds an xhtml message without attributes. If input is not valid xhtml-im fallback to normal.""" - message = None # fixes local variable 'message' referenced before assignment - Thanks to Aleksandr - text_plain = re.sub(r'<[^>]+>', '', text) # Try to determine if text has xhtml-tags - TODO needs improvement + """Builds an xhtml message without attributes. + If input is not valid xhtml-im fallback to normal.""" + message = None # init message variable + # Try to determine if text has xhtml-tags - TODO needs improvement + text_plain = re.sub(r'<[^>]+>', '', text) if text_plain != text: - message = xmpp.protocol.Message(body=text_plain) # Create body w stripped tags for reciptiens w/o xhtml-abilities - FIXME unescape " etc. - html = xmpp.Node('html', {'xmlns': 'http://jabber.org/protocol/xhtml-im'}) # Start creating a xhtml body + # Create body w stripped tags for reciptiens w/o xhtml-abilities + # FIXME unescape " etc. + message = xmpp.protocol.Message(body=text_plain) + # Start creating a xhtml body + html = xmpp.Node('html', \ + {'xmlns': 'http://jabber.org/protocol/xhtml-im'}) try: - html.addChild(node=xmpp.simplexml.XML2Node("" + text.encode('utf-8') + "")) + html.addChild(node=xmpp.simplexml.XML2Node( \ + "" + \ + text.encode('utf-8') + "")) message.addChild(node=html) except Exception, e: # Didn't work, incorrect markup or something. - self.log.debug('An error while building a xhtml message. Fallback to normal messagebody') - message = None # Fallback - don't sanitize invalid input. User is responsible! + self.log.debug('An error while building a xhtml message. '\ + 'Fallback to normal messagebody') + # Fallback - don't sanitize invalid input. User is responsible! + message = None if message is None: - message = xmpp.protocol.Message(body=text) # Normal body + # Normal body + message = xmpp.protocol.Message(body=text) return message def get_sender_username(self, mess): @@ -309,19 +412,21 @@ def get_full_jids(self, jid): """Returns all full jids, which belong to a bare jid - Example: A bare jid is bob@jabber.org, with two clients connected, which + Example: A bare jid is bob@jabber.org, with two clients connected, + which have the full jids bob@jabber.org/home and bob@jabber.org/work.""" for res in self.roster.getResources(jid): - full_jid = "%s/%s" % (jid,res) + full_jid = "%s/%s" % (jid, res) yield full_jid def status_type_changed(self, jid, new_status_type): - """Callback for tracking status types (available, away, offline, ...)""" + """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)) + 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. @@ -333,7 +438,6 @@ self.send(jid, message) def callback_presence(self, conn, presence): - self.__lastping = time.time() jid, type_, show, status = presence.getFrom(), \ presence.getType(), presence.getShow(), \ presence.getStatus() @@ -341,14 +445,14 @@ if self.jid.bareMatch(jid): # update internal status if type_ != self.OFFLINE: - self.__status = status - self.__show = show + self.__status = status + self.__show = show else: - self.__status = "" - self.__show = self.OFFLINE + self.__status = "" + self.__show = self.OFFLINE if not self.__acceptownmsgs: - # Ignore our own presence messages - return + # Ignore our own presence messages + return if type_ is None: # Keep track of status message and type changes @@ -377,10 +481,13 @@ if type_ == 'error': self.log.error(presence.getError()) - self.log.debug('Got presence: %s (type: %s, show: %s, status: %s, subscription: %s)' % (jid, type_, show, status, subscription)) + self.log.debug('Got presence: %s (type: %s, show: %s, status: %s, '\ + 'subscription: %s)' % (jid, type_, show, status, subscription)) - # If subscription is private, disregard anything not from the private domain - if self.__privatedomain and type_ in ('subscribe', 'subscribed', 'unsubscribe'): + # If subscription is private, + # disregard anything not from the private domain + if self.__privatedomain and type_ in ('subscribe', 'subscribed', \ + 'unsubscribe'): if self.__privatedomain == True: # Use the bot's domain domain = self.jid.getDomain() @@ -391,7 +498,8 @@ # Check if the sender is in the private domain user_domain = jid.getDomain() if domain != user_domain: - self.log.info('Ignoring subscribe request: %s does not match private domain (%s)' % (user_domain, domain)) + self.log.info('Ignoring subscribe request: %s does not '\ + 'match private domain (%s)' % (user_domain, domain)) return if type_ == 'subscribe': @@ -414,8 +522,8 @@ self.roster.Unauthorize(jid) def callback_message(self, conn, mess): - """Messages sent to the bot will arrive here. Command handling + routing is done in this function.""" - self.__lastping = time.time() + """Messages sent to the bot will arrive here. + Command handling + routing is done in this function.""" # Prepare to handle either private chats or group chats type = mess.getType() @@ -429,27 +537,33 @@ return # Ignore messages from before we joined - if xmpp.NS_DELAY in props: return + if xmpp.NS_DELAY in props: + return # Ignore messages from myself - if self.jid.bareMatch(jid): return - + if self.jid.bareMatch(jid): + return + self.log.debug("*** props = %s" % props) self.log.debug("*** jid = %s" % jid) self.log.debug("*** username = %s" % username) self.log.debug("*** type = %s" % type) self.log.debug("*** text = %s" % text) - # If a message format is not supported (eg. encrypted), txt will be None - if not text: return + # If a message format is not supported (eg. encrypted), + # txt will be None + if not text: + return # Ignore messages from users not seen by this bot if jid not in self.__seen: self.log.info('Ignoring message from unseen guest: %s' % jid) - self.log.debug("I've seen: %s" % ["%s" % x for x in self.__seen.keys()]) + self.log.debug("I've seen: %s" % + ["%s" % x for x in self.__seen.keys()]) return - # Remember the last-talked-in thread for replies + # Remember the last-talked-in message thread for replies + # FIXME i am not threadsafe self.__threads[jid] = mess.getThread() if ' ' in text: @@ -459,26 +573,23 @@ cmd = command.lower() self.log.debug("*** cmd = %s" % cmd) - if self.custom_message_handler is not None: - # Try the custom handler first. It can return None - # if you want JabberBot to fall back to the default. - reply = self.custom_message_handler(mess, text) - - # If your custom_message_handler returns True, it - # is assumed that the custom_message_handler has - # taken care of processing the message, so we do - # not process the message any further here. - if reply == True: - return - else: - reply = None - - if reply is None and self.commands.has_key(cmd): - try: - reply = self.commands[cmd](mess, args) - except Exception, e: - self.log.exception('An error happened while processing a message ("%s") from %s: %s"' % (text, jid, traceback.format_exc(e))) - reply = self.MSG_ERROR_OCCURRED + if cmd in self.commands: + def execute_and_send(): + try: + reply = self.commands[cmd](mess, args) + except Exception, e: + self.log.exception('An error happened while processing '\ + 'a message ("%s") from %s: %s"' % + (text, jid, traceback.format_exc(e))) + reply = self.MSG_ERROR_OCCURRED + if reply: + self.send_simple_reply(mess, reply) + # Experimental! + # if command should be executed in a seperate thread do it + if self.commands[cmd]._jabberbot_command_thread: + thread.start_new_thread(execute_and_send, ()) + else: + execute_and_send() else: # In private chat, it's okay for the bot to always respond. # In group chat, the bot should silently ignore commands it @@ -486,12 +597,15 @@ if type == 'groupchat': default_reply = None else: - default_reply = self.MSG_UNKNOWN_COMMAND % {'command': cmd} + default_reply = self.MSG_UNKNOWN_COMMAND % { + 'command': cmd, + 'helpcommand': self.__command_prefix + 'help', + } reply = self.unknown_command(mess, cmd, args) if reply is None: reply = default_reply - if reply: - self.send_simple_reply(mess, reply) + if reply: + self.send_simple_reply(mess, reply) def unknown_command(self, mess, cmd, args): """Default handler for unknown commands @@ -534,20 +648,30 @@ description = 'Available commands:' usage = '\n'.join(sorted([ - '%s: %s' % (name, (command.__doc__ or '(undocumented)').strip().split('\n', 1)[0]) - for (name, command) in self.commands.iteritems() if name != 'help' and not command._jabberbot_hidden + '%s: %s' % (name, (command.__doc__ or \ + '(undocumented)').strip().split('\n', 1)[0]) + for (name, command) in self.commands.iteritems() \ + if name != (self.__command_prefix + 'help') \ + and not command._jabberbot_command_hidden ])) - usage = '\n\n'.join(filter(None, [usage, self.MSG_HELP_TAIL])) + usage = '\n\n' + '\n\n'.join(filter(None, + [usage, self.MSG_HELP_TAIL % {'helpcommand': + self.__command_prefix + 'help'}])) else: description = '' + if (args not in self.commands and + (self.__command_prefix + args) in self.commands): + # Automatically add prefix if it's missing + args = self.__command_prefix + args if args in self.commands: - usage = (self.commands[args].__doc__ or 'undocumented').strip() + usage = (self.commands[args].__doc__ or \ + 'undocumented').strip() else: usage = self.MSG_HELP_UNDEFINED_COMMAND top = self.top_of_help_message() bottom = self.bottom_of_help_message() - return '\n\n'.join(filter(None, [top, description, usage, bottom])) + return ''.join(filter(None, [top, description, usage, bottom])) def idle_proc(self): """This function will be called in the main loop.""" @@ -558,17 +682,20 @@ To enable set self.PING_FREQUENCY to a value higher than zero. """ - if self.PING_FREQUENCY and time.time() - self.__lastping > self.PING_FREQUENCY: + if self.PING_FREQUENCY \ + and time.time() - self.__lastping > self.PING_FREQUENCY: self.__lastping = time.time() #logging.debug('Pinging the server.') - ping = xmpp.Protocol('iq', typ='get', payload=[xmpp.Node('ping', attrs={'xmlns':'urn:xmpp:ping'})]) + ping = xmpp.Protocol('iq', typ='get', \ + payload=[xmpp.Node('ping', attrs={'xmlns':'urn:xmpp:ping'})]) try: res = self.conn.SendAndWaitForResponse(ping, self.PING_TIMEOUT) #logging.debug('Got response: ' + str(res)) if res is None: self.on_ping_timeout() except IOError, e: - logging.error('Error pinging the server: %s, treating as ping timeout.' % e) + logging.error('Error pinging the server: %s, '\ + 'treating as ping timeout.' % e) self.on_ping_timeout() def on_ping_timeout(self): @@ -601,7 +728,8 @@ conn.Process(1) self.idle_proc() except KeyboardInterrupt: - self.log.info('bot stopped by user request. shutting down.') + self.log.info('bot stopped by user request. '\ + 'shutting down.') break self.shutdown() diff -Nru jabberbot-0.14/PKG-INFO jabberbot-0.15/PKG-INFO --- jabberbot-0.14/PKG-INFO 2011-07-31 10:18:56.000000000 +0000 +++ jabberbot-0.15/PKG-INFO 2012-03-08 11:26:12.000000000 +0000 @@ -1,12 +1,12 @@ -Metadata-Version: 1.0 +Metadata-Version: 1.1 Name: jabberbot -Version: 0.14 +Version: 0.15 Summary: A framework for writing Jabber/XMPP bots and services Home-page: http://thp.io/2007/python-jabberbot/ Author: Thomas Perl Author-email: m@thp.io License: GNU General Public License version 3 or later -Download-URL: http://thp.io/2007/python-jabberbot/jabberbot-0.14.tar.gz +Download-URL: http://thp.io/2007/python-jabberbot/jabberbot-0.15.tar.gz Description: The JabberBot framework allows you to easily write bots that use the XMPP protocol. You can create commands by decorating functions in your subclass or customize the diff -Nru jabberbot-0.14/README jabberbot-0.15/README --- jabberbot-0.14/README 2011-07-31 10:12:14.000000000 +0000 +++ jabberbot-0.15/README 2012-03-08 10:23:57.000000000 +0000 @@ -2,20 +2,34 @@ Python-JabberBot ~~~~~~~~~~~~~~~~ -This is python-jabberbot. See [1] for more information. Source code is -available at [2] (use [3] as Git clone URL). The contributors involved in -this release can be found in the file AUTHORS. Example code that you can -use to get started can be found in the "examples/" subdirectory. +A framework for writing IM/chat robots for the Jabber / XMPP protocol. + + +Website: + + http://thp.io/2007/python-jabberbot/ + +Source code: + + http://sourceforge.net/p/pythonjabberbot/code/ + Mirror: http://repo.or.cz/w/jabberbot.git + +Development version: + + git clone git://git.code.sf.net/p/pythonjabberbot/code pythonjabberbot-code Dependencies: - * xmpppy (http://xmpppy.sf.net/) + xmpppy (http://xmpppy.sf.net/) On Debian-based systems, xmpppy is packaged as "python-xmpp" +Mailing list: + + pythonjabberbot-devel@lists.sourceforge.net + https://lists.sourceforge.net/lists/listinfo/pythonjabberbot-devel + http://sourceforge.net/mailarchive/forum.php?forum_name=pythonjabberbot-devel -[1] http://thp.io/2007/python-jabberbot/ -[2] http://repo.or.cz/w/gpodder.git -[3] git://repo.or.cz/gpodder.git +Bug tracker: --- Thomas Perl + http://sourceforge.net/p/pythonjabberbot/tickets/