Made Configurable persistent.

This commit is contained in:
Jeremy Fincher 2003-11-11 15:58:20 +00:00
parent 028b23d41c
commit 5149d17b6a
12 changed files with 115 additions and 55 deletions

View File

@ -87,12 +87,13 @@ class Bugzilla(callbacks.PrivmsgCommandAndRegexp, plugins.Configurable):
threaded = True threaded = True
regexps = ['bzSnarfer'] regexps = ['bzSnarfer']
configurables = plugins.ConfigurableDictionary( configurables = plugins.ConfigurableDictionary(
[('bug-snarfer', plugins.ConfigurableTypes.bool, True, [('bug-snarfer', plugins.ConfigurableBoolType, True,
"""Determines whether the bug snarfer will be enabled, such that any """Determines whether the bug snarfer will be enabled, such that any
Bugzilla URLs seen in the channel will have their information reported Bugzilla URLs seen in the channel will have their information reported
into the channel.""")] into the channel.""")]
) )
def __init__(self): def __init__(self):
plugins.Configurable.__init__(self)
callbacks.PrivmsgCommandAndRegexp.__init__(self) callbacks.PrivmsgCommandAndRegexp.__init__(self)
self.entre = re.compile('&(\S*?);') self.entre = re.compile('&(\S*?);')
# Schema: {name, [url, description]} # Schema: {name, [url, description]}
@ -100,6 +101,7 @@ class Bugzilla(callbacks.PrivmsgCommandAndRegexp, plugins.Configurable):
self.shorthand = utils.abbrev(self.db.keys()) self.shorthand = utils.abbrev(self.db.keys())
def die(self): def die(self):
plugins.Configurable.die(self)
self.db.close() self.db.close()
del self.db del self.db

View File

@ -59,23 +59,29 @@ frowns = (':|', ':-/', ':-\\', ':\\', ':/', ':(', ':-(', ':\'(')
smileyre = re.compile('|'.join(map(re.escape, smileys))) smileyre = re.compile('|'.join(map(re.escape, smileys)))
frownre = re.compile('|'.join(map(re.escape, frowns))) frownre = re.compile('|'.join(map(re.escape, frowns)))
class ChannelDB(plugins.ChannelDBHandler, # Must be first (die). class ChannelDB(plugins.ChannelDBHandler,
plugins.Configurable, plugins.Configurable,
callbacks.Privmsg): callbacks.Privmsg):
noIgnore = True noIgnore = True
configurables = plugins.ConfigurableDictionary( configurables = plugins.ConfigurableDictionary(
[('self-stats', plugins.ConfigurableTypes.bool, True, [('self-stats', plugins.ConfigurableBoolType, True,
"""Determines whether the bot will keep channel statistics on itself, """Determines whether the bot will keep channel statistics on itself,
possibly skewing the channel stats (especially in cases where he's possibly skewing the channel stats (especially in cases where he's
relaying between channels on a network.""")] relaying between channels on a network.""")]
) )
def __init__(self): def __init__(self):
callbacks.Privmsg.__init__(self) callbacks.Privmsg.__init__(self)
plugins.Configurable.__init__(self)
plugins.ChannelDBHandler.__init__(self) plugins.ChannelDBHandler.__init__(self)
self.lastmsg = None self.lastmsg = None
self.laststate = None self.laststate = None
self.outFiltering = False self.outFiltering = False
def die(self):
callbacks.Privmsg.die(self)
plugins.Configurable.die(self)
plugins.ChannelDBHandler.die(self)
def makeDb(self, filename): def makeDb(self, filename):
if os.path.exists(filename): if os.path.exists(filename):
db = sqlite.connect(filename) db = sqlite.connect(filename)

View File

@ -72,13 +72,18 @@ class Ebay(callbacks.PrivmsgCommandAndRegexp, plugins.Configurable):
threaded = True threaded = True
regexps = ['ebaySnarfer'] regexps = ['ebaySnarfer']
configurables = plugins.ConfigurableDictionary( configurables = plugins.ConfigurableDictionary(
[('snarfer', utils.safeEval, True, [('snarfer', plugins.ConfigurableBoolType, True,
"""Determines whether the bot will automatically 'snarf' Ebay auction """Determines whether the bot will automatically 'snarf' Ebay auction
URLs and print information about them.""")] URLs and print information about them.""")]
) )
def __init__(self): def __init__(self):
plugins.Configurable.__init__(self)
callbacks.PrivmsgCommandAndRegexp.__init__(self) callbacks.PrivmsgCommandAndRegexp.__init__(self)
def die(self):
plugins.Configurable.die(self)
callbacks.PrivmsgCommandAndRegexp.die(self)
_reopts = re.I | re.S _reopts = re.I | re.S
_invalid = re.compile(r'(is invalid, still pending, or no longer in our '\ _invalid = re.compile(r'(is invalid, still pending, or no longer in our '\
'database)', _reopts) 'database)', _reopts)

View File

@ -61,19 +61,24 @@ def configure(onStart, afterConnect, advanced):
_chanCap = ircdb.makeChannelCapability _chanCap = ircdb.makeChannelCapability
class Enforcer(callbacks.Privmsg, plugins.Configurable): class Enforcer(callbacks.Privmsg, plugins.Configurable):
configurables = plugins.ConfigurableDictionary( configurables = plugins.ConfigurableDictionary(
[('auto-op', plugins.ConfigurableTypes.bool, False, [('auto-op', plugins.ConfigurableBoolType, False,
"""Determines whether the bot will automatically op people with """Determines whether the bot will automatically op people with
the <channel>.op capability when they join the channel."""), the <channel>.op capability when they join the channel."""),
('auto-voice', plugins.ConfigurableTypes.bool, False, ('auto-voice', plugins.ConfigurableBoolType, False,
"""Determines whether the bot will automatically voice people with """Determines whether the bot will automatically voice people with
the <channel>.voice capability when they join the channel."""), the <channel>.voice capability when they join the channel."""),
('auto-halfop', plugins.ConfigurableTypes.bool, False, ('auto-halfop', plugins.ConfigurableBoolType, False,
"""Determines whether the bot will automatically halfop people with """Determines whether the bot will automatically halfop people with
the <channel>.halfop capability when they join the channel."""),] the <channel>.halfop capability when they join the channel."""),]
) )
started = False started = False
def __init__(self): def __init__(self):
callbacks.Privmsg.__init__(self) callbacks.Privmsg.__init__(self)
plugins.Configurable.__init__(self)
def die(self):
callbacks.Privmsg.die(self)
plugins.Configurable.die(self)
def start(self, irc, msg, args): def start(self, irc, msg, args):
"""[<CHANSERV> <revenge>] """[<CHANSERV> <revenge>]

View File

@ -72,11 +72,11 @@ class Gameknot(callbacks.PrivmsgCommandAndRegexp, plugins.Configurable):
threaded = True threaded = True
regexps = ['gameknotSnarfer', 'gameknotStatsSnarfer'] regexps = ['gameknotSnarfer', 'gameknotStatsSnarfer']
configurables = plugins.ConfigurableDictionary( configurables = plugins.ConfigurableDictionary(
[('game-snarfer', plugins.ConfigurableTypes.bool, True, [('game-snarfer', plugins.ConfigurableBoolType, True,
"""Determines whether the game URL snarfer is active; if so, the bot """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 will reply to the channel with a summary of the game data when it
sees a Gameknot game on the channel."""), sees a Gameknot game on the channel."""),
('stats-snarfer', plugins.ConfigurableTypes.bool, True, ('stats-snarfer', plugins.ConfigurableBoolType, True,
"""Determines whether the stats URL snarfer is active; if so, the bot """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 will reply to the channel with a summary of the stats of any player
whose stats URL is seen on the channel.""")] whose stats URL is seen on the channel.""")]
@ -87,6 +87,14 @@ class Gameknot(callbacks.PrivmsgCommandAndRegexp, plugins.Configurable):
'"#FFFF00">(\d+)') '"#FFFF00">(\d+)')
_gkteam = re.compile(r'Team:(<.*?>)+(?P<name>.*?)</span>') _gkteam = re.compile(r'Team:(<.*?>)+(?P<name>.*?)</span>')
_gkseen = re.compile(r'(seen on GK:\s+([^[]+ago)|.*?is hiding.*?)') _gkseen = re.compile(r'(seen on GK:\s+([^[]+ago)|.*?is hiding.*?)')
def __init__(self):
plugins.Configurable.__init__(self)
callbacks.PrivmsgCommandAndRegexp.__init__(self)
def die(self):
plugins.Configurable.die(self)
callbacks.PrivmsgCommandAndRegexp.die(self)
def getStats(self, name): def getStats(self, name):
gkprofile = 'http://www.gameknot.com/stats.pl?%s' % name gkprofile = 'http://www.gameknot.com/stats.pl?%s' % name
try: try:

View File

@ -123,21 +123,26 @@ class Google(callbacks.PrivmsgCommandAndRegexp, plugins.Configurable):
threaded = True threaded = True
regexps = sets.Set(['googleSnarfer', 'googleGroups']) regexps = sets.Set(['googleSnarfer', 'googleGroups'])
configurables = plugins.ConfigurableDictionary( configurables = plugins.ConfigurableDictionary(
[('groups-snarfer', plugins.ConfigurableTypes.bool, True, [('groups-snarfer', plugins.ConfigurableBoolType, True,
"""Determines whether the groups snarfer is enabled. If so, URLs at """Determines whether the groups snarfer is enabled. If so, URLs at
groups.google.com will be snarfed and their group/title messaged to groups.google.com will be snarfed and their group/title messaged to
the channel."""), the channel."""),
('search-snarfer', plugins.ConfigurableTypes.bool, False, ('search-snarfer', plugins.ConfigurableBoolType, False,
"""Determines whether the search snarfer is enabled. If so, messages """Determines whether the search snarfer is enabled. If so, messages
(even unaddressed ones) beginning with the word 'google' will result (even unaddressed ones) beginning with the word 'google' will result
in the first URL Google returns being sent to the channel.""")] in the first URL Google returns being sent to the channel.""")]
) )
def __init__(self): def __init__(self):
super(Google, self).__init__() plugins.Configurable.__init__(self)
callbacks.PrivmsgCommandAndRegexp.__init__(self)
self.total = 0 self.total = 0
self.totalTime = 0 self.totalTime = 0
self.last24hours = structures.queue() self.last24hours = structures.queue()
def die(self):
plugins.Configurable.die(self)
callbacks.PrivmsgCommandAndRegexp.die(self)
def formatData(self, data): def formatData(self, data):
if isinstance(data, basestring): if isinstance(data, basestring):
return data return data

View File

@ -75,12 +75,20 @@ class Python(callbacks.PrivmsgCommandAndRegexp, plugins.Configurable):
threaded = True threaded = True
regexps = ['aspnRecipes'] regexps = ['aspnRecipes']
configurables = plugins.ConfigurableDictionary( configurables = plugins.ConfigurableDictionary(
[('aspn-snarfer', plugins.ConfigurableTypes.bool, True, [('aspn-snarfer', plugins.ConfigurableBoolType, True,
"""Determines whether the ASPN Python recipe snarfer is enabled. If """Determines whether the ASPN Python recipe snarfer is enabled. If
so, it will message the channel with the name of the recipe when it so, it will message the channel with the name of the recipe when it
sees an ASPN Python recipe link on the channel.""")] sees an ASPN Python recipe link on the channel.""")]
) )
def __init__(self):
plugins.Configurable.__init__(self)
callbacks.PrivmsgCommandAndRegexp.__init__(self)
def die(self):
plugins.Configurable.die(self)
callbacks.PrivmsgCommandAndRegexp.die(self)
def pydoc(self, irc, msg, args): def pydoc(self, irc, msg, args):
"""<python function> """<python function>

View File

@ -66,7 +66,7 @@ class QuoteGrabs(plugins.ChannelDBHandler,
plugins.Configurable, plugins.Configurable,
callbacks.Privmsg): callbacks.Privmsg):
configurables = plugins.ConfigurableDictionary( configurables = plugins.ConfigurableDictionary(
[('random-grabber', plugins.ConfigurableTypes.bool, False, [('random-grabber', plugins.ConfigurableBoolType, False,
"""Determines whether the bot will randomly grab possibly-suitable """Determines whether the bot will randomly grab possibly-suitable
quotes for someone."""),] quotes for someone."""),]
) )

View File

@ -95,12 +95,13 @@ class Relay(callbacks.Privmsg, plugins.Configurable):
noIgnore = True noIgnore = True
priority = sys.maxint priority = sys.maxint
configurables = plugins.ConfigurableDictionary( configurables = plugins.ConfigurableDictionary(
[('color', plugins.ConfigurableTypes.bool, True, [('color', plugins.ConfigurableBoolType, True,
"""Determines whether the bot will color relayed PRIVMSGs so as to """Determines whether the bot will color relayed PRIVMSGs so as to
make the messages easier to read."""),] make the messages easier to read."""),]
) )
def __init__(self): def __init__(self):
callbacks.Privmsg.__init__(self) callbacks.Privmsg.__init__(self)
plugins.Configurable.__init__(self)
self.ircs = {} self.ircs = {}
self._color = 0 self._color = 0
self._whois = {} self._whois = {}
@ -122,6 +123,8 @@ class Relay(callbacks.Privmsg, plugins.Configurable):
callbacks.Privmsg.__call__(self, irc, msg) callbacks.Privmsg.__call__(self, irc, msg)
def die(self): def die(self):
callbacks.Privmsg.die(self)
plugins.Configurable.die(self)
for irc in self.abbreviations: for irc in self.abbreviations:
if irc != self.originalIrc: if irc != self.originalIrc:
irc.callbacks[:] = [] irc.callbacks[:] = []

View File

@ -98,14 +98,21 @@ class Sourceforge(callbacks.PrivmsgCommandAndRegexp, plugins.Configurable):
_res =(_resolution, _assigned, _submitted, _priority, _status) _res =(_resolution, _assigned, _submitted, _priority, _status)
configurables = plugins.ConfigurableDictionary( configurables = plugins.ConfigurableDictionary(
[('tracker-snarfer', plugins.ConfigurableTypes.bool, True, [('tracker-snarfer', plugins.ConfigurableBoolType, True,
"""Determines whether the bot will reply to SF.net Tracker URLs in """Determines whether the bot will reply to SF.net Tracker URLs in
the channel with a nice summary of the tracker item."""), the channel with a nice summary of the tracker item."""),
('default-project', plugins.ConfigurableTypes.str, '', ('default-project', plugins.ConfigurableStrType, '',
"""Sets the default project (used by the bugs/rfes commands in the """Sets the default project (used by the bugs/rfes commands in the
case that no explicit project is given).""")] case that no explicit project is given).""")]
) )
_projectURL = 'http://sourceforge.net/projects/' _projectURL = 'http://sourceforge.net/projects/'
def __init__(self):
plugins.Configurable.__init__(self)
callbacks.PrivmsgCommandAndRegexp.__init__(self)
def die(self):
plugins.Configurable.die(self)
callbacks.PrivmsgCommandAndRegexp.die(self)
def _formatResp(self, text, num=''): def _formatResp(self, text, num=''):
""" """

View File

@ -64,18 +64,24 @@ def configure(onStart, afterConnect, advanced):
class URL(callbacks.Privmsg, plugins.Configurable, plugins.ChannelDBHandler): class URL(callbacks.Privmsg, plugins.Configurable, plugins.ChannelDBHandler):
configurables = plugins.ConfigurableDictionary( configurables = plugins.ConfigurableDictionary(
[('tinyurl-snarfer', plugins.ConfigurableTypes.bool, True, [('tinyurl-snarfer', plugins.ConfigurableBoolType, True,
"""Determines whether the bot will output shorter versions of URLs """Determines whether the bot will output shorter versions of URLs
longer than the tinyurl-minimum-length config variable."""), longer than the tinyurl-minimum-length config variable."""),
('tinyurl-minimum-length', plugins.ConfigurableTypes.int, 46, ('tinyurl-minimum-length', plugins.ConfigurableIntType, 46,
"""The minimum length a URL must be before the tinyurl-snarfer will """The minimum length a URL must be before the tinyurl-snarfer will
snarf it and offer a tinyurl replacement."""),] snarf it and offer a tinyurl replacement."""),]
) )
def __init__(self): def __init__(self):
self.nextMsgs = {} self.nextMsgs = {}
callbacks.Privmsg.__init__(self) callbacks.Privmsg.__init__(self)
plugins.Configurable.__init__(self)
plugins.ChannelDBHandler.__init__(self) plugins.ChannelDBHandler.__init__(self)
def die(self):
callbacks.Privmsg.die(self)
plugins.Configurable.die(self)
plugins.ChannelDBHandler.die(self)
def makeDb(self, filename): def makeDb(self, filename):
if os.path.exists(filename): if os.path.exists(filename):
return sqlite.connect(filename) return sqlite.connect(filename)

View File

@ -41,6 +41,7 @@ import types
import random import random
import urllib2 import urllib2
import threading import threading
import cPickle as pickle
import fix import fix
import cdb import cdb
@ -53,11 +54,6 @@ import ircutils
import privmsgs import privmsgs
import callbacks import callbacks
__all__ = ['ChannelDBHandler',
'PeriodicFileDownloader',
'ConfigurableDictionary',
'Configurable']
class ChannelDBHandler(object): class ChannelDBHandler(object):
"""A class to handle database stuff for individual channels transparently. """A class to handle database stuff for individual channels transparently.
""" """
@ -244,41 +240,35 @@ class ConfigurableDictionary(object):
def names(self): def names(self):
return self.originalNames return self.originalNames
# XXX: Make persistent.
class ConfigurableTypeError(TypeError): class ConfigurableTypeError(TypeError):
pass pass
class _ConfigurableTypes(object): def ConfigurableBoolType(s):
def bool(self, s): s = s.lower()
s = s.lower() if s in ('true', 'enable', 'on'):
if s in ('true', 'enable', 'on'): return True
return True elif s in ('false', 'disable', 'off'):
elif s in ('false', 'disable', 'off'): return False
return False else:
else: s = 'Value must be one of on/off/true/false/enable/disable.'
s = 'Value must be one of on/off/true/false/enable/disable.' raise ConfigurableTypeError, s
raise ConfigurableTypeError, s
def str(self, s): def ConfigurableStrType(s):
if s and s[0] not in '\'"' and s[-1] not in '\'"': if s and s[0] not in '\'"' and s[-1] not in '\'"':
s = repr(s) s = repr(s)
try: try:
v = utils.safeEval(s) v = utils.safeEval(s)
if type(v) is not str: if type(v) is not str:
raise ValueError raise ValueError
except ValueError: except ValueError:
raise ConfigurableTypeError, 'Value must be a string.' raise ConfigurableTypeError, 'Value must be a string.'
return v return v
def int(self, s): def ConfigurableIntType(s):
try: try:
return int(s) return int(s)
except ValueError: except ValueError:
raise ConfigurableTypeError, 'Value must be an int.' raise ConfigurableTypeError, 'Value must be an int.'
ConfigurableTypes = _ConfigurableTypes()
foobar = 'baz'
class Configurable(object): class Configurable(object):
"""A mixin class to provide a "config" command that can be consistent """A mixin class to provide a "config" command that can be consistent
@ -292,6 +282,21 @@ class Configurable(object):
variable should take on; help is a string that'll be returned to describe variable should take on; help is a string that'll be returned to describe
the purpose of the config variable. the purpose of the config variable.
""" """
def __init__(self):
className = self.__class__.__name__
self.filename = os.path.join(conf.confDir,'%s-configurable'%className)
try:
if os.path.exists(self.filename):
configurables = pickle.load(file(self.filename, 'rb'))
if configurables.names() == self.configurables.names():
self.configurables = configurables
except Exception, e:
debug.msg('%s raised when trying to unpickle %s configurables' %
(debug.exnToString(e), className))
def die(self):
pickle.dump(self.configurables, file(self.filename, 'wb'), -1)
def config(self, irc, msg, args): def config(self, irc, msg, args):
"""[<channel>] [<name>] [<value>] """[<channel>] [<name>] [<value>]
@ -318,7 +323,7 @@ class Configurable(object):
return return
if not value: if not value:
help = self.configurables.help(name) help = self.configurables.help(name)
value = self.configurables.get(name) value = self.configurables.get(name, channel=channel)
irc.reply(msg, '%s: %s (Current value: %r)' % (name, help, value)) irc.reply(msg, '%s: %s (Current value: %r)' % (name, help, value))
return return
try: try: