#### # AUTHOR: # AltBlue http://altblue.n0i.net ### # DESCRIPTION: # this makes your bot really smart ;-] # * take care of a 'knowledge' database... # * users are able to question the bot about his knowledge: # !whatis RFC # * admins are able to update his knowledge (delete, add, update) # !learn RFC Request For Comments # !isalso RFC reboot for cooldown # !forget RFC # !reload-dictionary # !save-dictionary #### # CHANGELOG: # * 2002.01.15: # - switched to MySQL storage. # * 2001.03.05: # - made dict-stat to report also the number of writeable entries # * 2000.09.29: # - multiple definition for a term # - append definition to a term (!isalso) # * 2000.09.28: # - read-only variables and the respective additional routines # - primitive dictionary stats. # * 2000.09.27: # - flood protection # - some bugfixes =] # - list/clear dictionary # * 2000.09.26: # - initial version.. lotta fucking work trying to understand # the stupid tcl wacked concepts about regexp =[ #### # CREDITS: # - thanks to tdarugar@binevolve.com for his tcl-sql package # - thanks to Bass and his Seen script that gave me lots of good ideas. # - all the people testing it on N0i.Net #### # # CONFIGURATION: # max. length for each answer set abd(maxlen) 200 # database setup... set abd(db_host) "[HOSTNAME]" set abd(db) "[DATABASE]" set abd(db_user) "[USERNAME]" set abd(db_pass) "[PASSWORD]" set abd(table) "dictionary" set abd(id) "id" set abd(term) "term" set abd(def) "definition" set abd(ro) "readonly" set abd(nick) "nick" set abd(uhost) "uhost" # flood protection, in the form x:y. Any queries beyond x in y seconds is considered a flood and ignored. set abd(flood) 6:15 # switch for ignoring flooders (1=on) set abd(ignore) 1 # define the amount of time a flooder is ignored (minutes). # (this is meaningless if abd(ignore) is 0) set abd(ignore_time) 2 ### bindings bind pub -|- !whatis abd_pub_whatis bind pub -|- !define abd_pub_whatis bind msg -|- whatis abd_msg_whatis bind msg -|- define abd_msg_whatis bind pub fD|fD !learn abd_pub_add bind msg fD|fD learn abd_msg_add bind pub fD|fD !forget abd_pub_del bind msg fD|fD forget abd_msg_del bind pub mE|mE !mark abd_pub_mark bind msg mE|mE mark abd_msg_mark bind pub mE|mE !unmark abd_pub_unmark bind msg mE|mE unmark abd_msg_unmark bind pub -|- !dict-stat abd_pub_stat bind msg -|- dict-stat abd_msg_stat ########################################### #### BE SMART! Don't modify anything below. ################################################################## set abd(version) "ABDict-v2.0" package require sql ########## Flood Protection proc abd_flood {nick uhost} { global abd abd_flood_array if {$abd(flood_num) == 0} {return 0} set i [expr $abd(flood_num) - 1] while {$i >= 1} { set abd_flood_array($i) $abd_flood_array([expr $i - 1]) incr i -1 } set abd_flood_array(0) [unixtime] if {[expr [unixtime] - $abd_flood_array([expr $abd(flood_num) - 1])] <= $abd(flood_time)} { putlog "$abd(version): Flood detected from $nick." if {$abd(ignore)} { newignore [join [maskhost *!*[string trimleft $uhost ~]]] $abd(version) flood $abd(ignore_time) } return 1 } {return 0} } proc abd_flood_init {} { global abd abd_flood_array if {![string match *:* $abd(flood)]} { putlog "$abd(version): var abd(flood) not set correctly." return 0 } set abd(flood_num) [lindex [split $abd(flood) :] 0] set abd(flood_time) [lindex [split $abd(flood) :] 1] set i [expr $abd(flood_num) - 1] while {$i >= 0} { set abd_flood_array($i) 0 incr i -1 } } abd_flood_init ############## WHATIS TERM proc abd_pub_whatis {nick uhost hand chan arg} { if {[abd_flood $nick $uhost]} {return 0} putcmdlog "($chan) $nick: WHATIS $arg" if {$arg == ""} { putserv "NOTICE $nick :Usage: whatis "; return 0} foreach def [abd_search $arg] { putserv "NOTICE $nick :$def" } } proc abd_msg_whatis {nick uhost hand arg} { if {[abd_flood $nick $uhost]} {return 0} putcmdlog "$nick: WHATIS $arg" if {$arg == ""} { putserv "NOTICE $nick :Usage: whatis "; return 0} foreach def [abd_search $arg] { putserv "NOTICE $nick :$def" } } ############## ADD TERM proc abd_pub_add {nick uhost hand chan arg} { if {[abd_flood $nick $uhost]} {return 0} putcmdlog "($chan) $nick: ADD $arg" if {$arg == ""} { putserv "NOTICE $nick :Usage: learn "; return 0 } set term [lindex $arg 0] set definition [lrange $arg 1 end] putserv "PRIVMSG $chan :\001ACTION says to $nick: [abd_add $term $definition $nick $uhost] \001" } proc abd_msg_add {nick uhost hand arg} { if {[abd_flood $nick $uhost]} {return 0} putcmdlog "$nick: ADD $arg" if {$arg == ""} { putserv "NOTICE $nick :Usage: learn "; return 0 } set term [lindex $arg 0] set definition [lrange $arg 1 end] putserv "NOTICE $nick :[abd_add $term $definition $nick $uhost]" } ############## DEL TERM proc abd_pub_del {nick uhost hand chan arg} { if {[abd_flood $nick $uhost]} {return 0} putcmdlog "($chan) $nick: FORGET $arg" if {$arg == ""} { putserv "NOTICE $nick :Usage: forget "; return 0} putserv "PRIVMSG $chan :\001ACTION says to $nick: [abd_del $arg] \001" } proc abd_msg_del {nick uhost hand arg} { if {[abd_flood $nick $uhost]} {return 0} putcmdlog "$nick: FORGET $arg" if {$arg == ""} { putserv "NOTICE $nick :Usage: forget "; return 0} putserv "NOTICE $nick :[abd_del $arg]" } ############## MARK/UNMARK TERMS proc abd_pub_mark {nick uhost hand chan arg} { if {[abd_flood $nick $uhost]} {return 0} putcmdlog "($chan) $nick: MARK $arg" if {$arg == ""} { putserv "NOTICE $nick :Usage: mark "; return 0} putserv "PRIVMSG $chan :\001ACTION says to $nick: [abd_mark 1 $arg] \001" } proc abd_msg_mark {nick uhost hand arg} { if {[abd_flood $nick $uhost]} {return 0} putcmdlog "$nick: MARK $arg" if {$arg == ""} { putserv "NOTICE $nick :Usage: mark "; return 0} putserv "NOTICE $nick :[abd_mark 1 $arg]" } proc abd_pub_unmark {nick uhost hand chan arg} { if {[abd_flood $nick $uhost]} {return 0} putcmdlog "($chan) $nick: UNMARK $arg" if {$arg == ""} { putserv "NOTICE $nick :Usage: unmark "; return 0} putserv "PRIVMSG $chan :\001ACTION says to $nick: [abd_mark 0 $arg] \001" } proc abd_msg_unmark {nick uhost hand arg} { if {[abd_flood $nick $uhost]} {return 0} putcmdlog "$nick: UNMARK $arg" if {$arg == ""} { putserv "NOTICE $nick :Usage: unmark "; return 0} putserv "NOTICE $nick :[abd_mark 0 $arg]" } ########## STAT dictionary proc abd_pub_stat {nick uhost hand chan arg} { if {[abd_flood $nick $uhost]} {return 0} putcmdlog "($chan) $nick: STAT DICTIONARY" putserv "PRIVMSG $chan :\001ACTION says to $nick: [abd_stat] \001" } proc abd_msg_stat {nick uhost hand arg} { if {[abd_flood $nick $uhost]} {return 0} putcmdlog "$nick: STAT DICTIONARY" putserv "NOTICE $nick :[abd_stat]" } ############################################################################### ############################################################################### ######################## INTERNAL ROUTINES #################################### ############################################################################### ############################################################################### ########## SEARCH term proc abd_search {oterm} { global abd regsub -all {'} $oterm {\\'} term set dbh [sql connect $abd(db_host) $abd(db_user) $abd(db_pass)] set res [catch {sql selectdb $dbh $abd(db)} msg] if {$res != 0} { return [list {Ouch, cannot connect to database!}] } sql query $dbh "SELECT COUNT(*) FROM $abd(table) WHERE LCASE($abd(term))=LCASE('$term')" set no [lindex [sql fetchrow $dbh] 0] sql endquery $dbh if {$no < 1} { return [list "Sorry, I do not know what $oterm is."] } set answer "" if {$no == 1} { append answer "AFAIK, $oterm is: " } else { append answer "I have $no definitions for '$oterm'. AFAIK, it could be: " } sql query $dbh "SELECT $abd(def) FROM $abd(table) WHERE LCASE($abd(term))=LCASE('$term')" while {[set row [sql fetchrow $dbh]] != ""} { if {$row == ""} break append answer "'[join $row]', " } sql endquery $dbh sql disconnect $dbh set answer [string trimright $answer {, }] append answer . set lanswer {} set len [string length $answer] if {$len > $abd(maxlen)} { set first 0 set last 0 while {$last < $len} { set first [string wordstart $answer $last] set last [string wordend $answer [expr $first+$abd(maxlen)]] if {$last < $len} { lappend lanswer "[string range $answer $first $last] ..." } { lappend lanswer "[string range $answer $first $last]" } } } { set lanswer [list $answer] } return $lanswer } ########## INSERT term proc abd_add {oterm odef onick ouhost} { global abd regsub -all {'} $oterm {\\'} term regsub -all {'} $odef {\\'} def regsub -all {'} $onick {\\'} nick regsub -all {'} $ouhost {\\'} uhost set dbh [sql connect $abd(db_host) $abd(db_user) $abd(db_pass)] set res [catch {sql selectdb $dbh $abd(db)} msg] if {$res != 0} { return "Ouch, cannot connect to database! ($msg)" } sql query $dbh "SELECT COUNT(*) FROM $abd(table) WHERE LCASE($abd(term))=LCASE('$term') AND LCASE($abd(def))=LCASE('$def')" if {[lindex [sql fetchrow $dbh] 0] != "0"} { sql endquery $dbh sql disconnect $dbh return "I already know that!" } sql endquery $dbh set res [catch {sql exec $dbh "INSERT INTO $abd(table) ($abd(term), $abd(def), $abd(nick), $abd(uhost)) VALUES('$term', '$def', '$nick', '$uhost')"} msg] if {$res != 0} { return "Ouch, cannot add term! ($msg)" } sql disconnect $dbh return "Thanks. I got it!" } ########## DELETE term proc abd_del {oterm} { global abd regsub -all {'} $oterm {\\'} term set dbh [sql connect $abd(db_host) $abd(db_user) $abd(db_pass)] set res [catch {sql selectdb $dbh $abd(db)} msg] if {$res != 0} { return "Ouch, cannot connect to database! ($msg)" } set res [catch {sql exec $dbh "DELETE FROM $abd(table) WHERE LCASE($abd(term))=LCASE('$term') AND $abd(ro)=0"} msg] if {$res != 0} { return "Cannot delete $term! ($msg)" } sql disconnect $dbh return "OK. Let's say I forgot about it ;P" } ########## MARK term proc abd_mark {tag oterm} { global abd regsub -all {'} $oterm {\\'} term set dbh [sql connect $abd(db_host) $abd(db_user) $abd(db_pass)] set res [catch {sql selectdb $dbh $abd(db)} msg] if {$res != 0} { return "Ouch, cannot connect to database! ($msg)" } set res [catch {sql exec $dbh "UPDATE $abd(table) SET $abd(ro)=$tag WHERE LCASE($abd(term))=LCASE('$term')"} msg] if {$res != 0} { return "Cannot mark $term! ($msg)" } sql disconnect $dbh if { $tag == 1 } { return "OK. $term is read-only." } { return "OK. $term is writeable." } } ########## STATUS proc abd_stat {} { global abd set dbh [sql connect $abd(db_host) $abd(db_user) $abd(db_pass)] set res [catch {sql selectdb $dbh $abd(db)} msg] if {$res != 0} { return [list {Ouch, cannot connect to database!}] } set ro 0 set rw 0 set total 0 sql query $dbh "SELECT $abd(term) FROM $abd(table) WHERE $abd(ro)=1 GROUP BY $abd(term)" while {[set row [sql fetchrow $dbh]] != ""} { incr ro incr total } sql endquery $dbh sql query $dbh "SELECT $abd(term) FROM $abd(table) WHERE $abd(ro)=0 GROUP BY $abd(term)" while {[set row [sql fetchrow $dbh]] != ""} { incr rw incr total } sql endquery $dbh sql disconnect $dbh set are "are" if {$rw == 1} { set are "is" } return "I have knowledge of $total terms, of which $ro are marked read-only and $rw $are writeable." } putlog "$abd(version) loaded."