Commit f21b41e1 authored by jake%acutex.net's avatar jake%acutex.net
Browse files

Fix for bug 72951 - oopsbot should have flooder defences

r= zach@zachlipton.com
a= hixie
parent c23d9eae
Loading
Loading
Loading
Loading
+102 −0
Original line number Diff line number Diff line
# -*- Mode: perl; indent-tabs-mode: nil -*-
# $Id: Flood.bm,v 1.1 2001/08/14 16:53:30 jake%acutex.net 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"}}, time());
    if (scalar(@{$self->{"join_$channel"}}) >= $self->{'numberOfJoins'}) {
        my $oldest = shift(@{$self->{"join_$channel"}});
        my $timechange = 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(@_);
    }
}