mirror of
https://github.com/Mikaela/Limnoria.git
synced 2024-11-23 02:49:27 +01:00
HUUUUUUUUGE Configuration change.
This commit is contained in:
parent
b18ad358ee
commit
6ca78924f3
@ -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.
|
||||
|
||||
|
@ -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:
|
||||
|
@ -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:
|
||||
|
@ -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):
|
||||
|
@ -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):
|
||||
|
@ -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)
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
||||
|
@ -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]
|
||||
|
@ -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)
|
||||
|
@ -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'
|
||||
|
||||
|
@ -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:
|
||||
|
@ -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
|
||||
|
@ -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]))
|
||||
|
@ -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)
|
||||
|
@ -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)
|
||||
|
||||
|
@ -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()
|
||||
|
@ -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))
|
||||
|
||||
|
@ -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))
|
||||
|
@ -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):
|
||||
|
@ -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>'
|
||||
|
@ -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)
|
||||
|
||||
|
@ -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'):
|
||||
|
@ -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)
|
||||
|
@ -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)
|
||||
|
||||
|
@ -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:
|
||||
|
@ -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
|
||||
|
3
setup.py
3
setup.py
@ -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']
|
||||
)
|
||||
|
59
src/Admin.py
59
src/Admin.py
@ -60,6 +60,10 @@ class Admin(privmsgs.CapabilityCheckingPrivmsg):
|
||||
def __init__(self):
|
||||
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:
|
||||
@ -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.')
|
||||
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.')
|
||||
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>
|
||||
|
||||
|
@ -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'):
|
||||
L = zip(*group.getValues())[0]
|
||||
irc.reply(utils.commaAndify(L))
|
||||
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)
|
||||
wrapper = getWrapper(name)
|
||||
wrapper.set(value)
|
||||
irc.replySuccess()
|
||||
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)
|
||||
wrapper = getWrapper(name)
|
||||
wrapper.reset()
|
||||
irc.replySuccess()
|
||||
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>
|
||||
|
18
src/Misc.py
18
src/Misc.py
@ -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>
|
||||
|
||||
|
123
src/Owner.py
123
src/Owner.py
@ -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()
|
||||
|
14
src/User.py
14
src/User.py
@ -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
|
||||
|
@ -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:
|
||||
|
@ -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]):
|
||||
|
630
src/conf.py
630
src/conf.py
@ -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:
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
31
src/ircdb.py
31
src/ircdb.py
@ -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):
|
||||
|
@ -148,8 +148,7 @@ 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)
|
||||
log.info('Not adding msg %s to queue' % msg)
|
||||
else:
|
||||
self.msgs.add(msg)
|
||||
if msg.command in _high:
|
||||
@ -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:
|
||||
|
67
src/log.py
67
src/log.py
@ -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
|
||||
|
@ -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):
|
||||
|
@ -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()
|
||||
|
219
src/registry.py
219
src/registry.py
@ -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()
|
||||
(key, value) = line.split(': ', 1)
|
||||
cache[key] = value
|
||||
for (i, line) in enumerate(fd):
|
||||
line = line.rstrip('\r\n')
|
||||
try:
|
||||
(key, value) = line.split(': ', 1)
|
||||
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 setValue(self, v):
|
||||
self.value = v
|
||||
|
||||
def get(self):
|
||||
return self.value
|
||||
|
||||
def default(self):
|
||||
return self.default
|
||||
|
||||
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 getChild(self, attr):
|
||||
return self.children[attr.lower()]
|
||||
|
||||
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 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
|
||||
self.__setattr__(attr, s)
|
||||
|
||||
def getValues(self, askChildren=False):
|
||||
L = Group.getValues(self, askChildren)
|
||||
L.insert(0, (self.getName(), str(self.value)))
|
||||
return L
|
||||
|
||||
def setChild(self, attr, s):
|
||||
self.__setattr__(attr, s)
|
||||
|
||||
|
||||
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:
|
||||
|
@ -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)
|
||||
|
@ -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:
|
||||
|
@ -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
|
||||
|
||||
|
44
test/test.py
44
test/test.py
@ -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.logDir, 'plugins')
|
||||
pluginLogDir = os.path.join(conf.supybot.directories.log(), 'plugins')
|
||||
if not os.path.exists(pluginLogDir):
|
||||
os.mkdir(pluginLogDir)
|
||||
|
||||
for filename in os.listdir(pluginLogDir):
|
||||
os.remove(os.path.join(pluginLogDir, filename))
|
||||
|
||||
|
@ -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')
|
||||
|
@ -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"')
|
||||
|
@ -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"
|
||||
|
@ -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')
|
||||
|
||||
|
@ -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):
|
||||
|
@ -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):
|
||||
|
@ -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):
|
||||
|
@ -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:
|
||||
|
@ -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:
|
||||
|
||||
|
@ -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
|
||||
|
Loading…
Reference in New Issue
Block a user