#!/usr/bin/perl -w # # recentdepart.pl # # irssi script # # This script, when loaded into irssi, will filter quit and parted messages # for channels listed in recdep_channels for any nick whos last message was # more then a specified time ago. # # It also filters join messages showing only join messages for nicks who recently # parted. # # [settings] # recdep_channels # - Should contain a list of chatnet and channel names that recentdepart # should monitor. Its format is a spcae delimited list of chatnet/#channel # pairs. Either chatnet or channel are optional but adding a / makes it # explicitly recognized as a chatnet or a channel name. A special case is just a # "*" which turns it on globally. # # "#irrsi #perl" - enables filtering on the #irssi and #perl # channels on all chatnets. # # "freenode IRCNet/#irssi" - enables filtering for all channels on frenode # and only the #irssi channel on IRCNet. # # "freenode/" - force "freenode" to be interpreted as the chatnet # name by adding a / to the end. # # "/freenode" - forces "freenode" to be interpreted as the channel # by prefixing it with the / delimiter. # # "*" - globally enables filtering. # # recdep_period # - specifies the window of time, after a nick last spoke, for which quit/part # notices will be let through the filter. # # recdep_rejoin # - specifies a time period durring which a join notice for someone rejoining will # be shown. Join messages are filtered if the nicks part/quit message was filtered # or if the nick is gone longer then the rejoin period. # Set to 0 to turn off filtering of join notices. # # recdep_nickperiod # - specifies a window of time like recdep_period that is used to filter nick change # notices. Set to 0 to turn off filtering of nick changes. # # recdep_use_hideshow # - whether to use hideshow script instead of ignoring # use strict; use warnings; use Irssi; use Irssi::Irc; our $VERSION = "0.7"; our %IRSSI = ( authors => 'Matthew Sytsma', contact => 'spiderpigy@yahoo.com', name => 'Recently Departed', description => 'Filters quit/part/join/nick notices based on time since last message. (Similar to weechat\'s smartfilter).', license => 'GNU GPLv2 or later', url => '', ); # store a hash of configure selected servers/channels my %chanlist; # Track recent times by server/nick/channel # (it is more optimal to go nick/channel then channel/nick because some quit signals are by server not by channel. # We will only have to loop through open channels that a nick has spoken in which should be less then looping # through all the monitored channels looking for the nick. my %nickhash=(); # Track recent times for parted nicks by server/channel/nick my %joinwatch=(); my $use_hide; sub on_setup_changed { my %old_chanlist = %chanlist; %chanlist = (); my @pairs = split(/ /, Irssi::settings_get_str("recdep_channels")); $use_hide = Irssi::settings_get_bool("recdep_use_hideshow"); foreach (@pairs) { my ($net, $chan, $more) = split(/\//); if ($more) { /\/(.+)/; $chan = $1; } # Irssi::active_win()->print("Initial Net: $net Chan: $chan"); if (!$net) { $net = '*'; } if ($net =~ /^[#!@&]/ && !$chan) { $chan = $net; $net = "*"; } if (!$chan) { $chan = "*"; } $chanlist{$net}{$chan} = 1; } # empty the storage in case theres a channel or server we are no longer filtering %nickhash=(); %joinwatch=(); } sub check_channel { my ($server, $channel) = @_; # quits dont have a channel so we need to see if this server possibly contains this channel if (!$channel || $channel eq '*') { # see if any non chatnet channel listings are open on this server if (keys %{ $chanlist{'*'} }) { foreach my $chan (keys %{ $chanlist{'*'} }) { if ($chan eq '*' || $server->channel_find($chan)) { return 1; } } } # see if there were any channels listed for this chatnet if (keys %{ $chanlist{$server->{'chatnet'}} }) { return 1; } else { return 0; } } # check for global channel matches and pair matches return (($chanlist{'*'}{'*'}) || ($chanlist{'*'}{$channel}) || ($chanlist{$server->{'chatnet'}}{'*'}) || ($chanlist{$server->{'chatnet'}}{$channel})); } # Hook for quitting sub on_quit { my ($server, $nick, $address, $reason) = @_; if ($server->{'nick'} eq $nick) { return; } if (check_channel($server, '*')) { my $recent = 0; foreach my $chan (keys %{ $nickhash{$server->{'tag'}}{lc($nick)} }) { if (time() - $nickhash{$server->{'tag'}}{lc($nick)}{$chan} < Irssi::settings_get_int("recdep_period")) { $recent = 1; if (Irssi::settings_get_int("recdep_rejoin") > 0) { $joinwatch{$server->{'tag'}}{$chan}{lc($nick)} = time(); } } } delete $nickhash{$server->{'tag'}}{lc($nick)}; if (!$recent) { $use_hide ? $Irssi::scripts::hideshow::hide_next = 1 : Irssi::signal_stop(); } } } # Hook for parting sub on_part { my ($server, $channel, $nick, $address, $reason) = @_; # cleanup if its you who left a channel if ($server->{'nick'} eq $nick) { # slightly painfull cleanup but we shouldn't hit this as often foreach my $nickd (keys %{ $nickhash{$server->{'tag'}} }) { delete $nickhash{$server->{'tag'}}{$nickd}{$channel}; if (!keys(%{ $nickhash{$server->{'tag'}}{$nickd} })) { delete $nickhash{$server->{'tag'}}{$nickd}; } } delete $joinwatch{$server->{'tag'}}{$channel}; } elsif (check_channel($server, $channel)) { if (!defined $nickhash{$server->{'tag'}}{lc($nick)}{$channel} || time() - $nickhash{$server->{'tag'}}{lc($nick)}{$channel} > Irssi::settings_get_int("recdep_period")) { $use_hide ? $Irssi::scripts::hideshow::hide_next = 1 : Irssi::signal_stop(); } elsif (Irssi::settings_get_int("recdep_rejoin") > 0) { $joinwatch{$server->{'tag'}}{$channel}{lc($nick)} = time(); } delete $nickhash{$server->{'tag'}}{lc($nick)}{$channel}; if (!keys(%{ $nickhash{$server->{'tag'}}{lc($nick)} })) { delete $nickhash{$server->{'tag'}}{lc($nick)}; } } } # Hook for public messages. sub on_public { my ($server, $msg, $nick, $addr, $target) = @_; if (!$target) { return; } if ($nick =~ /^#/) { return; } if ($server->{'nick'} eq $nick) { return; } if (check_channel($server, $target)) { $nickhash{$server->{'tag'}}{lc($nick)}{$target} = time(); } } # Hook for people joining sub on_join { my ($server, $channel, $nick, $address) = @_; if ($server->{'nick'} eq $nick) { return; } if (Irssi::settings_get_int("recdep_rejoin") == 0) { return; } if (check_channel($server, $channel)) { if (!defined $joinwatch{$server->{'tag'}}{$channel}{lc($nick)} || time() - $joinwatch{$server->{'tag'}}{$channel}{lc($nick)} > Irssi::settings_get_int("recdep_rejoin")) { $use_hide ? $Irssi::scripts::hideshow::hide_next = 1 : Irssi::signal_stop(); } } # loop through and delete all old nicks from the rejoin hash # this should be a small loop because it will only inlude nicks who recently left channel and who # passed the part message filter foreach my $nickd (keys %{ $joinwatch{$server->{'tag'}}{$channel} }) { if (time() - $joinwatch{$server->{'tag'}}{$channel}{lc($nickd)} < Irssi::settings_get_int("recdep_rejoin")) { next; } delete $joinwatch{$server->{'tag'}}{$channel}{lc($nickd)}; } if (!keys(%{ $joinwatch{$server->{'tag'}}{$channel} })) { delete $joinwatch{$server->{'tag'}}{$channel}; } } # Hook for nick changes sub on_nick { my ($server, $new, $old, $address) = @_; if ($server->{'nick'} eq $old || $server->{'nick'} eq $new) { return; } if (check_channel($server, '*')) { my $recent = 0; foreach my $chan (keys %{ $nickhash{$server->{'tag'}}{lc($old)} }) { if (time() - $nickhash{$server->{'tag'}}{lc($old)}{$chan} < Irssi::settings_get_int("recdep_nickperiod")) { $recent = 1; } } if (!$recent && Irssi::settings_get_int("recdep_nickperiod") > 0) { $use_hide ? $Irssi::scripts::hideshow::hide_next = 1 : Irssi::signal_stop(); } delete $nickhash{$server->{'tag'}}{lc($old)}; } } # Hook for cleanup on server quit sub on_serverquit { my ($server, $msg) = @_; delete $nickhash{$server->{'tag'}}; delete $joinwatch{$server->{'tag'}}; } # Setup hooks on events Irssi::signal_add_last("message public", "on_public"); Irssi::signal_add_last("message part", "on_part"); Irssi::signal_add_last("message quit", "on_quit"); Irssi::signal_add_last("message nick", "on_nick"); Irssi::signal_add_last("message join", "on_join"); Irssi::signal_add_last("server disconnected", "on_serverquit"); Irssi::signal_add_last("server quit", "on_serverquit"); Irssi::signal_add('setup changed', "on_setup_changed"); # Add settings Irssi::settings_add_str("recdentpepart", "recdep_channels", '*'); Irssi::settings_add_int("recdentpepart", "recdep_period", 600); Irssi::settings_add_int("recdentpepart", "recdep_rejoin", 120); Irssi::settings_add_int("recdentpepart", "recdep_nickperiod", 600); Irssi::settings_add_bool("recdentpepart", "recdep_use_hideshow", 0); on_setup_changed();