Posted to tcl by Army at Sat Oct 24 19:07:32 GMT 2026view raw
- # antiflood.tcl - Eggdrop script (Eggdrop 1.9+ for UTF-8, bot must be IRCop)
- # Add to eggdrop.conf: source scripts/antiflood.tcl
- # Also: .+chan #shqiperia .+chan #shoqeria .+chan #opers
- # Commands (in #opers): .sh add nick/ip | .sh rem nick/ip | .sh list
- # .f on | .f off | .f list | .f help (list/help answer in Albanian)
- # .f add 3:1 / .f del 3:1 -> rule: 3 similar lines in 1 second (count:seconds)
- # .f add 50 / .f del 50 -> only check texts with at least 50 characters
- # .f add shuntime 5 / 10 / 15 -> shun time in minutes (default 5); .f del shuntime -> back to 5
- # .f emoji on/off -> on: emoji-only text is not shunned; off: it is checked too
- # (trailing emoji after a text are always ignored when comparing, so text+emoji flood is shunned)
-
- # UTF-8: Eggdrop 1.9+ is already UTF-8 -> keep utf8conv 0.
- # Eggdrop 1.8 passes raw bytes -> set utf8conv 1 and the script decodes input / encodes output itself.
- array set fl {
- utf8conv 0 opers #opers sim 0.80 minlen 8 deflen 8 emojiex 1
- shunmin 5 defshun 5 emojishun 60 irccloudshun 60 on 1 oper_user botoper oper_pass operpass
- }
- set fl(chans) {#shqiperia #shoqeria}
- set fl(hist) {}
- set fl(alerts) {} ;# notice batches: dicts {norm text chans items ts}
- set fl(known) {} ;# flood texts remembered until 00:00 UTC: {norm text ts}
- set fl(day) [clock format [clock seconds] -format %Y-%m-%d -gmt 1]
- set fl(rules) {{2 1}} ;# {lines seconds}: 2 similar lines in 1 second
- set fl(shuns) {}
-
- proc fl:put {line} {
- global fl
- if {$fl(utf8conv)} {set line [encoding convertto utf-8 $line]}
- putserv $line
- }
- proc fl:in {s} {
- global fl
- if {$fl(utf8conv)} {return [encoding convertfrom utf-8 $s]}
- return $s
- }
- proc fl:say {m} {global fl; fl:put "PRIVMSG $fl(opers) :$m"}
- proc fl:hms {{t ""}} {if {$t eq ""} {set t [clock seconds]}; clock format $t -format %H:%M:%S}
- proc fl:nop {ip} {}
- proc fl:isip {x} {expr {[regexp {^[0-9.]+$} $x] || [regexp {^[0-9a-fA-F:]*:[0-9a-fA-F:]*$} $x]}}
-
- # emoji helpers (Eggdrop 1.9+ UTF-8)
- proc fl:isemo {n} {
- expr {$n <= 32 || ($n >= 0xD800 && $n <= 0xDFFF) || ($n >= 0x1F000 && $n <= 0x1FAFF)
- || ($n >= 0xE0020 && $n <= 0xE007F) || ($n >= 0x2190 && $n <= 0x21FF)
- || ($n >= 0x2300 && $n <= 0x23FF) || ($n >= 0x2600 && $n <= 0x27BF)
- || ($n >= 0x2B00 && $n <= 0x2BFF) || $n == 0xFE0F || $n == 0x200D || $n == 0x20E3}
- }
- proc fl:nocodes {text} {
- regsub -all {\u0003[0-9]{0,2}(,[0-9]{1,2})?|[\u0002\u000f\u0016\u001d\u001e\u001f]} $text "" text
- return $text
- }
- proc fl:nozw {text} { ;# remove invisible chars (zero-width, soft hyphen, BOM...)
- regsub -all {[\u00ad\u180e\u200b\u200c\u200e\u200f\u2060-\u2064\ufeff]} $text "" text
- return $text
- }
- proc fl:fold {text} { ;# fullwidth ASCII (U+FF01-FF5E) -> plain ASCII, then lower case
- set out ""
- foreach c [split $text ""] {
- scan $c %c n
- if {$n >= 0xFF01 && $n <= 0xFF5E} {set c [format %c [expr {$n - 0xFEE0}]]}
- append out $c
- }
- return [string tolower $out]
- }
- # text for matching: no IRC colors, no invisible chars, no trailing emoji/spaces, folded
- proc fl:norm {text} {
- set text [fl:nozw [fl:nocodes $text]]
- set i [expr {[string length $text] - 1}]
- while {$i >= 0} {
- scan [string index $text $i] %c n
- if {![fl:isemo $n]} break
- incr i -1
- }
- fl:fold [string range $text 0 $i]
- }
-
- # at 00:00 UTC delete all remembered texts
- proc fl:utcreset {} {
- global fl
- set today [clock format [clock seconds] -format %Y-%m-%d -gmt 1]
- if {$today ne $fl(day)} {set fl(day) $today; set fl(known) {}}
- }
-
- # similarity 0..1 (Levenshtein ratio)
- proc fl:sim {a b} {
- set a [string tolower $a]; set b [string tolower $b]
- set la [string length $a]; set lb [string length $b]
- if {$la == 0 || $lb == 0} {return 0.0}
- set prev {}
- for {set j 0} {$j <= $lb} {incr j} {lappend prev $j}
- for {set i 1} {$i <= $la} {incr i} {
- set cur [list $i]; set ca [string index $a [expr {$i-1}]]
- for {set j 1} {$j <= $lb} {incr j} {
- set cost [expr {$ca eq [string index $b [expr {$j-1}]] ? 0 : 1}]
- lappend cur [expr {min([lindex $prev $j]+1, [lindex $cur [expr {$j-1}]]+1, [lindex $prev [expr {$j-1}]]+$cost)}]
- }
- set prev $cur
- }
- expr {1.0 - double([lindex $prev $lb]) / max($la, $lb)}
- }
-
- # is norm >= 80% similar to a text remembered today?
- proc fl:hit {norm} {
- global fl
- set lb [string length $norm]
- foreach k $fl(known) {
- set a [lindex $k 0]
- set la [string length $a]
- set mx [expr {max($la, $lb, 1)}]
- if {double(abs($la - $lb)) / $mx > 1.0 - $fl(sim)} continue ;# too different in length
- if {[fl:sim $a $norm] >= $fl(sim)} {return 1}
- }
- return 0
- }
-
- # run "fn ip" once the IP of nick is known (WHOIS as IRCop)
- proc fl:withip {nick fn} {
- global fl
- set k [string tolower $nick]
- if {[info exists fl(ip,$k)]} {{*}$fn $fl(ip,$k)} else {
- set fl(pend,$k) $fn
- fl:put "WHOIS $nick"
- }
- }
-
- bind raw - 311 fl:r311
- proc fl:r311 {from key text} { ;# WHOIS: nick ident host * :realname
- global fl
- set w [split [fl:in $text]]
- set fl(ident,[string tolower [lindex $w 1]]) [lindex $w 2]
- return 0
- }
-
- bind raw - 378 fl:r378
- proc fl:r378 {from key text} {
- global fl
- set text [fl:in $text]
- set w [split $text]
- set k [string tolower [lindex $w 1]]
- set fl(ip,$k) [lindex $w end]
- set fl(host,$k) [lindex [split [lindex $w end-1] @] end] ;# real host from "*@host ip"
- if {[info exists fl(pend,$k)]} {
- set fn $fl(pend,$k); unset fl(pend,$k)
- {*}$fn $fl(ip,$k)
- }
- return 1
- }
-
- bind sign - * fl:sign
- proc fl:sign {nick uhost hand chan reason} {
- global fl; set nick [fl:in $nick]; foreach v {ip ident host} {catch {unset fl($v,[string tolower $nick])}}
- }
-
- # does the text end with emoji (after some real text)?
- proc fl:tailemoji {text} {
- set t [fl:nozw [fl:nocodes $text]]
- set i [expr {[string length $t] - 1}]
- while {$i >= 0} {
- scan [string index $t $i] %c n
- if {![fl:isemo $n]} break
- incr i -1
- }
- set head [string range $t 0 $i]
- set tail [string range $t [expr {$i + 1}] end]
- expr {[string trim $head] ne "" && [string trim $tail] ne ""}
- }
-
- # IRCCloud user (real host *.irccloud.com, ident uid123456) -> returns the ident, else ""
- proc fl:irccloud {nick} {
- global fl
- set k [string tolower $nick]
- if {![info exists fl(host,$k)] || ![info exists fl(ident,$k)]} {return ""}
- set u [string trimleft $fl(ident,$k) ~]
- if {[string match -nocase *irccloud.com $fl(host,$k)] && [regexp -nocase {^[us]id[0-9]+$} $u]} {return $u}
- return ""
- }
-
- proc fl:shun {nick text chans emo ip} {
- global fl
- set id [fl:irccloud $nick] ;# shared IRCCloud IP -> shun the ident instead
- set mask [expr {$id ne "" ? "$id@*" : "*@$ip"}]
- fl:issue $nick $ip $mask $text $chans $emo [expr {$id ne ""}]
- }
-
- proc fl:issue {nick ip mask text chans emo irc} {
- global fl
- foreach s $fl(shuns) {
- if {[lindex $s 5] eq $mask} { ;# already shunned (e.g. another nick with the same IP): no new SHUN, nick still reported
- if {[llength $chans]} {fl:queue $nick $chans $text [lindex $s 6] [lindex $s 7]}
- fl:say "[fl:hms] Flood : $nick - $ip - $text"
- return
- }
- }
- set mins $fl(shunmin); set why {}
- if {$emo} { ;# emoji at the end of the flood text -> 1 hour
- if {$fl(emojishun) > $mins} {set mins $fl(emojishun)}
- lappend why "emoji në fund të tekstit"
- }
- if {$irc} { ;# IRCCloud ident shun -> 1 hour
- if {$fl(irccloudshun) > $mins} {set mins $fl(irccloudshun)}
- lappend why "IRCCloud: shun mbi $mask"
- }
- fl:put "SHUN +$mask ${mins}m :Flood"
- lappend fl(shuns) [list [clock seconds] $nick $ip $text [expr {[clock seconds] + $mins*60}] $mask $mins $why]
- if {[llength $chans]} {fl:queue $nick $chans $text $mins $why} ;# notice is collected, then sent once
- fl:say "[fl:hms] Flood : $nick - $ip - $text"
- }
-
- # ---- notices: nicks of the same flood text are collected for ~3 s and announced in ONE notice ----
- proc fl:similar {a b} {
- global fl
- set la [string length $a]; set lb [string length $b]
- set mx [expr {max($la, $lb, 1)}]
- if {double(abs($la - $lb)) / $mx > 1.0 - $fl(sim)} {return 0}
- expr {[fl:sim $a $b] >= $fl(sim)}
- }
- proc fl:queue {nick chans text mins why} {
- global fl
- set n [fl:norm $text]
- if {$n eq ""} {set n $text}
- set i 0
- foreach b $fl(alerts) {
- if {[fl:similar [dict get $b norm] $n]} { ;# same flood text -> same notice
- set items [dict get $b items]
- if {![dict exists $items $nick]} {dict set items $nick [list $mins $why]}
- dict set b items $items
- set cl [dict get $b chans]
- foreach c $chans {if {[lsearch -exact $cl $c] < 0} {lappend cl $c}}
- dict set b chans $cl
- set fl(alerts) [lreplace $fl(alerts) $i $i $b]
- return
- }
- incr i
- }
- lappend fl(alerts) [dict create norm $n text $text chans $chans items [dict create $nick [list $mins $why]] ts [clock milliseconds]]
- utimer 3 fl:flush
- }
- proc fl:flush {} {
- global fl
- set now [clock milliseconds]; set keep {}
- foreach b $fl(alerts) {
- if {$now - [dict get $b ts] >= 2800} {fl:sendalert $b} else {lappend keep $b}
- }
- set fl(alerts) $keep
- }
- proc fl:sendalert {b} {
- set items [dict get $b items]
- set nicks [dict keys $items]
- set chans [join [dict get $b chans] { }]
- if {[llength $nicks] == 1} { ;# single nick
- lassign [dict get $items [lindex $nicks 0]] mins why
- set extra ""
- if {[llength $why]} {set extra " - shun $mins min ([join $why {, }])"}
- fl:say " FLOOD Nick [lindex $nicks 0] sapo beri flood perseritje ne dhomat $chans$extra"
- return
- }
- set text [dict get $b text]
- if {[string length $text] > 100} {set text "[string range $text 0 99]&"}
- fl:say " FLOOD Nick [join [lrange $nicks 0 9] { }] sapo bene flood perseritje ne dhomat $chans : $text"
- for {set i 10} {$i < [llength $nicks]} {incr i 10} {
- fl:say " FLOOD (vazhdim) Nick [join [lrange $nicks $i [expr {$i + 9}]] { }]"
- }
- set notes {}
- dict for {n v} $items {
- lassign $v mins why
- if {[llength $why]} {lappend notes "$n: $mins min ([join $why {, }])"}
- }
- if {[llength $notes]} {fl:say "Shun: [string range [join $notes {; }] 0 349]"}
- }
-
- proc fl:trigger {msgs} {
- global fl
- fl:utcreset
- foreach m $msgs { ;# remember the flood text for today
- if {![fl:hit [lindex $m 4]]} {lappend fl(known) [list [lindex $m 4] [lindex $m 3] [clock seconds]]}
- }
- if {[llength $fl(known)] > 200} {set fl(known) [lrange $fl(known) end-199 end]}
- set nicks {}
- foreach m $msgs {
- if {[lsearch -exact $nicks [lindex $m 1]] < 0} {lappend nicks [lindex $m 1]}
- }
- foreach n $nicks {
- set chans {}; set text ""; set emo 0
- foreach m $msgs {
- if {[lindex $m 1] eq $n} {
- if {[lsearch -exact $chans [lindex $m 2]] < 0} {lappend chans [lindex $m 2]}
- set text [lindex $m 3]
- if {[fl:tailemoji $text]} {set emo 1} ;# flood text ends with emoji -> longer shun
- }
- }
- fl:withip $n [list fl:shun $n $text $chans $emo]
- }
- set new {}
- foreach h $fl(hist) {if {[lsearch -exact $msgs $h] < 0} {lappend new $h}}
- set fl(hist) $new
- }
-
- bind pubm - "* *" fl:pubm
- proc fl:pubm {nick uhost hand chan text} {
- global fl
- if {!$fl(on) || ![llength $fl(rules)] || [lsearch -nocase $fl(chans) $chan] < 0} return
- set nick [fl:in $nick]; set text [fl:in $text]
- set fl(ident,[string tolower $nick]) [lindex [split $uhost @] 0] ;# remember the ident (uid123456)
- set norm [fl:norm $text] ;# trailing emoji are ignored when comparing texts
- if {$norm eq ""} { ;# emoji-only text
- if {$fl(emojiex) && [string trim [fl:nozw [fl:nocodes $text]]] ne ""} return ;# .f emoji on -> exempt
- set norm [fl:nocodes $text] ;# .f emoji off -> checked like normal text
- }
- if {[string length $norm] < $fl(minlen)} return
- fl:utcreset
- if {[fl:hit $norm]} { ;# text flooded earlier today -> shun at once
- fl:withip $nick [list fl:shun $nick $text [list $chan] [fl:tailemoji $text]]
- return
- }
- set now [clock milliseconds]
- set keep 1
- foreach r $fl(rules) {if {[lindex $r 1] > $keep} {set keep [lindex $r 1]}}
- set new {}
- foreach h $fl(hist) {if {$now - [lindex $h 0] <= $keep*1000} {lappend new $h}}
- lappend new [list $now $nick $chan $text $norm]
- set fl(hist) $new
- set k [string tolower $nick]
- if {![info exists fl(ip,$k)] && ![info exists fl(pend,$k)]} {fl:withip $nick fl:nop}
- foreach r $fl(rules) { ;# e.g. {2 1} = 2 similar lines in 1 second
- lassign $r cnt secs
- set similar {}
- foreach h $fl(hist) {
- if {$now - [lindex $h 0] <= $secs*1000 && [fl:sim [lindex $h 4] $norm] >= $fl(sim)} {lappend similar $h}
- }
- if {[llength $similar] >= $cnt} {fl:trigger $similar; return} ;# same nick or multi nick
- }
- }
-
- bind pub - .sh fl:sh
- proc fl:sh {nick uhost hand chan text} {
- global fl
- set text [fl:in $text]
- if {[string tolower $chan] ne [string tolower $fl(opers)]} return
- lassign [split [string trim $text]] sub arg
- switch -nocase -- $sub {
- list {
- if {![llength $fl(shuns)]} {fl:say "Shun list empty"}
- foreach s $fl(shuns) {
- fl:say "[fl:hms [lindex $s 0]] Flood : [lindex $s 1] - [lindex $s 2] - [lindex $s 3]"
- }
- }
- add {
- if {$arg eq ""} return
- if {[regexp -nocase {^([us]id[0-9]+)(@\*)?$} $arg -> idn]} {fl:issue $arg - "$idn@*" manual {} 0 1; return} ;# .sh add uid123456
- if {[fl:isip $arg]} {fl:shun $arg manual {} 0 $arg} else {fl:withip $arg [list fl:shun $arg manual {} 0]}
- }
- rem {
- if {$arg eq ""} return
- set mask ""
- if {[regexp -nocase {^([us]id[0-9]+)(@\*)?$} $arg -> idn]} {set mask "$idn@*"} else {
- foreach s $fl(shuns) {
- if {[string equal -nocase [lindex $s 1] $arg] || [lindex $s 2] eq $arg} {set mask [lindex $s 5]}
- }
- if {$mask eq "" && [fl:isip $arg]} {set mask "*@$arg"}
- if {$mask eq "" && [info exists fl(ip,[string tolower $arg])]} {set mask "*@$fl(ip,[string tolower $arg])"}
- }
- if {$mask eq ""} {fl:say "Nuk u gjet $arg"; return}
- fl:put "SHUN -$mask"
- set new {}
- foreach s $fl(shuns) {if {[lindex $s 5] ne $mask} {lappend new $s}}
- set fl(shuns) $new
- fl:say "Shun removed: $arg ($mask)"
- }
- }
- }
-
- set fl(helptext) {
- == ANTI-FLOOD - KOMANDAT (përdoren vetëm në #opers) ==
- Si punon: roboti krahason tekstet me Levenshtein (ngjashmëri 80%). Nëse një nick ose disa nick, me IP të njëjtë ose të ndryshme, dërgojnë tekste të njëjta ose të ngjashme shpejt, IP-ja shun-ohet dhe njoftohet #opers.
- Kujtesa: tekstet e flood-it ruhen deri në 00:00 UTC; kushdo që e dërgon prapë një tekst të ngjashëm (80%) shun-ohet menjëherë, edhe me një mesazh të vetëm. Në 00:00 UTC të gjitha tekstet e ruajtura fshihen.
- Shumë nick: nëse disa nick (me IP të njëjtë ose të ndryshme) dërgojnë të njëjtin tekst brenda pak sekondash, njoftimi në #opers liston të gjithë nick-et dhe tekstin e flood-it në një mesazh (pret rreth 3 sek për t'i mbledhur); secili nick shun-ohet menjëherë dhe veç e veç.
- .f on / .f off - ndez ose fik mbrojtjen anti-flood. Shërben që ta ndalosh robotin përkohësisht pa e mbyllur.
- .f list - shfaq cilësimet aktuale (rregullat, gjatësia, koha e shun-it, emoji) me shpjegim.
- .f help - shfaq këtë ndihmë me të gjitha komandat.
- .f add 3:1 / .f del 3:1 - shton ose heq një rregull rreshta:sekonda. 3:1 = 3 rreshta të ngjashëm brenda 1 sekonde. Shërben për të kapur flood të shpejtë; mund të kesh disa rregulla (p.sh. 2:1 dhe 3:5).
- .f add 50 / .f del 50 - vendos gjatësinë minimale të tekstit në karaktere (p.sh. 50, 100, 300); tekstet më të shkurtra nuk kontrollohen. .f del e kthen në 8. Shërben që fjalët e shkurtra ("po", "mirë") të mos shun-ohen gabimisht.
- .f add shuntime 10 / .f del shuntime - vendos sa minuta zgjat shun-i (1-1440, p.sh. 5, 10, 15). .f del e kthen në 5 min.
- .f emoji on / .f emoji off - on: mesazhet vetëm me emoji nuk shun-ohen; off: kontrollohen si tekst normal. Emoji në fund të tekstit injorohen gjithmonë, prandaj tekst + emoji flood shun-ohet.
- Emoji në fund: nëse teksti i flood-it mbaron me emoji (p.sh. "tekst 😂"), shun-i zgjat 60 min në vend të kohës normale (nëse koha normale është më e gjatë, përdoret ajo).
- IRCCloud: nëse përdoruesi vjen nga irccloud.com (ident uid123456), IP-ja është e përbashkët me shumë përdorues, prandaj shun-i vihet mbi ident-in (uid123456@*) për 60 min. Mund ta përdorësh edhe me dorë: .sh add uid123456 / .sh rem uid123456.
- .sh list - lista e shun-eve aktive, një rresht për nick: ora Flood : Nick - IP - teksti.
- .sh add nick/ip - shun manual për një nick ose IP (përdor kohën e shun-it aktuale).
- .sh rem nick/ip - heq shun-in e një nicku ose IP-je para kohe.
- }
-
- proc fl:help {} {
- global fl
- foreach l [split $fl(helptext) "\n"] {if {$l ne ""} {fl:say $l}}
- }
-
- proc fl:status {} {
- global fl
- set rs [join [lmap r $fl(rules) {join $r :}] { }]
- if {$rs eq ""} {set rs "asnjë"}
- fl:say "Statusi: [expr {$fl(on) ? {NDEZUR} : {FIKUR}}] - mbrojtja anti-flood (.f on / .f off)"
- fl:say "Dhomat e kontrolluara: $fl(chans)"
- fl:say "Ngjashmëria: [expr {int($fl(sim)*100)}]% (Levenshtein) - tekstet me kaq ngjashmëri ose më shumë llogariten të njëjta"
- fl:say "Rregullat (rreshta:sekonda): $rs - p.sh. 2:1 = 2 rreshta të ngjashëm brenda 1 sekonde (.f add/del 3:1)"
- fl:say "Gjatësia minimale e tekstit: $fl(minlen) karaktere - tekstet më të shkurtra nuk kontrollohen (.f add/del 50)"
- fl:say "Koha e shun-it: $fl(shunmin) min - sa zgjat bllokimi i IP-së (.f add/del shuntime 10)"
- fl:say "Shun-i për flood me emoji në fund të tekstit: 60 min (vlerë fikse) - zakonisht përdoret nga ata që shtojnë emoji për të shmangur zbulimin"
- fl:say "Shun-i për IRCCloud: 60 min mbi ident-in (uid123456@*), jo mbi IP-në e përbashkët të IRCCloud"
- fl:say "Emoji: [expr {$fl(emojiex) ? {ON} : {OFF}}] - ON: mesazhet vetëm me emoji nuk shun-ohen; OFF: kontrollohen (.f emoji on/off)"
- fl:say "Tekste të ruajtura: [llength $fl(known)] - tekstet e flood-it mbahen deri në 00:00 UTC dhe fshihen automatikisht"
- fl:say "Shkruaj .f help për shpjegimin e të gjitha komandave."
- }
-
- bind pub - .f fl:f
- proc fl:f {nick uhost hand chan text} {
- global fl
- set text [fl:in $text]
- if {[string tolower $chan] ne [string tolower $fl(opers)]} return
- lassign [split [string trim $text]] sub arg arg2
- switch -nocase -- $sub {
- on {set fl(on) 1; fl:say "Anti-flood ON"}
- off {set fl(on) 0; fl:say "Anti-flood OFF"}
- emoji {
- if {$arg eq "on" || $arg eq "off"} {
- set fl(emojiex) [expr {$arg eq "on"}]
- fl:say "Emoji-only text: [expr {$fl(emojiex) ? {exempt (ON)} : {checked (OFF)}}]"
- }
- }
- add - del {
- set add [string equal -nocase $sub add]
- if {[string equal -nocase $arg shuntime]} { ;# .f add shuntime 5 / 10 / 15 (minutes)
- if {$add} {
- if {![regexp {^[0-9]{1,4}$} $arg2]} {fl:say "Format: .f add shuntime minutes (1-1440)"; return}
- scan $arg2 %d n
- if {$n < 1 || $n > 1440} {fl:say "Format: .f add shuntime minutes (1-1440)"; return}
- set fl(shunmin) $n
- fl:say "Shun time: $n min"
- } else { ;# .f del shuntime -> back to default
- set fl(shunmin) $fl(defshun)
- fl:say "Shun time reset: $fl(defshun) min"
- }
- return
- }
- if {[regexp {^\d+:\d+$} $arg]} { ;# rate rule count:seconds
- scan $arg %d:%d c s
- if {$c < 2 || $s < 1} {fl:say "Format: .f add count:seconds (count>=2, seconds>=1)"; return}
- set i [lsearch -exact $fl(rules) [list $c $s]]
- if {$add} {
- if {$i < 0} {lappend fl(rules) [list $c $s]}
- fl:say "Rule added: $c lines / ${s}s"
- } elseif {$i >= 0} {
- set fl(rules) [lreplace $fl(rules) $i $i]; fl:say "Rule removed: $c:$s"
- } else {fl:say "Rule $arg not found"}
- } elseif {[regexp {^\d+$} $arg]} { ;# min text length
- scan $arg %d n
- if {$add} {set fl(minlen) $n; fl:say "Min text length: $n chars"} \
- elseif {$fl(minlen) == $n} {set fl(minlen) $fl(deflen); fl:say "Min text length reset: $fl(deflen) chars"} \
- else {fl:say "Length $n not set"}
- }
- }
- help {fl:help}
- list {fl:status}
- }
- }
-
- # forget expired shuns (the server removes the shun itself after 10m)
- bind time - "* * * * *" fl:expire
- proc fl:expire {args} {
- global fl
- fl:utcreset
- set now [clock seconds]; set new {}
- foreach s $fl(shuns) {if {$now < [lindex $s 4]} {lappend new $s}}
- set fl(shuns) $new
- }
-
- # become IRCop on connect
- bind evnt - init-server fl:oper
- proc fl:oper {type} {global fl; fl:put "OPER $fl(oper_user) $fl(oper_pass)"}
-
- putlog "antiflood.tcl loaded"
-
Add a comment