A bunch more %r -> %s conversions as well as wrap updates, new policy for

Channel.voice and some bug fixes for Debian
This commit is contained in:
James Vega 2004-10-25 22:59:03 +00:00
parent 2adef7c265
commit cbd5abbab7
17 changed files with 172 additions and 212 deletions

View File

@ -124,7 +124,7 @@ class Debian(callbacks.Privmsg,
# that). # that).
if not optlist and not glob: if not optlist and not glob:
raise callbacks.ArgumentError raise callbacks.ArgumentError
if optlist and glob > 1: if optlist and glob:
irc.error('You must specify either a glob or a regexp/exact ' irc.error('You must specify either a glob or a regexp/exact '
'search, but not both.') 'search, but not both.')
for (option, arg) in optlist: for (option, arg) in optlist:
@ -181,8 +181,8 @@ class Debian(callbacks.Privmsg,
irc.reply('I found no packages with that file.') irc.reply('I found no packages with that file.')
else: else:
irc.reply(utils.commaAndify(packages)) irc.reply(utils.commaAndify(packages))
file = wrap(file, [getopts({'regexp':'regexpMatcher', 'exact':'text'}), file = wrap(file, [getopts({'regexp':'regexpMatcher','exact':'something'}),
additional('text')]) additional('glob')])
_debreflags = re.DOTALL | re.IGNORECASE _debreflags = re.DOTALL | re.IGNORECASE
_deblistre = re.compile(r'<h3>Package ([^<]+)</h3>(.*?)</ul>', _debreflags) _deblistre = re.compile(r'<h3>Package ([^<]+)</h3>(.*?)</ul>', _debreflags)
@ -264,8 +264,8 @@ class Debian(callbacks.Privmsg,
archPredicate = lambda s, arg=arg: (arg in s) archPredicate = lambda s, arg=arg: (arg in s)
predicates.append(archPredicate) predicates.append(archPredicate)
for glob in globs: for glob in globs:
glob = glob.replace('*', '.*').replace('?', '.?') glob = fnmatch.translate(glob)
predicates.append(re.compile(r'.*%s.*' % glob).search) predicates.append(re.compile(glob).search)
packages = [] packages = []
try: try:
fd = webutils.getUrlFd('http://incoming.debian.org/') fd = webutils.getUrlFd('http://incoming.debian.org/')
@ -285,18 +285,16 @@ class Debian(callbacks.Privmsg,
irc.reply(utils.commaAndify(packages)) irc.reply(utils.commaAndify(packages))
incoming = thread(wrap(incoming, incoming = thread(wrap(incoming,
[getopts({'regexp':'regexpMatcher', 'arch':'text'}), [getopts({'regexp':'regexpMatcher', 'arch':'text'}),
any('text')])) any('glob')]))
_newpkgre = re.compile(r'<li><a href[^>]+>([^<]+)</a>') _newpkgre = re.compile(r'<li><a href[^>]+>([^<]+)</a>')
def new(self, irc, msg, args, optlist, glob): def new(self, irc, msg, args, section, glob):
"""[{main,contrib,non-free}] [<glob>] """[{main,contrib,non-free}] [<glob>]
Checks for packages that have been added to Debian's unstable branch Checks for packages that have been added to Debian's unstable branch
in the past week. If no glob is specified, returns a list of all in the past week. If no glob is specified, returns a list of all
packages. If no section is specified, defaults to main. packages. If no section is specified, defaults to main.
""" """
if '?' not in glob and '*' not in glob:
glob = '*%s*' % glob
try: try:
fd = webutils.getUrlFd( fd = webutils.getUrlFd(
'http://packages.debian.org/unstable/newpkg_%s' % section) 'http://packages.debian.org/unstable/newpkg_%s' % section)
@ -316,7 +314,7 @@ class Debian(callbacks.Privmsg,
irc.error('No packages matched that search.') irc.error('No packages matched that search.')
new = wrap(new, [optional(('literal', ('main', 'contrib', 'non-free')), new = wrap(new, [optional(('literal', ('main', 'contrib', 'non-free')),
'main'), 'main'),
additional('text', '*')]) additional('glob', '*')])
_severity = re.compile(r'.*(?:severity set to `([^\']+)\'|' _severity = re.compile(r'.*(?:severity set to `([^\']+)\'|'
r'severity:\s+([^\s]+))', re.I) r'severity:\s+([^\s]+))', re.I)

View File

@ -163,7 +163,7 @@ class Channel(callbacks.Privmsg):
irc.queueMsg(ircmsgs.halfops(channel, nicks)) irc.queueMsg(ircmsgs.halfops(channel, nicks))
halfop = wrap(halfop, [('checkChannelCapability', 'halfop'), halfop = wrap(halfop, [('checkChannelCapability', 'halfop'),
('haveOp', 'halfop someone'), ('haveOp', 'halfop someone'),
many('nickInChannel')]) any('nickInChannel')])
def voice(self, irc, msg, args, channel, nicks): def voice(self, irc, msg, args, channel, nicks):
"""[<channel>] [<nick> ...] """[<channel>] [<nick> ...]
@ -173,12 +173,21 @@ class Channel(callbacks.Privmsg):
voice you. <channel> is only necessary if the message isn't sent in the voice you. <channel> is only necessary if the message isn't sent in the
channel itself. channel itself.
""" """
if not nicks: if nicks:
if len(nicks) == 1 and msg.nick in nicks:
capability = 'voice'
else:
capability = 'op'
else:
nicks = [msg.nick] nicks = [msg.nick]
irc.queueMsg(ircmsgs.voices(channel, nicks)) capability = 'voice'
voice = wrap(voice, [('checkChannelCapability', 'voice'), capability = ircdb.makeChannelCapability(channel, capability)
('haveOp', 'voice someone'), if ircdb.checkCapability(msg.prefix, capability):
many('nickInChannel')]) irc.queueMsg(ircmsgs.voices(channel, nicks))
else:
irc.errorNoCapability(capability)
voice = wrap(voice, ['channel', ('haveOp', 'voice someone'),
any('nickInChannel')])
def deop(self, irc, msg, args, channel, nicks): def deop(self, irc, msg, args, channel, nicks):
"""[<channel>] [<nick> ...] """[<channel>] [<nick> ...]
@ -193,11 +202,10 @@ class Channel(callbacks.Privmsg):
'yourself.', Raise=True) 'yourself.', Raise=True)
if not nicks: if not nicks:
nicks = [msg.nick] nicks = [msg.nick]
else: irc.queueMsg(ircmsgs.deops(channel, nicks))
irc.queueMsg(ircmsgs.deops(channel, nicks))
deop = wrap(deop, [('checkChannelCapability', 'op'), deop = wrap(deop, [('checkChannelCapability', 'op'),
('haveOp', 'deop someone'), ('haveOp', 'deop someone'),
many('nickInChannel')]) any('nickInChannel')])
def dehalfop(self, irc, msg, args, channel, nicks): def dehalfop(self, irc, msg, args, channel, nicks):
"""[<channel>] [<nick> ...] """[<channel>] [<nick> ...]
@ -212,11 +220,10 @@ class Channel(callbacks.Privmsg):
'dehalfop me yourself.', Raise=True) 'dehalfop me yourself.', Raise=True)
if not nicks: if not nicks:
nicks = [msg.nick] nicks = [msg.nick]
else: irc.queueMsg(ircmsgs.dehalfops(channel, nicks))
irc.queueMsg(ircmsgs.dehalfops(channel, nicks))
dehalfop = wrap(dehalfop, [('checkChannelCapability', 'halfop'), dehalfop = wrap(dehalfop, [('checkChannelCapability', 'halfop'),
('haveOp', 'dehalfop someone'), ('haveOp', 'dehalfop someone'),
many('nickInChannel')]) any('nickInChannel')])
# XXX These nicks should really be sets, rather than lists, especially # XXX These nicks should really be sets, rather than lists, especially
# we check whether the bot's nick is in them. # we check whether the bot's nick is in them.
@ -235,11 +242,10 @@ class Channel(callbacks.Privmsg):
'me yourself.', Raise=True) 'me yourself.', Raise=True)
if not nicks: if not nicks:
nicks = [msg.nick] nicks = [msg.nick]
else: irc.queueMsg(ircmsgs.devoices(channel, nicks))
irc.queueMsg(ircmsgs.devoices(channel, nicks))
devoice = wrap(devoice, [('checkChannelCapability', 'voice'), devoice = wrap(devoice, [('checkChannelCapability', 'voice'),
('haveOp', 'devoice someone'), ('haveOp', 'devoice someone'),
many('nickInChannel')]) any('nickInChannel')])
def cycle(self, irc, msg, args, channel, key): def cycle(self, irc, msg, args, channel, key):
"""[<channel>] [<key>] """[<channel>] [<key>]
@ -299,11 +305,13 @@ class Channel(callbacks.Privmsg):
# Check that they're not trying to make us kickban ourself. # Check that they're not trying to make us kickban ourself.
self.log.debug('In kban') self.log.debug('In kban')
if not ircutils.isNick(bannedNick): if not ircutils.isNick(bannedNick):
self.log.warning('%r tried to kban a non nick: %r', self.log.warning('%s tried to kban a non nick: %s',
msg.prefix, bannedNick) utils.quoted(msg.prefix),
utils.quoted(bannedNick))
raise callbacks.ArgumentError raise callbacks.ArgumentError
elif bannedNick == irc.nick: elif bannedNick == irc.nick:
self.log.warning('%r tried to make me kban myself.', msg.prefix) self.log.warning('%s tried to make me kban myself.',
utils.quoted(msg.prefix))
irc.error('I cowardly refuse to kickban myself.') irc.error('I cowardly refuse to kickban myself.')
return return
if not reason: if not reason:
@ -337,7 +345,8 @@ class Channel(callbacks.Privmsg):
# Check (again) that they're not trying to make us kickban ourself. # Check (again) that they're not trying to make us kickban ourself.
if ircutils.hostmaskPatternEqual(banmask, irc.prefix): if ircutils.hostmaskPatternEqual(banmask, irc.prefix):
if ircutils.hostmaskPatternEqual(banmask, irc.prefix): if ircutils.hostmaskPatternEqual(banmask, irc.prefix):
self.log.warning('%r tried to make me kban myself.',msg.prefix) self.log.warning('%s tried to make me kban myself.',
utils.quoted(msg.prefix))
irc.error('I cowardly refuse to ban myself.') irc.error('I cowardly refuse to ban myself.')
return return
else: else:
@ -360,15 +369,16 @@ class Channel(callbacks.Privmsg):
doBan() doBan()
elif ircdb.checkCapability(msg.prefix, capability): elif ircdb.checkCapability(msg.prefix, capability):
if ircdb.checkCapability(bannedHostmask, capability): if ircdb.checkCapability(bannedHostmask, capability):
self.log.warning('%s tried to ban %r, but both have %s', self.log.warning('%s tried to ban %s, but both have %s',
msg.prefix, bannedHostmask, capability) msg.prefix, utils.quoted(bannedHostmask),
capability)
irc.error('%s has %s too, you can\'t ban him/her/it.' % irc.error('%s has %s too, you can\'t ban him/her/it.' %
(bannedNick, capability)) (bannedNick, capability))
else: else:
doBan() doBan()
else: else:
self.log.warning('%r attempted kban without %s', self.log.warning('%s attempted kban without %s',
msg.prefix, capability) utils.quoted(msg.prefix), capability)
irc.errorNoCapability(capability) irc.errorNoCapability(capability)
exact,nick,user,host exact,nick,user,host
kban = wrap(kban, kban = wrap(kban,
@ -519,7 +529,8 @@ class Channel(callbacks.Privmsg):
# XXX Add the expirations. # XXX Add the expirations.
c = ircdb.channels.getChannel(channel) c = ircdb.channels.getChannel(channel)
if len(c.ignores) == 0: if len(c.ignores) == 0:
s = 'I\'m not currently ignoring any hostmasks in %r' % channel s = 'I\'m not currently ignoring any hostmasks in %s' % \
utils.quoted(channel)
irc.reply(s) irc.reply(s)
else: else:
L = sorted(c.ignores) L = sorted(c.ignores)

View File

@ -387,8 +387,8 @@ class Misc(callbacks.Privmsg):
if irc.nested: if irc.nested:
irc.reply(utils.commaAndify(names)) irc.reply(utils.commaAndify(names))
else: else:
irc.reply('The %r command is available in the %s %s.' % irc.reply('The %s command is available in the %s %s.' %
(command, plugin, (utils.quoted(command), plugin,
utils.pluralize('plugin', len(names)))) utils.pluralize('plugin', len(names))))
else: else:
irc.error('There is no such command %s.' % command) irc.error('There is no such command %s.' % command)

View File

@ -75,7 +75,8 @@ def loadPluginModule(name, ignoreDeprecation=False):
try: try:
files.extend(os.listdir(dir)) files.extend(os.listdir(dir))
except EnvironmentError: # OSError, IOError superclass. except EnvironmentError: # OSError, IOError superclass.
log.warning('Invalid plugin directory: %r; removing.', dir) log.warning('Invalid plugin directory: %s; removing.',
utils.quoted(dir))
conf.supybot.directories.plugins().remove(dir) conf.supybot.directories.plugins().remove(dir)
loweredFiles = map(str.lower, files) loweredFiles = map(str.lower, files)
try: try:
@ -98,7 +99,8 @@ def loadPluginModule(name, ignoreDeprecation=False):
if ignoreDeprecation: if ignoreDeprecation:
log.warning('Deprecated plugin loaded: %s', name) log.warning('Deprecated plugin loaded: %s', name)
else: else:
raise Deprecated, 'Attempted to load deprecated plugin %r' % name raise Deprecated, 'Attempted to load deprecated plugin %s' % \
utils.quoted(name)
if module.__name__ in sys.modules: if module.__name__ in sys.modules:
sys.modules[module.__name__] = module sys.modules[module.__name__] = module
linecache.checkcache() linecache.checkcache()
@ -396,7 +398,8 @@ class Owner(privmsgs.CapabilityCheckingPrivmsg):
self._evalEnv['_'] = x self._evalEnv['_'] = x
irc.reply(repr(x)) irc.reply(repr(x))
except SyntaxError, e: except SyntaxError, e:
irc.reply('%s: %r' % (utils.exnToString(e), s)) irc.reply('%s: %s' % (utils.exnToString(e),
utils.quoted(s)))
except Exception, e: except Exception, e:
self.log.exception('Uncaught exception in Owner.eval. ' self.log.exception('Uncaught exception in Owner.eval. '
'This is not a bug. Please do not ' 'This is not a bug. Please do not '
@ -491,7 +494,6 @@ class Owner(privmsgs.CapabilityCheckingPrivmsg):
Sends the raw string given to the server. Sends the raw string given to the server.
""" """
s = privmsgs.getArgs(args)
try: try:
m = ircmsgs.IrcMsg(s) m = ircmsgs.IrcMsg(s)
except Exception, e: except Exception, e:

View File

@ -46,6 +46,7 @@ from itertools import imap, ifilter
import supybot.conf as conf import supybot.conf as conf
import supybot.utils as utils import supybot.utils as utils
import supybot.ircdb as ircdb import supybot.ircdb as ircdb
from supybot.commands import *
import supybot.ircutils as ircutils import supybot.ircutils as ircutils
import supybot.privmsgs as privmsgs import supybot.privmsgs as privmsgs
import supybot.callbacks as callbacks import supybot.callbacks as callbacks
@ -55,26 +56,22 @@ class User(callbacks.Privmsg):
if password and ircutils.isChannel(msg.args[0]): if password and ircutils.isChannel(msg.args[0]):
raise callbacks.Error, conf.supybot.replies.requiresPrivacy() raise callbacks.Error, conf.supybot.replies.requiresPrivacy()
def list(self, irc, msg, args): def list(self, irc, msg, args, optlist, glob):
"""[--capability <capability>] [<glob>] """[--capability=<capability>] [<glob>]
Returns the valid registered usernames matching <glob>. If <glob> is Returns the valid registered usernames matching <glob>. If <glob> is
not given, returns all registered usernames. not given, returns all registered usernames.
""" """
(optlist, rest) = getopt.getopt(args, '', ['capability='])
predicates = [] predicates = []
for (option, arg) in optlist: for (option, arg) in optlist:
if option == '--capability': if option == 'capability':
def p(u, cap=arg): def p(u, cap=arg):
try: try:
return u.checkCapability(cap) return u.checkCapability(cap)
except KeyError: except KeyError:
return False return False
predicates.append(p) predicates.append(p)
glob = privmsgs.getArgs(rest, required=0, optional=1)
if glob: if glob:
if '*' not in glob and '?' not in glob:
glob = '*%s*' % glob
r = re.compile(fnmatch.translate(glob), re.I) r = re.compile(fnmatch.translate(glob), re.I)
def p(u): def p(u):
return r.match(u.name) is not None return r.match(u.name) is not None
@ -94,8 +91,10 @@ class User(callbacks.Privmsg):
irc.reply('There are no matching registered users.') irc.reply('There are no matching registered users.')
else: else:
irc.reply('There are no registered users.') irc.reply('There are no registered users.')
list = wrap(list, [getopts({'capability':'capability'}),
additional('glob')])
def register(self, irc, msg, args): def register(self, irc, msg, args, optlist, name, password):
"""[--hashed] <name> <password> """[--hashed] <name> <password>
Registers <name> with the given password <password> and the current Registers <name> with the given password <password> and the current
@ -104,14 +103,11 @@ class User(callbacks.Privmsg):
not in a channel. If --hashed is given, the password will be hashed not in a channel. If --hashed is given, the password will be hashed
on disk, rather than being stored in the default configured format. on disk, rather than being stored in the default configured format.
""" """
(optlist, rest) = getopt.getopt(args, '', ['hashed'])
(name, password) = privmsgs.getArgs(rest, required=2)
addHostmask = True addHostmask = True
hashed = conf.supybot.databases.users.hash() hashed = conf.supybot.databases.users.hash()
for (option, arg) in optlist: for (option, arg) in optlist:
if option == '--hashed': if option == 'hashed':
hashed = True hashed = True
self._checkNotChannel(irc, msg, password)
try: try:
ircdb.users.getUserId(name) ircdb.users.getUserId(name)
irc.error('That name is already assigned to someone.', Raise=True) irc.error('That name is already assigned to someone.', Raise=True)
@ -136,30 +132,26 @@ class User(callbacks.Privmsg):
user.addHostmask(msg.prefix) user.addHostmask(msg.prefix)
ircdb.users.setUser(user) ircdb.users.setUser(user)
irc.replySuccess() irc.replySuccess()
register = wrap(register, ['private', getopts({'hashed':''}), 'something',
'something'])
def unregister(self, irc, msg, args): def unregister(self, irc, msg, args, user, password):
"""<name> [<password>] """<name> [<password>]
Unregisters <name> from the user database. If the user giving this Unregisters <name> from the user database. If the user giving this
command is an owner user, the password is not necessary. command is an owner user, the password is not necessary.
""" """
(name, password) = privmsgs.getArgs(args, optional=1) if not user.checkPassword(password):
self._checkNotChannel(irc, msg, password) user = ircdb.users.getUser(msg.prefix)
try: if not user.checkCapability('owner'):
id = ircdb.users.getUserId(name)
u = ircdb.users.getUser(id)
except KeyError:
irc.error('That username isn\'t registered.')
return
if not u.checkPassword(password):
u = ircdb.users.getUser(msg.prefix)
if not u.checkCapability('owner'):
irc.error(conf.supybot.replies.incorrectAuthentication()) irc.error(conf.supybot.replies.incorrectAuthentication())
return return
ircdb.users.delUser(id) ircdb.users.delUser(user.id)
irc.replySuccess() irc.replySuccess()
unregister = wrap(unregister, ['private', 'otherUser',
additional('something')])
def changename(self, irc, msg, args): def changename(self, irc, msg, args, user, newname, password):
"""<name> <new name> [<password>] """<name> <new name> [<password>]
Changes your current user database name to the new name given. Changes your current user database name to the new name given.
@ -167,17 +159,9 @@ class User(callbacks.Privmsg):
If you include the <password> parameter, this message must be sent If you include the <password> parameter, this message must be sent
to the bot privately (not on a channel). to the bot privately (not on a channel).
""" """
(name,newname,password) = privmsgs.getArgs(args,required=2,optional=1)
self._checkNotChannel(irc, msg, password)
try:
id = ircdb.users.getUserId(name)
user = ircdb.users.getUser(id)
except KeyError:
irc.error('That username isn\'t registered.')
return
try: try:
id = ircdb.users.getUserId(newname) id = ircdb.users.getUserId(newname)
irc.error('%r is already registered.' % newname) irc.error('%s is already registered.' % utils.quoted(newname))
return return
except KeyError: except KeyError:
pass pass
@ -185,8 +169,10 @@ class User(callbacks.Privmsg):
user.name = newname user.name = newname
ircdb.users.setUser(user) ircdb.users.setUser(user)
irc.replySuccess() irc.replySuccess()
changename = wrap(changename, ['private', 'otherUser', 'something',
additional('something')])
def addhostmask(self, irc, msg, args): def addhostmask(self, irc, msg, args, user, hostmask, password):
"""[<name>] [<hostmask>] [<password>] """[<name>] [<hostmask>] [<password>]
Adds the hostmask <hostmask> to the user specified by <name>. The Adds the hostmask <hostmask> to the user specified by <name>. The
@ -198,12 +184,8 @@ class User(callbacks.Privmsg):
hostmask. If <name> is not given, it defaults to your currently hostmask. If <name> is not given, it defaults to your currently
identified name. identified name.
""" """
(name, hostmask, password) = privmsgs.getArgs(args, 0, 3)
self._checkNotChannel(irc, msg, password)
if not hostmask: if not hostmask:
hostmask = msg.prefix hostmask = msg.prefix
if not name:
name = msg.prefix
if not ircutils.isUserHostmask(hostmask): if not ircutils.isUserHostmask(hostmask):
irc.errorInvalid('hostmask', hostmask, 'Make sure your hostmask ' irc.errorInvalid('hostmask', hostmask, 'Make sure your hostmask '
'includes a nick, then an exclamation point (!), then ' 'includes a nick, then an exclamation point (!), then '
@ -211,11 +193,6 @@ class User(callbacks.Privmsg):
'free to use wildcards (* and ?, which work just like ' 'free to use wildcards (* and ?, which work just like '
'they do on the command line) in any of these parts.', 'they do on the command line) in any of these parts.',
Raise=True) Raise=True)
try:
id = ircdb.users.getUserId(name)
user = ircdb.users.getUser(id)
except KeyError:
irc.errorNoUser(Raise=True)
try: try:
otherId = ircdb.users.getUserId(hostmask) otherId = ircdb.users.getUserId(hostmask)
if otherId != id: if otherId != id:
@ -241,8 +218,11 @@ class User(callbacks.Privmsg):
except ValueError, e: except ValueError, e:
irc.error(str(e), Raise=True) irc.error(str(e), Raise=True)
irc.replySuccess() irc.replySuccess()
addhostmask = wrap(addhostmask, [first('otherUser', 'user'),
optional('something'),
additional('something')])
def removehostmask(self, irc, msg, args): def removehostmask(self, irc, msg, args, user, hostmask, password):
"""<name> <hostmask> [<password>] """<name> <hostmask> [<password>]
Removes the hostmask <hostmask> from the record of the user specified Removes the hostmask <hostmask> from the record of the user specified
@ -251,14 +231,6 @@ class User(callbacks.Privmsg):
recognized by his hostmask. If you include the <password> parameter, recognized by his hostmask. If you include the <password> parameter,
this message must be sent to the bot privately (not on a channel). this message must be sent to the bot privately (not on a channel).
""" """
(name, hostmask, password) = privmsgs.getArgs(args, 2, 1)
self._checkNotChannel(irc, msg, password)
try:
id = ircdb.users.getUserId(name)
user = ircdb.users.getUser(id)
except KeyError:
irc.errorNoUser()
return
if not user.checkPassword(password) and \ if not user.checkPassword(password) and \
not user.checkHostmask(msg.prefix): not user.checkHostmask(msg.prefix):
u = ircdb.users.getUser(msg.prefix) u = ircdb.users.getUser(msg.prefix)
@ -277,8 +249,10 @@ class User(callbacks.Privmsg):
return return
ircdb.users.setUser(user) ircdb.users.setUser(user)
irc.replySuccess(s) irc.replySuccess(s)
removehostmask = wrap(removehostmask, ['private', 'otherUser', 'something',
additional('something')])
def setpassword(self, irc, msg, args): def setpassword(self, irc, msg, args, optlist, user, password,newpassword):
"""[--hashed] <name> <old password> <new password> """[--hashed] <name> <old password> <new password>
Sets the new password for the user specified by <name> to Sets the new password for the user specified by <name> to
@ -289,57 +263,36 @@ class User(callbacks.Privmsg):
changed isn't that same owner user), then <old password> needn't be changed isn't that same owner user), then <old password> needn't be
correct. correct.
""" """
(optlist, rest) = getopt.getopt(args, '', ['hashed'])
(name, oldpassword, newpassword) = privmsgs.getArgs(rest, 3)
hashed = conf.supybot.databases.users.hash() hashed = conf.supybot.databases.users.hash()
for (option, arg) in optlist: for (option, arg) in optlist:
if option == '--hashed': if option == 'hashed':
hashed = True hashed = True
self._checkNotChannel(irc, msg, oldpassword+newpassword)
try:
id = ircdb.users.getUserId(name)
user = ircdb.users.getUser(id)
except KeyError:
irc.errorNoUser()
return
u = ircdb.users.getUser(msg.prefix) u = ircdb.users.getUser(msg.prefix)
if user.checkPassword(oldpassword) or \ if user.checkPassword(password) or \
(u.checkCapability('owner') and not u == user): (u.checkCapability('owner') and not u == user):
user.setPassword(newpassword, hashed=hashed) user.setPassword(newpassword, hashed=hashed)
ircdb.users.setUser(user) ircdb.users.setUser(user)
irc.replySuccess() irc.replySuccess()
else: else:
irc.error(conf.supybot.replies.incorrectAuthentication()) irc.error(conf.supybot.replies.incorrectAuthentication())
setpassword = wrap(setpassword, [getopts({'hashed':''}), 'otherUser',
'something', 'something'])
def username(self, irc, msg, args): def username(self, irc, msg, args, user):
"""<hostmask|nick> """<hostmask|nick>
Returns the username of the user specified by <hostmask> or <nick> if Returns the username of the user specified by <hostmask> or <nick> if
the user is registered. the user is registered.
""" """
hostmask = privmsgs.getArgs(args) irc.reply(user.name)
if not ircutils.isUserHostmask(hostmask): username = wrap(username, ['otherUser'])
try:
hostmask = irc.state.nickToHostmask(hostmask)
except KeyError:
irc.reply('I couldn\'t find %s in my user database.' %hostmask)
return
try:
user = ircdb.users.getUser(hostmask)
irc.reply(user.name)
except KeyError:
irc.reply('I couldn\'t find %s in my user database.' % hostmask)
def hostmasks(self, irc, msg, args): def hostmasks(self, irc, msg, args, name):
"""[<name>] """[<name>]
Returns the hostmasks of the user specified by <name>; if <name> isn't Returns the hostmasks of the user specified by <name>; if <name> isn't
specified, returns the hostmasks of the user calling the command. specified, returns the hostmasks of the user calling the command.
""" """
if ircutils.isChannel(msg.args[0]):
irc.errorRequiresPrivacy()
return
name = privmsgs.getArgs(args, required=0, optional=1)
try: try:
user = ircdb.users.getUser(msg.prefix) user = ircdb.users.getUser(msg.prefix)
if name: if name:
@ -355,38 +308,24 @@ class User(callbacks.Privmsg):
irc.reply(repr(user.hostmasks)) irc.reply(repr(user.hostmasks))
except KeyError: except KeyError:
irc.errorNotRegistered() irc.errorNotRegistered()
hostmasks = wrap(hostmasks, ['private', additional('something')])
def capabilities(self, irc, msg, args): def capabilities(self, irc, msg, args, user):
"""[<name>] """[<name>]
Returns the capabilities of the user specified by <name>; if <name> Returns the capabilities of the user specified by <name>; if <name>
isn't specified, returns the hostmasks of the user calling the command. isn't specified, returns the hostmasks of the user calling the command.
""" """
if not args: irc.reply('[%s]' % '; '.join(user.capabilities))
name = msg.prefix capabilities = wrap(capabilities, [first('otherUser', 'user')])
else:
name = privmsgs.getArgs(args)
try:
user = ircdb.users.getUser(name)
irc.reply('[%s]' % '; '.join(user.capabilities))
except KeyError:
irc.errorNoUser()
def identify(self, irc, msg, args): def identify(self, irc, msg, args, user, password):
"""<name> <password> """<name> <password>
Identifies the user as <name>. This command (and all other Identifies the user as <name>. This command (and all other
commands that include a password) must be sent to the bot privately, commands that include a password) must be sent to the bot privately,
not in a channel. not in a channel.
""" """
(name, password) = privmsgs.getArgs(args, 2)
self._checkNotChannel(irc, msg)
try:
id = ircdb.users.getUserId(name)
user = ircdb.users.getUser(id)
except KeyError:
irc.errorNoUser()
return
if user.checkPassword(password): if user.checkPassword(password):
try: try:
user.addAuth(msg.prefix) user.addAuth(msg.prefix)
@ -397,8 +336,9 @@ class User(callbacks.Privmsg):
'doesn\'t match any of your known hostmasks.') 'doesn\'t match any of your known hostmasks.')
else: else:
irc.error(conf.supybot.replies.incorrectAuthentication()) irc.error(conf.supybot.replies.incorrectAuthentication())
identify = wrap(identify, ['private', 'otherUser', 'something'])
def unidentify(self, irc, msg, args): def unidentify(self, irc, msg, args, user):
"""takes no arguments """takes no arguments
Un-identifies you. Note that this may not result in the desired Un-identifies you. Note that this may not result in the desired
@ -406,12 +346,6 @@ class User(callbacks.Privmsg):
have added hostmasks to your user that can cause the bot to continue to have added hostmasks to your user that can cause the bot to continue to
recognize you. recognize you.
""" """
try:
id = ircdb.users.getUserId(msg.prefix)
user = ircdb.users.getUser(id)
except KeyError:
irc.errorNoUser()
return
user.clearAuth() user.clearAuth()
ircdb.users.setUser(user) ircdb.users.setUser(user)
irc.replySuccess('If you remain recognized after giving this command, ' irc.replySuccess('If you remain recognized after giving this command, '
@ -419,6 +353,7 @@ class User(callbacks.Privmsg):
'by password. You must remove whatever hostmask is ' 'by password. You must remove whatever hostmask is '
'causing you to be recognized in order not to be ' 'causing you to be recognized in order not to be '
'recognized.') 'recognized.')
unidentify = wrap(unidentify, ['user'])
def whoami(self, irc, msg, args): def whoami(self, irc, msg, args):
"""takes no arguments """takes no arguments
@ -430,8 +365,9 @@ class User(callbacks.Privmsg):
irc.reply(user.name) irc.reply(user.name)
except KeyError: except KeyError:
irc.reply('I don\'t recognize you.') irc.reply('I don\'t recognize you.')
whoami = wrap(whoami)
def setsecure(self, irc, msg, args): def setsecure(self, irc, msg, args, user, password, value):
"""<password> [<True|False>] """<password> [<True|False>]
Sets the secure flag on the user of the person sending the message. Sets the secure flag on the user of the person sending the message.
@ -441,21 +377,8 @@ class User(callbacks.Privmsg):
If a specific True/False value is not given, it inverts the current If a specific True/False value is not given, it inverts the current
value. value.
""" """
(password, value) = privmsgs.getArgs(args, optional=1) if value is None:
self._checkNotChannel(irc, msg, password)
try:
id = ircdb.users.getUserId(msg.prefix)
user = ircdb.users.getUser(id)
except KeyError:
irc.errorNotRegistered()
if value == '':
value = not user.secure value = not user.secure
elif value.lower() in ('true', 'on', 'enable'):
value = True
elif value.lower() in ('false', 'off', 'disable'):
value = False
else:
irc.errorInvalid('boolean value', value, Raise=True)
if user.checkPassword(password) and \ if user.checkPassword(password) and \
user.checkHostmask(msg.prefix, useAuth=False): user.checkHostmask(msg.prefix, useAuth=False):
user.secure = value user.secure = value
@ -463,6 +386,8 @@ class User(callbacks.Privmsg):
irc.reply('Secure flag set to %s' % value) irc.reply('Secure flag set to %s' % value)
else: else:
irc.error(conf.supybot.replies.incorrectAuthentication()) irc.error(conf.supybot.replies.incorrectAuthentication())
setsecure = wrap(setsecure, ['private', 'user', 'something',
additional('boolean')])
def stats(self, irc, msg, args): def stats(self, irc, msg, args):
"""takes no arguments """takes no arguments
@ -488,6 +413,7 @@ class User(callbacks.Privmsg):
'%s and %s.' % (users, hostmasks, '%s and %s.' % (users, hostmasks,
utils.nItems('owner', owners), utils.nItems('owner', owners),
utils.nItems('admin', admins))) utils.nItems('admin', admins)))
stats = wrap(stats)
## def config(self, irc, msg, args): ## def config(self, irc, msg, args):

View File

@ -480,8 +480,8 @@ class RichReplyMethods(object):
if 'Raise' not in kwargs: if 'Raise' not in kwargs:
kwargs['Raise'] = True kwargs['Raise'] = True
if isinstance(capability, basestring): # checkCommandCapability! if isinstance(capability, basestring): # checkCommandCapability!
log.warning('Denying %s for lacking %r capability.', log.warning('Denying %s for lacking %s capability.',
self.msg.prefix, capability) self.msg.prefix, utils.quoted(capability))
if not self._getConfig(conf.supybot.reply.noCapabilityError): if not self._getConfig(conf.supybot.reply.noCapabilityError):
v = self._getConfig(conf.supybot.replies.noCapability) v = self._getConfig(conf.supybot.replies.noCapability)
s = self.__makeReply(v % capability, s) s = self.__makeReply(v % capability, s)
@ -1092,7 +1092,8 @@ class Privmsg(irclib.IrcCallback):
def getCommand(self, name): def getCommand(self, name):
"""Gets the given command from this plugin.""" """Gets the given command from this plugin."""
name = canonicalName(name) name = canonicalName(name)
assert self.isCommand(name), '%r is not a command.' % name assert self.isCommand(name), '%s is not a command.' % \
utils.quoted(name)
return getattr(self, name) return getattr(self, name)
def callCommand(self, name, irc, msg, *L, **kwargs): def callCommand(self, name, irc, msg, *L, **kwargs):
@ -1243,7 +1244,8 @@ class PrivmsgRegexp(Privmsg):
r = re.compile(value.__doc__, self.flags) r = re.compile(value.__doc__, self.flags)
self.res.append((r, name)) self.res.append((r, name))
except re.error, e: except re.error, e:
self.log.warning('Invalid regexp: %r (%s)',value.__doc__,e) self.log.warning('Invalid regexp: %s (%s)',
utils.quoted(value.__doc__), e)
utils.sortBy(operator.itemgetter(1), self.res) utils.sortBy(operator.itemgetter(1), self.res)
def callCommand(self, name, irc, msg, *L, **kwargs): def callCommand(self, name, irc, msg, *L, **kwargs):

View File

@ -426,6 +426,12 @@ def checkCapability(irc, msg, args, state, cap):
def anything(irc, msg, args, state): def anything(irc, msg, args, state):
state.args.append(args.pop(0)) state.args.append(args.pop(0))
def getGlob(irc, msg, args, state):
glob = args.pop(0)
if '*' not in glob and '?' not in glob:
glob = '*%s*' % glob
state.args.append(glob)
def getUrl(irc, msg, args, state): def getUrl(irc, msg, args, state):
if webutils.urlRe.match(args[0]): if webutils.urlRe.match(args[0]):
state.args.append(args.pop(0)) state.args.append(args.pop(0))
@ -521,6 +527,7 @@ wrappers = ircutils.IrcDict({
'something': getSomething, 'something': getSomething,
'filename': getSomething, # XXX Check for validity. 'filename': getSomething, # XXX Check for validity.
'commandName': getCommandName, 'commandName': getCommandName,
'glob': getGlob,
'text': anything, 'text': anything,
'somethingWithoutSpaces': getSomethingNoSpaces, 'somethingWithoutSpaces': getSomethingNoSpaces,
'capability': getSomethingNoSpaces, 'capability': getSomethingNoSpaces,

View File

@ -457,7 +457,7 @@ registerChannelValue(supybot.replies, 'notRegistered',
but they're not currently recognized.""")) but they're not currently recognized."""))
registerChannelValue(supybot.replies, 'noCapability', registerChannelValue(supybot.replies, 'noCapability',
registry.NormalizedString("""You don't have the %r capability. If you registry.NormalizedString("""You don't have the %s capability. If you
think that you should have this capability, be sure that you are identified 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 before trying again. The 'whoami' command can tell you if you're
identified.""", """Determines what error message is given when the bot is identified.""", """Determines what error message is given when the bot is

View File

@ -225,10 +225,11 @@ class IrcUser(object):
self.hostmasks = hostmasks self.hostmasks = hostmasks
def __repr__(self): def __repr__(self):
return '%s(id=%s, ignore=%s, password="", name=%r, hashed=%r, ' \ return '%s(id=%s, ignore=%s, password="", name=%s, hashed=%r, ' \
'capabilities=%r, hostmasks=[], secure=%r)\n' % \ 'capabilities=%r, hostmasks=[], secure=%r)\n' % \
(self.__class__.__name__, self.id, self.ignore, self.name, (self.__class__.__name__, self.id, self.ignore,
self.hashed, self.capabilities, self.secure) utils.quoted(self.name), self.hashed, self.capabilities,
self.secure)
def __hash__(self): def __hash__(self):
return hash(self.id) return hash(self.id)
@ -655,7 +656,8 @@ class UsersDictionary(utils.IterableMap):
log.error('Multiple matches found in user database. ' log.error('Multiple matches found in user database. '
'Removing the offending hostmasks.') 'Removing the offending hostmasks.')
for (id, hostmask) in ids.iteritems(): for (id, hostmask) in ids.iteritems():
log.error('Removing %r from user %s.', hostmask, id) log.error('Removing %s from user %s.',
utils.quoted(hostmask), id)
self.users[id].removeHostmask(hostmask) self.users[id].removeHostmask(hostmask)
raise DuplicateHostmask, 'Ids %r matched.' % ids raise DuplicateHostmask, 'Ids %r matched.' % ids
else: # Not a hostmask, must be a name. else: # Not a hostmask, must be a name.
@ -855,7 +857,8 @@ class IgnoresDB(object):
expiration = 0 expiration = 0
self.add(hostmask, expiration) self.add(hostmask, expiration)
except Exception, e: except Exception, e:
log.error('Invalid line in ignores database: %r', line) log.error('Invalid line in ignores database: %s',
utils.quoted(line))
fd.close() fd.close()
def flush(self): def flush(self):

View File

@ -172,7 +172,8 @@ class IrcMsgQueue(object):
if msg in self.msgs and \ if msg in self.msgs and \
not conf.supybot.protocols.irc.queueDuplicateMessages(): not conf.supybot.protocols.irc.queueDuplicateMessages():
s = str(msg).strip() s = str(msg).strip()
log.warning('Not adding message %r to queue, already added.', s) log.warning('Not adding message %s to queue, already added.',
utils.quoted(s))
return False return False
else: else:
self.msgs.add(msg) self.msgs.add(msg)

View File

@ -43,6 +43,7 @@ import time
import string import string
import supybot.conf as conf import supybot.conf as conf
import supybot.utils as utils
import supybot.ircutils as ircutils import supybot.ircutils as ircutils
### ###
@ -197,8 +198,9 @@ class IrcMsg(object):
def __repr__(self): def __repr__(self):
if self._repr is not None: if self._repr is not None:
return self._repr return self._repr
self._repr = 'IrcMsg(prefix=%r, command=%r, args=%r)' % \ self._repr = 'IrcMsg(prefix=%s, command=%s, args=%r)' % \
(self.prefix, self.command, self.args) (utils.quoted(self.prefix), utils.quoted(self.command),
self.args)
return self._repr return self._repr
def __reduce__(self): def __reduce__(self):

View File

@ -100,7 +100,7 @@ def toLower(s, casemapping=None):
elif casemapping == 'ascii': # freenode elif casemapping == 'ascii': # freenode
return s.lower() return s.lower()
else: else:
raise ValueError, 'Invalid casemapping: %r' % casemapping raise ValueError, 'Invalid casemapping: %s' % utils.quoted(casemapping)
def strEqual(nick1, nick2): def strEqual(nick1, nick2):
"""Returns True if nick1 == nick2 according to IRC case rules.""" """Returns True if nick1 == nick2 according to IRC case rules."""
@ -437,7 +437,7 @@ def safeArgument(s):
if isinstance(s, unicode): if isinstance(s, unicode):
s = s.encode('utf-8') s = s.encode('utf-8')
elif not isinstance(s, basestring): elif not isinstance(s, basestring):
debug('Got a non-string in safeArgument: %r', s) debug('Got a non-string in safeArgument: %s', utils.quoted(s))
s = str(s) s = str(s)
if isValidArgument(s): if isValidArgument(s):
return s return s

View File

@ -88,7 +88,8 @@ class Logger(logging.Logger):
eId = hex(hash(eStrId) & 0xFFFFF) eId = hex(hash(eStrId) & 0xFFFFF)
logging.Logger.exception(self, *args) logging.Logger.exception(self, *args)
if hasattr(e, '__revision__') and e.__revision__: if hasattr(e, '__revision__') and e.__revision__:
self.error('Exception __revision__: %r', e.__revision__) self.error('Exception __revision__: %s',
utils.quoted(e.__revision__))
self.error('Exception id: %s', eId) self.error('Exception id: %s', eId)
# The traceback should be sufficient if we want it. # The traceback should be sufficient if we want it.
# self.error('Exception string: %s', eStrId) # self.error('Exception string: %s', eStrId)

View File

@ -507,10 +507,10 @@ def safeEval(s, namespace={'True': True, 'False': False, 'None': None}):
if node.__class__ is compiler.ast.Module: if node.__class__ is compiler.ast.Module:
return node.doc return node.doc
else: else:
raise ValueError, 'Unsafe string: %r' % s raise ValueError, 'Unsafe string: %s' % quoted(s)
node = nodes[0] node = nodes[0]
if node.__class__ is not compiler.ast.Discard: if node.__class__ is not compiler.ast.Discard:
raise ValueError, 'Invalid expression: %r' % s raise ValueError, 'Invalid expression: %s' % quoted(s)
node = node.getChildNodes()[0] node = node.getChildNodes()[0]
def checkNode(node): def checkNode(node):
if node.__class__ is compiler.ast.Const: if node.__class__ is compiler.ast.Const:
@ -529,7 +529,7 @@ def safeEval(s, namespace={'True': True, 'False': False, 'None': None}):
if checkNode(node): if checkNode(node):
return eval(s, namespace, namespace) return eval(s, namespace, namespace)
else: else:
raise ValueError, 'Unsafe string: %r' % s raise ValueError, 'Unsafe string: %s' % quoted(s)
def exnToString(e): def exnToString(e):
"""Turns a simple exception instance into a string (better than str(e))""" """Turns a simple exception instance into a string (better than str(e))"""
@ -726,7 +726,7 @@ class AtomicFile(file):
def __init__(self, filename, mode='w', allowEmptyOverwrite=True, def __init__(self, filename, mode='w', allowEmptyOverwrite=True,
makeBackupIfSmaller=True, tmpDir=None, backupDir=None): makeBackupIfSmaller=True, tmpDir=None, backupDir=None):
if mode not in ('w', 'wb'): if mode not in ('w', 'wb'):
raise ValueError, 'Invalid mode: %r' % mode raise ValueError, 'Invalid mode: %s' % quoted(mode)
self.rolledback = False self.rolledback = False
self.allowEmptyOverwrite = allowEmptyOverwrite self.allowEmptyOverwrite = allowEmptyOverwrite
self.makeBackupIfSmaller = makeBackupIfSmaller self.makeBackupIfSmaller = makeBackupIfSmaller
@ -826,7 +826,7 @@ def toBool(s):
elif s in ('false', 'off', 'disable', 'disabled'): elif s in ('false', 'off', 'disable', 'disabled'):
return False return False
else: else:
raise ValueError, 'Invalid string for toBool: %r' % s raise ValueError, 'Invalid string for toBool: %s' % quoted(s)
def mapinto(f, L): def mapinto(f, L):
for (i, x) in enumerate(L): for (i, x) in enumerate(L):

View File

@ -33,7 +33,7 @@ import supybot.conf as conf
import supybot.ircdb as ircdb import supybot.ircdb as ircdb
import supybot.ircmsgs as ircmsgs import supybot.ircmsgs as ircmsgs
class ChannelTestCase(ChannelPluginTestCase, PluginDocumentation): class ChannelTestCase(ChannelPluginTestCase):
plugins = ('Channel', 'User') plugins = ('Channel', 'User')
def testLobotomies(self): def testLobotomies(self):
self.assertRegexp('lobotomies', 'not.*any') self.assertRegexp('lobotomies', 'not.*any')

View File

@ -32,29 +32,29 @@ import time
from testsupport import * from testsupport import *
class DebianTestCase(PluginTestCase, PluginDocumentation): class DebianTestCase(PluginTestCase):
plugins = ('Debian',) plugins = ('Debian',)
timeout = 100 timeout = 100
cleanDataDir = False cleanDataDir = False
fileDownloaded = False fileDownloaded = False
if network: if network:
def setup(self, nick='test'): def setUp(self, nick='test'):
plugintestcase.setup(self) PluginTestCase.setUp(self)
try: try:
datadir = conf.supybot.directories.data() datadir = conf.supybot.directories.data()
if os.path.exists(os.path.join(datadir, if os.path.exists(os.path.join(datadir,
'contents-i386.gz')): 'Contents-i386.gz')):
pass pass
else: else:
print print
print "downloading files, this may take awhile" print "Downloading files, this may take awhile"
filename = os.path.join(datadir, 'contents-i386.gz') filename = os.path.join(datadir, 'Contents-i386.gz')
while not os.path.exists(filename): while not os.path.exists(filename):
time.sleep(1) time.sleep(1)
print "download complete" print "Download complete"
print "starting test ..." print "Starting test ..."
self.filedownloaded = true self.fileDownloaded = True
except KeyboardInterrupt: except KeyboardInterrupt:
pass pass

View File

@ -88,6 +88,7 @@ class CommandsTestCase(SupyTestCase):
def testAny(self): def testAny(self):
self.assertState([any('int')], ['1', '2', '3'], [[1, 2, 3]]) self.assertState([any('int')], ['1', '2', '3'], [[1, 2, 3]])
self.assertState([None, any('int')], ['1', '2', '3'], ['1', [2, 3]]) self.assertState([None, any('int')], ['1', '2', '3'], ['1', [2, 3]])
self.assertState([any('int')], [], [[]])
## def testAny(self): ## def testAny(self):
## self.assertState([None, any('int'), None], ## self.assertState([None, any('int'), None],
@ -104,6 +105,12 @@ class CommandsTestCase(SupyTestCase):
self.assertState(spec, ['#foo', '+s'], ['#foo', '+s']) self.assertState(spec, ['#foo', '+s'], ['#foo', '+s'])
self.assertState(spec, ['+s'], ['#foo', '+s'], target='#foo') self.assertState(spec, ['+s'], ['#foo', '+s'], target='#foo')
def testGlob(self):
spec = ['glob']
self.assertState(spec, ['foo'], ['*foo*'])
self.assertState(spec, ['?foo'], ['?foo'])
self.assertState(spec, ['foo*'], ['foo*'])
def testGetId(self): def testGetId(self):
spec = ['id'] spec = ['id']
self.assertState(spec, ['#12'], [12]) self.assertState(spec, ['#12'], [12])