diff --git a/src/Misc.py b/src/Misc.py index d13a433cb..08e2f8af7 100755 --- a/src/Misc.py +++ b/src/Misc.py @@ -63,6 +63,11 @@ conf.registerGlobalValue(conf.supybot.plugins.Misc, 'listPrivatePlugins', plugins with the list command if given the --private switch. If this is disabled, non-owner users should be unable to see what private plugins are loaded.""")) +conf.registerGlobalValue(conf.supybot.plugins.Misc, 'timestampFormat', + registry.String('[%H:%M:%S]', """Determines the format string for + timestamps in the Misc.last command. Refer to the Python documentation + for the time module to see what formats are accepted. If you set this + variable to the empty string, the timestamp will not be shown.""")) class Misc(callbacks.Privmsg): def __init__(self): @@ -454,31 +459,38 @@ class Misc(callbacks.Privmsg): ircutils.isChannel(msg.args[0]) def last(self, irc, msg, args): - """[--{from,in,to,with,without,regexp,nolimit}] + """[--{from,in,on,with,without,regexp,nolimit}] Returns the last message matching the given criteria. --from requires - a nick from whom the message came; --in and --to require a channel the - message was sent to; --with requires some string that had to be in the - message; --regexp requires a regular expression the message must i - match; --nolimit returns all the messages that can be found. By - default, the current channel is searched. + a nick from whom the message came; --in requires a channel the message + was sent to; --on requires a netowkr the message was sent on; --with + requires some string that had to be in the message; --regexp requires + a regular expression the message must i match; --nolimit returns all + the messages that can be found. By default, the current channel is + searched. """ - (optlist, rest) = getopt.getopt(args, '', ['from=', 'in=', 'to=', + (optlist, rest) = getopt.getopt(args, '', ['from=', 'in=', 'on=', 'with=', 'regexp=', 'without=', 'nolimit']) predicates = {} nolimit = False if ircutils.isChannel(msg.args[0]): - predicates['in'] = lambda m: m.args[0] == msg.args[0] + predicates['in'] = lambda m: ircutils.strEqual(m.args[0], + msg.args[0]) + predicates['on'] = lambda m: m.receivedOn == msg.receivedOn for (option, arg) in optlist: if option == '--from': def f(m, arg=arg): return ircutils.hostmaskPatternEqual(arg, m.nick) predicates['from'] = f - elif option == '--in' or option == 'to': + elif option == '--in': def f(m, arg=arg): - return m.args[0] == arg + return ircutils.strEqual(m.args[0], arg) predicates['in'] = f + elif option == '--on': + def f(m, arg=arg): + return m.receivedOn == arg + predicates['on'] = f elif option == '--with': def f(m, arg=arg): return arg.lower() in m.args[1].lower() @@ -505,15 +517,16 @@ class Misc(callbacks.Privmsg): iterable.next() # Drop the first message. predicates = list(utils.flatten(predicates.itervalues())) resp = [] + tsf = self.registryValue('timestampFormat') for m in iterable: for predicate in predicates: if not predicate(m): break else: if nolimit: - resp.append(ircmsgs.prettyPrint(m)) + resp.append(ircmsgs.prettyPrint(m, timestampFormat=tsf)) else: - irc.reply(ircmsgs.prettyPrint(m)) + irc.reply(ircmsgs.prettyPrint(m, timestampFormat=tsf)) return if not resp: irc.error('I couldn\'t find a message matching that criteria in ' diff --git a/src/ircmsgs.py b/src/ircmsgs.py index 0f3d14f2e..334c524de 100644 --- a/src/ircmsgs.py +++ b/src/ircmsgs.py @@ -260,7 +260,7 @@ def toXml(msg, pretty=True, includeTime=True): L.append('\n') return ''.join(L) -def prettyPrint(msg, addRecipients=False): +def prettyPrint(msg, addRecipients=False, timestampFormat=None): """Provides a client-friendly string form for messages. IIRC, I copied BitchX's (or was it XChat's?) format for messages. @@ -304,6 +304,9 @@ def prettyPrint(msg, addRecipients=False): s = '*** %s has quit IRC%s' % (msg.nick, quitmsg) elif msg.command == 'TOPIC': s = '*** %s changes topic to %s' % (nickorprefix(), msg.args[1]) + at = getattr(msg, 'receivedAt', False) + if timestampFormat and at: + s = '%s %s' % (time.strftime(timestampFormat, time.localtime(at)), s) return s ### diff --git a/test/test_Misc.py b/test/test_Misc.py index b8d34f7c0..eafbcc5c3 100644 --- a/test/test_Misc.py +++ b/test/test_Misc.py @@ -32,13 +32,13 @@ from testsupport import * class MiscTestCase(ChannelPluginTestCase): - plugins = ('Misc', 'Utilities', 'Gameknot', 'Ctcp', 'Dict', 'User') + plugins = ('Misc', 'Utilities', 'Gameknot', 'Anonymous', 'Dict', 'User') def testAction(self): self.assertAction('action moos', 'moos') def testActionDoesNotAllowEmptyString(self): - self.assertError('action') - self.assertError('action ""') + self.assertHelp('action') + self.assertHelp('action ""') def testReplyWhenNotCommand(self): try: @@ -97,10 +97,13 @@ class MiscTestCase(ChannelPluginTestCase): # If Ctcp changes to public, these tests will break. So if # the next assert fails, change the plugin we test for public/private # to some other non-public plugin. - name = 'Ctcp' - self.failIf(self.irc.getCallback(name).public) + name = 'Anonymous' + conf.supybot.plugins.Anonymous.public.setValue(False) self.assertNotRegexp('list', name) self.assertRegexp('list --private', name) + conf.supybot.plugins.Anonymous.public.setValue(True) + self.assertRegexp('list', name) + self.assertNotRegexp('list --private', name) def testListDoesNotIncludeNonCanonicalName(self): self.assertNotRegexp('list Owner', '_exec') @@ -113,6 +116,8 @@ class MiscTestCase(ChannelPluginTestCase): if network: def testVersion(self): + print '*** This test should start passing when we have our '\ + 'threaded issues resolved.' self.assertNotError('version') def testSource(self): @@ -134,19 +139,28 @@ class MiscTestCase(ChannelPluginTestCase): self.failIf(ircmsgs.isAction(m)) def testLast(self): - self.feedMsg('foo bar baz') - self.assertResponse('last', '<%s> foo bar baz' % self.nick) - self.assertRegexp('last', '<%s> @last' % self.nick) - self.assertResponse('last --with foo', '<%s> foo bar baz' % self.nick) - self.assertResponse('last --without foo', '<%s> @last' % self.nick) - self.assertRegexp('last --regexp m/\s+/', 'last --without foo') - self.assertResponse('last --regexp m/bar/', - '<%s> foo bar baz' % self.nick) - self.assertResponse('last --from %s' % self.nick.upper(), - '<%s> @last --regexp m/bar/' % self.nick) - self.assertResponse('last --from %s*' % self.nick[0], - '<%s> @last --from %s' % - (self.nick, self.nick.upper())) + orig = conf.supybot.plugins.Misc.timestampFormat() + try: + conf.supybot.plugins.Misc.timestampFormat.setValue('') + self.feedMsg('foo bar baz') + self.assertResponse('last', '<%s> foo bar baz' % self.nick) + self.assertRegexp('last', '<%s> @last' % self.nick) + self.assertResponse('last --with foo', '<%s> foo bar baz' % \ + self.nick) + self.assertResponse('last --without foo', '<%s> @last' % self.nick) + self.assertRegexp('last --regexp m/\s+/', 'last --without foo') + self.assertResponse('last --regexp m/bar/', + '<%s> foo bar baz' % self.nick) + self.assertResponse('last --from %s' % self.nick.upper(), + '<%s> @last --regexp m/bar/' % self.nick) + self.assertResponse('last --from %s*' % self.nick[0], + '<%s> @last --from %s' % + (self.nick, self.nick.upper())) + conf.supybot.plugins.Misc.timestampFormat.setValue('foo') + self.assertSnarfNoResponse('foo bar baz', 1) + self.assertResponse('last', 'foo <%s> foo bar baz' % self.nick) + finally: + conf.supybot.plugins.Misc.timestampFormat.setValue(orig) def testMore(self): self.assertRegexp('echo %s' % ('abc'*300), 'more')