2010-03-22 08:33:44 +01:00
# File: AntiFlood.pm
2010-03-24 07:47:40 +01:00
# Author: pragma_
2010-03-17 07:36:54 +01:00
#
2010-03-22 08:33:44 +01:00
# Purpose: Keeps track of which nick has said what and when. Used in
# conjunction with OperatorStuff and Quotegrabs for kick/quiet on flood
# and grabbing quotes, respectively.
2010-03-17 07:36:54 +01:00
package PBot::AntiFlood ;
use warnings ;
use strict ;
2010-03-24 07:47:40 +01:00
use vars qw( $VERSION ) ;
$ VERSION = $ PBot:: PBot:: VERSION ;
2010-03-17 07:36:54 +01:00
use Time::HiRes qw( gettimeofday ) ;
2010-03-22 08:33:44 +01:00
use Carp ( ) ;
sub new {
if ( ref ( $ _ [ 1 ] ) eq 'HASH' ) {
Carp:: croak ( "Options to AntiFlood should be key/value pairs, not hash reference" ) ;
}
2010-03-17 07:36:54 +01:00
2010-03-22 08:33:44 +01:00
my ( $ class , % conf ) = @ _ ;
2010-03-17 07:36:54 +01:00
2010-03-22 08:33:44 +01:00
my $ self = bless { } , $ class ;
$ self - > initialize ( % conf ) ;
return $ self ;
}
2010-03-17 07:36:54 +01:00
2010-03-22 08:33:44 +01:00
sub initialize {
my ( $ self , % conf ) = @ _ ;
2010-03-17 07:36:54 +01:00
2010-03-22 08:33:44 +01:00
my $ pbot = delete $ conf { pbot } ;
if ( not defined $ pbot ) {
Carp:: croak ( "Missing pbot reference to AntiFlood" ) ;
}
$ self - > { pbot } = $ pbot ;
$ self - > { FLOOD_CHAT } = 0 ;
$ self - > { FLOOD_JOIN } = 1 ;
$ self - > { flood_msg_count } = 0 ;
$ self - > { last_timestamp } = gettimeofday ;
$ self - > { message_history } = { } ;
2010-03-23 19:24:02 +01:00
$ pbot - > timer - > register ( sub { $ self - > prune_message_history } , 60 * 60 * 1 ) ;
2010-03-22 08:33:44 +01:00
}
2010-03-17 07:36:54 +01:00
sub check_flood {
2010-03-22 08:33:44 +01:00
my ( $ self , $ channel , $ nick , $ user , $ host , $ text , $ max , $ mode ) = @ _ ;
2010-03-17 07:36:54 +01:00
my $ now = gettimeofday ;
$ channel = lc $ channel ;
2010-03-26 06:14:03 +01:00
$ self - > { pbot } - > logger - > log ( sprintf ( "%-14s | %-65s | %s\n" , $ channel , "$nick!$user\@$host" , $ text ) ) ;
2010-03-17 07:36:54 +01:00
2010-03-22 08:33:44 +01:00
return if $ nick eq $ self - > { pbot } - > botnick ;
2010-03-17 07:36:54 +01:00
2010-03-22 08:33:44 +01:00
if ( exists $ { $ self - > message_history } { $ nick } ) {
#$self->{pbot}->logger->log("nick exists\n");
2010-03-17 07:36:54 +01:00
2010-03-22 08:33:44 +01:00
if ( not exists $ { $ self - > message_history } { $ nick } { $ channel } ) {
#$self->{pbot}->logger->log("adding new channel for existing nick\n");
$ { $ self - > message_history } { $ nick } { $ channel } { offenses } = 0 ;
$ { $ self - > message_history } { $ nick } { $ channel } { messages } = [] ;
2010-03-17 07:36:54 +01:00
}
2010-03-22 08:33:44 +01:00
#$self->{pbot}->logger->log("appending new message\n");
2010-03-17 07:36:54 +01:00
2010-03-22 08:33:44 +01:00
push ( @ { $ { $ self - > message_history } { $ nick } { $ channel } { messages } } , { timestamp = > $ now , msg = > $ text , mode = > $ mode } ) ;
2010-03-17 07:36:54 +01:00
2010-03-22 08:33:44 +01:00
my $ length = $# { $ { $ self - > message_history } { $ nick } { $ channel } { messages } } + 1 ;
2010-03-17 07:36:54 +01:00
2010-03-22 08:33:44 +01:00
#$self->{pbot}->logger->log("length: $length, max nick messages: $MAX_NICK_MESSAGES\n");
2010-03-17 07:36:54 +01:00
2010-03-22 08:33:44 +01:00
if ( $ length >= $ self - > { pbot } - > { MAX_NICK_MESSAGES } ) {
my % msg = % { shift ( @ { $ { $ self - > message_history } { $ nick } { $ channel } { messages } } ) } ;
#$self->{pbot}->logger->log("shifting message off top: $msg{msg}, $msg{timestamp}\n");
2010-03-17 07:36:54 +01:00
$ length - - ;
}
2010-03-23 19:24:02 +01:00
return if ( $ channel =~ /^#/ ) and ( not exists $ { $ self - > { pbot } - > channels - > channels } { $ channel } or $ { $ self - > { pbot } - > channels - > channels } { $ channel } { is_op } == 0 ) ;
2010-03-17 07:36:54 +01:00
2010-03-22 08:33:44 +01:00
#$self->{pbot}->logger->log("length: $length, max: $max\n");
2010-03-17 07:36:54 +01:00
if ( $ length >= $ max ) {
2010-03-22 08:33:44 +01:00
# $self->{pbot}->logger->log("More than $max messages spoken, comparing time differences\n");
my % msg = % { @ { $ { $ self - > message_history } { $ nick } { $ channel } { messages } } [ $ length - $ max ] } ;
my % last = % { @ { $ { $ self - > message_history } { $ nick } { $ channel } { messages } } [ $ length - 1 ] } ;
2010-03-17 07:36:54 +01:00
2010-03-22 08:33:44 +01:00
#$self->{pbot}->logger->log("Comparing $last{timestamp} against $msg{timestamp}: " . ($last{timestamp} - $msg{timestamp}) . " seconds\n");
2010-03-17 07:36:54 +01:00
2010-03-22 08:33:44 +01:00
if ( $ last { timestamp } - $ msg { timestamp } <= 10 && not $ self - > { pbot } - > admins - > loggedin ( $ channel , "$nick!$user\@$host" ) ) {
$ { $ self - > message_history } { $ nick } { $ channel } { offenses } + + ;
my $ length = $ { $ self - > message_history } { $ nick } { $ channel } { offenses } * $ { $ self - > message_history } { $ nick } { $ channel } { offenses } * 30 ;
2010-03-17 07:36:54 +01:00
if ( $ channel =~ /^#/ ) { #channel flood (opposed to private message or otherwise)
2010-03-22 08:33:44 +01:00
if ( $ mode == $ self - > { FLOOD_CHAT } ) {
2010-03-23 19:24:02 +01:00
$ self - > { pbot } - > chanops - > quiet_nick_timed ( $ nick , $ channel , $ length ) ;
2010-03-22 08:33:44 +01:00
$ self - > { pbot } - > conn - > privmsg ( $ nick , "You have been quieted due to flooding. Please use a web paste service such as http://codepad.org for lengthy pastes. You will be allowed to speak again in $length seconds." ) ;
$ self - > { pbot } - > logger - > log ( "$nick $channel flood offense ${ $self->message_history }{$nick}{$channel}{offenses} earned $length second quiet\n" ) ;
2010-03-17 07:36:54 +01:00
}
} else { # private message flood
2010-03-22 08:33:44 +01:00
$ self - > { pbot } - > logger - > log ( "$nick msg flood offense ${ $self->message_history }{$nick}{$channel}{offenses} earned $length second ignore\n" ) ;
2010-04-02 20:23:31 +02:00
$ self - > { pbot } - > conn - > privmsg ( $ nick , "You have used too many commands in too short a time period, you have been ignored for $length seconds." ) ;
2010-03-23 04:09:03 +01:00
$ self - > { pbot } - > { ignorelistcmds } - > ignore_user ( "" , "floodcontrol" , "" , "" , "$nick!$user\@$host $channel $length" ) ;
2010-04-02 20:23:31 +02:00
2010-03-17 07:36:54 +01:00
}
}
}
} else {
2010-03-22 08:33:44 +01:00
#$self->{pbot}->logger->log("brand new nick addition\n");
2010-03-17 07:36:54 +01:00
# new addition
2010-03-22 08:33:44 +01:00
$ { $ self - > message_history } { $ nick } { $ channel } { offenses } = 0 ;
$ { $ self - > message_history } { $ nick } { $ channel } { messages } = [] ;
push ( @ { $ { $ self - > message_history } { $ nick } { $ channel } { messages } } , { timestamp = > $ now , msg = > $ text , mode = > $ mode } ) ;
2010-03-17 07:36:54 +01:00
}
}
2010-03-22 08:33:44 +01:00
sub message_history {
my $ self = shift ;
return $ self - > { message_history } ;
}
2010-03-23 19:24:02 +01:00
sub prune_message_history {
my $ self = shift ;
$ self - > { pbot } - > logger - > log ( "Pruning message history . . .\n" ) ;
2010-03-29 10:39:48 +02:00
foreach my $ nick ( keys % { $ self - > { message_history } } ) {
foreach my $ channel ( keys % { $ self - > { message_history } - > { $ nick } } )
2010-03-23 19:24:02 +01:00
{
2010-04-10 00:53:17 +02:00
#$self->{pbot}->logger->log("Checking [$nick][$channel]\n");
2010-03-29 10:39:48 +02:00
my $ length = $# { $ self - > { message_history } - > { $ nick } { $ channel } { messages } } + 1 ;
my % last = % { @ { $ self - > { message_history } - > { $ nick } { $ channel } { messages } } [ $ length - 1 ] } ;
2010-03-23 19:24:02 +01:00
if ( gettimeofday - $ last { timestamp } >= 60 * 60 * 24 ) {
$ self - > { pbot } - > logger - > log ( "$nick in $channel hasn't spoken in 24 hours, removing message history.\n" ) ;
2010-03-29 10:39:48 +02:00
delete $ self - > { message_history } - > { $ nick } { $ channel } ;
2010-03-23 19:24:02 +01:00
}
}
}
}
2010-03-17 07:36:54 +01:00
1 ;