snr.pl


# script to display signal to noise ratio of a channel in the statusbar
# todo:
#  -check if message is ignored and consider it as noise
#   this needs separate signal handlers for each message 
#   considered as signal
#  -add theme support for the sbar

use strict;
use Irssi;
use Irssi::TextUI;
use Irssi::Irc;
use 5.6.1;
use IO::File;
use vars qw($VERSION %IRSSI $snrbar %snr);

$VERSION = "0.3";
%IRSSI = (
    authors     => 'Valentin Batz',
    contact	=> 'senneth@irssi.org',
    name        => 'snr',
    description => 'displays a signal to noise ratio in the statusbar',
    url		=> 'http://www.oberkommando.org/~senneth/irssi/scripts/',
    license     => 'GPLv2',
    sbitems	=> 'snr',
    revision    => '$LastChangedRevision: 449 $',
    changed     => '$LastChangedDate: 2007-12-12 22:12:35 +0100 (Mi, 12 Dez 2007) $',
    version     => $VERSION,
);

my %snr;
my $snrbar;

sub noise {
	# "nicklist new", CHANNEL_REC, NICK_REC
	# "nicklist remove", CHANNEL_REC, NICK_REC
	# "nicklist changed", CHANNEL_REC, NICK_REC, char *old_nick
	# "channel mode changed", CHANNEL_REC, char *setby
	# "nick mode changed", CHANNEL_REC, NICK_REC, char *setby, char *mode, char *type
	# "channel topic changed", CHANNEL_REC
	my $channel = shift;
	# don't start counting stuff until channel is synched / won't work with /set channel_sync off
	return unless $channel->{synced};
	if ($channel->{chat_type} eq 'IRC') {
		my ($s,$n) = split( "/", $snr{$channel->{server}->{tag}}{$channel->{name}});
		$n++;
		$snr{$channel->{server}->{tag}}{$channel->{name}} = $s.'/'.$n;
		refresh_sbar();
	}
}

sub signal {
	# "message public",         SERVER_REC, char *msg, char *nick, char *address, char *target
	# "message irc action",     SERVER_REC, char *msg, char *nick, char *address, char *target
	# "message own_public",     SERVER_REC, char *msg, char *target
	# "message irc own_action", SERVER_REC, char *msg, char *target
	my ($server, $msg, $nick, $addr, $target) = @_;
	$target = $nick if (!defined($target) && !defined($addr));
	my $channel = $server->channel_find($target);
	return unless defined $channel;
	if ($channel->{chat_type} eq 'IRC') {
		my ($s,$n) = split( "/", $snr{$channel->{server}->{tag}}{$channel->{name}});
		$s++;
		$snr{$channel->{server}->{tag}}{$channel->{name}} = $s.'/'.$n;
		refresh_sbar();
	}
}

sub add_channel {
	my $channel = shift;
	return if defined($snr{$channel->{server}->{tag}}{$channel->{name}});
	if ($channel->{chat_type} eq 'IRC') {
		$snr{$channel->{server}->{tag}}{$channel->{name}} = "0/0";
	}
}

sub init {
	foreach my $channel (Irssi::channels()) {
		if ($channel->{chat_type} eq 'IRC' && !defined($snr{$channel->{server}->{tag}}{$channel->{name}})) {
			$snr{$channel->{server}->{tag}}{$channel->{name}} = "0/0";
		}
	}
}

sub refresh_sbar() {
	my $witem = Irssi::active_win()->{active};
	return unless defined $witem && $witem->{type} eq 'CHANNEL';
	my $server = $witem->{server};
	$snrbar = $snr{$server->{tag}}{$witem->{name}};
	my ($s,$n) = split( "/", $snrbar);
	my $snrcolor = '';
	my $ratio = 0;
	if ($n > 0 && $s > 0) {
	    $ratio = $s / ($s + $n);
            if ($ratio > 0.6) {
              $snrcolor = '%g';
            }
            elsif ($ratio > 0.3) {
              $snrcolor = '%y';
            }
            else {
              $snrcolor = '%r';
            }
	}
	my $snrpercent = sprintf("%.2f%%", $ratio * 100);
	my $snrtmpstr = "%Ksnr%n%G:%n$snrcolor $snrbar($snrpercent)%B";
	$snrbar = $snrtmpstr;
        Irssi::statusbar_items_redraw('snr');
}
        
sub refresh_sbar_later() {
	Irssi::timeout_add_once(1000, "refresh_sbar", undef);
}
                
sub sb_snr {
        my ($item, $get_size_only) = @_;
	$item->default_handler($get_size_only, "{sb $snrbar}", "", 1);
}

sub cmd_save {
        my $filename = Irssi::get_irssi_dir()."/snr";
        my $io = new IO::File $filename, "w";
        if (defined $io) {
                my $dumper = Data::Dumper->new([\%snr]);
                $dumper->Purity(1)->Deepcopy(1);
                $io->print($dumper->Dump);
                $io->close;
        }
        Irssi::print("snr saved to ".$filename);
}


sub UNLOAD {
	cmd_save();
}

sub cmd_load {
    my $filename = Irssi::get_irssi_dir()."/snr";;
    my $io = new IO::File $filename, "r";
    if (defined $io) {
        no strict 'vars';
        my $text;
        $text .= $_ foreach ($io->getlines);
        my $stats = eval "$text";
        %snr = %$stats if ref $stats;
    }
    Irssi::print("snr loaded from ".$filename);
}

sub say_snr {
  my ($data,$server,$item) = @_;
  return unless ref $item;
  if ($item->{'type'} eq 'CHANNEL') {
 	my $channel = $server->channel_find($item->{'name'});
        return unless defined $channel;
        if ($channel->{chat_type} eq 'IRC') {
           my ($s,$n) = split('/', $snr{$channel->{server}->{tag}}{$channel->{name}});
           return if (($s + $n) == 0);
           my $str = sprintf("%d/%d(%.2f%)", $s, $n, ($s/($s + $n))*100);
	   $channel->command('say '.$str);
        }
  }
}

Irssi::statusbar_item_register('snr', undef, 'sb_snr');

Irssi::signal_add("nicklist new",\&noise);
Irssi::signal_add("nicklist changed", \&noise);
Irssi::signal_add("nicklist remove", \&noise);
Irssi::signal_add("channel mode changed", \&noise);
Irssi::signal_add("nick mode changed", \&noise);
Irssi::signal_add("message public", \&signal);
Irssi::signal_add("message own_public", \&signal);
Irssi::signal_add("message irc action", \&signal);
Irssi::signal_add("message irc own_action", \&signal);
Irssi::signal_add("channel created", \&add_channel);
Irssi::signal_add("channel topic changed", \&noise);
Irssi::signal_add('setup saved', \&cmd_save);

Irssi::signal_add('window item changed', \&refresh_sbar_later);
Irssi::signal_add('window item server changed', \&refresh_sbar_later);
# thanks to Paul Raade (laaama on IRCnet) for pointing that out
Irssi::signal_add('window changed', \&refresh_sbar_later);
Irssi::command_bind('snr', \&say_snr);
cmd_load();
init();