### # Copyright (c) 2002-2004, 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. ### """ Handles configuration of the bot while it's running. """ import supybot __revision__ = "$Id$" __author__ = supybot.authors.jemfinch import os import getopt import signal import supybot.log as log import supybot.conf as conf import supybot.utils as utils import supybot.world as world import supybot.ircdb as ircdb from supybot.commands import * import supybot.ircutils as ircutils import supybot.registry as registry import supybot.callbacks as callbacks ### # Now, to setup the registry. ### def getWrapper(name): parts = registry.split(name) if not parts or parts[0] not in ('supybot', 'users'): raise InvalidRegistryName, name group = getattr(conf, parts.pop(0)) while parts: try: group = group.get(parts.pop(0)) # We'll catch registry.InvalidRegistryName and re-raise it here so # that we have a useful error message for the user. except (registry.NonExistentRegistryEntry, registry.InvalidRegistryName): raise registry.InvalidRegistryName, name return group def getCapability(name): capability = 'owner' # Default to requiring the owner capability. parts = registry.split(name) while parts: part = parts.pop() if ircutils.isChannel(part): # If a registry value has a channel in it, it requires a channel.op # capability, or so we assume. We'll see if we're proven wrong. capability = ircdb.makeChannelCapability(part, 'op') ### Do more later, for specific capabilities/sections. return capability def _reload(): ircdb.users.reload() ircdb.channels.reload() registry.open(world.registryFilename) def _hupHandler(sig, frame): log.info('Received SIGHUP, reloading configuration.') _reload() if os.name == 'posix': signal.signal(signal.SIGHUP, _hupHandler) def getConfigVar(irc, msg, args, state): name = args[0] if name.startswith('conf.'): name = name[5:] if not name.startswith('supybot') and not name.startswith('users'): name = 'supybot.' + name try: group = getWrapper(name) state.args.append(group) del args[0] except registry.InvalidRegistryName, e: irc.errorInvalid('configuration variable', str(e)) addConverter('configVar', getConfigVar) class Config(callbacks.Privmsg): def callCommand(self, name, irc, msg, *L, **kwargs): try: super(Config, self).callCommand(name, irc, msg, *L, **kwargs) except registry.InvalidRegistryValue, e: irc.error(str(e)) def _list(self, group): L = [] for (vname, v) in group._children.iteritems(): if hasattr(group, 'channelValue') and group.channelValue and \ ircutils.isChannel(vname) and not v._children: continue if hasattr(v, 'channelValue') and v.channelValue: vname = '#' + vname if v._added and not all(ircutils.isChannel, v._added): vname = '@' + vname L.append(vname) utils.sortBy(str.lower, L) return L def list(self, irc, msg, args, group): """ Returns the configuration variables available under the given configuration . If a variable has values under it, it is preceded by an '@' sign. If a variable is a 'ChannelValue', that is, it can be separately configured for each channel using the 'channel' command in this plugin, it is preceded by an '#' sign. """ L = self._list(group) if L: irc.reply(utils.commaAndify(L)) else: irc.error('There don\'t seem to be any values in %s.' % group._name) list = wrap(list, ['configVar']) def search(self, irc, msg, args, word): """ Searches for in the current configuration variables. """ L = [] for (name, _) in conf.supybot.getValues(getChildren=True): if word in name.lower(): possibleChannel = registry.split(name)[-1] if not ircutils.isChannel(possibleChannel): L.append(name) if L: irc.reply(utils.commaAndify(L)) else: irc.reply('There were no matching configuration variables.') search = wrap(search, ['lowered']) # XXX compose with withoutSpaces? def _getValue(self, irc, msg, group): if hasattr(group, 'value'): if not group._private: irc.reply(str(group) or ' ') else: capability = getCapability(group._name) if ircdb.checkCapability(msg.prefix, capability): irc.reply(str(group), private=True) else: irc.errorNoCapability(capability) else: irc.error('That registry variable has no value. Use the list ' 'command in this plugin to see what variables are ' 'available in this group.') def _setValue(self, irc, msg, group, value): capability = getCapability(group._name) if ircdb.checkCapability(msg.prefix, capability): # I think callCommand catches exceptions here. Should it? group.set(value) irc.replySuccess() else: irc.errorNoCapability(capability) def channel(self, irc, msg, args, channel, group, value): """[] [] If is given, sets the channel configuration variable for to for . Otherwise, returns the current channel configuration value of . is only necessary if the message isn't sent in the channel itself.""" if not group.channelValue: irc.error('That configuration variable is not a channel-specific ' 'configuration variable.') return group = group.get(channel) if value is not None: self._setValue(irc, msg, group, value) else: self._getValue(irc, msg, group) channel = wrap(channel, ['channel', 'configVar', additional('text')]) def config(self, irc, msg, args, group, value): """ [] If is given, sets the value of to . Otherwise, returns the current value of . You may omit the leading "supybot." in the name if you so choose. """ if value is not None: self._setValue(irc, msg, group, value) else: self._getValue(irc, msg, group) config = wrap(config, ['configVar', additional('text')]) def help(self, irc, msg, args, group): """ Returns the description of the configuration variable . """ if hasattr(group, '_help'): s = group.help() if s: if hasattr(group, 'value') and not group._private: s += ' (Current value: %s)' % group irc.reply(s) else: irc.reply('That configuration group exists, but seems to have ' 'no help. Try "config list %s" to see if it has ' 'any children values.') else: irc.error('%s has no help.' % name) help = wrap(help, ['configVar']) def default(self, irc, msg, args, group): """ Returns the default value of the configuration variable . """ v = group.__class__(group._default, '') irc.reply(str(v)) default = wrap(default, ['configVar']) def reload(self, irc, msg, args): """takes no arguments Reloads the various configuration files (user database, channel database, registry, etc.). """ _reload() # This was factored out for SIGHUP handling. irc.replySuccess() reload = wrap(reload, [('checkCapability', 'owner')]) def export(self, irc, msg, args, filename): """ Exports the public variables of your configuration to . If you want to show someone your configuration file, but you don't want that person to be able to see things like passwords, etc., this command will export a "sanitized" configuration file suitable for showing publicly. """ registry.close(conf.supybot, filename, private=False) irc.replySuccess() export = wrap(export, [('checkCapability', 'owner'), 'filename']) Class = Config # vim:set shiftwidth=4 tabstop=8 expandtab textwidth=78: