3
0
mirror of https://github.com/pragma-/pbot.git synced 2024-10-02 01:18:40 +02:00
pbot/PBot/BanTracker.pm
Pragmatic Software d1e7abc175 Add event dispatcher module to allow modules to dispatch events and
to register handlers to listen for events.

Update IRCHandlers module to use new event dispatcher to dispatch
irc.events.  Update several modules to register with dispatcher
to listen for irc.events.

Improve handling of reconnection upon unexpected disconnection.

Todo: update more things to use new event dispatcher logic.
2014-11-01 00:15:21 +00:00

163 lines
5.2 KiB
Perl

# File: BanTracker.pm
# Author: pragma_
#
# Purpose: Populates and maintains channel banlists by checking mode +b on
# joining channels and by tracking modes +b and -b in channels.
#
# Does NOT do banning or unbanning.
package PBot::BanTracker;
use warnings;
use strict;
use Time::HiRes qw/gettimeofday/;
use Time::Duration;
use Data::Dumper;
use Carp ();
sub new {
if(ref($_[1]) eq 'HASH') {
Carp::croak("Options to BanTracker should be key/value pairs, not hash reference");
}
my ($class, %conf) = @_;
my $self = bless {}, $class;
$self->initialize(%conf);
return $self;
}
sub initialize {
my ($self, %conf) = @_;
$self->{pbot} = delete $conf{pbot} // Carp::croak("Missing pbot reference to BanTracker");
$self->{banlist} = {};
$self->{pbot}->{registry}->add_default('text', 'bantracker', 'chanserv_ban_timeout', '604800');
$self->{pbot}->{commands}->register(sub { $self->dumpbans(@_) }, "dumpbans", 60);
$self->{pbot}->{event_dispatcher}->register_handler('irc.endofnames', sub { $self->get_banlist(@_) });
$self->{pbot}->{event_dispatcher}->register_handler('irc.banlist', sub { $self->on_banlist_entry(@_) });
$self->{pbot}->{event_dispatcher}->register_handler('irc.quietlist', sub { $self->on_quietlist_entry(@_) });
}
sub dumpbans {
my ($self, $from, $nick, $user, $host, $arguments) = @_;
my $bans = Dumper($self->{banlist});
return $bans;
}
sub get_banlist {
my ($self, $event_type, $event) = @_;
my $channel = lc $event->{event}->{args}[1];
delete $self->{banlist}->{$channel};
$self->{pbot}->{logger}->log("Retrieving banlist for $channel.\n");
$event->{conn}->sl("mode $channel +b");
$event->{conn}->sl("mode $channel +q");
return 0;
}
sub on_banlist_entry {
my ($self, $event_type, $event) = @_;
my $channel = lc $event->{event}->{args}[1];
my $target = lc $event->{event}->{args}[2];
my $source = lc $event->{event}->{args}[3];
my $timestamp = $event->{event}->{args}[4];
my $ago = ago(gettimeofday - $timestamp);
$self->{pbot}->{logger}->log("ban-tracker: [banlist entry] $channel: $target banned by $source $ago.\n");
$self->{banlist}->{$channel}->{'+b'}->{$target} = [ $source, $timestamp ];
return 0;
}
sub on_quietlist_entry {
my ($self, $event_type, $event) = @_;
my $channel = lc $event->{event}->{args}[1];
my $target = lc $event->{event}->{args}[3];
my $source = lc $event->{event}->{args}[4];
my $timestamp = $event->{event}->{args}[5];
my $ago = ago(gettimeofday - $timestamp);
$self->{pbot}->{logger}->log("ban-tracker: [quietlist entry] $channel: $target quieted by $source $ago.\n");
$self->{banlist}->{$channel}->{'+q'}->{$target} = [ $source, $timestamp ];
return 0;
}
sub get_baninfo {
my ($self, $mask, $channel, $account) = @_;
my ($bans, $ban_account);
$account = undef if $account eq '-1';
$account = lc $account if defined $account;
$self->{pbot}->{logger}->log("[get-baninfo] Getting baninfo for $mask in $channel using account " . (defined $account ? $account : "[undefined]") . "\n");
foreach my $mode (keys %{ $self->{banlist}{$channel} }) {
foreach my $banmask (keys %{ $self->{banlist}{$channel}{$mode} }) {
if($banmask =~ m/^\$a:(.*)/) {
$ban_account = lc $1;
} else {
$ban_account = "";
}
my $banmask_key = $banmask;
$banmask = quotemeta $banmask;
$banmask =~ s/\\\*/.*?/g;
$banmask =~ s/\\\?/./g;
if((defined $account and $account eq $ban_account) or $mask =~ m/^$banmask$/i) {
if(not defined $bans) {
$bans = [];
}
my $baninfo = {};
$baninfo->{banmask} = $banmask_key;
$baninfo->{channel} = $channel;
$baninfo->{owner} = $self->{banlist}{$channel}{$mode}{$banmask_key}[0];
$baninfo->{when} = $self->{banlist}{$channel}{$mode}{$banmask_key}[1];
$baninfo->{type} = $mode;
$self->{pbot}->{logger}->log("get-baninfo: dump: " . Dumper($baninfo) . "\n");
push @$bans, $baninfo;
}
}
}
return $bans;
}
sub track_mode {
my $self = shift;
my ($source, $mode, $target, $channel) = @_;
if($mode eq "+b" or $mode eq "+q") {
$self->{pbot}->{logger}->log("ban-tracker: $target " . ($mode eq '+b' ? 'banned' : 'quieted') . " by $source in $channel.\n");
$self->{banlist}->{$channel}->{$mode}->{$target} = [ $source, gettimeofday ];
$self->{pbot}->{antiflood}->devalidate_accounts($target, $channel);
}
elsif($mode eq "-b" or $mode eq "-q") {
$self->{pbot}->{logger}->log("ban-tracker: $target " . ($mode eq '-b' ? 'unbanned' : 'unquieted') . " by $source in $channel.\n");
delete $self->{banlist}->{$channel}->{$mode eq "-b" ? "+b" : "+q"}->{$target};
if($mode eq "-b") {
if($self->{pbot}->{chanops}->{unban_timeout}->find_index($channel, $target)) {
$self->{pbot}->{chanops}->{unban_timeout}->remove($channel, $target);
} elsif($self->{pbot}->{chanops}->{unban_timeout}->find_index($channel, "$target\$##stop_join_flood")) {
# freenode strips channel forwards from unban result if no ban exists with a channel forward
$self->{pbot}->{chanops}->{unban_timeout}->remove($channel, "$target\$##stop_join_flood");
}
}
} else {
$self->{pbot}->{logger}->log("BanTracker: Unknown mode '$mode'\n");
}
}
1;