###############################
##                           ##
##   qdb.org quotefetcher   ##
##        for eggdrops       ##
##---------------------------##
##       version: 1.4.7      ##
##      ( 25th Nov 2003 )    ##
##---------------------------##
##       by DJ Grenola       ##
##---------------------------##
##  suggestions & bugs to :  ##
##                           ##
##  qdb@alicampbell.org.uk  ##
##                           ##
## You can modify it, but if ##
## you don't credit me, I'll ##
## urinate in your shoes.    ##
##                           ##
###############################

## TBC
## ~~~
## - check through documentation
## - add op-only spit on/off command
## - seems to suffer from the usual eggdrop problem of
##      refusing to display two identical lines in the
##      same batch (try !qdb 99060 for the proof)


## VERSION HISTORY
##
## 1.4.7: (25th Nov 2003)
##       - ?? fixed bug where bot would fail to refill
##         qdb_cache if random public spit mode is activated ??
##       - script now works in channels whose names contain
##         [square brackets], and sending to nicks containing
##         [square brackets]. Thanks to CGlass for pointing
##         this bug out. No thanks to TCL and its cavalier
##         approach to string handling.

## 1.4.6: (9th Oct 2003)
##       - added option to allow bot to /notice excessively
##         long quotes to people rather than /msging them
##       - uncommented unset lines for the nick/HTTP token
##         and channel/HTTP token hashes
##       - updated documentation
##       - added ability to be able to automatically spit
##         out a random quote every so often
##       - modified random fetch procedure to filter out
##         long quotes before they are placed into the cache
##         (rather than their being filtered afterwards)
##       - fixed bug with quotes containing '[' or ']' not
##         being displayed

## 1.4.5: (14th Sept 2003)
##       - added config option to set the HTTP fetch type
##         (blocking / non-blocking [i.e. callback]) for
##         random, text search and number fetches.
##         Following a discussion on the egghelp forums
##         about different people's preferences for different
##         HTTP modes, I decided to make it possible for the
##         user to pick his or her own.

## 1.4.4: (10th Sept 2003)
##       - added qdb_enable_debug constant to enable logging
##         of the control flow into the eggdrop logs
##       - fixed bug with qdb_seconds_per_quote preventing
##         any quotes from being displayed ([clock clicks
##         -milliseconds] was returning apparently negative
##         numbers)

## 1.4.3: (not released)
##       - improved documentation
##       - fixed minor bugs in qdb_dot_org_fetch_random
##         and qdbCallback_number

## 1.4.2: (3rd September 2003)
##       - no longer requires a channel name and so will
##         now work out of the box across all channels the
##         bot is in.

## 1.4.1: (29th August 2003)
##       - 'fixed' problem with regsub for ampersand
##         substitution not working ('&' appearing as
##         '&amp;').

## 1.4: (29th August 2003)
##       - added new !qdb <text> command to search
##         for quotes containing text <text>
##       - added new !qdb_usage command to display the
##         script commands publically
##       - few minor crappy fixes
##       - removed all puts commands, as some eggdrops
##         didn't seem to like them (thanks to ^DooM^)
##       - floodprotection (^DooM^ again)
##       - configuration improvements
##       - fixed bug that sometimes caused last lines of
##         quotes to be missed out

## b0.3: (25th August 2003)
##       - added new !qdb <quotenumber> command to get a
##            specific numbered quote
##       - !qdb command now shows the quote numbers
##       - added private message mode for quotes with lots
##            of lines
##       - few minor crappy fixes

## b0.2: (5th August 2003)
##       - initial release version

## b0.1: (?)
##       - experimental, slow, and crap

# ---------------------------------------------
# *** READ THIS ***

# INSTALLATION

# i) Place this TCL into your eggdrop/scripts directory,
# ii) make the usual 'source scripts/qdb.org-1.4.7.tcl'
#     entry at the end of eggdrop.conf,
# iii) and rehash the bot as normal.

# CONFIGURATION

# The configuration section is below. Those who insist on
# tinkering will find another section beneath that defining
# various constants used by the script, including nearly all
# the outputted text, and the regexps used to strip the
# quotes from the HTML.
# Don't muck with this if you don't know what you're doing.

# COMMANDS

# The following commands are recognised by default:

# !qdb
#     - this displays a random quote from the qdb.org quote
#       database. The quote will always appear in a public
#       channel, because the script will ignore entries in
#       its random quote cache that have a length exceeding
#       qdb_max_public_line_length.
#	  If you didn't understand this, don't worry about it.

# !qdb <text>
#     - Needless to say, the < and > symbols should not be
#       typed in as part of the command.
#       (Unless you're searching for a nick, of course.)
#       Fetches the quote numbers of the first N quotes
#       (N being defined by qdb_max_search_results in the
#       config section below) that match text <text>.

# !qdb <number>
#     - Again, no < and > symbols, please.
#       Fetches a specific numbered quote from the QDB.
#       If it contains less lines than the
#       qdb_max_public_line_length setting,
#       the quote will be displayed publically. If not, it
#       will be privately /msged or /noticed to the use
#       who requested it.

# HOW IT WORKS

# If you want to get on and configure the damn thing, skip this
# section.

# The random quote function was the first written. It makes
# an HTTP query to the 'Random' link on the qdb.org front page.
# It receives the response and then uses various regexp filters
# to extract the quotes and place them in a TCL list (qdb_cache).
# Meanwhile the numbers of the quotes are stored in another list
# (qdb_number_list). The position of the next item that is
# to be displayed from these lists is marked by the
# qdb_cache_pointer_position variable. Requesting quotes is
# disabled when the cache is empty and, if not already running,
# an HTTP query is attempted to refill the cache. The cache will
# also be refilled when the pointer hits the end of the list.
# When a quote is requested, qdb_cache[qdb_cache_pointer_position]
# and qdb_number_list[qdb_cache_pointer_position] are read,
# the pointer is incremented, and the resulting texts are again
# regexp filtered by the qdb_output function for de-escaping of
# HTML entities (&nbsp; etc). If the length of the quote exceeds
# qdb_max_public_line_length, the pointer is repeatedly
# incremented until this condition is not true. If
# qdb_max_public_line_length is set low, only a few quotes will
# be displayed by this command per HTML query.
# The TCL commands to display the quote in the channel are placed
# on the event queue with the 'after' command and told to execute
# a certain time apart (this time being set by the
# qdb_seconds_per_line setting). 

# The !qdb <number> command works similarly to the random quote
# function and borrows a lot of code from it. This time, an HTTP 
# query is made to the appropriate qdb.org web page for a certain
# numbered quote. The returned HTML is stripped of its tags as
# before. If the quote is longer than qdb_max_public_line_length,
# it is privately messaged to the requesting nick. Otherwise,
# it is displayed in the channel.

# The !qdb <text> command is a little different to the other two.
# It makes a request using the search function of the qdb.org
# website (for a maximum number of quotes set by
# qdb_max_search_results), and parses out only the quote numbers
# of the returned HTML. These are always displayed publically.

# ---------------------------------------------
# *** CONFIGURATION SECTION ***
#
set qdb_command "!qdb"
#
# ^-- the public command to invoke the script
#
#     the suffices '_usage' and '_version', when appended onto the
#     end of this command, also form commands.
#     i.e. if qdb_command were, say, "!moose", then the commands
#     "!qdb_usage" and "!qdb_version"
#     would now become "!moose_usage" and "!moose_version".
#
#
set qdb_max_public_line_length 5
#
# ^-- this defines the longest quote (number of lines) which the
#     script will display publically rather than as a private message.
#     Somewhere between 4 and 12 should be good depending on how
#     busy your channel is.
#
#
set qdb_seconds_per_quote 6
#
# ^-- this defines the number of seconds that the bot will wait
#     for after displaying one quote before it allows another
#     to be requested. Set to 0 to disable this floodprotection.
#
#
set qdb_seconds_per_line 0
#
# ^-- this defines the number of seconds that the bot will wait
#     between displaying two lines of the same quote.
#     Bear in mind that eggdrops won't display more than one
#     line per few seconds even if this is set to 0.
#
#
set qdb_max_search_results ""
#
# ^-- maximum number of search results returned by a
#     !qdb <text> query.
#     Bear in mind that the maximum IRC server line length will
#     limit you if you set this to something large (> 25 or so).
#
#
set qdb_enable_debug 1
#
# ^-- this should always be set to 0 unless you're having some
#     issues, i.e. the script isn't working. Beware that it will
#     spray all sorts of debugging crap into the eggdrop logs.
#
#
set qdb_use_blocking_number_fetch 1
set qdb_use_blocking_search_fetch 1
set qdb_use_blocking_random_fetch 0
#
# ^-- these three define the mode used to make the queries to the
#     qdb.org webserver. If you don't understand the difference
#     between blocking and non-blocking (callback) fetch, you're
#     probably better leaving them as they are.
#     This is the default because it has been discovered through
#     experimentation than blocking fetches are a lot faster than non-
#     blocking ones, at least on my eggdrop (1.6.12 / linux 2.4.20).
#     However some may consider non-blocking fetches to be more stable.
#     Feel free to set them to 0 but don't be surprised if it's as
#     slow as a snail.
#
#
set qdb_lock_reset_time_ms 600000
#
# ^-- this is the amount of time in milliseconds the script
#     will wait for before removing a stale qdb_lock lock.
#     This attempts to combat failed fetch-related siezeups,
#     and their associated annoying public messages.
#     Currently 10 minutes. You shouldn't need to change it.
#     This is new and experimental. I don't know whether it
#     solves the problem successfully or not.
#
#
set qdb_use_notices 1
#
# ^-- this specifies whether lengthy quotes should be /noticed to
#     the recipient's nick or /msged there. Set to 0 to use /msg.
#     Set to 1 to use /notice.
#
#
set qdb_spit_channel_list [list]

# lappend qdb_spit_channel_list "#somechannel 5"
# lappend qdb_spit_channel_list "#someotherchannel 10"
# lappend qdb_spit_channel_list "#anotherchannel 60"
# lappend qdb_spit_channel_list "#\[channel_with_brackets\] 120"
# # ... more lappend lines here ...

#
# ^-- if you want the bot to spit out random quotes into a channel
#     or channels, you will need to add the names of the channels
#     to which this applies by uncommenting one or more 'lappend ...' lines
#     here and changing the channel name and the number that follows
#     it. This number is the number of minutes you want the bot to
#     wait between spitting out one quote and spitting out the next.
#     It may be set independently for each of the channels on the list.
#     There must be a space between the channel name and the number.
#	If your channel name contains [square brackets], you will need
#	to slash-escape the brackets ( '[' -> '\[' , ']' -> '\]' )
#	as in the example above.

# ----------------------------------------------
# *** DEFAULT CHANNEL (PUBLIC) COMMANDS ***

# !qdb          :  displays a random quote from the qdb.org QDB;
# !qdb <number> :  displays a specific quote from the qdb.org QDB;
# !qdb <text>   :  searches for quotes matching <text> in the qdb.org
#                   QDB and displays their numbers;
# !qdb_version  :  shows the script version;
# !qdb_usage    :  shows the commands that this script supports.

## ---------------------------------------------
## Don't change anything below this line.
## ---------------------------------------------

package require http

set agent "Mozilla"
set query_prefix "http://qdb.mercenariesguild.net/"
set query_suffix_random "random"
set query_suffix_number ""
set query_suffix_search "search"
set query_get_parm_number ""
set query_get_parm_search "query="
set qdb_cache_pointer_position 0
set qdb_cache [list]
set qdb_number_list [list]
set qdb_lock 0
set qdb_result_regexp "<blockquote class=\"quote-body\">.*<p>(.*?)</p>(.*</blockquote>)"
set qdb_number_regexp "<span class=\"quote-id\">#(\[0-9\]+)</span>(.*)"
set qdb_newline_regexp "<br/>|</p>"

# text strings

set qdb_ver 					"!qdb for qdb.mg.net 1.4.7 by DJGrenola (qdb@alicampbell.org.uk)"
set qdb_search_result_individual_prefix 	"Q# "
set qdb_search_result_separator 		" | "
set qdb_command_suffix_debug 		"_debug"
set qdb_command_suffix_version 		"_version"
set qdb_command_suffix_usage 		"_usage"
set qdb_command_suffix_restart 		"_restart"

set qdb_errortext_http_fetch 		"error: \002$qdb_command\002: command is locked while HTTP fetch is performed. Try again in 10 seconds."
set qdb_errortext_failed_init 		"error: \002$qdb_command\002: qdb_cache is empty (failed initialisation)"
set qdb_errortext_cache_spent		"error: \002$qdb_command\002: qdb_cache is spent (HTTP lookup to refresh it must have failed ... retrying)" 
set qdb_errortext_fetch_locked		"error: \002$qdb_command\002: qdb_lock is 1, cannot continue."
set qdb_errortext_infinite_loop		"error: \002$qdb_command\002: safety_measure > 100 in qdbCallback, assuming infinite loop and terminating"
set qdb_errortext_spitlist_invalid		"error: \002$qdb_command\002: qdb_spit_channel_list contains illegal entries : check configuration"
set qdb_unlock_warning				"$qdb_ver: WARNING: waited longer than $qdb_lock_reset_time_ms for query, unlocking."

set qdb_text_search_result_prefix 		"\002$qdb_command\002 search results: "
set qdb_text_usage_1 				"\002$qdb_command\002: usage: $qdb_command for a random quote"
set qdb_text_usage_2 				"              $qdb_command <number> for a specific quote"
set qdb_text_usage_3 				"              $qdb_command <text> to search for a quote containing text <text>"
set qdb_text_no_search_results 		"\002$qdb_command\002: no results found for search"
set qdb_text_version 				"\002$qdb_ver"
set qdb_text_too_many_lines_1		"\002$qdb_command\002: quote has too many lines to display in public channel (max $qdb_max_public_line_length, quote has "
set qdb_text_too_many_lines_2		"): sending as privmsg instead"
set qdb_text_quote_not_found			"\002$qdb_command\002: quote not found"
set qdb_text_running				"$qdb_ver: RUNNING, use $qdb_command to invoke."

bind pubm * "* $qdb_command*" qdb_decide
bind pubm * "* $qdb_command$qdb_command_suffix_usage" qdb_usage_wrapper
bind pubm * "* $qdb_command$qdb_command_suffix_version" qdb_version

#bind pubm * "* $qdb_command$qdb_command_suffix_restart" qdb_restart
#bind pubm * "* $qdb_command$qdb_command_suffix_debug" qdb_debug

## -------------------------------------------------
## DEFINITELY don't change anything below THIS line.
## -------------------------------------------------

set startup_time [clock clicks -milliseconds]
set qdb_last_quote_time [expr $startup_time - 60000]
set qdb_nick_token_hash(0) 0
set qdb_channel_token_hash(0) 0

## we need to disable any 'after' commands that may still be on
## the event queue from a previous invocation:

set qdb_after_list [after info]

foreach token $qdb_after_list {
	set id_info [after info $token]
	if { [regexp .*qdb_spit.* $id_info] } {
		if { $qdb_enable_debug } {
			putlog "!qdb: procname matched qdb_spit: removing from queue"
		}
		after cancel $token
	}
}

proc qdb_debug { n u h c t } {

	global qdb_cache
	global qdb_lock
	global qdb_cache_pointer_position
	global qdb_number_list

	op "list length: [llength $qdb_cache] | number list length: [llength $qdb_number_list] | lock: $qdb_lock | qdb_cache_pointer_position: $qdb_cache_pointer_position" $c

}

proc locked_error { c } {
	global qdb_errortext_http_fetch
	op $qdb_errortext_http_fetch $c
}

proc qdb_decide { n u h c t } {

	global qdb_command
	global qdb_command_suffix_debug
	global qdb_command_suffix_restart
	global qdb_command_suffix_usage
	global qdb_command_suffix_version
	global qdb_last_quote_time
	global qdb_seconds_per_quote
	global qdb_enable_debug

	if { $qdb_enable_debug } {
		putlog "qdb_decide $n $u $h $c $t"
	}

	if { [expr "$qdb_last_quote_time + ($qdb_seconds_per_quote * 1000)"] > [clock clicks -milliseconds] } {
		if { $qdb_enable_debug } {
			putlog "qdb_decide: returned due to inter-quote time being exceeded: b_l_q_t = $qdb_last_quote_time | b_s_p_q = $qdb_seconds_per_quote | clock clicks = [clock clicks -milliseconds]"
		}
		return
	}

	if { [regexp "($qdb_command$qdb_command_suffix_debug *)|($qdb_command$qdb_command_suffix_restart *)|($qdb_command$qdb_command_suffix_usage *)|($qdb_command$qdb_command_suffix_version)" $t] } {
		if { $qdb_enable_debug } {
			putlog "qdb_decide: returned due to detection of valid alternate command suffix"
		}
		return
	}

	if { [string length $t] > [string length $qdb_command] } {
		set blah "^$qdb_command *\[0-9\]+$"
		if { [regexp $blah $t match] } {
			qdb_number $n $u $h $c $t
			return
		}
		qdb_search $n $u $h $c $t
	} else {
		qdb_random $n $u $h $c $t
	}

}

proc qdb_search { n u h c t } {

	global qdb_command
	global agent
	global query_get_parm_search
	global query_get_parm_number
	global query_prefix
	global query_suffix_search
	global qdb_max_search_results
	global qdb_enable_debug
	global qdb_nick_token_hash
	global qdb_channel_token_hash
	global qdb_use_blocking_search_fetch

	if { $qdb_enable_debug } {
		putlog "qdb_search $n $u $h $c $t"
	}
		
	regexp "^$qdb_command *(.*)$" $t blah text

	http::config -useragent $agent
	set url $query_prefix$query_suffix_search[http::formatQuery $query_get_parm_search $text $query_get_parm_number $qdb_max_search_results]
	if { $qdb_use_blocking_search_fetch == 0 } {
		set token [http::geturl $url -command qdbCallback_search]
		set qdb_nick_token_hash($token) $n
		set qdb_channel_token_hash($token) $c
	} else {
		set token [http::geturl $url]
		set qdb_nick_token_hash($token) $n
		set qdb_channel_token_hash($token) $c
		qdbCallback_search $token
	}

}

proc qdbCallback_search { token } {

	global qdb_result_regexp
	global qdb_number_regexp
	global qdb_max_search_results
	global qdb_search_result_separator
	global qdb_text_search_result_prefix
	global qdb_command
	global qdb_search_result_individual_prefix
	global qdb_text_no_search_results
	global qdb_enable_debug
	global qdb_nick_token_hash
	global qdb_channel_token_hash

	set nick $qdb_nick_token_hash($token)
	set channel $qdb_channel_token_hash($token)
	unset qdb_nick_token_hash($token)
	unset qdb_channel_token_hash($token)

	if { $qdb_enable_debug } {
		putlog "qdbCallback_search $token $nick $channel"
	}

	catch {

		set result [http::data $token]
		unset $token
		set search_results [list]

		set count 0

		while { [regexp $qdb_number_regexp $result blah numero the_rest] } {

			if { $count > $qdb_max_search_results } {
				break
			}

			lappend search_results $numero

			set result $the_rest
			incr count

		}

		set search_result_string $qdb_text_search_result_prefix
		set separator ""

		foreach search_result $search_results {
			set search_result_string $search_result_string$separator$qdb_search_result_individual_prefix$search_result
			set separator $qdb_search_result_separator
		}

		if { $count == 0 } {
			op $qdb_text_no_search_results $channel
			return
		}

		op $search_result_string $channel

		return

	} errorstg

	putlog $errorstg

}

proc qdb_number { n u h c t } {
	global qdb_enable_debug
	if { $qdb_enable_debug } {
		putlog "qdb_number $n $u $h $c $t"
	}
	regexp "\[0-9\]+" $t match
	qdb_dot_org_fetch_number $match $n $c
}

proc qdb_usage_wrapper { n u h c t } {
	qdb_usage $c
}

proc qdb_usage { c } {

	global qdb_text_usage_1
	global qdb_text_usage_2
	global qdb_text_usage_3

	op $qdb_text_usage_1 $c
	op $qdb_text_usage_2 $c
	op $qdb_text_usage_3 $c

}

proc qdb_version { n u h c t } {
	global qdb_text_version
	op $qdb_text_version $c
}

proc qdb_random { n u h c t } {

	global qdb_cache
	global qdb_cache_pointer_position
	global qdb_lock
	global qdb_number_list
	global qdb_command
	global qdb_errortext_failed_init
	global qdb_errortext_cache_spent
	global qdb_enable_debug
	global qdb_lock_reset_time_ms

	if { $qdb_enable_debug } {
		putlog "qdb_random $n $u $h $c $t"
	}

#	if { $qdb_lock != 0 } {
#		locked_error $c
#		return
#	}
	if { $qdb_lock != 0 } {
		locked_error $c
		if { [expr [clock clicks -milliseconds] - $qdb_lock] < $qdb_lock_reset_time_ms } {
#			putlog $qdb_ver $qdb_errortext_fetch_locked
			return
		}
		putlog $qdb_unlock_warning
		set qdb_lock 0
	}

	if { [llength $qdb_cache] == 0 } {
		op "qdb_cache is empty - refilling ..." $c
		qdb_dot_org_fetch_random
		return
	}

	if { $qdb_cache_pointer_position >= [llength $qdb_cache] } {
		op "qdb_cache is empty - refilling ..." $c
		qdb_dot_org_fetch_random
		return
	}

	if { [qdb_output [lindex $qdb_cache $qdb_cache_pointer_position] [lindex $qdb_number_list $qdb_cache_pointer_position] $c $n 1] == 0 } {
		set failed 1
	} else {
		set failed 0
	}

	incr qdb_cache_pointer_position

	if { $qdb_cache_pointer_position >= [llength $qdb_cache] } {
		if { $failed == 1 } {
			locked_error $c
		}
		qdb_dot_org_fetch_random
	} else {
		if { $failed == 1 } {
			qdb_random $n $u $h $c $t
		}
	}
}

proc qdb_output { text number c n allow_failure } {

	global qdb_max_public_line_length
	global qdb_command
	global qdb_text_too_many_lines_1
	global qdb_text_too_many_lines_2
	global qdb_seconds_per_line
	global qdb_last_quote_time
	global qdb_enable_debug
	global qdb_use_notices
	global qdb_newline_regexp

	if { $qdb_enable_debug } {
		putlog "qdb_output $text $number $c $n $allow_failure"
	}

	regsub -all "(\r)|(\n)" "$text " "" tmp4
	regsub -all $qdb_newline_regexp $tmp4 "\n" tmp5
	regsub -all "&gt;" $tmp5 ">" tmp6
	regsub -all "&lt;" $tmp6 "<" tmp7
	regsub -all "&quot;" $tmp7 "'" tmp8
	regsub -all "&nbsp;" $tmp8 " " tmp9
	regsub -all "amp;" $tmp9 "" tmp10

	set lst [split $tmp10 "\n"]

	if { [llength $lst] > $qdb_max_public_line_length } {
		if { $allow_failure } {
			return 0
		}
		op $qdb_text_too_many_lines_1[llength $lst]$qdb_text_too_many_lines_2 $c
		if { $qdb_use_notices } {
			set target "NOTICE $n"
		} else {
			set target "PRIVMSG $n"
		}
	} else {
		set target "PRIVMSG $c"
	}

	set delay_increment [expr $qdb_seconds_per_line * 1000]
	set delay 0

	set qdb_last_quote_time [clock clicks -milliseconds]

	# fix for brackets-in-channel-name bug (thanks CGlass)
	regsub -all \\\] $target \\\] target
	regsub -all \\\[ $target \\\[ target

	foreach tmp11 $lst {
		# these two might look like NOPs. They aren't. Remember square brackets are reserved in regexp too.
		regsub -all \\\] $tmp11 \\\] tmp11
		regsub -all \\\[ $tmp11 \\\[ tmp11
		if { $number == -1 } {
			set to_ex "putserv \"$target :\002|qdb|\002 $tmp11\""
			after $delay $to_ex
		} else {
			set to_ex "putserv \"$target :\002|qdb $number|\002 $tmp11\""
			after $delay $to_ex
		}
		set delay [expr $delay + $delay_increment]
	}

	return 1

}

proc qdb_restart { n u h c t } {
	qdb_init
}

proc qdb_init { } {

	global qdb_ver
	global qdb_spit_channel_list
	global qdb_error
	global qdb_errortext_spitlist_invalid
	global qdb_enable_debug
	global qdb_lock

	set qdb_lock 0

	qdb_dot_org_fetch_random

	if { [llength $qdb_spit_channel_list] > 0 } {

		if { [check_qdb_spit_channel_list] == 0 } {

			putlog $qdb_errortext_spitlist_invalid
			die

		} else {

			set i 0

			while { $i < [llength $qdb_spit_channel_list] } {

				regexp "^(.+) (\[0-9\]+)$" [lindex $qdb_spit_channel_list $i] blah channel delay
				set delay_ms [expr $delay * 60000]

				# fix for brackets-in-channel-name bug (thanks CGlass)
				regsub -all \\\] $channel \\\] channel
				regsub -all \\\[ $channel \\\[ channel
				set to_ex "qdb_spit $channel $delay_ms"

				if { $qdb_enable_debug } {
					putlog "!qdb: placing spit command on event queue : after $delay_ms $to_ex"
				}

				after $delay_ms $to_ex

				incr i

			}
		}
	}
}

proc qdb_spit { channel delay } {

	global qdb_enable_debug

	if { $qdb_enable_debug } {
		putlog "qdb_spit $channel $delay"
	}

	qdb_random \000 \000 \000 $channel \000

	regsub -all \\\] $channel \\\] channel
	regsub -all \\\[ $channel \\\[ channel

	set to_ex "qdb_spit $channel $delay"
	if { $qdb_enable_debug } {
		putlog "!qdb: placing spit command on event queue : after $delay $to_ex"
	}
	after $delay $to_ex

}

proc check_qdb_spit_channel_list { } {

	global qdb_spit_channel_list

	set i 0

	while { $i < [llength $qdb_spit_channel_list] } {
		if { [regexp "^.+ \[0-9\]+$" [lindex $qdb_spit_channel_list $i]] == 0 } {
			return 0
		} else {
			incr i
		}
	}

	return 1

}

proc qdb_dot_org_fetch_number { n nick c } {

	global agent
	global query_prefix
	global query_suffix_number
	global qdb_enable_debug
	global qdb_nick_token_hash
	global qdb_channel_token_hash
	global qdb_use_blocking_number_fetch

	if { $qdb_enable_debug } {
		putlog "qdb_dot_org_fetch_number $n $nick $c"
	}

	set query $query_prefix$query_suffix_number$n

	http::config -useragent $agent

	if { $qdb_use_blocking_number_fetch == 0 } {
		set token [http::geturl $query -command qdbCallback_number]
		set qdb_nick_token_hash($token) $nick
		set qdb_channel_token_hash($token) $c
	} else {
		set token [http::geturl $query]
		set qdb_nick_token_hash($token) $nick
		set qdb_channel_token_hash($token) $c
		qdbCallback_number $token
	}

}

proc qdb_dot_org_fetch_random { } {

	global agent
	global query_prefix
	global query_suffix_random
	global qdb_lock
	global qdb_command
	global qdb_errortext_fetch_locked
	global qdb_ver
	global qdb_enable_debug
	global qdb_use_blocking_random_fetch

	if { $qdb_enable_debug } {
		putlog "qdb_dot_org_fetch_random"
	}

	set qdb_lock [clock clicks -milliseconds]

	http::config -useragent $agent

	if { $qdb_use_blocking_random_fetch == 0 } {
		set token [http::geturl $query_prefix$query_suffix_random -command qdbCallback_random]
	} else {
		set token [http::geturl $query_prefix$query_suffix_random]
		qdbCallback_random $token
	}

}

proc qdbCallback_number { token } {

	global qdb_result_regexp
	global qdb_command
	global qdb_text_quote_not_found
	global qdb_enable_debug
	global qdb_nick_token_hash
	global qdb_channel_token_hash

	if { $qdb_enable_debug } {
		putlog "qdbCallback_number $token"
	}

	set nick $qdb_nick_token_hash($token)
	set c $qdb_channel_token_hash($token)
	unset qdb_nick_token_hash($token)
	unset qdb_channel_token_hash($token)

	if { $qdb_enable_debug } {
		putlog "qdbCallback_number $token $nick $c"
	}

	catch {

		set result [http::data $token]
		unset $token

		if { [regexp $qdb_result_regexp $result tmp data] == 1} {
			qdb_output $data -1 $c $nick 0
		} else {
			op $qdb_text_quote_not_found $c
		}

		return 1

	} blah

	if { $qdb_enable_debug } {
		putlog $blah
	}

	return 0

}

proc qdbCallback_random { token } {

	global qdb_lock
	global qdb_cache
	global qdb_cache_pointer_position
	global qdb_result_regexp
	global qdb_number_regexp
	global qdb_number_list
	global qdb_command
	global qdb_errortext_infinite_loop
	global qdb_ver
	global qdb_enable_debug
	global qdb_max_public_line_length
	global qdb_newline_regexp

	set qdb_cache [list]
	set qdb_number_list [list]

	if { $qdb_enable_debug } {
		putlog "qdbCallback_random $token"
	}

	catch {

		set result [http::data $token]
		unset $token

		set safety_measure 0
		set limit [expr $qdb_max_public_line_length + 1]

		while { [regexp $qdb_result_regexp $result tmp data the_rest] } {

			if { [regexp "(.*$qdb_newline_regexp.*){$limit}" $data] == 0 } {
				if { [regexp $qdb_number_regexp $result blah numero] == 1 } {
					lappend qdb_number_list $numero
				} else {
					lappend qdb_number_list -1
				}
				lappend qdb_cache $data
			}

			set result $the_rest
			incr safety_measure

			if { $safety_measure > 100 } {
				putlog $qdb_ver $qdb_errortext_infinite_loop
				set qdb_lock 0
				return
			}
		}

		set qdb_cache_pointer_position 0
		set qdb_lock 0
		return

	} errorstg

	if { $qdb_enable_debug } {
		putlog $errorstg
	}

	set qdb_lock 0

}

proc op { o c } {
	putserv "PRIVMSG $c :$o"
}

qdb_init

putlog $qdb_text_running
