mirror of
https://github.com/Mikaela/Limnoria.git
synced 2024-11-27 05:09:23 +01:00
Changed callCommand to give a name rather than a method; added invalidCommand throttling, ctcp throttling, and whole bunch of other crap.
This commit is contained in:
parent
3848ca4aa5
commit
ee70224aa3
@ -89,9 +89,9 @@ class Amazon(callbacks.PrivmsgCommandAndRegexp):
|
|||||||
threaded = True
|
threaded = True
|
||||||
regexps = ['amzSnarfer']
|
regexps = ['amzSnarfer']
|
||||||
|
|
||||||
def callCommand(self, method, irc, msg, *L, **kwargs):
|
def callCommand(self, name, irc, msg, *L, **kwargs):
|
||||||
try:
|
try:
|
||||||
super(Amazon, self).callCommand(method, irc, msg, *L, **kwargs)
|
super(Amazon, self).callCommand(name, irc, msg, *L, **kwargs)
|
||||||
except amazon.NoLicenseKey, e:
|
except amazon.NoLicenseKey, e:
|
||||||
irc.error('You must have a free Amazon web services license key '
|
irc.error('You must have a free Amazon web services license key '
|
||||||
'in order to use this command. You can get one at '
|
'in order to use this command. You can get one at '
|
||||||
|
@ -65,6 +65,11 @@ conf.registerGlobalValue(conf.supybot.plugins.Anonymous, 'requireRegistration',
|
|||||||
conf.registerGlobalValue(conf.supybot.plugins.Anonymous, 'requireCapability',
|
conf.registerGlobalValue(conf.supybot.plugins.Anonymous, 'requireCapability',
|
||||||
registry.String('', """Determines what capability (if any) the bot should
|
registry.String('', """Determines what capability (if any) the bot should
|
||||||
require people trying to use this plugin to have."""))
|
require people trying to use this plugin to have."""))
|
||||||
|
conf.registerGlobalValue(conf.supybot.plugins.Anonymous, 'allowPrivateTarget',
|
||||||
|
registry.Boolean(False, """Determines whether the bot will require targets
|
||||||
|
of the "say" command to be public (i.e., channels). If this is True, the
|
||||||
|
bot will allow people to use the "say" command to send private messages to
|
||||||
|
other users."""))
|
||||||
|
|
||||||
|
|
||||||
class Anonymous(callbacks.Privmsg):
|
class Anonymous(callbacks.Privmsg):
|
||||||
@ -89,7 +94,7 @@ class Anonymous(callbacks.Privmsg):
|
|||||||
c = ircdb.channels.getChannel(channel)
|
c = ircdb.channels.getChannel(channel)
|
||||||
if c.lobotomized:
|
if c.lobotomized:
|
||||||
irc.error('I\'m lobotomized in %s.' % channel, Raise=True)
|
irc.error('I\'m lobotomized in %s.' % channel, Raise=True)
|
||||||
if not c.checkCapability(self.__class__.__name__):
|
if not c.checkCapability(self.name()):
|
||||||
irc.error('That channel has set its capabilities so as to '
|
irc.error('That channel has set its capabilities so as to '
|
||||||
'disallow the use of this plugin.', Raise=True)
|
'disallow the use of this plugin.', Raise=True)
|
||||||
|
|
||||||
|
@ -46,44 +46,85 @@ sys.path.append(os.pardir)
|
|||||||
|
|
||||||
import supybot.conf as conf
|
import supybot.conf as conf
|
||||||
import supybot.ircmsgs as ircmsgs
|
import supybot.ircmsgs as ircmsgs
|
||||||
|
import supybot.ircutils as ircutils
|
||||||
|
import supybot.registry as registry
|
||||||
import supybot.callbacks as callbacks
|
import supybot.callbacks as callbacks
|
||||||
|
|
||||||
notice = ircmsgs.notice
|
conf.registerPlugin('Ctcp')
|
||||||
|
conf.registerGlobalValue(conf.supybot.abuse.flood, 'ctcp',
|
||||||
|
registry.Boolean(True, """Determines whether the bot will defend itself
|
||||||
|
against CTCP flooding."""))
|
||||||
|
conf.registerGlobalValue(conf.supybot.abuse.flood.ctcp, 'maximum',
|
||||||
|
registry.PositiveInteger(5, """Determines how many CTCP messages (not
|
||||||
|
including actions) the bot will reply to from a given user in a minute.
|
||||||
|
If a user sends more than this many CTCP messages in a 60 second period,
|
||||||
|
the bot will ignore CTCP messages from this user for
|
||||||
|
supybot.abuse.flood.ctcp.punishment seconds."""))
|
||||||
|
conf.registerGlobalValue(conf.supybot.abuse.flood.ctcp, 'punishment',
|
||||||
|
registry.PositiveInteger(300, """Determines how many seconds the bot will
|
||||||
|
ignore CTCP messages from users who flood it with CTCP messages."""))
|
||||||
|
|
||||||
class Ctcp(callbacks.PrivmsgRegexp):
|
class Ctcp(callbacks.PrivmsgRegexp):
|
||||||
public = False
|
public = False
|
||||||
|
def __init__(self):
|
||||||
|
self.__parent = super(Ctcp, self)
|
||||||
|
self.__parent.__init__()
|
||||||
|
self.ignores = ircutils.IrcDict()
|
||||||
|
self.floods = ircutils.FloodQueue(60)
|
||||||
|
|
||||||
|
def callCommand(self, name, irc, msg, *L, **kwargs):
|
||||||
|
if conf.supybot.abuse.flood.ctcp():
|
||||||
|
now = time.time()
|
||||||
|
for (ignore, expiration) in self.ignores.items():
|
||||||
|
if expiration < now:
|
||||||
|
del self.ignores[ignore]
|
||||||
|
elif ircutils.hostmaskPatternEqual(ignore, msg.prefix):
|
||||||
|
return
|
||||||
|
self.floods.enqueue(msg)
|
||||||
|
max = conf.supybot.abuse.flood.ctcp.maximum()
|
||||||
|
if self.floods.len(msg) > max:
|
||||||
|
expires = conf.supybot.abuse.flood.ctcp.punishment()
|
||||||
|
self.log.warning('Apparent CTCP flood from %s, '
|
||||||
|
'ignoring CTCP messages for %s seconds.',
|
||||||
|
msg.prefix, expires)
|
||||||
|
ignoreMask = '*!%s@%s' % (msg.user, msg.host)
|
||||||
|
self.ignores[ignoreMask] = now + expires
|
||||||
|
return
|
||||||
|
self.__parent.callCommand(name, irc, msg, *L, **kwargs)
|
||||||
|
|
||||||
|
def _reply(self, irc, msg, s):
|
||||||
|
s = '\x01%s\x01' % s
|
||||||
|
irc.reply(s, notice=True, private=True, to=msg.nick)
|
||||||
|
|
||||||
def ping(self, irc, msg, match):
|
def ping(self, irc, msg, match):
|
||||||
"\x01PING (.*)\x01"
|
"\x01PING (.*)\x01"
|
||||||
self.log.info('Received CTCP PING from %s', msg.prefix)
|
self.log.info('Received CTCP PING from %s', msg.prefix)
|
||||||
irc.queueMsg(notice(msg.nick, '\x01PING %s\x01' % match.group(1)))
|
self._reply(irc, msg, 'PING %s' % match.group(1))
|
||||||
|
|
||||||
def version(self, irc, msg, match):
|
def version(self, irc, msg, match):
|
||||||
"\x01VERSION\x01"
|
"\x01VERSION\x01"
|
||||||
self.log.info('Received CTCP VERSION from %s', msg.prefix)
|
self.log.info('Received CTCP VERSION from %s', msg.prefix)
|
||||||
s = '\x01VERSION Supybot %s\x01' % conf.version
|
self._reply(irc, msg, 'VERSION Supybot %s' % conf.version)
|
||||||
irc.queueMsg(notice(msg.nick, s))
|
|
||||||
|
|
||||||
def userinfo(self, irc, msg, match):
|
def userinfo(self, irc, msg, match):
|
||||||
"\x01USERINFO\x01"
|
"\x01USERINFO\x01"
|
||||||
self.log.info('Received CTCP USERINFO from %s', msg.prefix)
|
self.log.info('Received CTCP USERINFO from %s', msg.prefix)
|
||||||
irc.queueMsg(notice(msg.nick, '\x01USERINFO\x01'))
|
self._reply(irc, msg, 'USERINFO')
|
||||||
|
|
||||||
def time(self, irc, msg, match):
|
def time(self, irc, msg, match):
|
||||||
"\x01TIME\x01"
|
"\x01TIME\x01"
|
||||||
self.log.info('Received CTCP TIME from %s' % msg.prefix)
|
self.log.info('Received CTCP TIME from %s' % msg.prefix)
|
||||||
irc.queueMsg(notice(msg.nick, '\x01%s\x01' % time.ctime()))
|
self._reply(irc, msg, time.ctime())
|
||||||
|
|
||||||
def finger(self, irc, msg, match):
|
def finger(self, irc, msg, match):
|
||||||
"\x01FINGER\x01"
|
"\x01FINGER\x01"
|
||||||
self.log.info('Received CTCP FINGER from %s' % msg.prefix)
|
self.log.info('Received CTCP FINGER from %s' % msg.prefix)
|
||||||
s = '\x01Supybot, the best Python bot in existence!\x01'
|
irc._reply(irc, msg, 'Supybot, the best Python IRC bot in existence!')
|
||||||
irc.queueMsg(notice(msg.nick, s))
|
|
||||||
|
|
||||||
def source(self, irc, msg, match):
|
def source(self, irc, msg, match):
|
||||||
"\x01SOURCE\x01"
|
"\x01SOURCE\x01"
|
||||||
self.log.info('Received CTCP SOURCE from %s' % msg.prefix)
|
self.log.info('Received CTCP SOURCE from %s' % msg.prefix)
|
||||||
s = 'http://www.sourceforge.net/projects/supybot/'
|
self._reply(irc, msg, 'http://www.sourceforge.net/projects/supybot/')
|
||||||
irc.queueMsg(notice(msg.nick, s))
|
|
||||||
|
|
||||||
Class = Ctcp
|
Class = Ctcp
|
||||||
# vim:set shiftwidth=4 tabstop=8 expandtab textwidth=78:
|
# vim:set shiftwidth=4 tabstop=8 expandtab textwidth=78:
|
||||||
|
@ -84,9 +84,9 @@ class Weather(callbacks.Privmsg):
|
|||||||
the name of 'weather' which should override this help."""
|
the name of 'weather' which should override this help."""
|
||||||
weatherCommands = ['ham', 'cnn', 'wunder']
|
weatherCommands = ['ham', 'cnn', 'wunder']
|
||||||
threaded = True
|
threaded = True
|
||||||
def callCommand(self, method, irc, msg, *L, **kwargs):
|
def callCommand(self, name, irc, msg, *L, **kwargs):
|
||||||
try:
|
try:
|
||||||
super(Weather, self).callCommand(method, irc, msg, *L, **kwargs)
|
super(Weather, self).callCommand(name, irc, msg, *L, **kwargs)
|
||||||
except webutils.WebError, e:
|
except webutils.WebError, e:
|
||||||
irc.error(str(e))
|
irc.error(str(e))
|
||||||
|
|
||||||
|
@ -133,10 +133,10 @@ class HangmanGame:
|
|||||||
|
|
||||||
|
|
||||||
class Words(callbacks.Privmsg):
|
class Words(callbacks.Privmsg):
|
||||||
def callCommand(self, command, irc, msg, args, *L, **kw):
|
def callCommand(self, name, irc, msg, args, *L, **kw):
|
||||||
# We'll catch the IOError here.
|
# We'll catch the IOError here.
|
||||||
try:
|
try:
|
||||||
super(Words, self).callCommand(command, irc, msg, args, *L, **kw)
|
super(Words, self).callCommand(name, irc, msg, args, *L, **kw)
|
||||||
except EnvironmentError, e:
|
except EnvironmentError, e:
|
||||||
irc.error('I couldn\'t open the words file. This plugin expects '
|
irc.error('I couldn\'t open the words file. This plugin expects '
|
||||||
'a words file (i.e., a file with one word per line, in '
|
'a words file (i.e., a file with one word per line, in '
|
||||||
|
38
src/Admin.py
38
src/Admin.py
@ -60,7 +60,8 @@ import supybot.callbacks as callbacks
|
|||||||
class Admin(privmsgs.CapabilityCheckingPrivmsg):
|
class Admin(privmsgs.CapabilityCheckingPrivmsg):
|
||||||
capability = 'admin'
|
capability = 'admin'
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
privmsgs.CapabilityCheckingPrivmsg.__init__(self)
|
self.__parent = super(Admin, self)
|
||||||
|
self.__parent.__init__()
|
||||||
self.joins = {}
|
self.joins = {}
|
||||||
self.pendingNickChanges = {}
|
self.pendingNickChanges = {}
|
||||||
|
|
||||||
@ -157,7 +158,7 @@ class Admin(privmsgs.CapabilityCheckingPrivmsg):
|
|||||||
else:
|
else:
|
||||||
channels.append(channel)
|
channels.append(channel)
|
||||||
if not ircutils.isChannel(channel):
|
if not ircutils.isChannel(channel):
|
||||||
irc.error('%r is not a valid channel.' % channel)
|
irc.errorInvalid('channel', channel)
|
||||||
return
|
return
|
||||||
conf.supybot.channels().add(original)
|
conf.supybot.channels().add(original)
|
||||||
maxchannels = irc.state.supported.get('maxchannels', sys.maxint)
|
maxchannels = irc.state.supported.get('maxchannels', sys.maxint)
|
||||||
@ -237,7 +238,7 @@ class Admin(privmsgs.CapabilityCheckingPrivmsg):
|
|||||||
irc.queueMsg(ircmsgs.nick(nick))
|
irc.queueMsg(ircmsgs.nick(nick))
|
||||||
self.pendingNickChanges[irc.getRealIrc()] = irc
|
self.pendingNickChanges[irc.getRealIrc()] = irc
|
||||||
else:
|
else:
|
||||||
irc.error('That\'s not a valid nick.')
|
irc.errorInvalid('nick', nick)
|
||||||
else:
|
else:
|
||||||
irc.reply(irc.nick)
|
irc.reply(irc.nick)
|
||||||
|
|
||||||
@ -351,21 +352,33 @@ class Admin(privmsgs.CapabilityCheckingPrivmsg):
|
|||||||
irc.error(s)
|
irc.error(s)
|
||||||
|
|
||||||
def ignore(self, irc, msg, args):
|
def ignore(self, irc, msg, args):
|
||||||
"""<hostmask|nick>
|
"""<hostmask|nick> [<expires>]
|
||||||
|
|
||||||
Ignores <hostmask> or, if a nick is given, ignores whatever hostmask
|
Ignores <hostmask> or, if a nick is given, ignores whatever hostmask
|
||||||
that nick is currently using.
|
that nick is currently using. <expires> is a "seconds from now" value
|
||||||
|
that determines when the ignore will expire; if, for instance, you wish
|
||||||
|
for the ignore to expire in an hour, you could give an <expires> of
|
||||||
|
3600. If no <expires> is given, the ignore will never automatically
|
||||||
|
expire.
|
||||||
"""
|
"""
|
||||||
arg = privmsgs.getArgs(args)
|
(nickOrHostmask, expires) = privmsgs.getArgs(args, optional=1)
|
||||||
if ircutils.isUserHostmask(arg):
|
if ircutils.isUserHostmask(nickOrHostmask):
|
||||||
hostmask = arg
|
hostmask = nickOrHostmask
|
||||||
else:
|
else:
|
||||||
try:
|
try:
|
||||||
hostmask = irc.state.nickToHostmask(arg)
|
hostmask = irc.state.nickToHostmask(nickOrHostmask)
|
||||||
except KeyError:
|
except KeyError:
|
||||||
irc.error('I can\'t find a hostmask for %s' % arg)
|
irc.error('I can\'t find a hostmask for %s.' % nickOrHostmask)
|
||||||
return
|
return
|
||||||
ircdb.ignores.addHostmask(hostmask)
|
if expires:
|
||||||
|
try:
|
||||||
|
expires = int(float(expires))
|
||||||
|
expires += int(time.time())
|
||||||
|
except ValueError:
|
||||||
|
irc.errorInvalid('number of seconds', expires, Raise=True)
|
||||||
|
else:
|
||||||
|
expires = 0
|
||||||
|
ircdb.ignores.add(hostmask, expires)
|
||||||
irc.replySuccess()
|
irc.replySuccess()
|
||||||
|
|
||||||
def unignore(self, irc, msg, args):
|
def unignore(self, irc, msg, args):
|
||||||
@ -384,7 +397,7 @@ class Admin(privmsgs.CapabilityCheckingPrivmsg):
|
|||||||
irc.error('I can\'t find a hostmask for %s' % arg)
|
irc.error('I can\'t find a hostmask for %s' % arg)
|
||||||
return
|
return
|
||||||
try:
|
try:
|
||||||
ircdb.ignores.removeHostmask(hostmask)
|
ircdb.ignores.remove(hostmask)
|
||||||
irc.replySuccess()
|
irc.replySuccess()
|
||||||
except KeyError:
|
except KeyError:
|
||||||
irc.error('%s wasn\'t in the ignores database.' % hostmask)
|
irc.error('%s wasn\'t in the ignores database.' % hostmask)
|
||||||
@ -394,6 +407,7 @@ class Admin(privmsgs.CapabilityCheckingPrivmsg):
|
|||||||
|
|
||||||
Returns the hostmasks currently being globally ignored.
|
Returns the hostmasks currently being globally ignored.
|
||||||
"""
|
"""
|
||||||
|
# XXX Add the expirations.
|
||||||
if ircdb.ignores.hostmasks:
|
if ircdb.ignores.hostmasks:
|
||||||
irc.reply(utils.commaAndify(imap(repr, ircdb.ignores.hostmasks)))
|
irc.reply(utils.commaAndify(imap(repr, ircdb.ignores.hostmasks)))
|
||||||
else:
|
else:
|
||||||
|
@ -441,23 +441,34 @@ class Channel(callbacks.Privmsg):
|
|||||||
unlobotomize = privmsgs.checkChannelCapability(unlobotomize, 'op')
|
unlobotomize = privmsgs.checkChannelCapability(unlobotomize, 'op')
|
||||||
|
|
||||||
def permban(self, irc, msg, args, channel):
|
def permban(self, irc, msg, args, channel):
|
||||||
"""[<channel>] <nick|hostmask>
|
"""[<channel>] <nick|hostmask> [<expires>]
|
||||||
|
|
||||||
If you have the #channel,op capability, this will effect a permanent
|
If you have the #channel,op capability, this will effect a permanent
|
||||||
(persistent) ban from interacting with the bot on the given <hostmask>
|
(persistent) ban from interacting with the bot on the given <hostmask>
|
||||||
(or the current hostmask associated with <nick>. <channel> is only
|
(or the current hostmask associated with <nick>. Other plugins may
|
||||||
necessary if the message isn't sent in the channel itself.
|
enforce this ban by actually banning users with matching hostmasks when
|
||||||
|
they join. <expires> is an optional argument specifying when (in
|
||||||
|
"seconds from now") the ban should expire; if none is given, the ban
|
||||||
|
will never automatically expire. <channel> is only necessary if the
|
||||||
|
message isn't sent in the channel itself.
|
||||||
"""
|
"""
|
||||||
arg = privmsgs.getArgs(args)
|
(nickOrHostmask, expires) = privmsgs.getArgs(args, optional=1)
|
||||||
if ircutils.isNick(arg):
|
if ircutils.isNick(nickOrHostmask):
|
||||||
banmask = ircutils.banmask(irc.state.nickToHostmask(arg))
|
banmask = ircutils.banmask(irc.state.nickToHostmask(nickOrHostmask))
|
||||||
elif ircutils.isUserHostmask(arg):
|
elif ircutils.isUserHostmask(nickOrHostmask):
|
||||||
banmask = arg
|
banmask = nickOrHostmask
|
||||||
else:
|
else:
|
||||||
irc.error('That\'s not a valid nick or hostmask.')
|
irc.errorInvalid('nick or hostmask', nickOrHostmask, Raise=True)
|
||||||
return
|
if expires:
|
||||||
|
try:
|
||||||
|
expires = int(float(expires))
|
||||||
|
expires += int(time.time())
|
||||||
|
except ValueError:
|
||||||
|
irc.errorInvalid('number of seconds',nickOrHostmask,Raise=True)
|
||||||
|
else:
|
||||||
|
expires = 0
|
||||||
c = ircdb.channels.getChannel(channel)
|
c = ircdb.channels.getChannel(channel)
|
||||||
c.addBan(banmask)
|
c.addBan(banmask, expires)
|
||||||
ircdb.channels.setChannel(channel, c)
|
ircdb.channels.setChannel(channel, c)
|
||||||
irc.replySuccess()
|
irc.replySuccess()
|
||||||
permban = privmsgs.checkChannelCapability(permban, 'op')
|
permban = privmsgs.checkChannelCapability(permban, 'op')
|
||||||
@ -483,6 +494,7 @@ class Channel(callbacks.Privmsg):
|
|||||||
If you have the #channel,op capability, this will show you the
|
If you have the #channel,op capability, this will show you the
|
||||||
current bans on #channel.
|
current bans on #channel.
|
||||||
"""
|
"""
|
||||||
|
# XXX Add the expirations.
|
||||||
c = ircdb.channels.getChannel(channel)
|
c = ircdb.channels.getChannel(channel)
|
||||||
if c.bans:
|
if c.bans:
|
||||||
irc.reply(utils.commaAndify(map(utils.dqrepr, c.bans)))
|
irc.reply(utils.commaAndify(map(utils.dqrepr, c.bans)))
|
||||||
@ -491,23 +503,32 @@ class Channel(callbacks.Privmsg):
|
|||||||
permbans = privmsgs.checkChannelCapability(permbans, 'op')
|
permbans = privmsgs.checkChannelCapability(permbans, 'op')
|
||||||
|
|
||||||
def ignore(self, irc, msg, args, channel):
|
def ignore(self, irc, msg, args, channel):
|
||||||
"""[<channel>] <nick|hostmask>
|
"""[<channel>] <nick|hostmask> [<expires>]
|
||||||
|
|
||||||
If you have the #channel,op capability, this will set a permanent
|
If you have the #channel,op capability, this will set a permanent
|
||||||
(persistent) ignore on <hostmask> or the hostmask currently associated
|
(persistent) ignore on <hostmask> or the hostmask currently associated
|
||||||
with <nick>. <channel> is only necessary if the message isn't sent in
|
with <nick>. <expires> is an optional argument specifying when (in
|
||||||
the channel itself.
|
"seconds from now") the ignore will expire; if it isn't given, the
|
||||||
|
ignore will never automatically expire. <channel> is only necessary
|
||||||
|
if the message isn't sent in the channel itself.
|
||||||
"""
|
"""
|
||||||
arg = privmsgs.getArgs(args)
|
(nickOrHostmask, expires) = privmsgs.getArgs(args, optional=1)
|
||||||
if ircutils.isNick(arg):
|
if ircutils.isNick(nickOrHostmask):
|
||||||
banmask = ircutils.banmask(irc.state.nickToHostmask(arg))
|
banmask = ircutils.banmask(irc.state.nickToHostmask(nickOrHostmask))
|
||||||
elif ircutils.isUserHostmask(arg):
|
elif ircutils.isUserHostmask(nickOrHostmask):
|
||||||
banmask = arg
|
banmask = nickOrHostmask
|
||||||
else:
|
else:
|
||||||
irc.error('That\'s not a valid nick or hostmask.')
|
irc.errorInvalid('nick or hostmask', nickOrHostmask, Raise=True)
|
||||||
return
|
if expires:
|
||||||
|
try:
|
||||||
|
expires = int(float(expires))
|
||||||
|
expires += int(time.time())
|
||||||
|
except ValueError:
|
||||||
|
irc.errorInvalid('number of seconds',nickOrHostmask,Raise=True)
|
||||||
|
else:
|
||||||
|
expires = 0
|
||||||
c = ircdb.channels.getChannel(channel)
|
c = ircdb.channels.getChannel(channel)
|
||||||
c.addIgnore(banmask)
|
c.addIgnore(banmask, expires)
|
||||||
ircdb.channels.setChannel(channel, c)
|
ircdb.channels.setChannel(channel, c)
|
||||||
irc.replySuccess()
|
irc.replySuccess()
|
||||||
ignore = privmsgs.checkChannelCapability(ignore, 'op')
|
ignore = privmsgs.checkChannelCapability(ignore, 'op')
|
||||||
@ -533,6 +554,7 @@ 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.
|
||||||
"""
|
"""
|
||||||
|
# XXX Add the expirations.
|
||||||
channelarg = privmsgs.getArgs(args, required=0, optional=1)
|
channelarg = privmsgs.getArgs(args, required=0, optional=1)
|
||||||
channel = channelarg or channel
|
channel = channelarg or channel
|
||||||
c = ircdb.channels.getChannel(channel)
|
c = ircdb.channels.getChannel(channel)
|
||||||
|
@ -101,7 +101,7 @@ class Config(callbacks.Privmsg):
|
|||||||
try:
|
try:
|
||||||
super(Config, self).callCommand(name, irc, msg, *L, **kwargs)
|
super(Config, self).callCommand(name, irc, msg, *L, **kwargs)
|
||||||
except InvalidRegistryName, e:
|
except InvalidRegistryName, e:
|
||||||
irc.error('%r is not a valid configuration variable.' % e.args[0])
|
irc.errorInvalid('configuration variable', e.args[0])
|
||||||
except registry.InvalidRegistryValue, e:
|
except registry.InvalidRegistryValue, e:
|
||||||
irc.error(str(e))
|
irc.error(str(e))
|
||||||
|
|
||||||
|
54
src/Misc.py
54
src/Misc.py
@ -66,19 +66,50 @@ conf.registerGlobalValue(conf.supybot.plugins.Misc, 'listPrivatePlugins',
|
|||||||
|
|
||||||
class Misc(callbacks.Privmsg):
|
class Misc(callbacks.Privmsg):
|
||||||
priority = sys.maxint
|
priority = sys.maxint
|
||||||
|
def __init__(self):
|
||||||
|
super(Misc, self).__init__()
|
||||||
|
timeout = conf.supybot.abuse.flood.command.invalid
|
||||||
|
self.invalidCommands = ircutils.FloodQueue(timeout)
|
||||||
|
|
||||||
def invalidCommand(self, irc, msg, tokens):
|
def invalidCommand(self, irc, msg, tokens):
|
||||||
self.log.debug('Misc.invalidCommand called (tokens %s)', tokens)
|
self.log.debug('Misc.invalidCommand called (tokens %s)', tokens)
|
||||||
if conf.supybot.reply.whenNotCommand():
|
# First, we check for invalidCommand floods. This is rightfully done
|
||||||
|
# here since this will be the last invalidCommand called, and thus it
|
||||||
|
# will only be called if this is *truly* an invalid command.
|
||||||
|
maximum = conf.supybot.abuse.flood.command.invalid.maximum()
|
||||||
|
self.invalidCommands.enqueue(msg)
|
||||||
|
if self.invalidCommands.len(msg) > maximum and \
|
||||||
|
not ircdb.checkCapability(msg.prefix, 'owner'):
|
||||||
|
punishment = conf.supybot.abuse.flood.command.invalid.punishment()
|
||||||
|
banmask = '*!%s@%s' % (msg.user, msg.host)
|
||||||
|
self.log.info('Ignoring %s for %s seconds due to an apparent '
|
||||||
|
'invalid command flood.', banmask, punishment)
|
||||||
|
if tokens and tokens[0] == 'Error:':
|
||||||
|
self.log.warning('Apparent error loop with another Supybot '
|
||||||
|
'observed at %s. Consider ignoring this bot '
|
||||||
|
'permanently.', log.timestamp())
|
||||||
|
ircdb.ignores.add(banmask, time.time() + punishment)
|
||||||
|
irc.reply('You\'ve given me %s invalid commands within the last '
|
||||||
|
'minute; I\'m now ignoring you for %s.' %
|
||||||
|
(maximum, utils.timeElapsed(punishment)))
|
||||||
|
return
|
||||||
|
# Now, for normal handling.
|
||||||
|
channel = msg.args[0]
|
||||||
|
if conf.get(conf.supybot.reply.whenNotCommand, channel):
|
||||||
command = tokens and tokens[0] or ''
|
command = tokens and tokens[0] or ''
|
||||||
irc.error('%r is not a valid command.' % command)
|
irc.errorInvalid('command', command)
|
||||||
else:
|
else:
|
||||||
if tokens:
|
if tokens:
|
||||||
# echo [] will get us an empty token set, but there's no need
|
# echo [] will get us an empty token set, but there's no need
|
||||||
# to log this in that case anyway, it being a nested command.
|
# to log this in that case anyway, it being a nested command.
|
||||||
self.log.info('Not replying to %s, not a command.' % tokens[0])
|
self.log.info('Not replying to %s, not a command.' % tokens[0])
|
||||||
if not isinstance(irc.irc, irclib.Irc):
|
if not isinstance(irc.irc, irclib.Irc):
|
||||||
brackets = conf.supybot.reply.brackets.get(msg.args[0])()
|
brackets = conf.get(conf.supybot.reply.brackets, channel)
|
||||||
irc.reply(''.join([brackets[0],' '.join(tokens), brackets[1]]))
|
if brackets:
|
||||||
|
(left, right) = brackets
|
||||||
|
irc.reply(left + ' '.join(tokens) + right)
|
||||||
|
else:
|
||||||
|
pass # Let's just do nothing, I can't think of better.
|
||||||
|
|
||||||
def list(self, irc, msg, args):
|
def list(self, irc, msg, args):
|
||||||
"""[--private] [<module name>]
|
"""[--private] [<module name>]
|
||||||
@ -505,8 +536,7 @@ class Misc(callbacks.Privmsg):
|
|||||||
try:
|
try:
|
||||||
i = int(s)
|
i = int(s)
|
||||||
except ValueError:
|
except ValueError:
|
||||||
irc.error('Invalid argument: %s' % arg)
|
irc.errorInvalid('argument', arg, Raise=True)
|
||||||
return
|
|
||||||
if kind == 'y':
|
if kind == 'y':
|
||||||
seconds += i*31536000
|
seconds += i*31536000
|
||||||
elif kind == 'w':
|
elif kind == 'w':
|
||||||
@ -522,10 +552,10 @@ class Misc(callbacks.Privmsg):
|
|||||||
irc.reply(str(seconds))
|
irc.reply(str(seconds))
|
||||||
|
|
||||||
def tell(self, irc, msg, args):
|
def tell(self, irc, msg, args):
|
||||||
"""<nick|channel> <text>
|
"""<nick> <text>
|
||||||
|
|
||||||
Tells the <nick|channel> whatever <text> is. Use nested commands to
|
Tells the <nick> whatever <text> is. Use nested commands to your
|
||||||
your benefit here.
|
benefit here.
|
||||||
"""
|
"""
|
||||||
(target, text) = privmsgs.getArgs(args, required=2)
|
(target, text) = privmsgs.getArgs(args, required=2)
|
||||||
if target.lower() == 'me':
|
if target.lower() == 'me':
|
||||||
@ -534,11 +564,9 @@ class Misc(callbacks.Privmsg):
|
|||||||
irc.error('Dude, just give the command. No need for the tell.')
|
irc.error('Dude, just give the command. No need for the tell.')
|
||||||
return
|
return
|
||||||
elif not ircutils.isNick(target):
|
elif not ircutils.isNick(target):
|
||||||
irc.error('%s is not a valid nick.' % target)
|
irc.errorInvalid('nick', target, Raise=True)
|
||||||
return
|
|
||||||
elif ircutils.nickEqual(target, irc.nick):
|
elif ircutils.nickEqual(target, irc.nick):
|
||||||
irc.error('You just told me, why should I tell myself?')
|
irc.error('You just told me, why should I tell myself?',Raise=True)
|
||||||
return
|
|
||||||
elif target not in irc.state.nicksToHostmasks and \
|
elif target not in irc.state.nicksToHostmasks and \
|
||||||
not ircdb.checkCapability(msg.prefix, 'owner'):
|
not ircdb.checkCapability(msg.prefix, 'owner'):
|
||||||
# We'll let owners do this.
|
# We'll let owners do this.
|
||||||
|
54
src/Owner.py
54
src/Owner.py
@ -63,6 +63,7 @@ import supybot.ircutils as ircutils
|
|||||||
import supybot.privmsgs as privmsgs
|
import supybot.privmsgs as privmsgs
|
||||||
import supybot.registry as registry
|
import supybot.registry as registry
|
||||||
import supybot.callbacks as callbacks
|
import supybot.callbacks as callbacks
|
||||||
|
import supybot.structures as structures
|
||||||
|
|
||||||
class Deprecated(ImportError):
|
class Deprecated(ImportError):
|
||||||
pass
|
pass
|
||||||
@ -131,7 +132,8 @@ conf.supybot.plugins.Owner.register('public', registry.Boolean(True,
|
|||||||
# supybot.commands.
|
# supybot.commands.
|
||||||
###
|
###
|
||||||
|
|
||||||
conf.registerGroup(conf.supybot.commands, 'defaultPlugins')
|
conf.registerGroup(conf.supybot.commands, 'defaultPlugins',
|
||||||
|
orderAlphabetically=True)
|
||||||
conf.supybot.commands.defaultPlugins.help = utils.normalizeWhitespace("""
|
conf.supybot.commands.defaultPlugins.help = utils.normalizeWhitespace("""
|
||||||
Determines what commands have default plugins set, and which plugins are set to
|
Determines what commands have default plugins set, and which plugins are set to
|
||||||
be the default for each of those commands.""".strip())
|
be the default for each of those commands.""".strip())
|
||||||
@ -185,10 +187,13 @@ class Owner(privmsgs.CapabilityCheckingPrivmsg):
|
|||||||
capability = 'owner'
|
capability = 'owner'
|
||||||
_srcPlugins = ircutils.IrcSet(('Admin', 'Channel', 'Config',
|
_srcPlugins = ircutils.IrcSet(('Admin', 'Channel', 'Config',
|
||||||
'Misc', 'Owner', 'User'))
|
'Misc', 'Owner', 'User'))
|
||||||
def __init__(self):
|
def __init__(self, *args, **kwargs):
|
||||||
callbacks.Privmsg.__init__(self)
|
self.__parent = super(Owner, self)
|
||||||
|
self.__parent.__init__()
|
||||||
# Setup log object/command.
|
# Setup log object/command.
|
||||||
self.log = LogProxy(self.log)
|
self.log = LogProxy(self.log)
|
||||||
|
# Setup command flood detection.
|
||||||
|
self.commands = ircutils.FloodQueue(60)
|
||||||
# Setup exec command.
|
# Setup exec command.
|
||||||
setattr(self.__class__, 'exec', self.__class__._exec)
|
setattr(self.__class__, 'exec', self.__class__._exec)
|
||||||
# Setup Irc objects, connected to networks. If world.ircs is already
|
# Setup Irc objects, connected to networks. If world.ircs is already
|
||||||
@ -236,14 +241,14 @@ class Owner(privmsgs.CapabilityCheckingPrivmsg):
|
|||||||
return None
|
return None
|
||||||
return msg
|
return msg
|
||||||
|
|
||||||
def isCommand(self, methodName):
|
def isCommand(self, name):
|
||||||
return methodName == 'log' or \
|
return name == 'log' or \
|
||||||
privmsgs.CapabilityCheckingPrivmsg.isCommand(self, methodName)
|
self.__parent.isCommand(name)
|
||||||
|
|
||||||
def reset(self):
|
def reset(self):
|
||||||
# This has to be done somewhere, I figure here is as good place as any.
|
# This has to be done somewhere, I figure here is as good place as any.
|
||||||
callbacks.Privmsg._mores.clear()
|
callbacks.Privmsg._mores.clear()
|
||||||
privmsgs.CapabilityCheckingPrivmsg.reset(self)
|
self.__parent.reset()
|
||||||
|
|
||||||
def do001(self, irc, msg):
|
def do001(self, irc, msg):
|
||||||
self.log.info('Loading plugins.')
|
self.log.info('Loading plugins.')
|
||||||
@ -416,8 +421,8 @@ class Owner(privmsgs.CapabilityCheckingPrivmsg):
|
|||||||
except Exception, e:
|
except Exception, e:
|
||||||
irc.reply(utils.exnToString(e))
|
irc.reply(utils.exnToString(e))
|
||||||
else:
|
else:
|
||||||
# This should never happen, so I haven't bothered updating
|
# There's a potential that allowEval got changed after we were
|
||||||
# this error string to say --allow-eval.
|
# loaded. Let's be extra-special-safe.
|
||||||
irc.error('You must run Supybot with the --allow-eval '
|
irc.error('You must run Supybot with the --allow-eval '
|
||||||
'option for this command to be enabled.')
|
'option for this command to be enabled.')
|
||||||
|
|
||||||
@ -434,7 +439,8 @@ class Owner(privmsgs.CapabilityCheckingPrivmsg):
|
|||||||
except Exception, e:
|
except Exception, e:
|
||||||
irc.reply(utils.exnToString(e))
|
irc.reply(utils.exnToString(e))
|
||||||
else:
|
else:
|
||||||
# This should never happen.
|
# There's a potential that allowEval got changed after we were
|
||||||
|
# loaded. Let's be extra-special-safe.
|
||||||
irc.error('You must run Supybot with the --allow-eval '
|
irc.error('You must run Supybot with the --allow-eval '
|
||||||
'option for this command to be enabled.')
|
'option for this command to be enabled.')
|
||||||
else:
|
else:
|
||||||
@ -481,13 +487,11 @@ class Owner(privmsgs.CapabilityCheckingPrivmsg):
|
|||||||
s = 'I don\'t have a default plugin set for that command.'
|
s = 'I don\'t have a default plugin set for that command.'
|
||||||
irc.error(s)
|
irc.error(s)
|
||||||
elif not cbs:
|
elif not cbs:
|
||||||
irc.error('That\'s not a valid command.')
|
irc.errorInvalid('command', command, Raise=True)
|
||||||
return
|
|
||||||
elif plugin:
|
elif plugin:
|
||||||
cb = irc.getCallback(plugin)
|
cb = irc.getCallback(plugin)
|
||||||
if cb is None:
|
if cb is None:
|
||||||
irc.error('That\'s not a valid plugin.')
|
irc.errorInvalid('plugin', plugin, Raise=True)
|
||||||
return
|
|
||||||
registerDefaultPlugin(command, plugin)
|
registerDefaultPlugin(command, plugin)
|
||||||
irc.replySuccess()
|
irc.replySuccess()
|
||||||
else:
|
else:
|
||||||
@ -568,7 +572,7 @@ class Owner(privmsgs.CapabilityCheckingPrivmsg):
|
|||||||
utils.nItems('line', len(linecache.cache)))
|
utils.nItems('line', len(linecache.cache)))
|
||||||
linecache.clearcache()
|
linecache.clearcache()
|
||||||
sys.exc_clear()
|
sys.exc_clear()
|
||||||
collected = world.upkeep(scheduleNext=False)
|
collected = world.upkeep()
|
||||||
if gc.garbage:
|
if gc.garbage:
|
||||||
L.append('Garbage! %r.' % gc.garbage)
|
L.append('Garbage! %r.' % gc.garbage)
|
||||||
L.append('%s collected.' % utils.nItems('object', collected))
|
L.append('%s collected.' % utils.nItems('object', collected))
|
||||||
@ -686,7 +690,7 @@ class Owner(privmsgs.CapabilityCheckingPrivmsg):
|
|||||||
irc.error('I couldn\'t reconnect. You should restart me instead.')
|
irc.error('I couldn\'t reconnect. You should restart me instead.')
|
||||||
|
|
||||||
def defaultcapability(self, irc, msg, args):
|
def defaultcapability(self, irc, msg, args):
|
||||||
"""<add|remove> <capability>
|
"""{add|remove} <capability>
|
||||||
|
|
||||||
Adds or removes (according to the first argument) <capability> from the
|
Adds or removes (according to the first argument) <capability> from the
|
||||||
default capabilities given to users (the configuration variable
|
default capabilities given to users (the configuration variable
|
||||||
@ -709,8 +713,8 @@ class Owner(privmsgs.CapabilityCheckingPrivmsg):
|
|||||||
conf.supybot.capabilities().add(anticap)
|
conf.supybot.capabilities().add(anticap)
|
||||||
irc.replySuccess()
|
irc.replySuccess()
|
||||||
else:
|
else:
|
||||||
irc.error('That\'s not a valid action to take. Valid actions '
|
irc.errorInvalid('action to take', action,
|
||||||
'are "add" and "remove"')
|
'Valid actions include "add" and "remove".')
|
||||||
|
|
||||||
def disable(self, irc, msg, args):
|
def disable(self, irc, msg, args):
|
||||||
"""[<plugin>] <command>
|
"""[<plugin>] <command>
|
||||||
@ -751,7 +755,6 @@ class Owner(privmsgs.CapabilityCheckingPrivmsg):
|
|||||||
self._disabled.remove(command, plugin)
|
self._disabled.remove(command, plugin)
|
||||||
irc.replySuccess()
|
irc.replySuccess()
|
||||||
except KeyError:
|
except KeyError:
|
||||||
raise
|
|
||||||
irc.error('That command wasn\'t disabled.')
|
irc.error('That command wasn\'t disabled.')
|
||||||
|
|
||||||
def rename(self, irc, msg, args):
|
def rename(self, irc, msg, args):
|
||||||
@ -762,17 +765,14 @@ class Owner(privmsgs.CapabilityCheckingPrivmsg):
|
|||||||
(plugin, command, newName) = privmsgs.getArgs(args, required=3)
|
(plugin, command, newName) = privmsgs.getArgs(args, required=3)
|
||||||
name = callbacks.canonicalName(newName)
|
name = callbacks.canonicalName(newName)
|
||||||
if name != newName:
|
if name != newName:
|
||||||
irc.error('%s is a not a valid new command name. '
|
irc.errorInvalid('command name', name,
|
||||||
'Try making it lowercase and removing - and _.' %newName)
|
'Try making it lowercase and removing dashes '
|
||||||
return
|
'and underscores.', Raise=True)
|
||||||
cb = irc.getCallback(plugin)
|
cb = irc.getCallback(plugin)
|
||||||
if cb is None:
|
if cb is None:
|
||||||
irc.error('%s is not a valid plugin.' % plugin)
|
irc.errorInvalid('plugin', plugin, Raise=True)
|
||||||
return
|
|
||||||
if not cb.isCommand(command):
|
if not cb.isCommand(command):
|
||||||
s = '%s is not a valid command in the %s plugin.' % (name, plugin)
|
irc.errorInvalid('command in the %s plugin'%plugin,name,Raise=True)
|
||||||
irc.error(s)
|
|
||||||
return
|
|
||||||
if hasattr(cb, name):
|
if hasattr(cb, name):
|
||||||
irc.error('The %s plugin already has an attribute named %s.' %
|
irc.error('The %s plugin already has an attribute named %s.' %
|
||||||
(plugin, name))
|
(plugin, name))
|
||||||
|
26
src/User.py
26
src/User.py
@ -116,13 +116,12 @@ class User(callbacks.Privmsg):
|
|||||||
self._checkNotChannel(irc, msg, password)
|
self._checkNotChannel(irc, msg, password)
|
||||||
try:
|
try:
|
||||||
ircdb.users.getUserId(name)
|
ircdb.users.getUserId(name)
|
||||||
irc.error('That name is already assigned to someone.')
|
irc.error('That name is already assigned to someone.', Raise=True)
|
||||||
return
|
|
||||||
except KeyError:
|
except KeyError:
|
||||||
pass
|
pass
|
||||||
if ircutils.isUserHostmask(name):
|
if ircutils.isUserHostmask(name):
|
||||||
irc.error('Hostmasks aren\'t valid usernames.')
|
irc.errorInvalid('username', name,
|
||||||
return
|
'Hostmasks are not valid usernames.', Raise=True)
|
||||||
try:
|
try:
|
||||||
u = ircdb.users.getUser(msg.prefix)
|
u = ircdb.users.getUser(msg.prefix)
|
||||||
if u.checkCapability('owner'):
|
if u.checkCapability('owner'):
|
||||||
@ -208,7 +207,7 @@ class User(callbacks.Privmsg):
|
|||||||
if not name:
|
if not name:
|
||||||
name = msg.prefix
|
name = msg.prefix
|
||||||
if not ircutils.isUserHostmask(hostmask):
|
if not ircutils.isUserHostmask(hostmask):
|
||||||
irc.error('That\'s not a valid 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 '
|
||||||
'a user, then an at symbol (@), then a host. Feel '
|
'a user, then an at symbol (@), then a host. Feel '
|
||||||
'free to use wildcards (* and ?, which work just like '
|
'free to use wildcards (* and ?, which work just like '
|
||||||
@ -404,7 +403,10 @@ class User(callbacks.Privmsg):
|
|||||||
def unidentify(self, irc, msg, args):
|
def unidentify(self, irc, msg, args):
|
||||||
"""takes no arguments
|
"""takes no arguments
|
||||||
|
|
||||||
Un-identifies the user.
|
Un-identifies you. Note that this may not result in the desired
|
||||||
|
effect of causing the bot not to recognize you anymore, since you may
|
||||||
|
have added hostmasks to your user that can cause the bot to continue to
|
||||||
|
recognize you.
|
||||||
"""
|
"""
|
||||||
try:
|
try:
|
||||||
id = ircdb.users.getUserId(msg.prefix)
|
id = ircdb.users.getUserId(msg.prefix)
|
||||||
@ -450,11 +452,12 @@ class User(callbacks.Privmsg):
|
|||||||
irc.errorNotRegistered()
|
irc.errorNotRegistered()
|
||||||
if value == '':
|
if value == '':
|
||||||
value = not user.secure
|
value = not user.secure
|
||||||
elif value.lower() in ('true', 'false'):
|
elif value.lower() in ('true', 'on', 'enable'):
|
||||||
value = eval(value.capitalize())
|
value = True
|
||||||
|
elif value.lower() in ('false', 'off', 'disable'):
|
||||||
|
value = False
|
||||||
else:
|
else:
|
||||||
irc.error('%s is not a valid boolean value.' % value)
|
irc.errorInvalid('boolean value', value, Raise=True)
|
||||||
return
|
|
||||||
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
|
||||||
@ -518,8 +521,7 @@ class User(callbacks.Privmsg):
|
|||||||
## wrapper = Config.getWrapper(name)
|
## wrapper = Config.getWrapper(name)
|
||||||
## wrapper = wrapper.get(str(id))
|
## wrapper = wrapper.get(str(id))
|
||||||
## except InvalidRegistryValue, e:
|
## except InvalidRegistryValue, e:
|
||||||
## irc.error('%r is not a valid configuration variable.' % name)
|
## irc.errorInvalid('configuration variable', name, Raise=True)
|
||||||
## return
|
|
||||||
## if list:
|
## if list:
|
||||||
## pass
|
## pass
|
||||||
## else:
|
## else:
|
||||||
|
@ -29,6 +29,8 @@
|
|||||||
# POSSIBILITY OF SUCH DAMAGE.
|
# POSSIBILITY OF SUCH DAMAGE.
|
||||||
###
|
###
|
||||||
|
|
||||||
|
__revision__ = "$Id$"
|
||||||
|
|
||||||
import sys
|
import sys
|
||||||
import os.path
|
import os.path
|
||||||
|
|
||||||
@ -40,13 +42,27 @@ othersDir = os.path.join(installDir, 'others')
|
|||||||
sys.path.insert(0, srcDir)
|
sys.path.insert(0, srcDir)
|
||||||
sys.path.insert(0, othersDir)
|
sys.path.insert(0, othersDir)
|
||||||
|
|
||||||
class authors: # This is basically a bag.
|
class Author(object):
|
||||||
jemfinch = 'Jeremy Fincher (jemfinch) <jemfinch@users.sf.net>'
|
def __init__(self, name=None, nick=None, email=None, **kwargs):
|
||||||
jamessan = 'James Vega (jamessan) <jamessan@users.sf.net>'
|
self.__dict__.update(kwargs)
|
||||||
strike = 'Daniel DiPaolo (Strike) <ddipaolo@users.sf.net>'
|
self.name = name
|
||||||
baggins = 'William Robinson (baggins) <airbaggins@users.sf.net>'
|
self.nick = nick
|
||||||
skorobeus = 'Kevin Murphy (Skorobeus) <skoro@skoroworld.com>'
|
self.email = email
|
||||||
inkedmn = 'Brett Kelly (inkedmn) <inkedmn@users.sf.net>'
|
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
return '%s (%s) <%s>' % (self.name, self.nick, self.email)
|
||||||
|
|
||||||
|
class authors(object): # This is basically a bag.
|
||||||
|
jemfinch = Author('Jeremy Fincher', 'jemfinch', 'jemfinch@users.sf.net')
|
||||||
|
jamessan = Author('James Vega', 'jamessan', 'jamessan@users.sf.net')
|
||||||
|
strike = Author('Daniel DiPaolo', 'Strike', 'ddipaolo@users.sf.net')
|
||||||
|
baggins = Author('William Robinson', 'baggins', 'airbaggins@users.sf.net')
|
||||||
|
skorobeus = Author('Kevin Murphy', 'Skorobeus', 'skoro@skoroworld.com')
|
||||||
|
inkedmn = Author('Brett Kelly', 'inkedmn', 'inkedmn@users.sf.net')
|
||||||
|
bwp = Author('Brett Phipps', 'bwp', 'phippsb@gmail.com')
|
||||||
|
|
||||||
|
# Let's be somewhat safe about this.
|
||||||
|
def __getattr__(self, attr):
|
||||||
|
return Author('Unknown author', 'unknown', 'unknown@supybot.org')
|
||||||
|
|
||||||
# vim:set shiftwidth=4 tabstop=8 expandtab textwidth=78:
|
# vim:set shiftwidth=4 tabstop=8 expandtab textwidth=78:
|
||||||
|
202
src/callbacks.py
202
src/callbacks.py
@ -65,7 +65,7 @@ import supybot.ircmsgs as ircmsgs
|
|||||||
import supybot.ircutils as ircutils
|
import supybot.ircutils as ircutils
|
||||||
import supybot.registry as registry
|
import supybot.registry as registry
|
||||||
|
|
||||||
def addressed(nick, msg, prefixChars=None,
|
def addressed(nick, msg, prefixChars=None, nicks=None,
|
||||||
prefixStrings=None, whenAddressedByNick=None):
|
prefixStrings=None, whenAddressedByNick=None):
|
||||||
"""If msg is addressed to 'name', returns the portion after the address.
|
"""If msg is addressed to 'name', returns the portion after the address.
|
||||||
Otherwise returns the empty string.
|
Otherwise returns the empty string.
|
||||||
@ -88,7 +88,12 @@ def addressed(nick, msg, prefixChars=None,
|
|||||||
whenAddressedByNick = get(conf.supybot.reply.whenAddressedBy.nick)
|
whenAddressedByNick = get(conf.supybot.reply.whenAddressedBy.nick)
|
||||||
if prefixStrings is None:
|
if prefixStrings is None:
|
||||||
prefixStrings = get(conf.supybot.reply.whenAddressedBy.strings)
|
prefixStrings = get(conf.supybot.reply.whenAddressedBy.strings)
|
||||||
nick = ircutils.toLower(nick)
|
if nicks is None:
|
||||||
|
nicks = get(conf.supybot.reply.whenAddressedBy.nicks)
|
||||||
|
nicks = map(ircutils.toLower, nicks)
|
||||||
|
else:
|
||||||
|
nicks = list(nicks) # Just in case.
|
||||||
|
nicks.insert(0, ircutils.toLower(nick))
|
||||||
# Ok, let's see if it's a private message.
|
# Ok, let's see if it's a private message.
|
||||||
if ircutils.nickEqual(target, nick):
|
if ircutils.nickEqual(target, nick):
|
||||||
payload = stripPrefixStrings(payload)
|
payload = stripPrefixStrings(payload)
|
||||||
@ -96,21 +101,22 @@ def addressed(nick, msg, prefixChars=None,
|
|||||||
payload = payload[1:].lstrip()
|
payload = payload[1:].lstrip()
|
||||||
return payload
|
return payload
|
||||||
# Ok, not private. Does it start with our nick?
|
# Ok, not private. Does it start with our nick?
|
||||||
elif whenAddressedByNick and \
|
elif whenAddressedByNick:
|
||||||
ircutils.toLower(payload).startswith(nick):
|
for nick in nicks:
|
||||||
|
if ircutils.toLower(payload).startswith(nick):
|
||||||
try:
|
try:
|
||||||
(maybeNick, rest) = payload.split(None, 1)
|
(maybeNick, rest) = payload.split(None, 1)
|
||||||
while not ircutils.isNick(maybeNick, strictRfc=True):
|
while not ircutils.isNick(maybeNick, strictRfc=True):
|
||||||
if maybeNick[-1].isalnum():
|
if maybeNick[-1].isalnum():
|
||||||
return ''
|
continue
|
||||||
maybeNick = maybeNick[:-1]
|
maybeNick = maybeNick[:-1]
|
||||||
if ircutils.nickEqual(maybeNick, nick):
|
if ircutils.nickEqual(maybeNick, nick):
|
||||||
return rest
|
return rest
|
||||||
else:
|
else:
|
||||||
return ''
|
continue
|
||||||
except ValueError: # split didn't work.
|
except ValueError: # split didn't work.
|
||||||
return ''
|
continue
|
||||||
elif payload and any(payload.startswith, prefixStrings):
|
if payload and any(payload.startswith, prefixStrings):
|
||||||
return stripPrefixStrings(payload)
|
return stripPrefixStrings(payload)
|
||||||
elif payload and payload[0] in prefixChars:
|
elif payload and payload[0] in prefixChars:
|
||||||
return payload[1:].strip()
|
return payload[1:].strip()
|
||||||
@ -213,10 +219,6 @@ class ArgumentError(Error):
|
|||||||
"""The bot replies with a help message when this is raised."""
|
"""The bot replies with a help message when this is raised."""
|
||||||
pass
|
pass
|
||||||
|
|
||||||
class CannotNest(Error):
|
|
||||||
"""Exception to be raised by commands that cannot be nested."""
|
|
||||||
pass
|
|
||||||
|
|
||||||
class Tokenizer:
|
class Tokenizer:
|
||||||
# This will be used as a global environment to evaluate strings in.
|
# This will be used as a global environment to evaluate strings in.
|
||||||
# Evaluation is, of course, necessary in order to allowed escaped
|
# Evaluation is, of course, necessary in order to allowed escaped
|
||||||
@ -310,10 +312,10 @@ def tokenize(s, brackets=None, channel=None):
|
|||||||
start = time.time()
|
start = time.time()
|
||||||
try:
|
try:
|
||||||
if brackets is None:
|
if brackets is None:
|
||||||
tokens = conf.channelValue(conf.supybot.reply.brackets, channel)
|
tokens = conf.get(conf.supybot.reply.brackets, channel)
|
||||||
else:
|
else:
|
||||||
tokens = brackets
|
tokens = brackets
|
||||||
if conf.channelValue(conf.supybot.reply.pipeSyntax, channel):
|
if conf.get(conf.supybot.reply.pipeSyntax, channel):
|
||||||
tokens = '%s|' % tokens
|
tokens = '%s|' % tokens
|
||||||
log.stat('tokenize took %s seconds.' % (time.time() - start))
|
log.stat('tokenize took %s seconds.' % (time.time() - start))
|
||||||
return Tokenizer(tokens).tokenize(s)
|
return Tokenizer(tokens).tokenize(s)
|
||||||
@ -329,14 +331,15 @@ def getCommands(tokens):
|
|||||||
L.extend(getCommands(elt))
|
L.extend(getCommands(elt))
|
||||||
return L
|
return L
|
||||||
|
|
||||||
def findCallbackForCommand(irc, commandName):
|
def findCallbackForCommand(irc, name):
|
||||||
"""Given a command name and an Irc object, returns a list of callbacks that
|
"""Given a command name and an Irc object, returns a list of callbacks that
|
||||||
commandName is in."""
|
commandName is in."""
|
||||||
L = []
|
L = []
|
||||||
|
name = canonicalName(name)
|
||||||
for callback in irc.callbacks:
|
for callback in irc.callbacks:
|
||||||
if not isinstance(callback, PrivmsgRegexp):
|
if not isinstance(callback, PrivmsgRegexp):
|
||||||
if hasattr(callback, 'isCommand'):
|
if hasattr(callback, 'isCommand'):
|
||||||
if callback.isCommand(commandName):
|
if callback.isCommand(name):
|
||||||
L.append(callback)
|
L.append(callback)
|
||||||
return L
|
return L
|
||||||
|
|
||||||
@ -344,16 +347,17 @@ def formatArgumentError(method, name=None, channel=None):
|
|||||||
if name is None:
|
if name is None:
|
||||||
name = method.__name__
|
name = method.__name__
|
||||||
if hasattr(method, '__doc__') and method.__doc__:
|
if hasattr(method, '__doc__') and method.__doc__:
|
||||||
if conf.channelValue(conf.supybot.reply.showSimpleSyntax, channel):
|
if conf.get(conf.supybot.reply.showSimpleSyntax, channel):
|
||||||
return getSyntax(method, name=name)
|
return getSyntax(method, name=name)
|
||||||
else:
|
else:
|
||||||
return getHelp(method, name=name)
|
return getHelp(method, name=name)
|
||||||
else:
|
else:
|
||||||
return 'Invalid arguments for %s.' % method.__name__
|
return 'Invalid arguments for %s.' % method.__name__
|
||||||
|
|
||||||
def checkCommandCapability(msg, cb, command):
|
def checkCommandCapability(msg, cb, commandName):
|
||||||
|
assert isinstance(commandName, basestring), commandName
|
||||||
plugin = cb.name().lower()
|
plugin = cb.name().lower()
|
||||||
pluginCommand = '%s.%s' % (plugin, command)
|
pluginCommand = '%s.%s' % (plugin, commandName)
|
||||||
def checkCapability(capability):
|
def checkCapability(capability):
|
||||||
assert ircdb.isAntiCapability(capability)
|
assert ircdb.isAntiCapability(capability)
|
||||||
if ircdb.checkCapability(msg.prefix, capability):
|
if ircdb.checkCapability(msg.prefix, capability):
|
||||||
@ -362,12 +366,12 @@ def checkCommandCapability(msg, cb, command):
|
|||||||
raise RuntimeError, capability
|
raise RuntimeError, capability
|
||||||
try:
|
try:
|
||||||
antiPlugin = ircdb.makeAntiCapability(plugin)
|
antiPlugin = ircdb.makeAntiCapability(plugin)
|
||||||
antiCommand = ircdb.makeAntiCapability(command)
|
antiCommand = ircdb.makeAntiCapability(commandName)
|
||||||
antiPluginCommand = ircdb.makeAntiCapability(pluginCommand)
|
antiPluginCommand = ircdb.makeAntiCapability(pluginCommand)
|
||||||
checkCapability(antiPlugin)
|
checkCapability(antiPlugin)
|
||||||
checkCapability(antiCommand)
|
checkCapability(antiCommand)
|
||||||
checkCapability(antiPluginCommand)
|
checkCapability(antiPluginCommand)
|
||||||
checkAtEnd = [command, pluginCommand]
|
checkAtEnd = [commandName, pluginCommand]
|
||||||
default = conf.supybot.capabilities.default()
|
default = conf.supybot.capabilities.default()
|
||||||
if ircutils.isChannel(msg.args[0]):
|
if ircutils.isChannel(msg.args[0]):
|
||||||
channel = msg.args[0]
|
channel = msg.args[0]
|
||||||
@ -376,7 +380,7 @@ def checkCommandCapability(msg, cb, command):
|
|||||||
checkCapability(ircdb.makeChannelCapability(channel,
|
checkCapability(ircdb.makeChannelCapability(channel,
|
||||||
antiPluginCommand))
|
antiPluginCommand))
|
||||||
chanPlugin = ircdb.makeChannelCapability(channel, plugin)
|
chanPlugin = ircdb.makeChannelCapability(channel, plugin)
|
||||||
chanCommand = ircdb.makeChannelCapability(channel, command)
|
chanCommand = ircdb.makeChannelCapability(channel, commandName)
|
||||||
chanPluginCommand = ircdb.makeChannelCapability(channel,
|
chanPluginCommand = ircdb.makeChannelCapability(channel,
|
||||||
pluginCommand)
|
pluginCommand)
|
||||||
checkAtEnd += [chanCommand, chanPlugin, chanPluginCommand]
|
checkAtEnd += [chanCommand, chanPlugin, chanPluginCommand]
|
||||||
@ -437,49 +441,56 @@ class RichReplyMethods(object):
|
|||||||
else:
|
else:
|
||||||
self.reply(prefixer(s), **kwargs)
|
self.reply(prefixer(s), **kwargs)
|
||||||
|
|
||||||
def _error(self, s, Raise, **kwargs):
|
def _error(self, s, Raise=False, **kwargs):
|
||||||
if Raise:
|
if Raise:
|
||||||
raise Error, s
|
raise Error, s
|
||||||
else:
|
else:
|
||||||
self.error(s, **kwargs)
|
self.error(s, **kwargs)
|
||||||
|
|
||||||
def errorNoCapability(self, capability, s='', Raise=False, **kwargs):
|
def errorNoCapability(self, capability, s='', **kwargs):
|
||||||
if isinstance(capability, basestring): # checkCommandCapability!
|
if isinstance(capability, basestring): # checkCommandCapability!
|
||||||
log.warning('Denying %s for lacking %r capability.',
|
log.warning('Denying %s for lacking %r capability.',
|
||||||
self.msg.prefix, capability)
|
self.msg.prefix, 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)
|
||||||
self._error(s, Raise, **kwargs)
|
self._error(s, **kwargs)
|
||||||
else:
|
else:
|
||||||
log.warning('Denying %s for some unspecified capability '
|
log.warning('Denying %s for some unspecified capability '
|
||||||
'(or a default).', self.msg.prefix)
|
'(or a default).', self.msg.prefix)
|
||||||
v = self._getConfig(conf.supybot.replies.genericNoCapability)
|
v = self._getConfig(conf.supybot.replies.genericNoCapability)
|
||||||
self._error(self.__makeReply(v, s), Raise, **kwargs)
|
self._error(self.__makeReply(v, s), **kwargs)
|
||||||
|
|
||||||
def errorPossibleBug(self, s='', Raise=False, **kwargs):
|
def errorPossibleBug(self, s='', **kwargs):
|
||||||
v = self._getConfig(conf.supybot.replies.possibleBug)
|
v = self._getConfig(conf.supybot.replies.possibleBug)
|
||||||
if s:
|
if s:
|
||||||
s += ' (%s)' % v
|
s += ' (%s)' % v
|
||||||
else:
|
else:
|
||||||
s = v
|
s = v
|
||||||
self._error(s, Raise, **kwargs)
|
self._error(s, **kwargs)
|
||||||
|
|
||||||
def errorNotRegistered(self, s='', Raise=False, **kwargs):
|
def errorNotRegistered(self, s='', **kwargs):
|
||||||
v = self._getConfig(conf.supybot.replies.notRegistered)
|
v = self._getConfig(conf.supybot.replies.notRegistered)
|
||||||
self._error(self.__makeReply(v, s), Raise, **kwargs)
|
self._error(self.__makeReply(v, s), **kwargs)
|
||||||
|
|
||||||
def errorNoUser(self, s='', name='that user', Raise=False, **kwargs):
|
def errorNoUser(self, s='', name='that user', **kwargs):
|
||||||
v = self._getConfig(conf.supybot.replies.noUser)
|
v = self._getConfig(conf.supybot.replies.noUser)
|
||||||
try:
|
try:
|
||||||
v = v % name
|
v = v % name
|
||||||
except TypeError:
|
except TypeError:
|
||||||
log.warning('supybot.replies.noUser should have one "%s" in it.')
|
log.warning('supybot.replies.noUser should have one "%s" in it.')
|
||||||
self._error(self.__makeReply(v, s), Raise, **kwargs)
|
self._error(self.__makeReply(v, s), **kwargs)
|
||||||
|
|
||||||
def errorRequiresPrivacy(self, s='', Raise=False, **kwargs):
|
def errorRequiresPrivacy(self, s='', **kwargs):
|
||||||
v = self._getConfig(conf.supybot.replies.requiresPrivacy)
|
v = self._getConfig(conf.supybot.replies.requiresPrivacy)
|
||||||
self._error(self.__makeReply(v, s), Raise, **kwargs)
|
self._error(self.__makeReply(v, s), **kwargs)
|
||||||
|
|
||||||
|
def errorInvalid(self, what, given=None, s='', **kwargs):
|
||||||
|
if given is not None:
|
||||||
|
v = '%r is not a valid %s.' % (given, what)
|
||||||
|
else:
|
||||||
|
v = 'That\'s not a valid %s.' % what
|
||||||
|
self._error(self.__makeReply(v, s), **kwargs)
|
||||||
|
|
||||||
|
|
||||||
class IrcObjectProxy(RichReplyMethods):
|
class IrcObjectProxy(RichReplyMethods):
|
||||||
@ -542,21 +553,14 @@ class IrcObjectProxy(RichReplyMethods):
|
|||||||
log.exception('Uncaught exception in %s.invalidCommand',
|
log.exception('Uncaught exception in %s.invalidCommand',
|
||||||
cb.name())
|
cb.name())
|
||||||
|
|
||||||
def _callCommand(self, name, command, cb):
|
def _callCommand(self, name, cb):
|
||||||
try:
|
try:
|
||||||
self.commandMethod = command
|
self.commandMethod = cb.getCommand(name)
|
||||||
try:
|
try:
|
||||||
cb.callCommand(command, self, self.msg, self.args)
|
cb.callCommand(name, self, self.msg, self.args)
|
||||||
except (getopt.GetoptError, ArgumentError):
|
|
||||||
self.reply(formatArgumentError(command, name=name))
|
|
||||||
except CannotNest, e:
|
|
||||||
if not isinstance(self.irc, irclib.Irc):
|
|
||||||
self.error('Command %r cannot be nested.' % name)
|
|
||||||
except (SyntaxError, Error), e:
|
|
||||||
cb.log.info('Error return: %s', e)
|
|
||||||
self.error(str(e))
|
|
||||||
except Exception, e:
|
except Exception, e:
|
||||||
cb.log.exception('Uncaught exception:')
|
cb.log.exception('Uncaught exception in %s.%s:',
|
||||||
|
cb.name(), name)
|
||||||
if conf.supybot.reply.detailedErrors():
|
if conf.supybot.reply.detailedErrors():
|
||||||
self.error(utils.exnToString(e))
|
self.error(utils.exnToString(e))
|
||||||
else:
|
else:
|
||||||
@ -567,27 +571,27 @@ class IrcObjectProxy(RichReplyMethods):
|
|||||||
def finalEval(self):
|
def finalEval(self):
|
||||||
assert not self.finalEvaled, 'finalEval called twice.'
|
assert not self.finalEvaled, 'finalEval called twice.'
|
||||||
self.finalEvaled = True
|
self.finalEvaled = True
|
||||||
name = canonicalName(self.args[0])
|
name = self.args[0]
|
||||||
cbs = findCallbackForCommand(self, name)
|
cbs = findCallbackForCommand(self, name)
|
||||||
if len(cbs) == 0:
|
if len(cbs) == 0:
|
||||||
for cb in self.irc.callbacks:
|
for cb in self.irc.callbacks:
|
||||||
if isinstance(cb, PrivmsgRegexp):
|
if isinstance(cb, PrivmsgRegexp):
|
||||||
for (r, m) in cb.res:
|
for (r, name) in cb.res:
|
||||||
if r.search(self.msg.args[1]):
|
if r.search(self.msg.args[1]):
|
||||||
log.debug('Skipping invalidCommand: %s.%s',
|
log.debug('Skipping invalidCommand: %s.%s',
|
||||||
m.im_class.__name__,m.im_func.func_name)
|
cb.name(), name)
|
||||||
return
|
return
|
||||||
elif isinstance(cb, PrivmsgCommandAndRegexp):
|
elif isinstance(cb, PrivmsgCommandAndRegexp):
|
||||||
for (r, m) in cb.res:
|
for (r, name) in cb.res:
|
||||||
if r.search(self.msg.args[1]):
|
if r.search(self.msg.args[1]):
|
||||||
log.debug('Skipping invalidCommand: %s.%s',
|
log.debug('Skipping invalidCommand: %s.%s',
|
||||||
m.im_class.__name__,m.im_func.func_name)
|
cb.name(), name)
|
||||||
return
|
return
|
||||||
payload = addressed(self.irc.nick, self.msg)
|
payload = addressed(self.irc.nick, self.msg)
|
||||||
for (r, m) in cb.addressedRes:
|
for (r, name) in cb.addressedRes:
|
||||||
if r.search(payload):
|
if r.search(payload):
|
||||||
log.debug('Skipping invalidCommand: %s.%s',
|
log.debug('Skipping invalidCommand: %s.%s',
|
||||||
m.im_class.__name__,m.im_func.func_name)
|
cb.name(), name)
|
||||||
return
|
return
|
||||||
# Ok, no regexp-based things matched.
|
# Ok, no regexp-based things matched.
|
||||||
self._callInvalidCommands()
|
self._callInvalidCommands()
|
||||||
@ -604,18 +608,13 @@ class IrcObjectProxy(RichReplyMethods):
|
|||||||
else:
|
else:
|
||||||
del self.args[0]
|
del self.args[0]
|
||||||
cb = cbs[0]
|
cb = cbs[0]
|
||||||
cap = checkCommandCapability(self.msg, cb, name)
|
|
||||||
if cap:
|
|
||||||
self.errorNoCapability(cap)
|
|
||||||
return
|
|
||||||
command = getattr(cb, name)
|
|
||||||
Privmsg.handled = True
|
Privmsg.handled = True
|
||||||
if cb.threaded or conf.supybot.debug.threadAllCommands():
|
if cb.threaded or conf.supybot.debug.threadAllCommands():
|
||||||
t = CommandThread(target=self._callCommand,
|
t = CommandThread(target=self._callCommand,
|
||||||
args=(name, command, cb))
|
args=(name, cb))
|
||||||
t.start()
|
t.start()
|
||||||
else:
|
else:
|
||||||
self._callCommand(name, command, cb)
|
self._callCommand(name, cb)
|
||||||
|
|
||||||
def reply(self, s, noLengthCheck=False, prefixName=True,
|
def reply(self, s, noLengthCheck=False, prefixName=True,
|
||||||
action=None, private=None, notice=None, to=None, msg=None):
|
action=None, private=None, notice=None, to=None, msg=None):
|
||||||
@ -768,7 +767,8 @@ class CommandThread(threading.Thread):
|
|||||||
to run in threads.
|
to run in threads.
|
||||||
"""
|
"""
|
||||||
def __init__(self, target=None, args=(), kwargs={}):
|
def __init__(self, target=None, args=(), kwargs={}):
|
||||||
(self.name, self.command, self.cb) = args
|
(self.name, self.cb) = args
|
||||||
|
self.command = self.cb.getCommand(self.name)
|
||||||
world.threadsSpawned += 1
|
world.threadsSpawned += 1
|
||||||
threadName = 'Thread #%s (for %s.%s)' % (world.threadsSpawned,
|
threadName = 'Thread #%s (for %s.%s)' % (world.threadsSpawned,
|
||||||
self.cb.name(), self.name)
|
self.cb.name(), self.name)
|
||||||
@ -925,17 +925,17 @@ class Privmsg(irclib.IrcCallback):
|
|||||||
else:
|
else:
|
||||||
self.__parent.__call__(irc, msg)
|
self.__parent.__call__(irc, msg)
|
||||||
|
|
||||||
def isCommand(self, methodName):
|
def isCommand(self, name):
|
||||||
"""Returns whether a given method name is a command in this plugin."""
|
"""Returns whether a given method name is a command in this plugin."""
|
||||||
# This function is ugly, but I don't want users to call methods like
|
# This function is ugly, but I don't want users to call methods like
|
||||||
# doPrivmsg or __init__ or whatever, and this is good to stop them.
|
# doPrivmsg or __init__ or whatever, and this is good to stop them.
|
||||||
|
|
||||||
# Don't canonicalize this name: consider outFilter(self, irc, msg).
|
# Don't canonicalize this name: consider outFilter(self, irc, msg).
|
||||||
# methodName = canonicalName(methodName)
|
# name = canonicalName(name)
|
||||||
if self._disabled.disabled(methodName, plugin=self.name()):
|
if self._disabled.disabled(name, plugin=self.name()):
|
||||||
return False
|
return False
|
||||||
if hasattr(self, methodName):
|
if hasattr(self, name):
|
||||||
method = getattr(self, methodName)
|
method = getattr(self, name)
|
||||||
if inspect.ismethod(method):
|
if inspect.ismethod(method):
|
||||||
code = method.im_func.func_code
|
code = method.im_func.func_code
|
||||||
return inspect.getargs(code)[0] == self.commandArgs
|
return inspect.getargs(code)[0] == self.commandArgs
|
||||||
@ -944,19 +944,32 @@ class Privmsg(irclib.IrcCallback):
|
|||||||
else:
|
else:
|
||||||
return False
|
return False
|
||||||
|
|
||||||
def getCommand(self, methodName):
|
def getCommand(self, name):
|
||||||
"""Gets the given command from this plugin."""
|
"""Gets the given command from this plugin."""
|
||||||
assert self.isCommand(methodName)
|
name = canonicalName(name)
|
||||||
methodName = canonicalName(methodName)
|
assert self.isCommand(name), '%r is not a command.' % name
|
||||||
return getattr(self, methodName)
|
return getattr(self, name)
|
||||||
|
|
||||||
def callCommand(self, method, irc, msg, *L):
|
def callCommand(self, name, irc, msg, *L, **kwargs):
|
||||||
name = method.im_func.func_name
|
#print '*', name, utils.stackTrace()
|
||||||
|
checkCapabilities = kwargs.pop('checkCapabilities', True)
|
||||||
|
if checkCapabilities:
|
||||||
|
cap = checkCommandCapability(msg, self, name)
|
||||||
|
if cap:
|
||||||
|
irc.errorNoCapability(cap)
|
||||||
|
return
|
||||||
|
method = self.getCommand(name)
|
||||||
assert L, 'Odd, nothing in L. This can\'t happen.'
|
assert L, 'Odd, nothing in L. This can\'t happen.'
|
||||||
self.log.info('%r called by %s', name, msg.prefix)
|
self.log.info('%s.%s called by %s.', self.name(), name, msg.prefix)
|
||||||
self.log.debug('args: %s', L[0])
|
self.log.debug('args: %s', L[0])
|
||||||
start = time.time()
|
start = time.time()
|
||||||
|
try:
|
||||||
method(irc, msg, *L)
|
method(irc, msg, *L)
|
||||||
|
except (getopt.GetoptError, ArgumentError):
|
||||||
|
irc.reply(formatArgumentError(method, name=name))
|
||||||
|
except (SyntaxError, Error), e:
|
||||||
|
self.log.debug('Error return: %s', utils.exnToString(e))
|
||||||
|
irc.error(str(e))
|
||||||
elapsed = time.time() - start
|
elapsed = time.time() - start
|
||||||
log.stat('%s took %s seconds', name, elapsed)
|
log.stat('%s took %s seconds', name, elapsed)
|
||||||
|
|
||||||
@ -1080,13 +1093,14 @@ class PrivmsgRegexp(Privmsg):
|
|||||||
self.log.warning('Invalid regexp: %r (%s)',value.__doc__,e)
|
self.log.warning('Invalid regexp: %r (%s)',value.__doc__,e)
|
||||||
self.res.sort(lambda (r1, m1), (r2, m2): cmp(m1.__name__, m2.__name__))
|
self.res.sort(lambda (r1, m1), (r2, m2): cmp(m1.__name__, m2.__name__))
|
||||||
|
|
||||||
def callCommand(self, method, irc, msg, *L):
|
def callCommand(self, name, irc, msg, *L, **kwargs):
|
||||||
try:
|
try:
|
||||||
self.__parent.callCommand(method, irc, msg, *L)
|
self.__parent.callCommand(name, irc, msg, *L, **kwargs)
|
||||||
except Exception, e:
|
except Exception, e:
|
||||||
# We catch exceptions here because IrcObjectProxy isn't doing our
|
# We catch exceptions here because IrcObjectProxy isn't doing our
|
||||||
# dirty work for us anymore.
|
# dirty work for us anymore.
|
||||||
self.log.exception('Uncaught exception from callCommand:')
|
self.log.exception('Uncaught exception in %s.%s:',
|
||||||
|
self.name(), name)
|
||||||
if conf.supybot.reply.detailedErrors():
|
if conf.supybot.reply.detailedErrors():
|
||||||
irc.error(utils.exnToString(e))
|
irc.error(utils.exnToString(e))
|
||||||
else:
|
else:
|
||||||
@ -1126,17 +1140,31 @@ class PrivmsgCommandAndRegexp(Privmsg):
|
|||||||
for name in self.regexps:
|
for name in self.regexps:
|
||||||
method = getattr(self, name)
|
method = getattr(self, name)
|
||||||
r = re.compile(method.__doc__, self.flags)
|
r = re.compile(method.__doc__, self.flags)
|
||||||
self.res.append((r, method))
|
self.res.append((r, name))
|
||||||
for name in self.addressedRegexps:
|
for name in self.addressedRegexps:
|
||||||
method = getattr(self, name)
|
method = getattr(self, name)
|
||||||
r = re.compile(method.__doc__, self.flags)
|
r = re.compile(method.__doc__, self.flags)
|
||||||
self.addressedRes.append((r, method))
|
self.addressedRes.append((r, name))
|
||||||
|
|
||||||
def callCommand(self, f, irc, msg, *L, **kwargs):
|
def isCommand(self, name):
|
||||||
|
return self.__parent.isCommand(name) or \
|
||||||
|
name in self.regexps or \
|
||||||
|
name in self.addressedRegexps
|
||||||
|
|
||||||
|
def getCommand(self, name):
|
||||||
try:
|
try:
|
||||||
self.__parent.callCommand(f, irc, msg, *L)
|
return getattr(self, name) # Regexp stuff.
|
||||||
|
except AttributeError:
|
||||||
|
return self.__parent.getCommand(name)
|
||||||
|
|
||||||
|
def callCommand(self, name, irc, msg, *L, **kwargs):
|
||||||
|
try:
|
||||||
|
self.__parent.callCommand(name, irc, msg, *L, **kwargs)
|
||||||
except Exception, e:
|
except Exception, e:
|
||||||
if 'catchErrors' in kwargs and kwargs['catchErrors']:
|
# As annoying as it is, Python doesn't allow *L in addition to
|
||||||
|
# well-defined keyword arguments. So we have to do this trick.
|
||||||
|
catchErrors = kwargs.pop('catchErrors', False)
|
||||||
|
if catchErrors:
|
||||||
self.log.exception('Uncaught exception in callCommand:')
|
self.log.exception('Uncaught exception in callCommand:')
|
||||||
if conf.supybot.reply.detailedErrors():
|
if conf.supybot.reply.detailedErrors():
|
||||||
irc.error(utils.exnToString(e))
|
irc.error(utils.exnToString(e))
|
||||||
@ -1147,24 +1175,22 @@ class PrivmsgCommandAndRegexp(Privmsg):
|
|||||||
|
|
||||||
def doPrivmsg(self, irc, msg):
|
def doPrivmsg(self, irc, msg):
|
||||||
if Privmsg.errored:
|
if Privmsg.errored:
|
||||||
self.log.info('%s not running due to Privmsg.errored.',
|
self.log.debug('%s not running due to Privmsg.errored.',
|
||||||
self.name())
|
self.name())
|
||||||
return
|
return
|
||||||
for (r, method) in self.res:
|
for (r, name) in self.res:
|
||||||
name = method.__name__
|
|
||||||
for m in r.finditer(msg.args[1]):
|
for m in r.finditer(msg.args[1]):
|
||||||
proxy = self.Proxy(irc, msg)
|
proxy = self.Proxy(irc, msg)
|
||||||
self.callCommand(method, proxy, msg, m, catchErrors=True)
|
self.callCommand(name, proxy, msg, m, catchErrors=True)
|
||||||
if not Privmsg.handled:
|
if not Privmsg.handled:
|
||||||
s = addressed(irc.nick, msg)
|
s = addressed(irc.nick, msg)
|
||||||
if s:
|
if s:
|
||||||
for (r, method) in self.addressedRes:
|
for (r, name) in self.addressedRes:
|
||||||
name = method.__name__
|
|
||||||
if Privmsg.handled and name not in self.alwaysCall:
|
if Privmsg.handled and name not in self.alwaysCall:
|
||||||
continue
|
continue
|
||||||
for m in r.finditer(s):
|
for m in r.finditer(s):
|
||||||
proxy = self.Proxy(irc, msg)
|
proxy = self.Proxy(irc, msg)
|
||||||
self.callCommand(method,proxy,msg,m,catchErrors=True)
|
self.callCommand(name, proxy, msg, m, catchErrors=True)
|
||||||
Privmsg.handled = True
|
Privmsg.handled = True
|
||||||
|
|
||||||
|
|
||||||
|
61
src/conf.py
61
src/conf.py
@ -86,7 +86,9 @@ allowDefaultOwner = False
|
|||||||
supybot = registry.Group()
|
supybot = registry.Group()
|
||||||
supybot.setName('supybot')
|
supybot.setName('supybot')
|
||||||
|
|
||||||
def registerGroup(Group, name, group=None):
|
def registerGroup(Group, name, group=None, **kwargs):
|
||||||
|
if kwargs:
|
||||||
|
group = registry.Group(**kwargs)
|
||||||
return Group.register(name, group)
|
return Group.register(name, group)
|
||||||
|
|
||||||
def registerGlobalValue(group, name, value):
|
def registerGlobalValue(group, name, value):
|
||||||
@ -109,8 +111,8 @@ def registerPlugin(name, currentValue=None, public=True):
|
|||||||
supybot.plugins.get(name).setValue(currentValue)
|
supybot.plugins.get(name).setValue(currentValue)
|
||||||
return registerGroup(users.plugins, name)
|
return registerGroup(users.plugins, name)
|
||||||
|
|
||||||
def channelValue(group, channel=None):
|
def get(group, channel=None):
|
||||||
if channel is None:
|
if channel is None or not group.channelValue:
|
||||||
return group()
|
return group()
|
||||||
else:
|
else:
|
||||||
return group.get(channel)()
|
return group.get(channel)()
|
||||||
@ -120,7 +122,7 @@ def channelValue(group, channel=None):
|
|||||||
###
|
###
|
||||||
users = registry.Group()
|
users = registry.Group()
|
||||||
users.setName('users')
|
users.setName('users')
|
||||||
registerGroup(users, 'plugins')
|
registerGroup(users, 'plugins', orderAlphabetically=True)
|
||||||
|
|
||||||
def registerUserValue(group, name, value):
|
def registerUserValue(group, name, value):
|
||||||
assert group._name.startswith('users')
|
assert group._name.startswith('users')
|
||||||
@ -188,7 +190,8 @@ class Networks(registry.SpaceSeparatedSetOfStrings):
|
|||||||
List = ircutils.IrcSet
|
List = ircutils.IrcSet
|
||||||
|
|
||||||
registerGlobalValue(supybot, 'networks',
|
registerGlobalValue(supybot, 'networks',
|
||||||
Networks([], """Determines what networks the bot will connect to."""))
|
Networks([], """Determines what networks the bot will connect to.""",
|
||||||
|
orderAlphabetically=True))
|
||||||
|
|
||||||
class Servers(registry.SpaceSeparatedListOfStrings):
|
class Servers(registry.SpaceSeparatedListOfStrings):
|
||||||
def normalize(self, s):
|
def normalize(self, s):
|
||||||
@ -278,8 +281,8 @@ registerChannelValue(supybot.reply.mores, 'instant',
|
|||||||
registerGlobalValue(supybot.reply, 'oneToOne',
|
registerGlobalValue(supybot.reply, 'oneToOne',
|
||||||
registry.Boolean(True, """Determines whether the bot will send
|
registry.Boolean(True, """Determines whether the bot will send
|
||||||
multi-message replies in a single message or in multiple messages. For
|
multi-message replies in a single message or in multiple messages. For
|
||||||
safety purposes (so the bot can't possibly flood) it will normally send
|
safety purposes (so the bot is less likely to flood) it will normally send
|
||||||
everything in a single message."""))
|
everything in a single message, using mores if necessary."""))
|
||||||
|
|
||||||
class ValidBrackets(registry.OnlySomeStrings):
|
class ValidBrackets(registry.OnlySomeStrings):
|
||||||
validStrings = ('', '[]', '<>', '{}', '()')
|
validStrings = ('', '[]', '<>', '{}', '()')
|
||||||
@ -409,6 +412,11 @@ registerChannelValue(supybot.reply.whenAddressedBy, 'nick',
|
|||||||
registry.Boolean(True, """Determines whether the bot will reply when people
|
registry.Boolean(True, """Determines whether the bot will reply when people
|
||||||
address it by its nick, rather than with a prefix character."""))
|
address it by its nick, rather than with a prefix character."""))
|
||||||
|
|
||||||
|
registerChannelValue(supybot.reply.whenAddressedBy, 'nicks',
|
||||||
|
registry.SpaceSeparatedSetOfStrings([], """Determines what extra nicks the
|
||||||
|
bot will always respond to when addressed by, even if its current nick is
|
||||||
|
something else."""))
|
||||||
|
|
||||||
###
|
###
|
||||||
# Replies
|
# Replies
|
||||||
###
|
###
|
||||||
@ -501,6 +509,7 @@ registerGlobalValue(supybot, 'flush',
|
|||||||
inside the bot, your changes won't be flushed. To make this change
|
inside the bot, your changes won't be flushed. To make this change
|
||||||
permanent, you must edit the registry yourself."""))
|
permanent, you must edit the registry yourself."""))
|
||||||
|
|
||||||
|
|
||||||
###
|
###
|
||||||
# supybot.commands. For stuff relating to commands.
|
# supybot.commands. For stuff relating to commands.
|
||||||
###
|
###
|
||||||
@ -508,6 +517,42 @@ registerGroup(supybot, 'commands')
|
|||||||
# supybot.commands.disabled moved to callbacks for canonicalName.
|
# supybot.commands.disabled moved to callbacks for canonicalName.
|
||||||
|
|
||||||
|
|
||||||
|
###
|
||||||
|
# supybot.abuse. For stuff relating to abuse of the bot.
|
||||||
|
###
|
||||||
|
registerGroup(supybot, 'abuse')
|
||||||
|
registerGroup(supybot.abuse, 'flood')
|
||||||
|
registerGlobalValue(supybot.abuse.flood, 'command',
|
||||||
|
registry.Boolean(True, """Determines whether the bot will defend itself
|
||||||
|
against command-flooding."""))
|
||||||
|
registerGlobalValue(supybot.abuse.flood.command, 'maximum',
|
||||||
|
registry.PositiveInteger(12, """Determines how many commands users are
|
||||||
|
allowed per minute. If a user sends more than this many commands in any
|
||||||
|
60 second period, he or she will be ignored for
|
||||||
|
supybot.abuse.flood.command.punishment seconds."""))
|
||||||
|
registerGlobalValue(supybot.abuse.flood.command, 'punishment',
|
||||||
|
registry.PositiveInteger(300, """Determines how many seconds the bot
|
||||||
|
will ignore users who flood it with commands."""))
|
||||||
|
|
||||||
|
registerGlobalValue(supybot.abuse.flood.command, 'invalid',
|
||||||
|
registry.Boolean(True, """Determines whether the bot will defend itself
|
||||||
|
against invalid command-flooding."""))
|
||||||
|
registerGlobalValue(supybot.abuse.flood.command.invalid, 'maximum',
|
||||||
|
registry.PositiveInteger(5, """Determines how many invalid commands users
|
||||||
|
are allowed per minute. If a user sends more than this many invalid
|
||||||
|
commands in any 60 second period, he or she will be ignored for
|
||||||
|
supybot.abuse.flood.command.invalid.punishment seconds. Typically, this
|
||||||
|
value is lower than supybot.abuse.flood.command.maximum, since it's far
|
||||||
|
less likely (and far more annoying) for users to flood with invalid
|
||||||
|
commands than for them to flood with valid commands."""))
|
||||||
|
registerGlobalValue(supybot.abuse.flood.command.invalid, 'punishment',
|
||||||
|
registry.PositiveInteger(600, """Determines how many seconds the bot
|
||||||
|
will ignore users who flood it with invalid commands. Typically, this
|
||||||
|
value is higher than supybot.abuse.flood.command.punishment, since it's far
|
||||||
|
less likely (and far more annoying) for users to flood witih invalid
|
||||||
|
commands than for them to flood with valid commands."""))
|
||||||
|
|
||||||
|
|
||||||
###
|
###
|
||||||
# supybot.drivers. For stuff relating to Supybot's drivers (duh!)
|
# supybot.drivers. For stuff relating to Supybot's drivers (duh!)
|
||||||
###
|
###
|
||||||
@ -601,7 +646,7 @@ registerGlobalValue(supybot.directories, 'plugins',
|
|||||||
a new one. E.g. you can say: bot: 'config supybot.directories.plugins
|
a new one. E.g. you can say: bot: 'config supybot.directories.plugins
|
||||||
[config supybot.directories.plugins], newPluginDirectory'."""))
|
[config supybot.directories.plugins], newPluginDirectory'."""))
|
||||||
|
|
||||||
registerGroup(supybot, 'plugins') # This will be used by plugins, but not here.
|
registerGroup(supybot, 'plugins', orderAlphabetically=True)
|
||||||
registerGlobalValue(supybot.plugins, 'alwaysLoadDefault',
|
registerGlobalValue(supybot.plugins, 'alwaysLoadDefault',
|
||||||
registry.Boolean(True, """Determines whether the bot will always load
|
registry.Boolean(True, """Determines whether the bot will always load
|
||||||
the default plugins (Admin, Channel, Config, Misc, Owner, and User)
|
the default plugins (Admin, Channel, Config, Misc, Owner, and User)
|
||||||
|
132
src/ircdb.py
132
src/ircdb.py
@ -39,6 +39,7 @@ import os
|
|||||||
import sets
|
import sets
|
||||||
import time
|
import time
|
||||||
import string
|
import string
|
||||||
|
import operator
|
||||||
from itertools import imap, ilen, ifilter
|
from itertools import imap, ilen, ifilter
|
||||||
|
|
||||||
import supybot.log as log
|
import supybot.log as log
|
||||||
@ -310,32 +311,17 @@ class IrcUser(object):
|
|||||||
|
|
||||||
|
|
||||||
class IrcChannel(object):
|
class IrcChannel(object):
|
||||||
"""This class holds the capabilities, bans, and ignores of a channel.
|
"""This class holds the capabilities, bans, and ignores of a channel."""
|
||||||
"""
|
|
||||||
defaultOff = ('op', 'halfop', 'voice', 'protected')
|
defaultOff = ('op', 'halfop', 'voice', 'protected')
|
||||||
def __init__(self, bans=None, silences=None, exceptions=None, ignores=None,
|
def __init__(self, bans=None, silences=None, exceptions=None, ignores=None,
|
||||||
capabilities=None, lobotomized=False, defaultAllow=True):
|
capabilities=None, lobotomized=False, defaultAllow=True):
|
||||||
self.defaultAllow = defaultAllow
|
self.defaultAllow = defaultAllow
|
||||||
if bans is None:
|
self.expiredBans = []
|
||||||
self.bans = []
|
self.bans = bans or {}
|
||||||
else:
|
self.ignores = ignores or {}
|
||||||
self.bans = bans
|
self.silences = silences or []
|
||||||
if exceptions is None:
|
self.exceptions = exceptions or []
|
||||||
self.exceptions = []
|
self.capabilities = capabilities or CapabilitySet()
|
||||||
else:
|
|
||||||
self.exceptions = exceptions
|
|
||||||
if silences is None:
|
|
||||||
self.silences = []
|
|
||||||
else:
|
|
||||||
self.silences = silences
|
|
||||||
if ignores is None:
|
|
||||||
self.ignores = []
|
|
||||||
else:
|
|
||||||
self.ignores = ignores
|
|
||||||
if capabilities is None:
|
|
||||||
self.capabilities = CapabilitySet()
|
|
||||||
else:
|
|
||||||
self.capabilities = capabilities
|
|
||||||
for capability in self.defaultOff:
|
for capability in self.defaultOff:
|
||||||
if capability not in self.capabilities:
|
if capability not in self.capabilities:
|
||||||
self.capabilities.add(makeAntiCapability(capability))
|
self.capabilities.add(makeAntiCapability(capability))
|
||||||
@ -349,28 +335,33 @@ class IrcChannel(object):
|
|||||||
self.capabilities, self.lobotomized,
|
self.capabilities, self.lobotomized,
|
||||||
self.defaultAllow, self.silences, self.exceptions)
|
self.defaultAllow, self.silences, self.exceptions)
|
||||||
|
|
||||||
def addBan(self, hostmask):
|
def addBan(self, hostmask, expiration=0):
|
||||||
"""Adds a ban to the channel banlist."""
|
"""Adds a ban to the channel banlist."""
|
||||||
self.bans.append(hostmask)
|
self.bans[hostmask] = int(expiration)
|
||||||
|
|
||||||
def removeBan(self, hostmask):
|
def removeBan(self, hostmask):
|
||||||
"""Removes a ban from the channel banlist."""
|
"""Removes a ban from the channel banlist."""
|
||||||
self.bans = [s for s in self.bans if s != hostmask]
|
return self.bans.pop(hostmask)
|
||||||
|
|
||||||
def checkBan(self, hostmask):
|
def checkBan(self, hostmask):
|
||||||
"""Checks whether a given hostmask is banned by the channel banlist."""
|
"""Checks whether a given hostmask is banned by the channel banlist."""
|
||||||
for pat in self.bans:
|
now = time.time()
|
||||||
if ircutils.hostmaskPatternEqual(pat, hostmask):
|
for (pattern, expiration) in self.bans.items():
|
||||||
|
if now < expiration or not expiration:
|
||||||
|
if ircutils.hostmaskPatternEqual(pattern, hostmask):
|
||||||
return True
|
return True
|
||||||
|
else:
|
||||||
|
self.expiredBans.append((pattern, expiration))
|
||||||
|
del self.bans[pattern]
|
||||||
return False
|
return False
|
||||||
|
|
||||||
def addIgnore(self, hostmask):
|
def addIgnore(self, hostmask, expiration=0):
|
||||||
"""Adds an ignore to the channel ignore list."""
|
"""Adds an ignore to the channel ignore list."""
|
||||||
self.ignores.append(hostmask)
|
self.ignores[hostmask] = int(expiration)
|
||||||
|
|
||||||
def removeIgnore(self, hostmask):
|
def removeIgnore(self, hostmask):
|
||||||
"""Removes an ignore from the channel ignore list."""
|
"""Removes an ignore from the channel ignore list."""
|
||||||
self.ignores = [s for s in self.ignores if s != hostmask]
|
return self.ignores.pop(hostmask)
|
||||||
|
|
||||||
def addCapability(self, capability):
|
def addCapability(self, capability):
|
||||||
"""Adds a capability to the channel's default capabilities."""
|
"""Adds a capability to the channel's default capabilities."""
|
||||||
@ -398,12 +389,16 @@ class IrcChannel(object):
|
|||||||
"""Checks whether a given hostmask is to be ignored by the channel."""
|
"""Checks whether a given hostmask is to be ignored by the channel."""
|
||||||
if self.lobotomized:
|
if self.lobotomized:
|
||||||
return True
|
return True
|
||||||
for mask in self.bans:
|
if self.checkBan(hostmask):
|
||||||
if ircutils.hostmaskPatternEqual(mask, hostmask):
|
|
||||||
return True
|
return True
|
||||||
for mask in self.ignores:
|
now = time.time()
|
||||||
if ircutils.hostmaskPatternEqual(mask, hostmask):
|
for (pattern, expiration) in self.ignores.items():
|
||||||
|
if now < expiration or not expiration:
|
||||||
|
if ircutils.hostmaskPatternEqual(pattern, hostmask):
|
||||||
return True
|
return True
|
||||||
|
else:
|
||||||
|
del self.ignores[pattern]
|
||||||
|
# Later we may wish to keep expiredIgnores, but not now.
|
||||||
return False
|
return False
|
||||||
|
|
||||||
def preserve(self, fd, indent=''):
|
def preserve(self, fd, indent=''):
|
||||||
@ -415,14 +410,14 @@ class IrcChannel(object):
|
|||||||
write('defaultAllow %s' % self.defaultAllow)
|
write('defaultAllow %s' % self.defaultAllow)
|
||||||
for capability in self.capabilities:
|
for capability in self.capabilities:
|
||||||
write('capability ' + capability)
|
write('capability ' + capability)
|
||||||
for ban in self.bans:
|
bans = self.bans.items()
|
||||||
write('ban ' + ban)
|
utils.sortBy(operator.itemgetter(1), bans)
|
||||||
for silence in self.silences:
|
for (ban, expiration) in bans:
|
||||||
write('silence ' + silence)
|
write('ban %s %d' % (ban, expiration))
|
||||||
for exception in self.exceptions:
|
ignores = self.ignores.items()
|
||||||
write('exception ' + exception)
|
utils.sortBy(operator.itemgetter(1), ignores)
|
||||||
for ignore in self.ignores:
|
for (ignore, expiration) in ignores:
|
||||||
write('ignore ' + ignore)
|
write('ignore %s %d' % (ignore, expiration))
|
||||||
fd.write(os.linesep)
|
fd.write(os.linesep)
|
||||||
|
|
||||||
|
|
||||||
@ -511,22 +506,14 @@ class IrcChannelCreator(Creator):
|
|||||||
def ban(self, rest, lineno):
|
def ban(self, rest, lineno):
|
||||||
if self.name is None:
|
if self.name is None:
|
||||||
raise ValueError, 'Unexpected channel description without channel.'
|
raise ValueError, 'Unexpected channel description without channel.'
|
||||||
self.c.bans.append(rest)
|
(pattern, expiration) = rest
|
||||||
|
self.c.bans[pattern] = int(float(expiration))
|
||||||
|
|
||||||
def ignore(self, rest, lineno):
|
def ignore(self, rest, lineno):
|
||||||
if self.name is None:
|
if self.name is None:
|
||||||
raise ValueError, 'Unexpected channel description without channel.'
|
raise ValueError, 'Unexpected channel description without channel.'
|
||||||
self.c.ignores.append(rest)
|
(pattern, expiration) = rest
|
||||||
|
self.c.ignores[pattern] = int(float(expiration))
|
||||||
def silence(self, rest, lineno):
|
|
||||||
if self.name is None:
|
|
||||||
raise ValueError, 'Unexpected channel description without channel.'
|
|
||||||
self.c.silences.append(rest)
|
|
||||||
|
|
||||||
def exception(self, rest, lineno):
|
|
||||||
if self.name is None:
|
|
||||||
raise ValueError, 'Unexpected channel description without channel.'
|
|
||||||
self.c.exceptions.append(rest)
|
|
||||||
|
|
||||||
def finish(self):
|
def finish(self):
|
||||||
if self.hadChannel:
|
if self.hadChannel:
|
||||||
@ -782,20 +769,32 @@ class ChannelsDictionary(utils.IterableMap):
|
|||||||
class IgnoresDB(object):
|
class IgnoresDB(object):
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
self.filename = None
|
self.filename = None
|
||||||
self.hostmasks = sets.Set()
|
self.hostmasks = {}
|
||||||
|
|
||||||
def open(self, filename):
|
def open(self, filename):
|
||||||
self.filename = filename
|
self.filename = filename
|
||||||
fd = file(self.filename)
|
fd = file(self.filename)
|
||||||
for line in utils.nonCommentNonEmptyLines(fd):
|
for line in utils.nonCommentNonEmptyLines(fd):
|
||||||
self.hostmasks.add(line.rstrip('\r\n'))
|
try:
|
||||||
|
line = line.rstrip('\r\n')
|
||||||
|
L = line.split()
|
||||||
|
hostmask = L.pop(0)
|
||||||
|
if L:
|
||||||
|
expiration = int(float(L.pop(0)))
|
||||||
|
else:
|
||||||
|
expiration = 0
|
||||||
|
self.add(hostmask, expiration)
|
||||||
|
except Exception, e:
|
||||||
|
log.error('Invalid line in ignores database: %r', line)
|
||||||
fd.close()
|
fd.close()
|
||||||
|
|
||||||
def flush(self):
|
def flush(self):
|
||||||
if self.filename is not None:
|
if self.filename is not None:
|
||||||
fd = utils.transactionalFile(self.filename)
|
fd = utils.transactionalFile(self.filename)
|
||||||
for hostmask in self.hostmasks:
|
now = time.time()
|
||||||
fd.write(hostmask)
|
for (hostmask, expiration) in self.hostmasks.items():
|
||||||
|
if now < expiration or not expiration:
|
||||||
|
fd.write('%s %s' % (hostmask, expiration))
|
||||||
fd.write(os.linesep)
|
fd.write(os.linesep)
|
||||||
fd.close()
|
fd.close()
|
||||||
else:
|
else:
|
||||||
@ -809,26 +808,33 @@ class IgnoresDB(object):
|
|||||||
|
|
||||||
def reload(self):
|
def reload(self):
|
||||||
if self.filename is not None:
|
if self.filename is not None:
|
||||||
|
oldhostmasks = self.hostmasks.copy()
|
||||||
self.hostmasks.clear()
|
self.hostmasks.clear()
|
||||||
try:
|
try:
|
||||||
self.open(self.filename)
|
self.open(self.filename)
|
||||||
except EnvironmentError, e:
|
except EnvironmentError, e:
|
||||||
log.warning('IgnoresDB.reload failed: %s', e)
|
log.warning('IgnoresDB.reload failed: %s', e)
|
||||||
|
# Let's be somewhat transactional.
|
||||||
|
self.hostmasks.update(oldhostmasks)
|
||||||
else:
|
else:
|
||||||
log.warning('IgnoresDB.reload called without self.filename.')
|
log.warning('IgnoresDB.reload called without self.filename.')
|
||||||
|
|
||||||
def checkIgnored(self, prefix):
|
def checkIgnored(self, prefix):
|
||||||
for hostmask in self.hostmasks:
|
now = time.time()
|
||||||
|
for (hostmask, expiration) in self.hostmasks.items():
|
||||||
|
if expiration and now > expiration:
|
||||||
|
del self.hostmasks[hostmask]
|
||||||
|
else:
|
||||||
if ircutils.hostmaskPatternEqual(hostmask, prefix):
|
if ircutils.hostmaskPatternEqual(hostmask, prefix):
|
||||||
return True
|
return True
|
||||||
return False
|
return False
|
||||||
|
|
||||||
def addHostmask(self, hostmask):
|
def add(self, hostmask, expiration=0):
|
||||||
assert ircutils.isUserHostmask(hostmask)
|
assert ircutils.isUserHostmask(hostmask)
|
||||||
self.hostmasks.add(hostmask)
|
self.hostmasks[hostmask] = expiration
|
||||||
|
|
||||||
def removeHostmask(self, hostmask):
|
def remove(self, hostmask):
|
||||||
self.hostmasks.remove(hostmask)
|
del self.hostmasks[hostmask]
|
||||||
|
|
||||||
|
|
||||||
confDir = conf.supybot.directories.conf()
|
confDir = conf.supybot.directories.conf()
|
||||||
|
@ -55,8 +55,7 @@ reconnectWaits = (0, 60, 300)
|
|||||||
class SocketDriver(drivers.IrcDriver, drivers.ServersMixin):
|
class SocketDriver(drivers.IrcDriver, drivers.ServersMixin):
|
||||||
def __init__(self, irc):
|
def __init__(self, irc):
|
||||||
self.irc = irc
|
self.irc = irc
|
||||||
drivers.ServersMixin.__init__(self, irc)
|
super(SocketDriver, self).__init__(irc)
|
||||||
drivers.IrcDriver.__init__(self) # Must come after setting irc.
|
|
||||||
self.conn = None
|
self.conn = None
|
||||||
self.servers = ()
|
self.servers = ()
|
||||||
self.eagains = 0
|
self.eagains = 0
|
||||||
|
@ -182,6 +182,11 @@ class FunctionsTestCase(SupyTestCase):
|
|||||||
finally:
|
finally:
|
||||||
conf.supybot.reply.whenNotAddressed.setValue(original)
|
conf.supybot.reply.whenNotAddressed.setValue(original)
|
||||||
|
|
||||||
|
def testAddressedWithMultipleNicks(self):
|
||||||
|
msg = ircmsgs.privmsg('#foo', 'bar: baz')
|
||||||
|
self.failUnless(callbacks.addressed('bar', msg))
|
||||||
|
self.failUnless(callbacks.addressed('biff', msg, nicks=['bar']))
|
||||||
|
|
||||||
def testReply(self):
|
def testReply(self):
|
||||||
prefix = 'foo!bar@baz'
|
prefix = 'foo!bar@baz'
|
||||||
channelMsg = ircmsgs.privmsg('#foo', 'bar baz', prefix=prefix)
|
channelMsg = ircmsgs.privmsg('#foo', 'bar baz', prefix=prefix)
|
||||||
@ -259,6 +264,7 @@ class PrivmsgTestCase(ChannelPluginTestCase):
|
|||||||
original = conf.supybot.reply.errorInPrivate()
|
original = conf.supybot.reply.errorInPrivate()
|
||||||
conf.supybot.reply.errorInPrivate.setValue(False)
|
conf.supybot.reply.errorInPrivate.setValue(False)
|
||||||
m = self.getMsg("eval irc.error('foo', private=True)")
|
m = self.getMsg("eval irc.error('foo', private=True)")
|
||||||
|
self.failUnless(m, 'No message returned.')
|
||||||
self.failIf(ircutils.isChannel(m.args[0]))
|
self.failIf(ircutils.isChannel(m.args[0]))
|
||||||
finally:
|
finally:
|
||||||
conf.supybot.reply.errorInPrivate.setValue(original)
|
conf.supybot.reply.errorInPrivate.setValue(original)
|
||||||
@ -271,6 +277,7 @@ class PrivmsgTestCase(ChannelPluginTestCase):
|
|||||||
original = conf.supybot.reply.errorWithNotice()
|
original = conf.supybot.reply.errorWithNotice()
|
||||||
conf.supybot.reply.errorWithNotice.setValue(True)
|
conf.supybot.reply.errorWithNotice.setValue(True)
|
||||||
m = self.getMsg("eval irc.error('foo')")
|
m = self.getMsg("eval irc.error('foo')")
|
||||||
|
self.failUnless(m, 'No message returned.')
|
||||||
self.failUnless(m.command == 'NOTICE')
|
self.failUnless(m.command == 'NOTICE')
|
||||||
finally:
|
finally:
|
||||||
conf.supybot.reply.errorWithNotice.setValue(original)
|
conf.supybot.reply.errorWithNotice.setValue(original)
|
||||||
|
Loading…
Reference in New Issue
Block a user