Advertisement
Guest User

Untitled

a guest
Jun 23rd, 2017
73
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 13.89 KB | None | 0 0
  1. ####################################################################
  2. #
  3. # File: util.tcl
  4. #
  5. # Description: Utility library of common functions
  6. #
  7. # Author: makk@EFnet
  8. #
  9. # Date: October 26, 2010
  10. #
  11. ####################################################################
  12.  
  13. package provide util 1.0
  14.  
  15. namespace eval ::util:: {
  16. namespace export -clear loadDatabase s populate timeDiff toGMT toLocal now\
  17. currentYear timezone DST formatShortDate formatDateTime formatWordDate\
  18. formatWordDateTime put putMessage putNotice putAction mbind logStackable\
  19. c /c b /b r /r u /u bindSQL scheduleBackup registerCleanup htmlDecode\
  20. parseHTML geturlex
  21.  
  22. variable ns [namespace current]
  23. variable maxMessageLen 508
  24. variable floodSupport 1
  25. }
  26.  
  27. proc ::util::loadDatabase {db database {sqlScripts {}}} {
  28. global tcl_platform
  29. variable ns
  30.  
  31. foreach item [concat $database $sqlScripts] {
  32. catch {exec chmod 600 $item}
  33. }
  34.  
  35. if {[catch {
  36. if {$tcl_platform(platform) == "unix"} {
  37. load "[pwd]/tclsqlite3.so" "tclsqlite3"
  38. } else {
  39. load "[pwd]/tclsqlite3.dll" "tclsqlite3"
  40. }
  41. sqlite3 $db $database
  42. } error]} {
  43. return -code error "*** Failed to open database '$database': $error"
  44. }
  45.  
  46. foreach script $sqlScripts {
  47. if {[catch {set f [open $script r]} error]} {
  48. return -code error "*** Failed to open SQL script '$script': $error"
  49. } else {
  50. catch {$db eval [read $f]}
  51. catch {close $f}
  52. }
  53. }
  54.  
  55. catch {$db function REGEXP ${ns}::regexpSQL}
  56. return 1
  57. }
  58.  
  59. proc ::util::s {quantity {suffix "s"}} {
  60. return [expr {$quantity == 1 ? "" : $suffix}]
  61. }
  62.  
  63. proc ::util::regexpSQL {expr text} {
  64. if {[catch {set ret [regexp -nocase -- $expr $text]}]} {
  65. # invalid expression
  66. return 0
  67. }
  68. return $ret
  69. }
  70.  
  71. # add list placeholder support - ex: db eval [populate {SELECT * FROM t WHERE u IN(::var)}]
  72. proc ::util::populate {sql} {
  73. set s ""
  74. set pos 0
  75. foreach {first last} [join [regexp -all -indices -inline {::[\w$]+} $sql]] {
  76. append s [string range $sql $pos [expr $first - 1]]
  77. set var [string range $sql [expr $first + 2] $last]
  78. upvar $var list
  79. if {[info exists list]} {
  80. set varName "${var}$"
  81. upvar $varName a
  82. array unset a *
  83. set items {}
  84. set i 0
  85. foreach item $list {
  86. set a($i) $item
  87. lappend items ":${varName}($i)"
  88. incr i
  89. }
  90. append s [join $items ,]
  91. } else {
  92. append s "NULL"
  93. }
  94. set pos [expr $last + 1]
  95. }
  96. return [append s [string range $sql $pos end]]
  97. }
  98.  
  99. # Some sort of Eggdrop/TCL bug results in clock changes not updating properly,
  100. # so we anchor at [unixtime] to be safe
  101.  
  102. proc ::util::timeDiff {date1 {future "away"} {past "ago"}} {
  103. set secs [expr [clock scan $date1 -base [unixtime] -gmt 1] - [unixtime]]
  104. set rel [expr {$secs < 0 ? $past : $future}]
  105. set secs [expr abs($secs)]
  106. set days [expr $secs / (60 * 60 * 24)]
  107. set secs [expr {$days ? 0 : $secs % (60 * 60 * 24)}]
  108. set hrs [expr $secs / (60 * 60)]
  109. set secs [expr $secs % (60 * 60)]
  110. set mins [expr $secs / 60]
  111. set secs [expr {($hrs || $mins) ? 0 : $secs % 60}]
  112. foreach {value unit} [list $days d $hrs h $mins m $secs s] {
  113. if {$value > 0} {
  114. append text "$value$unit "
  115. }
  116. }
  117. return [expr {[info exists text] ? "$text$rel" : "NOW"}]
  118. }
  119.  
  120. proc ::util::toGMT {{date ""}} {
  121. return [clock format [clock scan $date -base [unixtime]] -format "%Y-%m-%d %H:%M:%S" -gmt 1]
  122. }
  123.  
  124. proc ::util::toLocal {{date ""}} {
  125. return [clock format [clock scan $date -base [unixtime] -gmt 1] -format "%Y-%m-%d %H:%M:%S"]
  126. }
  127.  
  128. proc ::util::now {{gmt 1}} {
  129. return [expr {$gmt ? [toGMT] : [toLocal]}]
  130. }
  131.  
  132. proc ::util::currentYear {{gmt 1}} {
  133. return [clock format [unixtime] -format "%Y" -gmt $gmt]
  134. }
  135.  
  136. proc ::util::timezone {{withOffset 0}} {
  137. return [strftime "%Z[expr {$withOffset ? " %z" : ""}]" [unixtime]]
  138. }
  139.  
  140. proc ::util::validTimeZone {tz} {
  141. set timezones {
  142. gmt ut utc bst wet wat at nft nst ndt ast adt est edt cst cdt mst mdt
  143. pst pdt yst ydt hst hdt cat ahst nt idlw cet cest met mewt mest swt
  144. sst eet eest bt it zp4 zp5 ist zp6 wast wadt jt cct jst cast cadt
  145. east eadt gst nzt nzst nzdt idle
  146. }
  147. return [expr {[lsearch -exact $timezones $tz] != -1}]
  148. }
  149.  
  150. proc ::util::DST {} {
  151. set cmp [string compare [clock format [unixtime] -format "%Z"]\
  152. [clock format [clock scan "6 months" -base [unixtime]] -format "%Z"]]
  153. return [expr {($cmp < 0) ? 1 : (($cmp > 0) ? 0 : -1)}]
  154. }
  155.  
  156. proc ::util::wordDay {day} {
  157. if {[regexp {^\d+$} $day]} {
  158. if {$day < 11 || $day > 13} {
  159. switch [string index $day end] {
  160. 1 { return "${day}st" }
  161. 2 { return "${day}nd" }
  162. 3 { return "${day}rd" }
  163. }
  164. }
  165. return "${day}th"
  166. }
  167. return $day
  168. }
  169.  
  170. proc ::util::shortYear {utime format} {
  171. if {[clock format [unixtime] -format "%Y"] == [clock format $utime -format "%Y"]} {
  172. return ""
  173. }
  174. return $format
  175. }
  176.  
  177. proc ::util::formatShortDate {datetime} {
  178. set dt [clock scan $datetime -base [unixtime] -gmt 1]
  179. return [string trimleft [clock format $dt -format "%m/%d[shortYear $dt "/%y"]"] 0]
  180. }
  181.  
  182. proc ::util::formatDateTime {datetime} {
  183. set dt [clock scan $datetime -base [unixtime] -gmt 1]
  184. regsub -all {\s{2,}} [clock format $dt -format "%m/%d[shortYear $dt "/%Y"] %l:%M%P"] " " date
  185. return [string trimleft $date 0]
  186. }
  187.  
  188. proc ::util::formatWordDate {datetime} {
  189. set dt [clock scan $datetime -base [unixtime] -gmt 1]
  190. set date [clock format $dt -format "%b %e[shortYear $dt ", %Y"]"]
  191. return "[lindex $date 0] [wordDay [lrange $date 1 end]]"
  192. }
  193.  
  194. proc ::util::formatWordDateTime {datetime} {
  195. set dt [clock scan $datetime -base [unixtime] -gmt 1]
  196. set date [clock format $dt -format "%b %e[shortYear $dt ", %Y"] at %l:%M%P"]
  197. regsub {:00} $date "" date
  198. return "[lindex $date 0] [wordDay [lindex $date 1]] [lrange $date 2 end]"
  199. }
  200.  
  201. proc ::util::log {loglevel text} {
  202. if {$loglevel >= 1 && $loglevel <= 8} {
  203. return [putloglev $loglevel * $text]
  204. }
  205. return
  206. }
  207.  
  208. proc ::util::logStackable {unick host handle dest text} {
  209. if {$unick == $dest} {
  210. putcmdlog "($unick!$host) !$handle! $text"
  211. } else {
  212. putcmdlog "<<$unick>> !$handle! $text"
  213. }
  214. return 1
  215. }
  216.  
  217. proc ::util::initCapabilities {from keyword text} {
  218. variable floodSupport 0
  219. return 0
  220. }
  221. bind raw - 001 ::util::initCapabilities
  222.  
  223. proc ::util::capabilities {from keyword text} {
  224. variable floodSupport
  225. if {[lsearch -exact [split $text] "CPRIVMSG"] >= 0} {
  226. set floodSupport 1
  227. }
  228. return 0
  229. }
  230. bind raw - 005 ::util::capabilities
  231.  
  232. proc ::util::putfast {text} {
  233. append text "\r\n"
  234. return [putdccraw 0 [string length $text] $text]
  235. }
  236.  
  237. proc ::util::put {text {queue putquick} {loglevel 0} {prefix ""} {suffix ""} {ellipsis "..."}} {
  238. global botname
  239. variable maxMessageLen
  240.  
  241. set maxText [expr $maxMessageLen - [string length $botname]\
  242. - [string length $prefix] - [string length $suffix] - 1]
  243.  
  244. if {$maxText < [string length $text]} {
  245. incr maxText -[string length $ellipsis]
  246. } else {
  247. set ellipsis ""
  248. }
  249.  
  250. set message "$prefix[string range $text 0 $maxText]$ellipsis$suffix"
  251. log $loglevel "\[>\] $message"
  252. return [$queue $message]
  253. }
  254.  
  255. proc ::util::putType {type unick dest text {queue putquick} {loglevel 0}} {
  256. global botnick
  257. variable floodSupport
  258.  
  259. if {$floodSupport} {
  260. foreach chan [concat [list $dest] [channels]] {
  261. if {[validchan $chan] && [isop $botnick $chan] && [onchan $unick $chan]} {
  262. return [put $text $queue $loglevel "C$type $unick $chan :"]
  263. }
  264. }
  265. }
  266. if {[string index $dest 0] != "#" && $queue == "putfast"} {
  267. set queue putquick
  268. }
  269. return [put $text $queue $loglevel "$type $unick :"]
  270. }
  271.  
  272. proc ::util::putMessage {unick dest text {queue putquick} {loglevel 0}} {
  273. return [putType "PRIVMSG" $unick $dest $text $queue $loglevel]
  274. }
  275.  
  276. proc ::util::putNotice {unick dest text {queue putquick} {loglevel 0}} {
  277. return [putType "NOTICE" $unick $dest $text $queue $loglevel]
  278. }
  279.  
  280. proc ::util::putAction {unick dest text {queue putquick} {loglevel 0}} {
  281. return [put $text $queue $loglevel "PRIVMSG $unick :\001ACTION found " "\001"]
  282. }
  283.  
  284. proc ::util::redirect {handler unick host handle text} {
  285. if {[llength $handler] == 1} {
  286. return [$handler $unick $host $handle $unick $text]
  287. }
  288. return [[lindex $handler 0] [lrange $handler 1 end] $unick $host $handle $unick $text]
  289. }
  290.  
  291. proc ::util::mbind {types flags triggers handler} {
  292. variable ns
  293.  
  294. set totalBinds 0
  295. set msgHandler [list ${ns}::redirect $handler]
  296.  
  297. foreach type $types {
  298. set eventHandler $handler
  299. if {$type == "msg" || $type == "msgm"} {
  300. set eventHandler $msgHandler
  301. }
  302. foreach trigger $triggers {
  303. if {$type == "msgm" && [llength $trigger] > 1} {
  304. set trigger [lrange [split $trigger] 1 end]
  305. }
  306. bind $type $flags $trigger $eventHandler
  307. incr totalBinds
  308. }
  309. }
  310. return $totalBinds
  311. }
  312.  
  313. proc ::util::c {color {bgcolor ""}} {
  314. return "\003$color[expr {$bgcolor == "" ? "" : ",$bgcolor"}]"
  315. }
  316. proc ::util::/c {} { return "\003" }
  317. proc ::util::b {} { return "\002" }
  318. proc ::util::/b {} { return "\002" }
  319. proc ::util::r {} { return "\026" }
  320. proc ::util::/r {} { return "\026" }
  321. proc ::util::u {} { return "\037" }
  322. proc ::util::/u {} { return "\037" }
  323.  
  324. # for database maintenance - use with caution!
  325. proc ::util::sql {command db handle idx query} {
  326. putcmdlog "#$handle# $command $query"
  327. if {[catch {$db eval $query row {
  328. set results {}
  329. foreach field $row(*) {
  330. lappend results "[b]$field[/b]($row($field))"
  331. }
  332. putdcc $idx [join $results]
  333. }} error]} {
  334. putdcc $idx "*** SQL query failed: $error"
  335. }
  336. return 0
  337. }
  338.  
  339. proc ::util::bindSQL {command db {flags "n"}} {
  340. variable ns
  341. return [bind dcc $flags $command [list ${ns}::sql $command $db]]
  342. }
  343.  
  344. proc ::util::backup {db dbFile loglevel minute hour day month year} {
  345. set backupFile "$dbFile.bak"
  346. log $loglevel "Backing up $dbFile database to $backupFile..."
  347. catch {
  348. $db backup $backupFile
  349. exec chmod 600 $backupFile
  350. }
  351. return
  352. }
  353.  
  354. proc ::util::scheduleBackup {db dbFile {when "04:00"} {loglevel 0}} {
  355. variable ns
  356. set when [split $when ":"]
  357. set hour [lindex $when 0]
  358. set minute [lindex $when 1]
  359. return [bind time - "$minute $hour * * *" [list ${ns}::backup $db $dbFile $loglevel]]
  360. }
  361.  
  362. proc ::util::cleanup {nsRef db type} {
  363. foreach bind [binds "*${nsRef}::*"] {
  364. foreach {type flags command {} handler} $bind {
  365. catch {unbind $type $flags $command $handler}
  366. }
  367. }
  368. catch {$db close}
  369. namespace delete $nsRef
  370. return
  371. }
  372.  
  373. proc ::util::registerCleanup {nsRef db} {
  374. variable ns
  375. return [bind evnt - prerehash [list ${ns}::cleanup $nsRef $db]]
  376. }
  377.  
  378. proc ::util::geturlex {url args} {
  379. http::config -useragent "Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 6.2; Trident/4.0)"
  380.  
  381. array set URI [::uri::split $url] ;# Need host info from here
  382. foreach x {1 2 3 4 5} {
  383. if {[catch {set token [eval [list http::geturl $url] $args]}]} {
  384. break
  385. }
  386. if {![string match {30[1237]} [::http::ncode $token]]} {
  387. return $token
  388. }
  389. array set meta [string tolower [set ${token}(meta)]]
  390. if {![info exist meta(location)]} {
  391. return $token
  392. }
  393. array set uri [::uri::split $meta(location)]
  394. unset meta
  395. if {$uri(host) == ""} {
  396. set uri(host) $URI(host)
  397. }
  398. # problem w/ relative versus absolute paths
  399. set url [eval ::uri::join [array get uri]]
  400. }
  401. return -1
  402. }
  403.  
  404. array set ::util::htmlEntityMap {
  405. quot \x22 amp \x26 lt \x3C gt \x3E nbsp \xA0 iexcl \xA1 cent \xA2 pound \xA3
  406. curren \xA4 yen \xA5 brvbar \xA6 sect \xA7 uml \xA8 copy \xA9 ordf \xAA
  407. laquo \xAB not \xAC shy \xAD reg \xAE macr \xAF deg \xB0 plusmn \xB1
  408. sup2 \xB2 sup3 \xB3 acute \xB4 micro \xB5 para \xB6 middot \xB7 cedil \xB8
  409. sup1 \xB9 ordm \xBA raquo \xBB frac14 \xBC frac12 \xBD frac34 \xBE
  410. iquest \xBF Agrave \xC0 Aacute \xC1 Acirc \xC2 Atilde \xC3 Auml \xC4
  411. Aring \xC5 AElig \xC6 Ccedil \xC7 Egrave \xC8 Eacute \xC9 Ecirc \xCA
  412. Euml \xCB Igrave \xCC Iacute \xCD Icirc \xCE Iuml \xCF ETH \xD0 Ntilde \xD1
  413. Ograve \xD2 Oacute \xD3 Ocirc \xD4 Otilde \xD5 Ouml \xD6 times \xD7
  414. Oslash \xD8 Ugrave \xD9 Uacute \xDA Ucirc \xDB Uuml \xDC Yacute \xDD
  415. THORN \xDE szlig \xDF agrave \xE0 aacute \xE1 acirc \xE2 atilde \xE3
  416. auml \xE4 aring \xE5 aelig \xE6 ccedil \xE7 egrave \xE8 eacute \xE9
  417. ecirc \xEA euml \xEB igrave \xEC iacute \xED icirc \xEE iuml \xEF eth \xF0
  418. ntilde \xF1 ograve \xF2 oacute \xF3 ocirc \xF4 otilde \xF5 ouml \xF6
  419. divide \xF7 oslash \xF8 ugrave \xF9 uacute \xFA ucirc \xFB uuml \xFC
  420. yacute \xFD thorn \xFE yuml \xFF
  421. ob \x7b cb \x7d bsl \\
  422. #8203 " " #x200b " " ndash - #8211 - #x2013 - mdash -- #8212 -- #x2014 --
  423. circ ^ #710 ^ #x2c6 ^ tilde ~ #732 ~ #x2dc ~
  424. lsquo ' #8216 ' #x2018 ' rsquo ' #8217 ' #x2019 ' sbquo ' #8218 ' #x201a '
  425. ldquo \" #8220 \" #x201c \" rdquo \" #8221 \" #x201d \" bdquo \" #8222 \" #x201e \"
  426. dagger | #8224 | #x2020 | Dagger | #8225 | #x2021 |
  427. lsaquo < #8249 < #x2039 < rsaquo > #8250 > #x203a >
  428. }
  429.  
  430. proc ::util::getHTMLEntity {text {unknown ?}} {
  431. variable htmlEntityMap
  432. set result $unknown
  433. catch {set result $htmlEntityMap($text)}
  434. return $result
  435. }
  436.  
  437. proc ::util::htmlDecode {text} {
  438. if {![regexp & $text]} {
  439. return $text
  440. }
  441. regsub -all {([][$\\])} $text {\\\1} new
  442. regsub -all {&(#[xX]?[\da-fA-F]{1,4});} $new {[getHTMLEntity [string tolower \1] "\x26\1;"]} new
  443. regsub -all {&#(\d+);} $new {[format %c [scan \1 %d tmp;set tmp]]} new
  444. regsub -all {&#[xX]([\da-fA-F]+);} $new {[format %c [scan [expr "0x\1"] %d tmp;set tmp]]} new
  445. regsub -all {&([a-zA-Z]+);} $new {[getHTMLEntity \1]} new
  446. return [subst $new]
  447. }
  448.  
  449. proc ::util::parseHTML {html {cmd testParser} {start hmstart}} {
  450. regsub -all \{ $html {\&ob;} html
  451. regsub -all \} $html {\&cb;} html
  452. regsub -all {\\} $html {\&bsl;} html
  453. set w " \t\r\n" ;# white space
  454. set exp <(/?)(\[^$w>]+)\[$w]*(\[^>]*)>
  455. set sub "\}\n$cmd {\\2} {\\1} {\\3} \{"
  456. regsub -all $exp $html $sub html
  457. eval "$cmd {$start} {} {} {$html}"
  458. eval "$cmd {$start} / {} {}"
  459. }
  460.  
  461. proc ::util::testParser {tag state props body} {
  462. if {$state == ""} {
  463. set msg "Start $tag"
  464. if {$props != ""} {
  465. set msg "$msg with args: $props"
  466. }
  467. set msg "$msg\n$body"
  468. } else {
  469. set msg "End $tag"
  470. }
  471. putlog $msg
  472. }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement