Return to Snippet

Revision: 15974
at July 22, 2009 04:11 by tueb


Initial Code
##
### moxquizz.tcl -- quizzbot for eggdrop 1.6.9+
##
### Author: Moxon <[email protected]> (AKA Sascha Lüdecke)
##
### Credits:
##       - Artwork was done with heavy support of Michee <[email protected]>
##       - Julika loved to discuss and suggested many things.
##       - Imran Ghory provided more than 600 english questions.
##       - Questions have been edited by Michee, Julika, Tobac, Imran and
##         Klinikai_Eset
##       - ManInBlack for supplemental scripting
##       - numerous others, see the README for a more complete list.
##         If you are missing, please tell me!
##
### Copyright (C) 2000 Moxon AKA Sascha Lüdecke
##
##  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., 675 Mass Ave, Cambridge, MA 02139, USA.
##
###
## $Id: moxquizz.tcl,v 1.175 2002/07/05 19:21:26 chat Exp $


###########################################################################
###########################################################################
###########################################################################

########################
### typ's mod config ###
########################

#Wieviel Sekunden sollen Schätzfragen offen sein?
set schaetztime 15

#Soll !vok erlaubt sein? (1=ja,2=nein)
set allow_vok 1

#Soll !halt erlaubt sein? (1=ja,2=nein)
set allow_halt 1

#Soll das Anti-Highlighting aktiviert sein? (1=ja,2=nein)
set allow_antihl 1

#Soll das gelbe Design aktiviert sein? (1=ja,2=nein)
set go_yellow 0

#Sollen statistische Daten im Web veröffentlicht werden? (1=ja,2=nein)
set allow_web 1

#Sollen Zeitrekorde gespeichert werden? (1=ja,2=nein)
set allow_rec 1

#Soll der Bot jeden Tag einmal neu starten? (1=ja,2=nein)
set daily_restart 0

########################
###  typ's mod info  ###
########################

# 1. Schätzfragen
#  Schätzfragen müssen als Kategorie "Category: Schätzfragen" haben, ansonsten werden sie wie normale Quizfragen behandelt.
#  Die Antworten dürfen lediglich aus arabischen Zahlen (ohne Trennpunkte) sein.

# 2. Ratefragen
#  Ratefragen müssen als Kategorie "Category: Ratefragen" haben, ansonsten werden sie wie normale Quizfragen behandelt.
#  Die Antworten müssen folgendes Format haben: Antwort1*Antwort2*Antwort3
#  Das Ratefragen-Script basiert auf dem KAOS-Script von Mark A. Day.

# 3. A/M
#  A/M steht für Anschläge pro Minute

# 4. Web-Statistiken
#  Soweit von Dir erlaubt, sendet der QuizBot informationen an http://moxquiz.bplaced.net/homepage/

# 5. Support
#  Support gibt es im MoxQuizz-Forum unter http://www.moxquizz.de/forum oder im #quiz.de @ irc.gamesurge.net


########################
###   new commands   ###
########################

#!vok - Zeigt die Antwort ohne Vokale
#!halt - Hält den Bot an

#!alltimes - Zeigt die ewige Rangliste an

#!server - Zeigt den Server, zu dem der Bot verbunden ist
#!time - Zeigt Deine Spieldauer

#!rehash - OPs only
#!restart - OPs only


###########################################################################
###########################################################################
###########################################################################


## Version:
set version_moxquizz "0.8.1 {type:mod von http://moxquiz.bplaced.net/homepage/}"

package require msgcat
package require http

namespace import -force msgcat::*

###########################################################################
##
## ATTENTION:
##
## Defaults for bot configuration.  Don't edit here, edit the file
## moxquizz.rc instead!
##
###########################################################################


# system stuff
variable quizbasedir        moxquizz
variable datadir            $quizbasedir/quizdata
variable configfile         $quizbasedir/moxquizz.rc
variable intldir            $quizbasedir/intl

variable rankfile           $datadir/rank.data
variable allstarsfile       $datadir/rankallstars.data
variable statsfile          $datadir/stats.data
variable userqfile          $datadir/questions.user.new
variable commentsfile       $datadir/comments.txt

variable quizhelp

# these will be searched in $intldir/$quizconf(language)
variable channeltipfile     channeltips.txt
variable channelrulesfile   channelrules.txt
variable pricesfile         prices.txt
variable helpfile           help.txt


#
# Configuration map
#
variable quizconf

set quizconf(quizchannel)        "#quiz"
set quizconf(quizloglevel)       1

# several global numbers
set quizconf(maxranklines)       100
set quizconf(tipcycle)           5
set quizconf(useractivetime)     240
set quizconf(userqbufferlength)  20
set quizconf(winscore)           30
set quizconf(overrunlimit)       29

# timer delays in seconds
set quizconf(askdelay)           12
set quizconf(tipdelay)           14

# safety features and other configs
set quizconf(lastwinner_restriction)  no
set quizconf(lastwinner_max_games)    4
set quizconf(overrun_protection)      no
set quizconf(colorize)                yes
set quizconf(monthly_allstars)        no
set quizconf(channeltips)             yes
set quizconf(pausemoderated)          no
set quizconf(userquestions)           yes
set quizconf(msgwhisper)              no
set quizconf(channelrules)            yes
set quizconf(prices)                  yes
set quizconf(stripumlauts)            no
set quizconf(statslog)                no
set quizconf(aftergameaction)         newgame

set quizconf(language)                de


##
###########################################################################

##
## stuff for the game state
##

#typ
set ctcp-version "MoxQuizz für #Quiz.de @ irc.GameSurge.net"
variable answerarray
variable schaetzarray

variable schaetzend ""
variable schaetzwert ""
variable timetypasked [clock clicks -milliseconds]

variable subexp ""
variable bestscore 0

# values = stopped, paused, asked, waittoask, halted
variable quizstate "halted"
variable statepaused ""
variable statemoderated ""
variable usergame 0
variable timeasked [unixtime]
variable revoltmax 0
# values = newgame, stop, halt, exit
variable aftergame $quizconf(aftergameaction)
variable channeltips ""
variable channelrules ""
variable prices ""

#
# variables for the ranks and user handling
#
variable timerankreset [unixtime]
variable userlist
variable allstarsarray
variable revoltlist ""
variable lastsolver ""
variable lastsolvercount 0
variable lastwinner ""
variable lastwinnercount 0
variable allstars_starttime 0
variable ignore_for_userquest ""

#
# stuff for the question
#
variable tiplist ""
variable theq
variable qnumber 0
variable qnum_thisgame 0
variable userqnumber 0
variable tipno 0
variable qlist ""
variable qlistorder ""
variable userqlist ""

#
# doesn't fit elsewhere
#
variable whisperprefix "NOTICE"
variable statsfilefd "closed"

##################################################
## bindings

# bot running status
bind dcc P !init moxquiz_init
bind dcc P !stop moxquiz_stop
bind dcc P !halt moxquiz_halt
bind dcc P !pause moxquiz_pause
bind dcc P !cont moxquiz_cont
bind dcc P !reset moxquiz_reset
bind dcc m !exit moxquiz_exit
bind dcc m !aftergame moxquiz_aftergame

# bot speaking and hopping stuff
bind dcc P !say moxquiz_say
bind dcc P !act moxquiz_action
bind dcc m !allsay moxquiz_say_everywhere
bind dcc m !allact moxquiz_action_everywhere
bind dcc Q !join moxquiz_join
bind dcc Q !part moxquiz_part
bind dcc Q !quizto moxquiz_quizto
bind dcc Q !quizleave moxquiz_quizleave

# commands for the questions
bind dcc P !solve moxquiz_solve
bind dcc P !tip moxquiz_tip
bind dcc P !skipuserquest moxquiz_skipuserquest
bind dcc Q !setscore moxquiz_set_score

bind dcc m !qsave moxquiz_saveuserquests
bind dcc Q !reload moxquiz_reload


# status and configuration
bind dcc P !status moxquiz_status
bind dcc m !load moxquiz_config_load
bind dcc m !save moxquiz_config_save
bind dcc m !set moxquiz_config_set

# userquest and other user (public) commands
bind pubm - * moxquiz_pubm
bind pub - !ask moxquiz_ask
bind pub - .ask moxquiz_ask
bind pub - !quiz moxquiz_ask
bind pub - .quiz moxquiz_ask
bind pub - !start moxquiz_ask
bind pub - .start moxquiz_ask

bind pub - !revolt moxquiz_user_revolt
bind pub - !end moxquiz_user_revolt

bind msg - !userquest moxquiz_userquest
bind msg - !usercancel moxquiz_usercancel
bind msg - !usertip moxquiz_usertip
bind msg - !usersolve moxquiz_usersolve
bind pub P !nuq moxquiz_userquest_ignore
bind pub P !uq moxquiz_userquest_unignore
bind pub P !listnuq moxquiz_userquest_listignores
bind pub P !clearnuq moxquiz_userquest_clearignores

bind msg - !qhelp moxquiz_help
bind pub - !qhelp moxquiz_pub_help
bind pub - !score moxquiz_pub_score
bind pub - !rank moxquiz_pub_rank
bind pub - !allstars moxquiz_pub_allstars
bind pub - !comment moxquiz_pub_comment
bind pub - !fehler moxquiz_pub_comment
bind dcc - !comment moxquiz_dcc_comment
bind msg - !rules moxquiz_rules
bind pub - !rules moxquiz_pub_rules
bind msg - !version moxquiz_version
bind pub - !version moxquiz_pub_version

# mini funstuff
bind pub - !hi moxquiz_pub_hi
#bind ctcp - action moxquiz_purr

# commands to manage players and rank
bind dcc P !allstars moxquiz_allstars
bind dcc m !allstarssend moxquiz_allstars_send
bind dcc m !allstarsload moxquiz_allstars_load
bind dcc P !rank moxquiz_rank
bind dcc Q !rankdelete moxquiz_rank_delete
bind dcc m !rankload moxquiz_rank_load
bind dcc m !ranksave moxquiz_rank_save
bind dcc Q !rankreset moxquiz_rank_reset
bind dcc Q !rankset moxquiz_rank_set


# Some events the bot reacts on
bind nick - * moxquiz_on_nickchanged
bind join - * moxquiz_on_joined
bind mode - "*m" moxquiz_on_moderated
bind evnt - prerehash mx_event
bind evnt - rehash mx_event

## DEBUG
bind dcc n !colors moxquiz_colors

###########################################################################
#
# bot running commands
#
###########################################################################

## reset game
proc moxquiz_reset {handle idx arg} {
    global quizstate
    moxquiz_stop $handle $idx $arg
    moxquiz_rank_reset $handle $idx $arg
    moxquiz_init $handle $idx $arg
}

## initialize
proc moxquiz_init {handle idx arg} {
    global qlist version_moxquizz banner bannerspace quizstate
    global quizconf aftergame

    set quizstate "halted"
    set aftergame $quizconf(aftergameaction)
    if {$quizconf(quizchannel) != ""} {
        mxirc_say $quizconf(quizchannel) [mc "%sHello!  I am a MoxQuizz version %s and ready to squeeze your brain!" "[banner] [botcolor txt]" "[col bold]$version_moxquizz[col bold][botcolor txt]"]
        mxirc_say $quizconf(quizchannel) [mc "%s%d questions in database, just %s!ask%s.  Report bugs and suggestions to [email protected]" "[bannerspace] [botcolor txt]" [llength $qlist]  [col bold] "[col bold][botcolor txt]"]
        mx_log "--- Game initialized"
    } else {
        mxirc_dcc $idx "ERROR: quizchannel is set to an empty string, use .!quizto to set one."
    }
    return 1
}

## stop
## stop everything and kill all timers
proc moxquiz_stop {handle idx arg} {
    global quizstate banner bannerspace
    global quizconf
    variable t
    variable prefix [banner]


    ## called directly?
    if {[info level] != 1} {
	set prefix [bannerspace]
    } else {
	set prefix [banner]
    }

    set quizstate "stopped"

    ## kill timers
    foreach t [utimers] {
	if {[lindex $t 1] == "mx_timer_ask" || [lindex $t 1] == "mx_timer_tip"} {
	    killutimer [lindex $t 2]
	}
    }

    mx_log "--- Game stopped."
    mxirc_say $quizconf(quizchannel) [mc "%s %sQuiz stopped." $prefix [botcolor boldtxt]]
    return 1
}


## halt
## halt everything and kill all timers
proc moxquiz_halt {handle idx arg} {
    global quizstate banner bannerspace
    global quizconf

    variable t
    variable prefix [banner]

    ## called directly?
    if {[info level] != 1} {
	set prefix [bannerspace]
    } else {
	set prefix [banner]
    }

    set quizstate "halted"

    ## kill timers
    foreach t [utimers] {
	if {[lindex $t 1] == "mx_timer_ask" || [lindex $t 1] == "mx_timer_tip"} {
	    killutimer [lindex $t 2]
	}
    }

    mx_log "--- Game halted."
    mxirc_say $quizconf(quizchannel) [mc "%s %sQuiz halted.  Say !ask for new questions." $prefix [botcolor boldtxt]]
    utimer 5 mx_restart
    return 1
}


## reload questions
proc moxquiz_reload {handle idx arg} {
    global qlist quizconf
    global datadir

    variable alist ""
    variable banks
    variable suffix

    set arg [string trim $arg]
    if {$arg == ""} {
	# get question files
	set alist [glob -nocomplain "$datadir/questions.*"]

	# get suffixes
	foreach file $alist {
	    regexp "^.*\\.(\[^\\.\]+)$" $file foo suffix
	    set banks($suffix) 1
	}

	# report them
	mxirc_dcc $idx "There are the following question banks available (current: $quizconf(questionset)): [lsort [array names banks]]"
    } else {
	if {[mx_read_questions $arg] != 0} {
	    mxirc_dcc $idx "There was an error reading files for $arg."
	    mxirc_dcc $idx "There are [llength $qlist] questions available."
	} else {
	    mxirc_dcc $idx "Reloaded database, [llength $qlist] questions."
	    set quizconf(questionset) $arg
	}
    }

    return 1
}

## pause
proc moxquiz_pause {handle idx arg} {
    global quizstate statepaused banner timeasked
    global quizconf

    variable qwasopen "."

    if {[regexp "(halted|paused)" $quizstate]} {
	mxirc_dcc $idx "Quiz state is $quizstate.  Command ignored."
    } else {
	if {$quizstate == "asked"} {
	    foreach t [utimers] {
		if {[lindex $t 1] == "mx_timer_tip"} {
		    killutimer [lindex $t 2]
		}
	    }
	    set qwasopen [mc " after %s." [mx_duration $timeasked]]
	} elseif {$quizstate == "waittoask"} {
	    foreach t [utimers] {
		if {[lindex $t 1] == "mx_timer_ask"} {
		    killutimer [lindex $t 2]
		}
	    }
	}
	set statepaused $quizstate
	set quizstate "paused"
	mx_log "--- Game paused$qwasopen  Quiz state was: $statepaused"
	mxirc_say $quizconf(quizchannel) [mc "%sQuiz paused%s" "[banner] [botcolor boldtxt]" $qwasopen]
    }
    return 1
}


## continue
proc moxquiz_cont {handle idx arg} {
    global quizstate banner bannerspace timeasked
    global theq statepaused usergame statemoderated
    global quizconf

    if {$quizstate != "paused"} {
	mxirc_dcc $idx "Game not paused, command ignored."
    } else {
	if {$statepaused == "asked"} {
	    if {$usergame == 1} {
		set txt [mc "%sQuiz continued.  The user question open since %s, worth %d point(s):" "[banner] [botcolor boldtxt]" [mx_duration $timeasked] $theq(Score)]
	    } else {
		set txt [mc "%sQuiz continued.  The question open since %s, worth %d point(s):" "[banner] [botcolor boldtxt]" [mx_duration $timeasked] $theq(Score)]
            }
	    mxirc_say $quizconf(quizchannel) $txt
	    mxirc_say $quizconf(quizchannel) "[bannerspace] [botcolor question]$theq(Question)"
	    utimer $quizconf(tipdelay) mx_timer_tip
	} else {
	    mxirc_say $quizconf(quizchannel) [mc "%sQuiz continued.  Since there is no open question, a new one will be asked." "[banner] [botcolor boldtxt]"]
	    utimer 3 mx_timer_ask
	}
	set quizstate $statepaused
	set statepaused ""
	set statemoderated ""
	mx_log "--- Game continued."
    }
    return 1
}

## show module status
proc moxquiz_status {handle idx arg} {
    global quizstate statepaused qlist banner version_moxquizz userlist
    global timeasked uptime
    global usergame userqlist theq qnumber
    global qnum_thisgame aftergame

    global quizconf

    variable askleft 0 rankleft 0 tipleft 0
    variable txt
    variable chansjoined ""

    ## banner and where I am
    set txt "I am [mx_strip_colors [banner]] version $version_moxquizz, up for [mx_duration $uptime]"
    if {$quizconf(quizchannel) == ""} {
	set txt "$txt, not quizzing on any channel."
    } else {
	set txt "$txt, quizzing on channel \"$quizconf(quizchannel)\"."
    }
    mxirc_dcc $idx $txt

    mxirc_dcc $idx "I know the channels [channels]."

    foreach chan [channels] {
	if {[botonchan $chan]} {
	    set chansjoined "$chansjoined $chan"
	}
    }
    if {$chansjoined == ""} {
	set chansjoined " none"
    }
    mxirc_dcc $idx "I currently joined:$chansjoined."

    if {$quizstate == "asked" || $statepaused == "asked"} {
	## Game running?  User game?
	set txt "There is a"
	if {$usergame == 1} {
	    set txt "$txt user"
	}
	set txt "$txt game running."
	if {[mx_userquests_available]} {
	    set txt "$txt  [mx_userquests_available] user quests scheduled."
	} else {
	    set txt "$txt  No user quest is scheduled."
	}
	set txt "$txt  Quiz state is: $quizstate."
	mxirc_dcc $idx $txt

	## Open question?  Quiz state?
	set txt "The"
	if {[info exists theq(Level)]} {
	    set txt "$txt level $theq(Level)"
	}
	set txt "$txt question no. $qnum_thisgame is:"
	if {[info exists theq(Category)]} {
	    set txt "$txt ($theq(Category))"
	}
	mxirc_dcc $idx "$txt \"$theq(Question)\" open for [mx_duration $timeasked], worth $theq(Score) points."
    } else {
	## no open question, no game running
	set txt "There is no question open."
	set txt "$txt  Quiz state is: $quizstate."
	if {[mx_userquests_available]} {
	    set txt "$txt  [mx_userquests_available] user quests scheduled."
	} else {
	    set txt "$txt  No user quest is scheduled."
	}
	mxirc_dcc $idx $txt
    }

    mxirc_dcc $idx "Action after game won: $aftergame"

    foreach t [utimers] {
	if {[lindex $t 1] == "mx_timer_ask"} {
	    set askleft [lindex $t 0]
	}
	if {[lindex $t 1] == "mx_timer_tip"} {
	    set tipleft [lindex $t 0]
	}
    }

    mxirc_dcc $idx "Tipdelay: $quizconf(tipdelay) ($tipleft)  Askdelay: $quizconf(askdelay) ($askleft) Tipcycle: $quizconf(tipcycle)"
    mxirc_dcc $idx "I know about [llength $qlist] normal and [llength $userqlist] user questions.  Question number is $qnumber."
    mxirc_dcc $idx "There are [llength [array names userlist]] known people, winscore is $quizconf(winscore)."
    mxirc_dcc $idx "Game row restriction: $quizconf(lastwinner_restriction), row length: $quizconf(lastwinner_max_games)"
    return 1
}


## exit -- finish da thing and logoff
proc moxquiz_exit {handle idx arg} {
    global rankfile uptime botnick statsfilefd
    global quizconf
    mx_log "--- EXIT requested."
    mxirc_say $quizconf(quizchannel) [mc "%sI am leaving now, after running for %s." "[banner] [botcolor boldtxt]" [mx_duration $uptime]]
    if {$arg != ""} {
	mxirc_say $quizconf(quizchannel) "[bannerspace] $arg"
    }
    # moxquiz_quizleave $handle $idx $arg
    moxquiz_rank_save $handle $idx {}
    moxquiz_saveuserquests $handle $idx "all"
    moxquiz_config_save $handle $idx {}
    if {$statsfilefd != "closed"} { close $statsfilefd }
    mxirc_dcc $idx "$botnick now exits."
    mx_log "--- $botnick exited"
    mx_log "**********************************************************************"

    utimer 10 die
}

## aftergame -- what to do if the game is over (won or desert detection)
proc moxquiz_aftergame {handle idx arg} {
    global aftergame quizstate

    variable thisnext "this"

    if {$quizstate == "stopped" || $quizstate == "halted"} {
	set thisnext "next"
    }

    if {$arg == ""} {
	mxirc_dcc $idx "After $thisnext game I am planning to: \"$aftergame\"."
	mxirc_dcc $idx "Possible values are: exit, halt, stop, newgame."
    } else {
	switch -regexp $arg {
	    "(exit|halt|stop|newgame)" {
		set aftergame $arg
		mxirc_dcc $idx "After $thisnext game I now will: \"$aftergame\"."
	    }
	    default {
		mxirc_dcc $idx "Invalid action.  Chosse from: exit, halt, stop and newgame."
	    }
	}
    }
    return 1
}

####################
# bot control stuff
####################

## echo a text send by /msg
proc moxquiz_say {handle idx arg} {
    global funstuff_enabled botnick
    global quizconf

    variable channel

    set arg [string trim $arg]
    if {[regexp -nocase "^(#\[^ \]+)( +.*)?" $arg foo channel arg]} {
	## check if on channel $channel
	if {[validchan $channel] && ![botonchan $channel]} {
	    mxirc_dcc $idx "Sorry, I'm not on channel \"$channel\"."
	    return
	}
    } else {
	set channel $quizconf(quizchannel)
    }
    set arg [string trim $arg]

    mxirc_say $channel "$arg"
    variable unused "" cmd "" rest ""

    # if it was a fun command, execute it with some delay
    if {$funstuff_enabled &&
    [regexp "^(!\[^ \]+)(( *)(.*))?" $arg unused cmd waste spaces rest] &&
    [llength [bind pub - $cmd]] != 0} {
	if {$rest == ""} {
	    set rest "{}"
	} else {
            set rest "{$rest}"
        }
	eval "[bind pub - $cmd] {$botnick} {} {} {$channel} $rest"
    }
}


## say something on al channels
proc moxquiz_say_everywhere {handle idx arg} {
    if {$arg != ""} {
	mxirc_say_everywhere $arg
    } else {
	mxirc_dcc $idx "What shall I say on every channel?"
    }
}


## act as sent by /msg
proc moxquiz_action {handle idx arg} {
    global quizconf
    variable channel

    set arg [string trim $arg]
    if {[regexp -nocase "^(#\[^ \]+)( +.*)?" $arg foo channel arg]} {
	## check if on channel $channel
	if {[validchan $channel] && ![botonchan $channel]} {
	    mxirc_dc $idx "Sorry, I'm not on channel \"$channel\"."
	    return
	}
    } else {
	set channel $quizconf(quizchannel)
    }
    set arg [string trim $arg]

    mxirc_action $channel "$arg"
}


## say something on al channels
proc moxquiz_action_everywhere {handle idx arg} {
    if {$arg != ""} {
	mxirc_action_everywhere $arg
    } else {
	mxirc_dcc $idx "What shall act like on every channel?"
    }
}


## hop to another channel
proc moxquiz_join {handle idx arg} {
    global quizconf
    if {[regexp -nocase "^(#\[^ \]+)( +.*)?" $arg foo channel arg]} {
	set channel [string tolower $channel]
	if {[validchan $channel] && [botonchan $channel]} {
	    set txt "I am already on $channel."
	    if {$channel == $quizconf(quizchannel)} {
		set txt "$txt  It's the quizchannel."
	    }
	    mxirc_dcc $idx $txt
	} else {
	    channel add $channel
	    channel set $channel -inactive
	    mxirc_dcc $idx "Joined channel $channel."
	}
    } else {
	mxirc_dcc $idx "Please specify channel I shall join. \"$arg\" not recognized."
    }
}


## part an channel
proc moxquiz_part {handle idx arg} {
    global quizconf
    if {[regexp -nocase "^(#\[^ \]+)( +.*)?" $arg foo channel arg]} {
	set channel [string tolower $channel]
	if {[validchan $channel]} {
	    if {$channel == $quizconf(quizchannel)} {
		mxirc_dcc $idx "Cannot leave quizchannel via part.  User !quizleave or !quizto to do this."
	    } else {
		channel set $channel +inactive
		mxirc_dcc $idx "Left channel $channel."
	    }
	} else {
	    mxirc_dcc $idx "I am not on $channel."
	}
    } else {
	mxirc_dcc $idx "Please specify channel I shall part. \"$arg\" not recognized."
    }
}

## quiz to another channel
proc moxquiz_quizto {handle idx arg} {
    global quizconf
    if {[regexp "^#.*" $arg] == 0} {
	mxirc_dcc $idx "$arg not a valid channel."
    } else {
	if {$quizconf(quizchannel) != ""} {
	    # channel set $quizconf(quizchannel) +inactive
	    mxirc_say $quizconf(quizchannel) [mc "Quiz is leaving to %s.  Goodbye!" $arg]
	}
	set quizconf(quizchannel) [string tolower $arg]
	channel add $quizconf(quizchannel)
	channel set $quizconf(quizchannel) -inactive
	mxirc_say $quizconf(quizchannel) [mc "Quiz is now on this channel.  Hello!"]
	mxirc_dcc $idx "quiz to channel $quizconf(quizchannel)."
	mx_log "--- quizto channel $quizconf(quizchannel)"
    }
    return 1
}


## quiz leave a channel
proc moxquiz_quizleave {handle idx arg} {
    global quizconf banner
    if {$arg == ""} {
	mxirc_say $quizconf(quizchannel) [mc "%s Goodbye." [banner]]
    } else {
	mxirc_say $quizconf(quizchannel) "[banner] $arg"
    }
    if {$quizconf(quizchannel) != ""} {
	channel set $quizconf(quizchannel) +inactive
	mx_log "--- quizleave channel $quizconf(quizchannel)"
	mxirc_dcc $idx "quiz left channel $quizconf(quizchannel)"
	set quizconf(quizchannel) ""
    } else {
	mxirc_dcc $idx "I'm not quizzing on any channel."
    }
    return 1
}


###########################################################################
#
# commands for the questions
#
###########################################################################

## something was said. Solution?
proc moxquiz_pubm {nick host handle channel text} {
    global quizstate banner bannerspace
    global timeasked theq aftergame
    global usergame revoltlist
    global lastsolver lastsolvercount
    global lastwinner lastwinnercount
    global botnick bestscore
    global userlist channeltips prices
    global quizconf

    variable bestscore 0 lastbestscore 0 lastbest ""
    variable userarray
    variable authorsolved 0 waitforrank 0 gameend 0

    ## only accept chatter on quizchannel
    if {![mx_str_ieq $channel $quizconf(quizchannel)]} {
	return
    }

    ## record that the $nick spoke and create entries for unknown people
    mx_getcreate_userentry $nick $host
    array set userarray $userlist($nick)
    set hostmask $userarray(mask)

    ## not in asking state?
    if {$quizstate != "asked"} {
	return
    }

    # nick has revolted
    #if {[lsearch -exact $revoltlist $hostmask] != -1} {
    #	return
    #}

    # tweak umlauts in input
    set text [mx_tweak_umlauts $text]


    global answerarray
    set regohneleerzeichen $theq(Regexp)
    regsub -all " " $regohneleerzeichen "" regohneleerzeichen

    if {[regexp -nocase -- $theq(Regexp) $text] || [regexp -nocase -- $regohneleerzeichen $text]} {


        foreach name [array names answerarray] {
            unset answerarray($name)
	}


        ## ignore games_max in a row winner
        if {[mx_str_ieq [maskhost $host] $lastwinner]
        && $lastwinnercount >= $quizconf(lastwinner_max_games)
        && $quizconf(lastwinner_restriction) == "yes"} {
            mxirc_notc $nick [mc "No, you've won the last %d games." $quizconf(lastwinner_max_games)]
            return
        }

        # nick is author of userquest
        #if {([info exists theq(Author)] && [mx_str_ieq $nick $theq(Author)])
        #|| ([info exists theq(Hostmask)] && [mx_str_ieq [maskhost $host] $theq(Hostmask)])} {
        #    set authorsolved 1
        #}

        ## return if overrunprotection set and limit reached
        #if {$quizconf(overrun_protection) == "yes"
        #    && $userarray(score) == 0
        #    && [mx_users_in_rank] > 2
        #    && [mx_overrun_limit_reached]} {
        #    # [pending] TRANSLATE!
        #    mxirc_notc $nick [mc "Sorry, overrun protection enabled.  Wait till end of game."]
        #    return
        #}

	## reset quiz state related stuff (and save userquestions)
	mx_answered
	set duration [mx_duration $timeasked]
        
        set recdiff ""

        global timetypasked schaetzend allow_rec

        if {$schaetzend != "" && $schaetzend > 0} {
           set typdur $schaetzend
           set schaetzend ""
        } else {
          set typdur [expr ([clock clicks -milliseconds] - $timetypasked) * 0.001]

          if {$typdur < "10.000" &&$allow_rec} {

              set record [mx_read_record $text]

              if {$record == ""} {
                 mx_save_record $nick $typdur $text
              } else {
                 set recholder [lindex [split $record] 0]
                 set rectime [lindex [split $record] 1]

                 if {$rectime > $typdur} {
                    mx_save_record $nick $typdur $text
                 }

                 set recdiff [expr $typdur - $rectime]
                 if {$recdiff > 0} {set recdiff "+${recdiff}"} else {set recdiff "\002$recdiff\002"}
                 set recdiff "\(${recdiff} Sek. zu [mx_nickobf $recholder]\) "
              }
          }
        }




	# if it wasn't the author
	if {!$authorsolved} {
	    ## save last top score for the test if reset is near (later below)
	    set lastbest [lindex [lsort -command mx_sortrank [array names userlist]] 0]
	    if {$lastbest == ""} {
		set lastbestscore 0
	    } else {
		array set aa $userlist($lastbest)
		set lastbestscore $aa(score)
	    }

	    ## record nick for bonus points
	    if {[mx_str_ieq [maskhost $host] $lastsolver]} {
		incr lastsolvercount
	    } else {
		set lastsolver [maskhost $host]
		set lastsolvercount 1
	    }

	    ## save score (set started time to time of first point)
	    incr userarray(score) $theq(Score)
	    if {$userarray(score) == 1} {
		set userarray(started) [unixtime]
	    }
	    set userlist($nick) [array get userarray]

	    ## tell channel, that the question is solved
	    mx_log "--- solved after $duration by $nick with \"$text\", now $userarray(score) points"
            

            set daten [mx_incr_qnr]

            

            global vokspamvar hintmax hintlist
            set vokspamvar 0
            set hintmax 0
            set hintlist ""


            mx_statslog "solved" [list [unixtime] $nick $duration $userarray(score) $theq(Score)]

	    #mxirc_say $channel [mc "%s solved after %s and now has %s<%d>%s points (+%d) on rank %d." "[banner] [botcolor nick]$nick[botcolor txt]" $duration [botcolor nick] $userarray(score) [botcolor txt] $theq(Score) [mx_get_rank_pos $nick]]

            set answrnr [mx_answ_user $nick 1]
            set total [regexp -all -nocase {[A-Z]|[0-9]|[ ]} $text]
            
            #bugfix:
            if {$typdur <= 0 || $typdur == ""} { set typdur $duration }

            set speed [expr $total / $typdur * 60]

            mxirc_rsay $channel [mc "%s löst nach %s Sek. ${recdiff}seine %d. Frage und hat %s<%d>%s Punkte (+%d), Platz 9,1~%d~1,11.%s %sA/M: %3.0f" "[banner] [botcolor nick]$nick[botcolor txt]" $typdur $answrnr [botcolor nick] $userarray(score) [botcolor txt] $theq(Score) [mx_get_rank_pos $nick] [botcolor norm] [botcolor question] $speed]


            # remove area of tip generation tags
	    regsub -all "\#(\[^\#\]*\)\#" $theq(Answer) "\\1" answer


            global schaetzwert subexp
            if {$schaetzwert != ""} {
                set showanswer $answer
                set schaetzwert [commas $schaetzwert]
                
                set stotal [regexp -all -nocase {[A-Z]|[0-9]|[ ]} $subexp]

                if {$stotal > 4} {
                   regsub -all $subexp $showanswer [commas $subexp] showanswer
                }

                set showanswer "$showanswer \(Abweichung: ${schaetzwert}\)"
                set schaetzwert ""

            } else {
                set showanswer $answer
            }


	    mxirc_say $channel [mc "%sThe answer was:%s%s" "[bannerspace] [botcolor txt]" "[botcolor norm] [botcolor answer]" $showanswer]
	    ## honor good games!
	    if {$lastsolvercount == 3} {
		mxirc_say $channel [mc "%sThree in a row!" "[bannerspace] [botcolor txt]"]
		mx_log "--- $nick has three in a row."
                mx_statslog "tiar" [list [unixtime] $nick 3 0]
	    } elseif {$lastsolvercount == 5} {
		mxirc_say $channel [mc "%sCongratulation, five in a row! You receive an extra point." "[bannerspace] [botcolor txt]"]
		mx_log "--- $nick has five in a row.  score++"
                mx_statslog "tiar" [list [unixtime] $nick 5 1]
		moxquiz_rank_set $botnick 0 "$nick +1"
	    } elseif {$lastsolvercount == 10} {
		mxirc_say $channel [mc "%sTen in a row! This is really rare, so you get 3 extra points." "[bannerspace] [botcolor txt]"]
		mx_log "--- $nick has ten in a row.  score += 3"
                mx_statslog "tiar" [list [unixtime] $nick 10 3]
		moxquiz_rank_set $botnick 0 "$nick +3"
	    } elseif {$lastsolvercount == 20} {
		mxirc_say $channel [mc "%sTwenty in a row! This is extremely rare, so you get 5 extra points." "[bannerspace] [botcolor txt]"]
		mx_log "--- $nick has twenty in a row.  score += 5"
                mx_statslog "tiar" [list [unixtime] $nick 20 5]
		moxquiz_rank_set $botnick 0 "$nick +5"
	    }

	    ## rankreset, if above winscore
	    # notify if this comes near
	    set best [lindex [lsort -command mx_sortrank [array names userlist]] 0]
	    if {$best == ""} {
		set bestscore 0
	    } else {
		array set aa $userlist($best)
		set bestscore $aa(score)
	    }

	    set waitforrank 0
	    if {[mx_str_ieq $best $nick] && $bestscore > $lastbestscore} {
		array set aa $userlist($best)
		# tell the end is near
		if {$bestscore >= $quizconf(winscore)} {
                   
                    mx_web $daten $nick

                    set price "."

                    if {$quizconf(prices) == "yes"} {
                        set price " [lindex $prices [rand [llength $prices]]]"
                    }

		    mxirc_say $channel [mc "%s%s reaches %d points and wins%s" "[bannerspace] [botcolor txt]" $nick $quizconf(winscore) $price]
		    set now [unixtime]
		    if {[mx_str_ieq [maskhost $host] $lastwinner]} {
			incr lastwinnercount
			if {$lastwinnercount >= $quizconf(lastwinner_max_games)
			&& $quizconf(lastwinner_restriction) == "yes"} {
			    mxirc_say $channel [mc "%s: since you won %d games in a row, you will be ignored for the next game." $nick $quizconf(lastwinner_max_games)]
			}
		    } else {
			set lastwinner [maskhost $host]
			set lastwinnercount 1
		    }
		    # save $nick in allstars table
		    mx_saveallstar $now [expr $now - $aa(started)] $bestscore $nick [maskhost $host]
                    mx_statslog "gamewon" [list $now $nick $bestscore $quizconf(winscore) [expr $now - $aa(started)]]

                    moxquiz_alltimestars_load $botnick 0 $nick
		    #mxsave_alltimestars

                    moxquiz_rank $botnick 0 {}
		    moxquiz_rank_reset $botnick {} {}
		    set gameend 1
		    set waitforrank 15
		} elseif {$bestscore == [expr $quizconf(winscore) / 2]} {
		    mxirc_say $channel [mc "%sHalftime.  Game is won at %d points." \
                                        "[bannerspace] [botcolor txt]" $quizconf(winscore)]
		} elseif {$bestscore == [expr $quizconf(winscore) - 10]} {
		    mxirc_say $channel [mc "%s%s has 10 points to go." "[bannerspace] [botcolor txt]" $best]
		} elseif {$bestscore == [expr $quizconf(winscore) - 5]} {
		    mxirc_say $channel [mc "%s%s has 5 points to go." "[bannerspace] [botcolor txt]" $best]
		} elseif {$bestscore >= [expr $quizconf(winscore) - 3]} {
		    mxirc_say $channel [mc "%s%s has %d point(s) to go." \
                                        "[bannerspace] [botcolor txt]" $best [expr $quizconf(winscore) - $bestscore]]
		}
                
		# show rank at 1/3, 2/3 of and 5 before winscore
		set spitrank 1
		foreach third [list [expr $quizconf(winscore) / 3] [expr 2 * $quizconf(winscore) / 3] [expr $quizconf(winscore) - 5]] {
		    if {$lastbestscore < $third && $bestscore >= $third && $spitrank} {
			moxquiz_rank $botnick 0 {}
			set spitrank 0
			set waitforrank 15
		    }
		}

	    }
	} else {
	    ## tell channel, that the question is solved by author
	    mx_log "--- solved after $duration by $nick with \"$text\" by author"
	    mxirc_say $channel [mc "%s solved own question after %s and gets no points, keeping %s<%d>%s points on rank %d." \
                                "[banner] [botcolor nick]$nick[botcolor txt]" $duration [botcolor nick] $userarray(score) [botcolor txt] [mx_get_rank_pos $nick]]
	    # remove area of tip generation tags
	    regsub -all "\#(\[^\#\]*\)\#" $theq(Answer) "\\1" answer
	    mxirc_say $channel [mc "%sThe answer was:%s%s" "[bannerspace] [botcolor txt]" "[botcolor norm] [botcolor answer]" $answer]
	}

	## Give some occasional tips
	if {$quizconf(channeltips) == "yes" && [rand 30] == 0} {
	    mxirc_say $channel [mc "%sHint: %s" "[bannerspace] [botcolor txt]" [lindex $channeltips [rand [llength $channeltips]]]]
	}

	## check if game has ended and react
	if {!$gameend || $aftergame == "newgame"} {
	    # set up ask timer
	    utimer [expr $waitforrank + $quizconf(askdelay)] mx_timer_ask
	} else {
	    mx_aftergameaction
	}
    } elseif {[info exists theq(Category)]} {

        if {$theq(Category) == "Schätzfragen"} {
            

            set text [lindex [split $text] 0]

            if {![regexp "\[0-9\]+" $text]} { return }
            if {[regexp "\[A-Z\]" $text]} { return }
            if {[regexp "\[a-z\]" $text]} { return }

            if {[regexp -all -nocase {[A-Z]|[0-9]|[ ]} $text] > 12} {}


            global schaetzarray timetypasked subexp
            regsub -all \[{',.!}] $subexp "" subexp

            set entry schaetzarray($nick)

            set text [expr $text - $subexp]

            if {$text < 0} {
               set text [expr $text * -1]
            }


            set typdur [expr ([clock clicks -milliseconds] - $timetypasked) * 0.001]
            set schaetzarray($nick) [list $text $typdur]
        }

    } else {



      if {$text==""||$text==" "} { return }

      if {[info exists answerarray($nick)]} {

          set textzwei [lindex $answerarray($nick) 1]


          if {$textzwei==$text||$textzwei=="$text "} { return }


          regsub "\{" $textzwei "" textzwei
          regsub "\}" $textzwei "" textzwei

          regsub " " $textzwei "" textzwei


          if {$textzwei==$text||$textzwei=="$text "} { return }

          set textzwei "$textzwei $text"
          regsub "\{" $textzwei "" textzwei
          regsub "\}" $textzwei "" textzwei

          regsub " " $textzwei "" textzwei

          set entry $answerarray($nick)
			    set answerarray($nick) [list \
				    $nick \
				    $textzwei \
                  ]

          moxquiz_pubm $nick $host $handle $channel $textzwei


      } else {
			    set answerarray($nick) [list \
				    $nick \
				    $text \
                  ]
      }

    }
}


## Tool function to get the question introduction text
proc mx_get_qtext {complete} {
    global theq qnum_thisgame timeasked usergame qlist

    set qtext [list "Die Frage Nr. %d (aus [commas [llength $qlist]] Fragen) ist" \
               "The question no. %d is worth %d points" \
               "The question no. %d by %s is" \
               "The question no. %d by %s is worth %d points" \
               "The user question no. %d is" \
               "The user question no. %d is worth %d points" \
               "The user question no. %d by %s is" \
               "The user question no. %d by %s is worth %d points" \
               "The level %s question no. %d is" \
               "The level %s question no. %d is worth %d points" \
               "The level %s question no. %d by %s is" \
               "The level %s question no. %d by %s is worth %d points" \
               "The level %s user question no. %d is" \
               "The level %s user question no. %d is worth %d points" \
               "The level %s user question no. %d by %s is" \
               "The level %s user question no. %d by %s is worth %d points" ]


    ## game runs, tell user the question via msg
    set qtextnum 0
    set txt [list $qnum_thisgame]

    if {[info exists theq(Level)]} {
        incr qtextnum 8
        set txt [linsert $txt 0 $theq(Level)]
    }

    if {$usergame == 1} { incr qtextnum 4 }

    if {[info exists theq(Author)]} {
        incr qtextnum 2
        lappend txt [mx_nickobf $theq(Author)]
    }

    if {$theq(Score) > 1} {
        incr qtextnum 1
        lappend txt $theq(Score)
    }

    set txt [linsert $txt 0 mc [lindex $qtext $qtextnum]]
    set txt [eval $txt]

    if {$complete == "long"} {
        set txt [mc "%s, open for %s:" $txt [mx_duration $timeasked]]
        if {[info exists theq(Category)]} {
            set txt "$txt \($theq(Category)\)"
        }
        set txt "$txt $theq(Question)"
    } else {
        set txt "$txt:"
    }


    global datadir
    set fname "$datadir/qnr.data"

    if {[file exists $fname] && [file readable $fname]} {

       set fp [open $fname "r"]
       set daten [read -nonewline $fp]
       close $fp

    } else {
      
      set daten 0

      global statsfile
      set fp [open $statsfile "r"]
      set data [read $fp]
      close $fp

      foreach line [split $data "\n"] {

              if {[lindex [split $line] 0] == "solved"} {
                 incr daten
              }
      }

      set file [open $fname w]
      puts $file $daten
      close $file

    }

    #return [eval $txt]
    set pre "[banner] [botcolor boldtxt]"
    return "${pre}($daten.) $txt"
}



## ask a question, start game
proc moxquiz_ask {nick host handle channel arg} {
    global qlist quizstate botnick banner bannerspace
    global tipno tiplist
    global userqnumber usergame userqlist
    global timeasked qnumber theq
    global qnum_thisgame
    global userlist timerankreset
    global quizconf schaetztime subexp
    variable anum 0
    variable txt

    ## only accept chatter on quizchannel
    if {![mx_str_ieq $channel $quizconf(quizchannel)]} {
	return
    }

    switch -exact $quizstate {
	"paused" {
	    mxirc_notc $nick [mc "Game is paused."]
	    return 1
	}
	"stopped" {
	    mxirc_notc $nick [mc "Game is stopped."]
	    return 1
	}
    }

    ## record that $nick spoke (prevents desert detection from stopping,
    ## when an user joins and starts the game with !ask)
    if {![mx_str_ieq $nick $botnick]} {
	mx_getcreate_userentry $nick $host
    }

    ## any questions available?
    if {[llength $qlist] == 0 && [mx_userquests_available] == 0} {
	mxirc_say $channel [mc "%sSorry, my database is empty." "[banner] [botcolor boldtxt]"]
    } elseif {$quizstate == "asked"} {
        mxirc_notc $nick [mx_get_qtext "long"]
    } elseif {$quizstate == "waittoask" && ![mx_str_ieq $nick $botnick]} {
	## no, user has to be patient
	mxirc_notc $nick [mc "Please stand by, the next question comes in less than %d seconds." $quizconf(askdelay)]
    } else {
	##
	## ok, now lets see, which question to ask (normal or user)
	##

	## clear old question
	foreach k [array names theq] {
	    unset theq($k)
	}

	if {[mx_userquests_available]} {
	    ## select a user question
	    array set theq [lindex $userqlist $userqnumber]
            set usergame 1
	    incr userqnumber
	    mx_log "--- asking a user question: $theq(Question)"
	} else {
	    variable ok 0
	    
            global bestscore

            while {!$ok} {

                array set theq [lindex $qlist [mx_next_qnumber]]
                set usergame 0

		# skip question if author is about to win
		if {[info exists theq(Author)] && [info exists userlist($theq(Author))]} {
		    array set auser $userlist($theq(Author))
		    if {$auser(score) >= [expr $quizconf(winscore) - 5]} {
			mx_log "--- skipping question number $qnumber, author is about to win"
		    } else {
			mx_log "--- asking question number $qnumber: $theq(Question)"
			set ok 1
		    }
		} else {
		    mx_log "--- asking question number $qnumber: $theq(Question)"
		    set ok 1
		}


                if {[info exists theq(Category)]} {

                   if {$theq(Category) == "Ratefragen" && $bestscore > 24} {
                      set ok 0
                   } elseif {$theq(Category) == "Schätzfragen"} {

                      if {[mx_quizzer_count] < 3} {
		         set ok 0
                      }
                   }
                }

                if {!$ok} {
                   foreach k [array names theq] {
                           unset theq($k)
                   }
                }
                incr qnumber
	    }
	}
	incr qnum_thisgame
	if {$qnum_thisgame == 1} {
	    set timerankreset [unixtime]
	    mx_log "---- it's the no. $qnum_thisgame in this game, rank timer started at: [unixtime]"
            mx_statslog "gamestart" [list $timerankreset]
	} else {
	    mx_log "---- it's the no. $qnum_thisgame in this game."
	}

        if {[info exists theq(Category)]} {
            if {$theq(Category) == "Schätzfragen"} {
               global schaetzarray
               foreach name [array names schaetzarray] {
                       unset schaetzarray($name)
               }
               utimer $schaetztime mx_schaetz_end
            } elseif {$theq(Category) == "Ratefragen"} {
               Rate_Start $nick $host $handle $channel $arg
               return
            }
        }



	##
	## ok, set some minimal required fields like score, regexp and the tiplist.
	##

	## set regexp to match
	if {![info exists theq(Regexp)]} {
	    ## mask all regexp special chars except "."
	    set aexp [mx_tweak_umlauts $theq(Answer)]
	    regsub -all "(\\+|\\?|\\*|\\^|\\$|\\(|\\)|\\\[|\\\]|\\||\\\\)" $aexp "\\\\\\1" aexp
	    # get #...# area tags for tipgeneration as regexp
	    regsub -all ".*\#(\[^\#\]*\)\#.*" $aexp "\\1" aexp
	    set theq(Regexp) $aexp
	} else {
	    set theq(Regexp) [mx_tweak_umlauts $theq(Regexp)]
	}


        set subexp $theq(Answer)
	# protect embedded numbers
	if {[regexp "\[0-9\]+" $theq(Regexp)]} {
	    set newexp ""
	    set oldexp $theq(Regexp)
	    set theq(Oldexp) $oldexp

	    while {[regexp -indices "(\[0-9\]+)" $oldexp pair]} {
		set subexp [string range $oldexp [lindex $pair 0]  [lindex $pair 1]]
		set newexp "${newexp}[string range $oldexp -1 [expr [lindex $pair 0] - 1]]"
		if {[regexp -- $theq(Regexp) $subexp]} {
		    set newexp "${newexp}(^|\[^0-9\])${subexp}(\$|\[^0-9\])"
		} else {
		    set newexp "${newexp}${subexp}"
		}
		set oldexp "[string range $oldexp [expr [lindex $pair 1] + 1] [string length $oldexp]]"
	    }
	    set newexp "${newexp}${oldexp}"
	    set theq(Regexp) $newexp
	    #mx_log "---- replaced regexp '$theq(Oldexp)' with '$newexp' to protect numbers."
	}

	## set score
	if {![info exists theq(Score)]} {
	    set theq(Score) 1
	}

        ## obfs question (answer script stopper)
        # [pending] done elsewhere
        ##set theq(Question) [mx_obfs $theq(Question)]

	## set category
	## set anum [lsearch -exact $alist "Category"]
	## if {![info exists theq(Category)} {
	##    set theq(Category) "unknown"
	##}

	## initialize tiplist
	set anum 0
	set tiplist ""
	while {[info exists theq(Tip$anum)]} {
	    lappend tiplist $theq(Tip$anum)
	    incr anum
	}
	# No tips found?  construct standard list
	if {$anum == 0} {
	    set add "·"

	    # extract area of tip generation tags (side effect sets answer)
	    if {![regsub -all ".*\#(\[^\#\]*\)\#.*" $theq(Answer) "\\1" answer]} {
		set answer $theq(Answer)
	    }

	    ## use tipcycle from questions or
	    ## generate less tips if all words shorter than $tipcycle
	    if {[info exists theq(Tipcycle)]} {
		set limit $theq(Tipcycle)
	    } else {
		set limit $quizconf(tipcycle)
		## check if at least one word long enough
		set tmplist [lsort -command mx_cmp_length -decreasing [split $answer " "]]
		# not a big word
		if {[string length [lindex $tmplist 0]] < $quizconf(tipcycle)} {
		    set limit [string length [lindex $tmplist 0]]
		}
	    }

	    for {set anum 0} {$anum < $limit} {incr anum} {
		set tiptext ""
		set letterno 0
		for {set i 0} {$i < [string length $answer]} {incr i} {
		    if {([expr [expr $letterno - $anum] % $quizconf(tipcycle)] == 0) ||
		    ([regexp "\[- \.,`'\"\]" [string range $answer $i $i] foo])} {
			set tiptext "$tiptext[string range $answer $i $i]"
			if {[regexp "\[- \.,`'\"\]" [string range $answer $i $i] foo]} {
			    set letterno -1
			}
		    } else {
			set tiptext "$tiptext$add"
		    }
		    incr letterno
		}
		lappend tiplist $tiptext
	    }

            # reverse tips for numeric questions
            if {[regexp "^\[0-9\]+$" $answer]} {
                set foo ""
                for {set i [expr [llength $tiplist] - 1]} {$i >= 0} {set i [expr $i - 1]} {
                    lappend foo [lindex $tiplist $i]
                }
                set tiplist $foo
            }
	}

	##
	## Now print question header and question
	##

        mxirc_serv $channel [mx_get_qtext "short"]

	set txt "[bannerspace] [botcolor question]"
	if {[info exists theq(Category)]} {
	    set txt "$txt\($theq(Category)\) $theq(Question)"

        if {[info exists theq(Category)]} {
            if {$theq(Category) == "Schätzfragen"} {
               set txt "$txt \[\00306Ihr habt\00310 $schaetztime \00306Sekunden, um eine Schätzung in arabischen Zahlen abzugeben.\]\003"
            }
        }

	} else {
	    set txt "$txt$theq(Question)"
	}

	mxirc_serv $channel $txt

	set quizstate "asked"
	set tipno 0
	set timeasked [unixtime]
	global timetypasked
        set timetypasked [clock clicks -milliseconds]
	## set up tip timer
	utimer $quizconf(tipdelay) mx_timer_tip
    }
}





proc mx_quizzer_count {} {
global quizconf userlist

  set qnow [unixtime]
  set usermax 0

  foreach u [lsort -command mx_sortrank [array names userlist]] {

    array set aa $userlist($u)
    if {[expr $qnow - $aa(lastspoken)] <= $quizconf(useractivetime)} {
       incr usermax
    }
  }
  return $usermax
}



## A user dislikes the question
proc moxquiz_user_revolt {nick host handle channel text} {
    global revoltlist revoltmax tipno botnick quizstate
    global userlist RateRunning raterevolt
    global quizconf

    ## only accept revolts on the quizchannel
    if {![mx_str_ieq $channel $quizconf(quizchannel)]} {
	return
    }

    if {$quizstate == "asked" || $RateRunning == 1} {
	if {$tipno < 1&&$RateRunning!=1} {
	    mxirc_action $channel [mc "does not react on revolts before at least one tip was given."]
	    return
	}

	## ensure that the revolting user has an entry
	if {![info exists userlist($nick)]} {
	    mx_getcreate_userentry $nick $host
	}

	## calculate people needed to make a revolution (50% of active users)
	mx_log "--- a game runs, !revolt.  revoltmax = $revoltmax"
	if {$revoltmax == 0} {
	    set now [unixtime]
	    foreach u [array names userlist] {
		array set afoo $userlist($u)
		if {[expr $now - $afoo(lastspoken)] <= $quizconf(useractivetime)} {
		    incr revoltmax
		}
	    }
	    mx_log "---- active people are $revoltmax"
	    # one and two player shoud revolt "both"
	    if {$revoltmax > 2} {
		set revoltmax [expr int(ceil(double($revoltmax) / 2))]
	    }
	    mx_log "---- people needed for a successful revolution: $revoltmax"
	}

	# records known users dislike
	if {[info exists userlist($nick)]} {
	    array set anarray $userlist($nick)
	    set hostmask $anarray(mask)
	    if {[lsearch -exact $revoltlist $hostmask] == -1} {
		#mxirc_quick_notc $nick [mc "Since you are revolting, you will be ignored for this question."]
		
                if {[llength $revoltlist] < $revoltmax && $revoltmax > 0} {
                    mxirc_quick_action $channel [mc "sees that %s and %d other dislike the question, you need %d people." \
                                           $nick [llength $revoltlist] $revoltmax]
                }
                lappend revoltlist $hostmask
		set anarray(lastspoken) [unixtime]
		set userlist($nick) [array get anarray]
		mx_log "--- $nick is revolting, revoltmax is $revoltmax"
                mx_statslog "revolt" [list [unixtime] $nick [llength $revoltlist] $revoltmax]
	    }
	}
	if {[llength $revoltlist] >= $revoltmax} {
	    set revoltmax 0
	    mx_log "--- solution forced by revolting."
            mx_statslog "revoltsolve" [list [unixtime] $revoltmax]
	    #mxirc_action $channel [mc "will solve the question immediately."]
            
            if {$RateRunning != 0} {

                   foreach t [utimers] {
                           set tname [lindex $t 1]

                           if {[lindex $t 1] == "RateTimeUp"||[lindex $t 1] == "RateDisplayRemainingTime"||[lindex $t 1] == "Rate_ShowResults"||[regexp $tname "Rate"]} {
 	                      killutimer [lindex $t 2]
                           }
                   }
               
               RateTimesUp
               set revoltlist ""
               set revoltmax 0
               set vokspamvar 0
               set raterevolt 1
            } else {
              moxquiz_solve $botnick 0 {}
            }
	}
    }
}


## solve question
proc moxquiz_solve {handle idx arg} {
    global quizstate theq banner bannerspace
    global botnick lastsolvercount lastsolver timeasked
    global quizconf

    variable txt
    variable answer
    if {$quizstate != "asked"} {
	mxirc_dcc $idx "There is no open question."
    } else {
	mx_answered
	set lastsolver ""
	set lastsolvercount 0

	if {[mx_str_ieq $botnick $handle]} {
	    set txt [mc "%sAutomatically solved after %s." \
                     "[banner] [botcolor boldtxt]" [mx_duration $timeasked]]
	} else {
	    set txt [mc "%sManually solved after %s by %s" \
                     "[banner] [botcolor boldtxt]" [mx_duration $timeasked] $handle]
	}
	mxirc_serv $quizconf(quizchannel) $txt

	# remove area of tip generation tags
	regsub -all "\#(\[^\#\]*\)\#" $theq(Answer) "\\1" answer
	mxirc_say $quizconf(quizchannel) [mc "%sThe answer is:%s%s" \
                                          "[bannerspace] [botcolor txt]" "[botcolor norm] [botcolor answer]" $answer]

	# remove protection of numbers from regexp
	if {[info exists theq(Oldexp)]} {
	    set theexp $theq(Oldexp)
	} else {
	    set theexp $theq(Regexp)
	}

	#if {$answer != $theexp} {
	#    mxirc_say $quizconf(quizchannel) [mc "%sAnd should match:%s%s" \
        #                                      "[bannerspace] [botcolor txt]" "[botcolor norm] [botcolor answer]" $theexp]
	#}

	mx_log "--- solved by $handle manually."
	# schedule ask
	utimer $quizconf(askdelay) mx_timer_ask
    }
    return 1
}


## show a tip
proc moxquiz_tip {handle idx arg} {
    global tipno quizstate banner bannerspace
    global botnick tiplist
    global quizconf theq

        if {[info exists theq(Category)]} {
            if {$theq(Category) == "Schätzfragen"} {
               return
            }
        }


    if {$quizstate == "asked"} {
	if {$arg != ""} {
	    mxirc_dcc $idx "Extra tip \'$arg\' will be given."
	    set tiplist [linsert $tiplist $tipno $arg]
	}
	if {$tipno == [llength $tiplist]} {
	    # enough tips, solve!
	    set tipno 0
	    moxquiz_solve $botnick 0 {}
	} else {
	    set tiptext [lindex $tiplist $tipno]
	    mxirc_say $quizconf(quizchannel) [mc "%sHint %d:" "[banner] [botcolor boldtxt]" [expr $tipno + 1]]
	    mxirc_say $quizconf(quizchannel) "[bannerspace] [botcolor tip]$tiptext"
	    foreach j [utimers] {
		if {[lindex $j 1] == "mx_timer_tip"} {
		    killutimer [lindex $j 2]
		}
	    }
	    mx_log "----- Tip number $tipno: $tiptext"
	    # only short delay after last tip
	    incr tipno
	    if {$tipno == [llength $tiplist]} {
		utimer 15 mx_timer_tip
	    } else {
		utimer $quizconf(tipdelay) mx_timer_tip
	    }
	}
    } else {
	mxirc_dcc $idx "Sorry, no question is open."
    }
    return 1
}


## schedule a userquest
proc moxquiz_userquest {nick host handle arg} {
    global userqlist ignore_for_userquest
    global quizconf usebadwords badwords
    variable uanswer "" uquestion "" umatch ""
    variable tmptext ""

    if {$quizconf(userquestions) == "no"} {
	mxirc_notc $nick [mc "Sorry, userquestions are disabled."]
	return
    }

    if {[lsearch $ignore_for_userquest [string tolower $nick]] != -1 || [lsearch $ignore_for_userquest [maskhost $host]] != -1} {
        mxirc_notc $nick "Sorry, you are not allowed to post userquestions at this time."
        return
    }

    if {![onchan $nick $quizconf(quizchannel)]} {
	mxirc_notc $nick [mc "Sorry, you MUST be in the quizchannel to ask questions."]
    } else {
	if {[mx_userquests_available] >= $quizconf(userqbufferlength)} {
	    mxirc_notc $nick [mc "Sorry, there are already %d user questions scheduled.  Try again later." $quizconf(userqbufferlength)]
	} elseif {[info exists usebadwords] && $usebadwords && [regexp -nocase "([join $badwords, "|"])" $arg]} {
            mxirc_notc $nick [mc "Sorry, your userquest would trigger the badwords detection and will not be asked."]
        } else {
	    set arg [mx_strip_colors $arg]
            if {$quizconf(stripumlauts) == "yes"} {
                set arg [mx_tweak_umlauts $arg]
            }
	    if {[regexp "^(.+)::(.+)::(.+)$" $arg foo uquestion uanswer umatch] || \
		    [regexp "(.+)::(.+)" $arg foo uquestion uanswer]} {
		set uquestion [string trim $uquestion]
		set uanswer [string trim $uanswer]
		set alist [list "Question" "$uquestion" "Answer" "$uanswer" "Author" "$nick" "Date" [ctime [unixtime]] "TipGiven" "no"]
		if {$umatch != ""} {
		    set umatch [string trim $umatch]
		    lappend alist "Regexp" "$umatch"
		    set tmptext [mc " (regexp \"%s\")" $umatch]
		}
		lappend userqlist $alist

		mxirc_notc $nick [mc "Your quest \"%s\" is scheduled with answer \"%s\"%s and will be asked after %d questions." \
                                  $uquestion $uanswer $tmptext [expr [mx_userquests_available] - 1]]
		mx_log "--- Userquest scheduled by $nick: \"$uquestion\"."
	    } else {
		mxirc_notc $nick [mc "Wrong number of parameters.  Use alike <question>::<answer>::<regexp>.  The regexp is optional and used with care."]
		mxirc_notc $nick [mc "You said: \"%s\".  I recognize this as: \"%s\" and \"%s\", regexp: \"%s\"." \
                                  $arg $uquestion $uanswer $umatch]
		mx_log "--- userquest from $nick failed with: \"$arg\""
	    }
	}
    }
    return
}


## usertip
proc moxquiz_usertip {nick host handle arg} {
    global quizstate usergame theq
    global quizconf

    if {[onchan $nick $quizconf(quizchannel)]} {
	mx_log "--- Usertip requested by $nick: \"$arg\"."
	if {$quizstate == "asked" && $usergame == 1} {
	    if {[info exists theq(Author)] && ![mx_str_ieq $nick $theq(Author)]} {
		mxirc_notc $nick [mc "No, only %s can give tips here!" $theq(Author)]
	    } else {
		moxquiz_tip $nick 0 $arg
                if {$arg != ""} {
                    set theq(TipGiven) "yes"
                }
	    }
	} else {
	    mxirc_notc $nick [mc "No usergame running."]
	}
    } else {
	mxirc_notc $nick [mc "Sorry, you MUST be in the quizchannel to give tips."]
    }
    return 1
}


## usersolve
proc moxquiz_usersolve {nick host handle arg} {
    global quizstate usergame theq

    mx_log "--- Usersolve requested by $nick."
    if {$quizstate == "asked" && $usergame == 1} {
	if {[info exists theq(Author)] && ![mx_str_ieq $nick $theq(Author)]} {
	    mxirc_notc $nick [mc "No, only %s can solve this question!" $theq(Author)]
	} else {
	    moxquiz_solve $nick 0 {}
	}
    } else {
	mxirc_notc $nick [mc "No usergame running."]
    }
    return 1
}


bind msg - !adver mx_adver
## advertisement
proc mx_adver {nick host handle arg} {
    global botnick
    global quizconf

    mxirc_say $quizconf(quizchannel) "[banner] $botnick is a MoxQuizz © by Moxon <[email protected]>"
    mxirc_say $quizconf(quizchannel) "[bannerspace] and can be downloaded from http://meta-x.de/moxquizz"

    return 0
}


## usercancel
proc moxquiz_usercancel {nick host handle arg} {
    global quizstate usergame theq userqnumber userqlist
    mx_log "--- Usercancel requested by $nick."
    if {$quizstate == "asked" && $usergame == 1} {
	if {[info exists theq(Author)] && ![mx_str_ieq $nick $theq(Author)]} {
	    mxirc_notc $nick [mc "No, only %s can cancel this question!" $theq(Author)]
	} else {
	    mxirc_notc $nick [mc "Your question is canceled and will be solved."]
	    set theq(Comment) "canceled by user"
	    moxquiz_solve "user canceling" 0 {}
	}
    } elseif {[mx_userquests_available]} {
	array set aq [lindex $userqlist $userqnumber]
	if {[mx_str_ieq $aq(Author) $nick]} {
	    mxirc_notc $nick [mc "Your question \"%s\" will be skipped." $aq(Question)]
	    set aq(Comment) "canceled by user"
	    set userqlist [lreplace $userqlist $userqnumber $userqnumber [array get aq]]
	    incr userqnumber
	} else {
	    mxirc_notc $nick [mc "Sorry, the next question is by %s." $aq(Author)]
	}
    } else {
	mxirc_notc $nick [mc "No usergame running."]
    }
    return 1
}




## ignore nick!*@* and *!ident@*.subdomain.domain for userquests for 45 minutes
proc moxquiz_userquest_ignore {nick host handle channel arg} {
    global quizconf ignore_for_userquest

    regsub -all " +" [string trim $arg] " " arg

    variable nicks [split $arg]
    variable n

    for {set pos 0} {$pos < [llength $nicks]} {incr pos} {
        set n [string tolower [lindex $nicks $pos]]
        mxirc_notc $nick "Ignoring userquestions from $n for 45 minutes."
        lappend ignore_for_userquest $n
        if {[onchan $n $quizconf(quizchannel)]} {
            lappend ignore_for_userquest [maskhost [getchanhost $n $quizconf(quizchannel)]]
        } else {
            lappend ignore_for_userquest "-!-@-"
        }

    }
    timer 45 mx_timer_userquest_ignore_expire
    return 1
}

## unignore nick!*@* and *!ident@*.subdomain.domain for userquests for 45 minutes
proc moxquiz_userquest_unignore {nick host handle channel arg} {
    ## replace each [split $arg] and associated hostmask with some impossible values
    global quizconf ignore_for_userquest

    regsub -all " +" [string trim $arg] " " arg

    variable nicks [split $arg]
    variable n

    for {set pos 0} {$pos < [llength $nicks]} {incr pos} {
        set n [string tolower [lindex $nicks $pos]]
        set listpos [lsearch -exact $ignore_for_userquest $n]
        if {$listpos != -1} {
            set ignore_for_userquest [lreplace $ignore_for_userquest $listpos [expr $listpos + 1] "@" "-!-@-"]
            mxirc_notc $nick "User $n removed from ignore list for userquestions."
        } else {
            mxirc_notc $nick "User $n was not in the ignore list for userquestions."
        }
    }
    return 1
}

## lists current names on ignore for userquestions
proc moxquiz_userquest_clearignores {nick host handle channel arg} {
    variable ignore_for_userquest
    for {set pos 0} {$pos < [llength $ignore_for_userquest]} {incr pos 2} {
        set ignore_for_userquest [lreplace $ignore_for_userquest $pos [expr $pos + 1] "@" "-!-@-"]
    }
    mxirc_notc $nick "Cleared ignore list for userquestions."
    return 1
}

## lists current names on ignore for userquestions
proc moxquiz_userquest_listignores {nick host handle channel arg} {
    variable ignore_for_userquest
    variable nicks ""
    for {set pos 0} {$pos < [llength $ignore_for_userquest]} {incr pos 2} {
        if {[lindex $ignore_for_userquest $pos] != "@"} {
            lappend nicks [lindex $ignore_for_userquest $pos]
        }
    }
    if {$nicks != ""} {
        mxirc_notc $nick "Currently ignored for userquestions: $nicks"
    } else {
        mxirc_notc $nick "Currently ignoring nobody for userquestions."
    }
    return 1
}

## removes the first two elements from ignore_for_userquest (nick and mask)
proc mx_timer_userquest_ignore_expire {} {
    global ignore_for_userquest
    set ignore_for_userquest [lreplace $ignore_for_userquest 0 1]
}

## pubm !score to report scores
proc moxquiz_pub_score {nick host handle channel arg} {
    global alltimestarsarray allstarsarray userlist
    global quizconf

    variable allstarspos
    variable pos 0
    variable target
    variable self 0

    if {![mx_str_ieq $channel $quizconf(quizchannel)]} {
	return 0
    } else {
        set arg [string trim "$arg"]

        if {$arg != "" && $arg != $nick} {
            set target $arg
        } else {
            set target $nick
            set self 1
        }

	# report rank entries
	if {[info exists userlist($targ

Initial URL
http://www.moxquiz.de

Initial Description
here is a little update for the popular moxquizz script for eggdrop i wrote

you can download the script here: http://moxquiz.bplaced.net/homepage/?seite=download 

an also see a list of the new features there

Initial Title
moxquizz update (german)

Initial Tags

                                

Initial Language
TCL