diff --git a/PBot/AntiFlood.pm b/PBot/AntiFlood.pm index 4e9f4c10..291e3908 100644 --- a/PBot/AntiFlood.pm +++ b/PBot/AntiFlood.pm @@ -393,7 +393,7 @@ sub check_flood { $self->{pbot}->{messagehistory}->{database}->update_channel_data($account, $channel, $channel_data); } else { # private message flood - next if exists ${ $self->{pbot}->{ignorelist}->{ignore_list} }{"$nick!$user\@$host"}{$channel}; + next if exists $self->{pbot}->{ignorelist}->{ignore_list}->{".*!$user\@$host"}->{$channel}; my $channel_data = $self->{pbot}->{messagehistory}->{database}->get_channel_data($account, $channel, 'offenses', 'last_offense'); $channel_data->{offenses}++; @@ -402,7 +402,7 @@ sub check_flood { my $length = $self->{pbot}->{registry}->get_array_value('antiflood', 'chat_flood_punishment', $channel_data->{offenses} - 1); - $self->{pbot}->{ignorelist}->{commands}->ignore_user("", "floodcontrol", "", "", "$nick!$user\@$host $channel $length"); + $self->{pbot}->{ignorelist}->add(".*!$user\@$host", $channel, $length); $length = duration($length); $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."); @@ -583,14 +583,14 @@ sub check_bans { CHECKBAN: if($check_ban) { - $self->{pbot}->{logger}->log("anti-flood: [check-bans] checking shitlist for $hostmask->{hostmask} in channel $channel\n") if $debug_checkban >= 4; - if ($self->{pbot}->{shitlist}->check_shitlist($hostmask->{hostmask}, $channel)) { + $self->{pbot}->{logger}->log("anti-flood: [check-bans] checking blacklist for $hostmask->{hostmask} in channel $channel\n") if $debug_checkban >= 4; + if ($self->{pbot}->{blacklist}->check_blacklist($hostmask->{hostmask}, $channel)) { my $baninfo = {}; $baninfo->{banmask} = $hostmask->{hostmask}; $baninfo->{channel} = $channel; - $baninfo->{owner} = 'shitlist'; + $baninfo->{owner} = 'blacklist'; $baninfo->{when} = 0; - $baninfo->{type} = 'shitlist'; + $baninfo->{type} = 'blacklist'; push @$bans, $baninfo; next; } @@ -662,7 +662,7 @@ sub check_bans { $self->{pbot}->{logger}->log("anti-flood: [check-bans] $mask evaded $baninfo->{banmask} banned in $baninfo->{channel} by $baninfo->{owner}, banning $banmask\n"); my ($bannick) = $mask =~ m/^([^!]+)/; if($self->{pbot}->{registry}->get_value('antiflood', 'enforce')) { - if ($baninfo->{type} eq 'shitlist') { + if ($baninfo->{type} eq 'blacklist') { $self->{pbot}->{chanops}->add_op_command($baninfo->{channel}, "kick $baninfo->{channel} $bannick I don't think so"); } else { $self->{pbot}->{chanops}->add_op_command($baninfo->{channel}, "kick $baninfo->{channel} $bannick Ban evasion"); diff --git a/PBot/BlackList.pm b/PBot/BlackList.pm new file mode 100644 index 00000000..53752fb7 --- /dev/null +++ b/PBot/BlackList.pm @@ -0,0 +1,205 @@ +# File: BlackList.pm +# Author: pragma_ +# +# Purpose: Manages list of hostmasks that are not allowed to join a channel. + +package PBot::BlackList; + +use warnings; +use strict; + +use feature 'switch'; +no if $] >= 5.018, warnings => "experimental::smartmatch"; + +use Carp (); +use Time::HiRes qw(gettimeofday); + +sub new { + if(ref($_[1]) eq 'HASH') { + Carp::croak("Options to " . __FILE__ . " 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 " . __FILE__); + $self->{filename} = delete $conf{filename}; + + $self->{blacklist} = {}; + + $self->{pbot}->{commands}->register(sub { return $self->blacklist(@_) }, "blacklist", 10); + + $self->load_blacklist; +} + +sub add { + my ($self, $channel, $hostmask) = @_; + + $self->{blacklist}->{lc $channel}->{lc $hostmask} = 1; + $self->save_blacklist(); +} + +sub remove { + my $self = shift; + my ($channel, $hostmask) = @_; + + $channel = lc $channel; + $hostmask = lc $hostmask; + + if (exists $self->{blacklist}->{$channel}) { + delete $self->{blacklist}->{$channel}->{$hostmask}; + } + + if (keys $self->{blacklist}->{$channel} == 0) { + delete $self->{blacklist}->{$channel}; + } + + $self->save_blacklist(); +} + +sub load_blacklist { + my $self = shift; + my $filename; + + if(@_) { $filename = shift; } else { $filename = $self->{filename}; } + + if(not defined $filename) { + Carp::carp "No blacklist path specified -- skipping loading of blacklist"; + return; + } + + $self->{pbot}->{logger}->log("Loading blacklist from $filename ...\n"); + + open(FILE, "< $filename") or Carp::croak "Couldn't open $filename: $!\n"; + my @contents = ; + close(FILE); + + my $i = 0; + + foreach my $line (@contents) { + chomp $line; + $i++; + + my ($channel, $hostmask) = split(/\s+/, $line); + + if(not defined $hostmask || not defined $channel) { + Carp::croak "Syntax error around line $i of $filename\n"; + } + + if(exists $self->{blacklist}->{$channel}->{$hostmask}) { + Carp::croak "Duplicate blacklist entry [$hostmask][$channel] found in $filename around line $i\n"; + } + + $self->{blacklist}->{$channel}->{$hostmask} = 1; + } + + $self->{pbot}->{logger}->log(" $i entries in blacklist\n"); + $self->{pbot}->{logger}->log("Done.\n"); +} + +sub save_blacklist { + my $self = shift; + my $filename; + + if(@_) { $filename = shift; } else { $filename = $self->{filename}; } + + if(not defined $filename) { + Carp::carp "No blacklist path specified -- skipping saving of blacklist\n"; + return; + } + + open(FILE, "> $filename") or die "Couldn't open $filename: $!\n"; + + foreach my $channel (keys %{ $self->{blacklist} }) { + foreach my $hostmask (keys %{ $self->{blacklist}->{$channel} }) { + print FILE "$channel $hostmask\n"; + } + } + + close(FILE); +} + +sub check_blacklist { + my $self = shift; + my ($hostmask, $channel) = @_; + + return 0 if not defined $channel; + + foreach my $shit_channel (keys %{ $self->{blacklist} }) { + foreach my $shit_hostmask (keys %{ $self->{blacklist}->{$shit_channel} }) { + my $shit_channel_escaped = quotemeta $shit_channel; + my $shit_hostmask_escaped = quotemeta $shit_hostmask; + + $shit_channel_escaped =~ s/\\(\.|\*)/$1/g; + $shit_hostmask_escaped =~ s/\\(\.|\*)/$1/g; + + if(($channel =~ /$shit_channel_escaped/i) && ($hostmask =~ /$shit_hostmask_escaped/i)) { + $self->{pbot}->{logger}->log("$hostmask blacklisted in channel $channel (matches [$shit_hostmask] host and [$shit_channel] channel)\n"); + return 1; + } + } + } + return 0; +} + +sub blacklist { + my ($self, $from, $nick, $user, $host, $arguments) = @_; + $arguments = lc $arguments; + + my ($command, $args) = split / /, $arguments, 2; + + return "Usage: blacklist , where commands are: list/show, add, remove" if not defined $command; + + given($command) { + when($_ eq "list" or $_ eq "show") { + my $text = "Blacklist:\n"; + my $entries = 0; + foreach my $channel (keys %{ $self->{blacklist} }) { + $text .= " $channel:\n"; + foreach my $mask (keys %{ $self->{blacklist}->{$channel} }) { + $text .= " $mask,\n"; + $entries++; + } + } + $text .= "none" if $entries == 0; + return $text; + } + when("add") { + my ($mask, $channel) = split / /, $args, 2; + return "Usage: blacklist add [channel]" if not defined $mask; + + $channel = '.*' if not defined $channel; + + $self->{pbot}->{logger}->log("$nick!$user\@$host added [$mask] to blacklist for channel [$channel]\n"); + $self->add($channel, $mask); + return "$mask blacklisted in channel $channel"; + } + when("remove") { + my ($mask, $channel) = split / /, $args, 2; + return "Usage: blacklist remove [channel]" if not defined $mask; + + $channel = '.*' if not defined $channel; + + if(not exists $self->{blacklist}->{$channel} and not exists $self->{blacklist}->{$channel}->{$mask}) { + $self->{pbot}->{logger}->log("$nick attempt to remove nonexistent [$mask][$channel] from blacklist\n"); + return "$mask not found in blacklist for channel $channel (use `blacklist list` to display blacklist)"; + } + + $self->remove($channel, $mask); + $self->{pbot}->{logger}->log("$nick!$user\@$host removed [$mask] from blacklist for channel [$channel]\n"); + return "$mask removed from blacklist for channel $channel"; + } + default { + return "Unknown command '$command'; commands are: list/show, add, remove"; + } + } +} + +1; diff --git a/PBot/IgnoreList.pm b/PBot/IgnoreList.pm index 414bb4f4..3b169a53 100644 --- a/PBot/IgnoreList.pm +++ b/PBot/IgnoreList.pm @@ -13,7 +13,7 @@ use Time::HiRes qw(gettimeofday); sub new { if(ref($_[1]) eq 'HASH') { - Carp::croak("Options to Commands should be key/value pairs, not hash reference"); + Carp::croak("Options to " . __FILE__ . " should be key/value pairs, not hash reference"); } my ($class, %conf) = @_; @@ -26,7 +26,7 @@ sub new { sub initialize { my ($self, %conf) = @_; - $self->{pbot} = delete $conf{pbot} // Carp::croak("Missing pbot reference to Channels"); + $self->{pbot} = delete $conf{pbot} // Carp::croak("Missing pbot reference to " . __FILE__); $self->{filename} = delete $conf{filename}; $self->{ignore_list} = {}; @@ -45,9 +45,9 @@ sub add { my ($hostmask, $channel, $length) = @_; if($length == -1) { - ${ $self->{ignore_list} }{$hostmask}{$channel} = -1; + $self->{ignore_list}->{$hostmask}->{$channel} = -1; } else { - ${ $self->{ignore_list} }{$hostmask}{$channel} = gettimeofday + $length; + $self->{ignore_list}->{$hostmask}->{$channel} = gettimeofday + $length; } $self->save_ignores(); @@ -57,7 +57,12 @@ sub remove { my $self = shift; my ($hostmask, $channel) = @_; - delete ${ $self->{ignore_list} }{$hostmask}{$channel}; + delete $self->{ignore_list}->{$hostmask}->{$channel}; + + if (not keys %{ $self->{ignore_list}->{$hostmask} }) { + delete $self->{ignore_list}->{$hostmask}; + } + $self->save_ignores(); } @@ -94,7 +99,7 @@ sub load_ignores { Carp::croak "Duplicate ignore [$hostmask][$channel] found in $filename around line $i\n"; } - ${ $self->{ignore_list} }{$hostmask}{$channel} = $length; + $self->{ignore_list}->{$hostmask}->{$channel} = $length; } $self->{pbot}->{logger}->log(" $i entries in ignorelist\n"); @@ -114,10 +119,10 @@ sub save_ignores { open(FILE, "> $filename") or die "Couldn't open $filename: $!\n"; - foreach my $ignored (keys %{ $self->{ignore_list} }) { - foreach my $ignored_channel (keys %{ ${ $self->{ignore_list} }{$ignored} }) { - my $length = $self->{ignore_list}->{$ignored}{$ignored_channel}; - print FILE "$ignored $ignored_channel $length\n"; + foreach my $hostmask (keys %{ $self->{ignore_list} }) { + foreach my $channel (keys %{ $self->{ignore_list}->{$hostmask} }) { + my $length = $self->{ignore_list}->{$hostmask}->{$channel}; + print FILE "$hostmask $channel $length\n"; } } @@ -162,8 +167,7 @@ sub check_ignore { } foreach my $ignored (keys %{ $self->{ignore_list} }) { - foreach my $ignored_channel (keys %{ ${ $self->{ignore_list} }{$ignored} }) { - #$self->{pbot}->{logger}->log("check_ignore: comparing '$hostmask' against '$ignored' for channel '$channel'\n"); + foreach my $ignored_channel (keys %{ $self->{ignore_list}->{$ignored} }) { my $ignored_channel_escaped = quotemeta $ignored_channel; my $ignored_escaped = quotemeta $ignored; @@ -185,16 +189,14 @@ sub check_ignore_timeouts { foreach my $hostmask (keys %{ $self->{ignore_list} }) { foreach my $channel (keys %{ $self->{ignore_list}->{$hostmask} }) { - next if($self->{ignore_list}->{$hostmask}{$channel} == -1); #permanent ignore + next if($self->{ignore_list}->{$hostmask}->{$channel} == -1); #permanent ignore - if($self->{ignore_list}->{$hostmask}{$channel} < $now) { - $self->{commands}->unignore_user("", "floodcontrol", "", "", "$hostmask $channel"); + if($self->{ignore_list}->{$hostmask}->{$channel} < $now) { + $self->{pbot}->{logger}->log("Unignoring $hostmask in channel $channel.\n"); + $self->remove($hostmask, $channel); if($hostmask eq ".*") { $self->{pbot}->{conn}->me($channel, "awakens."); } - } else { - #my $timediff = $ignore_list{$host}{$channel} - $now; - #${logger}->log "ignore: $host has $timediff seconds remaining\n" } } } diff --git a/PBot/IgnoreListCommands.pm b/PBot/IgnoreListCommands.pm index 2af3f7ac..03aedfcd 100644 --- a/PBot/IgnoreListCommands.pm +++ b/PBot/IgnoreListCommands.pm @@ -90,7 +90,7 @@ sub unignore_user { if(not exists ${ $self->{pbot}->{ignorelist}->{ignore_list} }{$target}{$channel}) { $self->{pbot}->{logger}->log("$nick attempt to remove nonexistent [$target][$channel] from ignore list\n"); - return "/msg $nick [$target][$channel] not found in ignore list (use '!ignore list' to list ignores"; + return "/msg $nick [$target][$channel] not found in ignore list (use `ignore list` to list ignores)"; } $self->{pbot}->{ignorelist}->remove($target, $channel); diff --git a/PBot/Interpreter.pm b/PBot/Interpreter.pm index 2a085949..67188b36 100644 --- a/PBot/Interpreter.pm +++ b/PBot/Interpreter.pm @@ -92,7 +92,7 @@ sub process_line { if((defined $command && $command !~ /^login/i) || defined $has_url || defined $has_code) { if(defined $from && $pbot->{ignorelist}->check_ignore($nick, $user, $host, $from) && not $pbot->{admins}->loggedin($from, "$nick!$user\@$host")) { # ignored hostmask - $pbot->{logger}->log("ignored text: [$from][$nick!$user\@$host\[$text\]\n"); + $pbot->{logger}->log("ignored message: $from <$nick!$user\@$host> $text\n"); return; } } diff --git a/PBot/PBot.pm b/PBot/PBot.pm index 6ced930f..7a6ee889 100644 --- a/PBot/PBot.pm +++ b/PBot/PBot.pm @@ -44,7 +44,7 @@ use PBot::ChanOps; use PBot::Factoids; use PBot::BotAdmins; use PBot::IgnoreList; -use PBot::ShitList; +use PBot::BlackList; use PBot::Quotegrabs; use PBot::Timer; use PBot::AntiAway; @@ -113,7 +113,7 @@ sub initialize { $self->{messagehistory} = PBot::MessageHistory->new(pbot => $self, filename => delete $conf{messagehistory_file}, %conf); $self->{antiflood} = PBot::AntiFlood->new(pbot => $self, %conf); $self->{ignorelist} = PBot::IgnoreList->new(pbot => $self, filename => delete $conf{ignorelist_file}, %conf); - $self->{shitlist} = PBot::ShitList->new(pbot => $self, filename => delete $conf{shitlist_file}, %conf); + $self->{blacklist} = PBot::BlackList->new(pbot => $self, filename => delete $conf{blacklist_file}, %conf); $self->{irc} = PBot::IRC->new(); $self->{irchandlers} = PBot::IRCHandlers->new(pbot => $self, %conf); $self->{channels} = PBot::Channels->new(pbot => $self, filename => delete $conf{channels_file}, %conf); diff --git a/PBot/ShitList.pm b/PBot/ShitList.pm deleted file mode 100644 index d1d4d158..00000000 --- a/PBot/ShitList.pm +++ /dev/null @@ -1,204 +0,0 @@ -# File: ShitList.pm -# Author: pragma_ -# -# Purpose: Manages list of hostmasks that are not allowed to join a channel. - -package PBot::ShitList; - -use warnings; -use strict; - -use Carp (); -use Time::HiRes qw(gettimeofday); - -sub new { - if(ref($_[1]) eq 'HASH') { - Carp::croak("Options to " . __FILE__ . " 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 " . __FILE__); - $self->{filename} = delete $conf{filename}; - - $self->{shitlist} = {}; - - $self->{pbot}->{commands}->register(sub { return $self->shitlist_user(@_) }, "shitlist", 10); - $self->{pbot}->{commands}->register(sub { return $self->unshitlist_user(@_) }, "unshitlist", 10); - - $self->load_shitlist; -} - -sub add { - my ($self, $channel, $hostmask) = @_; - - $self->{shitlist}->{lc $channel}->{lc $hostmask} = 1; - $self->save_shitlist(); -} - -sub remove { - my $self = shift; - my ($channel, $hostmask) = @_; - - $channel = lc $channel; - $hostmask = lc $hostmask; - - if (exists $self->{shitlist}->{$channel}) { - delete $self->{shitlist}->{$channel}->{$hostmask}; - } - - if (keys $self->{shitlist}->{$channel} == 0) { - delete $self->{shitlist}->{$channel}; - } - - $self->save_shitlist(); -} - -sub load_shitlist { - my $self = shift; - my $filename; - - if(@_) { $filename = shift; } else { $filename = $self->{filename}; } - - if(not defined $filename) { - Carp::carp "No shitlist path specified -- skipping loading of shitlist"; - return; - } - - $self->{pbot}->{logger}->log("Loading shitlist from $filename ...\n"); - - open(FILE, "< $filename") or Carp::croak "Couldn't open $filename: $!\n"; - my @contents = ; - close(FILE); - - my $i = 0; - - foreach my $line (@contents) { - chomp $line; - $i++; - - my ($channel, $hostmask) = split(/\s+/, $line); - - if(not defined $hostmask || not defined $channel) { - Carp::croak "Syntax error around line $i of $filename\n"; - } - - if(exists $self->{shitlist}->{$channel}->{$hostmask}) { - Carp::croak "Duplicate shitlist entry [$hostmask][$channel] found in $filename around line $i\n"; - } - - $self->{shitlist}->{$channel}->{$hostmask} = 1; - } - - $self->{pbot}->{logger}->log(" $i entries in shitlist\n"); - $self->{pbot}->{logger}->log("Done.\n"); -} - -sub save_shitlist { - my $self = shift; - my $filename; - - if(@_) { $filename = shift; } else { $filename = $self->{filename}; } - - if(not defined $filename) { - Carp::carp "No shitlist path specified -- skipping saving of shitlist\n"; - return; - } - - open(FILE, "> $filename") or die "Couldn't open $filename: $!\n"; - - foreach my $channel (keys %{ $self->{shitlist} }) { - foreach my $hostmask (keys %{ $self->{shitlist}->{$channel} }) { - print FILE "$channel $hostmask\n"; - } - } - - close(FILE); -} - -sub check_shitlist { - my $self = shift; - my ($hostmask, $channel) = @_; - - return 0 if not defined $channel; - - foreach my $shit_channel (keys %{ $self->{shitlist} }) { - foreach my $shit_hostmask (keys %{ $self->{shitlist}->{$shit_channel} }) { - my $shit_channel_escaped = quotemeta $shit_channel; - my $shit_hostmask_escaped = quotemeta $shit_hostmask; - - $shit_channel_escaped =~ s/\\(\.|\*)/$1/g; - $shit_hostmask_escaped =~ s/\\(\.|\*)/$1/g; - - if(($channel =~ /$shit_channel_escaped/i) && ($hostmask =~ /$shit_hostmask_escaped/i)) { - $self->{pbot}->{logger}->log("$hostmask shitlisted in channel $channel (matches [$shit_hostmask] host and [$shit_channel] channel)\n"); - return 1; - } - } - } - return 0; -} - -sub shitlist_user { - my $self = shift; - my ($from, $nick, $user, $host, $arguments) = @_; - - return "Usage: shitlist [channel]" if not $arguments; - - my ($target, $channel) = split /\s+/, $arguments; - - if($target =~ /^list$/i) { - my $text = "Shitlisted: "; - - my $sep = ""; - foreach my $channel (keys %{ $self->{shitlist} }) { - $text .= "$channel: "; - foreach my $hostmask (keys %{ $self->{shitlist}->{$channel} }) { - $text .= $sep . $hostmask; - $sep = ";\n"; - } - } - return $text; - } - - if(not defined $channel) { - $channel = ".*"; # all channels - } - - $self->{pbot}->{logger}->log("$nick!$user\@$host added [$target] to shitlist for channel [$channel]\n"); - $self->add($channel, $target); - return "$target added to shitlist for channel $channel"; -} - -sub unshitlist_user { - my $self = shift; - my ($from, $nick, $user, $host, $arguments) = @_; - my ($target, $channel) = split /\s+/, $arguments if $arguments; - - if(not defined $target) { - return "Usage: unshitlist [channel]"; - } - - if(not defined $channel) { - $channel = ".*"; - } - - if(not exists $self->{shitlist}->{$channel} and not exists $self->{shitlist}->{$channel}->{$target}) { - $self->{pbot}->{logger}->log("$nick attempt to remove nonexistent [$target][$channel] from shitlist\n"); - return "$target not found in shitlist for channel $channel (use `shitlist list` to display shitlist)"; - } - - $self->remove($channel, $target); - $self->{pbot}->{logger}->log("$nick!$user\@$host removed [$target] from shitlist for channel [$channel]\n"); - return "$target removed from shitlist for channel $channel"; -} - -1; diff --git a/config/shitlist b/config/blacklist similarity index 100% rename from config/shitlist rename to config/blacklist diff --git a/pbot.pl b/pbot.pl index e09d0ace..14a527e9 100755 --- a/pbot.pl +++ b/pbot.pl @@ -103,8 +103,8 @@ $config{channels_file} = "$config{config_dir}/channels"; # Location of file containing ignorelist entries $config{ignorelist_file} = "$config{config_dir}/ignorelist"; -# Location of file containing shitlist entries -$config{shitlist_file} = "$config{config_dir}/shitlist"; +# Location of file containing blacklist entries +$config{blacklist_file} = "$config{config_dir}/blacklist"; # Location of file containing factoids and modules $config{factoids_file} = "$config{data_dir}/factoids";