Updated completely.

This commit is contained in:
Jeremy Fincher 2004-09-28 07:10:27 +00:00
parent d5a324a0d2
commit 4b5909331a
3 changed files with 410 additions and 297 deletions

View File

@ -181,7 +181,7 @@ class Admin(privmsgs.CapabilityCheckingPrivmsg):
irc.reply(utils.commaAndify(L))
else:
irc.reply('I\'m not currently in any channels.')
channels = commands.wrap(channels, wrappers=['private'], noExtra=True)
channels = commands.wrap(channels, ['private'], noExtra=True)
def do484(self, irc, msg):
irc = self.pendingNickChanges.get(irc, None)
@ -231,7 +231,7 @@ class Admin(privmsgs.CapabilityCheckingPrivmsg):
self.pendingNickChanges[irc.getRealIrc()] = irc
else:
irc.reply(irc.nick)
nick = commands.wrap(nick, optional=['nick'])
nick = commands.wrap(nick, ['?nick'])
def part(self, irc, msg, args):
"""<channel> [<channel> ...] [<reason>]
@ -342,7 +342,7 @@ class Admin(privmsgs.CapabilityCheckingPrivmsg):
expires += time.time()
ircdb.ignores.add(hostmask, expires)
irc.replySuccess()
ignore = commands.wrap(ignore, ['hostmask'], [('int', 0)])
ignore = commands.wrap(ignore, ['hostmask', ('?int', 0)])
def unignore(self, irc, msg, args, hostmask):
"""<hostmask|nick>

View File

@ -62,50 +62,41 @@ conf.registerChannelValue(conf.supybot.plugins.Channel, 'alwaysRejoin',
rejoin a channel whenever it's kicked from the channel."""))
class Channel(callbacks.Privmsg):
def haveOps(self, irc, channel, what):
try:
if irc.nick in irc.state.channels[channel].ops:
return True
else:
irc.error('How can I %s? I\'m not opped in %s.' %
(what, channel))
return False
except KeyError:
irc.error('I don\'t seem to be in %s.' % channel)
def doKick(self, irc, msg):
channel = msg.args[0]
if msg.args[1] == irc.nick:
if self.registryValue('alwaysRejoin', channel):
irc.sendMsg(ircmsgs.join(channel)) # Fix for keys.
def mode(self, irc, msg, args, channel, mode):
def mode(self, irc, msg, args, channel):
"""[<channel>] <mode> [<arg> ...]
Sets the mode in <channel> to <mode>, sending the arguments given.
<channel> is only necessary if the message isn't sent in the channel
itself.
"""
if self.haveOps(irc, channel, 'change the mode'):
irc.queueMsg(ircmsgs.mode(channel, mode))
mode = commands.wrap(mode, [('channel', 'op'), 'something'])
irc.queueMsg(ircmsgs.mode(channel, args))
mode = commands.wrap(mode,
['channel',
('checkChannelCapability', 'op'),
('haveOp', 'change the mode')],
requireExtra=True)
def limit(self, irc, msg, args, channel, limit):
"""[<channel>] <limit>
"""[<channel>] [<limit>]
Sets the channel limit to <limit>. If <limit> is 0, removes the
channel limit. <channel> is only necessary if the message isn't sent
in the channel itself.
Sets the channel limit to <limit>. If <limit> is 0, or isn't given,
removes the channel limit. <channel> is only necessary if the message
isn't sent in the channel itself.
"""
if limit < 0:
irc.error('%r is not a positive integer.' % limit, Raise=True)
if limit:
if self.haveOps(irc, channel, 'set the limit'):
irc.queueMsg(ircmsgs.mode(channel, ['+l', limit]))
else:
if self.haveOps(irc, channel, 'unset the limit'):
irc.queueMsg(ircmsgs.mode(channel, ['-l']))
limit = commands.wrap(mode, [('channel', 'op'), 'int'])
limit = commands.wrap(mode, ['channel',
('checkChannelCapability', 'op'),
('haveOp', 'change the limit'),
('?nonNegativeInt', 0)])
def moderate(self, irc, msg, args, channel):
"""[<channel>]
@ -114,9 +105,10 @@ class Channel(callbacks.Privmsg):
send messages to the channel. <channel> is only necessary if the
message isn't sent in the channel itself.
"""
if self.haveOps(irc, channel, 'moderate the channel'):
irc.queueMsg(ircmsgs.mode(channel, ['+m']))
moderate = commands.wrap(moderate, [('channel', 'op')])
moderate = commands.wrap(moderate, ['channel',
('checkChannelCapability', 'op'),
('haveOp', 'moderate the channel')])
def unmoderate(self, irc, msg, args, channel):
"""[<channel>]
@ -125,9 +117,11 @@ class Channel(callbacks.Privmsg):
send messages to the channel. <channel> is only necessary if the
message isn't sent in the channel itself.
"""
if self.haveOps(irc, channel, 'unmoderate the channel'):
irc.queueMsg(ircmsgs.mode(channel, ['-m']))
unmoderate = commands.wrap(unmoderate, [('channel', 'op')])
unmoderate = commands.wrap(unmoderate, ['channel',
('checkChannelCapability', 'op'),
('haveOp',
'unmoderate the channel')])
def key(self, irc, msg, args, channel, key):
"""[<channel>] [<key>]
@ -137,12 +131,13 @@ class Channel(callbacks.Privmsg):
if the message isn't sent in the channel itself.
"""
if key:
if self.haveOps(irc, channel, 'set the keyword'):
irc.queueMsg(ircmsgs.mode(channel, ['+k', key]))
else:
if self.haveOps(irc, channel, 'unset the keyword'):
irc.queueMsg(ircmsgs.mode(channel, ['-k']))
key = commands.wrap(key, [('channel', 'op')], ['something'])
key = commands.wrap(key, ['channel',
('checkChannelCapability', 'op'),
('haveOp', 'change the keyword'),
'?somethingWithoutSpaces'])
def op(self, irc, msg, args, channel):
"""[<channel>] [<nick> ...]
@ -154,9 +149,10 @@ class Channel(callbacks.Privmsg):
"""
if not args:
args = [msg.nick]
if self.haveOps(irc, channel, 'op you'):
irc.queueMsg(ircmsgs.ops(channel, args))
op = commands.wrap(op, [('channel', 'op')])
op = commands.wrap(op, ['channel',
('checkChannelCapability', 'op'),
('haveOp', 'op someone')])
def halfop(self, irc, msg, args, channel):
"""[<channel>]
@ -168,9 +164,10 @@ class Channel(callbacks.Privmsg):
"""
if not args:
args = [msg.nick]
if self.haveOps(irc, channel, 'halfop you'):
irc.queueMsg(ircmsgs.halfops(channel, args))
halfop = commands.wrap(halfop, [('channel', 'halfop')])
halfop = commands.wrap(halfop, ['channel',
('checkChannelCapability', 'halfop'),
('haveOp', 'halfop someone')])
def voice(self, irc, msg, args, channel):
"""[<channel>]
@ -182,9 +179,10 @@ class Channel(callbacks.Privmsg):
"""
if not args:
args = [msg.nick]
if self.haveOps(irc, channel, 'voice you'):
irc.queueMsg(ircmsgs.voices(channel, args))
voice = commands.wrap(voice, [('channel', 'voice')])
voice = commands.wrap(voice, ['channel',
('checkChannelCapability', 'voice'),
('haveOp', 'voice someone')])
def deop(self, irc, msg, args, channel):
"""[<channel>] [<nick> ...]
@ -199,9 +197,11 @@ class Channel(callbacks.Privmsg):
irc.error('I cowardly refuse to deop myself. If you really want '
'me deopped, tell me to op you and then deop me '
'yourself.')
elif self.haveOps(irc, channel, 'deop someone'):
else:
irc.queueMsg(ircmsgs.deops(channel, args))
deop = commands.wrap(deop, [('channel', 'op')])
deop = commands.wrap(deop, ['channel',
('checkChannelCapability', 'op'),
('haveOp', 'deop someone')])
def dehalfop(self, irc, msg, args, channel):
"""[<channel>] [<nick> ...]
@ -216,9 +216,11 @@ class Channel(callbacks.Privmsg):
irc.error('I cowardly refuse to dehalfop myself. If you really '
'want me dehalfopped, tell me to op you and then '
'dehalfop me yourself.')
elif self.haveOps(irc, channel, 'dehalfop someone'):
else:
irc.queueMsg(ircmsgs.dehalfops(channel, args))
dehalfop = commands.wrap(dehalfop, [('channel', 'halfop')])
dehalfop = commands.wrap(dehalfop, ['channel',
('checkChannelCapability', 'halfop'),
('haveOp', 'dehalfop someone')])
def devoice(self, irc, msg, args, channel):
"""[<channel>] [<nick> ...]
@ -233,9 +235,11 @@ class Channel(callbacks.Privmsg):
irc.error('I cowardly refuse to devoice myself. If you really '
'want me devoiced, tell me to op you and then devoice '
'me yourself.')
elif self.haveOps(irc, channel, 'devoice someone'):
else:
irc.queueMsg(ircmsgs.devoices(channel, args))
devoice = commands.wrap(devoice, [('channel', 'voice')])
devoice = commands.wrap(devoice, ['channel',
('checkChannelCapability', 'voice'),
('haveOp', 'devoice someone')])
def cycle(self, irc, msg, args, channel, key):
"""[<channel>] [<key>]
@ -249,7 +253,9 @@ class Channel(callbacks.Privmsg):
key = None
irc.queueMsg(ircmsgs.part(channel))
irc.queueMsg(ircmsgs.join(channel, key))
cycle = commands.wrap(cycle, [('channel', 'op')], ['something'])
cycle = commands.wrap(cycle, ['channel',
('checkChannelCapability', 'op'),
'anything'])
def kick(self, irc, msg, args, channel, nick, reason):
"""[<channel>] <nick> [<reason>]
@ -259,7 +265,6 @@ class Channel(callbacks.Privmsg):
<channel> is only necessary if the message isn't sent in the channel
itself.
"""
if self.haveOps(irc, channel, 'kick someone'):
if nick not in irc.state.channels[channel].users:
irc.error('%s isn\'t in %s.' % (nick, channel))
return
@ -271,9 +276,14 @@ class Channel(callbacks.Privmsg):
'length for a KICK reason on this server.')
return
irc.queueMsg(ircmsgs.kick(channel, nick, reason))
kick = commands.wrap(kick, [('channel', 'op'), 'something'], ['something'])
kick = commands.wrap(kick, ['channel',
('checkChannelCapability', 'op'),
('haveOp', 'kick someone'),
'nick',
'?anything'])
def kban(self, irc, msg, args, channel, bannedNick, length, reason, *optlist):
def kban(self, irc, msg, args,
optlist, channel, bannedNick, length, reason):
"""[<channel>] [--{exact,nick,user,host}] <nick> [<seconds>] [<reason>]
If you have the #channel,op capability, this will kickban <nick> for
@ -287,7 +297,7 @@ class Channel(callbacks.Privmsg):
itself.
"""
# Check that they're not trying to make us kickban ourself.
self.log.critical('In kban')
self.log.debug('In kban')
if not ircutils.isNick(bannedNick):
self.log.warning('%r tried to kban a non nick: %r',
msg.prefix, bannedNick)
@ -358,24 +368,24 @@ class Channel(callbacks.Privmsg):
irc.queueMsg(ircmsgs.unban(channel, banmask))
schedule.addEvent(f, time.time() + length)
if bannedNick == msg.nick:
if self.haveOps(irc, channel, 'kick or ban someone'):
doBan()
elif ircdb.checkCapability(msg.prefix, capability):
if ircdb.checkCapability(bannedHostmask, capability):
self.log.warning('%r tried to ban %r, but both have %s',
self.log.warning('%s tried to ban %r, but both have %s',
msg.prefix, bannedHostmask, capability)
irc.error('%s has %s too, you can\'t ban him/her/it.' %
(bannedNick, capability))
elif self.haveOps(irc, channel, 'kick or ban someone'):
else:
doBan()
else:
self.log.warning('%r attempted kban without %s',
msg.prefix, capability)
irc.errorNoCapability(capability)
exact,nick,user,host
kban = \
commands.wrap(kban, ['channel', 'something'],
[('expiry', 0), 'something'],
kban = commands.wrap(kban, ['channel',
('checkChannelCapability', 'op'),
('haveOp', 'kick or ban someone'),
'nick', ('expiry?', 0), '?anything'],
getopts={'exact': None,
'nick': None,
'user': None,
@ -389,9 +399,11 @@ class Channel(callbacks.Privmsg):
the channel. <channel> is only necessary if the message isn't sent
in the channel itself.
"""
if self.haveOps(irc, channel, 'unban someone'):
irc.queueMsg(ircmsgs.unban(channel, hostmask))
unban = commands.wrap(unban, [('channel', 'op'), 'hostmask'])
unban = commands.wrap(unban, ['channel',
('checkChannelCapability', 'op'),
('haveOp', 'unban someone'),
'hostmask'])
def invite(self, irc, msg, args, channel, nick):
"""[<channel>] <nick>
@ -400,9 +412,11 @@ class Channel(callbacks.Privmsg):
to join <channel>. <channel> is only necessary if the message isn't
sent in the channel itself.
"""
if self.haveOps(irc, channel, 'invite someone'):
irc.queueMsg(ircmsgs.invite(nick, channel))
invite = commands.wrap(invite, [('channel', 'op'), 'something'])
invite = commands.wrap(invite, ['channel',
('checkChannelCapability', 'op'),
('haveOp', 'invite someone'),
'nick'])
def lobotomize(self, irc, msg, args, channel):
"""[<channel>]
@ -416,7 +430,8 @@ class Channel(callbacks.Privmsg):
c.lobotomized = True
ircdb.channels.setChannel(channel, c)
irc.replySuccess()
lobotomize = commands.wrap(lobotomize, [('channel', 'op')])
lobotomize = commands.wrap(lobotomize, ['channel',
('checkChannelCapability', 'op')])
def unlobotomize(self, irc, msg, args, channel):
"""[<channel>]
@ -430,7 +445,9 @@ class Channel(callbacks.Privmsg):
c.lobotomized = False
ircdb.channels.setChannel(channel, c)
irc.replySuccess()
unlobotomize = commands.wrap(unlobotomize, [('channel', 'op')])
unlobotomize = commands.wrap(unlobotomize,
['channel',
('checkChannelCapability', 'op')])
def permban(self, irc, msg, args, channel, banmask, expires):
"""[<channel>] <nick|hostmask> [<expires>]
@ -448,8 +465,10 @@ class Channel(callbacks.Privmsg):
c.addBan(banmask, expires)
ircdb.channels.setChannel(channel, c)
irc.replySuccess()
permban = \
commands.wrap(permban, [('channel', 'op'), 'banmask', ('expiry', 0)])
permban = commands.wrap(permban, ['channel',
('checkChannelCapability', 'op'),
'hostmask',
('?expiry', 0)])
def unpermban(self, irc, msg, args, channel, banmask):
"""[<channel>] <hostmask>
@ -458,13 +477,13 @@ class Channel(callbacks.Privmsg):
ban on <hostmask>. <channel> is only necessary if the message isn't
sent in the channel itself.
"""
banmask = privmsgs.getArgs(args)
c = ircdb.channels.getChannel(channel)
c.removeBan(banmask)
ircdb.channels.setChannel(channel, c)
#irc.queueMsg(ircmsgs.unban(channel, banmask))
irc.replySuccess()
unpermban = commands.wrap(unpermban, [('channel', 'op'), 'banmask'])
unpermban = commands.wrap(unpermban, ['channel',
('checkChannelCapability', 'op'),
'hostmask'])
def permbans(self, irc, msg, args, channel):
"""[<channel>]
@ -478,7 +497,8 @@ class Channel(callbacks.Privmsg):
irc.reply(utils.commaAndify(map(utils.dqrepr, c.bans)))
else:
irc.reply('There are currently no permanent bans on %s' % channel)
permbans = commands.wrap(permbans, [('channel', 'op')])
permbans = commands.wrap(permbans, ['channel',
('checkChannelCapability', 'op')])
def ignore(self, irc, msg, args, channel, banmask, expires):
"""[<channel>] <nick|hostmask> [<expires>]
@ -494,8 +514,10 @@ class Channel(callbacks.Privmsg):
c.addIgnore(banmask, expires)
ircdb.channels.setChannel(channel, c)
irc.replySuccess()
ignore = \
commands.wrap(ignore,[('channel', 'op'), 'banmask', ('expiry', 0)])
ignore = commands.wrap(ignore, ['channel',
('checkChannelCapability', 'op'),
'hostmask',
('?expiry', 0)])
def unignore(self, irc, msg, args, channel, banmask):
"""[<channel>] <hostmask>
@ -508,7 +530,9 @@ class Channel(callbacks.Privmsg):
c.removeIgnore(banmask)
ircdb.channels.setChannel(channel, c)
irc.replySuccess()
unignore = commands.wrap(unignore, [('channel', 'op'), 'something'])
unignore = commands.wrap(unignore, ['channel',
('checkChannelCapability', 'op'),
'hostmask'])
def ignores(self, irc, msg, args, channel):
"""[<channel>]
@ -525,7 +549,8 @@ class Channel(callbacks.Privmsg):
else:
L = sorted(c.ignores)
irc.reply(utils.commaAndify(imap(repr, L)))
ignores = commands.wrap(ignores, [('channel', 'op')])
ignores = commands.wrap(ignores, ['channel',
('checkChannelCapability', 'op')])
def addcapability(self, irc, msg, args, channel, hostmask, capabilities):
"""[<channel>] <name|hostmask> <capability> [<capability> ...]
@ -545,9 +570,11 @@ class Channel(callbacks.Privmsg):
user.addCapability(c)
ircdb.users.setUser(id, user)
irc.replySuccess()
addcapability = \
commands.wrap(addcapability,
[('channel', 'op'), 'hostmask', 'something'])
addcapability = commands.wrap(addcapability,
['channel',
('checkChannelCapability', 'op'),
'hostmask',
'somethingWithoutSpaces'])
def removecapability(self, irc, msg, args, channel, hostmask, capabilities):
"""[<channel>] <name|hostmask> <capability> [<capability> ...]
@ -576,10 +603,14 @@ class Channel(callbacks.Privmsg):
(utils.commaAndify(fail),
utils.pluralize('capability', len(fail))), Raise=True)
irc.replySuccess()
removecapability = \
commands.wrap(removecapability,
[('channel', 'op'), 'hostmask', 'something'])
removecapability = commands.wrap(removecapability,
['channel',
('checkChannelCapability', 'op'),
'hostmask',
'somethingWithoutSpaces'])
# XXX This needs to be fix0red to be like Owner.defaultcapability. Or
# something else. This is a horrible interface.
def setdefaultcapability(self, irc, msg, args, channel, v):
"""[<channel>] {True|False}
@ -595,8 +626,10 @@ class Channel(callbacks.Privmsg):
c.setDefaultCapability(False)
ircdb.channels.setChannel(channel, c)
irc.replySuccess()
setdefaultcapability = \
commands.wrap(setdefaultcapability, [('channel', 'op'), 'boolean'])
setdefaultcapability = commands.wrap(setdefaultcapability,
['channel',
('checkChannelCapability', 'op'),
'boolean'])
def setcapability(self, irc, msg, args, channel, capabilities):
"""[<channel>] <capability> [<capability> ...]
@ -610,8 +643,10 @@ class Channel(callbacks.Privmsg):
chan.addCapability(c)
ircdb.channels.setChannel(channel, chan)
irc.replySuccess()
setcapability = \
commands.wrap(setcapability, [('channel', 'op'), 'something'])
setcapability = commands.wrap(setcapability,
['channel',
('checkChannelCapability', 'op'),
'something'])
def unsetcapability(self, irc, msg, args, channel, capabilities):
"""[<channel>] <capability> [<capability> ...]
@ -634,8 +669,10 @@ class Channel(callbacks.Privmsg):
(utils.commaAndify(fail),
utils.pluralize('capability', len(fail))), Raise=True)
irc.replySuccess()
unsetcapability = \
commands.wrap(unsetcapability, [('channel', 'op'), 'something'])
unsetcapability = commands.wrap(unsetcapability,
['channel',
('checkChannelCapability', 'op'),
'somethingWithoutSpaces'])
def capabilities(self, irc, msg, args, channel):
"""[<channel>]
@ -645,7 +682,7 @@ class Channel(callbacks.Privmsg):
"""
c = ircdb.channels.getChannel(channel)
L = sorted(c.capabilities)
irc.reply('[%s]' % '; '.join(L))
irc.reply(' '.join(L))
capabilities = commands.wrap(capabilities, ['channel'])
def lobotomies(self, irc, msg, args):
@ -687,15 +724,16 @@ class Channel(callbacks.Privmsg):
if ircdb.checkCapability(hostmask, capability):
irc.reply(s, to=nick, private=True)
def alert(self, irc, msg, args, channel):
def alert(self, irc, msg, args, channel, text):
"""[<channel>] <text>
Sends <text> to all the users in <channel> who have the <channel>,op
capability.
"""
text = privmsgs.getArgs(args)
self.alertOps(irc, channel, text, frm=msg.nick)
alert = privmsgs.checkChannelCapability(alert, 'op')
alert = commands.wrap(alert, ['channel',
('checkChannelCapability', 'op'),
'something'])
Class = Channel

View File

@ -43,6 +43,7 @@ import time
import types
import threading
import supybot.log as log
import supybot.conf as conf
import supybot.utils as utils
import supybot.world as world
@ -69,31 +70,6 @@ def thread(f):
f(self, irc, msg, args, *L, **kwargs)
return utils.changeFunctionName(newf, f.func_name, f.__doc__)
def private(f):
"""Makes sure a command is given in private."""
def newf(self, irc, msg, args, *L, **kwargs):
if ircutils.isChannel(msg.args[0]):
irc.errorRequiresPrivacy()
else:
f(self, irc, msg, args, *L, **kwargs)
return utils.changeFunctionName(newf, f.func_name, f.__doc__)
def checkCapability(f, capability):
"""Makes sure a user has a certain capability before a command will run.
capability can be either a string or a callable object which will be called
in order to produce a string for ircdb.checkCapability."""
def newf(self, irc, msg, args):
cap = capability
if callable(cap):
cap = cap()
if ircdb.checkCapability(msg.prefix, cap):
f(self, irc, msg, args)
else:
self.log.info('%s attempted %s without %s.',
msg.prefix, f.func_name, cap)
irc.errorNoCapability(cap)
return utils.changeFunctionName(newf, f.func_name, f.__doc__)
class UrlSnarfThread(threading.Thread):
def __init__(self, *args, **kwargs):
assert 'url' in kwargs
@ -158,105 +134,141 @@ def urlSnarfer(f):
newf = utils.changeFunctionName(newf, f.func_name, f.__doc__)
return newf
wrappers = ircutils.IrcDict({
decorators = ircutils.IrcDict({
'thread': thread,
'private': private,
'urlSnarfer': urlSnarfer,
'checkCapability': checkCapability,
})
###
# Arg wrappers, wrappers that add arguments to the command.
# Arg wrappers, wrappers that add arguments to the command. They accept the
# irc, msg, and args, of course, as well as a State object which holds the args
# (and kwargs, though none currently take advantage of that) to be given to the
# command being decorated, as well as the name of the command, the plugin, the
# log, etc.
###
def getInt(irc, msg, args, default=None, type='integer'):
s = args.pop(0)
# This is just so we can centralize this, since it may change.
def _int(s):
return int(float(s))
def getInt(irc, msg, args, state, default=None, type='integer', p=None):
try:
return int(s)
i = _int(args[0])
if p is not None:
if not p(i):
raise ValueError
state.args.append(_int(args[0]))
del args[0]
except ValueError:
if default is not None:
return default
state.args.append(default)
else:
irc.errorInvalid(type, s, Raise=True)
irc.errorInvalid(type, args[0])
def getId(irc, msg, args):
getInt(irc, msg, args, type='id')
def getPositiveInt(irc, msg, args, state, *L):
getInt(irc, msg, args, state,
p=lambda i: i<=0, type='positive integer', *L)
def getExpiry(irc, msg, args, default=None):
s = args.pop(0)
def getNonNegativeInt(irc, msg, args, state, *L):
getInt(irc, msg, args, state,
p=lambda i: i<0, type='non-negative integer', *L)
def getId(irc, msg, args, state):
getInt(irc, msg, args, state, type='id')
def getExpiry(irc, msg, args, state, default=None):
now = int(time.time())
try:
expires = int(float(s))
expires += int(time.time())
expires = _int(args[0])
if expires:
expires += now
state.args.append(expires)
del args[0]
except ValueError:
if default is not None:
return default
if default:
default += now
state.args.append(default)
else:
irc.errorInvalid('number of seconds', s, Raise=True)
def getBoolean(irc, msg, args, default=None):
s = args.pop(0).strip().lower()
if s in ('true', 'on', 'enable', 'enabled'):
return True
elif s in ('false', 'off', 'disable', 'disabled'):
return False
elif default is not None:
return default
irc.errorInvalid('number of seconds', args[0])
# XXX This should be handled elsewhere; perhaps all optional args should
# consider their first extra arg to be a default.
except IndexError:
if default is not None:
if default:
default += now
state.args.append(default)
else:
irc.error("Value must be either True or False (or On or Off).",
Raise=True)
raise
def getChannelDb(irc, msg, args, **kwargs):
def getBoolean(irc, msg, args, state, default=None):
try:
state.args.append(utils.toBool(args[0]))
del args[0]
except ValueError:
if default is not None:
state.args.append(default)
else:
irc.errorInvalid('boolean', args[0])
def getChannelDb(irc, msg, args, state, **kwargs):
if not conf.supybot.databases.plugins.channelSpecific():
return None
state.args.append(None)
state.channel = None
else:
return channel(irc, msg, args, **kwargs)
getChannel(irc, msg, args, state, **kwargs)
def validChannel(irc, msg, args):
s = args.pop(0)
if ircutils.isChannel(s):
return s
def getHaveOp(irc, msg, args, state, action='do that'):
if state.channel not in irc.state.channels:
irc.error('I\'m not even in %s.' % state.channel, Raise=True)
if irc.nick not in irc.state.channels[state.channel].ops:
irc.error('I need to be opped to %s.' % action, Raise=True)
def validChannel(irc, msg, args, state):
if ircutils.isChannel(args[0]):
# XXX Check maxlength in irc.state.supported.
state.args.append(args.pop(0))
else:
irc.errorInvalid('channel', s, Raise=True)
irc.errorInvalid('channel', args[0])
def getHostmask(irc, msg, args):
def getHostmask(irc, msg, args, state):
if ircutils.isUserHostmask(args[0]):
return args.pop(0)
state.args.append(args.pop(0))
else:
try:
s = args.pop(0)
return irc.state.nickToHostmask(s)
hostmask = irc.state.nickToHostmask(args[0])
state.args.append(hostmask)
del args[0]
except KeyError:
irc.errorInvalid('nick or hostmask', s, Raise=True)
irc.errorInvalid('nick or hostmask', args[0])
def getBanmask(irc, msg, args):
if ircutils.isUserHostmask(args[0]):
return args.pop(0)
else:
try:
s = args.pop(0)
return ircutils.banmask(irc.state.nickToHostmask(s))
except KeyError:
irc.errorInvalid('nick or hostmask', s, Raise=True)
def getBanmask(irc, msg, args, state):
getHostmask(irc, msg, args, state)
# XXX Channel-specific stuff.
state.args[-1] = ircutils.banmask(state.args[-1])
def getUser(irc, msg, args):
def getUser(irc, msg, args, state):
try:
return ircdb.users.getUser(msg.prefix)
state.args.append(ircdb.users.getUser(msg.prefix))
except KeyError:
irc.errorNotRegistered(Raise=True)
def getOtherUser(irc, msg, args):
s = args.pop(0)
def getOtherUser(irc, msg, args, state):
try:
return ircdb.users.getUser(s)
state.args.append(ircdb.users.getUser(args[0]))
del args[0]
except KeyError:
try:
hostmask = getHostmask(irc, msg, [s])
return ircdb.users.getUser(hostmask)
getHostmask(irc, msg, args, state)
hostmask = state.args.pop()
state.args.append(ircdb.users.getUser(hostmask))
except (KeyError, IndexError, callbacks.Error):
irc.errorNoUser(Raise=True)
def _getRe(f):
def get(irc, msg, args):
def get(irc, msg, args, state):
original = args[:]
s = args.pop(0)
def isRe(s):
try:
@ -264,170 +276,233 @@ def _getRe(f):
return True
except ValueError:
return False
try:
while not isRe(s):
s += ' ' + args.pop(0)
return f(s)
state.args.append(f(s))
except IndexError:
args[:] = original
raise
return get
getMatcher = _getRe(utils.perlReToPythonRe)
getReplacer = _getRe(utils.perlReToReplacer)
def getNick(irc, msg, args):
s = args.pop(0)
if ircutils.isNick(s):
def getNick(irc, msg, args, state):
if ircutils.isNick(args[0]):
if 'nicklen' in irc.state.supported:
if len(s) > irc.state.supported['nicklen']:
if len(args[0]) > irc.state.supported['nicklen']:
irc.errorInvalid('nick', s,
'That nick is too long for this server.',
Raise=True)
return s
'That nick is too long for this server.')
state.args.append(args.pop(0))
else:
irc.errorInvalid('nick', s, Raise=True)
irc.errorInvalid('nick', s)
def getChannel(irc, msg, args, cap=None):
if ircutils.isChannel(args[0]):
def getChannel(irc, msg, args, state):
if args and ircutils.isChannel(args[0]):
channel = args.pop(0)
elif ircutils.isChannel(msg.args[0]):
channel = msg.args[0]
else:
state.log.debug('Raising ArgumentError because there is no channel.')
raise callbacks.ArgumentError
if cap is not None:
if callable(cap):
cap = cap()
cap = ircdb.makeChannelCapability(channel, cap)
state.channel = channel
state.args.append(channel)
def checkChannelCapability(irc, msg, args, state, cap):
assert state.channel, \
'You must use a channel arg before you use checkChannelCapability.'
cap = ircdb.canonicalCapability(cap)
cap = ircdb.makeChannelCapability(state.channel, cap)
if not ircdb.checkCapability(msg.prefix, cap):
irc.errorNoCapability(cap, Raise=True)
return channel
def getLowered(irc, msg, args):
return ircutils.toLower(args.pop(0))
def getLowered(irc, msg, args, state):
state.args.append(ircutils.toLower(args.pop(0)))
def getSomething(irc, msg, args):
s = args.pop(0)
if not s:
# XXX Better reply? How?
irc.error('You must not give the empty string as an argument.',
Raise=True)
return s
def getSomething(irc, msg, args, state, errorMsg=None, p=None):
if p is None:
p = lambda _: True
if not args[0] or not p(args[0]):
if errorMsg is None:
errorMsg = 'You must not give the empty string as an argument.'
irc.error(errorMsg, Raise=True)
else:
state.args.append(args.pop(0))
def getPlugin(irc, msg, args, requirePresent=False):
s = args.pop(0)
cb = irc.getCallback(s)
def getSomethingNoSpaces(irc, msg, args, state, *L):
def p(s):
return len(s.split(None, 1)) == 1
getSomething(irc, msg, args, state, p=p, *L)
def getPlugin(irc, msg, args, state, requirePresent=False):
cb = irc.getCallback(args[0])
if requirePresent and cb is None:
irc.errorInvalid('plugin', s, Raise=True)
return cb
irc.errorInvalid('plugin', s)
state.args.append(cb)
del args[0]
argWrappers = ircutils.IrcDict({
def private(irc, msg, args, state):
if ircutils.isChannel(msg.args[0]):
irc.errorRequiresPrivacy(Raise=True)
def checkCapability(irc, msg, args, state, cap):
cap = ircdb.canonicalCapability(cap)
if not ircdb.checkCapability(msg.prefix, cap):
state.log.warning('%s tried %s without %s.',
msg.prefix, state.name, cap)
irc.errorNoCapability(cap, Raise=True)
def anything(irc, msg, args, state):
state.args.append(args.pop(0))
wrappers = ircutils.IrcDict({
'id': getId,
'int': getInt,
'positiveInt': getPositiveInt,
'nonNegativeInt': getNonNegativeInt,
'haveOp': getHaveOp,
'expiry': getExpiry,
'nick': getNick,
'channel': getChannel,
'plugin': getPlugin,
'boolean': getBoolean,
'lowered': getLowered,
'anything': anything,
'something': getSomething,
'somethingWithoutSpaces': getSomethingNoSpaces,
'channelDb': getChannelDb,
'hostmask': getHostmask,
'banmask': getBanmask,
'user': getUser,
'private': private,
'otherUser': getOtherUser,
'regexpMatcher': getMatcher,
'validChannel': validChannel,
'regexpReplacer': getReplacer,
'checkCapability': checkCapability,
'checkChannelCapability': checkChannelCapability,
})
def args(irc,msg,args, required=[], optional=[], getopts=None, noExtra=False):
starArgs = []
req = required[:]
opt = optional[:]
class State(object):
def __init__(self, name=None, logger=None):
if logger is None:
logger = log
self.args = []
self.kwargs = {}
self.name = name
self.log = logger
self.getopts = []
self.channel = None
def args(irc,msg,args, types=[], state=None,
getopts=None, noExtra=False, requireExtra=False, combineRest=True):
orig = args[:]
if state is None:
state = State(name='unknown', logger=log)
if requireExtra:
combineRest = False # Implied by requireExtra.
types = types[:] # We're going to destroy this.
if getopts is not None:
getoptL = []
for (key, value) in getopts.iteritems():
if value != '': # value can be None, remember.
key += '='
getoptL.append(key)
def getArgWrapper(x):
if isinstance(x, tuple):
assert x
name = x[0]
args = x[1:]
def callWrapper(spec):
if isinstance(spec, tuple):
assert spec, 'tuple specification cannot be empty.'
name = spec[0]
specArgs = spec[1:]
else:
assert isinstance(x, basestring) or x is None
name = x
args = ()
if name is not None:
return argWrappers[name], args
assert isinstance(spec, basestring) or spec is None
name = spec
specArgs = ()
if name is None:
name = 'anything'
enforce = True
optional = False
if name.startswith('?'):
optional = True
name = name[1:]
elif name.endswith('?'):
optional = True
enforce = False
name = name[:-1]
wrapper = wrappers[name]
try:
wrapper(irc, msg, args, state, *specArgs)
except (callbacks.Error, ValueError, callbacks.ArgumentError), e:
state.log.debug('%r when calling wrapper.', utils.exnToString(e))
if not enforce:
state.args.append('')
else:
return lambda irc, msg, args: args.pop(0), args
def getConversion(name):
(converter, convertArgs) = getArgWrapper(name)
v = converter(irc, msg, args, *convertArgs)
return v
def callConverter(name):
v = getConversion(name)
starArgs.append(v)
state.log.debug('Re-raising %s because of enforce.', e)
raise
except IndexError, e:
state.log.debug('%r when calling wrapper.', utils.exnToString(e))
if optional:
state.args.append('')
else:
state.log.debug('Raising ArgumentError because of '
'non-optional args.')
raise callbacks.ArgumentError
# First, we getopt stuff.
if getopts is not None:
L = []
(optlist, args) = getopt.getopt(args, '', getoptL)
for (opt, arg) in optlist:
opt = opt[2:] # Strip --
assert opt in getopts
if arg is not None:
assert getopts[opt] != ''
L.append((opt, getConversion(getopts[opt])))
state.getopts.append((opt, callWrapper(getopts[opt])))
else:
assert getopts[opt] == ''
L.append((opt, True))
starArgs.append(L)
state.getopts.append((opt, True))
# Second, we get out everything but the last argument.
try:
while len(req) + len(opt) > 1:
if req:
callConverter(req.pop(0))
else:
assert opt
callConverter(opt.pop(0))
# Second, we get out everything but the last argument (or, if combineRest
# is False, we'll clear out all the types).
while len(types) > 1 or (types and not combineRest):
callWrapper(types.pop(0))
# Third, if there is a remaining required or optional argument
# (there's a possibility that there were no required or optional
# arguments) then we join the remaining args and work convert that.
if req or opt:
if types:
assert len(types) == 1
if args:
rest = ' '.join(args)
args = [rest]
if required:
converterName = req.pop(0)
else:
converterName = opt.pop(0)
callConverter(converterName)
except IndexError:
if req:
raise callbacks.ArgumentError
while opt:
del opt[-1]
starArgs.append('')
callWrapper(types.pop(0))
if noExtra and args:
log.debug('noExtra and args: %r (originally %r)', args, orig)
raise callbacks.ArgumentError
return starArgs
if requireExtra and not args:
log.debug('requireExtra and not args: %r (originally %r)', args, orig)
log.debug('args: %r' % args)
log.debug('State.args: %r' % state.args)
log.debug('State.getopts: %r' % state.getopts)
return state
# These are used below, but we need to rename them so their names aren't
# shadowed by our locals.
_args = args
_wrappers = wrappers
def wrap(f, required=[], optional=[],
wrappers=None, getopts=None, noExtra=False):
_decorators = decorators
def wrap(f, *argsArgs, **argsKwargs):
def newf(self, irc, msg, args, **kwargs):
starArgs = _args(irc, msg, args,
getopts=getopts, noExtra=noExtra,
required=required, optional=optional)
f(self, irc, msg, args, *starArgs, **kwargs)
state = State('%s.%s' % (self.name(), f.func_name), self.log)
state.cb = self # This should probably be in State.__init__.
_args(irc,msg,args, state=state, *argsArgs, **argsKwargs)
if state.getopts:
f(self, irc, msg, args, state.getopts, *state.args, **state.kwargs)
else:
f(self, irc, msg, args, *state.args, **state.kwargs)
if wrappers is not None:
wrappers = map(_wrappers.__getitem__, wrappers)
for wrapper in wrappers:
newf = wrapper(newf)
decorators = argsKwargs.pop('decorators', None)
if decorators is not None:
decorators = map(_decorators.__getitem__, decorators)
for decorator in decorators:
newf = decorator(newf)
return utils.changeFunctionName(newf, f.func_name, f.__doc__)