slang.tcl 5.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201
  1. #
  2. # slang.tcl - June 24 2010
  3. # by horgh
  4. #
  5. # Requires Tcl 8.5+ and tcllib
  6. #
  7. # Made with heavy inspiration from perpleXa's urbandict script!
  8. #
  9. # Must .chanset #channel +ud
  10. #
  11. # Uses is.gd to shorten long definition URL if isgd.tcl package present
  12. #
  13. package require htmlparse
  14. package require http
  15. namespace eval ud {
  16. # set this to !ud or whatever you want
  17. variable trigger "slang"
  18. # maximum lines to output
  19. variable max_lines 1
  20. # approximate characters per line
  21. variable line_length 400
  22. # show truncated message / url if more than one line
  23. variable show_truncate 1
  24. variable output_cmd "putserv"
  25. variable client "Mozilla/5.0 (compatible; Y!J; for robot study; keyoshid)"
  26. variable url http://www.urbandictionary.com/define.php
  27. variable url_random http://www.urbandictionary.com/random.php
  28. # regex to find the word
  29. # if we have an inexact match, then we may have the word wrapped in <a/>.
  30. variable word_regexp {<div class='word' data-defid='.*?'>\s*?(?:<a class="index".*?<\/a>\s*?)?<span>\s*(?:<a href=\"[^>]+\">)?(.*?)(?:</a>)?\s*?</span>}
  31. variable list_regexp {<div class='text'.*? id='entry_.*?'>.*?</div>}
  32. variable def_regexp {id='entry_(.*?)'>.*?<div class="definition">(.*?)</div>}
  33. setudef flag ud
  34. bind pub -|- $ud::trigger ud::handler
  35. # 0 if isgd package is present
  36. variable isgd_disabled [catch {package require isgd}]
  37. }
  38. proc ud::handler {nick uhost hand chan argv} {
  39. if {![channel get $chan ud]} { return }
  40. set argv [string trim $argv]
  41. set argv [split $argv]
  42. if {[string is digit [lindex $argv 0]]} {
  43. set number [lindex $argv 0]
  44. set query [join [lrange $argv 1 end]]
  45. } else {
  46. set query [join $argv]
  47. set number 1
  48. }
  49. set query [string trim $query]
  50. if {[llength $argv] == 1 && [string is digit [lindex $argv 0]]} {
  51. $ud::output_cmd "PRIVMSG $chan :Usage: $ud::trigger \[#\] <query> (or just $ud::trigger for random definition)"
  52. return
  53. }
  54. if {$query == ""} {
  55. if {[catch {ud::get_random} result]} {
  56. $ud::output_cmd "PRIVMSG $chan :Error: $result"
  57. return
  58. }
  59. ud::output $chan $result
  60. } else {
  61. if {[catch {ud::get_def $query $number} result]} {
  62. $ud::output_cmd "PRIVMSG $chan :Error: $result"
  63. return
  64. }
  65. ud::output $chan $result
  66. }
  67. }
  68. proc ud::output {chan def_dict} {
  69. foreach line [ud::split_line $ud::line_length [dict get $def_dict definition]] {
  70. if {[incr output] > $ud::max_lines} {
  71. if {$ud::show_truncate} {
  72. $ud::output_cmd "PRIVMSG $chan :Output truncated. [ud::def_url $def_dict]"
  73. }
  74. break
  75. }
  76. $ud::output_cmd "PRIVMSG $chan :$line"
  77. }
  78. }
  79. proc ud::get_random {} {
  80. set result [ud::http_fetch $ud::url_random ""]
  81. set word [dict get $result word]
  82. set defs_html [dict get $result definitions]
  83. if {[llength $defs_html] < 1} {
  84. error "Failure finding random definition."
  85. }
  86. return [ud::parse $word [lindex $defs_html 0]]
  87. }
  88. proc ud::get_def {query number} {
  89. set page [expr {int(ceil($number / 7.0))}]
  90. set number [expr {$number - (($page - 1) * 7)}]
  91. set http_query [http::formatQuery term $query page $page]
  92. set result [ud::http_fetch $ud::url $http_query]
  93. set word [dict get $result word]
  94. set defs_html [dict get $result definitions]
  95. if {[llength $defs_html] < $number} {
  96. error "[llength $defs_html] definitions found."
  97. }
  98. return [ud::parse $word [lindex $defs_html [expr {$number - 1}]]]
  99. }
  100. proc ud::http_fetch {url http_query} {
  101. http::config -useragent $ud::client
  102. set token [http::geturl $url -timeout 20000 -query $http_query]
  103. set data [http::data $token]
  104. set ncode [http::ncode $token]
  105. set meta [http::meta $token]
  106. http::cleanup $token
  107. # Follow redirects
  108. if {[regexp -- {30[01237]} $ncode]} {
  109. set new_url [dict get $meta Location]
  110. return [ud::http_fetch $new_url $http_query]
  111. }
  112. if {$ncode != 200} {
  113. error "HTTP fetch error. Code: $ncode"
  114. }
  115. # pull out the word.
  116. if {![regexp -- $ud::word_regexp $data -> word]} {
  117. error "Failed to parse word"
  118. }
  119. set word [string trim $word]
  120. set definitions [regexp -all -inline -- $ud::list_regexp $data]
  121. if {![llength $definitions]} {
  122. error "No definitions found"
  123. }
  124. return [list word $word definitions $definitions]
  125. }
  126. proc ud::parse {word raw_definition} {
  127. if {![regexp $ud::def_regexp $raw_definition -> number definition]} {
  128. error "Could not parse HTML"
  129. }
  130. set definition [htmlparse::mapEscapes $definition]
  131. set definition [regsub -all -- {<.*?>} $definition ""]
  132. set definition [regsub -all -- {\n+} $definition " "]
  133. set definition [string tolower $definition]
  134. return [list number $number word $word definition "$word is $definition"]
  135. }
  136. proc ud::def_url {def_dict} {
  137. set word [dict get $def_dict word]
  138. set number [dict get $def_dict number]
  139. set raw_url ${ud::url}?[http::formatQuery term $word defid $number]
  140. if {$ud::isgd_disabled} {
  141. return $raw_url
  142. } else {
  143. if {[catch {isgd::shorten $raw_url} shortened]} {
  144. return "$raw_url (is.gd error)"
  145. } else {
  146. return $shortened
  147. }
  148. }
  149. }
  150. # by fedex
  151. proc ud::split_line {max str} {
  152. set last [expr {[string length $str] -1}]
  153. set start 0
  154. set end [expr {$max -1}]
  155. set lines []
  156. while {$start <= $last} {
  157. if {$last >= $end} {
  158. set end [string last { } $str $end]
  159. }
  160. lappend lines [string trim [string range $str $start $end]]
  161. set start $end
  162. set end [expr {$start + $max}]
  163. }
  164. return $lines
  165. }
  166. putlog "slang.tcl loaded"