Permalink
Switch branches/tags
Nothing to show
Find file Copy path
Fetching contributors…
Cannot retrieve contributors at this time
485 lines (406 sloc) 14 KB
use strict;
use warnings;
use Irssi;
use Irssi::Irc;
use Data::Dumper;
#use Devel::NYTProf;
our $VERSION = '1.2';
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 => '2018-01-07',
);
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;
# $ignorlist{$server/$channel}
my %ignorlist;
# marker for the refresh
my $time_tag_ignorlist;
my $time_tag_clean;
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");
message("quits can take up a large part of the activity. ");
message("Using dejunk, all these events on large channels are hidden");
message("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("Dejunk will save its data when it unloads, so when you upgrade");
message("it, or restart irssi quickly, it will remember ");
message("which nicks were active recently on which networks.");
message("");
message("Commands:");
message(" /dejunk status");
message(" Show which activity dejunk has seen recently.");
message(" /dejunk save");
message(" Force saving of data immediately.");
message(" Should not be needed at all.");
message("");
message("Settings:");
message(" dejunk_joinpart_enabled");
message(" Hide all non-relevant joins, parts, quits and nickchanges.");
message(" dejunk_joinpart_idle_time");
message(" The amount of minutes of inactivity after which a user");
message(" will be hidden.");
message(" dejunk_joinpart_min_size");
message(" Activity on channels with fewer users than this");
message(" is not hidden.");
message(" dejunk_joinpart_show_unknown");
message(" If it's unknown whether the user has been active recently, ");
message(" show them if this setting is true. This is only relevant");
message(" if the script has just been loaded for the first time.");
message(" dejunk_debug");
message(" set to ON to see debug messages.");
message("dejunk_update_ignorlist_time");
message(" set the update time in seconds for the ignorlist");
message("dejunk_clean_data_time");
message(" set the repeat time in seconds for the shrinking function");
message(" clean_activity_data");
message("");
message("You can see the current values of all dejunk settings via");
message("the following command:");
message(" /set dejunk");
message("You can change a setting via commands like this one:");
message(" /set dejunk_joinpart_min_size 100");
}
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 is_ignor_channel($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 is_ignor_channel($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});
return if is_ignor_nick($server,$nick);
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(),
}
}
return if is_ignor_nick($server,$newnick);
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 get_channel_str {
my $channel=$_[0];
my $cn=$channel->{'server'}->{'tag'}."/".$channel->{'name'};
#print $cn;
return $cn;
}
sub list_channels {
my @cl = Irssi::channels;
foreach (@cl) {
my @nicks= $_->nicks();
my $cn=get_channel_str($_);
#print $cn," ",$#nicks," ",$_->{"_irssi"};
if (!exists($ignorlist{$cn})) {
$ignorlist{$cn}=0;
}
if ($#nicks > Irssi::settings_get_int('dejunk_joinpart_min_size')) {
$ignorlist{$cn}=1;
}
}
}
sub is_ignor_channel {
(my $server, my $channel)=@_;
my $cn=$server->{"tag"}."/".$channel;
my $ci=1;
if (exists($ignorlist{$cn})) {
if ($ignorlist{$cn} >0) {
$ci=0;
}
}
#print "is_ignor_channel: ",$cn," ",$ci;
return $ci;
}
sub is_ignor_nick {
(my $server, my $nick)=@_;
my $res=1;
foreach my $channel ($server->channels()) {
if ($channel->nick_find($nick)) {
my $cn=get_channel_str($channel);
if (exists($ignorlist{$cn})) {
if ($ignorlist{$cn} >0) {
$res=0;
}
}
}
}
#print "is_ignor_nick: $server->{'tag'} $nick $res";
return $res;
}
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;
}
local $/;
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();
# DB::finish_profile();
}
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);
}
sub setup_change {
if ($time_tag_ignorlist) {
Irssi::timeout_remove($time_tag_ignorlist)
}
$time_tag_ignorlist=
Irssi::timeout_add(Irssi::settings_get_int('dejunk_update_ignorlist_time')
*1000,'list_channels',0);
if ($time_tag_clean) {
Irssi::timeout_remove($time_tag_clean)
}
$time_tag_clean=
Irssi::timeout_add(Irssi::settings_get_int('dejunk_clean_data_time')
*1000,'clean_activity_data',0);
}
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_int( 'dejunk', 'dejunk_update_ignorlist_time', 60);
Irssi::settings_add_int( 'dejunk', 'dejunk_clean_data_time', 60*10);
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::signal_add('setup changed', 'setup_change');
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',
],
);
setup_change();
list_channels();
load_data();
report_status();
message("Type /dejunk help for help.");