google.tcl 5.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203
  1. #
  2. # 0.3 - ?
  3. # - switch from decode_html to htmlparse::mapEscape
  4. # - fix issue with encoding getting ascii
  5. # - add !g1 for one result
  6. # - strip remaining html from api result
  7. #
  8. # 0.2 - May 10 2010
  9. # - fix for garbled utf chars in api queries
  10. # - added +google channel flag to enable
  11. # - strip html from !convert as some formatting may be present
  12. # - fix decode_html to convert html utf to hex
  13. # - convert <sup></sup> to exponent
  14. #
  15. # 0.1 - Some time in April 2010
  16. # - Initial release
  17. #
  18. # Created Feb 28 2010
  19. #
  20. # Requires Tcl 8.5+
  21. # Requires tcllib for json
  22. #
  23. package require http
  24. package require json
  25. package require htmlparse
  26. namespace eval google {
  27. #variable output_cmd "cd::putnow"
  28. variable output_cmd "putserv"
  29. # Not enforced for API queries
  30. variable useragent "Lynx/2.8.8dev.2 libwww-FM/2.14 SSL-MM/1.4.1"
  31. variable convert_url "http://www.google.ca/search"
  32. variable convert_regexp {<table class=std>.*?<b>(.*?)</b>.*?</table>}
  33. variable api_url "http://ajax.googleapis.com/ajax/services/search/"
  34. variable api_referer "http://www.egghelp.org"
  35. bind pub -|- "!g" google::search
  36. bind pub -|- "!google" google::search
  37. bind pub -|- "!g1" google::search1
  38. bind pub -|- "!news" google::news
  39. bind pub -|- "!images" google::images
  40. bind pub -|- "!convert" google::convert
  41. setudef flag google
  42. }
  43. proc google::convert_fetch {terms} {
  44. http::config -useragent $google::useragent
  45. set query [http::formatQuery q $terms]
  46. set token [http::geturl ${google::convert_url}?${query}]
  47. set data [http::data $token]
  48. set ncode [http::ncode $token]
  49. http::cleanup $token
  50. # debug
  51. #set fid [open "g-debug.txt" w]
  52. #puts $fid $data
  53. #close $fid
  54. if {$ncode != 200} {
  55. error "HTTP query failed: $ncode"
  56. }
  57. return $data
  58. }
  59. proc google::convert_parse {html} {
  60. if {![regexp -- $google::convert_regexp $html -> result]} {
  61. error "Parse error or no result"
  62. }
  63. set result [htmlparse::mapEscapes $result]
  64. # change <sup>num</sup> to ^num (exponent)
  65. set result [regsub -all -- {<sup>(.*?)</sup>} $result {^\1}]
  66. # strip rest of html code
  67. return [regsub -all -- {<.*?>} $result ""]
  68. }
  69. # Query normal html for conversions
  70. proc google::convert {nick uhost hand chan argv} {
  71. if {![channel get $chan google]} { return }
  72. if {[string length $argv] == 0} {
  73. $google::output_cmd "PRIVMSG $chan :Please provide a query."
  74. return
  75. }
  76. if {[catch {google::convert_fetch $argv} data]} {
  77. $google::output_cmd "PRIVMSG $chan :Error fetching results: $data."
  78. return
  79. }
  80. if {[catch {google::convert_parse $data} result]} {
  81. $google::output_cmd "PRIVMSG $chan :Error: $result."
  82. return
  83. }
  84. $google::output_cmd "PRIVMSG $chan :\002$result\002"
  85. }
  86. # Output for results from api query
  87. proc google::output {chan url title content} {
  88. regsub -all -- {(?:<b>|</b>)} $title "\002" title
  89. regsub -all -- {<.*?>} $title "" title
  90. set output "$title @ $url"
  91. $google::output_cmd "PRIVMSG $chan :[htmlparse::mapEscapes $output]"
  92. }
  93. # Return results from API query of $url
  94. proc google::api_fetch {terms url} {
  95. set query [http::formatQuery v "1.0" q $terms safe off]
  96. set headers [list Referer $google::api_referer]
  97. set token [http::geturl ${url}?${query} -headers $headers -method GET]
  98. set data [http::data $token]
  99. set ncode [http::ncode $token]
  100. http::cleanup $token
  101. # debug
  102. #set fid [open "g-debug.txt" w]
  103. #fconfigure $fid -translation binary -encoding binary
  104. #puts $fid $data
  105. #close $fid
  106. if {$ncode != 200} {
  107. error "HTTP query failed: $ncode"
  108. }
  109. return [json::json2dict $data]
  110. }
  111. # Validate input and then return list of results
  112. proc google::api_validate {argv url} {
  113. if {[string length $argv] == 0} {
  114. error "Please supply search terms."
  115. }
  116. if {[catch {google::api_fetch $argv $url} data]} {
  117. error "Error fetching results: $data."
  118. }
  119. set response [dict get $data responseData]
  120. set results [dict get $response results]
  121. if {[llength $results] == 0} {
  122. error "No results."
  123. }
  124. return $results
  125. }
  126. # Query api
  127. proc google::api_handler {chan argv url {num {}}} {
  128. if {[catch {google::api_validate $argv $url} results]} {
  129. $google::output_cmd "PRIVMSG $chan :$results"
  130. return
  131. }
  132. foreach result $results {
  133. if {$num != "" && [incr count] > $num} {
  134. return
  135. }
  136. dict with result {
  137. # $language holds lang in news results, doesn't exist in web results
  138. if {![info exists language] || $language == "en"} {
  139. google::output $chan $unescapedUrl $title $content
  140. }
  141. }
  142. }
  143. }
  144. # Regular API search
  145. proc google::search {nick uhost hand chan argv} {
  146. if {![channel get $chan google]} { return }
  147. google::api_handler $chan $argv ${google::api_url}web
  148. }
  149. # Regular API search, 1 result
  150. proc google::search1 {nick uhost hand chan argv} {
  151. if {![channel get $chan google]} { return }
  152. google::api_handler $chan $argv ${google::api_url}web 1
  153. }
  154. # News from API
  155. proc google::news {nick uhost hand chan argv} {
  156. if {![channel get $chan google]} { return }
  157. google::api_handler $chan $argv ${google::api_url}news
  158. }
  159. # Images from API
  160. proc google::images {nick uhost hand chan argv} {
  161. if {![channel get $chan google]} { return }
  162. google::api_handler $chan $argv ${google::api_url}images
  163. }