mirror of
https://github.com/pragma-/pbot.git
synced 2024-11-26 22:09:26 +01:00
Track and handle nick-change events; add anti-nick-flood detection
This commit is contained in:
parent
1f242aeec4
commit
cd54dcb573
@ -49,7 +49,8 @@ sub initialize {
|
||||
$self->{ENTER_ABUSE_MAX_OFFENSES} = 3;
|
||||
$self->{ENTER_ABUSE_MAX_SECONDS} = 20;
|
||||
|
||||
$self->{channels} = {}; # per-channel statistics, e.g. for optimized tracking of last spoken nick for enter-abuse detection, etc
|
||||
$self->{channels} = {}; # per-channel statistics, e.g. for optimized tracking of last spoken nick for enter-abuse detection, etc
|
||||
$self->{nickflood} = {}; # statistics to track nickchange flooding
|
||||
|
||||
my $filename = delete $conf{banwhitelist_file} // $self->{pbot}->{data_dir} . '/ban_whitelist';
|
||||
$self->{ban_whitelist} = PBot::DualIndexHashObject->new(name => 'BanWhitelist', filename => $filename);
|
||||
@ -165,16 +166,22 @@ sub check_flood {
|
||||
$channel = lc $channel;
|
||||
|
||||
my $mask = "$nick!$user\@$host";
|
||||
$self->{pbot}->logger->log(sprintf("%-14s | %-65s | %s\n", $channel eq $mask ? "QUIT" : $channel, $mask, $text));
|
||||
|
||||
my $account = $self->{pbot}->{messagehistory}->get_message_account($nick, $user, $host);
|
||||
|
||||
if($mode == $self->{pbot}->{messagehistory}->{MSG_NICKCHANGE}) {
|
||||
$self->{pbot}->logger->log(sprintf("%-14s | %-65s | %s\n", "NICKCHANGE", $mask, $text));
|
||||
$self->{nickflood}->{$account}->{changes}++;
|
||||
} else {
|
||||
$self->{pbot}->logger->log(sprintf("%-14s | %-65s | %s\n", $channel eq $mask ? "QUIT" : $channel, $mask, $text));
|
||||
}
|
||||
|
||||
# handle QUIT events
|
||||
# (these events come from $channel nick!user@host, not a specific channel or nick,
|
||||
# so they need to be dispatched to all channels the nick has been seen on)
|
||||
if($mode == $self->{pbot}->{messagehistory}->{MSG_DEPARTURE} and $text =~ /^QUIT/) {
|
||||
my @channels = $self->{pbot}->{messagehistory}->{database}->get_channels($account);
|
||||
foreach my $chan (@channels) {
|
||||
next if $chan !~ m/^#/;
|
||||
$self->check_join_watch($account, $chan, $text, $mode);
|
||||
}
|
||||
|
||||
@ -183,7 +190,20 @@ sub check_flood {
|
||||
return;
|
||||
}
|
||||
|
||||
$self->check_join_watch($account, $channel, $text, $mode);
|
||||
if($mode == $self->{pbot}->{messagehistory}->{MSG_NICKCHANGE}) {
|
||||
my @channels = $self->{pbot}->{messagehistory}->{database}->get_channels($account);
|
||||
return if not @channels;
|
||||
$channel = undef;
|
||||
foreach my $chan (@channels) {
|
||||
if($chan =~ m/^#/) {
|
||||
$channel = $chan;
|
||||
last;
|
||||
}
|
||||
}
|
||||
return if not defined $channel;
|
||||
} else {
|
||||
$self->check_join_watch($account, $channel, $text, $mode);
|
||||
}
|
||||
|
||||
# do not do flood processing for bot messages
|
||||
if($nick eq $self->{pbot}->botnick) {
|
||||
@ -275,6 +295,10 @@ sub check_flood {
|
||||
my $joins = $self->{pbot}->{messagehistory}->{database}->get_recent_messages($account, $channel, $max_messages, $self->{pbot}->{messagehistory}->{MSG_JOIN});
|
||||
$msg = $joins->[0];
|
||||
}
|
||||
elsif($mode == $self->{pbot}->{messagehistory}->{MSG_NICKCHANGE}) {
|
||||
my $nickchanges = $self->{pbot}->{messagehistory}->{database}->get_recent_messages($account, $channel, $max_messages, $self->{pbot}->{messagehistory}->{MSG_NICKCHANGE});
|
||||
$msg = $nickchanges->[0];
|
||||
}
|
||||
elsif($mode == $self->{pbot}->{messagehistory}->{MSG_DEPARTURE}) {
|
||||
# no flood checks to be done for departure events
|
||||
return;
|
||||
@ -340,6 +364,22 @@ sub check_flood {
|
||||
$self->{pbot}->logger->log("$nick msg flood offense " . $channel_data->{offenses} . " earned $length ignore\n");
|
||||
$self->{pbot}->conn->privmsg($nick, "You have used too many commands in too short a time period, you have been ignored for $length.");
|
||||
}
|
||||
} elsif($mode == $self->{pbot}->{messagehistory}->{MSG_NICKCHANGE} and $self->{nickflood}->{$account}->{changes} >= $max_messages) {
|
||||
$self->{nickflood}->{$account}->{offenses}++;
|
||||
$self->{nickflood}->{$account}->{changes} = $max_messages - 2; # allow 1 more change (to go back to original nick)
|
||||
$self->{nickflood}->{$account}->{timestamp} = gettimeofday;
|
||||
|
||||
my $length = $self->{nickflood}->{$account}->{offenses} ** $self->{nickflood}->{$account}->{offenses} * $self->{nickflood}->{$account}->{offenses} * 60 * 4;
|
||||
|
||||
my @channels = $self->{pbot}->{messagehistory}->{database}->get_channels($account);
|
||||
foreach my $chan (@channels) {
|
||||
$self->{pbot}->chanops->ban_user_timed("*!$user\@$host", $chan, $length);
|
||||
}
|
||||
|
||||
($nick) = $text =~ m/NICKCHANGE (.*)/;
|
||||
$length = duration($length);
|
||||
$self->{pbot}->logger->log("$nick nickchange flood offense " . $self->{nickflood}->{$account}->{offenses} . " earned $length ban\n");
|
||||
$self->{pbot}->conn->privmsg($nick, "You have been temporarily banned due to nick-change flooding. You will be unbanned in $length.");
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -672,6 +712,18 @@ sub adjust_offenses {
|
||||
#$self->{pbot}->logger->log("[adjust-offenses] [$id][$channel] decreasing enter abuse offenses to $channel_data->{enter_abuses}\n");
|
||||
$self->{pbot}->{messagehistory}->{database}->update_channel_data($id, $channel, $channel_data);
|
||||
}
|
||||
|
||||
foreach my $account (keys %{ $self->{nickflood} }) {
|
||||
if($self->{nickflood}->{$account}->{offenses} > 0 and gettimeofday - $self->{nickflood}->{$account}->{timestamp} >= 60 * 60 * 24) {
|
||||
$self->{nickflood}->{$account}->{offenses}--;
|
||||
|
||||
if($self->{nickflood}->{$account}->{offenses} == 0) {
|
||||
delete $self->{nickflood}->{$account};
|
||||
} else {
|
||||
$self->{nickflood}->{$account}->{timestamp} = gettimeofday;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
1;
|
||||
|
@ -220,6 +220,7 @@ sub on_departure {
|
||||
# QUIT messages must be dispatched to each channel the user is on
|
||||
my @channels = $self->{pbot}->{messagehistory}->{database}->get_channels($message_account);
|
||||
foreach my $chan (@channels) {
|
||||
next if $chan !~ m/^#/;
|
||||
$self->{pbot}->{messagehistory}->add_message($message_account, "$nick!$user\@$host", $chan, $text, $self->{pbot}->{messagehistory}->{MSG_DEPARTURE});
|
||||
}
|
||||
} else {
|
||||
@ -236,6 +237,27 @@ sub on_departure {
|
||||
}
|
||||
}
|
||||
|
||||
sub on_nickchange {
|
||||
my ($self, $conn, $event) = @_;
|
||||
my ($nick, $user, $host, $newnick) = ($event->nick, $event->user, $event->host, $event->args);
|
||||
|
||||
$self->{pbot}->logger->log("$nick!$user\@$host changed nick to $newnick\n");
|
||||
|
||||
my $message_account = $self->{pbot}->{messagehistory}->{database}->get_message_account($nick, $user, $host);
|
||||
$self->{pbot}->{messagehistory}->{database}->devalidate_all_channels($message_account);
|
||||
my @channels = $self->{pbot}->{messagehistory}->{database}->get_channels($message_account);
|
||||
foreach my $channel (@channels) {
|
||||
next if $channel !~ m/^#/;
|
||||
$self->{pbot}->{messagehistory}->add_message($message_account, "$nick!$user\@$host", $channel, "NICKCHANGE $newnick", $self->{pbot}->{messagehistory}->{MSG_NICKCHANGE});
|
||||
}
|
||||
|
||||
my $newnick_account = $self->{pbot}->{messagehistory}->{database}->get_message_account($newnick, $user, $host);
|
||||
$self->{pbot}->{messagehistory}->{database}->devalidate_all_channels($newnick_account);
|
||||
$self->{pbot}->{messagehistory}->{database}->update_hostmask_data($newnick_account, { last_seen => scalar gettimeofday });
|
||||
|
||||
$self->{pbot}->antiflood->check_flood("$nick!$user\@$host", $nick, $user, $host, "NICKCHANGE $newnick", 3, 60 * 60, $self->{pbot}->{messagehistory}->{MSG_NICKCHANGE});
|
||||
}
|
||||
|
||||
sub pbot {
|
||||
my $self = shift;
|
||||
return $self->{pbot};
|
||||
|
@ -39,9 +39,10 @@ sub initialize {
|
||||
$self->{database}->begin();
|
||||
$self->{database}->devalidate_all_channels();
|
||||
|
||||
$self->{MSG_CHAT} = 0; # PRIVMSG, ACTION
|
||||
$self->{MSG_JOIN} = 1; # JOIN
|
||||
$self->{MSG_DEPARTURE} = 2; # PART, QUIT, KICK
|
||||
$self->{MSG_CHAT} = 0; # PRIVMSG, ACTION
|
||||
$self->{MSG_JOIN} = 1; # JOIN
|
||||
$self->{MSG_DEPARTURE} = 2; # PART, QUIT, KICK
|
||||
$self->{MSG_NICKCHANGE} = 3; # CHANGED NICK
|
||||
|
||||
$self->{pbot}->commands->register(sub { $self->recall_message(@_) }, "recall", 0);
|
||||
}
|
||||
|
@ -265,7 +265,7 @@ sub get_message_account {
|
||||
return $id if defined $id;
|
||||
|
||||
my $rows = eval {
|
||||
my $sth = $self->{dbh}->prepare('SELECT id,hostmask FROM Hostmasks WHERE hostmask LIKE ?');
|
||||
my $sth = $self->{dbh}->prepare('SELECT id,hostmask FROM Hostmasks WHERE hostmask LIKE ? ORDER BY last_seen DESC');
|
||||
$sth->bind_param(1, "$nick!%");
|
||||
$sth->execute();
|
||||
my $rows = $sth->fetchall_arrayref({});
|
||||
|
@ -203,6 +203,7 @@ sub connect {
|
||||
$self->conn->add_handler('join' , sub { $self->irchandlers->on_join(@_) });
|
||||
$self->conn->add_handler('kick' , sub { $self->irchandlers->on_kick(@_) });
|
||||
$self->conn->add_handler('quit' , sub { $self->irchandlers->on_departure(@_) });
|
||||
$self->conn->add_handler('nick' , sub { $self->irchandlers->on_nickchange(@_) });
|
||||
$self->conn->add_handler('pong' , sub { $self->lagchecker->on_pong(@_) });
|
||||
$self->conn->add_handler('whoisaccount' , sub { $self->antiflood->on_whoisaccount(@_) });
|
||||
$self->conn->add_handler('banlist' , sub { $self->bantracker->on_banlist_entry(@_) });
|
||||
|
@ -13,7 +13,7 @@ use warnings;
|
||||
# These are set automatically by the build/commit script
|
||||
use constant {
|
||||
BUILD_NAME => "PBot",
|
||||
BUILD_REVISION => 575,
|
||||
BUILD_REVISION => 576,
|
||||
BUILD_DATE => "2014-05-15",
|
||||
};
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user