mirror of
https://github.com/jlu5/SupyPlugins.git
synced 2025-04-30 07:21:12 -05:00
RelayNext: yet another crappy antiflood mechanism
Also, catch errors in outFilter so they don't block all of the bot's output.
This commit is contained in:
parent
d0ff6c9a16
commit
f62c6eef73
@ -61,6 +61,20 @@ conf.registerChannelValue(RelayNext, 'noHighlight',
|
|||||||
registry.Boolean(False, _("""Determines whether the bot should prefix nicks
|
registry.Boolean(False, _("""Determines whether the bot should prefix nicks
|
||||||
with a hyphen (-) to prevent excess highlights (in PRIVMSGs and actions).""")))
|
with a hyphen (-) to prevent excess highlights (in PRIVMSGs and actions).""")))
|
||||||
|
|
||||||
|
conf.registerGroup(RelayNext, 'antiflood')
|
||||||
|
conf.registerChannelValue(RelayNext.antiflood, 'enable',
|
||||||
|
registry.Boolean(False, _("""Determines whether flood prevention should be enabled
|
||||||
|
for the given channel.""")))
|
||||||
|
conf.registerChannelValue(RelayNext.antiflood, 'seconds',
|
||||||
|
registry.PositiveInteger(20, _("""Determines how many seconds messages should be queued
|
||||||
|
for flood protection.""")))
|
||||||
|
conf.registerChannelValue(RelayNext.antiflood, 'maximum',
|
||||||
|
registry.PositiveInteger(15, _("""Determines the maximum amount of incoming messages
|
||||||
|
the bot will allow from a relay channel before flood protection is triggered.""")))
|
||||||
|
conf.registerChannelValue(RelayNext.antiflood, 'timeout',
|
||||||
|
registry.PositiveInteger(60, _("""Determines the amount of time in seconds the bot should
|
||||||
|
block messages if flood protection is triggered.""")))
|
||||||
|
|
||||||
conf.registerGroup(RelayNext, 'events')
|
conf.registerGroup(RelayNext, 'events')
|
||||||
|
|
||||||
_events = ('quit', 'join', 'part', 'nick', 'mode', 'kick')
|
_events = ('quit', 'join', 'part', 'nick', 'mode', 'kick')
|
||||||
|
@ -31,6 +31,7 @@
|
|||||||
from copy import deepcopy
|
from copy import deepcopy
|
||||||
import pickle
|
import pickle
|
||||||
import re
|
import re
|
||||||
|
import traceback
|
||||||
|
|
||||||
import supybot.world as world
|
import supybot.world as world
|
||||||
import supybot.irclib as irclib
|
import supybot.irclib as irclib
|
||||||
@ -41,6 +42,7 @@ from supybot.commands import *
|
|||||||
import supybot.plugins as plugins
|
import supybot.plugins as plugins
|
||||||
import supybot.ircutils as ircutils
|
import supybot.ircutils as ircutils
|
||||||
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
|
||||||
_ = PluginInternationalization('RelayNext')
|
_ = PluginInternationalization('RelayNext')
|
||||||
@ -91,6 +93,10 @@ class RelayNext(callbacks.Plugin):
|
|||||||
self.ircstates = {}
|
self.ircstates = {}
|
||||||
self.lastmsg = {}
|
self.lastmsg = {}
|
||||||
|
|
||||||
|
# This part facilitates flood protection
|
||||||
|
self.msgcounters = {}
|
||||||
|
self.floodTriggered = False
|
||||||
|
|
||||||
self.db = {}
|
self.db = {}
|
||||||
self.loadDB()
|
self.loadDB()
|
||||||
world.flushers.append(self.exportDB)
|
world.flushers.append(self.exportDB)
|
||||||
@ -144,7 +150,7 @@ class RelayNext(callbacks.Plugin):
|
|||||||
results.append(cn[0])
|
results.append(cn[0])
|
||||||
return results
|
return results
|
||||||
|
|
||||||
def _format(self, irc, msg):
|
def _format(self, irc, msg, announcement=False):
|
||||||
s = ''
|
s = ''
|
||||||
nick = msg.nick
|
nick = msg.nick
|
||||||
userhost = ''
|
userhost = ''
|
||||||
@ -166,39 +172,48 @@ class RelayNext(callbacks.Plugin):
|
|||||||
userhost = ' (%s)' % msg.prefix.split('!', 1)[1]
|
userhost = ' (%s)' % msg.prefix.split('!', 1)[1]
|
||||||
except:
|
except:
|
||||||
pass
|
pass
|
||||||
|
if announcement:
|
||||||
if msg.command == 'NICK':
|
# Announcements use a special syntax
|
||||||
newnick = msg.args[0]
|
s = '*** %s' % announcement
|
||||||
if color:
|
else:
|
||||||
newnick = self.simpleHash(newnick)
|
if msg.command == 'NICK':
|
||||||
s = '- %s is now known as %s' % (nick, newnick)
|
newnick = msg.args[0]
|
||||||
elif msg.command == 'PRIVMSG':
|
if color:
|
||||||
text = msg.args[1]
|
newnick = self.simpleHash(newnick)
|
||||||
if re.match('^\x01ACTION .*\x01$', text):
|
s = '- %s is now known as %s' % (nick, newnick)
|
||||||
text = text[8:-1]
|
elif msg.command == 'PRIVMSG':
|
||||||
s = '* %s %s' % (nick, text)
|
text = msg.args[1]
|
||||||
else:
|
if re.match('^\x01ACTION .*\x01$', text):
|
||||||
s = '<%s> %s' % (nick, msg.args[1])
|
text = text[8:-1]
|
||||||
elif msg.command == 'JOIN':
|
s = '* %s %s' % (nick, text)
|
||||||
s = '- %s%s has joined %s' % (nick, userhost, channel)
|
else:
|
||||||
elif msg.command == 'PART':
|
s = '<%s> %s' % (nick, msg.args[1])
|
||||||
# Part message isn't a required field and can be empty sometimes
|
elif msg.command == 'JOIN':
|
||||||
try:
|
s = '- %s%s has joined %s' % (nick, userhost, channel)
|
||||||
partmsg = ' (%s)' % msg.args[1]
|
elif msg.command == 'PART':
|
||||||
except:
|
# Part message isn't a required field and can be empty sometimes
|
||||||
partmsg = ''
|
try:
|
||||||
s = '- %s%s has left %s%s' % (nick, userhost, channel, partmsg)
|
partmsg = ' (%s)' % msg.args[1]
|
||||||
elif msg.command == 'QUIT':
|
except:
|
||||||
s = '- %s has quit (%s)' % (nick, msg.args[0])
|
partmsg = ''
|
||||||
elif msg.command == 'MODE':
|
s = '- %s%s has left %s%s' % (nick, userhost, channel, partmsg)
|
||||||
modes = ' '.join(msg.args[1:])
|
elif msg.command == 'QUIT':
|
||||||
s = '- %s%s set mode %s on %s' % (nick, userhost, modes, channel)
|
s = '- %s has quit (%s)' % (nick, msg.args[0])
|
||||||
|
elif msg.command == 'MODE':
|
||||||
|
modes = ' '.join(msg.args[1:])
|
||||||
|
s = '- %s%s set mode %s on %s' % (nick, userhost, modes, channel)
|
||||||
|
|
||||||
if s: # Add the network name and some final touch-ups
|
if s: # Add the network name and some final touch-ups
|
||||||
s = "\x02[%s]\x02 %s" % (netname, s)
|
s = "\x02[%s]\x02 %s" % (netname, s)
|
||||||
s = s.replace("- -", "-", 1)
|
s = s.replace("- -", "-", 1)
|
||||||
return s
|
return s
|
||||||
|
|
||||||
|
def checkFlood(self, channel, source, command):
|
||||||
|
maximum = self.registryValue("antiflood.maximum", channel)
|
||||||
|
enabled = self.registryValue("antiflood.enable", channel)
|
||||||
|
if enabled and len(self.msgcounters[(source, command)]) > maximum:
|
||||||
|
return True
|
||||||
|
|
||||||
def relay(self, irc, msg, channel=None):
|
def relay(self, irc, msg, channel=None):
|
||||||
channel = channel or msg.args[0]
|
channel = channel or msg.args[0]
|
||||||
# Get the source channel
|
# Get the source channel
|
||||||
@ -206,6 +221,29 @@ class RelayNext(callbacks.Plugin):
|
|||||||
source = source.lower()
|
source = source.lower()
|
||||||
out_s = self._format(irc, msg)
|
out_s = self._format(irc, msg)
|
||||||
if out_s:
|
if out_s:
|
||||||
|
### Begin Flood checking clause
|
||||||
|
timeout = self.registryValue("antiflood.timeout", channel)
|
||||||
|
seconds = self.registryValue("antiflood.seconds", channel)
|
||||||
|
maximum = self.registryValue("antiflood.maximum", channel)
|
||||||
|
try:
|
||||||
|
self.msgcounters[(source, msg.command)].enqueue(msg.prefix)
|
||||||
|
except KeyError:
|
||||||
|
self.msgcounters[(source, msg.command)] = TimeoutQueue(seconds)
|
||||||
|
if self.checkFlood(channel, source, msg.command):
|
||||||
|
self.log.debug("RelayNext (%s): message from %s blocked by "
|
||||||
|
"flood preotection.", irc.network, channel)
|
||||||
|
if self.floodTriggered:
|
||||||
|
return
|
||||||
|
c = msg.command.lower()
|
||||||
|
e = format("Flood detected on %s (%s %ss/%s seconds), "
|
||||||
|
"not relaying %ss for %s seconds!", channel,
|
||||||
|
maximum, c, seconds, c, timeout)
|
||||||
|
out_s = self._format(irc, msg, announcement=e)
|
||||||
|
self.log.info("RelayNext (%s): %s", irc.network, e)
|
||||||
|
self.floodTriggered = True
|
||||||
|
else:
|
||||||
|
self.floodTriggered = False
|
||||||
|
### End Flood checking clause
|
||||||
for relay in self.db.values():
|
for relay in self.db.values():
|
||||||
if source in relay: # If our channel is in a relay
|
if source in relay: # If our channel is in a relay
|
||||||
# Remove ourselves so we don't get duplicated messages
|
# Remove ourselves so we don't get duplicated messages
|
||||||
@ -255,12 +293,18 @@ class RelayNext(callbacks.Plugin):
|
|||||||
def outFilter(self, irc, msg):
|
def outFilter(self, irc, msg):
|
||||||
# Catch our own messages and send them into the relay (this is
|
# Catch our own messages and send them into the relay (this is
|
||||||
# useful because Supybot is often a multi-purpose bot!)
|
# useful because Supybot is often a multi-purpose bot!)
|
||||||
if msg.command == 'PRIVMSG' and not msg.relayedMsg:
|
try:
|
||||||
if msg.args[0] in self._getAllRelaysForNetwork(irc):
|
if msg.command == 'PRIVMSG' and not msg.relayedMsg:
|
||||||
new_msg = deepcopy(msg)
|
if msg.args[0] in self._getAllRelaysForNetwork(irc):
|
||||||
new_msg.nick = irc.nick
|
new_msg = deepcopy(msg)
|
||||||
self.relay(irc, new_msg, channel=msg.args[0])
|
new_msg.nick = irc.nick
|
||||||
return msg
|
self.relay(irc, new_msg, channel=msg.args[0])
|
||||||
|
except Exception as e:
|
||||||
|
# We want to log errors, but not block the bot's output
|
||||||
|
traceback.print_exc()
|
||||||
|
log.error(str(e))
|
||||||
|
finally:
|
||||||
|
return msg
|
||||||
|
|
||||||
### User commands
|
### User commands
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user