#by wiebe @ QuakeNet


#script uses chase.tcl for nick chasing (can work without)
#
#script uses cprivmsg.tcl (can work without)
#
#?? [<channel>] <entry> <nick>
#	when <nick> is a channel op, the script assumes an error
#	and does not say it to <nick>
#
#commands:
# pub:
# "botnick term"
# "botnick help term"
# msg:
# /msg bot term
# /msg bot help term
# dcc:
# term


#channel flags
#+term			set ?? trigger on/off
#+term-noglobal		global terms are ignored
#+term-globaloverride	global terms override channel terms
#+term-silent		unknown users get a private reply
#+term-nounknown	unknown users are ignored completly
#+term-op		any @ can redirect to a nick
#+term-hop		any % and @ can redirect to a nick
#+term-voice		any +, % and @ can redirect to a nick
#+term-msg		sent replies with privmsg instead of by notice
#+term-share		channel terms can  be shown on other chans
# term-flood		a term will not be repeated in the channel for this duration in seconds
#+term-redirect		auto use channel specified with term-redirectchan
# term-redirectchan	when +term-redirect, and no chan is used, auto use this chan
#+term-announce		set on/off channel announcements (use "botnick term announce")
# term-announcedelay	time in minutes between announcements


#botnet
#bot flag 1 = share database
#bot flag 2 = bot acts as hub
#
#Bot A set with 12 (on itself and Bot B)
#Bot B set with 1 (on itself and Bot A)
#Bot A and B share entries when linked
#when bots A and B link, Bot A sends database to Bot B


########################################################################
# settings
#
########################################################################

#file to save database
set termsetting(file) "termdb.txt"

#max allowed lines per entry (item item_2 .. item_N)
set termsetting(maxlines) "5"

#key to use for encrypting data sent over botnet
set termsetting(key) "1234567890"

#reload database on every check? 1=yes, 0=no
#only needed if the database can be altered by other means than this script/bot
set termsetting(reload) "0"


########################################################################
# create flags
#
########################################################################

setudef flag term
setudef flag term-announce
setudef flag term-globaloverride
setudef flag term-noglobal
setudef flag term-silent
setudef flag term-nounknown
setudef flag term-msg
setudef flag term-voice
setudef flag term-hop
setudef flag term-op
setudef flag term-share
setudef flag term-redirect
setudef int term-flood
setudef int term-announcedelay
setudef str term-redirectchan



########################################################################
# help term
# help for channel commands
#
########################################################################
bind pubm omn|fvlomn "% ${botnet-nick} help term" term:term:help:pub
bind pubm omn|fvlomn "% ${botnet-nick} help term *" term:term:help:pub

proc term:term:help:pub { nick uhost handle chan text } {
  set cmd [lindex [split $text] 3]

#add
  if { [string equal -nocase $cmd add] } {
    lappend output "Usage: term add <channel> <term> <description>"
    lappend output "Adds an entry to the term database for the list with the given description."
    lappend output "Channel name \"global\" can be used for the global list."
    lappend output "Entries can have multiple lines (item, item_2, item_3, etc.)"

#mod
  } elseif { [string equal -nocase $cmd mod] } {
    lappend output "Usage: term mod <channel> <term> <description>"
    lappend output "Modifies an entry in the term database."
    lappend output "Channel name \"global\" can be used for the global list."

#del
  } elseif { [string equal -nocase $cmd del] } {
    lappend output "Usage: term del <channel> <term>"
    lappend output "Deletes an entry from the term database."
    lappend output "Channel name \"global\" can be used for the global list."

#info
  } elseif { [string equal -nocase $cmd info] } {
    lappend output "Usage: term info <channel> <term>"
    lappend output "Shows info for an entry in the term database."
    lappend output "Channel name \"global\" can be used for the global list."

#search
  } elseif { [string equal -nocase $cmd search] } {
    lappend output "Usage: term search <channel> <term> \[<description>\]"
    lappend output "Searches the term database, wildcards are supported in <term> and <description>."
    lappend output "Channel name \"global\" can be used for the global list."

#show
  } elseif { [string equal -nocase $cmd show] } {
    lappend output "Usage: term show \[<channel>\] <term> \[<nick>\]"
    lappend output "Same as the \"??\" command, but forces this bot to respond."
    lappend output "Channel name \"global\" can be used for the global list."

#announce
  } elseif { [string equal -nocase $cmd announce] } {
    lappend output "Usage: term announce <channel> on|off \[<delay>\]"
    lappend output "Enables or disables announcements. Add the text to announce under entry \"announce\" (supports multiple lines, same as add). Delay is given in minutes."

#unknown/no cmd
  } else {
    lappend output "Usage: term add|mod|del|info|search|announce"
    lappend output "See \"help term <subcommand>\" for help on a subcommand."
  }

#output
  if { [catch {set x [cnotice $nick $output]} error] || !$x } {
    foreach t $output { puthelp "NOTICE $nick :$t" }
  }
  putloglev c $chan "[lindex [split $text] 1]: $nick $uhost $handle $chan [join [lrange [split $text] 2 end]]"
}



########################################################################
# help term
# help for private commands
#
########################################################################
bind msgm omn|fvlomn "help term" term:term:help:msg
bind msgm omn|fvlomn "help term *" term:term:help:msg

proc term:term:help:msg { nick uhost handle text } {
  set cmd [lindex [split $text] 2]

#add
  if { [string equal -nocase $cmd add] } {
    lappend output "Usage: term add <channel> <term> <description>"
    lappend output "Adds an entry to the term database for the list with the given description."
    lappend output "Channel name \"global\" can be used for the global list."
    lappend output "Entries can have multiple lines (item, item_2, item_3, etc.)"

#mod
  } elseif { [string equal -nocase $cmd mod] } {
    lappend output "Usage: term mod <channel> <term> <description>"
    lappend output "Modifies an entry in the term database."
    lappend output "Channel name \"global\" can be used for the global list."

#del
  } elseif { [string equal -nocase $cmd del] } {
    lappend output "Usage: term del <channel> <term>"
    lappend output "Deletes an entry from the term database."
    lappend output "Channel name \"global\" can be used for the global list."

#info
  } elseif { [string equal -nocase $cmd info] } {
    lappend output "Usage: term info <channel> <term>"
    lappend output "Shows info for an entry in the term database."
    lappend output "Channel name \"global\" can be used for the global list."

#search
  } elseif { [string equal -nocase $cmd search] } {
    lappend output "Usage: term search <channel> <term> \[<description>\]"
    lappend output "Searches the term database, wildcards are supported in <term> and <description>."
    lappend output "Channel name \"global\" can be used for the global list."

#announce
  } elseif { [string equal -nocase $cmd announce] } {
    lappend output "Usage: term announce <channel> on|off \[<delay>\]"
    lappend output "Enables or disables announcements. Add the text to announce under entry \"announce\" (supports multiple lines, same as add). Delay is given in minutes."

#unknown/no cmd
  } else {
    lappend output "Usage: term add|mod|del|info|search|announce"
    lappend output "See \"help term <subcommand>\" for help on a subcommand."
  }

#output
  if { [catch {set x [cnotice $nick $output]} error] || !$x } {
    foreach t $output { puthelp "NOTICE $nick :$t" }
  }
  putcmdlog "($nick!$uhost) !$handle! $text"
}



########################################################################
# term channel command
# 'botnick term'
#
########################################################################
bind pubm omn|fvlomn "% ${botnet-nick} term" c_term:term:pub
bind pubm omn|fvlomn "% ${botnet-nick} term *" c_term:term:pub

proc c_term:term:pub { nick uhost handle chan text } {
  set cmd [lindex [split $text] 2]

#add
  if { [string equal -nocase $cmd add] } {
    set result [term:add $handle [join [lrange [split $text] 3 end]]]
    set output [lrange $result 1 end]
    if { ![lindex $result 0] } { lappend output "Usage: term add <channel> <term> <description>" }

#mod
  } elseif { [string equal -nocase $cmd mod] } {
    set result [term:mod $handle [join [lrange [split $text] 3 end]]]
    set output [lrange $result 1 end]
    if { ![lindex $result 0] } { lappend output "Usage: term mod <channel> <term> <description>" }

#del
  } elseif { [string equal -nocase $cmd del] } {
    set result [term:del $handle [join [lrange [split $text] 3 4]]]
    set output [lrange $result 1 end]
    if { ![lindex $result 0] } { lappend output "Usage: term del <channel> <term>" }

#info
  } elseif { [string equal -nocase $cmd info] } {
    set result [term:info $handle [join [lrange [split $text] 3 4]]]
    set output [lrange $result 1 end]
    if { ![lindex $result 0] } { lappend output "Usage: term info <channel> <term>" }

#search
  } elseif { [string equal -nocase $cmd search] } {
    set result [term:search $handle [join [lrange [split $text] 3 end]] 10]
    set output [lrange $result 1 end]

    foreach part $output {
      foreach line [term:breakup CNOTICE $nick $part] {
        lappend output2 $line
      }
    }
    set output $output2
    if { ![lindex $result 0] } { lappend output "Usage: term search <channel> <term> \[<description>\]" }

#show
  } elseif { [string equal -nocase $cmd show] } {
    if { ![channel get $chan term] } { return 0 }
    term:check:pub $nick $uhost $handle $chan [join [lrange [split $text] 2 end]] 1
    set output ""

#announce
  } elseif { [string equal -nocase $cmd announce] } {
    set result [term:announce $handle [join [lrange [split $text] 3 5]]]
    set output [lrange $result 1 end]
    if { ![lindex $result 0] } { lappend output "Usage: term announce <channel> on|off \[<delay>\]" }

#unknown cmd
  } else {
    lappend output "Usage: term show|add|mod|del|info|search|announce"
  }

#output
  if { [catch {set x [cnotice $nick $output]} error] || !$x } {
    foreach t $output { puthelp "NOTICE $nick :$t" }
  }
  putloglev c $chan "[lindex [split $text] 1]: $nick $uhost $handle $chan [join [lrange [split $text] 2 end]]"
}



########################################################################
# term private command
# /msg bot term
#
########################################################################
bind msg omn|fvlomn term term:term:msg

proc term:term:msg { nick uhost handle text } {
  set cmd [lindex [split $text] 0]

#add
  if { [string equal -nocase $cmd add] } {
    set text [join [lrange [split $text] 1 end]]
    set result [term:add $handle $text]
    set output [lrange $result 1 end]
    if { ![lindex $result 0] } { lappend output "Usage: term add <channel> <term> <description>" }

#mod
  } elseif { [string equal -nocase $cmd mod] } {
    set text [join [lrange [split $text] 1 end]]
    set result [term:mod $handle $text]
    set output [lrange $result 1 end]
    if { ![lindex $result 0] } { lappend output "Usage: term mod <channel> <term> <description>" }

#del
  } elseif { [string equal -nocase $cmd del] } {
    set result [term:del $handle [join [lrange [split $text] 1 2]]]
    set output [lrange $result 1 end]
    if { ![lindex $result 0] } { lappend output "Usage: term del <channel> <term>" }

#info
  } elseif { [string equal -nocase $cmd info] } {
    set result [term:info $handle [join [lrange [split $text] 1 2]]]
    set output [lrange $result 1 end]
    if { ![lindex $result 0] } { lappend output "Usage: term info <channel> <term>" }

#search
  } elseif { [string equal -nocase $cmd search] } {
    set text [join [lrange [split $text] 1 end]]
    set result [term:search $handle $text 10]
    set output [lrange $result 1 end]

    foreach part $output {
      foreach line [term:breakup CNOTICE $nick $part] {
        lappend output2 $line
      }
    }
    set output $output2
    if { ![lindex $result 0] } { lappend output "Usage: term search <channel> <term> \[<description>\]" }

#announce
  } elseif { [string equal -nocase $cmd announce] } {
    set result [term:announce $handle [join [lrange [split $text] 1 3]]]
    set output [lrange $result 1 end]
    if { ![lindex $result 0] } { lappend output "Usage: term announce <channel> on|off \[<delay>\]" }

#unknown cmd
  } else {
    lappend output "Usage: term add|mod|del|info|search|announce"
  }

#output
  if { [catch {set x [cnotice $nick $output]} error] || !$x } {
    foreach t $output { puthelp "NOTICE $nick :$t" }
  }
  return 1
}



########################################################################
# term dcc command
#
########################################################################
bind dcc -|- term term:term:dcc

proc term:term:dcc { handle idx text } {
  set cmd [lindex [split $text] 0]
  set text "[join [lrange [split $text] 1 2]] [string range [join [lrange [split $text] 3 end]] 0 509]"

#add
  if { [string equal -nocase $cmd add] } {
    set result [term:add $handle $text]
    set output [lrange $result 1 end]
    if { ![lindex $result 0] } {
      lappend output "Usage: term add <channel> <term> <description>"
      lappend output "Adds an entry to the term database for the list with the given description."
      lappend output "Channel name \"global\" can be used for the global list."
      lappend output "Entries can have multiple lines (item, item_2, item_3, etc.)"
    }

#mod
  } elseif { [string equal -nocase $cmd mod] } {
    set result [term:mod $handle $text]
    set output [lrange $result 1 end]
    if { ![lindex $result 0] } {
      lappend output "Usage: term mod <channel> <term> <description>"
      lappend output "Modifies an entry in the term database."
      lappend output "Channel name \"global\" can be used for the global list."
    }

#del
  } elseif { [string equal -nocase $cmd del] } {
    set result [term:del $handle [join [lrange [split $text] 0 1]]]
    set output [lrange $result 1 end]
    if { ![lindex $result 0] } {
      lappend output "Usage: term del <channel> <term>"
      lappend output "Deletes an entry from the term database."
      lappend output "Channel name \"global\" can be used for the global list."
    }

#info
  } elseif { [string equal -nocase $cmd info] } {
    set result [term:info $handle [join [lrange [split $text] 0 1]]]
    set output [lrange $result 1 end]
    if { ![lindex $result 0] } {
      lappend output "Usage: term info <channel> <term>"
      lappend output "Shows info for an entry in the term database."
      lappend output "Channel name \"global\" can be used for the global list."
    }

#search
  } elseif { [string equal -nocase $cmd search] } {
    set result [term:search $handle $text 150]
    set output [lrange $result 1 end]
    if { ![lindex $result 0] } {
      lappend output "Usage: term search <channel> <term> \[<description>\]"
      lappend output "Searches the term database, wildcards are supported in <term> and <description>."
      lappend output "Channel name \"global\" can be used for the global list."
    }


#announce
  } elseif { [string equal -nocase $cmd announce] } {
    set result [term:announce $handle $text]
    set output [lrange $result 1 end]
    if { ![lindex $result 0] } {
      lappend output "Usage: term announce <channel> on|off \[<delay>\]"
      lappend output "Usage: term announce <channel> on|off \[<delay>\]"
      lappend output "Enables or disables announcements. Add the text to announce under entry \"announce\" (supports multiple lines, same as add). Delay is given in minutes."
    }

#load
  } elseif { [string equal -nocase $cmd load] } {
    term:load
    lappend output "Reloaded term database."

#unknown cmd
  } else {
    lappend output "Usage: term add|mod|del|info|search|announce|load"
    lappend output "See \"term <subcommand>\" for help on a subcommand."
  }

#output
  foreach t $output { putidx $idx $t }
  return 1
}



########################################################################
# add
# this central proc is used to ADD entries to the database
# parameter text is 'channel term description'
# returns a list of the result(s)
# first list element is 1 (success) or 0 (failure)
#
########################################################################
proc term:add { handle text } {
  global termdb termdbi
  term:reload
#set variables
  set channel [lindex [split $text] 0]
  set channeli [string tolower $channel]
  set term [lindex [split $text] 1]
  set termi [string tolower $term]
  set desc [join [lrange [split $text] 2 end]]
  set ts [clock seconds]
  if { [string equal $channeli global] } {
    set listname "global list"
  } else {
    set listname "list of $channel"
  }
#no channel
  if { [string equal $channeli ""] } {
    lappend result 0
    lappend result "No channel specified."
#no valid channel
  } elseif { ![string match \#* $channeli] && ![string equal $channeli global] } {
    lappend result 0
    lappend result "No valid channel specified."
#no term
  } elseif { [string equal $termi ""] } {
    lappend result 0
    lappend result "No term specified."
#no valid term
  } elseif { [string equal $termi global] || [string match \#* $termi] } {
    lappend result 0
    lappend result "No valid term specified (\"global\" is not allowed and can not start with \"#\")."
#no description
  } elseif { [string equal $desc ""] } {
    lappend result 0
    lappend result "No description specified."
#term too long
  } elseif { [string length $termi] > "30" } {
    lappend result 0
    lappend result "Term too long, max allowed is 30 chars."
#no access to add global entries
  } elseif { [string equal $channeli global] && ![matchattr $handle omn] } {
    lappend result 0
    lappend result "No access to add global entries."
#no access or unknown channel
  } elseif { ![string equal $channeli global] && (![validchan $channeli] || ![matchattr $handle omn|omn $channeli]) } {
    lappend result 0
    lappend result "No access or unknown channel $channel"
#already added
  } elseif { [info exists termdb($channeli,$termi)] } {
    lappend result 1
    lappend result "An entry with the name \"$term\" already exists on ${listname}."
#add it
  } else {
    set termdb($channeli,$termi) $desc
    set termdbi($channeli,$termi) "$handle $ts"
    term:send $channeli $termi add
    term:save
    lappend result 1
    lappend result "Added \"$term\" to $listname with description:"
    lappend result $desc
  }
  return $result
}



########################################################################
# mod
# this central proc is used to MOD (modify) entries in the database
# parameter text is 'channel term description'
# returns a list of the result(s)
# first list element is 1 (success) or 0 (failure)
#
########################################################################
proc term:mod { handle text } {
  global termdb termdbi
  term:reload
#set variables
  set channel [lindex [split $text] 0]
  set channeli [string tolower $channel]
  set term [lindex [split $text] 1]
  set termi [string tolower $term]
  set desc [join [lrange [split $text] 2 end]]
  set ts [clock seconds]
  if { [string equal $channeli global] } {
    set listname "global list"
  } else {
    set listname "list of $channel"
  }
#no channel
  if { [string equal $channeli ""] } {
    lappend result 0
    lappend result "No channel specified."
#no valid channel
  } elseif { ![string match \#* $channeli] && ![string equal $channeli global] } {
    lappend result 0
    lappend result "No valid channel specified."
#no term
  } elseif { [string equal $termi ""] } {
    lappend result 0
    lappend result "No term specified."
#no description
  } elseif { [string equal $desc ""] } {
    lappend result 0
    lappend result "No description specified."
#no access to mod global entries
  } elseif { [string equal $channeli global] && ![matchattr $handle omn] } {
    lappend result 0
    lappend result "No access to mod global entries."
#no access or unknown channel
  } elseif { ![string equal $channeli global] && (![validchan $channeli] || ![matchattr $handle omn|omn $channeli]) } {
    lappend result 0
    lappend result "No access or unknown channel $channel"
#not added
  } elseif { ![info exists termdb($channeli,$termi)] } {
    lappend result 1
    lappend result "An entry with the name \"$term\" does not exist on ${listname}."
#mod it
  } else {
    lappend result 1
    lappend result "Modified \"$term\" in $listname with description:"
    lappend result "From: $termdb($channeli,$termi)"
    lappend result "To: $desc"
    set termdb($channeli,$termi) $desc
    set termdbi($channeli,$termi) "$handle $ts"
    term:send $channeli $termi add
    term:save
  }
  return $result
}



########################################################################
# del
# this central proc is used to DEL (delete) entries from the database
# parameter text is 'channel term'
# returns a list of the result(s)
# first list element is 1 (success) or 0 (failure)
#
########################################################################
proc term:del { handle text } {
  global termdb termdbi
  term:reload
#set variables
  set channel [lindex [split $text] 0]
  set channeli [string tolower $channel]
  set term [lindex [split $text] 1]
  set termi [string tolower $term]
  if { [string equal $channeli global] } {
    set listname "global list"
  } else {
    set listname "list of $channel"
  }
#no channel
  if { [string equal $channeli ""] } {
    lappend result 0
    lappend result "No channel specified."
#no valid channel
  } elseif { ![string match \#* $channeli] && ![string equal $channeli global] } {
    lappend result 0
    lappend result "No valid channel specified."
#no term
  } elseif { [string equal $termi ""] } {
    lappend result 0
    lappend result "No term specified."
#no access to delete global entries
  } elseif { [string equal $channeli global] && ![matchattr $handle omn] } {
    lappend result 0
    lappend result "No access to delete global entries."
#no access or unknown channel
  } elseif { ![string equal $channeli global] && (![validchan $channeli] || ![matchattr $handle omn|omn $channeli]) } {
    lappend result 0
    lappend result "No access or unknown channel $channel"
#not added
  } elseif { ![info exists termdb($channeli,$termi)] } {
    lappend result 1
    lappend result "An entry with the name \"$term\" does not exist on ${listname}."
#del it
  } else {
    lappend result 1
    lappend result "Deleted \"$term\" from $listname with description:"
    set entries [term:findall $channeli $termi]
#delete all lines of it
    foreach entry $entries {
      lappend result $termdb($channeli,$entry)
      unset termdb($channeli,$entry)
      unset termdbi($channeli,$entry)
      term:send $channeli $entry del
    }
    term:save
  }
  return $result
}



########################################################################
# info
# this central proc is used to get INFO from entries in the database
# parameter text is 'channel term'
# returns a list of the result(s)
# first list element is 1 (success) or 0 (failure)
#
########################################################################
proc term:info { handle text } {
  global termdb termdbi
  term:reload
#set variables
  set channel [lindex [split $text] 0]
  set channeli [string tolower $channel]
  set term [lindex [split $text] 1]
  set termi [string tolower $term]
  if { [string equal $channeli global] } {
    set listname "global list"
  } else {
    set listname "list of $channel"
  }
#no channel
  if { [string equal $channeli ""] } {
    lappend result 0
    lappend result "No channel specified."
#no valid channel
  } elseif { ![string match \#* $channeli] && ![string equal $channeli global] } {
    lappend result 0
    lappend result "No valid channel specified."
#no term
  } elseif { [string equal $termi ""] } {
    lappend result 0
    lappend result "No term specified/"
#no access to view global info
  } elseif { [string equal $channeli global] && ![matchattr $handle omn] } {
    lappend result 0
    lappend result "No access to view info of global entries."
#no access or unknown channel
  } elseif { ![string equal $channeli global] && (![validchan $channeli] || ![matchattr $handle omn|omn $channeli]) } {
    lappend result 0
    lappend result "No access or unknown channel $channel"
#not added
  } elseif { ![info exists termdb($channeli,$termi)] && [string equal [set termi [term:findbest $channeli $termi]] ""] } {
    lappend result 1
    lappend result "An entry with the name \"$term\" does not exist on ${listname}."
#info
  } else {
    if { ![string equal -nocase $term $termi] } { set term $termi }
    set desc $termdb($channeli,$termi)
    set by $termdbi($channeli,$termi)
    set ts [lindex [split $by] 1]
    set by [lindex [split $by] 0]
    set ts "on [clock format $ts -format %d/%m/%y] ([term:ts $ts] ago)"
    lappend result 1
    lappend result "Entry \"$term\" on $listname was added by $by $ts"
  }
  return $result
}



########################################################################
# search
# this central proc is used to SEARCH entries in the database
# parameter text is 'channel term description'
# returns a list of the result(s)
# first list element is 1 (success) or 0 (failure)
#
########################################################################
proc term:search { handle text limit } {
  global termdb
  term:reload
#set variables
  set channel [lindex [split $text] 0]
  set channeli [string tolower $channel]
  set term [lindex [split $text] 1]
  set termi [string tolower $term]
  set desc [join [lrange [split $text] 2 end]]
  if { [string equal $desc ""] } { set desc * }
  set desci $desc
  if { [string equal $channeli global] } {
    set listname "global list"
  } else {
    set listname "list of $channel"
  }
#no channel
  if { [string equal $channeli ""] } {
    lappend result 0
    lappend result "No channel specified."
#no valid channel
  } elseif { ![string match \#* $channeli] && ![string equal $channeli global] } {
    lappend result 0
    lappend result "No valid channel specified."
#no term
  } elseif { [string equal $termi ""] } {
    lappend result 0
    lappend result "No term specified."
#no access to search global list
  } elseif { [string equal $channeli global] && ![matchattr $handle omn] } {
    lappend result 0
    lappend result "No access to search the global list."
#no access or unknown channel
  } elseif { ![string equal $channeli global] && (![validchan $channeli] || ![matchattr $handle omn|fvlomn $channeli]) } {
    lappend result 0
    lappend result "No access or unknown channel $channel"
#search
  } else {
    lappend result 1
    regsub -all {\[} $termi {\\[} termi
    regsub -all {\]} $termi {\\]} termi
    regsub -all {\\} $termi {\\\\} termi
    regsub -all {\[} $desci {\\[} desci
    regsub -all {\]} $desci {\\]} desci
    regsub -all {\\} $desci {\\\\} desci
    set x 0
    foreach name [lsort [array names termdb]] {
      if { [string match -nocase $channeli,$termi $name] && [string match -nocase $desci $termdb($name)] } {
        set fterm [join [lrange [split $name ,] 1 end] ,]
        set fdesc $termdb($name)
        if { [string equal [llength $result] 1] } { lappend result "Listing entries.." }
        incr x 1
        if { [string equal $x [expr $limit +1]] && ![string equal $limit 0] } {
          lappend result "More than $limit results, aborting.."
          break
        }
        lappend result "$fterm - $fdesc"
      }
    }
    if { [string equal [llength $result] 1] } {
      lappend result "No entries found matching entry name \"$term\" and description \"$desc\" on $listname"
    } else {
      lappend result "End of list for entries matching entry name \"$term\" and description \"$desc\" on $listname"
    }
  }
  return $result
}



########################################################################
# anounce
# this central proc controles the ANNOUNCE feature
# parameter text is 'channel on|off delay'
# returns a list of the result(s)
# first list element is 1 (success) or 0 (failure)
#
########################################################################
proc term:announce { handle text } {
  global termdb
#set variables
  set channel [lindex [split $text] 0]
  set channeli [string tolower $channel]
  set status [lindex [split $text] 1]
  set delay [lindex [split $text] 2]
  set result 0
  if { [string equal $delay ""] && [validchan $channel] } {
    set delay [channel get $channel term-announcedelay]
    if { $delay <= "5" } { set delay 5 }
  }
#no channel
  if { [string equal $channel ""] } {
    lappend result "No channel specified."
#no access or unknown channel
  } elseif { ![validchan $channel] || ![matchattr $handle omn|omn $channel] } {
    lappend result "No access or unknown channel $channel"
#no status
  } elseif { [string equal $status ""] } {
    lappend result "No status specified."
#no valid status
  } elseif { ![string equal -nocase $status on] && ![string equal -nocase $status off] } {
    lappend result "No valid value for status specified (should be on or off)."
#no valid delay
  } elseif { [string equal -nocase $status on] && ([string equal $delay ""] || ![string is int $delay] || $delay <= "0") } {
    lappend result "No valid delay specified (delay is given in minutes)."
#off
  } elseif { [string equal -nocase $status off] } {
    set result 1
    if { [channel get $channel term-announce] } {
      channel set $channel -term-announce
      lappend result "Term announcement disabled on $channel"
    } else {
      lappend result "Term announcement already disabled on $channel"
    }
#on
  } elseif { [string equal -nocase $status on] } {
    set result 1
    term:reload
    if { ![info exists termdb($channeli,announce)] } {
      lappend result "No entry with name \"announce\" exists on term list for $channel"
    } else {
      if { $delay <= "5" } { set delay 5 }
      channel set $channel +term-announce
      channel set $channel term-announcedelay $delay
      lappend result "Term announcement enabled with delay $delay minutes on $channel"
      lappend result "Announcing following text:"
      foreach name [term:findall $channeli announce] { lappend result $termdb($channeli,$name) }
    }
  }
  return $result
}



########################################################################
# announcetime
# this proc makes the announcements
#
########################################################################
bind time - "* * * * *" term:announcetime

proc term:announcetime { mi ho da mo ye } {
  global termdb
  set now [expr [unixtime] / 60]
#check channels
  foreach chan [channels] {
    set chan [string tolower $chan]
    if { [channel get $chan inactive] } { continue }
    if { ![botonchan $chan] } { continue }
    if { ![channel get $chan term-announce] } { continue }
    set delay [channel get $chan term-announcedelay]
    if { $delay <= "5" } { set delay 5 }
    if { ![info exists termdb($chan,announce)] } {
      channel set $chan -term-announce
      continue
    }
#multibot
    if { ![term:whichbot $chan $now] } { continue }
#only when unixtime % delay equals 0
    if { ![string equal [expr round(fmod($now,$delay))] 0] } { continue }
#get all lines
    foreach name [term:findall $chan announce] {
      set line $termdb($chan,$name)
#strip control codes
      if { [string match *c* [lindex [split [getchanmode $chan]] 0]] } {
        set line [stripcodes bcru $line]
      }
#sent
      puthelp "PRIVMSG $chan :$line"
    }
  }
}



########################################################################
# when the bot is started, disable announcement for all channels
#
########################################################################

if { [expr [unixtime] - $::uptime] <= "60" } {
  foreach chan [channels] {
    if { ![channel get $chan term-announce] } { continue }
    channel set $chan -term-announce
  }
}



########################################################################
# help ??
# public help for ?? command
#
########################################################################
bind pubm omn|fvlomn "% ${botnet-nick} help \\?\\?" term:check:help:pub

proc term:check:help:pub { nick uhost handle chan text } {
  lappend output "Usage: ?? \[<channel>\] <term> \[<nick>\]"
  lappend output "Shows a matching entry from the database."
  if { [catch {set x [cnotice $nick $output]} error] || !$x } {
    foreach t $output { puthelp "NOTICE $nick :$t" }
  }
  putloglev c $chan "[lindex [split $text] 1]: $nick $uhost $handle $chan [join [lrange [split $text] 2 end]]"
}



########################################################################
# ??
# usage: '?? [<channel>] <term> [<nick>]
# the 'core' of this script
#
########################################################################
bind pubm -|- "% \\?\\? *" term:check:pub

proc term:check:pub { nick uhost handle chan text {bot 0}} {
  if { [isignore $nick!$uhost] } { return 0 }
  if { ![validchan $chan] } { return 0 }
  if { ![channel get $chan term] } { return 0 }
#unknown user joined less than 60s ago
  if { ![matchattr $handle fvlomn|fvlomn $chan] && [expr [unixtime] - [getchanjoin $nick $chan]] < 60 } { return 0 }

#multibot support, should this bot respond?
  if { ![term:whichbot $chan $text] && !$bot } { return 0 }

#redirect(2), channel(1) or private(0)
  set way 0
  if { [matchattr $handle omn|fvlomn $chan] } { set way 2 }
  if { [channel get $chan term-op] && [isop $nick $chan] } { set way 2 }
  if { [channel get $chan term-hop] && ([isop $nick $chan] || [ishalfop $nick $chan]) } { set way 2 }
  if { [channel get $chan term-voice] && ([isop $nick $chan] || [ishalfop $nick $chan] || [isvoice $nick $chan]) } { set way 2 }
  if { ![channel get $chan term-silent] && ![string equal $way 2] } { set way 1 }
  if { [channel get $chan term-nounknown] && [string equal $way 0] } { return 0 }
  if { [string match *m* [lindex [split [getchanmode $chan]] 0]] && ![botisop $chan] && ![botishalfop $chan] && ![botisvoice $chan] } { set way 0 }

  global termdb
  term:reload
  set channel $chan
  set term [lindex [split $text] 1]
  set tnick [lindex [split $text] 2]

#check if "?? #chan term" is used or "?? global term"
  if { ([string match \#* $term] || [string equal -nocase global $term]) && ![string equal $tnick ""] } {
    set channel $term
    set term $tnick
    set tnick [lindex [split $text] 3]
#redirect flag
  } elseif { [channel get $chan term-redirect] } {
    if { [validchan [channel get $chan term-redirectchan]] } {
      set channel [channel get $chan term-redirectchan]
    } else {
      channel set $chan -term-redirect
    }
  }

#chasenick
  if { ![onchan $tnick $chan] } {
    if { [catch {set ct [chase:chasenick $tnick]} error] } {
      putlog "ERROR using chase.tcl, perhaps you have not loaded it?"
    }
    if { ![string equal $ct -1] && [onchan $ct $chan] && ![onchansplit $ct] } { set tnick $ct }
  }

  if { ![onchan $tnick $chan] } { set tnick "" }
  if { [onchansplit $tnick $chan] } { set tnick "" }

#do not allow "?? .. entry nick" to a channel (half)op
#assume this to be an error/typo
  if { [ishalfop $tnick $chan] } { set tnick "" }
  if { [isop $tnick $chan] } { set tnick "" }

  set channeli [string tolower $channel]
  set channelf $channeli
  set termi [string tolower $term]

#check if entry exists on global list
#then on channel list
#then search channel list
#then search global list


  if { [string equal $termi ""] } {
#?? global term was used
  } elseif { [string equal $channeli global] } {
    if { ![info exists termdb($channeli,$termi)] } {
      set termi [term:findbest $channeli $termi]
    }
#no global, only check channel list
#if other channel is used, only check that, do not check current channel or global list
  } elseif { [channel get $chan term-noglobal] || ![string equal -nocase $chan $channeli] } {
    if { ![info exists termdb($channeli,$termi)] } {
      set termi [term:findbest $channeli $termi]
    }
#check global list first
  } elseif { [channel get $chan term-globaloverride] } {
    if { [info exists termdb(global,$termi)] } {
      set channelf global
    } elseif { ![info exists termdb($channeli,$termi)] } {
      set termi [term:findbest $channeli $termi]
    }
    if { [string equal $termi ""] } {
      set termi [string tolower $term]
      set termi [term:findbest global $termi]
      set channelf global
    }
#check channel list first
  } else {
    if { ![info exists termdb($channeli,$termi)] && [info exists termdb(global,$termi)] } {
      set channelf global
    } elseif { ![info exists termdb($channeli,$termi)] } {
      set termi [term:findbest $channeli $termi]
      if { [string equal $termi ""] } {
        set termi [string tolower $term]
        set termi [term:findbest global $termi]
        set channelf global
      }
    }
  }

#get all entries for termi
  if { ![string equal $termi ""] } { set termi [term:findall $channelf $termi] }

#no match found
  if { [string equal $termi ""] } {
    if { [matchattr $handle omn|fvlomn $chan] } {
      if { [string equal $term ""] } {
        lappend output "Usage: ?? \[<channel>\] <term> \[<nick>\]"
      } else {
        lappend output "No entry found for \"$term\""
      }
    }
#other chan is used, but not valid, -term-share is set or user has no access there
  } elseif { ![string equal -nocase $channeli $chan] && ![string equal $channeli global] && (![validchan $channeli] || ![channel get $channeli term-share] || ![matchattr $handle omn|fvlomn $channeli]) } {
    if { [matchattr $handle omn|fvlomn $chan] } {
      lappend output "No access, unknown channel or channel is set -term-share $channeli"
    }

#sent
  } else {
#sent to chan
    if { ![string equal $way 0] } {
#check flood
      if { [term:flood $chan $channelf $termi] } { return 0 }
      set line "([lindex $termi 0]) $termdb($channelf,[lindex $termi 0])"
      if { [string equal $way 2] && ![string equal $tnick ""] } { set line "$tnick, $line" }
      if { [string match *c* [lindex [split [getchanmode $chan]] 0]] } {
        set line [stripcodes bcru $line]
      }
      foreach line [term:breakup PRIVMSG $chan $line] { puthelp "PRIVMSG $chan :$line" }
      set termi [lrange $termi 1 end]
      foreach line $termi {
        set line $termdb($channelf,$line)
        if { [string match *c* [lindex [split [getchanmode $chan]] 0]] } {
          set line [stripcodes bcru $line]
        }
        foreach line [term:breakup PRIVMSG $chan $line] { puthelp "PRIVMSG $chan :$line" }
      }
      putloglev c $chan "[lindex [split $text] 0]: $nick $uhost $handle $chan [join [lrange [split $text] 1 end]]"
      return 0
#sent private
    } else {
      set line "([lindex $termi 0]) $termdb($channelf,[lindex $termi 0])"
      lappend output $line
      set termi [lrange $termi 1 end]
      foreach line $termi { lappend output $termdb($channelf,$line) }

      foreach part $output {
        foreach line [term:breakup CPRIVMSG $nick $part] {
          lappend output2 $line
        }
      }
      set output $output2

      if { [channel get $chan term-msg] } {
        if { [catch {set x [cprivmsg $nick $output]} error] || !$x } {
          foreach t $output { puthelp "PRIVMSG $nick :$t" }
        }
      } else {
        if { [catch {set x [cnotice $nick $output]} error] || !$x } {
          foreach t $output { puthelp "NOTICE $nick :$t" }
        }
      }
      putloglev c $chan "[lindex [split $text] 0]: $nick $uhost $handle $chan [join [lrange [split $text] 1 end]]"
      return 0
    }
  }
  if { ![info exists output] || [string equal $output ""] } { return 0 }
  if { [catch {set x [cnotice $nick $output]} error] || !$x } {
    foreach t $output { puthelp "NOTICE $nick :$t" }
  }
}



########################################################################
# findbest
# this proc is used by the ?? command
# it returns the best match for the given term
#
########################################################################
proc term:findbest { channel term } {
  global termdb
  regsub -all {\[} $term {\\[} term
  regsub -all {\]} $term {\\]} term
  regsub -all {\\} $term {\\\\} term
  regsub -all {\[} $channel {\\[} channel
  regsub -all {\]} $channel {\\]} channel
  regsub -all {\\} $channel {\\\\} channel
  set x 2
  while { $x <= "8" } { set f($x) "" ; incr x 1 }
#check all entries
  foreach name [lsort [array names termdb]] {
#match entry names
#1 best match, return at once
    if { [string match -nocase $channel,$term* $name] } {
      return [lindex [split $name ,] 1]
    }
#2
    if { [string match -nocase $name* $channel,$term] } {
      if { [string equal $f(2) ""] } { set f(2) [lindex [split $name ,] 1] }
    }
#3
    if { [string match -nocase $channel,*$term $name] } {
      if { [string equal $f(3) ""] } { set f(3) [lindex [split $name ,] 1] }
    }
#4
    if { [string match -nocase $channel,*$term* $name] } {
      if { [string equal $f(4) ""] } { set f(4) [lindex [split $name ,] 1] }
    }
#matching entry names failed, match descriptions
#5
    if { [string match -nocase "* $term *" $termdb($name)] && [string match -nocase $channel,* $name] } {
      if { [string equal $f(5) ""] } { set f(5) [lindex [split $name ,] 1] }
    }
#6
    if { [string match -nocase "* $term*" $termdb($name)] && [string match -nocase $channel,* $name] } {
      if { [string equal $f(6) ""] } { set f(6) [lindex [split $name ,] 1] }
    }
#7
    if { [string match -nocase "*$term *" $termdb($name)] && [string match -nocase $channel,* $name] } {
      if { [string equal $f(7) ""] } { set f(7) [lindex [split $name ,] 1] }
    }
#8
    if { [string match -nocase "*$term*" $termdb($name)] && [string match -nocase $channel,* $name] } {
      if { [string equal $f(8) ""] } { set f(8) [lindex [split $name ,] 1] }
    }
  }
#return results, priority from 2 to 8
  set x 2
  while { $x <= "8" } {
    if { ![string equal $f($x) ""] } { return $f($x) }
    incr x 1
  }
}



########################################################################
# findall
# this proc finds all entries associated with the given entry
# for multi line support
#
########################################################################
proc term:findall { channel term } {
  global termsetting termdb
  if { ![info exists termdb($channel,$term)] } { return }
  lappend result $term
  set c 2
  if { ![info exists termsetting(maxlines)] } { set termsetting(maxlines) 4 }
  set max $termsetting(maxlines)
  if { ![string is digit $max] } { set max 4 }
  while { $c <= $max } {
    if { [info exists termdb($channel,${term}_$c)] } { lappend result ${term}_$c }
    incr c 1
  }
  return $result
}



########################################################################
# flood
# this proc prevents entries being said in the channel within short time
# returns 0 for show it, 1 for do not show it
#
########################################################################
proc term:flood { chan channel term } {
  global termfl
  set item [string tolower $chan,$channel,$term]
  set now [clock seconds]
  set limit [channel get $chan term-flood]
  if { $limit < "5" } { set limit 5 }
  if { ![info exists termfl($item)] } {
    set termfl($item) $now
    return 0
  } elseif { [expr $now - $termfl($item)] <= $limit } {
    return 1
  } else {
    set termfl($item) $now
    return 0
  }
}



########################################################################
# breakup
# this proc breaks up lines if they are too long
# max length of a line sent to IRC server is 510 chars (excl.2 for line end)
# same limit applies to message shown on IRC "nick!user@host PRIVMSG #channel/nick :test"
#
########################################################################
proc term:breakup { cmd channel text } {
  global botname
  set add [string length "$botname $cmd $channel :"]
  if { [expr [string length $text] + $add] <= 500 } {
    lappend output $text
#30 or less words, do it ugly
  } elseif { [llength [split $text]] <= "30" } {
    lappend output "[string range $text 0 [expr 500 - $add]].."
    lappend output "..[string range $text [expr 490 - $add] 780]"
#do it neat
  } else {
    set x 10
    while { [expr [string length [join [lrange [split $text] 0 end-$x]]] + $add] > "500" } {
      incr x 1
    }
    lappend output "[join [lrange [split $text] 0 end-$x]] .."
    lappend output ".. [join [lrange [split $text] end-[expr $x -1] end]]"
  }
  return $output
}


########################################################################
# cleanup
# this proc cleans up the array used for flood proc
#
########################################################################
bind time - "?0 * * * *" term:cleanup

proc term:cleanup { mi ho da mo ye } {
  if { [string equal $::botname $::botnick] } { return 0 }
  global termfl
  set now [clock seconds]
  foreach name [array names termfl] {
    if { [expr $now - $termfl($name)] > "120" } { unset termfl($name) }
  }
}



########################################################################
# backup
# this proc backs up the database file at midnight
#
########################################################################
bind time - "00 00 * * *" term:backup

proc term:backup { mi ho da mo ye } {
  global termsetting
  set source $termsetting(file)
  if { ![file exists $source] } { return 0 }

#number of backup files to keep
  set x 15
  while { $x > "1" } {
    if { [file exists $source[expr $x -1]] } {
      file copy -force $source[expr $x -1] $source$x
    }
    incr x -1
  }
  file copy -force $source $source$x
}



########################################################################
# ts (timestamp)
# this proc converts a unixtime stamp into Xy Xw Xd Xh Xs format
#
########################################################################
proc term:ts { ts } {
  if { ![string is digit $ts] } { return 0 }
  if { $ts > [unixtime] } {
    set ts [duration [expr $ts - [unixtime]]]
  } else {
    set ts [duration [expr [unixtime] - $ts]]
  }
  set ts [string map [list " seconds" "s" "search" "replace" "search" "replace"] $ts]
  set ts [string map [list " second" "s" "search" "replace" "search" "replace"] $ts]
  set ts [string map [list " minutes" "m" "search" "replace" "search" "replace"] $ts]
  set ts [string map [list " minute" "m" "search" "replace" "search" "replace"] $ts]
  set ts [string map [list " hours" "h" "search" "replace" "search" "replace"] $ts]
  set ts [string map [list " hour" "h" "search" "replace" "search" "replace"] $ts]
  set ts [string map [list " days" "d" "search" "replace" "search" "replace"] $ts]
  set ts [string map [list " day" "d" "search" "replace" "search" "replace"] $ts]
  set ts [string map [list " weeks" "w" "search" "replace" "search" "replace"] $ts]
  set ts [string map [list " week" "w" "search" "replace" "search" "replace"] $ts]
  set ts [string map [list " years" "y" "search" "replace" "search" "replace"] $ts]
  set ts [string map [list " year" "y" "search" "replace" "search" "replace"] $ts]
  return [join [lrange [split $ts] 0 1]]
}




########################################################################
# save
# this proc saves the database to file
#
########################################################################
proc term:save { } {
  global termsetting termdb termdbi
  set file $termsetting(file)
  set fs [open $file w]
  foreach name [lsort [array names termdb]] {
    set channel [lindex [split $name ,] 0]
    set term [join [lrange [split $name ,] 1 end] ,]
    set desc $termdb($name)
    set handle [lindex [split $termdbi($channel,$term)] 0]
    set ts [lindex [split $termdbi($channel,$term)] 1]
    puts $fs "$channel $term $handle $ts $desc"
  }
  close $fs
}



########################################################################
# load
# this proc loads the database from file
#
########################################################################
proc term:load { } {
  global termsetting termdb termdbi
  if { [info exists termdb] } { unset termdb }
  if { [info exists termdbi] } { unset termdbi }
  set file $termsetting(file)
  if { ![file exists $file] } { return 0 }
  set filesocket [open $file r]
  set data [read $filesocket]
  close $filesocket
  foreach line [split $data \n] {
    if { ![string equal $line ""] } {
      set channel [lindex [split $line] 0]
      set term [lindex [split $line] 1]
      set handle [lindex [split $line] 2]
      set ts [lindex [split $line] 3]
      set desc [join [lrange [split $line] 4 end]]
      set termdb($channel,$term) "$desc"
      set termdbi($channel,$term) "$handle $ts"
    }
  }
}
#load database when starting / rehashing
term:load



########################################################################
# reload
# this proc reloads the database depending on the value of $termsetting(reload)
#
########################################################################
proc term:reload { } {
  global termsetting
  if { ![info exists termsetting(reload)] } { set termsetting(reload) 0 }
  if { [string equal $termsetting(reload) 1] } { term:load }
}



########################################################################
# multibot part of script
#
########################################################################



########################################################################
# whichbot
# this proc determines which bot should reply on the channel
# returns 1 if this bot should respond, 0 otherwise
#
########################################################################
proc term:whichbot { chan text } {
  if { ![validchan $chan] } { return 0 }
  if { [string equal $text ""] } { set text a }
  global botnet-nick
  set bots [chanlist $chan b]
  set result ""

#get all bot handles
  foreach b $bots {
    if { ![string match -nocase *1* [botattr [nick2hand $b]]] } { continue }
    if { ![string equal [lsearch -exact $result [nick2hand $b]] -1] } { continue }
    if { [onchansplit $b $chan] } { continue }
    lappend result [nick2hand $b]
  }
#sort
  set bots [lsort $result]
#no bots found
  if { [string equal $bots ""] } { return 1 }
#substract 1 (as lindex starts counting from 0, not 1
  set nrbots [expr [llength $bots] -1]
#chars
  set char [split a0b9c8d7e6fghijklmnopqrstu5v4w3x2y1z ""]
#convert the first char of md5 hash from text into a number ranging 1 to 36 (a-z 0-9)
#this makes it that it depends on the text what bot reacts
  set char [lsearch -exact $char [string range [md5 $text] 0 0]]
#calc number-of-bots / 36 * charnumber
#example: 2 / 36 * 36 = 1, so 2nd bot (lindex 1 is 2nd element)
#example: 2 / 36 * 1 = 0, so 1st bot (lindex 0 is 1st element)
  set bot [expr round(${nrbots}. / 36. * $char)]
#convert given number to a botname
  set bot [lindex [split $bots] $bot]
#see if we are that bot or not
  if { [string equal $bot ${botnet-nick}] } { return 1 } else { return 0 }
}



########################################################################
# send
# this proc sends entry changes to other bots
#
########################################################################
proc term:send { channel term action } {
  global termsetting termdb termdbi
  if { ![info exists termsetting(key)] } { set termsetting(key) "" }
  set key $termsetting(key)

  foreach bot [bots] {
    if { [string match -nocase *1* [botattr $bot]] } {
#add
      if { [string equal $action add] } {
        set desc $termdb($channel,$term)
        set handle [lindex [split $termdbi($channel,$term)] 0]
        set ts [lindex [split $termdbi($channel,$term)] 1]
        set lines ""
        lappend lines "a1 $channel $term $handle $ts"
#break up lines
#messages over botnet are limited to 400 chars?
        while { ![string equal [string length $desc] 0] } {
          lappend lines "a2 $channel $term [string range $desc 0 99]"
          set desc [string range $desc 100 end]
        }
        foreach line $lines {
          set line [encrypt $key $line]
          putbot $bot "term $line"
        }
#del
      } elseif { [string equal $action del] } {
        set line "d $channel $term"
        set line [encrypt $key $line]
        putbot $bot "term $line"
      }
    }
  }
}



########################################################################
# bot
# this proc handles incoming bot communication for this script
#
########################################################################
bind bot - term term:bot

proc term:bot { bot cmd text } {
  global termsetting termdb termdbi
  if { ![string equal $cmd term] } { return 0 }
  if { ![info exists termsetting(key)] } { set termsetting(key) "" }
  set key $termsetting(key)
  set text [decrypt $key $text]
  set cmd [lindex [split $text] 0]
  set text [join [lrange [split $text] 1 end]]
#add 1, create initial entry
  if { [string equal $cmd a1] } {
    set channel [lindex [split $text] 0]
    set term [lindex [split $text] 1]
    set handle [lindex [split $text] 2]
    set ts [lindex [split $text] 3]
    set termdb($channel,$term) ""
    set termdbi($channel,$term) "$handle $ts"
    term:save
#add 2, add info to existing entry
  } elseif { [string equal $cmd a2] } {
    set channel [lindex [split $text] 0]
    set term [lindex [split $text] 1]
    set desc [join [lrange [split $text] 2 end]]
    if { ![info exists termdb($channel,$term)] } { set termdb($channel,$term) "" }
    append termdb($channel,$term) $desc
    term:save
#del
  } elseif { [string equal $cmd d] } {
    set channel [lindex [split $text] 0]
    set term [lindex [split $text] 1]
    if { [info exists termdb($channel,$term)] } { unset termdb($channel,$term) }
    if { [info exists termdbi($channel,$term)] } { unset termdbi($channel,$term) }
    term:save
#add all 1, create initial entry
  } elseif { [string equal $cmd aa1] && [string match -nocase *2* [botattr $bot]] } {
    set channel [lindex [split $text] 0]
    set term [lindex [split $text] 1]
    set handle [lindex [split $text] 2]
    set ts [lindex [split $text] 3]
    set termdb($channel,$term) ""
    set termdbi($channel,$term) "$handle $ts"
    term:save
#add all 2, add to info to existing entry
  } elseif { [string equal $cmd aa2] && [string match -nocase *2* [botattr $bot]] } {
    set channel [lindex [split $text] 0]
    set term [lindex [split $text] 1]
    set desc [join [lrange [split $text] 2 end]]
    if { ![info exists termdb($channel,$term)] } { set termdb($channel,$term) "" }
    append termdb($channel,$term) $desc
    term:save
#delete all
  } elseif { [string equal $cmd da] && [string match -nocase *2* [botattr $bot]] } {
    if { [info exists termdb] } { unset termdb }
    if { [info exists termdbi] } { unset termdbi }
    term:save
  }
}



########################################################################
# link
# this proc sends the entire database to the bot that links
#
########################################################################
bind link - * term:link

proc term:link { bot via } {
  global botnick termsetting termdb termdbi
  if { [string match -nocase *1* [botattr $bot]] && [string match -nocase *2* [botattr $botnick]] } {
    if { ![info exists termsetting(key)] } { set termsetting(key) "" }
    set key $termsetting(key)
    set line "da"
    set line [encrypt $key $line]
    putbot $bot "term $line"
    foreach name [lsort [array names termdb]] {
      set channel [lindex [split $name ,] 0]
      set term [join [lrange [split $name ,] 1 end] ,]
      set handle [lindex [split $termdbi($channel,$term)] 0]
      set ts [lindex [split $termdbi($channel,$term)] 1]
      set desc $termdb($name)
      set lines ""
      lappend lines "aa1 $channel $term $handle $ts"
      while { ![string equal [string length $desc] 0] } {
        lappend lines "aa2 $channel $term [string range $desc 0 99]"
        set desc [string range $desc 100 end]
      }
      foreach line $lines {
        set line [encrypt $key $line]
        putbot $bot "term $line"
      }
    }
  }
}

