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

View File

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