google.tcl 5.0 KB

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