From e4f198ddf7532d575e52b0fda7fb1f6e2f72c2ef Mon Sep 17 00:00:00 2001 From: Valentin Lorentz Date: Wed, 20 Oct 2010 08:50:59 +0200 Subject: [PATCH 01/19] Fix in Seen plugin : AssertionError: 'Someone was last seen in #test 0 seconds ago: *** test has joined #test' does not match 'test has joined' --- plugins/Seen/test.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/Seen/test.py b/plugins/Seen/test.py index 0ae819b6b..77eaeb37d 100644 --- a/plugins/Seen/test.py +++ b/plugins/Seen/test.py @@ -50,7 +50,7 @@ class ChannelDBTestCase(ChannelPluginTestCase): self.assertNotRegexp('seen asldfkjasdlfkj', 'KeyError') def testAny(self): - self.assertRegexp('seen any', 'test has joined') + self.assertRegexp('seen any', 'test has joined') self.irc.feedMsg(ircmsgs.mode(self.channel, args=('+o', self.nick), prefix=self.prefix)) self.assertRegexp('seen any %s' % self.nick, From e122102e5a7375763d3e2d04cf3425f7bc42a741 Mon Sep 17 00:00:00 2001 From: Valentin Lorentz Date: Wed, 20 Oct 2010 19:11:00 +0200 Subject: [PATCH 02/19] Fix a test in Unix plugin --- plugins/Unix/test.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/plugins/Unix/test.py b/plugins/Unix/test.py index 062ce550b..d554d90c7 100644 --- a/plugins/Unix/test.py +++ b/plugins/Unix/test.py @@ -37,7 +37,8 @@ if os.name == 'posix': if utils.findBinaryInPath('aspell') is not None or \ utils.findBinaryInPath('ispell') is not None: def testSpell(self): - self.assertRegexp('spell Strike', 'correctly') + self.assertRegexp('spell Strike', + '(correctly|Possible spellings)') # ispell won't find any results. aspell will make some # suggestions. self.assertRegexp('spell z0opadfnaf83nflafl230kasdf023hflasdf', From e6fc429701cc3ef584494387c98a6f690b914b89 Mon Sep 17 00:00:00 2001 From: Valentin Lorentz Date: Sat, 22 Jan 2011 09:59:19 +0100 Subject: [PATCH 03/19] Fix bug with rainbow when message contains numeric characters #SF3140981 --- plugins/Filter/plugin.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/Filter/plugin.py b/plugins/Filter/plugin.py index 2367ece8c..0e126312c 100644 --- a/plugins/Filter/plugin.py +++ b/plugins/Filter/plugin.py @@ -391,7 +391,7 @@ class Filter(callbacks.Plugin): Returns colorized like a rainbow. """ - colors = utils.iter.cycle([4, 7, 8, 3, 2, 12, 6]) + colors = utils.iter.cycle(['04', '07', '08', '03', '02', '12', '06']) L = [self._color(c, fg=colors.next()) for c in text] irc.reply(''.join(L) + '\x03') rainbow = wrap(rainbow, ['text']) From 720b6d23eb27937cc6598461d770c9c32106130b Mon Sep 17 00:00:00 2001 From: Valentin Lorentz Date: Sat, 22 Jan 2011 10:17:32 +0100 Subject: [PATCH 04/19] Fix to global hostmask. Fix bug #SF3088559 and submitted as patch #SF3163843 --- src/ircutils.py | 2 +- test/test_ircutils.py | 4 ++++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/src/ircutils.py b/src/ircutils.py index c7b5408df..b2ec6bd2d 100644 --- a/src/ircutils.py +++ b/src/ircutils.py @@ -196,7 +196,7 @@ def banmask(hostmask): L[-1] = '*' return '*!*@' + ':'.join(L) else: - if '.' in host: + if len(host.split('.')) > 2: # If it is a subdomain return '*!*@*%s' % host[host.find('.'):] else: return '*!*@' + host diff --git a/test/test_ircutils.py b/test/test_ircutils.py index 6dc11e21d..b0fca5261 100644 --- a/test/test_ircutils.py +++ b/test/test_ircutils.py @@ -233,6 +233,10 @@ class FunctionsTestCase(SupyTestCase): msg.prefix), '%r didn\'t match %r' % (msg.prefix, banmask)) self.assertEqual(ircutils.banmask('foobar!user@host'), '*!*@host') + self.assertEqual(ircutils.banmask('foobar!user@host.tld'), + '*!*@host.tld') + self.assertEqual(ircutils.banmask('foobar!user@sub.host.tld'), + '*!*@*.host.tld') self.assertEqual(ircutils.banmask('foo!bar@2001::'), '*!*@2001::*') def testSeparateModes(self): From e4cced8364d82c60c92dbfb7bbb053318a3e2842 Mon Sep 17 00:00:00 2001 From: Valentin Lorentz Date: Sat, 22 Jan 2011 10:28:27 +0100 Subject: [PATCH 05/19] Fix depluralize. --- src/utils/str.py | 53 ++++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 47 insertions(+), 6 deletions(-) diff --git a/src/utils/str.py b/src/utils/str.py index c6d801ce7..5c6324605 100644 --- a/src/utils/str.py +++ b/src/utils/str.py @@ -1,6 +1,7 @@ ### # Copyright (c) 2002-2005, Jeremiah Fincher # Copyright (c) 2008-2009, James Vega +# Copyright (c) 2010, Valentin Lorentz # All rights reserved. # # Redistribution and use in source and binary forms, with or without @@ -41,6 +42,9 @@ import textwrap from iter import all, any from structures import TwoWayDictionary +from supybot.i18n import PluginInternationalization +internationalizeFunction=PluginInternationalization().internationalizeFunction + curry = new.instancemethod chars = string.maketrans('', '') @@ -55,9 +59,14 @@ def rsplit(s, sep=None, maxsplit=-1): else: return s.rsplit(sep, maxsplit) -def normalizeWhitespace(s): +def normalizeWhitespace(s, removeNewline=True): """Normalizes the whitespace in a string; \s+ becomes one space.""" - return ' '.join(s.split()) + s = str(s) + if removeNewline: + s = str.replace(s, '\n', '') + while ' ' in s: + s = str.replace(s, ' ', ' ') + return s def distance(s, t): """Returns the levenshtein edit distance between two strings.""" @@ -252,12 +261,13 @@ def matchCase(s1, s2): L[i] = L[i].upper() return ''.join(L) -consonants = 'bcdfghjklmnpqrstvwxz' -_pluralizeRegex = re.compile('[%s]y$' % consonants) +@internationalizeFunction('pluralize') def pluralize(s): """Returns the plural of s. Put any exceptions to the general English rule of appending 's' in the plurals dictionary. """ + consonants = 'bcdfghjklmnpqrstvwxz' + _pluralizeRegex = re.compile('[%s]y$' % consonants) lowered = s.lower() # Exception dictionary if lowered in plurals: @@ -274,9 +284,11 @@ def pluralize(s): else: return matchCase(s, s+'s') -_depluralizeRegex = re.compile('[%s]ies' % consonants) +@internationalizeFunction('depluralize') def depluralize(s): """Returns the singular of s.""" + consonants = 'bcdfghjklmnpqrstvwxz' + _depluralizeRegex = re.compile('[%s]ies' % consonants) lowered = s.lower() if lowered in plurals: return matchCase(s, plurals[lowered]) @@ -293,17 +305,28 @@ def depluralize(s): def nItems(n, item, between=None): """Works like this: + >>> nItems(4, '') + '4' + >>> nItems(1, 'clock') '1 clock' >>> nItems(10, 'clock') '10 clocks' + >>> nItems(4, '', between='grandfather') + '4 grandfather' + >>> nItems(10, 'clock', between='grandfather') '10 grandfather clocks' """ assert isinstance(n, int) or isinstance(n, long), \ 'The order of the arguments to nItems changed again, sorry.' + if item == '': + if between is None: + return format('%s', n) + else: + return format('%s %s', n, item) if between is None: if n != 1: return format('%s %p', n, item) @@ -315,6 +338,7 @@ def nItems(n, item, between=None): else: return format('%s %s %s', n, between, item) +@internationalizeFunction('ordinal') def ordinal(i): """Returns i + the ordinal indicator for the number. @@ -333,6 +357,7 @@ def ordinal(i): ord = 'rd' return '%s%s' % (i, ord) +@internationalizeFunction('be') def be(i): """Returns the form of the verb 'to be' based on the number i.""" if i == 1: @@ -340,6 +365,7 @@ def be(i): else: return 'are' +@internationalizeFunction('has') def has(i): """Returns the form of the verb 'to have' based on the number i.""" if i == 1: @@ -362,7 +388,7 @@ def timestamp(t): t = time.time() return time.ctime(t) -_formatRe = re.compile('%((?:\d+)?\.\d+f|[bfhiLnpqrstu%])') +_formatRe = re.compile('%((?:\d+)?\.\d+f|[bfhiLnpqrsStuv%])') def format(s, *args, **kwargs): """w00t. @@ -377,8 +403,11 @@ def format(s, *args, **kwargs): p: pluralize (takes a string) q: quoted (takes a string) n: nItems (takes a 2-tuple of (n, item) or a 3-tuple of (n, between, item)) + S: returns a human-readable size (takes an int) t: time, formatted (takes an int) u: url, wrapped in braces (this should be configurable at some point) + v: void : takes one or many arguments, but doesn't display it + (useful for translation) """ args = list(args) args.reverse() # For more efficient popping. @@ -425,10 +454,22 @@ def format(s, *args, **kwargs): return nItems(t[0], t[2], between=t[1]) else: raise ValueError, 'Invalid value for %%n in format: %s' % t + elif char == 'S': + t = args.pop() + if not isinstance(t, (int, long)): + raise ValueError, 'Invalid value for %%S in format: %s' % t + for suffix in ['B','KB','MB','GB','TB']: + if t < 1024: + return "%i%s" % (t, suffix) + t /= 1024 + elif char == 't': return timestamp(args.pop()) elif char == 'u': return '<%s>' % args.pop() + elif char == 'v': + args.pop() + return '' elif char == '%': return '%' else: From 1cceeb7ad1ebb5668a269a75f8b38561314c3c66 Mon Sep 17 00:00:00 2001 From: Valentin Lorentz Date: Wed, 26 Jan 2011 08:30:20 +0100 Subject: [PATCH 06/19] Fix @String decode utf8 issue --- plugins/String/plugin.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/String/plugin.py b/plugins/String/plugin.py index 6d0073529..ccc6496ec 100644 --- a/plugins/String/plugin.py +++ b/plugins/String/plugin.py @@ -85,7 +85,7 @@ class String(callbacks.Plugin): . """ try: - irc.reply(text.decode(encoding).encode('utf-8')) + irc.reply(text.decode(encoding)) except LookupError: irc.errorInvalid(_('encoding'), encoding) except binascii.Error: From ad2ad802636f4e575083165d9031a3a29520847f Mon Sep 17 00:00:00 2001 From: Valentin Lorentz Date: Wed, 26 Jan 2011 09:56:04 +0100 Subject: [PATCH 07/19] Fix forgotten merge --- src/utils/str.py | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/utils/str.py b/src/utils/str.py index e1f9c0b14..5c6324605 100644 --- a/src/utils/str.py +++ b/src/utils/str.py @@ -287,10 +287,7 @@ def pluralize(s): @internationalizeFunction('depluralize') def depluralize(s): """Returns the singular of s.""" -<<<<<<< HEAD consonants = 'bcdfghjklmnpqrstvwxz' -======= ->>>>>>> testing _depluralizeRegex = re.compile('[%s]ies' % consonants) lowered = s.lower() if lowered in plurals: From e705d3b5a9b6432f75a6c178070565a0f2a3b4e9 Mon Sep 17 00:00:00 2001 From: Valentin Lorentz Date: Wed, 26 Jan 2011 10:01:58 +0100 Subject: [PATCH 08/19] Fix name conflict with _() --- src/commands.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/commands.py b/src/commands.py index bc54ce884..1983ffe87 100644 --- a/src/commands.py +++ b/src/commands.py @@ -314,7 +314,7 @@ def _getRe(f): s = args.pop(0) def isRe(s): try: - _ = f(s) + foo = f(s) return True except ValueError: return False From e8814fc07ab7f11bc083706a2736c5b25a9a9ee0 Mon Sep 17 00:00:00 2001 From: Valentin Lorentz Date: Thu, 27 Jan 2011 19:32:43 +0100 Subject: [PATCH 09/19] Add checkCapabilityButIgnoreOwner converter --- plugins/AutoMode/plugin.py | 5 +---- src/commands.py | 6 ++++++ src/ircdb.py | 22 ++++++++++++---------- 3 files changed, 19 insertions(+), 14 deletions(-) diff --git a/plugins/AutoMode/plugin.py b/plugins/AutoMode/plugin.py index 73f1a64bb..f1d321efb 100644 --- a/plugins/AutoMode/plugin.py +++ b/plugins/AutoMode/plugin.py @@ -50,11 +50,8 @@ class AutoMode(callbacks.Plugin): return fallthrough = self.registryValue('fallthrough', channel) def do(type): - if ircdb.checkCapability(msg.prefix, 'owner') and not \ - self.registryValue('owner'): - raise Continue cap = ircdb.makeChannelCapability(channel, type) - if ircdb.checkCapability(msg.prefix, cap): + if ircdb.checkCapability(msg.prefix, cap, ignoreOwner=True): if self.registryValue(type, channel): self.log.info('Sending auto-%s of %s in %s.', type, msg.prefix, channel) diff --git a/src/commands.py b/src/commands.py index 1983ffe87..be0dc2d4b 100644 --- a/src/commands.py +++ b/src/commands.py @@ -477,6 +477,11 @@ def checkCapability(irc, msg, args, state, cap): if not ircdb.checkCapability(msg.prefix, cap): state.errorNoCapability(cap, Raise=True) +def checkCapabilityButIgnoreOwner(irc, msg, args, state, cap): + cap = ircdb.canonicalCapability(cap) + if not ircdb.checkCapability(msg.prefix, cap, ignoreOwner=True): + state.errorNoCapability(cap, Raise=True) + def owner(irc, msg, args, state): checkCapability(irc, msg, args, state, 'owner') @@ -590,6 +595,7 @@ wrappers = ircutils.IrcDict({ 'channel': getChannel, 'channelDb': getChannelDb, 'checkCapability': checkCapability, + 'checkCapabilityButIgnoreOwner': checkCapabilityButIgnoreOwner, 'checkChannelCapability': checkChannelCapability, 'color': getIrcColor, 'commandName': getCommandName, diff --git a/src/ircdb.py b/src/ircdb.py index edd5ad5db..1da09badc 100644 --- a/src/ircdb.py +++ b/src/ircdb.py @@ -1,6 +1,7 @@ ### # Copyright (c) 2002-2009, Jeremiah Fincher # Copyright (c) 2009, James Vega +# Copyright (c) 2011, Valentin Lorentz # All rights reserved. # # Redistribution and use in source and binary forms, with or without @@ -152,7 +153,7 @@ class CapabilitySet(set): elif self.__parent.__contains__(_invert(capability)): return False else: - raise KeyError, capability + return False def __repr__(self): return '%s([%s])' % (self.__class__.__name__, @@ -165,16 +166,16 @@ class UserCapabilitySet(CapabilitySet): self.__parent = super(UserCapabilitySet, self) self.__parent.__init__(*args, **kwargs) - def __contains__(self, capability): + def __contains__(self, capability, ignoreOwner=False): capability = ircutils.toLower(capability) - if capability == 'owner' or capability == antiOwner: + if not ignoreOwner and capability == 'owner' or capability == antiOwner: return True - elif self.__parent.__contains__('owner'): + elif not ignoreOwner and self.__parent.__contains__('owner'): return True else: return self.__parent.__contains__(capability) - def check(self, capability): + def check(self, capability, ignoreOwner=False): """Returns the appropriate boolean for whether a given capability is 'allowed' given its (or its anticapability's) presence in the set. Differs from CapabilitySet in that it handles the 'owner' capability @@ -186,7 +187,7 @@ class UserCapabilitySet(CapabilitySet): return not isAntiCapability(capability) else: return isAntiCapability(capability) - elif self.__parent.__contains__('owner'): + elif not ignoreOwner and self.__parent.__contains__('owner'): if isAntiCapability(capability): return False else: @@ -236,7 +237,7 @@ class IrcUser(object): """Takes from the user the given capability.""" self.capabilities.remove(capability) - def _checkCapability(self, capability): + def _checkCapability(self, capability, ignoreOwner=False): """Checks the user for a given capability.""" if self.ignore: if isAntiCapability(capability): @@ -244,7 +245,7 @@ class IrcUser(object): else: return False else: - return self.capabilities.check(capability) + return self.capabilities.check(capability, ignoreOwner) def setPassword(self, password, hashed=False): """Sets the user's password.""" @@ -1009,7 +1010,8 @@ def _checkCapabilityForUnknownUser(capability, users=users, channels=channels): else: return _x(capability, conf.supybot.capabilities.default()) -def checkCapability(hostmask, capability, users=users, channels=channels): +def checkCapability(hostmask, capability, users=users, channels=channels, + ignoreOwner=False): """Checks that the user specified by name/hostmask has the capability given. """ if world.testing: @@ -1028,7 +1030,7 @@ def checkCapability(hostmask, capability, users=users, channels=channels): return _checkCapabilityForUnknownUser(capability, users=users, channels=channels) if capability in u.capabilities: - return u._checkCapability(capability) + return u._checkCapability(capability, ignoreOwner) else: if isChannelCapability(capability): (channel, capability) = fromChannelCapability(capability) From 60bbbc63ab66ff3355bac20edd300a7a376ad112 Mon Sep 17 00:00:00 2001 From: Daniel Folkinshteyn Date: Tue, 25 Jan 2011 01:26:42 -0500 Subject: [PATCH 10/19] Topic: get shouldn't require capabilities, since it's a read-only operation. --- plugins/Topic/plugin.py | 3 --- 1 file changed, 3 deletions(-) diff --git a/plugins/Topic/plugin.py b/plugins/Topic/plugin.py index 66256493a..0e2d299f0 100644 --- a/plugins/Topic/plugin.py +++ b/plugins/Topic/plugin.py @@ -403,9 +403,6 @@ class Topic(callbacks.Plugin): index into the topics. is only necessary if the message isn't sent in the channel itself. """ - if not self._checkManageCapabilities(irc, msg, channel): - capabilities = self.registryValue('requireManageCapability') - irc.errorNoCapability(capabilities, Raise=True) topics = self._splitTopic(irc.state.getTopic(channel), channel) irc.reply(topics[number]) get = wrap(get, ['inChannel', 'topicNumber']) From b0bd346775bfae62e201bfe9d3db5860cca78a11 Mon Sep 17 00:00:00 2001 From: Daniel Folkinshteyn Date: Tue, 18 Jan 2011 13:51:34 -0500 Subject: [PATCH 11/19] Services: add some more strings indicating identification success. --- plugins/Services/plugin.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/plugins/Services/plugin.py b/plugins/Services/plugin.py index e372c2048..1acd35626 100644 --- a/plugins/Services/plugin.py +++ b/plugins/Services/plugin.py @@ -303,6 +303,8 @@ class Services(callbacks.Plugin): pass elif ('now recognized' in s) or \ ('already identified' in s) or \ + ('already logged in' in s) or \ + ('successfully identified' in s) or \ ('password accepted' in s) or \ ('now identified' in s): # freenode, oftc, arstechnica, zirc, .... From 7c7b093b33de3be88782a3d655a64f7a327f28c9 Mon Sep 17 00:00:00 2001 From: Daniel Folkinshteyn Date: Wed, 1 Dec 2010 16:52:01 -0500 Subject: [PATCH 12/19] Google: add some extra matching capability to google calc now should be able to display any 'special' result from google. --- plugins/Google/plugin.py | 8 ++++++-- plugins/Google/test.py | 1 + src/version.py | 3 +++ 3 files changed, 10 insertions(+), 2 deletions(-) create mode 100644 src/version.py diff --git a/plugins/Google/plugin.py b/plugins/Google/plugin.py index a0d94d062..59660fc69 100644 --- a/plugins/Google/plugin.py +++ b/plugins/Google/plugin.py @@ -302,7 +302,8 @@ class Google(callbacks.PluginRegexp): url = r'http://google.com/search?q=' + s return url - _calcRe = re.compile(r'(.*?)', re.I) + _calcRe1 = re.compile(r']*>(.*?)', re.I) + _calcRe2 = re.compile(r'(.*?)', re.I) _calcSupRe = re.compile(r'(.*?)', re.I) _calcFontRe = re.compile(r'(.*?)') _calcTimesRe = re.compile(r'&(?:times|#215);') @@ -314,12 +315,15 @@ class Google(callbacks.PluginRegexp): """ url = self._googleUrl(expr) html = utils.web.getUrl(url) - match = self._calcRe.search(html) + match = self._calcRe1.search(html) + if match is None: + match = self._calcRe2.search(html) if match is not None: s = match.group(1) s = self._calcSupRe.sub(r'^(\1)', s) s = self._calcFontRe.sub(r',', s) s = self._calcTimesRe.sub(r'*', s) + s = utils.web.htmlToText(s) irc.reply(s) else: irc.reply(_('Google\'s calculator didn\'t come up with anything.')) diff --git a/plugins/Google/test.py b/plugins/Google/test.py index fe7affc3d..663562a9e 100644 --- a/plugins/Google/test.py +++ b/plugins/Google/test.py @@ -39,6 +39,7 @@ class GoogleTestCase(ChannelPluginTestCase): def testCalc(self): self.assertNotRegexp('google calc e^(i*pi)+1', r'didn\'t') self.assertNotRegexp('google calc 1 usd in gbp', r'didn\'t') + self.assertRegexp('google calc current time in usa', r'Time in.*USA') def testHtmlHandled(self): self.assertNotRegexp('google calc ' diff --git a/src/version.py b/src/version.py new file mode 100644 index 000000000..93286227a --- /dev/null +++ b/src/version.py @@ -0,0 +1,3 @@ +"""stick the various versioning attributes in here, so we only have to change +them once.""" +version = '0.83.4.1+gribble (2010-10-10T17:52:04-0400)' From 125e766d84978cb960bd72c43f891f6f5da71dcc Mon Sep 17 00:00:00 2001 From: Valentin Lorentz Date: Sat, 29 Jan 2011 11:43:47 +0100 Subject: [PATCH 13/19] Remove gribble's version.py --- src/version.py | 3 --- 1 file changed, 3 deletions(-) delete mode 100644 src/version.py diff --git a/src/version.py b/src/version.py deleted file mode 100644 index 93286227a..000000000 --- a/src/version.py +++ /dev/null @@ -1,3 +0,0 @@ -"""stick the various versioning attributes in here, so we only have to change -them once.""" -version = '0.83.4.1+gribble (2010-10-10T17:52:04-0400)' From 9665c178aa14eaa01722475798c22562879465fa Mon Sep 17 00:00:00 2001 From: Daniel Folkinshteyn Date: Sat, 4 Sep 2010 01:25:57 -0400 Subject: [PATCH 14/19] Scheduler: don't immediately execute commands when restoring repeated events. --- plugins/Scheduler/plugin.py | 65 +++++++++++++++++++++++++++++++++++++ 1 file changed, 65 insertions(+) diff --git a/plugins/Scheduler/plugin.py b/plugins/Scheduler/plugin.py index ec1dd77af..dc45f297e 100644 --- a/plugins/Scheduler/plugin.py +++ b/plugins/Scheduler/plugin.py @@ -42,6 +42,62 @@ class Scheduler(callbacks.Plugin): self.__parent = super(Scheduler, self) self.__parent.__init__(irc) self.events = {} + self._restoreEvents(irc) + world.flushers.append(self._flush) + + def _restoreEvents(self, irc): + try: + pkl = open(filename, 'rb') + try: + eventdict = pickle.load(pkl) + except Exception, e: + self.log.debug('Unable to load pickled data: %s', e) + return + finally: + pkl.close() + except IOError, e: + self.log.debug('Unable to open pickle file: %s', e) + return + for name, event in eventdict.iteritems(): + ircobj = callbacks.ReplyIrcProxy(irc, event['msg']) + try: + if event['type'] == 'single': # non-repeating event + n = None + if schedule.schedule.counter > int(name): + # counter not reset, we're probably reloading the plugin + # though we'll never know for sure, because other + # plugins can schedule stuff, too. + n = int(name) + self._add(ircobj, event['msg'], + event['time'], event['command'], n) + elif event['type'] == 'repeat': # repeating event + self._repeat(ircobj, event['msg'], name, + event['time'], event['command'], False) + except AssertionError, e: + if str(e) == 'An event with the same name has already been scheduled.': + # we must be reloading the plugin, event is still scheduled + self.log.info('Event %s already exists, adding to dict.' % (name,)) + self.events[name] = event + else: + raise + + def _flush(self): + try: + pklfd, tempfn = tempfile.mkstemp(suffix='scheduler', dir=datadir) + pkl = os.fdopen(pklfd, 'wb') + try: + pickle.dump(self.events, pkl) + except Exception, e: + self.log.warning('Unable to store pickled data: %s', e) + pkl.close() + shutil.move(tempfn, filename) + except (IOError, shutil.Error), e: + self.log.warning('File error: %s', e) + + def die(self): + self._flush() + world.flushers.remove(self._flush) + self.__parent.die() def _makeCommandFunction(self, irc, msg, command, remove=True): """Makes a function suitable for scheduling from command.""" @@ -90,6 +146,15 @@ class Scheduler(callbacks.Plugin): irc.error(_('Invalid event id.')) remove = wrap(remove, ['lowered']) + def _repeat(self, irc, msg, name, seconds, command, now=True): + f = self._makeCommandFunction(irc, msg, command, remove=False) + id = schedule.addPeriodicEvent(f, seconds, name, now) + assert id == name + self.events[name] = {'command':command, + 'msg':msg, + 'time':seconds, + 'type':'repeat'} + @internationalizeDocstring def repeat(self, irc, msg, args, name, seconds, command): """ From 0e67977cdd5d392be9ab50e03034d7830ab2a6ca Mon Sep 17 00:00:00 2001 From: Daniel Folkinshteyn Date: Tue, 7 Sep 2010 20:27:51 -0400 Subject: [PATCH 15/19] Badwords: add plugin docstring, and fix/standardize some method docstrings. --- plugins/BadWords/plugin.py | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/plugins/BadWords/plugin.py b/plugins/BadWords/plugin.py index 5ea306b00..5e3ba816d 100644 --- a/plugins/BadWords/plugin.py +++ b/plugins/BadWords/plugin.py @@ -43,6 +43,9 @@ from supybot.i18n import PluginInternationalization, internationalizeDocstring _ = PluginInternationalization('BadWords') class BadWords(callbacks.Privmsg): + """Maintains a list of words that the bot is not allowed to say. + Can also be used to kick people that say these words, if the bot + has op.""" def __init__(self, irc): self.__parent = super(BadWords, self) self.__parent.__init__(irc) @@ -68,7 +71,7 @@ class BadWords(callbacks.Privmsg): def inFilter(self, irc, msg): self.filtering = True # We need to check for bad words here rather than in doPrivmsg because - # messages don't get to doPrivmsg is the user is ignored. + # messages don't get to doPrivmsg if the user is ignored. if msg.command == 'PRIVMSG': self.updateRegexp() s = ircutils.stripFormatting(msg.args[1]) @@ -124,7 +127,7 @@ class BadWords(callbacks.Privmsg): def add(self, irc, msg, args, words): """ [ ...] - Adds all s to the list of words the bot isn't to say. + Adds all s to the list of words being censored. """ set = self.words() set.update(words) @@ -136,7 +139,7 @@ class BadWords(callbacks.Privmsg): def remove(self, irc, msg, args, words): """ [ ...] - Removes a s from the list of words the bot isn't to say. + Removes s from the list of words being censored. """ set = self.words() for word in words: From 817190ff6b7b21965d09ff37cf9b55316baf4595 Mon Sep 17 00:00:00 2001 From: Daniel Folkinshteyn Date: Wed, 8 Sep 2010 00:11:28 -0400 Subject: [PATCH 16/19] BadWords: improve help for requireWordBoundaries config. Make a note that the plugin requires restart or the words set updating, for changes to this setting to take effect. --- plugins/BadWords/config.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/plugins/BadWords/config.py b/plugins/BadWords/config.py index a2b130a4a..2961f348e 100644 --- a/plugins/BadWords/config.py +++ b/plugins/BadWords/config.py @@ -57,7 +57,9 @@ conf.registerGlobalValue(BadWords,'requireWordBoundaries', words to be independent words, or whether it will censor them within other words. For instance, if 'darn' is a bad word, then if this is true, 'darn' will be censored, but 'darnit' will not. You probably want this to be - false."""))) + false. After changing this setting, the BadWords regexp needs to be + regenerated by adding/removing a word to the list, or reloading the + plugin."""))) class String256(registry.String): def __call__(self): From bcc745b956d3f4acaa15eb5b30d20121a3384ab3 Mon Sep 17 00:00:00 2001 From: Valentin Lorentz Date: Sat, 29 Jan 2011 11:52:14 +0100 Subject: [PATCH 17/19] Update BadWord's .pot and fr.po --- plugins/BadWords/locale/fr.po | 57 ++++++++++++++++++++++++++--------- plugins/BadWords/messages.pot | 57 +++++++++++++++++++++++++++-------- 2 files changed, 87 insertions(+), 27 deletions(-) diff --git a/plugins/BadWords/locale/fr.po b/plugins/BadWords/locale/fr.po index f1c08d634..101478d6b 100644 --- a/plugins/BadWords/locale/fr.po +++ b/plugins/BadWords/locale/fr.po @@ -1,7 +1,7 @@ msgid "" msgstr "" "Project-Id-Version: Supybot-fr\n" -"POT-Creation-Date: 2010-10-16 18:51+CEST\n" +"POT-Creation-Date: 2011-01-29 11:48+CET\n" "PO-Revision-Date: \n" "Last-Translator: Valentin Lorentz \n" "Language-Team: Supybot-fr \n" @@ -13,6 +13,13 @@ msgstr "" "X-Poedit-Country: France\n" "X-Poedit-SourceCharset: ASCII\n" +#: __init__.py:30 +msgid "" +"\n" +"Filters bad words on outgoing messages from the bot, so the bot can't be made\n" +"to say bad words.\n" +msgstr "" + #: config.py:40 msgid "Would you like to add some bad words?" msgstr "Voulez-vous ajouter quelques mots interdits ?" @@ -33,17 +40,19 @@ msgid "" " words to be independent words, or whether it will censor them within other\n" " words. For instance, if 'darn' is a bad word, then if this is true, 'darn'\n" " will be censored, but 'darnit' will not. You probably want this to be\n" -" false." -msgstr "Détermine si le bot requiert que les gros mots soient indépendants, ou si ils peuvent être dans d'autres mots. Par exemple, si 'pute' est censuré et que c'est True, 'merde' sera censuré, mais pas 'emmerder'. Il est probablement mieux que ceci soit False." +" false. After changing this setting, the BadWords regexp needs to be\n" +" regenerated by adding/removing a word to the list, or reloading the\n" +" plugin." +msgstr "Détermine si le bot requiert que les gros mots soient indépendants, ou si ils peuvent être dans d'autres mots. Par exemple, si 'merde' est censuré et que c'est True, 'merde' sera censuré, mais pas 'emmerder'. Il est probablement mieux que ceci soit False. Après avoir modifié ce paramètre, l'expression régulière de BadWords doit être regénérée en ajoutant/supprimant un mot de la liste, ou en rechargeant le plugin." -#: config.py:71 +#: config.py:73 msgid "" "Determines what characters will replace bad words; a\n" " chunk of these characters matching the size of the replaced bad word will\n" " be used to replace the bad words you've configured." msgstr "Détermine par quels caractères seront remplacés les gros mots ; un morceau de ces caractères ayant la même taille que le mot remplacé sera utilisé pour remplacer les gros mots que vous avez configurés." -#: config.py:79 +#: config.py:81 msgid "" "Determines the manner in which\n" " bad words will be replaced. 'nastyCharacters' (the default) will replace a\n" @@ -54,13 +63,13 @@ msgid "" " supybot.plugins.BadWords.simpleReplacement." msgstr "Détermine la manière dont les gros mots sont remplacés. 'nastyCharacters' (par défaut) remplacera un gros mot par le même nombre de 'caractères obscènes' (comme ceux utilisés dans les bandes dessinées ; configurables dans supybot.plugins.BadWords.nastyChars). 'simple' remplacera le mot pas une simple chaîne (peu importe la taille du gros mot) ; cette chaîne est configurable dans supybot.plugins.BadWords.simpleReplacement." -#: config.py:87 +#: config.py:89 msgid "" "Determines what word will replace bad\n" " words if the replacement method is 'simple'." msgstr "Détermin quel mot remplacera les mots interdits, si la méthode de remplacement est 'simple'." -#: config.py:90 +#: config.py:92 msgid "" "Determines whether the bot will strip\n" " formatting characters from messages before it checks them for bad words.\n" @@ -69,13 +78,33 @@ msgid "" " plugins that do coloring or bolding of text." msgstr "Détermine si le bot retirera les caractères de formattage avant de vérifier qu'ils contiennent des gros mots. Si ceci est False, ce sera relativement simple de contourner la protection. Si c'est True, toutefois, il fera perdre toute couleur ou formattage des autres plugins" -#: config.py:97 +#: config.py:99 msgid "" "Determines whether the bot will kick people with\n" " a warning when they use bad words." msgstr "Détermine si le bot kickera les gens avec un avertissement lorsqu'ils utilisent des gros mots." -#: plugin.py:110 +#: config.py:102 +msgid "" +"You have been kicked for using a word\n" +" prohibited in the presence of this bot. Please use more appropriate\n" +" language in the future." +msgstr "Vous avez été kické(e) parce que vous avez utilisé un mot interdit en la présence de ce bot. Veuillez utiliser un langage plus approprié par le futur." + +#: config.py:104 +msgid "" +"Determines the kick message used by the\n" +" bot when kicking users for saying bad words." +msgstr "Détermine le message de kick utilisé par le bot lorsqu'il kick des utilisateurs utilisant des gros mots." + +#: plugin.py:46 +msgid "" +"Maintains a list of words that the bot is not allowed to say.\n" +" Can also be used to kick people that say these words, if the bot\n" +" has op." +msgstr "" + +#: plugin.py:113 msgid "" "takes no arguments\n" "\n" @@ -86,26 +115,26 @@ msgstr "" "\n" "Retourne une liste de mots qui sont censurés." -#: plugin.py:120 +#: plugin.py:123 msgid "I'm not currently censoring any bad words." msgstr "Je ne censure actuellement aucun mot." -#: plugin.py:125 +#: plugin.py:128 msgid "" " [ ...]\n" "\n" -" Adds all s to the list of words the bot isn't to say.\n" +" Adds all s to the list of words being censored.\n" " " msgstr "" " [ ...]\n" "\n" "Ajoute tous les mots à la liste des mots que le bot ne doit pas dire." -#: plugin.py:137 +#: plugin.py:140 msgid "" " [ ...]\n" "\n" -" Removes a s from the list of words the bot isn't to say.\n" +" Removes s from the list of words being censored.\n" " " msgstr "" " [ ...]\n" diff --git a/plugins/BadWords/messages.pot b/plugins/BadWords/messages.pot index 8363e3e24..5bc528674 100644 --- a/plugins/BadWords/messages.pot +++ b/plugins/BadWords/messages.pot @@ -5,7 +5,7 @@ msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" -"POT-Creation-Date: 2010-10-16 18:51+CEST\n" +"POT-Creation-Date: 2011-01-29 11:48+CET\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" @@ -15,6 +15,14 @@ msgstr "" "Generated-By: pygettext.py 1.5\n" +#: __init__.py:30 +#, docstring +msgid "" +"\n" +"Filters bad words on outgoing messages from the bot, so the bot can't be made\n" +"to say bad words.\n" +msgstr "" + #: config.py:40 msgid "Would you like to add some bad words?" msgstr "" @@ -35,17 +43,19 @@ msgid "" " words to be independent words, or whether it will censor them within other\n" " words. For instance, if 'darn' is a bad word, then if this is true, 'darn'\n" " will be censored, but 'darnit' will not. You probably want this to be\n" -" false." +" false. After changing this setting, the BadWords regexp needs to be\n" +" regenerated by adding/removing a word to the list, or reloading the\n" +" plugin." msgstr "" -#: config.py:71 +#: config.py:73 msgid "" "Determines what characters will replace bad words; a\n" " chunk of these characters matching the size of the replaced bad word will\n" " be used to replace the bad words you've configured." msgstr "" -#: config.py:79 +#: config.py:81 msgid "" "Determines the manner in which\n" " bad words will be replaced. 'nastyCharacters' (the default) will replace a\n" @@ -56,13 +66,13 @@ msgid "" " supybot.plugins.BadWords.simpleReplacement." msgstr "" -#: config.py:87 +#: config.py:89 msgid "" "Determines what word will replace bad\n" " words if the replacement method is 'simple'." msgstr "" -#: config.py:90 +#: config.py:92 msgid "" "Determines whether the bot will strip\n" " formatting characters from messages before it checks them for bad words.\n" @@ -71,13 +81,34 @@ msgid "" " plugins that do coloring or bolding of text." msgstr "" -#: config.py:97 +#: config.py:99 msgid "" "Determines whether the bot will kick people with\n" " a warning when they use bad words." msgstr "" -#: plugin.py:110 +#: config.py:102 +msgid "" +"You have been kicked for using a word\n" +" prohibited in the presence of this bot. Please use more appropriate\n" +" language in the future." +msgstr "" + +#: config.py:104 +msgid "" +"Determines the kick message used by the\n" +" bot when kicking users for saying bad words." +msgstr "" + +#: plugin.py:46 +#, docstring +msgid "" +"Maintains a list of words that the bot is not allowed to say.\n" +" Can also be used to kick people that say these words, if the bot\n" +" has op." +msgstr "" + +#: plugin.py:113 #, docstring msgid "" "takes no arguments\n" @@ -86,25 +117,25 @@ msgid "" " " msgstr "" -#: plugin.py:120 +#: plugin.py:123 msgid "I'm not currently censoring any bad words." msgstr "" -#: plugin.py:125 +#: plugin.py:128 #, docstring msgid "" " [ ...]\n" "\n" -" Adds all s to the list of words the bot isn't to say.\n" +" Adds all s to the list of words being censored.\n" " " msgstr "" -#: plugin.py:137 +#: plugin.py:140 #, docstring msgid "" " [ ...]\n" "\n" -" Removes a s from the list of words the bot isn't to say.\n" +" Removes s from the list of words being censored.\n" " " msgstr "" From c456abd25c609b70af9664325b7e83482477e253 Mon Sep 17 00:00:00 2001 From: Valentin Lorentz Date: Sun, 30 Jan 2011 12:04:57 +0100 Subject: [PATCH 18/19] Later: fix french translation --- plugins/Later/locale/fr.po | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/Later/locale/fr.po b/plugins/Later/locale/fr.po index b03053f05..49f8ded4d 100644 --- a/plugins/Later/locale/fr.po +++ b/plugins/Later/locale/fr.po @@ -104,5 +104,5 @@ msgstr "Il n'y a pas de note pour %r" #: plugin.py:182 msgid "Sent %s: <%s> %s" -msgstr "Envoyé le %s : <%s> %s" +msgstr "Envoyé %s : <%s> %s" From 5395000e760198475f466e5c209caae6d4115ff5 Mon Sep 17 00:00:00 2001 From: Valentin Lorentz Date: Wed, 9 Feb 2011 17:16:26 +0100 Subject: [PATCH 19/19] Change website supybot-fr.tk -> supybot.fr.cr --- plugins/Config/locale/fr.po | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/Config/locale/fr.po b/plugins/Config/locale/fr.po index 9eb4cb258..0178e0a67 100644 --- a/plugins/Config/locale/fr.po +++ b/plugins/Config/locale/fr.po @@ -34,7 +34,7 @@ msgid "" msgstr "" "\n" "\n" -"Retourne les variables de configuration qui sont dans le de configuration. Si la variable a des sous-variables, elle sera précédée par le signe '@'. Si une variable est une 'ChannelValue', elle sera précédée par le signe '#'. Plus d'informations : http://supybot-fr.tk/Configuration" +"Retourne les variables de configuration qui sont dans le de configuration. Si la variable a des sous-variables, elle sera précédée par le signe '@'. Si une variable est une 'ChannelValue', elle sera précédée par le signe '#'. Plus d'informations : http://supybot.fr.cr/Configuration" #: plugin.py:148 msgid "There don't seem to be any values in %s."