I'm still not happy with what's below, mainly because it's rather easy to just /part, change nick and join and start over without warnings. Maybe it's better to use ident/address parts, but I'm too lazy now...

There's no automatic removal of warning levels, using set -u would result in a simple restart of mIRC giving everyone a clean state.

Code:
on $@*:TEXT:m/[^\w\s]{[color:orange]5[/color],}[color:blue]|[/color](\S)\1{[color:red]3[/color],}[color:blue]|[/color]\s{[color:#666666]15[/color],}/iS:*: {
  ; [color:orange]5[/color] non-word characters in a row: &é"(§ or :-))) or !!?!! or "caféé!?" or 
  ; [color:blue]OR[/color] 4 = [color:red]3[/color] + 1 the same characters (except whitespace): yoooo or zeeeend or awwww or !!!! or Item....Price
  ; [color:blue]OR[/color] more than [color:#666666]15[/color] whitespace in a row: a               b
  ; m/regex/iS means match the regex in case insensitive manner (i) and first strip control codes (S)
  inc $+(%,punctuation.,$network,.,$chan,.,$nick)
  var %v = $($+(%,punctuation.,$network,.,$chan,.,$nick),2)
  if (%v == 1) { 
    .msg $chan $nick Please dont excessively repeat characters. $&
      This is your first warning. Next warning will be followed by a kick
  }
  elseif (%v == 2) { 
    kick $chan $nick Please stop with the excessive characters
  }
  elseif (%v == 3) { 
    ban -ku43200 $chan $nick 3rd Warning *12 hour ban*
  }
  elseif (%v == 4) {
    ban -ku86400 $chan $nick 4th and final warning *24 hour ban*
  }
  else ban -ku86400 $chan $nick Punctuation kick *24 hour ban*
}

; copy count when someone changes nick
; you should probably kickban them for ban evasion or something instead
on *:NICK:{
  var %i = 1
  while ($var($+(%,punctuation.,$network,.,*,.,$nick),%i)) {
    if ($($v1,2) > $($+(%,punctuation.,$network,.,$chan,.,$newnick),2)) {
      set $+(%,punctuation.,$network,.,$chan,.,$newnick) $v1
    }
    inc %i
  }
}

; little nicklist menu to show and change status if you select a single nick
menu nicklist {
  $iif($snick(#,0) == 1,punctuation)
  . $style(2) level = $calc(0 + $($+(%,punctuation.,$network,.,$chan,.,$1),2)):.
  .set...:set $+(%,punctuation.,$network,.,$chan,.,$1) $$?="New level?"
  .unset:unset $+(%,punctuation.,$network,.,$chan,.,$1)
  .inc:inc $+(%,punctuation.,$network,.,$chan,.,$1)
  .dec:dec $+(%,punctuation.,$network,.,$chan,.,$1)
}