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();