RelayLink: add experimental flood protection as per #3 (still needs to be tested!)

This commit is contained in:
GLolol 2014-07-03 14:09:10 -07:00
parent 1ca254ab51
commit 703ed5d35a
2 changed files with 248 additions and 143 deletions

View File

@ -85,13 +85,42 @@ conf.registerChannelValue(RelayLink.ignore, 'affectPrivmsgs',
from the nicks listed in ignore. If set to False, the bot will only from the nicks listed in ignore. If set to False, the bot will only
ignore joins/parts/nicks/modes/quits (not kicks) from those nicks."""))) ignore joins/parts/nicks/modes/quits (not kicks) from those nicks.""")))
# conf.registerGroup(RelayLink, 'sepTags') # class FloodPreventionConfigHandler(registry.String):
# conf.registerChannelValue(RelayLink.sepTags, 'channels', # """Invalid input. This value should be given in the form 'positiveInt:positiveInt'
# registry.String('@', _("""Determines the separator string used for the # (amount:seconds)"""
# bot for channels (when both nicks and IncludeNetwork are on)."""))) # def setValue(self, v):
# conf.registerChannelValue(RelayLink.sepTags, 'nicks', # try:
# registry.String('/', _("""Determines the separator string used for the # i = [int(n) for n in v.split(":")]
# bot for nicks (when both nicks and IncludeNetwork are on)."""))) # except ValueError:
# self.error()
# return
# if len(i) < 2 or i[0] < 0 or i[1] < 0:
# self.error()
# return
# else:
# registry.String.setValue(self, v)
conf.registerGroup(RelayLink, 'antiflood')
conf.registerGlobalValue(RelayLink.antiflood, 'enable',
registry.Boolean(False, _("""Determines whether flood protection should
be used by the relayer.""")))
conf.registerGlobalValue(RelayLink.antiflood, 'privmsgs',
registry.NonNegativeInteger(0, _("""Determines how many PRIVMSGs the bot will allow
before flood protection is triggered. This setting should be set based on how much
traffic a channel gets, so a default is not included. Setting this' to 0
effectively disables flood prevention.""")))
conf.registerGlobalValue(RelayLink.antiflood, 'nonPrivmsgs',
registry.NonNegativeInteger(0, _("""Determines how many non-PRIVMSG
events (joins, parts, nicks, etc.) the bot will allow before flood
protection is triggered. This setting should be set based on how much
traffic a channel gets, so a default is not included. Setting this to
0 effectively disables flood prevention.""")))
conf.registerGlobalValue(RelayLink.antiflood, 'seconds',
registry.PositiveInteger(30, _("""Determines how many seconds the bot
should wait before relaying if flood prevention is triggered.""")))
conf.registerGlobalValue(RelayLink.antiflood, 'announce',
registry.Boolean(True, _("""Determines whether the bot should announce
flood alerts to the channel.""")))
class ValidNonPrivmsgsHandling(registry.OnlySomeStrings): class ValidNonPrivmsgsHandling(registry.OnlySomeStrings):
validStrings = ('privmsg', 'notice', 'nothing') validStrings = ('privmsg', 'notice', 'nothing')

View File

@ -44,6 +44,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
import supybot.callbacks as callbacks import supybot.callbacks as callbacks
from supybot.utils.structures import TimeoutQueue
try: try:
from supybot.i18n import PluginInternationalization from supybot.i18n import PluginInternationalization
from supybot.i18n import internationalizeDocstring from supybot.i18n import internationalizeDocstring
@ -79,6 +80,11 @@ class RelayLink(callbacks.Plugin):
self.ircstates = {} self.ircstates = {}
for IRC in world.ircs: for IRC in world.ircs:
self.addIRC(IRC) self.addIRC(IRC)
floodProtectTimeout = conf.supybot.plugins.RelayLink.antiflood.seconds
self.nonPrivmsgCounter = TimeoutQueue(floodProtectTimeout)
self.privmsgCounter = TimeoutQueue(floodProtectTimeout)
self.floodActivated = False
# self.nonPrivmsgsCounter = self.privmsgsCounter = 0
try: try:
conf.supybot.plugins.RelayLink.substitutes.addCallback( conf.supybot.plugins.RelayLink.substitutes.addCallback(
self._loadFromConfig) self._loadFromConfig)
@ -127,7 +133,31 @@ class RelayLink(callbacks.Plugin):
num = num % 11 num = num % 11
return colors[num] return colors[num]
def floodDetect(self):
if self.registryValue("antiflood.announce") and not self.floodActivated:
msgs = self.registryValue("antiflood.nonPrivmsgs")
secs = self.registryValue("antiflood.seconds")
s = ("%(network)s*** Flood detected ({msgs} non-PRIVMSG messages in {secs} seconds). Not relaying messages"
" for {secs} seconds!".format(secs=secs, msgs=msgs))
self.floodActivated = True
return s
else:
return
def getPrivmsgData(self, channel, nick, text, colored): def getPrivmsgData(self, channel, nick, text, colored):
if self.registryValue("antiflood.enable") and \
self.registryValue("antiflood.privmsgs") > 0 and \
(len(self.privmsgCounter) > self.registryValue("antiflood.privmsgs")):
if self.registryValue("antiflood.announce") and not self.floodActivated:
msgs = self.registryValue("antiflood.privmsgs")
secs = self.registryValue("antiflood.seconds")
s = ("%(network)s*** Flood detected ({msgs} messages in {secs} seconds). Not relaying messages"
" for {secs} seconds!".format(secs=secs, msgs=msgs)), {}
self.floodActivated = True
return s
else:
return
self.floodActivated = False
color = self.simpleHash(nick) color = self.simpleHash(nick)
nickprefix = '' nickprefix = ''
if nick in self.nickSubstitutions: if nick in self.nickSubstitutions:
@ -184,6 +214,7 @@ class RelayLink(callbacks.Plugin):
def doPrivmsg(self, irc, msg): def doPrivmsg(self, irc, msg):
self.addIRC(irc) self.addIRC(irc)
self.privmsgCounter.enqueue([0])
channel = msg.args[0] channel = msg.args[0]
s = msg.args[1] s = msg.args[1]
s, args = self.getPrivmsgData(channel, msg.nick, s, s, args = self.getPrivmsgData(channel, msg.nick, s,
@ -191,7 +222,7 @@ class RelayLink(callbacks.Plugin):
ignoreNicks = [ircutils.toLower(item) for item in \ ignoreNicks = [ircutils.toLower(item) for item in \
self.registryValue('ignore.nicks', msg.args[0])] self.registryValue('ignore.nicks', msg.args[0])]
if self.registryValue('ignore.affectPrivmsgs', msg.args[0]) \ if self.registryValue('ignore.affectPrivmsgs', msg.args[0]) \
== 1 and ircutils.toLower(msg.nick) in ignoreNicks: and ircutils.toLower(msg.nick) in ignoreNicks:
return return
elif channel not in irc.state.channels: # in private elif channel not in irc.state.channels: # in private
# cuts off the end of commands, so that passwords # cuts off the end of commands, so that passwords
@ -222,10 +253,19 @@ class RelayLink(callbacks.Plugin):
def doMode(self, irc, msg): def doMode(self, irc, msg):
ignoreNicks = [ircutils.toLower(item) for item in \ ignoreNicks = [ircutils.toLower(item) for item in \
self.registryValue('ignore.nicks', msg.args[0])] self.registryValue('ignore.nicks', msg.args[0])]
if ircutils.toLower(msg.nick) not in ignoreNicks:
self.addIRC(irc) self.addIRC(irc)
self.nonPrivmsgCounter.enqueue([0])
args = {'nick': msg.nick, 'channel': msg.args[0], args = {'nick': msg.nick, 'channel': msg.args[0],
'mode': ' '.join(msg.args[1:]), 'userhost': ''} 'mode': ' '.join(msg.args[1:]), 'userhost': ''}
if self.registryValue("antiflood.enable") and \
self.registryValue("antiflood.nonprivmsgs") > 0 and \
(len(self.nonPrivmsgCounter) > self.registryValue("antiflood.nonprivmsgs")):
s = self.floodDetect()
if s:
self.sendToOthers(irc, msg.args[0], s, args)
else: return
elif ircutils.toLower(msg.nick) not in ignoreNicks:
self.floodActivated = False
if self.registryValue('color', msg.args[0]): if self.registryValue('color', msg.args[0]):
# args['color'] = '\x03%s' % self.registryValue('colors.mode', msg.args[0]) # args['color'] = '\x03%s' % self.registryValue('colors.mode', msg.args[0])
args['nick'] = '\x03%s%s\x03' % (self.simpleHash(msg.nick), msg.nick) args['nick'] = '\x03%s%s\x03' % (self.simpleHash(msg.nick), msg.nick)
@ -245,7 +285,16 @@ class RelayLink(callbacks.Plugin):
s = '%(network)s\x0309*** Relay joined to %(channel)s' s = '%(network)s\x0309*** Relay joined to %(channel)s'
else: else:
s = '%(network)s*** Relay joined to %(channel)s' s = '%(network)s*** Relay joined to %(channel)s'
self.nonPrivmsgCounter.enqueue([0])
if self.registryValue("antiflood.enable") and \
self.registryValue("antiflood.nonprivmsgs") > 0 and \
(len(self.nonPrivmsgCounter) > self.registryValue("antiflood.nonprivmsgs")):
s = self.floodDetect()
if s:
self.sendToOthers(irc, msg.args[0], s, args)
else: return
elif ircutils.toLower(msg.nick) not in ignoreNicks: elif ircutils.toLower(msg.nick) not in ignoreNicks:
self.floodActivated = False
if self.registryValue('color', msg.args[0]): if self.registryValue('color', msg.args[0]):
args['nick'] = '\x03%s%s\x03' % (self.simpleHash(msg.nick), msg.nick) args['nick'] = '\x03%s%s\x03' % (self.simpleHash(msg.nick), msg.nick)
if self.registryValue('hostmasks', msg.args[0]): if self.registryValue('hostmasks', msg.args[0]):
@ -258,10 +307,19 @@ class RelayLink(callbacks.Plugin):
def doPart(self, irc, msg): def doPart(self, irc, msg):
ignoreNicks = [ircutils.toLower(item) for item in \ ignoreNicks = [ircutils.toLower(item) for item in \
self.registryValue('ignore.nicks', msg.args[0])] self.registryValue('ignore.nicks', msg.args[0])]
if ircutils.toLower(msg.nick) not in ignoreNicks: self.nonPrivmsgCounter.enqueue([0])
self.addIRC(irc)
args = {'nick': msg.nick, 'channel': msg.args[0], 'message': '', args = {'nick': msg.nick, 'channel': msg.args[0], 'message': '',
'userhost': ''} 'userhost': ''}
if self.registryValue("antiflood.enable") and \
self.registryValue("antiflood.nonprivmsgs") > 0 and \
(len(self.nonPrivmsgCounter) > self.registryValue("antiflood.nonprivmsgs")):
s = self.floodDetect()
if s:
self.sendToOthers(irc, msg.args[0], s, args)
else: return
elif ircutils.toLower(msg.nick) not in ignoreNicks:
self.addIRC(irc)
self.floodActivated = False
if self.registryValue('color', msg.args[0]): if self.registryValue('color', msg.args[0]):
args['nick'] = '\x03%s%s\x03' % (self.simpleHash(msg.nick), msg.nick) args['nick'] = '\x03%s%s\x03' % (self.simpleHash(msg.nick), msg.nick)
if self.registryValue('hostmasks', msg.args[0]): if self.registryValue('hostmasks', msg.args[0]):
@ -277,6 +335,15 @@ class RelayLink(callbacks.Plugin):
self.addIRC(irc) self.addIRC(irc)
args = {'kicked': msg.args[1], 'channel': msg.args[0], args = {'kicked': msg.args[1], 'channel': msg.args[0],
'kicker': msg.nick, 'message': msg.args[2], 'userhost': ''} 'kicker': msg.nick, 'message': msg.args[2], 'userhost': ''}
self.nonPrivmsgCounter.enqueue([0])
if self.registryValue("antiflood.enable") and \
self.registryValue("antiflood.nonprivmsgs") > 0 and \
(len(self.nonPrivmsgCounter) > self.registryValue("antiflood.nonprivmsgs")):
s = self.floodDetect()
if s:
self.sendToOthers(irc, msg.args[0], s, args)
else: return
self.floodActivated = False
if self.registryValue('color', msg.args[0]): if self.registryValue('color', msg.args[0]):
args['kicked'] = '\x03%s%s\x03' % (self.simpleHash(msg.args[1]), msg.args[1]) args['kicked'] = '\x03%s%s\x03' % (self.simpleHash(msg.args[1]), msg.args[1])
if self.registryValue('hostmasks', msg.args[0]): if self.registryValue('hostmasks', msg.args[0]):
@ -291,9 +358,18 @@ class RelayLink(callbacks.Plugin):
def doNick(self, irc, msg): def doNick(self, irc, msg):
ignoreNicks = [ircutils.toLower(item) for item in \ ignoreNicks = [ircutils.toLower(item) for item in \
self.registryValue('ignore.nicks')] self.registryValue('ignore.nicks')]
if ircutils.toLower(msg.nick) not in ignoreNicks:
self.addIRC(irc) self.addIRC(irc)
args = {'oldnick': msg.nick, 'newnick': msg.args[0]} args = {'oldnick': msg.nick, 'newnick': msg.args[0]}
self.nonPrivmsgCounter.enqueue([0])
if self.registryValue("antiflood.enable") and \
self.registryValue("antiflood.nonprivmsgs") > 0 and \
(len(self.nonPrivmsgCounter) > self.registryValue("antiflood.nonprivmsgs")):
s = self.floodDetect()
if s:
self.sendToOthers(irc, msg.args[0], s, args)
else: return
self.floodActivated = False
if ircutils.toLower(msg.nick) not in ignoreNicks:
if self.registryValue('color'): if self.registryValue('color'):
args['oldnick'] = '\x03%s%s\x03' % (self.simpleHash(msg.nick), msg.nick) args['oldnick'] = '\x03%s%s\x03' % (self.simpleHash(msg.nick), msg.nick)
args['newnick'] = '\x03%s%s\x03' % (self.simpleHash(msg.args[0]), msg.args[0]) args['newnick'] = '\x03%s%s\x03' % (self.simpleHash(msg.args[0]), msg.args[0])