#!/usr/bin/perl -T
# Quizgr script for irssi with "KAOS" questions enabled, modified for greek too
# copyright Athanasius Emilius Arvanitis
# arvan@kronos.eng.auth.gr
# based on quiz.pl version 0.7
# Quiz script for irssi
# (C) Simon Huggins 2001
# huggie@earth.li

# This program is free software; you can redistribute it and/or modify it
# under the terms of the GNU General Public License as published by the Free
# Software Foundation; either version 2 of the License, or (at your option)
# any later version.
#
# This program is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
# or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
# for more details.
#
# You should have received a copy of the GNU General Public License along
# with this program; if not, write to the Free Software Foundation, Inc., 59
# Temple Place, Suite 330, Boston, MA 02111-1307  USA
#
# DONE:
#       - support for many answers (not alternate though) per question
#       - support for hellenic (aka greek)
#       - remembers the questions that were answered (stores them in
#       ./wr/used_questions)
#       - if nobody says anything for a period of time, game ends
#       - added !repeat to repeat the question
#       - it wont crash if you smile!
# TODO:
#       - known bug: sometimes it ignores some people (they cant join etc).
#	if you join #CHANNEL and the bot is on #channel forget it...
#       - fix kaos hints
#       - if we have kaos, there should be more time to answer
#       - CLEAN up the code
# 	- Do something when people quit (remove from team, readd when rejoin?)

use strict;
use vars qw($VERSION %IRSSI);
    
use Irssi 20020217.1542 (); # Version 0.8.1 or perhaps get the most up to date irssi version
$VERSION = "0.7GR03";
%IRSSI = (
authors     => "Athanasius Emilius Arvanitis based on Simon Huggins quiz 0.7",
contact     => "arvan",
name        => "Quizgr",
description => "Turns irssi into a quiz bot. Has greek language and many answers support",
license     => "GPLv2",
url         => "http://kronos.eng.auth.gr/~arvan/irssi/",
changed     => "Tue Nov 26 13:37:59 EET 2002",
);

use Irssi::Irc;
use Data::Dumper;

Irssi::settings_add_str("misc","quiz_admin","jbg");
Irssi::settings_add_str("misc","quiz_passwd","stuff");
Irssi::settings_add_str("misc","quiz_file","$ENV{HOME}/.irssi/scripts/autorun/gr_quiz_questions");
Irssi::settings_add_str("misc","used_file","$ENV{HOME}/.irssi/scripts/autorun/wr/used_questions");

Irssi::settings_add_int("misc","quiz_qlength",70);
Irssi::settings_add_int("misc","quiz_hints",7);
Irssi::settings_add_int("misc","quiz_target_score",50);
Irssi::settings_add_int("misc","quiz_leave_concealed_chars",1);

Irssi::command("set cmd_queue_speed 2010");

{
# when warnings used $s complains
my $s;
my $answerBAKforCHAOS;

sub load_questions($$) {
	my ($game,$force) = @_;
	my $tag = $game->{'tag'};
	my $channel = $game->{'channel'};

	$game->{'used_questions'}=[];

	my $server = Irssi::server_find_tag($tag);

	if (!defined $server) {
		Irssi::print("Hrm, couldn't find server for tag ($tag) in load_questions");
		return;
	}

	return if $game->{'questions'} and not $force;

	#the next must be checked

	my $file = Irssi::settings_get_str("quiz_file");
	if (open(QS, "<", $file)) { #open for QS
		@{$game->{'questions'}}=sort <QS>;
		close(QS);
		Irssi::print("Loaded questions");

		my $file2 = Irssi::settings_get_str("used_file");
		if (open(QS2, "<", $file2)) { #open for QS2
			@{$game->{'used_questions'}}=sort <QS2>;
			close(QS2);

			#from perlfaq copy paste 
			@{$game->{'intersection'}} = @{$game->{'difference'}} = ();
			%{$game->{'count'}} = ();

			my $element;
			foreach $element (@{$game->{'questions'}}, @{$game->{'used_questions'}}) { ${$game->{'count'}}{$element}++ };

			foreach $element (keys %{$game->{'count'}}) { #open foreach
				push @{ ${$game->{'count'}}{$element} > 1 ? \@{$game->{'intersection'}} : \@{$game->{'difference'}} }, $element;
				} #close foreach

			my $ts = Irssi::settings_get_int("quiz_target_score");
			my $qCounter=@{$game->{'questions'}};
			${$game->{'usedCounter'}}=@{$game->{'used_questions'}};

			my $qGOT=($qCounter - ${$game->{'usedCounter'}}); 
			my $qNEEDED=(2*($ts)+12);
			Irssi::print("${$game->{'usedCounter'}} used out of $qCounter total questions");

			if ( $qGOT >= $qNEEDED ) { 
				@{$game->{'questions'}}=@{$game->{'difference'}};
				Irssi::print("Loaded not used questions");
				return 1;#used
				}

			if ( $qGOT < $qNEEDED ) { 
				@{$game->{'used_questions'}}=();
				Irssi::print("Clearing used questions");
				return 1;#used
				}
		} #close QS2

		return 1;#questions
		} #close QS

	  else {
		$server->command("msg $channel Can't find quiz questions, sorry.");
		return;
		}



}

sub start_game($) {
	my $game = shift;
	my $tag = $game->{'tag'};
	my $channel = $game->{'channel'};
	my $server = Irssi::server_find_tag($tag);

	if (!defined $server) {
		Irssi::print("Hrm, couldn't find server for tag ($tag) in start_game");
		return;
		}

	Irssi::timeout_remove($game->{'timeouttag'});
	undef $game->{'timeouttag'};

	if (!keys %{$game->{'teams'}}) {
		$server->command("msg $channel Sorry no one joined!");
		$game->{'state'} = "over";
		game_over($game);
		return;
		}

	$game->{'state'} = "game";

	$server->command("msg $channel Game starts now. Questions last ".
		Irssi::settings_get_int("quiz_qlength").
		" seconds and there are ".
		(Irssi::settings_get_int("quiz_hints")-1).
		" hints.  First to reach ".
		Irssi::settings_get_int("quiz_target_score")." wins.");
	next_question($game);
}

sub show_scores($) {
	my $game = shift;
	my $tag = $game->{'tag'};
	my $channel = $game->{'channel'};
	my $server = Irssi::server_find_tag($tag);

	if (!defined $server) {
		Irssi::print("Hrm, couldn't find server for tag ($tag) in show_scores");
		return;
		}

	my (@redscorers,@bluescorers);

	foreach my $score (sort keys %{$game->{'scores'}}) {
		if ($score =~ /^blue/) {
			$score =~ s/^blue//;
			push @bluescorers, "$score(".
				$game->{'scores'}->{"blue".$score}.")";
		} else {
			$score =~ s/^red//;
			push @redscorers, "$score(".
				$game->{'scores'}->{"red".$score}.")";
		}
	}

	$server->command("msg $channel 12Blue: ".$game->{'bluescore'}
		."  ".join(",",@bluescorers));
	$server->command("msg $channel 4Red: ".$game->{'redscore'}
		."  ".join(",",@redscorers));
	
	my $ts = Irssi::settings_get_int("quiz_target_score");

	if ($game->{'bluescore'} >= $ts or $game->{'redscore'} >= $ts) {
		if ($game->{'bluescore'} > $game->{'redscore'}) {
			$server->command("msg $channel 12Blue team wins ".
				$game->{'bluescore'}." to ".
				$game->{'redscore'});
		} else {
			$server->command("msg $channel 4Red team wins ".
				$game->{'redscore'}." to ".
				$game->{'bluescore'});
		}
		$game->{'state'}="over";
	} elsif ($game->{'state'} ne "over") {
		$game->{'state'}="pause";
		$server->command("msg $channel Next question in 6 20 seconds.");
		if ($game->{'timeouttag'}) {
			Irssi::timeout_remove($game->{'timeouttag'});
		}
		$game->{'timeouttag'} = Irssi::timeout_add(20000,
			"next_question",$game);
		$game->{'timeout'} = time() + 20;
	}
	game_over($game);
}

sub hint($) {
	my $game = shift;
	my $tag = $game->{'tag'};
	my $channel = $game->{'channel'};
	my $server = Irssi::server_find_tag($tag);

	if (!defined $server) {
		Irssi::print("Hrm, couldn't find server for tag ($tag) in hint");
		return;
		}

	return if game_over($game);
	if ($game->{'end'} <= time()) {
		$server->command("msg $channel Time's up.  The answer is: 2  ".$game->{'answer'});
		show_scores($game);
	} else {
		$game->{'hint'}++;
		my $num = $game->{'current_answer'} =~ s/\*/*/g;
		if ($num <= Irssi::settings_get_int("quiz_leave_concealed_chars")) {
			return;
			}

		my $pos = index($game->{'current_answer'},"*");
		if ($pos >= 0) {
			#$game->{'current_answer'} =~ s/\*/substr($game->{'answer'},$pos,1)/e;
			$game->{'current_answer'} =~ s/\*/substr($answerBAKforCHAOS,$pos,1)/e;
			}

		my $hinttime = $game->{'hint'}*$game->{'hintlen'};
		if ($hinttime != int($hinttime)) {
			$hinttime = sprintf("%.2f", $hinttime);
			}
		$server->command("msg $channel 2  $hinttime second hint: 6  ".
			$game->{'current_answer'});
	} #else end
}

sub game_over($) {
	my $game = shift;
	my $tag = $game->{'tag'};
	my $channel = $game->{'channel'};
	my $server = Irssi::server_find_tag($tag);

	if (!defined $server) {
		Irssi::print("Hrm, couldn't find server for tag ($tag) in game_over");
		return;
	}

	if ($game->{'state'} eq "over") {
		Irssi::timeout_remove($game->{'timeouttag'});
		undef $game->{'timeouttag'};
		undef $game->{'state'};
		undef $game->{'teams'};
		undef $game->{'scores'};

                #save used questions
                my $file2 = Irssi::settings_get_str("used_file");
                if (open(QS2, ">", $file2)) {
                        my $line;
			@{$game->{'used_questions'}}=sort @{$game->{'used_questions'}};
                        foreach $line (@{$game->{'used_questions'}}){
                                print QS2 $line ;
                                }
                        close(QS2);
                        Irssi::print("Saved used questions");
                        }


		$server->command("msg $channel Trivia is disabled.  Use !start or !trivon to restart.");
		return 1;
	}
	return;
}

sub next_question($) {
	my $game = shift;
	my $tag = $game->{'tag'};
	my $channel = $game->{'channel'};
	my $server = Irssi::server_find_tag($tag);

	if (!defined $server) {
		Irssi::print("Hrm, couldn't find server for tag ($tag) in next_question");
		return;
	}

	#check previous text time and
	#if noone says anything for 180 seconds end game
	if (defined $game->{'time_last_text'}) {
		my $diff2=time() - $game->{'time_last_text'};
		if ( $diff2 > 180) {
			$game->{'state'}="over";
		}
	}


	my $len = Irssi::settings_get_int("quiz_qlength")/
		Irssi::settings_get_int("quiz_hints");
	if ($game->{'timeouttag'}) {
		Irssi::timeout_remove($game->{'timeouttag'});
	}
	$game->{'timeouttag'} = Irssi::timeout_add($len*1000, "hint",$game);
	my $t = time();
	$game->{'timeout'} = $t + $len;
	$game->{'end'} = Irssi::settings_get_int("quiz_qlength")+$t;
	$game->{'hint'}=0;
	$game->{'hintlen'} = $len;
	if (!@{$game->{'questions'}}) {
		load_questions($game,1);
		if (!$game->{'questions'}) {
			$server->command("msg $channel Hmmm, no questions found sorry");
			$game->{'state'}="over";
		}
		Irssi::print("Questions looped");
	}
	return if game_over($game);

	#random question
	${$game->{'randIDX'}}= @{$game->{'questions'}};
	${$game->{'randIDX'}}=rand(${$game->{'randIDX'}});
	my $q = ${$game->{'questions'}}[${$game->{'randIDX'}}];
	${$game->{'the_question'}} = $q;

	#removing it from the questions
	splice (@{$game->{'questions'}}, ${$game->{'randIDX'}}, 1);

	#see faq for splice/random may be bad
	#my $q = splice(@{$game->{'questions'}},rand(@{$game->{'questions'}}),1);
	chomp $q;
	$q =~ s///;
	#($game->{'answer'} = $q) =~ s/^(.*)\|//;

	($game->{'question'}, $game->{'answer'}) = split(/\|/, $q,2);
	$answerBAKforCHAOS = $game->{'answer'};
	if ( $game->{'answer'} =~ /\|/ ) 
		{ $server->command("msg $channel KAOS CHAOS ×ÁÏÓ!!!"); 
			$game->{'answer'} = $game->{'answer'}."|";

		}
	$server->command("msg $channel 13Question: 10 $game->{'question'} ");
	#added Á-Ùá-ù so it can hide greek too
	($game->{'current_answer'} = $game->{'answer'}) =~ s/[a-zA-Z0-9Á-Ùá-ù]/*/g;
	#$q = s/^(.*)\|.*?$/$1/;
	$server->command("msg $channel Answer:  ".$game->{'current_answer'});
	$game->{'state'}="question";
}

sub invite_join($$) {
	my ($server,$channel) = @_;
	my $game = $s->{$server->{'tag'}}->{$channel};

	$server->command("msg $channel Team Trivia thingummie v($VERSION) starts in 1 minute.  Type 4!join red or 12!join blue");
	$game->{'timeouttag'} = Irssi::timeout_add(60000,"start_game",$game);
	$game->{'timeout'} = time()+60;
}

sub secstonormal($) {
	my $seconds = shift;
	my ($m,$s);

	$s = $seconds % 60;
	$m = ($seconds - $s)/60;
	return sprintf("%02d:%02d",$m,$s);
}

sub do_pubcommand($$$$) {
	my ($command,$channel,$server,$nick) = @_;
	my $game = $s->{$server->{'tag'}}->{$channel};

	$command = lc $command;
	$command =~ s/\s*$//;

	if ($command =~ /^!bang$/) {
		$server->command("msg $channel Dumping...");
		foreach (split /\n/,Dumper($s)) {
			Irssi::print("$_");
		}
	} elsif ($command =~ /^!trivon$|^!start$|!ðÜìå$/) {
		if ($s->{$server->{'tag'}}->{$channel}) {
			if ($s->{$server->{'tag'}}->{$channel}->{'state'}) {
				$server->command("msg $nick Trivia is already on.  Use !trivoff or !stop to remove it.");
				return;
			}
			#undef $s->{$server->{'tag'}}->{$channel};
		} else {
			# create structure magically
			$game = $s->{$server->{'tag'}}->{$channel} = {};
			$game->{'tag'} = $server->{'tag'};
			$game->{'channel'} = $channel;
		}
		$game->{'teams'}={};
		$game->{'redscore'} = 0;
		$game->{'bluescore'} = 0;
		load_questions($game,0);
		$game->{'state'} = "join";
		invite_join($server,$channel);
	} elsif ($command =~ /^!trivoff$|^!stop$|!öôÜíåé$/) {
		return if !$game->{'state'};
		$game->{'state'}="over";
		game_over($game);
	} elsif ($command =~ /^!join/) {
		if ($command =~ /^!join (red|blue)$/) {
			return if !$game->{'state'};
			$game->{'teams'}->{$nick}=$1;
			if ($1 eq "blue") {
				$server->command("notice $nick You have joined the 12Blue team");
			} else {
				$server->command("notice $nick You have joined the 4Red team");
			}
		}
	} elsif ($command =~ /^!teams/) {
		return if !$game->{'state'};
		my @blue=();
		my @red=();
		foreach (sort keys %{$game->{'teams'}}) {
			push @blue, $_ if $game->{'teams'}->{$_} eq "blue";
			push @red,  $_ if $game->{'teams'}->{$_} eq "red";
		}
		$server->command("msg $channel 12Blue: ".join(",",@blue));
		$server->command("msg $channel 4Red : ".join(",",@red));
	} elsif ($command =~ /^!repeat$/) {
		return if !$game->{'state'};
		$server->command("msg $channel Question is $game->{'question'}");
	} elsif ($command =~ /^!timeleft$/) {
		if ($game->{'state'} eq "join" and $game->{'timeout'}) {
			my $diff = $game->{'timeout'} - time();
			if ($diff > 0) {
				$server->command("msg $channel Time left: ".secstonormal($diff));
			} else {
				Irssi::print("Timeleft: $diff ??");
			}
		}
	}
}

sub do_command($$$) {
	my ($command,$nick,$server) = @_;

	$command = lc $command;
	$command =~ s/\s*$//;

	if ($command =~ /^!bang$/) {
		$server->command("msg $nick BOOM!");
	} elsif ($command =~ /^admin/) {
		if ($command !~ /^admin (.*)$/) {
			$server->command("msg $nick admin needs a nick to change the admin user to!");
		} else {
			Irssi::settings_remove("quiz_admin");
			Irssi::settings_add_str("misc","quiz_admin",$1);
			$server->command("msg $nick admin user is now $1");
		}
	} else {
		#$server->command("msg $nick Unknown command '$command'");
	}
}

#check check_answer for bad { }

sub check_answer($$$$) {
	my ($server,$channel,$nick,$text) = @_;
	my $game = $s->{$server->{'tag'}}->{$channel};

	return if not exists $game->{'teams'}->{$nick};

	#if $text exist check time and remembers it for end-game
	if (defined $text) {
		$game->{'time_last_text'} = time();
		}


	$text =~ s/\s*$//;
	$text =~ s/^ //;
	$text =~ s/ $//;
	
	#from cgi input-purify / try without it and it will crash with :( 
	#dont know if it needs em all, may check it in future
	
	if ($text =~ s/([\&;\`'\\\|"*?~<>^\(\)\[\]\{\}\$\n\r])/\\$1/g)
		{ $text="abcdef";
		}
	# is the above the reason it didnt join?

	#if greek supports troubles you comment the next
	$text =~ y/ÜÝÞßúÀüýûàþ¢¶¸¹ºÚ¼¾Û¿ÁÂÃÄÅÆÇÈÉÊËÌÍÎÏÐÑÓÔÕÖ×ØÙ/áåçéééïõõõù¶áåçééïõõùáâãäåæçèéêëìíîïðñóôõö÷øù/;

	my $answerNOtonos = lc $game->{'answer'};
	#if greek supports troubles you comment the next
	$answerNOtonos =~ y/ÜÝÞßúÀüýûàþ¢¶¸¹ºÚ¼¾Û¿ÁÂÃÄÅÆÇÈÉÊËÌÍÎÏÐÑÓÔÕÖ×ØÙ/áåçéééïõõõù¶áåçééïõõùáâãäåæçèéêëìíîïðñóôõö÷øù/;

	if ( $answerNOtonos =~ /\|/) { 

			if ( ($answerNOtonos =~ /\|$text\|/)||($answerNOtonos =~ /^$text\|/) ) 
			{
			$answerNOtonos =~ s/$text\|//;

	 		$game->{'answer'}=$answerNOtonos; 


			$server->command("msg $channel  2Correct answer by ".
			($game->{'teams'}->{$nick} eq "blue"?"12":"4").
			 $nick.": ".$text);

			$game->{$game->{'teams'}->{$nick}."score"}++;
			$game->{'scores'}->{$game->{'teams'}->{$nick}.$nick}++;

			if ($answerNOtonos eq "") { 

        			#putting it in used
				if (@{$game->{'used_questions'}}){
        			${$game->{'usedCounter'}} = @{$game->{'used_questions'}};
				} else {${$game->{'usedCounter'}}=0;}

        			${$game->{'used_questions'}}[${$game->{'usedCounter'}}]=${$game->{'the_question'}};

				$game->{'state'}="won";

				$server->command("msg $channel 2  $answerBAKforCHAOS") ;
				show_scores($game);
				return;
				}

			#show_scores($game);
			}
	}


                elsif (( $answerNOtonos !~ /\|/) && (lc $text eq $answerNOtonos))  {

			$server->command("msg $channel 2Correct answer by ".
			($game->{'teams'}->{$nick} eq "blue"?"12":"4").
			$nick.": ".$game->{'answer'});
			$game->{'state'}="won";

        			#putting it in used
				if (@{$game->{'used_questions'}}){
        			${$game->{'usedCounter'}} = @{$game->{'used_questions'}};
				} else {${$game->{'usedCounter'}} =0;}

        			${$game->{'used_questions'}}[${$game->{'usedCounter'}}]=${$game->{'the_question'}};

			$game->{$game->{'teams'}->{$nick}."score"}++;
			$game->{'scores'}->{$game->{'teams'}->{$nick}.$nick}++;
			show_scores($game);
			return;
		}



	my $show=0;
	my @chars = split //,$text;

	for (my $i=0; $i<length($game->{'answer'}); $i++) {
		if (lc $chars[$i] eq lc substr($game->{'answer'},$i,1)) {
			$show = 1 if substr($game->{'current_answer'},$i,1)
				eq "*";
			substr($game->{'current_answer'},$i,1) =
				substr($game->{'answer'},$i,1);
		}
	}

	$server->command("msg $channel Answer: ".$game->{'current_answer'})
		if $show;
}



sub event_privmsg {
	my ($server,$data,$nick,$address) = @_;
	my ($target, $text) = split / :/,$data,2;
	my ($command);

	if ($target =~ /^#/) {
		my $game = $s->{$server->{'tag'}}->{$target};
		if ($text =~ /^!/) {
			do_pubcommand($text,$target,$server,$nick);
		} elsif ($game->{'state'} eq "question") {
			check_answer($server,$target,$nick,$text);
		}
	} else {
		if ($nick ne Irssi::settings_get_str("quiz_admin")) {
			my ($passwd);
			($passwd, $command) = split /\s/,$text,2;
			if ($passwd ne Irssi::settings_get_str("quiz_passwd")) {
				#Irssi::print("$nick tried to do $command but got the password wrong.");
				Irssi::print("$nick got the password wrong.");
			}
		} else {
			$command = $text;
		}
		do_command($command,$nick,$server);
	}
}

sub event_changed_nick {
	my ($channel,$nick,$oldnick) = @_;
	my $server = $channel->{'server'};
	my $game = $s->{$server->{'tag'}}->{$channel->{'name'}};

	return if !$game->{'state'};
	
	my $nicktxt = $nick->{'nick'};
	if ($game->{'teams'}->{$oldnick}) {
		$game->{'teams'}->{$nicktxt} = $game->{'teams'}->{$oldnick};
		delete $game->{'teams'}->{$oldnick};
		}

}


}

Irssi::signal_add_last("event privmsg", "event_privmsg");
# Irssi::signal_add_last("massjoin", "sig_massjoin");
#Irssi::signal_add_last("message nick", "on_nick"); #when /nick
#Irssi::signal_add_last("message part", "on_part");
#Irssi::signal_add_last("message join", "on_join");
#Irssi::signal_add_last("message quit", "on_quit");

# Channel::nicks(channel) Return a list of all nicks in channel.

Irssi::signal_add("nicklist changed", "event_changed_nick");
