Regarding the google API change, I found this script (
http://digdilem.org/irc/index.cgi?entry=2617142644) and modified the FordLawnmower script to work with the new google API. This is the Xchat script with the new google API.
Code: Select all
#
# google.tcl - X-Chat TCL script that implements a public !google command
#
# prerequisites:
# You need to have
#
# + TCL 8.5 (http://www.tcl.tk/) and
# + TCLLib 1.15 (http://tcllib.sourceforge.net/)
#
# installed.
# You also need a Google API key and a Google Search Id.
#
# installation:
# Put google.tcl into startup scripts folder
#
# + on Windows the folder usually is "C:\Users\[Your User Name]\AppData\Roaming\X-Chat 2"
# + on Linux the folder usually is "~/.xchat2/"
#
# and setup the configuration below.
#
# usage:
# If someone (including you) types
#
# !google My Search Terms
#
# into a channel or a private message
# this script will respond with the first
# search results found.
# By default only the first 3 results are shown
# but this can be configured.
#
#################
# configuration #
#################
set API_KEY "YOUR_GOOGLE_API_KEY"
set SEARCH_ID "YOUR_GOOGLE_SEARCH_ID"
set MAX_RESULTS 3
###################
# !google command #
###################
package require http
package require tls
package require json
# need https for google rest api
::http::register https 443 ::tls::socket
# search request queue
set google_queue {}
# say to user or channel
proc google_say { target data } {
command "msg $target $data"
}
# enqueue cmd to be executed
proc google_enqueue { cmd } {
global google_queue
lappend google_queue $cmd
}
# called every second to handle enqueued commands
proc google_hook { } {
global google_queue
if { [ llength $google_queue ] > 0 } {
set cmd [ lindex $google_queue 0 ]
set google_queue [ lrange $google_queue 1 end ]
eval $cmd
}
}
# send the result to dest if it is a channel. otherwise send it to src
proc google_result { target data } {
google_say $target "Search Results:"
global MAX_RESULTS
for { set i 0 } { $i < $MAX_RESULTS } { incr i } {
set title [ dict get [ lindex [ dict get $data items ] $i ] title ]
set link [ dict get [ lindex [ dict get $data items ] $i ] link ]
set index [ expr $i + 1 ]
google_say $target "\[$index\] $title"
google_say $target "\[$index\] $link"
}
}
# perform google search request
proc google_request { target query } {
global API_KEY SEARCH_ID
set token [ ::http::geturl "https://www.googleapis.com/customsearch/v1?[::http::formatQuery alt json key $API_KEY cx $SEARCH_ID q $query]" ]
set body [ ::http::data $token ]
::http::cleanup $token
set json [ ::json::json2dict $body ]
google_result $target $json
}
# schedule a google search query
proc google_query { target query } {
google_enqueue [ list google_request $target $query ]
}
# create !google command
on !google omega {
splitsrc
if { [ string index $_dest 0 ] eq "#" } {
google_query $_dest $_rest
} else {
google_query $_nick $_rest
}
complete EAT_NONE
}
# also handle your commands
on XC_UCHANMSG omega {
set target [ channel ]
set data [ lindex $_raw 2 ]
set first [ lindex $data 0 ]
if { ( $first eq "!google" ) && ( [ llength $data ] > 1 ) } {
google_query $target [ lrange $data 1 end ]
}
complete EAT_NONE
}
# start hook
timer -repeat 1 google_hook
print "!google command version 1.1 started."
And this is how the script ended.
Code: Select all
##############################################################################################
## ## Advanced Google.tcl for eggdrop by Ford_Lawnmower irc.geekshed.net #Script-Help ## ##
##############################################################################################
## ## To use this script you must set channel flag +google (ie .chanset #chan +google) ## ##
##############################################################################################
package require http
package require json
if {[catch {package require tls}]} {
set httpsSupport false
} else {
set httpsSupport true
}
set url "www.googleapis.com/customsearch/v1?"
if {$httpsSupport} {
::http::register https 443 [list ::tls::socket -request 1 -require 0 -ssl3 1 -tls1 1]
set url https://$url
} else { set url http://$url }
##############################################################################################
## ## Start Setup. ## ##
##############################################################################################
## Change the country code between the "" below to change the language of your results. ##
set googlectry "en"
## Change the number between the "" below to change the number of results returned. ##
set googlemax "1"
set API_KEY ""
set SEARCH_ID ""
##############################################################################################
## ## End Setup. ## ##
##############################################################################################
proc googleweb {nick host hand chan type search} {
global googlectry googlemax API_KEY SEARCH_ID url
if {$search == ""} {puthelp "PRIVMSG $chan :Error: <search> is empty"; return 1 }
if {$type == ""} {
set googleurl $url[::http::formatQuery alt json key $API_KEY cx $SEARCH_ID hl $googlectry q $search]
} else { set googleurl $url[::http::formatQuery alt json key $API_KEY cx $SEARCH_ID hl $googlectry q $search searchType $type] }
if {[catch {http::geturl $googleurl -timeout 5000} sockerr]} {
puthelp "privmsg $chan :Timeout connecting to Google: $sockerr"
return 1
} elseif {[http::ncode $sockerr] != 200 || ![string equal [http::status $sockerr] "ok"]} {
puthelp "privmsg $chan :Error querying Google: [http::ncode $sockerr] -> [http::status $sockerr]"
http::cleanup $sockerr
return 1
} else {
set data [http::data $sockerr]
http::cleanup $sockerr
set json [ ::json::json2dict $data ]
for { set i 0 } { $i < $googlemax } { incr i } {
set title ""
set link ""
catch { set title [ dict get [ lindex [ dict get $json items ] $i ] title ] }
catch { set link [ dict get [ lindex [ dict get $json items ] $i ] link ] }
set index [ expr $i + 1 ]
if {[string length $title] && [string length $link]} {
putserv "PRIVMSG $chan :$title $link"
} else {
puthelp "PRIVMSG $chan :Google found no results"
}
}
}
return 1
}
proc google {nick host hand chan text} { googleweb $nick $host $hand $chan "" $text}
proc gimage {nick host hand chan text} { googleweb $nick $host $hand $chan image $text}
foreach bind [binds google] {lassign $bind type flags mask num proc; unbind $type $flags $mask $proc}
foreach bind [binds gimage] {lassign $bind type flags mask num proc; unbind $type $flags $mask $proc}
bind pub - .google google
bind pub - .gimage gimage
putlog "Google script loaded"
If you don't have the json package, you can add json.tcl to your scripts folder as follows:
Code: Select all
#
# JSON parser for Tcl.
#
# See http://www.json.org/ && http://www.ietf.org/rfc/rfc4627.txt
#
# Total rework of the code published with version number 1.0 by
# Thomas Maeder, Glue Software Engineering AG
#
# $Id: json.tcl,v 1.5 2009/12/10 17:48:12 andreas_kupries Exp $
#
if {![package vsatisfies [package provide Tcl] 8.5]} {
package require dict
}
package provide json 1.1
namespace eval json {
# Regular expression for tokenizing a JSON text (cf. http://json.org/)
# tokens consisting of a single character
variable singleCharTokens { "{" "}" ":" "\\[" "\\]" "," }
variable singleCharTokenRE "\[[join $singleCharTokens {}]\]"
# quoted string tokens
variable escapableREs { "[\\\"\\\\/bfnrt]" "u[[:xdigit:]]{4}" }
variable escapedCharRE "\\\\(?:[join $escapableREs |])"
variable unescapedCharRE {[^\\\"]}
variable stringRE "\"(?:$escapedCharRE|$unescapedCharRE)*\""
# (unquoted) words
variable wordTokens { "true" "false" "null" }
variable wordTokenRE [join $wordTokens "|"]
# number tokens
# negative lookahead (?!0)[[:digit:]]+ might be more elegant, but
# would slow down tokenizing by a factor of up to 3!
variable positiveRE {[1-9][[:digit:]]*}
variable cardinalRE "-?(?:$positiveRE|0)"
variable fractionRE {[.][[:digit:]]+}
variable exponentialRE {[eE][+-]?[[:digit:]]+}
variable numberRE "${cardinalRE}(?:$fractionRE)?(?:$exponentialRE)?"
# JSON token
variable tokenRE "$singleCharTokenRE|$stringRE|$wordTokenRE|$numberRE"
# 0..n white space characters
set whiteSpaceRE {[[:space:]]*}
# Regular expression for validating a JSON text
variable validJsonRE "^(?:${whiteSpaceRE}(?:$tokenRE))*${whiteSpaceRE}$"
}
# Validate JSON text
# @param jsonText JSON text
# @return 1 iff $jsonText conforms to the JSON grammar
# (@see http://json.org/)
proc json::validate {jsonText} {
variable validJsonRE
return [regexp -- $validJsonRE $jsonText]
}
# Parse JSON text into a dict
# @param jsonText JSON text
# @return dict (or list) containing the object represented by $jsonText
proc json::json2dict {jsonText} {
variable tokenRE
set tokens [regexp -all -inline -- $tokenRE $jsonText]
set nrTokens [llength $tokens]
set tokenCursor 0
return [parseValue $tokens $nrTokens tokenCursor]
}
# Throw an exception signaling an unexpected token
proc json::unexpected {tokenCursor token expected} {
return -code error "unexpected token \"$token\" at position $tokenCursor; expecting $expected"
}
# Get rid of the quotes surrounding a string token and substitute the
# real characters for escape sequences within it
# @param token
# @return unquoted unescaped value of the string contained in $token
proc json::unquoteUnescapeString {token} {
set unquoted [string range $token 1 end-1]
return [subst -nocommands -novariables $unquoted]
}
# Parse an object member
# @param tokens list of tokens
# @param nrTokens length of $tokens
# @param tokenCursorName name (in caller's context) of variable
# holding current position in $tokens
# @param objectDictName name (in caller's context) of dict
# representing the JSON object of which to
# parse the next member
proc json::parseObjectMember {tokens nrTokens tokenCursorName objectDictName} {
upvar $tokenCursorName tokenCursor
upvar $objectDictName objectDict
set token [lindex $tokens $tokenCursor]
incr tokenCursor
set leadingChar [string index $token 0]
if {$leadingChar eq "\""} {
set memberName [unquoteUnescapeString $token]
if {$tokenCursor == $nrTokens} {
unexpected $tokenCursor "END" "\":\""
} else {
set token [lindex $tokens $tokenCursor]
incr tokenCursor
if {$token eq ":"} {
set memberValue [parseValue $tokens $nrTokens tokenCursor]
dict set objectDict $memberName $memberValue
} else {
unexpected $tokenCursor $token "\":\""
}
}
} else {
unexpected $tokenCursor $token "STRING"
}
}
# Parse the members of an object
# @param tokens list of tokens
# @param nrTokens length of $tokens
# @param tokenCursorName name (in caller's context) of variable
# holding current position in $tokens
# @param objectDictName name (in caller's context) of dict
# representing the JSON object of which to
# parse the next member
proc json::parseObjectMembers {tokens nrTokens tokenCursorName objectDictName} {
upvar $tokenCursorName tokenCursor
upvar $objectDictName objectDict
while true {
parseObjectMember $tokens $nrTokens tokenCursor objectDict
set token [lindex $tokens $tokenCursor]
incr tokenCursor
switch -exact $token {
"," {
# continue
}
"\}" {
break
}
default {
unexpected $tokenCursor $token "\",\"|\"\}\""
}
}
}
}
# Parse an object
# @param tokens list of tokens
# @param nrTokens length of $tokens
# @param tokenCursorName name (in caller's context) of variable
# holding current position in $tokens
# @return parsed object (Tcl dict)
proc json::parseObject {tokens nrTokens tokenCursorName} {
upvar $tokenCursorName tokenCursor
if {$tokenCursor == $nrTokens} {
unexpected $tokenCursor "END" "OBJECT"
} else {
set result [dict create]
set token [lindex $tokens $tokenCursor]
if {$token eq "\}"} {
# empty object
incr tokenCursor
} else {
parseObjectMembers $tokens $nrTokens tokenCursor result
}
return $result
}
}
# Parse the elements of an array
# @param tokens list of tokens
# @param nrTokens length of $tokens
# @param tokenCursorName name (in caller's context) of variable
# holding current position in $tokens
# @param resultName name (in caller's context) of the list
# representing the JSON array
proc json::parseArrayElements {tokens nrTokens tokenCursorName resultName} {
upvar $tokenCursorName tokenCursor
upvar $resultName result
while true {
lappend result [parseValue $tokens $nrTokens tokenCursor]
if {$tokenCursor == $nrTokens} {
unexpected $tokenCursor "END" "\",\"|\"\]\""
} else {
set token [lindex $tokens $tokenCursor]
incr tokenCursor
switch -exact $token {
"," {
# continue
}
"\]" {
break
}
default {
unexpected $tokenCursor $token "\",\"|\"\]\""
}
}
}
}
}
# Parse an array
# @param tokens list of tokens
# @param nrTokens length of $tokens
# @param tokenCursorName name (in caller's context) of variable
# holding current position in $tokens
# @return parsed array (Tcl list)
proc json::parseArray {tokens nrTokens tokenCursorName} {
upvar $tokenCursorName tokenCursor
if {$tokenCursor == $nrTokens} {
unexpected $tokenCursor "END" "ARRAY"
} else {
set result {}
set token [lindex $tokens $tokenCursor]
set leadingChar [string index $token 0]
if {$leadingChar eq "\]"} {
# empty array
incr tokenCursor
} else {
parseArrayElements $tokens $nrTokens tokenCursor result
}
return $result
}
}
# Parse a value
# @param tokens list of tokens
# @param nrTokens length of $tokens
# @param tokenCursorName name (in caller's context) of variable
# holding current position in $tokens
# @return parsed value (dict, list, string, number)
proc json::parseValue {tokens nrTokens tokenCursorName} {
upvar $tokenCursorName tokenCursor
if {$tokenCursor == $nrTokens} {
unexpected $tokenCursor "END" "VALUE"
} else {
set token [lindex $tokens $tokenCursor]
incr tokenCursor
set leadingChar [string index $token 0]
switch -exact $leadingChar {
"\{" {
return [parseObject $tokens $nrTokens tokenCursor]
}
"\[" {
return [parseArray $tokens $nrTokens tokenCursor]
}
"\"" {
# quoted string
return [unquoteUnescapeString $token]
}
"t" -
"f" -
"n" {
# bare word: true, false or null
return $token
}
default {
# number?
if {[string is double -strict $token]} {
return $token
} else {
unexpected $tokenCursor $token "VALUE"
}
}
}
}
}
proc json::dict2json {dictVal} {
# XXX: Currently this API isn't symmetrical, as to create proper
# XXX: JSON text requires type knowledge of the input data
set json ""
dict for {key val} $dictVal {
# key must always be a string, val may be a number, string or
# bare word (true|false|null)
if {0 && ![string is double -strict $val]
&& ![regexp {^(?:true|false|null)$} $val]} {
set val "\"$val\""
}
append json "\"$key\": $val," \n
}
return "\{${json}\}"
}
proc json::list2json {listVal} {
return "\[$[join $listVal ,]\]"
}
proc json::string2json {str} {
return "\"$str\""
}