diff options
Diffstat (limited to 'scripts/dejunk.pl')
| -rw-r--r-- | scripts/dejunk.pl | 418 |
1 files changed, 418 insertions, 0 deletions
diff --git a/scripts/dejunk.pl b/scripts/dejunk.pl new file mode 100644 index 0000000..5c7c01a --- /dev/null +++ b/scripts/dejunk.pl @@ -0,0 +1,418 @@ +use strict; +use warnings; + +use Irssi; +use Irssi::Irc; + +use Data::Dumper; + +our $VERSION = '1.0'; +our %IRSSI = ( + authors => 'Joost Vunderink (Garion)', + contact => 'joost@vunderink.net', + name => 'Dejunk', + description => 'Prevents all kinds of junk from showing up', + license => 'Public Domain', + url => 'http://www.garion.org/irssi/', + changed => '29 September 2012 10:15:10', +); + +my ($STATUS_ACTIVE, $STATUS_INACTIVE, $STATUS_UNKNOWN) = (1, 2, 3); +my $activity_filename = 'dejunk.activity.data'; + +# $activity{$tag}{$nickuserhost_mask} = { +# last_msg => time(), +# } +my %activity; + +sub cmd_dejunk { + my ($args, $server, $item) = @_; + if ($args =~ m/^(help)|(status)|(save)/i ) { + Irssi::command_runsub ('dejunk', $args, $server, $item); + return; + } + message("Use /dejunk help for help."); +} + +sub cmd_dejunk_help { + message("Dejunk is a script that prevents some clutter from showing up."); + message("In large and/or busy channels, joins, parts, nickchanges and quits can take up a large part of the activity. ". + "Using dejunk, all these events on large channels are hidden if the user doing ". + "them has not said anything for a while."); + message("This way, you only see such activity when it matters."); + message(""); + message("Commands:"); + message("/dejunk save - Force saving of data immediately."); + message(""); + message("Settings:"); + message("dejunk_joinpart_enabled - Hide all non-relevant joins, parts, quits and nickchanges."); + message("dejunk_joinpart_idle_time - The amount of minutes of inactivity after which a user will be hidden."); + message("dejunk_joinpart_min_size - Activity on channels with fewer users than this is not hidden."); + message("dejunk_joinpart_show_unknown - If it's unknown whether the user has been active recently, ". + "show them if this setting is true. ". + "This is only relevant if the script has just been loaded for the first time."); + message("dejunk_debug - set to ON to see debug messages."); +} + +sub cmd_dejunk_status { + report_status(); +} + +sub cmd_dejunk_save { + message("Saving dejunk data."); + save_data(); +} + +sub event_join { + my ($server, $channel, $nick, $host) = @_; + + # Don't handle my own JOINs. + return if ($nick eq $server->{nick}); + + return if channel_size_below_joinpart_minimum($server, $channel); + + my $status = get_client_status($server->{tag}, $host, $nick); + my $show_unknown = Irssi::settings_get_bool('dejunk_joinpart_show_unknown'); + + debug(sprintf("JOIN: channel=$channel nick=$nick host=$host tag=%s", $server->{tag})); + + if ($status == $STATUS_ACTIVE) { + debug("Showing: active client"); + return; + } + elsif ($status == $STATUS_UNKNOWN && $show_unknown) { + debug("Showing: unknown client and dejunk_joinpart_show_unknown is true"); + return; + } + + debug("Hiding: Is idle client."); + Irssi::signal_stop(); +} + +sub event_part { + my ($server, $channel, $nick, $host) = @_; + + # Don't handle my own JOINs. + return if ($nick eq $server->{nick}); + + return if channel_size_below_joinpart_minimum($server, $channel); + + my $status = get_client_status($server->{tag}, $host, $nick); + my $show_unknown = Irssi::settings_get_bool('dejunk_joinpart_show_unknown'); + + debug("PART: nick=$nick host=$host channel=$channel tag=%s", $server->{tag}); + + if ($status == $STATUS_ACTIVE) { + debug("Showing: active client"); + return; + } + elsif ($status == $STATUS_UNKNOWN && $show_unknown) { + debug("Showing: unknown client and dejunk_joinpart_show_unknown is true"); + return; + } + + debug("Hiding: Is idle client."); + Irssi::signal_stop(); +} + +sub event_quit { + my ($server, $nick, $host) = @_; + + # Don't handle my own QUITs. + return if ($nick eq $server->{nick}); + + my $channel = get_smallest_channel($server, $nick); + if (!$channel) { + warning("QUIT: Could not get smallest channel for nick '%s' on network '%s'!", + $nick, $server->{tag}); + return; + } + return if channel_size_below_joinpart_minimum($server, $channel); + + debug("QUIT: nick=$nick host=$host tag=%s", $server->{tag}); + + my $status = get_client_status($server->{tag}, $host, $nick); + my $show_unknown = Irssi::settings_get_bool('dejunk_joinpart_show_unknown'); + + if ($status == $STATUS_ACTIVE) { + debug("Showing: active client"); + return; + } + elsif ($status == $STATUS_UNKNOWN && $show_unknown) { + debug("Showing: unknown client and dejunk_joinpart_show_unknown is true"); + return; + } + + debug("Hiding: Is idle client."); + Irssi::signal_stop(); +} + +sub event_nick { + my ($server, $newnick, $oldnick, $hostmask) = @_; + debug("NICK: old=$oldnick new=$newnick host=$hostmask tag=%s", $server->{tag}); + + my $nuh_mask = "$oldnick!$hostmask"; + if (exists $activity{$server->{tag}}{$nuh_mask}) { + debug("Old client $oldnick was active; adding 'now' for $newnick"); + $activity{$server->{tag}}{$nuh_mask} = { + last_msg => time(), + } + } + + my $channel = get_smallest_channel($server, $newnick); + if (!$channel) { + warning("NICK: Could not get smallest channel for nick '%s' on network '%s'!", + $newnick, $server->{tag}); + return; + } + return if channel_size_below_joinpart_minimum($server, $channel); + + my $status = get_client_status($server->{tag}, $hostmask, $oldnick); + my $show_unknown = Irssi::settings_get_bool('dejunk_joinpart_show_unknown'); + + if ($status == $STATUS_ACTIVE) { + debug("Showing: active client"); + return; + } + elsif ($status == $STATUS_UNKNOWN && $show_unknown) { + debug("Showing: unknown client and dejunk_joinpart_show_unknown is true"); + return; + } + + debug("Hiding: Is idle client."); + Irssi::signal_stop(); +} + +sub event_public { + my ($server, $data, $nick, $hostmask, $channel) = @_; + + # Don't handle my own messages. + return if ($nick eq $server->{nick}); + + debug("MSG: nick=$nick hostmask=$hostmask tag=%s channel=$channel", $server->{tag}); + my $nuh_mask = "$nick!$hostmask"; + $activity{$server->{tag}}{$nuh_mask} = { + last_msg => time(), + } +} + +sub is_active_client { + my ($tag, $host, $nick) = @_; + + my $status = get_client_status($tag, $host, $nick); + + if ($status == $STATUS_ACTIVE) { + return 1; + } + + return; +} + +sub get_client_status { + my ($tag, $host, $nick) = @_; + + my $nuh_mask = "$nick!$host"; + if (exists $activity{$tag}{$nuh_mask}) { + my $d = $activity{$tag}{$nuh_mask}; + if (time() - $d->{last_msg} < 60 * Irssi::settings_get_int('dejunk_joinpart_idle_time')) { + return $STATUS_ACTIVE; + } + else { + return $STATUS_INACTIVE; + } + } + + return $STATUS_UNKNOWN; +} + +sub channel_size_below_joinpart_minimum { + my ($server, $channel) = @_; + my $chan_obj = $server->channel_find($channel); + if (!$chan_obj) { + warning("Minsize check: could not find channel '%s' on network '%s'!", + $channel, $server->{tag}); + return 1; + } + my @nicks = $chan_obj->nicks(); + if (scalar @nicks < Irssi::settings_get_int('dejunk_joinpart_min_size')) { + return 1; + } + return 0; +} + +sub get_smallest_channel { + my ($server, $nick) = @_; + + my $count = 999999999; + my $found_channel; + for my $channel ($server->channels()) { + if ($channel->nick_find($nick)) { + my @nicks = $channel->nicks(); + if (scalar @nicks < $count) { + $count = scalar @nicks; + $found_channel = $channel; + } + } + } + + return $found_channel->{name}; +} + +sub load_data { + load_activity_data(); +} + +sub load_activity_data { + my $fn = Irssi::get_irssi_dir() . '/' . $activity_filename; + + if (!-r $fn) { + return; + } + + open my $fh, '<', $fn; + if (!$fh) { + error("Could not read dejunk activity data from $fn: $!"); + return; + } + $/ = undef; + my $file_contents = <$fh>; + eval { + my $data = eval $file_contents; + %activity = %$data; + }; + if ($@) { + error("Error loading activity data from $fn: $@"); + } + else { + message("Activity data loaded from $activity_filename"); + } + close $fh; +} + +sub save_data { + save_activity_data(); +} + +sub save_activity_data { + clean_activity_data(); + my $fn = Irssi::get_irssi_dir() . '/' . $activity_filename; + open my $fh, '>', $fn; + if (!$fh) { + error("Could not write dejunk activity data to $fn: $!"); + return; + } + $Data::Dumper::Indent = 1; + $Data::Dumper::Purity = 1; + $Data::Dumper::Sortkeys = 1; + $Data::Dumper::Terse = 1; + print $fh Dumper \%activity; + close $fh; +} + +sub clean_activity_data { + my $threshold_time = time() - 10 * Irssi::settings_get_int('dejunk_joinpart_idle_time'); + # Go through all clients and remove the ones that haven't performed any action + # in the past 10 * joinpart_idle_time seconds. + # The factor 10 is here to make sure that if the user increases joinpart_idle_time + # (by no more than a factor 10), we will still have enough data available for the + # script to work properly. + for my $tag (keys %activity) { + for my $nuh_mask (keys %{ $activity{$tag} }) { + if ($activity{$tag}{$nuh_mask}{last_msg} < $threshold_time) { + debug("Deleting old data for $tag:$nuh_mask"); + delete $activity{$tag}{$nuh_mask}; + } + } + } +} + +sub report_status { + message("Status report:"); + if (Irssi::settings_get_bool('dejunk_joinpart_enabled')) { + message("Join/part hiding enabled."); + my @tags = keys %activity; + my $num_hosts = 0; + for my $tag (@tags) { + $num_hosts += scalar keys %{ $activity{$tag} }; + } + message(sprintf "Joins, parts, nickchanges, and quits are only shown for clients that have been active ". + "in the past %d minutes (setting 'dejunk_joinpart_idle_time').", (Irssi::settings_get_int('dejunk_joinpart_idle_time'))); + message(sprintf "Joins, parts, nickchanges, and quits are always shown for channels with fewer than %d clients ". + "(setting 'dejunk_joinpart_min_size').", + Irssi::settings_get_int('dejunk_joinpart_min_size')); + message(sprintf "Currently, there is activity data on %d hostmask(s) in %d tag(s).", $num_hosts, scalar @tags); + } + else { + message("Join/part hiding disabled."); + } +} + +sub UNLOAD { + message("Dejunk is being unloaded - saving data."); + save_data(); +} + +sub debug { + if (Irssi::settings_get_bool('dejunk_debug')) { + _log('debug', @_); + } +} + +sub error { + _log('error', @_); +} + +sub warning { + _log('warning', @_); +} + +sub message { + _log('message', @_); +} + +sub _log { + my ($level, $fmt, @args) = @_; + my $str = sprintf($fmt, @args); + Irssi::printformat(MSGLEVEL_CLIENTCRAP, "dejunk_$level", $str); +} + +Irssi::command_bind('dejunk', 'cmd_dejunk'); +Irssi::command_bind('dejunk help', 'cmd_dejunk_help'); +Irssi::command_bind('dejunk status', 'cmd_dejunk_status'); +Irssi::command_bind('dejunk save', 'cmd_dejunk_save'); + +Irssi::settings_add_bool('dejunk', 'dejunk_joinpart_enabled', 1); +Irssi::settings_add_int( 'dejunk', 'dejunk_joinpart_min_size', 40); +Irssi::settings_add_int( 'dejunk', 'dejunk_joinpart_idle_time', 15); +Irssi::settings_add_bool('dejunk', 'dejunk_joinpart_show_unknown', 1); +Irssi::settings_add_bool('dejunk', 'dejunk_debug', 0); + +Irssi::signal_add({ + 'server connected' => \&event_connected, + 'server disconnected' => \&event_disconnected, + 'message join' => \&event_join, + 'message part' => \&event_part, + 'message quit' => \&event_quit, + 'message nick' => \&event_nick, +}); +Irssi::signal_add_last('message public', \&event_public); + +Irssi::theme_register( + [ + 'dejunk_error', + '{line_start}{hilight Dejunk:} [%RERROR%n] $0', + + 'dejunk_warning', + '{line_start}{hilight Dejunk:} [%YWARN%n] $0', + + 'dejunk_message', + '{line_start}{hilight Dejunk:} $0', + + 'dejunk_debug', + '{line_start}{hilight Dejunk:} [DEBUG] $0', + ], +); + +load_data(); +report_status(); +message("Type /dejunk help for help."); |
