diff --git a/plugins/Factoids.py b/plugins/Factoids.py index 66d55bbfd..f0127bb05 100644 --- a/plugins/Factoids.py +++ b/plugins/Factoids.py @@ -182,12 +182,13 @@ class Factoids(plugins.ChannelDBHandler, for result in cursor.fetchall(): factoids.append('(#%s) %s' % (counter, result[0])) counter += 1 - irc.reply('%r could be %s' % (key, ', or '.join(factoids))) + irc.replies(factoids, prefixer='%r could be ' % key, + joiner=', or ', onlyPrefixFirst=True) else: try: irc.reply(cursor.fetchall()[number-1][0]) except IndexError: - irc.error('That\'s not a valid number for this key.') + irc.error('That\'s not a valid number for that key.') return def lock(self, irc, msg, args): diff --git a/plugins/RSS.py b/plugins/RSS.py index 90f5697f9..3d12eeebc 100644 --- a/plugins/RSS.py +++ b/plugins/RSS.py @@ -101,6 +101,7 @@ class RSS(callbacks.Privmsg, configurable.Mixin): def __call__(self, irc, msg): callbacks.Privmsg.__call__(self, irc, msg) + irc = callbacks.IrcObjectProxyRegexp(irc, msg) feeds = self.configurables.getChannels('announce-news-feeds') for (channel, d) in feeds.iteritems(): sep = self.configurables.get('headline-separator', channel) @@ -127,9 +128,7 @@ class RSS(callbacks.Privmsg, configurable.Mixin): pre = prefix + name if bold: pre = ircutils.bold(pre) - headlines = sep.join(newheadlines) - s = '%s: %s' % (pre, headlines) - irc.queueMsg(ircmsgs.privmsg(channel, s)) + irc.replies(headlines, prefixer=pre, joiner=sep) def getFeed(self, url): now = time.time() diff --git a/src/callbacks.py b/src/callbacks.py index d61f04b6f..59d6fc25c 100644 --- a/src/callbacks.py +++ b/src/callbacks.py @@ -296,38 +296,65 @@ class RichReplyMethods(object): return s def replySuccess(self, s='', **kwargs): - self.reply(self.__makeReply(conf.supybot.replies.success(), s), - **kwargs) + v = conf.supybot.replies.success.get(self.msg.args[0]) + s = self.__makeReply(v(), s) + self.reply(s, **kwargs) def replyError(self, s='', **kwargs): - self.reply(self.__makeReply(conf.supybot.replies.error(), s), - **kwargs) + v = conf.supybot.replies.error.get(self.msg.args[0]) + s = self.__makeReply(v(), s) + self.reply(s, **kwargs) + + def replies(self, L, prefixer=''.join, + joiner=utils.commaAndify, onlyPrefixFirst=False): + if prefixer is None: + prefixer = '' + if joiner is None: + joiner = utils.commaAndify + if isinstance(prefixer, basestring): + prefixer = prefixer.__add__ + if isinstance(joiner, basestring): + joiner = joiner.join + if conf.supybot.reply.oneToOne(): + self.reply(prefixer(joiner(L))) + else: + first = True + for s in L: + if onlyPrefixFirst: + if first: + self.reply(prefixer(s)) + first = False + else: + self.reply(s) + else: + self.reply(prefixer(s)) def errorNoCapability(self, capability, s='', **kwargs): log.warning('Denying %s for lacking %r capability', self.msg.prefix, capability) - noCapability = conf.supybot.replies.noCapability() - s = self.__makeReply(noCapability % capability, s) + v = conf.supybot.replies.noCapability.get(self.msg.args[0]) + s = self.__makeReply(v() % capability, s) self.error(s, **kwargs) def errorPossibleBug(self, s='', **kwargs): + v = conf.supybot.replies.possibleBug.get(self.msg.args[0]) if s: - s += ' (%s)' % conf.supybot.replies.possibleBug() + s += ' (%s)' % v() else: - s = conf.supybot.replies.possibleBug() + s = v() self.error(s, **kwargs) def errorNotRegistered(self, s='', **kwargs): - notRegistered = conf.supybot.replies.notRegistered() - self.error(self.__makeReply(notRegistered, s), **kwargs) + v = conf.supybot.replies.notRegistered.get(self.msg.args[0]) + self.error(self.__makeReply(v(), s), **kwargs) def errorNoUser(self, s='', **kwargs): - noUser = conf.supybot.replies.noUser() - self.error(self.__makeReply(noUser, s), **kwargs) + v = conf.supybot.replies.noUser.get(self.msg.args[0]) + self.error(self.__makeReply(v(), s), **kwargs) def errorRequiresPrivacy(self, s='', **kwargs): - requiresPrivacy = conf.supybot.replies.requiresPrivacy() - self.error(self.__makeReply(requiresPrivacy, s), **kwargs) + v = conf.supybot.replies.requiresPrivacy.get(self.msg.args[0]) + self.error(self.__makeReply(v(), s), **kwargs) class IrcObjectProxy(RichReplyMethods): @@ -668,6 +695,8 @@ class Privmsg(irclib.IrcCallback): if self.noIgnore or not ircdb.checkIgnored(msg.prefix,msg.args[0]): self.__parent.__call__(irc, msg) else: + # We want this to be under logging.DEBUG: it's not very useful, + # even for debugging things :) self.log.log(0, 'Ignoring %s', msg.prefix) else: self.__parent.__call__(irc, msg) diff --git a/src/conf.py b/src/conf.py index e5d14e8fe..7b18c2dc8 100644 --- a/src/conf.py +++ b/src/conf.py @@ -81,7 +81,7 @@ be sent to the server if it requires one.""")) supybot.register('server', registry.String('irc.freenode.net', """Determines what server the bot connects to.""")) -supybot.register('channels', registry.CommaSeparatedListOfStrings('#supybot', +supybot.register('channels', registry.CommaSeparatedListOfStrings(['#supybot'], """Determines what channels the bot will join when it connects to the server. """)) @@ -141,6 +141,11 @@ the snarf message.""")) # TODO: These should probably all be channel-specific. supybot.registerGroup('reply') +supybot.reply.register('oneToOne', registry.Boolean(True, """Determines whether +the bot will send multi-message replies in a single messsage or in multiple +messages. For safety purposes (so the bot can't possibly flood) it will +normally send everything in a single message.""")) + supybot.reply.register('errorInPrivate', registry.Boolean(False, """ Determines whether the bot will send error messages to users in private.""")) @@ -211,49 +216,52 @@ why these default to what they do.""")) ### # Replies ### -# TODO: These should be channel-specific. supybot.registerGroup('replies') -supybot.replies.register('error', registry.NormalizedString("""An error has -occurred and has been logged. Please contact this bot's administrator for more -information.""", """Determines what error message the bot gives when it wants -to be ambiguous.""")) +supybot.replies.registerGroup('error', registry.GroupWithDefault( + registry.NormalizedString("""An error has occurred and has been logged. + Please contact this bot's administrator for more information.""", """ + Determines what error message the bot gives when it wants to be + ambiguous."""))) -supybot.replies.register('noCapability', registry.NormalizedString("""You -don\'t have the %r capability. If you think that you should have this -capability, be sure that you are identified before trying again. The 'whoami' -command can tell you if you're identified.""", """Determines what error message -is given when the bot is telling someone they aren't cool enough to use the -command they tried to use.""")) +supybot.replies.registerGroup('noCapability', registry.GroupWithDefault( + registry.NormalizedString("""You don't have the %r capability. If you + think that you should have this capability, be sure that you are identified + before trying again. The 'whoami' command can tell you if you're + identified.""", """Determines what error message is given when the bot is + telling someone they aren't cool enough to use the command they tried to + use."""))) -supybot.replies.register('success', registry.NormalizedString("""The operation -succeeded.""", """Determines what message the bot replies with when a command -succeeded.""")) +supybot.replies.registerGroup('success', registry.GroupWithDefault( + registry.NormalizedString("""The operation succeeded.""", """Determines + what message the bot replies with when a command succeeded."""))) -supybot.replies.register('incorrectAuthentication', -registry.NormalizedString("""Your hostmask doesn't match or your password is -wrong.""", """Determines what message the bot replies wiwth when someone tries -to use a command that requires being identified or having a password and -neither credential is correct.""")) +supybot.replies.registerGroup('incorrectAuthentication', + registry.GroupWithDefault( + registry.NormalizedString("""Your hostmask doesn't match or your password + is wrong.""", """Determines what message the bot replies with when someone + tries to use a command that requires being identified or having a password + and neither credential is correct."""))) -supybot.replies.register('noUser', registry.NormalizedString("""I can't find -that user in my user database.""", """Determines what error message the bot -replies with when someone tries to accessing some information on a user the -bot doesn't know about.""")) +supybot.replies.registerGroup('noUser', registry.GroupWithDefault( + registry.NormalizedString("""I can't find that user in my user + database.""", """Determines what error message the bot replies with when + someone tries to accessing some information on a user the bot doesn't know + about."""))) -supybot.replies.register('notRegistered', registry.NormalizedString(""" -You must be registered to use this command. If you are already registered, you -must either identify (using the identify command) or add a hostmask matching -your current hostmask (using the addhostmask command).""", """Determines what -error message the bot replies with when someone tries to do something that -requires them to be registered but they're not currently recognized.""")) +supybot.replies.registerGroup('notRegistered', registry.GroupWithDefault( + registry.NormalizedString("""You must be registered to use this command. + If you are already registered, you must either identify (using the identify + command) or add a hostmask matching your current hostmask (using the + addhostmask command).""", """Determines what error message the bot replies + with when someone tries to do something that requires them to be registered + but they're not currently recognized."""))) -# XXX: removed replyInvalidArgument. +supybot.replies.registerGroup('requiresPrivacy', registry.GroupWithDefault( + registry.NormalizedString("""That operation cannot be done in a + channel.""", """Determines what error messages the bot sends to people who + try to do things in a channel that really should be done in private."""))) -supybot.replies.register('requiresPrivacy', registry.NormalizedString(""" -That operation cannot be done in a channel.""", """Determines what error -messages the bot sends to people who try to do things in a channel that really -should be done in private.""")) supybot.replies.register('possibleBug', registry.NormalizedString("""This may be a bug. If you think it is, please file a bug report at .""", diff --git a/src/irclib.py b/src/irclib.py index 25c395c12..482c3638b 100644 --- a/src/irclib.py +++ b/src/irclib.py @@ -148,7 +148,7 @@ class IrcMsgQueue(object): def enqueue(self, msg): """Enqueues a given message.""" if msg in self.msgs: - log.info('Not adding msg %s to queue' % msg) + log.warning('Not adding message %r to queue, already added.' % msg) else: self.msgs.add(msg) if msg.command in _high: diff --git a/src/registry.py b/src/registry.py index 5369cad02..0595942ac 100644 --- a/src/registry.py +++ b/src/registry.py @@ -138,6 +138,10 @@ class NormalizedString(String): s = utils.normalizeWhitespace(s.strip()) String.set(self, s) + def setValue(self, s): + s = utils.normalizeWhitespace(s.strip()) + String.setValue(self, s) + class StringSurroundedBySpaces(String): def set(self, s): String.set(self, s) @@ -180,6 +184,9 @@ class Group(object): else: self.__nonExistentEntry(original) + def get(self, attr): + return self.__getattr__(attr) + def getChild(self, attr): return self.children[attr.lower()] @@ -251,10 +258,6 @@ class GroupWithValue(Group): def __str__(self): return str(self.value) -## def getValues(self, getChildren=False): -## L = Group.getValues(self, getChildren=False) -## L.insert(0, (self.getName(), str(self.value))) -## return L class GroupWithDefault(GroupWithValue): def __init__(self, value): @@ -264,22 +267,26 @@ class GroupWithDefault(GroupWithValue): v = copy.copy(self.value) v.set(s) self.register(attr, v) - + def __getattr__(self, attr): try: - return Group.__getattr__(self, attr) + return GroupWithValue.__getattr__(self, attr) except NonExistentRegistryEntry: - return self.value + self.__makeChild(attr, str(self)) + return self.__getattr__(attr) def setName(self, name): - Group.setName(self, name) + GroupWithValue.setName(self, name) for (k, v) in cache.iteritems(): if k.startswith(self.name): (_, group) = rsplit(k, '.', 1) self.__makeChild(group, v) - def setChild(self, attr, s): - self.__setattr__(attr, s) + def getValues(self, getChildren=False): + L = GroupWithValue.getValues(self, getChildren) + me = str(self) + L = [v for v in L if str(v[1]) != me] + return L if __name__ == '__main__':