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
regexps = ['bzSnarfer']
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
Bugzilla URLs seen in the channel will have their information reported
into the channel.""")]
)
def __init__(self):
plugins.Configurable.__init__(self)
callbacks.PrivmsgCommandAndRegexp.__init__(self)
self.entre = re.compile('&(\S*?);')
# Schema: {name, [url, description]}
@ -100,6 +101,7 @@ class Bugzilla(callbacks.PrivmsgCommandAndRegexp, plugins.Configurable):
self.shorthand = utils.abbrev(self.db.keys())
def die(self):
plugins.Configurable.die(self)
self.db.close()
del self.db

View File

@ -59,23 +59,29 @@ frowns = (':|', ':-/', ':-\\', ':\\', ':/', ':(', ':-(', ':\'(')
smileyre = re.compile('|'.join(map(re.escape, smileys)))
frownre = re.compile('|'.join(map(re.escape, frowns)))
class ChannelDB(plugins.ChannelDBHandler, # Must be first (die).
class ChannelDB(plugins.ChannelDBHandler,
plugins.Configurable,
callbacks.Privmsg):
noIgnore = True
configurables = plugins.ConfigurableDictionary(
[('self-stats', plugins.ConfigurableTypes.bool, True,
[('self-stats', plugins.ConfigurableBoolType, 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.""")]
)
def __init__(self):
callbacks.Privmsg.__init__(self)
plugins.Configurable.__init__(self)
plugins.ChannelDBHandler.__init__(self)
self.lastmsg = None
self.laststate = None
self.outFiltering = False
def die(self):
callbacks.Privmsg.die(self)
plugins.Configurable.die(self)
plugins.ChannelDBHandler.die(self)
def makeDb(self, filename):
if os.path.exists(filename):
db = sqlite.connect(filename)

View File

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

View File

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

View File

@ -72,11 +72,11 @@ class Gameknot(callbacks.PrivmsgCommandAndRegexp, plugins.Configurable):
threaded = True
regexps = ['gameknotSnarfer', 'gameknotStatsSnarfer']
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
will reply to the channel with a summary of the game data when it
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
will reply to the channel with a summary of the stats of any player
whose stats URL is seen on the channel.""")]
@ -87,6 +87,14 @@ class Gameknot(callbacks.PrivmsgCommandAndRegexp, plugins.Configurable):
'"#FFFF00">(\d+)')
_gkteam = re.compile(r'Team:(<.*?>)+(?P<name>.*?)</span>')
_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):
gkprofile = 'http://www.gameknot.com/stats.pl?%s' % name
try:

View File

@ -123,21 +123,26 @@ class Google(callbacks.PrivmsgCommandAndRegexp, plugins.Configurable):
threaded = True
regexps = sets.Set(['googleSnarfer', 'googleGroups'])
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
groups.google.com will be snarfed and their group/title messaged to
the channel."""),
('search-snarfer', plugins.ConfigurableTypes.bool, False,
('search-snarfer', plugins.ConfigurableBoolType, 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):
super(Google, self).__init__()
plugins.Configurable.__init__(self)
callbacks.PrivmsgCommandAndRegexp.__init__(self)
self.total = 0
self.totalTime = 0
self.last24hours = structures.queue()
def die(self):
plugins.Configurable.die(self)
callbacks.PrivmsgCommandAndRegexp.die(self)
def formatData(self, data):
if isinstance(data, basestring):
return data

View File

@ -75,12 +75,20 @@ class Python(callbacks.PrivmsgCommandAndRegexp, plugins.Configurable):
threaded = True
regexps = ['aspnRecipes']
configurables = plugins.ConfigurableDictionary(
[('aspn-snarfer', plugins.ConfigurableTypes.bool, True,
[('aspn-snarfer', plugins.ConfigurableBoolType, True,
"""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)
callbacks.PrivmsgCommandAndRegexp.__init__(self)
def die(self):
plugins.Configurable.die(self)
callbacks.PrivmsgCommandAndRegexp.die(self)
def pydoc(self, irc, msg, args):
"""<python function>

View File

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

View File

@ -95,12 +95,13 @@ class Relay(callbacks.Privmsg, plugins.Configurable):
noIgnore = True
priority = sys.maxint
configurables = plugins.ConfigurableDictionary(
[('color', plugins.ConfigurableTypes.bool, True,
[('color', plugins.ConfigurableBoolType, 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)
self.ircs = {}
self._color = 0
self._whois = {}
@ -122,6 +123,8 @@ class Relay(callbacks.Privmsg, plugins.Configurable):
callbacks.Privmsg.__call__(self, irc, msg)
def die(self):
callbacks.Privmsg.die(self)
plugins.Configurable.die(self)
for irc in self.abbreviations:
if irc != self.originalIrc:
irc.callbacks[:] = []

View File

@ -98,14 +98,21 @@ class Sourceforge(callbacks.PrivmsgCommandAndRegexp, plugins.Configurable):
_res =(_resolution, _assigned, _submitted, _priority, _status)
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
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
case that no explicit project is given).""")]
)
_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=''):
"""

View File

@ -64,18 +64,24 @@ def configure(onStart, afterConnect, advanced):
class URL(callbacks.Privmsg, plugins.Configurable, plugins.ChannelDBHandler):
configurables = plugins.ConfigurableDictionary(
[('tinyurl-snarfer', plugins.ConfigurableTypes.bool, True,
[('tinyurl-snarfer', plugins.ConfigurableBoolType, True,
"""Determines whether the bot will output shorter versions of URLs
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
snarf it and offer a tinyurl replacement."""),]
)
def __init__(self):
self.nextMsgs = {}
callbacks.Privmsg.__init__(self)
plugins.Configurable.__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):
if os.path.exists(filename):
return sqlite.connect(filename)

View File

@ -41,6 +41,7 @@ import types
import random
import urllib2
import threading
import cPickle as pickle
import fix
import cdb
@ -53,11 +54,6 @@ import ircutils
import privmsgs
import callbacks
__all__ = ['ChannelDBHandler',
'PeriodicFileDownloader',
'ConfigurableDictionary',
'Configurable']
class ChannelDBHandler(object):
"""A class to handle database stuff for individual channels transparently.
"""
@ -244,41 +240,35 @@ class ConfigurableDictionary(object):
def names(self):
return self.originalNames
# XXX: Make persistent.
class ConfigurableTypeError(TypeError):
pass
class _ConfigurableTypes(object):
def bool(self, 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 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 str(self, 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 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 int(self, s):
try:
return int(s)
except ValueError:
raise ConfigurableTypeError, 'Value must be an int.'
ConfigurableTypes = _ConfigurableTypes()
foobar = 'baz'
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
@ -292,6 +282,21 @@ class Configurable(object):
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)
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):
"""[<channel>] [<name>] [<value>]
@ -318,7 +323,7 @@ class Configurable(object):
return
if not value:
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))
return
try: