summaryrefslogtreecommitdiffstats
path: root/scripts/dejunk.pl
diff options
context:
space:
mode:
Diffstat (limited to 'scripts/dejunk.pl')
-rw-r--r--scripts/dejunk.pl418
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.");