#---------------------------------------------------------------------------------------
#- no!spam 1.6.3
#
#- First beta    : PPSlim  (Nothing is real but the pain.)
#- Revised by    : wreck (until v1.6.2)
#- maintained by : PPSlim (Can you feel it)
#---------------------------------------------------------------------------------------
#- Credits:
#-   Wreck - This wouldn't be a reality without him. (also, ideas after he left)
#-   PPSlim (for the idea[s]), Quasimodo (bugs, storage idea), MC_8 (masktype), 
#-   stdragon (for input on correctly handling lists), CafeiN (bots were self-banning)
#-   hoax (for more channel cycling safety ideas (spam-cycle*)), RedAlert (CTRL codes)
#---------------------------------------------------------------------------------------
#- DCC commands: (see .desc file for full explanations)
#
#-   +spam <type> <string>          Add spam string.
#-   -spam <type> <string>          Remove spam string.
#-   spams <type> [string]          View spam strings. (that match string)
#
#-   +spamchan <#channel> [type]    Add spam channel. (add type to channel)
#-   -spamchan <#channel> [type]    Remove spam channel. (remove type from channel)
#-   spamchans                      View spam channels.
#
#- Valid types:
#-   c = channel messages, d = dcc-cycling, m = private messages, n = notices
#-   p = part messages, a = can be used for all types (with the +spam/-spam commands)
#
#- Strings can contain wildcards (* ?) and case doesn't matter.
#---------------------------------------------------------------------------------------
#- New in 1.6.2+:
#-  This section is now inside the no.spam.vers file
#---------------------------------------------------------------------------------------

# Set this to the full path of your spam channels file.
# (this is where the channels that no!spam will monitor are kept)
# Don't manually create this. You have to do it in the partyline using +/-spamchan.
set spam-chan-file "/home/wreck/eggdrop/scripts/spam.chans"

# If the alleged spammer doesn't meet atleast this flag, ban them.
# (if o is given, anyone who has the +o flag on the bot won't be banned)
set spam-ignore-flag "m"

# People who meet atleast this flag can use the no!spam DCC commands.
set spam-dcc-flag "m"

# If you want no!spam to keep a log of all the spammers it bans, then set this to 1.
set spam-log 1

# Set this to the full path of your spammers log file.
set spam-log-file "/home/wreck/eggdrop/scripts/spammers"

#---------------------------------------------------------------------------------------
#- channel messages

# If you want no!spam to monitor the channel messages (normal chat) in channels
# that you have marked as +c (see DCC commands) then set this to 1. Otherwise
# leave it set to 0, to keep it disabled.
#
# This works by you providing a list of search strings to look for. If one of
# these strings is found in anyone's (that doesn't meet $spam-ignore-flag)
# channel message then they will be banned accordingly.
#
set spam-chan-check 0

# Set this to the full path of your channel spam strings file.
# You don't have to create one now, you can wait and do it using +/-spam.
#
# Please Note:  If you want to use the same spam strings for each type,
#               then specify the same file for each. Now just use +/-spam
#               on one and it will be applied to them all.
#
# Example of a channel spam file: (don't include the # of course =))
#
# *join #*
# *http://www.*
# *first to join * gets a cookie*
#
set spam_file(chan) "/home/wreck/eggdrop/scripts/spam.chan"

# Where should the ban be applied?
# 0 = always local ban (channel the spam originated in)
# 1 = always global ban (all channels the bot is in)
set spam-chan-global 1

# How should the hosts of the spammers be masked?
# 0 = *!user@host.domain
# 1 = *!*user@host.domain
# 2 = *!*@host.domain
# 3 = *!*user@*.domain
# 4 = *!*@*.domain
# 5 = nick!user@host.domain
# 6 = nick!*user@host.domain
# 7 = nick!*@host.domain
# 8 = nick!*user@*.domain
# 9 = nick!*@*.domain
# 10 = regular eggdrop mask
set spam-chan-mask 0

# How long (in minutes) should the ban last?
# Set to 0, to make it a permanent ban. (not recommended)
# Set to -1, to use the default ban-time. (as set in your eggdrop.conf)
# NOTE: no-longer uses "" for default ban-time.
#       Applies to all "set spam-*-time" settings
set spam-chan-time 60

# Ban reasons. One will be picked at random each time a spammer is banned.
#
# Please Note: That using * as the first character in your ban message
#              automagically makes the ban sticky.
#
set spam-chan-reason {
  "Chan message abuse"
  "Would you like some green eggs with that spam?"
  "Just say no to spam."
  "*I feel like making this stick."
}

#---------------------------------------------------------------------------------------
#- part messages
#- Please Note: This works exactly like above, except part messages are checked on
#-              channels that're marked as +p

set spam-part-check 0

set spam_file(part) "/home/wreck/eggdrop/scripts/spam.part"

# 0 = always local ban
# 1 = always global ban
set spam-part-global 1

set spam-part-mask 0
set spam-part-time 60

set spam-part-reason {
  "Part message abuse"
  "Would you like some green eggs with that spam?"
  "Just say no to spam."
}

#---------------------------------------------------------------------------------------
#- notices
#- Please Note: This works just like above except notices that are sent to your bot,
#-              and channel notices to channels that're marked as +n are monitored.

set spam-notc-check 1

set spam_file(notc) "/home/wreck/eggdrop/scripts/spam.notc"

# 0 = only ban globally if we have to
# 1 = always global ban
set spam-notc-global 1

set spam-notc-mask 0
set spam-notc-time 60

set spam-notc-reason {
  "Notice abuse"
  "Would you like some green eggs with that spam?"
  "Just say no to spam."
}

#---------------------------------------------------------------------------------------
#- invites

# If you want no!spam to ban people, who don't match $spam-ignore-flag, that
# invite your bot to a channel that it isn't on, set this to 1. Otherwise set
# it to 0, to keep it disabled.
#
# Please Note:  If the person who sent this notice left the channel they sent it
#               from, the ban will become global (only for $spam-invt-time) even
#               if $spam-invt-global is set to 1.
#
#               And since no message is sent with invites, you don't need to add
#               any search strings. You also don't need to mark any channels for
#               this, since it (the spammer or spam) won't necessarily be in one.
#
set spam-invt-check 1

# 0 = only ban globally if we have to
# 1 = always global ban
set spam-invt-global 1

set spam-invt-mask 0
set spam-invt-time 60

set spam-invt-reason {
  "Don't invite me to your channel!"
  "Would you like some green eggs with that spam?"
  "Just say no to spam."
}

#---------------------------------------------------------------------------------------
#- private messages

# If you want no!spam to monitor your bots private messages (/msg) for spam, then
# set this to 1, otherwise 0 to disable.
#
# Please Note:  This works just like invite checking, with the exception that you
#               are required to store a list of search strings (see DCC commands)
#
set spam-priv-check 0

set spam_file(priv) "/home/wreck/eggdrop/scripts/spam.priv"

# 0 = only ban globally if we have to
# 1 = always global ban
set spam-priv-global 1

set spam-priv-mask 0
set spam-priv-time 60

set spam-priv-reason {
  "Message abuse"
  "Would you like some green eggs with that spam?"
  "Just say no to spam."
}

#---------------------------------------------------------------------------------------
#- dcc sends

# If you want no!spam to monitor DCC sends that people send to your bot, then
# keep this set to 1, otherwise 0 to disable.
#
set spam-file-check 1

# If you want no!spam to cycle channels that are marked as +d (see DCC commands)
# every $spam-file-interval minutes, so it can pick-up DCC-on-joins and ban the
# sender, then keep this set to 1.
#
# Also Note:    This will also help catch on-join-message spammers too, well,
#               that's if you have private message checking enabled. =P
#
set spam-file-cycle 1

# Interval to cycle channels. (in minutes)
set spam-file-interval 360

# Pick interval at random? (from $spam-file-lower to $spam-file-interval)
set spam-file-rand 1

# If using random intervals, set this to the lower radom limit. (mins)
#   EG: Set $spam-file-interval 20, and $spam-file-lower 10
#       would make the bot cycle the channel every 10 to 20 mins
#       rather than the original random method, 1 to 20 mins
# NOTE: To make it use the original random method, set this to 1
#       or set higher or same as $spam-file-interval
set $spam-file-lower 10

# Do you want to skip cycling a channel if the bot isn't opped there?
set spam-cycle-noop 1

# Do you want to skip cycling a channel if the bot is the only op left?
set spam-cycle-onlyop 1

# Do you want to skip cycling a channel if it is opless?
set spam-cycle-juped 1

# Don't cycle a channel if there isn't atleast $spam-cycle-min nick(s) matching
# the $spam-cycle-flag flag, that are opped? (setting spam-cycle-min to 0 will disable this)
set spam-cycle-min 1
set spam-cycle-flag "b"

# If you want to keep a list of files that you don't want to ban for, then set
# them here. You can safely set this to "" to not accept any files. Wildcards work.
set spam-file-safe {
  "motd.txt"
}

# If you want your bot to send a notice to someone for sending a specific file out,
# then set it here.
#
# Example:
#
#   set spam_file_warn(life_stages.text.shs) {
#     "You were banned because you are infected with a virus."
#     "If you need help removing it, join #dmsetup."
#     "Please don't respond to this, I'm a bot."
#   }
#
# Each line is sent seperately, file name NEEDS to be lower case.
#

# 0 = only ban globally if we have to
# 1 = always global ban
set spam-file-global 1

set spam-file-mask 0
set spam-file-time 60

set spam-file-reason {
  "I got your DCC right here pal."
}

#---------------------------------------------------------------------------------------
#- code begins here
#- please don't edit anything below, unless you know what you are doing.
#---------------------------------------------------------------------------------------

if {!${spam-chan-check} && !${spam-part-check} && !${spam-notc-check} && !${spam-invt-check} && !${spam-priv-check} && !${spam-file-check}} {
  putlog "Loading no.spam.tcl has been skipped :You have it disabled."
  return 0
}

if {[info exists nospam-loaded]} {set nospam-loaded ""}

proc lcase {str} {return [string tolower $str]}
proc strlen {str} {return [string length $str]}
proc strcomp {one two} {if {[strlen $one] == [strlen $two] && [lcase $one] == [lcase $two]} {return 1} ; return 0}
proc strfind {find str} {if {[string match *$find* $str]} {return 1} ; return 0}
proc lremove {list str} {return [lreplace $list [lsearch -exact $list $str] [lsearch -exact $list $str]]}
proc lsplit {list str idx} {return [lindex [split $list $str] $idx]}
proc fexists {file} {return [file exists $file]}
proc findnick {nick} {foreach chan [channels] {if {[onchan $nick $chan]} {return $chan}} ; return ""}
proc isbothost {uhost} {global botname ; return [strcomp $uhost $botname]}
proc pushkick {chan nick rsn} {if {[strlen $chan]} {if {[onchan $nick $chan]} {putserv "KICK $chan $nick :$rsn"}} else {foreach chan [channels] {if {[onchan $nick $chan]} {puthelp "KICK $chan $nick :$rsn"}}}}
proc fulltype {type} {switch $type {c {return chan} d {return file} i {return invt} m {return priv} n {return notc} p {return part}}}
proc ns:chanban {chan mask creator comment time} {
  if {$time >= 0} {
    newchanban $chan $mask $creator $comment $time
  } else {
    newchanban $chan $mask $creator $comment
  }
}
proc ns:ban {mask creator comment time} {
  if {$time >= 0} {
    newban $mask $creator $comment $time
  } else {
    newban $mask $creator $comment
  }
}
proc ctrl:filter {str} {
  regsub -all -- {([\003]{1}[0-9]{0,2}[\,]{0,1}[0-9]{0,2})} $str "" str; #color
  regsub -all -- "\017" $str "" str; #plain
  regsub -all -- "\037" $str "" str; #underline
  regsub -all -- "\002" $str "" str; #bold
  regsub -all -- "\026" $str "" str; #reverse
  return $str
}
proc ns:rand {low high} {
  if {$low >= $high} {
    return [rand $high]
  } else {
    set i [expr $high - $low]
    set x [rand $i]
    return [expr $x + $low]
  }
}

# masktype{} is based upon the original by MC_8 (mc.moretools.tcl)
proc masktype {uhost type} {
  switch $type {
    0 {set ban "*[string range $uhost [string first ! $uhost] e]"}
    1 {set ban "*!*[string trimleft [string range $uhost [expr [string first ! $uhost]+1] e] "~"]"}
    2 {set ban "*!*[string range $uhost [string first @ $uhost] e]"}
    3 {set ident [string range $uhost [expr [string first ! $uhost]+1] [expr [string last @ $uhost]-1]] ; set ban "*!*[string trimleft $ident "~"][string range [maskhost $uhost] [string first @ [maskhost $uhost]] e]"}
    4 {set ban "*!*[string range [maskhost $uhost] [string last "@" [maskhost $uhost]] e]"}
    5 {set ban $uhost}
    6 {set nick [string range $uhost 0 [expr [string first "!" $uhost]-1]] ; set ident [string range $uhost [expr [string first "!" $uhost]+1] [expr [string last "@" $uhost]-1]] ; set ban "$nick!*[string trimleft $ident "~"][string range $uhost [string last "@" $uhost] e]"}
    7 {set nick [string range $uhost 0 [expr [string first "!" $uhost]-1]] ; set ban "$nick!*[string range $uhost [string last "@" $uhost] e]"}
    8 {set nick [string range $uhost 0 [expr [string first "!" $uhost]-1]] ; set ident [string range $uhost [expr [string first "!" $uhost]+1] [expr [string last "@" $uhost]-1]] ; set ban "$nick!*[string trimleft $ident "~"][string range [maskhost $uhost] [string last "@" [maskhost $uhost]] e]"}
    9 {set nick [string range $uhost 0 [expr [string first "!" $uhost]-1]] ; set ban "$nick!*[string range [maskhost $uhost] [string last "@" [maskhost $uhost]] e]"}
    default {set ban "*!*[string range $uhost [string first "@" $uhost] e]"}
  }
  set nnick [lindex [split $ban !] 0]
  set iident [string range $ban [expr [string first ! $ban]+1] [expr [string last @ $ban]-1]]
  set hhost [string range $ban [expr [string last @ $ban]+1] e]
  if {$iident != [set temp [string range $iident [expr [string length $iident]-9] e]]} {set iident *[string trimleft $temp *]}
  if {$hhost != [set temp [string range $hhost [expr [string length $hhost]-63] e]]} {set hhost *[string trimleft $temp *]}
  return $nnick!$iident@$hhost
}

proc spamchan {chan type} {
  global spam_chans
  if {![info exists spam_chans]} {return 0}
  foreach cflags $spam_chans {
    set chanx [lsplit $cflags + 0]
    set flags [lsplit $cflags + 1]
    if {[strlen $type]} {if {[strcomp $chanx $chan] && [strfind $type $flags]} {return 1}} else {return [strcomp $chanx $chan]}
  }
  return 0
}

proc spamignore {who} {
  global spam-ignore-flag
  if {[string match *!*@* $who]} {return [matchattr [finduser $who] ${spam-ignore-flag}]}
  return [matchattr $who ${spam-ignore-flag}]
}

proc spammask {uhost type} {
  global spam-chan-mask spam-file-mask spam-invt-mask spam-notc-mask spam-part-mask spam-priv-mask
  switch $type {
    c {return [masktype $uhost ${spam-chan-mask}]} d {return [masktype $uhost ${spam-file-mask}]} i {return [masktype $uhost ${spam-invt-mask}]} m {return [masktype $uhost ${spam-priv-mask}]} n {return [masktype $uhost ${spam-notc-mask}]} p {return [masktype $uhost ${spam-part-mask}]}
  }
}

proc spamban {type} {
  global spam-chan-reason spam-file-reason spam-invt-reason spam-notc-reason spam-part-reason spam-priv-reason
  switch $type {
    c {return [lindex ${spam-chan-reason} [rand [llength ${spam-chan-reason}]]]} d {return [lindex ${spam-file-reason} [rand [llength ${spam-file-reason}]]]} i {return [lindex ${spam-invt-reason} [rand [llength ${spam-invt-reason}]]]} m {return [lindex ${spam-priv-reason} [rand [llength ${spam-priv-reason}]]]} n {return [lindex ${spam-notc-reason} [rand [llength ${spam-notc-reason}]]]} p {return [lindex ${spam-part-reason} [rand [llength ${spam-part-reason}]]]}
  }
}

proc spamsyntax {idx cmd} {
  switch $cmd {
    +spam {
      putidx $idx "Usage: +spam <type> <string>"
      putidx $idx "Valid types: c (chan), m (priv msg), n (notices), p (parts) or a for all."
    }
    -spam {
      putidx $idx "Usage: -spam <type> <string>"
      putidx $idx "Valid types: c (chan), m (priv msg), n (notices), p (parts) or a for all."
    }
    spams {
      putidx $idx "Usage: spams <type> \[string\]"
      putidx $idx "Valid types: c (chan), m (priv msg), n (notices), p (parts)."
    }
    +spamchan {
      putidx $idx "Usage: +spamchan <#channel> \[type\]"
      putidx $idx "Valid types: c (chan), d (dcc-cycle), n (notices), p (parts)."
    }
    -spamchan {
      putidx $idx "Usage: -spamchan <#channel> \[type\]"
      putidx $idx "Valid types: c (chan), d (dcc-cycle), n (notices), p (parts)."
    }
  }
}

#---------------------------------------------------------------------------------------
#- spam string file procs

proc sf_write {type data} {
  global spam_file
  set file [open $spam_file([fulltype $type]) w]
  foreach spam [join [list $data]] {puts $file $spam}
  close $file
}

proc sf_read {type} {
  global spam_file
  set file [open $spam_file([fulltype $type]) r]
  set data ""
  while {![eof $file]} {
    set temp [gets $file]
    if {[strlen $temp] <= 4} {continue}
    if {[strlen $data]} {lappend data $temp} else {set data [list $temp]}
  }
  close $file
  return $data
}

proc sf_matches {type} {
  global spam-chan-matches spam-notc-matches spam-part-matches spam-priv-matches
  switch [fulltype $type] {chan {if {[info exists spam-chan-matches]} {return ${spam-chan-matches}}} notc {if {[info exists spam-notc-matches]} {return ${spam-notc-matches}}} part {if {[info exists spam-part-matches]} {return ${spam-part-matches}}} priv {if {[info exists spam-priv-matches]} {return ${spam-priv-matches}}}}
  return ""
}

proc sf_append {type data} {
  global spam-chan-matches spam-notc-matches spam-part-matches spam-priv-matches spam_file
  set type [fulltype $type]
  switch $type {chan {if {[info exists spam-chan-matches]} {lappend spam-chan-matches $data} else {set spam-chan-matches [list $data]}} notc {if {[info exists spam-notc-matches]} {lappend spam-notc-matches $data} else {set spam-notc-matches [list $data]}} part {if {[info exists spam-part-matches]} {lappend spam-part-matches $data} else {set spam-part-matches [list $data]}} priv {if {[info exists spam-priv-matches]} {lappend spam-priv-matches $data} else {set spam-priv-matches [list $data]}}}
  set file [open $spam_file($type) a+]
  puts $file $data
  close $file
}

proc sf_remove {type item} {
  global spam-chan-matches spam-notc-matches spam-part-matches spam-priv-matches spam_file
  set type [fulltype $type]
  if {![fexists $spam_file($type)]} {return ""}
  switch $type {
    chan {if {[info exists spam-chan-matches] && [strlen ${spam-chan-matches}] != [strlen [set spam-chan-matches [lremove ${spam-chan-matches} $item]]]} {sf_write c ${spam-chan-matches} ; return 1}}
    notc {if {[info exists spam-notc-matches] && [strlen ${spam-notc-matches}] != [strlen [set spam-notc-matches [lremove ${spam-notc-matches} $item]]]} {sf_write n ${spam-notc-matches} ; return 1}}
    part {if {[info exists spam-part-matches] && [strlen ${spam-part-matches}] != [strlen [set spam-part-matches [lremove ${spam-part-matches} $item]]]} {sf_write p ${spam-part-matches} ; return 1}}
    priv {if {[info exists spam-priv-matches] && [strlen ${spam-priv-matches}] != [strlen [set spam-priv-matches [lremove ${spam-priv-matches} $item]]]} {sf_write m ${spam-priv-matches} ; return 1}}
  }
  return 0
}

#---------------------------------------------------------------------------------------
#- spam chan file procs

proc scf_write {data} {
  global spam-chan-file spam_chans
  set file [open ${spam-chan-file} w]
  if {[strlen $data]} {puts $file $data} else {puts $file [join $spam_chans]}
  close $file
}

proc scf_read {} {
  global spam-chan-file spam_chans
  if {![fexists ${spam-chan-file}]} {return 0}
  set data [gets [set file [open ${spam-chan-file} r]]]
  close $file
  set spam_chans [join [lrange [split $data] 0 end]]
}

proc scf_getentry {chan} {
  global spam_chans
  foreach cflags $spam_chans {if {[strcomp [lsplit $cflags + 0] $chan]} {return $cflags}}
  return ""
}

proc scf_addtype {old new} {
  global spam_chans
  set chan [lcase [lsplit $old + 0]]
  if {![strlen [set new [lcase [string trimleft $new +]]]]} {return 0}
  if {![info exists spam_chans]} {
    set spam_chans "$chan+$new"
    return 1
  }
  if {![strlen [set temp [lcase [lsplit $old + 1]]]]} {
    set spam_chans [lremove $spam_chans $old]
    lappend spam_chans "$chan+$new"
    return 1
  }
  for {set x 0} {$x < [strlen $new]} {incr x} {
    set i [string index $new $x]
    if {[strfind $i $temp]} {continue}
    append temp $i
  }
  if {[strlen $temp] > 0} {
    set i [lsearch -exact $spam_chans $old]
    set spam_chans [lreplace $spam_chans $i $i "$chan+$temp"]
    return 1
  }
  return 0
}

proc scf_remtype {old del} {
  global spam_chans
  set chan [lcase [lsplit $old + 0]]
  set temp [lcase [lsplit $old + 1]]
  set del [lcase [string trimleft $del -]]
  set replaced 0
  if {![strlen $del] || ![strlen $temp]} {return 0}
  for {set x 0} {$x < [strlen $del]} {incr x} {
    set i [string index $del $x]
    if {[strfind $i $temp]} {regsub $i $temp "" temp ; incr replaced}
  }
  if {$replaced > 0} {
    set i [lsearch -exact $spam_chans $old]
    set spam_chans [lreplace $spam_chans $i $i "$chan+$temp"]
    return 1
  }
  return 0
}

proc scf_append {chan flags} {
  global spam_chans
  set chan [lcase $chan]
  if {[info exists spam_chans]} {
    lappend spam_chans "$chan+$flags"
  } else {
    set spam_chans $chan+$flags
  }
  scf_write ""
}

proc scf_remove {chan} {
  global spam_chans
  set temp ""
  foreach cflags $spam_chans {
    if {[strcomp [lsplit $cflags + 0] $chan]} {set temp $cflags ; break}
  }
  if {[strlen $temp] > 0} {
    set spam_chans [lremove $spam_chans $temp]
    scf_write ""
    return 1
  }
  return 0
}

#---------------------------------------------------------------------------------------
#- spammer logger

proc sl_write {who type where what why} {
  global spam-log spam-log-file
  if {!${spam-log}} {return 0}
  set file [open ${spam-log-file} a+]
  set when [strftime "%D %r"]
  switch [fulltype $type] {
    chan {puts $file "$when: $who said: (#$why) $what in: $where"}
    file {puts $file "$when: $who was sending: $what to people who join: $where"}
    invt {puts $file "$when: $who invited me to: $where"}
    notc {if {![strlen $where]} {set where "me"} ; puts $file "$when: $who noticed: $where with: (#$why) $what"}
    part {puts $file "$when: $who parted from: $where with: (#$why) $what"}
    priv {puts $file "$when: $who messaged me with: (#$why) $what"}
  }
  close $file
}

#---------------------------------------------------------------------------------------
#- dcc-on-join procs

if {${spam-file-check} && ${spam-file-cycle}} {
  proc cyclechan {chan} {
    global spam-cycle-noop spam-cycle-onlyop spam-cycle-juped spam-cycle-min spam-cycle-flag
    if {![botonchan $chan]} {return 0}
    if {![botisop $chan] && ${spam-cycle-noop}} {return 0}
    set total 0
    if {${spam-cycle-min} >= 1} {
      foreach nick [chanlist $chan ${spam-cycle-flag}] {if {[isop $nick $chan]} {incr total}}
      if {$total <= ${spam-cycle-min}} {return 0}
    }
    set total 0
    foreach nick [chanlist $chan] {if {[isop $nick $chan]} {incr total}}
    if {!$total && ${spam-cycle-juped}} {return 0}
    if {$total == 1 && [botisop $chan] && ${spam-cycle-onlyop}} {return 0}
    if {[strfind k [lindex [split [set chanm [getchanmode $chan]]] 0]]} {set chanj "JOIN $chan :[lindex [split $chanm] 1]"} else {set chanj "JOIN $chan"}
    puthelp "PART $chan"
    puthelp $chanj
  }

  proc nospam_ctimer {} {
    global spam-file-interval spam-file-rand spam-file-lower
    foreach t [timers] {if {[strcomp [lindex $t 1] nospam_cycle]} {killtimer [lindex $t 2]}}
    if {${spam-file-rand}} {set i [ns:rand ${spam-file-lower} ${spam-file-interval}]} else {set i ${spam-file-interval}}
    timer $i nospam_cycle
  }

  proc nospam_cycle {} {
    global spam_chans
    foreach cflags $spam_chans {if {[strfind d [lsplit $cflags + 1]]} {cyclechan [lsplit $cflags + 0]}}
    nospam_ctimer
  }
}

#---------------------------------------------------------------------------------------
#- event monitoring

if {${spam-part-check}} {
  proc part:abuse {from key arg} {
    global spam-part-time spam-part-global botnick
    if {[llength [set arg [split $arg]]] < 2 || ![spamchan [set chan [lindex $arg 0]] p] || ![strlen [set matches [sf_matches p]]] || [isbothost $from] || [spamignore $from]} {return 0}
    if {[strlen [set pmsg [string trimleft [lcase [join [lrange $arg 1 end]]] :]]] <= 4} {return 0}
    set idx 0
    set pmsg [ctrl:filter $pmsg]
    foreach match [join [list $matches]] {
      incr idx
      if {[string match [lcase $match] $pmsg]} {
        if {${spam-part-global}} {
          ns:ban [spammask $from p] $botnick [spamban p] ${spam-part-time}
        } else {
          ns:chanban $chan [spammask $from p] $botnick [spamban p] ${spam-part-time}
        }
        sl_write $from p $chan $pmsg $idx
        break
      }
    }
    return 0
  }
  bind raw - PART part:abuse
  append nospam-loaded p
}

if {${spam-chan-check}} {
  proc chan:abuse {from key arg} {
    global spam-chan-time spam-chan-global botnick
    set arg [split $arg]
    set nick [lsplit $from ! 0]
    if {![strlen [set matches [sf_matches c]]] || ![spamchan [set chan [lindex $arg 0]] c] || [isbothost $from] || [spamignore $from]} {return 0}
    if {[strlen [set text [lcase [string trimleft [lrange $arg 1 end] :]]]] <= 4} {return 0}
    set idx 0
    set text [ctrl:filter $text]
    foreach match [join [list $matches]] {
      incr idx
      if {[string match [lcase $match] $text]} {
        set rsn [spamban c]
        if {${spam-chan-global}} {
          ns:ban [spammask $from c] $botnick $rsn ${spam-chan-time}
          pushkick "" $nick $rsn
        } else {
          ns:chanban $chan [spammask $from c] $botnick $rsn ${spam-chan-time}
          pushkick $chan $nick $rsn
        }
        sl_write $from c $chan $text $idx
        break
      }
    }
    return 0
  }
  bind raw - PRIVMSG chan:abuse
  append nospam-loaded c
}

if {${spam-notc-check}} {
  proc notc:abuse {from key arg} {
    global spam-notc-time spam-notc-global botnick
    if {[llength [set arg [split $arg]]] < 2 || ![strlen [set matches [sf_matches n]]] || [isbothost $from] || [spamignore $from]} {return 0}
    set chan [lindex $arg 0]
    if {[spamchan $chan n] || [strcomp $chan $botnick]} {
      if {[strcomp $chan $botnick]} {set chan ""}
      set nick [lsplit $from ! 0]
      if {[strlen [set nmsg [string trimleft [lcase [join [lrange $arg 1 end]]] :]]] <= 4} {return 0}
      set idx 0
      set nmsg [ctrl:filter $nmsg]
      foreach match [join [list $matches]] {
        incr idx
        if {[string match [lcase $match] $nmsg]} {
          if {![string match "*!*@*" $from]} {
            putlog "no!spam match for server notice - do nothing"
            return 0
          }
          set rsn [spamban n]
          if {![strlen $chan]} {
            ns:ban [spammask $from n] $botnick $rsn ${spam-notc-time}
            pushkick "" $nick $rsn  
          } else {
            if {${spam-notc-global}} {
              ns:ban [spammask $from n] $botnick $rsn ${spam-notc-time}
              pushkick "" $nick $rsn
            } else {
              ns:chanban $chan [spammask $from n] $botnick $rsn ${spam-notc-time}
              pushkick $chan $nick $rsn
            }
          }
          sl_write $from n $chan $nmsg $idx
          break
        }
      }
    }
    return 0
  }
  bind raw - NOTICE notc:abuse
  append nospam-loaded n
}

if {${spam-priv-check}} {
  proc priv:abuse {nick uhost hand text} {
    global spam-priv-time spam-priv-global botnick
    if {![strlen [set matches [sf_matches m]]] || [strlen [set text [lcase $text]]] <= 4 || [isbothost [set from "$nick!$uhost"]] || [spamignore $hand]} {return 0}
    set idx 0
    set text [ctrl:filter $text]
    foreach match [join [list $matches]] {
      incr idx
      if {[string match [lcase $match] $text]} {
        set rsn [spamban m]
        if {![strlen [set chan [findnick $nick]]]} {
          ns:ban [spammask $from m] $botnick $rsn ${spam-priv-time}
        } else {
          if {${spam-priv-global}} {
            ns:ban [spammask $from m] $botnick $rsn ${spam-priv-time}
            pushkick "" $nick $rsn
          } else {
            ns:chanban $chan [spammask $from m] $botnick $rsn ${spam-priv-time}
            pushkick $chan $nick $rsn
          }
        }
        sl_write $from m "" $text $idx
        break
      }
    }
    return 0
  }
  bind msgm - * priv:abuse
  append nospam-loaded m
}

if {${spam-invt-check}} {
  proc invt:abuse {from key arg} {
    global spam-invt-time spam-invt-global botnick
    if {[llength [set arg [split $arg]]] < 2 || [isbothost $from] || [spamignore $from]} {return 0}
    if {![validchan [string trimleft [lindex $arg 1] :]]} {
      set rsn [spamban i]
      if {![strlen [set chan [findnick [set nick [lsplit $from ! 0]]]]]} {
        ns:ban [spammask $from i] $botnick $rsn ${spam-invt-time}
      } else {
        if {${spam-invt-global}} {
          ns:ban [spammask $from i] $botnick $rsn ${spam-invt-time}
          pushkick "" $nick $rsn
        } else {
          ns:chanban $chan [spammask $from i] $botnick $rsn ${spam-invt-time}
          pushkick $chan $nick $rsn
        }
      }
      sl_write $from i $chan "" ""
    } 
    return 0 
  }
  bind raw - INVITE invt:abuse
  append nospam-loaded i
}

if {${spam-file-check}} {
  proc file:abuse {nick uhost hand dest key arg} {
    global spam-file-safe spam-file-global spam-file-time spam_file_warn botnick
    if {[isbothost [set from "$nick!$uhost"]] || [spamignore $hand]} {return 0}
    set arg [split $arg]
    if {[strcomp [lindex $arg 0] send]} {
      set file [lcase [lindex $arg 1]]
      if {[info exists spam-file-safe] && [llength ${spam-file-safe}] > 0} {
        foreach safe [join ${spam-file-safe}] {if {[string match [lcase $safe] $file]} {return 0}}
      }
      set rsn [spamban d]
      if {![strlen [set chan [findnick $nick]]]} {
        ns:ban [spammask $from d] $botnick $rsn ${spam-file-time}
      } else {
        if {${spam-file-global}} {
          ns:ban [spammask $from d] $botnick $rsn ${spam-file-time}
          pushkick "" $nick $rsn       
        } else {
          ns:chanban $chan [spammask $from d] $botnick $rsn ${spam-file-time}
          pushkick $chan $nick $rsn
        }
      }
      if {[info exists spam_file_warn([lcase $file])]} {
        foreach msg $spam_file_warn([lcase $file]) {puthelp "NOTICE $nick :$msg"}
      }
      sl_write $from d $chan $file ""
    }
    return 0
  }
  bind ctcp - DCC file:abuse
  append nospam-loaded d
  if {${spam-file-cycle}} {nospam_ctimer}
}

#---------------------------------------------------------------------------------------
#- dcc commands

proc dcc:+spam {hand idx arg} {
  putcmdlog "#$hand# +spam \002$arg"
  if {[llength [set arg [split $arg]]] < 2 || [strlen [set type [lcase [lindex $arg 0]]]] > 1} {spamsyntax $idx "+spam" ; return 0}
  if {[strlen [set spam [join [lrange $arg 1 end]]]] <= 4} {putidx $idx "Try a bigger search string than that." ; return 0}
  if {$type == "a"} {
    sf_append c $spam ; sf_append n $spam ; sf_append p $spam ; sf_append m $spam
    putidx $idx "Successfully added spam string \002$spam\002 to all types."
    return 0
  }
  sf_append $type $spam
  putidx $idx "Successfully added \002[fulltype $type]\002 spam string \002$spam"
  return 0
}

proc dcc:-spam {hand idx arg} {
  putcmdlog "#$hand# -spam \002$arg"
  if {[llength [set arg [split $arg]]] < 2 || [strlen [set type [lcase [lindex $arg 0]]]] > 1} {spamsyntax $idx "-spam" ; return 0}
  set spam [join [lrange $arg 1 end]]
  if {$type == "a"} {
    sf_remove c $spam ; sf_remove n $spam ; sf_remove p $spam ; sf_remove m $spam
    putidx $idx "Removed spam string \002$spam\002 from all types."
    return 0
  }
  if {[sf_remove $type $spam]} {
    putidx $idx "Removed \002[fulltype $type]\002 spam string \002$spam"
  } else {
    putidx $idx "Unable to remove \002[fulltype $type]\002 spam string \002$spam"
  }
  return 0
}

proc dcc:spams {hand idx arg} {
  putcmdlog "#$hand# spams \002$arg"
  if {![llength [set arg [split $arg]]]} {spamsyntax $idx "spams" ; return 0}
  set spams [sf_matches [set type [lcase [lindex $arg 0]]]]
  set type [fulltype $type]
  if {![strlen $spams]} {putidx $idx "You have no \002$type\002 spam strings." ; return 0}
  if {[llength $arg] == 1} {
    putidx $idx ",---- [llength [join $spams]] \002$type\002 spam strings"
    foreach spam [join [list $spams]] {putidx $idx "| $spam"}
    putidx $idx "`---- end"
  } else {
    set string [join [lrange $arg 1 end]]
    putidx $idx ",---- \002$type\002 spam strings, that match: $string"
    foreach spam [join [list $spams]] {if {[string match [lcase $string] [lcase $spam]]} {putidx $idx "| $spam"}}
    putidx $idx "`---- end"
  }
  return 0
}

proc dcc:+spamchan {hand idx arg} {
  putcmdlog "#$hand# +spamchan \002$arg"
  if {![llength [set arg [split $arg]]]} {spamsyntax $idx "+spamchan" ; return 0}
  set chan [lindex $arg 0]
  if {[llength $arg] == 1} {
    if {[spamchan $chan ""]} {
      set imsg "I am already monitoring: \002$chan"
    } else {
      scf_append $chan cdnp
      set imsg "Successfully added spam channel \002$chan+cdnp"
    }
  } else {
    set flags [lcase [lindex $arg 1]]
    if {[spamchan $chan ""]} {
      set cflags [scf_getentry $chan]
      if {[scf_addtype $cflags $flags]} {scf_write "" ; set imsg "Successfully changed \002$cflags\002 to \002[scf_getentry $chan]"} else {set imsg "Unable to make any changes to \002$chan"}
    } else {
      scf_append $chan $flags
      set imsg "Successfully added spam channel \002$chan+$flags"
    }
  }
  putidx $idx $imsg
  return 0
}

proc dcc:-spamchan {hand idx arg} {
  putcmdlog "#$hand# -spamchan \002$arg"
  if {![llength [set arg [split $arg]]]} {spamsyntax $idx "-spamchan" ; return 0}
  set chan [lindex $arg 0]
  if {[llength $arg] == 1} {
    if {[scf_remove $chan]} {set imsg "Successfully removed spam channel \002$chan"} else {set imsg "Unable to remove spam channel \002$chan"}
  } else {
    set flags [lcase [lindex $arg 1]]
    set cflags [scf_getentry $chan]
    if {[scf_remtype $cflags $flags]} {scf_write "" ; set imsg "Successfully changed \002$cflags\002 to \002[scf_getentry $chan]"} else {set imsg "Unable to make any changes to \002$chan"}
  }
  putidx $idx $imsg
  return 0
}

proc dcc:spamchans {hand idx arg} {
  global spam_chans spam-invt-check spam-priv-check
  putcmdlog "#$hand# spamchans \002$arg"
  putidx $idx ",---- spam channels  \[types\] \[#channel\]"
  if {[info exists spam_chans]} {
    foreach cflags $spam_chans {
      set type [lsplit $cflags + 1]
      if {${spam-invt-check}} {append type i}
      if {${spam-priv-check}} {append type m}
      putidx $idx "| $type	[lsplit $cflags + 0]"
    }
  }
  putidx $idx "`---- end"
  return 0
}

bind dcc ${spam-dcc-flag} +spam dcc:+spam
bind dcc ${spam-dcc-flag} -spam dcc:-spam
bind dcc ${spam-dcc-flag} spams dcc:spams
bind dcc ${spam-dcc-flag} +spamchan dcc:+spamchan
bind dcc ${spam-dcc-flag} -spamchan dcc:-spamchan
bind dcc ${spam-dcc-flag} spamchans dcc:spamchans

#---------------------------------------------------------------------------------------
#- load our spamfile buffers

catch {unset spam-chan-matches spam-notc-matches spam-part-matches spam-priv-matches spam_chans}

if {${spam-chan-check} && [fexists $spam_file(chan)]} {set spam-chan-matches [sf_read c]}
if {${spam-notc-check} && [fexists $spam_file(notc)]} {set spam-notc-matches [sf_read n]}
if {${spam-part-check} && [fexists $spam_file(part)]} {set spam-part-matches [sf_read p]}
if {${spam-priv-check} && [fexists $spam_file(priv)]} {set spam-priv-matches [sf_read m]}
if {[fexists ${spam-chan-file}]} {scf_read}

#---------------------------------------------------------------------------------------
#- we're done

set nospam-ver {10602 1.6.2}

putlog "no!spam [lindex ${nospam-ver} 1] \+${nospam-loaded} :Loaded"
