Added userdata registry.

This commit is contained in:
Jeremy Fincher 2004-04-30 18:24:35 +00:00
parent ab1b7d3434
commit e3b690f933
10 changed files with 169 additions and 102 deletions

View File

@ -391,22 +391,30 @@ class Factoids(plugins.ChannelDBHandler, callbacks.Privmsg):
_sqlTrans = string.maketrans('*?', '%_')
def search(self, irc, msg, args):
"""[<channel>] [--{regexp}=<value>] [<glob>]
"""[<channel>] [--values] [--{regexp}=<value>] [<glob>]
Searches the keyspace for keys matching <glob>. If --regexp is given,
it associated value is taken as a regexp and matched against the keys.
If --values is given, search the value space instead of the keyspace.
"""
channel = privmsgs.getChannel(msg, args)
(optlist, rest) = getopt.getopt(args, '', ['regexp='])
(optlist, rest) = getopt.getopt(args, '', ['values', 'regexp='])
if not optlist and not rest:
raise callbacks.ArgumentError
criteria = []
tables = ['keys']
formats = []
criteria = []
target = 'keys.key'
predicateName = 'p'
db = self.getDb(channel)
for (option, arg) in optlist:
if option == '--regexp':
criteria.append('%s(key)' % predicateName)
if option == '--values':
target = 'factoids.fact'
if 'factoids' not in tables:
tables.append('factoids')
criteria.append('factoids.key_id=keys.id')
elif option == '--regexp':
criteria.append('%s(TARGET)' % predicateName)
try:
r = utils.perlReToPythonRe(arg)
except ValueError, e:
@ -419,10 +427,12 @@ class Factoids(plugins.ChannelDBHandler, callbacks.Privmsg):
for glob in rest:
if '*' not in glob and '?' not in glob:
glob = '*%s*' % glob
criteria.append('key LIKE %s')
criteria.append('TARGET LIKE %s')
formats.append(glob.translate(self._sqlTrans))
cursor = db.cursor()
sql = """SELECT key FROM keys WHERE %s""" % ' AND '.join(criteria)
sql = """SELECT keys.key FROM %s WHERE %s""" % \
(tables, ' AND '.join(criteria))
sql = sql.replace('TARGET', target)
cursor.execute(sql, formats)
if cursor.rowcount == 0:
irc.reply('No keys matched that query.')

View File

@ -130,15 +130,6 @@ class Infobot(callbacks.PrivmsgRegexp):
cursor.execute('DELETE FROM are_factoids WHERE key=%s', key)
irc.reply(self.getRandomSaying('confirms'))
def tell(self, irc, msg, match):
r"^tell\s+(.+?)\s+about\s+(.+?)(?!\?+)[.! ]*$"
(nick, key) = match.groups()
try:
s = '%s wants you to know that %s' %(msg.nick,self.getFactoid(key))
irc.reply(nick, s)
except KeyError:
irc.reply('I don\'t know anything about %s' % key)
def factoid(self, irc, msg, match):
r"^(no[ :,-]+)?(.+?)\s+(was|is|am|were|are)\s+(also\s+)?(.+?)(?!\?+)$"
(correction, key, isAre, addition, value) = match.groups()

View File

@ -58,17 +58,17 @@ conf.registerGlobalValue(conf.supybot.plugins.Infobot, 'infobotStyleStatus',
original Infobot style message or with a short message."""))
# FIXME: rename; description
conf.registerChannelValue(conf.supybot.plugins.Infobot,
'catchWhenNotAddressed', registry.Boolean(True,
"""Whether to catch non-addressed stuff at all."""))
'catchWhenNotAddressed', registry.Boolean(True, """Whether to catch
non-addressed stuff at all."""))
conf.registerChannelValue(conf.supybot.plugins.Infobot,
'replyQuestionWhenNotAddressed', registry.Boolean(True,
"""Whether to answer to non-addressed stuff we know about."""))
'replyQuestionWhenNotAddressed', registry.Boolean(True, """Whether to
answer to non-addressed stuff we know about."""))
conf.registerChannelValue(conf.supybot.plugins.Infobot,
'replyDontKnowWhenNotAddressed', registry.Boolean(False,
"""Whether to answer to non-addressed stuff we don't know about."""))
'replyDontKnowWhenNotAddressed', registry.Boolean(False, """Whether to
answer to non-addressed stuff we don't know about."""))
conf.registerChannelValue(conf.supybot.plugins.Infobot,
'replyWhenNotAddressed', registry.Boolean(False,
"""Whether to answer to non-addressed, non-question stuff."""))
'replyWhenNotAddressed', registry.Boolean(False, """Whether to answer to
non-addressed, non-question stuff."""))
# Replies
# XXX: Move this to a plaintext db of some sort?

View File

@ -353,9 +353,7 @@ class Misc(callbacks.Privmsg):
return
module = sys.modules[cb.__class__.__module__]
if hasattr(module, '__author__') and module.__author__:
s = module.__author__.replace('@', ' AT ')
s = s.replace('.', ' DOT ')
irc.reply(s)
irc.reply(utils.mungeEmailForWeb(module.__author__))
else:
irc.reply('That plugin doesn\'t have an author that claims it.')
@ -448,7 +446,7 @@ class Misc(callbacks.Privmsg):
irc.reply(ircmsgs.prettyPrint(m))
return
irc.error('I couldn\'t find a message matching that criteria in '
'my history of %s messages.' % len(irc.state.history))
'my history of %s messages.' % len(irc.state.history))
def seconds(self, irc, msg, args):
"""[<years>y] [<weeks>w] [<days>d] [<hours>h] [<minutes>m] [<seconds>s]

View File

@ -103,7 +103,6 @@ def loadPluginClass(irc, module, register=None):
"""Loads the plugin Class from the given module into the given irc."""
cb = module.Class()
name = cb.name()
conf.registerPlugin(name)
public = True
if hasattr(cb, 'public'):
public = cb.public
@ -194,6 +193,8 @@ class Owner(privmsgs.CapabilityCheckingPrivmsg):
(_, _, name) = name.split('.')
except ValueError: # unpack list of wrong size.
continue
if name == name.lower(): # This can't be right.
name = name.capitalize() # Let's at least capitalize it.
conf.registerPlugin(name)
if name.startswith('supybot.commands.defaultPlugins'):
try:
@ -478,7 +479,8 @@ class Owner(privmsgs.CapabilityCheckingPrivmsg):
else:
irc.error(str(e))
return
loadPluginClass(irc, module)
cb = loadPluginClass(irc, module)
name = cb.name() # Let's normalize this.
conf.registerPlugin(name, True)
irc.replySuccess()

View File

@ -811,6 +811,28 @@ class Privmsg(irclib.IrcCallback):
else:
return group.get(channel)()
def userValue(self, name, prefixOrName, default=None):
try:
id = str(ircdb.users.getUserId(prefixOrName))
except KeyError:
return None
plugin = self.name()
group = conf.users.plugins.get(plugin)
names = name.split('.')
for name in names:
group = group.get(name)
return group.get(id)()
def setUserValue(self, prefixOrName, name, value):
id = str(ircdb.users.getUserId(prefixOrName))
plugin = self.name()
group = conf.users.plugins.get(plugin)
names = name.split('.')
for name in names:
group = group.get(name)
group = group.get(id)
group.set(value)
class IrcObjectProxyRegexp(RichReplyMethods):
def __init__(self, irc, msg):

View File

@ -65,24 +65,42 @@ daemonized = False
###
allowEval = False
###
# The standard registry.
###
supybot = registry.Group()
supybot.setName('supybot')
def registerPlugin(name, currentValue=None):
supybot.plugins.register(name, registry.Boolean(False, """Determines
whether this plugin is loaded by default.""", showDefault=False))
if currentValue is not None:
supybot.plugins.get(name).setValue(currentValue)
def registerGroup(Group, name, group=None):
Group.register(name, group)
def registerGlobalValue(group, name, value):
group.register(name, value)
def registerChannelValue(group, name, value):
value.supplyDefault = True
group.register(name, value)
def registerGlobalValue(group, name, value):
def registerPlugin(name, currentValue=None):
registerGlobalValue(supybot.plugins, name,
registry.Boolean(False, """Determines whether this plugin is loaded by
default.""", showDefault=False))
if currentValue is not None:
supybot.plugins.get(name).setValue(currentValue)
registerGroup(users.plugins, name)
###
# The user info registry.
###
users = registry.Group()
users.setName('users')
registerGroup(users, 'plugins')
def registerUserValue(group, name, value):
assert group.name.startswith('users.')
value.supplyDefault = True
group.register(name, value)
def registerGroup(Group, name, group=None):
Group.register(name, group)
class ValidNick(registry.String):
"""Value must be a valid IRC nick."""
@ -410,37 +428,37 @@ required for changes to this variable to take effect."""))
###
registerGroup(supybot, 'drivers')
registerGlobalValue(supybot.drivers, 'poll',
registry.Float(1.0, """Determines the default length of time a driver should
block waiting for input."""))
registry.Float(1.0, """Determines the default length of time a driver
should block waiting for input."""))
class ValidDriverModule(registry.OnlySomeStrings):
validStrings = ('default', 'socketDrivers',
'twistedDrivers', 'asyncoreDrivers')
registerGlobalValue(supybot.drivers, 'module',
ValidDriverModule('default', """Determines what driver module the bot will
use. socketDrivers, a simple driver based on timeout sockets, is used by
default because it's simple and stable. asyncoreDrivers is a bit older (and
less well-maintained) but allows you to integrate with asyncore-based
applications. twistedDrivers is very stable and simple, and if you've got
Twisted installed, is probably your best bet."""))
ValidDriverModule('default', """Determines what driver module the bot will
use. socketDrivers, a simple driver based on timeout sockets, is used by
default because it's simple and stable. asyncoreDrivers is a bit older
(and less well-maintained) but allows you to integrate with asyncore-based
applications. twistedDrivers is very stable and simple, and if you've got
Twisted installed, is probably your best bet."""))
###
# supybot.directories, for stuff relating to directories.
###
registerGroup(supybot, 'directories')
registerGlobalValue(supybot.directories, 'conf',
registry.String('conf', """Determines what directory configuration data is
put into."""))
registry.String('conf', """Determines what directory configuration data is
put into."""))
registerGlobalValue(supybot.directories, 'data',
registry.String('data', """Determines what directory data is put into."""))
registry.String('data', """Determines what directory data is put into."""))
registerGlobalValue(supybot.directories, 'plugins',
registry.CommaSeparatedListOfStrings([_srcDir,_pluginsDir], """Determines
what directories the bot will look for plugins in. Accepts a
comma-separated list of strings. This means that to add another directory,
you can nest the former value and add a new one. E.g. you can say: bot:
'config supybot.directories.plugins [config supybot.directories.plugins],
newPluginDirectory'."""))
registry.CommaSeparatedListOfStrings([_srcDir,_pluginsDir], """Determines
what directories the bot will look for plugins in. Accepts a
comma-separated list of strings. This means that to add another directory,
you can nest the former value and add a new one. E.g. you can say: bot:
'config supybot.directories.plugins [config supybot.directories.plugins],
newPluginDirectory'."""))
supybot.register('plugins') # This will be used by plugins, but not here.
@ -450,28 +468,28 @@ supybot.register('plugins') # This will be used by plugins, but not here.
registerGroup(supybot, 'databases')
registerGroup(supybot.databases, 'users')
registerGlobalValue(supybot.databases.users, 'filename',
registry.String('users.conf', """Determines what filename will be used for
the users database. This file will go into the directory specified by the
supybot.directories.conf variable."""))
registry.String('users.conf', """Determines what filename will be used for
the users database. This file will go into the directory specified by the
supybot.directories.conf variable."""))
registerGlobalValue(supybot.databases.users, 'timeoutIdentification',
registry.Integer(0, """Determines how long it takes identification to time
out. If the value is less than or equal to zero, identification never times
out."""))
registry.Integer(0, """Determines how long it takes identification to time
out. If the value is less than or equal to zero, identification never
times out."""))
registerGlobalValue(supybot.databases.users, 'hash',
registry.Boolean(True, """Determines whether the passwords in the user
database will be hashed by default."""))
registry.Boolean(True, """Determines whether the passwords in the user
database will be hashed by default."""))
registerGroup(supybot.databases, 'ignores')
registerGlobalValue(supybot.databases.ignores, 'filename',
registry.String('ignores.conf', """Determines what filename will be used for
the ignores database. This file will go into the directory specified by the
supybot.directories.conf variable."""))
registry.String('ignores.conf', """Determines what filename will be used
for the ignores database. This file will go into the directory specified
by the supybot.directories.conf variable."""))
registerGroup(supybot.databases, 'channels')
registerGlobalValue(supybot.databases.channels, 'filename',
registry.String('channels.conf', """Determines what filename will be used
for the channels database. This file will go into the directory specified
by the supybot.directories.conf variable."""))
registry.String('channels.conf', """Determines what filename will be used
for the channels database. This file will go into the directory specified
by the supybot.directories.conf variable."""))
###
# Protocol information.
@ -494,37 +512,38 @@ class StrictRfc(registry.Boolean):
registerGroup(supybot, 'protocols')
registerGroup(supybot.protocols, 'irc')
registerGlobalValue(supybot.protocols.irc, 'strictRfc',
StrictRfc(False, """Determines whether the bot will strictly follow the RFC;
currently this only affects what strings are considered to be nicks. If
you're using a server or a network that requires you to message a nick such
as services@this.network.server then you you should set this to False."""))
StrictRfc(False, """Determines whether the bot will strictly follow the
RFC; currently this only affects what strings are considered to be nicks.
If you're using a server or a network that requires you to message a nick
such as services@this.network.server then you you should set this to
False."""))
registerGlobalValue(supybot.protocols.irc, 'maxHistoryLength',
registry.Integer(1000, """Determines how many old messages the bot will keep
around in its history. Changing this variable will not take effect until
the bot is restarted."""))
registry.Integer(1000, """Determines how many old messages the bot will
keep around in its history. Changing this variable will not take effect
until the bot is restarted."""))
registerGlobalValue(supybot.protocols.irc, 'throttleTime',
registry.Float(1.0, """A floating point number of seconds to throttle queued
messages -- that is, messages will not be sent faster than once per
throttleTime seconds."""))
registry.Float(1.0, """A floating point number of seconds to throttle
queued messages -- that is, messages will not be sent faster than once per
throttleTime seconds."""))
registerGlobalValue(supybot.protocols.irc, 'ping',
registry.Boolean(True, """Determines whether the bot will send PINGs to the
server it's connected to in order to keep the connection alive and discover
earlier when it breaks. Really, this option only exists for debugging
purposes: you always should make it True unless you're testing some strange
server issues."""))
registry.Boolean(True, """Determines whether the bot will send PINGs to the
server it's connected to in order to keep the connection alive and discover
earlier when it breaks. Really, this option only exists for debugging
purposes: you always should make it True unless you're testing some strange
server issues."""))
registerGlobalValue(supybot.protocols.irc.ping, 'interval',
registry.Integer(120, """Determines the number of seconds between sending
pings to the server, if pings are being sent to the server."""))
registry.Integer(120, """Determines the number of seconds between sending
pings to the server, if pings are being sent to the server."""))
registerGroup(supybot.protocols, 'http')
registerGlobalValue(supybot.protocols.http, 'peekSize',
registry.PositiveInteger(4096, """Determines how many bytes the bot will
'peek' at when looking through a URL for a doctype or title or something
similar. It'll give up after it reads this many bytes, even if it hasn't
found what it was looking for."""))
registry.PositiveInteger(4096, """Determines how many bytes the bot will
'peek' at when looking through a URL for a doctype or title or something
similar. It'll give up after it reads this many bytes, even if it hasn't
found what it was looking for."""))
###
@ -532,12 +551,20 @@ registerGlobalValue(supybot.protocols.http, 'peekSize',
###
registerGroup(supybot, 'debug')
registerGlobalValue(supybot.debug, 'threadAllCommands',
registry.Boolean(False, """Determines whether the bot will automatically
thread all commands."""))
registry.Boolean(False, """Determines whether the bot will automatically
thread all commands."""))
registerGlobalValue(supybot.debug, 'flushVeryOften',
registry.Boolean(False, """Determines whether the bot will automatically
flush all flushers *very* often. Useful for debugging when you don't know
what's breaking or when, but think that it might be logged."""))
registry.Boolean(False, """Determines whether the bot will automatically
flush all flushers *very* often. Useful for debugging when you don't know
what's breaking or when, but think that it might be logged."""))
# Let's open this now since we've got our directories setup.
userdataFilename = os.path.join(supybot.directories.conf(), 'userdata.conf')
if not os.path.exists(userdataFilename):
fd = file(userdataFilename, 'w')
fd.write('\n')
fd.close()
registry.open(userdataFilename)
# vim:set shiftwidth=4 tabstop=8 expandtab textwidth=78:

View File

@ -58,11 +58,13 @@ class NonExistentRegistryEntry(RegistryException):
_cache = utils.InsensitivePreservingDict()
_lastModified = 0
def open(filename):
def open(filename, clear=False):
"""Initializes the module by loading the registry file into memory."""
global _lastModified
_cache.clear()
fd = utils.nonCommentNonEmptyLines(file(filename))
if clear:
_cache.clear()
_fd = file(filename)
fd = utils.nonCommentNonEmptyLines(_fd)
for (i, line) in enumerate(fd):
line = line.rstrip('\r\n')
try:
@ -71,6 +73,7 @@ def open(filename):
raise InvalidRegistryFile, 'Error unpacking line #%s' % (i+1)
_cache[key] = value
_lastModified = time.time()
_fd.close()
def close(registry, filename, annotated=True, helpOnceOnly=False):
first = True
@ -206,7 +209,7 @@ class Group(object):
class Value(Group):
"""Invalid registry value. If you're getting this message, report it,
because someone forgot to put a proper help string here."""
because we forgot to put a proper help string here."""
def __init__(self, default, help,
private=False, showDefault=True, **kwargs):
Group.__init__(self, **kwargs)
@ -217,7 +220,12 @@ class Value(Group):
self.setValue(default)
def error(self):
raise InvalidRegistryValue, utils.normalizeWhitespace(self.__doc__)
if self.__doc__:
s = self.__doc__
else:
s = """Invalid registry value. If you're getting this message,
report it, because we forgot to put a proper help string here."""
raise InvalidRegistryValue, utils.normalizeWhitespace(s)
def setName(self, *args):
if self.name == 'unset':
@ -396,7 +404,7 @@ class Regexp(Value):
Value.setValue(self, None)
else:
raise InvalidRegistryValue, \
'Can\'t set to a regexp, there would be an inconsistency ' \
'Can\'t setValue a regexp, there would be an inconsistency '\
'between the regexp and the recorded string value.'
def __str__(self):

View File

@ -628,6 +628,12 @@ class InsensitivePreservingDict(UserDict.DictMixin, object):
def __reduce__(self):
return (self.__class__, (dict(self.data.values()),))
def mungeEmailForWeb(s):
s = s.replace('@', ' AT ')
s = s.replace('.', ' DOT ')
return s
if __name__ == '__main__':
import sys, doctest
doctest.testmod(sys.modules['__main__'])

View File

@ -65,7 +65,10 @@ commandsProcessed = 0
ircs = [] # A list of all the IRCs.
flushers = [] # A periodic function will flush all these.
def _flushUserData():
registry.close(conf.users, conf.userdataFilename, annotated=False)
flushers = [_flushUserData] # A periodic function will flush all these.
registryFilename = None