#---------------------------------------------------------------------# # 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*

)} $html - html if {[regexp {

Found: 0

} $html]} { set output(error) "No results found." } # html parsing # set re {
(.+?)} set results 0 ; set output(reply) "" while {$results < ${incith::acronym::search_results}} { regexp -nocase -- $re $html - acronym regsub -nocase -- $re $html {} html if {[info exists acronym]} { append output(reply) "${acronym}${incith::acronym::separator}" unset acronym } else { break } incr results } # check for errors if {$output(reply) == "" && ![info exists output(error)]} { set output(error) "Could not parse the results." } else { set output(reply) [string trimright $output(reply) "${incith::acronym::separator}"] } # process the output array set static($token,output) [array get output] process_output $token } # [process_output] : create the output and send it # proc process_output {token} { upvar #0 $token state upvar #0 incith::acronym::static static array set output $static($token,output) # check for errors if {[info exists output(error)]} { send_output $output(chan) $output(error) $output(who) return } # send the result send_output $output(chan) "$output(reply)" # clean the static array for this http session foreach value [array get static] { if {[info exists static($value)]} { if {[string match *${token}* $value]} { unset static($value) } } } } # [ipl] : a neat/handy putlog procedure # proc ipl {who {where {}} {what {}}} { if {$where == "" && $what == ""} { # first argument only = data only putlog "${incith::acronym::version}: ${who}" } elseif {$where != "" && $what == ""} { # two arguments = who and data putlog "${incith::acronym::version}: <${who}> ${where}" } else { # all three... putlog "${incith::acronym::version}: <${who}/${where}> ${what}" } } # [send_output] : sends $data appropriately out to $where # proc send_output {where data {isErrorNick {}}} { if {${incith::acronym::notices} >= 1} { foreach line [incith::acronym::line_wrap $data] { putquick "NOTICE $where :${line}" } } elseif {${incith::acronym::notice_errors_only} >= 1 && $isErrorNick != ""} { foreach line [incith::acronym::line_wrap $data] { putquick "NOTICE $isErrorNick :${line}" } } else { foreach line [incith::acronym::line_wrap $data] { putquick "PRIVMSG $where :${line}" } } } # [line_wrap] : takes a long line in, and chops it before the specified length # http://forum.egghelp.org/viewtopic.php?t=6690 # proc line_wrap {str {splitChr { }}} { set out [set cur {}] set i 0 set len $incith::acronym::split_length foreach word [split [set str][set str ""] $splitChr] { if {[incr i [string len $word]] > $len} { lappend out [join $cur $splitChr] set cur [list $word] set i [string len $word] } else { lappend cur $word } incr i } lappend out [join $cur $splitChr] } } # the script has loaded. namespace eval incith::acronym { putallbots "incith:acronym time $static(botnet,${botnet-nick},time)" } incith::acronym::ipl "loaded." # EOF