# -*- Mode: perl; indent-tabs-mode: nil -*- # $Id: Flood.bm,v 1.2 2003/10/03 15:46:54 ian%hixie.ch Exp $ ########################### # Flood Protection module # ########################### package BotModules::Flood; use vars qw(@ISA); @ISA = qw(BotModules); 1; sub RegisterConfig { my $self = shift; $self->SUPER::RegisterConfig(@_); foreach my $chan (@{$self->{'channels'}}) { $self->registerVariables( ["join_$chan", 0, 0, []] ); } $self->registerVariables( ['numberOfJoins', 1, 1, '7'], ['secondsToTrigger', 1, 1, '2'], ['minutesToProtect', 1, 1, '5'], ); } sub Help { my $self = shift; my ($event) = @_; return { '' => 'This module will help control "join flood" attacks on IRC', }; } # Set - called to set a variable to a particular value. sub Set { my $self = shift; my ($event, $variable, $value) = @_; # If changing the setting for numberOfJoins make sure # that the arrays are empty. Otherwise, reducing the # numberOfJoins value would not work properly. if ($variable eq 'numberOfJoins') { foreach my $chan (@{$self->{'channels'}}) { @{$self->{"join_$chan"}} = (); } } # now actually do the setting of the variable return $self->SUPER::Set($event, $variable, $value); } sub JoinedChannel { my $self = shift; my ($event, $channel) = @_; $self->registerVariables( ["join_$channel", 0, 0, []] ); return $self->SUPER::JoinedChannel($event, $channel); # call inherited method } sub SpottedJoin { my $self = shift; my ($event, $channel, $who) = @_; # If numberOfJoins or secondsToTrigger is not a positive Integer, don't do anything if ($self->{'numberOfJoins'} !~ m/^[1-9][0-9]*$/o || $self->{'secondsToTrigger'} !~ m/^[1-9][0-9]*$/o) { # We didn't do anything, so don't pretend like we did :) return $self->SUPER::SpottedJoin($event, $channel, $who); } # Here we have the 'join_times' array to push and shift to/from push(@{$self->{"join_$channel"}}, $event->{'time'}); if (scalar(@{$self->{"join_$channel"}}) >= $self->{'numberOfJoins'}) { my $oldest = shift(@{$self->{"join_$channel"}}); my $timechange = $event->{'time'} - $oldest; if ($self->{'secondsToTrigger'} >= $timechange) { # We have just seen many joins happen very quickly. This channel should # have its mode set to +i until an op can figure out what went wrong. $self->mode($event, $event->{'channel'}, '+i'); my $extra_text = ""; # If minutesToProtect is a positive integer we should set mode -i after # that number of minutes has passed. if ($self->{'minutesToProtect'} =~ m/^[1-9][0-9]*$/o) { my $seconds = $self->{'minutesToProtect'} * 60; my @mode = ('mode', $event->{'channel'}, '-i'); $self->schedule($event, $seconds, 1, @mode); $extra_text = "I'll set it -i in $self->{'minutesToProtect'} minutes"; } $self->say($event, "I just saw a lot of joins happen very quickly. Because of " . "that I set this channel's mode to be Invite Only... $extra_text"); } } # By returning 0 we ensure that a join won't be processed more than once. return 0; } sub Scheduled { my $self = shift; my ($event, @data) = @_; my $what = shift(@data); if ($what eq 'mode') { $self->mode($event, @data); } else { # Call the inherited event return $self->SUPER::Schedule(@_); } }