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('*?', '%_') _sqlTrans = string.maketrans('*?', '%_')
def search(self, irc, msg, args): 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, Searches the keyspace for keys matching <glob>. If --regexp is given,
it associated value is taken as a regexp and matched against the keys. 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) channel = privmsgs.getChannel(msg, args)
(optlist, rest) = getopt.getopt(args, '', ['regexp=']) (optlist, rest) = getopt.getopt(args, '', ['values', 'regexp='])
if not optlist and not rest: if not optlist and not rest:
raise callbacks.ArgumentError raise callbacks.ArgumentError
criteria = [] tables = ['keys']
formats = [] formats = []
criteria = []
target = 'keys.key'
predicateName = 'p' predicateName = 'p'
db = self.getDb(channel) db = self.getDb(channel)
for (option, arg) in optlist: for (option, arg) in optlist:
if option == '--regexp': if option == '--values':
criteria.append('%s(key)' % predicateName) 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: try:
r = utils.perlReToPythonRe(arg) r = utils.perlReToPythonRe(arg)
except ValueError, e: except ValueError, e:
@ -419,10 +427,12 @@ class Factoids(plugins.ChannelDBHandler, callbacks.Privmsg):
for glob in rest: for glob in rest:
if '*' not in glob and '?' not in glob: if '*' not in glob and '?' not in glob:
glob = '*%s*' % glob glob = '*%s*' % glob
criteria.append('key LIKE %s') criteria.append('TARGET LIKE %s')
formats.append(glob.translate(self._sqlTrans)) formats.append(glob.translate(self._sqlTrans))
cursor = db.cursor() 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) cursor.execute(sql, formats)
if cursor.rowcount == 0: if cursor.rowcount == 0:
irc.reply('No keys matched that query.') 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) cursor.execute('DELETE FROM are_factoids WHERE key=%s', key)
irc.reply(self.getRandomSaying('confirms')) 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): def factoid(self, irc, msg, match):
r"^(no[ :,-]+)?(.+?)\s+(was|is|am|were|are)\s+(also\s+)?(.+?)(?!\?+)$" r"^(no[ :,-]+)?(.+?)\s+(was|is|am|were|are)\s+(also\s+)?(.+?)(?!\?+)$"
(correction, key, isAre, addition, value) = match.groups() (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.""")) original Infobot style message or with a short message."""))
# FIXME: rename; description # FIXME: rename; description
conf.registerChannelValue(conf.supybot.plugins.Infobot, conf.registerChannelValue(conf.supybot.plugins.Infobot,
'catchWhenNotAddressed', registry.Boolean(True, 'catchWhenNotAddressed', registry.Boolean(True, """Whether to catch
"""Whether to catch non-addressed stuff at all.""")) non-addressed stuff at all."""))
conf.registerChannelValue(conf.supybot.plugins.Infobot, conf.registerChannelValue(conf.supybot.plugins.Infobot,
'replyQuestionWhenNotAddressed', registry.Boolean(True, 'replyQuestionWhenNotAddressed', registry.Boolean(True, """Whether to
"""Whether to answer to non-addressed stuff we know about.""")) answer to non-addressed stuff we know about."""))
conf.registerChannelValue(conf.supybot.plugins.Infobot, conf.registerChannelValue(conf.supybot.plugins.Infobot,
'replyDontKnowWhenNotAddressed', registry.Boolean(False, 'replyDontKnowWhenNotAddressed', registry.Boolean(False, """Whether to
"""Whether to answer to non-addressed stuff we don't know about.""")) answer to non-addressed stuff we don't know about."""))
conf.registerChannelValue(conf.supybot.plugins.Infobot, conf.registerChannelValue(conf.supybot.plugins.Infobot,
'replyWhenNotAddressed', registry.Boolean(False, 'replyWhenNotAddressed', registry.Boolean(False, """Whether to answer to
"""Whether to answer to non-addressed, non-question stuff.""")) non-addressed, non-question stuff."""))
# Replies # Replies
# XXX: Move this to a plaintext db of some sort? # XXX: Move this to a plaintext db of some sort?

View File

@ -353,9 +353,7 @@ class Misc(callbacks.Privmsg):
return return
module = sys.modules[cb.__class__.__module__] module = sys.modules[cb.__class__.__module__]
if hasattr(module, '__author__') and module.__author__: if hasattr(module, '__author__') and module.__author__:
s = module.__author__.replace('@', ' AT ') irc.reply(utils.mungeEmailForWeb(module.__author__))
s = s.replace('.', ' DOT ')
irc.reply(s)
else: else:
irc.reply('That plugin doesn\'t have an author that claims it.') irc.reply('That plugin doesn\'t have an author that claims it.')

View File

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

View File

@ -811,6 +811,28 @@ class Privmsg(irclib.IrcCallback):
else: else:
return group.get(channel)() 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): class IrcObjectProxyRegexp(RichReplyMethods):
def __init__(self, irc, msg): def __init__(self, irc, msg):

View File

@ -65,24 +65,42 @@ daemonized = False
### ###
allowEval = False allowEval = False
###
# The standard registry.
###
supybot = registry.Group() supybot = registry.Group()
supybot.setName('supybot') supybot.setName('supybot')
def registerPlugin(name, currentValue=None): def registerGroup(Group, name, group=None):
supybot.plugins.register(name, registry.Boolean(False, """Determines Group.register(name, group)
whether this plugin is loaded by default.""", showDefault=False))
if currentValue is not None: def registerGlobalValue(group, name, value):
supybot.plugins.get(name).setValue(currentValue) group.register(name, value)
def registerChannelValue(group, name, value): def registerChannelValue(group, name, value):
value.supplyDefault = True value.supplyDefault = True
group.register(name, value) 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) group.register(name, value)
def registerGroup(Group, name, group=None):
Group.register(name, group)
class ValidNick(registry.String): class ValidNick(registry.String):
"""Value must be a valid IRC nick.""" """Value must be a valid IRC nick."""
@ -410,8 +428,8 @@ required for changes to this variable to take effect."""))
### ###
registerGroup(supybot, 'drivers') registerGroup(supybot, 'drivers')
registerGlobalValue(supybot.drivers, 'poll', registerGlobalValue(supybot.drivers, 'poll',
registry.Float(1.0, """Determines the default length of time a driver should registry.Float(1.0, """Determines the default length of time a driver
block waiting for input.""")) should block waiting for input."""))
class ValidDriverModule(registry.OnlySomeStrings): class ValidDriverModule(registry.OnlySomeStrings):
validStrings = ('default', 'socketDrivers', validStrings = ('default', 'socketDrivers',
@ -420,8 +438,8 @@ class ValidDriverModule(registry.OnlySomeStrings):
registerGlobalValue(supybot.drivers, 'module', registerGlobalValue(supybot.drivers, 'module',
ValidDriverModule('default', """Determines what driver module the bot will ValidDriverModule('default', """Determines what driver module the bot will
use. socketDrivers, a simple driver based on timeout sockets, is used by 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 default because it's simple and stable. asyncoreDrivers is a bit older
less well-maintained) but allows you to integrate with asyncore-based (and less well-maintained) but allows you to integrate with asyncore-based
applications. twistedDrivers is very stable and simple, and if you've got applications. twistedDrivers is very stable and simple, and if you've got
Twisted installed, is probably your best bet.""")) Twisted installed, is probably your best bet."""))
@ -455,17 +473,17 @@ registerGlobalValue(supybot.databases.users, 'filename',
supybot.directories.conf variable.""")) supybot.directories.conf variable."""))
registerGlobalValue(supybot.databases.users, 'timeoutIdentification', registerGlobalValue(supybot.databases.users, 'timeoutIdentification',
registry.Integer(0, """Determines how long it takes identification to time 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. If the value is less than or equal to zero, identification never
out.""")) times out."""))
registerGlobalValue(supybot.databases.users, 'hash', registerGlobalValue(supybot.databases.users, 'hash',
registry.Boolean(True, """Determines whether the passwords in the user registry.Boolean(True, """Determines whether the passwords in the user
database will be hashed by default.""")) database will be hashed by default."""))
registerGroup(supybot.databases, 'ignores') registerGroup(supybot.databases, 'ignores')
registerGlobalValue(supybot.databases.ignores, 'filename', registerGlobalValue(supybot.databases.ignores, 'filename',
registry.String('ignores.conf', """Determines what filename will be used for registry.String('ignores.conf', """Determines what filename will be used
the ignores database. This file will go into the directory specified by the for the ignores database. This file will go into the directory specified
supybot.directories.conf variable.""")) by the supybot.directories.conf variable."""))
registerGroup(supybot.databases, 'channels') registerGroup(supybot.databases, 'channels')
registerGlobalValue(supybot.databases.channels, 'filename', registerGlobalValue(supybot.databases.channels, 'filename',
@ -494,19 +512,20 @@ class StrictRfc(registry.Boolean):
registerGroup(supybot, 'protocols') registerGroup(supybot, 'protocols')
registerGroup(supybot.protocols, 'irc') registerGroup(supybot.protocols, 'irc')
registerGlobalValue(supybot.protocols.irc, 'strictRfc', registerGlobalValue(supybot.protocols.irc, 'strictRfc',
StrictRfc(False, """Determines whether the bot will strictly follow the RFC; StrictRfc(False, """Determines whether the bot will strictly follow the
currently this only affects what strings are considered to be nicks. If RFC; currently this only affects what strings are considered to be nicks.
you're using a server or a network that requires you to message a nick such If you're using a server or a network that requires you to message a nick
as services@this.network.server then you you should set this to False.""")) such as services@this.network.server then you you should set this to
False."""))
registerGlobalValue(supybot.protocols.irc, 'maxHistoryLength', registerGlobalValue(supybot.protocols.irc, 'maxHistoryLength',
registry.Integer(1000, """Determines how many old messages the bot will keep registry.Integer(1000, """Determines how many old messages the bot will
around in its history. Changing this variable will not take effect until keep around in its history. Changing this variable will not take effect
the bot is restarted.""")) until the bot is restarted."""))
registerGlobalValue(supybot.protocols.irc, 'throttleTime', registerGlobalValue(supybot.protocols.irc, 'throttleTime',
registry.Float(1.0, """A floating point number of seconds to throttle queued registry.Float(1.0, """A floating point number of seconds to throttle
messages -- that is, messages will not be sent faster than once per queued messages -- that is, messages will not be sent faster than once per
throttleTime seconds.""")) throttleTime seconds."""))
registerGlobalValue(supybot.protocols.irc, 'ping', registerGlobalValue(supybot.protocols.irc, 'ping',
@ -539,5 +558,13 @@ registerGlobalValue(supybot.debug, 'flushVeryOften',
flush all flushers *very* often. Useful for debugging when you don't know 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.""")) 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: # vim:set shiftwidth=4 tabstop=8 expandtab textwidth=78:

View File

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

View File

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

View File

@ -65,7 +65,10 @@ commandsProcessed = 0
ircs = [] # A list of all the IRCs. 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 registryFilename = None