HUUUUUUUUGE Configuration change.

This commit is contained in:
Jeremy Fincher 2004-01-18 07:58:26 +00:00
parent b18ad358ee
commit 6ca78924f3
59 changed files with 887 additions and 1091 deletions

View File

@ -1,3 +1,6 @@
* Removed Admin.setprefixchar, since it's unneeded with the new
configuration.
* Changed the reply method of the irc object given to plugins not
to require a msg object.

View File

@ -40,5 +40,11 @@ othersDir = os.path.join(installDir, 'others')
sys.path.insert(0, srcDir)
sys.path.insert(0, othersDir)
# vim:set shiftwidth=4 tabstop=8 expandtab textwidth=78:
import registry
supybot = registry.Group()
supybot.setName('supybot')
supybot.registerGroup('plugins')
# vim:set shiftwidth=4 tabstop=8 expandtab textwidth=78:

View File

@ -156,7 +156,7 @@ def makeNewAlias(name, alias):
class Alias(callbacks.Privmsg):
def __init__(self):
callbacks.Privmsg.__init__(self)
filename = os.path.join(conf.dataDir, 'Aliases.db')
filename = os.path.join(conf.supybot.directories.data(), 'Aliases.db')
# Schema: {name: [alias, locked]}
self.aliases = structures.PersistentDictionary(filename)
@ -207,7 +207,7 @@ class Alias(callbacks.Privmsg):
def addAlias(self, irc, name, alias, lock=False):
if self._invalidCharsRe.search(name):
raise AliasError, 'Names cannot contain spaces or square brackets.'
if conf.enablePipeSyntax and '|' in name:
if conf.supybot.pipeSyntax() and '|' in name:
raise AliasError, 'Names cannot contain pipes.'
realName = callbacks.canonicalName(name)
if name != realName:

View File

@ -67,7 +67,7 @@ priorityKeys = ['p1', 'p2', 'p3', 'p4', 'p5', 'Low', 'Normal', 'High',
severityKeys = ['enhancement', 'trivial', 'minor', 'normal', 'major',
'critical', 'blocker']
dbfilename = os.path.join(conf.dataDir, 'Bugzilla.db')
dbfilename = os.path.join(conf.supybot.directories.data(), 'Bugzilla.db')
def makeDb(filename):
if os.path.exists(filename):

View File

@ -91,7 +91,8 @@ class ChannelLogger(irclib.IrcCallback):
return self.logs[channel]
else:
try:
log = file(os.path.join(conf.logDir, '%s.log' % channel), 'a')
logDir = conf.supybot.directories.log()
log = file(os.path.join(logDir, '%s.log' % channel), 'a')
self.logs[channel] = log
return log
except IOError:
@ -99,7 +100,7 @@ class ChannelLogger(irclib.IrcCallback):
return StringIO()
def timestamp(self, log):
log.write(time.strftime(conf.logTimestampFormat))
log.write(time.strftime(conf.supybot.log.timestampFormat()))
log.write(' ')
def doPrivmsg(self, irc, msg):

View File

@ -71,8 +71,8 @@ class DCC(callbacks.Privmsg):
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.settimeout(60)
host = ircutils.hostFromHostmask(irc.prefix)
if conf.externalIP is not None:
ip = conf.externalIP
if conf.supybot.externalIP():
ip = conf.supybot.externalIP()
else:
try:
ip = socket.gethostbyname(host)

View File

@ -93,7 +93,7 @@ class Debian(callbacks.Privmsg,
'debian/dists/unstable/Contents-i386.gz',
604800, None)
}
contents = os.path.join(conf.dataDir, 'Contents-i386.gz')
contents = os.path.join(conf.supybot.directories.data(),'Contents-i386.gz')
configurables = configurable.Dictionary(
[('python-zegrep', configurable.BoolType, False,
"""An advanced option, mostly just for testing; uses a Python-coded

View File

@ -51,7 +51,7 @@ except ImportError:
raise callbacks.Error, 'You need to have PySQLite installed to use this ' \
'plugin. Download it at <http://pysqlite.sf.net/>'
dbfilename = os.path.join(conf.dataDir, 'Dunno.db')
dbfilename = os.path.join(conf.supybot.directories.data(), 'Dunno.db')
def configure(onStart, afterConnect, advanced):
# This will be called by setup.py to configure this module. onStart and

View File

@ -172,7 +172,8 @@ class Ebay(callbacks.PrivmsgCommandAndRegexp, configurable.Mixin):
return '; '.join(resp)
else:
raise EbayError, 'That doesn\'t appear to be a proper eBay ' \
'auction page. (%s)' % conf.replyPossibleBug
'auction page. (%s)' % \
conf.supybot.replies.possibleBug()
Class = Ebay

View File

@ -134,7 +134,7 @@ class Enforcer(callbacks.Privmsg, configurable.Mixin):
irc.queueMsg(ircmsgs.topic(channel, self.topics[channel]))
if self.configurables.get('revenge', channel):
irc.queueMsg(ircmsgs.kick(channel, msg.nick,
conf.replyNoCapability %
conf.supybot.replies.noCapability() %
_chanCap(channel, 'topic')))
else:
self.topics[channel] = msg.args[1]

View File

@ -319,7 +319,7 @@ class Factoids(plugins.ChannelDBHandler,
counter = 0
for (added_by, added_at) in factoids:
counter += 1
added_at = time.strftime(conf.humanTimestampFormat,
added_at = time.strftime(conf.supybot.humanTimestampFormat(),
time.localtime(int(added_at)))
L.append('#%s was added by %s at %s' % (counter,added_by,added_at))
factoids = '; '.join(L)

View File

@ -159,7 +159,7 @@ class Gameknot(callbacks.PrivmsgCommandAndRegexp, configurable.Mixin):
raise callbacks.Error, 'No user %s exists.' % name
else:
raise callbacks.Error,'The format of the page was odd. %s' % \
conf.replyPossibleBug
conf.supybot.replies.possibleBug()
except urllib2.URLError:
raise callbacks.Error, 'Couldn\'t connect to gameknot.com'

View File

@ -63,11 +63,13 @@ class HeraldDB(object):
def __init__(self):
self.heralds = {}
self.open()
dataDir = conf.supybot.directories.data()
self.filename = os.path.join(dataDir, 'Herald.db')
def open(self):
filename = os.path.join(conf.dataDir, 'Herald.db')
if os.path.exists(filename):
fd = file(filename)
dataDir = conf.supybot.directories.data()
if os.path.exists(self.filename):
fd = file(self.filename)
for line in fd:
line = line.rstrip()
try:
@ -81,7 +83,7 @@ class HeraldDB(object):
fd.close()
def close(self):
fd = file(os.path.join(conf.dataDir, 'Herald.db'), 'w')
fd = file(self.filename, 'w')
L = self.heralds.items()
L.sort()
for ((id, channel), msg) in L:

View File

@ -52,7 +52,7 @@ except ImportError:
raise callbacks.Error, 'You need to have PySQLite installed to use this ' \
'plugin. Download it at <http://pysqlite.sf.net/>'
dbfilename = os.path.join(conf.dataDir, 'Infobot.db')
dbfilename = os.path.join(conf.supybot.directories.data(), 'Infobot.db')
def configure(onStart, afterConnect, advanced):
from questions import expect, anything, something, yn

View File

@ -64,13 +64,16 @@ def configure(onStart, afterConnect, advanced):
onStart.append('load Lookup')
print 'This module allows you to define commands that do a simple key'
print 'lookup and return some simple value. It has a command "add"'
### TODO: fix conf.dataDir here. I'm waiting until we rewrite this with
### a proper question.py print statement.
print 'that takes a command name and a file in conf.dataDir and adds a'
print 'command with that name that responds with mapping from that file.'
print 'The file itself should be composed of lines of the form key:value.'
while yn('Would you like to add a file?') == 'y':
filename = something('What\'s the filename?')
try:
fd = file(os.path.join(conf.dataDir, filename))
dataDir = conf.supybot.directories.data()
fd = file(os.path.join(dataDir, filename))
except EnvironmentError, e:
print 'I couldn\'t open that file: %s' % e
continue
@ -97,7 +100,8 @@ class Lookup(callbacks.Privmsg):
def __init__(self):
callbacks.Privmsg.__init__(self)
self.lookupDomains = sets.Set()
self.dbHandler = LookupDB(name=os.path.join(conf.dataDir, 'Lookup'))
dataDir = conf.supybot.directories.data()
self.dbHandler = LookupDB(name=os.path.join(dataDir, 'Lookup'))
def _shrink(self, s):
return utils.ellipsisify(s, 50)
@ -132,8 +136,8 @@ class Lookup(callbacks.Privmsg):
Adds a lookup for <name> with the key/value pairs specified in the
colon-delimited file specified by <filename>. <filename> is searched
for in conf.dataDir. If <name> is not singular, we try to make it
singular before creating the command.
for in conf.supybot.directories.data. If <name> is not singular, we
try to make it singular before creating the command.
"""
(name, filename) = privmsgs.getArgs(args, required=2)
name = utils.depluralize(name)
@ -151,7 +155,8 @@ class Lookup(callbacks.Privmsg):
except sqlite.DatabaseError:
# Good, there's no such database.
try:
filename = os.path.join(conf.dataDir, filename)
dataDir = conf.supybot.directories.data()
filename = os.path.join(dataDir, filename)
fd = file(filename)
except EnvironmentError, e:
irc.error('Could not open %s: %s' % (filename, e.args[1]))

View File

@ -65,7 +65,7 @@ except ImportError:
raise callbacks.Error, 'You need to have PySQLite installed to use this ' \
'plugin. Download it at <http://pysqlite.sf.net/>'
dbfilename = os.path.join(conf.dataDir, 'MoobotFactoids')
dbfilename = os.path.join(conf.supybot.directories.data(), 'MoobotFactoids')
def configure(onStart, afterConnect, advanced):
# This will be called by setup.py to configure this module. onStart and
@ -421,19 +421,19 @@ class MoobotFactoids(callbacks.PrivmsgCommandAndRegexp):
# First, creation info.
# Map the integer created_by to the username
creat_by = ircdb.users.getUser(created_by).name
creat_at = time.strftime(conf.humanTimestampFormat,
creat_at = time.strftime(conf.supybot.humanTimestampFormat(),
time.localtime(int(created_at)))
s += "Created by %s on %s." % (creat_by, creat_at)
# Next, modification info, if any.
if modified_by is not None:
mod_by = ircdb.users.getUser(modified_by).name
mod_at = time.strftime(conf.humanTimestampFormat,
mod_at = time.strftime(conf.supybot.humanTimestampFormat(),
time.localtime(int(modified_at)))
s += " Last modified by %s on %s." % (mod_by, mod_at)
# Next, last requested info, if any
if last_requested_by is not None:
last_by = last_requested_by # not an int user id
last_at = time.strftime(conf.humanTimestampFormat,
last_at = time.strftime(conf.supybot.humanTimestampFormat(),
time.localtime(int(last_requested_at)))
req_count = requested_count
times_str = utils.nItems('time', requested_count)
@ -441,7 +441,7 @@ class MoobotFactoids(callbacks.PrivmsgCommandAndRegexp):
(last_by, last_at, times_str)
# Last, locked info
if locked_at is not None:
lock_at = time.strftime(conf.humanTimestampFormat,
lock_at = time.strftime(conf.supybot.humanTimestampFormat(),
time.localtime(int(locked_at)))
lock_by = ircdb.users.getUser(locked_by).name
s += " Locked by %s on %s." % (lock_by, lock_at)

View File

@ -139,14 +139,14 @@ class News(plugins.ChannelDBHandler, callbacks.Privmsg):
if int(expires_at) == 0:
s = '%s (Subject: "%s", added by %s on %s)' % \
(item, subject, added_by,
time.strftime(conf.humanTimestampFormat,
time.strftime(conf.supybot.humanTimestampFormat(),
time.localtime(int(added_at))))
else:
s = '%s (Subject: "%s", added by %s on %s, expires at %s)' % \
(item, subject, added_by,
time.strftime(conf.humanTimestampFormat,
time.strftime(conf.supybot.humanTimestampFormat(),
time.localtime(int(added_at))),
time.strftime(conf.humanTimestampFormat,
time.strftime(conf.supybot.humanTimestampFormat(),
time.localtime(int(expires_at))))
irc.reply(s)

View File

@ -57,8 +57,6 @@ except ImportError:
raise callbacks.Error, 'You need to have PySQLite installed to use this ' \
'plugin. Download it at <http://pysqlite.sf.net/>'
dbfilename = os.path.join(conf.dataDir, 'Notes.db')
class NoteDb(plugins.DBHandler):
def makeDb(self, filename):
"create Notes database and tables"
@ -85,7 +83,8 @@ class NoteDb(plugins.DBHandler):
class Note(callbacks.Privmsg):
def __init__(self):
callbacks.Privmsg.__init__(self)
self.dbHandler = NoteDb(name=os.path.join(conf.dataDir, 'Notes'))
dataDir = conf.supybot.directories.data()
self.dbHandler = NoteDb(name=os.path.join(dataDir, 'Notes'))
def setAsRead(self, id):
db = self.dbHandler.getDb()

View File

@ -256,7 +256,7 @@ class QuoteGrabs(plugins.ChannelDBHandler,
irc.error('No quotegrab for id %r' % id)
return
quote, hostmask, timestamp = cursor.fetchone()
time_str = time.strftime(conf.humanTimestampFormat,
time_str = time.strftime(conf.supybot.humanTimestampFormat(),
time.localtime(float(timestamp)))
irc.reply('%s (Said by: %s on %s)' % (quote, hostmask, time_str))

View File

@ -220,7 +220,7 @@ class Quotes(plugins.ChannelDBHandler, callbacks.Privmsg):
cursor.execute("""SELECT * FROM quotes WHERE id=%s""", id)
if cursor.rowcount == 1:
(id, added_by, added_at, quote) = cursor.fetchone()
timestamp = time.strftime(conf.humanTimestampFormat,
timestamp = time.strftime(conf.supybot.humanTimestampFormat(),
time.localtime(int(added_at)))
irc.reply('Quote %r added by %s at %s.' %
(quote, added_by, timestamp))

View File

@ -30,7 +30,7 @@
###
"""
Logs raw IRC messages to a file, conf.dataDir/raw.log
Logs raw IRC messages to a file.
"""
__revision__ = "$Id$"
@ -47,7 +47,8 @@ import irclib
###
class RawLogger(irclib.IrcCallback):
def __init__(self):
self.fd = file(os.path.join(conf.logDir, 'raw.log'), 'a')
logDir = conf.supybot.directories.log()
self.fd = file(os.path.join(logDir, 'raw.log'), 'a')
world.flushers.append(self.fd.flush)
def inFilter(self, irc, msg):

View File

@ -452,7 +452,7 @@ class Relay(callbacks.Privmsg, configurable.Mixin):
channels = utils.commaAndify(L)
if '317' in d:
idle = utils.timeElapsed(d['317'].args[2])
signon = time.strftime(conf.humanTimestampFormat,
signon = time.strftime(conf.supybot.humanTimestampFormat(),
time.localtime(float(d['317'].args[3])))
else:
idle = '<unknown>'

View File

@ -170,7 +170,7 @@ class Sourceforge(callbacks.PrivmsgCommandAndRegexp, configurable.Mixin):
resp = imap(lambda s: utils.ellipsisify(s, 50), resp)
return '%s' % utils.commaAndify(resp)
raise callbacks.Error, 'No Trackers were found. (%s)' % \
conf.replyPossibleBug
conf.supybot.replies.possibleBug()
except webutils.WebError, e:
raise callbacks.Error, str(e)

View File

@ -61,7 +61,8 @@ def configure(onStart, afterConnect, advanced):
class UptimeDB(object):
def __init__(self, filename='uptimes'):
self.filename = os.path.join(conf.dataDir, filename)
dataDir = conf.supybot.directories.data()
self.filename = os.path.join(dataDir, filename)
if os.path.exists(self.filename):
fd = file(self.filename)
s = fd.read()
@ -137,9 +138,9 @@ class Status(callbacks.Privmsg):
return
def format((started, ended)):
return '%s until %s; up for %s' % \
(time.strftime(conf.humanTimestampFormat,
(time.strftime(conf.supybot.humanTimestampFormat(),
time.localtime(started)),
time.strftime(conf.humanTimestampFormat,
time.strftime(conf.supybot.humanTimestampFormat(),
time.localtime(ended)),
utils.timeElapsed(ended-started))
irc.reply(utils.commaAndify(imap(format, L)))
@ -186,7 +187,7 @@ class Status(callbacks.Privmsg):
try:
r = os.popen('ps -o rss -p %s' % pid)
r.readline() # VSZ Header.
mem = r.readline().strip() + ' kB'
mem = r.readline().strip()
finally:
r.close()
elif sys.platform.startswith('netbsd'):

View File

@ -87,7 +87,8 @@ class TodoDB(plugins.DBHandler):
class Todo(callbacks.Privmsg):
def __init__(self):
callbacks.Privmsg.__init__(self)
self.dbHandler = TodoDB(os.path.join(conf.dataDir, 'Todo'))
dataDir = conf.supybot.directories.data()
self.dbHandler = TodoDB(os.path.join(dataDir, 'Todo'))
def die(self):
self.dbHandler.die()
@ -170,7 +171,7 @@ class Todo(callbacks.Privmsg):
active = 'Inactive'
if pri:
task += ', priority: %s' % pri
added_at = time.strftime(conf.humanTimestampFormat,
added_at = time.strftime(conf.supybot.humanTimestampFormat(),
time.localtime(int(added_at)))
s = "%s todo for %s: %s (Added at %s)" % \
(active, name, task, added_at)

View File

@ -260,7 +260,7 @@ class URL(callbacks.PrivmsgCommandAndRegexp,
return (tinyurl, updateDb)
def _formatUrl(self, url, added, addedBy):
when = time.strftime(conf.humanTimestampFormat,
when = time.strftime(conf.supybot.supybot.humanTimestampFormat(),
time.localtime(int(added)))
return '<%s> (added by %s at %s)' % (url, addedBy, when)

View File

@ -160,9 +160,10 @@ class Words(callbacks.Privmsg, configurable.Mixin):
def __init__(self):
callbacks.Privmsg.__init__(self)
configurable.Mixin.__init__(self)
self.dbHandler = WordsDB(os.path.join(conf.dataDir, 'Words'))
dataDir = conf.supybot.directories.data()
self.dbHandler = WordsDB(os.path.join(dataDir, 'Words'))
try:
dictfile = os.path.join(conf.dataDir, 'dict')
dictfile = os.path.join(dataDir, 'dict')
self.wordList = file(dictfile).readlines()
self.gotDictFile = True
except IOError:
@ -367,31 +368,7 @@ class Words(callbacks.Privmsg, configurable.Mixin):
Class = Words
if __name__ == '__main__':
import sys, log
if len(sys.argv) < 2:
fd = sys.stdin
else:
try:
fd = file(sys.argv[1])
except EnvironmentError, e:
sys.stderr.write(str(e) + '\n')
sys.exit(-1)
db = WordsDB(os.path.join(conf.dataDir, 'Words')).getDb()
cursor = db.cursor()
cursor.execute("""PRAGMA cache_size=20000""")
lineno = 0
for line in fd:
lineno += 1
line = line.rstrip()
try:
addWord(db, line)
except KeyboardInterrupt:
sys.exit(-1)
except Exception, e:
sys.stderr.write('Error on line %s: %s\n' % (lineno, e))
db.commit()
### TODO: Write a script to make the database.
# vim:set shiftwidth=4 tabstop=8 expandtab textwidth=78:

View File

@ -38,6 +38,7 @@ __revision__ = "$Id$"
import re
import os
import sys
import atexit
if sys.version_info < (2, 3, 0):
sys.stderr.write('This program requires Python >= 2.3.0\n')
@ -105,6 +106,10 @@ if __name__ == '__main__':
parser.add_option('-p', '--password', action='store',
dest='password', default='',
help='server password the bot should use')
parser.add_option('', '--enable-eval', action='store_true',
dest='allowEval',
help='Determines whether the bot will '
'allow the evaluation of arbitrary Python code.')
(options, args) = parser.parse_args()
@ -128,12 +133,12 @@ if __name__ == '__main__':
log.info('Finished writing registry file.')
atexit.register(closeRegistry)
nick = options.nick or conf.supybot.nick.get()
user = options.user or conf.supybot.user.get()
ident = options.ident or conf.supybot.ident.get()
password = options.password or conf.supybot.password.get()
nick = options.nick or conf.supybot.nick()
user = options.user or conf.supybot.user()
ident = options.ident or conf.supybot.ident()
password = options.password or conf.supybot.password()
server = options.server or conf.supybot.server.get()
server = options.server or conf.supybot.server()
if ':' in server:
serverAndPort = server.split(':', 1)
serverAndPort[1] = int(serverAndPort[1])
@ -150,12 +155,15 @@ if __name__ == '__main__':
except ImportError:
log.warning('Psyco isn\'t installed, cannot -OO.')
if not os.path.exists(conf.supybot.directories.log.get()):
os.mkdir(conf.supybot.directories.log.get())
if not os.path.exists(conf.supybot.directories.conf.get()):
os.mkdir(conf.supybot.directories.conf.get())
if not os.path.exists(conf.supybot.directories.data.get()):
os.mkdir(conf.supybot.directories.data.get())
if options.allowEval:
conf.allowEval = True
if not os.path.exists(conf.supybot.directories.log()):
os.mkdir(conf.supybot.directories.log())
if not os.path.exists(conf.supybot.directories.conf()):
os.mkdir(conf.supybot.directories.conf())
if not os.path.exists(conf.supybot.directories.data()):
os.mkdir(conf.supybot.directories.data())
import irclib
import ircmsgs

View File

@ -89,7 +89,8 @@ setup(
'supybot.others.unum': os.path.join('others', 'unum'),
'supybot.others.unum.units':
os.path.join(os.path.join('others', 'unum'), 'units')},
scripts=['scripts/supybot-wizard',
scripts=['scripts/supybot',
'scripts/supybot-wizard',
'scripts/supybot-adduser',
'scripts/supybot-newplugin']
)

View File

@ -61,6 +61,10 @@ class Admin(privmsgs.CapabilityCheckingPrivmsg):
privmsgs.CapabilityCheckingPrivmsg.__init__(self)
self.joins = {}
def do376(self, irc, msg):
irc.queueMsg(ircmsgs.joins(conf.supybot.channels()))
do422 = do377 = do376
def do471(self, irc, msg):
try:
channel = msg.args[1]
@ -107,7 +111,7 @@ class Admin(privmsgs.CapabilityCheckingPrivmsg):
def doInvite(self, irc, msg):
if msg.args[1] not in irc.state.channels:
if conf.alwaysJoinOnInvite:
if conf.supybot.alwaysJoinOnInvite():
irc.queueMsg(ircmsgs.join(msg.args[1]))
else:
if ircdb.checkCapability(msg.prefix, 'admin'):
@ -182,16 +186,14 @@ class Admin(privmsgs.CapabilityCheckingPrivmsg):
if command in ('enable', 'identify'):
irc.error('You can\'t disable %s!' % command)
else:
# This has to know that defaultCapabilties gets turned into a
# dictionary.
try:
capability = ircdb.makeAntiCapability(command)
except ValueError:
irc.error('%r is not a valid command.' % command)
return
if command in conf.defaultCapabilities:
conf.defaultCapabilities.remove(command)
conf.defaultCapabilities.add(capability)
if command in conf.supybot.defaultCapabilities():
conf.supybot.defaultCapabilities().remove(command)
conf.supybot.defaultCapabilities().add(capability)
irc.replySuccess()
def enable(self, irc, msg, args):
@ -205,8 +207,8 @@ class Admin(privmsgs.CapabilityCheckingPrivmsg):
except ValueError:
irc.error('%r is not a valid command.' % command)
return
if anticapability in conf.defaultCapabilities:
conf.defaultCapabilities.remove(anticapability)
if anticapability in conf.supybot.defaultCapabilities():
conf.supybot.defaultCapabilities().remove(anticapability)
irc.replySuccess()
else:
irc.error('That command wasn\'t disabled.')
@ -228,14 +230,14 @@ class Admin(privmsgs.CapabilityCheckingPrivmsg):
# Thus, the owner capability can't be given in the bot. Admin users
# can only give out capabilities they have themselves (which will
# depend on both conf.defaultAllow and conf.defaultCapabilities), but
# generally means they can't mess with channel capabilities.
# depend on both conf.supybot.defaultAllow and
# conf.supybot.defaultCapabilities), but generally means they can't
# mess with channel capabilities.
(name, capability) = privmsgs.getArgs(args, required=2)
if capability == 'owner':
irc.error('The "owner" capability can\'t be added in the bot. '
'Use the supybot-adduser program (or edit the '
'users.conf file yourself) to add an owner '
'capability.')
'users.conf file yourself) to add an owner capability.')
return
if ircdb.checkCapability(msg.prefix, capability) or \
'-' in capability:
@ -292,7 +294,7 @@ class Admin(privmsgs.CapabilityCheckingPrivmsg):
except KeyError:
irc.error('I can\'t find a hostmask for %s' % arg)
return
conf.ignores.append(hostmask)
conf.supybot.ignores().append(hostmask)
irc.replySuccess()
def unignore(self, irc, msg, args):
@ -311,38 +313,23 @@ class Admin(privmsgs.CapabilityCheckingPrivmsg):
irc.error('I can\'t find a hostmask for %s' % arg)
return
try:
conf.ignores.remove(hostmask)
while hostmask in conf.ignores:
conf.ignores.remove(hostmask)
conf.supybot.ignores().remove(hostmask)
while hostmask in conf.supybot.ignores():
conf.supybot.ignores().remove(hostmask)
irc.replySuccess()
except ValueError:
irc.error('%s wasn\'t in conf.ignores.' % hostmask)
irc.error('%s wasn\'t in conf.supybot.ignores.' % hostmask)
def ignores(self, irc, msg, args):
"""takes no arguments
Returns the hostmasks currently being globally ignored.
"""
if conf.ignores:
irc.reply(utils.commaAndify(imap(repr, conf.ignores)))
if conf.supybot.ignores():
irc.reply(utils.commaAndify(imap(repr, conf.supybot.ignores())))
else:
irc.reply('I\'m not currently globally ignoring anyone.')
def setprefixchar(self, irc, msg, args):
"""<prefixchars>
Sets the prefix chars by which the bot can be addressed.
"""
s = privmsgs.getArgs(args)
for c in s:
if c not in conf.validPrefixChars:
s = 'PrefixChars must be something in %r'%conf.validPrefixChars
irc.error(s)
return
else:
conf.prefixChars = s
irc.replySuccess()
def reportbug(self, irc, msg, args):
"""<description>

View File

@ -37,6 +37,8 @@ import plugins
import conf
import utils
import ircdb
import ircutils
import registry
import privmsgs
import callbacks
@ -50,7 +52,7 @@ class InvalidRegistryName(callbacks.Error):
pass
def getWrapper(name):
parts = name.split()
parts = name.split('.')
if not parts or parts[0] != 'supybot':
raise InvalidRegistryName, name
group = conf.supybot
@ -62,13 +64,25 @@ def getWrapper(name):
raise InvalidRegistryName, name
return group
def getCapability(name):
capability = 'owner' # Default to requiring the owner capability.
parts = name.split('.')
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
class Config(callbacks.Privmsg):
def callCommand(self, method, irc, msg, *L):
try:
callbacks.Privmsg.callCommand(method, irc, msg, *L)
callbacks.Privmsg.callCommand(self, method, irc, msg, *L)
except InvalidRegistryName, e:
irc.error('%r is not a valid configuration variable.' % e)
irc.error('%r is not a valid configuration variable.' % e.args[0])
except registry.InvalidRegistryValue, e:
irc.error(str(e))
@ -81,8 +95,11 @@ class Config(callbacks.Privmsg):
name = privmsgs.getArgs(args)
group = getWrapper(name)
if hasattr(group, 'getValues'):
try:
L = zip(*group.getValues())[0]
irc.reply(utils.commaAndify(L))
except TypeError:
irc.error('There don\'t seem to be any values in %r' % name)
else:
irc.error('%r is not a valid configuration group.' % name)
@ -101,9 +118,13 @@ class Config(callbacks.Privmsg):
Sets the current value of the configuration variable <name> to <value>.
"""
(name, value) = privmsgs.getArgs(args, required=2)
capability = getCapability(name)
if ircdb.checkCapability(msg.prefix, capability):
wrapper = getWrapper(name)
wrapper.set(value)
irc.replySuccess()
else:
irc.errorNoCapability(capability)
def help(self, irc, msg, args):
"""<name>
@ -112,7 +133,7 @@ class Config(callbacks.Privmsg):
"""
name = privmsgs.getArgs(args)
wrapper = getWrapper(name)
irc.reply(msg, wrapper.help)
irc.reply(wrapper.help)
def reset(self, irc, msg, args):
"""<name>
@ -120,9 +141,13 @@ class Config(callbacks.Privmsg):
Resets the configuration variable <name> to its original value.
"""
name = privmsgs.getArgs(args)
capability = getCapability(name)
if ircdb.checkCapability(msg.prefix, capability):
wrapper = getWrapper(name)
wrapper.reset()
irc.replySuccess()
else:
irc.errorNoCapability(capability)
def default(self, irc, msg, args):
"""<name>

View File

@ -54,7 +54,7 @@ class Misc(callbacks.Privmsg):
priority = sys.maxint
def invalidCommand(self, irc, msg, tokens):
self.log.debug('Misc.invalidCommand called (tokens %s)', tokens)
if conf.replyWhenNotCommand:
if conf.supybot.reply.whenNotCommand():
command = tokens and tokens[0] or ''
irc.error('%r is not a valid command.' % command)
else:
@ -146,7 +146,7 @@ class Misc(callbacks.Privmsg):
cb = irc.getCallback(args[0])
if cb is not None:
command = callbacks.canonicalName(privmsgs.getArgs(args[1:]))
command = command.lstrip(conf.prefixChars)
command = command.lstrip(conf.supybot.prefixChars())
name = ' '.join(args)
if hasattr(cb, 'isCommand') and cb.isCommand(command):
method = getattr(cb, command)
@ -158,7 +158,7 @@ class Misc(callbacks.Privmsg):
return
command = callbacks.canonicalName(privmsgs.getArgs(args))
# Users might expect "@help @list" to work.
command = command.lstrip(conf.prefixChars)
command = command.lstrip(conf.supybot.prefixChars())
cbs = callbacks.findCallbackForCommand(irc, command)
if len(cbs) > 1:
tokens = [command]
@ -235,7 +235,7 @@ class Misc(callbacks.Privmsg):
except:
self.log.exception('Couldn\'t get id string: %r', s)
names = {}
dirs = map(os.path.abspath, conf.pluginDirs)
dirs = map(os.path.abspath, conf.supybot.directories.plugins())
for (name, module) in sys.modules.items(): # Don't use iteritems.
if hasattr(module, '__revision__'):
if 'supybot' in module.__file__:
@ -268,7 +268,8 @@ class Misc(callbacks.Privmsg):
return
filenameArg = os.path.basename(filenameArg)
ret = []
for (dirname, _, filenames) in os.walk(conf.logDir):
dirname = conf.supybot.directories.log()
for (dirname,_,filenames) in os.walk(dirname):
if filenameArg:
if filenameArg in filenames:
filename = os.path.join(dirname, filenameArg)
@ -284,13 +285,6 @@ class Misc(callbacks.Privmsg):
else:
irc.error('I couldn\'t find any logfiles.')
def getprefixchar(self, irc, msg, args):
"""takes no arguments
Returns the prefix character(s) the bot is currently using.
"""
irc.reply(repr(conf.prefixChars))
def plugin(self, irc, msg, args):
"""<command>

View File

@ -55,6 +55,7 @@ import irclib
import ircmsgs
import drivers
import privmsgs
import registry
import callbacks
class Deprecated(ImportError):
@ -63,7 +64,8 @@ class Deprecated(ImportError):
def loadPluginModule(name, ignoreDeprecation=False):
"""Loads (and returns) the module for the plugin with the given name."""
files = []
for dir in conf.pluginDirs:
pluginDirs = conf.supybot.directories.plugins()
for dir in pluginDirs:
try:
files.extend(os.listdir(dir))
except EnvironmentError: # OSError, IOError superclass.
@ -74,7 +76,7 @@ def loadPluginModule(name, ignoreDeprecation=False):
name = os.path.splitext(files[index])[0]
except ValueError: # We'd rather raise the ImportError, so we'll let go...
pass
moduleInfo = imp.find_module(name, conf.pluginDirs)
moduleInfo = imp.find_module(name, pluginDirs)
module = imp.load_module(name, *moduleInfo)
if 'deprecated' in module.__dict__ and module.deprecated:
if ignoreDeprecation:
@ -91,8 +93,14 @@ def loadPluginClass(irc, module):
callback = module.Class()
assert not irc.getCallback(callback.name())
irc.addCallback(callback)
if hasattr(callback, 'configure'):
callback.configure(irc)
def registerPlugin(name, currentValue=None):
conf.supybot.plugins.registerGroup(name,
registry.GroupWithValue(registry.Boolean(False, """Determines
whether this plugin is loaded by default.""")))
if currentValue is not None:
conf.supybot.plugins.getChild(name).setValue(currentValue)
class Owner(privmsgs.CapabilityCheckingPrivmsg):
# This plugin must be first; its priority must be lowest; otherwise odd
@ -107,6 +115,27 @@ class Owner(privmsgs.CapabilityCheckingPrivmsg):
'capabilities': 'User',
'addcapability': 'Admin',
'removecapability': 'Admin'}
for (name, s) in registry.cache.iteritems():
if name.startswith('supybot.plugins'):
try:
(_, _, name) = name.split('.')
except ValueError: #unpack list of wrong size.
continue
registerPlugin(name)
def do001(self, irc, msg):
self.log.info('Loading other src/ plugins.')
for s in ('Admin', 'Channel', 'Config', 'Misc', 'User'):
self.log.info('Loading %s.' % s)
m = loadPluginModule(s)
loadPluginClass(irc, m)
for (name, value) in conf.supybot.plugins.getValues():
if value():
s = rsplit(name, '.', 1)[-1]
if not irc.getCallback(s):
self.log.info('Loading %s.' % s)
m = loadPluginModule(s)
loadPluginClass(irc, m)
def disambiguate(self, irc, tokens, ambiguousCommands=None):
"""Disambiguates the given tokens based on the plugins loaded and
@ -227,7 +256,7 @@ class Owner(privmsgs.CapabilityCheckingPrivmsg):
except Exception, e:
irc.reply(utils.exnToString(e))
else:
irc.error(conf.replyEvalNotAllowed)
irc.error('You must enable conf.allowEval for that to work.')
def _exec(self, irc, msg, args):
"""<statement>
@ -242,77 +271,7 @@ class Owner(privmsgs.CapabilityCheckingPrivmsg):
except Exception, e:
irc.reply(utils.exnToString(e))
else:
irc.error(conf.replyEvalNotAllowed)
def setconf(self, irc, msg, args):
"""[<name> [<value>]]
Lists adjustable variables in the conf-module by default, shows the
variable type with only the <name> argument and sets the value of the
variable to <value> when both arguments are given.
"""
(name, value) = privmsgs.getArgs(args, required=0, optional=2)
if name and value:
if conf.allowEval:
try:
value = eval(value)
except Exception, e:
irc.error(utils.exnToString(e))
return
setattr(conf, name, value)
irc.replySuccess()
else:
if name == 'allowEval':
irc.error('You can\'t set the value of allowEval.')
return
elif name not in conf.types:
irc.error('I can\'t set that conf variable.')
return
else:
converter = conf.types[name]
try:
value = converter(value)
except ValueError, e:
irc.error(str(e))
return
setattr(conf, name, value)
irc.replySuccess()
elif name:
typeNames = {conf.mystr: 'string',
conf.mybool: 'boolean',
float: 'float'}
try:
type = typeNames[conf.types[name]]
except KeyError:
irc.error('That configuration variable doesn\'t exist.')
return
try:
value = getattr(conf, name)
irc.reply('%s is a %s (%s).' % (name, type, value))
except KeyError:
irc.error('%s is of an unknown type.' % name)
else:
options = conf.types.keys()
options.sort()
irc.reply(', '.join(options))
def setdefaultcapability(self, irc, msg, args):
"""<capability>
Sets the default capability to be allowed for any command.
"""
capability = callbacks.canonicalName(privmsgs.getArgs(args))
conf.defaultCapabilities.add(capability)
irc.replySuccess()
def unsetdefaultcapability(self, irc, msg, args):
"""<capability>
Unsets the default capability for any command.
"""
capability = callbacks.canonicalName(privmsgs.getArgs(args))
conf.defaultCapabilities.remove(capability)
irc.replySuccess()
irc.error('You must enable conf.allowEval for that to work.')
def ircquote(self, irc, msg, args):
"""<string to be sent to the server>
@ -357,9 +316,9 @@ class Owner(privmsgs.CapabilityCheckingPrivmsg):
"""[--deprecated] <plugin>
Loads the plugin <plugin> from any of the directories in
conf.pluginDirs; usually this includes the main installed directory
and 'plugins' in the current directory. --deprecated is necessary
if you wish to load deprecated plugins.
conf.supybot.directories.plugins; usually this includes the main
installed directory and 'plugins' in the current directory.
--deprecated is necessary if you wish to load deprecated plugins.
"""
(optlist, args) = getopt.getopt(args, '', ['deprecated'])
ignoreDeprecation = False
@ -385,6 +344,7 @@ class Owner(privmsgs.CapabilityCheckingPrivmsg):
irc.error(utils.exnToString(e))
return
loadPluginClass(irc, module)
registerPlugin(name, True)
irc.replySuccess()
def reload(self, irc, msg, args):
@ -429,6 +389,7 @@ class Owner(privmsgs.CapabilityCheckingPrivmsg):
callback.die()
del callback
gc.collect()
registerPlugin(name, False)
irc.replySuccess()
else:
irc.error('There was no callback %s' % name)
@ -436,8 +397,8 @@ class Owner(privmsgs.CapabilityCheckingPrivmsg):
def reconf(self, irc, msg, args):
"""takes no arguments
Reloads the configuration files in conf.dataDir: conf/users.conf and
conf/channels.conf, by default.
Reloads the configuration files for the user and channel databases:
conf/users.conf and conf/channels.conf, by default.
"""
ircdb.users.reload()
ircdb.channels.reload()

View File

@ -53,7 +53,7 @@ import callbacks
class User(callbacks.Privmsg):
def _checkNotChannel(self, irc, msg, password=' '):
if password and ircutils.isChannel(msg.args[0]):
raise callbacks.Error, conf.replyRequiresPrivacy
raise callbacks.Error, conf.supybot.replies.requiresPrivacy()
def list(self, irc, msg, args):
"""[<glob>]
@ -136,7 +136,7 @@ class User(callbacks.Privmsg):
ircdb.users.delUser(id)
irc.replySuccess()
else:
irc.error(conf.replyIncorrectAuth)
irc.error(conf.supybot.replies.incorrectAuthentication())
def changename(self, irc, msg, args):
"""<name> <new name> [<password>]
@ -201,7 +201,7 @@ class User(callbacks.Privmsg):
ircdb.users.setUser(id, user)
irc.replySuccess()
else:
irc.error(conf.replyIncorrectAuth)
irc.error(conf.supybot.replies.incorrectAuthentication())
return
def removehostmask(self, irc, msg, args):
@ -229,7 +229,7 @@ class User(callbacks.Privmsg):
ircdb.users.setUser(id, user)
irc.replySuccess()
else:
irc.error(conf.replyIncorrectAuth)
irc.error(conf.supybot.replies.incorrectAuthentication())
return
def setpassword(self, irc, msg, args):
@ -258,7 +258,7 @@ class User(callbacks.Privmsg):
ircdb.users.setUser(id, user)
irc.replySuccess()
else:
irc.error(conf.replyIncorrectAuth)
irc.error(conf.supybot.replies.incorrectAuthentication())
def username(self, irc, msg, args):
"""<hostmask|nick>
@ -345,7 +345,7 @@ class User(callbacks.Privmsg):
irc.error('Your secure flag is true and your hostmask '
'doesn\'t match any of your known hostmasks.')
else:
irc.error(conf.replyIncorrectAuth)
irc.error(conf.supybot.replies.incorrectAuthentication())
def unidentify(self, irc, msg, args):
"""takes no arguments
@ -403,7 +403,7 @@ class User(callbacks.Privmsg):
ircdb.users.setUser(id, user)
irc.reply('Secure flag set to %s' % value)
else:
irc.error(conf.replyIncorrectAuth)
irc.error(conf.supybot.replies.incorrectAuthentication())
Class = User

View File

@ -42,7 +42,6 @@ import asynchat
import log
import conf
import repl
import ircdb
import world
import drivers
@ -53,7 +52,7 @@ class AsyncoreRunnerDriver(drivers.IrcDriver):
def run(self):
log.debug(repr(asyncore.socket_map))
try:
asyncore.poll(conf.poll)
asyncore.poll(conf.supybot.drivers.poll())
except:
log.exception('Uncaught exception:')
@ -75,7 +74,8 @@ class AsyncoreDriver(asynchat.async_chat, object):
def scheduleReconnect(self):
when = time.time() + 60
whenS = time.strftime(conf.logTimestampFormat, time.localtime(when))
whenS = time.strftime(conf.supybot.log.timestampFormat(),
time.localtime(when))
if not world.dying:
log.info('Scheduling reconnect to %s at %s', self.server, whenS)
def makeNewDriver():
@ -121,116 +121,12 @@ class AsyncoreDriver(asynchat.async_chat, object):
log.info('Driver for %s dying.', self.irc)
self.close()
class ReplListener(asyncore.dispatcher, object):
def __init__(self, port=conf.telnetPort):
asyncore.dispatcher.__init__(self)
self.create_socket(socket.AF_INET, socket.SOCK_STREAM)
self.set_reuse_addr()
self.bind(('', port))
self.listen(5)
def handle_accept(self):
(sock, addr) = self.accept()
log.info('Connection made to telnet-REPL: %s', addr)
Repl((sock, addr))
class Repl(asynchat.async_chat, object):
filename = 'repl'
def __init__(self, (sock, addr)):
asynchat.async_chat.__init__(self, sock)
self.buffer = ''
self.prompt = """SupyBot version %s.
Python %s
Type disconnect() to disconnect.
Name: """ % (world.version, sys.version.translate(string.ascii, '\r\n'))
self.u = None
self.authed = False
self.set_terminator('\r\n')
self.repl = repl.Repl(addr[0])
self.repl.namespace['disconnect'] = self.close
self.push(self.prompt)
self.tries = 0
_re = re.compile(r'(?<!\r)\n')
def push(self, data):
asynchat.async_chat.push(self, self._re.sub('\r\n', data))
def collect_incoming_data(self, data):
if self.tries >= 3:
self.close()
self.buffer += data
if len(self.buffer) > 1024:
self.close()
def handle_close(self):
self.close()
def handle_error(self):
self.close()
def found_terminator(self):
if self.u is None:
try:
name = self.buffer
self.buffer = ''
id = ircdb.users.getUserId(name)
self.u = ircdb.users.getUser(id)
self.prompt = 'Password: '
except KeyError:
self.push('Unknown user.\n')
self.tries += 1
self.prompt = 'Name: '
log.warning('Unknown user %s on telnet REPL.', name)
self.push(self.prompt)
elif self.u is not None and not self.authed:
password = self.buffer
self.buffer = ''
if self.u.checkPassword(password):
if self.u.checkCapability('owner'):
self.authed = True
self.prompt = '>>> '
else:
self.push('Only owners can use this feature.\n')
self.close()
msg = 'Attempted non-owner user %s on telnet REPL' % name
log.warning(msg)
else:
self.push('Incorrect Password.\n')
self.prompt = 'Name: '
self.u = None
msg = 'Invalid password for user %s on telnet REPL.' % name
log.warning(msg)
self.push(self.prompt)
elif self.authed:
log.info('Telnet REPL: %s', self.buffer)
ret = self.repl.addLine(self.buffer+'\r\n')
self.buffer = ''
if ret is not repl.NotYet:
if ret is not None:
s = self._re.sub('\r\n', str(ret))
self.push(s)
self.push('\r\n')
self.prompt = '>>> '
else:
self.prompt = '... '
self.push(self.prompt)
try:
ignore(poller)
except NameError:
poller = AsyncoreRunnerDriver()
if conf.telnetEnable and __name__ != '__main__':
try:
ignore(_listener)
except NameError:
_listener = ReplListener()
Driver = AsyncoreDriver
if __name__ == '__main__':
ReplListener()
asyncore.loop()
# vim:set shiftwidth=4 tabstop=8 expandtab textwidth=78:

View File

@ -70,11 +70,11 @@ def addressed(nick, msg):
"""
nick = ircutils.toLower(nick)
if ircutils.nickEqual(msg.args[0], nick):
if msg.args[1][0] in conf.prefixChars:
if msg.args[1][0] in conf.supybot.prefixChars():
return msg.args[1][1:].strip()
else:
return msg.args[1].strip()
elif conf.replyWhenAddressedByNick and \
elif conf.supybot.reply.whenAddressedByNick() and \
ircutils.toLower(msg.args[1]).startswith(nick):
try:
(maybeNick, rest) = msg.args[1].split(None, 1)
@ -86,9 +86,9 @@ def addressed(nick, msg):
return ''
except ValueError: # split didn't work.
return ''
elif msg.args[1] and msg.args[1][0] in conf.prefixChars:
elif msg.args[1] and msg.args[1][0] in conf.supybot.prefixChars():
return msg.args[1][1:].strip()
elif conf.replyWhenNotAddressed:
elif conf.supybot.reply.whenNotAddressed():
return msg.args[1]
else:
return ''
@ -112,7 +112,7 @@ def reply(msg, s, prefixName=True, private=False, notice=False, to=None):
s = ircutils.safeArgument(s)
to = to or msg.nick
if ircutils.isChannel(msg.args[0]) and not private:
if notice or conf.replyWithPrivateNotice:
if notice or conf.supybot.reply.withPrivateNotice():
m = ircmsgs.notice(to, s)
elif prefixName:
m = ircmsgs.privmsg(msg.args[0], '%s: %s' % (to, s))
@ -227,7 +227,7 @@ def tokenize(s):
try:
if s != _lastTokenized:
_lastTokenized = s
if conf.enablePipeSyntax:
if conf.supybot.pipeSyntax():
tokens = '|'
else:
tokens = ''
@ -263,7 +263,7 @@ def formatArgumentError(method, name=None):
if name is None:
name = method.__name__
if hasattr(method, '__doc__') and method.__doc__:
if conf.showOnlySyntax:
if conf.supybot.showSimpleSyntax():
return getSyntax(method, name=name)
else:
return getHelp(method, name=name)
@ -281,7 +281,7 @@ def checkCommandCapability(msg, cb, command):
if ircdb.checkCapability(msg.prefix, antichancap):
log.info('Preventing because of antichancap: %s', msg.prefix)
return False
return conf.defaultAllow or \
return conf.supybot.defaultAllow() or \
ircdb.checkCapability(msg.prefix, command) or \
ircdb.checkCapability(msg.prefix, chancap)
@ -296,30 +296,38 @@ class RichReplyMethods(object):
return s
def replySuccess(self, s='', **kwargs):
self.reply(self.__makeReply(conf.replySuccess, s), **kwargs)
self.reply(self.__makeReply(conf.supybot.replies.success(), s),
**kwargs)
def replyError(self, s='', **kwargs):
self.reply(self.__makeReply(conf.replyError, s), **kwargs)
self.reply(self.__makeReply(conf.supybot.replies.error(), s),
**kwargs)
def errorNoCapability(self, capability, s='', **kwargs):
log.warning('Denying %s for lacking %r capability',
self.msg.prefix, capability)
s = self.__makeReply(conf.replyNoCapability % capability, s)
noCapability = conf.supybot.replies.noCapability()
s = self.__makeReply(noCapability % capability, s)
self.error(s, **kwargs)
def errorPossibleBug(self, s='', **kwargs):
if s:
s += ' (%s)' % conf.replyPossibleBug
s += ' (%s)' % conf.supybot.replies.possibleBug()
else:
s = conf.supybot.replies.possibleBug()
self.error(s, **kwargs)
def errorNotRegistered(self, s='', **kwargs):
self.error(self.__makeReply(conf.replyNotRegistered, s), **kwargs)
notRegistered = conf.supybot.replies.notRegistered()
self.error(self.__makeReply(notRegistered, s), **kwargs)
def errorNoUser(self, s='', **kwargs):
self.error(self.__makeReply(conf.replyNoUser, s), **kwargs)
noUser = conf.supybot.replies.noUser()
self.error(self.__makeReply(noUser, s), **kwargs)
def errorRequiresPrivacy(self, s='', **kwargs):
self.error(self.__makeReply(conf.replyRequiresPrivacy, s), **kwargs)
requiresPrivacy = conf.supybot.replies.requiresPrivacy()
self.error(self.__makeReply(requiresPrivacy, s), **kwargs)
class IrcObjectProxy(RichReplyMethods):
@ -337,7 +345,7 @@ class IrcObjectProxy(RichReplyMethods):
self.notice = False
self.private = False
self.finished = False
self.prefixName = conf.replyWithNickPrefix
self.prefixName = conf.supybot.reply.withNickPrefix()
self.noLengthCheck = False
if not args:
self.finalEvaled = True
@ -462,18 +470,18 @@ class IrcObjectProxy(RichReplyMethods):
to=<nick|channel>: The nick or channel the reply should go to.
Defaults to msg.args[0] (or msg.nick if private)
"""
# These use |= or &= based on whether or not they default to True or
# False. Those that default to True use &=; those that default to
# False use |=.
# These use and or or based on whether or not they default to True or
# False. Those that default to True use and; those that default to
# False use or.
assert not isinstance(s, ircmsgs.IrcMsg), \
'Old code alert: there is no longer a "msg" argument to reply.'
msg = self.msg
self.action |= action
self.notice |= notice
self.private |= private
self.action = action or self.action
self.notice = notice or self.notice
self.private = private or self.private
self.to = to or self.to
self.prefixName &= prefixName
self.noLengthCheck |= noLengthCheck
self.prefixName = prefixName or self.prefixName
self.noLengthCheck = noLengthCheck or self.noLengthCheck
if self.finalEvaled:
if isinstance(self.irc, self.__class__):
self.irc.reply(s, self.noLengthCheck, self.prefixName,
@ -532,7 +540,7 @@ class IrcObjectProxy(RichReplyMethods):
self.irc.error(s, private)
else:
s = 'Error: ' + s
if private or conf.errorReplyPrivate:
if private or conf.supybot.reply.errorInPrivate():
self.irc.queueMsg(ircmsgs.privmsg(self.msg.nick, s))
else:
self.irc.queueMsg(reply(self.msg, s))
@ -655,22 +663,6 @@ class Privmsg(irclib.IrcCallback):
dispatcher.__doc__ = docstring
setattr(self.__class__, canonicalname, dispatcher)
def configure(self, irc):
fakeIrc = ConfigIrcProxy(irc)
for args in conf.commandsOnStart:
args = args[:]
command = canonicalName(args.pop(0))
if self.isCommand(command):
self.log.debug('%s: %r', command, args)
method = getattr(self, command)
line = '%s %s' % (command, ' '.join(imap(utils.dqrepr, args)))
msg = ircmsgs.privmsg(fakeIrc.nick, line, fakeIrc.prefix)
try:
world.startup = True
method(fakeIrc, msg, args)
finally:
world.startup = False
def __call__(self, irc, msg):
if msg.command == 'PRIVMSG':
if self.noIgnore or not ircdb.checkIgnored(msg.prefix,msg.args[0]):

View File

@ -35,307 +35,308 @@ import fix
import os
import sys
import string
import sets
import os.path
import logging
import utils
import registry
import ircutils
###
# Directions:
#
# Boolean values should be either True or False.
###
###
# Directories.
###
logDir = 'logs'
confDir = 'conf'
dataDir = 'data'
installDir = os.path.dirname(os.path.dirname(sys.modules[__name__].__file__))
pluginDirs = [os.path.join(installDir, s) for s in ('src', 'plugins')]
###
# Files.
###
userfile = 'users.conf'
channelfile = 'channels.conf'
###
# minimumLogPriority: The minimum priority that will be logged. Defaults to
# logging.INFO, which is probably a good value. Can also
# be usefully set to logging.{DEBUG,WARNING,ERROR,CRITICAL}
###
minimumLogPriority = logging.INFO
###
# stdoutLogging: Determines whether or not the bot logs to stdout.
###
stdoutLogging = True
###
# colorizedStdoutLogging: Determines whether or not the bot logs colored logs
# to stdout.
###
colorizedStdoutLogging = True
###
# logTimestampFormat: A format string defining how timestamps should be. Check
# the Python library reference for the "time" module to see
# what the various format specifiers mean.
###
logTimestampFormat = '[%d-%b-%Y %H:%M:%S]'
###
# humanTimestampFormat: A format string defining how timestamps should be
# formatted for human consumption. Check the Python
# library reference for the "time" module to see what the
# various format specifiers mean.
###
humanTimestampFormat = '%I:%M %p, %B %d, %Y'
###
# externalIP: A string that is the external IP of the bot. If this is None,
# the bot will attempt to find out its IP dynamically (though
# sometimes this doesn't work.)
###
externalIP = None
###
# throttleTime: A floating point number of seconds to throttle queued messages.
# (i.e., messages will not be sent faster than once per
# throttleTime units.)
###
throttleTime = 1.0
###
# snarfThrottle: A floating point number of seconds to throttle snarfed URLs,
# in order to prevent loops between two bots.
###
snarfThrottle = 10.0
_srcDir = os.path.join(installDir, 'src')
_pluginsDir = os.path.join(installDir, 'plugins')
###
# allowEval: True if the owner (and only the owner) should be able to eval
# arbitrary Python code.
# arbitrary Python code. This is specifically *not* a registry
# variable because it shouldn't be modifiable in the bot.
###
allowEval = False
###
# replyWhenNotCommand: True if you want the bot reply when someone apparently
# addresses him but there is no command. Otherwise he'll
# just remain silent.
###
replyWhenNotCommand = True
supybot = registry.Group()
supybot.setName('supybot')
supybot.registerGroup('plugins') # This will be used by plugins, but not here.
class ValidNick(registry.String):
def set(self, s):
original = getattr(self, 'value', self.default)
registry.String.set(self, s)
if not ircutils.isNick(self.value):
self.value = original
raise registry.InvalidRegistryValue, 'Value must be a valid nick.'
supybot.register('nick', ValidNick('supybot',
"""Determines the bot's nick."""))
supybot.register('ident', ValidNick('supybot',
"""Determines the bot's ident."""))
supybot.register('user', registry.String('supybot', """Determines the user
the bot sends to the server."""))
supybot.register('password', registry.String('', """Determines the password to
be sent to the server if it requires one."""))
# TODO: Make this check for validity.
supybot.register('server', registry.String('irc.freenode.net', """Determines
what server the bot connects to."""))
supybot.register('channels', registry.CommaSeparatedListOfStrings('#supybot',
"""Determines what channels the bot will join when it connects to the server.
"""))
supybot.registerGroup('databases')
supybot.databases.registerGroup('users')
supybot.databases.registerGroup('channels')
supybot.databases.users.register('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."""))
supybot.databases.channels.register('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."""))
supybot.registerGroup('directories')
supybot.directories.register('conf', registry.String('conf', """
Determines what directory configuration data is put into."""))
supybot.directories.register('data', registry.String('data', """
Determines what directory data is put into."""))
supybot.directories.register('plugins',
registry.CommaSeparatedListOfStrings(['plugins',_srcDir,_pluginsDir],
"""Determines what directories the bot will look for plugins in."""))
supybot.register('humanTimestampFormat', registry.String('%I:%M %p, %B %d, %Y',
"""Determines how timestamps printed for human reading should be formatted.
Refer to the Python documentation for the time module to see valid formatting
characteres for time formats."""))
class IP(registry.String):
def set(self, s):
original = getattr(self, 'value', self.default)
registry.String.set(self, s)
if self.value: # Empty string is alright.
if not (utils.isIP(self.value) or utils.isIPV6(self.value)):
raise registry.InvalidRegistryValue, \
'Value must be a valid IP.'
supybot.register('externalIP', IP('', """A string that is the external IP of
the bot. If this is the empty string, the bot will attempt to find out its IP
dynamically (though sometimes that doesn't work, hence this variable)."""))
# XXX Should this (and a few others) be made into a group 'network' or
# 'server' or something?
supybot.register('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."""))
supybot.register('snarfThrottle', registry.Float(10.0, """A floating point
number of seconds to throttle snarfed URLs, in order to prevent loops between
two bots snarfing the same URLs and having the snarfed URL in the output of
the snarf message."""))
###
# replyWithPrivateNotice: True if replies to a user in a channel should be
# noticed to that user instead of sent to the channel
# itself.
# Reply/error tweaking.
###
replyWithPrivateNotice = False
# TODO: These should probably all be channel-specific.
supybot.registerGroup('reply')
supybot.reply.register('errorInPrivate', registry.Boolean(False, """
Determines whether the bot will send error messages to users in private."""))
supybot.reply.register('whenNotCommand', registry.Boolean(True, """
Determines whether the bot will reply with an error message when it is
addressed but not given a valid command. If this value is False, the bot
will remain silent."""))
supybot.reply.register('withPrivateNotice', registry.Boolean(False, """
Determines whether the bot will reply with a private notice to users rather
than sending a message to a channel. Private notices are particularly nice
because they don't generally cause IRC clients to open a new query window."""))
supybot.reply.register('withNickPrefix', registry.Boolean(True, """
Determines whether the bot will always prefix the user's nick to its reply to
that user's command."""))
supybot.reply.register('whenAddressedByNick', registry.Boolean(True, """
Determines whether the bot will reply when people address it by its nick,
rather than with a prefix character."""))
supybot.reply.register('whenNotAddressed', registry.Boolean(False, """
Determines whether the bot should attempt to reply to all messages even if they
don't address it (either via its nick or a prefix character). If you set this
to True, you almost certainly want to set supybot.reply.whenNotCommand to
False."""))
# XXX: Removed requireRegistration: it wasn't being used.
supybot.reply.register('requireChannelCommandsToBeSentInChannel',
registry.Boolean(False, """Determines whether the bot will allow you to send
channel-related commands outside of that channel. Sometimes people find it
confusing if a channel-related command (like Filter.outfilter) changes the
behavior of the channel but was sent outside the channel itself."""))
supybot.register('followIdentificationThroughNickChanges',
registry.Boolean(False, """Determines whether the bot will unidentify someone
when that person changes his or her nick. Setting this to True will cause the
bot to track such changes. It defaults to false for a little greater security.
"""))
supybot.register('alwaysJoinOnInvite', registry.Boolean(False, """Determines
whether the bot will always join a channel when it's invited. If this value
is False, the bot will only join a channel if the user inviting it has the
'admin' capability (or if it's explicitly told to join the channel using the
Admin.join command)"""))
supybot.register('pipeSyntax', registry.Boolean(False, """Supybot allows
nested commands; generally, commands are nested via square brackets. Supybot
can also provide a syntax more similar to UNIX pipes. The square bracket
nesting syntax is always enabled, but when this value is True, users can also
nest commands by saying 'bot: foo | bar' instead of 'bot: bar [foo]'."""))
supybot.register('showSimpleSyntax', registry.Boolean(False, """Supybot
normally replies with the full help whenever a user misuses a command. If this
value is set to True, the bot will only reply with the syntax of the command
(the first line of the docstring) rather than the full help."""))
supybot.register('defaultCapabilities',
registry.CommaSeparatedSetOfStrings(['-owner', '-admin', '-trusted'], """
These are the capabilities that are given to everyone by default. If they are
normal capabilities, then the user will have to have the appropriate
anti-capability if you want to override these capabilities; if they are
anti-capabilities, then the user will have to have the actual capability to
override these capabilities. See docs/CAPABILITIES if you don't understand
why these default to what they do."""))
###
# replyWithNickPrefix: True if the bot should always prefix the nick of the
# person giving the command to its reply.
# Replies
###
replyWithNickPrefix = True
# TODO: These should be channel-specific.
supybot.registerGroup('replies')
supybot.replies.register('error', registry.NormalizedString("""An error has
occurred and has been logged. Please contact this bot's administrator for more
information.""", """Determines what error message the bot gives when it wants
to be ambiguous."""))
supybot.replies.register('noCapability', registry.NormalizedString("""You
don\'t have the %r capability. If you think that you should have this
capability, be sure that you are identified before trying again. The 'whoami'
command can tell you if you're identified.""", """Determines what error message
is given when the bot is telling someone they aren't cool enough to use the
command they tried to use."""))
supybot.replies.register('success', registry.NormalizedString("""The operation
succeeded.""", """Determines what message the bot replies with when a command
succeeded."""))
supybot.replies.register('incorrectAuthentication',
registry.NormalizedString("""Your hostmask doesn't match or your password is
wrong.""", """Determines what message the bot replies wiwth when someone tries
to use a command that requires being identified or having a password and
neither credential is correct."""))
supybot.replies.register('noUser', registry.NormalizedString("""I can't find
that user in my user database.""", """Determines what error message the bot
replies with when someone tries to accessing some information on a user the
bot doesn't know about."""))
supybot.replies.register('notRegistered', registry.NormalizedString("""
You must be registered to use this command. If you are already registered, you
must either identify (using the identify command) or add a hostmask matching
your current hostmask (using the addhostmask command).""", """Determines what
error message the bot replies with when someone tries to do something that
requires them to be registered but they're not currently recognized."""))
# XXX: removed replyInvalidArgument.
supybot.replies.register('requiresPrivacy', registry.NormalizedString("""
That operation cannot be done in a channel.""", """Determines what error
messages the bot sends to people who try to do things in a channel that really
should be done in private."""))
supybot.replies.register('possibleBug', registry.NormalizedString("""This may
be a bug. If you think it is, please file a bug report at
<http://sourceforge.net/tracker/?func=add&group_id=58965&atid=489447>.""",
"""Determines what message the bot sends when it thinks you've encountered a
bug that the developers don't know about."""))
supybot.register('pingServer', 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."""))
supybot.register('pingInterval', registry.Integer(120, """Determines the
number of seconds between sending pings to the server, if pings are being sent
to the server."""))
supybot.register('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."""))
supybot.register('nickmods', registry.CommaSeparatedListOfStrings(
'__%s__,%s^,%s`,%s_,%s__,_%s,__%s,[%s]'.split(','),
"""A list of modifications to be made to a nick when the nick the bot tries
to get from the server is in use. There should be one %s in each string;
this will get replaced with the original nick."""))
supybot.register('defaultAllow', registry.Boolean(True, """Determines whether
the bot by default will allow users to run commands. If this is disabled, a
user will have to have the capability for whatever command he wishes to run.
"""))
supybot.register('defaultIgnore', registry.Boolean(False, """Determines
whether the bot will ignore unregistered users by default. Of course, that'll
make it particularly hard for those users to register with the bot, but that's
your problem to solve."""))
supybot.register('ignores', registry.CommaSeparatedListOfStrings('', """
A list of hostmasks ignored by the bot. Add people you don't like to here.
"""))
class ValidPrefixChars(registry.String):
def set(self, s):
registry.String.set(self, s)
if self.value.translate(string.ascii,
'`~!@#$%^&*()_-+=[{}]\\|\'";:,<.>/?'):
raise registry.InvalidRegistryValue, \
'Value must contain only ~!@#$%^&*()_-+=[{}]\\|\'";:,<.>/?'
supybot.register('prefixChars', ValidPrefixChars('@', """Determines what prefix
characters the bot will reply to. A prefix character is a single character
that the bot will use to determine what messages are addressed to it; when
there are no prefix characters set, it just uses its nick."""))
###
# replyWhenAddressedByNick: True if the bot should reply to messages of the
# form "botnick: foo" where "botnick" is the bot's
# nick.
# Driver stuff.
###
replyWhenAddressedByNick = True
supybot.registerGroup('drivers')
supybot.drivers.register('poll', registry.Float(1.0, """Determines the default
length of time a driver should block waiting for input."""))
###
# replyWhenNotAddressed: True if the bot should reply to messages even if they
# don't address it at all. If you have this on, you'll
# almost certainly want to make sure replyWhenNotCommand
# is turned off.
###
replyWhenNotAddressed = False
class ValidDriverModule(registry.String):
def set(self, s):
original = getattr(self, 'value', self.default)
registry.String.set(self, s)
if self.value not in ('socketDrivers',
'twistedDrivers',
'asyncoreDrivers'):
self.value = original
raise registry.InvalidRegistryValue, \
'Value must be one of "socketDrivers", "asyncoreDrivers", ' \
'or twistedDrivers.'
else:
# TODO: check to make sure Twisted is available if it's set to
# twistedDrivers.
pass
###
# requireRegistration: Oftentimes a plugin will want to record who added or
# changed or messed with it last. Supybot's user database
# is an excellent way to determine who exactly someone is.
# You may, however, want something a little less
# "intrustive," so you can set this variable to False to
# tell such plugins that they should use the hostmask when
# the user isn't registered with the user database.
###
requireRegistration = False
###
# requireChannelCommandsToBeSentInChannel: Normally, you can send channel
# related commands in private or in
# another channel. Sometimes this
# can be confusing, though, if the
# command changes the behavior of
# the bot in the channel. Set this
# variable to True if you want to
# require such commands to be sent
# in the channel to which they apply.
###
requireChannelCommandsToBeSentInChannel = False
###
# followIdentificationThroughNickChanges: By default the bot will simply
# unidentify someone when he changes
# his nick. Setting this to True will
# cause the bot to track such changes.
###
followIdentificationThroughNickChanges = False
###
# alwaysJoinOnInvite: Causes the bot to always join a channel when it's
# invited. Defaults to False, in which case the bot will
# only join if the user inviting it has the 'admin'
# capability.
###
alwaysJoinOnInvite = False
###
# enablePipeSyntax: Supybot allows nested commands; generally, commands are
# nested via [square brackets]. Supybot can also use a
# syntax more similar to Unix pipes. What would be (and
# still can be; the pipe syntax doesn't disable the bracket
# syntax) "bot: bar [foo]" can now by "bot: foo | bar"
# This variable enables such syntax.
###
enablePipeSyntax = False
###
# showOnlySyntax : Supybot normally returns the full help whenever a user
# misuses a command. If this option is set to True, the bot
# will only return the syntax of the command (the first line
# of the docstring) rather than the full help.
###
showOnlySyntax = False
###
# defaultCapabilities: Capabilities allowed to everyone by default. You almost
# certainly want to have !owner and !admin in here.
###
defaultCapabilities = sets.Set(['-owner', '-admin', '-trusted'])
###
# reply%s: Stock replies for various reasons.
###
replyError = 'An error has occurred and has been logged. ' \
'Please contact this bot\'s administrator for more information.'
replyNoCapability = 'You don\'t have the "%s" capability. If you think ' \
'that you should have this capability, be sure that ' \
'you are identified via the "whoami" command.'
replySuccess = 'The operation succeeded.'
replyIncorrectAuth = 'Your hostmask doesn\'t match or your password is wrong.'
replyNoUser = 'I can\'t find that user in my database.'
replyNotRegistered = 'You must be registered to use this command. ' \
'If you are already registered, you must either ' \
'identify (using the identify command) or add a ' \
'hostmask matching your current hostmask (using ' \
'the addhostmask command).'
replyInvalidArgument = 'I can\'t send \\r, \\n, or \\0 (\\x00).'
replyRequiresPrivacy = 'That can\'t be done in a channel.'
replyEvalNotAllowed = 'You must enable conf.allowEval for that to work.'
replyPossibleBug = 'This may be a bug. If you think it is, please file a bug '\
'report at <http://sourceforge.net/tracker/?' \
'func=add&group_id=58965&atid=489447>'
###
# errorReplyPrivate: True if errors should be reported privately so as not to
# bother the channel.
###
errorReplyPrivate = False
###
# telnetEnable: A boolean saying whether or not to enable the telnet REPL.
# This will allow a user with the 'owner' capability to telnet
# into the bot and see how it's working internally. A lifesaver
# for development.
###
telnetEnable = False
telnetPort = 31337
###
# poll: the length of a polling term.
# If asyncore drivers are all you're using, feel free to make
# this arbitrarily large -- be warned, however, that all other
# drivers are just sitting around while asyncore waits during
# this poll period (including the schedule). It'll take more
# CPU, but you probably don't want to set this more than 0.01
# when you've got non-asyncore drivers to worry about.
###
poll = 1
###
# pingServer: Determines whether the bot will send PINGs to the server it's
# connected to in order to keep the connection alive. Sometimes
# this seems to result in instability.
###
pingServer = True
###
# maxHistory: Maximum number of messages kept in an Irc object's state.
###
maxHistory = 1000
###
# pingInterval: Number of seconds between PINGs to the server.
# 0 means not to ping the server.
###
pingInterval = 120
###
# nickmods: List of ways to 'spice up' a nick so the bot doesn't run out of
# nicks if all his normal ones are taken.
###
nickmods = ['%s^', '^%s^', '__%s__', '%s_', '%s__', '__%s', '^^%s^^', '{%s}',
'[%s]', '][%s][', '}{%s}{', '}{}%s', '^_^%s', '%s^_^', '^_^%s^_^']
###
# defaultAllow: Are commands allowed by default?
###
defaultAllow = True
###
# defaultIgnore: True if users should be ignored by default.
# It's a really easy way to make sure that people who want to
# talk to the bot register first. (Of course, they can't
# register if they're ignored. We'll work on that.)
###
defaultIgnore = False
###
# ignores: Hostmasks to ignore.
###
ignores = []
###
# prefixChars: A string of chars that are valid prefixes to address the bot.
###
prefixChars = '@'
###
# validPrefixChars: A string of chars that are allowed to be used as
# prefixChars.
###
validPrefixChars = '`~!@#$%^&*()_-+=[{}]\\|\'";:,<.>/?'
###
# detailedTracebacks: A boolean describing whether or not the bot will give
# *extremely* detailed tracebacks. Be cautioned, this eats
# a lot of log file space.
###
detailedTracebacks = True
###
# driverModule: A string that is the module where the default driver for the
# bot will be found.
###
driverModule = 'socketDrivers'
#driverModule = 'asyncoreDrivers'
#driverModule = 'twistedDrivers'
supybot.drivers.register('module', ValidDriverModule('socketDrivers', """
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."""))
###############################
###############################
@ -346,71 +347,4 @@ driverModule = 'socketDrivers'
###############################
version ='0.76.1'
commandsOnStart = []
# This is a dictionary mapping names to converter functions for use in the
# Owner.setconf command.
def mybool(s):
"""Converts a string read from the user into a bool, fuzzily."""
if s.capitalize() == 'False' or s == '0':
return False
elif s.capitalize() == 'True' or s == '1':
return True
else:
raise ValueError, 'invalid literal for mybool()'
def mystr(s):
"""Converts a string read from the user into a real string."""
while s and s[0] in "'\"" and s[0] == s[-1]:
s = s[1:-1]
return s
types = {
'logDir': mystr,
'confDir': mystr,
'dataDir': mystr,
#'pluginDirs': (list, str),
'userfile': mystr,
'channelfile': mystr,
'logTimestampFormat': mystr,
'humanTimestampFormat': mystr,
'throttleTime': float,
'snarfThrottle': float,
#'allowEval': mybool,
'replyWhenNotCommand': mybool,
'replyWithPrivateNotice': mybool,
'replyWithNickPrefix': mybool,
'replyWhenAddressedByNick': mybool,
'requireRegistration': mybool,
'enablePipeSyntax': mybool,
'replyError': mystr,
'replyNoCapability': mystr,
'replySuccess': mystr,
'replyIncorrectAuth': mystr,
'replyNoUser': mystr,
'replyNotRegistered': mystr,
'replyInvalidArgument': mystr,
'replyRequiresPrivacy': mystr,
'replyEvalNotAllowed': mystr,
'errorReplyPrivate': mybool,
#'telnetEnable': mybool,
#'telnetPort': int,
'poll': float,
#'maxHistory': int,
'pingInterval': float,
#'nickmods': (list, str),
'defaultAllow': mybool,
'defaultIgnore': mybool,
#'ignores': (list, str),
'prefixChars': mystr,
'detailedTracebacks': mybool,
'driverModule': mystr,
'showOnlySyntax': mybool,
'pingServer': mybool,
'followIdentificationThroughNickChanges': mybool
}
if os.name == 'nt':
colorizedStdoutLogging = False
# vim:set shiftwidth=4 tabstop=8 expandtab textwidth=78:

View File

@ -210,7 +210,8 @@ class Mixin(object):
"""
def __init__(self):
className = self.__class__.__name__
self.filename = os.path.join(conf.confDir, '%s-configurable'%className)
self.filename = os.path.join(conf.supybot.directories.conf(),
'%s-configurable'%className)
if os.path.exists(self.filename):
fd = file(self.filename)
for line in fd:
@ -258,7 +259,7 @@ class Mixin(object):
flushDictionary(self.configurables)
fd.close()
def config(self, irc, msg, args):
def configurableishnessify(self, irc, msg, args):
"""[<channel>] [<name>] [<value>]
Sets the value of config variable <name> to <value> on <channel>. If

View File

@ -148,8 +148,11 @@ def run():
del _drivers[name]
_drivers[name] = driver
def newDriver(server, irc, moduleName=conf.driverModule):
"""Returns a new driver for the given server using conf.driverModule."""
def newDriver(server, irc, moduleName=None):
"""Returns a new driver for the given server using the irc given and using
conf.supybot.driverModule to determine what driver to pick."""
if moduleName is None:
moduleName = conf.supybot.drivers.module()
driver = __import__(moduleName).Driver(server, irc)
irc.driver = driver
return driver

View File

@ -556,8 +556,11 @@ class ChannelsDictionary(utils.IterableMap):
###
# Later, I might add some special handling for botnet.
###
users = UsersDB(os.path.join(conf.confDir, conf.userfile))
channels = ChannelsDictionary(os.path.join(conf.confDir, conf.channelfile))
confDir = conf.supybot.directories.conf()
users = UsersDB(os.path.join(confDir,
conf.supybot.databases.users.filename()))
channels = ChannelsDictionary(os.path.join(confDir,
conf.supybot.databases.channels.filename()))
###
# Useful functions for checking credentials.
@ -567,9 +570,9 @@ def checkIgnored(hostmask, recipient='', users=users, channels=channels):
Checks if the user is ignored by the recipient of the message.
"""
for ignore in conf.ignores:
for ignore in conf.supybot.ignores():
if ircutils.hostmaskPatternEqual(ignore, hostmask):
log.info('Ignoring %s due to conf.ignores.', hostmask)
log.info('Ignoring %s due to conf.supybot.ignores.', hostmask)
return True
try:
id = users.getUserId(hostmask)
@ -584,8 +587,9 @@ def checkIgnored(hostmask, recipient='', users=users, channels=channels):
else:
return False
else:
if conf.defaultIgnore:
log.info('Ignoring %s due to conf.defaultIgnore', hostmask)
if conf.supybot.defaultIgnore():
log.info('Ignoring %s due to conf.supybot.defaultIgnore',
hostmask)
return True
else:
return False
@ -625,17 +629,17 @@ def _checkCapabilityForUnknownUser(capability, users=users, channels=channels):
return _x(capability, c.defaultAllow)
except KeyError:
pass
if capability in conf.defaultCapabilities:
if capability in conf.supybot.defaultCapabilities():
return True
elif invertCapability(capability) in conf.defaultCapabilities:
elif invertCapability(capability) in conf.supybot.defaultCapabilities():
return False
else:
return _x(capability, conf.defaultAllow)
return _x(capability, conf.supybot.defaultAllow())
def checkCapability(hostmask, capability, users=users, channels=channels):
"""Checks that the user specified by name/hostmask has the capabilty given.
"""
if world.startup:
if world.testing:
return _x(capability, True)
try:
u = users.getUser(hostmask)
@ -666,12 +670,13 @@ def checkCapability(hostmask, capability, users=users, channels=channels):
return c.checkCapability(capability)
else:
return _x(capability, c.defaultAllow)
if capability in conf.defaultCapabilities:
defaultCapabilities = conf.supybot.defaultCapabilities()
if capability in defaultCapabilities:
return True
elif invertCapability(capability) in conf.defaultCapabilities:
elif invertCapability(capability) in defaultCapabilities:
return False
else:
return _x(capability, conf.defaultAllow)
return _x(capability, conf.supybot.defaultAllow())
def checkCapabilities(hostmask, capabilities, requireAll=False):

View File

@ -148,7 +148,6 @@ class IrcMsgQueue(object):
def enqueue(self, msg):
"""Enqueues a given message."""
if msg in self.msgs:
if not world.startup:
log.info('Not adding msg %s to queue' % msg)
else:
self.msgs.add(msg)
@ -257,7 +256,7 @@ class IrcState(IrcCommandDispatcher):
"""
__slots__ = ('history', 'nicksToHostmasks', 'channels')
def __init__(self):
self.history = RingBuffer(conf.maxHistory)
self.history = RingBuffer(conf.supybot.maxHistoryLength())
self.reset()
def reset(self):
@ -407,7 +406,7 @@ class Irc(IrcCommandDispatcher):
world.ircs.append(self)
self.originalNick = intern(nick)
self.nick = self.originalNick
self.nickmods = cycle(conf.nickmods)
self.nickmods = cycle(conf.supybot.nickmods())
self.password = password
self.user = intern(user or nick) # Default to nick
self.ident = intern(ident or nick) # Ditto.
@ -486,13 +485,15 @@ class Irc(IrcCommandDispatcher):
if self.fastqueue:
msg = self.fastqueue.dequeue()
elif self.queue:
if not world.testing and now - self.lastTake <= conf.throttleTime:
if not world.testing and now - self.lastTake <= \
conf.supybot.throttleTime():
log.debug('Irc.takeMsg throttling.')
else:
self.lastTake = now
msg = self.queue.dequeue()
elif conf.pingServer and \
now > (self.lastping + conf.pingInterval) and self.afterConnect:
elif conf.supybot.pingServer() and \
now > (self.lastping + conf.supybot.pingInterval()) and \
self.afterConnect:
if self.outstandingPing:
s = 'Reconnecting to %s, ping not replied to.' % self.server
log.warning(s)
@ -525,16 +526,6 @@ class Irc(IrcCommandDispatcher):
msg._len = len(str(msg))
self.state.addMsg(self, msg)
log.debug('Outgoing message: ' + str(msg).rstrip('\r\n'))
if msg.command == 'NICK':
# We don't want a race condition where the server's NICK
# back to us is lost and someone else steals our nick and uses
# it to abuse our 'owner' power we give to ourselves. Ergo, on
# outgoing messages that change our nick, we pre-emptively
# delete the 'owner' user we setup for ourselves.
user = ircdb.users.getUser(0)
user.unsetAuth()
user.hostmasks = []
ircdb.users.setUser(0, user)
return msg
else:
return None
@ -542,7 +533,6 @@ class Irc(IrcCommandDispatcher):
def do001(self, msg):
"""Does some logging."""
log.info('Received 001 from the server.')
log.info('Hostmasks of user 0: %r', ircdb.users.getUser(0).hostmasks)
def do002(self, msg):
"""Logs the ircd version."""
@ -577,21 +567,11 @@ class Irc(IrcCommandDispatcher):
"""Handles NICK messages."""
if msg.nick == self.nick:
newNick = intern(msg.args[0])
user = ircdb.users.getUser(0)
user.unsetAuth()
user.hostmasks = []
try:
ircdb.users.getUser(newNick)
log.error('User already registered with name %s' % newNick)
except KeyError:
user.name = newNick
ircdb.users.setUser(0, user)
self.nick = newNick
(nick, user, domain) = ircutils.splitHostmask(msg.prefix)
self.prefix = ircutils.joinHostmask(self.nick, user, domain)
self.prefix = intern(self.prefix)
log.info('Changing user 0 hostmask to %r' % self.prefix)
elif conf.followIdentificationThroughNickChanges:
elif conf.supybot.followIdentificationThroughNickChanges():
# We use elif here because this means it's someone else's nick
# change, not our own.
try:
@ -620,13 +600,7 @@ class Irc(IrcCommandDispatcher):
# This catches cases where we know our own nick (from sending it to the
# server) but we don't yet know our prefix.
if msg.nick == self.nick and self.prefix != msg.prefix:
log.info('Updating user 0 prefix: %r' % msg.prefix)
self.prefix = msg.prefix
user = ircdb.users.getUser(0)
user.hostmasks = []
user.name = self.nick
user.addHostmask(msg.prefix)
ircdb.users.setUser(0, user)
# This keeps our nick and server attributes updated.
if msg.command in self._nickSetters:

View File

@ -42,13 +42,57 @@ import logging
import ansi
import conf
import registry
class LogLevel(registry.Value):
def set(self, s):
s = s.upper()
try:
self.value = getattr(logging, s)
except AttributeError:
s = 'Invalid log level: should be one of ' \
'DEBUG, INFO, WARNING, ERROR, or CRITICAL.'
raise registry.InvalidRegistryValue, s
def __str__(self):
return logging.getLevelName(self.value)
conf.supybot.directories.register('log', registry.String('logs', """Determines
what directory the bot will store its logfiles in."""))
conf.supybot.registerGroup('log')
conf.supybot.log.register('minimumPriority', LogLevel(logging.INFO,
"""Determines what the minimum priority logged will be. Valid values are
DEBUG, INFO, WARNING, ERROR, and CRITICAL, in order of increasing
priority."""))
conf.supybot.log.register('timestampFormat',
registry.String('[%d-%b-%Y %H:%M:%S]',
"""Determines the format string for timestamps in logfiles. Refer to the
Python documentation for the time module to see what formats are accepted."""))
conf.supybot.log.register('detailedTracebacks', registry.Boolean(True, """
Determines whether highly detailed tracebacks will be logged. While more
informative (and thus more useful for debugging) they also take a significantly
greater amount of space in the logs. Hopefully, however, such uncaught
exceptions aren't very common."""))
conf.supybot.log.registerGroup('stdout',
registry.GroupWithValue(registry.Boolean(True, """Determines whether the bot
will log to stdout.""")))
class BooleanRequiredFalseOnWindows(registry.Boolean):
def set(self, s):
registry.Boolean.set(self, s)
if self.value and os.name == 'nt':
raise InvalidRegistryValue, 'Value cannot be true on Windows.'
conf.supybot.log.stdout.register('colorized',
BooleanRequiredFalseOnWindows(False, """Determines whether the bot's logs to
stdout (if enabled) will be colorized with ANSI color."""))
deadlyExceptions = [KeyboardInterrupt, SystemExit]
if not os.path.exists(conf.logDir):
os.mkdir(conf.logDir, 0755)
if not os.path.exists(conf.supybot.directories.log()):
os.mkdir(conf.supybot.directories.log(), 0755)
pluginLogDir = os.path.join(conf.logDir, 'plugins')
pluginLogDir = os.path.join(conf.supybot.directories.log(), 'plugins')
if not os.path.exists(pluginLogDir):
os.mkdir(pluginLogDir, 0755)
@ -56,14 +100,14 @@ if not os.path.exists(pluginLogDir):
class Formatter(logging.Formatter):
def formatTime(self, record, datefmt=None):
if datefmt is None:
datefmt = conf.logTimestampFormat
datefmt = conf.supybot.log.timestampFormat()
return logging.Formatter.formatTime(self, record, datefmt)
def formatException(self, (E, e, tb)):
for exn in deadlyExceptions:
if issubclass(e.__class__, exn):
raise
if conf.detailedTracebacks:
if conf.supybot.log.detailedTracebacks():
try:
return cgitb.text((E, e, tb)).rstrip('\r\n')
except:
@ -120,7 +164,7 @@ class DailyRotatingHandler(BetterFileHandler):
class ColorizedFormatter(Formatter):
def formatException(self, (E, e, tb)):
if conf.colorizedStdoutLogging:
if conf.supybot.log.stdout.colorized():
return ''.join([ansi.BOLD, ansi.RED,
Formatter.formatException(self, (E, e, tb)),
ansi.RESET])
@ -128,7 +172,7 @@ class ColorizedFormatter(Formatter):
return Formatter.formatException(self, (E, e, tb))
def format(self, record, *args, **kwargs):
if conf.colorizedStdoutLogging:
if conf.supybot.log.stdout.colorized():
color = ''
if record.levelno == logging.CRITICAL:
color = ansi.WHITE + ansi.BOLD
@ -148,13 +192,14 @@ pluginFormatter = Formatter('%(levelname)s %(asctime)s %(name)s %(message)s')
# These are not.
_logger = logging.getLogger('supybot')
_handler = BetterFileHandler(os.path.join(conf.logDir, 'misc.log'))
_handler = BetterFileHandler(os.path.join(conf.supybot.directories.log(),
'misc.log'))
_handler.setFormatter(formatter)
_handler.setLevel(-1)
_logger.addHandler(_handler)
_logger.setLevel(conf.minimumLogPriority)
_logger.setLevel(conf.supybot.log.minimumPriority())
if conf.stdoutLogging:
if conf.supybot.log.stdout():
_stdoutHandler = BetterStreamHandler(sys.stdout)
_formatString = '%(name)s: %(levelname)s %(message)s'
_stdoutFormatter = ColorizedFormatter(_formatString)
@ -178,7 +223,7 @@ def getPluginLogger(name):
if not log.handlers:
filename = os.path.join(pluginLogDir, '%s.log' % name)
handler = BetterFileHandler(filename)
handler.setLevel(conf.minimumLogPriority)
handler.setLevel(conf.supybot.log.minimumPriority())
handler.setFormatter(pluginFormatter)
log.addHandler(handler)
return log

View File

@ -118,7 +118,7 @@ class ChannelDBHandler(object):
"""Override this to specialize the filenames of your databases."""
channel = ircutils.toLower(channel)
prefix = '%s-%s%s' % (channel, self.__class__.__name__, self.suffix)
return os.path.join(conf.dataDir, prefix)
return os.path.join(conf.supybot.directories.data(), prefix)
def makeDb(self, filename):
"""Override this to create your databases."""
@ -174,7 +174,7 @@ class PeriodicFileDownloader(object):
you want with this; you may want to build a database, take some stats,
or simply rename the file. You can pass None as your function and the
file with automatically be renamed to match the filename you have it listed
under. It'll be in conf.dataDir, of course.
under. It'll be in conf.supybot.directories.data, of course.
Aside from that dictionary, simply use self.getFile(filename) in any method
that makes use of a periodically downloaded file, and you'll be set.
@ -187,7 +187,8 @@ class PeriodicFileDownloader(object):
self.downloadedCounter = {}
for filename in self.periodicFiles:
if self.periodicFiles[filename][-1] is None:
fullname = os.path.join(conf.dataDir, filename)
fullname = os.path.join(conf.supybot.directories.data(),
filename)
if os.path.exists(fullname):
self.lastDownloaded[filename] = os.stat(fullname).st_ctime
else:
@ -206,7 +207,8 @@ class PeriodicFileDownloader(object):
self.log.warning('Error downloading %s', url)
self.log.exception('Exception:')
return
newFilename = os.path.join(conf.dataDir, utils.mktemp())
confDir = conf.supybot.directories.data()
newFilename = os.path.join(confDir, utils.mktemp())
outfd = file(newFilename, 'wb')
start = time.time()
s = infd.read(4096)
@ -220,7 +222,7 @@ class PeriodicFileDownloader(object):
self.downloadedCounter[filename] += 1
self.lastDownloaded[filename] = time.time()
if f is None:
toFilename = os.path.join(conf.dataDir, filename)
toFilename = os.path.join(confDir, filename)
if os.name == 'nt':
# Windows, grrr...
if os.path.exists(toFilename):

View File

@ -57,7 +57,7 @@ def getChannel(msg, args):
removed).
"""
if args and ircutils.isChannel(args[0]):
if conf.requireChannelCommandsToBeSentInChannel:
if conf.supybot.reply.requireChannelCommandsToBeSentInChannel():
if args[0] != msg.args[0]:
s = 'Channel commands must be sent in the channel to which ' \
'they apply.'
@ -145,22 +145,6 @@ def thread(f):
t.start()
return utils.changeFunctionName(newf, f.func_name, f.__doc__)
def name(f):
"""Makes sure a name is available based on conf.requireRegistration."""
def newf(self, irc, msg, args, *L):
try:
name = ircdb.users.getUser(msg.prefix).name
except KeyError:
if conf.requireRegistration:
irc.errorNotRegistered()
return
else:
name = msg.prefix
L = (name,) + L
ff = types.MethodType(f, self, self.__class__)
ff(irc, msg, args, *L)
return utils.changeFunctionName(newf, f.func_name, f.__doc__)
def channel(f):
"""Gives the command an extra channel arg as if it had called getChannel"""
def newf(self, irc, msg, args, *L):
@ -175,7 +159,7 @@ def urlSnarfer(f):
f = _threadedWrapMethod(f)
def newf(self, irc, msg, match, *L):
now = time.time()
cutoff = now - conf.snarfThrottle
cutoff = now - conf.supybot.snarfThrottle()
q = getattr(self, '_snarfedUrls', None)
if q is None:
q = structures.smallqueue()

View File

@ -32,13 +32,17 @@
__revision__ = "$Id$"
import copy
import sets
import types
import utils
class RegistryException(Exception):
pass
class InvalidRegistryFile(RegistryException):
pass
class InvalidRegistryValue(RegistryException):
pass
@ -50,43 +54,47 @@ def open(filename):
"""Initializes the module by loading the registry file into memory."""
cache.clear()
fd = utils.nonCommentNonEmptyLines(file(filename))
for line in fd:
line = line.rstrip()
for (i, line) in enumerate(fd):
line = line.rstrip('\r\n')
try:
(key, value) = line.split(': ', 1)
cache[key] = value
except ValueError:
raise InvalidRegistryFile, 'Error unpacking line #%s' % (i+1)
cache[key.lower()] = value
def close(registry, filename):
fd = file(filename, 'w')
for (name, value) in registry.getValues(askChildren=True):
for (name, value) in registry.getValues(getChildren=True):
fd.write('%s: %s\n' % (name, value))
fd.close()
class Value(object):
def __init__(self, default, help):
self.help = utils.normalizeWhitespace(help)
self.value = self.default = default
self.default = default
self.help = utils.normalizeWhitespace(help.strip())
self.setValue(default)
self.set(str(self)) # This is needed.
def set(self, s):
"""Override this with a function to convert a string to whatever type
you want, and set it to .value."""
# self.value = value
raise NotImplementedError
def get(self):
return self.value
def default(self):
return self.default
def setValue(self, v):
self.value = v
def reset(self):
self.value = self.default
def help(self):
return self.help
self.setValue(self.default)
def __str__(self):
return repr(self.value)
# This is simply prettier than naming this function get(self)
def __call__(self):
return self.value
class Boolean(Value):
def set(self, s):
s = s.lower()
@ -94,6 +102,8 @@ class Boolean(Value):
self.value = True
elif s in ('false', 'off', 'disabled'):
self.value = False
elif s == 'toggle':
self.value = not self.value
else:
raise InvalidRegistryValue, 'Value must be True or False.'
@ -104,9 +114,16 @@ class Integer(Value):
except ValueError:
raise InvalidRegistryValue, 'Value must be an integer.'
class Float(Value):
def set(self, s):
try:
self.value = float(s)
except ValueError:
raise InvalidRegistryValue, 'Value must be a float.'
class String(Value):
def set(self, s):
if s and s[0] not in '\'"' and s[-1] not in '\'"':
if not s or (s[0] not in '\'"' and s[-1] not in '\'"'):
s = repr(s)
try:
v = utils.safeEval(s)
@ -116,6 +133,11 @@ class String(Value):
except ValueError: # This catches utils.safeEval(s) errors too.
raise InvalidRegistryValue, 'Value must be a string.'
class NormalizedString(String):
def set(self, s):
s = utils.normalizeWhitespace(s.strip())
String.set(self, s)
class StringSurroundedBySpaces(String):
def set(self, s):
String.set(self, s)
@ -124,13 +146,25 @@ class StringSurroundedBySpaces(String):
if self.value.rstrip() == self.value:
self.value += ' '
class CommaSeparatedListOfStrings(String):
def set(self, s):
String.set(self, s)
self.value = map(str.strip, self.value.split(','))
def __str__(self):
return ','.join(self.value)
class CommaSeparatedSetOfStrings(CommaSeparatedListOfStrings):
def set(self, s):
CommaSeparatedListOfStrings.set(self, s)
self.value = sets.Set(self.value)
class Group(object):
def __init__(self):
self.__dict__['name'] = 'unset'
self.__dict__['values'] = {}
self.__dict__['children'] = {}
self.__dict__['originals'] = {}
self.name = 'unset'
self.values = {}
self.children = {}
self.originals = {}
def __nonExistentEntry(self, attr):
s = '%s is not a valid entry in %s' % (attr, self.name)
@ -140,56 +174,24 @@ class Group(object):
original = attr
attr = attr.lower()
if attr in self.values:
return self.values[attr].get()
return self.values[attr]
elif attr in self.children:
return self.children[attr]
else:
self.__nonExistentEntry(original)
def __setattr__(self, attr, s):
original = attr
attr = attr.lower()
if attr in self.values:
self.values[attr].set(s)
elif attr in self.children and hasattr(self.children[attr], 'set'):
self.children[attr].set(s)
else:
self.__nonExistentEntry(original)
def get(self, attr):
return self.__getattr__(attr)
def help(self, attr):
original = attr
attr = attr.lower()
if attr in self.values:
return self.values[attr].help
elif attr in self.children and hasattr(self.children[attr], 'help'):
return self.children[attr].help
else:
self.__nonExistentEntry(original)
def default(self, attr):
original = attr
attr = attr.lower()
if attr in self.values:
return self.values[attr].default
elif attr in self.children and hasattr(self.children[attr], 'default'):
return self.children[attr].default
else:
self.__nonExistentEntry(original)
def getChild(self, attr):
return self.children[attr.lower()]
def setName(self, name):
self.__dict__['name'] = name
self.name = name
def getName(self):
return self.__dict__['name']
return self.name
def register(self, name, value):
original = name
name = name.lower()
if name in self.values:
value.set(str(self.values[name]))
self.values[name] = value
self.originals[name] = original
if cache:
@ -200,11 +202,10 @@ class Group(object):
def registerGroup(self, name, group=None):
original = name
name = name.lower()
if name in self.children:
return # Ignore redundant group inserts.
if group is None:
group = Group()
if name in self.children:
group.__dict__['values'] = self.children[name].values
group.__dict__['children'] = self.children[name].children
self.children[name] = group
self.originals[name] = original
fullname = '%s.%s' % (self.name, name)
@ -212,25 +213,52 @@ class Group(object):
if cache and fullname in cache:
group.set(cache[fullname])
def getValues(self, askChildren=False):
def getValues(self, getChildren=False):
L = []
items = self.values.items()
utils.sortBy(lambda (k, _): (k.lower(), len(k), k), items)
for (name, child) in self.children.items():
if hasattr(child, 'value'):
items.append((name, child))
utils.sortBy(lambda (k, _): (len(k), k.lower(), k), items)
for (name, value) in items:
L.append(('%s.%s' % (self.getName(), name), str(value)))
if askChildren:
L.append(('%s.%s' % (self.getName(), self.originals[name]), value))
if getChildren:
items = self.children.items()
utils.sortBy(lambda (k, _): (k.lower(), len(k), k), items)
for (_, child) in items:
L.extend(child.getValues(askChildren))
L.extend(child.getValues(getChildren))
return L
class GroupWithDefault(Group):
class GroupWithValue(Group):
def __init__(self, value):
Group.__init__(self)
self.__dict__['help'] = value.help
self.__dict__['value'] = self.__dict__['default'] = value
self.value = value
self.help = value.help
def set(self, s):
self.value.set(s)
def setValue(self, v):
self.value.setValue(v)
def reset(self):
self.value.reset()
def __call__(self):
return self.value()
def __str__(self):
return str(self.value)
## def getValues(self, getChildren=False):
## L = Group.getValues(self, getChildren=False)
## L.insert(0, (self.getName(), str(self.value)))
## return L
class GroupWithDefault(GroupWithValue):
def __init__(self, value):
GroupWithValue.__init__(self, value)
def __makeChild(self, attr, s):
v = copy.copy(self.value)
@ -241,13 +269,7 @@ class GroupWithDefault(Group):
try:
return Group.__getattr__(self, attr)
except NonExistentRegistryEntry:
return self.value.get()
def __setattr__(self, attr, s):
try:
Group.__setattr__(self, attr, s)
except NonExistentRegistryEntry:
self.__makeChild(attr, s)
return self.value
def setName(self, name):
Group.setName(self, name)
@ -256,25 +278,16 @@ class GroupWithDefault(Group):
(_, group) = rsplit(k, '.', 1)
self.__makeChild(group, v)
def set(self, *args):
if len(args) == 1:
self.value.set(args[0])
else:
assert len(args) == 2
(attr, s) = args
def setChild(self, attr, s):
self.__setattr__(attr, s)
def getValues(self, askChildren=False):
L = Group.getValues(self, askChildren)
L.insert(0, (self.getName(), str(self.value)))
return L
if __name__ == '__main__':
import sys
sys.setrecursionlimit(40)
supybot = Group()
supybot.setName('supybot')
supybot.register('throttleTime', Integer(1, """Determines the minimum
supybot.register('throttleTime', Float(1, """Determines the minimum
number of seconds the bot will wait between sending messages to the server.
"""))
supybot.registerGroup('plugins')
@ -282,9 +295,19 @@ if __name__ == '__main__':
supybot.plugins.topic.registerGroup('separator',
GroupWithDefault(StringSurroundedBySpaces(' || ',
'Determines what separator the bot uses to separate topic entries.')))
supybot.plugins.topic.separator.set('#supybot', ' |||| ')
supybot.plugins.topic.separator.setChild('#supybot', ' |||| ')
supybot.plugins.topic.separator.set(' <> ')
supybot.throttleTime.set(10)
supybot.registerGroup('log')
supybot.log.registerGroup('stdout',
GroupWithValue(Boolean(False,
"""Help for stdout.""")))
supybot.log.stdout.register('colorized', Boolean(False,
'Help colorized'))
supybot.log.stdout.setValue(True)
for (k, v) in supybot.getValues():
print '%s: %s' % (k, v)
@ -292,11 +315,11 @@ if __name__ == '__main__':
print 'Asking children'
print
for (k, v) in supybot.getValues(askChildren=True):
for (k, v) in supybot.getValues(getChildren=True):
print '%s: %s' % (k, v)
print supybot.help('throttleTime')
print supybot.plugins.topic.help('separator')
print supybot.throttleTime.help
print supybot.plugins.topic.separator.help
# vim:set shiftwidth=4 tabstop=8 expandtab textwidth=78:

View File

@ -40,6 +40,7 @@ __revision__ ="$Id$"
import fix
import time
import atexit
import socket
from itertools import imap
@ -51,13 +52,17 @@ import ircmsgs
import schedule
instances = 0
originalPoll = conf.poll
originalPoll = conf.supybot.drivers.poll()
def resetPoll():
log.info('Resetting supybot.drivers.poll to %s', originalPoll)
conf.supybot.drivers.poll.setValue(originalPoll)
atexit.register(resetPoll)
class SocketDriver(drivers.IrcDriver):
def __init__(self, (server, port), irc, reconnectWaits=(0, 60, 300)):
global instances
instances += 1
conf.poll = originalPoll / instances
conf.supybot.drivers.poll.setValue(originalPoll / instances)
self.server = (server, port)
drivers.IrcDriver.__init__(self) # Must come after server is set.
self.irc = irc
@ -88,7 +93,9 @@ class SocketDriver(drivers.IrcDriver):
def run(self):
if not self.connected:
time.sleep(conf.poll) # Otherwise we might spin.
# We sleep here because otherwise, if we're the only driver, we'll
# spin at 100% CPU while we're disconnected.
time.sleep(conf.supybot.drivers.poll())
return
self._sendIfMsgs()
try:
@ -124,12 +131,13 @@ class SocketDriver(drivers.IrcDriver):
return
self.irc.reset()
self.conn = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
self.conn.settimeout(conf.poll*10) # Allow more time for connect.
# We allow more time for the connect here, since it might take longer.
self.conn.settimeout(conf.supybot.drivers.poll()*10)
if self.reconnectWaitsIndex < len(self.reconnectWaits)-1:
self.reconnectWaitsIndex += 1
try:
self.conn.connect(self.server)
self.conn.settimeout(conf.poll)
self.conn.settimeout(conf.supybot.drivers.poll())
except socket.error, e:
if e.args[0] != 115:
log.warning('Error connecting to %s: %s', self.server, e)
@ -144,7 +152,8 @@ class SocketDriver(drivers.IrcDriver):
def _scheduleReconnect(self):
when = time.time() + self.reconnectWaits[self.reconnectWaitsIndex]
whenS = time.strftime(conf.logTimestampFormat, time.localtime(when))
whenS = time.strftime(conf.supybot.log.timestampFormat(),
time.localtime(when))
if not world.dying:
log.info('Scheduling reconnect to %s at %s', self.server, whenS)
schedule.addEvent(self.reconnect, when)

View File

@ -42,14 +42,13 @@ import drivers
import ircmsgs
from twisted.internet import reactor
from twisted.manhole.telnet import Shell, ShellFactory
from twisted.protocols.basic import LineReceiver
from twisted.internet.protocol import ReconnectingClientFactory
class TwistedRunnerDriver(drivers.IrcDriver):
def run(self):
try:
reactor.iterate(conf.poll)
reactor.iterate(conf.supybot.drivers.poll())
except:
log.exception('Uncaught exception outside reactor:')
@ -104,26 +103,6 @@ class SupyReconnectingFactory(ReconnectingClientFactory):
def die(self):
pass
class MyShell(Shell):
def checkUserAndPass(self, username, password):
try:
id = ircdb.users.getUserId(username)
u = ircdb.users.getUser(id)
if u.checkPassword(password) and u.checkCapability('owner'):
return True
else:
return False
except KeyError:
return False
class MyShellFactory(ShellFactory):
protocol = MyShell
if conf.telnetEnable and __name__ != '__main__':
reactor.listenTCP(conf.telnetPort, MyShellFactory())
Driver = SupyReconnectingFactory
try:

View File

@ -92,7 +92,8 @@ def upkeep():
log.debug('Pattern cache size: %s'%len(ircutils._patternCache))
log.debug('HostmaskPatternEqual cache size: %s' %
len(ircutils._hostmaskPatternEqualCache))
log.info('%s upkeep ran.', time.strftime(conf.logTimestampFormat))
log.info('%s upkeep ran.',
time.strftime(conf.supybot.log.timestampFormat()))
return collected
def makeDriversDie():
@ -129,7 +130,6 @@ atexit.register(startDying)
##################################################
##################################################
##################################################
startup = False
testing = False
dying = False

View File

@ -29,17 +29,30 @@
# POSSIBILITY OF SUCH DAMAGE.
###
import os
import supybot
import logging
registryFilename = os.path.join('test-conf', 'test.conf')
fd = file(registryFilename, 'w')
fd.write("""
supybot.directories.data: test-data
supybot.directories.conf: test-conf
supybot.directories.log: test-log
supybot.reply.whenNotCommand: False
supybot.log.stdout: False
supybot.log.minimumPriority: DEBUG
supybot.log.detailedTracebacks: False
supybot.throttleTime: 0
""")
fd.close()
import registry
registry.open(registryFilename)
import log
import conf
conf.dataDir = 'test-data'
conf.confDir = 'test-conf'
conf.logDir = 'test-log'
conf.replyWhenNotCommand = False
conf.stdoutLogging = False
conf.minimumLogPriority = logging.DEBUG
conf.detailedTracebacks = False # Bugs in cgitb can be bad.
import fix
@ -62,16 +75,19 @@ if __name__ == '__main__':
import testsupport
import optparse
if not os.path.exists(conf.dataDir):
os.mkdir(conf.dataDir)
if not os.path.exists(conf.supybot.directories.data()):
os.mkdir(conf.supybot.directories.data())
if not os.path.exists(conf.confDir):
os.mkdir(conf.confDir)
if not os.path.exists(conf.supybot.directories.conf()):
os.mkdir(conf.supybot.directories.conf())
if not os.path.exists(conf.logDir):
os.mkdir(conf.logDir)
if not os.path.exists(conf.supybot.directories.log()):
os.mkdir(conf.supybot.directories.log())
pluginLogDir = os.path.join(conf.supybot.directories.log(), 'plugins')
if not os.path.exists(pluginLogDir):
os.mkdir(pluginLogDir)
pluginLogDir = os.path.join(conf.logDir, 'plugins')
for filename in os.listdir(pluginLogDir):
os.remove(os.path.join(pluginLogDir, filename))

View File

@ -51,7 +51,7 @@ class AdminTestCase(PluginTestCase, PluginDocumentation):
self.assertNotError('admin unignore foo!bar@baz')
self.assertError('admin unignore foo!bar@baz')
finally:
conf.ignores = []
conf.supybot.ignores.set('')
def testIgnores(self):
try:
@ -61,13 +61,7 @@ class AdminTestCase(PluginTestCase, PluginDocumentation):
self.assertNotError('admin ignore foo!bar@baz')
self.assertNotError('admin ignores')
finally:
conf.ignores = []
def testSetprefixchar(self):
self.assertNotError('setprefixchar $')
self.assertResponse('getprefixchar', "'$'")
self.assertError('setprefixchar p')
self.assertNoResponse(' ', 2) # make sure we return
conf.supybot.ignores.set('')
def testAddcapability(self):
self.assertError('addcapability sdlkfj foo')

View File

@ -107,12 +107,12 @@ class AliasTestCase(ChannelPluginTestCase, PluginDocumentation):
self.assertError('alias add [] foo')
self.assertError('alias add "foo bar" foo')
try:
conf.enablePipeSyntax = True
conf.supybot.pipeSyntax.setValue(True)
self.assertError('alias add "foo|bar" foo')
conf.enablePipeSyntax = False
conf.supybot.pipeSyntax.setValue(False)
self.assertNotError('alias add "foo|bar" foo')
finally:
conf.enablePipeSyntax = False
conf.supybot.pipeSyntax.setValue(False)
def testNotCannotNestRaised(self):
self.assertNotError('alias add mytell "tell $channel $1"')

View File

@ -44,13 +44,14 @@ if network:
def setUp(self, nick='test'):
PluginTestCase.setUp(self)
try:
if os.path.exists(os.path.join(conf.dataDir,
dataDir = conf.supybot.directories.data()
if os.path.exists(os.path.join(dataDir,
'Contents-i386.gz')):
pass
else:
print
print "Downloading files, this may take awhile"
filename = os.path.join(conf.dataDir, 'Contents-i386.gz')
filename = os.path.join(dataDir, 'Contents-i386.gz')
while not os.path.exists(filename):
time.sleep(1)
print "Download complete"

View File

@ -42,29 +42,32 @@ class MiscTestCase(ChannelPluginTestCase, PluginDocumentation):
def testReplyWhenNotCommand(self):
try:
conf.replyWhenNotCommand = True
original = str(conf.supybot.reply.whenNotCommand)
conf.supybot.reply.whenNotCommand.set('True')
self.prefix = 'somethingElse!user@host.domain.tld'
self.assertRegexp('foo bar baz', 'not.*command')
finally:
conf.replyWhenNotCommand = False
conf.supybot.reply.whenNotCommand.set(original)
if network:
def testNotReplyWhenRegexpsMatch(self):
try:
conf.replyWhenNotCommand = True
original = str(conf.supybot.reply.whenNotCommand)
conf.supybot.reply.whenNotCommand.set('True')
self.prefix = 'somethingElse!user@host.domain.tld'
self.assertNotError('http://gameknot.com/chess.pl?bd=1019508')
finally:
conf.replyWhenNotCommand = False
conf.supybot.reply.whenNotCommand.set(original)
def testNotReplyWhenNotCanonicalName(self):
try:
conf.replyWhenNotCommand = True
original = str(conf.supybot.reply.whenNotCommand)
conf.supybot.reply.whenNotCommand.set('True')
self.prefix = 'somethingElse!user@host.domain.tld'
self.assertNotRegexp('STrLeN foobar', 'command')
self.assertResponse('StRlEn foobar', '6')
finally:
conf.repylWhenNotCommand = False
conf.supybot.reply.whenNotCommand.set(original)
def testHelp(self):
self.assertHelp('help list')
@ -78,11 +81,11 @@ class MiscTestCase(ChannelPluginTestCase, PluginDocumentation):
def testHelpStripsPrefixChars(self):
try:
original = conf.prefixChars
conf.prefixChars = '@'
original = str(conf.supybot.prefixChars)
conf.supybot.prefixChars.set('@')
self.assertHelp('help @list')
finally:
conf.prefixChars = original
conf.supybot.prefixChars.set(original)
def testHelpIsCaseInsensitive(self):
self.assertHelp('help LIST')
@ -122,9 +125,6 @@ class MiscTestCase(ChannelPluginTestCase, PluginDocumentation):
self.assertNotError('upkeep')
self.assertNotError('logfilesize')
def testGetprefixchar(self):
self.assertNotError('getprefixchar')
def testPlugin(self):
self.assertResponse('plugin plugin', 'Misc')

View File

@ -103,43 +103,6 @@ class OwnerTestCase(PluginTestCase, PluginDocumentation):
self.assertNotError('load ALIAS')
self.assertNotError('unload ALIAS')
def testSetconf(self):
self.assertRegexp('setconf', 'confDir')
self.assertNotRegexp('setconf', 'allowEval')
self.assertResponse('setconf confDir',
'confDir is a string (%s).' % conf.confDir)
self.assertError('setconf whackyConfOption')
try:
originalConfAllowEval = conf.allowEval
conf.allowEval = False
self.assertError('setconf alsdkfj 100')
self.assertError('setconf poll "foo"')
try:
originalReplySuccess = conf.replySuccess
self.assertResponse('setconf replySuccess foo', 'foo')
self.assertResponse('setconf replySuccess "foo"', 'foo')
self.assertResponse('setconf replySuccess \'foo\'', 'foo')
finally:
conf.replySuccess = originalReplySuccess
try:
originalReplyWhenNotCommand = conf.replyWhenNotCommand
self.assertNotError('setconf replyWhenNotCommand True')
self.failUnless(conf.replyWhenNotCommand)
self.assertNotError('setconf replyWhenNotCommand False')
self.failIf(conf.replyWhenNotCommand)
self.assertNotError('setconf replyWhenNotCommand true')
self.failUnless(conf.replyWhenNotCommand)
self.assertNotError('setconf replyWhenNotCommand false')
self.failIf(conf.replyWhenNotCommand)
self.assertNotError('setconf replyWhenNotCommand 1')
self.failUnless(conf.replyWhenNotCommand)
self.assertNotError('setconf replyWhenNotCommand 0')
self.failIf(conf.replyWhenNotCommand)
finally:
conf.replyWhenNotCommand = originalReplyWhenNotCommand
finally:
conf.allowEval = originalConfAllowEval
class FunctionsTestCase(unittest.TestCase):
def testLoadPluginModule(self):

View File

@ -48,6 +48,7 @@ class StatusTestCase(PluginTestCase, PluginDocumentation):
def testCpustats(self):
m = self.assertNotError('status cpu')
self.failIf('kB kB' in m.args[1])
self.failIf('None' in m.args[1], 'None in cpu output: %r.' % m)
for s in ['linux', 'freebsd', 'openbsd', 'netbsd', 'darwin']:
if sys.platform.startswith(s):

View File

@ -86,7 +86,7 @@ class TokenizerTestCase(unittest.TestCase):
def testPipe(self):
try:
conf.enablePipeSyntax = True
conf.supybot.pipeSyntax.set('True')
self.assertRaises(SyntaxError, tokenize, '| foo')
self.assertRaises(SyntaxError, tokenize, 'foo ||bar')
self.assertRaises(SyntaxError, tokenize, 'bar |')
@ -100,7 +100,7 @@ class TokenizerTestCase(unittest.TestCase):
self.assertEqual(tokenize('foo bar | baz quux'),
['baz', 'quux', ['foo', 'bar']])
finally:
conf.enablePipeSyntax = False
conf.supybot.pipeSyntax.set('False')
def testBold(self):
s = '\x02foo\x02'
@ -127,9 +127,9 @@ class FunctionsTestCase(unittest.TestCase):
self.assertEqual('foobar--', callbacks.canonicalName('foobar--'))
def testAddressed(self):
oldprefixchars = conf.prefixChars
oldprefixchars = str(conf.supybot.prefixChars)
nick = 'supybot'
conf.prefixChars = '~!@'
conf.supybot.prefixChars.set('~!@')
inChannel = ['~foo', '@foo', '!foo',
'%s: foo' % nick, '%s foo' % nick,
'%s: foo' % nick.capitalize(), '%s: foo' % nick.upper()]
@ -142,7 +142,7 @@ class FunctionsTestCase(unittest.TestCase):
self.assertEqual('foo', callbacks.addressed(nick, msg), msg)
msg = ircmsgs.privmsg(nick, 'foo')
self.assertEqual('foo', callbacks.addressed(nick, msg))
conf.prefixChars = oldprefixchars
conf.supybot.prefixChars.set(oldprefixchars)
msg = ircmsgs.privmsg('#foo', '%s::::: bar' % nick)
self.assertEqual('bar', callbacks.addressed(nick, msg))
msg = ircmsgs.privmsg('#foo', '%s: foo' % nick.upper())
@ -155,12 +155,12 @@ class FunctionsTestCase(unittest.TestCase):
msg2 = ircmsgs.privmsg('#foo', 'bar')
self.assertEqual(callbacks.addressed('blah', msg1), 'bar')
try:
original = conf.replyWhenNotAddressed
conf.replyWhenNotAddressed = True
original = str(conf.supybot.reply.whenNotAddressed)
conf.supybot.reply.whenNotAddressed.set('True')
self.assertEqual(callbacks.addressed('blah', msg1), 'bar')
self.assertEqual(callbacks.addressed('blah', msg2), 'bar')
finally:
conf.replyWhenNotAddressed = original
conf.supybot.reply.whenNotAddressed.set(original)
def testReply(self):
prefix = 'foo!bar@baz'
@ -220,17 +220,17 @@ class PrivmsgTestCase(ChannelPluginTestCase):
def testErrorPrivateKwarg(self):
try:
originalConfErrorReplyPrivate = conf.errorReplyPrivate
conf.errorReplyPrivate = False
original = str(conf.supybot.reply.errorInPrivate)
conf.supybot.reply.errorInPrivate.set('False')
m = self.getMsg("eval irc.error('foo', private=True)")
self.failIf(ircutils.isChannel(m.args[0]))
finally:
conf.errorReplyPrivate = originalConfErrorReplyPrivate
conf.supybot.reply.errorInPrivate.set(original)
def testErrorReplyPrivate(self):
try:
originalConfErrorReplyPrivate = conf.errorReplyPrivate
conf.errorReplyPrivate = False
original = str(conf.supybot.reply.errorInPrivate)
conf.supybot.reply.errorInPrivate.set('False')
# If this doesn't raise an error, we've got a problem, so the next
# two assertions shouldn't run. So we first check that what we
# expect to error actually does so we don't go on a wild goose
@ -239,11 +239,11 @@ class PrivmsgTestCase(ChannelPluginTestCase):
self.assertError(s)
m = self.getMsg(s)
self.failUnless(ircutils.isChannel(m.args[0]))
conf.errorReplyPrivate = True
conf.supybot.reply.errorInPrivate.set('True')
m = self.getMsg(s)
self.failIf(ircutils.isChannel(m.args[0]))
finally:
conf.errorReplyPrivate = originalConfErrorReplyPrivate
conf.supybot.reply.errorInPrivate.set(original)
# Now for stuff not based on the plugins.
class First(callbacks.Privmsg):
@ -300,12 +300,12 @@ class PrivmsgTestCase(ChannelPluginTestCase):
def testEmptyNest(self):
try:
conf.replyWhenNotCommand = True
conf.supybot.reply.whenNotCommand.set('True')
self.assertError('echo []')
conf.replyWhenNotCommand = False
conf.supybot.reply.whenNotCommand.set('False')
self.assertResponse('echo []', '[]')
finally:
conf.replyWhenNotCommand = False
conf.supybot.reply.whenNotCommand.set('False')
def testDispatcherHelp(self):
self.assertNotRegexp('help first', r'\(dispatcher')
@ -317,18 +317,6 @@ class PrivmsgTestCase(ChannelPluginTestCase):
self.assertError('first blah')
self.assertResponse('third foo bar baz', 'foo bar baz')
def testConfigureHandlesNonCanonicalCommands(self):
# Note that this also ends up testing that the FakeIrc object in
# callbacks actually works.
try:
original = conf.commandsOnStart
tokens = callbacks.tokenize('Admin setprefixchar $')
conf.commandsOnStart = [tokens]
self.assertNotError('load Admin')
self.assertEqual(conf.prefixChars, '$')
finally:
conf.commandsOnStart = original
def testSyntaxErrorNotEscaping(self):
self.assertError('load [foo')
self.assertError('load foo]')
@ -342,14 +330,14 @@ class PrivmsgTestCase(ChannelPluginTestCase):
def testInvalidCommandOneReplyOnly(self):
try:
original = conf.replyWhenNotCommand
conf.replyWhenNotCommand = True
original = str(conf.supybot.reply.whenNotCommand)
conf.supybot.reply.whenNotCommand.set('True')
self.assertRegexp('asdfjkl', 'not a valid command')
self.irc.addCallback(self.InvalidCommand())
self.assertResponse('asdfjkl', 'foo')
self.assertNoResponse(' ', 2)
finally:
conf.replyWhenNotCommand = original
conf.supybot.reply.whenNotCommand.set(original)
class BadInvalidCommand(callbacks.Privmsg):
def invalidCommand(self, irc, msg, tokens):
@ -358,12 +346,12 @@ class PrivmsgTestCase(ChannelPluginTestCase):
def testBadInvalidCommandDoesNotKillAll(self):
try:
original = conf.replyWhenNotCommand
conf.replyWhenNotCommand = True
original = str(conf.supybot.reply.whenNotCommand)
conf.supybot.reply.whenNotCommand.set('True')
self.irc.addCallback(self.BadInvalidCommand())
self.assertRegexp('asdfjkl', 'not a valid command')
finally:
conf.replyWhenNotCommand = original
conf.supybot.reply.whenNotCommand.set(original)
class PrivmsgCommandAndRegexpTestCase(PluginTestCase):

View File

@ -35,10 +35,20 @@ import os
import unittest
import conf
import world
import ircdb
import ircutils
class FunctionsTestCase(unittest.TestCase):
class IrcdbTestCase(unittest.TestCase):
def setUp(self):
world.testing = False
unittest.TestCase.setUp(self)
def tearDown(self):
world.testing = True
unittest.TestCase.tearDown(self)
class FunctionsTestCase(IrcdbTestCase):
def testIsAntiCapability(self):
self.failIf(ircdb.isAntiCapability('foo'))
self.failIf(ircdb.isAntiCapability('#foo.bar'))
@ -188,7 +198,7 @@ class UserCapabilitySetTestCase(unittest.TestCase):
## self.failUnless(s.check('owner'))
class IrcUserTestCase(unittest.TestCase):
class IrcUserTestCase(IrcdbTestCase):
def testCapabilities(self):
u = ircdb.IrcUser()
u.addCapability('foo')
@ -259,7 +269,7 @@ class IrcUserTestCase(unittest.TestCase):
u = ircdb.IrcUser(capabilities=('foo',))
self.assertRaises(KeyError, u.removeCapability, 'bar')
class IrcChannelTestCase(unittest.TestCase):
class IrcChannelTestCase(IrcdbTestCase):
def testInit(self):
c = ircdb.IrcChannel()
self.failIf(c.checkCapability('op'))
@ -302,8 +312,9 @@ class IrcChannelTestCase(unittest.TestCase):
c.removeBan(banmask)
self.failIf(c.checkIgnored(prefix))
class UsersDBTestCase(unittest.TestCase):
filename = os.path.join(conf.confDir, 'UsersDBTestCase.conf')
class UsersDBTestCase(IrcdbTestCase):
filename = os.path.join(conf.supybot.directories.conf(),
'UsersDBTestCase.conf')
def setUp(self):
try:
os.remove(self.filename)
@ -352,8 +363,9 @@ class UsersDBTestCase(unittest.TestCase):
self.assertRaises(ValueError, self.users.setUser, id, u2)
class CheckCapabilityTestCase(unittest.TestCase):
filename = os.path.join(conf.confDir, 'CheckCapabilityTestCase.conf')
class CheckCapabilityTestCase(IrcdbTestCase):
filename = os.path.join(conf.supybot.directories.conf(),
'CheckCapabilityTestCase.conf')
owner = 'owner!owner@owner'
nothing = 'nothing!nothing@nothing'
justfoo = 'justfoo!justfoo@justfoo'
@ -455,9 +467,9 @@ class CheckCapabilityTestCase(unittest.TestCase):
def testNothing(self):
self.assertEqual(self.checkCapability(self.nothing, self.cap),
conf.defaultAllow)
conf.supybot.defaultAllow())
self.assertEqual(self.checkCapability(self.nothing, self.anticap),
not conf.defaultAllow)
not conf.supybot.defaultAllow())
def testJustFoo(self):
self.failUnless(self.checkCapability(self.justfoo, self.cap))
@ -503,11 +515,11 @@ class CheckCapabilityTestCase(unittest.TestCase):
u.setAuth(self.securefoo)
self.users.setUser(id, u)
try:
originalConfDefaultAllow = conf.defaultAllow
conf.defaultAllow = False
originalConfDefaultAllow = conf.supybot.defaultAllow()
conf.supybot.defaultAllow.set('False')
self.failIf(self.checkCapability('a' + self.securefoo, self.cap))
finally:
conf.defaultAllow = originalConfDefaultAllow
conf.supybot.defaultAllow.set(str(originalConfDefaultAllow))
# vim:set shiftwidth=4 tabstop=8 expandtab textwidth=78:

View File

@ -208,18 +208,20 @@ class IrcStateTestCase(unittest.TestCase):
prefix = 'nick!user@host'
irc = FakeIrc()
def testHistory(self):
oldconfmaxhistory = conf.maxHistory
conf.maxHistory = 10
oldconfmaxhistory = str(conf.supybot.maxHistoryLength)
conf.supybot.maxHistoryLength.set('10')
state = irclib.IrcState()
for msg in msgs:
try:
state.addMsg(self.irc, msg)
except Exception:
pass
self.failIf(len(state.history) > conf.maxHistory)
self.assertEqual(len(state.history), conf.maxHistory)
self.assertEqual(list(state.history), msgs[len(msgs)-conf.maxHistory:])
conf.maxHistory = oldconfmaxhistory
self.failIf(len(state.history)>conf.supybot.maxHistoryLength())
self.assertEqual(len(state.history),
conf.supybot.maxHistoryLength())
self.assertEqual(list(state.history),
msgs[len(msgs)-conf.supybot.maxHistoryLength():])
conf.supybot.maxHistoryLength.set(oldconfmaxhistory)
def testEmptyTopic(self):
state = irclib.IrcState()
@ -392,8 +394,6 @@ class IrcCallbackTestCase(unittest.TestCase):
self.assertEqual(doCommandCatcher.L, commands)
def testFirstCommands(self):
oldconfthrottle = conf.throttleTime
conf.throttleTime = 0
nick = 'nick'
user = 'user any user'
password = 'password'
@ -411,7 +411,6 @@ class IrcCallbackTestCase(unittest.TestCase):
msgs.pop()
expected.insert(0, ircmsgs.password(password))
self.assertEqual(msgs, expected)
conf.throttleTime = oldconfthrottle
# vim:set shiftwidth=4 tabstop=8 expandtab textwidth=78:

View File

@ -112,15 +112,17 @@ class PluginTestCase(unittest.TestCase):
if self.__class__ in (PluginTestCase, ChannelPluginTestCase):
# Necessary because there's a test in here that shouldn\'t run.
return
conf.prefixChars = '@'
conf.replyWhenNotCommand = False
conf.supybot.prefixChars.set('@')
conf.supybot.reply.whenNotCommand.setValue(False)
self.myVerbose = world.myVerbose
if self.cleanConfDir:
for filename in os.listdir(conf.confDir):
os.remove(os.path.join(conf.confDir, filename))
for filename in os.listdir(conf.supybot.directories.conf()):
os.remove(os.path.join(conf.supybot.directories.conf(),
filename))
if self.cleanDataDir:
for filename in os.listdir(conf.dataDir):
os.remove(os.path.join(conf.dataDir, filename))
for filename in os.listdir(conf.supybot.directories.data()):
os.remove(os.path.join(conf.supybot.directories.data(),
filename))
ircdb.users.reload()
ircdb.channels.reload()
if self.plugins is None:
@ -130,10 +132,10 @@ class PluginTestCase(unittest.TestCase):
self.irc = irclib.Irc(nick)
while self.irc.takeMsg():
pass
OwnerModule = Owner.loadPluginModule('Owner')
MiscModule = OwnerModule.loadPluginModule('Misc')
_ = OwnerModule.loadPluginClass(self.irc, OwnerModule)
_ = OwnerModule.loadPluginClass(self.irc, MiscModule)
#OwnerModule = Owner.loadPluginModule('Owner')
MiscModule = Owner.loadPluginModule('Misc')
_ = Owner.loadPluginClass(self.irc, Owner)
_ = Owner.loadPluginClass(self.irc, MiscModule)
if isinstance(self.plugins, str):
self.plugins = [self.plugins]
else:
@ -300,8 +302,8 @@ class ChannelPluginTestCase(PluginTestCase):
timeout = self.timeout
if self.myVerbose:
print # Newline, just like PluginTestCase.
if query[0] not in conf.prefixChars:
query = conf.prefixChars[0] + query
if query[0] not in conf.supybot.prefixChars():
query = conf.supybot.prefixChars()[0] + query
msg = ircmsgs.privmsg(to, query, prefix=frm)
if self.myVerbose:
print 'Feeding: %r' % msg