# bleh stuff use Irssi; use Irssi::Irc; use strict; use vars qw($VERSION %IRSSI $DEBUG); # 0.05 -- Add IPv6 support $VERSION = q$Revision$; %IRSSI = (authors => 'Don Armstrong', name => 'auto_bleh', description => 'Provides /ak /aq /ab /abr /abrn /arn /amb /amr /at', license => 'GPL', changed => q$Id$, ); $DEBUG = 1 unless defined $DEBUG; my ($actions, %defaults); %defaults = (GET_OP => 1, # Should we try to get opped when we auto_bleh? USE_CHANSERV => 1, # Should we use chanserv to get opped? EXPIRE => 6000, # Do not try to do anything if the action is more than 6000 seconds old. DEOP => 1, # Automatically deop after we've done whatever we were supposed to do. TIMEOUT => 10, # Timeout /at bans after 10 minutes SMELLSLIKEFN => qr/freenode.net/, ); my %command_bindings = (ak => 'cmd_ak', ab => 'cmd_ab', aq => 'cmd_aq', ar => 'cmd_ar', abr => 'cmd_abr', abk => 'cmd_abk', abrn => 'cmd_abrn', abk => 'cmd_abkn', arn => 'cmd_arn', amb => 'cmd_amb', amr => 'cmd_amr', at => 'cmd_at', ); my %bans_to_remove; sub cmd_at { my ($data, $server, $witem) = @_; return do_auto_bleh([qw(timeout)],$data,$server,$witem); } sub cmd_ak { my ($data, $server, $witem) = @_; return do_auto_bleh([qw(kick)],$data,$server,$witem); } sub cmd_abk { my ($data, $server, $witem) = @_; return do_auto_bleh([qw(kick ban)],$data,$server,$witem); } sub cmd_abkn { my ($data, $server, $witem) = @_; return do_auto_bleh([qw(kick ban notice)],$data,$server,$witem); } sub cmd_amb{ my ($data, $server, $witem) = @_; my @nicks = split /\s+/, $data; for (@nicks) { next unless /\w/; do_auto_bleh([qw(ban)],$_,$server,$witem); } } sub cmd_ab { my ($data, $server, $witem) = @_; return do_auto_bleh([qw(ban)],$data,$server,$witem); } sub cmd_aq { my ($data, $server, $witem) = @_; return do_auto_bleh([qw(quiet)],$data,$server,$witem); } sub cmd_amr{ my ($data, $server, $witem) = @_; my @nicks = split /\s+/, $data; for (@nicks) { next unless /\w/; do_auto_bleh([qw(remove)],$_,$server,$witem); } } sub cmd_ar { my ($data, $server, $witem) = @_; return do_auto_bleh([qw(remove)],$data,$server,$witem); } sub cmd_abr{ my ($data, $server, $witem) =@_; return do_auto_bleh([qw(remove ban)],$data,$server,$witem); } sub cmd_abrn{ my ($data, $server, $witem) =@_; return do_auto_bleh([qw(remove ban notice)],$data,$server,$witem); } sub cmd_arn{ my ($data, $server, $witem) =@_; return do_auto_bleh([qw(remove,notice)],$data,$server,$witem); } sub do_auto_bleh { my ($cmd, $data, $server, $witem, $duration) = @_; if (!$server || !$server->{connected}) { Irssi::print("Not connected to server"); return; } if ($witem->{type} ne 'CHANNEL') { Irssi::print("Can't autokick on a non-channel. [$witem->{type}]"); return; } if (ref($cmd) eq 'HASH') { # do nothing } elsif (ref($cmd) eq 'ARRAY') { $cmd = {map {$_,1} @$cmd}; } elsif (not ref($cmd)) { $cmd = {map {$_,1} split /\s*,\s*/, $cmd}; } else { die "Cmd: $cmd option to do_auto_bleh is not a supported type"; } # Fix up options for opn which doesn't do quiet or remove; # turn them into ban and kick, respectively. if ($server->{address} !~ $defaults{SMELLSLIKEFN}) { my %fn_mapping = (remove => 'kick', # OFTC now supports +q/-q. #quiet => 'ban', #unquiet => 'unban', #timeout => 'btimeout', ); for my $key (keys %fn_mapping) { if ($$cmd{$key}) { delete $$cmd{$key}; $$cmd{$fn_mapping{$key}} = 1; } } } # use Data::Dumper; # Irssi::print(Dumper($data,$server,$witem)); # set the network that we're on, the channel and the nick to kick # once we've been opped # Heh. Looks like $data needs to be sanitized a bit. $data =~ /^\s*([^\s]+)\s*(\d+)?\s*(.+?|)\s*$/; my $nick = $1; my $timeout = $2; my $reason = $3; $timeout = $defaults{TIMEOUT} if not defined $timeout or $timeout eq ''; $reason = 'you should know better' if not defined $reason or $reason eq ''; my $nick_rec = $witem->nick_find($nick); if (not defined $nick_rec) { Irssi::print("Unable to find nick: $nick"); } my $hostname = $nick_rec->{host} if defined $nick_rec; Irssi::print("Unable to find hostname for $nick") if not defined $hostname or $hostname eq ''; $hostname =~ s/.+\@//; Irssi::print("Nick set to '$nick' from '$data', reason set to '$reason'.") if $DEBUG; my $action = {type => $cmd, nick => $nick, nick_rec => $nick_rec, network => $witem->{server}->{chatnet}, server => $witem->{server}, completed => 0, inserted => time, channel => $witem->{name}, reason => $reason, hostname => $hostname, timeout => $timeout, }; Irssi::print(i_want($action)) if $DEBUG; if ($witem->{chanop}) { # we seem to be opped. take_action($action,$server,$witem); } else { $actions->{$data.$action->{inserted}}=$action; get_op($server, $action->{channel}) if $defaults{GET_OP}; } } sub get_op { my ($server,$channel) = @_; Irssi::print("MSG chanserv op $channel"); $server->command("MSG chanserv op $channel") if $defaults{USE_CHANSERV}; } sub i_want { my $action = shift; return "I've wanted to ".join(',', keys %{$action->{type}||{'do something to'=>undef}}}). " $action->{nick} off $action->{channel} on $action->{network} since $action->{inserted}"; } sub take_action { my ($action,$server,$channel) = @_; my $type = $action->{type}; # Now support multiple actions against a single nick (to FE, kick # ban, or remove ban). See /abr foo if ($type->{timeout}) { Irssi::print("Quieting $action->{nick} on $action->{channel} with hostname $action->{hostname} for $action->{timeout} minutes"); $server->send_raw("MODE $action->{channel} +q *!*@".$action->{hostname}) if $action->{hostname} ne ''; #quiet hostname $bans_to_remove{"$action->{nick}$action->{inserted}"} = $action; } if ($type->{btimeout}) { Irssi::print("Quieting $action->{nick} on $action->{channel} with hostname $action->{hostname} for $action->{timeout} minutes"); $server->send_raw("MODE $action->{channel} +b *!*@".$action->{hostname}) if $action->{hostname} ne ''; #quiet hostname $bans_to_remove{"$action->{nick}$action->{inserted}"} = $action; } if ($type->{quiet}) { Irssi::print("Quieting $action->{nick} on $action->{channel} with hostname $action->{hostname}") if $DEBUG; # Find hostname if ($action->{hostname}) { $server->send_raw("MODE $action->{channel} +q *!*@".$action->{hostname}) if $action->{hostname} ne ''; #quiet hostname } } if ($type->{ban}) { Irssi::print("Banning $action->{nick} from $action->{channel} with hostname $action->{hostname}") if $DEBUG; $server->send_raw("MODE $action->{channel} +b *!*@".$action->{hostname}) if $action->{hostname} ne ''; # ban hostname } if ($type->{kick}) { Irssi::print("Kicking $action->{nick} from $action->{channel}") if $DEBUG; if ($action->{reason} =~ /\s/) { $server->send_raw("KICK $action->{channel} $action->{nick} :$action->{reason}"); } else { $server->send_raw("KICK $action->{channel} $action->{nick} $action->{reason}"); } } if ($type->{remove}) { Irssi::print("Removing $action->{nick} from $action->{channel}") if $DEBUG; if ($action->{reason} =~ /\s/) { $server->send_raw("REMOVE $action->{channel} $action->{nick} :$action->{reason}"); } else { $server->send_raw("REMOVE $action->{channel} $action->{nick} $action->{reason}"); } } if ($type->{notice}) { Irssi::print("Noticing $action->{nick} with $action->{reason}") if $DEBUG; $server->command("NOTICE $action->{nick} $action->{reason}"); } # unquiet if ($type->{unquiet}) { Irssi::print("Unquieting $action->{nick} on $action->{channel} with hostname $action->{hostname}") if $DEBUG; $server->command("MODE $action->{channel} -q *!*@".$action->{hostname}); } # unban if ($type->{unban}) { Irssi::print("Unbanning $action->{nick} on $action->{channel} with hostname $action->{hostname}") if $DEBUG; $server->command("MODE $action->{channel} -b *!*@".$action->{hostname}); } return; #for now. } sub deop_us { my ($rec) = @_; my $channel = $rec->{channel}; my $server = $rec->{server}; #$server->command("MODE $channel->{name} -o $channel->{ownick}->{nick}"); if ($channel->{chanop}) { Irssi::print("MODE $channel->{name} -o $channel->{ownnick}->{nick}"); $channel->command("/deop $channel->{ownnick}->{nick}"); } } sub sig_mode_change { my ($channel,$nick) = @_; #Irssi::print(Dumper($nick)); #Irssi::print(Dumper($channel)); # Are there any actions to process? # See if we got opped. return if scalar(keys %$actions) eq 0; if ($channel->{server}->{nick} eq $nick->{nick} and $nick->{op}) { # Ok, we've been opped, or we are opped now, so do whatever we're supposed to do. Irssi::print("We've been opped") if $DEBUG; # We seem to need some sort of delay here for the chanop stuff to catch up Irssi::timeout_add_once(400,'attempt_actions',undef); } else { Irssi::print("Fooey. Not opped.") if $DEBUG; } } sub attempt_actions { my @deop_array; foreach (keys %$actions) { #Irssi::print(Dumper($actions->{$_})); # See if this action is too old if (time - $actions->{$_}->{inserted} > $defaults{EXPIRE}) { Irssi::print("Expiring action: \"".i_want($actions->{$_})."\" because of time"); delete $actions->{$_}; next; } Irssi::print(i_want($actions->{$_})) if $DEBUG; # Find the server to take action on my $server = Irssi::server_find_chatnet($actions->{$_}->{network}); if (not defined $server) { Irssi::print("Unable to find server for chatnet: $actions->{$_}->{network}"); return; } Irssi::print("Found server for chatnet: $actions->{$_}->{network}") if $DEBUG; # Find the channel to take action on my $s_channel = $server->channel_find($actions->{$_}->{channel}); if (not defined $s_channel){ Irssi::print("Unable to find channel for channel: $actions->{$_}->{channel}"); return; } Irssi::print("Found channel for channel: $actions->{$_}->{channel}") if $DEBUG; # Are we opped on that channel? if ($s_channel->{chanop}) { # Yes? Take the action against the user. Irssi::print("We are opped on the channel!") if $DEBUG; take_action($actions->{$_},$server,$s_channel); push @deop_array,{server=>$server,channel=>$s_channel} if $defaults{DEOP}; delete $actions->{$_}; # Do not repeat this action. } else { Irssi::print("We are not opped on the channel.") if $DEBUG; } } foreach (@deop_array) { deop_us($_); } } sub try_to_remove_bans { return unless keys %bans_to_remove; for my $key (keys %bans_to_remove) { if (($bans_to_remove{$key}{inserted} + $bans_to_remove{$key}{timeout}*60) < time) { $bans_to_remove{$key}{type} = {unquiet => 1}; #unquiet $actions->{$key} = $bans_to_remove{$key}; delete $bans_to_remove{$key}; get_op($actions->{$key}{server}, $actions->{$key}{channel}) if $defaults{GET_OP}; } } } # call the try to remove bans function every minute Irssi::timeout_add(1000*60,'try_to_remove_bans',undef); Irssi::signal_add_last('nick mode changed','sig_mode_change'); my ($command,$function); while (($command,$function) = each %command_bindings) { Irssi::command_bind($command,$function); } 1;