#---------------------------------------------------------------------#
# incith:acronym $Rev:: 128 $ #
# $Id:: incith-acronym.tcl 128 2009-09-14 02:02:52Z incith $ #
# #
# searches for acronyms at: #
# http://acronyms.silmaril.ie/cgi-bin/uncgi/acronyms #
# tested on Eggdrop 1.6.19 & Windrop v1.6.17 #
# #
# Usage: #
# .chanset #channel +acronym #
# !acronym #
# #
# ChangeLog (m/d/y): #
# 1/21/09: script recreated. #
# #
# TODO: #
# - Suggestions/Thanks/Bugs/Ideas, e-mail at bottom of header. #
# #
# LICENSE (GPLv3): #
# 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 3 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. #
# (http://www.gnu.org/licenses/gpl-3.0.txt) #
# #
# Copyright (C) 2009, Jordan #
# http://incith.com ~ incith@gmail.com ~ irc.freenode.net/#incith #
#---------------------------------------------------------------------#
package require http 2.3
setudef flag acronym
# 0 (zero) will disable an optional variable, 1 or above enables
#
namespace eval incith::acronym {
# the bind prefix/command char(s) ({!} or {! .} etc, separate with space)
variable command_chars {! .}
# binds {one two three}
variable binds {acronym a}
# maximum number of search results to return?
variable search_results 8
# allow binds to be used in /msg's to the bot?
variable private_messages 1
# send public/channel output to the user instead?
variable public_to_private 0
# send replies as notices instead of private messages?
variable notices 0
# levels required to use the binds, global|channel.
variable public_flags {-|-}
variable private_flag {-|-} # only the global flag matters.
# only send script 'errors' as notices? (not enough input etc)
variable notice_errors_only 0
# this will be used to separate items (Foo; Bar; Baz)
variable separator {; }
# maximum length of a reply before breaking it up
variable split_length 440
# if you're using a proxy, enter it here {example.com:3128}
variable proxy {}
# how long (in seconds) before the http request times out?
variable timeout 15
# use the callback function for non-blocking http fetches?
# note: your eggdrop must be patched or else this will slow
# lookups down a lot and even break some things.
variable callback 0
# if your bots participate in a botnet, you can enable this variable
# and load the script on all of the bots, but only one bot will respond
# to acronym requests. if that bot quits, the next bot in line will start.
# there is no reason to disable this variable that I can think of.
variable botnet 1
}
# script begings
namespace eval incith::acronym {
global botnet-nick nick
if {${botnet-nick} == ""} { set botnet-nick ${nick} }
variable version "incith:acronym-SVN"
variable debug 1
array set static {}
if {${incith::acronym::botnet} >= 1} {
set static(botnet,${botnet-nick},time) [clock seconds]
} else {
set static(botnet,${botnet-nick},time) "noswarm"
}
if {![info exists static(botnet,bots)]} {
set static(botnet,bots) ${botnet-nick}
}
}
# bind the binds
foreach command_char [split ${incith::acronym::command_chars} " "] {
foreach bind [split ${incith::acronym::binds} " "] {
# public message binds
bind pub ${incith::acronym::public_flags} "${command_char}${bind}" incith::acronym::message_handler
# private message binds
if {${incith::acronym::private_messages} >= 1} {
bind msg ${incith::acronym::private_flag} "${command_char}${bind}" incith::acronym::message_handler
}
}
}
# bind the botnet binds
if {${incith::acronym::botnet} >= 1} {
bind bot - incith:acronym incith::acronym::bot_msg
bind link - * incith::acronym::bot_link
# it really depends what works better for you, checking if
# the bot is onchan or just making sure they are linked.
# bind disc - * incith::acronym::bot_disc
}
namespace eval incith::acronym {
proc bot_msg {from cmd text} {
global botnet-nick
upvar #0 incith::acronym::static static
if {${incith::acronym::debug} >= 1} {
putlog "${incith::acronym::version} (botmsg): <${from}> ${cmd} ${text}"
}
# receiving a bots load time
if {[string match "time ?*" $text]} {
regexp -- {time (.*)} $text - time
set static(botnet,${from},time) $time
# make sure this bot is in our bot list
if {![string match "*${from}*" $static(botnet,bots)]} {
putlog "${incith::acronym::version} (botnet): ${from} has joined the incith:acronym swarm."
append static(botnet,bots) ";${from}"
regsub -all -- {;;} $static(botnet,bots) {;} static(botnet,bots)
set static(botnet,bots) [string trimright $static(botnet,bots) {;}]
}
}
}
proc bot_link {bot hub} {
global botnet-nick
upvar #0 incith::acronym::static static
# send our time to the bots
putallbots "incith:acronym time $static(botnet,${botnet-nick},time)"
}
proc bot_disc {bot} {
global botnet-nick
upvar #0 incith::acronym::static static
if {[string match "*${bot}*" $static(botnet,bots)]} {
if {$static(botnet,${bot},time) != "noswarm"} {
putlog "${incith::acronym::version} (botnet): ${bot} has left the incith:acronym swarm."
}
}
# remove this bot from our bot list
regsub -all -- $bot $static(botnet,bots) {} static(botnet,bots)
regsub -all -- {;;} $static(botnet,bots) {;} static(botnet,bots)
set static(botnet,bots) [string trimright $static(botnet,bots) {;}]
# remove their time? If they just lost link, they might still be [onchan]
# unset static(botnet,${bot},time)
}
}
namespace eval incith::acronym {
# [message_handler] : handles public & private messages
#
proc message_handler {nick uhand hand args} {
global botnet-nick lastbind
upvar #0 incith::acronym::static static
set input(who) $nick
if {[llength $args] >= 2} { # public message
set input(where) [lindex $args 0]
if {${incith::acronym::public_to_private} >= 1} {
set input(chan) $input(who)
} else {
set input(chan) $input(where)
}
set input(query) [lindex $args 1]
if {[channel get $input(where) acronym] != 1} {
return
}
} else { # private message
set input(where) "private"
set input(chan) $input(who)
set input(query) [lindex $args 0]
if {${incith::acronym::private_messages} <= 0} {
return
}
}
# botnet
if {${incith::acronym::botnet} >= 1 && $input(where) != "private"} {
foreach bot [split $static(botnet,bots) ";"] {
# skip ourselves, bots not on the input channel, and bots not participating
if {${bot} == ${botnet-nick} || ![onchan ${bot} $input(where)] || $static(botnet,${bot},time) == "noswarm"} {
continue
# bots that load last will serve first. change > into < to reverse.
} elseif {$static(botnet,${bot},time) > $static(botnet,${botnet-nick},time)} {
if {${incith::acronym::debug} >= 1} {
putlog "${incith::acronym::version} (botnet): $bot loaded before me."
}
return
# should 2 bots have the same time, set a new random time (this did happen in testing)
} elseif {$static(botnet,${bot},time) == $static(botnet,${botnet-nick},time)} {
if {${incith::acronym::debug} >= 1} {
putlog "${incith::acronym::version} (botnet): $bot had the same load time as me, fixing."
}
set static(botnet,${botnet-nick},time) [expr [clock seconds] + int(rand()*60)+1]
putallbots "incith:acronym time $static(botnet,${botnet-nick},time)"
return
}
}
# looks like we're serving, make sure we keep the botnet up to date
putallbots "incith:acronym time $static(botnet,${botnet-nick},time)"
}
# log it
if {[regexp -- {^\s*$} $input(query)]} {
ipl "<${input(who)}/${input(where)}>"
send_output $input(chan) "Syntax: ${lastbind} "
return
} else {
ipl ${input(who)} ${input(where)} "${lastbind} ${input(query)}"
}
# do some things:
# set input(query) [string map "{%20} {+}" [::http::formatQuery $input(query)]]
set input(url) {http://acronyms.silmaril.ie/cgi-bin/uncgi/acronyms}
# fetch the html
fetch_html [array get input]
}
# [fetch_html] : fetch html of a given url
#
proc fetch_html {tmpInput} {
upvar #0 incith::acronym::static static
array set input $tmpInput
# setup the timeout, for use below
set timeout [expr round(1000 * ${incith::acronym::timeout})]
# setup proxy information, if any
if {[string match {*:*} ${incith::acronym::proxy}] == 1} {
set proxy_info [split ${incith::acronym::proxy} ":"]
}
# the "browser" we are using
# NT 5.1 - XP, NT 6.0 - Vista
set ua "Opera/9.63 (Windows NT 6.0; U; en)"
if {[info exists proxy_info] == 1} {
::http::config -useragent $ua -proxyhost [lindex $proxy_info 0] -proxyport [lindex $proxy_info 1]
} else {
::http::config -useragent $ua
}
# retrieve the html
if {$incith::acronym::callback >= 1} {
catch {set token [::http::geturl "$input(url)" -query "terms=${input(query)}" -command incith::acronym::httpCommand -timeout $timeout]} output(status)
} else {
catch {set token [::http::geturl "$input(url)" -query "terms=${input(query)}" -timeout $timeout]} output(status)
}
# need to check for some errors here:
if {[string match "couldn't open socket: host is unreachable*" $output(status)]} {
send_output $input(chan) "Unknown host '${input(query)}'." $input(who)
return
} elseif {[string match "Missing host part:*" $output(status)]} {
send_output $input(chan) "Invalid host '${input(query)}'." $input(who)
return
} elseif {[string match "Unsupported URL type*" $output(status)]} {
send_output $input(chan) "Unsupported URL type '${input(query)}'." $input(who)
return
} elseif {![string match "::http::*" $output(status)]} {
send_output $input(chan) "Unknown Error: $output(status)" $input(who)
return
}
# no errors, move on:
set static($token,input) [array get input]
# manually call our callback procedure if we're not using callbacks
if {$incith::acronym::callback <= 0} {
httpCommand $token
}
}
# [httpCommand] : makes sure the http request succeeded
#
proc httpCommand {token} {
upvar #0 $token state
upvar #0 incith::acronym::static static
# build the output array
array set output $static($token,input)
putlog "acronym - status([::http::status $token]) ncode([::http::ncode $token])"
switch -exact [::http::status $token] {
"timeout" {
if {$incith::acronym::debug >= 1} {
ipl $output(who) $output(where) "status = timeout (url = $state(url))"
}
set output(error) "Operation timed out after ${incith::acronym::timeout} seconds."
}
"error" {
if {$incith::acronym::debug >= 1} {
ipl $output(who) $output(where) "status = error([::http::error $token]) (url = $state(url))"
}
set output(error) "An unknown error occurred. (Error #01)"
}
"ok" {
switch -glob [::http::ncode $token] {
3* {
array set meta $state(meta)
if {$incith::acronym::debug >= 1} {
ipl $output(who) $output(where) "redirecting to $meta(Location)"
}
set output(url) $meta(Location)
fetch_html [array get output]
return
}
200 {
if {$incith::acronym::debug >= 1} {
ipl $output(who) $output(where) "parsing $state(url)"
}
}
default {
if {$incith::acronym::debug >= 1} {
ipl $output(who) $output(where) "status = default, error"
}
set output(error) "An unknown error occurred. (Error #02)"
}
}
}
default {
if {$incith::acronym::debug >= 1} {
ipl $output(who) $output(where) "status = unknown, default, error"
}
set output(error) "An unknown error occurred. (Error #03)"
}
}
set static($token,output) [array get output]
process_html $token
}
# [process_html] :
#
proc process_html {token} {
upvar #0 $token state
upvar #0 incith::acronym::static static
array set output $static($token,output)
# get the html
set html $state(body)
# store the HTML to a file
if {$incith::acronym::debug >= 2} {
set fopen [open incith-acronym.html w]
puts $fopen $html
close $fopen
}
# html cleanups
regsub -all {\n} $html {} html
regsub -all {\t} $html {} html
regsub -all { } $html { } html
regsub -all {>} $html {>} html
regsub -all {<} $html {<} html
regexp -nocase -- {(.*
.*Found: \d*
Found: 0
} $html]} { set output(error) "No results found." } # html parsing # set re {