bomb.pl


use strict;
use Irssi 20030108 qw(server_find_tag signal_add_first signal_add command_bind timeout_add timeout_remove settings_add_time settings_get_time settings_get_str get_irssi_dir);
use vars qw(%bombs %tomeouts $VERSION %IRSSI %stats %flood);
use Data::Dumper;
use Irssi::Irc;
use IO::File;

$VERSION = '0.9';
%IRSSI = (
    authors     => 'Valentin Batz',
    contact     => 'senneth@irssi.org',
    name        => 'bomb',
    description => '/bomb someone, he has to decide which wire to cut (tracks nickchanges and parts)',
    license     => 'GPLv2',
    commands	=> 'bomb bombstats bombadd',
    url         => 'http://www.oberkommando.org/~senneth/irssi/scripts/',
    revision    => '$LastChangedRevision: 369 $',
    changed     => '$LastChangedDate: 2006-01-31 22:35:03 +0100 (Di, 31 Jan 2006) $',
);

my %bombs;
my %timeouts;
my %flood;
my %stats;

sub flood_remover {
	# we remove the parted nicks after 5h if they don't come back
	my ($data)=@_;
	my ($servertag,$channel)=split(/ /,$data,2);
	timeout_remove($flood{$servertag}{$channel});
	delete($flood{$servertag}{$channel});
}

sub garbage_remover {
	# we remove the parted nicks after 5h if they don't come back
	my ($data)=@_;
	my ($servertag,$channel,$nick)=split(/ /,$data,3);
	delete($bombs{$servertag}{$channel}{$nick});
	timeout_remove($timeouts{$servertag}{$channel}{$nick});
	delete($timeouts{$servertag}{$channel}{$nick});
}

sub event_join {
	#  "message join", SERVER_REC, char *channel, char *nick, char *address
	my ($server,$channel,$nick,$address)=@_;
	my $jointimer=settings_get_time('bomb_join_reset_bomb_tick_time');
	return unless defined $bombs{$server->{tag}}{$channel}{$nick};
	if ($bombs{$server->{tag}}{$channel}{$nick} eq 'reset') {
		timeout_remove($timeouts{$server->{tag}}{$channel}{$nick});
		$bombs{$server->{tag}}{$channel}{$nick} = 'set';
		$timeouts{$server->{tag}}{$channel}{$nick}=timeout_add($jointimer,'bombtimer',"$server->{tag} $channel $nick");
		$server->command("msg $channel Haha $nick, you thought you can avoid that bomb by parting? no! defuse it(!red, !blue or !green) 15s remaining.");
	}
}

sub event_quit {
	# "message quit", SERVER_REC, char *nick, char *address, char *reason
	my ($server,$nick,$address,$reason )=@_;
	return unless $server;
	return unless defined $bombs{$server->{tag}};
	foreach my $channel ($server->channels()) {
		if (defined $bombs{$server->{tag}}{$channel->{name}}{$nick}){
                        delete($bombs{$server->{tag}}{$channel->{name}}{$nick});
                        timeout_remove($timeouts{$server->{tag}}{$channel->{name}}{$nick});
                        delete($timeouts{$server->{tag}}{$channel->{name}}{$nick});
		}
	}
	
}

sub event_part {
 	# "message part", SERVER_REC, char *channel, char *nick, char *address, char *reason
 	my ($server,$channel,$nick,$address,$reason) = @_;
	my $purgetime=settings_get_time('bomb_part_purge_time');
 	if (defined $bombs{$server->{tag}}{$channel}{$nick}) {
 		$bombs{$server->{tag}}{$channel}{$nick} = 'reset';
 		timeout_remove($timeouts{$server->{tag}}{$channel}{$nick});
 		delete($timeouts{$server->{tag}}{$channel}{$nick});
 		$timeouts{$server->{tag}}{$channel}{$nick}=timeout_add($purgetime,'garbage_remover',"$server->{tag} $channel $nick");
 	}
}

sub event_nick {
	# "message nick", SERVER_REC, char *newnick, char *oldnick, char *address
	# we track nickchanges here for $bombs and for $stats 
	# someone can overtake the stats of others but I don't care about it aslong noone finds it out ;)
	my ($server, $newnick, $nick, $address) = @_;
	$newnick = substr($newnick, 1) if ($newnick =~ /^:/);
	return unless defined $bombs{$server->{tag}};
	return if (lc $nick eq lc $newnick); 
	my $bombtimer=settings_get_time('bomb_tick_time_after_nickchange');
        foreach my $channel ($server->channels()) {
        	if (defined $bombs{$server->{tag}}{$channel->{name}}{$nick}) {
        		delete($bombs{$server->{tag}}{$channel->{name}}{$nick});
        		timeout_remove($timeouts{$server->{tag}}{$channel->{name}}{$nick});
        		delete($timeouts{$server->{tag}}{$channel->{name}}{$nick});
        		$bombs{$server->{tag}}{$channel->{name}}{$newnick} = 'set';
        		$server->command("msg $channel->{name} $newnick: You changed you nick. Now you have only 10s to defuse the bomb!");
        		$timeouts{$server->{tag}}{$channel->{name}}{$newnick}=timeout_add($bombtimer,'bombtimer',"$server->{tag} $channel->{name} $newnick");
        	}
		if (defined ($stats{'defusers'}{lc $nick})){
			if (defined $stats{'defusers'}{lc $newnick}) {
				$stats{'defusers'}{lc $newnick}=$stats{'defusers'}{lc $newnick}+$stats{'defusers'}{lc $nick};
			} else {
				$stats{'defusers'}{lc $newnick}=$stats{'defusers'}{lc $nick};
			}
			delete ($stats{'defusers'}{lc $nick});
		}
		if (defined ($stats{'exploders'}{lc $nick})) {
			if (defined $stats{'exploders'}{lc $newnick}) {
				$stats{'exploders'}{lc $newnick}=$stats{'exploders'}{lc $newnick}+$stats{'exploders'}{lc $nick};
			} else {
				$stats{'exploders'}{lc $newnick}=$stats{'exploders'}{lc $nick};
			}
			delete ($stats{'exploders'}{lc $nick});
		}
		if (defined ($stats{'cowards'}{lc $nick})) {
			if (defined $stats{'cowards'}{lc $newnick}) {
				$stats{'cowards'}{lc $newnick}=$stats{'cowards'}{lc $newnick}+$stats{'cowards'}{lc $nick};
			} else {
				$stats{'cowards'}{lc $newnick}=$stats{'cowards'}{lc $nick};
			}
			delete ($stats{'cowards'}{lc $nick});
		}
        		   
	}
}

sub bombtimer {
	my ($data)=@_;
	my ($servertag,$channel,$nick)=split(/ /,$data,3);
	my $server=server_find_tag($servertag);
	my $floodtime=settings_get_time('bomb_flood_time');
	return unless $server;
	if (defined $bombs{$servertag}{$channel}{$nick}) {
		if ($bombs{$servertag}{$channel}{$nick} eq 'set') {
			if (defined $stats{'cowards'}{lc $nick}) {
				$stats{'cowards'}{lc $nick}++;
			} else {
				$stats{'cowards'}{lc $nick}=1;
			}
			$server->command("kick $channel $nick *BOOM* You didn't even try to defuse the bomb :(");
		}
		delete($bombs{$servertag}{$channel}{$nick});
	}
	timeout_remove($timeouts{$servertag}{$channel}{$nick});
	delete($timeouts{$servertag}{$channel}{$nick});
	timeout_remove($flood{$servertag}{$channel});
	$flood{$servertag}{$channel}=timeout_add($floodtime,'flood_remover',"$servertag $channel");
}

sub public_message_handler {
	# "message public", SERVER_REC, char *msg, char *nick, char *address, char *target
	my ($server,$msg,$nick,$adress,$target)=@_;
	my $floodtime=settings_get_time('bomb_flood_time');
	return unless defined $bombs{$server->{tag}}{$target}{$nick};
	if ($msg=~/(^!red|^!blue|^!green)/) {
		my $number=int(rand(3));
		if ($number == 0) {
			$server->command("msg $target Congratulations $nick, you have defused the bomb.");
			if (defined $stats{'defusers'}{lc $nick}) {
				$stats{'defusers'}{lc $nick}++;
			} else {
				$stats{'defusers'}{lc $nick}=1;
			}
			delete($bombs{$server->{tag}}{$target}{$nick});
			timeout_remove($timeouts{$server->{tag}}{$target}{$nick});
			delete($timeouts{$server->{tag}}{$target}{$nick});
			timeout_remove($flood{$server->{tag}}{$target});
			$flood{$server->{tag}}{$target}=timeout_add($floodtime,'flood_remover',"$server->{tag} $target");
		} else {
			if (defined $stats{'exploders'}{lc $nick}) {
				$stats{'exploders'}{lc $nick}++;
			} else {
				$stats{'exploders'}{lc $nick}=1;
			}
			$server->command("kick $target $nick *BOOOM* wrong wire, better luck next time!");
			delete($bombs{$server->{tag}}{$target}{$nick});
			timeout_remove($timeouts{$server->{tag}}{$target}{$nick});
			delete($timeouts{$server->{tag}}{$target}{$nick});
			timeout_remove($flood{$server->{tag}}{$target});
			$flood{$server->{tag}}{$target}=timeout_add($floodtime,'flood_remover',"$server->{tag} $target");
		}
	}
}

sub bomb {
	my ($nick,$server,$winitem)=@_;
	$nick=~s/\s+$//g;
	my $bombticktime=settings_get_time('bomb_tick_time');
	my $bombticktimestr=settings_get_str('bomb_tick_time');
	return unless $server;
	return unless $nick;
	return unless $server->{chat_type} eq 'IRC';
	return unless $winitem->{type} eq 'CHANNEL';
	my $channel = $server->channel_find($winitem->{name});
	return unless $channel;
	return unless $channel->{chanop};
	return if defined $flood{$server->{tag}}{$channel->{name}};
	return if defined $bombs{$server->{tag}}{$channel->{name}}{$nick};
	my $nickrec=$channel->nick_find($nick);
	if ($nickrec && $nickrec->{serverop}==0){
		$bombs{$server->{tag}}{$channel->{name}}{$nick} = 'set';
		$timeouts{$server->{tag}}{$channel->{name}}{$nick}=timeout_add($bombticktime,'bombtimer',"$server->{tag} $channel->{name} $nick");
		timeout_remove($flood{$server->{tag}}{$channel->{name}});
		$flood{$server->{tag}}{$channel->{name}}=timeout_add($bombticktime,'flood_remover',"$server->{tag} $channel->{name}");
		$winitem->command("me hands $nick a ticking bomb. Try to defuse it by cutting the right wire with !red, !blue or !green hurry up you have only ".$bombticktimestr."!");
	}
}

sub bombstats {
	my ($args,$server,$winitem)=@_;
	$args=~s/\s+$//g;
	return unless $server;
        return unless $server->{chat_type} eq 'IRC';
        return unless ref($winitem) && $winitem->{type} eq 'CHANNEL';
 	my ($maxd,$maxb,$maxc,$countd,$countb,$countc,$sum)=(0,0,0,0,0,0,0);
	my ($bestd,$bestb,$bestc)=('','','');
	if ($args ne ''){
		my $final="$args has no stats yet";
		my $defusers=0;
		my $explodes=0;
		my $runaways=0;
		if ($stats{'defusers'}{lc $args}) {
			$defusers=$stats{'defusers'}{lc $args}+0;
		}
		if ($stats{'exploders'}{lc $args}) {
			$explodes = $stats{'exploders'}{lc $args}+0;
		}
		if ($stats{'cowards'}{lc $args}) {
			$runaways = $stats{'cowards'}{lc $args}+0;
		}
		if ($defusers||$explodes||$runaways) {
			my $summ=$defusers+$explodes+$runaways;
			$final = sprintf("%s has %d(%.2f%%) defuses, %d(%.2f%%) explodes, %d(%.2f%%) runaways",$args,$defusers,$defusers/$summ*100,$explodes,$explodes/$summ*100,$runaways,$runaways/$summ*100);
		    	#$final = "$args has $defusers defuses, $explodes explodes, $runaways runaways";
		}
		$winitem->command("say $final") if ($final ne "");
	} else {
		foreach (sort keys %{ $stats{'defusers'} }) {
			if ($stats{'defusers'}{$_}>$maxd) {
				$maxd=$stats{'defusers'}{$_};
				$bestd=$_;
			}
			if ($stats{'defusers'}{$_}==$maxd && index($bestd,$_) == -1) {
				$bestd=$bestd.', '.$_;
			}
			$countd=$countd+$stats{'defusers'}{$_};
		}
		foreach (sort keys %{ $stats{'exploders'} }) {
			if ($stats{'exploders'}{$_}>$maxb) {
				$maxb=$stats{'exploders'}{$_};
				$bestb=$_;
			}
			if ($stats{'exploders'}{$_}==$maxb && index($bestb,$_) == -1) {
				$bestb=$bestb.', '.$_;
			}
			$countb=$countb+$stats{'exploders'}{$_};
		}
		foreach (sort keys %{ $stats{'cowards'} }) {
			if ($stats{'cowards'}{$_}>$maxc) {
				$maxc=$stats{'cowards'}{$_};
				$bestc=$_;
			}
			if ($stats{'cowards'}{$_}==$maxc && index($bestc,$_) == -1) {
				$bestc=$bestc.', '.$_;
			}
			$countc=$countc+$stats{'cowards'}{$_};
		}
		$sum=$countc+$countb+$countd;
		if ($maxd>0) {$winitem->command("say Best defusers: $bestd with $maxd defuses.");}
		if ($maxb>0) {$winitem->command("say Best exploders: $bestb with $maxb explodes.");}
		if ($maxc>0) {$winitem->command("say Best cowards: $bestc with $maxc runaways.");}
		if ($sum>0) {
			my $msg=sprintf("Bombs handed: %d, defused %d(%.2f%%), exploded %d(%.2f%%), not touched %d(%.2f%%)",$sum,$countd,$countd/$sum*100,$countb,$countb/$sum*100,$countc,$countc/$sum*100);
			$winitem->command("say $msg");
		}
	}
}

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

# save on unload
sub UNLOAD {
	cmd_save();
}

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

sub bombadd {
	my ($args,$server,$winitem)=@_;
	$args=~s/\s+$//g;
	return unless $server;
	return unless $args;
	return unless $args ne "";
	my ($nick,$cmd,$newnick)=split(/ /,$args,3);
	if ($cmd eq 'cowards'){
                        if (defined $stats{'cowards'}{lc $nick}) {
                                $stats{'cowards'}{lc $nick}++;
                        } else {
                                $stats{'cowards'}{lc $nick}=1;
                        }
 	}
	if ($cmd eq 'defusers'){
                        if (defined $stats{'defusers'}{lc $nick}) {
                                $stats{'defusers'}{lc $nick}++;
                        } else {
                                $stats{'defusers'}{lc $nick}=1;
                        }
 	}
	if ($cmd eq 'exploders'){
                        if (defined $stats{'exploders'}{lc $nick}) {
                                $stats{'exploders'}{lc $nick}++;
                        } else {
                                $stats{'exploders'}{lc $nick}=1;
                        }
 	}
	if ($cmd eq 'merge' && $newnick ne '' && lc $nick ne lc $newnick) {
		if (defined ($stats{'defusers'}{lc $nick})){
			if (defined $stats{'defusers'}{lc $newnick}) {
				$stats{'defusers'}{lc $newnick}=$stats{'defusers'}{lc $newnick}+$stats{'defusers'}{lc $nick};
			} else {
				$stats{'defusers'}{lc $newnick}=$stats{'defusers'}{lc $nick};
			}
			delete ($stats{'defusers'}{lc $nick});
		}
		if (defined ($stats{'exploders'}{lc $nick})){
			if (defined $stats{'exploders'}{lc $newnick}) {
				$stats{'exploders'}{lc $newnick}=$stats{'exploders'}{lc $newnick}+$stats{'exploders'}{lc $nick};
			} else {
				$stats{'exploders'}{lc $newnick}=$stats{'exploders'}{lc $nick};
			}
			delete ($stats{'exploders'}{lc $nick});
		}
		if (defined ($stats{'cowards'}{lc $nick})){
			if (defined $stats{'cowards'}{lc $newnick}) {
				$stats{'cowards'}{lc $newnick}=$stats{'cowards'}{lc $newnick}+$stats{'cowards'}{lc $nick};
			} else {
				$stats{'cowards'}{lc $newnick}=$stats{'cowards'}{lc $nick};
			}
			delete ($stats{'cowards'}{lc $nick});
		}
	}
	if ($cmd eq 'delete'){
		if (defined( $stats{'defusers'}{lc $nick})) {
			delete($stats{'defusers'}{lc $nick});
		}
		if (defined( $stats{'cowards'}{lc $nick})) {
			delete($stats{'cowards'}{lc $nick});
		}
		if (defined( $stats{'exploders'}{lc $nick})) {
			delete($stats{'exploders'}{lc $nick});
		}
	}
				
}
signal_add('message public','public_message_handler');
signal_add('message nick', 'event_nick');
signal_add('message part','event_part');
signal_add('message quit','event_quit');
signal_add('message join','event_join');
signal_add('setup saved', 'cmd_save');

command_bind('bomb','bomb');
command_bind('bombstats','bombstats');
command_bind('bombadd','bombadd');
settings_add_time('misc','bomb_flood_time','15s');
settings_add_time('misc','bomb_tick_time','30s');
settings_add_time('misc','bomb_part_purge_time','5h');
settings_add_time('misc','bomb_tick_time_after_nickchange','10s');
settings_add_time('misc','bomb_join_reset_bomb_tick_time','15s');
cmd_load();