Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- #!/usr/bin/env python
- ###
- # Copyright (c) 2003-2004, Jeremiah Fincher
- # Copyright (c) 2009, James Vega
- # 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.
- ###
- import os
- import sys
- def error(s):
- sys.stderr.write(s)
- if not s.endswith(os.linesep):
- sys.stderr.write(os.linesep)
- sys.exit(-1)
- if sys.version_info < (2, 3, 0):
- error('This program requires Python >= 2.3.0')
- import supybot
- import re
- import sets
- import time
- import pydoc
- import pprint
- import socket
- import logging
- import optparse
- import supybot.ansi as ansi
- import supybot.utils as utils
- import supybot.plugin as plugin
- import supybot.ircutils as ircutils
- import supybot.registry as registry
- import supybot.questions as questions
- from supybot.questions import output, yn, anything, something, expect, getpass
- def getPlugins(pluginDirs):
- plugins = set([])
- join = os.path.join
- for pluginDir in pluginDirs:
- try:
- for filename in os.listdir(pluginDir):
- fname = join(pluginDir, filename)
- if (filename.endswith('.py') or os.path.isdir(fname)) \
- and filename[0].isupper():
- plugins.add(os.path.splitext(filename)[0])
- except OSError:
- continue
- plugins.discard('Owner')
- plugins = list(plugins)
- plugins.sort()
- return plugins
- def loadPlugin(name):
- import supybot.plugin as plugin
- try:
- module = plugin.loadPluginModule(name)
- if hasattr(module, 'Class'):
- return module
- else:
- output("""That plugin loaded fine, but didn't seem to be a real
- Supybot plugin; there was no Class variable to tell us what class
- to load when we load the plugin. We'll skip over it for now, but
- you can always add it later.""")
- return None
- except Exception, e:
- output("""We encountered a bit of trouble trying to load plugin %r.
- Python told us %r. We'll skip over it for now, you can always add it
- later.""" % (name, utils.gen.exnToString(e)))
- return None
- def describePlugin(module, showUsage):
- if module.__doc__:
- output(module.__doc__, unformatted=False)
- elif hasattr(module.Class, '__doc__'):
- output(module.Class.__doc__, unformatted=False)
- else:
- output("""Unfortunately, this plugin doesn't seem to have any
- documentation. Sorry about that.""")
- if showUsage:
- if hasattr(module, 'example'):
- if yn('This plugin has a usage example. '
- 'Would you like to see it?', default=False):
- pydoc.pager(module.example)
- else:
- output("""This plugin has no usage example.""")
- def clearLoadedPlugins(plugins, pluginRegistry):
- for plugin in plugins:
- try:
- pluginKey = pluginRegistry.get(plugin)
- if pluginKey():
- plugins.remove(plugin)
- except registry.NonExistentRegistryEntry:
- continue
- _windowsVarRe = re.compile(r'%(\w+)%')
- def getDirectoryName(default, basedir=os.curdir, prompt=True):
- done = False
- while not done:
- if prompt:
- dir = something('What directory do you want to use?',
- default=os.path.join(basedir, default))
- else:
- dir = os.path.join(basedir, default)
- orig_dir = dir
- dir = os.path.expanduser(dir)
- dir = _windowsVarRe.sub(r'$\1', dir)
- dir = os.path.expandvars(dir)
- dir = os.path.abspath(dir)
- try:
- os.makedirs(dir)
- done = True
- except OSError, e:
- if e.args[0] != 17: # File exists.
- output("""Sorry, I couldn't make that directory for some
- reason. The Operating System told me %s. You're going to
- have to pick someplace else.""" % e)
- prompt = True
- else:
- done = True
- return (dir, os.path.dirname(orig_dir))
- def main():
- import supybot.log as log
- import supybot.conf as conf
- log._stdoutHandler.setLevel(100) # *Nothing* gets through this!
- parser = optparse.OptionParser(usage='Usage: %prog [options]',
- version='Supybot %s' % conf.version)
- parser.add_option('', '--allow-root', action='store_true',
- dest='allowRoot',
- help='Determines whether the wizard will be allowed to '
- 'run as root. You don\'t want this. Don\'t do it.'
- ' Even if you think you want it, you don\'t. '
- 'You\'re probably dumb if you do this.')
- parser.add_option('', '--no-network', action='store_false',
- dest='network',
- help='Determines whether the wizard will be allowed to '
- 'run without a network connection.')
- (options, args) = parser.parse_args()
- if os.name == 'posix':
- if (os.getuid() == 0 or os.geteuid() == 0) and not options.allowRoot:
- error('Please, don\'t run this as root.')
- filename = ''
- if args:
- parser.error('This program takes no non-option arguments.')
- output("""This is a wizard to help you start running supybot. What it
- will do is create the necessary config files based on the options you
- select here. So hold on tight and be ready to be interrogated :)""")
- output("""First of all, we can bold the questions you're asked so you can
- easily distinguish the mostly useless blather (like this) from the
- questions that you actually have to answer.""")
- if yn('Would you like to try this bolding?', default=True):
- questions.useBold = True
- if not yn('Do you see this in bold?'):
- output("""Sorry, it looks like your terminal isn't ANSI compliant.
- Try again some other day, on some other terminal :)""")
- questions.useBold = False
- else:
- output("""Great!""")
- ###
- # Preliminary questions.
- ###
- output("""We've got some preliminary things to get out of the way before
- we can really start asking you questions that directly relate to what your
- bot is going to be like.""")
- # Advanced?
- output("""We want to know if you consider yourself an advanced Supybot
- user because some questions are just utterly boring and useless for new
- users. Others might not make sense unless you've used Supybot for some
- time.""")
- advanced = yn('Are you an advanced Supybot user?', default=False)
- ### Directories.
- # We set these variables in cache because otherwise conf and log will
- # create directories for the default values, which might not be what the
- # user wants.
- if advanced:
- output("""Now we've got to ask you some questions about where some of
- your directories are (or, perhaps, will be :)). If you're running this
- wizard from the directory you'll actually be starting your bot from and
- don't mind creating some directories in the current directory, then
- just don't give answers to these questions and we'll create the
- directories we need right here in this directory.""")
- # conf.supybot.directories.log
- output("""Your bot will need to put his logs somewhere. Do you have
- any specific place you'd like them? If not, just press enter and we'll
- make a directory named "logs" right here.""")
- (logDir, basedir) = getDirectoryName('logs')
- conf.supybot.directories.log.setValue(logDir)
- # conf.supybot.directories.data
- output("""Your bot will need to put various data somewhere. Things
- like databases, downloaded files, etc. Do you have any specific place
- you'd like the bot to put these things? If not, just press enter and
- we'll make a directory named "data" right here.""")
- (dataDir, basedir) = getDirectoryName('data', basedir=basedir)
- conf.supybot.directories.data.setValue(dataDir)
- # conf.supybot.directories.conf
- output("""Your bot must know where to find his configuration files.
- It'll probably only make one or two, but it's gotta have some place to
- put them. Where should that place be? If you don't care, just press
- enter and we'll make a directory right here named "conf" where it'll
- store his stuff. """)
- (confDir, basedir) = getDirectoryName('conf', basedir=basedir)
- conf.supybot.directories.conf.setValue(confDir)
- # conf.supybot.directories.backup
- output("""Your bot must know where to place backups of its conf and
- data files. Where should that place be? If you don't care, just press
- enter and we'll make a directory right here named "backup" where it'll
- store his stuff.""")
- (backupDir, basedir) = getDirectoryName('backup', basedir=basedir)
- conf.supybot.directories.backup.setValue(backupDir)
- # pluginDirs
- output("""Your bot will also need to know where to find his plugins at.
- Of course, he already knows where the plugins that he came with are,
- but your own personal plugins that you write for will probably be
- somewhere else.""")
- pluginDirs = conf.supybot.directories.plugins()
- output("""Currently, the bot knows about the following directories:""")
- output(format('%L', pluginDirs + [plugin._pluginsDir]))
- while yn('Would you like to add another plugin directory? '
- 'Adding a local plugin directory is good style.',
- default=True):
- (pluginDir, _) = getDirectoryName('plugins', basedir=basedir)
- if pluginDir not in pluginDirs:
- pluginDirs.append(pluginDir)
- conf.supybot.directories.plugins.setValue(pluginDirs)
- else:
- output("""Your bot needs to create some directories in order to store
- the various log, config, and data files.""")
- basedir = something("""Where would you like to create these
- directories?""", default=os.curdir)
- # conf.supybot.directories.log
- (logDir, basedir) = getDirectoryName('logs', prompt=False)
- conf.supybot.directories.log.setValue(logDir)
- # conf.supybot.directories.data
- (dataDir, basedir) = getDirectoryName('data',
- basedir=basedir, prompt=False)
- conf.supybot.directories.data.setValue(dataDir)
- # conf.supybot.directories.conf
- (confDir, basedir) = getDirectoryName('conf',
- basedir=basedir, prompt=False)
- conf.supybot.directories.conf.setValue(confDir)
- # conf.supybot.directories.backup
- (backupDir, basedir) = getDirectoryName('backup',
- basedir=basedir, prompt=False)
- conf.supybot.directories.backup.setValue(backupDir)
- # pluginDirs
- pluginDirs = conf.supybot.directories.plugins()
- (pluginDir, _) = getDirectoryName('plugins',
- basedir=basedir, prompt=False)
- if pluginDir not in pluginDirs:
- pluginDirs.append(pluginDir)
- conf.supybot.directories.plugins.setValue(pluginDirs)
- output("Good! We're done with the directory stuff.")
- ###
- # Bot stuff
- ###
- output("""Now we're going to ask you things that actually relate to the
- bot you'll be running.""")
- network = None
- while not network:
- output("""First, we need to know the name of the network you'd like to
- connect to. Not the server host, mind you, but the name of the
- network. If you plan to connect to irc.freenode.net, for instance, you
- should answer this question with 'freenode' (without the quotes).""")
- network = something('What IRC network will you be connecting to?')
- if '.' in network:
- output("""There shouldn't be a '.' in the network name. Remember,
- this is the network name, not the actual server you plan to connect
- to.""")
- network = None
- elif not registry.isValidRegistryName(network):
- output("""That's not a valid name for one reason or another. Please
- pick a simpler name, one more likely to be valid.""")
- network = None
- conf.supybot.networks.setValue([network])
- network = conf.registerNetwork(network)
- defaultServer = None
- server = None
- ip = None
- while not ip:
- serverString = something('What server would you like to connect to?',
- default=defaultServer)
- if options.network:
- try:
- output("""Looking up %s...""" % serverString)
- ip = socket.gethostbyname(serverString)
- except:
- output("""Sorry, I couldn't find that server. Perhaps you
- misspelled it? Also, be sure not to put the port in the
- server's name -- we'll ask you about that later.""")
- else:
- ip = 'no network available'
- output("""Found %s (%s).""" % (serverString, ip))
- output("""IRC Servers almost always accept connections on port
- 6667. They can, however, accept connections anywhere their admin
- feels like he wants to accept connections from.""")
- if yn('Does this server require connection on a non-standard port?',
- default=False):
- port = 0
- while not port:
- port = something('What port is that?')
- try:
- i = int(port)
- if not (0 < i < 65536):
- raise ValueError
- except ValueError:
- output("""That's not a valid port.""")
- port = 0
- else:
- port = 6667
- server = ':'.join([serverString, str(port)])
- network.servers.setValue([server])
- # conf.supybot.nick
- # Force the user into specifying a nick if he didn't have one already
- while True:
- nick = something('What nick would you like your bot to use?',
- default=None)
- try:
- conf.supybot.nick.set(nick)
- break
- except registry.InvalidRegistryValue:
- output("""That's not a valid nick. Go ahead and pick another.""")
- # conf.supybot.user
- if advanced:
- output("""If you've ever done a /whois on a person, you know that IRC
- provides a way for users to show the world their full name. What would
- you like your bot's full name to be? If you don't care, just press
- enter and it'll be the same as your bot's nick.""")
- user = ''
- user = something('What would you like your bot\'s full name to be?',
- default=nick)
- conf.supybot.user.set(user)
- # conf.supybot.ident (if advanced)
- defaultIdent = 'supybot'
- if advanced:
- output("""IRC servers also allow you to set your ident, which they
- might need if they can't find your identd server. What would you like
- your ident to be? If you don't care, press enter and we'll use
- 'supybot'. In fact, we prefer that you do this, because it provides
- free advertising for Supybot when users /whois your bot. But, of
- course, it's your call.""")
- while True:
- ident = something('What would you like your bot\'s ident to be?',
- default=defaultIdent)
- try:
- conf.supybot.ident.set(ident)
- break
- except registry.InvalidRegistryValue:
- output("""That was not a valid ident. Go ahead and pick
- another.""")
- else:
- conf.supybot.ident.set(defaultIdent)
- if advanced:
- # conf.supybot.networks.<network>.ssl
- output("""Some servers allow you to use a secure connection via SSL.
- This requires having pyOpenSSL installed. Currently, you also need
- Twisted installed as only the Twisted drivers supports SSL
- connections.""")
- if yn('Do you want to use an SSL connection?', default=False):
- network.ssl.setValue(True)
- # conf.supybot.networks.<network>.password
- output("""Some servers require a password to connect to them. Most
- public servers don't. If you try to connect to a server and for some
- reason it just won't work, it might be that you need to set a
- password.""")
- if yn('Do you want to set such a password?', default=False):
- network.password.set(getpass())
- # conf.supybot.networks.<network>.channels
- output("""Of course, having an IRC bot isn't the most useful thing in the
- world unless you can make that bot join some channels.""")
- if yn('Do you want your bot to join some channels when he connects?',
- default=True):
- defaultChannels = ' '.join(network.channels())
- output("""Separate channels with spaces. If the channel is locked
- with a key, follow the channel name with the key separated
- by a comma. For example:
- #supybot-bots #mychannel,mykey #otherchannel""");
- while True:
- channels = something('What channels?', default=defaultChannels)
- try:
- network.channels.set(channels)
- break
- except registry.InvalidRegistryValue, e:
- output(""""%s" is an invalid IRC channel. Be sure to prefix
- the channel with # (or +, or !, or &, but no one uses those
- channels, really). Be sure the channel key (if you are
- supplying one) does not contain a comma.""" % e.channel)
- else:
- network.channels.setValue([])
- ###
- # Plugins
- ###
- def configurePlugin(module, advanced):
- if hasattr(module, 'configure'):
- output("""Beginning configuration for %s...""" %
- module.Class.__name__)
- module.configure(advanced)
- print # Blank line :)
- output("""Done!""")
- else:
- conf.registerPlugin(module.__name__, currentValue=True)
- plugins = getPlugins(pluginDirs + [plugin._pluginsDir])
- for s in ('Admin', 'User', 'Channel', 'Misc', 'Config'):
- m = loadPlugin(s)
- if m is not None:
- configurePlugin(m, advanced)
- else:
- error('There was an error loading one of the core plugins that '
- 'under almost all circumstances are loaded. Go ahead and '
- 'fix that error and run this script again.')
- clearLoadedPlugins(plugins, conf.supybot.plugins)
- output("""Now we're going to run you through plugin configuration. There's
- a variety of plugins in supybot by default, but you can create and
- add your own, of course. We'll allow you to take a look at the known
- plugins' descriptions and configure them
- if you like what you see.""")
- # bulk
- addedBulk = False
- if advanced and yn('Would you like to add plugins en masse first?'):
- addedBulk = True
- output(format("""The available plugins are: %L.""", plugins))
- output("""What plugins would you like to add? If you've changed your
- mind and would rather not add plugins in bulk like this, just press
- enter and we'll move on to the individual plugin configuration.""")
- massPlugins = anything('Separate plugin names by spaces or commas:')
- for name in re.split(r',?\s+', massPlugins):
- module = loadPlugin(name)
- if module is not None:
- configurePlugin(module, advanced)
- clearLoadedPlugins(plugins, conf.supybot.plugins)
- # individual
- if yn('Would you like to look at plugins individually?'):
- output("""Next comes your opportunity to learn more about the plugins
- that are available and select some (or all!) of them to run in your
- bot. Before you have to make a decision, of course, you'll be able to
- see a short description of the plugin and, if you choose, an example
- session with the plugin. Let's begin.""")
- # until we get example strings again, this will default to false
- #showUsage =yn('Would you like the option of seeing usage examples?')
- showUsage = False
- name = expect('What plugin would you like to look at?',
- plugins, acceptEmpty=True)
- while name:
- module = loadPlugin(name)
- if module is not None:
- describePlugin(module, showUsage)
- if yn('Would you like to load this plugin?', default=True):
- configurePlugin(module, advanced)
- clearLoadedPlugins(plugins, conf.supybot.plugins)
- if not yn('Would you like add another plugin?'):
- break
- name = expect('What plugin would you like to look at?', plugins)
- ###
- # Sundry
- ###
- output("""Although supybot offers a supybot-adduser script, with which
- you can add users to your bot's user database, it's *very* important that
- you have an owner user for you bot.""")
- if yn('Would you like to add an owner user for your bot?', default=True):
- import supybot.ircdb as ircdb
- name = something('What should the owner\'s username be?')
- try:
- id = ircdb.users.getUserId(name)
- u = ircdb.users.getUser(id)
- if u._checkCapability('owner'):
- output("""That user already exists, and has owner capabilities
- already. Perhaps you added it before? """)
- if yn('Do you want to remove its owner capability?',
- default=False):
- u.removeCapability('owner')
- ircdb.users.setUser(id, u)
- else:
- output("""That user already exists, but doesn't have owner
- capabilities.""")
- if yn('Do you want to add to it owner capabilities?',
- default=False):
- u.addCapability('owner')
- ircdb.users.setUser(id, u)
- except KeyError:
- password = getpass('What should the owner\'s password be?')
- u = ircdb.users.newUser()
- u.name = name
- u.setPassword(password)
- u.addCapability('owner')
- ircdb.users.setUser(u)
- output("""Of course, when you're in an IRC channel you can address the bot
- by its nick and it will respond, if you give it a valid command (it may or
- may not respond, depending on what your config variable replyWhenNotCommand
- is set to). But your bot can also respond to a short "prefix character,"
- so instead of saying "bot: do this," you can say, "@do this" and achieve
- the same effect. Of course, you don't *have* to have a prefix char, but
- if the bot ends up participating significantly in your channel, it'll ease
- things.""")
- if yn('Would you like to set the prefix char(s) for your bot? ',
- default=True):
- output("""Enter any characters you want here, but be careful: they
- should be rare enough that people don't accidentally address the bot
- (simply because they'll probably be annoyed if they do address the bot
- on accident). You can even have more than one. I (jemfinch) am quite
- partial to @, but that's because I've been using it since my ocamlbot
- days.""")
- import supybot.callbacks as callbacks
- c = ''
- while not c:
- try:
- c = anything('What would you like your bot\'s prefix '
- 'character(s) to be?')
- conf.supybot.reply.whenAddressedBy.chars.set(c)
- except registry.InvalidRegistryValue, e:
- output(str(e))
- c = ''
- else:
- conf.supybot.reply.whenAddressedBy.chars.set('')
- ###
- # logging variables.
- ###
- if advanced:
- # conf.supybot.log.stdout
- output("""By default, your bot will log not only to files in the logs
- directory you gave it, but also to stdout. We find this useful for
- debugging, and also just for the pretty output (it's colored!)""")
- stdout = not yn('Would you like to turn off this logging to stdout?',
- default=False)
- conf.supybot.log.stdout.setValue(stdout)
- if conf.supybot.log.stdout():
- # conf.something
- output("""Some terminals may not be able to display the pretty
- colors logged to stderr. By default, though, we turn the colors
- off for Windows machines and leave it on for *nix machines.""")
- if os.name is not 'nt':
- conf.supybot.log.stdout.colorized.setValue(
- not yn('Would you like to turn this colorization off?',
- default=False))
- # conf.supybot.log.level
- output("""Your bot can handle debug messages at several priorities,
- CRITICAL, ERROR, WARNING, INFO, and DEBUG, in decreasing order of
- priority. By default, your bot will log all of these priorities except
- DEBUG. You can, however, specify that it only log messages above a
- certain priority level.""")
- priority = str(conf.supybot.log.level)
- logLevel = something('What would you like the minimum priority to be?'
- ' Just press enter to accept the default.',
- default=priority).lower()
- while logLevel not in ['debug','info','warning','error','critical']:
- output("""That's not a valid priority. Valid priorities include
- 'DEBUG', 'INFO', 'WARNING', 'ERROR', and 'CRITICAL'""")
- logLevel = something('What would you like the minimum priority to '
- 'be? Just press enter to accept the default.',
- default=priority).lower()
- conf.supybot.log.level.set(logLevel)
- # conf.supybot.databases.plugins.channelSpecific
- output("""Many plugins in Supybot are channel-specific. Their
- databases, likewise, are specific to each channel the bot is in. Many
- people don't want this, so we have one central location in which to
- say that you would prefer all databases for all channels to be shared.
- This variable, supybot.databases.plugins.channelSpecific, is that
- place.""")
- conf.supybot.databases.plugins.channelSpecific.setValue(
- not yn('Would you like plugin databases to be shared by all '
- 'channels, rather than specific to each channel the '
- 'bot is in?'))
- output("""There are a lot of options we didn't ask you about simply
- because we'd rather you get up and running and have time
- left to play around with your bot. But come back and see
- us! When you've played around with your bot enough to
- know what you like, what you don't like, what you'd like
- to change, then take a look at your configuration file
- when your bot isn't running and read the comments,
- tweaking values to your heart's desire.""")
- # Let's make sure that src/ plugins are loaded.
- conf.registerPlugin('Admin', True)
- conf.registerPlugin('Channel', True)
- conf.registerPlugin('Config', True)
- conf.registerPlugin('Misc', True)
- conf.registerPlugin('User', True)
- ###
- # Write the registry
- ###
- # We're going to need to do a darcs predist thing here.
- #conf.supybot.debug.generated.setValue('...')
- if not filename:
- filename = '%s.conf' % nick
- registry.close(conf.supybot, filename)
- # Done!
- output("""All done! Your new bot configuration is %s. If you're running
- a *nix based OS, you can probably start your bot with the command line
- "supybot %s". If you're not running a *nix or similar machine, you'll
- just have to start it like you start all your other Python scripts.""" % \
- (filename, filename))
- if __name__ == '__main__':
- try:
- main()
- except KeyboardInterrupt:
- # We may still be using bold text when exiting during a prompt
- if questions.useBold:
- import supybot.ansi as ansi
- print ansi.RESET
- print
- print
- output("""Well, it looks like you canceled out of the wizard before
- it was done. Unfortunately, I didn't get to write anything to file.
- Please run the wizard again to completion.""")
- # vim:set shiftwidth=4 softtabstop=4 expandtab textwidth=79:
Add Comment
Please, Sign In to add comment