diff --git a/plugins/Bugzilla.py b/plugins/Bugzilla.py index 601c96886..cb3b7d41c 100644 --- a/plugins/Bugzilla.py +++ b/plugins/Bugzilla.py @@ -52,6 +52,7 @@ import ircutils import privmsgs import callbacks import structures +import configurable dbfilename = os.path.join(conf.dataDir, 'Bugzilla.db') def makeDb(filename): @@ -83,18 +84,18 @@ def configure(onStart, afterConnect, advanced): replyNoBugzilla = 'I don\'t have a bugzilla %r' -class Bugzilla(callbacks.PrivmsgCommandAndRegexp, plugins.Configurable): +class Bugzilla(callbacks.PrivmsgCommandAndRegexp, configurable.Mixin): """Show a link to a bug report with a brief description""" threaded = True regexps = ['bzSnarfer'] - configurables = plugins.ConfigurableDictionary( - [('bug-snarfer', plugins.ConfigurableBoolType, False, + configurables = configurable.Dictionary( + [('bug-snarfer', configurable.BoolType, False, """Determines whether the bug snarfer will be enabled, such that any Bugzilla URLs seen in the channel will have their information reported into the channel.""")] ) def __init__(self): - plugins.Configurable.__init__(self) + configurable.Mixin.__init__(self) callbacks.PrivmsgCommandAndRegexp.__init__(self) self.entre = re.compile('&(\S*?);') # Schema: {name, [url, description]} @@ -102,7 +103,7 @@ class Bugzilla(callbacks.PrivmsgCommandAndRegexp, plugins.Configurable): self.shorthand = utils.abbrev(self.db.keys()) def die(self): - plugins.Configurable.die(self) + configurable.Mixin.die(self) self.db.close() del self.db diff --git a/plugins/ChannelDB.py b/plugins/ChannelDB.py index 55a92c48a..2df34f75e 100644 --- a/plugins/ChannelDB.py +++ b/plugins/ChannelDB.py @@ -57,6 +57,7 @@ import plugins import privmsgs import ircutils import callbacks +import configurable smileys = (':)', ';)', ':]', ':-)', ':-D', ':D', ':P', ':p', '(=', '=)') frowns = (':|', ':-/', ':-\\', ':\\', ':/', ':(', ':-(', ':\'(') @@ -65,21 +66,21 @@ smileyre = re.compile('|'.join(imap(re.escape, smileys))) frownre = re.compile('|'.join(imap(re.escape, frowns))) class ChannelDB(plugins.ChannelDBHandler, - plugins.Configurable, + configurable.Mixin, callbacks.Privmsg): noIgnore = True - configurables = plugins.ConfigurableDictionary( - [('self-stats', plugins.ConfigurableBoolType, True, + configurables = configurable.Dictionary( + [('self-stats', configurable.BoolType, True, """Determines whether the bot will keep channel statistics on itself, possibly skewing the channel stats (especially in cases where he's relaying between channels on a network."""), - ('wordstats-top-n', plugins.ConfigurableIntType, 3, + ('wordstats-top-n', configurable.IntType, 3, """Determines the maximum number of top users to show for a given wordstat when you request the wordstats for a particular word.""")] ) def __init__(self): callbacks.Privmsg.__init__(self) - plugins.Configurable.__init__(self) + configurable.Mixin.__init__(self) plugins.ChannelDBHandler.__init__(self) self.lastmsg = None self.laststate = None @@ -87,7 +88,7 @@ class ChannelDB(plugins.ChannelDBHandler, def die(self): callbacks.Privmsg.die(self) - plugins.Configurable.die(self) + configurable.Mixin.die(self) plugins.ChannelDBHandler.die(self) def makeDb(self, filename): diff --git a/plugins/Debian.py b/plugins/Debian.py index 61a714ec4..2e11c56c1 100644 --- a/plugins/Debian.py +++ b/plugins/Debian.py @@ -53,6 +53,7 @@ import conf import utils import privmsgs import callbacks +import configurable def configure(onStart, afterConnect, advanced): @@ -82,7 +83,7 @@ def configure(onStart, afterConnect, advanced): onStart.append('disable file') class Debian(callbacks.Privmsg, - plugins.Configurable, + configurable.Mixin, plugins.PeriodicFileDownloader): threaded = True periodicFiles = { @@ -93,8 +94,8 @@ class Debian(callbacks.Privmsg, 604800, None) } contents = os.path.join(conf.dataDir, 'Contents-i386.gz') - configurables = plugins.ConfigurableDictionary( - [('python-zegrep', plugins.ConfigurableBoolType, False, + configurables = configurable.Dictionary( + [('python-zegrep', configurable.BoolType, False, """An advanced option, mostly just for testing; uses a Python-coded zegrep rather than the actual zegrep executable, generally resulting in a 50x slowdown. What would take 2 seconds will take 100 with this @@ -102,12 +103,12 @@ class Debian(callbacks.Privmsg, ) def __init__(self): callbacks.Privmsg.__init__(self) - plugins.Configurable.__init__(self) + configurable.Mixin.__init__(self) plugins.PeriodicFileDownloader.__init__(self) def die(self): callbacks.Privmsg.die(self) - plugins.Configurable.die(self) + configurable.Mixin.die(self) def file(self, irc, msg, args): """[--{regexp,exact}=] [] diff --git a/plugins/Dict.py b/plugins/Dict.py index e88451a1e..904f40e30 100644 --- a/plugins/Dict.py +++ b/plugins/Dict.py @@ -49,6 +49,7 @@ import plugins import ircutils import privmsgs import callbacks +import configurable def configure(onStart, afterConnect, advanced): @@ -64,20 +65,20 @@ def configure(onStart, afterConnect, advanced): onStart.append('dict config server %s' % server) replyTimeout = 'Timeout on the dictd server.' -class Dict(callbacks.Privmsg, plugins.Configurable): +class Dict(callbacks.Privmsg, configurable.Mixin): threaded = True - configurables = plugins.ConfigurableDictionary( - [('server', plugins.ConfigurableStrType, 'dict.org', + configurables = configurable.Dictionary( + [('server', configurable.StrType, 'dict.org', """Determines what server the bot will connect to to receive definitions from."""),] ) def __init__(self): callbacks.Privmsg.__init__(self) - plugins.Configurable.__init__(self) + configurable.Mixin.__init__(self) def die(self): callbacks.Privmsg.die(self) - plugins.Configurable.die(self) + configurable.Mixin.die(self) def dictionaries(self, irc, msg, args): """takes no arguments. diff --git a/plugins/Ebay.py b/plugins/Ebay.py index 6a6185dd9..29f9a49f6 100644 --- a/plugins/Ebay.py +++ b/plugins/Ebay.py @@ -48,6 +48,7 @@ import plugins import ircutils import privmsgs import callbacks +import configurable def configure(onStart, afterConnect, advanced): @@ -67,24 +68,24 @@ def configure(onStart, afterConnect, advanced): class EbayError(callbacks.Error): pass -class Ebay(callbacks.PrivmsgCommandAndRegexp, plugins.Configurable): +class Ebay(callbacks.PrivmsgCommandAndRegexp, configurable.Mixin): """ Module for eBay stuff. Currently contains a URL snarfer and a command to get info about an auction. """ threaded = True regexps = ['ebaySnarfer'] - configurables = plugins.ConfigurableDictionary( - [('auction-snarfer', plugins.ConfigurableBoolType, False, + configurables = configurable.Dictionary( + [('auction-snarfer', configurable.BoolType, False, """Determines whether the bot will automatically 'snarf' Ebay auction URLs and print information about them.""")] ) def __init__(self): - plugins.Configurable.__init__(self) + configurable.Mixin.__init__(self) callbacks.PrivmsgCommandAndRegexp.__init__(self) def die(self): - plugins.Configurable.die(self) + configurable.Mixin.die(self) callbacks.PrivmsgCommandAndRegexp.die(self) _reopts = re.I | re.S diff --git a/plugins/Enforcer.py b/plugins/Enforcer.py index a516934b9..875be1ec3 100644 --- a/plugins/Enforcer.py +++ b/plugins/Enforcer.py @@ -45,6 +45,7 @@ import plugins import privmsgs import ircutils import callbacks +import configurable def configure(onStart, afterConnect, advanced): from questions import expect, anything, something, yn @@ -61,18 +62,18 @@ def configure(onStart, afterConnect, advanced): # Enforcer: Enforces capabilities on JOIN, MODE, KICK, etc. ### _chanCap = ircdb.makeChannelCapability -class Enforcer(callbacks.Privmsg, plugins.Configurable): - configurables = plugins.ConfigurableDictionary( - [('auto-op', plugins.ConfigurableBoolType, False, +class Enforcer(callbacks.Privmsg, configurable.Mixin): + configurables = configurable.Dictionary( + [('auto-op', configurable.BoolType, False, """Determines whether the bot will automatically op people with the .op capability when they join the channel."""), - ('auto-voice', plugins.ConfigurableBoolType, False, + ('auto-voice', configurable.BoolType, False, """Determines whether the bot will automatically voice people with the .voice capability when they join the channel."""), - ('auto-halfop', plugins.ConfigurableBoolType, False, + ('auto-halfop', configurable.BoolType, False, """Determines whether the bot will automatically halfop people with the .halfop capability when they join the channel."""), - ('revenge', plugins.ConfigurableBoolType, False, + ('revenge', configurable.BoolType, False, """Determines whether the bot will take revenge on people who do things it doesn't like (somewhat like 'bitch mode' in other IRC bots)."""),] @@ -80,11 +81,11 @@ class Enforcer(callbacks.Privmsg, plugins.Configurable): started = False def __init__(self): callbacks.Privmsg.__init__(self) - plugins.Configurable.__init__(self) + configurable.Mixin.__init__(self) def die(self): callbacks.Privmsg.die(self) - plugins.Configurable.die(self) + configurable.Mixin.die(self) def start(self, irc, msg, args): """[] diff --git a/plugins/FunDB.py b/plugins/FunDB.py index 5a64ced73..9df028c3a 100755 --- a/plugins/FunDB.py +++ b/plugins/FunDB.py @@ -55,6 +55,7 @@ import ircmsgs import ircutils import privmsgs import callbacks +import configurable tableCreateStatements = { 'larts': ("""CREATE TABLE larts ( @@ -79,26 +80,26 @@ tableCreateStatements = { )""",), } -class FunDB(callbacks.Privmsg, plugins.Configurable, plugins.ChannelDBHandler): +class FunDB(callbacks.Privmsg, configurable.Mixin, plugins.ChannelDBHandler): """ Contains the 'fun' commands that require a database. Currently includes database-backed commands for crossword puzzle solving, anagram searching, larting, praising, excusing, and insulting. """ - configurables = plugins.ConfigurableDictionary( - [('show-ids', plugins.ConfigurableBoolType, False, + configurables = configurable.Dictionary( + [('show-ids', configurable.BoolType, False, """Determines whether the bot will show the id of an excuse/insult/praise/lart.""")] ) _tables = sets.Set(['lart', 'insult', 'excuse', 'praise']) def __init__(self): callbacks.Privmsg.__init__(self) - plugins.Configurable.__init__(self) + configurable.Mixin.__init__(self) plugins.ChannelDBHandler.__init__(self) def die(self): callbacks.Privmsg.die(self) - plugins.Configurable.die(self) + configurable.Mixin.die(self) plugins.ChannelDBHandler.die(self) def makeDb(self, dbfilename, replace=False): diff --git a/plugins/Gameknot.py b/plugins/Gameknot.py index 74bc744c4..ce6a5ab1e 100644 --- a/plugins/Gameknot.py +++ b/plugins/Gameknot.py @@ -47,6 +47,7 @@ import plugins import ircutils import privmsgs import callbacks +import configurable def configure(onStart, afterConnect, advanced): @@ -69,15 +70,15 @@ def configure(onStart, afterConnect, advanced): onStart.append('Gameknot toggle stat off') -class Gameknot(callbacks.PrivmsgCommandAndRegexp, plugins.Configurable): +class Gameknot(callbacks.PrivmsgCommandAndRegexp, configurable.Mixin): threaded = True regexps = ['gameknotSnarfer', 'gameknotStatsSnarfer'] - configurables = plugins.ConfigurableDictionary( - [('game-snarfer', plugins.ConfigurableBoolType, True, + configurables = configurable.Dictionary( + [('game-snarfer', configurable.BoolType, True, """Determines whether the game URL snarfer is active; if so, the bot will reply to the channel with a summary of the game data when it sees a Gameknot game on the channel."""), - ('stats-snarfer', plugins.ConfigurableBoolType, True, + ('stats-snarfer', configurable.BoolType, True, """Determines whether the stats URL snarfer is active; if so, the bot will reply to the channel with a summary of the stats of any player whose stats URL is seen on the channel.""")] @@ -89,11 +90,11 @@ class Gameknot(callbacks.PrivmsgCommandAndRegexp, plugins.Configurable): _gkteam = re.compile(r'Team:(<.*?>)+(?P.*?)') _gkseen = re.compile(r'(seen on GK:\s+([^[]+ago)|.*?is hiding.*?)') def __init__(self): - plugins.Configurable.__init__(self) + configurable.Mixin.__init__(self) callbacks.PrivmsgCommandAndRegexp.__init__(self) def die(self): - plugins.Configurable.die(self) + configurable.Mixin.die(self) callbacks.PrivmsgCommandAndRegexp.die(self) def getStats(self, name): diff --git a/plugins/Google.py b/plugins/Google.py index 16951c713..72d5c37d9 100644 --- a/plugins/Google.py +++ b/plugins/Google.py @@ -55,6 +55,7 @@ import ircutils import privmsgs import callbacks import structures +import configurable def configure(onStart, afterConnect, advanced): from questions import expect, anything, something, yn @@ -125,28 +126,28 @@ def search(log, *args, **kwargs): log.exception('Uncaught SOAP error:') raise callbacks.Error, 'Invalid Google license key.' -class Google(callbacks.PrivmsgCommandAndRegexp, plugins.Configurable): +class Google(callbacks.PrivmsgCommandAndRegexp, configurable.Mixin): threaded = True regexps = sets.Set(['googleSnarfer', 'googleGroups']) - configurables = plugins.ConfigurableDictionary( - [('groups-snarfer', plugins.ConfigurableBoolType, False, + configurables = configurable.Dictionary( + [('groups-snarfer', configurable.BoolType, False, """Determines whether the groups snarfer is enabled. If so, URLs at groups.google.com will be snarfed and their group/title messaged to the channel."""), - ('search-snarfer', plugins.ConfigurableBoolType, False, + ('search-snarfer', configurable.BoolType, False, """Determines whether the search snarfer is enabled. If so, messages (even unaddressed ones) beginning with the word 'google' will result in the first URL Google returns being sent to the channel.""")] ) def __init__(self): - plugins.Configurable.__init__(self) + configurable.Mixin.__init__(self) callbacks.PrivmsgCommandAndRegexp.__init__(self) self.total = 0 self.totalTime = 0 self.last24hours = structures.queue() def die(self): - plugins.Configurable.die(self) + configurable.Mixin.die(self) callbacks.PrivmsgCommandAndRegexp.die(self) def formatData(self, data): diff --git a/plugins/Karma.py b/plugins/Karma.py index 8ebcefb0a..29cba5419 100644 --- a/plugins/Karma.py +++ b/plugins/Karma.py @@ -46,6 +46,7 @@ import utils import plugins import privmsgs import callbacks +import configurable def configure(onStart, afterConnect, advanced): @@ -57,23 +58,23 @@ def configure(onStart, afterConnect, advanced): onStart.append('load Karma') class Karma(callbacks.PrivmsgCommandAndRegexp, - plugins.Configurable, + configurable.Mixin, plugins.ChannelDBHandler): addressedRegexps = ['increaseKarma', 'decreaseKarma'] - configurables = plugins.ConfigurableDictionary( - [('simple-output', plugins.ConfigurableBoolType, False, + configurables = configurable.Dictionary( + [('simple-output', configurable.BoolType, False, """Determines whether the bot will output shorter versions of the karma output when requesting a single thing's karma. (example: 'foo: 1')""")] ) def __init__(self): callbacks.PrivmsgCommandAndRegexp.__init__(self) - plugins.Configurable.__init__(self) + configurable.Mixin.__init__(self) plugins.ChannelDBHandler.__init__(self) def die(self): callbacks.PrivmsgCommandAndRegexp.die(self) - plugins.Configurable.die(self) + configurable.Mixin.die(self) plugins.ChannelDBHandler.die(self) def makeDb(self, filename): diff --git a/plugins/Python.py b/plugins/Python.py index bf03c7550..3c1f3dadb 100644 --- a/plugins/Python.py +++ b/plugins/Python.py @@ -57,6 +57,7 @@ import webutils import ircutils import privmsgs import callbacks +import configurable L = [os.__file__] if hasattr(math, '__file__'): @@ -75,22 +76,22 @@ def configure(onStart, afterConnect, advanced): Would you like to enable this snarfer?""") == 'y': onStart.append('python config aspn-snarfer on') -class Python(callbacks.PrivmsgCommandAndRegexp, plugins.Configurable): +class Python(callbacks.PrivmsgCommandAndRegexp, configurable.Mixin): modulechars = '%s%s%s' % (string.ascii_letters, string.digits, '_.') regexps = ['aspnRecipes'] - configurables = plugins.ConfigurableDictionary( - [('aspn-snarfer', plugins.ConfigurableBoolType, False, + configurables = configurable.Dictionary( + [('aspn-snarfer', configurable.BoolType, False, """Determines whether the ASPN Python recipe snarfer is enabled. If so, it will message the channel with the name of the recipe when it sees an ASPN Python recipe link on the channel.""")] ) def __init__(self): - plugins.Configurable.__init__(self) + configurable.Mixin.__init__(self) callbacks.PrivmsgCommandAndRegexp.__init__(self) def die(self): - plugins.Configurable.die(self) + configurable.Mixin.die(self) callbacks.PrivmsgCommandAndRegexp.die(self) def pydoc(self, irc, msg, args): diff --git a/plugins/QuoteGrabs.py b/plugins/QuoteGrabs.py index 5dccfdcc3..f3239a70b 100644 --- a/plugins/QuoteGrabs.py +++ b/plugins/QuoteGrabs.py @@ -54,6 +54,7 @@ import plugins import ircutils import privmsgs import callbacks +import configurable def configure(onStart, afterConnect, advanced): @@ -69,10 +70,10 @@ minRandomLength = 8 minRandomWords = 3 class QuoteGrabs(plugins.ChannelDBHandler, - plugins.Configurable, + configurable.Mixin, callbacks.Privmsg): - configurables = plugins.ConfigurableDictionary( - [('random-grabber', plugins.ConfigurableBoolType, False, + configurables = configurable.Dictionary( + [('random-grabber', configurable.BoolType, False, """Determines whether the bot will randomly grab possibly-suitable quotes for someone."""),] ) diff --git a/plugins/Relay.py b/plugins/Relay.py index e7e263794..305b59845 100644 --- a/plugins/Relay.py +++ b/plugins/Relay.py @@ -51,6 +51,7 @@ import ircmsgs import ircutils import privmsgs import callbacks +import configurable def configure(onStart, afterConnect, advanced): import socket @@ -107,17 +108,17 @@ def reload(x=None): else: (ircs, ircstates, lastmsg, channels, abbreviations, originalIrc) = x -class Relay(callbacks.Privmsg, plugins.Configurable): +class Relay(callbacks.Privmsg, configurable.Mixin): noIgnore = True priority = sys.maxint - configurables = plugins.ConfigurableDictionary( - [('color', plugins.ConfigurableBoolType, True, + configurables = configurable.Dictionary( + [('color', configurable.BoolType, True, """Determines whether the bot will color relayed PRIVMSGs so as to make the messages easier to read."""),] ) def __init__(self): callbacks.Privmsg.__init__(self) - plugins.Configurable.__init__(self) + configurable.Mixin.__init__(self) self.ircs = ircs self._color = 0 self._whois = {} @@ -139,7 +140,7 @@ class Relay(callbacks.Privmsg, plugins.Configurable): def die(self): callbacks.Privmsg.die(self) - plugins.Configurable.die(self) + configurable.Mixin.die(self) for irc in self.abbreviations: if irc != originalIrc: irc.callbacks[:] = [] diff --git a/plugins/Sourceforge.py b/plugins/Sourceforge.py index a7309aa8f..90f7b8c45 100644 --- a/plugins/Sourceforge.py +++ b/plugins/Sourceforge.py @@ -47,6 +47,7 @@ import ircutils import privmsgs import webutils import callbacks +import configurable def configure(onStart, afterConnect, advanced): @@ -94,7 +95,7 @@ def configure(onStart, afterConnect, advanced): class TrackerError(Exception): pass -class Sourceforge(callbacks.PrivmsgCommandAndRegexp, plugins.Configurable): +class Sourceforge(callbacks.PrivmsgCommandAndRegexp, configurable.Mixin): """ Module for Sourceforge stuff. Currently contains commands to query a project's most recent bugs and rfes. @@ -114,21 +115,21 @@ class Sourceforge(callbacks.PrivmsgCommandAndRegexp, plugins.Configurable): _status = re.compile(r'(Status): (.+?)', _reopts) _res =(_resolution, _assigned, _submitted, _priority, _status) - configurables = plugins.ConfigurableDictionary( - [('tracker-snarfer', plugins.ConfigurableBoolType, False, + configurables = configurable.Dictionary( + [('tracker-snarfer', configurable.BoolType, False, """Determines whether the bot will reply to SF.net Tracker URLs in the channel with a nice summary of the tracker item."""), - ('default-project', plugins.ConfigurableStrType, '', + ('default-project', configurable.StrType, '', """Sets the default project (used by the bugs/rfes commands in the case that no explicit project is given).""")] ) _projectURL = 'http://sourceforge.net/projects/' def __init__(self): - plugins.Configurable.__init__(self) + configurable.Mixin.__init__(self) callbacks.PrivmsgCommandAndRegexp.__init__(self) def die(self): - plugins.Configurable.die(self) + configurable.Mixin.die(self) callbacks.PrivmsgCommandAndRegexp.die(self) def _formatResp(self, text, num=''): diff --git a/plugins/Topic.py b/plugins/Topic.py index 7be9a2483..69cc349c8 100644 --- a/plugins/Topic.py +++ b/plugins/Topic.py @@ -47,25 +47,26 @@ import ircmsgs import plugins import privmsgs import callbacks +import configurable def ConfigurableTopicSeparator(s): - s = plugins.ConfigurableStrType(s) + s = configurable.StrType(s) if s.lstrip() == s: s = ' ' + s if s.rstrip() == s: s += ' ' return s -class Topic(callbacks.Privmsg, plugins.Configurable): +class Topic(callbacks.Privmsg, configurable.Mixin): topicFormatter = '%s (%s)' topicUnformatter = re.compile('(.*) \((\S+)\)') - configurables = plugins.ConfigurableDictionary( - [('separator', plugins.ConfigurableStrType, ' || ', + configurables = configurable.Dictionary( + [('separator', configurable.StrType, ' || ', "The separator between individual topics in the channel topic.")] ) def __init__(self): callbacks.Privmsg.__init__(self) - plugins.Configurable.__init__(self) + configurable.Mixin.__init__(self) def _splitTopic(self, topic, channel): separator = self.configurables.get('separator', channel) diff --git a/plugins/URL.py b/plugins/URL.py index cb7593ecb..eee91c69c 100644 --- a/plugins/URL.py +++ b/plugins/URL.py @@ -54,6 +54,7 @@ import ircmsgs import ircutils import privmsgs import callbacks +import configurable def configure(onStart, afterConnect, advanced): # This will be called by setup.py to configure this module. onStart and @@ -82,26 +83,26 @@ def configure(onStart, afterConnect, advanced): length that will trigger this snarfer?""") class URL(callbacks.PrivmsgCommandAndRegexp, - plugins.Configurable, + configurable.Mixin, plugins.ChannelDBHandler): regexps = ['tinyurlSnarfer'] - configurables = plugins.ConfigurableDictionary( - [('tinyurl-snarfer', plugins.ConfigurableBoolType, False, + configurables = configurable.Dictionary( + [('tinyurl-snarfer', configurable.BoolType, False, """Determines whether the bot will output shorter versions of URLs longer than the tinyurl-minimum-length config variable."""), - ('tinyurl-minimum-length', plugins.ConfigurableIntType, 46, + ('tinyurl-minimum-length', configurable.IntType, 46, """The minimum length a URL must be before the tinyurl-snarfer will snarf it and offer a tinyurl replacement."""),] ) def __init__(self): self.nextMsgs = {} callbacks.PrivmsgCommandAndRegexp.__init__(self) - plugins.Configurable.__init__(self) + configurable.Mixin.__init__(self) plugins.ChannelDBHandler.__init__(self) def die(self): callbacks.PrivmsgCommandAndRegexp.die(self) - plugins.Configurable.die(self) + configurable.Mixin.die(self) plugins.ChannelDBHandler.die(self) def makeDb(self, filename): diff --git a/src/configurable.py b/src/configurable.py new file mode 100644 index 000000000..d61730bab --- /dev/null +++ b/src/configurable.py @@ -0,0 +1,212 @@ +#!/usr/bin/env python + +### +# Copyright (c) 2002, Jeremiah Fincher +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are met: +# +# * Redistributions of source code must retain the above copyright notice, +# this list of conditions, and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright notice, +# this list of conditions, and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# * Neither the name of the author of this software nor the name of +# contributors to this software may be used to endorse or promote products +# derived from this software without specific prior written consent. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +# POSSIBILITY OF SUCH DAMAGE. +### + +__revision__ = "$Id$" + +import os + +import conf +import utils +import ircdb +import ircutils +import privmsgs +import callbacks + +class Dictionary(object): + """This is a dictionary to handle configuration for individual channels, + including a default configuration for channels that haven't modified their + configuration from the default. + """ + def __init__(self, seq): + self.helps = {} + self.types = {} + self.defaults = {} + self.originalNames = {} + self.unparsedValues = {} + self.channels = ircutils.IrcDict() + for (name, type, default, help) in seq: + if ',' in name: + raise ValueError, 'There can be no commas in the name.' + original = name + name = callbacks.canonicalName(name) + self.originalNames[name] = original + self.helps[name] = utils.normalizeWhitespace(help) + self.types[name] = type + self.defaults[name] = default + + def get(self, name, channel=None): + name = callbacks.canonicalName(name) + if channel is not None: + try: + return self.channels[channel][name] + except KeyError: + return self.defaults[name] + else: + return self.defaults[name] + + def set(self, name, value, channel=None): + name = callbacks.canonicalName(name) + if name not in self.originalNames: + raise KeyError, name + if ',' in name: + raise ValueError, 'There can be no commas in the name.' + self.unparsedValues[(channel, name)] = value + if channel is not None: + d = self.channels.setdefault(channel, {}) + d[name] = self.types[name](value) + else: + self.defaults[name] = self.types[name](value) + + def help(self, name): + return self.helps[callbacks.canonicalName(name)] + + def names(self): + L = self.originalNames.values() + L.sort() + return L + +class Error(TypeError): + pass + +def BoolType(s): + s = s.lower() + if s in ('true', 'enable', 'on'): + return True + elif s in ('false', 'disable', 'off'): + return False + else: + s = 'Value must be one of on/off/true/false/enable/disable.' + raise Error, s + +def StrType(s): + if s and s[0] not in '\'"' and s[-1] not in '\'"': + s = repr(s) + try: + v = utils.safeEval(s) + if type(v) is not str: + raise ValueError + except ValueError: # This catches the utils.safeEval(s) errors too. + raise Error, 'Value must be a string.' + return v + +def IntType(s): + try: + return int(s) + except ValueError: + raise Error, 'Value must be an int.' + +class Mixin(object): + """A mixin class to provide a "config" command that can be consistent + across all plugins, in order to unify the configuration for each plugin. + + Plugins subclassing this should have a "configurables" attribute which is + a ConfigurableDictionary initialized with a list of 4-tuples of + (name, type, default, help). Name is the string name of the config + variable; type is a function taking a string and returning some value of + the type the variable is supposed to be; default is the default value the + variable should take on; help is a string that'll be returned to describe + the purpose of the config variable. + """ + def __init__(self): + className = self.__class__.__name__ + self.filename = os.path.join(conf.confDir, '%s-configurable'%className) + if os.path.exists(self.filename): + fd = file(self.filename) + for line in fd: + line = line.rstrip() + (channel, name, value) = line.split(',', 2) + if channel == 'default': + channel = None + try: + # The eval here is to turn from "'foo'" to 'foo'. + self.configurables.set(name, eval(value), channel) + except Error, e: + s = 'Couldn\'t read configurable from file: %s' + self.log.warning(s, e) + except KeyError, e: + s = 'Configurable variable %s doesn\'t exist anymore.' + self.log.warning(s, name) + + def die(self): + fd = file(self.filename, 'w') + L = self.configurables.unparsedValues.items() + L.sort() + for ((channel, name), value) in L: + if channel is None: + channel = 'default' + name = self.configurables.originalNames[name] + fd.write('%s,%s,%r\n' % (channel, name, value)) + fd.close() + + def config(self, irc, msg, args): + """[] [] [] + + Sets the value of config variable to on . If + is given but is not, returns the help and current value + for . If neither nor is given, returns the valid + config variables for this plugin. is only necessary if the + message isn't sent in the channel itself. + """ + try: + channel = privmsgs.getChannel(msg, args) + capability = ircdb.makeChannelCapability(channel, 'op') + except callbacks.ArgumentError: + raise + except callbacks.Error: + channel = None + capability = 'admin' + if not ircdb.checkCapability(msg.prefix, capability): + irc.error(msg, conf.replyNoCapability % capability) + return + (name, value) = privmsgs.getArgs(args, required=0, optional=2) + if not name: + irc.reply(msg, utils.commaAndify(self.configurables.names())) + return + try: + if not value: + help = self.configurables.help(name) + value = self.configurables.get(name, channel=channel) + s = '%s: %s (Current value: %r)' % (name, help, value) + irc.reply(msg, s) + return + try: + self.configurables.set(name, value, channel) + irc.reply(msg, conf.replySuccess) + except Error, e: + irc.error(msg, str(e)) + except KeyError: + irc.error(msg, 'There is no config variable %r' % name) + + + + +# vim:set shiftwidth=4 tabstop=8 expandtab textwidth=78: + diff --git a/src/plugins.py b/src/plugins.py index ff5639ed3..322ac27b5 100644 --- a/src/plugins.py +++ b/src/plugins.py @@ -241,171 +241,6 @@ class PeriodicFileDownloader(object): world.threadsSpawned += 1 -class ConfigurableDictionary(object): - """This is a dictionary to handle configuration for individual channels, - including a default configuration for channels that haven't modified their - configuration from the default. - """ - def __init__(self, seq): - self.helps = {} - self.types = {} - self.defaults = {} - self.originalNames = {} - self.unparsedValues = {} - self.channels = ircutils.IrcDict() - for (name, type, default, help) in seq: - if ',' in name: - raise ValueError, 'There can be no commas in the name.' - original = name - name = callbacks.canonicalName(name) - self.originalNames[name] = original - self.helps[name] = utils.normalizeWhitespace(help) - self.types[name] = type - self.defaults[name] = default - - def get(self, name, channel=None): - name = callbacks.canonicalName(name) - if channel is not None: - try: - return self.channels[channel][name] - except KeyError: - return self.defaults[name] - else: - return self.defaults[name] - - def set(self, name, value, channel=None): - name = callbacks.canonicalName(name) - if name not in self.originalNames: - raise KeyError, name - if ',' in name: - raise ValueError, 'There can be no commas in the name.' - self.unparsedValues[(channel, name)] = value - if channel is not None: - d = self.channels.setdefault(channel, {}) - d[name] = self.types[name](value) - else: - self.defaults[name] = self.types[name](value) - - def help(self, name): - return self.helps[callbacks.canonicalName(name)] - - def names(self): - L = self.originalNames.values() - L.sort() - return L - -class ConfigurableTypeError(TypeError): - pass - -def ConfigurableBoolType(s): - s = s.lower() - if s in ('true', 'enable', 'on'): - return True - elif s in ('false', 'disable', 'off'): - return False - else: - s = 'Value must be one of on/off/true/false/enable/disable.' - raise ConfigurableTypeError, s - -def ConfigurableStrType(s): - if s and s[0] not in '\'"' and s[-1] not in '\'"': - s = repr(s) - try: - v = utils.safeEval(s) - if type(v) is not str: - raise ValueError - except ValueError: - raise ConfigurableTypeError, 'Value must be a string.' - return v - -def ConfigurableIntType(s): - try: - return int(s) - except ValueError: - raise ConfigurableTypeError, 'Value must be an int.' - -class Configurable(object): - """A mixin class to provide a "config" command that can be consistent - across all plugins, in order to unify the configuration for each plugin. - - Plugins subclassing this should have a "configurables" attribute which is - a ConfigurableDictionary initialized with a list of 4-tuples of - (name, type, default, help). Name is the string name of the config - variable; type is a function taking a string and returning some value of - the type the variable is supposed to be; default is the default value the - variable should take on; help is a string that'll be returned to describe - the purpose of the config variable. - """ - def __init__(self): - className = self.__class__.__name__ - self.filename = os.path.join(conf.confDir, '%s-configurable'%className) - if os.path.exists(self.filename): - fd = file(self.filename) - for line in fd: - line = line.rstrip() - (channel, name, value) = line.split(',', 2) - if channel == 'default': - channel = None - try: - self.configurables.set(name, eval(value), channel) - except ConfigurableTypeError, e: - s = 'Couldn\'t read configurable from file: %s' - self.log.warning(s, e) - except KeyError, e: - s = 'Configurable variable %s doesn\'t exist anymore.' - self.log.warning(s, name) - - def die(self): - fd = file(self.filename, 'w') - L = self.configurables.unparsedValues.items() - L.sort() - for ((channel, name), value) in L: - if channel is None: - channel = 'default' - name = self.configurables.originalNames[name] - fd.write('%s,%s,%r\n' % (channel, name, value)) - fd.close() - - def config(self, irc, msg, args): - """[] [] [] - - Sets the value of config variable to on . If - is given but is not, returns the help and current value - for . If neither nor is given, returns the valid - config variables for this plugin. is only necessary if the - message isn't sent in the channel itself. - """ - try: - channel = privmsgs.getChannel(msg, args) - capability = ircdb.makeChannelCapability(channel, 'op') - except callbacks.ArgumentError: - raise - except callbacks.Error: - channel = None - capability = 'admin' - if not ircdb.checkCapability(msg.prefix, capability): - irc.error(msg, conf.replyNoCapability % capability) - return - (name, value) = privmsgs.getArgs(args, required=0, optional=2) - if not name: - irc.reply(msg, utils.commaAndify(self.configurables.names())) - return - try: - if not value: - help = self.configurables.help(name) - value = self.configurables.get(name, channel=channel) - s = '%s: %s (Current value: %r)' % (name, help, value) - irc.reply(msg, s) - return - try: - self.configurables.set(name, value, channel) - irc.reply(msg, conf.replySuccess) - except ConfigurableTypeError, e: - irc.error(msg, str(e)) - except KeyError: - irc.error(msg, 'There is no config variable %r' % name) - - _randomnickRe = re.compile(r'\$randomnick', re.I) _randomdateRe = re.compile(r'\$randomdate', re.I) _randomintRe = re.compile(r'\$randomint', re.I)