Code:
on *:TEXT:*:#:{
  if (!$hget(badwords)) { hmake badwords | hload -n badwords badwords.txt | .timer.hfree.badwords 1 3600 hfree -w badwords }
  var %text = $1-
  tokenize 32 $strip($1-)
  while ($0) {
    if ($hfind(badwords,$1,1,n).data) {
      if ($($+(%,$nick,@badword),2)) {
        kick # $nick blah2
        unset $+(%,$nick,@badword)
      }
      else { 
        kick # $nick blah
        set -u86400 $+(%,$nick,@badword) 1
      }
      return
    }
    tokenize 32 $2-
  }
}

* code untested *

The first line, check s if the badword hashtable exists and if not, creates it, and loads it from the txt file using the -n (load data only option), lastly it creates a timer that well erase the hashtable each hour (3600secs), this well cause the next instance of the event to load it from file again, allowing for a hourly update should the badwords.txt file be altered. You may wish to change this frequency. A manual reload apon event can be envoked by simply typing /HFREE badwords.

next I save the original text, tokenize it with out colors etc, loop through each word, I know look for the matching data in the hashtable *(note below) if its found i then see if the already done once flag on the nick has been set already, (i have adjusted this from being %nick.bad to being %nick@badword as @badword can not be part of a nick while .bad can be). If its already set I kick message 2, and unset the var, if it wasnt kick message 1 and set the flag, Im using -u86400 so the flAg self falls after 24hours, u can remove this or change the value, means currently u can be kicked once per 24hours without incurring a message2 kick. then I exit the event either way flag up or down. IF the word was NOT found i then retokenize $2- into $1-, and loop back for the next word. [b]<------ this was the bit you missed it would have likely caused your script to freeze mirc as it would have never ended.