#by wiebe @ QuakeNet

#strip codes?
#add actions (pub|msg)

#censor.tcl

#-censor add|del|search


#channel settings:
# +censor on/off
# +censor-pm ban on pm
# +censor-instagib ban always
# +censor-op ignore ops
# +censor-hop ignore halfops
# +censor-voice ignore voices
# censor-bantime time to ban

#cmd for channels -censor
#cmd for /msg censor
#cmd for partyline .censor

#bind pubm <flags> <mask> <proc>
#procname <nick> <user@host> <handle> <channel> <text>

#file to save entries
set censor(file) "censor.txt"

#default reason
set censor(reason) "You are violating channel rules"

#vhost
set censor(vhost) ".users.quakenet.org"

#default bantime
set censor(bantime) 30

setudef flag censor
setudef flag censor-pm
setudef flag censor-instagib
setudef flag censor-op
setudef flag censor-hop
setudef flag censor-voice
setudef int censor-bantime



#text
proc censor:checkpub { nick uhost handle chan text } {
  global lastbind censor censordb
  set target [string tolower $chan]
  set mask [join [lrange [split $lastbind] 1 end]]
  if { ![channel get $chan censor] } { return 0 }
  if { [matchattr $handle bfvlomn|fvlomn $chan] } { return 0 }

  if { [info exists censordb($target,$mask)] } {
    set options [lindex [split $censordb($target,$mask)] 0]
    set reason [join [lrange [split $censordb($target,$mask)] 1 end]]
  } elseif { [info exists censordb(global,$mask)] } {
    set options [string tolower [lindex [split $censordb(global,$mask)] 0]]
    set reason [join [lrange [split $censordb(global,$mask)] 1 end]]
  } else { return 0 }

  if { [isop $nick $chan] && [channel get $chan censor-op] } { return 0 }
  if { [ishalfop $nick $chan] && [channel get $chan censor-hop] } { return 0 }
  if { [isvoice $nick $chan] && [channel get $chan censor-voice] } { return 0 }

  if { [channel get $chan censor-instagib] } { set options b }

  if { [string equal $options w] } {
    puthelp "NOTICE $nick :$reason"
    putlog "censor: warning $nick ($uhost) in $chan for TEXT=$text MASK=$mask REASON=$reason"
  } elseif { [string equal $options k] } {
    putkick $chan $nick $reason
    putlog "censor: kicking $nick ($uhost) in $chan for TEXT=$text MASK=$mask REASON=$reason"
  } elseif { [string equal $options b] } {
    set bmask *!*@[lindex [split $uhost @] 1]
    set duration [channel get $chan censor-bantime]
    if { $duration <= 0 } {
      if { [info exists censor(bantime)] } { set duration $censor(bantime) }
      if { ![string is digit $duration] || $duration <= 0 } { set duration 30 }
    }
    pushmode $chan +b $bmask
    utimer 3 [list newchanban $chan $bmask censor.tcl $reason $duration]
    putlog "censor: banning $nick ($uhost) in $chan for TEXT=$text MASK=$mask REASON=$reason"
    set account ""
    if { [catch {set account [whox $nick a]} error] } {
      putlog "ERROR using whox.tcl, perhaps you have not loaded it?"
      error $error
    }
    if { ![string equal $account ""] && ![string equal $account 0] && [info exists censor(vhost)] && ![string equal $censor(vhost) ""] } {
      set amask *!*@$account$censor(vhost)
      if { ![string equal $bmask $amask] } {
        pushmode $chan +b $amask
        utimer 3 [list newchanban $chan $amask $nick $reason $duration]
      }
    }
  }
}



#action
bind ctcp - ACTION censor:checkaction

proc censor:checkaction { nick uhost handle dest keyword text } {
  global censor censordb
  if { [string equal $dest $::botnick] } {

  } elseif { [validchan $dest] } {
    set chan $dest

    if { ![channel get $chan censor] } { return 0 }
    if { [matchattr $handle bfvlomn|fvlomn $chan] } { return 0 }
    if { [isop $nick $chan] && [channel get $chan censor-op] } { return 0 }
    if { [ishalfop $nick $chan] && [channel get $chan censor-hop] } { return 0 }
    if { [isvoice $nick $chan] && [channel get $chan censor-voice] } { return 0 }
    if { [channel get $chan censor-instagib] } { set options b }

    foreach name [array names censordb] {
      set target [lindex [split $name ,] 0]
      if { [string equal -nocase $target global] || [string equal -nocase $target $chan] } {
        set mask [join [lrange [split $name ,] 1 end] ,]
        set mask2 $mask
        regsub -all {\[} $mask2 {\\[} mask2
        regsub -all {\]} $mask2 {\\]} mask2
        regsub -all {\\} $mask2 {\\\\} mask2
        if { [string match -nocase $mask2 $text] } {
          set reason [join [lrange [split $censordb($name)] 1 end]]
          set options [lindex [split $censordb($name)] 0]
          if { [string equal $options w] } {
            puthelp "NOTICE $nick :$reason"
            putlog "censor: warning $nick ($uhost) in $chan for TEXT=$text MASK=$mask REASON=$reason"
          } elseif { [string equal $options k] } {
            putkick $chan $nick $reason
            putlog "censor: kicking $nick ($uhost) in $chan for TEXT=$text MASK=$mask REASON=$reason"
          } elseif { [string equal $options b] } {
            set bmask *!*@[lindex [split $uhost @] 1]
            set duration [channel get $chan censor-bantime]
            if { $duration <= 0 } {
              if { [info exists censor(bantime)] } { set duration $censor(bantime) }
              if { ![string is digit $duration] || $duration <= 0 } { set duration 30 }
            }
            pushmode $chan +b $bmask
            utimer 3 [list newchanban $chan $bmask censor.tcl $reason $duration]
            putlog "censor: banning $nick ($uhost) in $chan for TEXT=$text MASK=$mask REASON=$reason"
            set account ""
            if { [catch {set account [whox $nick a]} error] } {
              putlog "ERROR using whox.tcl, perhaps you have not loaded it?"
              error $error
            }
            if { ![string equal $account ""] && ![string equal $account 0] && [info exists censor(vhost)] && ![string equal $censor(vhost) ""] } {
              set amask *!*@$account$censor(vhost)
              if { ![string equal $bmask $amask] } {
                pushmode $chan +b $amask
                utimer 3 [list newchanban $chan $amask $nick $reason $duration]
              }
            }
          }
          return 0
        }
      }
    }
  }
}



#checkmsg
proc censor:checkmsg { nick uhost handle text } {
  global lastbind censor censordb
  set mask $lastbind
  if { ![info exists censordb(pm,$mask)] } { return 0 }
  set options [lindex [split $censordb(pm,$mask)] 0]
  set reason [join [lrange [split $censordb(pm,$mask)] 1 end]]
  foreach chan [channels] {
    if { [botonchan $chan] && [onchan $nick $chan] && [channel get $chan censor-pm] && ![matchattr $handle bfvlomn|fvlomn $chan] } {
      if { ![isop $nick $chan] || ![channel get $chan censor-op] } {
        if { ![ishalfop $nick $chan] || ![channel get $chan censor-hop] } {
          if { ![isvoice $nick $chan] || ![channel get $chan censor-voice] } {
            if { [channel get $chan censor-instagib] } { set options b }
            if { [string equal $options b] } {
              set bmask *!*@[lindex [split $uhost @] 1]
              set duration [channel get $chan censor-bantime]
            }
            if { $duration <= 0 } {
              if { [info exists censor(bantime)] } { set duration $censor(bantime) }
              if { ![string is digit $duration] || $duration <= 0 } { set duration 30 }
            }
            pushmode $chan +b $bmask
            utimer 3 [list newchanban $chan $bmask censor.tcl $reason $duration]
            putlog "censor: banning $nick ($uhost) in $chan for TEXT=$text MASK=$mask REASON=$reason"
            set account ""
            if { [catch {set account [whox $nick a]} error] } {
              putlog "ERROR using whox.tcl, perhaps you have not loaded it?"
              error $error
            }
            if { ![string equal $account ""] && ![string equal $account 0] && [info exists censor(vhost)] && ![string equal $censor(vhost) ""] } {
              set amask *!*@$account$censor(vhost)
              if { ![string equal $bmask $amask] } {
                pushmode $chan +b $amask
                utimer 3 [list newchanban $chan $amask $nick $reason $duration]
              }
            }
          }
        }
      }
    }
  }
}



#censor pub
#bind pub mn -censor censor:pub

proc censor:pub { nick uhost handle chan text } {
  global censor censordb
  set cmd [lindex [split $text] 0]
  set output ""
  if { [string equal -nocase $cmd add] } {
    set target [string tolower [lindex [split $text] 1]]
    set options [string tolower [lindex [split $text] 2]]
    set rest [join [lrange [split $text] 3 end]]
    set mask [lindex [split $rest @] 0]
    set reason [lindex [split $rest @] 1]
    if { [string equal $reason ""] } { set reason $censor(reason) }
    if { ![string match \#* $target] && ![string equal $target global] && ![string equal $target pm] } {
      lappend output "Usage: censor add <target> <options> <mask>@<reason>"
      lappend output "Target can be a channel, pm or global."
    } elseif { (([string equal $target global]) || ([string equal $target pm])) && (![matchattr $handle mn]) } {
      lappend output "censor: not enough access to add global/pm entries."
    } elseif { ![string equal $target global] && ![string equal $target pm] && ![validchan $target] } {
      lappend output "censor add <target> <options> <mask>@<reason>"
      lappend output "Target can be a channel, pm or global."
    } elseif { ![string equal $target global] && ![string equal $target pm] && ![matchattr $handle mn|mn $target] } {
      lappend output "censor: not enough access on $target to add entries"
    } elseif { [string equal [lsearch -exact "w k b" $options] -1] } {
      lappend output "censor add <target> <options> <mask>@<reason>"
      lappend output "Options are w=warn k=kick b=ban"
    } elseif { [string equal $mask ""] } {
      lappend output "censor add <target> <options> <mask>@<reason>"
      lappend output "Mask is matched against messages, wildcards *%? and spaces are supported (may not contain a @ char)."
    } else {
      set censordb($target,$mask) "$options $reason"
      censor:save
      censor:load
      lappend output "censor: added MASK=$mask TARGET=$target OPTION=$options REASON=$reason"
    }
  } elseif { [string equal -nocase $cmd del] } {
    set target [string tolower [lindex [split $text] 1]]
    set mask [join [lrange [split $text] 2 end]]
    if { [string equal $target ""] || [string equal $mask ""] } {
      lappend output "censor del <target> <mask>"
    } elseif { (([string equal $target global]) || ([string equal $target pm])) && (![matchattr $handle mn]) } {
      lappend output "censor: not enough access to remove global/pm entries."
    } elseif { ![string equal $target global] && ![string equal $target pm] && ![validchan $target] } {
      lappend output "censor del <target> <mask>"
    } elseif { ![string equal $target global] && ![string equal $target pm] && ![matchattr $handle mn|mn $target] } {
      lappend output "censor: not enough access on $target to remove entries."
    } elseif { ![info exists censordb($target,$mask)] } {
      lappend output "censor: no match found for $mask on $target"
    } else {
      unset censordb($target,$mask)
      censor:save
      censor:load
      lappend output "censor: removed $mask on $target"
    }
  } elseif { [string equal -nocase $cmd search] } {
    set target [string tolower [lindex [split $text] 1]]
    set mask [join [lrange [split $text] 2 end]]
    if { [string equal $mask ""] } { set mask * }
    set target2 $target
    set mask2 $mask
    regsub -all {\[} $target2 {\\[} target2
    regsub -all {\]} $target2 {\\]} target2
    regsub -all {\\} $target2 {\\\\} target2
    regsub -all {\[} $mask2 {\\[} mask2
    regsub -all {\]} $mask2 {\\]} mask2
    regsub -all {\\} $mask2 {\\\\} mask2
    if { [string equal $target ""] } {
      lappend output "censor search <target> \[<mask>\]"
    } else {
      set x 0
      foreach name [lsort [array names censordb]] {
        if { [string match -nocase $target2,$mask2 $name] } {
          incr x 1
          if { $x > 3 } {
            lappend output "Too many lines in output, restrict your query."
            break
          }
          set t [lindex [split $name ,] 0]
          set o [lindex [split $censordb($name)] 0]
          set m [join [lrange [split $name ,] 1 end] ,]
          set r [join [lrange [split $censordb($name)] 1 end]]
          lappend output "TARGET=$t OPTIONS=$o MASK=$m REASON=$r"
        }
      }
      if { [string equal $x 0] } {
        lappend output "censor: no matches found for TARGET=$target and MASK=$mask"
      }
    }
  } else {
    lappend output "Usage: censor <add|del|search>"
    lappend output "Channel settings: +censor +censor-pm +censor-instagib +censor-op +censor-hop +censor-voice and censor-bantime"
  }
  if { [catch {set x [cnotice $nick $output]} error] || !$x } {
    foreach t $output { puthelp "NOTICE $nick :$t" }
  }
}



#censor msg
#bind msg mn censor censor:msg

proc censor:msg { nick uhost handle text } {
  censor:pub $nick $uhost $handle chan $text
}



#censor dcc
bind dcc - censor censor:dcc

proc censor:dcc { handle idx text } {
  global censor censordb
  set cmd [lindex [split $text] 0]
  if { [string equal -nocase $cmd add] } {
    set target [string tolower [lindex [split $text] 1]]
    set options [string tolower [lindex [split $text] 2]]
    set rest [join [lrange [split $text] 3 end]]
    set mask [lindex [split $rest @] 0]
    set reason [lindex [split $rest @] 1]
    if { [string equal $reason ""] } { set reason $censor(reason) }
    if { ![string match #* $target] && ![string equal $target global] && ![string equal $target pm] } {
      putidx $idx "censor add <target> <options> <mask>@<reason>"
      putidx $idx "target can be a channel, pm or global"
    } elseif { (([string equal $target global]) || ([string equal $target pm])) && (![matchattr $handle mn]) } {
      putidx $idx "censor: not enough access to add global/pm entries"
    } elseif { ![string equal $target global] && ![string equal $target pm] && ![validchan $target] } {
      putidx $idx "censor add <target> <options> <mask>@<reason>"
      putidx $idx "target can be a channel, pm or global"
    } elseif { ![string equal $target global] && ![string equal $target pm] && ![matchattr $handle mn|mn $target] } {
      putidx $idx "censor: not enough access on $target to add entries"
    } elseif { [string equal [lsearch -exact "w k b" $options] -1] } {
      putidx $idx "censor add <target> <options> <mask>@<reason>"
      putidx $idx "options are w=warn k=kick b=ban"
    } elseif { [string equal $mask ""] } {
      putidx $idx "censor add <target> <options> <mask>@<reason>"
      putidx $idx "mask is matched against messages, wildcards *%? and spaces are supported (may not contain a @ char)"
    } else {
      set censordb($target,$mask) "$options $reason"
      censor:save
      censor:load
      putidx $idx "censor: added MASK=$mask TARGET=$target OPTION=$options REASON=$reason"
    }
  } elseif { [string equal -nocase $cmd del] } {
    set target [string tolower [lindex [split $text] 1]]
    set mask [join [lrange [split $text] 2 end]]
    if { [string equal $target ""] || [string equal $mask ""] } {
      putidx $idx "censor del <target> <mask>"
    } elseif { (([string equal $target global]) || ([string equal $target pm])) && (![matchattr $handle mn]) } {
      putidx $idx "censor: not enough access to remove global/pm entries"
    } elseif { ![string equal $target global] && ![string equal $target pm] && ![validchan $target] } {
      putidx $idx "censor del <target> <mask>"
    } elseif { ![string equal $target global] && ![string equal $target pm] && ![matchattr $handle mn|mn $target] } {
      putidx $idx "censor: not enough access on $target to remove entries"
    } elseif { ![info exists censordb($target,$mask)] } {
      putidx $idx "censor: no match found for $mask on $target"
    } else {
      unset censordb($target,$mask)
      censor:save
      censor:load
      putidx $idx "censor: removed $mask on $target"
    }
  } elseif { [string equal -nocase $cmd search] } {
    set target [string tolower [lindex [split $text] 1]]
    set mask [join [lrange [split $text] 2 end]]
    if { [string equal $mask ""] } { set mask * }
    set target2 $target
    set mask2 $mask
    regsub -all {\[} $target2 {\\[} target2
    regsub -all {\]} $target2 {\\]} target2
    regsub -all {\\} $target2 {\\\\} target2
    regsub -all {\[} $mask2 {\\[} mask2
    regsub -all {\]} $mask2 {\\]} mask2
    regsub -all {\\} $mask2 {\\\\} mask2
    if { [string equal $target ""] } {
      putidx $idx "censor search <target> \[<mask>\]"
    } else {
      set x 0
      foreach name [lsort [array names censordb]] {
        if { [string match -nocase $target2,$mask2 $name] } {
          incr x 1
          if { $x > 50 } {
            putidx $idx "too many lines in output, restrict your query."
            return 0
          }
          set t [lindex [split $name ,] 0]
          set o [lindex [split $censordb($name)] 0]
          set m [join [lrange [split $name ,] 1 end] ,]
          set r [join [lrange [split $censordb($name)] 1 end]]
          putidx $idx "TARGET=$t OPTIONS=$o MASK=$m REASON=$r"
        }
      }
      if { [string equal $x 0] } {
        putidx $idx "censor: no matches found for TARGET=$target and MASK=$mask"
      }
    }
  } else {
    putidx $idx "censor add|del|search"
    putidx $idx "channel settings: +censor +censor-pm +censor-instagib +censor-op +censor-hop +censor-voice and censor-bantime"
  }
}



#load
proc censor:load { } {
  global censor censordb

  #type flags mask count proc
  #re-do binds for channel
  foreach bind [binds pubm] {
    set proc [lindex $bind 4]
    if { [string equal $proc censor:checkpub] } {
      set type [lindex $bind 0]
      set flags [lindex $bind 1]
      set mask [lindex $bind 2]
      unbind $type $flags $mask $proc
    }
  }

  #re-do binds for msg
  foreach bind [binds msgm] {
    set proc [lindex $bind 4]
    if { [string equal $proc censor:checkmsg] } {
      set type [lindex $bind 0]
      set flags [lindex $bind 1]
      set mask [lindex $bind 2]
      unbind $type $flags $mask $proc
    }
  }

  if { [info exists censordb] } { unset censordb }
  set file $censor(file)
  if { ![file exists $file] } { return 0 }
  set filesocket [open "$file" r]
  set data [read $filesocket]
  close $filesocket
  foreach line [split $data \n] {
    if { ![string equal $line ""] } {
#target options mask@reason
      set target [lindex [split $line] 0]
      set options [lindex [split $line] 1]
      set rest [join [lrange [split $line] 2 end]]
      set mask [lindex [split $rest @] 0]
      set reason [lindex [split $rest @] 1]
      set censordb($target,$mask) "$options $reason"
      if { [string equal $target pm] } {
        bind msgm - $mask censor:checkmsg
      } elseif { [string equal $target global] } {
        bind pubm - "% $mask" censor:checkpub
      } else {
        bind pubm - "$target $mask" censor:checkpub
      }
    }
  }
}



#save
proc censor:save { } {
  global censor censordb
  set file $censor(file)
  set fs [open "$file" w]
  foreach name [lsort [array names censordb]] {
    set target [lindex [split $name ,] 0]
    set options [lindex [split $censordb($name)] 0]
    set mask [join [lrange [split $name ,] 1 end] ,]
    set reason [join [lrange [split $censordb($name)] 1 end]]
    puts $fs "$target $options $mask@$reason"
  }
  close $fs
}



#load
censor:load

