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 60
Code: 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"
}