Code: Select all
.chanset #chan x:flood:bantype 2
.chanset #chan x:repeat:rate 2:8
Code: Select all
.tcl unset ::xchannel::offenses
Code: Select all
<(TBOT> User defined channel flags:
<(TBOT>      +x:flood +x:repeat
<(TBOT> User defined channel settings:
<(TBOT> x:flood:bantype: 2   x:flood:bantime: 0   x:repeat:bantype: 2   x:repeat:bantime: 10   
<(TBOT> User defined channel strings:
<(TBOT> x:flood:punish: w:k
<(TBOT> x:flood:reason: {don't flood}
<(TBOT> x:repeat:punish: w:k
<(TBOT> x:repeat:reason: {repeating is lame}
<(TBOT> x:repeat:rate: 3:8
<(TBOT> flood settings: chan ctcp join kick deop nick
<(TBOT> number:           10    3    5    3    3    5
<(TBOT> time  :           60   60   60   10   10   60Code: Select all
###########################################################################################
#                                                                                         #
# xchannel.tcl -- universal channel protection script for eggdrop by demond@demond.net    #
#                                                                                         #
#                 a higly-optimized, versatile and modular script for all of your channel #
#                 protection needs; using an uniform routine for counting and punishing   #
#                 offenses, it's been designed for easier further enhancement and adding  #
#                 new offense type handlers; all configuration is done via .chanset       #
#                                                                                         #
#                 as of the current version, supported offense types are:                 #
#                                                                                         #
#                   % plain public flood (via chan message, notice, ctcp action)          #
#                   % mass flood (by a botnet via mass join, part, nick, public flood)    #
#                   % repeating                                                           #
#                   % using CAPS                                                          #
#                   % using colors                                                        #
#                   % spamming with URLs                                                  #
#                   % profanity (using bad words)                                         #
#                   % in addition, the script features automatic +l limiter               #
#                                                                                         #
#                 DO NOT EDIT this script (even if you know what you are doing ;)         #
#                 instead, configure it to suit you from bot's party-line by using        #
#                 .chanset command and the following settings (you have to specify any    #
#                 of these only if you need default value changed; to see defaults, use   #
#                 .chaninfo #chan):                                                       #
#                                                                                         #
#       common -- (these are mandatory for each offense type handler)                     #
#                                                                                         #
#                 below, 'type' is one of: repeat, flood, color, spam, caps, bad          #
#                                                                                         #
#                 x:type         - the on/off switch                                      #
#                 x:type:punish  - sequence of letters w, k, or b, separated by a colon   #
#                                  for example, w:k:b means: on 1st offense, warn only;   #
#                                  on 2nd offense, kick; on 3rd and more offenses, ban    #
#                 x:type:bantype - ban type number (see [maskhost] proc below for types)  #
#                 x:type:bantime - ban time in minutes                                    #
#                 x:type:reason  - the kick/ban reason                                    #
#                                                                                         #
#       custom -- (these are specific for a particular offense type)                      #
#                                                                                         #
#                 repeat handler     - x:repeat:rate   (n:m) n repeats in m seconds       #
#                 mass flood handler - x:mass:rate     (n:m) n events in m seconds        #
#                                      x:mass:duration (n)   +mi locking duration, in min #
#                 caps handler       - x:caps:percent  (n)   percentage of cap letters    #
#                 bad words handler  - x:bad:file   (name)   file name of bad words file  # 
#                 +l limiter         - x:limit:slack   (n)   minimal difference between   #
#                                                            current user count and +l    #
#                                                                                         #
#                 IMPORTANT: all offense handlers (except mass) are disabled by default   #
#                            to enable particular protection, use .chanset #chan +x:type  #
#                                                                                         #
#                      NOTE: defined bad words are treated as regular expressions (RE)    #
#                                                                                         #
#  ver.history -- 1.0 - initial version                                                   #
#                                                                                         #
###########################################################################################
package require Tcl 8.3
package require eggdrop 1.6
namespace eval xchannel {
variable version "xchannel-1.0"
variable names {punish bantype bantime reason}
variable udefs {str    int     int     str}
set conf1g(repeat) {pubm w:k:b 1 10 "repeating is lame"}
set conf1g(flood)  {flud w:k:b 1 10 "stop flooding"}
set conf1g(color)  {pubm w:k:b 1 10 "no colors"}
set conf1g(spam)   {pubm w:k:b 1 10 "spam"}
set conf1g(caps)   {pubm w:k:b 1 10 "caps off"}
set conf1g(bad)    {pubm w:k:b 1 10 "do not curse"}
set custom(repeat) {rate:str:3:10}
set custom(mass)   {rate:str:20:3 duration:int:5}
set custom(limit)  {slack:int:5}
set custom(caps)   {percent:int:70}
set custom(bad)    {file:str:badwords.txt}
variable mbinds {join part nick pubm}
variable lbinds {join part kick sign}
variable arrays {offenses:10:720 repeats:2:5}
variable handlers {repeat color spam caps bad}
variable colore {([\002\017\026\037]|[\003]{1}[0-9]{0,2}[\,]{0,1}[0-9]{0,2})}
bind time - * [namespace current]::timer
proc maskhost {nuhost {type 0}} {
	scan $nuhost {%[^!]!%[^@]@%s} nick user host
	scan [set mask [::maskhost $nuhost]] {%*[^@]@%s} mhost
	switch $type {
	0 - 1 {return $mask}       ;# *!*foo@*.bar.com
	2 {return *!*@$host}       ;# *!*@moo.bar.com
	3 {return *!*@$mhost}      ;# *!*@*.bar.com
	4 {return *!*$user@$host}  ;# *!*foo@moo.bar.com
	5 {return *!*$user@*}      ;# *!*foo@*
	6 {return $nuhost}
	}
}
proc fixargs {chan text args} {
	upvar $chan xchan; upvar $text xtext
	if {$::lastbind == "**"} {
		set xtext $xchan
		set xchan [lindex $args 0]
	} {
		set n [llength $args]
		set xtext [lindex $args [incr n -1]]
	}
}
proc init {chan} {
	variable handlers
	variable names; variable udefs
	variable mbinds; variable lbinds
	variable conf1g; variable custom
	setudef flag x:limit
	foreach elem $lbinds {
		bind $elem - * [namespace current]::limit
	}
	foreach elem $mbinds {
		bind $elem - * [namespace current]::mass
	}
	foreach {type data} [array get conf1g] {
		setudef flag x:$type
		bind [lindex $data 0] - * [namespace current]::$type
		if {[lsearch $handlers $type] != -1} {
			bind notc - ** [namespace current]::$type
			bind ctcp - * [namespace current]::$type
		}
		foreach val [lrange $data 1 e] name $names udef $udefs {
			setudef $udef x:$type:$name
			if {$udef == "int"} {set void 0} {set void ""}
			if {[channel get $chan x:$type:$name] == $void} {
				channel set $chan x:$type:$name $val
			}
		}
	}
	foreach {type data} [array get custom] {
		foreach elem $data {
			scan $elem {%[^:]:%[^:]:%s} name udef val
			setudef $udef x:$type:$name
			if {$udef == "int"} {set void 0} {set void ""}
			if {[channel get $chan x:$type:$name] == $void} {
				channel set $chan x:$type:$name $val
			}
		}		
	}
}
proc timer {min hour day month year} {
	variable arrays
	foreach elem $arrays {
		scan $elem {%[^:]:%[^:]:%s} name freq val
		upvar #1 [namespace current]::$name arr
		if {([unixtime]/60) % $freq == 0} {
			if {[info exists arr]} {
			foreach {hash data} [array get arr] {
				set ts [lindex $data 0]
				if {[unixtime] - $ts >= $val*60} {
					unset arr($hash)
				}
			}}
		} 
	}
}
proc punish {nick uhost hand chan type {subtype ""}} {
	variable offenses
	if {[isop $nick $chan] || 
	    [matchattr $hand of|of $chan]} {return}
	if {$subtype == ""} {set what $type} {set what $subtype}
	set chan [string tolower $chan]
	set hash [md5 $chan:$what:[maskhost $nick!$uhost]]
	if {![info exists offenses($hash)]} {set n 1} {
		set n [lindex $offenses($hash) 1]; incr n
	}
	set offenses($hash) [list [unixtime] $n]
	set reason "[channel get $chan x:$type:reason] ($n)"
	set punish [split [channel get $chan x:$type:punish] :]
	set len [llength $punish]; if {$n > $len} {set n $len}
	switch [lindex $punish [incr n -1]] {
	"w" {putserv "privmsg $nick :$reason"}
	"k" {if {[botisop $chan]} {putkick $chan $nick $reason}}
	"b" {
		set time [channel get $chan x:$type:bantime]
		set type [channel get $chan x:$type:bantype]
		newchanban $chan [maskhost $nick!$uhost $type] $::nick $reason $time
		if {[botisop $chan]} {putkick $chan $nick $reason}
	}}
}
proc repeat {nick uhost hand chan args} {
	variable repeats
	fixargs chan text $args
	if {[isbotnick $chan]} {return}
	set chan [string tolower $chan]
	if {![channel get $chan x:repeat]} {return}
	scan [channel get $chan x:repeat:rate] {%[^:]:%s} maxr maxt
	set hash [md5 $chan:$text:[maskhost $nick!$uhost]]
	if {![info exists repeats($hash)]} {
		set n 1; set ts [unixtime]
	} {
		set n [lindex $repeats($hash) 1]; incr n
		set ts [lindex $repeats($hash) 0]
		if {[unixtime] - $ts >= $maxt} {
			set n 1; set ts [unixtime]
		} {
			if {$n >= $maxr} {
				punish $nick $uhost $hand $chan repeat
				unset repeats($hash); return
			}
		} 
	}
	set repeats($hash) [list $ts $n]
}
proc mass {nick uhost hand chan args} {
	variable mcount; variable version
	set chan [string tolower $chan]
	if {![info exists mcount($chan)]} {
		set n 1; set ts [unixtime]
	} {
		set n [lindex $mcount($chan) 1]; incr n
		set ts [lindex $mcount($chan) 0]
		scan [channel get $chan x:mass:rate] {%[^:]:%s} maxr maxt
		if {$n >= $maxr} {
			if {[unixtime] - $ts <= $maxt} {
				#set buf "mode $chan +mi\n"
				#putdccraw 0 [llength $buf] $buf
				putquick "mode $chan +im" -next
				putlog "$version: Mass Flood on $chan!!! Locking..."
		utimer	5 [list	putserv "notice $chan :Mass Flood!!! We'll re-open shortly..."]
				set dur [channel get $chan x:mass:duration]
				::timer $dur [list putserv "mode $chan -mi"]
				unset mcount($chan); return
			} {
				set n 1; set ts [unixtime]
			}
		}
	}
	set mcount($chan) [list $ts $n]
}
proc limit {nick uhost hand chan args} {
	if {![channel get $chan x:limit]} {return}
	set slack [channel get $chan x:limit:slack]
	set len [llength [chanlist $chan]]
	set lim [split [getchanmode $chan]]
	if {[string first l [lindex $lim 0]] == -1} {
		set limit $len
	} elseif {[string first k [lindex $lim 0]] == -1} {
		set limit [lindex $lim 1]
	} {
		set limit [lindex $lim 2]
	}
	if {$limit - $len < $slack} {
		incr limit $slack
	} elseif {$limit - $len > [expr 2*$slack]} {
		incr limit -$slack
	} {return}
	putserv "mode $chan +l $limit" 
}
proc bad {nick uhost hand chan args} {
	variable words; variable colore
	fixargs chan text $args
	if {[isbotnick $chan]} {return}
	set chan [string tolower $chan]
	if {![channel get $chan x:bad]} {return}
	if {[info exists words($chan)]} {
	regsub -all $colore $text {} text
	foreach elem $words($chan) {
		if {[regexp -nocase -- $elem $text]} {
			punish $nick $uhost $hand $chan bad
		}
	}} 
}
proc caps {nick uhost hand chan args} {
	fixargs chan text $args
	if {[isbotnick $chan]} {return}
	if {![channel get $chan x:caps]} {return}
	set n 0; foreach c [split $text {}] {
		if {[string is upper $c]} {incr n}
	}
	set pct [channel get $chan x:caps:percent]
	if {100*$n/[string length $text] >= $pct} {
		punish $nick $uhost $hand $chan caps
	}
}
proc color {nick uhost hand chan args} {
	variable colore
	fixargs chan text $args
	if {[isbotnick $chan]} {return}
	if {![channel get $chan x:color]} {return}
	if {[regexp $colore $text]} {
		punish $nick $uhost $hand $chan color
	}
}
proc spam {nick uhost hand chan args} {
	variable colore
	fixargs chan text $args
	if {[isbotnick $chan]} {return}
	if {![channel get $chan x:spam]} {return}
	regsub -all $colore $text {} text
	if {[regexp {(?i)(http://|www\.|irc\.|\s#)} $text]} {
		punish $nick $uhost $hand $chan spam
	}
}
proc flood {nick uhost hand type chan} {
	if {$chan == "*"} {return}
	if {![channel get $chan x:flood]} {return}
	if {$type == "kick" || $type == "deop"} {return}
	punish $nick $uhost $hand $chan flood $type
	return 1
}
foreach chan [channels] {
	init $chan
	set fn [channel get $chan x:bad:file] 
	set chan [string tolower $chan]
	if {![catch {set f [open $fn]} err]} {
		set words($chan) {}
		foreach elem [split [read $f] " \t\r\n\f"] {
			if {$elem != ""} {lappend words($chan) $elem} 
		}
		close $f
	} {
		putlog "$version: $err"
	}
}
putlog "$version by demond loaded"
}