#by wiebe @ QuakeNet

#features:
#support for hidden users (quakenet chanmode D/d)
#minimum/maximum settings
#overhead/underhead settings to allow user count to increase/decrease without needing to change limit all the time
#check time at 1 minute resolution (so check every 1,2,3,4,...,N minutes)

#.autolimit sync over botnet
#allow * to set all chans

#################################################################
#config
#################################################################

#default settings, used when nothing is set, or - (or nothing) is specified in autolimit command

#default delay settings (minutes)
set autolimitdefault(delay) 3

#default buffer setting
set autolimitdefault(buffer) 10

#default overhead setting
set autolimitdefault(overhead) 3

#default underhead setting
set autolimitdefault(underhead) 2

#default minimum setting
set autolimitdefault(minimum) 0

#default maximum setting
set autolimitdefault(maximum) 0

#default hidden setting
set autolimitdefault(hidden) 1

#################################################################
#config end
#################################################################

#################################################################
#code
#################################################################

#setting flags
setudef flag autolimit
setudef flag autolimit-hidden
setudef int autolimit-delay
setudef int autolimit-buffer
setudef int autolimit-overhead
setudef int autolimit-underhead
setudef int autolimit-minimum
setudef int autolimit-maximum


bind time - "* * * * *" autolimit:allcheck

#autolimit:allcheck
proc autolimit:allcheck { mi ho da mo ye } {
  global autolimitdefault autolimittime server
  if { [string equal $server ""] } { return }
  set now [clock seconds]
  if { [info exist now] } {
    foreach chan [channels] {
      if { [channel get $chan autolimit] } {
        if { ([botisop $chan]) || ([botishalfop $chan]) } {
          set delay [expr $autolimitdefault(delay) * 60]
          if { ([string is digit [channel get $chan autolimit-delay]]) && (![string equal [channel get $chan autolimit-delay] ""]) } {
            set delay [expr [channel get $chan autolimit-delay] * 60]
          }
          if { (![string is digit $delay]) || $delay < 0 || (![info exist delay]) } {
            set delay [expr 3 * 60]
          }
          if { ![info exist autolimittime($chan)] } { set autolimittime($chan) 1 }
          set last $autolimittime($chan)
          if { [string equal $last 1] || $now >= [expr $last + $delay] } {
            set autolimittime($chan) $now
            autolimit:check $chan
          }
          if { [info exist last] } { unset last }
          if { [info exist delay] } { unset delay }
        }
      }
    }
  }
}


#autolimit:check chan
proc autolimit:check { chan } {
  global autolimitdefault autolimithidden

#check if +autolimit is set
  if { ![channel get $chan autolimit] } { return }

#check if bot is +o or +h
  if { (![botisop $chan]) && (![botishalfop $chan]) } { return }

#get and check settings buffer
  set buffer [channel get $chan autolimit-buffer]
  if { (![string is digit $buffer]) || $buffer <= 0 || (![info exist buffer]) } { set buffer $autolimitdefault(buffer) }
  if { (![string is digit $buffer]) || $buffer <= 0 || (![info exist buffer]) } { set buffer 10 }

#get and check settings overhead
  set overhead [channel get $chan autolimit-overhead]
  if { (![string is digit $overhead]) || $overhead < 0 || (![info exist overhead]) } { set overhead $autolimitdefault(overhead) }
  if { (![string is digit $overhead]) || $overhead < 0 || (![info exist overhead]) } { set overhead 2 }

#get and check settings underhead
  set underhead [channel get $chan autolimit-underhead]
  if { (![string is digit $underhead]) || $underhead < 0 || (![info exist underhead]) } { set underhead $autolimitdefault(underhead) }
  if { (![string is digit $underhead]) || $underhead < 0 || (![info exist underhead]) } { set underhead 2 }

#get and check settings minimum
  set minimum [channel get $chan autolimit-minimum]
  if { (![string is digit $minimum]) || $minimum < 0 || (![info exist minimum]) } { set minimum $autolimitdefault(minimum) }
  if { (![string is digit $minimum]) || $minimum < 0 || (![info exist minimum]) } { set minimum 0 }

#get and check settings maximum
  set maximum [channel get $chan autolimit-maximum]
  if { (![string is digit $maximum]) || $maximum < 0 || (![info exist maximum]) } { set maximum $autolimitdefault(maximum) }
  if { (![string is digit $maximum]) || $maximum < 0 || (![info exist maximum]) } { set maximum 0 }

#check if we need to check for hidden users
  if { (![channel get $chan autolimit-hidden]) || (([channel get $chan autolimit-hidden]) && (![string match *d* [lindex [split [string tolower [getchanmode $chan]]] 0]])) } {

#get new limit
    set limit [autolimit:check.calc $chan $buffer "$overhead $underhead $minimum $maximum"]

#check and set new limit
    if { [info exist limit] } { pushmode $chan +l $limit }

#checking hidden users
  } else {
    set autolimithidden($chan) "1"
    puthelp "LIST $chan"
  }
}


#autolimit:check.calc chan buffer overhead underhead minimum maximum hidden
proc autolimit:check.calc { chan buffer ar } {

#check if the bot is on chan
  if { ![botonchan $chan] } { return }

#get the number of users
  set users [llength [chanlist $chan]]

#get the overhead
  set overhead [lindex [split $ar] 0]
  if { ![info exist overhead] } { set overhead 0 }
  if { [string equal $overhead ""] } { set overhead 0 }

#get the underhead
  set underhead [lindex [split $ar] 1]
  if { ![info exist underhead] } { set underhead 0 }
  if { [string equal $underhead ""] } { set underhead 0 }

#get the minimum
  set minimum [lindex [split $ar] 2]
  if { ![info exist minimum] } { set minimum 0 }
  if { [string equal $minimum ""] } { set minimum 0 }

#get the maximum
  set maximum [lindex [split $ar] 3]
  if { ![info exist maximum] } { set maximum 0 }
  if { [string equal $maximum ""] } { set maximum 0 }

#get the number of hidden users
  set hidden [lindex [split $ar] 4]
  if { ![info exist hidden] } { set hidden 0 }
  if { [string equal $hidden ""] } { set hidden 0 }

#get total number of users
  set users [expr $users + $hidden]

#check if we have numbers
  if { ![string is digit $buffer] } { return }
  if { ![string is digit $overhead] } { return }
  if { ![string is digit $underhead] } { return }
  if { ![string is digit $minimum] } { return }
  if { ![string is digit $maximum] } { return }
  if { ![string is digit $hidden] } { return }

#check for correct numbers
  if { [expr $buffer - $overhead] <= 0 } { set overhead 0 }
  if { [expr $buffer - $underhead] <= 0 } { set underhead 0 }
  if { [expr $buffer - $overhead - $underhead] <= 0 } { set overhead 0 }
  if { $maximum < $minimum && $maximum > 0 } { set maximum 0 }

#get the current modes set
  set modes [string tolower [getchanmode $chan]]

#get the 1st word of the modes
  set modev [lindex [split $modes] 0]

#set some vars
  set count 0
  set num 0
  set limit 0

#while loop to get the right position of l
  while {$count < [string length $modev]} {
    set ch [string index $modev $count]
    if { [string equal $ch k]} {
      incr num
    }
    if { [string equal $ch l] } {
      incr num
#get the limit
	set limit [lindex [split $modes] $num]
    }
    incr count
  }

#get the new limit
  set newlimit [expr $users + $buffer - $overhead]

#get highest and lowest values
  set highest [expr $users + $buffer]
  set lowest [expr $users + $buffer - $overhead - $underhead]

#check if the limit is within range
  if { $highest >= $limit && $limit >= $lowest } {

#check if limit is lower then minimum, else stop
    if { $limit < $minimum } { return $minimum } else { return }
  }

#check if newlimit is smaller than minimum
  if { $newlimit < $minimum } {
    if { ![string equal $minimum $limit] } {
      return $minimum
    }
    return
  }

#check if newlimit is bigger than maximum
  if { $newlimit > $maximum && $maximum > 0 } {
    set newlimit $maximum
  }

#return the newlimit
  if { ![string equal $limit $newlimit] } { return $newlimit }
}


#This is returned for each channel in a LIST reply.
#The user count includes all users, invisable or otherwise.
#If no topic is currently set, none will be given, although the colon will still be included,
#format: server 322 <you> <channel> <users> :<topic>

bind raw -|- "322" autolimit:hidden

proc autolimit:hidden { server raw ar } {
  global autolimithidden
  set chan [lindex [split $ar] 1]
  set users [lindex [split $ar] 2]
#check var
  if { [info exist autolimithidden($chan)] } {
    if { [string equal $autolimithidden($chan) 1] } {
      autolimit:check.hidden $chan $users
      unset autolimithidden($chan)
    }
  }
  return 0
}


#autolimit:check.hidden chan users
proc autolimit:check.hidden { chan users } {
  global autolimitdefault autolimithidden

#check if +autolimit is set
  if { ![channel get $chan autolimit] } { return }

#check if bot is +o or +h
  if { (![botisop $chan]) && (![botishalfop $chan]) } { return }

#get and check settings buffer
  set buffer [channel get $chan autolimit-buffer]
  if { (![string is digit $buffer]) || $buffer <= 0 || (![info exist buffer]) } { set buffer $autolimitdefault(buffer) }
  if { (![string is digit $buffer]) || $buffer <= 0 || (![info exist buffer]) } { set buffer 10 }

#get and check settings overhead
  set overhead [channel get $chan autolimit-overhead]
  if { (![string is digit $overhead]) || $overhead < 0 || (![info exist overhead]) } { set overhead $autolimitdefault(overhead) }
  if { (![string is digit $overhead]) || $overhead < 0 || (![info exist overhead]) } { set overhead 2 }

#get and check settings underhead
  set underhead [channel get $chan autolimit-underhead]
  if { (![string is digit $underhead]) || $underhead < 0 || (![info exist underhead]) } { set underhead $autolimitdefault(underhead) }
  if { (![string is digit $underhead]) || $underhead < 0 || (![info exist underhead]) } { set underhead 2 }

#get and check settings minimum
  set minimum [channel get $chan autolimit-minimum]
  if { (![string is digit $minimum]) || $minimum < 0 || (![info exist minimum]) } { set minimum $autolimitdefault(minimum) }
  if { (![string is digit $minimum]) || $minimum < 0 || (![info exist minimum]) } { set minimum 0 }

#get and check settings maximum
  set maximum [channel get $chan autolimit-maximum]
  if { (![string is digit $maximum]) || $maximum < 0 || (![info exist maximum]) } { set maximum $autolimitdefault(maximum) }
  if { (![string is digit $maximum]) || $maximum < 0 || (![info exist maximum]) } { set maximum 0 }

#get new limit
  set hidden [expr $users - [llength [chanlist $chan]]]
  set limit [autolimit:check.calc $chan $buffer "$overhead $underhead $minimum $maximum $hidden"]

#check and set new limit
  if { [info exist limit] } { pushmode $chan +l $limit }
}


proc autolimit:info { chan } {
  global autolimitdefault

#check if autolimit is on or off
  if { ![channel get $chan autolimit] } {
    set status off
  } else {
    set status on
  }

#get and check settings delay
  set delay [channel get $chan autolimit-delay]
  if { (![string is digit $delay]) || $delay <= 0 || (![info exist delay]) } {
    set delay $autolimitdefault(delay)
    channel set $chan autolimit-delay $delay
  }
  if { (![string is digit $delay]) || $delay <= 0 || (![info exist delay]) } {
    set delay 3
    channel set $chan autolimit-delay $delay
  }

#get and check settings buffer
  set buffer [channel get $chan autolimit-buffer]
  if { (![string is digit $buffer]) || $buffer <= 0 || (![info exist buffer]) } {
    set buffer $autolimitdefault(buffer)
    channel set $chan autolimit-buffer $buffer
  }
  if { (![string is digit $buffer]) || $buffer <= 0 || (![info exist buffer]) } {
    set buffer 10
    channel set $chan autolimit-buffer $buffer
  }

#get and check settings overhead
  set overhead [channel get $chan autolimit-overhead]
  if { (![string is digit $overhead]) || $overhead < 0 || (![info exist overhead]) } {
    set overhead $autolimitdefault(overhead)
    channel set $chan autolimit-overhead $overhead
  }
  if { (![string is digit $overhead]) || $overhead < 0 || (![info exist overhead]) } {
    set overhead 3
    channel set $chan autolimit-overhead $overhead
  }

#get and check settings underhead
  set underhead [channel get $chan autolimit-underhead]
  if { (![string is digit $underhead]) || $underhead < 0 || (![info exist underhead]) } {
    set underhead $autolimitdefault(underhead)
    channel set $chan autolimit-underhead $underhead
  }
  if { (![string is digit $underhead]) || $underhead < 0 || (![info exist underhead]) } {
    set underhead 2
    channel set $chan autolimit-underhead $underhead
  }

#get and check settings minimum
  set minimum [channel get $chan autolimit-minimum]
  if { (![string is digit $minimum]) || $minimum < 0 || (![info exist minimum]) } {
    set minimum $autolimitdefault(minimum)
    channel set $chan autolimit-minimum $minimum
  }
  if { (![string is digit $minimum]) || $minimum < 0 || (![info exist minimum]) } {
    set minimum 0
    channel set $chan autolimit-minimum $minimum
  }

#get and check settings maximum
  set maximum [channel get $chan autolimit-maximum]
  if { (![string is digit $maximum]) || $maximum < 0 || (![info exist maximum]) } {
    set maximum $autolimitdefault(maximum)
    channel set $chan autolimit-maximum $maximum
  }
  if { (![string is digit $maximum]) || $maximum < 0 || (![info exist maximum]) } {
    set maximum 0
    channel set $chan autolimit-maximum $maximum
  }

#get and check setting for hidden users
  if { ![channel get $chan autolimit-hidden] } {
    set hidden 0
  } else {
    set hidden 1
  }
  return "status=$status delay=$delay buffer=$buffer overhead=$overhead underhead=$underhead minimum=$minimum maximum=$maximum hidden=$hidden"
}


proc autolimit:set { ar } {
  global autolimitdefault
  set chan [lindex [split $ar] 0]

#get and check settings delay
  set delay [lindex [split $ar] 1]
  if { [string equal $delay -] } { set delay $autolimitdefault(delay) }
  if { (![string is digit $delay]) || $delay <= 0 || (![info exist delay]) } {
    set delay $autolimitdefault(delay)
  }
  if { (![string is digit $delay]) || $delay <= 0 || (![info exist delay]) } {
    set delay 3
  }
  channel set $chan autolimit-delay $delay

#get and check settings buffer
  set buffer [lindex [split $ar] 2]
  if { [string equal $buffer -] } { set buffer $autolimitdefault(buffer) }
  if { (![string is digit $buffer]) || $buffer <= 0 || (![info exist buffer]) } {
    set buffer $autolimitdefault(buffer)
  }
  if { (![string is digit $buffer]) || $buffer <= 0 || (![info exist buffer]) } {
    set buffer 10
  }
  channel set $chan autolimit-buffer $buffer

#get and check settings overhead
  set overhead [lindex [split $ar] 3]
  if { [string equal $overhead -] } { set overhead $autolimitdefault(overhead) }
  if { (![string is digit $overhead]) || $overhead < 0 || (![info exist overhead]) } {
    set overhead $autolimitdefault(overhead)
  }
  if { (![string is digit $overhead]) || $overhead < 0 || (![info exist overhead]) } {
    set overhead 3
  }
  channel set $chan autolimit-overhead $overhead

#get and check settings underhead
  set underhead [lindex [split $ar] 4]
  if { [string equal $underhead -] } { set underhead $autolimitdefault(underhead) }
  if { (![string is digit $underhead]) || $underhead < 0 || (![info exist underhead]) } {
    set underhead $autolimitdefault(underhead)
  }
  if { (![string is digit $underhead]) || $underhead < 0 || (![info exist underhead]) } {
    set underhead 2
  }
  channel set $chan autolimit-underhead $underhead

#get and check settings minimum
  set minimum [lindex [split $ar] 5]
  if { [string equal $minimum -] } { set minimum $autolimitdefault(minimum) }
  if { (![string is digit $minimum]) || $minimum < 0 || (![info exist minimum]) } {
    set minimum $autolimitdefault(minimum)
  }
  if { (![string is digit $minimum]) || $minimum < 0 || (![info exist minimum]) } {
    set minimum 0
  }
  channel set $chan autolimit-minimum $minimum

#get and check settings maximum
  set maximum [lindex [split $ar] 6]
  if { [string equal $maximum -] } { set maximum $autolimitdefault(maximum) }
  if { (![string is digit $maximum]) || $maximum < 0 || (![info exist maximum]) } {
    set maximum $autolimitdefault(maximum)
  }
  if { (![string is digit $maximum]) || $maximum < 0 || (![info exist maximum]) } {
    set maximum 0
  }
  channel set $chan autolimit-maximum $maximum

  set hidden [lindex [split $ar] 7]
  if { [string equal $hidden -] } { set hidden $autolimitdefault(hidden) }
  if { ![string equal $hidden 1] && ![string equal $hidden 0] } { set hidden 0 }
  if { [string equal $hidden 1] } { channel set $chan +autolimit-hidden } else { channel set $chan -autolimit-hidden }

}



bind dcc - autolimit autolimit:dcc

proc autolimit:dcc { handle idx ar } {
  global lastbind
  set chan [lindex [split $ar] 0]
  if { ![validchan $chan] } {
    putidx $idx "autolimit: error, $lastbind <chan> <on|off|info|set> \[delay|-\] \[buffer|-\] \[overhead|-\] \[underhead|-\] \[minimum|-\] \[maximum|-\] \[hidden|-\]"
    return
  }
  set cmd [lindex [split $ar] 1]
  if { [string equal $cmd ""] } {
    putidx $idx "autolimit: error, $lastbind <chan> <on|off|info|set> \[delay|-\] \[buffer|-\] \[overhead|-\] \[underhead|-\] \[minimum|-\] \[maximum|-\] \[hidden|-\]"
    return
  }
  if { (![matchattr $handle m|- $chan]) && (![matchattr $handle -|m $chan]) } {
   putidx $idx "autolimit: you have no access for $chan"
   return
  }
  if { [string equal -nocase $cmd info] } {
    putidx $idx "autolimit: settings for $chan [autolimit:info $chan]"
  } elseif { [string equal -nocase $cmd set] } {
    autolimit:set "$chan [join [lrange [split $ar] 2 end]]"
    channel set $chan +autolimit
    putidx $idx "autolimit: settings for $chan [autolimit:info $chan]"
  } elseif { [string equal -nocase $cmd off] } {
    channel set $chan -autolimit
    putidx $idx "autolimit: off for $chan"
  } elseif { [string equal -nocase $cmd on] } {
    autolimit:set "$chan [join [lrange [split $ar] 2 end]]"
    channel set $chan +autolimit
    putidx $idx "autolimit: on for $chan with settings [autolimit:info $chan]"
  } else {
    putidx $idx "autolimit: error, $lastbind <chan> <on|off|info|set> \[delay|-\] \[buffer|-\] \[overhead|-\] \[underhead|-\] \[minimum|-\] \[maximum|-\] \[hidden|-\]"
  }
}


#bind msg mn|mn autolimit autolimit:msg

proc autolimit:msg { nick uhost handle text } {
  global lastbind
  set chan [lindex [split $text] 0]
  set cmd [lindex [split $text] 1]
  if { [string equal $chan ""] || [string equal $cmd ""] } {
    lappend output "usage: autolimit <chan> <on|off|info|set> \[delay|-\] \[buffer|-\] \[overhead|-\] \[underhead|-\] \[minimum|-\] \[maximum|-\] \[hidden|-\]"
  } elseif { ![validchan $chan] || ![matchattr $handle m|m $chan] } {    lappend output "$chan\017 is an unknown channel or you have no access on it."
  } elseif { [string equal -nocase $cmd info] } {
    lappend output "Autolimit settings for $chan [autolimit:info $chan]"
  } elseif { [string equal -nocase $cmd set] } {
    autolimit:set "$chan [join [lrange [split $text] 2 end]]"
    channel set $chan +autolimit
    lappend output "Autolimit settings for $chan [autolimit:info $chan]"
  } elseif { [string equal -nocase $cmd off] } {
    channel set $chan -autolimit
    lappend output "Autolimit off for $chan"
  } elseif { [string equal -nocase $cmd on] } {
    autolimit:set "$chan [join [lrange [split $text] 2 end]]"
    channel set $chan +autolimit
    lappend output "Autolimit on for $chan with settings [autolimit:info $chan]"
  } else {
    lappend output "usage: autolimit <chan> <on|off|info|set> \[delay|-\] \[buffer|-\] \[overhead|-\] \[underhead|-\] \[minimum|-\] \[maximum|-\] \[hidden|-\]"
  }
  if { [catch {set x [cnotice $nick $output]} error] || !$x } {
    foreach t $output { puthelp "NOTICE $nick :$t" }
  }
}


bind mode - "% +l" autolimit:mode

proc autolimit:mode { nick uhost handle chan mode param } {
  if { ![string equal $nick ""] } { return 0 }
  if { [string match *@* $uhost] } { return 0 }
  autolimit:check $chan
}



#netsplit
bind splt -|- * autolimit:splt

proc autolimit:splt { nick uhost handle chan } {
  global autolimittime
  set autolimittime($chan) 1
}

