diff options
Diffstat (limited to '')
-rw-r--r-- | scripts/quizgr.pl | 655 |
1 files changed, 655 insertions, 0 deletions
diff --git a/scripts/quizgr.pl b/scripts/quizgr.pl new file mode 100644 index 0000000..a2d36df --- /dev/null +++ b/scripts/quizgr.pl @@ -0,0 +1,655 @@ +#!/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"); |